/* * VFIO generic eventfd code for IRQFD support. * Derived from drivers/vfio/pci/vfio_pci_intrs.c * * Copyright (C) 2012 Red Hat, Inc. All rights reserved. * Author: Alex Williamson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #define DRIVER_VERSION "0.1" #define DRIVER_AUTHOR "Alex Williamson " #define DRIVER_DESC "IRQFD support for VFIO bus drivers" static struct workqueue_struct *vfio_irqfd_cleanup_wq; static DEFINE_SPINLOCK(virqfd_lock); static int __init vfio_virqfd_init(void) { vfio_irqfd_cleanup_wq = create_singlethread_workqueue("vfio-irqfd-cleanup"); if (!vfio_irqfd_cleanup_wq) return -ENOMEM; return 0; } static void __exit vfio_virqfd_exit(void) { destroy_workqueue(vfio_irqfd_cleanup_wq); } static void virqfd_deactivate(struct virqfd *virqfd) { queue_work(vfio_irqfd_cleanup_wq, &virqfd->shutdown); } static int virqfd_wakeup(wait_queue_t *wait, unsigned mode, int sync, void *key) { struct virqfd *virqfd = container_of(wait, struct virqfd, wait); unsigned long flags = (unsigned long)key; if (flags & POLLIN) { /* An event has been signaled, call function */ if ((!virqfd->handler || virqfd->handler(virqfd->opaque, virqfd->data)) && virqfd->thread) schedule_work(&virqfd->inject); } if (flags & POLLHUP) { unsigned long flags; spin_lock_irqsave(&virqfd_lock, flags); /* * The eventfd is closing, if the virqfd has not yet been * queued for release, as determined by testing whether the * virqfd pointer to it is still valid, queue it now. As * with kvm irqfds, we know we won't race against the virqfd * going away because we hold the lock to get here. */ if (*(virqfd->pvirqfd) == virqfd) { *(virqfd->pvirqfd) = NULL; virqfd_deactivate(virqfd); } spin_unlock_irqrestore(&virqfd_lock, flags); } return 0; } static void virqfd_ptable_queue_proc(struct file *file, wait_queue_head_t *wqh, poll_table *pt) { struct virqfd *virqfd = container_of(pt, struct virqfd, pt); add_wait_queue(wqh, &virqfd->wait); } static void virqfd_shutdown(struct work_struct *work) { struct virqfd *virqfd = container_of(work, struct virqfd, shutdown); u64 cnt; eventfd_ctx_remove_wait_queue(virqfd->eventfd, &virqfd->wait, &cnt); flush_work(&virqfd->inject); eventfd_ctx_put(virqfd->eventfd); kfree(virqfd); } static void virqfd_inject(struct work_struct *work) { struct virqfd *virqfd = container_of(work, struct virqfd, inject); if (virqfd->thread) virqfd->thread(virqfd->opaque, virqfd->data); } int vfio_virqfd_enable(void *opaque, int (*handler)(void *, void *), void (*thread)(void *, void *), void *data, struct virqfd **pvirqfd, int fd) { struct fd irqfd; struct eventfd_ctx *ctx; struct virqfd *virqfd; int ret = 0; unsigned int events; virqfd = kzalloc(sizeof(*virqfd), GFP_KERNEL); if (!virqfd) return -ENOMEM; virqfd->pvirqfd = pvirqfd; virqfd->opaque = opaque; virqfd->handler = handler; virqfd->thread = thread; virqfd->data = data; INIT_WORK(&virqfd->shutdown, virqfd_shutdown); INIT_WORK(&virqfd->inject, virqfd_inject); irqfd = fdget(fd); if (!irqfd.file) { ret = -EBADF; goto err_fd; } ctx = eventfd_ctx_fileget(irqfd.file); if (IS_ERR(ctx)) { ret = PTR_ERR(ctx); goto err_ctx; } virqfd->eventfd = ctx; /* * virqfds can be released by closing the eventfd or directly * through ioctl. These are both done through a workqueue, so * we update the pointer to the virqfd under lock to avoid * pushing multiple jobs to release the same virqfd. */ spin_lock_irq(&virqfd_lock); if (*pvirqfd) { spin_unlock_irq(&virqfd_lock); ret = -EBUSY; goto err_busy; } *pvirqfd = virqfd; spin_unlock_irq(&virqfd_lock); /* * Install our own custom wake-up handling so we are notified via * a callback whenever someone signals the underlying eventfd. */ init_waitqueue_func_entry(&virqfd->wait, virqfd_wakeup); init_poll_funcptr(&virqfd->pt, virqfd_ptable_queue_proc); events = irqfd.file->f_op->poll(irqfd.file, &virqfd->pt); /* * Check if there was an event already pending on the eventfd * before we registered and trigger it as if we didn't miss it. */ if (events & POLLIN) { if ((!handler || handler(opaque, data)) && thread) schedule_work(&virqfd->inject); } /* * Do not drop the file until the irqfd is fully initialized, * otherwise we might race against the POLLHUP. */ fdput(irqfd); return 0; err_busy: eventfd_ctx_put(ctx); err_ctx: fdput(irqfd); err_fd: kfree(virqfd); return ret; } EXPORT_SYMBOL_GPL(vfio_virqfd_enable); void vfio_virqfd_disable(struct virqfd **pvirqfd) { unsigned long flags; spin_lock_irqsave(&virqfd_lock, flags); if (*pvirqfd) { virqfd_deactivate(*pvirqfd); *pvirqfd = NULL; } spin_unlock_irqrestore(&virqfd_lock, flags); /* * Block until we know all outstanding shutdown jobs have completed. * Even if we don't queue the job, flush the wq to be sure it's * been released. */ flush_workqueue(vfio_irqfd_cleanup_wq); } EXPORT_SYMBOL_GPL(vfio_virqfd_disable); module_init(vfio_virqfd_init); module_exit(vfio_virqfd_exit); MODULE_VERSION(DRIVER_VERSION); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); td class='ls-mode'>-rw-r--r--aess.h1668logplain -rw-r--r--ak4113.h11112logplain -rw-r--r--ak4114.h10424logplain -rw-r--r--ak4117.h9193logplain -rw-r--r--ak4531_codec.h3173logplain -rw-r--r--ak4641.h622logplain -rw-r--r--ak4xxx-adda.h3416logplain -rw-r--r--alc5623.h497logplain -rw-r--r--asequencer.h3670logplain -rw-r--r--asound.h1285logplain -rw-r--r--asoundef.h17098logplain -rw-r--r--atmel-abdac.h639logplain -rw-r--r--atmel-ac97c.h1342logplain -rw-r--r--compress_driver.h6772logplain -rw-r--r--control.h8704logplain -rw-r--r--core.h14380logplain -rw-r--r--cs35l33.h1034logplain -rw-r--r--cs35l34.h887logplain -rw-r--r--cs4231-regs.h8480logplain -rw-r--r--cs4271.h1417logplain -rw-r--r--cs42l52.h738logplain -rw-r--r--cs42l56.h1192logplain -rw-r--r--cs42l73.h507logplain -rw-r--r--cs8403.h8833logplain -rw-r--r--cs8427.h10649logplain -rw-r--r--da7213.h1178logplain -rw-r--r--da7218.h2681logplain -rw-r--r--da7219-aad.h2476logplain -rw-r--r--da7219.h1064logplain -rw-r--r--da9055.h914logplain -rw-r--r--designware_i2s.h2249logplain -rw-r--r--dmaengine_pcm.h6157logplain -rw-r--r--emu10k1.h91396logplain -rw-r--r--emu10k1_synth.h1382logplain -rw-r--r--emu8000.h4109logplain -rw-r--r--emu8000_reg.h10459logplain -rw-r--r--emux_legacy.h5503logplain -rw-r--r--emux_synth.h7649logplain -rw-r--r--es1688.h3618logplain -rw-r--r--gus.h20691logplain -rw-r--r--hda_chmap.h2621logplain -rw-r--r--hda_hwdep.h1412logplain -rw-r--r--hda_i915.h1645logplain -rw-r--r--hda_register.h9475logplain -rw-r--r--hda_regmap.h6714logplain -rw-r--r--hda_verbs.h17130logplain -rw-r--r--hdaudio.h18455logplain -rw-r--r--hdaudio_ext.h7119logplain -rw-r--r--hdmi-codec.h2290logplain -rw-r--r--hwdep.h2624logplain -rw-r--r--i2c.h3555logplain -rw-r--r--info.h7584logplain