aboutsummarylogtreecommitdiffstats
path: root/0002-ipu6-make-libcamhal-init-process-global.patch
blob: 47f8cfbb762a61e8847abf8c2a302aed7e2e0e54 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
From: Nick Stickney <nstickney@gmail.com>
Date: Wed, 29 Apr 2026
Subject: [PATCH] ipu6: process-global libcamhal init plus shm-race wait

Two cooperating problems addressed:

1. Per-instance halInitialized_ guard. libcamera enumerates twice
   during a single process (probe + match), so kervel's per-instance
   guard didn't prevent a second camera_hal_init(); mInitTimes climbed
   to 2; subsequent re-init lost the per-process SHM segment, and
   later camera_device_open() failed with "No attached camera shared
   memory!". Fix: process-static std::once_flag and drop the
   destructor's camera_hal_deinit() entirely (let the OS reclaim
   libcamhal state at process exit).

2. Boot-time shm key collision with GDM. GDM's greeter spawns its own
   wireplumber under a DynamicUser= transient uid; that wireplumber
   loads libcamera-ipu6 and ends up in libcamhal's init, which calls
   shmget(0x43414d, 8192, 0640). libcamhal hardcodes mode 0640, so
   the resulting segment is owner+group only. When the user logs in
   and their wireplumber runs the same shmget, gdm's segment may
   still be attached (nattch=1) and the user's call returns EACCES.
   libcamhal logs "Fail to allocate shared memory by shmget" and
   continues running but in a broken state -- every later
   camera_device_open fails with "No attached camera shared memory!".

   Fix: before calling camera_hal_init(), poll the shm key directly.
   On EACCES, sleep 100ms and retry, up to a 5s deadline. Once we
   see ENOENT (the conflicting segment is gone) or a successful
   lookup, proceed -- libcamhal's own shmget will then succeed.

Per-camera lifecycle (camera_device_open / camera_device_close in
IPU6CameraData) is unchanged; those remain correctly paired.
---
 src/libcamera/pipeline/ipu6/ipu6.cpp | 50 ++++++++++++++++++++++++----
 1 file changed, 44 insertions(+), 6 deletions(-)

--- a/src/libcamera/pipeline/ipu6/ipu6.cpp
+++ b/src/libcamera/pipeline/ipu6/ipu6.cpp
@@ -10,6 +10,8 @@
  * the Intel PSYS hardware via libcamhal, producing high-quality NV12 output.
  */
 
+#include <atomic>
+#include <chrono>
 #include <algorithm>
 #include <climits>
 #include <cstring>
@@ -24,6 +26,9 @@
 #include <fcntl.h>
 #include <linux/videodev2.h>
 #include <sys/mman.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <errno.h>
 #include <unistd.h>
 
 #include <libcamera/base/log.h>
@@ -151,7 +156,6 @@
 	}
 
 	DmaBufAllocator dmaBufAllocator_;
-	bool halInitialized_ = false;
 };
 
 /* --- IPU6CameraData implementation --- */
@@ -551,10 +555,11 @@
 
 PipelineHandlerIPU6::~PipelineHandlerIPU6()
 {
-	if (halInitialized_) {
-		icamera::camera_hal_deinit();
-		halInitialized_ = false;
-	}
+	/*
+	 * Intentionally do not call icamera::camera_hal_deinit() here.
+	 * libcamhal's per-process SHM segment does not survive a
+	 * deinit/reinit cycle; let the OS reclaim it at process exit.
+	 */
 }
 
 std::unique_ptr<CameraConfiguration>
@@ -649,13 +654,42 @@
 	 * we return false so the 'simple' pipeline handler can fall back
 	 * to software ISP.
 	 */
-	if (!halInitialized_) {
-		int ret = icamera::camera_hal_init();
-		if (ret < 0) {
-			LOG(IPU6, Debug) << "Failed to init camera HAL, falling back";
-			return false;
+	static std::once_flag halInitFlag;
+	static std::atomic<int> halInitResult{ 0 };
+	std::call_once(halInitFlag, [] {
+		/*
+		 * libcamhal uses a fixed SysV shm key (0x43414d == "CAM").
+		 * If another libcamhal-using process (notably GDM's greeter
+		 * wireplumber, running under a DynamicUser= transient uid)
+		 * still holds that segment, our shmget inside camera_hal_init
+		 * will fail with EACCES and libcamhal will silently end up
+		 * in a broken state. Wait up to 5 seconds for the segment
+		 * to be released before letting libcamhal init.
+		 */
+		using namespace std::chrono;
+		auto deadline = steady_clock::now() + seconds(5);
+		for (;;) {
+			int id = shmget(0x43414d, 8192, 0640);
+			int e = errno;
+			if (id >= 0 || e == ENOENT)
+				break;
+			if (e != EACCES) {
+				LOG(IPU6, Warning) << "shmget probe returned errno=" << e
+						   << ", proceeding";
+				break;
+			}
+			if (steady_clock::now() >= deadline) {
+				LOG(IPU6, Warning) << "shm key 0x43414d still held by "
+						      "another uid after 5s, proceeding";
+				break;
+			}
+			std::this_thread::sleep_for(milliseconds(100));
 		}
-		halInitialized_ = true;
+		halInitResult = icamera::camera_hal_init();
+	});
+	if (halInitResult < 0) {
+		LOG(IPU6, Debug) << "Failed to init camera HAL, falling back";
+		return false;
 	}
 
 	int numCameras = icamera::get_number_of_cameras();