Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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 */
 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 the
 73 * eventfd. The properties of the HSM ioeventfd are built from a &struct
 74 * 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 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
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 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);
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	else
239		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}