xref: /openbmc/linux/virt/kvm/coalesced_mmio.c (revision 4bdf0bb7)
1 /*
2  * KVM coalesced MMIO
3  *
4  * Copyright (c) 2008 Bull S.A.S.
5  *
6  *  Author: Laurent Vivier <Laurent.Vivier@bull.net>
7  *
8  */
9 
10 #include "iodev.h"
11 
12 #include <linux/kvm_host.h>
13 #include <linux/kvm.h>
14 
15 #include "coalesced_mmio.h"
16 
17 static inline struct kvm_coalesced_mmio_dev *to_mmio(struct kvm_io_device *dev)
18 {
19 	return container_of(dev, struct kvm_coalesced_mmio_dev, dev);
20 }
21 
22 static int coalesced_mmio_in_range(struct kvm_coalesced_mmio_dev *dev,
23 				   gpa_t addr, int len)
24 {
25 	struct kvm_coalesced_mmio_zone *zone;
26 	struct kvm_coalesced_mmio_ring *ring;
27 	unsigned avail;
28 	int i;
29 
30 	/* Are we able to batch it ? */
31 
32 	/* last is the first free entry
33 	 * check if we don't meet the first used entry
34 	 * there is always one unused entry in the buffer
35 	 */
36 	ring = dev->kvm->coalesced_mmio_ring;
37 	avail = (ring->first - ring->last - 1) % KVM_COALESCED_MMIO_MAX;
38 	if (avail < KVM_MAX_VCPUS) {
39 		/* full */
40 		return 0;
41 	}
42 
43 	/* is it in a batchable area ? */
44 
45 	for (i = 0; i < dev->nb_zones; i++) {
46 		zone = &dev->zone[i];
47 
48 		/* (addr,len) is fully included in
49 		 * (zone->addr, zone->size)
50 		 */
51 
52 		if (zone->addr <= addr &&
53 		    addr + len <= zone->addr + zone->size)
54 			return 1;
55 	}
56 	return 0;
57 }
58 
59 static int coalesced_mmio_write(struct kvm_io_device *this,
60 				gpa_t addr, int len, const void *val)
61 {
62 	struct kvm_coalesced_mmio_dev *dev = to_mmio(this);
63 	struct kvm_coalesced_mmio_ring *ring = dev->kvm->coalesced_mmio_ring;
64 	if (!coalesced_mmio_in_range(dev, addr, len))
65 		return -EOPNOTSUPP;
66 
67 	spin_lock(&dev->lock);
68 
69 	/* copy data in first free entry of the ring */
70 
71 	ring->coalesced_mmio[ring->last].phys_addr = addr;
72 	ring->coalesced_mmio[ring->last].len = len;
73 	memcpy(ring->coalesced_mmio[ring->last].data, val, len);
74 	smp_wmb();
75 	ring->last = (ring->last + 1) % KVM_COALESCED_MMIO_MAX;
76 	spin_unlock(&dev->lock);
77 	return 0;
78 }
79 
80 static void coalesced_mmio_destructor(struct kvm_io_device *this)
81 {
82 	struct kvm_coalesced_mmio_dev *dev = to_mmio(this);
83 
84 	kfree(dev);
85 }
86 
87 static const struct kvm_io_device_ops coalesced_mmio_ops = {
88 	.write      = coalesced_mmio_write,
89 	.destructor = coalesced_mmio_destructor,
90 };
91 
92 int kvm_coalesced_mmio_init(struct kvm *kvm)
93 {
94 	struct kvm_coalesced_mmio_dev *dev;
95 	int ret;
96 
97 	dev = kzalloc(sizeof(struct kvm_coalesced_mmio_dev), GFP_KERNEL);
98 	if (!dev)
99 		return -ENOMEM;
100 	spin_lock_init(&dev->lock);
101 	kvm_iodevice_init(&dev->dev, &coalesced_mmio_ops);
102 	dev->kvm = kvm;
103 	kvm->coalesced_mmio_dev = dev;
104 
105 	ret = kvm_io_bus_register_dev(kvm, &kvm->mmio_bus, &dev->dev);
106 	if (ret < 0)
107 		kfree(dev);
108 
109 	return ret;
110 }
111 
112 int kvm_vm_ioctl_register_coalesced_mmio(struct kvm *kvm,
113 				         struct kvm_coalesced_mmio_zone *zone)
114 {
115 	struct kvm_coalesced_mmio_dev *dev = kvm->coalesced_mmio_dev;
116 
117 	if (dev == NULL)
118 		return -EINVAL;
119 
120 	down_write(&kvm->slots_lock);
121 	if (dev->nb_zones >= KVM_COALESCED_MMIO_ZONE_MAX) {
122 		up_write(&kvm->slots_lock);
123 		return -ENOBUFS;
124 	}
125 
126 	dev->zone[dev->nb_zones] = *zone;
127 	dev->nb_zones++;
128 
129 	up_write(&kvm->slots_lock);
130 	return 0;
131 }
132 
133 int kvm_vm_ioctl_unregister_coalesced_mmio(struct kvm *kvm,
134 					   struct kvm_coalesced_mmio_zone *zone)
135 {
136 	int i;
137 	struct kvm_coalesced_mmio_dev *dev = kvm->coalesced_mmio_dev;
138 	struct kvm_coalesced_mmio_zone *z;
139 
140 	if (dev == NULL)
141 		return -EINVAL;
142 
143 	down_write(&kvm->slots_lock);
144 
145 	i = dev->nb_zones;
146 	while(i) {
147 		z = &dev->zone[i - 1];
148 
149 		/* unregister all zones
150 		 * included in (zone->addr, zone->size)
151 		 */
152 
153 		if (zone->addr <= z->addr &&
154 		    z->addr + z->size <= zone->addr + zone->size) {
155 			dev->nb_zones--;
156 			*z = dev->zone[dev->nb_zones];
157 		}
158 		i--;
159 	}
160 
161 	up_write(&kvm->slots_lock);
162 
163 	return 0;
164 }
165