All of lore.kernel.org
 help / color / mirror / Atom feed
* [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2)
@ 2008-10-24 22:05 Jan Kara
  2008-10-27  7:22 ` tristan.ye
                   ` (3 more replies)
  0 siblings, 4 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:05 UTC (permalink / raw)
  To: ocfs2-devel

Hello,

the following patch series implements quotas for OCFS2. The patch
series is based on:
git://git.kernel.org/pub/scm/linux/kernel/git/mfasheh/ocfs2.git linux-next

I've adressed Joel's comments, also node recovery is now fully working
and I've fixed a few issues I found during my testing. So I'm currently
not aware of any bugs. Please review, test, comment. Thanks.

								Honza

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

* [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2)
@ 2008-10-24 22:07 Jan Kara
  2008-10-24 22:07 ` [Ocfs2-devel] [PATCH 01/29] quota: Add callbacks for allocating and destroying dquot structures Jan Kara
                   ` (28 more replies)
  0 siblings, 29 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:07 UTC (permalink / raw)
  To: ocfs2-devel

Oops, and now really the patch series... Sorry for the bad post.

							Honza

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

* [Ocfs2-devel] [PATCH 01/29] quota: Add callbacks for allocating and destroying dquot structures
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
@ 2008-10-24 22:07 ` Jan Kara
  2008-10-24 22:07 ` [Ocfs2-devel] [PATCH 02/29] quota: Increase size of variables for limits and inode usage Jan Kara
                   ` (27 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:07 UTC (permalink / raw)
  To: ocfs2-devel

Some filesystems would like to keep private information together with each
dquot. Add callbacks alloc_dquot and destroy_dquot allowing filesystem to
allocate larger dquots from their private slab in a similar fashion we
currently allocate inodes.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/dquot.c            |   19 +++++++++++++++----
 include/linux/quota.h |    2 ++
 2 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/fs/dquot.c b/fs/dquot.c
index ad7e590..c228b31 100644
--- a/fs/dquot.c
+++ b/fs/dquot.c
@@ -417,6 +417,14 @@ out_dqlock:
 	return ret;
 }
 
+static void destroy_dquot(struct dquot *dquot)
+{
+	if (dquot->dq_sb->dq_op->destroy_dquot)
+		dquot->dq_sb->dq_op->destroy_dquot(dquot);
+	else
+		kmem_cache_free(dquot_cachep, dquot);
+}
+
 /* Invalidate all dquots on the list. Note that this function is called after
  * quota is disabled and pointers from inodes removed so there cannot be new
  * quota users. There can still be some users of quotas due to inodes being
@@ -465,7 +473,7 @@ restart:
 		remove_dquot_hash(dquot);
 		remove_free_dquot(dquot);
 		remove_inuse(dquot);
-		kmem_cache_free(dquot_cachep, dquot);
+		destroy_dquot(dquot);
 	}
 	spin_unlock(&dq_list_lock);
 }
@@ -529,7 +537,7 @@ static void prune_dqcache(int count)
 		remove_dquot_hash(dquot);
 		remove_free_dquot(dquot);
 		remove_inuse(dquot);
-		kmem_cache_free(dquot_cachep, dquot);
+		destroy_dquot(dquot);
 		count--;
 		head = free_dquots.prev;
 	}
@@ -631,7 +639,10 @@ static struct dquot *get_empty_dquot(struct super_block *sb, int type)
 {
 	struct dquot *dquot;
 
-	dquot = kmem_cache_zalloc(dquot_cachep, GFP_NOFS);
+	if (sb->dq_op->alloc_dquot)
+		dquot = sb->dq_op->alloc_dquot(sb, type);
+	else
+		dquot = kmem_cache_zalloc(dquot_cachep, GFP_NOFS);
 	if(!dquot)
 		return NODQUOT;
 
@@ -684,7 +695,7 @@ we_slept:
 		dqstats.lookups++;
 		spin_unlock(&dq_list_lock);
 		if (empty)
-			kmem_cache_free(dquot_cachep, empty);
+			destroy_dquot(empty);
 	}
 	/* Wait for dq_lock - after this we know that either dquot_release() is already
 	 * finished or it will be canceled due to dq_count > 1 test */
diff --git a/include/linux/quota.h b/include/linux/quota.h
index 376a050..eeae7a9 100644
--- a/include/linux/quota.h
+++ b/include/linux/quota.h
@@ -294,6 +294,8 @@ struct dquot_operations {
 	int (*free_inode) (const struct inode *, unsigned long);
 	int (*transfer) (struct inode *, struct iattr *);
 	int (*write_dquot) (struct dquot *);		/* Ordinary dquot write */
+	struct dquot *(*alloc_dquot)(struct super_block *, int);	/* Allocate memory for new dquot (can be NULL if no special entries dquot are needed) */
+	void (*destroy_dquot)(struct dquot *);		/* Free memory for dquot (can be NULL if alloc_dquot is NULL) */
 	int (*acquire_dquot) (struct dquot *);		/* Quota is going to be created on disk */
 	int (*release_dquot) (struct dquot *);		/* Quota is going to be deleted from disk */
 	int (*mark_dirty) (struct dquot *);		/* Dquot is marked dirty */
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 02/29] quota: Increase size of variables for limits and inode usage
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
  2008-10-24 22:07 ` [Ocfs2-devel] [PATCH 01/29] quota: Add callbacks for allocating and destroying dquot structures Jan Kara
@ 2008-10-24 22:07 ` Jan Kara
  2008-10-24 22:07 ` [Ocfs2-devel] [PATCH 03/29] quota: Remove bogus 'optimization' in check_idq() and check_bdq() Jan Kara
                   ` (26 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:07 UTC (permalink / raw)
  To: ocfs2-devel

So far quota was fine with quota block limits and inode limits/numbers in
a 32-bit type. Now with rapid increase in storage sizes there are coming
requests to be able to handle quota limits above 4TB / more that 2^32 inodes.
So bump up sizes of types in mem_dqblk structure to 64-bits to be able to
handle this. Also update inode allocation / checking functions to use qsize_t
and make global structure keep quota limits in bytes so that things are
consistent.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/dquot.c               |   50 ++++++++++++++++++++++++++-------------------
 fs/quota_v1.c            |   25 +++++++++++++++++-----
 fs/quota_v2.c            |   21 +++++++++++++++---
 include/linux/quota.h    |   28 +++++++++++--------------
 include/linux/quotaops.h |    4 +-
 5 files changed, 79 insertions(+), 49 deletions(-)

diff --git a/fs/dquot.c b/fs/dquot.c
index c228b31..413e71d 100644
--- a/fs/dquot.c
+++ b/fs/dquot.c
@@ -833,7 +833,7 @@ static void drop_dquot_ref(struct super_block *sb, int type)
 	}
 }
 
-static inline void dquot_incr_inodes(struct dquot *dquot, unsigned long number)
+static inline void dquot_incr_inodes(struct dquot *dquot, qsize_t number)
 {
 	dquot->dq_dqb.dqb_curinodes += number;
 }
@@ -843,7 +843,7 @@ static inline void dquot_incr_space(struct dquot *dquot, qsize_t number)
 	dquot->dq_dqb.dqb_curspace += number;
 }
 
-static inline void dquot_decr_inodes(struct dquot *dquot, unsigned long number)
+static inline void dquot_decr_inodes(struct dquot *dquot, qsize_t number)
 {
 	if (dquot->dq_dqb.dqb_curinodes > number)
 		dquot->dq_dqb.dqb_curinodes -= number;
@@ -860,7 +860,7 @@ static inline void dquot_decr_space(struct dquot *dquot, qsize_t number)
 		dquot->dq_dqb.dqb_curspace -= number;
 	else
 		dquot->dq_dqb.dqb_curspace = 0;
-	if (toqb(dquot->dq_dqb.dqb_curspace) <= dquot->dq_dqb.dqb_bsoftlimit)
+	if (dquot->dq_dqb.dqb_curspace <= dquot->dq_dqb.dqb_bsoftlimit)
 		dquot->dq_dqb.dqb_btime = (time_t) 0;
 	clear_bit(DQ_BLKS_B, &dquot->dq_flags);
 }
@@ -1036,7 +1036,7 @@ static inline char ignore_hardlimit(struct dquot *dquot)
 }
 
 /* needs dq_data_lock */
-static int check_idq(struct dquot *dquot, ulong inodes, char *warntype)
+static int check_idq(struct dquot *dquot, qsize_t inodes, char *warntype)
 {
 	*warntype = QUOTA_NL_NOWARN;
 	if (inodes <= 0 || test_bit(DQ_FAKE_B, &dquot->dq_flags))
@@ -1075,7 +1075,7 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *war
 		return QUOTA_OK;
 
 	if (dquot->dq_dqb.dqb_bhardlimit &&
-	   toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bhardlimit &&
+	    dquot->dq_dqb.dqb_curspace + space > dquot->dq_dqb.dqb_bhardlimit &&
             !ignore_hardlimit(dquot)) {
 		if (!prealloc)
 			*warntype = QUOTA_NL_BHARDWARN;
@@ -1083,7 +1083,7 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *war
 	}
 
 	if (dquot->dq_dqb.dqb_bsoftlimit &&
-	   toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bsoftlimit &&
+	    dquot->dq_dqb.dqb_curspace + space > dquot->dq_dqb.dqb_bsoftlimit &&
 	    dquot->dq_dqb.dqb_btime && get_seconds() >= dquot->dq_dqb.dqb_btime &&
             !ignore_hardlimit(dquot)) {
 		if (!prealloc)
@@ -1092,7 +1092,7 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *war
 	}
 
 	if (dquot->dq_dqb.dqb_bsoftlimit &&
-	   toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bsoftlimit &&
+	    dquot->dq_dqb.dqb_curspace + space > dquot->dq_dqb.dqb_bsoftlimit &&
 	    dquot->dq_dqb.dqb_btime == 0) {
 		if (!prealloc) {
 			*warntype = QUOTA_NL_BSOFTWARN;
@@ -1109,7 +1109,7 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *war
 	return QUOTA_OK;
 }
 
-static int info_idq_free(struct dquot *dquot, ulong inodes)
+static int info_idq_free(struct dquot *dquot, qsize_t inodes)
 {
 	if (test_bit(DQ_FAKE_B, &dquot->dq_flags) ||
 	    dquot->dq_dqb.dqb_curinodes <= dquot->dq_dqb.dqb_isoftlimit)
@@ -1126,15 +1126,13 @@ static int info_idq_free(struct dquot *dquot, ulong inodes)
 static int info_bdq_free(struct dquot *dquot, qsize_t space)
 {
 	if (test_bit(DQ_FAKE_B, &dquot->dq_flags) ||
-	    toqb(dquot->dq_dqb.dqb_curspace) <= dquot->dq_dqb.dqb_bsoftlimit)
+	    dquot->dq_dqb.dqb_curspace <= dquot->dq_dqb.dqb_bsoftlimit)
 		return QUOTA_NL_NOWARN;
 
-	if (toqb(dquot->dq_dqb.dqb_curspace - space) <=
-	    dquot->dq_dqb.dqb_bsoftlimit)
+	if (dquot->dq_dqb.dqb_curspace - space <= dquot->dq_dqb.dqb_bsoftlimit)
 		return QUOTA_NL_BSOFTBELOW;
-	if (toqb(dquot->dq_dqb.dqb_curspace) >= dquot->dq_dqb.dqb_bhardlimit &&
-	    toqb(dquot->dq_dqb.dqb_curspace - space) <
-						dquot->dq_dqb.dqb_bhardlimit)
+	if (dquot->dq_dqb.dqb_curspace >= dquot->dq_dqb.dqb_bhardlimit &&
+	    dquot->dq_dqb.dqb_curspace - space < dquot->dq_dqb.dqb_bhardlimit)
 		return QUOTA_NL_BHARDBELOW;
 	return QUOTA_NL_NOWARN;
 }
@@ -1277,7 +1275,7 @@ warn_put_all:
 /*
  * This operation can block, but only after everything is updated
  */
-int dquot_alloc_inode(const struct inode *inode, unsigned long number)
+int dquot_alloc_inode(const struct inode *inode, qsize_t number)
 {
 	int cnt, ret = NO_QUOTA;
 	char warntype[MAXQUOTAS];
@@ -1362,7 +1360,7 @@ out_sub:
 /*
  * This operation can block, but only after everything is updated
  */
-int dquot_free_inode(const struct inode *inode, unsigned long number)
+int dquot_free_inode(const struct inode *inode, qsize_t number)
 {
 	unsigned int cnt;
 	char warntype[MAXQUOTAS];
@@ -1879,14 +1877,24 @@ int vfs_dq_quota_on_remount(struct super_block *sb)
 	return ret;
 }
 
+static inline qsize_t qbtos(qsize_t blocks)
+{
+	return blocks << QIF_DQBLKSIZE_BITS;
+}
+
+static inline qsize_t stoqb(qsize_t space)
+{
+	return (space + QIF_DQBLKSIZE - 1) >> QIF_DQBLKSIZE_BITS;
+}
+
 /* Generic routine for getting common part of quota structure */
 static void do_get_dqblk(struct dquot *dquot, struct if_dqblk *di)
 {
 	struct mem_dqblk *dm = &dquot->dq_dqb;
 
 	spin_lock(&dq_data_lock);
-	di->dqb_bhardlimit = dm->dqb_bhardlimit;
-	di->dqb_bsoftlimit = dm->dqb_bsoftlimit;
+	di->dqb_bhardlimit = stoqb(dm->dqb_bhardlimit);
+	di->dqb_bsoftlimit = stoqb(dm->dqb_bsoftlimit);
 	di->dqb_curspace = dm->dqb_curspace;
 	di->dqb_ihardlimit = dm->dqb_ihardlimit;
 	di->dqb_isoftlimit = dm->dqb_isoftlimit;
@@ -1933,8 +1941,8 @@ static int do_set_dqblk(struct dquot *dquot, struct if_dqblk *di)
 		check_blim = 1;
 	}
 	if (di->dqb_valid & QIF_BLIMITS) {
-		dm->dqb_bsoftlimit = di->dqb_bsoftlimit;
-		dm->dqb_bhardlimit = di->dqb_bhardlimit;
+		dm->dqb_bsoftlimit = qbtos(di->dqb_bsoftlimit);
+		dm->dqb_bhardlimit = qbtos(di->dqb_bhardlimit);
 		check_blim = 1;
 	}
 	if (di->dqb_valid & QIF_INODES) {
@@ -1952,7 +1960,7 @@ static int do_set_dqblk(struct dquot *dquot, struct if_dqblk *di)
 		dm->dqb_itime = di->dqb_itime;
 
 	if (check_blim) {
-		if (!dm->dqb_bsoftlimit || toqb(dm->dqb_curspace) < dm->dqb_bsoftlimit) {
+		if (!dm->dqb_bsoftlimit || dm->dqb_curspace < dm->dqb_bsoftlimit) {
 			dm->dqb_btime = 0;
 			clear_bit(DQ_BLKS_B, &dquot->dq_flags);
 		}
diff --git a/fs/quota_v1.c b/fs/quota_v1.c
index 5ae15b1..3e078ee 100644
--- a/fs/quota_v1.c
+++ b/fs/quota_v1.c
@@ -14,14 +14,27 @@ MODULE_AUTHOR("Jan Kara");
 MODULE_DESCRIPTION("Old quota format support");
 MODULE_LICENSE("GPL");
 
+#define QUOTABLOCK_BITS 10
+#define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS)
+
+static inline qsize_t v1_stoqb(qsize_t space)
+{
+	return (space + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS;
+}
+
+static inline qsize_t v1_qbtos(qsize_t blocks)
+{
+	return blocks << QUOTABLOCK_BITS;
+}
+
 static void v1_disk2mem_dqblk(struct mem_dqblk *m, struct v1_disk_dqblk *d)
 {
 	m->dqb_ihardlimit = d->dqb_ihardlimit;
 	m->dqb_isoftlimit = d->dqb_isoftlimit;
 	m->dqb_curinodes = d->dqb_curinodes;
-	m->dqb_bhardlimit = d->dqb_bhardlimit;
-	m->dqb_bsoftlimit = d->dqb_bsoftlimit;
-	m->dqb_curspace = ((qsize_t)d->dqb_curblocks) << QUOTABLOCK_BITS;
+	m->dqb_bhardlimit = v1_qbtos(d->dqb_bhardlimit);
+	m->dqb_bsoftlimit = v1_qbtos(d->dqb_bsoftlimit);
+	m->dqb_curspace = v1_qbtos(d->dqb_curblocks);
 	m->dqb_itime = d->dqb_itime;
 	m->dqb_btime = d->dqb_btime;
 }
@@ -31,9 +44,9 @@ static void v1_mem2disk_dqblk(struct v1_disk_dqblk *d, struct mem_dqblk *m)
 	d->dqb_ihardlimit = m->dqb_ihardlimit;
 	d->dqb_isoftlimit = m->dqb_isoftlimit;
 	d->dqb_curinodes = m->dqb_curinodes;
-	d->dqb_bhardlimit = m->dqb_bhardlimit;
-	d->dqb_bsoftlimit = m->dqb_bsoftlimit;
-	d->dqb_curblocks = toqb(m->dqb_curspace);
+	d->dqb_bhardlimit = v1_stoqb(m->dqb_bhardlimit);
+	d->dqb_bsoftlimit = v1_stoqb(m->dqb_bsoftlimit);
+	d->dqb_curblocks = v1_stoqb(m->dqb_curspace);
 	d->dqb_itime = m->dqb_itime;
 	d->dqb_btime = m->dqb_btime;
 }
diff --git a/fs/quota_v2.c b/fs/quota_v2.c
index b53827d..51c4717 100644
--- a/fs/quota_v2.c
+++ b/fs/quota_v2.c
@@ -26,6 +26,19 @@ typedef char *dqbuf_t;
 #define GETIDINDEX(id, depth) (((id) >> ((V2_DQTREEDEPTH-(depth)-1)*8)) & 0xff)
 #define GETENTRIES(buf) ((struct v2_disk_dqblk *)(((char *)buf)+sizeof(struct v2_disk_dqdbheader)))
 
+#define QUOTABLOCK_BITS 10
+#define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS)
+
+static inline qsize_t v2_stoqb(qsize_t space)
+{
+	return (space + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS;
+}
+
+static inline qsize_t v2_qbtos(qsize_t blocks)
+{
+	return blocks << QUOTABLOCK_BITS;
+}
+
 /* Check whether given file is really vfsv0 quotafile */
 static int v2_check_quota_file(struct super_block *sb, int type)
 {
@@ -104,8 +117,8 @@ static void disk2memdqb(struct mem_dqblk *m, struct v2_disk_dqblk *d)
 	m->dqb_isoftlimit = le32_to_cpu(d->dqb_isoftlimit);
 	m->dqb_curinodes = le32_to_cpu(d->dqb_curinodes);
 	m->dqb_itime = le64_to_cpu(d->dqb_itime);
-	m->dqb_bhardlimit = le32_to_cpu(d->dqb_bhardlimit);
-	m->dqb_bsoftlimit = le32_to_cpu(d->dqb_bsoftlimit);
+	m->dqb_bhardlimit = v2_qbtos(le32_to_cpu(d->dqb_bhardlimit));
+	m->dqb_bsoftlimit = v2_qbtos(le32_to_cpu(d->dqb_bsoftlimit));
 	m->dqb_curspace = le64_to_cpu(d->dqb_curspace);
 	m->dqb_btime = le64_to_cpu(d->dqb_btime);
 }
@@ -116,8 +129,8 @@ static void mem2diskdqb(struct v2_disk_dqblk *d, struct mem_dqblk *m, qid_t id)
 	d->dqb_isoftlimit = cpu_to_le32(m->dqb_isoftlimit);
 	d->dqb_curinodes = cpu_to_le32(m->dqb_curinodes);
 	d->dqb_itime = cpu_to_le64(m->dqb_itime);
-	d->dqb_bhardlimit = cpu_to_le32(m->dqb_bhardlimit);
-	d->dqb_bsoftlimit = cpu_to_le32(m->dqb_bsoftlimit);
+	d->dqb_bhardlimit = cpu_to_le32(v2_qbtos(m->dqb_bhardlimit));
+	d->dqb_bsoftlimit = cpu_to_le32(v2_qbtos(m->dqb_bsoftlimit));
 	d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
 	d->dqb_btime = cpu_to_le64(m->dqb_btime);
 	d->dqb_id = cpu_to_le32(id);
diff --git a/include/linux/quota.h b/include/linux/quota.h
index eeae7a9..5167786 100644
--- a/include/linux/quota.h
+++ b/include/linux/quota.h
@@ -41,15 +41,6 @@
 #define __DQUOT_VERSION__	"dquot_6.5.1"
 #define __DQUOT_NUM_VERSION__	6*10000+5*100+1
 
-/* Size of blocks in which are counted size limits */
-#define QUOTABLOCK_BITS 10
-#define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS)
-
-/* Conversion routines from and to quota blocks */
-#define qb2kb(x) ((x) << (QUOTABLOCK_BITS-10))
-#define kb2qb(x) ((x) >> (QUOTABLOCK_BITS-10))
-#define toqb(x) (((x) + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS)
-
 #define MAXQUOTAS 2
 #define USRQUOTA  0		/* element used for user quotas */
 #define GRPQUOTA  1		/* element used for group quotas */
@@ -82,6 +73,11 @@
 #define Q_GETQUOTA 0x800007	/* get user quota structure */
 #define Q_SETQUOTA 0x800008	/* set user quota structure */
 
+/* Size of block in which space limits are passed through the quota
+ * interface */
+#define QIF_DQBLKSIZE_BITS 10
+#define QIF_DQBLKSIZE (1 << QIF_DQBLKSIZE_BITS)
+
 /*
  * Quota structure used for communication with userspace via quotactl
  * Following flags are used to specify which fields are valid
@@ -189,12 +185,12 @@ extern spinlock_t dq_data_lock;
  * Data for one user/group kept in memory
  */
 struct mem_dqblk {
-	__u32 dqb_bhardlimit;	/* absolute limit on disk blks alloc */
-	__u32 dqb_bsoftlimit;	/* preferred limit on disk blks */
+	qsize_t dqb_bhardlimit;	/* absolute limit on disk blks alloc */
+	qsize_t dqb_bsoftlimit;	/* preferred limit on disk blks */
 	qsize_t dqb_curspace;	/* current used space */
-	__u32 dqb_ihardlimit;	/* absolute limit on allocated inodes */
-	__u32 dqb_isoftlimit;	/* preferred inode limit */
-	__u32 dqb_curinodes;	/* current # allocated inodes */
+	qsize_t dqb_ihardlimit;	/* absolute limit on allocated inodes */
+	qsize_t dqb_isoftlimit;	/* preferred inode limit */
+	qsize_t dqb_curinodes;	/* current # allocated inodes */
 	time_t dqb_btime;	/* time limit for excessive disk use */
 	time_t dqb_itime;	/* time limit for excessive inode use */
 };
@@ -289,9 +285,9 @@ struct dquot_operations {
 	int (*initialize) (struct inode *, int);
 	int (*drop) (struct inode *);
 	int (*alloc_space) (struct inode *, qsize_t, int);
-	int (*alloc_inode) (const struct inode *, unsigned long);
+	int (*alloc_inode) (const struct inode *, qsize_t);
 	int (*free_space) (struct inode *, qsize_t);
-	int (*free_inode) (const struct inode *, unsigned long);
+	int (*free_inode) (const struct inode *, qsize_t);
 	int (*transfer) (struct inode *, struct iattr *);
 	int (*write_dquot) (struct dquot *);		/* Ordinary dquot write */
 	struct dquot *(*alloc_dquot)(struct super_block *, int);	/* Allocate memory for new dquot (can be NULL if no special entries dquot are needed) */
diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h
index ca6b9b5..9e7bc4b 100644
--- a/include/linux/quotaops.h
+++ b/include/linux/quotaops.h
@@ -29,10 +29,10 @@ int dquot_initialize(struct inode *inode, int type);
 int dquot_drop(struct inode *inode);
 
 int dquot_alloc_space(struct inode *inode, qsize_t number, int prealloc);
-int dquot_alloc_inode(const struct inode *inode, unsigned long number);
+int dquot_alloc_inode(const struct inode *inode, qsize_t number);
 
 int dquot_free_space(struct inode *inode, qsize_t number);
-int dquot_free_inode(const struct inode *inode, unsigned long number);
+int dquot_free_inode(const struct inode *inode, qsize_t number);
 
 int dquot_transfer(struct inode *inode, struct iattr *iattr);
 int dquot_commit(struct dquot *dquot);
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 03/29] quota: Remove bogus 'optimization' in check_idq() and check_bdq()
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
  2008-10-24 22:07 ` [Ocfs2-devel] [PATCH 01/29] quota: Add callbacks for allocating and destroying dquot structures Jan Kara
  2008-10-24 22:07 ` [Ocfs2-devel] [PATCH 02/29] quota: Increase size of variables for limits and inode usage Jan Kara
@ 2008-10-24 22:07 ` Jan Kara
  2008-10-24 22:07 ` [Ocfs2-devel] [PATCH 04/29] quota: Make _SUSPENDED just a flag Jan Kara
                   ` (25 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:07 UTC (permalink / raw)
  To: ocfs2-devel

Checks like <= 0 for an unsigned type do not make much sence. The value
could be only 0 and that does not happen often enough for the check
to be worth it.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/dquot.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/fs/dquot.c b/fs/dquot.c
index 413e71d..9eda830 100644
--- a/fs/dquot.c
+++ b/fs/dquot.c
@@ -1039,7 +1039,7 @@ static inline char ignore_hardlimit(struct dquot *dquot)
 static int check_idq(struct dquot *dquot, qsize_t inodes, char *warntype)
 {
 	*warntype = QUOTA_NL_NOWARN;
-	if (inodes <= 0 || test_bit(DQ_FAKE_B, &dquot->dq_flags))
+	if (test_bit(DQ_FAKE_B, &dquot->dq_flags))
 		return QUOTA_OK;
 
 	if (dquot->dq_dqb.dqb_ihardlimit &&
@@ -1071,7 +1071,7 @@ static int check_idq(struct dquot *dquot, qsize_t inodes, char *warntype)
 static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *warntype)
 {
 	*warntype = QUOTA_NL_NOWARN;
-	if (space <= 0 || test_bit(DQ_FAKE_B, &dquot->dq_flags))
+	if (test_bit(DQ_FAKE_B, &dquot->dq_flags))
 		return QUOTA_OK;
 
 	if (dquot->dq_dqb.dqb_bhardlimit &&
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 04/29] quota: Make _SUSPENDED just a flag
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (2 preceding siblings ...)
  2008-10-24 22:07 ` [Ocfs2-devel] [PATCH 03/29] quota: Remove bogus 'optimization' in check_idq() and check_bdq() Jan Kara
@ 2008-10-24 22:07 ` Jan Kara
  2008-10-24 22:07 ` [Ocfs2-devel] [PATCH 05/29] quota: Allow to separately enable quota accounting and enforcing limits Jan Kara
                   ` (24 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:07 UTC (permalink / raw)
  To: ocfs2-devel

Upto now, DQUOT_USR_SUSPENDED behaved like a state - i.e., either quota
was enabled or suspended or none. Now allowed states are 0, ENABLED,
ENABLED | SUSPENDED. This will be useful later when we implement separate
enabling of quota usage tracking and limits enforcement because we need to
keep track of a state which has been suspended.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/dquot.c               |   10 ++++++----
 include/linux/quotaops.h |    6 ++++--
 2 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/fs/dquot.c b/fs/dquot.c
index 9eda830..46d46df 100644
--- a/fs/dquot.c
+++ b/fs/dquot.c
@@ -1566,18 +1566,20 @@ static inline void reset_enable_flags(struct quota_info *dqopt, int type,
 {
 	switch (type) {
 		case USRQUOTA:
-			dqopt->flags &= ~DQUOT_USR_ENABLED;
 			if (remount)
 				dqopt->flags |= DQUOT_USR_SUSPENDED;
-			else
+			else {
+				dqopt->flags &= ~DQUOT_USR_ENABLED;
 				dqopt->flags &= ~DQUOT_USR_SUSPENDED;
+			}
 			break;
 		case GRPQUOTA:
-			dqopt->flags &= ~DQUOT_GRP_ENABLED;
 			if (remount)
 				dqopt->flags |= DQUOT_GRP_SUSPENDED;
-			else
+			else {
+				dqopt->flags &= ~DQUOT_GRP_ENABLED;
 				dqopt->flags &= ~DQUOT_GRP_SUSPENDED;
+			}
 			break;
 	}
 }
diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h
index 9e7bc4b..12363cc 100644
--- a/include/linux/quotaops.h
+++ b/include/linux/quotaops.h
@@ -70,8 +70,10 @@ static inline struct mem_dqinfo *sb_dqinfo(struct super_block *sb, int type)
 static inline int sb_has_quota_enabled(struct super_block *sb, int type)
 {
 	if (type == USRQUOTA)
-		return sb_dqopt(sb)->flags & DQUOT_USR_ENABLED;
-	return sb_dqopt(sb)->flags & DQUOT_GRP_ENABLED;
+		return (sb_dqopt(sb)->flags & DQUOT_USR_ENABLED)
+			&& !(sb_dqopt(sb)->flags & DQUOT_USR_SUSPENDED);
+	return (sb_dqopt(sb)->flags & DQUOT_GRP_ENABLED)
+		&& !(sb_dqopt(sb)->flags & DQUOT_GROUP_SUSPENDED);
 }
 
 static inline int sb_any_quota_enabled(struct super_block *sb)
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 05/29] quota: Allow to separately enable quota accounting and enforcing limits
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (3 preceding siblings ...)
  2008-10-24 22:07 ` [Ocfs2-devel] [PATCH 04/29] quota: Make _SUSPENDED just a flag Jan Kara
@ 2008-10-24 22:07 ` Jan Kara
  2008-10-24 22:07 ` [Ocfs2-devel] [PATCH 06/29] ext3: Use sb_any_quota_loaded() instead of sb_any_quota_enabled() Jan Kara
                   ` (23 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:07 UTC (permalink / raw)
  To: ocfs2-devel

Split DQUOT_USR_ENABLED (and DQUOT_GRP_ENABLED) into DQUOT_USR_USAGE_ENABLED
and DQUOT_USR_LIMITS_ENABLED. This way we are able to separately enable /
disable whether we should:
1) ignore quotas completely
2) just keep uptodate information about usage
3) actually enforce quota limits

This is going to be useful when quota is treated as filesystem metadata - we
then want to keep quota information uptodate all the time and just enable /
disable limits enforcement.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/dquot.c               |  222 ++++++++++++++++++++++++++++-----------------
 fs/quota.c               |    8 +-
 include/linux/quota.h    |   30 ++++++-
 include/linux/quotaops.h |   86 ++++++++++++++----
 4 files changed, 234 insertions(+), 112 deletions(-)

diff --git a/fs/dquot.c b/fs/dquot.c
index 46d46df..96ed45b 100644
--- a/fs/dquot.c
+++ b/fs/dquot.c
@@ -489,7 +489,7 @@ int vfs_quota_sync(struct super_block *sb, int type)
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 		if (type != -1 && cnt != type)
 			continue;
-		if (!sb_has_quota_enabled(sb, cnt))
+		if (!sb_has_quota_active(sb, cnt))
 			continue;
 		spin_lock(&dq_list_lock);
 		dirty = &dqopt->info[cnt].dqi_dirty_list;
@@ -514,8 +514,8 @@ int vfs_quota_sync(struct super_block *sb, int type)
 	}
 
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++)
-		if ((cnt == type || type == -1) && sb_has_quota_enabled(sb, cnt)
-			&& info_dirty(&dqopt->info[cnt]))
+		if ((cnt == type || type == -1) && sb_has_quota_active(sb, cnt)
+		    && info_dirty(&dqopt->info[cnt]))
 			sb->dq_op->write_info(sb, cnt);
 	spin_lock(&dq_list_lock);
 	dqstats.syncs++;
@@ -594,7 +594,7 @@ we_slept:
 		/* We have more than one user... nothing to do */
 		atomic_dec(&dquot->dq_count);
 		/* Releasing dquot during quotaoff phase? */
-		if (!sb_has_quota_enabled(dquot->dq_sb, dquot->dq_type) &&
+		if (!sb_has_quota_active(dquot->dq_sb, dquot->dq_type) &&
 		    atomic_read(&dquot->dq_count) == 1)
 			wake_up(&dquot->dq_wait_unused);
 		spin_unlock(&dq_list_lock);
@@ -668,7 +668,7 @@ static struct dquot *dqget(struct super_block *sb, unsigned int id, int type)
 	unsigned int hashent = hashfn(sb, id, type);
 	struct dquot *dquot, *empty = NODQUOT;
 
-        if (!sb_has_quota_enabled(sb, type))
+        if (!sb_has_quota_active(sb, type))
 		return NODQUOT;
 we_slept:
 	spin_lock(&dq_list_lock);
@@ -1039,7 +1039,8 @@ static inline char ignore_hardlimit(struct dquot *dquot)
 static int check_idq(struct dquot *dquot, qsize_t inodes, char *warntype)
 {
 	*warntype = QUOTA_NL_NOWARN;
-	if (test_bit(DQ_FAKE_B, &dquot->dq_flags))
+	if (!sb_has_quota_limits_enabled(dquot->dq_sb, dquot->dq_type) ||
+	    test_bit(DQ_FAKE_B, &dquot->dq_flags))
 		return QUOTA_OK;
 
 	if (dquot->dq_dqb.dqb_ihardlimit &&
@@ -1071,7 +1072,8 @@ static int check_idq(struct dquot *dquot, qsize_t inodes, char *warntype)
 static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *warntype)
 {
 	*warntype = QUOTA_NL_NOWARN;
-	if (test_bit(DQ_FAKE_B, &dquot->dq_flags))
+	if (!sb_has_quota_limits_enabled(dquot->dq_sb, dquot->dq_type) ||
+	    test_bit(DQ_FAKE_B, &dquot->dq_flags))
 		return QUOTA_OK;
 
 	if (dquot->dq_dqb.dqb_bhardlimit &&
@@ -1112,7 +1114,8 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *war
 static int info_idq_free(struct dquot *dquot, qsize_t inodes)
 {
 	if (test_bit(DQ_FAKE_B, &dquot->dq_flags) ||
-	    dquot->dq_dqb.dqb_curinodes <= dquot->dq_dqb.dqb_isoftlimit)
+	    dquot->dq_dqb.dqb_curinodes <= dquot->dq_dqb.dqb_isoftlimit ||
+	    !sb_has_quota_limits_enabled(dquot->dq_sb, dquot->dq_type))
 		return QUOTA_NL_NOWARN;
 
 	if (dquot->dq_dqb.dqb_curinodes - inodes <= dquot->dq_dqb.dqb_isoftlimit)
@@ -1506,7 +1509,7 @@ warn_put_all:
 /* Wrapper for transferring ownership of an inode */
 int vfs_dq_transfer(struct inode *inode, struct iattr *iattr)
 {
-	if (sb_any_quota_enabled(inode->i_sb) && !IS_NOQUOTA(inode)) {
+	if (sb_any_quota_active(inode->i_sb) && !IS_NOQUOTA(inode)) {
 		vfs_dq_init(inode);
 		if (inode->i_sb->dq_op->transfer(inode, iattr) == NO_QUOTA)
 			return 1;
@@ -1547,53 +1550,22 @@ struct dquot_operations dquot_operations = {
 	.write_info	= dquot_commit_info
 };
 
-static inline void set_enable_flags(struct quota_info *dqopt, int type)
-{
-	switch (type) {
-		case USRQUOTA:
-			dqopt->flags |= DQUOT_USR_ENABLED;
-			dqopt->flags &= ~DQUOT_USR_SUSPENDED;
-			break;
-		case GRPQUOTA:
-			dqopt->flags |= DQUOT_GRP_ENABLED;
-			dqopt->flags &= ~DQUOT_GRP_SUSPENDED;
-			break;
-	}
-}
-
-static inline void reset_enable_flags(struct quota_info *dqopt, int type,
-				      int remount)
-{
-	switch (type) {
-		case USRQUOTA:
-			if (remount)
-				dqopt->flags |= DQUOT_USR_SUSPENDED;
-			else {
-				dqopt->flags &= ~DQUOT_USR_ENABLED;
-				dqopt->flags &= ~DQUOT_USR_SUSPENDED;
-			}
-			break;
-		case GRPQUOTA:
-			if (remount)
-				dqopt->flags |= DQUOT_GRP_SUSPENDED;
-			else {
-				dqopt->flags &= ~DQUOT_GRP_ENABLED;
-				dqopt->flags &= ~DQUOT_GRP_SUSPENDED;
-			}
-			break;
-	}
-}
-
-
 /*
  * Turn quota off on a device. type == -1 ==> quotaoff for all types (umount)
  */
-int vfs_quota_off(struct super_block *sb, int type, int remount)
+int vfs_quota_disable(struct super_block *sb, int type, unsigned int flags)
 {
 	int cnt, ret = 0;
 	struct quota_info *dqopt = sb_dqopt(sb);
 	struct inode *toputinode[MAXQUOTAS];
 
+	/* Cannot turn off usage accounting without turning off limits, or
+	 * suspend quotas and simultaneously turn quotas off. */
+	if ((flags & DQUOT_USAGE_ENABLED && !(flags & DQUOT_LIMITS_ENABLED))
+	    || (flags & DQUOT_SUSPENDED && flags & (DQUOT_LIMITS_ENABLED |
+	    DQUOT_USAGE_ENABLED)))
+		return -EINVAL;
+
 	/* We need to serialize quota_off() for device */
 	mutex_lock(&dqopt->dqonoff_mutex);
 
@@ -1602,7 +1574,7 @@ int vfs_quota_off(struct super_block *sb, int type, int remount)
 	 * sometimes we are called when fill_super() failed and calling
 	 * sync_fs() in such cases does no good.
 	 */
-	if (!sb_any_quota_enabled(sb) && !sb_any_quota_suspended(sb)) {
+	if (!sb_any_quota_loaded(sb)) {
 		mutex_unlock(&dqopt->dqonoff_mutex);
 		return 0;
 	}
@@ -1610,17 +1582,28 @@ int vfs_quota_off(struct super_block *sb, int type, int remount)
 		toputinode[cnt] = NULL;
 		if (type != -1 && cnt != type)
 			continue;
-		/* If we keep inodes of quota files after remount and quotaoff
-		 * is called, drop kept inodes. */
-		if (!remount && sb_has_quota_suspended(sb, cnt)) {
-			iput(dqopt->files[cnt]);
-			dqopt->files[cnt] = NULL;
-			reset_enable_flags(dqopt, cnt, 0);
+		if (!sb_has_quota_loaded(sb, cnt))
 			continue;
+
+		if (flags & DQUOT_SUSPENDED) {
+			dqopt->flags |=
+				dquot_state_flag(DQUOT_SUSPENDED, cnt);
+		} else {
+			dqopt->flags &= ~dquot_state_flag(flags, cnt);
+			/* Turning off suspended quotas? */
+			if (!sb_has_quota_loaded(sb, cnt) &&
+			    sb_has_quota_suspended(sb, cnt)) {
+				dqopt->flags &=	~dquot_state_flag(
+							DQUOT_SUSPENDED, cnt);
+				iput(dqopt->files[cnt]);
+				dqopt->files[cnt] = NULL;
+				continue;
+			}
 		}
-		if (!sb_has_quota_enabled(sb, cnt))
+
+		/* We still have to keep quota loaded? */
+		if (sb_has_quota_loaded(sb, cnt) && !(flags & DQUOT_SUSPENDED))
 			continue;
-		reset_enable_flags(dqopt, cnt, remount);
 
 		/* Note: these are blocking operations */
 		drop_dquot_ref(sb, cnt);
@@ -1636,7 +1619,7 @@ int vfs_quota_off(struct super_block *sb, int type, int remount)
 		put_quota_format(dqopt->info[cnt].dqi_format);
 
 		toputinode[cnt] = dqopt->files[cnt];
-		if (!remount)
+		if (!sb_has_quota_loaded(sb, cnt))
 			dqopt->files[cnt] = NULL;
 		dqopt->info[cnt].dqi_flags = 0;
 		dqopt->info[cnt].dqi_igrace = 0;
@@ -1659,7 +1642,7 @@ int vfs_quota_off(struct super_block *sb, int type, int remount)
 			mutex_lock(&dqopt->dqonoff_mutex);
 			/* If quota was reenabled in the meantime, we have
 			 * nothing to do */
-			if (!sb_has_quota_enabled(sb, cnt)) {
+			if (!sb_has_quota_loaded(sb, cnt)) {
 				mutex_lock_nested(&toputinode[cnt]->i_mutex, I_MUTEX_QUOTA);
 				toputinode[cnt]->i_flags &= ~(S_IMMUTABLE |
 				  S_NOATIME | S_NOQUOTA);
@@ -1669,10 +1652,13 @@ int vfs_quota_off(struct super_block *sb, int type, int remount)
 			}
 			mutex_unlock(&dqopt->dqonoff_mutex);
 			/* On remount RO, we keep the inode pointer so that we
-			 * can reenable quota on the subsequent remount RW.
-			 * But we have better not keep inode pointer when there
-			 * is pending delete on the quota file... */
-			if (!remount)
+			 * can reenable quota on the subsequent remount RW. We
+			 * have to check 'flags' variable and not use sb_has_
+			 * function because another quotaon / quotaoff could
+			 * change global state before we got here. We refuse
+			 * to suspend quotas when there is pending delete on
+			 * the quota file... */
+			if (!(flags & DQUOT_SUSPENDED))
 				iput(toputinode[cnt]);
 			else if (!toputinode[cnt]->i_nlink)
 				ret = -EBUSY;
@@ -1682,12 +1668,22 @@ int vfs_quota_off(struct super_block *sb, int type, int remount)
 	return ret;
 }
 
+int vfs_quota_off(struct super_block *sb, int type, int remount)
+{
+	return vfs_quota_disable(sb, type, remount ? DQUOT_SUSPENDED :
+				 (DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED));
+}
+
 /*
  *	Turn quotas on on a device
  */
 
-/* Helper function when we already have the inode */
-static int vfs_quota_on_inode(struct inode *inode, int type, int format_id)
+/*
+ * Helper function to turn quotas on when we already have the inode of
+ * quota file and no quota information is loaded.
+ */
+static int vfs_load_quota_inode(struct inode *inode, int type, int format_id,
+	unsigned int flags)
 {
 	struct quota_format_type *fmt = find_quota_format(format_id);
 	struct super_block *sb = inode->i_sb;
@@ -1709,6 +1705,11 @@ static int vfs_quota_on_inode(struct inode *inode, int type, int format_id)
 		error = -EINVAL;
 		goto out_fmt;
 	}
+	/* Usage always has to be set... */
+	if (!(flags & DQUOT_USAGE_ENABLED)) {
+		error = -EINVAL;
+		goto out_fmt;
+	}
 
 	/* As we bypass the pagecache we must now flush the inode so that
 	 * we see all the changes from userspace... */
@@ -1717,8 +1718,7 @@ static int vfs_quota_on_inode(struct inode *inode, int type, int format_id)
 	invalidate_bdev(sb->s_bdev);
 	mutex_lock(&inode->i_mutex);
 	mutex_lock(&dqopt->dqonoff_mutex);
-	if (sb_has_quota_enabled(sb, type) ||
-			sb_has_quota_suspended(sb, type)) {
+	if (sb_has_quota_loaded(sb, type)) {
 		error = -EBUSY;
 		goto out_lock;
 	}
@@ -1750,7 +1750,7 @@ static int vfs_quota_on_inode(struct inode *inode, int type, int format_id)
 	}
 	mutex_unlock(&dqopt->dqio_mutex);
 	mutex_unlock(&inode->i_mutex);
-	set_enable_flags(dqopt, type);
+	dqopt->flags |= dquot_state_flag(flags, type);
 
 	add_dquot_ref(sb, type);
 	mutex_unlock(&dqopt->dqonoff_mutex);
@@ -1783,20 +1783,23 @@ static int vfs_quota_on_remount(struct super_block *sb, int type)
 	struct quota_info *dqopt = sb_dqopt(sb);
 	struct inode *inode;
 	int ret;
+	unsigned int flags;
 
 	mutex_lock(&dqopt->dqonoff_mutex);
 	if (!sb_has_quota_suspended(sb, type)) {
 		mutex_unlock(&dqopt->dqonoff_mutex);
 		return 0;
 	}
-	BUG_ON(sb_has_quota_enabled(sb, type));
-
 	inode = dqopt->files[type];
 	dqopt->files[type] = NULL;
-	reset_enable_flags(dqopt, type, 0);
+	flags = dqopt->flags & dquot_state_flag(DQUOT_USAGE_ENABLED |
+						DQUOT_LIMITS_ENABLED, type);
+	dqopt->flags &= ~dquot_state_flag(DQUOT_STATE_FLAGS, type);
 	mutex_unlock(&dqopt->dqonoff_mutex);
 
-	ret = vfs_quota_on_inode(inode, type, dqopt->info[type].dqi_fmt_id);
+	flags = dquot_generic_flag(flags, type);
+	ret = vfs_load_quota_inode(inode, type, dqopt->info[type].dqi_fmt_id,
+				   flags);
 	iput(inode);
 
 	return ret;
@@ -1812,12 +1815,12 @@ int vfs_quota_on_path(struct super_block *sb, int type, int format_id,
 	if (path->mnt->mnt_sb != sb)
 		error = -EXDEV;
 	else
-		error = vfs_quota_on_inode(path->dentry->d_inode, type,
-					   format_id);
+		error = vfs_load_quota_inode(path->dentry->d_inode, type,
+					     format_id, DQUOT_USAGE_ENABLED |
+					     DQUOT_LIMITS_ENABLED);
 	return error;
 }
 
-/* Actual function called from quotactl() */
 int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path,
 		 int remount)
 {
@@ -1836,6 +1839,50 @@ int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path,
 }
 
 /*
+ * More powerful function for turning on quotas allowing setting
+ * of individual quota flags
+ */
+int vfs_quota_enable(struct inode *inode, int type, int format_id,
+		unsigned int flags)
+{
+	int ret = 0;
+	struct super_block *sb = inode->i_sb;
+	struct quota_info *dqopt = sb_dqopt(sb);
+
+	/* Just unsuspend quotas? */
+	if (flags & DQUOT_SUSPENDED)
+		return vfs_quota_on_remount(sb, type);
+	if (!flags)
+		return 0;
+	/* Just updating flags needed? */
+	if (sb_has_quota_loaded(sb, type)) {
+		mutex_lock(&dqopt->dqonoff_mutex);
+		/* Now do a reliable test... */
+		if (!sb_has_quota_loaded(sb, type)) {
+			mutex_unlock(&dqopt->dqonoff_mutex);
+			goto load_quota;
+		}
+		if (flags & DQUOT_USAGE_ENABLED &&
+		    sb_has_quota_usage_enabled(sb, type)) {
+			ret = -EBUSY;
+			goto out_lock;
+		}
+		if (flags & DQUOT_LIMITS_ENABLED &&
+		    sb_has_quota_limits_enabled(sb, type)) {
+			ret = -EBUSY;
+			goto out_lock;
+		}
+		sb_dqopt(sb)->flags |= dquot_state_flag(flags, type);
+out_lock:
+		mutex_unlock(&dqopt->dqonoff_mutex);
+		return ret;
+	}
+
+load_quota:
+	return vfs_load_quota_inode(inode, type, format_id, flags);
+}
+
+/*
  * This function is used when filesystem needs to initialize quotas
  * during mount time.
  */
@@ -1856,7 +1903,8 @@ int vfs_quota_on_mount(struct super_block *sb, char *qf_name,
 
 	error = security_quota_on(dentry);
 	if (!error)
-		error = vfs_quota_on_inode(dentry->d_inode, type, format_id);
+		error = vfs_load_quota_inode(dentry->d_inode, type, format_id,
+				DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED);
 
 out:
 	dput(dentry);
@@ -1993,12 +2041,14 @@ int vfs_set_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *d
 	int rc;
 
 	mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
-	if (!(dquot = dqget(sb, id, type))) {
-		mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
-		return -ESRCH;
+	dquot = dqget(sb, id, type);
+	if (!dquot) {
+		rc = -ESRCH;
+		goto out;
 	}
 	rc = do_set_dqblk(dquot, di);
 	dqput(dquot);
+out:
 	mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
 	return rc;
 }
@@ -2009,7 +2059,7 @@ int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
 	struct mem_dqinfo *mi;
   
 	mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
-	if (!sb_has_quota_enabled(sb, type)) {
+	if (!sb_has_quota_active(sb, type)) {
 		mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
 		return -ESRCH;
 	}
@@ -2028,11 +2078,12 @@ int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
 int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
 {
 	struct mem_dqinfo *mi;
+	int err = 0;
 
 	mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
-	if (!sb_has_quota_enabled(sb, type)) {
-		mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
-		return -ESRCH;
+	if (!sb_has_quota_active(sb, type)) {
+		err = -ESRCH;
+		goto out;
 	}
 	mi = sb_dqopt(sb)->info + type;
 	spin_lock(&dq_data_lock);
@@ -2046,8 +2097,9 @@ int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
 	mark_info_dirty(sb, type);
 	/* Force write to disk */
 	sb->dq_op->write_info(sb, type);
+out:
 	mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
-	return 0;
+	return err;
 }
 
 struct quotactl_ops vfs_quotactl_ops = {
@@ -2209,9 +2261,11 @@ EXPORT_SYMBOL(register_quota_format);
 EXPORT_SYMBOL(unregister_quota_format);
 EXPORT_SYMBOL(dqstats);
 EXPORT_SYMBOL(dq_data_lock);
+EXPORT_SYMBOL(vfs_quota_enable);
 EXPORT_SYMBOL(vfs_quota_on);
 EXPORT_SYMBOL(vfs_quota_on_path);
 EXPORT_SYMBOL(vfs_quota_on_mount);
+EXPORT_SYMBOL(vfs_quota_disable);
 EXPORT_SYMBOL(vfs_quota_off);
 EXPORT_SYMBOL(vfs_quota_sync);
 EXPORT_SYMBOL(vfs_get_dqinfo);
diff --git a/fs/quota.c b/fs/quota.c
index 7f4386e..a8026f1 100644
--- a/fs/quota.c
+++ b/fs/quota.c
@@ -73,7 +73,7 @@ static int generic_quotactl_valid(struct super_block *sb, int type, int cmd, qid
 		case Q_SETQUOTA:
 		case Q_GETQUOTA:
 			/* This is just informative test so we are satisfied without a lock */
-			if (!sb_has_quota_enabled(sb, type))
+			if (!sb_has_quota_active(sb, type))
 				return -ESRCH;
 	}
 
@@ -175,7 +175,7 @@ static void quota_sync_sb(struct super_block *sb, int type)
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 		if (type != -1 && cnt != type)
 			continue;
-		if (!sb_has_quota_enabled(sb, cnt))
+		if (!sb_has_quota_active(sb, cnt))
 			continue;
 		mutex_lock_nested(&sb_dqopt(sb)->files[cnt]->i_mutex, I_MUTEX_QUOTA);
 		truncate_inode_pages(&sb_dqopt(sb)->files[cnt]->i_data, 0);
@@ -201,7 +201,7 @@ restart:
 		for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 			if (type != -1 && type != cnt)
 				continue;
-			if (!sb_has_quota_enabled(sb, cnt))
+			if (!sb_has_quota_active(sb, cnt))
 				continue;
 			if (!info_dirty(&sb_dqopt(sb)->info[cnt]) &&
 			    list_empty(&sb_dqopt(sb)->info[cnt].dqi_dirty_list))
@@ -245,7 +245,7 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, void
 			__u32 fmt;
 
 			down_read(&sb_dqopt(sb)->dqptr_sem);
-			if (!sb_has_quota_enabled(sb, type)) {
+			if (!sb_has_quota_active(sb, type)) {
 				up_read(&sb_dqopt(sb)->dqptr_sem);
 				return -ESRCH;
 			}
diff --git a/include/linux/quota.h b/include/linux/quota.h
index 5167786..0e4b550 100644
--- a/include/linux/quota.h
+++ b/include/linux/quota.h
@@ -320,12 +320,34 @@ struct quota_format_type {
 	struct quota_format_type *qf_next;
 };
 
-#define DQUOT_USR_ENABLED	0x01		/* User diskquotas enabled */
-#define DQUOT_GRP_ENABLED	0x02		/* Group diskquotas enabled */
-#define DQUOT_USR_SUSPENDED	0x04		/* User diskquotas are off, but
+/* Quota state flags - they actually come in two flavors - for users and groups */
+enum {
+	_DQUOT_USAGE_ENABLED = 0,		/* Track disk usage for users */
+	_DQUOT_LIMITS_ENABLED,			/* Enforce quota limits for users */
+	_DQUOT_SUSPENDED,			/* User diskquotas are off, but
 						 * we have necessary info in
 						 * memory to turn them on */
-#define DQUOT_GRP_SUSPENDED	0x08		/* The same for group quotas */
+	_DQUOT_STATE_FLAGS
+};
+#define DQUOT_USAGE_ENABLED	(1 << _DQUOT_USAGE_ENABLED)
+#define DQUOT_LIMITS_ENABLED	(1 << _DQUOT_LIMITS_ENABLED)
+#define DQUOT_SUSPENDED		(1 << _DQUOT_SUSPENDED)
+#define DQUOT_STATE_FLAGS	(DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED | \
+				 DQUOT_SUSPENDED)
+
+static inline unsigned int dquot_state_flag(unsigned int flags, int type)
+{
+	if (type == USRQUOTA)
+		return flags;
+	return flags << _DQUOT_STATE_FLAGS;
+}
+
+static inline unsigned int dquot_generic_flag(unsigned int flags, int type)
+{
+	if (type == USRQUOTA)
+		return flags;
+	return flags >> _DQUOT_STATE_FLAGS;
+}
 
 struct quota_info {
 	unsigned int flags;			/* Flags for diskquotas on this device */
diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h
index 12363cc..f7dcc30 100644
--- a/include/linux/quotaops.h
+++ b/include/linux/quotaops.h
@@ -43,11 +43,14 @@ int dquot_mark_dquot_dirty(struct dquot *dquot);
 
 int vfs_quota_on(struct super_block *sb, int type, int format_id,
  	char *path, int remount);
+int vfs_quota_enable(struct inode *inode, int type, int format_id,
+	unsigned int flags);
 int vfs_quota_on_path(struct super_block *sb, int type, int format_id,
  	struct path *path);
 int vfs_quota_on_mount(struct super_block *sb, char *qf_name,
  	int format_id, int type);
 int vfs_quota_off(struct super_block *sb, int type, int remount);
+int vfs_quota_disable(struct super_block *sb, int type, unsigned int flags);
 int vfs_quota_sync(struct super_block *sb, int type);
 int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii);
 int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii);
@@ -67,26 +70,22 @@ static inline struct mem_dqinfo *sb_dqinfo(struct super_block *sb, int type)
  * Functions for checking status of quota
  */
 
-static inline int sb_has_quota_enabled(struct super_block *sb, int type)
+static inline int sb_has_quota_usage_enabled(struct super_block *sb, int type)
 {
-	if (type == USRQUOTA)
-		return (sb_dqopt(sb)->flags & DQUOT_USR_ENABLED)
-			&& !(sb_dqopt(sb)->flags & DQUOT_USR_SUSPENDED);
-	return (sb_dqopt(sb)->flags & DQUOT_GRP_ENABLED)
-		&& !(sb_dqopt(sb)->flags & DQUOT_GROUP_SUSPENDED);
+	return sb_dqopt(sb)->flags &
+				dquot_state_flag(DQUOT_USAGE_ENABLED, type);
 }
 
-static inline int sb_any_quota_enabled(struct super_block *sb)
+static inline int sb_has_quota_limits_enabled(struct super_block *sb, int type)
 {
-	return sb_has_quota_enabled(sb, USRQUOTA) ||
-		sb_has_quota_enabled(sb, GRPQUOTA);
+	return sb_dqopt(sb)->flags &
+				dquot_state_flag(DQUOT_LIMITS_ENABLED, type);
 }
 
 static inline int sb_has_quota_suspended(struct super_block *sb, int type)
 {
-	if (type == USRQUOTA)
-		return sb_dqopt(sb)->flags & DQUOT_USR_SUSPENDED;
-	return sb_dqopt(sb)->flags & DQUOT_GRP_SUSPENDED;
+	return sb_dqopt(sb)->flags &
+				dquot_state_flag(DQUOT_SUSPENDED, type);
 }
 
 static inline int sb_any_quota_suspended(struct super_block *sb)
@@ -95,6 +94,34 @@ static inline int sb_any_quota_suspended(struct super_block *sb)
 		sb_has_quota_suspended(sb, GRPQUOTA);
 }
 
+/* Does kernel know about any quota information for given sb + type? */
+static inline int sb_has_quota_loaded(struct super_block *sb, int type)
+{
+	/* Currently if anything is on, then quota usage is on as well */
+	return sb_has_quota_usage_enabled(sb, type);
+}
+
+static inline int sb_any_quota_loaded(struct super_block *sb)
+{
+	return sb_has_quota_loaded(sb, USRQUOTA) ||
+		sb_has_quota_loaded(sb, GRPQUOTA);
+}
+
+static inline int sb_has_quota_active(struct super_block *sb, int type)
+{
+	return sb_has_quota_loaded(sb, type) &&
+	       !sb_has_quota_suspended(sb, type);
+}
+
+static inline int sb_any_quota_active(struct super_block *sb)
+{
+	return sb_has_quota_active(sb, USRQUOTA) ||
+	       sb_has_quota_active(sb, GRPQUOTA);
+}
+
+/* For backward compatibility until we remove all users */
+#define sb_any_quota_enabled(sb) sb_any_quota_active(sb)
+
 /*
  * Operations supported for diskquotas.
  */
@@ -109,7 +136,7 @@ extern struct quotactl_ops vfs_quotactl_ops;
 static inline void vfs_dq_init(struct inode *inode)
 {
 	BUG_ON(!inode->i_sb);
-	if (sb_any_quota_enabled(inode->i_sb) && !IS_NOQUOTA(inode))
+	if (sb_any_quota_active(inode->i_sb) && !IS_NOQUOTA(inode))
 		inode->i_sb->dq_op->initialize(inode, -1);
 }
 
@@ -117,7 +144,7 @@ static inline void vfs_dq_init(struct inode *inode)
  * a transaction (deadlocks possible otherwise) */
 static inline int vfs_dq_prealloc_space_nodirty(struct inode *inode, qsize_t nr)
 {
-	if (sb_any_quota_enabled(inode->i_sb)) {
+	if (sb_any_quota_active(inode->i_sb)) {
 		/* Used space is updated in alloc_space() */
 		if (inode->i_sb->dq_op->alloc_space(inode, nr, 1) == NO_QUOTA)
 			return 1;
@@ -137,7 +164,7 @@ static inline int vfs_dq_prealloc_space(struct inode *inode, qsize_t nr)
 
 static inline int vfs_dq_alloc_space_nodirty(struct inode *inode, qsize_t nr)
 {
-	if (sb_any_quota_enabled(inode->i_sb)) {
+	if (sb_any_quota_active(inode->i_sb)) {
 		/* Used space is updated in alloc_space() */
 		if (inode->i_sb->dq_op->alloc_space(inode, nr, 0) == NO_QUOTA)
 			return 1;
@@ -157,7 +184,7 @@ static inline int vfs_dq_alloc_space(struct inode *inode, qsize_t nr)
 
 static inline int vfs_dq_alloc_inode(struct inode *inode)
 {
-	if (sb_any_quota_enabled(inode->i_sb)) {
+	if (sb_any_quota_active(inode->i_sb)) {
 		vfs_dq_init(inode);
 		if (inode->i_sb->dq_op->alloc_inode(inode, 1) == NO_QUOTA)
 			return 1;
@@ -167,7 +194,7 @@ static inline int vfs_dq_alloc_inode(struct inode *inode)
 
 static inline void vfs_dq_free_space_nodirty(struct inode *inode, qsize_t nr)
 {
-	if (sb_any_quota_enabled(inode->i_sb))
+	if (sb_any_quota_active(inode->i_sb))
 		inode->i_sb->dq_op->free_space(inode, nr);
 	else
 		inode_sub_bytes(inode, nr);
@@ -181,7 +208,7 @@ static inline void vfs_dq_free_space(struct inode *inode, qsize_t nr)
 
 static inline void vfs_dq_free_inode(struct inode *inode)
 {
-	if (sb_any_quota_enabled(inode->i_sb))
+	if (sb_any_quota_active(inode->i_sb))
 		inode->i_sb->dq_op->free_inode(inode, 1);
 }
 
@@ -202,12 +229,12 @@ static inline int vfs_dq_off(struct super_block *sb, int remount)
 
 #else
 
-static inline int sb_has_quota_enabled(struct super_block *sb, int type)
+static inline int sb_has_quota_usage_enabled(struct super_block *sb, int type)
 {
 	return 0;
 }
 
-static inline int sb_any_quota_enabled(struct super_block *sb)
+static inline int sb_has_quota_limits_enabled(struct super_block *sb, int type)
 {
 	return 0;
 }
@@ -222,6 +249,25 @@ static inline int sb_any_quota_suspended(struct super_block *sb)
 	return 0;
 }
 
+/* Does kernel know about any quota information for given sb + type? */
+static inline int sb_has_quota_loaded(struct super_block *sb, int type)
+{
+	return 0;
+}
+
+static inline int sb_any_quota_loaded(struct super_block *sb)
+{
+	return 0;
+}
+
+static inline int sb_any_quota_active(struct super_block *sb)
+{
+	return 0;
+}
+
+/* For backward compatibility until we remove all users */
+#define sb_any_quota_enabled(sb) sb_any_quota_active(sb)
+
 /*
  * NO-OP when quota not configured.
  */
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 06/29] ext3: Use sb_any_quota_loaded() instead of sb_any_quota_enabled()
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (4 preceding siblings ...)
  2008-10-24 22:07 ` [Ocfs2-devel] [PATCH 05/29] quota: Allow to separately enable quota accounting and enforcing limits Jan Kara
@ 2008-10-24 22:07 ` Jan Kara
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 07/29] ext4: " Jan Kara
                   ` (22 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:07 UTC (permalink / raw)
  To: ocfs2-devel

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/ext3/super.c |   12 ++++--------
 1 files changed, 4 insertions(+), 8 deletions(-)

diff --git a/fs/ext3/super.c b/fs/ext3/super.c
index 399a96a..12c2e22 100644
--- a/fs/ext3/super.c
+++ b/fs/ext3/super.c
@@ -1018,8 +1018,7 @@ static int parse_options (char *options, struct super_block *sb,
 		case Opt_grpjquota:
 			qtype = GRPQUOTA;
 set_qf_name:
-			if ((sb_any_quota_enabled(sb) ||
-			     sb_any_quota_suspended(sb)) &&
+			if (sb_any_quota_loaded(sb) &&
 			    !sbi->s_qf_names[qtype]) {
 				printk(KERN_ERR
 					"EXT3-fs: Cannot change journaled "
@@ -1058,8 +1057,7 @@ set_qf_name:
 		case Opt_offgrpjquota:
 			qtype = GRPQUOTA;
 clear_qf_name:
-			if ((sb_any_quota_enabled(sb) ||
-			     sb_any_quota_suspended(sb)) &&
+			if (sb_any_quota_loaded(sb) &&
 			    sbi->s_qf_names[qtype]) {
 				printk(KERN_ERR "EXT3-fs: Cannot change "
 					"journaled quota options when "
@@ -1078,8 +1076,7 @@ clear_qf_name:
 		case Opt_jqfmt_vfsv0:
 			qfmt = QFMT_VFS_V0;
 set_qf_format:
-			if ((sb_any_quota_enabled(sb) ||
-			     sb_any_quota_suspended(sb)) &&
+			if (sb_any_quota_loaded(sb) &&
 			    sbi->s_jquota_fmt != qfmt) {
 				printk(KERN_ERR "EXT3-fs: Cannot change "
 					"journaled quota options when "
@@ -1098,8 +1095,7 @@ set_qf_format:
 			set_opt(sbi->s_mount_opt, GRPQUOTA);
 			break;
 		case Opt_noquota:
-			if (sb_any_quota_enabled(sb) ||
-			    sb_any_quota_suspended(sb)) {
+			if (sb_any_quota_loaded(sb)) {
 				printk(KERN_ERR "EXT3-fs: Cannot change quota "
 					"options when quota turned on.\n");
 				return 0;
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 07/29] ext4: Use sb_any_quota_loaded() instead of sb_any_quota_enabled()
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (5 preceding siblings ...)
  2008-10-24 22:07 ` [Ocfs2-devel] [PATCH 06/29] ext3: Use sb_any_quota_loaded() instead of sb_any_quota_enabled() Jan Kara
@ 2008-10-24 22:08 ` Jan Kara
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 08/29] reiserfs: " Jan Kara
                   ` (21 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:08 UTC (permalink / raw)
  To: ocfs2-devel

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/ext4/super.c |   11 ++++-------
 1 files changed, 4 insertions(+), 7 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index dea8f13..ab8b8a1 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1206,8 +1206,7 @@ static int parse_options(char *options, struct super_block *sb,
 		case Opt_grpjquota:
 			qtype = GRPQUOTA;
 set_qf_name:
-			if ((sb_any_quota_enabled(sb) ||
-			     sb_any_quota_suspended(sb)) &&
+			if (sb_any_quota_loaded(sb) &&
 			    !sbi->s_qf_names[qtype]) {
 				printk(KERN_ERR
 				       "EXT4-fs: Cannot change journaled "
@@ -1246,8 +1245,7 @@ set_qf_name:
 		case Opt_offgrpjquota:
 			qtype = GRPQUOTA;
 clear_qf_name:
-			if ((sb_any_quota_enabled(sb) ||
-			     sb_any_quota_suspended(sb)) &&
+			if (sb_any_quota_loaded(sb) &&
 			    sbi->s_qf_names[qtype]) {
 				printk(KERN_ERR "EXT4-fs: Cannot change "
 					"journaled quota options when "
@@ -1266,8 +1264,7 @@ clear_qf_name:
 		case Opt_jqfmt_vfsv0:
 			qfmt = QFMT_VFS_V0;
 set_qf_format:
-			if ((sb_any_quota_enabled(sb) ||
-			     sb_any_quota_suspended(sb)) &&
+			if (sb_any_quota_loaded(sb) &&
 			    sbi->s_jquota_fmt != qfmt) {
 				printk(KERN_ERR "EXT4-fs: Cannot change "
 					"journaled quota options when "
@@ -1286,7 +1283,7 @@ set_qf_format:
 			set_opt(sbi->s_mount_opt, GRPQUOTA);
 			break;
 		case Opt_noquota:
-			if (sb_any_quota_enabled(sb)) {
+			if (sb_any_quota_loaded(sb)) {
 				printk(KERN_ERR "EXT4-fs: Cannot change quota "
 					"options when quota turned on.\n");
 				return 0;
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 08/29] reiserfs: Use sb_any_quota_loaded() instead of sb_any_quota_enabled().
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (6 preceding siblings ...)
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 07/29] ext4: " Jan Kara
@ 2008-10-24 22:08 ` Jan Kara
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 09/29] quota: Remove compatibility function sb_any_quota_enabled() Jan Kara
                   ` (20 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:08 UTC (permalink / raw)
  To: ocfs2-devel

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/reiserfs/super.c |    8 +++-----
 1 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c
index d318c7e..6bf9de4 100644
--- a/fs/reiserfs/super.c
+++ b/fs/reiserfs/super.c
@@ -994,8 +994,7 @@ static int reiserfs_parse_options(struct super_block *s, char *options,	/* strin
 		if (c == 'u' || c == 'g') {
 			int qtype = c == 'u' ? USRQUOTA : GRPQUOTA;
 
-			if ((sb_any_quota_enabled(s) ||
-			     sb_any_quota_suspended(s)) &&
+			if (sb_any_quota_loaded(s) &&
 			    (!*arg != !REISERFS_SB(s)->s_qf_names[qtype])) {
 				reiserfs_warning(s,
 						 "reiserfs_parse_options: cannot change journaled quota options when quota turned on.");
@@ -1041,8 +1040,7 @@ static int reiserfs_parse_options(struct super_block *s, char *options,	/* strin
 						 "reiserfs_parse_options: unknown quota format specified.");
 				return 0;
 			}
-			if ((sb_any_quota_enabled(s) ||
-			     sb_any_quota_suspended(s)) &&
+			if (sb_any_quota_loaded(s) &&
 			    *qfmt != REISERFS_SB(s)->s_jquota_fmt) {
 				reiserfs_warning(s,
 						 "reiserfs_parse_options: cannot change journaled quota options when quota turned on.");
@@ -1067,7 +1065,7 @@ static int reiserfs_parse_options(struct super_block *s, char *options,	/* strin
 	}
 	/* This checking is not precise wrt the quota type but for our purposes it is sufficient */
 	if (!(*mount_options & (1 << REISERFS_QUOTA))
-	    && sb_any_quota_enabled(s)) {
+	    && sb_any_quota_loaded(s)) {
 		reiserfs_warning(s,
 				 "reiserfs_parse_options: quota options must be present when quota is turned on.");
 		return 0;
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 09/29] quota: Remove compatibility function sb_any_quota_enabled()
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (7 preceding siblings ...)
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 08/29] reiserfs: " Jan Kara
@ 2008-10-24 22:08 ` Jan Kara
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 10/29] quota: Introduce DQUOT_QUOTA_SYS_FILE flag Jan Kara
                   ` (19 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:08 UTC (permalink / raw)
  To: ocfs2-devel

Signed-off-by: Jan Kara <jack@suse.cz>
---
 include/linux/quotaops.h |    6 ------
 1 files changed, 0 insertions(+), 6 deletions(-)

diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h
index f7dcc30..94f00ec 100644
--- a/include/linux/quotaops.h
+++ b/include/linux/quotaops.h
@@ -119,9 +119,6 @@ static inline int sb_any_quota_active(struct super_block *sb)
 	       sb_has_quota_active(sb, GRPQUOTA);
 }
 
-/* For backward compatibility until we remove all users */
-#define sb_any_quota_enabled(sb) sb_any_quota_active(sb)
-
 /*
  * Operations supported for diskquotas.
  */
@@ -265,9 +262,6 @@ static inline int sb_any_quota_active(struct super_block *sb)
 	return 0;
 }
 
-/* For backward compatibility until we remove all users */
-#define sb_any_quota_enabled(sb) sb_any_quota_active(sb)
-
 /*
  * NO-OP when quota not configured.
  */
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 10/29] quota: Introduce DQUOT_QUOTA_SYS_FILE flag
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (8 preceding siblings ...)
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 09/29] quota: Remove compatibility function sb_any_quota_enabled() Jan Kara
@ 2008-10-24 22:08 ` Jan Kara
  2008-10-29 23:09   ` Mark Fasheh
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 11/29] quota: Move quotaio_v[12].h from include/linux/ to fs/ Jan Kara
                   ` (18 subsequent siblings)
  28 siblings, 1 reply; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:08 UTC (permalink / raw)
  To: ocfs2-devel

If filesystem can handle quota files as system files hidden from users, we can
skip a lot of cache invalidation, syncing, inode flags setting etc. when
turning quotas on, off and quota_sync. Allow filesystem to indicate that it is
hiding quota files from users by DQUOT_QUOTA_SYS_FILE flag.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/dquot.c            |   45 ++++++++++++++++++++++++++++++---------------
 fs/quota.c            |    3 +++
 include/linux/quota.h |    7 +++++++
 3 files changed, 40 insertions(+), 15 deletions(-)

diff --git a/fs/dquot.c b/fs/dquot.c
index 96ed45b..5b82722 100644
--- a/fs/dquot.c
+++ b/fs/dquot.c
@@ -1627,6 +1627,11 @@ int vfs_quota_disable(struct super_block *sb, int type, unsigned int flags)
 		dqopt->ops[cnt] = NULL;
 	}
 	mutex_unlock(&dqopt->dqonoff_mutex);
+
+	/* Skip syncing and setting flags if quota files are hidden */
+	if (dqopt->flags & DQUOT_QUOTA_SYS_FILE)
+		goto put_inodes;
+
 	/* Sync the superblock so that buffers with quota data are written to
 	 * disk (and so userspace sees correct data afterwards). */
 	if (sb->s_op->sync_fs)
@@ -1651,6 +1656,12 @@ int vfs_quota_disable(struct super_block *sb, int type, unsigned int flags)
 				mark_inode_dirty(toputinode[cnt]);
 			}
 			mutex_unlock(&dqopt->dqonoff_mutex);
+		}
+	if (sb->s_bdev)
+		invalidate_bdev(sb->s_bdev);
+put_inodes:
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+		if (toputinode[cnt]) {
 			/* On remount RO, we keep the inode pointer so that we
 			 * can reenable quota on the subsequent remount RW. We
 			 * have to check 'flags' variable and not use sb_has_
@@ -1663,8 +1674,6 @@ int vfs_quota_disable(struct super_block *sb, int type, unsigned int flags)
 			else if (!toputinode[cnt]->i_nlink)
 				ret = -EBUSY;
 		}
-	if (sb->s_bdev)
-		invalidate_bdev(sb->s_bdev);
 	return ret;
 }
 
@@ -1711,25 +1720,31 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id,
 		goto out_fmt;
 	}
 
-	/* As we bypass the pagecache we must now flush the inode so that
-	 * we see all the changes from userspace... */
-	write_inode_now(inode, 1);
-	/* And now flush the block cache so that kernel sees the changes */
-	invalidate_bdev(sb->s_bdev);
+	if (!(dqopt->flags & DQUOT_QUOTA_SYS_FILE)) {
+		/* As we bypass the pagecache we must now flush the inode so
+		 * that we see all the changes from userspace... */
+		write_inode_now(inode, 1);
+		/* And now flush the block cache so that kernel sees the
+		 * changes */
+		invalidate_bdev(sb->s_bdev);
+	}
 	mutex_lock(&inode->i_mutex);
 	mutex_lock(&dqopt->dqonoff_mutex);
 	if (sb_has_quota_loaded(sb, type)) {
 		error = -EBUSY;
 		goto out_lock;
 	}
-	/* We don't want quota and atime on quota files (deadlocks possible)
-	 * Also nobody should write to the file - we use special IO operations
-	 * which ignore the immutable bit. */
-	down_write(&dqopt->dqptr_sem);
-	oldflags = inode->i_flags & (S_NOATIME | S_IMMUTABLE | S_NOQUOTA);
-	inode->i_flags |= S_NOQUOTA | S_NOATIME | S_IMMUTABLE;
-	up_write(&dqopt->dqptr_sem);
-	sb->dq_op->drop(inode);
+
+	if (!(dqopt->flags & DQUOT_QUOTA_SYS_FILE)) {
+		/* We don't want quota and atime on quota files (deadlocks
+		 * possible) Also nobody should write to the file - we use
+		 * special IO operations which ignore the immutable bit. */
+		down_write(&dqopt->dqptr_sem);
+		oldflags = inode->i_flags & (S_NOATIME | S_IMMUTABLE | S_NOQUOTA);
+		inode->i_flags |= S_NOQUOTA | S_NOATIME | S_IMMUTABLE;
+		up_write(&dqopt->dqptr_sem);
+		sb->dq_op->drop(inode);
+	}
 
 	error = -EIO;
 	dqopt->files[type] = igrab(inode);
diff --git a/fs/quota.c b/fs/quota.c
index a8026f1..2c6ea78 100644
--- a/fs/quota.c
+++ b/fs/quota.c
@@ -160,6 +160,9 @@ static void quota_sync_sb(struct super_block *sb, int type)
 	int cnt;
 
 	sb->s_qcop->quota_sync(sb, type);
+
+	if (sb_dqopt(sb)->flags & DQUOT_QUOTA_SYS_FILE)
+		return;
 	/* This is not very clever (and fast) but currently I don't know about
 	 * any other simple way of getting quota data to disk and we must get
 	 * them there for userspace to be visible... */
diff --git a/include/linux/quota.h b/include/linux/quota.h
index 0e4b550..8dd5333 100644
--- a/include/linux/quota.h
+++ b/include/linux/quota.h
@@ -334,6 +334,13 @@ enum {
 #define DQUOT_SUSPENDED		(1 << _DQUOT_SUSPENDED)
 #define DQUOT_STATE_FLAGS	(DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED | \
 				 DQUOT_SUSPENDED)
+/* Other quota flags */
+#define DQUOT_QUOTA_SYS_FILE	(1 << 6)	/* Quota file is a special
+						 * system file and user cannot
+						 * touch it. Filesystem is
+						 * responsible for setting
+						 * S_NOQUOTA, S_NOATIME flags
+						 */
 
 static inline unsigned int dquot_state_flag(unsigned int flags, int type)
 {
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 11/29] quota: Move quotaio_v[12].h from include/linux/ to fs/
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (9 preceding siblings ...)
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 10/29] quota: Introduce DQUOT_QUOTA_SYS_FILE flag Jan Kara
@ 2008-10-24 22:08 ` Jan Kara
  2008-10-29 23:10   ` Mark Fasheh
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 12/29] quota: Split off quota tree handling into a separate file Jan Kara
                   ` (17 subsequent siblings)
  28 siblings, 1 reply; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:08 UTC (permalink / raw)
  To: ocfs2-devel

Since these include files are used only by implementation of quota formats,
there's no need to have them in include/linux/.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/quota_v1.c              |    3 +-
 fs/quota_v2.c              |    7 ++--
 fs/quotaio_v1.h            |   33 ++++++++++++++++++
 fs/quotaio_v2.h            |   79 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/quotaio_v1.h |   33 ------------------
 include/linux/quotaio_v2.h |   79 --------------------------------------------
 6 files changed, 118 insertions(+), 116 deletions(-)
 create mode 100644 fs/quotaio_v1.h
 create mode 100644 fs/quotaio_v2.h
 delete mode 100644 include/linux/quotaio_v1.h
 delete mode 100644 include/linux/quotaio_v2.h

diff --git a/fs/quota_v1.c b/fs/quota_v1.c
index 3e078ee..b4af1c6 100644
--- a/fs/quota_v1.c
+++ b/fs/quota_v1.c
@@ -3,13 +3,14 @@
 #include <linux/quota.h>
 #include <linux/quotaops.h>
 #include <linux/dqblk_v1.h>
-#include <linux/quotaio_v1.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/module.h>
 
 #include <asm/byteorder.h>
 
+#include "quotaio_v1.h"
+
 MODULE_AUTHOR("Jan Kara");
 MODULE_DESCRIPTION("Old quota format support");
 MODULE_LICENSE("GPL");
diff --git a/fs/quota_v2.c b/fs/quota_v2.c
index 51c4717..a21d1a7 100644
--- a/fs/quota_v2.c
+++ b/fs/quota_v2.c
@@ -6,7 +6,6 @@
 #include <linux/fs.h>
 #include <linux/mount.h>
 #include <linux/dqblk_v2.h>
-#include <linux/quotaio_v2.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/module.h>
@@ -15,6 +14,8 @@
 
 #include <asm/byteorder.h>
 
+#include "quotaio_v2.h"
+
 MODULE_AUTHOR("Jan Kara");
 MODULE_DESCRIPTION("Quota format v2 support");
 MODULE_LICENSE("GPL");
@@ -129,8 +130,8 @@ static void mem2diskdqb(struct v2_disk_dqblk *d, struct mem_dqblk *m, qid_t id)
 	d->dqb_isoftlimit = cpu_to_le32(m->dqb_isoftlimit);
 	d->dqb_curinodes = cpu_to_le32(m->dqb_curinodes);
 	d->dqb_itime = cpu_to_le64(m->dqb_itime);
-	d->dqb_bhardlimit = cpu_to_le32(v2_qbtos(m->dqb_bhardlimit));
-	d->dqb_bsoftlimit = cpu_to_le32(v2_qbtos(m->dqb_bsoftlimit));
+	d->dqb_bhardlimit = cpu_to_le32(v2_stoqb(m->dqb_bhardlimit));
+	d->dqb_bsoftlimit = cpu_to_le32(v2_stoqb(m->dqb_bsoftlimit));
 	d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
 	d->dqb_btime = cpu_to_le64(m->dqb_btime);
 	d->dqb_id = cpu_to_le32(id);
diff --git a/fs/quotaio_v1.h b/fs/quotaio_v1.h
new file mode 100644
index 0000000..746654b
--- /dev/null
+++ b/fs/quotaio_v1.h
@@ -0,0 +1,33 @@
+#ifndef _LINUX_QUOTAIO_V1_H
+#define _LINUX_QUOTAIO_V1_H
+
+#include <linux/types.h>
+
+/*
+ * The following constants define the amount of time given a user
+ * before the soft limits are treated as hard limits (usually resulting
+ * in an allocation failure). The timer is started when the user crosses
+ * their soft limit, it is reset when they go below their soft limit.
+ */
+#define MAX_IQ_TIME  604800	/* (7*24*60*60) 1 week */
+#define MAX_DQ_TIME  604800	/* (7*24*60*60) 1 week */
+
+/*
+ * The following structure defines the format of the disk quota file
+ * (as it appears on disk) - the file is an array of these structures
+ * indexed by user or group number.
+ */
+struct v1_disk_dqblk {
+	__u32 dqb_bhardlimit;	/* absolute limit on disk blks alloc */
+	__u32 dqb_bsoftlimit;	/* preferred limit on disk blks */
+	__u32 dqb_curblocks;	/* current block count */
+	__u32 dqb_ihardlimit;	/* absolute limit on allocated inodes */
+	__u32 dqb_isoftlimit;	/* preferred inode limit */
+	__u32 dqb_curinodes;	/* current # allocated inodes */
+	time_t dqb_btime;	/* time limit for excessive disk use */
+	time_t dqb_itime;	/* time limit for excessive inode use */
+};
+
+#define v1_dqoff(UID)      ((loff_t)((UID) * sizeof (struct v1_disk_dqblk)))
+
+#endif	/* _LINUX_QUOTAIO_V1_H */
diff --git a/fs/quotaio_v2.h b/fs/quotaio_v2.h
new file mode 100644
index 0000000..303d7cb
--- /dev/null
+++ b/fs/quotaio_v2.h
@@ -0,0 +1,79 @@
+/*
+ *	Definitions of structures for vfsv0 quota format
+ */
+
+#ifndef _LINUX_QUOTAIO_V2_H
+#define _LINUX_QUOTAIO_V2_H
+
+#include <linux/types.h>
+#include <linux/quota.h>
+
+/*
+ * Definitions of magics and versions of current quota files
+ */
+#define V2_INITQMAGICS {\
+	0xd9c01f11,	/* USRQUOTA */\
+	0xd9c01927	/* GRPQUOTA */\
+}
+
+#define V2_INITQVERSIONS {\
+	0,		/* USRQUOTA */\
+	0		/* GRPQUOTA */\
+}
+
+/*
+ * The following structure defines the format of the disk quota file
+ * (as it appears on disk) - the file is a radix tree whose leaves point
+ * to blocks of these structures.
+ */
+struct v2_disk_dqblk {
+	__le32 dqb_id;		/* id this quota applies to */
+	__le32 dqb_ihardlimit;	/* absolute limit on allocated inodes */
+	__le32 dqb_isoftlimit;	/* preferred inode limit */
+	__le32 dqb_curinodes;	/* current # allocated inodes */
+	__le32 dqb_bhardlimit;	/* absolute limit on disk space (in QUOTABLOCK_SIZE) */
+	__le32 dqb_bsoftlimit;	/* preferred limit on disk space (in QUOTABLOCK_SIZE) */
+	__le64 dqb_curspace;	/* current space occupied (in bytes) */
+	__le64 dqb_btime;	/* time limit for excessive disk use */
+	__le64 dqb_itime;	/* time limit for excessive inode use */
+};
+
+/*
+ * Here are header structures as written on disk and their in-memory copies
+ */
+/* First generic header */
+struct v2_disk_dqheader {
+	__le32 dqh_magic;	/* Magic number identifying file */
+	__le32 dqh_version;	/* File version */
+};
+
+/* Header with type and version specific information */
+struct v2_disk_dqinfo {
+	__le32 dqi_bgrace;	/* Time before block soft limit becomes hard limit */
+	__le32 dqi_igrace;	/* Time before inode soft limit becomes hard limit */
+	__le32 dqi_flags;	/* Flags for quotafile (DQF_*) */
+	__le32 dqi_blocks;	/* Number of blocks in file */
+	__le32 dqi_free_blk;	/* Number of first free block in the list */
+	__le32 dqi_free_entry;	/* Number of block with at least one free entry */
+};
+
+/*
+ *  Structure of header of block with quota structures. It is padded to 16 bytes so
+ *  there will be space for exactly 21 quota-entries in a block
+ */
+struct v2_disk_dqdbheader {
+	__le32 dqdh_next_free;	/* Number of next block with free entry */
+	__le32 dqdh_prev_free;	/* Number of previous block with free entry */
+	__le16 dqdh_entries;	/* Number of valid entries in block */
+	__le16 dqdh_pad1;
+	__le32 dqdh_pad2;
+};
+
+#define V2_DQINFOOFF	sizeof(struct v2_disk_dqheader)	/* Offset of info header in file */
+#define V2_DQBLKSIZE_BITS	10
+#define V2_DQBLKSIZE	(1 << V2_DQBLKSIZE_BITS)	/* Size of block with quota structures */
+#define V2_DQTREEOFF	1		/* Offset of tree in file in blocks */
+#define V2_DQTREEDEPTH	4		/* Depth of quota tree */
+#define V2_DQSTRINBLK	((V2_DQBLKSIZE - sizeof(struct v2_disk_dqdbheader)) / sizeof(struct v2_disk_dqblk))	/* Number of entries in one blocks */
+
+#endif /* _LINUX_QUOTAIO_V2_H */
diff --git a/include/linux/quotaio_v1.h b/include/linux/quotaio_v1.h
deleted file mode 100644
index 746654b..0000000
--- a/include/linux/quotaio_v1.h
+++ /dev/null
@@ -1,33 +0,0 @@
-#ifndef _LINUX_QUOTAIO_V1_H
-#define _LINUX_QUOTAIO_V1_H
-
-#include <linux/types.h>
-
-/*
- * The following constants define the amount of time given a user
- * before the soft limits are treated as hard limits (usually resulting
- * in an allocation failure). The timer is started when the user crosses
- * their soft limit, it is reset when they go below their soft limit.
- */
-#define MAX_IQ_TIME  604800	/* (7*24*60*60) 1 week */
-#define MAX_DQ_TIME  604800	/* (7*24*60*60) 1 week */
-
-/*
- * The following structure defines the format of the disk quota file
- * (as it appears on disk) - the file is an array of these structures
- * indexed by user or group number.
- */
-struct v1_disk_dqblk {
-	__u32 dqb_bhardlimit;	/* absolute limit on disk blks alloc */
-	__u32 dqb_bsoftlimit;	/* preferred limit on disk blks */
-	__u32 dqb_curblocks;	/* current block count */
-	__u32 dqb_ihardlimit;	/* absolute limit on allocated inodes */
-	__u32 dqb_isoftlimit;	/* preferred inode limit */
-	__u32 dqb_curinodes;	/* current # allocated inodes */
-	time_t dqb_btime;	/* time limit for excessive disk use */
-	time_t dqb_itime;	/* time limit for excessive inode use */
-};
-
-#define v1_dqoff(UID)      ((loff_t)((UID) * sizeof (struct v1_disk_dqblk)))
-
-#endif	/* _LINUX_QUOTAIO_V1_H */
diff --git a/include/linux/quotaio_v2.h b/include/linux/quotaio_v2.h
deleted file mode 100644
index 303d7cb..0000000
--- a/include/linux/quotaio_v2.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- *	Definitions of structures for vfsv0 quota format
- */
-
-#ifndef _LINUX_QUOTAIO_V2_H
-#define _LINUX_QUOTAIO_V2_H
-
-#include <linux/types.h>
-#include <linux/quota.h>
-
-/*
- * Definitions of magics and versions of current quota files
- */
-#define V2_INITQMAGICS {\
-	0xd9c01f11,	/* USRQUOTA */\
-	0xd9c01927	/* GRPQUOTA */\
-}
-
-#define V2_INITQVERSIONS {\
-	0,		/* USRQUOTA */\
-	0		/* GRPQUOTA */\
-}
-
-/*
- * The following structure defines the format of the disk quota file
- * (as it appears on disk) - the file is a radix tree whose leaves point
- * to blocks of these structures.
- */
-struct v2_disk_dqblk {
-	__le32 dqb_id;		/* id this quota applies to */
-	__le32 dqb_ihardlimit;	/* absolute limit on allocated inodes */
-	__le32 dqb_isoftlimit;	/* preferred inode limit */
-	__le32 dqb_curinodes;	/* current # allocated inodes */
-	__le32 dqb_bhardlimit;	/* absolute limit on disk space (in QUOTABLOCK_SIZE) */
-	__le32 dqb_bsoftlimit;	/* preferred limit on disk space (in QUOTABLOCK_SIZE) */
-	__le64 dqb_curspace;	/* current space occupied (in bytes) */
-	__le64 dqb_btime;	/* time limit for excessive disk use */
-	__le64 dqb_itime;	/* time limit for excessive inode use */
-};
-
-/*
- * Here are header structures as written on disk and their in-memory copies
- */
-/* First generic header */
-struct v2_disk_dqheader {
-	__le32 dqh_magic;	/* Magic number identifying file */
-	__le32 dqh_version;	/* File version */
-};
-
-/* Header with type and version specific information */
-struct v2_disk_dqinfo {
-	__le32 dqi_bgrace;	/* Time before block soft limit becomes hard limit */
-	__le32 dqi_igrace;	/* Time before inode soft limit becomes hard limit */
-	__le32 dqi_flags;	/* Flags for quotafile (DQF_*) */
-	__le32 dqi_blocks;	/* Number of blocks in file */
-	__le32 dqi_free_blk;	/* Number of first free block in the list */
-	__le32 dqi_free_entry;	/* Number of block with at least one free entry */
-};
-
-/*
- *  Structure of header of block with quota structures. It is padded to 16 bytes so
- *  there will be space for exactly 21 quota-entries in a block
- */
-struct v2_disk_dqdbheader {
-	__le32 dqdh_next_free;	/* Number of next block with free entry */
-	__le32 dqdh_prev_free;	/* Number of previous block with free entry */
-	__le16 dqdh_entries;	/* Number of valid entries in block */
-	__le16 dqdh_pad1;
-	__le32 dqdh_pad2;
-};
-
-#define V2_DQINFOOFF	sizeof(struct v2_disk_dqheader)	/* Offset of info header in file */
-#define V2_DQBLKSIZE_BITS	10
-#define V2_DQBLKSIZE	(1 << V2_DQBLKSIZE_BITS)	/* Size of block with quota structures */
-#define V2_DQTREEOFF	1		/* Offset of tree in file in blocks */
-#define V2_DQTREEDEPTH	4		/* Depth of quota tree */
-#define V2_DQSTRINBLK	((V2_DQBLKSIZE - sizeof(struct v2_disk_dqdbheader)) / sizeof(struct v2_disk_dqblk))	/* Number of entries in one blocks */
-
-#endif /* _LINUX_QUOTAIO_V2_H */
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 12/29] quota: Split off quota tree handling into a separate file
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (10 preceding siblings ...)
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 11/29] quota: Move quotaio_v[12].h from include/linux/ to fs/ Jan Kara
@ 2008-10-24 22:08 ` Jan Kara
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 13/29] quota: Convert union in mem_dqinfo to a pointer Jan Kara
                   ` (16 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:08 UTC (permalink / raw)
  To: ocfs2-devel

There is going to be a new version of quota format having 64-bit
quota limits and a new quota format for OCFS2. They are both
going to use the same tree structure as VFSv0 quota format. So
split out tree handling into a separate file and make size of
leaf blocks, amount of space usable in each block (needed for
checksumming) and structures contained in them configurable
so that the code can be shared.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/Kconfig                  |    5 +
 fs/Makefile                 |    1 +
 fs/quota_tree.c             |  645 +++++++++++++++++++++++++++++++++++++++++++
 fs/quota_tree.h             |   25 ++
 fs/quota_v2.c               |  596 ++++------------------------------------
 fs/quotaio_v2.h             |   33 +--
 include/linux/dqblk_qtree.h |   56 ++++
 include/linux/dqblk_v2.h    |   19 +-
 8 files changed, 799 insertions(+), 581 deletions(-)
 create mode 100644 fs/quota_tree.c
 create mode 100644 fs/quota_tree.h
 create mode 100644 include/linux/dqblk_qtree.h

diff --git a/fs/Kconfig b/fs/Kconfig
index 9e9d70c..b8ce84d 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -601,6 +601,10 @@ config PRINT_QUOTA_WARNING
 	  Note that this behavior is currently deprecated and may go away in
 	  future. Please use notification via netlink socket instead.
 
+# Generic support for tree structured quota files. Seleted when needed.
+config QUOTA_TREE
+	 tristate
+
 config QFMT_V1
 	tristate "Old quota format support"
 	depends on QUOTA
@@ -612,6 +616,7 @@ config QFMT_V1
 config QFMT_V2
 	tristate "Quota format v2 support"
 	depends on QUOTA
+	select QUOTA_TREE
 	help
 	  This quota format allows using quotas with 32-bit UIDs/GIDs. If you
 	  need this functionality say Y here.
diff --git a/fs/Makefile b/fs/Makefile
index b6f27dc..a11db74 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_GENERIC_ACL)	+= generic_acl.o
 obj-$(CONFIG_QUOTA)		+= dquot.o
 obj-$(CONFIG_QFMT_V1)		+= quota_v1.o
 obj-$(CONFIG_QFMT_V2)		+= quota_v2.o
+obj-$(CONFIG_QUOTA_TREE)	+= quota_tree.o
 obj-$(CONFIG_QUOTACTL)		+= quota.o
 
 obj-$(CONFIG_DNOTIFY)		+= dnotify.o
diff --git a/fs/quota_tree.c b/fs/quota_tree.c
new file mode 100644
index 0000000..953404c
--- /dev/null
+++ b/fs/quota_tree.c
@@ -0,0 +1,645 @@
+/*
+ *	vfsv0 quota IO operations on file
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/dqblk_v2.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/quotaops.h>
+
+#include <asm/byteorder.h>
+
+#include "quota_tree.h"
+
+MODULE_AUTHOR("Jan Kara");
+MODULE_DESCRIPTION("Quota trie support");
+MODULE_LICENSE("GPL");
+
+#define __QUOTA_QT_PARANOIA
+
+typedef char *dqbuf_t;
+
+static int get_index(struct qtree_mem_dqinfo *info, qid_t id, int depth)
+{
+	unsigned int epb = info->dqi_usable_bs >> 2;
+
+	depth = info->dqi_qtree_depth - depth - 1;
+	while (depth--)
+		id /= epb;
+	return id % epb;
+}
+
+/* Number of entries in one blocks */
+static inline int qtree_dqstr_in_blk(struct qtree_mem_dqinfo *info)
+{
+	return (info->dqi_usable_bs - sizeof(struct qt_disk_dqdbheader))
+	       / info->dqi_entry_size;
+}
+
+static dqbuf_t getdqbuf(size_t size)
+{
+	dqbuf_t buf = kmalloc(size, GFP_NOFS);
+	if (!buf)
+		printk(KERN_WARNING "VFS: Not enough memory for quota buffers.\n");
+	return buf;
+}
+
+static inline void freedqbuf(dqbuf_t buf)
+{
+	kfree(buf);
+}
+
+static inline ssize_t read_blk(struct qtree_mem_dqinfo *info, uint blk, dqbuf_t buf)
+{
+	struct super_block *sb = info->dqi_sb;
+
+	memset(buf, 0, info->dqi_usable_bs);
+	return sb->s_op->quota_read(sb, info->dqi_type, (char *)buf,
+	       info->dqi_usable_bs, blk << info->dqi_blocksize_bits);
+}
+
+static inline ssize_t write_blk(struct qtree_mem_dqinfo *info, uint blk, dqbuf_t buf)
+{
+	struct super_block *sb = info->dqi_sb;
+
+	return sb->s_op->quota_write(sb, info->dqi_type, (char *)buf,
+	       info->dqi_usable_bs, blk << info->dqi_blocksize_bits);
+}
+
+/* Remove empty block from list and return it */
+static int get_free_dqblk(struct qtree_mem_dqinfo *info)
+{
+	dqbuf_t buf = getdqbuf(info->dqi_usable_bs);
+	struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
+	int ret, blk;
+
+	if (!buf)
+		return -ENOMEM;
+	if (info->dqi_free_blk) {
+		blk = info->dqi_free_blk;
+		ret = read_blk(info, blk, buf);
+		if (ret < 0)
+			goto out_buf;
+		info->dqi_free_blk = le32_to_cpu(dh->dqdh_next_free);
+	}
+	else {
+		memset(buf, 0, info->dqi_usable_bs);
+		/* Assure block allocation... */
+		ret = write_blk(info, info->dqi_blocks, buf);
+		if (ret < 0)
+			goto out_buf;
+		blk = info->dqi_blocks++;
+	}
+	mark_info_dirty(info->dqi_sb, info->dqi_type);
+	ret = blk;
+out_buf:
+	freedqbuf(buf);
+	return ret;
+}
+
+/* Insert empty block to the list */
+static int put_free_dqblk(struct qtree_mem_dqinfo *info, dqbuf_t buf, uint blk)
+{
+	struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
+	int err;
+
+	dh->dqdh_next_free = cpu_to_le32(info->dqi_free_blk);
+	dh->dqdh_prev_free = cpu_to_le32(0);
+	dh->dqdh_entries = cpu_to_le16(0);
+	err = write_blk(info, blk, buf);
+	if (err < 0)
+		return err;
+	info->dqi_free_blk = blk;
+	mark_info_dirty(info->dqi_sb, info->dqi_type);
+	return 0;
+}
+
+/* Remove given block from the list of blocks with free entries */
+static int remove_free_dqentry(struct qtree_mem_dqinfo *info, dqbuf_t buf, uint blk)
+{
+	dqbuf_t tmpbuf = getdqbuf(info->dqi_usable_bs);
+	struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
+	uint nextblk = le32_to_cpu(dh->dqdh_next_free);
+	uint prevblk = le32_to_cpu(dh->dqdh_prev_free);
+	int err;
+
+	if (!tmpbuf)
+		return -ENOMEM;
+	if (nextblk) {
+		err = read_blk(info, nextblk, tmpbuf);
+		if (err < 0)
+			goto out_buf;
+		((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free =
+							dh->dqdh_prev_free;
+		err = write_blk(info, nextblk, tmpbuf);
+		if (err < 0)
+			goto out_buf;
+	}
+	if (prevblk) {
+		err = read_blk(info, prevblk, tmpbuf);
+		if (err < 0)
+			goto out_buf;
+		((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_next_free =
+							dh->dqdh_next_free;
+		err = write_blk(info, prevblk, tmpbuf);
+		if (err < 0)
+			goto out_buf;
+	} else {
+		info->dqi_free_entry = nextblk;
+		mark_info_dirty(info->dqi_sb, info->dqi_type);
+	}
+	freedqbuf(tmpbuf);
+	dh->dqdh_next_free = dh->dqdh_prev_free = cpu_to_le32(0);
+	/* No matter whether write succeeds block is out of list */
+	if (write_blk(info, blk, buf) < 0)
+		printk(KERN_ERR "VFS: Can't write block (%u) with free entries.\n", blk);
+	return 0;
+out_buf:
+	freedqbuf(tmpbuf);
+	return err;
+}
+
+/* Insert given block to the beginning of list with free entries */
+static int insert_free_dqentry(struct qtree_mem_dqinfo *info, dqbuf_t buf, uint blk)
+{
+	dqbuf_t tmpbuf = getdqbuf(info->dqi_usable_bs);
+	struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
+	int err;
+
+	if (!tmpbuf)
+		return -ENOMEM;
+	dh->dqdh_next_free = cpu_to_le32(info->dqi_free_entry);
+	dh->dqdh_prev_free = cpu_to_le32(0);
+	err = write_blk(info, blk, buf);
+	if (err < 0)
+		goto out_buf;
+	if (info->dqi_free_entry) {
+		err = read_blk(info, info->dqi_free_entry, tmpbuf);
+		if (err < 0)
+			goto out_buf;
+		((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free =
+							cpu_to_le32(blk);
+		err = write_blk(info, info->dqi_free_entry, tmpbuf);
+		if (err < 0)
+			goto out_buf;
+	}
+	freedqbuf(tmpbuf);
+	info->dqi_free_entry = blk;
+	mark_info_dirty(info->dqi_sb, info->dqi_type);
+	return 0;
+out_buf:
+	freedqbuf(tmpbuf);
+	return err;
+}
+
+/* Is the entry in the block free? */
+int qtree_entry_unused(struct qtree_mem_dqinfo *info, char *disk)
+{
+	int i;
+
+	for (i = 0; i < info->dqi_entry_size; i++)
+		if (disk[i])
+			return 0;
+	return 1;
+}
+EXPORT_SYMBOL(qtree_entry_unused);
+
+/* Find space for dquot */
+static uint find_free_dqentry(struct qtree_mem_dqinfo *info,
+			      struct dquot *dquot, int *err)
+{
+	uint blk, i;
+	struct qt_disk_dqdbheader *dh;
+	dqbuf_t buf = getdqbuf(info->dqi_usable_bs);
+	char *ddquot;
+
+	*err = 0;
+	if (!buf) {
+		*err = -ENOMEM;
+		return 0;
+	}
+	dh = (struct qt_disk_dqdbheader *)buf;
+	if (info->dqi_free_entry) {
+		blk = info->dqi_free_entry;
+		*err = read_blk(info, blk, buf);
+		if (*err < 0)
+			goto out_buf;
+	} else {
+		blk = get_free_dqblk(info);
+		if ((int)blk < 0) {
+			*err = blk;
+			freedqbuf(buf);
+			return 0;
+		}
+		memset(buf, 0, info->dqi_usable_bs);
+		/* This is enough as block is already zeroed and entry list is empty... */
+		info->dqi_free_entry = blk;
+		mark_info_dirty(dquot->dq_sb, dquot->dq_type);
+	}
+	/* Block will be full? */
+	if (le16_to_cpu(dh->dqdh_entries) + 1 >= qtree_dqstr_in_blk(info)) {
+		*err = remove_free_dqentry(info, buf, blk);
+		if (*err < 0) {
+			printk(KERN_ERR "VFS: find_free_dqentry(): Can't "
+			       "remove block (%u) from entry free list.\n",
+			       blk);
+			goto out_buf;
+		}
+	}
+	le16_add_cpu(&dh->dqdh_entries, 1);
+	/* Find free structure in block */
+	for (i = 0, ddquot = ((char *)buf) + sizeof(struct qt_disk_dqdbheader);
+	     i < qtree_dqstr_in_blk(info) && !qtree_entry_unused(info, ddquot);
+	     i++, ddquot += info->dqi_entry_size);
+#ifdef __QUOTA_QT_PARANOIA
+	if (i == qtree_dqstr_in_blk(info)) {
+		printk(KERN_ERR "VFS: find_free_dqentry(): Data block full "
+				"but it shouldn't.\n");
+		*err = -EIO;
+		goto out_buf;
+	}
+#endif
+	*err = write_blk(info, blk, buf);
+	if (*err < 0) {
+		printk(KERN_ERR "VFS: find_free_dqentry(): Can't write quota "
+				"data block %u.\n", blk);
+		goto out_buf;
+	}
+	dquot->dq_off = (blk << info->dqi_blocksize_bits) +
+			sizeof(struct qt_disk_dqdbheader) +
+			i * info->dqi_entry_size;
+	freedqbuf(buf);
+	return blk;
+out_buf:
+	freedqbuf(buf);
+	return 0;
+}
+
+/* Insert reference to structure into the trie */
+static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
+			  uint *treeblk, int depth)
+{
+	dqbuf_t buf = getdqbuf(info->dqi_usable_bs);
+	int ret = 0, newson = 0, newact = 0;
+	__le32 *ref;
+	uint newblk;
+
+	if (!buf)
+		return -ENOMEM;
+	if (!*treeblk) {
+		ret = get_free_dqblk(info);
+		if (ret < 0)
+			goto out_buf;
+		*treeblk = ret;
+		memset(buf, 0, info->dqi_usable_bs);
+		newact = 1;
+	} else {
+		ret = read_blk(info, *treeblk, buf);
+		if (ret < 0) {
+			printk(KERN_ERR "VFS: Can't read tree quota block "
+					"%u.\n", *treeblk);
+			goto out_buf;
+		}
+	}
+	ref = (__le32 *)buf;
+	newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]);
+	if (!newblk)
+		newson = 1;
+	if (depth == info->dqi_qtree_depth - 1) {
+#ifdef __QUOTA_QT_PARANOIA
+		if (newblk) {
+			printk(KERN_ERR "VFS: Inserting already present quota "
+					"entry (block %u).\n",
+			       le32_to_cpu(ref[get_index(info,
+						dquot->dq_id, depth)]));
+			ret = -EIO;
+			goto out_buf;
+		}
+#endif
+		newblk = find_free_dqentry(info, dquot, &ret);
+	} else {
+		ret = do_insert_tree(info, dquot, &newblk, depth+1);
+	}
+	if (newson && ret >= 0) {
+		ref[get_index(info, dquot->dq_id, depth)] =
+							cpu_to_le32(newblk);
+		ret = write_blk(info, *treeblk, buf);
+	} else if (newact && ret < 0) {
+		put_free_dqblk(info, buf, *treeblk);
+	}
+out_buf:
+	freedqbuf(buf);
+	return ret;
+}
+
+/* Wrapper for inserting quota structure into tree */
+static inline int dq_insert_tree(struct qtree_mem_dqinfo *info,
+				 struct dquot *dquot)
+{
+	int tmp = QT_TREEOFF;
+	return do_insert_tree(info, dquot, &tmp, 0);
+}
+
+/*
+ *	We don't have to be afraid of deadlocks as we never have quotas on quota files...
+ */
+int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
+{
+	int type = dquot->dq_type;
+	struct super_block *sb = dquot->dq_sb;
+	ssize_t ret;
+	dqbuf_t ddquot = getdqbuf(info->dqi_entry_size);
+
+	if (!ddquot)
+		return -ENOMEM;
+
+	/* dq_off is guarded by dqio_mutex */
+	if (!dquot->dq_off) {
+		ret = dq_insert_tree(info, dquot);
+		if (ret < 0) {
+			printk(KERN_ERR "VFS: Error %zd occurred while "
+					"creating quota.\n", ret);
+			freedqbuf(ddquot);
+			return ret;
+		}
+	}
+	spin_lock(&dq_data_lock);
+	info->dqi_ops->mem2disk_dqblk(ddquot, dquot);
+	spin_unlock(&dq_data_lock);
+	ret = sb->s_op->quota_write(sb, type, (char *)ddquot,
+					info->dqi_entry_size, dquot->dq_off);
+	if (ret != info->dqi_entry_size) {
+		printk(KERN_WARNING "VFS: dquota write failed on dev %s\n",
+		       sb->s_id);
+		if (ret >= 0)
+			ret = -ENOSPC;
+	} else {
+		ret = 0;
+	}
+	dqstats.writes++;
+	freedqbuf(ddquot);
+
+	return ret;
+}
+EXPORT_SYMBOL(qtree_write_dquot);
+
+/* Free dquot entry in data block */
+static int free_dqentry(struct qtree_mem_dqinfo *info, struct dquot *dquot,
+			uint blk)
+{
+	struct qt_disk_dqdbheader *dh;
+	dqbuf_t buf = getdqbuf(info->dqi_usable_bs);
+	int ret = 0;
+
+	if (!buf)
+		return -ENOMEM;
+	if (dquot->dq_off >> info->dqi_blocksize_bits != blk) {
+		printk(KERN_ERR "VFS: Quota structure has offset to other "
+		  "block (%u) than it should (%u).\n", blk,
+		  (uint)(dquot->dq_off >> info->dqi_blocksize_bits));
+		goto out_buf;
+	}
+	ret = read_blk(info, blk, buf);
+	if (ret < 0) {
+		printk(KERN_ERR "VFS: Can't read quota data block %u\n", blk);
+		goto out_buf;
+	}
+	dh = (struct qt_disk_dqdbheader *)buf;
+	le16_add_cpu(&dh->dqdh_entries, -1);
+	if (!le16_to_cpu(dh->dqdh_entries)) {	/* Block got free? */
+		ret = remove_free_dqentry(info, buf, blk);
+		if (ret >= 0)
+			ret = put_free_dqblk(info, buf, blk);
+		if (ret < 0) {
+			printk(KERN_ERR "VFS: Can't move quota data block (%u) "
+			  "to free list.\n", blk);
+			goto out_buf;
+		}
+	} else {
+		memset(buf +
+		       (dquot->dq_off & ((1 << info->dqi_blocksize_bits) - 1)),
+		       0, info->dqi_entry_size);
+		if (le16_to_cpu(dh->dqdh_entries) ==
+		    qtree_dqstr_in_blk(info) - 1) {
+			/* Insert will write block itself */
+			ret = insert_free_dqentry(info, buf, blk);
+			if (ret < 0) {
+				printk(KERN_ERR "VFS: Can't insert quota data "
+				       "block (%u) to free entry list.\n", blk);
+				goto out_buf;
+			}
+		} else {
+			ret = write_blk(info, blk, buf);
+			if (ret < 0) {
+				printk(KERN_ERR "VFS: Can't write quota data "
+				  "block %u\n", blk);
+				goto out_buf;
+			}
+		}
+	}
+	dquot->dq_off = 0;	/* Quota is now unattached */
+out_buf:
+	freedqbuf(buf);
+	return ret;
+}
+
+/* Remove reference to dquot from tree */
+static int remove_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
+		       uint *blk, int depth)
+{
+	dqbuf_t buf = getdqbuf(info->dqi_usable_bs);
+	int ret = 0;
+	uint newblk;
+	__le32 *ref = (__le32 *)buf;
+
+	if (!buf)
+		return -ENOMEM;
+	ret = read_blk(info, *blk, buf);
+	if (ret < 0) {
+		printk(KERN_ERR "VFS: Can't read quota data block %u\n", *blk);
+		goto out_buf;
+	}
+	newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]);
+	if (depth == info->dqi_qtree_depth - 1) {
+		ret = free_dqentry(info, dquot, newblk);
+		newblk = 0;
+	} else {
+		ret = remove_tree(info, dquot, &newblk, depth+1);
+	}
+	if (ret >= 0 && !newblk) {
+		int i;
+		ref[get_index(info, dquot->dq_id, depth)] = cpu_to_le32(0);
+		/* Block got empty? */
+		for (i = 0;
+		     i < (info->dqi_usable_bs >> 2) && !ref[i];
+		     i++);
+		/* Don't put the root block into the free block list */
+		if (i == (info->dqi_usable_bs >> 2)
+		    && *blk != QT_TREEOFF) {
+			put_free_dqblk(info, buf, *blk);
+			*blk = 0;
+		} else {
+			ret = write_blk(info, *blk, buf);
+			if (ret < 0)
+				printk(KERN_ERR "VFS: Can't write quota tree "
+				  "block %u.\n", *blk);
+		}
+	}
+out_buf:
+	freedqbuf(buf);
+	return ret;
+}
+
+/* Delete dquot from tree */
+int qtree_delete_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
+{
+	uint tmp = QT_TREEOFF;
+
+	if (!dquot->dq_off)	/* Even not allocated? */
+		return 0;
+	return remove_tree(info, dquot, &tmp, 0);
+}
+EXPORT_SYMBOL(qtree_delete_dquot);
+
+/* Find entry in block */
+static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info,
+				 struct dquot *dquot, uint blk)
+{
+	dqbuf_t buf = getdqbuf(info->dqi_usable_bs);
+	loff_t ret = 0;
+	int i;
+	char *ddquot;
+
+	if (!buf)
+		return -ENOMEM;
+	ret = read_blk(info, blk, buf);
+	if (ret < 0) {
+		printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk);
+		goto out_buf;
+	}
+	for (i = 0, ddquot = ((char *)buf) + sizeof(struct qt_disk_dqdbheader);
+	     i < qtree_dqstr_in_blk(info) && !info->dqi_ops->is_id(ddquot, dquot);
+	     i++, ddquot += info->dqi_entry_size);
+	if (i == qtree_dqstr_in_blk(info)) {
+		printk(KERN_ERR "VFS: Quota for id %u referenced "
+		  "but not present.\n", dquot->dq_id);
+		ret = -EIO;
+		goto out_buf;
+	} else {
+		ret = (blk << info->dqi_blocksize_bits) + sizeof(struct
+		  qt_disk_dqdbheader) + i * info->dqi_entry_size;
+	}
+out_buf:
+	freedqbuf(buf);
+	return ret;
+}
+
+/* Find entry for given id in the tree */
+static loff_t find_tree_dqentry(struct qtree_mem_dqinfo *info,
+				struct dquot *dquot, uint blk, int depth)
+{
+	dqbuf_t buf = getdqbuf(info->dqi_usable_bs);
+	loff_t ret = 0;
+	__le32 *ref = (__le32 *)buf;
+
+	if (!buf)
+		return -ENOMEM;
+	ret = read_blk(info, blk, buf);
+	if (ret < 0) {
+		printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk);
+		goto out_buf;
+	}
+	ret = 0;
+	blk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]);
+	if (!blk)	/* No reference? */
+		goto out_buf;
+	if (depth < info->dqi_qtree_depth - 1)
+		ret = find_tree_dqentry(info, dquot, blk, depth+1);
+	else
+		ret = find_block_dqentry(info, dquot, blk);
+out_buf:
+	freedqbuf(buf);
+	return ret;
+}
+
+/* Find entry for given id in the tree - wrapper function */
+static inline loff_t find_dqentry(struct qtree_mem_dqinfo *info,
+				  struct dquot *dquot)
+{
+	return find_tree_dqentry(info, dquot, QT_TREEOFF, 0);
+}
+
+int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
+{
+	int type = dquot->dq_type;
+	struct super_block *sb = dquot->dq_sb;
+	loff_t offset;
+	dqbuf_t ddquot;
+	int ret = 0;
+
+#ifdef __QUOTA_QT_PARANOIA
+	/* Invalidated quota? */
+	if (!sb_dqopt(dquot->dq_sb)->files[type]) {
+		printk(KERN_ERR "VFS: Quota invalidated while reading!\n");
+		return -EIO;
+	}
+#endif
+	/* Do we know offset of the dquot entry in the quota file? */
+	if (!dquot->dq_off) {
+		offset = find_dqentry(info, dquot);
+		if (offset <= 0) {	/* Entry not present? */
+			if (offset < 0)
+				printk(KERN_ERR "VFS: Can't read quota "
+				  "structure for id %u.\n", dquot->dq_id);
+			dquot->dq_off = 0;
+			set_bit(DQ_FAKE_B, &dquot->dq_flags);
+			memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
+			ret = offset;
+			goto out;
+		}
+		dquot->dq_off = offset;
+	}
+	ddquot = getdqbuf(info->dqi_entry_size);
+	if (!ddquot)
+		return -ENOMEM;
+	ret = sb->s_op->quota_read(sb, type, (char *)ddquot,
+				   info->dqi_entry_size, dquot->dq_off);
+	if (ret != info->dqi_entry_size) {
+		if (ret >= 0)
+			ret = -EIO;
+		printk(KERN_ERR "VFS: Error while reading quota "
+				"structure for id %u.\n", dquot->dq_id);
+		set_bit(DQ_FAKE_B, &dquot->dq_flags);
+		memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
+		freedqbuf(ddquot);
+		goto out;
+	}
+	spin_lock(&dq_data_lock);
+	info->dqi_ops->disk2mem_dqblk(dquot, ddquot);
+	if (!dquot->dq_dqb.dqb_bhardlimit &&
+	    !dquot->dq_dqb.dqb_bsoftlimit &&
+	    !dquot->dq_dqb.dqb_ihardlimit &&
+	    !dquot->dq_dqb.dqb_isoftlimit)
+		set_bit(DQ_FAKE_B, &dquot->dq_flags);
+	spin_unlock(&dq_data_lock);
+	freedqbuf(ddquot);
+out:
+	dqstats.reads++;
+	return ret;
+}
+EXPORT_SYMBOL(qtree_read_dquot);
+
+/* Check whether dquot should not be deleted. We know we are
+ * the only one operating on dquot (thanks to dq_lock) */
+int qtree_release_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
+{
+	if (test_bit(DQ_FAKE_B, &dquot->dq_flags) && !(dquot->dq_dqb.dqb_curinodes | dquot->dq_dqb.dqb_curspace))
+		return qtree_delete_dquot(info, dquot);
+	return 0;
+}
+EXPORT_SYMBOL(qtree_release_dquot);
diff --git a/fs/quota_tree.h b/fs/quota_tree.h
new file mode 100644
index 0000000..a1ab8db
--- /dev/null
+++ b/fs/quota_tree.h
@@ -0,0 +1,25 @@
+/*
+ *	Definitions of structures for vfsv0 quota format
+ */
+
+#ifndef _LINUX_QUOTA_TREE_H
+#define _LINUX_QUOTA_TREE_H
+
+#include <linux/types.h>
+#include <linux/quota.h>
+
+/*
+ *  Structure of header of block with quota structures. It is padded to 16 bytes so
+ *  there will be space for exactly 21 quota-entries in a block
+ */
+struct qt_disk_dqdbheader {
+	__le32 dqdh_next_free;	/* Number of next block with free entry */
+	__le32 dqdh_prev_free;	/* Number of previous block with free entry */
+	__le16 dqdh_entries;	/* Number of valid entries in block */
+	__le16 dqdh_pad1;
+	__le32 dqdh_pad2;
+};
+
+#define QT_TREEOFF	1		/* Offset of tree in file in blocks */
+
+#endif /* _LINUX_QUOTAIO_TREE_H */
diff --git a/fs/quota_v2.c b/fs/quota_v2.c
index a21d1a7..a87f102 100644
--- a/fs/quota_v2.c
+++ b/fs/quota_v2.c
@@ -14,6 +14,7 @@
 
 #include <asm/byteorder.h>
 
+#include "quota_tree.h"
 #include "quotaio_v2.h"
 
 MODULE_AUTHOR("Jan Kara");
@@ -22,10 +23,15 @@ MODULE_LICENSE("GPL");
 
 #define __QUOTA_V2_PARANOIA
 
-typedef char *dqbuf_t;
+static void v2_mem2diskdqb(void *dp, struct dquot *dquot);
+static void v2_disk2memdqb(struct dquot *dquot, void *dp);
+static int v2_is_id(void *dp, struct dquot *dquot);
 
-#define GETIDINDEX(id, depth) (((id) >> ((V2_DQTREEDEPTH-(depth)-1)*8)) & 0xff)
-#define GETENTRIES(buf) ((struct v2_disk_dqblk *)(((char *)buf)+sizeof(struct v2_disk_dqdbheader)))
+static struct qtree_fmt_operations v2_qtree_ops = {
+	.mem2disk_dqblk = v2_mem2diskdqb,
+	.disk2mem_dqblk = v2_disk2memdqb,
+	.is_id = v2_is_id,
+};
 
 #define QUOTABLOCK_BITS 10
 #define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS)
@@ -64,7 +70,7 @@ static int v2_check_quota_file(struct super_block *sb, int type)
 static int v2_read_file_info(struct super_block *sb, int type)
 {
 	struct v2_disk_dqinfo dinfo;
-	struct mem_dqinfo *info = sb_dqopt(sb)->info+type;
+	struct mem_dqinfo *info = sb_dqinfo(sb, type);
 	ssize_t size;
 
 	size = sb->s_op->quota_read(sb, type, (char *)&dinfo,
@@ -80,9 +86,16 @@ static int v2_read_file_info(struct super_block *sb, int type)
 	info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace);
 	info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace);
 	info->dqi_flags = le32_to_cpu(dinfo.dqi_flags);
-	info->u.v2_i.dqi_blocks = le32_to_cpu(dinfo.dqi_blocks);
-	info->u.v2_i.dqi_free_blk = le32_to_cpu(dinfo.dqi_free_blk);
-	info->u.v2_i.dqi_free_entry = le32_to_cpu(dinfo.dqi_free_entry);
+	info->u.v2_i.i.dqi_sb = sb;
+	info->u.v2_i.i.dqi_type = type;
+	info->u.v2_i.i.dqi_blocks = le32_to_cpu(dinfo.dqi_blocks);
+	info->u.v2_i.i.dqi_free_blk = le32_to_cpu(dinfo.dqi_free_blk);
+	info->u.v2_i.i.dqi_free_entry = le32_to_cpu(dinfo.dqi_free_entry);
+	info->u.v2_i.i.dqi_blocksize_bits = V2_DQBLKSIZE_BITS;
+	info->u.v2_i.i.dqi_usable_bs = 1 << V2_DQBLKSIZE_BITS;
+	info->u.v2_i.i.dqi_qtree_depth = qtree_depth(&info->u.v2_i.i);
+	info->u.v2_i.i.dqi_entry_size = sizeof(struct v2_disk_dqblk);
+	info->u.v2_i.i.dqi_ops = &v2_qtree_ops;
 	return 0;
 }
 
@@ -90,7 +103,7 @@ static int v2_read_file_info(struct super_block *sb, int type)
 static int v2_write_file_info(struct super_block *sb, int type)
 {
 	struct v2_disk_dqinfo dinfo;
-	struct mem_dqinfo *info = sb_dqopt(sb)->info+type;
+	struct mem_dqinfo *info = sb_dqinfo(sb, type);
 	ssize_t size;
 
 	spin_lock(&dq_data_lock);
@@ -99,9 +112,9 @@ static int v2_write_file_info(struct super_block *sb, int type)
 	dinfo.dqi_igrace = cpu_to_le32(info->dqi_igrace);
 	dinfo.dqi_flags = cpu_to_le32(info->dqi_flags & DQF_MASK);
 	spin_unlock(&dq_data_lock);
-	dinfo.dqi_blocks = cpu_to_le32(info->u.v2_i.dqi_blocks);
-	dinfo.dqi_free_blk = cpu_to_le32(info->u.v2_i.dqi_free_blk);
-	dinfo.dqi_free_entry = cpu_to_le32(info->u.v2_i.dqi_free_entry);
+	dinfo.dqi_blocks = cpu_to_le32(info->u.v2_i.i.dqi_blocks);
+	dinfo.dqi_free_blk = cpu_to_le32(info->u.v2_i.i.dqi_free_blk);
+	dinfo.dqi_free_entry = cpu_to_le32(info->u.v2_i.i.dqi_free_entry);
 	size = sb->s_op->quota_write(sb, type, (char *)&dinfo,
 	       sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF);
 	if (size != sizeof(struct v2_disk_dqinfo)) {
@@ -112,8 +125,11 @@ static int v2_write_file_info(struct super_block *sb, int type)
 	return 0;
 }
 
-static void disk2memdqb(struct mem_dqblk *m, struct v2_disk_dqblk *d)
+static void v2_disk2memdqb(struct dquot *dquot, void *dp)
 {
+	struct v2_disk_dqblk *d = dp, empty;
+	struct mem_dqblk *m = &dquot->dq_dqb;
+
 	m->dqb_ihardlimit = le32_to_cpu(d->dqb_ihardlimit);
 	m->dqb_isoftlimit = le32_to_cpu(d->dqb_isoftlimit);
 	m->dqb_curinodes = le32_to_cpu(d->dqb_curinodes);
@@ -122,10 +138,20 @@ static void disk2memdqb(struct mem_dqblk *m, struct v2_disk_dqblk *d)
 	m->dqb_bsoftlimit = v2_qbtos(le32_to_cpu(d->dqb_bsoftlimit));
 	m->dqb_curspace = le64_to_cpu(d->dqb_curspace);
 	m->dqb_btime = le64_to_cpu(d->dqb_btime);
+	/* We need to escape back all-zero structure */
+	memset(&empty, 0, sizeof(struct v2_disk_dqblk));
+	empty.dqb_itime = cpu_to_le64(1);
+	if (!memcmp(&empty, dp, sizeof(struct v2_disk_dqblk)))
+		m->dqb_itime = 0;
 }
 
-static void mem2diskdqb(struct v2_disk_dqblk *d, struct mem_dqblk *m, qid_t id)
+static void v2_mem2diskdqb(void *dp, struct dquot *dquot)
 {
+	struct v2_disk_dqblk *d = dp;
+	struct mem_dqblk *m = &dquot->dq_dqb;
+	struct qtree_mem_dqinfo *info =
+			&sb_dqinfo(dquot->dq_sb, dquot->dq_type)->u.v2_i.i;
+
 	d->dqb_ihardlimit = cpu_to_le32(m->dqb_ihardlimit);
 	d->dqb_isoftlimit = cpu_to_le32(m->dqb_isoftlimit);
 	d->dqb_curinodes = cpu_to_le32(m->dqb_curinodes);
@@ -134,553 +160,35 @@ static void mem2diskdqb(struct v2_disk_dqblk *d, struct mem_dqblk *m, qid_t id)
 	d->dqb_bsoftlimit = cpu_to_le32(v2_stoqb(m->dqb_bsoftlimit));
 	d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
 	d->dqb_btime = cpu_to_le64(m->dqb_btime);
-	d->dqb_id = cpu_to_le32(id);
-}
-
-static dqbuf_t getdqbuf(void)
-{
-	dqbuf_t buf = kmalloc(V2_DQBLKSIZE, GFP_NOFS);
-	if (!buf)
-		printk(KERN_WARNING "VFS: Not enough memory for quota buffers.\n");
-	return buf;
-}
-
-static inline void freedqbuf(dqbuf_t buf)
-{
-	kfree(buf);
-}
-
-static inline ssize_t read_blk(struct super_block *sb, int type, uint blk, dqbuf_t buf)
-{
-	memset(buf, 0, V2_DQBLKSIZE);
-	return sb->s_op->quota_read(sb, type, (char *)buf,
-	       V2_DQBLKSIZE, blk << V2_DQBLKSIZE_BITS);
-}
-
-static inline ssize_t write_blk(struct super_block *sb, int type, uint blk, dqbuf_t buf)
-{
-	return sb->s_op->quota_write(sb, type, (char *)buf,
-	       V2_DQBLKSIZE, blk << V2_DQBLKSIZE_BITS);
-}
-
-/* Remove empty block from list and return it */
-static int get_free_dqblk(struct super_block *sb, int type)
-{
-	dqbuf_t buf = getdqbuf();
-	struct mem_dqinfo *info = sb_dqinfo(sb, type);
-	struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf;
-	int ret, blk;
-
-	if (!buf)
-		return -ENOMEM;
-	if (info->u.v2_i.dqi_free_blk) {
-		blk = info->u.v2_i.dqi_free_blk;
-		if ((ret = read_blk(sb, type, blk, buf)) < 0)
-			goto out_buf;
-		info->u.v2_i.dqi_free_blk = le32_to_cpu(dh->dqdh_next_free);
-	}
-	else {
-		memset(buf, 0, V2_DQBLKSIZE);
-		/* Assure block allocation... */
-		if ((ret = write_blk(sb, type, info->u.v2_i.dqi_blocks, buf)) < 0)
-			goto out_buf;
-		blk = info->u.v2_i.dqi_blocks++;
-	}
-	mark_info_dirty(sb, type);
-	ret = blk;
-out_buf:
-	freedqbuf(buf);
-	return ret;
-}
-
-/* Insert empty block to the list */
-static int put_free_dqblk(struct super_block *sb, int type, dqbuf_t buf, uint blk)
-{
-	struct mem_dqinfo *info = sb_dqinfo(sb, type);
-	struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf;
-	int err;
-
-	dh->dqdh_next_free = cpu_to_le32(info->u.v2_i.dqi_free_blk);
-	dh->dqdh_prev_free = cpu_to_le32(0);
-	dh->dqdh_entries = cpu_to_le16(0);
-	info->u.v2_i.dqi_free_blk = blk;
-	mark_info_dirty(sb, type);
-	/* Some strange block. We had better leave it... */
-	if ((err = write_blk(sb, type, blk, buf)) < 0)
-		return err;
-	return 0;
+	d->dqb_id = cpu_to_le32(dquot->dq_id);
+	if (qtree_entry_unused(info, dp))
+		d->dqb_itime = cpu_to_le64(1);
 }
 
-/* Remove given block from the list of blocks with free entries */
-static int remove_free_dqentry(struct super_block *sb, int type, dqbuf_t buf, uint blk)
+static int v2_is_id(void *dp, struct dquot *dquot)
 {
-	dqbuf_t tmpbuf = getdqbuf();
-	struct mem_dqinfo *info = sb_dqinfo(sb, type);
-	struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf;
-	uint nextblk = le32_to_cpu(dh->dqdh_next_free), prevblk = le32_to_cpu(dh->dqdh_prev_free);
-	int err;
+	struct v2_disk_dqblk *d = dp;
+	struct qtree_mem_dqinfo *info =
+			&sb_dqinfo(dquot->dq_sb, dquot->dq_type)->u.v2_i.i;
 
-	if (!tmpbuf)
-		return -ENOMEM;
-	if (nextblk) {
-		if ((err = read_blk(sb, type, nextblk, tmpbuf)) < 0)
-			goto out_buf;
-		((struct v2_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = dh->dqdh_prev_free;
-		if ((err = write_blk(sb, type, nextblk, tmpbuf)) < 0)
-			goto out_buf;
-	}
-	if (prevblk) {
-		if ((err = read_blk(sb, type, prevblk, tmpbuf)) < 0)
-			goto out_buf;
-		((struct v2_disk_dqdbheader *)tmpbuf)->dqdh_next_free = dh->dqdh_next_free;
-		if ((err = write_blk(sb, type, prevblk, tmpbuf)) < 0)
-			goto out_buf;
-	}
-	else {
-		info->u.v2_i.dqi_free_entry = nextblk;
-		mark_info_dirty(sb, type);
-	}
-	freedqbuf(tmpbuf);
-	dh->dqdh_next_free = dh->dqdh_prev_free = cpu_to_le32(0);
-	/* No matter whether write succeeds block is out of list */
-	if (write_blk(sb, type, blk, buf) < 0)
-		printk(KERN_ERR "VFS: Can't write block (%u) with free entries.\n", blk);
-	return 0;
-out_buf:
-	freedqbuf(tmpbuf);
-	return err;
-}
-
-/* Insert given block to the beginning of list with free entries */
-static int insert_free_dqentry(struct super_block *sb, int type, dqbuf_t buf, uint blk)
-{
-	dqbuf_t tmpbuf = getdqbuf();
-	struct mem_dqinfo *info = sb_dqinfo(sb, type);
-	struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf;
-	int err;
-
-	if (!tmpbuf)
-		return -ENOMEM;
-	dh->dqdh_next_free = cpu_to_le32(info->u.v2_i.dqi_free_entry);
-	dh->dqdh_prev_free = cpu_to_le32(0);
-	if ((err = write_blk(sb, type, blk, buf)) < 0)
-		goto out_buf;
-	if (info->u.v2_i.dqi_free_entry) {
-		if ((err = read_blk(sb, type, info->u.v2_i.dqi_free_entry, tmpbuf)) < 0)
-			goto out_buf;
-		((struct v2_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = cpu_to_le32(blk);
-		if ((err = write_blk(sb, type, info->u.v2_i.dqi_free_entry, tmpbuf)) < 0)
-			goto out_buf;
-	}
-	freedqbuf(tmpbuf);
-	info->u.v2_i.dqi_free_entry = blk;
-	mark_info_dirty(sb, type);
-	return 0;
-out_buf:
-	freedqbuf(tmpbuf);
-	return err;
-}
-
-/* Find space for dquot */
-static uint find_free_dqentry(struct dquot *dquot, int *err)
-{
-	struct super_block *sb = dquot->dq_sb;
-	struct mem_dqinfo *info = sb_dqopt(sb)->info+dquot->dq_type;
-	uint blk, i;
-	struct v2_disk_dqdbheader *dh;
-	struct v2_disk_dqblk *ddquot;
-	struct v2_disk_dqblk fakedquot;
-	dqbuf_t buf;
-
-	*err = 0;
-	if (!(buf = getdqbuf())) {
-		*err = -ENOMEM;
+	if (qtree_entry_unused(info, dp))
 		return 0;
-	}
-	dh = (struct v2_disk_dqdbheader *)buf;
-	ddquot = GETENTRIES(buf);
-	if (info->u.v2_i.dqi_free_entry) {
-		blk = info->u.v2_i.dqi_free_entry;
-		if ((*err = read_blk(sb, dquot->dq_type, blk, buf)) < 0)
-			goto out_buf;
-	}
-	else {
-		blk = get_free_dqblk(sb, dquot->dq_type);
-		if ((int)blk < 0) {
-			*err = blk;
-			freedqbuf(buf);
-			return 0;
-		}
-		memset(buf, 0, V2_DQBLKSIZE);
-		/* This is enough as block is already zeroed and entry list is empty... */
-		info->u.v2_i.dqi_free_entry = blk;
-		mark_info_dirty(sb, dquot->dq_type);
-	}
-	if (le16_to_cpu(dh->dqdh_entries)+1 >= V2_DQSTRINBLK)	/* Block will be full? */
-		if ((*err = remove_free_dqentry(sb, dquot->dq_type, buf, blk)) < 0) {
-			printk(KERN_ERR "VFS: find_free_dqentry(): Can't remove block (%u) from entry free list.\n", blk);
-			goto out_buf;
-		}
-	le16_add_cpu(&dh->dqdh_entries, 1);
-	memset(&fakedquot, 0, sizeof(struct v2_disk_dqblk));
-	/* Find free structure in block */
-	for (i = 0; i < V2_DQSTRINBLK && memcmp(&fakedquot, ddquot+i, sizeof(struct v2_disk_dqblk)); i++);
-#ifdef __QUOTA_V2_PARANOIA
-	if (i == V2_DQSTRINBLK) {
-		printk(KERN_ERR "VFS: find_free_dqentry(): Data block full but it shouldn't.\n");
-		*err = -EIO;
-		goto out_buf;
-	}
-#endif
-	if ((*err = write_blk(sb, dquot->dq_type, blk, buf)) < 0) {
-		printk(KERN_ERR "VFS: find_free_dqentry(): Can't write quota data block %u.\n", blk);
-		goto out_buf;
-	}
-	dquot->dq_off = (blk<<V2_DQBLKSIZE_BITS)+sizeof(struct v2_disk_dqdbheader)+i*sizeof(struct v2_disk_dqblk);
-	freedqbuf(buf);
-	return blk;
-out_buf:
-	freedqbuf(buf);
-	return 0;
+	return le32_to_cpu(d->dqb_id) == dquot->dq_id;
 }
 
-/* Insert reference to structure into the trie */
-static int do_insert_tree(struct dquot *dquot, uint *treeblk, int depth)
-{
-	struct super_block *sb = dquot->dq_sb;
-	dqbuf_t buf;
-	int ret = 0, newson = 0, newact = 0;
-	__le32 *ref;
-	uint newblk;
-
-	if (!(buf = getdqbuf()))
-		return -ENOMEM;
-	if (!*treeblk) {
-		ret = get_free_dqblk(sb, dquot->dq_type);
-		if (ret < 0)
-			goto out_buf;
-		*treeblk = ret;
-		memset(buf, 0, V2_DQBLKSIZE);
-		newact = 1;
-	}
-	else {
-		if ((ret = read_blk(sb, dquot->dq_type, *treeblk, buf)) < 0) {
-			printk(KERN_ERR "VFS: Can't read tree quota block %u.\n", *treeblk);
-			goto out_buf;
-		}
-	}
-	ref = (__le32 *)buf;
-	newblk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]);
-	if (!newblk)
-		newson = 1;
-	if (depth == V2_DQTREEDEPTH-1) {
-#ifdef __QUOTA_V2_PARANOIA
-		if (newblk) {
-			printk(KERN_ERR "VFS: Inserting already present quota entry (block %u).\n", le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]));
-			ret = -EIO;
-			goto out_buf;
-		}
-#endif
-		newblk = find_free_dqentry(dquot, &ret);
-	}
-	else
-		ret = do_insert_tree(dquot, &newblk, depth+1);
-	if (newson && ret >= 0) {
-		ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(newblk);
-		ret = write_blk(sb, dquot->dq_type, *treeblk, buf);
-	}
-	else if (newact && ret < 0)
-		put_free_dqblk(sb, dquot->dq_type, buf, *treeblk);
-out_buf:
-	freedqbuf(buf);
-	return ret;
-}
-
-/* Wrapper for inserting quota structure into tree */
-static inline int dq_insert_tree(struct dquot *dquot)
+static int v2_read_dquot(struct dquot *dquot)
 {
-	int tmp = V2_DQTREEOFF;
-	return do_insert_tree(dquot, &tmp, 0);
+	return qtree_read_dquot(&sb_dqinfo(dquot->dq_sb, dquot->dq_type)->u.v2_i.i, dquot);
 }
 
-/*
- *	We don't have to be afraid of deadlocks as we never have quotas on quota files...
- */
 static int v2_write_dquot(struct dquot *dquot)
 {
-	int type = dquot->dq_type;
-	ssize_t ret;
-	struct v2_disk_dqblk ddquot, empty;
-
-	/* dq_off is guarded by dqio_mutex */
-	if (!dquot->dq_off)
-		if ((ret = dq_insert_tree(dquot)) < 0) {
-			printk(KERN_ERR "VFS: Error %zd occurred while creating quota.\n", ret);
-			return ret;
-		}
-	spin_lock(&dq_data_lock);
-	mem2diskdqb(&ddquot, &dquot->dq_dqb, dquot->dq_id);
-	/* Argh... We may need to write structure full of zeroes but that would be
-	 * treated as an empty place by the rest of the code. Format change would
-	 * be definitely cleaner but the problems probably are not worth it */
-	memset(&empty, 0, sizeof(struct v2_disk_dqblk));
-	if (!memcmp(&empty, &ddquot, sizeof(struct v2_disk_dqblk)))
-		ddquot.dqb_itime = cpu_to_le64(1);
-	spin_unlock(&dq_data_lock);
-	ret = dquot->dq_sb->s_op->quota_write(dquot->dq_sb, type,
-	      (char *)&ddquot, sizeof(struct v2_disk_dqblk), dquot->dq_off);
-	if (ret != sizeof(struct v2_disk_dqblk)) {
-		printk(KERN_WARNING "VFS: dquota write failed on dev %s\n", dquot->dq_sb->s_id);
-		if (ret >= 0)
-			ret = -ENOSPC;
-	}
-	else
-		ret = 0;
-	dqstats.writes++;
-
-	return ret;
+	return qtree_write_dquot(&sb_dqinfo(dquot->dq_sb, dquot->dq_type)->u.v2_i.i, dquot);
 }
 
-/* Free dquot entry in data block */
-static int free_dqentry(struct dquot *dquot, uint blk)
-{
-	struct super_block *sb = dquot->dq_sb;
-	int type = dquot->dq_type;
-	struct v2_disk_dqdbheader *dh;
-	dqbuf_t buf = getdqbuf();
-	int ret = 0;
-
-	if (!buf)
-		return -ENOMEM;
-	if (dquot->dq_off >> V2_DQBLKSIZE_BITS != blk) {
-		printk(KERN_ERR "VFS: Quota structure has offset to other "
-		  "block (%u) than it should (%u).\n", blk,
-		  (uint)(dquot->dq_off >> V2_DQBLKSIZE_BITS));
-		goto out_buf;
-	}
-	if ((ret = read_blk(sb, type, blk, buf)) < 0) {
-		printk(KERN_ERR "VFS: Can't read quota data block %u\n", blk);
-		goto out_buf;
-	}
-	dh = (struct v2_disk_dqdbheader *)buf;
-	le16_add_cpu(&dh->dqdh_entries, -1);
-	if (!le16_to_cpu(dh->dqdh_entries)) {	/* Block got free? */
-		if ((ret = remove_free_dqentry(sb, type, buf, blk)) < 0 ||
-		    (ret = put_free_dqblk(sb, type, buf, blk)) < 0) {
-			printk(KERN_ERR "VFS: Can't move quota data block (%u) "
-			  "to free list.\n", blk);
-			goto out_buf;
-		}
-	}
-	else {
-		memset(buf+(dquot->dq_off & ((1 << V2_DQBLKSIZE_BITS)-1)), 0,
-		  sizeof(struct v2_disk_dqblk));
-		if (le16_to_cpu(dh->dqdh_entries) == V2_DQSTRINBLK-1) {
-			/* Insert will write block itself */
-			if ((ret = insert_free_dqentry(sb, type, buf, blk)) < 0) {
-				printk(KERN_ERR "VFS: Can't insert quota data block (%u) to free entry list.\n", blk);
-				goto out_buf;
-			}
-		}
-		else
-			if ((ret = write_blk(sb, type, blk, buf)) < 0) {
-				printk(KERN_ERR "VFS: Can't write quota data "
-				  "block %u\n", blk);
-				goto out_buf;
-			}
-	}
-	dquot->dq_off = 0;	/* Quota is now unattached */
-out_buf:
-	freedqbuf(buf);
-	return ret;
-}
-
-/* Remove reference to dquot from tree */
-static int remove_tree(struct dquot *dquot, uint *blk, int depth)
-{
-	struct super_block *sb = dquot->dq_sb;
-	int type = dquot->dq_type;
-	dqbuf_t buf = getdqbuf();
-	int ret = 0;
-	uint newblk;
-	__le32 *ref = (__le32 *)buf;
-	
-	if (!buf)
-		return -ENOMEM;
-	if ((ret = read_blk(sb, type, *blk, buf)) < 0) {
-		printk(KERN_ERR "VFS: Can't read quota data block %u\n", *blk);
-		goto out_buf;
-	}
-	newblk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]);
-	if (depth == V2_DQTREEDEPTH-1) {
-		ret = free_dqentry(dquot, newblk);
-		newblk = 0;
-	}
-	else
-		ret = remove_tree(dquot, &newblk, depth+1);
-	if (ret >= 0 && !newblk) {
-		int i;
-		ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(0);
-		for (i = 0; i < V2_DQBLKSIZE && !buf[i]; i++);	/* Block got empty? */
-		/* Don't put the root block into the free block list */
-		if (i == V2_DQBLKSIZE && *blk != V2_DQTREEOFF) {
-			put_free_dqblk(sb, type, buf, *blk);
-			*blk = 0;
-		}
-		else
-			if ((ret = write_blk(sb, type, *blk, buf)) < 0)
-				printk(KERN_ERR "VFS: Can't write quota tree "
-				  "block %u.\n", *blk);
-	}
-out_buf:
-	freedqbuf(buf);
-	return ret;	
-}
-
-/* Delete dquot from tree */
-static int v2_delete_dquot(struct dquot *dquot)
-{
-	uint tmp = V2_DQTREEOFF;
-
-	if (!dquot->dq_off)	/* Even not allocated? */
-		return 0;
-	return remove_tree(dquot, &tmp, 0);
-}
-
-/* Find entry in block */
-static loff_t find_block_dqentry(struct dquot *dquot, uint blk)
-{
-	dqbuf_t buf = getdqbuf();
-	loff_t ret = 0;
-	int i;
-	struct v2_disk_dqblk *ddquot = GETENTRIES(buf);
-
-	if (!buf)
-		return -ENOMEM;
-	if ((ret = read_blk(dquot->dq_sb, dquot->dq_type, blk, buf)) < 0) {
-		printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk);
-		goto out_buf;
-	}
-	if (dquot->dq_id)
-		for (i = 0; i < V2_DQSTRINBLK &&
-		     le32_to_cpu(ddquot[i].dqb_id) != dquot->dq_id; i++);
-	else {	/* ID 0 as a bit more complicated searching... */
-		struct v2_disk_dqblk fakedquot;
-
-		memset(&fakedquot, 0, sizeof(struct v2_disk_dqblk));
-		for (i = 0; i < V2_DQSTRINBLK; i++)
-			if (!le32_to_cpu(ddquot[i].dqb_id) &&
-			    memcmp(&fakedquot, ddquot+i, sizeof(struct v2_disk_dqblk)))
-				break;
-	}
-	if (i == V2_DQSTRINBLK) {
-		printk(KERN_ERR "VFS: Quota for id %u referenced "
-		  "but not present.\n", dquot->dq_id);
-		ret = -EIO;
-		goto out_buf;
-	}
-	else
-		ret = (blk << V2_DQBLKSIZE_BITS) + sizeof(struct
-		  v2_disk_dqdbheader) + i * sizeof(struct v2_disk_dqblk);
-out_buf:
-	freedqbuf(buf);
-	return ret;
-}
-
-/* Find entry for given id in the tree */
-static loff_t find_tree_dqentry(struct dquot *dquot, uint blk, int depth)
-{
-	dqbuf_t buf = getdqbuf();
-	loff_t ret = 0;
-	__le32 *ref = (__le32 *)buf;
-
-	if (!buf)
-		return -ENOMEM;
-	if ((ret = read_blk(dquot->dq_sb, dquot->dq_type, blk, buf)) < 0) {
-		printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk);
-		goto out_buf;
-	}
-	ret = 0;
-	blk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]);
-	if (!blk)	/* No reference? */
-		goto out_buf;
-	if (depth < V2_DQTREEDEPTH-1)
-		ret = find_tree_dqentry(dquot, blk, depth+1);
-	else
-		ret = find_block_dqentry(dquot, blk);
-out_buf:
-	freedqbuf(buf);
-	return ret;
-}
-
-/* Find entry for given id in the tree - wrapper function */
-static inline loff_t find_dqentry(struct dquot *dquot)
-{
-	return find_tree_dqentry(dquot, V2_DQTREEOFF, 0);
-}
-
-static int v2_read_dquot(struct dquot *dquot)
-{
-	int type = dquot->dq_type;
-	loff_t offset;
-	struct v2_disk_dqblk ddquot, empty;
-	int ret = 0;
-
-#ifdef __QUOTA_V2_PARANOIA
-	/* Invalidated quota? */
-	if (!dquot->dq_sb || !sb_dqopt(dquot->dq_sb)->files[type]) {
-		printk(KERN_ERR "VFS: Quota invalidated while reading!\n");
-		return -EIO;
-	}
-#endif
-	offset = find_dqentry(dquot);
-	if (offset <= 0) {	/* Entry not present? */
-		if (offset < 0)
-			printk(KERN_ERR "VFS: Can't read quota "
-			  "structure for id %u.\n", dquot->dq_id);
-		dquot->dq_off = 0;
-		set_bit(DQ_FAKE_B, &dquot->dq_flags);
-		memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
-		ret = offset;
-	}
-	else {
-		dquot->dq_off = offset;
-		if ((ret = dquot->dq_sb->s_op->quota_read(dquot->dq_sb, type,
-		    (char *)&ddquot, sizeof(struct v2_disk_dqblk), offset))
-		    != sizeof(struct v2_disk_dqblk)) {
-			if (ret >= 0)
-				ret = -EIO;
-			printk(KERN_ERR "VFS: Error while reading quota "
-			  "structure for id %u.\n", dquot->dq_id);
-			memset(&ddquot, 0, sizeof(struct v2_disk_dqblk));
-		}
-		else {
-			ret = 0;
-			/* We need to escape back all-zero structure */
-			memset(&empty, 0, sizeof(struct v2_disk_dqblk));
-			empty.dqb_itime = cpu_to_le64(1);
-			if (!memcmp(&empty, &ddquot, sizeof(struct v2_disk_dqblk)))
-				ddquot.dqb_itime = 0;
-		}
-		disk2memdqb(&dquot->dq_dqb, &ddquot);
-		if (!dquot->dq_dqb.dqb_bhardlimit &&
-			!dquot->dq_dqb.dqb_bsoftlimit &&
-			!dquot->dq_dqb.dqb_ihardlimit &&
-			!dquot->dq_dqb.dqb_isoftlimit)
-			set_bit(DQ_FAKE_B, &dquot->dq_flags);
-	}
-	dqstats.reads++;
-
-	return ret;
-}
-
-/* Check whether dquot should not be deleted. We know we are
- * the only one operating on dquot (thanks to dq_lock) */
 static int v2_release_dquot(struct dquot *dquot)
 {
-	if (test_bit(DQ_FAKE_B, &dquot->dq_flags) && !(dquot->dq_dqb.dqb_curinodes | dquot->dq_dqb.dqb_curspace))
-		return v2_delete_dquot(dquot);
-	return 0;
+	return qtree_release_dquot(&sb_dqinfo(dquot->dq_sb, dquot->dq_type)->u.v2_i.i, dquot);
 }
 
 static struct quota_format_ops v2_format_ops = {
diff --git a/fs/quotaio_v2.h b/fs/quotaio_v2.h
index 303d7cb..530fe58 100644
--- a/fs/quotaio_v2.h
+++ b/fs/quotaio_v2.h
@@ -21,6 +21,12 @@
 	0		/* GRPQUOTA */\
 }
 
+/* First generic header */
+struct v2_disk_dqheader {
+	__le32 dqh_magic;	/* Magic number identifying file */
+	__le32 dqh_version;	/* File version */
+};
+
 /*
  * The following structure defines the format of the disk quota file
  * (as it appears on disk) - the file is a radix tree whose leaves point
@@ -38,15 +44,6 @@ struct v2_disk_dqblk {
 	__le64 dqb_itime;	/* time limit for excessive inode use */
 };
 
-/*
- * Here are header structures as written on disk and their in-memory copies
- */
-/* First generic header */
-struct v2_disk_dqheader {
-	__le32 dqh_magic;	/* Magic number identifying file */
-	__le32 dqh_version;	/* File version */
-};
-
 /* Header with type and version specific information */
 struct v2_disk_dqinfo {
 	__le32 dqi_bgrace;	/* Time before block soft limit becomes hard limit */
@@ -57,23 +54,7 @@ struct v2_disk_dqinfo {
 	__le32 dqi_free_entry;	/* Number of block with at least one free entry */
 };
 
-/*
- *  Structure of header of block with quota structures. It is padded to 16 bytes so
- *  there will be space for exactly 21 quota-entries in a block
- */
-struct v2_disk_dqdbheader {
-	__le32 dqdh_next_free;	/* Number of next block with free entry */
-	__le32 dqdh_prev_free;	/* Number of previous block with free entry */
-	__le16 dqdh_entries;	/* Number of valid entries in block */
-	__le16 dqdh_pad1;
-	__le32 dqdh_pad2;
-};
-
 #define V2_DQINFOOFF	sizeof(struct v2_disk_dqheader)	/* Offset of info header in file */
-#define V2_DQBLKSIZE_BITS	10
-#define V2_DQBLKSIZE	(1 << V2_DQBLKSIZE_BITS)	/* Size of block with quota structures */
-#define V2_DQTREEOFF	1		/* Offset of tree in file in blocks */
-#define V2_DQTREEDEPTH	4		/* Depth of quota tree */
-#define V2_DQSTRINBLK	((V2_DQBLKSIZE - sizeof(struct v2_disk_dqdbheader)) / sizeof(struct v2_disk_dqblk))	/* Number of entries in one blocks */
+#define V2_DQBLKSIZE_BITS 10				/* Size of leaf block in tree */
 
 #endif /* _LINUX_QUOTAIO_V2_H */
diff --git a/include/linux/dqblk_qtree.h b/include/linux/dqblk_qtree.h
new file mode 100644
index 0000000..82a1652
--- /dev/null
+++ b/include/linux/dqblk_qtree.h
@@ -0,0 +1,56 @@
+/*
+ *	Definitions of structures and functions for quota formats using trie
+ */
+
+#ifndef _LINUX_DQBLK_QTREE_H
+#define _LINUX_DQBLK_QTREE_H
+
+#include <linux/types.h>
+
+/* Numbers of blocks needed for updates - we count with the smallest
+ * possible block size (1024) */
+#define QTREE_INIT_ALLOC 4
+#define QTREE_INIT_REWRITE 2
+#define QTREE_DEL_ALLOC 0
+#define QTREE_DEL_REWRITE 6
+
+struct dquot;
+
+/* Operations */
+struct qtree_fmt_operations {
+	void (*mem2disk_dqblk)(void *disk, struct dquot *dquot);	/* Convert given entry from in memory format to disk one */
+	void (*disk2mem_dqblk)(struct dquot *dquot, void *disk);	/* Convert given entry from disk format to in memory one */
+	int (*is_id)(void *disk, struct dquot *dquot);	/* Is this structure for given id? */
+};
+
+/* Inmemory copy of version specific information */
+struct qtree_mem_dqinfo {
+	struct super_block *dqi_sb;	/* Sb quota is on */
+	int dqi_type;			/* Quota type */
+	unsigned int dqi_blocks;	/* # of blocks in quota file */
+	unsigned int dqi_free_blk;	/* First block in list of free blocks */
+	unsigned int dqi_free_entry;	/* First block with free entry */
+	unsigned int dqi_blocksize_bits;	/* Block size of quota file */
+	unsigned int dqi_entry_size;	/* Size of quota entry in quota file */
+	unsigned int dqi_usable_bs;	/* Space usable in block for quota data */
+	unsigned int dqi_qtree_depth;	/* Precomputed depth of quota tree */
+	struct qtree_fmt_operations *dqi_ops;	/* Operations for entry manipulation */
+};
+
+int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot);
+int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot);
+int qtree_delete_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot);
+int qtree_release_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot);
+int qtree_entry_unused(struct qtree_mem_dqinfo *info, char *disk);
+static inline int qtree_depth(struct qtree_mem_dqinfo *info)
+{
+	unsigned int epb = info->dqi_usable_bs >> 2;
+	unsigned long long entries = epb;
+	int i;
+
+	for (i = 1; entries < (1ULL << 32); i++)
+		entries *= epb;
+	return i;
+}
+
+#endif /* _LINUX_DQBLK_QTREE_H */
diff --git a/include/linux/dqblk_v2.h b/include/linux/dqblk_v2.h
index 4f85332..e5e22a7 100644
--- a/include/linux/dqblk_v2.h
+++ b/include/linux/dqblk_v2.h
@@ -1,26 +1,23 @@
 /*
- *	Definitions of structures for vfsv0 quota format
+ *  Definitions for vfsv0 quota format
  */
 
 #ifndef _LINUX_DQBLK_V2_H
 #define _LINUX_DQBLK_V2_H
 
-#include <linux/types.h>
+#include <linux/dqblk_qtree.h>
 
-/* id numbers of quota format */
+/* Id number of quota format */
 #define QFMT_VFS_V0 2
 
 /* Numbers of blocks needed for updates */
-#define V2_INIT_ALLOC 4
-#define V2_INIT_REWRITE 2
-#define V2_DEL_ALLOC 0
-#define V2_DEL_REWRITE 6
+#define V2_INIT_ALLOC QTREE_INIT_ALLOC
+#define V2_INIT_REWRITE QTREE_INIT_REWRITE
+#define V2_DEL_ALLOC QTREE_DEL_ALLOC
+#define V2_DEL_REWRITE QTREE_DEL_REWRITE
 
-/* Inmemory copy of version specific information */
 struct v2_mem_dqinfo {
-	unsigned int dqi_blocks;
-	unsigned int dqi_free_blk;
-	unsigned int dqi_free_entry;
+	struct qtree_mem_dqinfo i;
 };
 
 #endif /* _LINUX_DQBLK_V2_H */
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 13/29] quota: Convert union in mem_dqinfo to a pointer
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (11 preceding siblings ...)
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 12/29] quota: Split off quota tree handling into a separate file Jan Kara
@ 2008-10-24 22:08 ` Jan Kara
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 14/29] quota: Allow negative usage of space and inodes Jan Kara
                   ` (15 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:08 UTC (permalink / raw)
  To: ocfs2-devel

Coming quota support for OCFS2 is going to need quite a bit
of additional per-sb quota information. Moreover having fs.h
include all the types needed for this structure would be a
pain in the a**. So remove the union from mem_dqinfo and add
a private pointer for filesystem's use.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/quota_v2.c            |   53 +++++++++++++++++++++++++++++----------------
 include/linux/dqblk_v1.h |    4 ---
 include/linux/dqblk_v2.h |    4 ---
 include/linux/quota.h    |    5 +---
 4 files changed, 35 insertions(+), 31 deletions(-)

diff --git a/fs/quota_v2.c b/fs/quota_v2.c
index a87f102..a371919 100644
--- a/fs/quota_v2.c
+++ b/fs/quota_v2.c
@@ -71,6 +71,7 @@ static int v2_read_file_info(struct super_block *sb, int type)
 {
 	struct v2_disk_dqinfo dinfo;
 	struct mem_dqinfo *info = sb_dqinfo(sb, type);
+	struct qtree_mem_dqinfo *qinfo;
 	ssize_t size;
 
 	size = sb->s_op->quota_read(sb, type, (char *)&dinfo,
@@ -80,22 +81,29 @@ static int v2_read_file_info(struct super_block *sb, int type)
 			sb->s_id);
 		return -1;
 	}
+	info->dqi_priv = kmalloc(sizeof(struct qtree_mem_dqinfo), GFP_NOFS);
+	if (!info->dqi_priv) {
+		printk(KERN_WARNING "Not enough memory for quota information"
+				    "structure.\n");
+		return -1;
+	}
+	qinfo = info->dqi_priv;
 	/* limits are stored as unsigned 32-bit data */
 	info->dqi_maxblimit = 0xffffffff;
 	info->dqi_maxilimit = 0xffffffff;
 	info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace);
 	info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace);
 	info->dqi_flags = le32_to_cpu(dinfo.dqi_flags);
-	info->u.v2_i.i.dqi_sb = sb;
-	info->u.v2_i.i.dqi_type = type;
-	info->u.v2_i.i.dqi_blocks = le32_to_cpu(dinfo.dqi_blocks);
-	info->u.v2_i.i.dqi_free_blk = le32_to_cpu(dinfo.dqi_free_blk);
-	info->u.v2_i.i.dqi_free_entry = le32_to_cpu(dinfo.dqi_free_entry);
-	info->u.v2_i.i.dqi_blocksize_bits = V2_DQBLKSIZE_BITS;
-	info->u.v2_i.i.dqi_usable_bs = 1 << V2_DQBLKSIZE_BITS;
-	info->u.v2_i.i.dqi_qtree_depth = qtree_depth(&info->u.v2_i.i);
-	info->u.v2_i.i.dqi_entry_size = sizeof(struct v2_disk_dqblk);
-	info->u.v2_i.i.dqi_ops = &v2_qtree_ops;
+	qinfo->dqi_sb = sb;
+	qinfo->dqi_type = type;
+	qinfo->dqi_blocks = le32_to_cpu(dinfo.dqi_blocks);
+	qinfo->dqi_free_blk = le32_to_cpu(dinfo.dqi_free_blk);
+	qinfo->dqi_free_entry = le32_to_cpu(dinfo.dqi_free_entry);
+	qinfo->dqi_blocksize_bits = V2_DQBLKSIZE_BITS;
+	qinfo->dqi_usable_bs = 1 << V2_DQBLKSIZE_BITS;
+	qinfo->dqi_qtree_depth = qtree_depth(qinfo);
+	qinfo->dqi_entry_size = sizeof(struct v2_disk_dqblk);
+	qinfo->dqi_ops = &v2_qtree_ops;
 	return 0;
 }
 
@@ -104,6 +112,7 @@ static int v2_write_file_info(struct super_block *sb, int type)
 {
 	struct v2_disk_dqinfo dinfo;
 	struct mem_dqinfo *info = sb_dqinfo(sb, type);
+	struct qtree_mem_dqinfo *qinfo = info->dqi_priv;
 	ssize_t size;
 
 	spin_lock(&dq_data_lock);
@@ -112,9 +121,9 @@ static int v2_write_file_info(struct super_block *sb, int type)
 	dinfo.dqi_igrace = cpu_to_le32(info->dqi_igrace);
 	dinfo.dqi_flags = cpu_to_le32(info->dqi_flags & DQF_MASK);
 	spin_unlock(&dq_data_lock);
-	dinfo.dqi_blocks = cpu_to_le32(info->u.v2_i.i.dqi_blocks);
-	dinfo.dqi_free_blk = cpu_to_le32(info->u.v2_i.i.dqi_free_blk);
-	dinfo.dqi_free_entry = cpu_to_le32(info->u.v2_i.i.dqi_free_entry);
+	dinfo.dqi_blocks = cpu_to_le32(qinfo->dqi_blocks);
+	dinfo.dqi_free_blk = cpu_to_le32(qinfo->dqi_free_blk);
+	dinfo.dqi_free_entry = cpu_to_le32(qinfo->dqi_free_entry);
 	size = sb->s_op->quota_write(sb, type, (char *)&dinfo,
 	       sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF);
 	if (size != sizeof(struct v2_disk_dqinfo)) {
@@ -150,7 +159,7 @@ static void v2_mem2diskdqb(void *dp, struct dquot *dquot)
 	struct v2_disk_dqblk *d = dp;
 	struct mem_dqblk *m = &dquot->dq_dqb;
 	struct qtree_mem_dqinfo *info =
-			&sb_dqinfo(dquot->dq_sb, dquot->dq_type)->u.v2_i.i;
+			sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
 
 	d->dqb_ihardlimit = cpu_to_le32(m->dqb_ihardlimit);
 	d->dqb_isoftlimit = cpu_to_le32(m->dqb_isoftlimit);
@@ -169,7 +178,7 @@ static int v2_is_id(void *dp, struct dquot *dquot)
 {
 	struct v2_disk_dqblk *d = dp;
 	struct qtree_mem_dqinfo *info =
-			&sb_dqinfo(dquot->dq_sb, dquot->dq_type)->u.v2_i.i;
+			sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
 
 	if (qtree_entry_unused(info, dp))
 		return 0;
@@ -178,24 +187,30 @@ static int v2_is_id(void *dp, struct dquot *dquot)
 
 static int v2_read_dquot(struct dquot *dquot)
 {
-	return qtree_read_dquot(&sb_dqinfo(dquot->dq_sb, dquot->dq_type)->u.v2_i.i, dquot);
+	return qtree_read_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv, dquot);
 }
 
 static int v2_write_dquot(struct dquot *dquot)
 {
-	return qtree_write_dquot(&sb_dqinfo(dquot->dq_sb, dquot->dq_type)->u.v2_i.i, dquot);
+	return qtree_write_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv, dquot);
 }
 
 static int v2_release_dquot(struct dquot *dquot)
 {
-	return qtree_release_dquot(&sb_dqinfo(dquot->dq_sb, dquot->dq_type)->u.v2_i.i, dquot);
+	return qtree_release_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv, dquot);
+}
+
+static int v2_free_file_info(struct super_block *sb, int type)
+{
+	kfree(sb_dqinfo(sb, type)->dqi_priv);
+	return 0;
 }
 
 static struct quota_format_ops v2_format_ops = {
 	.check_quota_file	= v2_check_quota_file,
 	.read_file_info		= v2_read_file_info,
 	.write_file_info	= v2_write_file_info,
-	.free_file_info		= NULL,
+	.free_file_info		= v2_free_file_info,
 	.read_dqblk		= v2_read_dquot,
 	.commit_dqblk		= v2_write_dquot,
 	.release_dqblk		= v2_release_dquot,
diff --git a/include/linux/dqblk_v1.h b/include/linux/dqblk_v1.h
index 57f1250..9cea901 100644
--- a/include/linux/dqblk_v1.h
+++ b/include/linux/dqblk_v1.h
@@ -17,8 +17,4 @@
 #define V1_DEL_ALLOC 0
 #define V1_DEL_REWRITE 2
 
-/* Special information about quotafile */
-struct v1_mem_dqinfo {
-};
-
 #endif	/* _LINUX_DQBLK_V1_H */
diff --git a/include/linux/dqblk_v2.h b/include/linux/dqblk_v2.h
index e5e22a7..ff8af1b 100644
--- a/include/linux/dqblk_v2.h
+++ b/include/linux/dqblk_v2.h
@@ -16,8 +16,4 @@
 #define V2_DEL_ALLOC QTREE_DEL_ALLOC
 #define V2_DEL_REWRITE QTREE_DEL_REWRITE
 
-struct v2_mem_dqinfo {
-	struct qtree_mem_dqinfo i;
-};
-
 #endif /* _LINUX_DQBLK_V2_H */
diff --git a/include/linux/quota.h b/include/linux/quota.h
index 8dd5333..e05c30d 100644
--- a/include/linux/quota.h
+++ b/include/linux/quota.h
@@ -210,10 +210,7 @@ struct mem_dqinfo {
 	unsigned int dqi_igrace;
 	qsize_t dqi_maxblimit;
 	qsize_t dqi_maxilimit;
-	union {
-		struct v1_mem_dqinfo v1_i;
-		struct v2_mem_dqinfo v2_i;
-	} u;
+	void *dqi_priv;
 };
 
 struct super_block;
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 14/29] quota: Allow negative usage of space and inodes
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (12 preceding siblings ...)
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 13/29] quota: Convert union in mem_dqinfo to a pointer Jan Kara
@ 2008-10-24 22:08 ` Jan Kara
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 15/29] quota: Keep which entries were set by SETQUOTA quotactl Jan Kara
                   ` (14 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:08 UTC (permalink / raw)
  To: ocfs2-devel

For clustered filesystems, it can happen that space / inode usage goes
negative temporarily (because some node is allocating another node
is freeing and they are not completely in sync). So let quota code
allow this and change qsize_t so a signed type so that we don't
underflow the variables.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/dquot.c            |    6 ++++--
 include/linux/quota.h |    3 ++-
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/fs/dquot.c b/fs/dquot.c
index 5b82722..9d2a1f0 100644
--- a/fs/dquot.c
+++ b/fs/dquot.c
@@ -845,7 +845,8 @@ static inline void dquot_incr_space(struct dquot *dquot, qsize_t number)
 
 static inline void dquot_decr_inodes(struct dquot *dquot, qsize_t number)
 {
-	if (dquot->dq_dqb.dqb_curinodes > number)
+	if (sb_dqopt(dquot->dq_sb)->flags & DQUOT_NEGATIVE_USAGE ||
+	    dquot->dq_dqb.dqb_curinodes >= number)
 		dquot->dq_dqb.dqb_curinodes -= number;
 	else
 		dquot->dq_dqb.dqb_curinodes = 0;
@@ -856,7 +857,8 @@ static inline void dquot_decr_inodes(struct dquot *dquot, qsize_t number)
 
 static inline void dquot_decr_space(struct dquot *dquot, qsize_t number)
 {
-	if (dquot->dq_dqb.dqb_curspace > number)
+	if (sb_dqopt(dquot->dq_sb)->flags & DQUOT_NEGATIVE_USAGE ||
+	    dquot->dq_dqb.dqb_curspace >= number)
 		dquot->dq_dqb.dqb_curspace -= number;
 	else
 		dquot->dq_dqb.dqb_curspace = 0;
diff --git a/include/linux/quota.h b/include/linux/quota.h
index e05c30d..0ee2a55 100644
--- a/include/linux/quota.h
+++ b/include/linux/quota.h
@@ -170,7 +170,7 @@ enum {
 #include <asm/atomic.h>
 
 typedef __kernel_uid32_t qid_t; /* Type in which we store ids in memory */
-typedef __u64 qsize_t;          /* Type in which we store sizes */
+typedef __s64 qsize_t;          /* Type in which we store sizes */
 
 extern spinlock_t dq_data_lock;
 
@@ -338,6 +338,7 @@ enum {
 						 * responsible for setting
 						 * S_NOQUOTA, S_NOATIME flags
 						 */
+#define DQUOT_NEGATIVE_USAGE	(1 << 7)	/* Allow negative quota usage */
 
 static inline unsigned int dquot_state_flag(unsigned int flags, int type)
 {
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 15/29] quota: Keep which entries were set by SETQUOTA quotactl
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (13 preceding siblings ...)
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 14/29] quota: Allow negative usage of space and inodes Jan Kara
@ 2008-10-24 22:08 ` Jan Kara
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 16/29] quota: Add helpers to allow ocfs2 specific quota initialization, freeing and recovery Jan Kara
                   ` (13 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:08 UTC (permalink / raw)
  To: ocfs2-devel

Quota in a clustered environment needs to synchronize quota information
among cluster nodes. This means we have to occasionally update some
information in dquot from disk / network. On the other hand we have to
be careful not to overwrite changes administrator did via SETQUOTA.
So indicate in dquot->dq_flags which entries have been set by SETQUOTA
and quota format can clear these flags when it properly propagated
the changes.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/dquot.c            |   12 ++++++++++--
 include/linux/quota.h |   26 ++++++++++++++++++++------
 2 files changed, 30 insertions(+), 8 deletions(-)

diff --git a/fs/dquot.c b/fs/dquot.c
index 9d2a1f0..124a20a 100644
--- a/fs/dquot.c
+++ b/fs/dquot.c
@@ -2006,25 +2006,33 @@ static int do_set_dqblk(struct dquot *dquot, struct if_dqblk *di)
 	if (di->dqb_valid & QIF_SPACE) {
 		dm->dqb_curspace = di->dqb_curspace;
 		check_blim = 1;
+		__set_bit(DQ_LASTSET_B + QIF_SPACE_B, &dquot->dq_flags);
 	}
 	if (di->dqb_valid & QIF_BLIMITS) {
 		dm->dqb_bsoftlimit = qbtos(di->dqb_bsoftlimit);
 		dm->dqb_bhardlimit = qbtos(di->dqb_bhardlimit);
 		check_blim = 1;
+		__set_bit(DQ_LASTSET_B + QIF_BLIMITS_B, &dquot->dq_flags);
 	}
 	if (di->dqb_valid & QIF_INODES) {
 		dm->dqb_curinodes = di->dqb_curinodes;
 		check_ilim = 1;
+		__set_bit(DQ_LASTSET_B + QIF_INODES_B, &dquot->dq_flags);
 	}
 	if (di->dqb_valid & QIF_ILIMITS) {
 		dm->dqb_isoftlimit = di->dqb_isoftlimit;
 		dm->dqb_ihardlimit = di->dqb_ihardlimit;
 		check_ilim = 1;
+		__set_bit(DQ_LASTSET_B + QIF_ILIMITS_B, &dquot->dq_flags);
 	}
-	if (di->dqb_valid & QIF_BTIME)
+	if (di->dqb_valid & QIF_BTIME) {
 		dm->dqb_btime = di->dqb_btime;
-	if (di->dqb_valid & QIF_ITIME)
+		__set_bit(DQ_LASTSET_B + QIF_BTIME_B, &dquot->dq_flags);
+	}
+	if (di->dqb_valid & QIF_ITIME) {
 		dm->dqb_itime = di->dqb_itime;
+		__set_bit(DQ_LASTSET_B + QIF_ITIME_B, &dquot->dq_flags);
+	}
 
 	if (check_blim) {
 		if (!dm->dqb_bsoftlimit || dm->dqb_curspace < dm->dqb_bsoftlimit) {
diff --git a/include/linux/quota.h b/include/linux/quota.h
index 0ee2a55..f81e80c 100644
--- a/include/linux/quota.h
+++ b/include/linux/quota.h
@@ -82,12 +82,21 @@
  * Quota structure used for communication with userspace via quotactl
  * Following flags are used to specify which fields are valid
  */
-#define QIF_BLIMITS	1
-#define QIF_SPACE	2
-#define QIF_ILIMITS	4
-#define QIF_INODES	8
-#define QIF_BTIME	16
-#define QIF_ITIME	32
+enum {
+	QIF_BLIMITS_B = 0,
+	QIF_SPACE_B,
+	QIF_ILIMITS_B,
+	QIF_INODES_B,
+	QIF_BTIME_B,
+	QIF_ITIME_B,
+};
+
+#define QIF_BLIMITS	(1 << QIF_BLIMITS_B)
+#define QIF_SPACE	(1 << QIF_SPACE_B)
+#define QIF_ILIMITS	(1 << QIF_ILIMITS_B)
+#define QIF_INODES	(1 << QIF_INODES_B)
+#define QIF_BTIME	(1 << QIF_BTIME_B)
+#define QIF_ITIME	(1 << QIF_ITIME_B)
 #define QIF_LIMITS	(QIF_BLIMITS | QIF_ILIMITS)
 #define QIF_USAGE	(QIF_SPACE | QIF_INODES)
 #define QIF_TIMES	(QIF_BTIME | QIF_ITIME)
@@ -244,6 +253,11 @@ extern struct dqstats dqstats;
 #define DQ_FAKE_B	3	/* no limits only usage */
 #define DQ_READ_B	4	/* dquot was read into memory */
 #define DQ_ACTIVE_B	5	/* dquot is active (dquot_release not called) */
+#define DQ_LASTSET_B	6	/* Following 6 bits (see QIF_) are reserved\
+				 * for the mask of entries set via SETQUOTA\
+				 * quotactl. They are set under dq_data_lock\
+				 * and the quota format handling dquot can\
+				 * clear them when it sees fit. */
 
 struct dquot {
 	struct hlist_node dq_hash;	/* Hash list in memory */
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 16/29] quota: Add helpers to allow ocfs2 specific quota initialization, freeing and recovery
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (14 preceding siblings ...)
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 15/29] quota: Keep which entries were set by SETQUOTA quotactl Jan Kara
@ 2008-10-24 22:08 ` Jan Kara
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 17/29] quota: Implement function for scanning active dquots Jan Kara
                   ` (12 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:08 UTC (permalink / raw)
  To: ocfs2-devel

OCFS2 needs to peek whether quota structure is already in memory so
that it can avoid expensive cluster locking in that case. Similarly
when freeing dquots, it checks whether it is the last quota structure
user or not. Finally, it needs to get reference to dquot structure for
specified id and quota type when recovering quota file after crash.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/dquot.c               |   38 ++++++++++++++++++++++++++++++++------
 include/linux/quotaops.h |    4 ++++
 2 files changed, 36 insertions(+), 6 deletions(-)

diff --git a/fs/dquot.c b/fs/dquot.c
index 124a20a..f6bcacd 100644
--- a/fs/dquot.c
+++ b/fs/dquot.c
@@ -213,8 +213,6 @@ static struct hlist_head *dquot_hash;
 
 struct dqstats dqstats;
 
-static void dqput(struct dquot *dquot);
-
 static inline unsigned int
 hashfn(const struct super_block *sb, unsigned int id, int type)
 {
@@ -568,7 +566,7 @@ static struct shrinker dqcache_shrinker = {
  * NOTE: If you change this function please check whether dqput_blocks() works right...
  * MUST be called with either dqptr_sem or dqonoff_mutex held
  */
-static void dqput(struct dquot *dquot)
+void dqput(struct dquot *dquot)
 {
 	int ret;
 
@@ -660,10 +658,28 @@ static struct dquot *get_empty_dquot(struct super_block *sb, int type)
 }
 
 /*
+ * Check whether dquot is in memory.
+ * MUST be called with either dqptr_sem or dqonoff_mutex held
+ */
+int dquot_is_cached(struct super_block *sb, unsigned int id, int type)
+{
+	unsigned int hashent = hashfn(sb, id, type);
+	int ret = 0;
+
+        if (!sb_has_quota_active(sb, type))
+		return 0;
+	spin_lock(&dq_list_lock);
+	if (find_dquot(hashent, sb, id, type) != NODQUOT)
+		ret = 1;
+	spin_unlock(&dq_list_lock);
+	return ret;
+}
+
+/*
  * Get reference to dquot
  * MUST be called with either dqptr_sem or dqonoff_mutex held
  */
-static struct dquot *dqget(struct super_block *sb, unsigned int id, int type)
+struct dquot *dqget(struct super_block *sb, unsigned int id, int type)
 {
 	unsigned int hashent = hashfn(sb, id, type);
 	struct dquot *dquot, *empty = NODQUOT;
@@ -1182,17 +1198,23 @@ out_err:
  * 	Release all quotas referenced by inode
  *	Transaction must be started at an entry
  */
-int dquot_drop(struct inode *inode)
+int dquot_drop_locked(struct inode *inode)
 {
 	int cnt;
 
-	down_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 		if (inode->i_dquot[cnt] != NODQUOT) {
 			dqput(inode->i_dquot[cnt]);
 			inode->i_dquot[cnt] = NODQUOT;
 		}
 	}
+	return 0;
+}
+
+int dquot_drop(struct inode *inode)
+{
+	down_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
+	dquot_drop_locked(inode);
 	up_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
 	return 0;
 }
@@ -2304,7 +2326,11 @@ EXPORT_SYMBOL(dquot_release);
 EXPORT_SYMBOL(dquot_mark_dquot_dirty);
 EXPORT_SYMBOL(dquot_initialize);
 EXPORT_SYMBOL(dquot_drop);
+EXPORT_SYMBOL(dquot_drop_locked);
 EXPORT_SYMBOL(vfs_dq_drop);
+EXPORT_SYMBOL(dqget);
+EXPORT_SYMBOL(dqput);
+EXPORT_SYMBOL(dquot_is_cached);
 EXPORT_SYMBOL(dquot_alloc_space);
 EXPORT_SYMBOL(dquot_alloc_inode);
 EXPORT_SYMBOL(dquot_free_space);
diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h
index 94f00ec..1f990f2 100644
--- a/include/linux/quotaops.h
+++ b/include/linux/quotaops.h
@@ -27,6 +27,10 @@ void sync_dquots(struct super_block *sb, int type);
 
 int dquot_initialize(struct inode *inode, int type);
 int dquot_drop(struct inode *inode);
+int dquot_drop_locked(struct inode *inode);
+struct dquot *dqget(struct super_block *sb, unsigned int id, int type);
+void dqput(struct dquot *dquot);
+int dquot_is_cached(struct super_block *sb, unsigned int id, int type);
 
 int dquot_alloc_space(struct inode *inode, qsize_t number, int prealloc);
 int dquot_alloc_inode(const struct inode *inode, qsize_t number);
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 17/29] quota: Implement function for scanning active dquots
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (15 preceding siblings ...)
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 16/29] quota: Add helpers to allow ocfs2 specific quota initialization, freeing and recovery Jan Kara
@ 2008-10-24 22:08 ` Jan Kara
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 18/29] mm: Export pdflush_operation() Jan Kara
                   ` (11 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:08 UTC (permalink / raw)
  To: ocfs2-devel

OCFS2 needs to scan all active dquots once in a while and sync quota
information among cluster nodes. Provide a helper function for it so
that it does not have to reimplement internally a list which VFS
already has. Moreover this function is probably going to be useful
for other clustered filesystems if they decide to use VFS quotas.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/dquot.c               |   36 ++++++++++++++++++++++++++++++++++++
 include/linux/quotaops.h |    3 +++
 2 files changed, 39 insertions(+), 0 deletions(-)

diff --git a/fs/dquot.c b/fs/dquot.c
index f6bcacd..7be2a01 100644
--- a/fs/dquot.c
+++ b/fs/dquot.c
@@ -476,6 +476,41 @@ restart:
 	spin_unlock(&dq_list_lock);
 }
 
+/* Call callback for every active dquot on given filesystem */
+int dquot_scan_active(struct super_block *sb,
+		      int (*fn)(struct dquot *dquot, unsigned long priv),
+		      unsigned long priv)
+{
+	struct dquot *dquot, *old_dquot = NULL;
+	int ret = 0;
+
+	mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
+	spin_lock(&dq_list_lock);
+	list_for_each_entry(dquot, &inuse_list, dq_inuse) {
+		if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags))
+			continue;
+		if (dquot->dq_sb != sb)
+			continue;
+		/* Now we have active dquot so we can just increase use count */
+		atomic_inc(&dquot->dq_count);
+		dqstats.lookups++;
+		spin_unlock(&dq_list_lock);
+		dqput(old_dquot);
+		old_dquot = dquot;
+		ret = fn(dquot, priv);
+		if (ret < 0)
+			goto out;
+		spin_lock(&dq_list_lock);
+		/* We are safe to continue now because our dquot could not
+		 * be moved out of the inuse list while we hold the reference */
+	}
+	spin_unlock(&dq_list_lock);
+out:
+	dqput(old_dquot);
+	mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
+	return ret;
+}
+
 int vfs_quota_sync(struct super_block *sb, int type)
 {
 	struct list_head *dirty;
@@ -2314,6 +2349,7 @@ EXPORT_SYMBOL(vfs_quota_on_path);
 EXPORT_SYMBOL(vfs_quota_on_mount);
 EXPORT_SYMBOL(vfs_quota_disable);
 EXPORT_SYMBOL(vfs_quota_off);
+EXPORT_SYMBOL(dquot_scan_active);
 EXPORT_SYMBOL(vfs_quota_sync);
 EXPORT_SYMBOL(vfs_get_dqinfo);
 EXPORT_SYMBOL(vfs_set_dqinfo);
diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h
index 1f990f2..f2147eb 100644
--- a/include/linux/quotaops.h
+++ b/include/linux/quotaops.h
@@ -31,6 +31,9 @@ int dquot_drop_locked(struct inode *inode);
 struct dquot *dqget(struct super_block *sb, unsigned int id, int type);
 void dqput(struct dquot *dquot);
 int dquot_is_cached(struct super_block *sb, unsigned int id, int type);
+int dquot_scan_active(struct super_block *sb,
+		      int (*fn)(struct dquot *dquot, unsigned long priv),
+		      unsigned long priv);
 
 int dquot_alloc_space(struct inode *inode, qsize_t number, int prealloc);
 int dquot_alloc_inode(const struct inode *inode, qsize_t number);
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 18/29] mm: Export pdflush_operation()
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (16 preceding siblings ...)
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 17/29] quota: Implement function for scanning active dquots Jan Kara
@ 2008-10-24 22:08 ` Jan Kara
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 19/29] ocfs2: Fix check of return value of ocfs2_start_trans() Jan Kara
                   ` (10 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:08 UTC (permalink / raw)
  To: ocfs2-devel

OCSF2 will need to queue up work for periodic syncing of quotas
among nodes in the cluster. pdflush() is good thread for this so
export it's controlling function so that OCFS2 can use it.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 mm/pdflush.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/mm/pdflush.c b/mm/pdflush.c
index 0cbe0c6..1127615 100644
--- a/mm/pdflush.c
+++ b/mm/pdflush.c
@@ -223,6 +223,7 @@ int pdflush_operation(void (*fn)(unsigned long), unsigned long arg0)
 
 	return ret;
 }
+EXPORT_SYMBOL(pdflush_operation);
 
 static void start_one_pdflush_thread(void)
 {
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 19/29] ocfs2: Fix check of return value of ocfs2_start_trans()
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (17 preceding siblings ...)
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 18/29] mm: Export pdflush_operation() Jan Kara
@ 2008-10-24 22:08 ` Jan Kara
  2008-10-30 23:34   ` Mark Fasheh
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 20/29] ocfs2: Support nested transactions Jan Kara
                   ` (9 subsequent siblings)
  28 siblings, 1 reply; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:08 UTC (permalink / raw)
  To: ocfs2-devel

On failure, ocfs2_start_trans() returns values like ERR_PTR(-ENOMEM).
Thus checks for !handle are wrong. Fix them to use IS_ERR().

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/ocfs2/file.c |   20 ++++++++++----------
 1 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index 8d3225a..e135da1 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -247,8 +247,8 @@ int ocfs2_update_inode_atime(struct inode *inode,
 	mlog_entry_void();
 
 	handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
-	if (handle == NULL) {
-		ret = -ENOMEM;
+	if (IS_ERR(handle)) {
+		ret = PTR_ERR(handle);
 		mlog_errno(ret);
 		goto out;
 	}
@@ -312,8 +312,8 @@ static int ocfs2_simple_size_update(struct inode *inode,
 	handle_t *handle = NULL;
 
 	handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
-	if (handle == NULL) {
-		ret = -ENOMEM;
+	if (IS_ERR(handle)) {
+		ret = PTR_ERR(handle);
 		mlog_errno(ret);
 		goto out;
 	}
@@ -1056,8 +1056,8 @@ static int __ocfs2_write_remove_suid(struct inode *inode,
 		   (unsigned long long)OCFS2_I(inode)->ip_blkno, inode->i_mode);
 
 	handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
-	if (handle == NULL) {
-		ret = -ENOMEM;
+	if (IS_ERR(handle)) {
+		ret = PTR_ERR(handle);
 		mlog_errno(ret);
 		goto out;
 	}
@@ -1260,8 +1260,8 @@ static int __ocfs2_remove_inode_range(struct inode *inode,
 	}
 
 	handle = ocfs2_start_trans(osb, OCFS2_REMOVE_EXTENT_CREDITS);
-	if (handle == NULL) {
-		ret = -ENOMEM;
+	if (IS_ERR(handle)) {
+		ret = PTR_ERR(handle);
 		mlog_errno(ret);
 		goto out;
 	}
@@ -1353,8 +1353,8 @@ static int ocfs2_zero_partial_clusters(struct inode *inode,
 		goto out;
 
 	handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
-	if (handle == NULL) {
-		ret = -ENOMEM;
+	if (IS_ERR(handle)) {
+		ret = PTR_ERR(handle);
 		mlog_errno(ret);
 		goto out;
 	}
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 20/29] ocfs2: Support nested transactions
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (18 preceding siblings ...)
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 19/29] ocfs2: Fix check of return value of ocfs2_start_trans() Jan Kara
@ 2008-10-24 22:08 ` Jan Kara
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 21/29] ocfs2: Fix checking of return value of new_inode() Jan Kara
                   ` (8 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:08 UTC (permalink / raw)
  To: ocfs2-devel

OCFS2 can easily support nested transactions. We just have to
take care and not spoil statistics acquire semaphore unnecessarily.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/ocfs2/journal.c |   14 +++++++-------
 1 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c
index 81e4067..f3d7c15 100644
--- a/fs/ocfs2/journal.c
+++ b/fs/ocfs2/journal.c
@@ -256,11 +256,9 @@ handle_t *ocfs2_start_trans(struct ocfs2_super *osb, int max_buffs)
 	BUG_ON(osb->journal->j_state == OCFS2_JOURNAL_FREE);
 	BUG_ON(max_buffs <= 0);
 
-	/* JBD might support this, but our journalling code doesn't yet. */
-	if (journal_current_handle()) {
-		mlog(ML_ERROR, "Recursive transaction attempted!\n");
-		BUG();
-	}
+	/* Nested transaction? Just return the handle... */
+	if (journal_current_handle())
+		return jbd2_journal_start(journal, max_buffs);
 
 	down_read(&osb->journal->j_trans_barrier);
 
@@ -285,16 +283,18 @@ handle_t *ocfs2_start_trans(struct ocfs2_super *osb, int max_buffs)
 int ocfs2_commit_trans(struct ocfs2_super *osb,
 		       handle_t *handle)
 {
-	int ret;
+	int ret, nested;
 	struct ocfs2_journal *journal = osb->journal;
 
 	BUG_ON(!handle);
 
+	nested = handle->h_ref > 1;
 	ret = jbd2_journal_stop(handle);
 	if (ret < 0)
 		mlog_errno(ret);
 
-	up_read(&journal->j_trans_barrier);
+	if (!nested)
+		up_read(&journal->j_trans_barrier);
 
 	return ret;
 }
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 21/29] ocfs2: Fix checking of return value of new_inode()
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (19 preceding siblings ...)
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 20/29] ocfs2: Support nested transactions Jan Kara
@ 2008-10-24 22:08 ` Jan Kara
  2008-10-30 23:51   ` Mark Fasheh
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 22/29] ocfs2: Let inode be really deleted when ocfs2_mknod_locked() fails Jan Kara
                   ` (7 subsequent siblings)
  28 siblings, 1 reply; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:08 UTC (permalink / raw)
  To: ocfs2-devel

new_inode() does not return ERR_PTR() but NULL in case of failure. Correct
checking of the return value.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/ocfs2/namei.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c
index 485a6aa..f594f30 100644
--- a/fs/ocfs2/namei.c
+++ b/fs/ocfs2/namei.c
@@ -378,8 +378,8 @@ static int ocfs2_mknod_locked(struct ocfs2_super *osb,
 	}
 
 	inode = new_inode(dir->i_sb);
-	if (IS_ERR(inode)) {
-		status = PTR_ERR(inode);
+	if (!inode) {
+		status = -ENOMEM;
 		mlog(ML_ERROR, "new_inode failed!\n");
 		goto leave;
 	}
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 22/29] ocfs2: Let inode be really deleted when ocfs2_mknod_locked() fails
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (20 preceding siblings ...)
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 21/29] ocfs2: Fix checking of return value of new_inode() Jan Kara
@ 2008-10-24 22:08 ` Jan Kara
  2008-10-30 23:52   ` Mark Fasheh
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 23/29] ocfs2: Assign feature bits and system inodes to quota feature and quota files Jan Kara
                   ` (6 subsequent siblings)
  28 siblings, 1 reply; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:08 UTC (permalink / raw)
  To: ocfs2-devel

We forgot to set i_nlink to 0 when returning due to error from ocfs2_mknod_locked()
and thus inode was not properly released via ocfs2_delete_inode() (e.g. claimed
space was not released). Fix it.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/ocfs2/namei.c |    4 +++-
 1 files changed, 3 insertions(+), 1 deletions(-)

diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c
index f594f30..f4967e6 100644
--- a/fs/ocfs2/namei.c
+++ b/fs/ocfs2/namei.c
@@ -491,8 +491,10 @@ leave:
 			brelse(*new_fe_bh);
 			*new_fe_bh = NULL;
 		}
-		if (inode)
+		if (inode) {
+			clear_nlink(inode);
 			iput(inode);
+		}
 	}
 
 	mlog_exit(status);
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 23/29] ocfs2: Assign feature bits and system inodes to quota feature and quota files
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (21 preceding siblings ...)
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 22/29] ocfs2: Let inode be really deleted when ocfs2_mknod_locked() fails Jan Kara
@ 2008-10-24 22:08 ` Jan Kara
  2008-10-28 22:16   ` Joel Becker
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 24/29] ocfs2: Mark system files as not subject to quota accounting Jan Kara
                   ` (5 subsequent siblings)
  28 siblings, 1 reply; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:08 UTC (permalink / raw)
  To: ocfs2-devel

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/ocfs2/inode.c    |    2 ++
 fs/ocfs2/ocfs2_fs.h |   23 ++++++++++++++++++++---
 fs/ocfs2/super.c    |   17 +++++++++++++++++
 3 files changed, 39 insertions(+), 3 deletions(-)

diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c
index 4903688..da3a360 100644
--- a/fs/ocfs2/inode.c
+++ b/fs/ocfs2/inode.c
@@ -292,6 +292,8 @@ int ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe,
 		mlog(0, "local alloc inode: i_ino=%lu\n", inode->i_ino);
 	} else if (fe->i_flags & cpu_to_le32(OCFS2_BITMAP_FL)) {
 		OCFS2_I(inode)->ip_flags |= OCFS2_INODE_BITMAP;
+	} else if (fe->i_flags & cpu_to_le32(OCFS2_QUOTA_FL)) {
+		inode->i_flags |= S_NOQUOTA;
 	} else if (fe->i_flags & cpu_to_le32(OCFS2_SUPER_BLOCK_FL)) {
 		mlog(0, "superblock inode: i_ino=%lu\n", inode->i_ino);
 		/* we can't actually hit this as read_inode can't
diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h
index f24ce3d..dd17137 100644
--- a/fs/ocfs2/ocfs2_fs.h
+++ b/fs/ocfs2/ocfs2_fs.h
@@ -93,7 +93,9 @@
 					 | OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP \
 					 | OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK \
 					 | OCFS2_FEATURE_INCOMPAT_XATTR)
-#define OCFS2_FEATURE_RO_COMPAT_SUPP	OCFS2_FEATURE_RO_COMPAT_UNWRITTEN
+#define OCFS2_FEATURE_RO_COMPAT_SUPP	(OCFS2_FEATURE_RO_COMPAT_UNWRITTEN \
+					 | OCFS2_FEATURE_RO_COMPAT_USRQUOTA \
+					 | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)
 
 /*
  * Heartbeat-only devices are missing journals and other files.  The
@@ -157,6 +159,12 @@
  */
 #define OCFS2_FEATURE_RO_COMPAT_UNWRITTEN	0x0001
 
+/*
+ * Maintain quota information for this filesystem
+ */
+#define OCFS2_FEATURE_RO_COMPAT_USRQUOTA	0x0002
+#define OCFS2_FEATURE_RO_COMPAT_GRPQUOTA	0x0004
+
 /* The byte offset of the first backup block will be 1G.
  * The following will be 4G, 16G, 64G, 256G and 1T.
  */
@@ -186,6 +194,7 @@
 #define OCFS2_HEARTBEAT_FL	(0x00000200)	/* Heartbeat area */
 #define OCFS2_CHAIN_FL		(0x00000400)	/* Chain allocator */
 #define OCFS2_DEALLOC_FL	(0x00000800)	/* Truncate log */
+#define OCFS2_QUOTA_FL		(0x00001000)	/* Quota file */
 
 /*
  * Flags on ocfs2_dinode.i_dyn_features
@@ -323,13 +332,17 @@ enum {
 #define OCFS2_FIRST_ONLINE_SYSTEM_INODE SLOT_MAP_SYSTEM_INODE
 	HEARTBEAT_SYSTEM_INODE,
 	GLOBAL_BITMAP_SYSTEM_INODE,
-#define OCFS2_LAST_GLOBAL_SYSTEM_INODE GLOBAL_BITMAP_SYSTEM_INODE
+	USER_QUOTA_SYSTEM_INODE,
+	GROUP_QUOTA_SYSTEM_INODE,
+#define OCFS2_LAST_GLOBAL_SYSTEM_INODE GROUP_QUOTA_SYSTEM_INODE
 	ORPHAN_DIR_SYSTEM_INODE,
 	EXTENT_ALLOC_SYSTEM_INODE,
 	INODE_ALLOC_SYSTEM_INODE,
 	JOURNAL_SYSTEM_INODE,
 	LOCAL_ALLOC_SYSTEM_INODE,
 	TRUNCATE_LOG_SYSTEM_INODE,
+	LOCAL_USER_QUOTA_SYSTEM_INODE,
+	LOCAL_GROUP_QUOTA_SYSTEM_INODE,
 	NUM_SYSTEM_INODES
 };
 
@@ -343,6 +356,8 @@ static struct ocfs2_system_inode_info ocfs2_system_inodes[NUM_SYSTEM_INODES] = {
 	[SLOT_MAP_SYSTEM_INODE]			= { "slot_map", 0, S_IFREG | 0644 },
 	[HEARTBEAT_SYSTEM_INODE]		= { "heartbeat", OCFS2_HEARTBEAT_FL, S_IFREG | 0644 },
 	[GLOBAL_BITMAP_SYSTEM_INODE]		= { "global_bitmap", 0, S_IFREG | 0644 },
+	[USER_QUOTA_SYSTEM_INODE]		= { "aquota.user", OCFS2_QUOTA_FL, S_IFREG | 0644 },
+	[GROUP_QUOTA_SYSTEM_INODE]		= { "aquota.group", OCFS2_QUOTA_FL, S_IFREG | 0644 },
 
 	/* Slot-specific system inodes (one copy per slot) */
 	[ORPHAN_DIR_SYSTEM_INODE]		= { "orphan_dir:%04d", 0, S_IFDIR | 0755 },
@@ -350,7 +365,9 @@ static struct ocfs2_system_inode_info ocfs2_system_inodes[NUM_SYSTEM_INODES] = {
 	[INODE_ALLOC_SYSTEM_INODE]		= { "inode_alloc:%04d", OCFS2_BITMAP_FL | OCFS2_CHAIN_FL, S_IFREG | 0644 },
 	[JOURNAL_SYSTEM_INODE]			= { "journal:%04d", OCFS2_JOURNAL_FL, S_IFREG | 0644 },
 	[LOCAL_ALLOC_SYSTEM_INODE]		= { "local_alloc:%04d", OCFS2_BITMAP_FL | OCFS2_LOCAL_ALLOC_FL, S_IFREG | 0644 },
-	[TRUNCATE_LOG_SYSTEM_INODE]		= { "truncate_log:%04d", OCFS2_DEALLOC_FL, S_IFREG | 0644 }
+	[TRUNCATE_LOG_SYSTEM_INODE]		= { "truncate_log:%04d", OCFS2_DEALLOC_FL, S_IFREG | 0644 },
+	[LOCAL_USER_QUOTA_SYSTEM_INODE]		= { "aquota%04d.user", OCFS2_QUOTA_FL, S_IFREG | 0644 },
+	[LOCAL_GROUP_QUOTA_SYSTEM_INODE]	= { "aquota%04d.group", OCFS2_QUOTA_FL, S_IFREG | 0644 },
 };
 
 /* Parameter passed from mount.ocfs2 to module */
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
index 304b63a..4712bc7 100644
--- a/fs/ocfs2/super.c
+++ b/fs/ocfs2/super.c
@@ -221,6 +221,19 @@ static int ocfs2_sync_fs(struct super_block *sb, int wait)
 	return 0;
 }
 
+static int ocfs2_need_system_inode(struct ocfs2_super *osb, int ino)
+{
+	if (!OCFS2_HAS_RO_COMPAT_FEATURE(osb->sb, OCFS2_FEATURE_RO_COMPAT_USRQUOTA)
+	    && (ino == USER_QUOTA_SYSTEM_INODE
+		|| ino == LOCAL_USER_QUOTA_SYSTEM_INODE))
+		return 0;
+	if (!OCFS2_HAS_RO_COMPAT_FEATURE(osb->sb, OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)
+	    && (ino == GROUP_QUOTA_SYSTEM_INODE
+		|| ino == LOCAL_GROUP_QUOTA_SYSTEM_INODE))
+		return 0;
+	return 1;
+}
+
 static int ocfs2_init_global_system_inodes(struct ocfs2_super *osb)
 {
 	struct inode *new = NULL;
@@ -247,6 +260,8 @@ static int ocfs2_init_global_system_inodes(struct ocfs2_super *osb)
 
 	for (i = OCFS2_FIRST_ONLINE_SYSTEM_INODE;
 	     i <= OCFS2_LAST_GLOBAL_SYSTEM_INODE; i++) {
+		if (!ocfs2_need_system_inode(osb, i))
+			continue;
 		new = ocfs2_get_system_file_inode(osb, i, osb->slot_num);
 		if (!new) {
 			ocfs2_release_system_inodes(osb);
@@ -277,6 +292,8 @@ static int ocfs2_init_local_system_inodes(struct ocfs2_super *osb)
 	for (i = OCFS2_LAST_GLOBAL_SYSTEM_INODE + 1;
 	     i < NUM_SYSTEM_INODES;
 	     i++) {
+		if (!ocfs2_need_system_inode(osb, i))
+			continue;
 		new = ocfs2_get_system_file_inode(osb, i, osb->slot_num);
 		if (!new) {
 			ocfs2_release_system_inodes(osb);
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 24/29] ocfs2: Mark system files as not subject to quota accounting
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (22 preceding siblings ...)
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 23/29] ocfs2: Assign feature bits and system inodes to quota feature and quota files Jan Kara
@ 2008-10-24 22:08 ` Jan Kara
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 25/29] ocfs2: Implementation of local and global quota file handling Jan Kara
                   ` (4 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:08 UTC (permalink / raw)
  To: ocfs2-devel

Mark system files as not subject to quota accounting. This prevents
possible recursions into quota code and thus deadlocks.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/ocfs2/inode.c |    4 +++-
 1 files changed, 3 insertions(+), 1 deletions(-)

diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c
index da3a360..738b63f 100644
--- a/fs/ocfs2/inode.c
+++ b/fs/ocfs2/inode.c
@@ -284,8 +284,10 @@ int ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe,
 
 	inode->i_nlink = le16_to_cpu(fe->i_links_count);
 
-	if (fe->i_flags & cpu_to_le32(OCFS2_SYSTEM_FL))
+	if (fe->i_flags & cpu_to_le32(OCFS2_SYSTEM_FL)) {
 		OCFS2_I(inode)->ip_flags |= OCFS2_INODE_SYSTEM_FILE;
+		inode->i_flags |= S_NOQUOTA;
+	}
 
 	if (fe->i_flags & cpu_to_le32(OCFS2_LOCAL_ALLOC_FL)) {
 		OCFS2_I(inode)->ip_flags |= OCFS2_INODE_BITMAP;
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 25/29] ocfs2: Implementation of local and global quota file handling
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (23 preceding siblings ...)
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 24/29] ocfs2: Mark system files as not subject to quota accounting Jan Kara
@ 2008-10-24 22:08 ` Jan Kara
  2008-10-28 19:07   ` Joel Becker
                     ` (2 more replies)
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 26/29] ocfs2: Add quota calls for allocation and freeing of inodes and space Jan Kara
                   ` (3 subsequent siblings)
  28 siblings, 3 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:08 UTC (permalink / raw)
  To: ocfs2-devel

For each quota type each node has local quota file. In this file it stores
changes users have made to disk usage via this node. Once in a while this
information is synced to global file (and thus with other nodes) so that
limits enforcement at least aproximately works.

Global quota files contain all the information about usage and limits. It's
mostly handled by the generic VFS code (which implements a trie of structures
inside a quota file). We only have to provide functions to convert structures
from on-disk format to in-memory one. We also have to provide wrappers for
various quota functions starting transactions and acquiring necessary cluster
locks before the actual IO is really started.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/ocfs2/Makefile          |    2 +
 fs/ocfs2/cluster/masklog.h |    1 +
 fs/ocfs2/dir.c             |    4 +-
 fs/ocfs2/dlmglue.c         |  137 +++++++
 fs/ocfs2/dlmglue.h         |   17 +
 fs/ocfs2/file.c            |    6 +-
 fs/ocfs2/file.h            |    3 +
 fs/ocfs2/inode.h           |    2 +
 fs/ocfs2/ocfs2_fs.h        |   95 +++++
 fs/ocfs2/ocfs2_lockid.h    |    5 +
 fs/ocfs2/quota.h           |   97 +++++
 fs/ocfs2/quota_global.c    |  863 ++++++++++++++++++++++++++++++++++++++++++++
 fs/ocfs2/quota_local.c     |  833 ++++++++++++++++++++++++++++++++++++++++++
 fs/ocfs2/super.c           |   38 ++-
 14 files changed, 2096 insertions(+), 7 deletions(-)
 create mode 100644 fs/ocfs2/quota.h
 create mode 100644 fs/ocfs2/quota_global.c
 create mode 100644 fs/ocfs2/quota_local.c

diff --git a/fs/ocfs2/Makefile b/fs/ocfs2/Makefile
index 589dcdf..11f14e1 100644
--- a/fs/ocfs2/Makefile
+++ b/fs/ocfs2/Makefile
@@ -35,6 +35,8 @@ ocfs2-objs := \
 	sysfile.o 		\
 	uptodate.o		\
 	ver.o			\
+	quota_local.o		\
+	quota_global.o		\
 	xattr.o
 
 ocfs2_stackglue-objs := stackglue.o
diff --git a/fs/ocfs2/cluster/masklog.h b/fs/ocfs2/cluster/masklog.h
index 57670c6..7e72a81 100644
--- a/fs/ocfs2/cluster/masklog.h
+++ b/fs/ocfs2/cluster/masklog.h
@@ -113,6 +113,7 @@
 #define ML_QUORUM	0x0000000008000000ULL /* net connection quorum */
 #define ML_EXPORT	0x0000000010000000ULL /* ocfs2 export operations */
 #define ML_XATTR	0x0000000020000000ULL /* ocfs2 extended attributes */
+#define ML_QUOTA	0x0000000040000000ULL /* ocfs2 quota operations */
 /* bits that are infrequently given and frequently matched in the high word */
 #define ML_ERROR	0x0000000100000000ULL /* sent to KERN_ERR */
 #define ML_NOTICE	0x0000000200000000ULL /* setn to KERN_NOTICE */
diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c
index 026e6eb..084ba9c 100644
--- a/fs/ocfs2/dir.c
+++ b/fs/ocfs2/dir.c
@@ -82,8 +82,8 @@ static int ocfs2_do_extend_dir(struct super_block *sb,
 			       struct ocfs2_alloc_context *meta_ac,
 			       struct buffer_head **new_bh);
 
-static struct buffer_head *ocfs2_bread(struct inode *inode,
-				       int block, int *err, int reada)
+struct buffer_head *ocfs2_bread(struct inode *inode,
+				int block, int *err, int reada)
 {
 	struct buffer_head *bh = NULL;
 	int tmperr;
diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c
index ec68442..eab90e8 100644
--- a/fs/ocfs2/dlmglue.c
+++ b/fs/ocfs2/dlmglue.c
@@ -32,6 +32,7 @@
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
 #include <linux/time.h>
+#include <linux/quotaops.h>
 
 #define MLOG_MASK_PREFIX ML_DLM_GLUE
 #include <cluster/masklog.h>
@@ -51,6 +52,7 @@
 #include "slot_map.h"
 #include "super.h"
 #include "uptodate.h"
+#include "quota.h"
 
 #include "buffer_head_io.h"
 
@@ -68,6 +70,7 @@ struct ocfs2_mask_waiter {
 static struct ocfs2_super *ocfs2_get_dentry_osb(struct ocfs2_lock_res *lockres);
 static struct ocfs2_super *ocfs2_get_inode_osb(struct ocfs2_lock_res *lockres);
 static struct ocfs2_super *ocfs2_get_file_osb(struct ocfs2_lock_res *lockres);
+static struct ocfs2_super *ocfs2_get_qinfo_osb(struct ocfs2_lock_res *lockres);
 
 /*
  * Return value from ->downconvert_worker functions.
@@ -102,6 +105,7 @@ static int ocfs2_dentry_convert_worker(struct ocfs2_lock_res *lockres,
 static void ocfs2_dentry_post_unlock(struct ocfs2_super *osb,
 				     struct ocfs2_lock_res *lockres);
 
+static void ocfs2_set_qinfo_lvb(struct ocfs2_lock_res *lockres);
 
 #define mlog_meta_lvb(__level, __lockres) ocfs2_dump_meta_lvb_info(__level, __PRETTY_FUNCTION__, __LINE__, __lockres)
 
@@ -258,6 +262,12 @@ static struct ocfs2_lock_res_ops ocfs2_flock_lops = {
 	.flags		= 0,
 };
 
+static struct ocfs2_lock_res_ops ocfs2_qinfo_lops = {
+	.set_lvb	= ocfs2_set_qinfo_lvb,
+	.get_osb	= ocfs2_get_qinfo_osb,
+	.flags		= LOCK_TYPE_REQUIRES_REFRESH | LOCK_TYPE_USES_LVB,
+};
+
 static inline int ocfs2_is_inode_lock(struct ocfs2_lock_res *lockres)
 {
 	return lockres->l_type == OCFS2_LOCK_TYPE_META ||
@@ -279,6 +289,13 @@ static inline struct ocfs2_dentry_lock *ocfs2_lock_res_dl(struct ocfs2_lock_res
 	return (struct ocfs2_dentry_lock *)lockres->l_priv;
 }
 
+static inline struct ocfs2_mem_dqinfo *ocfs2_lock_res_qinfo(struct ocfs2_lock_res *lockres)
+{
+	BUG_ON(lockres->l_type != OCFS2_LOCK_TYPE_QINFO);
+
+	return (struct ocfs2_mem_dqinfo *)lockres->l_priv;
+}
+
 static inline struct ocfs2_super *ocfs2_get_lockres_osb(struct ocfs2_lock_res *lockres)
 {
 	if (lockres->l_ops->get_osb)
@@ -507,6 +524,13 @@ static struct ocfs2_super *ocfs2_get_inode_osb(struct ocfs2_lock_res *lockres)
 	return OCFS2_SB(inode->i_sb);
 }
 
+static struct ocfs2_super *ocfs2_get_qinfo_osb(struct ocfs2_lock_res *lockres)
+{
+	struct ocfs2_mem_dqinfo *info = lockres->l_priv;
+
+	return OCFS2_SB(info->dqi_gi.dqi_sb);
+}
+
 static struct ocfs2_super *ocfs2_get_file_osb(struct ocfs2_lock_res *lockres)
 {
 	struct ocfs2_file_private *fp = lockres->l_priv;
@@ -609,6 +633,17 @@ void ocfs2_file_lock_res_init(struct ocfs2_lock_res *lockres,
 	lockres->l_flags |= OCFS2_LOCK_NOCACHE;
 }
 
+void ocfs2_qinfo_lock_res_init(struct ocfs2_lock_res *lockres,
+			       struct ocfs2_mem_dqinfo *info)
+{
+	ocfs2_lock_res_init_once(lockres);
+	ocfs2_build_lock_name(OCFS2_LOCK_TYPE_QINFO, info->dqi_gi.dqi_type,
+			      0, lockres->l_name);
+	ocfs2_lock_res_init_common(OCFS2_SB(info->dqi_gi.dqi_sb), lockres,
+				   OCFS2_LOCK_TYPE_QINFO, &ocfs2_qinfo_lops,
+				   info);
+}
+
 void ocfs2_lock_res_free(struct ocfs2_lock_res *res)
 {
 	mlog_entry_void();
@@ -3450,6 +3485,108 @@ static int ocfs2_dentry_convert_worker(struct ocfs2_lock_res *lockres,
 	return UNBLOCK_CONTINUE_POST;
 }
 
+static void ocfs2_set_qinfo_lvb(struct ocfs2_lock_res *lockres)
+{
+	struct ocfs2_qinfo_lvb *lvb;
+	struct ocfs2_mem_dqinfo *oinfo = ocfs2_lock_res_qinfo(lockres);
+	struct mem_dqinfo *info = sb_dqinfo(oinfo->dqi_gi.dqi_sb,
+					    oinfo->dqi_gi.dqi_type);
+
+	mlog_entry_void();
+
+	lvb = (struct ocfs2_qinfo_lvb *)ocfs2_dlm_lvb(&lockres->l_lksb);
+	lvb->lvb_version   = OCFS2_LVB_VERSION;
+	lvb->lvb_bgrace = cpu_to_be32(info->dqi_bgrace);
+	lvb->lvb_igrace = cpu_to_be32(info->dqi_igrace);
+	lvb->lvb_syncms = cpu_to_be32(oinfo->dqi_syncms);
+	lvb->lvb_blocks = cpu_to_be32(oinfo->dqi_gi.dqi_blocks);
+	lvb->lvb_free_blk = cpu_to_be32(oinfo->dqi_gi.dqi_free_blk);
+	lvb->lvb_free_entry = cpu_to_be32(oinfo->dqi_gi.dqi_free_entry);
+
+	mlog_exit_void();
+}
+
+void ocfs2_qinfo_unlock(struct ocfs2_mem_dqinfo *oinfo, int ex)
+{
+	struct ocfs2_lock_res *lockres = &oinfo->dqi_gqlock;
+	struct ocfs2_super *osb = OCFS2_SB(oinfo->dqi_gi.dqi_sb);
+	int level = ex ? DLM_LOCK_EX : DLM_LOCK_PR;
+
+	mlog_entry_void();
+	if (!ocfs2_is_hard_readonly(osb) && !ocfs2_mount_local(osb))
+		ocfs2_cluster_unlock(osb, lockres, level);
+	mlog_exit_void();
+}
+
+/* Lock quota info, this function expects at least shared lock on the quota file
+ * so that we can safely refresh quota info from disk. */
+int ocfs2_qinfo_lock(struct ocfs2_mem_dqinfo *oinfo, int ex)
+{
+	struct mem_dqinfo *info = sb_dqinfo(oinfo->dqi_gi.dqi_sb,
+					    oinfo->dqi_gi.dqi_type);
+	struct ocfs2_lock_res *lockres = &oinfo->dqi_gqlock;
+	struct ocfs2_super *osb = OCFS2_SB(oinfo->dqi_gi.dqi_sb);
+	struct ocfs2_qinfo_lvb *lvb;
+	int level = ex ? DLM_LOCK_EX : DLM_LOCK_PR;
+	int status = 0;
+	struct buffer_head *bh;
+	struct ocfs2_global_disk_dqinfo *gdinfo;
+
+	mlog_entry_void();
+
+	/* We'll allow faking a readonly metadata lock for
+	 * rodevices. */
+	if (ocfs2_is_hard_readonly(osb)) {
+		if (ex)
+			status = -EROFS;
+		goto bail;
+	}
+	if (ocfs2_mount_local(osb))
+		goto bail;
+
+	status = ocfs2_cluster_lock(osb, lockres, level, 0, 0);
+	if (status < 0) {
+		mlog_errno(status);
+		goto bail;
+	}
+	if (!ocfs2_should_refresh_lock_res(lockres))
+		goto bail;
+	/* OK, we have the lock but we need to refresh the quota info */
+	lvb = ocfs2_dlm_lvb(&lockres->l_lksb);
+	if (lvb->lvb_version == OCFS2_LVB_VERSION) {
+		info->dqi_bgrace = be32_to_cpu(lvb->lvb_bgrace);
+		info->dqi_igrace = be32_to_cpu(lvb->lvb_igrace);
+		oinfo->dqi_syncms = be32_to_cpu(lvb->lvb_syncms);
+		oinfo->dqi_gi.dqi_blocks = be32_to_cpu(lvb->lvb_blocks);
+		oinfo->dqi_gi.dqi_free_blk = be32_to_cpu(lvb->lvb_free_blk);
+		oinfo->dqi_gi.dqi_free_entry =
+					be32_to_cpu(lvb->lvb_free_entry);
+	} else {
+		bh = ocfs2_read_quota_block(oinfo->dqi_gqinode, 0, &status);
+		if (!bh) {
+			ocfs2_qinfo_unlock(oinfo, ex);
+			mlog_errno(status);
+			goto bail_refresh;
+		}
+		gdinfo = (struct ocfs2_global_disk_dqinfo *)
+					(bh->b_data + OCFS2_GLOBAL_INFO_OFF);
+		info->dqi_bgrace = le32_to_cpu(gdinfo->dqi_bgrace);
+		info->dqi_igrace = le32_to_cpu(gdinfo->dqi_igrace);
+		oinfo->dqi_syncms = le32_to_cpu(gdinfo->dqi_syncms);
+		oinfo->dqi_gi.dqi_blocks = le32_to_cpu(gdinfo->dqi_blocks);
+		oinfo->dqi_gi.dqi_free_blk = le32_to_cpu(gdinfo->dqi_free_blk);
+		oinfo->dqi_gi.dqi_free_entry =
+					le32_to_cpu(gdinfo->dqi_free_entry);
+		brelse(bh);
+		ocfs2_track_lock_refresh(lockres);
+	}
+bail_refresh:
+	ocfs2_complete_lock_res_refresh(lockres, status);
+bail:
+	mlog_exit(status);
+	return status;
+}
+
 /*
  * This is the filesystem locking protocol.  It provides the lock handling
  * hooks for the underlying DLM.  It has a maximum version number.
diff --git a/fs/ocfs2/dlmglue.h b/fs/ocfs2/dlmglue.h
index 2bb01f0..6a34048 100644
--- a/fs/ocfs2/dlmglue.h
+++ b/fs/ocfs2/dlmglue.h
@@ -49,6 +49,17 @@ struct ocfs2_meta_lvb {
 	__be32       lvb_reserved2;
 };
 
+struct ocfs2_qinfo_lvb {
+	__u8	lvb_version;
+	__u8	lvb_reserved[3];
+	__be32	lvb_bgrace;
+	__be32	lvb_igrace;
+	__be32	lvb_syncms;
+	__be32	lvb_blocks;
+	__be32	lvb_free_blk;
+	__be32	lvb_free_entry;
+};
+
 /* ocfs2_inode_lock_full() 'arg_flags' flags */
 /* don't wait on recovery. */
 #define OCFS2_META_LOCK_RECOVERY	(0x01)
@@ -69,6 +80,9 @@ void ocfs2_dentry_lock_res_init(struct ocfs2_dentry_lock *dl,
 struct ocfs2_file_private;
 void ocfs2_file_lock_res_init(struct ocfs2_lock_res *lockres,
 			      struct ocfs2_file_private *fp);
+struct ocfs2_mem_dqinfo;
+void ocfs2_qinfo_lock_res_init(struct ocfs2_lock_res *lockres,
+                               struct ocfs2_mem_dqinfo *info);
 void ocfs2_lock_res_free(struct ocfs2_lock_res *res);
 int ocfs2_create_new_inode_locks(struct inode *inode);
 int ocfs2_drop_inode_locks(struct inode *inode);
@@ -103,6 +117,9 @@ int ocfs2_dentry_lock(struct dentry *dentry, int ex);
 void ocfs2_dentry_unlock(struct dentry *dentry, int ex);
 int ocfs2_file_lock(struct file *file, int ex, int trylock);
 void ocfs2_file_unlock(struct file *file);
+int ocfs2_qinfo_lock(struct ocfs2_mem_dqinfo *oinfo, int ex);
+void ocfs2_qinfo_unlock(struct ocfs2_mem_dqinfo *oinfo, int ex);
+
 
 void ocfs2_mark_lockres_freeing(struct ocfs2_lock_res *lockres);
 void ocfs2_simple_drop_lockres(struct ocfs2_super *osb,
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index e135da1..9af16e0 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -303,9 +303,9 @@ bail:
 	return status;
 }
 
-static int ocfs2_simple_size_update(struct inode *inode,
-				    struct buffer_head *di_bh,
-				    u64 new_i_size)
+int ocfs2_simple_size_update(struct inode *inode,
+			     struct buffer_head *di_bh,
+			     u64 new_i_size)
 {
 	int ret;
 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
diff --git a/fs/ocfs2/file.h b/fs/ocfs2/file.h
index e92382c..172f9fb 100644
--- a/fs/ocfs2/file.h
+++ b/fs/ocfs2/file.h
@@ -51,6 +51,9 @@ int ocfs2_add_inode_data(struct ocfs2_super *osb,
 			 struct ocfs2_alloc_context *data_ac,
 			 struct ocfs2_alloc_context *meta_ac,
 			 enum ocfs2_alloc_restarted *reason_ret);
+int ocfs2_simple_size_update(struct inode *inode,
+			     struct buffer_head *di_bh,
+			     u64 new_i_size);
 int ocfs2_extend_no_holes(struct inode *inode, u64 new_i_size,
 			  u64 zero_to);
 int ocfs2_setattr(struct dentry *dentry, struct iattr *attr);
diff --git a/fs/ocfs2/inode.h b/fs/ocfs2/inode.h
index 2f37af9..e90a159 100644
--- a/fs/ocfs2/inode.h
+++ b/fs/ocfs2/inode.h
@@ -142,6 +142,8 @@ int ocfs2_mark_inode_dirty(handle_t *handle,
 			   struct buffer_head *bh);
 int ocfs2_aio_read(struct file *file, struct kiocb *req, struct iocb *iocb);
 int ocfs2_aio_write(struct file *file, struct kiocb *req, struct iocb *iocb);
+struct buffer_head *ocfs2_bread(struct inode *inode,
+				int block, int *err, int reada);
 
 void ocfs2_set_inode_flags(struct inode *inode);
 void ocfs2_get_inode_flags(struct ocfs2_inode_info *oi);
diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h
index dd17137..04bebd2 100644
--- a/fs/ocfs2/ocfs2_fs.h
+++ b/fs/ocfs2/ocfs2_fs.h
@@ -878,6 +878,101 @@ static inline int ocfs2_xattr_get_type(struct ocfs2_xattr_entry *xe)
 	return xe->xe_type & OCFS2_XATTR_TYPE_MASK;
 }
 
+/*
+ *  On disk structures for global quota file
+ */
+
+/* Magic numbers and known versions for global quota files */
+#define OCFS2_GLOBAL_QMAGICS {\
+	0x0cf52470, /* USRQUOTA */ \
+	0x0cf52471  /* GRPQUOTA */ \
+}
+
+#define OCFS2_GLOBAL_QVERSIONS {\
+	0, \
+	0, \
+}
+
+/* Generic header of all quota files */
+struct ocfs2_disk_dqheader {
+	__le32 dqh_magic;	/* Magic number identifying file */
+	__le32 dqh_version;	/* Quota format version */
+};
+
+#define OCFS2_GLOBAL_INFO_OFF (sizeof(struct ocfs2_disk_dqheader))
+
+/* Information header of global quota file (immediately follows the generic
+ * header) */
+struct ocfs2_global_disk_dqinfo {
+/*00*/	__le32 dqi_bgrace;
+	__le32 dqi_igrace;
+	__le32 dqi_syncms;
+	__le32 dqi_blocks;
+/*10*/	__le32 dqi_free_blk;
+	__le32 dqi_free_entry;
+};
+
+/* Structure with global user / group information. We reserve some space
+ * for future use. */
+struct ocfs2_global_disk_dqblk {
+/*00*/	__le32 dqb_id;          /* ID the structure belongs to */
+	__le32 dqb_use_count;   /* Number of nodes having reference to this structure */
+	__le64 dqb_ihardlimit;  /* absolute limit on allocated inodes */
+/*10*/	__le64 dqb_isoftlimit;  /* preferred inode limit */
+	__le64 dqb_curinodes;   /* current # allocated inodes */
+/*20*/	__le64 dqb_bhardlimit;  /* absolute limit on disk space */
+	__le64 dqb_bsoftlimit;  /* preferred limit on disk space */
+/*30*/	__le64 dqb_curspace;    /* current space occupied */
+	__le64 dqb_btime;       /* time limit for excessive disk use */
+/*40*/	__le64 dqb_itime;       /* time limit for excessive inode use */
+	__le64 dqb_pad1;
+/*50*/	__le64 dqb_pad2;
+};
+
+/*
+ *  On-disk structures for local quota file
+ */
+
+/* Magic numbers and known versions for local quota files */
+#define OCFS2_LOCAL_QMAGICS {\
+	0x0cf524c0, /* USRQUOTA */ \
+	0x0cf524c1  /* GRPQUOTA */ \
+}
+
+#define OCFS2_LOCAL_QVERSIONS {\
+	0, \
+	0, \
+}
+
+/* Quota flags in dqinfo header */
+#define OLQF_CLEAN	0x0001	/* Quota file is empty (this should be after\
+				 * quota has been cleanly turned off) */
+
+#define OCFS2_LOCAL_INFO_OFF (sizeof(struct ocfs2_disk_dqheader))
+
+/* Information header of local quota file (immediately follows the generic
+ * header) */
+struct ocfs2_local_disk_dqinfo {
+	__le32 dqi_flags;	/* Flags for quota file */
+	__le32 dqi_chunks;	/* Number of chunks of quota structures
+				 * with a bitmap */
+	__le32 dqi_blocks;	/* Number of blocks allocated for quota file */
+};
+
+/* Header of one chunk of a quota file */
+struct ocfs2_local_disk_chunk {
+	__le32 dqc_free;	/* Number of free entries in the bitmap */
+	u8 dqc_bitmap[0];	/* Bitmap of entries in the corresponding
+				 * chunk of quota file */
+};
+
+/* One entry in local quota file */
+struct ocfs2_local_disk_dqblk {
+/*00*/	__le64 dqb_id;		/* id this quota applies to */
+	__le64 dqb_spacemod;	/* Change in the amount of used space */
+/*10*/	__le64 dqb_inodemod;	/* Change in the amount of used inodes */
+};
+
 #ifdef __KERNEL__
 static inline int ocfs2_fast_symlink_chars(struct super_block *sb)
 {
diff --git a/fs/ocfs2/ocfs2_lockid.h b/fs/ocfs2/ocfs2_lockid.h
index 82c200f..eb6f50c 100644
--- a/fs/ocfs2/ocfs2_lockid.h
+++ b/fs/ocfs2/ocfs2_lockid.h
@@ -46,6 +46,7 @@ enum ocfs2_lock_type {
 	OCFS2_LOCK_TYPE_DENTRY,
 	OCFS2_LOCK_TYPE_OPEN,
 	OCFS2_LOCK_TYPE_FLOCK,
+	OCFS2_LOCK_TYPE_QINFO,
 	OCFS2_NUM_LOCK_TYPES
 };
 
@@ -77,6 +78,9 @@ static inline char ocfs2_lock_type_char(enum ocfs2_lock_type type)
 		case OCFS2_LOCK_TYPE_FLOCK:
 			c = 'F';
 			break;
+		case OCFS2_LOCK_TYPE_QINFO:
+			c = 'Q';
+			break;
 		default:
 			c = '\0';
 	}
@@ -95,6 +99,7 @@ static char *ocfs2_lock_type_strings[] = {
 	[OCFS2_LOCK_TYPE_DENTRY] = "Dentry",
 	[OCFS2_LOCK_TYPE_OPEN] = "Open",
 	[OCFS2_LOCK_TYPE_FLOCK] = "Flock",
+	[OCFS2_LOCK_TYPE_QINFO] = "Quota",
 };
 
 static inline const char *ocfs2_lock_type_string(enum ocfs2_lock_type type)
diff --git a/fs/ocfs2/quota.h b/fs/ocfs2/quota.h
new file mode 100644
index 0000000..87545ca
--- /dev/null
+++ b/fs/ocfs2/quota.h
@@ -0,0 +1,97 @@
+/*
+ * quota.h for OCFS2
+ *
+ * On disk quota structures for local and global quota file, in-memory
+ * structures.
+ *
+ */
+
+#ifndef _OCFS2_QUOTA_H
+#define _OCFS2_QUOTA_H
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/quota.h>
+#include <linux/list.h>
+#include <linux/dqblk_qtree.h>
+
+#include "ocfs2.h"
+
+/* Common stuff */
+/* id number of quota format */
+#define QFMT_OCFS2 3
+
+/* How many bytes to we reserve in each quota file block for our internal
+ * purposes? E.g. checksums... */
+#define OCFS2_QBLK_RESERVED_SPACE 8
+
+/*
+ * In-memory structures
+ */
+struct ocfs2_dquot {
+	struct dquot dq_dquot;	/* Generic VFS dquot */
+	loff_t dq_local_off;	/* Offset in the local quota file */
+	struct ocfs2_quota_chunk *dq_chunk;	/* Chunk dquot is in */
+	unsigned int dq_use_count;	/* Number of nodes having reference to this entry in global quota file */
+	s64 dq_origspace;	/* Last globally synced space usage */
+	s64 dq_originodes;	/* Last globally synced inode usage */
+};
+
+/* In-memory structure with quota header information */
+struct ocfs2_mem_dqinfo {
+	unsigned int dqi_type;		/* Quota type this structure describes */
+	unsigned int dqi_chunks;	/* Number of chunks in local quota file */
+	unsigned int dqi_blocks;	/* Number of blocks allocated for local quota file */
+	unsigned int dqi_syncms;	/* How often should we sync with other nodes */
+	struct list_head dqi_chunk;	/* List of chunks */
+	struct inode *dqi_gqinode;	/* Global quota file inode */
+	struct ocfs2_lock_res dqi_gqlock;	/* Lock protecting quota information structure */
+	struct buffer_head *dqi_gqi_bh;	/* Buffer head with global quota file inode - set only if inode lock is obtained */
+	int dqi_gqi_count;		/* Number of holders of dqi_gqi_bh */
+	struct buffer_head *dqi_lqi_bh;	/* Buffer head with local quota file inode */
+	struct buffer_head *dqi_ibh;	/* Buffer with information header */
+	struct qtree_mem_dqinfo dqi_gi;	/* Info about global file */
+};
+
+static inline struct ocfs2_dquot *OCFS2_DQUOT(struct dquot *dquot)
+{
+	return container_of(dquot, struct ocfs2_dquot, dq_dquot);
+}
+
+struct ocfs2_quota_chunk {
+	struct list_head qc_chunk;	/* List of quotafile chunks */
+	int qc_num;			/* Number of quota chunk */
+	struct buffer_head *qc_headerbh;	/* Buffer head with chunk header */
+};
+
+extern struct kmem_cache *ocfs2_dquot_cachep;
+extern struct kmem_cache *ocfs2_qf_chunk_cachep;
+
+extern struct qtree_fmt_operations ocfs2_global_ops;
+
+ssize_t ocfs2_quota_read(struct super_block *sb, int type, char *data,
+			 size_t len, loff_t off);
+ssize_t ocfs2_quota_write(struct super_block *sb, int type,
+			  const char *data, size_t len, loff_t off);
+int ocfs2_global_read_info(struct super_block *sb, int type);
+int ocfs2_global_write_info(struct super_block *sb, int type);
+int ocfs2_global_read_dquot(struct dquot *dquot);
+int __ocfs2_sync_dquot(struct dquot *dquot, int freeing);
+static inline int ocfs2_sync_dquot(struct dquot *dquot)
+{
+	return __ocfs2_sync_dquot(dquot, 0);
+}
+static inline int ocfs2_global_release_dquot(struct dquot *dquot)
+{
+	return __ocfs2_sync_dquot(dquot, 1);
+}
+
+int ocfs2_lock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex);
+void ocfs2_unlock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex);
+struct buffer_head *ocfs2_read_quota_block(struct inode *inode,
+					   int block, int *err);
+
+extern struct dquot_operations ocfs2_quota_operations;
+extern struct quota_format_type ocfs2_quota_format;
+
+#endif /* _OCFS2_QUOTA_H */
diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c
new file mode 100644
index 0000000..b937f07
--- /dev/null
+++ b/fs/ocfs2/quota_global.c
@@ -0,0 +1,863 @@
+/*
+ *  Implementation of operations over global quota file
+ */
+#include <linux/fs.h>
+#include <linux/quota.h>
+#include <linux/quotaops.h>
+#include <linux/dqblk_qtree.h>
+
+#define MLOG_MASK_PREFIX ML_QUOTA
+#include <cluster/masklog.h>
+
+#include "ocfs2_fs.h"
+#include "ocfs2.h"
+#include "alloc.h"
+#include "inode.h"
+#include "journal.h"
+#include "file.h"
+#include "sysfile.h"
+#include "dlmglue.h"
+#include "quota.h"
+
+static void ocfs2_global_disk2memdqb(struct dquot *dquot, void *dp)
+{
+	struct ocfs2_global_disk_dqblk *d = dp;
+	struct mem_dqblk *m = &dquot->dq_dqb;
+
+	/* Update from disk only entries not set by the admin */
+	if (!test_bit(DQ_LASTSET_B + QIF_ILIMITS_B, &dquot->dq_flags)) {
+		m->dqb_ihardlimit = le64_to_cpu(d->dqb_ihardlimit);
+		m->dqb_isoftlimit = le64_to_cpu(d->dqb_isoftlimit);
+	}
+	if (!test_bit(DQ_LASTSET_B + QIF_INODES_B, &dquot->dq_flags))
+		m->dqb_curinodes = le64_to_cpu(d->dqb_curinodes);
+	if (!test_bit(DQ_LASTSET_B + QIF_BLIMITS_B, &dquot->dq_flags)) {
+		m->dqb_bhardlimit = le64_to_cpu(d->dqb_bhardlimit);
+		m->dqb_bsoftlimit = le64_to_cpu(d->dqb_bsoftlimit);
+	}
+	if (!test_bit(DQ_LASTSET_B + QIF_SPACE_B, &dquot->dq_flags))
+		m->dqb_curspace = le64_to_cpu(d->dqb_curspace);
+	if (!test_bit(DQ_LASTSET_B + QIF_BTIME_B, &dquot->dq_flags))
+		m->dqb_btime = le64_to_cpu(d->dqb_btime);
+	if (!test_bit(DQ_LASTSET_B + QIF_ITIME_B, &dquot->dq_flags))
+		m->dqb_itime = le64_to_cpu(d->dqb_itime);
+	OCFS2_DQUOT(dquot)->dq_use_count = le32_to_cpu(d->dqb_use_count);
+}
+
+static void ocfs2_global_mem2diskdqb(void *dp, struct dquot *dquot)
+{
+	struct ocfs2_global_disk_dqblk *d = dp;
+	struct mem_dqblk *m = &dquot->dq_dqb;
+
+	d->dqb_id = cpu_to_le32(dquot->dq_id);
+	d->dqb_use_count = cpu_to_le32(OCFS2_DQUOT(dquot)->dq_use_count);
+	d->dqb_ihardlimit = cpu_to_le64(m->dqb_ihardlimit);
+	d->dqb_isoftlimit = cpu_to_le64(m->dqb_isoftlimit);
+	d->dqb_curinodes = cpu_to_le64(m->dqb_curinodes);
+	d->dqb_bhardlimit = cpu_to_le64(m->dqb_bhardlimit);
+	d->dqb_bsoftlimit = cpu_to_le64(m->dqb_bsoftlimit);
+	d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
+	d->dqb_btime = cpu_to_le64(m->dqb_btime);
+	d->dqb_itime = cpu_to_le64(m->dqb_itime);
+	d->dqb_pad1 = d->dqb_pad2 = 0;
+}
+
+static int ocfs2_global_is_id(void *dp, struct dquot *dquot)
+{
+	struct ocfs2_global_disk_dqblk *d = dp;
+	struct ocfs2_mem_dqinfo *oinfo =
+			sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+
+	if (qtree_entry_unused(&oinfo->dqi_gi, dp))
+		return 0;
+	return le32_to_cpu(d->dqb_id) == dquot->dq_id;
+}
+
+struct qtree_fmt_operations ocfs2_global_ops = {
+	.mem2disk_dqblk = ocfs2_global_mem2diskdqb,
+	.disk2mem_dqblk = ocfs2_global_disk2memdqb,
+	.is_id = ocfs2_global_is_id,
+};
+
+
+struct buffer_head *ocfs2_read_quota_block(struct inode *inode,
+					   int block, int *err)
+{
+	return ocfs2_bread(inode, block, err, 0);
+}
+
+/* Read data from global quotafile - avoid pagecache and such because we cannot
+ * afford acquiring the locks... We use quota cluster lock to serialize
+ * operations. Caller is responsible for acquiring it. */
+ssize_t ocfs2_quota_read(struct super_block *sb, int type, char *data,
+			 size_t len, loff_t off)
+{
+	struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
+	struct inode *gqinode = oinfo->dqi_gqinode;
+	loff_t i_size = i_size_read(gqinode);
+	int offset = off & (sb->s_blocksize - 1);
+	sector_t blk = off >> sb->s_blocksize_bits;
+	int err = 0;
+	struct buffer_head *bh;
+	size_t toread, tocopy;
+
+	if (off > i_size)
+		return 0;
+	if (off + len > i_size)
+		len = i_size - off;
+	toread = len;
+	while (toread > 0) {
+		tocopy = min((size_t)(sb->s_blocksize - offset), toread);
+		bh = ocfs2_read_quota_block(gqinode, blk, &err);
+		if (!bh) {
+			mlog_errno(err);
+			return err;
+		}
+		memcpy(data, bh->b_data + offset, tocopy);
+		brelse(bh);
+		offset = 0;
+		toread -= tocopy;
+		data += tocopy;
+		blk++;
+	}
+	return len;
+}
+
+/* Write to quotafile (we know the transaction is already started and has
+ * enough credits) */
+ssize_t ocfs2_quota_write(struct super_block *sb, int type,
+			  const char *data, size_t len, loff_t off)
+{
+	struct mem_dqinfo *info = sb_dqinfo(sb, type);
+	struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
+	struct inode *gqinode = oinfo->dqi_gqinode;
+	int offset = off & (sb->s_blocksize - 1);
+	sector_t blk = off >> sb->s_blocksize_bits;
+	int err = 0;
+	struct buffer_head *bh;
+	handle_t *handle = journal_current_handle();
+	size_t tocopy, towrite = len;
+
+	if (!handle) {
+		mlog(ML_ERROR, "Quota write (off=%llu, len=%llu) cancelled "
+		     "because transaction was not started.\n",
+		     (unsigned long long)off, (unsigned long long)len);
+		return -EIO;
+	}
+	mutex_lock_nested(&gqinode->i_mutex, I_MUTEX_QUOTA);
+	if (gqinode->i_size < off + len) {
+		down_write(&OCFS2_I(gqinode)->ip_alloc_sem);
+		err = ocfs2_extend_no_holes(gqinode, off + len, off);
+		up_write(&OCFS2_I(gqinode)->ip_alloc_sem);
+		if (err < 0)
+			goto out;
+		err = ocfs2_simple_size_update(gqinode,
+					       oinfo->dqi_gqi_bh,
+					       off + len);
+		if (err < 0)
+			goto out;
+	}
+	WARN_ON(off >> sb->s_blocksize_bits != \
+		(off + len) >> sb->s_blocksize_bits);
+	WARN_ON(((off + len) & ((1 << sb->s_blocksize_bits) - 1)) >
+		sb->s_blocksize - OCFS2_QBLK_RESERVED_SPACE);
+	for (towrite = len; towrite > 0; towrite -= tocopy) {
+		tocopy = min(towrite, (size_t)(sb->s_blocksize - offset));
+		bh = ocfs2_read_quota_block(gqinode, blk, &err);
+		if (!bh) {
+			mlog_errno(err);
+			return err;
+		}
+		err = ocfs2_journal_access(handle, gqinode, bh,
+						OCFS2_JOURNAL_ACCESS_WRITE);
+		if (err < 0) {
+			brelse(bh);
+			goto out;
+		}
+		lock_buffer(bh);
+		memcpy(bh->b_data + offset, data, tocopy);
+		flush_dcache_page(bh->b_page);
+		unlock_buffer(bh);
+		err = ocfs2_journal_dirty(handle, bh);
+		brelse(bh);
+		if (err < 0)
+			goto out;
+		offset = 0;
+		data += tocopy;
+		blk++;
+	}
+out:
+	/* Nothing written? */
+	if (len == towrite) {
+		mutex_unlock(&gqinode->i_mutex);
+		mlog_errno(err);
+		return err;
+	}
+	gqinode->i_version++;
+	ocfs2_mark_inode_dirty(handle, gqinode, oinfo->dqi_gqi_bh);
+	mutex_unlock(&gqinode->i_mutex);
+	return len - towrite;
+}
+
+int ocfs2_lock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex)
+{
+	int status;
+	struct buffer_head *bh = NULL;
+
+	status = ocfs2_inode_lock(oinfo->dqi_gqinode, &bh, ex);
+	if (status < 0)
+		return status;
+	spin_lock(&dq_data_lock);
+	if (!oinfo->dqi_gqi_count++)
+		oinfo->dqi_gqi_bh = bh;
+	else
+		WARN_ON(bh != oinfo->dqi_gqi_bh);
+	spin_unlock(&dq_data_lock);
+	return 0;
+}
+
+void ocfs2_unlock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex)
+{
+	ocfs2_inode_unlock(oinfo->dqi_gqinode, ex);
+	brelse(oinfo->dqi_gqi_bh);
+	spin_lock(&dq_data_lock);
+	if (!--oinfo->dqi_gqi_count)
+		oinfo->dqi_gqi_bh = NULL;
+	spin_unlock(&dq_data_lock);
+}
+
+/* Read information header from global quota file */
+int ocfs2_global_read_info(struct super_block *sb, int type)
+{
+	struct inode *gqinode = NULL;
+	unsigned int ino[MAXQUOTAS] = { USER_QUOTA_SYSTEM_INODE,
+					GROUP_QUOTA_SYSTEM_INODE };
+	struct ocfs2_global_disk_dqinfo dinfo;
+	struct mem_dqinfo *info = sb_dqinfo(sb, type);
+	struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
+	int status;
+
+	mlog_entry_void();
+
+	/* Read global header */
+	gqinode = ocfs2_get_system_file_inode(OCFS2_SB(sb), ino[type],
+			OCFS2_INVALID_SLOT);
+	if (!gqinode) {
+		mlog(ML_ERROR, "failed to get global quota inode (type=%d)\n",
+			type);
+		status = -EINVAL;
+		goto out_err;
+	}
+	oinfo->dqi_gi.dqi_sb = sb;
+	oinfo->dqi_gi.dqi_type = type;
+	ocfs2_qinfo_lock_res_init(&oinfo->dqi_gqlock, oinfo);
+	oinfo->dqi_gi.dqi_entry_size = sizeof(struct ocfs2_global_disk_dqblk);
+	oinfo->dqi_gi.dqi_ops = &ocfs2_global_ops;
+	oinfo->dqi_gqi_bh = NULL;
+	oinfo->dqi_gqi_count = 0;
+	oinfo->dqi_gqinode = gqinode;
+	status = ocfs2_lock_global_qf(oinfo, 0);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out_err;
+	}
+	status = sb->s_op->quota_read(sb, type, (char *)&dinfo,
+				      sizeof(struct ocfs2_global_disk_dqinfo),
+				      OCFS2_GLOBAL_INFO_OFF);
+	ocfs2_unlock_global_qf(oinfo, 0);
+	if (status != sizeof(struct ocfs2_global_disk_dqinfo)) {
+		mlog(ML_ERROR, "Cannot read global quota info (%d).\n",
+		     status);
+		if (status >= 0)
+			status = -EIO;
+		mlog_errno(status);
+		goto out_err;
+	}
+	info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace);
+	info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace);
+	oinfo->dqi_syncms = le32_to_cpu(dinfo.dqi_syncms);
+	oinfo->dqi_gi.dqi_blocks = le32_to_cpu(dinfo.dqi_blocks);
+	oinfo->dqi_gi.dqi_free_blk = le32_to_cpu(dinfo.dqi_free_blk);
+	oinfo->dqi_gi.dqi_free_entry = le32_to_cpu(dinfo.dqi_free_entry);
+	oinfo->dqi_gi.dqi_blocksize_bits = sb->s_blocksize_bits;
+	oinfo->dqi_gi.dqi_usable_bs = sb->s_blocksize -
+						OCFS2_QBLK_RESERVED_SPACE;
+	oinfo->dqi_gi.dqi_qtree_depth = qtree_depth(&oinfo->dqi_gi);
+out_err:
+	mlog_exit(status);
+	return status;
+}
+
+/* Write information to global quota file. Expects exlusive lock on quota
+ * file inode and quota info */
+static int __ocfs2_global_write_info(struct super_block *sb, int type)
+{
+	struct mem_dqinfo *info = sb_dqinfo(sb, type);
+	struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
+	struct ocfs2_global_disk_dqinfo dinfo;
+	ssize_t size;
+
+	spin_lock(&dq_data_lock);
+	info->dqi_flags &= ~DQF_INFO_DIRTY;
+	dinfo.dqi_bgrace = cpu_to_le32(info->dqi_bgrace);
+	dinfo.dqi_igrace = cpu_to_le32(info->dqi_igrace);
+	spin_unlock(&dq_data_lock);
+	dinfo.dqi_syncms = cpu_to_le32(oinfo->dqi_syncms);
+	dinfo.dqi_blocks = cpu_to_le32(oinfo->dqi_gi.dqi_blocks);
+	dinfo.dqi_free_blk = cpu_to_le32(oinfo->dqi_gi.dqi_free_blk);
+	dinfo.dqi_free_entry = cpu_to_le32(oinfo->dqi_gi.dqi_free_entry);
+	size = sb->s_op->quota_write(sb, type, (char *)&dinfo,
+				     sizeof(struct ocfs2_global_disk_dqinfo),
+				     OCFS2_GLOBAL_INFO_OFF);
+	if (size != sizeof(struct ocfs2_global_disk_dqinfo)) {
+		mlog(ML_ERROR, "Cannot write global quota info structure\n");
+		if (size >= 0)
+			size = -EIO;
+		return size;
+	}
+	return 0;
+}
+
+int ocfs2_global_write_info(struct super_block *sb, int type)
+{
+	int err;
+	struct ocfs2_mem_dqinfo *info = sb_dqinfo(sb, type)->dqi_priv;
+
+	err = ocfs2_qinfo_lock(info, 1);
+	if (err < 0)
+		return err;
+	err = __ocfs2_global_write_info(sb, type);
+	ocfs2_qinfo_unlock(info, 1);
+	return err;
+}
+
+/* Read in information from global quota file and acquire a reference to it.
+ * dquot_acquire() has already started the transaction and locked quota file */
+int ocfs2_global_read_dquot(struct dquot *dquot)
+{
+	int err, err2, ex = 0;
+	struct ocfs2_mem_dqinfo *info =
+			sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+
+	err = ocfs2_qinfo_lock(info, 0);
+	if (err < 0)
+		goto out;
+	err = qtree_read_dquot(&info->dqi_gi, dquot);
+	if (err < 0)
+		goto out_qlock;
+	OCFS2_DQUOT(dquot)->dq_use_count++;
+	OCFS2_DQUOT(dquot)->dq_origspace = dquot->dq_dqb.dqb_curspace;
+	OCFS2_DQUOT(dquot)->dq_originodes = dquot->dq_dqb.dqb_curinodes;
+	if (!dquot->dq_off) {	/* No real quota entry? */
+		/* Upgrade to exclusive lock for allocation */
+		err = ocfs2_qinfo_lock(info, 1);
+		if (err < 0)
+			goto out_qlock;
+		ex = 1;
+	}
+	err = qtree_write_dquot(&info->dqi_gi, dquot);
+	if (ex && info_dirty(sb_dqinfo(dquot->dq_sb, dquot->dq_type))) {
+		err2 = __ocfs2_global_write_info(dquot->dq_sb, dquot->dq_type);
+		if (!err)
+			err = err2;
+	}
+out_qlock:
+	if (ex)
+		ocfs2_qinfo_unlock(info, 1);
+	ocfs2_qinfo_unlock(info, 0);
+out:
+	if (err < 0)
+		mlog_errno(err);
+	return err;
+}
+
+/* Sync local information about quota modifications with global quota file.
+ * Caller must have started the transaction and obtained exclusive lock for
+ * global quota file inode */
+int __ocfs2_sync_dquot(struct dquot *dquot, int freeing)
+{
+	int err, err2;
+	struct super_block *sb = dquot->dq_sb;
+	int type = dquot->dq_type;
+	struct ocfs2_mem_dqinfo *info = sb_dqinfo(sb, type)->dqi_priv;
+	struct ocfs2_global_disk_dqblk dqblk;
+	s64 spacechange, inodechange;
+	time_t olditime, oldbtime;
+
+	err = sb->s_op->quota_read(sb, type, (char *)&dqblk,
+				   sizeof(struct ocfs2_global_disk_dqblk),
+				   dquot->dq_off);
+	if (err != sizeof(struct ocfs2_global_disk_dqblk)) {
+		if (err >= 0) {
+			mlog(ML_ERROR, "Short read from global quota file "
+				       "(%u read)\n", err);
+			err = -EIO;
+		}
+		goto out;
+	}
+
+	/* Update space and inode usage. Get also other information from
+	 * global quota file so that we don't overwrite any changes there.
+	 * We are */
+	spin_lock(&dq_data_lock);
+	spacechange = dquot->dq_dqb.dqb_curspace -
+					OCFS2_DQUOT(dquot)->dq_origspace;
+	inodechange = dquot->dq_dqb.dqb_curinodes -
+					OCFS2_DQUOT(dquot)->dq_originodes;
+	olditime = dquot->dq_dqb.dqb_itime;
+	oldbtime = dquot->dq_dqb.dqb_btime;
+	ocfs2_global_disk2memdqb(dquot, &dqblk);
+	mlog(0, "Syncing global dquot %d space %lld+%lld, inodes %lld+%lld\n",
+	     dquot->dq_id, dquot->dq_dqb.dqb_curspace, spacechange,
+	     dquot->dq_dqb.dqb_curinodes, inodechange);
+	if (!test_bit(DQ_LASTSET_B + QIF_SPACE_B, &dquot->dq_flags))
+		dquot->dq_dqb.dqb_curspace += spacechange;
+	if (!test_bit(DQ_LASTSET_B + QIF_INODES_B, &dquot->dq_flags))
+		dquot->dq_dqb.dqb_curinodes += inodechange;
+	/* Now merge grace time changes... */
+	if (!test_bit(DQ_LASTSET_B + QIF_BTIME_B, &dquot->dq_flags) &&
+	    oldbtime > 0) {
+		if (dquot->dq_dqb.dqb_btime > 0)
+			dquot->dq_dqb.dqb_btime =
+					min(dquot->dq_dqb.dqb_btime, oldbtime);
+		else
+			dquot->dq_dqb.dqb_btime = oldbtime;
+	}
+	if (!test_bit(DQ_LASTSET_B + QIF_ITIME_B, &dquot->dq_flags) &&
+	    olditime > 0) {
+		if (dquot->dq_dqb.dqb_itime > 0)
+			dquot->dq_dqb.dqb_itime =
+					min(dquot->dq_dqb.dqb_itime, olditime);
+		else
+			dquot->dq_dqb.dqb_itime = olditime;
+	}
+	/* All information is properly updated, clear the flags */
+	__clear_bit(DQ_LASTSET_B + QIF_SPACE_B, &dquot->dq_flags);
+	__clear_bit(DQ_LASTSET_B + QIF_INODES_B, &dquot->dq_flags);
+	__clear_bit(DQ_LASTSET_B + QIF_BLIMITS_B, &dquot->dq_flags);
+	__clear_bit(DQ_LASTSET_B + QIF_ILIMITS_B, &dquot->dq_flags);
+	__clear_bit(DQ_LASTSET_B + QIF_BTIME_B, &dquot->dq_flags);
+	__clear_bit(DQ_LASTSET_B + QIF_ITIME_B, &dquot->dq_flags);
+	OCFS2_DQUOT(dquot)->dq_origspace = dquot->dq_dqb.dqb_curspace;
+	OCFS2_DQUOT(dquot)->dq_originodes = dquot->dq_dqb.dqb_curinodes;
+	spin_unlock(&dq_data_lock);
+	err = ocfs2_qinfo_lock(info, freeing);
+	if (err < 0) {
+		mlog(ML_ERROR, "Failed to lock quota info, loosing quota write"
+			       " (type=%d, id=%u)\n", dquot->dq_type,
+			       (unsigned)dquot->dq_id);
+		goto out;
+	}
+	if (freeing)
+		OCFS2_DQUOT(dquot)->dq_use_count--;
+	err = qtree_write_dquot(&info->dqi_gi, dquot);
+	if (err < 0)
+		goto out_qlock;
+	if (freeing && !OCFS2_DQUOT(dquot)->dq_use_count) {
+		err = qtree_release_dquot(&info->dqi_gi, dquot);
+		if (info_dirty(sb_dqinfo(sb, type))) {
+			err2 = __ocfs2_global_write_info(sb, type);
+			if (!err)
+				err = err2;
+		}
+	}
+out_qlock:
+	ocfs2_qinfo_unlock(info, freeing);
+out:
+	if (err < 0)
+		mlog_errno(err);
+	return err;
+}
+
+/*
+ *  Wrappers for generic quota functions
+ */
+
+static int ocfs2_write_dquot(struct dquot *dquot)
+{
+	handle_t *handle;
+	struct ocfs2_super *osb = OCFS2_SB(dquot->dq_sb);
+	int status = 0;
+
+	mlog_entry("id=%u, type=%d", dquot->dq_id, dquot->dq_type);
+
+	handle = ocfs2_start_trans(osb, OCFS2_QWRITE_CREDITS);
+	if (IS_ERR(handle)) {
+		status = PTR_ERR(handle);
+		mlog_errno(status);
+		goto out;
+	}
+	status = dquot_commit(dquot);
+	ocfs2_commit_trans(osb, handle);
+out:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_calc_qdel_credits(struct super_block *sb, int type)
+{
+	struct ocfs2_mem_dqinfo *oinfo;
+	int features[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
+				    OCFS2_FEATURE_RO_COMPAT_GRPQUOTA };
+
+	if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, features[type]))
+		return 0;
+
+	oinfo = sb_dqinfo(sb, type)->dqi_priv;
+	/* We modify tree, leaf block, global info, local chunk header,
+	 * global and local inode */
+	return oinfo->dqi_gi.dqi_qtree_depth + 2 + 1 +
+	       2 * OCFS2_INODE_UPDATE_CREDITS;
+}
+
+static int ocfs2_release_dquot(struct dquot *dquot)
+{
+	handle_t *handle;
+	struct ocfs2_mem_dqinfo *oinfo =
+			sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+	struct ocfs2_super *osb = OCFS2_SB(dquot->dq_sb);
+	int status = 0;
+
+	mlog_entry("id=%u, type=%d", dquot->dq_id, dquot->dq_type);
+
+	status = ocfs2_lock_global_qf(oinfo, 1);
+	if (status < 0)
+		goto out;
+	handle = ocfs2_start_trans(osb,
+		ocfs2_calc_qdel_credits(dquot->dq_sb, dquot->dq_type));
+	if (IS_ERR(handle)) {
+		status = PTR_ERR(handle);
+		mlog_errno(status);
+		goto out_ilock;
+	}
+	status = dquot_release(dquot);
+	ocfs2_commit_trans(osb, handle);
+out_ilock:
+	ocfs2_unlock_global_qf(oinfo, 1);
+out:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_calc_qinit_credits(struct super_block *sb, int type)
+{
+	struct ocfs2_mem_dqinfo *oinfo;
+	int features[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
+				    OCFS2_FEATURE_RO_COMPAT_GRPQUOTA };
+	struct ocfs2_dinode *lfe, *gfe;
+
+	if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, features[type]))
+		return 0;
+
+	oinfo = sb_dqinfo(sb, type)->dqi_priv;
+	gfe = (struct ocfs2_dinode *)oinfo->dqi_gqi_bh->b_data;
+	lfe = (struct ocfs2_dinode *)oinfo->dqi_lqi_bh->b_data;
+	/* We can extend local file + global file. In local file we
+	 * can modify info, chunk header block and dquot block. In
+	 * global file we can modify info, tree and leaf block */
+	return ocfs2_calc_extend_credits(sb, &lfe->id2.i_list, 0) +
+	       ocfs2_calc_extend_credits(sb, &gfe->id2.i_list, 0) +
+	       3 + oinfo->dqi_gi.dqi_qtree_depth + 2;
+}
+
+static int ocfs2_acquire_dquot(struct dquot *dquot)
+{
+	handle_t *handle;
+	struct ocfs2_mem_dqinfo *oinfo =
+			sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+	struct ocfs2_super *osb = OCFS2_SB(dquot->dq_sb);
+	int status = 0;
+
+	mlog_entry("id=%u, type=%d", dquot->dq_id, dquot->dq_type);
+	/* We need an exclusive lock, because we're going to update use count
+	 * and instantiate possibly new dquot structure */
+	status = ocfs2_lock_global_qf(oinfo, 1);
+	if (status < 0)
+		goto out;
+	handle = ocfs2_start_trans(osb,
+		ocfs2_calc_qinit_credits(dquot->dq_sb, dquot->dq_type));
+	if (IS_ERR(handle)) {
+		status = PTR_ERR(handle);
+		mlog_errno(status);
+		goto out_ilock;
+	}
+	status = dquot_acquire(dquot);
+	ocfs2_commit_trans(osb, handle);
+out_ilock:
+	ocfs2_unlock_global_qf(oinfo, 1);
+out:
+	mlog_exit(status);
+	return status;
+}
+
+static int ocfs2_mark_dquot_dirty(struct dquot *dquot)
+{
+	unsigned long mask = (1 << (DQ_LASTSET_B + QIF_ILIMITS_B)) |
+			     (1 << (DQ_LASTSET_B + QIF_BLIMITS_B)) |
+			     (1 << (DQ_LASTSET_B + QIF_INODES_B)) |
+			     (1 << (DQ_LASTSET_B + QIF_SPACE_B)) |
+			     (1 << (DQ_LASTSET_B + QIF_BTIME_B)) |
+			     (1 << (DQ_LASTSET_B + QIF_ITIME_B));
+	int sync = 0;
+	int status;
+	struct super_block *sb = dquot->dq_sb;
+	int type = dquot->dq_type;
+	struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
+	handle_t *handle;
+	struct ocfs2_super *osb = OCFS2_SB(sb);
+
+	mlog_entry("id=%u, type=%d", dquot->dq_id, type);
+	dquot_mark_dquot_dirty(dquot);
+
+	/* In case user set some limits, sync dquot immediately to global
+	 * quota file so that information propagates quicker */
+	spin_lock(&dq_data_lock);
+	if (dquot->dq_flags & mask)
+		sync = 1;
+	spin_unlock(&dq_data_lock);
+	if (!sync) {
+		status = ocfs2_write_dquot(dquot);
+		goto out;
+	}
+	status = ocfs2_lock_global_qf(oinfo, 1);
+	if (status < 0)
+		goto out;
+	handle = ocfs2_start_trans(osb, OCFS2_QSYNC_CREDITS);
+	if (IS_ERR(handle)) {
+		status = PTR_ERR(handle);
+		mlog_errno(status);
+		goto out_ilock;
+	}
+	status = ocfs2_sync_dquot(dquot);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out_trans;
+	}
+	/* Now write updated local dquot structure */
+	status = dquot_commit(dquot);
+out_trans:
+	ocfs2_commit_trans(osb, handle);
+out_ilock:
+	ocfs2_unlock_global_qf(oinfo, 1);
+out:
+	mlog_exit(status);
+	return status;
+}
+
+/* This should happen only after set_dqinfo(). */
+static int ocfs2_write_info(struct super_block *sb, int type)
+{
+	handle_t *handle;
+	int status = 0;
+	struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
+
+	mlog_entry_void();
+
+	status = ocfs2_lock_global_qf(oinfo, 1);
+	if (status < 0)
+		goto out;
+	handle = ocfs2_start_trans(OCFS2_SB(sb), OCFS2_QINFO_WRITE_CREDITS);
+	if (IS_ERR(handle)) {
+		status = PTR_ERR(handle);
+		mlog_errno(status);
+		goto out_ilock;
+	}
+	status = dquot_commit_info(sb, type);
+	ocfs2_commit_trans(OCFS2_SB(sb), handle);
+out_ilock:
+	ocfs2_unlock_global_qf(oinfo, 1);
+out:
+	mlog_exit(status);
+	return status;
+}
+
+/* This is difficult. We have to lock quota inode and start transaction
+ * in this function but we don't want to take the penalty of exlusive
+ * quota file lock when we are just going to use cached structures. So
+ * we just take read lock check whether we have dquot cached and if so,
+ * we don't have to take the write lock... */
+static int ocfs2_dquot_initialize(struct inode *inode, int type)
+{
+	handle_t *handle = NULL;
+	int status = 0;
+	struct super_block *sb = inode->i_sb;
+	struct ocfs2_mem_dqinfo *oinfo;
+	int exclusive = 0;
+	int cnt;
+	qid_t id;
+
+	mlog_entry_void();
+
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+		if (type != -1 && cnt != type)
+			continue;
+		oinfo = sb_dqinfo(sb, cnt)->dqi_priv;
+		status = ocfs2_lock_global_qf(oinfo, 0);
+		if (status < 0)
+			goto out;
+		/* This is just a performance optimization not a reliable test.
+		 * Since we hold an inode lock, noone can actually release
+		 * the structure until we are finished with initialization. */
+		if (inode->i_dquot[cnt] != NODQUOT) {
+			ocfs2_unlock_global_qf(oinfo, 0);
+			continue;
+		}
+		/* When we have inode lock, we know that no dquot_release() can
+		 * run and thus we can safely check whether we need to
+		 * read+modify global file to get quota information or whether
+		 * our node already has it. */
+		if (cnt == USRQUOTA)
+			id = inode->i_uid;
+		else if (cnt == GRPQUOTA)
+			id = inode->i_gid;
+		else
+			BUG();
+		/* Obtain exclusion from quota off... */
+		down_write(&sb_dqopt(sb)->dqptr_sem);
+		exclusive = !dquot_is_cached(sb, id, cnt);
+		up_write(&sb_dqopt(sb)->dqptr_sem);
+		if (exclusive) {
+			status = ocfs2_lock_global_qf(oinfo, 1);
+			if (status < 0) {
+				exclusive = 0;
+				mlog_errno(status);
+				goto out_ilock;
+			}
+			handle = ocfs2_start_trans(OCFS2_SB(sb),
+					ocfs2_calc_qinit_credits(sb, cnt));
+			if (IS_ERR(handle)) {
+				status = PTR_ERR(handle);
+				mlog_errno(status);
+				goto out_ilock;
+			}
+		}
+		dquot_initialize(inode, cnt);
+		if (exclusive) {
+			ocfs2_commit_trans(OCFS2_SB(sb), handle);
+			ocfs2_unlock_global_qf(oinfo, 1);
+		}
+		ocfs2_unlock_global_qf(oinfo, 0);
+	}
+	mlog_exit(0);
+	return 0;
+out_ilock:
+	if (exclusive)
+		ocfs2_unlock_global_qf(oinfo, 1);
+	ocfs2_unlock_global_qf(oinfo, 0);
+out:
+	mlog_exit(status);
+	return status;
+}
+
+static int ocfs2_dquot_drop_slow(struct inode *inode)
+{
+	int status;
+	int cnt;
+	int got_lock[MAXQUOTAS] = {0, 0};
+	handle_t *handle;
+	struct super_block *sb = inode->i_sb;
+	struct ocfs2_mem_dqinfo *oinfo;
+
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+		oinfo = sb_dqinfo(sb, cnt)->dqi_priv;
+		status = ocfs2_lock_global_qf(oinfo, 1);
+		if (status < 0)
+			goto out;
+		got_lock[cnt] = 1;
+	}
+	handle = ocfs2_start_trans(OCFS2_SB(sb),
+			ocfs2_calc_qinit_credits(sb, USRQUOTA) +
+			ocfs2_calc_qinit_credits(sb, GRPQUOTA));
+	if (IS_ERR(handle)) {
+		status = PTR_ERR(handle);
+		mlog_errno(status);
+				goto out;
+	}
+	dquot_drop(inode);
+	ocfs2_commit_trans(OCFS2_SB(sb), handle);
+out:
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+		if (got_lock[cnt]) {
+			oinfo = sb_dqinfo(sb, cnt)->dqi_priv;
+			ocfs2_unlock_global_qf(oinfo, 1);
+		}
+	return status;
+}
+
+/* See the comment before ocfs2_dquot_initialize. */
+static int ocfs2_dquot_drop(struct inode *inode)
+{
+	int status = 0;
+	struct super_block *sb = inode->i_sb;
+	struct ocfs2_mem_dqinfo *oinfo;
+	int exclusive = 0;
+	int cnt;
+	int got_lock[MAXQUOTAS] = {0, 0};
+
+	mlog_entry_void();
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+		oinfo = sb_dqinfo(sb, cnt)->dqi_priv;
+		status = ocfs2_lock_global_qf(oinfo, 0);
+		if (status < 0)
+			goto out;
+		got_lock[cnt] = 1;
+	}
+	/* Lock against anyone releasing references so that when when we check
+	 * we know we are not going to be last ones to release dquot */
+	down_write(&sb_dqopt(sb)->dqptr_sem);
+	/* Urgh, this is a terrible hack :( */
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+		if (inode->i_dquot[cnt] != NODQUOT &&
+		    atomic_read(&inode->i_dquot[cnt]->dq_count) > 1) {
+			exclusive = 1;
+			break;
+		}
+	}
+	if (!exclusive)
+		dquot_drop_locked(inode);
+	up_write(&sb_dqopt(sb)->dqptr_sem);
+out:
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+		if (got_lock[cnt]) {
+			oinfo = sb_dqinfo(sb, cnt)->dqi_priv;
+			ocfs2_unlock_global_qf(oinfo, 0);
+		}
+	/* In case we bailed out because we had to do expensive locking
+	 * do it now... */
+	if (exclusive)
+		status = ocfs2_dquot_drop_slow(inode);
+	mlog_exit(status);
+	return status;
+}
+
+static struct dquot *ocfs2_alloc_dquot(struct super_block *sb, int type)
+{
+	struct ocfs2_dquot *dquot =
+				kmem_cache_zalloc(ocfs2_dquot_cachep, GFP_NOFS);
+
+	if (!dquot)
+		return NULL;
+	return &dquot->dq_dquot;
+}
+
+static void ocfs2_destroy_dquot(struct dquot *dquot)
+{
+	kmem_cache_free(ocfs2_dquot_cachep, dquot);
+}
+
+struct dquot_operations ocfs2_quota_operations = {
+	.initialize	= ocfs2_dquot_initialize,
+	.drop		= ocfs2_dquot_drop,
+	.alloc_space	= dquot_alloc_space,
+	.alloc_inode	= dquot_alloc_inode,
+	.free_space	= dquot_free_space,
+	.free_inode	= dquot_free_inode,
+	.transfer	= dquot_transfer,
+	.write_dquot	= ocfs2_write_dquot,
+	.acquire_dquot	= ocfs2_acquire_dquot,
+	.release_dquot	= ocfs2_release_dquot,
+	.mark_dirty	= ocfs2_mark_dquot_dirty,
+	.write_info	= ocfs2_write_info,
+	.alloc_dquot	= ocfs2_alloc_dquot,
+	.destroy_dquot	= ocfs2_destroy_dquot,
+};
diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c
new file mode 100644
index 0000000..55c3f2f
--- /dev/null
+++ b/fs/ocfs2/quota_local.c
@@ -0,0 +1,833 @@
+/*
+ *  Implementation of operations over local quota file
+ */
+
+#include <linux/fs.h>
+#include <linux/quota.h>
+#include <linux/quotaops.h>
+#include <linux/module.h>
+
+#define MLOG_MASK_PREFIX ML_QUOTA
+#include <cluster/masklog.h>
+
+#include "ocfs2_fs.h"
+#include "ocfs2.h"
+#include "inode.h"
+#include "alloc.h"
+#include "file.h"
+#include "buffer_head_io.h"
+#include "journal.h"
+#include "sysfile.h"
+#include "dlmglue.h"
+#include "quota.h"
+
+/* Number of local quota structures per block */
+static inline unsigned int ol_quota_entries_per_block(struct super_block *sb)
+{
+	return ((sb->s_blocksize - OCFS2_QBLK_RESERVED_SPACE) /
+		sizeof(struct ocfs2_local_disk_dqblk));
+}
+
+/* Number of blocks with entries in one chunk */
+static inline unsigned int ol_chunk_blocks(struct super_block *sb)
+{
+	return ((sb->s_blocksize - sizeof(struct ocfs2_local_disk_chunk) -
+		 OCFS2_QBLK_RESERVED_SPACE) << 3) /
+	       ol_quota_entries_per_block(sb);
+}
+
+/* Number of entries in a chunk bitmap */
+static unsigned int ol_chunk_entries(struct super_block *sb)
+{
+	return ol_chunk_blocks(sb) * ol_quota_entries_per_block(sb);
+}
+
+/* Offset of the chunk in quota file */
+static unsigned int ol_quota_chunk_block(struct super_block *sb, int c)
+{
+	/* 1 block for local quota file info, 1 block per chunk for chunk info */
+	return 1 + (ol_chunk_blocks(sb) + 1) * c;
+}
+
+/* Offset of the dquot structure in the quota file */
+static loff_t ol_dqblk_off(struct super_block *sb, int c, int off)
+{
+	int epb = ol_quota_entries_per_block(sb);
+
+	return ((ol_quota_chunk_block(sb, c) + 1 + off / epb)
+		<< sb->s_blocksize_bits) +
+		(off % epb) * sizeof(struct ocfs2_local_disk_dqblk);
+}
+
+/* Compute block number from given offset */
+static inline unsigned int ol_dqblk_file_block(struct super_block *sb, loff_t off)
+{
+	return off >> sb->s_blocksize_bits;
+}
+
+static inline unsigned int ol_dqblk_block_offset(struct super_block *sb, loff_t off)
+{
+	return off & ((1 << sb->s_blocksize_bits) - 1);
+}
+
+/* Compute offset in the chunk of a structure with the given offset */
+static int ol_dqblk_chunk_off(struct super_block *sb, int c, loff_t off)
+{
+	int epb = ol_quota_entries_per_block(sb);
+
+	return ((off >> sb->s_blocksize_bits) -
+			ol_quota_chunk_block(sb, c) - 1) * epb
+	       + ((unsigned int)(off & ((1 << sb->s_blocksize_bits) - 1))) /
+		 sizeof(struct ocfs2_local_disk_dqblk);
+}
+
+/* Write bufferhead into the fs */
+static int ocfs2_modify_bh(struct inode *inode, struct buffer_head *bh,
+		void (*modify)(struct buffer_head *, void *), void *private)
+{
+	struct super_block *sb = inode->i_sb;
+	handle_t *handle;
+	int status;
+
+	handle = ocfs2_start_trans(OCFS2_SB(sb), 1);
+	if (IS_ERR(handle)) {
+		status = PTR_ERR(handle);
+		mlog_errno(status);
+		return status;
+	}
+	status = ocfs2_journal_access(handle, inode, bh,
+				      OCFS2_JOURNAL_ACCESS_WRITE);
+	if (status < 0) {
+		mlog_errno(status);
+		ocfs2_commit_trans(OCFS2_SB(sb), handle);
+		return status;
+	}
+	lock_buffer(bh);
+	modify(bh, private);
+	unlock_buffer(bh);
+	status = ocfs2_journal_dirty(handle, bh);
+	if (status < 0) {
+		mlog_errno(status);
+		ocfs2_commit_trans(OCFS2_SB(sb), handle);
+		return status;
+	}
+	status = ocfs2_commit_trans(OCFS2_SB(sb), handle);
+	if (status < 0) {
+		mlog_errno(status);
+		return status;
+	}
+	return 0;
+}
+
+/* Check whether we understand format of quota files */
+static int ocfs2_local_check_quota_file(struct super_block *sb, int type)
+{
+	unsigned int lmagics[MAXQUOTAS] = OCFS2_LOCAL_QMAGICS;
+	unsigned int lversions[MAXQUOTAS] = OCFS2_LOCAL_QVERSIONS;
+	unsigned int gmagics[MAXQUOTAS] = OCFS2_GLOBAL_QMAGICS;
+	unsigned int gversions[MAXQUOTAS] = OCFS2_GLOBAL_QVERSIONS;
+	unsigned int ino[MAXQUOTAS] = { USER_QUOTA_SYSTEM_INODE,
+					GROUP_QUOTA_SYSTEM_INODE };
+	struct buffer_head *bh;
+	struct inode *linode = sb_dqopt(sb)->files[type];
+	struct inode *ginode = NULL;
+	struct ocfs2_disk_dqheader *dqhead;
+	int status, ret = 0;
+
+	/* First check whether we understand local quota file */
+	bh = ocfs2_read_quota_block(linode, 0, &status);
+	if (!bh) {
+		mlog_errno(status);
+		mlog(ML_ERROR, "failed to read quota file header (type=%d)\n",
+			type);
+		goto out_err;
+	}
+	dqhead = (struct ocfs2_disk_dqheader *)(bh->b_data);
+	if (le32_to_cpu(dqhead->dqh_magic) != lmagics[type]) {
+		mlog(ML_ERROR, "quota file magic does not match (%u != %u),"
+			" type=%d\n", le32_to_cpu(dqhead->dqh_magic),
+			lmagics[type], type);
+		goto out_err;
+	}
+	if (le32_to_cpu(dqhead->dqh_version) != lversions[type]) {
+		mlog(ML_ERROR, "quota file version does not match (%u != %u),"
+			" type=%d\n", le32_to_cpu(dqhead->dqh_version),
+			lversions[type], type);
+		goto out_err;
+	}
+	brelse(bh);
+	bh = NULL;
+
+	/* Next check whether we understand global quota file */
+	ginode = ocfs2_get_system_file_inode(OCFS2_SB(sb), ino[type],
+						OCFS2_INVALID_SLOT);
+	if (!ginode) {
+		mlog(ML_ERROR, "cannot get global quota file inode "
+				"(type=%d)\n", type);
+		goto out_err;
+	}
+	/* Since the header is read only, we don't care about locking */
+	bh = ocfs2_read_quota_block(ginode, 0, &status);
+	if (!bh) {
+		mlog_errno(status);
+		mlog(ML_ERROR, "failed to read global quota file header "
+				"(type=%d)\n", type);
+		goto out_err;
+	}
+	dqhead = (struct ocfs2_disk_dqheader *)(bh->b_data);
+	if (le32_to_cpu(dqhead->dqh_magic) != gmagics[type]) {
+		mlog(ML_ERROR, "global quota file magic does not match "
+			"(%u != %u), type=%d\n",
+			le32_to_cpu(dqhead->dqh_magic), gmagics[type], type);
+		goto out_err;
+	}
+	if (le32_to_cpu(dqhead->dqh_version) != gversions[type]) {
+		mlog(ML_ERROR, "global quota file version does not match "
+			"(%u != %u), type=%d\n",
+			le32_to_cpu(dqhead->dqh_version), gversions[type],
+			type);
+		goto out_err;
+	}
+
+	ret = 1;
+out_err:
+	brelse(bh);
+	iput(ginode);
+	return ret;
+}
+
+/* Release given list of quota file chunks */
+static void ocfs2_release_local_quota_bitmaps(struct list_head *head)
+{
+	struct ocfs2_quota_chunk *pos, *next;
+
+	list_for_each_entry_safe(pos, next, head, qc_chunk) {
+		list_del(&pos->qc_chunk);
+		brelse(pos->qc_headerbh);
+		kmem_cache_free(ocfs2_qf_chunk_cachep, pos);
+	}
+}
+
+/* Load quota bitmaps into memory */
+static int ocfs2_load_local_quota_bitmaps(struct inode *inode,
+			struct ocfs2_local_disk_dqinfo *ldinfo,
+			struct list_head *head)
+{
+	struct ocfs2_quota_chunk *newchunk;
+	int i, status;
+
+	INIT_LIST_HEAD(head);
+	for (i = 0; i < le32_to_cpu(ldinfo->dqi_chunks); i++) {
+		newchunk = kmem_cache_alloc(ocfs2_qf_chunk_cachep, GFP_NOFS);
+		if (!newchunk) {
+			ocfs2_release_local_quota_bitmaps(head);
+			return -ENOMEM;
+		}
+		newchunk->qc_num = i;
+		newchunk->qc_headerbh = ocfs2_read_quota_block(inode,
+				ol_quota_chunk_block(inode->i_sb, i),
+				&status);
+		if (!newchunk->qc_headerbh) {
+			mlog_errno(status);
+			kmem_cache_free(ocfs2_qf_chunk_cachep, newchunk);
+			ocfs2_release_local_quota_bitmaps(head);
+			return status;
+		}
+		list_add_tail(&newchunk->qc_chunk, head);
+	}
+	return 0;
+}
+
+static void olq_update_info(struct buffer_head *bh, void *private)
+{
+	struct mem_dqinfo *info = private;
+	struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
+	struct ocfs2_local_disk_dqinfo *ldinfo;
+
+	ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data +
+						OCFS2_LOCAL_INFO_OFF);
+	spin_lock(&dq_data_lock);
+	ldinfo->dqi_flags = cpu_to_le32(info->dqi_flags & DQF_MASK);
+	ldinfo->dqi_chunks = cpu_to_le32(oinfo->dqi_chunks);
+	ldinfo->dqi_blocks = cpu_to_le32(oinfo->dqi_blocks);
+	spin_unlock(&dq_data_lock);
+}
+
+/* Read information header from quota file */
+static int ocfs2_local_read_info(struct super_block *sb, int type)
+{
+	struct ocfs2_local_disk_dqinfo *ldinfo;
+	struct mem_dqinfo *info = sb_dqinfo(sb, type);
+	struct ocfs2_mem_dqinfo *oinfo;
+	struct inode *lqinode = sb_dqopt(sb)->files[type];
+	int status;
+	struct buffer_head *bh = NULL;
+	int locked = 0;
+
+	info->dqi_maxblimit = 0x7fffffffffffffffLL;
+	info->dqi_maxilimit = 0x7fffffffffffffffLL;
+	oinfo = kmalloc(sizeof(struct ocfs2_mem_dqinfo), GFP_NOFS);
+	if (!oinfo) {
+		mlog(ML_ERROR, "failed to allocate memory for ocfs2 quota"
+			       " info.");
+		goto out_err;
+	}
+	info->dqi_priv = oinfo;
+	oinfo->dqi_type = type;
+	INIT_LIST_HEAD(&oinfo->dqi_chunk);
+	oinfo->dqi_lqi_bh = NULL;
+	oinfo->dqi_ibh = NULL;
+
+	status = ocfs2_global_read_info(sb, type);
+	if (status < 0)
+		goto out_err;
+
+	status = ocfs2_inode_lock(lqinode, &oinfo->dqi_lqi_bh, 1);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out_err;
+	}
+	locked = 1;
+
+	/* Now read local header */
+	bh = ocfs2_read_quota_block(lqinode, 0, &status);
+	if (!bh) {
+		mlog_errno(status);
+		mlog(ML_ERROR, "failed to read quota file info header "
+			"(type=%d)\n", type);
+		goto out_err;
+	}
+	ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data +
+						OCFS2_LOCAL_INFO_OFF);
+	info->dqi_flags = le32_to_cpu(ldinfo->dqi_flags);
+	oinfo->dqi_chunks = le32_to_cpu(ldinfo->dqi_chunks);
+	oinfo->dqi_blocks = le32_to_cpu(ldinfo->dqi_blocks);
+	oinfo->dqi_ibh = bh;
+
+	/* We crashed when using local quota file? */
+	if (!(info->dqi_flags & OLQF_CLEAN))
+		goto out_err;	/* So far we just bail out. Later we should resync here */
+
+	status = ocfs2_load_local_quota_bitmaps(sb_dqopt(sb)->files[type],
+						ldinfo,
+						&oinfo->dqi_chunk);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out_err;
+	}
+
+	/* Now mark quota file as used */
+	info->dqi_flags &= ~OLQF_CLEAN;
+	status = ocfs2_modify_bh(lqinode, bh, olq_update_info, info);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out_err;
+	}
+
+	return 0;
+out_err:
+	if (oinfo) {
+		iput(oinfo->dqi_gqinode);
+		ocfs2_simple_drop_lockres(OCFS2_SB(sb), &oinfo->dqi_gqlock);
+		ocfs2_lock_res_free(&oinfo->dqi_gqlock);
+		brelse(oinfo->dqi_lqi_bh);
+		if (locked)
+			ocfs2_inode_unlock(lqinode, 1);
+		ocfs2_release_local_quota_bitmaps(&oinfo->dqi_chunk);
+		kfree(oinfo);
+	}
+	brelse(bh);
+	return -1;
+}
+
+/* Write local info to quota file */
+static int ocfs2_local_write_info(struct super_block *sb, int type)
+{
+	struct mem_dqinfo *info = sb_dqinfo(sb, type);
+	struct buffer_head *bh = ((struct ocfs2_mem_dqinfo *)info->dqi_priv)
+						->dqi_ibh;
+	int status;
+
+	status = ocfs2_modify_bh(sb_dqopt(sb)->files[type], bh, olq_update_info,
+				 info);
+	if (status < 0) {
+		mlog_errno(status);
+		return -1;
+	}
+
+	return 0;
+}
+
+/* Release info from memory */
+static int ocfs2_local_free_info(struct super_block *sb, int type)
+{
+	struct mem_dqinfo *info = sb_dqinfo(sb, type);
+	struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
+	struct ocfs2_quota_chunk *chunk;
+	struct ocfs2_local_disk_chunk *dchunk;
+	int mark_clean = 1, len;
+	int status;
+
+	iput(oinfo->dqi_gqinode);
+	ocfs2_simple_drop_lockres(OCFS2_SB(sb), &oinfo->dqi_gqlock);
+	ocfs2_lock_res_free(&oinfo->dqi_gqlock);
+	list_for_each_entry(chunk, &oinfo->dqi_chunk, qc_chunk) {
+		dchunk = (struct ocfs2_local_disk_chunk *)
+					(chunk->qc_headerbh->b_data);
+		if (chunk->qc_num < oinfo->dqi_chunks - 1) {
+			len = ol_chunk_entries(sb);
+		} else {
+			len = (oinfo->dqi_blocks -
+			       ol_quota_chunk_block(sb, chunk->qc_num) - 1)
+			      * ol_quota_entries_per_block(sb);
+		}
+		/* Not all entries free? Bug! */
+		if (le32_to_cpu(dchunk->dqc_free) != len) {
+			mlog(ML_ERROR, "releasing quota file with used "
+					"entries (type=%d)\n", type);
+			mark_clean = 0;
+		}
+	}
+	ocfs2_release_local_quota_bitmaps(&oinfo->dqi_chunk);
+
+	if (!mark_clean)
+		goto out;
+
+	/* Mark local file as clean */
+	info->dqi_flags |= OLQF_CLEAN;
+	status = ocfs2_modify_bh(sb_dqopt(sb)->files[type],
+				 oinfo->dqi_ibh,
+				 olq_update_info,
+				 info);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out;
+	}
+
+out:
+	ocfs2_inode_unlock(sb_dqopt(sb)->files[type], 1);
+	brelse(oinfo->dqi_ibh);
+	brelse(oinfo->dqi_lqi_bh);
+	kfree(oinfo);
+	return 0;
+}
+
+static void olq_set_dquot(struct buffer_head *bh, void *private)
+{
+	struct ocfs2_dquot *od = private;
+	struct ocfs2_local_disk_dqblk *dqblk;
+	struct super_block *sb = od->dq_dquot.dq_sb;
+
+	dqblk = (struct ocfs2_local_disk_dqblk *)(bh->b_data
+		+ ol_dqblk_block_offset(sb, od->dq_local_off));
+
+	dqblk->dqb_id = cpu_to_le64(od->dq_dquot.dq_id);
+	spin_lock(&dq_data_lock);
+	dqblk->dqb_spacemod = cpu_to_le64(od->dq_dquot.dq_dqb.dqb_curspace -
+					  od->dq_origspace);
+	dqblk->dqb_inodemod = cpu_to_le64(od->dq_dquot.dq_dqb.dqb_curinodes -
+					  od->dq_originodes);
+	spin_unlock(&dq_data_lock);
+	mlog(0, "Writing local dquot %u space %lld inodes %lld\n",
+	     od->dq_dquot.dq_id, dqblk->dqb_spacemod, dqblk->dqb_inodemod);
+}
+
+/* Write dquot to local quota file */
+static int ocfs2_local_write_dquot(struct dquot *dquot)
+{
+	struct super_block *sb = dquot->dq_sb;
+	struct ocfs2_dquot *od = OCFS2_DQUOT(dquot);
+	struct buffer_head *bh;
+	int status;
+
+	bh = ocfs2_read_quota_block(sb_dqopt(sb)->files[dquot->dq_type],
+				    ol_dqblk_file_block(sb, od->dq_local_off),
+				    &status);
+	if (!bh) {
+		mlog_errno(status);
+		goto out;
+	}
+	status = ocfs2_modify_bh(sb_dqopt(sb)->files[dquot->dq_type], bh,
+				 olq_set_dquot, od);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out;
+	}
+out:
+	brelse(bh);
+	return status;
+}
+
+/* Find free entry in local quota file */
+static struct ocfs2_quota_chunk *ocfs2_find_free_entry(struct super_block *sb,
+						       int type,
+						       int *offset)
+{
+	struct mem_dqinfo *info = sb_dqinfo(sb, type);
+	struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
+	struct ocfs2_quota_chunk *chunk;
+	struct ocfs2_local_disk_chunk *dchunk;
+	int found = 0, len;
+
+	list_for_each_entry(chunk, &oinfo->dqi_chunk, qc_chunk) {
+		dchunk = (struct ocfs2_local_disk_chunk *)
+						chunk->qc_headerbh->b_data;
+		if (le32_to_cpu(dchunk->dqc_free) > 0) {
+			found = 1;
+			break;
+		}
+	}
+	if (!found)
+		return NULL;
+
+	if (chunk->qc_num < oinfo->dqi_chunks - 1) {
+		len = ol_chunk_entries(sb);
+	} else {
+		len = (oinfo->dqi_blocks -
+		       ol_quota_chunk_block(sb, chunk->qc_num) - 1)
+		      * ol_quota_entries_per_block(sb);
+	}
+
+	found = ocfs2_find_next_zero_bit(dchunk->dqc_bitmap, len, 0);
+	/* We failed? */
+	if (found == len) {
+		mlog(ML_ERROR, "Did not find empty entry in chunk %d with %u"
+		     " entries free (type=%d)\n", chunk->qc_num,
+		     le32_to_cpu(dchunk->dqc_free), type);
+		return ERR_PTR(-EIO);
+	}
+	*offset = found;
+	return chunk;
+}
+
+/* Add new chunk to the local quota file */
+static struct ocfs2_quota_chunk *ocfs2_local_quota_add_chunk(
+							struct super_block *sb,
+							int type,
+							int *offset)
+{
+	struct mem_dqinfo *info = sb_dqinfo(sb, type);
+	struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
+	struct inode *lqinode = sb_dqopt(sb)->files[type];
+	struct ocfs2_quota_chunk *chunk = NULL;
+	struct ocfs2_local_disk_chunk *dchunk;
+	int status;
+	handle_t *handle;
+	struct buffer_head *bh = NULL;
+	u64 p_blkno;
+
+	/* We are protected by dqio_sem so no locking needed */
+	status = ocfs2_extend_no_holes(lqinode,
+				       lqinode->i_size + 2 * sb->s_blocksize,
+				       lqinode->i_size);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out;
+	}
+	status = ocfs2_simple_size_update(lqinode, oinfo->dqi_lqi_bh,
+					  lqinode->i_size + 2 * sb->s_blocksize);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out;
+	}
+
+	chunk = kmem_cache_alloc(ocfs2_qf_chunk_cachep, GFP_NOFS);
+	if (!chunk) {
+		status = -ENOMEM;
+		mlog_errno(status);
+		goto out;
+	}
+
+	down_read(&OCFS2_I(lqinode)->ip_alloc_sem);
+	status = ocfs2_extent_map_get_blocks(lqinode, oinfo->dqi_blocks,
+					     &p_blkno, NULL, NULL);
+	up_read(&OCFS2_I(lqinode)->ip_alloc_sem);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out;
+	}
+	bh = sb_getblk(sb, p_blkno);
+	if (!bh) {
+		status = -ENOMEM;
+		mlog_errno(status);
+		goto out;
+	}
+	dchunk = (struct ocfs2_local_disk_chunk *)bh->b_data;
+
+	handle = ocfs2_start_trans(OCFS2_SB(sb), 2);
+	if (IS_ERR(handle)) {
+		status = PTR_ERR(handle);
+		mlog_errno(status);
+		goto out;
+	}
+
+	status = ocfs2_journal_access(handle, lqinode, bh,
+				      OCFS2_JOURNAL_ACCESS_WRITE);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out_trans;
+	}
+	lock_buffer(bh);
+	dchunk->dqc_free = ol_quota_entries_per_block(sb);
+	memset(dchunk->dqc_bitmap, 0,
+	       sb->s_blocksize - sizeof(struct ocfs2_local_disk_chunk) -
+	       OCFS2_QBLK_RESERVED_SPACE);
+	set_buffer_uptodate(bh);
+	unlock_buffer(bh);
+	status = ocfs2_journal_dirty(handle, bh);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out_trans;
+	}
+
+	oinfo->dqi_blocks += 2;
+	oinfo->dqi_chunks++;
+	status = ocfs2_local_write_info(sb, type);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out_trans;
+	}
+	status = ocfs2_commit_trans(OCFS2_SB(sb), handle);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out;
+	}
+
+	list_add_tail(&chunk->qc_chunk, &oinfo->dqi_chunk);
+	chunk->qc_num = list_entry(chunk->qc_chunk.prev,
+				   struct ocfs2_quota_chunk,
+				   qc_chunk)->qc_num + 1;
+	chunk->qc_headerbh = bh;
+	*offset = 0;
+	return chunk;
+out_trans:
+	ocfs2_commit_trans(OCFS2_SB(sb), handle);
+out:
+	brelse(bh);
+	kmem_cache_free(ocfs2_qf_chunk_cachep, chunk);
+	return ERR_PTR(status);
+}
+
+/* Find free entry in local quota file */
+static struct ocfs2_quota_chunk *ocfs2_extend_local_quota_file(
+						       struct super_block *sb,
+						       int type,
+						       int *offset)
+{
+	struct mem_dqinfo *info = sb_dqinfo(sb, type);
+	struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
+	struct ocfs2_quota_chunk *chunk;
+	struct inode *lqinode = sb_dqopt(sb)->files[type];
+	struct ocfs2_local_disk_chunk *dchunk;
+	int epb = ol_quota_entries_per_block(sb);
+	unsigned int chunk_blocks;
+	int status;
+	handle_t *handle;
+
+	if (list_empty(&oinfo->dqi_chunk))
+		return ocfs2_local_quota_add_chunk(sb, type, offset);
+	/* Is the last chunk full? */
+	chunk = list_entry(oinfo->dqi_chunk.prev,
+			struct ocfs2_quota_chunk, qc_chunk);
+	chunk_blocks = oinfo->dqi_blocks -
+			ol_quota_chunk_block(sb, chunk->qc_num) - 1;
+	if (ol_chunk_blocks(sb) == chunk_blocks)
+		return ocfs2_local_quota_add_chunk(sb, type, offset);
+
+	/* We are protected by dqio_sem so no locking needed */
+	status = ocfs2_extend_no_holes(lqinode,
+				       lqinode->i_size + sb->s_blocksize,
+				       lqinode->i_size);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out;
+	}
+	status = ocfs2_simple_size_update(lqinode, oinfo->dqi_lqi_bh,
+					  lqinode->i_size + sb->s_blocksize);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out;
+	}
+	handle = ocfs2_start_trans(OCFS2_SB(sb), 2);
+	if (IS_ERR(handle)) {
+		status = PTR_ERR(handle);
+		mlog_errno(status);
+		goto out;
+	}
+	status = ocfs2_journal_access(handle, lqinode, chunk->qc_headerbh,
+				 OCFS2_JOURNAL_ACCESS_WRITE);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out_trans;
+	}
+
+	dchunk = (struct ocfs2_local_disk_chunk *)chunk->qc_headerbh->b_data;
+	lock_buffer(chunk->qc_headerbh);
+	le32_add_cpu(&dchunk->dqc_free, ol_quota_entries_per_block(sb));
+	unlock_buffer(chunk->qc_headerbh);
+	status = ocfs2_journal_dirty(handle, chunk->qc_headerbh);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out_trans;
+	}
+	oinfo->dqi_blocks++;
+	status = ocfs2_local_write_info(sb, type);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out_trans;
+	}
+
+	status = ocfs2_commit_trans(OCFS2_SB(sb), handle);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out;
+	}
+	*offset = chunk_blocks * epb;
+	return chunk;
+out_trans:
+	ocfs2_commit_trans(OCFS2_SB(sb), handle);
+out:
+	return ERR_PTR(status);
+}
+
+void olq_alloc_dquot(struct buffer_head *bh, void *private)
+{
+	int *offset = private;
+	struct ocfs2_local_disk_chunk *dchunk;
+
+	dchunk = (struct ocfs2_local_disk_chunk *)bh->b_data;
+	ocfs2_set_bit(*offset, dchunk->dqc_bitmap);
+	le32_add_cpu(&dchunk->dqc_free, -1);
+}
+
+/* Create dquot in the local file for given id */
+static int ocfs2_create_local_dquot(struct dquot *dquot)
+{
+	struct super_block *sb = dquot->dq_sb;
+	int type = dquot->dq_type;
+	struct inode *lqinode = sb_dqopt(sb)->files[type];
+	struct ocfs2_quota_chunk *chunk;
+	struct ocfs2_dquot *od = OCFS2_DQUOT(dquot);
+	int offset;
+	int status;
+
+	chunk = ocfs2_find_free_entry(sb, type, &offset);
+	if (!chunk) {
+		chunk = ocfs2_extend_local_quota_file(sb, type, &offset);
+		if (IS_ERR(chunk))
+			return PTR_ERR(chunk);
+	} else if (IS_ERR(chunk)) {
+		return PTR_ERR(chunk);
+	}
+	od->dq_local_off = ol_dqblk_off(sb, chunk->qc_num, offset);
+	od->dq_chunk = chunk;
+
+	/* Initialize dquot structure on disk */
+	status = ocfs2_local_write_dquot(dquot);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out;
+	}
+
+	/* Mark structure as allocated */
+	status = ocfs2_modify_bh(lqinode, chunk->qc_headerbh, olq_alloc_dquot,
+				 &offset);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out;
+	}
+out:
+	return status;
+}
+
+/* Create entry in local file for dquot, load data from the global file */
+static int ocfs2_local_read_dquot(struct dquot *dquot)
+{
+	int status;
+
+	mlog_entry("id=%u, type=%d\n", dquot->dq_id, dquot->dq_type);
+
+	status = ocfs2_global_read_dquot(dquot);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out_err;
+	}
+
+	/* Now create entry in the local quota file */
+	status = ocfs2_create_local_dquot(dquot);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out_err;
+	}
+	mlog_exit(0);
+	return 0;
+out_err:
+	mlog_exit(status);
+	return status;
+}
+
+/* Release dquot structure from local quota file. ocfs2_release_dquot() has
+ * already started a transaction and obtained exclusive lock for global
+ * quota file. */
+static int ocfs2_local_release_dquot(struct dquot *dquot)
+{
+	int status;
+	int type = dquot->dq_type;
+	struct ocfs2_dquot *od = OCFS2_DQUOT(dquot);
+	struct super_block *sb = dquot->dq_sb;
+	struct ocfs2_local_disk_chunk *dchunk;
+	int offset;
+	handle_t *handle = journal_current_handle();
+
+	BUG_ON(!handle);
+	/* First write all local changes to global file */
+	status = ocfs2_global_release_dquot(dquot);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out;
+	}
+
+	status = ocfs2_journal_access(handle, sb_dqopt(sb)->files[type],
+			od->dq_chunk->qc_headerbh, OCFS2_JOURNAL_ACCESS_WRITE);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out;
+	}
+	offset = ol_dqblk_chunk_off(sb, od->dq_chunk->qc_num,
+					     od->dq_local_off);
+	dchunk = (struct ocfs2_local_disk_chunk *)
+			(od->dq_chunk->qc_headerbh->b_data);
+	/* Mark structure as freed */
+	lock_buffer(od->dq_chunk->qc_headerbh);
+	ocfs2_clear_bit(offset, dchunk->dqc_bitmap);
+	le32_add_cpu(&dchunk->dqc_free, 1);
+	unlock_buffer(od->dq_chunk->qc_headerbh);
+	status = ocfs2_journal_dirty(handle, od->dq_chunk->qc_headerbh);
+	if (status < 0) {
+		mlog_errno(status);
+		goto out;
+	}
+	status = 0;
+out:
+	/* Clear the read bit so that next time someone uses this
+	 * dquot he reads fresh info from disk and allocates local
+	 * dquot structure */
+	clear_bit(DQ_READ_B, &dquot->dq_flags);
+	return status;
+}
+
+static struct quota_format_ops ocfs2_format_ops = {
+	.check_quota_file	= ocfs2_local_check_quota_file,
+	.read_file_info		= ocfs2_local_read_info,
+	.write_file_info	= ocfs2_global_write_info,
+	.free_file_info		= ocfs2_local_free_info,
+	.read_dqblk		= ocfs2_local_read_dquot,
+	.commit_dqblk		= ocfs2_local_write_dquot,
+	.release_dqblk		= ocfs2_local_release_dquot,
+};
+
+struct quota_format_type ocfs2_quota_format = {
+	.qf_fmt_id = QFMT_OCFS2,
+	.qf_ops = &ocfs2_format_ops,
+	.qf_owner = THIS_MODULE
+};
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
index 4712bc7..92539fe 100644
--- a/fs/ocfs2/super.c
+++ b/fs/ocfs2/super.c
@@ -65,10 +65,13 @@
 #include "uptodate.h"
 #include "ver.h"
 #include "xattr.h"
+#include "quota.h"
 
 #include "buffer_head_io.h"
 
 static struct kmem_cache *ocfs2_inode_cachep = NULL;
+struct kmem_cache *ocfs2_dquot_cachep;
+struct kmem_cache *ocfs2_qf_chunk_cachep;
 
 /* OCFS2 needs to schedule several differnt types of work which
  * require cluster locking, disk I/O, recovery waits, etc. Since these
@@ -137,6 +140,8 @@ static const struct super_operations ocfs2_sops = {
 	.put_super	= ocfs2_put_super,
 	.remount_fs	= ocfs2_remount,
 	.show_options   = ocfs2_show_options,
+	.quota_read	= ocfs2_quota_read,
+	.quota_write	= ocfs2_quota_write,
 };
 
 enum {
@@ -1071,6 +1076,7 @@ static int __init ocfs2_init(void)
 
 	ocfs2_set_locking_protocol();
 
+	status = register_quota_format(&ocfs2_quota_format);
 leave:
 	if (status < 0) {
 		ocfs2_free_mem_caches();
@@ -1094,6 +1100,8 @@ static void __exit ocfs2_exit(void)
 		destroy_workqueue(ocfs2_wq);
 	}
 
+	unregister_quota_format(&ocfs2_quota_format);
+
 	debugfs_remove(ocfs2_debugfs_root);
 
 	ocfs2_free_mem_caches();
@@ -1209,8 +1217,27 @@ static int ocfs2_initialize_mem_caches(void)
 				       (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|
 						SLAB_MEM_SPREAD),
 				       ocfs2_inode_init_once);
-	if (!ocfs2_inode_cachep)
+	ocfs2_dquot_cachep = kmem_cache_create("ocfs2_dquot_cache",
+					sizeof(struct ocfs2_dquot),
+					0,
+					(SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|
+						SLAB_MEM_SPREAD),
+					NULL);
+	ocfs2_qf_chunk_cachep = kmem_cache_create("ocfs2_qf_chunk_cache",
+					sizeof(struct ocfs2_quota_chunk),
+					0,
+					(SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD),
+					NULL);
+	if (!ocfs2_inode_cachep || !ocfs2_dquot_cachep ||
+	    !ocfs2_qf_chunk_cachep) {
+		if (ocfs2_inode_cachep)
+			kmem_cache_destroy(ocfs2_inode_cachep);
+		if (ocfs2_dquot_cachep)
+			kmem_cache_destroy(ocfs2_dquot_cachep);
+		if (ocfs2_qf_chunk_cachep)
+			kmem_cache_destroy(ocfs2_qf_chunk_cachep);
 		return -ENOMEM;
+	}
 
 	return 0;
 }
@@ -1219,8 +1246,15 @@ static void ocfs2_free_mem_caches(void)
 {
 	if (ocfs2_inode_cachep)
 		kmem_cache_destroy(ocfs2_inode_cachep);
-
 	ocfs2_inode_cachep = NULL;
+
+	if (ocfs2_dquot_cachep)
+		kmem_cache_destroy(ocfs2_dquot_cachep);
+	ocfs2_dquot_cachep = NULL;
+
+	if (ocfs2_qf_chunk_cachep)
+		kmem_cache_destroy(ocfs2_qf_chunk_cachep);
+	ocfs2_qf_chunk_cachep = NULL;
 }
 
 static int ocfs2_get_sector(struct super_block *sb,
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 26/29] ocfs2: Add quota calls for allocation and freeing of inodes and space
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (24 preceding siblings ...)
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 25/29] ocfs2: Implementation of local and global quota file handling Jan Kara
@ 2008-10-24 22:08 ` Jan Kara
  2008-11-06  0:06   ` Mark Fasheh
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 27/29] ocfs2: Implement quota syncing thread Jan Kara
                   ` (2 subsequent siblings)
  28 siblings, 1 reply; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:08 UTC (permalink / raw)
  To: ocfs2-devel

Add quota calls for allocation and freeing of inodes and space, also update
estimates on number of needed credits for a transaction. Move out inode
allocation from ocfs2_mknod_locked() because vfs_dq_init() must be called
outside of a transaction.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/ocfs2/alloc.c   |   18 ++++++-
 fs/ocfs2/aops.c    |   16 +++++-
 fs/ocfs2/dir.c     |   24 ++++++++-
 fs/ocfs2/file.c    |   70 ++++++++++++++++++++++--
 fs/ocfs2/inode.c   |   10 +++-
 fs/ocfs2/journal.h |   84 ++++++++++++++++++++++++-----
 fs/ocfs2/namei.c   |  151 +++++++++++++++++++++++++++++++++++----------------
 fs/ocfs2/xattr.c   |    4 +-
 8 files changed, 298 insertions(+), 79 deletions(-)

diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c
index 0cc2deb..f18be46 100644
--- a/fs/ocfs2/alloc.c
+++ b/fs/ocfs2/alloc.c
@@ -28,6 +28,7 @@
 #include <linux/slab.h>
 #include <linux/highmem.h>
 #include <linux/swap.h>
+#include <linux/quotaops.h>
 
 #define MLOG_MASK_PREFIX ML_DISK_ALLOC
 #include <cluster/masklog.h>
@@ -6350,6 +6351,8 @@ static int ocfs2_do_truncate(struct ocfs2_super *osb,
 		goto bail;
 	}
 
+	vfs_dq_free_space_nodirty(inode,
+			ocfs2_clusters_to_bytes(osb->sb, clusters_to_del));
 	spin_lock(&OCFS2_I(inode)->ip_lock);
 	OCFS2_I(inode)->ip_clusters = le32_to_cpu(fe->i_clusters) -
 				      clusters_to_del;
@@ -6663,6 +6666,7 @@ int ocfs2_convert_inline_data_to_extents(struct inode *inode,
 	struct page **pages = NULL;
 	loff_t end = osb->s_clustersize;
 	struct ocfs2_extent_tree et;
+	int did_quota = 0;
 
 	has_data = i_size_read(inode) ? 1 : 0;
 
@@ -6682,7 +6686,8 @@ int ocfs2_convert_inline_data_to_extents(struct inode *inode,
 		}
 	}
 
-	handle = ocfs2_start_trans(osb, OCFS2_INLINE_TO_EXTENTS_CREDITS);
+	handle = ocfs2_start_trans(osb,
+				   ocfs2_inline_to_extents_credits(osb->sb));
 	if (IS_ERR(handle)) {
 		ret = PTR_ERR(handle);
 		mlog_errno(ret);
@@ -6701,6 +6706,13 @@ int ocfs2_convert_inline_data_to_extents(struct inode *inode,
 		unsigned int page_end;
 		u64 phys;
 
+		if (vfs_dq_alloc_space_nodirty(inode,
+				       ocfs2_clusters_to_bytes(osb->sb, 1))) {
+			ret = -EDQUOT;
+			goto out_commit;
+		}
+		did_quota = 1;
+
 		ret = ocfs2_claim_clusters(osb, handle, data_ac, 1, &bit_off,
 					   &num);
 		if (ret) {
@@ -6774,6 +6786,10 @@ int ocfs2_convert_inline_data_to_extents(struct inode *inode,
 	}
 
 out_commit:
+	if (ret < 0 && did_quota)
+		vfs_dq_free_space_nodirty(inode,
+					  ocfs2_clusters_to_bytes(osb->sb, 1));
+
 	ocfs2_commit_trans(osb, handle);
 
 out_unlock:
diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c
index c22543b..a427888 100644
--- a/fs/ocfs2/aops.c
+++ b/fs/ocfs2/aops.c
@@ -27,6 +27,7 @@
 #include <linux/swap.h>
 #include <linux/pipe_fs_i.h>
 #include <linux/mpage.h>
+#include <linux/quotaops.h>
 
 #define MLOG_MASK_PREFIX ML_FILE_IO
 #include <cluster/masklog.h>
@@ -1750,6 +1751,11 @@ int ocfs2_write_begin_nolock(struct address_space *mapping,
 
 	wc->w_handle = handle;
 
+	if (clusters_to_alloc && vfs_dq_alloc_space_nodirty(inode,
+			ocfs2_clusters_to_bytes(osb->sb, clusters_to_alloc))) {
+		ret = -EDQUOT;
+		goto out_commit;
+	}
 	/*
 	 * We don't want this to fail in ocfs2_write_end(), so do it
 	 * here.
@@ -1758,7 +1764,7 @@ int ocfs2_write_begin_nolock(struct address_space *mapping,
 				   OCFS2_JOURNAL_ACCESS_WRITE);
 	if (ret) {
 		mlog_errno(ret);
-		goto out_commit;
+		goto out_quota;
 	}
 
 	/*
@@ -1771,14 +1777,14 @@ int ocfs2_write_begin_nolock(struct address_space *mapping,
 					 mmap_page);
 	if (ret) {
 		mlog_errno(ret);
-		goto out_commit;
+		goto out_quota;
 	}
 
 	ret = ocfs2_write_cluster_by_desc(mapping, data_ac, meta_ac, wc, pos,
 					  len);
 	if (ret) {
 		mlog_errno(ret);
-		goto out_commit;
+		goto out_quota;
 	}
 
 	if (data_ac)
@@ -1790,6 +1796,10 @@ success:
 	*pagep = wc->w_target_page;
 	*fsdata = wc;
 	return 0;
+out_quota:
+	if (clusters_to_alloc)
+		vfs_dq_free_space(inode,
+			  ocfs2_clusters_to_bytes(osb->sb, clusters_to_alloc));
 out_commit:
 	ocfs2_commit_trans(osb, handle);
 
diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c
index 084ba9c..57c6e19 100644
--- a/fs/ocfs2/dir.c
+++ b/fs/ocfs2/dir.c
@@ -40,6 +40,7 @@
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/highmem.h>
+#include <linux/quotaops.h>
 
 #define MLOG_MASK_PREFIX ML_NAMEI
 #include <cluster/masklog.h>
@@ -1216,9 +1217,9 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh,
 				   unsigned int blocks_wanted,
 				   struct buffer_head **first_block_bh)
 {
-	int ret, credits = OCFS2_INLINE_TO_EXTENTS_CREDITS;
 	u32 alloc, bit_off, len;
 	struct super_block *sb = dir->i_sb;
+	int ret, credits = ocfs2_inline_to_extents_credits(sb);
 	u64 blkno, bytes = blocks_wanted << sb->s_blocksize_bits;
 	struct ocfs2_super *osb = OCFS2_SB(dir->i_sb);
 	struct ocfs2_inode_info *oi = OCFS2_I(dir);
@@ -1227,6 +1228,7 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh,
 	struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
 	handle_t *handle;
 	struct ocfs2_extent_tree et;
+	int did_quota = 0;
 
 	ocfs2_init_dinode_extent_tree(&et, dir, di_bh);
 
@@ -1264,6 +1266,12 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh,
 		goto out_sem;
 	}
 
+	if (vfs_dq_alloc_space_nodirty(dir,
+				ocfs2_clusters_to_bytes(osb->sb, alloc))) {
+		ret = -EDQUOT;
+		goto out_commit;
+	}
+	did_quota = 1;
 	/*
 	 * Try to claim as many clusters as the bitmap can give though
 	 * if we only get one now, that's enough to continue. The rest
@@ -1386,6 +1394,9 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh,
 	dirdata_bh = NULL;
 
 out_commit:
+	if (ret < 0 && did_quota)
+		vfs_dq_free_space_nodirty(dir,
+			ocfs2_clusters_to_bytes(osb->sb, 2));
 	ocfs2_commit_trans(osb, handle);
 
 out_sem:
@@ -1410,7 +1421,7 @@ static int ocfs2_do_extend_dir(struct super_block *sb,
 			       struct buffer_head **new_bh)
 {
 	int status;
-	int extend;
+	int extend, did_quota = 0;
 	u64 p_blkno, v_blkno;
 
 	spin_lock(&OCFS2_I(dir)->ip_lock);
@@ -1420,6 +1431,13 @@ static int ocfs2_do_extend_dir(struct super_block *sb,
 	if (extend) {
 		u32 offset = OCFS2_I(dir)->ip_clusters;
 
+		if (vfs_dq_alloc_space_nodirty(dir,
+					ocfs2_clusters_to_bytes(sb, 1))) {
+			status = -EDQUOT;
+			goto bail;
+		}
+		did_quota = 1;
+
 		status = ocfs2_add_inode_data(OCFS2_SB(sb), dir, &offset,
 					      1, 0, parent_fe_bh, handle,
 					      data_ac, meta_ac, NULL);
@@ -1445,6 +1463,8 @@ static int ocfs2_do_extend_dir(struct super_block *sb,
 	}
 	status = 0;
 bail:
+	if (did_quota && status < 0)
+		vfs_dq_free_space_nodirty(dir, ocfs2_clusters_to_bytes(sb, 1));
 	mlog_exit(status);
 	return status;
 }
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index 9af16e0..75cdb0e 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -35,6 +35,7 @@
 #include <linux/mount.h>
 #include <linux/writeback.h>
 #include <linux/falloc.h>
+#include <linux/quotaops.h>
 
 #define MLOG_MASK_PREFIX ML_INODE
 #include <cluster/masklog.h>
@@ -56,6 +57,7 @@
 #include "suballoc.h"
 #include "super.h"
 #include "xattr.h"
+#include "quota.h"
 
 #include "buffer_head_io.h"
 
@@ -536,6 +538,8 @@ static int __ocfs2_extend_allocation(struct inode *inode, u32 logical_start,
 	enum ocfs2_alloc_restarted why;
 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
 	struct ocfs2_extent_tree et;
+	u32 total_clusters = clusters_to_add;
+	int did_quota = 0;
 
 	mlog_entry("(clusters_to_add = %u)\n", clusters_to_add);
 
@@ -584,6 +588,12 @@ restart_all:
 		goto leave;
 	}
 
+	if (!did_quota && vfs_dq_alloc_space_nodirty(inode,
+	    ocfs2_clusters_to_bytes(osb->sb, total_clusters))) {
+		status = -EDQUOT;
+		goto leave;
+	}
+	did_quota = 1;
 restarted_transaction:
 	/* reserve a write to the file entry early on - that we if we
 	 * run out of credits in the allocation path, we can still
@@ -654,6 +664,9 @@ restarted_transaction:
 	     OCFS2_I(inode)->ip_clusters, (long long)i_size_read(inode));
 
 leave:
+	if (status < 0 && did_quota)
+		vfs_dq_free_space(inode,
+			ocfs2_clusters_to_bytes(osb->sb, total_clusters));
 	if (handle) {
 		ocfs2_commit_trans(osb, handle);
 		handle = NULL;
@@ -886,6 +899,9 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
 	struct ocfs2_super *osb = OCFS2_SB(sb);
 	struct buffer_head *bh = NULL;
 	handle_t *handle = NULL;
+	int locked[MAXQUOTAS] = {0, 0};
+	int credits, qtype;
+	struct ocfs2_mem_dqinfo *oinfo;
 
 	mlog_entry("(0x%p, '%.*s')\n", dentry,
 	           dentry->d_name.len, dentry->d_name.name);
@@ -956,11 +972,47 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
 		}
 	}
 
-	handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
-	if (IS_ERR(handle)) {
-		status = PTR_ERR(handle);
-		mlog_errno(status);
-		goto bail_unlock;
+	if ((attr->ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) ||
+	    (attr->ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) {
+		credits = OCFS2_INODE_UPDATE_CREDITS;
+		if (attr->ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid
+		    && OCFS2_HAS_RO_COMPAT_FEATURE(sb,
+		    OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) {
+			oinfo = sb_dqinfo(sb, USRQUOTA)->dqi_priv;
+			status = ocfs2_lock_global_qf(oinfo, 1);
+			if (status < 0)
+				goto bail_unlock;
+			credits += ocfs2_calc_qinit_credits(sb, USRQUOTA) +
+				ocfs2_calc_qdel_credits(sb, USRQUOTA);
+			locked[USRQUOTA] = 1;
+		}
+		if (attr->ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid
+		    && OCFS2_HAS_RO_COMPAT_FEATURE(sb,
+		    OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) {
+			oinfo = sb_dqinfo(sb, GRPQUOTA)->dqi_priv;
+			status = ocfs2_lock_global_qf(oinfo, 1);
+			if (status < 0)
+				goto bail_unlock;
+			credits += ocfs2_calc_qinit_credits(sb, GRPQUOTA) +
+				   ocfs2_calc_qdel_credits(sb, GRPQUOTA);
+			locked[GRPQUOTA] = 1;
+		}
+		handle = ocfs2_start_trans(osb, credits);
+		if (IS_ERR(handle)) {
+			status = PTR_ERR(handle);
+			mlog_errno(status);
+			goto bail_unlock;
+		}
+		status = vfs_dq_transfer(inode, attr) ? -EDQUOT : 0;
+		if (status < 0)
+			goto bail_commit;
+	} else {
+		handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
+		if (IS_ERR(handle)) {
+			status = PTR_ERR(handle);
+			mlog_errno(status);
+			goto bail_unlock;
+		}
 	}
 
 	/*
@@ -983,6 +1035,12 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
 bail_commit:
 	ocfs2_commit_trans(osb, handle);
 bail_unlock:
+	for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+		if (!locked[qtype])
+			continue;
+		oinfo = sb_dqinfo(sb, qtype)->dqi_priv;
+		ocfs2_unlock_global_qf(oinfo, 1);
+	}
 	ocfs2_inode_unlock(inode, 1);
 bail_unlock_rw:
 	if (size_change)
@@ -1259,7 +1317,7 @@ static int __ocfs2_remove_inode_range(struct inode *inode,
 		}
 	}
 
-	handle = ocfs2_start_trans(osb, OCFS2_REMOVE_EXTENT_CREDITS);
+	handle = ocfs2_start_trans(osb, ocfs2_remove_extent_credits(osb->sb));
 	if (IS_ERR(handle)) {
 		ret = PTR_ERR(handle);
 		mlog_errno(ret);
diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c
index 738b63f..224f78a 100644
--- a/fs/ocfs2/inode.c
+++ b/fs/ocfs2/inode.c
@@ -28,6 +28,7 @@
 #include <linux/slab.h>
 #include <linux/highmem.h>
 #include <linux/pagemap.h>
+#include <linux/quotaops.h>
 
 #include <asm/byteorder.h>
 
@@ -619,7 +620,8 @@ static int ocfs2_remove_inode(struct inode *inode,
 		goto bail;
 	}
 
-	handle = ocfs2_start_trans(osb, OCFS2_DELETE_INODE_CREDITS);
+	handle = ocfs2_start_trans(osb, OCFS2_DELETE_INODE_CREDITS +
+					ocfs2_quota_trans_credits(inode->i_sb));
 	if (IS_ERR(handle)) {
 		status = PTR_ERR(handle);
 		mlog_errno(status);
@@ -651,6 +653,7 @@ static int ocfs2_remove_inode(struct inode *inode,
 	}
 
 	ocfs2_remove_from_cache(inode, di_bh);
+	vfs_dq_free_inode(inode);
 
 	status = ocfs2_free_dinode(handle, inode_alloc_inode,
 				   inode_alloc_bh, di);
@@ -933,7 +936,10 @@ void ocfs2_delete_inode(struct inode *inode)
 
 	mlog_entry("(inode->i_ino = %lu)\n", inode->i_ino);
 
-	if (is_bad_inode(inode)) {
+	/* When we fail in read_inode() we mark inode as bad. The second test
+	 * catches the case when inode allocation fails before allocating
+	 * a block for inode. */
+	if (is_bad_inode(inode) || !OCFS2_I(inode)->ip_blkno) {
 		mlog(0, "Skipping delete of bad inode\n");
 		goto bail;
 	}
diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h
index d4d14e9..c13bd8c 100644
--- a/fs/ocfs2/journal.h
+++ b/fs/ocfs2/journal.h
@@ -293,6 +293,37 @@ int                  ocfs2_journal_dirty_data(handle_t *handle,
 /* extended attribute block update */
 #define OCFS2_XATTR_BLOCK_UPDATE_CREDITS 1
 
+/* global quotafile inode update, data block */
+#define OCFS2_QINFO_WRITE_CREDITS (OCFS2_INODE_UPDATE_CREDITS + 1)
+
+/*
+ * The two writes below can accidentally see global info dirty due
+ * to set_info() quotactl so make them prepared for the writes.
+ */
+/* quota data block, global info */
+/* Write to local quota file */
+#define OCFS2_QWRITE_CREDITS (OCFS2_QINFO_WRITE_CREDITS + 1)
+
+/* global quota data block, local quota data block, global quota inode,
+ * global quota info */
+#define OCFS2_QSYNC_CREDITS (OCFS2_INODE_UPDATE_CREDITS + 3)
+
+static inline int ocfs2_quota_trans_credits(struct super_block *sb)
+{
+	int credits = 0;
+
+	if (OCFS2_HAS_RO_COMPAT_FEATURE(sb, OCFS2_FEATURE_RO_COMPAT_USRQUOTA))
+		credits += OCFS2_QWRITE_CREDITS;
+	if (OCFS2_HAS_RO_COMPAT_FEATURE(sb, OCFS2_FEATURE_RO_COMPAT_GRPQUOTA))
+		credits += OCFS2_QWRITE_CREDITS;
+	return credits;
+}
+
+/* Number of credits needed for removing quota structure from file */
+int ocfs2_calc_qdel_credits(struct super_block *sb, int type);
+/* Number of credits needed for initialization of new quota structure */
+int ocfs2_calc_qinit_credits(struct super_block *sb, int type);
+
 /* group extend. inode update and last group update. */
 #define OCFS2_GROUP_EXTEND_CREDITS	(OCFS2_INODE_UPDATE_CREDITS + 1)
 
@@ -303,8 +334,11 @@ int                  ocfs2_journal_dirty_data(handle_t *handle,
  * prev. group desc. if we relink. */
 #define OCFS2_SUBALLOC_ALLOC (3)
 
-#define OCFS2_INLINE_TO_EXTENTS_CREDITS (OCFS2_SUBALLOC_ALLOC		\
-					 + OCFS2_INODE_UPDATE_CREDITS)
+static inline int ocfs2_inline_to_extents_credits(struct super_block *sb)
+{
+	return OCFS2_SUBALLOC_ALLOC + OCFS2_INODE_UPDATE_CREDITS +
+	       ocfs2_quota_trans_credits(sb);
+}
 
 /* dinode + group descriptor update. We don't relink on free yet. */
 #define OCFS2_SUBALLOC_FREE  (2)
@@ -313,16 +347,23 @@ int                  ocfs2_journal_dirty_data(handle_t *handle,
 #define OCFS2_TRUNCATE_LOG_FLUSH_ONE_REC (OCFS2_SUBALLOC_FREE 		      \
 					 + OCFS2_TRUNCATE_LOG_UPDATE)
 
-#define OCFS2_REMOVE_EXTENT_CREDITS (OCFS2_TRUNCATE_LOG_UPDATE + OCFS2_INODE_UPDATE_CREDITS)
+static inline int ocfs2_remove_extent_credits(struct super_block *sb)
+{
+	return OCFS2_TRUNCATE_LOG_UPDATE + OCFS2_INODE_UPDATE_CREDITS +
+	       ocfs2_quota_trans_credits(sb);
+}
 
 /* data block for new dir/symlink, 2 for bitmap updates (bitmap fe +
  * bitmap block for the new bit) */
 #define OCFS2_DIR_LINK_ADDITIONAL_CREDITS (1 + 2)
 
 /* parent fe, parent block, new file entry, inode alloc fe, inode alloc
- * group descriptor + mkdir/symlink blocks */
-#define OCFS2_MKNOD_CREDITS (3 + OCFS2_SUBALLOC_ALLOC                         \
-			    + OCFS2_DIR_LINK_ADDITIONAL_CREDITS)
+ * group descriptor + mkdir/symlink blocks + quota update */
+static inline int ocfs2_mknod_credits(struct super_block *sb)
+{
+	return 3 + OCFS2_SUBALLOC_ALLOC + OCFS2_DIR_LINK_ADDITIONAL_CREDITS +
+	       ocfs2_quota_trans_credits(sb);
+}
 
 /* local alloc metadata change + main bitmap updates */
 #define OCFS2_WINDOW_MOVE_CREDITS (OCFS2_INODE_UPDATE_CREDITS                 \
@@ -332,13 +373,21 @@ int                  ocfs2_journal_dirty_data(handle_t *handle,
  * for the dinode, one for the new block. */
 #define OCFS2_SIMPLE_DIR_EXTEND_CREDITS (2)
 
-/* file update (nlink, etc) + directory mtime/ctime + dir entry block */
-#define OCFS2_LINK_CREDITS  (2*OCFS2_INODE_UPDATE_CREDITS + 1)
+/* file update (nlink, etc) + directory mtime/ctime + dir entry block + quota
+ * update on dir */
+static inline int ocfs2_link_credits(struct super_block *sb)
+{
+	return 2*OCFS2_INODE_UPDATE_CREDITS + 1 +
+	       ocfs2_quota_trans_credits(sb);
+}
 
 /* inode + dir inode (if we unlink a dir), + dir entry block + orphan
  * dir inode link */
-#define OCFS2_UNLINK_CREDITS  (2 * OCFS2_INODE_UPDATE_CREDITS + 1             \
-			      + OCFS2_LINK_CREDITS)
+static inline int ocfs2_unlink_credits(struct super_block *sb)
+{
+	/* The quota update from ocfs2_link_credits is unused here... */
+	return 2 * OCFS2_INODE_UPDATE_CREDITS + 1 + ocfs2_link_credits(sb);
+}
 
 /* dinode + orphan dir dinode + inode alloc dinode + orphan dir entry +
  * inode alloc group descriptor */
@@ -347,8 +396,10 @@ int                  ocfs2_journal_dirty_data(handle_t *handle,
 /* dinode update, old dir dinode update, new dir dinode update, old
  * dir dir entry, new dir dir entry, dir entry update for renaming
  * directory + target unlink */
-#define OCFS2_RENAME_CREDITS (3 * OCFS2_INODE_UPDATE_CREDITS + 3              \
-			     + OCFS2_UNLINK_CREDITS)
+static inline int ocfs2_rename_credits(struct super_block *sb)
+{
+	return 3 * OCFS2_INODE_UPDATE_CREDITS + 3 + ocfs2_unlink_credits(sb);
+}
 
 /* global bitmap dinode, group desc., relinked group,
  * suballocator dinode, group desc., relinked group,
@@ -386,18 +437,19 @@ static inline int ocfs2_calc_extend_credits(struct super_block *sb,
 	 * credit for the dinode there. */
 	extent_blocks = 1 + 1 + le16_to_cpu(root_el->l_tree_depth);
 
-	return bitmap_blocks + sysfile_bitmap_blocks + extent_blocks;
+	return bitmap_blocks + sysfile_bitmap_blocks + extent_blocks +
+	       ocfs2_quota_trans_credits(sb);
 }
 
 static inline int ocfs2_calc_symlink_credits(struct super_block *sb)
 {
-	int blocks = OCFS2_MKNOD_CREDITS;
+	int blocks = ocfs2_mknod_credits(sb);
 
 	/* links can be longer than one block so we may update many
 	 * within our single allocated extent. */
 	blocks += ocfs2_clusters_to_blocks(sb, 1);
 
-	return blocks;
+	return blocks + ocfs2_quota_trans_credits(sb);
 }
 
 static inline int ocfs2_calc_group_alloc_credits(struct super_block *sb,
@@ -434,6 +486,8 @@ static inline int ocfs2_calc_tree_trunc_credits(struct super_block *sb,
 	/* update to the truncate log. */
 	credits += OCFS2_TRUNCATE_LOG_UPDATE;
 
+	credits += ocfs2_quota_trans_credits(sb);
+
 	return credits;
 }
 
diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c
index f4967e6..11a3fa7 100644
--- a/fs/ocfs2/namei.c
+++ b/fs/ocfs2/namei.c
@@ -40,6 +40,7 @@
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/highmem.h>
+#include <linux/quotaops.h>
 
 #define MLOG_MASK_PREFIX ML_NAMEI
 #include <cluster/masklog.h>
@@ -66,12 +67,12 @@
 
 static int ocfs2_mknod_locked(struct ocfs2_super *osb,
 			      struct inode *dir,
-			      struct dentry *dentry, int mode,
+			      struct inode *inode,
+			      struct dentry *dentry,
 			      dev_t dev,
 			      struct buffer_head **new_fe_bh,
 			      struct buffer_head *parent_fe_bh,
 			      handle_t *handle,
-			      struct inode **ret_inode,
 			      struct ocfs2_alloc_context *inode_ac);
 
 static int ocfs2_prepare_orphan_dir(struct ocfs2_super *osb,
@@ -186,6 +187,35 @@ bail:
 	return ret;
 }
 
+static struct inode *ocfs2_get_init_inode(struct inode *dir, int mode)
+{
+	struct inode *inode;
+
+	inode = new_inode(dir->i_sb);
+	if (!inode) {
+		mlog(ML_ERROR, "new_inode failed!\n");
+		return NULL;
+	}
+
+	/* populate as many fields early on as possible - many of
+	 * these are used by the support functions here and in
+	 * callers. */
+	if (S_ISDIR(mode))
+		inode->i_nlink = 2;
+	else
+		inode->i_nlink = 1;
+	inode->i_uid = current->fsuid;
+	if (dir->i_mode & S_ISGID) {
+		inode->i_gid = dir->i_gid;
+		if (S_ISDIR(mode))
+			mode |= S_ISGID;
+	} else
+		inode->i_gid = current->fsgid;
+	inode->i_mode = mode;
+	vfs_dq_init(inode);
+	return inode;
+}
+
 static int ocfs2_mknod(struct inode *dir,
 		       struct dentry *dentry,
 		       int mode,
@@ -201,6 +231,7 @@ static int ocfs2_mknod(struct inode *dir,
 	struct inode *inode = NULL;
 	struct ocfs2_alloc_context *inode_ac = NULL;
 	struct ocfs2_alloc_context *data_ac = NULL;
+	int did_quota_inode = 0;
 
 	mlog_entry("(0x%p, 0x%p, %d, %lu, '%.*s')\n", dir, dentry, mode,
 		   (unsigned long)dev, dentry->d_name.len,
@@ -260,7 +291,14 @@ static int ocfs2_mknod(struct inode *dir,
 		}
 	}
 
-	handle = ocfs2_start_trans(osb, OCFS2_MKNOD_CREDITS);
+	inode = ocfs2_get_init_inode(dir, mode);
+	if (!inode) {
+		status = -ENOMEM;
+		mlog_errno(status);
+		goto leave;
+	}
+
+	handle = ocfs2_start_trans(osb, ocfs2_mknod_credits(dir->i_sb));
 	if (IS_ERR(handle)) {
 		status = PTR_ERR(handle);
 		handle = NULL;
@@ -268,10 +306,19 @@ static int ocfs2_mknod(struct inode *dir,
 		goto leave;
 	}
 
+	/* We don't use standard VFS wrapper because we don't want vfs_dq_init
+	 * to be called. */
+	if (sb_any_quota_active(osb->sb) &&
+	    osb->sb->dq_op->alloc_inode(inode, 1) == NO_QUOTA) {
+		status = -EDQUOT;
+		goto leave;
+	}
+	did_quota_inode = 1;
+
 	/* do the real work now. */
-	status = ocfs2_mknod_locked(osb, dir, dentry, mode, dev,
+	status = ocfs2_mknod_locked(osb, dir, inode, dentry, dev,
 				    &new_fe_bh, parent_fe_bh, handle,
-				    &inode, inode_ac);
+				    inode_ac);
 	if (status < 0) {
 		mlog_errno(status);
 		goto leave;
@@ -320,6 +367,8 @@ static int ocfs2_mknod(struct inode *dir,
 	d_instantiate(dentry, inode);
 	status = 0;
 leave:
+	if (status < 0 && did_quota_inode)
+		vfs_dq_free_inode(inode);
 	if (handle)
 		ocfs2_commit_trans(osb, handle);
 
@@ -332,8 +381,10 @@ leave:
 	brelse(de_bh);
 	brelse(parent_fe_bh);
 
-	if ((status < 0) && inode)
+	if ((status < 0) && inode) {
+		clear_nlink(inode);
 		iput(inode);
+	}
 
 	if (inode_ac)
 		ocfs2_free_alloc_context(inode_ac);
@@ -348,12 +399,12 @@ leave:
 
 static int ocfs2_mknod_locked(struct ocfs2_super *osb,
 			      struct inode *dir,
-			      struct dentry *dentry, int mode,
+			      struct inode *inode,
+			      struct dentry *dentry,
 			      dev_t dev,
 			      struct buffer_head **new_fe_bh,
 			      struct buffer_head *parent_fe_bh,
 			      handle_t *handle,
-			      struct inode **ret_inode,
 			      struct ocfs2_alloc_context *inode_ac)
 {
 	int status = 0;
@@ -361,14 +412,12 @@ static int ocfs2_mknod_locked(struct ocfs2_super *osb,
 	struct ocfs2_extent_list *fel;
 	u64 fe_blkno = 0;
 	u16 suballoc_bit;
-	struct inode *inode = NULL;
 
-	mlog_entry("(0x%p, 0x%p, %d, %lu, '%.*s')\n", dir, dentry, mode,
-		   (unsigned long)dev, dentry->d_name.len,
+	mlog_entry("(0x%p, 0x%p, %d, %lu, '%.*s')\n", dir, dentry,
+		   inode->i_mode, (unsigned long)dev, dentry->d_name.len,
 		   dentry->d_name.name);
 
 	*new_fe_bh = NULL;
-	*ret_inode = NULL;
 
 	status = ocfs2_claim_new_inode(osb, handle, inode_ac, &suballoc_bit,
 				       &fe_blkno);
@@ -377,23 +426,11 @@ static int ocfs2_mknod_locked(struct ocfs2_super *osb,
 		goto leave;
 	}
 
-	inode = new_inode(dir->i_sb);
-	if (!inode) {
-		status = -ENOMEM;
-		mlog(ML_ERROR, "new_inode failed!\n");
-		goto leave;
-	}
-
 	/* populate as many fields early on as possible - many of
 	 * these are used by the support functions here and in
 	 * callers. */
 	inode->i_ino = ino_from_blkno(osb->sb, fe_blkno);
 	OCFS2_I(inode)->ip_blkno = fe_blkno;
-	if (S_ISDIR(mode))
-		inode->i_nlink = 2;
-	else
-		inode->i_nlink = 1;
-	inode->i_mode = mode;
 	spin_lock(&osb->osb_lock);
 	inode->i_generation = osb->s_next_generation++;
 	spin_unlock(&osb->osb_lock);
@@ -421,17 +458,11 @@ static int ocfs2_mknod_locked(struct ocfs2_super *osb,
 	fe->i_blkno = cpu_to_le64(fe_blkno);
 	fe->i_suballoc_bit = cpu_to_le16(suballoc_bit);
 	fe->i_suballoc_slot = cpu_to_le16(inode_ac->ac_alloc_slot);
-	fe->i_uid = cpu_to_le32(current->fsuid);
-	if (dir->i_mode & S_ISGID) {
-		fe->i_gid = cpu_to_le32(dir->i_gid);
-		if (S_ISDIR(mode))
-			mode |= S_ISGID;
-	} else
-		fe->i_gid = cpu_to_le32(current->fsgid);
-	fe->i_mode = cpu_to_le16(mode);
-	if (S_ISCHR(mode) || S_ISBLK(mode))
+	fe->i_uid = cpu_to_le32(inode->i_uid);
+	fe->i_gid = cpu_to_le32(inode->i_gid);
+	fe->i_mode = cpu_to_le16(inode->i_mode);
+	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
 		fe->id1.dev1.i_rdev = cpu_to_le64(huge_encode_dev(dev));
-
 	fe->i_links_count = cpu_to_le16(inode->i_nlink);
 
 	fe->i_last_eb_blk = 0;
@@ -446,7 +477,7 @@ static int ocfs2_mknod_locked(struct ocfs2_super *osb,
 	/*
 	 * If supported, directories start with inline data.
 	 */
-	if (S_ISDIR(mode) && ocfs2_supports_inline_data(osb)) {
+	if (S_ISDIR(inode->i_mode) && ocfs2_supports_inline_data(osb)) {
 		u16 feat = le16_to_cpu(fe->i_dyn_features);
 
 		fe->i_dyn_features = cpu_to_le16(feat | OCFS2_INLINE_DATA_FL);
@@ -484,17 +515,12 @@ static int ocfs2_mknod_locked(struct ocfs2_super *osb,
 	status = 0; /* error in ocfs2_create_new_inode_locks is not
 		     * critical */
 
-	*ret_inode = inode;
 leave:
 	if (status < 0) {
 		if (*new_fe_bh) {
 			brelse(*new_fe_bh);
 			*new_fe_bh = NULL;
 		}
-		if (inode) {
-			clear_nlink(inode);
-			iput(inode);
-		}
 	}
 
 	mlog_exit(status);
@@ -588,7 +614,7 @@ static int ocfs2_link(struct dentry *old_dentry,
 		goto out_unlock_inode;
 	}
 
-	handle = ocfs2_start_trans(osb, OCFS2_LINK_CREDITS);
+	handle = ocfs2_start_trans(osb, ocfs2_link_credits(osb->sb));
 	if (IS_ERR(handle)) {
 		err = PTR_ERR(handle);
 		handle = NULL;
@@ -775,7 +801,7 @@ static int ocfs2_unlink(struct inode *dir,
 		}
 	}
 
-	handle = ocfs2_start_trans(osb, OCFS2_UNLINK_CREDITS);
+	handle = ocfs2_start_trans(osb, ocfs2_unlink_credits(osb->sb));
 	if (IS_ERR(handle)) {
 		status = PTR_ERR(handle);
 		handle = NULL;
@@ -1181,7 +1207,7 @@ static int ocfs2_rename(struct inode *old_dir,
 		}
 	}
 
-	handle = ocfs2_start_trans(osb, OCFS2_RENAME_CREDITS);
+	handle = ocfs2_start_trans(osb, ocfs2_rename_credits(osb->sb));
 	if (IS_ERR(handle)) {
 		status = PTR_ERR(handle);
 		handle = NULL;
@@ -1496,6 +1522,7 @@ static int ocfs2_symlink(struct inode *dir,
 	handle_t *handle = NULL;
 	struct ocfs2_alloc_context *inode_ac = NULL;
 	struct ocfs2_alloc_context *data_ac = NULL;
+	int did_quota = 0, did_quota_inode = 0;
 
 	mlog_entry("(0x%p, 0x%p, symname='%s' actual='%.*s')\n", dir,
 		   dentry, symname, dentry->d_name.len, dentry->d_name.name);
@@ -1552,6 +1579,13 @@ static int ocfs2_symlink(struct inode *dir,
 		}
 	}
 
+	inode = ocfs2_get_init_inode(dir, S_IFLNK | S_IRWXUGO);
+	if (!inode) {
+		status = -ENOMEM;
+		mlog_errno(status);
+		goto bail;
+	}
+
 	handle = ocfs2_start_trans(osb, credits);
 	if (IS_ERR(handle)) {
 		status = PTR_ERR(handle);
@@ -1560,10 +1594,18 @@ static int ocfs2_symlink(struct inode *dir,
 		goto bail;
 	}
 
-	status = ocfs2_mknod_locked(osb, dir, dentry,
-				    S_IFLNK | S_IRWXUGO, 0,
-				    &new_fe_bh, parent_fe_bh, handle,
-				    &inode, inode_ac);
+	/* We don't use standard VFS wrapper because we don't want vfs_dq_init
+	 * to be called. */
+	if (sb_any_quota_active(osb->sb) &&
+	    osb->sb->dq_op->alloc_inode(inode, 1) == NO_QUOTA) {
+		status = -EDQUOT;
+		goto bail;
+	}
+	did_quota_inode = 1;
+
+	status = ocfs2_mknod_locked(osb, dir, inode, dentry,
+				    0, &new_fe_bh, parent_fe_bh, handle,
+				    inode_ac);
 	if (status < 0) {
 		mlog_errno(status);
 		goto bail;
@@ -1576,6 +1618,12 @@ static int ocfs2_symlink(struct inode *dir,
 		u32 offset = 0;
 
 		inode->i_op = &ocfs2_symlink_inode_operations;
+		if (vfs_dq_alloc_space_nodirty(inode,
+		    ocfs2_clusters_to_bytes(osb->sb, 1))) {
+			status = -EDQUOT;
+			goto bail;
+		}
+		did_quota = 1;
 		status = ocfs2_add_inode_data(osb, inode, &offset, 1, 0,
 					      new_fe_bh,
 					      handle, data_ac, NULL,
@@ -1632,6 +1680,11 @@ static int ocfs2_symlink(struct inode *dir,
 	dentry->d_op = &ocfs2_dentry_ops;
 	d_instantiate(dentry, inode);
 bail:
+	if (status < 0 && did_quota)
+		vfs_dq_free_space_nodirty(inode,
+					ocfs2_clusters_to_bytes(osb->sb, 1));
+	if (status < 0 && did_quota_inode)
+		vfs_dq_free_inode(inode);
 	if (handle)
 		ocfs2_commit_trans(osb, handle);
 
@@ -1644,8 +1697,10 @@ bail:
 		ocfs2_free_alloc_context(inode_ac);
 	if (data_ac)
 		ocfs2_free_alloc_context(data_ac);
-	if ((status < 0) && inode)
+	if ((status < 0) && inode) {
+		clear_nlink(inode);
 		iput(inode);
+	}
 
 	mlog_exit(status);
 
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
index 802c414..d4bc0ab 100644
--- a/fs/ocfs2/xattr.c
+++ b/fs/ocfs2/xattr.c
@@ -332,7 +332,7 @@ static int __ocfs2_remove_xattr_range(struct inode *inode,
 		}
 	}
 
-	handle = ocfs2_start_trans(osb, OCFS2_REMOVE_EXTENT_CREDITS);
+	handle = ocfs2_start_trans(osb, ocfs2_remove_extent_credits(osb->sb));
 	if (IS_ERR(handle)) {
 		ret = PTR_ERR(handle);
 		mlog_errno(ret);
@@ -4312,7 +4312,7 @@ static int ocfs2_rm_xattr_cluster(struct inode *inode,
 		}
 	}
 
-	handle = ocfs2_start_trans(osb, OCFS2_REMOVE_EXTENT_CREDITS);
+	handle = ocfs2_start_trans(osb, ocfs2_remove_extent_credits(osb->sb));
 	if (handle == NULL) {
 		ret = -ENOMEM;
 		mlog_errno(ret);
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 27/29] ocfs2: Implement quota syncing thread
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (25 preceding siblings ...)
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 26/29] ocfs2: Add quota calls for allocation and freeing of inodes and space Jan Kara
@ 2008-10-24 22:08 ` Jan Kara
  2008-11-06  0:27   ` Mark Fasheh
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 28/29] ocfs2: Implement quota recovery Jan Kara
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 29/29] ocfs2: Enable quota accounting on mount, disable on umount Jan Kara
  28 siblings, 1 reply; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:08 UTC (permalink / raw)
  To: ocfs2-devel

This patch implements functions and timer setup which handles periodic
syncing of locally cached quota information to global quota file.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/ocfs2/quota.h        |    3 ++
 fs/ocfs2/quota_global.c |   71 +++++++++++++++++++++++++++++++++++++++++++++++
 fs/ocfs2/quota_local.c  |    4 ++
 3 files changed, 78 insertions(+), 0 deletions(-)

diff --git a/fs/ocfs2/quota.h b/fs/ocfs2/quota.h
index 87545ca..179c635 100644
--- a/fs/ocfs2/quota.h
+++ b/fs/ocfs2/quota.h
@@ -14,6 +14,7 @@
 #include <linux/quota.h>
 #include <linux/list.h>
 #include <linux/dqblk_qtree.h>
+#include <linux/timer.h>
 
 #include "ocfs2.h"
 
@@ -43,6 +44,7 @@ struct ocfs2_mem_dqinfo {
 	unsigned int dqi_chunks;	/* Number of chunks in local quota file */
 	unsigned int dqi_blocks;	/* Number of blocks allocated for local quota file */
 	unsigned int dqi_syncms;	/* How often should we sync with other nodes */
+	unsigned int dqi_syncjiff;	/* Precomputed dqi_syncms in jiffies */
 	struct list_head dqi_chunk;	/* List of chunks */
 	struct inode *dqi_gqinode;	/* Global quota file inode */
 	struct ocfs2_lock_res dqi_gqlock;	/* Lock protecting quota information structure */
@@ -51,6 +53,7 @@ struct ocfs2_mem_dqinfo {
 	struct buffer_head *dqi_lqi_bh;	/* Buffer head with local quota file inode */
 	struct buffer_head *dqi_ibh;	/* Buffer with information header */
 	struct qtree_mem_dqinfo dqi_gi;	/* Info about global file */
+	struct timer_list dqi_sync_timer;	/* Timer for syncing dquots */
 };
 
 static inline struct ocfs2_dquot *OCFS2_DQUOT(struct dquot *dquot)
diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c
index b937f07..39b860f 100644
--- a/fs/ocfs2/quota_global.c
+++ b/fs/ocfs2/quota_global.c
@@ -1,10 +1,14 @@
 /*
  *  Implementation of operations over global quota file
  */
+#include <linux/spinlock.h>
 #include <linux/fs.h>
 #include <linux/quota.h>
 #include <linux/quotaops.h>
 #include <linux/dqblk_qtree.h>
+#include <linux/jiffies.h>
+#include <linux/timer.h>
+#include <linux/writeback.h>
 
 #define MLOG_MASK_PREFIX ML_QUOTA
 #include <cluster/masklog.h>
@@ -19,6 +23,8 @@
 #include "dlmglue.h"
 #include "quota.h"
 
+static void qsync_timer_fn(unsigned long oinfo_ptr);
+
 static void ocfs2_global_disk2memdqb(struct dquot *dquot, void *dp)
 {
 	struct ocfs2_global_disk_dqblk *d = dp;
@@ -276,6 +282,7 @@ int ocfs2_global_read_info(struct super_block *sb, int type)
 	info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace);
 	info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace);
 	oinfo->dqi_syncms = le32_to_cpu(dinfo.dqi_syncms);
+	oinfo->dqi_syncjiff = msecs_to_jiffies(oinfo->dqi_syncms);
 	oinfo->dqi_gi.dqi_blocks = le32_to_cpu(dinfo.dqi_blocks);
 	oinfo->dqi_gi.dqi_free_blk = le32_to_cpu(dinfo.dqi_free_blk);
 	oinfo->dqi_gi.dqi_free_entry = le32_to_cpu(dinfo.dqi_free_entry);
@@ -283,6 +290,10 @@ int ocfs2_global_read_info(struct super_block *sb, int type)
 	oinfo->dqi_gi.dqi_usable_bs = sb->s_blocksize -
 						OCFS2_QBLK_RESERVED_SPACE;
 	oinfo->dqi_gi.dqi_qtree_depth = qtree_depth(&oinfo->dqi_gi);
+	setup_timer(&oinfo->dqi_sync_timer, qsync_timer_fn,
+		    (unsigned long)oinfo);
+	mod_timer(&oinfo->dqi_sync_timer,
+		  round_jiffies(jiffies + oinfo->dqi_syncjiff));
 out_err:
 	mlog_exit(status);
 	return status;
@@ -470,6 +481,66 @@ out:
 }
 
 /*
+ *  Functions for periodic syncing of dquots with global file
+ */
+static int ocfs2_sync_dquot_helper(struct dquot *dquot, unsigned long type)
+{
+	handle_t *handle;
+	struct super_block *sb = dquot->dq_sb;
+	struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
+	struct ocfs2_super *osb = OCFS2_SB(sb);
+	int status = 0;
+
+	mlog_entry("id=%u qtype=%u type=%lu device=%s\n", dquot->dq_id,
+		   dquot->dq_type, type, sb->s_id);
+	if (type != dquot->dq_type)
+		goto out;
+	status = ocfs2_lock_global_qf(oinfo, 1);
+	if (status < 0)
+		goto out;
+
+	handle = ocfs2_start_trans(osb, OCFS2_QSYNC_CREDITS);
+	if (IS_ERR(handle)) {
+		status = PTR_ERR(handle);
+		mlog_errno(status);
+		goto out_ilock;
+	}
+	mutex_lock(&sb_dqopt(sb)->dqio_mutex);
+	status = ocfs2_sync_dquot(dquot);
+	mutex_unlock(&sb_dqopt(sb)->dqio_mutex);
+	if (status < 0)
+		mlog_errno(status);
+	/* We have to write local structure as well... */
+	dquot_mark_dquot_dirty(dquot);
+	status = dquot_commit(dquot);
+	if (status < 0)
+		mlog_errno(status);
+	ocfs2_commit_trans(osb, handle);
+out_ilock:
+	ocfs2_unlock_global_qf(oinfo, 1);
+out:
+	mlog_exit(status);
+	return status;
+}
+
+static void ocfs2_do_qsync(unsigned long oinfo_ptr)
+{
+	struct ocfs2_mem_dqinfo *oinfo = (struct ocfs2_mem_dqinfo *)oinfo_ptr;
+	struct super_block *sb = oinfo->dqi_gqinode->i_sb;
+
+	dquot_scan_active(sb, ocfs2_sync_dquot_helper, oinfo->dqi_type);
+}
+
+static void qsync_timer_fn(unsigned long oinfo_ptr)
+{
+	struct ocfs2_mem_dqinfo *oinfo = (struct ocfs2_mem_dqinfo *)oinfo_ptr;
+
+	pdflush_operation(ocfs2_do_qsync, oinfo_ptr);
+	mod_timer(&oinfo->dqi_sync_timer,
+		  round_jiffies(jiffies + oinfo->dqi_syncjiff));
+}
+
+/*
  *  Wrappers for generic quota functions
  */
 
diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c
index 55c3f2f..1db7a16 100644
--- a/fs/ocfs2/quota_local.c
+++ b/fs/ocfs2/quota_local.c
@@ -368,6 +368,10 @@ static int ocfs2_local_free_info(struct super_block *sb, int type)
 	int mark_clean = 1, len;
 	int status;
 
+	/* At this point we know there are no more dquots and thus
+	 * even if there's some sync in the pdflush queue, it won't
+	 * find any dquots and return without doing anything */
+	del_timer_sync(&oinfo->dqi_sync_timer);
 	iput(oinfo->dqi_gqinode);
 	ocfs2_simple_drop_lockres(OCFS2_SB(sb), &oinfo->dqi_gqlock);
 	ocfs2_lock_res_free(&oinfo->dqi_gqlock);
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 28/29] ocfs2: Implement quota recovery
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (26 preceding siblings ...)
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 27/29] ocfs2: Implement quota syncing thread Jan Kara
@ 2008-10-24 22:08 ` Jan Kara
  2008-11-06  0:52   ` Mark Fasheh
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 29/29] ocfs2: Enable quota accounting on mount, disable on umount Jan Kara
  28 siblings, 1 reply; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:08 UTC (permalink / raw)
  To: ocfs2-devel

Implement functions for recovery after a crash. Functions just
read local quota file and sync info to global quota file.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/ocfs2/journal.c      |  105 +++++++++---
 fs/ocfs2/journal.h      |    1 +
 fs/ocfs2/ocfs2.h        |    4 +-
 fs/ocfs2/quota.h        |   21 +++
 fs/ocfs2/quota_global.c |    1 -
 fs/ocfs2/quota_local.c  |  430 ++++++++++++++++++++++++++++++++++++++++++++++-
 6 files changed, 530 insertions(+), 32 deletions(-)

diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c
index f3d7c15..d928db9 100644
--- a/fs/ocfs2/journal.c
+++ b/fs/ocfs2/journal.c
@@ -45,6 +45,7 @@
 #include "slot_map.h"
 #include "super.h"
 #include "sysfile.h"
+#include "quota.h"
 
 #include "buffer_head_io.h"
 
@@ -52,7 +53,7 @@ DEFINE_SPINLOCK(trans_inc_lock);
 
 static int ocfs2_force_read_journal(struct inode *inode);
 static int ocfs2_recover_node(struct ocfs2_super *osb,
-			      int node_num);
+			      int node_num, int slot_num);
 static int __ocfs2_recovery_thread(void *arg);
 static int ocfs2_commit_cache(struct ocfs2_super *osb);
 static int ocfs2_wait_on_mount(struct ocfs2_super *osb);
@@ -877,6 +878,7 @@ struct ocfs2_la_recovery_item {
 	int			lri_slot;
 	struct ocfs2_dinode	*lri_la_dinode;
 	struct ocfs2_dinode	*lri_tl_dinode;
+	struct ocfs2_quota_recovery *lri_qrec;
 };
 
 /* Does the second half of the recovery process. By this point, the
@@ -897,6 +899,7 @@ void ocfs2_complete_recovery(struct work_struct *work)
 	struct ocfs2_super *osb = journal->j_osb;
 	struct ocfs2_dinode *la_dinode, *tl_dinode;
 	struct ocfs2_la_recovery_item *item, *n;
+	struct ocfs2_quota_recovery *qrec;
 	LIST_HEAD(tmp_la_list);
 
 	mlog_entry_void();
@@ -942,6 +945,16 @@ void ocfs2_complete_recovery(struct work_struct *work)
 		if (ret < 0)
 			mlog_errno(ret);
 
+		qrec = item->lri_qrec;
+		if (qrec) {
+			mlog(0, "Recovering quota files");
+			ret = ocfs2_finish_quota_recovery(osb, qrec,
+							  item->lri_slot);
+			if (ret < 0)
+				mlog_errno(ret);
+			/* Recovery info is already freed now */
+		}
+
 		kfree(item);
 	}
 
@@ -955,7 +968,8 @@ void ocfs2_complete_recovery(struct work_struct *work)
 static void ocfs2_queue_recovery_completion(struct ocfs2_journal *journal,
 					    int slot_num,
 					    struct ocfs2_dinode *la_dinode,
-					    struct ocfs2_dinode *tl_dinode)
+					    struct ocfs2_dinode *tl_dinode,
+					    struct ocfs2_quota_recovery *qrec)
 {
 	struct ocfs2_la_recovery_item *item;
 
@@ -970,6 +984,9 @@ static void ocfs2_queue_recovery_completion(struct ocfs2_journal *journal,
 		if (tl_dinode)
 			kfree(tl_dinode);
 
+		if (qrec)
+			ocfs2_free_quota_recovery(qrec);
+
 		mlog_errno(-ENOMEM);
 		return;
 	}
@@ -978,6 +995,7 @@ static void ocfs2_queue_recovery_completion(struct ocfs2_journal *journal,
 	item->lri_la_dinode = la_dinode;
 	item->lri_slot = slot_num;
 	item->lri_tl_dinode = tl_dinode;
+	item->lri_qrec = qrec;
 
 	spin_lock(&journal->j_lock);
 	list_add_tail(&item->lri_list, &journal->j_la_cleanups);
@@ -997,6 +1015,7 @@ void ocfs2_complete_mount_recovery(struct ocfs2_super *osb)
 		ocfs2_queue_recovery_completion(journal,
 						osb->slot_num,
 						osb->local_alloc_copy,
+						NULL,
 						NULL);
 		ocfs2_schedule_truncate_log_flush(osb, 0);
 
@@ -1005,11 +1024,26 @@ void ocfs2_complete_mount_recovery(struct ocfs2_super *osb)
 	}
 }
 
+void ocfs2_complete_quota_recovery(struct ocfs2_super *osb)
+{
+	if (osb->quota_rec) {
+		ocfs2_queue_recovery_completion(osb->journal,
+						osb->slot_num,
+						NULL,
+						NULL,
+						osb->quota_rec);
+		osb->quota_rec = NULL;
+	}
+}
+
 static int __ocfs2_recovery_thread(void *arg)
 {
-	int status, node_num;
+	int status, node_num, slot_num;
 	struct ocfs2_super *osb = arg;
 	struct ocfs2_recovery_map *rm = osb->recovery_map;
+	int *rm_quota = NULL;
+	int rm_quota_used = 0, i;
+	struct ocfs2_quota_recovery *qrec;
 
 	mlog_entry_void();
 
@@ -1018,6 +1052,11 @@ static int __ocfs2_recovery_thread(void *arg)
 		goto bail;
 	}
 
+	rm_quota = kzalloc(osb->max_slots * sizeof(int), GFP_NOFS);
+	if (!rm_quota) {
+		status = -ENOMEM;
+		goto bail;
+	}
 restart:
 	status = ocfs2_super_lock(osb, 1);
 	if (status < 0) {
@@ -1031,8 +1070,28 @@ restart:
 		 * clear it until ocfs2_recover_node() has succeeded. */
 		node_num = rm->rm_entries[0];
 		spin_unlock(&osb->osb_lock);
-
-		status = ocfs2_recover_node(osb, node_num);
+		mlog(0, "checking node %d\n", node_num);
+		slot_num = ocfs2_node_num_to_slot(osb, node_num);
+		if (slot_num == -ENOENT) {
+			status = 0;
+			mlog(0, "no slot for this node, so no recovery"
+			     "required.\n");
+			goto skip_recovery;
+		}
+		mlog(0, "node %d was using slot %d\n", node_num, slot_num);
+
+		/* It is a bit subtle with quota recovery. We cannot do it
+		 * immediately because we have to obtain cluster locks from
+		 * quota files and we also don't want to just skip it because
+		 * then quota usage would be out of sync until some node takes
+		 * the slot. So we remember which nodes need quota recovery
+		 * and when everything else is done, we recover quotas. */
+		for (i = 0; i < rm_quota_used && rm_quota[i] != slot_num; i++);
+		if (i == rm_quota_used)
+			rm_quota[rm_quota_used++] = slot_num;
+
+		status = ocfs2_recover_node(osb, node_num, slot_num);
+skip_recovery:
 		if (!status) {
 			ocfs2_recovery_map_clear(osb, node_num);
 		} else {
@@ -1056,11 +1115,22 @@ restart:
 
 	ocfs2_super_unlock(osb, 1);
 
+	/* Now it is right time to recover quotas... */
+	for (i = 0; i < rm_quota_used; i++) {
+		qrec = ocfs2_begin_quota_recovery(osb, rm_quota[i]);
+		if (IS_ERR(qrec)) {
+			status = PTR_ERR(qrec);
+			mlog_errno(status);
+		}
+		ocfs2_queue_recovery_completion(osb->journal, rm_quota[i],
+						NULL, NULL, qrec);
+	}
+
 	/* We always run recovery on our own orphan dir - the dead
 	 * node(s) may have disallowd a previos inode delete. Re-processing
 	 * is therefore required. */
 	ocfs2_queue_recovery_completion(osb->journal, osb->slot_num, NULL,
-					NULL);
+					NULL, NULL);
 
 bail:
 	mutex_lock(&osb->recovery_lock);
@@ -1075,6 +1145,9 @@ bail:
 
 	mutex_unlock(&osb->recovery_lock);
 
+	if (rm_quota)
+		kfree(rm_quota);
+
 	mlog_exit(status);
 	/* no one is callint kthread_stop() for us so the kthread() api
 	 * requires that we call do_exit().  And it isn't exported, but
@@ -1303,31 +1376,19 @@ done:
  * far less concerning.
  */
 static int ocfs2_recover_node(struct ocfs2_super *osb,
-			      int node_num)
+			      int node_num, int slot_num)
 {
 	int status = 0;
-	int slot_num;
 	struct ocfs2_dinode *la_copy = NULL;
 	struct ocfs2_dinode *tl_copy = NULL;
 
-	mlog_entry("(node_num=%d, osb->node_num = %d)\n",
-		   node_num, osb->node_num);
-
-	mlog(0, "checking node %d\n", node_num);
+	mlog_entry("(node_num=%d, slot_num=%d, osb->node_num = %d)\n",
+		   node_num, slot_num, osb->node_num);
 
 	/* Should not ever be called to recover ourselves -- in that
 	 * case we should've called ocfs2_journal_load instead. */
 	BUG_ON(osb->node_num == node_num);
 
-	slot_num = ocfs2_node_num_to_slot(osb, node_num);
-	if (slot_num == -ENOENT) {
-		status = 0;
-		mlog(0, "no slot for this node, so no recovery required.\n");
-		goto done;
-	}
-
-	mlog(0, "node %d was using slot %d\n", node_num, slot_num);
-
 	status = ocfs2_replay_journal(osb, node_num, slot_num);
 	if (status < 0) {
 		if (status == -EBUSY) {
@@ -1363,7 +1424,7 @@ static int ocfs2_recover_node(struct ocfs2_super *osb,
 
 	/* This will kfree the memory pointed to by la_copy and tl_copy */
 	ocfs2_queue_recovery_completion(osb->journal, slot_num, la_copy,
-					tl_copy);
+					tl_copy, NULL);
 
 	status = 0;
 done:
diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h
index c13bd8c..f10a564 100644
--- a/fs/ocfs2/journal.h
+++ b/fs/ocfs2/journal.h
@@ -173,6 +173,7 @@ void   ocfs2_recovery_thread(struct ocfs2_super *osb,
 			     int node_num);
 int    ocfs2_mark_dead_nodes(struct ocfs2_super *osb);
 void   ocfs2_complete_mount_recovery(struct ocfs2_super *osb);
+void ocfs2_complete_quota_recovery(struct ocfs2_super *osb);
 
 static inline void ocfs2_start_checkpoint(struct ocfs2_super *osb)
 {
diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h
index a21a465..37c2e29 100644
--- a/fs/ocfs2/ocfs2.h
+++ b/fs/ocfs2/ocfs2.h
@@ -205,6 +205,7 @@ enum ocfs2_mount_options
 struct ocfs2_journal;
 struct ocfs2_slot_info;
 struct ocfs2_recovery_map;
+struct ocfs2_quota_recovery;
 struct ocfs2_super
 {
 	struct task_struct *commit_task;
@@ -286,10 +287,11 @@ struct ocfs2_super
 	char *local_alloc_debug_buf;
 #endif
 
-	/* Next two fields are for local node slot recovery during
+	/* Next three fields are for local node slot recovery during
 	 * mount. */
 	int dirty;
 	struct ocfs2_dinode *local_alloc_copy;
+	struct ocfs2_quota_recovery *quota_rec;
 
 	struct ocfs2_alloc_stats alloc_stats;
 	char dev_str[20];		/* "major,minor" of the device */
diff --git a/fs/ocfs2/quota.h b/fs/ocfs2/quota.h
index 179c635..7c4d771 100644
--- a/fs/ocfs2/quota.h
+++ b/fs/ocfs2/quota.h
@@ -38,6 +38,17 @@ struct ocfs2_dquot {
 	s64 dq_originodes;	/* Last globally synced inode usage */
 };
 
+/* Description of one chunk to recover in memory */
+struct ocfs2_recovery_chunk {
+	struct list_head rc_list;	/* List of chunks */
+	int rc_chunk;			/* Chunk number */
+	unsigned long *rc_bitmap;	/* Bitmap of entries to recover */
+};
+
+struct ocfs2_quota_recovery {
+	struct list_head r_list[MAXQUOTAS];	/* List of chunks to recover */
+};
+
 /* In-memory structure with quota header information */
 struct ocfs2_mem_dqinfo {
 	unsigned int dqi_type;		/* Quota type this structure describes */
@@ -54,6 +65,10 @@ struct ocfs2_mem_dqinfo {
 	struct buffer_head *dqi_ibh;	/* Buffer with information header */
 	struct qtree_mem_dqinfo dqi_gi;	/* Info about global file */
 	struct timer_list dqi_sync_timer;	/* Timer for syncing dquots */
+	struct ocfs2_quota_recovery *dqi_rec;	/* Pointer to recovery
+						 * information, in case we
+						 * enable quotas on file
+						 * needing it */
 };
 
 static inline struct ocfs2_dquot *OCFS2_DQUOT(struct dquot *dquot)
@@ -72,6 +87,12 @@ extern struct kmem_cache *ocfs2_qf_chunk_cachep;
 
 extern struct qtree_fmt_operations ocfs2_global_ops;
 
+struct ocfs2_quota_recovery *ocfs2_begin_quota_recovery(
+				struct ocfs2_super *osb, int slot_num);
+int ocfs2_finish_quota_recovery(struct ocfs2_super *osb,
+				struct ocfs2_quota_recovery *rec,
+				int slot_num);
+void ocfs2_free_quota_recovery(struct ocfs2_quota_recovery *rec);
 ssize_t ocfs2_quota_read(struct super_block *sb, int type, char *data,
 			 size_t len, loff_t off);
 ssize_t ocfs2_quota_write(struct super_block *sb, int type,
diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c
index 39b860f..2ba4763 100644
--- a/fs/ocfs2/quota_global.c
+++ b/fs/ocfs2/quota_global.c
@@ -85,7 +85,6 @@ struct qtree_fmt_operations ocfs2_global_ops = {
 	.is_id = ocfs2_global_is_id,
 };
 
-
 struct buffer_head *ocfs2_read_quota_block(struct inode *inode,
 					   int block, int *err)
 {
diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c
index 1db7a16..cad01d6 100644
--- a/fs/ocfs2/quota_local.c
+++ b/fs/ocfs2/quota_local.c
@@ -49,14 +49,25 @@ static unsigned int ol_quota_chunk_block(struct super_block *sb, int c)
 	return 1 + (ol_chunk_blocks(sb) + 1) * c;
 }
 
-/* Offset of the dquot structure in the quota file */
-static loff_t ol_dqblk_off(struct super_block *sb, int c, int off)
+static unsigned int ol_dqblk_block(struct super_block *sb, int c, int off)
+{
+	int epb = ol_quota_entries_per_block(sb);
+
+	return ol_quota_chunk_block(sb, c) + 1 + off / epb;
+}
+
+static unsigned int ol_dqblk_block_off(struct super_block *sb, int c, int off)
 {
 	int epb = ol_quota_entries_per_block(sb);
 
-	return ((ol_quota_chunk_block(sb, c) + 1 + off / epb)
-		<< sb->s_blocksize_bits) +
-		(off % epb) * sizeof(struct ocfs2_local_disk_dqblk);
+	return (off % epb) * sizeof(struct ocfs2_local_disk_dqblk);
+}
+
+/* Offset of the dquot structure in the quota file */
+static loff_t ol_dqblk_off(struct super_block *sb, int c, int off)
+{
+	return (ol_dqblk_block(sb, c, off) << sb->s_blocksize_bits) +
+	       ol_dqblk_block_off(sb, c, off);
 }
 
 /* Compute block number from given offset */
@@ -253,6 +264,384 @@ static void olq_update_info(struct buffer_head *bh, void *private)
 	spin_unlock(&dq_data_lock);
 }
 
+static int ocfs2_add_recovery_chunk(struct super_block *sb,
+				    struct ocfs2_local_disk_chunk *dchunk,
+				    int chunk,
+				    struct list_head *head)
+{
+	struct ocfs2_recovery_chunk *rc;
+
+	rc = kmalloc(sizeof(struct ocfs2_recovery_chunk), GFP_NOFS);
+	if (!rc)
+		return -ENOMEM;
+	rc->rc_chunk = chunk;
+	rc->rc_bitmap = kmalloc(sb->s_blocksize, GFP_NOFS);
+	if (!rc->rc_bitmap) {
+		kfree(rc);
+		return -ENOMEM;
+	}
+	memcpy(rc->rc_bitmap, dchunk->dqc_bitmap,
+	       (ol_chunk_entries(sb) + 7) >> 3);
+	list_add_tail(&rc->rc_list, head);
+	return 0;
+}
+
+static void free_recovery_list(struct list_head *head)
+{
+	struct ocfs2_recovery_chunk *next;
+	struct ocfs2_recovery_chunk *rchunk;
+
+	list_for_each_entry_safe(rchunk, next, head, rc_list) {
+		list_del(&rchunk->rc_list);
+		kfree(rchunk->rc_bitmap);
+		kfree(rchunk);
+	}
+}
+
+void ocfs2_free_quota_recovery(struct ocfs2_quota_recovery *rec)
+{
+	int type;
+
+	for (type = 0; type < MAXQUOTAS; type++)
+		free_recovery_list(&(rec->r_list[type]));
+	kfree(rec);
+}
+
+/* Load entries in our quota file we have to recover*/
+static int ocfs2_recovery_load_quota(struct inode *lqinode,
+				     struct ocfs2_local_disk_dqinfo *ldinfo,
+				     int type,
+				     struct list_head *head)
+{
+	struct super_block *sb = lqinode->i_sb;
+	struct buffer_head *hbh;
+	struct ocfs2_local_disk_chunk *dchunk;
+	int i, chunks = le32_to_cpu(ldinfo->dqi_chunks);
+	int status = 0;
+
+	for (i = 0; i < chunks; i++) {
+		hbh = ocfs2_read_quota_block(lqinode,
+					     ol_quota_chunk_block(sb, i),
+					     &status);
+		if (!hbh) {
+			mlog_errno(status);
+			break;
+		}
+		dchunk = (struct ocfs2_local_disk_chunk *)hbh->b_data;
+		if (le32_to_cpu(dchunk->dqc_free) < ol_chunk_entries(sb))
+			status = ocfs2_add_recovery_chunk(sb, dchunk, i, head);
+		brelse(hbh);
+		if (status < 0)
+			break;
+	}
+	if (status < 0)
+		free_recovery_list(head);
+	return status;
+}
+
+static struct ocfs2_quota_recovery *ocfs2_alloc_quota_recovery(void)
+{
+	int type;
+	struct ocfs2_quota_recovery *rec;
+
+	rec = kmalloc(sizeof(struct ocfs2_quota_recovery), GFP_NOFS);
+	if (!rec)
+		return NULL;
+	for (type = 0; type < MAXQUOTAS; type++)
+		INIT_LIST_HEAD(&(rec->r_list[type]));
+	return rec;
+}
+
+/* Load information we need for quota recovery into memory */
+struct ocfs2_quota_recovery *ocfs2_begin_quota_recovery(
+						struct ocfs2_super *osb,
+						int slot_num)
+{
+	unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
+					    OCFS2_FEATURE_RO_COMPAT_GRPQUOTA};
+	unsigned int ino[MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE,
+					LOCAL_GROUP_QUOTA_SYSTEM_INODE };
+	struct super_block *sb = osb->sb;
+	struct ocfs2_local_disk_dqinfo *ldinfo;
+	struct inode *lqinode;
+	struct buffer_head *bh;
+	int type;
+	int status;
+	struct ocfs2_quota_recovery *rec;
+
+	mlog(ML_NOTICE, "Beginning quota recovery in slot %u\n", slot_num);
+	rec = ocfs2_alloc_quota_recovery();
+	if (!rec)
+		return ERR_PTR(-ENOMEM);
+	/* First init... */
+
+	for (type = 0; type < MAXQUOTAS; type++) {
+		if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type]))
+			continue;
+		lqinode = ocfs2_get_system_file_inode(osb, ino[type], slot_num);
+		if (!lqinode) {
+			status = -ENOENT;
+			goto out;
+		}
+		status = ocfs2_inode_lock_full(lqinode, NULL, 1,
+						       OCFS2_META_LOCK_NOQUEUE);
+		/* Someone else is holding the lock? Then he must be
+		 * doing the recovery. Just skip the file... */
+		if (status == -EAGAIN) {
+			mlog(ML_NOTICE, "skipping quota recovery for slot %d "
+			     "because quota file is locked.\n", slot_num);
+			status = 0;
+			goto out_put;
+		} else if (status < 0) {
+			mlog_errno(status);
+			goto out_put;
+		}
+		/* Now read local header */
+		bh = ocfs2_read_quota_block(lqinode, 0, &status);
+		if (!bh) {
+			mlog_errno(status);
+			mlog(ML_ERROR, "failed to read quota file info header "
+				"(slot=%d type=%d)\n", slot_num, type);
+			goto out_lock;
+		}
+		ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data +
+							OCFS2_LOCAL_INFO_OFF);
+		status = ocfs2_recovery_load_quota(lqinode, ldinfo, type,
+						   &rec->r_list[type]);
+		brelse(bh);
+out_lock:
+		ocfs2_inode_unlock(lqinode, 1);
+out_put:
+		iput(lqinode);
+		if (status < 0)
+			break;
+	}
+out:
+	if (status < 0) {
+		ocfs2_free_quota_recovery(rec);
+		rec = ERR_PTR(status);
+	}
+	return rec;
+}
+
+/* Sync changes in local quota file into global quota file and
+ * reinitialize local quota file.
+ * The function expects local quota file to be already locked and
+ * dqonoff_mutex locked. */
+static int ocfs2_recover_local_quota_file(struct inode *lqinode,
+					  int type,
+					  struct ocfs2_quota_recovery *rec)
+{
+	struct super_block *sb = lqinode->i_sb;
+	struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
+	struct ocfs2_local_disk_chunk *dchunk;
+	struct ocfs2_local_disk_dqblk *dqblk;
+	struct dquot *dquot;
+	handle_t *handle;
+	struct buffer_head *hbh = NULL, *qbh = NULL;
+	int status = 0;
+	int bit, chunk;
+	struct ocfs2_recovery_chunk *rchunk, *next;
+	qsize_t spacechange, inodechange;
+
+	mlog_entry("ino=%lu type=%u", (unsigned long)lqinode->i_ino, type);
+
+	status = ocfs2_lock_global_qf(oinfo, 1);
+	if (status < 0)
+		goto out;
+
+	list_for_each_entry_safe(rchunk, next, &(rec->r_list[type]), rc_list) {
+		chunk = rchunk->rc_chunk;
+		hbh = ocfs2_read_quota_block(lqinode,
+					     ol_quota_chunk_block(sb, chunk),
+					     &status);
+		if (!hbh) {
+			mlog_errno(status);
+			break;
+		}
+		dchunk = (struct ocfs2_local_disk_chunk *)hbh->b_data;
+		for_each_bit(bit, rchunk->rc_bitmap, ol_chunk_entries(sb)) {
+			qbh = ocfs2_read_quota_block(lqinode,
+						ol_dqblk_block(sb, chunk, bit),
+						&status);
+			if (!qbh) {
+				mlog_errno(status);
+				break;
+			}
+			dqblk = (struct ocfs2_local_disk_dqblk *)(qbh->b_data +
+				ol_dqblk_block_off(sb, chunk, bit));
+			dquot = dqget(sb, le64_to_cpu(dqblk->dqb_id), type);
+			if (!dquot) {
+				status = -EIO;
+				mlog(ML_ERROR, "Failed to get quota structure "
+				     "for id %u, type %d. Cannot finish quota "
+				     "file recovery.\n",
+				     (unsigned)le64_to_cpu(dqblk->dqb_id),
+				     type);
+				goto out_put_bh;
+			}
+			handle = ocfs2_start_trans(OCFS2_SB(sb),
+						   OCFS2_QSYNC_CREDITS);
+			if (IS_ERR(handle)) {
+				status = PTR_ERR(handle);
+				mlog_errno(status);
+				goto out_put_dquot;
+			}
+			mutex_lock(&sb_dqopt(sb)->dqio_mutex);
+			spin_lock(&dq_data_lock);
+			/* Add usage from quota entry into quota changes
+			 * of our node. Auxiliary variables are important
+			 * due to signedness */
+			spacechange = le64_to_cpu(dqblk->dqb_spacemod);
+			inodechange = le64_to_cpu(dqblk->dqb_inodemod);
+			dquot->dq_dqb.dqb_curspace += spacechange;
+			dquot->dq_dqb.dqb_curinodes += inodechange;
+			spin_unlock(&dq_data_lock);
+			/* We want to drop reference held by the crashed
+			 * node. Since we have our own reference we know
+			 * global structure actually won't be freed. */
+			status = ocfs2_global_release_dquot(dquot);
+			if (status < 0) {
+				mlog_errno(status);
+				goto out_commit;
+			}
+			/* Release local quota file entry */
+			status = ocfs2_journal_access(handle, lqinode,
+					qbh, OCFS2_JOURNAL_ACCESS_WRITE);
+			if (status < 0) {
+				mlog_errno(status);
+				goto out_commit;
+			}
+			lock_buffer(qbh);
+			WARN_ON(!ocfs2_test_bit(bit, dchunk->dqc_bitmap));
+			ocfs2_clear_bit(bit, dchunk->dqc_bitmap);
+			le32_add_cpu(&dchunk->dqc_free, 1);
+			unlock_buffer(qbh);
+			status = ocfs2_journal_dirty(handle, qbh);
+			if (status < 0)
+				mlog_errno(status);
+out_commit:
+			mutex_unlock(&sb_dqopt(sb)->dqio_mutex);
+			ocfs2_commit_trans(OCFS2_SB(sb), handle);
+out_put_dquot:
+			dqput(dquot);
+out_put_bh:
+			brelse(qbh);
+			if (status < 0)
+				break;
+		}
+		brelse(hbh);
+		list_del(&rchunk->rc_list);
+		kfree(rchunk->rc_bitmap);
+		kfree(rchunk);
+		if (status < 0)
+			break;
+	}
+	ocfs2_unlock_global_qf(oinfo, 1);
+out:
+	if (status < 0)
+		free_recovery_list(&(rec->r_list[type]));
+	mlog_exit(status);
+	return status;
+}
+
+/* Recover local quota files for given node different from us */
+int ocfs2_finish_quota_recovery(struct ocfs2_super *osb,
+				struct ocfs2_quota_recovery *rec,
+				int slot_num)
+{
+	unsigned int ino[MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE,
+					LOCAL_GROUP_QUOTA_SYSTEM_INODE };
+	struct super_block *sb = osb->sb;
+	struct ocfs2_local_disk_dqinfo *ldinfo;
+	struct buffer_head *bh;
+	handle_t *handle;
+	int type;
+	int status = 0;
+	struct inode *lqinode;
+	unsigned int flags;
+
+	mlog(ML_NOTICE, "Finishing quota recovery in slot %u\n", slot_num);
+	mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
+	for (type = 0; type < MAXQUOTAS; type++) {
+		if (list_empty(&(rec->r_list[type])))
+			continue;
+		mlog(0, "Recovering quota in slot %d\n", slot_num);
+		lqinode = ocfs2_get_system_file_inode(osb, ino[type], slot_num);
+		if (!lqinode) {
+			status = -ENOENT;
+			goto out;
+		}
+		status = ocfs2_inode_lock_full(lqinode, NULL, 1,
+						       OCFS2_META_LOCK_NOQUEUE);
+		/* Someone else is holding the lock? Then he must be
+		 * doing the recovery. Just skip the file... */
+		if (status == -EAGAIN) {
+			mlog(ML_NOTICE, "skipping quota recovery for slot %d "
+			     "because quota file is locked.\n", slot_num);
+			status = 0;
+			goto out_put;
+		} else if (status < 0) {
+			mlog_errno(status);
+			goto out_put;
+		}
+		/* Now read local header */
+		bh = ocfs2_read_quota_block(lqinode, 0, &status);
+		if (!bh) {
+			mlog_errno(status);
+			mlog(ML_ERROR, "failed to read quota file info header "
+				"(slot=%d type=%d)\n", slot_num, type);
+			goto out_lock;
+		}
+		ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data +
+							OCFS2_LOCAL_INFO_OFF);
+		/* Is recovery still needed? */
+		flags = le32_to_cpu(ldinfo->dqi_flags);
+		if (!(flags & OLQF_CLEAN))
+			status = ocfs2_recover_local_quota_file(lqinode,
+								type,
+								rec);
+		/* We don't want to mark file as clean when it is actually
+		 * active */
+		if (slot_num == osb->slot_num)
+			goto out_bh;
+		/* Mark quota file as clean if we are recovering quota file of
+		 * some other node. */
+		handle = ocfs2_start_trans(osb, 1);
+		if (IS_ERR(handle)) {
+			status = PTR_ERR(handle);
+			mlog_errno(status);
+			goto out_bh;
+		}
+		status = ocfs2_journal_access(handle, lqinode, bh,
+					      OCFS2_JOURNAL_ACCESS_WRITE);
+		if (status < 0) {
+			mlog_errno(status);
+			goto out_trans;
+		}
+		lock_buffer(bh);
+		ldinfo->dqi_flags = cpu_to_le32(flags | OLQF_CLEAN);
+		unlock_buffer(bh);
+		status = ocfs2_journal_dirty(handle, bh);
+		if (status < 0)
+			mlog_errno(status);
+out_trans:
+		ocfs2_commit_trans(osb, handle);
+out_bh:
+		brelse(bh);
+out_lock:
+		ocfs2_inode_unlock(lqinode, 1);
+out_put:
+		iput(lqinode);
+		if (status < 0)
+			break;
+	}
+out:
+	mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
+	kfree(rec);
+	return status;
+}
+
 /* Read information header from quota file */
 static int ocfs2_local_read_info(struct super_block *sb, int type)
 {
@@ -262,6 +651,7 @@ static int ocfs2_local_read_info(struct super_block *sb, int type)
 	struct inode *lqinode = sb_dqopt(sb)->files[type];
 	int status;
 	struct buffer_head *bh = NULL;
+	struct ocfs2_quota_recovery *rec;
 	int locked = 0;
 
 	info->dqi_maxblimit = 0x7fffffffffffffffLL;
@@ -275,6 +665,7 @@ static int ocfs2_local_read_info(struct super_block *sb, int type)
 	info->dqi_priv = oinfo;
 	oinfo->dqi_type = type;
 	INIT_LIST_HEAD(&oinfo->dqi_chunk);
+	oinfo->dqi_rec = NULL;
 	oinfo->dqi_lqi_bh = NULL;
 	oinfo->dqi_ibh = NULL;
 
@@ -305,10 +696,27 @@ static int ocfs2_local_read_info(struct super_block *sb, int type)
 	oinfo->dqi_ibh = bh;
 
 	/* We crashed when using local quota file? */
-	if (!(info->dqi_flags & OLQF_CLEAN))
-		goto out_err;	/* So far we just bail out. Later we should resync here */
+	if (!(info->dqi_flags & OLQF_CLEAN)) {
+		rec = OCFS2_SB(sb)->quota_rec;
+		if (!rec) {
+			rec = ocfs2_alloc_quota_recovery();
+			if (!rec) {
+				status = -ENOMEM;
+				mlog_errno(status);
+				goto out_err;
+			}
+			OCFS2_SB(sb)->quota_rec = rec;
+		}
 
-	status = ocfs2_load_local_quota_bitmaps(sb_dqopt(sb)->files[type],
+		status = ocfs2_recovery_load_quota(lqinode, ldinfo, type,
+                                                   &rec->r_list[type]);
+		if (status < 0) {
+			mlog_errno(status);
+			goto out_err;
+		}
+	}
+
+	status = ocfs2_load_local_quota_bitmaps(lqinode,
 						ldinfo,
 						&oinfo->dqi_chunk);
 	if (status < 0) {
@@ -394,6 +802,12 @@ static int ocfs2_local_free_info(struct super_block *sb, int type)
 	}
 	ocfs2_release_local_quota_bitmaps(&oinfo->dqi_chunk);
 
+	/* dqonoff_mutex protects us against racing with recovery thread... */
+	if (oinfo->dqi_rec) {
+		ocfs2_free_quota_recovery(oinfo->dqi_rec);
+		mark_clean = 0;
+	}
+
 	if (!mark_clean)
 		goto out;
 
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 29/29] ocfs2: Enable quota accounting on mount, disable on umount
  2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (27 preceding siblings ...)
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 28/29] ocfs2: Implement quota recovery Jan Kara
@ 2008-10-24 22:08 ` Jan Kara
  2008-10-28 19:11   ` Joel Becker
  28 siblings, 1 reply; 69+ messages in thread
From: Jan Kara @ 2008-10-24 22:08 UTC (permalink / raw)
  To: ocfs2-devel

Enable quota usage tracking on mount and disable it on umount. Also
add support for quota on and quota off quotactls and usrquota and
grpquota mount options.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/ocfs2/journal.c |   20 ++++-
 fs/ocfs2/ocfs2.h   |    3 +
 fs/ocfs2/super.c   |  217 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 237 insertions(+), 3 deletions(-)

diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c
index d928db9..096b67b 100644
--- a/fs/ocfs2/journal.c
+++ b/fs/ocfs2/journal.c
@@ -56,7 +56,7 @@ static int ocfs2_recover_node(struct ocfs2_super *osb,
 			      int node_num, int slot_num);
 static int __ocfs2_recovery_thread(void *arg);
 static int ocfs2_commit_cache(struct ocfs2_super *osb);
-static int ocfs2_wait_on_mount(struct ocfs2_super *osb);
+static int __ocfs2_wait_on_mount(struct ocfs2_super *osb, int quota);
 static int ocfs2_journal_toggle_dirty(struct ocfs2_super *osb,
 				      int dirty, int replayed);
 static int ocfs2_trylock_journal(struct ocfs2_super *osb,
@@ -65,6 +65,17 @@ static int ocfs2_recover_orphans(struct ocfs2_super *osb,
 				 int slot);
 static int ocfs2_commit_thread(void *arg);
 
+static inline int ocfs2_wait_on_mount(struct ocfs2_super *osb)
+{
+	return __ocfs2_wait_on_mount(osb, 0);
+}
+
+static inline int ocfs2_wait_on_quotas(struct ocfs2_super *osb)
+{
+	return __ocfs2_wait_on_mount(osb, 1);
+}
+
+
 
 /*
  * The recovery_list is a simple linked list of node numbers to recover.
@@ -915,6 +926,8 @@ void ocfs2_complete_recovery(struct work_struct *work)
 
 		mlog(0, "Complete recovery for slot %d\n", item->lri_slot);
 
+		ocfs2_wait_on_quotas(osb);
+
 		la_dinode = item->lri_la_dinode;
 		if (la_dinode) {
 			mlog(0, "Clean up local alloc %llu\n",
@@ -1719,13 +1732,14 @@ static int ocfs2_recover_orphans(struct ocfs2_super *osb,
 	return ret;
 }
 
-static int ocfs2_wait_on_mount(struct ocfs2_super *osb)
+static int __ocfs2_wait_on_mount(struct ocfs2_super *osb, int quota)
 {
 	/* This check is good because ocfs2 will wait on our recovery
 	 * thread before changing it to something other than MOUNTED
 	 * or DISABLED. */
 	wait_event(osb->osb_mount_event,
-		   atomic_read(&osb->vol_state) == VOLUME_MOUNTED ||
+		  (!quota && atomic_read(&osb->vol_state) == VOLUME_MOUNTED) ||
+		   atomic_read(&osb->vol_state) == VOLUME_MOUNTED_QUOTAS ||
 		   atomic_read(&osb->vol_state) == VOLUME_DISABLED);
 
 	/* If there's an error on mount, then we may never get to the
diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h
index 37c2e29..3d6d775 100644
--- a/fs/ocfs2/ocfs2.h
+++ b/fs/ocfs2/ocfs2.h
@@ -161,6 +161,7 @@ enum ocfs2_vol_state
 {
 	VOLUME_INIT = 0,
 	VOLUME_MOUNTED,
+	VOLUME_MOUNTED_QUOTAS,
 	VOLUME_DISMOUNTED,
 	VOLUME_DISABLED
 };
@@ -195,6 +196,8 @@ enum ocfs2_mount_options
 	OCFS2_MOUNT_LOCALFLOCKS = 1 << 5, /* No cluster aware user file locks */
 	OCFS2_MOUNT_NOUSERXATTR = 1 << 6, /* No user xattr */
 	OCFS2_MOUNT_INODE64 = 1 << 7,	/* Allow inode numbers > 2^32 */
+	OCFS2_MOUNT_USRQUOTA = 1 << 8, /* We support user quotas */
+	OCFS2_MOUNT_GRPQUOTA = 1 << 9, /* We support group quotas */
 };
 
 #define OCFS2_OSB_SOFT_RO	0x0001
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
index 92539fe..bddc210 100644
--- a/fs/ocfs2/super.c
+++ b/fs/ocfs2/super.c
@@ -41,6 +41,7 @@
 #include <linux/debugfs.h>
 #include <linux/mount.h>
 #include <linux/seq_file.h>
+#include <linux/quotaops.h>
 
 #define MLOG_MASK_PREFIX ML_SUPER
 #include <cluster/masklog.h>
@@ -127,6 +128,9 @@ static int ocfs2_get_sector(struct super_block *sb,
 static void ocfs2_write_super(struct super_block *sb);
 static struct inode *ocfs2_alloc_inode(struct super_block *sb);
 static void ocfs2_destroy_inode(struct inode *inode);
+static int ocfs2_susp_quotas(struct ocfs2_super *osb, int unsuspend);
+static int ocfs2_enable_quotas(struct ocfs2_super *osb);
+static void ocfs2_disable_quotas(struct ocfs2_super *osb);
 
 static const struct super_operations ocfs2_sops = {
 	.statfs		= ocfs2_statfs,
@@ -163,6 +167,8 @@ enum {
 	Opt_user_xattr,
 	Opt_nouser_xattr,
 	Opt_inode64,
+	Opt_usrquota,
+	Opt_grpquota,
 	Opt_err,
 };
 
@@ -185,6 +191,8 @@ static const match_table_t tokens = {
 	{Opt_user_xattr, "user_xattr"},
 	{Opt_nouser_xattr, "nouser_xattr"},
 	{Opt_inode64, "inode64"},
+	{Opt_usrquota, "usrquota"},
+	{Opt_grpquota, "grpquota"},
 	{Opt_err, NULL}
 };
 
@@ -448,6 +456,12 @@ static int ocfs2_remount(struct super_block *sb, int *flags, char *data)
 
 	/* We're going to/from readonly mode. */
 	if ((*flags & MS_RDONLY) != (sb->s_flags & MS_RDONLY)) {
+		/* Disable quota accounting before remounting RO */
+		if (*flags & MS_RDONLY) {
+			ret = ocfs2_susp_quotas(osb, 0);
+			if (ret < 0)
+				goto out;
+		}
 		/* Lock here so the check of HARD_RO and the potential
 		 * setting of SOFT_RO is atomic. */
 		spin_lock(&osb->osb_lock);
@@ -483,6 +497,21 @@ static int ocfs2_remount(struct super_block *sb, int *flags, char *data)
 		}
 unlock_osb:
 		spin_unlock(&osb->osb_lock);
+		/* Enable quota accounting after remounting RW */
+		if (!ret && !(*flags & MS_RDONLY)) {
+			if (sb_any_quota_suspended(sb))
+				ret = ocfs2_susp_quotas(osb, 1);
+			else
+				ret = ocfs2_enable_quotas(osb);
+			if (ret < 0) {
+				/* Return back changes... */
+				spin_lock(&osb->osb_lock);
+				sb->s_flags |= MS_RDONLY;
+				osb->osb_flags |= OCFS2_OSB_SOFT_RO;
+				spin_unlock(&osb->osb_lock);
+				goto out;
+			}
+		}
 	}
 
 	if (!ret) {
@@ -641,6 +670,131 @@ static int ocfs2_verify_userspace_stack(struct ocfs2_super *osb,
 	return 0;
 }
 
+static int ocfs2_susp_quotas(struct ocfs2_super *osb, int unsuspend)
+{
+	int type;
+	struct super_block *sb = osb->sb;
+	unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
+					     OCFS2_FEATURE_RO_COMPAT_GRPQUOTA};
+	int status = 0;
+
+	for (type = 0; type < MAXQUOTAS; type++) {
+		if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type]))
+			continue;
+		if (unsuspend)
+			status = vfs_quota_enable(
+					sb_dqopt(sb)->files[type],
+					type, QFMT_OCFS2,
+					DQUOT_SUSPENDED);
+		else
+			status = vfs_quota_disable(sb, type,
+						   DQUOT_SUSPENDED);
+		if (status < 0)
+			break;
+	}
+	if (status < 0)
+		mlog(ML_ERROR, "Failed to suspend/unsuspend quotas on "
+		     "remount (error = %d).\n", status);
+	return status;
+}
+
+static int ocfs2_enable_quotas(struct ocfs2_super *osb)
+{
+	struct inode *inode[MAXQUOTAS] = { NULL, NULL };
+	struct super_block *sb = osb->sb;
+	unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
+					     OCFS2_FEATURE_RO_COMPAT_GRPQUOTA};
+	unsigned int ino[MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE,
+					LOCAL_GROUP_QUOTA_SYSTEM_INODE };
+	int status;
+	int type;
+
+	sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE | DQUOT_NEGATIVE_USAGE;
+	for (type = 0; type < MAXQUOTAS; type++) {
+		if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type]))
+			continue;
+		inode[type] = ocfs2_get_system_file_inode(osb, ino[type],
+							osb->slot_num);
+		if (!inode[type]) {
+			status = -ENOENT;
+			goto out_quota_off;
+		}
+		status = vfs_quota_enable(inode[type], type, QFMT_OCFS2,
+						DQUOT_USAGE_ENABLED);
+		if (status < 0)
+			goto out_quota_off;
+	}
+
+	for (type = 0; type < MAXQUOTAS; type++)
+		iput(inode[type]);
+	return 0;
+out_quota_off:
+	ocfs2_disable_quotas(osb);
+	for (type = 0; type < MAXQUOTAS; type++)
+		iput(inode[type]);
+	mlog_errno(status);
+	return status;
+}
+
+static void ocfs2_disable_quotas(struct ocfs2_super *osb)
+{
+	int type;
+	struct inode *inode;
+	struct super_block *sb = osb->sb;
+
+	/* We mostly ignore errors in this function because there's not much
+	 * we can do when we see them */
+	for (type = 0; type < MAXQUOTAS; type++) {
+		if (!sb_has_quota_loaded(sb, type))
+			continue;
+		inode = igrab(sb->s_dquot.files[type]);
+		/* Turn off quotas. This will remove all dquot structures from
+		 * memory and so they will be automatically synced to global
+		 * quota files */
+		vfs_quota_disable(sb, type, DQUOT_USAGE_ENABLED |
+					    DQUOT_LIMITS_ENABLED);
+		if (!inode)
+			continue;
+		iput(inode);
+	}
+}
+
+/* Handle quota on quotactl */
+static int ocfs2_quota_on(struct super_block *sb, int type, int format_id,
+			  char *path, int remount)
+{
+	unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
+					     OCFS2_FEATURE_RO_COMPAT_GRPQUOTA};
+
+	if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type]))
+		return -EINVAL;
+
+	if (remount)
+		return 0;	/* Just ignore it has been handled in
+				 * ocfs2_remount() */
+	return vfs_quota_enable(sb_dqopt(sb)->files[type], type,
+				    format_id, DQUOT_LIMITS_ENABLED);
+}
+
+/* Handle quota off quotactl */
+static int ocfs2_quota_off(struct super_block *sb, int type, int remount)
+{
+	if (remount)
+		return 0;	/* Ignore now and handle later in
+				 * ocfs2_remount() */
+	return vfs_quota_disable(sb, type, DQUOT_LIMITS_ENABLED);
+}
+
+static struct quotactl_ops ocfs2_quotactl_ops = {
+	.quota_on	= ocfs2_quota_on,
+	.quota_off	= ocfs2_quota_off,
+	.quota_sync	= vfs_quota_sync,
+	.get_info	= vfs_get_dqinfo,
+	.set_info	= vfs_set_dqinfo,
+	.get_dqblk	= vfs_get_dqblk,
+	.set_dqblk	= vfs_set_dqblk,
+};
+
 static int ocfs2_fill_super(struct super_block *sb, void *data, int silent)
 {
 	struct dentry *root;
@@ -679,6 +833,22 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent)
 	osb->osb_commit_interval = parsed_options.commit_interval;
 	osb->local_alloc_default_bits = ocfs2_megabytes_to_clusters(sb, parsed_options.localalloc_opt);
 	osb->local_alloc_bits = osb->local_alloc_default_bits;
+	if (osb->s_mount_opt & OCFS2_MOUNT_USRQUOTA &&
+	    !OCFS2_HAS_RO_COMPAT_FEATURE(sb,
+					 OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) {
+		status = -EINVAL;
+		mlog(ML_ERROR, "User quotas were requested, but this "
+		     "filesystem does not have the feature enabled.\n");
+		goto read_super_error;
+	}
+	if (osb->s_mount_opt & OCFS2_MOUNT_GRPQUOTA &&
+	    !OCFS2_HAS_RO_COMPAT_FEATURE(sb,
+					 OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) {
+		status = -EINVAL;
+		mlog(ML_ERROR, "Group quotas were requested, but this "
+		     "filesystem does not have the feature enabled.\n");
+		goto read_super_error;
+	}
 
 	status = ocfs2_verify_userspace_stack(osb, &parsed_options);
 	if (status)
@@ -780,6 +950,23 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent)
 	atomic_set(&osb->vol_state, VOLUME_MOUNTED);
 	wake_up(&osb->osb_mount_event);
 
+	/* Now we can initialize quotas because we can afford to wait
+	 * for cluster locks recovery now. That also means that truncation
+	 * log recovery can happen but that waits for proper quota setup */
+	if (!(sb->s_flags & MS_RDONLY)) {
+		status = ocfs2_enable_quotas(osb);
+		if (status < 0) {
+			mlog_errno(status);
+			goto read_super_error;
+		}
+	}
+
+	ocfs2_complete_quota_recovery(osb);
+
+	/* Now we wake up again for processes waiting for quotas */
+	atomic_set(&osb->vol_state, VOLUME_MOUNTED_QUOTAS);
+	wake_up(&osb->osb_mount_event);
+
 	mlog_exit(status);
 	return status;
 
@@ -967,6 +1154,28 @@ static int ocfs2_parse_options(struct super_block *sb,
 		case Opt_inode64:
 			mopt->mount_opt |= OCFS2_MOUNT_INODE64;
 			break;
+		case Opt_usrquota:
+			/* We check only on remount, otherwise features
+			 * aren't yet initialized. */
+			if (is_remount && !OCFS2_HAS_RO_COMPAT_FEATURE(sb,
+			    OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) {
+				mlog(ML_ERROR, "User quota requested but "
+				     "filesystem feature is not set\n");
+				status = 0;
+				goto bail;
+			}
+			mopt->mount_opt |= OCFS2_MOUNT_USRQUOTA;
+			break;
+		case Opt_grpquota:
+			if (is_remount && !OCFS2_HAS_RO_COMPAT_FEATURE(sb,
+			    OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) {
+				mlog(ML_ERROR, "Group quota requested but "
+				     "filesystem feature is not set\n");
+				status = 0;
+				goto bail;
+			}
+			mopt->mount_opt |= OCFS2_MOUNT_GRPQUOTA;
+			break;
 		default:
 			mlog(ML_ERROR,
 			     "Unrecognized mount option \"%s\" "
@@ -1030,6 +1239,10 @@ static int ocfs2_show_options(struct seq_file *s, struct vfsmount *mnt)
 	if (osb->osb_cluster_stack[0])
 		seq_printf(s, ",cluster_stack=%.*s", OCFS2_STACK_LABEL_LEN,
 			   osb->osb_cluster_stack);
+	if (opts & OCFS2_MOUNT_USRQUOTA)
+		seq_printf(s, ",usrquota");
+	if (opts & OCFS2_MOUNT_GRPQUOTA)
+		seq_printf(s, ",grpquota");
 
 	if (opts & OCFS2_MOUNT_NOUSERXATTR)
 		seq_printf(s, ",nouser_xattr");
@@ -1354,6 +1567,8 @@ static void ocfs2_dismount_volume(struct super_block *sb, int mnt_err)
 	osb = OCFS2_SB(sb);
 	BUG_ON(!osb);
 
+	ocfs2_disable_quotas(osb);
+
 	ocfs2_shutdown_local_alloc(osb);
 
 	ocfs2_truncate_log_shutdown(osb);
@@ -1464,6 +1679,8 @@ static int ocfs2_initialize_super(struct super_block *sb,
 	sb->s_fs_info = osb;
 	sb->s_op = &ocfs2_sops;
 	sb->s_export_op = &ocfs2_export_ops;
+	sb->s_qcop = &ocfs2_quotactl_ops;
+	sb->dq_op = &ocfs2_quota_operations;
 	sb->s_xattr = ocfs2_xattr_handlers;
 	sb->s_time_gran = 1;
 	sb->s_flags |= MS_NOATIME;
-- 
1.5.2.4

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

* [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2)
  2008-10-24 22:05 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
@ 2008-10-27  7:22 ` tristan.ye
  2008-10-27  9:08 ` tristan.ye
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 69+ messages in thread
From: tristan.ye @ 2008-10-27  7:22 UTC (permalink / raw)
  To: ocfs2-devel

On Sat, 2008-10-25 at 00:05 +0200, Jan Kara wrote:
> Hello,
> 
> the following patch series implements quotas for OCFS2. The patch
> series is based on:
> git://git.kernel.org/pub/scm/linux/kernel/git/mfasheh/ocfs2.git linux-next

Jan,

I was working on the latest pulled ocfs2.git you mentioned above,seems
the new patches series you've sent still can be applied correctly
there,hit following errors when applying the patches,

[root at ocfs2-test6 linux-next]# for i in $(seq 29);do echo "****Applying
patch #${i}****";patch -p1</work/quota-patches/quota-patch${i};done

****Applying patch #1****
patching file fs/dquot.c
patching file include/linux/quota.h
****Applying patch #2****
patching file fs/dquot.c
patching file fs/quota_v1.c
patching file fs/quota_v2.c
patching file include/linux/quota.h
patching file include/linux/quotaops.h
****Applying patch #3****
patching file fs/dquot.c
****Applying patch #4****
patching file fs/dquot.c
patching file include/linux/quotaops.h
****Applying patch #5****
patching file fs/dquot.c
patching file fs/quota.c
patching file include/linux/quota.h
patching file include/linux/quotaops.h
****Applying patch #6****
patching file fs/ext3/super.c
****Applying patch #7****
patching file fs/ext4/super.c
****Applying patch #8****
patching file fs/reiserfs/super.c
****Applying patch #9****
patching file include/linux/quotaops.h
****Applying patch #10****
patching file fs/dquot.c
patching file fs/quota.c
patching file include/linux/quota.h
****Applying patch #11****
patching file fs/quota_v1.c
patching file fs/quota_v2.c
patching file fs/quotaio_v1.h
patching file fs/quotaio_v2.h
patching file include/linux/quotaio_v1.h
patching file include/linux/quotaio_v2.h
****Applying patch #12****
patching file fs/Kconfig
Hunk #1 succeeded at 583 (offset -18 lines).
patching file fs/Makefile
Hunk #1 succeeded at 53 (offset -1 lines).
patching file fs/quota_tree.c
patching file fs/quota_tree.h
patching file fs/quota_v2.c
patching file fs/quotaio_v2.h
patching file include/linux/dqblk_qtree.h
patching file include/linux/dqblk_v2.h
****Applying patch #13****
patching file fs/quota_v2.c
patching file include/linux/dqblk_v1.h
patching file include/linux/dqblk_v2.h
patching file include/linux/quota.h
****Applying patch #14****
patching file fs/dquot.c
patching file include/linux/quota.h
****Applying patch #15****
patching file fs/dquot.c
patching file include/linux/quota.h
****Applying patch #16****
patching file fs/dquot.c
patching file include/linux/quotaops.h
****Applying patch #17****
patching file fs/dquot.c
patching file include/linux/quotaops.h
****Applying patch #18****
patching file mm/pdflush.c
****Applying patch #19****
patching file fs/ocfs2/file.c
Hunk #1 succeeded at 246 (offset -1 lines).
Hunk #3 succeeded at 1209 (offset 153 lines).
Hunk #4 succeeded at 1259 (offset -1 lines).
Hunk #5 succeeded at 1506 (offset 153 lines).
****Applying patch #20****
patching file fs/ocfs2/journal.c
Hunk #2 FAILED at 283.
1 out of 2 hunks FAILED -- saving rejects to file fs/ocfs2/journal.c.rej
****Applying patch #21****
patching file fs/ocfs2/namei.c
Hunk #1 succeeded at 382 (offset 4 lines).
****Applying patch #22****
patching file fs/ocfs2/namei.c
Hunk #1 succeeded at 495 (offset 4 lines).
****Applying patch #23****
patching file fs/ocfs2/inode.c
Hunk #1 succeeded at 286 (offset -6 lines).
patching file fs/ocfs2/ocfs2_fs.h
Hunk #1 FAILED at 93.
Hunk #2 succeeded at 155 (offset -4 lines).
Hunk #4 succeeded at 322 (offset -10 lines).
Hunk #6 succeeded at 355 (offset -10 lines).
1 out of 6 hunks FAILED -- saving rejects to file
fs/ocfs2/ocfs2_fs.h.rej
patching file fs/ocfs2/super.c
Hunk #1 succeeded at 213 (offset -8 lines).
Hunk #3 succeeded at 284 (offset -8 lines).
****Applying patch #24****
patching file fs/ocfs2/inode.c
Hunk #1 succeeded at 278 (offset -6 lines).
****Applying patch #25****
patching file fs/ocfs2/Makefile
Hunk #1 FAILED at 35.
1 out of 1 hunk FAILED -- saving rejects to file fs/ocfs2/Makefile.rej
patching file fs/ocfs2/cluster/masklog.h
Hunk #1 FAILED at 113.
1 out of 1 hunk FAILED -- saving rejects to file
fs/ocfs2/cluster/masklog.h.rej
patching file fs/ocfs2/dir.c
Hunk #1 FAILED at 82.
1 out of 1 hunk FAILED -- saving rejects to file fs/ocfs2/dir.c.rej
patching file fs/ocfs2/dlmglue.c
Hunk #9 succeeded at 3490 (offset 5 lines).
patching file fs/ocfs2/dlmglue.h
patching file fs/ocfs2/file.c
Hunk #1 succeeded at 302 (offset -1 lines).
patching file fs/ocfs2/file.h
Hunk #1 FAILED at 51.
1 out of 1 hunk FAILED -- saving rejects to file fs/ocfs2/file.h.rej
patching file fs/ocfs2/inode.h
Hunk #1 succeeded at 140 (offset -2 lines).
patching file fs/ocfs2/ocfs2_fs.h
Hunk #1 succeeded at 730 with fuzz 2 (offset -148 lines).
patching file fs/ocfs2/ocfs2_lockid.h
patching file fs/ocfs2/quota.h
patching file fs/ocfs2/quota_global.c
patching file fs/ocfs2/quota_local.c
patching file fs/ocfs2/super.c
Hunk #1 FAILED at 65.
Hunk #2 succeeded at 139 (offset -1 lines).
Hunk #3 succeeded at 1040 (offset -36 lines).
Hunk #4 succeeded at 1099 (offset -1 lines).
Hunk #5 succeeded at 1180 (offset -37 lines).
Hunk #6 succeeded at 1245 (offset -1 lines).
1 out of 6 hunks FAILED -- saving rejects to file fs/ocfs2/super.c.rej
****Applying patch #26****
patching file fs/ocfs2/alloc.c
Hunk #2 succeeded at 5946 (offset -405 lines).
Hunk #3 FAILED at 6261.
Hunk #4 succeeded at 6681 (offset -5 lines).
Hunk #5 succeeded at 6301 (offset -405 lines).
Hunk #6 succeeded at 6780 (offset -6 lines).
1 out of 6 hunks FAILED -- saving rejects to file fs/ocfs2/alloc.c.rej
patching file fs/ocfs2/aops.c
Hunk #2 succeeded at 1737 (offset -14 lines).
Hunk #4 succeeded at 1763 (offset -14 lines).
patching file fs/ocfs2/dir.c
Hunk #2 succeeded at 1183 (offset -34 lines).
Hunk #3 FAILED at 1194.
Hunk #4 succeeded at 1263 (offset -3 lines).
Hunk #5 succeeded at 1360 (offset -34 lines).
Hunk #6 succeeded at 1418 (offset -3 lines).
Hunk #7 FAILED at 1428.
Hunk #8 succeeded at 1429 (offset -34 lines).
2 out of 8 hunks FAILED -- saving rejects to file fs/ocfs2/dir.c.rej
patching file fs/ocfs2/file.c
Hunk #2 FAILED at 57.
Hunk #3 FAILED at 538.
Hunk #4 succeeded at 744 (offset 156 lines).
Hunk #6 succeeded at 1057 (offset 158 lines).
Hunk #7 succeeded at 966 (offset -6 lines).
Hunk #8 succeeded at 1193 (offset 158 lines).
Hunk #9 succeeded at 1311 (offset -6 lines).
2 out of 9 hunks FAILED -- saving rejects to file fs/ocfs2/file.c.rej
patching file fs/ocfs2/inode.c
Hunk #2 succeeded at 602 (offset -18 lines).
Hunk #4 succeeded at 911 (offset -25 lines).
patching file fs/ocfs2/journal.h
Hunk #1 succeeded at 283 with fuzz 2 (offset -10 lines).
Hunk #3 succeeded at 337 (offset -10 lines).
Hunk #5 succeeded at 386 with fuzz 2 (offset -10 lines).
Hunk #6 FAILED at 427.
Hunk #7 succeeded at 474 (offset -12 lines).
1 out of 7 hunks FAILED -- saving rejects to file fs/ocfs2/journal.h.rej
patching file fs/ocfs2/namei.c
Hunk #2 succeeded at 66 (offset -1 lines).
Hunk #4 succeeded at 230 (offset -1 lines).
Hunk #6 succeeded at 305 (offset -1 lines).
Hunk #8 succeeded at 385 with fuzz 2 (offset 4 lines).
Hunk #10 succeeded at 416 (offset 4 lines).
Hunk #12 succeeded at 462 (offset 4 lines).
Hunk #14 succeeded at 519 (offset 4 lines).
Hunk #16 succeeded at 808 (offset 7 lines).
Hunk #17 succeeded at 1214 (offset 7 lines).
Hunk #18 succeeded at 1539 (offset 17 lines).
Hunk #19 succeeded at 1586 (offset 7 lines).
Hunk #20 succeeded at 1611 (offset 17 lines).
Hunk #21 FAILED at 1635.
Hunk #22 succeeded at 1687 (offset 7 lines).
Hunk #23 succeeded at 1717 (offset 20 lines).
1 out of 23 hunks FAILED -- saving rejects to file fs/ocfs2/namei.c.rej
can't find file to patch@input line 945
Perhaps you used the wrong -p or --strip option?
The text leading up to this was:
--------------------------
|diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
|index 802c414..d4bc0ab 100644
|--- a/fs/ocfs2/xattr.c
|+++ b/fs/ocfs2/xattr.c
--------------------------
File to patch: 
==============end=======================


So i turn to the latest 2.6.27 mainline
kernel(git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git) for another attempt,it succeeded:)

However,the testing for quota on ocfs2 still being blocked by the mkfs
tools patch,which did not work for me(missing quota.c file in patch?):(


Regards,

Tristan.



> 
> I've adressed Joel's comments, also node recovery is now fully working
> and I've fixed a few issues I found during my testing. So I'm currently
> not aware of any bugs. Please review, test, comment. Thanks.
> 
> 								Honza

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

* [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2)
  2008-10-24 22:05 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
  2008-10-27  7:22 ` tristan.ye
@ 2008-10-27  9:08 ` tristan.ye
  2008-10-27 11:23   ` Jan Kara
  2008-10-29 22:58 ` Mark Fasheh
  2008-11-06  1:09 ` Mark Fasheh
  3 siblings, 1 reply; 69+ messages in thread
From: tristan.ye @ 2008-10-27  9:08 UTC (permalink / raw)
  To: ocfs2-devel

On Sat, 2008-10-25 at 00:05 +0200, Jan Kara wrote:
> Hello,
> 
> the following patch series implements quotas for OCFS2. The patch
> series is based on:
> git://git.kernel.org/pub/scm/linux/kernel/git/mfasheh/ocfs2.git linux-next

Jan,

Try with linux-next branch on mark's tree this time,still hit a failure
at patch 25

[root at ocfs2-test5 linux-next]# patch
-p1</kernel/quota-patches/quota-patch25
patching file fs/ocfs2/Makefile
Hunk #1 FAILED at 35.
1 out of 1 hunk FAILED -- saving rejects to file fs/ocfs2/Makefile.rej
patching file fs/ocfs2/cluster/masklog.h
patching file fs/ocfs2/dir.c
Hunk #1 FAILED at 82.
1 out of 1 hunk FAILED -- saving rejects to file fs/ocfs2/dir.c.rej
patching file fs/ocfs2/dlmglue.c
Hunk #9 succeeded at 3490 (offset 5 lines).
patching file fs/ocfs2/dlmglue.h
patching file fs/ocfs2/file.c
patching file fs/ocfs2/file.h
patching file fs/ocfs2/inode.h
Hunk #1 succeeded at 144 (offset 2 lines).
patching file fs/ocfs2/ocfs2_fs.h
patching file fs/ocfs2/ocfs2_lockid.h
patching file fs/ocfs2/quota.h
patching file fs/ocfs2/quota_global.c
patching file fs/ocfs2/quota_local.c
patching file fs/ocfs2/super.c
Hunk #3 succeeded at 1077 (offset 1 line).
Hunk #5 succeeded at 1218 (offset 1 line).

Anyway,i do succeeded to apply these patches with linus's latest
mainline 2.6.27 master branch. the tools patch for mkfs also works:),
for current testing,seems all POSIX quota tools(including
quotaon,quotaoff and setquota.) failed to be applied on ocfs2-quota?
right? the failure may due to the fact that tools can not find 2 hidden
quota files?

After mkfs with --fs-feature=usrquota,grpquota.... and also mount it
with usrquota and grpquota options explicitly specified,seems still
failed to enable the quota by quotaon even i did saw these options
showed up in /etc/mtab. the errors was somewhat like i never specified
the options when mounting,
[root@ocfs2-test6 quota_examples-0.0.21]# quotaon -vugf /quota/
quotaon: Mountpoint (or device) /quota not found.

When i check this quota-supported ocfs2 volume by debufs.ocfs2,do find
the 2 quota files under system folder.so,ocfs2 defaultly generate the
quota files without a quotacheck like extN did? so how can I
refresh/check the files by force?should wait the fsck.ocfs2 to have it
come true?

Thus,for current testing, i should expect the quoctl POSXI api to do all
things for me? since we can not do any test manually without the help of
quota tools. am I understanding the things in a right way?

Regards,

Tristan.

> 
> I've adressed Joel's comments, also node recovery is now fully working
> and I've fixed a few issues I found during my testing. So I'm currently
> not aware of any bugs. Please review, test, comment. Thanks.
> 
> 								Honza

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

* [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2)
  2008-10-27  9:08 ` tristan.ye
@ 2008-10-27 11:23   ` Jan Kara
  2008-10-27 11:23     ` Jan Kara
       [not found]     ` <1225159789.6555.24.camel@tristan-laptop.cn.oracle.com>
  0 siblings, 2 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-27 11:23 UTC (permalink / raw)
  To: ocfs2-devel

On Mon 27-10-08 17:08:54, tristan.ye wrote:
> On Sat, 2008-10-25 at 00:05 +0200, Jan Kara wrote:
> > Hello,
> > 
> > the following patch series implements quotas for OCFS2. The patch
> > series is based on:
> > git://git.kernel.org/pub/scm/linux/kernel/git/mfasheh/ocfs2.git linux-next
> 
> Jan,
> 
> Try with linux-next branch on mark's tree this time,still hit a failure
> at patch 25
> 
> [root at ocfs2-test5 linux-next]# patch
> -p1</kernel/quota-patches/quota-patch25
> patching file fs/ocfs2/Makefile
> Hunk #1 FAILED at 35.
> 1 out of 1 hunk FAILED -- saving rejects to file fs/ocfs2/Makefile.rej
> patching file fs/ocfs2/cluster/masklog.h
> patching file fs/ocfs2/dir.c
> Hunk #1 FAILED at 82.
> 1 out of 1 hunk FAILED -- saving rejects to file fs/ocfs2/dir.c.rej
> patching file fs/ocfs2/dlmglue.c
> Hunk #9 succeeded at 3490 (offset 5 lines).
> patching file fs/ocfs2/dlmglue.h
> patching file fs/ocfs2/file.c
> patching file fs/ocfs2/file.h
> patching file fs/ocfs2/inode.h
> Hunk #1 succeeded at 144 (offset 2 lines).
> patching file fs/ocfs2/ocfs2_fs.h
> patching file fs/ocfs2/ocfs2_lockid.h
> patching file fs/ocfs2/quota.h
> patching file fs/ocfs2/quota_global.c
> patching file fs/ocfs2/quota_local.c
> patching file fs/ocfs2/super.c
> Hunk #3 succeeded at 1077 (offset 1 line).
> Hunk #5 succeeded at 1218 (offset 1 line).
> 
> Anyway,i do succeeded to apply these patches with linus's latest
> mainline 2.6.27 master branch. the tools patch for mkfs also works:),
  Hmm, that's a bit strange. I've just rebased my set of patches on top of
linux-next branch in Mark's tree and I got two rejects but in a different
patch... Also my latest patch set probably wouldn't work against 2.6.27
because it's already based on xattr and acl changes which aren't in 2.6.27.
I'm attaching tarball of the patches just for reference.

> for current testing,seems all POSIX quota tools(including
> quotaon,quotaoff and setquota.) failed to be applied on ocfs2-quota?
> right? the failure may due to the fact that tools can not find 2 hidden
> quota files?
  Yes, tools should print some message that filesystem does not support
quotas or something like that.
 
> After mkfs with --fs-feature=usrquota,grpquota.... and also mount it
> with usrquota and grpquota options explicitly specified,seems still
> failed to enable the quota by quotaon even i did saw these options
> showed up in /etc/mtab. the errors was somewhat like i never specified
> the options when mounting,
> [root at ocfs2-test6 quota_examples-0.0.21]# quotaon -vugf /quota/
> quotaon: Mountpoint (or device) /quota not found.
  Well, the -f option is definitely wrong (that means turn quotas off).
Something like: quotaon -avug
  should do the work for you - it will find the filesystem in the /etc/mtab
automatically.

> When i check this quota-supported ocfs2 volume by debufs.ocfs2,do find
> the 2 quota files under system folder.so,ocfs2 defaultly generate the
> quota files without a quotacheck like extN did? so how can I
> refresh/check the files by force?should wait the fsck.ocfs2 to have it
> come true?
  Mkfs creates these two system files (and also node local system files
called aquota0001.user etc). For checking / fixing these files, you have
to wait for fsck.ocfs2 support. But I think you can test quite a lot even
without it - most of the bugs would not probably result in the corruption
of quota file anyway.

> Thus,for current testing, i should expect the quoctl POSXI api to do all
> things for me? since we can not do any test manually without the help of
> quota tools. am I understanding the things in a right way?
  Yes, quotactl() is the only interface you can use (it is used by quota-tools
as well).
								Honza
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

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

* [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2)
  2008-10-27 11:23   ` Jan Kara
@ 2008-10-27 11:23     ` Jan Kara
       [not found]     ` <1225159789.6555.24.camel@tristan-laptop.cn.oracle.com>
  1 sibling, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-27 11:23 UTC (permalink / raw)
  To: ocfs2-devel

  Sorry, forgot to attach patches...

							Honza

On Mon 27-10-08 12:23:12, Jan Kara wrote:
> On Mon 27-10-08 17:08:54, tristan.ye wrote:
> > On Sat, 2008-10-25 at 00:05 +0200, Jan Kara wrote:
> > > Hello,
> > > 
> > > the following patch series implements quotas for OCFS2. The patch
> > > series is based on:
> > > git://git.kernel.org/pub/scm/linux/kernel/git/mfasheh/ocfs2.git linux-next
> > 
> > Jan,
> > 
> > Try with linux-next branch on mark's tree this time,still hit a failure
> > at patch 25
> > 
> > [root at ocfs2-test5 linux-next]# patch
> > -p1</kernel/quota-patches/quota-patch25
> > patching file fs/ocfs2/Makefile
> > Hunk #1 FAILED at 35.
> > 1 out of 1 hunk FAILED -- saving rejects to file fs/ocfs2/Makefile.rej
> > patching file fs/ocfs2/cluster/masklog.h
> > patching file fs/ocfs2/dir.c
> > Hunk #1 FAILED at 82.
> > 1 out of 1 hunk FAILED -- saving rejects to file fs/ocfs2/dir.c.rej
> > patching file fs/ocfs2/dlmglue.c
> > Hunk #9 succeeded at 3490 (offset 5 lines).
> > patching file fs/ocfs2/dlmglue.h
> > patching file fs/ocfs2/file.c
> > patching file fs/ocfs2/file.h
> > patching file fs/ocfs2/inode.h
> > Hunk #1 succeeded at 144 (offset 2 lines).
> > patching file fs/ocfs2/ocfs2_fs.h
> > patching file fs/ocfs2/ocfs2_lockid.h
> > patching file fs/ocfs2/quota.h
> > patching file fs/ocfs2/quota_global.c
> > patching file fs/ocfs2/quota_local.c
> > patching file fs/ocfs2/super.c
> > Hunk #3 succeeded at 1077 (offset 1 line).
> > Hunk #5 succeeded at 1218 (offset 1 line).
> > 
> > Anyway,i do succeeded to apply these patches with linus's latest
> > mainline 2.6.27 master branch. the tools patch for mkfs also works:),
>   Hmm, that's a bit strange. I've just rebased my set of patches on top of
> linux-next branch in Mark's tree and I got two rejects but in a different
> patch... Also my latest patch set probably wouldn't work against 2.6.27
> because it's already based on xattr and acl changes which aren't in 2.6.27.
> I'm attaching tarball of the patches just for reference.
> 
> > for current testing,seems all POSIX quota tools(including
> > quotaon,quotaoff and setquota.) failed to be applied on ocfs2-quota?
> > right? the failure may due to the fact that tools can not find 2 hidden
> > quota files?
>   Yes, tools should print some message that filesystem does not support
> quotas or something like that.
>  
> > After mkfs with --fs-feature=usrquota,grpquota.... and also mount it
> > with usrquota and grpquota options explicitly specified,seems still
> > failed to enable the quota by quotaon even i did saw these options
> > showed up in /etc/mtab. the errors was somewhat like i never specified
> > the options when mounting,
> > [root at ocfs2-test6 quota_examples-0.0.21]# quotaon -vugf /quota/
> > quotaon: Mountpoint (or device) /quota not found.
>   Well, the -f option is definitely wrong (that means turn quotas off).
> Something like: quotaon -avug
>   should do the work for you - it will find the filesystem in the /etc/mtab
> automatically.
> 
> > When i check this quota-supported ocfs2 volume by debufs.ocfs2,do find
> > the 2 quota files under system folder.so,ocfs2 defaultly generate the
> > quota files without a quotacheck like extN did? so how can I
> > refresh/check the files by force?should wait the fsck.ocfs2 to have it
> > come true?
>   Mkfs creates these two system files (and also node local system files
> called aquota0001.user etc). For checking / fixing these files, you have
> to wait for fsck.ocfs2 support. But I think you can test quite a lot even
> without it - most of the bugs would not probably result in the corruption
> of quota file anyway.
> 
> > Thus,for current testing, i should expect the quoctl POSXI api to do all
> > things for me? since we can not do any test manually without the help of
> > quota tools. am I understanding the things in a right way?
>   Yes, quotactl() is the only interface you can use (it is used by quota-tools
> as well).
> 								Honza
> -- 
> Jan Kara <jack@suse.cz>
> SUSE Labs, CR
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR
-------------- next part --------------
A non-text attachment was scrubbed...
Name: ocfs2-patches.tar.gz
Type: application/x-compressed-tar
Size: 67500 bytes
Desc: not available
Url : http://oss.oracle.com/pipermail/ocfs2-devel/attachments/20081027/4c9f59cc/attachment-0001.bin 

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

* [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2)
       [not found]     ` <1225159789.6555.24.camel@tristan-laptop.cn.oracle.com>
@ 2008-10-28 11:23       ` tristan.ye
  2008-10-29  1:57         ` Jan Kara
  0 siblings, 1 reply; 69+ messages in thread
From: tristan.ye @ 2008-10-28 11:23 UTC (permalink / raw)
  To: ocfs2-devel

On Tue, 2008-10-28 at 10:09 +0800, tristan.ye wrote:
> On Mon, 2008-10-27 at 12:23 +0100, Jan Kara wrote:
> > On Mon 27-10-08 17:08:54, tristan.ye wrote:
> > > On Sat, 2008-10-25 at 00:05 +0200, Jan Kara wrote:
> > > > Hello,
> > > > 
> > > > the following patch series implements quotas for OCFS2. The patch
> > > > series is based on:
> > > > git://git.kernel.org/pub/scm/linux/kernel/git/mfasheh/ocfs2.git linux-next
> > > 
> > > Jan,
> > > 
> > > Try with linux-next branch on mark's tree this time,still hit a failure
> > > at patch 25
> > > 
> > > [root at ocfs2-test5 linux-next]# patch
> > > -p1</kernel/quota-patches/quota-patch25
> > > patching file fs/ocfs2/Makefile
> > > Hunk #1 FAILED at 35.
> > > 1 out of 1 hunk FAILED -- saving rejects to file fs/ocfs2/Makefile.rej
> > > patching file fs/ocfs2/cluster/masklog.h
> > > patching file fs/ocfs2/dir.c
> > > Hunk #1 FAILED at 82.
> > > 1 out of 1 hunk FAILED -- saving rejects to file fs/ocfs2/dir.c.rej
> > > patching file fs/ocfs2/dlmglue.c
> > > Hunk #9 succeeded at 3490 (offset 5 lines).
> > > patching file fs/ocfs2/dlmglue.h
> > > patching file fs/ocfs2/file.c
> > > patching file fs/ocfs2/file.h
> > > patching file fs/ocfs2/inode.h
> > > Hunk #1 succeeded at 144 (offset 2 lines).
> > > patching file fs/ocfs2/ocfs2_fs.h
> > > patching file fs/ocfs2/ocfs2_lockid.h
> > > patching file fs/ocfs2/quota.h
> > > patching file fs/ocfs2/quota_global.c
> > > patching file fs/ocfs2/quota_local.c
> > > patching file fs/ocfs2/super.c
> > > Hunk #3 succeeded at 1077 (offset 1 line).
> > > Hunk #5 succeeded at 1218 (offset 1 line).
> > > 
> > > Anyway,i do succeeded to apply these patches with linus's latest
> > > mainline 2.6.27 master branch. the tools patch for mkfs also works:),
> >   Hmm, that's a bit strange. I've just rebased my set of patches on top of
> > linux-next branch in Mark's tree and I got two rejects but in a different
> > patch... Also my latest patch set probably wouldn't work against 2.6.27
> > because it's already based on xattr and acl changes which aren't in 2.6.27.
> > I'm attaching tarball of the patches just for reference.
> 
> I'm also hitting rejects by using the newly attached patch set on
> linux-next branch of mark's tree,
> 3 patches hit the reject failure,they were patch #5,#22 #23,following
> output will tell the detail,
> 
> ============message output when applying the patches==============
> patch
> -p1<../ocfs2-patches/0005-quota-Allow-to-separately-enable-quota-accounting-a.patch 
> patching file fs/dquot.c
> Hunk #1 succeeded at 489 (offset 2 lines).
> Hunk #3 succeeded at 594 (offset 2 lines).
> Hunk #5 succeeded at 1041 (offset 4 lines).
> Hunk #7 succeeded at 1116 (offset 4 lines).
> Hunk #9 succeeded at 1552 (offset 4 lines).
> Hunk #11 succeeded at 1584 (offset 4 lines).
> Hunk #13 succeeded at 1644 (offset 4 lines).
> Hunk #15 succeeded at 1670 (offset 4 lines).
> Hunk #17 succeeded at 1720 (offset 4 lines).
> Hunk #19 succeeded at 1785 (offset 4 lines).
> Hunk #20 FAILED at 1817.
> Hunk #22 succeeded at 1905 (offset 4 lines).
> Hunk #24 succeeded at 2061 (offset 4 lines).
> Hunk #26 succeeded at 2099 (offset 4 lines).
> 1 out of 27 hunks FAILED -- saving rejects to file fs/dquot.c.rej
> 
> 
> patch
> -p1<../ocfs2-patches/0022-ocfs2-Implementation-of-local-and-global-quota-file.patch 
> patching file fs/ocfs2/Makefile
> Hunk #1 FAILED at 35.
> 1 out of 1 hunk FAILED -- saving rejects to file fs/ocfs2/Makefile.rej
> patching file fs/ocfs2/cluster/masklog.h
> patching file fs/ocfs2/dir.c
> Hunk #1 FAILED at 82.
> 1 out of 1 hunk FAILED -- saving rejects to file fs/ocfs2/dir.c.rej
> patching file fs/ocfs2/dlmglue.c
> Hunk #9 succeeded at 3490 (offset 5 lines).
> patching file fs/ocfs2/dlmglue.h
> patching file fs/ocfs2/file.c
> patching file fs/ocfs2/file.h
> patching file fs/ocfs2/inode.h
> Hunk #1 succeeded at 144 (offset 2 lines).
> patching file fs/ocfs2/ocfs2_fs.h
> Hunk #1 succeeded at 878 (offset -1 lines).
> patching file fs/ocfs2/ocfs2_lockid.h
> patching file fs/ocfs2/quota.h
> patching file fs/ocfs2/quota_global.c
> patching file fs/ocfs2/quota_local.c
> patching file fs/ocfs2/super.c
> Hunk #3 succeeded at 1077 (offset 1 line).
> Hunk #5 succeeded at 1218 (offset 1 line).
> 
> patch
> -p1<../ocfs2-patches/0023-ocfs2-Add-quota-calls-for-allocation-and-freeing-of.patch 
> patching file fs/ocfs2/alloc.c
> Hunk #2 succeeded at 6377 (offset 26 lines).
> Hunk #4 succeeded at 6712 (offset 26 lines).
> Hunk #6 succeeded at 6812 (offset 26 lines).
> patching file fs/ocfs2/aops.c
> Hunk #2 succeeded at 1756 (offset 5 lines).
> Hunk #4 succeeded at 1782 (offset 5 lines).
> patching file fs/ocfs2/dir.c
> Hunk #2 succeeded at 1183 (offset -34 lines).
> Hunk #4 succeeded at 1232 (offset -34 lines).
> Hunk #6 succeeded at 1387 (offset -34 lines).
> Hunk #8 succeeded at 1429 (offset -34 lines).
> patching file fs/ocfs2/file.c
> Hunk #4 succeeded at 589 (offset 1 line).
> Hunk #6 succeeded at 902 (offset 3 lines).
> Hunk #8 succeeded at 1038 (offset 3 lines).
> Hunk #9 FAILED at 1320.
> 1 out of 9 hunks FAILED -- saving rejects to file fs/ocfs2/file.c.rej
> patching file fs/ocfs2/inode.c
> Hunk #2 succeeded at 617 (offset -3 lines).
> Hunk #4 succeeded at 933 (offset -3 lines).
> patching file fs/ocfs2/journal.h
> patching file fs/ocfs2/namei.c
> Hunk #8 succeeded at 386 with fuzz 2 (offset 5 lines).
> Hunk #10 succeeded at 417 (offset 5 lines).
> Hunk #11 FAILED at 431.
> Hunk #13 succeeded at 482 (offset 5 lines).
> Hunk #14 FAILED at 520.
> Hunk #15 succeeded at 612 (offset -2 lines).
> Hunk #16 succeeded at 809 (offset 8 lines).
> Hunk #17 succeeded at 1212 (offset 5 lines).
> Hunk #18 succeeded at 1540 (offset 18 lines).
> Hunk #19 succeeded at 1584 (offset 5 lines).
> Hunk #20 succeeded at 1612 (offset 18 lines).
> Hunk #21 succeeded at 1623 (offset 5 lines).
> Hunk #22 succeeded at 1698 (offset 18 lines).
> Hunk #23 succeeded at 1705 (offset 8 lines).
> 2 out of 23 hunks FAILED -- saving rejects to file fs/ocfs2/namei.c.rej
> patching file fs/ocfs2/xattr.c
> Hunk #1 succeeded at 358 (offset 9 lines).
> Hunk #2 FAILED at 4267.
> 1 out of 2 hunks FAILED -- saving rejects to file fs/ocfs2/xattr.c.rej
> ==========3 patches(#5,#22,#23) hit reject failure=======
> 
> Due to above failure, i definitely hit a compiling error when building
> the krenel for testing.
> fs/dquot.c: In function ?vfs_quota_on_path?:
> fs/dquot.c:1894: error: implicit declaration of function
> ?vfs_quota_on_inode?
> make[1]: *** [fs/dquot.o] Error 1
> make: *** [fs] Error 2
> 
> Will you check on your environment? or you will tell me the exact env
> you use to make things comfortably successful?
Jan,

Sorry for making the noise again:)

I'm just curious about one thing, as you said we choose quotaon to
enforce the quota limitation on ocfs2 to keep the consistency with other
fs, and the quotaon finally call the quoctl with the cmd word
'Q_QUOTAON', here i found the quoctl() need a agrumnet to point to  the
path name of file containing the quotas for the filesystem,but
unfortunately these files were hidden from userspace on ocfs2. so this
quotactl call for turning on the quota will definitely fail, is that the
case?

Since the existing POSIX quota tools did not work  under ocfs2
currently, i've written a simple version of the quota tools(just
including getquota,setquotas and quotactl) by the help of quotactl()
API.  All of its binaries and src attached.

As what i said above, my version of quotaon(invoked by 'quotactl -o')
also did not work here ,and fortunately, we really do succeed to set/get
the quota for usr/group.
you can have a check in your env if you wish.

Thanks and regards,

Tristan.

> 
> Yesterday,i really succeeded with your first patch set(0-29 patches
> totally) on linus's mainline kernel 2.6.27, will it be OK for testing?
> 
> 
> 
> Thanks and regards,
> 
> Tristan.
> 
> 
> > 
> > > for current testing,seems all POSIX quota tools(including
> > > quotaon,quotaoff and setquota.) failed to be applied on ocfs2-quota?
> > > right? the failure may due to the fact that tools can not find 2 hidden
> > > quota files?
> >   Yes, tools should print some message that filesystem does not support
> > quotas or something like that.
> >  
> > > After mkfs with --fs-feature=usrquota,grpquota.... and also mount it
> > > with usrquota and grpquota options explicitly specified,seems still
> > > failed to enable the quota by quotaon even i did saw these options
> > > showed up in /etc/mtab. the errors was somewhat like i never specified
> > > the options when mounting,
> > > [root at ocfs2-test6 quota_examples-0.0.21]# quotaon -vugf /quota/
> > > quotaon: Mountpoint (or device) /quota not found.
> >   Well, the -f option is definitely wrong (that means turn quotas off).
> > Something like: quotaon -avug
> >   should do the work for you - it will find the filesystem in the /etc/mtab
> > automatically.
> > 
> > > When i check this quota-supported ocfs2 volume by debufs.ocfs2,do find
> > > the 2 quota files under system folder.so,ocfs2 defaultly generate the
> > > quota files without a quotacheck like extN did? so how can I
> > > refresh/check the files by force?should wait the fsck.ocfs2 to have it
> > > come true?
> >   Mkfs creates these two system files (and also node local system files
> > called aquota0001.user etc). For checking / fixing these files, you have
> > to wait for fsck.ocfs2 support. But I think you can test quite a lot even
> > without it - most of the bugs would not probably result in the corruption
> > of quota file anyway.
> > 
> > > Thus,for current testing, i should expect the quoctl POSXI api to do all
> > > things for me? since we can not do any test manually without the help of
> > > quota tools. am I understanding the things in a right way?
> >   Yes, quotactl() is the only interface you can use (it is used by quota-tools
> > as well).
> > 								Honza
-------------- next part --------------
A non-text attachment was scrubbed...
Name: myquota_tools.tgz
Type: application/x-compressed-tar
Size: 14427 bytes
Desc: not available
Url : http://oss.oracle.com/pipermail/ocfs2-devel/attachments/20081028/1c6a6dcb/attachment-0001.bin 

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

* [Ocfs2-devel] [PATCH 25/29] ocfs2: Implementation of local and global quota file handling
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 25/29] ocfs2: Implementation of local and global quota file handling Jan Kara
@ 2008-10-28 19:07   ` Joel Becker
  2008-10-28 19:36   ` Joel Becker
  2008-11-05 22:49   ` Mark Fasheh
  2 siblings, 0 replies; 69+ messages in thread
From: Joel Becker @ 2008-10-28 19:07 UTC (permalink / raw)
  To: ocfs2-devel

On Sat, Oct 25, 2008 at 12:08:18AM +0200, Jan Kara wrote:
> For each quota type each node has local quota file. In this file it stores
> changes users have made to disk usage via this node. Once in a while this
> information is synced to global file (and thus with other nodes) so that
> limits enforcement at least aproximately works.
> 
> Global quota files contain all the information about usage and limits. It's
> mostly handled by the generic VFS code (which implements a trie of structures
> inside a quota file). We only have to provide functions to convert structures
> from on-disk format to in-memory one. We also have to provide wrappers for
> various quota functions starting transactions and acquiring necessary cluster
> locks before the actual IO is really started.

	Thanks for the description, it makes it a lot more
understandable.

Joel

> Signed-off-by: Jan Kara <jack@suse.cz>
> ---
>  fs/ocfs2/Makefile          |    2 +
>  fs/ocfs2/cluster/masklog.h |    1 +
>  fs/ocfs2/dir.c             |    4 +-
>  fs/ocfs2/dlmglue.c         |  137 +++++++
>  fs/ocfs2/dlmglue.h         |   17 +
>  fs/ocfs2/file.c            |    6 +-
>  fs/ocfs2/file.h            |    3 +
>  fs/ocfs2/inode.h           |    2 +
>  fs/ocfs2/ocfs2_fs.h        |   95 +++++
>  fs/ocfs2/ocfs2_lockid.h    |    5 +
>  fs/ocfs2/quota.h           |   97 +++++
>  fs/ocfs2/quota_global.c    |  863 ++++++++++++++++++++++++++++++++++++++++++++
>  fs/ocfs2/quota_local.c     |  833 ++++++++++++++++++++++++++++++++++++++++++
>  fs/ocfs2/super.c           |   38 ++-
>  14 files changed, 2096 insertions(+), 7 deletions(-)
>  create mode 100644 fs/ocfs2/quota.h
>  create mode 100644 fs/ocfs2/quota_global.c
>  create mode 100644 fs/ocfs2/quota_local.c
> 
> diff --git a/fs/ocfs2/Makefile b/fs/ocfs2/Makefile
> index 589dcdf..11f14e1 100644
> --- a/fs/ocfs2/Makefile
> +++ b/fs/ocfs2/Makefile
> @@ -35,6 +35,8 @@ ocfs2-objs := \
>  	sysfile.o 		\
>  	uptodate.o		\
>  	ver.o			\
> +	quota_local.o		\
> +	quota_global.o		\
>  	xattr.o
>  
>  ocfs2_stackglue-objs := stackglue.o
> diff --git a/fs/ocfs2/cluster/masklog.h b/fs/ocfs2/cluster/masklog.h
> index 57670c6..7e72a81 100644
> --- a/fs/ocfs2/cluster/masklog.h
> +++ b/fs/ocfs2/cluster/masklog.h
> @@ -113,6 +113,7 @@
>  #define ML_QUORUM	0x0000000008000000ULL /* net connection quorum */
>  #define ML_EXPORT	0x0000000010000000ULL /* ocfs2 export operations */
>  #define ML_XATTR	0x0000000020000000ULL /* ocfs2 extended attributes */
> +#define ML_QUOTA	0x0000000040000000ULL /* ocfs2 quota operations */
>  /* bits that are infrequently given and frequently matched in the high word */
>  #define ML_ERROR	0x0000000100000000ULL /* sent to KERN_ERR */
>  #define ML_NOTICE	0x0000000200000000ULL /* setn to KERN_NOTICE */
> diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c
> index 026e6eb..084ba9c 100644
> --- a/fs/ocfs2/dir.c
> +++ b/fs/ocfs2/dir.c
> @@ -82,8 +82,8 @@ static int ocfs2_do_extend_dir(struct super_block *sb,
>  			       struct ocfs2_alloc_context *meta_ac,
>  			       struct buffer_head **new_bh);
>  
> -static struct buffer_head *ocfs2_bread(struct inode *inode,
> -				       int block, int *err, int reada)
> +struct buffer_head *ocfs2_bread(struct inode *inode,
> +				int block, int *err, int reada)
>  {
>  	struct buffer_head *bh = NULL;
>  	int tmperr;
> diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c
> index ec68442..eab90e8 100644
> --- a/fs/ocfs2/dlmglue.c
> +++ b/fs/ocfs2/dlmglue.c
> @@ -32,6 +32,7 @@
>  #include <linux/debugfs.h>
>  #include <linux/seq_file.h>
>  #include <linux/time.h>
> +#include <linux/quotaops.h>
>  
>  #define MLOG_MASK_PREFIX ML_DLM_GLUE
>  #include <cluster/masklog.h>
> @@ -51,6 +52,7 @@
>  #include "slot_map.h"
>  #include "super.h"
>  #include "uptodate.h"
> +#include "quota.h"
>  
>  #include "buffer_head_io.h"
>  
> @@ -68,6 +70,7 @@ struct ocfs2_mask_waiter {
>  static struct ocfs2_super *ocfs2_get_dentry_osb(struct ocfs2_lock_res *lockres);
>  static struct ocfs2_super *ocfs2_get_inode_osb(struct ocfs2_lock_res *lockres);
>  static struct ocfs2_super *ocfs2_get_file_osb(struct ocfs2_lock_res *lockres);
> +static struct ocfs2_super *ocfs2_get_qinfo_osb(struct ocfs2_lock_res *lockres);
>  
>  /*
>   * Return value from ->downconvert_worker functions.
> @@ -102,6 +105,7 @@ static int ocfs2_dentry_convert_worker(struct ocfs2_lock_res *lockres,
>  static void ocfs2_dentry_post_unlock(struct ocfs2_super *osb,
>  				     struct ocfs2_lock_res *lockres);
>  
> +static void ocfs2_set_qinfo_lvb(struct ocfs2_lock_res *lockres);
>  
>  #define mlog_meta_lvb(__level, __lockres) ocfs2_dump_meta_lvb_info(__level, __PRETTY_FUNCTION__, __LINE__, __lockres)
>  
> @@ -258,6 +262,12 @@ static struct ocfs2_lock_res_ops ocfs2_flock_lops = {
>  	.flags		= 0,
>  };
>  
> +static struct ocfs2_lock_res_ops ocfs2_qinfo_lops = {
> +	.set_lvb	= ocfs2_set_qinfo_lvb,
> +	.get_osb	= ocfs2_get_qinfo_osb,
> +	.flags		= LOCK_TYPE_REQUIRES_REFRESH | LOCK_TYPE_USES_LVB,
> +};
> +
>  static inline int ocfs2_is_inode_lock(struct ocfs2_lock_res *lockres)
>  {
>  	return lockres->l_type == OCFS2_LOCK_TYPE_META ||
> @@ -279,6 +289,13 @@ static inline struct ocfs2_dentry_lock *ocfs2_lock_res_dl(struct ocfs2_lock_res
>  	return (struct ocfs2_dentry_lock *)lockres->l_priv;
>  }
>  
> +static inline struct ocfs2_mem_dqinfo *ocfs2_lock_res_qinfo(struct ocfs2_lock_res *lockres)
> +{
> +	BUG_ON(lockres->l_type != OCFS2_LOCK_TYPE_QINFO);
> +
> +	return (struct ocfs2_mem_dqinfo *)lockres->l_priv;
> +}
> +
>  static inline struct ocfs2_super *ocfs2_get_lockres_osb(struct ocfs2_lock_res *lockres)
>  {
>  	if (lockres->l_ops->get_osb)
> @@ -507,6 +524,13 @@ static struct ocfs2_super *ocfs2_get_inode_osb(struct ocfs2_lock_res *lockres)
>  	return OCFS2_SB(inode->i_sb);
>  }
>  
> +static struct ocfs2_super *ocfs2_get_qinfo_osb(struct ocfs2_lock_res *lockres)
> +{
> +	struct ocfs2_mem_dqinfo *info = lockres->l_priv;
> +
> +	return OCFS2_SB(info->dqi_gi.dqi_sb);
> +}
> +
>  static struct ocfs2_super *ocfs2_get_file_osb(struct ocfs2_lock_res *lockres)
>  {
>  	struct ocfs2_file_private *fp = lockres->l_priv;
> @@ -609,6 +633,17 @@ void ocfs2_file_lock_res_init(struct ocfs2_lock_res *lockres,
>  	lockres->l_flags |= OCFS2_LOCK_NOCACHE;
>  }
>  
> +void ocfs2_qinfo_lock_res_init(struct ocfs2_lock_res *lockres,
> +			       struct ocfs2_mem_dqinfo *info)
> +{
> +	ocfs2_lock_res_init_once(lockres);
> +	ocfs2_build_lock_name(OCFS2_LOCK_TYPE_QINFO, info->dqi_gi.dqi_type,
> +			      0, lockres->l_name);
> +	ocfs2_lock_res_init_common(OCFS2_SB(info->dqi_gi.dqi_sb), lockres,
> +				   OCFS2_LOCK_TYPE_QINFO, &ocfs2_qinfo_lops,
> +				   info);
> +}
> +
>  void ocfs2_lock_res_free(struct ocfs2_lock_res *res)
>  {
>  	mlog_entry_void();
> @@ -3450,6 +3485,108 @@ static int ocfs2_dentry_convert_worker(struct ocfs2_lock_res *lockres,
>  	return UNBLOCK_CONTINUE_POST;
>  }
>  
> +static void ocfs2_set_qinfo_lvb(struct ocfs2_lock_res *lockres)
> +{
> +	struct ocfs2_qinfo_lvb *lvb;
> +	struct ocfs2_mem_dqinfo *oinfo = ocfs2_lock_res_qinfo(lockres);
> +	struct mem_dqinfo *info = sb_dqinfo(oinfo->dqi_gi.dqi_sb,
> +					    oinfo->dqi_gi.dqi_type);
> +
> +	mlog_entry_void();
> +
> +	lvb = (struct ocfs2_qinfo_lvb *)ocfs2_dlm_lvb(&lockres->l_lksb);
> +	lvb->lvb_version   = OCFS2_LVB_VERSION;
> +	lvb->lvb_bgrace = cpu_to_be32(info->dqi_bgrace);
> +	lvb->lvb_igrace = cpu_to_be32(info->dqi_igrace);
> +	lvb->lvb_syncms = cpu_to_be32(oinfo->dqi_syncms);
> +	lvb->lvb_blocks = cpu_to_be32(oinfo->dqi_gi.dqi_blocks);
> +	lvb->lvb_free_blk = cpu_to_be32(oinfo->dqi_gi.dqi_free_blk);
> +	lvb->lvb_free_entry = cpu_to_be32(oinfo->dqi_gi.dqi_free_entry);
> +
> +	mlog_exit_void();
> +}
> +
> +void ocfs2_qinfo_unlock(struct ocfs2_mem_dqinfo *oinfo, int ex)
> +{
> +	struct ocfs2_lock_res *lockres = &oinfo->dqi_gqlock;
> +	struct ocfs2_super *osb = OCFS2_SB(oinfo->dqi_gi.dqi_sb);
> +	int level = ex ? DLM_LOCK_EX : DLM_LOCK_PR;
> +
> +	mlog_entry_void();
> +	if (!ocfs2_is_hard_readonly(osb) && !ocfs2_mount_local(osb))
> +		ocfs2_cluster_unlock(osb, lockres, level);
> +	mlog_exit_void();
> +}
> +
> +/* Lock quota info, this function expects at least shared lock on the quota file
> + * so that we can safely refresh quota info from disk. */
> +int ocfs2_qinfo_lock(struct ocfs2_mem_dqinfo *oinfo, int ex)
> +{
> +	struct mem_dqinfo *info = sb_dqinfo(oinfo->dqi_gi.dqi_sb,
> +					    oinfo->dqi_gi.dqi_type);
> +	struct ocfs2_lock_res *lockres = &oinfo->dqi_gqlock;
> +	struct ocfs2_super *osb = OCFS2_SB(oinfo->dqi_gi.dqi_sb);
> +	struct ocfs2_qinfo_lvb *lvb;
> +	int level = ex ? DLM_LOCK_EX : DLM_LOCK_PR;
> +	int status = 0;
> +	struct buffer_head *bh;
> +	struct ocfs2_global_disk_dqinfo *gdinfo;
> +
> +	mlog_entry_void();
> +
> +	/* We'll allow faking a readonly metadata lock for
> +	 * rodevices. */
> +	if (ocfs2_is_hard_readonly(osb)) {
> +		if (ex)
> +			status = -EROFS;
> +		goto bail;
> +	}
> +	if (ocfs2_mount_local(osb))
> +		goto bail;
> +
> +	status = ocfs2_cluster_lock(osb, lockres, level, 0, 0);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto bail;
> +	}
> +	if (!ocfs2_should_refresh_lock_res(lockres))
> +		goto bail;
> +	/* OK, we have the lock but we need to refresh the quota info */
> +	lvb = ocfs2_dlm_lvb(&lockres->l_lksb);
> +	if (lvb->lvb_version == OCFS2_LVB_VERSION) {
> +		info->dqi_bgrace = be32_to_cpu(lvb->lvb_bgrace);
> +		info->dqi_igrace = be32_to_cpu(lvb->lvb_igrace);
> +		oinfo->dqi_syncms = be32_to_cpu(lvb->lvb_syncms);
> +		oinfo->dqi_gi.dqi_blocks = be32_to_cpu(lvb->lvb_blocks);
> +		oinfo->dqi_gi.dqi_free_blk = be32_to_cpu(lvb->lvb_free_blk);
> +		oinfo->dqi_gi.dqi_free_entry =
> +					be32_to_cpu(lvb->lvb_free_entry);
> +	} else {
> +		bh = ocfs2_read_quota_block(oinfo->dqi_gqinode, 0, &status);
> +		if (!bh) {
> +			ocfs2_qinfo_unlock(oinfo, ex);
> +			mlog_errno(status);
> +			goto bail_refresh;
> +		}
> +		gdinfo = (struct ocfs2_global_disk_dqinfo *)
> +					(bh->b_data + OCFS2_GLOBAL_INFO_OFF);
> +		info->dqi_bgrace = le32_to_cpu(gdinfo->dqi_bgrace);
> +		info->dqi_igrace = le32_to_cpu(gdinfo->dqi_igrace);
> +		oinfo->dqi_syncms = le32_to_cpu(gdinfo->dqi_syncms);
> +		oinfo->dqi_gi.dqi_blocks = le32_to_cpu(gdinfo->dqi_blocks);
> +		oinfo->dqi_gi.dqi_free_blk = le32_to_cpu(gdinfo->dqi_free_blk);
> +		oinfo->dqi_gi.dqi_free_entry =
> +					le32_to_cpu(gdinfo->dqi_free_entry);
> +		brelse(bh);
> +		ocfs2_track_lock_refresh(lockres);
> +	}
> +bail_refresh:
> +	ocfs2_complete_lock_res_refresh(lockres, status);
> +bail:
> +	mlog_exit(status);
> +	return status;
> +}
> +
>  /*
>   * This is the filesystem locking protocol.  It provides the lock handling
>   * hooks for the underlying DLM.  It has a maximum version number.
> diff --git a/fs/ocfs2/dlmglue.h b/fs/ocfs2/dlmglue.h
> index 2bb01f0..6a34048 100644
> --- a/fs/ocfs2/dlmglue.h
> +++ b/fs/ocfs2/dlmglue.h
> @@ -49,6 +49,17 @@ struct ocfs2_meta_lvb {
>  	__be32       lvb_reserved2;
>  };
>  
> +struct ocfs2_qinfo_lvb {
> +	__u8	lvb_version;
> +	__u8	lvb_reserved[3];
> +	__be32	lvb_bgrace;
> +	__be32	lvb_igrace;
> +	__be32	lvb_syncms;
> +	__be32	lvb_blocks;
> +	__be32	lvb_free_blk;
> +	__be32	lvb_free_entry;
> +};
> +
>  /* ocfs2_inode_lock_full() 'arg_flags' flags */
>  /* don't wait on recovery. */
>  #define OCFS2_META_LOCK_RECOVERY	(0x01)
> @@ -69,6 +80,9 @@ void ocfs2_dentry_lock_res_init(struct ocfs2_dentry_lock *dl,
>  struct ocfs2_file_private;
>  void ocfs2_file_lock_res_init(struct ocfs2_lock_res *lockres,
>  			      struct ocfs2_file_private *fp);
> +struct ocfs2_mem_dqinfo;
> +void ocfs2_qinfo_lock_res_init(struct ocfs2_lock_res *lockres,
> +                               struct ocfs2_mem_dqinfo *info);
>  void ocfs2_lock_res_free(struct ocfs2_lock_res *res);
>  int ocfs2_create_new_inode_locks(struct inode *inode);
>  int ocfs2_drop_inode_locks(struct inode *inode);
> @@ -103,6 +117,9 @@ int ocfs2_dentry_lock(struct dentry *dentry, int ex);
>  void ocfs2_dentry_unlock(struct dentry *dentry, int ex);
>  int ocfs2_file_lock(struct file *file, int ex, int trylock);
>  void ocfs2_file_unlock(struct file *file);
> +int ocfs2_qinfo_lock(struct ocfs2_mem_dqinfo *oinfo, int ex);
> +void ocfs2_qinfo_unlock(struct ocfs2_mem_dqinfo *oinfo, int ex);
> +
>  
>  void ocfs2_mark_lockres_freeing(struct ocfs2_lock_res *lockres);
>  void ocfs2_simple_drop_lockres(struct ocfs2_super *osb,
> diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
> index e135da1..9af16e0 100644
> --- a/fs/ocfs2/file.c
> +++ b/fs/ocfs2/file.c
> @@ -303,9 +303,9 @@ bail:
>  	return status;
>  }
>  
> -static int ocfs2_simple_size_update(struct inode *inode,
> -				    struct buffer_head *di_bh,
> -				    u64 new_i_size)
> +int ocfs2_simple_size_update(struct inode *inode,
> +			     struct buffer_head *di_bh,
> +			     u64 new_i_size)
>  {
>  	int ret;
>  	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
> diff --git a/fs/ocfs2/file.h b/fs/ocfs2/file.h
> index e92382c..172f9fb 100644
> --- a/fs/ocfs2/file.h
> +++ b/fs/ocfs2/file.h
> @@ -51,6 +51,9 @@ int ocfs2_add_inode_data(struct ocfs2_super *osb,
>  			 struct ocfs2_alloc_context *data_ac,
>  			 struct ocfs2_alloc_context *meta_ac,
>  			 enum ocfs2_alloc_restarted *reason_ret);
> +int ocfs2_simple_size_update(struct inode *inode,
> +			     struct buffer_head *di_bh,
> +			     u64 new_i_size);
>  int ocfs2_extend_no_holes(struct inode *inode, u64 new_i_size,
>  			  u64 zero_to);
>  int ocfs2_setattr(struct dentry *dentry, struct iattr *attr);
> diff --git a/fs/ocfs2/inode.h b/fs/ocfs2/inode.h
> index 2f37af9..e90a159 100644
> --- a/fs/ocfs2/inode.h
> +++ b/fs/ocfs2/inode.h
> @@ -142,6 +142,8 @@ int ocfs2_mark_inode_dirty(handle_t *handle,
>  			   struct buffer_head *bh);
>  int ocfs2_aio_read(struct file *file, struct kiocb *req, struct iocb *iocb);
>  int ocfs2_aio_write(struct file *file, struct kiocb *req, struct iocb *iocb);
> +struct buffer_head *ocfs2_bread(struct inode *inode,
> +				int block, int *err, int reada);
>  
>  void ocfs2_set_inode_flags(struct inode *inode);
>  void ocfs2_get_inode_flags(struct ocfs2_inode_info *oi);
> diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h
> index dd17137..04bebd2 100644
> --- a/fs/ocfs2/ocfs2_fs.h
> +++ b/fs/ocfs2/ocfs2_fs.h
> @@ -878,6 +878,101 @@ static inline int ocfs2_xattr_get_type(struct ocfs2_xattr_entry *xe)
>  	return xe->xe_type & OCFS2_XATTR_TYPE_MASK;
>  }
>  
> +/*
> + *  On disk structures for global quota file
> + */
> +
> +/* Magic numbers and known versions for global quota files */
> +#define OCFS2_GLOBAL_QMAGICS {\
> +	0x0cf52470, /* USRQUOTA */ \
> +	0x0cf52471  /* GRPQUOTA */ \
> +}
> +
> +#define OCFS2_GLOBAL_QVERSIONS {\
> +	0, \
> +	0, \
> +}
> +
> +/* Generic header of all quota files */
> +struct ocfs2_disk_dqheader {
> +	__le32 dqh_magic;	/* Magic number identifying file */
> +	__le32 dqh_version;	/* Quota format version */
> +};
> +
> +#define OCFS2_GLOBAL_INFO_OFF (sizeof(struct ocfs2_disk_dqheader))
> +
> +/* Information header of global quota file (immediately follows the generic
> + * header) */
> +struct ocfs2_global_disk_dqinfo {
> +/*00*/	__le32 dqi_bgrace;
> +	__le32 dqi_igrace;
> +	__le32 dqi_syncms;
> +	__le32 dqi_blocks;
> +/*10*/	__le32 dqi_free_blk;
> +	__le32 dqi_free_entry;
> +};
> +
> +/* Structure with global user / group information. We reserve some space
> + * for future use. */
> +struct ocfs2_global_disk_dqblk {
> +/*00*/	__le32 dqb_id;          /* ID the structure belongs to */
> +	__le32 dqb_use_count;   /* Number of nodes having reference to this structure */
> +	__le64 dqb_ihardlimit;  /* absolute limit on allocated inodes */
> +/*10*/	__le64 dqb_isoftlimit;  /* preferred inode limit */
> +	__le64 dqb_curinodes;   /* current # allocated inodes */
> +/*20*/	__le64 dqb_bhardlimit;  /* absolute limit on disk space */
> +	__le64 dqb_bsoftlimit;  /* preferred limit on disk space */
> +/*30*/	__le64 dqb_curspace;    /* current space occupied */
> +	__le64 dqb_btime;       /* time limit for excessive disk use */
> +/*40*/	__le64 dqb_itime;       /* time limit for excessive inode use */
> +	__le64 dqb_pad1;
> +/*50*/	__le64 dqb_pad2;
> +};
> +
> +/*
> + *  On-disk structures for local quota file
> + */
> +
> +/* Magic numbers and known versions for local quota files */
> +#define OCFS2_LOCAL_QMAGICS {\
> +	0x0cf524c0, /* USRQUOTA */ \
> +	0x0cf524c1  /* GRPQUOTA */ \
> +}
> +
> +#define OCFS2_LOCAL_QVERSIONS {\
> +	0, \
> +	0, \
> +}
> +
> +/* Quota flags in dqinfo header */
> +#define OLQF_CLEAN	0x0001	/* Quota file is empty (this should be after\
> +				 * quota has been cleanly turned off) */
> +
> +#define OCFS2_LOCAL_INFO_OFF (sizeof(struct ocfs2_disk_dqheader))
> +
> +/* Information header of local quota file (immediately follows the generic
> + * header) */
> +struct ocfs2_local_disk_dqinfo {
> +	__le32 dqi_flags;	/* Flags for quota file */
> +	__le32 dqi_chunks;	/* Number of chunks of quota structures
> +				 * with a bitmap */
> +	__le32 dqi_blocks;	/* Number of blocks allocated for quota file */
> +};
> +
> +/* Header of one chunk of a quota file */
> +struct ocfs2_local_disk_chunk {
> +	__le32 dqc_free;	/* Number of free entries in the bitmap */
> +	u8 dqc_bitmap[0];	/* Bitmap of entries in the corresponding
> +				 * chunk of quota file */
> +};
> +
> +/* One entry in local quota file */
> +struct ocfs2_local_disk_dqblk {
> +/*00*/	__le64 dqb_id;		/* id this quota applies to */
> +	__le64 dqb_spacemod;	/* Change in the amount of used space */
> +/*10*/	__le64 dqb_inodemod;	/* Change in the amount of used inodes */
> +};
> +
>  #ifdef __KERNEL__
>  static inline int ocfs2_fast_symlink_chars(struct super_block *sb)
>  {
> diff --git a/fs/ocfs2/ocfs2_lockid.h b/fs/ocfs2/ocfs2_lockid.h
> index 82c200f..eb6f50c 100644
> --- a/fs/ocfs2/ocfs2_lockid.h
> +++ b/fs/ocfs2/ocfs2_lockid.h
> @@ -46,6 +46,7 @@ enum ocfs2_lock_type {
>  	OCFS2_LOCK_TYPE_DENTRY,
>  	OCFS2_LOCK_TYPE_OPEN,
>  	OCFS2_LOCK_TYPE_FLOCK,
> +	OCFS2_LOCK_TYPE_QINFO,
>  	OCFS2_NUM_LOCK_TYPES
>  };
>  
> @@ -77,6 +78,9 @@ static inline char ocfs2_lock_type_char(enum ocfs2_lock_type type)
>  		case OCFS2_LOCK_TYPE_FLOCK:
>  			c = 'F';
>  			break;
> +		case OCFS2_LOCK_TYPE_QINFO:
> +			c = 'Q';
> +			break;
>  		default:
>  			c = '\0';
>  	}
> @@ -95,6 +99,7 @@ static char *ocfs2_lock_type_strings[] = {
>  	[OCFS2_LOCK_TYPE_DENTRY] = "Dentry",
>  	[OCFS2_LOCK_TYPE_OPEN] = "Open",
>  	[OCFS2_LOCK_TYPE_FLOCK] = "Flock",
> +	[OCFS2_LOCK_TYPE_QINFO] = "Quota",
>  };
>  
>  static inline const char *ocfs2_lock_type_string(enum ocfs2_lock_type type)
> diff --git a/fs/ocfs2/quota.h b/fs/ocfs2/quota.h
> new file mode 100644
> index 0000000..87545ca
> --- /dev/null
> +++ b/fs/ocfs2/quota.h
> @@ -0,0 +1,97 @@
> +/*
> + * quota.h for OCFS2
> + *
> + * On disk quota structures for local and global quota file, in-memory
> + * structures.
> + *
> + */
> +
> +#ifndef _OCFS2_QUOTA_H
> +#define _OCFS2_QUOTA_H
> +
> +#include <linux/types.h>
> +#include <linux/slab.h>
> +#include <linux/quota.h>
> +#include <linux/list.h>
> +#include <linux/dqblk_qtree.h>
> +
> +#include "ocfs2.h"
> +
> +/* Common stuff */
> +/* id number of quota format */
> +#define QFMT_OCFS2 3
> +
> +/* How many bytes to we reserve in each quota file block for our internal
> + * purposes? E.g. checksums... */
> +#define OCFS2_QBLK_RESERVED_SPACE 8
> +
> +/*
> + * In-memory structures
> + */
> +struct ocfs2_dquot {
> +	struct dquot dq_dquot;	/* Generic VFS dquot */
> +	loff_t dq_local_off;	/* Offset in the local quota file */
> +	struct ocfs2_quota_chunk *dq_chunk;	/* Chunk dquot is in */
> +	unsigned int dq_use_count;	/* Number of nodes having reference to this entry in global quota file */
> +	s64 dq_origspace;	/* Last globally synced space usage */
> +	s64 dq_originodes;	/* Last globally synced inode usage */
> +};
> +
> +/* In-memory structure with quota header information */
> +struct ocfs2_mem_dqinfo {
> +	unsigned int dqi_type;		/* Quota type this structure describes */
> +	unsigned int dqi_chunks;	/* Number of chunks in local quota file */
> +	unsigned int dqi_blocks;	/* Number of blocks allocated for local quota file */
> +	unsigned int dqi_syncms;	/* How often should we sync with other nodes */
> +	struct list_head dqi_chunk;	/* List of chunks */
> +	struct inode *dqi_gqinode;	/* Global quota file inode */
> +	struct ocfs2_lock_res dqi_gqlock;	/* Lock protecting quota information structure */
> +	struct buffer_head *dqi_gqi_bh;	/* Buffer head with global quota file inode - set only if inode lock is obtained */
> +	int dqi_gqi_count;		/* Number of holders of dqi_gqi_bh */
> +	struct buffer_head *dqi_lqi_bh;	/* Buffer head with local quota file inode */
> +	struct buffer_head *dqi_ibh;	/* Buffer with information header */
> +	struct qtree_mem_dqinfo dqi_gi;	/* Info about global file */
> +};
> +
> +static inline struct ocfs2_dquot *OCFS2_DQUOT(struct dquot *dquot)
> +{
> +	return container_of(dquot, struct ocfs2_dquot, dq_dquot);
> +}
> +
> +struct ocfs2_quota_chunk {
> +	struct list_head qc_chunk;	/* List of quotafile chunks */
> +	int qc_num;			/* Number of quota chunk */
> +	struct buffer_head *qc_headerbh;	/* Buffer head with chunk header */
> +};
> +
> +extern struct kmem_cache *ocfs2_dquot_cachep;
> +extern struct kmem_cache *ocfs2_qf_chunk_cachep;
> +
> +extern struct qtree_fmt_operations ocfs2_global_ops;
> +
> +ssize_t ocfs2_quota_read(struct super_block *sb, int type, char *data,
> +			 size_t len, loff_t off);
> +ssize_t ocfs2_quota_write(struct super_block *sb, int type,
> +			  const char *data, size_t len, loff_t off);
> +int ocfs2_global_read_info(struct super_block *sb, int type);
> +int ocfs2_global_write_info(struct super_block *sb, int type);
> +int ocfs2_global_read_dquot(struct dquot *dquot);
> +int __ocfs2_sync_dquot(struct dquot *dquot, int freeing);
> +static inline int ocfs2_sync_dquot(struct dquot *dquot)
> +{
> +	return __ocfs2_sync_dquot(dquot, 0);
> +}
> +static inline int ocfs2_global_release_dquot(struct dquot *dquot)
> +{
> +	return __ocfs2_sync_dquot(dquot, 1);
> +}
> +
> +int ocfs2_lock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex);
> +void ocfs2_unlock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex);
> +struct buffer_head *ocfs2_read_quota_block(struct inode *inode,
> +					   int block, int *err);
> +
> +extern struct dquot_operations ocfs2_quota_operations;
> +extern struct quota_format_type ocfs2_quota_format;
> +
> +#endif /* _OCFS2_QUOTA_H */
> diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c
> new file mode 100644
> index 0000000..b937f07
> --- /dev/null
> +++ b/fs/ocfs2/quota_global.c
> @@ -0,0 +1,863 @@
> +/*
> + *  Implementation of operations over global quota file
> + */
> +#include <linux/fs.h>
> +#include <linux/quota.h>
> +#include <linux/quotaops.h>
> +#include <linux/dqblk_qtree.h>
> +
> +#define MLOG_MASK_PREFIX ML_QUOTA
> +#include <cluster/masklog.h>
> +
> +#include "ocfs2_fs.h"
> +#include "ocfs2.h"
> +#include "alloc.h"
> +#include "inode.h"
> +#include "journal.h"
> +#include "file.h"
> +#include "sysfile.h"
> +#include "dlmglue.h"
> +#include "quota.h"
> +
> +static void ocfs2_global_disk2memdqb(struct dquot *dquot, void *dp)
> +{
> +	struct ocfs2_global_disk_dqblk *d = dp;
> +	struct mem_dqblk *m = &dquot->dq_dqb;
> +
> +	/* Update from disk only entries not set by the admin */
> +	if (!test_bit(DQ_LASTSET_B + QIF_ILIMITS_B, &dquot->dq_flags)) {
> +		m->dqb_ihardlimit = le64_to_cpu(d->dqb_ihardlimit);
> +		m->dqb_isoftlimit = le64_to_cpu(d->dqb_isoftlimit);
> +	}
> +	if (!test_bit(DQ_LASTSET_B + QIF_INODES_B, &dquot->dq_flags))
> +		m->dqb_curinodes = le64_to_cpu(d->dqb_curinodes);
> +	if (!test_bit(DQ_LASTSET_B + QIF_BLIMITS_B, &dquot->dq_flags)) {
> +		m->dqb_bhardlimit = le64_to_cpu(d->dqb_bhardlimit);
> +		m->dqb_bsoftlimit = le64_to_cpu(d->dqb_bsoftlimit);
> +	}
> +	if (!test_bit(DQ_LASTSET_B + QIF_SPACE_B, &dquot->dq_flags))
> +		m->dqb_curspace = le64_to_cpu(d->dqb_curspace);
> +	if (!test_bit(DQ_LASTSET_B + QIF_BTIME_B, &dquot->dq_flags))
> +		m->dqb_btime = le64_to_cpu(d->dqb_btime);
> +	if (!test_bit(DQ_LASTSET_B + QIF_ITIME_B, &dquot->dq_flags))
> +		m->dqb_itime = le64_to_cpu(d->dqb_itime);
> +	OCFS2_DQUOT(dquot)->dq_use_count = le32_to_cpu(d->dqb_use_count);
> +}
> +
> +static void ocfs2_global_mem2diskdqb(void *dp, struct dquot *dquot)
> +{
> +	struct ocfs2_global_disk_dqblk *d = dp;
> +	struct mem_dqblk *m = &dquot->dq_dqb;
> +
> +	d->dqb_id = cpu_to_le32(dquot->dq_id);
> +	d->dqb_use_count = cpu_to_le32(OCFS2_DQUOT(dquot)->dq_use_count);
> +	d->dqb_ihardlimit = cpu_to_le64(m->dqb_ihardlimit);
> +	d->dqb_isoftlimit = cpu_to_le64(m->dqb_isoftlimit);
> +	d->dqb_curinodes = cpu_to_le64(m->dqb_curinodes);
> +	d->dqb_bhardlimit = cpu_to_le64(m->dqb_bhardlimit);
> +	d->dqb_bsoftlimit = cpu_to_le64(m->dqb_bsoftlimit);
> +	d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
> +	d->dqb_btime = cpu_to_le64(m->dqb_btime);
> +	d->dqb_itime = cpu_to_le64(m->dqb_itime);
> +	d->dqb_pad1 = d->dqb_pad2 = 0;
> +}
> +
> +static int ocfs2_global_is_id(void *dp, struct dquot *dquot)
> +{
> +	struct ocfs2_global_disk_dqblk *d = dp;
> +	struct ocfs2_mem_dqinfo *oinfo =
> +			sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
> +
> +	if (qtree_entry_unused(&oinfo->dqi_gi, dp))
> +		return 0;
> +	return le32_to_cpu(d->dqb_id) == dquot->dq_id;
> +}
> +
> +struct qtree_fmt_operations ocfs2_global_ops = {
> +	.mem2disk_dqblk = ocfs2_global_mem2diskdqb,
> +	.disk2mem_dqblk = ocfs2_global_disk2memdqb,
> +	.is_id = ocfs2_global_is_id,
> +};
> +
> +
> +struct buffer_head *ocfs2_read_quota_block(struct inode *inode,
> +					   int block, int *err)
> +{
> +	return ocfs2_bread(inode, block, err, 0);
> +}
> +
> +/* Read data from global quotafile - avoid pagecache and such because we cannot
> + * afford acquiring the locks... We use quota cluster lock to serialize
> + * operations. Caller is responsible for acquiring it. */
> +ssize_t ocfs2_quota_read(struct super_block *sb, int type, char *data,
> +			 size_t len, loff_t off)
> +{
> +	struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
> +	struct inode *gqinode = oinfo->dqi_gqinode;
> +	loff_t i_size = i_size_read(gqinode);
> +	int offset = off & (sb->s_blocksize - 1);
> +	sector_t blk = off >> sb->s_blocksize_bits;
> +	int err = 0;
> +	struct buffer_head *bh;
> +	size_t toread, tocopy;
> +
> +	if (off > i_size)
> +		return 0;
> +	if (off + len > i_size)
> +		len = i_size - off;
> +	toread = len;
> +	while (toread > 0) {
> +		tocopy = min((size_t)(sb->s_blocksize - offset), toread);
> +		bh = ocfs2_read_quota_block(gqinode, blk, &err);
> +		if (!bh) {
> +			mlog_errno(err);
> +			return err;
> +		}
> +		memcpy(data, bh->b_data + offset, tocopy);
> +		brelse(bh);
> +		offset = 0;
> +		toread -= tocopy;
> +		data += tocopy;
> +		blk++;
> +	}
> +	return len;
> +}
> +
> +/* Write to quotafile (we know the transaction is already started and has
> + * enough credits) */
> +ssize_t ocfs2_quota_write(struct super_block *sb, int type,
> +			  const char *data, size_t len, loff_t off)
> +{
> +	struct mem_dqinfo *info = sb_dqinfo(sb, type);
> +	struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
> +	struct inode *gqinode = oinfo->dqi_gqinode;
> +	int offset = off & (sb->s_blocksize - 1);
> +	sector_t blk = off >> sb->s_blocksize_bits;
> +	int err = 0;
> +	struct buffer_head *bh;
> +	handle_t *handle = journal_current_handle();
> +	size_t tocopy, towrite = len;
> +
> +	if (!handle) {
> +		mlog(ML_ERROR, "Quota write (off=%llu, len=%llu) cancelled "
> +		     "because transaction was not started.\n",
> +		     (unsigned long long)off, (unsigned long long)len);
> +		return -EIO;
> +	}
> +	mutex_lock_nested(&gqinode->i_mutex, I_MUTEX_QUOTA);
> +	if (gqinode->i_size < off + len) {
> +		down_write(&OCFS2_I(gqinode)->ip_alloc_sem);
> +		err = ocfs2_extend_no_holes(gqinode, off + len, off);
> +		up_write(&OCFS2_I(gqinode)->ip_alloc_sem);
> +		if (err < 0)
> +			goto out;
> +		err = ocfs2_simple_size_update(gqinode,
> +					       oinfo->dqi_gqi_bh,
> +					       off + len);
> +		if (err < 0)
> +			goto out;
> +	}
> +	WARN_ON(off >> sb->s_blocksize_bits != \
> +		(off + len) >> sb->s_blocksize_bits);
> +	WARN_ON(((off + len) & ((1 << sb->s_blocksize_bits) - 1)) >
> +		sb->s_blocksize - OCFS2_QBLK_RESERVED_SPACE);
> +	for (towrite = len; towrite > 0; towrite -= tocopy) {
> +		tocopy = min(towrite, (size_t)(sb->s_blocksize - offset));
> +		bh = ocfs2_read_quota_block(gqinode, blk, &err);
> +		if (!bh) {
> +			mlog_errno(err);
> +			return err;
> +		}
> +		err = ocfs2_journal_access(handle, gqinode, bh,
> +						OCFS2_JOURNAL_ACCESS_WRITE);
> +		if (err < 0) {
> +			brelse(bh);
> +			goto out;
> +		}
> +		lock_buffer(bh);
> +		memcpy(bh->b_data + offset, data, tocopy);
> +		flush_dcache_page(bh->b_page);
> +		unlock_buffer(bh);
> +		err = ocfs2_journal_dirty(handle, bh);
> +		brelse(bh);
> +		if (err < 0)
> +			goto out;
> +		offset = 0;
> +		data += tocopy;
> +		blk++;
> +	}
> +out:
> +	/* Nothing written? */
> +	if (len == towrite) {
> +		mutex_unlock(&gqinode->i_mutex);
> +		mlog_errno(err);
> +		return err;
> +	}
> +	gqinode->i_version++;
> +	ocfs2_mark_inode_dirty(handle, gqinode, oinfo->dqi_gqi_bh);
> +	mutex_unlock(&gqinode->i_mutex);
> +	return len - towrite;
> +}
> +
> +int ocfs2_lock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex)
> +{
> +	int status;
> +	struct buffer_head *bh = NULL;
> +
> +	status = ocfs2_inode_lock(oinfo->dqi_gqinode, &bh, ex);
> +	if (status < 0)
> +		return status;
> +	spin_lock(&dq_data_lock);
> +	if (!oinfo->dqi_gqi_count++)
> +		oinfo->dqi_gqi_bh = bh;
> +	else
> +		WARN_ON(bh != oinfo->dqi_gqi_bh);
> +	spin_unlock(&dq_data_lock);
> +	return 0;
> +}
> +
> +void ocfs2_unlock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex)
> +{
> +	ocfs2_inode_unlock(oinfo->dqi_gqinode, ex);
> +	brelse(oinfo->dqi_gqi_bh);
> +	spin_lock(&dq_data_lock);
> +	if (!--oinfo->dqi_gqi_count)
> +		oinfo->dqi_gqi_bh = NULL;
> +	spin_unlock(&dq_data_lock);
> +}
> +
> +/* Read information header from global quota file */
> +int ocfs2_global_read_info(struct super_block *sb, int type)
> +{
> +	struct inode *gqinode = NULL;
> +	unsigned int ino[MAXQUOTAS] = { USER_QUOTA_SYSTEM_INODE,
> +					GROUP_QUOTA_SYSTEM_INODE };
> +	struct ocfs2_global_disk_dqinfo dinfo;
> +	struct mem_dqinfo *info = sb_dqinfo(sb, type);
> +	struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
> +	int status;
> +
> +	mlog_entry_void();
> +
> +	/* Read global header */
> +	gqinode = ocfs2_get_system_file_inode(OCFS2_SB(sb), ino[type],
> +			OCFS2_INVALID_SLOT);
> +	if (!gqinode) {
> +		mlog(ML_ERROR, "failed to get global quota inode (type=%d)\n",
> +			type);
> +		status = -EINVAL;
> +		goto out_err;
> +	}
> +	oinfo->dqi_gi.dqi_sb = sb;
> +	oinfo->dqi_gi.dqi_type = type;
> +	ocfs2_qinfo_lock_res_init(&oinfo->dqi_gqlock, oinfo);
> +	oinfo->dqi_gi.dqi_entry_size = sizeof(struct ocfs2_global_disk_dqblk);
> +	oinfo->dqi_gi.dqi_ops = &ocfs2_global_ops;
> +	oinfo->dqi_gqi_bh = NULL;
> +	oinfo->dqi_gqi_count = 0;
> +	oinfo->dqi_gqinode = gqinode;
> +	status = ocfs2_lock_global_qf(oinfo, 0);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out_err;
> +	}
> +	status = sb->s_op->quota_read(sb, type, (char *)&dinfo,
> +				      sizeof(struct ocfs2_global_disk_dqinfo),
> +				      OCFS2_GLOBAL_INFO_OFF);
> +	ocfs2_unlock_global_qf(oinfo, 0);
> +	if (status != sizeof(struct ocfs2_global_disk_dqinfo)) {
> +		mlog(ML_ERROR, "Cannot read global quota info (%d).\n",
> +		     status);
> +		if (status >= 0)
> +			status = -EIO;
> +		mlog_errno(status);
> +		goto out_err;
> +	}
> +	info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace);
> +	info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace);
> +	oinfo->dqi_syncms = le32_to_cpu(dinfo.dqi_syncms);
> +	oinfo->dqi_gi.dqi_blocks = le32_to_cpu(dinfo.dqi_blocks);
> +	oinfo->dqi_gi.dqi_free_blk = le32_to_cpu(dinfo.dqi_free_blk);
> +	oinfo->dqi_gi.dqi_free_entry = le32_to_cpu(dinfo.dqi_free_entry);
> +	oinfo->dqi_gi.dqi_blocksize_bits = sb->s_blocksize_bits;
> +	oinfo->dqi_gi.dqi_usable_bs = sb->s_blocksize -
> +						OCFS2_QBLK_RESERVED_SPACE;
> +	oinfo->dqi_gi.dqi_qtree_depth = qtree_depth(&oinfo->dqi_gi);
> +out_err:
> +	mlog_exit(status);
> +	return status;
> +}
> +
> +/* Write information to global quota file. Expects exlusive lock on quota
> + * file inode and quota info */
> +static int __ocfs2_global_write_info(struct super_block *sb, int type)
> +{
> +	struct mem_dqinfo *info = sb_dqinfo(sb, type);
> +	struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
> +	struct ocfs2_global_disk_dqinfo dinfo;
> +	ssize_t size;
> +
> +	spin_lock(&dq_data_lock);
> +	info->dqi_flags &= ~DQF_INFO_DIRTY;
> +	dinfo.dqi_bgrace = cpu_to_le32(info->dqi_bgrace);
> +	dinfo.dqi_igrace = cpu_to_le32(info->dqi_igrace);
> +	spin_unlock(&dq_data_lock);
> +	dinfo.dqi_syncms = cpu_to_le32(oinfo->dqi_syncms);
> +	dinfo.dqi_blocks = cpu_to_le32(oinfo->dqi_gi.dqi_blocks);
> +	dinfo.dqi_free_blk = cpu_to_le32(oinfo->dqi_gi.dqi_free_blk);
> +	dinfo.dqi_free_entry = cpu_to_le32(oinfo->dqi_gi.dqi_free_entry);
> +	size = sb->s_op->quota_write(sb, type, (char *)&dinfo,
> +				     sizeof(struct ocfs2_global_disk_dqinfo),
> +				     OCFS2_GLOBAL_INFO_OFF);
> +	if (size != sizeof(struct ocfs2_global_disk_dqinfo)) {
> +		mlog(ML_ERROR, "Cannot write global quota info structure\n");
> +		if (size >= 0)
> +			size = -EIO;
> +		return size;
> +	}
> +	return 0;
> +}
> +
> +int ocfs2_global_write_info(struct super_block *sb, int type)
> +{
> +	int err;
> +	struct ocfs2_mem_dqinfo *info = sb_dqinfo(sb, type)->dqi_priv;
> +
> +	err = ocfs2_qinfo_lock(info, 1);
> +	if (err < 0)
> +		return err;
> +	err = __ocfs2_global_write_info(sb, type);
> +	ocfs2_qinfo_unlock(info, 1);
> +	return err;
> +}
> +
> +/* Read in information from global quota file and acquire a reference to it.
> + * dquot_acquire() has already started the transaction and locked quota file */
> +int ocfs2_global_read_dquot(struct dquot *dquot)
> +{
> +	int err, err2, ex = 0;
> +	struct ocfs2_mem_dqinfo *info =
> +			sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
> +
> +	err = ocfs2_qinfo_lock(info, 0);
> +	if (err < 0)
> +		goto out;
> +	err = qtree_read_dquot(&info->dqi_gi, dquot);
> +	if (err < 0)
> +		goto out_qlock;
> +	OCFS2_DQUOT(dquot)->dq_use_count++;
> +	OCFS2_DQUOT(dquot)->dq_origspace = dquot->dq_dqb.dqb_curspace;
> +	OCFS2_DQUOT(dquot)->dq_originodes = dquot->dq_dqb.dqb_curinodes;
> +	if (!dquot->dq_off) {	/* No real quota entry? */
> +		/* Upgrade to exclusive lock for allocation */
> +		err = ocfs2_qinfo_lock(info, 1);
> +		if (err < 0)
> +			goto out_qlock;
> +		ex = 1;
> +	}
> +	err = qtree_write_dquot(&info->dqi_gi, dquot);
> +	if (ex && info_dirty(sb_dqinfo(dquot->dq_sb, dquot->dq_type))) {
> +		err2 = __ocfs2_global_write_info(dquot->dq_sb, dquot->dq_type);
> +		if (!err)
> +			err = err2;
> +	}
> +out_qlock:
> +	if (ex)
> +		ocfs2_qinfo_unlock(info, 1);
> +	ocfs2_qinfo_unlock(info, 0);
> +out:
> +	if (err < 0)
> +		mlog_errno(err);
> +	return err;
> +}
> +
> +/* Sync local information about quota modifications with global quota file.
> + * Caller must have started the transaction and obtained exclusive lock for
> + * global quota file inode */
> +int __ocfs2_sync_dquot(struct dquot *dquot, int freeing)
> +{
> +	int err, err2;
> +	struct super_block *sb = dquot->dq_sb;
> +	int type = dquot->dq_type;
> +	struct ocfs2_mem_dqinfo *info = sb_dqinfo(sb, type)->dqi_priv;
> +	struct ocfs2_global_disk_dqblk dqblk;
> +	s64 spacechange, inodechange;
> +	time_t olditime, oldbtime;
> +
> +	err = sb->s_op->quota_read(sb, type, (char *)&dqblk,
> +				   sizeof(struct ocfs2_global_disk_dqblk),
> +				   dquot->dq_off);
> +	if (err != sizeof(struct ocfs2_global_disk_dqblk)) {
> +		if (err >= 0) {
> +			mlog(ML_ERROR, "Short read from global quota file "
> +				       "(%u read)\n", err);
> +			err = -EIO;
> +		}
> +		goto out;
> +	}
> +
> +	/* Update space and inode usage. Get also other information from
> +	 * global quota file so that we don't overwrite any changes there.
> +	 * We are */
> +	spin_lock(&dq_data_lock);
> +	spacechange = dquot->dq_dqb.dqb_curspace -
> +					OCFS2_DQUOT(dquot)->dq_origspace;
> +	inodechange = dquot->dq_dqb.dqb_curinodes -
> +					OCFS2_DQUOT(dquot)->dq_originodes;
> +	olditime = dquot->dq_dqb.dqb_itime;
> +	oldbtime = dquot->dq_dqb.dqb_btime;
> +	ocfs2_global_disk2memdqb(dquot, &dqblk);
> +	mlog(0, "Syncing global dquot %d space %lld+%lld, inodes %lld+%lld\n",
> +	     dquot->dq_id, dquot->dq_dqb.dqb_curspace, spacechange,
> +	     dquot->dq_dqb.dqb_curinodes, inodechange);
> +	if (!test_bit(DQ_LASTSET_B + QIF_SPACE_B, &dquot->dq_flags))
> +		dquot->dq_dqb.dqb_curspace += spacechange;
> +	if (!test_bit(DQ_LASTSET_B + QIF_INODES_B, &dquot->dq_flags))
> +		dquot->dq_dqb.dqb_curinodes += inodechange;
> +	/* Now merge grace time changes... */
> +	if (!test_bit(DQ_LASTSET_B + QIF_BTIME_B, &dquot->dq_flags) &&
> +	    oldbtime > 0) {
> +		if (dquot->dq_dqb.dqb_btime > 0)
> +			dquot->dq_dqb.dqb_btime =
> +					min(dquot->dq_dqb.dqb_btime, oldbtime);
> +		else
> +			dquot->dq_dqb.dqb_btime = oldbtime;
> +	}
> +	if (!test_bit(DQ_LASTSET_B + QIF_ITIME_B, &dquot->dq_flags) &&
> +	    olditime > 0) {
> +		if (dquot->dq_dqb.dqb_itime > 0)
> +			dquot->dq_dqb.dqb_itime =
> +					min(dquot->dq_dqb.dqb_itime, olditime);
> +		else
> +			dquot->dq_dqb.dqb_itime = olditime;
> +	}
> +	/* All information is properly updated, clear the flags */
> +	__clear_bit(DQ_LASTSET_B + QIF_SPACE_B, &dquot->dq_flags);
> +	__clear_bit(DQ_LASTSET_B + QIF_INODES_B, &dquot->dq_flags);
> +	__clear_bit(DQ_LASTSET_B + QIF_BLIMITS_B, &dquot->dq_flags);
> +	__clear_bit(DQ_LASTSET_B + QIF_ILIMITS_B, &dquot->dq_flags);
> +	__clear_bit(DQ_LASTSET_B + QIF_BTIME_B, &dquot->dq_flags);
> +	__clear_bit(DQ_LASTSET_B + QIF_ITIME_B, &dquot->dq_flags);
> +	OCFS2_DQUOT(dquot)->dq_origspace = dquot->dq_dqb.dqb_curspace;
> +	OCFS2_DQUOT(dquot)->dq_originodes = dquot->dq_dqb.dqb_curinodes;
> +	spin_unlock(&dq_data_lock);
> +	err = ocfs2_qinfo_lock(info, freeing);
> +	if (err < 0) {
> +		mlog(ML_ERROR, "Failed to lock quota info, loosing quota write"
> +			       " (type=%d, id=%u)\n", dquot->dq_type,
> +			       (unsigned)dquot->dq_id);
> +		goto out;
> +	}
> +	if (freeing)
> +		OCFS2_DQUOT(dquot)->dq_use_count--;
> +	err = qtree_write_dquot(&info->dqi_gi, dquot);
> +	if (err < 0)
> +		goto out_qlock;
> +	if (freeing && !OCFS2_DQUOT(dquot)->dq_use_count) {
> +		err = qtree_release_dquot(&info->dqi_gi, dquot);
> +		if (info_dirty(sb_dqinfo(sb, type))) {
> +			err2 = __ocfs2_global_write_info(sb, type);
> +			if (!err)
> +				err = err2;
> +		}
> +	}
> +out_qlock:
> +	ocfs2_qinfo_unlock(info, freeing);
> +out:
> +	if (err < 0)
> +		mlog_errno(err);
> +	return err;
> +}
> +
> +/*
> + *  Wrappers for generic quota functions
> + */
> +
> +static int ocfs2_write_dquot(struct dquot *dquot)
> +{
> +	handle_t *handle;
> +	struct ocfs2_super *osb = OCFS2_SB(dquot->dq_sb);
> +	int status = 0;
> +
> +	mlog_entry("id=%u, type=%d", dquot->dq_id, dquot->dq_type);
> +
> +	handle = ocfs2_start_trans(osb, OCFS2_QWRITE_CREDITS);
> +	if (IS_ERR(handle)) {
> +		status = PTR_ERR(handle);
> +		mlog_errno(status);
> +		goto out;
> +	}
> +	status = dquot_commit(dquot);
> +	ocfs2_commit_trans(osb, handle);
> +out:
> +	mlog_exit(status);
> +	return status;
> +}
> +
> +int ocfs2_calc_qdel_credits(struct super_block *sb, int type)
> +{
> +	struct ocfs2_mem_dqinfo *oinfo;
> +	int features[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
> +				    OCFS2_FEATURE_RO_COMPAT_GRPQUOTA };
> +
> +	if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, features[type]))
> +		return 0;
> +
> +	oinfo = sb_dqinfo(sb, type)->dqi_priv;
> +	/* We modify tree, leaf block, global info, local chunk header,
> +	 * global and local inode */
> +	return oinfo->dqi_gi.dqi_qtree_depth + 2 + 1 +
> +	       2 * OCFS2_INODE_UPDATE_CREDITS;
> +}
> +
> +static int ocfs2_release_dquot(struct dquot *dquot)
> +{
> +	handle_t *handle;
> +	struct ocfs2_mem_dqinfo *oinfo =
> +			sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
> +	struct ocfs2_super *osb = OCFS2_SB(dquot->dq_sb);
> +	int status = 0;
> +
> +	mlog_entry("id=%u, type=%d", dquot->dq_id, dquot->dq_type);
> +
> +	status = ocfs2_lock_global_qf(oinfo, 1);
> +	if (status < 0)
> +		goto out;
> +	handle = ocfs2_start_trans(osb,
> +		ocfs2_calc_qdel_credits(dquot->dq_sb, dquot->dq_type));
> +	if (IS_ERR(handle)) {
> +		status = PTR_ERR(handle);
> +		mlog_errno(status);
> +		goto out_ilock;
> +	}
> +	status = dquot_release(dquot);
> +	ocfs2_commit_trans(osb, handle);
> +out_ilock:
> +	ocfs2_unlock_global_qf(oinfo, 1);
> +out:
> +	mlog_exit(status);
> +	return status;
> +}
> +
> +int ocfs2_calc_qinit_credits(struct super_block *sb, int type)
> +{
> +	struct ocfs2_mem_dqinfo *oinfo;
> +	int features[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
> +				    OCFS2_FEATURE_RO_COMPAT_GRPQUOTA };
> +	struct ocfs2_dinode *lfe, *gfe;
> +
> +	if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, features[type]))
> +		return 0;
> +
> +	oinfo = sb_dqinfo(sb, type)->dqi_priv;
> +	gfe = (struct ocfs2_dinode *)oinfo->dqi_gqi_bh->b_data;
> +	lfe = (struct ocfs2_dinode *)oinfo->dqi_lqi_bh->b_data;
> +	/* We can extend local file + global file. In local file we
> +	 * can modify info, chunk header block and dquot block. In
> +	 * global file we can modify info, tree and leaf block */
> +	return ocfs2_calc_extend_credits(sb, &lfe->id2.i_list, 0) +
> +	       ocfs2_calc_extend_credits(sb, &gfe->id2.i_list, 0) +
> +	       3 + oinfo->dqi_gi.dqi_qtree_depth + 2;
> +}
> +
> +static int ocfs2_acquire_dquot(struct dquot *dquot)
> +{
> +	handle_t *handle;
> +	struct ocfs2_mem_dqinfo *oinfo =
> +			sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
> +	struct ocfs2_super *osb = OCFS2_SB(dquot->dq_sb);
> +	int status = 0;
> +
> +	mlog_entry("id=%u, type=%d", dquot->dq_id, dquot->dq_type);
> +	/* We need an exclusive lock, because we're going to update use count
> +	 * and instantiate possibly new dquot structure */
> +	status = ocfs2_lock_global_qf(oinfo, 1);
> +	if (status < 0)
> +		goto out;
> +	handle = ocfs2_start_trans(osb,
> +		ocfs2_calc_qinit_credits(dquot->dq_sb, dquot->dq_type));
> +	if (IS_ERR(handle)) {
> +		status = PTR_ERR(handle);
> +		mlog_errno(status);
> +		goto out_ilock;
> +	}
> +	status = dquot_acquire(dquot);
> +	ocfs2_commit_trans(osb, handle);
> +out_ilock:
> +	ocfs2_unlock_global_qf(oinfo, 1);
> +out:
> +	mlog_exit(status);
> +	return status;
> +}
> +
> +static int ocfs2_mark_dquot_dirty(struct dquot *dquot)
> +{
> +	unsigned long mask = (1 << (DQ_LASTSET_B + QIF_ILIMITS_B)) |
> +			     (1 << (DQ_LASTSET_B + QIF_BLIMITS_B)) |
> +			     (1 << (DQ_LASTSET_B + QIF_INODES_B)) |
> +			     (1 << (DQ_LASTSET_B + QIF_SPACE_B)) |
> +			     (1 << (DQ_LASTSET_B + QIF_BTIME_B)) |
> +			     (1 << (DQ_LASTSET_B + QIF_ITIME_B));
> +	int sync = 0;
> +	int status;
> +	struct super_block *sb = dquot->dq_sb;
> +	int type = dquot->dq_type;
> +	struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
> +	handle_t *handle;
> +	struct ocfs2_super *osb = OCFS2_SB(sb);
> +
> +	mlog_entry("id=%u, type=%d", dquot->dq_id, type);
> +	dquot_mark_dquot_dirty(dquot);
> +
> +	/* In case user set some limits, sync dquot immediately to global
> +	 * quota file so that information propagates quicker */
> +	spin_lock(&dq_data_lock);
> +	if (dquot->dq_flags & mask)
> +		sync = 1;
> +	spin_unlock(&dq_data_lock);
> +	if (!sync) {
> +		status = ocfs2_write_dquot(dquot);
> +		goto out;
> +	}
> +	status = ocfs2_lock_global_qf(oinfo, 1);
> +	if (status < 0)
> +		goto out;
> +	handle = ocfs2_start_trans(osb, OCFS2_QSYNC_CREDITS);
> +	if (IS_ERR(handle)) {
> +		status = PTR_ERR(handle);
> +		mlog_errno(status);
> +		goto out_ilock;
> +	}
> +	status = ocfs2_sync_dquot(dquot);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out_trans;
> +	}
> +	/* Now write updated local dquot structure */
> +	status = dquot_commit(dquot);
> +out_trans:
> +	ocfs2_commit_trans(osb, handle);
> +out_ilock:
> +	ocfs2_unlock_global_qf(oinfo, 1);
> +out:
> +	mlog_exit(status);
> +	return status;
> +}
> +
> +/* This should happen only after set_dqinfo(). */
> +static int ocfs2_write_info(struct super_block *sb, int type)
> +{
> +	handle_t *handle;
> +	int status = 0;
> +	struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
> +
> +	mlog_entry_void();
> +
> +	status = ocfs2_lock_global_qf(oinfo, 1);
> +	if (status < 0)
> +		goto out;
> +	handle = ocfs2_start_trans(OCFS2_SB(sb), OCFS2_QINFO_WRITE_CREDITS);
> +	if (IS_ERR(handle)) {
> +		status = PTR_ERR(handle);
> +		mlog_errno(status);
> +		goto out_ilock;
> +	}
> +	status = dquot_commit_info(sb, type);
> +	ocfs2_commit_trans(OCFS2_SB(sb), handle);
> +out_ilock:
> +	ocfs2_unlock_global_qf(oinfo, 1);
> +out:
> +	mlog_exit(status);
> +	return status;
> +}
> +
> +/* This is difficult. We have to lock quota inode and start transaction
> + * in this function but we don't want to take the penalty of exlusive
> + * quota file lock when we are just going to use cached structures. So
> + * we just take read lock check whether we have dquot cached and if so,
> + * we don't have to take the write lock... */
> +static int ocfs2_dquot_initialize(struct inode *inode, int type)
> +{
> +	handle_t *handle = NULL;
> +	int status = 0;
> +	struct super_block *sb = inode->i_sb;
> +	struct ocfs2_mem_dqinfo *oinfo;
> +	int exclusive = 0;
> +	int cnt;
> +	qid_t id;
> +
> +	mlog_entry_void();
> +
> +	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
> +		if (type != -1 && cnt != type)
> +			continue;
> +		oinfo = sb_dqinfo(sb, cnt)->dqi_priv;
> +		status = ocfs2_lock_global_qf(oinfo, 0);
> +		if (status < 0)
> +			goto out;
> +		/* This is just a performance optimization not a reliable test.
> +		 * Since we hold an inode lock, noone can actually release
> +		 * the structure until we are finished with initialization. */
> +		if (inode->i_dquot[cnt] != NODQUOT) {
> +			ocfs2_unlock_global_qf(oinfo, 0);
> +			continue;
> +		}
> +		/* When we have inode lock, we know that no dquot_release() can
> +		 * run and thus we can safely check whether we need to
> +		 * read+modify global file to get quota information or whether
> +		 * our node already has it. */
> +		if (cnt == USRQUOTA)
> +			id = inode->i_uid;
> +		else if (cnt == GRPQUOTA)
> +			id = inode->i_gid;
> +		else
> +			BUG();
> +		/* Obtain exclusion from quota off... */
> +		down_write(&sb_dqopt(sb)->dqptr_sem);
> +		exclusive = !dquot_is_cached(sb, id, cnt);
> +		up_write(&sb_dqopt(sb)->dqptr_sem);
> +		if (exclusive) {
> +			status = ocfs2_lock_global_qf(oinfo, 1);
> +			if (status < 0) {
> +				exclusive = 0;
> +				mlog_errno(status);
> +				goto out_ilock;
> +			}
> +			handle = ocfs2_start_trans(OCFS2_SB(sb),
> +					ocfs2_calc_qinit_credits(sb, cnt));
> +			if (IS_ERR(handle)) {
> +				status = PTR_ERR(handle);
> +				mlog_errno(status);
> +				goto out_ilock;
> +			}
> +		}
> +		dquot_initialize(inode, cnt);
> +		if (exclusive) {
> +			ocfs2_commit_trans(OCFS2_SB(sb), handle);
> +			ocfs2_unlock_global_qf(oinfo, 1);
> +		}
> +		ocfs2_unlock_global_qf(oinfo, 0);
> +	}
> +	mlog_exit(0);
> +	return 0;
> +out_ilock:
> +	if (exclusive)
> +		ocfs2_unlock_global_qf(oinfo, 1);
> +	ocfs2_unlock_global_qf(oinfo, 0);
> +out:
> +	mlog_exit(status);
> +	return status;
> +}
> +
> +static int ocfs2_dquot_drop_slow(struct inode *inode)
> +{
> +	int status;
> +	int cnt;
> +	int got_lock[MAXQUOTAS] = {0, 0};
> +	handle_t *handle;
> +	struct super_block *sb = inode->i_sb;
> +	struct ocfs2_mem_dqinfo *oinfo;
> +
> +	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
> +		oinfo = sb_dqinfo(sb, cnt)->dqi_priv;
> +		status = ocfs2_lock_global_qf(oinfo, 1);
> +		if (status < 0)
> +			goto out;
> +		got_lock[cnt] = 1;
> +	}
> +	handle = ocfs2_start_trans(OCFS2_SB(sb),
> +			ocfs2_calc_qinit_credits(sb, USRQUOTA) +
> +			ocfs2_calc_qinit_credits(sb, GRPQUOTA));
> +	if (IS_ERR(handle)) {
> +		status = PTR_ERR(handle);
> +		mlog_errno(status);
> +				goto out;
> +	}
> +	dquot_drop(inode);
> +	ocfs2_commit_trans(OCFS2_SB(sb), handle);
> +out:
> +	for (cnt = 0; cnt < MAXQUOTAS; cnt++)
> +		if (got_lock[cnt]) {
> +			oinfo = sb_dqinfo(sb, cnt)->dqi_priv;
> +			ocfs2_unlock_global_qf(oinfo, 1);
> +		}
> +	return status;
> +}
> +
> +/* See the comment before ocfs2_dquot_initialize. */
> +static int ocfs2_dquot_drop(struct inode *inode)
> +{
> +	int status = 0;
> +	struct super_block *sb = inode->i_sb;
> +	struct ocfs2_mem_dqinfo *oinfo;
> +	int exclusive = 0;
> +	int cnt;
> +	int got_lock[MAXQUOTAS] = {0, 0};
> +
> +	mlog_entry_void();
> +	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
> +		oinfo = sb_dqinfo(sb, cnt)->dqi_priv;
> +		status = ocfs2_lock_global_qf(oinfo, 0);
> +		if (status < 0)
> +			goto out;
> +		got_lock[cnt] = 1;
> +	}
> +	/* Lock against anyone releasing references so that when when we check
> +	 * we know we are not going to be last ones to release dquot */
> +	down_write(&sb_dqopt(sb)->dqptr_sem);
> +	/* Urgh, this is a terrible hack :( */
> +	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
> +		if (inode->i_dquot[cnt] != NODQUOT &&
> +		    atomic_read(&inode->i_dquot[cnt]->dq_count) > 1) {
> +			exclusive = 1;
> +			break;
> +		}
> +	}
> +	if (!exclusive)
> +		dquot_drop_locked(inode);
> +	up_write(&sb_dqopt(sb)->dqptr_sem);
> +out:
> +	for (cnt = 0; cnt < MAXQUOTAS; cnt++)
> +		if (got_lock[cnt]) {
> +			oinfo = sb_dqinfo(sb, cnt)->dqi_priv;
> +			ocfs2_unlock_global_qf(oinfo, 0);
> +		}
> +	/* In case we bailed out because we had to do expensive locking
> +	 * do it now... */
> +	if (exclusive)
> +		status = ocfs2_dquot_drop_slow(inode);
> +	mlog_exit(status);
> +	return status;
> +}
> +
> +static struct dquot *ocfs2_alloc_dquot(struct super_block *sb, int type)
> +{
> +	struct ocfs2_dquot *dquot =
> +				kmem_cache_zalloc(ocfs2_dquot_cachep, GFP_NOFS);
> +
> +	if (!dquot)
> +		return NULL;
> +	return &dquot->dq_dquot;
> +}
> +
> +static void ocfs2_destroy_dquot(struct dquot *dquot)
> +{
> +	kmem_cache_free(ocfs2_dquot_cachep, dquot);
> +}
> +
> +struct dquot_operations ocfs2_quota_operations = {
> +	.initialize	= ocfs2_dquot_initialize,
> +	.drop		= ocfs2_dquot_drop,
> +	.alloc_space	= dquot_alloc_space,
> +	.alloc_inode	= dquot_alloc_inode,
> +	.free_space	= dquot_free_space,
> +	.free_inode	= dquot_free_inode,
> +	.transfer	= dquot_transfer,
> +	.write_dquot	= ocfs2_write_dquot,
> +	.acquire_dquot	= ocfs2_acquire_dquot,
> +	.release_dquot	= ocfs2_release_dquot,
> +	.mark_dirty	= ocfs2_mark_dquot_dirty,
> +	.write_info	= ocfs2_write_info,
> +	.alloc_dquot	= ocfs2_alloc_dquot,
> +	.destroy_dquot	= ocfs2_destroy_dquot,
> +};
> diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c
> new file mode 100644
> index 0000000..55c3f2f
> --- /dev/null
> +++ b/fs/ocfs2/quota_local.c
> @@ -0,0 +1,833 @@
> +/*
> + *  Implementation of operations over local quota file
> + */
> +
> +#include <linux/fs.h>
> +#include <linux/quota.h>
> +#include <linux/quotaops.h>
> +#include <linux/module.h>
> +
> +#define MLOG_MASK_PREFIX ML_QUOTA
> +#include <cluster/masklog.h>
> +
> +#include "ocfs2_fs.h"
> +#include "ocfs2.h"
> +#include "inode.h"
> +#include "alloc.h"
> +#include "file.h"
> +#include "buffer_head_io.h"
> +#include "journal.h"
> +#include "sysfile.h"
> +#include "dlmglue.h"
> +#include "quota.h"
> +
> +/* Number of local quota structures per block */
> +static inline unsigned int ol_quota_entries_per_block(struct super_block *sb)
> +{
> +	return ((sb->s_blocksize - OCFS2_QBLK_RESERVED_SPACE) /
> +		sizeof(struct ocfs2_local_disk_dqblk));
> +}
> +
> +/* Number of blocks with entries in one chunk */
> +static inline unsigned int ol_chunk_blocks(struct super_block *sb)
> +{
> +	return ((sb->s_blocksize - sizeof(struct ocfs2_local_disk_chunk) -
> +		 OCFS2_QBLK_RESERVED_SPACE) << 3) /
> +	       ol_quota_entries_per_block(sb);
> +}
> +
> +/* Number of entries in a chunk bitmap */
> +static unsigned int ol_chunk_entries(struct super_block *sb)
> +{
> +	return ol_chunk_blocks(sb) * ol_quota_entries_per_block(sb);
> +}
> +
> +/* Offset of the chunk in quota file */
> +static unsigned int ol_quota_chunk_block(struct super_block *sb, int c)
> +{
> +	/* 1 block for local quota file info, 1 block per chunk for chunk info */
> +	return 1 + (ol_chunk_blocks(sb) + 1) * c;
> +}
> +
> +/* Offset of the dquot structure in the quota file */
> +static loff_t ol_dqblk_off(struct super_block *sb, int c, int off)
> +{
> +	int epb = ol_quota_entries_per_block(sb);
> +
> +	return ((ol_quota_chunk_block(sb, c) + 1 + off / epb)
> +		<< sb->s_blocksize_bits) +
> +		(off % epb) * sizeof(struct ocfs2_local_disk_dqblk);
> +}
> +
> +/* Compute block number from given offset */
> +static inline unsigned int ol_dqblk_file_block(struct super_block *sb, loff_t off)
> +{
> +	return off >> sb->s_blocksize_bits;
> +}
> +
> +static inline unsigned int ol_dqblk_block_offset(struct super_block *sb, loff_t off)
> +{
> +	return off & ((1 << sb->s_blocksize_bits) - 1);
> +}
> +
> +/* Compute offset in the chunk of a structure with the given offset */
> +static int ol_dqblk_chunk_off(struct super_block *sb, int c, loff_t off)
> +{
> +	int epb = ol_quota_entries_per_block(sb);
> +
> +	return ((off >> sb->s_blocksize_bits) -
> +			ol_quota_chunk_block(sb, c) - 1) * epb
> +	       + ((unsigned int)(off & ((1 << sb->s_blocksize_bits) - 1))) /
> +		 sizeof(struct ocfs2_local_disk_dqblk);
> +}
> +
> +/* Write bufferhead into the fs */
> +static int ocfs2_modify_bh(struct inode *inode, struct buffer_head *bh,
> +		void (*modify)(struct buffer_head *, void *), void *private)
> +{
> +	struct super_block *sb = inode->i_sb;
> +	handle_t *handle;
> +	int status;
> +
> +	handle = ocfs2_start_trans(OCFS2_SB(sb), 1);
> +	if (IS_ERR(handle)) {
> +		status = PTR_ERR(handle);
> +		mlog_errno(status);
> +		return status;
> +	}
> +	status = ocfs2_journal_access(handle, inode, bh,
> +				      OCFS2_JOURNAL_ACCESS_WRITE);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		ocfs2_commit_trans(OCFS2_SB(sb), handle);
> +		return status;
> +	}
> +	lock_buffer(bh);
> +	modify(bh, private);
> +	unlock_buffer(bh);
> +	status = ocfs2_journal_dirty(handle, bh);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		ocfs2_commit_trans(OCFS2_SB(sb), handle);
> +		return status;
> +	}
> +	status = ocfs2_commit_trans(OCFS2_SB(sb), handle);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		return status;
> +	}
> +	return 0;
> +}
> +
> +/* Check whether we understand format of quota files */
> +static int ocfs2_local_check_quota_file(struct super_block *sb, int type)
> +{
> +	unsigned int lmagics[MAXQUOTAS] = OCFS2_LOCAL_QMAGICS;
> +	unsigned int lversions[MAXQUOTAS] = OCFS2_LOCAL_QVERSIONS;
> +	unsigned int gmagics[MAXQUOTAS] = OCFS2_GLOBAL_QMAGICS;
> +	unsigned int gversions[MAXQUOTAS] = OCFS2_GLOBAL_QVERSIONS;
> +	unsigned int ino[MAXQUOTAS] = { USER_QUOTA_SYSTEM_INODE,
> +					GROUP_QUOTA_SYSTEM_INODE };
> +	struct buffer_head *bh;
> +	struct inode *linode = sb_dqopt(sb)->files[type];
> +	struct inode *ginode = NULL;
> +	struct ocfs2_disk_dqheader *dqhead;
> +	int status, ret = 0;
> +
> +	/* First check whether we understand local quota file */
> +	bh = ocfs2_read_quota_block(linode, 0, &status);
> +	if (!bh) {
> +		mlog_errno(status);
> +		mlog(ML_ERROR, "failed to read quota file header (type=%d)\n",
> +			type);
> +		goto out_err;
> +	}
> +	dqhead = (struct ocfs2_disk_dqheader *)(bh->b_data);
> +	if (le32_to_cpu(dqhead->dqh_magic) != lmagics[type]) {
> +		mlog(ML_ERROR, "quota file magic does not match (%u != %u),"
> +			" type=%d\n", le32_to_cpu(dqhead->dqh_magic),
> +			lmagics[type], type);
> +		goto out_err;
> +	}
> +	if (le32_to_cpu(dqhead->dqh_version) != lversions[type]) {
> +		mlog(ML_ERROR, "quota file version does not match (%u != %u),"
> +			" type=%d\n", le32_to_cpu(dqhead->dqh_version),
> +			lversions[type], type);
> +		goto out_err;
> +	}
> +	brelse(bh);
> +	bh = NULL;
> +
> +	/* Next check whether we understand global quota file */
> +	ginode = ocfs2_get_system_file_inode(OCFS2_SB(sb), ino[type],
> +						OCFS2_INVALID_SLOT);
> +	if (!ginode) {
> +		mlog(ML_ERROR, "cannot get global quota file inode "
> +				"(type=%d)\n", type);
> +		goto out_err;
> +	}
> +	/* Since the header is read only, we don't care about locking */
> +	bh = ocfs2_read_quota_block(ginode, 0, &status);
> +	if (!bh) {
> +		mlog_errno(status);
> +		mlog(ML_ERROR, "failed to read global quota file header "
> +				"(type=%d)\n", type);
> +		goto out_err;
> +	}
> +	dqhead = (struct ocfs2_disk_dqheader *)(bh->b_data);
> +	if (le32_to_cpu(dqhead->dqh_magic) != gmagics[type]) {
> +		mlog(ML_ERROR, "global quota file magic does not match "
> +			"(%u != %u), type=%d\n",
> +			le32_to_cpu(dqhead->dqh_magic), gmagics[type], type);
> +		goto out_err;
> +	}
> +	if (le32_to_cpu(dqhead->dqh_version) != gversions[type]) {
> +		mlog(ML_ERROR, "global quota file version does not match "
> +			"(%u != %u), type=%d\n",
> +			le32_to_cpu(dqhead->dqh_version), gversions[type],
> +			type);
> +		goto out_err;
> +	}
> +
> +	ret = 1;
> +out_err:
> +	brelse(bh);
> +	iput(ginode);
> +	return ret;
> +}
> +
> +/* Release given list of quota file chunks */
> +static void ocfs2_release_local_quota_bitmaps(struct list_head *head)
> +{
> +	struct ocfs2_quota_chunk *pos, *next;
> +
> +	list_for_each_entry_safe(pos, next, head, qc_chunk) {
> +		list_del(&pos->qc_chunk);
> +		brelse(pos->qc_headerbh);
> +		kmem_cache_free(ocfs2_qf_chunk_cachep, pos);
> +	}
> +}
> +
> +/* Load quota bitmaps into memory */
> +static int ocfs2_load_local_quota_bitmaps(struct inode *inode,
> +			struct ocfs2_local_disk_dqinfo *ldinfo,
> +			struct list_head *head)
> +{
> +	struct ocfs2_quota_chunk *newchunk;
> +	int i, status;
> +
> +	INIT_LIST_HEAD(head);
> +	for (i = 0; i < le32_to_cpu(ldinfo->dqi_chunks); i++) {
> +		newchunk = kmem_cache_alloc(ocfs2_qf_chunk_cachep, GFP_NOFS);
> +		if (!newchunk) {
> +			ocfs2_release_local_quota_bitmaps(head);
> +			return -ENOMEM;
> +		}
> +		newchunk->qc_num = i;
> +		newchunk->qc_headerbh = ocfs2_read_quota_block(inode,
> +				ol_quota_chunk_block(inode->i_sb, i),
> +				&status);
> +		if (!newchunk->qc_headerbh) {
> +			mlog_errno(status);
> +			kmem_cache_free(ocfs2_qf_chunk_cachep, newchunk);
> +			ocfs2_release_local_quota_bitmaps(head);
> +			return status;
> +		}
> +		list_add_tail(&newchunk->qc_chunk, head);
> +	}
> +	return 0;
> +}
> +
> +static void olq_update_info(struct buffer_head *bh, void *private)
> +{
> +	struct mem_dqinfo *info = private;
> +	struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
> +	struct ocfs2_local_disk_dqinfo *ldinfo;
> +
> +	ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data +
> +						OCFS2_LOCAL_INFO_OFF);
> +	spin_lock(&dq_data_lock);
> +	ldinfo->dqi_flags = cpu_to_le32(info->dqi_flags & DQF_MASK);
> +	ldinfo->dqi_chunks = cpu_to_le32(oinfo->dqi_chunks);
> +	ldinfo->dqi_blocks = cpu_to_le32(oinfo->dqi_blocks);
> +	spin_unlock(&dq_data_lock);
> +}
> +
> +/* Read information header from quota file */
> +static int ocfs2_local_read_info(struct super_block *sb, int type)
> +{
> +	struct ocfs2_local_disk_dqinfo *ldinfo;
> +	struct mem_dqinfo *info = sb_dqinfo(sb, type);
> +	struct ocfs2_mem_dqinfo *oinfo;
> +	struct inode *lqinode = sb_dqopt(sb)->files[type];
> +	int status;
> +	struct buffer_head *bh = NULL;
> +	int locked = 0;
> +
> +	info->dqi_maxblimit = 0x7fffffffffffffffLL;
> +	info->dqi_maxilimit = 0x7fffffffffffffffLL;
> +	oinfo = kmalloc(sizeof(struct ocfs2_mem_dqinfo), GFP_NOFS);
> +	if (!oinfo) {
> +		mlog(ML_ERROR, "failed to allocate memory for ocfs2 quota"
> +			       " info.");
> +		goto out_err;
> +	}
> +	info->dqi_priv = oinfo;
> +	oinfo->dqi_type = type;
> +	INIT_LIST_HEAD(&oinfo->dqi_chunk);
> +	oinfo->dqi_lqi_bh = NULL;
> +	oinfo->dqi_ibh = NULL;
> +
> +	status = ocfs2_global_read_info(sb, type);
> +	if (status < 0)
> +		goto out_err;
> +
> +	status = ocfs2_inode_lock(lqinode, &oinfo->dqi_lqi_bh, 1);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out_err;
> +	}
> +	locked = 1;
> +
> +	/* Now read local header */
> +	bh = ocfs2_read_quota_block(lqinode, 0, &status);
> +	if (!bh) {
> +		mlog_errno(status);
> +		mlog(ML_ERROR, "failed to read quota file info header "
> +			"(type=%d)\n", type);
> +		goto out_err;
> +	}
> +	ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data +
> +						OCFS2_LOCAL_INFO_OFF);
> +	info->dqi_flags = le32_to_cpu(ldinfo->dqi_flags);
> +	oinfo->dqi_chunks = le32_to_cpu(ldinfo->dqi_chunks);
> +	oinfo->dqi_blocks = le32_to_cpu(ldinfo->dqi_blocks);
> +	oinfo->dqi_ibh = bh;
> +
> +	/* We crashed when using local quota file? */
> +	if (!(info->dqi_flags & OLQF_CLEAN))
> +		goto out_err;	/* So far we just bail out. Later we should resync here */
> +
> +	status = ocfs2_load_local_quota_bitmaps(sb_dqopt(sb)->files[type],
> +						ldinfo,
> +						&oinfo->dqi_chunk);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out_err;
> +	}
> +
> +	/* Now mark quota file as used */
> +	info->dqi_flags &= ~OLQF_CLEAN;
> +	status = ocfs2_modify_bh(lqinode, bh, olq_update_info, info);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out_err;
> +	}
> +
> +	return 0;
> +out_err:
> +	if (oinfo) {
> +		iput(oinfo->dqi_gqinode);
> +		ocfs2_simple_drop_lockres(OCFS2_SB(sb), &oinfo->dqi_gqlock);
> +		ocfs2_lock_res_free(&oinfo->dqi_gqlock);
> +		brelse(oinfo->dqi_lqi_bh);
> +		if (locked)
> +			ocfs2_inode_unlock(lqinode, 1);
> +		ocfs2_release_local_quota_bitmaps(&oinfo->dqi_chunk);
> +		kfree(oinfo);
> +	}
> +	brelse(bh);
> +	return -1;
> +}
> +
> +/* Write local info to quota file */
> +static int ocfs2_local_write_info(struct super_block *sb, int type)
> +{
> +	struct mem_dqinfo *info = sb_dqinfo(sb, type);
> +	struct buffer_head *bh = ((struct ocfs2_mem_dqinfo *)info->dqi_priv)
> +						->dqi_ibh;
> +	int status;
> +
> +	status = ocfs2_modify_bh(sb_dqopt(sb)->files[type], bh, olq_update_info,
> +				 info);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
> +/* Release info from memory */
> +static int ocfs2_local_free_info(struct super_block *sb, int type)
> +{
> +	struct mem_dqinfo *info = sb_dqinfo(sb, type);
> +	struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
> +	struct ocfs2_quota_chunk *chunk;
> +	struct ocfs2_local_disk_chunk *dchunk;
> +	int mark_clean = 1, len;
> +	int status;
> +
> +	iput(oinfo->dqi_gqinode);
> +	ocfs2_simple_drop_lockres(OCFS2_SB(sb), &oinfo->dqi_gqlock);
> +	ocfs2_lock_res_free(&oinfo->dqi_gqlock);
> +	list_for_each_entry(chunk, &oinfo->dqi_chunk, qc_chunk) {
> +		dchunk = (struct ocfs2_local_disk_chunk *)
> +					(chunk->qc_headerbh->b_data);
> +		if (chunk->qc_num < oinfo->dqi_chunks - 1) {
> +			len = ol_chunk_entries(sb);
> +		} else {
> +			len = (oinfo->dqi_blocks -
> +			       ol_quota_chunk_block(sb, chunk->qc_num) - 1)
> +			      * ol_quota_entries_per_block(sb);
> +		}
> +		/* Not all entries free? Bug! */
> +		if (le32_to_cpu(dchunk->dqc_free) != len) {
> +			mlog(ML_ERROR, "releasing quota file with used "
> +					"entries (type=%d)\n", type);
> +			mark_clean = 0;
> +		}
> +	}
> +	ocfs2_release_local_quota_bitmaps(&oinfo->dqi_chunk);
> +
> +	if (!mark_clean)
> +		goto out;
> +
> +	/* Mark local file as clean */
> +	info->dqi_flags |= OLQF_CLEAN;
> +	status = ocfs2_modify_bh(sb_dqopt(sb)->files[type],
> +				 oinfo->dqi_ibh,
> +				 olq_update_info,
> +				 info);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out;
> +	}
> +
> +out:
> +	ocfs2_inode_unlock(sb_dqopt(sb)->files[type], 1);
> +	brelse(oinfo->dqi_ibh);
> +	brelse(oinfo->dqi_lqi_bh);
> +	kfree(oinfo);
> +	return 0;
> +}
> +
> +static void olq_set_dquot(struct buffer_head *bh, void *private)
> +{
> +	struct ocfs2_dquot *od = private;
> +	struct ocfs2_local_disk_dqblk *dqblk;
> +	struct super_block *sb = od->dq_dquot.dq_sb;
> +
> +	dqblk = (struct ocfs2_local_disk_dqblk *)(bh->b_data
> +		+ ol_dqblk_block_offset(sb, od->dq_local_off));
> +
> +	dqblk->dqb_id = cpu_to_le64(od->dq_dquot.dq_id);
> +	spin_lock(&dq_data_lock);
> +	dqblk->dqb_spacemod = cpu_to_le64(od->dq_dquot.dq_dqb.dqb_curspace -
> +					  od->dq_origspace);
> +	dqblk->dqb_inodemod = cpu_to_le64(od->dq_dquot.dq_dqb.dqb_curinodes -
> +					  od->dq_originodes);
> +	spin_unlock(&dq_data_lock);
> +	mlog(0, "Writing local dquot %u space %lld inodes %lld\n",
> +	     od->dq_dquot.dq_id, dqblk->dqb_spacemod, dqblk->dqb_inodemod);
> +}
> +
> +/* Write dquot to local quota file */
> +static int ocfs2_local_write_dquot(struct dquot *dquot)
> +{
> +	struct super_block *sb = dquot->dq_sb;
> +	struct ocfs2_dquot *od = OCFS2_DQUOT(dquot);
> +	struct buffer_head *bh;
> +	int status;
> +
> +	bh = ocfs2_read_quota_block(sb_dqopt(sb)->files[dquot->dq_type],
> +				    ol_dqblk_file_block(sb, od->dq_local_off),
> +				    &status);
> +	if (!bh) {
> +		mlog_errno(status);
> +		goto out;
> +	}
> +	status = ocfs2_modify_bh(sb_dqopt(sb)->files[dquot->dq_type], bh,
> +				 olq_set_dquot, od);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out;
> +	}
> +out:
> +	brelse(bh);
> +	return status;
> +}
> +
> +/* Find free entry in local quota file */
> +static struct ocfs2_quota_chunk *ocfs2_find_free_entry(struct super_block *sb,
> +						       int type,
> +						       int *offset)
> +{
> +	struct mem_dqinfo *info = sb_dqinfo(sb, type);
> +	struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
> +	struct ocfs2_quota_chunk *chunk;
> +	struct ocfs2_local_disk_chunk *dchunk;
> +	int found = 0, len;
> +
> +	list_for_each_entry(chunk, &oinfo->dqi_chunk, qc_chunk) {
> +		dchunk = (struct ocfs2_local_disk_chunk *)
> +						chunk->qc_headerbh->b_data;
> +		if (le32_to_cpu(dchunk->dqc_free) > 0) {
> +			found = 1;
> +			break;
> +		}
> +	}
> +	if (!found)
> +		return NULL;
> +
> +	if (chunk->qc_num < oinfo->dqi_chunks - 1) {
> +		len = ol_chunk_entries(sb);
> +	} else {
> +		len = (oinfo->dqi_blocks -
> +		       ol_quota_chunk_block(sb, chunk->qc_num) - 1)
> +		      * ol_quota_entries_per_block(sb);
> +	}
> +
> +	found = ocfs2_find_next_zero_bit(dchunk->dqc_bitmap, len, 0);
> +	/* We failed? */
> +	if (found == len) {
> +		mlog(ML_ERROR, "Did not find empty entry in chunk %d with %u"
> +		     " entries free (type=%d)\n", chunk->qc_num,
> +		     le32_to_cpu(dchunk->dqc_free), type);
> +		return ERR_PTR(-EIO);
> +	}
> +	*offset = found;
> +	return chunk;
> +}
> +
> +/* Add new chunk to the local quota file */
> +static struct ocfs2_quota_chunk *ocfs2_local_quota_add_chunk(
> +							struct super_block *sb,
> +							int type,
> +							int *offset)
> +{
> +	struct mem_dqinfo *info = sb_dqinfo(sb, type);
> +	struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
> +	struct inode *lqinode = sb_dqopt(sb)->files[type];
> +	struct ocfs2_quota_chunk *chunk = NULL;
> +	struct ocfs2_local_disk_chunk *dchunk;
> +	int status;
> +	handle_t *handle;
> +	struct buffer_head *bh = NULL;
> +	u64 p_blkno;
> +
> +	/* We are protected by dqio_sem so no locking needed */
> +	status = ocfs2_extend_no_holes(lqinode,
> +				       lqinode->i_size + 2 * sb->s_blocksize,
> +				       lqinode->i_size);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out;
> +	}
> +	status = ocfs2_simple_size_update(lqinode, oinfo->dqi_lqi_bh,
> +					  lqinode->i_size + 2 * sb->s_blocksize);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out;
> +	}
> +
> +	chunk = kmem_cache_alloc(ocfs2_qf_chunk_cachep, GFP_NOFS);
> +	if (!chunk) {
> +		status = -ENOMEM;
> +		mlog_errno(status);
> +		goto out;
> +	}
> +
> +	down_read(&OCFS2_I(lqinode)->ip_alloc_sem);
> +	status = ocfs2_extent_map_get_blocks(lqinode, oinfo->dqi_blocks,
> +					     &p_blkno, NULL, NULL);
> +	up_read(&OCFS2_I(lqinode)->ip_alloc_sem);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out;
> +	}
> +	bh = sb_getblk(sb, p_blkno);
> +	if (!bh) {
> +		status = -ENOMEM;
> +		mlog_errno(status);
> +		goto out;
> +	}
> +	dchunk = (struct ocfs2_local_disk_chunk *)bh->b_data;
> +
> +	handle = ocfs2_start_trans(OCFS2_SB(sb), 2);
> +	if (IS_ERR(handle)) {
> +		status = PTR_ERR(handle);
> +		mlog_errno(status);
> +		goto out;
> +	}
> +
> +	status = ocfs2_journal_access(handle, lqinode, bh,
> +				      OCFS2_JOURNAL_ACCESS_WRITE);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out_trans;
> +	}
> +	lock_buffer(bh);
> +	dchunk->dqc_free = ol_quota_entries_per_block(sb);
> +	memset(dchunk->dqc_bitmap, 0,
> +	       sb->s_blocksize - sizeof(struct ocfs2_local_disk_chunk) -
> +	       OCFS2_QBLK_RESERVED_SPACE);
> +	set_buffer_uptodate(bh);
> +	unlock_buffer(bh);
> +	status = ocfs2_journal_dirty(handle, bh);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out_trans;
> +	}
> +
> +	oinfo->dqi_blocks += 2;
> +	oinfo->dqi_chunks++;
> +	status = ocfs2_local_write_info(sb, type);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out_trans;
> +	}
> +	status = ocfs2_commit_trans(OCFS2_SB(sb), handle);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out;
> +	}
> +
> +	list_add_tail(&chunk->qc_chunk, &oinfo->dqi_chunk);
> +	chunk->qc_num = list_entry(chunk->qc_chunk.prev,
> +				   struct ocfs2_quota_chunk,
> +				   qc_chunk)->qc_num + 1;
> +	chunk->qc_headerbh = bh;
> +	*offset = 0;
> +	return chunk;
> +out_trans:
> +	ocfs2_commit_trans(OCFS2_SB(sb), handle);
> +out:
> +	brelse(bh);
> +	kmem_cache_free(ocfs2_qf_chunk_cachep, chunk);
> +	return ERR_PTR(status);
> +}
> +
> +/* Find free entry in local quota file */
> +static struct ocfs2_quota_chunk *ocfs2_extend_local_quota_file(
> +						       struct super_block *sb,
> +						       int type,
> +						       int *offset)
> +{
> +	struct mem_dqinfo *info = sb_dqinfo(sb, type);
> +	struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
> +	struct ocfs2_quota_chunk *chunk;
> +	struct inode *lqinode = sb_dqopt(sb)->files[type];
> +	struct ocfs2_local_disk_chunk *dchunk;
> +	int epb = ol_quota_entries_per_block(sb);
> +	unsigned int chunk_blocks;
> +	int status;
> +	handle_t *handle;
> +
> +	if (list_empty(&oinfo->dqi_chunk))
> +		return ocfs2_local_quota_add_chunk(sb, type, offset);
> +	/* Is the last chunk full? */
> +	chunk = list_entry(oinfo->dqi_chunk.prev,
> +			struct ocfs2_quota_chunk, qc_chunk);
> +	chunk_blocks = oinfo->dqi_blocks -
> +			ol_quota_chunk_block(sb, chunk->qc_num) - 1;
> +	if (ol_chunk_blocks(sb) == chunk_blocks)
> +		return ocfs2_local_quota_add_chunk(sb, type, offset);
> +
> +	/* We are protected by dqio_sem so no locking needed */
> +	status = ocfs2_extend_no_holes(lqinode,
> +				       lqinode->i_size + sb->s_blocksize,
> +				       lqinode->i_size);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out;
> +	}
> +	status = ocfs2_simple_size_update(lqinode, oinfo->dqi_lqi_bh,
> +					  lqinode->i_size + sb->s_blocksize);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out;
> +	}
> +	handle = ocfs2_start_trans(OCFS2_SB(sb), 2);
> +	if (IS_ERR(handle)) {
> +		status = PTR_ERR(handle);
> +		mlog_errno(status);
> +		goto out;
> +	}
> +	status = ocfs2_journal_access(handle, lqinode, chunk->qc_headerbh,
> +				 OCFS2_JOURNAL_ACCESS_WRITE);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out_trans;
> +	}
> +
> +	dchunk = (struct ocfs2_local_disk_chunk *)chunk->qc_headerbh->b_data;
> +	lock_buffer(chunk->qc_headerbh);
> +	le32_add_cpu(&dchunk->dqc_free, ol_quota_entries_per_block(sb));
> +	unlock_buffer(chunk->qc_headerbh);
> +	status = ocfs2_journal_dirty(handle, chunk->qc_headerbh);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out_trans;
> +	}
> +	oinfo->dqi_blocks++;
> +	status = ocfs2_local_write_info(sb, type);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out_trans;
> +	}
> +
> +	status = ocfs2_commit_trans(OCFS2_SB(sb), handle);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out;
> +	}
> +	*offset = chunk_blocks * epb;
> +	return chunk;
> +out_trans:
> +	ocfs2_commit_trans(OCFS2_SB(sb), handle);
> +out:
> +	return ERR_PTR(status);
> +}
> +
> +void olq_alloc_dquot(struct buffer_head *bh, void *private)
> +{
> +	int *offset = private;
> +	struct ocfs2_local_disk_chunk *dchunk;
> +
> +	dchunk = (struct ocfs2_local_disk_chunk *)bh->b_data;
> +	ocfs2_set_bit(*offset, dchunk->dqc_bitmap);
> +	le32_add_cpu(&dchunk->dqc_free, -1);
> +}
> +
> +/* Create dquot in the local file for given id */
> +static int ocfs2_create_local_dquot(struct dquot *dquot)
> +{
> +	struct super_block *sb = dquot->dq_sb;
> +	int type = dquot->dq_type;
> +	struct inode *lqinode = sb_dqopt(sb)->files[type];
> +	struct ocfs2_quota_chunk *chunk;
> +	struct ocfs2_dquot *od = OCFS2_DQUOT(dquot);
> +	int offset;
> +	int status;
> +
> +	chunk = ocfs2_find_free_entry(sb, type, &offset);
> +	if (!chunk) {
> +		chunk = ocfs2_extend_local_quota_file(sb, type, &offset);
> +		if (IS_ERR(chunk))
> +			return PTR_ERR(chunk);
> +	} else if (IS_ERR(chunk)) {
> +		return PTR_ERR(chunk);
> +	}
> +	od->dq_local_off = ol_dqblk_off(sb, chunk->qc_num, offset);
> +	od->dq_chunk = chunk;
> +
> +	/* Initialize dquot structure on disk */
> +	status = ocfs2_local_write_dquot(dquot);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out;
> +	}
> +
> +	/* Mark structure as allocated */
> +	status = ocfs2_modify_bh(lqinode, chunk->qc_headerbh, olq_alloc_dquot,
> +				 &offset);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out;
> +	}
> +out:
> +	return status;
> +}
> +
> +/* Create entry in local file for dquot, load data from the global file */
> +static int ocfs2_local_read_dquot(struct dquot *dquot)
> +{
> +	int status;
> +
> +	mlog_entry("id=%u, type=%d\n", dquot->dq_id, dquot->dq_type);
> +
> +	status = ocfs2_global_read_dquot(dquot);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out_err;
> +	}
> +
> +	/* Now create entry in the local quota file */
> +	status = ocfs2_create_local_dquot(dquot);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out_err;
> +	}
> +	mlog_exit(0);
> +	return 0;
> +out_err:
> +	mlog_exit(status);
> +	return status;
> +}
> +
> +/* Release dquot structure from local quota file. ocfs2_release_dquot() has
> + * already started a transaction and obtained exclusive lock for global
> + * quota file. */
> +static int ocfs2_local_release_dquot(struct dquot *dquot)
> +{
> +	int status;
> +	int type = dquot->dq_type;
> +	struct ocfs2_dquot *od = OCFS2_DQUOT(dquot);
> +	struct super_block *sb = dquot->dq_sb;
> +	struct ocfs2_local_disk_chunk *dchunk;
> +	int offset;
> +	handle_t *handle = journal_current_handle();
> +
> +	BUG_ON(!handle);
> +	/* First write all local changes to global file */
> +	status = ocfs2_global_release_dquot(dquot);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out;
> +	}
> +
> +	status = ocfs2_journal_access(handle, sb_dqopt(sb)->files[type],
> +			od->dq_chunk->qc_headerbh, OCFS2_JOURNAL_ACCESS_WRITE);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out;
> +	}
> +	offset = ol_dqblk_chunk_off(sb, od->dq_chunk->qc_num,
> +					     od->dq_local_off);
> +	dchunk = (struct ocfs2_local_disk_chunk *)
> +			(od->dq_chunk->qc_headerbh->b_data);
> +	/* Mark structure as freed */
> +	lock_buffer(od->dq_chunk->qc_headerbh);
> +	ocfs2_clear_bit(offset, dchunk->dqc_bitmap);
> +	le32_add_cpu(&dchunk->dqc_free, 1);
> +	unlock_buffer(od->dq_chunk->qc_headerbh);
> +	status = ocfs2_journal_dirty(handle, od->dq_chunk->qc_headerbh);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto out;
> +	}
> +	status = 0;
> +out:
> +	/* Clear the read bit so that next time someone uses this
> +	 * dquot he reads fresh info from disk and allocates local
> +	 * dquot structure */
> +	clear_bit(DQ_READ_B, &dquot->dq_flags);
> +	return status;
> +}
> +
> +static struct quota_format_ops ocfs2_format_ops = {
> +	.check_quota_file	= ocfs2_local_check_quota_file,
> +	.read_file_info		= ocfs2_local_read_info,
> +	.write_file_info	= ocfs2_global_write_info,
> +	.free_file_info		= ocfs2_local_free_info,
> +	.read_dqblk		= ocfs2_local_read_dquot,
> +	.commit_dqblk		= ocfs2_local_write_dquot,
> +	.release_dqblk		= ocfs2_local_release_dquot,
> +};
> +
> +struct quota_format_type ocfs2_quota_format = {
> +	.qf_fmt_id = QFMT_OCFS2,
> +	.qf_ops = &ocfs2_format_ops,
> +	.qf_owner = THIS_MODULE
> +};
> diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
> index 4712bc7..92539fe 100644
> --- a/fs/ocfs2/super.c
> +++ b/fs/ocfs2/super.c
> @@ -65,10 +65,13 @@
>  #include "uptodate.h"
>  #include "ver.h"
>  #include "xattr.h"
> +#include "quota.h"
>  
>  #include "buffer_head_io.h"
>  
>  static struct kmem_cache *ocfs2_inode_cachep = NULL;
> +struct kmem_cache *ocfs2_dquot_cachep;
> +struct kmem_cache *ocfs2_qf_chunk_cachep;
>  
>  /* OCFS2 needs to schedule several differnt types of work which
>   * require cluster locking, disk I/O, recovery waits, etc. Since these
> @@ -137,6 +140,8 @@ static const struct super_operations ocfs2_sops = {
>  	.put_super	= ocfs2_put_super,
>  	.remount_fs	= ocfs2_remount,
>  	.show_options   = ocfs2_show_options,
> +	.quota_read	= ocfs2_quota_read,
> +	.quota_write	= ocfs2_quota_write,
>  };
>  
>  enum {
> @@ -1071,6 +1076,7 @@ static int __init ocfs2_init(void)
>  
>  	ocfs2_set_locking_protocol();
>  
> +	status = register_quota_format(&ocfs2_quota_format);
>  leave:
>  	if (status < 0) {
>  		ocfs2_free_mem_caches();
> @@ -1094,6 +1100,8 @@ static void __exit ocfs2_exit(void)
>  		destroy_workqueue(ocfs2_wq);
>  	}
>  
> +	unregister_quota_format(&ocfs2_quota_format);
> +
>  	debugfs_remove(ocfs2_debugfs_root);
>  
>  	ocfs2_free_mem_caches();
> @@ -1209,8 +1217,27 @@ static int ocfs2_initialize_mem_caches(void)
>  				       (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|
>  						SLAB_MEM_SPREAD),
>  				       ocfs2_inode_init_once);
> -	if (!ocfs2_inode_cachep)
> +	ocfs2_dquot_cachep = kmem_cache_create("ocfs2_dquot_cache",
> +					sizeof(struct ocfs2_dquot),
> +					0,
> +					(SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|
> +						SLAB_MEM_SPREAD),
> +					NULL);
> +	ocfs2_qf_chunk_cachep = kmem_cache_create("ocfs2_qf_chunk_cache",
> +					sizeof(struct ocfs2_quota_chunk),
> +					0,
> +					(SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD),
> +					NULL);
> +	if (!ocfs2_inode_cachep || !ocfs2_dquot_cachep ||
> +	    !ocfs2_qf_chunk_cachep) {
> +		if (ocfs2_inode_cachep)
> +			kmem_cache_destroy(ocfs2_inode_cachep);
> +		if (ocfs2_dquot_cachep)
> +			kmem_cache_destroy(ocfs2_dquot_cachep);
> +		if (ocfs2_qf_chunk_cachep)
> +			kmem_cache_destroy(ocfs2_qf_chunk_cachep);
>  		return -ENOMEM;
> +	}
>  
>  	return 0;
>  }
> @@ -1219,8 +1246,15 @@ static void ocfs2_free_mem_caches(void)
>  {
>  	if (ocfs2_inode_cachep)
>  		kmem_cache_destroy(ocfs2_inode_cachep);
> -
>  	ocfs2_inode_cachep = NULL;
> +
> +	if (ocfs2_dquot_cachep)
> +		kmem_cache_destroy(ocfs2_dquot_cachep);
> +	ocfs2_dquot_cachep = NULL;
> +
> +	if (ocfs2_qf_chunk_cachep)
> +		kmem_cache_destroy(ocfs2_qf_chunk_cachep);
> +	ocfs2_qf_chunk_cachep = NULL;
>  }
>  
>  static int ocfs2_get_sector(struct super_block *sb,
> -- 
> 1.5.2.4
> 
> 
> _______________________________________________
> Ocfs2-devel mailing list
> Ocfs2-devel at oss.oracle.com
> http://oss.oracle.com/mailman/listinfo/ocfs2-devel

-- 

"Born under a bad sign.
 I been down since I began to crawl.
 If it wasn't for bad luck,
 I wouldn't have no luck at all."

Joel Becker
Principal Software Developer
Oracle
E-mail: joel.becker at oracle.com
Phone: (650) 506-8127

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

* [Ocfs2-devel] [PATCH 29/29] ocfs2: Enable quota accounting on mount, disable on umount
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 29/29] ocfs2: Enable quota accounting on mount, disable on umount Jan Kara
@ 2008-10-28 19:11   ` Joel Becker
  2008-10-29  2:30     ` Jan Kara
  0 siblings, 1 reply; 69+ messages in thread
From: Joel Becker @ 2008-10-28 19:11 UTC (permalink / raw)
  To: ocfs2-devel

On Sat, Oct 25, 2008 at 12:08:22AM +0200, Jan Kara wrote:
> Enable quota usage tracking on mount and disable it on umount. Also
> add support for quota on and quota off quotactls and usrquota and
> grpquota mount options.
> 

<snip>

> diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
> index 92539fe..bddc210 100644
> --- a/fs/ocfs2/super.c
> +++ b/fs/ocfs2/super.c
> @@ -780,6 +950,23 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent)
>  	atomic_set(&osb->vol_state, VOLUME_MOUNTED);
>  	wake_up(&osb->osb_mount_event);
>  
> +	/* Now we can initialize quotas because we can afford to wait
> +	 * for cluster locks recovery now. That also means that truncation
> +	 * log recovery can happen but that waits for proper quota setup */
> +	if (!(sb->s_flags & MS_RDONLY)) {
> +		status = ocfs2_enable_quotas(osb);
> +		if (status < 0) {
> +			mlog_errno(status);
> +			goto read_super_error;
> +		}
> +	}
> +
> +	ocfs2_complete_quota_recovery(osb);
> +
> +	/* Now we wake up again for processes waiting for quotas */
> +	atomic_set(&osb->vol_state, VOLUME_MOUNTED_QUOTAS);
> +	wake_up(&osb->osb_mount_event);
> +
>  	mlog_exit(status);
>  	return status;
>  

	This hunk still has the bug where you are jumping to
read_super_error after setting sb->s_root.  I'm guessing you have that
fixed on your machine?

Joel

-- 

Life's Little Instruction Book #80

	"Slow dance"

Joel Becker
Principal Software Developer
Oracle
E-mail: joel.becker at oracle.com
Phone: (650) 506-8127

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

* [Ocfs2-devel] [PATCH 25/29] ocfs2: Implementation of local and global quota file handling
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 25/29] ocfs2: Implementation of local and global quota file handling Jan Kara
  2008-10-28 19:07   ` Joel Becker
@ 2008-10-28 19:36   ` Joel Becker
  2008-10-29  2:29     ` Jan Kara
  2008-11-05 22:49   ` Mark Fasheh
  2 siblings, 1 reply; 69+ messages in thread
From: Joel Becker @ 2008-10-28 19:36 UTC (permalink / raw)
  To: ocfs2-devel

On Sat, Oct 25, 2008 at 12:08:18AM +0200, Jan Kara wrote:
> For each quota type each node has local quota file. In this file it stores
> changes users have made to disk usage via this node. Once in a while this
> information is synced to global file (and thus with other nodes) so that
> limits enforcement at least aproximately works.
> diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h
> index dd17137..04bebd2 100644
> --- a/fs/ocfs2/ocfs2_fs.h
> +++ b/fs/ocfs2/ocfs2_fs.h
> @@ -878,6 +878,101 @@ static inline int ocfs2_xattr_get_type(struct ocfs2_xattr_entry *xe)
>  	return xe->xe_type & OCFS2_XATTR_TYPE_MASK;
>  }
>  
> +/*
> + *  On disk structures for global quota file
> + */
> +
> +/* Magic numbers and known versions for global quota files */
> +#define OCFS2_GLOBAL_QMAGICS {\
> +	0x0cf52470, /* USRQUOTA */ \
> +	0x0cf52471  /* GRPQUOTA */ \
> +}
> +
> +#define OCFS2_GLOBAL_QVERSIONS {\
> +	0, \
> +	0, \
> +}
> +
> +/* Generic header of all quota files */
> +struct ocfs2_disk_dqheader {
> +	__le32 dqh_magic;	/* Magic number identifying file */
> +	__le32 dqh_version;	/* Quota format version */
> +};
> +
> +#define OCFS2_GLOBAL_INFO_OFF (sizeof(struct ocfs2_disk_dqheader))
> +
> +/* Information header of global quota file (immediately follows the generic
> + * header) */
> +struct ocfs2_global_disk_dqinfo {
> +/*00*/	__le32 dqi_bgrace;
> +	__le32 dqi_igrace;
> +	__le32 dqi_syncms;
> +	__le32 dqi_blocks;
> +/*10*/	__le32 dqi_free_blk;
> +	__le32 dqi_free_entry;
> +};

	The way I read this, the first block of a quota file contains
first the disk_dqheader, and then the local info
(global_disk_dqinfo,local_disk_dqinfo).  Is that right?  I'd love to see
the ecc field (__le64 for now) in the disk_dqheader.

> +/* Structure with global user / group information. We reserve some space
> + * for future use. */
> +struct ocfs2_global_disk_dqblk {
> +/*00*/	__le32 dqb_id;          /* ID the structure belongs to */
> +	__le32 dqb_use_count;   /* Number of nodes having reference to this structure */
> +	__le64 dqb_ihardlimit;  /* absolute limit on allocated inodes */
> +/*10*/	__le64 dqb_isoftlimit;  /* preferred inode limit */
> +	__le64 dqb_curinodes;   /* current # allocated inodes */
> +/*20*/	__le64 dqb_bhardlimit;  /* absolute limit on disk space */
> +	__le64 dqb_bsoftlimit;  /* preferred limit on disk space */
> +/*30*/	__le64 dqb_curspace;    /* current space occupied */
> +	__le64 dqb_btime;       /* time limit for excessive disk use */
> +/*40*/	__le64 dqb_itime;       /* time limit for excessive inode use */
> +	__le64 dqb_pad1;
> +/*50*/	__le64 dqb_pad2;
> +};

	We also need space for the ecc data here.  It looks like ecc
space wasn't added to this series.  Rather than comment on each
structure, just let me know when that's ready.

Joel


-- 

"I don't want to achieve immortality through my work; I want to
 achieve immortality through not dying."
        - Woody Allen

Joel Becker
Principal Software Developer
Oracle
E-mail: joel.becker at oracle.com
Phone: (650) 506-8127

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

* [Ocfs2-devel] [PATCH 23/29] ocfs2: Assign feature bits and system inodes to quota feature and quota files
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 23/29] ocfs2: Assign feature bits and system inodes to quota feature and quota files Jan Kara
@ 2008-10-28 22:16   ` Joel Becker
  2008-10-29  2:32     ` Jan Kara
  0 siblings, 1 reply; 69+ messages in thread
From: Joel Becker @ 2008-10-28 22:16 UTC (permalink / raw)
  To: ocfs2-devel

On Sat, Oct 25, 2008 at 12:08:16AM +0200, Jan Kara wrote:
> diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h
> index f24ce3d..dd17137 100644
> --- a/fs/ocfs2/ocfs2_fs.h
> +++ b/fs/ocfs2/ocfs2_fs.h
> @@ -93,7 +93,9 @@
>  					 | OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP \
>  					 | OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK \
>  					 | OCFS2_FEATURE_INCOMPAT_XATTR)
> -#define OCFS2_FEATURE_RO_COMPAT_SUPP	OCFS2_FEATURE_RO_COMPAT_UNWRITTEN
> +#define OCFS2_FEATURE_RO_COMPAT_SUPP	(OCFS2_FEATURE_RO_COMPAT_UNWRITTEN \
> +					 | OCFS2_FEATURE_RO_COMPAT_USRQUOTA \
> +					 | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)

	I know you're just in the progress of working, so this isn't an
immediate thing, but I wanted to put this down in the discussion.
	When we land the quota work, the feature bits have to be
absolutely last.  It's not about whether quota works (eg, the mounting
code later in the series).  It's about the cluster's handling of quotas.
After this patch, a node will happily mount the filesystem while
ignoring quotas.  Another node, one with the full series, will actually
use and write out quotas.  This leads to inconsistency.  By having the
very last patch add quotas to the supported features, we ensure this
doesn't happen.

Joel

-- 

"Up and down that road in our worn out shoes,
 Talking bout good things and singing the blues."

Joel Becker
Principal Software Developer
Oracle
E-mail: joel.becker at oracle.com
Phone: (650) 506-8127

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

* [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2)
  2008-10-28 11:23       ` tristan.ye
@ 2008-10-29  1:57         ` Jan Kara
  2008-10-29  2:32           ` tristan.ye
  0 siblings, 1 reply; 69+ messages in thread
From: Jan Kara @ 2008-10-29  1:57 UTC (permalink / raw)
  To: ocfs2-devel

> Jan,
> 
> Sorry for making the noise again:)
  I'm happy the patches finally appply fine for you :).

> I'm just curious about one thing, as you said we choose quotaon to
> enforce the quota limitation on ocfs2 to keep the consistency with other
> fs, and the quotaon finally call the quoctl with the cmd word
> 'Q_QUOTAON', here i found the quoctl() need a agrumnet to point to  the
> path name of file containing the quotas for the filesystem,but
> unfortunately these files were hidden from userspace on ocfs2. so this
> quotactl call for turning on the quota will definitely fail, is that the
> case?
  No, the quotactl won't fail because OCFS2 kernel module overloads
function implementing the Q_QUOTAON handling and so filename is actually
ignored and it provides corresponding system inode instead.

> Since the existing POSIX quota tools did not work  under ocfs2
> currently, i've written a simple version of the quota tools(just
> including getquota,setquotas and quotactl) by the help of quotactl()
> API.  All of its binaries and src attached.
  It is strange that they don't work for you. Are you using the version of
quota-tools I sent you (like week ago after I sent the first set of
patches)? Because the changed version of quota-tools works here for me just
fine... I'm attaching it to this mail again just in case it got lost somewhere.

> As what i said above, my version of quotaon(invoked by 'quotactl -o')
> also did not work here ,and fortunately, we really do succeed to set/get
> the quota for usr/group.
> you can have a check in your env if you wish.
  Well, having your own tool for turning quotas on is possible as well, but
I think it would be better if you used provided quota-tools.

								Honza
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR
-------------- next part --------------
A non-text attachment was scrubbed...
Name: quota-tools-ocfs2.tar.gz
Type: application/x-compressed-tar
Size: 419370 bytes
Desc: not available
Url : http://oss.oracle.com/pipermail/ocfs2-devel/attachments/20081029/200da1c3/attachment-0001.bin 

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

* [Ocfs2-devel] [PATCH 25/29] ocfs2: Implementation of local and global quota file handling
  2008-10-28 19:36   ` Joel Becker
@ 2008-10-29  2:29     ` Jan Kara
  2008-10-29 10:51       ` Joel Becker
  0 siblings, 1 reply; 69+ messages in thread
From: Jan Kara @ 2008-10-29  2:29 UTC (permalink / raw)
  To: ocfs2-devel

On Tue 28-10-08 12:36:52, Joel Becker wrote:
> On Sat, Oct 25, 2008 at 12:08:18AM +0200, Jan Kara wrote:
> > For each quota type each node has local quota file. In this file it stores
> > changes users have made to disk usage via this node. Once in a while this
> > information is synced to global file (and thus with other nodes) so that
> > limits enforcement at least aproximately works.
> > diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h
> > index dd17137..04bebd2 100644
> > --- a/fs/ocfs2/ocfs2_fs.h
> > +++ b/fs/ocfs2/ocfs2_fs.h
> > @@ -878,6 +878,101 @@ static inline int ocfs2_xattr_get_type(struct ocfs2_xattr_entry *xe)
> >  	return xe->xe_type & OCFS2_XATTR_TYPE_MASK;
> >  }
> >  
> > +/*
> > + *  On disk structures for global quota file
> > + */
> > +
> > +/* Magic numbers and known versions for global quota files */
> > +#define OCFS2_GLOBAL_QMAGICS {\
> > +	0x0cf52470, /* USRQUOTA */ \
> > +	0x0cf52471  /* GRPQUOTA */ \
> > +}
> > +
> > +#define OCFS2_GLOBAL_QVERSIONS {\
> > +	0, \
> > +	0, \
> > +}
> > +
> > +/* Generic header of all quota files */
> > +struct ocfs2_disk_dqheader {
> > +	__le32 dqh_magic;	/* Magic number identifying file */
> > +	__le32 dqh_version;	/* Quota format version */
> > +};
> > +
> > +#define OCFS2_GLOBAL_INFO_OFF (sizeof(struct ocfs2_disk_dqheader))
> > +
> > +/* Information header of global quota file (immediately follows the generic
> > + * header) */
> > +struct ocfs2_global_disk_dqinfo {
> > +/*00*/	__le32 dqi_bgrace;
> > +	__le32 dqi_igrace;
> > +	__le32 dqi_syncms;
> > +	__le32 dqi_blocks;
> > +/*10*/	__le32 dqi_free_blk;
> > +	__le32 dqi_free_entry;
> > +};
> 
> 	The way I read this, the first block of a quota file contains
> first the disk_dqheader, and then the local info
> (global_disk_dqinfo,local_disk_dqinfo).  Is that right?  I'd love to see
> the ecc field (__le64 for now) in the disk_dqheader.
  Yes. Actually, this set of patches already counts with ECC, but it's
hidden ;). See below.

> > +/* Structure with global user / group information. We reserve some space
> > + * for future use. */
> > +struct ocfs2_global_disk_dqblk {
> > +/*00*/	__le32 dqb_id;          /* ID the structure belongs to */
> > +	__le32 dqb_use_count;   /* Number of nodes having reference to this structure */
> > +	__le64 dqb_ihardlimit;  /* absolute limit on allocated inodes */
> > +/*10*/	__le64 dqb_isoftlimit;  /* preferred inode limit */
> > +	__le64 dqb_curinodes;   /* current # allocated inodes */
> > +/*20*/	__le64 dqb_bhardlimit;  /* absolute limit on disk space */
> > +	__le64 dqb_bsoftlimit;  /* preferred limit on disk space */
> > +/*30*/	__le64 dqb_curspace;    /* current space occupied */
> > +	__le64 dqb_btime;       /* time limit for excessive disk use */
> > +/*40*/	__le64 dqb_itime;       /* time limit for excessive inode use */
> > +	__le64 dqb_pad1;
> > +/*50*/	__le64 dqb_pad2;
> > +};
> 
> 	We also need space for the ecc data here.  It looks like ecc
> space wasn't added to this series.  Rather than comment on each
> structure, just let me know when that's ready.
  What I did is, that in the end of each quotafile block there are 8 bytes
reserved for OCFS2 use (both in local and global quota files). So these
bytes are intended to be used for ECC. I hope that's fine.

								Honza

-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

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

* [Ocfs2-devel] [PATCH 29/29] ocfs2: Enable quota accounting on mount, disable on umount
  2008-10-28 19:11   ` Joel Becker
@ 2008-10-29  2:30     ` Jan Kara
  0 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-29  2:30 UTC (permalink / raw)
  To: ocfs2-devel

On Tue 28-10-08 12:11:03, Joel Becker wrote:
> On Sat, Oct 25, 2008 at 12:08:22AM +0200, Jan Kara wrote:
> > Enable quota usage tracking on mount and disable it on umount. Also
> > add support for quota on and quota off quotactls and usrquota and
> > grpquota mount options.
> > 
> 
> <snip>
> 
> > diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
> > index 92539fe..bddc210 100644
> > --- a/fs/ocfs2/super.c
> > +++ b/fs/ocfs2/super.c
> > @@ -780,6 +950,23 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent)
> >  	atomic_set(&osb->vol_state, VOLUME_MOUNTED);
> >  	wake_up(&osb->osb_mount_event);
> >  
> > +	/* Now we can initialize quotas because we can afford to wait
> > +	 * for cluster locks recovery now. That also means that truncation
> > +	 * log recovery can happen but that waits for proper quota setup */
> > +	if (!(sb->s_flags & MS_RDONLY)) {
> > +		status = ocfs2_enable_quotas(osb);
> > +		if (status < 0) {
> > +			mlog_errno(status);
> > +			goto read_super_error;
> > +		}
> > +	}
> > +
> > +	ocfs2_complete_quota_recovery(osb);
> > +
> > +	/* Now we wake up again for processes waiting for quotas */
> > +	atomic_set(&osb->vol_state, VOLUME_MOUNTED_QUOTAS);
> > +	wake_up(&osb->osb_mount_event);
> > +
> >  	mlog_exit(status);
> >  	return status;
> >  
> 
> 	This hunk still has the bug where you are jumping to
> read_super_error after setting sb->s_root.  I'm guessing you have that
> fixed on your machine?
  Not yet fixed but yeah, I have it on my todo list :). These patches went
out before you pointed out the bug to me. Thanks for the review.

								Honza
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

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

* [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2)
  2008-10-29  1:57         ` Jan Kara
@ 2008-10-29  2:32           ` tristan.ye
  0 siblings, 0 replies; 69+ messages in thread
From: tristan.ye @ 2008-10-29  2:32 UTC (permalink / raw)
  To: ocfs2-devel

On Wed, 2008-10-29 at 02:57 +0100, Jan Kara wrote:
> > Jan,
> > 
> > Sorry for making the noise again:)
>   I'm happy the patches finally appply fine for you :).

Thanks a lot for your clarifications for my questions,I'll try your
provided tools later.

For the second version(26patches) patch set you've provided, i did not
succeed to apply on your recommended kernel(Mark's linux-next branch),
but only get it done with a success on linus's mainline-2.6.27 kernel.
so it may make a little bit difference with your working environment,
will it make any hurt for quota testing?

Tristan.

> 
> > I'm just curious about one thing, as you said we choose quotaon to
> > enforce the quota limitation on ocfs2 to keep the consistency with other
> > fs, and the quotaon finally call the quoctl with the cmd word
> > 'Q_QUOTAON', here i found the quoctl() need a agrumnet to point to  the
> > path name of file containing the quotas for the filesystem,but
> > unfortunately these files were hidden from userspace on ocfs2. so this
> > quotactl call for turning on the quota will definitely fail, is that the
> > case?
>   No, the quotactl won't fail because OCFS2 kernel module overloads
> function implementing the Q_QUOTAON handling and so filename is actually
> ignored and it provides corresponding system inode instead.
> 
> > Since the existing POSIX quota tools did not work  under ocfs2
> > currently, i've written a simple version of the quota tools(just
> > including getquota,setquotas and quotactl) by the help of quotactl()
> > API.  All of its binaries and src attached.
>   It is strange that they don't work for you. Are you using the version of
> quota-tools I sent you (like week ago after I sent the first set of
> patches)? Because the changed version of quota-tools works here for me just
> fine... I'm attaching it to this mail again just in case it got lost somewhere.
> 
> > As what i said above, my version of quotaon(invoked by 'quotactl -o')
> > also did not work here ,and fortunately, we really do succeed to set/get
> > the quota for usr/group.
> > you can have a check in your env if you wish.
>   Well, having your own tool for turning quotas on is possible as well, but
> I think it would be better if you used provided quota-tools.
> 
> 								Honza

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

* [Ocfs2-devel] [PATCH 23/29] ocfs2: Assign feature bits and system inodes to quota feature and quota files
  2008-10-28 22:16   ` Joel Becker
@ 2008-10-29  2:32     ` Jan Kara
  0 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-29  2:32 UTC (permalink / raw)
  To: ocfs2-devel

On Tue 28-10-08 15:16:06, Joel Becker wrote:
> On Sat, Oct 25, 2008 at 12:08:16AM +0200, Jan Kara wrote:
> > diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h
> > index f24ce3d..dd17137 100644
> > --- a/fs/ocfs2/ocfs2_fs.h
> > +++ b/fs/ocfs2/ocfs2_fs.h
> > @@ -93,7 +93,9 @@
> >  					 | OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP \
> >  					 | OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK \
> >  					 | OCFS2_FEATURE_INCOMPAT_XATTR)
> > -#define OCFS2_FEATURE_RO_COMPAT_SUPP	OCFS2_FEATURE_RO_COMPAT_UNWRITTEN
> > +#define OCFS2_FEATURE_RO_COMPAT_SUPP	(OCFS2_FEATURE_RO_COMPAT_UNWRITTEN \
> > +					 | OCFS2_FEATURE_RO_COMPAT_USRQUOTA \
> > +					 | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)
> 
> 	I know you're just in the progress of working, so this isn't an
> immediate thing, but I wanted to put this down in the discussion.
> 	When we land the quota work, the feature bits have to be
> absolutely last.  It's not about whether quota works (eg, the mounting
> code later in the series).  It's about the cluster's handling of quotas.
> After this patch, a node will happily mount the filesystem while
> ignoring quotas.  Another node, one with the full series, will actually
> use and write out quotas.  This leads to inconsistency.  By having the
> very last patch add quotas to the supported features, we ensure this
> doesn't happen.
  Ah, OK. I didn't realize this. Thanks for pointing this out. I'll move
this patch to the end and merge it with the "Enable quotas on mount" patch.

								Honza
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

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

* [Ocfs2-devel] [PATCH 25/29] ocfs2: Implementation of local and global quota file handling
  2008-10-29  2:29     ` Jan Kara
@ 2008-10-29 10:51       ` Joel Becker
  2008-10-30  7:33         ` Jan Kara
  0 siblings, 1 reply; 69+ messages in thread
From: Joel Becker @ 2008-10-29 10:51 UTC (permalink / raw)
  To: ocfs2-devel

On Wed, Oct 29, 2008 at 03:29:02AM +0100, Jan Kara wrote:
> > > +/* Structure with global user / group information. We reserve some space
> > > + * for future use. */
> > > +struct ocfs2_global_disk_dqblk {
> > > +/*00*/	__le32 dqb_id;          /* ID the structure belongs to */
> > > +	__le32 dqb_use_count;   /* Number of nodes having reference to this structure */
> > > +	__le64 dqb_ihardlimit;  /* absolute limit on allocated inodes */
> > > +/*10*/	__le64 dqb_isoftlimit;  /* preferred inode limit */
> > > +	__le64 dqb_curinodes;   /* current # allocated inodes */
> > > +/*20*/	__le64 dqb_bhardlimit;  /* absolute limit on disk space */
> > > +	__le64 dqb_bsoftlimit;  /* preferred limit on disk space */
> > > +/*30*/	__le64 dqb_curspace;    /* current space occupied */
> > > +	__le64 dqb_btime;       /* time limit for excessive disk use */
> > > +/*40*/	__le64 dqb_itime;       /* time limit for excessive inode use */
> > > +	__le64 dqb_pad1;
> > > +/*50*/	__le64 dqb_pad2;
> > > +};
> > 
> > 	We also need space for the ecc data here.  It looks like ecc
> > space wasn't added to this series.  Rather than comment on each
> > structure, just let me know when that's ready.
>   What I did is, that in the end of each quotafile block there are 8 bytes
> reserved for OCFS2 use (both in local and global quota files). So these
> bytes are intended to be used for ECC. I hope that's fine.

	So every block type (disk header, dq_block, etc) has the last 8
bytes free?  This seems like something we should find a way to mention
in the disk header.

Joel

-- 

Bram's Law:
	The easier a piece of software is to write, the worse it's
	implemented in practice.

Joel Becker
Principal Software Developer
Oracle
E-mail: joel.becker at oracle.com
Phone: (650) 506-8127

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

* [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2)
  2008-10-24 22:05 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
  2008-10-27  7:22 ` tristan.ye
  2008-10-27  9:08 ` tristan.ye
@ 2008-10-29 22:58 ` Mark Fasheh
  2008-10-30  5:02   ` Jan Kara
  2008-11-06  1:09 ` Mark Fasheh
  3 siblings, 1 reply; 69+ messages in thread
From: Mark Fasheh @ 2008-10-29 22:58 UTC (permalink / raw)
  To: ocfs2-devel

On Sat, Oct 25, 2008 at 12:05:04AM +0200, Jan Kara wrote:
> Hello,
> 
> the following patch series implements quotas for OCFS2. The patch
> series is based on:
> git://git.kernel.org/pub/scm/linux/kernel/git/mfasheh/ocfs2.git linux-next

Great, thanks for posting these again.


> I've adressed Joel's comments, also node recovery is now fully working
> and I've fixed a few issues I found during my testing. So I'm currently
> not aware of any bugs. Please review, test, comment. Thanks.

How are we going to handle the vfs patches? I think realistically, they
should just all go into the merge_window branch of ocfs2.git along with the
Ocfs2 patches to add quoata support (and make use of the VFS features you
added). The VFS patches at least though, should probably get posted to
linux-fsdevel before I pick them up.
	--Mark

--
Mark Fasheh

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

* [Ocfs2-devel] [PATCH 10/29] quota: Introduce DQUOT_QUOTA_SYS_FILE flag
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 10/29] quota: Introduce DQUOT_QUOTA_SYS_FILE flag Jan Kara
@ 2008-10-29 23:09   ` Mark Fasheh
  2008-10-30  7:24     ` Jan Kara
  0 siblings, 1 reply; 69+ messages in thread
From: Mark Fasheh @ 2008-10-29 23:09 UTC (permalink / raw)
  To: ocfs2-devel

On Sat, Oct 25, 2008 at 12:08:03AM +0200, Jan Kara wrote:
> If filesystem can handle quota files as system files hidden from users, we can
> skip a lot of cache invalidation, syncing, inode flags setting etc. when
> turning quotas on, off and quota_sync. Allow filesystem to indicate that it is
> hiding quota files from users by DQUOT_QUOTA_SYS_FILE flag.
> 
> Signed-off-by: Jan Kara <jack@suse.cz>
> ---
>  fs/dquot.c            |   45 ++++++++++++++++++++++++++++++---------------
>  fs/quota.c            |    3 +++
>  include/linux/quota.h |    7 +++++++
>  3 files changed, 40 insertions(+), 15 deletions(-)
> 
> diff --git a/fs/dquot.c b/fs/dquot.c
> index 96ed45b..5b82722 100644
> --- a/fs/dquot.c
> +++ b/fs/dquot.c
> @@ -1627,6 +1627,11 @@ int vfs_quota_disable(struct super_block *sb, int type, unsigned int flags)
>  		dqopt->ops[cnt] = NULL;
>  	}
>  	mutex_unlock(&dqopt->dqonoff_mutex);
> +
> +	/* Skip syncing and setting flags if quota files are hidden */
> +	if (dqopt->flags & DQUOT_QUOTA_SYS_FILE)
> +		goto put_inodes;
> +
>  	/* Sync the superblock so that buffers with quota data are written to
>  	 * disk (and so userspace sees correct data afterwards). */
>  	if (sb->s_op->sync_fs)
> @@ -1651,6 +1656,12 @@ int vfs_quota_disable(struct super_block *sb, int type, unsigned int flags)
>  				mark_inode_dirty(toputinode[cnt]);
>  			}
>  			mutex_unlock(&dqopt->dqonoff_mutex);
> +		}
> +	if (sb->s_bdev)
> +		invalidate_bdev(sb->s_bdev);

Is the indentation here weird, or am I just reading this wrong?


Otherwise btw, this patch is a fantastic idea. I hadn't thought of it
before, but this should save Ocfs2 a good amount of pain.
	--Mark

--
Mark Fasheh

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

* [Ocfs2-devel] [PATCH 11/29] quota: Move quotaio_v[12].h from include/linux/ to fs/
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 11/29] quota: Move quotaio_v[12].h from include/linux/ to fs/ Jan Kara
@ 2008-10-29 23:10   ` Mark Fasheh
  0 siblings, 0 replies; 69+ messages in thread
From: Mark Fasheh @ 2008-10-29 23:10 UTC (permalink / raw)
  To: ocfs2-devel

On Sat, Oct 25, 2008 at 12:08:04AM +0200, Jan Kara wrote:
> Since these include files are used only by implementation of quota formats,
> there's no need to have them in include/linux/.

Also a good idea imho.
	--Mark

--
Mark Fasheh

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

* [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2)
  2008-10-29 22:58 ` Mark Fasheh
@ 2008-10-30  5:02   ` Jan Kara
  2008-10-30 23:32     ` Mark Fasheh
  0 siblings, 1 reply; 69+ messages in thread
From: Jan Kara @ 2008-10-30  5:02 UTC (permalink / raw)
  To: ocfs2-devel

On Wed 29-10-08 15:58:32, Mark Fasheh wrote:
> On Sat, Oct 25, 2008 at 12:05:04AM +0200, Jan Kara wrote:
> > Hello,
> > 
> > the following patch series implements quotas for OCFS2. The patch
> > series is based on:
> > git://git.kernel.org/pub/scm/linux/kernel/git/mfasheh/ocfs2.git linux-next
> 
> Great, thanks for posting these again.
> 
> > I've adressed Joel's comments, also node recovery is now fully working
> > and I've fixed a few issues I found during my testing. So I'm currently
> > not aware of any bugs. Please review, test, comment. Thanks.
> 
> How are we going to handle the vfs patches? I think realistically, they
> should just all go into the merge_window branch of ocfs2.git along with the
> Ocfs2 patches to add quoata support (and make use of the VFS features you
> added). The VFS patches at least though, should probably get posted to
> linux-fsdevel before I pick them up.
  Yup. I don't know if you've noticed but I've already sent majority of VFS
patches to Andrew Morton (linux-fsdevel CC'd) and they are in -mm. He had
some valuable comments and other people also tripped onto various minor
problems. I'm now incorporating all the changes and will send next version
of the patchset when I'm done. Finally, I think it's the easiest if the
patches go to Linus via your tree. Probably I'll push via that path also
64-bit quota format patch although it has nothing to do with OCFS2, since
otherwise we would have to synchronize unnecessarily.

								Honza
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

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

* [Ocfs2-devel] [PATCH 10/29] quota: Introduce DQUOT_QUOTA_SYS_FILE flag
  2008-10-29 23:09   ` Mark Fasheh
@ 2008-10-30  7:24     ` Jan Kara
  0 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-30  7:24 UTC (permalink / raw)
  To: ocfs2-devel

On Wed 29-10-08 16:09:34, Mark Fasheh wrote:
> On Sat, Oct 25, 2008 at 12:08:03AM +0200, Jan Kara wrote:
> > If filesystem can handle quota files as system files hidden from users, we can
> > skip a lot of cache invalidation, syncing, inode flags setting etc. when
> > turning quotas on, off and quota_sync. Allow filesystem to indicate that it is
> > hiding quota files from users by DQUOT_QUOTA_SYS_FILE flag.
> > 
> > Signed-off-by: Jan Kara <jack@suse.cz>
> > ---
> >  fs/dquot.c            |   45 ++++++++++++++++++++++++++++++---------------
> >  fs/quota.c            |    3 +++
> >  include/linux/quota.h |    7 +++++++
> >  3 files changed, 40 insertions(+), 15 deletions(-)
> > 
> > diff --git a/fs/dquot.c b/fs/dquot.c
> > index 96ed45b..5b82722 100644
> > --- a/fs/dquot.c
> > +++ b/fs/dquot.c
> > @@ -1627,6 +1627,11 @@ int vfs_quota_disable(struct super_block *sb, int type, unsigned int flags)
> >  		dqopt->ops[cnt] = NULL;
> >  	}
> >  	mutex_unlock(&dqopt->dqonoff_mutex);
> > +
> > +	/* Skip syncing and setting flags if quota files are hidden */
> > +	if (dqopt->flags & DQUOT_QUOTA_SYS_FILE)
> > +		goto put_inodes;
> > +
> >  	/* Sync the superblock so that buffers with quota data are written to
> >  	 * disk (and so userspace sees correct data afterwards). */
> >  	if (sb->s_op->sync_fs)
> > @@ -1651,6 +1656,12 @@ int vfs_quota_disable(struct super_block *sb, int type, unsigned int flags)
> >  				mark_inode_dirty(toputinode[cnt]);
> >  			}
> >  			mutex_unlock(&dqopt->dqonoff_mutex);
> > +		}
> > +	if (sb->s_bdev)
> > +		invalidate_bdev(sb->s_bdev);
> 
> Is the indentation here weird, or am I just reading this wrong?
  It is:
	for (...)
		if (...) {
			...
		}

  Which looks strange in the patch but otherwise is fine.

> Otherwise btw, this patch is a fantastic idea. I hadn't thought of it
> before, but this should save Ocfs2 a good amount of pain.
  Thanks :).

								Honza
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

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

* [Ocfs2-devel] [PATCH 25/29] ocfs2: Implementation of local and global quota file handling
  2008-10-29 10:51       ` Joel Becker
@ 2008-10-30  7:33         ` Jan Kara
  2008-10-30 20:31           ` Joel Becker
  0 siblings, 1 reply; 69+ messages in thread
From: Jan Kara @ 2008-10-30  7:33 UTC (permalink / raw)
  To: ocfs2-devel

On Wed 29-10-08 03:51:39, Joel Becker wrote:
> On Wed, Oct 29, 2008 at 03:29:02AM +0100, Jan Kara wrote:
> > > > +/* Structure with global user / group information. We reserve some space
> > > > + * for future use. */
> > > > +struct ocfs2_global_disk_dqblk {
> > > > +/*00*/	__le32 dqb_id;          /* ID the structure belongs to */
> > > > +	__le32 dqb_use_count;   /* Number of nodes having reference to this structure */
> > > > +	__le64 dqb_ihardlimit;  /* absolute limit on allocated inodes */
> > > > +/*10*/	__le64 dqb_isoftlimit;  /* preferred inode limit */
> > > > +	__le64 dqb_curinodes;   /* current # allocated inodes */
> > > > +/*20*/	__le64 dqb_bhardlimit;  /* absolute limit on disk space */
> > > > +	__le64 dqb_bsoftlimit;  /* preferred limit on disk space */
> > > > +/*30*/	__le64 dqb_curspace;    /* current space occupied */
> > > > +	__le64 dqb_btime;       /* time limit for excessive disk use */
> > > > +/*40*/	__le64 dqb_itime;       /* time limit for excessive inode use */
> > > > +	__le64 dqb_pad1;
> > > > +/*50*/	__le64 dqb_pad2;
> > > > +};
> > > 
> > > 	We also need space for the ecc data here.  It looks like ecc
> > > space wasn't added to this series.  Rather than comment on each
> > > structure, just let me know when that's ready.
> >   What I did is, that in the end of each quotafile block there are 8 bytes
> > reserved for OCFS2 use (both in local and global quota files). So these
> > bytes are intended to be used for ECC. I hope that's fine.
> 
> 	So every block type (disk header, dq_block, etc) has the last 8
> bytes free?
  Yes.

> This seems like something we should find a way to mention
> in the disk header.
  You mean like in the ocfs2_fs.h file? There is already a constant and
a comment in quota.h, but probably it makes sence to move it to ocfs2_fs.h.

								Honza
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

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

* [Ocfs2-devel] [PATCH 25/29] ocfs2: Implementation of local and global quota file handling
  2008-10-30  7:33         ` Jan Kara
@ 2008-10-30 20:31           ` Joel Becker
  2008-10-31  5:00             ` Jan Kara
  0 siblings, 1 reply; 69+ messages in thread
From: Joel Becker @ 2008-10-30 20:31 UTC (permalink / raw)
  To: ocfs2-devel

On Thu, Oct 30, 2008 at 08:33:39AM +0100, Jan Kara wrote:
> On Wed 29-10-08 03:51:39, Joel Becker wrote:
> > On Wed, Oct 29, 2008 at 03:29:02AM +0100, Jan Kara wrote:
> > >   What I did is, that in the end of each quotafile block there are 8 bytes
> > > reserved for OCFS2 use (both in local and global quota files). So these
> > > bytes are intended to be used for ECC. I hope that's fine.
> > 
> > 	So every block type (disk header, dq_block, etc) has the last 8
> > bytes free?
>   Yes.
> 
> > This seems like something we should find a way to mention
> > in the disk header.
>   You mean like in the ocfs2_fs.h file? There is already a constant and
> a comment in quota.h, but probably it makes sence to move it to ocfs2_fs.h.

	Well, if other filesystems get it via quota.h too, it should
stay there.  Perhaps just a comment in ocfs2_fs.h about it so readers
are aware.

Joel

-- 

 The zen have a saying:
 "When you learn how to listen, ANYONE can be your teacher."

Joel Becker
Principal Software Developer
Oracle
E-mail: joel.becker at oracle.com
Phone: (650) 506-8127

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

* [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2)
  2008-10-30  5:02   ` Jan Kara
@ 2008-10-30 23:32     ` Mark Fasheh
  0 siblings, 0 replies; 69+ messages in thread
From: Mark Fasheh @ 2008-10-30 23:32 UTC (permalink / raw)
  To: ocfs2-devel

On Thu, Oct 30, 2008 at 06:02:54AM +0100, Jan Kara wrote:
> On Wed 29-10-08 15:58:32, Mark Fasheh wrote:
> > On Sat, Oct 25, 2008 at 12:05:04AM +0200, Jan Kara wrote:
> > > Hello,
> > > 
> > > the following patch series implements quotas for OCFS2. The patch
> > > series is based on:
> > > git://git.kernel.org/pub/scm/linux/kernel/git/mfasheh/ocfs2.git linux-next
> > 
> > Great, thanks for posting these again.
> > 
> > > I've adressed Joel's comments, also node recovery is now fully working
> > > and I've fixed a few issues I found during my testing. So I'm currently
> > > not aware of any bugs. Please review, test, comment. Thanks.
> > 
> > How are we going to handle the vfs patches? I think realistically, they
> > should just all go into the merge_window branch of ocfs2.git along with the
> > Ocfs2 patches to add quoata support (and make use of the VFS features you
> > added). The VFS patches at least though, should probably get posted to
> > linux-fsdevel before I pick them up.
>   Yup. I don't know if you've noticed but I've already sent majority of VFS
> patches to Andrew Morton (linux-fsdevel CC'd) and they are in -mm. He had
> some valuable comments and other people also tripped onto various minor
> problems. I'm now incorporating all the changes and will send next version
> of the patchset when I'm done.

Oh, ok great!


> Finally, I think it's the easiest if the patches go to Linus via your
> tree. Probably I'll push via that path also 64-bit quota format patch
> although it has nothing to do with OCFS2, since otherwise we would have to
> synchronize unnecessarily.

Yeah, that sounds good. Once they're all ready, we'll move it over to
ocfs2.git. I certainly don't mind carrying any other patches for you either.
	--Mark

--
Mark Fasheh

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

* [Ocfs2-devel] [PATCH 19/29] ocfs2: Fix check of return value of ocfs2_start_trans()
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 19/29] ocfs2: Fix check of return value of ocfs2_start_trans() Jan Kara
@ 2008-10-30 23:34   ` Mark Fasheh
  0 siblings, 0 replies; 69+ messages in thread
From: Mark Fasheh @ 2008-10-30 23:34 UTC (permalink / raw)
  To: ocfs2-devel

On Sat, Oct 25, 2008 at 12:08:12AM +0200, Jan Kara wrote:
> On failure, ocfs2_start_trans() returns values like ERR_PTR(-ENOMEM).
> Thus checks for !handle are wrong. Fix them to use IS_ERR().

Btw, this is in the 'fixes' branch of ocfs2.git. now.
	--Mark

--
Mark Fasheh

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

* [Ocfs2-devel] [PATCH 21/29] ocfs2: Fix checking of return value of new_inode()
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 21/29] ocfs2: Fix checking of return value of new_inode() Jan Kara
@ 2008-10-30 23:51   ` Mark Fasheh
  0 siblings, 0 replies; 69+ messages in thread
From: Mark Fasheh @ 2008-10-30 23:51 UTC (permalink / raw)
  To: ocfs2-devel

On Sat, Oct 25, 2008 at 12:08:14AM +0200, Jan Kara wrote:
> new_inode() does not return ERR_PTR() but NULL in case of failure. Correct
> checking of the return value.

This one also I'm carrying now.
	--Mark

--
Mark Fasheh

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

* [Ocfs2-devel] [PATCH 22/29] ocfs2: Let inode be really deleted when ocfs2_mknod_locked() fails
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 22/29] ocfs2: Let inode be really deleted when ocfs2_mknod_locked() fails Jan Kara
@ 2008-10-30 23:52   ` Mark Fasheh
  2008-10-31  5:05     ` Jan Kara
  0 siblings, 1 reply; 69+ messages in thread
From: Mark Fasheh @ 2008-10-30 23:52 UTC (permalink / raw)
  To: ocfs2-devel

On Sat, Oct 25, 2008 at 12:08:15AM +0200, Jan Kara wrote:
> We forgot to set i_nlink to 0 when returning due to error from ocfs2_mknod_locked()
> and thus inode was not properly released via ocfs2_delete_inode() (e.g. claimed
> space was not released). Fix it.

By the way, does ocfs2_delete_inode() complain when this can't be found in
the orphan dir?
	--Mark

--
Mark Fasheh

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

* [Ocfs2-devel] [PATCH 25/29] ocfs2: Implementation of local and global quota file handling
  2008-10-30 20:31           ` Joel Becker
@ 2008-10-31  5:00             ` Jan Kara
  0 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-31  5:00 UTC (permalink / raw)
  To: ocfs2-devel

On Thu 30-10-08 13:31:21, Joel Becker wrote:
> On Thu, Oct 30, 2008 at 08:33:39AM +0100, Jan Kara wrote:
> > On Wed 29-10-08 03:51:39, Joel Becker wrote:
> > > On Wed, Oct 29, 2008 at 03:29:02AM +0100, Jan Kara wrote:
> > > >   What I did is, that in the end of each quotafile block there are 8 bytes
> > > > reserved for OCFS2 use (both in local and global quota files). So these
> > > > bytes are intended to be used for ECC. I hope that's fine.
> > > 
> > > 	So every block type (disk header, dq_block, etc) has the last 8
> > > bytes free?
> >   Yes.
> > 
> > > This seems like something we should find a way to mention
> > > in the disk header.
> >   You mean like in the ocfs2_fs.h file? There is already a constant and
> > a comment in quota.h, but probably it makes sence to move it to ocfs2_fs.h.
> 
> 	Well, if other filesystems get it via quota.h too, it should
> stay there.  Perhaps just a comment in ocfs2_fs.h about it so readers
> are aware.
  No - it is OCFS2-specific constant in OCFS2-specific quota header. I've
moved the constant to ocfs2_fs.h since it defines disk format in a way...

								Honza
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

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

* [Ocfs2-devel] [PATCH 22/29] ocfs2: Let inode be really deleted when ocfs2_mknod_locked() fails
  2008-10-30 23:52   ` Mark Fasheh
@ 2008-10-31  5:05     ` Jan Kara
  0 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-10-31  5:05 UTC (permalink / raw)
  To: ocfs2-devel

On Thu 30-10-08 16:52:34, Mark Fasheh wrote:
> On Sat, Oct 25, 2008 at 12:08:15AM +0200, Jan Kara wrote:
> > We forgot to set i_nlink to 0 when returning due to error from ocfs2_mknod_locked()
> > and thus inode was not properly released via ocfs2_delete_inode() (e.g. claimed
> > space was not released). Fix it.
> 
> By the way, does ocfs2_delete_inode() complain when this can't be found in
> the orphan dir?
  If the allocation fails due to quota, inode is not really initialized so
it does not complain. Previously, when I did quota checks after allocation
of space, I saw some complaints about inode not being in orphan dir - yes.
But they are in fact false since we remove the inode in the same
transaction is which we allocated it so it cannot happen that it would be
left allocated in case of crash. So yeah, it might be nice to actually
tweak the code so that we can avoid the warning in case of error cleanup.

								Honza

-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

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

* [Ocfs2-devel] [PATCH 25/29] ocfs2: Implementation of local and global quota file handling
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 25/29] ocfs2: Implementation of local and global quota file handling Jan Kara
  2008-10-28 19:07   ` Joel Becker
  2008-10-28 19:36   ` Joel Becker
@ 2008-11-05 22:49   ` Mark Fasheh
  2008-11-20 14:53     ` Jan Kara
  2 siblings, 1 reply; 69+ messages in thread
From: Mark Fasheh @ 2008-11-05 22:49 UTC (permalink / raw)
  To: ocfs2-devel

On Sat, Oct 25, 2008 at 12:08:18AM +0200, Jan Kara wrote:
> @@ -3450,6 +3485,108 @@ static int ocfs2_dentry_convert_worker(struct ocfs2_lock_res *lockres,
>  	return UNBLOCK_CONTINUE_POST;
>  }
>  
> +static void ocfs2_set_qinfo_lvb(struct ocfs2_lock_res *lockres)
> +{
> +	struct ocfs2_qinfo_lvb *lvb;
> +	struct ocfs2_mem_dqinfo *oinfo = ocfs2_lock_res_qinfo(lockres);
> +	struct mem_dqinfo *info = sb_dqinfo(oinfo->dqi_gi.dqi_sb,
> +					    oinfo->dqi_gi.dqi_type);
> +
> +	mlog_entry_void();
> +
> +	lvb = (struct ocfs2_qinfo_lvb *)ocfs2_dlm_lvb(&lockres->l_lksb);
> +	lvb->lvb_version   = OCFS2_LVB_VERSION;

You might want your own 'OCFS2_QINFO_LVB_VERSION' value. That one is
misnamed in that it actually only governs meta data lvbs. If we changed the
meta data lvb format, we'd want to bump it seperately from the quote lvb
format version and vice-versa. We should probably rename OCFS2_LVB_VERSION
to OCFS2_INODE_LVB_VERSION at some point too, but don't feel like you have
to do that in this series.


> +	lvb->lvb_bgrace = cpu_to_be32(info->dqi_bgrace);
> +	lvb->lvb_igrace = cpu_to_be32(info->dqi_igrace);
> +	lvb->lvb_syncms = cpu_to_be32(oinfo->dqi_syncms);
> +	lvb->lvb_blocks = cpu_to_be32(oinfo->dqi_gi.dqi_blocks);
> +	lvb->lvb_free_blk = cpu_to_be32(oinfo->dqi_gi.dqi_free_blk);
> +	lvb->lvb_free_entry = cpu_to_be32(oinfo->dqi_gi.dqi_free_entry);
> +
> +	mlog_exit_void();
> +}
> +
> +void ocfs2_qinfo_unlock(struct ocfs2_mem_dqinfo *oinfo, int ex)
> +{
> +	struct ocfs2_lock_res *lockres = &oinfo->dqi_gqlock;
> +	struct ocfs2_super *osb = OCFS2_SB(oinfo->dqi_gi.dqi_sb);
> +	int level = ex ? DLM_LOCK_EX : DLM_LOCK_PR;
> +
> +	mlog_entry_void();
> +	if (!ocfs2_is_hard_readonly(osb) && !ocfs2_mount_local(osb))
> +		ocfs2_cluster_unlock(osb, lockres, level);
> +	mlog_exit_void();
> +}
> +
> +/* Lock quota info, this function expects at least shared lock on the quota file
> + * so that we can safely refresh quota info from disk. */
> +int ocfs2_qinfo_lock(struct ocfs2_mem_dqinfo *oinfo, int ex)
> +{
> +	struct mem_dqinfo *info = sb_dqinfo(oinfo->dqi_gi.dqi_sb,
> +					    oinfo->dqi_gi.dqi_type);
> +	struct ocfs2_lock_res *lockres = &oinfo->dqi_gqlock;
> +	struct ocfs2_super *osb = OCFS2_SB(oinfo->dqi_gi.dqi_sb);
> +	struct ocfs2_qinfo_lvb *lvb;
> +	int level = ex ? DLM_LOCK_EX : DLM_LOCK_PR;
> +	int status = 0;
> +	struct buffer_head *bh;
> +	struct ocfs2_global_disk_dqinfo *gdinfo;
> +
> +	mlog_entry_void();
> +
> +	/* We'll allow faking a readonly metadata lock for
> +	 * rodevices. */

You might want to update this comment   ;)


> +	if (ocfs2_is_hard_readonly(osb)) {
> +		if (ex)
> +			status = -EROFS;
> +		goto bail;
> +	}
> +	if (ocfs2_mount_local(osb))
> +		goto bail;
> +
> +	status = ocfs2_cluster_lock(osb, lockres, level, 0, 0);
> +	if (status < 0) {
> +		mlog_errno(status);
> +		goto bail;
> +	}
> +	if (!ocfs2_should_refresh_lock_res(lockres))
> +		goto bail;
> +	/* OK, we have the lock but we need to refresh the quota info */
> +	lvb = ocfs2_dlm_lvb(&lockres->l_lksb);
> +	if (lvb->lvb_version == OCFS2_LVB_VERSION) {
> +		info->dqi_bgrace = be32_to_cpu(lvb->lvb_bgrace);
> +		info->dqi_igrace = be32_to_cpu(lvb->lvb_igrace);
> +		oinfo->dqi_syncms = be32_to_cpu(lvb->lvb_syncms);
> +		oinfo->dqi_gi.dqi_blocks = be32_to_cpu(lvb->lvb_blocks);
> +		oinfo->dqi_gi.dqi_free_blk = be32_to_cpu(lvb->lvb_free_blk);
> +		oinfo->dqi_gi.dqi_free_entry =
> +					be32_to_cpu(lvb->lvb_free_entry);
> +	} else {
> +		bh = ocfs2_read_quota_block(oinfo->dqi_gqinode, 0, &status);
> +		if (!bh) {
> +			ocfs2_qinfo_unlock(oinfo, ex);
> +			mlog_errno(status);
> +			goto bail_refresh;
> +		}
> +		gdinfo = (struct ocfs2_global_disk_dqinfo *)
> +					(bh->b_data + OCFS2_GLOBAL_INFO_OFF);
> +		info->dqi_bgrace = le32_to_cpu(gdinfo->dqi_bgrace);
> +		info->dqi_igrace = le32_to_cpu(gdinfo->dqi_igrace);
> +		oinfo->dqi_syncms = le32_to_cpu(gdinfo->dqi_syncms);
> +		oinfo->dqi_gi.dqi_blocks = le32_to_cpu(gdinfo->dqi_blocks);
> +		oinfo->dqi_gi.dqi_free_blk = le32_to_cpu(gdinfo->dqi_free_blk);
> +		oinfo->dqi_gi.dqi_free_entry =
> +					le32_to_cpu(gdinfo->dqi_free_entry);
> +		brelse(bh);
> +		ocfs2_track_lock_refresh(lockres);
> +	}
> +bail_refresh:
> +	ocfs2_complete_lock_res_refresh(lockres, status);

Can we put the refresh logic in another function, please?


> diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h
> index dd17137..04bebd2 100644
> --- a/fs/ocfs2/ocfs2_fs.h
> +++ b/fs/ocfs2/ocfs2_fs.h
> @@ -878,6 +878,101 @@ static inline int ocfs2_xattr_get_type(struct ocfs2_xattr_entry *xe)
>  	return xe->xe_type & OCFS2_XATTR_TYPE_MASK;
>  }
>  
> +/*
> + *  On disk structures for global quota file
> + */
> +
> +/* Magic numbers and known versions for global quota files */
> +#define OCFS2_GLOBAL_QMAGICS {\
> +	0x0cf52470, /* USRQUOTA */ \
> +	0x0cf52471  /* GRPQUOTA */ \
> +}
> +
> +#define OCFS2_GLOBAL_QVERSIONS {\
> +	0, \
> +	0, \
> +}
> +
> +/* Generic header of all quota files */
> +struct ocfs2_disk_dqheader {
> +	__le32 dqh_magic;	/* Magic number identifying file */
> +	__le32 dqh_version;	/* Quota format version */
> +};
> +
> +#define OCFS2_GLOBAL_INFO_OFF (sizeof(struct ocfs2_disk_dqheader))
> +
> +/* Information header of global quota file (immediately follows the generic
> + * header) */
> +struct ocfs2_global_disk_dqinfo {
> +/*00*/	__le32 dqi_bgrace;
> +	__le32 dqi_igrace;
> +	__le32 dqi_syncms;
> +	__le32 dqi_blocks;
> +/*10*/	__le32 dqi_free_blk;
> +	__le32 dqi_free_entry;
> +};

Can we get some comments explaining the fields in this structure? I had to
search around to get the idea behind some of them...


> +/* Structure with global user / group information. We reserve some space
> + * for future use. */
> +struct ocfs2_global_disk_dqblk {
> +/*00*/	__le32 dqb_id;          /* ID the structure belongs to */
> +	__le32 dqb_use_count;   /* Number of nodes having reference to this structure */
> +	__le64 dqb_ihardlimit;  /* absolute limit on allocated inodes */
> +/*10*/	__le64 dqb_isoftlimit;  /* preferred inode limit */
> +	__le64 dqb_curinodes;   /* current # allocated inodes */
> +/*20*/	__le64 dqb_bhardlimit;  /* absolute limit on disk space */
> +	__le64 dqb_bsoftlimit;  /* preferred limit on disk space */
> +/*30*/	__le64 dqb_curspace;    /* current space occupied */
> +	__le64 dqb_btime;       /* time limit for excessive disk use */
> +/*40*/	__le64 dqb_itime;       /* time limit for excessive inode use */
> +	__le64 dqb_pad1;
> +/*50*/	__le64 dqb_pad2;
> +};
> +
> +/*
> + *  On-disk structures for local quota file
> + */
> +
> +/* Magic numbers and known versions for local quota files */
> +#define OCFS2_LOCAL_QMAGICS {\
> +	0x0cf524c0, /* USRQUOTA */ \
> +	0x0cf524c1  /* GRPQUOTA */ \
> +}
> +
> +#define OCFS2_LOCAL_QVERSIONS {\
> +	0, \
> +	0, \
> +}
> +
> +/* Quota flags in dqinfo header */
> +#define OLQF_CLEAN	0x0001	/* Quota file is empty (this should be after\
> +				 * quota has been cleanly turned off) */
> +
> +#define OCFS2_LOCAL_INFO_OFF (sizeof(struct ocfs2_disk_dqheader))
> +
> +/* Information header of local quota file (immediately follows the generic
> + * header) */
> +struct ocfs2_local_disk_dqinfo {
> +	__le32 dqi_flags;	/* Flags for quota file */
> +	__le32 dqi_chunks;	/* Number of chunks of quota structures
> +				 * with a bitmap */
> +	__le32 dqi_blocks;	/* Number of blocks allocated for quota file */
> +};
> +
> +/* Header of one chunk of a quota file */
> +struct ocfs2_local_disk_chunk {
> +	__le32 dqc_free;	/* Number of free entries in the bitmap */
> +	u8 dqc_bitmap[0];	/* Bitmap of entries in the corresponding
> +				 * chunk of quota file */
> +};
> +
> +/* One entry in local quota file */
> +struct ocfs2_local_disk_dqblk {
> +/*00*/	__le64 dqb_id;		/* id this quota applies to */
> +	__le64 dqb_spacemod;	/* Change in the amount of used space */
> +/*10*/	__le64 dqb_inodemod;	/* Change in the amount of used inodes */
> +};
> +
>  #ifdef __KERNEL__
>  static inline int ocfs2_fast_symlink_chars(struct super_block *sb)
>  {
> diff --git a/fs/ocfs2/ocfs2_lockid.h b/fs/ocfs2/ocfs2_lockid.h
> index 82c200f..eb6f50c 100644
> --- a/fs/ocfs2/ocfs2_lockid.h
> +++ b/fs/ocfs2/ocfs2_lockid.h
> @@ -46,6 +46,7 @@ enum ocfs2_lock_type {
>  	OCFS2_LOCK_TYPE_DENTRY,
>  	OCFS2_LOCK_TYPE_OPEN,
>  	OCFS2_LOCK_TYPE_FLOCK,
> +	OCFS2_LOCK_TYPE_QINFO,
>  	OCFS2_NUM_LOCK_TYPES
>  };
>  
> @@ -77,6 +78,9 @@ static inline char ocfs2_lock_type_char(enum ocfs2_lock_type type)
>  		case OCFS2_LOCK_TYPE_FLOCK:
>  			c = 'F';
>  			break;
> +		case OCFS2_LOCK_TYPE_QINFO:
> +			c = 'Q';
> +			break;
>  		default:
>  			c = '\0';
>  	}
> @@ -95,6 +99,7 @@ static char *ocfs2_lock_type_strings[] = {
>  	[OCFS2_LOCK_TYPE_DENTRY] = "Dentry",
>  	[OCFS2_LOCK_TYPE_OPEN] = "Open",
>  	[OCFS2_LOCK_TYPE_FLOCK] = "Flock",
> +	[OCFS2_LOCK_TYPE_QINFO] = "Quota",
>  };
>  
>  static inline const char *ocfs2_lock_type_string(enum ocfs2_lock_type type)
> diff --git a/fs/ocfs2/quota.h b/fs/ocfs2/quota.h
> new file mode 100644
> index 0000000..87545ca
> --- /dev/null
> +++ b/fs/ocfs2/quota.h
> @@ -0,0 +1,97 @@
> +/*
> + * quota.h for OCFS2
> + *
> + * On disk quota structures for local and global quota file, in-memory
> + * structures.
> + *
> + */
> +
> +#ifndef _OCFS2_QUOTA_H
> +#define _OCFS2_QUOTA_H
> +
> +#include <linux/types.h>
> +#include <linux/slab.h>
> +#include <linux/quota.h>
> +#include <linux/list.h>
> +#include <linux/dqblk_qtree.h>
> +
> +#include "ocfs2.h"
> +
> +/* Common stuff */
> +/* id number of quota format */
> +#define QFMT_OCFS2 3
> +
> +/* How many bytes to we reserve in each quota file block for our internal
> + * purposes? E.g. checksums... */
> +#define OCFS2_QBLK_RESERVED_SPACE 8
> +
> +/*
> + * In-memory structures
> + */
> +struct ocfs2_dquot {
> +	struct dquot dq_dquot;	/* Generic VFS dquot */
> +	loff_t dq_local_off;	/* Offset in the local quota file */
> +	struct ocfs2_quota_chunk *dq_chunk;	/* Chunk dquot is in */
> +	unsigned int dq_use_count;	/* Number of nodes having reference to this entry in global quota file */
> +	s64 dq_origspace;	/* Last globally synced space usage */
> +	s64 dq_originodes;	/* Last globally synced inode usage */
> +};
> +
> +/* In-memory structure with quota header information */
> +struct ocfs2_mem_dqinfo {
> +	unsigned int dqi_type;		/* Quota type this structure describes */
> +	unsigned int dqi_chunks;	/* Number of chunks in local quota file */
> +	unsigned int dqi_blocks;	/* Number of blocks allocated for local quota file */
> +	unsigned int dqi_syncms;	/* How often should we sync with other nodes */
> +	struct list_head dqi_chunk;	/* List of chunks */
> +	struct inode *dqi_gqinode;	/* Global quota file inode */
> +	struct ocfs2_lock_res dqi_gqlock;	/* Lock protecting quota information structure */
> +	struct buffer_head *dqi_gqi_bh;	/* Buffer head with global quota file inode - set only if inode lock is obtained */
> +	int dqi_gqi_count;		/* Number of holders of dqi_gqi_bh */
> +	struct buffer_head *dqi_lqi_bh;	/* Buffer head with local quota file inode */
> +	struct buffer_head *dqi_ibh;	/* Buffer with information header */
> +	struct qtree_mem_dqinfo dqi_gi;	/* Info about global file */
> +};
> +
> +static inline struct ocfs2_dquot *OCFS2_DQUOT(struct dquot *dquot)
> +{
> +	return container_of(dquot, struct ocfs2_dquot, dq_dquot);
> +}
> +
> +struct ocfs2_quota_chunk {
> +	struct list_head qc_chunk;	/* List of quotafile chunks */
> +	int qc_num;			/* Number of quota chunk */
> +	struct buffer_head *qc_headerbh;	/* Buffer head with chunk header */
> +};
> +
> +extern struct kmem_cache *ocfs2_dquot_cachep;
> +extern struct kmem_cache *ocfs2_qf_chunk_cachep;
> +
> +extern struct qtree_fmt_operations ocfs2_global_ops;
> +
> +ssize_t ocfs2_quota_read(struct super_block *sb, int type, char *data,
> +			 size_t len, loff_t off);
> +ssize_t ocfs2_quota_write(struct super_block *sb, int type,
> +			  const char *data, size_t len, loff_t off);
> +int ocfs2_global_read_info(struct super_block *sb, int type);
> +int ocfs2_global_write_info(struct super_block *sb, int type);
> +int ocfs2_global_read_dquot(struct dquot *dquot);
> +int __ocfs2_sync_dquot(struct dquot *dquot, int freeing);
> +static inline int ocfs2_sync_dquot(struct dquot *dquot)
> +{
> +	return __ocfs2_sync_dquot(dquot, 0);
> +}
> +static inline int ocfs2_global_release_dquot(struct dquot *dquot)
> +{
> +	return __ocfs2_sync_dquot(dquot, 1);
> +}
> +
> +int ocfs2_lock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex);
> +void ocfs2_unlock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex);
> +struct buffer_head *ocfs2_read_quota_block(struct inode *inode,
> +					   int block, int *err);
> +
> +extern struct dquot_operations ocfs2_quota_operations;
> +extern struct quota_format_type ocfs2_quota_format;
> +
> +#endif /* _OCFS2_QUOTA_H */
> diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c
> new file mode 100644
> index 0000000..b937f07
> --- /dev/null
> +++ b/fs/ocfs2/quota_global.c
> @@ -0,0 +1,863 @@
> +/*
> + *  Implementation of operations over global quota file
> + */
> +#include <linux/fs.h>
> +#include <linux/quota.h>
> +#include <linux/quotaops.h>
> +#include <linux/dqblk_qtree.h>
> +
> +#define MLOG_MASK_PREFIX ML_QUOTA
> +#include <cluster/masklog.h>
> +
> +#include "ocfs2_fs.h"
> +#include "ocfs2.h"
> +#include "alloc.h"
> +#include "inode.h"
> +#include "journal.h"
> +#include "file.h"
> +#include "sysfile.h"
> +#include "dlmglue.h"
> +#include "quota.h"
> +
> +static void ocfs2_global_disk2memdqb(struct dquot *dquot, void *dp)
> +{
> +	struct ocfs2_global_disk_dqblk *d = dp;
> +	struct mem_dqblk *m = &dquot->dq_dqb;
> +
> +	/* Update from disk only entries not set by the admin */
> +	if (!test_bit(DQ_LASTSET_B + QIF_ILIMITS_B, &dquot->dq_flags)) {
> +		m->dqb_ihardlimit = le64_to_cpu(d->dqb_ihardlimit);
> +		m->dqb_isoftlimit = le64_to_cpu(d->dqb_isoftlimit);
> +	}
> +	if (!test_bit(DQ_LASTSET_B + QIF_INODES_B, &dquot->dq_flags))
> +		m->dqb_curinodes = le64_to_cpu(d->dqb_curinodes);
> +	if (!test_bit(DQ_LASTSET_B + QIF_BLIMITS_B, &dquot->dq_flags)) {
> +		m->dqb_bhardlimit = le64_to_cpu(d->dqb_bhardlimit);
> +		m->dqb_bsoftlimit = le64_to_cpu(d->dqb_bsoftlimit);
> +	}
> +	if (!test_bit(DQ_LASTSET_B + QIF_SPACE_B, &dquot->dq_flags))
> +		m->dqb_curspace = le64_to_cpu(d->dqb_curspace);
> +	if (!test_bit(DQ_LASTSET_B + QIF_BTIME_B, &dquot->dq_flags))
> +		m->dqb_btime = le64_to_cpu(d->dqb_btime);
> +	if (!test_bit(DQ_LASTSET_B + QIF_ITIME_B, &dquot->dq_flags))
> +		m->dqb_itime = le64_to_cpu(d->dqb_itime);
> +	OCFS2_DQUOT(dquot)->dq_use_count = le32_to_cpu(d->dqb_use_count);
> +}
> +
> +static void ocfs2_global_mem2diskdqb(void *dp, struct dquot *dquot)
> +{
> +	struct ocfs2_global_disk_dqblk *d = dp;
> +	struct mem_dqblk *m = &dquot->dq_dqb;
> +
> +	d->dqb_id = cpu_to_le32(dquot->dq_id);
> +	d->dqb_use_count = cpu_to_le32(OCFS2_DQUOT(dquot)->dq_use_count);
> +	d->dqb_ihardlimit = cpu_to_le64(m->dqb_ihardlimit);
> +	d->dqb_isoftlimit = cpu_to_le64(m->dqb_isoftlimit);
> +	d->dqb_curinodes = cpu_to_le64(m->dqb_curinodes);
> +	d->dqb_bhardlimit = cpu_to_le64(m->dqb_bhardlimit);
> +	d->dqb_bsoftlimit = cpu_to_le64(m->dqb_bsoftlimit);
> +	d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
> +	d->dqb_btime = cpu_to_le64(m->dqb_btime);
> +	d->dqb_itime = cpu_to_le64(m->dqb_itime);
> +	d->dqb_pad1 = d->dqb_pad2 = 0;

Just to be clear - do we definitely want to reset dqb_pad1 and dqb_pad2
every time the header is written? This means that a future version of the
quota code can't put values there without having them overwritten by nodes
which don't understand the new fields...


> +/* Write to quotafile (we know the transaction is already started and has
> + * enough credits) */
> +ssize_t ocfs2_quota_write(struct super_block *sb, int type,
> +			  const char *data, size_t len, loff_t off)
> +{
> +	struct mem_dqinfo *info = sb_dqinfo(sb, type);
> +	struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
> +	struct inode *gqinode = oinfo->dqi_gqinode;
> +	int offset = off & (sb->s_blocksize - 1);
> +	sector_t blk = off >> sb->s_blocksize_bits;
> +	int err = 0;
> +	struct buffer_head *bh;
> +	handle_t *handle = journal_current_handle();
> +	size_t tocopy, towrite = len;
> +
> +	if (!handle) {
> +		mlog(ML_ERROR, "Quota write (off=%llu, len=%llu) cancelled "
> +		     "because transaction was not started.\n",
> +		     (unsigned long long)off, (unsigned long long)len);
> +		return -EIO;
> +	}
> +	mutex_lock_nested(&gqinode->i_mutex, I_MUTEX_QUOTA);
> +	if (gqinode->i_size < off + len) {
> +		down_write(&OCFS2_I(gqinode)->ip_alloc_sem);
> +		err = ocfs2_extend_no_holes(gqinode, off + len, off);
> +		up_write(&OCFS2_I(gqinode)->ip_alloc_sem);
> +		if (err < 0)
> +			goto out;
> +		err = ocfs2_simple_size_update(gqinode,
> +					       oinfo->dqi_gqi_bh,
> +					       off + len);
> +		if (err < 0)
> +			goto out;

Is it safe if we crash / error here, after the size has been extended but
before we've written into the newly allocated blocks? In particular, could
we wind up reading those blocks later and wrongly interpreting whatever data
happens to be there? Hmm ok, I guess ocfs2_zero_extend() from
ocfs2_extend_no_holes() fixes that for you, but it's using the page cache.
Does that interact reasonably with what we're doing below?


> +	}
> +	WARN_ON(off >> sb->s_blocksize_bits != \
> +		(off + len) >> sb->s_blocksize_bits);
> +	WARN_ON(((off + len) & ((1 << sb->s_blocksize_bits) - 1)) >
> +		sb->s_blocksize - OCFS2_QBLK_RESERVED_SPACE);
> +	for (towrite = len; towrite > 0; towrite -= tocopy) {
> +		tocopy = min(towrite, (size_t)(sb->s_blocksize - offset));
> +		bh = ocfs2_read_quota_block(gqinode, blk, &err);
> +		if (!bh) {
> +			mlog_errno(err);
> +			return err;
> +		}
> +		err = ocfs2_journal_access(handle, gqinode, bh,
> +						OCFS2_JOURNAL_ACCESS_WRITE);
> +		if (err < 0) {
> +			brelse(bh);
> +			goto out;
> +		}

We can optimize away the disk read if the block is newly allocated. There's
no need to read it off disk, so we can just sb_getblk() it. Likewise, you'd
want to use OCFS2_JOURNAL_ACCESS_CREATE for those. How often does the quota
file get extended though? If it's sufficiently rare, we could save this for
later. Otherwise, it's probably worth the effort imho.
	--Mark

--
Mark Fasheh

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

* [Ocfs2-devel] [PATCH 26/29] ocfs2: Add quota calls for allocation and freeing of inodes and space
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 26/29] ocfs2: Add quota calls for allocation and freeing of inodes and space Jan Kara
@ 2008-11-06  0:06   ` Mark Fasheh
  2008-11-20 15:19     ` Jan Kara
  0 siblings, 1 reply; 69+ messages in thread
From: Mark Fasheh @ 2008-11-06  0:06 UTC (permalink / raw)
  To: ocfs2-devel

On Sat, Oct 25, 2008 at 12:08:19AM +0200, Jan Kara wrote:
> diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
> index 9af16e0..75cdb0e 100644
> --- a/fs/ocfs2/file.c
> +++ b/fs/ocfs2/file.c
> @@ -35,6 +35,7 @@
>  #include <linux/mount.h>
>  #include <linux/writeback.h>
>  #include <linux/falloc.h>
> +#include <linux/quotaops.h>
>  
>  #define MLOG_MASK_PREFIX ML_INODE
>  #include <cluster/masklog.h>
> @@ -56,6 +57,7 @@
>  #include "suballoc.h"
>  #include "super.h"
>  #include "xattr.h"
> +#include "quota.h"
>  
>  #include "buffer_head_io.h"
>  
> @@ -536,6 +538,8 @@ static int __ocfs2_extend_allocation(struct inode *inode, u32 logical_start,
>  	enum ocfs2_alloc_restarted why;
>  	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
>  	struct ocfs2_extent_tree et;
> +	u32 total_clusters = clusters_to_add;
> +	int did_quota = 0;
>  
>  	mlog_entry("(clusters_to_add = %u)\n", clusters_to_add);
>  
> @@ -584,6 +588,12 @@ restart_all:
>  		goto leave;
>  	}
>  
> +	if (!did_quota && vfs_dq_alloc_space_nodirty(inode,
> +	    ocfs2_clusters_to_bytes(osb->sb, total_clusters))) {
> +		status = -EDQUOT;
> +		goto leave;
> +	}
> +	did_quota = 1;
>  restarted_transaction:
>  	/* reserve a write to the file entry early on - that we if we
>  	 * run out of credits in the allocation path, we can still
> @@ -654,6 +664,9 @@ restarted_transaction:
>  	     OCFS2_I(inode)->ip_clusters, (long long)i_size_read(inode));
>  
>  leave:
> +	if (status < 0 && did_quota)
> +		vfs_dq_free_space(inode,
> +			ocfs2_clusters_to_bytes(osb->sb, total_clusters));

Shouldn't you subtract clusters_to_add clusters instead of total_clusters
here? In theory, we could have allocated *some* of the inode but failed
during a later pass.



> @@ -956,11 +972,47 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
>  		}
>  	}
>  
> -	handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
> -	if (IS_ERR(handle)) {
> -		status = PTR_ERR(handle);
> -		mlog_errno(status);
> -		goto bail_unlock;
> +	if ((attr->ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) ||
> +	    (attr->ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) {
> +		credits = OCFS2_INODE_UPDATE_CREDITS;
> +		if (attr->ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid
> +		    && OCFS2_HAS_RO_COMPAT_FEATURE(sb,
> +		    OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) {
> +			oinfo = sb_dqinfo(sb, USRQUOTA)->dqi_priv;
> +			status = ocfs2_lock_global_qf(oinfo, 1);
> +			if (status < 0)
> +				goto bail_unlock;
> +			credits += ocfs2_calc_qinit_credits(sb, USRQUOTA) +
> +				ocfs2_calc_qdel_credits(sb, USRQUOTA);
> +			locked[USRQUOTA] = 1;
> +		}
> +		if (attr->ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid
> +		    && OCFS2_HAS_RO_COMPAT_FEATURE(sb,
> +		    OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) {
> +			oinfo = sb_dqinfo(sb, GRPQUOTA)->dqi_priv;
> +			status = ocfs2_lock_global_qf(oinfo, 1);
> +			if (status < 0)
> +				goto bail_unlock;
> +			credits += ocfs2_calc_qinit_credits(sb, GRPQUOTA) +
> +				   ocfs2_calc_qdel_credits(sb, GRPQUOTA);
> +			locked[GRPQUOTA] = 1;
> +		}
> +		handle = ocfs2_start_trans(osb, credits);
> +		if (IS_ERR(handle)) {
> +			status = PTR_ERR(handle);
> +			mlog_errno(status);
> +			goto bail_unlock;
> +		}
> +		status = vfs_dq_transfer(inode, attr) ? -EDQUOT : 0;
> +		if (status < 0)
> +			goto bail_commit;

Ok, so this will all cause syncing on the global quota file because we need
to transfer one record to/from another? Is there any better way?

Rest of the patch looks great btw.
	--Mark

--
Mark Fasheh

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

* [Ocfs2-devel] [PATCH 27/29] ocfs2: Implement quota syncing thread
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 27/29] ocfs2: Implement quota syncing thread Jan Kara
@ 2008-11-06  0:27   ` Mark Fasheh
  0 siblings, 0 replies; 69+ messages in thread
From: Mark Fasheh @ 2008-11-06  0:27 UTC (permalink / raw)
  To: ocfs2-devel

On Sat, Oct 25, 2008 at 12:08:20AM +0200, Jan Kara wrote:
> This patch implements functions and timer setup which handles periodic
> syncing of locally cached quota information to global quota file.

This too looks good.
	--Mark

--
Mark Fasheh

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

* [Ocfs2-devel] [PATCH 28/29] ocfs2: Implement quota recovery
  2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 28/29] ocfs2: Implement quota recovery Jan Kara
@ 2008-11-06  0:52   ` Mark Fasheh
  2008-11-20 16:51     ` Jan Kara
  0 siblings, 1 reply; 69+ messages in thread
From: Mark Fasheh @ 2008-11-06  0:52 UTC (permalink / raw)
  To: ocfs2-devel

On Sat, Oct 25, 2008 at 12:08:21AM +0200, Jan Kara wrote:
> Implement functions for recovery after a crash. Functions just
> read local quota file and sync info to global quota file.
> 
> Signed-off-by: Jan Kara <jack@suse.cz>
> ---
>  fs/ocfs2/journal.c      |  105 +++++++++---
>  fs/ocfs2/journal.h      |    1 +
>  fs/ocfs2/ocfs2.h        |    4 +-
>  fs/ocfs2/quota.h        |   21 +++
>  fs/ocfs2/quota_global.c |    1 -
>  fs/ocfs2/quota_local.c  |  430 ++++++++++++++++++++++++++++++++++++++++++++++-
>  6 files changed, 530 insertions(+), 32 deletions(-)
> 
> diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c
> index f3d7c15..d928db9 100644
> --- a/fs/ocfs2/journal.c
> +++ b/fs/ocfs2/journal.c
> @@ -45,6 +45,7 @@
>  #include "slot_map.h"
>  #include "super.h"
>  #include "sysfile.h"
> +#include "quota.h"
>  
>  #include "buffer_head_io.h"
>  
> @@ -52,7 +53,7 @@ DEFINE_SPINLOCK(trans_inc_lock);
>  
>  static int ocfs2_force_read_journal(struct inode *inode);
>  static int ocfs2_recover_node(struct ocfs2_super *osb,
> -			      int node_num);
> +			      int node_num, int slot_num);
>  static int __ocfs2_recovery_thread(void *arg);
>  static int ocfs2_commit_cache(struct ocfs2_super *osb);
>  static int ocfs2_wait_on_mount(struct ocfs2_super *osb);
> @@ -877,6 +878,7 @@ struct ocfs2_la_recovery_item {
>  	int			lri_slot;
>  	struct ocfs2_dinode	*lri_la_dinode;
>  	struct ocfs2_dinode	*lri_tl_dinode;
> +	struct ocfs2_quota_recovery *lri_qrec;
>  };
>  
>  /* Does the second half of the recovery process. By this point, the
> @@ -897,6 +899,7 @@ void ocfs2_complete_recovery(struct work_struct *work)
>  	struct ocfs2_super *osb = journal->j_osb;
>  	struct ocfs2_dinode *la_dinode, *tl_dinode;
>  	struct ocfs2_la_recovery_item *item, *n;
> +	struct ocfs2_quota_recovery *qrec;
>  	LIST_HEAD(tmp_la_list);
>  
>  	mlog_entry_void();
> @@ -942,6 +945,16 @@ void ocfs2_complete_recovery(struct work_struct *work)
>  		if (ret < 0)
>  			mlog_errno(ret);
>  
> +		qrec = item->lri_qrec;
> +		if (qrec) {
> +			mlog(0, "Recovering quota files");
> +			ret = ocfs2_finish_quota_recovery(osb, qrec,
> +							  item->lri_slot);
> +			if (ret < 0)
> +				mlog_errno(ret);
> +			/* Recovery info is already freed now */
> +		}
> +
>  		kfree(item);
>  	}
>  
> @@ -955,7 +968,8 @@ void ocfs2_complete_recovery(struct work_struct *work)
>  static void ocfs2_queue_recovery_completion(struct ocfs2_journal *journal,
>  					    int slot_num,
>  					    struct ocfs2_dinode *la_dinode,
> -					    struct ocfs2_dinode *tl_dinode)
> +					    struct ocfs2_dinode *tl_dinode,
> +					    struct ocfs2_quota_recovery *qrec)
>  {
>  	struct ocfs2_la_recovery_item *item;
>  
> @@ -970,6 +984,9 @@ static void ocfs2_queue_recovery_completion(struct ocfs2_journal *journal,
>  		if (tl_dinode)
>  			kfree(tl_dinode);
>  
> +		if (qrec)
> +			ocfs2_free_quota_recovery(qrec);
> +
>  		mlog_errno(-ENOMEM);
>  		return;
>  	}
> @@ -978,6 +995,7 @@ static void ocfs2_queue_recovery_completion(struct ocfs2_journal *journal,
>  	item->lri_la_dinode = la_dinode;
>  	item->lri_slot = slot_num;
>  	item->lri_tl_dinode = tl_dinode;
> +	item->lri_qrec = qrec;
>  
>  	spin_lock(&journal->j_lock);
>  	list_add_tail(&item->lri_list, &journal->j_la_cleanups);
> @@ -997,6 +1015,7 @@ void ocfs2_complete_mount_recovery(struct ocfs2_super *osb)
>  		ocfs2_queue_recovery_completion(journal,
>  						osb->slot_num,
>  						osb->local_alloc_copy,
> +						NULL,
>  						NULL);
>  		ocfs2_schedule_truncate_log_flush(osb, 0);
>  
> @@ -1005,11 +1024,26 @@ void ocfs2_complete_mount_recovery(struct ocfs2_super *osb)
>  	}
>  }
>  
> +void ocfs2_complete_quota_recovery(struct ocfs2_super *osb)
> +{
> +	if (osb->quota_rec) {
> +		ocfs2_queue_recovery_completion(osb->journal,
> +						osb->slot_num,
> +						NULL,
> +						NULL,
> +						osb->quota_rec);
> +		osb->quota_rec = NULL;
> +	}
> +}
> +
>  static int __ocfs2_recovery_thread(void *arg)
>  {
> -	int status, node_num;
> +	int status, node_num, slot_num;
>  	struct ocfs2_super *osb = arg;
>  	struct ocfs2_recovery_map *rm = osb->recovery_map;
> +	int *rm_quota = NULL;
> +	int rm_quota_used = 0, i;
> +	struct ocfs2_quota_recovery *qrec;
>  
>  	mlog_entry_void();
>  
> @@ -1018,6 +1052,11 @@ static int __ocfs2_recovery_thread(void *arg)
>  		goto bail;
>  	}
>  
> +	rm_quota = kzalloc(osb->max_slots * sizeof(int), GFP_NOFS);
> +	if (!rm_quota) {
> +		status = -ENOMEM;
> +		goto bail;
> +	}
>  restart:
>  	status = ocfs2_super_lock(osb, 1);
>  	if (status < 0) {
> @@ -1031,8 +1070,28 @@ restart:
>  		 * clear it until ocfs2_recover_node() has succeeded. */
>  		node_num = rm->rm_entries[0];
>  		spin_unlock(&osb->osb_lock);
> -
> -		status = ocfs2_recover_node(osb, node_num);
> +		mlog(0, "checking node %d\n", node_num);
> +		slot_num = ocfs2_node_num_to_slot(osb, node_num);
> +		if (slot_num == -ENOENT) {
> +			status = 0;
> +			mlog(0, "no slot for this node, so no recovery"
> +			     "required.\n");
> +			goto skip_recovery;
> +		}
> +		mlog(0, "node %d was using slot %d\n", node_num, slot_num);
> +
> +		/* It is a bit subtle with quota recovery. We cannot do it
> +		 * immediately because we have to obtain cluster locks from
> +		 * quota files and we also don't want to just skip it because
> +		 * then quota usage would be out of sync until some node takes
> +		 * the slot. So we remember which nodes need quota recovery
> +		 * and when everything else is done, we recover quotas. */
> +		for (i = 0; i < rm_quota_used && rm_quota[i] != slot_num; i++);
> +		if (i == rm_quota_used)
> +			rm_quota[rm_quota_used++] = slot_num;
> +
> +		status = ocfs2_recover_node(osb, node_num, slot_num);
> +skip_recovery:
>  		if (!status) {
>  			ocfs2_recovery_map_clear(osb, node_num);
>  		} else {
> @@ -1056,11 +1115,22 @@ restart:
>  
>  	ocfs2_super_unlock(osb, 1);
>  
> +	/* Now it is right time to recover quotas... */
> +	for (i = 0; i < rm_quota_used; i++) {
> +		qrec = ocfs2_begin_quota_recovery(osb, rm_quota[i]);
> +		if (IS_ERR(qrec)) {
> +			status = PTR_ERR(qrec);
> +			mlog_errno(status);
> +		}
> +		ocfs2_queue_recovery_completion(osb->journal, rm_quota[i],
> +						NULL, NULL, qrec);
> +	}

I think we want to do this *within* the super block cluster lock. What if a
node mounts and then crashes (with quota file changes in it's journal) in
between ocfs2_super_unlock(osb, 1) and the call in
ocfs2_begin_quota_recovery()? We'd get the cluster lock, but the quota file
info would be stale because we'd have an unrecovered slot.


Also, we call ocfs2_queue_recovery_completion() here even when quota isn't
enabled on the file system. Perhaps ocfs2_begin_quota_recovery() should
return NULL in that case and we can skip the recovery_completion. Otherwise
I think we're just running it against an empty qrec, which seems like a bug
too.


> +/* Load information we need for quota recovery into memory */
> +struct ocfs2_quota_recovery *ocfs2_begin_quota_recovery(
> +						struct ocfs2_super *osb,
> +						int slot_num)
> +{
> +	unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
> +					    OCFS2_FEATURE_RO_COMPAT_GRPQUOTA};
> +	unsigned int ino[MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE,
> +					LOCAL_GROUP_QUOTA_SYSTEM_INODE };
> +	struct super_block *sb = osb->sb;
> +	struct ocfs2_local_disk_dqinfo *ldinfo;
> +	struct inode *lqinode;
> +	struct buffer_head *bh;
> +	int type;
> +	int status;
> +	struct ocfs2_quota_recovery *rec;
> +
> +	mlog(ML_NOTICE, "Beginning quota recovery in slot %u\n", slot_num);
> +	rec = ocfs2_alloc_quota_recovery();
> +	if (!rec)
> +		return ERR_PTR(-ENOMEM);
> +	/* First init... */
> +
> +	for (type = 0; type < MAXQUOTAS; type++) {
> +		if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type]))
> +			continue;
> +		lqinode = ocfs2_get_system_file_inode(osb, ino[type], slot_num);
> +		if (!lqinode) {
> +			status = -ENOENT;
> +			goto out;
> +		}

Can we add a comment here saying that we've already recovered the journal, so local
quota file meta data is up to date and can be trusted?

> +		status = ocfs2_inode_lock_full(lqinode, NULL, 1,
> +						       OCFS2_META_LOCK_NOQUEUE);

I think you also want to add OCFS2_META_LOCK_RECOVERY here, otherwise we'll
hang if another node dies before we get the lock.


> +		/* Someone else is holding the lock? Then he must be
> +		 * doing the recovery. Just skip the file... */
> +		if (status == -EAGAIN) {
> +			mlog(ML_NOTICE, "skipping quota recovery for slot %d "
> +			     "because quota file is locked.\n", slot_num);
> +			status = 0;
> +			goto out_put;
> +		} else if (status < 0) {
> +			mlog_errno(status);
> +			goto out_put;
> +		}
> +		/* Now read local header */
> +		bh = ocfs2_read_quota_block(lqinode, 0, &status);
> +		if (!bh) {
> +			mlog_errno(status);
> +			mlog(ML_ERROR, "failed to read quota file info header "
> +				"(slot=%d type=%d)\n", slot_num, type);
> +			goto out_lock;
> +		}
> +		ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data +
> +							OCFS2_LOCAL_INFO_OFF);
> +		status = ocfs2_recovery_load_quota(lqinode, ldinfo, type,
> +						   &rec->r_list[type]);
> +		brelse(bh);
> +out_lock:
> +		ocfs2_inode_unlock(lqinode, 1);
> +out_put:
> +		iput(lqinode);
> +		if (status < 0)
> +			break;
> +	}
> +out:
> +	if (status < 0) {
> +		ocfs2_free_quota_recovery(rec);
> +		rec = ERR_PTR(status);
> +	}
> +	return rec;
> +}
> +
> +/* Sync changes in local quota file into global quota file and
> + * reinitialize local quota file.
> + * The function expects local quota file to be already locked and
> + * dqonoff_mutex locked. */
> +static int ocfs2_recover_local_quota_file(struct inode *lqinode,
> +					  int type,
> +					  struct ocfs2_quota_recovery *rec)
> +{
> +	struct super_block *sb = lqinode->i_sb;
> +	struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
> +	struct ocfs2_local_disk_chunk *dchunk;
> +	struct ocfs2_local_disk_dqblk *dqblk;
> +	struct dquot *dquot;
> +	handle_t *handle;
> +	struct buffer_head *hbh = NULL, *qbh = NULL;
> +	int status = 0;
> +	int bit, chunk;
> +	struct ocfs2_recovery_chunk *rchunk, *next;
> +	qsize_t spacechange, inodechange;
> +
> +	mlog_entry("ino=%lu type=%u", (unsigned long)lqinode->i_ino, type);
> +
> +	status = ocfs2_lock_global_qf(oinfo, 1);
> +	if (status < 0)
> +		goto out;
> +
> +	list_for_each_entry_safe(rchunk, next, &(rec->r_list[type]), rc_list) {
> +		chunk = rchunk->rc_chunk;
> +		hbh = ocfs2_read_quota_block(lqinode,
> +					     ol_quota_chunk_block(sb, chunk),
> +					     &status);
> +		if (!hbh) {
> +			mlog_errno(status);
> +			break;
> +		}
> +		dchunk = (struct ocfs2_local_disk_chunk *)hbh->b_data;
> +		for_each_bit(bit, rchunk->rc_bitmap, ol_chunk_entries(sb)) {
> +			qbh = ocfs2_read_quota_block(lqinode,
> +						ol_dqblk_block(sb, chunk, bit),
> +						&status);
> +			if (!qbh) {
> +				mlog_errno(status);
> +				break;
> +			}
> +			dqblk = (struct ocfs2_local_disk_dqblk *)(qbh->b_data +
> +				ol_dqblk_block_off(sb, chunk, bit));
> +			dquot = dqget(sb, le64_to_cpu(dqblk->dqb_id), type);
> +			if (!dquot) {
> +				status = -EIO;
> +				mlog(ML_ERROR, "Failed to get quota structure "
> +				     "for id %u, type %d. Cannot finish quota "
> +				     "file recovery.\n",
> +				     (unsigned)le64_to_cpu(dqblk->dqb_id),
> +				     type);
> +				goto out_put_bh;
> +			}
> +			handle = ocfs2_start_trans(OCFS2_SB(sb),
> +						   OCFS2_QSYNC_CREDITS);
> +			if (IS_ERR(handle)) {
> +				status = PTR_ERR(handle);
> +				mlog_errno(status);
> +				goto out_put_dquot;
> +			}
> +			mutex_lock(&sb_dqopt(sb)->dqio_mutex);
> +			spin_lock(&dq_data_lock);
> +			/* Add usage from quota entry into quota changes
> +			 * of our node. Auxiliary variables are important
> +			 * due to signedness */
> +			spacechange = le64_to_cpu(dqblk->dqb_spacemod);
> +			inodechange = le64_to_cpu(dqblk->dqb_inodemod);
> +			dquot->dq_dqb.dqb_curspace += spacechange;
> +			dquot->dq_dqb.dqb_curinodes += inodechange;
> +			spin_unlock(&dq_data_lock);
> +			/* We want to drop reference held by the crashed
> +			 * node. Since we have our own reference we know
> +			 * global structure actually won't be freed. */
> +			status = ocfs2_global_release_dquot(dquot);
> +			if (status < 0) {
> +				mlog_errno(status);
> +				goto out_commit;
> +			}
> +			/* Release local quota file entry */
> +			status = ocfs2_journal_access(handle, lqinode,
> +					qbh, OCFS2_JOURNAL_ACCESS_WRITE);
> +			if (status < 0) {
> +				mlog_errno(status);
> +				goto out_commit;
> +			}
> +			lock_buffer(qbh);
> +			WARN_ON(!ocfs2_test_bit(bit, dchunk->dqc_bitmap));
> +			ocfs2_clear_bit(bit, dchunk->dqc_bitmap);
> +			le32_add_cpu(&dchunk->dqc_free, 1);
> +			unlock_buffer(qbh);
> +			status = ocfs2_journal_dirty(handle, qbh);
> +			if (status < 0)
> +				mlog_errno(status);
> +out_commit:
> +			mutex_unlock(&sb_dqopt(sb)->dqio_mutex);
> +			ocfs2_commit_trans(OCFS2_SB(sb), handle);
> +out_put_dquot:
> +			dqput(dquot);
> +out_put_bh:
> +			brelse(qbh);
> +			if (status < 0)
> +				break;
> +		}
> +		brelse(hbh);
> +		list_del(&rchunk->rc_list);
> +		kfree(rchunk->rc_bitmap);
> +		kfree(rchunk);
> +		if (status < 0)
> +			break;
> +	}
> +	ocfs2_unlock_global_qf(oinfo, 1);
> +out:
> +	if (status < 0)
> +		free_recovery_list(&(rec->r_list[type]));
> +	mlog_exit(status);
> +	return status;
> +}
> +
> +/* Recover local quota files for given node different from us */
> +int ocfs2_finish_quota_recovery(struct ocfs2_super *osb,
> +				struct ocfs2_quota_recovery *rec,
> +				int slot_num)
> +{
> +	unsigned int ino[MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE,
> +					LOCAL_GROUP_QUOTA_SYSTEM_INODE };
> +	struct super_block *sb = osb->sb;
> +	struct ocfs2_local_disk_dqinfo *ldinfo;
> +	struct buffer_head *bh;
> +	handle_t *handle;
> +	int type;
> +	int status = 0;
> +	struct inode *lqinode;
> +	unsigned int flags;
> +
> +	mlog(ML_NOTICE, "Finishing quota recovery in slot %u\n", slot_num);
> +	mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
> +	for (type = 0; type < MAXQUOTAS; type++) {
> +		if (list_empty(&(rec->r_list[type])))
> +			continue;
> +		mlog(0, "Recovering quota in slot %d\n", slot_num);
> +		lqinode = ocfs2_get_system_file_inode(osb, ino[type], slot_num);
> +		if (!lqinode) {
> +			status = -ENOENT;
> +			goto out;
> +		}
> +		status = ocfs2_inode_lock_full(lqinode, NULL, 1,
> +						       OCFS2_META_LOCK_NOQUEUE);
> +		/* Someone else is holding the lock? Then he must be
> +		 * doing the recovery. Just skip the file... */

Hmm, given that we have to do this for quota recovery completion, what's the
point of all the work we do in ocfs2_begin_quota_recovery() ?

	--Mark

--
Mark Fasheh

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

* [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2)
  2008-10-24 22:05 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
                   ` (2 preceding siblings ...)
  2008-10-29 22:58 ` Mark Fasheh
@ 2008-11-06  1:09 ` Mark Fasheh
  2008-11-20 19:33   ` Jan Kara
  3 siblings, 1 reply; 69+ messages in thread
From: Mark Fasheh @ 2008-11-06  1:09 UTC (permalink / raw)
  To: ocfs2-devel

On Sat, Oct 25, 2008 at 12:05:04AM +0200, Jan Kara wrote:
> Hello,
> 
> the following patch series implements quotas for OCFS2. The patch
> series is based on:
> git://git.kernel.org/pub/scm/linux/kernel/git/mfasheh/ocfs2.git linux-next
> 
> I've adressed Joel's comments, also node recovery is now fully working
> and I've fixed a few issues I found during my testing. So I'm currently
> not aware of any bugs. Please review, test, comment. Thanks.

Ok, so these patches look pretty good. I've made comments via e-mail and for
the most part, I'm done reviewing. There's always the chance I see something
later (especially once I have it in front of me in the form of a git
branch). None of the comments I had so far though were so critical that we'd
need to wait on inclusion in ocfs2.git for at least preliminary testing. I'd
like your opinion on that first though, before I proceed.
	--Mark

--
Mark Fasheh

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

* [Ocfs2-devel] [PATCH 25/29] ocfs2: Implementation of local and global quota file handling
  2008-11-05 22:49   ` Mark Fasheh
@ 2008-11-20 14:53     ` Jan Kara
  0 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-11-20 14:53 UTC (permalink / raw)
  To: ocfs2-devel

  Hi Mark!

  Thanks for the review. Your comments which aren't deleted has been just
silently implemented :).

> > +static void ocfs2_global_mem2diskdqb(void *dp, struct dquot *dquot)
> > +{
> > +	struct ocfs2_global_disk_dqblk *d = dp;
> > +	struct mem_dqblk *m = &dquot->dq_dqb;
> > +
> > +	d->dqb_id = cpu_to_le32(dquot->dq_id);
> > +	d->dqb_use_count = cpu_to_le32(OCFS2_DQUOT(dquot)->dq_use_count);
> > +	d->dqb_ihardlimit = cpu_to_le64(m->dqb_ihardlimit);
> > +	d->dqb_isoftlimit = cpu_to_le64(m->dqb_isoftlimit);
> > +	d->dqb_curinodes = cpu_to_le64(m->dqb_curinodes);
> > +	d->dqb_bhardlimit = cpu_to_le64(m->dqb_bhardlimit);
> > +	d->dqb_bsoftlimit = cpu_to_le64(m->dqb_bsoftlimit);
> > +	d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
> > +	d->dqb_btime = cpu_to_le64(m->dqb_btime);
> > +	d->dqb_itime = cpu_to_le64(m->dqb_itime);
> > +	d->dqb_pad1 = d->dqb_pad2 = 0;
> 
> Just to be clear - do we definitely want to reset dqb_pad1 and dqb_pad2
> every time the header is written? This means that a future version of the
> quota code can't put values there without having them overwritten by nodes
> which don't understand the new fields...
  OK, done. Since the block is zeroed-out before it's reused, it's fine to
not zero padding fields. OTOH I suspect that we'll have to require all
nodes to understand new meaning of these fields when we start using them
anyway. The only advantage is we will not have to change disk layout.

> > +/* Write to quotafile (we know the transaction is already started and has
> > + * enough credits) */
> > +ssize_t ocfs2_quota_write(struct super_block *sb, int type,
> > +			  const char *data, size_t len, loff_t off)
> > +{
> > +	struct mem_dqinfo *info = sb_dqinfo(sb, type);
> > +	struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
> > +	struct inode *gqinode = oinfo->dqi_gqinode;
> > +	int offset = off & (sb->s_blocksize - 1);
> > +	sector_t blk = off >> sb->s_blocksize_bits;
> > +	int err = 0;
> > +	struct buffer_head *bh;
> > +	handle_t *handle = journal_current_handle();
> > +	size_t tocopy, towrite = len;
> > +
> > +	if (!handle) {
> > +		mlog(ML_ERROR, "Quota write (off=%llu, len=%llu) cancelled "
> > +		     "because transaction was not started.\n",
> > +		     (unsigned long long)off, (unsigned long long)len);
> > +		return -EIO;
> > +	}
> > +	mutex_lock_nested(&gqinode->i_mutex, I_MUTEX_QUOTA);
> > +	if (gqinode->i_size < off + len) {
> > +		down_write(&OCFS2_I(gqinode)->ip_alloc_sem);
> > +		err = ocfs2_extend_no_holes(gqinode, off + len, off);
> > +		up_write(&OCFS2_I(gqinode)->ip_alloc_sem);
> > +		if (err < 0)
> > +			goto out;
> > +		err = ocfs2_simple_size_update(gqinode,
> > +					       oinfo->dqi_gqi_bh,
> > +					       off + len);
> > +		if (err < 0)
> > +			goto out;
> 
> Is it safe if we crash / error here, after the size has been extended but
> before we've written into the newly allocated blocks? In particular, could
> we wind up reading those blocks later and wrongly interpreting whatever data
> happens to be there? Hmm ok, I guess ocfs2_zero_extend() from
> ocfs2_extend_no_holes() fixes that for you, but it's using the page cache.
> Does that interact reasonably with what we're doing below?
  It should work fine. I actually don't use ocfs2_zero_extend() (note that
I pass 'off' to ocfs2_extend_no_holes() as the third parameter). But when
generic quota code decides to extend the file, it checks whether the write
succeeded and if not, it just internally does not increase it's notion of
quotafile size.

> > +	}
> > +	WARN_ON(off >> sb->s_blocksize_bits != \
> > +		(off + len) >> sb->s_blocksize_bits);
> > +	WARN_ON(((off + len) & ((1 << sb->s_blocksize_bits) - 1)) >
> > +		sb->s_blocksize - OCFS2_QBLK_RESERVED_SPACE);
> > +	for (towrite = len; towrite > 0; towrite -= tocopy) {
> > +		tocopy = min(towrite, (size_t)(sb->s_blocksize - offset));
> > +		bh = ocfs2_read_quota_block(gqinode, blk, &err);
> > +		if (!bh) {
> > +			mlog_errno(err);
> > +			return err;
> > +		}
> > +		err = ocfs2_journal_access(handle, gqinode, bh,
> > +						OCFS2_JOURNAL_ACCESS_WRITE);
> > +		if (err < 0) {
> > +			brelse(bh);
> > +			goto out;
> > +		}
> 
> We can optimize away the disk read if the block is newly allocated. There's
> no need to read it off disk, so we can just sb_getblk() it. Likewise, you'd
> want to use OCFS2_JOURNAL_ACCESS_CREATE for those. How often does the quota
> file get extended though? If it's sufficiently rare, we could save this for
> later. Otherwise, it's probably worth the effort imho.
  Actually, extending quota file is really rare operation - happens only
once per 20 new users of the cluster :). But your comment got me an idea,
that we can save read in most rewrite cases because upper level quota code
provides us with a complete content of the block. So I've rewritten the
function to optimize this case. But please check whether the function is
right when I submit next version of patches.

									Honza
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

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

* [Ocfs2-devel] [PATCH 26/29] ocfs2: Add quota calls for allocation and freeing of inodes and space
  2008-11-06  0:06   ` Mark Fasheh
@ 2008-11-20 15:19     ` Jan Kara
  0 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-11-20 15:19 UTC (permalink / raw)
  To: ocfs2-devel

On Wed 05-11-08 16:06:29, Mark Fasheh wrote:
> On Sat, Oct 25, 2008 at 12:08:19AM +0200, Jan Kara wrote:
> > diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
> > index 9af16e0..75cdb0e 100644
> > --- a/fs/ocfs2/file.c
> > +++ b/fs/ocfs2/file.c
> > @@ -35,6 +35,7 @@
> >  #include <linux/mount.h>
> >  #include <linux/writeback.h>
> >  #include <linux/falloc.h>
> > +#include <linux/quotaops.h>
> >  
> >  #define MLOG_MASK_PREFIX ML_INODE
> >  #include <cluster/masklog.h>
> > @@ -56,6 +57,7 @@
> >  #include "suballoc.h"
> >  #include "super.h"
> >  #include "xattr.h"
> > +#include "quota.h"
> >  
> >  #include "buffer_head_io.h"
> >  
> > @@ -536,6 +538,8 @@ static int __ocfs2_extend_allocation(struct inode *inode, u32 logical_start,
> >  	enum ocfs2_alloc_restarted why;
> >  	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
> >  	struct ocfs2_extent_tree et;
> > +	u32 total_clusters = clusters_to_add;
> > +	int did_quota = 0;
> >  
> >  	mlog_entry("(clusters_to_add = %u)\n", clusters_to_add);
> >  
> > @@ -584,6 +588,12 @@ restart_all:
> >  		goto leave;
> >  	}
> >  
> > +	if (!did_quota && vfs_dq_alloc_space_nodirty(inode,
> > +	    ocfs2_clusters_to_bytes(osb->sb, total_clusters))) {
> > +		status = -EDQUOT;
> > +		goto leave;
> > +	}
> > +	did_quota = 1;
> >  restarted_transaction:
> >  	/* reserve a write to the file entry early on - that we if we
> >  	 * run out of credits in the allocation path, we can still
> > @@ -654,6 +664,9 @@ restarted_transaction:
> >  	     OCFS2_I(inode)->ip_clusters, (long long)i_size_read(inode));
> >  
> >  leave:
> > +	if (status < 0 && did_quota)
> > +		vfs_dq_free_space(inode,
> > +			ocfs2_clusters_to_bytes(osb->sb, total_clusters));
> 
> Shouldn't you subtract clusters_to_add clusters instead of total_clusters
> here? In theory, we could have allocated *some* of the inode but failed
> during a later pass.
  True, actually to be correct WRT crash in the middle of the allocation,
we have to release unused quota before we restart a transaction and then ask
for remaining blocks again - quota operation must always belong to the same
transaction as the allocation itself. It should be fixed now.

> > @@ -956,11 +972,47 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
> >  		}
> >  	}
> >  
> > -	handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
> > -	if (IS_ERR(handle)) {
> > -		status = PTR_ERR(handle);
> > -		mlog_errno(status);
> > -		goto bail_unlock;
> > +	if ((attr->ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) ||
> > +	    (attr->ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) {
> > +		credits = OCFS2_INODE_UPDATE_CREDITS;
> > +		if (attr->ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid
> > +		    && OCFS2_HAS_RO_COMPAT_FEATURE(sb,
> > +		    OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) {
> > +			oinfo = sb_dqinfo(sb, USRQUOTA)->dqi_priv;
> > +			status = ocfs2_lock_global_qf(oinfo, 1);
> > +			if (status < 0)
> > +				goto bail_unlock;
> > +			credits += ocfs2_calc_qinit_credits(sb, USRQUOTA) +
> > +				ocfs2_calc_qdel_credits(sb, USRQUOTA);
> > +			locked[USRQUOTA] = 1;
> > +		}
> > +		if (attr->ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid
> > +		    && OCFS2_HAS_RO_COMPAT_FEATURE(sb,
> > +		    OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) {
> > +			oinfo = sb_dqinfo(sb, GRPQUOTA)->dqi_priv;
> > +			status = ocfs2_lock_global_qf(oinfo, 1);
> > +			if (status < 0)
> > +				goto bail_unlock;
> > +			credits += ocfs2_calc_qinit_credits(sb, GRPQUOTA) +
> > +				   ocfs2_calc_qdel_credits(sb, GRPQUOTA);
> > +			locked[GRPQUOTA] = 1;
> > +		}
> > +		handle = ocfs2_start_trans(osb, credits);
> > +		if (IS_ERR(handle)) {
> > +			status = PTR_ERR(handle);
> > +			mlog_errno(status);
> > +			goto bail_unlock;
> > +		}
> > +		status = vfs_dq_transfer(inode, attr) ? -EDQUOT : 0;
> > +		if (status < 0)
> > +			goto bail_commit;
> 
> Ok, so this will all cause syncing on the global quota file because we need
> to transfer one record to/from another? Is there any better way?
  Yes, we take exclusive lock on global quota file because this file might
be the first file user ever has (or the last file source user has) and so
we have to create new quota structure for him (remove quota structure
respectively). This is a rare case but has to be taken care of. Of course,
we could play similar tricks as we do in ocfs2_dquot_initialize() to
optimize for the common case where shared lock is enough because all
structures already exist. But I felt chown/chgrp is rare enough so for now
I took the simple way. If this is a problem, it can be changed.

									Honza
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

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

* [Ocfs2-devel] [PATCH 28/29] ocfs2: Implement quota recovery
  2008-11-06  0:52   ` Mark Fasheh
@ 2008-11-20 16:51     ` Jan Kara
  0 siblings, 0 replies; 69+ messages in thread
From: Jan Kara @ 2008-11-20 16:51 UTC (permalink / raw)
  To: ocfs2-devel

On Wed 05-11-08 16:52:27, Mark Fasheh wrote:
> On Sat, Oct 25, 2008 at 12:08:21AM +0200, Jan Kara wrote:
> > Implement functions for recovery after a crash. Functions just
> > read local quota file and sync info to global quota file.
> > 
> > Signed-off-by: Jan Kara <jack@suse.cz>
> > ---
> >  fs/ocfs2/journal.c      |  105 +++++++++---
> >  fs/ocfs2/journal.h      |    1 +
> >  fs/ocfs2/ocfs2.h        |    4 +-
> >  fs/ocfs2/quota.h        |   21 +++
> >  fs/ocfs2/quota_global.c |    1 -
> >  fs/ocfs2/quota_local.c  |  430 ++++++++++++++++++++++++++++++++++++++++++++++-
> >  6 files changed, 530 insertions(+), 32 deletions(-)
> > 
> > diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c
> > index f3d7c15..d928db9 100644
> > --- a/fs/ocfs2/journal.c
> > +++ b/fs/ocfs2/journal.c
> > @@ -45,6 +45,7 @@
> >  #include "slot_map.h"
> >  #include "super.h"
> >  #include "sysfile.h"
> > +#include "quota.h"
> >  
> >  #include "buffer_head_io.h"
> >  
> > @@ -52,7 +53,7 @@ DEFINE_SPINLOCK(trans_inc_lock);
> >  
> >  static int ocfs2_force_read_journal(struct inode *inode);
> >  static int ocfs2_recover_node(struct ocfs2_super *osb,
> > -			      int node_num);
> > +			      int node_num, int slot_num);
> >  static int __ocfs2_recovery_thread(void *arg);
> >  static int ocfs2_commit_cache(struct ocfs2_super *osb);
> >  static int ocfs2_wait_on_mount(struct ocfs2_super *osb);
> > @@ -877,6 +878,7 @@ struct ocfs2_la_recovery_item {
> >  	int			lri_slot;
> >  	struct ocfs2_dinode	*lri_la_dinode;
> >  	struct ocfs2_dinode	*lri_tl_dinode;
> > +	struct ocfs2_quota_recovery *lri_qrec;
> >  };
> >  
> >  /* Does the second half of the recovery process. By this point, the
> > @@ -897,6 +899,7 @@ void ocfs2_complete_recovery(struct work_struct *work)
> >  	struct ocfs2_super *osb = journal->j_osb;
> >  	struct ocfs2_dinode *la_dinode, *tl_dinode;
> >  	struct ocfs2_la_recovery_item *item, *n;
> > +	struct ocfs2_quota_recovery *qrec;
> >  	LIST_HEAD(tmp_la_list);
> >  
> >  	mlog_entry_void();
> > @@ -942,6 +945,16 @@ void ocfs2_complete_recovery(struct work_struct *work)
> >  		if (ret < 0)
> >  			mlog_errno(ret);
> >  
> > +		qrec = item->lri_qrec;
> > +		if (qrec) {
> > +			mlog(0, "Recovering quota files");
> > +			ret = ocfs2_finish_quota_recovery(osb, qrec,
> > +							  item->lri_slot);
> > +			if (ret < 0)
> > +				mlog_errno(ret);
> > +			/* Recovery info is already freed now */
> > +		}
> > +
> >  		kfree(item);
> >  	}
> >  
> > @@ -955,7 +968,8 @@ void ocfs2_complete_recovery(struct work_struct *work)
> >  static void ocfs2_queue_recovery_completion(struct ocfs2_journal *journal,
> >  					    int slot_num,
> >  					    struct ocfs2_dinode *la_dinode,
> > -					    struct ocfs2_dinode *tl_dinode)
> > +					    struct ocfs2_dinode *tl_dinode,
> > +					    struct ocfs2_quota_recovery *qrec)
> >  {
> >  	struct ocfs2_la_recovery_item *item;
> >  
> > @@ -970,6 +984,9 @@ static void ocfs2_queue_recovery_completion(struct ocfs2_journal *journal,
> >  		if (tl_dinode)
> >  			kfree(tl_dinode);
> >  
> > +		if (qrec)
> > +			ocfs2_free_quota_recovery(qrec);
> > +
> >  		mlog_errno(-ENOMEM);
> >  		return;
> >  	}
> > @@ -978,6 +995,7 @@ static void ocfs2_queue_recovery_completion(struct ocfs2_journal *journal,
> >  	item->lri_la_dinode = la_dinode;
> >  	item->lri_slot = slot_num;
> >  	item->lri_tl_dinode = tl_dinode;
> > +	item->lri_qrec = qrec;
> >  
> >  	spin_lock(&journal->j_lock);
> >  	list_add_tail(&item->lri_list, &journal->j_la_cleanups);
> > @@ -997,6 +1015,7 @@ void ocfs2_complete_mount_recovery(struct ocfs2_super *osb)
> >  		ocfs2_queue_recovery_completion(journal,
> >  						osb->slot_num,
> >  						osb->local_alloc_copy,
> > +						NULL,
> >  						NULL);
> >  		ocfs2_schedule_truncate_log_flush(osb, 0);
> >  
> > @@ -1005,11 +1024,26 @@ void ocfs2_complete_mount_recovery(struct ocfs2_super *osb)
> >  	}
> >  }
> >  
> > +void ocfs2_complete_quota_recovery(struct ocfs2_super *osb)
> > +{
> > +	if (osb->quota_rec) {
> > +		ocfs2_queue_recovery_completion(osb->journal,
> > +						osb->slot_num,
> > +						NULL,
> > +						NULL,
> > +						osb->quota_rec);
> > +		osb->quota_rec = NULL;
> > +	}
> > +}
> > +
> >  static int __ocfs2_recovery_thread(void *arg)
> >  {
> > -	int status, node_num;
> > +	int status, node_num, slot_num;
> >  	struct ocfs2_super *osb = arg;
> >  	struct ocfs2_recovery_map *rm = osb->recovery_map;
> > +	int *rm_quota = NULL;
> > +	int rm_quota_used = 0, i;
> > +	struct ocfs2_quota_recovery *qrec;
> >  
> >  	mlog_entry_void();
> >  
> > @@ -1018,6 +1052,11 @@ static int __ocfs2_recovery_thread(void *arg)
> >  		goto bail;
> >  	}
> >  
> > +	rm_quota = kzalloc(osb->max_slots * sizeof(int), GFP_NOFS);
> > +	if (!rm_quota) {
> > +		status = -ENOMEM;
> > +		goto bail;
> > +	}
> >  restart:
> >  	status = ocfs2_super_lock(osb, 1);
> >  	if (status < 0) {
> > @@ -1031,8 +1070,28 @@ restart:
> >  		 * clear it until ocfs2_recover_node() has succeeded. */
> >  		node_num = rm->rm_entries[0];
> >  		spin_unlock(&osb->osb_lock);
> > -
> > -		status = ocfs2_recover_node(osb, node_num);
> > +		mlog(0, "checking node %d\n", node_num);
> > +		slot_num = ocfs2_node_num_to_slot(osb, node_num);
> > +		if (slot_num == -ENOENT) {
> > +			status = 0;
> > +			mlog(0, "no slot for this node, so no recovery"
> > +			     "required.\n");
> > +			goto skip_recovery;
> > +		}
> > +		mlog(0, "node %d was using slot %d\n", node_num, slot_num);
> > +
> > +		/* It is a bit subtle with quota recovery. We cannot do it
> > +		 * immediately because we have to obtain cluster locks from
> > +		 * quota files and we also don't want to just skip it because
> > +		 * then quota usage would be out of sync until some node takes
> > +		 * the slot. So we remember which nodes need quota recovery
> > +		 * and when everything else is done, we recover quotas. */
> > +		for (i = 0; i < rm_quota_used && rm_quota[i] != slot_num; i++);
> > +		if (i == rm_quota_used)
> > +			rm_quota[rm_quota_used++] = slot_num;
> > +
> > +		status = ocfs2_recover_node(osb, node_num, slot_num);
> > +skip_recovery:
> >  		if (!status) {
> >  			ocfs2_recovery_map_clear(osb, node_num);
> >  		} else {
> > @@ -1056,11 +1115,22 @@ restart:
> >  
> >  	ocfs2_super_unlock(osb, 1);
> >  
> > +	/* Now it is right time to recover quotas... */
> > +	for (i = 0; i < rm_quota_used; i++) {
> > +		qrec = ocfs2_begin_quota_recovery(osb, rm_quota[i]);
> > +		if (IS_ERR(qrec)) {
> > +			status = PTR_ERR(qrec);
> > +			mlog_errno(status);
> > +		}
> > +		ocfs2_queue_recovery_completion(osb->journal, rm_quota[i],
> > +						NULL, NULL, qrec);
> > +	}
> 
> I think we want to do this *within* the super block cluster lock. What if a
> node mounts and then crashes (with quota file changes in it's journal) in
> between ocfs2_super_unlock(osb, 1) and the call in
> ocfs2_begin_quota_recovery()? We'd get the cluster lock, but the quota file
> info would be stale because we'd have an unrecovered slot.
  I'm not sure I see the problem (which is probably because I miss
something in the recovery code). When node is using its local quota file,
it has a cluster lock from it. So as soon as it has some modifications of
it in its journal, it also holds its lock. So I thought that if this node
crashes, noone can obtain lock from the quota file before the journal of
the node is replayed. Or is it otherwise?
  The only problem I can see is that if node doing recovery crashes in the
place you describe, data in the local quota file will remain unsynced
until somebody starts using the slot. Maybe you meant this bug. But then I
don't see how moving inside super block cluster lock will help it. Argh,
this is nasty. Probably we'll have to do what you once proposed - i.e.,
scanning all slots in the cluster and check whether quota files don't need
recovery in them...

> Also, we call ocfs2_queue_recovery_completion() here even when quota isn't
> enabled on the file system. Perhaps ocfs2_begin_quota_recovery() should
> return NULL in that case and we can skip the recovery_completion. Otherwise
> I think we're just running it against an empty qrec, which seems like a bug
> too.
  This might be slightly suboptimal but is handled just fine AFAICS.

> > +/* Load information we need for quota recovery into memory */
> > +struct ocfs2_quota_recovery *ocfs2_begin_quota_recovery(
> > +						struct ocfs2_super *osb,
> > +						int slot_num)
> > +{
> > +	unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
> > +					    OCFS2_FEATURE_RO_COMPAT_GRPQUOTA};
> > +	unsigned int ino[MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE,
> > +					LOCAL_GROUP_QUOTA_SYSTEM_INODE };
> > +	struct super_block *sb = osb->sb;
> > +	struct ocfs2_local_disk_dqinfo *ldinfo;
> > +	struct inode *lqinode;
> > +	struct buffer_head *bh;
> > +	int type;
> > +	int status;
> > +	struct ocfs2_quota_recovery *rec;
> > +
> > +	mlog(ML_NOTICE, "Beginning quota recovery in slot %u\n", slot_num);
> > +	rec = ocfs2_alloc_quota_recovery();
> > +	if (!rec)
> > +		return ERR_PTR(-ENOMEM);
> > +	/* First init... */
> > +
> > +	for (type = 0; type < MAXQUOTAS; type++) {
> > +		if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type]))
> > +			continue;
> > +		lqinode = ocfs2_get_system_file_inode(osb, ino[type], slot_num);
> > +		if (!lqinode) {
> > +			status = -ENOENT;
> > +			goto out;
> > +		}
> 
> Can we add a comment here saying that we've already recovered the journal, so local
> quota file meta data is up to date and can be trusted?
  OK.

> > +		status = ocfs2_inode_lock_full(lqinode, NULL, 1,
> > +						       OCFS2_META_LOCK_NOQUEUE);
> 
> I think you also want to add OCFS2_META_LOCK_RECOVERY here, otherwise we'll
> hang if another node dies before we get the lock.
  Because our recovery thread would get stuck waiting for recovery, I see.
But I really have to be sure that the journal for the slot has been
replayed... OK, but when this is done under super block cluster lock, noone
can really start using the slot before we get to
ocfs2_begin_quota_recovery() so that should be fine. We even should not
need NOQUEUE because if someone starts using the slot, we would not start
recovering it and thus would not get to ocfs2_begin_quota_recovery().

> > +		/* Someone else is holding the lock? Then he must be
> > +		 * doing the recovery. Just skip the file... */
> > +		if (status == -EAGAIN) {
> > +			mlog(ML_NOTICE, "skipping quota recovery for slot %d "
> > +			     "because quota file is locked.\n", slot_num);
> > +			status = 0;
> > +			goto out_put;
> > +		} else if (status < 0) {
> > +			mlog_errno(status);
> > +			goto out_put;
> > +		}
> > +		/* Now read local header */
> > +		bh = ocfs2_read_quota_block(lqinode, 0, &status);
> > +		if (!bh) {
> > +			mlog_errno(status);
> > +			mlog(ML_ERROR, "failed to read quota file info header "
> > +				"(slot=%d type=%d)\n", slot_num, type);
> > +			goto out_lock;
> > +		}
> > +		ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data +
> > +							OCFS2_LOCAL_INFO_OFF);
> > +		status = ocfs2_recovery_load_quota(lqinode, ldinfo, type,
> > +						   &rec->r_list[type]);
> > +		brelse(bh);
> > +out_lock:
> > +		ocfs2_inode_unlock(lqinode, 1);
> > +out_put:
> > +		iput(lqinode);
> > +		if (status < 0)
> > +			break;
> > +	}
> > +out:
> > +	if (status < 0) {
> > +		ocfs2_free_quota_recovery(rec);
> > +		rec = ERR_PTR(status);
> > +	}
> > +	return rec;
> > +}
> > +
> > +/* Sync changes in local quota file into global quota file and
> > + * reinitialize local quota file.
> > + * The function expects local quota file to be already locked and
> > + * dqonoff_mutex locked. */
> > +static int ocfs2_recover_local_quota_file(struct inode *lqinode,
> > +					  int type,
> > +					  struct ocfs2_quota_recovery *rec)
> > +{
> > +	struct super_block *sb = lqinode->i_sb;
> > +	struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
> > +	struct ocfs2_local_disk_chunk *dchunk;
> > +	struct ocfs2_local_disk_dqblk *dqblk;
> > +	struct dquot *dquot;
> > +	handle_t *handle;
> > +	struct buffer_head *hbh = NULL, *qbh = NULL;
> > +	int status = 0;
> > +	int bit, chunk;
> > +	struct ocfs2_recovery_chunk *rchunk, *next;
> > +	qsize_t spacechange, inodechange;
> > +
> > +	mlog_entry("ino=%lu type=%u", (unsigned long)lqinode->i_ino, type);
> > +
> > +	status = ocfs2_lock_global_qf(oinfo, 1);
> > +	if (status < 0)
> > +		goto out;
> > +
> > +	list_for_each_entry_safe(rchunk, next, &(rec->r_list[type]), rc_list) {
> > +		chunk = rchunk->rc_chunk;
> > +		hbh = ocfs2_read_quota_block(lqinode,
> > +					     ol_quota_chunk_block(sb, chunk),
> > +					     &status);
> > +		if (!hbh) {
> > +			mlog_errno(status);
> > +			break;
> > +		}
> > +		dchunk = (struct ocfs2_local_disk_chunk *)hbh->b_data;
> > +		for_each_bit(bit, rchunk->rc_bitmap, ol_chunk_entries(sb)) {
> > +			qbh = ocfs2_read_quota_block(lqinode,
> > +						ol_dqblk_block(sb, chunk, bit),
> > +						&status);
> > +			if (!qbh) {
> > +				mlog_errno(status);
> > +				break;
> > +			}
> > +			dqblk = (struct ocfs2_local_disk_dqblk *)(qbh->b_data +
> > +				ol_dqblk_block_off(sb, chunk, bit));
> > +			dquot = dqget(sb, le64_to_cpu(dqblk->dqb_id), type);
> > +			if (!dquot) {
> > +				status = -EIO;
> > +				mlog(ML_ERROR, "Failed to get quota structure "
> > +				     "for id %u, type %d. Cannot finish quota "
> > +				     "file recovery.\n",
> > +				     (unsigned)le64_to_cpu(dqblk->dqb_id),
> > +				     type);
> > +				goto out_put_bh;
> > +			}
> > +			handle = ocfs2_start_trans(OCFS2_SB(sb),
> > +						   OCFS2_QSYNC_CREDITS);
> > +			if (IS_ERR(handle)) {
> > +				status = PTR_ERR(handle);
> > +				mlog_errno(status);
> > +				goto out_put_dquot;
> > +			}
> > +			mutex_lock(&sb_dqopt(sb)->dqio_mutex);
> > +			spin_lock(&dq_data_lock);
> > +			/* Add usage from quota entry into quota changes
> > +			 * of our node. Auxiliary variables are important
> > +			 * due to signedness */
> > +			spacechange = le64_to_cpu(dqblk->dqb_spacemod);
> > +			inodechange = le64_to_cpu(dqblk->dqb_inodemod);
> > +			dquot->dq_dqb.dqb_curspace += spacechange;
> > +			dquot->dq_dqb.dqb_curinodes += inodechange;
> > +			spin_unlock(&dq_data_lock);
> > +			/* We want to drop reference held by the crashed
> > +			 * node. Since we have our own reference we know
> > +			 * global structure actually won't be freed. */
> > +			status = ocfs2_global_release_dquot(dquot);
> > +			if (status < 0) {
> > +				mlog_errno(status);
> > +				goto out_commit;
> > +			}
> > +			/* Release local quota file entry */
> > +			status = ocfs2_journal_access(handle, lqinode,
> > +					qbh, OCFS2_JOURNAL_ACCESS_WRITE);
> > +			if (status < 0) {
> > +				mlog_errno(status);
> > +				goto out_commit;
> > +			}
> > +			lock_buffer(qbh);
> > +			WARN_ON(!ocfs2_test_bit(bit, dchunk->dqc_bitmap));
> > +			ocfs2_clear_bit(bit, dchunk->dqc_bitmap);
> > +			le32_add_cpu(&dchunk->dqc_free, 1);
> > +			unlock_buffer(qbh);
> > +			status = ocfs2_journal_dirty(handle, qbh);
> > +			if (status < 0)
> > +				mlog_errno(status);
> > +out_commit:
> > +			mutex_unlock(&sb_dqopt(sb)->dqio_mutex);
> > +			ocfs2_commit_trans(OCFS2_SB(sb), handle);
> > +out_put_dquot:
> > +			dqput(dquot);
> > +out_put_bh:
> > +			brelse(qbh);
> > +			if (status < 0)
> > +				break;
> > +		}
> > +		brelse(hbh);
> > +		list_del(&rchunk->rc_list);
> > +		kfree(rchunk->rc_bitmap);
> > +		kfree(rchunk);
> > +		if (status < 0)
> > +			break;
> > +	}
> > +	ocfs2_unlock_global_qf(oinfo, 1);
> > +out:
> > +	if (status < 0)
> > +		free_recovery_list(&(rec->r_list[type]));
> > +	mlog_exit(status);
> > +	return status;
> > +}
> > +
> > +/* Recover local quota files for given node different from us */
> > +int ocfs2_finish_quota_recovery(struct ocfs2_super *osb,
> > +				struct ocfs2_quota_recovery *rec,
> > +				int slot_num)
> > +{
> > +	unsigned int ino[MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE,
> > +					LOCAL_GROUP_QUOTA_SYSTEM_INODE };
> > +	struct super_block *sb = osb->sb;
> > +	struct ocfs2_local_disk_dqinfo *ldinfo;
> > +	struct buffer_head *bh;
> > +	handle_t *handle;
> > +	int type;
> > +	int status = 0;
> > +	struct inode *lqinode;
> > +	unsigned int flags;
> > +
> > +	mlog(ML_NOTICE, "Finishing quota recovery in slot %u\n", slot_num);
> > +	mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
> > +	for (type = 0; type < MAXQUOTAS; type++) {
> > +		if (list_empty(&(rec->r_list[type])))
> > +			continue;
> > +		mlog(0, "Recovering quota in slot %d\n", slot_num);
> > +		lqinode = ocfs2_get_system_file_inode(osb, ino[type], slot_num);
> > +		if (!lqinode) {
> > +			status = -ENOENT;
> > +			goto out;
> > +		}
> > +		status = ocfs2_inode_lock_full(lqinode, NULL, 1,
> > +						       OCFS2_META_LOCK_NOQUEUE);
> > +		/* Someone else is holding the lock? Then he must be
> > +		 * doing the recovery. Just skip the file... */
> 
> Hmm, given that we have to do this for quota recovery completion, what's the
> point of all the work we do in ocfs2_begin_quota_recovery() ?
  I case we are recovering our own slot, we have to know, which entries in
local quota file are just stale entries (used by the node before the crash)
and which are really currently used because we sync and release stale entries
while fs is mounted and node is normally using quota files...

									Honza
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

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

* [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2)
  2008-11-06  1:09 ` Mark Fasheh
@ 2008-11-20 19:33   ` Jan Kara
  2008-11-24 20:50     ` Mark Fasheh
  0 siblings, 1 reply; 69+ messages in thread
From: Jan Kara @ 2008-11-20 19:33 UTC (permalink / raw)
  To: ocfs2-devel

  Hi Mark,

On Wed 05-11-08 17:09:16, Mark Fasheh wrote:
> On Sat, Oct 25, 2008 at 12:05:04AM +0200, Jan Kara wrote:
> > Hello,
> > 
> > the following patch series implements quotas for OCFS2. The patch
> > series is based on:
> > git://git.kernel.org/pub/scm/linux/kernel/git/mfasheh/ocfs2.git linux-next
> > 
> > I've adressed Joel's comments, also node recovery is now fully working
> > and I've fixed a few issues I found during my testing. So I'm currently
> > not aware of any bugs. Please review, test, comment. Thanks.
> 
> Ok, so these patches look pretty good. I've made comments via e-mail and for
> the most part, I'm done reviewing. There's always the chance I see something
> later (especially once I have it in front of me in the form of a git
> branch). None of the comments I had so far though were so critical that we'd
> need to wait on inclusion in ocfs2.git for at least preliminary testing. I'd
> like your opinion on that first though, before I proceed.
  Attached is a tarball of patches rebased on top of your linux-next branch
from yesterday. The patches also contain changes you asked for in your
review (except for checking all quota files on node recovery - I'll
implement that when we agree that is the way to go). I think you can merge
them in the state in which they are.

									Honza
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR
-------------- next part --------------
A non-text attachment was scrubbed...
Name: ocfs2-patches.tar.gz
Type: application/x-compressed-tar
Size: 68764 bytes
Desc: not available
Url : http://oss.oracle.com/pipermail/ocfs2-devel/attachments/20081120/6eb00997/attachment-0001.bin 

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

* [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2)
  2008-11-20 19:33   ` Jan Kara
@ 2008-11-24 20:50     ` Mark Fasheh
  0 siblings, 0 replies; 69+ messages in thread
From: Mark Fasheh @ 2008-11-24 20:50 UTC (permalink / raw)
  To: ocfs2-devel

On Thu, Nov 20, 2008 at 08:33:20PM +0100, Jan Kara wrote:
> On Wed 05-11-08 17:09:16, Mark Fasheh wrote:
> > On Sat, Oct 25, 2008 at 12:05:04AM +0200, Jan Kara wrote:
> > > Hello,
> > > 
> > > the following patch series implements quotas for OCFS2. The patch
> > > series is based on:
> > > git://git.kernel.org/pub/scm/linux/kernel/git/mfasheh/ocfs2.git linux-next
> > > 
> > > I've adressed Joel's comments, also node recovery is now fully working
> > > and I've fixed a few issues I found during my testing. So I'm currently
> > > not aware of any bugs. Please review, test, comment. Thanks.
> > 
> > Ok, so these patches look pretty good. I've made comments via e-mail and for
> > the most part, I'm done reviewing. There's always the chance I see something
> > later (especially once I have it in front of me in the form of a git
> > branch). None of the comments I had so far though were so critical that we'd
> > need to wait on inclusion in ocfs2.git for at least preliminary testing. I'd
> > like your opinion on that first though, before I proceed.
>   Attached is a tarball of patches rebased on top of your linux-next branch
> from yesterday.

Great, these are all up in ocfs2.git now.
	--Mark

--
Mark Fasheh

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

end of thread, other threads:[~2008-11-24 20:50 UTC | newest]

Thread overview: 69+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-10-24 22:07 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
2008-10-24 22:07 ` [Ocfs2-devel] [PATCH 01/29] quota: Add callbacks for allocating and destroying dquot structures Jan Kara
2008-10-24 22:07 ` [Ocfs2-devel] [PATCH 02/29] quota: Increase size of variables for limits and inode usage Jan Kara
2008-10-24 22:07 ` [Ocfs2-devel] [PATCH 03/29] quota: Remove bogus 'optimization' in check_idq() and check_bdq() Jan Kara
2008-10-24 22:07 ` [Ocfs2-devel] [PATCH 04/29] quota: Make _SUSPENDED just a flag Jan Kara
2008-10-24 22:07 ` [Ocfs2-devel] [PATCH 05/29] quota: Allow to separately enable quota accounting and enforcing limits Jan Kara
2008-10-24 22:07 ` [Ocfs2-devel] [PATCH 06/29] ext3: Use sb_any_quota_loaded() instead of sb_any_quota_enabled() Jan Kara
2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 07/29] ext4: " Jan Kara
2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 08/29] reiserfs: " Jan Kara
2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 09/29] quota: Remove compatibility function sb_any_quota_enabled() Jan Kara
2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 10/29] quota: Introduce DQUOT_QUOTA_SYS_FILE flag Jan Kara
2008-10-29 23:09   ` Mark Fasheh
2008-10-30  7:24     ` Jan Kara
2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 11/29] quota: Move quotaio_v[12].h from include/linux/ to fs/ Jan Kara
2008-10-29 23:10   ` Mark Fasheh
2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 12/29] quota: Split off quota tree handling into a separate file Jan Kara
2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 13/29] quota: Convert union in mem_dqinfo to a pointer Jan Kara
2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 14/29] quota: Allow negative usage of space and inodes Jan Kara
2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 15/29] quota: Keep which entries were set by SETQUOTA quotactl Jan Kara
2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 16/29] quota: Add helpers to allow ocfs2 specific quota initialization, freeing and recovery Jan Kara
2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 17/29] quota: Implement function for scanning active dquots Jan Kara
2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 18/29] mm: Export pdflush_operation() Jan Kara
2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 19/29] ocfs2: Fix check of return value of ocfs2_start_trans() Jan Kara
2008-10-30 23:34   ` Mark Fasheh
2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 20/29] ocfs2: Support nested transactions Jan Kara
2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 21/29] ocfs2: Fix checking of return value of new_inode() Jan Kara
2008-10-30 23:51   ` Mark Fasheh
2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 22/29] ocfs2: Let inode be really deleted when ocfs2_mknod_locked() fails Jan Kara
2008-10-30 23:52   ` Mark Fasheh
2008-10-31  5:05     ` Jan Kara
2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 23/29] ocfs2: Assign feature bits and system inodes to quota feature and quota files Jan Kara
2008-10-28 22:16   ` Joel Becker
2008-10-29  2:32     ` Jan Kara
2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 24/29] ocfs2: Mark system files as not subject to quota accounting Jan Kara
2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 25/29] ocfs2: Implementation of local and global quota file handling Jan Kara
2008-10-28 19:07   ` Joel Becker
2008-10-28 19:36   ` Joel Becker
2008-10-29  2:29     ` Jan Kara
2008-10-29 10:51       ` Joel Becker
2008-10-30  7:33         ` Jan Kara
2008-10-30 20:31           ` Joel Becker
2008-10-31  5:00             ` Jan Kara
2008-11-05 22:49   ` Mark Fasheh
2008-11-20 14:53     ` Jan Kara
2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 26/29] ocfs2: Add quota calls for allocation and freeing of inodes and space Jan Kara
2008-11-06  0:06   ` Mark Fasheh
2008-11-20 15:19     ` Jan Kara
2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 27/29] ocfs2: Implement quota syncing thread Jan Kara
2008-11-06  0:27   ` Mark Fasheh
2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 28/29] ocfs2: Implement quota recovery Jan Kara
2008-11-06  0:52   ` Mark Fasheh
2008-11-20 16:51     ` Jan Kara
2008-10-24 22:08 ` [Ocfs2-devel] [PATCH 29/29] ocfs2: Enable quota accounting on mount, disable on umount Jan Kara
2008-10-28 19:11   ` Joel Becker
2008-10-29  2:30     ` Jan Kara
  -- strict thread matches above, loose matches on Subject: below --
2008-10-24 22:05 [Ocfs2-devel] [PATCH 00/00] Implement quotas for OCFS2 (version 2) Jan Kara
2008-10-27  7:22 ` tristan.ye
2008-10-27  9:08 ` tristan.ye
2008-10-27 11:23   ` Jan Kara
2008-10-27 11:23     ` Jan Kara
     [not found]     ` <1225159789.6555.24.camel@tristan-laptop.cn.oracle.com>
2008-10-28 11:23       ` tristan.ye
2008-10-29  1:57         ` Jan Kara
2008-10-29  2:32           ` tristan.ye
2008-10-29 22:58 ` Mark Fasheh
2008-10-30  5:02   ` Jan Kara
2008-10-30 23:32     ` Mark Fasheh
2008-11-06  1:09 ` Mark Fasheh
2008-11-20 19:33   ` Jan Kara
2008-11-24 20:50     ` Mark Fasheh

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.