diff options
Diffstat (limited to 'fs/gfs2')
-rw-r--r-- | fs/gfs2/dir.c | 91 | ||||
-rw-r--r-- | fs/gfs2/incore.h | 3 | ||||
-rw-r--r-- | fs/gfs2/ops_fstype.c | 3 | ||||
-rw-r--r-- | fs/gfs2/super.c | 12 |
4 files changed, 89 insertions, 20 deletions
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c index 4ee008c6d64b..6a92592304fb 100644 --- a/fs/gfs2/dir.c +++ b/fs/gfs2/dir.c @@ -82,6 +82,8 @@ #define gfs2_disk_hash2offset(h) (((u64)(h)) >> 1) #define gfs2_dir_offset2hash(p) ((u32)(((u64)(p)) << 1)) +#define GFS2_HASH_INDEX_MASK 0xffffc000 +#define GFS2_USE_HASH_FLAG 0x2000 struct qstr gfs2_qdot __read_mostly; struct qstr gfs2_qdotdot __read_mostly; @@ -1223,10 +1225,10 @@ static int compare_dents(const void *a, const void *b) int ret = 0; dent_a = *(const struct gfs2_dirent **)a; - hash_a = be32_to_cpu(dent_a->de_hash); + hash_a = dent_a->de_cookie; dent_b = *(const struct gfs2_dirent **)b; - hash_b = be32_to_cpu(dent_b->de_hash); + hash_b = dent_b->de_cookie; if (hash_a > hash_b) ret = 1; @@ -1264,19 +1266,20 @@ static int compare_dents(const void *a, const void *b) */ static int do_filldir_main(struct gfs2_inode *dip, struct dir_context *ctx, - const struct gfs2_dirent **darr, u32 entries, - int *copied) + struct gfs2_dirent **darr, u32 entries, + u32 sort_start, int *copied) { const struct gfs2_dirent *dent, *dent_next; u64 off, off_next; unsigned int x, y; int run = 0; - sort(darr, entries, sizeof(struct gfs2_dirent *), compare_dents, NULL); + if (sort_start < entries) + sort(&darr[sort_start], entries - sort_start, + sizeof(struct gfs2_dirent *), compare_dents, NULL); dent_next = darr[0]; - off_next = be32_to_cpu(dent_next->de_hash); - off_next = gfs2_disk_hash2offset(off_next); + off_next = dent_next->de_cookie; for (x = 0, y = 1; x < entries; x++, y++) { dent = dent_next; @@ -1284,8 +1287,7 @@ static int do_filldir_main(struct gfs2_inode *dip, struct dir_context *ctx, if (y < entries) { dent_next = darr[y]; - off_next = be32_to_cpu(dent_next->de_hash); - off_next = gfs2_disk_hash2offset(off_next); + off_next = dent_next->de_cookie; if (off < ctx->pos) continue; @@ -1332,6 +1334,40 @@ static void *gfs2_alloc_sort_buffer(unsigned size) return ptr; } + +static int gfs2_set_cookies(struct gfs2_sbd *sdp, struct buffer_head *bh, + unsigned leaf_nr, struct gfs2_dirent **darr, + unsigned entries) +{ + int sort_id = -1; + int i; + + for (i = 0; i < entries; i++) { + unsigned offset; + + darr[i]->de_cookie = be32_to_cpu(darr[i]->de_hash); + darr[i]->de_cookie = gfs2_disk_hash2offset(darr[i]->de_cookie); + + if (!sdp->sd_args.ar_loccookie) + continue; + offset = (char *)(darr[i]) - + (bh->b_data + gfs2_dirent_offset(bh->b_data)); + offset /= GFS2_MIN_DIRENT_SIZE; + offset += leaf_nr * sdp->sd_max_dents_per_leaf; + if (offset >= GFS2_USE_HASH_FLAG || + leaf_nr >= GFS2_USE_HASH_FLAG) { + darr[i]->de_cookie |= GFS2_USE_HASH_FLAG; + if (sort_id < 0) + sort_id = i; + continue; + } + darr[i]->de_cookie &= GFS2_HASH_INDEX_MASK; + darr[i]->de_cookie |= offset; + } + return sort_id; +} + + static int gfs2_dir_read_leaf(struct inode *inode, struct dir_context *ctx, int *copied, unsigned *depth, u64 leaf_no) @@ -1341,12 +1377,11 @@ static int gfs2_dir_read_leaf(struct inode *inode, struct dir_context *ctx, struct buffer_head *bh; struct gfs2_leaf *lf; unsigned entries = 0, entries2 = 0; - unsigned leaves = 0; - const struct gfs2_dirent **darr, *dent; + unsigned leaves = 0, leaf = 0, offset, sort_offset; + struct gfs2_dirent **darr, *dent; struct dirent_gather g; struct buffer_head **larr; - int leaf = 0; - int error, i; + int error, i, need_sort = 0, sort_id; u64 lfn = leaf_no; do { @@ -1362,6 +1397,11 @@ static int gfs2_dir_read_leaf(struct inode *inode, struct dir_context *ctx, brelse(bh); } while(lfn); + if (*depth < GFS2_DIR_MAX_DEPTH || !sdp->sd_args.ar_loccookie) { + need_sort = 1; + sort_offset = 0; + } + if (!entries) return 0; @@ -1375,8 +1415,8 @@ static int gfs2_dir_read_leaf(struct inode *inode, struct dir_context *ctx, larr = gfs2_alloc_sort_buffer((leaves + entries + 99) * sizeof(void *)); if (!larr) goto out; - darr = (const struct gfs2_dirent **)(larr + leaves); - g.pdent = darr; + darr = (struct gfs2_dirent **)(larr + leaves); + g.pdent = (const struct gfs2_dirent **)darr; g.offset = 0; lfn = leaf_no; @@ -1387,6 +1427,7 @@ static int gfs2_dir_read_leaf(struct inode *inode, struct dir_context *ctx, lf = (struct gfs2_leaf *)bh->b_data; lfn = be64_to_cpu(lf->lf_next); if (lf->lf_entries) { + offset = g.offset; entries2 += be16_to_cpu(lf->lf_entries); dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size, gfs2_dirent_gather, NULL, &g); @@ -1404,17 +1445,26 @@ static int gfs2_dir_read_leaf(struct inode *inode, struct dir_context *ctx, goto out_free; } error = 0; + sort_id = gfs2_set_cookies(sdp, bh, leaf, &darr[offset], + be16_to_cpu(lf->lf_entries)); + if (!need_sort && sort_id >= 0) { + need_sort = 1; + sort_offset = offset + sort_id; + } larr[leaf++] = bh; } else { + larr[leaf++] = NULL; brelse(bh); } } while(lfn); BUG_ON(entries2 != entries); - error = do_filldir_main(ip, ctx, darr, entries, copied); + error = do_filldir_main(ip, ctx, darr, entries, need_sort ? + sort_offset : entries, copied); out_free: for(i = 0; i < leaf; i++) - brelse(larr[i]); + if (larr[i]) + brelse(larr[i]); kvfree(larr); out: return error; @@ -1520,7 +1570,7 @@ int gfs2_dir_read(struct inode *inode, struct dir_context *ctx, struct gfs2_inode *dip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); struct dirent_gather g; - const struct gfs2_dirent **darr, *dent; + struct gfs2_dirent **darr, *dent; struct buffer_head *dibh; int copied = 0; int error; @@ -1544,7 +1594,7 @@ int gfs2_dir_read(struct inode *inode, struct dir_context *ctx, /* 96 is max number of dirents which can be stuffed into an inode */ darr = kmalloc(96 * sizeof(struct gfs2_dirent *), GFP_NOFS); if (darr) { - g.pdent = darr; + g.pdent = (const struct gfs2_dirent **)darr; g.offset = 0; dent = gfs2_dirent_scan(inode, dibh->b_data, dibh->b_size, gfs2_dirent_gather, NULL, &g); @@ -1561,8 +1611,9 @@ int gfs2_dir_read(struct inode *inode, struct dir_context *ctx, error = -EIO; goto out; } + gfs2_set_cookies(sdp, dibh, 0, darr, dip->i_entries); error = do_filldir_main(dip, ctx, darr, - dip->i_entries, &copied); + dip->i_entries, 0, &copied); out: kfree(darr); } diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 921304e1d785..845fb09cc606 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -562,6 +562,8 @@ struct gfs2_args { unsigned int ar_errors:2; /* errors=withdraw | panic */ unsigned int ar_nobarrier:1; /* do not send barriers */ unsigned int ar_rgrplvb:1; /* use lvbs for rgrp info */ + unsigned int ar_loccookie:1; /* use location based readdir + cookies */ int ar_commit; /* Commit interval */ int ar_statfs_quantum; /* The fast statfs interval */ int ar_quota_quantum; /* The quota interval */ @@ -689,6 +691,7 @@ struct gfs2_sbd { u64 sd_heightsize[GFS2_MAX_META_HEIGHT + 1]; u32 sd_max_jheight; /* Max height of journaled file's meta tree */ u64 sd_jheightsize[GFS2_MAX_META_HEIGHT + 1]; + u32 sd_max_dents_per_leaf; /* Max number of dirents in a leaf block */ struct gfs2_args sd_args; /* Mount arguments */ struct gfs2_tune sd_tune; /* Filesystem tuning structure */ diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 1f9de173c4a0..7aacdf2bafd1 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -352,6 +352,9 @@ static int gfs2_read_sb(struct gfs2_sbd *sdp, int silent) sdp->sd_jheightsize[x] = ~0; gfs2_assert(sdp, sdp->sd_max_jheight <= GFS2_MAX_META_HEIGHT); + sdp->sd_max_dents_per_leaf = (sdp->sd_sb.sb_bsize - + sizeof(struct gfs2_leaf)) / + GFS2_MIN_DIRENT_SIZE; return 0; } diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 03fa155f703e..0f3d64606e93 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -83,6 +83,8 @@ enum { Opt_nobarrier, Opt_rgrplvb, Opt_norgrplvb, + Opt_loccookie, + Opt_noloccookie, Opt_error, }; @@ -122,6 +124,8 @@ static const match_table_t tokens = { {Opt_nobarrier, "nobarrier"}, {Opt_rgrplvb, "rgrplvb"}, {Opt_norgrplvb, "norgrplvb"}, + {Opt_loccookie, "loccookie"}, + {Opt_noloccookie, "noloccookie"}, {Opt_error, NULL} }; @@ -278,6 +282,12 @@ int gfs2_mount_args(struct gfs2_args *args, char *options) case Opt_norgrplvb: args->ar_rgrplvb = 0; break; + case Opt_loccookie: + args->ar_loccookie = 1; + break; + case Opt_noloccookie: + args->ar_loccookie = 0; + break; case Opt_error: default: pr_warn("invalid mount option: %s\n", o); @@ -1418,6 +1428,8 @@ static int gfs2_show_options(struct seq_file *s, struct dentry *root) seq_puts(s, ",demote_interface_used"); if (args->ar_rgrplvb) seq_puts(s, ",rgrplvb"); + if (args->ar_loccookie) + seq_puts(s, ",loccookie"); return 0; } |