Merge tag 'docs-5.13' of git://git.lwn.net/linux
[linux-2.6-microblaze.git] / drivers / virt / acrn / ioeventfd.c
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(p->eventfd);
48         list_del(&p->list);
49         kfree(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(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
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
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(client->vm, addr, val, size, req->type);
225         if (p)
226                 eventfd_signal(p->eventfd, 1);
227         mutex_unlock(&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(&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
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 }