1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
4 #include "rgw_d3n_datacache.h"
5 #include "rgw_rest_client.h"
6 #include "rgw_auth_s3.h"
8 #include "rgw_common.h"
9 #include "rgw_auth_s3.h"
11 #include "rgw_crypt_sanitize.h"
13 #if __has_include(<filesystem>)
15 namespace efs
= std::filesystem
;
17 #include <experimental/filesystem>
18 namespace efs
= std::experimental::filesystem
;
21 #define dout_subsys ceph_subsys_rgw
25 int D3nCacheAioWriteRequest::d3n_prepare_libaio_write_op(bufferlist
& bl
, unsigned int len
, string oid
, string cache_location
)
27 std::string location
= cache_location
+ oid
;
30 lsubdout(g_ceph_context
, rgw_datacache
, 20) << "D3nDataCache: " << __func__
<< "(): Write To Cache, location=" << location
<< dendl
;
31 cb
= new struct aiocb
;
32 mode_t mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
33 memset(cb
, 0, sizeof(struct aiocb
));
34 r
= fd
= ::open(location
.c_str(), O_WRONLY
| O_CREAT
| O_TRUNC
, mode
);
36 ldout(cct
, 0) << "ERROR: D3nCacheAioWriteRequest::create_io: open file failed, errno=" << errno
<< ", location='" << location
.c_str() << "'" << dendl
;
39 if (g_conf()->rgw_d3n_l1_fadvise
!= POSIX_FADV_NORMAL
)
40 posix_fadvise(fd
, 0, 0, g_conf()->rgw_d3n_l1_fadvise
);
45 ldout(cct
, 0) << "ERROR: D3nCacheAioWriteRequest::create_io: memory allocation failed" << dendl
;
49 memcpy((void*)data
, bl
.c_str(), len
);
59 D3nDataCache::D3nDataCache()
60 : cct(nullptr), io_type(_io_type::ASYNC_IO
), free_data_cache_size(0), outstanding_write_size(0)
62 lsubdout(g_ceph_context
, rgw_datacache
, 5) << "D3nDataCache: " << __func__
<< "()" << dendl
;
65 void D3nDataCache::init(CephContext
*_cct
) {
67 free_data_cache_size
= cct
->_conf
->rgw_d3n_l1_datacache_size
;
70 cache_location
= cct
->_conf
->rgw_d3n_l1_datacache_persistent_path
;
71 if(cache_location
.back() != '/') {
72 cache_location
+= "/";
75 if (efs::exists(cache_location
)) {
76 // d3n: evict the cache storage directory
77 if (g_conf()->rgw_d3n_l1_evict_cache_on_start
) {
78 lsubdout(g_ceph_context
, rgw
, 5) << "D3nDataCache: init: evicting the persistent storage directory on start" << dendl
;
79 for (auto& p
: efs::directory_iterator(cache_location
)) {
80 efs::remove_all(p
.path());
84 // create the cache storage directory
85 lsubdout(g_ceph_context
, rgw
, 5) << "D3nDataCache: init: creating the persistent storage directory on start" << dendl
;
86 efs::create_directories(cache_location
);
88 } catch (const efs::filesystem_error
& e
) {
89 lderr(g_ceph_context
) << "D3nDataCache: init: ERROR initializing the cache storage directory '" << cache_location
<<
90 "' : " << e
.what() << dendl
;
93 auto conf_eviction_policy
= cct
->_conf
.get_val
<std::string
>("rgw_d3n_l1_eviction_policy");
94 ceph_assert(conf_eviction_policy
== "lru" || conf_eviction_policy
== "random");
95 if (conf_eviction_policy
== "lru")
96 eviction_policy
= _eviction_policy::LRU
;
97 if (conf_eviction_policy
== "random")
98 eviction_policy
= _eviction_policy::RANDOM
;
100 #if defined(HAVE_LIBAIO)
102 struct aioinit ainit
{0};
103 ainit
.aio_threads
= cct
->_conf
.get_val
<int64_t>("rgw_d3n_libaio_aio_threads");
104 ainit
.aio_num
= cct
->_conf
.get_val
<int64_t>("rgw_d3n_libaio_aio_num");
105 ainit
.aio_idle_time
= 120;
110 int D3nDataCache::d3n_io_write(bufferlist
& bl
, unsigned int len
, std::string oid
)
112 D3nChunkDataInfo
* chunk_info
= new D3nChunkDataInfo
;
113 std::string location
= cache_location
+ oid
;
115 lsubdout(g_ceph_context
, rgw_datacache
, 20) << "D3nDataCache: " << __func__
<< "(): location=" << location
<< dendl
;
116 FILE *cache_file
= nullptr;
120 cache_file
= fopen(location
.c_str(), "w+");
121 if (cache_file
== nullptr) {
122 ldout(cct
, 0) << "ERROR: D3nDataCache::fopen file has return error, errno=" << errno
<< dendl
;
126 nbytes
= fwrite(bl
.c_str(), 1, len
, cache_file
);
128 ldout(cct
, 0) << "ERROR: D3nDataCache::io_write: fwrite has returned error: nbytes!=len, nbytes=" << nbytes
<< ", len=" << len
<< dendl
;
132 r
= fclose(cache_file
);
134 ldout(cct
, 0) << "ERROR: D3nDataCache::fclsoe file has return error, errno=" << errno
<< dendl
;
138 { // update cahce_map entries for new chunk in cache
139 const std::lock_guard
l(d3n_cache_lock
);
140 chunk_info
->oid
= oid
;
141 chunk_info
->set_ctx(cct
);
142 chunk_info
->size
= len
;
143 d3n_cache_map
.insert(pair
<string
, D3nChunkDataInfo
*>(oid
, chunk_info
));
149 void d3n_libaio_write_cb(sigval sigval
)
151 lsubdout(g_ceph_context
, rgw_datacache
, 30) << "D3nDataCache: " << __func__
<< "()" << dendl
;
152 D3nCacheAioWriteRequest
* c
= static_cast<D3nCacheAioWriteRequest
*>(sigval
.sival_ptr
);
153 c
->priv_data
->d3n_libaio_write_completion_cb(c
);
157 void D3nDataCache::d3n_libaio_write_completion_cb(D3nCacheAioWriteRequest
* c
)
159 D3nChunkDataInfo
* chunk_info
{nullptr};
161 ldout(cct
, 5) << "D3nDataCache: " << __func__
<< "(): oid=" << c
->oid
<< dendl
;
163 { // update cache_map entries for new chunk in cache
164 const std::lock_guard
l(d3n_cache_lock
);
165 d3n_outstanding_write_list
.erase(c
->oid
);
166 chunk_info
= new D3nChunkDataInfo
;
167 chunk_info
->oid
= c
->oid
;
168 chunk_info
->set_ctx(cct
);
169 chunk_info
->size
= c
->cb
->aio_nbytes
;
170 d3n_cache_map
.insert(pair
<string
, D3nChunkDataInfo
*>(c
->oid
, chunk_info
));
173 { // update free size
174 const std::lock_guard
l(d3n_eviction_lock
);
175 free_data_cache_size
-= c
->cb
->aio_nbytes
;
176 outstanding_write_size
-= c
->cb
->aio_nbytes
;
177 lru_insert_head(chunk_info
);
183 int D3nDataCache::d3n_libaio_create_write_request(bufferlist
& bl
, unsigned int len
, std::string oid
)
185 lsubdout(g_ceph_context
, rgw_datacache
, 30) << "D3nDataCache: " << __func__
<< "(): Write To Cache, oid=" << oid
<< ", len=" << len
<< dendl
;
186 struct D3nCacheAioWriteRequest
* wr
= new struct D3nCacheAioWriteRequest(cct
);
188 if ((r
= wr
->d3n_prepare_libaio_write_op(bl
, len
, oid
, cache_location
)) < 0) {
189 ldout(cct
, 0) << "ERROR: D3nDataCache: " << __func__
<< "() prepare libaio write op r=" << r
<< dendl
;
192 wr
->cb
->aio_sigevent
.sigev_notify
= SIGEV_THREAD
;
193 wr
->cb
->aio_sigevent
.sigev_notify_function
= d3n_libaio_write_cb
;
194 wr
->cb
->aio_sigevent
.sigev_notify_attributes
= nullptr;
195 wr
->cb
->aio_sigevent
.sigev_value
.sival_ptr
= (void*)wr
;
197 wr
->priv_data
= this;
199 if ((r
= ::aio_write(wr
->cb
)) != 0) {
200 ldout(cct
, 0) << "ERROR: D3nDataCache: " << __func__
<< "() aio_write r=" << r
<< dendl
;
211 void D3nDataCache::put(bufferlist
& bl
, unsigned int len
, std::string
& oid
)
214 uint64_t freed_size
= 0, _free_data_cache_size
= 0, _outstanding_write_size
= 0;
216 ldout(cct
, 10) << "D3nDataCache::" << __func__
<< "(): oid=" << oid
<< ", len=" << len
<< dendl
;
218 const std::lock_guard
l(d3n_cache_lock
);
219 std::unordered_map
<string
, D3nChunkDataInfo
*>::iterator iter
= d3n_cache_map
.find(oid
);
220 if (iter
!= d3n_cache_map
.end()) {
221 ldout(cct
, 10) << "D3nDataCache::" << __func__
<< "(): data already cached, no rewrite" << dendl
;
224 auto it
= d3n_outstanding_write_list
.find(oid
);
225 if (it
!= d3n_outstanding_write_list
.end()) {
226 ldout(cct
, 10) << "D3nDataCache: NOTE: data put in cache already issued, no rewrite" << dendl
;
229 d3n_outstanding_write_list
.insert(oid
);
232 const std::lock_guard
l(d3n_eviction_lock
);
233 _free_data_cache_size
= free_data_cache_size
;
234 _outstanding_write_size
= outstanding_write_size
;
236 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
;
237 while (len
> (_free_data_cache_size
- _outstanding_write_size
+ freed_size
)) {
238 ldout(cct
, 20) << "D3nDataCache: enter eviction" << dendl
;
239 if (eviction_policy
== _eviction_policy::LRU
) {
241 } else if (eviction_policy
== _eviction_policy::RANDOM
) {
242 sr
= random_eviction();
244 ldout(cct
, 0) << "D3nDataCache: Warning: unknown cache eviction policy, defaulting to lru eviction" << dendl
;
248 ldout(cct
, 2) << "D3nDataCache: Warning: eviction was not able to free disk space, not writing to cache" << dendl
;
249 d3n_outstanding_write_list
.erase(oid
);
252 ldout(cct
, 20) << "D3nDataCache: completed eviction of " << sr
<< " bytes" << dendl
;
256 r
= d3n_libaio_create_write_request(bl
, len
, oid
);
258 const std::lock_guard
l(d3n_cache_lock
);
259 d3n_outstanding_write_list
.erase(oid
);
260 ldout(cct
, 1) << "D3nDataCache: create_aio_write_request fail, r=" << r
<< dendl
;
264 const std::lock_guard
l(d3n_eviction_lock
);
265 free_data_cache_size
+= freed_size
;
266 outstanding_write_size
+= len
;
269 bool D3nDataCache::get(const string
& oid
, const off_t len
)
271 const std::lock_guard
l(d3n_cache_lock
);
273 string location
= cache_location
+ oid
;
275 lsubdout(g_ceph_context
, rgw_datacache
, 20) << "D3nDataCache: " << __func__
<< "(): location=" << location
<< dendl
;
276 std::unordered_map
<string
, D3nChunkDataInfo
*>::iterator iter
= d3n_cache_map
.find(oid
);
277 if (!(iter
== d3n_cache_map
.end())) {
278 // check inside cache whether file exists or not!!!! then make exist true;
279 struct D3nChunkDataInfo
* chdo
= iter
->second
;
281 int r
= stat(location
.c_str(), &st
);
282 if ( r
!= -1 && st
.st_size
== len
) { // file exists and containes required data range length
285 /*get D3nChunkDataInfo*/
286 const std::lock_guard
l(d3n_eviction_lock
);
288 lru_insert_head(chdo
);
290 d3n_cache_map
.erase(oid
);
291 const std::lock_guard
l(d3n_eviction_lock
);
300 size_t D3nDataCache::random_eviction()
302 lsubdout(g_ceph_context
, rgw_datacache
, 20) << "D3nDataCache: " << __func__
<< "()" << dendl
;
304 int random_index
= 0;
305 size_t freed_size
= 0;
306 D3nChunkDataInfo
* del_entry
;
307 string del_oid
, location
;
309 const std::lock_guard
l(d3n_cache_lock
);
310 n_entries
= d3n_cache_map
.size();
311 if (n_entries
<= 0) {
315 random_index
= ceph::util::generate_random_number
<int>(0, n_entries
-1);
316 std::unordered_map
<string
, D3nChunkDataInfo
*>::iterator iter
= d3n_cache_map
.begin();
317 std::advance(iter
, random_index
);
318 del_oid
= iter
->first
;
319 del_entry
= iter
->second
;
320 ldout(cct
, 20) << "D3nDataCache: random_eviction: index:" << random_index
<< ", free size: " << del_entry
->size
<< dendl
;
321 freed_size
= del_entry
->size
;
324 d3n_cache_map
.erase(del_oid
); // oid
327 location
= cache_location
+ del_oid
;
328 ::remove(location
.c_str());
332 size_t D3nDataCache::lru_eviction()
334 lsubdout(g_ceph_context
, rgw_datacache
, 20) << "D3nDataCache: " << __func__
<< "()" << dendl
;
336 size_t freed_size
= 0;
337 D3nChunkDataInfo
* del_entry
;
338 string del_oid
, location
;
341 const std::lock_guard
l(d3n_eviction_lock
);
343 if (del_entry
== nullptr) {
344 ldout(cct
, 2) << "D3nDataCache: lru_eviction: del_entry=null_ptr" << dendl
;
347 lru_remove(del_entry
);
351 const std::lock_guard
l(d3n_cache_lock
);
352 n_entries
= d3n_cache_map
.size();
353 if (n_entries
<= 0) {
354 ldout(cct
, 2) << "D3nDataCache: lru_eviction: cache_map.size<=0" << dendl
;
357 del_oid
= del_entry
->oid
;
358 ldout(cct
, 20) << "D3nDataCache: lru_eviction: oid to remove: " << del_oid
<< dendl
;
359 d3n_cache_map
.erase(del_oid
); // oid
361 freed_size
= del_entry
->size
;
363 location
= cache_location
+ del_oid
;
364 ::remove(location
.c_str());