linux/drivers/vfio/platform/vfio_platform_irq.c
<<
>>
Prefs
   1/*
   2 * VFIO platform devices interrupt handling
   3 *
   4 * Copyright (C) 2013 - Virtual Open Systems
   5 * Author: Antonios Motakis <a.motakis@virtualopensystems.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License, version 2, as
   9 * published by the Free Software Foundation.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 */
  16
  17#include <linux/eventfd.h>
  18#include <linux/interrupt.h>
  19#include <linux/slab.h>
  20#include <linux/types.h>
  21#include <linux/vfio.h>
  22#include <linux/irq.h>
  23
  24#include "vfio_platform_private.h"
  25
  26static void vfio_platform_mask(struct vfio_platform_irq *irq_ctx)
  27{
  28        unsigned long flags;
  29
  30        spin_lock_irqsave(&irq_ctx->lock, flags);
  31
  32        if (!irq_ctx->masked) {
  33                disable_irq_nosync(irq_ctx->hwirq);
  34                irq_ctx->masked = true;
  35        }
  36
  37        spin_unlock_irqrestore(&irq_ctx->lock, flags);
  38}
  39
  40static int vfio_platform_mask_handler(void *opaque, void *unused)
  41{
  42        struct vfio_platform_irq *irq_ctx = opaque;
  43
  44        vfio_platform_mask(irq_ctx);
  45
  46        return 0;
  47}
  48
  49static int vfio_platform_set_irq_mask(struct vfio_platform_device *vdev,
  50                                      unsigned index, unsigned start,
  51                                      unsigned count, uint32_t flags,
  52                                      void *data)
  53{
  54        if (start != 0 || count != 1)
  55                return -EINVAL;
  56
  57        if (!(vdev->irqs[index].flags & VFIO_IRQ_INFO_MASKABLE))
  58                return -EINVAL;
  59
  60        if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
  61                int32_t fd = *(int32_t *)data;
  62
  63                if (fd >= 0)
  64                        return vfio_virqfd_enable((void *) &vdev->irqs[index],
  65                                                  vfio_platform_mask_handler,
  66                                                  NULL, NULL,
  67                                                  &vdev->irqs[index].mask, fd);
  68
  69                vfio_virqfd_disable(&vdev->irqs[index].mask);
  70                return 0;
  71        }
  72
  73        if (flags & VFIO_IRQ_SET_DATA_NONE) {
  74                vfio_platform_mask(&vdev->irqs[index]);
  75
  76        } else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
  77                uint8_t mask = *(uint8_t *)data;
  78
  79                if (mask)
  80                        vfio_platform_mask(&vdev->irqs[index]);
  81        }
  82
  83        return 0;
  84}
  85
  86static void vfio_platform_unmask(struct vfio_platform_irq *irq_ctx)
  87{
  88        unsigned long flags;
  89
  90        spin_lock_irqsave(&irq_ctx->lock, flags);
  91
  92        if (irq_ctx->masked) {
  93                enable_irq(irq_ctx->hwirq);
  94                irq_ctx->masked = false;
  95        }
  96
  97        spin_unlock_irqrestore(&irq_ctx->lock, flags);
  98}
  99
 100static int vfio_platform_unmask_handler(void *opaque, void *unused)
 101{
 102        struct vfio_platform_irq *irq_ctx = opaque;
 103
 104        vfio_platform_unmask(irq_ctx);
 105
 106        return 0;
 107}
 108
 109static int vfio_platform_set_irq_unmask(struct vfio_platform_device *vdev,
 110                                        unsigned index, unsigned start,
 111                                        unsigned count, uint32_t flags,
 112                                        void *data)
 113{
 114        if (start != 0 || count != 1)
 115                return -EINVAL;
 116
 117        if (!(vdev->irqs[index].flags & VFIO_IRQ_INFO_MASKABLE))
 118                return -EINVAL;
 119
 120        if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
 121                int32_t fd = *(int32_t *)data;
 122
 123                if (fd >= 0)
 124                        return vfio_virqfd_enable((void *) &vdev->irqs[index],
 125                                                  vfio_platform_unmask_handler,
 126                                                  NULL, NULL,
 127                                                  &vdev->irqs[index].unmask,
 128                                                  fd);
 129
 130                vfio_virqfd_disable(&vdev->irqs[index].unmask);
 131                return 0;
 132        }
 133
 134        if (flags & VFIO_IRQ_SET_DATA_NONE) {
 135                vfio_platform_unmask(&vdev->irqs[index]);
 136
 137        } else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
 138                uint8_t unmask = *(uint8_t *)data;
 139
 140                if (unmask)
 141                        vfio_platform_unmask(&vdev->irqs[index]);
 142        }
 143
 144        return 0;
 145}
 146
 147static irqreturn_t vfio_automasked_irq_handler(int irq, void *dev_id)
 148{
 149        struct vfio_platform_irq *irq_ctx = dev_id;
 150        unsigned long flags;
 151        int ret = IRQ_NONE;
 152
 153        spin_lock_irqsave(&irq_ctx->lock, flags);
 154
 155        if (!irq_ctx->masked) {
 156                ret = IRQ_HANDLED;
 157
 158                /* automask maskable interrupts */
 159                disable_irq_nosync(irq_ctx->hwirq);
 160                irq_ctx->masked = true;
 161        }
 162
 163        spin_unlock_irqrestore(&irq_ctx->lock, flags);
 164
 165        if (ret == IRQ_HANDLED)
 166                eventfd_signal(irq_ctx->trigger, 1);
 167
 168        return ret;
 169}
 170
 171static irqreturn_t vfio_irq_handler(int irq, void *dev_id)
 172{
 173        struct vfio_platform_irq *irq_ctx = dev_id;
 174
 175        eventfd_signal(irq_ctx->trigger, 1);
 176
 177        return IRQ_HANDLED;
 178}
 179
 180static int vfio_set_trigger(struct vfio_platform_device *vdev, int index,
 181                            int fd, irq_handler_t handler)
 182{
 183        struct vfio_platform_irq *irq = &vdev->irqs[index];
 184        struct eventfd_ctx *trigger;
 185        int ret;
 186
 187        if (irq->trigger) {
 188                irq_clear_status_flags(irq->hwirq, IRQ_NOAUTOEN);
 189                free_irq(irq->hwirq, irq);
 190                kfree(irq->name);
 191                eventfd_ctx_put(irq->trigger);
 192                irq->trigger = NULL;
 193        }
 194
 195        if (fd < 0) /* Disable only */
 196                return 0;
 197
 198        irq->name = kasprintf(GFP_KERNEL, "vfio-irq[%d](%s)",
 199                                                irq->hwirq, vdev->name);
 200        if (!irq->name)
 201                return -ENOMEM;
 202
 203        trigger = eventfd_ctx_fdget(fd);
 204        if (IS_ERR(trigger)) {
 205                kfree(irq->name);
 206                return PTR_ERR(trigger);
 207        }
 208
 209        irq->trigger = trigger;
 210
 211        irq_set_status_flags(irq->hwirq, IRQ_NOAUTOEN);
 212        ret = request_irq(irq->hwirq, handler, 0, irq->name, irq);
 213        if (ret) {
 214                kfree(irq->name);
 215                eventfd_ctx_put(trigger);
 216                irq->trigger = NULL;
 217                return ret;
 218        }
 219
 220        if (!irq->masked)
 221                enable_irq(irq->hwirq);
 222
 223        return 0;
 224}
 225
 226static int vfio_platform_set_irq_trigger(struct vfio_platform_device *vdev,
 227                                         unsigned index, unsigned start,
 228                                         unsigned count, uint32_t flags,
 229                                         void *data)
 230{
 231        struct vfio_platform_irq *irq = &vdev->irqs[index];
 232        irq_handler_t handler;
 233
 234        if (vdev->irqs[index].flags & VFIO_IRQ_INFO_AUTOMASKED)
 235                handler = vfio_automasked_irq_handler;
 236        else
 237                handler = vfio_irq_handler;
 238
 239        if (!count && (flags & VFIO_IRQ_SET_DATA_NONE))
 240                return vfio_set_trigger(vdev, index, -1, handler);
 241
 242        if (start != 0 || count != 1)
 243                return -EINVAL;
 244
 245        if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
 246                int32_t fd = *(int32_t *)data;
 247
 248                return vfio_set_trigger(vdev, index, fd, handler);
 249        }
 250
 251        if (flags & VFIO_IRQ_SET_DATA_NONE) {
 252                handler(irq->hwirq, irq);
 253
 254        } else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
 255                uint8_t trigger = *(uint8_t *)data;
 256
 257                if (trigger)
 258                        handler(irq->hwirq, irq);
 259        }
 260
 261        return 0;
 262}
 263
 264int vfio_platform_set_irqs_ioctl(struct vfio_platform_device *vdev,
 265                                 uint32_t flags, unsigned index, unsigned start,
 266                                 unsigned count, void *data)
 267{
 268        int (*func)(struct vfio_platform_device *vdev, unsigned index,
 269                    unsigned start, unsigned count, uint32_t flags,
 270                    void *data) = NULL;
 271
 272        switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {
 273        case VFIO_IRQ_SET_ACTION_MASK:
 274                func = vfio_platform_set_irq_mask;
 275                break;
 276        case VFIO_IRQ_SET_ACTION_UNMASK:
 277                func = vfio_platform_set_irq_unmask;
 278                break;
 279        case VFIO_IRQ_SET_ACTION_TRIGGER:
 280                func = vfio_platform_set_irq_trigger;
 281                break;
 282        }
 283
 284        if (!func)
 285                return -ENOTTY;
 286
 287        return func(vdev, index, start, count, flags, data);
 288}
 289
 290int vfio_platform_irq_init(struct vfio_platform_device *vdev)
 291{
 292        int cnt = 0, i;
 293
 294        while (vdev->get_irq(vdev, cnt) >= 0)
 295                cnt++;
 296
 297        vdev->irqs = kcalloc(cnt, sizeof(struct vfio_platform_irq), GFP_KERNEL);
 298        if (!vdev->irqs)
 299                return -ENOMEM;
 300
 301        for (i = 0; i < cnt; i++) {
 302                int hwirq = vdev->get_irq(vdev, i);
 303
 304                if (hwirq < 0)
 305                        goto err;
 306
 307                spin_lock_init(&vdev->irqs[i].lock);
 308
 309                vdev->irqs[i].flags = VFIO_IRQ_INFO_EVENTFD;
 310
 311                if (irq_get_trigger_type(hwirq) & IRQ_TYPE_LEVEL_MASK)
 312                        vdev->irqs[i].flags |= VFIO_IRQ_INFO_MASKABLE
 313                                                | VFIO_IRQ_INFO_AUTOMASKED;
 314
 315                vdev->irqs[i].count = 1;
 316                vdev->irqs[i].hwirq = hwirq;
 317                vdev->irqs[i].masked = false;
 318        }
 319
 320        vdev->num_irqs = cnt;
 321
 322        return 0;
 323err:
 324        kfree(vdev->irqs);
 325        return -EINVAL;
 326}
 327
 328void vfio_platform_irq_cleanup(struct vfio_platform_device *vdev)
 329{
 330        int i;
 331
 332        for (i = 0; i < vdev->num_irqs; i++)
 333                vfio_set_trigger(vdev, i, -1, NULL);
 334
 335        vdev->num_irqs = 0;
 336        kfree(vdev->irqs);
 337}
 338