1 // Copyright 2020 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "host-common/goldfish_sync.h"
16 #include "host-common/GoldfishSyncCommandQueue.h"
17
18 #include "aemu/base/containers/Lookup.h"
19 #include "aemu/base/synchronization/ConditionVariable.h"
20 #include "aemu/base/synchronization/Lock.h"
21
22 #include <unordered_map>
23
24 using android::base::AutoLock;
25 using android::base::ConditionVariable;
26 using android::base::Lock;
27 using android::base::StaticLock;
28 using android::GoldfishSyncCommandQueue;
29
30 // Commands can be tagged with with unique id's,
31 // so that for the commands that require a reply
32 // from the guest, we signal them properly.
33 static uint64_t sUniqueId = 0;
34 // When we track command completion, we need to be
35 // careful about concurrent access.
36 // |sCommandReplyLock| protects
37 // |sUniqueId| and |wait_map|, including
38 // the |CommandWaitInfo| structures within.
39 static StaticLock sCommandReplyLock = {};
40
next_unique_id()41 uint64_t next_unique_id() {
42 AutoLock lock(sCommandReplyLock);
43 uint64_t res = sUniqueId;
44 sUniqueId += 1;
45 return res;
46 }
47
48 struct CommandWaitInfo {
49 Lock lock; // protects other parts of this struct
50 bool done = false;
51 ConditionVariable cvDone;
52 uint64_t return_value;
53 };
54
55 // |wait_map| keeps track of all the commands in flight
56 // that require a reply from the guest.
57 static std::unordered_map<uint64_t, std::unique_ptr<CommandWaitInfo> >
58 wait_map;
59
allocWait(uint64_t id)60 static CommandWaitInfo* allocWait(uint64_t id) {
61 AutoLock lock(sCommandReplyLock);
62 std::unique_ptr<CommandWaitInfo>& res =
63 wait_map[id];
64 res.reset(new CommandWaitInfo);
65 return res.get();
66 }
67
freeWait(uint64_t id)68 static void freeWait(uint64_t id) {
69 AutoLock lock(sCommandReplyLock);
70 wait_map.erase(id);
71 }
72
73 static GoldfishSyncDeviceInterface* sGoldfishSyncHwFuncs = NULL;
74
75 ////////////////////////////////////////////////////////////////////////////////
76 // Goldfish sync device: command send/receive protocol
77 // To send commands to the virtual device, there are two
78 // alternatives:
79 // - |sendCommand|, which just sends the command without waiting
80 // for a reply.
81 // - |sendCommandAndGetResult|, which sends the command and waits
82 // for that command to have completed in the guest.
83
84 // |sendCommand| is used to send Goldfish sync commands while
85 // not caring about a reply from the guest. During normal operation,
86 // we will only use |sendCommand| to send over a |goldfish_sync_timeline_inc|
87 // call, to signal fence FD's on the guest.
sendCommand(uint32_t cmd,uint64_t handle,uint32_t time_arg)88 static void sendCommand(uint32_t cmd,
89 uint64_t handle,
90 uint32_t time_arg) {
91 GoldfishSyncCommandQueue::hostSignal
92 (cmd, handle, time_arg, 0
93 // last arg 0 OK because we will not reference it
94 );
95 }
96
97 // Receiving commands can be interesting because we do not know when
98 // the kernel will get to servicing a command we sent from the host.
99 //
100 // |receiveCommandResult| is for host->guest goldfish sync commands
101 // that require a reply from the guest. So far, this is used only
102 // in the functional tests, as we never issue
103 // |goldfish_sync_create_timeline| / |goldfish_sync_create_fence|
104 // from the host directly in normal operation.
105 //
106 // This function will be called by the virtual device
107 // upon receiving a reply from the guest for the host->guest
108 // commands that require replies.
109 //
110 // The implementation is that such commands will use a condition
111 // variable that waits on the result.
goldfish_sync_receive_hostcmd_result(uint32_t cmd,uint64_t handle,uint32_t time_arg,uint64_t hostcmd_handle)112 void goldfish_sync_receive_hostcmd_result(uint32_t cmd,
113 uint64_t handle,
114 uint32_t time_arg,
115 uint64_t hostcmd_handle) {
116 if (auto elt = android::base::find(wait_map, hostcmd_handle)) {
117 CommandWaitInfo* wait_info = elt->get();
118 AutoLock lock(wait_info->lock);
119 wait_info->return_value = handle;
120 wait_info->done = true;
121 wait_info->cvDone.broadcast();
122 }
123 }
124
125 // |sendCommandAndGetResult| uses |sendCommand| and
126 // |goldfish_sync_receive_hostcmd_result| for processing
127 // commands that require replies from the guest.
sendCommandAndGetResult(uint64_t cmd,uint64_t handle,uint64_t time_arg,uint64_t hostcmd_handle)128 static uint64_t sendCommandAndGetResult(uint64_t cmd,
129 uint64_t handle,
130 uint64_t time_arg,
131 uint64_t hostcmd_handle) {
132
133 // queue a signal to the device
134 GoldfishSyncCommandQueue::hostSignal
135 (cmd, handle, time_arg, hostcmd_handle);
136
137 CommandWaitInfo* waitInfo = allocWait(hostcmd_handle);
138
139 uint64_t res;
140
141 {
142 AutoLock lock(waitInfo->lock);
143 while (!waitInfo->done) {
144 waitInfo->cvDone.wait(&waitInfo->lock);
145 }
146
147 res = waitInfo->return_value;
148 }
149
150 freeWait(hostcmd_handle);
151
152 return res;
153 }
154
155 // Goldfish sync host-side interface implementation/////////////////////////////
156
goldfish_sync_create_timeline()157 uint64_t goldfish_sync_create_timeline() {
158 return sendCommandAndGetResult(CMD_CREATE_SYNC_TIMELINE,
159 0, 0, next_unique_id());
160 }
161
goldfish_sync_create_fence(uint64_t timeline,uint32_t pt)162 int goldfish_sync_create_fence(uint64_t timeline, uint32_t pt) {
163 return (int)sendCommandAndGetResult(CMD_CREATE_SYNC_FENCE,
164 timeline, pt, next_unique_id());
165 }
166
goldfish_sync_timeline_inc(uint64_t timeline,uint32_t howmuch)167 void goldfish_sync_timeline_inc(uint64_t timeline, uint32_t howmuch) {
168 sendCommand(CMD_SYNC_TIMELINE_INC, timeline, howmuch);
169 }
170
goldfish_sync_destroy_timeline(uint64_t timeline)171 void goldfish_sync_destroy_timeline(uint64_t timeline) {
172 sendCommand(CMD_DESTROY_SYNC_TIMELINE, timeline, 0);
173 }
174
goldfish_sync_register_trigger_wait(trigger_wait_fn_t f)175 void goldfish_sync_register_trigger_wait(trigger_wait_fn_t f) {
176 if (goldfish_sync_device_exists()) {
177 sGoldfishSyncHwFuncs->registerTriggerWait(f);
178 }
179 }
180
goldfish_sync_device_exists()181 bool goldfish_sync_device_exists() {
182 // The idea here is that the virtual device should set
183 // sGoldfishSyncHwFuncs. If it didn't do that, we take
184 // that to mean there is no virtual device.
185 return sGoldfishSyncHwFuncs != NULL;
186 }
187
goldfish_sync_set_hw_funcs(GoldfishSyncDeviceInterface * hw_funcs)188 void goldfish_sync_set_hw_funcs(GoldfishSyncDeviceInterface* hw_funcs) {
189 sGoldfishSyncHwFuncs = hw_funcs;
190 GoldfishSyncCommandQueue::setQueueCommand
191 (sGoldfishSyncHwFuncs->doHostCommand);
192 }
193
194