You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
194 lines
6.4 KiB
194 lines
6.4 KiB
// 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 "base/Lookup.h"
|
|
#include "base/ConditionVariable.h"
|
|
#include "base/Lock.h"
|
|
|
|
#include <unordered_map>
|
|
|
|
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<uint64_t, std::unique_ptr<CommandWaitInfo> >
|
|
wait_map;
|
|
|
|
static CommandWaitInfo* allocWait(uint64_t id) {
|
|
AutoLock lock(sCommandReplyLock);
|
|
std::unique_ptr<CommandWaitInfo>& 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);
|
|
}
|
|
|