Skip to contentSkip to conversation

64-bit inode version

RFC PATCH In review Kalpak Shah 3 participants 4 messages Apr 11, 2007 – May 16, 2007 ext4 file system

lore
Share & tools
lore
#0 lore raw

RFC PATCH 1/1 64-bit inode version

Hi,

This patch is on top of the nanosecond timestamp and i_version_hi patches.

This patch adds 64-bit inode version support to ext4. The lower 32 bits are stored in the osd1.linux1.l_i_version field while the high 32 bits are stored in the i_version_hi field newly created in the ext4_inode.

We need to make sure that existing filesystems can also avail the new fields that have been added to the inode. We use s_want_extra_isize and s_min_extra_isize to decide by how much we should expand the inode. If EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE feature is set then we expand by max(s_want_extra_isize, s_min_extra_isize , sizeof(ext4_inode) - EXT4_GOOD_OLD_INODE_SIZE) bytes. Actually it is still an open question about whether users should be able to set s_*_extra_isize smaller than the known fields or not.

This patch also adds the functionality to expand inodes to include the newly added fields. We start by trying to expand by s_want_extra_isize bytes and if its fails we try to expand by s_min_extra_isize bytes. This is done by changing the i_extra_isize if enough space is available in the inode and no EAs are present. If EAs are present and there is enough space in the inode then the EAs in the inode are shifted to make space. If enough space is not available in the inode due to the EAs then 1 or more EAs are shifted to the external EA block. In the worst case when even the external EA block does not have enough space we inform the user that some EA would need to be deleted or s_min_extra_isize would have to be reduced.

This would be online expansion of inodes. I am also working on adding an "expand_inodes" option to e2fsck which will expand all the used inodes.

fs/ext4/inode.c +37 -2
Index: linux-2.6.20/fs/ext4/inode.c
===================================================================
@@ -2756,6 +2756,13 @@ void ext4_read_inode(struct inode * inod
 	EXT4_INODE_GET_XTIME(i_atime, inode, raw_inode);
 	EXT4_EINODE_GET_XTIME(i_crtime, ei, raw_inode);
 
+	ei->i_fs_version = le32_to_cpu(raw_inode->i_disk_version);
+	if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE) {
+		if (EXT4_FITS_IN_INODE(raw_inode, ei, i_version_hi))
+			ei->i_fs_version |= (__u64)(le32_to_cpu(raw_inode->i_version_hi))
+									 << 32;
+	}
+
 	if (S_ISREG(inode->i_mode)) {
 		inode->i_op = &ext4_file_inode_operations;
 		inode->i_fop = &ext4_file_operations;
@@ -2898,8 +2905,14 @@ static int ext4_do_update_inode(handle_t
 	} else for (block = 0; block < EXT4_N_BLOCKS; block++)
 		raw_inode->i_block[block] = ei->i_data[block];
 
-	if (ei->i_extra_isize)
+	raw_inode->i_disk_version = cpu_to_le32(ei->i_fs_version);
+	if (ei->i_extra_isize) {
+		if (EXT4_FITS_IN_INODE(raw_inode, ei, i_version_hi)) {
+			raw_inode->i_version_hi = cpu_to_le32(ei->i_fs_version
+									>> 32);
+		}
 		raw_inode->i_extra_isize = cpu_to_le16(ei->i_extra_isize);
+	}
 
 	BUFFER_TRACE(bh, "call ext4_journal_dirty_metadata");
 	rc = ext4_journal_dirty_metadata(handle, bh);
@@ -3173,10 +3186,32 @@ ext4_reserve_inode_write(handle_t *handl
 int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode)
 {
 	struct ext4_iloc iloc;
-	int err;
+	int err, ret;
+	static int expand_message;
 
 	might_sleep();
 	err = ext4_reserve_inode_write(handle, inode, &iloc);
+	if (EXT4_I(inode)->i_extra_isize <
+	    EXT4_SB(inode->i_sb)->s_want_extra_isize &&
+	    !(EXT4_I(inode)->i_state & EXT4_STATE_NO_EXPAND)) {
+		/* We need extra buffer credits since we may write into EA block
+		 * with this same handle */
+		if ((jbd2_journal_extend(handle,
+			     EXT4_DATA_TRANS_BLOCKS(inode->i_sb))) == 0) {
+			ret = ext4_expand_extra_isize(inode,
+  					EXT4_SB(inode->i_sb)->s_want_extra_isize,
+					iloc, handle);
+			if (ret) {
+				EXT4_I(inode)->i_state |= EXT4_STATE_NO_EXPAND;
+				if (!expand_message) {
+					ext4_warning(inode->i_sb, __FUNCTION__,
+					"Unable to expand inode %lu. Delete some"
+					" EAs or run e2fsck.", inode->i_ino);
+					expand_message = 1;
+				}
+			}
+		}
+	}
 	if (!err)
 		err = ext4_mark_iloc_dirty(handle, inode, &iloc);
 	return err;
include/linux/ext4_fs.h +4 -1
Index: linux-2.6.20/include/linux/ext4_fs.h
===================================================================
@@ -201,6 +201,7 @@ struct ext4_group_desc
 #define EXT4_STATE_JDATA		0x00000001 /* journaled data exists */
 #define EXT4_STATE_NEW			0x00000002 /* inode is newly created */
 #define EXT4_STATE_XATTR		0x00000004 /* has in-inode xattrs */
+#define EXT4_STATE_NO_EXPAND		0x00000008 /* No space for expansion */
 
 /* Used to pass group descriptor data when online resize is done */
 struct ext4_new_group_input {
@@ -291,7 +292,7 @@ struct ext4_inode {
 	__le32	i_flags;	/* File flags */
 	union {
 		struct {
-			__u32  l_i_reserved1;
+			__u32  l_i_version;
 		} linux1;
 		struct {
 			__u32  h_i_translator;
@@ -399,6 +400,8 @@ do {									       \
 				       raw_inode->xtime ## _extra);	       \
 } while (0)
 
+#define i_disk_version osd1.linux1.l_i_version
+
 #if defined(__KERNEL__) || defined(__linux__)
 #define i_reserved1	osd1.linux1.l_i_reserved1
 #define i_frag		osd2.linux2.l_i_frag
include/linux/ext4_fs_i.h +1 -0
Index: linux-2.6.20/include/linux/ext4_fs_i.h
===================================================================
@@ -154,6 +154,7 @@ struct ext4_inode_info {
 	unsigned long i_ext_generation;
 	struct ext4_ext_cache i_cached_extent;
 	struct timespec i_crtime;
+	__u64 i_fs_version;
 };
 
 #endif	/* _LINUX_EXT4_FS_I */
fs/ext4/xattr.c +262 -0

286 lines not on this page · raw message

fs/ext4/xattr.h +3 -0
Index: linux-2.6.20/fs/ext4/xattr.h
===================================================================
@@ -74,6 +74,9 @@ extern int ext4_xattr_set_handle(handle_
 extern void ext4_xattr_delete_inode(handle_t *, struct inode *);
 extern void ext4_xattr_put_super(struct super_block *);
 
+int ext4_expand_extra_isize(struct inode *inode, int new_extra_isize,
+			    struct ext4_iloc iloc, handle_t *handle);
+
 extern int init_ext4_xattr(void);
 extern void exit_ext4_xattr(void);
 

Thanks,
Kalpak Shah.

#1 lore raw
Quoted 3 lines · Kalpak Shah

This patch adds 64-bit inode version support to ext4. The lower 32 bits are stored in the osd1.linux1.l_i_version field while the high 32 bits are stored in the i_version_hi field newly created in the ext4_inode.

Note that this is NOT a duplicate of the Bull "change attribute" patches, rather this implements only the on-disk storage of the 64-bit version number, and the ability to resize i_extra_isize to allow inodes in the filesystem which weren't created with a big enough i_extra_isize to add new fields. That would basically be needed by all ext4 filesystems that did not yet have the nanosecond timestamp patches applied when they were created.

fs/ext4/inode.c void ext4_read_inode(struct inode * inod · line 2763

Quoted 3 lines
fs/ext4/inode.c +3 -0
+		if (EXT4_FITS_IN_INODE(raw_inode, ei, i_version_hi))
+			ei->i_fs_version |= (__u64)(le32_to_cpu(raw_inode->i_version_hi))
+									 << 32;

Minor nit - can the wrapping here be fixed when it is added to the git tree:

			ei->i_fs_version |=
				(__u64)le32_to_cpu(raw_inode->i_version_hi)<<32;

fs/ext4/inode.c static int ext4_do_update_inode(handle_t · line 2912

Quoted 3 lines
fs/ext4/inode.c +3 -0
+		if (EXT4_FITS_IN_INODE(raw_inode, ei, i_version_hi)) {
+			raw_inode->i_version_hi = cpu_to_le32(ei->i_fs_version
+									>> 32);

And here:

			raw_inode->i_version_hi =
				cpu_to_le32(ei->i_fs_version >> 32);

Cheers, Andreas
--
Andreas Dilger
Principal Software Engineer
Cluster File Systems, Inc.

#2 lore raw
In reply to Kalpak Shah
Quoted 11 lines · Kalpak Shah

Hi,

This patch is on top of the nanosecond timestamp and i_version_hi patches.

This patch adds 64-bit inode version support to ext4. The lower 32 bits are stored in the osd1.linux1.l_i_version field while the high 32 bits are stored in the i_version_hi field newly created in the ext4_inode.

We need to make sure that existing filesystems can also avail the new fields that have been added to the inode.

Hi Kalpak,

Failed to build ext4 as module. It is because CONFIG_EXT4DEV_FS_XATTR is not configed but ext4_expand_extra_isize() assumes it's on.

Quoted 10 lines
(quoted diff) +2 -1
@@ -3173,10 +3186,32 @@ ext4_reserve_inode_write(handle_t *handl
 int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode)
 {
 	struct ext4_iloc iloc;
-	int err;
+	int err, ret;
+	static int expand_message;
 
 	might_sleep();
 	err = ext4_reserve_inode_write(handle, inode, &iloc);

Review ext4_reserve_inode_write(handle_t *handl · line 3203

Quoted 10 lines
(quoted diff) +10 -0
+	if (EXT4_I(inode)->i_extra_isize <
+	    EXT4_SB(inode->i_sb)->s_want_extra_isize &&
+	    !(EXT4_I(inode)->i_state & EXT4_STATE_NO_EXPAND)) {
+		/* We need extra buffer credits since we may write into EA block
+		 * with this same handle */
+		if ((jbd2_journal_extend(handle,
+			     EXT4_DATA_TRANS_BLOCKS(inode->i_sb))) == 0) {
+			ret = ext4_expand_extra_isize(inode,
+  					EXT4_SB(inode->i_sb)->s_want_extra_isize,
+					iloc, handle);

Here is the place where ext4_expand_extra_isize can be called without xattrs turned on.

fs/ext4/xattr.c ext4_xattr_release_block(handle_t *handl · line 506

Quoted 10 lines

Index: linux-2.6.20/fs/ext4/xattr.c
===================================================================

fs/ext4/xattr.c +2 -0
@@ -502,6 +502,20 @@ ext4_xattr_release_block(handle_t *handl
 	}
 }
 
+static inline size_t ext3_xattr_free_space(struct ext4_xattr_entry *last,
+				    size_t *min_offs, void *base, int *total)

should renamed to ext4_xattr_free_space()

fs/ext4/xattr.c ext4_xattr_release_block(handle_t *handl · line 509

Quoted 3 lines
fs/ext4/xattr.c +3 -0
+static void ext3_xattr_shift_entries(struct ext4_xattr_entry *entry,
+				     int value_offs_shift, void *to,
+				     void *from, size_t n, int blocksize)

Should rename to ext4_xxx_xxx().

Quoted 6 lines
fs/ext4/xattr.c +6 -0
+/* Expand an inode by new_extra_isize bytes.
+ * Returns 0 on success or negative error number on failure.
+ */
+int ext4_expand_extra_isize(struct inode *inode, int new_extra_isize,
+			    struct ext4_iloc iloc, handle_t *handle)
+{

....

Quoted 6 lines

Index: linux-2.6.20/fs/ext4/xattr.h
===================================================================

fs/ext4/xattr.h extern int ext4_xattr_set_handle(handle_ · line 82

Quoted 9 lines
fs/ext4/xattr.h +3 -0
@@ -74,6 +74,9 @@ extern int ext4_xattr_set_handle(handle_
 extern void ext4_xattr_delete_inode(handle_t *, struct inode *);
 extern void ext4_xattr_put_super(struct super_block *);
 
+int ext4_expand_extra_isize(struct inode *inode, int new_extra_isize,
+			    struct ext4_iloc iloc, handle_t *handle);
+
 extern int init_ext4_xattr(void);
 extern void exit_ext4_xattr(void);
 

The following patch moved the ext4_expand_extra_isize() function to inode.c and provide proper defines in xattr.h. Renamed the ext3 functions to ext4_xxx_xxx().

Compile tested. Can you Ack the changes. Appreciate if you can let me know it passes your tests.

fs/ext4/inode.c +34 -0
Index: linux-2.6.22-rc1/fs/ext4/inode.c
===================================================================
@@ -3097,6 +3097,40 @@
 }
 
 /*
+ * Expand an inode by new_extra_isize bytes.
+ * Returns 0 on success or negative error number on failure.
+ */
+int ext4_expand_extra_isize(struct inode *inode, unsigned int new_extra_isize,
+                        struct ext4_iloc iloc, handle_t *handle)
+{
+	struct ext4_inode *raw_inode;
+	struct ext4_xattr_ibody_header *header;
+	struct ext4_xattr_entry *entry;
+
+	if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) {
+		return 0;
+	}
+
+	raw_inode = ext4_raw_inode(&iloc);
+
+	header = IHDR(inode, raw_inode);
+        entry = IFIRST(header);
+
+	/* No extended attributes present */
+	if (!(EXT4_I(inode)->i_state & EXT4_STATE_XATTR) ||
+		header->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC)) {
+		memset((void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE, 0,
+			new_extra_isize);
+		EXT4_I(inode)->i_extra_isize = new_extra_isize;
+		return 0;
+	}
+
+	/* try to expand with EA present */
+	return ext4_expand_extra_isize_ea(inode, new_extra_isize,
+						raw_inode, handle);
+}
+
+/*
  * What we do here is to mark the in-core inode as clean with respect to inode
  * dirtiness (it may still be data-dirty).
  * This means that the in-core inode may be reaped by prune_icache
fs/ext4/xattr.c +10 -28
Index: linux-2.6.22-rc1/fs/ext4/xattr.c
===================================================================
@@ -66,13 +66,6 @@
 #define BFIRST(bh) ENTRY(BHDR(bh)+1)
 #define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)
 
-#define IHDR(inode, raw_inode) \
-	((struct ext4_xattr_ibody_header *) \
-		((void *)raw_inode + \
-		 EXT4_GOOD_OLD_INODE_SIZE + \
-		 EXT4_I(inode)->i_extra_isize))
-#define IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr)+1))
-
 #ifdef EXT4_XATTR_DEBUG
 # define ea_idebug(inode, f...) do { \
 		printk(KERN_DEBUG "inode %s:%lu: ", \
@@ -508,7 +501,7 @@
 	return;
 }
 
-static inline size_t ext3_xattr_free_space(struct ext4_xattr_entry *last,
+static inline size_t ext4_xattr_free_space(struct ext4_xattr_entry *last,
 				    size_t *min_offs, void *base, int *total)
 {
 	for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) {
@@ -1083,7 +1076,7 @@
 	return error;
 }
 
-static void ext3_xattr_shift_entries(struct ext4_xattr_entry *entry,
+static void ext4_xattr_shift_entries(struct ext4_xattr_entry *entry,
 				     int value_offs_shift, void *to,
 				     void *from, size_t n, int blocksize)
 {
@@ -1103,13 +1096,14 @@
 	memmove(to, from, n);
 }
 
-/* Expand an inode by new_extra_isize bytes.
+/*
+ * Expand an inode by new_extra_isize bytes when EA presents.
  * Returns 0 on success or negative error number on failure.
+ *
  */
 int ext4_expand_extra_isize(struct inode *inode, int new_extra_isize,
-			    struct ext4_iloc iloc, handle_t *handle)
+			    struct ext4_inode *raw_inode, handle_t *handle)
 {
-	struct ext4_inode *raw_inode;
 	struct ext4_xattr_ibody_header *header;
 	struct ext4_xattr_entry *entry, *last, *first;
 	struct buffer_head *bh = NULL;
@@ -1123,27 +1117,15 @@
 	int s_min_extra_isize = EXT4_SB(inode->i_sb)->s_es->s_min_extra_isize;
 
 	down_write(&EXT4_I(inode)->xattr_sem);
-
 retry:
 	if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) {
 		up_write(&EXT4_I(inode)->xattr_sem);
 		return 0;
 	}
 
-	raw_inode = ext4_raw_inode(&iloc);
-
 	header = IHDR(inode, raw_inode);
 	entry = IFIRST(header);
 
-	/* No extended attributes present */
-	if (!(EXT4_I(inode)->i_state & EXT4_STATE_XATTR) ||
-	    header->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC)) {
-		memset((void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE, 0,
-		       new_extra_isize);
-		EXT4_I(inode)->i_extra_isize = new_extra_isize;
-		goto cleanup;
-	}
-
 	/*
 	 * Check if enough free space is available in the inode to shift the
 	 * entries ahead by new_extra_isize.
@@ -1155,10 +1137,10 @@
 	last = entry;
 	total_ino = sizeof(struct ext4_xattr_ibody_header);
 
-	free = ext3_xattr_free_space(last, &min_offs, base, &total_ino);
+	free = ext4_xattr_free_space(last, &min_offs, base, &total_ino);
 	if (free >= new_extra_isize) {
 		entry = IFIRST(header);
-		ext3_xattr_shift_entries(entry,	EXT4_I(inode)->i_extra_isize
+		ext4_xattr_shift_entries(entry,	EXT4_I(inode)->i_extra_isize
 				- new_extra_isize, (void *)raw_inode +
 				EXT4_GOOD_OLD_INODE_SIZE + new_extra_isize,
 				(void *)header, total_ino,
@@ -1188,7 +1170,7 @@
 		first = BFIRST(bh);
 		end = bh->b_data + bh->b_size;
 		min_offs = end - base;
-		free = ext3_xattr_free_space(first, &min_offs, base,
+		free = ext4_xattr_free_space(first, &min_offs, base,
 					     &total_blk);
 		if (free < new_extra_isize) {
 			if (!tried_min_extra_isize && s_min_extra_isize) {
@@ -1287,7 +1269,7 @@
 		else
 			shift_bytes = entry_size + size;
 		/* Adjust the offsets and shift the remaining entries ahead */
-		ext3_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize -
+		ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize -
 			shift_bytes, (void *)raw_inode +
 			EXT4_GOOD_OLD_INODE_SIZE + extra_isize + shift_bytes,
 			(void *)header, total_ino - entry_size,
fs/ext4/xattr.h +16 -2
Index: linux-2.6.22-rc1/fs/ext4/xattr.h
===================================================================
@@ -56,6 +56,13 @@
 #define EXT4_XATTR_SIZE(size) \
 	(((size) + EXT4_XATTR_ROUND) & ~EXT4_XATTR_ROUND)
 
+#define IHDR(inode, raw_inode) \
+	((struct ext4_xattr_ibody_header *) \
+		((void *)raw_inode + \
+		EXT4_GOOD_OLD_INODE_SIZE + \
+		EXT4_I(inode)->i_extra_isize))
+#define IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr)+1))
+
 # ifdef CONFIG_EXT4DEV_FS_XATTR
 
 extern struct xattr_handler ext4_xattr_user_handler;
@@ -74,8 +81,8 @@
 extern void ext4_xattr_delete_inode(handle_t *, struct inode *);
 extern void ext4_xattr_put_super(struct super_block *);
 
-int ext4_expand_extra_isize(struct inode *inode, int new_extra_isize,
-			    struct ext4_iloc iloc, handle_t *handle);
+extern int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
+			    struct ext4_inode *raw_inode, handle_t *handle);
 
 extern int init_ext4_xattr(void);
 extern void exit_ext4_xattr(void);
@@ -132,6 +139,13 @@
 {
 }
 
+static int
+ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
+			    struct ext4_inode *raw_inode, handle_t *handle)
+{
+	return -EOPNOTSUPP;
+}
+
 #define ext4_xattr_handlers	NULL
 
 # endif  /* CONFIG_EXT4DEV_FS_XATTR */
author Acked-by
#3 lore raw

Hi Mingming,

Thanks for pointing out the problem and making the required changes. I have tested the changes and it works properly for all cases. The only downside being that if a filesystem was using EAs and now is compiled without XATTR support, inode expansion will not work on some inodes. But the "-E expand_extra_isize" option patch for e2fsck should solve this problem.

Thanks,
Kalpak.

P.S.: Maybe the "allow more than 32000 subdirectories" should also be included in the 2.6.21-ext4 patchset (or 2.6.22-ext4 rather).

Quoted 300 lines · Mingming Cao
Quoted 11 lines · Kalpak Shah

Hi,

This patch is on top of the nanosecond timestamp and i_version_hi patches.

This patch adds 64-bit inode version support to ext4. The lower 32 bits are stored in the osd1.linux1.l_i_version field while the high 32 bits are stored in the i_version_hi field newly created in the ext4_inode.

We need to make sure that existing filesystems can also avail the new fields that have been added to the inode.

Hi Kalpak,

Failed to build ext4 as module. It is because CONFIG_EXT4DEV_FS_XATTR is not configed but ext4_expand_extra_isize() assumes it's on.

Quoted 10 lines
(quoted diff) +2 -1
@@ -3173,10 +3186,32 @@ ext4_reserve_inode_write(handle_t *handl
 int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode)
 {
 	struct ext4_iloc iloc;
-	int err;
+	int err, ret;
+	static int expand_message;
 
 	might_sleep();
 	err = ext4_reserve_inode_write(handle, inode, &iloc);

Review ext4_reserve_inode_write(handle_t *handl · line 3203

Quoted 10 lines
(quoted diff) +10 -0
+	if (EXT4_I(inode)->i_extra_isize <
+	    EXT4_SB(inode->i_sb)->s_want_extra_isize &&
+	    !(EXT4_I(inode)->i_state & EXT4_STATE_NO_EXPAND)) {
+		/* We need extra buffer credits since we may write into EA block
+		 * with this same handle */
+		if ((jbd2_journal_extend(handle,
+			     EXT4_DATA_TRANS_BLOCKS(inode->i_sb))) == 0) {
+			ret = ext4_expand_extra_isize(inode,
+  					EXT4_SB(inode->i_sb)->s_want_extra_isize,
+					iloc, handle);

Here is the place where ext4_expand_extra_isize can be called without xattrs turned on.

fs/ext4/xattr.c ext4_xattr_release_block(handle_t *handl · line 506

Quoted 10 lines

Index: linux-2.6.20/fs/ext4/xattr.c
===================================================================

fs/ext4/xattr.c +2 -0
@@ -502,6 +502,20 @@ ext4_xattr_release_block(handle_t *handl
 	}
 }
 
+static inline size_t ext3_xattr_free_space(struct ext4_xattr_entry *last,
+				    size_t *min_offs, void *base, int *total)

should renamed to ext4_xattr_free_space()

fs/ext4/xattr.c ext4_xattr_release_block(handle_t *handl · line 509

Quoted 3 lines
fs/ext4/xattr.c +3 -0
+static void ext3_xattr_shift_entries(struct ext4_xattr_entry *entry,
+				     int value_offs_shift, void *to,
+				     void *from, size_t n, int blocksize)

Should rename to ext4_xxx_xxx().

Quoted 6 lines
fs/ext4/xattr.c +6 -0
+/* Expand an inode by new_extra_isize bytes.
+ * Returns 0 on success or negative error number on failure.
+ */
+int ext4_expand_extra_isize(struct inode *inode, int new_extra_isize,
+			    struct ext4_iloc iloc, handle_t *handle)
+{

....

Quoted 6 lines

Index: linux-2.6.20/fs/ext4/xattr.h
===================================================================

fs/ext4/xattr.h extern int ext4_xattr_set_handle(handle_ · line 82

Quoted 9 lines
fs/ext4/xattr.h +3 -0
@@ -74,6 +74,9 @@ extern int ext4_xattr_set_handle(handle_
 extern void ext4_xattr_delete_inode(handle_t *, struct inode *);
 extern void ext4_xattr_put_super(struct super_block *);
 
+int ext4_expand_extra_isize(struct inode *inode, int new_extra_isize,
+			    struct ext4_iloc iloc, handle_t *handle);
+
 extern int init_ext4_xattr(void);
 extern void exit_ext4_xattr(void);
 

The following patch moved the ext4_expand_extra_isize() function to inode.c and provide proper defines in xattr.h. Renamed the ext3 functions to ext4_xxx_xxx().

Compile tested. Can you Ack the changes. Appreciate if you can let me know it passes your tests.

Index: linux-2.6.22-rc1/fs/ext4/inode.c
===================================================================

fs/ext4/inode.c +34 -0
@@ -3097,6 +3097,40 @@
 }
 
 /*
+ * Expand an inode by new_extra_isize bytes.
+ * Returns 0 on success or negative error number on failure.
+ */
+int ext4_expand_extra_isize(struct inode *inode, unsigned int new_extra_isize,
+                        struct ext4_iloc iloc, handle_t *handle)
+{
+	struct ext4_inode *raw_inode;
+	struct ext4_xattr_ibody_header *header;
+	struct ext4_xattr_entry *entry;
+
+	if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) {
+		return 0;
+	}
+
+	raw_inode = ext4_raw_inode(&iloc);
+
+	header = IHDR(inode, raw_inode);
+        entry = IFIRST(header);
+
+	/* No extended attributes present */
+	if (!(EXT4_I(inode)->i_state & EXT4_STATE_XATTR) ||
+		header->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC)) {
+		memset((void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE, 0,
+			new_extra_isize);
+		EXT4_I(inode)->i_extra_isize = new_extra_isize;
+		return 0;
+	}
+
+	/* try to expand with EA present */
+	return ext4_expand_extra_isize_ea(inode, new_extra_isize,
+						raw_inode, handle);
+}
+
+/*
  * What we do here is to mark the in-core inode as clean with respect to inode
  * dirtiness (it may still be data-dirty).
  * This means that the in-core inode may be reaped by prune_icache

Index: linux-2.6.22-rc1/fs/ext4/xattr.c
===================================================================

fs/ext4/xattr.c +10 -28
@@ -66,13 +66,6 @@
 #define BFIRST(bh) ENTRY(BHDR(bh)+1)
 #define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)
 
-#define IHDR(inode, raw_inode) \
-	((struct ext4_xattr_ibody_header *) \
-		((void *)raw_inode + \
-		 EXT4_GOOD_OLD_INODE_SIZE + \
-		 EXT4_I(inode)->i_extra_isize))
-#define IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr)+1))
-
 #ifdef EXT4_XATTR_DEBUG
 # define ea_idebug(inode, f...) do { \
 		printk(KERN_DEBUG "inode %s:%lu: ", \
@@ -508,7 +501,7 @@
 	return;
 }
 
-static inline size_t ext3_xattr_free_space(struct ext4_xattr_entry *last,
+static inline size_t ext4_xattr_free_space(struct ext4_xattr_entry *last,
 				    size_t *min_offs, void *base, int *total)
 {
 	for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) {
@@ -1083,7 +1076,7 @@
 	return error;
 }
 
-static void ext3_xattr_shift_entries(struct ext4_xattr_entry *entry,
+static void ext4_xattr_shift_entries(struct ext4_xattr_entry *entry,
 				     int value_offs_shift, void *to,
 				     void *from, size_t n, int blocksize)
 {
@@ -1103,13 +1096,14 @@
 	memmove(to, from, n);
 }
 
-/* Expand an inode by new_extra_isize bytes.
+/*
+ * Expand an inode by new_extra_isize bytes when EA presents.
  * Returns 0 on success or negative error number on failure.
+ *
  */
 int ext4_expand_extra_isize(struct inode *inode, int new_extra_isize,
-			    struct ext4_iloc iloc, handle_t *handle)
+			    struct ext4_inode *raw_inode, handle_t *handle)
 {
-	struct ext4_inode *raw_inode;
 	struct ext4_xattr_ibody_header *header;
 	struct ext4_xattr_entry *entry, *last, *first;
 	struct buffer_head *bh = NULL;
@@ -1123,27 +1117,15 @@
 	int s_min_extra_isize = EXT4_SB(inode->i_sb)->s_es->s_min_extra_isize;
 
 	down_write(&EXT4_I(inode)->xattr_sem);
-
 retry:
 	if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) {
 		up_write(&EXT4_I(inode)->xattr_sem);
 		return 0;
 	}
 
-	raw_inode = ext4_raw_inode(&iloc);
-
 	header = IHDR(inode, raw_inode);
 	entry = IFIRST(header);
 
-	/* No extended attributes present */
-	if (!(EXT4_I(inode)->i_state & EXT4_STATE_XATTR) ||
-	    header->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC)) {
-		memset((void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE, 0,
-		       new_extra_isize);
-		EXT4_I(inode)->i_extra_isize = new_extra_isize;
-		goto cleanup;
-	}
-
 	/*
 	 * Check if enough free space is available in the inode to shift the
 	 * entries ahead by new_extra_isize.
@@ -1155,10 +1137,10 @@
 	last = entry;
 	total_ino = sizeof(struct ext4_xattr_ibody_header);
 
-	free = ext3_xattr_free_space(last, &min_offs, base, &total_ino);
+	free = ext4_xattr_free_space(last, &min_offs, base, &total_ino);
 	if (free >= new_extra_isize) {
 		entry = IFIRST(header);
-		ext3_xattr_shift_entries(entry,	EXT4_I(inode)->i_extra_isize
+		ext4_xattr_shift_entries(entry,	EXT4_I(inode)->i_extra_isize
 				- new_extra_isize, (void *)raw_inode +
 				EXT4_GOOD_OLD_INODE_SIZE + new_extra_isize,
 				(void *)header, total_ino,
@@ -1188,7 +1170,7 @@
 		first = BFIRST(bh);
 		end = bh->b_data + bh->b_size;
 		min_offs = end - base;
-		free = ext3_xattr_free_space(first, &min_offs, base,
+		free = ext4_xattr_free_space(first, &min_offs, base,
 					     &total_blk);
 		if (free < new_extra_isize) {
 			if (!tried_min_extra_isize && s_min_extra_isize) {
@@ -1287,7 +1269,7 @@
 		else
 			shift_bytes = entry_size + size;
 		/* Adjust the offsets and shift the remaining entries ahead */
-		ext3_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize -
+		ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize -
 			shift_bytes, (void *)raw_inode +
 			EXT4_GOOD_OLD_INODE_SIZE + extra_isize + shift_bytes,
 			(void *)header, total_ino - entry_size,

Index: linux-2.6.22-rc1/fs/ext4/xattr.h
===================================================================

fs/ext4/xattr.h +16 -2
@@ -56,6 +56,13 @@
 #define EXT4_XATTR_SIZE(size) \
 	(((size) + EXT4_XATTR_ROUND) & ~EXT4_XATTR_ROUND)
 
+#define IHDR(inode, raw_inode) \
+	((struct ext4_xattr_ibody_header *) \
+		((void *)raw_inode + \
+		EXT4_GOOD_OLD_INODE_SIZE + \
+		EXT4_I(inode)->i_extra_isize))
+#define IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr)+1))
+
 # ifdef CONFIG_EXT4DEV_FS_XATTR
 
 extern struct xattr_handler ext4_xattr_user_handler;
@@ -74,8 +81,8 @@
 extern void ext4_xattr_delete_inode(handle_t *, struct inode *);
 extern void ext4_xattr_put_super(struct super_block *);
 
-int ext4_expand_extra_isize(struct inode *inode, int new_extra_isize,
-			    struct ext4_iloc iloc, handle_t *handle);
+extern int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
+			    struct ext4_inode *raw_inode, handle_t *handle);
 
 extern int init_ext4_xattr(void);
 extern void exit_ext4_xattr(void);
@@ -132,6 +139,13 @@
 {
 }
 
+static int
+ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
+			    struct ext4_inode *raw_inode, handle_t *handle)
+{
+	return -EOPNOTSUPP;
+}
+
 #define ext4_xattr_handlers	NULL
 
 # endif  /* CONFIG_EXT4DEV_FS_XATTR */