- struct au_nhash supports the variable size of hash table.
- struct au_vdir supports the variable size of deblk.
- remove AuSize_DEBLK and AuSize_NHASH macros.
- replace au_nhash_{new,del,init,move,fin}() by
  au_nhash_{alloc,wh_free}().
- rename struct au_whtmp_rmdir_args to struct au_whtmp_rmdir which
  contains au_nhash.
- new functions au_whtmp_rmdir_{alloc,free}().

- support for variable hash table.
  au_dpri_whlist(), au_test_empty_lower(), aufs_rmdir(),
  au_ren_del_whtmp(), do_rename(), may_rename_srcdir(), aufs_rename(),
  del_wh_children(), au_whtmp_rmdir_alloc(), au_whtmp_rmdir_free(),
  call_rmdir_whtmp(), au_whtmp_kick_rmdir().
- support for variable deblk.
  au_dpri_vdir(), vdir.c entirely.

Signed-off-by: J. R. Okajima <[email protected]>
---
 fs/aufs/debug.c    |   24 ++--
 fs/aufs/dir.c      |   13 +-
 fs/aufs/dir.h      |   33 ++---
 fs/aufs/i_op_del.c |   24 +--
 fs/aufs/i_op_ren.c |   39 +++--
 fs/aufs/vdir.c     |  438 ++++++++++++++++++++++++++--------------------------
 fs/aufs/whout.c    |   53 +++++--
 fs/aufs/whout.h    |    7 +-
 8 files changed, 325 insertions(+), 306 deletions(-)

diff --git a/fs/aufs/debug.c b/fs/aufs/debug.c
index 2feba39..3339b16 100644
--- a/fs/aufs/debug.c
+++ b/fs/aufs/debug.c
@@ -37,24 +37,26 @@ char *au_plevel = KERN_DEBUG;
 
 void au_dpri_whlist(struct au_nhash *whlist)
 {
-       int i;
+       unsigned long ul, n;
        struct hlist_head *head;
        struct au_vdir_wh *tpos;
        struct hlist_node *pos;
 
-       for (i = 0; i < AuSize_NHASH; i++) {
-               head = whlist->heads + i;
+       n = whlist->nh_num;
+       head = whlist->nh_head;
+       for (ul = 0; ul < n; ul++) {
                hlist_for_each_entry(tpos, pos, head, wh_hash)
                        dpri("b%d, %.*s, %d\n",
                             tpos->wh_bindex,
                             tpos->wh_str.len, tpos->wh_str.name,
                             tpos->wh_str.len);
+               head++;
        }
 }
 
 void au_dpri_vdir(struct au_vdir *vdir)
 {
-       int i;
+       unsigned long ul;
        union au_vdir_deblk_p p;
        unsigned char *o;
 
@@ -63,13 +65,13 @@ void au_dpri_vdir(struct au_vdir *vdir)
                return;
        }
 
-       dpri("nblk %d, deblk %p, last{%d, %p}, ver %lu\n",
-            vdir->vd_nblk, vdir->vd_deblk,
-            vdir->vd_last.i, vdir->vd_last.p.p, vdir->vd_version);
-       for (i = 0; i < vdir->vd_nblk; i++) {
-               p.deblk = vdir->vd_deblk[i];
-               o = p.p;
-               dpri("[%d]: %p\n", i, o);
+       dpri("deblk %u, nblk %lu, deblk %p, last{%lu, %p}, ver %lu\n",
+            vdir->vd_deblk_sz, vdir->vd_nblk, vdir->vd_deblk,
+            vdir->vd_last.ul, vdir->vd_last.p.deblk, vdir->vd_version);
+       for (ul = 0; ul < vdir->vd_nblk; ul++) {
+               p.deblk = vdir->vd_deblk[ul];
+               o = p.deblk;
+               dpri("[%lu]: %p\n", ul, o);
 #if 0 // verbose
                int j;
                for (j = 0; j < 8; j++) {
diff --git a/fs/aufs/dir.c b/fs/aufs/dir.c
index 599d7f2..c722345 100644
--- a/fs/aufs/dir.c
+++ b/fs/aufs/dir.c
@@ -518,9 +518,10 @@ static int sio_test_empty(struct dentry *dentry, struct 
test_empty_arg *arg)
 int au_test_empty_lower(struct dentry *dentry)
 {
        int err;
+       unsigned int rdhash;
        struct inode *inode;
+       struct au_nhash whlist;
        struct test_empty_arg arg;
-       struct au_nhash *whlist;
        aufs_bindex_t bindex, bstart, btail;
        unsigned int mnt_flags;
 
@@ -528,15 +529,15 @@ int au_test_empty_lower(struct dentry *dentry)
        inode = dentry->d_inode;
        AuDebugOn(!inode || !S_ISDIR(inode->i_mode));
 
-       whlist = au_nhash_new(GFP_NOFS);
-       err = PTR_ERR(whlist);
-       if (IS_ERR(whlist))
+       rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, dentry));
+       err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS);
+       if (unlikely(err))
                goto out;
 
        bstart = au_dbstart(dentry);
        mnt_flags = au_mntflags(dentry->d_sb);
-       arg.whlist = whlist;
        arg.flags = 0;
+       arg.whlist = &whlist;
        if (au_test_dlgt(mnt_flags))
                au_fset_testempty(arg.flags, DLGT);
        if (au_opt_test(mnt_flags, SHWH))
@@ -560,7 +561,7 @@ int au_test_empty_lower(struct dentry *dentry)
        }
 
  out_whlist:
-       au_nhash_del(whlist);
+       au_nhash_wh_free(&whlist);
  out:
        AuTraceErr(err);
        return err;
diff --git a/fs/aufs/dir.h b/fs/aufs/dir.h
index 5cf5159..161a2fd 100644
--- a/fs/aufs/dir.h
+++ b/fs/aufs/dir.h
@@ -39,24 +39,14 @@ typedef ino_t       au_filldir_ino_t;
 
 /* ---------------------------------------------------------------------- */
 
-/* need to be faster and smaller */
-
-/* todo: changeable? */
-#define AuSize_DEBLK   512
-#define AuSize_NHASH   32
-#if AuSize_DEBLK < NAME_MAX || PAGE_SIZE < AuSize_DEBLK
-#error invalid size AuSize_DEBLK
-#endif
-
-typedef char au_vdir_deblk_t[AuSize_DEBLK];
-
 struct au_nhash {
-       struct hlist_head heads[AuSize_NHASH];
+       unsigned int            nh_num;
+       struct hlist_head       *nh_head;
 };
 
 struct au_vdir_destr {
        unsigned char   len;
-       char            name[0];
+       unsigned char   name[0];
 } __packed;
 
 struct au_vdir_dehstr {
@@ -83,16 +73,16 @@ struct au_vdir_wh {
 } __packed;
 
 union au_vdir_deblk_p {
-       unsigned char           *p;
-       au_vdir_deblk_t         *deblk;
+       unsigned char           *deblk;
        struct au_vdir_de       *de;
 };
 
 struct au_vdir {
-       au_vdir_deblk_t **vd_deblk;
-       int             vd_nblk;
+       unsigned char   **vd_deblk;
+       unsigned long   vd_nblk;
+       unsigned int    vd_deblk_sz;
        struct {
-               int                     i;
+               unsigned long           ul;
                union au_vdir_deblk_p   p;
        } vd_last;
 
@@ -110,11 +100,8 @@ int au_test_empty(struct dentry *dentry, struct au_nhash 
*whlist);
 
 /* vdir.c */
 unsigned int au_rdhash_est(loff_t sz);
-struct au_nhash *au_nhash_new(gfp_t gfp);
-void au_nhash_del(struct au_nhash *nhash);
-void au_nhash_init(struct au_nhash *nhash);
-void au_nhash_move(struct au_nhash *dst, struct au_nhash *src);
-void au_nhash_fin(struct au_nhash *nhash);
+int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp);
+void au_nhash_wh_free(struct au_nhash *whlist);
 int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt,
                            int limit);
 int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int namelen);
diff --git a/fs/aufs/i_op_del.c b/fs/aufs/i_op_del.c
index e2ce684..909ac92 100644
--- a/fs/aufs/i_op_del.c
+++ b/fs/aufs/i_op_del.c
@@ -456,8 +456,7 @@ int aufs_rmdir(struct inode *dir, struct dentry *dentry)
        struct dentry *parent, *wh_dentry, *h_dentry;
        struct au_dtime dt;
        aufs_bindex_t bwh, bindex, bstart;
-       struct au_whtmp_rmdir_args *args;
-       struct au_nhash *whlist;
+       struct au_whtmp_rmdir *args;
        struct super_block *sb;
        unsigned int mnt_flags;
        struct au_pin pin;
@@ -470,20 +469,15 @@ int aufs_rmdir(struct inode *dir, struct dentry *dentry)
                goto out;
        IMustLock(inode);
 
-       whlist = au_nhash_new(GFP_NOFS);
-       err = PTR_ERR(whlist);
-       if (IS_ERR(whlist))
-               goto out;
-
        err = -ENOMEM;
-       args = kmalloc(sizeof(*args), GFP_NOFS);
+       args = au_whtmp_rmdir_alloc(dir->i_sb, GFP_NOFS);
        if (unlikely(!args))
-               goto out_whlist;
+               goto out;
 
        aufs_read_lock(dentry, AuLock_DW);
        parent = dentry->d_parent; /* dir inode is locked */
        di_write_lock_parent(parent);
-       err = au_test_empty(dentry, whlist);
+       err = au_test_empty(dentry, &args->whlist);
        if (unlikely(err))
                goto out_args;
 
@@ -501,7 +495,7 @@ int aufs_rmdir(struct inode *dir, struct dentry *dentry)
 
        rmdir_later = 0;
        if (bindex == bstart) {
-               err = renwh_and_rmdir(dentry, bstart, whlist, dir);
+               err = renwh_and_rmdir(dentry, bstart, &args->whlist, dir);
                if (err > 0) {
                        rmdir_later = err;
                        err = 0;
@@ -525,8 +519,7 @@ int aufs_rmdir(struct inode *dir, struct dentry *dentry)
                epilog(dir, dentry, bindex);
 
                if (rmdir_later) {
-                       au_whtmp_kick_rmdir(dir, bstart, h_dentry, whlist,
-                                           args);
+                       au_whtmp_kick_rmdir(dir, bstart, h_dentry, args);
                        args = NULL;
                }
 
@@ -555,9 +548,8 @@ int aufs_rmdir(struct inode *dir, struct dentry *dentry)
  out_args:
        di_write_unlock(parent);
        aufs_read_unlock(dentry, AuLock_DW);
-       kfree(args);
- out_whlist:
-       au_nhash_del(whlist);
+       if (args)
+               au_whtmp_rmdir_free(args);
  out:
        AuTraceErr(err);
        if (unlikely(err == -EBUSY && au_test_nfsd(current)))
diff --git a/fs/aufs/i_op_ren.c b/fs/aufs/i_op_ren.c
index 903da95..2b26b30 100644
--- a/fs/aufs/i_op_ren.c
+++ b/fs/aufs/i_op_ren.c
@@ -69,7 +69,7 @@ struct au_ren_args {
        struct au_hin_ignore ign[2];
 #endif
        struct vfsub_args vargs;
-       struct au_whtmp_rmdir_args *thargs;
+       struct au_whtmp_rmdir *thargs;
        struct dentry *wh_dentry[2], *h_dst, *h_src;
 };
 
@@ -255,8 +255,10 @@ int au_ren_del_whtmp(struct au_ren_args *a)
                        AuWarn("failed removing whtmp dir %.*s (%d), "
                               "ignored.\n", AuDLNPair(a->h_dst), err);
        } else {
-               au_whtmp_kick_rmdir(a->dir, a->btgt, a->h_dst, &a->whlist,
-                                   a->thargs);
+               au_nhash_wh_free(&a->thargs->whlist);
+               a->thargs->whlist = a->whlist;
+               a->whlist.nh_num = 0;
+               au_whtmp_kick_rmdir(a->dir, a->btgt, a->h_dst, a->thargs);
                dput(a->h_dst);
                a->thargs = NULL;
        }
@@ -307,7 +309,7 @@ int do_rename(struct au_ren_args *a)
        /* prepare workqueue args */
        if (au_ftest_ren(a->flags, ISDIR) && a->h_dentry[DST]->d_inode) {
                err = -ENOMEM;
-               a->thargs = kmalloc(sizeof(*a->thargs), GFP_NOFS);
+               a->thargs = au_whtmp_rmdir_alloc(a->src_dentry->d_sb, GFP_NOFS);
                if (unlikely(!a->thargs))
                        goto out;
                a->h_dst = dget(a->h_dentry[DST]);
@@ -456,7 +458,8 @@ int do_rename(struct au_ren_args *a)
  out_thargs:
        if (a->thargs) {
                dput(a->h_dst);
-               kfree(a->thargs);
+               au_whtmp_rmdir_free(a->thargs);
+               a->thargs = NULL;
        }
  out:
        AuTraceErr(err);
@@ -492,14 +495,15 @@ static int may_rename_srcdir(struct dentry *dentry, 
aufs_bindex_t btgt)
 
        bstart = au_dbstart(dentry);
        if (bstart != btgt) {
-               struct au_nhash *whlist;
+               struct au_nhash whlist;
+               unsigned int rdhash;
 
-               whlist = au_nhash_new(GFP_NOFS);
-               err = PTR_ERR(whlist);
-               if (IS_ERR(whlist))
+               rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, dentry));
+               err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS);
+               if (unlikely(err))
                        goto out;
-               err = au_test_empty(dentry, whlist);
-               au_nhash_del(whlist);
+               err = au_test_empty(dentry, &whlist);
+               au_nhash_wh_free(&whlist);
                goto out;
        }
 
@@ -990,6 +994,7 @@ int aufs_rename(struct inode *src_dir, struct dentry 
*src_dentry,
                struct inode *dir, struct dentry *dentry)
 {
        int err;
+       unsigned int rdhash;
        aufs_bindex_t bend, bindex;
        unsigned char do_dt_dstdir, hinotify;
        struct inode *inode[2];
@@ -1062,15 +1067,19 @@ int aufs_rename(struct inode *src_dir, struct dentry 
*src_dentry,
 
        /* are they available to be renamed */
        err = 0;
-       au_nhash_init(&p->a.whlist);
        if (au_ftest_ren(p->a.flags, ISDIR) && inode[DST]) {
+               rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, dentry));
+               err = au_nhash_alloc(&p->a.whlist, rdhash, GFP_NOFS);
+               if (unlikely(err))
+                       goto out_unlock;
+
                au_set_dbstart(dentry, p->a.bstart[DST]);
                err = may_rename_dstdir(dentry, p->a.btgt, &p->a.whlist);
                au_set_dbstart(dentry, p->a.btgt);
        }
        p->a.h_dentry[DST] = au_h_dptr(dentry, au_dbstart(dentry));
        if (unlikely(err))
-               goto out_unlock;
+               goto out_children;
        /* todo: minor optimize,
           their sb may be same while their bindex differs? */
        p->a.h_dentry[SRC] = au_h_dptr(src_dentry, au_dbstart(src_dentry));
@@ -1233,7 +1242,7 @@ int aufs_rename(struct inode *src_dir, struct dentry 
*src_dentry,
  out_hdir:
        au_ren_unlock(&p->a);
  out_children:
-       au_nhash_fin(&p->a.whlist);
+       au_nhash_wh_free(&p->a.whlist);
  out_unlock:
        if (unlikely(err && au_ftest_ren(p->a.flags, ISDIR))) {
                au_update_dbstart(dentry);
@@ -1254,6 +1263,8 @@ int aufs_rename(struct inode *src_dir, struct dentry 
*src_dentry,
                di_write_unlock2(p->a.parent[SRC], p->a.parent[DST]);
        aufs_read_and_write_unlock2(dentry, src_dentry);
  out_free:
+       if (p->a.thargs)
+               au_whtmp_rmdir_free(p->a.thargs);
        kfree(p);
  out:
        iput(inode[DST]);
diff --git a/fs/aufs/vdir.c b/fs/aufs/vdir.c
index ea9ef37..e3564fd 100644
--- a/fs/aufs/vdir.c
+++ b/fs/aufs/vdir.c
@@ -24,27 +24,16 @@
 
 #include "aufs.h"
 
-static int calc_size(int namelen)
+static unsigned int calc_size(int namelen)
 {
-       int sz;
-
-       sz = sizeof(struct au_vdir_de) + namelen;
-       if (sizeof(ino_t) == sizeof(long)) {
-               const int mask = sizeof(ino_t) - 1;
-               if (sz & mask) {
-                       sz += sizeof(ino_t);
-                       sz &= ~mask;
-               }
-       }
-
-       AuDebugOn(sz % sizeof(ino_t));
-       return sz;
+       BUILD_BUG_ON(sizeof(ino_t) != sizeof(long));
+       return ALIGN(sizeof(struct au_vdir_de) + namelen, sizeof(ino_t));
 }
 
 static int set_deblk_end(union au_vdir_deblk_p *p,
                         union au_vdir_deblk_p *deblk_end)
 {
-       if (calc_size(0) <= deblk_end->p - p->p) {
+       if (calc_size(0) <= deblk_end->deblk - p->deblk) {
                p->de->de_str.len = 0;
                /* smp_mb(); */
                return 0;
@@ -56,16 +45,18 @@ static int set_deblk_end(union au_vdir_deblk_p *p,
 static int is_deblk_end(union au_vdir_deblk_p *p,
                        union au_vdir_deblk_p *deblk_end)
 {
-       if (calc_size(0) <= deblk_end->p - p->p)
+       if (calc_size(0) <= deblk_end->deblk - p->deblk)
                return !p->de->de_str.len;
        return 1;
 }
 
-static au_vdir_deblk_t *last_deblk(struct au_vdir *vdir)
+static unsigned char *last_deblk(struct au_vdir *vdir)
 {
        return vdir->vd_deblk[vdir->vd_nblk - 1];
 }
 
+/* ---------------------------------------------------------------------- */
+
 /* estimate the apropriate size for name hash table */
 unsigned int au_rdhash_est(loff_t sz)
 {
@@ -81,91 +72,115 @@ unsigned int au_rdhash_est(loff_t sz)
        return n;
 }
 
-void au_nhash_init(struct au_nhash *nhash)
+int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp)
 {
-       int i;
-       for (i = 0; i < AuSize_NHASH; i++)
-               INIT_HLIST_HEAD(nhash->heads + i);
+       struct hlist_head *head;
+       unsigned int u;
+
+       head = kmalloc(sizeof(*nhash->nh_head) * num_hash, gfp);
+       if (head) {
+               nhash->nh_num = num_hash;
+               nhash->nh_head = head;
+               for (u = 0; u < num_hash; u++)
+                       INIT_HLIST_HEAD(head++);
+               return 0; /* success */
+       }
+
+       return -ENOMEM;
 }
 
-struct au_nhash *au_nhash_new(gfp_t gfp)
+static void au_nhash_wh_do_free(struct hlist_head *head)
 {
-       struct au_nhash *nhash;
+       struct au_vdir_wh *tpos;
+       struct hlist_node *pos, *node;
 
-       nhash = kmalloc(sizeof(*nhash), gfp);
-       if (nhash) {
-               au_nhash_init(nhash);
-               return nhash;
+       hlist_for_each_entry_safe(tpos, pos, node, head, wh_hash) {
+               /* hlist_del(pos); */
+               kfree(tpos);
        }
-       return ERR_PTR(-ENOMEM);
 }
 
-void au_nhash_del(struct au_nhash *nhash)
+static void au_nhash_de_do_free(struct hlist_head *head)
 {
-       au_nhash_fin(nhash);
-       kfree(nhash);
+       struct au_vdir_dehstr *tpos;
+       struct hlist_node *pos, *node;
+
+       hlist_for_each_entry_safe(tpos, pos, node, head, hash) {
+               /* hlist_del(pos); */
+               au_cache_free_dehstr(tpos);
+       }
 }
 
-void au_nhash_move(struct au_nhash *dst, struct au_nhash *src)
+static void au_nhash_do_free(struct au_nhash *nhash,
+                            void (*free)(struct hlist_head *head))
 {
-       int i;
+       unsigned int n;
+       struct hlist_head *head;
 
-       AuTraceEnter();
+       n = nhash->nh_num;
+       if (!n)
+               return;
 
-       *dst = *src;
-       for (i = 0; i < AuSize_NHASH; i++) {
-               struct hlist_head *h;
-               h = dst->heads + i;
-               if (h->first)
-                       h->first->pprev = &h->first;
-               INIT_HLIST_HEAD(src->heads + i);
-       }
-       /* smp_mb(); */
+       head = nhash->nh_head;
+       while (n-- > 0)
+               free(head++);
+       kfree(nhash->nh_head);
 }
 
-/* ---------------------------------------------------------------------- */
-
-void au_nhash_fin(struct au_nhash *whlist)
+void au_nhash_wh_free(struct au_nhash *whlist)
 {
-       int i;
-       struct hlist_head *head;
-       struct au_vdir_wh *tpos;
-       struct hlist_node *pos, *n;
-
-       AuTraceEnter();
+       au_nhash_do_free(whlist, au_nhash_wh_do_free);
+}
 
-       for (i = 0; i < AuSize_NHASH; i++) {
-               head = whlist->heads + i;
-               hlist_for_each_entry_safe(tpos, pos, n, head, wh_hash) {
-                       /* hlist_del(pos); */
-                       kfree(tpos);
-               }
-       }
+static void au_nhash_de_free(struct au_nhash *delist)
+{
+       au_nhash_do_free(delist, au_nhash_de_do_free);
 }
 
+/* ---------------------------------------------------------------------- */
+
 int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt,
                            int limit)
 {
-       int n, i;
+       int num;
+       unsigned int u, n;
        struct hlist_head *head;
        struct au_vdir_wh *tpos;
        struct hlist_node *pos;
 
        LKTRTrace("limit %d\n", limit);
 
-       n = 0;
-       for (i = 0; i < AuSize_NHASH; i++) {
-               head = whlist->heads + i;
+       num = 0;
+       n = whlist->nh_num;
+       head = whlist->nh_head;
+       for (u = 0; u < n; u++, head++)
                hlist_for_each_entry(tpos, pos, head, wh_hash)
-                       if (tpos->wh_bindex == btgt && ++n > limit)
+                       if (tpos->wh_bindex == btgt && ++num > limit)
                                return 1;
-       }
        return 0;
 }
 
-static unsigned int au_name_hash(const unsigned char *name, unsigned int len)
+static struct hlist_head *au_name_hash(struct au_nhash *nhash,
+                                      unsigned char *name,
+                                      unsigned int len)
+{
+       unsigned int v;
+       /* const unsigned int magic_bit = 12; */
+
+       AuDebugOn(!nhash->nh_num || !nhash->nh_head);
+
+       v = 0;
+       while (len--)
+               v += *name++;
+       /* v = hash_long(v, magic_bit); */
+       v %= nhash->nh_num;
+       return nhash->nh_head + v;
+}
+
+static int au_nhash_test_name(struct au_vdir_destr *str, const char *name,
+                             int namelen)
 {
-       return full_name_hash(name, len) % AuSize_NHASH;
+       return str->len == namelen && !memcmp(str->name, name, namelen);
 }
 
 /* returns found(true) or not */
@@ -178,11 +193,11 @@ int au_nhash_test_known_wh(struct au_nhash *whlist, char 
*name, int namelen)
 
        LKTRTrace("%.*s\n", namelen, name);
 
-       head = whlist->heads + au_name_hash(name, namelen);
+       head = au_name_hash(whlist, name, namelen);
        hlist_for_each_entry(tpos, pos, head, wh_hash) {
                str = &tpos->wh_str;
                LKTRTrace("%.*s\n", str->len, str->name);
-               if (str->len == namelen && !memcmp(str->name, name, namelen))
+               if (au_nhash_test_name(str, name, namelen))
                        return 1;
        }
        return 0;
@@ -197,6 +212,7 @@ int au_nhash_append_wh(struct au_nhash *whlist, char *name, 
int namelen,
        struct au_vdir_wh *wh;
 
        LKTRTrace("%.*s\n", namelen, name);
+       AuDebugOn(!whlist->nh_num || !whlist->nh_head);
 
        err = -ENOMEM;
        wh = kmalloc(sizeof(*wh) + namelen, GFP_NOFS);
@@ -209,8 +225,7 @@ int au_nhash_append_wh(struct au_nhash *whlist, char *name, 
int namelen,
        str = &wh->wh_str;
        str->len = namelen;
        memcpy(str->name, name, namelen);
-       hlist_add_head(&wh->wh_hash,
-                      whlist->heads + au_name_hash(name, namelen));
+       hlist_add_head(&wh->wh_hash, au_name_hash(whlist, name, namelen));
        /* smp_mb(); */
 
  out:
@@ -222,24 +237,24 @@ int au_nhash_append_wh(struct au_nhash *whlist, char 
*name, int namelen,
 
 void au_vdir_free(struct au_vdir *vdir)
 {
-       au_vdir_deblk_t **deblk;
+       unsigned char **deblk;
 
        AuTraceEnter();
 
        deblk = vdir->vd_deblk;
-       while (vdir->vd_nblk--) {
-               kfree(*deblk);
-               deblk++;
-       }
+       while (vdir->vd_nblk--)
+               kfree(*deblk++);
        kfree(vdir->vd_deblk);
        au_cache_free_vdir(vdir);
 }
 
 static int append_deblk(struct au_vdir *vdir)
 {
-       int err, sz, i;
-       au_vdir_deblk_t **o;
+       int err, sz;
+       unsigned long ul;
+       const unsigned int deblk_sz = vdir->vd_deblk_sz;
        union au_vdir_deblk_p p, deblk_end;
+       unsigned char **o;
 
        AuTraceEnter();
 
@@ -249,13 +264,15 @@ static int append_deblk(struct au_vdir *vdir)
        if (unlikely(!o))
                goto out;
        vdir->vd_deblk = o;
-       p.deblk = kmalloc(sizeof(*p.deblk), GFP_NOFS);
+       LKTRTrace("%u\n", deblk_sz);
+       p.deblk = kmalloc(deblk_sz, GFP_NOFS);
+       LKTRTrace("%p\n", p.deblk);
        if (p.deblk) {
-               i = vdir->vd_nblk++;
-               vdir->vd_deblk[i] = p.deblk;
-               vdir->vd_last.i = i;
-               vdir->vd_last.p.p = p.p;
-               deblk_end.deblk = p.deblk + 1;
+               ul = vdir->vd_nblk++;
+               vdir->vd_deblk[ul] = p.deblk;
+               vdir->vd_last.ul = ul;
+               vdir->vd_last.p.deblk = p.deblk;
+               deblk_end.deblk = p.deblk + deblk_sz;
                err = set_deblk_end(&p, &deblk_end);
                AuDebugOn(err);
        }
@@ -265,7 +282,7 @@ static int append_deblk(struct au_vdir *vdir)
        return err;
 }
 
-static struct au_vdir *alloc_vdir(void)
+static struct au_vdir *alloc_vdir(struct file *file)
 {
        struct au_vdir *vdir;
        int err;
@@ -281,6 +298,8 @@ static struct au_vdir *alloc_vdir(void)
                goto out_free;
 
        vdir->vd_nblk = 0;
+       /* estimate the apropriate size for deblk */
+       vdir->vd_deblk_sz = au_dir_size(file, /*dentry*/NULL);
        vdir->vd_version = 0;
        vdir->vd_jiffy = 0;
        err = append_deblk(vdir);
@@ -310,12 +329,12 @@ static int reinit_vdir(struct au_vdir *vdir)
                vdir->vd_nblk--;
        }
        p.deblk = vdir->vd_deblk[0];
-       deblk_end.deblk = p.deblk + 1;
+       deblk_end.deblk = p.deblk + vdir->vd_deblk_sz;
        err = set_deblk_end(&p, &deblk_end);
        AuDebugOn(err);
        vdir->vd_version = 0;
        vdir->vd_jiffy = 0;
-       vdir->vd_last.i = 0;
+       vdir->vd_last.ul = 0;
        vdir->vd_last.p.deblk = vdir->vd_deblk[0];
        /* smp_mb(); */
        return err;
@@ -323,24 +342,6 @@ static int reinit_vdir(struct au_vdir *vdir)
 
 /* ---------------------------------------------------------------------- */
 
-static void free_dehlist(struct au_nhash *dehlist)
-{
-       int i;
-       struct hlist_head *head;
-       struct au_vdir_dehstr *tpos;
-       struct hlist_node *pos, *n;
-
-       AuTraceEnter();
-
-       for (i = 0; i < AuSize_NHASH; i++) {
-               head = dehlist->heads + i;
-               hlist_for_each_entry_safe(tpos, pos, n, head, hash) {
-                       /* hlist_del(pos); */
-                       au_cache_free_dehstr(tpos);
-               }
-       }
-}
-
 /* returns found(true) or not */
 static int test_known(struct au_nhash *delist, char *name, int namelen)
 {
@@ -351,11 +352,11 @@ static int test_known(struct au_nhash *delist, char 
*name, int namelen)
 
        LKTRTrace("%.*s\n", namelen, name);
 
-       head = delist->heads + au_name_hash(name, namelen);
+       head = au_name_hash(delist, name, namelen);
        hlist_for_each_entry(tpos, pos, head, hash) {
                str = tpos->str;
                LKTRTrace("%.*s\n", str->len, str->name);
-               if (str->len == namelen && !memcmp(str->name, name, namelen))
+               if (au_nhash_test_name(str, name, namelen))
                        return 1;
        }
        return 0;
@@ -365,7 +366,9 @@ static int test_known(struct au_nhash *delist, char *name, 
int namelen)
 static int append_de(struct au_vdir *vdir, char *name, int namelen, ino_t ino,
                     unsigned int d_type, struct au_nhash *delist)
 {
-       int err, sz;
+       int err;
+       unsigned int sz;
+       const unsigned int deblk_sz = vdir->vd_deblk_sz;
        union au_vdir_deblk_p p, *room, deblk_end;
        struct au_vdir_dehstr *dehstr;
 
@@ -373,20 +376,20 @@ static int append_de(struct au_vdir *vdir, char *name, 
int namelen, ino_t ino,
                  namelen, name, namelen, (unsigned long)ino, d_type);
 
        p.deblk = last_deblk(vdir);
-       deblk_end.deblk = p.deblk + 1;
+       deblk_end.deblk = p.deblk + deblk_sz;
        room = &vdir->vd_last.p;
-       AuDebugOn(room->p < p.p || deblk_end.p <= room->p
+       AuDebugOn(room->deblk < p.deblk || deblk_end.deblk <= room->deblk
                  || !is_deblk_end(room, &deblk_end));
 
        sz = calc_size(namelen);
-       if (unlikely(sz > deblk_end.p - room->p)) {
+       if (unlikely(sz > deblk_end.deblk - room->deblk)) {
                err = append_deblk(vdir);
                if (unlikely(err))
                        goto out;
                p.deblk = last_deblk(vdir);
-               deblk_end.deblk = p.deblk + 1;
+               deblk_end.deblk = p.deblk + deblk_sz;
                /* smp_mb(); */
-               AuDebugOn(room->p != p.p);
+               AuDebugOn(room->deblk != p.deblk);
        }
 
        err = -ENOMEM;
@@ -394,8 +397,7 @@ static int append_de(struct au_vdir *vdir, char *name, int 
namelen, ino_t ino,
        if (unlikely(!dehstr))
                goto out;
        dehstr->str = &room->de->de_str;
-       hlist_add_head(&dehstr->hash,
-                      delist->heads + au_name_hash(name, namelen));
+       hlist_add_head(&dehstr->hash, au_name_hash(delist, name, namelen));
 
        room->de->de_ino = ino;
        room->de->de_type = d_type;
@@ -403,7 +405,7 @@ static int append_de(struct au_vdir *vdir, char *name, int 
namelen, ino_t ino,
        memcpy(room->de->de_str.name, name, namelen);
 
        err = 0;
-       room->p += sz;
+       room->deblk += sz;
        if (unlikely(set_deblk_end(room, &deblk_end)))
                err = append_deblk(vdir);
        /* smp_mb(); */
@@ -488,8 +490,8 @@ static int au_wh_ino(struct super_block *sb, aufs_bindex_t 
bindex, ino_t h_ino,
 struct fillvdir_arg {
        struct file             *file;
        struct au_vdir          *vdir;
-       struct au_nhash         *delist;
-       struct au_nhash         *whlist;
+       struct au_nhash         delist;
+       struct au_nhash         whlist;
        aufs_bindex_t           bindex;
        unsigned int            flags;
        int                     err;
@@ -500,7 +502,6 @@ static int fillvdir(void *__arg, const char *__name, int 
namelen, loff_t offset,
 {
        struct fillvdir_arg *arg = __arg;
        char *name = (void *)__name;
-       aufs_bindex_t bindex, bend;
        struct super_block *sb;
        ino_t ino;
 
@@ -508,38 +509,35 @@ static int fillvdir(void *__arg, const char *__name, int 
namelen, loff_t offset,
                  namelen, name, namelen, (unsigned long long)h_ino, d_type);
 
        sb = arg->file->f_dentry->d_sb;
-       bend = arg->bindex;
        arg->err = 0;
        au_fset_fillvdir(arg->flags, CALLED);
        /* smp_mb(); */
        if (namelen <= AUFS_WH_PFX_LEN
            || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) {
-               for (bindex = 0; bindex < bend; bindex++)
-                       if (test_known(arg->delist + bindex, name, namelen)
-                           || au_nhash_test_known_wh(arg->whlist + bindex,
-                                                     name, namelen))
-                               goto out; /* already exists or whiteouted */
+               if (test_known(&arg->delist, name, namelen)
+                   || au_nhash_test_known_wh(&arg->whlist, name, namelen))
+                       goto out; /* already exists or whiteouted */
 
                ino = 1; /* why does gcc warns? */
-               arg->err = au_ino(sb, bend, h_ino, d_type, &ino);
+               arg->err = au_ino(sb, arg->bindex, h_ino, d_type, &ino);
                if (!arg->err)
                        arg->err = append_de(arg->vdir, name, namelen, ino,
-                                            d_type, arg->delist + bend);
+                                            d_type, &arg->delist);
        } else {
                name += AUFS_WH_PFX_LEN;
                namelen -= AUFS_WH_PFX_LEN;
-               for (bindex = 0; bindex < bend; bindex++)
-                       if (au_nhash_test_known_wh(arg->whlist + bend, name,
-                                                  namelen))
-                               goto out; /* already whiteouted */
+               if (au_nhash_test_known_wh(&arg->whlist, name, namelen))
+                       goto out; /* already whiteouted */
 
                ino = 1; /* dummy */
                if (au_ftest_fillvdir(arg->flags, SHWH))
-                       arg->err = au_wh_ino(sb, bend, h_ino, d_type, &ino);
+                       arg->err = au_wh_ino(sb, arg->bindex, h_ino, d_type,
+                                            &ino);
                if (!arg->err)
                        arg->err = au_nhash_append_wh
-                               (arg->whlist + bend, name, namelen, ino, d_type,
-                                bend, au_ftest_fillvdir(arg->flags, SHWH));
+                               (&arg->whlist, name, namelen, ino, d_type,
+                                arg->bindex,
+                                au_ftest_fillvdir(arg->flags, SHWH));
        }
 
  out:
@@ -551,8 +549,7 @@ static int fillvdir(void *__arg, const char *__name, int 
namelen, loff_t offset,
 }
 
 static int au_handle_shwh(struct super_block *sb, struct au_vdir *vdir,
-                         aufs_bindex_t bstart, aufs_bindex_t bend,
-                         struct au_nhash *_whlist, struct au_nhash *_delist)
+                         struct au_nhash *whlist, struct au_nhash *delist)
 {
 #ifdef CONFIG_AUFS_SHWH
        int err, i;
@@ -573,24 +570,18 @@ static int au_handle_shwh(struct super_block *sb, struct 
au_vdir *vdir,
                goto out;
 
        err = 0;
+       nh = whlist->nh_num;
        memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN);
        p += AUFS_WH_PFX_LEN;
-       for (bindex = bstart; !err && bindex <= bend; bindex++) {
-               whlist = _whlist + bindex;
-               delist = _delist + bindex;
-
-               for (i = 0; i < AuSize_NHASH; i++) {
-                       head = whlist->heads + i;
-                       hlist_for_each_entry_safe(tpos, pos, n, head, wh_hash) {
-                               destr = &tpos->wh_str;
-                               memcpy(p, destr->name, destr->len);
-                               err = append_de(vdir, o,
-                                               destr->len + AUFS_WH_PFX_LEN,
-                                               tpos->wh_ino, tpos->wh_type,
-                                               delist);
-                               if (unlikely(err))
-                                       break;
-                       }
+       for (u = 0; u < nh; u++) {
+               head = whlist->nh_head + u;
+               hlist_for_each_entry_safe(tpos, pos, n, head, wh_hash) {
+                       destr = &tpos->wh_str;
+                       memcpy(p, destr->name, destr->len);
+                       err = append_de(vdir, o, destr->len + AUFS_WH_PFX_LEN,
+                                       tpos->wh_ino, tpos->wh_type, delist);
+                       if (unlikely(err))
+                               break;
                }
        }
 
@@ -607,30 +598,27 @@ static int au_handle_shwh(struct super_block *sb, struct 
au_vdir *vdir,
 static int au_do_read_vdir(struct fillvdir_arg *arg)
 {
        int err;
-       unsigned int mnt_flags;
+       unsigned int rdhash, mnt_flags;
        loff_t offset;
        aufs_bindex_t bend, bindex, bstart;
        unsigned char dlgt, shwh;
        struct super_block *sb;
-       struct file *hf;
+       struct file *hf, *file;
 
        AuTraceEnter();
 
        err = -ENOMEM;
-       bend = au_fbend(arg->file);
-       arg->delist = kmalloc(sizeof(*arg->delist) * (bend + 1), GFP_NOFS);
-       if (unlikely(!arg->delist))
+       file = arg->file;
+       rdhash = au_rdhash_est(au_dir_size(file, /*dentry*/NULL));
+       err = au_nhash_alloc(&arg->delist, rdhash, GFP_NOFS);
+       if (unlikely(err))
                goto out;
-       arg->whlist = kmalloc(sizeof(*arg->whlist) * (bend + 1), GFP_NOFS);
-       if (unlikely(!arg->whlist))
+       err = au_nhash_alloc(&arg->whlist, rdhash, GFP_NOFS);
+       if (unlikely(err))
                goto out_delist;
-       err = 0;
-       for (bindex = 0; bindex <= bend; bindex++) {
-               au_nhash_init(arg->delist + bindex);
-               au_nhash_init(arg->whlist + bindex);
-       }
 
-       sb = arg->file->f_dentry->d_sb;
+       err = 0;
+       sb = file->f_dentry->d_sb;
        mnt_flags = au_mntflags(sb);
        dlgt = !!au_test_dlgt(mnt_flags);
        arg->flags = 0;
@@ -639,9 +627,10 @@ static int au_do_read_vdir(struct fillvdir_arg *arg)
                shwh = 1;
                au_fset_fillvdir(arg->flags, SHWH);
        }
-       bstart = au_fbstart(arg->file);
+       bstart = au_fbstart(file);
+       bend = au_fbend(file);
        for (bindex = bstart; !err && bindex <= bend; bindex++) {
-               hf = au_h_fptr(arg->file, bindex);
+               hf = au_h_fptr(file, bindex);
                if (!hf)
                        continue;
 
@@ -661,17 +650,12 @@ static int au_do_read_vdir(struct fillvdir_arg *arg)
        }
 
        if (!err && shwh)
-               err = au_handle_shwh(sb, arg->vdir, bstart, bend, arg->whlist,
-                                    arg->delist);
+               err = au_handle_shwh(sb, arg->vdir, &arg->whlist, &arg->delist);
 
-       for (bindex = bstart; bindex <= bend; bindex++) {
-               free_dehlist(arg->delist + bindex);
-               au_nhash_fin(arg->whlist + bindex);
-       }
-       kfree(arg->whlist);
+       au_nhash_wh_free(&arg->whlist);
 
  out_delist:
-       kfree(arg->delist);
+       au_nhash_de_free(&arg->delist);
  out:
        AuTraceErr(err);
        return err;
@@ -705,7 +689,7 @@ static int read_vdir(struct file *file, int may_read)
        if (!vdir) {
                AuDebugOn(au_fvdir_cache(file));
                do_read = 1;
-               vdir = alloc_vdir();
+               vdir = alloc_vdir(file);
                err = PTR_ERR(vdir);
                if (IS_ERR(vdir))
                        goto out;
@@ -733,7 +717,7 @@ static int read_vdir(struct file *file, int may_read)
                /* todo: necessary? */
                /* file->f_pos = 0; */
                vdir->vd_version = inode->i_version;
-               vdir->vd_last.i = 0;
+               vdir->vd_last.ul = 0;
                vdir->vd_last.p.deblk = vdir->vd_deblk[0];
                if (allocated)
                        au_set_ivdir(inode, allocated);
@@ -747,14 +731,17 @@ static int read_vdir(struct file *file, int may_read)
 
 static int copy_vdir(struct au_vdir *tgt, struct au_vdir *src)
 {
-       int err, i, rerr, n;
+       int err, rerr;
+       unsigned long ul, n;
+       const unsigned int deblk_sz = src->vd_deblk_sz;
 
        AuTraceEnter();
        AuDebugOn(tgt->vd_nblk != 1);
 
        err = -ENOMEM;
        if (tgt->vd_nblk < src->vd_nblk) {
-               au_vdir_deblk_t **p;
+               unsigned char **p;
+
                p = au_kzrealloc(tgt->vd_deblk, sizeof(*p) * tgt->vd_nblk,
                                 sizeof(*p) * src->vd_nblk, GFP_NOFS);
                if (unlikely(!p))
@@ -762,22 +749,32 @@ static int copy_vdir(struct au_vdir *tgt, struct au_vdir 
*src)
                tgt->vd_deblk = p;
        }
 
-       tgt->vd_nblk = src->vd_nblk;
-       n = src->vd_nblk;
-       memcpy(tgt->vd_deblk[0], src->vd_deblk[0], AuSize_DEBLK);
-       /* tgt->vd_last.i = 0; */
-       /* tgt->vd_last.p.deblk = tgt->vd_deblk[0]; */
+       if (tgt->vd_deblk_sz != deblk_sz) {
+               unsigned char *p;
+
+               tgt->vd_deblk_sz = deblk_sz;
+               p = krealloc(tgt->vd_deblk[0], deblk_sz, GFP_NOFS);
+               if (unlikely(!p))
+                       goto out;
+               tgt->vd_deblk[0] = p;
+       }
+       memcpy(tgt->vd_deblk[0], src->vd_deblk[0], deblk_sz);
        tgt->vd_version = src->vd_version;
        tgt->vd_jiffy = src->vd_jiffy;
 
-       for (i = 1; i < n; i++) {
-               tgt->vd_deblk[i] = kmalloc(AuSize_DEBLK, GFP_NOFS);
-               if (tgt->vd_deblk[i])
-                       memcpy(tgt->vd_deblk[i], src->vd_deblk[i],
-                              AuSize_DEBLK);
-               else
+       n = src->vd_nblk;
+       for (ul = 0; ul < n; ul++) {
+               tgt->vd_deblk[ul] = kmemdup(src->vd_deblk[ul], deblk_sz,
+                                           GFP_NOFS);
+               if (unlikely(!tgt->vd_deblk[ul]))
                        goto out;
+               tgt->vd_nblk++;
        }
+       tgt->vd_nblk = n;
+       tgt->vd_last.ul = tgt->vd_last.ul;
+       tgt->vd_last.p.deblk = tgt->vd_deblk[tgt->vd_last.ul];
+       tgt->vd_last.p.deblk += src->vd_last.p.deblk
+               - src->vd_deblk[src->vd_last.ul];
        /* smp_mb(); */
        return 0; /* success */
 
@@ -809,7 +806,7 @@ int au_vdir_init(struct file *file)
        allocated = NULL;
        vdir_cache = au_fvdir_cache(file);
        if (!vdir_cache) {
-               vdir_cache = alloc_vdir();
+               vdir_cache = alloc_vdir(file);
                err = PTR_ERR(vdir_cache);
                if (IS_ERR(vdir_cache))
                        goto out;
@@ -839,20 +836,22 @@ static loff_t calc_offset(struct au_vdir *vdir)
        loff_t offset;
        union au_vdir_deblk_p p;
 
-       p.deblk = vdir->vd_deblk[vdir->vd_last.i];
-       offset = vdir->vd_last.p.p - p.p;
-       offset += sizeof(*p.deblk) * vdir->vd_last.i;
+       p.deblk = vdir->vd_deblk[vdir->vd_last.ul];
+       offset = vdir->vd_last.p.deblk - p.deblk;
+       offset += vdir->vd_deblk_sz * vdir->vd_last.ul;
        return offset;
 }
 
 /* returns true or false */
 static int seek_vdir(struct file *file)
 {
-       int valid, i, n;
-       struct dentry *dentry;
-       struct au_vdir *vdir_cache;
+       int valid;
+       unsigned int deblk_sz;
+       unsigned long ul, n;
        loff_t offset;
        union au_vdir_deblk_p p, deblk_end;
+       struct au_vdir *vdir_cache;
+       struct dentry *dentry;
 
        dentry = file->f_dentry;
        LKTRTrace("%.*s, pos %lld\n", AuDLNPair(dentry), file->f_pos);
@@ -865,32 +864,34 @@ static int seek_vdir(struct file *file)
        if (file->f_pos == offset)
                goto out;
 
-       vdir_cache->vd_last.i = 0;
+       vdir_cache->vd_last.ul = 0;
        vdir_cache->vd_last.p.deblk = vdir_cache->vd_deblk[0];
        if (!file->f_pos)
                goto out;
 
        valid = 0;
-       i = file->f_pos / AuSize_DEBLK;
-       LKTRTrace("i %d\n", i);
-       if (i >= vdir_cache->vd_nblk)
+       deblk_sz = vdir_cache->vd_deblk_sz;
+       ul = file->f_pos / deblk_sz;
+       LKTRTrace("ul %lu\n", ul);
+       if (ul >= vdir_cache->vd_nblk)
                goto out;
 
        n = vdir_cache->vd_nblk;
-       for (; i < n; i++) {
-               p.deblk = vdir_cache->vd_deblk[i];
-               deblk_end.deblk = p.deblk + 1;
-               offset = i;
-               offset *= AuSize_DEBLK;
+       for (; ul < n; ul++) {
+               p.deblk = vdir_cache->vd_deblk[ul];
+               deblk_end.deblk = p.deblk + deblk_sz;
+               offset = ul;
+               offset *= deblk_sz;
                while (!is_deblk_end(&p, &deblk_end) && offset < file->f_pos) {
-                       int l;
+                       unsigned int l;
+
                        l = calc_size(p.de->de_str.len);
                        offset += l;
-                       p.p += l;
+                       p.deblk += l;
                }
                if (!is_deblk_end(&p, &deblk_end)) {
                        valid = 1;
-                       vdir_cache->vd_last.i = i;
+                       vdir_cache->vd_last.ul = ul;
                        vdir_cache->vd_last.p = p;
                        break;
                }
@@ -904,7 +905,8 @@ static int seek_vdir(struct file *file)
 
 int au_vdir_fill_de(struct file *file, void *dirent, filldir_t filldir)
 {
-       int err, l;
+       int err;
+       unsigned int l, deblk_sz;
        struct dentry *dentry;
        struct au_vdir *vdir_cache;
        struct au_vdir_de *de;
@@ -918,9 +920,10 @@ int au_vdir_fill_de(struct file *file, void *dirent, 
filldir_t filldir)
        if (!seek_vdir(file))
                return 0;
 
+       deblk_sz = vdir_cache->vd_deblk_sz;
        while (1) {
-               deblk_end.deblk
-                       = vdir_cache->vd_deblk[vdir_cache->vd_last.i] + 1;
+               deblk_end.deblk = vdir_cache->vd_deblk[vdir_cache->vd_last.ul];
+               deblk_end.deblk += deblk_sz;
                while (!is_deblk_end(&vdir_cache->vd_last.p, &deblk_end)) {
                        de = vdir_cache->vd_last.p.de;
                        LKTRTrace("%.*s, off%lld, i%lu, dt%d\n",
@@ -937,15 +940,14 @@ int au_vdir_fill_de(struct file *file, void *dirent, 
filldir_t filldir)
                        }
 
                        l = calc_size(de->de_str.len);
-                       vdir_cache->vd_last.p.p += l;
+                       vdir_cache->vd_last.p.deblk += l;
                        file->f_pos += l;
                }
-               if (vdir_cache->vd_last.i < vdir_cache->vd_nblk - 1) {
-                       vdir_cache->vd_last.i++;
+               if (vdir_cache->vd_last.ul < vdir_cache->vd_nblk - 1) {
+                       vdir_cache->vd_last.ul++;
                        vdir_cache->vd_last.p.deblk
-                               = vdir_cache->vd_deblk[vdir_cache->vd_last.i];
-                       file->f_pos = sizeof(*vdir_cache->vd_last.p.deblk)
-                               * vdir_cache->vd_last.i;
+                               = vdir_cache->vd_deblk[vdir_cache->vd_last.ul];
+                       file->f_pos = deblk_sz * vdir_cache->vd_last.ul;
                        continue;
                }
                break;
diff --git a/fs/aufs/whout.c b/fs/aufs/whout.c
index 8521cc2..6462275 100644
--- a/fs/aufs/whout.c
+++ b/fs/aufs/whout.c
@@ -942,7 +942,8 @@ struct dentry *au_wh_create(struct dentry *dentry, 
aufs_bindex_t bindex,
 static int del_wh_children(struct dentry *h_dentry, struct au_nhash *whlist,
                           aufs_bindex_t bindex, struct au_ndx *ndx)
 {
-       int err, i;
+       int err;
+       unsigned long ul, n;
        struct qstr wh_name;
        char *p;
        struct inode *h_inode;
@@ -965,8 +966,9 @@ static int del_wh_children(struct dentry *h_dentry, struct 
au_nhash *whlist,
 
        /* already checked by au_test_h_perm(). */
        err = 0;
-       for (i = 0; !err && i < AuSize_NHASH; i++) {
-               head = whlist->heads + i;
+       n = whlist->nh_num;
+       head = whlist->nh_head;
+       for (ul = 0; !err && ul < n; ul++, head++) {
                hlist_for_each_entry(tpos, pos, head, wh_hash) {
                        if (tpos->wh_bindex != bindex)
                                continue;
@@ -1091,18 +1093,42 @@ int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t 
bindex,
        return err;
 }
 
-static void au_whtmp_rmdir_free_args(struct au_whtmp_rmdir_args *args)
+struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp)
+{
+       struct au_whtmp_rmdir *whtmp;
+       int err;
+
+       whtmp = kmalloc(sizeof(*whtmp), gfp);
+       if (unlikely(!whtmp)) {
+               whtmp = ERR_PTR(-ENOMEM);
+               goto out;
+       }
+
+       whtmp->dir = NULL;
+       whtmp->wh_dentry = NULL;
+       /* no estimation for dir size */
+       err = au_nhash_alloc(&whtmp->whlist, AUFS_RDHASH_DEF, gfp);
+       if (unlikely(err)) {
+               kfree(whtmp);
+               whtmp = ERR_PTR(err);
+       }
+
+ out:
+       return whtmp;
+}
+
+void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp)
 {
-       au_nhash_fin(&args->whlist);
-       dput(args->wh_dentry);
-       iput(args->dir);
-       kfree(args);
+       dput(whtmp->wh_dentry);
+       iput(whtmp->dir);
+       au_nhash_wh_free(&whtmp->whlist);
+       kfree(whtmp);
 }
 
 static void call_rmdir_whtmp(void *args)
 {
        int err;
-       struct au_whtmp_rmdir_args *a = args;
+       struct au_whtmp_rmdir *a = args;
        struct super_block *sb;
        struct dentry *h_parent;
        struct inode *h_dir;
@@ -1139,14 +1165,13 @@ static void call_rmdir_whtmp(void *args)
        /* vfsub_i_unlock(a->dir); */
        au_nwt_done(&au_sbi(sb)->si_nowait);
        si_read_unlock(sb);
-       au_whtmp_rmdir_free_args(a);
+       au_whtmp_rmdir_free(a);
        if (unlikely(err))
                AuIOErr("err %d\n", err);
 }
 
 void au_whtmp_kick_rmdir(struct inode *dir, aufs_bindex_t bindex,
-                        struct dentry *wh_dentry, struct au_nhash *whlist,
-                        struct au_whtmp_rmdir_args *args)
+                        struct dentry *wh_dentry, struct au_whtmp_rmdir *args)
 {
        int wkq_err;
 
@@ -1157,12 +1182,10 @@ void au_whtmp_kick_rmdir(struct inode *dir, 
aufs_bindex_t bindex,
        args->dir = au_igrab(dir);
        args->bindex = bindex;
        args->wh_dentry = dget(wh_dentry);
-       au_nhash_init(&args->whlist);
-       au_nhash_move(&args->whlist, whlist);
        wkq_err = au_wkq_nowait(call_rmdir_whtmp, args, dir->i_sb, /*dlgt*/0);
        if (unlikely(wkq_err)) {
                AuWarn("rmdir error %.*s (%d), ignored\n",
                       AuDLNPair(wh_dentry), wkq_err);
-               au_whtmp_rmdir_free_args(args);
+               au_whtmp_rmdir_free(args);
        }
 }
diff --git a/fs/aufs/whout.h b/fs/aufs/whout.h
index 056ca13..9f3ab25 100644
--- a/fs/aufs/whout.h
+++ b/fs/aufs/whout.h
@@ -70,19 +70,20 @@ struct dentry *au_wh_create(struct dentry *dentry, 
aufs_bindex_t bindex,
                            struct dentry *h_parent, struct au_ndx *ndx);
 
 /* real rmdir the whiteout-ed dir */
-struct au_whtmp_rmdir_args {
+struct au_whtmp_rmdir {
        struct inode *dir;
        aufs_bindex_t bindex;
        struct dentry *wh_dentry;
        struct au_nhash whlist;
 };
 
+struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp);
+void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp);
 struct au_nhash;
 int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex,
                   struct dentry *wh_dentry, struct au_nhash *whlist);
 void au_whtmp_kick_rmdir(struct inode *dir, aufs_bindex_t bindex,
-                        struct dentry *wh_dentry, struct au_nhash *whlist,
-                        struct au_whtmp_rmdir_args *args);
+                        struct dentry *wh_dentry, struct au_whtmp_rmdir *args);
 
 /* ---------------------------------------------------------------------- */
 
-- 
1.6.1.284.g5dc13


------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day 
trial. Simplify your report design, integration and deployment - and focus on 
what you do best, core application coding. Discover what's new with 
Crystal Reports now.  http://p.sf.net/sfu/bobj-july

Reply via email to