On Fri, Jul 25, 2003 at 01:42:47PM +0200, Georges Toth wrote:
> > so, if its okay with you, I'll send you the patches
> > and some basic 'regression' tests I do ... sometimes ;)
> 
> that's ok for me :-)
> just tell me what to do and i'll do it :-D
> (what kernel, which patches, what tools, ...)

okay here you go ...

attached two patches (one labeled -mq the other -bq)

what I would like you to do is the following:

 - test -mq extensively as it introduces a new
   way the quota hash is handled ...
   I'm especially interrested in

   + race conditions on UP and SMP
   + any BUG() traces of course
   + unusual behaviour ;)

 - think about some brilliant tests, which actually
   verify that quota works as expected ...
   basically what to check:

   + quota change on create/unlink
   + quota transfer on chown/chgrp
   + special link/symlink/mkdev handling
   + quota enforcement (soft + hard)
   + ...
    
 - usually a good thing (TM) would be to start with
   some 'defined' environment, do some heavy automated
   testing and build a md5hash on the quota report.
 - this should be compared with the unpatched kernel
   (same release though) and _any_ differences should
   be reported ...
 - later the proven test scripts should be used on
   the -mq -bq combo, to spot any differences there

I would suggest 2.4.22-pre8, both patches should
apply without fuzz or reject ... -bq goes ontop -mq

I consider using quotatools 3.09 a good idea, although
3.08 should suffice, v0 and v2 quota formats should
be tested ...
 
if you have any questions *grin*, feel free to ask!

TIA,
Herbert

> -- 
> regards,
> Georges Toth
;
; Quota Hash Abstraction 
;
; separate quota hashes per superblock to speed up
; hash lookups and generalize the quota hash interface.
;
; (C) 2003 Herbert P�tzl <[EMAIL PROTECTED]>
;
; Changelog:  
;
;   0.04  - first public release
;   0.05  - trivial fix (lost temp variable)
;         - some required locking added
;   0.06  - cleanup of invalidate_dquots
;         - cleanup of new_dqhash
;         - remove_dquot_ref restricted to hash
;         - check_compat_quotactl_valid checks for hash
;         - added dummy code for CONFIG_QUOTA disabled 
;
; this patch is free software; you can redistribute it and/or
; modify it under the terms of the GNU General Public License
; as published by the Free Software Foundation; either version 2
; of the License, or (at your option) any later version.
; 
; this patch is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
; GNU General Public License for more details.
; 

diff -NurP --minimal linux-2.4.22-pre7/drivers/char/sysrq.c 
linux-2.4.22-pre7-mq0.06/drivers/char/sysrq.c
--- linux-2.4.22-pre7/drivers/char/sysrq.c      2003-07-19 14:14:23.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06/drivers/char/sysrq.c       2003-07-21 04:58:04.000000000 
+0200
@@ -160,7 +160,7 @@
                                file->f_mode &= ~2;
                }
                file_list_unlock();
-               DQUOT_OFF(sb);
+               DQUOT_OFF(sb->s_dqh);
                fsync_dev(sb->s_dev);
                flags = MS_RDONLY;
                if (sb->s_op && sb->s_op->remount_fs) {
diff -NurP --minimal linux-2.4.22-pre7/fs/buffer.c linux-2.4.22-pre7-mq0.06/fs/buffer.c
--- linux-2.4.22-pre7/fs/buffer.c       2003-07-19 14:14:30.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06/fs/buffer.c        2003-07-21 04:58:04.000000000 +0200
@@ -336,7 +336,7 @@
 
        lock_kernel();
        sync_inodes_sb(sb);
-       DQUOT_SYNC_SB(sb);
+       DQUOT_SYNC_DQH(sb->s_dqh);
        lock_super(sb);
        if (sb->s_dirt && sb->s_op && sb->s_op->write_super)
                sb->s_op->write_super(sb);
diff -NurP --minimal linux-2.4.22-pre7/fs/dquot.c linux-2.4.22-pre7-mq0.06/fs/dquot.c
--- linux-2.4.22-pre7/fs/dquot.c        2003-07-19 14:14:30.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06/fs/dquot.c 2003-07-22 03:29:06.000000000 +0200
@@ -68,6 +68,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/proc_fs.h>
+#include <linux/quotaops.h>
 
 #include <asm/uaccess.h>
 
@@ -150,7 +151,6 @@
 
 static LIST_HEAD(inuse_list);
 static LIST_HEAD(free_dquots);
-static struct list_head dquot_hash[NR_DQHASH];
 
 static void dqput(struct dquot *);
 static struct dquot *dqduplicate(struct dquot *);
@@ -175,31 +175,33 @@
        dquot->dq_dup_ref--;
 }
 
-static inline int const hashfn(struct super_block *sb, unsigned int id, int type)
+static inline int const hashfn(unsigned int id, int type)
 {
-       return((HASHDEV(sb->s_dev) ^ id) * (MAXQUOTAS - type)) % NR_DQHASH;
+       return (id * (MAXQUOTAS - type)) % NR_DQHASH;
 }
 
-static inline void insert_dquot_hash(struct dquot *dquot)
+static inline void insert_dquot_hash(struct dqhash *hash, struct dquot *dquot)
 {
-       struct list_head *head = dquot_hash + hashfn(dquot->dq_sb, dquot->dq_id, 
dquot->dq_type);
+       struct list_head *head = hash->dqh_hash + hashfn(dquot->dq_id, dquot->dq_type);
        list_add(&dquot->dq_hash, head);
+       dquot->dq_dqh = hash;
 }
 
 static inline void remove_dquot_hash(struct dquot *dquot)
 {
        list_del(&dquot->dq_hash);
        INIT_LIST_HEAD(&dquot->dq_hash);
+       dquot->dq_dqh = NULL;
 }
 
-static inline struct dquot *find_dquot(unsigned int hashent, struct super_block *sb, 
unsigned int id, int type)
+static inline struct dquot *find_dquot(struct dqhash *hash, unsigned int hashent, 
unsigned int id, int type)
 {
        struct list_head *head;
        struct dquot *dquot;
 
-       for (head = dquot_hash[hashent].next; head != dquot_hash+hashent; head = 
head->next) {
+       for (head = hash->dqh_hash[hashent].next; head != hash->dqh_hash+hashent; head 
= head->next) {
                dquot = list_entry(head, struct dquot, dq_hash);
-               if (dquot->dq_sb == sb && dquot->dq_id == id && dquot->dq_type == type)
+               if (dquot->dq_id == id && dquot->dq_type == type)
                        return dquot;
        }
        return NODQUOT;
@@ -317,7 +319,7 @@
 static int read_dqblk(struct dquot *dquot)
 {
        int ret;
-       struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
+       struct quota_info *dqopt = dqh_dqopt(dquot->dq_dqh);
 
        lock_dquot(dquot);
        down(&dqopt->dqio_sem);
@@ -330,7 +332,7 @@
 static int commit_dqblk(struct dquot *dquot)
 {
        int ret;
-       struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
+       struct quota_info *dqopt = dqh_dqopt(dquot->dq_dqh);
 
        down(&dqopt->dqio_sem);
        ret = dqopt->ops[dquot->dq_type]->commit_dqblk(dquot);
@@ -341,50 +343,118 @@
 /* Invalidate all dquots on the list, wait for all users. Note that this function is 
called
  * after quota is disabled so no new quota might be created. As we only insert to the 
end of
  * inuse list, we don't have to restart searching... */
-static void invalidate_dquots(struct super_block *sb, int type)
+static void invalidate_dquots(struct dqhash *hash, int type)
+{
+       int i;
+
+       for (i=0; i<NR_DQHASH; i++) {
+               struct list_head *head = &hash->dqh_hash[i];
+
+               while ((head = head->next) != &hash->dqh_hash[i]) {
+                       struct dquot *dquot = list_entry(head, struct dquot, dq_hash);
+
+                       if (dquot->dq_type != type)
+                               continue;
+                       dquot->dq_flags |= DQ_INVAL;
+
+                       if (dquot->dq_count)
+                               /*
+                                *  Wait for any users of quota. As we have already 
cleared the flags in
+                                *  superblock and cleared all pointers from inodes we 
are assured
+                                *  that there will be no new users of this quota.
+                                */
+                               __wait_dquot_unused(dquot);
+                               
+                       remove_dquot_hash(dquot);
+                       remove_free_dquot(dquot);
+                       remove_inuse(dquot);
+                       kmem_cache_free(dquot_cachep, dquot);
+                       /* now restart */
+                       head = &hash->dqh_hash[i];
+               }
+       }
+}
+
+/* Dquota Hash Management Functions */
+
+static LIST_HEAD(dqhash_list);
+
+
+struct dqhash *new_dqhash(struct super_block *sb, unsigned int id)
+{
+       struct dqhash *hash;
+       int i;
+       
+       hash = kmalloc(sizeof(struct dqhash),  GFP_USER);
+       if (!hash)
+               return ERR_PTR(-ENOMEM);
+
+       memset(hash, 0, sizeof(struct dqhash));
+       hash->dqh_id = id;
+       INIT_LIST_HEAD(&hash->dqh_list);
+       for (i = 0; i < NR_DQHASH; i++)
+               INIT_LIST_HEAD(hash->dqh_hash + i);
+       sema_init(&hash->dqh_dqopt.dqio_sem, 1);
+       sema_init(&hash->dqh_dqopt.dqoff_sem, 1);
+       hash->dqh_qop = &dquot_operations;
+       hash->dqh_qcop = &vfs_quotactl_ops;
+       hash->dqh_sb = sb;
+
+       lock_kernel();
+       list_add(&hash->dqh_list, &dqhash_list);
+       unlock_kernel();
+       dprintk ("��� new_dqhash: %p [#%d,#0x%lx]\n", hash, hash->dqh_id, 
hash->dqh_dqdom);
+       return hash;
+}
+
+void destroy_dqhash(struct dqhash *hash)
+{
+       int cnt;
+
+       dprintk ("��� destroy_dqhash: %p [#%d,#0x%lx]\n", hash, hash->dqh_id, 
hash->dqh_dqdom);
+       lock_kernel();
+       list_del_init(&hash->dqh_list);
+       unlock_kernel();
+       for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+               invalidate_dquots(hash, cnt);
+       kfree(hash);
+}
+
+
+struct dqhash *find_dqhash(unsigned int id)
 {
-       struct dquot *dquot;
        struct list_head *head;
+       struct dqhash *hash;
 
-restart:
-       list_for_each(head, &inuse_list) {
-               dquot = list_entry(head, struct dquot, dq_inuse);
-               if (dquot->dq_sb != sb)
-                       continue;
-               if (dquot->dq_type != type)
-                       continue;
-               dquot->dq_flags |= DQ_INVAL;
-               if (dquot->dq_count)
-                       /*
-                        *  Wait for any users of quota. As we have already cleared 
the flags in
-                        *  superblock and cleared all pointers from inodes we are 
assured
-                        *  that there will be no new users of this quota.
-                        */
-                       __wait_dquot_unused(dquot);
-               /* Quota now have no users and it has been written on last dqput() */
-               remove_dquot_hash(dquot);
-               remove_free_dquot(dquot);
-               remove_inuse(dquot);
-               kmem_cache_free(dquot_cachep, dquot);
-               goto restart;
+       lock_kernel();
+       list_for_each(head, &dqhash_list) {
+               hash = list_entry(head, struct dqhash, dqh_list);
+               if (hash->dqh_id == id)
+                       goto dqh_found;
        }
+       hash = NULL;
+       
+dqh_found:
+       unlock_kernel();
+       return hash;
 }
 
-static int vfs_quota_sync(struct super_block *sb, int type)
+
+static int vfs_quota_sync(struct dqhash *hash, int type)
 {
        struct list_head *head;
        struct dquot *dquot;
-       struct quota_info *dqopt = sb_dqopt(sb);
+       struct quota_info *dqopt = dqh_dqopt(hash);
        int cnt;
 
 restart:
        list_for_each(head, &inuse_list) {
                dquot = list_entry(head, struct dquot, dq_inuse);
-               if (sb && dquot->dq_sb != sb)
+               if (hash && dquot->dq_dqh != hash)
                        continue;
                 if (type != -1 && dquot->dq_type != type)
                        continue;
-               if (!dquot->dq_sb)      /* Invalidated? */
+               if (!dquot->dq_dqh)     /* Invalidated? */
                        continue;
                if (!dquot_dirty(dquot) && !(dquot->dq_flags & DQ_LOCKED))
                        continue;
@@ -395,16 +465,16 @@
                if (dquot->dq_flags & DQ_LOCKED)
                        wait_on_dquot(dquot);
                if (dquot_dirty(dquot))
-                       sb->dq_op->sync_dquot(dquot);
+                       hash->dqh_qop->sync_dquot(dquot);
                dqput(dquot);
                goto restart;
        }
        for (cnt = 0; cnt < MAXQUOTAS; cnt++)
-               if ((cnt == type || type == -1) && sb_has_quota_enabled(sb, cnt))
+               if ((cnt == type || type == -1) && dqh_has_quota_enabled(hash, cnt))
                        dqopt->info[cnt].dqi_flags &= ~DQF_ANY_DQUOT_DIRTY;
        for (cnt = 0; cnt < MAXQUOTAS; cnt++)
-               if ((cnt == type || type == -1) && sb_has_quota_enabled(sb, cnt) && 
info_dirty(&dqopt->info[cnt]))
-                       dqopt->ops[cnt]->write_file_info(sb, cnt);
+               if ((cnt == type || type == -1) && dqh_has_quota_enabled(hash, cnt) && 
info_dirty(&dqopt->info[cnt]))
+                       dqopt->ops[cnt]->write_file_info(hash, cnt);
        dqstats.syncs++;
 
        return 0;
@@ -419,10 +489,11 @@
        spin_lock(&sb_lock);
        list_for_each(head, &super_blocks) {
                struct super_block *sb = list_entry(head, struct super_block, s_list);
+               struct dqhash *dqh = sb->s_dqh;
 
                for (cnt = 0, dirty = 0; cnt < MAXQUOTAS; cnt++)
-                       if ((type == cnt || type == -1) && sb_has_quota_enabled(sb, 
cnt)
-                           && sb_dqopt(sb)->info[cnt].dqi_flags & DQF_ANY_DQUOT_DIRTY)
+                       if ((type == cnt || type == -1) && dqh_has_quota_enabled(dqh, 
cnt)
+                           && dqh_dqopt(dqh)->info[cnt].dqi_flags & 
DQF_ANY_DQUOT_DIRTY)
                                dirty = 1;
                if (!dirty)
                        continue;
@@ -442,12 +513,14 @@
 void sync_dquots_dev(kdev_t dev, int type)
 {
        struct super_block *sb;
+       struct dqhash *dqh;
 
        if (dev) {
                if ((sb = get_super(dev))) {
                        lock_kernel();
-                       if (sb->s_qcop->quota_sync)
-                               sb->s_qcop->quota_sync(sb, type);
+                       dqh = sb->s_dqh;
+                       if (dqh && dqh->dqh_qcop->quota_sync)
+                               dqh->dqh_qcop->quota_sync(dqh, type);
                        unlock_kernel();
                        drop_super(sb);
                }
@@ -455,19 +528,20 @@
        else {
                while ((sb = get_super_to_sync(type))) {
                        lock_kernel();
-                       if (sb->s_qcop->quota_sync)
-                               sb->s_qcop->quota_sync(sb, type);
+                       dqh = sb->s_dqh;
+                       if (dqh && dqh->dqh_qcop->quota_sync)
+                               dqh->dqh_qcop->quota_sync(dqh, type);
                        unlock_kernel();
                        drop_super(sb);
                }
        }
 }
 
-void sync_dquots_sb(struct super_block *sb, int type)
+void sync_dquots_dqh(struct dqhash *hash, int type)
 {
        lock_kernel();
-       if (sb->s_qcop->quota_sync)
-               sb->s_qcop->quota_sync(sb, type);
+       if (hash->dqh_qcop->quota_sync)
+               hash->dqh_qcop->quota_sync(hash, type);
        unlock_kernel();
 }
 
@@ -559,7 +633,7 @@
        wake_up(&dquot->dq_wait_free);
 }
 
-static struct dquot *get_empty_dquot(struct super_block *sb, int type)
+static struct dquot *get_empty_dquot(struct dqhash *hash, int type)
 {
        struct dquot *dquot;
 
@@ -573,8 +647,9 @@
        INIT_LIST_HEAD(&dquot->dq_free);
        INIT_LIST_HEAD(&dquot->dq_inuse);
        INIT_LIST_HEAD(&dquot->dq_hash);
-       dquot->dq_sb = sb;
-       dquot->dq_dev = sb->s_dev;
+       dquot->dq_dqh = hash;
+       if (hash->dqh_sb)
+               dquot->dq_dev = hash->dqh_sb->s_dev;
        dquot->dq_type = type;
        dquot->dq_count = 1;
        /* all dquots go on the inuse_list */
@@ -583,11 +658,11 @@
        return dquot;
 }
 
-static struct dquot *dqget(struct super_block *sb, unsigned int id, int type)
+static struct dquot *dqget(struct dqhash *hash, unsigned int id, int type)
 {
-       unsigned int hashent = hashfn(sb, id, type);
+       unsigned int hashent = hashfn(id, type);
        struct dquot *dquot, *empty = NODQUOT;
-       struct quota_info *dqopt = sb_dqopt(sb);
+       struct quota_info *dqopt = dqh_dqopt(hash);
 
 we_slept:
         if (!is_enabled(dqopt, type)) {
@@ -596,16 +671,16 @@
                 return NODQUOT;
        }
 
-       if ((dquot = find_dquot(hashent, sb, id, type)) == NODQUOT) {
+       if ((dquot = find_dquot(hash, hashent, id, type)) == NODQUOT) {
                if (empty == NODQUOT) {
-                       if ((empty = get_empty_dquot(sb, type)) == NODQUOT)
+                       if ((empty = get_empty_dquot(hash, type)) == NODQUOT)
                                schedule();     /* Try to wait for a moment... */
                        goto we_slept;
                }
                dquot = empty;
                dquot->dq_id = id;
                /* hash it first so it can be found */
-               insert_dquot_hash(dquot);
+               insert_dquot_hash(hash, dquot);
                read_dqblk(dquot);
        } else {
                if (!dquot->dq_count)
@@ -617,7 +692,7 @@
                        dqput(empty);
        }
 
-       if (!dquot->dq_sb) {    /* Has somebody invalidated entry under us? */
+       if (!dquot->dq_dqh) {   /* Has somebody invalidated entry under us? */
                printk(KERN_ERR "VFS: dqget(): Quota invalidated in dqget()!\n");
                dqput(dquot);
                return NODQUOT;
@@ -634,7 +709,7 @@
        if (dquot == NODQUOT)
                return NODQUOT;
        get_dquot_ref(dquot);
-       if (!dquot->dq_sb) {
+       if (!dquot->dq_dqh) {
                printk(KERN_ERR "VFS: dqduplicate(): Invalidated quota to be 
duplicated!\n");
                put_dquot_ref(dquot);
                return NODQUOT;
@@ -676,9 +751,10 @@
        return 0;
 }
 
-static void add_dquot_ref(struct super_block *sb, int type)
+static void add_dquot_ref(struct dqhash *hash, int type)
 {
        struct list_head *p;
+       struct super_block *sb = hash->dqh_sb;
 
 restart:
        file_list_lock();
@@ -689,7 +765,7 @@
                        struct vfsmount *mnt = mntget(filp->f_vfsmnt);
                        struct dentry *dentry = dget(filp->f_dentry);
                        file_list_unlock();
-                       sb->dq_op->initialize(inode, type);
+                       hash->dqh_qop->initialize(inode, type);
                        dput(dentry);
                        mntput(mnt);
                        /* As we may have blocked we had better restart... */
@@ -821,7 +897,7 @@
        if (!need_print_warning(dquot, flag))
                return;
        dquot->dq_flags |= flag;
-       tty_write_message(current->tty, (char *)bdevname(dquot->dq_sb->s_dev));
+       tty_write_message(current->tty, (char 
*)bdevname(dquot->dq_dqh->dqh_sb->s_dev));
        if (warntype == ISOFTWARN || warntype == BSOFTWARN)
                tty_write_message(current->tty, ": warning, ");
        else
@@ -861,7 +937,7 @@
 
 static inline char ignore_hardlimit(struct dquot *dquot)
 {
-       struct mem_dqinfo *info = &sb_dqopt(dquot->dq_sb)->info[dquot->dq_type];
+       struct mem_dqinfo *info = &dqh_dqopt(dquot->dq_dqh)->info[dquot->dq_type];
 
        return capable(CAP_SYS_RESOURCE) &&
            (info->dqi_format->qf_fmt_id != QFMT_VFS_OLD || !(info->dqi_flags & 
V1_DQF_RSQUASH));
@@ -892,7 +968,7 @@
           (dquot->dq_dqb.dqb_curinodes + inodes) > dquot->dq_dqb.dqb_isoftlimit &&
            dquot->dq_dqb.dqb_itime == 0) {
                *warntype = ISOFTWARN;
-               dquot->dq_dqb.dqb_itime = CURRENT_TIME + 
sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_igrace;
+               dquot->dq_dqb.dqb_itime = CURRENT_TIME + 
dqh_dqopt(dquot->dq_dqh)->info[dquot->dq_type].dqi_igrace;
        }
 
        return QUOTA_OK;
@@ -926,7 +1002,7 @@
            dquot->dq_dqb.dqb_btime == 0) {
                if (!prealloc) {
                        *warntype = BSOFTWARN;
-                       dquot->dq_dqb.dqb_btime = CURRENT_TIME + 
sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_bgrace;
+                       dquot->dq_dqb.dqb_btime = CURRENT_TIME + 
dqh_dqopt(dquot->dq_dqh)->info[dquot->dq_type].dqi_bgrace;
                }
                else
                        /*
@@ -947,6 +1023,7 @@
 void dquot_initialize(struct inode *inode, int type)
 {
        struct dquot *dquot[MAXQUOTAS];
+       struct dqhash *dqh = inode->i_sb->s_dqh;
        unsigned int id = 0;
        int cnt;
 
@@ -957,7 +1034,7 @@
                dquot[cnt] = NODQUOT;
                if (type != -1 && cnt != type)
                        continue;
-               if (!sb_has_quota_enabled(inode->i_sb, cnt))
+               if (!dqh_has_quota_enabled(dqh, cnt))
                        continue;
                if (inode->i_dquot[cnt] == NODQUOT) {
                        switch (cnt) {
@@ -968,12 +1045,12 @@
                                        id = inode->i_gid;
                                        break;
                        }
-                       dquot[cnt] = dqget(inode->i_sb, id, cnt);
+                       dquot[cnt] = dqget(dqh, id, cnt);
                }
        }
        /* NOBLOCK START: Here we shouldn't block */
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
-               if (dquot[cnt] == NODQUOT || !sb_has_quota_enabled(inode->i_sb, cnt) 
|| inode->i_dquot[cnt] != NODQUOT)
+               if (dquot[cnt] == NODQUOT || !dqh_has_quota_enabled(dqh, cnt) || 
inode->i_dquot[cnt] != NODQUOT)
                        continue;
                inode->i_dquot[cnt] = dquot[cnt];
                dquot[cnt] = NODQUOT;
@@ -1129,6 +1206,7 @@
        qsize_t space;
        struct dquot *transfer_from[MAXQUOTAS];
        struct dquot *transfer_to[MAXQUOTAS];
+       struct dqhash *dqh = inode->i_sb->s_dqh;
        int cnt, ret = NO_QUOTA, chuid = (iattr->ia_valid & ATTR_UID) && inode->i_uid 
!= iattr->ia_uid,
            chgid = (iattr->ia_valid & ATTR_GID) && inode->i_gid != iattr->ia_gid;
        char warntype[MAXQUOTAS];
@@ -1140,18 +1218,18 @@
        }
        /* First build the transfer_to list - here we can block on reading of 
dquots... */
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
-               if (!sb_has_quota_enabled(inode->i_sb, cnt))
+               if (!dqh_has_quota_enabled(dqh, cnt))
                        continue;
                switch (cnt) {
                        case USRQUOTA:
                                if (!chuid)
                                        continue;
-                               transfer_to[cnt] = dqget(inode->i_sb, iattr->ia_uid, 
cnt);
+                               transfer_to[cnt] = dqget(dqh, iattr->ia_uid, cnt);
                                break;
                        case GRPQUOTA:
                                if (!chgid)
                                        continue;
-                               transfer_to[cnt] = dqget(inode->i_sb, iattr->ia_gid, 
cnt);
+                               transfer_to[cnt] = dqget(dqh, iattr->ia_gid, cnt);
                                break;
                }
        }
@@ -1160,7 +1238,7 @@
        /* Build the transfer_from list and check the limits */
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
                /* The second test can fail when quotaoff is in progress... */
-               if (transfer_to[cnt] == NODQUOT || !sb_has_quota_enabled(inode->i_sb, 
cnt))
+               if (transfer_to[cnt] == NODQUOT || !dqh_has_quota_enabled(dqh, cnt))
                        continue;
                transfer_from[cnt] = dqduplicate(inode->i_dquot[cnt]);
                if (transfer_from[cnt] == NODQUOT)      /* Can happen on quotafiles 
(quota isn't initialized on them)... */
@@ -1254,19 +1332,17 @@
 }
 
 /* Function in inode.c - remove pointers to dquots in icache */
-extern void remove_dquot_ref(struct super_block *, int);
+extern void remove_dquot_ref(struct dqhash *, int);
 
 /*
  * Turn quota off on a device. type == -1 ==> quotaoff for all types (umount)
  */
-int vfs_quota_off(struct super_block *sb, int type)
+int vfs_quota_off(struct dqhash *hash, int type)
 {
        int cnt;
-       struct quota_info *dqopt = sb_dqopt(sb);
+       struct quota_info *dqopt = dqh_dqopt(hash);
 
        lock_kernel();
-       if (!sb)
-               goto out;
 
        /* We need to serialize quota_off() for device */
        down(&dqopt->dqoff_sem);
@@ -1278,12 +1354,12 @@
                reset_enable_flags(dqopt, cnt);
 
                /* Note: these are blocking operations */
-               remove_dquot_ref(sb, cnt);
-               invalidate_dquots(sb, cnt);
+               remove_dquot_ref(hash, cnt);
+               invalidate_dquots(hash, cnt);
                if (info_dirty(&dqopt->info[cnt]))
-                       dqopt->ops[cnt]->write_file_info(sb, cnt);
+                       dqopt->ops[cnt]->write_file_info(hash, cnt);
                if (dqopt->ops[cnt]->free_file_info)
-                       dqopt->ops[cnt]->free_file_info(sb, cnt);
+                       dqopt->ops[cnt]->free_file_info(hash, cnt);
                put_quota_format(dqopt->info[cnt].dqi_format);
 
                fput(dqopt->files[cnt]);
@@ -1294,16 +1370,15 @@
                dqopt->ops[cnt] = NULL;
        }
        up(&dqopt->dqoff_sem);
-out:
        unlock_kernel();
        return 0;
 }
 
-int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path)
+int vfs_quota_on(struct dqhash *hash, int type, int format_id, char *path)
 {
        struct file *f = NULL;
        struct inode *inode;
-       struct quota_info *dqopt = sb_dqopt(sb);
+       struct quota_info *dqopt = dqh_dqopt(hash);
        struct quota_format_type *fmt = find_quota_format(format_id);
        int error;
 
@@ -1330,7 +1405,7 @@
        if (!S_ISREG(inode->i_mode))
                goto out_f;
        error = -EINVAL;
-       if (!fmt->qf_ops->check_quota_file(sb, type))
+       if (!fmt->qf_ops->check_quota_file(hash, type))
                goto out_f;
        /* We don't want quota on quota files */
        dquot_drop(inode);
@@ -1338,11 +1413,11 @@
 
        dqopt->ops[type] = fmt->qf_ops;
        dqopt->info[type].dqi_format = fmt;
-       if ((error = dqopt->ops[type]->read_file_info(sb, type)) < 0)
+       if ((error = dqopt->ops[type]->read_file_info(hash, type)) < 0)
                goto out_f;
        set_enable_flags(dqopt, type);
 
-       add_dquot_ref(sb, type);
+       add_dquot_ref(hash, type);
 
        up(&dqopt->dqoff_sem);
        return 0;
@@ -1375,9 +1450,9 @@
        di->dqb_valid = QIF_ALL;
 }
 
-int vfs_get_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di)
+int vfs_get_dqblk(struct dqhash *hash, int type, qid_t id, struct if_dqblk *di)
 {
-       struct dquot *dquot = dqget(sb, id, type);
+       struct dquot *dquot = dqget(hash, id, type);
 
        if (!dquot)
                return -EINVAL;
@@ -1421,7 +1496,7 @@
                        dquot->dq_flags &= ~DQ_BLKS;
                }
                else if (!(di->dqb_valid & QIF_BTIME))  /* Set grace only if user 
hasn't provided his own... */
-                       dm->dqb_btime = CURRENT_TIME + 
sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_bgrace;
+                       dm->dqb_btime = CURRENT_TIME + 
dqh_dqopt(dquot->dq_dqh)->info[dquot->dq_type].dqi_bgrace;
        }
        if (check_ilim) {
                if (!dm->dqb_isoftlimit || dm->dqb_curinodes < dm->dqb_isoftlimit) {
@@ -1429,7 +1504,7 @@
                        dquot->dq_flags &= ~DQ_INODES;
                }
                else if (!(di->dqb_valid & QIF_ITIME))  /* Set grace only if user 
hasn't provided his own... */
-                       dm->dqb_itime = CURRENT_TIME + 
sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_igrace;
+                       dm->dqb_itime = CURRENT_TIME + 
dqh_dqopt(dquot->dq_dqh)->info[dquot->dq_type].dqi_igrace;
        }
        if (dm->dqb_bhardlimit || dm->dqb_bsoftlimit || dm->dqb_ihardlimit || 
dm->dqb_isoftlimit)
                dquot->dq_flags &= ~DQ_FAKE;
@@ -1438,9 +1513,9 @@
        dquot->dq_flags |= DQ_MOD;
 }
 
-int vfs_set_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di)
+int vfs_set_dqblk(struct dqhash *hash, int type, qid_t id, struct if_dqblk *di)
 {
-       struct dquot *dquot = dqget(sb, id, type);
+       struct dquot *dquot = dqget(hash, id, type);
 
        if (!dquot)
                return -EINVAL;
@@ -1450,9 +1525,9 @@
 }
 
 /* Generic routine for getting common part of quota file information */
-int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
+int vfs_get_dqinfo(struct dqhash *hash, int type, struct if_dqinfo *ii)
 {
-       struct mem_dqinfo *mi = sb_dqopt(sb)->info + type;
+       struct mem_dqinfo *mi = dqh_dqopt(hash)->info + type;
 
        ii->dqi_bgrace = mi->dqi_bgrace;
        ii->dqi_igrace = mi->dqi_igrace;
@@ -1462,9 +1537,9 @@
 }
 
 /* Generic routine for setting common part of quota file information */
-int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
+int vfs_set_dqinfo(struct dqhash *hash, int type, struct if_dqinfo *ii)
 {
-       struct mem_dqinfo *mi = sb_dqopt(sb)->info + type;
+       struct mem_dqinfo *mi = dqh_dqopt(hash)->info + type;
 
        if (ii->dqi_valid & IIF_BGRACE)
                mi->dqi_bgrace = ii->dqi_bgrace;
@@ -1510,11 +1585,7 @@
 
 static int __init dquot_init(void)
 {
-       int i;
-
        register_sysctl_table(sys_table, 0);
-       for (i = 0; i < NR_DQHASH; i++)
-               INIT_LIST_HEAD(dquot_hash + i);
        printk(KERN_NOTICE "VFS: Disk quotas v%s\n", __DQUOT_VERSION__);
 
        return 0;
diff -NurP --minimal linux-2.4.22-pre7/fs/ext3/super.c 
linux-2.4.22-pre7-mq0.06/fs/ext3/super.c
--- linux-2.4.22-pre7/fs/ext3/super.c   2003-07-19 14:14:30.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06/fs/ext3/super.c    2003-07-21 04:58:04.000000000 +0200
@@ -1131,7 +1131,7 @@
         * set up enough so that it can read an inode
         */
        sb->s_op = &ext3_sops;
-       sb->dq_op = &ext3_qops;
+       sb->s_dqh->dqh_qop = &ext3_qops;
        INIT_LIST_HEAD(&sbi->s_orphan); /* unlinked but open files */
 
        sb->s_root = 0;
@@ -1783,7 +1783,7 @@
 {
        int nblocks, ret;
        handle_t *handle;
-       struct quota_info *dqops = sb_dqopt(dquot->dq_sb);
+       struct quota_info *dqops = dqh_dqopt(dquot->dq_dqh);
        struct inode *qinode;
 
        switch (dqops->info[dquot->dq_type].dqi_format->qf_fmt_id) {
diff -NurP --minimal linux-2.4.22-pre7/fs/inode.c linux-2.4.22-pre7-mq0.06/fs/inode.c
--- linux-2.4.22-pre7/fs/inode.c        2003-07-19 14:14:30.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06/fs/inode.c 2003-07-22 02:51:57.000000000 +0200
@@ -95,6 +95,7 @@
                struct address_space * const mapping = &inode->i_data;
 
                inode->i_sb = sb;
+               inode->i_dqh = sb->s_dqh;       /* this is the default */
                inode->i_dev = sb->s_dev;
                inode->i_blkbits = sb->s_blocksize_bits;
                inode->i_flags = 0;
@@ -1205,13 +1206,14 @@
 void put_dquot_list(struct list_head *);
 int remove_inode_dquot_ref(struct inode *, short, struct list_head *);
 
-void remove_dquot_ref(struct super_block *sb, short type)
+void remove_dquot_ref(struct dqhash *hash, short type)
 {
        struct inode *inode;
        struct list_head *act_head;
+       struct super_block *sb = hash->dqh_sb;
        LIST_HEAD(tofree_head);
 
-       if (!sb->dq_op)
+       if (!hash->dqh_qop)
                return; /* nothing to do */
        /* We have to be protected against other CPUs */
        lock_kernel();          /* This lock is for quota code */
@@ -1219,22 +1221,22 @@
  
        list_for_each(act_head, &inode_in_use) {
                inode = list_entry(act_head, struct inode, i_list);
-               if (inode->i_sb == sb && IS_QUOTAINIT(inode))
+               if (inode->i_dqh == hash && IS_QUOTAINIT(inode))
                        remove_inode_dquot_ref(inode, type, &tofree_head);
        }
        list_for_each(act_head, &inode_unused) {
                inode = list_entry(act_head, struct inode, i_list);
-               if (inode->i_sb == sb && IS_QUOTAINIT(inode))
+               if (inode->i_dqh == hash && IS_QUOTAINIT(inode))
                        remove_inode_dquot_ref(inode, type, &tofree_head);
        }
        list_for_each(act_head, &sb->s_dirty) {
                inode = list_entry(act_head, struct inode, i_list);
-               if (IS_QUOTAINIT(inode))
+               if (inode->i_dqh == hash && IS_QUOTAINIT(inode))
                        remove_inode_dquot_ref(inode, type, &tofree_head);
        }
        list_for_each(act_head, &sb->s_locked_inodes) {
                inode = list_entry(act_head, struct inode, i_list);
-               if (IS_QUOTAINIT(inode))
+               if (inode->i_dqh == hash && IS_QUOTAINIT(inode))
                        remove_inode_dquot_ref(inode, type, &tofree_head);
        }
        spin_unlock(&inode_lock);
diff -NurP --minimal linux-2.4.22-pre7/fs/namespace.c 
linux-2.4.22-pre7-mq0.06/fs/namespace.c
--- linux-2.4.22-pre7/fs/namespace.c    2003-06-13 16:51:37.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06/fs/namespace.c     2003-07-21 04:58:04.000000000 +0200
@@ -337,7 +337,7 @@
                /* last instance - try to be smart */
                spin_unlock(&dcache_lock);
                lock_kernel();
-               DQUOT_OFF(sb);
+               DQUOT_OFF(sb->s_dqh);
                acct_auto_close(sb->s_dev);
                unlock_kernel();
                spin_lock(&dcache_lock);
diff -NurP --minimal linux-2.4.22-pre7/fs/quota.c linux-2.4.22-pre7-mq0.06/fs/quota.c
--- linux-2.4.22-pre7/fs/quota.c        2003-07-19 14:14:30.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06/fs/quota.c 2003-07-22 02:51:57.000000000 +0200
@@ -17,63 +17,63 @@
 struct dqstats dqstats;
 
 /* Check validity of quotactl */
-static int check_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id)
+static int check_quotactl_valid(struct dqhash *hash, int type, int cmd, qid_t id)
 {
        if (type >= MAXQUOTAS)
                return -EINVAL;
-       if (!sb && cmd != Q_SYNC)
+       if (!hash && cmd != Q_SYNC)
                return -ENODEV;
        /* Is operation supported? */
-       if (sb && !sb->s_qcop)
+       if (hash && !hash->dqh_qcop)
                return -ENOSYS;
 
        switch (cmd) {
                case Q_GETFMT:
                        break;
                case Q_QUOTAON:
-                       if (!sb->s_qcop->quota_on)
+                       if (!hash->dqh_qcop->quota_on)
                                return -ENOSYS;
                        break;
                case Q_QUOTAOFF:
-                       if (!sb->s_qcop->quota_off)
+                       if (!hash->dqh_qcop->quota_off)
                                return -ENOSYS;
                        break;
                case Q_SETINFO:
-                       if (!sb->s_qcop->set_info)
+                       if (!hash->dqh_qcop->set_info)
                                return -ENOSYS;
                        break;
                case Q_GETINFO:
-                       if (!sb->s_qcop->get_info)
+                       if (!hash->dqh_qcop->get_info)
                                return -ENOSYS;
                        break;
                case Q_SETQUOTA:
-                       if (!sb->s_qcop->set_dqblk)
+                       if (!hash->dqh_qcop->set_dqblk)
                                return -ENOSYS;
                        break;
                case Q_GETQUOTA:
-                       if (!sb->s_qcop->get_dqblk)
+                       if (!hash->dqh_qcop->get_dqblk)
                                return -ENOSYS;
                        break;
                case Q_SYNC:
-                       if (sb && !sb->s_qcop->quota_sync)
+                       if (hash && !hash->dqh_qcop->quota_sync)
                                return -ENOSYS;
                        break;
                case Q_XQUOTAON:
                case Q_XQUOTAOFF:
                case Q_XQUOTARM:
-                       if (!sb->s_qcop->set_xstate)
+                       if (!hash->dqh_qcop->set_xstate)
                                return -ENOSYS;
                        break;
                case Q_XGETQSTAT:
-                       if (!sb->s_qcop->get_xstate)
+                       if (!hash->dqh_qcop->get_xstate)
                                return -ENOSYS;
                        break;
                case Q_XSETQLIM:
-                       if (!sb->s_qcop->set_xquota)
+                       if (!hash->dqh_qcop->set_xquota)
                                return -ENOSYS;
                        break;
                case Q_XGETQUOTA:
-                       if (!sb->s_qcop->get_xquota)
+                       if (!hash->dqh_qcop->get_xquota)
                                return -ENOSYS;
                        break;
                default:
@@ -88,7 +88,7 @@
                case Q_SETINFO:
                case Q_SETQUOTA:
                case Q_GETQUOTA:
-                       if (!sb_has_quota_enabled(sb, type))
+                       if (!dqh_has_quota_enabled(hash, type))
                                return -ESRCH;
        }
        /* Check privileges */
@@ -134,7 +134,7 @@
 }
 
 /* Copy parameters and call proper function */
-static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, caddr_t 
addr)
+static int do_quotactl(struct dqhash *hash, int type, int cmd, qid_t id, caddr_t addr)
 {
        int ret;
 
@@ -144,17 +144,17 @@
 
                        if (IS_ERR(pathname = getname(addr)))
                                return PTR_ERR(pathname);
-                       ret = sb->s_qcop->quota_on(sb, type, id, pathname);
+                       ret = hash->dqh_qcop->quota_on(hash, type, id, pathname);
                        putname(pathname);
                        return ret;
                }
                case Q_QUOTAOFF:
-                       return sb->s_qcop->quota_off(sb, type);
+                       return hash->dqh_qcop->quota_off(hash, type);
 
                case Q_GETFMT: {
                        __u32 fmt;
 
-                       fmt = sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id;
+                       fmt = dqh_dqopt(hash)->info[type].dqi_format->qf_fmt_id;
                        if (copy_to_user(addr, &fmt, sizeof(fmt)))
                                return -EFAULT;
                        return 0;
@@ -162,7 +162,7 @@
                case Q_GETINFO: {
                        struct if_dqinfo info;
 
-                       if ((ret = sb->s_qcop->get_info(sb, type, &info)))
+                       if ((ret = hash->dqh_qcop->get_info(hash, type, &info)))
                                return ret;
                        if (copy_to_user(addr, &info, sizeof(info)))
                                return -EFAULT;
@@ -173,12 +173,12 @@
 
                        if (copy_from_user(&info, addr, sizeof(info)))
                                return -EFAULT;
-                       return sb->s_qcop->set_info(sb, type, &info);
+                       return hash->dqh_qcop->set_info(hash, type, &info);
                }
                case Q_GETQUOTA: {
                        struct if_dqblk idq;
 
-                       if ((ret = sb->s_qcop->get_dqblk(sb, type, id, &idq)))
+                       if ((ret = hash->dqh_qcop->get_dqblk(hash, type, id, &idq)))
                                return ret;
                        if (copy_to_user(addr, &idq, sizeof(idq)))
                                return -EFAULT;
@@ -189,11 +189,11 @@
 
                        if (copy_from_user(&idq, addr, sizeof(idq)))
                                return -EFAULT;
-                       return sb->s_qcop->set_dqblk(sb, type, id, &idq);
+                       return hash->dqh_qcop->set_dqblk(hash, type, id, &idq);
                }
                case Q_SYNC:
-                       if (sb)
-                               return sb->s_qcop->quota_sync(sb, type);
+                       if (hash)
+                               return hash->dqh_qcop->quota_sync(hash, type);
                        sync_dquots_dev(NODEV, type);
                        return 0;
                case Q_XQUOTAON:
@@ -203,12 +203,12 @@
 
                        if (copy_from_user(&flags, addr, sizeof(flags)))
                                return -EFAULT;
-                       return sb->s_qcop->set_xstate(sb, flags, cmd);
+                       return hash->dqh_qcop->set_xstate(hash, flags, cmd);
                }
                case Q_XGETQSTAT: {
                        struct fs_quota_stat fqs;
                
-                       if ((ret = sb->s_qcop->get_xstate(sb, &fqs)))
+                       if ((ret = hash->dqh_qcop->get_xstate(hash, &fqs)))
                                return ret;
                        if (copy_to_user(addr, &fqs, sizeof(fqs)))
                                return -EFAULT;
@@ -219,12 +219,12 @@
 
                        if (copy_from_user(&fdq, addr, sizeof(fdq)))
                                return -EFAULT;
-                      return sb->s_qcop->set_xquota(sb, type, id, &fdq);
+                      return hash->dqh_qcop->set_xquota(hash, type, id, &fdq);
                }
                case Q_XGETQUOTA: {
                        struct fs_disk_quota fdq;
 
-                       if ((ret = sb->s_qcop->get_xquota(sb, type, id, &fdq)))
+                       if ((ret = hash->dqh_qcop->get_xquota(hash, type, id, &fdq)))
                                return ret;
                        if (copy_to_user(addr, &fdq, sizeof(fdq)))
                                return -EFAULT;
@@ -237,40 +237,40 @@
        return 0;
 }
 
-static int check_compat_quotactl_valid(struct super_block *sb, int type, int cmd, 
qid_t id)
+static int check_compat_quotactl_valid(struct dqhash *hash, int type, int cmd, qid_t 
id)
 {
        if (type >= MAXQUOTAS)
                return -EINVAL;
        /* Is operation supported? */
        /* sb==NULL for GETSTATS calls */
-       if (sb && !sb->s_qcop)
+       if (hash && !hash->dqh_qcop)
                return -ENOSYS;
 
        switch (cmd) {
                case Q_COMP_QUOTAON:
-                       if (!sb->s_qcop->quota_on)
+                       if (!hash || !hash->dqh_qcop->quota_on)
                                return -ENOSYS;
                        break;
                case Q_COMP_QUOTAOFF:
-                       if (!sb->s_qcop->quota_off)
+                       if (!hash || !hash->dqh_qcop->quota_off)
                                return -ENOSYS;
                        break;
                case Q_COMP_SYNC:
-                       if (sb && !sb->s_qcop->quota_sync)
+                       if (!hash || !hash->dqh_qcop->quota_sync)
                                return -ENOSYS;
                        break;
                case Q_V1_SETQLIM:
                case Q_V1_SETUSE:
                case Q_V1_SETQUOTA:
-                       if (!sb->s_qcop->set_dqblk)
+                       if (!hash || !hash->dqh_qcop->set_dqblk)
                                return -ENOSYS;
                        break;
                case Q_V1_GETQUOTA:
-                       if (!sb->s_qcop->get_dqblk)
+                       if (!hash || !hash->dqh_qcop->get_dqblk)
                                return -ENOSYS;
                        break;
                case Q_V1_RSQUASH:
-                       if (!sb->s_qcop->set_info)
+                       if (!hash || !hash->dqh_qcop->set_info)
                                return -ENOSYS;
                        break;
                case Q_V1_GETSTATS:
@@ -295,13 +295,14 @@
                case Q_V2_SETUSE:
                case Q_V1_GETQUOTA:
                case Q_V2_GETQUOTA:
-                       if (!sb_has_quota_enabled(sb, type))
+                       if (!dqh_has_quota_enabled(hash, type))
                                return -ESRCH;
        }
        if (cmd != Q_COMP_QUOTAON &&
            cmd != Q_COMP_QUOTAOFF &&
            cmd != Q_COMP_SYNC &&
-           sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id != QFMT_VFS_OLD)
+           hash &&
+           dqh_dqopt(hash)->info[type].dqi_format->qf_fmt_id != QFMT_VFS_OLD)
                return -ESRCH;
 
        /* Check privileges */
@@ -317,21 +318,21 @@
        return 0;
 }
 
-static int v1_set_rsquash(struct super_block *sb, int type, int flag)
+static int v1_set_rsquash(struct dqhash *hash, int type, int flag)
 {
        struct if_dqinfo info;
 
        info.dqi_valid = IIF_FLAGS;
        info.dqi_flags = flag ? V1_DQF_RSQUASH : 0;
-       return sb->s_qcop->set_info(sb, type, &info);
+       return hash->dqh_qcop->set_info(hash, type, &info);
 }
 
-static int v1_get_dqblk(struct super_block *sb, int type, qid_t id, struct 
v1c_mem_dqblk *mdq)
+static int v1_get_dqblk(struct dqhash *hash, int type, qid_t id, struct v1c_mem_dqblk 
*mdq)
 {
        struct if_dqblk idq;
        int ret;
 
-       if ((ret = sb->s_qcop->get_dqblk(sb, type, id, &idq)) < 0)
+       if ((ret = hash->dqh_qcop->get_dqblk(hash, type, id, &idq)) < 0)
                return ret;
        mdq->dqb_ihardlimit = idq.dqb_ihardlimit;
        mdq->dqb_isoftlimit = idq.dqb_isoftlimit;
@@ -344,7 +345,7 @@
        if (id == 0) {  /* Times for id 0 are in fact grace times */
                struct if_dqinfo info;
 
-               if ((ret = sb->s_qcop->get_info(sb, type, &info)) < 0)
+               if ((ret = hash->dqh_qcop->get_info(hash, type, &info)) < 0)
                        return ret;
                mdq->dqb_btime = info.dqi_bgrace;
                mdq->dqb_itime = info.dqi_igrace;
@@ -352,7 +353,7 @@
        return 0;
 }
 
-static int v1_set_dqblk(struct super_block *sb, int type, int cmd, qid_t id, struct 
v1c_mem_dqblk *mdq)
+static int v1_set_dqblk(struct dqhash *hash, int type, int cmd, qid_t id, struct 
v1c_mem_dqblk *mdq)
 {
        struct if_dqblk idq;
        int ret;
@@ -370,14 +371,14 @@
                idq.dqb_curspace = ((qsize_t)mdq->dqb_curblocks) << QUOTABLOCK_BITS;
                idq.dqb_valid |= QIF_USAGE;
        }
-       ret = sb->s_qcop->set_dqblk(sb, type, id, &idq);
+       ret = hash->dqh_qcop->set_dqblk(hash, type, id, &idq);
        if (!ret && id == 0 && cmd == Q_V1_SETQUOTA) {  /* Times for id 0 are in fact 
grace times */
                struct if_dqinfo info;
 
                info.dqi_bgrace = mdq->dqb_btime;
                info.dqi_igrace = mdq->dqb_itime;
                info.dqi_valid = IIF_BGRACE | IIF_IGRACE;
-               ret = sb->s_qcop->set_info(sb, type, &info);
+               ret = hash->dqh_qcop->set_info(hash, type, &info);
        }
        return ret;
 }
@@ -388,7 +389,7 @@
 }
 
 /* Handle requests to old interface */
-static int do_compat_quotactl(struct super_block *sb, int type, int cmd, qid_t id, 
caddr_t addr)
+static int do_compat_quotactl(struct dqhash *hash, int type, int cmd, qid_t id, 
caddr_t addr)
 {
        int ret;
 
@@ -398,15 +399,15 @@
 
                        if (IS_ERR(pathname = getname(addr)))
                                return PTR_ERR(pathname);
-                       ret = sb->s_qcop->quota_on(sb, type, QFMT_VFS_OLD, pathname);
+                       ret = hash->dqh_qcop->quota_on(hash, type, QFMT_VFS_OLD, 
pathname);
                        putname(pathname);
                        return ret;
                }
                case Q_COMP_QUOTAOFF:
-                       return sb->s_qcop->quota_off(sb, type);
+                       return hash->dqh_qcop->quota_off(hash, type);
                case Q_COMP_SYNC:
-                       if (sb)
-                               return sb->s_qcop->quota_sync(sb, type);
+                       if (hash)
+                               return hash->dqh_qcop->quota_sync(hash, type);
                        sync_dquots_dev(NODEV, type);
                        return 0;
                case Q_V1_RSQUASH: {
@@ -414,12 +415,12 @@
 
                        if (copy_from_user(&flag, addr, sizeof(flag)))
                                return -EFAULT;
-                       return v1_set_rsquash(sb, type, flag);
+                       return v1_set_rsquash(hash, type, flag);
                }
                case Q_V1_GETQUOTA: {
                        struct v1c_mem_dqblk mdq;
 
-                       if ((ret = v1_get_dqblk(sb, type, id, &mdq)))
+                       if ((ret = v1_get_dqblk(hash, type, id, &mdq)))
                                return ret;
                        if (copy_to_user(addr, &mdq, sizeof(mdq)))
                                return -EFAULT;
@@ -432,7 +433,7 @@
 
                        if (copy_from_user(&mdq, addr, sizeof(mdq)))
                                return -EFAULT;
-                       return v1_set_dqblk(sb, type, cmd, id, &mdq);
+                       return v1_set_dqblk(hash, type, cmd, id, &mdq);
                }
                case Q_V1_GETSTATS: {
                        struct v1c_dqstats dst;
@@ -461,6 +462,7 @@
 {
        uint cmds, type;
        struct super_block *sb = NULL;
+       struct dqhash *dqh = NULL;
        int ret = -EINVAL;
 
        lock_kernel();
@@ -472,15 +474,18 @@
                sb = NULL;
                goto out;
        }
+       else if (sb)
+               dqh = sb->s_dqh;
+               
        if (!NEW_COMMAND(cmds) && !XQM_COMMAND(cmds)) {
-               if ((ret = check_compat_quotactl_valid(sb, type, cmds, id)) < 0)
+               if ((ret = check_compat_quotactl_valid(dqh, type, cmds, id)) < 0)
                        goto out;
-               ret = do_compat_quotactl(sb, type, cmds, id, addr);
+               ret = do_compat_quotactl(dqh, type, cmds, id, addr);
                goto out;
        }
-       if ((ret = check_quotactl_valid(sb, type, cmds, id)) < 0)
+       if ((ret = check_quotactl_valid(dqh, type, cmds, id)) < 0)
                goto out;
-       ret = do_quotactl(sb, type, cmds, id, addr);
+       ret = do_quotactl(dqh, type, cmds, id, addr);
 out:
        if (sb)
                drop_super(sb);
diff -NurP --minimal linux-2.4.22-pre7/fs/quota_v1.c 
linux-2.4.22-pre7-mq0.06/fs/quota_v1.c
--- linux-2.4.22-pre7/fs/quota_v1.c     2003-07-19 14:14:30.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06/fs/quota_v1.c      2003-07-21 04:58:04.000000000 +0200
@@ -42,7 +42,7 @@
        loff_t offset;
        struct v1_disk_dqblk dqblk;
 
-       filp = sb_dqopt(dquot->dq_sb)->files[type];
+       filp = dqh_dqopt(dquot->dq_dqh)->files[type];
        if (filp == (struct file *)NULL)
                return -EINVAL;
 
@@ -71,7 +71,7 @@
        ssize_t ret;
        struct v1_disk_dqblk dqblk;
 
-       filp = sb_dqopt(dquot->dq_sb)->files[type];
+       filp = dqh_dqopt(dquot->dq_dqh)->files[type];
        offset = v1_dqoff(dquot->dq_id);
        fs = get_fs();
        set_fs(KERNEL_DS);
@@ -83,8 +83,8 @@
        v1_mem2disk_dqblk(&dqblk, &dquot->dq_dqb);
        dquot->dq_flags &= ~DQ_MOD;
        if (dquot->dq_id == 0) {
-               dqblk.dqb_btime = sb_dqopt(dquot->dq_sb)->info[type].dqi_bgrace;
-               dqblk.dqb_itime = sb_dqopt(dquot->dq_sb)->info[type].dqi_igrace;
+               dqblk.dqb_btime = dqh_dqopt(dquot->dq_dqh)->info[type].dqi_bgrace;
+               dqblk.dqb_itime = dqh_dqopt(dquot->dq_dqh)->info[type].dqi_igrace;
        }
        ret = 0;
        if (filp)
@@ -117,9 +117,9 @@
        __u32 dqh_version;      /* File version */
 };
 
-static int v1_check_quota_file(struct super_block *sb, int type)
+static int v1_check_quota_file(struct dqhash *hash, int type)
 {
-       struct file *f = sb_dqopt(sb)->files[type];
+       struct file *f = dqh_dqopt(hash)->files[type];
        struct inode *inode = f->f_dentry->d_inode;
        ulong blocks;
        size_t off; 
@@ -144,13 +144,13 @@
                return 1;       /* Probably not new format */
        if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type])
                return 1;       /* Definitely not new format */
-       printk(KERN_INFO "VFS: %s: Refusing to turn on old quota format on given file. 
It probably contains newer quota format.\n", kdevname(sb->s_dev));
+       printk(KERN_INFO "VFS: %s: Refusing to turn on old quota format on given file. 
It probably contains newer quota format.\n", kdevname(hash->dqh_sb->s_dev));
         return 0;              /* Seems like a new format file -> refuse it */
 }
 
-static int v1_read_file_info(struct super_block *sb, int type)
+static int v1_read_file_info(struct dqhash *hash, int type)
 {
-       struct quota_info *dqopt = sb_dqopt(sb);
+       struct quota_info *dqopt = dqh_dqopt(hash);
        mm_segment_t fs;
        loff_t offset;
        struct file *filp = dqopt->files[type];
@@ -175,9 +175,9 @@
        return ret;
 }
 
-static int v1_write_file_info(struct super_block *sb, int type)
+static int v1_write_file_info(struct dqhash *hash, int type)
 {
-       struct quota_info *dqopt = sb_dqopt(sb);
+       struct quota_info *dqopt = dqh_dqopt(hash);
        mm_segment_t fs;
        struct file *filp = dqopt->files[type];
        struct v1_disk_dqblk dqblk;
diff -NurP --minimal linux-2.4.22-pre7/fs/quota_v2.c 
linux-2.4.22-pre7-mq0.06/fs/quota_v2.c
--- linux-2.4.22-pre7/fs/quota_v2.c     2003-07-19 14:14:30.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06/fs/quota_v2.c      2003-07-21 04:58:04.000000000 +0200
@@ -22,10 +22,10 @@
 #define GETENTRIES(buf) ((struct v2_disk_dqblk *)(((char *)buf)+sizeof(struct 
v2_disk_dqdbheader)))
 
 /* Check whether given file is really vfsv0 quotafile */
-static int v2_check_quota_file(struct super_block *sb, int type)
+static int v2_check_quota_file(struct dqhash *hash, int type)
 {
        struct v2_disk_dqheader dqhead;
-       struct file *f = sb_dqopt(sb)->files[type];
+       struct file *f = dqh_dqopt(hash)->files[type];
        mm_segment_t fs;
        ssize_t size;
        loff_t offset = 0;
@@ -45,12 +45,12 @@
 }
 
 /* Read information header from quota file */
-static int v2_read_file_info(struct super_block *sb, int type)
+static int v2_read_file_info(struct dqhash *hash, int type)
 {
        mm_segment_t fs;
        struct v2_disk_dqinfo dinfo;
-       struct mem_dqinfo *info = sb_dqopt(sb)->info+type;
-       struct file *f = sb_dqopt(sb)->files[type];
+       struct mem_dqinfo *info = dqh_dqopt(hash)->info+type;
+       struct file *f = dqh_dqopt(hash)->files[type];
        ssize_t size;
        loff_t offset = V2_DQINFOOFF;
 
@@ -73,12 +73,12 @@
 }
 
 /* Write information header to quota file */
-static int v2_write_file_info(struct super_block *sb, int type)
+static int v2_write_file_info(struct dqhash *hash, int type)
 {
        mm_segment_t fs;
        struct v2_disk_dqinfo dinfo;
-       struct mem_dqinfo *info = sb_dqopt(sb)->info+type;
-       struct file *f = sb_dqopt(sb)->files[type];
+       struct mem_dqinfo *info = dqh_dqopt(hash)->info+type;
+       struct file *f = dqh_dqopt(hash)->files[type];
        ssize_t size;
        loff_t offset = V2_DQINFOOFF;
 
@@ -281,8 +281,8 @@
 /* Find space for dquot */
 static uint find_free_dqentry(struct dquot *dquot, int *err)
 {
-       struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
-       struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info+dquot->dq_type;
+       struct file *filp = dqh_dqopt(dquot->dq_dqh)->files[dquot->dq_type];
+       struct mem_dqinfo *info = dqh_dqopt(dquot->dq_dqh)->info+dquot->dq_type;
        uint blk, i;
        struct v2_disk_dqdbheader *dh;
        struct v2_disk_dqblk *ddquot;
@@ -342,8 +342,8 @@
 /* Insert reference to structure into the trie */
 static int do_insert_tree(struct dquot *dquot, uint *treeblk, int depth)
 {
-       struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
-       struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type;
+       struct file *filp = dqh_dqopt(dquot->dq_dqh)->files[dquot->dq_type];
+       struct mem_dqinfo *info = dqh_dqopt(dquot->dq_dqh)->info + dquot->dq_type;
        dqbuf_t buf;
        int ret = 0, newson = 0, newact = 0;
        u32 *ref;
@@ -416,7 +416,7 @@
                        printk(KERN_ERR "VFS: Error %d occured while creating 
quota.\n", ret);
                        return ret;
                }
-       filp = sb_dqopt(dquot->dq_sb)->files[type];
+       filp = dqh_dqopt(dquot->dq_dqh)->files[type];
        offset = dquot->dq_off;
        mem2diskdqb(&ddquot, &dquot->dq_dqb, dquot->dq_id);
        fs = get_fs();
@@ -437,8 +437,8 @@
 /* Free dquot entry in data block */
 static int free_dqentry(struct dquot *dquot, uint blk)
 {
-       struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
-       struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type;
+       struct file *filp = dqh_dqopt(dquot->dq_dqh)->files[dquot->dq_type];
+       struct mem_dqinfo *info = dqh_dqopt(dquot->dq_dqh)->info + dquot->dq_type;
        struct v2_disk_dqdbheader *dh;
        dqbuf_t buf = getdqbuf();
        int ret = 0;
@@ -486,8 +486,8 @@
 /* Remove reference to dquot from tree */
 static int remove_tree(struct dquot *dquot, uint *blk, int depth)
 {
-       struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
-       struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type;
+       struct file *filp = dqh_dqopt(dquot->dq_dqh)->files[dquot->dq_type];
+       struct mem_dqinfo *info = dqh_dqopt(dquot->dq_dqh)->info + dquot->dq_type;
        dqbuf_t buf = getdqbuf();
        int ret = 0;
        uint newblk;
@@ -536,7 +536,7 @@
 /* Find entry in block */
 static loff_t find_block_dqentry(struct dquot *dquot, uint blk)
 {
-       struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+       struct file *filp = dqh_dqopt(dquot->dq_dqh)->files[dquot->dq_type];
        dqbuf_t buf = getdqbuf();
        loff_t ret = 0;
        int i;
@@ -573,7 +573,7 @@
 /* Find entry for given id in the tree */
 static loff_t find_tree_dqentry(struct dquot *dquot, uint blk, int depth)
 {
-       struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+       struct file *filp = dqh_dqopt(dquot->dq_dqh)->files[dquot->dq_type];
        dqbuf_t buf = getdqbuf();
        loff_t ret = 0;
        u32 *ref = (u32 *)buf;
@@ -612,10 +612,10 @@
        struct v2_disk_dqblk ddquot;
        int ret = 0;
 
-       filp = sb_dqopt(dquot->dq_sb)->files[type];
+       filp = dqh_dqopt(dquot->dq_dqh)->files[type];
 
 #ifdef __QUOTA_V2_PARANOIA
-       if (!filp || !dquot->dq_sb) {   /* Invalidated quota? */
+       if (!filp || !dquot->dq_dqh) {  /* Invalidated quota? */
                printk(KERN_ERR "VFS: Quota invalidated while reading!\n");
                return -EIO;
        }
diff -NurP --minimal linux-2.4.22-pre7/fs/super.c linux-2.4.22-pre7-mq0.06/fs/super.c
--- linux-2.4.22-pre7/fs/super.c        2003-07-19 14:14:30.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06/fs/super.c 2003-07-22 03:09:17.000000000 +0200
@@ -278,12 +278,10 @@
                atomic_set(&s->s_active, 1);
                sema_init(&s->s_vfs_rename_sem,1);
                sema_init(&s->s_nfsd_free_path_sem,1);
-               sema_init(&s->s_dquot.dqio_sem, 1);
-               sema_init(&s->s_dquot.dqoff_sem, 1);
                s->s_maxbytes = MAX_NON_LFS;
                s->s_op = &empty_sops;
-               s->dq_op = sb_dquot_ops;
-               s->s_qcop = sb_quotactl_ops;
+               /* quick hack to make dqhash id unique, sufficient for now */
+               s->s_dqh = new_dqhash(s, (unsigned int)s);
        }
        return s;
 }
@@ -296,6 +294,7 @@
  */
 static inline void destroy_super(struct super_block *s)
 {
+       destroy_dqhash(s->s_dqh);
        kfree(s);
 }
 
diff -NurP --minimal linux-2.4.22-pre7/fs/udf/super.c 
linux-2.4.22-pre7-mq0.06/fs/udf/super.c
--- linux-2.4.22-pre7/fs/udf/super.c    2002-08-03 02:39:45.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06/fs/udf/super.c     2003-07-21 04:58:04.000000000 +0200
@@ -1435,7 +1435,7 @@
 
        /* Fill in the rest of the superblock */
        sb->s_op = &udf_sb_ops;
-       sb->dq_op = NULL;
+       sb->s_dqh->dqh_qop = NULL;
        sb->s_dirt = 0;
        sb->s_magic = UDF_SUPER_MAGIC;
 
diff -NurP --minimal linux-2.4.22-pre7/fs/ufs/super.c 
linux-2.4.22-pre7-mq0.06/fs/ufs/super.c
--- linux-2.4.22-pre7/fs/ufs/super.c    2002-08-03 02:39:45.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06/fs/ufs/super.c     2003-07-21 04:58:04.000000000 +0200
@@ -744,7 +744,7 @@
        sb->s_blocksize = fs32_to_cpu(sb, usb1->fs_fsize);
        sb->s_blocksize_bits = fs32_to_cpu(sb, usb1->fs_fshift);
        sb->s_op = &ufs_super_ops;
-       sb->dq_op = NULL; /***/
+       sb->s_dqh->dqh_qop = NULL; /***/
        sb->s_magic = fs32_to_cpu(sb, usb3->fs_magic);
 
        uspi->s_sblkno = fs32_to_cpu(sb, usb1->fs_sblkno);
diff -NurP --minimal linux-2.4.22-pre7/include/linux/fs.h 
linux-2.4.22-pre7-mq0.06/include/linux/fs.h
--- linux-2.4.22-pre7/include/linux/fs.h        2003-07-21 23:28:32.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06/include/linux/fs.h 2003-07-22 03:12:14.000000000 +0200
@@ -469,6 +469,7 @@
        struct file_lock        *i_flock;
        struct address_space    *i_mapping;
        struct address_space    i_data;
+       struct dqhash           *i_dqh;
        struct dquot            *i_dquot[MAXQUOTAS];
        /* These three should probably be a union */
        struct list_head        i_devices;
@@ -744,8 +745,6 @@
        unsigned long long      s_maxbytes;     /* Max file size */
        struct file_system_type *s_type;
        struct super_operations *s_op;
-       struct dquot_operations *dq_op;
-       struct quotactl_ops     *s_qcop;
        unsigned long           s_flags;
        unsigned long           s_magic;
        struct dentry           *s_root;
@@ -760,7 +759,7 @@
 
        struct block_device     *s_bdev;
        struct list_head        s_instances;
-       struct quota_info       s_dquot;        /* Diskquota specific options */
+       struct dqhash           *s_dqh;         /* Diskquota hash */
 
        union {
                struct minix_sb_info    minix_sb;
diff -NurP --minimal linux-2.4.22-pre7/include/linux/mount.h 
linux-2.4.22-pre7-mq0.06/include/linux/mount.h
--- linux-2.4.22-pre7/include/linux/mount.h     2001-10-05 22:05:55.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06/include/linux/mount.h      2003-07-21 04:58:04.000000000 
+0200
@@ -29,6 +29,7 @@
        int mnt_flags;
        char *mnt_devname;              /* Name of device e.g. /dev/dsk/hda1 */
        struct list_head mnt_list;
+       struct dqhash *mnt_dqh;         /* Diskquota hash for mount */
 };
 
 static inline struct vfsmount *mntget(struct vfsmount *mnt)
diff -NurP --minimal linux-2.4.22-pre7/include/linux/quota.h 
linux-2.4.22-pre7-mq0.06/include/linux/quota.h
--- linux-2.4.22-pre7/include/linux/quota.h     2003-07-21 23:28:32.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06/include/linux/quota.h      2003-07-22 03:11:31.000000000 
+0200
@@ -182,7 +182,7 @@
 #define info_any_dirty(info) ((info)->dqi_flags & DQF_INFO_DIRTY ||\
                              (info)->dqi_flags & DQF_ANY_DQUOT_DIRTY)
 
-#define sb_dqopt(sb) (&(sb)->s_dquot)
+#define dqh_dqopt(dqh) (&(dqh)->dqh_dqopt)
 
 struct dqstats {
        int lookups;
@@ -197,7 +197,7 @@
 
 extern struct dqstats dqstats;
 
-#define NR_DQHASH 43            /* Just an arbitrary number */
+#define NR_DQHASH 13           /* Just an arbitrary number */
 
 #define DQ_LOCKED     0x01     /* dquot under IO */
 #define DQ_MOD        0x02     /* dquot modified since read */
@@ -216,7 +216,7 @@
        int dq_dup_ref;                 /* Number of duplicated refences */
 
        /* fields after this point are cleared when invalidating */
-       struct super_block *dq_sb;      /* superblock this applies to */
+       struct dqhash *dq_dqh;          /* quota hash backpointer */
        unsigned int dq_id;             /* ID this applies to (uid, gid) */
        kdev_t dq_dev;                  /* Device this applies to */
        loff_t dq_off;                  /* Offset of dquot on disk */
@@ -234,12 +234,12 @@
 
 /* Operations which must be implemented by each quota format */
 struct quota_format_ops {
-       int (*check_quota_file)(struct super_block *sb, int type);      /* Detect 
whether file is in our format */
-       int (*read_file_info)(struct super_block *sb, int type);        /* Read main 
info about file - called on quotaon() */
-       int (*write_file_info)(struct super_block *sb, int type);       /* Write main 
info about file */
-       int (*free_file_info)(struct super_block *sb, int type);        /* Called on 
quotaoff() */
-       int (*read_dqblk)(struct dquot *dquot);         /* Read structure for one user 
*/
-       int (*commit_dqblk)(struct dquot *dquot);       /* Write (or delete) structure 
for one user */
+       int (*check_quota_file)(struct dqhash *, int);  /* Detect whether file is in 
our format */
+       int (*read_file_info)(struct dqhash *, int);    /* Read main info about file - 
called on quotaon() */
+       int (*write_file_info)(struct dqhash *, int);   /* Write main info about file 
*/
+       int (*free_file_info)(struct dqhash *, int);    /* Called on quotaoff() */
+       int (*read_dqblk)(struct dquot *);              /* Read structure for one user 
*/
+       int (*commit_dqblk)(struct dquot *);            /* Write (or delete) structure 
for one user */
 };
 
 /* Operations working with dquots */
@@ -256,17 +256,17 @@
 
 /* Operations handling requests from userspace */
 struct quotactl_ops {
-       int (*quota_on)(struct super_block *, int, int, char *);
-       int (*quota_off)(struct super_block *, int);
-       int (*quota_sync)(struct super_block *, int);
-       int (*get_info)(struct super_block *, int, struct if_dqinfo *);
-       int (*set_info)(struct super_block *, int, struct if_dqinfo *);
-       int (*get_dqblk)(struct super_block *, int, qid_t, struct if_dqblk *);
-       int (*set_dqblk)(struct super_block *, int, qid_t, struct if_dqblk *);
-       int (*get_xstate)(struct super_block *, struct fs_quota_stat *);
-       int (*set_xstate)(struct super_block *, unsigned int, int);
-       int (*get_xquota)(struct super_block *, int, qid_t, struct fs_disk_quota *);
-       int (*set_xquota)(struct super_block *, int, qid_t, struct fs_disk_quota *);
+       int (*quota_on)(struct dqhash *, int, int, char *);
+       int (*quota_off)(struct dqhash *, int);
+       int (*quota_sync)(struct dqhash *, int);
+       int (*get_info)(struct dqhash *, int, struct if_dqinfo *);
+       int (*set_info)(struct dqhash *, int, struct if_dqinfo *);
+       int (*get_dqblk)(struct dqhash *, int, qid_t, struct if_dqblk *);
+       int (*set_dqblk)(struct dqhash *, int, qid_t, struct if_dqblk *);
+       int (*get_xstate)(struct dqhash *, struct fs_quota_stat *);
+       int (*set_xstate)(struct dqhash *, unsigned int, int);
+       int (*get_xquota)(struct dqhash *, int, qid_t, struct fs_disk_quota *);
+       int (*set_xquota)(struct dqhash *, int, qid_t, struct fs_disk_quota *);
 };
 
 struct quota_format_type {
@@ -288,11 +288,6 @@
        struct quota_format_ops *ops[MAXQUOTAS];        /* Operations for each type */
 };
 
-/* Inline would be better but we need to dereference super_block which is not defined 
yet */
-#define mark_dquot_dirty(dquot) do {\
-       dquot->dq_flags |= DQ_MOD;\
-       sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_flags |= DQF_ANY_DQUOT_DIRTY;\
-} while (0)
 
 #define dquot_dirty(dquot) ((dquot)->dq_flags & DQ_MOD)
 
@@ -307,14 +302,46 @@
        return 0;
 }
 
-#define sb_any_quota_enabled(sb) (is_enabled(sb_dqopt(sb), USRQUOTA) | 
is_enabled(sb_dqopt(sb), GRPQUOTA))
+#define dqh_any_quota_enabled(hash) (is_enabled(dqh_dqopt(hash), USRQUOTA)  \
+       | is_enabled(dqh_dqopt(hash), GRPQUOTA))
 
-#define sb_has_quota_enabled(sb, type) (is_enabled(sb_dqopt(sb), type))
+#define dqh_has_quota_enabled(hash, type) (is_enabled(dqh_dqopt(hash), type))
 
 int register_quota_format(struct quota_format_type *fmt);
 void unregister_quota_format(struct quota_format_type *fmt);
 void init_dquot_operations(struct dquot_operations *fsdqops);
 
+struct dqhash {
+       struct list_head dqh_list;      /* List of all quota hashes */
+       unsigned int dqh_id;            /* ID for hash */
+       struct quota_info dqh_dqopt;    /* Diskquota specific options */
+       struct dquot_operations *dqh_qop;
+       struct quotactl_ops *dqh_qcop;
+       struct super_block *dqh_sb;     /* super block */
+       struct list_head dqh_hash[NR_DQHASH];
+};
+
+#if defined(CONFIG_QUOTA)
+
+
+struct dqhash *new_dqhash(struct super_block *, unsigned int);
+void destroy_dqhash(struct dqhash *);
+struct dqhash *find_dqhash(unsigned int);
+
+static inline void mark_dquot_dirty(struct dquot *dq)
+{
+       dq->dq_flags |= DQ_MOD;
+       dqh_dqopt(dq->dq_dqh)->info[dq->dq_type].dqi_flags |= DQF_ANY_DQUOT_DIRTY;
+}
+
+#else /* CONFIG_QUOTA */
+
+#define new_dqhash(sb, dqdom)          (0)
+#define find_dqhash(dqdom)             (0)
+#define destroy_dqhash(hash)           do { } while(0)
+
+#endif /* CONFIG_QUOTA */
+
 #else
 
 # /* nodep */ include <sys/cdefs.h>
diff -NurP --minimal linux-2.4.22-pre7/include/linux/quotaops.h 
linux-2.4.22-pre7-mq0.06/include/linux/quotaops.h
--- linux-2.4.22-pre7/include/linux/quotaops.h  2003-07-21 23:28:52.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06/include/linux/quotaops.h   2003-07-22 03:23:13.000000000 
+0200
@@ -17,11 +17,13 @@
 
 #include <linux/fs.h>
 
+#define dprintk(...)   
+
 /*
  * declaration of quota_function calls in kernel.
  */
 extern void sync_dquots_dev(kdev_t dev, int type);
-extern void sync_dquots_sb(struct super_block *sb, int type);
+extern void sync_dquots_dqh(struct dqhash *hash, int type);
 
 extern void dquot_initialize(struct inode *inode, int type);
 extern void dquot_drop(struct inode *inode);
@@ -40,16 +42,13 @@
 extern struct dquot_operations dquot_operations;
 extern struct quotactl_ops vfs_quotactl_ops;
 
-#define sb_dquot_ops (&dquot_operations)
-#define sb_quotactl_ops (&vfs_quotactl_ops)
-
 static __inline__ void DQUOT_INIT(struct inode *inode)
 {
-       if (!inode->i_sb)
+       if (!inode->i_dqh)
                out_of_line_bug();
        lock_kernel();
-       if (sb_any_quota_enabled(inode->i_sb) && !IS_NOQUOTA(inode))
-               inode->i_sb->dq_op->initialize(inode, -1);
+       if (dqh_any_quota_enabled(inode->i_dqh) && !IS_NOQUOTA(inode))
+               inode->i_dqh->dqh_qop->initialize(inode, -1);
        unlock_kernel();
 }
 
@@ -57,9 +56,9 @@
 {
        lock_kernel();
        if (IS_QUOTAINIT(inode)) {
-               if (!inode->i_sb)
+               if (!inode->i_dqh)
                        out_of_line_bug();
-               inode->i_sb->dq_op->drop(inode);        /* Ops must be set when 
there's any quota... */
+               inode->i_dqh->dqh_qop->drop(inode);     /* Ops must be set when 
there's any quota... */
        }
        unlock_kernel();
 }
@@ -67,9 +66,9 @@
 static __inline__ int DQUOT_PREALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr)
 {
        lock_kernel();
-       if (sb_any_quota_enabled(inode->i_sb)) {
+       if (dqh_any_quota_enabled(inode->i_dqh)) {
                /* Used space is updated in alloc_space() */
-               if (inode->i_sb->dq_op->alloc_space(inode, nr, 1) == NO_QUOTA) {
+               if (inode->i_dqh->dqh_qop->alloc_space(inode, nr, 1) == NO_QUOTA) {
                        unlock_kernel();
                        return 1;
                }
@@ -91,9 +90,9 @@
 static __inline__ int DQUOT_ALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr)
 {
        lock_kernel();
-       if (sb_any_quota_enabled(inode->i_sb)) {
+       if (dqh_any_quota_enabled(inode->i_dqh)) {
                /* Used space is updated in alloc_space() */
-               if (inode->i_sb->dq_op->alloc_space(inode, nr, 0) == NO_QUOTA) {
+               if (inode->i_dqh->dqh_qop->alloc_space(inode, nr, 0) == NO_QUOTA) {
                        unlock_kernel();
                        return 1;
                }
@@ -115,9 +114,9 @@
 static __inline__ int DQUOT_ALLOC_INODE(struct inode *inode)
 {
        lock_kernel();
-       if (sb_any_quota_enabled(inode->i_sb)) {
+       if (dqh_any_quota_enabled(inode->i_dqh)) {
                DQUOT_INIT(inode);
-               if (inode->i_sb->dq_op->alloc_inode(inode, 1) == NO_QUOTA) {
+               if (inode->i_dqh->dqh_qop->alloc_inode(inode, 1) == NO_QUOTA) {
                        unlock_kernel();
                        return 1;
                }
@@ -129,8 +128,8 @@
 static __inline__ void DQUOT_FREE_SPACE_NODIRTY(struct inode *inode, qsize_t nr)
 {
        lock_kernel();
-       if (sb_any_quota_enabled(inode->i_sb))
-               inode->i_sb->dq_op->free_space(inode, nr);
+       if (dqh_any_quota_enabled(inode->i_dqh))
+               inode->i_dqh->dqh_qop->free_space(inode, nr);
        else
                inode_sub_bytes(inode, nr);
        unlock_kernel();
@@ -145,17 +144,17 @@
 static __inline__ void DQUOT_FREE_INODE(struct inode *inode)
 {
        lock_kernel();
-       if (sb_any_quota_enabled(inode->i_sb))
-               inode->i_sb->dq_op->free_inode(inode, 1);
+       if (dqh_any_quota_enabled(inode->i_dqh))
+               inode->i_dqh->dqh_qop->free_inode(inode, 1);
        unlock_kernel();
 }
 
 static __inline__ int DQUOT_TRANSFER(struct inode *inode, struct iattr *iattr)
 {
        lock_kernel();
-       if (sb_any_quota_enabled(inode->i_sb) && !IS_NOQUOTA(inode)) {
+       if (dqh_any_quota_enabled(inode->i_dqh) && !IS_NOQUOTA(inode)) {
                DQUOT_INIT(inode);
-               if (inode->i_sb->dq_op->transfer(inode, iattr) == NO_QUOTA) {
+               if (inode->i_dqh->dqh_qop->transfer(inode, iattr) == NO_QUOTA) {
                        unlock_kernel();
                        return 1;
                }
@@ -165,15 +164,15 @@
 }
 
 #define DQUOT_SYNC_DEV(dev)    sync_dquots_dev(dev, -1)
-#define DQUOT_SYNC_SB(sb)      sync_dquots_sb(sb, -1)
+#define DQUOT_SYNC_DQH(hash)   sync_dquots_dqh(hash, -1)
 
-static __inline__ int DQUOT_OFF(struct super_block *sb)
+static __inline__ int DQUOT_OFF(struct dqhash *hash)
 {
        int ret = -ENOSYS;
 
        lock_kernel();
-       if (sb->s_qcop && sb->s_qcop->quota_off)
-               ret = sb->s_qcop->quota_off(sb, -1);
+       if (hash->dqh_qcop && hash->dqh_qcop->quota_off)
+               ret = hash->dqh_qcop->quota_off(hash, -1);
        unlock_kernel();
        return ret;
 }
@@ -183,17 +182,16 @@
 /*
  * NO-OP when quota not configured.
  */
-#define sb_dquot_ops                           (NULL)
-#define sb_quotactl_ops                                (NULL)
 #define sync_dquots_dev(dev, type)             do { } while(0)
 #define DQUOT_INIT(inode)                      do { } while(0)
 #define DQUOT_DROP(inode)                      do { } while(0)
 #define DQUOT_ALLOC_INODE(inode)               (0)
 #define DQUOT_FREE_INODE(inode)                        do { } while(0)
 #define DQUOT_SYNC_DEV(dev)                    do { } while(0)
-#define DQUOT_SYNC_SB(sb)                      do { } while(0)
+#define DQUOT_SYNC_DQH(hash)                   do { } while(0)
 #define DQUOT_OFF(sb)                          do { } while(0)
 #define DQUOT_TRANSFER(inode, iattr)           (0)
+
 extern __inline__ int DQUOT_PREALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr)
 {
        lock_kernel();
;
; Bind VFS Mount Quota
;
; utilizing the quota hash abstraction done in mq-0.06
; this patch adds per vfsmount (--bind) quota for ext2.
;
; how it works: 
; each bind mount of type quota, creates his own quota domain, 
; which is propagated down from the mnt_root to each entry
; below (currently not on lookup, only on create)
;
; # mount -t quota --bind /mnt/somedir /mnt/somedir
;
; for now only ext2 is supported, but support for other
; filesystems should be trivial and straight forward.
;
; (C) 2003 Herbert P�tzl <[EMAIL PROTECTED]>
;
; Changelog:  
;
;   0.02  - first public release
;
; this patch is free software; you can redistribute it and/or
; modify it under the terms of the GNU General Public License
; as published by the Free Software Foundation; either version 2
; of the License, or (at your option) any later version.
; 
; this patch is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
; GNU General Public License for more details.
; 

diff -NurP --minimal linux-2.4.22-pre7-mq0.06/drivers/char/sysrq.c 
linux-2.4.22-pre7-mq0.06-bq0.02/drivers/char/sysrq.c
--- linux-2.4.22-pre7-mq0.06/drivers/char/sysrq.c       2003-07-21 04:58:04.000000000 
+0200
+++ linux-2.4.22-pre7-mq0.06-bq0.02/drivers/char/sysrq.c        2003-07-22 
00:56:05.000000000 +0200
@@ -146,6 +146,7 @@
        if (remount_flag) { /* Remount R/O */
                int ret, flags;
                struct list_head *p;
+               struct dqhash *dqh;
 
                if (sb->s_flags & MS_RDONLY) {
                        printk("R/O\n");
@@ -161,6 +162,8 @@
                }
                file_list_unlock();
                DQUOT_OFF(sb->s_dqh);
+               list_for_each_entry(dqh, &sb->s_dqhashes, dqh_list)
+                       DQUOT_OFF(dqh);
                fsync_dev(sb->s_dev);
                flags = MS_RDONLY;
                if (sb->s_op && sb->s_op->remount_fs) {
diff -NurP --minimal linux-2.4.22-pre7-mq0.06/fs/buffer.c 
linux-2.4.22-pre7-mq0.06-bq0.02/fs/buffer.c
--- linux-2.4.22-pre7-mq0.06/fs/buffer.c        2003-07-21 04:58:04.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06-bq0.02/fs/buffer.c 2003-07-22 00:56:05.000000000 +0200
@@ -331,12 +331,15 @@
 
 int fsync_super(struct super_block *sb)
 {
+       struct dqhash *dqh;
        kdev_t dev = sb->s_dev;
        sync_buffers(dev, 0);
 
        lock_kernel();
        sync_inodes_sb(sb);
        DQUOT_SYNC_DQH(sb->s_dqh);
+       list_for_each_entry(dqh, &sb->s_dqhashes, dqh_list)
+               DQUOT_SYNC_DQH(dqh);
        lock_super(sb);
        if (sb->s_dirt && sb->s_op && sb->s_op->write_super)
                sb->s_op->write_super(sb);
diff -NurP --minimal linux-2.4.22-pre7-mq0.06/fs/dquot.c 
linux-2.4.22-pre7-mq0.06-bq0.02/fs/dquot.c
--- linux-2.4.22-pre7-mq0.06/fs/dquot.c 2003-07-22 03:29:06.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06-bq0.02/fs/dquot.c  2003-07-22 00:59:05.000000000 +0200
@@ -177,7 +177,7 @@
 
 static inline int const hashfn(unsigned int id, int type)
 {
-       return (id * (MAXQUOTAS - type)) % NR_DQHASH;
+       return ((id + 42) * (MAXQUOTAS - type)) % NR_DQHASH;
 }
 
 static inline void insert_dquot_hash(struct dqhash *hash, struct dquot *dquot)
@@ -377,10 +377,10 @@
 
 /* Dquota Hash Management Functions */
 
-static LIST_HEAD(dqhash_list);
+static unsigned int dqhash_id = 0; 
 
 
-struct dqhash *new_dqhash(struct super_block *sb, unsigned int id)
+struct dqhash *new_dqhash(struct super_block *sb, unsigned long dqdom)
 {
        struct dqhash *hash;
        int i;
@@ -390,7 +390,9 @@
                return ERR_PTR(-ENOMEM);
 
        memset(hash, 0, sizeof(struct dqhash));
-       hash->dqh_id = id;
+       hash->dqh_id = dqhash_id++;
+       hash->dqh_dqdom = dqdom;
+       hash->dqh_count = 1;
        INIT_LIST_HEAD(&hash->dqh_list);
        for (i = 0; i < NR_DQHASH; i++)
                INIT_LIST_HEAD(hash->dqh_hash + i);
@@ -398,15 +400,17 @@
        sema_init(&hash->dqh_dqopt.dqoff_sem, 1);
        hash->dqh_qop = &dquot_operations;
        hash->dqh_qcop = &vfs_quotactl_ops;
-       hash->dqh_sb = sb;
+       hash->dqh_sb = sb;
 
        lock_kernel();
-       list_add(&hash->dqh_list, &dqhash_list);
+       list_add_tail(&hash->dqh_list, &sb->s_dqhashes);
        unlock_kernel();
        dprintk ("��� new_dqhash: %p [#%d,#0x%lx]\n", hash, hash->dqh_id, 
hash->dqh_dqdom);
        return hash;
 }
 
+extern void remove_dqhash_ref(struct dqhash *hash);
+
 void destroy_dqhash(struct dqhash *hash)
 {
        int cnt;
@@ -417,25 +421,37 @@
        unlock_kernel();
        for (cnt = 0; cnt < MAXQUOTAS; cnt++)
                invalidate_dquots(hash, cnt);
+       hash->dqh_dqdom = -1;
        kfree(hash);
 }
 
-
-struct dqhash *find_dqhash(unsigned int id)
+struct dqhash *get_existing_dqhash(struct super_block *sb, unsigned long dqdom)
 {
-       struct list_head *head;
        struct dqhash *hash;
 
        lock_kernel();
-       list_for_each(head, &dqhash_list) {
-               hash = list_entry(head, struct dqhash, dqh_list);
-               if (hash->dqh_id == id)
+       list_for_each_entry(hash, &sb->s_dqhashes, dqh_list)
+               if (hash->dqh_dqdom == dqdom)
                        goto dqh_found;
-       }
-       hash = NULL;
+       /* sorry no hash found */
+       unlock_kernel();
+       return NULL;
        
 dqh_found:
        unlock_kernel();
+       return dqhget(hash);
+}
+
+
+struct dqhash *get_dqhash(struct super_block *sb, unsigned long dqdom)
+{
+       struct dqhash *hash;
+
+       if (!sb)
+               BUG();  
+       hash = get_existing_dqhash(sb, dqdom);
+       if (!hash)
+               hash = new_dqhash(sb, dqdom);
        return hash;
 }
 
@@ -1023,7 +1039,7 @@
 void dquot_initialize(struct inode *inode, int type)
 {
        struct dquot *dquot[MAXQUOTAS];
-       struct dqhash *dqh = inode->i_sb->s_dqh;
+       struct dqhash *dqh = inode->i_dqh;
        unsigned int id = 0;
        int cnt;
 
@@ -1198,19 +1214,28 @@
 
 /*
  * Transfer the number of inode and blocks from one diskquota to an other.
- *
+ * If requested this, is also done from one quota hash to another.
  * This operation can block, but only after everything is updated
  */
-int dquot_transfer(struct inode *inode, struct iattr *iattr)
+int __dquot_transfer(struct inode *inode, struct iattr *iattr, struct dqhash *hash)
 {
        qsize_t space;
        struct dquot *transfer_from[MAXQUOTAS];
        struct dquot *transfer_to[MAXQUOTAS];
-       struct dqhash *dqh = inode->i_sb->s_dqh;
-       int cnt, ret = NO_QUOTA, chuid = (iattr->ia_valid & ATTR_UID) && inode->i_uid 
!= iattr->ia_uid,
-           chgid = (iattr->ia_valid & ATTR_GID) && inode->i_gid != iattr->ia_gid;
+       struct dqhash *dqh = inode->i_dqh;
+       uid_t new_uid = (iattr->ia_valid & ATTR_UID) ? iattr->ia_uid : inode->i_uid;
+       gid_t new_gid = (iattr->ia_valid & ATTR_GID) ? iattr->ia_gid : inode->i_gid;
+       int cnt, chuid, chgid;
        char warntype[MAXQUOTAS];
 
+       if (hash == dqh) {
+               chuid = (iattr->ia_valid & ATTR_UID) && inode->i_uid != iattr->ia_uid;
+               chgid = (iattr->ia_valid & ATTR_GID) && inode->i_gid != iattr->ia_gid;
+       }
+       /* transfer between quota hashes */
+       else 
+               chuid = chgid = 1;
+
        /* Clear the arrays */
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
                transfer_to[cnt] = transfer_from[cnt] = NODQUOT;
@@ -1218,18 +1244,18 @@
        }
        /* First build the transfer_to list - here we can block on reading of 
dquots... */
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
-               if (!dqh_has_quota_enabled(dqh, cnt))
+               if (!dqh_has_quota_enabled(hash, cnt))
                        continue;
                switch (cnt) {
                        case USRQUOTA:
                                if (!chuid)
                                        continue;
-                               transfer_to[cnt] = dqget(dqh, iattr->ia_uid, cnt);
+                               transfer_to[cnt] = dqget(hash, new_uid, cnt);
                                break;
                        case GRPQUOTA:
                                if (!chgid)
                                        continue;
-                               transfer_to[cnt] = dqget(dqh, iattr->ia_gid, cnt);
+                               transfer_to[cnt] = dqget(hash, new_gid, cnt);
                                break;
                }
        }
@@ -1237,12 +1263,16 @@
        space = inode_get_bytes(inode);
        /* Build the transfer_from list and check the limits */
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
-               /* The second test can fail when quotaoff is in progress... */
-               if (transfer_to[cnt] == NODQUOT || !dqh_has_quota_enabled(dqh, cnt))
-                       continue;
+               if (!dqh_has_quota_enabled(dqh, cnt))
+                       /* transfer from noquota domain ... */
+                       continue;
                transfer_from[cnt] = dqduplicate(inode->i_dquot[cnt]);
-               if (transfer_from[cnt] == NODQUOT)      /* Can happen on quotafiles 
(quota isn't initialized on them)... */
+               if (transfer_from[cnt] == NODQUOT)      
+                       /* Can happen on quotafiles (quota isn't initialized on 
them)... */
                        continue;
+               if (transfer_to[cnt] == NODQUOT || !dqh_has_quota_enabled(hash, cnt))
+                       /* transfer to noquota domain ... */
+                       continue;
                if (check_idq(transfer_to[cnt], 1, warntype+cnt) == NO_QUOTA ||
                    check_bdq(transfer_to[cnt], space, 0, warntype+cnt) == NO_QUOTA)
                        goto warn_put_all;
@@ -1252,29 +1282,34 @@
         * Finally perform the needed transfer from transfer_from to transfer_to
         */
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
-               /*
-                * Skip changes for same uid or gid or for non-existing quota-type.
-                */
-               if (transfer_from[cnt] == NODQUOT || transfer_to[cnt] == NODQUOT)
+               if (transfer_from[cnt] == NODQUOT && transfer_to[cnt] == NODQUOT)
+                       /* nothing to transfer ... */
                        continue;
 
-               dquot_decr_inodes(transfer_from[cnt], 1);
-               dquot_decr_space(transfer_from[cnt], space);
+               if (transfer_from[cnt] != NODQUOT) {
+                       dquot_decr_inodes(transfer_from[cnt], 1);
+                       dquot_decr_space(transfer_from[cnt], space);
+               }
 
-               dquot_incr_inodes(transfer_to[cnt], 1);
-               dquot_incr_space(transfer_to[cnt], space);
+               if (transfer_to[cnt] != NODQUOT) {
+                       dquot_incr_inodes(transfer_to[cnt], 1);
+                       dquot_incr_space(transfer_to[cnt], space);
+               }
 
-               if (inode->i_dquot[cnt] == NODQUOT)
-                       BUG();
                inode->i_dquot[cnt] = transfer_to[cnt];
-               /*
-                * We've got to release transfer_from[] twice - once for 
dquot_transfer() and
-                * once for inode. We don't want to release transfer_to[] as it's now 
placed in inode
-                */
-               transfer_to[cnt] = transfer_from[cnt];
        }
        /* NOBLOCK END. From now on we can block as we wish */
-       ret = QUOTA_OK;
+
+       for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+               /* First we must put duplicate - otherwise we might deadlock */
+               if (transfer_from[cnt] != NODQUOT) {
+                       dqputduplicate(transfer_from[cnt]);
+                       /* release for inode */
+                       dqput(transfer_from[cnt]);
+               }
+       }
+       return QUOTA_OK;
+
 warn_put_all:
        flush_warnings(transfer_to, warntype);
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
@@ -1284,6 +1319,25 @@
                if (transfer_to[cnt] != NODQUOT)
                        dqput(transfer_to[cnt]);
        }
+       return NO_QUOTA;
+}
+
+int dquot_transfer(struct inode *inode, struct iattr *iattr)
+{
+       return __dquot_transfer(inode, iattr, inode->i_dqh);
+}
+
+int dqhash_transfer(struct inode *inode, struct dqhash *hash)
+{
+       struct iattr iattr;
+       int ret = 0;
+       
+       if (hash == inode->i_dqh)
+               return ret;
+       dprintk("��� dqhash_transfer: %p [#0x%lx], hash: %p->%p\n",
+               inode, inode->i_dqdom, inode->i_dqh, hash);
+       iattr.ia_valid = 0;
+       ret = __dquot_transfer(inode, &iattr, hash);
        return ret;
 }
 
diff -NurP --minimal linux-2.4.22-pre7-mq0.06/fs/ext2/ialloc.c 
linux-2.4.22-pre7-mq0.06-bq0.02/fs/ext2/ialloc.c
--- linux-2.4.22-pre7-mq0.06/fs/ext2/ialloc.c   2003-06-13 16:51:37.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06-bq0.02/fs/ext2/ialloc.c    2003-07-22 00:56:05.000000000 
+0200
@@ -392,6 +392,8 @@
        inode->u.ext2_i.i_block_group = group;
        ext2_set_inode_flags(inode);
        insert_inode_hash(inode);
+       
+       dqdom_cond_modify(inode, dir->i_dqdom); /* is this wise? */
        inode->i_generation = event++;
        mark_inode_dirty(inode);
 
diff -NurP --minimal linux-2.4.22-pre7-mq0.06/fs/ext2/inode.c 
linux-2.4.22-pre7-mq0.06-bq0.02/fs/ext2/inode.c
--- linux-2.4.22-pre7-mq0.06/fs/ext2/inode.c    2003-06-13 16:51:37.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06-bq0.02/fs/ext2/inode.c     2003-07-22 00:56:05.000000000 
+0200
@@ -969,6 +969,7 @@
        inode->i_blksize = PAGE_SIZE;   /* This is the optimal IO size (for stat), not 
the fs block size */
        inode->i_blocks = le32_to_cpu(raw_inode->i_blocks);
        inode->i_version = ++event;
+       inode->i_dqdom = le32_to_cpu(raw_inode->i_raw_dqdom);
        inode->u.ext2_i.i_flags = le32_to_cpu(raw_inode->i_flags);
        inode->u.ext2_i.i_faddr = le32_to_cpu(raw_inode->i_faddr);
        inode->u.ext2_i.i_frag_no = raw_inode->i_frag;
@@ -1091,6 +1092,7 @@
                raw_inode->i_uid_high = 0;
                raw_inode->i_gid_high = 0;
        }
+       raw_inode->i_raw_dqdom = cpu_to_le32(inode->i_dqdom);
        raw_inode->i_links_count = cpu_to_le16(inode->i_nlink);
        raw_inode->i_size = cpu_to_le32(inode->i_size);
        raw_inode->i_atime = cpu_to_le32(inode->i_atime);
diff -NurP --minimal linux-2.4.22-pre7-mq0.06/fs/ext2/ioctl.c 
linux-2.4.22-pre7-mq0.06-bq0.02/fs/ext2/ioctl.c
--- linux-2.4.22-pre7-mq0.06/fs/ext2/ioctl.c    2003-06-13 16:51:37.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06-bq0.02/fs/ext2/ioctl.c     2003-07-22 00:56:05.000000000 
+0200
@@ -70,6 +70,9 @@
                inode->i_ctime = CURRENT_TIME;
                mark_inode_dirty(inode);
                return 0;
+       case EXT2_IOC_GETDQDOM:
+               /* fixme: if stealth, return -ENOTTY */
+               return put_user(inode->i_dqdom, (int *) arg);
        default:
                return -ENOTTY;
        }
diff -NurP --minimal linux-2.4.22-pre7-mq0.06/fs/inode.c 
linux-2.4.22-pre7-mq0.06-bq0.02/fs/inode.c
--- linux-2.4.22-pre7-mq0.06/fs/inode.c 2003-07-22 02:51:57.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06-bq0.02/fs/inode.c  2003-07-22 00:56:05.000000000 +0200
@@ -95,7 +95,6 @@
                struct address_space * const mapping = &inode->i_data;
 
                inode->i_sb = sb;
-               inode->i_dqh = sb->s_dqh;       /* this is the default */
                inode->i_dev = sb->s_dev;
                inode->i_blkbits = sb->s_blocksize_bits;
                inode->i_flags = 0;
@@ -113,6 +112,8 @@
                inode->i_pipe = NULL;
                inode->i_bdev = NULL;
                inode->i_cdev = NULL;
+               inode->i_dqh = dqhget(sb->s_dqh);   /* this is the default */
+               inode->i_dqdom = 0;
 
                mapping->a_ops = &empty_aops;
                mapping->host = inode;
@@ -124,6 +125,8 @@
 
 static void destroy_inode(struct inode *inode) 
 {
+       if (inode->i_dqh)
+               dqhput(inode->i_dqh);
        if (inode_has_buffers(inode))
                BUG();
        if (inode->i_sb->s_op->destroy_inode)
diff -NurP --minimal linux-2.4.22-pre7-mq0.06/fs/namei.c 
linux-2.4.22-pre7-mq0.06-bq0.02/fs/namei.c
--- linux-2.4.22-pre7-mq0.06/fs/namei.c 2003-06-13 16:51:37.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06-bq0.02/fs/namei.c  2003-07-22 00:56:05.000000000 +0200
@@ -306,6 +306,7 @@
                                dput(dentry);
                        else
                                result = dentry;
+                       dqdom_lookup_update(dir, dentry);
                }
                up(&dir->i_sem);
                return result;
@@ -974,6 +975,8 @@
        DQUOT_INIT(dir);
        lock_kernel();
        error = dir->i_op->create(dir, dentry, mode);
+       if (!error)
+               dqdom_cond_update(dir, dentry);
        unlock_kernel();
 exit_lock:
        up(&dir->i_zombie);
@@ -1481,6 +1484,8 @@
                        else {
                                lock_kernel();
                                error = dir->i_op->unlink(dir, dentry);
+                               if (!error)
+                                       dqdom_unlink_update(dir, dentry); /* special? 
*/
                                unlock_kernel();
                                if (!error)
                                        d_delete(dentry);
@@ -1551,6 +1556,8 @@
        DQUOT_INIT(dir);
        lock_kernel();
        error = dir->i_op->symlink(dir, dentry, oldname);
+       if (!error)
+               dqdom_cond_update(dir, dentry);  /* not portable? */
        unlock_kernel();
 
 exit_lock:
@@ -1624,6 +1631,8 @@
        DQUOT_INIT(dir);
        lock_kernel();
        error = dir->i_op->link(old_dentry, dir, new_dentry);
+       if (!error)
+               dqdom_cond_update(dir, new_dentry);
        unlock_kernel();
 
 exit_lock:
@@ -1899,6 +1908,8 @@
        lock_kernel();
        error = vfs_rename(old_dir->d_inode, old_dentry,
                                   new_dir->d_inode, new_dentry);
+       if (!error)
+               dqdom_cond_update(new_dir->d_inode, old_dentry);
        unlock_kernel();
 
        dput(new_dentry);
diff -NurP --minimal linux-2.4.22-pre7-mq0.06/fs/namespace.c 
linux-2.4.22-pre7-mq0.06-bq0.02/fs/namespace.c
--- linux-2.4.22-pre7-mq0.06/fs/namespace.c     2003-07-21 04:58:04.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06-bq0.02/fs/namespace.c      2003-07-22 00:56:05.000000000 
+0200
@@ -140,6 +140,7 @@
                mnt->mnt_flags = old->mnt_flags;
                atomic_inc(&sb->s_active);
                mnt->mnt_sb = sb;
+               mnt->mnt_dqh = dqhget(sb->s_dqh);
                mnt->mnt_root = dget(root);
                mnt->mnt_mountpoint = mnt->mnt_root;
                mnt->mnt_parent = mnt;
@@ -150,6 +151,7 @@
 void __mntput(struct vfsmount *mnt)
 {
        struct super_block *sb = mnt->mnt_sb;
+       dqhput(mnt->mnt_dqh);
        dput(mnt->mnt_root);
        free_vfsmnt(mnt);
        kill_super(sb);
@@ -270,6 +272,7 @@
                list_add(&p->mnt_list, &kill);
        }
 
+       DQUOT_OFF(mnt->mnt_dqh);
        while (!list_empty(&kill)) {
                mnt = list_entry(kill.next, struct vfsmount, mnt_list);
                list_del_init(&mnt->mnt_list);
@@ -337,7 +340,7 @@
                /* last instance - try to be smart */
                spin_unlock(&dcache_lock);
                lock_kernel();
-               DQUOT_OFF(sb->s_dqh);
+               DQUOT_OFF(mnt->mnt_dqh);
                acct_auto_close(sb->s_dev);
                unlock_kernel();
                spin_lock(&dcache_lock);
@@ -476,6 +479,7 @@
                err = 0;
        }
        spin_unlock(&dcache_lock);
+       /* do we need something here? */
 out_unlock:
        up(&nd->dentry->d_inode->i_zombie);
        return err;
@@ -484,7 +488,7 @@
 /*
  * do loopback mount.
  */
-static int do_loopback(struct nameidata *nd, char *old_name, int recurse)
+static int do_loopback(struct nameidata *nd, char *old_name, char *type, int recurse)
 {
        struct nameidata old_nd;
        struct vfsmount *mnt = NULL;
@@ -508,6 +512,16 @@
        }
 
        if (mnt) {
+               char *dom_info = strstr(type, "quota");
+               unsigned long dqdom = 0;
+               
+               /* disable if not capable? */
+               if (dom_info) { /* start new quota domain */
+                       dqdom = mnt->mnt_mountpoint->d_inode->i_ino;
+                       dqhput(mnt->mnt_dqh);
+                       mnt->mnt_dqh = get_dqhash(mnt->mnt_sb, dqdom);
+                       dqdom_modify(mnt->mnt_root->d_inode, dqdom);
+               }
                err = graft_tree(mnt, nd);
                if (err) {
                        spin_lock(&dcache_lock);
@@ -733,7 +747,7 @@
                retval = do_remount(&nd, flags & ~MS_REMOUNT, mnt_flags,
                                    data_page);
        else if (flags & MS_BIND)
-               retval = do_loopback(&nd, dev_name, flags & MS_REC);
+               retval = do_loopback(&nd, dev_name, type_page, flags & MS_REC);
        else if (flags & MS_MOVE)
                retval = do_move_mount(&nd, dev_name);
        else
diff -NurP --minimal linux-2.4.22-pre7-mq0.06/fs/quota.c 
linux-2.4.22-pre7-mq0.06-bq0.02/fs/quota.c
--- linux-2.4.22-pre7-mq0.06/fs/quota.c 2003-07-22 02:51:57.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06-bq0.02/fs/quota.c  2003-07-22 02:20:07.000000000 +0200
@@ -133,6 +133,35 @@
        return ERR_PTR(ret);
 }
 
+/* Resolve mount pathname to dqhash */
+static struct dqhash *resolve_mnt(const char *path)
+{
+       int ret;
+       mode_t mode;
+       struct nameidata nd;
+       kdev_t dev;
+       struct dqhash *dqh;
+
+       ret = user_path_walk(path, &nd);
+       if (ret)
+               goto out;
+
+       dev = nd.dentry->d_inode->i_rdev;
+       mode = nd.dentry->d_inode->i_mode;
+       path_release(&nd);
+
+       ret = -ENOTBLK;
+       if (!S_ISDIR(mode))
+               goto out;
+       ret = -ENODEV;  /* compatible, but correct? */
+       dqh = nd.dentry->d_inode->i_dqh;
+       if (!dqh)
+               goto out;
+       return dqh;
+out:
+       return ERR_PTR(ret);
+}
+
 /* Copy parameters and call proper function */
 static int do_quotactl(struct dqhash *hash, int type, int cmd, qid_t id, caddr_t addr)
 {
@@ -472,7 +501,11 @@
        if (cmds != Q_V1_GETSTATS && cmds != Q_V2_GETSTATS && IS_ERR(sb = 
resolve_dev(special))) {
                ret = PTR_ERR(sb);
                sb = NULL;
-               goto out;
+               /* maybe only a mountpoint was specified? */
+               if (IS_ERR(dqh = resolve_mnt(special))) {
+                       ret = PTR_ERR(dqh);
+                       goto out;
+               }
        }
        else if (sb)
                dqh = sb->s_dqh;
diff -NurP --minimal linux-2.4.22-pre7-mq0.06/fs/super.c 
linux-2.4.22-pre7-mq0.06-bq0.02/fs/super.c
--- linux-2.4.22-pre7-mq0.06/fs/super.c 2003-07-22 03:09:17.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06-bq0.02/fs/super.c  2003-07-22 00:56:05.000000000 +0200
@@ -271,6 +271,7 @@
                INIT_LIST_HEAD(&s->s_locked_inodes);
                INIT_LIST_HEAD(&s->s_files);
                INIT_LIST_HEAD(&s->s_instances);
+               INIT_LIST_HEAD(&s->s_dqhashes);
                init_rwsem(&s->s_umount);
                sema_init(&s->s_lock, 1);
                down_write(&s->s_umount);
@@ -280,8 +281,7 @@
                sema_init(&s->s_nfsd_free_path_sem,1);
                s->s_maxbytes = MAX_NON_LFS;
                s->s_op = &empty_sops;
-               /* quick hack to make dqhash id unique, sufficient for now */
-               s->s_dqh = new_dqhash(s, (unsigned int)s);
+               s->s_dqh = new_dqhash(s, 0);
        }
        return s;
 }
@@ -294,7 +294,10 @@
  */
 static inline void destroy_super(struct super_block *s)
 {
-       destroy_dqhash(s->s_dqh);
+       dqhput(s->s_dqh);
+       
+       if (!list_empty(&s->s_dqhashes))
+               BUG();
        kfree(s);
 }
 
@@ -810,6 +813,7 @@
        if (type->fs_flags & FS_NOMOUNT)
                sb->s_flags |= MS_NOUSER;
        mnt->mnt_sb = sb;
+       mnt->mnt_dqh = dqhget(sb->s_dqh);
        mnt->mnt_root = dget(sb->s_root);
        mnt->mnt_mountpoint = sb->s_root;
        mnt->mnt_parent = mnt;
diff -NurP --minimal linux-2.4.22-pre7-mq0.06/include/linux/ext2_fs.h 
linux-2.4.22-pre7-mq0.06-bq0.02/include/linux/ext2_fs.h
--- linux-2.4.22-pre7-mq0.06/include/linux/ext2_fs.h    2003-07-22 02:56:01.000000000 
+0200
+++ linux-2.4.22-pre7-mq0.06-bq0.02/include/linux/ext2_fs.h     2003-07-22 
01:05:03.000000000 +0200
@@ -210,6 +210,7 @@
 #define        EXT2_IOC_SETFLAGS               _IOW('f', 2, long)
 #define        EXT2_IOC_GETVERSION             _IOR('v', 1, long)
 #define        EXT2_IOC_SETVERSION             _IOW('v', 2, long)
+#define        EXT2_IOC_GETDQDOM               _IOR('d', 1, long)
 
 /*
  * Structure of an inode on the disk
@@ -249,7 +250,7 @@
                        __u16   i_pad1;
                        __u16   l_i_uid_high;   /* these 2 fields    */
                        __u16   l_i_gid_high;   /* were reserved2[0] */
-                       __u32   l_i_reserved2;
+                       __u32   l_i_dqdom;
                } linux2;
                struct {
                        __u8    h_i_frag;       /* Fragment number */
@@ -278,7 +279,7 @@
 #define i_gid_low      i_gid
 #define i_uid_high     osd2.linux2.l_i_uid_high
 #define i_gid_high     osd2.linux2.l_i_gid_high
-#define i_reserved2    osd2.linux2.l_i_reserved2
+#define i_raw_dqdom    osd2.linux2.l_i_dqdom
 #endif
 
 #ifdef __hurd__
diff -NurP --minimal linux-2.4.22-pre7-mq0.06/include/linux/fs.h 
linux-2.4.22-pre7-mq0.06-bq0.02/include/linux/fs.h
--- linux-2.4.22-pre7-mq0.06/include/linux/fs.h 2003-07-22 03:12:14.000000000 +0200
+++ linux-2.4.22-pre7-mq0.06-bq0.02/include/linux/fs.h  2003-07-22 02:04:23.000000000 
+0200
@@ -477,6 +477,7 @@
        struct block_device     *i_bdev;
        struct char_device      *i_cdev;
 
+       unsigned long           i_dqdom;          /* last diskquota domain */
        unsigned long           i_dnotify_mask; /* Directory notify events */
        struct dnotify_struct   *i_dnotify; /* for directory notifications */
 
@@ -759,7 +760,8 @@
 
        struct block_device     *s_bdev;
        struct list_head        s_instances;
-       struct dqhash           *s_dqh;         /* Diskquota hash */
+       struct dqhash           *s_dqh;
+       struct list_head        s_dqhashes;     /* Diskquota hashes */
 
        union {
                struct minix_sb_info    minix_sb;
diff -NurP --minimal linux-2.4.22-pre7-mq0.06/include/linux/quota.h 
linux-2.4.22-pre7-mq0.06-bq0.02/include/linux/quota.h
--- linux-2.4.22-pre7-mq0.06/include/linux/quota.h      2003-07-22 03:11:31.000000000 
+0200
+++ linux-2.4.22-pre7-mq0.06-bq0.02/include/linux/quota.h       2003-07-22 
02:04:11.000000000 +0200
@@ -314,19 +314,35 @@
 struct dqhash {
        struct list_head dqh_list;      /* List of all quota hashes */
        unsigned int dqh_id;            /* ID for hash */
+       int dqh_count;                  /* Use count */
        struct quota_info dqh_dqopt;    /* Diskquota specific options */
        struct dquot_operations *dqh_qop;
        struct quotactl_ops *dqh_qcop;
        struct super_block *dqh_sb;     /* super block */
        struct list_head dqh_hash[NR_DQHASH];
+       unsigned long dqh_dqdom;        /* Diskquota domain */
 };
 
 #if defined(CONFIG_QUOTA)
 
-
-struct dqhash *new_dqhash(struct super_block *, unsigned int);
+struct dqhash *new_dqhash(struct super_block *, unsigned long);
+struct dqhash *get_dqhash(struct super_block *, unsigned long);
+struct dqhash *get_existing_dqhash(struct super_block *, unsigned long);
 void destroy_dqhash(struct dqhash *);
-struct dqhash *find_dqhash(unsigned int);
+
+static inline void dqhput(struct dqhash *hash)
+{
+       if (--hash->dqh_count > 0)
+               return;
+       destroy_dqhash(hash);
+}
+
+static inline struct dqhash *dqhget(struct dqhash *hash)
+{
+       hash->dqh_count++;
+       return hash; 
+}
+
 
 static inline void mark_dquot_dirty(struct dquot *dq)
 {
@@ -337,9 +353,12 @@
 #else /* CONFIG_QUOTA */
 
 #define new_dqhash(sb, dqdom)          (0)
-#define find_dqhash(dqdom)             (0)
+#define get_dqhash(sb, dqdom)          (0)
 #define destroy_dqhash(hash)           do { } while(0)
 
+#define dqhput(hash)                   do { } while(0)
+#define dqhget(hash)                   (hash)
+
 #endif /* CONFIG_QUOTA */
 
 #else
diff -NurP --minimal linux-2.4.22-pre7-mq0.06/include/linux/quotaops.h 
linux-2.4.22-pre7-mq0.06-bq0.02/include/linux/quotaops.h
--- linux-2.4.22-pre7-mq0.06/include/linux/quotaops.h   2003-07-22 03:23:13.000000000 
+0200
+++ linux-2.4.22-pre7-mq0.06-bq0.02/include/linux/quotaops.h    2003-07-22 
02:21:13.000000000 +0200
@@ -35,6 +35,7 @@
 extern void dquot_free_inode(const struct inode *inode, unsigned long number);
 
 extern int  dquot_transfer(struct inode *inode, struct iattr *iattr);
+extern int  dqhash_transfer(struct inode *inode, struct dqhash *hash);
 
 /*
  * Operations supported for diskquotas.
@@ -177,6 +178,74 @@
        return ret;
 }
 
+static __inline__ void dqdom_modify(struct inode *inode, unsigned long dqdom)
+{
+       struct dqhash *new_dqh;
+
+       new_dqh = get_existing_dqhash(inode->i_sb, dqdom);
+       if (!new_dqh)
+               /* fallback to superblock hash */
+               new_dqh = dqhget(inode->i_sb->s_dqh);
+               
+       dqhash_transfer(inode, new_dqh);
+       dqhput(inode->i_dqh);
+       inode->i_dqh = new_dqh;
+       inode->i_dqdom = new_dqh->dqh_dqdom;
+       mark_inode_dirty(inode);
+}
+
+static __inline__ void dqdom_cond_modify(struct inode *inode, unsigned long dqdom)
+{
+       if (inode->i_dqdom != dqdom)
+               dqdom_modify(inode, dqdom);
+}
+
+static __inline__ void dqdom_unlink(struct inode *dir, struct inode *inode)
+{
+       if (inode->i_nlink == 0)
+               return;
+       /* inode from other dir? */
+       if (inode->i_dqdom != dir->i_dqdom)
+               return;
+       /* inode still referenced ... */
+       if (!inode->i_dqdom)
+               return;
+       /* transfer back to superblock hash */
+       dqdom_modify(inode, 0);
+}
+
+
+static __inline__ void dqdom_update(struct inode *dir, struct dentry *dentry)
+{
+       if (!dentry->d_inode)
+               BUG();
+       if (dentry->d_inode->i_dqdom == dir->i_dqdom)
+               BUG();
+       dqdom_modify(dentry->d_inode, dir->i_dqdom);
+}
+
+static __inline__ void dqdom_cond_update(struct inode *dir, struct dentry *dentry)
+{
+       if (!dentry->d_inode)
+               BUG();
+       if (dentry->d_inode->i_dqdom != dir->i_dqdom)
+               dqdom_modify(dentry->d_inode, dir->i_dqdom);
+}
+
+static __inline__ void dqdom_lookup_update(struct inode *dir, struct dentry *dentry)
+{
+       if (!dentry->d_inode)
+               return; /* negative pathwalk */
+       dqdom_cond_update(dir, dentry);
+}
+
+static __inline__ void dqdom_unlink_update(struct inode *dir, struct dentry *dentry)
+{
+       if (!dentry->d_inode)
+               BUG();
+       dqdom_unlink(dir, dentry->d_inode);
+}
+
 #else
 
 /*
@@ -192,6 +263,17 @@
 #define DQUOT_OFF(sb)                          do { } while(0)
 #define DQUOT_TRANSFER(inode, iattr)           (0)
 
+#define dqdom_lookup_update(inode, dentry)     do { } while(0)
+#define dqdom_unlink_update(inode, dentry)     do { } while(0)
+#define dqdom_cond_update(inode, dentry)       do { } while(0)
+
+#define dqdom_modify(inode, dqdom)             do { } while(0)
+#define dqdom_cond_modify(inode, dqdom)                do { } while(0)
+
+#define new_dqhash(sb, dqdom)                  (0)
+#define get_dqhash(sb, dqdom)                  (0)
+#define destroy_dqhash(hash)                   do { } while(0)
+
 extern __inline__ int DQUOT_PREALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr)
 {
        lock_kernel();

Reply via email to