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"
12 #if defined(__linux__)
16 #if __has_include(<filesystem>)
18 namespace efs
= std::filesystem
;
20 #include <experimental/filesystem>
21 namespace efs
= std::experimental::filesystem
;
24 #define dout_subsys ceph_subsys_rgw
28 int D3nCacheAioWriteRequest::d3n_prepare_libaio_write_op(bufferlist
& bl
, unsigned int len
, string oid
, string cache_location
)
30 std::string location
= cache_location
+ oid
;
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
);
39 ldout(cct
, 0) << "ERROR: D3nCacheAioWriteRequest::create_io: open file failed, errno=" << errno
<< ", location='" << location
.c_str() << "'" << dendl
;
42 if (g_conf()->rgw_d3n_l1_fadvise
!= POSIX_FADV_NORMAL
)
43 posix_fadvise(fd
, 0, 0, g_conf()->rgw_d3n_l1_fadvise
);
48 ldout(cct
, 0) << "ERROR: D3nCacheAioWriteRequest::create_io: memory allocation failed" << dendl
;
52 memcpy((void*)data
, bl
.c_str(), len
);
62 D3nDataCache::D3nDataCache()
63 : cct(nullptr), io_type(_io_type::ASYNC_IO
), free_data_cache_size(0), outstanding_write_size(0)
65 lsubdout(g_ceph_context
, rgw_datacache
, 5) << "D3nDataCache: " << __func__
<< "()" << dendl
;
68 void D3nDataCache::init(CephContext
*_cct
) {
70 free_data_cache_size
= cct
->_conf
->rgw_d3n_l1_datacache_size
;
73 cache_location
= cct
->_conf
->rgw_d3n_l1_datacache_persistent_path
;
74 if(cache_location
.back() != '/') {
75 cache_location
+= "/";
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());
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
);
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
;
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
;
103 #if defined(HAVE_LIBAIO) && defined(__GLIBC__)
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");
108 ainit
.aio_idle_time
= 120;
113 int D3nDataCache::d3n_io_write(bufferlist
& bl
, unsigned int len
, std::string oid
)
115 D3nChunkDataInfo
* chunk_info
= new D3nChunkDataInfo
;
116 std::string location
= cache_location
+ oid
;
118 lsubdout(g_ceph_context
, rgw_datacache
, 20) << "D3nDataCache: " << __func__
<< "(): location=" << location
<< dendl
;
119 FILE *cache_file
= nullptr;
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
;
129 nbytes
= fwrite(bl
.c_str(), 1, len
, cache_file
);
131 ldout(cct
, 0) << "ERROR: D3nDataCache::io_write: fwrite has returned error: nbytes!=len, nbytes=" << nbytes
<< ", len=" << len
<< dendl
;
135 r
= fclose(cache_file
);
137 ldout(cct
, 0) << "ERROR: D3nDataCache::fclsoe file has return error, errno=" << errno
<< dendl
;
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
));
152 void d3n_libaio_write_cb(sigval sigval
)
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
);
160 void D3nDataCache::d3n_libaio_write_completion_cb(D3nCacheAioWriteRequest
* c
)
162 D3nChunkDataInfo
* chunk_info
{nullptr};
164 ldout(cct
, 5) << "D3nDataCache: " << __func__
<< "(): oid=" << c
->oid
<< dendl
;
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
));
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
);
186 int D3nDataCache::d3n_libaio_create_write_request(bufferlist
& bl
, unsigned int len
, std::string oid
)
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
);
191 if ((r
= wr
->d3n_prepare_libaio_write_op(bl
, len
, oid
, cache_location
)) < 0) {
192 ldout(cct
, 0) << "ERROR: D3nDataCache: " << __func__
<< "() prepare libaio write op r=" << r
<< dendl
;
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
;
200 wr
->priv_data
= this;
202 if ((r
= ::aio_write(wr
->cb
)) != 0) {
203 ldout(cct
, 0) << "ERROR: D3nDataCache: " << __func__
<< "() aio_write r=" << r
<< dendl
;
214 void D3nDataCache::put(bufferlist
& bl
, unsigned int len
, std::string
& oid
)
217 uint64_t freed_size
= 0, _free_data_cache_size
= 0, _outstanding_write_size
= 0;
219 ldout(cct
, 10) << "D3nDataCache::" << __func__
<< "(): oid=" << oid
<< ", len=" << len
<< dendl
;
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
;
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
;
232 d3n_outstanding_write_list
.insert(oid
);
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
;
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
) {
244 } else if (eviction_policy
== _eviction_policy::RANDOM
) {
245 sr
= random_eviction();
247 ldout(cct
, 0) << "D3nDataCache: Warning: unknown cache eviction policy, defaulting to lru eviction" << dendl
;
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
);
255 ldout(cct
, 20) << "D3nDataCache: completed eviction of " << sr
<< " bytes" << dendl
;
259 r
= d3n_libaio_create_write_request(bl
, len
, oid
);
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
;
267 const std::lock_guard
l(d3n_eviction_lock
);
268 free_data_cache_size
+= freed_size
;
269 outstanding_write_size
+= len
;
272 bool D3nDataCache::get(const string
& oid
, const off_t len
)
274 const std::lock_guard
l(d3n_cache_lock
);
276 string location
= cache_location
+ oid
;
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
;
284 int r
= stat(location
.c_str(), &st
);
285 if ( r
!= -1 && st
.st_size
== len
) { // file exists and containes required data range length
288 /*get D3nChunkDataInfo*/
289 const std::lock_guard
l(d3n_eviction_lock
);
291 lru_insert_head(chdo
);
293 d3n_cache_map
.erase(oid
);
294 const std::lock_guard
l(d3n_eviction_lock
);
303 size_t D3nDataCache::random_eviction()
305 lsubdout(g_ceph_context
, rgw_datacache
, 20) << "D3nDataCache: " << __func__
<< "()" << dendl
;
307 int random_index
= 0;
308 size_t freed_size
= 0;
309 D3nChunkDataInfo
* del_entry
;
310 string del_oid
, location
;
312 const std::lock_guard
l(d3n_cache_lock
);
313 n_entries
= d3n_cache_map
.size();
314 if (n_entries
<= 0) {
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
;
327 d3n_cache_map
.erase(del_oid
); // oid
330 location
= cache_location
+ del_oid
;
331 ::remove(location
.c_str());
335 size_t D3nDataCache::lru_eviction()
337 lsubdout(g_ceph_context
, rgw_datacache
, 20) << "D3nDataCache: " << __func__
<< "()" << dendl
;
339 size_t freed_size
= 0;
340 D3nChunkDataInfo
* del_entry
;
341 string del_oid
, location
;
344 const std::lock_guard
l(d3n_eviction_lock
);
346 if (del_entry
== nullptr) {
347 ldout(cct
, 2) << "D3nDataCache: lru_eviction: del_entry=null_ptr" << dendl
;
350 lru_remove(del_entry
);
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
;
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
364 freed_size
= del_entry
->size
;
366 location
= cache_location
+ del_oid
;
367 ::remove(location
.c_str());