lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Thu,  3 Dec 2015 10:22:49 -0800
From:	Yunhong Jiang <yunhong.jiang@...ux.intel.com>
To:	alex.williamson@...hat.com, pbonzini@...hat.com
Cc:	kvm@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: [PATCH 2/5] VIRT: Support runtime irq_bypass consumer

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@...ux.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

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ