diff options
Diffstat (limited to 'recipes-extended/libvirt/libvirt/lxc_monitor-Avoid-AB-BA-lock-race.patch')
-rw-r--r-- | recipes-extended/libvirt/libvirt/lxc_monitor-Avoid-AB-BA-lock-race.patch | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/recipes-extended/libvirt/libvirt/lxc_monitor-Avoid-AB-BA-lock-race.patch b/recipes-extended/libvirt/libvirt/lxc_monitor-Avoid-AB-BA-lock-race.patch new file mode 100644 index 00000000..fc3880fb --- /dev/null +++ b/recipes-extended/libvirt/libvirt/lxc_monitor-Avoid-AB-BA-lock-race.patch | |||
@@ -0,0 +1,106 @@ | |||
1 | From 7882c6eca53fe9abe253497a50f6c5ae062176d3 Mon Sep 17 00:00:00 2001 | ||
2 | From: Mark Asselstine <mark.asselstine@windriver.com> | ||
3 | Date: Mon, 24 Sep 2018 11:11:35 -0400 | ||
4 | Subject: [PATCH] lxc_monitor: Avoid AB / BA lock race | ||
5 | |||
6 | A deadlock situation can occur when autostarting a LXC domain 'guest' | ||
7 | due to two threads attempting to take opposing locks while holding | ||
8 | opposing locks (AB BA problem). Thread A takes and holds the 'vm' lock | ||
9 | while attempting to take the 'client' lock, meanwhile, thread B takes | ||
10 | and holds the 'client' lock while attempting to take the 'vm' lock. | ||
11 | |||
12 | The potential for this can be seen as follows: | ||
13 | |||
14 | Thread A: | ||
15 | virLXCProcessAutostartDomain (takes vm lock) | ||
16 | --> virLXCProcessStart | ||
17 | --> virLXCProcessConnectMonitor | ||
18 | --> virLXCMonitorNew | ||
19 | --> virNetClientSetCloseCallback (wants client lock) | ||
20 | |||
21 | Thread B: | ||
22 | virNetClientIncomingEvent (takes client lock) | ||
23 | --> virNetClientIOHandleInput | ||
24 | --> virNetClientCallDispatch | ||
25 | --> virNetClientCallDispatchMessage | ||
26 | --> virNetClientProgramDispatch | ||
27 | --> virLXCMonitorHandleEventInit | ||
28 | --> virLXCProcessMonitorInitNotify (wants vm lock) | ||
29 | |||
30 | Since these threads are scheduled independently and are preemptible it | ||
31 | is possible for the deadlock scenario to occur where each thread locks | ||
32 | their first lock but both will fail to get their second lock and just | ||
33 | spin forever. You get something like: | ||
34 | |||
35 | virLXCProcessAutostartDomain (takes vm lock) | ||
36 | --> virLXCProcessStart | ||
37 | --> virLXCProcessConnectMonitor | ||
38 | --> virLXCMonitorNew | ||
39 | <...> | ||
40 | virNetClientIncomingEvent (takes client lock) | ||
41 | --> virNetClientIOHandleInput | ||
42 | --> virNetClientCallDispatch | ||
43 | --> virNetClientCallDispatchMessage | ||
44 | --> virNetClientProgramDispatch | ||
45 | --> virLXCMonitorHandleEventInit | ||
46 | --> virLXCProcessMonitorInitNotify (wants vm lock but spins) | ||
47 | <...> | ||
48 | --> virNetClientSetCloseCallback (wants client lock but spins) | ||
49 | |||
50 | Neither thread ever gets the lock it needs to be able to continue | ||
51 | while holding the lock that the other thread needs. | ||
52 | |||
53 | The actual window for preemption which can cause this deadlock is | ||
54 | rather small, between the calls to virNetClientProgramNew() and | ||
55 | execution of virNetClientSetCloseCallback(), both in | ||
56 | virLXCMonitorNew(). But it can be seen in real world use that this | ||
57 | small window is enough. | ||
58 | |||
59 | By moving the call to virNetClientSetCloseCallback() ahead of | ||
60 | virNetClientProgramNew() we can close any possible chance of the | ||
61 | deadlock taking place. There should be no other implications to the | ||
62 | move since the close callback (in the unlikely event was called) will | ||
63 | spin on the vm lock. The remaining work that takes place between the | ||
64 | old call location of virNetClientSetCloseCallback() and the new | ||
65 | location is unaffected by the move. | ||
66 | |||
67 | Upstream-Status: Backport commit 7882c6eca53f | ||
68 | |||
69 | Signed-off-by: Mark Asselstine <mark.asselstine@windriver.com> | ||
70 | Signed-off-by: Michal Privoznik <mprivozn@redhat.com> | ||
71 | --- | ||
72 | src/lxc/lxc_monitor.c | 11 +++++++---- | ||
73 | 1 file changed, 7 insertions(+), 4 deletions(-) | ||
74 | |||
75 | diff --git a/src/lxc/lxc_monitor.c b/src/lxc/lxc_monitor.c | ||
76 | index e765c16..0b18a14 100644 | ||
77 | --- a/src/lxc/lxc_monitor.c | ||
78 | +++ b/src/lxc/lxc_monitor.c | ||
79 | @@ -161,6 +161,13 @@ virLXCMonitorPtr virLXCMonitorNew(virDomainObjPtr vm, | ||
80 | if (virNetClientRegisterAsyncIO(mon->client) < 0) | ||
81 | goto error; | ||
82 | |||
83 | + /* avoid deadlock by making this call before assigning virLXCMonitorEvents */ | ||
84 | + virNetClientSetCloseCallback(mon->client, virLXCMonitorEOFNotify, mon, | ||
85 | + virLXCMonitorCloseFreeCallback); | ||
86 | + | ||
87 | + /* close callback now has its own reference */ | ||
88 | + virObjectRef(mon); | ||
89 | + | ||
90 | if (!(mon->program = virNetClientProgramNew(VIR_LXC_MONITOR_PROGRAM, | ||
91 | VIR_LXC_MONITOR_PROGRAM_VERSION, | ||
92 | virLXCMonitorEvents, | ||
93 | @@ -175,10 +182,6 @@ virLXCMonitorPtr virLXCMonitorNew(virDomainObjPtr vm, | ||
94 | mon->vm = virObjectRef(vm); | ||
95 | memcpy(&mon->cb, cb, sizeof(mon->cb)); | ||
96 | |||
97 | - virObjectRef(mon); | ||
98 | - virNetClientSetCloseCallback(mon->client, virLXCMonitorEOFNotify, mon, | ||
99 | - virLXCMonitorCloseFreeCallback); | ||
100 | - | ||
101 | cleanup: | ||
102 | VIR_FREE(sockpath); | ||
103 | return mon; | ||
104 | -- | ||
105 | 2.7.4 | ||
106 | |||