Skip to content

Commit a80713f

Browse files
committed
platform-msi: Simplify platform device MSI code
The allocation code is overly complex. It tries to have the MSI index space packed, which is not working when an interrupt is freed. There is no requirement for this. The only requirement is that the MSI index is unique. Move the MSI descriptor allocation into msi_domain_populate_irqs() and use the Linux interrupt number as MSI index which fulfils the unique requirement. This requires to lock the MSI descriptors which makes the lock order reverse to the regular MSI alloc/free functions vs. the domain mutex. Assign a seperate lockdep class for these MSI device domains. Signed-off-by: Thomas Gleixner <[email protected]> Tested-by: Nishanth Menon <[email protected]> Reviewed-by: Jason Gunthorpe <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 653b50c commit a80713f

File tree

2 files changed

+39
-94
lines changed

2 files changed

+39
-94
lines changed

drivers/base/platform-msi.c

+18-70
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,8 @@ void *platform_msi_get_host_data(struct irq_domain *domain)
246246
return data->host_data;
247247
}
248248

249+
static struct lock_class_key platform_device_msi_lock_class;
250+
249251
/**
250252
* __platform_msi_create_device_domain - Create a platform-msi device domain
251253
*
@@ -278,6 +280,13 @@ __platform_msi_create_device_domain(struct device *dev,
278280
if (err)
279281
return NULL;
280282

283+
/*
284+
* Use a separate lock class for the MSI descriptor mutex on
285+
* platform MSI device domains because the descriptor mutex nests
286+
* into the domain mutex. See alloc/free below.
287+
*/
288+
lockdep_set_class(&dev->msi.data->mutex, &platform_device_msi_lock_class);
289+
281290
data = dev->msi.data->platform_data;
282291
data->host_data = host_data;
283292
domain = irq_domain_create_hierarchy(dev->msi.domain, 0,
@@ -300,75 +309,23 @@ __platform_msi_create_device_domain(struct device *dev,
300309
return NULL;
301310
}
302311

303-
static void platform_msi_free_descs(struct device *dev, int base, int nvec)
304-
{
305-
struct msi_desc *desc, *tmp;
306-
307-
list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) {
308-
if (desc->msi_index >= base &&
309-
desc->msi_index < (base + nvec)) {
310-
list_del(&desc->list);
311-
free_msi_entry(desc);
312-
}
313-
}
314-
}
315-
316-
static int platform_msi_alloc_descs_with_irq(struct device *dev, int virq,
317-
int nvec)
318-
{
319-
struct msi_desc *desc;
320-
int i, base = 0;
321-
322-
if (!list_empty(dev_to_msi_list(dev))) {
323-
desc = list_last_entry(dev_to_msi_list(dev),
324-
struct msi_desc, list);
325-
base = desc->msi_index + 1;
326-
}
327-
328-
for (i = 0; i < nvec; i++) {
329-
desc = alloc_msi_entry(dev, 1, NULL);
330-
if (!desc)
331-
break;
332-
333-
desc->msi_index = base + i;
334-
desc->irq = virq + i;
335-
336-
list_add_tail(&desc->list, dev_to_msi_list(dev));
337-
}
338-
339-
if (i != nvec) {
340-
/* Clean up the mess */
341-
platform_msi_free_descs(dev, base, nvec);
342-
return -ENOMEM;
343-
}
344-
345-
return 0;
346-
}
347-
348312
/**
349313
* platform_msi_device_domain_free - Free interrupts associated with a platform-msi
350314
* device domain
351315
*
352316
* @domain: The platform-msi device domain
353317
* @virq: The base irq from which to perform the free operation
354-
* @nvec: How many interrupts to free from @virq
318+
* @nr_irqs: How many interrupts to free from @virq
355319
*/
356320
void platform_msi_device_domain_free(struct irq_domain *domain, unsigned int virq,
357-
unsigned int nvec)
321+
unsigned int nr_irqs)
358322
{
359323
struct platform_msi_priv_data *data = domain->host_data;
360-
struct msi_desc *desc, *tmp;
361324

362-
for_each_msi_entry_safe(desc, tmp, data->dev) {
363-
if (WARN_ON(!desc->irq || desc->nvec_used != 1))
364-
return;
365-
if (!(desc->irq >= virq && desc->irq < (virq + nvec)))
366-
continue;
367-
368-
irq_domain_free_irqs_common(domain, desc->irq, 1);
369-
list_del(&desc->list);
370-
free_msi_entry(desc);
371-
}
325+
msi_lock_descs(data->dev);
326+
irq_domain_free_irqs_common(domain, virq, nr_irqs);
327+
msi_free_msi_descs_range(data->dev, MSI_DESC_ALL, virq, virq + nr_irqs - 1);
328+
msi_unlock_descs(data->dev);
372329
}
373330

374331
/**
@@ -377,7 +334,7 @@ void platform_msi_device_domain_free(struct irq_domain *domain, unsigned int vir
377334
*
378335
* @domain: The platform-msi device domain
379336
* @virq: The base irq from which to perform the allocate operation
380-
* @nr_irqs: How many interrupts to free from @virq
337+
* @nr_irqs: How many interrupts to allocate from @virq
381338
*
382339
* Return 0 on success, or an error code on failure. Must be called
383340
* with irq_domain_mutex held (which can only be done as part of a
@@ -387,16 +344,7 @@ int platform_msi_device_domain_alloc(struct irq_domain *domain, unsigned int vir
387344
unsigned int nr_irqs)
388345
{
389346
struct platform_msi_priv_data *data = domain->host_data;
390-
int err;
391-
392-
err = platform_msi_alloc_descs_with_irq(data->dev, virq, nr_irqs);
393-
if (err)
394-
return err;
395-
396-
err = msi_domain_populate_irqs(domain->parent, data->dev,
397-
virq, nr_irqs, &data->arg);
398-
if (err)
399-
platform_msi_device_domain_free(domain, virq, nr_irqs);
347+
struct device *dev = data->dev;
400348

401-
return err;
349+
return msi_domain_populate_irqs(domain->parent, dev, virq, nr_irqs, &data->arg);
402350
}

kernel/irq/msi.c

+21-24
Original file line numberDiff line numberDiff line change
@@ -731,43 +731,40 @@ int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev,
731731
}
732732

733733
int msi_domain_populate_irqs(struct irq_domain *domain, struct device *dev,
734-
int virq, int nvec, msi_alloc_info_t *arg)
734+
int virq_base, int nvec, msi_alloc_info_t *arg)
735735
{
736736
struct msi_domain_info *info = domain->host_data;
737737
struct msi_domain_ops *ops = info->ops;
738738
struct msi_desc *desc;
739-
int ret = 0;
739+
int ret, virq;
740740

741-
for_each_msi_entry(desc, dev) {
742-
/* Don't even try the multi-MSI brain damage. */
743-
if (WARN_ON(!desc->irq || desc->nvec_used != 1)) {
744-
ret = -EINVAL;
745-
break;
741+
msi_lock_descs(dev);
742+
for (virq = virq_base; virq < virq_base + nvec; virq++) {
743+
desc = alloc_msi_entry(dev, 1, NULL);
744+
if (!desc) {
745+
ret = -ENOMEM;
746+
goto fail;
746747
}
747748

748-
if (!(desc->irq >= virq && desc->irq < (virq + nvec)))
749-
continue;
749+
desc->msi_index = virq;
750+
desc->irq = virq;
751+
list_add_tail(&desc->list, &dev->msi.data->list);
750752

751753
ops->set_desc(arg, desc);
752-
/* Assumes the domain mutex is held! */
753-
ret = irq_domain_alloc_irqs_hierarchy(domain, desc->irq, 1,
754-
arg);
754+
ret = irq_domain_alloc_irqs_hierarchy(domain, virq, 1, arg);
755755
if (ret)
756-
break;
757-
758-
irq_set_msi_desc_off(desc->irq, 0, desc);
759-
}
760-
761-
if (ret) {
762-
/* Mop up the damage */
763-
for_each_msi_entry(desc, dev) {
764-
if (!(desc->irq >= virq && desc->irq < (virq + nvec)))
765-
continue;
756+
goto fail;
766757

767-
irq_domain_free_irqs_common(domain, desc->irq, 1);
768-
}
758+
irq_set_msi_desc(virq, desc);
769759
}
760+
msi_unlock_descs(dev);
761+
return 0;
770762

763+
fail:
764+
for (--virq; virq >= virq_base; virq--)
765+
irq_domain_free_irqs_common(domain, virq, 1);
766+
msi_free_msi_descs_range(dev, MSI_DESC_ALL, virq_base, virq_base + nvec - 1);
767+
msi_unlock_descs(dev);
771768
return ret;
772769
}
773770

0 commit comments

Comments
 (0)