in acrn/ioeventfd.c [76:145]
static int acrn_ioeventfd_assign(struct acrn_vm *vm,
struct acrn_ioeventfd *args)
{
struct eventfd_ctx *eventfd;
struct hsm_ioeventfd *p;
int ret;
/* Check for range overflow */
if (args->addr + args->len < args->addr)
return -EINVAL;
/*
* Currently, acrn_ioeventfd is used to support vhost. 1,2,4,8 width
* accesses can cover vhost's requirements.
*/
if (!(args->len == 1 || args->len == 2 ||
args->len == 4 || args->len == 8))
return -EINVAL;
eventfd = eventfd_ctx_fdget(args->fd);
if (IS_ERR(eventfd))
return PTR_ERR(eventfd);
p = kzalloc(sizeof(*p), GFP_KERNEL);
if (!p) {
ret = -ENOMEM;
goto fail;
}
INIT_LIST_HEAD(&p->list);
p->addr = args->addr;
p->length = args->len;
p->eventfd = eventfd;
p->type = ioreq_type_from_flags(args->flags);
/*
* ACRN_IOEVENTFD_FLAG_DATAMATCH flag is set in virtio 1.0 support, the
* writing of notification register of each virtqueue may trigger the
* notification. There is no data matching requirement.
*/
if (args->flags & ACRN_IOEVENTFD_FLAG_DATAMATCH)
p->data = args->data;
else
p->wildcard = true;
mutex_lock(&vm->ioeventfds_lock);
if (hsm_ioeventfd_is_conflict(vm, p)) {
ret = -EEXIST;
goto unlock_fail;
}
/* register the I/O range into ioreq client */
ret = acrn_ioreq_range_add(vm->ioeventfd_client, p->type,
p->addr, p->addr + p->length - 1);
if (ret < 0)
goto unlock_fail;
list_add_tail(&p->list, &vm->ioeventfds);
mutex_unlock(&vm->ioeventfds_lock);
return 0;
unlock_fail:
mutex_unlock(&vm->ioeventfds_lock);
kfree(p);
fail:
eventfd_ctx_put(eventfd);
return ret;
}