// Copyright 2020 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "host-common/goldfish_sync.h" #include "host-common/GoldfishSyncCommandQueue.h" #include "aemu/base/containers/Lookup.h" #include "aemu/base/synchronization/ConditionVariable.h" #include "aemu/base/synchronization/Lock.h" #include using android::base::AutoLock; using android::base::ConditionVariable; using android::base::Lock; using android::base::StaticLock; using android::GoldfishSyncCommandQueue; // Commands can be tagged with with unique id's, // so that for the commands that require a reply // from the guest, we signal them properly. static uint64_t sUniqueId = 0; // When we track command completion, we need to be // careful about concurrent access. // |sCommandReplyLock| protects // |sUniqueId| and |wait_map|, including // the |CommandWaitInfo| structures within. static StaticLock sCommandReplyLock = {}; uint64_t next_unique_id() { AutoLock lock(sCommandReplyLock); uint64_t res = sUniqueId; sUniqueId += 1; return res; } struct CommandWaitInfo { Lock lock; // protects other parts of this struct bool done = false; ConditionVariable cvDone; uint64_t return_value; }; // |wait_map| keeps track of all the commands in flight // that require a reply from the guest. static std::unordered_map > wait_map; static CommandWaitInfo* allocWait(uint64_t id) { AutoLock lock(sCommandReplyLock); std::unique_ptr& res = wait_map[id]; res.reset(new CommandWaitInfo); return res.get(); } static void freeWait(uint64_t id) { AutoLock lock(sCommandReplyLock); wait_map.erase(id); } static GoldfishSyncDeviceInterface* sGoldfishSyncHwFuncs = NULL; //////////////////////////////////////////////////////////////////////////////// // Goldfish sync device: command send/receive protocol // To send commands to the virtual device, there are two // alternatives: // - |sendCommand|, which just sends the command without waiting // for a reply. // - |sendCommandAndGetResult|, which sends the command and waits // for that command to have completed in the guest. // |sendCommand| is used to send Goldfish sync commands while // not caring about a reply from the guest. During normal operation, // we will only use |sendCommand| to send over a |goldfish_sync_timeline_inc| // call, to signal fence FD's on the guest. static void sendCommand(uint32_t cmd, uint64_t handle, uint32_t time_arg) { GoldfishSyncCommandQueue::hostSignal (cmd, handle, time_arg, 0 // last arg 0 OK because we will not reference it ); } // Receiving commands can be interesting because we do not know when // the kernel will get to servicing a command we sent from the host. // // |receiveCommandResult| is for host->guest goldfish sync commands // that require a reply from the guest. So far, this is used only // in the functional tests, as we never issue // |goldfish_sync_create_timeline| / |goldfish_sync_create_fence| // from the host directly in normal operation. // // This function will be called by the virtual device // upon receiving a reply from the guest for the host->guest // commands that require replies. // // The implementation is that such commands will use a condition // variable that waits on the result. void goldfish_sync_receive_hostcmd_result(uint32_t cmd, uint64_t handle, uint32_t time_arg, uint64_t hostcmd_handle) { if (auto elt = android::base::find(wait_map, hostcmd_handle)) { CommandWaitInfo* wait_info = elt->get(); AutoLock lock(wait_info->lock); wait_info->return_value = handle; wait_info->done = true; wait_info->cvDone.broadcast(); } } // |sendCommandAndGetResult| uses |sendCommand| and // |goldfish_sync_receive_hostcmd_result| for processing // commands that require replies from the guest. static uint64_t sendCommandAndGetResult(uint64_t cmd, uint64_t handle, uint64_t time_arg, uint64_t hostcmd_handle) { // queue a signal to the device GoldfishSyncCommandQueue::hostSignal (cmd, handle, time_arg, hostcmd_handle); CommandWaitInfo* waitInfo = allocWait(hostcmd_handle); uint64_t res; { AutoLock lock(waitInfo->lock); while (!waitInfo->done) { waitInfo->cvDone.wait(&waitInfo->lock); } res = waitInfo->return_value; } freeWait(hostcmd_handle); return res; } // Goldfish sync host-side interface implementation///////////////////////////// uint64_t goldfish_sync_create_timeline() { return sendCommandAndGetResult(CMD_CREATE_SYNC_TIMELINE, 0, 0, next_unique_id()); } int goldfish_sync_create_fence(uint64_t timeline, uint32_t pt) { return (int)sendCommandAndGetResult(CMD_CREATE_SYNC_FENCE, timeline, pt, next_unique_id()); } void goldfish_sync_timeline_inc(uint64_t timeline, uint32_t howmuch) { sendCommand(CMD_SYNC_TIMELINE_INC, timeline, howmuch); } void goldfish_sync_destroy_timeline(uint64_t timeline) { sendCommand(CMD_DESTROY_SYNC_TIMELINE, timeline, 0); } void goldfish_sync_register_trigger_wait(trigger_wait_fn_t f) { if (goldfish_sync_device_exists()) { sGoldfishSyncHwFuncs->registerTriggerWait(f); } } bool goldfish_sync_device_exists() { // The idea here is that the virtual device should set // sGoldfishSyncHwFuncs. If it didn't do that, we take // that to mean there is no virtual device. return sGoldfishSyncHwFuncs != NULL; } void goldfish_sync_set_hw_funcs(GoldfishSyncDeviceInterface* hw_funcs) { sGoldfishSyncHwFuncs = hw_funcs; GoldfishSyncCommandQueue::setQueueCommand (sGoldfishSyncHwFuncs->doHostCommand); }