mirror of
https://github.com/aljazceru/kata-containers.git
synced 2025-12-26 18:44:47 +01:00
Fixes #635 Remove `Hotplugged bool` field from device and add two new fields instead: * `RefCount`: how many references to this device. One device can be referenced(`NewDevice()`) many times by same/different container(s), two devices are regarded identical if they have same hostPath * `AttachCount`: how many times this device has been attached. A device can only be hotplugged once to the qemu, every new Attach command will add the AttachCount, and real `Detach` will be done only when `AttachCount == 0` Signed-off-by: Wei Zhang <zhangwei555@huawei.com>
255 lines
6.4 KiB
Go
255 lines
6.4 KiB
Go
// Copyright (c) 2017 Intel Corporation
|
|
// Copyright (c) 2018 Huawei Corporation
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
package manager
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/kata-containers/runtime/virtcontainers/device/api"
|
|
"github.com/kata-containers/runtime/virtcontainers/device/config"
|
|
"github.com/kata-containers/runtime/virtcontainers/device/drivers"
|
|
)
|
|
|
|
const fileMode0640 = os.FileMode(0640)
|
|
|
|
// dirMode is the permission bits used for creating a directory
|
|
const dirMode = os.FileMode(0750) | os.ModeDir
|
|
|
|
func TestNewDevice(t *testing.T) {
|
|
dm := &deviceManager{
|
|
blockDriver: VirtioBlock,
|
|
devices: make(map[string]api.Device),
|
|
}
|
|
savedSysDevPrefix := config.SysDevPrefix
|
|
|
|
major := int64(252)
|
|
minor := int64(3)
|
|
|
|
tmpDir, err := ioutil.TempDir("", "")
|
|
assert.Nil(t, err)
|
|
|
|
config.SysDevPrefix = tmpDir
|
|
defer func() {
|
|
os.RemoveAll(tmpDir)
|
|
config.SysDevPrefix = savedSysDevPrefix
|
|
}()
|
|
|
|
path := "/dev/vfio/2"
|
|
deviceInfo := config.DeviceInfo{
|
|
ContainerPath: "",
|
|
Major: major,
|
|
Minor: minor,
|
|
UID: 2,
|
|
GID: 2,
|
|
DevType: "c",
|
|
}
|
|
|
|
_, err = dm.NewDevice(deviceInfo)
|
|
assert.NotNil(t, err)
|
|
|
|
format := strconv.FormatInt(major, 10) + ":" + strconv.FormatInt(minor, 10)
|
|
ueventPathPrefix := filepath.Join(config.SysDevPrefix, "char", format)
|
|
ueventPath := filepath.Join(ueventPathPrefix, "uevent")
|
|
|
|
// Return true for non-existent /sys/dev path.
|
|
deviceInfo.ContainerPath = path
|
|
_, err = dm.NewDevice(deviceInfo)
|
|
assert.Nil(t, err)
|
|
|
|
err = os.MkdirAll(ueventPathPrefix, dirMode)
|
|
assert.Nil(t, err)
|
|
|
|
// Should return error for bad data in uevent file
|
|
content := []byte("nonkeyvaluedata")
|
|
err = ioutil.WriteFile(ueventPath, content, fileMode0640)
|
|
assert.Nil(t, err)
|
|
|
|
_, err = dm.NewDevice(deviceInfo)
|
|
assert.NotNil(t, err)
|
|
|
|
content = []byte("MAJOR=252\nMINOR=3\nDEVNAME=vfio/2")
|
|
err = ioutil.WriteFile(ueventPath, content, fileMode0640)
|
|
assert.Nil(t, err)
|
|
|
|
device, err := dm.NewDevice(deviceInfo)
|
|
assert.Nil(t, err)
|
|
|
|
vfioDev, ok := device.(*drivers.VFIODevice)
|
|
assert.True(t, ok)
|
|
assert.Equal(t, vfioDev.DeviceInfo.HostPath, path)
|
|
assert.Equal(t, vfioDev.DeviceInfo.ContainerPath, path)
|
|
assert.Equal(t, vfioDev.DeviceInfo.DevType, "c")
|
|
assert.Equal(t, vfioDev.DeviceInfo.Major, major)
|
|
assert.Equal(t, vfioDev.DeviceInfo.Minor, minor)
|
|
assert.Equal(t, vfioDev.DeviceInfo.UID, uint32(2))
|
|
assert.Equal(t, vfioDev.DeviceInfo.GID, uint32(2))
|
|
}
|
|
|
|
func TestAttachVFIODevice(t *testing.T) {
|
|
dm := &deviceManager{
|
|
blockDriver: VirtioBlock,
|
|
devices: make(map[string]api.Device),
|
|
}
|
|
tmpDir, err := ioutil.TempDir("", "")
|
|
assert.Nil(t, err)
|
|
os.RemoveAll(tmpDir)
|
|
|
|
testFDIOGroup := "2"
|
|
testDeviceBDFPath := "0000:00:1c.0"
|
|
|
|
devicesDir := filepath.Join(tmpDir, testFDIOGroup, "devices")
|
|
err = os.MkdirAll(devicesDir, dirMode)
|
|
assert.Nil(t, err)
|
|
|
|
deviceFile := filepath.Join(devicesDir, testDeviceBDFPath)
|
|
_, err = os.Create(deviceFile)
|
|
assert.Nil(t, err)
|
|
|
|
savedIOMMUPath := config.SysIOMMUPath
|
|
config.SysIOMMUPath = tmpDir
|
|
|
|
defer func() {
|
|
config.SysIOMMUPath = savedIOMMUPath
|
|
}()
|
|
|
|
path := filepath.Join(vfioPath, testFDIOGroup)
|
|
deviceInfo := config.DeviceInfo{
|
|
HostPath: path,
|
|
ContainerPath: path,
|
|
DevType: "c",
|
|
}
|
|
|
|
device, err := dm.NewDevice(deviceInfo)
|
|
assert.Nil(t, err)
|
|
_, ok := device.(*drivers.VFIODevice)
|
|
assert.True(t, ok)
|
|
|
|
devReceiver := &api.MockDeviceReceiver{}
|
|
err = device.Attach(devReceiver)
|
|
assert.Nil(t, err)
|
|
|
|
err = device.Detach(devReceiver)
|
|
assert.Nil(t, err)
|
|
}
|
|
|
|
func TestAttachGenericDevice(t *testing.T) {
|
|
dm := &deviceManager{
|
|
blockDriver: VirtioBlock,
|
|
devices: make(map[string]api.Device),
|
|
}
|
|
path := "/dev/tty2"
|
|
deviceInfo := config.DeviceInfo{
|
|
HostPath: path,
|
|
ContainerPath: path,
|
|
DevType: "c",
|
|
}
|
|
|
|
device, err := dm.NewDevice(deviceInfo)
|
|
assert.Nil(t, err)
|
|
_, ok := device.(*drivers.GenericDevice)
|
|
assert.True(t, ok)
|
|
|
|
devReceiver := &api.MockDeviceReceiver{}
|
|
err = device.Attach(devReceiver)
|
|
assert.Nil(t, err)
|
|
|
|
err = device.Detach(devReceiver)
|
|
assert.Nil(t, err)
|
|
}
|
|
|
|
func TestAttachBlockDevice(t *testing.T) {
|
|
dm := &deviceManager{
|
|
blockDriver: VirtioBlock,
|
|
devices: make(map[string]api.Device),
|
|
}
|
|
path := "/dev/hda"
|
|
deviceInfo := config.DeviceInfo{
|
|
HostPath: path,
|
|
ContainerPath: path,
|
|
DevType: "b",
|
|
}
|
|
|
|
devReceiver := &api.MockDeviceReceiver{}
|
|
device, err := dm.NewDevice(deviceInfo)
|
|
assert.Nil(t, err)
|
|
_, ok := device.(*drivers.BlockDevice)
|
|
assert.True(t, ok)
|
|
|
|
err = device.Attach(devReceiver)
|
|
assert.Nil(t, err)
|
|
|
|
err = device.Detach(devReceiver)
|
|
assert.Nil(t, err)
|
|
|
|
// test virtio SCSI driver
|
|
dm.blockDriver = VirtioSCSI
|
|
device, err = dm.NewDevice(deviceInfo)
|
|
assert.Nil(t, err)
|
|
err = device.Attach(devReceiver)
|
|
assert.Nil(t, err)
|
|
|
|
err = device.Detach(devReceiver)
|
|
assert.Nil(t, err)
|
|
}
|
|
|
|
func TestAttachDetachDevice(t *testing.T) {
|
|
dm := NewDeviceManager(VirtioSCSI, nil)
|
|
|
|
path := "/dev/hda"
|
|
deviceInfo := config.DeviceInfo{
|
|
HostPath: path,
|
|
ContainerPath: path,
|
|
DevType: "b",
|
|
}
|
|
|
|
devReceiver := &api.MockDeviceReceiver{}
|
|
device, err := dm.NewDevice(deviceInfo)
|
|
assert.Nil(t, err)
|
|
|
|
// attach non-exist device
|
|
err = dm.AttachDevice("non-exist", devReceiver)
|
|
assert.NotNil(t, err)
|
|
|
|
// attach device
|
|
err = dm.AttachDevice(device.DeviceID(), devReceiver)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, device.GetAttachCount(), uint(1), "attach device count should be 1")
|
|
// attach device again(twice)
|
|
err = dm.AttachDevice(device.DeviceID(), devReceiver)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, device.GetAttachCount(), uint(2), "attach device count should be 2")
|
|
|
|
attached := dm.IsDeviceAttached(device.DeviceID())
|
|
assert.True(t, attached)
|
|
|
|
// detach device
|
|
err = dm.DetachDevice(device.DeviceID(), devReceiver)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, device.GetAttachCount(), uint(1), "attach device count should be 1")
|
|
// detach device again(twice)
|
|
err = dm.DetachDevice(device.DeviceID(), devReceiver)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, device.GetAttachCount(), uint(0), "attach device count should be 0")
|
|
// detach device again should report error
|
|
err = dm.DetachDevice(device.DeviceID(), devReceiver)
|
|
assert.NotNil(t, err)
|
|
assert.Equal(t, err, ErrDeviceNotAttached, "")
|
|
assert.Equal(t, device.GetAttachCount(), uint(0), "attach device count should be 0")
|
|
|
|
attached = dm.IsDeviceAttached(device.DeviceID())
|
|
assert.False(t, attached)
|
|
|
|
err = dm.RemoveDevice(device.DeviceID())
|
|
assert.Nil(t, err)
|
|
}
|