Patchwork lsi_soft_reset: Assertion `!s->current' failed with Windows XP setup

login
register
mail settings
Submitter Claudio Bley
Date April 30, 2013, 12:59 p.m.
Message ID <874neop42m.wl%cbley@av-test.de>
Download mbox | patch
Permalink /patch/240613/
State New
Headers show

Comments

Claudio Bley - April 30, 2013, 12:59 p.m.
At Mon, 29 Apr 2013 17:54:37 +0200,
Paolo Bonzini wrote:
> 
> Il 12/04/2013 16:02, Claudio Bley ha scritto:
> > Hi.
> > 
> > I'm using the latest qemu release 1.4.0 and libvirt 1.0.4.
> > 
> > The host system is a Ubuntu 12.04 LTS system, Intel Xeon processor,
> > 4GB RAM.
> > 
> > When trying to install Windows XP I constantly receive this error
> > (usually right before the graphical Setup is started). Once, I've seen
> > this error too:
> > 
> > hw/lsi53c895a.c:351: lsi_soft_reset: Assertion `((&s->queue)->tqh_first == ((void *)0))' failed.
> > 
> > I found the thread here
> > http://lists.gnu.org/archive/html/qemu-devel/2012-11/msg00518.html but
> > it didn't led me nowhere. 
> > 
> > I recompiled with DEBUG_SCSI and DEBUG_LSI enabled. Here's the command
> > libvirt put together and the last few lines of output. I can provide
> > the complete log if you need it.
> 
> Hi, sorry---I just read this now.
> 
> Can you please try the patches at
> http://cache.gmane.org//gmane/comp/emulators/qemu/208057-001.bin?

I gave it a shot and this fixed the bug for me.

I've applied the patches to git tag v1.4.1 with minor modifications:

- skipped patch 1 and 2 since they already had been applied upstream
  in the meantime, AFAICS

- s/vists/visits/ in subject line of patch 3

- updated patch 3 and 4 in order to adjust for moved-around files and
  some code changes since 1.3.0

Thanks!

Here are the updated patches:

------- >8 ---------------------------------------------
From e33168365eae0d6610762ebafd22f26a7a6168e1 Mon Sep 17 00:00:00 2001
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Mon, 17 Dec 2012 10:37:02 +0100
Subject: [PATCH 1/2] qdev: allow both pre- and post-order visits in qdev
 walking functions


Resetting should be done in post-order, not pre-order.  However,
qdev_walk_children and qbus_walk_children do not allow this.  Fix
it by adding two extra arguments to the functions.

---
 hw/qdev-core.h |   13 +++++++++----
 hw/qdev.c      |   45 +++++++++++++++++++++++++++++++++------------
 2 files changed, 42 insertions(+), 16 deletions(-)
Paolo Bonzini - April 30, 2013, 1:44 p.m.
Il 30/04/2013 14:59, Claudio Bley ha scritto:
> At Mon, 29 Apr 2013 17:54:37 +0200,
> Paolo Bonzini wrote:
>>
>> Il 12/04/2013 16:02, Claudio Bley ha scritto:
>>> Hi.
>>>
>>> I'm using the latest qemu release 1.4.0 and libvirt 1.0.4.
>>>
>>> The host system is a Ubuntu 12.04 LTS system, Intel Xeon processor,
>>> 4GB RAM.
>>>
>>> When trying to install Windows XP I constantly receive this error
>>> (usually right before the graphical Setup is started). Once, I've seen
>>> this error too:
>>>
>>> hw/lsi53c895a.c:351: lsi_soft_reset: Assertion `((&s->queue)->tqh_first == ((void *)0))' failed.
>>>
>>> I found the thread here
>>> http://lists.gnu.org/archive/html/qemu-devel/2012-11/msg00518.html but
>>> it didn't led me nowhere. 
>>>
>>> I recompiled with DEBUG_SCSI and DEBUG_LSI enabled. Here's the command
>>> libvirt put together and the last few lines of output. I can provide
>>> the complete log if you need it.
>>
>> Hi, sorry---I just read this now.
>>
>> Can you please try the patches at
>> http://cache.gmane.org//gmane/comp/emulators/qemu/208057-001.bin?
> 
> I gave it a shot and this fixed the bug for me.

Thanks very much.  It is late for 1.5 now, but these will be in 1.5.1
and 1.6.

Paolo

> I've applied the patches to git tag v1.4.1 with minor modifications:
> 
> - skipped patch 1 and 2 since they already had been applied upstream
>   in the meantime, AFAICS
> 
> - s/vists/visits/ in subject line of patch 3
> 
> - updated patch 3 and 4 in order to adjust for moved-around files and
>   some code changes since 1.3.0
> 
> Thanks!
> 
> Here are the updated patches:
> 
> ------- >8 ---------------------------------------------
> From e33168365eae0d6610762ebafd22f26a7a6168e1 Mon Sep 17 00:00:00 2001
> From: Paolo Bonzini <pbonzini@redhat.com>
> Date: Mon, 17 Dec 2012 10:37:02 +0100
> Subject: [PATCH 1/2] qdev: allow both pre- and post-order visits in qdev
>  walking functions
> 
> 
> Resetting should be done in post-order, not pre-order.  However,
> qdev_walk_children and qbus_walk_children do not allow this.  Fix
> it by adding two extra arguments to the functions.
> 
> ---
>  hw/qdev-core.h |   13 +++++++++----
>  hw/qdev.c      |   45 +++++++++++++++++++++++++++++++++------------
>  2 files changed, 42 insertions(+), 16 deletions(-)
> 
> diff --git a/hw/qdev-core.h b/hw/qdev-core.h
> index 2486f36..a0d05ca 100644
> --- a/hw/qdev-core.h
> +++ b/hw/qdev-core.h
> @@ -237,10 +237,15 @@ BusState *qbus_create(const char *typename, DeviceState *parent, const char *nam
>  /* Returns > 0 if either devfn or busfn skip walk somewhere in cursion,
>   *         < 0 if either devfn or busfn terminate walk somewhere in cursion,
>   *           0 otherwise. */
> -int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn,
> -                       qbus_walkerfn *busfn, void *opaque);
> -int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn,
> -                       qbus_walkerfn *busfn, void *opaque);
> +int qbus_walk_children(BusState *bus,
> +                       qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
> +                       qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
> +                       void *opaque);
> +int qdev_walk_children(DeviceState *dev,
> +                       qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
> +                       qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
> +                       void *opaque);
> +
>  void qdev_reset_all(DeviceState *dev);
>  
>  /**
> diff --git a/hw/qdev.c b/hw/qdev.c
> index 689cd54..772cfe0 100644
> --- a/hw/qdev.c
> +++ b/hw/qdev.c
> @@ -224,12 +224,12 @@ static int qbus_reset_one(BusState *bus, void *opaque)
>  
>  void qdev_reset_all(DeviceState *dev)
>  {
> -    qdev_walk_children(dev, qdev_reset_one, qbus_reset_one, NULL);
> +    qdev_walk_children(dev, qdev_reset_one, qbus_reset_one, NULL, NULL, NULL);
>  }
>  
>  void qbus_reset_all(BusState *bus)
>  {
> -    qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL);
> +    qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL, NULL, NULL);
>  }
>  
>  void qbus_reset_all_fn(void *opaque)
> @@ -327,49 +327,70 @@ BusState *qdev_get_child_bus(DeviceState *dev, const char *name)
>      return NULL;
>  }
>  
> -int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn,
> -                       qbus_walkerfn *busfn, void *opaque)
> +int qbus_walk_children(BusState *bus,
> +                       qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
> +                       qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
> +                       void *opaque)
>  {
>      BusChild *kid;
>      int err;
>  
> -    if (busfn) {
> -        err = busfn(bus, opaque);
> +    if (pre_busfn) {
> +        err = pre_busfn(bus, opaque);
>          if (err) {
>              return err;
>          }
>      }
>  
>      QTAILQ_FOREACH(kid, &bus->children, sibling) {
> -        err = qdev_walk_children(kid->child, devfn, busfn, opaque);
> +        err = qdev_walk_children(kid->child,
> +                                 pre_devfn, pre_busfn,
> +                                 post_devfn, post_busfn, opaque);
>          if (err < 0) {
>              return err;
>          }
>      }
>  
> +    if (post_busfn) {
> +        err = post_busfn(bus, opaque);
> +        if (err) {
> +            return err;
> +        }
> +    }
> +
>      return 0;
>  }
>  
> -int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn,
> -                       qbus_walkerfn *busfn, void *opaque)
> +int qdev_walk_children(DeviceState *dev,
> +                       qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
> +                       qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
> +                       void *opaque)
>  {
>      BusState *bus;
>      int err;
>  
> -    if (devfn) {
> -        err = devfn(dev, opaque);
> +    if (pre_devfn) {
> +        err = pre_devfn(dev, opaque);
>          if (err) {
>              return err;
>          }
>      }
>  
>      QLIST_FOREACH(bus, &dev->child_bus, sibling) {
> -        err = qbus_walk_children(bus, devfn, busfn, opaque);
> +        err = qbus_walk_children(bus, pre_devfn, pre_busfn,
> +                                 post_devfn, post_busfn, opaque);
>          if (err < 0) {
>              return err;
>          }
>      }
>  
> +    if (post_devfn) {
> +        err = post_devfn(dev, opaque);
> +        if (err) {
> +            return err;
> +        }
> +    }
> +
>      return 0;
>  }
>  
>

Patch

diff --git a/hw/qdev-core.h b/hw/qdev-core.h
index 2486f36..a0d05ca 100644
--- a/hw/qdev-core.h
+++ b/hw/qdev-core.h
@@ -237,10 +237,15 @@  BusState *qbus_create(const char *typename, DeviceState *parent, const char *nam
 /* Returns > 0 if either devfn or busfn skip walk somewhere in cursion,
  *         < 0 if either devfn or busfn terminate walk somewhere in cursion,
  *           0 otherwise. */
-int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn,
-                       qbus_walkerfn *busfn, void *opaque);
-int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn,
-                       qbus_walkerfn *busfn, void *opaque);
+int qbus_walk_children(BusState *bus,
+                       qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
+                       qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
+                       void *opaque);
+int qdev_walk_children(DeviceState *dev,
+                       qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
+                       qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
+                       void *opaque);
+
 void qdev_reset_all(DeviceState *dev);
 
 /**
diff --git a/hw/qdev.c b/hw/qdev.c
index 689cd54..772cfe0 100644
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -224,12 +224,12 @@  static int qbus_reset_one(BusState *bus, void *opaque)
 
 void qdev_reset_all(DeviceState *dev)
 {
-    qdev_walk_children(dev, qdev_reset_one, qbus_reset_one, NULL);
+    qdev_walk_children(dev, qdev_reset_one, qbus_reset_one, NULL, NULL, NULL);
 }
 
 void qbus_reset_all(BusState *bus)
 {
-    qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL);
+    qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL, NULL, NULL);
 }
 
 void qbus_reset_all_fn(void *opaque)
@@ -327,49 +327,70 @@  BusState *qdev_get_child_bus(DeviceState *dev, const char *name)
     return NULL;
 }
 
-int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn,
-                       qbus_walkerfn *busfn, void *opaque)
+int qbus_walk_children(BusState *bus,
+                       qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
+                       qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
+                       void *opaque)
 {
     BusChild *kid;
     int err;
 
-    if (busfn) {
-        err = busfn(bus, opaque);
+    if (pre_busfn) {
+        err = pre_busfn(bus, opaque);
         if (err) {
             return err;
         }
     }
 
     QTAILQ_FOREACH(kid, &bus->children, sibling) {
-        err = qdev_walk_children(kid->child, devfn, busfn, opaque);
+        err = qdev_walk_children(kid->child,
+                                 pre_devfn, pre_busfn,
+                                 post_devfn, post_busfn, opaque);
         if (err < 0) {
             return err;
         }
     }
 
+    if (post_busfn) {
+        err = post_busfn(bus, opaque);
+        if (err) {
+            return err;
+        }
+    }
+
     return 0;
 }
 
-int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn,
-                       qbus_walkerfn *busfn, void *opaque)
+int qdev_walk_children(DeviceState *dev,
+                       qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
+                       qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
+                       void *opaque)
 {
     BusState *bus;
     int err;
 
-    if (devfn) {
-        err = devfn(dev, opaque);
+    if (pre_devfn) {
+        err = pre_devfn(dev, opaque);
         if (err) {
             return err;
         }
     }
 
     QLIST_FOREACH(bus, &dev->child_bus, sibling) {
-        err = qbus_walk_children(bus, devfn, busfn, opaque);
+        err = qbus_walk_children(bus, pre_devfn, pre_busfn,
+                                 post_devfn, post_busfn, opaque);
         if (err < 0) {
             return err;
         }
     }
 
+    if (post_devfn) {
+        err = post_devfn(dev, opaque);
+        if (err) {
+            return err;
+        }
+    }
+
     return 0;
 }
 
-- 
1.7.9.5


From 79abbbbf1666452552876878dc16fd973eb41ee3 Mon Sep 17 00:00:00 2001
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Tue, 30 Apr 2013 14:36:23 +0200
Subject: [PATCH 2/2] qdev: switch reset to post-order


Post-order is the only sensible direction for the reset signals.
For example, suppose pre-order is used and the parent has some data
structures that cache children state (for example a list of active
requests).  When the reset method is invoked on the parent, these caches
could be in any state.

If post-order is used, on the other hand, these will be in a known state
when the reset method is invoked on the parent.

This change means that it is no longer possible to block the visit of
the devices, so the callback is changed to return void.  This is not
a problem, because PCI was returning 1 exactly in order to achieve the
same ordering that this patch implements.

PCI can then rely on the qdev core having sent a "reset signal" (whatever
that means) to the device, and only do the PCI-specific initialization
with pci_do_device_reset.

---
 hw/pci/pci.c   |   31 ++++++++++++++++---------------
 hw/qdev-core.h |    2 +-
 hw/qdev.c      |    6 +++---
 3 files changed, 20 insertions(+), 19 deletions(-)

diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index 2f45c8f..2fcf69d 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -45,7 +45,7 @@ 
 static void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent);
 static char *pcibus_get_dev_path(DeviceState *dev);
 static char *pcibus_get_fw_dev_path(DeviceState *dev);
-static int pcibus_reset(BusState *qbus);
+static void pcibus_reset(BusState *qbus);
 
 static Property pci_props[] = {
     DEFINE_PROP_PCI_DEVFN("addr", PCIDevice, devfn, -1),
@@ -164,16 +164,10 @@  void pci_device_deassert_intx(PCIDevice *dev)
     }
 }
 
-/*
- * This function is called on #RST and FLR.
- * FLR if PCI_EXP_DEVCTL_BCR_FLR is set
- */
-void pci_device_reset(PCIDevice *dev)
+static void pci_do_device_reset(PCIDevice *dev)
 {
     int r;
 
-    qdev_reset_all(&dev->qdev);
-
     dev->irq_state = 0;
     pci_update_irq_status(dev);
     pci_device_deassert_intx(dev);
@@ -206,8 +200,19 @@  void pci_device_reset(PCIDevice *dev)
 }
 
 /*
+ * This function is called on #RST and FLR.
+ * FLR if PCI_EXP_DEVCTL_BCR_FLR is set
+ */
+void pci_device_reset(PCIDevice *dev)
+{
+    qdev_reset_all(&dev->qdev);
+    pci_do_device_reset(dev);
+}
+
+/*
  * Trigger pci bus reset under a given bus.
- * To be called on RST# assert.
+ * Called via qbus_reset_all on RST# assert, after the devices
+ * have been reset qdev_reset_all-ed already.
  */
 void pci_bus_reset(PCIBus *bus)
 {
@@ -218,18 +223,14 @@  void pci_bus_reset(PCIBus *bus)
     }
     for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) {
         if (bus->devices[i]) {
-            pci_device_reset(bus->devices[i]);
+            pci_do_device_reset(bus->devices[i]);
         }
     }
 }
 
-static int pcibus_reset(BusState *qbus)
+static void pcibus_reset(BusState *qbus)
 {
     pci_bus_reset(DO_UPCAST(PCIBus, qbus, qbus));
-
-    /* topology traverse is done by pci_bus_reset().
-       Tell qbus/qdev walker not to traverse the tree */
-    return 1;
 }
 
 static void pci_host_bus_register(int domain, PCIBus *bus)
diff --git a/hw/qdev-core.h b/hw/qdev-core.h
index a0d05ca..9be24db 100644
--- a/hw/qdev-core.h
+++ b/hw/qdev-core.h
@@ -144,7 +144,7 @@  struct BusClass {
      * bindings can be found at http://playground.sun.com/1275/bindings/.
      */
     char *(*get_fw_dev_path)(DeviceState *dev);
-    int (*reset)(BusState *bus);
+    void (*reset)(BusState *bus);
     /* maximum devices allowed on the bus, 0: no limit. */
     int max_dev;
 };
diff --git a/hw/qdev.c b/hw/qdev.c
index 772cfe0..cd8ecf8 100644
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -217,19 +217,19 @@  static int qbus_reset_one(BusState *bus, void *opaque)
 {
     BusClass *bc = BUS_GET_CLASS(bus);
     if (bc->reset) {
-        return bc->reset(bus);
+        bc->reset(bus);
     }
     return 0;
 }
 
 void qdev_reset_all(DeviceState *dev)
 {
-    qdev_walk_children(dev, qdev_reset_one, qbus_reset_one, NULL, NULL, NULL);
+    qdev_walk_children(dev, NULL, NULL, qdev_reset_one, qbus_reset_one, NULL);
 }
 
 void qbus_reset_all(BusState *bus)
 {
-    qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL, NULL, NULL);
+    qbus_walk_children(bus, NULL, NULL, qdev_reset_one, qbus_reset_one, NULL);
 }
 
 void qbus_reset_all_fn(void *opaque)