linux/drivers/virt/acrn/ioeventfd.c
<<
>>
Prefs
   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}
 274