summaryrefslogtreecommitdiff
path: root/drivers/dahdi/wctc4xxp
diff options
context:
space:
mode:
authorShaun Ruffell <sruffell@digium.com>2009-03-23 23:48:43 +0000
committerShaun Ruffell <sruffell@digium.com>2009-03-23 23:48:43 +0000
commit04ca6980927cca2b56bd6948f17c4aff4a402354 (patch)
treee013f570ac19fb30a134ae99b750cfc673d0fcf1 /drivers/dahdi/wctc4xxp
parent9d3d65dc6d3a2d25dec66c777616018902fdc8a5 (diff)
Set TX_COMPLETE atomically with changes to the waiting_for_response_list.
This change is to catch a condition where it is possible, for whatever reason, for a response to come in before the request is marked tx complete. If this happened, it was possible to leak the response packet and double complete the command. git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@6222 a0bf4364-ded3-4de4-8d8a-66a801d63aff
Diffstat (limited to 'drivers/dahdi/wctc4xxp')
-rw-r--r--drivers/dahdi/wctc4xxp/base.c18
1 files changed, 15 insertions, 3 deletions
diff --git a/drivers/dahdi/wctc4xxp/base.c b/drivers/dahdi/wctc4xxp/base.c
index d092b4d..b374a6a 100644
--- a/drivers/dahdi/wctc4xxp/base.c
+++ b/drivers/dahdi/wctc4xxp/base.c
@@ -212,6 +212,7 @@ struct tcb {
void *data;
/* The number of bytes available in data. */
int data_len;
+ spinlock_t lock;
};
static inline void *hdr_from_cmd(struct tcb *cmd)
@@ -235,6 +236,7 @@ initialize_cmd(struct tcb *cmd, unsigned long cmd_flags)
cmd->flags = cmd_flags;
cmd->data = &cmd->cmd[0];
cmd->data_len = SFRAME_SIZE;
+ spin_lock_init(&cmd->lock);
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
@@ -910,7 +912,6 @@ wctc4xxp_retrieve(struct wctc4xxp_descriptor_ring *dr)
--dr->count;
WARN_ON(!c);
c->data_len = (d->des0 >> 16) & BUFFER1_SIZE_MASK;
- c->flags |= TX_COMPLETE;
} else {
c = NULL;
}
@@ -2024,6 +2025,7 @@ do_rx_response_packet(struct wcdte *wc, struct tcb *cmd)
const struct csm_encaps_hdr *rxhdr;
struct tcb *pos;
struct tcb *temp;
+ unsigned long flags;
rxhdr = cmd->data;
spin_lock_bh(&wc->cmd_list_lock);
@@ -2032,11 +2034,14 @@ do_rx_response_packet(struct wcdte *wc, struct tcb *cmd)
listhdr = pos->data;
if ((listhdr->function == rxhdr->function) &&
(listhdr->channel == rxhdr->channel)) {
+ spin_lock_irqsave(&pos->lock, flags);
list_del_init(&pos->node);
pos->flags &= ~(__WAIT_FOR_RESPONSE);
pos->response = cmd;
- WARN_ON(!(pos->flags & TX_COMPLETE));
- complete(&pos->complete);
+ if (pos->flags & TX_COMPLETE) {
+ complete(&pos->complete);
+ }
+ spin_unlock_irqrestore(&pos->lock, flags);
break;
}
}
@@ -2234,18 +2239,25 @@ wctc4xxp_receiveprep(struct wcdte *wc, struct tcb *cmd)
static inline void service_tx_ring(struct wcdte *wc)
{
struct tcb *cmd;
+ unsigned long flags;
while ((cmd = wctc4xxp_retrieve(wc->txd))) {
+ spin_lock_irqsave(&cmd->lock, flags);
+ cmd->flags |= TX_COMPLETE;
if (!(cmd->flags & (__WAIT_FOR_ACK | __WAIT_FOR_RESPONSE))) {
/* If we're not waiting for an ACK or Response from
* the DTE, this message should not be sitting on any
* lists. */
WARN_ON(!list_empty(&cmd->node));
if (DO_NOT_AUTO_FREE & cmd->flags) {
+ spin_unlock_irqrestore(&cmd->lock, flags);
WARN_ON(!(cmd->flags & TX_COMPLETE));
complete(&cmd->complete);
} else {
+ spin_unlock_irqrestore(&cmd->lock, flags);
free_cmd(cmd);
}
+ } else {
+ spin_unlock_irqrestore(&cmd->lock, flags);
}
/* We've freed up a spot in the hardware ring buffer. If
* another packet is queued up, let's submit it to the