xref: /freebsd-src/contrib/processor-trace/libipt/src/pt_image_section_cache.c (revision 85f87cf491bec6f90948a85b10f5523ea24db9e3)
174fe6c29SRuslan Bukin /*
2*85f87cf4SRuslan Bukin  * Copyright (c) 2016-2019, Intel Corporation
374fe6c29SRuslan Bukin  *
474fe6c29SRuslan Bukin  * Redistribution and use in source and binary forms, with or without
574fe6c29SRuslan Bukin  * modification, are permitted provided that the following conditions are met:
674fe6c29SRuslan Bukin  *
774fe6c29SRuslan Bukin  *  * Redistributions of source code must retain the above copyright notice,
874fe6c29SRuslan Bukin  *    this list of conditions and the following disclaimer.
974fe6c29SRuslan Bukin  *  * Redistributions in binary form must reproduce the above copyright notice,
1074fe6c29SRuslan Bukin  *    this list of conditions and the following disclaimer in the documentation
1174fe6c29SRuslan Bukin  *    and/or other materials provided with the distribution.
1274fe6c29SRuslan Bukin  *  * Neither the name of Intel Corporation nor the names of its contributors
1374fe6c29SRuslan Bukin  *    may be used to endorse or promote products derived from this software
1474fe6c29SRuslan Bukin  *    without specific prior written permission.
1574fe6c29SRuslan Bukin  *
1674fe6c29SRuslan Bukin  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1774fe6c29SRuslan Bukin  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1874fe6c29SRuslan Bukin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1974fe6c29SRuslan Bukin  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
2074fe6c29SRuslan Bukin  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2174fe6c29SRuslan Bukin  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2274fe6c29SRuslan Bukin  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2374fe6c29SRuslan Bukin  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2474fe6c29SRuslan Bukin  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2574fe6c29SRuslan Bukin  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2674fe6c29SRuslan Bukin  * POSSIBILITY OF SUCH DAMAGE.
2774fe6c29SRuslan Bukin  */
2874fe6c29SRuslan Bukin 
2974fe6c29SRuslan Bukin #include "pt_image_section_cache.h"
3074fe6c29SRuslan Bukin #include "pt_section.h"
3174fe6c29SRuslan Bukin 
3274fe6c29SRuslan Bukin #include "intel-pt.h"
3374fe6c29SRuslan Bukin 
3474fe6c29SRuslan Bukin #include <stdlib.h>
3574fe6c29SRuslan Bukin 
3674fe6c29SRuslan Bukin 
dupstr(const char * str)3774fe6c29SRuslan Bukin static char *dupstr(const char *str)
3874fe6c29SRuslan Bukin {
3974fe6c29SRuslan Bukin 	char *dup;
4074fe6c29SRuslan Bukin 	size_t len;
4174fe6c29SRuslan Bukin 
4274fe6c29SRuslan Bukin 	if (!str)
4374fe6c29SRuslan Bukin 		return NULL;
4474fe6c29SRuslan Bukin 
45*85f87cf4SRuslan Bukin 	/* Silently truncate the name if it gets too big. */
46*85f87cf4SRuslan Bukin 	len = strnlen(str, 4096ul);
47*85f87cf4SRuslan Bukin 
4874fe6c29SRuslan Bukin 	dup = malloc(len + 1);
4974fe6c29SRuslan Bukin 	if (!dup)
5074fe6c29SRuslan Bukin 		return NULL;
5174fe6c29SRuslan Bukin 
52*85f87cf4SRuslan Bukin 	dup[len] = 0;
53*85f87cf4SRuslan Bukin 
54*85f87cf4SRuslan Bukin 	return memcpy(dup, str, len);
5574fe6c29SRuslan Bukin }
5674fe6c29SRuslan Bukin 
pt_iscache_init(struct pt_image_section_cache * iscache,const char * name)5774fe6c29SRuslan Bukin int pt_iscache_init(struct pt_image_section_cache *iscache, const char *name)
5874fe6c29SRuslan Bukin {
5974fe6c29SRuslan Bukin 	if (!iscache)
6074fe6c29SRuslan Bukin 		return -pte_internal;
6174fe6c29SRuslan Bukin 
6274fe6c29SRuslan Bukin 	memset(iscache, 0, sizeof(*iscache));
6374fe6c29SRuslan Bukin 	iscache->limit = UINT64_MAX;
6474fe6c29SRuslan Bukin 	if (name) {
6574fe6c29SRuslan Bukin 		iscache->name = dupstr(name);
6674fe6c29SRuslan Bukin 		if (!iscache->name)
6774fe6c29SRuslan Bukin 			return -pte_nomem;
6874fe6c29SRuslan Bukin 	}
6974fe6c29SRuslan Bukin 
7074fe6c29SRuslan Bukin #if defined(FEATURE_THREADS)
7174fe6c29SRuslan Bukin 	{
7274fe6c29SRuslan Bukin 		int errcode;
7374fe6c29SRuslan Bukin 
7474fe6c29SRuslan Bukin 		errcode = mtx_init(&iscache->lock, mtx_plain);
7574fe6c29SRuslan Bukin 		if (errcode != thrd_success)
7674fe6c29SRuslan Bukin 			return -pte_bad_lock;
7774fe6c29SRuslan Bukin 	}
7874fe6c29SRuslan Bukin #endif /* defined(FEATURE_THREADS) */
7974fe6c29SRuslan Bukin 
8074fe6c29SRuslan Bukin 	return 0;
8174fe6c29SRuslan Bukin }
8274fe6c29SRuslan Bukin 
pt_iscache_fini(struct pt_image_section_cache * iscache)8374fe6c29SRuslan Bukin void pt_iscache_fini(struct pt_image_section_cache *iscache)
8474fe6c29SRuslan Bukin {
8574fe6c29SRuslan Bukin 	if (!iscache)
8674fe6c29SRuslan Bukin 		return;
8774fe6c29SRuslan Bukin 
8874fe6c29SRuslan Bukin 	(void) pt_iscache_clear(iscache);
8974fe6c29SRuslan Bukin 	free(iscache->name);
9074fe6c29SRuslan Bukin 
9174fe6c29SRuslan Bukin #if defined(FEATURE_THREADS)
9274fe6c29SRuslan Bukin 
9374fe6c29SRuslan Bukin 	mtx_destroy(&iscache->lock);
9474fe6c29SRuslan Bukin 
9574fe6c29SRuslan Bukin #endif /* defined(FEATURE_THREADS) */
9674fe6c29SRuslan Bukin }
9774fe6c29SRuslan Bukin 
pt_iscache_lock(struct pt_image_section_cache * iscache)9874fe6c29SRuslan Bukin static inline int pt_iscache_lock(struct pt_image_section_cache *iscache)
9974fe6c29SRuslan Bukin {
10074fe6c29SRuslan Bukin 	if (!iscache)
10174fe6c29SRuslan Bukin 		return -pte_internal;
10274fe6c29SRuslan Bukin 
10374fe6c29SRuslan Bukin #if defined(FEATURE_THREADS)
10474fe6c29SRuslan Bukin 	{
10574fe6c29SRuslan Bukin 		int errcode;
10674fe6c29SRuslan Bukin 
10774fe6c29SRuslan Bukin 		errcode = mtx_lock(&iscache->lock);
10874fe6c29SRuslan Bukin 		if (errcode != thrd_success)
10974fe6c29SRuslan Bukin 			return -pte_bad_lock;
11074fe6c29SRuslan Bukin 	}
11174fe6c29SRuslan Bukin #endif /* defined(FEATURE_THREADS) */
11274fe6c29SRuslan Bukin 
11374fe6c29SRuslan Bukin 	return 0;
11474fe6c29SRuslan Bukin }
11574fe6c29SRuslan Bukin 
pt_iscache_unlock(struct pt_image_section_cache * iscache)11674fe6c29SRuslan Bukin static inline int pt_iscache_unlock(struct pt_image_section_cache *iscache)
11774fe6c29SRuslan Bukin {
11874fe6c29SRuslan Bukin 	if (!iscache)
11974fe6c29SRuslan Bukin 		return -pte_internal;
12074fe6c29SRuslan Bukin 
12174fe6c29SRuslan Bukin #if defined(FEATURE_THREADS)
12274fe6c29SRuslan Bukin 	{
12374fe6c29SRuslan Bukin 		int errcode;
12474fe6c29SRuslan Bukin 
12574fe6c29SRuslan Bukin 		errcode = mtx_unlock(&iscache->lock);
12674fe6c29SRuslan Bukin 		if (errcode != thrd_success)
12774fe6c29SRuslan Bukin 			return -pte_bad_lock;
12874fe6c29SRuslan Bukin 	}
12974fe6c29SRuslan Bukin #endif /* defined(FEATURE_THREADS) */
13074fe6c29SRuslan Bukin 
13174fe6c29SRuslan Bukin 	return 0;
13274fe6c29SRuslan Bukin }
13374fe6c29SRuslan Bukin 
isid_from_index(uint16_t index)13474fe6c29SRuslan Bukin static inline int isid_from_index(uint16_t index)
13574fe6c29SRuslan Bukin {
13674fe6c29SRuslan Bukin 	return index + 1;
13774fe6c29SRuslan Bukin }
13874fe6c29SRuslan Bukin 
pt_iscache_expand(struct pt_image_section_cache * iscache)13974fe6c29SRuslan Bukin static int pt_iscache_expand(struct pt_image_section_cache *iscache)
14074fe6c29SRuslan Bukin {
14174fe6c29SRuslan Bukin 	struct pt_iscache_entry *entries;
14274fe6c29SRuslan Bukin 	uint16_t capacity, target;
14374fe6c29SRuslan Bukin 
14474fe6c29SRuslan Bukin 	if (!iscache)
14574fe6c29SRuslan Bukin 		return -pte_internal;
14674fe6c29SRuslan Bukin 
14774fe6c29SRuslan Bukin 	capacity = iscache->capacity;
14874fe6c29SRuslan Bukin 	target = capacity + 8;
14974fe6c29SRuslan Bukin 
15074fe6c29SRuslan Bukin 	/* Check for overflows. */
15174fe6c29SRuslan Bukin 	if (target < capacity)
15274fe6c29SRuslan Bukin 		return -pte_nomem;
15374fe6c29SRuslan Bukin 
15474fe6c29SRuslan Bukin 	entries = realloc(iscache->entries, target * sizeof(*entries));
15574fe6c29SRuslan Bukin 	if (!entries)
15674fe6c29SRuslan Bukin 		return -pte_nomem;
15774fe6c29SRuslan Bukin 
15874fe6c29SRuslan Bukin 	iscache->capacity = target;
15974fe6c29SRuslan Bukin 	iscache->entries = entries;
16074fe6c29SRuslan Bukin 	return 0;
16174fe6c29SRuslan Bukin }
16274fe6c29SRuslan Bukin 
pt_iscache_find_locked(struct pt_image_section_cache * iscache,const char * filename,uint64_t offset,uint64_t size,uint64_t laddr)16374fe6c29SRuslan Bukin static int pt_iscache_find_locked(struct pt_image_section_cache *iscache,
16474fe6c29SRuslan Bukin 				  const char *filename, uint64_t offset,
16574fe6c29SRuslan Bukin 				  uint64_t size, uint64_t laddr)
16674fe6c29SRuslan Bukin {
16774fe6c29SRuslan Bukin 	uint16_t idx, end;
16874fe6c29SRuslan Bukin 
16974fe6c29SRuslan Bukin 	if (!iscache || !filename)
17074fe6c29SRuslan Bukin 		return -pte_internal;
17174fe6c29SRuslan Bukin 
17274fe6c29SRuslan Bukin 	end = iscache->size;
17374fe6c29SRuslan Bukin 	for (idx = 0; idx < end; ++idx) {
17474fe6c29SRuslan Bukin 		const struct pt_iscache_entry *entry;
17574fe6c29SRuslan Bukin 		const struct pt_section *section;
17674fe6c29SRuslan Bukin 		const char *sec_filename;
17774fe6c29SRuslan Bukin 		uint64_t sec_offset, sec_size;
17874fe6c29SRuslan Bukin 
17974fe6c29SRuslan Bukin 		entry = &iscache->entries[idx];
18074fe6c29SRuslan Bukin 
18174fe6c29SRuslan Bukin 		/* We do not zero-initialize the array - a NULL check is
18274fe6c29SRuslan Bukin 		 * pointless.
18374fe6c29SRuslan Bukin 		 */
18474fe6c29SRuslan Bukin 		section = entry->section;
18574fe6c29SRuslan Bukin 		sec_filename = pt_section_filename(section);
18674fe6c29SRuslan Bukin 		sec_offset = pt_section_offset(section);
18774fe6c29SRuslan Bukin 		sec_size = pt_section_size(section);
18874fe6c29SRuslan Bukin 
18974fe6c29SRuslan Bukin 		if (entry->laddr != laddr)
19074fe6c29SRuslan Bukin 			continue;
19174fe6c29SRuslan Bukin 
19274fe6c29SRuslan Bukin 		if (sec_offset != offset)
19374fe6c29SRuslan Bukin 			continue;
19474fe6c29SRuslan Bukin 
19574fe6c29SRuslan Bukin 		if (sec_size != size)
19674fe6c29SRuslan Bukin 			continue;
19774fe6c29SRuslan Bukin 
19874fe6c29SRuslan Bukin 		/* We should not have a section without a filename. */
19974fe6c29SRuslan Bukin 		if (!sec_filename)
20074fe6c29SRuslan Bukin 			return -pte_internal;
20174fe6c29SRuslan Bukin 
20274fe6c29SRuslan Bukin 		if (strcmp(sec_filename, filename) != 0)
20374fe6c29SRuslan Bukin 			continue;
20474fe6c29SRuslan Bukin 
20574fe6c29SRuslan Bukin 		return isid_from_index(idx);
20674fe6c29SRuslan Bukin 	}
20774fe6c29SRuslan Bukin 
20874fe6c29SRuslan Bukin 	return 0;
20974fe6c29SRuslan Bukin }
21074fe6c29SRuslan Bukin 
pt_iscache_lru_free(struct pt_iscache_lru_entry * lru)21174fe6c29SRuslan Bukin static int pt_iscache_lru_free(struct pt_iscache_lru_entry *lru)
21274fe6c29SRuslan Bukin {
21374fe6c29SRuslan Bukin 	while (lru) {
21474fe6c29SRuslan Bukin 		struct pt_iscache_lru_entry *trash;
21574fe6c29SRuslan Bukin 		int errcode;
21674fe6c29SRuslan Bukin 
21774fe6c29SRuslan Bukin 		trash = lru;
21874fe6c29SRuslan Bukin 		lru = lru->next;
21974fe6c29SRuslan Bukin 
22074fe6c29SRuslan Bukin 		errcode = pt_section_unmap(trash->section);
22174fe6c29SRuslan Bukin 		if (errcode < 0)
22274fe6c29SRuslan Bukin 			return errcode;
22374fe6c29SRuslan Bukin 
22474fe6c29SRuslan Bukin 		free(trash);
22574fe6c29SRuslan Bukin 	}
22674fe6c29SRuslan Bukin 
22774fe6c29SRuslan Bukin 	return 0;
22874fe6c29SRuslan Bukin }
22974fe6c29SRuslan Bukin 
pt_iscache_lru_prune(struct pt_image_section_cache * iscache,struct pt_iscache_lru_entry ** tail)23074fe6c29SRuslan Bukin static int pt_iscache_lru_prune(struct pt_image_section_cache *iscache,
23174fe6c29SRuslan Bukin 				struct pt_iscache_lru_entry **tail)
23274fe6c29SRuslan Bukin {
23374fe6c29SRuslan Bukin 	struct pt_iscache_lru_entry *lru, **pnext;
23474fe6c29SRuslan Bukin 	uint64_t limit, used;
23574fe6c29SRuslan Bukin 
23674fe6c29SRuslan Bukin 	if (!iscache || !tail)
23774fe6c29SRuslan Bukin 		return -pte_internal;
23874fe6c29SRuslan Bukin 
23974fe6c29SRuslan Bukin 	limit = iscache->limit;
24074fe6c29SRuslan Bukin 	used = 0ull;
24174fe6c29SRuslan Bukin 
24274fe6c29SRuslan Bukin 	pnext = &iscache->lru;
24374fe6c29SRuslan Bukin 	for (lru = *pnext; lru; pnext = &lru->next, lru = *pnext) {
24474fe6c29SRuslan Bukin 
24574fe6c29SRuslan Bukin 		used += lru->size;
24674fe6c29SRuslan Bukin 		if (used <= limit)
24774fe6c29SRuslan Bukin 			continue;
24874fe6c29SRuslan Bukin 
24974fe6c29SRuslan Bukin 		/* The cache got too big; prune it starting from @lru. */
25074fe6c29SRuslan Bukin 		iscache->used = used - lru->size;
25174fe6c29SRuslan Bukin 		*pnext = NULL;
25274fe6c29SRuslan Bukin 		*tail = lru;
25374fe6c29SRuslan Bukin 
25474fe6c29SRuslan Bukin 		return 0;
25574fe6c29SRuslan Bukin 	}
25674fe6c29SRuslan Bukin 
25774fe6c29SRuslan Bukin 	/* We shouldn't prune the cache unnecessarily. */
25874fe6c29SRuslan Bukin 	return -pte_internal;
25974fe6c29SRuslan Bukin }
26074fe6c29SRuslan Bukin 
26174fe6c29SRuslan Bukin /* Add @section to the front of @iscache->lru.
26274fe6c29SRuslan Bukin  *
26374fe6c29SRuslan Bukin  * Returns a positive integer if we need to prune the cache.
26474fe6c29SRuslan Bukin  * Returns zero if we don't need to prune the cache.
26574fe6c29SRuslan Bukin  * Returns a negative pt_error_code otherwise.
26674fe6c29SRuslan Bukin  */
pt_isache_lru_new(struct pt_image_section_cache * iscache,struct pt_section * section)26774fe6c29SRuslan Bukin static int pt_isache_lru_new(struct pt_image_section_cache *iscache,
26874fe6c29SRuslan Bukin 			     struct pt_section *section)
26974fe6c29SRuslan Bukin {
27074fe6c29SRuslan Bukin 	struct pt_iscache_lru_entry *lru;
27174fe6c29SRuslan Bukin 	uint64_t memsize, used, total, limit;
27274fe6c29SRuslan Bukin 	int errcode;
27374fe6c29SRuslan Bukin 
27474fe6c29SRuslan Bukin 	if (!iscache)
27574fe6c29SRuslan Bukin 		return -pte_internal;
27674fe6c29SRuslan Bukin 
27774fe6c29SRuslan Bukin 	errcode = pt_section_memsize(section, &memsize);
27874fe6c29SRuslan Bukin 	if (errcode < 0)
27974fe6c29SRuslan Bukin 		return errcode;
28074fe6c29SRuslan Bukin 
28174fe6c29SRuslan Bukin 	/* Don't try to add the section if it is too big.  We'd prune it again
28274fe6c29SRuslan Bukin 	 * together with all other sections in our cache.
28374fe6c29SRuslan Bukin 	 */
28474fe6c29SRuslan Bukin 	limit = iscache->limit;
28574fe6c29SRuslan Bukin 	if (limit < memsize)
28674fe6c29SRuslan Bukin 		return 0;
28774fe6c29SRuslan Bukin 
28874fe6c29SRuslan Bukin 	errcode = pt_section_map_share(section);
28974fe6c29SRuslan Bukin 	if (errcode < 0)
29074fe6c29SRuslan Bukin 		return errcode;
29174fe6c29SRuslan Bukin 
29274fe6c29SRuslan Bukin 	lru = malloc(sizeof(*lru));
29374fe6c29SRuslan Bukin 	if (!lru) {
29474fe6c29SRuslan Bukin 		(void) pt_section_unmap(section);
29574fe6c29SRuslan Bukin 		return -pte_nomem;
29674fe6c29SRuslan Bukin 	}
29774fe6c29SRuslan Bukin 
29874fe6c29SRuslan Bukin 	lru->section = section;
29974fe6c29SRuslan Bukin 	lru->size = memsize;
30074fe6c29SRuslan Bukin 
30174fe6c29SRuslan Bukin 	lru->next = iscache->lru;
30274fe6c29SRuslan Bukin 	iscache->lru = lru;
30374fe6c29SRuslan Bukin 
30474fe6c29SRuslan Bukin 	used = iscache->used;
30574fe6c29SRuslan Bukin 	total = used + memsize;
30674fe6c29SRuslan Bukin 	if (total < used || total < memsize)
30774fe6c29SRuslan Bukin 		return -pte_overflow;
30874fe6c29SRuslan Bukin 
30974fe6c29SRuslan Bukin 	iscache->used = total;
31074fe6c29SRuslan Bukin 
31174fe6c29SRuslan Bukin 	return (limit < total) ? 1 : 0;
31274fe6c29SRuslan Bukin }
31374fe6c29SRuslan Bukin 
31474fe6c29SRuslan Bukin /* Add or move @section to the front of @iscache->lru.
31574fe6c29SRuslan Bukin  *
31674fe6c29SRuslan Bukin  * Returns a positive integer if we need to prune the cache.
31774fe6c29SRuslan Bukin  * Returns zero if we don't need to prune the cache.
31874fe6c29SRuslan Bukin  * Returns a negative pt_error_code otherwise.
31974fe6c29SRuslan Bukin  */
pt_iscache_lru_add(struct pt_image_section_cache * iscache,struct pt_section * section)32074fe6c29SRuslan Bukin static int pt_iscache_lru_add(struct pt_image_section_cache *iscache,
32174fe6c29SRuslan Bukin 			      struct pt_section *section)
32274fe6c29SRuslan Bukin {
32374fe6c29SRuslan Bukin 	struct pt_iscache_lru_entry *lru, **pnext;
32474fe6c29SRuslan Bukin 
32574fe6c29SRuslan Bukin 	if (!iscache)
32674fe6c29SRuslan Bukin 		return -pte_internal;
32774fe6c29SRuslan Bukin 
32874fe6c29SRuslan Bukin 	pnext = &iscache->lru;
32974fe6c29SRuslan Bukin 	for (lru = *pnext; lru; pnext = &lru->next, lru = *pnext) {
33074fe6c29SRuslan Bukin 
33174fe6c29SRuslan Bukin 		if (lru->section != section)
33274fe6c29SRuslan Bukin 			continue;
33374fe6c29SRuslan Bukin 
33474fe6c29SRuslan Bukin 		/* We found it in the cache.  Move it to the front. */
33574fe6c29SRuslan Bukin 		*pnext = lru->next;
33674fe6c29SRuslan Bukin 		lru->next = iscache->lru;
33774fe6c29SRuslan Bukin 		iscache->lru = lru;
33874fe6c29SRuslan Bukin 
33974fe6c29SRuslan Bukin 		return 0;
34074fe6c29SRuslan Bukin 	}
34174fe6c29SRuslan Bukin 
34274fe6c29SRuslan Bukin 	/* We didn't find it in the cache.  Add it. */
34374fe6c29SRuslan Bukin 	return pt_isache_lru_new(iscache, section);
34474fe6c29SRuslan Bukin }
34574fe6c29SRuslan Bukin 
34674fe6c29SRuslan Bukin 
34774fe6c29SRuslan Bukin /* Remove @section from @iscache->lru.
34874fe6c29SRuslan Bukin  *
34974fe6c29SRuslan Bukin  * Returns zero on success, a negative pt_error_code otherwise.
35074fe6c29SRuslan Bukin  */
pt_iscache_lru_remove(struct pt_image_section_cache * iscache,const struct pt_section * section)35174fe6c29SRuslan Bukin static int pt_iscache_lru_remove(struct pt_image_section_cache *iscache,
35274fe6c29SRuslan Bukin 				 const struct pt_section *section)
35374fe6c29SRuslan Bukin {
35474fe6c29SRuslan Bukin 	struct pt_iscache_lru_entry *lru, **pnext;
35574fe6c29SRuslan Bukin 
35674fe6c29SRuslan Bukin 	if (!iscache)
35774fe6c29SRuslan Bukin 		return -pte_internal;
35874fe6c29SRuslan Bukin 
35974fe6c29SRuslan Bukin 	pnext = &iscache->lru;
36074fe6c29SRuslan Bukin 	for (lru = *pnext; lru; pnext = &lru->next, lru = *pnext) {
36174fe6c29SRuslan Bukin 
36274fe6c29SRuslan Bukin 		if (lru->section != section)
36374fe6c29SRuslan Bukin 			continue;
36474fe6c29SRuslan Bukin 
36574fe6c29SRuslan Bukin 		/* We found it in the cache.  Remove it. */
36674fe6c29SRuslan Bukin 		*pnext = lru->next;
36774fe6c29SRuslan Bukin 		lru->next = NULL;
36874fe6c29SRuslan Bukin 		break;
36974fe6c29SRuslan Bukin 	}
37074fe6c29SRuslan Bukin 
37174fe6c29SRuslan Bukin 	return pt_iscache_lru_free(lru);
37274fe6c29SRuslan Bukin }
37374fe6c29SRuslan Bukin 
37474fe6c29SRuslan Bukin 
37574fe6c29SRuslan Bukin /* Add or move @section to the front of @iscache->lru and update its size.
37674fe6c29SRuslan Bukin  *
37774fe6c29SRuslan Bukin  * Returns a positive integer if we need to prune the cache.
37874fe6c29SRuslan Bukin  * Returns zero if we don't need to prune the cache.
37974fe6c29SRuslan Bukin  * Returns a negative pt_error_code otherwise.
38074fe6c29SRuslan Bukin  */
pt_iscache_lru_resize(struct pt_image_section_cache * iscache,struct pt_section * section,uint64_t memsize)38174fe6c29SRuslan Bukin static int pt_iscache_lru_resize(struct pt_image_section_cache *iscache,
38274fe6c29SRuslan Bukin 				 struct pt_section *section, uint64_t memsize)
38374fe6c29SRuslan Bukin {
38474fe6c29SRuslan Bukin 	struct pt_iscache_lru_entry *lru;
38574fe6c29SRuslan Bukin 	uint64_t oldsize, used;
38674fe6c29SRuslan Bukin 	int status;
38774fe6c29SRuslan Bukin 
38874fe6c29SRuslan Bukin 	if (!iscache)
38974fe6c29SRuslan Bukin 		return -pte_internal;
39074fe6c29SRuslan Bukin 
39174fe6c29SRuslan Bukin 	status = pt_iscache_lru_add(iscache, section);
39274fe6c29SRuslan Bukin 	if (status < 0)
39374fe6c29SRuslan Bukin 		return status;
39474fe6c29SRuslan Bukin 
39574fe6c29SRuslan Bukin 	lru = iscache->lru;
39674fe6c29SRuslan Bukin 	if (!lru) {
39774fe6c29SRuslan Bukin 		if (status)
39874fe6c29SRuslan Bukin 			return -pte_internal;
39974fe6c29SRuslan Bukin 		return 0;
40074fe6c29SRuslan Bukin 	}
40174fe6c29SRuslan Bukin 
40274fe6c29SRuslan Bukin 	/* If @section is cached, it must be first.
40374fe6c29SRuslan Bukin 	 *
40474fe6c29SRuslan Bukin 	 * We may choose not to cache it, though, e.g. if it is too big.
40574fe6c29SRuslan Bukin 	 */
40674fe6c29SRuslan Bukin 	if (lru->section != section) {
40774fe6c29SRuslan Bukin 		if (iscache->limit < memsize)
40874fe6c29SRuslan Bukin 			return 0;
40974fe6c29SRuslan Bukin 
41074fe6c29SRuslan Bukin 		return -pte_internal;
41174fe6c29SRuslan Bukin 	}
41274fe6c29SRuslan Bukin 
41374fe6c29SRuslan Bukin 	oldsize = lru->size;
41474fe6c29SRuslan Bukin 	lru->size = memsize;
41574fe6c29SRuslan Bukin 
41674fe6c29SRuslan Bukin 	/* If we need to prune anyway, we're done. */
41774fe6c29SRuslan Bukin 	if (status)
41874fe6c29SRuslan Bukin 		return status;
41974fe6c29SRuslan Bukin 
42074fe6c29SRuslan Bukin 	used = iscache->used;
42174fe6c29SRuslan Bukin 	used -= oldsize;
42274fe6c29SRuslan Bukin 	used += memsize;
42374fe6c29SRuslan Bukin 
42474fe6c29SRuslan Bukin 	iscache->used = used;
42574fe6c29SRuslan Bukin 
42674fe6c29SRuslan Bukin 	return (iscache->limit < used) ? 1 : 0;
42774fe6c29SRuslan Bukin }
42874fe6c29SRuslan Bukin 
42974fe6c29SRuslan Bukin /* Clear @iscache->lru.
43074fe6c29SRuslan Bukin  *
43174fe6c29SRuslan Bukin  * Unlike other iscache_lru functions, the caller does not lock @iscache.
43274fe6c29SRuslan Bukin  *
43374fe6c29SRuslan Bukin  * Return zero on success, a negative pt_error_code otherwise.
43474fe6c29SRuslan Bukin  */
pt_iscache_lru_clear(struct pt_image_section_cache * iscache)43574fe6c29SRuslan Bukin static int pt_iscache_lru_clear(struct pt_image_section_cache *iscache)
43674fe6c29SRuslan Bukin {
43774fe6c29SRuslan Bukin 	struct pt_iscache_lru_entry *lru;
43874fe6c29SRuslan Bukin 	int errcode;
43974fe6c29SRuslan Bukin 
44074fe6c29SRuslan Bukin 	errcode = pt_iscache_lock(iscache);
44174fe6c29SRuslan Bukin 	if (errcode < 0)
44274fe6c29SRuslan Bukin 		return errcode;
44374fe6c29SRuslan Bukin 
44474fe6c29SRuslan Bukin 	lru = iscache->lru;
44574fe6c29SRuslan Bukin 	iscache->lru = NULL;
44674fe6c29SRuslan Bukin 	iscache->used = 0ull;
44774fe6c29SRuslan Bukin 
44874fe6c29SRuslan Bukin 	errcode = pt_iscache_unlock(iscache);
44974fe6c29SRuslan Bukin 	if (errcode < 0)
45074fe6c29SRuslan Bukin 		return errcode;
45174fe6c29SRuslan Bukin 
45274fe6c29SRuslan Bukin 	return pt_iscache_lru_free(lru);
45374fe6c29SRuslan Bukin }
45474fe6c29SRuslan Bukin 
45574fe6c29SRuslan Bukin /* Search @iscache for a partial or exact match of @section loaded at @laddr and
45674fe6c29SRuslan Bukin  * return the corresponding index or @iscache->size if no match is found.
45774fe6c29SRuslan Bukin  *
45874fe6c29SRuslan Bukin  * The caller must lock @iscache.
45974fe6c29SRuslan Bukin  *
46074fe6c29SRuslan Bukin  * Returns a non-zero index on success, a negative pt_error_code otherwise.
46174fe6c29SRuslan Bukin  */
46274fe6c29SRuslan 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)46374fe6c29SRuslan Bukin pt_iscache_find_section_locked(const struct pt_image_section_cache *iscache,
46474fe6c29SRuslan Bukin 			       const char *filename, uint64_t offset,
46574fe6c29SRuslan Bukin 			       uint64_t size, uint64_t laddr)
46674fe6c29SRuslan Bukin {
46774fe6c29SRuslan Bukin 	const struct pt_section *section;
46874fe6c29SRuslan Bukin 	uint16_t idx, end;
46974fe6c29SRuslan Bukin 	int match;
47074fe6c29SRuslan Bukin 
47174fe6c29SRuslan Bukin 	if (!iscache || !filename)
47274fe6c29SRuslan Bukin 		return -pte_internal;
47374fe6c29SRuslan Bukin 
47474fe6c29SRuslan Bukin 	section = NULL;
47574fe6c29SRuslan Bukin 	match = end = iscache->size;
47674fe6c29SRuslan Bukin 	for (idx = 0; idx < end; ++idx) {
47774fe6c29SRuslan Bukin 		const struct pt_iscache_entry *entry;
47874fe6c29SRuslan Bukin 		const struct pt_section *sec;
47974fe6c29SRuslan Bukin 
48074fe6c29SRuslan Bukin 		entry = &iscache->entries[idx];
48174fe6c29SRuslan Bukin 
48274fe6c29SRuslan Bukin 		/* We do not zero-initialize the array - a NULL check is
48374fe6c29SRuslan Bukin 		 * pointless.
48474fe6c29SRuslan Bukin 		 */
48574fe6c29SRuslan Bukin 		sec = entry->section;
48674fe6c29SRuslan Bukin 
48774fe6c29SRuslan Bukin 		/* Avoid redundant match checks. */
48874fe6c29SRuslan Bukin 		if (sec != section) {
48974fe6c29SRuslan Bukin 			const char *sec_filename;
49074fe6c29SRuslan Bukin 
49174fe6c29SRuslan Bukin 			/* We don't have duplicates.  Skip the check. */
49274fe6c29SRuslan Bukin 			if (section)
49374fe6c29SRuslan Bukin 				continue;
49474fe6c29SRuslan Bukin 
49574fe6c29SRuslan Bukin 			if (offset != pt_section_offset(sec))
49674fe6c29SRuslan Bukin 				continue;
49774fe6c29SRuslan Bukin 
49874fe6c29SRuslan Bukin 			if (size != pt_section_size(sec))
49974fe6c29SRuslan Bukin 				continue;
50074fe6c29SRuslan Bukin 
50174fe6c29SRuslan Bukin 			sec_filename = pt_section_filename(sec);
50274fe6c29SRuslan Bukin 			if (!sec_filename)
50374fe6c29SRuslan Bukin 				return -pte_internal;
50474fe6c29SRuslan Bukin 
50574fe6c29SRuslan Bukin 			if (strcmp(filename, sec_filename) != 0)
50674fe6c29SRuslan Bukin 				continue;
50774fe6c29SRuslan Bukin 
50874fe6c29SRuslan Bukin 			/* Use the cached section instead. */
50974fe6c29SRuslan Bukin 			section = sec;
51074fe6c29SRuslan Bukin 			match = idx;
51174fe6c29SRuslan Bukin 		}
51274fe6c29SRuslan Bukin 
51374fe6c29SRuslan Bukin 		/* If we didn't continue, @section == @sec and we have a match.
51474fe6c29SRuslan Bukin 		 *
51574fe6c29SRuslan Bukin 		 * If we also find a matching load address, we're done.
51674fe6c29SRuslan Bukin 		 */
51774fe6c29SRuslan Bukin 		if (laddr == entry->laddr)
51874fe6c29SRuslan Bukin 			return idx;
51974fe6c29SRuslan Bukin 	}
52074fe6c29SRuslan Bukin 
52174fe6c29SRuslan Bukin 	return match;
52274fe6c29SRuslan Bukin }
52374fe6c29SRuslan Bukin 
pt_iscache_add(struct pt_image_section_cache * iscache,struct pt_section * section,uint64_t laddr)52474fe6c29SRuslan Bukin int pt_iscache_add(struct pt_image_section_cache *iscache,
52574fe6c29SRuslan Bukin 		   struct pt_section *section, uint64_t laddr)
52674fe6c29SRuslan Bukin {
52774fe6c29SRuslan Bukin 	const char *filename;
52874fe6c29SRuslan Bukin 	uint64_t offset, size;
52974fe6c29SRuslan Bukin 	uint16_t idx;
53074fe6c29SRuslan Bukin 	int errcode;
53174fe6c29SRuslan Bukin 
53274fe6c29SRuslan Bukin 	if (!iscache || !section)
53374fe6c29SRuslan Bukin 		return -pte_internal;
53474fe6c29SRuslan Bukin 
53574fe6c29SRuslan Bukin 	/* We must have a filename for @section. */
53674fe6c29SRuslan Bukin 	filename = pt_section_filename(section);
53774fe6c29SRuslan Bukin 	if (!filename)
53874fe6c29SRuslan Bukin 		return -pte_internal;
53974fe6c29SRuslan Bukin 
54074fe6c29SRuslan Bukin 	offset = pt_section_offset(section);
54174fe6c29SRuslan Bukin 	size = pt_section_size(section);
54274fe6c29SRuslan Bukin 
54374fe6c29SRuslan Bukin 	/* Adding a section is slightly complicated by a potential deadlock
54474fe6c29SRuslan Bukin 	 * scenario:
54574fe6c29SRuslan Bukin 	 *
54674fe6c29SRuslan Bukin 	 *   - in order to add a section, we need to attach to it, which
54774fe6c29SRuslan Bukin 	 *     requires taking the section's attach lock.
54874fe6c29SRuslan Bukin 	 *
54974fe6c29SRuslan Bukin 	 *   - if we are already attached to it, we may receive on-map
55074fe6c29SRuslan Bukin 	 *     notifications, which will be sent while holding the attach lock
55174fe6c29SRuslan Bukin 	 *     and require taking the iscache lock.
55274fe6c29SRuslan Bukin 	 *
55374fe6c29SRuslan Bukin 	 * Hence we can't attach to a section while holding the iscache lock.
55474fe6c29SRuslan Bukin 	 *
55574fe6c29SRuslan Bukin 	 *
55674fe6c29SRuslan Bukin 	 * We therefore attach to @section first and then lock @iscache.
55774fe6c29SRuslan Bukin 	 *
55874fe6c29SRuslan Bukin 	 * This opens a small window where an existing @section may be removed
55974fe6c29SRuslan Bukin 	 * from @iscache and replaced by a new matching section.  We would want
56074fe6c29SRuslan Bukin 	 * to share that new section rather than adding a duplicate @section.
56174fe6c29SRuslan Bukin 	 *
56274fe6c29SRuslan Bukin 	 * After locking @iscache, we therefore check for existing matching
56374fe6c29SRuslan Bukin 	 * sections and, if one is found, update @section.  This involves
56474fe6c29SRuslan Bukin 	 * detaching from @section and attaching to the existing section.
56574fe6c29SRuslan Bukin 	 *
56674fe6c29SRuslan Bukin 	 * And for this, we will have to temporarily unlock @iscache again.
56774fe6c29SRuslan Bukin 	 */
56874fe6c29SRuslan Bukin 	errcode = pt_section_get(section);
56974fe6c29SRuslan Bukin 	if (errcode < 0)
57074fe6c29SRuslan Bukin 		return errcode;
57174fe6c29SRuslan Bukin 
57274fe6c29SRuslan Bukin 	errcode = pt_section_attach(section, iscache);
57374fe6c29SRuslan Bukin 	if (errcode < 0)
57474fe6c29SRuslan Bukin 		goto out_put;
57574fe6c29SRuslan Bukin 
57674fe6c29SRuslan Bukin 	errcode = pt_iscache_lock(iscache);
57774fe6c29SRuslan Bukin 	if (errcode < 0)
57874fe6c29SRuslan Bukin 		goto out_detach;
57974fe6c29SRuslan Bukin 
58074fe6c29SRuslan Bukin 	/* We may need to repeat this step.
58174fe6c29SRuslan Bukin 	 *
58274fe6c29SRuslan Bukin 	 * Typically we don't and this takes only a single iteration.  One
58374fe6c29SRuslan Bukin 	 * scenario where we do repeat this is when adding a section with an
58474fe6c29SRuslan Bukin 	 * out-of-bounds size.
58574fe6c29SRuslan Bukin 	 *
58674fe6c29SRuslan Bukin 	 * We will not find a matching section in pt_iscache_add_file() so we
58774fe6c29SRuslan Bukin 	 * create a new section.  This will have its size reduced to match the
58874fe6c29SRuslan Bukin 	 * actual file size.
58974fe6c29SRuslan Bukin 	 *
59074fe6c29SRuslan Bukin 	 * For this reduced size, we may now find an existing section, and we
59174fe6c29SRuslan Bukin 	 * will take another trip in the below loop.
59274fe6c29SRuslan Bukin 	 */
59374fe6c29SRuslan Bukin 	for (;;) {
59474fe6c29SRuslan Bukin 		const struct pt_iscache_entry *entry;
59574fe6c29SRuslan Bukin 		struct pt_section *sec;
59674fe6c29SRuslan Bukin 		int match;
59774fe6c29SRuslan Bukin 
59874fe6c29SRuslan Bukin 		/* Find an existing section matching @section that we'd share
59974fe6c29SRuslan Bukin 		 * rather than adding @section.
60074fe6c29SRuslan Bukin 		 */
60174fe6c29SRuslan Bukin 		match = pt_iscache_find_section_locked(iscache, filename,
60274fe6c29SRuslan Bukin 						       offset, size, laddr);
60374fe6c29SRuslan Bukin 		if (match < 0) {
60474fe6c29SRuslan Bukin 			errcode = match;
60574fe6c29SRuslan Bukin 			goto out_unlock_detach;
60674fe6c29SRuslan Bukin 		}
60774fe6c29SRuslan Bukin 
60874fe6c29SRuslan Bukin 		/* We're done if we have not found a matching section. */
60974fe6c29SRuslan Bukin 		if (iscache->size <= match)
61074fe6c29SRuslan Bukin 			break;
61174fe6c29SRuslan Bukin 
61274fe6c29SRuslan Bukin 		entry = &iscache->entries[match];
61374fe6c29SRuslan Bukin 
61474fe6c29SRuslan Bukin 		/* We're also done if we found the same section again.
61574fe6c29SRuslan Bukin 		 *
61674fe6c29SRuslan Bukin 		 * We further check for a perfect match.  In that case, we don't
61774fe6c29SRuslan Bukin 		 * need to insert anything, at all.
61874fe6c29SRuslan Bukin 		 */
61974fe6c29SRuslan Bukin 		sec = entry->section;
62074fe6c29SRuslan Bukin 		if (sec == section) {
62174fe6c29SRuslan Bukin 			if (entry->laddr == laddr) {
62274fe6c29SRuslan Bukin 				errcode = pt_iscache_unlock(iscache);
62374fe6c29SRuslan Bukin 				if (errcode < 0)
62474fe6c29SRuslan Bukin 					goto out_detach;
62574fe6c29SRuslan Bukin 
62674fe6c29SRuslan Bukin 				errcode = pt_section_detach(section, iscache);
62774fe6c29SRuslan Bukin 				if (errcode < 0)
62874fe6c29SRuslan Bukin 					goto out_lru;
62974fe6c29SRuslan Bukin 
63074fe6c29SRuslan Bukin 				errcode = pt_section_put(section);
63174fe6c29SRuslan Bukin 				if (errcode < 0)
63274fe6c29SRuslan Bukin 					return errcode;
63374fe6c29SRuslan Bukin 
63474fe6c29SRuslan Bukin 				return isid_from_index((uint16_t) match);
63574fe6c29SRuslan Bukin 			}
63674fe6c29SRuslan Bukin 
63774fe6c29SRuslan Bukin 			break;
63874fe6c29SRuslan Bukin 		}
63974fe6c29SRuslan Bukin 
64074fe6c29SRuslan Bukin 		/* We update @section to share the existing @sec.
64174fe6c29SRuslan Bukin 		 *
64274fe6c29SRuslan Bukin 		 * This requires detaching from @section, which, in turn,
64374fe6c29SRuslan Bukin 		 * requires temporarily unlocking @iscache.
64474fe6c29SRuslan Bukin 		 *
64574fe6c29SRuslan Bukin 		 * We further need to remove @section from @iscache->lru.
64674fe6c29SRuslan Bukin 		 */
64774fe6c29SRuslan Bukin 		errcode = pt_section_get(sec);
64874fe6c29SRuslan Bukin 		if (errcode < 0)
64974fe6c29SRuslan Bukin 			goto out_unlock_detach;
65074fe6c29SRuslan Bukin 
65174fe6c29SRuslan Bukin 		errcode = pt_iscache_unlock(iscache);
65274fe6c29SRuslan Bukin 		if (errcode < 0) {
65374fe6c29SRuslan Bukin 			(void) pt_section_put(sec);
65474fe6c29SRuslan Bukin 			goto out_detach;
65574fe6c29SRuslan Bukin 		}
65674fe6c29SRuslan Bukin 
65774fe6c29SRuslan Bukin 		errcode = pt_section_detach(section, iscache);
65874fe6c29SRuslan Bukin 		if (errcode < 0) {
65974fe6c29SRuslan Bukin 			(void) pt_section_put(sec);
66074fe6c29SRuslan Bukin 			goto out_lru;
66174fe6c29SRuslan Bukin 		}
66274fe6c29SRuslan Bukin 
66374fe6c29SRuslan Bukin 		errcode = pt_section_attach(sec, iscache);
66474fe6c29SRuslan Bukin 		if (errcode < 0) {
66574fe6c29SRuslan Bukin 			(void) pt_section_put(sec);
66674fe6c29SRuslan Bukin 			goto out_lru;
66774fe6c29SRuslan Bukin 		}
66874fe6c29SRuslan Bukin 
66974fe6c29SRuslan Bukin 		errcode = pt_iscache_lock(iscache);
67074fe6c29SRuslan Bukin 		if (errcode < 0) {
67174fe6c29SRuslan Bukin 			(void) pt_section_put(section);
67274fe6c29SRuslan Bukin 			/* Complete the swap for cleanup. */
67374fe6c29SRuslan Bukin 			section = sec;
67474fe6c29SRuslan Bukin 			goto out_detach;
67574fe6c29SRuslan Bukin 		}
67674fe6c29SRuslan Bukin 
67774fe6c29SRuslan Bukin 		/* We may have received on-map notifications for @section and we
67874fe6c29SRuslan Bukin 		 * may have added @section to @iscache->lru.
67974fe6c29SRuslan Bukin 		 *
68074fe6c29SRuslan Bukin 		 * Since we're still holding a reference to it, no harm has been
68174fe6c29SRuslan Bukin 		 * done.  But we need to remove it before we drop our reference.
68274fe6c29SRuslan Bukin 		 */
68374fe6c29SRuslan Bukin 		errcode = pt_iscache_lru_remove(iscache, section);
68474fe6c29SRuslan Bukin 		if (errcode < 0) {
68574fe6c29SRuslan Bukin 			(void) pt_section_put(section);
68674fe6c29SRuslan Bukin 			/* Complete the swap for cleanup. */
68774fe6c29SRuslan Bukin 			section = sec;
68874fe6c29SRuslan Bukin 			goto out_unlock_detach;
68974fe6c29SRuslan Bukin 		}
69074fe6c29SRuslan Bukin 
69174fe6c29SRuslan Bukin 		/* Drop the reference to @section. */
69274fe6c29SRuslan Bukin 		errcode = pt_section_put(section);
69374fe6c29SRuslan Bukin 		if (errcode < 0) {
69474fe6c29SRuslan Bukin 			/* Complete the swap for cleanup. */
69574fe6c29SRuslan Bukin 			section = sec;
69674fe6c29SRuslan Bukin 			goto out_unlock_detach;
69774fe6c29SRuslan Bukin 		}
69874fe6c29SRuslan Bukin 
69974fe6c29SRuslan Bukin 		/* Swap sections.
70074fe6c29SRuslan Bukin 		 *
70174fe6c29SRuslan Bukin 		 * We will try again in the next iteration.
70274fe6c29SRuslan Bukin 		 */
70374fe6c29SRuslan Bukin 		section = sec;
70474fe6c29SRuslan Bukin 	}
70574fe6c29SRuslan Bukin 
70674fe6c29SRuslan Bukin 	/* Expand the cache, if necessary. */
70774fe6c29SRuslan Bukin 	if (iscache->capacity <= iscache->size) {
70874fe6c29SRuslan Bukin 		/* We must never exceed the capacity. */
70974fe6c29SRuslan Bukin 		if (iscache->capacity < iscache->size) {
71074fe6c29SRuslan Bukin 			errcode = -pte_internal;
71174fe6c29SRuslan Bukin 			goto out_unlock_detach;
71274fe6c29SRuslan Bukin 		}
71374fe6c29SRuslan Bukin 
71474fe6c29SRuslan Bukin 		errcode = pt_iscache_expand(iscache);
71574fe6c29SRuslan Bukin 		if (errcode < 0)
71674fe6c29SRuslan Bukin 			goto out_unlock_detach;
71774fe6c29SRuslan Bukin 
71874fe6c29SRuslan Bukin 		/* Make sure it is big enough, now. */
71974fe6c29SRuslan Bukin 		if (iscache->capacity <= iscache->size) {
72074fe6c29SRuslan Bukin 			errcode = -pte_internal;
72174fe6c29SRuslan Bukin 			goto out_unlock_detach;
72274fe6c29SRuslan Bukin 		}
72374fe6c29SRuslan Bukin 	}
72474fe6c29SRuslan Bukin 
72574fe6c29SRuslan Bukin 	/* Insert a new entry for @section at @laddr.
72674fe6c29SRuslan Bukin 	 *
72774fe6c29SRuslan Bukin 	 * This hands both attach and reference over to @iscache.  We will
72874fe6c29SRuslan Bukin 	 * detach and drop the reference again when the entry is removed.
72974fe6c29SRuslan Bukin 	 */
73074fe6c29SRuslan Bukin 	idx = iscache->size++;
73174fe6c29SRuslan Bukin 
73274fe6c29SRuslan Bukin 	iscache->entries[idx].section = section;
73374fe6c29SRuslan Bukin 	iscache->entries[idx].laddr = laddr;
73474fe6c29SRuslan Bukin 
73574fe6c29SRuslan Bukin 	errcode = pt_iscache_unlock(iscache);
73674fe6c29SRuslan Bukin 	if (errcode < 0)
73774fe6c29SRuslan Bukin 		return errcode;
73874fe6c29SRuslan Bukin 
73974fe6c29SRuslan Bukin 	return isid_from_index(idx);
74074fe6c29SRuslan Bukin 
74174fe6c29SRuslan Bukin  out_unlock_detach:
74274fe6c29SRuslan Bukin 	(void) pt_iscache_unlock(iscache);
74374fe6c29SRuslan Bukin 
74474fe6c29SRuslan Bukin  out_detach:
74574fe6c29SRuslan Bukin 	(void) pt_section_detach(section, iscache);
74674fe6c29SRuslan Bukin 
74774fe6c29SRuslan Bukin  out_lru:
74874fe6c29SRuslan Bukin 	(void) pt_iscache_lru_clear(iscache);
74974fe6c29SRuslan Bukin 
75074fe6c29SRuslan Bukin  out_put:
75174fe6c29SRuslan Bukin 	(void) pt_section_put(section);
75274fe6c29SRuslan Bukin 
75374fe6c29SRuslan Bukin 	return errcode;
75474fe6c29SRuslan Bukin }
75574fe6c29SRuslan Bukin 
pt_iscache_find(struct pt_image_section_cache * iscache,const char * filename,uint64_t offset,uint64_t size,uint64_t laddr)75674fe6c29SRuslan Bukin int pt_iscache_find(struct pt_image_section_cache *iscache,
75774fe6c29SRuslan Bukin 		    const char *filename, uint64_t offset, uint64_t size,
75874fe6c29SRuslan Bukin 		    uint64_t laddr)
75974fe6c29SRuslan Bukin {
76074fe6c29SRuslan Bukin 	int errcode, isid;
76174fe6c29SRuslan Bukin 
76274fe6c29SRuslan Bukin 	errcode = pt_iscache_lock(iscache);
76374fe6c29SRuslan Bukin 	if (errcode < 0)
76474fe6c29SRuslan Bukin 		return errcode;
76574fe6c29SRuslan Bukin 
76674fe6c29SRuslan Bukin 	isid = pt_iscache_find_locked(iscache, filename, offset, size, laddr);
76774fe6c29SRuslan Bukin 
76874fe6c29SRuslan Bukin 	errcode = pt_iscache_unlock(iscache);
76974fe6c29SRuslan Bukin 	if (errcode < 0)
77074fe6c29SRuslan Bukin 		return errcode;
77174fe6c29SRuslan Bukin 
77274fe6c29SRuslan Bukin 	return isid;
77374fe6c29SRuslan Bukin }
77474fe6c29SRuslan Bukin 
pt_iscache_lookup(struct pt_image_section_cache * iscache,struct pt_section ** section,uint64_t * laddr,int isid)77574fe6c29SRuslan Bukin int pt_iscache_lookup(struct pt_image_section_cache *iscache,
77674fe6c29SRuslan Bukin 		      struct pt_section **section, uint64_t *laddr, int isid)
77774fe6c29SRuslan Bukin {
77874fe6c29SRuslan Bukin 	uint16_t index;
77974fe6c29SRuslan Bukin 	int errcode, status;
78074fe6c29SRuslan Bukin 
78174fe6c29SRuslan Bukin 	if (!iscache || !section || !laddr)
78274fe6c29SRuslan Bukin 		return -pte_internal;
78374fe6c29SRuslan Bukin 
78474fe6c29SRuslan Bukin 	if (isid <= 0)
78574fe6c29SRuslan Bukin 		return -pte_bad_image;
78674fe6c29SRuslan Bukin 
78774fe6c29SRuslan Bukin 	isid -= 1;
78874fe6c29SRuslan Bukin 	if (isid > UINT16_MAX)
78974fe6c29SRuslan Bukin 		return -pte_internal;
79074fe6c29SRuslan Bukin 
79174fe6c29SRuslan Bukin 	index = (uint16_t) isid;
79274fe6c29SRuslan Bukin 
79374fe6c29SRuslan Bukin 	errcode = pt_iscache_lock(iscache);
79474fe6c29SRuslan Bukin 	if (errcode < 0)
79574fe6c29SRuslan Bukin 		return errcode;
79674fe6c29SRuslan Bukin 
79774fe6c29SRuslan Bukin 	if (iscache->size <= index)
79874fe6c29SRuslan Bukin 		status = -pte_bad_image;
79974fe6c29SRuslan Bukin 	else {
80074fe6c29SRuslan Bukin 		const struct pt_iscache_entry *entry;
80174fe6c29SRuslan Bukin 
80274fe6c29SRuslan Bukin 		entry = &iscache->entries[index];
80374fe6c29SRuslan Bukin 		*section = entry->section;
80474fe6c29SRuslan Bukin 		*laddr = entry->laddr;
80574fe6c29SRuslan Bukin 
80674fe6c29SRuslan Bukin 		status = pt_section_get(*section);
80774fe6c29SRuslan Bukin 	}
80874fe6c29SRuslan Bukin 
80974fe6c29SRuslan Bukin 	errcode = pt_iscache_unlock(iscache);
81074fe6c29SRuslan Bukin 	if (errcode < 0)
81174fe6c29SRuslan Bukin 		return errcode;
81274fe6c29SRuslan Bukin 
81374fe6c29SRuslan Bukin 	return status;
81474fe6c29SRuslan Bukin }
81574fe6c29SRuslan Bukin 
pt_iscache_clear(struct pt_image_section_cache * iscache)81674fe6c29SRuslan Bukin int pt_iscache_clear(struct pt_image_section_cache *iscache)
81774fe6c29SRuslan Bukin {
81874fe6c29SRuslan Bukin 	struct pt_iscache_lru_entry *lru;
81974fe6c29SRuslan Bukin 	struct pt_iscache_entry *entries;
82074fe6c29SRuslan Bukin 	uint16_t idx, end;
82174fe6c29SRuslan Bukin 	int errcode;
82274fe6c29SRuslan Bukin 
82374fe6c29SRuslan Bukin 	if (!iscache)
82474fe6c29SRuslan Bukin 		return -pte_internal;
82574fe6c29SRuslan Bukin 
82674fe6c29SRuslan Bukin 	errcode = pt_iscache_lock(iscache);
82774fe6c29SRuslan Bukin 	if (errcode < 0)
82874fe6c29SRuslan Bukin 		return errcode;
82974fe6c29SRuslan Bukin 
83074fe6c29SRuslan Bukin 	entries = iscache->entries;
83174fe6c29SRuslan Bukin 	end = iscache->size;
83274fe6c29SRuslan Bukin 	lru = iscache->lru;
83374fe6c29SRuslan Bukin 
83474fe6c29SRuslan Bukin 	iscache->entries = NULL;
83574fe6c29SRuslan Bukin 	iscache->capacity = 0;
83674fe6c29SRuslan Bukin 	iscache->size = 0;
83774fe6c29SRuslan Bukin 	iscache->lru = NULL;
83874fe6c29SRuslan Bukin 	iscache->used = 0ull;
83974fe6c29SRuslan Bukin 
84074fe6c29SRuslan Bukin 	errcode = pt_iscache_unlock(iscache);
84174fe6c29SRuslan Bukin 	if (errcode < 0)
84274fe6c29SRuslan Bukin 		return errcode;
84374fe6c29SRuslan Bukin 
84474fe6c29SRuslan Bukin 	errcode = pt_iscache_lru_free(lru);
84574fe6c29SRuslan Bukin 	if (errcode < 0)
84674fe6c29SRuslan Bukin 		return errcode;
84774fe6c29SRuslan Bukin 
84874fe6c29SRuslan Bukin 	for (idx = 0; idx < end; ++idx) {
84974fe6c29SRuslan Bukin 		struct pt_section *section;
85074fe6c29SRuslan Bukin 
85174fe6c29SRuslan Bukin 		section = entries[idx].section;
85274fe6c29SRuslan Bukin 
85374fe6c29SRuslan Bukin 		/* We do not zero-initialize the array - a NULL check is
85474fe6c29SRuslan Bukin 		 * pointless.
85574fe6c29SRuslan Bukin 		 */
85674fe6c29SRuslan Bukin 		errcode = pt_section_detach(section, iscache);
85774fe6c29SRuslan Bukin 		if (errcode < 0)
85874fe6c29SRuslan Bukin 			return errcode;
85974fe6c29SRuslan Bukin 
86074fe6c29SRuslan Bukin 		errcode = pt_section_put(section);
86174fe6c29SRuslan Bukin 		if (errcode < 0)
86274fe6c29SRuslan Bukin 			return errcode;
86374fe6c29SRuslan Bukin 	}
86474fe6c29SRuslan Bukin 
86574fe6c29SRuslan Bukin 	free(entries);
86674fe6c29SRuslan Bukin 	return 0;
86774fe6c29SRuslan Bukin }
86874fe6c29SRuslan Bukin 
pt_iscache_alloc(const char * name)86974fe6c29SRuslan Bukin struct pt_image_section_cache *pt_iscache_alloc(const char *name)
87074fe6c29SRuslan Bukin {
87174fe6c29SRuslan Bukin 	struct pt_image_section_cache *iscache;
87274fe6c29SRuslan Bukin 
87374fe6c29SRuslan Bukin 	iscache = malloc(sizeof(*iscache));
87474fe6c29SRuslan Bukin 	if (iscache)
87574fe6c29SRuslan Bukin 		pt_iscache_init(iscache, name);
87674fe6c29SRuslan Bukin 
87774fe6c29SRuslan Bukin 	return iscache;
87874fe6c29SRuslan Bukin }
87974fe6c29SRuslan Bukin 
pt_iscache_free(struct pt_image_section_cache * iscache)88074fe6c29SRuslan Bukin void pt_iscache_free(struct pt_image_section_cache *iscache)
88174fe6c29SRuslan Bukin {
88274fe6c29SRuslan Bukin 	if (!iscache)
88374fe6c29SRuslan Bukin 		return;
88474fe6c29SRuslan Bukin 
88574fe6c29SRuslan Bukin 	pt_iscache_fini(iscache);
88674fe6c29SRuslan Bukin 	free(iscache);
88774fe6c29SRuslan Bukin }
88874fe6c29SRuslan Bukin 
pt_iscache_set_limit(struct pt_image_section_cache * iscache,uint64_t limit)88974fe6c29SRuslan Bukin int pt_iscache_set_limit(struct pt_image_section_cache *iscache, uint64_t limit)
89074fe6c29SRuslan Bukin {
89174fe6c29SRuslan Bukin 	struct pt_iscache_lru_entry *tail;
89274fe6c29SRuslan Bukin 	int errcode, status;
89374fe6c29SRuslan Bukin 
89474fe6c29SRuslan Bukin 	if (!iscache)
89574fe6c29SRuslan Bukin 		return -pte_invalid;
89674fe6c29SRuslan Bukin 
89774fe6c29SRuslan Bukin 	status = 0;
89874fe6c29SRuslan Bukin 	tail = NULL;
89974fe6c29SRuslan Bukin 
90074fe6c29SRuslan Bukin 	errcode = pt_iscache_lock(iscache);
90174fe6c29SRuslan Bukin 	if (errcode < 0)
90274fe6c29SRuslan Bukin 		return errcode;
90374fe6c29SRuslan Bukin 
90474fe6c29SRuslan Bukin 	iscache->limit = limit;
90574fe6c29SRuslan Bukin 	if (limit < iscache->used)
90674fe6c29SRuslan Bukin 		status = pt_iscache_lru_prune(iscache, &tail);
90774fe6c29SRuslan Bukin 
90874fe6c29SRuslan Bukin 	errcode = pt_iscache_unlock(iscache);
90974fe6c29SRuslan Bukin 
91074fe6c29SRuslan Bukin 	if (errcode < 0 || status < 0)
91174fe6c29SRuslan Bukin 		return (status < 0) ? status : errcode;
91274fe6c29SRuslan Bukin 
91374fe6c29SRuslan Bukin 	return pt_iscache_lru_free(tail);
91474fe6c29SRuslan Bukin }
91574fe6c29SRuslan Bukin 
pt_iscache_name(const struct pt_image_section_cache * iscache)91674fe6c29SRuslan Bukin const char *pt_iscache_name(const struct pt_image_section_cache *iscache)
91774fe6c29SRuslan Bukin {
91874fe6c29SRuslan Bukin 	if (!iscache)
91974fe6c29SRuslan Bukin 		return NULL;
92074fe6c29SRuslan Bukin 
92174fe6c29SRuslan Bukin 	return iscache->name;
92274fe6c29SRuslan Bukin }
92374fe6c29SRuslan Bukin 
pt_iscache_add_file(struct pt_image_section_cache * iscache,const char * filename,uint64_t offset,uint64_t size,uint64_t vaddr)92474fe6c29SRuslan Bukin int pt_iscache_add_file(struct pt_image_section_cache *iscache,
92574fe6c29SRuslan Bukin 			const char *filename, uint64_t offset, uint64_t size,
92674fe6c29SRuslan Bukin 			uint64_t vaddr)
92774fe6c29SRuslan Bukin {
92874fe6c29SRuslan Bukin 	struct pt_section *section;
92974fe6c29SRuslan Bukin 	int errcode, match, isid;
93074fe6c29SRuslan Bukin 
93174fe6c29SRuslan Bukin 	if (!iscache || !filename)
93274fe6c29SRuslan Bukin 		return -pte_invalid;
93374fe6c29SRuslan Bukin 
93474fe6c29SRuslan Bukin 	errcode = pt_iscache_lock(iscache);
93574fe6c29SRuslan Bukin 	if (errcode < 0)
93674fe6c29SRuslan Bukin 		return errcode;
93774fe6c29SRuslan Bukin 
93874fe6c29SRuslan Bukin 	match = pt_iscache_find_section_locked(iscache, filename, offset,
93974fe6c29SRuslan Bukin 					       size, vaddr);
94074fe6c29SRuslan Bukin 	if (match < 0) {
94174fe6c29SRuslan Bukin 		(void) pt_iscache_unlock(iscache);
94274fe6c29SRuslan Bukin 		return match;
94374fe6c29SRuslan Bukin 	}
94474fe6c29SRuslan Bukin 
94574fe6c29SRuslan Bukin 	/* If we found a perfect match, we will share the existing entry.
94674fe6c29SRuslan Bukin 	 *
94774fe6c29SRuslan Bukin 	 * If we found a section, we need to grab a reference before we unlock.
94874fe6c29SRuslan Bukin 	 *
94974fe6c29SRuslan Bukin 	 * If we didn't find a matching section, we create a new section, which
95074fe6c29SRuslan Bukin 	 * implicitly gives us a reference to it.
95174fe6c29SRuslan Bukin 	 */
95274fe6c29SRuslan Bukin 	if (match < iscache->size) {
95374fe6c29SRuslan Bukin 		const struct pt_iscache_entry *entry;
95474fe6c29SRuslan Bukin 
95574fe6c29SRuslan Bukin 		entry = &iscache->entries[match];
95674fe6c29SRuslan Bukin 		if (entry->laddr == vaddr) {
95774fe6c29SRuslan Bukin 			errcode = pt_iscache_unlock(iscache);
95874fe6c29SRuslan Bukin 			if (errcode < 0)
95974fe6c29SRuslan Bukin 				return errcode;
96074fe6c29SRuslan Bukin 
96174fe6c29SRuslan Bukin 			return isid_from_index((uint16_t) match);
96274fe6c29SRuslan Bukin 		}
96374fe6c29SRuslan Bukin 
96474fe6c29SRuslan Bukin 		section = entry->section;
96574fe6c29SRuslan Bukin 
96674fe6c29SRuslan Bukin 		errcode = pt_section_get(section);
96774fe6c29SRuslan Bukin 		if (errcode < 0) {
96874fe6c29SRuslan Bukin 			(void) pt_iscache_unlock(iscache);
96974fe6c29SRuslan Bukin 			return errcode;
97074fe6c29SRuslan Bukin 		}
97174fe6c29SRuslan Bukin 
97274fe6c29SRuslan Bukin 		errcode = pt_iscache_unlock(iscache);
97374fe6c29SRuslan Bukin 		if (errcode < 0) {
97474fe6c29SRuslan Bukin 			(void) pt_section_put(section);
97574fe6c29SRuslan Bukin 			return errcode;
97674fe6c29SRuslan Bukin 		}
97774fe6c29SRuslan Bukin 	} else {
97874fe6c29SRuslan Bukin 		errcode = pt_iscache_unlock(iscache);
97974fe6c29SRuslan Bukin 		if (errcode < 0)
98074fe6c29SRuslan Bukin 			return errcode;
98174fe6c29SRuslan Bukin 
982*85f87cf4SRuslan Bukin 		section = NULL;
983*85f87cf4SRuslan Bukin 		errcode = pt_mk_section(&section, filename, offset, size);
984*85f87cf4SRuslan Bukin 		if (errcode < 0)
985*85f87cf4SRuslan Bukin 			return errcode;
98674fe6c29SRuslan Bukin 	}
98774fe6c29SRuslan Bukin 
98874fe6c29SRuslan Bukin 	/* We unlocked @iscache and hold a reference to @section. */
98974fe6c29SRuslan Bukin 	isid = pt_iscache_add(iscache, section, vaddr);
99074fe6c29SRuslan Bukin 
99174fe6c29SRuslan Bukin 	/* We grab a reference when we add the section.  Drop the one we
99274fe6c29SRuslan Bukin 	 * obtained before.
99374fe6c29SRuslan Bukin 	 */
99474fe6c29SRuslan Bukin 	errcode = pt_section_put(section);
99574fe6c29SRuslan Bukin 	if (errcode < 0)
99674fe6c29SRuslan Bukin 		return errcode;
99774fe6c29SRuslan Bukin 
99874fe6c29SRuslan Bukin 	return isid;
99974fe6c29SRuslan Bukin }
100074fe6c29SRuslan Bukin 
100174fe6c29SRuslan Bukin 
pt_iscache_read(struct pt_image_section_cache * iscache,uint8_t * buffer,uint64_t size,int isid,uint64_t vaddr)100274fe6c29SRuslan Bukin int pt_iscache_read(struct pt_image_section_cache *iscache, uint8_t *buffer,
100374fe6c29SRuslan Bukin 		    uint64_t size, int isid, uint64_t vaddr)
100474fe6c29SRuslan Bukin {
100574fe6c29SRuslan Bukin 	struct pt_section *section;
100674fe6c29SRuslan Bukin 	uint64_t laddr;
100774fe6c29SRuslan Bukin 	int errcode, status;
100874fe6c29SRuslan Bukin 
100974fe6c29SRuslan Bukin 	if (!iscache || !buffer || !size)
101074fe6c29SRuslan Bukin 		return -pte_invalid;
101174fe6c29SRuslan Bukin 
101274fe6c29SRuslan Bukin 	errcode = pt_iscache_lookup(iscache, &section, &laddr, isid);
101374fe6c29SRuslan Bukin 	if (errcode < 0)
101474fe6c29SRuslan Bukin 		return errcode;
101574fe6c29SRuslan Bukin 
101674fe6c29SRuslan Bukin 	if (vaddr < laddr) {
101774fe6c29SRuslan Bukin 		(void) pt_section_put(section);
101874fe6c29SRuslan Bukin 		return -pte_nomap;
101974fe6c29SRuslan Bukin 	}
102074fe6c29SRuslan Bukin 
102174fe6c29SRuslan Bukin 	vaddr -= laddr;
102274fe6c29SRuslan Bukin 
102374fe6c29SRuslan Bukin 	errcode = pt_section_map(section);
102474fe6c29SRuslan Bukin 	if (errcode < 0) {
102574fe6c29SRuslan Bukin 		(void) pt_section_put(section);
102674fe6c29SRuslan Bukin 		return errcode;
102774fe6c29SRuslan Bukin 	}
102874fe6c29SRuslan Bukin 
102974fe6c29SRuslan Bukin 	/* We truncate the read if it gets too big.  The user is expected to
103074fe6c29SRuslan Bukin 	 * issue further reads for the remaining part.
103174fe6c29SRuslan Bukin 	 */
103274fe6c29SRuslan Bukin 	if (UINT16_MAX < size)
103374fe6c29SRuslan Bukin 		size = UINT16_MAX;
103474fe6c29SRuslan Bukin 
103574fe6c29SRuslan Bukin 	status = pt_section_read(section, buffer, (uint16_t) size, vaddr);
103674fe6c29SRuslan Bukin 
103774fe6c29SRuslan Bukin 	errcode = pt_section_unmap(section);
103874fe6c29SRuslan Bukin 	if (errcode < 0) {
103974fe6c29SRuslan Bukin 		(void) pt_section_put(section);
104074fe6c29SRuslan Bukin 		return errcode;
104174fe6c29SRuslan Bukin 	}
104274fe6c29SRuslan Bukin 
104374fe6c29SRuslan Bukin 	errcode = pt_section_put(section);
104474fe6c29SRuslan Bukin 	if (errcode < 0)
104574fe6c29SRuslan Bukin 		return errcode;
104674fe6c29SRuslan Bukin 
104774fe6c29SRuslan Bukin 	return status;
104874fe6c29SRuslan Bukin }
104974fe6c29SRuslan Bukin 
pt_iscache_notify_map(struct pt_image_section_cache * iscache,struct pt_section * section)105074fe6c29SRuslan Bukin int pt_iscache_notify_map(struct pt_image_section_cache *iscache,
105174fe6c29SRuslan Bukin 			  struct pt_section *section)
105274fe6c29SRuslan Bukin {
105374fe6c29SRuslan Bukin 	struct pt_iscache_lru_entry *tail;
105474fe6c29SRuslan Bukin 	int errcode, status;
105574fe6c29SRuslan Bukin 
105674fe6c29SRuslan Bukin 	tail = NULL;
105774fe6c29SRuslan Bukin 
105874fe6c29SRuslan Bukin 	errcode = pt_iscache_lock(iscache);
105974fe6c29SRuslan Bukin 	if (errcode < 0)
106074fe6c29SRuslan Bukin 		return errcode;
106174fe6c29SRuslan Bukin 
106274fe6c29SRuslan Bukin 	status = pt_iscache_lru_add(iscache, section);
106374fe6c29SRuslan Bukin 	if (status > 0)
106474fe6c29SRuslan Bukin 		status = pt_iscache_lru_prune(iscache, &tail);
106574fe6c29SRuslan Bukin 
106674fe6c29SRuslan Bukin 	errcode = pt_iscache_unlock(iscache);
106774fe6c29SRuslan Bukin 
106874fe6c29SRuslan Bukin 	if (errcode < 0 || status < 0)
106974fe6c29SRuslan Bukin 		return (status < 0) ? status : errcode;
107074fe6c29SRuslan Bukin 
107174fe6c29SRuslan Bukin 	return pt_iscache_lru_free(tail);
107274fe6c29SRuslan Bukin }
107374fe6c29SRuslan Bukin 
pt_iscache_notify_resize(struct pt_image_section_cache * iscache,struct pt_section * section,uint64_t memsize)107474fe6c29SRuslan Bukin int pt_iscache_notify_resize(struct pt_image_section_cache *iscache,
107574fe6c29SRuslan Bukin 			     struct pt_section *section, uint64_t memsize)
107674fe6c29SRuslan Bukin {
107774fe6c29SRuslan Bukin 	struct pt_iscache_lru_entry *tail;
107874fe6c29SRuslan Bukin 	int errcode, status;
107974fe6c29SRuslan Bukin 
108074fe6c29SRuslan Bukin 	tail = NULL;
108174fe6c29SRuslan Bukin 
108274fe6c29SRuslan Bukin 	errcode = pt_iscache_lock(iscache);
108374fe6c29SRuslan Bukin 	if (errcode < 0)
108474fe6c29SRuslan Bukin 		return errcode;
108574fe6c29SRuslan Bukin 
108674fe6c29SRuslan Bukin 	status = pt_iscache_lru_resize(iscache, section, memsize);
108774fe6c29SRuslan Bukin 	if (status > 0)
108874fe6c29SRuslan Bukin 		status = pt_iscache_lru_prune(iscache, &tail);
108974fe6c29SRuslan Bukin 
109074fe6c29SRuslan Bukin 	errcode = pt_iscache_unlock(iscache);
109174fe6c29SRuslan Bukin 
109274fe6c29SRuslan Bukin 	if (errcode < 0 || status < 0)
109374fe6c29SRuslan Bukin 		return (status < 0) ? status : errcode;
109474fe6c29SRuslan Bukin 
109574fe6c29SRuslan Bukin 	return pt_iscache_lru_free(tail);
109674fe6c29SRuslan Bukin }
1097