Search Linux Wireless

Re: [PATCH 01/10] wcn36xx: fix buffer commit logic on TX path

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, May 16, 2018 at 5:08 PM, Daniel Mack <daniel@xxxxxxxxxx> wrote:
> When wcn36xx_dxe_tx_frame() is entered while the device is still processing
> the queue asyncronously, we are racing against the firmware code with
> updates to the buffer descriptors. Presumably, the firmware scans the ring
> buffer that holds the descriptors and scans for a valid control descriptor,
> and then assumes that the next descriptor contains the payload. If, however,
> the control descriptor is marked valid, but the payload descriptor isn't,
> the packet is not sent out.
>
> Another issue with the current code is that is lacks memory barriers before
> descriptors are marked valid. This is important because the CPU may reorder
> writes to memory, even if it is allocated as coherent DMA area, and hence
> the device may see incompletely written data.
>
> To fix this, the code in wcn36xx_dxe_tx_frame() was restructured a bit so
> that the payload descriptor is made valid before the control descriptor.
> Memory barriers are added to ensure coherency of shared memory areas.
>
> Signed-off-by: Daniel Mack <daniel@xxxxxxxxxx>
> ---
>  drivers/net/wireless/ath/wcn36xx/dxe.c | 75 +++++++++++++++++-----------------
>  1 file changed, 38 insertions(+), 37 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c
> index bd2b946a65c9..3d1cf7dd1f8e 100644
> --- a/drivers/net/wireless/ath/wcn36xx/dxe.c
> +++ b/drivers/net/wireless/ath/wcn36xx/dxe.c
> @@ -642,8 +642,8 @@ int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
>                          struct sk_buff *skb,
>                          bool is_low)
>  {
> -       struct wcn36xx_dxe_ctl *ctl = NULL;
> -       struct wcn36xx_dxe_desc *desc = NULL;
> +       struct wcn36xx_dxe_desc *desc_bd, *desc_skb;
> +       struct wcn36xx_dxe_ctl *ctl_bd, *ctl_skb;
>         struct wcn36xx_dxe_ch *ch = NULL;
>         unsigned long flags;
>         int ret;
> @@ -651,74 +651,75 @@ int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
>         ch = is_low ? &wcn->dxe_tx_l_ch : &wcn->dxe_tx_h_ch;
>
>         spin_lock_irqsave(&ch->lock, flags);
> -       ctl = ch->head_blk_ctl;
> +       ctl_bd = ch->head_blk_ctl;
> +       ctl_skb = ctl_bd->next;
>
>         /*
>          * If skb is not null that means that we reached the tail of the ring
>          * hence ring is full. Stop queues to let mac80211 back off until ring
>          * has an empty slot again.
>          */
> -       if (NULL != ctl->next->skb) {
> +       if (NULL != ctl_skb->skb) {
>                 ieee80211_stop_queues(wcn->hw);
>                 wcn->queues_stopped = true;
>                 spin_unlock_irqrestore(&ch->lock, flags);
>                 return -EBUSY;
>         }
>
> -       ctl->skb = NULL;
> -       desc = ctl->desc;
> +       if (unlikely(ctl_skb->bd_cpu_addr)) {
> +               wcn36xx_err("bd_cpu_addr cannot be NULL for skb DXE\n");
> +               ret = -EINVAL;
> +               goto unlock;
> +       }
> +
> +       desc_bd = ctl_bd->desc;
> +       desc_skb = ctl_skb->desc;
> +
> +       ctl_bd->skb = NULL;
>
>         /* write buffer descriptor */
> -       memcpy(ctl->bd_cpu_addr, bd, sizeof(*bd));
> +       memcpy(ctl_bd->bd_cpu_addr, bd, sizeof(*bd));
>
>         /* Set source address of the BD we send */
> -       desc->src_addr_l = ctl->bd_phy_addr;
> -
> -       desc->dst_addr_l = ch->dxe_wq;
> -       desc->fr_len = sizeof(struct wcn36xx_tx_bd);
> -       desc->ctrl = ch->ctrl_bd;
> +       desc_bd->src_addr_l = ctl_bd->bd_phy_addr;
> +       desc_bd->dst_addr_l = ch->dxe_wq;
> +       desc_bd->fr_len = sizeof(struct wcn36xx_tx_bd);
>
>         wcn36xx_dbg(WCN36XX_DBG_DXE, "DXE TX\n");
>
>         wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC1 >>> ",
> -                        (char *)desc, sizeof(*desc));
> +                        (char *)desc_bd, sizeof(*desc_bd));
>         wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP,
> -                        "BD   >>> ", (char *)ctl->bd_cpu_addr,
> +                        "BD   >>> ", (char *)ctl_bd->bd_cpu_addr,
>                          sizeof(struct wcn36xx_tx_bd));
>
> -       /* Set source address of the SKB we send */
> -       ctl = ctl->next;
> -       desc = ctl->desc;
> -       if (ctl->bd_cpu_addr) {
> -               wcn36xx_err("bd_cpu_addr cannot be NULL for skb DXE\n");
> -               ret = -EINVAL;
> -               goto unlock;
> -       }
> -
> -       desc->src_addr_l = dma_map_single(wcn->dev,
> -                                         skb->data,
> -                                         skb->len,
> -                                         DMA_TO_DEVICE);
> -       if (dma_mapping_error(wcn->dev, desc->src_addr_l)) {
> +       desc_skb->src_addr_l = dma_map_single(wcn->dev,
> +                                             skb->data,
> +                                             skb->len,
> +                                             DMA_TO_DEVICE);
> +       if (dma_mapping_error(wcn->dev, desc_skb->src_addr_l)) {
>                 dev_err(wcn->dev, "unable to DMA map src_addr_l\n");
>                 ret = -ENOMEM;
>                 goto unlock;
>         }
>
> -       ctl->skb = skb;
> -       desc->dst_addr_l = ch->dxe_wq;
> -       desc->fr_len = ctl->skb->len;
> -
> -       /* set dxe descriptor to VALID */
> -       desc->ctrl = ch->ctrl_skb;
> +       ctl_skb->skb = skb;
> +       desc_skb->dst_addr_l = ch->dxe_wq;
> +       desc_skb->fr_len = ctl_skb->skb->len;
>
>         wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC2 >>> ",
> -                        (char *)desc, sizeof(*desc));
> +                        (char *)desc_skb, sizeof(*desc_skb));
>         wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "SKB   >>> ",
> -                        (char *)ctl->skb->data, ctl->skb->len);
> +                        (char *)ctl_skb->skb->data, ctl_skb->skb->len);
>
>         /* Move the head of the ring to the next empty descriptor */
> -        ch->head_blk_ctl = ctl->next;
> +        ch->head_blk_ctl = ctl_skb->next;
> +
> +       /* Commit all previous writes and set descriptors to VALID */
> +       wmb();
> +       desc_skb->ctrl = ch->ctrl_skb;
> +       wmb();
> +       desc_bd->ctrl = ch->ctrl_bd;
>
>         /*
>          * When connected and trying to send data frame chip can be in sleep
> --
> 2.14.3
>
>
> _______________________________________________
> wcn36xx mailing list
> wcn36xx@xxxxxxxxxxxxxxxxxxx
> http://lists.infradead.org/mailman/listinfo/wcn36xx

Acked-by: Ramon Fried <ramon.fried@xxxxxxxxxx>



[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Wireless Personal Area Network]     [Linux Bluetooth]     [Wireless Regulations]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Hiking]     [MIPS Linux]     [ARM Linux]     [Linux RAID]

  Powered by Linux