]> git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/rgw_d3n_datacache.cc
import quincy 17.2.0
[ceph.git] / ceph / src / rgw / 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
13 #if __has_include(<filesystem>)
14 #include <filesystem>
15 namespace efs = std::filesystem;
16 #else
17 #include <experimental/filesystem>
18 namespace efs = std::experimental::filesystem;
19 #endif
20
21 #define dout_subsys ceph_subsys_rgw
22
23 using namespace std;
24
25 int D3nCacheAioWriteRequest::d3n_prepare_libaio_write_op(bufferlist& bl, unsigned int len, string oid, string cache_location)
26 {
27 std::string location = cache_location + oid;
28 int r = 0;
29
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);
35 if (fd < 0) {
36 ldout(cct, 0) << "ERROR: D3nCacheAioWriteRequest::create_io: open file failed, errno=" << errno << ", location='" << location.c_str() << "'" << dendl;
37 goto done;
38 }
39 if (g_conf()->rgw_d3n_l1_fadvise != POSIX_FADV_NORMAL)
40 posix_fadvise(fd, 0, 0, g_conf()->rgw_d3n_l1_fadvise);
41 cb->aio_fildes = fd;
42
43 data = malloc(len);
44 if (!data) {
45 ldout(cct, 0) << "ERROR: D3nCacheAioWriteRequest::create_io: memory allocation failed" << dendl;
46 goto close_file;
47 }
48 cb->aio_buf = data;
49 memcpy((void*)data, bl.c_str(), len);
50 cb->aio_nbytes = len;
51 goto done;
52
53 close_file:
54 ::close(fd);
55 done:
56 return r;
57 }
58
59 D3nDataCache::D3nDataCache()
60 : cct(nullptr), io_type(_io_type::ASYNC_IO), free_data_cache_size(0), outstanding_write_size(0)
61 {
62 lsubdout(g_ceph_context, rgw_datacache, 5) << "D3nDataCache: " << __func__ << "()" << dendl;
63 }
64
65 void D3nDataCache::init(CephContext *_cct) {
66 cct = _cct;
67 free_data_cache_size = cct->_conf->rgw_d3n_l1_datacache_size;
68 head = nullptr;
69 tail = nullptr;
70 cache_location = cct->_conf->rgw_d3n_l1_datacache_persistent_path;
71 if(cache_location.back() != '/') {
72 cache_location += "/";
73 }
74 try {
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());
81 }
82 }
83 } else {
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);
87 }
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;
91 }
92
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;
99
100 #if defined(HAVE_LIBAIO)
101 // libaio setup
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;
106 aio_init(&ainit);
107 #endif
108 }
109
110 int D3nDataCache::d3n_io_write(bufferlist& bl, unsigned int len, std::string oid)
111 {
112 D3nChunkDataInfo* chunk_info = new D3nChunkDataInfo;
113 std::string location = cache_location + oid;
114
115 lsubdout(g_ceph_context, rgw_datacache, 20) << "D3nDataCache: " << __func__ << "(): location=" << location << dendl;
116 FILE *cache_file = nullptr;
117 int r = 0;
118 size_t nbytes = 0;
119
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;
123 return -errno;
124 }
125
126 nbytes = fwrite(bl.c_str(), 1, len, cache_file);
127 if (nbytes != len) {
128 ldout(cct, 0) << "ERROR: D3nDataCache::io_write: fwrite has returned error: nbytes!=len, nbytes=" << nbytes << ", len=" << len << dendl;
129 return -EIO;
130 }
131
132 r = fclose(cache_file);
133 if (r != 0) {
134 ldout(cct, 0) << "ERROR: D3nDataCache::fclsoe file has return error, errno=" << errno << dendl;
135 return -errno;
136 }
137
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));
144 }
145
146 return r;
147 }
148
149 void d3n_libaio_write_cb(sigval sigval)
150 {
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);
154 }
155
156
157 void D3nDataCache::d3n_libaio_write_completion_cb(D3nCacheAioWriteRequest* c)
158 {
159 D3nChunkDataInfo* chunk_info{nullptr};
160
161 ldout(cct, 5) << "D3nDataCache: " << __func__ << "(): oid=" << c->oid << dendl;
162
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));
171 }
172
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);
178 }
179 delete c;
180 c = nullptr;
181 }
182
183 int D3nDataCache::d3n_libaio_create_write_request(bufferlist& bl, unsigned int len, std::string oid)
184 {
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);
187 int r=0;
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;
190 goto done;
191 }
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;
196 wr->oid = oid;
197 wr->priv_data = this;
198
199 if ((r = ::aio_write(wr->cb)) != 0) {
200 ldout(cct, 0) << "ERROR: D3nDataCache: " << __func__ << "() aio_write r=" << r << dendl;
201 goto error;
202 }
203 return 0;
204
205 error:
206 delete wr;
207 done:
208 return r;
209 }
210
211 void D3nDataCache::put(bufferlist& bl, unsigned int len, std::string& oid)
212 {
213 size_t sr = 0;
214 uint64_t freed_size = 0, _free_data_cache_size = 0, _outstanding_write_size = 0;
215
216 ldout(cct, 10) << "D3nDataCache::" << __func__ << "(): oid=" << oid << ", len=" << len << dendl;
217 {
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;
222 return;
223 }
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;
227 return;
228 }
229 d3n_outstanding_write_list.insert(oid);
230 }
231 {
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;
235 }
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) {
240 sr = lru_eviction();
241 } else if (eviction_policy == _eviction_policy::RANDOM) {
242 sr = random_eviction();
243 } else {
244 ldout(cct, 0) << "D3nDataCache: Warning: unknown cache eviction policy, defaulting to lru eviction" << dendl;
245 sr = lru_eviction();
246 }
247 if (sr == 0) {
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);
250 return;
251 }
252 ldout(cct, 20) << "D3nDataCache: completed eviction of " << sr << " bytes" << dendl;
253 freed_size += sr;
254 }
255 int r = 0;
256 r = d3n_libaio_create_write_request(bl, len, oid);
257 if (r < 0) {
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;
261 return;
262 }
263
264 const std::lock_guard l(d3n_eviction_lock);
265 free_data_cache_size += freed_size;
266 outstanding_write_size += len;
267 }
268
269 bool D3nDataCache::get(const string& oid, const off_t len)
270 {
271 const std::lock_guard l(d3n_cache_lock);
272 bool exist = false;
273 string location = cache_location + oid;
274
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;
280 struct stat st;
281 int r = stat(location.c_str(), &st);
282 if ( r != -1 && st.st_size == len) { // file exists and containes required data range length
283 exist = true;
284 /*LRU*/
285 /*get D3nChunkDataInfo*/
286 const std::lock_guard l(d3n_eviction_lock);
287 lru_remove(chdo);
288 lru_insert_head(chdo);
289 } else {
290 d3n_cache_map.erase(oid);
291 const std::lock_guard l(d3n_eviction_lock);
292 lru_remove(chdo);
293 delete chdo;
294 exist = false;
295 }
296 }
297 return exist;
298 }
299
300 size_t D3nDataCache::random_eviction()
301 {
302 lsubdout(g_ceph_context, rgw_datacache, 20) << "D3nDataCache: " << __func__ << "()" << dendl;
303 int n_entries = 0;
304 int random_index = 0;
305 size_t freed_size = 0;
306 D3nChunkDataInfo* del_entry;
307 string del_oid, location;
308 {
309 const std::lock_guard l(d3n_cache_lock);
310 n_entries = d3n_cache_map.size();
311 if (n_entries <= 0) {
312 return -1;
313 }
314 srand (time(NULL));
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;
322 delete del_entry;
323 del_entry = nullptr;
324 d3n_cache_map.erase(del_oid); // oid
325 }
326
327 location = cache_location + del_oid;
328 ::remove(location.c_str());
329 return freed_size;
330 }
331
332 size_t D3nDataCache::lru_eviction()
333 {
334 lsubdout(g_ceph_context, rgw_datacache, 20) << "D3nDataCache: " << __func__ << "()" << dendl;
335 int n_entries = 0;
336 size_t freed_size = 0;
337 D3nChunkDataInfo* del_entry;
338 string del_oid, location;
339
340 {
341 const std::lock_guard l(d3n_eviction_lock);
342 del_entry = tail;
343 if (del_entry == nullptr) {
344 ldout(cct, 2) << "D3nDataCache: lru_eviction: del_entry=null_ptr" << dendl;
345 return 0;
346 }
347 lru_remove(del_entry);
348 }
349
350 {
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;
355 return -1;
356 }
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
360 }
361 freed_size = del_entry->size;
362 delete del_entry;
363 location = cache_location + del_oid;
364 ::remove(location.c_str());
365 return freed_size;
366 }