zend_shared_alloc.c 14 KB

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