[memfd] Create support class for Linux memfd-backed region.

This creates a new helper class to allocate memfd-backed shared
memory regions with the Linux kernel. The feature has been
supported since at least Linux 3.17, which is sufficient for
a large number of desktop Linux and Android devices.

However, only recent version of GLibc or the Android C library
expose memfd_create(), so keep implementing it as a direct syscall.

NOTE: There is a similar function named memfd_create() inside
      of src/Reactor/ExecutableMemory.cpp, but it is easier to
      keep them separate for now to avoid introducing subtle
      dependency issues in the CMakeLists.txt file.

This will used in future CLs that implement Vulkan external
semaphores and external memory on Linux and Android for SwiftShader.

Bug: b/140419396

Change-Id: I378c34760930d8fef7d0659128ededb5227c323f
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/35128
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Tested-by: David Turner <digit@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 8dd0a2b..7623038 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -203,6 +203,7 @@
 
       data_deps = [
         "tests/GLESUnitTests:swiftshader_unittests",
+        "tests/SystemUnitTests:swiftshader_system_unittests",
       ]
     }
-}
\ No newline at end of file
+}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 924887d..71385bf 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1763,6 +1763,12 @@
     ${CMAKE_CURRENT_SOURCE_DIR}/include/vulkan/*.h}
 )
 
+if (LINUX OR ANDROID)
+    list(APPEND VULKAN_LIST
+        ${SOURCE_DIR}/System/Linux/MemFd.cpp
+	${SOURCE_DIR}/System/Linux/MemFd.hpp)
+endif(LINUX OR ANDROID)
+
 ###########################################################
 # Append OS specific files to lists
 ###########################################################
diff --git a/src/Android.bp b/src/Android.bp
index b74589b..a805366 100644
--- a/src/Android.bp
+++ b/src/Android.bp
@@ -555,6 +555,7 @@
         "System/CPUID.cpp",
         "System/Configurator.cpp",
         "System/Half.cpp",
+        "System/Linux/MemFd.cpp",
         "System/Math.cpp",
         "System/Memory.cpp",
         "System/Socket.cpp",
diff --git a/src/System/BUILD.gn b/src/System/BUILD.gn
index 9976e08..ebd31e8 100644
--- a/src/System/BUILD.gn
+++ b/src/System/BUILD.gn
@@ -27,6 +27,11 @@
     "Socket.hpp",
     "Timer.hpp",
   ]
+  if (is_linux || is_android) {
+    sources += [
+      "Linux/MemFd.hpp",
+    ]
+  }
 }
 
 swiftshader_source_set("System") {
@@ -39,6 +44,11 @@
     "Memory.cpp",
     "Timer.cpp",
   ]
+  if (is_linux || is_android) {
+    sources += [
+      "Linux/MemFd.cpp",
+    ]
+  }
 
   include_dirs = [ ".." ]
   public_deps = [
diff --git a/src/System/Linux/MemFd.cpp b/src/System/Linux/MemFd.cpp
new file mode 100644
index 0000000..553c636
--- /dev/null
+++ b/src/System/Linux/MemFd.cpp
@@ -0,0 +1,119 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// 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 "MemFd.hpp"
+#include "../Debug.hpp"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#ifndef MFD_CLOEXEC
+#define MFD_CLOEXEC       0x0001U
+#endif
+
+#if __aarch64__
+#define __NR_memfd_create 279
+#elif __arm__
+#define __NR_memfd_create 279
+#elif __powerpc64__
+#define __NR_memfd_create 360
+#elif __i386__
+#define __NR_memfd_create 356
+#elif __x86_64__
+#define __NR_memfd_create 319
+#endif /* __NR_memfd_create__ */
+
+namespace linux
+{
+
+MemFd::~MemFd()
+{
+	close();
+}
+
+void MemFd::importFd(int fd)
+{
+	close();
+	fd_ = fd;
+}
+
+int MemFd::exportFd() const
+{
+	if (fd_ < 0)
+	{
+		return fd_;
+	}
+
+	// Duplicate file descriptor while setting the clo-on-exec flag (Linux specific).
+	return ::fcntl(fd_, F_DUPFD_CLOEXEC, 0);
+}
+
+bool MemFd::allocate(const char* name, size_t size)
+{
+	close();
+
+#ifndef __NR_memfd_create
+	sw::trace("memfd_create() not supported on this system!");
+	return false;
+#else
+	// In the event of no system call this returns -1 with errno set
+	// as ENOSYS.
+	fd_ = syscall(__NR_memfd_create, name, MFD_CLOEXEC);
+	if (fd_ < 0)
+	{
+		sw::trace("memfd_create() returned %d: %s", errno, strerror(errno));
+		return false;
+	}
+	// Ensure there is enough space.
+	if (size > 0 && ::ftruncate(fd_, size) < 0)
+	{
+		sw::trace("ftruncate() %lld returned %d: %s", (long long)size, errno, strerror(errno));
+		close();
+		return false;
+	}
+#endif
+	return true;
+}
+
+void MemFd::close()
+{
+	if (fd_ >= 0)
+	{
+		// WARNING: Never retry on close() failure, even with EINTR, see
+		// https://lwn.net/Articles/576478/ for example.
+		int ret = ::close(fd_);
+		if (ret < 0) {
+			sw::trace("MemFd::close() failed with: %s", strerror(errno));
+			assert(false);
+		}
+		fd_ = -1;
+	}
+}
+
+void* MemFd::mapReadWrite(size_t offset, size_t size)
+{
+	void* addr = ::mmap(nullptr, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd_,
+						static_cast<off_t>(offset));
+	return (addr == MAP_FAILED) ? nullptr : addr;
+}
+
+bool MemFd::unmap(void* addr, size_t size)
+{
+	return ::munmap(addr, size) == 0;
+}
+
+}  // namespace linux
diff --git a/src/System/Linux/MemFd.hpp b/src/System/Linux/MemFd.hpp
new file mode 100644
index 0000000..8b88473
--- /dev/null
+++ b/src/System/Linux/MemFd.hpp
@@ -0,0 +1,73 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// 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 MEMFD_LINUX
+#define MEMFD_LINUX
+
+#include <cstddef>
+
+// Implementation of shared-memory regions backed by memfd_create(), which
+// unfortunately is not exported by older GLibc versions, though it has been
+// supported by the Linux kernel since 3.17 (good enough for Android and desktop
+// Linux).
+
+namespace linux
+{
+
+class MemFd {
+public:
+	MemFd() = default;
+
+	MemFd(const char* name, size_t size) : MemFd() {
+		allocate(name, size);
+	}
+
+	~MemFd();
+
+	// Return true iff the region is valid/allocated.
+	bool isValid() const { return fd_ >= 0; }
+
+	// Return region's internal file descriptor value. Useful for mapping.
+	int fd() const { return fd_; }
+
+	// Set the internal handle to |fd|.
+	void importFd(int fd);
+
+	// Return a copy of this instance's file descriptor (with CLO_EXEC set).
+	int exportFd() const;
+
+	// Implement memfd_create() through direct syscalls if possible.
+    // On success, return true and sets |fd| accordingly. On failure, return
+    // false and sets errno.
+	bool allocate(const char* name, size_t size);
+
+	// Map a segment of |size| bytes from |offset| from the region. 
+	// Both |offset| and |size| should be page-aligned. Returns nullptr/errno
+	// on failure.
+	void* mapReadWrite(size_t offset, size_t size);
+
+	// Unmap a region segment starting at |addr| of |size| bytes.
+	// Both |addr| and |size| should be page-aligned. Returns true on success
+	// or false/errno on failure.
+	bool unmap(void* addr, size_t size);
+
+	void close();
+
+private:
+	int fd_ = -1;
+};
+
+}  // namespace linux
+
+#endif  // MEMFD_LINUX
diff --git a/tests/SystemUnitTests/BUILD.gn b/tests/SystemUnitTests/BUILD.gn
new file mode 100644
index 0000000..8bebd4d
--- /dev/null
+++ b/tests/SystemUnitTests/BUILD.gn
@@ -0,0 +1,34 @@
+# Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+#
+# 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.
+
+import("//testing/test.gni")
+
+test("swiftshader_system_unittests") {
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+    "../../src/System",
+  ]
+
+  sources = [
+    "//gpu/swiftshader_tests_main.cc",
+    "unittests.cpp",
+  ]
+
+  include_dirs = [
+    "../../src"
+  ]
+}
diff --git a/tests/SystemUnitTests/unittests.cpp b/tests/SystemUnitTests/unittests.cpp
new file mode 100644
index 0000000..53ff9ec
--- /dev/null
+++ b/tests/SystemUnitTests/unittests.cpp
@@ -0,0 +1,88 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// 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 "System/Memory.hpp"
+#ifdef __linux__
+#include "System/Linux/MemFd.hpp"
+#endif
+
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include <cstdlib>
+
+using namespace sw;
+
+#ifdef __linux__
+TEST(MemFd, DefaultConstructor) {
+	linux::MemFd memfd;
+	ASSERT_FALSE(memfd.isValid());
+	ASSERT_EQ(-1, memfd.exportFd());
+}
+
+TEST(MemFd, AllocatingConstructor) {
+	const size_t kRegionSize = sw::memoryPageSize() * 8;
+	linux::MemFd memfd("test-region", kRegionSize);
+	ASSERT_TRUE(memfd.isValid());
+	ASSERT_GE(memfd.fd(), 0);
+	void* addr = memfd.mapReadWrite(0, kRegionSize);
+	ASSERT_TRUE(addr);
+	memfd.unmap(addr, kRegionSize);
+}
+
+TEST(MemFd, ExplicitAllocation) {
+	const size_t kRegionSize = sw::memoryPageSize() * 8;
+	linux::MemFd memfd;
+	ASSERT_FALSE(memfd.isValid());
+	ASSERT_EQ(-1, memfd.exportFd());
+	ASSERT_TRUE(memfd.allocate("test-region", kRegionSize));
+	ASSERT_TRUE(memfd.isValid());
+}
+
+TEST(MemFd, Close) {
+	const size_t kRegionSize = sw::memoryPageSize() * 8;
+	linux::MemFd memfd("test-region", kRegionSize);
+	ASSERT_TRUE(memfd.isValid());
+	int fd = memfd.exportFd();
+	memfd.close();
+	ASSERT_FALSE(memfd.isValid());
+	ASSERT_EQ(-1, memfd.exportFd());
+	::close(fd);
+}
+
+TEST(MemFd, ExportImportFd) {
+	const size_t kRegionSize = sw::memoryPageSize() * 8;
+	linux::MemFd memfd("test-region1", kRegionSize);
+	auto* addr = reinterpret_cast<uint8_t*>(memfd.mapReadWrite(0, kRegionSize));
+	ASSERT_TRUE(addr);
+	for (size_t n = 0; n < kRegionSize; ++n)
+	{
+		addr[n] = static_cast<uint8_t>(n);
+	}
+	int fd = memfd.exportFd();
+	ASSERT_TRUE(memfd.unmap(addr, kRegionSize));
+	memfd.close();
+
+	linux::MemFd memfd2;
+	memfd2.importFd(fd);
+	ASSERT_TRUE(memfd2.isValid());
+	addr = reinterpret_cast<uint8_t*>(memfd2.mapReadWrite(0, kRegionSize));
+	ASSERT_TRUE(addr);
+	for (size_t n = 0; n < kRegionSize; ++n)
+	{
+		ASSERT_EQ(addr[n], static_cast<uint8_t>(n)) << "# " << n;
+	}
+	ASSERT_TRUE(memfd2.unmap(addr, kRegionSize));
+}
+#endif  // __linux__