/* * summary bit vector * FLOATING - summary bit per function * DIRECTED - summary bit per cpu (only used in fallback path)
*/ staticstruct airq_iv *zpci_sbv;
/* * interrupt bit vectors * FLOATING - interrupt bit vector per function * DIRECTED - interrupt bit vector per cpu
*/ staticstruct airq_iv **zpci_ibv;
staticvoid zpci_handle_cpu_local_irq(bool rescan)
{ struct airq_iv *dibv = zpci_ibv[smp_processor_id()]; union zpci_sic_iib iib = {{0}}; unsignedlong bit; int irqs_on = 0;
for (bit = 0;;) { /* Scan the directed IRQ bit vector */
bit = airq_iv_scan(dibv, bit, airq_iv_end(dibv)); if (bit == -1UL) { if (!rescan || irqs_on++) /* End of second scan with interrupts on. */ break; /* First scan complete, re-enable interrupts. */ if (zpci_set_irq_ctrl(SIC_IRQ_MODE_D_SINGLE, PCI_ISC, &iib)) break;
bit = 0; continue;
}
inc_irq_stat(IRQIO_MSI);
generic_handle_irq(airq_iv_get_data(dibv, bit));
}
}
do {
zpci_handle_cpu_local_irq(false);
} while (atomic_dec_return(scheduled));
}
staticvoid zpci_handle_fallback_irq(void)
{ struct cpu_irq_data *cpu_data; union zpci_sic_iib iib = {{0}}; unsignedlong cpu; int irqs_on = 0;
for (cpu = 0;;) {
cpu = airq_iv_scan(zpci_sbv, cpu, airq_iv_end(zpci_sbv)); if (cpu == -1UL) { if (irqs_on++) /* End of second scan with interrupts on. */ break; /* First scan complete, re-enable interrupts. */ if (zpci_set_irq_ctrl(SIC_IRQ_MODE_SINGLE, PCI_ISC, &iib)) break;
cpu = 0; continue;
}
cpu_data = &per_cpu(irq_data, cpu); if (atomic_inc_return(&cpu_data->scheduled) > 1) continue;
inc_irq_stat(IRQIO_PCF); for (si = 0;;) { /* Scan adapter summary indicator bit vector */
si = airq_iv_scan(zpci_sbv, si, airq_iv_end(zpci_sbv)); if (si == -1UL) { if (irqs_on++) /* End of second scan with interrupts on. */ break; /* First scan complete, re-enable interrupts. */ if (zpci_set_irq_ctrl(SIC_IRQ_MODE_SINGLE, PCI_ISC, &iib)) break;
si = 0; continue;
}
/* Scan the adapter interrupt vector for this device. */
aibv = zpci_ibv[si]; for (ai = 0;;) {
ai = airq_iv_scan(aibv, ai, airq_iv_end(aibv)); if (ai == -1UL) break;
inc_irq_stat(IRQIO_MSI);
airq_iv_lock(aibv, ai);
generic_handle_irq(airq_iv_get_data(aibv, ai));
airq_iv_unlock(aibv, ai);
}
}
}
staticint __alloc_airq(struct zpci_dev *zdev, int msi_vecs, unsignedlong *bit)
{ if (irq_delivery == DIRECTED) { /* Allocate cpu vector bits */
*bit = airq_iv_alloc(zpci_ibv[0], msi_vecs); if (*bit == -1UL) return -EIO;
} else { /* Allocate adapter summary indicator bit */
*bit = airq_iv_alloc_bit(zpci_sbv); if (*bit == -1UL) return -EIO;
zdev->aisb = *bit;
/* * Request MSI interrupts: * When using MSI, nvec_used interrupt sources and their irq * descriptors are controlled through one msi descriptor. * Thus the outer loop over msi descriptors shall run only once, * while two inner loops iterate over the interrupt vectors. * When using MSI-X, each interrupt vector/irq descriptor * is bound to exactly one msi descriptor (nvec_used is one). * So the inner loops are executed once, while the outer iterates * over the MSI-X descriptors.
*/
hwirq = bit;
msi_for_each_desc(msi, &pdev->dev, MSI_DESC_NOTASSOCIATED) { if (hwirq - bit >= msi_vecs) break;
irqs_per_msi = min_t(unsignedint, msi_vecs, msi->nvec_used);
irq = __irq_alloc_descs(-1, 0, irqs_per_msi, 0, THIS_MODULE,
(irq_delivery == DIRECTED) ?
msi->affinity : NULL); if (irq < 0) return -ENOMEM;
for (i = 0; i < irqs_per_msi; i++) {
rc = irq_set_msi_desc_off(irq, i, msi); if (rc) return rc;
irq_set_chip_and_handler(irq + i, &zpci_irq_chip,
handle_percpu_irq);
}
msg.data = hwirq - bit; if (irq_delivery == DIRECTED) { if (msi->affinity)
cpu = cpumask_first(&msi->affinity->mask); else
cpu = 0;
cpu_addr = smp_cpu_get_cpu_address(cpu);
zpci_ibv = kcalloc(num_possible_cpus(), sizeof(*zpci_ibv),
GFP_KERNEL); if (!zpci_ibv) return -ENOMEM;
for_each_possible_cpu(cpu) { /* * Per CPU IRQ vectors look the same but bit-allocation * is only done on the first vector.
*/
zpci_ibv[cpu] = airq_iv_create(cache_line_size() * BITS_PER_BYTE,
AIRQ_IV_DATA |
AIRQ_IV_CACHELINE |
(!cpu ? AIRQ_IV_ALLOC : 0), NULL); if (!zpci_ibv[cpu]) return -ENOMEM;
}
on_each_cpu(cpu_enable_directed_irq, NULL, 1);
if (irq_delivery == DIRECTED)
zpci_airq.handler = zpci_directed_irq_handler;
rc = register_adapter_interrupt(&zpci_airq); if (rc) goto out; /* Set summary to 1 to be called every time for the ISC. */
*zpci_airq.lsi_ptr = 1;
switch (irq_delivery) { case FLOATING:
rc = zpci_floating_irq_init(); break; case DIRECTED:
rc = zpci_directed_irq_init(); break;
}
if (rc) goto out_airq;
/* * Enable floating IRQs (with suppression after one IRQ). When using * directed IRQs this enables the fallback path.
*/
zpci_set_irq_ctrl(SIC_IRQ_MODE_SINGLE, PCI_ISC, &iib);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.