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