]> git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/driver/rados/rgw_d3n_datacache.cc
ed375e2ac943ca24a343f915d4bf54476bcb3a3f
[ceph.git] / ceph / src / rgw / driver / rados / rgw_d3n_datacache.cc
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"
12 #if defined(__linux__)
13 #include <features.h>
14 #endif
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
28 int D3nCacheAioWriteRequest::d3n_prepare_libaio_write_op(bufferlist& bl, unsigned int len, string oid, string cache_location)
29 {
30 std::string location = cache_location + oid;
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
103 #if defined(HAVE_LIBAIO) && defined(__GLIBC__)
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");
108 ainit.aio_idle_time = 120;
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;
116 std::string location = cache_location + oid;
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;
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;
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;
276 string location = cache_location + oid;
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
330 location = cache_location + del_oid;
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;
366 location = cache_location + del_oid;
367 ::remove(location.c_str());
368 return freed_size;
369 }