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 | |
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 | |
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(ctx: p->eventfd); |
48 | list_del(entry: &p->list); |
49 | kfree(objp: p); |
50 | } |
51 | |
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 | */ |
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(fd: args->fd); |
96 | if (IS_ERR(ptr: eventfd)) |
97 | return PTR_ERR(ptr: eventfd); |
98 | |
99 | p = kzalloc(size: sizeof(*p), GFP_KERNEL); |
100 | if (!p) { |
101 | ret = -ENOMEM; |
102 | goto fail; |
103 | } |
104 | |
105 | INIT_LIST_HEAD(list: &p->list); |
106 | p->addr = args->addr; |
107 | p->length = args->len; |
108 | p->eventfd = eventfd; |
109 | p->type = ioreq_type_from_flags(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, ioeventfd: 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(client: vm->ioeventfd_client, type: p->type, |
130 | start: p->addr, end: p->addr + p->length - 1); |
131 | if (ret < 0) |
132 | goto unlock_fail; |
133 | |
134 | list_add_tail(new: &p->list, head: &vm->ioeventfds); |
135 | mutex_unlock(lock: &vm->ioeventfds_lock); |
136 | |
137 | return 0; |
138 | |
139 | unlock_fail: |
140 | mutex_unlock(lock: &vm->ioeventfds_lock); |
141 | kfree(objp: p); |
142 | fail: |
143 | eventfd_ctx_put(ctx: eventfd); |
144 | return ret; |
145 | } |
146 | |
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(fd: args->fd); |
154 | if (IS_ERR(ptr: eventfd)) |
155 | return PTR_ERR(ptr: 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(client: vm->ioeventfd_client, type: p->type, |
163 | start: p->addr, end: p->addr + p->length - 1); |
164 | acrn_ioeventfd_shutdown(vm, p); |
165 | break; |
166 | } |
167 | mutex_unlock(lock: &vm->ioeventfds_lock); |
168 | |
169 | eventfd_ctx_put(ctx: eventfd); |
170 | return 0; |
171 | } |
172 | |
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 | |
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(vm: client->vm, addr, data: val, len: size, type: req->type); |
225 | if (p) |
226 | eventfd_signal(ctx: p->eventfd); |
227 | mutex_unlock(lock: &client->vm->ioeventfds_lock); |
228 | |
229 | return 0; |
230 | } |
231 | |
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 | |
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(list: &vm->ioeventfds); |
250 | snprintf(buf: name, size: sizeof(name), fmt: "ioeventfd-%u" , vm->vmid); |
251 | vm->ioeventfd_client = acrn_ioreq_client_create(vm, |
252 | handler: acrn_ioeventfd_handler, |
253 | NULL, is_default: 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 | |
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(client: 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(lock: &vm->ioeventfds_lock); |
273 | } |
274 | |