]>
Commit | Line | Data |
---|---|---|
20effc67 TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab ft=cpp | |
3 | ||
4 | #include "rgw_d3n_datacache.h" | |
5 | #include "rgw_rest_client.h" | |
6 | #include "rgw_auth_s3.h" | |
7 | #include "rgw_op.h" | |
8 | #include "rgw_common.h" | |
9 | #include "rgw_auth_s3.h" | |
10 | #include "rgw_op.h" | |
11 | #include "rgw_crypt_sanitize.h" | |
1e59de90 TL |
12 | #if defined(__linux__) |
13 | #include <features.h> | |
14 | #endif | |
20effc67 TL |
15 | |
16 | #if __has_include(<filesystem>) | |
17 | #include <filesystem> | |
18 | namespace efs = std::filesystem; | |
19 | #else | |
20 | #include <experimental/filesystem> | |
21 | namespace efs = std::experimental::filesystem; | |
22 | #endif | |
23 | ||
24 | #define dout_subsys ceph_subsys_rgw | |
25 | ||
26 | using namespace std; | |
27 | ||
05a536ef | 28 | int D3nCacheAioWriteRequest::d3n_libaio_prepare_write_op(bufferlist& bl, unsigned int len, string oid, string cache_location) |
20effc67 | 29 | { |
05a536ef | 30 | std::string location = cache_location + url_encode(oid, true); |
20effc67 TL |
31 | int r = 0; |
32 | ||
33 | lsubdout(g_ceph_context, rgw_datacache, 20) << "D3nDataCache: " << __func__ << "(): Write To Cache, location=" << location << dendl; | |
34 | cb = new struct aiocb; | |
35 | mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; | |
36 | memset(cb, 0, sizeof(struct aiocb)); | |
37 | r = fd = ::open(location.c_str(), O_WRONLY | O_CREAT | O_TRUNC, mode); | |
38 | if (fd < 0) { | |
39 | ldout(cct, 0) << "ERROR: D3nCacheAioWriteRequest::create_io: open file failed, errno=" << errno << ", location='" << location.c_str() << "'" << dendl; | |
40 | goto done; | |
41 | } | |
42 | if (g_conf()->rgw_d3n_l1_fadvise != POSIX_FADV_NORMAL) | |
43 | posix_fadvise(fd, 0, 0, g_conf()->rgw_d3n_l1_fadvise); | |
44 | cb->aio_fildes = fd; | |
45 | ||
46 | data = malloc(len); | |
47 | if (!data) { | |
48 | ldout(cct, 0) << "ERROR: D3nCacheAioWriteRequest::create_io: memory allocation failed" << dendl; | |
49 | goto close_file; | |
50 | } | |
51 | cb->aio_buf = data; | |
52 | memcpy((void*)data, bl.c_str(), len); | |
53 | cb->aio_nbytes = len; | |
54 | goto done; | |
55 | ||
56 | close_file: | |
57 | ::close(fd); | |
58 | done: | |
59 | return r; | |
60 | } | |
61 | ||
62 | D3nDataCache::D3nDataCache() | |
63 | : cct(nullptr), io_type(_io_type::ASYNC_IO), free_data_cache_size(0), outstanding_write_size(0) | |
64 | { | |
65 | lsubdout(g_ceph_context, rgw_datacache, 5) << "D3nDataCache: " << __func__ << "()" << dendl; | |
66 | } | |
67 | ||
68 | void D3nDataCache::init(CephContext *_cct) { | |
69 | cct = _cct; | |
70 | free_data_cache_size = cct->_conf->rgw_d3n_l1_datacache_size; | |
71 | head = nullptr; | |
72 | tail = nullptr; | |
73 | cache_location = cct->_conf->rgw_d3n_l1_datacache_persistent_path; | |
74 | if(cache_location.back() != '/') { | |
75 | cache_location += "/"; | |
76 | } | |
77 | try { | |
78 | if (efs::exists(cache_location)) { | |
79 | // d3n: evict the cache storage directory | |
80 | if (g_conf()->rgw_d3n_l1_evict_cache_on_start) { | |
81 | lsubdout(g_ceph_context, rgw, 5) << "D3nDataCache: init: evicting the persistent storage directory on start" << dendl; | |
82 | for (auto& p : efs::directory_iterator(cache_location)) { | |
83 | efs::remove_all(p.path()); | |
84 | } | |
85 | } | |
86 | } else { | |
87 | // create the cache storage directory | |
88 | lsubdout(g_ceph_context, rgw, 5) << "D3nDataCache: init: creating the persistent storage directory on start" << dendl; | |
89 | efs::create_directories(cache_location); | |
90 | } | |
91 | } catch (const efs::filesystem_error& e) { | |
92 | lderr(g_ceph_context) << "D3nDataCache: init: ERROR initializing the cache storage directory '" << cache_location << | |
93 | "' : " << e.what() << dendl; | |
94 | } | |
95 | ||
96 | auto conf_eviction_policy = cct->_conf.get_val<std::string>("rgw_d3n_l1_eviction_policy"); | |
97 | ceph_assert(conf_eviction_policy == "lru" || conf_eviction_policy == "random"); | |
98 | if (conf_eviction_policy == "lru") | |
99 | eviction_policy = _eviction_policy::LRU; | |
100 | if (conf_eviction_policy == "random") | |
101 | eviction_policy = _eviction_policy::RANDOM; | |
102 | ||
1e59de90 | 103 | #if defined(HAVE_LIBAIO) && defined(__GLIBC__) |
20effc67 TL |
104 | // libaio setup |
105 | struct aioinit ainit{0}; | |
106 | ainit.aio_threads = cct->_conf.get_val<int64_t>("rgw_d3n_libaio_aio_threads"); | |
107 | ainit.aio_num = cct->_conf.get_val<int64_t>("rgw_d3n_libaio_aio_num"); | |
05a536ef | 108 | ainit.aio_idle_time = 10; |
20effc67 TL |
109 | aio_init(&ainit); |
110 | #endif | |
111 | } | |
112 | ||
113 | int D3nDataCache::d3n_io_write(bufferlist& bl, unsigned int len, std::string oid) | |
114 | { | |
115 | D3nChunkDataInfo* chunk_info = new D3nChunkDataInfo; | |
05a536ef | 116 | std::string location = cache_location + url_encode(oid, true); |
20effc67 TL |
117 | |
118 | lsubdout(g_ceph_context, rgw_datacache, 20) << "D3nDataCache: " << __func__ << "(): location=" << location << dendl; | |
119 | FILE *cache_file = nullptr; | |
120 | int r = 0; | |
121 | size_t nbytes = 0; | |
122 | ||
123 | cache_file = fopen(location.c_str(), "w+"); | |
124 | if (cache_file == nullptr) { | |
125 | ldout(cct, 0) << "ERROR: D3nDataCache::fopen file has return error, errno=" << errno << dendl; | |
126 | return -errno; | |
127 | } | |
128 | ||
129 | nbytes = fwrite(bl.c_str(), 1, len, cache_file); | |
130 | if (nbytes != len) { | |
131 | ldout(cct, 0) << "ERROR: D3nDataCache::io_write: fwrite has returned error: nbytes!=len, nbytes=" << nbytes << ", len=" << len << dendl; | |
132 | return -EIO; | |
133 | } | |
134 | ||
135 | r = fclose(cache_file); | |
136 | if (r != 0) { | |
137 | ldout(cct, 0) << "ERROR: D3nDataCache::fclsoe file has return error, errno=" << errno << dendl; | |
138 | return -errno; | |
139 | } | |
140 | ||
141 | { // update cahce_map entries for new chunk in cache | |
142 | const std::lock_guard l(d3n_cache_lock); | |
143 | chunk_info->oid = oid; | |
144 | chunk_info->set_ctx(cct); | |
145 | chunk_info->size = len; | |
146 | d3n_cache_map.insert(pair<string, D3nChunkDataInfo*>(oid, chunk_info)); | |
147 | } | |
148 | ||
149 | return r; | |
150 | } | |
151 | ||
152 | void d3n_libaio_write_cb(sigval sigval) | |
153 | { | |
154 | lsubdout(g_ceph_context, rgw_datacache, 30) << "D3nDataCache: " << __func__ << "()" << dendl; | |
155 | D3nCacheAioWriteRequest* c = static_cast<D3nCacheAioWriteRequest*>(sigval.sival_ptr); | |
156 | c->priv_data->d3n_libaio_write_completion_cb(c); | |
157 | } | |
158 | ||
159 | ||
160 | void D3nDataCache::d3n_libaio_write_completion_cb(D3nCacheAioWriteRequest* c) | |
161 | { | |
162 | D3nChunkDataInfo* chunk_info{nullptr}; | |
163 | ||
164 | ldout(cct, 5) << "D3nDataCache: " << __func__ << "(): oid=" << c->oid << dendl; | |
165 | ||
166 | { // update cache_map entries for new chunk in cache | |
167 | const std::lock_guard l(d3n_cache_lock); | |
168 | d3n_outstanding_write_list.erase(c->oid); | |
169 | chunk_info = new D3nChunkDataInfo; | |
170 | chunk_info->oid = c->oid; | |
171 | chunk_info->set_ctx(cct); | |
172 | chunk_info->size = c->cb->aio_nbytes; | |
173 | d3n_cache_map.insert(pair<string, D3nChunkDataInfo*>(c->oid, chunk_info)); | |
174 | } | |
175 | ||
176 | { // update free size | |
177 | const std::lock_guard l(d3n_eviction_lock); | |
178 | free_data_cache_size -= c->cb->aio_nbytes; | |
179 | outstanding_write_size -= c->cb->aio_nbytes; | |
180 | lru_insert_head(chunk_info); | |
181 | } | |
182 | delete c; | |
183 | c = nullptr; | |
184 | } | |
185 | ||
186 | int D3nDataCache::d3n_libaio_create_write_request(bufferlist& bl, unsigned int len, std::string oid) | |
187 | { | |
188 | lsubdout(g_ceph_context, rgw_datacache, 30) << "D3nDataCache: " << __func__ << "(): Write To Cache, oid=" << oid << ", len=" << len << dendl; | |
189 | struct D3nCacheAioWriteRequest* wr = new struct D3nCacheAioWriteRequest(cct); | |
190 | int r=0; | |
05a536ef | 191 | if ((r = wr->d3n_libaio_prepare_write_op(bl, len, oid, cache_location)) < 0) { |
20effc67 TL |
192 | ldout(cct, 0) << "ERROR: D3nDataCache: " << __func__ << "() prepare libaio write op r=" << r << dendl; |
193 | goto done; | |
194 | } | |
195 | wr->cb->aio_sigevent.sigev_notify = SIGEV_THREAD; | |
196 | wr->cb->aio_sigevent.sigev_notify_function = d3n_libaio_write_cb; | |
197 | wr->cb->aio_sigevent.sigev_notify_attributes = nullptr; | |
198 | wr->cb->aio_sigevent.sigev_value.sival_ptr = (void*)wr; | |
199 | wr->oid = oid; | |
200 | wr->priv_data = this; | |
201 | ||
202 | if ((r = ::aio_write(wr->cb)) != 0) { | |
203 | ldout(cct, 0) << "ERROR: D3nDataCache: " << __func__ << "() aio_write r=" << r << dendl; | |
204 | goto error; | |
205 | } | |
206 | return 0; | |
207 | ||
208 | error: | |
209 | delete wr; | |
210 | done: | |
211 | return r; | |
212 | } | |
213 | ||
214 | void D3nDataCache::put(bufferlist& bl, unsigned int len, std::string& oid) | |
215 | { | |
216 | size_t sr = 0; | |
217 | uint64_t freed_size = 0, _free_data_cache_size = 0, _outstanding_write_size = 0; | |
218 | ||
219 | ldout(cct, 10) << "D3nDataCache::" << __func__ << "(): oid=" << oid << ", len=" << len << dendl; | |
220 | { | |
221 | const std::lock_guard l(d3n_cache_lock); | |
222 | std::unordered_map<string, D3nChunkDataInfo*>::iterator iter = d3n_cache_map.find(oid); | |
223 | if (iter != d3n_cache_map.end()) { | |
224 | ldout(cct, 10) << "D3nDataCache::" << __func__ << "(): data already cached, no rewrite" << dendl; | |
225 | return; | |
226 | } | |
227 | auto it = d3n_outstanding_write_list.find(oid); | |
228 | if (it != d3n_outstanding_write_list.end()) { | |
229 | ldout(cct, 10) << "D3nDataCache: NOTE: data put in cache already issued, no rewrite" << dendl; | |
230 | return; | |
231 | } | |
232 | d3n_outstanding_write_list.insert(oid); | |
233 | } | |
234 | { | |
235 | const std::lock_guard l(d3n_eviction_lock); | |
236 | _free_data_cache_size = free_data_cache_size; | |
237 | _outstanding_write_size = outstanding_write_size; | |
238 | } | |
239 | ldout(cct, 20) << "D3nDataCache: Before eviction _free_data_cache_size:" << _free_data_cache_size << ", _outstanding_write_size:" << _outstanding_write_size << ", freed_size:" << freed_size << dendl; | |
240 | while (len > (_free_data_cache_size - _outstanding_write_size + freed_size)) { | |
241 | ldout(cct, 20) << "D3nDataCache: enter eviction" << dendl; | |
242 | if (eviction_policy == _eviction_policy::LRU) { | |
243 | sr = lru_eviction(); | |
244 | } else if (eviction_policy == _eviction_policy::RANDOM) { | |
245 | sr = random_eviction(); | |
246 | } else { | |
247 | ldout(cct, 0) << "D3nDataCache: Warning: unknown cache eviction policy, defaulting to lru eviction" << dendl; | |
248 | sr = lru_eviction(); | |
249 | } | |
250 | if (sr == 0) { | |
251 | ldout(cct, 2) << "D3nDataCache: Warning: eviction was not able to free disk space, not writing to cache" << dendl; | |
252 | d3n_outstanding_write_list.erase(oid); | |
253 | return; | |
254 | } | |
255 | ldout(cct, 20) << "D3nDataCache: completed eviction of " << sr << " bytes" << dendl; | |
256 | freed_size += sr; | |
257 | } | |
258 | int r = 0; | |
259 | r = d3n_libaio_create_write_request(bl, len, oid); | |
260 | if (r < 0) { | |
261 | const std::lock_guard l(d3n_cache_lock); | |
262 | d3n_outstanding_write_list.erase(oid); | |
263 | ldout(cct, 1) << "D3nDataCache: create_aio_write_request fail, r=" << r << dendl; | |
264 | return; | |
265 | } | |
266 | ||
267 | const std::lock_guard l(d3n_eviction_lock); | |
268 | free_data_cache_size += freed_size; | |
269 | outstanding_write_size += len; | |
270 | } | |
271 | ||
272 | bool D3nDataCache::get(const string& oid, const off_t len) | |
273 | { | |
274 | const std::lock_guard l(d3n_cache_lock); | |
275 | bool exist = false; | |
05a536ef | 276 | string location = cache_location + url_encode(oid, true); |
20effc67 TL |
277 | |
278 | lsubdout(g_ceph_context, rgw_datacache, 20) << "D3nDataCache: " << __func__ << "(): location=" << location << dendl; | |
279 | std::unordered_map<string, D3nChunkDataInfo*>::iterator iter = d3n_cache_map.find(oid); | |
280 | if (!(iter == d3n_cache_map.end())) { | |
281 | // check inside cache whether file exists or not!!!! then make exist true; | |
282 | struct D3nChunkDataInfo* chdo = iter->second; | |
283 | struct stat st; | |
284 | int r = stat(location.c_str(), &st); | |
285 | if ( r != -1 && st.st_size == len) { // file exists and containes required data range length | |
286 | exist = true; | |
287 | /*LRU*/ | |
288 | /*get D3nChunkDataInfo*/ | |
289 | const std::lock_guard l(d3n_eviction_lock); | |
290 | lru_remove(chdo); | |
291 | lru_insert_head(chdo); | |
292 | } else { | |
293 | d3n_cache_map.erase(oid); | |
294 | const std::lock_guard l(d3n_eviction_lock); | |
295 | lru_remove(chdo); | |
296 | delete chdo; | |
297 | exist = false; | |
298 | } | |
299 | } | |
300 | return exist; | |
301 | } | |
302 | ||
303 | size_t D3nDataCache::random_eviction() | |
304 | { | |
305 | lsubdout(g_ceph_context, rgw_datacache, 20) << "D3nDataCache: " << __func__ << "()" << dendl; | |
306 | int n_entries = 0; | |
307 | int random_index = 0; | |
308 | size_t freed_size = 0; | |
309 | D3nChunkDataInfo* del_entry; | |
310 | string del_oid, location; | |
311 | { | |
312 | const std::lock_guard l(d3n_cache_lock); | |
313 | n_entries = d3n_cache_map.size(); | |
314 | if (n_entries <= 0) { | |
315 | return -1; | |
316 | } | |
317 | srand (time(NULL)); | |
318 | random_index = ceph::util::generate_random_number<int>(0, n_entries-1); | |
319 | std::unordered_map<string, D3nChunkDataInfo*>::iterator iter = d3n_cache_map.begin(); | |
320 | std::advance(iter, random_index); | |
321 | del_oid = iter->first; | |
322 | del_entry = iter->second; | |
323 | ldout(cct, 20) << "D3nDataCache: random_eviction: index:" << random_index << ", free size: " << del_entry->size << dendl; | |
324 | freed_size = del_entry->size; | |
325 | delete del_entry; | |
326 | del_entry = nullptr; | |
327 | d3n_cache_map.erase(del_oid); // oid | |
328 | } | |
329 | ||
05a536ef | 330 | location = cache_location + url_encode(del_oid, true); |
20effc67 TL |
331 | ::remove(location.c_str()); |
332 | return freed_size; | |
333 | } | |
334 | ||
335 | size_t D3nDataCache::lru_eviction() | |
336 | { | |
337 | lsubdout(g_ceph_context, rgw_datacache, 20) << "D3nDataCache: " << __func__ << "()" << dendl; | |
338 | int n_entries = 0; | |
339 | size_t freed_size = 0; | |
340 | D3nChunkDataInfo* del_entry; | |
341 | string del_oid, location; | |
342 | ||
343 | { | |
344 | const std::lock_guard l(d3n_eviction_lock); | |
345 | del_entry = tail; | |
346 | if (del_entry == nullptr) { | |
347 | ldout(cct, 2) << "D3nDataCache: lru_eviction: del_entry=null_ptr" << dendl; | |
348 | return 0; | |
349 | } | |
350 | lru_remove(del_entry); | |
351 | } | |
352 | ||
353 | { | |
354 | const std::lock_guard l(d3n_cache_lock); | |
355 | n_entries = d3n_cache_map.size(); | |
356 | if (n_entries <= 0) { | |
357 | ldout(cct, 2) << "D3nDataCache: lru_eviction: cache_map.size<=0" << dendl; | |
358 | return -1; | |
359 | } | |
360 | del_oid = del_entry->oid; | |
361 | ldout(cct, 20) << "D3nDataCache: lru_eviction: oid to remove: " << del_oid << dendl; | |
362 | d3n_cache_map.erase(del_oid); // oid | |
363 | } | |
364 | freed_size = del_entry->size; | |
365 | delete del_entry; | |
05a536ef | 366 | location = cache_location + url_encode(del_oid, true); |
20effc67 TL |
367 | ::remove(location.c_str()); |
368 | return freed_size; | |
369 | } |