Gralloc region

Adds the layout and the region view implementation

Test: Builds
Bug: 64158954
Change-Id: I6f6cab5545ea920f9e3d3347ff2f13c3b3c93ed5
diff --git a/common/vsoc/lib/gralloc_layout.cpp b/common/vsoc/lib/gralloc_layout.cpp
new file mode 100644
index 0000000..9be790a
--- /dev/null
+++ b/common/vsoc/lib/gralloc_layout.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+// Define some of the string constants associated with the region layout.
+#include "common/vsoc/shm/gralloc_layout.h"
+
+namespace vsoc {
+namespace layout {
+
+namespace gralloc {
+
+const char* GrallocManagerLayout::region_name = "gralloc_manager";
+const char* GrallocBufferLayout::region_name = "gralloc_memory";
+
+}  // gralloc
+}  // layout
+}  // vsoc
diff --git a/common/vsoc/shm/BUILD b/common/vsoc/shm/BUILD
index 78772b8..6d5d0b6 100644
--- a/common/vsoc/shm/BUILD
+++ b/common/vsoc/shm/BUILD
@@ -4,6 +4,7 @@
         "base.h",
         "circqueue.h",
         "e2e_test_region_layout.h",
+        "gralloc_layout.h",
         "graphics.h",
         "lock.h",
         "version.h",
diff --git a/common/vsoc/shm/gralloc_layout.h b/common/vsoc/shm/gralloc_layout.h
new file mode 100644
index 0000000..37e5f8a
--- /dev/null
+++ b/common/vsoc/shm/gralloc_layout.h
@@ -0,0 +1,69 @@
+#pragma once
+/*
+ * 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.
+ */
+
+// Base macros for all layout structures.
+
+#include "common/vsoc/shm/base.h"
+#include "common/vsoc/shm/graphics.h"
+#include "common/vsoc/shm/lock.h"
+#include "common/vsoc/shm/version.h"
+#include "uapi/vsoc_shm.h"
+
+// Memory layout for the gralloc manager region.
+
+namespace vsoc {
+namespace layout {
+
+namespace gralloc {
+
+struct BufferEntry : public Base {
+  uint32_t owned_by;
+  vsoc_reg_off_t buffer_begin;
+  vsoc_reg_off_t buffer_end;
+
+  PixelFormatRegister pixel_format;
+  uint32_t stride;
+  uint32_t width;
+  uint32_t height;
+
+  uint32_t buffer_size() {
+    return buffer_end - buffer_begin;
+  }
+};
+ASSERT_SHM_COMPATIBLE(BufferEntry, gralloc);
+
+struct GrallocBufferLayout : public RegionLayout {
+  static const char* region_name;
+};
+ASSERT_SHM_COMPATIBLE(GrallocBufferLayout, gralloc);
+
+struct GrallocManagerLayout : public RegionLayout {
+  static const char* region_name;
+  typedef GrallocBufferLayout ManagedRegion;
+
+  uint32_t allocated_buffer_memory;
+  uint32_t buffer_count;
+  // Make sure this isn't the first field
+  GuestLock new_buffer_lock;
+  // Needs to be last entry
+  BufferEntry buffers_table[1];
+};
+ASSERT_SHM_COMPATIBLE(GrallocManagerLayout, gralloc);
+
+} // namespace gralloc
+} // namespace layout
+} // namespace vsoc
diff --git a/common/vsoc/shm/version.h b/common/vsoc/shm/version.h
index c4c3a5c..e9c081b 100644
--- a/common/vsoc/shm/version.h
+++ b/common/vsoc/shm/version.h
@@ -47,6 +47,8 @@
 //     #ifdef, etc is absolutely forbidden in this file and highly discouraged
 //     in the other vsoc/shm files.
 
+#include <cstdint>
+
 namespace vsoc {
 namespace layout {
 namespace version_info {
@@ -90,6 +92,17 @@
 }  // namespace constant_values
 }  // namespace multi_region
 
+// Versioning information for gralloc.h
+// Changes to these structures will affect only the gralloc region
+namespace gralloc {
+namespace {
+const uint32_t version = 0;
+}
+static const std::size_t BufferEntry_size = 28;
+static const std::size_t GrallocManagerLayout_size = 76;
+static const std::size_t GrallocBufferLayout_size = 1;
+}  // namespace gralloc
+
 // Versioning information for e2e_test_region.h
 // Changes to these structures will affect only the e2e_test_region
 namespace e2e_test {
diff --git a/guest/vsoc/gralloc/Android.mk b/guest/vsoc/gralloc/Android.mk
new file mode 100644
index 0000000..0d4ffbe
--- /dev/null
+++ b/guest/vsoc/gralloc/Android.mk
@@ -0,0 +1,45 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libvsoc_gralloc
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+    ../../../common/vsoc/lib/gralloc_layout.cpp \
+    gralloc_region.cpp
+
+LOCAL_C_INCLUDES += \
+    device/google/cuttlefish_common/guest/vsoc/gralloc \
+    device/google/cuttlefish_common \
+    device/google/cuttlefish_kernel \
+    system/core/base/include
+
+LOCAL_CFLAGS := \
+    -DLOG_TAG=\"libvsoc_gralloc\" \
+    -Wno-missing-field-initializers \
+    -Wall -Werror
+
+LOCAL_SHARED_LIBRARIES := \
+    libbase \
+    libcuttlefish_auto_resources \
+    libcuttlefish_fs \
+    liblog \
+    libvsoc
+
+LOCAL_VENDOR_MODULE := true
+include $(BUILD_SHARED_LIBRARY)
diff --git a/guest/vsoc/gralloc/gralloc_region.cpp b/guest/vsoc/gralloc/gralloc_region.cpp
new file mode 100644
index 0000000..5c5fcb0
--- /dev/null
+++ b/guest/vsoc/gralloc/gralloc_region.cpp
@@ -0,0 +1,164 @@
+/*
+ * 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 "gralloc_region.h"
+
+#include <atomic>
+#include <common/vsoc/lib/lock_guard.h>
+#include <log/log.h>
+#include <sys/types.h>
+#include <uapi/vsoc_shm.h>
+
+using vsoc::gralloc::GrallocRegion;
+using vsoc::layout::gralloc::BufferEntry;
+using vsoc::layout::gralloc::GrallocBufferLayout;
+using vsoc::layout::gralloc::GrallocManagerLayout;
+
+namespace {
+template <typename T>
+inline T gralloc_align(T val) {
+  return (val + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+}
+
+// Use the thread id to identify the original creator of a buffer.
+inline uint32_t gralloc_owned_value() {
+  return gettid();
+}
+
+}  // namespace
+
+GrallocRegion::GrallocRegion() {
+  // The construction in the singleton is thread safe, so we call Open here to
+  // make sure it opens thread safe too. The singleton will return null if the
+  // region failed to open.
+  Open();
+}
+
+bool GrallocRegion::Open(const char* domain) {
+  if (is_open_) {
+    return true;
+  }
+  if (!vsoc::ManagerRegionView<GrallocManagerLayout>::Open()) {
+    return false;
+  }
+  std::shared_ptr<vsoc::RegionControl> managed_region =
+      vsoc::RegionControl::Open(
+          GrallocManagerLayout::ManagedRegion::region_name, domain);
+  if (!managed_region) {
+    LOG_FATAL("Unable to open managed region");
+    return false;
+  }
+  offset_of_buffer_memory_ = gralloc_align<vsoc_reg_off_t>(
+      managed_region->region_desc().offset_of_region_data);
+  total_buffer_memory_ =
+      managed_region->region_size() - offset_of_buffer_memory_;
+
+  // TODO(jemoreira): Handle the case of unexpected values in the region.
+  is_open_ = true;
+  return true;
+}
+
+int GrallocRegion::AllocateBuffer(size_t size, uint32_t* begin_offset) {
+  size = gralloc_align<size_t>(size);
+  // Cache the value of buffer_count in shared memory.
+  uint32_t buffer_count_local = 0;
+  {
+    vsoc::LockGuard<vsoc::layout::GuestLock>(&data()->new_buffer_lock);
+    buffer_count_local = data()->buffer_count;
+  }
+  // Find a free buffer entry of the appropriate size.
+  for (uint32_t idx = 0; idx < buffer_count_local; ++idx) {
+    BufferEntry& entry = data()->buffers_table[idx];
+    if (entry.owned_by == VSOC_REGION_FREE && entry.buffer_size() == size) {
+      int fd = control_->CreateFdScopedPermission(
+          GrallocManagerLayout::ManagedRegion::region_name,
+          pointer_to_region_offset(&entry.owned_by),
+          gralloc_owned_value(),
+          entry.buffer_begin,
+          entry.buffer_end);
+      if (fd >= 0) {
+        if (begin_offset) {
+          *begin_offset = entry.buffer_begin;
+        }
+        return fd;
+      }
+    }
+  }
+
+  // We couldn't find any suitable buffer, create one
+  {
+    vsoc::LockGuard<vsoc::layout::GuestLock>(&data()->new_buffer_lock);
+    // don't use the cached value here!!!
+    uint32_t idx = data()->buffer_count;
+    if (pointer_to_region_offset(&data()->buffers_table[idx + 1]) >
+        control_->region_size()) {
+      ALOGE(
+          "Out of memory in gralloc_manager (total: %d, used: %d, "
+          "requested: %d)",
+          control_->region_size(),
+          pointer_to_region_offset(&data()->buffers_table[idx]),
+          sizeof(data()->buffers_table[idx]));
+      return -ENOMEM;
+    }
+    if (total_buffer_memory_ - data()->allocated_buffer_memory < size) {
+      ALOGE(
+          "Out of memory in gralloc_memory (total: %d, used: %d, requested: %d)",
+          total_buffer_memory_,
+          data()->allocated_buffer_memory,
+          size);
+      return -ENOMEM;
+    }
+    // Initialize the buffer entry and acquire ownership
+    // Do it before increasing buffer_count so that another thread looking for
+    // free entries doesn't find this one
+    BufferEntry& new_entry = data()->buffers_table[idx];
+    new_entry.buffer_begin =
+        offset_of_buffer_memory_ + data()->allocated_buffer_memory;
+    data()->allocated_buffer_memory += size;
+    new_entry.buffer_end = new_entry.buffer_begin + size;
+    int fd = control_->CreateFdScopedPermission(
+        GrallocManagerLayout::ManagedRegion::region_name,
+        pointer_to_region_offset(&new_entry.owned_by),
+        gralloc_owned_value(),
+        new_entry.buffer_begin,
+        new_entry.buffer_end);
+    if (fd < 0) {
+      LOG_FATAL(
+          "Unexpected error while creating fd scoped permission over "
+          "uncontested memory: %s",
+          strerror(-fd));
+      return fd;
+    }
+    // Increment buffer_count now that the entry can't be taken from us
+    data()->buffer_count++;
+    if (begin_offset) {
+      *begin_offset = new_entry.buffer_begin;
+    }
+    return fd;
+  }
+}
+
+/* static */
+// The C++03 standard does not guarantee this singleton implemention to be
+// thread safe, however magic statics are part of the gcc compiler since
+// version 4.3.
+GrallocRegion* GrallocRegion::GetInstance() {
+  static GrallocRegion singleton;
+  if (!singleton.is_open_) {
+    return NULL;
+  }
+  return &singleton;
+}
diff --git a/guest/vsoc/gralloc/gralloc_region.h b/guest/vsoc/gralloc/gralloc_region.h
new file mode 100644
index 0000000..f8098db
--- /dev/null
+++ b/guest/vsoc/gralloc/gralloc_region.h
@@ -0,0 +1,50 @@
+#pragma once
+
+/*
+ * 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/shm/gralloc_layout.h>
+#include <guest/vsoc/lib/manager_region_view.h>
+#include <stdlib.h>
+
+namespace vsoc {
+namespace gralloc {
+
+class GrallocRegion : public vsoc::ManagerRegionView<
+                          vsoc::layout::gralloc::GrallocManagerLayout> {
+ public:
+  // Allocates a gralloc buffer of (at least) the specified size. Returns a file
+  // descriptor that exposes the buffer when mmapped from 0 to (the page
+  // aligned) size (and fails to mmap anything outside of that range) or a
+  // negative number in case of error (e.g not enough free memory left).
+  // TODO(jemoreira): Include debug info like stride, width, height, etc
+  int AllocateBuffer(size_t size, uint32_t* begin_offset = nullptr);
+
+  static GrallocRegion* GetInstance();
+ protected:
+  GrallocRegion();
+  GrallocRegion(const GrallocRegion&);
+  GrallocRegion& operator=(const GrallocRegion&);
+
+  bool Open(const char* domain = nullptr);
+
+  vsoc_reg_off_t offset_of_buffer_memory_{};
+  uint32_t total_buffer_memory_{};
+  bool is_open_{false};
+};
+
+} // namespace gralloc
+} // namespace vsoc
diff --git a/guest/vsoc/lib/region_control.cpp b/guest/vsoc/lib/region_control.cpp
index d4e2555..cf48218 100644
--- a/guest/vsoc/lib/region_control.cpp
+++ b/guest/vsoc/lib/region_control.cpp
@@ -99,9 +99,9 @@
   if (retval) {
     int errno_ = errno;
     close(managed_region_fd);
-    if (errno != EBUSY) {
-      LOG(FATAL) << "Unable to create fd scoped permission (" << strerror(errno)
-                 << ")";
+    if (errno_ != EBUSY) {
+      LOG(FATAL) << "Unable to create fd scoped permission ("
+                 << strerror(errno_) << ")";
     }
     return -errno_;
   }