Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753029AbbLCSgs (ORCPT ); Thu, 3 Dec 2015 13:36:48 -0500 Received: from mga14.intel.com ([192.55.52.115]:26069 "EHLO mga14.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752844AbbLCSgq (ORCPT ); Thu, 3 Dec 2015 13:36:46 -0500 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.20,378,1444719600"; d="scan'208";a="7025553" From: Yunhong Jiang To: alex.williamson@redhat.com, pbonzini@redhat.com Cc: kvm@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 2/5] VIRT: Support runtime irq_bypass consumer Date: Thu, 3 Dec 2015 10:22:49 -0800 Message-Id: <1449166972-8894-3-git-send-email-yunhong.jiang@linux.intel.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1449166972-8894-1-git-send-email-yunhong.jiang@linux.intel.com> References: <1449166972-8894-1-git-send-email-yunhong.jiang@linux.intel.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 8172 Lines: 267 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 --- 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 +#include 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 #include #include +#include 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@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/