
On 11/9/19 1:12 AM, Jonathon Jongsma wrote:
Thanks for the thorough review Michal, clearly it needs more work. In particular, I was not yet aware of the private xml save/restore stuff. Thanks for the pointers in that regard. But I have one particular question below:
On Fri, 2019-11-08 at 11:46 +0100, Michal Privoznik wrote:
1: It's not that simple. Firstly, you need to grab an agent job (QEMU_AGENT_JOB_MODIFY ideally) to make sure no other thread is in the middle of talking to the agent. Secondly, some domains may not have an agent configured at all (in which case, priv->agent is going to be NULL). And lastly, we need to track the timeout set in domain private data so that if libvirtd restarts the same timeout is preserved. But changing private data means you have to acquire domain job too.
Is this really true? I'm obviously still not intimately familiar with the libvirt 'job' stuff, but my understanding of jobs is that it is a method to avoid multiple threads talking to the qemu monitor (or agent, for agent jobs) at the same time. The data integrity of the structs themselves are protected by mutex. But a mutex is insufficient to mediate access to the monitor/agent because waiting for a response may involve a sleep, and the mutex will be released while the thread sleeps. So this 'job' system was added on top to indicate that the monitor/agent was in use and other threads need to wait until the job is complete before they can interact with the monitor or agent. Is my understanding correct so far?
Yes, more or less. You're right in assuming that mutex protects strucutres, and job is like a flag: lock(struct); struct->doNotModify = true; unlock(struct); ... /* now any other thread sees doNotModify set => should avoid modifications */ lock(struct); struct->doNotModify = false; unlock(struct); But one can also argue that every API should grab a job (we have both query and modify jobs available), because resulting code is future proof. I mean, if anybody ever adds a monitor/agent call somewhere, we won't enter monitor without job set (i.e. unlock domain object without doNotModify flag set in my example). To put this into a perspective, imagine there is a thread that does two agent calls (e.g. creating a snapshot with --quiesce calls 'fsfreeze' before creating snapshot followed by 'fsthaw' once snapshot is created). Since talking to an agent means releasing its lock, this thread might execute freeze and thaw with different timeouts (if your API is issued meanwhile). Perhaps in this simple case it's no problem and I just want to apply defensive programming with no real trade.
In this situation, we're not talking to the qemu monitor or the agent, we're merely changing some struct data. So from a data-integrity point of view, we simply need to hold the mutex lock, right? (We do hold the mutex lock for the domain from calling qemuDomainObjFromDomain()). So it doesn't seem strictly necessary to acquire a job here (either for the agent, or for the domain/monitor).
One argument I can see for acquiring a job here is that it ensures that any private data is not changed while another thread is in the middle of a monitor query. And that's potentially a compelling justification for acquiring a job. But in this circumstance, I don't see that it gains us much.
timeout value to determine how long it should wait for a response. It sends the guest-sync command to the guest agent, and then waits for a response (releasing the mutex). While it is waiting, let's say that Thread B calls SetResponseTimeout(). Let's further imagine that SetResponseTimeout() doesn't acquire a job (as in my patch). It can acquire the agent mutex (since the other thread released it while sleeping) and safely change the mon->timeout value immediately. At some
For example, let's say Thread A has already acquired an agent job for getHostname(). This thread will call qemuAgentCommand() and pass the value of mon->timeout for the 'seconds' argument. Inside this function, it will further call qemuAgentGuestSync(), which again uses the mon- point, Thread A wakes up, handles the guest-sync response and issues the 'guest-get-host-name' command to the agent. It uses the value passed as an argument to qemuAgentCommand() to determine how long to wait for a response from the agent. So it will not use the new value set by thread B, but will use the old value that was set when it was first called. (But I think an argument could be made that it would be fine to use this new timeout value as well.)
This behavior all seems fine to me. Am I misunderstanding something fundamentally here? Or is there just a general policy that all data modification should be protected by a job, regardless of whether we're using the qemu monitor or agent? If so, perhaps that could be better documented.
Alright, let's use only locks. However, one hidden advantage of grabbing a job here is that EndJob() APIs automatically save domain status XML onto disk => if you update priv->agentTimeout in the API, the status XML is automatically written thus new value is safely stored on disk in case libvirtd restarts.
One final concern: in your fixup commit, you acquire both a normal domain job and an agent job (in the case where the agent is active). But this is something I've been working to remove from libvirt after a discussion with Daniel Berrange in https://bugzilla.redhat.com/show_bug.cgi?id=1759566
Yeah, well in this case we won't be really executing any guest agent API, merely mutually excluding with other threads that are talking to the agent. But let's use mutexes only. Michal