C++程序  |  261行  |  8.59 KB

/*
 * Copyright (C) 2017 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 "common/vsoc/lib/region_view.h"

#define LOG_TAG "vsoc: region_host"

#include <stdio.h>
#include <string.h>
#include <linux/futex.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>

#include <iomanip>
#include <sstream>
#include <thread>
#include <vector>

#include <glog/logging.h>

#include "common/libs/fs/shared_fd.h"
#include "common/libs/fs/shared_select.h"

using cvd::SharedFD;

namespace {

class HostRegionControl : public vsoc::RegionControl {
 public:
  HostRegionControl(const char* region_name,
                    const SharedFD& incoming_interrupt_fd,
                    const SharedFD& outgoing_interrupt_fd,
                    const SharedFD& shared_memory_fd)
      : region_name_{region_name},
        incoming_interrupt_fd_{incoming_interrupt_fd},
        outgoing_interrupt_fd_{outgoing_interrupt_fd},
        shared_memory_fd_{shared_memory_fd} {}

  int CreateFdScopedPermission(const char* /*managed_region_name*/,
                               uint32_t /*owner_offset*/,
                               uint32_t /*owned_val*/,
                               uint32_t /*begin_offset*/,
                               uint32_t /*end_offset*/) override {
    return -1;
  }

  bool InitializeRegion();

  virtual bool InterruptPeer() override {
    uint64_t one = 1;
    ssize_t rval = outgoing_interrupt_fd_->Write(&one, sizeof(one));
    if (rval != sizeof(one)) {
      LOG(FATAL) << __FUNCTION__ << ": rval (" << rval << ") != sizeof(one))";
      return false;
    }
    return true;
  }

  // Wake the local signal table scanner. Primarily used during shutdown
  virtual void InterruptSelf() override {
    uint64_t one = 1;
    ssize_t rval = incoming_interrupt_fd_->Write(&one, sizeof(one));
    if (rval != sizeof(one)) {
      LOG(FATAL) << __FUNCTION__ << ": rval (" << rval << ") != sizeof(one))";
    }
  }

  virtual void WaitForInterrupt() override {
    // Check then act isn't a problem here: the other side does
    // the following things in exactly this order:
    //   1. exchanges 1 with interrupt_signalled
    //   2. if interrupt_signalled was 0 it increments the eventfd
    // eventfd increments are persistent, so if interrupt_signalled was set
    // back to 1 while we are going to sleep the sleep will return
    // immediately.
    uint64_t missed{};
    cvd::SharedFDSet readset;
    readset.Set(incoming_interrupt_fd_);
    cvd::Select(&readset, NULL, NULL, NULL);
    ssize_t rval = incoming_interrupt_fd_->Read(&missed, sizeof(missed));
    if (rval != sizeof(missed)) {
      LOG(FATAL) << __FUNCTION__ << ": rval (" << rval
                 << ") != sizeof(missed)), are there more than one threads "
                    "waiting for interrupts?";
    }
    if (!missed) {
      LOG(FATAL) << __FUNCTION__ << ": woke with 0 interrupts";
    }
  }

  virtual void* Map() override {
    if (region_base_) {
      return region_base_;
    }
    // Now actually map the region
    region_base_ =
        shared_memory_fd_->Mmap(0, region_size(), PROT_READ | PROT_WRITE,
                                MAP_SHARED, region_desc_.region_begin_offset);
    if (region_base_ == MAP_FAILED) {
      LOG(FATAL) << "mmap failed for offset "
                 << region_desc_.region_begin_offset << " ("
                 << shared_memory_fd_->StrError() << ")";
      region_base_ = nullptr;
    }
    return region_base_;
  }


  virtual int SignalSelf(uint32_t offset) override {
    return syscall(SYS_futex, region_offset_to_pointer<int32_t*>(offset),
                   FUTEX_WAKE, -1, nullptr, nullptr, 0);
  }

  virtual int WaitForSignal(uint32_t offset, uint32_t expected_value) override {
    return syscall(SYS_futex, region_offset_to_pointer<int32_t*>(offset),
                   FUTEX_WAIT, expected_value, nullptr, nullptr, 0);
  }

 protected:
  const char* region_name_{};
  cvd::SharedFD incoming_interrupt_fd_;
  cvd::SharedFD outgoing_interrupt_fd_;
  cvd::SharedFD shared_memory_fd_;
};

// Default path to the ivshmem_server socket. This can vary when we're
// launching multiple CVDs.
constexpr int kMaxSupportedProtocolVersion = 0;

bool HostRegionControl::InitializeRegion() {
  size_t region_name_len = strlen(region_name_);
  if (region_name_len >= VSOC_DEVICE_NAME_SZ) {
    LOG(FATAL) << "Region name length (" << region_name_len << ") not < "
               << VSOC_DEVICE_NAME_SZ;
    return false;
  }
  vsoc_shm_layout_descriptor layout;
  ssize_t rval = shared_memory_fd_->Pread(&layout, sizeof(layout), 0);
  if (rval != sizeof(layout)) {
    LOG(FATAL) << "Unable to read layout, rval=" << rval << " ("
               << shared_memory_fd_->StrError() << ")";
    return false;
  }
  if (layout.major_version != CURRENT_VSOC_LAYOUT_MAJOR_VERSION) {
    LOG(FATAL) << "Incompatible major version: saw " << layout.major_version
               << " wanted " << CURRENT_VSOC_LAYOUT_MAJOR_VERSION;
  }
  std::vector<vsoc_device_region> descriptors;
  descriptors.resize(layout.region_count);
  ssize_t wanted = sizeof(vsoc_device_region) * layout.region_count;
  rval = shared_memory_fd_->Pread(descriptors.data(), wanted,
                                  layout.vsoc_region_desc_offset);
  if (rval != wanted) {
    LOG(FATAL) << "Unable to read region descriptors, rval=" << rval << " ("
               << shared_memory_fd_->StrError() << ")";
    return false;
  }
  for (const auto& desc : descriptors) {
    if (!strcmp(region_name_, desc.device_name)) {
      region_desc_ = desc;
      return true;
    }
  }

  std::ostringstream buf;
  for (const auto& desc : descriptors) {
    buf << " " << desc.device_name;
  }
  LOG(FATAL) << "Region name of " << region_name_
             << " not found among:" << buf.str();
  return false;
}
}  // namespace

std::shared_ptr<vsoc::RegionControl> vsoc::RegionControl::Open(
    const char* region_name, const char* domain) {
  AutoFreeBuffer msg;

  SharedFD region_server =
      SharedFD::SocketLocalClient(domain, false, SOCK_STREAM);
  if (!region_server->IsOpen()) {
    LOG(FATAL) << "Could not contact ivshmem_server ("
               << region_server->StrError() << ")";
    return nullptr;
  }

  // Check server protocol version.
  uint32_t protocol_version;
  ssize_t bytes = region_server->Recv(&protocol_version,
                                      sizeof(protocol_version), MSG_NOSIGNAL);
  if (bytes != sizeof(protocol_version)) {
    LOG(FATAL) << "Failed to recv protocol version; res=" << bytes << " ("
               << region_server->StrError() << ")";
    return nullptr;
  }

  if (protocol_version > kMaxSupportedProtocolVersion) {
    LOG(FATAL) << "Unsupported protocol version " << protocol_version
               << "; max supported version is " << kMaxSupportedProtocolVersion;
    return nullptr;
  }

  // Send requested region.
  int16_t size = strlen(region_name);
  bytes = region_server->Send(&size, sizeof(size), MSG_NOSIGNAL);
  if (bytes != sizeof(size)) {
    LOG(FATAL) << "Failed to send region name length; res=" << bytes << " ("
               << region_server->StrError() << ")";
    return nullptr;
  }

  bytes = region_server->Send(region_name, size, MSG_NOSIGNAL);
  if (bytes != size) {
    LOG(FATAL) << "Failed to send region name; res=" << bytes << " ("
               << region_server->StrError() << ")";
    return nullptr;
  }

  // Receive control sockets.
  uint64_t control_data;
  struct iovec iov {
    &control_data, sizeof(control_data)
  };
  cvd::InbandMessageHeader hdr{};
  hdr.msg_iov = &iov;
  hdr.msg_iovlen = 1;
  SharedFD fds[3];
  bytes = region_server->RecvMsgAndFDs(hdr, 0, &fds);
  if (bytes != sizeof(control_data)) {
    LOG(FATAL) << "Failed to complete handshake; res=" << bytes << " ("
               << region_server->StrError() << ")";
    return nullptr;
  }
  HostRegionControl* rval =
      new HostRegionControl(region_name, fds[0], fds[1], fds[2]);
  if (!rval) {
    return nullptr;
  }
  // Search for the region header
  if (!rval->InitializeRegion()) {
    // We already logged, so we can just bail out.
    return nullptr;
  }
  return std::shared_ptr<RegionControl>(rval);
}