mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Fix opcache.huge_code_pages
Building opcache into the main executable breaks opcache.huge_code_pages,
as we were relying on the fact that accel_remap_huge_pages() is not in the
same mapping as the main text segment.
Here I ensure that accel_remap_huge_pages() is placed out of the text
segment, and remap only the text segment. This approach is used in [1].
Closes GH-19388.
[1] 676bb7dec3/large_page-c/large_page.c (L260).
This commit is contained in:
1
NEWS
1
NEWS
@@ -9,6 +9,7 @@ PHP NEWS
|
||||
- Opcache:
|
||||
. Fixed bug GH-19486 (Incorrect opline after deoptimization). (Arnaud)
|
||||
. Fixed bug GH-19601 (Wrong JIT stack setup on aarch64/clang). (Arnaud)
|
||||
. Fixed bug GH-19388 (Broken opcache.huge_code_pages). (Arnaud)
|
||||
|
||||
- PCRE:
|
||||
. Upgraded to pcre2lib from 10.45 to 10.46. (nielsdos)
|
||||
|
||||
@@ -89,6 +89,9 @@
|
||||
#ifndef __has_feature
|
||||
# define __has_feature(x) 0
|
||||
#endif
|
||||
#ifndef __has_include
|
||||
# define __has_include(x) 0
|
||||
#endif
|
||||
|
||||
#if defined(ZEND_WIN32) && !defined(__clang__)
|
||||
# define ZEND_ASSUME(c) __assume(c)
|
||||
@@ -750,6 +753,10 @@ extern "C++" {
|
||||
# define ZEND_SET_ALIGNED(alignment, decl) decl
|
||||
#endif
|
||||
|
||||
#if __has_attribute(section)
|
||||
# define HAVE_ATTRIBUTE_SECTION
|
||||
#endif
|
||||
|
||||
#define ZEND_SLIDE_TO_ALIGNED(alignment, ptr) (((uintptr_t)(ptr) + ((alignment)-1)) & ~((alignment)-1))
|
||||
#define ZEND_SLIDE_TO_ALIGNED16(ptr) ZEND_SLIDE_TO_ALIGNED(Z_UL(16), ptr)
|
||||
|
||||
|
||||
@@ -2974,10 +2974,22 @@ static void accel_globals_dtor(zend_accel_globals *accel_globals)
|
||||
# include <sys/user.h>
|
||||
# define MAP_HUGETLB MAP_ALIGNED_SUPER
|
||||
# endif
|
||||
# if __has_include(<link.h>)
|
||||
# include <link.h>
|
||||
# endif
|
||||
# if __has_include(<elf.h>)
|
||||
# include <elf.h>
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# if defined(MAP_HUGETLB) || defined(MADV_HUGEPAGE)
|
||||
static zend_result accel_remap_huge_pages(void *start, size_t size, size_t real_size, const char *name, size_t offset)
|
||||
# define ZEND_HUGE_PAGE_SIZE (2UL * 1024 * 1024)
|
||||
|
||||
# if (defined(__linux__) || defined(__FreeBSD__)) && (defined(MAP_HUGETLB) || defined(MADV_HUGEPAGE)) && defined(HAVE_ATTRIBUTE_ALIGNED) && defined(HAVE_ATTRIBUTE_SECTION) && __has_include(<link.h>) && __has_include(<elf.h>)
|
||||
static zend_result
|
||||
__attribute__((section(".remap_stub")))
|
||||
__attribute__((aligned(ZEND_HUGE_PAGE_SIZE)))
|
||||
zend_never_inline
|
||||
accel_remap_huge_pages(void *start, size_t size, size_t real_size)
|
||||
{
|
||||
void *ret = MAP_FAILED;
|
||||
void *mem;
|
||||
@@ -3030,94 +3042,113 @@ static zend_result accel_remap_huge_pages(void *start, size_t size, size_t real_
|
||||
|
||||
// Given the MAP_FIXED flag the address can never diverge
|
||||
ZEND_ASSERT(ret == start);
|
||||
zend_mmap_set_name(start, size, "zend_huge_code_pages");
|
||||
|
||||
memcpy(start, mem, real_size);
|
||||
mprotect(start, size, PROT_READ | PROT_EXEC);
|
||||
zend_mmap_set_name(start, size, "zend_huge_code_pages");
|
||||
|
||||
munmap(mem, size);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static int accel_dl_iterate_phdr_callback(struct dl_phdr_info *info, size_t size, void *data) {
|
||||
if (info->dlpi_name == NULL || strcmp(info->dlpi_name, "") == 0) {
|
||||
*((uintptr_t*)data) = info->dlpi_addr;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static zend_result accel_find_program_section(ElfW(Shdr) *section) {
|
||||
|
||||
uintptr_t base_addr;
|
||||
if (dl_iterate_phdr(accel_dl_iterate_phdr_callback, &base_addr) != 1) {
|
||||
zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: executable base address not found");
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
FILE *f = fopen("/proc/self/exe", "r");
|
||||
#elif defined(__FreeBSD__)
|
||||
char path[4096];
|
||||
int mib[4];
|
||||
size_t len = sizeof(path);
|
||||
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_PROC;
|
||||
mib[2] = KERN_PROC_PATHNAME;
|
||||
mib[3] = -1; /* Current process */
|
||||
|
||||
if (sysctl(mib, 4, path, &len, NULL, 0) == -1) {
|
||||
zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: sysctl(KERN_PROC_PATHNAME) failed: %s (%d)",
|
||||
strerror(errno), errno);
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
FILE *f = fopen(path, "r");
|
||||
#endif
|
||||
if (!f) {
|
||||
zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: fopen(/proc/self/exe) failed: %s (%d)",
|
||||
strerror(errno), errno);
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
/* Read ELF header */
|
||||
ElfW(Ehdr) ehdr;
|
||||
if (!fread(&ehdr, sizeof(ehdr), 1, f)) {
|
||||
zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: fread() failed: %s (%d)",
|
||||
strerror(errno), errno);
|
||||
fclose(f);
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
/* Read section headers */
|
||||
ElfW(Shdr) shdrs[ehdr.e_shnum];
|
||||
if (fseek(f, ehdr.e_shoff, SEEK_SET) != 0) {
|
||||
zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: fseek() failed: %s (%d)",
|
||||
strerror(errno), errno);
|
||||
fclose(f);
|
||||
return FAILURE;
|
||||
}
|
||||
if (fread(shdrs, sizeof(shdrs[0]), ehdr.e_shnum, f) != ehdr.e_shnum) {
|
||||
zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: fread() failed: %s (%d)",
|
||||
strerror(errno), errno);
|
||||
fclose(f);
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
/* Find the program section */
|
||||
for (ElfW(Half) idx = 0; idx < ehdr.e_shnum; idx++) {
|
||||
ElfW(Shdr) *sh = &shdrs[idx];
|
||||
uintptr_t start = (uintptr_t)sh->sh_addr + base_addr;
|
||||
zend_accel_error(ACCEL_LOG_DEBUG, "considering section %016" PRIxPTR "-%016" PRIxPTR " vs %016" PRIxPTR "\n", start, start + sh->sh_size, (uintptr_t)accel_find_program_section);
|
||||
if ((uintptr_t)accel_find_program_section >= start && (uintptr_t)accel_find_program_section < start + sh->sh_size) {
|
||||
*section = *sh;
|
||||
section->sh_addr = (ElfW(Addr))start;
|
||||
return SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
static void accel_move_code_to_huge_pages(void)
|
||||
{
|
||||
#if defined(__linux__)
|
||||
FILE *f;
|
||||
long unsigned int huge_page_size = 2 * 1024 * 1024;
|
||||
|
||||
f = fopen("/proc/self/maps", "r");
|
||||
if (f) {
|
||||
long unsigned int start, end, offset, inode;
|
||||
char perm[5], dev[10], name[MAXPATHLEN];
|
||||
int ret;
|
||||
extern char *__progname;
|
||||
char buffer[MAXPATHLEN];
|
||||
|
||||
while (fgets(buffer, MAXPATHLEN, f)) {
|
||||
ret = sscanf(buffer, "%lx-%lx %4s %lx %9s %lu %s\n", &start, &end, perm, &offset, dev, &inode, name);
|
||||
if (ret >= 6) {
|
||||
/* try to find the php text segment and map it into huge pages
|
||||
Lines without 'name' are going to be skipped */
|
||||
if (ret > 6 && perm[0] == 'r' && perm[1] == '-' && perm[2] == 'x' && name[0] == '/' \
|
||||
&& strstr(name, __progname)) {
|
||||
long unsigned int seg_start = ZEND_MM_ALIGNED_SIZE_EX(start, huge_page_size);
|
||||
long unsigned int seg_end = (end & ~(huge_page_size-1L));
|
||||
long unsigned int real_end;
|
||||
|
||||
ret = fscanf(f, "%lx-", &start);
|
||||
if (ret == 1 && start == seg_end + huge_page_size) {
|
||||
real_end = end;
|
||||
seg_end = start;
|
||||
} else {
|
||||
real_end = seg_end;
|
||||
}
|
||||
|
||||
if (seg_end > seg_start) {
|
||||
zend_accel_error(ACCEL_LOG_DEBUG, "remap to huge page %lx-%lx %s \n", seg_start, seg_end, name);
|
||||
accel_remap_huge_pages((void*)seg_start, seg_end - seg_start, real_end - seg_start, name, offset + seg_start - start);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
ElfW(Shdr) section;
|
||||
if (accel_find_program_section(§ion) == FAILURE) {
|
||||
zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: program section not found");
|
||||
return;
|
||||
}
|
||||
#elif defined(__FreeBSD__)
|
||||
size_t s = 0;
|
||||
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid()};
|
||||
long unsigned int huge_page_size = 2 * 1024 * 1024;
|
||||
if (sysctl(mib, 4, NULL, &s, NULL, 0) == 0) {
|
||||
s = s * 4 / 3;
|
||||
void *addr = mmap(NULL, s, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
|
||||
if (addr != MAP_FAILED) {
|
||||
if (sysctl(mib, 4, addr, &s, NULL, 0) == 0) {
|
||||
uintptr_t start = (uintptr_t)addr;
|
||||
uintptr_t end = start + s;
|
||||
while (start < end) {
|
||||
struct kinfo_vmentry *entry = (struct kinfo_vmentry *)start;
|
||||
size_t sz = entry->kve_structsize;
|
||||
if (sz == 0) {
|
||||
break;
|
||||
}
|
||||
int permflags = entry->kve_protection;
|
||||
if ((permflags & KVME_PROT_READ) && !(permflags & KVME_PROT_WRITE) &&
|
||||
(permflags & KVME_PROT_EXEC) && entry->kve_path[0] != '\0') {
|
||||
long unsigned int seg_start = ZEND_MM_ALIGNED_SIZE_EX(start, huge_page_size);
|
||||
long unsigned int seg_end = (end & ~(huge_page_size-1L));
|
||||
if (seg_end > seg_start) {
|
||||
zend_accel_error(ACCEL_LOG_DEBUG, "remap to huge page %lx-%lx %s \n", seg_start, seg_end, entry->kve_path);
|
||||
accel_remap_huge_pages((void*)seg_start, seg_end - seg_start, seg_end - seg_start, entry->kve_path, entry->kve_offset + seg_start - start);
|
||||
// First relevant segment found is our binary
|
||||
break;
|
||||
}
|
||||
}
|
||||
start += sz;
|
||||
}
|
||||
}
|
||||
munmap(addr, s);
|
||||
}
|
||||
|
||||
uintptr_t start = ZEND_MM_ALIGNED_SIZE_EX(section.sh_addr, ZEND_HUGE_PAGE_SIZE);
|
||||
uintptr_t end = (section.sh_addr + section.sh_size) & ~(ZEND_HUGE_PAGE_SIZE-1UL);
|
||||
if (end > start) {
|
||||
zend_accel_error(ACCEL_LOG_DEBUG, "remap to huge page %" PRIxPTR "-%" PRIxPTR "\n", start, end);
|
||||
accel_remap_huge_pages((void*)start, end - start, end - start);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
# else
|
||||
static void accel_move_code_to_huge_pages(void)
|
||||
|
||||
Reference in New Issue
Block a user