From 04ca6980927cca2b56bd6948f17c4aff4a402354 Mon Sep 17 00:00:00 2001 From: Shaun Ruffell Date: Mon, 23 Mar 2009 23:48:43 +0000 Subject: 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 --- drivers/dahdi/wctc4xxp/base.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'drivers/dahdi/wctc4xxp/base.c') 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 -- cgit v1.2.3