diff mbox series

[2/3] pc-bios/s390-ccw: Fix boot problem with virtio-net devices

Message ID 20250116115826.192047-3-thuth@redhat.com (mailing list archive)
State New
Headers show
Series pc-bios/s390-ccw: Fix problems related to network booting | expand

Commit Message

Thomas Huth Jan. 16, 2025, 11:58 a.m. UTC
When we are trying to boot from virtio-net devices, the
s390-ccw bios currently leaves the virtio-net device enabled
after using it. That means that the receiving virt queues will
continue to happily write incoming network packets into memory.
This can corrupt data of the following boot process. For example,
if you set up a second guest on a virtual network and create a
lot of broadcast traffic there, e.g. with:

 ping -i 0.02 -s 1400  -b 192.168.1.255

and then you try to boot a guest with two boot devices, a network
device first (which should not be bootable) and e.g. a bootable SCSI
CD second, then this guest will fail to load the kernel from the CD
image:

 $ qemu-system-s390x -m 2G -nographic -device virtio-scsi-ccw \
    -netdev tap,id=net0 -device virtio-net-ccw,netdev=net0,bootindex=1 \
    -drive if=none,file=test.iso,format=raw,id=cd1 \
    -device scsi-cd,drive=cd1,bootindex=2
 LOADPARM=[        ]

 Network boot device detected
 Network boot starting...
   Using MAC address: 52:54:00:12:34:56
   Requesting information via DHCP: done
   Using IPv4 address: 192.168.1.76
   Using TFTP server: 192.168.1.1
 Trying pxelinux.cfg files...
   TFTP error: ICMP ERROR "port unreachable"
   Receiving data:  0 KBytes
 Repeating TFTP read request...
   TFTP error: ICMP ERROR "port unreachable"
 Failed to load OS from network.
 Failed to IPL from this network!
 LOADPARM=[        ]

 Using virtio-scsi.

 ! virtio-scsi:setup:inquiry: response VS RESP=ff !
 ERROR: No suitable device for IPL. Halting...

We really have to shut up the virtio-net devices after we're not
using it anymore. The easiest way to do this is to simply reset
the device, so let's do that now.

Signed-off-by: Thomas Huth <thuth@redhat.com>
---
 pc-bios/s390-ccw/virtio.h     |  1 +
 pc-bios/s390-ccw/netmain.c    | 33 +++++++++++++++++++++++----------
 pc-bios/s390-ccw/virtio-net.c |  5 +++++
 3 files changed, 29 insertions(+), 10 deletions(-)

Comments

Jared Rossi Jan. 16, 2025, 2:14 p.m. UTC | #1
Reviewed-by: Jared Rossi <jrossi@linux.ibm.com>

On 1/16/25 6:58 AM, Thomas Huth wrote:
> When we are trying to boot from virtio-net devices, the
> s390-ccw bios currently leaves the virtio-net device enabled
> after using it. That means that the receiving virt queues will
> continue to happily write incoming network packets into memory.
> This can corrupt data of the following boot process. For example,
> if you set up a second guest on a virtual network and create a
> lot of broadcast traffic there, e.g. with:
>
>   ping -i 0.02 -s 1400  -b 192.168.1.255
>
> and then you try to boot a guest with two boot devices, a network
> device first (which should not be bootable) and e.g. a bootable SCSI
> CD second, then this guest will fail to load the kernel from the CD
> image:
>
>   $ qemu-system-s390x -m 2G -nographic -device virtio-scsi-ccw \
>      -netdev tap,id=net0 -device virtio-net-ccw,netdev=net0,bootindex=1 \
>      -drive if=none,file=test.iso,format=raw,id=cd1 \
>      -device scsi-cd,drive=cd1,bootindex=2
>   LOADPARM=[        ]
>
>   Network boot device detected
>   Network boot starting...
>     Using MAC address: 52:54:00:12:34:56
>     Requesting information via DHCP: done
>     Using IPv4 address: 192.168.1.76
>     Using TFTP server: 192.168.1.1
>   Trying pxelinux.cfg files...
>     TFTP error: ICMP ERROR "port unreachable"
>     Receiving data:  0 KBytes
>   Repeating TFTP read request...
>     TFTP error: ICMP ERROR "port unreachable"
>   Failed to load OS from network.
>   Failed to IPL from this network!
>   LOADPARM=[        ]
>
>   Using virtio-scsi.
>
>   ! virtio-scsi:setup:inquiry: response VS RESP=ff !
>   ERROR: No suitable device for IPL. Halting...
>
> We really have to shut up the virtio-net devices after we're not
> using it anymore. The easiest way to do this is to simply reset
> the device, so let's do that now.
>
> Signed-off-by: Thomas Huth <thuth@redhat.com>
> ---
>   pc-bios/s390-ccw/virtio.h     |  1 +
>   pc-bios/s390-ccw/netmain.c    | 33 +++++++++++++++++++++++----------
>   pc-bios/s390-ccw/virtio-net.c |  5 +++++
>   3 files changed, 29 insertions(+), 10 deletions(-)
>
> diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h
> index f13fa6f5fe..5c5e808a50 100644
> --- a/pc-bios/s390-ccw/virtio.h
> +++ b/pc-bios/s390-ccw/virtio.h
> @@ -278,5 +278,6 @@ int virtio_reset(VDev *vdev);
>   int virtio_setup_ccw(VDev *vdev);
>   
>   int virtio_net_init(void *mac_addr);
> +void virtio_net_deinit(void);
>   
>   #endif /* VIRTIO_H */
> diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c
> index e46e470db4..335ea9b63e 100644
> --- a/pc-bios/s390-ccw/netmain.c
> +++ b/pc-bios/s390-ccw/netmain.c
> @@ -153,19 +153,10 @@ static int tftp_load(filename_ip_t *fnip, void *buffer, int len)
>       return rc;
>   }
>   
> -static int net_init(filename_ip_t *fn_ip)
> +static int net_init_ip(filename_ip_t *fn_ip)
>   {
>       int rc;
>   
> -    memset(fn_ip, 0, sizeof(filename_ip_t));
> -
> -    rc = virtio_net_init(mac);
> -    if (rc < 0) {
> -        puts("Could not initialize network device");
> -        return -101;
> -    }
> -    fn_ip->fd = rc;
> -
>       printf("  Using MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",
>              mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
>   
> @@ -221,11 +212,33 @@ static int net_init(filename_ip_t *fn_ip)
>       return rc;
>   }
>   
> +static int net_init(filename_ip_t *fn_ip)
> +{
> +    int rc;
> +
> +    memset(fn_ip, 0, sizeof(filename_ip_t));
> +
> +    rc = virtio_net_init(mac);
> +    if (rc < 0) {
> +        puts("Could not initialize network device");
> +        return -101;
> +    }
> +    fn_ip->fd = rc;
> +
> +    rc = net_init_ip(fn_ip);
> +    if (rc < 0) {
> +        virtio_net_deinit();
> +    }
> +
> +    return rc;
> +}
> +
>   static void net_release(filename_ip_t *fn_ip)
>   {
>       if (fn_ip->ip_version == 4) {
>           dhcp_send_release(fn_ip->fd);
>       }
> +    virtio_net_deinit();
>   }
>   
>   /**
> diff --git a/pc-bios/s390-ccw/virtio-net.c b/pc-bios/s390-ccw/virtio-net.c
> index 578c89d0c5..301445bf97 100644
> --- a/pc-bios/s390-ccw/virtio-net.c
> +++ b/pc-bios/s390-ccw/virtio-net.c
> @@ -140,3 +140,8 @@ int recv(int fd, void *buf, int maxlen, int flags)
>   
>       return len;
>   }
> +
> +void virtio_net_deinit(void)
> +{
> +    virtio_reset(virtio_get_device());
> +}
Philippe Mathieu-Daudé Jan. 16, 2025, 3:49 p.m. UTC | #2
On 16/1/25 12:58, Thomas Huth wrote:
> When we are trying to boot from virtio-net devices, the
> s390-ccw bios currently leaves the virtio-net device enabled
> after using it. That means that the receiving virt queues will
> continue to happily write incoming network packets into memory.
> This can corrupt data of the following boot process. For example,
> if you set up a second guest on a virtual network and create a
> lot of broadcast traffic there, e.g. with:
> 
>   ping -i 0.02 -s 1400  -b 192.168.1.255
> 
> and then you try to boot a guest with two boot devices, a network
> device first (which should not be bootable) and e.g. a bootable SCSI
> CD second, then this guest will fail to load the kernel from the CD
> image:
> 
>   $ qemu-system-s390x -m 2G -nographic -device virtio-scsi-ccw \
>      -netdev tap,id=net0 -device virtio-net-ccw,netdev=net0,bootindex=1 \
>      -drive if=none,file=test.iso,format=raw,id=cd1 \
>      -device scsi-cd,drive=cd1,bootindex=2
>   LOADPARM=[        ]
> 
>   Network boot device detected
>   Network boot starting...
>     Using MAC address: 52:54:00:12:34:56
>     Requesting information via DHCP: done
>     Using IPv4 address: 192.168.1.76
>     Using TFTP server: 192.168.1.1
>   Trying pxelinux.cfg files...
>     TFTP error: ICMP ERROR "port unreachable"
>     Receiving data:  0 KBytes
>   Repeating TFTP read request...
>     TFTP error: ICMP ERROR "port unreachable"
>   Failed to load OS from network.
>   Failed to IPL from this network!
>   LOADPARM=[        ]
> 
>   Using virtio-scsi.
> 
>   ! virtio-scsi:setup:inquiry: response VS RESP=ff !
>   ERROR: No suitable device for IPL. Halting...
> 
> We really have to shut up the virtio-net devices after we're not
> using it anymore. The easiest way to do this is to simply reset
> the device, so let's do that now.
> 
> Signed-off-by: Thomas Huth <thuth@redhat.com>
> ---
>   pc-bios/s390-ccw/virtio.h     |  1 +
>   pc-bios/s390-ccw/netmain.c    | 33 +++++++++++++++++++++++----------
>   pc-bios/s390-ccw/virtio-net.c |  5 +++++
>   3 files changed, 29 insertions(+), 10 deletions(-)


> diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c
> index e46e470db4..335ea9b63e 100644
> --- a/pc-bios/s390-ccw/netmain.c
> +++ b/pc-bios/s390-ccw/netmain.c
> @@ -153,19 +153,10 @@ static int tftp_load(filename_ip_t *fnip, void *buffer, int len)
>       return rc;
>   }
>   
> -static int net_init(filename_ip_t *fn_ip)
> +static int net_init_ip(filename_ip_t *fn_ip)
>   {
>       int rc;
>   
> -    memset(fn_ip, 0, sizeof(filename_ip_t));
> -
> -    rc = virtio_net_init(mac);
> -    if (rc < 0) {
> -        puts("Could not initialize network device");
> -        return -101;
> -    }
> -    fn_ip->fd = rc;
> -
>       printf("  Using MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",
>              mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
>   
> @@ -221,11 +212,33 @@ static int net_init(filename_ip_t *fn_ip)
>       return rc;
>   }
>   
> +static int net_init(filename_ip_t *fn_ip)
> +{
> +    int rc;
> +
> +    memset(fn_ip, 0, sizeof(filename_ip_t));
> +
> +    rc = virtio_net_init(mac);
> +    if (rc < 0) {
> +        puts("Could not initialize network device");
> +        return -101;
> +    }
> +    fn_ip->fd = rc;
> +
> +    rc = net_init_ip(fn_ip);
> +    if (rc < 0) {
> +        virtio_net_deinit();
> +    }
> +
> +    return rc;
> +}

Optionally extract net_init_ip() in a preliminary patch
for clarity to see where virtio_net_deinit() is added.
diff mbox series

Patch

diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h
index f13fa6f5fe..5c5e808a50 100644
--- a/pc-bios/s390-ccw/virtio.h
+++ b/pc-bios/s390-ccw/virtio.h
@@ -278,5 +278,6 @@  int virtio_reset(VDev *vdev);
 int virtio_setup_ccw(VDev *vdev);
 
 int virtio_net_init(void *mac_addr);
+void virtio_net_deinit(void);
 
 #endif /* VIRTIO_H */
diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c
index e46e470db4..335ea9b63e 100644
--- a/pc-bios/s390-ccw/netmain.c
+++ b/pc-bios/s390-ccw/netmain.c
@@ -153,19 +153,10 @@  static int tftp_load(filename_ip_t *fnip, void *buffer, int len)
     return rc;
 }
 
-static int net_init(filename_ip_t *fn_ip)
+static int net_init_ip(filename_ip_t *fn_ip)
 {
     int rc;
 
-    memset(fn_ip, 0, sizeof(filename_ip_t));
-
-    rc = virtio_net_init(mac);
-    if (rc < 0) {
-        puts("Could not initialize network device");
-        return -101;
-    }
-    fn_ip->fd = rc;
-
     printf("  Using MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",
            mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
 
@@ -221,11 +212,33 @@  static int net_init(filename_ip_t *fn_ip)
     return rc;
 }
 
+static int net_init(filename_ip_t *fn_ip)
+{
+    int rc;
+
+    memset(fn_ip, 0, sizeof(filename_ip_t));
+
+    rc = virtio_net_init(mac);
+    if (rc < 0) {
+        puts("Could not initialize network device");
+        return -101;
+    }
+    fn_ip->fd = rc;
+
+    rc = net_init_ip(fn_ip);
+    if (rc < 0) {
+        virtio_net_deinit();
+    }
+
+    return rc;
+}
+
 static void net_release(filename_ip_t *fn_ip)
 {
     if (fn_ip->ip_version == 4) {
         dhcp_send_release(fn_ip->fd);
     }
+    virtio_net_deinit();
 }
 
 /**
diff --git a/pc-bios/s390-ccw/virtio-net.c b/pc-bios/s390-ccw/virtio-net.c
index 578c89d0c5..301445bf97 100644
--- a/pc-bios/s390-ccw/virtio-net.c
+++ b/pc-bios/s390-ccw/virtio-net.c
@@ -140,3 +140,8 @@  int recv(int fd, void *buf, int maxlen, int flags)
 
     return len;
 }
+
+void virtio_net_deinit(void)
+{
+    virtio_reset(virtio_get_device());
+}