1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include "common/debug.h"
5 #include "SimplePolicy.h"
6
7 #define dout_context g_ceph_context
8 #define dout_subsys ceph_subsys_immutable_obj_cache
9 #undef dout_prefix
10 #define dout_prefix *_dout << "ceph::cache::SimplePolicy: " << this << " " \
11 << __func__ << ": "
12
13 namespace ceph {
14 namespace immutable_obj_cache {
15
16 SimplePolicy::SimplePolicy(CephContext *cct, uint64_t cache_size,
17 uint64_t max_inflight, double watermark)
18 : cct(cct), m_watermark(watermark), m_max_inflight_ops(max_inflight),
19 m_max_cache_size(cache_size) {
20
21 ldout(cct, 20) << "max cache size= " << m_max_cache_size
22 << " ,watermark= " << m_watermark
23 << " ,max inflight ops= " << m_max_inflight_ops << dendl;
24
25 m_cache_size = 0;
26
27 }
28
29 SimplePolicy::~SimplePolicy() {
30 ldout(cct, 20) << dendl;
31
32 for (auto it : m_cache_map) {
33 Entry* entry = (it.second);
34 delete entry;
35 }
36 }
37
38 cache_status_t SimplePolicy::alloc_entry(std::string file_name) {
39 ldout(cct, 20) << "alloc entry for: " << file_name << dendl;
40
41 std::unique_lock wlocker{m_cache_map_lock};
42
43 // cache hit when promoting
44 if (m_cache_map.find(file_name) != m_cache_map.end()) {
45 ldout(cct, 20) << "object is under promoting: " << file_name << dendl;
46 return OBJ_CACHE_SKIP;
47 }
48
49 if ((m_cache_size < m_max_cache_size) &&
50 (inflight_ops < m_max_inflight_ops)) {
51 Entry* entry = new Entry();
52 ceph_assert(entry != nullptr);
53 m_cache_map[file_name] = entry;
54 wlocker.unlock();
55 update_status(file_name, OBJ_CACHE_SKIP);
56 return OBJ_CACHE_NONE; // start promotion request
57 }
58
59 // if there's no free entry, return skip to read from rados
60 return OBJ_CACHE_SKIP;
61 }
62
63 cache_status_t SimplePolicy::lookup_object(std::string file_name) {
64 ldout(cct, 20) << "lookup: " << file_name << dendl;
65
66 std::shared_lock rlocker{m_cache_map_lock};
67
68 auto entry_it = m_cache_map.find(file_name);
69 // simply promote on first lookup
70 if (entry_it == m_cache_map.end()) {
71 rlocker.unlock();
72 return alloc_entry(file_name);
73 }
74
75 Entry* entry = entry_it->second;
76
77 if (entry->status == OBJ_CACHE_PROMOTED) {
78 // bump pos in lru on hit
79 m_promoted_lru.lru_touch(entry);
80 }
81
82 return entry->status;
83 }
84
85 void SimplePolicy::update_status(std::string file_name,
86 cache_status_t new_status, uint64_t size) {
87 ldout(cct, 20) << "update status for: " << file_name
88 << " new status = " << new_status << dendl;
89
90 std::unique_lock locker{m_cache_map_lock};
91
92 auto entry_it = m_cache_map.find(file_name);
93 if (entry_it == m_cache_map.end()) {
94 return;
95 }
96
97 ceph_assert(entry_it != m_cache_map.end());
98 Entry* entry = entry_it->second;
99
100 // to promote
101 if (entry->status == OBJ_CACHE_NONE && new_status== OBJ_CACHE_SKIP) {
102 entry->status = new_status;
103 entry->file_name = file_name;
104 inflight_ops++;
105 return;
106 }
107
108 // promoting done
109 if (entry->status == OBJ_CACHE_SKIP && new_status== OBJ_CACHE_PROMOTED) {
110 m_promoted_lru.lru_insert_top(entry);
111 entry->status = new_status;
112 entry->size = size;
113 m_cache_size += entry->size;
114 inflight_ops--;
115 return;
116 }
117
118 // promoting failed
119 if (entry->status == OBJ_CACHE_SKIP && new_status== OBJ_CACHE_NONE) {
120 // mark this entry as free
121 entry->file_name = "";
122 entry->status = new_status;
123
124 m_cache_map.erase(entry_it);
125 inflight_ops--;
126 delete entry;
127 return;
128 }
129
130 // to evict
131 if (entry->status == OBJ_CACHE_PROMOTED && new_status== OBJ_CACHE_NONE) {
132 // mark this entry as free
(1) Event parameter_hidden: |
declaration hides parameter "size" (declared at line 86) |
(2) Event caretline: |
^ |
133 uint64_t size = entry->size;
134 entry->file_name = "";
135 entry->size = 0;
136 entry->status = new_status;
137
138 m_promoted_lru.lru_remove(entry);
139 m_cache_map.erase(entry_it);
140 m_cache_size -= size;
141 delete entry;
142 return;
143 }
144 }
145
146 int SimplePolicy::evict_entry(std::string file_name) {
147 ldout(cct, 20) << "to evict: " << file_name << dendl;
148
149 update_status(file_name, OBJ_CACHE_NONE);
150
151 return 0;
152 }
153
154 cache_status_t SimplePolicy::get_status(std::string file_name) {
155 ldout(cct, 20) << file_name << dendl;
156
157 std::shared_lock locker{m_cache_map_lock};
158 auto entry_it = m_cache_map.find(file_name);
159 if (entry_it == m_cache_map.end()) {
160 return OBJ_CACHE_NONE;
161 }
162
163 return entry_it->second->status;
164 }
165
166 void SimplePolicy::get_evict_list(std::list<std::string>* obj_list) {
167 ldout(cct, 20) << dendl;
168
169 std::unique_lock locker{m_cache_map_lock};
170 // check free ratio, pop entries from LRU
171 if ((double)m_cache_size / m_max_cache_size > (1 - m_watermark)) {
172 // TODO(dehao): make this configurable
173 int evict_num = m_cache_map.size() * 0.1;
174 for (int i = 0; i < evict_num; i++) {
175 Entry* entry = reinterpret_cast<Entry*>(m_promoted_lru.lru_expire());
176 if (entry == nullptr) {
177 continue;
178 }
179 std::string file_name = entry->file_name;
180 obj_list->push_back(file_name);
181 }
182 }
183 }
184
185 // for unit test
186 uint64_t SimplePolicy::get_free_size() {
187 return m_max_cache_size - m_cache_size;
188 }
189
190 uint64_t SimplePolicy::get_promoting_entry_num() {
191 uint64_t index = 0;
192 std::shared_lock rlocker{m_cache_map_lock};
193 for (auto it : m_cache_map) {
194 if (it.second->status == OBJ_CACHE_SKIP) {
195 index++;
196 }
197 }
198 return index;
199 }
200
201 uint64_t SimplePolicy::get_promoted_entry_num() {
202 return m_promoted_lru.lru_get_size();
203 }
204
205 std::string SimplePolicy::get_evict_entry() {
206 Entry* entry = reinterpret_cast<Entry*>(m_promoted_lru.lru_get_next_expire());
207 if (entry == nullptr) {
208 return "";
209 }
210 return entry->file_name;
211 }
212
213 } // namespace immutable_obj_cache
214 } // namespace ceph
215