[libcamera-devel,v7,2/3] libcamera: Add UdmaHeap if DmaHeap is not valid
diff mbox series

Message ID 20230802065449.2904457-3-chenghaoyang@chromium.org
State Superseded
Headers show
Series
  • Add HeapAllocator
Related show

Commit Message

Harvey Yang Aug. 2, 2023, 6:51 a.m. UTC
If DmaHeap is not valid, fall back to UdmaHeap to allocate buffers.

Signed-off-by: Harvey Yang <chenghaoyang@chromium.org>
---
 src/libcamera/heap_allocator.cpp | 105 +++++++++++++++++++++++++++++++
 1 file changed, 105 insertions(+)

Patch
diff mbox series

diff --git a/src/libcamera/heap_allocator.cpp b/src/libcamera/heap_allocator.cpp
index 2a825d93..1caa1469 100644
--- a/src/libcamera/heap_allocator.cpp
+++ b/src/libcamera/heap_allocator.cpp
@@ -11,10 +11,14 @@ 
 #include <array>
 #include <fcntl.h>
 #include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include <unistd.h>
 
 #include <linux/dma-buf.h>
 #include <linux/dma-heap.h>
+#include <linux/udmabuf.h>
 
 #include <libcamera/base/log.h>
 
@@ -84,6 +88,18 @@  public:
 	UniqueFD alloc(const char *name, std::size_t size) override;
 };
 
+/**
+ * \class UdmaHeap
+ * \brief The derived class of Heap with Udmabuf implementation
+ */
+class UdmaHeap : public Heap
+{
+public:
+	UdmaHeap();
+	~UdmaHeap();
+	UniqueFD alloc(const char *name, std::size_t size) override;
+};
+
 /**
  * \brief Construct a DmaHeap with a list of |dmaHeapNames|
  */
@@ -98,6 +114,7 @@  DmaHeap::DmaHeap()
 			continue;
 		}
 
+		LOG(HeapAllocator, Info) << "Using DmaHeap allocator";
 		handle_ = UniqueFD(ret);
 		break;
 	}
@@ -138,6 +155,92 @@  UniqueFD DmaHeap::alloc(const char *name, std::size_t size)
 	return allocFd;
 }
 
+/**
+ * \brief Construct a UdmaHeap with `/udev/udmabuf`.
+ */
+UdmaHeap::UdmaHeap()
+{
+	int ret = ::open("/dev/udmabuf", O_RDWR);
+	if (ret < 0) {
+		ret = errno;
+		LOG(HeapAllocator, Error)
+			<< "UdmaHeap failed to open allocator: " << strerror(ret);
+		return;
+	}
+
+	LOG(HeapAllocator, Info) << "Using UdmaHeap allocator";
+	handle_ = UniqueFD(ret);
+}
+
+UdmaHeap::~UdmaHeap() = default;
+
+UniqueFD UdmaHeap::alloc(const char *name, std::size_t size)
+{
+	int ret = memfd_create(name, MFD_ALLOW_SEALING);
+	if (ret < 0) {
+		ret = errno;
+		LOG(HeapAllocator, Error)
+			<< "UdmaHeap failed to allocate memfd storage: "
+			<< strerror(ret);
+		return {};
+	}
+
+	UniqueFD memfd(ret);
+
+	ret = ftruncate(memfd.get(), size);
+	if (ret < 0) {
+		ret = errno;
+		LOG(HeapAllocator, Error)
+			<< "UdmaHeap failed to set memfd size: " << strerror(ret);
+		return {};
+	}
+
+	/* UdmaHeap Buffers *must* have the F_SEAL_SHRINK seal */
+	ret = fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_SHRINK);
+	if (ret < 0) {
+		ret = errno;
+		LOG(HeapAllocator, Error)
+			<< "UdmaHeap failed to seal the memfd: " << strerror(ret);
+		return {};
+	}
+
+	struct udmabuf_create create;
+
+	create.memfd = memfd.get();
+	create.flags = UDMABUF_FLAGS_CLOEXEC;
+	create.offset = 0;
+	create.size = size;
+
+	ret = ::ioctl(handle_.get(), UDMABUF_CREATE, &create);
+	if (ret < 0) {
+		ret = errno;
+		LOG(HeapAllocator, Error)
+			<< "UdmaHeap failed to allocate " << size << " bytes: "
+			<< strerror(ret);
+		return {};
+	}
+
+	if (create.size < size) {
+		LOG(HeapAllocator, Error)
+			<< "UdmaHeap allocated " << create.size << " bytes instead of "
+			<< size << " bytes";
+		return {};
+	}
+
+	if (create.size != size)
+		LOG(HeapAllocator, Warning)
+			<< "UdmaHeap allocated " << create.size << " bytes, "
+			<< "which is greater than requested : " << size << " bytes";
+
+	/* Fail if not suitable, the allocation will be free'd by UniqueFD */
+	LOG(HeapAllocator, Debug) << "UdmaHeap allocated " << create.size << " bytes";
+
+	/* The underlying memfd is kept as as a reference in the kernel */
+	UniqueFD uDma(ret);
+
+	return uDma;
+}
+
 /**
  * \class HeapAllocator
  * \brief The allocator that allocates heap buffers
@@ -160,6 +263,8 @@  UniqueFD DmaHeap::alloc(const char *name, std::size_t size)
 HeapAllocator::HeapAllocator()
 {
 	heap_ = std::make_unique<DmaHeap>();
+	if (!isValid())
+		heap_ = std::make_unique<UdmaHeap>();
 }
 
 HeapAllocator::~HeapAllocator() = default;