1*74fe6c29SRuslan Bukin /*
2*74fe6c29SRuslan Bukin * Copyright (c) 2016-2018, Intel Corporation
3*74fe6c29SRuslan Bukin *
4*74fe6c29SRuslan Bukin * Redistribution and use in source and binary forms, with or without
5*74fe6c29SRuslan Bukin * modification, are permitted provided that the following conditions are met:
6*74fe6c29SRuslan Bukin *
7*74fe6c29SRuslan Bukin * * Redistributions of source code must retain the above copyright notice,
8*74fe6c29SRuslan Bukin * this list of conditions and the following disclaimer.
9*74fe6c29SRuslan Bukin * * Redistributions in binary form must reproduce the above copyright notice,
10*74fe6c29SRuslan Bukin * this list of conditions and the following disclaimer in the documentation
11*74fe6c29SRuslan Bukin * and/or other materials provided with the distribution.
12*74fe6c29SRuslan Bukin * * Neither the name of Intel Corporation nor the names of its contributors
13*74fe6c29SRuslan Bukin * may be used to endorse or promote products derived from this software
14*74fe6c29SRuslan Bukin * without specific prior written permission.
15*74fe6c29SRuslan Bukin *
16*74fe6c29SRuslan Bukin * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17*74fe6c29SRuslan Bukin * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*74fe6c29SRuslan Bukin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*74fe6c29SRuslan Bukin * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20*74fe6c29SRuslan Bukin * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21*74fe6c29SRuslan Bukin * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22*74fe6c29SRuslan Bukin * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23*74fe6c29SRuslan Bukin * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24*74fe6c29SRuslan Bukin * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25*74fe6c29SRuslan Bukin * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26*74fe6c29SRuslan Bukin * POSSIBILITY OF SUCH DAMAGE.
27*74fe6c29SRuslan Bukin */
28*74fe6c29SRuslan Bukin
29*74fe6c29SRuslan Bukin #include "pt_image_section_cache.h"
30*74fe6c29SRuslan Bukin #include "pt_section.h"
31*74fe6c29SRuslan Bukin
32*74fe6c29SRuslan Bukin #include "intel-pt.h"
33*74fe6c29SRuslan Bukin
34*74fe6c29SRuslan Bukin #include <stdlib.h>
35*74fe6c29SRuslan Bukin
36*74fe6c29SRuslan Bukin
dupstr(const char * str)37*74fe6c29SRuslan Bukin static char *dupstr(const char *str)
38*74fe6c29SRuslan Bukin {
39*74fe6c29SRuslan Bukin char *dup;
40*74fe6c29SRuslan Bukin size_t len;
41*74fe6c29SRuslan Bukin
42*74fe6c29SRuslan Bukin if (!str)
43*74fe6c29SRuslan Bukin return NULL;
44*74fe6c29SRuslan Bukin
45*74fe6c29SRuslan Bukin len = strlen(str);
46*74fe6c29SRuslan Bukin dup = malloc(len + 1);
47*74fe6c29SRuslan Bukin if (!dup)
48*74fe6c29SRuslan Bukin return NULL;
49*74fe6c29SRuslan Bukin
50*74fe6c29SRuslan Bukin return strcpy(dup, str);
51*74fe6c29SRuslan Bukin }
52*74fe6c29SRuslan Bukin
pt_iscache_init(struct pt_image_section_cache * iscache,const char * name)53*74fe6c29SRuslan Bukin int pt_iscache_init(struct pt_image_section_cache *iscache, const char *name)
54*74fe6c29SRuslan Bukin {
55*74fe6c29SRuslan Bukin if (!iscache)
56*74fe6c29SRuslan Bukin return -pte_internal;
57*74fe6c29SRuslan Bukin
58*74fe6c29SRuslan Bukin memset(iscache, 0, sizeof(*iscache));
59*74fe6c29SRuslan Bukin iscache->limit = UINT64_MAX;
60*74fe6c29SRuslan Bukin if (name) {
61*74fe6c29SRuslan Bukin iscache->name = dupstr(name);
62*74fe6c29SRuslan Bukin if (!iscache->name)
63*74fe6c29SRuslan Bukin return -pte_nomem;
64*74fe6c29SRuslan Bukin }
65*74fe6c29SRuslan Bukin
66*74fe6c29SRuslan Bukin #if defined(FEATURE_THREADS)
67*74fe6c29SRuslan Bukin {
68*74fe6c29SRuslan Bukin int errcode;
69*74fe6c29SRuslan Bukin
70*74fe6c29SRuslan Bukin errcode = mtx_init(&iscache->lock, mtx_plain);
71*74fe6c29SRuslan Bukin if (errcode != thrd_success)
72*74fe6c29SRuslan Bukin return -pte_bad_lock;
73*74fe6c29SRuslan Bukin }
74*74fe6c29SRuslan Bukin #endif /* defined(FEATURE_THREADS) */
75*74fe6c29SRuslan Bukin
76*74fe6c29SRuslan Bukin return 0;
77*74fe6c29SRuslan Bukin }
78*74fe6c29SRuslan Bukin
pt_iscache_fini(struct pt_image_section_cache * iscache)79*74fe6c29SRuslan Bukin void pt_iscache_fini(struct pt_image_section_cache *iscache)
80*74fe6c29SRuslan Bukin {
81*74fe6c29SRuslan Bukin if (!iscache)
82*74fe6c29SRuslan Bukin return;
83*74fe6c29SRuslan Bukin
84*74fe6c29SRuslan Bukin (void) pt_iscache_clear(iscache);
85*74fe6c29SRuslan Bukin free(iscache->name);
86*74fe6c29SRuslan Bukin
87*74fe6c29SRuslan Bukin #if defined(FEATURE_THREADS)
88*74fe6c29SRuslan Bukin
89*74fe6c29SRuslan Bukin mtx_destroy(&iscache->lock);
90*74fe6c29SRuslan Bukin
91*74fe6c29SRuslan Bukin #endif /* defined(FEATURE_THREADS) */
92*74fe6c29SRuslan Bukin }
93*74fe6c29SRuslan Bukin
pt_iscache_lock(struct pt_image_section_cache * iscache)94*74fe6c29SRuslan Bukin static inline int pt_iscache_lock(struct pt_image_section_cache *iscache)
95*74fe6c29SRuslan Bukin {
96*74fe6c29SRuslan Bukin if (!iscache)
97*74fe6c29SRuslan Bukin return -pte_internal;
98*74fe6c29SRuslan Bukin
99*74fe6c29SRuslan Bukin #if defined(FEATURE_THREADS)
100*74fe6c29SRuslan Bukin {
101*74fe6c29SRuslan Bukin int errcode;
102*74fe6c29SRuslan Bukin
103*74fe6c29SRuslan Bukin errcode = mtx_lock(&iscache->lock);
104*74fe6c29SRuslan Bukin if (errcode != thrd_success)
105*74fe6c29SRuslan Bukin return -pte_bad_lock;
106*74fe6c29SRuslan Bukin }
107*74fe6c29SRuslan Bukin #endif /* defined(FEATURE_THREADS) */
108*74fe6c29SRuslan Bukin
109*74fe6c29SRuslan Bukin return 0;
110*74fe6c29SRuslan Bukin }
111*74fe6c29SRuslan Bukin
pt_iscache_unlock(struct pt_image_section_cache * iscache)112*74fe6c29SRuslan Bukin static inline int pt_iscache_unlock(struct pt_image_section_cache *iscache)
113*74fe6c29SRuslan Bukin {
114*74fe6c29SRuslan Bukin if (!iscache)
115*74fe6c29SRuslan Bukin return -pte_internal;
116*74fe6c29SRuslan Bukin
117*74fe6c29SRuslan Bukin #if defined(FEATURE_THREADS)
118*74fe6c29SRuslan Bukin {
119*74fe6c29SRuslan Bukin int errcode;
120*74fe6c29SRuslan Bukin
121*74fe6c29SRuslan Bukin errcode = mtx_unlock(&iscache->lock);
122*74fe6c29SRuslan Bukin if (errcode != thrd_success)
123*74fe6c29SRuslan Bukin return -pte_bad_lock;
124*74fe6c29SRuslan Bukin }
125*74fe6c29SRuslan Bukin #endif /* defined(FEATURE_THREADS) */
126*74fe6c29SRuslan Bukin
127*74fe6c29SRuslan Bukin return 0;
128*74fe6c29SRuslan Bukin }
129*74fe6c29SRuslan Bukin
isid_from_index(uint16_t index)130*74fe6c29SRuslan Bukin static inline int isid_from_index(uint16_t index)
131*74fe6c29SRuslan Bukin {
132*74fe6c29SRuslan Bukin return index + 1;
133*74fe6c29SRuslan Bukin }
134*74fe6c29SRuslan Bukin
pt_iscache_expand(struct pt_image_section_cache * iscache)135*74fe6c29SRuslan Bukin static int pt_iscache_expand(struct pt_image_section_cache *iscache)
136*74fe6c29SRuslan Bukin {
137*74fe6c29SRuslan Bukin struct pt_iscache_entry *entries;
138*74fe6c29SRuslan Bukin uint16_t capacity, target;
139*74fe6c29SRuslan Bukin
140*74fe6c29SRuslan Bukin if (!iscache)
141*74fe6c29SRuslan Bukin return -pte_internal;
142*74fe6c29SRuslan Bukin
143*74fe6c29SRuslan Bukin capacity = iscache->capacity;
144*74fe6c29SRuslan Bukin target = capacity + 8;
145*74fe6c29SRuslan Bukin
146*74fe6c29SRuslan Bukin /* Check for overflows. */
147*74fe6c29SRuslan Bukin if (target < capacity)
148*74fe6c29SRuslan Bukin return -pte_nomem;
149*74fe6c29SRuslan Bukin
150*74fe6c29SRuslan Bukin entries = realloc(iscache->entries, target * sizeof(*entries));
151*74fe6c29SRuslan Bukin if (!entries)
152*74fe6c29SRuslan Bukin return -pte_nomem;
153*74fe6c29SRuslan Bukin
154*74fe6c29SRuslan Bukin iscache->capacity = target;
155*74fe6c29SRuslan Bukin iscache->entries = entries;
156*74fe6c29SRuslan Bukin return 0;
157*74fe6c29SRuslan Bukin }
158*74fe6c29SRuslan Bukin
pt_iscache_find_locked(struct pt_image_section_cache * iscache,const char * filename,uint64_t offset,uint64_t size,uint64_t laddr)159*74fe6c29SRuslan Bukin static int pt_iscache_find_locked(struct pt_image_section_cache *iscache,
160*74fe6c29SRuslan Bukin const char *filename, uint64_t offset,
161*74fe6c29SRuslan Bukin uint64_t size, uint64_t laddr)
162*74fe6c29SRuslan Bukin {
163*74fe6c29SRuslan Bukin uint16_t idx, end;
164*74fe6c29SRuslan Bukin
165*74fe6c29SRuslan Bukin if (!iscache || !filename)
166*74fe6c29SRuslan Bukin return -pte_internal;
167*74fe6c29SRuslan Bukin
168*74fe6c29SRuslan Bukin end = iscache->size;
169*74fe6c29SRuslan Bukin for (idx = 0; idx < end; ++idx) {
170*74fe6c29SRuslan Bukin const struct pt_iscache_entry *entry;
171*74fe6c29SRuslan Bukin const struct pt_section *section;
172*74fe6c29SRuslan Bukin const char *sec_filename;
173*74fe6c29SRuslan Bukin uint64_t sec_offset, sec_size;
174*74fe6c29SRuslan Bukin
175*74fe6c29SRuslan Bukin entry = &iscache->entries[idx];
176*74fe6c29SRuslan Bukin
177*74fe6c29SRuslan Bukin /* We do not zero-initialize the array - a NULL check is
178*74fe6c29SRuslan Bukin * pointless.
179*74fe6c29SRuslan Bukin */
180*74fe6c29SRuslan Bukin section = entry->section;
181*74fe6c29SRuslan Bukin sec_filename = pt_section_filename(section);
182*74fe6c29SRuslan Bukin sec_offset = pt_section_offset(section);
183*74fe6c29SRuslan Bukin sec_size = pt_section_size(section);
184*74fe6c29SRuslan Bukin
185*74fe6c29SRuslan Bukin if (entry->laddr != laddr)
186*74fe6c29SRuslan Bukin continue;
187*74fe6c29SRuslan Bukin
188*74fe6c29SRuslan Bukin if (sec_offset != offset)
189*74fe6c29SRuslan Bukin continue;
190*74fe6c29SRuslan Bukin
191*74fe6c29SRuslan Bukin if (sec_size != size)
192*74fe6c29SRuslan Bukin continue;
193*74fe6c29SRuslan Bukin
194*74fe6c29SRuslan Bukin /* We should not have a section without a filename. */
195*74fe6c29SRuslan Bukin if (!sec_filename)
196*74fe6c29SRuslan Bukin return -pte_internal;
197*74fe6c29SRuslan Bukin
198*74fe6c29SRuslan Bukin if (strcmp(sec_filename, filename) != 0)
199*74fe6c29SRuslan Bukin continue;
200*74fe6c29SRuslan Bukin
201*74fe6c29SRuslan Bukin return isid_from_index(idx);
202*74fe6c29SRuslan Bukin }
203*74fe6c29SRuslan Bukin
204*74fe6c29SRuslan Bukin return 0;
205*74fe6c29SRuslan Bukin }
206*74fe6c29SRuslan Bukin
pt_iscache_lru_free(struct pt_iscache_lru_entry * lru)207*74fe6c29SRuslan Bukin static int pt_iscache_lru_free(struct pt_iscache_lru_entry *lru)
208*74fe6c29SRuslan Bukin {
209*74fe6c29SRuslan Bukin while (lru) {
210*74fe6c29SRuslan Bukin struct pt_iscache_lru_entry *trash;
211*74fe6c29SRuslan Bukin int errcode;
212*74fe6c29SRuslan Bukin
213*74fe6c29SRuslan Bukin trash = lru;
214*74fe6c29SRuslan Bukin lru = lru->next;
215*74fe6c29SRuslan Bukin
216*74fe6c29SRuslan Bukin errcode = pt_section_unmap(trash->section);
217*74fe6c29SRuslan Bukin if (errcode < 0)
218*74fe6c29SRuslan Bukin return errcode;
219*74fe6c29SRuslan Bukin
220*74fe6c29SRuslan Bukin free(trash);
221*74fe6c29SRuslan Bukin }
222*74fe6c29SRuslan Bukin
223*74fe6c29SRuslan Bukin return 0;
224*74fe6c29SRuslan Bukin }
225*74fe6c29SRuslan Bukin
pt_iscache_lru_prune(struct pt_image_section_cache * iscache,struct pt_iscache_lru_entry ** tail)226*74fe6c29SRuslan Bukin static int pt_iscache_lru_prune(struct pt_image_section_cache *iscache,
227*74fe6c29SRuslan Bukin struct pt_iscache_lru_entry **tail)
228*74fe6c29SRuslan Bukin {
229*74fe6c29SRuslan Bukin struct pt_iscache_lru_entry *lru, **pnext;
230*74fe6c29SRuslan Bukin uint64_t limit, used;
231*74fe6c29SRuslan Bukin
232*74fe6c29SRuslan Bukin if (!iscache || !tail)
233*74fe6c29SRuslan Bukin return -pte_internal;
234*74fe6c29SRuslan Bukin
235*74fe6c29SRuslan Bukin limit = iscache->limit;
236*74fe6c29SRuslan Bukin used = 0ull;
237*74fe6c29SRuslan Bukin
238*74fe6c29SRuslan Bukin pnext = &iscache->lru;
239*74fe6c29SRuslan Bukin for (lru = *pnext; lru; pnext = &lru->next, lru = *pnext) {
240*74fe6c29SRuslan Bukin
241*74fe6c29SRuslan Bukin used += lru->size;
242*74fe6c29SRuslan Bukin if (used <= limit)
243*74fe6c29SRuslan Bukin continue;
244*74fe6c29SRuslan Bukin
245*74fe6c29SRuslan Bukin /* The cache got too big; prune it starting from @lru. */
246*74fe6c29SRuslan Bukin iscache->used = used - lru->size;
247*74fe6c29SRuslan Bukin *pnext = NULL;
248*74fe6c29SRuslan Bukin *tail = lru;
249*74fe6c29SRuslan Bukin
250*74fe6c29SRuslan Bukin return 0;
251*74fe6c29SRuslan Bukin }
252*74fe6c29SRuslan Bukin
253*74fe6c29SRuslan Bukin /* We shouldn't prune the cache unnecessarily. */
254*74fe6c29SRuslan Bukin return -pte_internal;
255*74fe6c29SRuslan Bukin }
256*74fe6c29SRuslan Bukin
257*74fe6c29SRuslan Bukin /* Add @section to the front of @iscache->lru.
258*74fe6c29SRuslan Bukin *
259*74fe6c29SRuslan Bukin * Returns a positive integer if we need to prune the cache.
260*74fe6c29SRuslan Bukin * Returns zero if we don't need to prune the cache.
261*74fe6c29SRuslan Bukin * Returns a negative pt_error_code otherwise.
262*74fe6c29SRuslan Bukin */
pt_isache_lru_new(struct pt_image_section_cache * iscache,struct pt_section * section)263*74fe6c29SRuslan Bukin static int pt_isache_lru_new(struct pt_image_section_cache *iscache,
264*74fe6c29SRuslan Bukin struct pt_section *section)
265*74fe6c29SRuslan Bukin {
266*74fe6c29SRuslan Bukin struct pt_iscache_lru_entry *lru;
267*74fe6c29SRuslan Bukin uint64_t memsize, used, total, limit;
268*74fe6c29SRuslan Bukin int errcode;
269*74fe6c29SRuslan Bukin
270*74fe6c29SRuslan Bukin if (!iscache)
271*74fe6c29SRuslan Bukin return -pte_internal;
272*74fe6c29SRuslan Bukin
273*74fe6c29SRuslan Bukin errcode = pt_section_memsize(section, &memsize);
274*74fe6c29SRuslan Bukin if (errcode < 0)
275*74fe6c29SRuslan Bukin return errcode;
276*74fe6c29SRuslan Bukin
277*74fe6c29SRuslan Bukin /* Don't try to add the section if it is too big. We'd prune it again
278*74fe6c29SRuslan Bukin * together with all other sections in our cache.
279*74fe6c29SRuslan Bukin */
280*74fe6c29SRuslan Bukin limit = iscache->limit;
281*74fe6c29SRuslan Bukin if (limit < memsize)
282*74fe6c29SRuslan Bukin return 0;
283*74fe6c29SRuslan Bukin
284*74fe6c29SRuslan Bukin errcode = pt_section_map_share(section);
285*74fe6c29SRuslan Bukin if (errcode < 0)
286*74fe6c29SRuslan Bukin return errcode;
287*74fe6c29SRuslan Bukin
288*74fe6c29SRuslan Bukin lru = malloc(sizeof(*lru));
289*74fe6c29SRuslan Bukin if (!lru) {
290*74fe6c29SRuslan Bukin (void) pt_section_unmap(section);
291*74fe6c29SRuslan Bukin return -pte_nomem;
292*74fe6c29SRuslan Bukin }
293*74fe6c29SRuslan Bukin
294*74fe6c29SRuslan Bukin lru->section = section;
295*74fe6c29SRuslan Bukin lru->size = memsize;
296*74fe6c29SRuslan Bukin
297*74fe6c29SRuslan Bukin lru->next = iscache->lru;
298*74fe6c29SRuslan Bukin iscache->lru = lru;
299*74fe6c29SRuslan Bukin
300*74fe6c29SRuslan Bukin used = iscache->used;
301*74fe6c29SRuslan Bukin total = used + memsize;
302*74fe6c29SRuslan Bukin if (total < used || total < memsize)
303*74fe6c29SRuslan Bukin return -pte_overflow;
304*74fe6c29SRuslan Bukin
305*74fe6c29SRuslan Bukin iscache->used = total;
306*74fe6c29SRuslan Bukin
307*74fe6c29SRuslan Bukin return (limit < total) ? 1 : 0;
308*74fe6c29SRuslan Bukin }
309*74fe6c29SRuslan Bukin
310*74fe6c29SRuslan Bukin /* Add or move @section to the front of @iscache->lru.
311*74fe6c29SRuslan Bukin *
312*74fe6c29SRuslan Bukin * Returns a positive integer if we need to prune the cache.
313*74fe6c29SRuslan Bukin * Returns zero if we don't need to prune the cache.
314*74fe6c29SRuslan Bukin * Returns a negative pt_error_code otherwise.
315*74fe6c29SRuslan Bukin */
pt_iscache_lru_add(struct pt_image_section_cache * iscache,struct pt_section * section)316*74fe6c29SRuslan Bukin static int pt_iscache_lru_add(struct pt_image_section_cache *iscache,
317*74fe6c29SRuslan Bukin struct pt_section *section)
318*74fe6c29SRuslan Bukin {
319*74fe6c29SRuslan Bukin struct pt_iscache_lru_entry *lru, **pnext;
320*74fe6c29SRuslan Bukin
321*74fe6c29SRuslan Bukin if (!iscache)
322*74fe6c29SRuslan Bukin return -pte_internal;
323*74fe6c29SRuslan Bukin
324*74fe6c29SRuslan Bukin pnext = &iscache->lru;
325*74fe6c29SRuslan Bukin for (lru = *pnext; lru; pnext = &lru->next, lru = *pnext) {
326*74fe6c29SRuslan Bukin
327*74fe6c29SRuslan Bukin if (lru->section != section)
328*74fe6c29SRuslan Bukin continue;
329*74fe6c29SRuslan Bukin
330*74fe6c29SRuslan Bukin /* We found it in the cache. Move it to the front. */
331*74fe6c29SRuslan Bukin *pnext = lru->next;
332*74fe6c29SRuslan Bukin lru->next = iscache->lru;
333*74fe6c29SRuslan Bukin iscache->lru = lru;
334*74fe6c29SRuslan Bukin
335*74fe6c29SRuslan Bukin return 0;
336*74fe6c29SRuslan Bukin }
337*74fe6c29SRuslan Bukin
338*74fe6c29SRuslan Bukin /* We didn't find it in the cache. Add it. */
339*74fe6c29SRuslan Bukin return pt_isache_lru_new(iscache, section);
340*74fe6c29SRuslan Bukin }
341*74fe6c29SRuslan Bukin
342*74fe6c29SRuslan Bukin
343*74fe6c29SRuslan Bukin /* Remove @section from @iscache->lru.
344*74fe6c29SRuslan Bukin *
345*74fe6c29SRuslan Bukin * Returns zero on success, a negative pt_error_code otherwise.
346*74fe6c29SRuslan Bukin */
pt_iscache_lru_remove(struct pt_image_section_cache * iscache,const struct pt_section * section)347*74fe6c29SRuslan Bukin static int pt_iscache_lru_remove(struct pt_image_section_cache *iscache,
348*74fe6c29SRuslan Bukin const struct pt_section *section)
349*74fe6c29SRuslan Bukin {
350*74fe6c29SRuslan Bukin struct pt_iscache_lru_entry *lru, **pnext;
351*74fe6c29SRuslan Bukin
352*74fe6c29SRuslan Bukin if (!iscache)
353*74fe6c29SRuslan Bukin return -pte_internal;
354*74fe6c29SRuslan Bukin
355*74fe6c29SRuslan Bukin pnext = &iscache->lru;
356*74fe6c29SRuslan Bukin for (lru = *pnext; lru; pnext = &lru->next, lru = *pnext) {
357*74fe6c29SRuslan Bukin
358*74fe6c29SRuslan Bukin if (lru->section != section)
359*74fe6c29SRuslan Bukin continue;
360*74fe6c29SRuslan Bukin
361*74fe6c29SRuslan Bukin /* We found it in the cache. Remove it. */
362*74fe6c29SRuslan Bukin *pnext = lru->next;
363*74fe6c29SRuslan Bukin lru->next = NULL;
364*74fe6c29SRuslan Bukin break;
365*74fe6c29SRuslan Bukin }
366*74fe6c29SRuslan Bukin
367*74fe6c29SRuslan Bukin return pt_iscache_lru_free(lru);
368*74fe6c29SRuslan Bukin }
369*74fe6c29SRuslan Bukin
370*74fe6c29SRuslan Bukin
371*74fe6c29SRuslan Bukin /* Add or move @section to the front of @iscache->lru and update its size.
372*74fe6c29SRuslan Bukin *
373*74fe6c29SRuslan Bukin * Returns a positive integer if we need to prune the cache.
374*74fe6c29SRuslan Bukin * Returns zero if we don't need to prune the cache.
375*74fe6c29SRuslan Bukin * Returns a negative pt_error_code otherwise.
376*74fe6c29SRuslan Bukin */
pt_iscache_lru_resize(struct pt_image_section_cache * iscache,struct pt_section * section,uint64_t memsize)377*74fe6c29SRuslan Bukin static int pt_iscache_lru_resize(struct pt_image_section_cache *iscache,
378*74fe6c29SRuslan Bukin struct pt_section *section, uint64_t memsize)
379*74fe6c29SRuslan Bukin {
380*74fe6c29SRuslan Bukin struct pt_iscache_lru_entry *lru;
381*74fe6c29SRuslan Bukin uint64_t oldsize, used;
382*74fe6c29SRuslan Bukin int status;
383*74fe6c29SRuslan Bukin
384*74fe6c29SRuslan Bukin if (!iscache)
385*74fe6c29SRuslan Bukin return -pte_internal;
386*74fe6c29SRuslan Bukin
387*74fe6c29SRuslan Bukin status = pt_iscache_lru_add(iscache, section);
388*74fe6c29SRuslan Bukin if (status < 0)
389*74fe6c29SRuslan Bukin return status;
390*74fe6c29SRuslan Bukin
391*74fe6c29SRuslan Bukin lru = iscache->lru;
392*74fe6c29SRuslan Bukin if (!lru) {
393*74fe6c29SRuslan Bukin if (status)
394*74fe6c29SRuslan Bukin return -pte_internal;
395*74fe6c29SRuslan Bukin return 0;
396*74fe6c29SRuslan Bukin }
397*74fe6c29SRuslan Bukin
398*74fe6c29SRuslan Bukin /* If @section is cached, it must be first.
399*74fe6c29SRuslan Bukin *
400*74fe6c29SRuslan Bukin * We may choose not to cache it, though, e.g. if it is too big.
401*74fe6c29SRuslan Bukin */
402*74fe6c29SRuslan Bukin if (lru->section != section) {
403*74fe6c29SRuslan Bukin if (iscache->limit < memsize)
404*74fe6c29SRuslan Bukin return 0;
405*74fe6c29SRuslan Bukin
406*74fe6c29SRuslan Bukin return -pte_internal;
407*74fe6c29SRuslan Bukin }
408*74fe6c29SRuslan Bukin
409*74fe6c29SRuslan Bukin oldsize = lru->size;
410*74fe6c29SRuslan Bukin lru->size = memsize;
411*74fe6c29SRuslan Bukin
412*74fe6c29SRuslan Bukin /* If we need to prune anyway, we're done. */
413*74fe6c29SRuslan Bukin if (status)
414*74fe6c29SRuslan Bukin return status;
415*74fe6c29SRuslan Bukin
416*74fe6c29SRuslan Bukin used = iscache->used;
417*74fe6c29SRuslan Bukin used -= oldsize;
418*74fe6c29SRuslan Bukin used += memsize;
419*74fe6c29SRuslan Bukin
420*74fe6c29SRuslan Bukin iscache->used = used;
421*74fe6c29SRuslan Bukin
422*74fe6c29SRuslan Bukin return (iscache->limit < used) ? 1 : 0;
423*74fe6c29SRuslan Bukin }
424*74fe6c29SRuslan Bukin
425*74fe6c29SRuslan Bukin /* Clear @iscache->lru.
426*74fe6c29SRuslan Bukin *
427*74fe6c29SRuslan Bukin * Unlike other iscache_lru functions, the caller does not lock @iscache.
428*74fe6c29SRuslan Bukin *
429*74fe6c29SRuslan Bukin * Return zero on success, a negative pt_error_code otherwise.
430*74fe6c29SRuslan Bukin */
pt_iscache_lru_clear(struct pt_image_section_cache * iscache)431*74fe6c29SRuslan Bukin static int pt_iscache_lru_clear(struct pt_image_section_cache *iscache)
432*74fe6c29SRuslan Bukin {
433*74fe6c29SRuslan Bukin struct pt_iscache_lru_entry *lru;
434*74fe6c29SRuslan Bukin int errcode;
435*74fe6c29SRuslan Bukin
436*74fe6c29SRuslan Bukin errcode = pt_iscache_lock(iscache);
437*74fe6c29SRuslan Bukin if (errcode < 0)
438*74fe6c29SRuslan Bukin return errcode;
439*74fe6c29SRuslan Bukin
440*74fe6c29SRuslan Bukin lru = iscache->lru;
441*74fe6c29SRuslan Bukin iscache->lru = NULL;
442*74fe6c29SRuslan Bukin iscache->used = 0ull;
443*74fe6c29SRuslan Bukin
444*74fe6c29SRuslan Bukin errcode = pt_iscache_unlock(iscache);
445*74fe6c29SRuslan Bukin if (errcode < 0)
446*74fe6c29SRuslan Bukin return errcode;
447*74fe6c29SRuslan Bukin
448*74fe6c29SRuslan Bukin return pt_iscache_lru_free(lru);
449*74fe6c29SRuslan Bukin }
450*74fe6c29SRuslan Bukin
451*74fe6c29SRuslan Bukin /* Search @iscache for a partial or exact match of @section loaded at @laddr and
452*74fe6c29SRuslan Bukin * return the corresponding index or @iscache->size if no match is found.
453*74fe6c29SRuslan Bukin *
454*74fe6c29SRuslan Bukin * The caller must lock @iscache.
455*74fe6c29SRuslan Bukin *
456*74fe6c29SRuslan Bukin * Returns a non-zero index on success, a negative pt_error_code otherwise.
457*74fe6c29SRuslan Bukin */
458*74fe6c29SRuslan Bukin static int
pt_iscache_find_section_locked(const struct pt_image_section_cache * iscache,const char * filename,uint64_t offset,uint64_t size,uint64_t laddr)459*74fe6c29SRuslan Bukin pt_iscache_find_section_locked(const struct pt_image_section_cache *iscache,
460*74fe6c29SRuslan Bukin const char *filename, uint64_t offset,
461*74fe6c29SRuslan Bukin uint64_t size, uint64_t laddr)
462*74fe6c29SRuslan Bukin {
463*74fe6c29SRuslan Bukin const struct pt_section *section;
464*74fe6c29SRuslan Bukin uint16_t idx, end;
465*74fe6c29SRuslan Bukin int match;
466*74fe6c29SRuslan Bukin
467*74fe6c29SRuslan Bukin if (!iscache || !filename)
468*74fe6c29SRuslan Bukin return -pte_internal;
469*74fe6c29SRuslan Bukin
470*74fe6c29SRuslan Bukin section = NULL;
471*74fe6c29SRuslan Bukin match = end = iscache->size;
472*74fe6c29SRuslan Bukin for (idx = 0; idx < end; ++idx) {
473*74fe6c29SRuslan Bukin const struct pt_iscache_entry *entry;
474*74fe6c29SRuslan Bukin const struct pt_section *sec;
475*74fe6c29SRuslan Bukin
476*74fe6c29SRuslan Bukin entry = &iscache->entries[idx];
477*74fe6c29SRuslan Bukin
478*74fe6c29SRuslan Bukin /* We do not zero-initialize the array - a NULL check is
479*74fe6c29SRuslan Bukin * pointless.
480*74fe6c29SRuslan Bukin */
481*74fe6c29SRuslan Bukin sec = entry->section;
482*74fe6c29SRuslan Bukin
483*74fe6c29SRuslan Bukin /* Avoid redundant match checks. */
484*74fe6c29SRuslan Bukin if (sec != section) {
485*74fe6c29SRuslan Bukin const char *sec_filename;
486*74fe6c29SRuslan Bukin
487*74fe6c29SRuslan Bukin /* We don't have duplicates. Skip the check. */
488*74fe6c29SRuslan Bukin if (section)
489*74fe6c29SRuslan Bukin continue;
490*74fe6c29SRuslan Bukin
491*74fe6c29SRuslan Bukin if (offset != pt_section_offset(sec))
492*74fe6c29SRuslan Bukin continue;
493*74fe6c29SRuslan Bukin
494*74fe6c29SRuslan Bukin if (size != pt_section_size(sec))
495*74fe6c29SRuslan Bukin continue;
496*74fe6c29SRuslan Bukin
497*74fe6c29SRuslan Bukin sec_filename = pt_section_filename(sec);
498*74fe6c29SRuslan Bukin if (!sec_filename)
499*74fe6c29SRuslan Bukin return -pte_internal;
500*74fe6c29SRuslan Bukin
501*74fe6c29SRuslan Bukin if (strcmp(filename, sec_filename) != 0)
502*74fe6c29SRuslan Bukin continue;
503*74fe6c29SRuslan Bukin
504*74fe6c29SRuslan Bukin /* Use the cached section instead. */
505*74fe6c29SRuslan Bukin section = sec;
506*74fe6c29SRuslan Bukin match = idx;
507*74fe6c29SRuslan Bukin }
508*74fe6c29SRuslan Bukin
509*74fe6c29SRuslan Bukin /* If we didn't continue, @section == @sec and we have a match.
510*74fe6c29SRuslan Bukin *
511*74fe6c29SRuslan Bukin * If we also find a matching load address, we're done.
512*74fe6c29SRuslan Bukin */
513*74fe6c29SRuslan Bukin if (laddr == entry->laddr)
514*74fe6c29SRuslan Bukin return idx;
515*74fe6c29SRuslan Bukin }
516*74fe6c29SRuslan Bukin
517*74fe6c29SRuslan Bukin return match;
518*74fe6c29SRuslan Bukin }
519*74fe6c29SRuslan Bukin
pt_iscache_add(struct pt_image_section_cache * iscache,struct pt_section * section,uint64_t laddr)520*74fe6c29SRuslan Bukin int pt_iscache_add(struct pt_image_section_cache *iscache,
521*74fe6c29SRuslan Bukin struct pt_section *section, uint64_t laddr)
522*74fe6c29SRuslan Bukin {
523*74fe6c29SRuslan Bukin const char *filename;
524*74fe6c29SRuslan Bukin uint64_t offset, size;
525*74fe6c29SRuslan Bukin uint16_t idx;
526*74fe6c29SRuslan Bukin int errcode;
527*74fe6c29SRuslan Bukin
528*74fe6c29SRuslan Bukin if (!iscache || !section)
529*74fe6c29SRuslan Bukin return -pte_internal;
530*74fe6c29SRuslan Bukin
531*74fe6c29SRuslan Bukin /* We must have a filename for @section. */
532*74fe6c29SRuslan Bukin filename = pt_section_filename(section);
533*74fe6c29SRuslan Bukin if (!filename)
534*74fe6c29SRuslan Bukin return -pte_internal;
535*74fe6c29SRuslan Bukin
536*74fe6c29SRuslan Bukin offset = pt_section_offset(section);
537*74fe6c29SRuslan Bukin size = pt_section_size(section);
538*74fe6c29SRuslan Bukin
539*74fe6c29SRuslan Bukin /* Adding a section is slightly complicated by a potential deadlock
540*74fe6c29SRuslan Bukin * scenario:
541*74fe6c29SRuslan Bukin *
542*74fe6c29SRuslan Bukin * - in order to add a section, we need to attach to it, which
543*74fe6c29SRuslan Bukin * requires taking the section's attach lock.
544*74fe6c29SRuslan Bukin *
545*74fe6c29SRuslan Bukin * - if we are already attached to it, we may receive on-map
546*74fe6c29SRuslan Bukin * notifications, which will be sent while holding the attach lock
547*74fe6c29SRuslan Bukin * and require taking the iscache lock.
548*74fe6c29SRuslan Bukin *
549*74fe6c29SRuslan Bukin * Hence we can't attach to a section while holding the iscache lock.
550*74fe6c29SRuslan Bukin *
551*74fe6c29SRuslan Bukin *
552*74fe6c29SRuslan Bukin * We therefore attach to @section first and then lock @iscache.
553*74fe6c29SRuslan Bukin *
554*74fe6c29SRuslan Bukin * This opens a small window where an existing @section may be removed
555*74fe6c29SRuslan Bukin * from @iscache and replaced by a new matching section. We would want
556*74fe6c29SRuslan Bukin * to share that new section rather than adding a duplicate @section.
557*74fe6c29SRuslan Bukin *
558*74fe6c29SRuslan Bukin * After locking @iscache, we therefore check for existing matching
559*74fe6c29SRuslan Bukin * sections and, if one is found, update @section. This involves
560*74fe6c29SRuslan Bukin * detaching from @section and attaching to the existing section.
561*74fe6c29SRuslan Bukin *
562*74fe6c29SRuslan Bukin * And for this, we will have to temporarily unlock @iscache again.
563*74fe6c29SRuslan Bukin */
564*74fe6c29SRuslan Bukin errcode = pt_section_get(section);
565*74fe6c29SRuslan Bukin if (errcode < 0)
566*74fe6c29SRuslan Bukin return errcode;
567*74fe6c29SRuslan Bukin
568*74fe6c29SRuslan Bukin errcode = pt_section_attach(section, iscache);
569*74fe6c29SRuslan Bukin if (errcode < 0)
570*74fe6c29SRuslan Bukin goto out_put;
571*74fe6c29SRuslan Bukin
572*74fe6c29SRuslan Bukin errcode = pt_iscache_lock(iscache);
573*74fe6c29SRuslan Bukin if (errcode < 0)
574*74fe6c29SRuslan Bukin goto out_detach;
575*74fe6c29SRuslan Bukin
576*74fe6c29SRuslan Bukin /* We may need to repeat this step.
577*74fe6c29SRuslan Bukin *
578*74fe6c29SRuslan Bukin * Typically we don't and this takes only a single iteration. One
579*74fe6c29SRuslan Bukin * scenario where we do repeat this is when adding a section with an
580*74fe6c29SRuslan Bukin * out-of-bounds size.
581*74fe6c29SRuslan Bukin *
582*74fe6c29SRuslan Bukin * We will not find a matching section in pt_iscache_add_file() so we
583*74fe6c29SRuslan Bukin * create a new section. This will have its size reduced to match the
584*74fe6c29SRuslan Bukin * actual file size.
585*74fe6c29SRuslan Bukin *
586*74fe6c29SRuslan Bukin * For this reduced size, we may now find an existing section, and we
587*74fe6c29SRuslan Bukin * will take another trip in the below loop.
588*74fe6c29SRuslan Bukin */
589*74fe6c29SRuslan Bukin for (;;) {
590*74fe6c29SRuslan Bukin const struct pt_iscache_entry *entry;
591*74fe6c29SRuslan Bukin struct pt_section *sec;
592*74fe6c29SRuslan Bukin int match;
593*74fe6c29SRuslan Bukin
594*74fe6c29SRuslan Bukin /* Find an existing section matching @section that we'd share
595*74fe6c29SRuslan Bukin * rather than adding @section.
596*74fe6c29SRuslan Bukin */
597*74fe6c29SRuslan Bukin match = pt_iscache_find_section_locked(iscache, filename,
598*74fe6c29SRuslan Bukin offset, size, laddr);
599*74fe6c29SRuslan Bukin if (match < 0) {
600*74fe6c29SRuslan Bukin errcode = match;
601*74fe6c29SRuslan Bukin goto out_unlock_detach;
602*74fe6c29SRuslan Bukin }
603*74fe6c29SRuslan Bukin
604*74fe6c29SRuslan Bukin /* We're done if we have not found a matching section. */
605*74fe6c29SRuslan Bukin if (iscache->size <= match)
606*74fe6c29SRuslan Bukin break;
607*74fe6c29SRuslan Bukin
608*74fe6c29SRuslan Bukin entry = &iscache->entries[match];
609*74fe6c29SRuslan Bukin
610*74fe6c29SRuslan Bukin /* We're also done if we found the same section again.
611*74fe6c29SRuslan Bukin *
612*74fe6c29SRuslan Bukin * We further check for a perfect match. In that case, we don't
613*74fe6c29SRuslan Bukin * need to insert anything, at all.
614*74fe6c29SRuslan Bukin */
615*74fe6c29SRuslan Bukin sec = entry->section;
616*74fe6c29SRuslan Bukin if (sec == section) {
617*74fe6c29SRuslan Bukin if (entry->laddr == laddr) {
618*74fe6c29SRuslan Bukin errcode = pt_iscache_unlock(iscache);
619*74fe6c29SRuslan Bukin if (errcode < 0)
620*74fe6c29SRuslan Bukin goto out_detach;
621*74fe6c29SRuslan Bukin
622*74fe6c29SRuslan Bukin errcode = pt_section_detach(section, iscache);
623*74fe6c29SRuslan Bukin if (errcode < 0)
624*74fe6c29SRuslan Bukin goto out_lru;
625*74fe6c29SRuslan Bukin
626*74fe6c29SRuslan Bukin errcode = pt_section_put(section);
627*74fe6c29SRuslan Bukin if (errcode < 0)
628*74fe6c29SRuslan Bukin return errcode;
629*74fe6c29SRuslan Bukin
630*74fe6c29SRuslan Bukin return isid_from_index((uint16_t) match);
631*74fe6c29SRuslan Bukin }
632*74fe6c29SRuslan Bukin
633*74fe6c29SRuslan Bukin break;
634*74fe6c29SRuslan Bukin }
635*74fe6c29SRuslan Bukin
636*74fe6c29SRuslan Bukin /* We update @section to share the existing @sec.
637*74fe6c29SRuslan Bukin *
638*74fe6c29SRuslan Bukin * This requires detaching from @section, which, in turn,
639*74fe6c29SRuslan Bukin * requires temporarily unlocking @iscache.
640*74fe6c29SRuslan Bukin *
641*74fe6c29SRuslan Bukin * We further need to remove @section from @iscache->lru.
642*74fe6c29SRuslan Bukin */
643*74fe6c29SRuslan Bukin errcode = pt_section_get(sec);
644*74fe6c29SRuslan Bukin if (errcode < 0)
645*74fe6c29SRuslan Bukin goto out_unlock_detach;
646*74fe6c29SRuslan Bukin
647*74fe6c29SRuslan Bukin errcode = pt_iscache_unlock(iscache);
648*74fe6c29SRuslan Bukin if (errcode < 0) {
649*74fe6c29SRuslan Bukin (void) pt_section_put(sec);
650*74fe6c29SRuslan Bukin goto out_detach;
651*74fe6c29SRuslan Bukin }
652*74fe6c29SRuslan Bukin
653*74fe6c29SRuslan Bukin errcode = pt_section_detach(section, iscache);
654*74fe6c29SRuslan Bukin if (errcode < 0) {
655*74fe6c29SRuslan Bukin (void) pt_section_put(sec);
656*74fe6c29SRuslan Bukin goto out_lru;
657*74fe6c29SRuslan Bukin }
658*74fe6c29SRuslan Bukin
659*74fe6c29SRuslan Bukin errcode = pt_section_attach(sec, iscache);
660*74fe6c29SRuslan Bukin if (errcode < 0) {
661*74fe6c29SRuslan Bukin (void) pt_section_put(sec);
662*74fe6c29SRuslan Bukin goto out_lru;
663*74fe6c29SRuslan Bukin }
664*74fe6c29SRuslan Bukin
665*74fe6c29SRuslan Bukin errcode = pt_iscache_lock(iscache);
666*74fe6c29SRuslan Bukin if (errcode < 0) {
667*74fe6c29SRuslan Bukin (void) pt_section_put(section);
668*74fe6c29SRuslan Bukin /* Complete the swap for cleanup. */
669*74fe6c29SRuslan Bukin section = sec;
670*74fe6c29SRuslan Bukin goto out_detach;
671*74fe6c29SRuslan Bukin }
672*74fe6c29SRuslan Bukin
673*74fe6c29SRuslan Bukin /* We may have received on-map notifications for @section and we
674*74fe6c29SRuslan Bukin * may have added @section to @iscache->lru.
675*74fe6c29SRuslan Bukin *
676*74fe6c29SRuslan Bukin * Since we're still holding a reference to it, no harm has been
677*74fe6c29SRuslan Bukin * done. But we need to remove it before we drop our reference.
678*74fe6c29SRuslan Bukin */
679*74fe6c29SRuslan Bukin errcode = pt_iscache_lru_remove(iscache, section);
680*74fe6c29SRuslan Bukin if (errcode < 0) {
681*74fe6c29SRuslan Bukin (void) pt_section_put(section);
682*74fe6c29SRuslan Bukin /* Complete the swap for cleanup. */
683*74fe6c29SRuslan Bukin section = sec;
684*74fe6c29SRuslan Bukin goto out_unlock_detach;
685*74fe6c29SRuslan Bukin }
686*74fe6c29SRuslan Bukin
687*74fe6c29SRuslan Bukin /* Drop the reference to @section. */
688*74fe6c29SRuslan Bukin errcode = pt_section_put(section);
689*74fe6c29SRuslan Bukin if (errcode < 0) {
690*74fe6c29SRuslan Bukin /* Complete the swap for cleanup. */
691*74fe6c29SRuslan Bukin section = sec;
692*74fe6c29SRuslan Bukin goto out_unlock_detach;
693*74fe6c29SRuslan Bukin }
694*74fe6c29SRuslan Bukin
695*74fe6c29SRuslan Bukin /* Swap sections.
696*74fe6c29SRuslan Bukin *
697*74fe6c29SRuslan Bukin * We will try again in the next iteration.
698*74fe6c29SRuslan Bukin */
699*74fe6c29SRuslan Bukin section = sec;
700*74fe6c29SRuslan Bukin }
701*74fe6c29SRuslan Bukin
702*74fe6c29SRuslan Bukin /* Expand the cache, if necessary. */
703*74fe6c29SRuslan Bukin if (iscache->capacity <= iscache->size) {
704*74fe6c29SRuslan Bukin /* We must never exceed the capacity. */
705*74fe6c29SRuslan Bukin if (iscache->capacity < iscache->size) {
706*74fe6c29SRuslan Bukin errcode = -pte_internal;
707*74fe6c29SRuslan Bukin goto out_unlock_detach;
708*74fe6c29SRuslan Bukin }
709*74fe6c29SRuslan Bukin
710*74fe6c29SRuslan Bukin errcode = pt_iscache_expand(iscache);
711*74fe6c29SRuslan Bukin if (errcode < 0)
712*74fe6c29SRuslan Bukin goto out_unlock_detach;
713*74fe6c29SRuslan Bukin
714*74fe6c29SRuslan Bukin /* Make sure it is big enough, now. */
715*74fe6c29SRuslan Bukin if (iscache->capacity <= iscache->size) {
716*74fe6c29SRuslan Bukin errcode = -pte_internal;
717*74fe6c29SRuslan Bukin goto out_unlock_detach;
718*74fe6c29SRuslan Bukin }
719*74fe6c29SRuslan Bukin }
720*74fe6c29SRuslan Bukin
721*74fe6c29SRuslan Bukin /* Insert a new entry for @section at @laddr.
722*74fe6c29SRuslan Bukin *
723*74fe6c29SRuslan Bukin * This hands both attach and reference over to @iscache. We will
724*74fe6c29SRuslan Bukin * detach and drop the reference again when the entry is removed.
725*74fe6c29SRuslan Bukin */
726*74fe6c29SRuslan Bukin idx = iscache->size++;
727*74fe6c29SRuslan Bukin
728*74fe6c29SRuslan Bukin iscache->entries[idx].section = section;
729*74fe6c29SRuslan Bukin iscache->entries[idx].laddr = laddr;
730*74fe6c29SRuslan Bukin
731*74fe6c29SRuslan Bukin errcode = pt_iscache_unlock(iscache);
732*74fe6c29SRuslan Bukin if (errcode < 0)
733*74fe6c29SRuslan Bukin return errcode;
734*74fe6c29SRuslan Bukin
735*74fe6c29SRuslan Bukin return isid_from_index(idx);
736*74fe6c29SRuslan Bukin
737*74fe6c29SRuslan Bukin out_unlock_detach:
738*74fe6c29SRuslan Bukin (void) pt_iscache_unlock(iscache);
739*74fe6c29SRuslan Bukin
740*74fe6c29SRuslan Bukin out_detach:
741*74fe6c29SRuslan Bukin (void) pt_section_detach(section, iscache);
742*74fe6c29SRuslan Bukin
743*74fe6c29SRuslan Bukin out_lru:
744*74fe6c29SRuslan Bukin (void) pt_iscache_lru_clear(iscache);
745*74fe6c29SRuslan Bukin
746*74fe6c29SRuslan Bukin out_put:
747*74fe6c29SRuslan Bukin (void) pt_section_put(section);
748*74fe6c29SRuslan Bukin
749*74fe6c29SRuslan Bukin return errcode;
750*74fe6c29SRuslan Bukin }
751*74fe6c29SRuslan Bukin
pt_iscache_find(struct pt_image_section_cache * iscache,const char * filename,uint64_t offset,uint64_t size,uint64_t laddr)752*74fe6c29SRuslan Bukin int pt_iscache_find(struct pt_image_section_cache *iscache,
753*74fe6c29SRuslan Bukin const char *filename, uint64_t offset, uint64_t size,
754*74fe6c29SRuslan Bukin uint64_t laddr)
755*74fe6c29SRuslan Bukin {
756*74fe6c29SRuslan Bukin int errcode, isid;
757*74fe6c29SRuslan Bukin
758*74fe6c29SRuslan Bukin errcode = pt_iscache_lock(iscache);
759*74fe6c29SRuslan Bukin if (errcode < 0)
760*74fe6c29SRuslan Bukin return errcode;
761*74fe6c29SRuslan Bukin
762*74fe6c29SRuslan Bukin isid = pt_iscache_find_locked(iscache, filename, offset, size, laddr);
763*74fe6c29SRuslan Bukin
764*74fe6c29SRuslan Bukin errcode = pt_iscache_unlock(iscache);
765*74fe6c29SRuslan Bukin if (errcode < 0)
766*74fe6c29SRuslan Bukin return errcode;
767*74fe6c29SRuslan Bukin
768*74fe6c29SRuslan Bukin return isid;
769*74fe6c29SRuslan Bukin }
770*74fe6c29SRuslan Bukin
pt_iscache_lookup(struct pt_image_section_cache * iscache,struct pt_section ** section,uint64_t * laddr,int isid)771*74fe6c29SRuslan Bukin int pt_iscache_lookup(struct pt_image_section_cache *iscache,
772*74fe6c29SRuslan Bukin struct pt_section **section, uint64_t *laddr, int isid)
773*74fe6c29SRuslan Bukin {
774*74fe6c29SRuslan Bukin uint16_t index;
775*74fe6c29SRuslan Bukin int errcode, status;
776*74fe6c29SRuslan Bukin
777*74fe6c29SRuslan Bukin if (!iscache || !section || !laddr)
778*74fe6c29SRuslan Bukin return -pte_internal;
779*74fe6c29SRuslan Bukin
780*74fe6c29SRuslan Bukin if (isid <= 0)
781*74fe6c29SRuslan Bukin return -pte_bad_image;
782*74fe6c29SRuslan Bukin
783*74fe6c29SRuslan Bukin isid -= 1;
784*74fe6c29SRuslan Bukin if (isid > UINT16_MAX)
785*74fe6c29SRuslan Bukin return -pte_internal;
786*74fe6c29SRuslan Bukin
787*74fe6c29SRuslan Bukin index = (uint16_t) isid;
788*74fe6c29SRuslan Bukin
789*74fe6c29SRuslan Bukin errcode = pt_iscache_lock(iscache);
790*74fe6c29SRuslan Bukin if (errcode < 0)
791*74fe6c29SRuslan Bukin return errcode;
792*74fe6c29SRuslan Bukin
793*74fe6c29SRuslan Bukin if (iscache->size <= index)
794*74fe6c29SRuslan Bukin status = -pte_bad_image;
795*74fe6c29SRuslan Bukin else {
796*74fe6c29SRuslan Bukin const struct pt_iscache_entry *entry;
797*74fe6c29SRuslan Bukin
798*74fe6c29SRuslan Bukin entry = &iscache->entries[index];
799*74fe6c29SRuslan Bukin *section = entry->section;
800*74fe6c29SRuslan Bukin *laddr = entry->laddr;
801*74fe6c29SRuslan Bukin
802*74fe6c29SRuslan Bukin status = pt_section_get(*section);
803*74fe6c29SRuslan Bukin }
804*74fe6c29SRuslan Bukin
805*74fe6c29SRuslan Bukin errcode = pt_iscache_unlock(iscache);
806*74fe6c29SRuslan Bukin if (errcode < 0)
807*74fe6c29SRuslan Bukin return errcode;
808*74fe6c29SRuslan Bukin
809*74fe6c29SRuslan Bukin return status;
810*74fe6c29SRuslan Bukin }
811*74fe6c29SRuslan Bukin
pt_iscache_clear(struct pt_image_section_cache * iscache)812*74fe6c29SRuslan Bukin int pt_iscache_clear(struct pt_image_section_cache *iscache)
813*74fe6c29SRuslan Bukin {
814*74fe6c29SRuslan Bukin struct pt_iscache_lru_entry *lru;
815*74fe6c29SRuslan Bukin struct pt_iscache_entry *entries;
816*74fe6c29SRuslan Bukin uint16_t idx, end;
817*74fe6c29SRuslan Bukin int errcode;
818*74fe6c29SRuslan Bukin
819*74fe6c29SRuslan Bukin if (!iscache)
820*74fe6c29SRuslan Bukin return -pte_internal;
821*74fe6c29SRuslan Bukin
822*74fe6c29SRuslan Bukin errcode = pt_iscache_lock(iscache);
823*74fe6c29SRuslan Bukin if (errcode < 0)
824*74fe6c29SRuslan Bukin return errcode;
825*74fe6c29SRuslan Bukin
826*74fe6c29SRuslan Bukin entries = iscache->entries;
827*74fe6c29SRuslan Bukin end = iscache->size;
828*74fe6c29SRuslan Bukin lru = iscache->lru;
829*74fe6c29SRuslan Bukin
830*74fe6c29SRuslan Bukin iscache->entries = NULL;
831*74fe6c29SRuslan Bukin iscache->capacity = 0;
832*74fe6c29SRuslan Bukin iscache->size = 0;
833*74fe6c29SRuslan Bukin iscache->lru = NULL;
834*74fe6c29SRuslan Bukin iscache->used = 0ull;
835*74fe6c29SRuslan Bukin
836*74fe6c29SRuslan Bukin errcode = pt_iscache_unlock(iscache);
837*74fe6c29SRuslan Bukin if (errcode < 0)
838*74fe6c29SRuslan Bukin return errcode;
839*74fe6c29SRuslan Bukin
840*74fe6c29SRuslan Bukin errcode = pt_iscache_lru_free(lru);
841*74fe6c29SRuslan Bukin if (errcode < 0)
842*74fe6c29SRuslan Bukin return errcode;
843*74fe6c29SRuslan Bukin
844*74fe6c29SRuslan Bukin for (idx = 0; idx < end; ++idx) {
845*74fe6c29SRuslan Bukin struct pt_section *section;
846*74fe6c29SRuslan Bukin
847*74fe6c29SRuslan Bukin section = entries[idx].section;
848*74fe6c29SRuslan Bukin
849*74fe6c29SRuslan Bukin /* We do not zero-initialize the array - a NULL check is
850*74fe6c29SRuslan Bukin * pointless.
851*74fe6c29SRuslan Bukin */
852*74fe6c29SRuslan Bukin errcode = pt_section_detach(section, iscache);
853*74fe6c29SRuslan Bukin if (errcode < 0)
854*74fe6c29SRuslan Bukin return errcode;
855*74fe6c29SRuslan Bukin
856*74fe6c29SRuslan Bukin errcode = pt_section_put(section);
857*74fe6c29SRuslan Bukin if (errcode < 0)
858*74fe6c29SRuslan Bukin return errcode;
859*74fe6c29SRuslan Bukin }
860*74fe6c29SRuslan Bukin
861*74fe6c29SRuslan Bukin free(entries);
862*74fe6c29SRuslan Bukin return 0;
863*74fe6c29SRuslan Bukin }
864*74fe6c29SRuslan Bukin
pt_iscache_alloc(const char * name)865*74fe6c29SRuslan Bukin struct pt_image_section_cache *pt_iscache_alloc(const char *name)
866*74fe6c29SRuslan Bukin {
867*74fe6c29SRuslan Bukin struct pt_image_section_cache *iscache;
868*74fe6c29SRuslan Bukin
869*74fe6c29SRuslan Bukin iscache = malloc(sizeof(*iscache));
870*74fe6c29SRuslan Bukin if (iscache)
871*74fe6c29SRuslan Bukin pt_iscache_init(iscache, name);
872*74fe6c29SRuslan Bukin
873*74fe6c29SRuslan Bukin return iscache;
874*74fe6c29SRuslan Bukin }
875*74fe6c29SRuslan Bukin
pt_iscache_free(struct pt_image_section_cache * iscache)876*74fe6c29SRuslan Bukin void pt_iscache_free(struct pt_image_section_cache *iscache)
877*74fe6c29SRuslan Bukin {
878*74fe6c29SRuslan Bukin if (!iscache)
879*74fe6c29SRuslan Bukin return;
880*74fe6c29SRuslan Bukin
881*74fe6c29SRuslan Bukin pt_iscache_fini(iscache);
882*74fe6c29SRuslan Bukin free(iscache);
883*74fe6c29SRuslan Bukin }
884*74fe6c29SRuslan Bukin
pt_iscache_set_limit(struct pt_image_section_cache * iscache,uint64_t limit)885*74fe6c29SRuslan Bukin int pt_iscache_set_limit(struct pt_image_section_cache *iscache, uint64_t limit)
886*74fe6c29SRuslan Bukin {
887*74fe6c29SRuslan Bukin struct pt_iscache_lru_entry *tail;
888*74fe6c29SRuslan Bukin int errcode, status;
889*74fe6c29SRuslan Bukin
890*74fe6c29SRuslan Bukin if (!iscache)
891*74fe6c29SRuslan Bukin return -pte_invalid;
892*74fe6c29SRuslan Bukin
893*74fe6c29SRuslan Bukin status = 0;
894*74fe6c29SRuslan Bukin tail = NULL;
895*74fe6c29SRuslan Bukin
896*74fe6c29SRuslan Bukin errcode = pt_iscache_lock(iscache);
897*74fe6c29SRuslan Bukin if (errcode < 0)
898*74fe6c29SRuslan Bukin return errcode;
899*74fe6c29SRuslan Bukin
900*74fe6c29SRuslan Bukin iscache->limit = limit;
901*74fe6c29SRuslan Bukin if (limit < iscache->used)
902*74fe6c29SRuslan Bukin status = pt_iscache_lru_prune(iscache, &tail);
903*74fe6c29SRuslan Bukin
904*74fe6c29SRuslan Bukin errcode = pt_iscache_unlock(iscache);
905*74fe6c29SRuslan Bukin
906*74fe6c29SRuslan Bukin if (errcode < 0 || status < 0)
907*74fe6c29SRuslan Bukin return (status < 0) ? status : errcode;
908*74fe6c29SRuslan Bukin
909*74fe6c29SRuslan Bukin return pt_iscache_lru_free(tail);
910*74fe6c29SRuslan Bukin }
911*74fe6c29SRuslan Bukin
pt_iscache_name(const struct pt_image_section_cache * iscache)912*74fe6c29SRuslan Bukin const char *pt_iscache_name(const struct pt_image_section_cache *iscache)
913*74fe6c29SRuslan Bukin {
914*74fe6c29SRuslan Bukin if (!iscache)
915*74fe6c29SRuslan Bukin return NULL;
916*74fe6c29SRuslan Bukin
917*74fe6c29SRuslan Bukin return iscache->name;
918*74fe6c29SRuslan Bukin }
919*74fe6c29SRuslan Bukin
pt_iscache_add_file(struct pt_image_section_cache * iscache,const char * filename,uint64_t offset,uint64_t size,uint64_t vaddr)920*74fe6c29SRuslan Bukin int pt_iscache_add_file(struct pt_image_section_cache *iscache,
921*74fe6c29SRuslan Bukin const char *filename, uint64_t offset, uint64_t size,
922*74fe6c29SRuslan Bukin uint64_t vaddr)
923*74fe6c29SRuslan Bukin {
924*74fe6c29SRuslan Bukin struct pt_section *section;
925*74fe6c29SRuslan Bukin int errcode, match, isid;
926*74fe6c29SRuslan Bukin
927*74fe6c29SRuslan Bukin if (!iscache || !filename)
928*74fe6c29SRuslan Bukin return -pte_invalid;
929*74fe6c29SRuslan Bukin
930*74fe6c29SRuslan Bukin errcode = pt_iscache_lock(iscache);
931*74fe6c29SRuslan Bukin if (errcode < 0)
932*74fe6c29SRuslan Bukin return errcode;
933*74fe6c29SRuslan Bukin
934*74fe6c29SRuslan Bukin match = pt_iscache_find_section_locked(iscache, filename, offset,
935*74fe6c29SRuslan Bukin size, vaddr);
936*74fe6c29SRuslan Bukin if (match < 0) {
937*74fe6c29SRuslan Bukin (void) pt_iscache_unlock(iscache);
938*74fe6c29SRuslan Bukin return match;
939*74fe6c29SRuslan Bukin }
940*74fe6c29SRuslan Bukin
941*74fe6c29SRuslan Bukin /* If we found a perfect match, we will share the existing entry.
942*74fe6c29SRuslan Bukin *
943*74fe6c29SRuslan Bukin * If we found a section, we need to grab a reference before we unlock.
944*74fe6c29SRuslan Bukin *
945*74fe6c29SRuslan Bukin * If we didn't find a matching section, we create a new section, which
946*74fe6c29SRuslan Bukin * implicitly gives us a reference to it.
947*74fe6c29SRuslan Bukin */
948*74fe6c29SRuslan Bukin if (match < iscache->size) {
949*74fe6c29SRuslan Bukin const struct pt_iscache_entry *entry;
950*74fe6c29SRuslan Bukin
951*74fe6c29SRuslan Bukin entry = &iscache->entries[match];
952*74fe6c29SRuslan Bukin if (entry->laddr == vaddr) {
953*74fe6c29SRuslan Bukin errcode = pt_iscache_unlock(iscache);
954*74fe6c29SRuslan Bukin if (errcode < 0)
955*74fe6c29SRuslan Bukin return errcode;
956*74fe6c29SRuslan Bukin
957*74fe6c29SRuslan Bukin return isid_from_index((uint16_t) match);
958*74fe6c29SRuslan Bukin }
959*74fe6c29SRuslan Bukin
960*74fe6c29SRuslan Bukin section = entry->section;
961*74fe6c29SRuslan Bukin
962*74fe6c29SRuslan Bukin errcode = pt_section_get(section);
963*74fe6c29SRuslan Bukin if (errcode < 0) {
964*74fe6c29SRuslan Bukin (void) pt_iscache_unlock(iscache);
965*74fe6c29SRuslan Bukin return errcode;
966*74fe6c29SRuslan Bukin }
967*74fe6c29SRuslan Bukin
968*74fe6c29SRuslan Bukin errcode = pt_iscache_unlock(iscache);
969*74fe6c29SRuslan Bukin if (errcode < 0) {
970*74fe6c29SRuslan Bukin (void) pt_section_put(section);
971*74fe6c29SRuslan Bukin return errcode;
972*74fe6c29SRuslan Bukin }
973*74fe6c29SRuslan Bukin } else {
974*74fe6c29SRuslan Bukin errcode = pt_iscache_unlock(iscache);
975*74fe6c29SRuslan Bukin if (errcode < 0)
976*74fe6c29SRuslan Bukin return errcode;
977*74fe6c29SRuslan Bukin
978*74fe6c29SRuslan Bukin section = pt_mk_section(filename, offset, size);
979*74fe6c29SRuslan Bukin if (!section)
980*74fe6c29SRuslan Bukin return -pte_invalid;
981*74fe6c29SRuslan Bukin }
982*74fe6c29SRuslan Bukin
983*74fe6c29SRuslan Bukin /* We unlocked @iscache and hold a reference to @section. */
984*74fe6c29SRuslan Bukin isid = pt_iscache_add(iscache, section, vaddr);
985*74fe6c29SRuslan Bukin
986*74fe6c29SRuslan Bukin /* We grab a reference when we add the section. Drop the one we
987*74fe6c29SRuslan Bukin * obtained before.
988*74fe6c29SRuslan Bukin */
989*74fe6c29SRuslan Bukin errcode = pt_section_put(section);
990*74fe6c29SRuslan Bukin if (errcode < 0)
991*74fe6c29SRuslan Bukin return errcode;
992*74fe6c29SRuslan Bukin
993*74fe6c29SRuslan Bukin return isid;
994*74fe6c29SRuslan Bukin }
995*74fe6c29SRuslan Bukin
996*74fe6c29SRuslan Bukin
pt_iscache_read(struct pt_image_section_cache * iscache,uint8_t * buffer,uint64_t size,int isid,uint64_t vaddr)997*74fe6c29SRuslan Bukin int pt_iscache_read(struct pt_image_section_cache *iscache, uint8_t *buffer,
998*74fe6c29SRuslan Bukin uint64_t size, int isid, uint64_t vaddr)
999*74fe6c29SRuslan Bukin {
1000*74fe6c29SRuslan Bukin struct pt_section *section;
1001*74fe6c29SRuslan Bukin uint64_t laddr;
1002*74fe6c29SRuslan Bukin int errcode, status;
1003*74fe6c29SRuslan Bukin
1004*74fe6c29SRuslan Bukin if (!iscache || !buffer || !size)
1005*74fe6c29SRuslan Bukin return -pte_invalid;
1006*74fe6c29SRuslan Bukin
1007*74fe6c29SRuslan Bukin errcode = pt_iscache_lookup(iscache, §ion, &laddr, isid);
1008*74fe6c29SRuslan Bukin if (errcode < 0)
1009*74fe6c29SRuslan Bukin return errcode;
1010*74fe6c29SRuslan Bukin
1011*74fe6c29SRuslan Bukin if (vaddr < laddr) {
1012*74fe6c29SRuslan Bukin (void) pt_section_put(section);
1013*74fe6c29SRuslan Bukin return -pte_nomap;
1014*74fe6c29SRuslan Bukin }
1015*74fe6c29SRuslan Bukin
1016*74fe6c29SRuslan Bukin vaddr -= laddr;
1017*74fe6c29SRuslan Bukin
1018*74fe6c29SRuslan Bukin errcode = pt_section_map(section);
1019*74fe6c29SRuslan Bukin if (errcode < 0) {
1020*74fe6c29SRuslan Bukin (void) pt_section_put(section);
1021*74fe6c29SRuslan Bukin return errcode;
1022*74fe6c29SRuslan Bukin }
1023*74fe6c29SRuslan Bukin
1024*74fe6c29SRuslan Bukin /* We truncate the read if it gets too big. The user is expected to
1025*74fe6c29SRuslan Bukin * issue further reads for the remaining part.
1026*74fe6c29SRuslan Bukin */
1027*74fe6c29SRuslan Bukin if (UINT16_MAX < size)
1028*74fe6c29SRuslan Bukin size = UINT16_MAX;
1029*74fe6c29SRuslan Bukin
1030*74fe6c29SRuslan Bukin status = pt_section_read(section, buffer, (uint16_t) size, vaddr);
1031*74fe6c29SRuslan Bukin
1032*74fe6c29SRuslan Bukin errcode = pt_section_unmap(section);
1033*74fe6c29SRuslan Bukin if (errcode < 0) {
1034*74fe6c29SRuslan Bukin (void) pt_section_put(section);
1035*74fe6c29SRuslan Bukin return errcode;
1036*74fe6c29SRuslan Bukin }
1037*74fe6c29SRuslan Bukin
1038*74fe6c29SRuslan Bukin errcode = pt_section_put(section);
1039*74fe6c29SRuslan Bukin if (errcode < 0)
1040*74fe6c29SRuslan Bukin return errcode;
1041*74fe6c29SRuslan Bukin
1042*74fe6c29SRuslan Bukin return status;
1043*74fe6c29SRuslan Bukin }
1044*74fe6c29SRuslan Bukin
pt_iscache_notify_map(struct pt_image_section_cache * iscache,struct pt_section * section)1045*74fe6c29SRuslan Bukin int pt_iscache_notify_map(struct pt_image_section_cache *iscache,
1046*74fe6c29SRuslan Bukin struct pt_section *section)
1047*74fe6c29SRuslan Bukin {
1048*74fe6c29SRuslan Bukin struct pt_iscache_lru_entry *tail;
1049*74fe6c29SRuslan Bukin int errcode, status;
1050*74fe6c29SRuslan Bukin
1051*74fe6c29SRuslan Bukin tail = NULL;
1052*74fe6c29SRuslan Bukin
1053*74fe6c29SRuslan Bukin errcode = pt_iscache_lock(iscache);
1054*74fe6c29SRuslan Bukin if (errcode < 0)
1055*74fe6c29SRuslan Bukin return errcode;
1056*74fe6c29SRuslan Bukin
1057*74fe6c29SRuslan Bukin status = pt_iscache_lru_add(iscache, section);
1058*74fe6c29SRuslan Bukin if (status > 0)
1059*74fe6c29SRuslan Bukin status = pt_iscache_lru_prune(iscache, &tail);
1060*74fe6c29SRuslan Bukin
1061*74fe6c29SRuslan Bukin errcode = pt_iscache_unlock(iscache);
1062*74fe6c29SRuslan Bukin
1063*74fe6c29SRuslan Bukin if (errcode < 0 || status < 0)
1064*74fe6c29SRuslan Bukin return (status < 0) ? status : errcode;
1065*74fe6c29SRuslan Bukin
1066*74fe6c29SRuslan Bukin return pt_iscache_lru_free(tail);
1067*74fe6c29SRuslan Bukin }
1068*74fe6c29SRuslan Bukin
pt_iscache_notify_resize(struct pt_image_section_cache * iscache,struct pt_section * section,uint64_t memsize)1069*74fe6c29SRuslan Bukin int pt_iscache_notify_resize(struct pt_image_section_cache *iscache,
1070*74fe6c29SRuslan Bukin struct pt_section *section, uint64_t memsize)
1071*74fe6c29SRuslan Bukin {
1072*74fe6c29SRuslan Bukin struct pt_iscache_lru_entry *tail;
1073*74fe6c29SRuslan Bukin int errcode, status;
1074*74fe6c29SRuslan Bukin
1075*74fe6c29SRuslan Bukin tail = NULL;
1076*74fe6c29SRuslan Bukin
1077*74fe6c29SRuslan Bukin errcode = pt_iscache_lock(iscache);
1078*74fe6c29SRuslan Bukin if (errcode < 0)
1079*74fe6c29SRuslan Bukin return errcode;
1080*74fe6c29SRuslan Bukin
1081*74fe6c29SRuslan Bukin status = pt_iscache_lru_resize(iscache, section, memsize);
1082*74fe6c29SRuslan Bukin if (status > 0)
1083*74fe6c29SRuslan Bukin status = pt_iscache_lru_prune(iscache, &tail);
1084*74fe6c29SRuslan Bukin
1085*74fe6c29SRuslan Bukin errcode = pt_iscache_unlock(iscache);
1086*74fe6c29SRuslan Bukin
1087*74fe6c29SRuslan Bukin if (errcode < 0 || status < 0)
1088*74fe6c29SRuslan Bukin return (status < 0) ? status : errcode;
1089*74fe6c29SRuslan Bukin
1090*74fe6c29SRuslan Bukin return pt_iscache_lru_free(tail);
1091*74fe6c29SRuslan Bukin }
1092