Wow, you guys certainly wake up bright and early on Monday morning. Regarding the patch, I knew before I submitted it that it likely wouldn't be to final solution to the overall problem. I just have issues leaving things unresolved (or at least un-hacked-around, anyway) before I can get to sleep. Besides, it at least hacks around my problem of domain restarts, and by posting it, if someone else has the same issue, they could roll their own package with that patch to resolve it at least temporarily.
As far as the actual problem, I would agree that the storage driver should complete initialization and autostart before the QEMU driver does anything. I originally implemented a system like that (basically, I added an enum-backed field to the struct used for the state drivers), then created two separate lists in the driver initialization function (one for hypervisors, the other for everything else). Finally, I initialized and auto-started everything else, then did the hypervisors. I wanted to implement a proper driver dependency tree, but just couldn't figure out how to do it other than building the tree manually.
That, unfortunately, showed a new bug. The storage driver opens a connection (hardcoded) to 'qemu:///' during auto-start because it (or rather its backends) needs to be able to lookup storage pools and secrets by name/uuid. Both of those existing API functions require a connection in order to accomplish. After thinking about it over the weekend, I came up with a possible solution for the circular dependency issue, though.
What if a new connection URI were defined? The URI would not implement any domain-related API functions, only the API functions that would be considered global, like secret lookups, storage pool lookups, etc. The URI could be something like 'conf:///' and while it may be available to anyone, would likely only be useful within the libvirt code. That would allow the storage backends (and other sections of code) to have a valid hardcoded connection URI for when they require data from a connection, but are called when a connection won't exist, like during their auto-start sequence. Outside of a new connection URI, the other option would be to provide libvirt code a method to perform those lookups without needing a connection. That may already exist for all I know, but if it does, not everything was changed to use it. Luckily, it looks like only two sections of code actually open hard-coded connections (storage/storage_driver.c and lxc/lxc_process.c). I haven't looked into why lxc_process opens one, just found it in a grep of the code.
Lastly, I also agree that translating domain XML into a QEMU command line during a restart of libvirt shouldn't be required. If the command line arguments are required during initialization, perhaps they could be added to the domain XML after the initial translation? It'd be ugly to look at, but would serve to persist that data across libvirt restarts since the "running" version of the domain XML is stored under /var/run (or /run) alongside the PID. That, however, is actually a separate issue from the storage driver requiring a connection to QEMU during auto-start.