123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345 |
- #define pr_fmt(fmt) "genirq/ipi: " fmt
- #include <linux/irqdomain.h>
- #include <linux/irq.h>
- int irq_reserve_ipi(struct irq_domain *domain,
- const struct cpumask *dest)
- {
- unsigned int nr_irqs, offset;
- struct irq_data *data;
- int virq, i;
- if (!domain ||!irq_domain_is_ipi(domain)) {
- pr_warn("Reservation on a non IPI domain\n");
- return -EINVAL;
- }
- if (!cpumask_subset(dest, cpu_possible_mask)) {
- pr_warn("Reservation is not in possible_cpu_mask\n");
- return -EINVAL;
- }
- nr_irqs = cpumask_weight(dest);
- if (!nr_irqs) {
- pr_warn("Reservation for empty destination mask\n");
- return -EINVAL;
- }
- if (irq_domain_is_ipi_single(domain)) {
-
- nr_irqs = 1;
- offset = 0;
- } else {
- unsigned int next;
-
- offset = cpumask_first(dest);
-
- next = cpumask_next_zero(offset, dest);
- if (next < nr_cpu_ids)
- next = cpumask_next(next, dest);
- if (next < nr_cpu_ids) {
- pr_warn("Destination mask has holes\n");
- return -EINVAL;
- }
- }
- virq = irq_domain_alloc_descs(-1, nr_irqs, 0, NUMA_NO_NODE, NULL);
- if (virq <= 0) {
- pr_warn("Can't reserve IPI, failed to alloc descs\n");
- return -ENOMEM;
- }
- virq = __irq_domain_alloc_irqs(domain, virq, nr_irqs, NUMA_NO_NODE,
- (void *) dest, true, NULL);
- if (virq <= 0) {
- pr_warn("Can't reserve IPI, failed to alloc hw irqs\n");
- goto free_descs;
- }
- for (i = 0; i < nr_irqs; i++) {
- data = irq_get_irq_data(virq + i);
- cpumask_copy(data->common->affinity, dest);
- data->common->ipi_offset = offset;
- irq_set_status_flags(virq + i, IRQ_NO_BALANCING);
- }
- return virq;
- free_descs:
- irq_free_descs(virq, nr_irqs);
- return -EBUSY;
- }
- int irq_destroy_ipi(unsigned int irq, const struct cpumask *dest)
- {
- struct irq_data *data = irq_get_irq_data(irq);
- const struct cpumask *ipimask;
- struct irq_domain *domain;
- unsigned int nr_irqs;
- if (!irq || !data)
- return -EINVAL;
- domain = data->domain;
- if (WARN_ON(domain == NULL))
- return -EINVAL;
- if (!irq_domain_is_ipi(domain)) {
- pr_warn("Trying to destroy a non IPI domain!\n");
- return -EINVAL;
- }
- ipimask = irq_data_get_affinity_mask(data);
- if (!ipimask || WARN_ON(!cpumask_subset(dest, ipimask)))
-
- return -EINVAL;
- if (irq_domain_is_ipi_per_cpu(domain)) {
- irq = irq + cpumask_first(dest) - data->common->ipi_offset;
- nr_irqs = cpumask_weight(dest);
- } else {
- nr_irqs = 1;
- }
- irq_domain_free_irqs(irq, nr_irqs);
- return 0;
- }
- irq_hw_number_t ipi_get_hwirq(unsigned int irq, unsigned int cpu)
- {
- struct irq_data *data = irq_get_irq_data(irq);
- const struct cpumask *ipimask;
- if (!data || cpu >= nr_cpu_ids)
- return INVALID_HWIRQ;
- ipimask = irq_data_get_affinity_mask(data);
- if (!ipimask || !cpumask_test_cpu(cpu, ipimask))
- return INVALID_HWIRQ;
-
- if (irq_domain_is_ipi_per_cpu(data->domain))
- data = irq_get_irq_data(irq + cpu - data->common->ipi_offset);
- return data ? irqd_to_hwirq(data) : INVALID_HWIRQ;
- }
- EXPORT_SYMBOL_GPL(ipi_get_hwirq);
- static int ipi_send_verify(struct irq_chip *chip, struct irq_data *data,
- const struct cpumask *dest, unsigned int cpu)
- {
- const struct cpumask *ipimask;
- if (!chip || !data)
- return -EINVAL;
- if (!chip->ipi_send_single && !chip->ipi_send_mask)
- return -EINVAL;
- if (cpu >= nr_cpu_ids)
- return -EINVAL;
- ipimask = irq_data_get_affinity_mask(data);
- if (!ipimask)
- return -EINVAL;
- if (dest) {
- if (!cpumask_subset(dest, ipimask))
- return -EINVAL;
- } else {
- if (!cpumask_test_cpu(cpu, ipimask))
- return -EINVAL;
- }
- return 0;
- }
- int __ipi_send_single(struct irq_desc *desc, unsigned int cpu)
- {
- struct irq_data *data = irq_desc_get_irq_data(desc);
- struct irq_chip *chip = irq_data_get_irq_chip(data);
- #ifdef DEBUG
-
- if (WARN_ON_ONCE(ipi_send_verify(chip, data, NULL, cpu)))
- return -EINVAL;
- #endif
- if (!chip->ipi_send_single) {
- chip->ipi_send_mask(data, cpumask_of(cpu));
- return 0;
- }
-
- if (irq_domain_is_ipi_per_cpu(data->domain) &&
- cpu != data->common->ipi_offset) {
-
- unsigned irq = data->irq + cpu - data->common->ipi_offset;
- data = irq_get_irq_data(irq);
- }
- chip->ipi_send_single(data, cpu);
- return 0;
- }
- int __ipi_send_mask(struct irq_desc *desc, const struct cpumask *dest)
- {
- struct irq_data *data = irq_desc_get_irq_data(desc);
- struct irq_chip *chip = irq_data_get_irq_chip(data);
- unsigned int cpu;
- #ifdef DEBUG
-
- if (WARN_ON_ONCE(ipi_send_verify(chip, data, dest, 0)))
- return -EINVAL;
- #endif
- if (chip->ipi_send_mask) {
- chip->ipi_send_mask(data, dest);
- return 0;
- }
- if (irq_domain_is_ipi_per_cpu(data->domain)) {
- unsigned int base = data->irq;
- for_each_cpu(cpu, dest) {
- unsigned irq = base + cpu - data->common->ipi_offset;
- data = irq_get_irq_data(irq);
- chip->ipi_send_single(data, cpu);
- }
- } else {
- for_each_cpu(cpu, dest)
- chip->ipi_send_single(data, cpu);
- }
- return 0;
- }
- int ipi_send_single(unsigned int virq, unsigned int cpu)
- {
- struct irq_desc *desc = irq_to_desc(virq);
- struct irq_data *data = desc ? irq_desc_get_irq_data(desc) : NULL;
- struct irq_chip *chip = data ? irq_data_get_irq_chip(data) : NULL;
- if (WARN_ON_ONCE(ipi_send_verify(chip, data, NULL, cpu)))
- return -EINVAL;
- return __ipi_send_single(desc, cpu);
- }
- EXPORT_SYMBOL_GPL(ipi_send_single);
- int ipi_send_mask(unsigned int virq, const struct cpumask *dest)
- {
- struct irq_desc *desc = irq_to_desc(virq);
- struct irq_data *data = desc ? irq_desc_get_irq_data(desc) : NULL;
- struct irq_chip *chip = data ? irq_data_get_irq_chip(data) : NULL;
- if (WARN_ON_ONCE(ipi_send_verify(chip, data, dest, 0)))
- return -EINVAL;
- return __ipi_send_mask(desc, dest);
- }
- EXPORT_SYMBOL_GPL(ipi_send_mask);
|