From: wanghaibin wanghaibin.wang@huawei.com
virt inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8UROS CVE: NA
--------------------------------
Alloc/Free device id from pools for virtual devices and plug the helpers into its_msi_prepare()/its_free_device().
Signed-off-by: wanghaibin wanghaibin.wang@huawei.com Signed-off-by: Zenghui Yu yuzenghui@huawei.com Signed-off-by: Kunkun Jiang jiangkunkun@huawei.com Signed-off-by: Dongxu Sun sundongxu3@huawei.com --- drivers/irqchip/irq-gic-v3-its.c | 84 ++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+)
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index cd1f347a92b0..bc2ae643aa34 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -279,6 +279,10 @@ struct its_device { u32 nr_ites; u32 device_id; bool shared; + + /* For virtual devices which needed the devid managed */ + bool is_vdev; + struct rsv_devid_pool *devid_pool; };
static struct { @@ -306,6 +310,58 @@ static DEFINE_RAW_SPINLOCK(vmovp_lock);
static DEFINE_IDA(its_vpeid_ida);
+static void free_devid_to_rsv_pools(struct its_device *its_dev) +{ + struct rsv_devid_pool *pool = its_dev->devid_pool; + u32 id, size; + + WARN_ON(!pool); + + id = its_dev->device_id - pool->start; + size = pool->end - pool->start; + WARN_ON(id >= size); + + raw_spin_lock(&pool->devid_bm_lock); + clear_bit(id, pool->devid_bm); + raw_spin_unlock(&pool->devid_bm_lock); + + pr_debug("ITS: free devid (%u) to rsv_devid_pools\n", its_dev->device_id); +} + +static int alloc_devid_from_rsv_pools(struct rsv_devid_pool **devid_pool, + u32 *dev_id) +{ + struct rsv_devid_pool *pool; + int err = -ENOSPC; + + raw_spin_lock(&rsv_devid_pools_lock); + list_for_each_entry(pool, &rsv_devid_pools, entry) { + u32 size, id; + + size = pool->end - pool->start; + + raw_spin_lock(&pool->devid_bm_lock); + id = find_first_zero_bit(pool->devid_bm, size); + if (id >= size) { + /* No usable device id in this pool, try next. */ + raw_spin_unlock(&pool->devid_bm_lock); + continue; + } + + *dev_id = pool->start + id; + set_bit(id, pool->devid_bm); + raw_spin_unlock(&pool->devid_bm_lock); + + *devid_pool = pool; + err = 0; + break; + } + raw_spin_unlock(&rsv_devid_pools_lock); + + pr_debug("ITS: alloc devid (%u) from rsv_devid_pools\n", *dev_id); + return err; +} + #define gic_data_rdist() (raw_cpu_ptr(gic_rdists->rdist)) #define gic_data_rdist_cpu(cpu) (per_cpu_ptr(gic_rdists->rdist, cpu)) #define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base) @@ -3615,6 +3671,12 @@ static void its_free_device(struct its_device *its_dev) raw_spin_unlock_irqrestore(&its_dev->its->lock, flags); kfree(its_dev->event_map.col_map); kfree(its_dev->itt); + + if (its_dev->is_vdev) { + WARN_ON(!rsv_devid_pool_cap); + free_devid_to_rsv_pools(its_dev); + } + kfree(its_dev); }
@@ -3642,6 +3704,8 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev, struct msi_domain_info *msi_info; u32 dev_id; int err = 0; + int use_devid_pool = false; + struct rsv_devid_pool *pool = NULL;
/* * We ignore "dev" entirely, and rely on the dev_id that has @@ -3651,6 +3715,18 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev, */ dev_id = info->scratchpad[0].ul;
+ if (rsv_devid_pool_cap && !dev->of_node && !dev->fwnode && + info->scratchpad[0].ul == -1) + use_devid_pool = true; + + if (use_devid_pool) { + err = alloc_devid_from_rsv_pools(&pool, &dev_id); + if (err) { + pr_warn("ITS: No remaining device id\n"); + return err; + } + } + msi_info = msi_get_domain_info(domain); its = msi_info->data;
@@ -3667,6 +3743,9 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev, mutex_lock(&its->dev_alloc_lock); its_dev = its_find_device(its, dev_id); if (its_dev) { + /* Impossible ...*/ + WARN_ON_ONCE(use_devid_pool); + /* * We already have seen this ID, probably through * another alias (PCI bridge of some sort). No need to @@ -3687,6 +3766,11 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev, its_dev->shared = true;
pr_debug("ITT %d entries, %d bits\n", nvec, ilog2(nvec)); + + if (use_devid_pool) { + its_dev->is_vdev = true; + its_dev->devid_pool = pool; + } out: mutex_unlock(&its->dev_alloc_lock); info->scratchpad[0].ptr = its_dev;