[PATCH 2/5] VIRT: Support runtime irq_bypass consumer

Yunhong Jiang
2015-12-03T18:36:48Z

Extend the irq_bypass manager to support runtime consumers. A runtime
irq_bypass consumer can handle interrupt when an interrupt triggered. A
runtime consumer has it's handle_irq() function set and passing a
irq_context for the irq handling.

A producer keep a link for the runtime consumers, so that it can invoke
each consumer's handle_irq() when irq invoked.

Currently the irq_bypass manager has several code path assuming there is
only one consumer/producer pair for each token. For example, when
register the producer, it exits the loop after finding one match
consumer. This is updated to support both static consumer (like for
Posted Interrupt consumer) and runtime consumer.

Signed-off-by: Yunhong Jiang <yunhong.jiang@linux.intel.com>
---
include/linux/irqbypass.h | 8 +++++
virt/lib/irqbypass.c | 82 +++++++++++++++++++++++++++++++++++------------
2 files changed, 69 insertions(+), 21 deletions(-)

diff --git a/include/linux/irqbypass.h b/include/linux/irqbypass.h
index 1551b5b2f4c2..d5bec0c7be3a 100644
--- a/include/linux/irqbypass.h
+++ b/include/linux/irqbypass.h
@@ -12,6 +12,7 @@
#define IRQBYPASS_H

#include <linux/list.h>
+#include <linux/srcu.h>

struct irq_bypass_consumer;

@@ -47,6 +48,9 @@ struct irq_bypass_consumer;
*/
struct irq_bypass_producer {
struct list_head node;
+ /* Update side is synchronized by the lock on irqbypass.c */
+ struct srcu_struct srcu;
+ struct list_head consumers;
void *token;
int irq;
int (*add_consumer)(struct irq_bypass_producer *,
@@ -61,6 +65,7 @@ struct irq_bypass_producer {
* struct irq_bypass_consumer - IRQ bypass consumer definition
* @node: IRQ bypass manager private list management
* @token: opaque token to match between producer and consumer
+ * @sibling: consumers with same token list management
* @add_producer: Connect the IRQ consumer to an IRQ producer
* @del_producer: Disconnect the IRQ consumer from an IRQ producer
* @stop: Perform any quiesce operations necessary prior to add/del (optional)
@@ -73,6 +78,7 @@ struct irq_bypass_producer {
*/
struct irq_bypass_consumer {
struct list_head node;
+ struct list_head sibling;
void *token;
int (*add_producer)(struct irq_bypass_consumer *,
struct irq_bypass_producer *);
@@ -80,6 +86,8 @@ struct irq_bypass_consumer {
struct irq_bypass_producer *);
void (*stop)(struct irq_bypass_consumer *);
void (*start)(struct irq_bypass_consumer *);
+ int (*handle_irq)(void *arg);
+ void *irq_context;
};

int irq_bypass_register_producer(struct irq_bypass_producer *);
diff --git a/virt/lib/irqbypass.c b/virt/lib/irqbypass.c
index 09a03b5a21ff..43ef9e2c77dc 100644
--- a/virt/lib/irqbypass.c
+++ b/virt/lib/irqbypass.c
@@ -21,6 +21,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/rculist.h>

MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("IRQ bypass manager utility module");
@@ -49,11 +50,8 @@ static int __connect(struct irq_bypass_producer *prod,
prod->del_consumer(prod, cons);
}

- if (cons->start)
- cons->start(cons);
- if (prod->start)
- prod->start(prod);
-
+ if (!ret && cons->handle_irq)
+ list_add_rcu(&cons->sibling, &prod->consumers);
return ret;
}

@@ -71,6 +69,11 @@ static void __disconnect(struct irq_bypass_producer *prod,
if (prod->del_consumer)
prod->del_consumer(prod, cons);

+ if (cons->handle_irq) {
+ list_del_rcu(&cons->sibling);
+ synchronize_srcu(&prod->srcu);
+ }
+
if (cons->start)
cons->start(cons);
if (prod->start)
@@ -87,7 +90,8 @@ static void __disconnect(struct irq_bypass_producer *prod,
int irq_bypass_register_producer(struct irq_bypass_producer *producer)
{
struct irq_bypass_producer *tmp;
- struct irq_bypass_consumer *consumer;
+ struct list_head *node, *next, siblings = LIST_HEAD_INIT(siblings);
+ int ret;

might_sleep();

@@ -96,6 +100,9 @@ int irq_bypass_register_producer(struct irq_bypass_producer *producer)

mutex_lock(&lock);

+ INIT_LIST_HEAD(&producer->consumers);
+ init_srcu_struct(&producer->srcu);
+
list_for_each_entry(tmp, &producers, node) {
if (tmp->token == producer->token) {
mutex_unlock(&lock);
@@ -104,23 +111,48 @@ int irq_bypass_register_producer(struct irq_bypass_producer *producer)
}
}

- list_for_each_entry(consumer, &consumers, node) {
+ list_for_each_safe(node, next, &consumers) {
+ struct irq_bypass_consumer *consumer = container_of(
+ node, struct irq_bypass_consumer, node);
+
if (consumer->token == producer->token) {
- int ret = __connect(producer, consumer);
- if (ret) {
- mutex_unlock(&lock);
- module_put(THIS_MODULE);
- return ret;
- }
- break;
+ ret = __connect(producer, consumer);
+ if (ret)
+ goto error;
+ /* Keep the connected consumers temply */
+ list_del(&consumer->node);
+ list_add_rcu(&consumer->node, &siblings);
}
}

+ list_for_each_safe(node, next, &siblings) {
+ struct irq_bypass_consumer *consumer = container_of(
+ node, struct irq_bypass_consumer, node);
+
+ list_del(&consumer->node);
+ list_add(&consumer->node, &consumers);
+ if (consumer->start)
+ consumer->start(consumer);
+ }
+
+ if (producer->start)
+ producer->start(producer);
list_add(&producer->node, &producers);

mutex_unlock(&lock);
-
return 0;
+
+error:
+ list_for_each_safe(node, next, &siblings) {
+ struct irq_bypass_consumer *consumer = container_of(
+ node, struct irq_bypass_consumer, node);
+
+ list_del(&consumer->node);
+ list_add(&consumer->node, &consumers);
+ }
+ mutex_unlock(&lock);
+ module_put(THIS_MODULE);
+ return ret;
}
EXPORT_SYMBOL_GPL(irq_bypass_register_producer);

@@ -133,7 +165,7 @@ EXPORT_SYMBOL_GPL(irq_bypass_register_producer);
*/
void irq_bypass_unregister_producer(struct irq_bypass_producer *producer)
{
- struct irq_bypass_producer *tmp;
+ struct irq_bypass_producer *tmp, *n;
struct irq_bypass_consumer *consumer;

might_sleep();
@@ -143,7 +175,7 @@ void irq_bypass_unregister_producer(struct irq_bypass_producer *producer)

mutex_lock(&lock);

- list_for_each_entry(tmp, &producers, node) {
+ list_for_each_entry_safe(tmp, n, &producers, node) {
if (tmp->token != producer->token)
continue;

@@ -159,6 +191,7 @@ void irq_bypass_unregister_producer(struct irq_bypass_producer *producer)
break;
}

+ cleanup_srcu_struct(&producer->srcu);
mutex_unlock(&lock);

module_put(THIS_MODULE);
@@ -180,6 +213,9 @@ int irq_bypass_register_consumer(struct irq_bypass_consumer *consumer)
if (!consumer->add_producer || !consumer->del_producer)
return -EINVAL;

+ if (consumer->handle_irq && !consumer->irq_context)
+ return -EINVAL;
+
might_sleep();

if (!try_module_get(THIS_MODULE))
@@ -188,7 +224,7 @@ int irq_bypass_register_consumer(struct irq_bypass_consumer *consumer)
mutex_lock(&lock);

list_for_each_entry(tmp, &consumers, node) {
- if (tmp->token == consumer->token) {
+ if (tmp == consumer) {
mutex_unlock(&lock);
module_put(THIS_MODULE);
return -EBUSY;
@@ -203,6 +239,10 @@ int irq_bypass_register_consumer(struct irq_bypass_consumer *consumer)
module_put(THIS_MODULE);
return ret;
}
+ if (consumer->start)
+ consumer->start(consumer);
+ if (producer->start)
+ producer->start(producer);
break;
}
}
@@ -224,7 +264,7 @@ EXPORT_SYMBOL_GPL(irq_bypass_register_consumer);
*/
void irq_bypass_unregister_consumer(struct irq_bypass_consumer *consumer)
{
- struct irq_bypass_consumer *tmp;
+ struct irq_bypass_consumer *tmp, *n;
struct irq_bypass_producer *producer;

might_sleep();
@@ -234,8 +274,8 @@ void irq_bypass_unregister_consumer(struct irq_bypass_consumer *consumer)

mutex_lock(&lock);

- list_for_each_entry(tmp, &consumers, node) {
- if (tmp->token != consumer->token)
+ list_for_each_entry_safe(tmp, n, &consumers, node) {
+ if (tmp != consumer)
continue;

list_for_each_entry(producer, &producers, node) {
--
1.8.3.1