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, &section, &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