Message ID | 20210115092405.8081-1-pkshih@realtek.com |
---|---|
Headers | show |
Series | rtw88: improve TX performance in field | expand |
On Fri, Jan 15, 2021 at 1:26 AM Ping-Ke Shih <pkshih@realtek.com> wrote: > --- a/drivers/net/wireless/realtek/rtw88/pci.c > +++ b/drivers/net/wireless/realtek/rtw88/pci.c > @@ -935,16 +935,49 @@ static void rtw_pci_tx_isr(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci, > ring->r.rp = cur_rp; > } > > -static void rtw_pci_rx_isr(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci, > +static void rtw_pci_rx_isr(struct rtw_dev *rtwdev) > +{ > + struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; > + struct napi_struct *napi = &rtwpci->napi; > + > + napi_schedule(napi); I don't claim to be all that familiar with NAPI, but my understanding is that you are scheduling a NAPI poll, but immediately after you return here, you're re-enabling your RX interrupt. That doesn't sound like how NAPI is supposed to work -- you're supposed to wait to re-enable your interrupt until you're done with your polling function. Ref: https://wiki.linuxfoundation.org/networking/napi > +} ... > +static u32 rtw_pci_rx_napi(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci, > u8 hw_queue) ... Are you sure you don't want any locking in rtw_pci_rx_napi()? Previously, you held irq_lock for the entirety of rtw_pci_rx_isr(), but now all the RX work is being deferred to a NAPI context, without any additional lock. IIUC, that means you can be both handling RX and other ISR operations at the same time. Is that intentional? > +static int rtw_pci_napi_poll(struct napi_struct *napi, int budget) > +{ > + struct rtw_pci *rtwpci = container_of(napi, struct rtw_pci, napi); > + struct rtw_dev *rtwdev = container_of((void *)rtwpci, struct rtw_dev, > + priv); > + int work_done = 0; > + > + while (work_done < budget) { > + u32 work_done_once; > + > + work_done_once = rtw_pci_rx_napi(rtwdev, rtwpci, > + RTW_RX_QUEUE_MPDU); > + if (work_done_once == 0) > + break; > + work_done += work_done_once; > + } > + if (work_done < budget) { > + napi_complete_done(napi, work_done); > + /* When ISR happens during polling and before napi_complete > + * while no further data is received. Data on the dma_ring will > + * not be processed immediately. Check whether dma ring is > + * empty and perform napi_schedule accordingly. > + */ > + if (rtw_pci_get_hw_rx_ring_nr(rtwdev, rtwpci, NULL, NULL)) > + napi_schedule(napi); > + } > + > + return work_done; > +} > + > +static void rtw_pci_napi_init(struct rtw_dev *rtwdev) > +{ > + struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; > + > + init_dummy_netdev(&rtwpci->netdev); > + netif_napi_add(&rtwpci->netdev, &rtwpci->napi, rtw_pci_napi_poll, > + RTW_NAPI_WEIGHT_NUM); > + napi_enable(&rtwpci->napi); > +} ... > @@ -1547,6 +1624,8 @@ int rtw_pci_probe(struct pci_dev *pdev, > goto err_destroy_pci; > } > > + rtw_pci_napi_init(rtwdev); You're initializing NAPI after you've already established your ISR, and your ISR might start scheduling NAPI. Even if that's unlikely (because you haven't initiated any RX traffic yet), it seems like an ordering problem -- shouldn't you initialize the NAPI device, then set up the ISR, and only then call napi_enable()? Brian > + > return 0; > > err_destroy_pci:
> -----Original Message----- > From: Brian Norris [mailto:briannorris@chromium.org] > Sent: Saturday, January 23, 2021 6:58 AM > To: Pkshih > Cc: Yan-Hsuan Chuang; Kalle Valo; linux-wireless; Bernie Huang > Subject: Re: [PATCH v4 3/8] rtw88: add napi support > > On Fri, Jan 15, 2021 at 1:26 AM Ping-Ke Shih <pkshih@realtek.com> wrote: > > --- a/drivers/net/wireless/realtek/rtw88/pci.c > > +++ b/drivers/net/wireless/realtek/rtw88/pci.c > > @@ -935,16 +935,49 @@ static void rtw_pci_tx_isr(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci, > > ring->r.rp = cur_rp; > > } > > > > -static void rtw_pci_rx_isr(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci, > > +static void rtw_pci_rx_isr(struct rtw_dev *rtwdev) > > +{ > > + struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; > > + struct napi_struct *napi = &rtwpci->napi; > > + > > + napi_schedule(napi); > > I don't claim to be all that familiar with NAPI, but my understanding > is that you are scheduling a NAPI poll, but immediately after you > return here, you're re-enabling your RX interrupt. That doesn't sound > like how NAPI is supposed to work -- you're supposed to wait to > re-enable your interrupt until you're done with your polling function. > Ref: https://wiki.linuxfoundation.org/networking/napi > Will re-enable RX IMR until napi_poll function is done. > > +} > ... > > +static u32 rtw_pci_rx_napi(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci, > > u8 hw_queue) > ... > > Are you sure you don't want any locking in rtw_pci_rx_napi()? > Previously, you held irq_lock for the entirety of rtw_pci_rx_isr(), > but now all the RX work is being deferred to a NAPI context, without > any additional lock. IIUC, that means you can be both handling RX and > other ISR operations at the same time. Is that intentional? > irq_lock is used to protect TX ring->queue. The TX skb(s) are queued into the queue, and unlink the skb until TX_OK_ISR is received. So, RX doesn't need to hold this lock. > > +static int rtw_pci_napi_poll(struct napi_struct *napi, int budget) > > +{ > > + struct rtw_pci *rtwpci = container_of(napi, struct rtw_pci, napi); > > + struct rtw_dev *rtwdev = container_of((void *)rtwpci, struct rtw_dev, > > + priv); > > + int work_done = 0; > > + > > + while (work_done < budget) { > > + u32 work_done_once; > > + > > + work_done_once = rtw_pci_rx_napi(rtwdev, rtwpci, > > + RTW_RX_QUEUE_MPDU); > > + if (work_done_once == 0) > > + break; > > + work_done += work_done_once; > > + } > > + if (work_done < budget) { > > + napi_complete_done(napi, work_done); > > + /* When ISR happens during polling and before napi_complete > > + * while no further data is received. Data on the dma_ring will > > + * not be processed immediately. Check whether dma ring is > > + * empty and perform napi_schedule accordingly. > > + */ > > + if (rtw_pci_get_hw_rx_ring_nr(rtwdev, rtwpci, NULL, NULL)) > > + napi_schedule(napi); > > + } > > + > > + return work_done; > > +} > > + > > +static void rtw_pci_napi_init(struct rtw_dev *rtwdev) > > +{ > > + struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; > > + > > + init_dummy_netdev(&rtwpci->netdev); > > + netif_napi_add(&rtwpci->netdev, &rtwpci->napi, rtw_pci_napi_poll, > > + RTW_NAPI_WEIGHT_NUM); > > + napi_enable(&rtwpci->napi); > > +} > ... > > @@ -1547,6 +1624,8 @@ int rtw_pci_probe(struct pci_dev *pdev, > > goto err_destroy_pci; > > } > > > > + rtw_pci_napi_init(rtwdev); > > You're initializing NAPI after you've already established your ISR, > and your ISR might start scheduling NAPI. Even if that's unlikely > (because you haven't initiated any RX traffic yet), it seems like an > ordering problem -- shouldn't you initialize the NAPI device, then set > up the ISR, and only then call napi_enable()? > Will do it. Thanks for your advice. --- Ping-Ke
On Thu, Jan 28, 2021 at 1:45 AM Pkshih <pkshih@realtek.com> wrote: > > -----Original Message----- > > From: Brian Norris [mailto:briannorris@chromium.org] > > On Fri, Jan 15, 2021 at 1:26 AM Ping-Ke Shih <pkshih@realtek.com> wrote: > > > +static u32 rtw_pci_rx_napi(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci, > > > u8 hw_queue) > > ... > > > > Are you sure you don't want any locking in rtw_pci_rx_napi()? > > Previously, you held irq_lock for the entirety of rtw_pci_rx_isr(), > > but now all the RX work is being deferred to a NAPI context, without > > any additional lock. IIUC, that means you can be both handling RX and > > other ISR operations at the same time. Is that intentional? > > > > irq_lock is used to protect TX ring->queue. The TX skb(s) are queued into the > queue, and unlink the skb until TX_OK_ISR is received. So, RX doesn't need to > hold this lock. I could be misunderstanding your locking model, but IIUC, you're left with zero locking between NAPI RX and all other operations (H2C, link up/down -- including DMA free, etc.). irq_lock used to protect you from that. If I'm right, maybe it needs a rename and/or some additional comments. Brian
> -----Original Message----- > From: Brian Norris [mailto:briannorris@chromium.org] > Sent: Saturday, January 30, 2021 7:39 AM > To: Pkshih > Cc: Yan-Hsuan Chuang; Kalle Valo; linux-wireless; Bernie Huang > Subject: Re: [PATCH v4 3/8] rtw88: add napi support > > On Thu, Jan 28, 2021 at 1:45 AM Pkshih <pkshih@realtek.com> wrote: > > > -----Original Message----- > > > From: Brian Norris [mailto:briannorris@chromium.org] > > > On Fri, Jan 15, 2021 at 1:26 AM Ping-Ke Shih <pkshih@realtek.com> wrote: > > > > +static u32 rtw_pci_rx_napi(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci, > > > > u8 hw_queue) > > > ... > > > > > > Are you sure you don't want any locking in rtw_pci_rx_napi()? > > > Previously, you held irq_lock for the entirety of rtw_pci_rx_isr(), > > > but now all the RX work is being deferred to a NAPI context, without > > > any additional lock. IIUC, that means you can be both handling RX and > > > other ISR operations at the same time. Is that intentional? > > > > > > > irq_lock is used to protect TX ring->queue. The TX skb(s) are queued into the > > queue, and unlink the skb until TX_OK_ISR is received. So, RX doesn't need to > > hold this lock. > > I could be misunderstanding your locking model, but IIUC, you're left > with zero locking between NAPI RX and all other operations (H2C, link > up/down -- including DMA free, etc.). irq_lock used to protect you > from that. > Sorry, I'm wrong. I think irq_lock is used to protect not only TX ring->queue but also TX/RX rings. The RX ring rtwpci->rx_rings[RTW_RX_QUEUE_MPDU] is reset by rtw_pci_reset_buf_desc() when pci_stop(), and napi_poll() also uses it to know how many RX packets are needed to be received. Therefore, we plan to use irq_lock to protect napi_poll(), and then see if it affects performance. > If I'm right, maybe it needs a rename and/or some additional comments. > The original name and comment: /* Used for PCI TX queueing. */ spinlock_t irq_lock; Will change to /* Used for PCI TX/RX rings and TX queueing. */ spinlock_t irq_lock; --- Ping-Ke
On Mon, 2021-02-01 at 06:38 +0000, Pkshih wrote: > > -----Original Message----- > > From: Brian Norris [mailto:briannorris@chromium.org] > > Sent: Saturday, January 30, 2021 7:39 AM > > To: Pkshih > > Cc: Yan-Hsuan Chuang; Kalle Valo; linux-wireless; Bernie Huang > > Subject: Re: [PATCH v4 3/8] rtw88: add napi support > > > > On Thu, Jan 28, 2021 at 1:45 AM Pkshih <pkshih@realtek.com> wrote: > > > > -----Original Message----- > > > > From: Brian Norris [mailto:briannorris@chromium.org] > > > > On Fri, Jan 15, 2021 at 1:26 AM Ping-Ke Shih <pkshih@realtek.com> wrote: > > > > > +static u32 rtw_pci_rx_napi(struct rtw_dev *rtwdev, struct rtw_pci > *rtwpci, > > > > > u8 hw_queue) > > > > ... > > > > > > > > Are you sure you don't want any locking in rtw_pci_rx_napi()? > > > > Previously, you held irq_lock for the entirety of rtw_pci_rx_isr(), > > > > but now all the RX work is being deferred to a NAPI context, without > > > > any additional lock. IIUC, that means you can be both handling RX and > > > > other ISR operations at the same time. Is that intentional? > > > > > > > > > > irq_lock is used to protect TX ring->queue. The TX skb(s) are queued into > the > > > queue, and unlink the skb until TX_OK_ISR is received. So, RX doesn't need > to > > > hold this lock. > > > > I could be misunderstanding your locking model, but IIUC, you're left > > with zero locking between NAPI RX and all other operations (H2C, link > > up/down -- including DMA free, etc.). irq_lock used to protect you > > from that. > > > > Sorry, I'm wrong. I think irq_lock is used to protect not only TX ring->queue > but also TX/RX rings. The RX ring rtwpci->rx_rings[RTW_RX_QUEUE_MPDU] is reset > by rtw_pci_reset_buf_desc() when pci_stop(), and napi_poll() also uses it to > know how many RX packets are needed to be received. Therefore, we plan to > use irq_lock to protect napi_poll(), and then see if it affects performance. > I change my mind, because using irq_lock to protect napi_poll causes deadlock. I think that it's disallowed to hold a spin_lock_bh and call napi APIs that uses RCU lock. Then, I have another simple thinking -- enable NAPI only if interrupt is enabled. Other operations with RX ring are working only if interrupt is disabled. So, we don't need a lock to protect RX ring at all. The irq_lock is still used to protect TX ring/queue, and now it also used to protect switching IMR. Some comments are added to describe about this. Above is implemented in v5. --- Ping-Ke
On Mon, Feb 8, 2021 at 11:19 PM Pkshih <pkshih@realtek.com> wrote: > Then, I have another simple thinking -- enable NAPI only if interrupt is > enabled. Other operations with RX ring are working only if interrupt is > disabled. So, we don't need a lock to protect RX ring at all. That makes more sense; thanks for the update. > The irq_lock is still used to protect TX ring/queue, and now it also used > to protect switching IMR. Some comments are added to describe about this. > > Above is implemented in v5. I've taken a brief look, and that looks better. I'll likely provide my Reviewed-by there. Thanks, Brian