linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Quota Hash Abstraction 2.4.22-pre6
@ 2003-07-15 16:40 Herbert Pötzl
  2003-07-15 16:55 ` Herbert Pötzl
       [not found] ` <20030715234507.GA25420@atrey.karlin.mff.cuni.cz>
  0 siblings, 2 replies; 3+ messages in thread
From: Herbert Pötzl @ 2003-07-15 16:40 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: Jan Kara, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 836 bytes --]


Hi All!

I did some quota hash abstraction, which basically
replaces the one-and-only quota hash, referenced by
each and every super block (where quota enabled), 
with a dynamic, per super_block quota hash ...

this is the first step, and it is neither tuned for
performance nor the final implementation, it's just 
a in-between step to some future quota enhancements 
like the folowing ...

 - faster and smaller quota hashes
 - not only per super_block hashes, maybe per vfsmount?
 - not hardcoded user/group quota ...
 - better interface for quota extensions

please, if somebody has any quota tests, which he/she
is willing to do on this code, or just want to do some 
testing with this code, do it and send me the results ...

I'm not interested in getting this into 2.4 without
good reasons and extensive testing.

TIA,
Herbert


[-- Attachment #2: patch-2.4.22-pre6-mq0.04.diff --]
[-- Type: text/plain, Size: 40188 bytes --]

;
; Quota Hash Abstraction 
;
; separate quota hashes per superblock to speed up
; hash lookups and generalize the quota hash interface.
;
; (C) 2003 Herbert Pötzl <herbert@13thfloor.at>
;
; Changelog:  
;
;   0.04  - 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 -NurbP --minimal linux-2.4.22-pre6/fs/dquot.c linux-2.4.22-pre6-mq0.04/fs/dquot.c
--- linux-2.4.22-pre6/fs/dquot.c	2003-07-15 18:08:31.000000000 +0200
+++ linux-2.4.22-pre6-mq0.04/fs/dquot.c	2003-07-15 18:14:38.000000000 +0200
@@ -150,7 +152,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 +176,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 +320,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 +333,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,15 +344,16 @@
 /* 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)
 {
 	struct dquot *dquot;
 	struct list_head *head;
 
 restart:
+    	/* this could be optimized! (easily?) */
 	list_for_each(head, &inuse_list) {
 		dquot = list_entry(head, struct dquot, dq_inuse);
-		if (dquot->dq_sb != sb)
+		if (dquot->dq_dqh != hash)
 			continue;
 		if (dquot->dq_type != type)
 			continue;
@@ -366,25 +370,82 @@
 		remove_free_dquot(dquot);
 		remove_inuse(dquot);
 		kmem_cache_free(dquot_cachep, dquot);
+		/* hmm, somebody changed his mind? */
 		goto restart;
 	}
 }
 
-static int vfs_quota_sync(struct super_block *sb, int type)
+/* Dquota Hash Management Functions */
+
+static LIST_HEAD(dqhash_list);
+
+
+struct dqhash *new_dqhash(unsigned int id)
+{
+        struct dqhash *hash;
+	int i;
+    	
+	hash = kmalloc(sizeof(struct dqhash),  GFP_USER);
+	if (hash) {
+		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_dquot.dqio_sem, 1);
+		sema_init(&hash->dqh_dquot.dqoff_sem, 1);
+	}
+	list_add(&hash->dqh_list, &dqhash_list);
+	// printk ("new dqhash %p\n", hash);
+	return hash;
+}
+
+void destroy_dqhash(struct dqhash *hash)
+{
+	int cnt;
+
+	// printk ("destroy dqhash %p\n", hash);
+	list_del(&hash->dqh_list);
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+		invalidate_dquots(hash, cnt);
+	kfree(hash);
+}
+
+
+struct dqhash *find_dqhash(unsigned int id)
+{
+	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)
+			goto dqh_found;
+	}
+	hash = NULL;
+	
+dqh_found:
+	unlock_kernel();
+	return hash;
+}
+
+
+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;
@@ -400,11 +461,11 @@
 		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;
@@ -447,7 +508,7 @@
 		if ((sb = get_super(dev))) {
 			lock_kernel();
 			if (sb->s_qcop->quota_sync)
-				sb->s_qcop->quota_sync(sb, type);
+				sb->s_qcop->quota_sync(sb->s_dqh, type);
 			unlock_kernel();
 			drop_super(sb);
 		}
@@ -456,7 +517,7 @@
 		while ((sb = get_super_to_sync(type))) {
 			lock_kernel();
 			if (sb->s_qcop->quota_sync)
-				sb->s_qcop->quota_sync(sb, type);
+				sb->s_qcop->quota_sync(sb->s_dqh, type);
 			unlock_kernel();
 			drop_super(sb);
 		}
@@ -467,7 +528,7 @@
 {
 	lock_kernel();
 	if (sb->s_qcop->quota_sync)
-		sb->s_qcop->quota_sync(sb, type);
+		sb->s_qcop->quota_sync(sb->s_dqh, type);
 	unlock_kernel();
 }
 
@@ -559,7 +620,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 +634,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 +645,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 +658,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 +679,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 +696,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 +738,11 @@
 	return 0;
 }
 
-static void add_dquot_ref(struct super_block *sb, int type)
+/*when, where and why is this called? */
+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();
@@ -821,7 +885,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 +925,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 +956,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 +990,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 +1011,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 +1022,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 +1033,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 +1194,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 +1206,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 +1226,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 +1320,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 +1342,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]);
@@ -1299,11 +1363,11 @@
 	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 +1394,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 +1402,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 +1439,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 +1485,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 +1493,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 +1502,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 +1514,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 +1526,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 +1574,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 -NurbP --minimal linux-2.4.22-pre6/fs/inode.c linux-2.4.22-pre6-mq0.04/fs/inode.c
--- linux-2.4.22-pre6/fs/inode.c	2003-07-15 18:08:31.000000000 +0200
+++ linux-2.4.22-pre6-mq0.04/fs/inode.c	2003-07-15 18:14:38.000000000 +0200
@@ -1205,13 +1205,15 @@
 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)
+    	/* fix this later!!, we need dq_op here? */
+	if (sb && !sb->dq_op)
 		return;	/* nothing to do */
 	/* We have to be protected against other CPUs */
 	lock_kernel();		/* This lock is for quota code */
diff -NurbP --minimal linux-2.4.22-pre6/fs/quota.c linux-2.4.22-pre6-mq0.04/fs/quota.c
--- linux-2.4.22-pre6/fs/quota.c	2003-07-15 18:08:32.000000000 +0200
+++ linux-2.4.22-pre6-mq0.04/fs/quota.c	2003-07-15 18:14:38.000000000 +0200
@@ -136,6 +136,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)
 {
+    	struct dqhash *dqh = sb->s_dqh;
 	int ret;
 
 	switch (cmd) {
@@ -144,17 +145,17 @@
 
 			if (IS_ERR(pathname = getname(addr)))
 				return PTR_ERR(pathname);
-			ret = sb->s_qcop->quota_on(sb, type, id, pathname);
+			ret = sb->s_qcop->quota_on(dqh, type, id, pathname);
 			putname(pathname);
 			return ret;
 		}
 		case Q_QUOTAOFF:
-			return sb->s_qcop->quota_off(sb, type);
+			return sb->s_qcop->quota_off(dqh, type);
 
 		case Q_GETFMT: {
 			__u32 fmt;
 
-			fmt = sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id;
+			fmt = dqh_dqopt(dqh)->info[type].dqi_format->qf_fmt_id;
 			if (copy_to_user(addr, &fmt, sizeof(fmt)))
 				return -EFAULT;
 			return 0;
@@ -162,7 +163,7 @@
 		case Q_GETINFO: {
 			struct if_dqinfo info;
 
-			if ((ret = sb->s_qcop->get_info(sb, type, &info)))
+			if ((ret = sb->s_qcop->get_info(dqh, type, &info)))
 				return ret;
 			if (copy_to_user(addr, &info, sizeof(info)))
 				return -EFAULT;
@@ -173,12 +174,12 @@
 
 			if (copy_from_user(&info, addr, sizeof(info)))
 				return -EFAULT;
-			return sb->s_qcop->set_info(sb, type, &info);
+			return sb->s_qcop->set_info(dqh, type, &info);
 		}
 		case Q_GETQUOTA: {
 			struct if_dqblk idq;
 
-			if ((ret = sb->s_qcop->get_dqblk(sb, type, id, &idq)))
+			if ((ret = sb->s_qcop->get_dqblk(dqh, type, id, &idq)))
 				return ret;
 			if (copy_to_user(addr, &idq, sizeof(idq)))
 				return -EFAULT;
@@ -189,11 +190,11 @@
 
 			if (copy_from_user(&idq, addr, sizeof(idq)))
 				return -EFAULT;
-			return sb->s_qcop->set_dqblk(sb, type, id, &idq);
+			return sb->s_qcop->set_dqblk(dqh, type, id, &idq);
 		}
 		case Q_SYNC:
 			if (sb)
-				return sb->s_qcop->quota_sync(sb, type);
+				return sb->s_qcop->quota_sync(dqh, type);
 			sync_dquots_dev(NODEV, type);
 			return 0;
 		case Q_XQUOTAON:
@@ -320,18 +321,20 @@
 static int v1_set_rsquash(struct super_block *sb, int type, int flag)
 {
 	struct if_dqinfo info;
+	struct dqhash *dqh = sb->s_dqh;
 
 	info.dqi_valid = IIF_FLAGS;
 	info.dqi_flags = flag ? V1_DQF_RSQUASH : 0;
-	return sb->s_qcop->set_info(sb, type, &info);
+	return sb->s_qcop->set_info(dqh, type, &info);
 }
 
 static int v1_get_dqblk(struct super_block *sb, int type, qid_t id, struct v1c_mem_dqblk *mdq)
 {
 	struct if_dqblk idq;
+	struct dqhash *dqh = sb->s_dqh;
 	int ret;
 
-	if ((ret = sb->s_qcop->get_dqblk(sb, type, id, &idq)) < 0)
+	if ((ret = sb->s_qcop->get_dqblk(dqh, type, id, &idq)) < 0)
 		return ret;
 	mdq->dqb_ihardlimit = idq.dqb_ihardlimit;
 	mdq->dqb_isoftlimit = idq.dqb_isoftlimit;
@@ -344,7 +347,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 = sb->s_qcop->get_info(dqh, type, &info)) < 0)
 			return ret;
 		mdq->dqb_btime = info.dqi_bgrace;
 		mdq->dqb_itime = info.dqi_igrace;
@@ -355,6 +358,7 @@
 static int v1_set_dqblk(struct super_block *sb, int type, int cmd, qid_t id, struct v1c_mem_dqblk *mdq)
 {
 	struct if_dqblk idq;
+	struct dqhash *dqh = sb->s_dqh;
 	int ret;
 
 	idq.dqb_valid = 0;
@@ -370,14 +374,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 = sb->s_qcop->set_dqblk(dqh, 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 = sb->s_qcop->set_info(dqh, type, &info);
 	}
 	return ret;
 }
@@ -390,23 +394,31 @@
 /* Handle requests to old interface */
 static int do_compat_quotactl(struct super_block *sb, int type, int cmd, qid_t id, caddr_t addr)
 {
+	struct dqhash *dqh;
 	int ret;
 
+    	if (sb)
+	    	dqh = sb->s_dqh;
+	else {	
+	    	printk("sb == NULL"); /* is this possible? */
+	    	dqh = NULL;
+	}
+		
 	switch (cmd) {
 		case Q_COMP_QUOTAON: {
 			char *pathname;
 
 			if (IS_ERR(pathname = getname(addr)))
 				return PTR_ERR(pathname);
-			ret = sb->s_qcop->quota_on(sb, type, QFMT_VFS_OLD, pathname);
+			ret = sb->s_qcop->quota_on(dqh, type, QFMT_VFS_OLD, pathname);
 			putname(pathname);
 			return ret;
 		}
 		case Q_COMP_QUOTAOFF:
-			return sb->s_qcop->quota_off(sb, type);
+			return sb->s_qcop->quota_off(dqh, type);
 		case Q_COMP_SYNC:
 			if (sb)
-				return sb->s_qcop->quota_sync(sb, type);
+				return sb->s_qcop->quota_sync(dqh, type);
 			sync_dquots_dev(NODEV, type);
 			return 0;
 		case Q_V1_RSQUASH: {
diff -NurbP --minimal linux-2.4.22-pre6/fs/quota_v1.c linux-2.4.22-pre6-mq0.04/fs/quota_v1.c
--- linux-2.4.22-pre6/fs/quota_v1.c	2003-07-15 18:08:32.000000000 +0200
+++ linux-2.4.22-pre6-mq0.04/fs/quota_v1.c	2003-07-15 18:14:38.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 -NurbP --minimal linux-2.4.22-pre6/fs/quota_v2.c linux-2.4.22-pre6-mq0.04/fs/quota_v2.c
--- linux-2.4.22-pre6/fs/quota_v2.c	2003-07-15 18:08:32.000000000 +0200
+++ linux-2.4.22-pre6-mq0.04/fs/quota_v2.c	2003-07-15 18:14:38.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 -NurbP --minimal linux-2.4.22-pre6/fs/super.c linux-2.4.22-pre6-mq0.04/fs/super.c
--- linux-2.4.22-pre6/fs/super.c	2003-07-15 18:08:32.000000000 +0200
+++ linux-2.4.22-pre6-mq0.04/fs/super.c	2003-07-15 18:14:38.000000000 +0200
@@ -278,12 +278,13 @@
 		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((unsigned int)s);
+		s->s_dqh->dqh_sb = s;
 	}
 	return s;
 }
@@ -296,6 +297,7 @@
  */
 static inline void destroy_super(struct super_block *s)
 {
+    	destroy_dqhash(s->s_dqh);
 	kfree(s);
 }
 
diff -NurbP --minimal linux-2.4.22-pre6/include/linux/fs.h linux-2.4.22-pre6-mq0.04/include/linux/fs.h
--- linux-2.4.22-pre6/include/linux/fs.h	2003-07-15 18:08:40.000000000 +0200
+++ linux-2.4.22-pre6-mq0.04/include/linux/fs.h	2003-07-15 18:14:38.000000000 +0200
@@ -759,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 -NurbP --minimal linux-2.4.22-pre6/include/linux/mount.h linux-2.4.22-pre6-mq0.04/include/linux/mount.h
--- linux-2.4.22-pre6/include/linux/mount.h	2001-10-05 22:05:55.000000000 +0200
+++ linux-2.4.22-pre6-mq0.04/include/linux/mount.h	2003-07-15 18:14:38.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 -NurbP --minimal linux-2.4.22-pre6/include/linux/quota.h linux-2.4.22-pre6-mq0.04/include/linux/quota.h
--- linux-2.4.22-pre6/include/linux/quota.h	2003-07-15 18:08:41.000000000 +0200
+++ linux-2.4.22-pre6-mq0.04/include/linux/quota.h	2003-07-15 18:14:38.000000000 +0200
@@ -182,7 +182,9 @@
 #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 sb_dqopt(sb) (dqh_dqopt((sb)->s_dqh))
+
+#define dqh_dqopt(dqh) (&(dqh)->dqh_dquot)
 
 struct dqstats {
 	int lookups;
@@ -216,7 +218,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 +236,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,13 +258,13 @@
 
 /* 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 (*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 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 *);
@@ -288,11 +290,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)
 
@@ -311,10 +308,33 @@
 
 #define sb_has_quota_enabled(sb, type) (is_enabled(sb_dqopt(sb), type))
 
+#define dqh_any_quota_enabled(hash) (is_enabled(dqh_dqopt(hash), USRQUOTA)  \
+    	| is_enabled(dqh_dqopt(hash), GRPQUOTA))
+
+#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 {
+	unsigned int dqh_id;		/* ID for hash */
+	struct quota_info dqh_dquot;	/* Diskquota specific options */
+	struct list_head dqh_list;	/* List of all quota hashes */
+    	struct super_block *dqh_sb; 	/* super block if applicable */
+    	struct list_head dqh_hash[NR_DQHASH];
+};
+
+struct dqhash *new_dqhash(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
 
 # /* nodep */ include <sys/cdefs.h>
diff -NurbP --minimal linux-2.4.22-pre6/include/linux/quotaops.h linux-2.4.22-pre6-mq0.04/include/linux/quotaops.h
--- linux-2.4.22-pre6/include/linux/quotaops.h	2003-07-15 18:08:41.000000000 +0200
+++ linux-2.4.22-pre6-mq0.04/include/linux/quotaops.h	2003-07-15 18:14:38.000000000 +0200
@@ -47,6 +47,8 @@
 {
 	if (!inode->i_sb)
 		out_of_line_bug();
+	if (!inode->i_sb->s_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);
@@ -59,6 +61,8 @@
 	if (IS_QUOTAINIT(inode)) {
 		if (!inode->i_sb)
 			out_of_line_bug();
+		if (!inode->i_sb->s_dqh)
+			out_of_line_bug();
 		inode->i_sb->dq_op->drop(inode);	/* Ops must be set when there's any quota... */
 	}
 	unlock_kernel();
@@ -173,7 +177,7 @@
 
 	lock_kernel();
 	if (sb->s_qcop && sb->s_qcop->quota_off)
-		ret = sb->s_qcop->quota_off(sb, -1);
+		ret = sb->s_qcop->quota_off(sb->s_dqh, -1);
 	unlock_kernel();
 	return ret;
 }

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: Quota Hash Abstraction 2.4.22-pre6
  2003-07-15 16:40 Quota Hash Abstraction 2.4.22-pre6 Herbert Pötzl
@ 2003-07-15 16:55 ` Herbert Pötzl
       [not found] ` <20030715234507.GA25420@atrey.karlin.mff.cuni.cz>
  1 sibling, 0 replies; 3+ messages in thread
From: Herbert Pötzl @ 2003-07-15 16:55 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: Jan Kara, linux-kernel


ooops, as my compiler told me, this trivial patch
is required to compile it with 2.4.22-pre6 8-)

sorry for any inconvenience ...

best,
Herbert


--- linux-2.4.22-pre6-mq0.04/fs/dquot.c 2003-07-15 18:24:14.000000000 +0200
+++ linux-2.4.22-pre6-mq0.04-fix/fs/dquot.c      2003-07-15 18:47:23.000000000 +0200
@@ -434,6 +434,7 @@
        struct list_head *head;
        struct dquot *dquot;
        struct quota_info *dqopt = dqh_dqopt(hash);
+       struct super_block *sb = hash->dqh_sb;
        int cnt;
 
 restart:




^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: Quota Hash Abstraction 2.4.22-pre6
       [not found] ` <20030715234507.GA25420@atrey.karlin.mff.cuni.cz>
@ 2003-07-16  0:19   ` Herbert Pötzl
  0 siblings, 0 replies; 3+ messages in thread
From: Herbert Pötzl @ 2003-07-16  0:19 UTC (permalink / raw)
  To: Jan Kara; +Cc: linux-fsdevel, linux-kernel


Hi Honza!

On Wed, Jul 16, 2003 at 01:45:07AM +0200, Jan Kara wrote:
> > 
> > this is the first step, and it is neither tuned for
   ~~~~~~~~~~~~~~~~~~~~~~~
> > performance nor the final implementation, it's just 
> > a in-between step to some future quota enhancements 
			~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> > like the folowing ...
> > 
> >  - faster and smaller quota hashes
>   Actually I think we don't care too much about performance here - the
> number of dquots is rougly #active_users*#filesystems_with_quota and we
> do lookup only on open()...

hmm, actually we do the lookup in each dqget, which from
dquot_initialize (open), dquot_transfer (chown,chgrp), 
and vfs_get_dqblk/vfs_set_dqblk (quota ctls) ...

but I agree that we do not care too much about performance ...

> >  - not only per super_block hashes, maybe per vfsmount?
>   For current quota you must have per super_block hashes. If you'd like
> to have some "directory based" one the per vfsmount makes sense.

exactly, this is one of the future quota enhancements, 
I have in mind ... not exactly directory based, but more
vfsmount based ... (which includes the superblock case)

> >  - not hardcoded user/group quota ...
>   After a quick look I don't see any changes wrt this. What exactly do
> you mean by this?

as I wrote "first step", "future quota enhancements" ...

this hopefully will be in the next step, or the step after ...

> >  - better interface for quota extensions
> > 
> > please, if somebody has any quota tests, which he/she
> > is willing to do on this code, or just want to do some 
> > testing with this code, do it and send me the results ...
> > 
> > I'm not interested in getting this into 2.4 without
>   Actually I don't think you'll be able to get some change like this to
> 2.4 but 2.6 might be possible.

at the moment, I do not care if this is for 2.4, 2.6 
or 2.8 or for my personal tree ;) I just want to test 
some of my ideas regarding quota improvements/extensions, 
and the current design isn't flexible enough to allow it ... 

>   BTW: don't you need some locking on dqhash list when
> creating/destorying hashes?

I guess this is right, but I assume the kernel lock 
will do nicely (any objections? if not I'll add that)

thanks for looking at it ... ;)

best,
Herbert 


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2003-07-16  0:04 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2003-07-15 16:40 Quota Hash Abstraction 2.4.22-pre6 Herbert Pötzl
2003-07-15 16:55 ` Herbert Pötzl
     [not found] ` <20030715234507.GA25420@atrey.karlin.mff.cuni.cz>
2003-07-16  0:19   ` Herbert Pötzl

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).