|
@@ -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);
|
|
|
}
|