linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/10 v3] iommu/amd: lock splitting & GFP_KERNEL allocation
@ 2018-03-22 15:22 Sebastian Andrzej Siewior
  2018-03-22 15:22 ` [PATCH 01/10] iommu/amd: take into account that alloc_dev_data() may return NULL Sebastian Andrzej Siewior
                   ` (10 more replies)
  0 siblings, 11 replies; 14+ messages in thread
From: Sebastian Andrzej Siewior @ 2018-03-22 15:22 UTC (permalink / raw)
  To: Joerg Roedel; +Cc: iommu, linux-kernel, tglx, Scott Wood

Hi,

this is the rebase on top of iommu/x86/amd of my last series. It takes
Scotts comments into consideration from my v2.

It contains lock splitting and GFP_KERNEL allocation of remap-table.
Mostly cleanup.

The patches were boot tested on an AMD EPYC 7601.

Sebastian

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 01/10] iommu/amd: take into account that alloc_dev_data() may return NULL
  2018-03-22 15:22 [PATCH 00/10 v3] iommu/amd: lock splitting & GFP_KERNEL allocation Sebastian Andrzej Siewior
@ 2018-03-22 15:22 ` Sebastian Andrzej Siewior
  2018-03-22 15:22 ` [PATCH 02/10] iommu/amd: turn dev_data_list into a lock less list Sebastian Andrzej Siewior
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Sebastian Andrzej Siewior @ 2018-03-22 15:22 UTC (permalink / raw)
  To: Joerg Roedel
  Cc: iommu, linux-kernel, tglx, Scott Wood, Sebastian Andrzej Siewior,
	Baoquan He

find_dev_data() does not check whether the return value alloc_dev_data()
is NULL. This was okay once because the pointer was returned once as-is.
Since commit df3f7a6e8e85 ("iommu/amd: Use is_attach_deferred
call-back") the pointer may be used within find_dev_data() so a NULL
check is required.

Cc: Baoquan He <bhe@redhat.com>
Fixes: df3f7a6e8e85 ("iommu/amd: Use is_attach_deferred call-back")
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/iommu/amd_iommu.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 4cd19f93ca15..121c54f1c768 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -310,6 +310,8 @@ static struct iommu_dev_data *find_dev_data(u16 devid)
 
 	if (dev_data == NULL) {
 		dev_data = alloc_dev_data(devid);
+		if (!dev_data)
+			return NULL;
 
 		if (translation_pre_enabled(iommu))
 			dev_data->defer_attach = true;
-- 
2.16.2

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 02/10] iommu/amd: turn dev_data_list into a lock less list
  2018-03-22 15:22 [PATCH 00/10 v3] iommu/amd: lock splitting & GFP_KERNEL allocation Sebastian Andrzej Siewior
  2018-03-22 15:22 ` [PATCH 01/10] iommu/amd: take into account that alloc_dev_data() may return NULL Sebastian Andrzej Siewior
@ 2018-03-22 15:22 ` Sebastian Andrzej Siewior
  2018-03-22 15:22 ` [PATCH 03/10] iommu/amd: split domain id out of amd_iommu_devtable_lock Sebastian Andrzej Siewior
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Sebastian Andrzej Siewior @ 2018-03-22 15:22 UTC (permalink / raw)
  To: Joerg Roedel
  Cc: iommu, linux-kernel, tglx, Scott Wood, Sebastian Andrzej Siewior

alloc_dev_data() adds new items to dev_data_list and search_dev_data()
is searching for items in this list. Both protect the access to the list
with a spinlock.
There is no need to navigate forth and back within the list and there is
also no deleting of a specific item. This qualifies the list to become a
lock less list and as part of this, the spinlock can be removed.
With this change the ordering of those items within the list is changed:
before the change new items were added to the end of the list, now they
are added to the front. I don't think it matters but wanted to mention
it.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/iommu/amd_iommu.c       | 28 ++++++++++------------------
 drivers/iommu/amd_iommu_types.h |  2 +-
 2 files changed, 11 insertions(+), 19 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 121c54f1c768..d4c2b1a11924 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -83,8 +83,7 @@
 static DEFINE_RWLOCK(amd_iommu_devtable_lock);
 
 /* List of all available dev_data structures */
-static LIST_HEAD(dev_data_list);
-static DEFINE_SPINLOCK(dev_data_list_lock);
+static LLIST_HEAD(dev_data_list);
 
 LIST_HEAD(ioapic_map);
 LIST_HEAD(hpet_map);
@@ -203,40 +202,33 @@ static struct dma_ops_domain* to_dma_ops_domain(struct protection_domain *domain
 static struct iommu_dev_data *alloc_dev_data(u16 devid)
 {
 	struct iommu_dev_data *dev_data;
-	unsigned long flags;
 
 	dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL);
 	if (!dev_data)
 		return NULL;
 
 	dev_data->devid = devid;
-
-	spin_lock_irqsave(&dev_data_list_lock, flags);
-	list_add_tail(&dev_data->dev_data_list, &dev_data_list);
-	spin_unlock_irqrestore(&dev_data_list_lock, flags);
-
 	ratelimit_default_init(&dev_data->rs);
 
+	llist_add(&dev_data->dev_data_list, &dev_data_list);
 	return dev_data;
 }
 
 static struct iommu_dev_data *search_dev_data(u16 devid)
 {
 	struct iommu_dev_data *dev_data;
-	unsigned long flags;
+	struct llist_node *node;
 
-	spin_lock_irqsave(&dev_data_list_lock, flags);
-	list_for_each_entry(dev_data, &dev_data_list, dev_data_list) {
+	if (llist_empty(&dev_data_list))
+		return NULL;
+
+	node = dev_data_list.first;
+	llist_for_each_entry(dev_data, node, dev_data_list) {
 		if (dev_data->devid == devid)
-			goto out_unlock;
+			return dev_data;
 	}
 
-	dev_data = NULL;
-
-out_unlock:
-	spin_unlock_irqrestore(&dev_data_list_lock, flags);
-
-	return dev_data;
+	return NULL;
 }
 
 static int __last_alias(struct pci_dev *pdev, u16 alias, void *data)
diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h
index da886b0095aa..1c9b080276c9 100644
--- a/drivers/iommu/amd_iommu_types.h
+++ b/drivers/iommu/amd_iommu_types.h
@@ -627,7 +627,7 @@ struct devid_map {
  */
 struct iommu_dev_data {
 	struct list_head list;		  /* For domain->dev_list */
-	struct list_head dev_data_list;	  /* For global dev_data_list */
+	struct llist_node dev_data_list;  /* For global dev_data_list */
 	struct protection_domain *domain; /* Domain the device is bound to */
 	u16 devid;			  /* PCI Device ID */
 	u16 alias;			  /* Alias Device ID */
-- 
2.16.2

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 03/10] iommu/amd: split domain id out of amd_iommu_devtable_lock
  2018-03-22 15:22 [PATCH 00/10 v3] iommu/amd: lock splitting & GFP_KERNEL allocation Sebastian Andrzej Siewior
  2018-03-22 15:22 ` [PATCH 01/10] iommu/amd: take into account that alloc_dev_data() may return NULL Sebastian Andrzej Siewior
  2018-03-22 15:22 ` [PATCH 02/10] iommu/amd: turn dev_data_list into a lock less list Sebastian Andrzej Siewior
@ 2018-03-22 15:22 ` Sebastian Andrzej Siewior
  2018-03-22 15:22 ` [PATCH 04/10] iommu/amd: split irq_lookup_table out of the amd_iommu_devtable_lock Sebastian Andrzej Siewior
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Sebastian Andrzej Siewior @ 2018-03-22 15:22 UTC (permalink / raw)
  To: Joerg Roedel
  Cc: iommu, linux-kernel, tglx, Scott Wood, Sebastian Andrzej Siewior

domain_id_alloc() and domain_id_free() is used for id management. Those
two function share a bitmap (amd_iommu_pd_alloc_bitmap) and set/clear
bits based on id allocation. There is no need to share this with
amd_iommu_devtable_lock, it can use its own lock for this operation.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/iommu/amd_iommu.c | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index d4c2b1a11924..fcfdce70707d 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -81,6 +81,7 @@
 #define AMD_IOMMU_PGSIZES	((~0xFFFUL) & ~(2ULL << 38))
 
 static DEFINE_RWLOCK(amd_iommu_devtable_lock);
+static DEFINE_SPINLOCK(pd_bitmap_lock);
 
 /* List of all available dev_data structures */
 static LLIST_HEAD(dev_data_list);
@@ -1600,29 +1601,26 @@ static void del_domain_from_list(struct protection_domain *domain)
 
 static u16 domain_id_alloc(void)
 {
-	unsigned long flags;
 	int id;
 
-	write_lock_irqsave(&amd_iommu_devtable_lock, flags);
+	spin_lock(&pd_bitmap_lock);
 	id = find_first_zero_bit(amd_iommu_pd_alloc_bitmap, MAX_DOMAIN_ID);
 	BUG_ON(id == 0);
 	if (id > 0 && id < MAX_DOMAIN_ID)
 		__set_bit(id, amd_iommu_pd_alloc_bitmap);
 	else
 		id = 0;
-	write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
+	spin_unlock(&pd_bitmap_lock);
 
 	return id;
 }
 
 static void domain_id_free(int id)
 {
-	unsigned long flags;
-
-	write_lock_irqsave(&amd_iommu_devtable_lock, flags);
+	spin_lock(&pd_bitmap_lock);
 	if (id > 0 && id < MAX_DOMAIN_ID)
 		__clear_bit(id, amd_iommu_pd_alloc_bitmap);
-	write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
+	spin_unlock(&pd_bitmap_lock);
 }
 
 #define DEFINE_FREE_PT_FN(LVL, FN)				\
-- 
2.16.2

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 04/10] iommu/amd: split irq_lookup_table out of the amd_iommu_devtable_lock
  2018-03-22 15:22 [PATCH 00/10 v3] iommu/amd: lock splitting & GFP_KERNEL allocation Sebastian Andrzej Siewior
                   ` (2 preceding siblings ...)
  2018-03-22 15:22 ` [PATCH 03/10] iommu/amd: split domain id out of amd_iommu_devtable_lock Sebastian Andrzej Siewior
@ 2018-03-22 15:22 ` Sebastian Andrzej Siewior
  2018-03-22 15:22 ` [PATCH 05/10] iommu/amd: remove the special case from alloc_irq_table() Sebastian Andrzej Siewior
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Sebastian Andrzej Siewior @ 2018-03-22 15:22 UTC (permalink / raw)
  To: Joerg Roedel
  Cc: iommu, linux-kernel, tglx, Scott Wood, Sebastian Andrzej Siewior

The function get_irq_table() reads/writes irq_lookup_table while holding
the amd_iommu_devtable_lock. It also modifies
amd_iommu_dev_table[].data[2].
set_dte_entry() is using amd_iommu_dev_table[].data[0|1] (under the
domain->lock) so it should be okay. The access to the iommu is
serialized with its own (iommu's) lock.

So split out get_irq_table() out of amd_iommu_devtable_lock's lock.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/iommu/amd_iommu.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index fcfdce70707d..a87d2fee68db 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -82,6 +82,7 @@
 
 static DEFINE_RWLOCK(amd_iommu_devtable_lock);
 static DEFINE_SPINLOCK(pd_bitmap_lock);
+static DEFINE_SPINLOCK(iommu_table_lock);
 
 /* List of all available dev_data structures */
 static LLIST_HEAD(dev_data_list);
@@ -3623,7 +3624,7 @@ static struct irq_remap_table *alloc_irq_table(u16 devid, bool ioapic)
 	unsigned long flags;
 	u16 alias;
 
-	write_lock_irqsave(&amd_iommu_devtable_lock, flags);
+	spin_lock_irqsave(&iommu_table_lock, flags);
 
 	iommu = amd_iommu_rlookup_table[devid];
 	if (!iommu)
@@ -3688,7 +3689,7 @@ static struct irq_remap_table *alloc_irq_table(u16 devid, bool ioapic)
 	iommu_completion_wait(iommu);
 
 out_unlock:
-	write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
+	spin_unlock_irqrestore(&iommu_table_lock, flags);
 
 	return table;
 }
-- 
2.16.2

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 05/10] iommu/amd: remove the special case from alloc_irq_table()
  2018-03-22 15:22 [PATCH 00/10 v3] iommu/amd: lock splitting & GFP_KERNEL allocation Sebastian Andrzej Siewior
                   ` (3 preceding siblings ...)
  2018-03-22 15:22 ` [PATCH 04/10] iommu/amd: split irq_lookup_table out of the amd_iommu_devtable_lock Sebastian Andrzej Siewior
@ 2018-03-22 15:22 ` Sebastian Andrzej Siewior
  2018-03-22 15:22 ` [PATCH 06/10] iommu/amd: use `table' instead `irt' as variable name in amd_iommu_update_ga() Sebastian Andrzej Siewior
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Sebastian Andrzej Siewior @ 2018-03-22 15:22 UTC (permalink / raw)
  To: Joerg Roedel
  Cc: iommu, linux-kernel, tglx, Scott Wood, Sebastian Andrzej Siewior

alloc_irq_table() has a special ioapic argument. If set then it will
pre-allocate / reserve the first 32 indexes. The argument is only once
true and it would make alloc_irq_table() a little simpler if we would
extract the special bits to the caller.
The caller of irq_remapping_alloc() is holding irq_domain_mutex so the
initialization of iommu->irte_ops->set_allocated() should not race
against other user.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/iommu/amd_iommu.c | 34 ++++++++++++++++++++--------------
 1 file changed, 20 insertions(+), 14 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index a87d2fee68db..a5982a61f181 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -3617,7 +3617,7 @@ static struct irq_remap_table *get_irq_table(u16 devid)
 	return table;
 }
 
-static struct irq_remap_table *alloc_irq_table(u16 devid, bool ioapic)
+static struct irq_remap_table *alloc_irq_table(u16 devid)
 {
 	struct irq_remap_table *table = NULL;
 	struct amd_iommu *iommu;
@@ -3651,10 +3651,6 @@ static struct irq_remap_table *alloc_irq_table(u16 devid, bool ioapic)
 	/* Initialize table spin-lock */
 	raw_spin_lock_init(&table->lock);
 
-	if (ioapic)
-		/* Keep the first 32 indexes free for IOAPIC interrupts */
-		table->min_index = 32;
-
 	table->table = kmem_cache_alloc(amd_iommu_irq_cache, GFP_ATOMIC);
 	if (!table->table) {
 		kfree(table);
@@ -3669,12 +3665,6 @@ static struct irq_remap_table *alloc_irq_table(u16 devid, bool ioapic)
 		memset(table->table, 0,
 		       (MAX_IRQS_PER_TABLE * (sizeof(u64) * 2)));
 
-	if (ioapic) {
-		int i;
-
-		for (i = 0; i < 32; ++i)
-			iommu->irte_ops->set_allocated(table, i);
-	}
 
 	irq_lookup_table[devid] = table;
 	set_dte_irq_entry(devid, table);
@@ -3704,7 +3694,7 @@ static int alloc_irq_index(u16 devid, int count, bool align)
 	if (!iommu)
 		return -ENODEV;
 
-	table = alloc_irq_table(devid, false);
+	table = alloc_irq_table(devid);
 	if (!table)
 		return -ENODEV;
 
@@ -4130,10 +4120,26 @@ static int irq_remapping_alloc(struct irq_domain *domain, unsigned int virq,
 		return ret;
 
 	if (info->type == X86_IRQ_ALLOC_TYPE_IOAPIC) {
-		if (alloc_irq_table(devid, true))
+		struct irq_remap_table *table;
+		struct amd_iommu *iommu;
+
+		table = alloc_irq_table(devid);
+		if (table) {
+			if (!table->min_index) {
+				/*
+				 * Keep the first 32 indexes free for IOAPIC
+				 * interrupts.
+				 */
+				table->min_index = 32;
+				iommu = amd_iommu_rlookup_table[devid];
+				for (i = 0; i < 32; ++i)
+					iommu->irte_ops->set_allocated(table, i);
+			}
+			WARN_ON(table->min_index != 32);
 			index = info->ioapic_pin;
-		else
+		} else {
 			ret = -ENOMEM;
+		}
 	} else {
 		bool align = (info->type == X86_IRQ_ALLOC_TYPE_MSI);
 
-- 
2.16.2

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 06/10] iommu/amd: use `table' instead `irt' as variable name in amd_iommu_update_ga()
  2018-03-22 15:22 [PATCH 00/10 v3] iommu/amd: lock splitting & GFP_KERNEL allocation Sebastian Andrzej Siewior
                   ` (4 preceding siblings ...)
  2018-03-22 15:22 ` [PATCH 05/10] iommu/amd: remove the special case from alloc_irq_table() Sebastian Andrzej Siewior
@ 2018-03-22 15:22 ` Sebastian Andrzej Siewior
  2018-03-22 15:22 ` [PATCH 07/10] iommu/amd: factor out setting the remap table for a devid Sebastian Andrzej Siewior
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Sebastian Andrzej Siewior @ 2018-03-22 15:22 UTC (permalink / raw)
  To: Joerg Roedel
  Cc: iommu, linux-kernel, tglx, Scott Wood, Sebastian Andrzej Siewior

The variable of type struct irq_remap_table is always named `table'
except in amd_iommu_update_ga() where it is called `irt'. Make it
consistent and name it also `table'.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/iommu/amd_iommu.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index a5982a61f181..ea120c7b46c9 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -4405,7 +4405,7 @@ int amd_iommu_update_ga(int cpu, bool is_run, void *data)
 {
 	unsigned long flags;
 	struct amd_iommu *iommu;
-	struct irq_remap_table *irt;
+	struct irq_remap_table *table;
 	struct amd_ir_data *ir_data = (struct amd_ir_data *)data;
 	int devid = ir_data->irq_2_irte.devid;
 	struct irte_ga *entry = (struct irte_ga *) ir_data->entry;
@@ -4419,11 +4419,11 @@ int amd_iommu_update_ga(int cpu, bool is_run, void *data)
 	if (!iommu)
 		return -ENODEV;
 
-	irt = get_irq_table(devid);
-	if (!irt)
+	table = get_irq_table(devid);
+	if (!table)
 		return -ENODEV;
 
-	raw_spin_lock_irqsave(&irt->lock, flags);
+	raw_spin_lock_irqsave(&table->lock, flags);
 
 	if (ref->lo.fields_vapic.guest_mode) {
 		if (cpu >= 0)
@@ -4432,7 +4432,7 @@ int amd_iommu_update_ga(int cpu, bool is_run, void *data)
 		barrier();
 	}
 
-	raw_spin_unlock_irqrestore(&irt->lock, flags);
+	raw_spin_unlock_irqrestore(&table->lock, flags);
 
 	iommu_flush_irt(iommu, devid);
 	iommu_completion_wait(iommu);
-- 
2.16.2

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 07/10] iommu/amd: factor out setting the remap table for a devid
  2018-03-22 15:22 [PATCH 00/10 v3] iommu/amd: lock splitting & GFP_KERNEL allocation Sebastian Andrzej Siewior
                   ` (5 preceding siblings ...)
  2018-03-22 15:22 ` [PATCH 06/10] iommu/amd: use `table' instead `irt' as variable name in amd_iommu_update_ga() Sebastian Andrzej Siewior
@ 2018-03-22 15:22 ` Sebastian Andrzej Siewior
  2018-03-22 15:22 ` [PATCH 08/10] iommu/amd: drop the lock while allocating new irq remap table Sebastian Andrzej Siewior
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Sebastian Andrzej Siewior @ 2018-03-22 15:22 UTC (permalink / raw)
  To: Joerg Roedel
  Cc: iommu, linux-kernel, tglx, Scott Wood, Sebastian Andrzej Siewior

Setting the IRQ remap table for a specific devid (or its alias devid)
includes three steps. Those three steps are always repeated each time
this is done.
Introduce a new helper function, move those steps there and use that
function instead. The compiler can still decide if it is worth to
inline.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/iommu/amd_iommu.c | 23 ++++++++++++-----------
 1 file changed, 12 insertions(+), 11 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index ea120c7b46c9..11ea2d656be8 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -3617,6 +3617,14 @@ static struct irq_remap_table *get_irq_table(u16 devid)
 	return table;
 }
 
+static void set_remap_table_entry(struct amd_iommu *iommu, u16 devid,
+				  struct irq_remap_table *table)
+{
+	irq_lookup_table[devid] = table;
+	set_dte_irq_entry(devid, table);
+	iommu_flush_dte(iommu, devid);
+}
+
 static struct irq_remap_table *alloc_irq_table(u16 devid)
 {
 	struct irq_remap_table *table = NULL;
@@ -3637,9 +3645,7 @@ static struct irq_remap_table *alloc_irq_table(u16 devid)
 	alias = amd_iommu_alias_table[devid];
 	table = irq_lookup_table[alias];
 	if (table) {
-		irq_lookup_table[devid] = table;
-		set_dte_irq_entry(devid, table);
-		iommu_flush_dte(iommu, devid);
+		set_remap_table_entry(iommu, devid, table);
 		goto out;
 	}
 
@@ -3666,14 +3672,9 @@ static struct irq_remap_table *alloc_irq_table(u16 devid)
 		       (MAX_IRQS_PER_TABLE * (sizeof(u64) * 2)));
 
 
-	irq_lookup_table[devid] = table;
-	set_dte_irq_entry(devid, table);
-	iommu_flush_dte(iommu, devid);
-	if (devid != alias) {
-		irq_lookup_table[alias] = table;
-		set_dte_irq_entry(alias, table);
-		iommu_flush_dte(iommu, alias);
-	}
+	set_remap_table_entry(iommu, devid, table);
+	if (devid != alias)
+		set_remap_table_entry(iommu, alias, table);
 
 out:
 	iommu_completion_wait(iommu);
-- 
2.16.2

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 08/10] iommu/amd: drop the lock while allocating new irq remap table
  2018-03-22 15:22 [PATCH 00/10 v3] iommu/amd: lock splitting & GFP_KERNEL allocation Sebastian Andrzej Siewior
                   ` (6 preceding siblings ...)
  2018-03-22 15:22 ` [PATCH 07/10] iommu/amd: factor out setting the remap table for a devid Sebastian Andrzej Siewior
@ 2018-03-22 15:22 ` Sebastian Andrzej Siewior
  2018-03-22 15:22 ` [PATCH 09/10] iommu/amd: make amd_iommu_devtable_lock a spin_lock Sebastian Andrzej Siewior
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Sebastian Andrzej Siewior @ 2018-03-22 15:22 UTC (permalink / raw)
  To: Joerg Roedel
  Cc: iommu, linux-kernel, tglx, Scott Wood, Sebastian Andrzej Siewior

The irq_remap_table is allocated while the iommu_table_lock is held with
interrupts disabled.
>From looking at the call sites, all callers are in the early device
initialisation (apic_bsp_setup(), pci_enable_device(),
pci_enable_msi()) so make sense to drop the lock which also enables
interrupts and try to allocate that memory with GFP_KERNEL instead
GFP_ATOMIC.

Since during the allocation the iommu_table_lock is dropped, we need to
recheck if table exists after the lock has been reacquired. I *think*
that it is impossible that the "devid" entry appears in irq_lookup_table
while the lock is dropped since the same device can only be probed once.
However I check for both cases, just to be sure.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/iommu/amd_iommu.c | 65 +++++++++++++++++++++++++++++++++--------------
 1 file changed, 46 insertions(+), 19 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 11ea2d656be8..c493d345b3ef 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -3617,6 +3617,30 @@ static struct irq_remap_table *get_irq_table(u16 devid)
 	return table;
 }
 
+static struct irq_remap_table *__alloc_irq_table(void)
+{
+	struct irq_remap_table *table;
+
+	table = kzalloc(sizeof(*table), GFP_KERNEL);
+	if (!table)
+		return NULL;
+
+	table->table = kmem_cache_alloc(amd_iommu_irq_cache, GFP_KERNEL);
+	if (!table->table) {
+		kfree(table);
+		return NULL;
+	}
+	raw_spin_lock_init(&table->lock);
+
+	if (!AMD_IOMMU_GUEST_IR_GA(amd_iommu_guest_ir))
+		memset(table->table, 0,
+		       MAX_IRQS_PER_TABLE * sizeof(u32));
+	else
+		memset(table->table, 0,
+		       (MAX_IRQS_PER_TABLE * (sizeof(u64) * 2)));
+	return table;
+}
+
 static void set_remap_table_entry(struct amd_iommu *iommu, u16 devid,
 				  struct irq_remap_table *table)
 {
@@ -3628,6 +3652,7 @@ static void set_remap_table_entry(struct amd_iommu *iommu, u16 devid,
 static struct irq_remap_table *alloc_irq_table(u16 devid)
 {
 	struct irq_remap_table *table = NULL;
+	struct irq_remap_table *new_table = NULL;
 	struct amd_iommu *iommu;
 	unsigned long flags;
 	u16 alias;
@@ -3646,42 +3671,44 @@ static struct irq_remap_table *alloc_irq_table(u16 devid)
 	table = irq_lookup_table[alias];
 	if (table) {
 		set_remap_table_entry(iommu, devid, table);
-		goto out;
+		goto out_wait;
 	}
+	spin_unlock_irqrestore(&iommu_table_lock, flags);
 
 	/* Nothing there yet, allocate new irq remapping table */
-	table = kzalloc(sizeof(*table), GFP_ATOMIC);
-	if (!table)
+	new_table = __alloc_irq_table();
+	if (!new_table)
+		return NULL;
+
+	spin_lock_irqsave(&iommu_table_lock, flags);
+
+	table = irq_lookup_table[devid];
+	if (table)
 		goto out_unlock;
 
-	/* Initialize table spin-lock */
-	raw_spin_lock_init(&table->lock);
-
-	table->table = kmem_cache_alloc(amd_iommu_irq_cache, GFP_ATOMIC);
-	if (!table->table) {
-		kfree(table);
-		table = NULL;
-		goto out_unlock;
+	table = irq_lookup_table[alias];
+	if (table) {
+		set_remap_table_entry(iommu, devid, table);
+		goto out_wait;
 	}
 
-	if (!AMD_IOMMU_GUEST_IR_GA(amd_iommu_guest_ir))
-		memset(table->table, 0,
-		       MAX_IRQS_PER_TABLE * sizeof(u32));
-	else
-		memset(table->table, 0,
-		       (MAX_IRQS_PER_TABLE * (sizeof(u64) * 2)));
-
+	table = new_table;
+	new_table = NULL;
 
 	set_remap_table_entry(iommu, devid, table);
 	if (devid != alias)
 		set_remap_table_entry(iommu, alias, table);
 
-out:
+out_wait:
 	iommu_completion_wait(iommu);
 
 out_unlock:
 	spin_unlock_irqrestore(&iommu_table_lock, flags);
 
+	if (new_table) {
+		kmem_cache_free(amd_iommu_irq_cache, new_table->table);
+		kfree(new_table);
+	}
 	return table;
 }
 
-- 
2.16.2

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 09/10] iommu/amd: make amd_iommu_devtable_lock a spin_lock
  2018-03-22 15:22 [PATCH 00/10 v3] iommu/amd: lock splitting & GFP_KERNEL allocation Sebastian Andrzej Siewior
                   ` (7 preceding siblings ...)
  2018-03-22 15:22 ` [PATCH 08/10] iommu/amd: drop the lock while allocating new irq remap table Sebastian Andrzej Siewior
@ 2018-03-22 15:22 ` Sebastian Andrzej Siewior
  2018-03-22 15:22 ` [PATCH 10/10] iommu/amd: return proper error code in irq_remapping_alloc() Sebastian Andrzej Siewior
  2018-03-29  8:38 ` [PATCH 00/10 v3] iommu/amd: lock splitting & GFP_KERNEL allocation Joerg Roedel
  10 siblings, 0 replies; 14+ messages in thread
From: Sebastian Andrzej Siewior @ 2018-03-22 15:22 UTC (permalink / raw)
  To: Joerg Roedel
  Cc: iommu, linux-kernel, tglx, Scott Wood, Sebastian Andrzej Siewior

Before commit 0bb6e243d7fb ("iommu/amd: Support IOMMU_DOMAIN_DMA type
allocation") amd_iommu_devtable_lock had a read_lock() user but now
there are none. In fact, after the mentioned commit we had only
write_lock() user of the lock. Since there is no reason to keep it as
writer lock, change its type to a spin_lock.
I *think* that we might even be able to remove the lock because all its
current user seem to have their own protection.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/iommu/amd_iommu.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index c493d345b3ef..23e26a4b708e 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -80,7 +80,7 @@
  */
 #define AMD_IOMMU_PGSIZES	((~0xFFFUL) & ~(2ULL << 38))
 
-static DEFINE_RWLOCK(amd_iommu_devtable_lock);
+static DEFINE_SPINLOCK(amd_iommu_devtable_lock);
 static DEFINE_SPINLOCK(pd_bitmap_lock);
 static DEFINE_SPINLOCK(iommu_table_lock);
 
@@ -2097,9 +2097,9 @@ static int attach_device(struct device *dev,
 	}
 
 skip_ats_check:
-	write_lock_irqsave(&amd_iommu_devtable_lock, flags);
+	spin_lock_irqsave(&amd_iommu_devtable_lock, flags);
 	ret = __attach_device(dev_data, domain);
-	write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
+	spin_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
 
 	/*
 	 * We might boot into a crash-kernel here. The crashed kernel
@@ -2149,9 +2149,9 @@ static void detach_device(struct device *dev)
 	domain   = dev_data->domain;
 
 	/* lock device table */
-	write_lock_irqsave(&amd_iommu_devtable_lock, flags);
+	spin_lock_irqsave(&amd_iommu_devtable_lock, flags);
 	__detach_device(dev_data);
-	write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
+	spin_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
 
 	if (!dev_is_pci(dev))
 		return;
@@ -2814,7 +2814,7 @@ static void cleanup_domain(struct protection_domain *domain)
 	struct iommu_dev_data *entry;
 	unsigned long flags;
 
-	write_lock_irqsave(&amd_iommu_devtable_lock, flags);
+	spin_lock_irqsave(&amd_iommu_devtable_lock, flags);
 
 	while (!list_empty(&domain->dev_list)) {
 		entry = list_first_entry(&domain->dev_list,
@@ -2822,7 +2822,7 @@ static void cleanup_domain(struct protection_domain *domain)
 		__detach_device(entry);
 	}
 
-	write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
+	spin_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
 }
 
 static void protection_domain_free(struct protection_domain *domain)
-- 
2.16.2

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 10/10] iommu/amd: return proper error code in irq_remapping_alloc()
  2018-03-22 15:22 [PATCH 00/10 v3] iommu/amd: lock splitting & GFP_KERNEL allocation Sebastian Andrzej Siewior
                   ` (8 preceding siblings ...)
  2018-03-22 15:22 ` [PATCH 09/10] iommu/amd: make amd_iommu_devtable_lock a spin_lock Sebastian Andrzej Siewior
@ 2018-03-22 15:22 ` Sebastian Andrzej Siewior
  2018-03-29  8:38 ` [PATCH 00/10 v3] iommu/amd: lock splitting & GFP_KERNEL allocation Joerg Roedel
  10 siblings, 0 replies; 14+ messages in thread
From: Sebastian Andrzej Siewior @ 2018-03-22 15:22 UTC (permalink / raw)
  To: Joerg Roedel
  Cc: iommu, linux-kernel, tglx, Scott Wood, Sebastian Andrzej Siewior

In the unlikely case when alloc_irq_table() is not able to return a
remap table then "ret" will be assigned with an error code. Later, the
code checks `index' and if it is negative (which it is because it is
initialized with `-1') and then then function properly aborts but
returns `-1' instead `-ENOMEM' what was intended.
In order to correct this, I assign -ENOMEM to index.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/iommu/amd_iommu.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 23e26a4b708e..6107e24bed8a 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -4124,7 +4124,7 @@ static int irq_remapping_alloc(struct irq_domain *domain, unsigned int virq,
 	struct amd_ir_data *data = NULL;
 	struct irq_cfg *cfg;
 	int i, ret, devid;
-	int index = -1;
+	int index;
 
 	if (!info)
 		return -EINVAL;
@@ -4166,7 +4166,7 @@ static int irq_remapping_alloc(struct irq_domain *domain, unsigned int virq,
 			WARN_ON(table->min_index != 32);
 			index = info->ioapic_pin;
 		} else {
-			ret = -ENOMEM;
+			index = -ENOMEM;
 		}
 	} else {
 		bool align = (info->type == X86_IRQ_ALLOC_TYPE_MSI);
-- 
2.16.2

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH 00/10 v3] iommu/amd: lock splitting & GFP_KERNEL allocation
  2018-03-22 15:22 [PATCH 00/10 v3] iommu/amd: lock splitting & GFP_KERNEL allocation Sebastian Andrzej Siewior
                   ` (9 preceding siblings ...)
  2018-03-22 15:22 ` [PATCH 10/10] iommu/amd: return proper error code in irq_remapping_alloc() Sebastian Andrzej Siewior
@ 2018-03-29  8:38 ` Joerg Roedel
  10 siblings, 0 replies; 14+ messages in thread
From: Joerg Roedel @ 2018-03-29  8:38 UTC (permalink / raw)
  To: Sebastian Andrzej Siewior; +Cc: iommu, linux-kernel, tglx, Scott Wood

On Thu, Mar 22, 2018 at 04:22:32PM +0100, Sebastian Andrzej Siewior wrote:
> Hi,
> 
> this is the rebase on top of iommu/x86/amd of my last series. It takes
> Scotts comments into consideration from my v2.
> 
> It contains lock splitting and GFP_KERNEL allocation of remap-table.
> Mostly cleanup.
> 
> The patches were boot tested on an AMD EPYC 7601.

Applied, thanks.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 08/10] iommu/amd: drop the lock while allocating new irq remap table
  2018-02-23 22:27 ` [PATCH 08/10] iommu/amd: drop the lock while allocating new irq remap table Sebastian Andrzej Siewior
@ 2018-03-15 12:58   ` Joerg Roedel
  0 siblings, 0 replies; 14+ messages in thread
From: Joerg Roedel @ 2018-03-15 12:58 UTC (permalink / raw)
  To: Sebastian Andrzej Siewior; +Cc: iommu, linux-kernel, tglx

On Fri, Feb 23, 2018 at 11:27:34PM +0100, Sebastian Andrzej Siewior wrote:
> The irq_remap_table is allocated while the iommu_table_lock is held with
> interrupts disabled. While this works it makes RT scream very loudly.
> >From looking at the call sites, all callers are in the early device
> initialisation (apic_bsp_setup(), pci_enable_device(),
> pci_enable_msi()) so make sense to drop the lock which also enables
> interrupts and try to allocate that memory with GFP_KERNEL instead
> GFP_ATOMIC.
> 
> Since during the allocation the iommu_table_lock is dropped, we need to
> recheck if table exists after the lock has been reacquired. I *think*
> that it is impossible that the "devid" entry appears in irq_lookup_table
> while the lock is dropped since the same device can only be probed once.
> It is more likely that another device added an `alias' entry. However I
> check for both cases, just to be sure.
> 
> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>

This looks like it conflicts with the x86/amd branch as well.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 08/10] iommu/amd: drop the lock while allocating new irq remap table
  2018-02-23 22:27 Sebastian Andrzej Siewior
@ 2018-02-23 22:27 ` Sebastian Andrzej Siewior
  2018-03-15 12:58   ` Joerg Roedel
  0 siblings, 1 reply; 14+ messages in thread
From: Sebastian Andrzej Siewior @ 2018-02-23 22:27 UTC (permalink / raw)
  To: iommu; +Cc: linux-kernel, Joerg Roedel, tglx, Sebastian Andrzej Siewior

The irq_remap_table is allocated while the iommu_table_lock is held with
interrupts disabled. While this works it makes RT scream very loudly.
>From looking at the call sites, all callers are in the early device
initialisation (apic_bsp_setup(), pci_enable_device(),
pci_enable_msi()) so make sense to drop the lock which also enables
interrupts and try to allocate that memory with GFP_KERNEL instead
GFP_ATOMIC.

Since during the allocation the iommu_table_lock is dropped, we need to
recheck if table exists after the lock has been reacquired. I *think*
that it is impossible that the "devid" entry appears in irq_lookup_table
while the lock is dropped since the same device can only be probed once.
It is more likely that another device added an `alias' entry. However I
check for both cases, just to be sure.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/iommu/amd_iommu.c | 65 +++++++++++++++++++++++++++++++++--------------
 1 file changed, 46 insertions(+), 19 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index b763fcbd790d..de6cc41d6cd2 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -3588,6 +3588,30 @@ static void set_dte_irq_entry(u16 devid, struct irq_remap_table *table)
 	amd_iommu_dev_table[devid].data[2] = dte;
 }
 
+static struct irq_remap_table *alloc_irq_table(void)
+{
+	struct irq_remap_table *table;
+
+	table = kzalloc(sizeof(*table), GFP_ATOMIC);
+	if (!table)
+		return NULL;
+
+	table->table = kmem_cache_alloc(amd_iommu_irq_cache, GFP_KERNEL);
+	if (!table->table) {
+		kfree(table);
+		return NULL;
+	}
+	spin_lock_init(&table->lock);
+
+	if (!AMD_IOMMU_GUEST_IR_GA(amd_iommu_guest_ir))
+		memset(table->table, 0,
+		       MAX_IRQS_PER_TABLE * sizeof(u32));
+	else
+		memset(table->table, 0,
+		       (MAX_IRQS_PER_TABLE * (sizeof(u64) * 2)));
+	return table;
+}
+
 static void set_remap_table_entry(struct amd_iommu *iommu, u16 devid,
 				  struct irq_remap_table *table)
 {
@@ -3599,6 +3623,7 @@ static void set_remap_table_entry(struct amd_iommu *iommu, u16 devid,
 static struct irq_remap_table *get_irq_table(u16 devid)
 {
 	struct irq_remap_table *table = NULL;
+	struct irq_remap_table *new_table = NULL;
 	struct amd_iommu *iommu;
 	unsigned long flags;
 	u16 alias;
@@ -3617,42 +3642,44 @@ static struct irq_remap_table *get_irq_table(u16 devid)
 	table = irq_lookup_table[alias];
 	if (table) {
 		set_remap_table_entry(iommu, devid, table);
-		goto out;
+		goto out_wait;
 	}
+	raw_spin_unlock_irqrestore(&iommu_table_lock, flags);
 
 	/* Nothing there yet, allocate new irq remapping table */
-	table = kzalloc(sizeof(*table), GFP_ATOMIC);
-	if (!table)
+	new_table = alloc_irq_table();
+	if (!new_table)
+		return NULL;
+
+	raw_spin_lock_irqsave(&iommu_table_lock, flags);
+
+	table = irq_lookup_table[devid];
+	if (table)
 		goto out_unlock;
 
-	/* Initialize table spin-lock */
-	spin_lock_init(&table->lock);
-
-	table->table = kmem_cache_alloc(amd_iommu_irq_cache, GFP_ATOMIC);
-	if (!table->table) {
-		kfree(table);
-		table = NULL;
-		goto out_unlock;
+	table = irq_lookup_table[alias];
+	if (table) {
+		set_remap_table_entry(iommu, devid, table);
+		goto out_wait;
 	}
 
-	if (!AMD_IOMMU_GUEST_IR_GA(amd_iommu_guest_ir))
-		memset(table->table, 0,
-		       MAX_IRQS_PER_TABLE * sizeof(u32));
-	else
-		memset(table->table, 0,
-		       (MAX_IRQS_PER_TABLE * (sizeof(u64) * 2)));
-
+	table = new_table;
+	new_table = NULL;
 
 	set_remap_table_entry(iommu, devid, table);
 	if (devid != alias)
 		set_remap_table_entry(iommu, alias, table);
 
-out:
+out_wait:
 	iommu_completion_wait(iommu);
 
 out_unlock:
 	raw_spin_unlock_irqrestore(&iommu_table_lock, flags);
 
+	if (new_table) {
+		kmem_cache_free(amd_iommu_irq_cache, new_table->table);
+		kfree(new_table);
+	}
 	return table;
 }
 
-- 
2.16.1

^ permalink raw reply related	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2018-03-29  8:38 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-03-22 15:22 [PATCH 00/10 v3] iommu/amd: lock splitting & GFP_KERNEL allocation Sebastian Andrzej Siewior
2018-03-22 15:22 ` [PATCH 01/10] iommu/amd: take into account that alloc_dev_data() may return NULL Sebastian Andrzej Siewior
2018-03-22 15:22 ` [PATCH 02/10] iommu/amd: turn dev_data_list into a lock less list Sebastian Andrzej Siewior
2018-03-22 15:22 ` [PATCH 03/10] iommu/amd: split domain id out of amd_iommu_devtable_lock Sebastian Andrzej Siewior
2018-03-22 15:22 ` [PATCH 04/10] iommu/amd: split irq_lookup_table out of the amd_iommu_devtable_lock Sebastian Andrzej Siewior
2018-03-22 15:22 ` [PATCH 05/10] iommu/amd: remove the special case from alloc_irq_table() Sebastian Andrzej Siewior
2018-03-22 15:22 ` [PATCH 06/10] iommu/amd: use `table' instead `irt' as variable name in amd_iommu_update_ga() Sebastian Andrzej Siewior
2018-03-22 15:22 ` [PATCH 07/10] iommu/amd: factor out setting the remap table for a devid Sebastian Andrzej Siewior
2018-03-22 15:22 ` [PATCH 08/10] iommu/amd: drop the lock while allocating new irq remap table Sebastian Andrzej Siewior
2018-03-22 15:22 ` [PATCH 09/10] iommu/amd: make amd_iommu_devtable_lock a spin_lock Sebastian Andrzej Siewior
2018-03-22 15:22 ` [PATCH 10/10] iommu/amd: return proper error code in irq_remapping_alloc() Sebastian Andrzej Siewior
2018-03-29  8:38 ` [PATCH 00/10 v3] iommu/amd: lock splitting & GFP_KERNEL allocation Joerg Roedel
  -- strict thread matches above, loose matches on Subject: below --
2018-02-23 22:27 Sebastian Andrzej Siewior
2018-02-23 22:27 ` [PATCH 08/10] iommu/amd: drop the lock while allocating new irq remap table Sebastian Andrzej Siewior
2018-03-15 12:58   ` Joerg Roedel

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).