Skip to content

Conversation

@psumbera
Copy link
Contributor

The SysV shared memory allocator in OPcache hardcodes a maximum segment size of 32MB (SEG_ALLOC_SIZE_MAX). With JIT enabled, OPcache reserves 64MB (ZEND_JIT_DEFAULT_BUFFER_SIZE) from the last segment, causing startup to fail with "Insufficient shared memory!".

This patch increases SEG_ALLOC_SIZE_MAX to 64MB so the reserved JIT buffer fits in a single segment. Behavior on other platforms using mmap remains unaffected.

Fixes #20718.

The SysV shared memory allocator in OPcache hardcodes a maximum segment
size of 32MB (SEG_ALLOC_SIZE_MAX). With JIT enabled, OPcache reserves
64MB (ZEND_JIT_DEFAULT_BUFFER_SIZE) from the last segment, causing
startup to fail with "Insufficient shared memory!".

This patch increases SEG_ALLOC_SIZE_MAX to 64MB so the reserved JIT buffer
fits in a single segment. Behavior on other platforms using mmap remains
unaffected.

Fixes php#20718.
@iluuu1994
Copy link
Member

Won't this issue just immediately re-appear when configuring a larger buffer size? E.g. add -d opcache.jit_buffer_size=128M and it's back.

@iluuu1994
Copy link
Member

PHP 8.3 has the same issue, it's just enabled in a different way (opcache.jit_buffer_size has to be set).

@iluuu1994
Copy link
Member

iluuu1994 commented Dec 16, 2025

So, I think the maximum segment size should be bumped way up, at least on 64-bit architectures. Alternatively, we can request a bigger segment size only when JIT is enabled (though I don't really see a benefit over just always allocating bigger chunks). Something like:

diff --git a/ext/opcache/shared_alloc_shm.c b/ext/opcache/shared_alloc_shm.c
index 09a357d189e..2f7c9ba4217 100644
--- a/ext/opcache/shared_alloc_shm.c
+++ b/ext/opcache/shared_alloc_shm.c
@@ -20,6 +20,9 @@
 */
 
 #include "zend_shared_alloc.h"
+#ifdef HAVE_JIT
+# include "jit/zend_jit.h"
+#endif
 
 #ifdef USE_SHM
 
@@ -50,6 +53,23 @@ typedef struct  {
     int shm_id;
 } zend_shared_segment_shm;
 
+#ifdef HAVE_JIT
+static size_t ceil_to_power_of_2(size_t n)
+{
+	ZEND_ASSERT(n);
+
+	size_t m = n;
+	uint32_t r = 0;
+	while (m >>= 1) {
+		r++;
+	}
+	if (n != (((size_t)1) << r)) {
+		r++;
+	}
+	return ((size_t)1) << r;
+}
+#endif
+
 static int create_segments(size_t requested_size, zend_shared_segment_shm ***shared_segments_p, int *shared_segments_count, const char **error_in)
 {
 	int i;
@@ -61,6 +81,12 @@ static int create_segments(size_t requested_size, zend_shared_segment_shm ***sha
 	zend_shared_segment_shm *shared_segments;
 
 	seg_allocate_size = SEG_ALLOC_SIZE_MAX;
+#ifdef HAVE_JIT
+	if (JIT_G(on) && JIT_G(buffer_size) > seg_allocate_size) {
+		/* The JIT buffer must be allocated in a contiguous chunk. */
+		seg_allocate_size = ceil_to_power_of_2(JIT_G(buffer_size));
+	}
+#endif
 	/* determine segment size we _really_ need:
 	 * no more than to include requested_size
 	 */

@psumbera
Copy link
Contributor Author

PHP 8.3 has the same issue, it's just enabled in a different way (opcache.jit_buffer_size has to be set).

Yes, adding opcache.jit_buffer_size=64M into php.ini did the trick.

@psumbera
Copy link
Contributor Author

So, I think the maximum segment size should be bumped way up, at least on 64-bit architectures. Alternatively, we can request a bigger segment size only when JIT is enabled (though I don't really see a benefit over just always allocating bigger chunks). Something like:

With your patch I'm getting "Fatal Error Insufficient shared memory!" error from these two tests:

Zend/tests/return_types/internal_functions001.phpt
ext/zend_test/tests/observer_opline_01.phpt

@psumbera
Copy link
Contributor Author

So, I think the maximum segment size should be bumped way up, at least on 64-bit architectures. Alternatively, we can request a bigger segment size only when JIT is enabled (though I don't really see a benefit over just always allocating bigger chunks). Something like:

With your patch I'm getting "Fatal Error Insufficient shared memory!" error from these two tests:

Using both, your patch and SEG_ALLOC_SIZE_MAX set 64MB allows to pass all tests...l

@iluuu1994
Copy link
Member

I think it's probably better to just bump the limit to something much bigger. Big allocations are not a big concern in 64-bit address space anymore. Maybe @dstogov can comment as the original author.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants