vfs: make it possible to access the dentry hash/len as one 64-bit entry
[linux-3.10.git] / fs / gfs2 / dir.c
index 1cc2f8e..8aaeb07 100644 (file)
@@ -76,6 +76,8 @@
 #define IS_LEAF     1 /* Hashed (leaf) directory */
 #define IS_DINODE   2 /* Linear (stuffed dinode block) directory */
 
+#define MAX_RA_BLOCKS 32 /* max read-ahead blocks */
+
 #define gfs2_disk_hash2offset(h) (((u64)(h)) >> 1)
 #define gfs2_dir_offset2hash(p) ((u32)(((u64)(p)) << 1))
 
@@ -240,16 +242,15 @@ fail:
        return error;
 }
 
-static int gfs2_dir_read_stuffed(struct gfs2_inode *ip, char *buf,
-                                u64 offset, unsigned int size)
+static int gfs2_dir_read_stuffed(struct gfs2_inode *ip, __be64 *buf,
+                                unsigned int size)
 {
        struct buffer_head *dibh;
        int error;
 
        error = gfs2_meta_inode_buffer(ip, &dibh);
        if (!error) {
-               offset += sizeof(struct gfs2_dinode);
-               memcpy(buf, dibh->b_data + offset, size);
+               memcpy(buf, dibh->b_data + sizeof(struct gfs2_dinode), size);
                brelse(dibh);
        }
 
@@ -261,13 +262,12 @@ static int gfs2_dir_read_stuffed(struct gfs2_inode *ip, char *buf,
  * gfs2_dir_read_data - Read a data from a directory inode
  * @ip: The GFS2 Inode
  * @buf: The buffer to place result into
- * @offset: File offset to begin jdata_readng from
  * @size: Amount of data to transfer
  *
  * Returns: The amount of data actually copied or the error
  */
-static int gfs2_dir_read_data(struct gfs2_inode *ip, char *buf, u64 offset,
-                             unsigned int size, unsigned ra)
+static int gfs2_dir_read_data(struct gfs2_inode *ip, __be64 *buf,
+                             unsigned int size)
 {
        struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
        u64 lblock, dblock;
@@ -275,24 +275,14 @@ static int gfs2_dir_read_data(struct gfs2_inode *ip, char *buf, u64 offset,
        unsigned int o;
        int copied = 0;
        int error = 0;
-       u64 disksize = i_size_read(&ip->i_inode);
-
-       if (offset >= disksize)
-               return 0;
-
-       if (offset + size > disksize)
-               size = disksize - offset;
-
-       if (!size)
-               return 0;
 
        if (gfs2_is_stuffed(ip))
-               return gfs2_dir_read_stuffed(ip, buf, offset, size);
+               return gfs2_dir_read_stuffed(ip, buf, size);
 
        if (gfs2_assert_warn(sdp, gfs2_is_jdata(ip)))
                return -EINVAL;
 
-       lblock = offset;
+       lblock = 0;
        o = do_div(lblock, sdp->sd_jbsize) + sizeof(struct gfs2_meta_header);
 
        while (copied < size) {
@@ -311,8 +301,6 @@ static int gfs2_dir_read_data(struct gfs2_inode *ip, char *buf, u64 offset,
                        if (error || !dblock)
                                goto fail;
                        BUG_ON(extlen < 1);
-                       if (!ra)
-                               extlen = 1;
                        bh = gfs2_meta_ra(ip->i_gl, dblock, extlen);
                } else {
                        error = gfs2_meta_read(ip->i_gl, dblock, DIO_WAIT, &bh);
@@ -328,7 +316,7 @@ static int gfs2_dir_read_data(struct gfs2_inode *ip, char *buf, u64 offset,
                extlen--;
                memcpy(buf, bh->b_data + o, amount);
                brelse(bh);
-               buf += amount;
+               buf += (amount/sizeof(__be64));
                copied += amount;
                lblock++;
                o = sizeof(struct gfs2_meta_header);
@@ -371,7 +359,7 @@ static __be64 *gfs2_dir_get_hash_table(struct gfs2_inode *ip)
        if (hc == NULL)
                return ERR_PTR(-ENOMEM);
 
-       ret = gfs2_dir_read_data(ip, (char *)hc, 0, hsize, 1);
+       ret = gfs2_dir_read_data(ip, hc, hsize);
        if (ret < 0) {
                kfree(hc);
                return ERR_PTR(ret);
@@ -833,9 +821,9 @@ static struct gfs2_leaf *new_leaf(struct inode *inode, struct buffer_head **pbh,
        struct buffer_head *bh;
        struct gfs2_leaf *leaf;
        struct gfs2_dirent *dent;
-       struct qstr name = { .name = "", .len = 0, .hash = 0 };
+       struct qstr name = { .name = "" };
 
-       error = gfs2_alloc_block(ip, &bn, &n);
+       error = gfs2_alloc_blocks(ip, &bn, &n, 0, NULL);
        if (error)
                return NULL;
        bh = gfs2_meta_new(ip->i_gl, bn);
@@ -1390,6 +1378,52 @@ out:
        return error;
 }
 
+/**
+ * gfs2_dir_readahead - Issue read-ahead requests for leaf blocks.
+ *
+ * Note: we can't calculate each index like dir_e_read can because we don't
+ * have the leaf, and therefore we don't have the depth, and therefore we
+ * don't have the length. So we have to just read enough ahead to make up
+ * for the loss of information.
+ */
+static void gfs2_dir_readahead(struct inode *inode, unsigned hsize, u32 index,
+                              struct file_ra_state *f_ra)
+{
+       struct gfs2_inode *ip = GFS2_I(inode);
+       struct gfs2_glock *gl = ip->i_gl;
+       struct buffer_head *bh;
+       u64 blocknr = 0, last;
+       unsigned count;
+
+       /* First check if we've already read-ahead for the whole range. */
+       if (index + MAX_RA_BLOCKS < f_ra->start)
+               return;
+
+       f_ra->start = max((pgoff_t)index, f_ra->start);
+       for (count = 0; count < MAX_RA_BLOCKS; count++) {
+               if (f_ra->start >= hsize) /* if exceeded the hash table */
+                       break;
+
+               last = blocknr;
+               blocknr = be64_to_cpu(ip->i_hash_cache[f_ra->start]);
+               f_ra->start++;
+               if (blocknr == last)
+                       continue;
+
+               bh = gfs2_getbuf(gl, blocknr, 1);
+               if (trylock_buffer(bh)) {
+                       if (buffer_uptodate(bh)) {
+                               unlock_buffer(bh);
+                               brelse(bh);
+                               continue;
+                       }
+                       bh->b_end_io = end_buffer_read_sync;
+                       submit_bh(READA | REQ_META, bh);
+                       continue;
+               }
+               brelse(bh);
+       }
+}
 
 /**
  * dir_e_read - Reads the entries from a directory into a filldir buffer
@@ -1402,7 +1436,7 @@ out:
  */
 
 static int dir_e_read(struct inode *inode, u64 *offset, void *opaque,
-                     filldir_t filldir)
+                     filldir_t filldir, struct file_ra_state *f_ra)
 {
        struct gfs2_inode *dip = GFS2_I(inode);
        u32 hsize, len = 0;
@@ -1416,10 +1450,14 @@ static int dir_e_read(struct inode *inode, u64 *offset, void *opaque,
        hash = gfs2_dir_offset2hash(*offset);
        index = hash >> (32 - dip->i_depth);
 
+       if (dip->i_hash_cache == NULL)
+               f_ra->start = 0;
        lp = gfs2_dir_get_hash_table(dip);
        if (IS_ERR(lp))
                return PTR_ERR(lp);
 
+       gfs2_dir_readahead(inode, hsize, index, f_ra);
+
        while (index < hsize) {
                error = gfs2_dir_read_leaf(inode, offset, opaque, filldir,
                                           &copied, &depth,
@@ -1437,7 +1475,7 @@ static int dir_e_read(struct inode *inode, u64 *offset, void *opaque,
 }
 
 int gfs2_dir_read(struct inode *inode, u64 *offset, void *opaque,
-                 filldir_t filldir)
+                 filldir_t filldir, struct file_ra_state *f_ra)
 {
        struct gfs2_inode *dip = GFS2_I(inode);
        struct gfs2_sbd *sdp = GFS2_SB(inode);
@@ -1451,7 +1489,7 @@ int gfs2_dir_read(struct inode *inode, u64 *offset, void *opaque,
                return 0;
 
        if (dip->i_diskflags & GFS2_DIF_EXHASH)
-               return dir_e_read(inode, offset, opaque, filldir);
+               return dir_e_read(inode, offset, opaque, filldir, f_ra);
 
        if (!gfs2_is_stuffed(dip)) {
                gfs2_consist_inode(dip);
@@ -1695,7 +1733,6 @@ int gfs2_dir_del(struct gfs2_inode *dip, const struct dentry *dentry)
        const struct qstr *name = &dentry->d_name;
        struct gfs2_dirent *dent, *prev = NULL;
        struct buffer_head *bh;
-       int error;
 
        /* Returns _either_ the entry (if its first in block) or the
           previous entry otherwise */
@@ -1724,22 +1761,15 @@ int gfs2_dir_del(struct gfs2_inode *dip, const struct dentry *dentry)
        }
        brelse(bh);
 
-       error = gfs2_meta_inode_buffer(dip, &bh);
-       if (error)
-               return error;
-
        if (!dip->i_entries)
                gfs2_consist_inode(dip);
-       gfs2_trans_add_bh(dip->i_gl, bh, 1);
        dip->i_entries--;
        dip->i_inode.i_mtime = dip->i_inode.i_ctime = CURRENT_TIME;
        if (S_ISDIR(dentry->d_inode->i_mode))
                drop_nlink(&dip->i_inode);
-       gfs2_dinode_out(dip, bh->b_data);
-       brelse(bh);
        mark_inode_dirty(&dip->i_inode);
 
-       return error;
+       return 0;
 }
 
 /**
@@ -1814,13 +1844,17 @@ static int leaf_dealloc(struct gfs2_inode *dip, u32 index, u32 len,
        unsigned int x, size = len * sizeof(u64);
        int error;
 
+       error = gfs2_rindex_update(sdp);
+       if (error)
+               return error;
+
        memset(&rlist, 0, sizeof(struct gfs2_rgrp_list));
 
        ht = kzalloc(size, GFP_NOFS);
        if (!ht)
                return -ENOMEM;
 
-       if (!gfs2_alloc_get(dip)) {
+       if (!gfs2_qadata_get(dip)) {
                error = -ENOMEM;
                goto out;
        }
@@ -1829,10 +1863,6 @@ static int leaf_dealloc(struct gfs2_inode *dip, u32 index, u32 len,
        if (error)
                goto out_put;
 
-       error = gfs2_rindex_hold(sdp, &dip->i_alloc->al_ri_gh);
-       if (error)
-               goto out_qs;
-
        /*  Count the number of leaves  */
        bh = leaf_bh;
 
@@ -1847,7 +1877,7 @@ static int leaf_dealloc(struct gfs2_inode *dip, u32 index, u32 len,
                if (blk != leaf_no)
                        brelse(bh);
 
-               gfs2_rlist_add(sdp, &rlist, blk);
+               gfs2_rlist_add(dip, &rlist, blk);
                l_blocks++;
        }
 
@@ -1911,11 +1941,9 @@ out_rg_gunlock:
        gfs2_glock_dq_m(rlist.rl_rgrps, rlist.rl_ghs);
 out_rlist:
        gfs2_rlist_free(&rlist);
-       gfs2_glock_dq_uninit(&dip->i_alloc->al_ri_gh);
-out_qs:
        gfs2_quota_unhold(dip);
 out_put:
-       gfs2_alloc_put(dip);
+       gfs2_qadata_put(dip);
 out:
        kfree(ht);
        return error;