1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * ACRN HSM eventfd - use eventfd objects to signal expected I/O requests
4 *
5 * Copyright (C) 2020 Intel Corporation. All rights reserved.
6 *
7 * Authors:
8 * Shuo Liu <shuo.a.liu@intel.com>
9 * Yakui Zhao <yakui.zhao@intel.com>
10 */
11
12 #include <linux/eventfd.h>
13 #include <linux/slab.h>
14
15 #include "acrn_drv.h"
16
17 /**
18 * struct hsm_ioeventfd - Properties of HSM ioeventfd
19 * @list: Entry within &acrn_vm.ioeventfds of ioeventfds of a VM
20 * @eventfd: Eventfd of the HSM ioeventfd
21 * @addr: Address of I/O range
22 * @data: Data for matching
23 * @length: Length of I/O range
24 * @type: Type of I/O range (ACRN_IOREQ_TYPE_MMIO/ACRN_IOREQ_TYPE_PORTIO)
25 * @wildcard: Data matching or not
26 */
27 struct hsm_ioeventfd {
28 struct list_head list;
29 struct eventfd_ctx *eventfd;
30 u64 addr;
31 u64 data;
32 int length;
33 int type;
34 bool wildcard;
35 };
36
ioreq_type_from_flags(int flags)37 static inline int ioreq_type_from_flags(int flags)
38 {
39 return flags & ACRN_IOEVENTFD_FLAG_PIO ?
40 ACRN_IOREQ_TYPE_PORTIO : ACRN_IOREQ_TYPE_MMIO;
41 }
42
acrn_ioeventfd_shutdown(struct acrn_vm * vm,struct hsm_ioeventfd * p)43 static void acrn_ioeventfd_shutdown(struct acrn_vm *vm, struct hsm_ioeventfd *p)
44 {
45 lockdep_assert_held(&vm->ioeventfds_lock);
46
47 eventfd_ctx_put(p->eventfd);
48 list_del(&p->list);
49 kfree(p);
50 }
51
hsm_ioeventfd_is_conflict(struct acrn_vm * vm,struct hsm_ioeventfd * ioeventfd)52 static bool hsm_ioeventfd_is_conflict(struct acrn_vm *vm,
53 struct hsm_ioeventfd *ioeventfd)
54 {
55 struct hsm_ioeventfd *p;
56
57 lockdep_assert_held(&vm->ioeventfds_lock);
58
59 /* Either one is wildcard, the data matching will be skipped. */
60 list_for_each_entry(p, &vm->ioeventfds, list)
61 if (p->eventfd == ioeventfd->eventfd &&
62 p->addr == ioeventfd->addr &&
63 p->type == ioeventfd->type &&
64 (p->wildcard || ioeventfd->wildcard ||
65 p->data == ioeventfd->data))
66 return true;
67
68 return false;
69 }
70
71 /*
72 * Assign an eventfd to a VM and create a HSM ioeventfd associated with the
73 * eventfd. The properties of the HSM ioeventfd are built from a &struct
74 * acrn_ioeventfd.
75 */
acrn_ioeventfd_assign(struct acrn_vm * vm,struct acrn_ioeventfd * args)76 static int acrn_ioeventfd_assign(struct acrn_vm *vm,
77 struct acrn_ioeventfd *args)
78 {
79 struct eventfd_ctx *eventfd;
80 struct hsm_ioeventfd *p;
81 int ret;
82
83 /* Check for range overflow */
84 if (args->addr + args->len < args->addr)
85 return -EINVAL;
86
87 /*
88 * Currently, acrn_ioeventfd is used to support vhost. 1,2,4,8 width
89 * accesses can cover vhost's requirements.
90 */
91 if (!(args->len == 1 || args->len == 2 ||
92 args->len == 4 || args->len == 8))
93 return -EINVAL;
94
95 eventfd = eventfd_ctx_fdget(args->fd);
96 if (IS_ERR(eventfd))
97 return PTR_ERR(eventfd);
98
99 p = kzalloc(sizeof(*p), GFP_KERNEL);
100 if (!p) {
101 ret = -ENOMEM;
102 goto fail;
103 }
104
105 INIT_LIST_HEAD(&p->list);
106 p->addr = args->addr;
107 p->length = args->len;
108 p->eventfd = eventfd;
109 p->type = ioreq_type_from_flags(args->flags);
110
111 /*
112 * ACRN_IOEVENTFD_FLAG_DATAMATCH flag is set in virtio 1.0 support, the
113 * writing of notification register of each virtqueue may trigger the
114 * notification. There is no data matching requirement.
115 */
116 if (args->flags & ACRN_IOEVENTFD_FLAG_DATAMATCH)
117 p->data = args->data;
118 else
119 p->wildcard = true;
120
121 mutex_lock(&vm->ioeventfds_lock);
122
123 if (hsm_ioeventfd_is_conflict(vm, p)) {
124 ret = -EEXIST;
125 goto unlock_fail;
126 }
127
128 /* register the I/O range into ioreq client */
129 ret = acrn_ioreq_range_add(vm->ioeventfd_client, p->type,
130 p->addr, p->addr + p->length - 1);
131 if (ret < 0)
132 goto unlock_fail;
133
134 list_add_tail(&p->list, &vm->ioeventfds);
135 mutex_unlock(&vm->ioeventfds_lock);
136
137 return 0;
138
139 unlock_fail:
140 mutex_unlock(&vm->ioeventfds_lock);
141 kfree(p);
142 fail:
143 eventfd_ctx_put(eventfd);
144 return ret;
145 }
146
acrn_ioeventfd_deassign(struct acrn_vm * vm,struct acrn_ioeventfd * args)147 static int acrn_ioeventfd_deassign(struct acrn_vm *vm,
148 struct acrn_ioeventfd *args)
149 {
150 struct hsm_ioeventfd *p;
151 struct eventfd_ctx *eventfd;
152
153 eventfd = eventfd_ctx_fdget(args->fd);
154 if (IS_ERR(eventfd))
155 return PTR_ERR(eventfd);
156
157 mutex_lock(&vm->ioeventfds_lock);
158 list_for_each_entry(p, &vm->ioeventfds, list) {
159 if (p->eventfd != eventfd)
160 continue;
161
162 acrn_ioreq_range_del(vm->ioeventfd_client, p->type,
163 p->addr, p->addr + p->length - 1);
164 acrn_ioeventfd_shutdown(vm, p);
165 break;
166 }
167 mutex_unlock(&vm->ioeventfds_lock);
168
169 eventfd_ctx_put(eventfd);
170 return 0;
171 }
172
hsm_ioeventfd_match(struct acrn_vm * vm,u64 addr,u64 data,int len,int type)173 static struct hsm_ioeventfd *hsm_ioeventfd_match(struct acrn_vm *vm, u64 addr,
174 u64 data, int len, int type)
175 {
176 struct hsm_ioeventfd *p = NULL;
177
178 lockdep_assert_held(&vm->ioeventfds_lock);
179
180 list_for_each_entry(p, &vm->ioeventfds, list) {
181 if (p->type == type && p->addr == addr && p->length >= len &&
182 (p->wildcard || p->data == data))
183 return p;
184 }
185
186 return NULL;
187 }
188
acrn_ioeventfd_handler(struct acrn_ioreq_client * client,struct acrn_io_request * req)189 static int acrn_ioeventfd_handler(struct acrn_ioreq_client *client,
190 struct acrn_io_request *req)
191 {
192 struct hsm_ioeventfd *p;
193 u64 addr, val;
194 int size;
195
196 if (req->type == ACRN_IOREQ_TYPE_MMIO) {
197 /*
198 * I/O requests are dispatched by range check only, so a
199 * acrn_ioreq_client need process both READ and WRITE accesses
200 * of same range. READ accesses are safe to be ignored here
201 * because virtio PCI devices write the notify registers for
202 * notification.
203 */
204 if (req->reqs.mmio_request.direction == ACRN_IOREQ_DIR_READ) {
205 /* reading does nothing and return 0 */
206 req->reqs.mmio_request.value = 0;
207 return 0;
208 }
209 addr = req->reqs.mmio_request.address;
210 size = req->reqs.mmio_request.size;
211 val = req->reqs.mmio_request.value;
212 } else {
213 if (req->reqs.pio_request.direction == ACRN_IOREQ_DIR_READ) {
214 /* reading does nothing and return 0 */
215 req->reqs.pio_request.value = 0;
216 return 0;
217 }
218 addr = req->reqs.pio_request.address;
219 size = req->reqs.pio_request.size;
220 val = req->reqs.pio_request.value;
221 }
222
223 mutex_lock(&client->vm->ioeventfds_lock);
224 p = hsm_ioeventfd_match(client->vm, addr, val, size, req->type);
225 if (p)
226 eventfd_signal(p->eventfd);
227 mutex_unlock(&client->vm->ioeventfds_lock);
228
229 return 0;
230 }
231
acrn_ioeventfd_config(struct acrn_vm * vm,struct acrn_ioeventfd * args)232 int acrn_ioeventfd_config(struct acrn_vm *vm, struct acrn_ioeventfd *args)
233 {
234 int ret;
235
236 if (args->flags & ACRN_IOEVENTFD_FLAG_DEASSIGN)
237 ret = acrn_ioeventfd_deassign(vm, args);
238 else
239 ret = acrn_ioeventfd_assign(vm, args);
240
241 return ret;
242 }
243
acrn_ioeventfd_init(struct acrn_vm * vm)244 int acrn_ioeventfd_init(struct acrn_vm *vm)
245 {
246 char name[ACRN_NAME_LEN];
247
248 mutex_init(&vm->ioeventfds_lock);
249 INIT_LIST_HEAD(&vm->ioeventfds);
250 snprintf(name, sizeof(name), "ioeventfd-%u", vm->vmid);
251 vm->ioeventfd_client = acrn_ioreq_client_create(vm,
252 acrn_ioeventfd_handler,
253 NULL, false, name);
254 if (!vm->ioeventfd_client) {
255 dev_err(acrn_dev.this_device, "Failed to create ioeventfd ioreq client!\n");
256 return -EINVAL;
257 }
258
259 dev_dbg(acrn_dev.this_device, "VM %u ioeventfd init.\n", vm->vmid);
260 return 0;
261 }
262
acrn_ioeventfd_deinit(struct acrn_vm * vm)263 void acrn_ioeventfd_deinit(struct acrn_vm *vm)
264 {
265 struct hsm_ioeventfd *p, *next;
266
267 dev_dbg(acrn_dev.this_device, "VM %u ioeventfd deinit.\n", vm->vmid);
268 acrn_ioreq_client_destroy(vm->ioeventfd_client);
269 mutex_lock(&vm->ioeventfds_lock);
270 list_for_each_entry_safe(p, next, &vm->ioeventfds, list)
271 acrn_ioeventfd_shutdown(vm, p);
272 mutex_unlock(&vm->ioeventfds_lock);
273 }
274