aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Williamson <alex.williamson@redhat.com>2013-10-30 13:02:17 -0400
committerPaolo Bonzini <pbonzini@redhat.com>2013-10-30 14:02:03 -0400
commitec53500fae421e07c5d035918ca454a429732ef4 (patch)
tree3b9e0c67b9982f5b374e819a55cd510c14d481b8
parent84cffe499b9418d6c3b4de2ad9599cc2ec50c607 (diff)
kvm: Add VFIO device
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 <alex.williamson@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-rw-r--r--Documentation/virtual/kvm/devices/vfio.txt22
-rw-r--r--arch/x86/kvm/Kconfig1
-rw-r--r--arch/x86/kvm/Makefile2
-rw-r--r--include/linux/kvm_host.h1
-rw-r--r--include/uapi/linux/kvm.h4
-rw-r--r--virt/kvm/Kconfig3
-rw-r--r--virt/kvm/kvm_main.c5
-rw-r--r--virt/kvm/vfio.c220
8 files changed, 257 insertions, 1 deletions
diff --git a/Documentation/virtual/kvm/devices/vfio.txt b/Documentation/virtual/kvm/devices/vfio.txt
new file mode 100644
index 000000000000..ef51740c67ca
--- /dev/null
+++ b/Documentation/virtual/kvm/devices/vfio.txt
@@ -0,0 +1,22 @@
1VFIO virtual device
2===================
3
4Device types supported:
5 KVM_DEV_TYPE_VFIO
6
7Only one VFIO instance may be created per VM. The created device
8tracks VFIO groups in use by the VM and features of those groups
9important to the correctness and acceleration of the VM. As groups
10are enabled and disabled for use by the VM, KVM should be updated
11about their presence. When registered with KVM, a reference to the
12VFIO-group is held by KVM.
13
14Groups:
15 KVM_DEV_VFIO_GROUP
16
17KVM_DEV_VFIO_GROUP attributes:
18 KVM_DEV_VFIO_GROUP_ADD: Add a VFIO group to VFIO-KVM device tracking
19 KVM_DEV_VFIO_GROUP_DEL: Remove a VFIO group from VFIO-KVM device tracking
20
21For each, kvm_device_attr.addr points to an int32_t file descriptor
22for the VFIO group.
diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig
index a47a3e54b964..b89c5db2b832 100644
--- a/arch/x86/kvm/Kconfig
+++ b/arch/x86/kvm/Kconfig
@@ -38,6 +38,7 @@ config KVM
38 select PERF_EVENTS 38 select PERF_EVENTS
39 select HAVE_KVM_MSI 39 select HAVE_KVM_MSI
40 select HAVE_KVM_CPU_RELAX_INTERCEPT 40 select HAVE_KVM_CPU_RELAX_INTERCEPT
41 select KVM_VFIO
41 ---help--- 42 ---help---
42 Support hosting fully virtualized guest machines using hardware 43 Support hosting fully virtualized guest machines using hardware
43 virtualization extensions. You will need a fairly recent 44 virtualization extensions. You will need a fairly recent
diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile
index bf4fb04d0112..25d22b2d6509 100644
--- a/arch/x86/kvm/Makefile
+++ b/arch/x86/kvm/Makefile
@@ -9,7 +9,7 @@ KVM := ../../../virt/kvm
9 9
10kvm-y += $(KVM)/kvm_main.o $(KVM)/ioapic.o \ 10kvm-y += $(KVM)/kvm_main.o $(KVM)/ioapic.o \
11 $(KVM)/coalesced_mmio.o $(KVM)/irq_comm.o \ 11 $(KVM)/coalesced_mmio.o $(KVM)/irq_comm.o \
12 $(KVM)/eventfd.o $(KVM)/irqchip.o 12 $(KVM)/eventfd.o $(KVM)/irqchip.o $(KVM)/vfio.o
13kvm-$(CONFIG_KVM_DEVICE_ASSIGNMENT) += $(KVM)/assigned-dev.o $(KVM)/iommu.o 13kvm-$(CONFIG_KVM_DEVICE_ASSIGNMENT) += $(KVM)/assigned-dev.o $(KVM)/iommu.o
14kvm-$(CONFIG_KVM_ASYNC_PF) += $(KVM)/async_pf.o 14kvm-$(CONFIG_KVM_ASYNC_PF) += $(KVM)/async_pf.o
15 15
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index c9d4236ab442..7beddbd38ac7 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -1058,6 +1058,7 @@ struct kvm_device *kvm_device_from_filp(struct file *filp);
1058 1058
1059extern struct kvm_device_ops kvm_mpic_ops; 1059extern struct kvm_device_ops kvm_mpic_ops;
1060extern struct kvm_device_ops kvm_xics_ops; 1060extern struct kvm_device_ops kvm_xics_ops;
1061extern struct kvm_device_ops kvm_vfio_ops;
1061 1062
1062#ifdef CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT 1063#ifdef CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT
1063 1064
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 32c60b98ddf8..509cfbfe9658 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -845,6 +845,10 @@ struct kvm_device_attr {
845#define KVM_DEV_TYPE_FSL_MPIC_20 1 845#define KVM_DEV_TYPE_FSL_MPIC_20 1
846#define KVM_DEV_TYPE_FSL_MPIC_42 2 846#define KVM_DEV_TYPE_FSL_MPIC_42 2
847#define KVM_DEV_TYPE_XICS 3 847#define KVM_DEV_TYPE_XICS 3
848#define KVM_DEV_TYPE_VFIO 4
849#define KVM_DEV_VFIO_GROUP 1
850#define KVM_DEV_VFIO_GROUP_ADD 1
851#define KVM_DEV_VFIO_GROUP_DEL 2
848 852
849/* 853/*
850 * ioctls for VM fds 854 * ioctls for VM fds
diff --git a/virt/kvm/Kconfig b/virt/kvm/Kconfig
index 779262f59e25..fbe1a48bd629 100644
--- a/virt/kvm/Kconfig
+++ b/virt/kvm/Kconfig
@@ -27,3 +27,6 @@ config HAVE_KVM_MSI
27 27
28config HAVE_KVM_CPU_RELAX_INTERCEPT 28config HAVE_KVM_CPU_RELAX_INTERCEPT
29 bool 29 bool
30
31config KVM_VFIO
32 bool
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 9ca014da134d..82c4047aa0e3 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -2271,6 +2271,11 @@ static int kvm_ioctl_create_device(struct kvm *kvm,
2271 ops = &kvm_xics_ops; 2271 ops = &kvm_xics_ops;
2272 break; 2272 break;
2273#endif 2273#endif
2274#ifdef CONFIG_KVM_VFIO
2275 case KVM_DEV_TYPE_VFIO:
2276 ops = &kvm_vfio_ops;
2277 break;
2278#endif
2274 default: 2279 default:
2275 return -ENODEV; 2280 return -ENODEV;
2276 } 2281 }
diff --git a/virt/kvm/vfio.c b/virt/kvm/vfio.c
new file mode 100644
index 000000000000..597c258245ea
--- /dev/null
+++ b/virt/kvm/vfio.c
@@ -0,0 +1,220 @@
1/*
2 * VFIO-KVM bridge pseudo device
3 *
4 * Copyright (C) 2013 Red Hat, Inc. All rights reserved.
5 * Author: Alex Williamson <alex.williamson@redhat.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/errno.h>
13#include <linux/file.h>
14#include <linux/kvm_host.h>
15#include <linux/list.h>
16#include <linux/module.h>
17#include <linux/mutex.h>
18#include <linux/slab.h>
19#include <linux/uaccess.h>
20#include <linux/vfio.h>
21
22struct kvm_vfio_group {
23 struct list_head node;
24 struct vfio_group *vfio_group;
25};
26
27struct kvm_vfio {
28 struct list_head group_list;
29 struct mutex lock;
30};
31
32static struct vfio_group *kvm_vfio_group_get_external_user(struct file *filep)
33{
34 struct vfio_group *vfio_group;
35 struct vfio_group *(*fn)(struct file *);
36
37 fn = symbol_get(vfio_group_get_external_user);
38 if (!fn)
39 return ERR_PTR(-EINVAL);
40
41 vfio_group = fn(filep);
42
43 symbol_put(vfio_group_get_external_user);
44
45 return vfio_group;
46}
47
48static void kvm_vfio_group_put_external_user(struct vfio_group *vfio_group)
49{
50 void (*fn)(struct vfio_group *);
51
52 fn = symbol_get(vfio_group_put_external_user);
53 if (!fn)
54 return;
55
56 fn(vfio_group);
57
58 symbol_put(vfio_group_put_external_user);
59}
60
61static int kvm_vfio_set_group(struct kvm_device *dev, long attr, u64 arg)
62{
63 struct kvm_vfio *kv = dev->private;
64 struct vfio_group *vfio_group;
65 struct kvm_vfio_group *kvg;
66 void __user *argp = (void __user *)arg;
67 struct fd f;
68 int32_t fd;
69 int ret;
70
71 switch (attr) {
72 case KVM_DEV_VFIO_GROUP_ADD:
73 if (get_user(fd, (int32_t __user *)argp))
74 return -EFAULT;
75
76 f = fdget(fd);
77 if (!f.file)
78 return -EBADF;
79
80 vfio_group = kvm_vfio_group_get_external_user(f.file);
81 fdput(f);
82
83 if (IS_ERR(vfio_group))
84 return PTR_ERR(vfio_group);
85
86 mutex_lock(&kv->lock);
87
88 list_for_each_entry(kvg, &kv->group_list, node) {
89 if (kvg->vfio_group == vfio_group) {
90 mutex_unlock(&kv->lock);
91 kvm_vfio_group_put_external_user(vfio_group);
92 return -EEXIST;
93 }
94 }
95
96 kvg = kzalloc(sizeof(*kvg), GFP_KERNEL);
97 if (!kvg) {
98 mutex_unlock(&kv->lock);
99 kvm_vfio_group_put_external_user(vfio_group);
100 return -ENOMEM;
101 }
102
103 list_add_tail(&kvg->node, &kv->group_list);
104 kvg->vfio_group = vfio_group;
105
106 mutex_unlock(&kv->lock);
107
108 return 0;
109
110 case KVM_DEV_VFIO_GROUP_DEL:
111 if (get_user(fd, (int32_t __user *)argp))
112 return -EFAULT;
113
114 f = fdget(fd);
115 if (!f.file)
116 return -EBADF;
117
118 vfio_group = kvm_vfio_group_get_external_user(f.file);
119 fdput(f);
120
121 if (IS_ERR(vfio_group))
122 return PTR_ERR(vfio_group);
123
124 ret = -ENOENT;
125
126 mutex_lock(&kv->lock);
127
128 list_for_each_entry(kvg, &kv->group_list, node) {
129 if (kvg->vfio_group != vfio_group)
130 continue;
131
132 list_del(&kvg->node);
133 kvm_vfio_group_put_external_user(kvg->vfio_group);
134 kfree(kvg);
135 ret = 0;
136 break;
137 }
138
139 mutex_unlock(&kv->lock);
140
141 kvm_vfio_group_put_external_user(vfio_group);
142
143 return ret;
144 }
145
146 return -ENXIO;
147}
148
149static int kvm_vfio_set_attr(struct kvm_device *dev,
150 struct kvm_device_attr *attr)
151{
152 switch (attr->group) {
153 case KVM_DEV_VFIO_GROUP:
154 return kvm_vfio_set_group(dev, attr->attr, attr->addr);
155 }
156
157 return -ENXIO;
158}
159
160static int kvm_vfio_has_attr(struct kvm_device *dev,
161 struct kvm_device_attr *attr)
162{
163 switch (attr->group) {
164 case KVM_DEV_VFIO_GROUP:
165 switch (attr->attr) {
166 case KVM_DEV_VFIO_GROUP_ADD:
167 case KVM_DEV_VFIO_GROUP_DEL:
168 return 0;
169 }
170
171 break;
172 }
173
174 return -ENXIO;
175}
176
177static void kvm_vfio_destroy(struct kvm_device *dev)
178{
179 struct kvm_vfio *kv = dev->private;
180 struct kvm_vfio_group *kvg, *tmp;
181
182 list_for_each_entry_safe(kvg, tmp, &kv->group_list, node) {
183 kvm_vfio_group_put_external_user(kvg->vfio_group);
184 list_del(&kvg->node);
185 kfree(kvg);
186 }
187
188 kfree(kv);
189 kfree(dev); /* alloc by kvm_ioctl_create_device, free by .destroy */
190}
191
192static int kvm_vfio_create(struct kvm_device *dev, u32 type)
193{
194 struct kvm_device *tmp;
195 struct kvm_vfio *kv;
196
197 /* Only one VFIO "device" per VM */
198 list_for_each_entry(tmp, &dev->kvm->devices, vm_node)
199 if (tmp->ops == &kvm_vfio_ops)
200 return -EBUSY;
201
202 kv = kzalloc(sizeof(*kv), GFP_KERNEL);
203 if (!kv)
204 return -ENOMEM;
205
206 INIT_LIST_HEAD(&kv->group_list);
207 mutex_init(&kv->lock);
208
209 dev->private = kv;
210
211 return 0;
212}
213
214struct kvm_device_ops kvm_vfio_ops = {
215 .name = "kvm-vfio",
216 .create = kvm_vfio_create,
217 .destroy = kvm_vfio_destroy,
218 .set_attr = kvm_vfio_set_attr,
219 .has_attr = kvm_vfio_has_attr,
220};