commit 342ab070d42625e6e4b65caf66f7b7117f7d4509 Author: Andrew Price Date: Wed Sep 2 14:12:31 2015 -0500 gfs2_edit savemeta: speed up is_block_in_per_node() Previously is_block_in_per_node() called lgfs2_inode_read() and do_dinode_extended() for each block and savemeta was spending the vast majority of its time in this function according to perf. This patch speeds up the lookups by keeping a tree of per_node block addresses which can be quickly consulted. In my tests with a full 700G filesystem this patch cuts the run time in half. Resolves: rhbz#1162216 Signed-off-by: Andrew Price diff --git a/gfs2/edit/savemeta.c b/gfs2/edit/savemeta.c index b68b0ff..b08eb5e 100644 --- a/gfs2/edit/savemeta.c +++ b/gfs2/edit/savemeta.c @@ -74,8 +74,70 @@ static int block_is_a_journal(void) return FALSE; } +struct osi_root per_node_tree; +struct per_node_node { + struct osi_node node; + uint64_t block; +}; + +static void destroy_per_node_lookup(void) +{ + struct osi_node *n; + struct per_node_node *pnp; + + while ((n = osi_first(&per_node_tree))) { + pnp = (struct per_node_node *)n; + osi_erase(n, &per_node_tree); + free(pnp); + } +} + static int block_is_in_per_node(void) { + struct per_node_node *pnp = (struct per_node_node *)per_node_tree.osi_node; + + while (pnp) { + if (block < pnp->block) + pnp = (struct per_node_node *)pnp->node.osi_left; + else if (block > pnp->block) + pnp = (struct per_node_node *)pnp->node.osi_right; + else + return 1; + } + + return 0; +} + +static int insert_per_node_lookup(uint64_t blk) +{ + struct osi_node **newn = &per_node_tree.osi_node, *parent = NULL; + struct per_node_node *pnp; + + while (*newn) { + struct per_node_node *cur = (struct per_node_node *)*newn; + + parent = *newn; + if (blk < cur->block) + newn = &((*newn)->osi_left); + else if (blk > cur->block) + newn = &((*newn)->osi_right); + else + return 0; + } + + pnp = calloc(1, sizeof(struct per_node_node)); + if (pnp == NULL) { + perror("Failed to insert per_node lookup entry"); + return 1; + } + pnp->block = blk; + osi_link_node(&pnp->node, parent, newn); + osi_insert_color(&pnp->node, &per_node_tree); + return 0; +} + +static int init_per_node_lookup(void) +{ int i; struct gfs2_inode *per_node_di; @@ -85,7 +147,7 @@ static int block_is_in_per_node(void) per_node_di = lgfs2_inode_read(&sbd, masterblock("per_node")); if (per_node_di == NULL) { fprintf(stderr, "Failed to read per_node: %s\n", strerror(errno)); - exit(1); + return 1; } do_dinode_extended(&per_node_di->i_di, per_node_di->i_bh); @@ -94,11 +156,12 @@ static int block_is_in_per_node(void) for (i = 0; i < indirect_blocks; i++) { int d; for (d = 0; d < indirect->ii[i].dirents; d++) { - if (block == indirect->ii[i].dirent[d].block) - return TRUE; + int ret = insert_per_node_lookup(indirect->ii[i].dirent[d].block); + if (ret != 0) + return ret; } } - return FALSE; + return 0; } static int block_is_systemfile(void) @@ -749,6 +812,10 @@ void savemeta(char *out_fn, int saveoption, int gziplevel) printf("Filesystem size: %s\n", anthropomorphize(sbd.fssize * sbd.bsize)); get_journal_inode_blocks(); + err = init_per_node_lookup(); + if (err) + exit(1); + /* Write the savemeta file header */ err = save_header(&mfd, sbd.fssize * sbd.bsize); if (err) { @@ -810,6 +877,7 @@ void savemeta(char *out_fn, int saveoption, int gziplevel) } savemetaclose(&mfd); close(sbd.device_fd); + destroy_per_node_lookup(); free(indirect); gfs2_rgrp_free(&sbd.rgtree); exit(0);