NetBSD Problem Report #58049
From www@netbsd.org Mon Mar 18 12:59:47 2024
Return-Path: <www@netbsd.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))
(Client CN "mail.NetBSD.org", Issuer "mail.NetBSD.org CA" (not verified))
by mollari.NetBSD.org (Postfix) with ESMTPS id 8F8EB1A9239
for <gnats-bugs@gnats.NetBSD.org>; Mon, 18 Mar 2024 12:59:47 +0000 (UTC)
Message-Id: <20240318125946.2AF131A923A@mollari.NetBSD.org>
Date: Mon, 18 Mar 2024 12:59:46 +0000 (UTC)
From: isaki@pastel-flower.jp
Reply-To: isaki@pastel-flower.jp
To: gnats-bugs@NetBSD.org
Subject: vioif(4) panics if the virtq size is too small
X-Send-Pr-Version: www-1.0
>Number: 58049
>Category: kern
>Synopsis: vioif(4) panics if the virtq size is too small
>Confidential: no
>Severity: serious
>Priority: low
>Responsible: isaki
>State: closed
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Mon Mar 18 13:00:00 +0000 2024
>Closed-Date: Thu Mar 21 12:38:58 +0000 2024
>Last-Modified: Thu Mar 21 12:38:58 +0000 2024
>Originator: Tetsuya Isaki
>Release: NetBSD-current around 20240309
>Organization:
>Environment:
NetBSD 10.99.10 virt68k
>Description:
vioif(4) panics on packet sending, if the virtq size is too small.
When vioif(4) stores a fragmented mbuf packet to TX VirtQ, it uses one
VirtQ descriptor per (probably) one mbuf fragment. In other words,
if a mbuf consists of 10 fragments, it consumes 10+1 VirtQ descriptors
(additional one descriptor is a virtio-net header).
If the maximum number of VirtQ descriptors that the device provides
(= QUEUE_NUM) is less than that, for example 8,
"KASSERT(nsegs <= vq->vq_num)" in virtio_enqueue_reserve()
(at virtio.c:1073) fires. This is what I encountered.
The virtio_enqueue_reserve() is called
from vioif_net_enqueue()
from vioif_net_enqueue_tx()
from vioif_send_common_locked().
By the way, if the cause of panic is only that the packet (especially
non jumbo packet, at most 1500 bytes) was divided into too many
fragments, I think it would be good enough to defragment the mbuf.
How about the attached patch? It works to me.
--- src/sys/dev/pci/if_vioif.c
+++ src/sys/dev/pci/if_vioif.c
@@ -1993,6 +1993,8 @@ vioif_send_common_locked(struct ifnet *ifp, struct vioif_netqueue *netq,
for (;;) {
int slot, r;
+ bool need_defrag;
+
r = virtio_enqueue_prep(vsc, vq, &slot);
if (r == EAGAIN) {
txc->txc_no_free_slots = true;
@@ -2014,8 +2016,17 @@ vioif_send_common_locked(struct ifnet *ifp, struct vioif_netqueue *netq,
map = &netq->netq_maps[slot];
KASSERT(map->vnm_mbuf == NULL);
+ need_defrag = false;
r = vioif_net_load_mbuf(vsc, map, m, BUS_DMA_WRITE);
if (r != 0) {
+ need_defrag = true;
+ } else if (map->vnm_mbuf_map->dm_nsegs >= vq->vq_maxnsegs) {
+ /* loaded but too many segments for VirtQ */
+ vioif_net_unload_mbuf(vsc, map);
+ need_defrag = true;
+ }
+
+ if (need_defrag) {
/* maybe just too fragmented */
struct mbuf *newm;
Under normal conditions (like QEMU), the patch only increases one more comparison per each sending. I think there will be no demerit on
practical emulators.
(I have no idea about jumbo packet)
>How-To-Repeat:
I couldn't find how do I configure virtq-tx size on QEMU. QEMU looks
to use enough large size (probably 1024) always(?), so it's difficult
to reproduce that on QEMU.
pkgsrc/emulators/nono has such small VirtQ at this point (ver 0.7.0)
(for testing the device itself and for my learning...).
Install pkgsrc/emulators/nono(=0.7.0)
Install NetBSD/virt68k on it.
Run virt68k on nono.
Login from remote host using rsh, and then type "ls -l /usr/lib"
(not "rsh hostname ls -l /usr/lib" from remote).
It can 100% reproduce.
>Fix:
See above.
>Release-Note:
>Audit-Trail:
From: Jason Thorpe <thorpej@me.com>
To: Tetsuya Isaki <isaki@pastel-flower.jp>
Cc: kern-bug-people@netbsd.org,
gnats-admin@netbsd.org,
netbsd-bugs@netbsd.org,
gnats-bugs@netbsd.org
Subject: Re: kern/58049: vioif(4) panics if the virtq size is too small
Date: Mon, 18 Mar 2024 08:51:45 -0700
> On Mar 18, 2024, at 6:00 AM, isaki@pastel-flower.jp wrote:
>=20
> How about the attached patch? It works to me.
This looks mostly fine but I have one question...
> --- src/sys/dev/pci/if_vioif.c
> +++ src/sys/dev/pci/if_vioif.c
> @@ -1993,6 +1993,8 @@ vioif_send_common_locked(struct ifnet *ifp, =
struct vioif_netqueue *netq,
>=20
> for (;;) {
> int slot, r;
> + bool need_defrag;
> +
> r =3D virtio_enqueue_prep(vsc, vq, &slot);
> if (r =3D=3D EAGAIN) {
> txc->txc_no_free_slots =3D true;
> @@ -2014,8 +2016,17 @@ vioif_send_common_locked(struct ifnet *ifp, =
struct vioif_netqueue *netq,
> map =3D &netq->netq_maps[slot];
> KASSERT(map->vnm_mbuf =3D=3D NULL);
>=20
> + need_defrag =3D false;
> r =3D vioif_net_load_mbuf(vsc, map, m, BUS_DMA_WRITE);
> if (r !=3D 0) {
> + need_defrag =3D true;
> + } else if (map->vnm_mbuf_map->dm_nsegs >=3D vq->vq_maxnsegs) {
> + /* loaded but too many segments for VirtQ */
When the DMA map is created, do we not yet know the max number of =
segments? Or is the problem that different queues might have different =
segment counts and the DMA maps are shared between them? In a perfect =
world, this extra test isn't needed because the bus_dma back-end honors =
the device's maximum segment count.
-- thorpej
From: Tetsuya Isaki <isaki@pastel-flower.jp>
To: Jason Thorpe <thorpej@me.com>
Cc: kern-bug-people@netbsd.org,
gnats-admin@netbsd.org,
netbsd-bugs@netbsd.org,
gnats-bugs@netbsd.org
Subject: Re: kern/58049: vioif(4) panics if the virtq size is too small
Date: Wed, 20 Mar 2024 12:44:25 +0900
At Mon, 18 Mar 2024 08:51:45 -0700,
Jason Thorpe wrote:
> When the DMA map is created, do we not yet know the max number of
> segments? Or is the problem that different queues might have
> different segment counts and the DMA maps are shared between them?
> In a perfect world, this extra test isn't needed because the bus_dma
> back-end honors the device's maximum segment count.
Thank you for comment.
I have found the cause of this problem. This is the revised patch.
--- src/sys/dev/pci/if_vioif.c
+++ src/sys/dev/pci/if_vioif.c
@@ -1280,12 +1280,14 @@ vioif_alloc_mems(struct vioif_softc *sc)
struct virtio_net_hdr *hdrs;
int dir;
+ int nsegs;
dir = VIOIF_NETQ_DIR(qid);
netq = &sc->sc_netqs[qid];
vq_num = netq->netq_vq->vq_num;
maps = netq->netq_maps;
hdrs = netq->netq_maps_kva;
+ nsegs = uimin(dmaparams[dir].dma_nsegs, vq_num - 1/*hdr*/);
for (i = 0; i < vq_num; i++) {
maps[i].vnm_hdr = &hdrs[i];
@@ -1297,7 +1299,7 @@ vioif_alloc_mems(struct vioif_softc *sc)
goto err_reqs;
r = vioif_dmamap_create(sc, &maps[i].vnm_mbuf_map,
- dmaparams[dir].dma_size, dmaparams[dir].dma_nsegs,
+ dmaparams[dir].dma_size, nsegs,
dmaparams[dir].msg_payload);
if (r != 0)
goto err_reqs;
vioif_net_load_mbuf() (in vioif_send_common_locked()) loaded more
than 8 segments(fragments) despite (my) virtio queue size was 8.
So I tried to defragment in the previous patch. But it was not
correct as you pointed out.
This is because vioif_alloc_mems() told bus_dma that it was able to
handle up to hardcoded VIRTIO_NET_TX_MAXNSEGS (=16) segments during
initialization, despite my virtio queue size was 8(!).
If the virtq size is less than or equal to VIRTIO_NET_TX_MAXNSEGS,
the max number of dmamap segments for payload must be virtq size - 1.
Thanks,
---
Tetsuya Isaki <isaki@pastel-flower.jp / isaki@NetBSD.org>
From: Jason Thorpe <thorpej@me.com>
To: Tetsuya Isaki <isaki@pastel-flower.jp>
Cc: kern-bug-people@netbsd.org,
gnats-admin@netbsd.org,
netbsd-bugs@netbsd.org,
gnats-bugs@netbsd.org
Subject: Re: kern/58049: vioif(4) panics if the virtq size is too small
Date: Wed, 20 Mar 2024 04:41:55 -0700
> On Mar 19, 2024, at 8:44=E2=80=AFPM, Tetsuya Isaki =
<isaki@pastel-flower.jp> wrote:
>=20
> Thank you for comment.
> I have found the cause of this problem. This is the revised patch.
Looks good to me! Thanks for continuing to investigate after my =
comments!
-- thorpej
From: "Tetsuya Isaki" <isaki@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/58049 CVS commit: src/sys/dev/pci
Date: Thu, 21 Mar 2024 12:33:21 +0000
Module Name: src
Committed By: isaki
Date: Thu Mar 21 12:33:21 UTC 2024
Modified Files:
src/sys/dev/pci: if_vioif.c
Log Message:
Ensure that the number of bus_dma segments doesn't exceed VirtIO queue size.
This fixes reproducible panics when the host's VirtIO queue size is too small,
less than or equal to VIRTIO_NET_TX_MAXNSEGS(=16).
PR kern/58049.
To generate a diff of this commit:
cvs rdiff -u -r1.110 -r1.111 src/sys/dev/pci/if_vioif.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Responsible-Changed-From-To: kern-bug-people->isaki
Responsible-Changed-By: isaki@NetBSD.org
Responsible-Changed-When: Thu, 21 Mar 2024 12:38:58 +0000
Responsible-Changed-Why:
State-Changed-From-To: open->closed
State-Changed-By: isaki@NetBSD.org
State-Changed-When: Thu, 21 Mar 2024 12:38:58 +0000
State-Changed-Why:
Committed.
Thank you too for your reviewing, thorpej@!
>Unformatted:
(Contact us)
$NetBSD: query-full-pr,v 1.47 2022/09/11 19:34:41 kim Exp $
$NetBSD: gnats_config.sh,v 1.9 2014/08/02 14:16:04 spz Exp $
Copyright © 1994-2024
The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.