Skip to contentSkip to conversation

ocfs2: Implementation of local and global quota file handling

PATCH New Jan Kara 2 participants 3 messages Oct 20, 2008 – Oct 22, 2008 OCFS2

lore
Share & tools
lore

Jan Kara

author
#0 lore raw

PATCH 1/1 ocfs2: Implementation of local and global quota file handling

---
 fs/ocfs2/Makefile          |    2 +
 fs/ocfs2/cluster/masklog.h |    1 +
 fs/ocfs2/dlmglue.c         |  137 +++++++
 fs/ocfs2/dlmglue.h         |   17 +
 fs/ocfs2/file.c            |    6 +-
 fs/ocfs2/file.h            |    4 +
 fs/ocfs2/journal.h         |   15 +
 fs/ocfs2/ocfs2_lockid.h    |    5 +
 fs/ocfs2/quota.h           |  182 ++++++++++
 fs/ocfs2/quota_global.c    |  836 +++++++++++++++++++++++++++++++++++++++++++
 fs/ocfs2/quota_local.c     |  842 ++++++++++++++++++++++++++++++++++++++++++++
 fs/ocfs2/super.c           |   35 ++-
 12 files changed, 2077 insertions(+), 5 deletions(-)
 create mode 100644 fs/ocfs2/quota.h
 create mode 100644 fs/ocfs2/quota_global.c
 create mode 100644 fs/ocfs2/quota_local.c
fs/ocfs2/Makefile +2 -0
index f6956de..bf6dc41 100644
@@ -34,6 +34,8 @@ ocfs2-objs := \
 	symlink.o 		\
 	sysfile.o 		\
 	uptodate.o		\
+	quota_local.o		\
+	quota_global.o		\
 	ver.o
 
 ocfs2_stackglue-objs := stackglue.o
fs/ocfs2/cluster/masklog.h +1 -0
index 597e064..dd60347 100644
@@ -112,6 +112,7 @@
 #define ML_CONN		0x0000000004000000ULL /* net connection management */
 #define ML_QUORUM	0x0000000008000000ULL /* net connection quorum */
 #define ML_EXPORT	0x0000000010000000ULL /* ocfs2 export operations */
+#define ML_QUOTA	0x0000000020000000ULL /* 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 */
fs/ocfs2/dlmglue.c +137 -0

191 lines not on this page · raw message

fs/ocfs2/dlmglue.h +17 -0
index 2bb01f0..6a34048 100644
@@ -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,
fs/ocfs2/file.c +3 -3
index 89dc20e..0667dde 100644
@@ -302,9 +302,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);
fs/ocfs2/file.h +4 -0
index 1e27b4d..a56c423 100644
@@ -53,6 +53,10 @@ int ocfs2_do_extend_allocation(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_lock_allocators(struct inode *inode, struct ocfs2_dinode *di,
fs/ocfs2/journal.h +15 -0
index 2178ebf..271170a 100644
@@ -340,6 +340,21 @@ int                  ocfs2_journal_dirty_data(handle_t *handle,
 #define OCFS2_RENAME_CREDITS (3 * OCFS2_INODE_UPDATE_CREDITS + 3              \
 			     + OCFS2_UNLINK_CREDITS)
 
+/* 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_calc_extend_credits(struct super_block *sb,
 					    struct ocfs2_dinode *fe,
 					    u32 bits_wanted)
fs/ocfs2/ocfs2_lockid.h +5 -0
index 82c200f..eb6f50c 100644
@@ -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)
fs/ocfs2/quota.h new file +182 -0

182 lines not on this page · raw message

fs/ocfs2/quota_global.c new file +836 -0

836 lines not on this page · raw message

fs/ocfs2/quota_local.c new file +842 -0

842 lines not on this page · raw message

fs/ocfs2/super.c +33 -2
index 9ec1432..e043814 100644
@@ -64,10 +64,13 @@
 #include "sysfile.h"
 #include "uptodate.h"
 #include "ver.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
@@ -136,6 +139,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 {
@@ -1171,8 +1176,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;
 }
@@ -1181,8 +1205,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,
Signature · 1 line
1.5.2.4
#1 lore raw

PATCH 1/1 ocfs2: Implementation of local and global quota file handling

Quoted 1 line · Jan Kara

The commit message could maybe use a little discussion of what
quota bits go in the local file and what goes in the global. It seems like less is written to the global file, but I might be wrong about that (eg, generic quota code might be writing there).

fs/ocfs2/quota.h line 5

Quoted 11 lines
fs/ocfs2/quota.h new file +5 -0
new file mode 100644
index 0000000..33b47eb
@@ -0,0 +1,182 @@
+/*
+ * quota.h for OCFS2
+ *
+ * On disk quota structures for local and global quota file, in-memory
+ * structures.

I'm thinking the on-disk structures belong in ocfs2_fs.h, which
is where we define the disk format, and the in-memory structures belong here.

fs/ocfs2/quota.h line 110

Quoted 8 lines
fs/ocfs2/quota.h new file +8 -0
+/* 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 */
+};

Also, we'd love the on-disk structures to have the size/offset
annotations we put in ocfs2_fs.h. Yes, some of the smaller structures are pretty obvious, but having the hexadecimal values in the header often helps reading binary dumps.

fs/ocfs2/quota.h line 20

Quoted 7 lines
fs/ocfs2/quota.h new file +7 -0
+	while (toread > 0) {
+		tocopy = min(sb->s_blocksize - offset, toread);
+		bh = ocfs2_bread(gqinode, blk, &err, 0);
+		if (!bh) {
+			mlog_errno(err);
+			return err;
+		}

Fair warning - you use ocfs2_bread() a lot in here, but
ocfs2_bread() is going away. Before I realized you were going to use it (and conceptually that makes sense), it moved into fs/ocfs2/dir.c. Then it morphed into ocfs2_read_dir_block()as part of my read-metadata branch at
http://oss.oracle.com/git/jlbec/linux-2.6.git/?p=jlbec/linux-2.6.git;a=shortlog;h=read-metadata
In the end, this code will be doing ocfs2_read_quota_block(), or
perhaps different variations thereof (if there are different types of blocks in the quota file). I'm going to refactor ocfs2_read_dir_block() and introduce the function ocfs2_read_virt_blocks(). It will look exactly like ocfs2_read_blocks() does in my read-metadata branch, except that it will take virtual block numbers rather than physical ones. Thus, you'll be able to write ocfs2_read_quota_block() as a thin wrapper around it, and replace your ocfs2_bread() calls easily.
Obviously, you don't have to worry too much until this code
moves atop the latest kernel code. And I'm happy to help.

Quoted 20 lines
fs/ocfs2/quota.h new file +20 -0
+/* 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;

fs/ocfs2/quota.h line 50

Quoted 10 lines
fs/ocfs2/quota.h new file +10 -0
+	}
+	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;
+	}

ocfs2_journal_dirty() can't sanely return an error, because
journal_dirty() can't either. Someday it will become void. You can take out all the error checking of your ocfs2_journal_dirty() calls.
Is this modify_bh() function the reason you need nested
transactions? The callers can't be audited for their transaction state?

Joel

Signature · 10 lines
"It is not the function of our government to keep the citizen from
 falling into error; it is the function of the citizen to keep the
 government from falling into error."
	- Robert H. Jackson

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

PATCH 1/1 ocfs2: Implementation of local and global quota file handling

Quoted 7 lines · Joel Becker
Quoted 1 line · Jan Kara

The commit message could maybe use a little discussion of what
quota bits go in the local file and what goes in the global. It seems like less is written to the global file, but I might be wrong about that (eg, generic quota code might be writing there).

Well, global file is actually more complicated, but generic code handles
that. In terms of frequency, we write more often to the local file, that is right. It is a good idea to add there a description what goes where to the changelog. I'll do that.

Quoted 7 lines
Quoted 7 lines
fs/ocfs2/quota.h new file +1 -0
new file mode 100644
index 0000000..33b47eb
@@ -0,0 +1,182 @@
+/*
Quoted 8 lines

fs/ocfs2/quota.h line 5

Quoted 4 lines
fs/ocfs2/quota.h new file +4 -0
+ * quota.h for OCFS2
+ *
+ * On disk quota structures for local and global quota file, in-memory
+ * structures.

I'm thinking the on-disk structures belong in ocfs2_fs.h, which
is where we define the disk format, and the in-memory structures belong here.

OK, will do.

Quoted 5 lines
Quoted 5 lines
fs/ocfs2/quota.h new file +5 -0
+/* 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
Quoted 8 lines

fs/ocfs2/quota.h line 110

Quoted 3 lines
fs/ocfs2/quota.h new file +3 -0
+				 * with a bitmap */
+	__le32 dqi_blocks;	/* Number of blocks allocated for quota file */
+};

Also, we'd love the on-disk structures to have the size/offset
annotations we put in ocfs2_fs.h. Yes, some of the smaller structures are pretty obvious, but having the hexadecimal values in the header often helps reading binary dumps.

Will do.

Quoted 16 lines

fs/ocfs2/quota.h line 20

Quoted 7 lines
fs/ocfs2/quota.h new file +7 -0
+	while (toread > 0) {
+		tocopy = min(sb->s_blocksize - offset, toread);
+		bh = ocfs2_bread(gqinode, blk, &err, 0);
+		if (!bh) {
+			mlog_errno(err);
+			return err;
+		}

Fair warning - you use ocfs2_bread() a lot in here, but
ocfs2_bread() is going away. Before I realized you were going to use it (and conceptually that makes sense), it moved into fs/ocfs2/dir.c. Then it morphed into ocfs2_read_dir_block()as part of my read-metadata branch at
http://oss.oracle.com/git/jlbec/linux-2.6.git/?p=jlbec/linux-2.6.git;a=shortlog;h=read-metadata
In the end, this code will be doing ocfs2_read_quota_block(), or
perhaps different variations thereof (if there are different types of

Quoted 8 lines

blocks in the quota file). I'm going to refactor ocfs2_read_dir_block() and introduce the function ocfs2_read_virt_blocks(). It will look exactly like ocfs2_read_blocks() does in my read-metadata branch, except that it will take virtual block numbers rather than physical ones. Thus, you'll be able to write ocfs2_read_quota_block() as a thin wrapper around it, and replace your ocfs2_bread() calls easily.
Obviously, you don't have to worry too much until this code
moves atop the latest kernel code. And I'm happy to help.

I am warned ;) Seriously, when this happens it's trivial to fix and for
SLES11 I need a working version on top of 2.6.27... So I'll change this later when integrating the patches upstream.

Quoted 26 lines
Quoted 26 lines
fs/ocfs2/quota.h new file +26 -0
+/* 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) {
Quoted 8 lines

fs/ocfs2/quota.h line 50

Quoted 4 lines
fs/ocfs2/quota.h new file +4 -0
+		mlog_errno(status);
+		ocfs2_commit_trans(OCFS2_SB(sb), handle);
+		return status;
+	}

ocfs2_journal_dirty() can't sanely return an error, because
journal_dirty() can't either. Someday it will become void. You can take out all the error checking of your ocfs2_journal_dirty() calls.

Thanks for letting me know. I'm not sure if I'll change that for the
first version but I'll put that on my todo list.

Quoted 2 lines

Is this modify_bh() function the reason you need nested
transactions? The callers can't be audited for their transaction state?

No, modify_bh() is just a helper for updating quota file block headers,
I don't think it's ever called when the trasaction is already started. There are more subtle reasons why I wanted nested transactions as I described in one of my previous emails...
Thanks for the review.
Honza

Signature · 2 lines
Jan Kara <[email protected]>
SUSE Labs, CR