Skip to content

Commit

Permalink
rt2x00: Protect queue control with mutex
Browse files Browse the repository at this point in the history
Add wrapper functions in rt2x00queue.c to
start & stop queues. This control must be protected
using a mutex.

Queues can also be paused which will halt the flow
of packets between the driver and mac80211. This doesn't
require a mutex protection.

Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Ivo van Doorn authored and John W. Linville committed Dec 13, 2010
1 parent dbba306 commit 0b7fde5
Show file tree
Hide file tree
Showing 9 changed files with 302 additions and 141 deletions.
52 changes: 52 additions & 0 deletions drivers/net/wireless/rt2x00/rt2x00.h
Original file line number Diff line number Diff line change
Expand Up @@ -1073,6 +1073,58 @@ struct data_queue *rt2x00queue_get_queue(struct rt2x00_dev *rt2x00dev,
struct queue_entry *rt2x00queue_get_entry(struct data_queue *queue,
enum queue_index index);

/**
* rt2x00queue_pause_queue - Pause a data queue
* @queue: Pointer to &struct data_queue.
*
* This function will pause the data queue locally, preventing
* new frames to be added to the queue (while the hardware is
* still allowed to run).
*/
void rt2x00queue_pause_queue(struct data_queue *queue);

/**
* rt2x00queue_unpause_queue - unpause a data queue
* @queue: Pointer to &struct data_queue.
*
* This function will unpause the data queue locally, allowing
* new frames to be added to the queue again.
*/
void rt2x00queue_unpause_queue(struct data_queue *queue);

/**
* rt2x00queue_start_queue - Start a data queue
* @queue: Pointer to &struct data_queue.
*
* This function will start handling all pending frames in the queue.
*/
void rt2x00queue_start_queue(struct data_queue *queue);

/**
* rt2x00queue_stop_queue - Halt a data queue
* @queue: Pointer to &struct data_queue.
*
* This function will stop all pending frames in the queue.
*/
void rt2x00queue_stop_queue(struct data_queue *queue);

/**
* rt2x00queue_start_queues - Start all data queues
* @rt2x00dev: Pointer to &struct rt2x00_dev.
*
* This function will loop through all available queues to start them
*/
void rt2x00queue_start_queues(struct rt2x00_dev *rt2x00dev);

/**
* rt2x00queue_stop_queues - Halt all data queues
* @rt2x00dev: Pointer to &struct rt2x00_dev.
*
* This function will loop through all available queues to stop
* any pending frames.
*/
void rt2x00queue_stop_queues(struct rt2x00_dev *rt2x00dev);

/*
* Debugfs handlers.
*/
Expand Down
4 changes: 2 additions & 2 deletions drivers/net/wireless/rt2x00/rt2x00config.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev,
* else the changes will be ignored by the device.
*/
if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
rt2x00dev->ops->lib->stop_queue(rt2x00dev->rx);
rt2x00queue_stop_queue(rt2x00dev->rx);

/*
* Write new antenna setup to device and reset the link tuner.
Expand All @@ -160,7 +160,7 @@ void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev,
memcpy(active, &config, sizeof(config));

if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
rt2x00dev->ops->lib->start_queue(rt2x00dev->rx);
rt2x00queue_start_queue(rt2x00dev->rx);
}

void rt2x00lib_config(struct rt2x00_dev *rt2x00dev,
Expand Down
5 changes: 3 additions & 2 deletions drivers/net/wireless/rt2x00/rt2x00debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -339,12 +339,13 @@ static ssize_t rt2x00debug_read_queue_stats(struct file *file,
return -ENOMEM;

temp = data +
sprintf(data, "qid\tcount\tlimit\tlength\tindex\tdma done\tdone\n");
sprintf(data, "qid\tflags\t\tcount\tlimit\tlength\tindex\tdma done\tdone\n");

queue_for_each(intf->rt2x00dev, queue) {
spin_lock_irqsave(&queue->index_lock, irqflags);

temp += sprintf(temp, "%d\t%d\t%d\t%d\t%d\t%d\t%d\n", queue->qid,
temp += sprintf(temp, "%d\t0x%.8x\t%d\t%d\t%d\t%d\t%d\t\t%d\n",
queue->qid, (unsigned int)queue->flags,
queue->count, queue->limit, queue->length,
queue->index[Q_INDEX],
queue->index[Q_INDEX_DMA_DONE],
Expand Down
22 changes: 5 additions & 17 deletions drivers/net/wireless/rt2x00/rt2x00dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,21 +66,16 @@ int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev)
set_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags);

/*
* Enable RX.
* Enable queues.
*/
rt2x00dev->ops->lib->start_queue(rt2x00dev->rx);
rt2x00queue_start_queues(rt2x00dev);
rt2x00link_start_tuner(rt2x00dev);

/*
* Start watchdog monitoring.
*/
rt2x00link_start_watchdog(rt2x00dev);

/*
* Start the TX queues.
*/
ieee80211_wake_queues(rt2x00dev->hw);

return 0;
}

Expand All @@ -89,22 +84,16 @@ void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev)
if (!test_and_clear_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
return;

/*
* Stop the TX queues in mac80211.
*/
ieee80211_stop_queues(rt2x00dev->hw);
rt2x00queue_stop_queues(rt2x00dev);

/*
* Stop watchdog monitoring.
*/
rt2x00link_stop_watchdog(rt2x00dev);

/*
* Disable RX.
* Stop all queues
*/
rt2x00link_stop_tuner(rt2x00dev);
rt2x00dev->ops->lib->stop_queue(rt2x00dev->rx);
rt2x00queue_stop_queues(rt2x00dev);

/*
* Disable radio.
Expand Down Expand Up @@ -249,7 +238,6 @@ void rt2x00lib_txdone(struct queue_entry *entry,
struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb);
struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
enum data_queue_qid qid = skb_get_queue_mapping(entry->skb);
unsigned int header_length, i;
u8 rate_idx, rate_flags, retry_rates;
u8 skbdesc_flags = skbdesc->flags;
Expand Down Expand Up @@ -403,7 +391,7 @@ void rt2x00lib_txdone(struct queue_entry *entry,
* is reenabled when the txdone handler has finished.
*/
if (!rt2x00queue_threshold(entry->queue))
ieee80211_wake_queue(rt2x00dev->hw, qid);
rt2x00queue_unpause_queue(entry->queue);
}
EXPORT_SYMBOL_GPL(rt2x00lib_txdone);

Expand Down
9 changes: 0 additions & 9 deletions drivers/net/wireless/rt2x00/rt2x00lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,15 +177,6 @@ int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev,
*/
void rt2x00queue_index_inc(struct data_queue *queue, enum queue_index index);

/**
* rt2x00queue_stop_queues - Halt all data queues
* @rt2x00dev: Pointer to &struct rt2x00_dev.
*
* This function will loop through all available queues to stop
* any pending outgoing frames.
*/
void rt2x00queue_stop_queues(struct rt2x00_dev *rt2x00dev);

/**
* rt2x00queue_init_queues - Initialize all data queues
* @rt2x00dev: Pointer to &struct rt2x00_dev.
Expand Down
8 changes: 4 additions & 4 deletions drivers/net/wireless/rt2x00/rt2x00mac.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
struct rt2x00_dev *rt2x00dev = hw->priv;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
enum data_queue_qid qid = skb_get_queue_mapping(skb);
struct data_queue *queue;
struct data_queue *queue = NULL;

/*
* Mac80211 might be calling this function while we are trying
Expand Down Expand Up @@ -153,7 +153,7 @@ int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
goto exit_fail;

if (rt2x00queue_threshold(queue))
ieee80211_stop_queue(rt2x00dev->hw, qid);
rt2x00queue_pause_queue(queue);

return NETDEV_TX_OK;

Expand Down Expand Up @@ -352,7 +352,7 @@ int rt2x00mac_config(struct ieee80211_hw *hw, u32 changed)
* if for any reason the link tuner must be reset, this will be
* handled by rt2x00lib_config().
*/
rt2x00dev->ops->lib->stop_queue(rt2x00dev->rx);
rt2x00queue_stop_queue(rt2x00dev->rx);

/*
* When we've just turned on the radio, we want to reprogram
Expand All @@ -370,7 +370,7 @@ int rt2x00mac_config(struct ieee80211_hw *hw, u32 changed)
rt2x00lib_config_antenna(rt2x00dev, rt2x00dev->default_ant);

/* Turn RX back on */
rt2x00dev->ops->lib->start_queue(rt2x00dev->rx);
rt2x00queue_start_queue(rt2x00dev->rx);

return 0;
}
Expand Down
130 changes: 121 additions & 9 deletions drivers/net/wireless/rt2x00/rt2x00queue.c
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev,
rt2x00queue_free_skb(intf->beacon);

if (!enable_beacon) {
rt2x00dev->ops->lib->stop_queue(intf->beacon->queue);
rt2x00queue_stop_queue(intf->beacon->queue);
mutex_unlock(&intf->beacon_skb_mutex);
return 0;
}
Expand Down Expand Up @@ -738,6 +738,125 @@ void rt2x00queue_index_inc(struct data_queue *queue, enum queue_index index)
spin_unlock_irqrestore(&queue->index_lock, irqflags);
}

void rt2x00queue_pause_queue(struct data_queue *queue)
{
if (!test_bit(DEVICE_STATE_PRESENT, &queue->rt2x00dev->flags) ||
!test_bit(QUEUE_STARTED, &queue->flags) ||
test_and_set_bit(QUEUE_PAUSED, &queue->flags))
return;

switch (queue->qid) {
case QID_AC_BE:
case QID_AC_BK:
case QID_AC_VI:
case QID_AC_VO:
/*
* For TX queues, we have to disable the queue
* inside mac80211.
*/
ieee80211_stop_queue(queue->rt2x00dev->hw, queue->qid);
break;
default:
break;
}
}
EXPORT_SYMBOL_GPL(rt2x00queue_pause_queue);

void rt2x00queue_unpause_queue(struct data_queue *queue)
{
if (!test_bit(DEVICE_STATE_PRESENT, &queue->rt2x00dev->flags) ||
!test_bit(QUEUE_STARTED, &queue->flags) ||
!test_and_clear_bit(QUEUE_PAUSED, &queue->flags))
return;

switch (queue->qid) {
case QID_AC_BE:
case QID_AC_BK:
case QID_AC_VI:
case QID_AC_VO:
/*
* For TX queues, we have to enable the queue
* inside mac80211.
*/
ieee80211_wake_queue(queue->rt2x00dev->hw, queue->qid);
break;
default:
break;
}
}
EXPORT_SYMBOL_GPL(rt2x00queue_unpause_queue);

void rt2x00queue_start_queue(struct data_queue *queue)
{
mutex_lock(&queue->status_lock);

if (!test_bit(DEVICE_STATE_PRESENT, &queue->rt2x00dev->flags) ||
test_and_set_bit(QUEUE_STARTED, &queue->flags)) {
mutex_unlock(&queue->status_lock);
return;
}

set_bit(QUEUE_PAUSED, &queue->flags);

queue->rt2x00dev->ops->lib->start_queue(queue);

rt2x00queue_unpause_queue(queue);

mutex_unlock(&queue->status_lock);
}
EXPORT_SYMBOL_GPL(rt2x00queue_start_queue);

void rt2x00queue_stop_queue(struct data_queue *queue)
{
mutex_lock(&queue->status_lock);

if (!test_and_clear_bit(QUEUE_STARTED, &queue->flags)) {
mutex_unlock(&queue->status_lock);
return;
}

rt2x00queue_pause_queue(queue);

queue->rt2x00dev->ops->lib->stop_queue(queue);

mutex_unlock(&queue->status_lock);
}
EXPORT_SYMBOL_GPL(rt2x00queue_stop_queue);

void rt2x00queue_start_queues(struct rt2x00_dev *rt2x00dev)
{
struct data_queue *queue;

/*
* rt2x00queue_start_queue will call ieee80211_wake_queue
* for each queue after is has been properly initialized.
*/
tx_queue_for_each(rt2x00dev, queue)
rt2x00queue_start_queue(queue);

rt2x00queue_start_queue(rt2x00dev->rx);
}
EXPORT_SYMBOL_GPL(rt2x00queue_start_queues);

void rt2x00queue_stop_queues(struct rt2x00_dev *rt2x00dev)
{
struct data_queue *queue;

/*
* rt2x00queue_stop_queue will call ieee80211_stop_queue
* as well, but we are completely shutting doing everything
* now, so it is much safer to stop all TX queues at once,
* and use rt2x00queue_stop_queue for cleaning up.
*/
ieee80211_stop_queues(rt2x00dev->hw);

tx_queue_for_each(rt2x00dev, queue)
rt2x00queue_stop_queue(queue);

rt2x00queue_stop_queue(rt2x00dev->rx);
}
EXPORT_SYMBOL_GPL(rt2x00queue_stop_queues);

static void rt2x00queue_reset(struct data_queue *queue)
{
unsigned long irqflags;
Expand All @@ -756,14 +875,6 @@ static void rt2x00queue_reset(struct data_queue *queue)
spin_unlock_irqrestore(&queue->index_lock, irqflags);
}

void rt2x00queue_stop_queues(struct rt2x00_dev *rt2x00dev)
{
struct data_queue *queue;

txall_queue_for_each(rt2x00dev, queue)
rt2x00dev->ops->lib->stop_queue(queue);
}

void rt2x00queue_init_queues(struct rt2x00_dev *rt2x00dev)
{
struct data_queue *queue;
Expand Down Expand Up @@ -905,6 +1016,7 @@ void rt2x00queue_uninitialize(struct rt2x00_dev *rt2x00dev)
static void rt2x00queue_init(struct rt2x00_dev *rt2x00dev,
struct data_queue *queue, enum data_queue_qid qid)
{
mutex_init(&queue->status_lock);
spin_lock_init(&queue->index_lock);

queue->rt2x00dev = rt2x00dev;
Expand Down
Loading

0 comments on commit 0b7fde5

Please sign in to comment.