aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/3c503.c
diff options
context:
space:
mode:
authorBen Hutchings <ben@decadent.org.uk>2010-04-07 23:55:47 -0400
committerDavid S. Miller <davem@davemloft.net>2010-04-07 23:55:47 -0400
commitb0cf4dfb7cd21556efd9a6a67edcba0840b4d98d (patch)
tree96e70aaf37b7c35435324c66057be8aa34a7bdff /drivers/net/3c503.c
parente31d5a05948e4478ba8396063d1e1f39880928e2 (diff)
3c503: Fix IRQ probing
The driver attempts to select an IRQ for the NIC automatically by testing which of the supported IRQs are available and then probing each available IRQ with probe_irq_{on,off}(). There are obvious race conditions here, besides which: 1. The test for availability is done by passing a NULL handler, which now always returns -EINVAL, thus the device cannot be opened: <http://bugs.debian.org/566522> 2. probe_irq_off() will report only the first ISA IRQ handled, potentially leading to a false negative. There was another bug that meant it ignored all error codes from request_irq() except -EBUSY, so it would 'succeed' despite this (possibly causing conflicts with other ISA devices). This was fixed by ab08999d6029bb2c79c16be5405d63d2bedbdfea 'WARNING: some request_irq() failures ignored in el2_open()', which exposed bug 1. This patch: 1. Replaces the use of probe_irq_{on,off}() with a real interrupt handler 2. Adds a delay before checking the interrupt-seen flag 3. Disables interrupts on all failure paths 4. Distinguishes error codes from the second request_irq() call, consistently with the first Compile-tested only. Signed-off-by: Ben Hutchings <ben@decadent.org.uk> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/3c503.c')
-rw-r--r--drivers/net/3c503.c42
1 files changed, 30 insertions, 12 deletions
diff --git a/drivers/net/3c503.c b/drivers/net/3c503.c
index 66e0323c1839..b74a0eadbd6c 100644
--- a/drivers/net/3c503.c
+++ b/drivers/net/3c503.c
@@ -380,6 +380,12 @@ out:
380 return retval; 380 return retval;
381} 381}
382 382
383static irqreturn_t el2_probe_interrupt(int irq, void *seen)
384{
385 *(bool *)seen = true;
386 return IRQ_HANDLED;
387}
388
383static int 389static int
384el2_open(struct net_device *dev) 390el2_open(struct net_device *dev)
385{ 391{
@@ -391,23 +397,35 @@ el2_open(struct net_device *dev)
391 397
392 outb(EGACFR_NORM, E33G_GACFR); /* Enable RAM and interrupts. */ 398 outb(EGACFR_NORM, E33G_GACFR); /* Enable RAM and interrupts. */
393 do { 399 do {
394 retval = request_irq(*irqp, NULL, 0, "bogus", dev); 400 bool seen;
395 if (retval >= 0) { 401
402 retval = request_irq(*irqp, el2_probe_interrupt, 0,
403 dev->name, &seen);
404 if (retval == -EBUSY)
405 continue;
406 if (retval < 0)
407 goto err_disable;
408
396 /* Twinkle the interrupt, and check if it's seen. */ 409 /* Twinkle the interrupt, and check if it's seen. */
397 unsigned long cookie = probe_irq_on(); 410 seen = false;
411 smp_wmb();
398 outb_p(0x04 << ((*irqp == 9) ? 2 : *irqp), E33G_IDCFR); 412 outb_p(0x04 << ((*irqp == 9) ? 2 : *irqp), E33G_IDCFR);
399 outb_p(0x00, E33G_IDCFR); 413 outb_p(0x00, E33G_IDCFR);
400 if (*irqp == probe_irq_off(cookie) && /* It's a good IRQ line! */ 414 msleep(1);
401 ((retval = request_irq(dev->irq = *irqp, 415 free_irq(*irqp, el2_probe_interrupt);
402 eip_interrupt, 0, 416 if (!seen)
403 dev->name, dev)) == 0)) 417 continue;
404 break; 418
405 } else { 419 retval = request_irq(dev->irq = *irqp, eip_interrupt, 0,
406 if (retval != -EBUSY) 420 dev->name, dev);
407 return retval; 421 if (retval == -EBUSY)
408 } 422 continue;
423 if (retval < 0)
424 goto err_disable;
409 } while (*++irqp); 425 } while (*++irqp);
426
410 if (*irqp == 0) { 427 if (*irqp == 0) {
428 err_disable:
411 outb(EGACFR_IRQOFF, E33G_GACFR); /* disable interrupts. */ 429 outb(EGACFR_IRQOFF, E33G_GACFR); /* disable interrupts. */
412 return -EAGAIN; 430 return -EAGAIN;
413 } 431 }