xref: /openbmc/linux/drivers/virt/acrn/ioeventfd.c (revision d0034a7a4ac7fae708146ac0059b9c47a1543f0d)
1*d8ad5151SShuo Liu // SPDX-License-Identifier: GPL-2.0
2*d8ad5151SShuo Liu /*
3*d8ad5151SShuo Liu  * ACRN HSM eventfd - use eventfd objects to signal expected I/O requests
4*d8ad5151SShuo Liu  *
5*d8ad5151SShuo Liu  * Copyright (C) 2020 Intel Corporation. All rights reserved.
6*d8ad5151SShuo Liu  *
7*d8ad5151SShuo Liu  * Authors:
8*d8ad5151SShuo Liu  *	Shuo Liu <shuo.a.liu@intel.com>
9*d8ad5151SShuo Liu  *	Yakui Zhao <yakui.zhao@intel.com>
10*d8ad5151SShuo Liu  */
11*d8ad5151SShuo Liu 
12*d8ad5151SShuo Liu #include <linux/eventfd.h>
13*d8ad5151SShuo Liu #include <linux/slab.h>
14*d8ad5151SShuo Liu 
15*d8ad5151SShuo Liu #include "acrn_drv.h"
16*d8ad5151SShuo Liu 
17*d8ad5151SShuo Liu /**
18*d8ad5151SShuo Liu  * struct hsm_ioeventfd - Properties of HSM ioeventfd
19*d8ad5151SShuo Liu  * @list:	Entry within &acrn_vm.ioeventfds of ioeventfds of a VM
20*d8ad5151SShuo Liu  * @eventfd:	Eventfd of the HSM ioeventfd
21*d8ad5151SShuo Liu  * @addr:	Address of I/O range
22*d8ad5151SShuo Liu  * @data:	Data for matching
23*d8ad5151SShuo Liu  * @length:	Length of I/O range
24*d8ad5151SShuo Liu  * @type:	Type of I/O range (ACRN_IOREQ_TYPE_MMIO/ACRN_IOREQ_TYPE_PORTIO)
25*d8ad5151SShuo Liu  * @wildcard:	Data matching or not
26*d8ad5151SShuo Liu  */
27*d8ad5151SShuo Liu struct hsm_ioeventfd {
28*d8ad5151SShuo Liu 	struct list_head	list;
29*d8ad5151SShuo Liu 	struct eventfd_ctx	*eventfd;
30*d8ad5151SShuo Liu 	u64			addr;
31*d8ad5151SShuo Liu 	u64			data;
32*d8ad5151SShuo Liu 	int			length;
33*d8ad5151SShuo Liu 	int			type;
34*d8ad5151SShuo Liu 	bool			wildcard;
35*d8ad5151SShuo Liu };
36*d8ad5151SShuo Liu 
ioreq_type_from_flags(int flags)37*d8ad5151SShuo Liu static inline int ioreq_type_from_flags(int flags)
38*d8ad5151SShuo Liu {
39*d8ad5151SShuo Liu 	return flags & ACRN_IOEVENTFD_FLAG_PIO ?
40*d8ad5151SShuo Liu 		       ACRN_IOREQ_TYPE_PORTIO : ACRN_IOREQ_TYPE_MMIO;
41*d8ad5151SShuo Liu }
42*d8ad5151SShuo Liu 
acrn_ioeventfd_shutdown(struct acrn_vm * vm,struct hsm_ioeventfd * p)43*d8ad5151SShuo Liu static void acrn_ioeventfd_shutdown(struct acrn_vm *vm, struct hsm_ioeventfd *p)
44*d8ad5151SShuo Liu {
45*d8ad5151SShuo Liu 	lockdep_assert_held(&vm->ioeventfds_lock);
46*d8ad5151SShuo Liu 
47*d8ad5151SShuo Liu 	eventfd_ctx_put(p->eventfd);
48*d8ad5151SShuo Liu 	list_del(&p->list);
49*d8ad5151SShuo Liu 	kfree(p);
50*d8ad5151SShuo Liu }
51*d8ad5151SShuo Liu 
hsm_ioeventfd_is_conflict(struct acrn_vm * vm,struct hsm_ioeventfd * ioeventfd)52*d8ad5151SShuo Liu static bool hsm_ioeventfd_is_conflict(struct acrn_vm *vm,
53*d8ad5151SShuo Liu 				      struct hsm_ioeventfd *ioeventfd)
54*d8ad5151SShuo Liu {
55*d8ad5151SShuo Liu 	struct hsm_ioeventfd *p;
56*d8ad5151SShuo Liu 
57*d8ad5151SShuo Liu 	lockdep_assert_held(&vm->ioeventfds_lock);
58*d8ad5151SShuo Liu 
59*d8ad5151SShuo Liu 	/* Either one is wildcard, the data matching will be skipped. */
60*d8ad5151SShuo Liu 	list_for_each_entry(p, &vm->ioeventfds, list)
61*d8ad5151SShuo Liu 		if (p->eventfd == ioeventfd->eventfd &&
62*d8ad5151SShuo Liu 		    p->addr == ioeventfd->addr &&
63*d8ad5151SShuo Liu 		    p->type == ioeventfd->type &&
64*d8ad5151SShuo Liu 		    (p->wildcard || ioeventfd->wildcard ||
65*d8ad5151SShuo Liu 			p->data == ioeventfd->data))
66*d8ad5151SShuo Liu 			return true;
67*d8ad5151SShuo Liu 
68*d8ad5151SShuo Liu 	return false;
69*d8ad5151SShuo Liu }
70*d8ad5151SShuo Liu 
71*d8ad5151SShuo Liu /*
72*d8ad5151SShuo Liu  * Assign an eventfd to a VM and create a HSM ioeventfd associated with the
73*d8ad5151SShuo Liu  * eventfd. The properties of the HSM ioeventfd are built from a &struct
74*d8ad5151SShuo Liu  * acrn_ioeventfd.
75*d8ad5151SShuo Liu  */
acrn_ioeventfd_assign(struct acrn_vm * vm,struct acrn_ioeventfd * args)76*d8ad5151SShuo Liu static int acrn_ioeventfd_assign(struct acrn_vm *vm,
77*d8ad5151SShuo Liu 				 struct acrn_ioeventfd *args)
78*d8ad5151SShuo Liu {
79*d8ad5151SShuo Liu 	struct eventfd_ctx *eventfd;
80*d8ad5151SShuo Liu 	struct hsm_ioeventfd *p;
81*d8ad5151SShuo Liu 	int ret;
82*d8ad5151SShuo Liu 
83*d8ad5151SShuo Liu 	/* Check for range overflow */
84*d8ad5151SShuo Liu 	if (args->addr + args->len < args->addr)
85*d8ad5151SShuo Liu 		return -EINVAL;
86*d8ad5151SShuo Liu 
87*d8ad5151SShuo Liu 	/*
88*d8ad5151SShuo Liu 	 * Currently, acrn_ioeventfd is used to support vhost. 1,2,4,8 width
89*d8ad5151SShuo Liu 	 * accesses can cover vhost's requirements.
90*d8ad5151SShuo Liu 	 */
91*d8ad5151SShuo Liu 	if (!(args->len == 1 || args->len == 2 ||
92*d8ad5151SShuo Liu 	      args->len == 4 || args->len == 8))
93*d8ad5151SShuo Liu 		return -EINVAL;
94*d8ad5151SShuo Liu 
95*d8ad5151SShuo Liu 	eventfd = eventfd_ctx_fdget(args->fd);
96*d8ad5151SShuo Liu 	if (IS_ERR(eventfd))
97*d8ad5151SShuo Liu 		return PTR_ERR(eventfd);
98*d8ad5151SShuo Liu 
99*d8ad5151SShuo Liu 	p = kzalloc(sizeof(*p), GFP_KERNEL);
100*d8ad5151SShuo Liu 	if (!p) {
101*d8ad5151SShuo Liu 		ret = -ENOMEM;
102*d8ad5151SShuo Liu 		goto fail;
103*d8ad5151SShuo Liu 	}
104*d8ad5151SShuo Liu 
105*d8ad5151SShuo Liu 	INIT_LIST_HEAD(&p->list);
106*d8ad5151SShuo Liu 	p->addr = args->addr;
107*d8ad5151SShuo Liu 	p->length = args->len;
108*d8ad5151SShuo Liu 	p->eventfd = eventfd;
109*d8ad5151SShuo Liu 	p->type = ioreq_type_from_flags(args->flags);
110*d8ad5151SShuo Liu 
111*d8ad5151SShuo Liu 	/*
112*d8ad5151SShuo Liu 	 * ACRN_IOEVENTFD_FLAG_DATAMATCH flag is set in virtio 1.0 support, the
113*d8ad5151SShuo Liu 	 * writing of notification register of each virtqueue may trigger the
114*d8ad5151SShuo Liu 	 * notification. There is no data matching requirement.
115*d8ad5151SShuo Liu 	 */
116*d8ad5151SShuo Liu 	if (args->flags & ACRN_IOEVENTFD_FLAG_DATAMATCH)
117*d8ad5151SShuo Liu 		p->data = args->data;
118*d8ad5151SShuo Liu 	else
119*d8ad5151SShuo Liu 		p->wildcard = true;
120*d8ad5151SShuo Liu 
121*d8ad5151SShuo Liu 	mutex_lock(&vm->ioeventfds_lock);
122*d8ad5151SShuo Liu 
123*d8ad5151SShuo Liu 	if (hsm_ioeventfd_is_conflict(vm, p)) {
124*d8ad5151SShuo Liu 		ret = -EEXIST;
125*d8ad5151SShuo Liu 		goto unlock_fail;
126*d8ad5151SShuo Liu 	}
127*d8ad5151SShuo Liu 
128*d8ad5151SShuo Liu 	/* register the I/O range into ioreq client */
129*d8ad5151SShuo Liu 	ret = acrn_ioreq_range_add(vm->ioeventfd_client, p->type,
130*d8ad5151SShuo Liu 				   p->addr, p->addr + p->length - 1);
131*d8ad5151SShuo Liu 	if (ret < 0)
132*d8ad5151SShuo Liu 		goto unlock_fail;
133*d8ad5151SShuo Liu 
134*d8ad5151SShuo Liu 	list_add_tail(&p->list, &vm->ioeventfds);
135*d8ad5151SShuo Liu 	mutex_unlock(&vm->ioeventfds_lock);
136*d8ad5151SShuo Liu 
137*d8ad5151SShuo Liu 	return 0;
138*d8ad5151SShuo Liu 
139*d8ad5151SShuo Liu unlock_fail:
140*d8ad5151SShuo Liu 	mutex_unlock(&vm->ioeventfds_lock);
141*d8ad5151SShuo Liu 	kfree(p);
142*d8ad5151SShuo Liu fail:
143*d8ad5151SShuo Liu 	eventfd_ctx_put(eventfd);
144*d8ad5151SShuo Liu 	return ret;
145*d8ad5151SShuo Liu }
146*d8ad5151SShuo Liu 
acrn_ioeventfd_deassign(struct acrn_vm * vm,struct acrn_ioeventfd * args)147*d8ad5151SShuo Liu static int acrn_ioeventfd_deassign(struct acrn_vm *vm,
148*d8ad5151SShuo Liu 				   struct acrn_ioeventfd *args)
149*d8ad5151SShuo Liu {
150*d8ad5151SShuo Liu 	struct hsm_ioeventfd *p;
151*d8ad5151SShuo Liu 	struct eventfd_ctx *eventfd;
152*d8ad5151SShuo Liu 
153*d8ad5151SShuo Liu 	eventfd = eventfd_ctx_fdget(args->fd);
154*d8ad5151SShuo Liu 	if (IS_ERR(eventfd))
155*d8ad5151SShuo Liu 		return PTR_ERR(eventfd);
156*d8ad5151SShuo Liu 
157*d8ad5151SShuo Liu 	mutex_lock(&vm->ioeventfds_lock);
158*d8ad5151SShuo Liu 	list_for_each_entry(p, &vm->ioeventfds, list) {
159*d8ad5151SShuo Liu 		if (p->eventfd != eventfd)
160*d8ad5151SShuo Liu 			continue;
161*d8ad5151SShuo Liu 
162*d8ad5151SShuo Liu 		acrn_ioreq_range_del(vm->ioeventfd_client, p->type,
163*d8ad5151SShuo Liu 				     p->addr, p->addr + p->length - 1);
164*d8ad5151SShuo Liu 		acrn_ioeventfd_shutdown(vm, p);
165*d8ad5151SShuo Liu 		break;
166*d8ad5151SShuo Liu 	}
167*d8ad5151SShuo Liu 	mutex_unlock(&vm->ioeventfds_lock);
168*d8ad5151SShuo Liu 
169*d8ad5151SShuo Liu 	eventfd_ctx_put(eventfd);
170*d8ad5151SShuo Liu 	return 0;
171*d8ad5151SShuo Liu }
172*d8ad5151SShuo Liu 
hsm_ioeventfd_match(struct acrn_vm * vm,u64 addr,u64 data,int len,int type)173*d8ad5151SShuo Liu static struct hsm_ioeventfd *hsm_ioeventfd_match(struct acrn_vm *vm, u64 addr,
174*d8ad5151SShuo Liu 						 u64 data, int len, int type)
175*d8ad5151SShuo Liu {
176*d8ad5151SShuo Liu 	struct hsm_ioeventfd *p = NULL;
177*d8ad5151SShuo Liu 
178*d8ad5151SShuo Liu 	lockdep_assert_held(&vm->ioeventfds_lock);
179*d8ad5151SShuo Liu 
180*d8ad5151SShuo Liu 	list_for_each_entry(p, &vm->ioeventfds, list) {
181*d8ad5151SShuo Liu 		if (p->type == type && p->addr == addr && p->length >= len &&
182*d8ad5151SShuo Liu 		    (p->wildcard || p->data == data))
183*d8ad5151SShuo Liu 			return p;
184*d8ad5151SShuo Liu 	}
185*d8ad5151SShuo Liu 
186*d8ad5151SShuo Liu 	return NULL;
187*d8ad5151SShuo Liu }
188*d8ad5151SShuo Liu 
acrn_ioeventfd_handler(struct acrn_ioreq_client * client,struct acrn_io_request * req)189*d8ad5151SShuo Liu static int acrn_ioeventfd_handler(struct acrn_ioreq_client *client,
190*d8ad5151SShuo Liu 				  struct acrn_io_request *req)
191*d8ad5151SShuo Liu {
192*d8ad5151SShuo Liu 	struct hsm_ioeventfd *p;
193*d8ad5151SShuo Liu 	u64 addr, val;
194*d8ad5151SShuo Liu 	int size;
195*d8ad5151SShuo Liu 
196*d8ad5151SShuo Liu 	if (req->type == ACRN_IOREQ_TYPE_MMIO) {
197*d8ad5151SShuo Liu 		/*
198*d8ad5151SShuo Liu 		 * I/O requests are dispatched by range check only, so a
199*d8ad5151SShuo Liu 		 * acrn_ioreq_client need process both READ and WRITE accesses
200*d8ad5151SShuo Liu 		 * of same range. READ accesses are safe to be ignored here
201*d8ad5151SShuo Liu 		 * because virtio PCI devices write the notify registers for
202*d8ad5151SShuo Liu 		 * notification.
203*d8ad5151SShuo Liu 		 */
204*d8ad5151SShuo Liu 		if (req->reqs.mmio_request.direction == ACRN_IOREQ_DIR_READ) {
205*d8ad5151SShuo Liu 			/* reading does nothing and return 0 */
206*d8ad5151SShuo Liu 			req->reqs.mmio_request.value = 0;
207*d8ad5151SShuo Liu 			return 0;
208*d8ad5151SShuo Liu 		}
209*d8ad5151SShuo Liu 		addr = req->reqs.mmio_request.address;
210*d8ad5151SShuo Liu 		size = req->reqs.mmio_request.size;
211*d8ad5151SShuo Liu 		val = req->reqs.mmio_request.value;
212*d8ad5151SShuo Liu 	} else {
213*d8ad5151SShuo Liu 		if (req->reqs.pio_request.direction == ACRN_IOREQ_DIR_READ) {
214*d8ad5151SShuo Liu 			/* reading does nothing and return 0 */
215*d8ad5151SShuo Liu 			req->reqs.pio_request.value = 0;
216*d8ad5151SShuo Liu 			return 0;
217*d8ad5151SShuo Liu 		}
218*d8ad5151SShuo Liu 		addr = req->reqs.pio_request.address;
219*d8ad5151SShuo Liu 		size = req->reqs.pio_request.size;
220*d8ad5151SShuo Liu 		val = req->reqs.pio_request.value;
221*d8ad5151SShuo Liu 	}
222*d8ad5151SShuo Liu 
223*d8ad5151SShuo Liu 	mutex_lock(&client->vm->ioeventfds_lock);
224*d8ad5151SShuo Liu 	p = hsm_ioeventfd_match(client->vm, addr, val, size, req->type);
225*d8ad5151SShuo Liu 	if (p)
226*d8ad5151SShuo Liu 		eventfd_signal(p->eventfd, 1);
227*d8ad5151SShuo Liu 	mutex_unlock(&client->vm->ioeventfds_lock);
228*d8ad5151SShuo Liu 
229*d8ad5151SShuo Liu 	return 0;
230*d8ad5151SShuo Liu }
231*d8ad5151SShuo Liu 
acrn_ioeventfd_config(struct acrn_vm * vm,struct acrn_ioeventfd * args)232*d8ad5151SShuo Liu int acrn_ioeventfd_config(struct acrn_vm *vm, struct acrn_ioeventfd *args)
233*d8ad5151SShuo Liu {
234*d8ad5151SShuo Liu 	int ret;
235*d8ad5151SShuo Liu 
236*d8ad5151SShuo Liu 	if (args->flags & ACRN_IOEVENTFD_FLAG_DEASSIGN)
237*d8ad5151SShuo Liu 		ret = acrn_ioeventfd_deassign(vm, args);
238*d8ad5151SShuo Liu 	else
239*d8ad5151SShuo Liu 		ret = acrn_ioeventfd_assign(vm, args);
240*d8ad5151SShuo Liu 
241*d8ad5151SShuo Liu 	return ret;
242*d8ad5151SShuo Liu }
243*d8ad5151SShuo Liu 
acrn_ioeventfd_init(struct acrn_vm * vm)244*d8ad5151SShuo Liu int acrn_ioeventfd_init(struct acrn_vm *vm)
245*d8ad5151SShuo Liu {
246*d8ad5151SShuo Liu 	char name[ACRN_NAME_LEN];
247*d8ad5151SShuo Liu 
248*d8ad5151SShuo Liu 	mutex_init(&vm->ioeventfds_lock);
249*d8ad5151SShuo Liu 	INIT_LIST_HEAD(&vm->ioeventfds);
250*d8ad5151SShuo Liu 	snprintf(name, sizeof(name), "ioeventfd-%u", vm->vmid);
251*d8ad5151SShuo Liu 	vm->ioeventfd_client = acrn_ioreq_client_create(vm,
252*d8ad5151SShuo Liu 							acrn_ioeventfd_handler,
253*d8ad5151SShuo Liu 							NULL, false, name);
254*d8ad5151SShuo Liu 	if (!vm->ioeventfd_client) {
255*d8ad5151SShuo Liu 		dev_err(acrn_dev.this_device, "Failed to create ioeventfd ioreq client!\n");
256*d8ad5151SShuo Liu 		return -EINVAL;
257*d8ad5151SShuo Liu 	}
258*d8ad5151SShuo Liu 
259*d8ad5151SShuo Liu 	dev_dbg(acrn_dev.this_device, "VM %u ioeventfd init.\n", vm->vmid);
260*d8ad5151SShuo Liu 	return 0;
261*d8ad5151SShuo Liu }
262*d8ad5151SShuo Liu 
acrn_ioeventfd_deinit(struct acrn_vm * vm)263*d8ad5151SShuo Liu void acrn_ioeventfd_deinit(struct acrn_vm *vm)
264*d8ad5151SShuo Liu {
265*d8ad5151SShuo Liu 	struct hsm_ioeventfd *p, *next;
266*d8ad5151SShuo Liu 
267*d8ad5151SShuo Liu 	dev_dbg(acrn_dev.this_device, "VM %u ioeventfd deinit.\n", vm->vmid);
268*d8ad5151SShuo Liu 	acrn_ioreq_client_destroy(vm->ioeventfd_client);
269*d8ad5151SShuo Liu 	mutex_lock(&vm->ioeventfds_lock);
270*d8ad5151SShuo Liu 	list_for_each_entry_safe(p, next, &vm->ioeventfds, list)
271*d8ad5151SShuo Liu 		acrn_ioeventfd_shutdown(vm, p);
272*d8ad5151SShuo Liu 	mutex_unlock(&vm->ioeventfds_lock);
273*d8ad5151SShuo Liu }
274