root/ext/opcache/zend_shared_alloc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. zend_shared_alloc_create_lock
  2. no_memory_bailout
  3. copy_shared_segments
  4. zend_shared_alloc_try
  5. zend_shared_alloc_startup
  6. zend_shared_alloc_shutdown
  7. zend_shared_alloc_get_largest_free_block
  8. zend_shared_alloc
  9. zend_shared_memdup_size
  10. _zend_shared_memdup
  11. zend_shared_alloc_safe_unlock
  12. zend_shared_alloc_lock
  13. zend_shared_alloc_unlock
  14. zend_shared_alloc_init_xlat_table
  15. zend_shared_alloc_destroy_xlat_table
  16. zend_shared_alloc_clear_xlat_table
  17. zend_shared_alloc_register_xlat_entry
  18. zend_shared_alloc_get_xlat_entry
  19. zend_shared_alloc_get_free_memory
  20. zend_shared_alloc_save_state
  21. zend_shared_alloc_restore_state
  22. zend_accel_get_shared_model
  23. zend_accel_shared_protect
  24. zend_accel_in_shm

   1 /*
   2    +----------------------------------------------------------------------+
   3    | Zend OPcache                                                         |
   4    +----------------------------------------------------------------------+
   5    | Copyright (c) 1998-2016 The PHP Group                                |
   6    +----------------------------------------------------------------------+
   7    | This source file is subject to version 3.01 of the PHP license,      |
   8    | that is bundled with this package in the file LICENSE, and is        |
   9    | available through the world-wide-web at the following url:           |
  10    | http://www.php.net/license/3_01.txt                                  |
  11    | If you did not receive a copy of the PHP license and are unable to   |
  12    | obtain it through the world-wide-web, please send a note to          |
  13    | license@php.net so we can mail you a copy immediately.               |
  14    +----------------------------------------------------------------------+
  15    | Authors: Andi Gutmans <andi@zend.com>                                |
  16    |          Zeev Suraski <zeev@zend.com>                                |
  17    |          Stanislav Malyshev <stas@zend.com>                          |
  18    |          Dmitry Stogov <dmitry@zend.com>                             |
  19    +----------------------------------------------------------------------+
  20 */
  21 
  22 #include <errno.h>
  23 #include "ZendAccelerator.h"
  24 #include "zend_shared_alloc.h"
  25 #ifdef HAVE_UNISTD_H
  26 # include <unistd.h>
  27 #endif
  28 #include <fcntl.h>
  29 #ifndef ZEND_WIN32
  30 # include <sys/types.h>
  31 # include <dirent.h>
  32 # include <signal.h>
  33 # include <sys/stat.h>
  34 # include <stdio.h>
  35 #endif
  36 
  37 #ifdef HAVE_MPROTECT
  38 # include "sys/mman.h"
  39 #endif
  40 
  41 #define TMP_DIR "/tmp"
  42 #define SEM_FILENAME_PREFIX ".ZendSem."
  43 #define S_H(s) g_shared_alloc_handler->s
  44 
  45 /* True globals */
  46 /* old/new mapping. We can use true global even for ZTS because its usage
  47    is wrapped with exclusive lock anyway */
  48 static const zend_shared_memory_handlers *g_shared_alloc_handler = NULL;
  49 static const char *g_shared_model;
  50 /* pointer to globals allocated in SHM and shared across processes */
  51 zend_smm_shared_globals *smm_shared_globals;
  52 
  53 #ifndef ZEND_WIN32
  54 #ifdef ZTS
  55 static MUTEX_T zts_lock;
  56 #endif
  57 int lock_file;
  58 static char lockfile_name[sizeof(TMP_DIR) + sizeof(SEM_FILENAME_PREFIX) + 8];
  59 #endif
  60 
  61 static const zend_shared_memory_handler_entry handler_table[] = {
  62 #ifdef USE_MMAP
  63         { "mmap", &zend_alloc_mmap_handlers },
  64 #endif
  65 #ifdef USE_SHM
  66         { "shm", &zend_alloc_shm_handlers },
  67 #endif
  68 #ifdef USE_SHM_OPEN
  69         { "posix", &zend_alloc_posix_handlers },
  70 #endif
  71 #ifdef ZEND_WIN32
  72         { "win32", &zend_alloc_win32_handlers },
  73 #endif
  74         { NULL, NULL}
  75 };
  76 
  77 #ifndef ZEND_WIN32
  78 void zend_shared_alloc_create_lock(void)
  79 {
  80         int val;
  81 
  82 #ifdef ZTS
  83     zts_lock = tsrm_mutex_alloc();
  84 #endif
  85 
  86         sprintf(lockfile_name, "%s/%sXXXXXX", TMP_DIR, SEM_FILENAME_PREFIX);
  87         lock_file = mkstemp(lockfile_name);
  88         fchmod(lock_file, 0666);
  89 
  90         if (lock_file == -1) {
  91                 zend_accel_error(ACCEL_LOG_FATAL, "Unable to create lock file: %s (%d)", strerror(errno), errno);
  92         }
  93         val = fcntl(lock_file, F_GETFD, 0);
  94         val |= FD_CLOEXEC;
  95         fcntl(lock_file, F_SETFD, val);
  96 
  97         unlink(lockfile_name);
  98 }
  99 #endif
 100 
 101 static void no_memory_bailout(size_t allocate_size, char *error)
 102 {
 103         zend_accel_error(ACCEL_LOG_FATAL, "Unable to allocate shared memory segment of %ld bytes: %s: %s (%d)", allocate_size, error?error:"unknown", strerror(errno), errno );
 104 }
 105 
 106 static void copy_shared_segments(void *to, void *from, int count, int size)
 107 {
 108         zend_shared_segment **shared_segments_v = (zend_shared_segment **)to;
 109         void *shared_segments_to_p = ((char *)to + count*(sizeof(void *)));
 110         void *shared_segments_from_p = from;
 111         int i;
 112 
 113         for (i = 0; i < count; i++) {
 114                 shared_segments_v[i] =  shared_segments_to_p;
 115                 memcpy(shared_segments_to_p, shared_segments_from_p, size);
 116                 shared_segments_to_p = ((char *)shared_segments_to_p + size);
 117                 shared_segments_from_p = ((char *)shared_segments_from_p + size);
 118         }
 119 }
 120 
 121 static int zend_shared_alloc_try(const zend_shared_memory_handler_entry *he, size_t requested_size, zend_shared_segment ***shared_segments_p, int *shared_segments_count, char **error_in)
 122 {
 123         int res;
 124         g_shared_alloc_handler = he->handler;
 125         g_shared_model = he->name;
 126         ZSMMG(shared_segments) = NULL;
 127         ZSMMG(shared_segments_count) = 0;
 128 
 129         res = S_H(create_segments)(requested_size, shared_segments_p, shared_segments_count, error_in);
 130 
 131         if (res) {
 132                 /* this model works! */
 133                 return res;
 134         }
 135         if (*shared_segments_p) {
 136                 int i;
 137                 /* cleanup */
 138                 for (i = 0; i < *shared_segments_count; i++) {
 139                         if ((*shared_segments_p)[i]->p && (*shared_segments_p)[i]->p != (void *)-1) {
 140                                 S_H(detach_segment)((*shared_segments_p)[i]);
 141                         }
 142                 }
 143                 free(*shared_segments_p);
 144                 *shared_segments_p = NULL;
 145         }
 146         g_shared_alloc_handler = NULL;
 147         return ALLOC_FAILURE;
 148 }
 149 
 150 int zend_shared_alloc_startup(size_t requested_size)
 151 {
 152         zend_shared_segment **tmp_shared_segments;
 153         size_t shared_segments_array_size;
 154         zend_smm_shared_globals tmp_shared_globals, *p_tmp_shared_globals;
 155         char *error_in = NULL;
 156         const zend_shared_memory_handler_entry *he;
 157         int res = ALLOC_FAILURE;
 158 
 159 
 160         /* shared_free must be valid before we call zend_shared_alloc()
 161          * - make it temporarily point to a local variable
 162          */
 163         smm_shared_globals = &tmp_shared_globals;
 164         ZSMMG(shared_free) = requested_size; /* goes to tmp_shared_globals.shared_free */
 165 
 166         zend_shared_alloc_create_lock();
 167 
 168         if (ZCG(accel_directives).memory_model && ZCG(accel_directives).memory_model[0]) {
 169                 char *model = ZCG(accel_directives).memory_model;
 170                 /* "cgi" is really "shm"... */
 171                 if (strncmp(ZCG(accel_directives).memory_model, "cgi", sizeof("cgi")) == 0) {
 172                         model = "shm";
 173                 }
 174 
 175                 for (he = handler_table; he->name; he++) {
 176                         if (strcmp(model, he->name) == 0) {
 177                                 res = zend_shared_alloc_try(he, requested_size, &ZSMMG(shared_segments), &ZSMMG(shared_segments_count), &error_in);
 178                                 if (res) {
 179                                         /* this model works! */
 180                                 }
 181                                 break;
 182                         }
 183                 }
 184         }
 185 
 186         if (res == FAILED_REATTACHED) {
 187                 smm_shared_globals = NULL;
 188                 return res;
 189         }
 190 #if ENABLE_FILE_CACHE_FALLBACK
 191         if (ALLOC_FALLBACK == res) {
 192                 return ALLOC_FALLBACK;
 193         }
 194 #endif
 195 
 196         if (!g_shared_alloc_handler) {
 197                 /* try memory handlers in order */
 198                 for (he = handler_table; he->name; he++) {
 199                         res = zend_shared_alloc_try(he, requested_size, &ZSMMG(shared_segments), &ZSMMG(shared_segments_count), &error_in);
 200                         if (res) {
 201                                 /* this model works! */
 202                                 break;
 203                         }
 204                 }
 205         }
 206 
 207         if (!g_shared_alloc_handler) {
 208                 no_memory_bailout(requested_size, error_in);
 209                 return ALLOC_FAILURE;
 210         }
 211 
 212         if (res == SUCCESSFULLY_REATTACHED) {
 213                 return res;
 214         }
 215 #if ENABLE_FILE_CACHE_FALLBACK
 216         if (ALLOC_FALLBACK == res) {
 217                 return ALLOC_FALLBACK;
 218         }
 219 #endif
 220 
 221         shared_segments_array_size = ZSMMG(shared_segments_count) * S_H(segment_type_size)();
 222 
 223         /* move shared_segments and shared_free to shared memory */
 224         ZCG(locked) = 1; /* no need to perform a real lock at this point */
 225         p_tmp_shared_globals = (zend_smm_shared_globals *) zend_shared_alloc(sizeof(zend_smm_shared_globals));
 226         if (!p_tmp_shared_globals) {
 227                 zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
 228                 return ALLOC_FAILURE;;
 229         }
 230 
 231         tmp_shared_segments = zend_shared_alloc(shared_segments_array_size + ZSMMG(shared_segments_count) * sizeof(void *));
 232         if (!tmp_shared_segments) {
 233                 zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
 234                 return ALLOC_FAILURE;;
 235         }
 236 
 237         copy_shared_segments(tmp_shared_segments, ZSMMG(shared_segments)[0], ZSMMG(shared_segments_count), S_H(segment_type_size)());
 238 
 239         *p_tmp_shared_globals = tmp_shared_globals;
 240         smm_shared_globals = p_tmp_shared_globals;
 241 
 242         free(ZSMMG(shared_segments));
 243         ZSMMG(shared_segments) = tmp_shared_segments;
 244 
 245         ZSMMG(shared_memory_state).positions = (int *)zend_shared_alloc(sizeof(int) * ZSMMG(shared_segments_count));
 246         if (!ZSMMG(shared_memory_state).positions) {
 247                 zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
 248                 return ALLOC_FAILURE;;
 249         }
 250 
 251         ZCG(locked) = 0;
 252 
 253         return res;
 254 }
 255 
 256 void zend_shared_alloc_shutdown(void)
 257 {
 258         zend_shared_segment **tmp_shared_segments;
 259         size_t shared_segments_array_size;
 260         zend_smm_shared_globals tmp_shared_globals;
 261         int i;
 262 
 263         tmp_shared_globals = *smm_shared_globals;
 264         smm_shared_globals = &tmp_shared_globals;
 265         shared_segments_array_size = ZSMMG(shared_segments_count) * (S_H(segment_type_size)() + sizeof(void *));
 266         tmp_shared_segments = emalloc(shared_segments_array_size);
 267         copy_shared_segments(tmp_shared_segments, ZSMMG(shared_segments)[0], ZSMMG(shared_segments_count), S_H(segment_type_size)());
 268         ZSMMG(shared_segments) = tmp_shared_segments;
 269 
 270         for (i = 0; i < ZSMMG(shared_segments_count); i++) {
 271                 S_H(detach_segment)(ZSMMG(shared_segments)[i]);
 272         }
 273         efree(ZSMMG(shared_segments));
 274         ZSMMG(shared_segments) = NULL;
 275         g_shared_alloc_handler = NULL;
 276 #ifndef ZEND_WIN32
 277         close(lock_file);
 278 #endif
 279 }
 280 
 281 static size_t zend_shared_alloc_get_largest_free_block(void)
 282 {
 283         int i;
 284         size_t largest_block_size = 0;
 285 
 286         for (i = 0; i < ZSMMG(shared_segments_count); i++) {
 287                 size_t block_size = ZSMMG(shared_segments)[i]->size - ZSMMG(shared_segments)[i]->pos;
 288 
 289                 if (block_size>largest_block_size) {
 290                         largest_block_size = block_size;
 291                 }
 292         }
 293         return largest_block_size;
 294 }
 295 
 296 #define MIN_FREE_MEMORY 64*1024
 297 
 298 #define SHARED_ALLOC_FAILED() do {              \
 299                 zend_accel_error(ACCEL_LOG_WARNING, "Not enough free shared space to allocate %pd bytes (%pd bytes free)", (zend_long)size, (zend_long)ZSMMG(shared_free)); \
 300                 if (zend_shared_alloc_get_largest_free_block() < MIN_FREE_MEMORY) { \
 301                         ZSMMG(memory_exhausted) = 1; \
 302                 } \
 303         } while (0)
 304 
 305 void *zend_shared_alloc(size_t size)
 306 {
 307         int i;
 308         unsigned int block_size = ZEND_ALIGNED_SIZE(size);
 309 
 310 #if 1
 311         if (!ZCG(locked)) {
 312                 zend_accel_error(ACCEL_LOG_ERROR, "Shared memory lock not obtained");
 313         }
 314 #endif
 315         if (block_size > ZSMMG(shared_free)) { /* No hope to find a big-enough block */
 316                 SHARED_ALLOC_FAILED();
 317                 return NULL;
 318         }
 319         for (i = 0; i < ZSMMG(shared_segments_count); i++) {
 320                 if (ZSMMG(shared_segments)[i]->size - ZSMMG(shared_segments)[i]->pos >= block_size) { /* found a valid block */
 321                         void *retval = (void *) (((char *) ZSMMG(shared_segments)[i]->p) + ZSMMG(shared_segments)[i]->pos);
 322 
 323                         ZSMMG(shared_segments)[i]->pos += block_size;
 324                         ZSMMG(shared_free) -= block_size;
 325                         memset(retval, 0, block_size);
 326                         ZEND_ASSERT(((zend_uintptr_t)retval & 0x7) == 0); /* should be 8 byte aligned */
 327                         return retval;
 328                 }
 329         }
 330         SHARED_ALLOC_FAILED();
 331         return NULL;
 332 }
 333 
 334 int zend_shared_memdup_size(void *source, size_t size)
 335 {
 336         void *old_p;
 337 
 338         if ((old_p = zend_hash_index_find_ptr(&ZCG(xlat_table), (zend_ulong)source)) != NULL) {
 339                 /* we already duplicated this pointer */
 340                 return 0;
 341         }
 342         zend_shared_alloc_register_xlat_entry(source, source);
 343         return ZEND_ALIGNED_SIZE(size);
 344 }
 345 
 346 void *_zend_shared_memdup(void *source, size_t size, zend_bool free_source)
 347 {
 348         void *old_p, *retval;
 349 
 350         if ((old_p = zend_hash_index_find_ptr(&ZCG(xlat_table), (zend_ulong)source)) != NULL) {
 351                 /* we already duplicated this pointer */
 352                 return old_p;
 353         }
 354         retval = ZCG(mem);
 355         ZCG(mem) = (void*)(((char*)ZCG(mem)) + ZEND_ALIGNED_SIZE(size));
 356         memcpy(retval, source, size);
 357         zend_shared_alloc_register_xlat_entry(source, retval);
 358         if (free_source) {
 359                 efree(source);
 360         }
 361         return retval;
 362 }
 363 
 364 void zend_shared_alloc_safe_unlock(void)
 365 {
 366         if (ZCG(locked)) {
 367                 zend_shared_alloc_unlock();
 368         }
 369 }
 370 
 371 #ifndef ZEND_WIN32
 372 /* name l_type l_whence l_start l_len */
 373 static FLOCK_STRUCTURE(mem_write_lock, F_WRLCK, SEEK_SET, 0, 1);
 374 static FLOCK_STRUCTURE(mem_write_unlock, F_UNLCK, SEEK_SET, 0, 1);
 375 #endif
 376 
 377 void zend_shared_alloc_lock(void)
 378 {
 379 #ifndef ZEND_WIN32
 380 
 381 #ifdef ZTS
 382         tsrm_mutex_lock(zts_lock);
 383 #endif
 384 
 385 #if 0
 386         /* this will happen once per process, and will un-globalize mem_write_lock */
 387         if (mem_write_lock.l_pid == -1) {
 388                 mem_write_lock.l_pid = getpid();
 389         }
 390 #endif
 391 
 392         while (1) {
 393                 if (fcntl(lock_file, F_SETLKW, &mem_write_lock) == -1) {
 394                         if (errno == EINTR) {
 395                                 continue;
 396                         }
 397                         zend_accel_error(ACCEL_LOG_ERROR, "Cannot create lock - %s (%d)", strerror(errno), errno);
 398                 }
 399                 break;
 400         }
 401 #else
 402         zend_shared_alloc_lock_win32();
 403 #endif
 404 
 405         ZCG(locked) = 1;
 406 }
 407 
 408 void zend_shared_alloc_unlock(void)
 409 {
 410         ZCG(locked) = 0;
 411 
 412 #ifndef ZEND_WIN32
 413         if (fcntl(lock_file, F_SETLK, &mem_write_unlock) == -1) {
 414                 zend_accel_error(ACCEL_LOG_ERROR, "Cannot remove lock - %s (%d)", strerror(errno), errno);
 415         }
 416 #ifdef ZTS
 417         tsrm_mutex_unlock(zts_lock);
 418 #endif
 419 #else
 420         zend_shared_alloc_unlock_win32();
 421 #endif
 422 }
 423 
 424 void zend_shared_alloc_init_xlat_table(void)
 425 {
 426 
 427         /* Prepare translation table
 428          *
 429          * Make it persistent so that it uses malloc() and allocated blocks
 430          * won't be taken from space which is freed by efree in memdup.
 431          * Otherwise it leads to false matches in memdup check.
 432          */
 433         zend_hash_init(&ZCG(xlat_table), 128, NULL, NULL, 1);
 434 }
 435 
 436 void zend_shared_alloc_destroy_xlat_table(void)
 437 {
 438         /* Destroy translation table */
 439         zend_hash_destroy(&ZCG(xlat_table));
 440 }
 441 
 442 void zend_shared_alloc_clear_xlat_table(void)
 443 {
 444         zend_hash_clean(&ZCG(xlat_table));
 445 }
 446 
 447 void zend_shared_alloc_register_xlat_entry(const void *old, const void *new)
 448 {
 449         zend_hash_index_add_new_ptr(&ZCG(xlat_table), (zend_ulong)old, (void*)new);
 450 }
 451 
 452 void *zend_shared_alloc_get_xlat_entry(const void *old)
 453 {
 454         void *retval;
 455 
 456         if ((retval = zend_hash_index_find_ptr(&ZCG(xlat_table), (zend_ulong)old)) == NULL) {
 457                 return NULL;
 458         }
 459         return retval;
 460 }
 461 
 462 size_t zend_shared_alloc_get_free_memory(void)
 463 {
 464         return ZSMMG(shared_free);
 465 }
 466 
 467 void zend_shared_alloc_save_state(void)
 468 {
 469         int i;
 470 
 471         for (i = 0; i < ZSMMG(shared_segments_count); i++) {
 472                 ZSMMG(shared_memory_state).positions[i] = ZSMMG(shared_segments)[i]->pos;
 473         }
 474         ZSMMG(shared_memory_state).shared_free = ZSMMG(shared_free);
 475 }
 476 
 477 void zend_shared_alloc_restore_state(void)
 478 {
 479         int i;
 480 
 481         for (i = 0; i < ZSMMG(shared_segments_count); i++) {
 482                 ZSMMG(shared_segments)[i]->pos = ZSMMG(shared_memory_state).positions[i];
 483         }
 484         ZSMMG(shared_free) = ZSMMG(shared_memory_state).shared_free;
 485         ZSMMG(memory_exhausted) = 0;
 486         ZSMMG(wasted_shared_memory) = 0;
 487 }
 488 
 489 const char *zend_accel_get_shared_model(void)
 490 {
 491         return g_shared_model;
 492 }
 493 
 494 void zend_accel_shared_protect(int mode)
 495 {
 496 #ifdef HAVE_MPROTECT
 497         int i;
 498 
 499         if (!smm_shared_globals) {
 500                 return;
 501         }
 502 
 503         if (mode) {
 504                 mode = PROT_READ;
 505         } else {
 506                 mode = PROT_READ|PROT_WRITE;
 507         }
 508 
 509         for (i = 0; i < ZSMMG(shared_segments_count); i++) {
 510                 mprotect(ZSMMG(shared_segments)[i]->p, ZSMMG(shared_segments)[i]->size, mode);
 511         }
 512 #endif
 513 }
 514 
 515 int zend_accel_in_shm(void *ptr)
 516 {
 517         int i;
 518 
 519         if (!smm_shared_globals) {
 520                 return 0;
 521         }
 522 
 523         for (i = 0; i < ZSMMG(shared_segments_count); i++) {
 524                 if ((char*)ptr >= (char*)ZSMMG(shared_segments)[i]->p &&
 525                     (char*)ptr < (char*)ZSMMG(shared_segments)[i]->p + ZSMMG(shared_segments)[i]->size) {
 526                         return 1;
 527                 }
 528         }
 529         return 0;
 530 }

/* [<][>][^][v][top][bottom][index][help] */