/* * IRQ offload/bypass manager * * Copyright (C) 2015 Red Hat, Inc. * Copyright (c) 2015 Linaro Ltd. * * 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. * * Various virtualization hardware acceleration techniques allow bypassing or * offloading interrupts received from devices around the host kernel. Posted * Interrupts on Intel VT-d systems can allow interrupts to be received * directly by a virtual machine. ARM IRQ Forwarding allows forwarded physical * interrupts to be directly deactivated by the guest. This manager allows * interrupt producers and consumers to find each other to enable this sort of * bypass. */ #include #include #include #include MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("IRQ bypass manager utility module"); static LIST_HEAD(producers); static LIST_HEAD(consumers); static DEFINE_MUTEX(lock); /* @lock must be held when calling connect */ static int __connect(struct irq_bypass_producer *prod, struct irq_bypass_consumer *cons) { int ret = 0; if (prod->stop) prod->stop(prod); if (cons->stop) cons->stop(cons); if (prod->add_consumer) ret = prod->add_consumer(prod, cons); if (!ret) { ret = cons->add_producer(cons, prod); if (ret && prod->del_consumer) prod->del_consumer(prod, cons); } if (cons->start) cons->start(cons); if (prod->start) prod->start(prod); return ret; } /* @lock must be held when calling disconnect */ static void __disconnect(struct irq_bypass_producer *prod, struct irq_bypass_consumer *cons) { if (prod->stop) prod->stop(prod); if (cons->stop) cons->stop(cons); cons->del_producer(cons, prod); if (prod->del_consumer) prod->del_consumer(prod, cons); if (cons->start) cons->start(cons); if (prod->start) prod->start(prod); } /** * irq_bypass_register_producer - register IRQ bypass producer * @producer: pointer to producer structure * * Add the provided IRQ producer to the list of producers and connect * with any matching token found on the IRQ consumers list. */ int irq_bypass_register_producer(struct irq_bypass_producer *producer) { struct irq_bypass_producer *tmp; struct irq_bypass_consumer *consumer; if (!producer->token) return -EINVAL; might_sleep(); if (!try_module_get(THIS_MODULE)) return -ENODEV; mutex_lock(&lock); list_for_each_entry(tmp, &producers, node) { if (tmp->token == producer->token) { mutex_unlock(&lock); module_put(THIS_MODULE); return -EBUSY; } } list_for_each_entry(consumer, &consumers, node) { if (consumer->token == producer->token) { int ret = __connect(producer, consumer); if (ret) { mutex_unlock(&lock); module_put(THIS_MODULE); return ret; } break; } } list_add(&producer->node, &producers); mutex_unlock(&lock); return 0; } EXPORT_SYMBOL_GPL(irq_bypass_register_producer); /** * irq_bypass_unregister_producer - unregister IRQ bypass producer * @producer: pointer to producer structure * * Remove a previously registered IRQ producer from the list of producers * and disconnect it from any connected IRQ consumer. */ void irq_bypass_unregister_producer(struct irq_bypass_producer *producer) { struct irq_bypass_producer *tmp; struct irq_bypass_consumer *consumer; if (!producer->token) return; might_sleep(); if (!try_module_get(THIS_MODULE)) return; /* nothing in the list anyway */ mutex_lock(&lock); list_for_each_entry(tmp, &producers, node) { if (tmp->token != producer->token) continue; list_for_each_entry(consumer, &consumers, node) { if (consumer->token == producer->token) { __disconnect(producer, consumer); break; } } list_del(&producer->node); module_put(THIS_MODULE); break; } mutex_unlock(&lock); module_put(THIS_MODULE); } EXPORT_SYMBOL_GPL(irq_bypass_unregister_producer); /** * irq_bypass_register_consumer - register IRQ bypass consumer * @consumer: pointer to consumer structure * * Add the provided IRQ consumer to the list of consumers and connect * with any matching token found on the IRQ producer list. */ int irq_bypass_register_consumer(struct irq_bypass_consumer *consumer) { struct irq_bypass_consumer *tmp; struct irq_bypass_producer *producer; if (!consumer->token || !consumer->add_producer || !consumer->del_producer) return -EINVAL; might_sleep(); if (!try_module_get(THIS_MODULE)) return -ENODEV; mutex_lock(&lock); list_for_each_entry(tmp, &consumers, node) { if (tmp->token == consumer->token || tmp == consumer) { mutex_unlock(&lock); module_put(THIS_MODULE); return -EBUSY; } } list_for_each_entry(producer, &producers, node) { if (producer->token == consumer->token) { int ret = __connect(producer, consumer); if (ret) { mutex_unlock(&lock); module_put(THIS_MODULE); return ret; } break; } } list_add(&consumer->node, &consumers); mutex_unlock(&lock); return 0; } EXPORT_SYMBOL_GPL(irq_bypass_register_consumer); /** * irq_bypass_unregister_consumer - unregister IRQ bypass consumer * @consumer: pointer to consumer structure * * Remove a previously registered IRQ consumer from the list of consumers * and disconnect it from any connected IRQ producer. */ void irq_bypass_unregister_consumer(struct irq_bypass_consumer *consumer) { struct irq_bypass_consumer *tmp; struct irq_bypass_producer *producer; if (!consumer->token) return; might_sleep(); if (!try_module_get(THIS_MODULE)) return; /* nothing in the list anyway */ mutex_lock(&lock); list_for_each_entry(tmp, &consumers, node) { if (tmp != consumer) continue; list_for_each_entry(producer, &producers, node) { if (producer->token == consumer->token) { __disconnect(producer, consumer); break; } } list_del(&consumer->node); module_put(THIS_MODULE); break; } mutex_unlock(&lock); module_put(THIS_MODULE); } EXPORT_SYMBOL_GPL(irq_bypass_unregister_consumer); 9405f4321b86274a0c24b30896dd50529&id2=dd3b9f25c867cb2507a45e436d6ede8eb08e7b05'>diff)parentc14024dbb156c8392908aaa822097d27c6af8ec8 (diff)
Merge branch 'for-linus' of git://git.kernel.dk/linux-block
Pull block fixes from Jens Axboe: "A set of fixes for this series. This contains: - Set of fixes for the nvme target code - A revert of patch from this merge window, causing a regression with WRITE_SAME on iSCSI targets at least. - A fix for a use-after-free in the new O_DIRECT bdev code. - Two fixes for the xen-blkfront driver" * 'for-linus' of git://git.kernel.dk/linux-block: Revert "sd: remove __data_len hack for WRITE SAME" nvme-fc: use blk_rq_nr_phys_segments nvmet-rdma: Fix missing dma sync to nvme data structures nvmet: Call fatal_error from keep-alive timout expiration nvmet: cancel fatal error and flush async work before free controller nvmet: delete controllers deletion upon subsystem release nvmet_fc: correct logic in disconnect queue LS handling block: fix use after free in __blkdev_direct_IO xen-blkfront: correct maximum segment accounting xen-blkfront: feature flags handling adjustments
Diffstat (limited to 'security/integrity/ima/ima_appraise.c')