1 /*!
2 * \file trc_mem_acc_cache.cpp
3 * \brief OpenCSD : Memory accessor cache.
4 *
5 * \copyright Copyright (c) 2018, ARM Limited. All Rights Reserved.
6 */
7
8 /*
9 * Redistribution and use in source and binary forms, with or without modification,
10 * are permitted provided that the following conditions are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the copyright holder nor the names of its contributors
20 * may be used to endorse or promote products derived from this software without
21 * specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 #include <cstring>
36 #include <sstream>
37 #include <iomanip>
38 #include "mem_acc/trc_mem_acc_cache.h"
39 #include "mem_acc/trc_mem_acc_base.h"
40 #include "interfaces/trc_error_log_i.h"
41 #include "common/ocsd_error.h"
42
43 #ifdef LOG_CACHE_STATS
44 #define INC_HITS_RL(idx) m_hits++; m_hit_rl[m_mru_idx]++;
45 #define INC_MISS() m_misses++;
46 #define INC_PAGES() m_pages++;
47 #define SET_MAX_RL(idx) \
48 { \
49 if (m_hit_rl_max[idx] < m_hit_rl[idx]) \
50 m_hit_rl_max[idx] = m_hit_rl[idx]; \
51 m_hit_rl[idx] = 0; \
52 }
53 #define INC_RL(idx) m_hit_rl[m_mru_idx]++;
54 #else
55 #define INC_HITS_RL(idx)
56 #define INC_MISS()
57 #define INC_PAGES()
58 #define SET_MAX_RL(idx)
59 #define INC_RL(idx)
60 #endif
61
62 // uncomment to log cache ops
63 // #define LOG_CACHE_OPS
64 // #define LOG_CACHE_CREATION
65
createCaches()66 ocsd_err_t TrcMemAccCache::createCaches()
67 {
68 if (m_mru)
69 destroyCaches();
70 m_mru = (cache_block_t*) new (std::nothrow) cache_block_t[m_mru_num_pages];
71 if (!m_mru)
72 return OCSD_ERR_MEM;
73 for (int i = 0; i < m_mru_num_pages; i++) {
74 m_mru[i].data = new (std::nothrow) uint8_t[m_mru_page_size];
75 if (!m_mru[i].data)
76 return OCSD_ERR_MEM;
77 clearPage(&m_mru[i]);
78 }
79 #ifdef LOG_CACHE_STATS
80 m_hit_rl = (uint32_t *) new (std::nothrow) uint32_t[m_mru_num_pages];
81 m_hit_rl_max = (uint32_t*) new (std::nothrow) uint32_t[m_mru_num_pages];
82 if (!m_hit_rl || !m_hit_rl_max)
83 return OCSD_ERR_MEM;
84 for (int j = 0; j < m_mru_num_pages; j++) {
85 m_hit_rl[j] = 0;
86 m_hit_rl_max[j] = 0;
87 }
88 #endif
89 #ifdef LOG_CACHE_CREATION
90 std::ostringstream oss;
91 oss << "MemAcc Caches: Num Pages=" << m_mru_num_pages << "; Page size=" << m_mru_page_size << ";\n";
92 logMsg(oss.str());
93 #endif
94 return OCSD_OK;
95 }
96
destroyCaches()97 void TrcMemAccCache::destroyCaches()
98 {
99 if (m_mru) {
100 for (int i = 0; i < m_mru_num_pages; i++)
101 delete[] m_mru[i].data;
102 delete[] m_mru;
103 m_mru = 0;
104 }
105 #ifdef LOG_CACHE_STATS
106 if (m_hit_rl)
107 delete[] m_hit_rl;
108 if (m_hit_rl_max)
109 delete[] m_hit_rl_max;
110 m_hit_rl = 0;
111 m_hit_rl_max = 0;
112 #endif
113
114 }
115
getenvMemaccCacheSizes(bool & enable,int & page_size,int & num_pages)116 void TrcMemAccCache::getenvMemaccCacheSizes(bool& enable, int& page_size, int& num_pages)
117 {
118 char* env_var;
119 long env_val;
120
121 /* set defaults */
122 enable = true;
123 page_size = MEM_ACC_CACHE_DEFAULT_PAGE_SIZE;
124 num_pages = MEM_ACC_CACHE_DEFAULT_MRU_SIZE;
125
126 /* check environment for adjustments */
127
128 /* override the default on switch? if so no need to look further */
129 if ((env_var = getenv(OCSD_ENV_MEMACC_CACHE_OFF)) != NULL)
130 {
131 enable = false;
132 return;
133 }
134
135 /* check for tweak in page size */
136 if ((env_var = getenv(OCSD_ENV_MEMACC_CACHE_PG_SIZE)) != NULL)
137 {
138 env_val = strtol(env_var, NULL, 0);
139 /*
140 * if no valid conversion then env_val = 0,
141 * otherwise set val and allow TrcMemAccCache::setCacheSizes
142 * fn to ensure the value in bounds
143 */
144 if (env_val > 0)
145 page_size = (int)env_val;
146 }
147
148 /* check for tweak in number of pages */
149 if ((env_var = getenv(OCSD_ENV_MEMACC_CACHE_PG_NUM)) != NULL)
150 {
151 env_val = strtol(env_var, NULL, 0);
152 /*
153 * if no valid conversion then env_val = 0,
154 * otherwise set val and allow TrcMemAccCache::setCacheSizes
155 * fn to ensure the value in bounds
156 */
157 if (env_val > 0)
158 num_pages = (int)env_val;
159 }
160
161 }
162
enableCaching(bool bEnable)163 ocsd_err_t TrcMemAccCache::enableCaching(bool bEnable)
164 {
165 ocsd_err_t err = OCSD_OK;
166
167 if (bEnable)
168 {
169 // don't create caches if they are done already.
170 if (!m_mru)
171 err = createCaches();
172 }
173 else
174 destroyCaches();
175 m_bCacheEnabled = bEnable;
176
177 #ifdef LOG_CACHE_CREATION
178 std::ostringstream oss;
179 oss << "MemAcc Caches: " << (bEnable ? "Enabled" : "Disabled") << ";\n";
180 logMsg(oss.str());
181 #endif
182
183 return err;
184 }
185
setCacheSizes(const uint16_t page_size,const int nr_pages,const bool err_on_limit)186 ocsd_err_t TrcMemAccCache::setCacheSizes(const uint16_t page_size, const int nr_pages, const bool err_on_limit /*= false*/)
187 {
188 // do't re-create what we already have.
189 if (m_mru &&
190 (m_mru_num_pages == nr_pages) &&
191 (m_mru_page_size == page_size))
192 return OCSD_OK;
193
194 /* remove any caches with the existing sizes */
195 destroyCaches();
196
197 /* set page size within Max/Min range */
198 if (page_size > MEM_ACC_CACHE_PAGE_SIZE_MAX)
199 {
200 if (err_on_limit)
201 {
202 logMsg("MemAcc Caching: page size too large", OCSD_ERR_INVALID_PARAM_VAL);
203 return OCSD_ERR_INVALID_PARAM_VAL;
204 }
205 m_mru_page_size = MEM_ACC_CACHE_PAGE_SIZE_MAX;
206 }
207 else if (page_size < MEM_ACC_CACHE_PAGE_SIZE_MIN)
208 {
209 if (err_on_limit)
210 {
211 logMsg("MemAcc Caching: page size too small", OCSD_ERR_INVALID_PARAM_VAL);
212 return OCSD_ERR_INVALID_PARAM_VAL;
213 }
214 m_mru_page_size = MEM_ACC_CACHE_PAGE_SIZE_MIN;
215 }
216 else
217 m_mru_page_size = page_size;
218
219 /* set num pages within max/min range */
220 if (nr_pages > MEM_ACC_CACHE_MRU_SIZE_MAX)
221 {
222 if (err_on_limit)
223 {
224 logMsg("MemAcc Caching: number of pages too large", OCSD_ERR_INVALID_PARAM_VAL);
225 return OCSD_ERR_INVALID_PARAM_VAL;
226 }
227 m_mru_num_pages = MEM_ACC_CACHE_MRU_SIZE_MAX;
228 }
229 else if (nr_pages < MEM_ACC_CACHE_MRU_SIZE_MIN)
230 {
231 if (err_on_limit)
232 {
233 logMsg("MemAcc Caching: number of pages too small", OCSD_ERR_INVALID_PARAM_VAL);
234 return OCSD_ERR_INVALID_PARAM_VAL;
235 }
236 m_mru_num_pages = MEM_ACC_CACHE_MRU_SIZE_MIN;
237 }
238 else
239 m_mru_num_pages = nr_pages;
240
241 /* re-create with new sizes */
242 return createCaches();
243 }
244
245 /* return index of unused page, or oldest used page by sequence number */
findNewPage()246 int TrcMemAccCache::findNewPage()
247 {
248 uint32_t oldest_seq;
249 int current_idx, oldest_idx;
250 #ifdef LOG_CACHE_OPS
251 std::ostringstream oss;
252 #endif
253
254 /* set up search indexes and check the current search index has not wrapped. */
255 current_idx = m_mru_idx + 1;
256 oldest_idx = m_mru_idx;
257 oldest_seq = 0;
258 if (current_idx >= m_mru_num_pages)
259 current_idx = 0;
260
261 /* search forwards from m_mru_idx + 1 until we wrap and hit the index again */
262 while (current_idx != m_mru_idx) {
263 if (m_mru[current_idx].use_sequence == 0) {
264 #ifdef LOG_CACHE_OPS
265 oss << "TrcMemAccCache:: ALI-allocate clean page: [page: " << std::dec << current_idx << "]\n";
266 logMsg(oss.str());
267 #endif
268 return current_idx;
269 }
270
271 // if we find a page with a lower use sequence, that is older.
272 if ((oldest_seq == 0) || (oldest_seq > m_mru[current_idx].use_sequence)) {
273 oldest_seq = m_mru[current_idx].use_sequence;
274 oldest_idx = current_idx;
275 }
276
277 current_idx++;
278
279 // wrap around?
280 if (current_idx >= m_mru_num_pages)
281 current_idx = 0;
282 }
283 #ifdef LOG_CACHE_OPS
284 oss << "TrcMemAccCache:: ALI-evict and allocate old page: [page: " << std::dec << oldest_idx << "]\n";
285 logMsg(oss.str());
286 #endif
287 return oldest_idx;
288 }
289
incSequence()290 void TrcMemAccCache::incSequence()
291 {
292 m_mru[m_mru_idx].use_sequence = m_mru_sequence++;
293 if (m_mru_sequence == 0) {
294 // wrapped which throws out the oldest algorithm - oldest will now appear newer - so not evicted.
295 // arbitrarily re-sequence all in use..
296 m_mru_sequence = 1;
297 for (int i = 0; i < m_mru_num_pages; i++)
298 if (m_mru[i].use_sequence != 0)
299 m_mru[i].use_sequence = m_mru_sequence++;
300
301 // ensure newest still newest...
302 m_mru[m_mru_idx].use_sequence = m_mru_sequence++;
303 }
304 }
305
readBytesFromCache(TrcMemAccessorBase * p_accessor,const ocsd_vaddr_t address,const ocsd_mem_space_acc_t mem_space,const uint8_t trcID,uint32_t * numBytes,uint8_t * byteBuffer)306 ocsd_err_t TrcMemAccCache::readBytesFromCache(TrcMemAccessorBase *p_accessor, const ocsd_vaddr_t address, const ocsd_mem_space_acc_t mem_space, const uint8_t trcID, uint32_t *numBytes, uint8_t *byteBuffer)
307 {
308 uint32_t bytesRead = 0, reqBytes = *numBytes;
309 ocsd_err_t err = OCSD_OK;
310
311
312 #ifdef LOG_CACHE_OPS
313 std::ostringstream oss;
314 std::string memSpaceStr;
315 #endif
316
317 if (m_bCacheEnabled)
318 {
319 if (blockInCache(address, reqBytes, trcID))
320 {
321 bytesRead = reqBytes;
322 memcpy(byteBuffer, &m_mru[m_mru_idx].data[address - m_mru[m_mru_idx].st_addr], reqBytes);
323 incSequence();
324 #ifdef LOG_CACHE_OPS
325 oss << "TrcMemAccCache:: hit {page: " << std::dec << m_mru_idx << "; seq: " << m_mru[m_mru_idx].use_sequence << " CSID: " << std::hex << (int)m_mru[m_mru_idx].trcID;
326 oss << "} [addr:0x" << std::hex << address << ", bytes: " << std::dec << reqBytes << "]\n";
327 logMsg(oss.str());
328 #endif
329 INC_HITS_RL(m_mru_idx);
330 }
331 else
332 {
333 #ifdef LOG_CACHE_OPS
334 oss << "TrcMemAccCache:: miss [addr:0x" << std::hex << address << ", bytes: " << std::dec << reqBytes << "]\n";
335 logMsg(oss.str());
336 #endif
337 /* need a new cache page - check the underlying accessor for the data */
338 m_mru_idx = findNewPage();
339 m_mru[m_mru_idx].valid_len = p_accessor->readBytes(address, mem_space, trcID, m_mru_page_size, &m_mru[m_mru_idx].data[0]);
340
341 /* check return length valid - v bad if return length more than request */
342 if (m_mru[m_mru_idx].valid_len > m_mru_page_size)
343 {
344 m_mru[m_mru_idx].valid_len = 0; // set to nothing returned.
345 err = OCSD_ERR_MEM_ACC_BAD_LEN;
346 }
347
348 if (m_mru[m_mru_idx].valid_len > 0)
349 {
350 // got some data - so save the details
351 m_mru[m_mru_idx].st_addr = address;
352 m_mru[m_mru_idx].trcID = trcID;
353 incSequence();
354
355 // log the run length hit counts
356 SET_MAX_RL(m_mru_idx);
357
358 #ifdef LOG_CACHE_OPS
359 TrcMemAccessorBase::getMemAccSpaceString(memSpaceStr, mem_space);
360 oss.str("");
361 oss << "TrcMemAccCache:: ALI-load {page: " << std::dec << m_mru_idx << "; seq: " << m_mru[m_mru_idx].use_sequence << " CSID: " << std::hex << (int)m_mru[m_mru_idx].trcID;
362 oss << "} [mem space: " << memSpaceStr << ", addr:0x" << std::hex << address << ", bytes: " << std::dec << m_mru[m_mru_idx].valid_len << "]\n";
363 logMsg(oss.str());
364 #endif
365 INC_PAGES();
366
367 if (blockInPage(address, reqBytes, trcID)) /* check we got the data we needed */
368 {
369 bytesRead = reqBytes;
370 memcpy(byteBuffer, &m_mru[m_mru_idx].data[address - m_mru[m_mru_idx].st_addr], reqBytes);
371 INC_RL(m_mru_idx);
372 }
373 else
374 {
375 #ifdef LOG_CACHE_OPS
376 oss.str("");
377 oss << "TrcMemAccCache:: miss-after-load {page: " << std::dec << m_mru_idx << " } [addr:0x" << std::hex << address << ", bytes: " << std::dec << m_mru[m_mru_idx].valid_len << "]\n";
378 logMsg(oss.str());
379 #endif
380 INC_MISS();
381 }
382 }
383 }
384 }
385 *numBytes = bytesRead;
386 return err;
387 }
388
invalidateAll()389 void TrcMemAccCache::invalidateAll()
390 {
391 #ifdef LOG_CACHE_OPS
392 std::ostringstream oss;
393 oss << "TrcMemAccCache:: ALI-invalidate All\n";
394 logMsg(oss.str());
395 #endif
396
397 for (int i = 0; i < m_mru_num_pages; i++)
398 clearPage(&m_mru[i]);
399 m_mru_idx = 0;
400 }
401
invalidateByTraceID(int8_t trcID)402 void TrcMemAccCache::invalidateByTraceID(int8_t trcID)
403 {
404 #ifdef LOG_CACHE_OPS
405 std::ostringstream oss;
406 oss << "TrcMemAccCache:: ALI-invalidate by ID request {CSID: " << std::hex << (int)trcID << "}\n";
407 logMsg(oss.str());
408 #endif
409
410 for (int i = 0; i < m_mru_num_pages; i++)
411 {
412 if (m_mru[i].trcID == trcID)
413 {
414 #ifdef LOG_CACHE_OPS
415 oss.str("");
416 oss << "TrcMemAccCache:: ALI-invalidate page {page: " << std::dec << i << "; seq: " << m_mru[i].use_sequence << " CSID: " << std::hex << (int)m_mru[i].trcID;
417 oss << "} [addr:0x" << std::hex << m_mru[i].st_addr << ", bytes: " << std::dec << m_mru[i].valid_len << "]\n";
418 logMsg(oss.str());
419 #endif
420 clearPage(&m_mru[i]);
421 }
422 }
423 }
424
logMsg(const std::string & szMsg,ocsd_err_t err)425 void TrcMemAccCache::logMsg(const std::string &szMsg, ocsd_err_t err /*= OCSD_OK */ )
426 {
427 if (m_err_log)
428 {
429 if (err == OCSD_OK)
430 m_err_log->LogMessage(ITraceErrorLog::HANDLE_GEN_INFO, OCSD_ERR_SEV_INFO, szMsg);
431 else
432 {
433 ocsdError ocsd_err( OCSD_ERR_SEV_ERROR, err, szMsg);
434 m_err_log->LogError(ITraceErrorLog::HANDLE_GEN_INFO, &ocsd_err);
435 }
436 }
437 }
438
setErrorLog(ITraceErrorLog * log)439 void TrcMemAccCache::setErrorLog(ITraceErrorLog *log)
440 {
441 m_err_log = log;
442 }
443
logAndClearCounts()444 void TrcMemAccCache::logAndClearCounts()
445 {
446 #ifdef LOG_CACHE_STATS
447 std::ostringstream oss;
448
449 oss << "TrcMemAccCache:: cache performance: Page Size: 0x" << std::hex << m_mru_page_size << "; Number of Pages: " << std::dec << m_mru_num_pages << "\n";
450 oss << "Cache hits(" << std::dec << m_hits << "), misses(" << m_misses << "), new pages(" << m_pages << ")\n";
451 logMsg(oss.str());
452 for (int i = 0; i < m_mru_num_pages; i++)
453 {
454 if (m_hit_rl_max[i] < m_hit_rl[i])
455 m_hit_rl_max[i] = m_hit_rl[i];
456 oss.str("");
457 oss << "Run length max page " << std::dec << i << ": " << m_hit_rl_max[i] << "\n";
458 logMsg(oss.str());
459 }
460 m_hits = m_misses = m_pages = 0;
461 #endif
462 }
463
464 /* End of File trc_mem_acc_cache.cpp */
465