mirror of
https://github.com/aljazceru/kata-containers.git
synced 2026-01-10 01:44:21 +01:00
Fixes #50 Previously the devices are created with device manager and laterly attached to hypervisor with "device.Attach()", this could work, but there's no way to remember the reference count for every device, which means if we plug one device to hypervisor twice, it's truly inserted twice, but actually we only need to insert once but use it in many places. Use device manager as a consolidated entrypoint of device management can give us a way to handle many "references" to single device, because it can save all devices and remember it's use count. Signed-off-by: Wei Zhang <zhangwei555@huawei.com>
188 lines
4.4 KiB
Go
188 lines
4.4 KiB
Go
// Copyright (c) 2017-2018 Intel Corporation
|
|
// Copyright (c) 2018 Huawei Corporation
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
package manager
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"errors"
|
|
"sync"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
"github.com/kata-containers/runtime/virtcontainers/device/api"
|
|
"github.com/kata-containers/runtime/virtcontainers/device/config"
|
|
"github.com/kata-containers/runtime/virtcontainers/device/drivers"
|
|
"github.com/kata-containers/runtime/virtcontainers/utils"
|
|
)
|
|
|
|
const (
|
|
// VirtioBlock indicates block driver is virtio-blk based
|
|
VirtioBlock string = "virtio-blk"
|
|
// VirtioSCSI indicates block driver is virtio-scsi based
|
|
VirtioSCSI string = "virtio-scsi"
|
|
)
|
|
|
|
var (
|
|
// ErrIDExhausted represents that devices are too many
|
|
// and no more IDs can be generated
|
|
ErrIDExhausted = errors.New("IDs are exhausted")
|
|
// ErrDeviceNotExist represents device hasn't been created before
|
|
ErrDeviceNotExist = errors.New("device with specified ID hasn't been created")
|
|
// ErrDeviceAttached represents the device is already attached
|
|
ErrDeviceAttached = errors.New("device is already attached")
|
|
// ErrDeviceNotAttached represents the device isn't attached
|
|
ErrDeviceNotAttached = errors.New("device isn't attached")
|
|
)
|
|
|
|
type deviceManager struct {
|
|
blockDriver string
|
|
|
|
devices map[string]api.Device
|
|
sync.RWMutex
|
|
}
|
|
|
|
func deviceLogger() *logrus.Entry {
|
|
return api.DeviceLogger().WithField("subsystem", "device")
|
|
}
|
|
|
|
// NewDeviceManager creates a deviceManager object behaved as api.DeviceManager
|
|
func NewDeviceManager(blockDriver string, devices []api.Device) api.DeviceManager {
|
|
dm := &deviceManager{
|
|
devices: make(map[string]api.Device),
|
|
}
|
|
if blockDriver == VirtioBlock {
|
|
dm.blockDriver = VirtioBlock
|
|
} else {
|
|
dm.blockDriver = VirtioSCSI
|
|
}
|
|
|
|
for _, dev := range devices {
|
|
dm.devices[dev.DeviceID()] = dev
|
|
}
|
|
return dm
|
|
}
|
|
|
|
// createDevice creates one device based on DeviceInfo
|
|
func (dm *deviceManager) createDevice(devInfo config.DeviceInfo) (api.Device, error) {
|
|
path, err := config.GetHostPathFunc(devInfo)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
devInfo.HostPath = path
|
|
|
|
// device ID must be generated by manager instead of device itself
|
|
// in case of ID collision
|
|
if devInfo.ID, err = dm.newDeviceID(); err != nil {
|
|
return nil, err
|
|
}
|
|
if isVFIO(path) {
|
|
return drivers.NewVFIODevice(&devInfo), nil
|
|
} else if isBlock(devInfo) {
|
|
if devInfo.DriverOptions == nil {
|
|
devInfo.DriverOptions = make(map[string]string)
|
|
}
|
|
devInfo.DriverOptions["block-driver"] = dm.blockDriver
|
|
return drivers.NewBlockDevice(&devInfo), nil
|
|
} else {
|
|
deviceLogger().WithField("device", path).Info("Device has not been passed to the container")
|
|
return drivers.NewGenericDevice(&devInfo), nil
|
|
}
|
|
}
|
|
|
|
// NewDevice creates bundles of devices based on array of DeviceInfo
|
|
func (dm *deviceManager) NewDevice(devInfo config.DeviceInfo) (api.Device, error) {
|
|
dm.Lock()
|
|
defer dm.Unlock()
|
|
dev, err := dm.createDevice(devInfo)
|
|
if err == nil {
|
|
dm.devices[dev.DeviceID()] = dev
|
|
}
|
|
return dev, err
|
|
}
|
|
|
|
func (dm *deviceManager) newDeviceID() (string, error) {
|
|
for i := 0; i < 5; i++ {
|
|
// generate an random ID
|
|
randBytes, err := utils.GenerateRandomBytes(8)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
id := hex.EncodeToString(randBytes)
|
|
|
|
// check ID collision, choose another one if ID is in use
|
|
if _, ok := dm.devices[id]; !ok {
|
|
return id, nil
|
|
}
|
|
}
|
|
return "", ErrIDExhausted
|
|
}
|
|
|
|
func (dm *deviceManager) AttachDevice(id string, dr api.DeviceReceiver) error {
|
|
dm.Lock()
|
|
defer dm.Unlock()
|
|
|
|
d, ok := dm.devices[id]
|
|
if !ok {
|
|
return ErrDeviceNotExist
|
|
}
|
|
|
|
if d.IsAttached() {
|
|
return ErrDeviceAttached
|
|
}
|
|
|
|
if err := d.Attach(dr); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (dm *deviceManager) DetachDevice(id string, dr api.DeviceReceiver) error {
|
|
dm.Lock()
|
|
defer dm.Unlock()
|
|
|
|
d, ok := dm.devices[id]
|
|
if !ok {
|
|
return ErrDeviceNotExist
|
|
}
|
|
if !d.IsAttached() {
|
|
return ErrDeviceNotAttached
|
|
}
|
|
|
|
if err := d.Detach(dr); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
func (dm *deviceManager) GetDeviceByID(id string) api.Device {
|
|
dm.RLock()
|
|
defer dm.RUnlock()
|
|
if d, ok := dm.devices[id]; ok {
|
|
return d
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (dm *deviceManager) GetAllDevices() []api.Device {
|
|
dm.RLock()
|
|
defer dm.RUnlock()
|
|
devices := []api.Device{}
|
|
for _, v := range dm.devices {
|
|
devices = append(devices, v)
|
|
}
|
|
return devices
|
|
}
|
|
|
|
func (dm *deviceManager) IsDeviceAttached(id string) bool {
|
|
dm.RLock()
|
|
defer dm.RUnlock()
|
|
d, ok := dm.devices[id]
|
|
if !ok {
|
|
return false
|
|
}
|
|
return d.IsAttached()
|
|
}
|