diff options
author | John Levon <john.levon@nutanix.com> | 2021-03-10 10:37:59 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-03-10 10:37:59 +0000 |
commit | 052d709d0cfd77c36f5b31402c3cc1c3d868d618 (patch) | |
tree | 29f6fbba5fa94e80e7c53bbc2fdde474ae16a1b0 | |
parent | 997536eee6609827b5053afef497f94bc6f6dbf4 (diff) | |
download | libvfio-user-052d709d0cfd77c36f5b31402c3cc1c3d868d618.zip libvfio-user-052d709d0cfd77c36f5b31402c3cc1c3d868d618.tar.gz libvfio-user-052d709d0cfd77c36f5b31402c3cc1c3d868d618.tar.bz2 |
fix IRQ disable path (#386)
Properly fix IRQ disabling:
Allow count == 0 to mean "disable all IRQS of the given type". On our side,
disabling an IRQ means forgetting about the eventfd that was previously passed
over the socket.
Allow individual IRQs to be disabled, by means of a VFIO_IRQ_SET_DATA_EVENTFD
message with no file descriptors passed. In vfio, this is done via setting "-1"
in the fd slots; which isn't possible via auxiliary data. Thus, only one IRQ can
be disabled a a time in vfio-user.
Clean up "->type": this is never set, so wasn't having any effect. Follow up
changes will likely re-introduce this in some form.
Signed-off-by: John Levon <john.levon@nutanix.com>
Reviewed-by: Thanos Makatos <thanos.makatos@nutanix.com>
-rw-r--r-- | docs/vfio-user.rst | 6 | ||||
-rw-r--r-- | lib/irq.c | 293 | ||||
-rw-r--r-- | lib/libvfio-user.c | 9 | ||||
-rw-r--r-- | lib/private.h | 14 | ||||
-rw-r--r-- | test/unit-tests.c | 168 |
5 files changed, 309 insertions, 181 deletions
diff --git a/docs/vfio-user.rst b/docs/vfio-user.rst index f4562ef..93d61cd 100644 --- a/docs/vfio-user.rst +++ b/docs/vfio-user.rst @@ -1276,6 +1276,8 @@ VFIO IRQ set format was sent in the message meta-data. These descriptors will be signalled when the action defined by the action flags occurs. In AF_UNIX sockets, the descriptors are sent as SCM_RIGHTS type ancillary data. + If no file descriptors are provided, this de-assigns the specified + previously configured interrupts. * *VFIO_IRQ_SET_ACTION_MASK* indicates a masking event. It can be used with VFIO_IRQ_SET_DATA_BOOL or VFIO_IRQ_SET_DATA_NONE to mask an interrupt, or with VFIO_IRQ_SET_DATA_EVENTFD to generate an event when the guest masks @@ -1292,8 +1294,8 @@ VFIO IRQ set format * *index* is the index of IRQ type being setup. * *start* is the start of the sub-index being set. * *count* describes the number of sub-indexes being set. As a special case, a - count of 0 with data flags of VFIO_IRQ_SET_DATA_NONE disables all interrupts - of the index. + count (and start) of 0, with data flags of VFIO_IRQ_SET_DATA_NONE disables + all interrupts of the index. * *data* is an optional field included when the VFIO_IRQ_SET_DATA_BOOL flag is present. It contains an array of booleans that specify whether the action is to be performed on the corresponding @@ -54,52 +54,87 @@ vfio_irq_idx_to_str(int index) } static long -irqs_disable(vfu_ctx_t *vfu_ctx, uint32_t index) +dev_get_irqinfo(vfu_ctx_t *vfu_ctx, struct vfio_irq_info *irq_info_in, + struct vfio_irq_info *irq_info_out) +{ + assert(vfu_ctx != NULL); + assert(irq_info_in != NULL); + assert(irq_info_out != NULL); + + // Ensure provided argsz is sufficiently big and index is within bounds. + if ((irq_info_in->argsz < sizeof(struct vfio_irq_info)) || + (irq_info_in->index >= VFU_DEV_NUM_IRQS)) { + vfu_log(vfu_ctx, LOG_DEBUG, "bad irq_info (size=%d index=%d)\n", + irq_info_in->argsz, irq_info_in->index); + return -EINVAL; + } + + irq_info_out->count = vfu_ctx->irq_count[irq_info_in->index]; + irq_info_out->flags = VFIO_IRQ_INFO_EVENTFD; + + return 0; +} + +int +handle_device_get_irq_info(vfu_ctx_t *vfu_ctx, uint32_t size, + struct vfio_irq_info *irq_info_in, + struct vfio_irq_info *irq_info_out) +{ + assert(vfu_ctx != NULL); + assert(irq_info_in != NULL); + assert(irq_info_out != NULL); + + if (size != sizeof(*irq_info_in) || size != irq_info_in->argsz) { + vfu_log(vfu_ctx, LOG_WARNING, "IRQ info size %d", size); + return -EINVAL; + } + + return dev_get_irqinfo(vfu_ctx, irq_info_in, irq_info_out); +} + +static long +irqs_disable(vfu_ctx_t *vfu_ctx, uint32_t index, uint32_t start, uint32_t count) { - int *irq_efd = NULL; - uint32_t i; + size_t i; + int *efds; assert(vfu_ctx != NULL); assert(index < VFU_DEV_NUM_IRQS); + assert(start + count <= vfu_ctx->irq_count[index]); + + vfu_log(vfu_ctx, LOG_DEBUG, "disabling IRQ type %s range [%u-%u)", + vfio_irq_idx_to_str(index), start, start + count); + + if (count == 0) { + count = vfu_ctx->irq_count[index]; + } switch (index) { case VFIO_PCI_INTX_IRQ_INDEX: case VFIO_PCI_MSI_IRQ_INDEX: case VFIO_PCI_MSIX_IRQ_INDEX: - vfu_log(vfu_ctx, LOG_DEBUG, "disabling IRQ %s", - vfio_irq_idx_to_str(index)); - vfu_ctx->irqs->type = IRQ_NONE; - for (i = 0; i < vfu_ctx->irqs->max_ivs; i++) { - if (vfu_ctx->irqs->efds[i] >= 0) { - if (close(vfu_ctx->irqs->efds[i]) == -1) { - vfu_log(vfu_ctx, LOG_DEBUG, "failed to close IRQ fd %d: %m", - vfu_ctx->irqs->efds[i]); - } - vfu_ctx->irqs->efds[i] = -1; - } - } - return 0; + efds = vfu_ctx->irqs[index].efds; + break; case VFIO_PCI_ERR_IRQ_INDEX: - irq_efd = &vfu_ctx->irqs->err_efd; + efds = &vfu_ctx->irqs->err_efd; break; case VFIO_PCI_REQ_IRQ_INDEX: - irq_efd = &vfu_ctx->irqs->req_efd; + efds = &vfu_ctx->irqs->req_efd; break; } - if (irq_efd != NULL) { - if (*irq_efd != -1) { - if (close(*irq_efd) == -1) { + for (i = start; i < count; i++) { + if (efds[i] >= 0) { + if (close(efds[i]) == -1) { vfu_log(vfu_ctx, LOG_DEBUG, "failed to close IRQ fd %d: %m", - *irq_efd); + efds[i]); } - *irq_efd = -1; + + efds[i] = -1; } - return 0; } - vfu_log(vfu_ctx, LOG_DEBUG, "failed to disable IRQs"); - return -EINVAL; + return 0; } static int @@ -185,39 +220,11 @@ irqs_set_data_eventfd(vfu_ctx_t *vfu_ctx, struct vfio_irq_set *irq_set, } static long -irqs_trigger(vfu_ctx_t *vfu_ctx, struct vfio_irq_set *irq_set, void *data) -{ - int err = 0; - - assert(vfu_ctx != NULL); - assert(irq_set != NULL); - - if (irq_set->count == 0) { - return irqs_disable(vfu_ctx, irq_set->index); - } - - vfu_log(vfu_ctx, LOG_DEBUG, "setting IRQ %s flags=%#x", - vfio_irq_idx_to_str(irq_set->index), irq_set->flags); - - switch (irq_set->flags & VFIO_IRQ_SET_DATA_TYPE_MASK) { - case VFIO_IRQ_SET_DATA_NONE: - err = irqs_set_data_none(vfu_ctx, irq_set); - break; - case VFIO_IRQ_SET_DATA_BOOL: - err = irqs_set_data_bool(vfu_ctx, irq_set, data); - break; - case VFIO_IRQ_SET_DATA_EVENTFD: - err = irqs_set_data_eventfd(vfu_ctx, irq_set, data); - break; - } - - return err; -} - -static long -dev_set_irqs_validate(vfu_ctx_t *vfu_ctx, struct vfio_irq_set *irq_set) +device_set_irqs_validate(vfu_ctx_t *vfu_ctx, struct vfio_irq_set *irq_set, + size_t nr_fds) { uint32_t a_type, d_type; + int line; assert(vfu_ctx != NULL); assert(irq_set != NULL); @@ -228,162 +235,122 @@ dev_set_irqs_validate(vfu_ctx_t *vfu_ctx, struct vfio_irq_set *irq_set) // Ensure index is within bounds. if (irq_set->index >= VFU_DEV_NUM_IRQS) { - vfu_log(vfu_ctx, LOG_DEBUG, "bad IRQ index %d\n", irq_set->index); - return -EINVAL; + line = __LINE__; + goto invalid; } - /* TODO make each condition a function */ - // Only one of MASK/UNMASK/TRIGGER is valid. if ((a_type != VFIO_IRQ_SET_ACTION_MASK) && (a_type != VFIO_IRQ_SET_ACTION_UNMASK) && (a_type != VFIO_IRQ_SET_ACTION_TRIGGER)) { - vfu_log(vfu_ctx, LOG_DEBUG, "bad IRQ action mask %d\n", a_type); - return -EINVAL; + line = __LINE__; + goto invalid; } // Only one of NONE/BOOL/EVENTFD is valid. if ((d_type != VFIO_IRQ_SET_DATA_NONE) && (d_type != VFIO_IRQ_SET_DATA_BOOL) && (d_type != VFIO_IRQ_SET_DATA_EVENTFD)) { - vfu_log(vfu_ctx, LOG_DEBUG, "bad IRQ data %d\n", d_type); - return -EINVAL; + line = __LINE__; + goto invalid; } // Ensure irq_set's start and count are within bounds. if ((irq_set->start >= vfu_ctx->irq_count[irq_set->index]) || (irq_set->start + irq_set->count > vfu_ctx->irq_count[irq_set->index])) { - vfu_log(vfu_ctx, LOG_DEBUG, "bad IRQ start/count\n"); - return -EINVAL; + line = __LINE__; + goto invalid; } // Only TRIGGER is valid for ERR/REQ. if (((irq_set->index == VFIO_PCI_ERR_IRQ_INDEX) || (irq_set->index == VFIO_PCI_REQ_IRQ_INDEX)) && (a_type != VFIO_IRQ_SET_ACTION_TRIGGER)) { - vfu_log(vfu_ctx, LOG_DEBUG, "bad IRQ trigger w/o ERR/REQ\n"); - return -EINVAL; + line = __LINE__; + goto invalid; + } + // if count == 0, start must be 0 too + if ((irq_set->count == 0) && (irq_set->start != 0)) { + line = __LINE__; + goto invalid; } // count == 0 is only valid with ACTION_TRIGGER and DATA_NONE. if ((irq_set->count == 0) && ((a_type != VFIO_IRQ_SET_ACTION_TRIGGER) || (d_type != VFIO_IRQ_SET_DATA_NONE))) { - vfu_log(vfu_ctx, LOG_DEBUG, "bad IRQ count %d\n", irq_set->count); - return -EINVAL; + line = __LINE__; + goto invalid; } - // If IRQs are set, ensure index matches what's enabled for the device. - if ((irq_set->count != 0) && (vfu_ctx->irqs->type != IRQ_NONE) && - (irq_set->index != LM2VFIO_IRQT(vfu_ctx->irqs->type))) { - vfu_log(vfu_ctx, LOG_DEBUG, "bad IRQ index\n"); - return -EINVAL; + // If fd's are provided, ensure it's only for VFIO_IRQ_SET_DATA_EVENTFD + if (nr_fds != 0 && d_type != VFIO_IRQ_SET_DATA_EVENTFD) { + line = __LINE__; + goto invalid; + } + // If fd's are provided, ensure they match ->count + if (nr_fds != 0 && nr_fds != irq_set->count) { + line = __LINE__; + goto invalid; } return 0; + +invalid: + vfu_log(vfu_ctx, LOG_DEBUG, "invalid SET_IRQS (%d): action=%u data_type=%u " + "index=%u start=%u count=%u nr_fds=%u", line, a_type, d_type, + irq_set->index, irq_set->start, irq_set->count, nr_fds); + return -EINVAL; } -static long -dev_set_irqs(vfu_ctx_t *vfu_ctx, struct vfio_irq_set *irq_set, void *data) +int +handle_device_set_irqs(vfu_ctx_t *vfu_ctx, uint32_t size, + int *fds, size_t nr_fds, struct vfio_irq_set *irq_set) { - long ret; + uint32_t data_type; + int ret; assert(vfu_ctx != NULL); assert(irq_set != NULL); - // Ensure irq_set is valid. - ret = dev_set_irqs_validate(vfu_ctx, irq_set); + if (size < sizeof(*irq_set) || size != irq_set->argsz) { + vfu_log(vfu_ctx, LOG_ERR, "%s: bad size %u", __func__, size); + return -EINVAL; + } + + ret = device_set_irqs_validate(vfu_ctx, irq_set, nr_fds); if (ret != 0) { return ret; } switch (irq_set->flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { - case VFIO_IRQ_SET_ACTION_MASK: // fallthrough + case VFIO_IRQ_SET_ACTION_MASK: case VFIO_IRQ_SET_ACTION_UNMASK: // We're always edge-triggered without un/mask support. + // FIXME: return an error? We don't report MASKABLE return 0; + case VFIO_IRQ_SET_ACTION_TRIGGER: + break; } - return irqs_trigger(vfu_ctx, irq_set, data); -} - -static long -dev_get_irqinfo(vfu_ctx_t *vfu_ctx, struct vfio_irq_info *irq_info_in, - struct vfio_irq_info *irq_info_out) -{ - assert(vfu_ctx != NULL); - assert(irq_info_in != NULL); - assert(irq_info_out != NULL); - - // Ensure provided argsz is sufficiently big and index is within bounds. - if ((irq_info_in->argsz < sizeof(struct vfio_irq_info)) || - (irq_info_in->index >= VFU_DEV_NUM_IRQS)) { - vfu_log(vfu_ctx, LOG_DEBUG, "bad irq_info (size=%d index=%d)\n", - irq_info_in->argsz, irq_info_in->index); - return -EINVAL; - } - - irq_info_out->count = vfu_ctx->irq_count[irq_info_in->index]; - irq_info_out->flags = VFIO_IRQ_INFO_EVENTFD; - - return 0; -} + data_type = irq_set->flags & VFIO_IRQ_SET_DATA_TYPE_MASK; -int -handle_device_get_irq_info(vfu_ctx_t *vfu_ctx, uint32_t size, - struct vfio_irq_info *irq_info_in, - struct vfio_irq_info *irq_info_out) -{ - assert(vfu_ctx != NULL); - assert(irq_info_in != NULL); - assert(irq_info_out != NULL); - - if (size != sizeof(*irq_info_in) || size != irq_info_in->argsz) { - vfu_log(vfu_ctx, LOG_WARNING, "IRQ info size %d", size); - return -EINVAL; + if ((data_type == VFIO_IRQ_SET_DATA_NONE && irq_set->count == 0) || + (data_type == VFIO_IRQ_SET_DATA_EVENTFD && nr_fds == 0)) { + return irqs_disable(vfu_ctx, irq_set->index, + irq_set->start, irq_set->count); } - return dev_get_irqinfo(vfu_ctx, irq_info_in, irq_info_out); -} + vfu_log(vfu_ctx, LOG_DEBUG, "setting IRQ %s flags=%#x range [%u-%u)", + vfio_irq_idx_to_str(irq_set->index), irq_set->flags, + irq_set->start, irq_set->start + irq_set->count); -int -handle_device_set_irqs(vfu_ctx_t *vfu_ctx, uint32_t size, - int *fds, size_t nr_fds, struct vfio_irq_set *irq_set) -{ - void *data = NULL; - - assert(vfu_ctx != NULL); - assert(irq_set != NULL); - - if (size < sizeof(*irq_set) || size != irq_set->argsz) { - vfu_log(vfu_ctx, LOG_ERR, "bad size %d", size); - return -EINVAL; - } - - switch (irq_set->flags & VFIO_IRQ_SET_DATA_TYPE_MASK) { - case VFIO_IRQ_SET_DATA_NONE: - /* - * FIXME The way it's handled in this case is a hack, it should be - * properly fixed, see issue #359 for more details. - */ - vfu_log(vfu_ctx, LOG_NOTICE, - "ignore IRQ DATA_NONE, action=%#x, index=%ld, start=%ld, count=%ld", - irq_set->flags & VFIO_IRQ_SET_ACTION_TYPE_MASK, - irq_set->index, irq_set->start, irq_set->count); - return 0; - case VFIO_IRQ_SET_DATA_EVENTFD: - data = fds; - if (nr_fds != irq_set->count) { - vfu_log(vfu_ctx, LOG_ERR, - "bad number of FDs, expected=%u, actual=%d", nr_fds, - irq_set->count); - return -EINVAL; - } - break; - case VFIO_IRQ_SET_DATA_BOOL: - data = irq_set + 1; - break; - default: - vfu_log(vfu_ctx, LOG_ERR, "invalid IRQ type %d", - irq_set->flags & VFIO_IRQ_SET_DATA_TYPE_MASK); - return -EINVAL; + switch (data_type) { + case VFIO_IRQ_SET_DATA_NONE: + return irqs_set_data_none(vfu_ctx, irq_set); + case VFIO_IRQ_SET_DATA_EVENTFD: + return irqs_set_data_eventfd(vfu_ctx, irq_set, fds); + case VFIO_IRQ_SET_DATA_BOOL: + return irqs_set_data_bool(vfu_ctx, irq_set, irq_set + 1); + break; + default: + // we already checked this + abort(); } - - return dev_set_irqs(vfu_ctx, irq_set, data); } static bool diff --git a/lib/libvfio-user.c b/lib/libvfio-user.c index 77c9f0c..8df125f 100644 --- a/lib/libvfio-user.c +++ b/lib/libvfio-user.c @@ -1037,7 +1037,7 @@ vfu_realize_ctx(vfu_ctx_t *vfu_ctx) } } - //FIXME: assert(max_ivs > 0)? + // FIXME: assert(max_ivs > 0)? size = sizeof(int) * max_ivs; vfu_ctx->irqs = calloc(1, sizeof(vfu_irqs_t) + size); if (vfu_ctx->irqs == NULL) { @@ -1051,7 +1051,6 @@ vfu_realize_ctx(vfu_ctx_t *vfu_ctx) } vfu_ctx->irqs->err_efd = -1; vfu_ctx->irqs->req_efd = -1; - vfu_ctx->irqs->type = IRQ_NONE; vfu_ctx->irqs->max_ivs = max_ivs; // Reflect on the config space whether INTX is available. @@ -1423,10 +1422,8 @@ vfu_setup_device_nr_irqs(vfu_ctx_t *vfu_ctx, enum vfu_dev_irq_type type, assert(vfu_ctx != NULL); - if (type < VFU_DEV_INTX_IRQ || type > VFU_DEV_REQ_IRQ) { - vfu_log(vfu_ctx, LOG_ERR, "Invalid IRQ index %d, should be between " - "(%d to %d)", type, VFU_DEV_INTX_IRQ, - VFU_DEV_REQ_IRQ); + if (type >= VFU_DEV_NUM_IRQS) { + vfu_log(vfu_ctx, LOG_ERR, "Invalid IRQ type index %u", type); return ERROR_INT(EINVAL); } diff --git a/lib/private.h b/lib/private.h index 8e068c6..60bbba9 100644 --- a/lib/private.h +++ b/lib/private.h @@ -77,19 +77,11 @@ struct transport_ops { void (*fini)(vfu_ctx_t *vfu_ctx); }; -typedef enum { - IRQ_NONE = 0, - IRQ_INTX, - IRQ_MSI, - IRQ_MSIX, -} irq_type_t; - typedef struct { - irq_type_t type; /* irq type this device is using */ int err_efd; /* eventfd for irq err */ int req_efd; /* eventfd for irq req */ uint32_t max_ivs; /* maximum number of ivs supported */ - int efds[0]; /* XXX must be last */ + int efds[0]; /* must be last */ } vfu_irqs_t; struct migration; @@ -190,6 +182,10 @@ cmd_allowed_when_stopped_and_copying(uint16_t cmd); bool should_exec_command(vfu_ctx_t *vfu_ctx, uint16_t cmd); +int +handle_device_set_irqs(vfu_ctx_t *vfu_ctx, uint32_t size, + int *fds, size_t nr_fds, struct vfio_irq_set *irq_set); + #endif /* LIB_VFIO_USER_PRIVATE_H */ /* ex: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/test/unit-tests.c b/test/unit-tests.c index 25cba6c..929b83a 100644 --- a/test/unit-tests.c +++ b/test/unit-tests.c @@ -1684,7 +1684,172 @@ test_dirty_pages_without_dma(UNUSED void **state) assert_int_equal(0xabcd, r); } -int main(void) +static void +test_device_set_irqs(UNUSED void **state) +{ + vfu_irqs_t *irqs = alloca(sizeof (*irqs) + sizeof (int)); + struct vfio_irq_set irq_set = { 0, }; + vfu_ctx_t vfu_ctx = { 0, }; + int fd = 0xdead; + int ret; + + vfu_ctx.irq_count[VFU_DEV_MSIX_IRQ] = 2048; + vfu_ctx.irq_count[VFU_DEV_ERR_IRQ] = 1; + vfu_ctx.irq_count[VFU_DEV_REQ_IRQ] = 1; + vfu_ctx.irqs = irqs; + + /* validation tests */ + + irq_set.argsz = sizeof (irq_set); + + ret = handle_device_set_irqs(&vfu_ctx, 0, NULL, 0, &irq_set); + /* bad message size */ + assert_int_equal(-EINVAL, ret); + + irq_set.argsz = 3; + + ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set); + /* bad .argsz */ + assert_int_equal(-EINVAL, ret); + + irq_set.argsz = sizeof (irq_set); + irq_set.index = VFU_DEV_NUM_IRQS; + + ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set); + /* bad .index */ + assert_int_equal(-EINVAL, ret); + + irq_set.index = VFU_DEV_MSIX_IRQ; + irq_set.flags = VFIO_IRQ_SET_ACTION_MASK | VFIO_IRQ_SET_ACTION_UNMASK; + + ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set); + /* bad flags, MASK and UNMASK */ + assert_int_equal(-EINVAL, ret); + + irq_set.flags = VFIO_IRQ_SET_ACTION_MASK | VFIO_IRQ_SET_DATA_NONE | + VFIO_IRQ_SET_DATA_BOOL; + + ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set); + /* bad flags, DATA_NONE and DATA_BOOL */ + assert_int_equal(-EINVAL, ret); + + irq_set.flags = VFIO_IRQ_SET_ACTION_MASK | VFIO_IRQ_SET_DATA_NONE; + irq_set.start = 2047; + irq_set.count = 2; + + ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set); + /* bad start, count range */ + assert_int_equal(-EINVAL, ret); + + irq_set.start = 2049; + irq_set.count = 1; + + ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set); + /* bad start, count range */ + assert_int_equal(-EINVAL, ret); + + irq_set.start = 0; + irq_set.count = 1; + irq_set.index = VFU_DEV_ERR_IRQ; + + ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set); + /* bad action for err irq */ + assert_int_equal(-EINVAL, ret); + + irq_set.index = VFU_DEV_REQ_IRQ; + + ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set); + /* bad action for req irq */ + assert_int_equal(-EINVAL, ret); + + irq_set.start = 1; + irq_set.count = 0; + irq_set.index = VFU_DEV_MSIX_IRQ; + + ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set); + /* bad start for count == 0 */ + assert_int_equal(-EINVAL, ret); + + irq_set.flags = VFIO_IRQ_SET_ACTION_MASK | VFIO_IRQ_SET_DATA_NONE; + irq_set.count = 0; + irq_set.start = 0; + + ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set); + /* bad action for count == 0 */ + assert_int_equal(-EINVAL, ret); + + irq_set.flags = VFIO_IRQ_SET_ACTION_TRIGGER | VFIO_IRQ_SET_DATA_BOOL; + irq_set.count = 0; + irq_set.start = 0; + + ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set); + /* bad action and data type for count == 0 */ + assert_int_equal(-EINVAL, ret); + + irq_set.flags = VFIO_IRQ_SET_ACTION_TRIGGER | VFIO_IRQ_SET_DATA_BOOL; + irq_set.count = 1; + irq_set.start = 0; + + ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), &fd, 1, &irq_set); + /* bad fds for DATA_BOOL */ + assert_int_equal(-EINVAL, ret); + + irq_set.flags = VFIO_IRQ_SET_ACTION_TRIGGER | VFIO_IRQ_SET_DATA_NONE; + irq_set.count = 1; + irq_set.start = 0; + + ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), &fd, 1, &irq_set); + /* bad fds for DATA_NONE */ + assert_int_equal(-EINVAL, ret); + + irq_set.flags = VFIO_IRQ_SET_ACTION_TRIGGER | VFIO_IRQ_SET_DATA_EVENTFD; + irq_set.count = 2; + irq_set.start = 0; + + ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), &fd, 1, &irq_set); + /* bad fds for count == 2 */ + assert_int_equal(-EINVAL, ret); + + irqs->err_efd = irqs->req_efd = -1; + + /* Basic disable functionality. */ + + irq_set.index = VFU_DEV_REQ_IRQ; + irq_set.flags = VFIO_IRQ_SET_ACTION_TRIGGER | VFIO_IRQ_SET_DATA_NONE; + irq_set.count = 0; + irq_set.start = 0; + + ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set); + assert_int_equal(0, ret); + + irq_set.index = VFU_DEV_REQ_IRQ; + irq_set.flags = VFIO_IRQ_SET_ACTION_TRIGGER | VFIO_IRQ_SET_DATA_EVENTFD; + irq_set.count = 1; + irq_set.start = 0; + + ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set); + assert_int_equal(0, ret); + + /* Basic enable. */ + + irq_set.index = VFU_DEV_MSIX_IRQ; + vfu_ctx.irq_count[VFU_DEV_MSIX_IRQ] = 1; + irqs->efds[0] = -1; + fd = 0xbeef; + + irq_set.index = VFU_DEV_MSIX_IRQ; + irq_set.flags = VFIO_IRQ_SET_ACTION_TRIGGER | VFIO_IRQ_SET_DATA_EVENTFD; + irq_set.count = 1; + irq_set.start = 0; + + ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), &fd, 1, &irq_set); + assert_int_equal(0, ret); + assert_int_equal(0xbeef, irqs->efds[0]); + +} + +int +main(void) { const struct CMUnitTest tests[] = { cmocka_unit_test_setup(test_dma_map_without_dma, setup), @@ -1741,6 +1906,7 @@ int main(void) cmocka_unit_test_setup(test_should_exec_command, setup), cmocka_unit_test_setup(test_exec_command, setup), cmocka_unit_test_setup(test_dirty_pages_without_dma, setup), + cmocka_unit_test_setup(test_device_set_irqs, setup), }; return cmocka_run_group_tests(tests, NULL, NULL); |