Skip to content

Commit

Permalink
kvm: Add VFIO device
Browse files Browse the repository at this point in the history
So far we've succeeded at making KVM and VFIO mostly unaware of each
other, but areas are cropping up where a connection beyond eventfds
and irqfds needs to be made.  This patch introduces a KVM-VFIO device
that is meant to be a gateway for such interaction.  The user creates
the device and can add and remove VFIO groups to it via file
descriptors.  When a group is added, KVM verifies the group is valid
and gets a reference to it via the VFIO external user interface.

Signed-off-by: Alex Williamson <[email protected]>
Signed-off-by: Paolo Bonzini <[email protected]>
  • Loading branch information
awilliam authored and bonzini committed Oct 30, 2013
1 parent 84cffe4 commit ec53500
Show file tree
Hide file tree
Showing 8 changed files with 257 additions and 1 deletion.
22 changes: 22 additions & 0 deletions Documentation/virtual/kvm/devices/vfio.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
VFIO virtual device
===================

Device types supported:
KVM_DEV_TYPE_VFIO

Only one VFIO instance may be created per VM. The created device
tracks VFIO groups in use by the VM and features of those groups
important to the correctness and acceleration of the VM. As groups
are enabled and disabled for use by the VM, KVM should be updated
about their presence. When registered with KVM, a reference to the
VFIO-group is held by KVM.

Groups:
KVM_DEV_VFIO_GROUP

KVM_DEV_VFIO_GROUP attributes:
KVM_DEV_VFIO_GROUP_ADD: Add a VFIO group to VFIO-KVM device tracking
KVM_DEV_VFIO_GROUP_DEL: Remove a VFIO group from VFIO-KVM device tracking

For each, kvm_device_attr.addr points to an int32_t file descriptor
for the VFIO group.
1 change: 1 addition & 0 deletions arch/x86/kvm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ config KVM
select PERF_EVENTS
select HAVE_KVM_MSI
select HAVE_KVM_CPU_RELAX_INTERCEPT
select KVM_VFIO
---help---
Support hosting fully virtualized guest machines using hardware
virtualization extensions. You will need a fairly recent
Expand Down
2 changes: 1 addition & 1 deletion arch/x86/kvm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ KVM := ../../../virt/kvm

kvm-y += $(KVM)/kvm_main.o $(KVM)/ioapic.o \
$(KVM)/coalesced_mmio.o $(KVM)/irq_comm.o \
$(KVM)/eventfd.o $(KVM)/irqchip.o
$(KVM)/eventfd.o $(KVM)/irqchip.o $(KVM)/vfio.o
kvm-$(CONFIG_KVM_DEVICE_ASSIGNMENT) += $(KVM)/assigned-dev.o $(KVM)/iommu.o
kvm-$(CONFIG_KVM_ASYNC_PF) += $(KVM)/async_pf.o

Expand Down
1 change: 1 addition & 0 deletions include/linux/kvm_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -1058,6 +1058,7 @@ struct kvm_device *kvm_device_from_filp(struct file *filp);

extern struct kvm_device_ops kvm_mpic_ops;
extern struct kvm_device_ops kvm_xics_ops;
extern struct kvm_device_ops kvm_vfio_ops;

#ifdef CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT

Expand Down
4 changes: 4 additions & 0 deletions include/uapi/linux/kvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,10 @@ struct kvm_device_attr {
#define KVM_DEV_TYPE_FSL_MPIC_20 1
#define KVM_DEV_TYPE_FSL_MPIC_42 2
#define KVM_DEV_TYPE_XICS 3
#define KVM_DEV_TYPE_VFIO 4
#define KVM_DEV_VFIO_GROUP 1
#define KVM_DEV_VFIO_GROUP_ADD 1
#define KVM_DEV_VFIO_GROUP_DEL 2

/*
* ioctls for VM fds
Expand Down
3 changes: 3 additions & 0 deletions virt/kvm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@ config HAVE_KVM_MSI

config HAVE_KVM_CPU_RELAX_INTERCEPT
bool

config KVM_VFIO
bool
5 changes: 5 additions & 0 deletions virt/kvm/kvm_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2270,6 +2270,11 @@ static int kvm_ioctl_create_device(struct kvm *kvm,
case KVM_DEV_TYPE_XICS:
ops = &kvm_xics_ops;
break;
#endif
#ifdef CONFIG_KVM_VFIO
case KVM_DEV_TYPE_VFIO:
ops = &kvm_vfio_ops;
break;
#endif
default:
return -ENODEV;
Expand Down
220 changes: 220 additions & 0 deletions virt/kvm/vfio.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
/*
* VFIO-KVM bridge pseudo device
*
* Copyright (C) 2013 Red Hat, Inc. All rights reserved.
* Author: Alex Williamson <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/

#include <linux/errno.h>
#include <linux/file.h>
#include <linux/kvm_host.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/vfio.h>

struct kvm_vfio_group {
struct list_head node;
struct vfio_group *vfio_group;
};

struct kvm_vfio {
struct list_head group_list;
struct mutex lock;
};

static struct vfio_group *kvm_vfio_group_get_external_user(struct file *filep)
{
struct vfio_group *vfio_group;
struct vfio_group *(*fn)(struct file *);

fn = symbol_get(vfio_group_get_external_user);
if (!fn)
return ERR_PTR(-EINVAL);

vfio_group = fn(filep);

symbol_put(vfio_group_get_external_user);

return vfio_group;
}

static void kvm_vfio_group_put_external_user(struct vfio_group *vfio_group)
{
void (*fn)(struct vfio_group *);

fn = symbol_get(vfio_group_put_external_user);
if (!fn)
return;

fn(vfio_group);

symbol_put(vfio_group_put_external_user);
}

static int kvm_vfio_set_group(struct kvm_device *dev, long attr, u64 arg)
{
struct kvm_vfio *kv = dev->private;
struct vfio_group *vfio_group;
struct kvm_vfio_group *kvg;
void __user *argp = (void __user *)arg;
struct fd f;
int32_t fd;
int ret;

switch (attr) {
case KVM_DEV_VFIO_GROUP_ADD:
if (get_user(fd, (int32_t __user *)argp))
return -EFAULT;

f = fdget(fd);
if (!f.file)
return -EBADF;

vfio_group = kvm_vfio_group_get_external_user(f.file);
fdput(f);

if (IS_ERR(vfio_group))
return PTR_ERR(vfio_group);

mutex_lock(&kv->lock);

list_for_each_entry(kvg, &kv->group_list, node) {
if (kvg->vfio_group == vfio_group) {
mutex_unlock(&kv->lock);
kvm_vfio_group_put_external_user(vfio_group);
return -EEXIST;
}
}

kvg = kzalloc(sizeof(*kvg), GFP_KERNEL);
if (!kvg) {
mutex_unlock(&kv->lock);
kvm_vfio_group_put_external_user(vfio_group);
return -ENOMEM;
}

list_add_tail(&kvg->node, &kv->group_list);
kvg->vfio_group = vfio_group;

mutex_unlock(&kv->lock);

return 0;

case KVM_DEV_VFIO_GROUP_DEL:
if (get_user(fd, (int32_t __user *)argp))
return -EFAULT;

f = fdget(fd);
if (!f.file)
return -EBADF;

vfio_group = kvm_vfio_group_get_external_user(f.file);
fdput(f);

if (IS_ERR(vfio_group))
return PTR_ERR(vfio_group);

ret = -ENOENT;

mutex_lock(&kv->lock);

list_for_each_entry(kvg, &kv->group_list, node) {
if (kvg->vfio_group != vfio_group)
continue;

list_del(&kvg->node);
kvm_vfio_group_put_external_user(kvg->vfio_group);
kfree(kvg);
ret = 0;
break;
}

mutex_unlock(&kv->lock);

kvm_vfio_group_put_external_user(vfio_group);

return ret;
}

return -ENXIO;
}

static int kvm_vfio_set_attr(struct kvm_device *dev,
struct kvm_device_attr *attr)
{
switch (attr->group) {
case KVM_DEV_VFIO_GROUP:
return kvm_vfio_set_group(dev, attr->attr, attr->addr);
}

return -ENXIO;
}

static int kvm_vfio_has_attr(struct kvm_device *dev,
struct kvm_device_attr *attr)
{
switch (attr->group) {
case KVM_DEV_VFIO_GROUP:
switch (attr->attr) {
case KVM_DEV_VFIO_GROUP_ADD:
case KVM_DEV_VFIO_GROUP_DEL:
return 0;
}

break;
}

return -ENXIO;
}

static void kvm_vfio_destroy(struct kvm_device *dev)
{
struct kvm_vfio *kv = dev->private;
struct kvm_vfio_group *kvg, *tmp;

list_for_each_entry_safe(kvg, tmp, &kv->group_list, node) {
kvm_vfio_group_put_external_user(kvg->vfio_group);
list_del(&kvg->node);
kfree(kvg);
}

kfree(kv);
kfree(dev); /* alloc by kvm_ioctl_create_device, free by .destroy */
}

static int kvm_vfio_create(struct kvm_device *dev, u32 type)
{
struct kvm_device *tmp;
struct kvm_vfio *kv;

/* Only one VFIO "device" per VM */
list_for_each_entry(tmp, &dev->kvm->devices, vm_node)
if (tmp->ops == &kvm_vfio_ops)
return -EBUSY;

kv = kzalloc(sizeof(*kv), GFP_KERNEL);
if (!kv)
return -ENOMEM;

INIT_LIST_HEAD(&kv->group_list);
mutex_init(&kv->lock);

dev->private = kv;

return 0;
}

struct kvm_device_ops kvm_vfio_ops = {
.name = "kvm-vfio",
.create = kvm_vfio_create,
.destroy = kvm_vfio_destroy,
.set_attr = kvm_vfio_set_attr,
.has_attr = kvm_vfio_has_attr,
};

0 comments on commit ec53500

Please sign in to comment.