Commit eb4e545d authored by David Sterba's avatar David Sterba Committed by Linus Torvalds

ipwireless: Fix blocked sending

Packet sending is driven by two flags, tx_ready and tx_queued.
It was possible, that there were queued data for sending and
hardware was flagged as blocked but in fact it was not.

The tx_queued was indicator but should be really a counter else
first fragmented packet resets tx_queued flag, but there may be
pending packets which do not get sent.

New semantics:
tx_ready - set, if hw is ready to send packet, no packet is being
           transferred right now
           set the flag right at the place where data are copied
           into hw memory and not earlier without checking if it
           was succesful
tx_queued - count of enqueued packets, including fragments
Tested-by: default avatarMichal Rokos <michal.rokos@gmail.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.cz>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 979b0fea
...@@ -251,10 +251,11 @@ struct ipw_hardware { ...@@ -251,10 +251,11 @@ struct ipw_hardware {
int init_loops; int init_loops;
struct timer_list setup_timer; struct timer_list setup_timer;
/* Flag if hw is ready to send next packet */
int tx_ready; int tx_ready;
struct list_head tx_queue[NL_NUM_OF_PRIORITIES]; /* Count of pending packets to be sent */
/* True if any packets are queued for transmission */
int tx_queued; int tx_queued;
struct list_head tx_queue[NL_NUM_OF_PRIORITIES];
int rx_bytes_queued; int rx_bytes_queued;
struct list_head rx_queue; struct list_head rx_queue;
...@@ -404,6 +405,8 @@ static int do_send_fragment(struct ipw_hardware *hw, const unsigned char *data, ...@@ -404,6 +405,8 @@ static int do_send_fragment(struct ipw_hardware *hw, const unsigned char *data,
spin_lock_irqsave(&hw->spinlock, flags); spin_lock_irqsave(&hw->spinlock, flags);
hw->tx_ready = 0;
if (hw->hw_version == HW_VERSION_1) { if (hw->hw_version == HW_VERSION_1) {
outw((unsigned short) length, hw->base_port + IODWR); outw((unsigned short) length, hw->base_port + IODWR);
...@@ -492,6 +495,7 @@ static int do_send_packet(struct ipw_hardware *hw, struct ipw_tx_packet *packet) ...@@ -492,6 +495,7 @@ static int do_send_packet(struct ipw_hardware *hw, struct ipw_tx_packet *packet)
spin_lock_irqsave(&hw->spinlock, flags); spin_lock_irqsave(&hw->spinlock, flags);
list_add(&packet->queue, &hw->tx_queue[0]); list_add(&packet->queue, &hw->tx_queue[0]);
hw->tx_queued++;
spin_unlock_irqrestore(&hw->spinlock, flags); spin_unlock_irqrestore(&hw->spinlock, flags);
} else { } else {
if (packet->packet_callback) if (packet->packet_callback)
...@@ -949,12 +953,10 @@ static int send_pending_packet(struct ipw_hardware *hw, int priority_limit) ...@@ -949,12 +953,10 @@ static int send_pending_packet(struct ipw_hardware *hw, int priority_limit)
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&hw->spinlock, flags); spin_lock_irqsave(&hw->spinlock, flags);
if (hw->tx_queued && hw->tx_ready != 0) { if (hw->tx_queued && hw->tx_ready) {
int priority; int priority;
struct ipw_tx_packet *packet = NULL; struct ipw_tx_packet *packet = NULL;
hw->tx_ready--;
/* Pick a packet */ /* Pick a packet */
for (priority = 0; priority < priority_limit; priority++) { for (priority = 0; priority < priority_limit; priority++) {
if (!list_empty(&hw->tx_queue[priority])) { if (!list_empty(&hw->tx_queue[priority])) {
...@@ -963,6 +965,7 @@ static int send_pending_packet(struct ipw_hardware *hw, int priority_limit) ...@@ -963,6 +965,7 @@ static int send_pending_packet(struct ipw_hardware *hw, int priority_limit)
struct ipw_tx_packet, struct ipw_tx_packet,
queue); queue);
hw->tx_queued--;
list_del(&packet->queue); list_del(&packet->queue);
break; break;
...@@ -973,6 +976,7 @@ static int send_pending_packet(struct ipw_hardware *hw, int priority_limit) ...@@ -973,6 +976,7 @@ static int send_pending_packet(struct ipw_hardware *hw, int priority_limit)
spin_unlock_irqrestore(&hw->spinlock, flags); spin_unlock_irqrestore(&hw->spinlock, flags);
return 0; return 0;
} }
spin_unlock_irqrestore(&hw->spinlock, flags); spin_unlock_irqrestore(&hw->spinlock, flags);
/* Send */ /* Send */
...@@ -1063,7 +1067,7 @@ static irqreturn_t ipwireless_handle_v1_interrupt(int irq, ...@@ -1063,7 +1067,7 @@ static irqreturn_t ipwireless_handle_v1_interrupt(int irq,
if (irqn & IR_TXINTR) { if (irqn & IR_TXINTR) {
ack |= IR_TXINTR; ack |= IR_TXINTR;
spin_lock_irqsave(&hw->spinlock, flags); spin_lock_irqsave(&hw->spinlock, flags);
hw->tx_ready++; hw->tx_ready = 1;
spin_unlock_irqrestore(&hw->spinlock, flags); spin_unlock_irqrestore(&hw->spinlock, flags);
} }
/* Received data */ /* Received data */
...@@ -1170,7 +1174,7 @@ static irqreturn_t ipwireless_handle_v2_v3_interrupt(int irq, ...@@ -1170,7 +1174,7 @@ static irqreturn_t ipwireless_handle_v2_v3_interrupt(int irq,
if (memrxdone & MEMRX_RX_DONE) { if (memrxdone & MEMRX_RX_DONE) {
writew(0, &hw->memory_info_regs->memreg_rx_done); writew(0, &hw->memory_info_regs->memreg_rx_done);
spin_lock_irqsave(&hw->spinlock, flags); spin_lock_irqsave(&hw->spinlock, flags);
hw->tx_ready++; hw->tx_ready = 1;
spin_unlock_irqrestore(&hw->spinlock, flags); spin_unlock_irqrestore(&hw->spinlock, flags);
tx = 1; tx = 1;
} }
...@@ -1234,7 +1238,7 @@ static void send_packet(struct ipw_hardware *hw, int priority, ...@@ -1234,7 +1238,7 @@ static void send_packet(struct ipw_hardware *hw, int priority,
spin_lock_irqsave(&hw->spinlock, flags); spin_lock_irqsave(&hw->spinlock, flags);
list_add_tail(&packet->queue, &hw->tx_queue[priority]); list_add_tail(&packet->queue, &hw->tx_queue[priority]);
hw->tx_queued = 1; hw->tx_queued++;
spin_unlock_irqrestore(&hw->spinlock, flags); spin_unlock_irqrestore(&hw->spinlock, flags);
flush_packets_to_hw(hw); flush_packets_to_hw(hw);
......
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