NetBSD-Bugs archive

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

kern/58049: vioif(4) panics if the virtq size is too small



>Number:         58049
>Category:       kern
>Synopsis:       vioif(4) panics if the virtq size is too small
>Confidential:   no
>Severity:       serious
>Priority:       low
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Mon Mar 18 13:00:00 +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.



Home | Main Index | Thread Index | Old Index