libipt, block: cache the current section
The block decoder needs to find and map the section containing the block's start
IP in the current address-space on every call to pt_blk_next(). The image
maintains an LRU cache so finding the desired section is typically fast.
The operation still requires one additional pt_section_get() and one additional
pt_section_map() to ensure that the section is not unmapped or destroyed while
the block decoder uses it. Plus the corresponding pt_section_unmap() and
pt_section_put() at the end of pt_blk_next().
Cache the current section across pt_blk_next() calls to avoid this overhead.
We validate that a pt_image_find() would return the cached section before using
it. This check is much cheaper, though, as it does not require any additional
get/put or map/unmap.
Change-Id: I50ee5e549db0b2ad816c4b5825ae6802503e8bf8
Signed-off-by: Markus Metzger <markus.t.metzger@intel.com>
diff --git a/libipt/internal/include/pt_block_decoder.h b/libipt/internal/include/pt_block_decoder.h
index 9fc3939..cd667a3 100644
--- a/libipt/internal/include/pt_block_decoder.h
+++ b/libipt/internal/include/pt_block_decoder.h
@@ -33,6 +33,35 @@
#include "pt_image.h"
#include "pt_retstack.h"
+struct pt_section;
+
+
+/* A cached mapped section.
+ *
+ * This caches a single mapped section across pt_blk_next() calls to avoid
+ * repeated get/map and unmap/put of the current section.
+ *
+ * Since we can't guarantee that the image doesn't change between pt_blk_next()
+ * calls, we still need to validate that the cached section is accurate. This
+ * can be done without additional get/put or map/unmap of the cached section,
+ * though, and is significantly cheaper.
+ */
+struct pt_cached_section {
+ /* The cached section.
+ *
+ * The cache is valid if and only if @section is non-NULL.
+ *
+ * It needs to be unmapped and put. Use pt_blk_scache_invalidate() to
+ * release the cached section and to invalidate the cache.
+ */
+ struct pt_section *section;
+
+ /* The virtual address at which @section was loaded. */
+ uint64_t laddr;
+
+ /* The section identifier. */
+ int isid;
+};
/* A block decoder.
*
@@ -50,6 +79,9 @@
/* The image. */
struct pt_image *image;
+ /* The current cached section. */
+ struct pt_cached_section scache;
+
/* The current address space. */
struct pt_asid asid;
diff --git a/libipt/src/pt_block_decoder.c b/libipt/src/pt_block_decoder.c
index ed35040..ef15523 100644
--- a/libipt/src/pt_block_decoder.c
+++ b/libipt/src/pt_block_decoder.c
@@ -47,6 +47,99 @@
struct pt_section *, uint64_t);
+/* Release a cached section.
+ *
+ * If @scache does not contain a section, this does noting.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ * Returns -pte_internal, if @scache is NULL.
+ */
+static int pt_blk_scache_invalidate(struct pt_cached_section *scache)
+{
+ struct pt_section *section;
+ int errcode;
+
+ if (!scache)
+ return -pte_internal;
+
+ section = scache->section;
+ if (!section)
+ return 0;
+
+ errcode = pt_section_unmap(section);
+ if (errcode < 0)
+ return errcode;
+
+ scache->section = NULL;
+
+ return pt_section_put(section);
+}
+
+/* Cache @section loaded at @laddr identified by @isid in @scache.
+ *
+ * The caller transfers its use- and map-count to @scache.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ * Returns -pte_internal if @scache or @section is NULL.
+ * Returns -pte_internal if another section is already cached.
+ */
+static int pt_blk_cache_section(struct pt_cached_section *scache,
+ struct pt_section *section, uint64_t laddr,
+ int isid)
+{
+ if (!scache || !section)
+ return -pte_internal;
+
+ if (scache->section)
+ return -pte_internal;
+
+ scache->section = section;
+ scache->laddr = laddr;
+ scache->isid = isid;
+
+ return 0;
+}
+
+/* Get @scache's cached section.
+ *
+ * Check whether @scache contains a section that an image lookup of @ip in @asid
+ * would return. On success, provides the cached section in @psection and its
+ * load address in @pladdr.
+ *
+ * Returns the section's identifier on success, a negative error code otherwise.
+ * Returns -pte_internal if @scache, @psection, or @pladdr is NULL.
+ * Returns -pte_nomap if @scache does not have a section cached.
+ * Returns -pte_nomap if @scache's cached section does not contain @ip.
+ */
+static int pt_blk_cached_section(struct pt_cached_section *scache,
+ struct pt_section **psection, uint64_t *pladdr,
+ struct pt_image *image, struct pt_asid *asid,
+ uint64_t ip)
+{
+ struct pt_section *section;
+ uint64_t laddr;
+ int isid, errcode;
+
+ if (!scache || !psection || !pladdr)
+ return -pte_internal;
+
+
+ section = scache->section;
+ laddr = scache->laddr;
+ isid = scache->isid;
+ if (!section)
+ return -pte_nomap;
+
+ errcode = pt_image_validate(image, asid, ip, section, laddr, isid);
+ if (errcode < 0)
+ return errcode;
+
+ *psection = section;
+ *pladdr = laddr;
+
+ return isid;
+}
+
static void pt_blk_reset(struct pt_block_decoder *decoder)
{
if (!decoder)
@@ -78,6 +171,8 @@
pt_image_init(&decoder->default_image, NULL);
decoder->image = &decoder->default_image;
+ memset(&decoder->scache, 0, sizeof(decoder->scache));
+
pt_blk_reset(decoder);
return 0;
@@ -88,6 +183,9 @@
if (!decoder)
return;
+ /* Release the cached section so we don't leak it. */
+ (void) pt_blk_scache_invalidate(&decoder->scache);
+
pt_image_fini(&decoder->default_image);
pt_qry_decoder_fini(&decoder->query);
}
@@ -2277,21 +2375,42 @@
struct pt_block_cache *bcache;
struct pt_section *section;
uint64_t laddr;
- int isid, errcode, status;
+ int isid, errcode;
if (!decoder || !block)
return -pte_internal;
- isid = pt_image_find(decoder->image, §ion, &laddr, &decoder->asid,
- decoder->ip);
+ isid = pt_blk_cached_section(&decoder->scache, §ion, &laddr,
+ decoder->image, &decoder->asid,
+ decoder->ip);
if (isid < 0) {
if (isid != -pte_nomap)
return isid;
- /* Even if there is no such section in the image, we may still
- * read the memory via the callback function.
- */
- return pt_blk_proceed_no_event_uncached(decoder, block);
+ errcode = pt_blk_scache_invalidate(&decoder->scache);
+ if (errcode < 0)
+ return errcode;
+
+ isid = pt_image_find(decoder->image, §ion, &laddr,
+ &decoder->asid, decoder->ip);
+ if (isid < 0) {
+ if (isid != -pte_nomap)
+ return isid;
+
+ /* Even if there is no such section in the image, we may
+ * still read the memory via the callback function.
+ */
+ return pt_blk_proceed_no_event_uncached(decoder, block);
+ }
+
+ errcode = pt_section_map(section);
+ if (errcode < 0)
+ goto out_put;
+
+ errcode = pt_blk_cache_section(&decoder->scache, section, laddr,
+ isid);
+ if (errcode < 0)
+ goto out_unmap;
}
/* We do not switch sections inside a block. */
@@ -2302,35 +2421,15 @@
block->isid = isid;
}
- errcode = pt_section_map(section);
- if (errcode < 0)
- goto out_put;
-
bcache = pt_section_bcache(section);
- if (!bcache) {
- errcode = pt_section_unmap(section);
- if (errcode < 0)
- goto out_put;
-
- errcode = pt_section_put(section);
- if (errcode < 0)
- return errcode;
-
+ if (!bcache)
return pt_blk_proceed_no_event_uncached(decoder, block);
- }
- status = pt_blk_proceed_no_event_cached(decoder, block, bcache,
- section, laddr);
+ return pt_blk_proceed_no_event_cached(decoder, block, bcache, section,
+ laddr);
- errcode = pt_section_unmap(section);
- if (errcode < 0)
- goto out_put;
-
- errcode = pt_section_put(section);
- if (errcode < 0)
- return errcode;
-
- return status;
+out_unmap:
+ (void) pt_section_unmap(section);
out_put:
(void) pt_section_put(section);