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_clear_xlat_table
  15. zend_shared_alloc_register_xlat_entry
  16. zend_shared_alloc_get_xlat_entry
  17. zend_shared_alloc_get_free_memory
  18. zend_shared_alloc_save_state
  19. zend_shared_alloc_restore_state
  20. zend_accel_get_shared_model
  21. zend_accel_shared_protect

   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 HashTable xlat_table;
  49 static const zend_shared_memory_handlers *g_shared_alloc_handler = NULL;
  50 static const char *g_shared_model;
  51 /* pointer to globals allocated in SHM and shared across processes */
  52 zend_smm_shared_globals *smm_shared_globals;
  53 
  54 #ifndef ZEND_WIN32
  55 #ifdef ZTS
  56 static MUTEX_T zts_lock;
  57 #endif
  58 int lock_file;
  59 static char lockfile_name[sizeof(TMP_DIR) + sizeof(SEM_FILENAME_PREFIX) + 8];
  60 #endif
  61 
  62 static const zend_shared_memory_handler_entry handler_table[] = {
  63 #ifdef USE_MMAP
  64         { "mmap", &zend_alloc_mmap_handlers },
  65 #endif
  66 #ifdef USE_SHM
  67         { "shm", &zend_alloc_shm_handlers },
  68 #endif
  69 #ifdef USE_SHM_OPEN
  70         { "posix", &zend_alloc_posix_handlers },
  71 #endif
  72 #ifdef ZEND_WIN32
  73         { "win32", &zend_alloc_win32_handlers },
  74 #endif
  75         { NULL, NULL}
  76 };
  77 
  78 #ifndef ZEND_WIN32
  79 void zend_shared_alloc_create_lock(void)
  80 {
  81         int val;
  82 
  83 #ifdef ZTS
  84     zts_lock = tsrm_mutex_alloc();
  85 #endif
  86 
  87         sprintf(lockfile_name, "%s/%sXXXXXX", TMP_DIR, SEM_FILENAME_PREFIX);
  88         lock_file = mkstemp(lockfile_name);
  89         fchmod(lock_file, 0666);
  90 
  91         if (lock_file == -1) {
  92                 zend_accel_error(ACCEL_LOG_FATAL, "Unable to create lock file: %s (%d)", strerror(errno), errno);
  93         }
  94         val = fcntl(lock_file, F_GETFD, 0);
  95         val |= FD_CLOEXEC;
  96         fcntl(lock_file, F_SETFD, val);
  97 
  98         unlink(lockfile_name);
  99 }
 100 #endif
 101 
 102 static void no_memory_bailout(size_t allocate_size, char *error)
 103 {
 104         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 );
 105 }
 106 
 107 static void copy_shared_segments(void *to, void *from, int count, int size)
 108 {
 109         zend_shared_segment **shared_segments_v = (zend_shared_segment **)to;
 110         void *shared_segments_to_p = ((char *)to + count*(sizeof(void *)));
 111         void *shared_segments_from_p = from;
 112         int i;
 113 
 114         for (i = 0; i < count; i++) {
 115                 shared_segments_v[i] =  shared_segments_to_p;
 116                 memcpy(shared_segments_to_p, shared_segments_from_p, size);
 117                 shared_segments_to_p = ((char *)shared_segments_to_p + size);
 118                 shared_segments_from_p = ((char *)shared_segments_from_p + size);
 119         }
 120 }
 121 
 122 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)
 123 {
 124         int res;
 125         g_shared_alloc_handler = he->handler;
 126         g_shared_model = he->name;
 127         ZSMMG(shared_segments) = NULL;
 128         ZSMMG(shared_segments_count) = 0;
 129 
 130         res = S_H(create_segments)(requested_size, shared_segments_p, shared_segments_count, error_in);
 131 
 132         if (res) {
 133                 /* this model works! */
 134                 return res;
 135         }
 136         if (*shared_segments_p) {
 137                 int i;
 138                 /* cleanup */
 139                 for (i = 0; i < *shared_segments_count; i++) {
 140                         if ((*shared_segments_p)[i]->p && (*shared_segments_p)[i]->p != (void *)-1) {
 141                                 S_H(detach_segment)((*shared_segments_p)[i]);
 142                         }
 143                 }
 144                 free(*shared_segments_p);
 145                 *shared_segments_p = NULL;
 146         }
 147         g_shared_alloc_handler = NULL;
 148         return ALLOC_FAILURE;
 149 }
 150 
 151 int zend_shared_alloc_startup(size_t requested_size)
 152 {
 153         zend_shared_segment **tmp_shared_segments;
 154         size_t shared_segments_array_size;
 155         zend_smm_shared_globals tmp_shared_globals, *p_tmp_shared_globals;
 156         char *error_in = NULL;
 157         const zend_shared_memory_handler_entry *he;
 158         int res = ALLOC_FAILURE;
 159 
 160         TSRMLS_FETCH();
 161 
 162         /* shared_free must be valid before we call zend_shared_alloc()
 163          * - make it temporarily point to a local variable
 164          */
 165         smm_shared_globals = &tmp_shared_globals;
 166         ZSMMG(shared_free) = requested_size; /* goes to tmp_shared_globals.shared_free */
 167 
 168         zend_shared_alloc_create_lock();
 169 
 170         if (ZCG(accel_directives).memory_model && ZCG(accel_directives).memory_model[0]) {
 171                 char *model = ZCG(accel_directives).memory_model;
 172                 /* "cgi" is really "shm"... */
 173                 if (strncmp(ZCG(accel_directives).memory_model, "cgi", sizeof("cgi")) == 0) {
 174                         model = "shm";
 175                 }
 176 
 177                 for (he = handler_table; he->name; he++) {
 178                         if (strcmp(model, he->name) == 0) {
 179                                 res = zend_shared_alloc_try(he, requested_size, &ZSMMG(shared_segments), &ZSMMG(shared_segments_count), &error_in);
 180                                 if (res) {
 181                                         /* this model works! */
 182                                 }
 183                                 break;
 184                         }
 185                 }
 186         }
 187 
 188         if (res == FAILED_REATTACHED) {
 189                 smm_shared_globals = NULL;
 190                 return res;
 191         }
 192 
 193         if (!g_shared_alloc_handler) {
 194                 /* try memory handlers in order */
 195                 for (he = handler_table; he->name; he++) {
 196                         res = zend_shared_alloc_try(he, requested_size, &ZSMMG(shared_segments), &ZSMMG(shared_segments_count), &error_in);
 197                         if (res) {
 198                                 /* this model works! */
 199                                 break;
 200                         }
 201                 }
 202         }
 203 
 204         if (!g_shared_alloc_handler) {
 205                 no_memory_bailout(requested_size, error_in);
 206                 return ALLOC_FAILURE;
 207         }
 208 
 209         if (res == SUCCESSFULLY_REATTACHED) {
 210                 return res;
 211         }
 212 
 213         shared_segments_array_size = ZSMMG(shared_segments_count) * S_H(segment_type_size)();
 214 
 215         /* move shared_segments and shared_free to shared memory */
 216         ZCG(locked) = 1; /* no need to perform a real lock at this point */
 217         p_tmp_shared_globals = (zend_smm_shared_globals *) zend_shared_alloc(sizeof(zend_smm_shared_globals));
 218         if (!p_tmp_shared_globals) {
 219                 zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
 220                 return ALLOC_FAILURE;;
 221         }
 222 
 223         tmp_shared_segments = zend_shared_alloc(shared_segments_array_size + ZSMMG(shared_segments_count) * sizeof(void *));
 224         if (!tmp_shared_segments) {
 225                 zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
 226                 return ALLOC_FAILURE;;
 227         }
 228 
 229         copy_shared_segments(tmp_shared_segments, ZSMMG(shared_segments)[0], ZSMMG(shared_segments_count), S_H(segment_type_size)());
 230 
 231         *p_tmp_shared_globals = tmp_shared_globals;
 232         smm_shared_globals = p_tmp_shared_globals;
 233 
 234         free(ZSMMG(shared_segments));
 235         ZSMMG(shared_segments) = tmp_shared_segments;
 236 
 237         ZSMMG(shared_memory_state).positions = (int *)zend_shared_alloc(sizeof(int) * ZSMMG(shared_segments_count));
 238         if (!ZSMMG(shared_memory_state).positions) {
 239                 zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
 240                 return ALLOC_FAILURE;;
 241         }
 242 
 243         ZCG(locked) = 0;
 244 
 245         return res;
 246 }
 247 
 248 void zend_shared_alloc_shutdown(void)
 249 {
 250         zend_shared_segment **tmp_shared_segments;
 251         size_t shared_segments_array_size;
 252         zend_smm_shared_globals tmp_shared_globals;
 253         int i;
 254 
 255         tmp_shared_globals = *smm_shared_globals;
 256         smm_shared_globals = &tmp_shared_globals;
 257         shared_segments_array_size = ZSMMG(shared_segments_count) * (S_H(segment_type_size)() + sizeof(void *));
 258         tmp_shared_segments = emalloc(shared_segments_array_size);
 259         copy_shared_segments(tmp_shared_segments, ZSMMG(shared_segments)[0], ZSMMG(shared_segments_count), S_H(segment_type_size)());
 260         ZSMMG(shared_segments) = tmp_shared_segments;
 261 
 262         for (i = 0; i < ZSMMG(shared_segments_count); i++) {
 263                 S_H(detach_segment)(ZSMMG(shared_segments)[i]);
 264         }
 265         efree(ZSMMG(shared_segments));
 266         ZSMMG(shared_segments) = NULL;
 267         g_shared_alloc_handler = NULL;
 268 #ifndef ZEND_WIN32
 269         close(lock_file);
 270 #endif
 271 }
 272 
 273 static size_t zend_shared_alloc_get_largest_free_block(void)
 274 {
 275         int i;
 276         size_t largest_block_size = 0;
 277 
 278         for (i = 0; i < ZSMMG(shared_segments_count); i++) {
 279                 size_t block_size = ZSMMG(shared_segments)[i]->size - ZSMMG(shared_segments)[i]->pos;
 280 
 281                 if (block_size>largest_block_size) {
 282                         largest_block_size = block_size;
 283                 }
 284         }
 285         return largest_block_size;
 286 }
 287 
 288 #define MIN_FREE_MEMORY 64*1024
 289 
 290 #define SHARED_ALLOC_FAILED() do {              \
 291                 zend_accel_error(ACCEL_LOG_WARNING, "Not enough free shared space to allocate %ld bytes (%ld bytes free)", (long)size, (long)ZSMMG(shared_free)); \
 292                 if (zend_shared_alloc_get_largest_free_block() < MIN_FREE_MEMORY) { \
 293                         ZSMMG(memory_exhausted) = 1; \
 294                 } \
 295         } while (0)
 296 
 297 void *zend_shared_alloc(size_t size)
 298 {
 299         int i;
 300         unsigned int block_size = ZEND_ALIGNED_SIZE(size);
 301         TSRMLS_FETCH();
 302 
 303 #if 1
 304         if (!ZCG(locked)) {
 305                 zend_accel_error(ACCEL_LOG_ERROR, "Shared memory lock not obtained");
 306         }
 307 #endif
 308         if (block_size > ZSMMG(shared_free)) { /* No hope to find a big-enough block */
 309                 SHARED_ALLOC_FAILED();
 310                 return NULL;
 311         }
 312         for (i = 0; i < ZSMMG(shared_segments_count); i++) {
 313                 if (ZSMMG(shared_segments)[i]->size - ZSMMG(shared_segments)[i]->pos >= block_size) { /* found a valid block */
 314                         void *retval = (void *) (((char *) ZSMMG(shared_segments)[i]->p) + ZSMMG(shared_segments)[i]->pos);
 315 
 316                         ZSMMG(shared_segments)[i]->pos += block_size;
 317                         ZSMMG(shared_free) -= block_size;
 318                         memset(retval, 0, block_size);
 319                         return retval;
 320                 }
 321         }
 322         SHARED_ALLOC_FAILED();
 323         return NULL;
 324 }
 325 
 326 int zend_shared_memdup_size(void *source, size_t size)
 327 {
 328         void **old_p;
 329 
 330         if (zend_hash_index_find(&xlat_table, (ulong)source, (void **)&old_p) == SUCCESS) {
 331                 /* we already duplicated this pointer */
 332                 return 0;
 333         }
 334         zend_shared_alloc_register_xlat_entry(source, source);
 335         return ZEND_ALIGNED_SIZE(size);
 336 }
 337 
 338 void *_zend_shared_memdup(void *source, size_t size, zend_bool free_source TSRMLS_DC)
 339 {
 340         void **old_p, *retval;
 341 
 342         if (zend_hash_index_find(&xlat_table, (ulong)source, (void **)&old_p) == SUCCESS) {
 343                 /* we already duplicated this pointer */
 344                 return *old_p;
 345         }
 346         retval = ZCG(mem);;
 347         ZCG(mem) = (void*)(((char*)ZCG(mem)) + ZEND_ALIGNED_SIZE(size));
 348         memcpy(retval, source, size);
 349         zend_shared_alloc_register_xlat_entry(source, retval);
 350         if (free_source) {
 351                 interned_efree((char*)source);
 352         }
 353         return retval;
 354 }
 355 
 356 void zend_shared_alloc_safe_unlock(TSRMLS_D)
 357 {
 358         if (ZCG(locked)) {
 359                 zend_shared_alloc_unlock(TSRMLS_C);
 360         }
 361 }
 362 
 363 #ifndef ZEND_WIN32
 364 /* name l_type l_whence l_start l_len */
 365 static FLOCK_STRUCTURE(mem_write_lock, F_WRLCK, SEEK_SET, 0, 1);
 366 static FLOCK_STRUCTURE(mem_write_unlock, F_UNLCK, SEEK_SET, 0, 1);
 367 #endif
 368 
 369 void zend_shared_alloc_lock(TSRMLS_D)
 370 {
 371 #ifndef ZEND_WIN32
 372 
 373 #ifdef ZTS
 374         tsrm_mutex_lock(zts_lock);
 375 #endif
 376 
 377 #if 0
 378         /* this will happen once per process, and will un-globalize mem_write_lock */
 379         if (mem_write_lock.l_pid == -1) {
 380                 mem_write_lock.l_pid = getpid();
 381         }
 382 #endif
 383 
 384         while (1) {
 385                 if (fcntl(lock_file, F_SETLKW, &mem_write_lock) == -1) {
 386                         if (errno == EINTR) {
 387                                 continue;
 388                         }
 389                         zend_accel_error(ACCEL_LOG_ERROR, "Cannot create lock - %s (%d)", strerror(errno), errno);
 390                 }
 391                 break;
 392         }
 393 #else
 394         zend_shared_alloc_lock_win32();
 395 #endif
 396 
 397         ZCG(locked) = 1;
 398 
 399         /* Prepare translation table
 400          *
 401          * Make it persistent so that it uses malloc() and allocated blocks
 402          * won't be taken from space which is freed by efree in memdup.
 403          * Otherwise it leads to false matches in memdup check.
 404          */
 405         zend_hash_init(&xlat_table, 100, NULL, NULL, 1);
 406 }
 407 
 408 void zend_shared_alloc_unlock(TSRMLS_D)
 409 {
 410         /* Destroy translation table */
 411         zend_hash_destroy(&xlat_table);
 412 
 413         ZCG(locked) = 0;
 414 
 415 #ifndef ZEND_WIN32
 416         if (fcntl(lock_file, F_SETLK, &mem_write_unlock) == -1) {
 417                 zend_accel_error(ACCEL_LOG_ERROR, "Cannot remove lock - %s (%d)", strerror(errno), errno);
 418         }
 419 #ifdef ZTS
 420         tsrm_mutex_unlock(zts_lock);
 421 #endif
 422 #else
 423         zend_shared_alloc_unlock_win32();
 424 #endif
 425 }
 426 
 427 void zend_shared_alloc_clear_xlat_table(void)
 428 {
 429         zend_hash_clean(&xlat_table);
 430 }
 431 
 432 void zend_shared_alloc_register_xlat_entry(const void *old, const void *new)
 433 {
 434         zend_hash_index_update(&xlat_table, (ulong)old, (void*)&new, sizeof(void *), NULL);
 435 }
 436 
 437 void *zend_shared_alloc_get_xlat_entry(const void *old)
 438 {
 439         void **retval;
 440 
 441         if (zend_hash_index_find(&xlat_table, (ulong)old, (void **)&retval) == FAILURE) {
 442                 return NULL;
 443         }
 444         return *retval;
 445 }
 446 
 447 size_t zend_shared_alloc_get_free_memory(void)
 448 {
 449         return ZSMMG(shared_free);
 450 }
 451 
 452 void zend_shared_alloc_save_state(void)
 453 {
 454         int i;
 455 
 456         for (i = 0; i < ZSMMG(shared_segments_count); i++) {
 457                 ZSMMG(shared_memory_state).positions[i] = ZSMMG(shared_segments)[i]->pos;
 458         }
 459         ZSMMG(shared_memory_state).shared_free = ZSMMG(shared_free);
 460 }
 461 
 462 void zend_shared_alloc_restore_state(void)
 463 {
 464         int i;
 465 
 466         for (i = 0; i < ZSMMG(shared_segments_count); i++) {
 467                 ZSMMG(shared_segments)[i]->pos = ZSMMG(shared_memory_state).positions[i];
 468         }
 469         ZSMMG(shared_free) = ZSMMG(shared_memory_state).shared_free;
 470         ZSMMG(memory_exhausted) = 0;
 471         ZSMMG(wasted_shared_memory) = 0;
 472 }
 473 
 474 const char *zend_accel_get_shared_model(void)
 475 {
 476         return g_shared_model;
 477 }
 478 
 479 void zend_accel_shared_protect(int mode TSRMLS_DC)
 480 {
 481 #ifdef HAVE_MPROTECT
 482         int i;
 483 
 484         if (mode) {
 485                 mode = PROT_READ;
 486         } else {
 487                 mode = PROT_READ|PROT_WRITE;
 488         }
 489 
 490         for (i = 0; i < ZSMMG(shared_segments_count); i++) {
 491                 mprotect(ZSMMG(shared_segments)[i]->p, ZSMMG(shared_segments)[i]->size, mode);
 492         }
 493 #endif
 494 }

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