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

Message ID 20230214093736.4067033-3-chenghaoyang@google.com
State Superseded
Headers show
Series
  • Add HeapAllocator
Related show

Commit Message

Harvey Yang Feb. 14, 2023, 9:37 a.m. UTC
If DmaHeap is not valid, fall back to UdmaHeap to allocate buffers.

Signed-off-by: Harvey Yang <chenghaoyang@chromium.org>
---
 include/libcamera/meson.build    |   1 +
 include/libcamera/udma_heap.h    |  20 ++++++
 src/libcamera/heap_allocator.cpp |   3 +
 src/libcamera/meson.build        |   1 +
 src/libcamera/udma_heap.cpp      | 117 +++++++++++++++++++++++++++++++
 5 files changed, 142 insertions(+)
 create mode 100644 include/libcamera/udma_heap.h
 create mode 100644 src/libcamera/udma_heap.cpp

Patch
diff mbox series

diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build
index f486630a..a1414a57 100644
--- a/include/libcamera/meson.build
+++ b/include/libcamera/meson.build
@@ -19,6 +19,7 @@  libcamera_public_headers = files([
     'request.h',
     'stream.h',
     'transform.h',
+    'udma_heap.h',
 ])
 
 subdir('base')
diff --git a/include/libcamera/udma_heap.h b/include/libcamera/udma_heap.h
new file mode 100644
index 00000000..3ae46dc7
--- /dev/null
+++ b/include/libcamera/udma_heap.h
@@ -0,0 +1,20 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023, Ideas on Board Oy
+ *
+ * udma_heap.h - Udma Heap implementation.
+ */
+
+#include <libcamera/heap.h>
+
+namespace libcamera {
+
+class UdmaHeap : public Heap
+{
+public:
+	UdmaHeap();
+	~UdmaHeap();
+	UniqueFD alloc(const char *name, std::size_t size) override;
+};
+
+} /* namespace libcamera */
diff --git a/src/libcamera/heap_allocator.cpp b/src/libcamera/heap_allocator.cpp
index 594b1d6a..179c2c21 100644
--- a/src/libcamera/heap_allocator.cpp
+++ b/src/libcamera/heap_allocator.cpp
@@ -18,6 +18,7 @@ 
 #include <libcamera/base/log.h>
 
 #include <libcamera/dma_heap.h>
+#include <libcamera/udma_heap.h>
 
 namespace libcamera {
 
@@ -26,6 +27,8 @@  LOG_DEFINE_CATEGORY(HeapAllocator)
 HeapAllocator::HeapAllocator()
 {
 	heap_ = std::make_unique<DmaHeap>();
+	if (!isValid())
+		heap_ = std::make_unique<UdmaHeap>();
 }
 
 HeapAllocator::~HeapAllocator() = default;
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index ee586c0d..bce26d4b 100644
--- a/src/libcamera/meson.build
+++ b/src/libcamera/meson.build
@@ -45,6 +45,7 @@  libcamera_sources = files([
     'stream.cpp',
     'sysfs.cpp',
     'transform.cpp',
+    'udma_heap.cpp',
     'v4l2_device.cpp',
     'v4l2_pixelformat.cpp',
     'v4l2_subdevice.cpp',
diff --git a/src/libcamera/udma_heap.cpp b/src/libcamera/udma_heap.cpp
new file mode 100644
index 00000000..050ab80f
--- /dev/null
+++ b/src/libcamera/udma_heap.cpp
@@ -0,0 +1,117 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023, Ideas on Board Oy
+ *
+ * dma_heap.h - Dma Heap implementation.
+ */
+
+#include <libcamera/udma_heap.h>
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <linux/udmabuf.h>
+
+#include <libcamera/base/log.h>
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(UdmaHeap)
+
+UdmaHeap::UdmaHeap()
+{
+	int ret = ::open("/dev/udmabuf", O_RDWR, 0);
+	if (ret < 0) {
+		ret = errno;
+		LOG(UdmaHeap, Error)
+			<< "Failed to open allocator: " << strerror(ret);
+
+		if (ret == EACCES) {
+			LOG(UdmaHeap, Info)
+				<< "Consider making /dev/udmabuf accessible by the video group";
+			LOG(UdmaHeap, Info)
+				<< "Alternatively, add your user to the kvm group.";
+		}
+
+	} else {
+		handle_ = UniqueFD(ret);
+	}
+}
+
+UdmaHeap::~UdmaHeap() = default;
+
+UniqueFD UdmaHeap::alloc(const char *name, std::size_t size)
+{
+	if (!isValid()) {
+		LOG(UdmaHeap, Fatal) << "Allocation attempted without allocator" << name;
+		return {};
+	}
+
+	int ret;
+
+	ret = memfd_create(name, MFD_ALLOW_SEALING);
+	if (ret < 0) {
+		ret = errno;
+		LOG(UdmaHeap, Error)
+			<< "Failed to allocate memfd storage: "
+			<< strerror(ret);
+		return {};
+	}
+
+	UniqueFD memfd(ret);
+
+	ret = ftruncate(memfd.get(), size);
+	if (ret < 0) {
+		ret = errno;
+		LOG(UdmaHeap, Error)
+			<< "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(UdmaHeap, Error)
+			<< "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(UdmaHeap, Error)
+			<< "Failed to allocate " << size << " bytes: "
+			<< strerror(ret);
+		return {};
+	}
+
+	/* The underlying memfd is kept as as a reference in the kernel */
+	UniqueFD uDma(ret);
+
+	if (create.size != size)
+		LOG(UdmaHeap, Warning)
+			<< "Allocated " << create.size << " bytes instead of "
+			<< size << " bytes";
+
+	/* Fail if not suitable, the allocation will be free'd by UniqueFD */
+	if (create.size < size)
+		return {};
+
+	LOG(UdmaHeap, Debug) << "Allocated " << create.size << " bytes";
+
+	return uDma;
+}
+
+} /* namespace libcamera */