lkml.org 
[lkml]   [2013]   [Sep]   [24]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[ 067/117] ath9k: fix rx descriptor related race condition
    Date
    3.11-stable review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Felix Fietkau <nbd@openwrt.org>

    commit e96542e55a2aacf4bdeccfe2f17b77c4895b4df2 upstream.

    Similar to a race condition that exists in the tx path, the hardware
    might re-read the 'next' pointer of a descriptor of the last completed
    frame. This only affects non-EDMA (pre-AR93xx) devices.

    To deal with this race, defer clearing and re-linking a completed rx
    descriptor until the next one has been processed.

    Signed-off-by: Felix Fietkau <nbd@openwrt.org>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

    ---
    drivers/net/wireless/ath/ath9k/ath9k.h | 5 +----
    drivers/net/wireless/ath/ath9k/recv.c | 17 +++++++++++++----
    2 files changed, 14 insertions(+), 8 deletions(-)

    --- a/drivers/net/wireless/ath/ath9k/ath9k.h
    +++ b/drivers/net/wireless/ath/ath9k/ath9k.h
    @@ -79,10 +79,6 @@ struct ath_config {
    sizeof(struct ath_buf_state)); \
    } while (0)

    -#define ATH_RXBUF_RESET(_bf) do { \
    - (_bf)->bf_stale = false; \
    - } while (0)
    -
    /**
    * enum buffer_type - Buffer type flags
    *
    @@ -317,6 +313,7 @@ struct ath_rx {
    struct ath_descdma rxdma;
    struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX];

    + struct ath_buf *buf_hold;
    struct sk_buff *frag;

    u32 ampdu_ref;
    --- a/drivers/net/wireless/ath/ath9k/recv.c
    +++ b/drivers/net/wireless/ath/ath9k/recv.c
    @@ -42,8 +42,6 @@ static void ath_rx_buf_link(struct ath_s
    struct ath_desc *ds;
    struct sk_buff *skb;

    - ATH_RXBUF_RESET(bf);
    -
    ds = bf->bf_desc;
    ds->ds_link = 0; /* link to null */
    ds->ds_data = bf->bf_buf_addr;
    @@ -70,6 +68,14 @@ static void ath_rx_buf_link(struct ath_s
    sc->rx.rxlink = &ds->ds_link;
    }

    +static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_buf *bf)
    +{
    + if (sc->rx.buf_hold)
    + ath_rx_buf_link(sc, sc->rx.buf_hold);
    +
    + sc->rx.buf_hold = bf;
    +}
    +
    static void ath_setdefantenna(struct ath_softc *sc, u32 antenna)
    {
    /* XXX block beacon interrupts */
    @@ -117,7 +123,6 @@ static bool ath_rx_edma_buf_link(struct

    skb = bf->bf_mpdu;

    - ATH_RXBUF_RESET(bf);
    memset(skb->data, 0, ah->caps.rx_status_len);
    dma_sync_single_for_device(sc->dev, bf->bf_buf_addr,
    ah->caps.rx_status_len, DMA_TO_DEVICE);
    @@ -432,6 +437,7 @@ int ath_startrecv(struct ath_softc *sc)
    if (list_empty(&sc->rx.rxbuf))
    goto start_recv;

    + sc->rx.buf_hold = NULL;
    sc->rx.rxlink = NULL;
    list_for_each_entry_safe(bf, tbf, &sc->rx.rxbuf, list) {
    ath_rx_buf_link(sc, bf);
    @@ -677,6 +683,9 @@ static struct ath_buf *ath_get_next_rx_b
    }

    bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list);
    + if (bf == sc->rx.buf_hold)
    + return NULL;
    +
    ds = bf->bf_desc;

    /*
    @@ -1375,7 +1384,7 @@ requeue:
    if (edma) {
    ath_rx_edma_buf_link(sc, qtype);
    } else {
    - ath_rx_buf_link(sc, bf);
    + ath_rx_buf_relink(sc, bf);
    ath9k_hw_rxena(ah);
    }
    } while (1);



    \
     
     \ /
      Last update: 2013-09-25 03:01    [W:2.544 / U:0.028 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site