2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
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 +----------------------------------------------------------------------+
16 #include "hphp/runtime/base/apc-file-storage.h"
20 #include "hphp/util/alloc.h"
21 #include "hphp/util/timer.h"
22 #include "hphp/util/logger.h"
24 #include "hphp/runtime/base/complex-types.h"
25 #include "hphp/runtime/base/runtime-option.h"
26 #include "hphp/runtime/base/type-conversions.h"
27 #include "hphp/runtime/base/builtin-functions.h"
28 #include "hphp/runtime/server/server-stats.h"
29 #include "hphp/runtime/base/apc-stats.h"
30 #include "hphp/runtime/ext/apc/ext_apc.h"
32 #if !defined(HAVE_POSIX_FALLOCATE) && \
33 (_XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L || defined(__CYGWIN__))
34 # define HAVE_POSIX_FALLOCATE 1
39 //////////////////////////////////////////////////////////////////////
41 APCFileStorage s_apc_file_storage
;
43 void APCFileStorage::enable(const std::string
& prefix
,
44 int64_t chunkSize
, int64_t maxSize
) {
47 m_chunkSize
= chunkSize
;
49 if (m_state
!= StorageState::Invalid
) {
53 Logger::Error("Failed to open file for apc, fallback to in-memory mode");
56 m_state
= StorageState::Open
;
59 char *APCFileStorage::put(const char *data
, int32_t len
) {
61 if (m_state
!= StorageState::Open
||
62 len
+ PaddingSize
> m_chunkSize
- PaddingSize
) {
65 if (len
+ PaddingSize
> m_chunkRemain
&& !addFile()) {
66 m_state
= StorageState::Full
;
70 assert(len
+ PaddingSize
<= m_chunkRemain
);
71 strhash_t h
= hash_string_unsafe(data
, len
);
72 *(strhash_t
*)m_current
= h
;
73 m_current
+= sizeof(h
);
74 *(int32_t*)m_current
= len
;
75 m_current
+= sizeof(len
);
76 // should be no overlap
77 memcpy(m_current
, data
, len
);
78 char *addr
= m_current
;
80 m_current
+= len
+ sizeof(char);
81 *(strhash_t
*)m_current
= TombHash
;
82 m_chunkRemain
-= len
+ PaddingSize
;
86 void APCFileStorage::seal() {
88 if (m_state
== StorageState::Sealed
) {
91 assert(m_state
== StorageState::Open
|| m_state
== StorageState::Full
);
94 m_state
= StorageState::Sealed
;
96 for (int i
= 0; i
< (int)m_chunks
.size(); i
++) {
97 if (mprotect(m_chunks
[i
], m_chunkSize
, PROT_READ
) < 0) {
98 Logger::Error("Failed to mprotect chunk %d", i
);
103 void APCFileStorage::adviseOut() {
105 Timer
timer(Timer::WallTime
, "advising out apc prime");
106 for (int i
= 0; i
< (int)m_chunks
.size(); i
++) {
107 if (madvise(m_chunks
[i
], m_chunkSize
, MADV_DONTNEED
) < 0) {
108 Logger::Error("Failed to madvise chunk %d", i
);
113 bool APCFileStorage::hashCheck() {
115 for (int i
= 0; i
< (int)m_chunks
.size(); i
++) {
116 char *current
= (char*)m_chunks
[i
];
117 char *boundary
= (char*)m_chunks
[i
] + m_chunkSize
;
119 strhash_t h
= *(strhash_t
*)current
;
123 current
+= sizeof(h
);
124 int32_t len
= *(int32_t*)current
;
125 current
+= sizeof(len
);
127 len
+ PaddingSize
>= (int64_t)boundary
- (int64_t)current
) {
128 Logger::Error("invalid len %d at chunk %d offset %" PRId64
, len
, i
,
129 (int64_t)current
- (int64_t)m_chunks
[i
]);
132 strhash_t h_data
= hash_string(current
, len
);
134 Logger::Error("invalid hash at chunk %d offset %" PRId64
, i
,
135 (int64_t)current
- (int64_t)m_chunks
[i
]);
139 if (*current
!= '\0') {
140 Logger::Error("missing \\0 at chunk %d offset %" PRId64
, i
,
141 (int64_t)current
- (int64_t)m_chunks
[i
]);
150 void APCFileStorage::cleanup() {
152 for (unsigned int i
= 0 ; i
< m_fileNames
.size(); i
++) {
153 unlink(m_fileNames
[i
].c_str());
158 bool APCFileStorage::addFile() {
159 if ((int64_t)m_chunks
.size() * m_chunkSize
>= m_maxSize
) {
160 m_state
= StorageState::Full
;
164 snprintf(name
, sizeof(name
), "%s.XXXXXX", m_prefix
.c_str());
165 int fd
= mkstemp(name
);
167 Logger::Error("Failed to open temp file");
170 bool couldAllocate
= false;
171 #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
172 couldAllocate
= posix_fallocate(fd
, 0, m_chunkSize
) == 0;
173 #elif defined(__APPLE__)
174 fstore_t store
= { F_ALLOCATECONTIG
, F_PEOFPOSMODE
, 0, m_chunkSize
};
175 int ret
= fcntl(fd
, F_PREALLOCATE
, &store
);
177 store
.fst_flags
= F_ALLOCATEALL
;
178 ret
= fcntl(fd
, F_PREALLOCATE
, &store
);
180 couldAllocate
= false;
183 couldAllocate
= ftruncate(fd
, m_chunkSize
) == 0;
185 #error "No implementation for posix_fallocate on your platform."
187 if (!couldAllocate
) {
188 Logger::Error("Failed to posix_fallocate of size %" PRId64
, m_chunkSize
);
192 if (apcExtension::FileStorageKeepFileLinked
) {
193 m_fileNames
.push_back(std::string(name
));
197 char *addr
= (char *)mmap(nullptr, m_chunkSize
, PROT_READ
| PROT_WRITE
,
199 if (addr
== (char *)-1) {
200 Logger::Error("Failed to mmap %s of size %" PRId64
, name
, m_chunkSize
);
204 numa_interleave(addr
, m_chunkSize
);
206 m_chunkRemain
= m_chunkSize
- PaddingSize
;
207 m_chunks
.push_back(addr
);
212 ///////////////////////////////////////////////////////////////////////////////