Commit b1e3e2f0 authored by Dominik Brodowski's avatar Dominik Brodowski Committed by Linus Torvalds

[PATCH] pcmcia: remove racy try_irq()

Remove the racy try_irq/check_irq/undo_irq interface, and try to register the
correct handler directly in pcmcia_request_irq().  Also, simplify the IRQ
usage database, but avoid using the same IRQ twice.
Signed-off-by: default avatarDominik Brodowski <linux@brodo.de>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent b6c2f23b
...@@ -117,6 +117,11 @@ EXPORT_SYMBOL(pcmcia_socket_list); ...@@ -117,6 +117,11 @@ EXPORT_SYMBOL(pcmcia_socket_list);
EXPORT_SYMBOL(pcmcia_socket_list_rwsem); EXPORT_SYMBOL(pcmcia_socket_list_rwsem);
#ifdef CONFIG_PCMCIA_PROBE
/* mask ofIRQs already reserved by other cards, we should avoid using them */
static u8 pcmcia_used_irq[NR_IRQS];
#endif
/*==================================================================== /*====================================================================
Low-level PC Card interface drivers need to register with Card Low-level PC Card interface drivers need to register with Card
...@@ -1295,10 +1300,9 @@ int pcmcia_release_irq(client_handle_t handle, irq_req_t *req) ...@@ -1295,10 +1300,9 @@ int pcmcia_release_irq(client_handle_t handle, irq_req_t *req)
} }
#ifdef CONFIG_PCMCIA_PROBE #ifdef CONFIG_PCMCIA_PROBE
if (req->AssignedIRQ != s->pci_irq) pcmcia_used_irq[req->AssignedIRQ]--;
undo_irq(req->Attributes, req->AssignedIRQ);
#endif #endif
return CS_SUCCESS; return CS_SUCCESS;
} /* cs_release_irq */ } /* cs_release_irq */
...@@ -1530,65 +1534,96 @@ int pcmcia_request_io(client_handle_t handle, io_req_t *req) ...@@ -1530,65 +1534,96 @@ int pcmcia_request_io(client_handle_t handle, io_req_t *req)
======================================================================*/ ======================================================================*/
#ifdef CONFIG_PCMCIA_PROBE
static irqreturn_t test_action(int cpl, void *dev_id, struct pt_regs *regs)
{
return IRQ_NONE;
}
#endif
int pcmcia_request_irq(client_handle_t handle, irq_req_t *req) int pcmcia_request_irq(client_handle_t handle, irq_req_t *req)
{ {
struct pcmcia_socket *s; struct pcmcia_socket *s;
config_t *c; config_t *c;
int ret = CS_IN_USE, irq = 0; int ret = CS_IN_USE, irq = 0;
struct pcmcia_device *p_dev = handle_to_pdev(handle); struct pcmcia_device *p_dev = handle_to_pdev(handle);
if (CHECK_HANDLE(handle)) if (CHECK_HANDLE(handle))
return CS_BAD_HANDLE; return CS_BAD_HANDLE;
s = SOCKET(handle); s = SOCKET(handle);
if (!(s->state & SOCKET_PRESENT)) if (!(s->state & SOCKET_PRESENT))
return CS_NO_CARD; return CS_NO_CARD;
c = CONFIG(handle); c = CONFIG(handle);
if (c->state & CONFIG_LOCKED) if (c->state & CONFIG_LOCKED)
return CS_CONFIGURATION_LOCKED; return CS_CONFIGURATION_LOCKED;
if (c->state & CONFIG_IRQ_REQ) if (c->state & CONFIG_IRQ_REQ)
return CS_IN_USE; return CS_IN_USE;
#ifdef CONFIG_PCMCIA_PROBE #ifdef CONFIG_PCMCIA_PROBE
if (s->irq.AssignedIRQ != 0) { if (s->irq.AssignedIRQ != 0) {
/* If the interrupt is already assigned, it must be the same */ /* If the interrupt is already assigned, it must be the same */
irq = s->irq.AssignedIRQ; irq = s->irq.AssignedIRQ;
} else { } else {
u_int try, mask = s->irq_mask; int try;
for (try = 0; try < 2; try++) { u32 mask = s->irq_mask;
for (irq = 0; irq < 32; irq++) { void *data = NULL;
if ((mask >> irq) & 1) {
ret = try_irq(req->Attributes, irq, try); for (try = 0; try < 64; try++) {
if (!ret) irq = try % 32;
break;
} /* marked as available by driver, and not blocked by userspace? */
} if (!((mask >> irq) & 1))
if (!ret) continue;
/* avoid an IRQ which is already used by a PCMCIA card */
if ((try < 32) && pcmcia_used_irq[irq])
continue;
/* register the correct driver, if possible, of check whether
* registering a dummy handle works, i.e. if the IRQ isn't
* marked as used by the kernel resource management core */
ret = request_irq(irq,
(req->Attributes & IRQ_HANDLE_PRESENT) ? req->Handler : test_action,
((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) ||
(s->functions > 1) ||
(irq == s->pci_irq)) ? SA_SHIRQ : 0,
p_dev->dev.bus_id,
(req->Attributes & IRQ_HANDLE_PRESENT) ? req->Instance : data);
if (!ret) {
if (!(req->Attributes & IRQ_HANDLE_PRESENT))
free_irq(irq, data);
break; break;
}
} }
} }
#endif #endif
if (ret != 0) { if (ret) {
if (!s->pci_irq) if (!s->pci_irq)
return ret; return ret;
irq = s->pci_irq; irq = s->pci_irq;
} }
if (req->Attributes & IRQ_HANDLE_PRESENT) { if (ret && req->Attributes & IRQ_HANDLE_PRESENT) {
if (request_irq(irq, req->Handler, if (request_irq(irq, req->Handler,
((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) || ((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) ||
(s->functions > 1) || (s->functions > 1) ||
(irq == s->pci_irq)) ? SA_SHIRQ : 0, (irq == s->pci_irq)) ? SA_SHIRQ : 0,
p_dev->dev.bus_id, req->Instance)) p_dev->dev.bus_id, req->Instance))
return CS_IN_USE; return CS_IN_USE;
} }
c->irq.Attributes = req->Attributes; c->irq.Attributes = req->Attributes;
s->irq.AssignedIRQ = req->AssignedIRQ = irq; s->irq.AssignedIRQ = req->AssignedIRQ = irq;
s->irq.Config++; s->irq.Config++;
c->state |= CONFIG_IRQ_REQ; c->state |= CONFIG_IRQ_REQ;
handle->state |= CLIENT_IRQ_REQ; handle->state |= CLIENT_IRQ_REQ;
return CS_SUCCESS;
#ifdef CONFIG_PCMCIA_PROBE
pcmcia_used_irq[irq]++;
#endif
return CS_SUCCESS;
} /* pcmcia_request_irq */ } /* pcmcia_request_irq */
/*====================================================================== /*======================================================================
......
...@@ -140,8 +140,6 @@ int adjust_io_region(struct resource *res, unsigned long r_start, ...@@ -140,8 +140,6 @@ int adjust_io_region(struct resource *res, unsigned long r_start,
unsigned long r_end, struct pcmcia_socket *s); unsigned long r_end, struct pcmcia_socket *s);
struct resource *find_mem_region(u_long base, u_long num, u_long align, struct resource *find_mem_region(u_long base, u_long num, u_long align,
int low, struct pcmcia_socket *s); int low, struct pcmcia_socket *s);
int try_irq(u_int Attributes, int irq, int specific);
void undo_irq(u_int Attributes, int irq);
int adjust_resource_info(client_handle_t handle, adjust_t *adj); int adjust_resource_info(client_handle_t handle, adjust_t *adj);
void release_resource_db(struct pcmcia_socket *s); void release_resource_db(struct pcmcia_socket *s);
......
...@@ -34,122 +34,6 @@ ...@@ -34,122 +34,6 @@
#include <pcmcia/cistpl.h> #include <pcmcia/cistpl.h>
#include "cs_internal.h" #include "cs_internal.h"
static DECLARE_MUTEX(rsrc_sem);
#ifdef CONFIG_PCMCIA_PROBE
typedef struct irq_info_t {
u_int Attributes;
int time_share, dyn_share;
struct pcmcia_socket *Socket;
} irq_info_t;
/* Table of IRQ assignments */
static irq_info_t irq_table[NR_IRQS];
#endif
/*======================================================================
This checks to see if an interrupt is available, with support
for interrupt sharing. We don't support reserving interrupts
yet. If the interrupt is available, we allocate it.
======================================================================*/
#ifdef CONFIG_PCMCIA_PROBE
static irqreturn_t fake_irq(int i, void *d, struct pt_regs *r) { return IRQ_NONE; }
static inline int check_irq(int irq)
{
if (request_irq(irq, fake_irq, 0, "bogus", NULL) != 0)
return -1;
free_irq(irq, NULL);
return 0;
}
int try_irq(u_int Attributes, int irq, int specific)
{
irq_info_t *info = &irq_table[irq];
int ret = 0;
down(&rsrc_sem);
if (info->Attributes & RES_ALLOCATED) {
switch (Attributes & IRQ_TYPE) {
case IRQ_TYPE_EXCLUSIVE:
ret = CS_IN_USE;
break;
case IRQ_TYPE_DYNAMIC_SHARING:
if ((info->Attributes & RES_IRQ_TYPE)
!= RES_IRQ_TYPE_DYNAMIC) {
ret = CS_IN_USE;
break;
}
if (Attributes & IRQ_FIRST_SHARED) {
ret = CS_BAD_ATTRIBUTE;
break;
}
info->Attributes |= RES_IRQ_TYPE_DYNAMIC | RES_ALLOCATED;
info->dyn_share++;
break;
}
} else {
if ((info->Attributes & RES_RESERVED) && !specific) {
ret = CS_IN_USE;
goto out;
}
if (check_irq(irq) != 0) {
ret = CS_IN_USE;
goto out;
}
switch (Attributes & IRQ_TYPE) {
case IRQ_TYPE_EXCLUSIVE:
info->Attributes |= RES_ALLOCATED;
break;
case IRQ_TYPE_DYNAMIC_SHARING:
if (!(Attributes & IRQ_FIRST_SHARED)) {
ret = CS_BAD_ATTRIBUTE;
break;
}
info->Attributes |= RES_IRQ_TYPE_DYNAMIC | RES_ALLOCATED;
info->dyn_share = 1;
break;
}
}
out:
up(&rsrc_sem);
return ret;
}
#endif
/*====================================================================*/
#ifdef CONFIG_PCMCIA_PROBE
void undo_irq(u_int Attributes, int irq)
{
irq_info_t *info;
info = &irq_table[irq];
down(&rsrc_sem);
switch (Attributes & IRQ_TYPE) {
case IRQ_TYPE_EXCLUSIVE:
info->Attributes &= RES_RESERVED;
break;
case IRQ_TYPE_DYNAMIC_SHARING:
info->dyn_share--;
if (info->dyn_share == 0)
info->Attributes &= RES_RESERVED;
break;
}
up(&rsrc_sem);
}
#endif
/*====================================================================*/
#ifdef CONFIG_PCMCIA_PROBE #ifdef CONFIG_PCMCIA_PROBE
...@@ -167,7 +51,7 @@ static int adjust_irq(struct pcmcia_socket *s, adjust_t *adj) ...@@ -167,7 +51,7 @@ static int adjust_irq(struct pcmcia_socket *s, adjust_t *adj)
mask = 1 << irq; mask = 1 << irq;
if !(s->irq_mask & mask) if (!(s->irq_mask & mask))
return 0; return 0;
s->irq_mask &= ~mask; s->irq_mask &= ~mask;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment