From ffb5d0aca30f4730940b6f3265a2f634259ef5b0 Mon Sep 17 00:00:00 2001 From: Anatol Belski Date: Fri, 9 Oct 2015 23:07:10 +0200 Subject: [PATCH] Implemented file_cache_fallback mechanism which is essential as an attempt to fix the "failed to reattach" error on Windows. If file_cache is enabled, Opcache will automaticaly switch to file_cache_only mode in the case a process failed to map the shared segment at the required address. The important small part of the SHM will still be mapped, which allows information exchange between normal processes using SHM and those using the fallback mechanism. This is based on Dmitry's, Matt's and mine ideas. So many thanks for support! --- ext/opcache/ZendAccelerator.c | 15 +++++++++++++++ ext/opcache/ZendAccelerator.h | 9 +++++++++ ext/opcache/shared_alloc_win32.c | 26 +++++++++++++++++++++++++- ext/opcache/zend_accelerator_module.c | 9 ++++++++- ext/opcache/zend_shared_alloc.c | 10 ++++++++++ ext/opcache/zend_shared_alloc.h | 1 + 6 files changed, 68 insertions(+), 2 deletions(-) diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 862a85389fc..e6d7005a8b1 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -108,6 +108,9 @@ zend_accel_shared_globals *accel_shared_globals = NULL; zend_bool accel_startup_ok = 0; static char *zps_failure_reason = NULL; char *zps_api_failure_reason = NULL; +#if ENABLE_FILE_CACHE_FALLBACK +zend_bool fallback_process = 0; /* process uses file cache fallback */ +#endif static zend_op_array *(*accelerator_orig_compile_file)(zend_file_handle *file_handle, int type); static int (*accelerator_orig_zend_stream_open_function)(const char *filename, zend_file_handle *handle ); @@ -2655,6 +2658,15 @@ static int accel_startup(zend_extension *extension) zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - can not reattach to exiting shared memory."); return SUCCESS; break; +#if ENABLE_FILE_CACHE_FALLBACK + case ALLOC_FALLBACK: + zend_shared_alloc_lock(); + fallback_process = 1; + zend_accel_init_auto_globals(); + zend_shared_alloc_unlock(); + goto file_cache_fallback; + break; +#endif } /* from this point further, shared memory is supposed to be OK */ @@ -2682,6 +2694,9 @@ static int accel_startup(zend_extension *extension) zend_accel_init_auto_globals(); #endif } +#if ENABLE_FILE_CACHE_FALLBACK +file_cache_fallback: +#endif /* Override compiler */ accelerator_orig_compile_file = zend_compile_file; diff --git a/ext/opcache/ZendAccelerator.h b/ext/opcache/ZendAccelerator.h index 93344ec0f5a..798b2d7365d 100644 --- a/ext/opcache/ZendAccelerator.h +++ b/ext/opcache/ZendAccelerator.h @@ -139,6 +139,9 @@ extern int lock_file; #define DO_ALLOCA(x) do_alloca(x, use_heap) #define FREE_ALLOCA(x) free_alloca(x, use_heap) +#if defined(HAVE_OPCACHE_FILE_CACHE) && defined(ZEND_WIN32) +# define ENABLE_FILE_CACHE_FALLBACK 1 +#endif #if ZEND_WIN32 typedef unsigned __int64 accel_time_t; @@ -219,6 +222,9 @@ typedef struct _zend_accel_directives { zend_bool file_cache_only; zend_bool file_cache_consistency_checks; #endif +#if ENABLE_FILE_CACHE_FALLBACK + zend_bool file_cache_fallback; +#endif #ifdef HAVE_HUGE_CODE_PAGES zend_bool huge_code_pages; #endif @@ -293,6 +299,9 @@ typedef struct _zend_accel_shared_globals { } zend_accel_shared_globals; extern zend_bool accel_startup_ok; +#if ENABLE_FILE_CACHE_FALLBACK +extern zend_bool fallback_process; +#endif extern zend_accel_shared_globals *accel_shared_globals; #define ZCSG(element) (accel_shared_globals->element) diff --git a/ext/opcache/shared_alloc_win32.c b/ext/opcache/shared_alloc_win32.c index 2a86e6b8144..ce4bacf348d 100644 --- a/ext/opcache/shared_alloc_win32.c +++ b/ext/opcache/shared_alloc_win32.c @@ -146,11 +146,35 @@ static int zend_shared_alloc_reattach(size_t requested_size, char **error_in) return ALLOC_FAILURE; } fclose(fp); - /* Check if the requested address space is free */ if (VirtualQuery(wanted_mapping_base, &info, sizeof(info)) == 0 || info.State != MEM_FREE || info.RegionSize < requested_size) { +#if ENABLE_FILE_CACHE_FALLBACK + if (ZCG(accel_directives).file_cache && ZCG(accel_directives).file_cache_fallback) { + size_t pre_size, wanted_mb_save; + + wanted_mb_save = (size_t)wanted_mapping_base; + + err = ERROR_INVALID_ADDRESS; + zend_win_error_message(ACCEL_LOG_WARNING, "Base address marks unusable memory region (fall-back to file cache)", err); + + pre_size = ZEND_ALIGNED_SIZE(sizeof(zend_smm_shared_globals)) + ZEND_ALIGNED_SIZE(sizeof(zend_shared_segment)) + ZEND_ALIGNED_SIZE(sizeof(void *)) + ZEND_ALIGNED_SIZE(sizeof(int)); + /* Map only part of SHM to have access opcache shared globals */ + mapping_base = MapViewOfFileEx(memfile, FILE_MAP_ALL_ACCESS, 0, 0, pre_size + ZEND_ALIGNED_SIZE(sizeof(zend_accel_shared_globals)), NULL); + if (mapping_base == NULL) { + err = GetLastError(); + zend_win_error_message(ACCEL_LOG_FATAL, "Unable to reattach to opcache shared globals", err); + return ALLOC_FAILURE; + } + accel_shared_globals = (zend_accel_shared_globals *)((char *)((zend_smm_shared_globals *)mapping_base)->app_shared_globals + ((char *)mapping_base - (char *)wanted_mb_save)); + + /* Make this process to use file-cache only */ + ZCG(accel_directives).file_cache_only = 1; + + return ALLOC_FALLBACK; + } +#endif err = ERROR_INVALID_ADDRESS; zend_win_error_message(ACCEL_LOG_FATAL, "Base address marks unusable memory region", err); return ALLOC_FAILURE; diff --git a/ext/opcache/zend_accelerator_module.c b/ext/opcache/zend_accelerator_module.c index 11d631c538d..bd53f6e3735 100644 --- a/ext/opcache/zend_accelerator_module.c +++ b/ext/opcache/zend_accelerator_module.c @@ -309,6 +309,9 @@ ZEND_INI_BEGIN() STD_PHP_INI_ENTRY("opcache.file_cache_only" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_cache_only, zend_accel_globals, accel_globals) STD_PHP_INI_ENTRY("opcache.file_cache_consistency_checks" , "1" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_cache_consistency_checks, zend_accel_globals, accel_globals) #endif +#if ENABLE_FILE_CACHE_FALLBACK + STD_PHP_INI_ENTRY("opcache.file_cache_fallback" , "1" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_cache_fallback, zend_accel_globals, accel_globals) +#endif #ifdef HAVE_HUGE_CODE_PAGES STD_PHP_INI_BOOLEAN("opcache.huge_code_pages" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.huge_code_pages, zend_accel_globals, accel_globals) #endif @@ -733,7 +736,11 @@ static ZEND_FUNCTION(opcache_reset) RETURN_FALSE; } - if (!ZCG(enabled) || !accel_startup_ok || !ZCSG(accelerator_enabled)) { + if ((!ZCG(enabled) || !accel_startup_ok || !ZCSG(accelerator_enabled)) +#if ENABLE_FILE_CACHE_FALLBACK + && !fallback_process +#endif + ) { RETURN_FALSE; } diff --git a/ext/opcache/zend_shared_alloc.c b/ext/opcache/zend_shared_alloc.c index 09da83a6a63..1ad3fd2e28e 100644 --- a/ext/opcache/zend_shared_alloc.c +++ b/ext/opcache/zend_shared_alloc.c @@ -187,6 +187,11 @@ int zend_shared_alloc_startup(size_t requested_size) smm_shared_globals = NULL; return res; } +#if ENABLE_FILE_CACHE_FALLBACK + if (ALLOC_FALLBACK == res) { + return ALLOC_FALLBACK; + } +#endif if (!g_shared_alloc_handler) { /* try memory handlers in order */ @@ -207,6 +212,11 @@ int zend_shared_alloc_startup(size_t requested_size) if (res == SUCCESSFULLY_REATTACHED) { return res; } +#if ENABLE_FILE_CACHE_FALLBACK + if (ALLOC_FALLBACK == res) { + return ALLOC_FALLBACK; + } +#endif shared_segments_array_size = ZSMMG(shared_segments_count) * S_H(segment_type_size)(); diff --git a/ext/opcache/zend_shared_alloc.h b/ext/opcache/zend_shared_alloc.h index 398b64f432a..ec67343e89c 100644 --- a/ext/opcache/zend_shared_alloc.h +++ b/ext/opcache/zend_shared_alloc.h @@ -68,6 +68,7 @@ #define FAILED_REATTACHED 2 #define SUCCESSFULLY_REATTACHED 4 #define ALLOC_FAIL_MAPPING 8 +#define ALLOC_FALLBACK 9 typedef struct _zend_shared_segment { size_t size;