Merge tag 'docs-5.13' of git://git.lwn.net/linux
[linux-2.6-microblaze.git] / drivers / virt / acrn / irqfd.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * ACRN HSM irqfd: use eventfd objects to inject virtual interrupts
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/file.h>
14 #include <linux/poll.h>
15 #include <linux/slab.h>
16
17 #include "acrn_drv.h"
18
19 static LIST_HEAD(acrn_irqfd_clients);
20 static DEFINE_MUTEX(acrn_irqfds_mutex);
21
22 /**
23  * struct hsm_irqfd - Properties of HSM irqfd
24  * @vm:         Associated VM pointer
25  * @wait:       Entry of wait-queue
26  * @shutdown:   Async shutdown work
27  * @eventfd:    Associated eventfd
28  * @list:       Entry within &acrn_vm.irqfds of irqfds of a VM
29  * @pt:         Structure for select/poll on the associated eventfd
30  * @msi:        MSI data
31  */
32 struct hsm_irqfd {
33         struct acrn_vm          *vm;
34         wait_queue_entry_t      wait;
35         struct work_struct      shutdown;
36         struct eventfd_ctx      *eventfd;
37         struct list_head        list;
38         poll_table              pt;
39         struct acrn_msi_entry   msi;
40 };
41
42 static void acrn_irqfd_inject(struct hsm_irqfd *irqfd)
43 {
44         struct acrn_vm *vm = irqfd->vm;
45
46         acrn_msi_inject(vm, irqfd->msi.msi_addr,
47                         irqfd->msi.msi_data);
48 }
49
50 static void hsm_irqfd_shutdown(struct hsm_irqfd *irqfd)
51 {
52         u64 cnt;
53
54         lockdep_assert_held(&irqfd->vm->irqfds_lock);
55
56         /* remove from wait queue */
57         list_del_init(&irqfd->list);
58         eventfd_ctx_remove_wait_queue(irqfd->eventfd, &irqfd->wait, &cnt);
59         eventfd_ctx_put(irqfd->eventfd);
60         kfree(irqfd);
61 }
62
63 static void hsm_irqfd_shutdown_work(struct work_struct *work)
64 {
65         struct hsm_irqfd *irqfd;
66         struct acrn_vm *vm;
67
68         irqfd = container_of(work, struct hsm_irqfd, shutdown);
69         vm = irqfd->vm;
70         mutex_lock(&vm->irqfds_lock);
71         if (!list_empty(&irqfd->list))
72                 hsm_irqfd_shutdown(irqfd);
73         mutex_unlock(&vm->irqfds_lock);
74 }
75
76 /* Called with wqh->lock held and interrupts disabled */
77 static int hsm_irqfd_wakeup(wait_queue_entry_t *wait, unsigned int mode,
78                             int sync, void *key)
79 {
80         unsigned long poll_bits = (unsigned long)key;
81         struct hsm_irqfd *irqfd;
82         struct acrn_vm *vm;
83
84         irqfd = container_of(wait, struct hsm_irqfd, wait);
85         vm = irqfd->vm;
86         if (poll_bits & POLLIN)
87                 /* An event has been signaled, inject an interrupt */
88                 acrn_irqfd_inject(irqfd);
89
90         if (poll_bits & POLLHUP)
91                 /* Do shutdown work in thread to hold wqh->lock */
92                 queue_work(vm->irqfd_wq, &irqfd->shutdown);
93
94         return 0;
95 }
96
97 static void hsm_irqfd_poll_func(struct file *file, wait_queue_head_t *wqh,
98                                 poll_table *pt)
99 {
100         struct hsm_irqfd *irqfd;
101
102         irqfd = container_of(pt, struct hsm_irqfd, pt);
103         add_wait_queue(wqh, &irqfd->wait);
104 }
105
106 /*
107  * Assign an eventfd to a VM and create a HSM irqfd associated with the
108  * eventfd. The properties of the HSM irqfd are built from a &struct
109  * acrn_irqfd.
110  */
111 static int acrn_irqfd_assign(struct acrn_vm *vm, struct acrn_irqfd *args)
112 {
113         struct eventfd_ctx *eventfd = NULL;
114         struct hsm_irqfd *irqfd, *tmp;
115         __poll_t events;
116         struct fd f;
117         int ret = 0;
118
119         irqfd = kzalloc(sizeof(*irqfd), GFP_KERNEL);
120         if (!irqfd)
121                 return -ENOMEM;
122
123         irqfd->vm = vm;
124         memcpy(&irqfd->msi, &args->msi, sizeof(args->msi));
125         INIT_LIST_HEAD(&irqfd->list);
126         INIT_WORK(&irqfd->shutdown, hsm_irqfd_shutdown_work);
127
128         f = fdget(args->fd);
129         if (!f.file) {
130                 ret = -EBADF;
131                 goto out;
132         }
133
134         eventfd = eventfd_ctx_fileget(f.file);
135         if (IS_ERR(eventfd)) {
136                 ret = PTR_ERR(eventfd);
137                 goto fail;
138         }
139
140         irqfd->eventfd = eventfd;
141
142         /*
143          * Install custom wake-up handling to be notified whenever underlying
144          * eventfd is signaled.
145          */
146         init_waitqueue_func_entry(&irqfd->wait, hsm_irqfd_wakeup);
147         init_poll_funcptr(&irqfd->pt, hsm_irqfd_poll_func);
148
149         mutex_lock(&vm->irqfds_lock);
150         list_for_each_entry(tmp, &vm->irqfds, list) {
151                 if (irqfd->eventfd != tmp->eventfd)
152                         continue;
153                 ret = -EBUSY;
154                 mutex_unlock(&vm->irqfds_lock);
155                 goto fail;
156         }
157         list_add_tail(&irqfd->list, &vm->irqfds);
158         mutex_unlock(&vm->irqfds_lock);
159
160         /* Check the pending event in this stage */
161         events = vfs_poll(f.file, &irqfd->pt);
162
163         if (events & EPOLLIN)
164                 acrn_irqfd_inject(irqfd);
165
166         fdput(f);
167         return 0;
168 fail:
169         if (eventfd && !IS_ERR(eventfd))
170                 eventfd_ctx_put(eventfd);
171
172         fdput(f);
173 out:
174         kfree(irqfd);
175         return ret;
176 }
177
178 static int acrn_irqfd_deassign(struct acrn_vm *vm,
179                                struct acrn_irqfd *args)
180 {
181         struct hsm_irqfd *irqfd, *tmp;
182         struct eventfd_ctx *eventfd;
183
184         eventfd = eventfd_ctx_fdget(args->fd);
185         if (IS_ERR(eventfd))
186                 return PTR_ERR(eventfd);
187
188         mutex_lock(&vm->irqfds_lock);
189         list_for_each_entry_safe(irqfd, tmp, &vm->irqfds, list) {
190                 if (irqfd->eventfd == eventfd) {
191                         hsm_irqfd_shutdown(irqfd);
192                         break;
193                 }
194         }
195         mutex_unlock(&vm->irqfds_lock);
196         eventfd_ctx_put(eventfd);
197
198         return 0;
199 }
200
201 int acrn_irqfd_config(struct acrn_vm *vm, struct acrn_irqfd *args)
202 {
203         int ret;
204
205         if (args->flags & ACRN_IRQFD_FLAG_DEASSIGN)
206                 ret = acrn_irqfd_deassign(vm, args);
207         else
208                 ret = acrn_irqfd_assign(vm, args);
209
210         return ret;
211 }
212
213 int acrn_irqfd_init(struct acrn_vm *vm)
214 {
215         INIT_LIST_HEAD(&vm->irqfds);
216         mutex_init(&vm->irqfds_lock);
217         vm->irqfd_wq = alloc_workqueue("acrn_irqfd-%u", 0, 0, vm->vmid);
218         if (!vm->irqfd_wq)
219                 return -ENOMEM;
220
221         dev_dbg(acrn_dev.this_device, "VM %u irqfd init.\n", vm->vmid);
222         return 0;
223 }
224
225 void acrn_irqfd_deinit(struct acrn_vm *vm)
226 {
227         struct hsm_irqfd *irqfd, *next;
228
229         dev_dbg(acrn_dev.this_device, "VM %u irqfd deinit.\n", vm->vmid);
230         destroy_workqueue(vm->irqfd_wq);
231         mutex_lock(&vm->irqfds_lock);
232         list_for_each_entry_safe(irqfd, next, &vm->irqfds, list)
233                 hsm_irqfd_shutdown(irqfd);
234         mutex_unlock(&vm->irqfds_lock);
235 }