cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

irqbypass.c (5929B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * IRQ offload/bypass manager
      4 *
      5 * Copyright (C) 2015 Red Hat, Inc.
      6 * Copyright (c) 2015 Linaro Ltd.
      7 *
      8 * Various virtualization hardware acceleration techniques allow bypassing or
      9 * offloading interrupts received from devices around the host kernel.  Posted
     10 * Interrupts on Intel VT-d systems can allow interrupts to be received
     11 * directly by a virtual machine.  ARM IRQ Forwarding allows forwarded physical
     12 * interrupts to be directly deactivated by the guest.  This manager allows
     13 * interrupt producers and consumers to find each other to enable this sort of
     14 * bypass.
     15 */
     16
     17#include <linux/irqbypass.h>
     18#include <linux/list.h>
     19#include <linux/module.h>
     20#include <linux/mutex.h>
     21
     22MODULE_LICENSE("GPL v2");
     23MODULE_DESCRIPTION("IRQ bypass manager utility module");
     24
     25static LIST_HEAD(producers);
     26static LIST_HEAD(consumers);
     27static DEFINE_MUTEX(lock);
     28
     29/* @lock must be held when calling connect */
     30static int __connect(struct irq_bypass_producer *prod,
     31		     struct irq_bypass_consumer *cons)
     32{
     33	int ret = 0;
     34
     35	if (prod->stop)
     36		prod->stop(prod);
     37	if (cons->stop)
     38		cons->stop(cons);
     39
     40	if (prod->add_consumer)
     41		ret = prod->add_consumer(prod, cons);
     42
     43	if (!ret) {
     44		ret = cons->add_producer(cons, prod);
     45		if (ret && prod->del_consumer)
     46			prod->del_consumer(prod, cons);
     47	}
     48
     49	if (cons->start)
     50		cons->start(cons);
     51	if (prod->start)
     52		prod->start(prod);
     53
     54	return ret;
     55}
     56
     57/* @lock must be held when calling disconnect */
     58static void __disconnect(struct irq_bypass_producer *prod,
     59			 struct irq_bypass_consumer *cons)
     60{
     61	if (prod->stop)
     62		prod->stop(prod);
     63	if (cons->stop)
     64		cons->stop(cons);
     65
     66	cons->del_producer(cons, prod);
     67
     68	if (prod->del_consumer)
     69		prod->del_consumer(prod, cons);
     70
     71	if (cons->start)
     72		cons->start(cons);
     73	if (prod->start)
     74		prod->start(prod);
     75}
     76
     77/**
     78 * irq_bypass_register_producer - register IRQ bypass producer
     79 * @producer: pointer to producer structure
     80 *
     81 * Add the provided IRQ producer to the list of producers and connect
     82 * with any matching token found on the IRQ consumers list.
     83 */
     84int irq_bypass_register_producer(struct irq_bypass_producer *producer)
     85{
     86	struct irq_bypass_producer *tmp;
     87	struct irq_bypass_consumer *consumer;
     88	int ret;
     89
     90	if (!producer->token)
     91		return -EINVAL;
     92
     93	might_sleep();
     94
     95	if (!try_module_get(THIS_MODULE))
     96		return -ENODEV;
     97
     98	mutex_lock(&lock);
     99
    100	list_for_each_entry(tmp, &producers, node) {
    101		if (tmp->token == producer->token) {
    102			ret = -EBUSY;
    103			goto out_err;
    104		}
    105	}
    106
    107	list_for_each_entry(consumer, &consumers, node) {
    108		if (consumer->token == producer->token) {
    109			ret = __connect(producer, consumer);
    110			if (ret)
    111				goto out_err;
    112			break;
    113		}
    114	}
    115
    116	list_add(&producer->node, &producers);
    117
    118	mutex_unlock(&lock);
    119
    120	return 0;
    121out_err:
    122	mutex_unlock(&lock);
    123	module_put(THIS_MODULE);
    124	return ret;
    125}
    126EXPORT_SYMBOL_GPL(irq_bypass_register_producer);
    127
    128/**
    129 * irq_bypass_unregister_producer - unregister IRQ bypass producer
    130 * @producer: pointer to producer structure
    131 *
    132 * Remove a previously registered IRQ producer from the list of producers
    133 * and disconnect it from any connected IRQ consumer.
    134 */
    135void irq_bypass_unregister_producer(struct irq_bypass_producer *producer)
    136{
    137	struct irq_bypass_producer *tmp;
    138	struct irq_bypass_consumer *consumer;
    139
    140	if (!producer->token)
    141		return;
    142
    143	might_sleep();
    144
    145	if (!try_module_get(THIS_MODULE))
    146		return; /* nothing in the list anyway */
    147
    148	mutex_lock(&lock);
    149
    150	list_for_each_entry(tmp, &producers, node) {
    151		if (tmp->token != producer->token)
    152			continue;
    153
    154		list_for_each_entry(consumer, &consumers, node) {
    155			if (consumer->token == producer->token) {
    156				__disconnect(producer, consumer);
    157				break;
    158			}
    159		}
    160
    161		list_del(&producer->node);
    162		module_put(THIS_MODULE);
    163		break;
    164	}
    165
    166	mutex_unlock(&lock);
    167
    168	module_put(THIS_MODULE);
    169}
    170EXPORT_SYMBOL_GPL(irq_bypass_unregister_producer);
    171
    172/**
    173 * irq_bypass_register_consumer - register IRQ bypass consumer
    174 * @consumer: pointer to consumer structure
    175 *
    176 * Add the provided IRQ consumer to the list of consumers and connect
    177 * with any matching token found on the IRQ producer list.
    178 */
    179int irq_bypass_register_consumer(struct irq_bypass_consumer *consumer)
    180{
    181	struct irq_bypass_consumer *tmp;
    182	struct irq_bypass_producer *producer;
    183	int ret;
    184
    185	if (!consumer->token ||
    186	    !consumer->add_producer || !consumer->del_producer)
    187		return -EINVAL;
    188
    189	might_sleep();
    190
    191	if (!try_module_get(THIS_MODULE))
    192		return -ENODEV;
    193
    194	mutex_lock(&lock);
    195
    196	list_for_each_entry(tmp, &consumers, node) {
    197		if (tmp->token == consumer->token || tmp == consumer) {
    198			ret = -EBUSY;
    199			goto out_err;
    200		}
    201	}
    202
    203	list_for_each_entry(producer, &producers, node) {
    204		if (producer->token == consumer->token) {
    205			ret = __connect(producer, consumer);
    206			if (ret)
    207				goto out_err;
    208			break;
    209		}
    210	}
    211
    212	list_add(&consumer->node, &consumers);
    213
    214	mutex_unlock(&lock);
    215
    216	return 0;
    217out_err:
    218	mutex_unlock(&lock);
    219	module_put(THIS_MODULE);
    220	return ret;
    221}
    222EXPORT_SYMBOL_GPL(irq_bypass_register_consumer);
    223
    224/**
    225 * irq_bypass_unregister_consumer - unregister IRQ bypass consumer
    226 * @consumer: pointer to consumer structure
    227 *
    228 * Remove a previously registered IRQ consumer from the list of consumers
    229 * and disconnect it from any connected IRQ producer.
    230 */
    231void irq_bypass_unregister_consumer(struct irq_bypass_consumer *consumer)
    232{
    233	struct irq_bypass_consumer *tmp;
    234	struct irq_bypass_producer *producer;
    235
    236	if (!consumer->token)
    237		return;
    238
    239	might_sleep();
    240
    241	if (!try_module_get(THIS_MODULE))
    242		return; /* nothing in the list anyway */
    243
    244	mutex_lock(&lock);
    245
    246	list_for_each_entry(tmp, &consumers, node) {
    247		if (tmp != consumer)
    248			continue;
    249
    250		list_for_each_entry(producer, &producers, node) {
    251			if (producer->token == consumer->token) {
    252				__disconnect(producer, consumer);
    253				break;
    254			}
    255		}
    256
    257		list_del(&consumer->node);
    258		module_put(THIS_MODULE);
    259		break;
    260	}
    261
    262	mutex_unlock(&lock);
    263
    264	module_put(THIS_MODULE);
    265}
    266EXPORT_SYMBOL_GPL(irq_bypass_unregister_consumer);