Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0a0eb2ec authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Implement Ashmen-based metadata buffer"

parents b823e816 4e566ed0
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
sourceFiles = [
    "buffer_hub_base.cpp",
    "buffer_hub_client.cpp",
    "buffer_hub_metadata.cpp",
    "buffer_hub_rpc.cpp",
    "consumer_buffer.cpp",
    "detached_buffer.cpp",
@@ -69,3 +70,10 @@ cc_test {
    name: "buffer_hub-test",
}

cc_test {
    srcs: ["buffer_hub_metadata-test.cpp"],
    static_libs: ["libbufferhub"],
    shared_libs: sharedLibraries,
    header_libs: headerLibraries,
    name: "buffer_hub_metadata-test",
}
+105 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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 <gtest/gtest.h>
#include <private/dvr/buffer_hub_metadata.h>

using android::dvr::BufferHubDefs::IsBufferGained;

namespace android {
namespace dvr {

constexpr size_t kEmptyUserMetadataSize = 0;

class BufferHubMetadataTest : public ::testing::Test {};

TEST_F(BufferHubMetadataTest, Create_UserMetdataSizeTooBig) {
  BufferHubMetadata m1 =
      BufferHubMetadata::Create(std::numeric_limits<uint32_t>::max());
  EXPECT_FALSE(m1.IsValid());
}

TEST_F(BufferHubMetadataTest, Create_Success) {
  BufferHubMetadata m1 = BufferHubMetadata::Create(kEmptyUserMetadataSize);
  EXPECT_TRUE(m1.IsValid());
  EXPECT_NE(m1.metadata_header(), nullptr);
}

TEST_F(BufferHubMetadataTest, Import_Success) {
  BufferHubMetadata m1 = BufferHubMetadata::Create(kEmptyUserMetadataSize);
  EXPECT_TRUE(m1.IsValid());
  EXPECT_NE(m1.metadata_header(), nullptr);

  pdx::LocalHandle h2 = m1.ashmem_handle().Duplicate();
  EXPECT_TRUE(h2.IsValid());

  BufferHubMetadata m2 = BufferHubMetadata::Import(std::move(h2));
  EXPECT_FALSE(h2.IsValid());
  EXPECT_TRUE(m1.IsValid());
  BufferHubDefs::MetadataHeader* mh1 = m1.metadata_header();
  EXPECT_NE(mh1, nullptr);

  // TODO(b/111976433): Update this test once BufferHub state machine gets
  // updated. In the old model, buffer starts in the gained state (i.e.
  // valued 0). In the new model, buffer states in the released state.
  EXPECT_TRUE(IsBufferGained(mh1->fence_state.load()));

  EXPECT_TRUE(m2.IsValid());
  BufferHubDefs::MetadataHeader* mh2 = m2.metadata_header();
  EXPECT_NE(mh2, nullptr);

  // TODO(b/111976433): Update this test once BufferHub state machine gets
  // updated. In the old model, buffer starts in the gained state (i.e.
  // valued 0). In the new model, buffer states in the released state.
  EXPECT_TRUE(IsBufferGained(mh2->fence_state.load()));
}

TEST_F(BufferHubMetadataTest, MoveMetadataInvalidatesOldOne) {
  BufferHubMetadata m1 = BufferHubMetadata::Create(sizeof(int));
  EXPECT_TRUE(m1.IsValid());
  EXPECT_NE(m1.metadata_header(), nullptr);
  EXPECT_TRUE(m1.ashmem_handle().IsValid());
  EXPECT_EQ(m1.user_metadata_size(), sizeof(int));

  BufferHubMetadata m2 = std::move(m1);

  // After the move, the metadata header (a raw pointer) should be reset in the
  // older buffer.
  EXPECT_EQ(m1.metadata_header(), nullptr);
  EXPECT_NE(m2.metadata_header(), nullptr);

  EXPECT_FALSE(m1.ashmem_handle().IsValid());
  EXPECT_TRUE(m2.ashmem_handle().IsValid());

  EXPECT_EQ(m1.user_metadata_size(), 0U);
  EXPECT_EQ(m2.user_metadata_size(), sizeof(int));

  BufferHubMetadata m3{std::move(m2)};

  // After the move, the metadata header (a raw pointer) should be reset in the
  // older buffer.
  EXPECT_EQ(m2.metadata_header(), nullptr);
  EXPECT_NE(m3.metadata_header(), nullptr);

  EXPECT_FALSE(m2.ashmem_handle().IsValid());
  EXPECT_TRUE(m3.ashmem_handle().IsValid());

  EXPECT_EQ(m2.user_metadata_size(), 0U);
  EXPECT_EQ(m3.user_metadata_size(), sizeof(int));
}

}  // namespace dvr
}  // namespace android
+108 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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 <errno.h>
#include <sys/mman.h>

#include <cutils/ashmem.h>
#include <log/log.h>
#include <private/dvr/buffer_hub_metadata.h>

namespace android {
namespace dvr {

namespace {

static const int kAshmemProt = PROT_READ | PROT_WRITE;

}  // namespace

using BufferHubDefs::kMetadataHeaderSize;
using BufferHubDefs::MetadataHeader;

/* static */
BufferHubMetadata BufferHubMetadata::Create(size_t user_metadata_size) {
  // The size the of metadata buffer is used as the "width" parameter during
  // allocation. Thus it cannot overflow uint32_t.
  if (user_metadata_size >=
      (std::numeric_limits<uint32_t>::max() - kMetadataHeaderSize)) {
    ALOGE("BufferHubMetadata::Create: metadata size too big: %zu.",
          user_metadata_size);
    return {};
  }

  const size_t metadata_size = user_metadata_size + kMetadataHeaderSize;
  int fd = ashmem_create_region(/*name=*/"BufferHubMetadata", metadata_size);
  if (fd < 0) {
    ALOGE("BufferHubMetadata::Create: failed to create ashmem region.");
    return {};
  }

  // Hand over the ownership of the fd to a pdx::LocalHandle immediately after
  // the successful return of ashmem_create_region. The ashmem_handle is going
  // to own the fd and to prevent fd leaks during error handling.
  pdx::LocalHandle ashmem_handle{fd};

  if (ashmem_set_prot_region(ashmem_handle.Get(), kAshmemProt) != 0) {
    ALOGE("BufferHubMetadata::Create: failed to set protect region.");
    return {};
  }

  return BufferHubMetadata::Import(std::move(ashmem_handle));
}

/* static */
BufferHubMetadata BufferHubMetadata::Import(pdx::LocalHandle ashmem_handle) {
  if (!ashmem_valid(ashmem_handle.Get())) {
    ALOGE("BufferHubMetadata::Import: invalid ashmem fd.");
    return {};
  }

  size_t metadata_size = ashmem_get_size_region(ashmem_handle.Get());
  size_t user_metadata_size = metadata_size - kMetadataHeaderSize;

  auto metadata_header = static_cast<MetadataHeader*>(
      mmap(nullptr, metadata_size, kAshmemProt, MAP_SHARED, ashmem_handle.Get(),
           /*offset=*/0));
  if (metadata_header == nullptr) {
    ALOGE("BufferHubMetadata::Import: failed to map region.");
    return {};
  }

  return BufferHubMetadata(user_metadata_size, std::move(ashmem_handle),
                           metadata_header);
}

BufferHubMetadata::BufferHubMetadata(size_t user_metadata_size,
                                     pdx::LocalHandle ashmem_handle,
                                     MetadataHeader* metadata_header)
    : user_metadata_size_(user_metadata_size),
      ashmem_handle_(std::move(ashmem_handle)),
      metadata_header_(metadata_header) {}

BufferHubMetadata::~BufferHubMetadata() {
  if (metadata_header_ != nullptr) {
    int ret = munmap(metadata_header_, metadata_size());
    ALOGE_IF(ret != 0,
             "BufferHubMetadata::~BufferHubMetadata: failed to unmap ashmem, "
             "error=%d.",
             errno);
    metadata_header_ = nullptr;
  }
}

}  // namespace dvr
}  // namespace android
+93 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.
 */

#ifndef ANDROID_DVR_BUFFER_HUB_METADATA_H_
#define ANDROID_DVR_BUFFER_HUB_METADATA_H_

#include <pdx/file_handle.h>
#include <private/dvr/buffer_hub_defs.h>

namespace android {
namespace dvr {

class BufferHubMetadata {
 public:
  // Creates a new BufferHubMetadata backed by an ashmem region.
  //
  // @param user_metadata_size Size in bytes of the user defined metadata. The
  //        entire metadata shared memory region to be allocated is the size of
  //        canonical BufferHubDefs::MetadataHeader plus user_metadata_size.
  static BufferHubMetadata Create(size_t user_metadata_size);

  // Imports an existing BufferHubMetadata from an ashmem FD.
  //
  // TODO(b/112338294): Refactor BufferHub to use Binder as its internal IPC
  // backend instead of UDS.
  //
  // @param ashmem_handle Ashmem file handle representing an ashmem region.
  static BufferHubMetadata Import(pdx::LocalHandle ashmem_handle);

  BufferHubMetadata() = default;

  BufferHubMetadata(BufferHubMetadata&& other) { *this = std::move(other); }

  ~BufferHubMetadata();

  BufferHubMetadata& operator=(BufferHubMetadata&& other) {
    if (this != &other) {
      user_metadata_size_ = other.user_metadata_size_;
      other.user_metadata_size_ = 0;

      ashmem_handle_ = std::move(other.ashmem_handle_);

      // The old raw metadata_header_ pointer must be cleared, otherwise the
      // destructor will automatically mummap() the shared memory.
      metadata_header_ = other.metadata_header_;
      other.metadata_header_ = nullptr;
    }
    return *this;
  }

  // Returns true if the metadata is valid, i.e. the metadata has a valid ashmem
  // fd and the ashmem has been mapped into virtual address space.
  bool IsValid() const {
    return ashmem_handle_.IsValid() && metadata_header_ != nullptr;
  }

  size_t user_metadata_size() const { return user_metadata_size_; }
  size_t metadata_size() const {
    return user_metadata_size_ + BufferHubDefs::kMetadataHeaderSize;
  }

  const pdx::LocalHandle& ashmem_handle() const { return ashmem_handle_; }
  BufferHubDefs::MetadataHeader* metadata_header() { return metadata_header_; }

 private:
  BufferHubMetadata(size_t user_metadata_size, pdx::LocalHandle ashmem_handle,
                    BufferHubDefs::MetadataHeader* metadata_header);

  BufferHubMetadata(const BufferHubMetadata&) = delete;
  void operator=(const BufferHubMetadata&) = delete;

  size_t user_metadata_size_ = 0;
  pdx::LocalHandle ashmem_handle_;
  BufferHubDefs::MetadataHeader* metadata_header_ = nullptr;
};

}  // namespace dvr
}  // namespace android

#endif  // ANDROID_DVR_BUFFER_HUB_METADATA_H_