Commit 591e5bec authored by Marc Zyngier's avatar Marc Zyngier Committed by Thomas Gleixner

irqchip/gicv3-its: Fix mapping of LPIs to collections

The GICv3 ITS architecture allows a given [DevID, EventID] pair to be
translated to a [LPI, Collection] pair, where DevID is the device writing
the MSI, EventID is the payload being written, LPI is the actual
interrupt number, and Collection is roughly equivalent to a target CPU.

Each LPI can be mapped to a separate collection, but the ITS driver
insists on maintaining the collection on a device basis, instead of doing
it on a per interrupt basis.

This is obviously flawed, and this patch fixes it by adding a per interrupt
index that indicates which collection number is in use.
Reported-by: default avatarIan Campbell <ian.campbell@citrix.com>
Signed-off-by: default avatarMarc Zyngier <marc.zyngier@arm.com>
Cc: <linux-arm-kernel@lists.infradead.org>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: stable@vger.kernel.org	# 4.1, 4.0
Link: http://lkml.kernel.org/r/1437126402-11677-1-git-send-email-marc.zyngier@arm.comSigned-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent 75a06189
...@@ -75,6 +75,13 @@ struct its_node { ...@@ -75,6 +75,13 @@ struct its_node {
#define ITS_ITT_ALIGN SZ_256 #define ITS_ITT_ALIGN SZ_256
struct event_lpi_map {
unsigned long *lpi_map;
u16 *col_map;
irq_hw_number_t lpi_base;
int nr_lpis;
};
/* /*
* The ITS view of a device - belongs to an ITS, a collection, owns an * The ITS view of a device - belongs to an ITS, a collection, owns an
* interrupt translation table, and a list of interrupts. * interrupt translation table, and a list of interrupts.
...@@ -82,11 +89,8 @@ struct its_node { ...@@ -82,11 +89,8 @@ struct its_node {
struct its_device { struct its_device {
struct list_head entry; struct list_head entry;
struct its_node *its; struct its_node *its;
struct its_collection *collection; struct event_lpi_map event_map;
void *itt; void *itt;
unsigned long *lpi_map;
irq_hw_number_t lpi_base;
int nr_lpis;
u32 nr_ites; u32 nr_ites;
u32 device_id; u32 device_id;
}; };
...@@ -99,6 +103,14 @@ static struct rdists *gic_rdists; ...@@ -99,6 +103,14 @@ static struct rdists *gic_rdists;
#define gic_data_rdist() (raw_cpu_ptr(gic_rdists->rdist)) #define gic_data_rdist() (raw_cpu_ptr(gic_rdists->rdist))
#define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base) #define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base)
static struct its_collection *dev_event_to_col(struct its_device *its_dev,
u32 event)
{
struct its_node *its = its_dev->its;
return its->collections + its_dev->event_map.col_map[event];
}
/* /*
* ITS command descriptors - parameters to be encoded in a command * ITS command descriptors - parameters to be encoded in a command
* block. * block.
...@@ -134,7 +146,7 @@ struct its_cmd_desc { ...@@ -134,7 +146,7 @@ struct its_cmd_desc {
struct { struct {
struct its_device *dev; struct its_device *dev;
struct its_collection *col; struct its_collection *col;
u32 id; u32 event_id;
} its_movi_cmd; } its_movi_cmd;
struct { struct {
...@@ -241,7 +253,7 @@ static struct its_collection *its_build_mapd_cmd(struct its_cmd_block *cmd, ...@@ -241,7 +253,7 @@ static struct its_collection *its_build_mapd_cmd(struct its_cmd_block *cmd,
its_fixup_cmd(cmd); its_fixup_cmd(cmd);
return desc->its_mapd_cmd.dev->collection; return NULL;
} }
static struct its_collection *its_build_mapc_cmd(struct its_cmd_block *cmd, static struct its_collection *its_build_mapc_cmd(struct its_cmd_block *cmd,
...@@ -260,52 +272,72 @@ static struct its_collection *its_build_mapc_cmd(struct its_cmd_block *cmd, ...@@ -260,52 +272,72 @@ static struct its_collection *its_build_mapc_cmd(struct its_cmd_block *cmd,
static struct its_collection *its_build_mapvi_cmd(struct its_cmd_block *cmd, static struct its_collection *its_build_mapvi_cmd(struct its_cmd_block *cmd,
struct its_cmd_desc *desc) struct its_cmd_desc *desc)
{ {
struct its_collection *col;
col = dev_event_to_col(desc->its_mapvi_cmd.dev,
desc->its_mapvi_cmd.event_id);
its_encode_cmd(cmd, GITS_CMD_MAPVI); its_encode_cmd(cmd, GITS_CMD_MAPVI);
its_encode_devid(cmd, desc->its_mapvi_cmd.dev->device_id); its_encode_devid(cmd, desc->its_mapvi_cmd.dev->device_id);
its_encode_event_id(cmd, desc->its_mapvi_cmd.event_id); its_encode_event_id(cmd, desc->its_mapvi_cmd.event_id);
its_encode_phys_id(cmd, desc->its_mapvi_cmd.phys_id); its_encode_phys_id(cmd, desc->its_mapvi_cmd.phys_id);
its_encode_collection(cmd, desc->its_mapvi_cmd.dev->collection->col_id); its_encode_collection(cmd, col->col_id);
its_fixup_cmd(cmd); its_fixup_cmd(cmd);
return desc->its_mapvi_cmd.dev->collection; return col;
} }
static struct its_collection *its_build_movi_cmd(struct its_cmd_block *cmd, static struct its_collection *its_build_movi_cmd(struct its_cmd_block *cmd,
struct its_cmd_desc *desc) struct its_cmd_desc *desc)
{ {
struct its_collection *col;
col = dev_event_to_col(desc->its_movi_cmd.dev,
desc->its_movi_cmd.event_id);
its_encode_cmd(cmd, GITS_CMD_MOVI); its_encode_cmd(cmd, GITS_CMD_MOVI);
its_encode_devid(cmd, desc->its_movi_cmd.dev->device_id); its_encode_devid(cmd, desc->its_movi_cmd.dev->device_id);
its_encode_event_id(cmd, desc->its_movi_cmd.id); its_encode_event_id(cmd, desc->its_movi_cmd.event_id);
its_encode_collection(cmd, desc->its_movi_cmd.col->col_id); its_encode_collection(cmd, desc->its_movi_cmd.col->col_id);
its_fixup_cmd(cmd); its_fixup_cmd(cmd);
return desc->its_movi_cmd.dev->collection; return col;
} }
static struct its_collection *its_build_discard_cmd(struct its_cmd_block *cmd, static struct its_collection *its_build_discard_cmd(struct its_cmd_block *cmd,
struct its_cmd_desc *desc) struct its_cmd_desc *desc)
{ {
struct its_collection *col;
col = dev_event_to_col(desc->its_discard_cmd.dev,
desc->its_discard_cmd.event_id);
its_encode_cmd(cmd, GITS_CMD_DISCARD); its_encode_cmd(cmd, GITS_CMD_DISCARD);
its_encode_devid(cmd, desc->its_discard_cmd.dev->device_id); its_encode_devid(cmd, desc->its_discard_cmd.dev->device_id);
its_encode_event_id(cmd, desc->its_discard_cmd.event_id); its_encode_event_id(cmd, desc->its_discard_cmd.event_id);
its_fixup_cmd(cmd); its_fixup_cmd(cmd);
return desc->its_discard_cmd.dev->collection; return col;
} }
static struct its_collection *its_build_inv_cmd(struct its_cmd_block *cmd, static struct its_collection *its_build_inv_cmd(struct its_cmd_block *cmd,
struct its_cmd_desc *desc) struct its_cmd_desc *desc)
{ {
struct its_collection *col;
col = dev_event_to_col(desc->its_inv_cmd.dev,
desc->its_inv_cmd.event_id);
its_encode_cmd(cmd, GITS_CMD_INV); its_encode_cmd(cmd, GITS_CMD_INV);
its_encode_devid(cmd, desc->its_inv_cmd.dev->device_id); its_encode_devid(cmd, desc->its_inv_cmd.dev->device_id);
its_encode_event_id(cmd, desc->its_inv_cmd.event_id); its_encode_event_id(cmd, desc->its_inv_cmd.event_id);
its_fixup_cmd(cmd); its_fixup_cmd(cmd);
return desc->its_inv_cmd.dev->collection; return col;
} }
static struct its_collection *its_build_invall_cmd(struct its_cmd_block *cmd, static struct its_collection *its_build_invall_cmd(struct its_cmd_block *cmd,
...@@ -497,7 +529,7 @@ static void its_send_movi(struct its_device *dev, ...@@ -497,7 +529,7 @@ static void its_send_movi(struct its_device *dev,
desc.its_movi_cmd.dev = dev; desc.its_movi_cmd.dev = dev;
desc.its_movi_cmd.col = col; desc.its_movi_cmd.col = col;
desc.its_movi_cmd.id = id; desc.its_movi_cmd.event_id = id;
its_send_single_command(dev->its, its_build_movi_cmd, &desc); its_send_single_command(dev->its, its_build_movi_cmd, &desc);
} }
...@@ -528,7 +560,7 @@ static void its_send_invall(struct its_node *its, struct its_collection *col) ...@@ -528,7 +560,7 @@ static void its_send_invall(struct its_node *its, struct its_collection *col)
static inline u32 its_get_event_id(struct irq_data *d) static inline u32 its_get_event_id(struct irq_data *d)
{ {
struct its_device *its_dev = irq_data_get_irq_chip_data(d); struct its_device *its_dev = irq_data_get_irq_chip_data(d);
return d->hwirq - its_dev->lpi_base; return d->hwirq - its_dev->event_map.lpi_base;
} }
static void lpi_set_config(struct irq_data *d, bool enable) static void lpi_set_config(struct irq_data *d, bool enable)
...@@ -583,7 +615,7 @@ static int its_set_affinity(struct irq_data *d, const struct cpumask *mask_val, ...@@ -583,7 +615,7 @@ static int its_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
target_col = &its_dev->its->collections[cpu]; target_col = &its_dev->its->collections[cpu];
its_send_movi(its_dev, target_col, id); its_send_movi(its_dev, target_col, id);
its_dev->collection = target_col; its_dev->event_map.col_map[id] = cpu;
return IRQ_SET_MASK_OK_DONE; return IRQ_SET_MASK_OK_DONE;
} }
...@@ -713,8 +745,10 @@ static unsigned long *its_lpi_alloc_chunks(int nr_irqs, int *base, int *nr_ids) ...@@ -713,8 +745,10 @@ static unsigned long *its_lpi_alloc_chunks(int nr_irqs, int *base, int *nr_ids)
return bitmap; return bitmap;
} }
static void its_lpi_free(unsigned long *bitmap, int base, int nr_ids) static void its_lpi_free(struct event_lpi_map *map)
{ {
int base = map->lpi_base;
int nr_ids = map->nr_lpis;
int lpi; int lpi;
spin_lock(&lpi_lock); spin_lock(&lpi_lock);
...@@ -731,7 +765,8 @@ static void its_lpi_free(unsigned long *bitmap, int base, int nr_ids) ...@@ -731,7 +765,8 @@ static void its_lpi_free(unsigned long *bitmap, int base, int nr_ids)
spin_unlock(&lpi_lock); spin_unlock(&lpi_lock);
kfree(bitmap); kfree(map->lpi_map);
kfree(map->col_map);
} }
/* /*
...@@ -1099,11 +1134,11 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id, ...@@ -1099,11 +1134,11 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
struct its_device *dev; struct its_device *dev;
unsigned long *lpi_map; unsigned long *lpi_map;
unsigned long flags; unsigned long flags;
u16 *col_map = NULL;
void *itt; void *itt;
int lpi_base; int lpi_base;
int nr_lpis; int nr_lpis;
int nr_ites; int nr_ites;
int cpu;
int sz; int sz;
dev = kzalloc(sizeof(*dev), GFP_KERNEL); dev = kzalloc(sizeof(*dev), GFP_KERNEL);
...@@ -1117,20 +1152,24 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id, ...@@ -1117,20 +1152,24 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
sz = max(sz, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1; sz = max(sz, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1;
itt = kzalloc(sz, GFP_KERNEL); itt = kzalloc(sz, GFP_KERNEL);
lpi_map = its_lpi_alloc_chunks(nvecs, &lpi_base, &nr_lpis); lpi_map = its_lpi_alloc_chunks(nvecs, &lpi_base, &nr_lpis);
if (lpi_map)
col_map = kzalloc(sizeof(*col_map) * nr_lpis, GFP_KERNEL);
if (!dev || !itt || !lpi_map) { if (!dev || !itt || !lpi_map || !col_map) {
kfree(dev); kfree(dev);
kfree(itt); kfree(itt);
kfree(lpi_map); kfree(lpi_map);
kfree(col_map);
return NULL; return NULL;
} }
dev->its = its; dev->its = its;
dev->itt = itt; dev->itt = itt;
dev->nr_ites = nr_ites; dev->nr_ites = nr_ites;
dev->lpi_map = lpi_map; dev->event_map.lpi_map = lpi_map;
dev->lpi_base = lpi_base; dev->event_map.col_map = col_map;
dev->nr_lpis = nr_lpis; dev->event_map.lpi_base = lpi_base;
dev->event_map.nr_lpis = nr_lpis;
dev->device_id = dev_id; dev->device_id = dev_id;
INIT_LIST_HEAD(&dev->entry); INIT_LIST_HEAD(&dev->entry);
...@@ -1138,10 +1177,6 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id, ...@@ -1138,10 +1177,6 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
list_add(&dev->entry, &its->its_device_list); list_add(&dev->entry, &its->its_device_list);
raw_spin_unlock_irqrestore(&its->lock, flags); raw_spin_unlock_irqrestore(&its->lock, flags);
/* Bind the device to the first possible CPU */
cpu = cpumask_first(cpu_online_mask);
dev->collection = &its->collections[cpu];
/* Map device to its ITT */ /* Map device to its ITT */
its_send_mapd(dev, 1); its_send_mapd(dev, 1);
...@@ -1163,12 +1198,13 @@ static int its_alloc_device_irq(struct its_device *dev, irq_hw_number_t *hwirq) ...@@ -1163,12 +1198,13 @@ static int its_alloc_device_irq(struct its_device *dev, irq_hw_number_t *hwirq)
{ {
int idx; int idx;
idx = find_first_zero_bit(dev->lpi_map, dev->nr_lpis); idx = find_first_zero_bit(dev->event_map.lpi_map,
if (idx == dev->nr_lpis) dev->event_map.nr_lpis);
if (idx == dev->event_map.nr_lpis)
return -ENOSPC; return -ENOSPC;
*hwirq = dev->lpi_base + idx; *hwirq = dev->event_map.lpi_base + idx;
set_bit(idx, dev->lpi_map); set_bit(idx, dev->event_map.lpi_map);
return 0; return 0;
} }
...@@ -1288,7 +1324,8 @@ static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, ...@@ -1288,7 +1324,8 @@ static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
irq_domain_set_hwirq_and_chip(domain, virq + i, irq_domain_set_hwirq_and_chip(domain, virq + i,
hwirq, &its_irq_chip, its_dev); hwirq, &its_irq_chip, its_dev);
dev_dbg(info->scratchpad[1].ptr, "ID:%d pID:%d vID:%d\n", dev_dbg(info->scratchpad[1].ptr, "ID:%d pID:%d vID:%d\n",
(int)(hwirq - its_dev->lpi_base), (int)hwirq, virq + i); (int)(hwirq - its_dev->event_map.lpi_base),
(int)hwirq, virq + i);
} }
return 0; return 0;
...@@ -1300,6 +1337,9 @@ static void its_irq_domain_activate(struct irq_domain *domain, ...@@ -1300,6 +1337,9 @@ static void its_irq_domain_activate(struct irq_domain *domain,
struct its_device *its_dev = irq_data_get_irq_chip_data(d); struct its_device *its_dev = irq_data_get_irq_chip_data(d);
u32 event = its_get_event_id(d); u32 event = its_get_event_id(d);
/* Bind the LPI to the first possible CPU */
its_dev->event_map.col_map[event] = cpumask_first(cpu_online_mask);
/* Map the GIC IRQ and event to the device */ /* Map the GIC IRQ and event to the device */
its_send_mapvi(its_dev, d->hwirq, event); its_send_mapvi(its_dev, d->hwirq, event);
} }
...@@ -1327,17 +1367,16 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq, ...@@ -1327,17 +1367,16 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq,
u32 event = its_get_event_id(data); u32 event = its_get_event_id(data);
/* Mark interrupt index as unused */ /* Mark interrupt index as unused */
clear_bit(event, its_dev->lpi_map); clear_bit(event, its_dev->event_map.lpi_map);
/* Nuke the entry in the domain */ /* Nuke the entry in the domain */
irq_domain_reset_irq_data(data); irq_domain_reset_irq_data(data);
} }
/* If all interrupts have been freed, start mopping the floor */ /* If all interrupts have been freed, start mopping the floor */
if (bitmap_empty(its_dev->lpi_map, its_dev->nr_lpis)) { if (bitmap_empty(its_dev->event_map.lpi_map,
its_lpi_free(its_dev->lpi_map, its_dev->event_map.nr_lpis)) {
its_dev->lpi_base, its_lpi_free(&its_dev->event_map);
its_dev->nr_lpis);
/* Unmap device/itt */ /* Unmap device/itt */
its_send_mapd(its_dev, 0); its_send_mapd(its_dev, 0);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment