ioeventfd.c (6885B)
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, 1); 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}