From: Jakub Kicinski <moorray@wp.pl>
To: Stanislaw Gruszka <sgruszka@redhat.com>
Cc: "John W. Linville" <linville@tuxdriver.com>,
linux-wireless@vger.kernel.org, users@rt2x00.serialmonkey.com
Subject: Re: [rt2x00-users] [PATCH 3/5] rt2x00: rt2800usb: rework txstatus code
Date: Sat, 17 Mar 2012 17:53:11 +0100 [thread overview]
Message-ID: <20120317175311.1cf09433@north> (raw)
In-Reply-To: <1331720181-18564-3-git-send-email-sgruszka@redhat.com>
Hi,
I feel a bit guilty to comment on a patch you have first posted more
than a week ago and that have already been merged but to jump in with
patches seems even worse... Let's hope none of my points are valid ;-)
On Wed, 14 Mar 2012 11:16:19 +0100
Stanislaw Gruszka <sgruszka@redhat.com> wrote:
> Currently we read tx status register after each urb data transfer. As
> callback procedure also trigger reading, that causing we have many
> "threads" of reading status. To prevent that introduce TX_STATUS_READING
> flags, and check if we are already in process of sequential reading
> TX_STA_FIFO, before requesting new reads.
>
> Change timer to hrtimer, that make TX_STA_FIFO overruns less possible.
> Use 200 us for initial timeout, and then reschedule in 100 us period,
> this values probably have to be tuned.
>
> Make changes on txdone work. Schedule it from
> rt2800usb_tx_sta_fifo_read_completed() callback when first valid status
> show up. Check in callback if tx status timeout happens, and schedule
> work on that condition too. That make possible to remove tx status
> timeout from generic watchdog. I moved that to rt2800usb.
Does above mean that you want to check for timeouts in
rt2800usb_tx_sta_fifo_read_completed? You don't seem to be doing so.
> Loop in txdone work, that should prevent situation when we queue work,
> which is already processed, and after finish work is not rescheduled
> again.
>
> Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
> ---
> drivers/net/wireless/rt2x00/rt2800usb.c | 120 +++++++++++++++++++++-------
> drivers/net/wireless/rt2x00/rt2x00.h | 10 ++-
> drivers/net/wireless/rt2x00/rt2x00dev.c | 2 +-
> drivers/net/wireless/rt2x00/rt2x00queue.h | 12 ---
> drivers/net/wireless/rt2x00/rt2x00usb.c | 21 +-----
> 5 files changed, 101 insertions(+), 64 deletions(-)
>
> diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
> index 4eaa628..eacf94b 100644
> --- a/drivers/net/wireless/rt2x00/rt2800usb.c
> +++ b/drivers/net/wireless/rt2x00/rt2800usb.c
> @@ -114,45 +114,103 @@ static bool rt2800usb_txstatus_pending(struct rt2x00_dev *rt2x00dev)
> return false;
> }
>
> +static inline bool rt2800usb_entry_txstatus_timeout(struct queue_entry *entry)
> +{
> + bool tout;
> +
> + if (!test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags))
> + return false;
> +
> + tout = time_after(jiffies, entry->last_action + msecs_to_jiffies(100));
> + if (unlikely(tout))
> + WARNING(entry->queue->rt2x00dev,
> + "TX status timeout for entry %d in queue %d\n",
> + entry->entry_idx, entry->queue->qid);
> + return tout;
> +
> +}
> +
> +static bool rt2800usb_txstatus_timeout(struct rt2x00_dev *rt2x00dev)
> +{
> + struct data_queue *queue;
> + struct queue_entry *entry;
> +
> + tx_queue_for_each(rt2x00dev, queue) {
> + entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
> + if (rt2800usb_entry_txstatus_timeout(entry))
> + return true;
> + }
> + return false;
> +}
> +
> static bool rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev,
> int urb_status, u32 tx_status)
> {
> + bool valid;
> +
> if (urb_status) {
> - WARNING(rt2x00dev, "rt2x00usb_register_read_async failed: %d\n", urb_status);
> - return false;
> + WARNING(rt2x00dev, "TX status read failed %d\n", urb_status);
> +
> + goto stop_reading;
> }
>
> - /* try to read all TX_STA_FIFO entries before scheduling txdone_work */
> - if (rt2x00_get_field32(tx_status, TX_STA_FIFO_VALID)) {
> - if (!kfifo_put(&rt2x00dev->txstatus_fifo, &tx_status)) {
> - WARNING(rt2x00dev, "TX status FIFO overrun, "
> - "drop tx status report.\n");
> - queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work);
> - } else
> - return true;
> - } else if (!kfifo_is_empty(&rt2x00dev->txstatus_fifo)) {
> + valid = rt2x00_get_field32(tx_status, TX_STA_FIFO_VALID);
> + if (valid) {
> + if (!kfifo_put(&rt2x00dev->txstatus_fifo, &tx_status))
> + WARNING(rt2x00dev, "TX status FIFO overrun\n");
> +
> queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work);
> +
> + /* Reschedule urb to read TX status again instantly */
> + return true;
> } else if (rt2800usb_txstatus_pending(rt2x00dev)) {
> - mod_timer(&rt2x00dev->txstatus_timer, jiffies + msecs_to_jiffies(2));
> + /* Read register after 250 us */
> + hrtimer_start(&rt2x00dev->txstatus_timer, ktime_set(0, 250000),
> + HRTIMER_MODE_REL);
> + return false;
> }
>
> - return false;
> +stop_reading:
> + clear_bit(TX_STATUS_READING, &rt2x00dev->flags);
> + /*
> + * There is small race window above, between txstatus pending check and
> + * clear_bit someone could do rt2x00usb_interrupt_txdone, so recheck
> + * here again if status reading is needed.
> + */
> + if (rt2800usb_txstatus_pending(rt2x00dev) &&
> + test_and_set_bit(TX_STATUS_READING, &rt2x00dev->flags))
I would put a bang before that test_and...
> + return true;
> + else
> + return false;
> +}
> +
> +static void rt2800usb_async_read_tx_status(struct rt2x00_dev *rt2x00dev)
> +{
> +
> + if (test_and_set_bit(TX_STATUS_READING, &rt2x00dev->flags))
> + return;
> +
> + /* Read TX_STA_FIFO register after 500 us */
> + hrtimer_start(&rt2x00dev->txstatus_timer, ktime_set(0, 500000),
> + HRTIMER_MODE_REL);
> }
>
> static void rt2800usb_tx_dma_done(struct queue_entry *entry)
> {
> struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
>
> - rt2x00usb_register_read_async(rt2x00dev, TX_STA_FIFO,
> - rt2800usb_tx_sta_fifo_read_completed);
> + rt2800usb_async_read_tx_status(rt2x00dev);
> }
>
> -static void rt2800usb_tx_sta_fifo_timeout(unsigned long data)
> +static enum hrtimer_restart rt2800usb_tx_sta_fifo_timeout(struct hrtimer *timer)
> {
> - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
> + struct rt2x00_dev *rt2x00dev =
> + container_of(timer, struct rt2x00_dev, txstatus_timer);
>
> rt2x00usb_register_read_async(rt2x00dev, TX_STA_FIFO,
> rt2800usb_tx_sta_fifo_read_completed);
> +
> + return HRTIMER_NORESTART;
> }
>
> /*
> @@ -540,7 +598,7 @@ static void rt2800usb_txdone_nostatus(struct rt2x00_dev *rt2x00dev)
>
> if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags))
> rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE);
> - else if (rt2x00queue_status_timeout(entry))
> + else if (rt2800usb_entry_txstatus_timeout(entry))
> rt2x00lib_txdone_noinfo(entry, TXDONE_UNKNOWN);
> else
> break;
> @@ -553,17 +611,21 @@ static void rt2800usb_work_txdone(struct work_struct *work)
> struct rt2x00_dev *rt2x00dev =
> container_of(work, struct rt2x00_dev, txdone_work);
>
> - rt2800usb_txdone(rt2x00dev);
> + while (!kfifo_is_empty(&rt2x00dev->txstatus_fifo) ||
> + rt2800usb_txstatus_timeout(rt2x00dev)) {
>
> - rt2800usb_txdone_nostatus(rt2x00dev);
> + rt2800usb_txdone(rt2x00dev);
>
> - /*
> - * The hw may delay sending the packet after DMA complete
> - * if the medium is busy, thus the TX_STA_FIFO entry is
> - * also delayed -> use a timer to retrieve it.
> - */
> - if (rt2800usb_txstatus_pending(rt2x00dev))
> - mod_timer(&rt2x00dev->txstatus_timer, jiffies + msecs_to_jiffies(2));
> + rt2800usb_txdone_nostatus(rt2x00dev);
> +
> + /*
> + * The hw may delay sending the packet after DMA complete
> + * if the medium is busy, thus the TX_STA_FIFO entry is
> + * also delayed -> use a timer to retrieve it.
> + */
> + if (rt2800usb_txstatus_pending(rt2x00dev))
> + rt2800usb_async_read_tx_status(rt2x00dev);
How is it possible that this call will ever start the timer? The
reading "thread" won't exit if rt2800usb_txstatus_pending returns true
and every dma_done will schedule reading itself.
-- Kuba
next prev parent reply other threads:[~2012-03-17 16:59 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-03-14 10:16 [PATCH 1/5] rt2x00: rt2800usb: move additional txdone into new function Stanislaw Gruszka
2012-03-14 10:16 ` [PATCH 2/5] rt2x00: rt2800usb: rework txdone code Stanislaw Gruszka
2012-03-14 10:16 ` [PATCH 3/5] rt2x00: rt2800usb: rework txstatus code Stanislaw Gruszka
2012-03-17 16:53 ` Jakub Kicinski [this message]
2012-03-19 7:52 ` [rt2x00-users] " Stanislaw Gruszka
2012-03-19 13:13 ` Jakub Kicinski
2012-03-19 14:29 ` Stanislaw Gruszka
2012-03-14 10:16 ` [PATCH 4/5] rt2x00: rt2800usb: do not check packedid for aggregated frames Stanislaw Gruszka
2012-03-14 10:16 ` [PATCH 5/5] rt2x00: rt2800usb: limit tx queues length Stanislaw Gruszka
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20120317175311.1cf09433@north \
--to=moorray@wp.pl \
--cc=linux-wireless@vger.kernel.org \
--cc=linville@tuxdriver.com \
--cc=sgruszka@redhat.com \
--cc=users@rt2x00.serialmonkey.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).