net: mvpp2: Fix affinity hint allocation
authorMarc Zyngier <marc.zyngier@arm.com>
Tue, 30 Oct 2018 15:41:00 +0000 (15:41 +0000)
committerDavid S. Miller <davem@davemloft.net>
Tue, 30 Oct 2018 18:34:41 +0000 (11:34 -0700)
The mvpp2 driver has the curious behaviour of passing a stack variable
to irq_set_affinity_hint(), which results in the kernel exploding
the first time anyone accesses this information. News flash: userspace
does, and irqbalance will happily take the machine down. Great stuff.

An easy fix is to track the mask within the queue_vector structure,
and to make sure it has the same lifetime as the interrupt itself.

Fixes: e531f76757eb ("net: mvpp2: handle cases where more CPUs are available than s/w threads")
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/marvell/mvpp2/mvpp2.h
drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c

index 176c6b5..398328f 100644 (file)
@@ -796,6 +796,7 @@ struct mvpp2_queue_vector {
        int nrxqs;
        u32 pending_cause_rx;
        struct mvpp2_port *port;
+       struct cpumask *mask;
 };
 
 struct mvpp2_port {
index 14f9679..7a37a37 100644 (file)
@@ -3298,24 +3298,30 @@ static int mvpp2_irqs_init(struct mvpp2_port *port)
        for (i = 0; i < port->nqvecs; i++) {
                struct mvpp2_queue_vector *qv = port->qvecs + i;
 
-               if (qv->type == MVPP2_QUEUE_VECTOR_PRIVATE)
+               if (qv->type == MVPP2_QUEUE_VECTOR_PRIVATE) {
+                       qv->mask = kzalloc(cpumask_size(), GFP_KERNEL);
+                       if (!qv->mask) {
+                               err = -ENOMEM;
+                               goto err;
+                       }
+
                        irq_set_status_flags(qv->irq, IRQ_NO_BALANCING);
+               }
 
                err = request_irq(qv->irq, mvpp2_isr, 0, port->dev->name, qv);
                if (err)
                        goto err;
 
                if (qv->type == MVPP2_QUEUE_VECTOR_PRIVATE) {
-                       unsigned long mask = 0;
                        unsigned int cpu;
 
                        for_each_present_cpu(cpu) {
                                if (mvpp2_cpu_to_thread(port->priv, cpu) ==
                                    qv->sw_thread_id)
-                                       mask |= BIT(cpu);
+                                       cpumask_set_cpu(cpu, qv->mask);
                        }
 
-                       irq_set_affinity_hint(qv->irq, to_cpumask(&mask));
+                       irq_set_affinity_hint(qv->irq, qv->mask);
                }
        }
 
@@ -3325,6 +3331,8 @@ err:
                struct mvpp2_queue_vector *qv = port->qvecs + i;
 
                irq_set_affinity_hint(qv->irq, NULL);
+               kfree(qv->mask);
+               qv->mask = NULL;
                free_irq(qv->irq, qv);
        }
 
@@ -3339,6 +3347,8 @@ static void mvpp2_irqs_deinit(struct mvpp2_port *port)
                struct mvpp2_queue_vector *qv = port->qvecs + i;
 
                irq_set_affinity_hint(qv->irq, NULL);
+               kfree(qv->mask);
+               qv->mask = NULL;
                irq_clear_status_flags(qv->irq, IRQ_NO_BALANCING);
                free_irq(qv->irq, qv);
        }