summaryrefslogtreecommitdiffstats
path: root/fs/gfs2/dir.c
diff options
context:
space:
mode:
authorSteven Whitehouse <swhiteho@redhat.com>2006-03-28 14:14:04 -0500
committerSteven Whitehouse <swhiteho@redhat.com>2006-03-28 14:14:04 -0500
commit71b86f562b5eb6f94ea00bba060caa64d0137969 (patch)
tree63d982e09a9cb934fe656afe115031c0a9dc5e4a /fs/gfs2/dir.c
parent94aabbd99370f738da4f6cb4ea0b94cd9024002f (diff)
[GFS2] Further updates to dir and logging code
This reduces the size of the directory code by about 3k and gets readdir() to use the functions which were introduced in the previous directory code update. Two memory allocations are merged into one. Eliminates zeroing of some buffers which were never used before they were initialised by other data. There is still scope for further improvement in the directory code. On the logging side, a hand created mutex has been replaced by a standard Linux mutex in the log allocation code. Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
Diffstat (limited to 'fs/gfs2/dir.c')
-rw-r--r--fs/gfs2/dir.c412
1 files changed, 161 insertions, 251 deletions
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c
index f31f163da1a..ba3438553f3 100644
--- a/fs/gfs2/dir.c
+++ b/fs/gfs2/dir.c
@@ -60,6 +60,7 @@
#include <linux/buffer_head.h>
#include <linux/sort.h>
#include <linux/gfs2_ondisk.h>
+#include <linux/crc32.h>
#include <asm/semaphore.h>
#include "gfs2.h"
@@ -344,7 +345,8 @@ fail:
}
typedef int (*gfs2_dscan_t)(const struct gfs2_dirent *dent,
- const struct qstr *name);
+ const struct qstr *name,
+ void *opaque);
static inline int __gfs2_dirent_find(const struct gfs2_dirent *dent,
const struct qstr *name, int ret)
@@ -358,13 +360,15 @@ static inline int __gfs2_dirent_find(const struct gfs2_dirent *dent,
}
static int gfs2_dirent_find(const struct gfs2_dirent *dent,
- const struct qstr *name)
+ const struct qstr *name,
+ void *opaque)
{
return __gfs2_dirent_find(dent, name, 1);
}
static int gfs2_dirent_prev(const struct gfs2_dirent *dent,
- const struct qstr *name)
+ const struct qstr *name,
+ void *opaque)
{
return __gfs2_dirent_find(dent, name, 2);
}
@@ -374,7 +378,8 @@ static int gfs2_dirent_prev(const struct gfs2_dirent *dent,
* name->len holds size of block.
*/
static int gfs2_dirent_last(const struct gfs2_dirent *dent,
- const struct qstr *name)
+ const struct qstr *name,
+ void *opaque)
{
const char *start = name->name;
const char *end = (const char *)dent + be16_to_cpu(dent->de_rec_len);
@@ -384,17 +389,36 @@ static int gfs2_dirent_last(const struct gfs2_dirent *dent,
}
static int gfs2_dirent_find_space(const struct gfs2_dirent *dent,
- const struct qstr *name)
+ const struct qstr *name,
+ void *opaque)
{
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 (!dent->de_inum.no_addr)
+ actual = GFS2_DIRENT_SIZE(0);
if ((totlen - actual) >= required)
return 1;
return 0;
}
+struct dirent_gather {
+ const struct gfs2_dirent **pdent;
+ unsigned offset;
+};
+
+static int gfs2_dirent_gather(const struct gfs2_dirent *dent,
+ const struct qstr *name,
+ void *opaque)
+{
+ struct dirent_gather *g = opaque;
+ if (dent->de_inum.no_addr) {
+ g->pdent[g->offset++] = dent;
+ }
+ return 0;
+}
+
/*
* Other possible things to check:
* - Inode located within filesystem size (and on valid block)
@@ -431,19 +455,12 @@ error:
return -EIO;
}
-static struct gfs2_dirent *gfs2_dirent_scan(struct inode *inode,
- void *buf,
- unsigned int len, gfs2_dscan_t scan,
- const struct qstr *name)
+static int gfs2_dirent_offset(const void *buf)
{
- struct gfs2_meta_header *h = buf;
- struct gfs2_dirent *dent, *prev;
- unsigned offset;
- unsigned size;
- int ret = 0;
+ const struct gfs2_meta_header *h = buf;
+ int offset;
BUG_ON(buf == NULL);
- BUG_ON(name == NULL);
switch(be16_to_cpu(h->mh_type)) {
case GFS2_METATYPE_LF:
@@ -455,14 +472,36 @@ static struct gfs2_dirent *gfs2_dirent_scan(struct inode *inode,
default:
goto wrong_type;
}
+ return offset;
+wrong_type:
+ printk(KERN_WARNING "gfs2_scan_dirent: wrong block type %u\n",
+ be16_to_cpu(h->mh_type));
+ return -1;
+}
+static struct gfs2_dirent *gfs2_dirent_scan(struct inode *inode,
+ void *buf,
+ unsigned int len, gfs2_dscan_t scan,
+ const struct qstr *name,
+ void *opaque)
+{
+ struct gfs2_dirent *dent, *prev;
+ unsigned offset;
+ unsigned size;
+ int ret = 0;
+
+ ret = gfs2_dirent_offset(buf);
+ if (ret < 0)
+ goto consist_inode;
+
+ offset = ret;
prev = NULL;
dent = (struct gfs2_dirent *)(buf + offset);
size = be16_to_cpu(dent->de_rec_len);
if (gfs2_check_dirent(dent, offset, size, len, 1))
goto consist_inode;
do {
- ret = scan(dent, name);
+ ret = scan(dent, name, opaque);
if (ret)
break;
offset += size;
@@ -487,9 +526,6 @@ static struct gfs2_dirent *gfs2_dirent_scan(struct inode *inode,
return ERR_PTR(ret);
}
-wrong_type:
- printk(KERN_WARNING "gfs2_scan_dirent: %p wrong block type %u\n", scan,
- be16_to_cpu(h->mh_type));
consist_inode:
gfs2_consist_inode(inode->u.generic_ip);
return ERR_PTR(-EIO);
@@ -651,7 +687,8 @@ static struct gfs2_dirent *gfs2_dirent_alloc(struct inode *inode,
const struct qstr *name)
{
struct gfs2_dirent *dent;
- dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size, gfs2_dirent_find_space, name);
+ dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size,
+ gfs2_dirent_find_space, name, NULL);
if (!dent || IS_ERR(dent))
return dent;
return gfs2_init_dirent(inode, dent, name, bh);
@@ -734,7 +771,7 @@ static struct gfs2_dirent *gfs2_dirent_search(struct inode *inode,
return ERR_PTR(error);
do {
dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size,
- scan, name);
+ scan, name, NULL);
if (dent)
goto got_dent;
leaf = (struct gfs2_leaf *)bh->b_data;
@@ -751,7 +788,7 @@ static struct gfs2_dirent *gfs2_dirent_search(struct inode *inode,
error = gfs2_meta_inode_buffer(ip, &bh);
if (error)
return ERR_PTR(error);
- dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size, scan, name);
+ dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size, scan, name, NULL);
got_dent:
*pbh = bh;
return dent;
@@ -764,6 +801,7 @@ static struct gfs2_leaf *new_leaf(struct inode *inode, struct buffer_head **pbh,
struct buffer_head *bh = gfs2_meta_new(ip->i_gl, bn);
struct gfs2_leaf *leaf;
struct gfs2_dirent *dent;
+ struct qstr name = { .name = "", .len = 0, .hash = 0 };
if (!bh)
return NULL;
gfs2_trans_add_bh(ip->i_gl, bh, 1);
@@ -775,12 +813,7 @@ static struct gfs2_leaf *new_leaf(struct inode *inode, struct buffer_head **pbh,
leaf->lf_next = cpu_to_be64(0);
memset(leaf->lf_reserved, 0, sizeof(leaf->lf_reserved));
dent = (struct gfs2_dirent *)(leaf+1);
- dent->de_inum.no_formal_ino = cpu_to_be64(0);
- dent->de_inum.no_addr = cpu_to_be64(0);
- dent->de_hash = cpu_to_be32(0);
- dent->de_rec_len = cpu_to_be16(bh->b_size - sizeof(struct gfs2_leaf));
- dent->de_name_len = cpu_to_be16(0);
- dent->de_type = cpu_to_be16(0);
+ gfs2_qstr2dirent(&name, bh->b_size - sizeof(struct gfs2_leaf), dent);
*pbh = bh;
return leaf;
}
@@ -831,7 +864,7 @@ static int dir_make_exhash(struct inode *inode)
sizeof(struct gfs2_leaf);
args.name = bh->b_data;
dent = gfs2_dirent_scan(dip->i_vnode, bh->b_data, bh->b_size,
- gfs2_dirent_last, &args);
+ gfs2_dirent_last, &args, NULL);
if (!dent) {
brelse(bh);
brelse(dibh);
@@ -939,7 +972,7 @@ static int dir_split_leaf(struct inode *inode, const struct qstr *name)
lp[x] = cpu_to_be64(bn);
error = gfs2_dir_write_data(dip, (char *)lp, start * sizeof(uint64_t),
- half_len * sizeof(uint64_t));
+ half_len * sizeof(uint64_t));
if (error != half_len * sizeof(uint64_t)) {
if (error >= 0)
error = -EIO;
@@ -965,7 +998,7 @@ static int dir_split_leaf(struct inode *inode, const struct qstr *name)
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(dip->i_vnode, nbh, &str);
+ new = gfs2_dirent_alloc(inode, nbh, &str);
if (IS_ERR(new)) {
error = PTR_ERR(new);
break;
@@ -1154,10 +1187,10 @@ static int compare_dents(const void *a, const void *b)
static int do_filldir_main(struct gfs2_inode *dip, uint64_t *offset,
void *opaque, gfs2_filldir_t filldir,
- struct gfs2_dirent **darr, uint32_t entries,
+ const struct gfs2_dirent **darr, uint32_t entries,
int *copied)
{
- struct gfs2_dirent *dent, *dent_next;
+ const struct gfs2_dirent *dent, *dent_next;
struct gfs2_inum inum;
uint64_t off, off_next;
unsigned int x, y;
@@ -1216,189 +1249,74 @@ static int do_filldir_main(struct gfs2_inode *dip, uint64_t *offset,
return 0;
}
-/**
- * do_filldir_single - Read directory entries out of a single block
- * @dip: The GFS2 inode
- * @offset: The offset in the file to read from
- * @opaque: opaque data to pass to filldir
- * @filldir: The function to pass entries to
- * @bh: the block
- * @entries: the number of entries in the block
- * @copied: pointer to int that's non-zero if a entry has been copied out
- *
- * Returns: errno, >0 on exception from filldir
- */
-
-static int do_filldir_single(struct gfs2_inode *dip, uint64_t *offset,
- void *opaque, gfs2_filldir_t filldir,
- struct buffer_head *bh, uint32_t entries,
- int *copied)
+static int gfs2_dir_read_leaf(struct inode *inode, u64 *offset, void *opaque,
+ gfs2_filldir_t filldir, int *copied,
+ unsigned *depth, u64 leaf_no)
{
- struct gfs2_dirent **darr;
- struct gfs2_dirent *de;
- unsigned int e = 0;
- int error;
-
- if (!entries)
- return 0;
-
- darr = kcalloc(entries, sizeof(struct gfs2_dirent *), GFP_KERNEL);
- if (!darr)
- return -ENOMEM;
+ struct gfs2_inode *ip = inode->u.generic_ip;
+ struct buffer_head *bh;
+ struct gfs2_leaf *lf;
+ unsigned entries = 0;
+ unsigned leaves = 0;
+ const struct gfs2_dirent **darr, *dent;
+ struct dirent_gather g;
+ struct buffer_head **larr;
+ int leaf = 0;
+ int error, i;
+ u64 lfn = leaf_no;
- dirent_first(dip, bh, &de);
do {
- if (!de->de_inum.no_addr)
- continue;
- if (e >= entries) {
- gfs2_consist_inode(dip);
- error = -EIO;
- goto out;
- }
- darr[e++] = de;
- } while (dirent_next(dip, bh, &de) == 0);
-
- if (e != entries) {
- gfs2_consist_inode(dip);
- error = -EIO;
- goto out;
- }
-
- error = do_filldir_main(dip, offset, opaque, filldir, darr,
- entries, copied);
-
- out:
- kfree(darr);
-
- return error;
-}
-
-/**
- * do_filldir_multi - Read directory entries out of a linked leaf list
- * @dip: The GFS2 inode
- * @offset: The offset in the file to read from
- * @opaque: opaque data to pass to filldir
- * @filldir: The function to pass entries to
- * @bh: the first leaf in the list
- * @copied: pointer to int that's non-zero if a entry has been copied out
- *
- * Returns: errno, >0 on exception from filldir
- */
-
-static int do_filldir_multi(struct gfs2_inode *dip, uint64_t *offset,
- void *opaque, gfs2_filldir_t filldir,
- struct buffer_head *bh, int *copied)
-{
- struct buffer_head **larr = NULL;
- struct gfs2_dirent **darr;
- struct gfs2_leaf *leaf;
- struct buffer_head *tmp_bh;
- struct gfs2_dirent *de;
- unsigned int entries, e = 0;
- unsigned int leaves = 0, l = 0;
- unsigned int x;
- uint64_t ln;
- int error = 0;
-
- /* Count leaves and entries */
-
- leaf = (struct gfs2_leaf *)bh->b_data;
- entries = be16_to_cpu(leaf->lf_entries);
- ln = be64_to_cpu(leaf->lf_next);
-
- while (ln) {
- error = get_leaf(dip, ln, &tmp_bh);
+ error = get_leaf(ip, lfn, &bh);
if (error)
- return error;
-
- leaf = (struct gfs2_leaf *)tmp_bh->b_data;
- if (leaf->lf_entries) {
- entries += be16_to_cpu(leaf->lf_entries);
- leaves++;
- }
- ln = be64_to_cpu(leaf->lf_next);
-
- brelse(tmp_bh);
- }
+ goto out;
+ lf = (struct gfs2_leaf *)bh->b_data;
+ if (leaves == 0)
+ *depth = be16_to_cpu(lf->lf_depth);
+ entries += be16_to_cpu(lf->lf_entries);
+ leaves++;
+ lfn = be64_to_cpu(lf->lf_next);
+ brelse(bh);
+ } while(lfn);
if (!entries)
return 0;
- if (leaves) {
- larr = kcalloc(leaves, sizeof(struct buffer_head *),GFP_KERNEL);
- if (!larr)
- return -ENOMEM;
- }
-
- darr = kcalloc(entries, sizeof(struct gfs2_dirent *), GFP_KERNEL);
- if (!darr) {
- kfree(larr);
- return -ENOMEM;
- }
-
- leaf = (struct gfs2_leaf *)bh->b_data;
- if (leaf->lf_entries) {
- dirent_first(dip, bh, &de);
- do {
- if (!de->de_inum.no_addr)
- continue;
- if (e >= entries) {
- gfs2_consist_inode(dip);
- error = -EIO;
- goto out;
- }
- darr[e++] = de;
- } while (dirent_next(dip, bh, &de) == 0);
- }
- ln = be64_to_cpu(leaf->lf_next);
+ error = -ENOMEM;
+ larr = kmalloc((leaves + entries) * sizeof(void*), GFP_KERNEL);
+ if (!larr)
+ goto out;
+ darr = (const struct gfs2_dirent **)(larr + leaves);
+ g.pdent = darr;
+ g.offset = 0;
+ lfn = leaf_no;
- while (ln) {
- error = get_leaf(dip, ln, &tmp_bh);
+ do {
+ error = get_leaf(ip, lfn, &bh);
if (error)
- goto out;
-
- leaf = (struct gfs2_leaf *)tmp_bh->b_data;
- if (leaf->lf_entries) {
- dirent_first(dip, tmp_bh, &de);
- do {
- if (!de->de_inum.no_addr)
- continue;
- if (e >= entries) {
- gfs2_consist_inode(dip);
- error = -EIO;
- goto out;
- }
- darr[e++] = de;
- } while (dirent_next(dip, tmp_bh, &de) == 0);
-
- larr[l++] = tmp_bh;
-
- ln = be64_to_cpu(leaf->lf_next);
+ goto out_kfree;
+ lf = (struct gfs2_leaf *)bh->b_data;
+ lfn = be64_to_cpu(lf->lf_next);
+ if (lf->lf_entries) {
+ dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size,
+ gfs2_dirent_gather, NULL, &g);
+ error = PTR_ERR(dent);
+ if (IS_ERR(dent)) {
+ goto out_kfree;
+ }
+ error = 0;
+ larr[leaf++] = bh;
} else {
- ln = be64_to_cpu(leaf->lf_next);
- brelse(tmp_bh);
+ brelse(bh);
}
- }
+ } while(lfn);
- if (gfs2_assert_withdraw(dip->i_sbd, l == leaves)) {
- error = -EIO;
- goto out;
- }
- if (e != entries) {
- gfs2_consist_inode(dip);
- error = -EIO;
- goto out;
- }
-
- error = do_filldir_main(dip, offset, opaque, filldir, darr,
+ error = do_filldir_main(ip, offset, opaque, filldir, darr,
entries, copied);
-
- out:
- kfree(darr);
- for (x = 0; x < l; x++)
- brelse(larr[x]);
+out_kfree:
+ for(i = 0; i < leaf; i++)
+ brelse(larr[i]);
kfree(larr);
-
+out:
return error;
}
@@ -1412,18 +1330,18 @@ static int do_filldir_multi(struct gfs2_inode *dip, uint64_t *offset,
* Returns: errno
*/
-static int dir_e_read(struct gfs2_inode *dip, uint64_t *offset, void *opaque,
+static int dir_e_read(struct inode *inode, uint64_t *offset, void *opaque,
gfs2_filldir_t filldir)
{
+ struct gfs2_inode *dip = inode->u.generic_ip;
struct gfs2_sbd *sdp = dip->i_sbd;
- struct buffer_head *bh;
- struct gfs2_leaf *leaf;
- uint32_t hsize, len;
+ uint32_t hsize, len = 0;
uint32_t ht_offset, lp_offset, ht_offset_cur = -1;
uint32_t hash, index;
uint64_t *lp;
int copied = 0;
int error = 0;
+ unsigned depth;
hsize = 1 << dip->i_di.di_depth;
if (hsize * sizeof(uint64_t) != dip->i_di.di_size) {
@@ -1454,61 +1372,66 @@ static int dir_e_read(struct gfs2_inode *dip, uint64_t *offset, void *opaque,
ht_offset_cur = ht_offset;
}
- error = get_leaf(dip, be64_to_cpu(lp[lp_offset]), &bh);
+ error = gfs2_dir_read_leaf(inode, offset, opaque, filldir,
+ &copied, &depth,
+ be64_to_cpu(lp[lp_offset]));
if (error)
- goto out;
-
- leaf = (struct gfs2_leaf *)bh->b_data;
- if (leaf->lf_next)
- error = do_filldir_multi(dip, offset, opaque, filldir,
- bh, &copied);
- else
- error = do_filldir_single(dip, offset, opaque, filldir,
- bh,
- be16_to_cpu(leaf->lf_entries),
- &copied);
-
- brelse(bh);
-
- if (error) {
- if (error > 0)
- error = 0;
- goto out;
- }
+ break;
- len = 1 << (dip->i_di.di_depth - be16_to_cpu(leaf->lf_depth));
+ len = 1 << (dip->i_di.di_depth - depth);
index = (index & ~(len - 1)) + len;
}
- out:
+out:
kfree(lp);
-
+ if (error > 0)
+ error = 0;
return error;
}
-static int dir_l_read(struct gfs2_inode *dip, uint64_t *offset, void *opaque,
- gfs2_filldir_t filldir)
+int gfs2_dir_read(struct inode *inode, uint64_t *offset, void *opaque,
+ gfs2_filldir_t filldir)
{
+ struct gfs2_inode *dip = inode->u.generic_ip;
+ struct dirent_gather g;
+ const struct gfs2_dirent **darr, *dent;
struct buffer_head *dibh;
int copied = 0;
int error;
+ if (!dip->i_di.di_entries)
+ return 0;
+
+ if (dip->i_di.di_flags & GFS2_DIF_EXHASH)
+ return dir_e_read(inode, offset, opaque, filldir);
+
if (!gfs2_is_stuffed(dip)) {
gfs2_consist_inode(dip);
return -EIO;
}
- if (!dip->i_di.di_entries)
- return 0;
-
error = gfs2_meta_inode_buffer(dip, &dibh);
if (error)
return error;
- error = do_filldir_single(dip, offset,
- opaque, filldir,
- dibh, dip->i_di.di_entries,
- &copied);
+ error = -ENOMEM;
+ darr = kmalloc(dip->i_di.di_entries * sizeof(struct gfs2_dirent *),
+ GFP_KERNEL);
+ if (darr) {
+ g.pdent = darr;
+ g.offset = 0;
+ dent = gfs2_dirent_scan(inode, dibh->b_data, dibh->b_size,
+ gfs2_dirent_gather, NULL, &g);
+ if (IS_ERR(dent)) {
+ error = PTR_ERR(dent);
+ goto out;
+ }
+ error = do_filldir_main(dip, offset, opaque, filldir, darr,
+ dip->i_di.di_entries, &copied);
+out:
+ kfree(darr);
+ }
+
if (error > 0)
error = 0;
@@ -1694,7 +1617,7 @@ int gfs2_dir_del(struct gfs2_inode *dip, const struct qstr *name)
return PTR_ERR(dent);
}
/* If not first in block, adjust pointers accordingly */
- if (gfs2_dirent_find(dent, name) == 0) {
+ if (gfs2_dirent_find(dent, name, NULL) == 0) {
prev = dent;
dent = (struct gfs2_dirent *)((char *)dent + be16_to_cpu(prev->de_rec_len));
}
@@ -1724,19 +1647,6 @@ int gfs2_dir_del(struct gfs2_inode *dip, const struct qstr *name)
return error;
}
-int gfs2_dir_read(struct gfs2_inode *dip, uint64_t *offset, void *opaque,
- gfs2_filldir_t filldir)
-{
- int error;
-
- if (dip->i_di.di_flags & GFS2_DIF_EXHASH)
- error = dir_e_read(dip, offset, opaque, filldir);
- else
- error = dir_l_read(dip, offset, opaque, filldir);
-
- return error;
-}
-
/**
* gfs2_dir_mvino - Change inode number of directory entry
* @dip: The GFS2 inode