]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libblkid: prune unneeded buffers
authorThomas Weißschuh <thomas@t-8ch.de>
Sun, 10 Sep 2023 20:46:00 +0000 (22:46 +0200)
committerThomas Weißschuh <thomas@t-8ch.de>
Tue, 12 Sep 2023 11:15:08 +0000 (13:15 +0200)
When a new buffer is cached that is a superset of another existing
buffer the old buffer can be removed as future requests can be satisfied
by the new one.

As probe functions can have local references to buffered data, delay the
cleanup until the probefunc is finished to avoid accessing freed data.

For the bcachefs.img testfile this reduces the final (maximal) cache
from 34338 bytes in 54 buffers
  to 24760 bytes in 40 buffers.

Signed-off-by: Thomas Weißschuh <thomas@t-8ch.de>
libblkid/src/blkidP.h
libblkid/src/partitions/partitions.c
libblkid/src/probe.c
libblkid/src/superblocks/superblocks.c
libblkid/src/topology/topology.c

index 088a8a4c94b67c2053313c5793d963483814f14d..fb91185d0b11064f255573f48f1bbe157fbbbb83 100644 (file)
@@ -220,6 +220,7 @@ struct blkid_struct_probe
        struct blkid_chain      *wipe_chain;    /* superblock, partition, ... */
 
        struct list_head        buffers;        /* list of buffers */
+       struct list_head        prunable_buffers;       /* list of prunable buffers */
        struct list_head        hints;
 
        struct blkid_chain      chains[BLKID_NCHAINS];  /* array of chains */
@@ -433,6 +434,8 @@ extern int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id,
                        uint64_t *offset, const struct blkid_idmag **res)
                        __attribute__((nonnull(1)));
 
+extern void blkid_probe_prune_buffers(blkid_probe pr);
+
 /* returns superblock according to 'struct blkid_idmag' */
 extern const unsigned char *blkid_probe_get_sb_buffer(blkid_probe pr, const struct blkid_idmag *mag, size_t size);
 #define blkid_probe_get_sb(_pr, _mag, type) \
index 1c344fd30334d60cfe6c75aa442247a1324f86ea..b142ff7d8343cce161b5e9d863e01dd568ae40de 100644 (file)
@@ -556,6 +556,7 @@ static int idinfo_probe(blkid_probe pr, const struct blkid_idinfo *id,
                DBG(LOWPROBE, ul_debug(
                        "%s: ---> call probefunc()", id->name));
                rc = id->probefunc(pr, mag);
+               blkid_probe_prune_buffers(pr);
                if (rc < 0) {
                        /* reset after error */
                        reset_partlist(blkid_probe_get_partlist(pr));
index fa16f06082e0806999a4f00d66374f2ece16d4ab..6c17fb457c6153e7eec98a8f42b82cf70377ef02 100644 (file)
@@ -156,6 +156,7 @@ blkid_probe blkid_new_probe(void)
                pr->chains[i].enabled = chains_drvs[i]->dflt_enabled;
        }
        INIT_LIST_HEAD(&pr->buffers);
+       INIT_LIST_HEAD(&pr->prunable_buffers);
        INIT_LIST_HEAD(&pr->values);
        INIT_LIST_HEAD(&pr->hints);
        return pr;
@@ -646,6 +647,39 @@ static struct blkid_bufinfo *get_cached_buffer(blkid_probe pr, uint64_t off, uin
        return NULL;
 }
 
+/*
+ * Mark smaller buffers that can be satisfied by bf as prunable
+ */
+static void mark_prunable_buffers(blkid_probe pr, const struct blkid_bufinfo *bf)
+{
+       struct list_head *p, *next;
+
+       list_for_each_safe(p, next, &pr->buffers) {
+               struct blkid_bufinfo *x =
+                               list_entry(p, struct blkid_bufinfo, bufs);
+
+               if (bf->off <= x->off && bf->off + bf->len >= x->off + x->len) {
+                       list_del(&x->bufs);
+                       list_add(&x->bufs, &pr->prunable_buffers);
+               }
+       }
+}
+
+/*
+ * Remove buffers that are marked as prunable
+ */
+void blkid_probe_prune_buffers(blkid_probe pr)
+{
+       struct list_head *p, *next;
+
+       list_for_each_safe(p, next, &pr->prunable_buffers) {
+               struct blkid_bufinfo *x =
+                               list_entry(p, struct blkid_bufinfo, bufs);
+
+               remove_buffer(x);
+       }
+}
+
 /*
  * Zeroize in-memory data in already read buffer. The next blkid_probe_get_buffer()
  * will return modified buffer. This is usable when you want to call the same probing
@@ -746,6 +780,7 @@ const unsigned char *blkid_probe_get_buffer(blkid_probe pr, uint64_t off, uint64
                if (!bf)
                        return NULL;
 
+               mark_prunable_buffers(pr, bf);
                list_add_tail(&bf->bufs, &pr->buffers);
        }
 
@@ -775,6 +810,8 @@ int blkid_probe_reset_buffers(blkid_probe pr)
 
        pr->flags &= ~BLKID_FL_MODIF_BUFF;
 
+       blkid_probe_prune_buffers(pr);
+
        if (list_empty(&pr->buffers))
                return 0;
 
index c7789a15be29ae7ab20e2802a8b76d888a94cef2..e89af1b629c9ef5a84f35e4baffd4ed4cf976a0b 100644 (file)
@@ -418,6 +418,7 @@ static int superblocks_probe(blkid_probe pr, struct blkid_chain *chn)
                if (id->probefunc) {
                        DBG(LOWPROBE, ul_debug("\tcall probefunc()"));
                        rc = id->probefunc(pr, mag);
+                       blkid_probe_prune_buffers(pr);
                        if (rc != BLKID_PROBE_OK) {
                                blkid_probe_chain_reset_values(pr, chn);
                                if (rc < 0)
index cd1f4c25f747736b64256552c35370aad2566889..1fe7c639430752b68fbca8de14f5a6bb90de589c 100644 (file)
@@ -146,6 +146,7 @@ blkid_topology blkid_probe_get_topology(blkid_probe pr)
 static int topology_probe(blkid_probe pr, struct blkid_chain *chn)
 {
        size_t i;
+       int rc;
 
        if (chn->idx < -1)
                return -1;
@@ -182,7 +183,9 @@ static int topology_probe(blkid_probe pr, struct blkid_chain *chn)
 
                if (id->probefunc) {
                        DBG(LOWPROBE, ul_debug("%s: call probefunc()", id->name));
-                       if (id->probefunc(pr, NULL) != 0)
+                       rc = id->probefunc(pr, NULL);
+                       blkid_probe_prune_buffers(pr);
+                       if (rc != 0)
                                continue;
                }