在qemu中hw/vfio/common.c的vfio_get_device
int vfio_get_device(VFIOGroup *group, const char *name,
VFIODevice *vbasedev, Error **errp)
{
struct vfio_device_info dev_info = { .argsz = sizeof(dev_info) };
int ret, fd;
fd = ioctl(group->fd, VFIO_GROUP_GET_DEVICE_FD, name);
if (fd < 0) {
error_setg_errno(errp, errno, "error getting device from group %d",
group->groupid);
error_append_hint(errp,
"Verify all devices in group %d are bound to vfio-<bus> "
"or pci-stub and not already in use\n", group->groupid);
return fd;
}
}
来获得device,从vfio_get_device 也可以看出VFIO_GROUP_GET_DEVICE_FD 这个ioctl是在group中实现,而在kernel中group的ioctl如下:
static long vfio_group_fops_unl_ioctl(struct file *filep,
unsigned int cmd, unsigned long arg)
{
struct vfio_group *group = filep->private_data;
long ret = -ENOTTY;
switch (cmd) {
case VFIO_GROUP_GET_DEVICE_FD:
{
char *buf;
buf = strndup_user((const char __user *)arg, PAGE_SIZE);
if (IS_ERR(buf))
return PTR_ERR(buf);
ret = vfio_group_get_device_fd(group, buf);
kfree(buf);
break;
}
}
通过strndup_user 将用户空间的name copy到buf中,这个buf中就是一个字符串,代表具体的device.
static int vfio_group_get_device_fd(struct vfio_group *group, char *buf)
{
struct vfio_device *device;
struct file *filep;
int ret;
if (0 == atomic_read(&group->container_users) ||
!group->container->iommu_driver || !vfio_group_viable(group))
return -EINVAL;
if (group->noiommu && !capable(CAP_SYS_RAWIO))
return -EPERM;
//通过遍历group->device_list 来找vfio_device,匹配的规则是(!strcmp(dev_name(it->dev), buf)) 即名字相等.
device = vfio_device_get_from_name(group, buf);
if (!device)
return -ENODEV;
//调用device的open函数
ret = device->ops->open(device->device_data);
if (ret) {
vfio_device_put(device);
return ret;
}
/*
* We can't use anon_inode_getfd() because we need to modify
* the f_mode flags directly to allow more than just ioctls
*/
//找到没有使用的fd
ret = get_unused_fd_flags(O_CLOEXEC);
if (ret < 0) {
device->ops->release(device->device_data);
vfio_device_put(device);
return ret;
}
//使用匿名node将device和vfio_device_fops 想关联
filep = anon_inode_getfile("[vfio-device]", &vfio_device_fops,
device, O_RDWR);
if (IS_ERR(filep)) {
put_unused_fd(ret);
ret = PTR_ERR(filep);
device->ops->release(device->device_data);
vfio_device_put(device);
return ret;
}
/*
* TODO: add an anon_inode interface to do this.
* Appears to be missing by lack of need rather than
* explicitly prevented. Now there's need.
*/
filep->f_mode |= (FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
atomic_inc(&group->container_users);
//将fd和通过匿名node返回的filep关联.
fd_install(ret, filep);
if (group->noiommu)
dev_warn(device->dev, "vfio-noiommu device opened by user "
"(%s:%d)\n", current->comm, task_pid_nr(current));
return ret;
}
其中vfio_device_get_from_name的实现如下:可以看到是从group->device_list 中遍历查找device,匹配的规则就是name相等就行,所有的device都是挂在group->device_list 这个list中。
static struct vfio_device *vfio_device_get_from_name(struct vfio_group *group,
char *buf)
{
struct vfio_device *it, *device = NULL;
mutex_lock(&group->device_lock);
list_for_each_entry(it, &group->device_list, group_next) {
if (!strcmp(dev_name(it->dev), buf)) {
device = it;
vfio_device_get(device);
break;
}
}
mutex_unlock(&group->device_lock);
return device;
}
int vfio_get_device(VFIOGroup *group, const char *name,
VFIODevice *vbasedev, Error **errp)
{
struct vfio_device_info dev_info = { .argsz = sizeof(dev_info) };
int ret, fd;
fd = ioctl(group->fd, VFIO_GROUP_GET_DEVICE_FD, name);
if (fd < 0) {
error_setg_errno(errp, errno, "error getting device from group %d",
group->groupid);
error_append_hint(errp,
"Verify all devices in group %d are bound to vfio-<bus> "
"or pci-stub and not already in use\n", group->groupid);
return fd;
}
}
来获得device,从vfio_get_device 也可以看出VFIO_GROUP_GET_DEVICE_FD 这个ioctl是在group中实现,而在kernel中group的ioctl如下:
static long vfio_group_fops_unl_ioctl(struct file *filep,
unsigned int cmd, unsigned long arg)
{
struct vfio_group *group = filep->private_data;
long ret = -ENOTTY;
switch (cmd) {
case VFIO_GROUP_GET_DEVICE_FD:
{
char *buf;
buf = strndup_user((const char __user *)arg, PAGE_SIZE);
if (IS_ERR(buf))
return PTR_ERR(buf);
ret = vfio_group_get_device_fd(group, buf);
kfree(buf);
break;
}
}
通过strndup_user 将用户空间的name copy到buf中,这个buf中就是一个字符串,代表具体的device.
static int vfio_group_get_device_fd(struct vfio_group *group, char *buf)
{
struct vfio_device *device;
struct file *filep;
int ret;
if (0 == atomic_read(&group->container_users) ||
!group->container->iommu_driver || !vfio_group_viable(group))
return -EINVAL;
if (group->noiommu && !capable(CAP_SYS_RAWIO))
return -EPERM;
//通过遍历group->device_list 来找vfio_device,匹配的规则是(!strcmp(dev_name(it->dev), buf)) 即名字相等.
device = vfio_device_get_from_name(group, buf);
if (!device)
return -ENODEV;
//调用device的open函数
ret = device->ops->open(device->device_data);
if (ret) {
vfio_device_put(device);
return ret;
}
/*
* We can't use anon_inode_getfd() because we need to modify
* the f_mode flags directly to allow more than just ioctls
*/
//找到没有使用的fd
ret = get_unused_fd_flags(O_CLOEXEC);
if (ret < 0) {
device->ops->release(device->device_data);
vfio_device_put(device);
return ret;
}
//使用匿名node将device和vfio_device_fops 想关联
filep = anon_inode_getfile("[vfio-device]", &vfio_device_fops,
device, O_RDWR);
if (IS_ERR(filep)) {
put_unused_fd(ret);
ret = PTR_ERR(filep);
device->ops->release(device->device_data);
vfio_device_put(device);
return ret;
}
/*
* TODO: add an anon_inode interface to do this.
* Appears to be missing by lack of need rather than
* explicitly prevented. Now there's need.
*/
filep->f_mode |= (FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
atomic_inc(&group->container_users);
//将fd和通过匿名node返回的filep关联.
fd_install(ret, filep);
if (group->noiommu)
dev_warn(device->dev, "vfio-noiommu device opened by user "
"(%s:%d)\n", current->comm, task_pid_nr(current));
return ret;
}
其中vfio_device_get_from_name的实现如下:可以看到是从group->device_list 中遍历查找device,匹配的规则就是name相等就行,所有的device都是挂在group->device_list 这个list中。
static struct vfio_device *vfio_device_get_from_name(struct vfio_group *group,
char *buf)
{
struct vfio_device *it, *device = NULL;
mutex_lock(&group->device_lock);
list_for_each_entry(it, &group->device_list, group_next) {
if (!strcmp(dev_name(it->dev), buf)) {
device = it;
vfio_device_get(device);
break;
}
}
mutex_unlock(&group->device_lock);
return device;
}