brintos

brintos / linux-shallow public Read only

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