|
|
@@ -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;
|
|
|
@@ -108,7 +110,7 @@ static int gfs2_dir_get_existing_buffer(struct gfs2_inode *ip, u64 block,
|
|
|
struct buffer_head *bh;
|
|
|
int error;
|
|
|
|
|
|
- error = gfs2_meta_read(ip->i_gl, block, DIO_WAIT, &bh);
|
|
|
+ error = gfs2_meta_read(ip->i_gl, block, DIO_WAIT, 0, &bh);
|
|
|
if (error)
|
|
|
return error;
|
|
|
if (gfs2_metatype_check(GFS2_SB(&ip->i_inode), bh, GFS2_METATYPE_JD)) {
|
|
|
@@ -305,7 +307,7 @@ static int gfs2_dir_read_data(struct gfs2_inode *ip, __be64 *buf,
|
|
|
BUG_ON(extlen < 1);
|
|
|
bh = gfs2_meta_ra(ip->i_gl, dblock, extlen);
|
|
|
} else {
|
|
|
- error = gfs2_meta_read(ip->i_gl, dblock, DIO_WAIT, &bh);
|
|
|
+ error = gfs2_meta_read(ip->i_gl, dblock, DIO_WAIT, 0, &bh);
|
|
|
if (error)
|
|
|
goto fail;
|
|
|
}
|
|
|
@@ -443,6 +445,27 @@ static int gfs2_dirent_last(const struct gfs2_dirent *dent,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/* Look for the dirent that contains the offset specified in data. Once we
|
|
|
+ * find that dirent, there must be space available there for the new dirent */
|
|
|
+static int gfs2_dirent_find_offset(const struct gfs2_dirent *dent,
|
|
|
+ const struct qstr *name,
|
|
|
+ void *ptr)
|
|
|
+{
|
|
|
+ unsigned required = GFS2_DIRENT_SIZE(name->len);
|
|
|
+ unsigned actual = GFS2_DIRENT_SIZE(be16_to_cpu(dent->de_name_len));
|
|
|
+ unsigned totlen = be16_to_cpu(dent->de_rec_len);
|
|
|
+
|
|
|
+ if (ptr < (void *)dent || ptr >= (void *)dent + totlen)
|
|
|
+ return 0;
|
|
|
+ if (gfs2_dirent_sentinel(dent))
|
|
|
+ actual = 0;
|
|
|
+ if (ptr < (void *)dent + actual)
|
|
|
+ return -1;
|
|
|
+ if ((void *)dent + totlen >= ptr + required)
|
|
|
+ return 1;
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
static int gfs2_dirent_find_space(const struct gfs2_dirent *dent,
|
|
|
const struct qstr *name,
|
|
|
void *opaque)
|
|
|
@@ -682,6 +705,27 @@ static void dirent_del(struct gfs2_inode *dip, struct buffer_head *bh,
|
|
|
prev->de_rec_len = cpu_to_be16(prev_rec_len);
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+static struct gfs2_dirent *do_init_dirent(struct inode *inode,
|
|
|
+ struct gfs2_dirent *dent,
|
|
|
+ const struct qstr *name,
|
|
|
+ struct buffer_head *bh,
|
|
|
+ unsigned offset)
|
|
|
+{
|
|
|
+ struct gfs2_inode *ip = GFS2_I(inode);
|
|
|
+ struct gfs2_dirent *ndent;
|
|
|
+ unsigned totlen;
|
|
|
+
|
|
|
+ totlen = be16_to_cpu(dent->de_rec_len);
|
|
|
+ BUG_ON(offset + name->len > totlen);
|
|
|
+ gfs2_trans_add_meta(ip->i_gl, bh);
|
|
|
+ ndent = (struct gfs2_dirent *)((char *)dent + offset);
|
|
|
+ dent->de_rec_len = cpu_to_be16(offset);
|
|
|
+ gfs2_qstr2dirent(name, totlen - offset, ndent);
|
|
|
+ return ndent;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
/*
|
|
|
* Takes a dent from which to grab space as an argument. Returns the
|
|
|
* newly created dent.
|
|
|
@@ -691,31 +735,25 @@ static struct gfs2_dirent *gfs2_init_dirent(struct inode *inode,
|
|
|
const struct qstr *name,
|
|
|
struct buffer_head *bh)
|
|
|
{
|
|
|
- struct gfs2_inode *ip = GFS2_I(inode);
|
|
|
- struct gfs2_dirent *ndent;
|
|
|
- unsigned offset = 0, totlen;
|
|
|
+ unsigned offset = 0;
|
|
|
|
|
|
if (!gfs2_dirent_sentinel(dent))
|
|
|
offset = GFS2_DIRENT_SIZE(be16_to_cpu(dent->de_name_len));
|
|
|
- totlen = be16_to_cpu(dent->de_rec_len);
|
|
|
- BUG_ON(offset + name->len > totlen);
|
|
|
- gfs2_trans_add_meta(ip->i_gl, bh);
|
|
|
- ndent = (struct gfs2_dirent *)((char *)dent + offset);
|
|
|
- dent->de_rec_len = cpu_to_be16(offset);
|
|
|
- gfs2_qstr2dirent(name, totlen - offset, ndent);
|
|
|
- return ndent;
|
|
|
+ return do_init_dirent(inode, dent, name, bh, offset);
|
|
|
}
|
|
|
|
|
|
-static struct gfs2_dirent *gfs2_dirent_alloc(struct inode *inode,
|
|
|
- struct buffer_head *bh,
|
|
|
- const struct qstr *name)
|
|
|
+static struct gfs2_dirent *gfs2_dirent_split_alloc(struct inode *inode,
|
|
|
+ struct buffer_head *bh,
|
|
|
+ const struct qstr *name,
|
|
|
+ void *ptr)
|
|
|
{
|
|
|
struct gfs2_dirent *dent;
|
|
|
dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size,
|
|
|
- gfs2_dirent_find_space, name, NULL);
|
|
|
+ gfs2_dirent_find_offset, name, ptr);
|
|
|
if (!dent || IS_ERR(dent))
|
|
|
return dent;
|
|
|
- return gfs2_init_dirent(inode, dent, name, bh);
|
|
|
+ return do_init_dirent(inode, dent, name, bh,
|
|
|
+ (unsigned)(ptr - (void *)dent));
|
|
|
}
|
|
|
|
|
|
static int get_leaf(struct gfs2_inode *dip, u64 leaf_no,
|
|
|
@@ -723,7 +761,7 @@ static int get_leaf(struct gfs2_inode *dip, u64 leaf_no,
|
|
|
{
|
|
|
int error;
|
|
|
|
|
|
- error = gfs2_meta_read(dip->i_gl, leaf_no, DIO_WAIT, bhp);
|
|
|
+ error = gfs2_meta_read(dip->i_gl, leaf_no, DIO_WAIT, 0, bhp);
|
|
|
if (!error && gfs2_metatype_check(GFS2_SB(&dip->i_inode), *bhp, GFS2_METATYPE_LF)) {
|
|
|
/* pr_info("block num=%llu\n", leaf_no); */
|
|
|
error = -EIO;
|
|
|
@@ -1051,10 +1089,11 @@ static int dir_split_leaf(struct inode *inode, const struct qstr *name)
|
|
|
if (!gfs2_dirent_sentinel(dent) &&
|
|
|
be32_to_cpu(dent->de_hash) < divider) {
|
|
|
struct qstr str;
|
|
|
+ void *ptr = ((char *)dent - obh->b_data) + nbh->b_data;
|
|
|
str.name = (char*)(dent+1);
|
|
|
str.len = be16_to_cpu(dent->de_name_len);
|
|
|
str.hash = be32_to_cpu(dent->de_hash);
|
|
|
- new = gfs2_dirent_alloc(inode, nbh, &str);
|
|
|
+ new = gfs2_dirent_split_alloc(inode, nbh, &str, ptr);
|
|
|
if (IS_ERR(new)) {
|
|
|
error = PTR_ERR(new);
|
|
|
break;
|
|
|
@@ -1186,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;
|
|
|
@@ -1227,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;
|
|
|
@@ -1247,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;
|
|
|
@@ -1295,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)
|
|
|
@@ -1304,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 {
|
|
|
@@ -1325,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;
|
|
|
|
|
|
@@ -1338,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;
|
|
|
|
|
|
@@ -1350,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);
|
|
|
@@ -1367,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;
|
|
|
@@ -1483,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;
|
|
|
@@ -1507,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);
|
|
|
@@ -1524,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);
|
|
|
}
|
|
|
@@ -1560,15 +1648,22 @@ struct inode *gfs2_dir_search(struct inode *dir, const struct qstr *name,
|
|
|
|
|
|
dent = gfs2_dirent_search(dir, name, gfs2_dirent_find, &bh);
|
|
|
if (dent) {
|
|
|
+ struct inode *inode;
|
|
|
+ u16 rahead;
|
|
|
+
|
|
|
if (IS_ERR(dent))
|
|
|
return ERR_CAST(dent);
|
|
|
dtype = be16_to_cpu(dent->de_type);
|
|
|
+ rahead = be16_to_cpu(dent->de_rahead);
|
|
|
addr = be64_to_cpu(dent->de_inum.no_addr);
|
|
|
formal_ino = be64_to_cpu(dent->de_inum.no_formal_ino);
|
|
|
brelse(bh);
|
|
|
if (fail_on_exist)
|
|
|
return ERR_PTR(-EEXIST);
|
|
|
- return gfs2_inode_lookup(dir->i_sb, dtype, addr, formal_ino, 0);
|
|
|
+ inode = gfs2_inode_lookup(dir->i_sb, dtype, addr, formal_ino, 0);
|
|
|
+ if (!IS_ERR(inode))
|
|
|
+ GFS2_I(inode)->i_rahead = rahead;
|
|
|
+ return inode;
|
|
|
}
|
|
|
return ERR_PTR(-ENOENT);
|
|
|
}
|