msi: dynamic dev_msi

commit 86d8cd35c5641a6807506674f72030c4d3ceaa8e Intel-BKC.

Enable a single device to hold both device msi and msi/x interrupts.

Signed-off-by: Megha Dey <megha.dey@intel.com>
Signed-off-by: Chen Zhuo <sagazchen@tencent.com>
Signed-off-by: Xinghui Li <korantli@tencent.com>
This commit is contained in:
Zhuo Chen 2022-06-27 17:34:00 +08:00 committed by Jianping Liu
parent f2a3fa6707
commit 4daace26fb
6 changed files with 145 additions and 30 deletions

View File

@ -1724,7 +1724,9 @@ void device_initialize(struct device *dev)
#ifdef CONFIG_GENERIC_MSI_IRQ
raw_spin_lock_init(&dev->msi_lock);
INIT_LIST_HEAD(&dev->msi_list);
INIT_LIST_HEAD(&dev->dev_msi_list);
dev->msi_last_list = &dev->msi_list;
dev->dev_msi_last_list = &dev->dev_msi_list;
#endif
INIT_LIST_HEAD(&dev->links.consumers);
INIT_LIST_HEAD(&dev->links.suppliers);

View File

@ -424,7 +424,7 @@ int platform_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
static void device_msi_free_msi_entries(struct device *dev)
{
struct list_head *msi_list = dev_to_msi_list(dev);
struct list_head *msi_list = dev_to_dev_msi_list(dev);
struct msi_desc *entry, *tmp;
list_for_each_entry_safe(entry, tmp, msi_list, list) {
@ -445,6 +445,25 @@ static void device_msi_free_irqs(struct irq_domain *domain, struct device *dev)
device_msi_free_msi_entries(dev);
}
static void device_msi_free_irq(struct irq_domain *domain, struct device *dev, unsigned int irq)
{
struct msi_desc *entry, *tmp;
struct msi_domain_info *info = domain->host_data;
struct msi_domain_ops *ops = info->ops;
if (ops->msi_free_irq)
ops->msi_free_irq(domain, dev, irq);
list_for_each_entry_safe(entry, tmp, dev_to_dev_msi_list(dev), list) {
if (entry->irq == irq) {
list_del(&entry->list);
free_msi_entry(entry);
}
}
__msi_domain_free_irq(domain, dev, irq);
}
/**
* device_msi_alloc_irqs - Allocate MSI interrupts for a device
* @dev: Pointer to the device
@ -457,12 +476,14 @@ static int device_msi_alloc_irqs(struct irq_domain *domain, struct device *dev,
{
int i, ret = -ENOMEM;
dev->dev_msi_last_list = dev->dev_msi_list.prev;
for (i = 0; i < nvec; i++) {
struct msi_desc *entry = alloc_msi_entry(dev, 1, NULL);
if (!entry)
goto fail;
list_add_tail(&entry->list, dev_to_msi_list(dev));
list_add_tail(&entry->list, dev_to_dev_msi_list(dev));
}
ret = __msi_domain_alloc_irqs(domain, dev, nvec);
@ -473,12 +494,36 @@ fail:
return ret;
}
int device_msi_add_irq(struct irq_domain *domain, struct device *dev)
{
struct msi_desc *entry;
dev->dev_msi_last_list = dev->dev_msi_list.prev;
entry = alloc_msi_entry(dev, 1, NULL);
if (!entry)
goto fail;
list_add_tail(&entry->list, dev_to_dev_msi_list(dev));
entry = list_last_entry(dev_to_dev_msi_list(dev), struct msi_desc, list);
if (!__msi_domain_alloc_irqs(domain, dev, 1))
return entry->device_msi.hwirq;
list_del(&entry->list);
fail:
free_msi_entry(entry);
return -ENOMEM;
}
EXPORT_SYMBOL_GPL(device_msi_add_irq);
static void device_msi_update_dom_ops(struct msi_domain_info *info)
{
if (!info->ops->domain_alloc_irqs)
info->ops->domain_alloc_irqs = device_msi_alloc_irqs;
if (!info->ops->domain_free_irqs)
info->ops->domain_free_irqs = device_msi_free_irqs;
if (!info->ops->domain_free_irq)
info->ops->domain_free_irq = device_msi_free_irq;
if (!info->ops->msi_prepare)
info->ops->msi_prepare = arch_msi_prepare;
}

View File

@ -94,7 +94,7 @@ static void ims_array_free_msi_store(struct irq_domain *domain,
struct ims_array_data *ims = info->data;
struct msi_desc *entry;
for_each_msi_entry(entry, dev) {
for_each_dev_msi_entry(entry, dev) {
if (entry->device_msi.priv_iomem) {
clear_bit(entry->device_msi.hwirq, ims->map);
ims_array_reset_slot(entry->device_msi.priv_iomem);
@ -104,6 +104,23 @@ static void ims_array_free_msi_store(struct irq_domain *domain,
}
}
static void ims_array_free_msi_irq(struct irq_domain *domain,
struct device *dev, unsigned int irq)
{
struct msi_domain_info *info = domain->host_data;
struct ims_array_data *ims = info->data;
struct msi_desc *entry;
for_each_dev_msi_entry(entry, dev) {
if (entry->irq == irq && entry->device_msi.priv_iomem) {
clear_bit(entry->device_msi.hwirq, ims->map);
ims_array_reset_slot(entry->device_msi.priv_iomem);
entry->device_msi.priv_iomem = NULL;
entry->device_msi.hwirq = 0;
}
}
}
static int ims_array_alloc_msi_store(struct irq_domain *domain,
struct device *dev, int nvec)
{
@ -111,7 +128,7 @@ static int ims_array_alloc_msi_store(struct irq_domain *domain,
struct ims_array_data *ims = info->data;
struct msi_desc *entry;
for_each_msi_entry(entry, dev) {
for_each_new_dev_msi_entry(entry, dev) {
unsigned int idx;
idx = find_first_zero_bit(ims->map, ims->info.max_slots);
@ -144,6 +161,7 @@ static const struct ims_array_domain_template ims_array_domain_template = {
.ops = {
.msi_alloc_store = ims_array_alloc_msi_store,
.msi_free_store = ims_array_free_msi_store,
.msi_free_irq = ims_array_free_msi_irq,
.set_desc = ims_set_desc,
},
.info = {

View File

@ -1272,6 +1272,8 @@ struct device {
struct list_head *msi_last_list;
raw_spinlock_t msi_lock;
struct list_head msi_list;
struct list_head dev_msi_list;
struct list_head *dev_msi_last_list;
#endif
const struct dma_map_ops *dma_ops;

View File

@ -101,7 +101,6 @@ struct ti_sci_inta_msi_desc {
* @hwirq: The hardware irq number in the device domain
*/
struct device_msi_desc {
void *priv;
void __iomem *priv_iomem;
u16 hwirq;
};
@ -185,31 +184,45 @@ struct msi_desc {
/* Helpers to hide struct msi_desc implementation details */
#define msi_desc_to_dev(desc) ((desc)->dev)
#define dev_to_msi_list(dev) (&(dev)->msi_list)
#define dev_to_dev_msi_list(dev) (&(dev)->dev_msi_list)
#define first_msi_entry(dev) \
list_first_entry(dev_to_msi_list((dev)), struct msi_desc, list)
#define for_each_msi_entry(desc, dev) \
list_for_each_entry((desc), dev_to_msi_list((dev)), list)
#define __for_each_msi_entry(desc, msi_list) \
list_for_each_entry((desc), (msi_list), list)
#define for_each_msi_entry(desc, dev) \
__for_each_msi_entry((desc), dev_to_msi_list((dev)))
#define for_each_msi_entry_safe(desc, tmp, dev) \
list_for_each_entry_safe((desc), (tmp), dev_to_msi_list((dev)), list)
#define for_each_msi_vector(desc, __irq, dev) \
for_each_msi_entry((desc), (dev)) \
if ((desc)->irq) \
for (__irq = (desc)->irq; \
__irq < ((desc)->irq + (desc)->nvec_used); \
#define __for_each_msi_vector(desc, __irq, msi_list) \
__for_each_msi_entry((desc), (msi_list)) \
if ((desc)->irq) \
for (__irq = (desc)->irq; \
__irq < ((desc)->irq + (desc)->nvec_used); \
__irq++)
#define for_each_msi_vector(desc, __irq, dev) \
__for_each_msi_vector(desc, __irq, dev_to_msi_list((dev)))
/* Iterate through all the msi_descs starting from a given desc */
#define for_each_new_msi_entry(desc, dev) \
(desc) = list_entry((dev)->msi_last_list->next, struct msi_desc, list); \
list_for_each_entry_from((desc), dev_to_msi_list((dev)), list)
#define for_each_new_msi_vector(desc, __irq, dev) \
for_each_new_msi_entry((desc), (dev)) \
if ((desc)->irq) \
for ((__irq) = (desc)->irq; \
(__irq) < ((desc)->irq + (desc)->nvec_used); \
#define __for_each_new_msi_entry(desc, msi_last_list, msi_list) \
(desc) = list_entry((msi_last_list)->next, struct msi_desc, list); \
list_for_each_entry_from((desc), (msi_list), list)
#define for_each_new_msi_entry(desc, dev) \
__for_each_new_msi_entry((desc), (dev)->msi_last_list, dev_to_msi_list((dev)))
#define __for_each_new_msi_vector(desc, __irq, msi_last_list, msi_list) \
__for_each_new_msi_entry((desc), (msi_last_list), (msi_list)) \
if ((desc)->irq) \
for ((__irq) = (desc)->irq; \
(__irq) < ((desc)->irq + (desc)->nvec_used); \
(__irq)++)
#define for_each_new_msi_entry_safe(desc, tmp, dev) \
(desc) = list_entry((dev)->msi_last_list->next, struct msi_desc, list); \
list_for_each_entry_safe_from((desc), (tmp), dev_to_msi_list((dev)), list)
#define for_each_new_msi_vector(desc, __irq, dev) \
__for_each_new_msi_vector((desc), (__irq), (dev)->msi_last_list, dev_to_msi_list((dev)))
#define for_each_new_msi_entry_safe(desc, tmp, dev) \
(desc) = list_entry((dev)->msi_last_list->next, struct msi_desc, list); \
list_for_each_entry_safe_from((desc), (tmp), dev_to_msi_list((dev)), list)
#define for_each_dev_msi_entry(desc, dev) \
list_for_each_entry((desc), dev_to_dev_msi_list((dev)), list)
#define for_each_new_dev_msi_entry(desc, dev) \
__for_each_new_msi_entry((desc), (dev)->dev_msi_last_list, dev_to_dev_msi_list((dev)))
#ifdef CONFIG_IRQ_MSI_IOMMU
static inline const void *msi_desc_get_iommu_cookie(struct msi_desc *desc)
@ -397,10 +410,16 @@ struct msi_domain_ops {
struct device *dev, int nvec);
void (*domain_free_irqs)(struct irq_domain *domain,
struct device *dev);
void (*domain_free_irq)(struct irq_domain *domain,
struct device *dev,
unsigned int irq);
int (*msi_alloc_store)(struct irq_domain *domain,
struct device *dev, int nvec);
void (*msi_free_store)(struct irq_domain *domain,
struct device *dev);
void (*msi_free_irq)(struct irq_domain *domain,
struct device *dev, unsigned int irq);
};
/**
@ -464,7 +483,9 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
int nvec);
void __msi_domain_free_irqs(struct irq_domain *domain, struct device *dev);
void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev);
int device_msi_add_irq(struct irq_domain *domain, struct device *dev);
void msi_domain_free_irq(struct irq_domain *domain, struct device *dev, unsigned int irq);
void __msi_domain_free_irq(struct irq_domain *domain, struct device *dev, unsigned int irq);
struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain);
struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode,

View File

@ -424,6 +424,7 @@ static struct msi_domain_ops msi_domain_ops_default = {
.set_desc = msi_domain_ops_set_desc,
.domain_alloc_irqs = __msi_domain_alloc_irqs,
.domain_free_irqs = __msi_domain_free_irqs,
.domain_free_irq = __msi_domain_free_irq,
};
static void msi_domain_update_dom_ops(struct msi_domain_info *info)
@ -439,6 +440,8 @@ static void msi_domain_update_dom_ops(struct msi_domain_info *info)
ops->domain_alloc_irqs = msi_domain_ops_default.domain_alloc_irqs;
if (ops->domain_free_irqs == NULL)
ops->domain_free_irqs = msi_domain_ops_default.domain_free_irqs;
if (ops->domain_free_irq == NULL)
ops->domain_free_irq = msi_domain_ops_default.domain_free_irq;
if (!(info->flags & MSI_FLAG_USE_DEF_DOM_OPS))
return;
@ -593,6 +596,8 @@ int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
msi_alloc_info_t arg = { };
int i, ret, virq;
bool can_reserve;
struct list_head *msi_last_list;
struct list_head *msi_list;
ret = msi_domain_prepare_irqs(domain, dev, nvec, &arg);
if (ret)
@ -604,7 +609,15 @@ int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
return ret;
}
for_each_new_msi_entry(desc, dev) {
if (domain->bus_token == DOMAIN_BUS_DEVICE_MSI) {
msi_last_list = dev->dev_msi_last_list;
msi_list = dev_to_dev_msi_list(dev);
} else {
msi_last_list = dev->msi_last_list;
msi_list = dev_to_msi_list(dev);
}
__for_each_new_msi_entry(desc, msi_last_list, msi_list) {
ops->set_desc(&arg, desc);
virq = __irq_domain_alloc_irqs(domain, -1, desc->nvec_used,
@ -638,7 +651,7 @@ int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
if (!(info->flags & MSI_FLAG_ACTIVATE_EARLY))
goto skip_activate;
for_each_new_msi_vector(desc, i, dev) {
__for_each_new_msi_vector(desc, i, msi_last_list, msi_list) {
if (desc->irq == i) {
virq = desc->irq;
dev_dbg(dev, "irq [%d-%d] for MSI\n",
@ -662,7 +675,7 @@ skip_activate:
* so request_irq() will assign the final vector.
*/
if (can_reserve) {
for_each_new_msi_vector(desc, i, dev) {
__for_each_new_msi_vector(desc, i, msi_last_list, msi_list) {
irq_data = irq_domain_get_irq_data(domain, i);
irqd_clr_activated(irq_data);
}
@ -700,14 +713,18 @@ void __msi_domain_free_irqs(struct irq_domain *domain, struct device *dev)
struct msi_domain_ops *ops = info->ops;
struct msi_desc *desc;
int i;
struct list_head *msi_list;
for_each_msi_vector(desc, i, dev) {
msi_list = (domain->bus_token == DOMAIN_BUS_DEVICE_MSI)
? dev_to_dev_msi_list(dev) : dev_to_msi_list(dev);
__for_each_msi_vector(desc, i, msi_list) {
irq_data = irq_domain_get_irq_data(domain, i);
if (irqd_is_activated(irq_data))
irq_domain_deactivate_irq(irq_data);
}
for_each_msi_entry(desc, dev) {
__for_each_msi_entry(desc, msi_list) {
/*
* We might have failed to allocate an MSI early
* enough that there is no IRQ associated to this
@ -736,6 +753,7 @@ void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev)
return ops->domain_free_irqs(domain, dev);
}
EXPORT_SYMBOL_GPL(msi_domain_free_irqs);
/**
* msi_domain_free_irq - Free interrupt from a MSI interrupt @domain associated to @dev
@ -743,7 +761,7 @@ void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev)
* @dev: Pointer to device struct for which the interrupt needs to be freed
* @irq: Interrupt to be freed
*/
void msi_domain_free_irq(struct irq_domain *domain, struct device *dev, unsigned int irq)
void __msi_domain_free_irq(struct irq_domain *domain, struct device *dev, unsigned int irq)
{
struct msi_desc *desc = irq_get_msi_desc(irq);
struct irq_data *irq_data;
@ -758,6 +776,15 @@ void msi_domain_free_irq(struct irq_domain *domain, struct device *dev, unsigned
}
}
void msi_domain_free_irq(struct irq_domain *domain, struct device *dev, unsigned int irq)
{
struct msi_domain_info *info = domain->host_data;
struct msi_domain_ops *ops = info->ops;
return ops->domain_free_irq(domain, dev, irq);
}
EXPORT_SYMBOL_GPL(msi_domain_free_irq);
/**
* msi_get_domain_info - Get the MSI interrupt domain info for @domain
* @domain: The interrupt domain to retrieve data from
@ -782,7 +809,7 @@ static struct msi_desc *get_dev_msi_entry(struct device *dev, unsigned int nr)
struct msi_desc *entry;
int i = 0;
for_each_msi_entry(entry, dev) {
for_each_dev_msi_entry(entry, dev) {
if (i == nr)
return entry;
i++;