brintos

brintos / linux-shallow public Read only

0
0
Text · 6.7 KiB · 4e845c6 Raw
274 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * ACRN HSM eventfd - use eventfd objects to signal expected I/O requests4 *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 ioeventfd19 * @list:	Entry within &acrn_vm.ioeventfds of ioeventfds of a VM20 * @eventfd:	Eventfd of the HSM ioeventfd21 * @addr:	Address of I/O range22 * @data:	Data for matching23 * @length:	Length of I/O range24 * @type:	Type of I/O range (ACRN_IOREQ_TYPE_MMIO/ACRN_IOREQ_TYPE_PORTIO)25 * @wildcard:	Data matching or not26 */27struct 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 37static 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 43static 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 52static 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 the73 * eventfd. The properties of the HSM ioeventfd are built from a &struct74 * acrn_ioeventfd.75 */76static 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 width89	 * 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, the113	 * writing of notification register of each virtqueue may trigger the114	 * notification. There is no data matching requirement.115	 */116	if (args->flags & ACRN_IOEVENTFD_FLAG_DATAMATCH)117		p->data = args->data;118	else119		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 139unlock_fail:140	mutex_unlock(&vm->ioeventfds_lock);141	kfree(p);142fail:143	eventfd_ctx_put(eventfd);144	return ret;145}146 147static 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 173static 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 189static 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 a199		 * acrn_ioreq_client need process both READ and WRITE accesses200		 * of same range. READ accesses are safe to be ignored here201		 * because virtio PCI devices write the notify registers for202		 * 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 232int 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	else239		ret = acrn_ioeventfd_assign(vm, args);240 241	return ret;242}243 244int 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 263void 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