diff mbox series

[net] ionic: fix kernel panic due to multi-buffer handling

Message ID 20240618041412.3184919-1-ap420073@gmail.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series [net] ionic: fix kernel panic due to multi-buffer handling | expand

Checks

Context Check Description
netdev/series_format success Single patches do not need cover letters
netdev/tree_selection success Clearly marked for net
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag present in non-next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 8 this patch: 8
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers warning 5 maintainers not CCed: daniel@iogearbox.net john.fastabend@gmail.com hawk@kernel.org ast@kernel.org bpf@vger.kernel.org
netdev/build_clang success Errors and warnings before: 863 this patch: 863
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success Fixes tag looks correct
netdev/build_allmodconfig_warn success Errors and warnings before: 863 this patch: 863
netdev/checkpatch warning WARNING: line length of 81 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
netdev/contest success net-next-2024-06-18--12-00 (tests: 654)

Commit Message

Taehee Yoo June 18, 2024, 4:14 a.m. UTC
Currently, the ionic_run_xdp() doesn't handle multi-buffer packets
properly for XDP_TX and XDP_REDIRECT.
When a jumbo frame is received, the ionic_run_xdp() first makes xdp
frame with all necessary pages in the rx descriptor.
And if the action is either XDP_TX or XDP_REDIRECT, it should unmap
dma-mapping and reset page pointer to NULL for all pages, not only the
first page.
But it doesn't for SG pages. So, SG pages unexpectedly will be reused.
It eventually causes kernel panic.

Oops: general protection fault, probably for non-canonical address 0x504f4e4dbebc64ff: 0000 [#1] PREEMPT SMP NOPTI
CPU: 3 PID: 0 Comm: swapper/3 Not tainted 6.10.0-rc3+ #25
RIP: 0010:xdp_return_frame+0x42/0x90
Code: 01 75 12 5b 4c 89 e6 5d 31 c9 41 5c 31 d2 41 5d e9 73 fd ff ff 44 8b 6b 20 0f b7 43 0a 49 81 ed 68 01 00 00 49 29 c5 49 01 fd <41> 80 7d0
RSP: 0018:ffff99d00122ce08 EFLAGS: 00010202
RAX: 0000000000005453 RBX: ffff8d325f904000 RCX: 0000000000000001
RDX: 00000000670e1000 RSI: 000000011f90d000 RDI: 504f4e4d4c4b4a49
RBP: ffff99d003907740 R08: 0000000000000000 R09: 0000000000000000
R10: 000000011f90d000 R11: 0000000000000000 R12: ffff8d325f904010
R13: 504f4e4dbebc64fd R14: ffff8d3242b070c8 R15: ffff99d0039077c0
FS:  0000000000000000(0000) GS:ffff8d399f780000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f41f6c85e38 CR3: 000000037ac30000 CR4: 00000000007506f0
PKRU: 55555554
Call Trace:
 <IRQ>
 ? die_addr+0x33/0x90
 ? exc_general_protection+0x251/0x2f0
 ? asm_exc_general_protection+0x22/0x30
 ? xdp_return_frame+0x42/0x90
 ionic_tx_clean+0x211/0x280 [ionic 15881354510e6a9c655c59c54812b319ed2cd015]
 ionic_tx_cq_service+0xd3/0x210 [ionic 15881354510e6a9c655c59c54812b319ed2cd015]
 ionic_txrx_napi+0x41/0x1b0 [ionic 15881354510e6a9c655c59c54812b319ed2cd015]
 __napi_poll.constprop.0+0x29/0x1b0
 net_rx_action+0x2c4/0x350
 handle_softirqs+0xf4/0x320
 irq_exit_rcu+0x78/0xa0
 common_interrupt+0x77/0x90

Fixes: 5377805dc1c0 ("ionic: implement xdp frags support")
Signed-off-by: Taehee Yoo <ap420073@gmail.com>
---

In the XDP_DROP and XDP_ABORTED path, the ionic_rx_page_free() is called
to free page and unmap dma-mapping.
It resets only the first page pointer to NULL.
DROP/ABORTED path looks like it also has the same problem, but it's not.
Because all pages in the XDP_DROP/ABORTED path are not used anywhere it
can be reused without any free and unmap.
So, it's okay to remove the function in the xdp path.

I will send a separated patch for removing ionic_rx_page_free() in the
xdp path.

 .../net/ethernet/pensando/ionic/ionic_txrx.c  | 26 +++++++++++++++----
 1 file changed, 21 insertions(+), 5 deletions(-)

Comments

Nelson, Shannon June 18, 2024, 5:40 p.m. UTC | #1
On 6/17/2024 9:14 PM, Taehee Yoo wrote:
> 
> Currently, the ionic_run_xdp() doesn't handle multi-buffer packets
> properly for XDP_TX and XDP_REDIRECT.
> When a jumbo frame is received, the ionic_run_xdp() first makes xdp
> frame with all necessary pages in the rx descriptor.
> And if the action is either XDP_TX or XDP_REDIRECT, it should unmap
> dma-mapping and reset page pointer to NULL for all pages, not only the
> first page.
> But it doesn't for SG pages. So, SG pages unexpectedly will be reused.
> It eventually causes kernel panic.
> 
> Oops: general protection fault, probably for non-canonical address 0x504f4e4dbebc64ff: 0000 [#1] PREEMPT SMP NOPTI
> CPU: 3 PID: 0 Comm: swapper/3 Not tainted 6.10.0-rc3+ #25
> RIP: 0010:xdp_return_frame+0x42/0x90
> Code: 01 75 12 5b 4c 89 e6 5d 31 c9 41 5c 31 d2 41 5d e9 73 fd ff ff 44 8b 6b 20 0f b7 43 0a 49 81 ed 68 01 00 00 49 29 c5 49 01 fd <41> 80 7d0
> RSP: 0018:ffff99d00122ce08 EFLAGS: 00010202
> RAX: 0000000000005453 RBX: ffff8d325f904000 RCX: 0000000000000001
> RDX: 00000000670e1000 RSI: 000000011f90d000 RDI: 504f4e4d4c4b4a49
> RBP: ffff99d003907740 R08: 0000000000000000 R09: 0000000000000000
> R10: 000000011f90d000 R11: 0000000000000000 R12: ffff8d325f904010
> R13: 504f4e4dbebc64fd R14: ffff8d3242b070c8 R15: ffff99d0039077c0
> FS:  0000000000000000(0000) GS:ffff8d399f780000(0000) knlGS:0000000000000000
> CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> CR2: 00007f41f6c85e38 CR3: 000000037ac30000 CR4: 00000000007506f0
> PKRU: 55555554
> Call Trace:
>   <IRQ>
>   ? die_addr+0x33/0x90
>   ? exc_general_protection+0x251/0x2f0
>   ? asm_exc_general_protection+0x22/0x30
>   ? xdp_return_frame+0x42/0x90
>   ionic_tx_clean+0x211/0x280 [ionic 15881354510e6a9c655c59c54812b319ed2cd015]
>   ionic_tx_cq_service+0xd3/0x210 [ionic 15881354510e6a9c655c59c54812b319ed2cd015]
>   ionic_txrx_napi+0x41/0x1b0 [ionic 15881354510e6a9c655c59c54812b319ed2cd015]
>   __napi_poll.constprop.0+0x29/0x1b0
>   net_rx_action+0x2c4/0x350
>   handle_softirqs+0xf4/0x320
>   irq_exit_rcu+0x78/0xa0
>   common_interrupt+0x77/0x90
> 
> Fixes: 5377805dc1c0 ("ionic: implement xdp frags support")
> Signed-off-by: Taehee Yoo <ap420073@gmail.com>
> ---
> 
> In the XDP_DROP and XDP_ABORTED path, the ionic_rx_page_free() is called
> to free page and unmap dma-mapping.
> It resets only the first page pointer to NULL.
> DROP/ABORTED path looks like it also has the same problem, but it's not.
> Because all pages in the XDP_DROP/ABORTED path are not used anywhere it
> can be reused without any free and unmap.
> So, it's okay to remove the function in the xdp path.
> 
> I will send a separated patch for removing ionic_rx_page_free() in the
> xdp path.
> 
>   .../net/ethernet/pensando/ionic/ionic_txrx.c  | 26 +++++++++++++++----
>   1 file changed, 21 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c
> index 2427610f4306..792e530e3281 100644
> --- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c
> +++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c
> @@ -487,14 +487,13 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats,
>                            struct ionic_buf_info *buf_info,
>                            int len)
>   {
> +       int remain_len, frag_len, i, err = 0;
> +       struct skb_shared_info *sinfo;
>          u32 xdp_action = XDP_ABORTED;
>          struct xdp_buff xdp_buf;
>          struct ionic_queue *txq;
>          struct netdev_queue *nq;
>          struct xdp_frame *xdpf;
> -       int remain_len;
> -       int frag_len;
> -       int err = 0;

There's no need to change these 3 declarations.

> 
>          xdp_init_buff(&xdp_buf, IONIC_PAGE_SIZE, rxq->xdp_rxq_info);
>          frag_len = min_t(u16, len, IONIC_XDP_MAX_LINEAR_MTU + VLAN_ETH_HLEN);
> @@ -513,7 +512,6 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats,
>           */
>          remain_len = len - frag_len;
>          if (remain_len) {
> -               struct skb_shared_info *sinfo;
>                  struct ionic_buf_info *bi;
>                  skb_frag_t *frag;
> 
> @@ -576,7 +574,6 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats,
> 
>                  dma_unmap_page(rxq->dev, buf_info->dma_addr,
>                                 IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
> -

You can leave the whitespace alone

>                  err = ionic_xdp_post_frame(txq, xdpf, XDP_TX,
>                                             buf_info->page,
>                                             buf_info->page_offset,
> @@ -587,12 +584,22 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats,
>                          goto out_xdp_abort;
>                  }
>                  buf_info->page = NULL;
> +               if (xdp_frame_has_frags(xdpf)) {

You could use xdp_buff_has_frags(&xdp_buf) instead, or just drop this 
check and let nr_frags value handle it in the for-loop test, as long as 
you init sinfo->nr_frags = 0 at the start.


> +                       for (i = 0; i < sinfo->nr_frags; i++) {
> +                               buf_info++;
> +                               dma_unmap_page(rxq->dev, buf_info->dma_addr,
> +                                              IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
> +                               buf_info->page = NULL;
> +                       }
> +               }
> +
>                  stats->xdp_tx++;
> 
>                  /* the Tx completion will free the buffers */
>                  break;
> 
>          case XDP_REDIRECT:
> +               xdpf = xdp_convert_buff_to_frame(&xdp_buf);
>                  /* unmap the pages before handing them to a different device */
>                  dma_unmap_page(rxq->dev, buf_info->dma_addr,
>                                 IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
> @@ -603,6 +610,15 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats,
>                          goto out_xdp_abort;
>                  }
>                  buf_info->page = NULL;
> +               if (xdp_frame_has_frags(xdpf)) {

If you use xdp_buff_has_frags() then you would not have to waste time 
with xdp_convert_buff_to_frame().  Or, again, if you init nr_flags at 
the start then you could skip the test altogether and let the for-loop 
test deal with it.

> +                       for (i = 0; i < sinfo->nr_frags; i++) {
> +                               buf_info++;
> +                               dma_unmap_page(rxq->dev, buf_info->dma_addr,
> +                                              IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
> +                               buf_info->page = NULL;
> +                       }
> +               }
> +

Since this is repeated code, maybe build a little function, something like

static void ionic_xdp_rx_put_frags(struct ionic_queue *q,
				   struct ionic_buf_info *buf_info,
				   int nfrags)
{
	int i;

	for (i = 0; i < nfrags; i++) {
		buf_info++;
		dma_unmap_page(rxq->dev, buf_info->dma_addr,
			       IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
		buf_info->page = NULL;
	}
}

and call this with
	ionic_xdp_rx_put_frags(rxq, buf_info, sinfo->nr_frags);


Meanwhile, before removing ionic_rx_page_free(), be sure to check that 
ionic_rx_fill() notices that there are useful pages already there that 
can be reused and doesn't end up leaking pages.

We have some draft patches for converting this all to page_pool, which I 
think takes care of this and some other messy bits.  We've been side 
tracked internally, but really need to get back to those.

Thanks,
sln

>                  rxq->xdp_flush = true;
>                  stats->xdp_redirect++;
>                  break;
> --
> 2.34.1
>
Taehee Yoo June 19, 2024, 7:34 a.m. UTC | #2
On Wed, Jun 19, 2024 at 2:40 AM Nelson, Shannon <shannon.nelson@amd.com> wrote:

Hi Nelson, Shannon,
Thank you for the review!


>
> On 6/17/2024 9:14 PM, Taehee Yoo wrote:
> >
> > Currently, the ionic_run_xdp() doesn't handle multi-buffer packets
> > properly for XDP_TX and XDP_REDIRECT.
> > When a jumbo frame is received, the ionic_run_xdp() first makes xdp
> > frame with all necessary pages in the rx descriptor.
> > And if the action is either XDP_TX or XDP_REDIRECT, it should unmap
> > dma-mapping and reset page pointer to NULL for all pages, not only the
> > first page.
> > But it doesn't for SG pages. So, SG pages unexpectedly will be reused.
> > It eventually causes kernel panic.
> >
> > Oops: general protection fault, probably for non-canonical address 0x504f4e4dbebc64ff: 0000 [#1] PREEMPT SMP NOPTI
> > CPU: 3 PID: 0 Comm: swapper/3 Not tainted 6.10.0-rc3+ #25
> > RIP: 0010:xdp_return_frame+0x42/0x90
> > Code: 01 75 12 5b 4c 89 e6 5d 31 c9 41 5c 31 d2 41 5d e9 73 fd ff ff 44 8b 6b 20 0f b7 43 0a 49 81 ed 68 01 00 00 49 29 c5 49 01 fd <41> 80 7d0
> > RSP: 0018:ffff99d00122ce08 EFLAGS: 00010202
> > RAX: 0000000000005453 RBX: ffff8d325f904000 RCX: 0000000000000001
> > RDX: 00000000670e1000 RSI: 000000011f90d000 RDI: 504f4e4d4c4b4a49
> > RBP: ffff99d003907740 R08: 0000000000000000 R09: 0000000000000000
> > R10: 000000011f90d000 R11: 0000000000000000 R12: ffff8d325f904010
> > R13: 504f4e4dbebc64fd R14: ffff8d3242b070c8 R15: ffff99d0039077c0
> > FS:  0000000000000000(0000) GS:ffff8d399f780000(0000) knlGS:0000000000000000
> > CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> > CR2: 00007f41f6c85e38 CR3: 000000037ac30000 CR4: 00000000007506f0
> > PKRU: 55555554
> > Call Trace:
> >   <IRQ>
> >   ? die_addr+0x33/0x90
> >   ? exc_general_protection+0x251/0x2f0
> >   ? asm_exc_general_protection+0x22/0x30
> >   ? xdp_return_frame+0x42/0x90
> >   ionic_tx_clean+0x211/0x280 [ionic 15881354510e6a9c655c59c54812b319ed2cd015]
> >   ionic_tx_cq_service+0xd3/0x210 [ionic 15881354510e6a9c655c59c54812b319ed2cd015]
> >   ionic_txrx_napi+0x41/0x1b0 [ionic 15881354510e6a9c655c59c54812b319ed2cd015]
> >   __napi_poll.constprop.0+0x29/0x1b0
> >   net_rx_action+0x2c4/0x350
> >   handle_softirqs+0xf4/0x320
> >   irq_exit_rcu+0x78/0xa0
> >   common_interrupt+0x77/0x90
> >
> > Fixes: 5377805dc1c0 ("ionic: implement xdp frags support")
> > Signed-off-by: Taehee Yoo <ap420073@gmail.com>
> > ---
> >
> > In the XDP_DROP and XDP_ABORTED path, the ionic_rx_page_free() is called
> > to free page and unmap dma-mapping.
> > It resets only the first page pointer to NULL.
> > DROP/ABORTED path looks like it also has the same problem, but it's not.
> > Because all pages in the XDP_DROP/ABORTED path are not used anywhere it
> > can be reused without any free and unmap.
> > So, it's okay to remove the function in the xdp path.
> >
> > I will send a separated patch for removing ionic_rx_page_free() in the
> > xdp path.
> >
> >   .../net/ethernet/pensando/ionic/ionic_txrx.c  | 26 +++++++++++++++----
> >   1 file changed, 21 insertions(+), 5 deletions(-)
> >
> > diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c
> > index 2427610f4306..792e530e3281 100644
> > --- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c
> > +++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c
> > @@ -487,14 +487,13 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats,
> >                            struct ionic_buf_info *buf_info,
> >                            int len)
> >   {
> > +       int remain_len, frag_len, i, err = 0;
> > +       struct skb_shared_info *sinfo;
> >          u32 xdp_action = XDP_ABORTED;
> >          struct xdp_buff xdp_buf;
> >          struct ionic_queue *txq;
> >          struct netdev_queue *nq;
> >          struct xdp_frame *xdpf;
> > -       int remain_len;
> > -       int frag_len;
> > -       int err = 0;
>
> There's no need to change these 3 declarations.

Okay, I will add a new declaration 'i', not change these declarations.

>
> >
> >          xdp_init_buff(&xdp_buf, IONIC_PAGE_SIZE, rxq->xdp_rxq_info);
> >          frag_len = min_t(u16, len, IONIC_XDP_MAX_LINEAR_MTU + VLAN_ETH_HLEN);
> > @@ -513,7 +512,6 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats,
> >           */
> >          remain_len = len - frag_len;
> >          if (remain_len) {
> > -               struct skb_shared_info *sinfo;
> >                  struct ionic_buf_info *bi;
> >                  skb_frag_t *frag;
> >
> > @@ -576,7 +574,6 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats,
> >
> >                  dma_unmap_page(rxq->dev, buf_info->dma_addr,
> >                                 IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
> > -
>
> You can leave the whitespace alone

Thanks, I will remove it :)

>
> >                  err = ionic_xdp_post_frame(txq, xdpf, XDP_TX,
> >                                             buf_info->page,
> >                                             buf_info->page_offset,
> > @@ -587,12 +584,22 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats,
> >                          goto out_xdp_abort;
> >                  }
> >                  buf_info->page = NULL;
> > +               if (xdp_frame_has_frags(xdpf)) {
>
> You could use xdp_buff_has_frags(&xdp_buf) instead, or just drop this
> check and let nr_frags value handle it in the for-loop test, as long as
> you init sinfo->nr_frags = 0 at the start.

Right, xdp_buff_has_frags() is not needed here indeed.
I will remove it, Thanks!

>
>
> > +                       for (i = 0; i < sinfo->nr_frags; i++) {
> > +                               buf_info++;
> > +                               dma_unmap_page(rxq->dev, buf_info->dma_addr,
> > +                                              IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
> > +                               buf_info->page = NULL;
> > +                       }
> > +               }
> > +
> >                  stats->xdp_tx++;
> >
> >                  /* the Tx completion will free the buffers */
> >                  break;
> >
> >          case XDP_REDIRECT:
> > +               xdpf = xdp_convert_buff_to_frame(&xdp_buf);
> >                  /* unmap the pages before handing them to a different device */
> >                  dma_unmap_page(rxq->dev, buf_info->dma_addr,
> >                                 IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
> > @@ -603,6 +610,15 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats,
> >                          goto out_xdp_abort;
> >                  }
> >                  buf_info->page = NULL;
> > +               if (xdp_frame_has_frags(xdpf)) {
>
> If you use xdp_buff_has_frags() then you would not have to waste time
> with xdp_convert_buff_to_frame().  Or, again, if you init nr_flags at
> the start then you could skip the test altogether and let the for-loop
> test deal with it.
>
> > +                       for (i = 0; i < sinfo->nr_frags; i++) {
> > +                               buf_info++;
> > +                               dma_unmap_page(rxq->dev, buf_info->dma_addr,
> > +                                              IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
> > +                               buf_info->page = NULL;
> > +                       }
> > +               }
> > +
>
> Since this is repeated code, maybe build a little function, something like
>
> static void ionic_xdp_rx_put_frags(struct ionic_queue *q,
>                                    struct ionic_buf_info *buf_info,
>                                    int nfrags)
> {
>         int i;
>
>         for (i = 0; i < nfrags; i++) {
>                 buf_info++;
>                 dma_unmap_page(rxq->dev, buf_info->dma_addr,
>                                IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
>                 buf_info->page = NULL;
>         }
> }
>
> and call this with
>         ionic_xdp_rx_put_frags(rxq, buf_info, sinfo->nr_frags);

It looks great, I will apply your suggestion.
I will send a v2 patch with the above change :)

>
>
> Meanwhile, before removing ionic_rx_page_free(), be sure to check that
> ionic_rx_fill() notices that there are useful pages already there that
> can be reused and doesn't end up leaking pages.
>
> We have some draft patches for converting this all to page_pool, which I
> think takes care of this and some other messy bits.  We've been side
> tracked internally, but really need to get back to those.
>

Oh, I also planned to send a patch for converting page_pool.
And at that patch, ionic_rx_page_free() was going to be removed.
But you're already preparing to send a patch for converting to page_pool,
so I will not send my page_pool patch. And I will test your page_pool patch.
I agree with the page_pool approach because I checked the
improvement performance and it makes the code a little bit simpler.
Thanks a lot for sharing your plan :)

> Thanks,
> sln
>
> >                  rxq->xdp_flush = true;
> >                  stats->xdp_redirect++;
> >                  break;
> > --
> > 2.34.1
> >

Thanks a lot!
Taehee Yoo
Dan Carpenter June 21, 2024, 6:48 a.m. UTC | #3
Hi Taehee,

kernel test robot noticed the following build warnings:

url:    https://github.com/intel-lab-lkp/linux/commits/Taehee-Yoo/ionic-fix-kernel-panic-due-to-multi-buffer-handling/20240618-121629
base:   net/main
patch link:    https://lore.kernel.org/r/20240618041412.3184919-1-ap420073%40gmail.com
patch subject: [PATCH net] ionic: fix kernel panic due to multi-buffer handling
config: x86_64-randconfig-161-20240620 (https://download.01.org/0day-ci/archive/20240621/202406211040.17BtI0lu-lkp@intel.com/config)
compiler: gcc-13 (Ubuntu 13.2.0-4ubuntu3) 13.2.0

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
| Closes: https://lore.kernel.org/r/202406211040.17BtI0lu-lkp@intel.com/

New smatch warnings:
drivers/net/ethernet/pensando/ionic/ionic_txrx.c:588 ionic_run_xdp() error: uninitialized symbol 'sinfo'.

Old smatch warnings:
drivers/net/ethernet/pensando/ionic/ionic_txrx.c:253 ionic_rx_build_skb() warn: variable dereferenced before check 'buf_info->page' (see line 237)
drivers/net/ethernet/pensando/ionic/ionic_txrx.c:614 ionic_run_xdp() error: uninitialized symbol 'sinfo'.

vim +/sinfo +588 drivers/net/ethernet/pensando/ionic/ionic_txrx.c

180e35cdf035d1 Shannon Nelson 2024-02-14  483  static bool ionic_run_xdp(struct ionic_rx_stats *stats,
180e35cdf035d1 Shannon Nelson 2024-02-14  484  			  struct net_device *netdev,
180e35cdf035d1 Shannon Nelson 2024-02-14  485  			  struct bpf_prog *xdp_prog,
180e35cdf035d1 Shannon Nelson 2024-02-14  486  			  struct ionic_queue *rxq,
180e35cdf035d1 Shannon Nelson 2024-02-14  487  			  struct ionic_buf_info *buf_info,
180e35cdf035d1 Shannon Nelson 2024-02-14  488  			  int len)
180e35cdf035d1 Shannon Nelson 2024-02-14  489  {
17cb07b96a5f58 Taehee Yoo     2024-06-18  490  	int remain_len, frag_len, i, err = 0;
17cb07b96a5f58 Taehee Yoo     2024-06-18  491  	struct skb_shared_info *sinfo;
180e35cdf035d1 Shannon Nelson 2024-02-14  492  	u32 xdp_action = XDP_ABORTED;
180e35cdf035d1 Shannon Nelson 2024-02-14  493  	struct xdp_buff xdp_buf;
8eeed8373e1cca Shannon Nelson 2024-02-14  494  	struct ionic_queue *txq;
8eeed8373e1cca Shannon Nelson 2024-02-14  495  	struct netdev_queue *nq;
8eeed8373e1cca Shannon Nelson 2024-02-14  496  	struct xdp_frame *xdpf;
180e35cdf035d1 Shannon Nelson 2024-02-14  497  
180e35cdf035d1 Shannon Nelson 2024-02-14  498  	xdp_init_buff(&xdp_buf, IONIC_PAGE_SIZE, rxq->xdp_rxq_info);
5377805dc1c02a Shannon Nelson 2024-02-14  499  	frag_len = min_t(u16, len, IONIC_XDP_MAX_LINEAR_MTU + VLAN_ETH_HLEN);
180e35cdf035d1 Shannon Nelson 2024-02-14  500  	xdp_prepare_buff(&xdp_buf, ionic_rx_buf_va(buf_info),
5377805dc1c02a Shannon Nelson 2024-02-14  501  			 XDP_PACKET_HEADROOM, frag_len, false);
180e35cdf035d1 Shannon Nelson 2024-02-14  502  
180e35cdf035d1 Shannon Nelson 2024-02-14  503  	dma_sync_single_range_for_cpu(rxq->dev, ionic_rx_buf_pa(buf_info),
f81da39bf4c0a5 Shannon Nelson 2024-02-14  504  				      XDP_PACKET_HEADROOM, len,
180e35cdf035d1 Shannon Nelson 2024-02-14  505  				      DMA_FROM_DEVICE);
180e35cdf035d1 Shannon Nelson 2024-02-14  506  
180e35cdf035d1 Shannon Nelson 2024-02-14  507  	prefetchw(&xdp_buf.data_hard_start);
180e35cdf035d1 Shannon Nelson 2024-02-14  508  
5377805dc1c02a Shannon Nelson 2024-02-14  509  	/*  We limit MTU size to one buffer if !xdp_has_frags, so
5377805dc1c02a Shannon Nelson 2024-02-14  510  	 *  if the recv len is bigger than one buffer
5377805dc1c02a Shannon Nelson 2024-02-14  511  	 *     then we know we have frag info to gather
5377805dc1c02a Shannon Nelson 2024-02-14  512  	 */
5377805dc1c02a Shannon Nelson 2024-02-14  513  	remain_len = len - frag_len;
5377805dc1c02a Shannon Nelson 2024-02-14  514  	if (remain_len) {
5377805dc1c02a Shannon Nelson 2024-02-14  515  		struct ionic_buf_info *bi;
5377805dc1c02a Shannon Nelson 2024-02-14  516  		skb_frag_t *frag;
5377805dc1c02a Shannon Nelson 2024-02-14  517  
5377805dc1c02a Shannon Nelson 2024-02-14  518  		bi = buf_info;
5377805dc1c02a Shannon Nelson 2024-02-14  519  		sinfo = xdp_get_shared_info_from_buff(&xdp_buf);
5377805dc1c02a Shannon Nelson 2024-02-14  520  		sinfo->nr_frags = 0;
5377805dc1c02a Shannon Nelson 2024-02-14  521  		sinfo->xdp_frags_size = 0;
5377805dc1c02a Shannon Nelson 2024-02-14  522  		xdp_buff_set_frags_flag(&xdp_buf);
5377805dc1c02a Shannon Nelson 2024-02-14  523  
5377805dc1c02a Shannon Nelson 2024-02-14  524  		do {
5377805dc1c02a Shannon Nelson 2024-02-14  525  			if (unlikely(sinfo->nr_frags >= MAX_SKB_FRAGS)) {
5377805dc1c02a Shannon Nelson 2024-02-14  526  				err = -ENOSPC;
5377805dc1c02a Shannon Nelson 2024-02-14  527  				goto out_xdp_abort;
5377805dc1c02a Shannon Nelson 2024-02-14  528  			}
5377805dc1c02a Shannon Nelson 2024-02-14  529  
5377805dc1c02a Shannon Nelson 2024-02-14  530  			frag = &sinfo->frags[sinfo->nr_frags];
5377805dc1c02a Shannon Nelson 2024-02-14  531  			sinfo->nr_frags++;
5377805dc1c02a Shannon Nelson 2024-02-14  532  			bi++;
5377805dc1c02a Shannon Nelson 2024-02-14  533  			frag_len = min_t(u16, remain_len, ionic_rx_buf_size(bi));
5377805dc1c02a Shannon Nelson 2024-02-14  534  			dma_sync_single_range_for_cpu(rxq->dev, ionic_rx_buf_pa(bi),
5377805dc1c02a Shannon Nelson 2024-02-14  535  						      0, frag_len, DMA_FROM_DEVICE);
5377805dc1c02a Shannon Nelson 2024-02-14  536  			skb_frag_fill_page_desc(frag, bi->page, 0, frag_len);
5377805dc1c02a Shannon Nelson 2024-02-14  537  			sinfo->xdp_frags_size += frag_len;
5377805dc1c02a Shannon Nelson 2024-02-14  538  			remain_len -= frag_len;
5377805dc1c02a Shannon Nelson 2024-02-14  539  
5377805dc1c02a Shannon Nelson 2024-02-14  540  			if (page_is_pfmemalloc(bi->page))
5377805dc1c02a Shannon Nelson 2024-02-14  541  				xdp_buff_set_frag_pfmemalloc(&xdp_buf);
5377805dc1c02a Shannon Nelson 2024-02-14  542  		} while (remain_len > 0);
5377805dc1c02a Shannon Nelson 2024-02-14  543  	}

sinfo uninitialized on else path.

5377805dc1c02a Shannon Nelson 2024-02-14  544  
180e35cdf035d1 Shannon Nelson 2024-02-14  545  	xdp_action = bpf_prog_run_xdp(xdp_prog, &xdp_buf);
180e35cdf035d1 Shannon Nelson 2024-02-14  546  
180e35cdf035d1 Shannon Nelson 2024-02-14  547  	switch (xdp_action) {
180e35cdf035d1 Shannon Nelson 2024-02-14  548  	case XDP_PASS:
180e35cdf035d1 Shannon Nelson 2024-02-14  549  		stats->xdp_pass++;
180e35cdf035d1 Shannon Nelson 2024-02-14  550  		return false;  /* false = we didn't consume the packet */
180e35cdf035d1 Shannon Nelson 2024-02-14  551  
180e35cdf035d1 Shannon Nelson 2024-02-14  552  	case XDP_DROP:
180e35cdf035d1 Shannon Nelson 2024-02-14  553  		ionic_rx_page_free(rxq, buf_info);
180e35cdf035d1 Shannon Nelson 2024-02-14  554  		stats->xdp_drop++;
180e35cdf035d1 Shannon Nelson 2024-02-14  555  		break;
180e35cdf035d1 Shannon Nelson 2024-02-14  556  
180e35cdf035d1 Shannon Nelson 2024-02-14  557  	case XDP_TX:
8eeed8373e1cca Shannon Nelson 2024-02-14  558  		xdpf = xdp_convert_buff_to_frame(&xdp_buf);
8eeed8373e1cca Shannon Nelson 2024-02-14  559  		if (!xdpf)
8eeed8373e1cca Shannon Nelson 2024-02-14  560  			goto out_xdp_abort;
8eeed8373e1cca Shannon Nelson 2024-02-14  561  
8eeed8373e1cca Shannon Nelson 2024-02-14  562  		txq = rxq->partner;
8eeed8373e1cca Shannon Nelson 2024-02-14  563  		nq = netdev_get_tx_queue(netdev, txq->index);
8eeed8373e1cca Shannon Nelson 2024-02-14  564  		__netif_tx_lock(nq, smp_processor_id());
8eeed8373e1cca Shannon Nelson 2024-02-14  565  		txq_trans_cond_update(nq);
8eeed8373e1cca Shannon Nelson 2024-02-14  566  
8eeed8373e1cca Shannon Nelson 2024-02-14  567  		if (netif_tx_queue_stopped(nq) ||
1937b7ab6bd6bd Brett Creeley  2024-02-29  568  		    !netif_txq_maybe_stop(q_to_ndq(netdev, txq),
061b9bedbef124 Brett Creeley  2024-02-29  569  					  ionic_q_space_avail(txq),
061b9bedbef124 Brett Creeley  2024-02-29  570  					  1, 1)) {
8eeed8373e1cca Shannon Nelson 2024-02-14  571  			__netif_tx_unlock(nq);
8eeed8373e1cca Shannon Nelson 2024-02-14  572  			goto out_xdp_abort;
8eeed8373e1cca Shannon Nelson 2024-02-14  573  		}
8eeed8373e1cca Shannon Nelson 2024-02-14  574  
8eeed8373e1cca Shannon Nelson 2024-02-14  575  		dma_unmap_page(rxq->dev, buf_info->dma_addr,
8eeed8373e1cca Shannon Nelson 2024-02-14  576  			       IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
1937b7ab6bd6bd Brett Creeley  2024-02-29  577  		err = ionic_xdp_post_frame(txq, xdpf, XDP_TX,
8eeed8373e1cca Shannon Nelson 2024-02-14  578  					   buf_info->page,
8eeed8373e1cca Shannon Nelson 2024-02-14  579  					   buf_info->page_offset,
8eeed8373e1cca Shannon Nelson 2024-02-14  580  					   true);
8eeed8373e1cca Shannon Nelson 2024-02-14  581  		__netif_tx_unlock(nq);
8eeed8373e1cca Shannon Nelson 2024-02-14  582  		if (err) {
8eeed8373e1cca Shannon Nelson 2024-02-14  583  			netdev_dbg(netdev, "tx ionic_xdp_post_frame err %d\n", err);
8eeed8373e1cca Shannon Nelson 2024-02-14  584  			goto out_xdp_abort;
8eeed8373e1cca Shannon Nelson 2024-02-14  585  		}
491aee894a08bc Taehee Yoo     2024-06-03  586  		buf_info->page = NULL;
17cb07b96a5f58 Taehee Yoo     2024-06-18  587  		if (xdp_frame_has_frags(xdpf)) {
17cb07b96a5f58 Taehee Yoo     2024-06-18 @588  			for (i = 0; i < sinfo->nr_frags; i++) {
                                                                                ^^^^^
Warning generated here

17cb07b96a5f58 Taehee Yoo     2024-06-18  589  				buf_info++;
17cb07b96a5f58 Taehee Yoo     2024-06-18  590  				dma_unmap_page(rxq->dev, buf_info->dma_addr,
17cb07b96a5f58 Taehee Yoo     2024-06-18  591  					       IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
17cb07b96a5f58 Taehee Yoo     2024-06-18  592  				buf_info->page = NULL;
17cb07b96a5f58 Taehee Yoo     2024-06-18  593  			}
17cb07b96a5f58 Taehee Yoo     2024-06-18  594  		}
17cb07b96a5f58 Taehee Yoo     2024-06-18  595  
8eeed8373e1cca Shannon Nelson 2024-02-14  596  		stats->xdp_tx++;
8eeed8373e1cca Shannon Nelson 2024-02-14  597  
8eeed8373e1cca Shannon Nelson 2024-02-14  598  		/* the Tx completion will free the buffers */
8eeed8373e1cca Shannon Nelson 2024-02-14  599  		break;
8eeed8373e1cca Shannon Nelson 2024-02-14  600  
180e35cdf035d1 Shannon Nelson 2024-02-14  601  	case XDP_REDIRECT:
17cb07b96a5f58 Taehee Yoo     2024-06-18  602  		xdpf = xdp_convert_buff_to_frame(&xdp_buf);
587fc3f0dceb08 Shannon Nelson 2024-02-14  603  		/* unmap the pages before handing them to a different device */
587fc3f0dceb08 Shannon Nelson 2024-02-14  604  		dma_unmap_page(rxq->dev, buf_info->dma_addr,
587fc3f0dceb08 Shannon Nelson 2024-02-14  605  			       IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
587fc3f0dceb08 Shannon Nelson 2024-02-14  606  
587fc3f0dceb08 Shannon Nelson 2024-02-14  607  		err = xdp_do_redirect(netdev, &xdp_buf, xdp_prog);
587fc3f0dceb08 Shannon Nelson 2024-02-14  608  		if (err) {
587fc3f0dceb08 Shannon Nelson 2024-02-14  609  			netdev_dbg(netdev, "xdp_do_redirect err %d\n", err);
587fc3f0dceb08 Shannon Nelson 2024-02-14  610  			goto out_xdp_abort;
587fc3f0dceb08 Shannon Nelson 2024-02-14  611  		}
587fc3f0dceb08 Shannon Nelson 2024-02-14  612  		buf_info->page = NULL;
17cb07b96a5f58 Taehee Yoo     2024-06-18  613  		if (xdp_frame_has_frags(xdpf)) {
17cb07b96a5f58 Taehee Yoo     2024-06-18  614  			for (i = 0; i < sinfo->nr_frags; i++) {
17cb07b96a5f58 Taehee Yoo     2024-06-18  615  				buf_info++;
17cb07b96a5f58 Taehee Yoo     2024-06-18  616  				dma_unmap_page(rxq->dev, buf_info->dma_addr,
17cb07b96a5f58 Taehee Yoo     2024-06-18  617  					       IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
17cb07b96a5f58 Taehee Yoo     2024-06-18  618  				buf_info->page = NULL;
17cb07b96a5f58 Taehee Yoo     2024-06-18  619  			}
17cb07b96a5f58 Taehee Yoo     2024-06-18  620  		}
17cb07b96a5f58 Taehee Yoo     2024-06-18  621  
587fc3f0dceb08 Shannon Nelson 2024-02-14  622  		rxq->xdp_flush = true;
587fc3f0dceb08 Shannon Nelson 2024-02-14  623  		stats->xdp_redirect++;
587fc3f0dceb08 Shannon Nelson 2024-02-14  624  		break;
587fc3f0dceb08 Shannon Nelson 2024-02-14  625  
180e35cdf035d1 Shannon Nelson 2024-02-14  626  	case XDP_ABORTED:
180e35cdf035d1 Shannon Nelson 2024-02-14  627  	default:
8eeed8373e1cca Shannon Nelson 2024-02-14  628  		goto out_xdp_abort;
8eeed8373e1cca Shannon Nelson 2024-02-14  629  	}
8eeed8373e1cca Shannon Nelson 2024-02-14  630  
8eeed8373e1cca Shannon Nelson 2024-02-14  631  	return true;
8eeed8373e1cca Shannon Nelson 2024-02-14  632  
8eeed8373e1cca Shannon Nelson 2024-02-14  633  out_xdp_abort:
180e35cdf035d1 Shannon Nelson 2024-02-14  634  	trace_xdp_exception(netdev, xdp_prog, xdp_action);
180e35cdf035d1 Shannon Nelson 2024-02-14  635  	ionic_rx_page_free(rxq, buf_info);
180e35cdf035d1 Shannon Nelson 2024-02-14  636  	stats->xdp_aborted++;
180e35cdf035d1 Shannon Nelson 2024-02-14  637  
180e35cdf035d1 Shannon Nelson 2024-02-14  638  	return true;
180e35cdf035d1 Shannon Nelson 2024-02-14  639  }
diff mbox series

Patch

diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c
index 2427610f4306..792e530e3281 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c
@@ -487,14 +487,13 @@  static bool ionic_run_xdp(struct ionic_rx_stats *stats,
 			  struct ionic_buf_info *buf_info,
 			  int len)
 {
+	int remain_len, frag_len, i, err = 0;
+	struct skb_shared_info *sinfo;
 	u32 xdp_action = XDP_ABORTED;
 	struct xdp_buff xdp_buf;
 	struct ionic_queue *txq;
 	struct netdev_queue *nq;
 	struct xdp_frame *xdpf;
-	int remain_len;
-	int frag_len;
-	int err = 0;
 
 	xdp_init_buff(&xdp_buf, IONIC_PAGE_SIZE, rxq->xdp_rxq_info);
 	frag_len = min_t(u16, len, IONIC_XDP_MAX_LINEAR_MTU + VLAN_ETH_HLEN);
@@ -513,7 +512,6 @@  static bool ionic_run_xdp(struct ionic_rx_stats *stats,
 	 */
 	remain_len = len - frag_len;
 	if (remain_len) {
-		struct skb_shared_info *sinfo;
 		struct ionic_buf_info *bi;
 		skb_frag_t *frag;
 
@@ -576,7 +574,6 @@  static bool ionic_run_xdp(struct ionic_rx_stats *stats,
 
 		dma_unmap_page(rxq->dev, buf_info->dma_addr,
 			       IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
-
 		err = ionic_xdp_post_frame(txq, xdpf, XDP_TX,
 					   buf_info->page,
 					   buf_info->page_offset,
@@ -587,12 +584,22 @@  static bool ionic_run_xdp(struct ionic_rx_stats *stats,
 			goto out_xdp_abort;
 		}
 		buf_info->page = NULL;
+		if (xdp_frame_has_frags(xdpf)) {
+			for (i = 0; i < sinfo->nr_frags; i++) {
+				buf_info++;
+				dma_unmap_page(rxq->dev, buf_info->dma_addr,
+					       IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
+				buf_info->page = NULL;
+			}
+		}
+
 		stats->xdp_tx++;
 
 		/* the Tx completion will free the buffers */
 		break;
 
 	case XDP_REDIRECT:
+		xdpf = xdp_convert_buff_to_frame(&xdp_buf);
 		/* unmap the pages before handing them to a different device */
 		dma_unmap_page(rxq->dev, buf_info->dma_addr,
 			       IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
@@ -603,6 +610,15 @@  static bool ionic_run_xdp(struct ionic_rx_stats *stats,
 			goto out_xdp_abort;
 		}
 		buf_info->page = NULL;
+		if (xdp_frame_has_frags(xdpf)) {
+			for (i = 0; i < sinfo->nr_frags; i++) {
+				buf_info++;
+				dma_unmap_page(rxq->dev, buf_info->dma_addr,
+					       IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
+				buf_info->page = NULL;
+			}
+		}
+
 		rxq->xdp_flush = true;
 		stats->xdp_redirect++;
 		break;