All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V4 00/22] ext4: Add inline data support
@ 2012-02-20  7:00 Tao Ma
  2012-02-20  7:01 ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Tao Ma
  2012-02-21 23:44 ` [PATCH V4 00/22] ext4: Add inline data support Andreas Dilger
  0 siblings, 2 replies; 32+ messages in thread
From: Tao Ma @ 2012-02-20  7:00 UTC (permalink / raw)
  To: Ted Ts'o, Andreas Dilger, ext4 development, linux-fsdevel

Hi Ted, Andreas and list,
        This is the v4 attempt to add inline data support to ext4 inode.
For more information about the background, please refer to the thread
http://marc.info/?l=linux-ext4&m=131715205428067&w=2

Changlog from v3 to v4:
1. Add support for truncate which is really a bug.
2. Some bug fixes.
3. rebased to the latest kernel.

Changelog from v2 to v3:
1. Add support for evict data from inode if we can store xattr in it.
2. Add support for fiemap
3. Some nasty bug fixes

The v3 can be found here:
http://marc.info/?l=linux-ext4&m=132421821001634&w=2

The v2 can be found here:
http://marc.info/?l=linux-ext4&m=132189338604177&w=2

The v1 can be found here:
http://marc.info/?l=linux-ext4&m=131961438221255&w=2

any suggestions are welcomed.

Thanks
Tao

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

* [PATCH V4 01/22] ext4: Move extra inode read to a new function.
  2012-02-20  7:00 [PATCH V4 00/22] ext4: Add inline data support Tao Ma
@ 2012-02-20  7:01 ` Tao Ma
  2012-02-20  7:01   ` [PATCH V4 02/22] ext4: export inline xattr functions Tao Ma
                     ` (21 more replies)
  2012-02-21 23:44 ` [PATCH V4 00/22] ext4: Add inline data support Andreas Dilger
  1 sibling, 22 replies; 32+ messages in thread
From: Tao Ma @ 2012-02-20  7:01 UTC (permalink / raw)
  To: linux-ext4; +Cc: linux-fsdevel

From: Tao Ma <boyu.mt@taobao.com>

Currently, in ext4_iget we do a simple check to see whether
there does exist some information starting from the end
of i_extra_size. With inline data added, this procedure
is more complicated. So move it to a new function named
ext4_iget_extra_inode.

Signed-off-by: Tao Ma <boyu.mt@taobao.com>
---
 fs/ext4/inode.c |   19 ++++++++++++-------
 1 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index feaa82f..0b2d6c1 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3610,6 +3610,16 @@ static blkcnt_t ext4_inode_blocks(struct ext4_inode *raw_inode,
 	}
 }
 
+static inline void ext4_iget_extra_inode(struct inode *inode,
+					 struct ext4_inode *raw_inode,
+					 struct ext4_inode_info *ei)
+{
+	__le32 *magic = (void *)raw_inode +
+			EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize;
+	if (*magic == cpu_to_le32(EXT4_XATTR_MAGIC))
+		ext4_set_inode_state(inode, EXT4_STATE_XATTR);
+}
+
 struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
 {
 	struct ext4_iloc iloc;
@@ -3720,13 +3730,8 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
 			/* The extra space is currently unused. Use it. */
 			ei->i_extra_isize = sizeof(struct ext4_inode) -
 					    EXT4_GOOD_OLD_INODE_SIZE;
-		} else {
-			__le32 *magic = (void *)raw_inode +
-					EXT4_GOOD_OLD_INODE_SIZE +
-					ei->i_extra_isize;
-			if (*magic == cpu_to_le32(EXT4_XATTR_MAGIC))
-				ext4_set_inode_state(inode, EXT4_STATE_XATTR);
-		}
+		} else
+			ext4_iget_extra_inode(inode, raw_inode, ei);
 	} else
 		ei->i_extra_isize = 0;
 
-- 
1.7.0.4


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

* [PATCH V4 02/22] ext4: export inline xattr functions.
  2012-02-20  7:01 ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Tao Ma
@ 2012-02-20  7:01   ` Tao Ma
  2012-02-21 22:45     ` Andreas Dilger
  2012-02-20  7:01   ` [PATCH V4 03/22] ext4: Add the basic function for inline data support Tao Ma
                     ` (20 subsequent siblings)
  21 siblings, 1 reply; 32+ messages in thread
From: Tao Ma @ 2012-02-20  7:01 UTC (permalink / raw)
  To: linux-ext4; +Cc: linux-fsdevel

From: Tao Ma <boyu.mt@taobao.com>

Inline data needs some inline xattr functions, so export them
from fs/ext4/xattr.c so that inline.c can uses them.

Signed-off-by: Tao Ma <boyu.mt@taobao.com>
---
 fs/ext4/xattr.c |   63 ++++++++++++++++++++++++++----------------------------
 fs/ext4/xattr.h |   57 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 87 insertions(+), 33 deletions(-)

diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index 93a00d8..07eeaf3 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -61,11 +61,6 @@
 #include "xattr.h"
 #include "acl.h"
 
-#define BHDR(bh) ((struct ext4_xattr_header *)((bh)->b_data))
-#define ENTRY(ptr) ((struct ext4_xattr_entry *)(ptr))
-#define BFIRST(bh) ENTRY(BHDR(bh)+1)
-#define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)
-
 #ifdef EXT4_XATTR_DEBUG
 # define ea_idebug(inode, f...) do { \
 		printk(KERN_DEBUG "inode %s:%lu: ", \
@@ -255,7 +250,7 @@ cleanup:
 	return error;
 }
 
-static int
+int
 ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name,
 		     void *buffer, size_t buffer_size)
 {
@@ -522,21 +517,6 @@ static size_t ext4_xattr_free_space(struct ext4_xattr_entry *last,
 	return (*min_offs - ((void *)last - base) - sizeof(__u32));
 }
 
-struct ext4_xattr_info {
-	int name_index;
-	const char *name;
-	const void *value;
-	size_t value_len;
-};
-
-struct ext4_xattr_search {
-	struct ext4_xattr_entry *first;
-	void *base;
-	void *end;
-	struct ext4_xattr_entry *here;
-	int not_found;
-};
-
 static int
 ext4_xattr_set_entry(struct ext4_xattr_info *i, struct ext4_xattr_search *s)
 {
@@ -890,14 +870,8 @@ bad_block:
 #undef header
 }
 
-struct ext4_xattr_ibody_find {
-	struct ext4_xattr_search s;
-	struct ext4_iloc iloc;
-};
-
-static int
-ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
-		      struct ext4_xattr_ibody_find *is)
+int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
+			  struct ext4_xattr_ibody_find *is)
 {
 	struct ext4_xattr_ibody_header *header;
 	struct ext4_inode *raw_inode;
@@ -925,10 +899,33 @@ ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
 	return 0;
 }
 
-static int
-ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
-		     struct ext4_xattr_info *i,
-		     struct ext4_xattr_ibody_find *is)
+int ext4_xattr_ibody_inline_set(handle_t *handle, struct inode *inode,
+				struct ext4_xattr_info *i,
+				struct ext4_xattr_ibody_find *is)
+{
+	struct ext4_xattr_ibody_header *header;
+	struct ext4_xattr_search *s = &is->s;
+	int error;
+
+	if (EXT4_I(inode)->i_extra_isize == 0)
+		return -ENOSPC;
+	error = ext4_xattr_set_entry(i, s);
+	if (error)
+		return error;
+	header = IHDR(inode, ext4_raw_inode(&is->iloc));
+	if (!IS_LAST_ENTRY(s->first)) {
+		header->h_magic = cpu_to_le32(EXT4_XATTR_MAGIC);
+		ext4_set_inode_state(inode, EXT4_STATE_XATTR);
+	} else {
+		header->h_magic = cpu_to_le32(0);
+		ext4_clear_inode_state(inode, EXT4_STATE_XATTR);
+	}
+	return 0;
+}
+
+int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
+			 struct ext4_xattr_info *i,
+			 struct ext4_xattr_ibody_find *is)
 {
 	struct ext4_xattr_ibody_header *header;
 	struct ext4_xattr_search *s = &is->s;
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index 25b7387..2879761 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -63,6 +63,32 @@ struct ext4_xattr_entry {
 		EXT4_I(inode)->i_extra_isize))
 #define IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr)+1))
 
+#define BHDR(bh) ((struct ext4_xattr_header *)((bh)->b_data))
+#define ENTRY(ptr) ((struct ext4_xattr_entry *)(ptr))
+#define BFIRST(bh) ENTRY(BHDR(bh)+1)
+#define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)
+
+
+struct ext4_xattr_info {
+	int name_index;
+	const char *name;
+	const void *value;
+	size_t value_len;
+};
+
+struct ext4_xattr_search {
+	struct ext4_xattr_entry *first;
+	void *base;
+	void *end;
+	struct ext4_xattr_entry *here;
+	int not_found;
+};
+
+struct ext4_xattr_ibody_find {
+	struct ext4_xattr_search s;
+	struct ext4_iloc iloc;
+};
+
 # ifdef CONFIG_EXT4_FS_XATTR
 
 extern const struct xattr_handler ext4_xattr_user_handler;
@@ -88,6 +114,15 @@ extern void ext4_exit_xattr(void);
 
 extern const struct xattr_handler *ext4_xattr_handlers[];
 
+extern int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
+				 struct ext4_xattr_ibody_find *is);
+extern int ext4_xattr_ibody_inline_set(handle_t *handle, struct inode *inode,
+				       struct ext4_xattr_info *i,
+				       struct ext4_xattr_ibody_find *is);
+extern int ext4_xattr_ibody_get(struct inode *inode, int name_index,
+				const char *name,
+				void *buffer, size_t buffer_size);
+
 # else  /* CONFIG_EXT4_FS_XATTR */
 
 static inline int
@@ -141,6 +176,28 @@ ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
 
 #define ext4_xattr_handlers	NULL
 
+static inline int
+ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
+		      struct ext4_xattr_ibody_find *is)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int
+ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
+		     struct ext4_xattr_info *i,
+		     struct ext4_xattr_ibody_find *is)
+{
+	return -EOPNOTSUPP;
+}
+
+extern int ext4_xattr_ibody_get(struct inode *inode, int name_index,
+				const char *name,
+				void *buffer, size_t buffer_size)
+{
+	return -EOPNOTSUPP;
+}
+
 # endif  /* CONFIG_EXT4_FS_XATTR */
 
 #ifdef CONFIG_EXT4_FS_SECURITY
-- 
1.7.0.4


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

* [PATCH V4 03/22] ext4: Add the basic function for inline data support.
  2012-02-20  7:01 ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Tao Ma
  2012-02-20  7:01   ` [PATCH V4 02/22] ext4: export inline xattr functions Tao Ma
@ 2012-02-20  7:01   ` Tao Ma
  2012-04-07  0:21     ` Andreas Dilger
  2012-02-20  7:01   ` [PATCH V4 04/22] ext4: Add read support for inline data Tao Ma
                     ` (19 subsequent siblings)
  21 siblings, 1 reply; 32+ messages in thread
From: Tao Ma @ 2012-02-20  7:01 UTC (permalink / raw)
  To: linux-ext4; +Cc: linux-fsdevel

From: Tao Ma <boyu.mt@taobao.com>

Implement inline data with xattr. This idea is inspired by Andreas.
So now we use "system.data" to store xattr.
For inode_size = 256, currently we uses all the space between i_extra_isize
and inode_size. For inode_size > 256, we use half of that space.

Signed-off-by: Tao Ma <boyu.mt@taobao.com>
---
 fs/ext4/Makefile |    2 +-
 fs/ext4/ext4.h   |    7 +
 fs/ext4/inline.c |  456 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ext4/inode.c  |    5 +-
 fs/ext4/xattr.c  |   24 ---
 fs/ext4/xattr.h  |   60 +++++++-
 6 files changed, 525 insertions(+), 29 deletions(-)
 create mode 100644 fs/ext4/inline.c

diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index 56fd8f8..e88b7a6 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -9,6 +9,6 @@ ext4-y	:= balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \
 		ext4_jbd2.o migrate.o mballoc.o block_validity.o move_extent.o \
 		mmp.o indirect.o
 
-ext4-$(CONFIG_EXT4_FS_XATTR)		+= xattr.o xattr_user.o xattr_trusted.o
+ext4-$(CONFIG_EXT4_FS_XATTR)		+= xattr.o xattr_user.o xattr_trusted.o inline.o
 ext4-$(CONFIG_EXT4_FS_POSIX_ACL)	+= acl.o
 ext4-$(CONFIG_EXT4_FS_SECURITY)		+= xattr_security.o
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 513004f..18254c0 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -371,6 +371,7 @@ struct flex_groups {
 #define EXT4_EXTENTS_FL			0x00080000 /* Inode uses extents */
 #define EXT4_EA_INODE_FL	        0x00200000 /* Inode used for large EA */
 #define EXT4_EOFBLOCKS_FL		0x00400000 /* Blocks allocated beyond EOF */
+#define EXT4_INLINE_DATA_FL		0x00800000 /* Inode has inline data. */
 #define EXT4_RESERVED_FL		0x80000000 /* reserved for ext4 lib */
 
 #define EXT4_FL_USER_VISIBLE		0x004BDFFF /* User visible flags */
@@ -427,6 +428,7 @@ enum {
 	EXT4_INODE_EXTENTS	= 19,	/* Inode uses extents */
 	EXT4_INODE_EA_INODE	= 21,	/* Inode used for large EA */
 	EXT4_INODE_EOFBLOCKS	= 22,	/* Blocks allocated beyond EOF */
+	EXT4_INODE_INLINE_DATA	= 23,	/* Data in inode. */
 	EXT4_INODE_RESERVED	= 31,	/* reserved for ext4 lib */
 };
 
@@ -878,6 +880,10 @@ struct ext4_inode_info {
 	/* on-disk additional length */
 	__u16 i_extra_isize;
 
+	/* Indicate the inline data space. */
+	u16 i_inline_off;
+	u16 i_inline_size;
+
 #ifdef CONFIG_QUOTA
 	/* quota space reservation, managed internally by quota code */
 	qsize_t i_reserved_quota;
@@ -1307,6 +1313,7 @@ enum {
 	EXT4_STATE_DIO_UNWRITTEN,	/* need convert on dio done*/
 	EXT4_STATE_NEWENTRY,		/* File just added to dir */
 	EXT4_STATE_DELALLOC_RESERVED,	/* blks already reserved for delalloc */
+	EXT4_STATE_MAY_INLINE_DATA,	/* may have in-inode data */
 };
 
 #define EXT4_INODE_BIT_FNS(name, field, offset)				\
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
new file mode 100644
index 0000000..77cedd2
--- /dev/null
+++ b/fs/ext4/inline.c
@@ -0,0 +1,456 @@
+/*
+ * Copyright (c) 2011 Taobao.
+ * Written by Tao Ma <boyu.mt@taobao.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include "ext4_jbd2.h"
+#include "ext4.h"
+#include "xattr.h"
+
+#define EXT4_XATTR_SYSTEM_DATA_NAME	"data"
+#define EXT4_MIN_INLINE_DATA_SIZE	((sizeof(__le32) * EXT4_N_BLOCKS))
+
+int ext4_get_inline_size(struct inode *inode)
+{
+	if (EXT4_I(inode)->i_inline_off)
+		return EXT4_I(inode)->i_inline_size;
+
+	return 0;
+}
+
+static int get_max_inline_xattr_value_size(struct inode *inode,
+					   struct ext4_iloc *iloc)
+{
+	struct ext4_xattr_ibody_header *header;
+	struct ext4_xattr_entry *entry;
+	struct ext4_inode *raw_inode;
+	int free, min_offs;
+
+	if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR))
+		return EXT4_XATTR_SIZE(EXT4_SB(inode->i_sb)->s_inode_size -
+			EXT4_GOOD_OLD_INODE_SIZE -
+			EXT4_I(inode)->i_extra_isize -
+			sizeof(struct ext4_xattr_ibody_header) -
+			EXT4_XATTR_LEN(strlen(EXT4_XATTR_SYSTEM_DATA_NAME)) -
+			EXT4_XATTR_ROUND - sizeof(__u32));
+
+	down_read(&EXT4_I(inode)->xattr_sem);
+	raw_inode = ext4_raw_inode(iloc);
+	header = IHDR(inode, raw_inode);
+	entry = IFIRST(header);
+	min_offs = EXT4_SB(inode->i_sb)->s_inode_size -
+			EXT4_GOOD_OLD_INODE_SIZE -
+			EXT4_I(inode)->i_extra_isize -
+			sizeof(struct ext4_xattr_ibody_header);
+
+	/* Compute min_offs. */
+	for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) {
+		if (!entry->e_value_block && entry->e_value_size) {
+			size_t offs = le16_to_cpu(entry->e_value_offs);
+			if (offs < min_offs)
+				min_offs = offs;
+		}
+	}
+	free = min_offs -
+		((void *)entry - (void *)IFIRST(header)) - sizeof(__u32);
+
+	if (EXT4_I(inode)->i_inline_off) {
+		entry = (struct ext4_xattr_entry *)
+			((void *)raw_inode + EXT4_I(inode)->i_inline_off);
+
+		free += le32_to_cpu(entry->e_value_size);
+		goto out;
+	}
+
+	free -= EXT4_XATTR_LEN(strlen(EXT4_XATTR_SYSTEM_DATA_NAME));
+
+	if (free > EXT4_XATTR_ROUND)
+		free = EXT4_XATTR_SIZE(free - EXT4_XATTR_ROUND);
+	else
+		free = 0;
+
+out:
+	up_read(&EXT4_I(inode)->xattr_sem);
+	return free;
+}
+
+/*
+ * Get the maximum size we now can store in an inode.
+ * If we can't find the space for a xattr entry, don't use the space
+ * of the extents since we have no space to indicate the inline data.
+ */
+int ext4_get_max_inline_size(struct inode *inode)
+{
+	int error, max_inline_size;
+	struct ext4_iloc iloc;
+
+	if (EXT4_I(inode)->i_extra_isize == 0)
+		return 0;
+
+	error = ext4_get_inode_loc(inode, &iloc);
+	if (error) {
+		ext4_error_inode(inode, __func__, __LINE__, 0,
+				 "can't get inode location %lu",
+				 inode->i_ino);
+		return 0;
+	}
+
+	max_inline_size = get_max_inline_xattr_value_size(inode, &iloc);
+
+	brelse(iloc.bh);
+
+	if (!max_inline_size)
+		return 0;
+
+	return max_inline_size + EXT4_MIN_INLINE_DATA_SIZE;
+}
+
+int ext4_has_inline_data(struct inode *inode)
+{
+	return ext4_test_inode_flag(inode, EXT4_INODE_INLINE_DATA) &&
+	       EXT4_I(inode)->i_inline_off;
+}
+
+int ext4_find_inline_data(struct inode *inode)
+{
+	struct ext4_xattr_ibody_find is = {
+		.s = { .not_found = -ENODATA, },
+	};
+	struct ext4_xattr_info i = {
+		.name_index = EXT4_XATTR_INDEX_SYSTEM_DATA,
+		.name = EXT4_XATTR_SYSTEM_DATA_NAME,
+	};
+	int error;
+
+	if (EXT4_I(inode)->i_extra_isize == 0)
+		return 0;
+
+	error = ext4_get_inode_loc(inode, &is.iloc);
+	if (error)
+		return error;
+
+	error = ext4_xattr_ibody_find(inode, &i, &is);
+	if (error)
+		goto out;
+
+	if (!is.s.not_found) {
+		EXT4_I(inode)->i_inline_off = (u16)((void *)is.s.here -
+					(void *)ext4_raw_inode(&is.iloc));
+		EXT4_I(inode)->i_inline_size = EXT4_MIN_INLINE_DATA_SIZE +
+				le32_to_cpu(is.s.here->e_value_size);
+		ext4_set_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
+	}
+out:
+	brelse(is.iloc.bh);
+	return error;
+}
+
+static int ext4_read_inline_data(struct inode *inode, void *buffer, unsigned int len,
+				 struct ext4_iloc *iloc)
+{
+	struct ext4_xattr_entry *entry;
+	struct ext4_xattr_ibody_header *header;
+	int cp_len = 0;
+	struct ext4_inode *raw_inode;
+
+	if (!len)
+		return 0;
+
+	BUG_ON(len > EXT4_I(inode)->i_inline_size);
+
+	cp_len = len < EXT4_MIN_INLINE_DATA_SIZE ?
+			len : EXT4_MIN_INLINE_DATA_SIZE;
+
+	raw_inode = ext4_raw_inode(iloc);
+	memcpy(buffer, (void *)(raw_inode->i_block), cp_len);
+
+	len -= cp_len;
+	buffer += cp_len;
+
+	if (!len)
+		goto out;
+
+	header = IHDR(inode, raw_inode);
+	entry = (struct ext4_xattr_entry *)((void *)raw_inode +
+					    EXT4_I(inode)->i_inline_off);
+	len = min_t(unsigned int, len,
+		    (unsigned int)le32_to_cpu(entry->e_value_size));
+
+	memcpy(buffer,
+	       (void *)IFIRST(header) + le16_to_cpu(entry->e_value_offs), len);
+	cp_len += len;
+
+out:
+	return cp_len;
+}
+
+/*
+ * write the buffer to the inline inode.
+ * If 'create' is set, we don't need to do the extra copy in the xattr
+ * value since it is already handled by ext4_xattr_ibody_set. That saves
+ * us one memcpy.
+ */
+void ext4_write_inline_data(struct inode *inode, struct ext4_iloc *iloc,
+			    void *buffer, loff_t pos, unsigned int len)
+{
+	struct ext4_xattr_entry *entry;
+	struct ext4_xattr_ibody_header *header;
+	struct ext4_inode *raw_inode;
+	int cp_len = 0;
+
+	BUG_ON(!EXT4_I(inode)->i_inline_off);
+	BUG_ON(pos + len > EXT4_I(inode)->i_inline_size);
+
+	raw_inode = ext4_raw_inode(iloc);
+	buffer += pos;
+
+	if (pos < EXT4_MIN_INLINE_DATA_SIZE) {
+		cp_len = pos + len > EXT4_MIN_INLINE_DATA_SIZE ?
+			 EXT4_MIN_INLINE_DATA_SIZE - pos : len;
+		memcpy((void *)raw_inode->i_block + pos, buffer, cp_len);
+
+		len -= cp_len;
+		buffer += cp_len;
+		pos += cp_len;
+	}
+
+	if (!len)
+		return;
+
+	pos -= EXT4_MIN_INLINE_DATA_SIZE;
+	header = IHDR(inode, raw_inode);
+	entry = (struct ext4_xattr_entry *)((void *)raw_inode +
+					    EXT4_I(inode)->i_inline_off);
+
+	memcpy((void *)IFIRST(header) + le16_to_cpu(entry->e_value_offs) + pos,
+	       buffer, len);
+}
+
+static int ext4_create_inline_data(handle_t *handle,
+				   struct inode *inode, unsigned len)
+{
+	int error;
+	void *value = NULL;
+	struct ext4_xattr_ibody_find is = {
+		.s = { .not_found = -ENODATA, },
+	};
+	struct ext4_xattr_info i = {
+		.name_index = EXT4_XATTR_INDEX_SYSTEM_DATA,
+		.name = EXT4_XATTR_SYSTEM_DATA_NAME,
+	};
+
+	error = ext4_get_inode_loc(inode, &is.iloc);
+	if (error)
+		return error;
+
+	error = ext4_journal_get_write_access(handle, is.iloc.bh);
+	if (error)
+		goto out;
+
+	if (len > EXT4_MIN_INLINE_DATA_SIZE) {
+		value = (void *)empty_zero_page;
+		len -= EXT4_MIN_INLINE_DATA_SIZE;
+	} else {
+		value = "";
+		len = 0;
+	}
+
+	/* Insert the the xttr entry. */
+	i.value = value;
+	i.value_len = len;
+
+	error = ext4_xattr_ibody_find(inode, &i, &is);
+	if (error)
+		goto out;
+
+	BUG_ON(!is.s.not_found);
+
+	error = ext4_xattr_ibody_set(handle, inode, &i, &is);
+	if (error) {
+		if (error == -ENOSPC)
+			ext4_clear_inode_state(inode,
+					       EXT4_STATE_MAY_INLINE_DATA);
+		goto out;
+	}
+
+	memset((void *)ext4_raw_inode(&is.iloc)->i_block,
+		0, EXT4_MIN_INLINE_DATA_SIZE);
+
+	EXT4_I(inode)->i_inline_off = (u16)((void *)is.s.here -
+				      (void *)ext4_raw_inode(&is.iloc));
+	EXT4_I(inode)->i_inline_size = len + EXT4_MIN_INLINE_DATA_SIZE;
+	ext4_clear_inode_flag(inode, EXT4_INODE_EXTENTS);
+	ext4_set_inode_flag(inode, EXT4_INODE_INLINE_DATA);
+	get_bh(is.iloc.bh);
+	error = ext4_mark_iloc_dirty(handle, inode, &is.iloc);
+
+out:
+	brelse(is.iloc.bh);
+	return error;
+}
+
+static int ext4_update_inline_data(handle_t *handle, struct inode *inode,
+				   unsigned int len)
+{
+	int error;
+	void *value = NULL;
+	struct ext4_xattr_ibody_find is = {
+		.s = { .not_found = -ENODATA, },
+	};
+	struct ext4_xattr_info i = {
+		.name_index = EXT4_XATTR_INDEX_SYSTEM_DATA,
+		.name = EXT4_XATTR_SYSTEM_DATA_NAME,
+	};
+
+	/* If the old space is ok, write the data directly. */
+	if (len <= EXT4_I(inode)->i_inline_size)
+		return 0;
+
+	error = ext4_get_inode_loc(inode, &is.iloc);
+	if (error)
+		return error;
+
+	error = ext4_xattr_ibody_find(inode, &i, &is);
+	if (error)
+		goto out;
+
+	BUG_ON(is.s.not_found);
+
+	len -= EXT4_MIN_INLINE_DATA_SIZE;
+	value = kzalloc(len, GFP_NOFS);
+	if (!value)
+		goto out;
+
+	error = ext4_xattr_ibody_get(inode, i.name_index, i.name,
+				     value, len);
+	if (error == -ENODATA)
+		goto out;
+
+	error = ext4_journal_get_write_access(handle, is.iloc.bh);
+	if (error)
+		goto out;
+
+	/* Update the xttr entry. */
+	i.value = value;
+	i.value_len = len;
+
+	error = ext4_xattr_ibody_set(handle, inode, &i, &is);
+	if (error)
+		goto out;
+
+	EXT4_I(inode)->i_inline_off = (u16)((void *)is.s.here -
+				      (void *)ext4_raw_inode(&is.iloc));
+	EXT4_I(inode)->i_inline_size = EXT4_MIN_INLINE_DATA_SIZE +
+				le32_to_cpu(is.s.here->e_value_size);
+	ext4_set_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
+	get_bh(is.iloc.bh);
+	error = ext4_mark_iloc_dirty(handle, inode, &is.iloc);
+
+out:
+	kfree(value);
+	brelse(is.iloc.bh);
+	return error;
+}
+
+int ext4_prepare_inline_data(handle_t *handle, struct inode *inode,
+			     unsigned int len)
+{
+	int ret, size;
+	struct ext4_inode_info *ei = EXT4_I(inode);
+
+	if (!ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA))
+		return -ENOSPC;
+
+	size = ext4_get_max_inline_size(inode);
+	if (size < len)
+		return -ENOSPC;
+
+	down_write(&EXT4_I(inode)->xattr_sem);
+
+	if (ei->i_inline_off)
+		ret = ext4_update_inline_data(handle, inode, len);
+	else
+		ret = ext4_create_inline_data(handle, inode, len);
+
+	up_write(&EXT4_I(inode)->xattr_sem);
+
+	return ret;
+}
+
+static int ext4_destroy_inline_data_nolock(handle_t *handle, struct inode *inode)
+{
+	struct ext4_inode_info *ei = EXT4_I(inode);
+	struct ext4_xattr_ibody_find is = {
+		.s = { .not_found = 0, },
+	};
+	struct ext4_xattr_info i = {
+		.name_index = EXT4_XATTR_INDEX_SYSTEM_DATA,
+		.name = EXT4_XATTR_SYSTEM_DATA_NAME,
+		.value = NULL,
+		.value_len = 0,
+	};
+	int error;
+
+	if (!ei->i_inline_off)
+		return 0;
+
+	error = ext4_get_inode_loc(inode, &is.iloc);
+	if (error)
+		return error;
+
+	error = ext4_xattr_ibody_find(inode, &i, &is);
+	if (error)
+		goto out;
+
+	error = ext4_journal_get_write_access(handle, is.iloc.bh);
+	if (error)
+		goto out;
+
+	error = ext4_xattr_ibody_set(handle, inode, &i, &is);
+	if (error)
+		goto out;
+
+	memset((void *)ext4_raw_inode(&is.iloc)->i_block,
+		0, EXT4_MIN_INLINE_DATA_SIZE);
+
+	if (EXT4_HAS_INCOMPAT_FEATURE(inode->i_sb,
+				      EXT4_FEATURE_INCOMPAT_EXTENTS)) {
+		if (S_ISDIR(inode->i_mode) ||
+		    S_ISREG(inode->i_mode) || S_ISLNK(inode->i_mode)) {
+			ext4_set_inode_flag(inode, EXT4_INODE_EXTENTS);
+			ext4_ext_tree_init(handle, inode);
+		}
+	}
+	ext4_clear_inode_flag(inode, EXT4_INODE_INLINE_DATA);
+
+	get_bh(is.iloc.bh);
+	error = ext4_mark_iloc_dirty(handle, inode, &is.iloc);
+
+	EXT4_I(inode)->i_inline_off = 0;
+	EXT4_I(inode)->i_inline_size = 0;
+	ext4_clear_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
+out:
+	brelse(is.iloc.bh);
+	if (error == -ENODATA)
+		error = 0;
+	return error;
+}
+
+int ext4_destroy_inline_data(handle_t *handle, struct inode *inode)
+{
+	int ret;
+
+	down_write(&EXT4_I(inode)->xattr_sem);
+	ret = ext4_destroy_inline_data_nolock(handle, inode);
+	up_write(&EXT4_I(inode)->xattr_sem);
+
+	return ret;
+}
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 0b2d6c1..b94a388 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3616,8 +3616,10 @@ static inline void ext4_iget_extra_inode(struct inode *inode,
 {
 	__le32 *magic = (void *)raw_inode +
 			EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize;
-	if (*magic == cpu_to_le32(EXT4_XATTR_MAGIC))
+	if (*magic == cpu_to_le32(EXT4_XATTR_MAGIC)) {
 		ext4_set_inode_state(inode, EXT4_STATE_XATTR);
+		ext4_find_inline_data(inode);
+	}
 }
 
 struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
@@ -3653,6 +3655,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
 	set_nlink(inode, le16_to_cpu(raw_inode->i_links_count));
 
 	ext4_clear_state_flags(ei);	/* Only relevant on 32-bit archs */
+	ei->i_inline_off = 0;
 	ei->i_dir_start_lookup = 0;
 	ei->i_dtime = le32_to_cpu(raw_inode->i_dtime);
 	/* We now have enough fields to check if the inode was active or not.
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index 07eeaf3..71ef45e 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -899,30 +899,6 @@ int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
 	return 0;
 }
 
-int ext4_xattr_ibody_inline_set(handle_t *handle, struct inode *inode,
-				struct ext4_xattr_info *i,
-				struct ext4_xattr_ibody_find *is)
-{
-	struct ext4_xattr_ibody_header *header;
-	struct ext4_xattr_search *s = &is->s;
-	int error;
-
-	if (EXT4_I(inode)->i_extra_isize == 0)
-		return -ENOSPC;
-	error = ext4_xattr_set_entry(i, s);
-	if (error)
-		return error;
-	header = IHDR(inode, ext4_raw_inode(&is->iloc));
-	if (!IS_LAST_ENTRY(s->first)) {
-		header->h_magic = cpu_to_le32(EXT4_XATTR_MAGIC);
-		ext4_set_inode_state(inode, EXT4_STATE_XATTR);
-	} else {
-		header->h_magic = cpu_to_le32(0);
-		ext4_clear_inode_state(inode, EXT4_STATE_XATTR);
-	}
-	return 0;
-}
-
 int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
 			 struct ext4_xattr_info *i,
 			 struct ext4_xattr_ibody_find *is)
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index 2879761..2fe373f 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -21,6 +21,7 @@
 #define EXT4_XATTR_INDEX_TRUSTED		4
 #define	EXT4_XATTR_INDEX_LUSTRE			5
 #define EXT4_XATTR_INDEX_SECURITY	        6
+#define EXT4_XATTR_INDEX_SYSTEM_DATA		7
 
 struct ext4_xattr_header {
 	__le32	h_magic;	/* magic number for identification */
@@ -116,13 +117,26 @@ extern const struct xattr_handler *ext4_xattr_handlers[];
 
 extern int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
 				 struct ext4_xattr_ibody_find *is);
-extern int ext4_xattr_ibody_inline_set(handle_t *handle, struct inode *inode,
-				       struct ext4_xattr_info *i,
-				       struct ext4_xattr_ibody_find *is);
+extern int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
+			        struct ext4_xattr_info *i,
+			        struct ext4_xattr_ibody_find *is);
 extern int ext4_xattr_ibody_get(struct inode *inode, int name_index,
 				const char *name,
 				void *buffer, size_t buffer_size);
 
+extern int ext4_has_inline_data(struct inode *inode);
+extern int ext4_get_inline_size(struct inode *inode);
+extern int ext4_get_max_inline_size(struct inode *inode);
+extern int ext4_find_inline_data(struct inode *inode);
+extern void ext4_write_inline_data(struct inode *inode,
+				   struct ext4_iloc *iloc,
+				   void *buffer, loff_t pos,
+				   unsigned int len);
+extern int ext4_prepare_inline_data(handle_t *handle, struct inode *inode,
+				    unsigned int len);
+extern int ext4_init_inline_data(handle_t *handle, struct inode *inode,
+				 unsigned int len);
+extern int ext4_destroy_inline_data(handle_t *handle, struct inode *inode);
 # else  /* CONFIG_EXT4_FS_XATTR */
 
 static inline int
@@ -198,6 +212,46 @@ extern int ext4_xattr_ibody_get(struct inode *inode, int name_index,
 	return -EOPNOTSUPP;
 }
 
+static inline int ext4_find_inline_data(struct inode *inode)
+{
+	return 0;
+}
+
+static inline int ext4_has_inline_data(struct inode *inode)
+{
+	return 0;
+}
+
+static inline int ext4_get_inline_size(struct inode *inode)
+{
+	return 0;
+}
+
+static inline int ext4_get_max_inline_size(struct inode *inode)
+{
+	return 0;
+}
+
+static inline void ext4_write_inline_data(struct inode *inode,
+					  struct ext4_iloc *iloc,
+					  void *buffer, loff_t pos,
+					  unsigned int len)
+{
+	return;
+}
+
+static inline int ext4_init_inline_data(handle_t *handle,
+					struct inode *inode,
+					unsigned int len)
+{
+	return 0;
+}
+
+static inline int ext4_destroy_inline_data(handle_t *handle,
+					   struct inode *inode)
+{
+	return 0;
+}
 # endif  /* CONFIG_EXT4_FS_XATTR */
 
 #ifdef CONFIG_EXT4_FS_SECURITY
-- 
1.7.0.4


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

* [PATCH V4 04/22] ext4: Add read support for inline data.
  2012-02-20  7:01 ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Tao Ma
  2012-02-20  7:01   ` [PATCH V4 02/22] ext4: export inline xattr functions Tao Ma
  2012-02-20  7:01   ` [PATCH V4 03/22] ext4: Add the basic function for inline data support Tao Ma
@ 2012-02-20  7:01   ` Tao Ma
  2012-02-20  7:01   ` [PATCH V4 05/22] ext4: Add normal write " Tao Ma
                     ` (18 subsequent siblings)
  21 siblings, 0 replies; 32+ messages in thread
From: Tao Ma @ 2012-02-20  7:01 UTC (permalink / raw)
  To: linux-ext4; +Cc: linux-fsdevel

From: Tao Ma <boyu.mt@taobao.com>

Let readpage and readpages handle the case when we
want to read an inlined file.

Signed-off-by: Tao Ma <boyu.mt@taobao.comc.
---
 fs/ext4/inline.c |   61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ext4/inode.c  |   31 ++++++++++++++++++++++++++-
 fs/ext4/xattr.h  |    7 ++++++
 3 files changed, 98 insertions(+), 1 deletions(-)

diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index 77cedd2..97ca5de 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -444,6 +444,67 @@ out:
 	return error;
 }
 
+static int ext4_read_inline_page(struct inode *inode, struct page *page)
+{
+	void *kaddr;
+	int ret = 0;
+	size_t len;
+	struct ext4_iloc iloc;
+
+	BUG_ON(!PageLocked(page));
+	BUG_ON(!ext4_has_inline_data(inode));
+	BUG_ON(page->index);
+
+	if (!EXT4_I(inode)->i_inline_off) {
+		ext4_warning(inode->i_sb, "inode %lu doesn't have inline data.",
+			     inode->i_ino);
+		goto out;
+	}
+
+	ret = ext4_get_inode_loc(inode, &iloc);
+	if (ret)
+		goto out;
+
+	len = min_t(size_t, ext4_get_inline_size(inode), i_size_read(inode));
+	kaddr = kmap_atomic(page, KM_USER0);
+	ret = ext4_read_inline_data(inode, kaddr, len, &iloc);
+	flush_dcache_page(page);
+	kunmap_atomic(kaddr, KM_USER0);
+	zero_user_segment(page, len, PAGE_CACHE_SIZE);
+	SetPageUptodate(page);
+	brelse(iloc.bh);
+
+out:
+	return ret;
+}
+
+int ext4_readpage_inline(struct inode *inode, struct page *page)
+{
+	int ret = 0;
+
+	down_read(&EXT4_I(inode)->xattr_sem);
+	if (!ext4_has_inline_data(inode)) {
+		up_read(&EXT4_I(inode)->xattr_sem);
+		return -EAGAIN;
+	}
+
+	/*
+	 * Current inline data can only exist in the 1st page,
+	 * So for all the other pages, just set them uptodate.
+	 */
+	if (!page->index)
+		ret = ext4_read_inline_page(inode, page);
+	else if (!PageUptodate(page)) {
+		zero_user_segment(page, 0, PAGE_CACHE_SIZE);
+		SetPageUptodate(page);
+	}
+
+	up_read(&EXT4_I(inode)->xattr_sem);
+
+	unlock_page(page);
+	return ret >= 0 ? 0 : ret;
+}
+
 int ext4_destroy_inline_data(handle_t *handle, struct inode *inode)
 {
 	int ret;
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index b94a388..f631070 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -594,6 +594,9 @@ static int _ext4_get_block(struct inode *inode, sector_t iblock,
 	int ret = 0, started = 0;
 	int dio_credits;
 
+	if (ext4_has_inline_data(inode))
+		return -ERANGE;
+
 	map.m_lblk = iblock;
 	map.m_len = bh->b_size >> inode->i_blkbits;
 
@@ -2620,6 +2623,12 @@ static sector_t ext4_bmap(struct address_space *mapping, sector_t block)
 	journal_t *journal;
 	int err;
 
+	/*
+	 * XXX: Can we arrive here for a inline file? Maybe not.
+	 */
+	if (ext4_has_inline_data(inode))
+		return 0;
+
 	if (mapping_tagged(mapping, PAGECACHE_TAG_DIRTY) &&
 			test_opt(inode->i_sb, DELALLOC)) {
 		/*
@@ -2665,14 +2674,30 @@ static sector_t ext4_bmap(struct address_space *mapping, sector_t block)
 
 static int ext4_readpage(struct file *file, struct page *page)
 {
+	int ret = -EAGAIN;
+	struct inode *inode = page->mapping->host;
+
 	trace_ext4_readpage(page);
-	return mpage_readpage(page, ext4_get_block);
+
+	if (ext4_has_inline_data(inode))
+		ret = ext4_readpage_inline(inode, page);
+
+	if (ret == -EAGAIN)
+		return mpage_readpage(page, ext4_get_block);
+
+	return ret;
 }
 
 static int
 ext4_readpages(struct file *file, struct address_space *mapping,
 		struct list_head *pages, unsigned nr_pages)
 {
+	struct inode *inode = mapping->host;
+
+	/* If the file has inline data, no need to do readpages. */
+	if (ext4_has_inline_data(inode))
+		return 0;
+
 	return mpage_readpages(mapping, pages, nr_pages, ext4_get_block);
 }
 
@@ -2994,6 +3019,10 @@ static ssize_t ext4_direct_IO(int rw, struct kiocb *iocb,
 	if (ext4_should_journal_data(inode))
 		return 0;
 
+	/* Let buffer I/O handle the inline data case. */
+	if (ext4_has_inline_data(inode))
+		return 0;
+
 	trace_ext4_direct_IO_enter(inode, offset, iov_length(iov, nr_segs), rw);
 	if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
 		ret = ext4_ext_direct_IO(rw, iocb, iov, offset, nr_segs);
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index 2fe373f..8971d06 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -137,6 +137,8 @@ extern int ext4_prepare_inline_data(handle_t *handle, struct inode *inode,
 extern int ext4_init_inline_data(handle_t *handle, struct inode *inode,
 				 unsigned int len);
 extern int ext4_destroy_inline_data(handle_t *handle, struct inode *inode);
+
+extern int ext4_readpage_inline(struct inode *inode, struct page *page);
 # else  /* CONFIG_EXT4_FS_XATTR */
 
 static inline int
@@ -252,6 +254,11 @@ static inline int ext4_destroy_inline_data(handle_t *handle,
 {
 	return 0;
 }
+
+static inline int ext4_readpage_inline(struct inode *inode, struct page *page)
+{
+	return 0;
+}
 # endif  /* CONFIG_EXT4_FS_XATTR */
 
 #ifdef CONFIG_EXT4_FS_SECURITY
-- 
1.7.0.4


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

* [PATCH V4 05/22] ext4: Add normal write support for inline data.
  2012-02-20  7:01 ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Tao Ma
                     ` (2 preceding siblings ...)
  2012-02-20  7:01   ` [PATCH V4 04/22] ext4: Add read support for inline data Tao Ma
@ 2012-02-20  7:01   ` Tao Ma
  2012-02-20  7:01   ` [PATCH V4 06/22] ext4: Add journalled " Tao Ma
                     ` (17 subsequent siblings)
  21 siblings, 0 replies; 32+ messages in thread
From: Tao Ma @ 2012-02-20  7:01 UTC (permalink / raw)
  To: linux-ext4; +Cc: linux-fsdevel

From: Tao Ma <boyu.mt@taobao.com>

For a normal write case(not journalled write, not delayed allocation),
we write to the inline if the file is small and convert it to an extent
based file when the write is larger than the max inline size.

Signed-off-by: Tao Ma <boyu.mt@taobao.com>
---
 fs/ext4/ext4.h    |   11 +++
 fs/ext4/extents.c |    9 ++-
 fs/ext4/inline.c  |  232 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ext4/inode.c   |   59 +++++++++-----
 fs/ext4/xattr.h   |   26 ++++++
 5 files changed, 316 insertions(+), 21 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 18254c0..6d5a350 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1874,8 +1874,19 @@ struct buffer_head *ext4_getblk(handle_t *, struct inode *,
 						ext4_lblk_t, int, int *);
 struct buffer_head *ext4_bread(handle_t *, struct inode *,
 						ext4_lblk_t, int, int *);
+int ext4_get_block_write(struct inode *inode, sector_t iblock,
+			 struct buffer_head *bh_result, int create);
 int ext4_get_block(struct inode *inode, sector_t iblock,
 				struct buffer_head *bh_result, int create);
+int walk_page_buffers(handle_t *handle,
+			     struct buffer_head *head,
+			     unsigned from,
+			     unsigned to,
+			     int *partial,
+			     int (*fn)(handle_t *handle,
+				       struct buffer_head *bh));
+int do_journal_get_write_access(handle_t *handle,
+				struct buffer_head *bh);
 
 extern struct inode *ext4_iget(struct super_block *, unsigned long);
 extern int  ext4_write_inode(struct inode *, struct writeback_control *);
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 74f23c2..c861727 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -41,6 +41,7 @@
 #include <asm/uaccess.h>
 #include <linux/fiemap.h>
 #include "ext4_jbd2.h"
+#include "xattr.h"
 
 #include <trace/events/ext4.h>
 
@@ -2177,7 +2178,13 @@ int ext4_ext_calc_credits_for_single_extent(struct inode *inode, int nrblocks,
 int ext4_ext_index_trans_blocks(struct inode *inode, int nrblocks, int chunk)
 {
 	int index;
-	int depth = ext_depth(inode);
+	int depth;
+
+	/* If we are converting the inline data, only one is needed here. */
+	if (ext4_has_inline_data(inode))
+		return 1;
+
+	depth = ext_depth(inode);
 
 	if (chunk)
 		index = depth * 2;
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index 97ca5de..4946a24 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -14,6 +14,7 @@
 #include "ext4_jbd2.h"
 #include "ext4.h"
 #include "xattr.h"
+#include "truncate.h"
 
 #define EXT4_XATTR_SYSTEM_DATA_NAME	"data"
 #define EXT4_MIN_INLINE_DATA_SIZE	((sizeof(__le32) * EXT4_N_BLOCKS))
@@ -505,6 +506,237 @@ int ext4_readpage_inline(struct inode *inode, struct page *page)
 	return ret >= 0 ? 0 : ret;
 }
 
+static int ext4_convert_inline_data_to_extent(struct address_space *mapping,
+					      struct inode *inode,
+					      unsigned flags)
+{
+	int ret, needed_blocks;
+	handle_t *handle = NULL;
+	int retries = 0, sem_held = 0;
+	struct page *page = NULL;
+	unsigned from, to;
+	struct ext4_iloc iloc;
+
+	if (!ext4_has_inline_data(inode)) {
+		/*
+		 * clear the flag so that no new write
+		 * will trap here again.
+		 */
+		ext4_clear_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
+		return 0;
+	}
+
+	needed_blocks = ext4_writepage_trans_blocks(inode);
+
+	ret = ext4_get_inode_loc(inode, &iloc);
+	if (ret)
+		return ret;
+
+retry:
+	handle = ext4_journal_start(inode, needed_blocks);
+	if (IS_ERR(handle)) {
+		ret = PTR_ERR(handle);
+		handle = NULL;
+		goto out;
+	}
+
+	/* We cannot recurse into the filesystem as the transaction is already
+	 * started */
+	flags |= AOP_FLAG_NOFS;
+
+	page = grab_cache_page_write_begin(mapping, 0, flags);
+	if (!page) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	down_write(&EXT4_I(inode)->xattr_sem);
+	sem_held = 1;
+	/* If some one has already done this for us, just exit. */
+	if (!ext4_has_inline_data(inode)) {
+		ret = 0;
+		goto out;
+	}
+
+	from = 0;
+	to = ext4_get_inline_size(inode);
+	if (!PageUptodate(page)) {
+		ret = ext4_read_inline_page(inode, page);
+		if (ret < 0)
+			goto out;
+	}
+
+	ret = ext4_destroy_inline_data_nolock(handle, inode);
+	if (ret)
+		goto out;
+
+	if (ext4_should_dioread_nolock(inode))
+		ret = __block_write_begin(page, from, to, ext4_get_block_write);
+	else
+		ret = __block_write_begin(page, from, to, ext4_get_block);
+
+	if (!ret && ext4_should_journal_data(inode)) {
+		ret = walk_page_buffers(handle, page_buffers(page),
+				from, to, NULL, do_journal_get_write_access);
+	}
+
+	if (ret) {
+		unlock_page(page);
+		page_cache_release(page);
+		ext4_orphan_add(handle, inode);
+		up_write(&EXT4_I(inode)->xattr_sem);
+		sem_held = 0;
+		ext4_journal_stop(handle);
+		handle = NULL;
+		ext4_truncate_failed_write(inode);
+		/*
+		 * If truncate failed early the inode might
+		 * still be on the orphan list; we need to
+		 * make sure the inode is removed from the
+		 * orphan list in that case.
+		 */
+		if (inode->i_nlink)
+			ext4_orphan_del(NULL, inode);
+	}
+
+	if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
+		goto retry;
+
+	block_commit_write(page, from, to);
+out:
+	if (page) {
+		unlock_page(page);
+		page_cache_release(page);
+	}
+	if (sem_held)
+		up_write(&EXT4_I(inode)->xattr_sem);
+	if (handle)
+		ext4_journal_stop(handle);
+	brelse(iloc.bh);
+	return ret;
+}
+
+/*
+ * Try to write data in the inode.
+ * If the inode has inline data, check whether the new write can be
+ * in the inode also. If not, create the page the handle, move the data
+ * to the page make it update and let the later codes create extent for it.
+ */
+int ext4_try_to_write_inline_data(struct address_space *mapping,
+				  struct inode *inode,
+				  loff_t pos, unsigned len,
+				  unsigned flags,
+				  struct page **pagep)
+{
+	int ret;
+	handle_t *handle;
+	struct page *page;
+	struct ext4_iloc iloc;
+
+	if (pos + len > ext4_get_max_inline_size(inode))
+		goto convert;
+
+	ret = ext4_get_inode_loc(inode, &iloc);
+	if (ret)
+		return ret;
+
+	/*
+	 * The possible write could happen in the inode,
+	 * so try to reserve the space in inode first.
+	 */
+	handle = ext4_journal_start(inode, 1);
+	if (IS_ERR(handle)) {
+		ret = PTR_ERR(handle);
+		handle = NULL;
+		goto out;
+	}
+
+	ret = ext4_prepare_inline_data(handle, inode, pos + len);
+	if (ret && ret != -ENOSPC)
+		goto out;
+
+	/* We don't have space in inline inode, so convert it to extent. */
+	if (ret == -ENOSPC) {
+		ext4_journal_stop(handle);
+		brelse(iloc.bh);
+		goto convert;
+	}
+
+	flags |= AOP_FLAG_NOFS;
+
+	page = grab_cache_page_write_begin(mapping, 0, flags);
+	if (!page) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	*pagep = page;
+	down_read(&EXT4_I(inode)->xattr_sem);
+	if (!ext4_has_inline_data(inode)) {
+		ret = 0;
+		unlock_page(page);
+		page_cache_release(page);
+		goto out_up_read;
+	}
+
+	if (!PageUptodate(page)) {
+		ret = ext4_read_inline_page(inode, page);
+		if (ret < 0)
+			goto out_up_read;
+	}
+
+	ret = 1;
+	handle = NULL;
+out_up_read:
+	up_read(&EXT4_I(inode)->xattr_sem);
+out:
+	if (handle)
+		ext4_journal_stop(handle);
+	brelse(iloc.bh);
+	return ret;
+convert:
+	return ext4_convert_inline_data_to_extent(mapping,
+						  inode, flags);
+}
+
+int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len,
+			       unsigned copied, struct page *page)
+{
+	int ret;
+	void *kaddr;
+	struct ext4_iloc iloc;
+
+	if (unlikely(copied < len)) {
+		if (!PageUptodate(page)) {
+			copied = 0;
+			goto out;
+		}
+	}
+
+	ret = ext4_get_inode_loc(inode, &iloc);
+	if (ret) {
+		ext4_std_error(inode->i_sb, ret);
+		copied = 0;
+		goto out;
+	}
+
+	down_write(&EXT4_I(inode)->xattr_sem);
+	BUG_ON(!ext4_has_inline_data(inode));
+
+	kaddr = kmap_atomic(page, KM_USER0);
+	ext4_write_inline_data(inode, &iloc, kaddr, pos, len);
+	kunmap_atomic(kaddr, KM_USER0);
+	SetPageUptodate(page);
+	/* clear page dirty so that writepages wouldn't work for us. */
+	ClearPageDirty(page);
+
+	up_write(&EXT4_I(inode)->xattr_sem);
+	brelse(iloc.bh);
+out:
+	return copied;
+}
+
+
 int ext4_destroy_inline_data(handle_t *handle, struct inode *inode)
 {
 	int ret;
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index f631070..bae79b4 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -713,7 +713,7 @@ struct buffer_head *ext4_bread(handle_t *handle, struct inode *inode,
 	return NULL;
 }
 
-static int walk_page_buffers(handle_t *handle,
+int walk_page_buffers(handle_t *handle,
 			     struct buffer_head *head,
 			     unsigned from,
 			     unsigned to,
@@ -769,8 +769,8 @@ static int walk_page_buffers(handle_t *handle,
  * is elevated.  We'll still have enough credits for the tiny quotafile
  * write.
  */
-static int do_journal_get_write_access(handle_t *handle,
-				       struct buffer_head *bh)
+int do_journal_get_write_access(handle_t *handle,
+				struct buffer_head *bh)
 {
 	int dirty = buffer_dirty(bh);
 	int ret;
@@ -793,8 +793,6 @@ static int do_journal_get_write_access(handle_t *handle,
 	return ret;
 }
 
-static int ext4_get_block_write(struct inode *inode, sector_t iblock,
-		   struct buffer_head *bh_result, int create);
 static int ext4_write_begin(struct file *file, struct address_space *mapping,
 			    loff_t pos, unsigned len, unsigned flags,
 			    struct page **pagep, void **fsdata)
@@ -817,6 +815,17 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping,
 	from = pos & (PAGE_CACHE_SIZE - 1);
 	to = from + len;
 
+	if (ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) {
+		ret = ext4_try_to_write_inline_data(mapping, inode, pos, len,
+						    flags, pagep);
+		if (ret < 0)
+			goto out;
+		if (ret == 1) {
+			ret = 0;
+			goto out;
+		}
+	}
+
 retry:
 	handle = ext4_journal_start(inode, needed_blocks);
 	if (IS_ERR(handle)) {
@@ -834,6 +843,7 @@ retry:
 		ret = -ENOMEM;
 		goto out;
 	}
+
 	*pagep = page;
 
 	if (ext4_should_dioread_nolock(inode))
@@ -898,7 +908,12 @@ static int ext4_generic_write_end(struct file *file,
 	struct inode *inode = mapping->host;
 	handle_t *handle = ext4_journal_current_handle();
 
-	copied = block_write_end(file, mapping, pos, len, copied, page, fsdata);
+	if (ext4_has_inline_data(inode))
+		copied = ext4_write_inline_data_end(inode, pos, len,
+						    copied, page);
+	else
+		copied = block_write_end(file, mapping, pos,
+					 len, copied, page, fsdata);
 
 	/*
 	 * No need to use i_size_read() here, the i_size
@@ -2764,7 +2779,7 @@ static int ext4_releasepage(struct page *page, gfp_t wait)
  * We allocate an uinitialized extent if blocks haven't been allocated.
  * The extent will be converted to initialized after the IO is complete.
  */
-static int ext4_get_block_write(struct inode *inode, sector_t iblock,
+int ext4_get_block_write(struct inode *inode, sector_t iblock,
 		   struct buffer_head *bh_result, int create)
 {
 	ext4_debug("ext4_get_block_write: inode %lu, create flag %d\n",
@@ -3648,7 +3663,8 @@ static inline void ext4_iget_extra_inode(struct inode *inode,
 	if (*magic == cpu_to_le32(EXT4_XATTR_MAGIC)) {
 		ext4_set_inode_state(inode, EXT4_STATE_XATTR);
 		ext4_find_inline_data(inode);
-	}
+	} else
+		EXT4_I(inode)->i_inline_off = 0;
 }
 
 struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
@@ -3786,17 +3802,19 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
 				 ei->i_file_acl);
 		ret = -EIO;
 		goto bad_inode;
-	} else if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
-		if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
-		    (S_ISLNK(inode->i_mode) &&
-		     !ext4_inode_is_fast_symlink(inode)))
-			/* Validate extent which is part of inode */
-			ret = ext4_ext_check_inode(inode);
-	} else if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
-		   (S_ISLNK(inode->i_mode) &&
-		    !ext4_inode_is_fast_symlink(inode))) {
-		/* Validate block references which are part of inode */
-		ret = ext4_ind_check_inode(inode);
+	} else if (!ext4_has_inline_data(inode)) {
+		if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
+			if ((S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+			    (S_ISLNK(inode->i_mode) &&
+			     !ext4_inode_is_fast_symlink(inode))))
+				/* Validate extent which is part of inode */
+				ret = ext4_ext_check_inode(inode);
+		} else if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+			   (S_ISLNK(inode->i_mode) &&
+			    !ext4_inode_is_fast_symlink(inode))) {
+			/* Validate block references which are part of inode */
+			ret = ext4_ind_check_inode(inode);
+		}
 	}
 	if (ret)
 		goto bad_inode;
@@ -3979,9 +3997,10 @@ static int ext4_do_update_inode(handle_t *handle,
 				cpu_to_le32(new_encode_dev(inode->i_rdev));
 			raw_inode->i_block[2] = 0;
 		}
-	} else
+	} else if (!ext4_has_inline_data(inode)) {
 		for (block = 0; block < EXT4_N_BLOCKS; block++)
 			raw_inode->i_block[block] = ei->i_data[block];
+	}
 
 	raw_inode->i_disk_version = cpu_to_le32(inode->i_version);
 	if (ei->i_extra_isize) {
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index 8971d06..b9e5cec 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -139,6 +139,15 @@ extern int ext4_init_inline_data(handle_t *handle, struct inode *inode,
 extern int ext4_destroy_inline_data(handle_t *handle, struct inode *inode);
 
 extern int ext4_readpage_inline(struct inode *inode, struct page *page);
+extern int ext4_try_to_write_inline_data(struct address_space *mapping,
+					 struct inode *inode,
+					 loff_t pos, unsigned len,
+					 unsigned flags,
+					 struct page **pagep);
+extern int ext4_write_inline_data_end(struct inode *inode,
+				      loff_t pos, unsigned len,
+				      unsigned copied,
+				      struct page *page);
 # else  /* CONFIG_EXT4_FS_XATTR */
 
 static inline int
@@ -259,6 +268,23 @@ static inline int ext4_readpage_inline(struct inode *inode, struct page *page)
 {
 	return 0;
 }
+
+static inline int ext4_try_to_write_inline_data(struct address_space *mapping,
+						struct inode *inode,
+						loff_t pos, unsigned len,
+						unsigned flags,
+						struct page **pagep)
+{
+	return 0;
+}
+
+static inline int ext4_write_inline_data_end(struct inode *inode,
+					     loff_t pos, unsigned len,
+					     unsigned copied,
+					     struct page *page)
+{
+	return 0;
+}
 # endif  /* CONFIG_EXT4_FS_XATTR */
 
 #ifdef CONFIG_EXT4_FS_SECURITY
-- 
1.7.0.4


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

* [PATCH V4 06/22] ext4: Add journalled write support for inline data.
  2012-02-20  7:01 ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Tao Ma
                     ` (3 preceding siblings ...)
  2012-02-20  7:01   ` [PATCH V4 05/22] ext4: Add normal write " Tao Ma
@ 2012-02-20  7:01   ` Tao Ma
  2012-02-20  7:01   ` [PATCH V4 07/22] ext4: Add delalloc " Tao Ma
                     ` (16 subsequent siblings)
  21 siblings, 0 replies; 32+ messages in thread
From: Tao Ma @ 2012-02-20  7:01 UTC (permalink / raw)
  To: linux-ext4; +Cc: linux-fsdevel

From: Tao Ma <boyu.mt@taobao.com>

Signed-off-by: Tao Ma <boyu.mt@taobao.com>
---
 fs/ext4/inline.c |   24 ++++++++++++++++++++++
 fs/ext4/inode.c  |   59 +++++++++++++++++++++++++++++++++++++----------------
 fs/ext4/xattr.h  |   12 +++++++++++
 3 files changed, 77 insertions(+), 18 deletions(-)

diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index 4946a24..94359f0 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -736,6 +736,30 @@ out:
 	return copied;
 }
 
+struct buffer_head *
+ext4_journalled_write_inline_data(struct inode *inode,
+				  unsigned len,
+				  struct page *page)
+{
+	int ret;
+	void *kaddr;
+	struct ext4_iloc iloc;
+
+	ret = ext4_get_inode_loc(inode, &iloc);
+	if (ret) {
+		ext4_std_error(inode->i_sb, ret);
+		return NULL;
+	}
+
+	down_write(&EXT4_I(inode)->xattr_sem);
+	kaddr = kmap_atomic(page, KM_USER0);
+	ext4_write_inline_data(inode, &iloc, kaddr, 0, len);
+	kunmap_atomic(kaddr, KM_USER0);
+	up_write(&EXT4_I(inode)->xattr_sem);
+
+	return iloc.bh;
+}
+
 
 int ext4_destroy_inline_data(handle_t *handle, struct inode *inode)
 {
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index bae79b4..e4bcae5 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1064,16 +1064,21 @@ static int ext4_journalled_write_end(struct file *file,
 
 	BUG_ON(!ext4_handle_valid(handle));
 
-	if (copied < len) {
-		if (!PageUptodate(page))
-			copied = 0;
-		page_zero_new_buffers(page, from+copied, to);
-	}
+	if (ext4_has_inline_data(inode))
+		copied = ext4_write_inline_data_end(inode, pos, len,
+						    copied, page);
+	else {
+		if (copied < len) {
+			if (!PageUptodate(page))
+				copied = 0;
+			page_zero_new_buffers(page, from+copied, to);
+		}
 
-	ret = walk_page_buffers(handle, page_buffers(page), from,
-				to, &partial, write_end_fn);
-	if (!partial)
-		SetPageUptodate(page);
+		ret = walk_page_buffers(handle, page_buffers(page), from,
+					to, &partial, write_end_fn);
+		if (!partial)
+			SetPageUptodate(page);
+	}
 	new_i_size = pos + copied;
 	if (new_i_size > inode->i_size)
 		i_size_write(inode, pos+copied);
@@ -1835,15 +1840,23 @@ static int __ext4_journalled_writepage(struct page *page,
 {
 	struct address_space *mapping = page->mapping;
 	struct inode *inode = mapping->host;
-	struct buffer_head *page_bufs;
+	struct buffer_head *page_bufs = NULL;
 	handle_t *handle = NULL;
 	int ret = 0;
 	int err;
+	struct buffer_head *inode_bh = NULL;
 
 	ClearPageChecked(page);
-	page_bufs = page_buffers(page);
-	BUG_ON(!page_bufs);
-	walk_page_buffers(handle, page_bufs, 0, len, NULL, bget_one);
+
+	if (ext4_has_inline_data(inode)) {
+		BUG_ON(page->index != 0);
+		BUG_ON(len > ext4_get_max_inline_size(inode));
+		inode_bh = ext4_journalled_write_inline_data(inode, len, page);
+	} else {
+		page_bufs = page_buffers(page);
+		BUG_ON(!page_bufs);
+		walk_page_buffers(handle, page_bufs, 0, len, NULL, bget_one);
+	}
 	/* As soon as we unlock the page, it can go away, but we have
 	 * references to buffers so we are safe */
 	unlock_page(page);
@@ -1856,11 +1869,19 @@ static int __ext4_journalled_writepage(struct page *page,
 
 	BUG_ON(!ext4_handle_valid(handle));
 
-	ret = walk_page_buffers(handle, page_bufs, 0, len, NULL,
-				do_journal_get_write_access);
+	if (ext4_has_inline_data(inode) && inode_bh) {
+		ret = ext4_journal_get_write_access(handle, inode_bh);
+
+		err = ext4_handle_dirty_metadata(handle, inode, inode_bh);
+
+	} else {
+		ret = walk_page_buffers(handle, page_bufs, 0, len, NULL,
+					do_journal_get_write_access);
+
+		err = walk_page_buffers(handle, page_bufs, 0, len, NULL,
+					write_end_fn);
+	}
 
-	err = walk_page_buffers(handle, page_bufs, 0, len, NULL,
-				write_end_fn);
 	if (ret == 0)
 		ret = err;
 	EXT4_I(inode)->i_datasync_tid = handle->h_transaction->t_tid;
@@ -1868,9 +1889,11 @@ static int __ext4_journalled_writepage(struct page *page,
 	if (!ret)
 		ret = err;
 
-	walk_page_buffers(handle, page_bufs, 0, len, NULL, bput_one);
+	if (!ext4_has_inline_data(inode))
+		walk_page_buffers(handle, page_bufs, 0, len, NULL, bput_one);
 	ext4_set_inode_state(inode, EXT4_STATE_JDATA);
 out:
+	brelse(inode_bh);
 	return ret;
 }
 
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index b9e5cec..9eb42c6 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -148,6 +148,10 @@ extern int ext4_write_inline_data_end(struct inode *inode,
 				      loff_t pos, unsigned len,
 				      unsigned copied,
 				      struct page *page);
+extern struct buffer_head *
+ext4_journalled_write_inline_data(struct inode *inode,
+				  unsigned len,
+				  struct page *page);
 # else  /* CONFIG_EXT4_FS_XATTR */
 
 static inline int
@@ -285,6 +289,14 @@ static inline int ext4_write_inline_data_end(struct inode *inode,
 {
 	return 0;
 }
+
+static inline struct buffer_head *
+ext4_journalled_write_inline_data(struct inode *inode,
+				  unsigned len,
+				  struct page *page)
+{
+	return NULL;
+}
 # endif  /* CONFIG_EXT4_FS_XATTR */
 
 #ifdef CONFIG_EXT4_FS_SECURITY
-- 
1.7.0.4


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

* [PATCH V4 07/22] ext4: Add delalloc support for inline data.
  2012-02-20  7:01 ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Tao Ma
                     ` (4 preceding siblings ...)
  2012-02-20  7:01   ` [PATCH V4 06/22] ext4: Add journalled " Tao Ma
@ 2012-02-20  7:01   ` Tao Ma
  2012-02-20  7:01   ` [PATCH V4 08/22] ext4: Create a new function ext4_init_new_dir Tao Ma
                     ` (15 subsequent siblings)
  21 siblings, 0 replies; 32+ messages in thread
From: Tao Ma @ 2012-02-20  7:01 UTC (permalink / raw)
  To: linux-ext4; +Cc: linux-fsdevel

From: Tao Ma <boyu.mt@taobao.com>

For delayed allocation mode, we write to inline data if the file
is small enough. And in case of we write to some offset larger
than the inline size, the 1st page is dirtied, so that
ext4_da_writepages can handle the conversion. When the 1st page
is initialized with blocks, the inline part is removed.

Signed-off-by: Tao Ma <boyu.mt@taobao.com>
---
 fs/ext4/ext4.h   |    4 +
 fs/ext4/inline.c |  177 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ext4/inode.c  |   63 ++++++++++++++++---
 fs/ext4/xattr.h  |   27 ++++++++
 4 files changed, 262 insertions(+), 9 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 6d5a350..48c77c9 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1878,6 +1878,8 @@ int ext4_get_block_write(struct inode *inode, sector_t iblock,
 			 struct buffer_head *bh_result, int create);
 int ext4_get_block(struct inode *inode, sector_t iblock,
 				struct buffer_head *bh_result, int create);
+int ext4_da_get_block_prep(struct inode *inode, sector_t iblock,
+			   struct buffer_head *bh, int create);
 int walk_page_buffers(handle_t *handle,
 			     struct buffer_head *head,
 			     unsigned from,
@@ -1887,6 +1889,8 @@ int walk_page_buffers(handle_t *handle,
 				       struct buffer_head *bh));
 int do_journal_get_write_access(handle_t *handle,
 				struct buffer_head *bh);
+#define FALL_BACK_TO_NONDELALLOC 1
+#define CONVERT_INLINE_DATA	 2
 
 extern struct inode *ext4_iget(struct super_block *, unsigned long);
 extern int  ext4_write_inode(struct inode *, struct writeback_control *);
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index 94359f0..4e3dc98 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -760,6 +760,183 @@ ext4_journalled_write_inline_data(struct inode *inode,
 	return iloc.bh;
 }
 
+/*
+ * Try to make the page cache and handle ready for the inline data case.
+ * We can call this function in 2 cases:
+ * 1. The inode is created and the first write exceeds inline size. We can
+ *    clear the inode state safely.
+ * 2. The inode has inline data, then we need to read the data, make it
+ *    update and dirty so that ext4_da_writepages can handle it. We don't
+ *    need to start the journal since the file's metatdata isn't changed now.
+ */
+static int ext4_da_convert_inline_data_to_extent(struct address_space *mapping,
+						 struct inode *inode,
+						 unsigned flags,
+						 void **fsdata)
+{
+	int ret = 0, inline_size;
+	struct page *page;
+
+	page = grab_cache_page_write_begin(mapping, 0, flags);
+	if (!page)
+		return -ENOMEM;
+
+	down_read(&EXT4_I(inode)->xattr_sem);
+	if (!ext4_has_inline_data(inode)) {
+		ext4_clear_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
+		goto out;
+	}
+
+	inline_size = ext4_get_inline_size(inode);
+
+	if (!PageUptodate(page)) {
+		ret = ext4_read_inline_page(inode, page);
+		if (ret < 0)
+			goto out;
+	}
+
+	ret = __block_write_begin(page, 0, inline_size,
+				  ext4_da_get_block_prep);
+	if (ret) {
+		ext4_truncate_failed_write(inode);
+		goto out;
+	}
+
+	SetPageDirty(page);
+	SetPageUptodate(page);
+	ext4_clear_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
+	*fsdata = (void *)CONVERT_INLINE_DATA;
+
+out:
+	up_read(&EXT4_I(inode)->xattr_sem);
+	if (page) {
+		unlock_page(page);
+		page_cache_release(page);
+	}
+	return ret;
+}
+
+/*
+ * Prepare the write for the inline data.
+ * If the the data can be written into the inode, we just read
+ * the page and make it uptodate, and start the journal.
+ * Otherwise read the page, makes it dirty so that it can be
+ * handle in writepages(the i_disksize update is left to the
+ * normal ext4_da_write_end).
+ */
+int ext4_da_write_inline_data_begin(struct address_space *mapping,
+				    struct inode *inode,
+				    loff_t pos, unsigned len,
+				    unsigned flags,
+				    struct page **pagep,
+				    void **fsdata)
+{
+	int ret, inline_size;
+	handle_t *handle;
+	struct page *page;
+	struct ext4_iloc iloc;
+
+	ret = ext4_get_inode_loc(inode, &iloc);
+	if (ret)
+		return ret;
+
+	handle = ext4_journal_start(inode, 1);
+	if (IS_ERR(handle)) {
+		ret = PTR_ERR(handle);
+		handle = NULL;
+		goto out;
+	}
+
+	inline_size = ext4_get_max_inline_size(inode);
+
+	ret = -ENOSPC;
+	if (inline_size >= pos + len) {
+		ret = ext4_prepare_inline_data(handle, inode, pos + len);
+		if (ret && ret != -ENOSPC)
+			goto out;
+	}
+
+	if (ret == -ENOSPC) {
+		ret = ext4_da_convert_inline_data_to_extent(mapping,
+							    inode,
+							    flags,
+							    fsdata);
+		goto out;
+	}
+
+	/*
+	 * We cannot recurse into the filesystem as the transaction
+	 * is already started.
+	 */
+	flags |= AOP_FLAG_NOFS;
+
+	page = grab_cache_page_write_begin(mapping, 0, flags);
+	if (!page) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	down_read(&EXT4_I(inode)->xattr_sem);
+	if (!ext4_has_inline_data(inode)) {
+		ret = 0;
+		goto out_release_page;
+	}
+
+	if (!PageUptodate(page)) {
+		ret = ext4_read_inline_page(inode, page);
+		if (ret < 0)
+			goto out_release_page;
+	}
+
+	up_read(&EXT4_I(inode)->xattr_sem);
+	*pagep = page;
+	handle = NULL;
+	brelse(iloc.bh);
+	return 1;
+out_release_page:
+	up_read(&EXT4_I(inode)->xattr_sem);
+	unlock_page(page);
+	page_cache_release(page);
+out:
+	if (handle)
+		ext4_journal_stop(handle);
+	brelse(iloc.bh);
+	return ret;
+}
+
+int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos,
+				  unsigned len, unsigned copied,
+				  struct page *page)
+{
+	int i_size_changed = 0;
+
+	copied = ext4_write_inline_data_end(inode, pos, len, copied, page);
+
+	/*
+	 * No need to use i_size_read() here, the i_size
+	 * cannot change under us because we hold i_mutex.
+	 *
+	 * But it's important to update i_size while still holding page lock:
+	 * page writeout could otherwise come in and zero beyond i_size.
+	 */
+	if (pos+copied > inode->i_size) {
+		i_size_write(inode, pos+copied);
+		i_size_changed = 1;
+	}
+	unlock_page(page);
+	page_cache_release(page);
+
+	/*
+	 * Don't mark the inode dirty under page lock. First, it unnecessarily
+	 * makes the holding time of page lock longer. Second, it forces lock
+	 * ordering of page lock and transaction start for journaling
+	 * filesystems.
+	 */
+	if (i_size_changed)
+		mark_inode_dirty(inode);
+
+	return copied;
+}
 
 int ext4_destroy_inline_data(handle_t *handle, struct inode *inode)
 {
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index e4bcae5..549af8f 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1718,7 +1718,19 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock,
 	 * file system block.
 	 */
 	down_read((&EXT4_I(inode)->i_data_sem));
-	if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
+	if (ext4_has_inline_data(inode)) {
+		/*
+		 * We will soon create blocks for this page, and let
+		 * us pretend as if the blocks aren't allocated yet.
+		 * In case of clusters, we have to handle the work
+		 * of mapping from cluster so that the reserved space
+		 * is calculated properly.
+		 */
+		if ((EXT4_SB(inode->i_sb)->s_cluster_ratio > 1) &&
+		    ext4_find_delalloc_cluster(inode, map->m_lblk, 0))
+			map->m_flags |= EXT4_MAP_FROM_CLUSTER;
+		retval = 0;
+	} else if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
 		retval = ext4_ext_map_blocks(NULL, inode, map, 0);
 	else
 		retval = ext4_ind_map_blocks(NULL, inode, map, 0);
@@ -1765,8 +1777,8 @@ out_unlock:
  * We also have b_blocknr = physicalblock mapping unwritten extent and b_bdev
  * initialized properly.
  */
-static int ext4_da_get_block_prep(struct inode *inode, sector_t iblock,
-				  struct buffer_head *bh, int create)
+int ext4_da_get_block_prep(struct inode *inode, sector_t iblock,
+			   struct buffer_head *bh, int create)
 {
 	struct ext4_map_blocks map;
 	int ret = 0;
@@ -2040,7 +2052,8 @@ static int ext4_da_writepages_trans_blocks(struct inode *inode)
  * mpage_da_map_and_submit to map a single contiguous memory region
  * and then write them.
  */
-static int write_cache_pages_da(struct address_space *mapping,
+static int write_cache_pages_da(handle_t *handle,
+				struct address_space *mapping,
 				struct writeback_control *wbc,
 				struct mpage_da_data *mpd,
 				pgoff_t *done_index)
@@ -2119,6 +2132,17 @@ static int write_cache_pages_da(struct address_space *mapping,
 			wait_on_page_writeback(page);
 			BUG_ON(PageWriteback(page));
 
+			/*
+			 * If we have inline data and arrive here, it means that
+			 * we will soon create the block for the 1st page, so
+			 * we'd better clear the inline data here.
+			 */
+			if (ext4_has_inline_data(inode)) {
+				BUG_ON(ext4_test_inode_state(inode,
+						EXT4_STATE_MAY_INLINE_DATA));
+				ext4_destroy_inline_data(handle, inode);
+			}
+
 			if (mpd->next_page != page->index)
 				mpd->first_page = page->index;
 			mpd->next_page = page->index + 1;
@@ -2325,7 +2349,8 @@ retry:
 		 * contiguous region of logical blocks that need
 		 * blocks to be allocated by ext4 and submit them.
 		 */
-		ret = write_cache_pages_da(mapping, wbc, &mpd, &done_index);
+		ret = write_cache_pages_da(handle, mapping,
+					   wbc, &mpd, &done_index);
 		/*
 		 * If we have a contiguous extent of pages and we
 		 * haven't done the I/O yet, map the blocks and submit
@@ -2389,7 +2414,6 @@ out_writepages:
 	return ret;
 }
 
-#define FALL_BACK_TO_NONDELALLOC 1
 static int ext4_nonda_switch(struct super_block *sb)
 {
 	s64 free_blocks, dirty_blocks;
@@ -2443,6 +2467,19 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping,
 	}
 	*fsdata = (void *)0;
 	trace_ext4_da_write_begin(inode, pos, len, flags);
+
+	if (ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) {
+		ret = ext4_da_write_inline_data_begin(mapping, inode,
+						      pos, len, flags,
+						      pagep, fsdata);
+		if (ret < 0)
+			goto out;
+		if (ret == 1) {
+			ret = 0;
+			goto out;
+		}
+	}
+
 retry:
 	/*
 	 * With delayed allocation, we don't log the i_disksize update
@@ -2543,10 +2580,10 @@ static int ext4_da_write_end(struct file *file,
 	 * changes.  So let's piggyback the i_disksize mark_inode_dirty
 	 * into that.
 	 */
-
 	new_i_size = pos + copied;
 	if (copied && new_i_size > EXT4_I(inode)->i_disksize) {
-		if (ext4_da_should_update_i_disksize(page, end)) {
+		if (ext4_has_inline_data(inode) ||
+		    ext4_da_should_update_i_disksize(page, end)) {
 			down_write(&EXT4_I(inode)->i_data_sem);
 			if (new_i_size > EXT4_I(inode)->i_disksize) {
 				/*
@@ -2567,8 +2604,16 @@ static int ext4_da_write_end(struct file *file,
 			ext4_mark_inode_dirty(handle, inode);
 		}
 	}
-	ret2 = generic_write_end(file, mapping, pos, len, copied,
+
+	if (write_mode != CONVERT_INLINE_DATA &&
+	    ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA) &&
+	    ext4_has_inline_data(inode))
+		ret2 = ext4_da_write_inline_data_end(inode, pos, len, copied,
+						     page);
+	else
+		ret2 = generic_write_end(file, mapping, pos, len, copied,
 							page, fsdata);
+
 	copied = ret2;
 	if (ret2 < 0)
 		ret = ret2;
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index 9eb42c6..7825aff 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -152,6 +152,15 @@ extern struct buffer_head *
 ext4_journalled_write_inline_data(struct inode *inode,
 				  unsigned len,
 				  struct page *page);
+extern int ext4_da_write_inline_data_begin(struct address_space *mapping,
+					   struct inode *inode,
+					   loff_t pos, unsigned len,
+					   unsigned flags,
+					   struct page **pagep,
+					   void **fsdata);
+extern int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos,
+					 unsigned len, unsigned copied,
+					 struct page *page);
 # else  /* CONFIG_EXT4_FS_XATTR */
 
 static inline int
@@ -297,6 +306,24 @@ ext4_journalled_write_inline_data(struct inode *inode,
 {
 	return NULL;
 }
+
+static inline int
+ext4_da_write_inline_data_begin(struct address_space *mapping,
+				struct inode *inode,
+				loff_t pos, unsigned len,
+				unsigned flags,
+				struct page **pagep,
+				void **fsdata)
+{
+	return 0;
+}
+
+static inline int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos,
+						unsigned len, unsigned copied,
+						struct page *page)
+{
+	return 0;
+}
 # endif  /* CONFIG_EXT4_FS_XATTR */
 
 #ifdef CONFIG_EXT4_FS_SECURITY
-- 
1.7.0.4


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

* [PATCH V4 08/22] ext4: Create a new function ext4_init_new_dir.
  2012-02-20  7:01 ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Tao Ma
                     ` (5 preceding siblings ...)
  2012-02-20  7:01   ` [PATCH V4 07/22] ext4: Add delalloc " Tao Ma
@ 2012-02-20  7:01   ` Tao Ma
  2012-02-20  7:01   ` [PATCH V4 09/22] ext4: Refactor __ext4_check_dir_entry to accepts start and size Tao Ma
                     ` (14 subsequent siblings)
  21 siblings, 0 replies; 32+ messages in thread
From: Tao Ma @ 2012-02-20  7:01 UTC (permalink / raw)
  To: linux-ext4; +Cc: linux-fsdevel

From: Tao Ma <boyu.mt@taobao.com>

Currently, the initialization of dot and dotdot are encapsulated in
ext4_mkdir and also bond with dir_block. So create a new function
named ext4_init_new_dir and the initialization is moved to
ext4_init_dot_dotdot which only accepts a 'de'.

Signed-off-by: Tao Ma <boyu.mt@taobao.com>
---
 fs/ext4/ext4.h  |    2 +
 fs/ext4/namei.c |   74 +++++++++++++++++++++++++++++++++---------------------
 2 files changed, 47 insertions(+), 29 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 48c77c9..25cac6f 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2261,6 +2261,8 @@ extern loff_t ext4_llseek(struct file *file, loff_t offset, int origin);
 extern const struct inode_operations ext4_dir_inode_operations;
 extern const struct inode_operations ext4_special_inode_operations;
 extern struct dentry *ext4_get_parent(struct dentry *child);
+void ext4_init_dot_dotdot(struct inode *parent, struct inode *inode,
+			  struct ext4_dir_entry_2 *de, int blocksize);
 
 /* symlink.c */
 extern const struct inode_operations ext4_symlink_inode_operations;
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 2043f48..8a9645a 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -1806,13 +1806,54 @@ retry:
 	return err;
 }
 
+void ext4_init_dot_dotdot(struct inode *parent, struct inode *inode,
+			  struct ext4_dir_entry_2 *de, int blocksize)
+{
+	de->inode = cpu_to_le32(inode->i_ino);
+	de->name_len = 1;
+	de->rec_len = ext4_rec_len_to_disk(EXT4_DIR_REC_LEN(de->name_len),
+					   blocksize);
+	strcpy(de->name, ".");
+	ext4_set_de_type(parent->i_sb, de, S_IFDIR);
+	de = ext4_next_entry(de, blocksize);
+	de->inode = cpu_to_le32(parent->i_ino);
+	de->rec_len = ext4_rec_len_to_disk(blocksize - EXT4_DIR_REC_LEN(1),
+					   blocksize);
+	de->name_len = 2;
+	strcpy(de->name, "..");
+	ext4_set_de_type(parent->i_sb, de, S_IFDIR);
+	set_nlink(inode, 2);
+}
+
+static int ext4_init_new_dir(handle_t *handle, struct inode *parent,
+			     struct inode *inode)
+{
+	struct buffer_head *dir_block = NULL;
+	struct ext4_dir_entry_2 *de;
+	int err;
+	int blocksize = inode->i_sb->s_blocksize;
+
+	inode->i_size = EXT4_I(inode)->i_disksize = blocksize;
+	dir_block = ext4_bread(handle, inode, 0, 1, &err);
+	if (!dir_block)
+		goto out;
+	BUFFER_TRACE(dir_block, "get_write_access");
+	err = ext4_journal_get_write_access(handle, dir_block);
+	if (err)
+		goto out;
+	de = (struct ext4_dir_entry_2 *)dir_block->b_data;
+	ext4_init_dot_dotdot(parent, inode, de, blocksize);
+	BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata");
+	err = ext4_handle_dirty_metadata(handle, inode, dir_block);
+out:
+	brelse(dir_block);
+	return err;
+}
+
 static int ext4_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
 {
 	handle_t *handle;
 	struct inode *inode;
-	struct buffer_head *dir_block = NULL;
-	struct ext4_dir_entry_2 *de;
-	unsigned int blocksize = dir->i_sb->s_blocksize;
 	int err, retries = 0;
 
 	if (EXT4_DIR_LINK_MAX(dir))
@@ -1838,31 +1879,7 @@ retry:
 
 	inode->i_op = &ext4_dir_inode_operations;
 	inode->i_fop = &ext4_dir_operations;
-	inode->i_size = EXT4_I(inode)->i_disksize = inode->i_sb->s_blocksize;
-	dir_block = ext4_bread(handle, inode, 0, 1, &err);
-	if (!dir_block)
-		goto out_clear_inode;
-	BUFFER_TRACE(dir_block, "get_write_access");
-	err = ext4_journal_get_write_access(handle, dir_block);
-	if (err)
-		goto out_clear_inode;
-	de = (struct ext4_dir_entry_2 *) dir_block->b_data;
-	de->inode = cpu_to_le32(inode->i_ino);
-	de->name_len = 1;
-	de->rec_len = ext4_rec_len_to_disk(EXT4_DIR_REC_LEN(de->name_len),
-					   blocksize);
-	strcpy(de->name, ".");
-	ext4_set_de_type(dir->i_sb, de, S_IFDIR);
-	de = ext4_next_entry(de, blocksize);
-	de->inode = cpu_to_le32(dir->i_ino);
-	de->rec_len = ext4_rec_len_to_disk(blocksize - EXT4_DIR_REC_LEN(1),
-					   blocksize);
-	de->name_len = 2;
-	strcpy(de->name, "..");
-	ext4_set_de_type(dir->i_sb, de, S_IFDIR);
-	set_nlink(inode, 2);
-	BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata");
-	err = ext4_handle_dirty_metadata(handle, inode, dir_block);
+	err = ext4_init_new_dir(handle, dir, inode);
 	if (err)
 		goto out_clear_inode;
 	err = ext4_mark_inode_dirty(handle, inode);
@@ -1884,7 +1901,6 @@ out_clear_inode:
 	d_instantiate(dentry, inode);
 	unlock_new_inode(inode);
 out_stop:
-	brelse(dir_block);
 	ext4_journal_stop(handle);
 	if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries))
 		goto retry;
-- 
1.7.0.4


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

* [PATCH V4 09/22] ext4: Refactor __ext4_check_dir_entry to accepts start and size.
  2012-02-20  7:01 ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Tao Ma
                     ` (6 preceding siblings ...)
  2012-02-20  7:01   ` [PATCH V4 08/22] ext4: Create a new function ext4_init_new_dir Tao Ma
@ 2012-02-20  7:01   ` Tao Ma
  2012-02-20  7:01   ` [PATCH V4 10/22] ext4: Create __ext4_insert_dentry for dir entry insertion Tao Ma
                     ` (13 subsequent siblings)
  21 siblings, 0 replies; 32+ messages in thread
From: Tao Ma @ 2012-02-20  7:01 UTC (permalink / raw)
  To: linux-ext4; +Cc: linux-fsdevel

From: Tao Ma <boyu.mt@taobao.com>

__ext4_check_dir_entry is used to check whether the de is over
the block boundary. Now with inline data, it could be within the
block boundary while exceeds the inode size. So check this function
to check the overflow more precisely.

Signed-off-by: Tao Ma <boyu.mt@taobao.com>
---
 fs/ext4/dir.c   |   16 ++++++++--------
 fs/ext4/ext4.h  |    7 ++++---
 fs/ext4/namei.c |   13 +++++++++----
 3 files changed, 21 insertions(+), 15 deletions(-)

diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c
index 164c560..6c2199e 100644
--- a/fs/ext4/dir.c
+++ b/fs/ext4/dir.c
@@ -68,7 +68,7 @@ static unsigned char get_dtype(struct super_block *sb, int filetype)
 int __ext4_check_dir_entry(const char *function, unsigned int line,
 			   struct inode *dir, struct file *filp,
 			   struct ext4_dir_entry_2 *de,
-			   struct buffer_head *bh,
+			   struct buffer_head *bh, char *buf, int size,
 			   unsigned int offset)
 {
 	const char *error_msg = NULL;
@@ -81,9 +81,8 @@ int __ext4_check_dir_entry(const char *function, unsigned int line,
 		error_msg = "rec_len % 4 != 0";
 	else if (unlikely(rlen < EXT4_DIR_REC_LEN(de->name_len)))
 		error_msg = "rec_len is too small for name_len";
-	else if (unlikely(((char *) de - bh->b_data) + rlen >
-			  dir->i_sb->s_blocksize))
-		error_msg = "directory entry across blocks";
+	else if (unlikely(((char *) de - buf) + rlen > size))
+		error_msg = "directory entry across range";
 	else if (unlikely(le32_to_cpu(de->inode) >
 			le32_to_cpu(EXT4_SB(dir->i_sb)->s_es->s_inodes_count)))
 		error_msg = "inode out of bounds";
@@ -94,14 +93,14 @@ int __ext4_check_dir_entry(const char *function, unsigned int line,
 		ext4_error_file(filp, function, line, bh ? bh->b_blocknr : 0,
 				"bad entry in directory: %s - offset=%u(%u), "
 				"inode=%u, rec_len=%d, name_len=%d",
-				error_msg, (unsigned) (offset%bh->b_size),
+				error_msg, (unsigned) (offset%size),
 				offset, le32_to_cpu(de->inode),
 				rlen, de->name_len);
 	else
 		ext4_error_inode(dir, function, line, bh ? bh->b_blocknr : 0,
 				"bad entry in directory: %s - offset=%u(%u), "
 				"inode=%u, rec_len=%d, name_len=%d",
-				error_msg, (unsigned) (offset%bh->b_size),
+				error_msg, (unsigned) (offset%size),
 				offset, le32_to_cpu(de->inode),
 				rlen, de->name_len);
 
@@ -210,8 +209,9 @@ revalidate:
 		while (!error && filp->f_pos < inode->i_size
 		       && offset < sb->s_blocksize) {
 			de = (struct ext4_dir_entry_2 *) (bh->b_data + offset);
-			if (ext4_check_dir_entry(inode, filp, de,
-						 bh, offset)) {
+			if (ext4_check_dir_entry(inode, filp, de, bh,
+						 bh->b_data, bh->b_size,
+						 offset)) {
 				/*
 				 * On error, skip the f_pos to the next block
 				 */
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 25cac6f..8d27406 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1819,10 +1819,11 @@ ext4_fsblk_t ext4_inode_to_goal_block(struct inode *);
 extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *,
 				  struct file *,
 				  struct ext4_dir_entry_2 *,
-				  struct buffer_head *, unsigned int);
-#define ext4_check_dir_entry(dir, filp, de, bh, offset)			\
+				  struct buffer_head *, char *, int,
+				  unsigned int);
+#define ext4_check_dir_entry(dir, filp, de, bh, buf, size, offset)	\
 	unlikely(__ext4_check_dir_entry(__func__, __LINE__, (dir), (filp), \
-					(de), (bh), (offset)))
+					(de), (bh), (buf), (size), (offset)))
 extern int ext4_htree_store_dirent(struct file *dir_file, __u32 hash,
 				    __u32 minor_hash,
 				    struct ext4_dir_entry_2 *dirent);
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 8a9645a..8545aa3 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -583,6 +583,7 @@ static int htree_dirblock_to_tree(struct file *dir_file,
 					   EXT4_DIR_REC_LEN(0));
 	for (; de < top; de = ext4_next_entry(de, dir->i_sb->s_blocksize)) {
 		if (ext4_check_dir_entry(dir, NULL, de, bh,
+				bh->b_data, bh->b_size,
 				(block<<EXT4_BLOCK_SIZE_BITS(dir->i_sb))
 					 + ((char *)de - bh->b_data))) {
 			/* On error, skip the f_pos to the next block. */
@@ -821,7 +822,8 @@ static inline int search_dirblock(struct buffer_head *bh,
 		if ((char *) de + namelen <= dlimit &&
 		    ext4_match (namelen, name, de)) {
 			/* found a match - just to be sure, do a full check */
-			if (ext4_check_dir_entry(dir, NULL, de, bh, offset))
+			if (ext4_check_dir_entry(dir, NULL, de, bh, bh->b_data,
+						 bh->b_size, offset))
 				return -1;
 			*res_dir = de;
 			return 1;
@@ -1267,7 +1269,8 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
 		de = (struct ext4_dir_entry_2 *)bh->b_data;
 		top = bh->b_data + blocksize - reclen;
 		while ((char *) de <= top) {
-			if (ext4_check_dir_entry(dir, NULL, de, bh, offset))
+			if (ext4_check_dir_entry(dir, NULL, de, bh, bh->b_data,
+						 bh->b_size, offset))
 				return -EIO;
 			if (ext4_match(namelen, name, de))
 				return -EEXIST;
@@ -1650,7 +1653,8 @@ static int ext4_delete_entry(handle_t *handle,
 	pde = NULL;
 	de = (struct ext4_dir_entry_2 *) bh->b_data;
 	while (i < bh->b_size) {
-		if (ext4_check_dir_entry(dir, NULL, de, bh, i))
+		if (ext4_check_dir_entry(dir, NULL, de, bh,
+					 bh->b_data, bh->b_size, i))
 			return -EIO;
 		if (de == de_del)  {
 			BUFFER_TRACE(bh, "get_write_access");
@@ -1963,7 +1967,8 @@ static int empty_dir(struct inode *inode)
 			}
 			de = (struct ext4_dir_entry_2 *) bh->b_data;
 		}
-		if (ext4_check_dir_entry(inode, NULL, de, bh, offset)) {
+		if (ext4_check_dir_entry(inode, NULL, de, bh,
+					 bh->b_data, bh->b_size, offset)) {
 			de = (struct ext4_dir_entry_2 *)(bh->b_data +
 							 sb->s_blocksize);
 			offset = (offset | (sb->s_blocksize - 1)) + 1;
-- 
1.7.0.4


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

* [PATCH V4 10/22] ext4: Create __ext4_insert_dentry for dir entry insertion.
  2012-02-20  7:01 ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Tao Ma
                     ` (7 preceding siblings ...)
  2012-02-20  7:01   ` [PATCH V4 09/22] ext4: Refactor __ext4_check_dir_entry to accepts start and size Tao Ma
@ 2012-02-20  7:01   ` Tao Ma
  2012-02-20  7:01   ` [PATCH V4 11/22] ext4: let add_dir_entry handle inline data properly Tao Ma
                     ` (12 subsequent siblings)
  21 siblings, 0 replies; 32+ messages in thread
From: Tao Ma @ 2012-02-20  7:01 UTC (permalink / raw)
  To: linux-ext4; +Cc: linux-fsdevel

From: Tao Ma <boyu.mt@taobao.com>

The old add_dirent_to_buf handles all the work related to the
work of adding dir entry to a dir block. Now we have inline data,
so create 2 new function __ext4_find_dest_de and __ext4_insert_dentry
that do the real work and let add_dirent_to_buf call them.

Signed-off-by: Tao Ma <boyu.mt@taobao.com>
---
 fs/ext4/ext4.h  |   15 +++++++
 fs/ext4/namei.c |  111 +++++++++++++++++++++++++++++++++---------------------
 2 files changed, 83 insertions(+), 43 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 8d27406..5fa4739 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1828,6 +1828,21 @@ extern int ext4_htree_store_dirent(struct file *dir_file, __u32 hash,
 				    __u32 minor_hash,
 				    struct ext4_dir_entry_2 *dirent);
 extern void ext4_htree_free_dir_info(struct dir_private_info *p);
+extern int ext4_find_dest_de(struct inode *dir, struct inode *inode,
+			     struct buffer_head *bh,
+			     void *buf, int buf_size,
+			     const char *name, int namelen,
+			     struct ext4_dir_entry_2 **dest_de);
+void ext4_insert_dentry(struct inode *inode,
+			struct ext4_dir_entry_2 *de,
+			int buf_size,
+			const char *name, int namelen);
+static inline void ext4_update_dx_flag(struct inode *inode)
+{
+	if (!EXT4_HAS_COMPAT_FEATURE(inode->i_sb,
+				     EXT4_FEATURE_COMPAT_DIR_INDEX))
+		ext4_clear_inode_flag(inode, EXT4_INODE_INDEX);
+}
 
 /* fsync.c */
 extern int ext4_sync_file(struct file *, loff_t, loff_t, int);
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 8545aa3..7417f14 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -775,13 +775,6 @@ static void dx_insert_block(struct dx_frame *frame, u32 hash, ext4_lblk_t block)
 	dx_set_count(entries, count + 1);
 }
 
-static void ext4_update_dx_flag(struct inode *inode)
-{
-	if (!EXT4_HAS_COMPAT_FEATURE(inode->i_sb,
-				     EXT4_FEATURE_COMPAT_DIR_INDEX))
-		ext4_clear_inode_flag(inode, EXT4_INODE_INDEX);
-}
-
 /*
  * NOTE! unlike strncmp, ext4_match returns 1 for success, 0 for failure.
  *
@@ -1243,6 +1236,66 @@ errout:
 	return NULL;
 }
 
+int ext4_find_dest_de(struct inode *dir, struct inode *inode,
+		      struct buffer_head *bh,
+		      void *buf, int buf_size,
+		      const char *name, int namelen,
+		      struct ext4_dir_entry_2 **dest_de)
+{
+	struct ext4_dir_entry_2 *de;
+	unsigned short reclen = EXT4_DIR_REC_LEN(namelen);
+	int nlen, rlen;
+	unsigned int offset = 0;
+	char *top;
+
+	de = (struct ext4_dir_entry_2 *)buf;
+	top = buf + buf_size - reclen;
+	while ((char *) de <= top) {
+		if (ext4_check_dir_entry(dir, NULL, de, bh,
+					 buf, buf_size, offset))
+			return -EIO;
+		if (ext4_match(namelen, name, de))
+			return -EEXIST;
+		nlen = EXT4_DIR_REC_LEN(de->name_len);
+		rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);
+		if ((de->inode ? rlen - nlen : rlen) >= reclen)
+			break;
+		de = (struct ext4_dir_entry_2 *)((char *)de + rlen);
+		offset += rlen;
+	}
+	if ((char *) de > top)
+		return -ENOSPC;
+
+	*dest_de = de;
+	return 0;
+}
+
+void ext4_insert_dentry(struct inode *inode,
+			struct ext4_dir_entry_2 *de,
+			int buf_size,
+			const char *name, int namelen)
+{
+
+	int nlen, rlen;
+
+	nlen = EXT4_DIR_REC_LEN(de->name_len);
+	rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);
+	if (de->inode) {
+		struct ext4_dir_entry_2 *de1 =
+				(struct ext4_dir_entry_2 *)((char *)de + nlen);
+		de1->rec_len = ext4_rec_len_to_disk(rlen - nlen, buf_size);
+		de->rec_len = ext4_rec_len_to_disk(nlen, buf_size);
+		de = de1;
+	}
+	de->file_type = EXT4_FT_UNKNOWN;
+	if (inode) {
+		de->inode = cpu_to_le32(inode->i_ino);
+		ext4_set_de_type(inode->i_sb, de, inode->i_mode);
+	} else
+		de->inode = 0;
+	de->name_len = namelen;
+	memcpy(de->name, name, namelen);
+}
 /*
  * Add a new entry into a directory (leaf) block.  If de is non-NULL,
  * it points to a directory entry which is guaranteed to be large
@@ -1258,31 +1311,17 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
 	struct inode	*dir = dentry->d_parent->d_inode;
 	const char	*name = dentry->d_name.name;
 	int		namelen = dentry->d_name.len;
-	unsigned int	offset = 0;
 	unsigned int	blocksize = dir->i_sb->s_blocksize;
 	unsigned short	reclen;
-	int		nlen, rlen, err;
-	char		*top;
+	int		err;
 
 	reclen = EXT4_DIR_REC_LEN(namelen);
 	if (!de) {
-		de = (struct ext4_dir_entry_2 *)bh->b_data;
-		top = bh->b_data + blocksize - reclen;
-		while ((char *) de <= top) {
-			if (ext4_check_dir_entry(dir, NULL, de, bh, bh->b_data,
-						 bh->b_size, offset))
-				return -EIO;
-			if (ext4_match(namelen, name, de))
-				return -EEXIST;
-			nlen = EXT4_DIR_REC_LEN(de->name_len);
-			rlen = ext4_rec_len_from_disk(de->rec_len, blocksize);
-			if ((de->inode? rlen - nlen: rlen) >= reclen)
-				break;
-			de = (struct ext4_dir_entry_2 *)((char *)de + rlen);
-			offset += rlen;
-		}
-		if ((char *) de > top)
-			return -ENOSPC;
+		err = ext4_find_dest_de(dir, inode,
+					bh, bh->b_data, blocksize,
+					name, namelen, &de);
+		if (err)
+			return err;
 	}
 	BUFFER_TRACE(bh, "get_write_access");
 	err = ext4_journal_get_write_access(handle, bh);
@@ -1292,22 +1331,8 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
 	}
 
 	/* By now the buffer is marked for journaling */
-	nlen = EXT4_DIR_REC_LEN(de->name_len);
-	rlen = ext4_rec_len_from_disk(de->rec_len, blocksize);
-	if (de->inode) {
-		struct ext4_dir_entry_2 *de1 = (struct ext4_dir_entry_2 *)((char *)de + nlen);
-		de1->rec_len = ext4_rec_len_to_disk(rlen - nlen, blocksize);
-		de->rec_len = ext4_rec_len_to_disk(nlen, blocksize);
-		de = de1;
-	}
-	de->file_type = EXT4_FT_UNKNOWN;
-	if (inode) {
-		de->inode = cpu_to_le32(inode->i_ino);
-		ext4_set_de_type(dir->i_sb, de, inode->i_mode);
-	} else
-		de->inode = 0;
-	de->name_len = namelen;
-	memcpy(de->name, name, namelen);
+	ext4_insert_dentry(inode, de, blocksize, name, namelen);
+
 	/*
 	 * XXX shouldn't update any times until successful
 	 * completion of syscall, but too many callers depend
-- 
1.7.0.4


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

* [PATCH V4 11/22] ext4: let add_dir_entry handle inline data properly.
  2012-02-20  7:01 ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Tao Ma
                     ` (8 preceding siblings ...)
  2012-02-20  7:01   ` [PATCH V4 10/22] ext4: Create __ext4_insert_dentry for dir entry insertion Tao Ma
@ 2012-02-20  7:01   ` Tao Ma
  2012-02-20  7:01   ` [PATCH V4 12/22] ext4: Let ext4_readdir handle inline data Tao Ma
                     ` (11 subsequent siblings)
  21 siblings, 0 replies; 32+ messages in thread
From: Tao Ma @ 2012-02-20  7:01 UTC (permalink / raw)
  To: linux-ext4; +Cc: linux-fsdevel

From: Tao Ma <boyu.mt@taobao.com>

This patch let add_dir_entry handle the inline data case. So the
dir is initialized as inline dir first and then we can try to add
some files to it, when the inline space can't hold all the entries,
a dir block will be created and the dir entry will be moved to it.

Signed-off-by: Tao Ma <boyu.mt@taobao.com>
---
 fs/ext4/ext4.h   |    2 +
 fs/ext4/inline.c |  276 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ext4/namei.c  |   19 ++++
 fs/ext4/xattr.h  |   19 ++++
 4 files changed, 316 insertions(+), 0 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 5fa4739..5b17550 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2267,6 +2267,8 @@ static inline void ext4_mark_super_dirty(struct super_block *sb)
 
 /* dir.c */
 extern const struct file_operations ext4_dir_operations;
+extern void ext4_show_inline_dir(struct inode *dir, struct buffer_head *bh,
+				 void *inline_start, int inline_size);
 
 /* file.c */
 extern const struct inode_operations ext4_file_inode_operations;
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index 4e3dc98..2bf83ad 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -938,6 +938,282 @@ int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos,
 	return copied;
 }
 
+#ifdef INLINE_DIR_DEBUG
+void ext4_show_inline_dir(struct inode *dir, struct buffer_head *bh,
+			  void *inline_start, int inline_size)
+{
+	int offset;
+	unsigned short de_len;
+	struct ext4_dir_entry_2 *de = inline_start;
+	void *dlimit = inline_start + inline_size;
+
+	trace_printk("inode %lu\n", dir->i_ino);
+	offset = 0;
+	while ((void *)de < dlimit) {
+		de_len = ext4_rec_len_from_disk(de->rec_len, inline_size);
+		trace_printk("de: offset %u reclen %u name %*.s "
+			     "namelen %u ino %u\n",
+			     offset, de_len, de->name_len, de->name,
+			     de->name_len, le32_to_cpu(de->inode));
+		if (ext4_check_dir_entry(dir, NULL, de, bh,
+					 inline_start, inline_size, offset))
+			BUG();
+
+		offset += de_len;
+		de = (struct ext4_dir_entry_2 *) ((char *) de + de_len);
+	}
+}
+#else
+#define ext4_show_inline_dir(dir, bh, inline_start, inline_size)
+#endif
+
+/*
+ * Add a new entry into a inline dir.
+ * It will return -ENOSPC if no space is available, and -EIO
+ * and -EEXIST if directory entry already exists.
+ */
+static int ext4_add_dirent_to_inline(handle_t *handle,
+				     struct dentry *dentry,
+				     struct inode *inode,
+				     struct ext4_iloc *iloc,
+				     void *inline_start, int inline_size)
+{
+	struct inode	*dir = dentry->d_parent->d_inode;
+	const char	*name = dentry->d_name.name;
+	int		namelen = dentry->d_name.len;
+	unsigned short	reclen;
+	int		err;
+	struct ext4_dir_entry_2 *de;
+
+	reclen = EXT4_DIR_REC_LEN(namelen);
+	err = ext4_find_dest_de(dir, inode, iloc->bh,
+				inline_start, inline_size,
+				name, namelen, &de);
+	if (err)
+		return err;
+
+	err = ext4_journal_get_write_access(handle, iloc->bh);
+	if (err)
+		return err;
+	ext4_insert_dentry(inode, de, inline_size, name, namelen);
+
+	ext4_show_inline_dir(dir, iloc->bh, inline_start, inline_size);
+
+	/*
+	 * XXX shouldn't update any times until successful
+	 * completion of syscall, but too many callers depend
+	 * on this.
+	 *
+	 * XXX similarly, too many callers depend on
+	 * ext4_new_inode() setting the times, but error
+	 * recovery deletes the inode, so the worst that can
+	 * happen is that the times are slightly out of date
+	 * and/or different from the directory change time.
+	 */
+	dir->i_mtime = dir->i_ctime = ext4_current_time(dir);
+	ext4_update_dx_flag(dir);
+	dir->i_version++;
+	ext4_mark_inode_dirty(handle, dir);
+	return 1;
+}
+
+void* ext4_get_inline_xattr_pos(struct inode *inode, struct ext4_iloc *iloc)
+{
+	struct ext4_xattr_entry *entry;
+	struct ext4_xattr_ibody_header *header;
+
+	BUG_ON(!EXT4_I(inode)->i_inline_off);
+
+	header = IHDR(inode, ext4_raw_inode(iloc));
+	entry = (struct ext4_xattr_entry *)((void *)ext4_raw_inode(iloc) +
+					    EXT4_I(inode)->i_inline_off);
+
+	return (void *)IFIRST(header) + le16_to_cpu(entry->e_value_offs);
+}
+
+/* Set the final de to cover the whole block. */
+static void ext4_update_final_de(void *de_buf, int old_size, int new_size)
+{
+	struct ext4_dir_entry_2 *de, *prev_de;
+	void *limit;
+	int de_len;
+
+	de = (struct ext4_dir_entry_2 *)de_buf;
+	if (old_size) {
+		limit = de_buf + old_size;
+		do {
+			prev_de = de;
+			de_len = ext4_rec_len_from_disk(de->rec_len, old_size);
+			de_buf += de_len;
+			de = (struct ext4_dir_entry_2 *)de_buf;
+		} while (de_buf < limit);
+
+		prev_de->rec_len = ext4_rec_len_to_disk(de_len + new_size -
+							old_size, new_size);
+	} else {
+		/* this is just created, so create an empty entry. */
+		de->inode = 0;
+		de->rec_len = ext4_rec_len_to_disk(new_size, new_size);
+	}
+}
+
+static int ext4_update_inline_dir(handle_t *handle, struct dentry *dentry,
+				  struct inode *dir,
+				  struct ext4_iloc *iloc)
+{
+	int ret, reclen = EXT4_DIR_REC_LEN(dentry->d_name.len);
+	int old_size = EXT4_I(dir)->i_inline_size - EXT4_MIN_INLINE_DATA_SIZE;
+
+	ret = ext4_update_inline_data(handle, dir,
+				EXT4_I(dir)->i_inline_size + reclen);
+	if (ret)
+		return ret;
+
+	ext4_update_final_de(ext4_get_inline_xattr_pos(dir, iloc), old_size,
+			     EXT4_I(dir)->i_inline_size -
+						EXT4_MIN_INLINE_DATA_SIZE);
+	dir->i_size = EXT4_I(dir)->i_disksize = EXT4_I(dir)->i_inline_size;
+	return 0;
+}
+
+/*
+ * Try to add the new entry to the inline data.
+ * If succeeds, return 0. If not, extended the inline dir and copied data to
+ * the new created block.
+ */
+int ext4_try_add_inline_entry(handle_t *handle, struct dentry *dentry,
+			      struct inode *inode)
+{
+	int ret, inline_size;
+	void *inline_start, *backup_buf = NULL;
+	struct buffer_head *dir_block = NULL;
+	struct ext4_iloc iloc;
+	int blocksize = inode->i_sb->s_blocksize;
+	struct inode *dir = dentry->d_parent->d_inode;
+
+	ret = ext4_get_inode_loc(dir, &iloc);
+	if (ret)
+		return ret;
+
+	down_write(&EXT4_I(dir)->xattr_sem);
+	if (!ext4_has_inline_data(dir))
+		goto out;
+
+	inline_start = ext4_raw_inode(&iloc)->i_block;
+	inline_size = EXT4_MIN_INLINE_DATA_SIZE;
+
+	ret = ext4_add_dirent_to_inline(handle, dentry, inode, &iloc,
+					inline_start, inline_size);
+	if (ret != -ENOSPC)
+		goto out;
+
+	/* check whether it can be inserted to inline xattr space. */
+	inline_size = EXT4_I(dir)->i_inline_size -
+			EXT4_MIN_INLINE_DATA_SIZE;
+	if (inline_size > 0) {
+		inline_start = ext4_get_inline_xattr_pos(dir, &iloc);
+
+		ret = ext4_add_dirent_to_inline(handle, dentry, inode, &iloc,
+						inline_start, inline_size);
+		if (ret != -ENOSPC)
+			goto out;
+	}
+
+	/* Try to add more xattr space.*/
+	ret = ext4_update_inline_dir(handle, dentry, dir, &iloc);
+	if (ret && ret != -ENOSPC)
+		goto out;
+	else if (!ret) {
+		inline_size = EXT4_I(dir)->i_inline_size -
+				EXT4_MIN_INLINE_DATA_SIZE;
+		inline_start = ext4_get_inline_xattr_pos(dir, &iloc);
+
+		ret = ext4_add_dirent_to_inline(handle, dentry, inode, &iloc,
+						inline_start, inline_size);
+		if (ret != -ENOSPC)
+			goto out;
+	}
+
+	/*
+	 * The inline space is filled up, so create a new block for it.
+	 * As the extent tree will be created, we have to save the inline
+	 * dir first.
+	 */
+	inline_size = EXT4_I(dir)->i_inline_size;
+	backup_buf = kmalloc(inline_size, GFP_NOFS);
+	if (!backup_buf) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	memcpy(backup_buf, (void *)ext4_raw_inode(&iloc)->i_block,
+	       EXT4_MIN_INLINE_DATA_SIZE);
+	if (inline_size > EXT4_MIN_INLINE_DATA_SIZE)
+		memcpy(backup_buf + EXT4_MIN_INLINE_DATA_SIZE,
+		       ext4_get_inline_xattr_pos(dir, &iloc),
+		       inline_size - EXT4_MIN_INLINE_DATA_SIZE);
+
+	/* clear the entry and the flag in dir now. */
+	ret = ext4_destroy_inline_data_nolock(handle, dir);
+	if (ret)
+		goto out;
+
+	dir->i_size = EXT4_I(dir)->i_disksize = blocksize;
+	dir_block = ext4_bread(handle, dir, 0, 1, &ret);
+	if (!dir_block)
+		goto out;
+
+	BUFFER_TRACE(dir_block, "get_write_access");
+	ret = ext4_journal_get_write_access(handle, dir_block);
+	if (ret)
+		goto out;
+	memcpy(dir_block->b_data, backup_buf, inline_size);
+
+	/* Set the final de to cover the whole block. */
+	ext4_update_final_de(dir_block->b_data, inline_size,
+			     blocksize);
+
+	BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata");
+	ret = ext4_handle_dirty_metadata(handle, dir, dir_block);
+
+out:
+	kfree(backup_buf);
+	brelse(dir_block);
+	if (!ret || ret == 1)
+		ext4_mark_inode_dirty(handle, dir);
+	up_write(&EXT4_I(dir)->xattr_sem);
+	brelse(iloc.bh);
+	return ret;
+}
+
+/*
+ * Try to create the inline data for the new dir.
+ * If it succeeds, return 0, otherwise return the error.
+ * In case of ENOSPC, the caller should create the normal disk layout dir.
+ */
+int ext4_try_create_inline_dir(handle_t *handle, struct inode *parent,
+			       struct inode *inode)
+{
+	int ret, inline_size = EXT4_MIN_INLINE_DATA_SIZE;
+	struct ext4_iloc iloc;
+	struct ext4_dir_entry_2 *de;
+
+	ret = ext4_get_inode_loc(inode, &iloc);
+	if (ret)
+		return ret;
+
+	ret = ext4_prepare_inline_data(handle, inode, inline_size);
+	if (ret)
+		goto out;
+
+	de = (struct ext4_dir_entry_2 *)ext4_raw_inode(&iloc)->i_block;
+	ext4_init_dot_dotdot(parent, inode, de, inline_size);
+	inode->i_size = EXT4_I(inode)->i_disksize = inline_size;
+out:
+	brelse(iloc.bh);
+	return ret;
+}
+
 int ext4_destroy_inline_data(handle_t *handle, struct inode *inode)
 {
 	int ret;
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 7417f14..311efe2 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -1486,6 +1486,17 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
 	blocksize = sb->s_blocksize;
 	if (!dentry->d_name.len)
 		return -EINVAL;
+
+	if (ext4_has_inline_data(dir)) {
+		retval = ext4_try_add_inline_entry(handle, dentry, inode);
+		if (retval < 0)
+			return retval;
+		if (retval == 1) {
+			retval = 0;
+			return retval;
+		}
+	}
+
 	if (is_dx(dir)) {
 		retval = ext4_dx_add_entry(handle, dentry, inode);
 		if (!retval || (retval != ERR_BAD_DX_DIR))
@@ -1862,6 +1873,14 @@ static int ext4_init_new_dir(handle_t *handle, struct inode *parent,
 	int err;
 	int blocksize = inode->i_sb->s_blocksize;
 
+	if (ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) {
+		err = ext4_try_create_inline_dir(handle, parent, inode);
+		if (err < 0 && err != -ENOSPC)
+			goto out;
+		if (!err)
+			goto out;
+	}
+
 	inode->i_size = EXT4_I(inode)->i_disksize = blocksize;
 	dir_block = ext4_bread(handle, inode, 0, 1, &err);
 	if (!dir_block)
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index 7825aff..f4f3dd1 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -161,6 +161,11 @@ extern int ext4_da_write_inline_data_begin(struct address_space *mapping,
 extern int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos,
 					 unsigned len, unsigned copied,
 					 struct page *page);
+extern int ext4_try_add_inline_entry(handle_t *handle, struct dentry *dentry,
+				     struct inode *inode);
+extern int ext4_try_create_inline_dir(handle_t *handle,
+				      struct inode *parent,
+				      struct inode *inode);
 # else  /* CONFIG_EXT4_FS_XATTR */
 
 static inline int
@@ -324,6 +329,20 @@ static inline int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos,
 {
 	return 0;
 }
+
+static inline int ext4_try_add_inline_entry(handle_t *handle,
+					    struct dentry *dentry,
+					    struct inode *inode)
+{
+	return 0;
+}
+
+static inline int ext4_try_create_inline_dir(handle_t *handle,
+					     struct inode *parent,
+					     struct inode *inode)
+{
+	return 0;
+}
 # endif  /* CONFIG_EXT4_FS_XATTR */
 
 #ifdef CONFIG_EXT4_FS_SECURITY
-- 
1.7.0.4


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

* [PATCH V4 12/22] ext4: Let ext4_readdir handle inline data.
  2012-02-20  7:01 ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Tao Ma
                     ` (9 preceding siblings ...)
  2012-02-20  7:01   ` [PATCH V4 11/22] ext4: let add_dir_entry handle inline data properly Tao Ma
@ 2012-02-20  7:01   ` Tao Ma
  2012-02-20  7:01   ` [PATCH V4 13/22] ext4: Create a new function search_dir Tao Ma
                     ` (10 subsequent siblings)
  21 siblings, 0 replies; 32+ messages in thread
From: Tao Ma @ 2012-02-20  7:01 UTC (permalink / raw)
  To: linux-ext4; +Cc: linux-fsdevel

From: Tao Ma <boyu.mt@taobao.com>

Signed-off-by: Tao Ma <boyu.mt@taobao.com>
---
 fs/ext4/dir.c    |   23 ++++------
 fs/ext4/ext4.h   |   12 +++++
 fs/ext4/inline.c |  124 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ext4/xattr.h  |    7 +++
 4 files changed, 153 insertions(+), 13 deletions(-)

diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c
index 6c2199e..691548c 100644
--- a/fs/ext4/dir.c
+++ b/fs/ext4/dir.c
@@ -27,10 +27,7 @@
 #include <linux/slab.h>
 #include <linux/rbtree.h>
 #include "ext4.h"
-
-static unsigned char ext4_filetype_table[] = {
-	DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK
-};
+#include "xattr.h"
 
 static int ext4_readdir(struct file *, void *, filldir_t);
 static int ext4_dx_readdir(struct file *filp,
@@ -51,19 +48,13 @@ const struct file_operations ext4_dir_operations = {
 };
 
 
-static unsigned char get_dtype(struct super_block *sb, int filetype)
-{
-	if (!EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FILETYPE) ||
-	    (filetype >= EXT4_FT_MAX))
-		return DT_UNKNOWN;
-
-	return (ext4_filetype_table[filetype]);
-}
-
 /*
  * Return 0 if the directory entry is OK, and 1 if there is a problem
  *
  * Note: this is the opposite of what ext2 and ext3 historically returned...
+ *
+ * bh passed here can be an inode block or a dir data block, depending
+ * on the inode inline data flag.
  */
 int __ext4_check_dir_entry(const char *function, unsigned int line,
 			   struct inode *dir, struct file *filp,
@@ -122,6 +113,12 @@ static int ext4_readdir(struct file *filp,
 
 	sb = inode->i_sb;
 
+	if (ext4_has_inline_data(inode)) {
+		ret = ext4_read_inline_dir(filp, dirent, filldir);
+		if (ret > 0 || ret < 0)
+			return ret;
+	}
+
 	if (EXT4_HAS_COMPAT_FEATURE(inode->i_sb,
 				    EXT4_FEATURE_COMPAT_DIR_INDEX) &&
 	    ((ext4_test_inode_flag(inode, EXT4_INODE_INDEX)) ||
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 5b17550..d8cd350 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1843,6 +1843,18 @@ static inline void ext4_update_dx_flag(struct inode *inode)
 				     EXT4_FEATURE_COMPAT_DIR_INDEX))
 		ext4_clear_inode_flag(inode, EXT4_INODE_INDEX);
 }
+static unsigned char ext4_filetype_table[] = {
+	DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK
+};
+
+static inline  unsigned char get_dtype(struct super_block *sb, int filetype)
+{
+	if (!EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FILETYPE) ||
+	    (filetype >= EXT4_FT_MAX))
+		return DT_UNKNOWN;
+
+	return ext4_filetype_table[filetype];
+}
 
 /* fsync.c */
 extern int ext4_sync_file(struct file *, loff_t, loff_t, int);
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index 2bf83ad..e238b64 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -1187,6 +1187,130 @@ out:
 }
 
 /*
+ * Get the inline dentry at offset.
+ */
+static inline struct ext4_dir_entry_2 *
+ext4_get_inline_entry(struct inode *inode,
+		      struct ext4_iloc *iloc,
+		      unsigned int offset,
+		      void **inline_start,
+		      int *inline_size)
+{
+	void *inline_pos;
+
+	BUG_ON(offset > ext4_get_inline_size(inode));
+
+	if (offset < EXT4_MIN_INLINE_DATA_SIZE) {
+		inline_pos = ext4_raw_inode(iloc)->i_block;
+		*inline_size = EXT4_MIN_INLINE_DATA_SIZE;
+	} else {
+		inline_pos = ext4_get_inline_xattr_pos(inode, iloc);
+		offset -= EXT4_MIN_INLINE_DATA_SIZE;
+		*inline_size = ext4_get_inline_size(inode) -
+				EXT4_MIN_INLINE_DATA_SIZE;
+	}
+
+	if (inline_start)
+		*inline_start = inline_pos;
+	return (struct ext4_dir_entry_2 *)(inline_pos + offset);
+}
+
+int ext4_read_inline_dir(struct file *filp,
+			 void *dirent, filldir_t filldir)
+{
+	int error = 0;
+	unsigned int offset;
+	int i, stored;
+	void *inline_pos;
+	struct ext4_dir_entry_2 *de;
+	struct super_block *sb;
+	struct inode *inode = filp->f_path.dentry->d_inode;
+	int ret, inline_size = 0;
+	struct ext4_iloc iloc;
+
+	ret = ext4_get_inode_loc(inode, &iloc);
+	if (ret)
+		return ret;
+
+	down_read(&EXT4_I(inode)->xattr_sem);
+	if (!ext4_has_inline_data(inode))
+		goto out;
+
+	sb = inode->i_sb;
+	stored = 0;
+	offset = filp->f_pos & (sb->s_blocksize - 1);
+
+	while (!error && !stored && filp->f_pos < inode->i_size) {
+revalidate:
+		/* If the version has changed since the last call to
+		 * readdir(2), then we might be pointing to an invalid
+		 * dirent right now.  Scan from the start of the block
+		 * to make sure. */
+		if (filp->f_version != inode->i_version) {
+			for (i = 0; i < inode->i_size && i < offset; ) {
+				de = ext4_get_inline_entry(inode, &iloc, i,
+							   NULL, &inline_size);
+				/* It's too expensive to do a full
+				 * dirent test each time round this
+				 * loop, but we do have to test at
+				 * least that it is non-zero.  A
+				 * failure will be detected in the
+				 * dirent test below. */
+				if (ext4_rec_len_from_disk(de->rec_len,
+					inline_size) < EXT4_DIR_REC_LEN(1))
+					break;
+				i += ext4_rec_len_from_disk(de->rec_len,
+							    inline_size);
+			}
+			offset = i;
+			filp->f_pos = offset;
+			filp->f_version = inode->i_version;
+		}
+
+		while (!error && filp->f_pos < inode->i_size) {
+			de = ext4_get_inline_entry(inode, &iloc, offset,
+						   &inline_pos, &inline_size);
+			if (ext4_check_dir_entry(inode, filp, de,
+						 iloc.bh, inline_pos,
+						 inline_size, offset)) {
+				ret = stored;
+				goto out;
+			}
+			offset += ext4_rec_len_from_disk(de->rec_len,
+							 inline_size);
+			if (le32_to_cpu(de->inode)) {
+				/* We might block in the next section
+				 * if the data destination is
+				 * currently swapped out.  So, use a
+				 * version stamp to detect whether or
+				 * not the directory has been modified
+				 * during the copy operation.
+				 */
+				u64 version = filp->f_version;
+
+				error = filldir(dirent, de->name,
+						de->name_len,
+						filp->f_pos,
+						le32_to_cpu(de->inode),
+						get_dtype(sb, de->file_type));
+				if (error)
+					break;
+				if (version != filp->f_version)
+					goto revalidate;
+				stored++;
+			}
+			filp->f_pos += ext4_rec_len_from_disk(de->rec_len,
+							      inline_size);
+		}
+		offset = 0;
+	}
+out:
+	up_read(&EXT4_I(inode)->xattr_sem);
+	brelse(iloc.bh);
+	return ret;
+}
+
+/*
  * Try to create the inline data for the new dir.
  * If it succeeds, return 0, otherwise return the error.
  * In case of ENOSPC, the caller should create the normal disk layout dir.
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index f4f3dd1..8092846 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -166,6 +166,8 @@ extern int ext4_try_add_inline_entry(handle_t *handle, struct dentry *dentry,
 extern int ext4_try_create_inline_dir(handle_t *handle,
 				      struct inode *parent,
 				      struct inode *inode);
+extern int ext4_read_inline_dir(struct file *filp,
+				void *dirent, filldir_t filldir);
 # else  /* CONFIG_EXT4_FS_XATTR */
 
 static inline int
@@ -343,6 +345,11 @@ static inline int ext4_try_create_inline_dir(handle_t *handle,
 {
 	return 0;
 }
+static inline int ext4_read_inline_dir(struct file *filp,
+				       void *dirent, filldir_t filldir)
+{
+	return 0;
+}
 # endif  /* CONFIG_EXT4_FS_XATTR */
 
 #ifdef CONFIG_EXT4_FS_SECURITY
-- 
1.7.0.4


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

* [PATCH V4 13/22] ext4: Create a new function search_dir.
  2012-02-20  7:01 ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Tao Ma
                     ` (10 preceding siblings ...)
  2012-02-20  7:01   ` [PATCH V4 12/22] ext4: Let ext4_readdir handle inline data Tao Ma
@ 2012-02-20  7:01   ` Tao Ma
  2012-02-20  7:01   ` [PATCH V4 14/22] ext4: let ext4_find_entry handle inline data Tao Ma
                     ` (9 subsequent siblings)
  21 siblings, 0 replies; 32+ messages in thread
From: Tao Ma @ 2012-02-20  7:01 UTC (permalink / raw)
  To: linux-ext4; +Cc: linux-fsdevel

From: Tao Ma <boyu.mt@taobao.com>

search_dirblock is used to search a dir block, but the code is
almost the same for searching an inline dir.

So create a new fuction search_dir and let search_dirblock
call it.

Signed-off-by: Tao Ma <boyu.mt@taobao.com>
---
 fs/ext4/ext4.h  |    7 +++++++
 fs/ext4/namei.c |   26 +++++++++++++++++++-------
 2 files changed, 26 insertions(+), 7 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index d8cd350..c8dc24e 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1971,6 +1971,13 @@ extern int ext4_orphan_add(handle_t *, struct inode *);
 extern int ext4_orphan_del(handle_t *, struct inode *);
 extern int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash,
 				__u32 start_minor_hash, __u32 *next_hash);
+extern int search_dir(struct buffer_head *bh,
+		      char *search_buf,
+		      int buf_size,
+		      struct inode *dir,
+		      const struct qstr *d_name,
+		      unsigned int offset,
+		      struct ext4_dir_entry_2 **res_dir);
 
 /* resize.c */
 extern int ext4_group_add(struct super_block *sb,
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 311efe2..7e74503 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -794,11 +794,13 @@ static inline int ext4_match (int len, const char * const name,
 /*
  * Returns 0 if not found, -1 on failure, and 1 on success
  */
-static inline int search_dirblock(struct buffer_head *bh,
-				  struct inode *dir,
-				  const struct qstr *d_name,
-				  unsigned int offset,
-				  struct ext4_dir_entry_2 ** res_dir)
+int search_dir(struct buffer_head *bh,
+	       char *search_buf,
+	       int buf_size,
+	       struct inode *dir,
+	       const struct qstr *d_name,
+	       unsigned int offset,
+	       struct ext4_dir_entry_2 **res_dir)
 {
 	struct ext4_dir_entry_2 * de;
 	char * dlimit;
@@ -806,8 +808,8 @@ static inline int search_dirblock(struct buffer_head *bh,
 	const char *name = d_name->name;
 	int namelen = d_name->len;
 
-	de = (struct ext4_dir_entry_2 *) bh->b_data;
-	dlimit = bh->b_data + dir->i_sb->s_blocksize;
+	de = (struct ext4_dir_entry_2 *)search_buf;
+	dlimit = search_buf + buf_size;
 	while ((char *) de < dlimit) {
 		/* this code is executed quadratically often */
 		/* do minimal checking `by hand' */
@@ -832,6 +834,16 @@ static inline int search_dirblock(struct buffer_head *bh,
 	return 0;
 }
 
+static inline int search_dirblock(struct buffer_head *bh,
+				  struct inode *dir,
+				  const struct qstr *d_name,
+				  unsigned int offset,
+				  struct ext4_dir_entry_2 **res_dir)
+{
+	return search_dir(bh, bh->b_data, dir->i_sb->s_blocksize, dir,
+			  d_name, offset, res_dir);
+}
+
 
 /*
  *	ext4_find_entry()
-- 
1.7.0.4


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

* [PATCH V4 14/22] ext4: let ext4_find_entry handle inline data.
  2012-02-20  7:01 ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Tao Ma
                     ` (11 preceding siblings ...)
  2012-02-20  7:01   ` [PATCH V4 13/22] ext4: Create a new function search_dir Tao Ma
@ 2012-02-20  7:01   ` Tao Ma
  2012-02-20  7:01   ` [PATCH V4 15/22] ext4: make ext4_delete_entry generic Tao Ma
                     ` (8 subsequent siblings)
  21 siblings, 0 replies; 32+ messages in thread
From: Tao Ma @ 2012-02-20  7:01 UTC (permalink / raw)
  To: linux-ext4; +Cc: linux-fsdevel

From: Tao Ma <boyu.mt@taobao.com>

Create a new ext4_find_inline_entry to handle the case
of inline data.

Signed-off-by: Tao Ma <boyu.mt@taobao.com>
---
 fs/ext4/inline.c |   47 +++++++++++++++++++++++++++++++++++++++++++++++
 fs/ext4/namei.c  |   10 +++++++++-
 fs/ext4/xattr.h  |   12 ++++++++++++
 3 files changed, 68 insertions(+), 1 deletions(-)

diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index e238b64..90e1671 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -1338,6 +1338,53 @@ out:
 	return ret;
 }
 
+struct buffer_head *ext4_find_inline_entry(struct inode *dir,
+					const struct qstr *d_name,
+					struct ext4_dir_entry_2 **res_dir,
+					int *has_inline_data)
+{
+	int ret;
+	struct ext4_iloc iloc;
+	void *inline_start;
+	int inline_size;
+
+	if (ext4_get_inode_loc(dir, &iloc))
+		return NULL;
+
+	down_read(&EXT4_I(dir)->xattr_sem);
+	if (!ext4_has_inline_data(dir)) {
+		*has_inline_data = 0;
+		goto out;
+	}
+
+	inline_start = ext4_raw_inode(&iloc)->i_block;
+	inline_size = EXT4_MIN_INLINE_DATA_SIZE;
+	ret = search_dir(iloc.bh, inline_start, inline_size,
+			 dir, d_name, 0, res_dir);
+	if (ret == 1)
+		goto out_find;
+	if (ret < 0)
+		goto out;
+
+	if (ext4_get_inline_size(dir) == EXT4_MIN_INLINE_DATA_SIZE)
+		goto out;
+
+	inline_start = ext4_get_inline_xattr_pos(dir, &iloc);
+	inline_size = ext4_get_inline_size(dir) - EXT4_MIN_INLINE_DATA_SIZE;
+
+	ret = search_dir(iloc.bh, inline_start, inline_size,
+			 dir, d_name, 0, res_dir);
+	if (ret == 1)
+		goto out_find;
+
+out:
+	brelse(iloc.bh);
+	iloc.bh = NULL;
+out_find:
+	up_read(&EXT4_I(dir)->xattr_sem);
+	return iloc.bh;
+}
+
 int ext4_destroy_inline_data(handle_t *handle, struct inode *inode)
 {
 	int ret;
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 7e74503..1de97f5 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -844,7 +844,6 @@ static inline int search_dirblock(struct buffer_head *bh,
 			  d_name, offset, res_dir);
 }
 
-
 /*
  *	ext4_find_entry()
  *
@@ -879,6 +878,15 @@ static struct buffer_head * ext4_find_entry (struct inode *dir,
 	namelen = d_name->len;
 	if (namelen > EXT4_NAME_LEN)
 		return NULL;
+
+	if (ext4_has_inline_data(dir)) {
+		int has_inline_data = 1;
+		ret = ext4_find_inline_entry(dir, d_name, res_dir,
+					     &has_inline_data);
+		if (has_inline_data)
+			return ret;
+	}
+
 	if ((namelen <= 2) && (name[0] == '.') &&
 	    (name[1] == '.' || name[1] == '\0')) {
 		/*
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index 8092846..b86391f 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -168,6 +168,10 @@ extern int ext4_try_create_inline_dir(handle_t *handle,
 				      struct inode *inode);
 extern int ext4_read_inline_dir(struct file *filp,
 				void *dirent, filldir_t filldir);
+extern struct buffer_head *ext4_find_inline_entry(struct inode *dir,
+					const struct qstr *d_name,
+					struct ext4_dir_entry_2 **res_dir,
+					int *has_inline_data);
 # else  /* CONFIG_EXT4_FS_XATTR */
 
 static inline int
@@ -350,6 +354,14 @@ static inline int ext4_read_inline_dir(struct file *filp,
 {
 	return 0;
 }
+
+struct buffer_head *ext4_find_inline_entry(struct inode *dir,
+					const struct qstr *d_name,
+					struct ext4_dir_entry_2 **res_dir,
+					int *has_inline_data)
+{
+	return NULL;
+}
 # endif  /* CONFIG_EXT4_FS_XATTR */
 
 #ifdef CONFIG_EXT4_FS_SECURITY
-- 
1.7.0.4


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

* [PATCH V4 15/22] ext4: make ext4_delete_entry generic.
  2012-02-20  7:01 ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Tao Ma
                     ` (12 preceding siblings ...)
  2012-02-20  7:01   ` [PATCH V4 14/22] ext4: let ext4_find_entry handle inline data Tao Ma
@ 2012-02-20  7:01   ` Tao Ma
  2012-02-20  7:01   ` [PATCH V4 16/22] ext4: let ext4_delete_entry handle inline data Tao Ma
                     ` (7 subsequent siblings)
  21 siblings, 0 replies; 32+ messages in thread
From: Tao Ma @ 2012-02-20  7:01 UTC (permalink / raw)
  To: linux-ext4; +Cc: linux-fsdevel

From: Tao Ma <boyu.mt@taobao.com>

Currently ext4_delete_entry is used only for dir entry
removing from a dir block. So let us create a new function
ext4_generic_delete_entry and this function takes a entry_buf
and a buf_size so that it can be used for inline data.

Signed-off-by: Tao Ma <boyu.mt@taobao.com>
---
 fs/ext4/ext4.h  |    6 +++++
 fs/ext4/namei.c |   62 ++++++++++++++++++++++++++++++++++++------------------
 2 files changed, 47 insertions(+), 21 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index c8dc24e..26024a3 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1978,6 +1978,12 @@ extern int search_dir(struct buffer_head *bh,
 		      const struct qstr *d_name,
 		      unsigned int offset,
 		      struct ext4_dir_entry_2 **res_dir);
+extern int ext4_generic_delete_entry(handle_t *handle,
+				     struct inode *dir,
+				     struct ext4_dir_entry_2 *de_del,
+				     struct buffer_head *bh,
+				     void *entry_buf,
+				     int buf_size);
 
 /* resize.c */
 extern int ext4_group_add(struct super_block *sb,
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 1de97f5..8293121 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -1693,32 +1693,28 @@ cleanup:
 }
 
 /*
- * ext4_delete_entry deletes a directory entry by merging it with the
- * previous entry
+ * ext4_generic_delete_entry deletes a directory entry by merging it
+ * with the previous entry
  */
-static int ext4_delete_entry(handle_t *handle,
-			     struct inode *dir,
-			     struct ext4_dir_entry_2 *de_del,
-			     struct buffer_head *bh)
+int ext4_generic_delete_entry(handle_t *handle,
+			      struct inode *dir,
+			      struct ext4_dir_entry_2 *de_del,
+			      struct buffer_head *bh,
+			      void *entry_buf,
+			      int buf_size)
 {
 	struct ext4_dir_entry_2 *de, *pde;
 	unsigned int blocksize = dir->i_sb->s_blocksize;
-	int i, err;
+	int i;
 
 	i = 0;
 	pde = NULL;
-	de = (struct ext4_dir_entry_2 *) bh->b_data;
-	while (i < bh->b_size) {
+	de = (struct ext4_dir_entry_2 *)entry_buf;
+	while (i < buf_size) {
 		if (ext4_check_dir_entry(dir, NULL, de, bh,
 					 bh->b_data, bh->b_size, i))
 			return -EIO;
 		if (de == de_del)  {
-			BUFFER_TRACE(bh, "get_write_access");
-			err = ext4_journal_get_write_access(handle, bh);
-			if (unlikely(err)) {
-				ext4_std_error(dir->i_sb, err);
-				return err;
-			}
 			if (pde)
 				pde->rec_len = ext4_rec_len_to_disk(
 					ext4_rec_len_from_disk(pde->rec_len,
@@ -1729,12 +1725,6 @@ static int ext4_delete_entry(handle_t *handle,
 			else
 				de->inode = 0;
 			dir->i_version++;
-			BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
-			err = ext4_handle_dirty_metadata(handle, dir, bh);
-			if (unlikely(err)) {
-				ext4_std_error(dir->i_sb, err);
-				return err;
-			}
 			return 0;
 		}
 		i += ext4_rec_len_from_disk(de->rec_len, blocksize);
@@ -1744,6 +1734,36 @@ static int ext4_delete_entry(handle_t *handle,
 	return -ENOENT;
 }
 
+static int ext4_delete_entry(handle_t *handle,
+			     struct inode *dir,
+			     struct ext4_dir_entry_2 *de_del,
+			     struct buffer_head *bh)
+{
+	int err;
+
+	BUFFER_TRACE(bh, "get_write_access");
+	err = ext4_journal_get_write_access(handle, bh);
+	if (unlikely(err))
+		goto out;
+
+	err = ext4_generic_delete_entry(handle, dir, de_del,
+					bh, bh->b_data,
+					dir->i_sb->s_blocksize);
+	if (err)
+		goto out;
+
+	BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
+	err = ext4_handle_dirty_metadata(handle, dir, bh);
+	if (unlikely(err))
+		goto out;
+
+	return 0;
+out:
+	if (err != -ENOENT)
+		ext4_std_error(dir->i_sb, err);
+	return err;
+}
+
 /*
  * DIR_NLINK feature is set if 1) nlinks > EXT4_LINK_MAX or 2) nlinks == 2,
  * since this indicates that nlinks count was previously 1.
-- 
1.7.0.4


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

* [PATCH V4 16/22] ext4: let ext4_delete_entry handle inline data.
  2012-02-20  7:01 ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Tao Ma
                     ` (13 preceding siblings ...)
  2012-02-20  7:01   ` [PATCH V4 15/22] ext4: make ext4_delete_entry generic Tao Ma
@ 2012-02-20  7:01   ` Tao Ma
  2012-02-20  7:01   ` [PATCH V4 17/22] ext4: let empty_dir handle inline dir Tao Ma
                     ` (6 subsequent siblings)
  21 siblings, 0 replies; 32+ messages in thread
From: Tao Ma @ 2012-02-20  7:01 UTC (permalink / raw)
  To: linux-ext4; +Cc: linux-fsdevel

From: Tao Ma <boyu.mt@taobao.com>

Signed-off-by: Tao Ma <boyu.mt@taobao.com>
---
 fs/ext4/inline.c |   51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ext4/namei.c  |    9 +++++++++
 fs/ext4/xattr.h  |   11 +++++++++++
 3 files changed, 71 insertions(+), 0 deletions(-)

diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index 90e1671..3126ac3 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -1385,6 +1385,57 @@ out_find:
 	return iloc.bh;
 }
 
+int ext4_delete_inline_entry(handle_t *handle,
+			     struct inode *dir,
+			     struct ext4_dir_entry_2 *de_del,
+			     struct buffer_head *bh)
+{
+	int err, inline_size;
+	struct ext4_iloc iloc;
+	void *inline_start;
+
+	err = ext4_get_inode_loc(dir, &iloc);
+	if (err)
+		return err;
+
+	down_write(&EXT4_I(dir)->xattr_sem);
+	if (!ext4_has_inline_data(dir))
+		goto out;
+
+	if ((void *)de_del - ((void *)ext4_raw_inode(&iloc)->i_block) <
+		EXT4_MIN_INLINE_DATA_SIZE) {
+		inline_start = ext4_raw_inode(&iloc)->i_block;
+		inline_size = EXT4_MIN_INLINE_DATA_SIZE;
+	} else {
+		inline_start = ext4_get_inline_xattr_pos(dir, &iloc);
+		inline_size = ext4_get_inline_size(dir) -
+				EXT4_MIN_INLINE_DATA_SIZE;
+	}
+
+	err = ext4_journal_get_write_access(handle, bh);
+	if (err)
+		goto out;
+
+	err = ext4_generic_delete_entry(handle, dir, de_del, bh,
+					inline_start, inline_size);
+	if (err)
+		goto out;
+
+	BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
+	err = ext4_mark_inode_dirty(handle, dir);
+	if (unlikely(err))
+		goto out;
+
+	ext4_show_inline_dir(dir, iloc.bh, inline_start, inline_size);
+	err = 1;
+out:
+	up_write(&EXT4_I(dir)->xattr_sem);
+	brelse(iloc.bh);
+	if (err != 1 && err != -ENOENT)
+		ext4_std_error(dir->i_sb, err);
+	return err;
+}
+
 int ext4_destroy_inline_data(handle_t *handle, struct inode *inode)
 {
 	int ret;
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 8293121..9f7002c 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -1741,6 +1741,15 @@ static int ext4_delete_entry(handle_t *handle,
 {
 	int err;
 
+	if (ext4_has_inline_data(dir)) {
+		err = ext4_delete_inline_entry(handle, dir, de_del, bh);
+		if (err) {
+			if (err == 1)
+				err = 0;
+			return err;
+		}
+	}
+
 	BUFFER_TRACE(bh, "get_write_access");
 	err = ext4_journal_get_write_access(handle, bh);
 	if (unlikely(err))
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index b86391f..9991143 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -172,6 +172,10 @@ extern struct buffer_head *ext4_find_inline_entry(struct inode *dir,
 					const struct qstr *d_name,
 					struct ext4_dir_entry_2 **res_dir,
 					int *has_inline_data);
+extern int ext4_delete_inline_entry(handle_t *handle,
+				    struct inode *dir,
+				    struct ext4_dir_entry_2 *de_del,
+				    struct buffer_head *bh);
 # else  /* CONFIG_EXT4_FS_XATTR */
 
 static inline int
@@ -362,6 +366,13 @@ struct buffer_head *ext4_find_inline_entry(struct inode *dir,
 {
 	return NULL;
 }
+static inline int ext4_delete_inline_entry(handle_t *handle,
+					   struct inode *dir,
+					   struct ext4_dir_entry_2 *de_del,
+					   struct buffer_head *bh)
+{
+	return 0;
+}
 # endif  /* CONFIG_EXT4_FS_XATTR */
 
 #ifdef CONFIG_EXT4_FS_SECURITY
-- 
1.7.0.4


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

* [PATCH V4 17/22] ext4: let empty_dir handle inline dir.
  2012-02-20  7:01 ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Tao Ma
                     ` (14 preceding siblings ...)
  2012-02-20  7:01   ` [PATCH V4 16/22] ext4: let ext4_delete_entry handle inline data Tao Ma
@ 2012-02-20  7:01   ` Tao Ma
  2012-04-07  0:21     ` Andreas Dilger
  2012-02-20  7:01   ` [PATCH V4 18/22] ext4: let ext4_rename " Tao Ma
                     ` (5 subsequent siblings)
  21 siblings, 1 reply; 32+ messages in thread
From: Tao Ma @ 2012-02-20  7:01 UTC (permalink / raw)
  To: linux-ext4; +Cc: linux-fsdevel

From: Tao Ma <boyu.mt@taobao.com>

empty_dir is used when deleting a dir. So it should handle
inline dir properly.

Signed-off-by: Tao Ma <boyu.mt@taobao.com>
---
 fs/ext4/inline.c |   69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ext4/namei.c  |    8 ++++++
 fs/ext4/xattr.h  |    6 ++++
 3 files changed, 83 insertions(+), 0 deletions(-)

diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index 3126ac3..6612ce1 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -1436,6 +1436,75 @@ out:
 	return err;
 }
 
+int empty_inline_dir(struct inode *dir, int *has_inline_data)
+{
+	int err, inline_size;
+	struct ext4_iloc iloc;
+	void *inline_pos;
+	unsigned int offset;
+	struct ext4_dir_entry_2 *de, *de1;
+	int ret = 1;
+
+	err = ext4_get_inode_loc(dir, &iloc);
+	if (err) {
+		EXT4_ERROR_INODE(dir, "error %d getting inode %lu block",
+				 err, dir->i_ino);
+		return 1;
+	}
+
+	down_read(&EXT4_I(dir)->xattr_sem);
+	if (!ext4_has_inline_data(dir)) {
+		*has_inline_data = 0;
+		goto out;
+	}
+
+	de = ext4_get_inline_entry(dir, &iloc, 0,
+				   &inline_pos, &inline_size);
+	de1 = ext4_get_inline_entry(dir, &iloc,
+			ext4_rec_len_from_disk(de->rec_len, inline_size),
+			&inline_pos, &inline_size);
+	if (le32_to_cpu(de->inode) != dir->i_ino ||
+			!le32_to_cpu(de1->inode) ||
+			strcmp(".", de->name) ||
+			strcmp("..", de1->name)) {
+		ext4_warning(dir->i_sb,
+			     "bad directory (dir #%lu) - no `.' or `..'",
+			     dir->i_ino);
+		ret = 1;
+		goto out;
+	}
+
+	offset = ext4_rec_len_from_disk(de->rec_len, inline_size) +
+		 ext4_rec_len_from_disk(de1->rec_len, inline_size);
+	while (offset < dir->i_size) {
+		de = ext4_get_inline_entry(dir, &iloc, offset,
+					   &inline_pos, &inline_size);
+		if (ext4_check_dir_entry(dir, NULL, de,
+					 iloc.bh, inline_pos,
+					 inline_size, offset)) {
+			ext4_warning(dir->i_sb,
+				     "bad inline directory (dir #%lu) - "
+				     "inode %u, rec_len %u, name_len %d"
+				     "inline size %d\n",
+				     dir->i_ino, le32_to_cpu(de->inode),
+				     le16_to_cpu(de->rec_len), de->name_len,
+				     inline_size);
+			ret = 1;
+			goto out;
+		}
+		if (le32_to_cpu(de->inode)) {
+			ret = 0;
+			goto out;
+		}
+		offset += ext4_rec_len_from_disk(de->rec_len, inline_size);
+	}
+
+out:
+	up_read(&EXT4_I(dir)->xattr_sem);
+	brelse(iloc.bh);
+	return ret;
+}
+
 int ext4_destroy_inline_data(handle_t *handle, struct inode *inode)
 {
 	int ret;
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 9f7002c..b6b718e 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -2015,6 +2015,14 @@ static int empty_dir(struct inode *inode)
 	struct super_block *sb;
 	int err = 0;
 
+	if (ext4_has_inline_data(inode)) {
+		int has_inline_data = 1;
+
+		err = empty_inline_dir(inode, &has_inline_data);
+		if (has_inline_data)
+			return err;
+	}
+
 	sb = inode->i_sb;
 	if (inode->i_size < EXT4_DIR_REC_LEN(1) + EXT4_DIR_REC_LEN(2) ||
 	    !(bh = ext4_bread(NULL, inode, 0, 0, &err))) {
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index 9991143..6ccfa8d 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -176,6 +176,7 @@ extern int ext4_delete_inline_entry(handle_t *handle,
 				    struct inode *dir,
 				    struct ext4_dir_entry_2 *de_del,
 				    struct buffer_head *bh);
+extern int empty_inline_dir(struct inode *dir, int *has_inline_data);
 # else  /* CONFIG_EXT4_FS_XATTR */
 
 static inline int
@@ -373,6 +374,11 @@ static inline int ext4_delete_inline_entry(handle_t *handle,
 {
 	return 0;
 }
+
+static inline int empty_inline_dir(struct inode *dir, int *has_inline_data)
+{
+	return 0;
+}
 # endif  /* CONFIG_EXT4_FS_XATTR */
 
 #ifdef CONFIG_EXT4_FS_SECURITY
-- 
1.7.0.4


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

* [PATCH V4 18/22] ext4: let ext4_rename handle inline dir.
  2012-02-20  7:01 ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Tao Ma
                     ` (15 preceding siblings ...)
  2012-02-20  7:01   ` [PATCH V4 17/22] ext4: let empty_dir handle inline dir Tao Ma
@ 2012-02-20  7:01   ` Tao Ma
  2012-02-20  7:01   ` [PATCH V4 19/22] ext4: Let fiemap work with inline data Tao Ma
                     ` (4 subsequent siblings)
  21 siblings, 0 replies; 32+ messages in thread
From: Tao Ma @ 2012-02-20  7:01 UTC (permalink / raw)
  To: linux-ext4; +Cc: linux-fsdevel

From: Tao Ma <boyu.mt@taobao.com>

In case of we rename a dir, ext4_rename has to read the dir block
and change its dotdot's information. The old ext4_rename encapsulated
the dir_block read into itself. So this patch try to add a new function
ext4_get_dir_block which get the dir buffer information so the
ext4_rename can handle it properly.

Signed-off-by: Tao Ma <boyu.mt@taobao.com>
---
 fs/ext4/inline.c |   15 +++++++++++++++
 fs/ext4/namei.c  |   38 ++++++++++++++++++++++++++++++++------
 fs/ext4/xattr.h  |   12 ++++++++++++
 3 files changed, 59 insertions(+), 6 deletions(-)

diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index 6612ce1..f494c5f 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -1338,6 +1338,21 @@ out:
 	return ret;
 }
 
+struct buffer_head *ext4_get_first_inline_block(struct inode *inode,
+				void **buf, int *buf_size, int *retval)
+{
+	struct ext4_iloc iloc;
+
+	*retval = ext4_get_inode_loc(inode, &iloc);
+	if (*retval)
+		return NULL;
+
+	*buf = ext4_raw_inode(&iloc)->i_block;
+	*buf_size = EXT4_MIN_INLINE_DATA_SIZE;
+
+	return iloc.bh;
+}
+
 struct buffer_head *ext4_find_inline_entry(struct inode *dir,
 					const struct qstr *d_name,
 					struct ext4_dir_entry_2 **res_dir,
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index b6b718e..600371b 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -2507,6 +2507,31 @@ retry:
 	(ext4_next_entry((struct ext4_dir_entry_2 *)(buffer), size)->inode)
 
 /*
+ * Try to find buffer head where contains the parent block.
+ * It should be the inode block if it is inlined or the 1st block
+ * if it is a normal dir.
+ */
+static struct buffer_head *ext4_get_first_dir_block(handle_t *handle,
+						    struct inode *inode,
+						    void **buf,
+						    int *buf_size,
+						    int *retval)
+{
+	struct buffer_head *bh;
+
+	if (!ext4_has_inline_data(inode)) {
+		bh = ext4_bread(handle, inode, 0, 0, retval);
+		if (!bh)
+			return NULL;
+		*buf = bh->b_data;
+		*buf_size = inode->i_sb->s_blocksize;
+		return bh;
+	}
+
+	return ext4_get_first_inline_block(inode, buf, buf_size, retval);
+}
+
+/*
  * Anybody can rename anything with this: the permission checks are left to the
  * higher-level routines.
  */
@@ -2517,7 +2542,8 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
 	struct inode *old_inode, *new_inode;
 	struct buffer_head *old_bh, *new_bh, *dir_bh;
 	struct ext4_dir_entry_2 *old_de, *new_de;
-	int retval, force_da_alloc = 0;
+	int buf_size, retval, force_da_alloc = 0;
+	void *dir_buf = NULL;
 
 	dquot_initialize(old_dir);
 	dquot_initialize(new_dir);
@@ -2564,11 +2590,12 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
 				goto end_rename;
 		}
 		retval = -EIO;
-		dir_bh = ext4_bread(handle, old_inode, 0, 0, &retval);
+		dir_bh = ext4_get_first_dir_block(handle, old_inode,
+						  &dir_buf, &buf_size, &retval);
 		if (!dir_bh)
 			goto end_rename;
-		if (le32_to_cpu(PARENT_INO(dir_bh->b_data,
-				old_dir->i_sb->s_blocksize)) != old_dir->i_ino)
+		if (le32_to_cpu(PARENT_INO(dir_buf,
+					   buf_size)) != old_dir->i_ino)
 			goto end_rename;
 		retval = -EMLINK;
 		if (!new_inode && new_dir != old_dir &&
@@ -2648,8 +2675,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
 	old_dir->i_ctime = old_dir->i_mtime = ext4_current_time(old_dir);
 	ext4_update_dx_flag(old_dir);
 	if (dir_bh) {
-		PARENT_INO(dir_bh->b_data, new_dir->i_sb->s_blocksize) =
-						cpu_to_le32(new_dir->i_ino);
+		PARENT_INO(dir_buf, buf_size) = cpu_to_le32(new_dir->i_ino);
 		BUFFER_TRACE(dir_bh, "call ext4_handle_dirty_metadata");
 		retval = ext4_handle_dirty_metadata(handle, old_inode, dir_bh);
 		if (retval) {
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index 6ccfa8d..ed3ee6b 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -177,6 +177,10 @@ extern int ext4_delete_inline_entry(handle_t *handle,
 				    struct ext4_dir_entry_2 *de_del,
 				    struct buffer_head *bh);
 extern int empty_inline_dir(struct inode *dir, int *has_inline_data);
+extern struct buffer_head *ext4_get_first_inline_block(struct inode *inode,
+						       void **buf,
+						       int *buf_size,
+						       int *retval);
 # else  /* CONFIG_EXT4_FS_XATTR */
 
 static inline int
@@ -379,6 +383,14 @@ static inline int empty_inline_dir(struct inode *dir, int *has_inline_data)
 {
 	return 0;
 }
+
+static inline struct buffer_head *
+ext4_get_first_inline_block(struct inode *inode,
+			    void **buf, int *buf_size,
+			    int *retval)
+{
+	return NULL;
+}
 # endif  /* CONFIG_EXT4_FS_XATTR */
 
 #ifdef CONFIG_EXT4_FS_SECURITY
-- 
1.7.0.4


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

* [PATCH V4 19/22] ext4: Let fiemap work with inline data.
  2012-02-20  7:01 ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Tao Ma
                     ` (16 preceding siblings ...)
  2012-02-20  7:01   ` [PATCH V4 18/22] ext4: let ext4_rename " Tao Ma
@ 2012-02-20  7:01   ` Tao Ma
  2012-02-20  7:01   ` [PATCH V4 20/22] ext4: Evict inline data out if we needs to strore xattr in inode Tao Ma
                     ` (3 subsequent siblings)
  21 siblings, 0 replies; 32+ messages in thread
From: Tao Ma @ 2012-02-20  7:01 UTC (permalink / raw)
  To: linux-ext4; +Cc: linux-fsdevel

From: Tao Ma <boyu.mt@taobao.com>

fiemap is used to find the disk layout of a file, as for inline data,
let us just pretend like a file with just one extent.

Signed-off-by: Tao Ma <boyu.mt@taobao.com>
---
 fs/ext4/extents.c |    9 +++++++++
 fs/ext4/inline.c  |   35 +++++++++++++++++++++++++++++++++++
 fs/ext4/xattr.h   |   10 ++++++++++
 3 files changed, 54 insertions(+), 0 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index c861727..9f609dc 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -4923,6 +4923,15 @@ int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
 	ext4_lblk_t start_blk;
 	int error = 0;
 
+	if (ext4_has_inline_data(inode)) {
+		int has_inline = 1;
+
+		error = ext4_inline_data_fiemap(inode, fieinfo, &has_inline);
+
+		if (has_inline)
+			return error;
+	}
+
 	/* fallback to generic here if not in extents fmt */
 	if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)))
 		return generic_block_fiemap(inode, fieinfo, start, len,
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index f494c5f..3a65e64 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -15,6 +15,7 @@
 #include "ext4.h"
 #include "xattr.h"
 #include "truncate.h"
+#include <linux/fiemap.h>
 
 #define EXT4_XATTR_SYSTEM_DATA_NAME	"data"
 #define EXT4_MIN_INLINE_DATA_SIZE	((sizeof(__le32) * EXT4_N_BLOCKS))
@@ -1530,3 +1531,37 @@ int ext4_destroy_inline_data(handle_t *handle, struct inode *inode)
 
 	return ret;
 }
+
+int ext4_inline_data_fiemap(struct inode *inode,
+			    struct fiemap_extent_info *fieinfo,
+			    int *has_inline)
+{
+	__u64 physical = 0;
+	__u64 length;
+	__u32 flags = FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_LAST;
+	int error = 0;
+	struct ext4_iloc iloc;
+
+	down_read(&EXT4_I(inode)->xattr_sem);
+	if (!ext4_has_inline_data(inode)) {
+		*has_inline = 0;
+		goto out;
+	}
+
+	error = ext4_get_inode_loc(inode, &iloc);
+	if (error)
+		goto out;
+
+	physical = iloc.bh->b_blocknr << inode->i_sb->s_blocksize_bits;
+	physical += (char *)ext4_raw_inode(&iloc) - iloc.bh->b_data;
+	physical += offsetof(struct ext4_inode, i_block);
+	length = i_size_read(inode);
+
+	if (physical)
+		error = fiemap_fill_next_extent(fieinfo, 0, physical,
+						length, flags);
+	brelse(iloc.bh);
+out:
+	up_read(&EXT4_I(inode)->xattr_sem);
+	return (error < 0 ? error : 0);
+}
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index ed3ee6b..e80a212 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -181,6 +181,9 @@ extern struct buffer_head *ext4_get_first_inline_block(struct inode *inode,
 						       void **buf,
 						       int *buf_size,
 						       int *retval);
+extern int ext4_inline_data_fiemap(struct inode *inode,
+				   struct fiemap_extent_info *fieinfo,
+				   int *has_inline);
 # else  /* CONFIG_EXT4_FS_XATTR */
 
 static inline int
@@ -391,6 +394,13 @@ ext4_get_first_inline_block(struct inode *inode,
 {
 	return NULL;
 }
+
+static inline int ext4_inline_data_fiemap(struct inode *inode,
+					  struct fiemap_extent_info *fieinfo,
+					  int *has_inline)
+{
+	return 0;
+}
 # endif  /* CONFIG_EXT4_FS_XATTR */
 
 #ifdef CONFIG_EXT4_FS_SECURITY
-- 
1.7.0.4


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

* [PATCH V4 20/22] ext4: Evict inline data out if we needs to strore xattr in inode.
  2012-02-20  7:01 ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Tao Ma
                     ` (17 preceding siblings ...)
  2012-02-20  7:01   ` [PATCH V4 19/22] ext4: Let fiemap work with inline data Tao Ma
@ 2012-02-20  7:01   ` Tao Ma
  2012-02-20  7:01   ` [PATCH V4 21/22] ext4: let ext4_truncate handle inline data correctly Tao Ma
                     ` (2 subsequent siblings)
  21 siblings, 0 replies; 32+ messages in thread
From: Tao Ma @ 2012-02-20  7:01 UTC (permalink / raw)
  To: linux-ext4; +Cc: linux-fsdevel

From: Tao Ma <boyu.mt@taobao.com>

Now we stores data in the inode, but in case we need to store some
xattrs and inode doesn't have enough space, Andreas suggested that
we should keep the xattr(metadata) in and data should be pushed out.
So this patch does the work.

Signed-off-by: Tao Ma <boyu.mt@taobao.com>
---
 fs/ext4/inline.c |   94 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 fs/ext4/xattr.c  |   52 ++++++++++++++++++++++++++++--
 fs/ext4/xattr.h  |    9 +++--
 3 files changed, 145 insertions(+), 10 deletions(-)

diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index 3a65e64..b8576be 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -197,7 +197,7 @@ out:
 /*
  * write the buffer to the inline inode.
  * If 'create' is set, we don't need to do the extra copy in the xattr
- * value since it is already handled by ext4_xattr_ibody_set. That saves
+ * value since it is already handled by ext4_xattr_ibody_inline_set. That saves
  * us one memcpy.
  */
 void ext4_write_inline_data(struct inode *inode, struct ext4_iloc *iloc,
@@ -275,7 +275,7 @@ static int ext4_create_inline_data(handle_t *handle,
 
 	BUG_ON(!is.s.not_found);
 
-	error = ext4_xattr_ibody_set(handle, inode, &i, &is);
+	error = ext4_xattr_ibody_inline_set(handle, inode, &i, &is);
 	if (error) {
 		if (error == -ENOSPC)
 			ext4_clear_inode_state(inode,
@@ -344,7 +344,7 @@ static int ext4_update_inline_data(handle_t *handle, struct inode *inode,
 	i.value = value;
 	i.value_len = len;
 
-	error = ext4_xattr_ibody_set(handle, inode, &i, &is);
+	error = ext4_xattr_ibody_inline_set(handle, inode, &i, &is);
 	if (error)
 		goto out;
 
@@ -416,7 +416,7 @@ static int ext4_destroy_inline_data_nolock(handle_t *handle, struct inode *inode
 	if (error)
 		goto out;
 
-	error = ext4_xattr_ibody_set(handle, inode, &i, &is);
+	error = ext4_xattr_ibody_inline_set(handle, inode, &i, &is);
 	if (error)
 		goto out;
 
@@ -1565,3 +1565,89 @@ out:
 	up_read(&EXT4_I(inode)->xattr_sem);
 	return (error < 0 ? error : 0);
 }
+
+/*
+ * Called during xattr set, and if we can sparse space 'needed',
+ * just create the extent tree evict the data to the outer block.
+ *
+ * We use jbd2 instead of page cache to move data to the 1st block
+ * so that the whole transaction can be committed as a whole and
+ * the data isn't lost because of the delayed page cache write.
+ */
+int ext4_try_to_evict_inline_data(handle_t *handle,
+				  struct inode *inode,
+				  int needed)
+{
+	int error;
+	struct ext4_xattr_entry *entry;
+	struct ext4_xattr_ibody_header *header;
+	struct ext4_inode *raw_inode;
+	struct ext4_iloc iloc;
+	void *buf = NULL;
+	struct buffer_head *data_bh = NULL;
+	struct ext4_map_blocks map;
+
+	error = ext4_get_inode_loc(inode, &iloc);
+	if (error)
+		return error;
+
+	raw_inode = ext4_raw_inode(&iloc);
+	header = IHDR(inode, raw_inode);
+	entry = (struct ext4_xattr_entry *)((void *)raw_inode +
+					    EXT4_I(inode)->i_inline_off);
+	if (EXT4_XATTR_LEN(entry->e_name_len) +
+	    EXT4_XATTR_SIZE(le32_to_cpu(entry->e_value_size)) < needed) {
+		error = -ENOSPC;
+		goto out;
+	}
+
+	buf = kmalloc(ext4_get_inline_size(inode), GFP_NOFS);
+	if (!buf) {
+		error = -ENOMEM;
+		goto out;
+	}
+
+	error = ext4_read_inline_data(inode, buf,
+				      ext4_get_inline_size(inode), &iloc);
+	if (error < 0)
+		goto out;
+
+	error = ext4_destroy_inline_data_nolock(handle, inode);
+	if (error)
+		goto out;
+
+	map.m_lblk = 0;
+	map.m_len = 1;
+	map.m_flags = 0;
+	error = ext4_map_blocks(handle, inode, &map, EXT4_GET_BLOCKS_CREATE);
+	if (error < 0)
+		goto out;
+	if (!(map.m_flags & EXT4_MAP_MAPPED)) {
+		error = -EIO;
+		goto out;
+	}
+
+	data_bh = sb_getblk(inode->i_sb, map.m_pblk);
+	if (!data_bh) {
+		error = -EIO;
+		goto out;
+	}
+
+	lock_buffer(data_bh);
+	error = ext4_journal_get_create_access(handle, data_bh);
+	if (error) {
+		unlock_buffer(data_bh);
+		error = -EIO;
+		goto out;
+	}
+	memcpy(data_bh->b_data, buf, data_bh->b_size);
+	set_buffer_uptodate(data_bh);
+	unlock_buffer(data_bh);
+	error = ext4_handle_dirty_metadata(handle,
+					   inode, data_bh);
+out:
+	brelse(data_bh);
+	kfree(buf);
+	brelse(iloc.bh);
+	return error;
+}
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index 71ef45e..a296ee1 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -899,6 +899,30 @@ int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
 	return 0;
 }
 
+int ext4_xattr_ibody_inline_set(handle_t *handle, struct inode *inode,
+				struct ext4_xattr_info *i,
+				struct ext4_xattr_ibody_find *is)
+{
+	struct ext4_xattr_ibody_header *header;
+	struct ext4_xattr_search *s = &is->s;
+	int error;
+
+	if (EXT4_I(inode)->i_extra_isize == 0)
+		return -ENOSPC;
+	error = ext4_xattr_set_entry(i, s);
+	if (error)
+		return error;
+	header = IHDR(inode, ext4_raw_inode(&is->iloc));
+	if (!IS_LAST_ENTRY(s->first)) {
+		header->h_magic = cpu_to_le32(EXT4_XATTR_MAGIC);
+		ext4_set_inode_state(inode, EXT4_STATE_XATTR);
+	} else {
+		header->h_magic = cpu_to_le32(0);
+		ext4_clear_inode_state(inode, EXT4_STATE_XATTR);
+	}
+	return 0;
+}
+
 int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
 			 struct ext4_xattr_info *i,
 			 struct ext4_xattr_ibody_find *is)
@@ -910,8 +934,22 @@ int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
 	if (EXT4_I(inode)->i_extra_isize == 0)
 		return -ENOSPC;
 	error = ext4_xattr_set_entry(i, s);
-	if (error)
-		return error;
+	if (error) {
+		if (error == -ENOSPC &&
+		    ext4_has_inline_data(inode)) {
+			error = ext4_try_to_evict_inline_data(handle, inode,
+					EXT4_XATTR_LEN(strlen(i->name) +
+					EXT4_XATTR_SIZE(i->value_len)));
+			if (error)
+				return error;
+			error = ext4_xattr_ibody_find(inode, i, is);
+			if (error)
+				return error;
+			error = ext4_xattr_set_entry(i, s);
+		}
+		if (error)
+			return error;
+	}
 	header = IHDR(inode, ext4_raw_inode(&is->iloc));
 	if (!IS_LAST_ENTRY(s->first)) {
 		header->h_magic = cpu_to_le32(EXT4_XATTR_MAGIC);
@@ -1057,9 +1095,17 @@ ext4_xattr_set(struct inode *inode, int name_index, const char *name,
 {
 	handle_t *handle;
 	int error, retries = 0;
+	int credits = EXT4_DATA_TRANS_BLOCKS(inode->i_sb);
 
 retry:
-	handle = ext4_journal_start(inode, EXT4_DATA_TRANS_BLOCKS(inode->i_sb));
+	/*
+	 * In case of inline data, we may push out the data to a block,
+	 * So reserve the journal space first.
+	 */
+	if (ext4_has_inline_data(inode))
+		credits += ext4_writepage_trans_blocks(inode) + 1;
+
+	handle = ext4_journal_start(inode, credits);
 	if (IS_ERR(handle)) {
 		error = PTR_ERR(handle);
 	} else {
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index e80a212..d74df33 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -117,9 +117,9 @@ extern const struct xattr_handler *ext4_xattr_handlers[];
 
 extern int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
 				 struct ext4_xattr_ibody_find *is);
-extern int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
-			        struct ext4_xattr_info *i,
-			        struct ext4_xattr_ibody_find *is);
+extern int ext4_xattr_ibody_inline_set(handle_t *handle, struct inode *inode,
+			 	       struct ext4_xattr_info *i,
+				       struct ext4_xattr_ibody_find *is);
 extern int ext4_xattr_ibody_get(struct inode *inode, int name_index,
 				const char *name,
 				void *buffer, size_t buffer_size);
@@ -184,6 +184,9 @@ extern struct buffer_head *ext4_get_first_inline_block(struct inode *inode,
 extern int ext4_inline_data_fiemap(struct inode *inode,
 				   struct fiemap_extent_info *fieinfo,
 				   int *has_inline);
+extern int ext4_try_to_evict_inline_data(handle_t *handle,
+					 struct inode *inode,
+					 int needed);
 # else  /* CONFIG_EXT4_FS_XATTR */
 
 static inline int
-- 
1.7.0.4


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

* [PATCH V4 21/22] ext4: let ext4_truncate handle inline data correctly.
  2012-02-20  7:01 ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Tao Ma
                     ` (18 preceding siblings ...)
  2012-02-20  7:01   ` [PATCH V4 20/22] ext4: Evict inline data out if we needs to strore xattr in inode Tao Ma
@ 2012-02-20  7:01   ` Tao Ma
  2012-02-20  7:01   ` [PATCH V4 22/22] ext4: Enable ext4 inline support Tao Ma
  2012-02-21  6:51   ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Andreas Dilger
  21 siblings, 0 replies; 32+ messages in thread
From: Tao Ma @ 2012-02-20  7:01 UTC (permalink / raw)
  To: linux-ext4; +Cc: linux-fsdevel

From: Robin Dong <sanbai@taobao.com>

Signed-off-by: Robin Dong <sanbai@taobao.com>
Signed-off-by: Tao Ma <boyu.mt@taobao.com>
---
 fs/ext4/inline.c |   74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ext4/inode.c  |    5 +++
 fs/ext4/xattr.h  |    8 ++++++
 3 files changed, 87 insertions(+), 0 deletions(-)

diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index b8576be..0d32a39 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -1651,3 +1651,77 @@ out:
 	brelse(iloc.bh);
 	return error;
 }
+
+void ext4_inline_data_truncate(struct inode *inode)
+{
+	handle_t *handle;
+	int inline_size = ext4_get_inline_size(inode);
+	size_t isize = i_size_read(inode);
+	int needed_blocks, value_len;
+	void *value = NULL;
+	struct ext4_xattr_ibody_find is = {
+		.s = { .not_found = -ENODATA, },
+	};
+	struct ext4_xattr_info i = {
+		.name_index = EXT4_XATTR_INDEX_SYSTEM_DATA,
+		.name = EXT4_XATTR_SYSTEM_DATA_NAME,
+	};
+
+	needed_blocks = ext4_writepage_trans_blocks(inode);
+	handle = ext4_journal_start(inode, needed_blocks);
+	if (IS_ERR(handle))
+		return;
+
+	if (ext4_orphan_add(handle, inode))
+		goto out;
+
+	EXT4_I(inode)->i_disksize = inode->i_size;
+
+	if (isize < ext4_get_max_inline_size(inode)) {
+		if (ext4_get_inode_loc(inode, &is.iloc))
+			goto out;
+
+		if (isize < EXT4_MIN_INLINE_DATA_SIZE)
+			memset(ext4_raw_inode(&is.iloc)->i_block + isize, 0,
+					EXT4_MIN_INLINE_DATA_SIZE - isize);
+		if (inline_size > EXT4_MIN_INLINE_DATA_SIZE &&
+				isize < inline_size) {
+			if (ext4_xattr_ibody_find(inode, &i, &is))
+				goto out_bh;
+
+			BUG_ON(!is.s.not_found);
+
+			value_len = le32_to_cpu(is.s.here->e_value_size);
+			value = kmalloc(value_len, GFP_NOFS);
+
+			if (ext4_xattr_ibody_get(inode, i.name_index, i.name,
+						value, value_len))
+				goto out_bh;
+
+			i.value = value;
+			i.value_len = isize - EXT4_MIN_INLINE_DATA_SIZE;
+			if (ext4_xattr_ibody_inline_set(handle, inode, &i, &is))
+				goto out_bh;
+		}
+		if (isize < inline_size)
+			EXT4_I(inode)->i_inline_size = isize <
+				EXT4_MIN_INLINE_DATA_SIZE ?
+				EXT4_MIN_INLINE_DATA_SIZE : isize;
+out_bh:
+		if (value)
+			kfree(value);
+		brelse(is.iloc.bh);
+	} else
+		ext4_convert_inline_data_to_extent(inode->i_mapping, inode, 0);
+
+out:
+	if (inode->i_nlink)
+		ext4_orphan_del(handle, inode);
+
+	inode->i_mtime = inode->i_ctime = ext4_current_time(inode);
+	ext4_mark_inode_dirty(handle, inode);
+	if (IS_SYNC(inode))
+		ext4_handle_sync(handle);
+
+	ext4_journal_stop(handle);
+}
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 549af8f..805709b 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3496,6 +3496,11 @@ void ext4_truncate(struct inode *inode)
 	if (inode->i_size == 0 && !test_opt(inode->i_sb, NO_AUTO_DA_ALLOC))
 		ext4_set_inode_state(inode, EXT4_STATE_DA_ALLOC_CLOSE);
 
+	if (ext4_has_inline_data(inode)) {
+		ext4_inline_data_truncate(inode);
+		return;
+	}
+
 	if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
 		ext4_ext_truncate(inode);
 	else
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index d74df33..4f12d02 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -187,6 +187,8 @@ extern int ext4_inline_data_fiemap(struct inode *inode,
 extern int ext4_try_to_evict_inline_data(handle_t *handle,
 					 struct inode *inode,
 					 int needed);
+extern void ext4_inline_data_truncate(struct inode *inode);
+
 # else  /* CONFIG_EXT4_FS_XATTR */
 
 static inline int
@@ -404,6 +406,12 @@ static inline int ext4_inline_data_fiemap(struct inode *inode,
 {
 	return 0;
 }
+
+static inline void ext4_inline_data_truncate(struct inode *inode)
+{
+	return;
+}
+
 # endif  /* CONFIG_EXT4_FS_XATTR */
 
 #ifdef CONFIG_EXT4_FS_SECURITY
-- 
1.7.0.4


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

* [PATCH V4 22/22] ext4: Enable ext4 inline support.
  2012-02-20  7:01 ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Tao Ma
                     ` (19 preceding siblings ...)
  2012-02-20  7:01   ` [PATCH V4 21/22] ext4: let ext4_truncate handle inline data correctly Tao Ma
@ 2012-02-20  7:01   ` Tao Ma
  2012-02-21  6:51   ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Andreas Dilger
  21 siblings, 0 replies; 32+ messages in thread
From: Tao Ma @ 2012-02-20  7:01 UTC (permalink / raw)
  To: linux-ext4; +Cc: linux-fsdevel

From: Tao Ma <boyu.mt@taobao.com>

Signed-off-by: Tao Ma <boyu.mt@taobao.com>
---
 fs/ext4/ext4.h   |    3 ++-
 fs/ext4/ialloc.c |    4 ++++
 2 files changed, 6 insertions(+), 1 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 26024a3..9486cce 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1452,7 +1452,8 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
 					 EXT4_FEATURE_INCOMPAT_EXTENTS| \
 					 EXT4_FEATURE_INCOMPAT_64BIT| \
 					 EXT4_FEATURE_INCOMPAT_FLEX_BG| \
-					 EXT4_FEATURE_INCOMPAT_MMP)
+					 EXT4_FEATURE_INCOMPAT_MMP |	\
+					 EXT4_FEATURE_INCOMPAT_INLINEDATA)
 #define EXT4_FEATURE_RO_COMPAT_SUPP	(EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER| \
 					 EXT4_FEATURE_RO_COMPAT_LARGE_FILE| \
 					 EXT4_FEATURE_RO_COMPAT_GDT_CSUM| \
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index 25d8c97..2401934 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -901,6 +901,10 @@ got:
 
 	ei->i_extra_isize = EXT4_SB(sb)->s_want_extra_isize;
 
+	ei->i_inline_off = 0;
+	if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_INLINEDATA))
+		ext4_set_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
+
 	ret = inode;
 	dquot_initialize(inode);
 	err = dquot_alloc_inode(inode);
-- 
1.7.0.4


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

* Re: [PATCH V4 01/22] ext4: Move extra inode read to a new function.
  2012-02-20  7:01 ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Tao Ma
                     ` (20 preceding siblings ...)
  2012-02-20  7:01   ` [PATCH V4 22/22] ext4: Enable ext4 inline support Tao Ma
@ 2012-02-21  6:51   ` Andreas Dilger
  2012-02-21  8:13     ` Tao Ma
  21 siblings, 1 reply; 32+ messages in thread
From: Andreas Dilger @ 2012-02-21  6:51 UTC (permalink / raw)
  To: Tao Ma; +Cc: linux-ext4, linux-fsdevel

On 2012-02-20, at 12:01 AM, Tao Ma wrote:
> From: Tao Ma <boyu.mt@taobao.com>
> 
> Currently, in ext4_iget we do a simple check to see whether
> there does exist some information starting from the end
> of i_extra_size. With inline data added, this procedure
> is more complicated. So move it to a new function named
> ext4_iget_extra_inode.
> 
> Signed-off-by: Tao Ma <boyu.mt@taobao.com>
> ---
> fs/ext4/inode.c |   19 ++++++++++++-------
> 1 files changed, 12 insertions(+), 7 deletions(-)
> 
> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
> index feaa82f..0b2d6c1 100644
> --- a/fs/ext4/inode.c
> +++ b/fs/ext4/inode.c
> @@ -3610,6 +3610,16 @@ static blkcnt_t ext4_inode_blocks(struct ext4_inode *raw_inode,
> 	}
> }
> 
> +static inline void ext4_iget_extra_inode(struct inode *inode,
> +					 struct ext4_inode *raw_inode,
> +					 struct ext4_inode_info *ei)
> +{
> +	__le32 *magic = (void *)raw_inode +
> +			EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize;
> +	if (*magic == cpu_to_le32(EXT4_XATTR_MAGIC))
> +		ext4_set_inode_state(inode, EXT4_STATE_XATTR);
> +}

The patch looks fine, and is a nice little cleanup, but...

> struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
> {
> 	struct ext4_iloc iloc;
> @@ -3720,13 +3730,8 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
> 			/* The extra space is currently unused. Use it. */
> 			ei->i_extra_isize = sizeof(struct ext4_inode) -
> 					    EXT4_GOOD_OLD_INODE_SIZE;
> -		} else {
> -			__le32 *magic = (void *)raw_inode +
> -					EXT4_GOOD_OLD_INODE_SIZE +
> -					ei->i_extra_isize;
> -			if (*magic == cpu_to_le32(EXT4_XATTR_MAGIC))
> -				ext4_set_inode_state(inode, EXT4_STATE_XATTR);
> -		}
> +		} else
> +			ext4_iget_extra_inode(inode, raw_inode, ei);

This does not follow Documentation/CodingStyle - there should be braces around
the "else" clause if it is needed for the "if" clause.  The below code could be
fixed at the same time.

> 	} else
> 		ei->i_extra_isize = 0;
> 
> -- 
> 1.7.0.4
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


Cheers, Andreas






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

* Re: [PATCH V4 01/22] ext4: Move extra inode read to a new function.
  2012-02-21  6:51   ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Andreas Dilger
@ 2012-02-21  8:13     ` Tao Ma
  0 siblings, 0 replies; 32+ messages in thread
From: Tao Ma @ 2012-02-21  8:13 UTC (permalink / raw)
  To: Andreas Dilger; +Cc: linux-ext4, linux-fsdevel

On 02/21/2012 02:51 PM, Andreas Dilger wrote:
> On 2012-02-20, at 12:01 AM, Tao Ma wrote:
>> From: Tao Ma <boyu.mt@taobao.com>
>>
>> Currently, in ext4_iget we do a simple check to see whether
>> there does exist some information starting from the end
>> of i_extra_size. With inline data added, this procedure
>> is more complicated. So move it to a new function named
>> ext4_iget_extra_inode.
>>
>> Signed-off-by: Tao Ma <boyu.mt@taobao.com>
>> ---
>> fs/ext4/inode.c |   19 ++++++++++++-------
>> 1 files changed, 12 insertions(+), 7 deletions(-)
>>
>> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
>> index feaa82f..0b2d6c1 100644
>> --- a/fs/ext4/inode.c
>> +++ b/fs/ext4/inode.c
>> @@ -3610,6 +3610,16 @@ static blkcnt_t ext4_inode_blocks(struct ext4_inode *raw_inode,
>> 	}
>> }
>>
>> +static inline void ext4_iget_extra_inode(struct inode *inode,
>> +					 struct ext4_inode *raw_inode,
>> +					 struct ext4_inode_info *ei)
>> +{
>> +	__le32 *magic = (void *)raw_inode +
>> +			EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize;
>> +	if (*magic == cpu_to_le32(EXT4_XATTR_MAGIC))
>> +		ext4_set_inode_state(inode, EXT4_STATE_XATTR);
>> +}
> 
> The patch looks fine, and is a nice little cleanup, but...
> 
>> struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
>> {
>> 	struct ext4_iloc iloc;
>> @@ -3720,13 +3730,8 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
>> 			/* The extra space is currently unused. Use it. */
>> 			ei->i_extra_isize = sizeof(struct ext4_inode) -
>> 					    EXT4_GOOD_OLD_INODE_SIZE;
>> -		} else {
>> -			__le32 *magic = (void *)raw_inode +
>> -					EXT4_GOOD_OLD_INODE_SIZE +
>> -					ei->i_extra_isize;
>> -			if (*magic == cpu_to_le32(EXT4_XATTR_MAGIC))
>> -				ext4_set_inode_state(inode, EXT4_STATE_XATTR);
>> -		}
>> +		} else
>> +			ext4_iget_extra_inode(inode, raw_inode, ei);
> 
> This does not follow Documentation/CodingStyle - there should be braces around
> the "else" clause if it is needed for the "if" clause.  The below code could be
> fixed at the same time.
OK, I will fix it. Thanks for the review.

Thanks
Tao
> 
>> 	} else
>> 		ei->i_extra_isize = 0;
>>
>> -- 
>> 1.7.0.4
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
> 
> Cheers, Andreas
> 
> 
> 
> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


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

* Re: [PATCH V4 02/22] ext4: export inline xattr functions.
  2012-02-20  7:01   ` [PATCH V4 02/22] ext4: export inline xattr functions Tao Ma
@ 2012-02-21 22:45     ` Andreas Dilger
  0 siblings, 0 replies; 32+ messages in thread
From: Andreas Dilger @ 2012-02-21 22:45 UTC (permalink / raw)
  To: Tao Ma; +Cc: linux-ext4, linux-fsdevel

On 2012-02-20, at 12:01 AM, Tao Ma wrote:
> From: Tao Ma <boyu.mt@taobao.com>
> 
> Inline data needs some inline xattr functions, so export them
> from fs/ext4/xattr.c so that inline.c can uses them.
> 
> Signed-off-by: Tao Ma <boyu.mt@taobao.com>
> ---
> fs/ext4/xattr.c |   63 ++++++++++++++++++++++++++----------------------------
> fs/ext4/xattr.h |   57 +++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 87 insertions(+), 33 deletions(-)
> 
> diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
> index 93a00d8..07eeaf3 100644
> --- a/fs/ext4/xattr.c
> +++ b/fs/ext4/xattr.c
> @@ -61,11 +61,6 @@
> #include "xattr.h"
> #include "acl.h"
> 
> -#define BHDR(bh) ((struct ext4_xattr_header *)((bh)->b_data))
> -#define ENTRY(ptr) ((struct ext4_xattr_entry *)(ptr))
> -#define BFIRST(bh) ENTRY(BHDR(bh)+1)
> -#define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)
> -
> #ifdef EXT4_XATTR_DEBUG
> # define ea_idebug(inode, f...) do { \
> 		printk(KERN_DEBUG "inode %s:%lu: ", \
> @@ -255,7 +250,7 @@ cleanup:
> 	return error;
> }
> 
> -static int
> +int
> ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name,
> 		     void *buffer, size_t buffer_size)
> {
> @@ -522,21 +517,6 @@ static size_t ext4_xattr_free_space(struct ext4_xattr_entry *last,
> 	return (*min_offs - ((void *)last - base) - sizeof(__u32));
> }
> 
> -struct ext4_xattr_info {
> -	int name_index;
> -	const char *name;
> -	const void *value;
> -	size_t value_len;
> -};
> -
> -struct ext4_xattr_search {
> -	struct ext4_xattr_entry *first;
> -	void *base;
> -	void *end;
> -	struct ext4_xattr_entry *here;
> -	int not_found;
> -};
> -
> static int
> ext4_xattr_set_entry(struct ext4_xattr_info *i, struct ext4_xattr_search *s)
> {
> @@ -890,14 +870,8 @@ bad_block:
> #undef header
> }
> 
> -struct ext4_xattr_ibody_find {
> -	struct ext4_xattr_search s;
> -	struct ext4_iloc iloc;
> -};
> -
> -static int
> -ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
> -		      struct ext4_xattr_ibody_find *is)
> +int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
> +			  struct ext4_xattr_ibody_find *is)
> {
> 	struct ext4_xattr_ibody_header *header;
> 	struct ext4_inode *raw_inode;
> @@ -925,10 +899,33 @@ ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
> 	return 0;
> }
> 
> -static int
> -ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
> -		     struct ext4_xattr_info *i,
> -		     struct ext4_xattr_ibody_find *is)
> +int ext4_xattr_ibody_inline_set(handle_t *handle, struct inode *inode,
> +				struct ext4_xattr_info *i,
> +				struct ext4_xattr_ibody_find *is)
> +{

The addition of this function should be moved into [PATCH 03/22], since
there are otherwise no users of this function in this patch.

> +	struct ext4_xattr_ibody_header *header;
> +	struct ext4_xattr_search *s = &is->s;
> +	int error;
> +
> +	if (EXT4_I(inode)->i_extra_isize == 0)
> +		return -ENOSPC;
> +	error = ext4_xattr_set_entry(i, s);
> +	if (error)
> +		return error;
> +	header = IHDR(inode, ext4_raw_inode(&is->iloc));
> +	if (!IS_LAST_ENTRY(s->first)) {
> +		header->h_magic = cpu_to_le32(EXT4_XATTR_MAGIC);
> +		ext4_set_inode_state(inode, EXT4_STATE_XATTR);
> +	} else {
> +		header->h_magic = cpu_to_le32(0);
> +		ext4_clear_inode_state(inode, EXT4_STATE_XATTR);
> +	}
> +	return 0;
> +}
> +
> +int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
> +			 struct ext4_xattr_info *i,
> +			 struct ext4_xattr_ibody_find *is)
> {
> 	struct ext4_xattr_ibody_header *header;
> 	struct ext4_xattr_search *s = &is->s;
> diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
> index 25b7387..2879761 100644
> --- a/fs/ext4/xattr.h
> +++ b/fs/ext4/xattr.h
> @@ -63,6 +63,32 @@ struct ext4_xattr_entry {
> 		EXT4_I(inode)->i_extra_isize))
> #define IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr)+1))
> 
> +#define BHDR(bh) ((struct ext4_xattr_header *)((bh)->b_data))
> +#define ENTRY(ptr) ((struct ext4_xattr_entry *)(ptr))
> +#define BFIRST(bh) ENTRY(BHDR(bh)+1)
> +#define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)
> +
> +
> +struct ext4_xattr_info {
> +	int name_index;
> +	const char *name;
> +	const void *value;
> +	size_t value_len;
> +};
> +
> +struct ext4_xattr_search {
> +	struct ext4_xattr_entry *first;
> +	void *base;
> +	void *end;
> +	struct ext4_xattr_entry *here;
> +	int not_found;
> +};
> +
> +struct ext4_xattr_ibody_find {
> +	struct ext4_xattr_search s;
> +	struct ext4_iloc iloc;
> +};
> +
> # ifdef CONFIG_EXT4_FS_XATTR
> 
> extern const struct xattr_handler ext4_xattr_user_handler;
> @@ -88,6 +114,15 @@ extern void ext4_exit_xattr(void);
> 
> extern const struct xattr_handler *ext4_xattr_handlers[];
> 
> +extern int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
> +				 struct ext4_xattr_ibody_find *is);
> +extern int ext4_xattr_ibody_inline_set(handle_t *handle, struct inode *inode,
> +				       struct ext4_xattr_info *i,
> +				       struct ext4_xattr_ibody_find *is);
> +extern int ext4_xattr_ibody_get(struct inode *inode, int name_index,
> +				const char *name,
> +				void *buffer, size_t buffer_size);
> +
> # else  /* CONFIG_EXT4_FS_XATTR */
> 
> static inline int
> @@ -141,6 +176,28 @@ ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
> 
> #define ext4_xattr_handlers	NULL
> 
> +static inline int
> +ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
> +		      struct ext4_xattr_ibody_find *is)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +static inline int
> +ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
> +		     struct ext4_xattr_info *i,
> +		     struct ext4_xattr_ibody_find *is)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +extern int ext4_xattr_ibody_get(struct inode *inode, int name_index,
> +				const char *name,
> +				void *buffer, size_t buffer_size)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> # endif  /* CONFIG_EXT4_FS_XATTR */
> 
> #ifdef CONFIG_EXT4_FS_SECURITY
> -- 
> 1.7.0.4
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


Cheers, Andreas






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

* Re: [PATCH V4 00/22] ext4: Add inline data support
  2012-02-20  7:00 [PATCH V4 00/22] ext4: Add inline data support Tao Ma
  2012-02-20  7:01 ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Tao Ma
@ 2012-02-21 23:44 ` Andreas Dilger
  2012-02-22  1:34   ` Tao Ma
  1 sibling, 1 reply; 32+ messages in thread
From: Andreas Dilger @ 2012-02-21 23:44 UTC (permalink / raw)
  To: Tao Ma; +Cc: Ted Ts'o, ext4 development, linux-fsdevel

On 2012-02-20, at 12:00 AM, Tao Ma wrote:
> Hi Ted, Andreas and list,
>        This is the v4 attempt to add inline data support to ext4 inode.
> For more information about the background, please refer to the thread
> http://marc.info/?l=linux-ext4&m=131715205428067&w=2
> 
> Changlog from v3 to v4:
> 1. Add support for truncate which is really a bug.
> 2. Some bug fixes.
> 3. rebased to the latest kernel.

I'm starting to look through this patch series, and a number of things are
missing that would make it much easier to understand and accept:
- a good comment and possibly a diagram at the start of fs/ext4/xattr.c
  that describes where and how the inline data is stored in the inode,
  what the policies are for storing data inline or externally, etc.
- some benchmark data that shows why landing this code is desirable.
  My comments in the above thread show that small files and directories
  could benefit from this, but real proof now that you have made this
  patch is whether this translates into noticeable space savings, and
  hopefully also noticeable performance improvements in some benchmarks:
  - I suspect that running some tests with bigalloc + 512-byte inodes
    or similar could show significant space savings and speedups for
    cold-cache directory traversal
  - measuring boot time on a distro with Gnome or KDE could show real
    speedups due to the many small files and directories used at startup
  - running a benchmark like mongo or postmark with small files and
    with 256- or 512-byte inodes may also show real speedups
  - is there some workload that you are using that shows speedups that
    could be described in general terms and show relative performance,
    even if it is not possible to supply the actual benchmark/tests?

I'll go through the patches and suggest cleanups and improvements, but
without improved documentation and real performance tests the patch is
very unlikely to be accepted by Ted.

> Changelog from v2 to v3:
> 1. Add support for evict data from inode if we can store xattr in it.
> 2. Add support for fiemap
> 3. Some nasty bug fixes
> 
> The v3 can be found here:
> http://marc.info/?l=linux-ext4&m=132421821001634&w=2
> 
> The v2 can be found here:
> http://marc.info/?l=linux-ext4&m=132189338604177&w=2
> 
> The v1 can be found here:
> http://marc.info/?l=linux-ext4&m=131961438221255&w=2
> 
> any suggestions are welcomed.
> 
> Thanks
> Tao


Cheers, Andreas






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

* Re: [PATCH V4 00/22] ext4: Add inline data support
  2012-02-21 23:44 ` [PATCH V4 00/22] ext4: Add inline data support Andreas Dilger
@ 2012-02-22  1:34   ` Tao Ma
  0 siblings, 0 replies; 32+ messages in thread
From: Tao Ma @ 2012-02-22  1:34 UTC (permalink / raw)
  To: Andreas Dilger; +Cc: Ted Ts'o, ext4 development, linux-fsdevel

On 02/22/2012 07:44 AM, Andreas Dilger wrote:
> On 2012-02-20, at 12:00 AM, Tao Ma wrote:
>> Hi Ted, Andreas and list,
>>        This is the v4 attempt to add inline data support to ext4 inode.
>> For more information about the background, please refer to the thread
>> http://marc.info/?l=linux-ext4&m=131715205428067&w=2
>>
>> Changlog from v3 to v4:
>> 1. Add support for truncate which is really a bug.
>> 2. Some bug fixes.
>> 3. rebased to the latest kernel.
> 
> I'm starting to look through this patch series, and a number of things are
Thanks for the review.
> missing that would make it much easier to understand and accept:
> - a good comment and possibly a diagram at the start of fs/ext4/xattr.c
>   that describes where and how the inline data is stored in the inode,
>   what the policies are for storing data inline or externally, etc.
sure, I will add it.
> - some benchmark data that shows why landing this code is desirable.
>   My comments in the above thread show that small files and directories
>   could benefit from this, but real proof now that you have made this
>   patch is whether this translates into noticeable space savings, and
>   hopefully also noticeable performance improvements in some benchmarks:
>   - I suspect that running some tests with bigalloc + 512-byte inodes
>     or similar could show significant space savings and speedups for
>     cold-cache directory traversal
>   - measuring boot time on a distro with Gnome or KDE could show real
>     speedups due to the many small files and directories used at startup
>   - running a benchmark like mongo or postmark with small files and
>     with 256- or 512-byte inodes may also show real speedups
>   - is there some workload that you are using that shows speedups that
>     could be described in general terms and show relative performance,
>     even if it is not possible to supply the actual benchmark/tests?
OK, I was just playing around with some bug fixes these days(to make it
stable in our production system) and doesn't do some performance tests
when upgrading from v2 to v3. I will try to do some tests and return the
data back in v5.

Thanks
Tao
> 
> I'll go through the patches and suggest cleanups and improvements, but
> without improved documentation and real performance tests the patch is
> very unlikely to be accepted by Ted.
> 
>> Changelog from v2 to v3:
>> 1. Add support for evict data from inode if we can store xattr in it.
>> 2. Add support for fiemap
>> 3. Some nasty bug fixes
>>
>> The v3 can be found here:
>> http://marc.info/?l=linux-ext4&m=132421821001634&w=2
>>
>> The v2 can be found here:
>> http://marc.info/?l=linux-ext4&m=132189338604177&w=2
>>
>> The v1 can be found here:
>> http://marc.info/?l=linux-ext4&m=131961438221255&w=2
>>
>> any suggestions are welcomed.
>>
>> Thanks
>> Tao
> 
> 
> Cheers, Andreas
> 
> 
> 
> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


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

* Re: [PATCH V4 03/22] ext4: Add the basic function for inline data support.
  2012-02-20  7:01   ` [PATCH V4 03/22] ext4: Add the basic function for inline data support Tao Ma
@ 2012-04-07  0:21     ` Andreas Dilger
  2012-04-13  2:59       ` Tao Ma
  0 siblings, 1 reply; 32+ messages in thread
From: Andreas Dilger @ 2012-04-07  0:21 UTC (permalink / raw)
  To: Tao Ma; +Cc: linux-ext4, linux-fsdevel

On 2012-02-20, at 12:01 AM, Tao Ma wrote:

> From: Tao Ma <boyu.mt@taobao.com>
> 
> Implement inline data with xattr. This idea is inspired by Andreas.
> So now we use "system.data" to store xattr.
> For inode_size = 256, currently we uses all the space between i_extra_isize
> and inode_size. For inode_size > 256, we use half of that space.
> 
> Signed-off-by: Tao Ma <boyu.mt@taobao.com>
> ---
> fs/ext4/Makefile |    2 +-
> fs/ext4/ext4.h   |    7 +
> fs/ext4/inline.c |  456 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> fs/ext4/inode.c  |    5 +-
> fs/ext4/xattr.c  |   24 ---
> fs/ext4/xattr.h  |   60 +++++++-
> 6 files changed, 525 insertions(+), 29 deletions(-)
> create mode 100644 fs/ext4/inline.c
> 
> diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
> index 56fd8f8..e88b7a6 100644
> --- a/fs/ext4/Makefile
> +++ b/fs/ext4/Makefile
> @@ -9,6 +9,6 @@ ext4-y	:= balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \
> 		ext4_jbd2.o migrate.o mballoc.o block_validity.o move_extent.o \
> 		mmp.o indirect.o
> 
> -ext4-$(CONFIG_EXT4_FS_XATTR)		+= xattr.o xattr_user.o xattr_trusted.o
> +ext4-$(CONFIG_EXT4_FS_XATTR)		+= xattr.o xattr_user.o xattr_trusted.o inline.o
> ext4-$(CONFIG_EXT4_FS_POSIX_ACL)	+= acl.o
> ext4-$(CONFIG_EXT4_FS_SECURITY)		+= xattr_security.o
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index 513004f..18254c0 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -371,6 +371,7 @@ struct flex_groups {
> #define EXT4_EXTENTS_FL			0x00080000 /* Inode uses extents */
> #define EXT4_EA_INODE_FL	        0x00200000 /* Inode used for large EA */
> #define EXT4_EOFBLOCKS_FL		0x00400000 /* Blocks allocated beyond EOF */
> +#define EXT4_INLINE_DATA_FL		0x00800000 /* Inode has inline data. */

This patch defines EXT4_INLINE_DATA_FL here, but it isn't used anywhere in
the code.  This would make the code a lot more efficient if this flag were
checked at the start of ext4_find_inline_data() and skip searching all of
the xattrs if no INLINE_DATA_FL is set.  That avoids overhead for reading
every regular inode that does not have inline data.

> #define EXT4_RESERVED_FL		0x80000000 /* reserved for ext4 lib */
> 
> #define EXT4_FL_USER_VISIBLE		0x004BDFFF /* User visible flags */
> @@ -427,6 +428,7 @@ enum {
> 	EXT4_INODE_EXTENTS	= 19,	/* Inode uses extents */
> 	EXT4_INODE_EA_INODE	= 21,	/* Inode used for large EA */
> 	EXT4_INODE_EOFBLOCKS	= 22,	/* Blocks allocated beyond EOF */
> +	EXT4_INODE_INLINE_DATA	= 23,	/* Data in inode. */
> 	EXT4_INODE_RESERVED	= 31,	/* reserved for ext4 lib */
> };
> 
> @@ -878,6 +880,10 @@ struct ext4_inode_info {
> 	/* on-disk additional length */
> 	__u16 i_extra_isize;
> 
> +	/* Indicate the inline data space. */
> +	u16 i_inline_off;
> +	u16 i_inline_size;
> +
> #ifdef CONFIG_QUOTA
> 	/* quota space reservation, managed internally by quota code */
> 	qsize_t i_reserved_quota;
> @@ -1307,6 +1313,7 @@ enum {
> 	EXT4_STATE_DIO_UNWRITTEN,	/* need convert on dio done*/
> 	EXT4_STATE_NEWENTRY,		/* File just added to dir */
> 	EXT4_STATE_DELALLOC_RESERVED,	/* blks already reserved for delalloc */
> +	EXT4_STATE_MAY_INLINE_DATA,	/* may have in-inode data */
> };
> 
> #define EXT4_INODE_BIT_FNS(name, field, offset)				\
> diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
> new file mode 100644
> index 0000000..77cedd2
> --- /dev/null
> +++ b/fs/ext4/inline.c
> @@ -0,0 +1,456 @@
> +/*
> + * Copyright (c) 2011 Taobao.
> + * Written by Tao Ma <boyu.mt@taobao.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of version 2.1 of the GNU Lesser General Public License
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +#include "ext4_jbd2.h"
> +#include "ext4.h"
> +#include "xattr.h"
> +
> +#define EXT4_XATTR_SYSTEM_DATA_NAME	"data"

If this constant name was shorter it would be easier to use, like:

#define EXT4_XATTR_SYSTEM_DATA "data"

> +#define EXT4_MIN_INLINE_DATA_SIZE	((sizeof(__le32) * EXT4_N_BLOCKS))
> +
> +int ext4_get_inline_size(struct inode *inode)
> +{
> +	if (EXT4_I(inode)->i_inline_off)
> +		return EXT4_I(inode)->i_inline_size;
> +
> +	return 0;
> +}
> +
> +static int get_max_inline_xattr_value_size(struct inode *inode,
> +					   struct ext4_iloc *iloc)
> +{
> +	struct ext4_xattr_ibody_header *header;
> +	struct ext4_xattr_entry *entry;
> +	struct ext4_inode *raw_inode;
> +	int free, min_offs;
> +
> +	if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR))
> +		return EXT4_XATTR_SIZE(EXT4_SB(inode->i_sb)->s_inode_size -
> +			EXT4_GOOD_OLD_INODE_SIZE -
> +			EXT4_I(inode)->i_extra_isize -
> +			sizeof(struct ext4_xattr_ibody_header) -
> +			EXT4_XATTR_LEN(strlen(EXT4_XATTR_SYSTEM_DATA_NAME)) -
> +			EXT4_XATTR_ROUND - sizeof(__u32));

Most of this calculation is common with min_offs below.  It would be nice
to compute it once instead of twice, since this calculation here is
pretty complex to read and would be simplified with an intermediate value.

	min_offs = EXT4_SB(inode->i_sb)->s_inode_size -
			EXT4_GOOD_OLD_INODE_SIZE -
			EXT4_I(inode)->i_extra_isize -
			sizeof(struct ext4_xattr_ibody_header) -
			EXT4_XATTR_LEN(strlen(EXT4_XATTR_SYSTEM_DATA));

	if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR))
		return EXT4_XATTR_SIZE(min_offs -
				       EXT4_XATTR_ROUND - sizeof(__u32));

It isn't obvious what the sizeof(__u32) represents here.  The EXT4_XATTR_ROUND
is to compensate for the "round_up" factor used in EXT4_XATTR_SIZE(), but it would be useful to document this.

> +	down_read(&EXT4_I(inode)->xattr_sem);
> +	raw_inode = ext4_raw_inode(iloc);
> +	header = IHDR(inode, raw_inode);
> +	entry = IFIRST(header);
> +	min_offs = EXT4_SB(inode->i_sb)->s_inode_size -
> +			EXT4_GOOD_OLD_INODE_SIZE -
> +			EXT4_I(inode)->i_extra_isize -
> +			sizeof(struct ext4_xattr_ibody_header);

> +
> +	/* Compute min_offs. */
> +	for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) {
> +		if (!entry->e_value_block && entry->e_value_size) {
> +			size_t offs = le16_to_cpu(entry->e_value_offs);
> +			if (offs < min_offs)
> +				min_offs = offs;
> +		}
> +	}
> +	free = min_offs -
> +		((void *)entry - (void *)IFIRST(header)) - sizeof(__u32);
> +
> +	if (EXT4_I(inode)->i_inline_off) {
> +		entry = (struct ext4_xattr_entry *)
> +			((void *)raw_inode + EXT4_I(inode)->i_inline_off);
> +
> +		free += le32_to_cpu(entry->e_value_size);
> +		goto out;
> +	}
> +
> +	free -= EXT4_XATTR_LEN(strlen(EXT4_XATTR_SYSTEM_DATA_NAME));
> +
> +	if (free > EXT4_XATTR_ROUND)
> +		free = EXT4_XATTR_SIZE(free - EXT4_XATTR_ROUND);
> +	else
> +		free = 0;
> +
> +out:
> +	up_read(&EXT4_I(inode)->xattr_sem);
> +	return free;
> +}
> +
> +/*
> + * Get the maximum size we now can store in an inode.
> + * If we can't find the space for a xattr entry, don't use the space
> + * of the extents since we have no space to indicate the inline data.
> + */
> +int ext4_get_max_inline_size(struct inode *inode)
> +{
> +	int error, max_inline_size;
> +	struct ext4_iloc iloc;
> +
> +	if (EXT4_I(inode)->i_extra_isize == 0)
> +		return 0;
> +
> +	error = ext4_get_inode_loc(inode, &iloc);
> +	if (error) {
> +		ext4_error_inode(inode, __func__, __LINE__, 0,
> +				 "can't get inode location %lu",
> +				 inode->i_ino);
> +		return 0;
> +	}
> +
> +	max_inline_size = get_max_inline_xattr_value_size(inode, &iloc);
> +
> +	brelse(iloc.bh);
> +
> +	if (!max_inline_size)
> +		return 0;
> +
> +	return max_inline_size + EXT4_MIN_INLINE_DATA_SIZE;
> +}
> +
> +int ext4_has_inline_data(struct inode *inode)
> +{
> +	return ext4_test_inode_flag(inode, EXT4_INODE_INLINE_DATA) &&
> +	       EXT4_I(inode)->i_inline_off;
> +}
> +
> +int ext4_find_inline_data(struct inode *inode)
> +{
> +	struct ext4_xattr_ibody_find is = {
> +		.s = { .not_found = -ENODATA, },
> +	};
> +	struct ext4_xattr_info i = {
> +		.name_index = EXT4_XATTR_INDEX_SYSTEM_DATA,
> +		.name = EXT4_XATTR_SYSTEM_DATA_NAME,
> +	};
> +	int error;
> +
> +	if (EXT4_I(inode)->i_extra_isize == 0)
> +		return 0;
> +
> +	error = ext4_get_inode_loc(inode, &is.iloc);
> +	if (error)
> +		return error;
> +
> +	error = ext4_xattr_ibody_find(inode, &i, &is);
> +	if (error)
> +		goto out;
> +
> +	if (!is.s.not_found) {
> +		EXT4_I(inode)->i_inline_off = (u16)((void *)is.s.here -
> +					(void *)ext4_raw_inode(&is.iloc));
> +		EXT4_I(inode)->i_inline_size = EXT4_MIN_INLINE_DATA_SIZE +
> +				le32_to_cpu(is.s.here->e_value_size);
> +		ext4_set_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
> +	}
> +out:
> +	brelse(is.iloc.bh);
> +	return error;
> +}
> +
> +static int ext4_read_inline_data(struct inode *inode, void *buffer, unsigned int len,
> +				 struct ext4_iloc *iloc)
> +{
> +	struct ext4_xattr_entry *entry;
> +	struct ext4_xattr_ibody_header *header;
> +	int cp_len = 0;
> +	struct ext4_inode *raw_inode;
> +
> +	if (!len)
> +		return 0;
> +
> +	BUG_ON(len > EXT4_I(inode)->i_inline_size);
> +
> +	cp_len = len < EXT4_MIN_INLINE_DATA_SIZE ?
> +			len : EXT4_MIN_INLINE_DATA_SIZE;
> +
> +	raw_inode = ext4_raw_inode(iloc);
> +	memcpy(buffer, (void *)(raw_inode->i_block), cp_len);
> +
> +	len -= cp_len;
> +	buffer += cp_len;
> +
> +	if (!len)
> +		goto out;
> +
> +	header = IHDR(inode, raw_inode);
> +	entry = (struct ext4_xattr_entry *)((void *)raw_inode +
> +					    EXT4_I(inode)->i_inline_off);
> +	len = min_t(unsigned int, len,
> +		    (unsigned int)le32_to_cpu(entry->e_value_size));
> +
> +	memcpy(buffer,
> +	       (void *)IFIRST(header) + le16_to_cpu(entry->e_value_offs), len);
> +	cp_len += len;
> +
> +out:
> +	return cp_len;
> +}
> +
> +/*
> + * write the buffer to the inline inode.
> + * If 'create' is set, we don't need to do the extra copy in the xattr
> + * value since it is already handled by ext4_xattr_ibody_set. That saves
> + * us one memcpy.
> + */
> +void ext4_write_inline_data(struct inode *inode, struct ext4_iloc *iloc,
> +			    void *buffer, loff_t pos, unsigned int len)
> +{
> +	struct ext4_xattr_entry *entry;
> +	struct ext4_xattr_ibody_header *header;
> +	struct ext4_inode *raw_inode;
> +	int cp_len = 0;
> +
> +	BUG_ON(!EXT4_I(inode)->i_inline_off);
> +	BUG_ON(pos + len > EXT4_I(inode)->i_inline_size);
> +
> +	raw_inode = ext4_raw_inode(iloc);
> +	buffer += pos;
> +
> +	if (pos < EXT4_MIN_INLINE_DATA_SIZE) {
> +		cp_len = pos + len > EXT4_MIN_INLINE_DATA_SIZE ?
> +			 EXT4_MIN_INLINE_DATA_SIZE - pos : len;
> +		memcpy((void *)raw_inode->i_block + pos, buffer, cp_len);
> +
> +		len -= cp_len;
> +		buffer += cp_len;
> +		pos += cp_len;
> +	}
> +
> +	if (!len)
> +		return;
> +
> +	pos -= EXT4_MIN_INLINE_DATA_SIZE;
> +	header = IHDR(inode, raw_inode);
> +	entry = (struct ext4_xattr_entry *)((void *)raw_inode +
> +					    EXT4_I(inode)->i_inline_off);
> +
> +	memcpy((void *)IFIRST(header) + le16_to_cpu(entry->e_value_offs) + pos,
> +	       buffer, len);
> +}
> +
> +static int ext4_create_inline_data(handle_t *handle,
> +				   struct inode *inode, unsigned len)
> +{
> +	int error;
> +	void *value = NULL;
> +	struct ext4_xattr_ibody_find is = {
> +		.s = { .not_found = -ENODATA, },
> +	};
> +	struct ext4_xattr_info i = {
> +		.name_index = EXT4_XATTR_INDEX_SYSTEM_DATA,
> +		.name = EXT4_XATTR_SYSTEM_DATA_NAME,
> +	};
> +
> +	error = ext4_get_inode_loc(inode, &is.iloc);
> +	if (error)
> +		return error;
> +
> +	error = ext4_journal_get_write_access(handle, is.iloc.bh);
> +	if (error)
> +		goto out;
> +
> +	if (len > EXT4_MIN_INLINE_DATA_SIZE) {
> +		value = (void *)empty_zero_page;
> +		len -= EXT4_MIN_INLINE_DATA_SIZE;
> +	} else {
> +		value = "";
> +		len = 0;
> +	}
> +
> +	/* Insert the the xttr entry. */
> +	i.value = value;
> +	i.value_len = len;
> +
> +	error = ext4_xattr_ibody_find(inode, &i, &is);
> +	if (error)
> +		goto out;
> +
> +	BUG_ON(!is.s.not_found);
> +
> +	error = ext4_xattr_ibody_set(handle, inode, &i, &is);
> +	if (error) {
> +		if (error == -ENOSPC)
> +			ext4_clear_inode_state(inode,
> +					       EXT4_STATE_MAY_INLINE_DATA);
> +		goto out;
> +	}
> +
> +	memset((void *)ext4_raw_inode(&is.iloc)->i_block,
> +		0, EXT4_MIN_INLINE_DATA_SIZE);
> +
> +	EXT4_I(inode)->i_inline_off = (u16)((void *)is.s.here -
> +				      (void *)ext4_raw_inode(&is.iloc));
> +	EXT4_I(inode)->i_inline_size = len + EXT4_MIN_INLINE_DATA_SIZE;
> +	ext4_clear_inode_flag(inode, EXT4_INODE_EXTENTS);
> +	ext4_set_inode_flag(inode, EXT4_INODE_INLINE_DATA);
> +	get_bh(is.iloc.bh);
> +	error = ext4_mark_iloc_dirty(handle, inode, &is.iloc);
> +
> +out:
> +	brelse(is.iloc.bh);
> +	return error;
> +}
> +
> +static int ext4_update_inline_data(handle_t *handle, struct inode *inode,
> +				   unsigned int len)
> +{
> +	int error;
> +	void *value = NULL;
> +	struct ext4_xattr_ibody_find is = {
> +		.s = { .not_found = -ENODATA, },
> +	};
> +	struct ext4_xattr_info i = {
> +		.name_index = EXT4_XATTR_INDEX_SYSTEM_DATA,
> +		.name = EXT4_XATTR_SYSTEM_DATA_NAME,
> +	};
> +
> +	/* If the old space is ok, write the data directly. */
> +	if (len <= EXT4_I(inode)->i_inline_size)
> +		return 0;
> +
> +	error = ext4_get_inode_loc(inode, &is.iloc);
> +	if (error)
> +		return error;
> +
> +	error = ext4_xattr_ibody_find(inode, &i, &is);
> +	if (error)
> +		goto out;
> +
> +	BUG_ON(is.s.not_found);
> +
> +	len -= EXT4_MIN_INLINE_DATA_SIZE;
> +	value = kzalloc(len, GFP_NOFS);
> +	if (!value)
> +		goto out;
> +
> +	error = ext4_xattr_ibody_get(inode, i.name_index, i.name,
> +				     value, len);
> +	if (error == -ENODATA)
> +		goto out;
> +
> +	error = ext4_journal_get_write_access(handle, is.iloc.bh);
> +	if (error)
> +		goto out;
> +
> +	/* Update the xttr entry. */
> +	i.value = value;
> +	i.value_len = len;
> +
> +	error = ext4_xattr_ibody_set(handle, inode, &i, &is);
> +	if (error)
> +		goto out;
> +
> +	EXT4_I(inode)->i_inline_off = (u16)((void *)is.s.here -
> +				      (void *)ext4_raw_inode(&is.iloc));
> +	EXT4_I(inode)->i_inline_size = EXT4_MIN_INLINE_DATA_SIZE +
> +				le32_to_cpu(is.s.here->e_value_size);
> +	ext4_set_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
> +	get_bh(is.iloc.bh);
> +	error = ext4_mark_iloc_dirty(handle, inode, &is.iloc);
> +
> +out:
> +	kfree(value);
> +	brelse(is.iloc.bh);
> +	return error;
> +}
> +
> +int ext4_prepare_inline_data(handle_t *handle, struct inode *inode,
> +			     unsigned int len)
> +{
> +	int ret, size;
> +	struct ext4_inode_info *ei = EXT4_I(inode);
> +
> +	if (!ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA))
> +		return -ENOSPC;
> +
> +	size = ext4_get_max_inline_size(inode);
> +	if (size < len)
> +		return -ENOSPC;
> +
> +	down_write(&EXT4_I(inode)->xattr_sem);
> +
> +	if (ei->i_inline_off)
> +		ret = ext4_update_inline_data(handle, inode, len);
> +	else
> +		ret = ext4_create_inline_data(handle, inode, len);
> +
> +	up_write(&EXT4_I(inode)->xattr_sem);
> +
> +	return ret;
> +}
> +
> +static int ext4_destroy_inline_data_nolock(handle_t *handle, struct inode *inode)
> +{
> +	struct ext4_inode_info *ei = EXT4_I(inode);
> +	struct ext4_xattr_ibody_find is = {
> +		.s = { .not_found = 0, },
> +	};
> +	struct ext4_xattr_info i = {
> +		.name_index = EXT4_XATTR_INDEX_SYSTEM_DATA,
> +		.name = EXT4_XATTR_SYSTEM_DATA_NAME,
> +		.value = NULL,
> +		.value_len = 0,
> +	};
> +	int error;
> +
> +	if (!ei->i_inline_off)
> +		return 0;
> +
> +	error = ext4_get_inode_loc(inode, &is.iloc);
> +	if (error)
> +		return error;
> +
> +	error = ext4_xattr_ibody_find(inode, &i, &is);
> +	if (error)
> +		goto out;
> +
> +	error = ext4_journal_get_write_access(handle, is.iloc.bh);
> +	if (error)
> +		goto out;
> +
> +	error = ext4_xattr_ibody_set(handle, inode, &i, &is);
> +	if (error)
> +		goto out;
> +
> +	memset((void *)ext4_raw_inode(&is.iloc)->i_block,
> +		0, EXT4_MIN_INLINE_DATA_SIZE);
> +
> +	if (EXT4_HAS_INCOMPAT_FEATURE(inode->i_sb,
> +				      EXT4_FEATURE_INCOMPAT_EXTENTS)) {
> +		if (S_ISDIR(inode->i_mode) ||
> +		    S_ISREG(inode->i_mode) || S_ISLNK(inode->i_mode)) {
> +			ext4_set_inode_flag(inode, EXT4_INODE_EXTENTS);
> +			ext4_ext_tree_init(handle, inode);
> +		}
> +	}
> +	ext4_clear_inode_flag(inode, EXT4_INODE_INLINE_DATA);
> +
> +	get_bh(is.iloc.bh);
> +	error = ext4_mark_iloc_dirty(handle, inode, &is.iloc);
> +
> +	EXT4_I(inode)->i_inline_off = 0;
> +	EXT4_I(inode)->i_inline_size = 0;
> +	ext4_clear_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
> +out:
> +	brelse(is.iloc.bh);
> +	if (error == -ENODATA)
> +		error = 0;
> +	return error;
> +}
> +
> +int ext4_destroy_inline_data(handle_t *handle, struct inode *inode)
> +{
> +	int ret;
> +
> +	down_write(&EXT4_I(inode)->xattr_sem);
> +	ret = ext4_destroy_inline_data_nolock(handle, inode);
> +	up_write(&EXT4_I(inode)->xattr_sem);
> +
> +	return ret;
> +}
> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
> index 0b2d6c1..b94a388 100644
> --- a/fs/ext4/inode.c
> +++ b/fs/ext4/inode.c
> @@ -3616,8 +3616,10 @@ static inline void ext4_iget_extra_inode(struct inode *inode,
> {
> 	__le32 *magic = (void *)raw_inode +
> 			EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize;
> -	if (*magic == cpu_to_le32(EXT4_XATTR_MAGIC))
> +	if (*magic == cpu_to_le32(EXT4_XATTR_MAGIC)) {
> 		ext4_set_inode_state(inode, EXT4_STATE_XATTR);
> +		ext4_find_inline_data(inode);
> +	}
> }
> 
> struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
> @@ -3653,6 +3655,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
> 	set_nlink(inode, le16_to_cpu(raw_inode->i_links_count));
> 
> 	ext4_clear_state_flags(ei);	/* Only relevant on 32-bit archs */
> +	ei->i_inline_off = 0;
> 	ei->i_dir_start_lookup = 0;
> 	ei->i_dtime = le32_to_cpu(raw_inode->i_dtime);
> 	/* We now have enough fields to check if the inode was active or not.
> diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
> index 07eeaf3..71ef45e 100644
> --- a/fs/ext4/xattr.c
> +++ b/fs/ext4/xattr.c
> @@ -899,30 +899,6 @@ int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
> 	return 0;
> }
> 
> -int ext4_xattr_ibody_inline_set(handle_t *handle, struct inode *inode,
> -				struct ext4_xattr_info *i,
> -				struct ext4_xattr_ibody_find *is)
> -{
> -	struct ext4_xattr_ibody_header *header;
> -	struct ext4_xattr_search *s = &is->s;
> -	int error;
> -
> -	if (EXT4_I(inode)->i_extra_isize == 0)
> -		return -ENOSPC;
> -	error = ext4_xattr_set_entry(i, s);
> -	if (error)
> -		return error;
> -	header = IHDR(inode, ext4_raw_inode(&is->iloc));
> -	if (!IS_LAST_ENTRY(s->first)) {
> -		header->h_magic = cpu_to_le32(EXT4_XATTR_MAGIC);
> -		ext4_set_inode_state(inode, EXT4_STATE_XATTR);
> -	} else {
> -		header->h_magic = cpu_to_le32(0);
> -		ext4_clear_inode_state(inode, EXT4_STATE_XATTR);
> -	}
> -	return 0;
> -}

On further review, this function was added in [PATCH 02/22] without any
users, and is being deleted here immediately.  It should just be removed
from 02/22 entirely.

> int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
> 			 struct ext4_xattr_info *i,
> 			 struct ext4_xattr_ibody_find *is)
> diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
> index 2879761..2fe373f 100644
> --- a/fs/ext4/xattr.h
> +++ b/fs/ext4/xattr.h
> @@ -21,6 +21,7 @@
>  #define EXT4_XATTR_INDEX_TRUSTED		4
>  #define	EXT4_XATTR_INDEX_LUSTRE			5
>  #define EXT4_XATTR_INDEX_SECURITY	        6
> +#define EXT4_XATTR_INDEX_SYSTEM_DATA		7

Is this intended for all "system.*" uses?  Then it should be changed to be
named "SYSTEM" instead of "SYSTEM_DATA":

#define EXT4_XATTR_INDEX_SYSTEM			7


> struct ext4_xattr_header {
> 	__le32	h_magic;	/* magic number for identification */
> @@ -116,13 +117,26 @@ extern const struct xattr_handler *ext4_xattr_handlers[];
> 
> extern int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
> 				 struct ext4_xattr_ibody_find *is);
> -extern int ext4_xattr_ibody_inline_set(handle_t *handle, struct inode *inode,
> -				       struct ext4_xattr_info *i,
> -				       struct ext4_xattr_ibody_find *is);
> +extern int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
> +			        struct ext4_xattr_info *i,
> +			        struct ext4_xattr_ibody_find *is);
> extern int ext4_xattr_ibody_get(struct inode *inode, int name_index,
> 				const char *name,
> 				void *buffer, size_t buffer_size);
> 
> +extern int ext4_has_inline_data(struct inode *inode);
> +extern int ext4_get_inline_size(struct inode *inode);
> +extern int ext4_get_max_inline_size(struct inode *inode);
> +extern int ext4_find_inline_data(struct inode *inode);
> +extern void ext4_write_inline_data(struct inode *inode,
> +				   struct ext4_iloc *iloc,
> +				   void *buffer, loff_t pos,
> +				   unsigned int len);
> +extern int ext4_prepare_inline_data(handle_t *handle, struct inode *inode,
> +				    unsigned int len);
> +extern int ext4_init_inline_data(handle_t *handle, struct inode *inode,
> +				 unsigned int len);
> +extern int ext4_destroy_inline_data(handle_t *handle, struct inode *inode);
> # else  /* CONFIG_EXT4_FS_XATTR */
> 
> static inline int
> @@ -198,6 +212,46 @@ extern int ext4_xattr_ibody_get(struct inode *inode, int name_index,
> 	return -EOPNOTSUPP;
> }
> 
> +static inline int ext4_find_inline_data(struct inode *inode)
> +{
> +	return 0;
> +}
> +
> +static inline int ext4_has_inline_data(struct inode *inode)
> +{
> +	return 0;
> +}
> +
> +static inline int ext4_get_inline_size(struct inode *inode)
> +{
> +	return 0;
> +}
> +
> +static inline int ext4_get_max_inline_size(struct inode *inode)
> +{
> +	return 0;
> +}
> +
> +static inline void ext4_write_inline_data(struct inode *inode,
> +					  struct ext4_iloc *iloc,
> +					  void *buffer, loff_t pos,
> +					  unsigned int len)
> +{
> +	return;
> +}
> +
> +static inline int ext4_init_inline_data(handle_t *handle,
> +					struct inode *inode,
> +					unsigned int len)
> +{
> +	return 0;
> +}
> +
> +static inline int ext4_destroy_inline_data(handle_t *handle,
> +					   struct inode *inode)
> +{
> +	return 0;
> +}
> # endif  /* CONFIG_EXT4_FS_XATTR */
> 
> #ifdef CONFIG_EXT4_FS_SECURITY
> -- 
> 1.7.0.4
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


Cheers, Andreas






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

* Re: [PATCH V4 17/22] ext4: let empty_dir handle inline dir.
  2012-02-20  7:01   ` [PATCH V4 17/22] ext4: let empty_dir handle inline dir Tao Ma
@ 2012-04-07  0:21     ` Andreas Dilger
  2012-04-17  3:55       ` Tao Ma
  0 siblings, 1 reply; 32+ messages in thread
From: Andreas Dilger @ 2012-04-07  0:21 UTC (permalink / raw)
  To: Tao Ma; +Cc: linux-ext4, linux-fsdevel

On 2012-02-20, at 12:01 AM, Tao Ma wrote:
> From: Tao Ma <boyu.mt@taobao.com>
> 
> empty_dir is used when deleting a dir. So it should handle
> inline dir properly.
> 
> Signed-off-by: Tao Ma <boyu.mt@taobao.com>
> ---
> fs/ext4/inline.c |   69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> fs/ext4/namei.c  |    8 ++++++
> fs/ext4/xattr.h  |    6 ++++
> 3 files changed, 83 insertions(+), 0 deletions(-)
> 
> diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
> index 3126ac3..6612ce1 100644
> --- a/fs/ext4/inline.c
> +++ b/fs/ext4/inline.c
> @@ -1436,6 +1436,75 @@ out:
> 	return err;
> }
> 
> +int empty_inline_dir(struct inode *dir, int *has_inline_data)
> +{
> +	int err, inline_size;
> +	struct ext4_iloc iloc;
> +	void *inline_pos;
> +	unsigned int offset;
> +	struct ext4_dir_entry_2 *de, *de1;
> +	int ret = 1;
> +
> +	err = ext4_get_inode_loc(dir, &iloc);
> +	if (err) {
> +		EXT4_ERROR_INODE(dir, "error %d getting inode %lu block",
> +				 err, dir->i_ino);
> +		return 1;
> +	}
> +
> +	down_read(&EXT4_I(dir)->xattr_sem);
> +	if (!ext4_has_inline_data(dir)) {
> +		*has_inline_data = 0;
> +		goto out;
> +	}
> +
> +	de = ext4_get_inline_entry(dir, &iloc, 0,
> +				   &inline_pos, &inline_size);
> +	de1 = ext4_get_inline_entry(dir, &iloc,
> +			ext4_rec_len_from_disk(de->rec_len, inline_size),
> +			&inline_pos, &inline_size);
> +	if (le32_to_cpu(de->inode) != dir->i_ino ||
> +			!le32_to_cpu(de1->inode) ||
> +			strcmp(".", de->name) ||
> +			strcmp("..", de1->name)) {
> +		ext4_warning(dir->i_sb,
> +			     "bad directory (dir #%lu) - no `.' or `..'",
> +			     dir->i_ino);
> +		ret = 1;
> +		goto out;
> +	}
> +
> +	offset = ext4_rec_len_from_disk(de->rec_len, inline_size) +
> +		 ext4_rec_len_from_disk(de1->rec_len, inline_size);
> +	while (offset < dir->i_size) {
> +		de = ext4_get_inline_entry(dir, &iloc, offset,
> +					   &inline_pos, &inline_size);
> +		if (ext4_check_dir_entry(dir, NULL, de,
> +					 iloc.bh, inline_pos,
> +					 inline_size, offset)) {
> +			ext4_warning(dir->i_sb,
> +				     "bad inline directory (dir #%lu) - "
> +				     "inode %u, rec_len %u, name_len %d"
> +				     "inline size %d\n",
> +				     dir->i_ino, le32_to_cpu(de->inode),
> +				     le16_to_cpu(de->rec_len), de->name_len,
> +				     inline_size);
> +			ret = 1;
> +			goto out;
> +		}

Most of the code in this function is duplicated with empty_dir().  Instead of
duplicating everything here, it seems that it is possible to just use the code
in empty_dir() with a few small changes:

- a helper function which reads the first directory block instead of ext4_bread() 
> +		if (le32_to_cpu(de->inode)) {
> +			ret = 0;
> +			goto out;
> +		}
> +		offset += ext4_rec_len_from_disk(de->rec_len, inline_size);
> +	}
> +
> +out:
> +	up_read(&EXT4_I(dir)->xattr_sem);
> +	brelse(iloc.bh);
> +	return ret;
> +}
> +
> int ext4_destroy_inline_data(handle_t *handle, struct inode *inode)
> {
> 	int ret;
> diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
> index 9f7002c..b6b718e 100644
> --- a/fs/ext4/namei.c
> +++ b/fs/ext4/namei.c
> @@ -2015,6 +2015,14 @@ static int empty_dir(struct inode *inode)
> 	struct super_block *sb;
> 	int err = 0;
> 
> +	if (ext4_has_inline_data(inode)) {
> +		int has_inline_data = 1;
> +
> +		err = empty_inline_dir(inode, &has_inline_data);
> +		if (has_inline_data)
> +			return err;
> +	}
> +
> 	sb = inode->i_sb;
> 	if (inode->i_size < EXT4_DIR_REC_LEN(1) + EXT4_DIR_REC_LEN(2) ||
> 	    !(bh = ext4_bread(NULL, inode, 0, 0, &err))) {
> diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
> index 9991143..6ccfa8d 100644
> --- a/fs/ext4/xattr.h
> +++ b/fs/ext4/xattr.h
> @@ -176,6 +176,7 @@ extern int ext4_delete_inline_entry(handle_t *handle,
> 				    struct inode *dir,
> 				    struct ext4_dir_entry_2 *de_del,
> 				    struct buffer_head *bh);
> +extern int empty_inline_dir(struct inode *dir, int *has_inline_data);
> # else  /* CONFIG_EXT4_FS_XATTR */
> 
> static inline int
> @@ -373,6 +374,11 @@ static inline int ext4_delete_inline_entry(handle_t *handle,
> {
> 	return 0;
> }
> +
> +static inline int empty_inline_dir(struct inode *dir, int *has_inline_data)
> +{
> +	return 0;
> +}
> # endif  /* CONFIG_EXT4_FS_XATTR */
> 
> #ifdef CONFIG_EXT4_FS_SECURITY
> -- 
> 1.7.0.4
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


Cheers, Andreas






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

* Re: [PATCH V4 03/22] ext4: Add the basic function for inline data support.
  2012-04-07  0:21     ` Andreas Dilger
@ 2012-04-13  2:59       ` Tao Ma
  0 siblings, 0 replies; 32+ messages in thread
From: Tao Ma @ 2012-04-13  2:59 UTC (permalink / raw)
  To: Andreas Dilger; +Cc: linux-ext4, linux-fsdevel

Hi Andreas,
	thanks for the detailed review and sorry for the delay. I will
integrate all the advices you gave to my next post.

Thanks
Tao
On 04/07/2012 08:21 AM, Andreas Dilger wrote:
> On 2012-02-20, at 12:01 AM, Tao Ma wrote:
> 
>> From: Tao Ma <boyu.mt@taobao.com>
>>
>> Implement inline data with xattr. This idea is inspired by Andreas.
>> So now we use "system.data" to store xattr.
>> For inode_size = 256, currently we uses all the space between i_extra_isize
>> and inode_size. For inode_size > 256, we use half of that space.
>>
>> Signed-off-by: Tao Ma <boyu.mt@taobao.com>
>> ---
>> fs/ext4/Makefile |    2 +-
>> fs/ext4/ext4.h   |    7 +
>> fs/ext4/inline.c |  456 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>> fs/ext4/inode.c  |    5 +-
>> fs/ext4/xattr.c  |   24 ---
>> fs/ext4/xattr.h  |   60 +++++++-
>> 6 files changed, 525 insertions(+), 29 deletions(-)
>> create mode 100644 fs/ext4/inline.c
>>
>> diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
>> index 56fd8f8..e88b7a6 100644
>> --- a/fs/ext4/Makefile
>> +++ b/fs/ext4/Makefile
>> @@ -9,6 +9,6 @@ ext4-y	:= balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \
>> 		ext4_jbd2.o migrate.o mballoc.o block_validity.o move_extent.o \
>> 		mmp.o indirect.o
>>
>> -ext4-$(CONFIG_EXT4_FS_XATTR)		+= xattr.o xattr_user.o xattr_trusted.o
>> +ext4-$(CONFIG_EXT4_FS_XATTR)		+= xattr.o xattr_user.o xattr_trusted.o inline.o
>> ext4-$(CONFIG_EXT4_FS_POSIX_ACL)	+= acl.o
>> ext4-$(CONFIG_EXT4_FS_SECURITY)		+= xattr_security.o
>> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
>> index 513004f..18254c0 100644
>> --- a/fs/ext4/ext4.h
>> +++ b/fs/ext4/ext4.h
>> @@ -371,6 +371,7 @@ struct flex_groups {
>> #define EXT4_EXTENTS_FL			0x00080000 /* Inode uses extents */
>> #define EXT4_EA_INODE_FL	        0x00200000 /* Inode used for large EA */
>> #define EXT4_EOFBLOCKS_FL		0x00400000 /* Blocks allocated beyond EOF */
>> +#define EXT4_INLINE_DATA_FL		0x00800000 /* Inode has inline data. */
> 
> This patch defines EXT4_INLINE_DATA_FL here, but it isn't used anywhere in
> the code.  This would make the code a lot more efficient if this flag were
> checked at the start of ext4_find_inline_data() and skip searching all of
> the xattrs if no INLINE_DATA_FL is set.  That avoids overhead for reading
> every regular inode that does not have inline data.
> 
>> #define EXT4_RESERVED_FL		0x80000000 /* reserved for ext4 lib */
>>
>> #define EXT4_FL_USER_VISIBLE		0x004BDFFF /* User visible flags */
>> @@ -427,6 +428,7 @@ enum {
>> 	EXT4_INODE_EXTENTS	= 19,	/* Inode uses extents */
>> 	EXT4_INODE_EA_INODE	= 21,	/* Inode used for large EA */
>> 	EXT4_INODE_EOFBLOCKS	= 22,	/* Blocks allocated beyond EOF */
>> +	EXT4_INODE_INLINE_DATA	= 23,	/* Data in inode. */
>> 	EXT4_INODE_RESERVED	= 31,	/* reserved for ext4 lib */
>> };
>>
>> @@ -878,6 +880,10 @@ struct ext4_inode_info {
>> 	/* on-disk additional length */
>> 	__u16 i_extra_isize;
>>
>> +	/* Indicate the inline data space. */
>> +	u16 i_inline_off;
>> +	u16 i_inline_size;
>> +
>> #ifdef CONFIG_QUOTA
>> 	/* quota space reservation, managed internally by quota code */
>> 	qsize_t i_reserved_quota;
>> @@ -1307,6 +1313,7 @@ enum {
>> 	EXT4_STATE_DIO_UNWRITTEN,	/* need convert on dio done*/
>> 	EXT4_STATE_NEWENTRY,		/* File just added to dir */
>> 	EXT4_STATE_DELALLOC_RESERVED,	/* blks already reserved for delalloc */
>> +	EXT4_STATE_MAY_INLINE_DATA,	/* may have in-inode data */
>> };
>>
>> #define EXT4_INODE_BIT_FNS(name, field, offset)				\
>> diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
>> new file mode 100644
>> index 0000000..77cedd2
>> --- /dev/null
>> +++ b/fs/ext4/inline.c
>> @@ -0,0 +1,456 @@
>> +/*
>> + * Copyright (c) 2011 Taobao.
>> + * Written by Tao Ma <boyu.mt@taobao.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of version 2.1 of the GNU Lesser General Public License
>> + * as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +#include "ext4_jbd2.h"
>> +#include "ext4.h"
>> +#include "xattr.h"
>> +
>> +#define EXT4_XATTR_SYSTEM_DATA_NAME	"data"
> 
> If this constant name was shorter it would be easier to use, like:
> 
> #define EXT4_XATTR_SYSTEM_DATA "data"
> 
>> +#define EXT4_MIN_INLINE_DATA_SIZE	((sizeof(__le32) * EXT4_N_BLOCKS))
>> +
>> +int ext4_get_inline_size(struct inode *inode)
>> +{
>> +	if (EXT4_I(inode)->i_inline_off)
>> +		return EXT4_I(inode)->i_inline_size;
>> +
>> +	return 0;
>> +}
>> +
>> +static int get_max_inline_xattr_value_size(struct inode *inode,
>> +					   struct ext4_iloc *iloc)
>> +{
>> +	struct ext4_xattr_ibody_header *header;
>> +	struct ext4_xattr_entry *entry;
>> +	struct ext4_inode *raw_inode;
>> +	int free, min_offs;
>> +
>> +	if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR))
>> +		return EXT4_XATTR_SIZE(EXT4_SB(inode->i_sb)->s_inode_size -
>> +			EXT4_GOOD_OLD_INODE_SIZE -
>> +			EXT4_I(inode)->i_extra_isize -
>> +			sizeof(struct ext4_xattr_ibody_header) -
>> +			EXT4_XATTR_LEN(strlen(EXT4_XATTR_SYSTEM_DATA_NAME)) -
>> +			EXT4_XATTR_ROUND - sizeof(__u32));
> 
> Most of this calculation is common with min_offs below.  It would be nice
> to compute it once instead of twice, since this calculation here is
> pretty complex to read and would be simplified with an intermediate value.
> 
> 	min_offs = EXT4_SB(inode->i_sb)->s_inode_size -
> 			EXT4_GOOD_OLD_INODE_SIZE -
> 			EXT4_I(inode)->i_extra_isize -
> 			sizeof(struct ext4_xattr_ibody_header) -
> 			EXT4_XATTR_LEN(strlen(EXT4_XATTR_SYSTEM_DATA));
> 
> 	if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR))
> 		return EXT4_XATTR_SIZE(min_offs -
> 				       EXT4_XATTR_ROUND - sizeof(__u32));
> 
> It isn't obvious what the sizeof(__u32) represents here.  The EXT4_XATTR_ROUND
> is to compensate for the "round_up" factor used in EXT4_XATTR_SIZE(), but it would be useful to document this.
> 
>> +	down_read(&EXT4_I(inode)->xattr_sem);
>> +	raw_inode = ext4_raw_inode(iloc);
>> +	header = IHDR(inode, raw_inode);
>> +	entry = IFIRST(header);
>> +	min_offs = EXT4_SB(inode->i_sb)->s_inode_size -
>> +			EXT4_GOOD_OLD_INODE_SIZE -
>> +			EXT4_I(inode)->i_extra_isize -
>> +			sizeof(struct ext4_xattr_ibody_header);
> 
>> +
>> +	/* Compute min_offs. */
>> +	for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) {
>> +		if (!entry->e_value_block && entry->e_value_size) {
>> +			size_t offs = le16_to_cpu(entry->e_value_offs);
>> +			if (offs < min_offs)
>> +				min_offs = offs;
>> +		}
>> +	}
>> +	free = min_offs -
>> +		((void *)entry - (void *)IFIRST(header)) - sizeof(__u32);
>> +
>> +	if (EXT4_I(inode)->i_inline_off) {
>> +		entry = (struct ext4_xattr_entry *)
>> +			((void *)raw_inode + EXT4_I(inode)->i_inline_off);
>> +
>> +		free += le32_to_cpu(entry->e_value_size);
>> +		goto out;
>> +	}
>> +
>> +	free -= EXT4_XATTR_LEN(strlen(EXT4_XATTR_SYSTEM_DATA_NAME));
>> +
>> +	if (free > EXT4_XATTR_ROUND)
>> +		free = EXT4_XATTR_SIZE(free - EXT4_XATTR_ROUND);
>> +	else
>> +		free = 0;
>> +
>> +out:
>> +	up_read(&EXT4_I(inode)->xattr_sem);
>> +	return free;
>> +}
>> +
>> +/*
>> + * Get the maximum size we now can store in an inode.
>> + * If we can't find the space for a xattr entry, don't use the space
>> + * of the extents since we have no space to indicate the inline data.
>> + */
>> +int ext4_get_max_inline_size(struct inode *inode)
>> +{
>> +	int error, max_inline_size;
>> +	struct ext4_iloc iloc;
>> +
>> +	if (EXT4_I(inode)->i_extra_isize == 0)
>> +		return 0;
>> +
>> +	error = ext4_get_inode_loc(inode, &iloc);
>> +	if (error) {
>> +		ext4_error_inode(inode, __func__, __LINE__, 0,
>> +				 "can't get inode location %lu",
>> +				 inode->i_ino);
>> +		return 0;
>> +	}
>> +
>> +	max_inline_size = get_max_inline_xattr_value_size(inode, &iloc);
>> +
>> +	brelse(iloc.bh);
>> +
>> +	if (!max_inline_size)
>> +		return 0;
>> +
>> +	return max_inline_size + EXT4_MIN_INLINE_DATA_SIZE;
>> +}
>> +
>> +int ext4_has_inline_data(struct inode *inode)
>> +{
>> +	return ext4_test_inode_flag(inode, EXT4_INODE_INLINE_DATA) &&
>> +	       EXT4_I(inode)->i_inline_off;
>> +}
>> +
>> +int ext4_find_inline_data(struct inode *inode)
>> +{
>> +	struct ext4_xattr_ibody_find is = {
>> +		.s = { .not_found = -ENODATA, },
>> +	};
>> +	struct ext4_xattr_info i = {
>> +		.name_index = EXT4_XATTR_INDEX_SYSTEM_DATA,
>> +		.name = EXT4_XATTR_SYSTEM_DATA_NAME,
>> +	};
>> +	int error;
>> +
>> +	if (EXT4_I(inode)->i_extra_isize == 0)
>> +		return 0;
>> +
>> +	error = ext4_get_inode_loc(inode, &is.iloc);
>> +	if (error)
>> +		return error;
>> +
>> +	error = ext4_xattr_ibody_find(inode, &i, &is);
>> +	if (error)
>> +		goto out;
>> +
>> +	if (!is.s.not_found) {
>> +		EXT4_I(inode)->i_inline_off = (u16)((void *)is.s.here -
>> +					(void *)ext4_raw_inode(&is.iloc));
>> +		EXT4_I(inode)->i_inline_size = EXT4_MIN_INLINE_DATA_SIZE +
>> +				le32_to_cpu(is.s.here->e_value_size);
>> +		ext4_set_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
>> +	}
>> +out:
>> +	brelse(is.iloc.bh);
>> +	return error;
>> +}
>> +
>> +static int ext4_read_inline_data(struct inode *inode, void *buffer, unsigned int len,
>> +				 struct ext4_iloc *iloc)
>> +{
>> +	struct ext4_xattr_entry *entry;
>> +	struct ext4_xattr_ibody_header *header;
>> +	int cp_len = 0;
>> +	struct ext4_inode *raw_inode;
>> +
>> +	if (!len)
>> +		return 0;
>> +
>> +	BUG_ON(len > EXT4_I(inode)->i_inline_size);
>> +
>> +	cp_len = len < EXT4_MIN_INLINE_DATA_SIZE ?
>> +			len : EXT4_MIN_INLINE_DATA_SIZE;
>> +
>> +	raw_inode = ext4_raw_inode(iloc);
>> +	memcpy(buffer, (void *)(raw_inode->i_block), cp_len);
>> +
>> +	len -= cp_len;
>> +	buffer += cp_len;
>> +
>> +	if (!len)
>> +		goto out;
>> +
>> +	header = IHDR(inode, raw_inode);
>> +	entry = (struct ext4_xattr_entry *)((void *)raw_inode +
>> +					    EXT4_I(inode)->i_inline_off);
>> +	len = min_t(unsigned int, len,
>> +		    (unsigned int)le32_to_cpu(entry->e_value_size));
>> +
>> +	memcpy(buffer,
>> +	       (void *)IFIRST(header) + le16_to_cpu(entry->e_value_offs), len);
>> +	cp_len += len;
>> +
>> +out:
>> +	return cp_len;
>> +}
>> +
>> +/*
>> + * write the buffer to the inline inode.
>> + * If 'create' is set, we don't need to do the extra copy in the xattr
>> + * value since it is already handled by ext4_xattr_ibody_set. That saves
>> + * us one memcpy.
>> + */
>> +void ext4_write_inline_data(struct inode *inode, struct ext4_iloc *iloc,
>> +			    void *buffer, loff_t pos, unsigned int len)
>> +{
>> +	struct ext4_xattr_entry *entry;
>> +	struct ext4_xattr_ibody_header *header;
>> +	struct ext4_inode *raw_inode;
>> +	int cp_len = 0;
>> +
>> +	BUG_ON(!EXT4_I(inode)->i_inline_off);
>> +	BUG_ON(pos + len > EXT4_I(inode)->i_inline_size);
>> +
>> +	raw_inode = ext4_raw_inode(iloc);
>> +	buffer += pos;
>> +
>> +	if (pos < EXT4_MIN_INLINE_DATA_SIZE) {
>> +		cp_len = pos + len > EXT4_MIN_INLINE_DATA_SIZE ?
>> +			 EXT4_MIN_INLINE_DATA_SIZE - pos : len;
>> +		memcpy((void *)raw_inode->i_block + pos, buffer, cp_len);
>> +
>> +		len -= cp_len;
>> +		buffer += cp_len;
>> +		pos += cp_len;
>> +	}
>> +
>> +	if (!len)
>> +		return;
>> +
>> +	pos -= EXT4_MIN_INLINE_DATA_SIZE;
>> +	header = IHDR(inode, raw_inode);
>> +	entry = (struct ext4_xattr_entry *)((void *)raw_inode +
>> +					    EXT4_I(inode)->i_inline_off);
>> +
>> +	memcpy((void *)IFIRST(header) + le16_to_cpu(entry->e_value_offs) + pos,
>> +	       buffer, len);
>> +}
>> +
>> +static int ext4_create_inline_data(handle_t *handle,
>> +				   struct inode *inode, unsigned len)
>> +{
>> +	int error;
>> +	void *value = NULL;
>> +	struct ext4_xattr_ibody_find is = {
>> +		.s = { .not_found = -ENODATA, },
>> +	};
>> +	struct ext4_xattr_info i = {
>> +		.name_index = EXT4_XATTR_INDEX_SYSTEM_DATA,
>> +		.name = EXT4_XATTR_SYSTEM_DATA_NAME,
>> +	};
>> +
>> +	error = ext4_get_inode_loc(inode, &is.iloc);
>> +	if (error)
>> +		return error;
>> +
>> +	error = ext4_journal_get_write_access(handle, is.iloc.bh);
>> +	if (error)
>> +		goto out;
>> +
>> +	if (len > EXT4_MIN_INLINE_DATA_SIZE) {
>> +		value = (void *)empty_zero_page;
>> +		len -= EXT4_MIN_INLINE_DATA_SIZE;
>> +	} else {
>> +		value = "";
>> +		len = 0;
>> +	}
>> +
>> +	/* Insert the the xttr entry. */
>> +	i.value = value;
>> +	i.value_len = len;
>> +
>> +	error = ext4_xattr_ibody_find(inode, &i, &is);
>> +	if (error)
>> +		goto out;
>> +
>> +	BUG_ON(!is.s.not_found);
>> +
>> +	error = ext4_xattr_ibody_set(handle, inode, &i, &is);
>> +	if (error) {
>> +		if (error == -ENOSPC)
>> +			ext4_clear_inode_state(inode,
>> +					       EXT4_STATE_MAY_INLINE_DATA);
>> +		goto out;
>> +	}
>> +
>> +	memset((void *)ext4_raw_inode(&is.iloc)->i_block,
>> +		0, EXT4_MIN_INLINE_DATA_SIZE);
>> +
>> +	EXT4_I(inode)->i_inline_off = (u16)((void *)is.s.here -
>> +				      (void *)ext4_raw_inode(&is.iloc));
>> +	EXT4_I(inode)->i_inline_size = len + EXT4_MIN_INLINE_DATA_SIZE;
>> +	ext4_clear_inode_flag(inode, EXT4_INODE_EXTENTS);
>> +	ext4_set_inode_flag(inode, EXT4_INODE_INLINE_DATA);
>> +	get_bh(is.iloc.bh);
>> +	error = ext4_mark_iloc_dirty(handle, inode, &is.iloc);
>> +
>> +out:
>> +	brelse(is.iloc.bh);
>> +	return error;
>> +}
>> +
>> +static int ext4_update_inline_data(handle_t *handle, struct inode *inode,
>> +				   unsigned int len)
>> +{
>> +	int error;
>> +	void *value = NULL;
>> +	struct ext4_xattr_ibody_find is = {
>> +		.s = { .not_found = -ENODATA, },
>> +	};
>> +	struct ext4_xattr_info i = {
>> +		.name_index = EXT4_XATTR_INDEX_SYSTEM_DATA,
>> +		.name = EXT4_XATTR_SYSTEM_DATA_NAME,
>> +	};
>> +
>> +	/* If the old space is ok, write the data directly. */
>> +	if (len <= EXT4_I(inode)->i_inline_size)
>> +		return 0;
>> +
>> +	error = ext4_get_inode_loc(inode, &is.iloc);
>> +	if (error)
>> +		return error;
>> +
>> +	error = ext4_xattr_ibody_find(inode, &i, &is);
>> +	if (error)
>> +		goto out;
>> +
>> +	BUG_ON(is.s.not_found);
>> +
>> +	len -= EXT4_MIN_INLINE_DATA_SIZE;
>> +	value = kzalloc(len, GFP_NOFS);
>> +	if (!value)
>> +		goto out;
>> +
>> +	error = ext4_xattr_ibody_get(inode, i.name_index, i.name,
>> +				     value, len);
>> +	if (error == -ENODATA)
>> +		goto out;
>> +
>> +	error = ext4_journal_get_write_access(handle, is.iloc.bh);
>> +	if (error)
>> +		goto out;
>> +
>> +	/* Update the xttr entry. */
>> +	i.value = value;
>> +	i.value_len = len;
>> +
>> +	error = ext4_xattr_ibody_set(handle, inode, &i, &is);
>> +	if (error)
>> +		goto out;
>> +
>> +	EXT4_I(inode)->i_inline_off = (u16)((void *)is.s.here -
>> +				      (void *)ext4_raw_inode(&is.iloc));
>> +	EXT4_I(inode)->i_inline_size = EXT4_MIN_INLINE_DATA_SIZE +
>> +				le32_to_cpu(is.s.here->e_value_size);
>> +	ext4_set_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
>> +	get_bh(is.iloc.bh);
>> +	error = ext4_mark_iloc_dirty(handle, inode, &is.iloc);
>> +
>> +out:
>> +	kfree(value);
>> +	brelse(is.iloc.bh);
>> +	return error;
>> +}
>> +
>> +int ext4_prepare_inline_data(handle_t *handle, struct inode *inode,
>> +			     unsigned int len)
>> +{
>> +	int ret, size;
>> +	struct ext4_inode_info *ei = EXT4_I(inode);
>> +
>> +	if (!ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA))
>> +		return -ENOSPC;
>> +
>> +	size = ext4_get_max_inline_size(inode);
>> +	if (size < len)
>> +		return -ENOSPC;
>> +
>> +	down_write(&EXT4_I(inode)->xattr_sem);
>> +
>> +	if (ei->i_inline_off)
>> +		ret = ext4_update_inline_data(handle, inode, len);
>> +	else
>> +		ret = ext4_create_inline_data(handle, inode, len);
>> +
>> +	up_write(&EXT4_I(inode)->xattr_sem);
>> +
>> +	return ret;
>> +}
>> +
>> +static int ext4_destroy_inline_data_nolock(handle_t *handle, struct inode *inode)
>> +{
>> +	struct ext4_inode_info *ei = EXT4_I(inode);
>> +	struct ext4_xattr_ibody_find is = {
>> +		.s = { .not_found = 0, },
>> +	};
>> +	struct ext4_xattr_info i = {
>> +		.name_index = EXT4_XATTR_INDEX_SYSTEM_DATA,
>> +		.name = EXT4_XATTR_SYSTEM_DATA_NAME,
>> +		.value = NULL,
>> +		.value_len = 0,
>> +	};
>> +	int error;
>> +
>> +	if (!ei->i_inline_off)
>> +		return 0;
>> +
>> +	error = ext4_get_inode_loc(inode, &is.iloc);
>> +	if (error)
>> +		return error;
>> +
>> +	error = ext4_xattr_ibody_find(inode, &i, &is);
>> +	if (error)
>> +		goto out;
>> +
>> +	error = ext4_journal_get_write_access(handle, is.iloc.bh);
>> +	if (error)
>> +		goto out;
>> +
>> +	error = ext4_xattr_ibody_set(handle, inode, &i, &is);
>> +	if (error)
>> +		goto out;
>> +
>> +	memset((void *)ext4_raw_inode(&is.iloc)->i_block,
>> +		0, EXT4_MIN_INLINE_DATA_SIZE);
>> +
>> +	if (EXT4_HAS_INCOMPAT_FEATURE(inode->i_sb,
>> +				      EXT4_FEATURE_INCOMPAT_EXTENTS)) {
>> +		if (S_ISDIR(inode->i_mode) ||
>> +		    S_ISREG(inode->i_mode) || S_ISLNK(inode->i_mode)) {
>> +			ext4_set_inode_flag(inode, EXT4_INODE_EXTENTS);
>> +			ext4_ext_tree_init(handle, inode);
>> +		}
>> +	}
>> +	ext4_clear_inode_flag(inode, EXT4_INODE_INLINE_DATA);
>> +
>> +	get_bh(is.iloc.bh);
>> +	error = ext4_mark_iloc_dirty(handle, inode, &is.iloc);
>> +
>> +	EXT4_I(inode)->i_inline_off = 0;
>> +	EXT4_I(inode)->i_inline_size = 0;
>> +	ext4_clear_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
>> +out:
>> +	brelse(is.iloc.bh);
>> +	if (error == -ENODATA)
>> +		error = 0;
>> +	return error;
>> +}
>> +
>> +int ext4_destroy_inline_data(handle_t *handle, struct inode *inode)
>> +{
>> +	int ret;
>> +
>> +	down_write(&EXT4_I(inode)->xattr_sem);
>> +	ret = ext4_destroy_inline_data_nolock(handle, inode);
>> +	up_write(&EXT4_I(inode)->xattr_sem);
>> +
>> +	return ret;
>> +}
>> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
>> index 0b2d6c1..b94a388 100644
>> --- a/fs/ext4/inode.c
>> +++ b/fs/ext4/inode.c
>> @@ -3616,8 +3616,10 @@ static inline void ext4_iget_extra_inode(struct inode *inode,
>> {
>> 	__le32 *magic = (void *)raw_inode +
>> 			EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize;
>> -	if (*magic == cpu_to_le32(EXT4_XATTR_MAGIC))
>> +	if (*magic == cpu_to_le32(EXT4_XATTR_MAGIC)) {
>> 		ext4_set_inode_state(inode, EXT4_STATE_XATTR);
>> +		ext4_find_inline_data(inode);
>> +	}
>> }
>>
>> struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
>> @@ -3653,6 +3655,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
>> 	set_nlink(inode, le16_to_cpu(raw_inode->i_links_count));
>>
>> 	ext4_clear_state_flags(ei);	/* Only relevant on 32-bit archs */
>> +	ei->i_inline_off = 0;
>> 	ei->i_dir_start_lookup = 0;
>> 	ei->i_dtime = le32_to_cpu(raw_inode->i_dtime);
>> 	/* We now have enough fields to check if the inode was active or not.
>> diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
>> index 07eeaf3..71ef45e 100644
>> --- a/fs/ext4/xattr.c
>> +++ b/fs/ext4/xattr.c
>> @@ -899,30 +899,6 @@ int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
>> 	return 0;
>> }
>>
>> -int ext4_xattr_ibody_inline_set(handle_t *handle, struct inode *inode,
>> -				struct ext4_xattr_info *i,
>> -				struct ext4_xattr_ibody_find *is)
>> -{
>> -	struct ext4_xattr_ibody_header *header;
>> -	struct ext4_xattr_search *s = &is->s;
>> -	int error;
>> -
>> -	if (EXT4_I(inode)->i_extra_isize == 0)
>> -		return -ENOSPC;
>> -	error = ext4_xattr_set_entry(i, s);
>> -	if (error)
>> -		return error;
>> -	header = IHDR(inode, ext4_raw_inode(&is->iloc));
>> -	if (!IS_LAST_ENTRY(s->first)) {
>> -		header->h_magic = cpu_to_le32(EXT4_XATTR_MAGIC);
>> -		ext4_set_inode_state(inode, EXT4_STATE_XATTR);
>> -	} else {
>> -		header->h_magic = cpu_to_le32(0);
>> -		ext4_clear_inode_state(inode, EXT4_STATE_XATTR);
>> -	}
>> -	return 0;
>> -}
> 
> On further review, this function was added in [PATCH 02/22] without any
> users, and is being deleted here immediately.  It should just be removed
> from 02/22 entirely.
> 
>> int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
>> 			 struct ext4_xattr_info *i,
>> 			 struct ext4_xattr_ibody_find *is)
>> diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
>> index 2879761..2fe373f 100644
>> --- a/fs/ext4/xattr.h
>> +++ b/fs/ext4/xattr.h
>> @@ -21,6 +21,7 @@
>>  #define EXT4_XATTR_INDEX_TRUSTED		4
>>  #define	EXT4_XATTR_INDEX_LUSTRE			5
>>  #define EXT4_XATTR_INDEX_SECURITY	        6
>> +#define EXT4_XATTR_INDEX_SYSTEM_DATA		7
> 
> Is this intended for all "system.*" uses?  Then it should be changed to be
> named "SYSTEM" instead of "SYSTEM_DATA":
> 
> #define EXT4_XATTR_INDEX_SYSTEM			7
> 
> 
>> struct ext4_xattr_header {
>> 	__le32	h_magic;	/* magic number for identification */
>> @@ -116,13 +117,26 @@ extern const struct xattr_handler *ext4_xattr_handlers[];
>>
>> extern int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
>> 				 struct ext4_xattr_ibody_find *is);
>> -extern int ext4_xattr_ibody_inline_set(handle_t *handle, struct inode *inode,
>> -				       struct ext4_xattr_info *i,
>> -				       struct ext4_xattr_ibody_find *is);
>> +extern int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
>> +			        struct ext4_xattr_info *i,
>> +			        struct ext4_xattr_ibody_find *is);
>> extern int ext4_xattr_ibody_get(struct inode *inode, int name_index,
>> 				const char *name,
>> 				void *buffer, size_t buffer_size);
>>
>> +extern int ext4_has_inline_data(struct inode *inode);
>> +extern int ext4_get_inline_size(struct inode *inode);
>> +extern int ext4_get_max_inline_size(struct inode *inode);
>> +extern int ext4_find_inline_data(struct inode *inode);
>> +extern void ext4_write_inline_data(struct inode *inode,
>> +				   struct ext4_iloc *iloc,
>> +				   void *buffer, loff_t pos,
>> +				   unsigned int len);
>> +extern int ext4_prepare_inline_data(handle_t *handle, struct inode *inode,
>> +				    unsigned int len);
>> +extern int ext4_init_inline_data(handle_t *handle, struct inode *inode,
>> +				 unsigned int len);
>> +extern int ext4_destroy_inline_data(handle_t *handle, struct inode *inode);
>> # else  /* CONFIG_EXT4_FS_XATTR */
>>
>> static inline int
>> @@ -198,6 +212,46 @@ extern int ext4_xattr_ibody_get(struct inode *inode, int name_index,
>> 	return -EOPNOTSUPP;
>> }
>>
>> +static inline int ext4_find_inline_data(struct inode *inode)
>> +{
>> +	return 0;
>> +}
>> +
>> +static inline int ext4_has_inline_data(struct inode *inode)
>> +{
>> +	return 0;
>> +}
>> +
>> +static inline int ext4_get_inline_size(struct inode *inode)
>> +{
>> +	return 0;
>> +}
>> +
>> +static inline int ext4_get_max_inline_size(struct inode *inode)
>> +{
>> +	return 0;
>> +}
>> +
>> +static inline void ext4_write_inline_data(struct inode *inode,
>> +					  struct ext4_iloc *iloc,
>> +					  void *buffer, loff_t pos,
>> +					  unsigned int len)
>> +{
>> +	return;
>> +}
>> +
>> +static inline int ext4_init_inline_data(handle_t *handle,
>> +					struct inode *inode,
>> +					unsigned int len)
>> +{
>> +	return 0;
>> +}
>> +
>> +static inline int ext4_destroy_inline_data(handle_t *handle,
>> +					   struct inode *inode)
>> +{
>> +	return 0;
>> +}
>> # endif  /* CONFIG_EXT4_FS_XATTR */
>>
>> #ifdef CONFIG_EXT4_FS_SECURITY
>> -- 
>> 1.7.0.4
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
> 
> Cheers, Andreas
> 
> 
> 
> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


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

* Re: [PATCH V4 17/22] ext4: let empty_dir handle inline dir.
  2012-04-07  0:21     ` Andreas Dilger
@ 2012-04-17  3:55       ` Tao Ma
  0 siblings, 0 replies; 32+ messages in thread
From: Tao Ma @ 2012-04-17  3:55 UTC (permalink / raw)
  To: Andreas Dilger; +Cc: linux-ext4, linux-fsdevel

Hi Andreas,
On 04/07/2012 08:21 AM, Andreas Dilger wrote:
> On 2012-02-20, at 12:01 AM, Tao Ma wrote:
>> From: Tao Ma <boyu.mt@taobao.com>
>>
>> empty_dir is used when deleting a dir. So it should handle
>> inline dir properly.
>>
>> Signed-off-by: Tao Ma <boyu.mt@taobao.com>
>> ---
>> fs/ext4/inline.c |   69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>> fs/ext4/namei.c  |    8 ++++++
>> fs/ext4/xattr.h  |    6 ++++
>> 3 files changed, 83 insertions(+), 0 deletions(-)
>>
>> diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
>> index 3126ac3..6612ce1 100644
>> --- a/fs/ext4/inline.c
>> +++ b/fs/ext4/inline.c
>> @@ -1436,6 +1436,75 @@ out:
>> 	return err;
>> }
>>
>> +int empty_inline_dir(struct inode *dir, int *has_inline_data)
>> +{
>> +	int err, inline_size;
>> +	struct ext4_iloc iloc;
>> +	void *inline_pos;
>> +	unsigned int offset;
>> +	struct ext4_dir_entry_2 *de, *de1;
>> +	int ret = 1;
>> +
>> +	err = ext4_get_inode_loc(dir, &iloc);
>> +	if (err) {
>> +		EXT4_ERROR_INODE(dir, "error %d getting inode %lu block",
>> +				 err, dir->i_ino);
>> +		return 1;
>> +	}
>> +
>> +	down_read(&EXT4_I(dir)->xattr_sem);
>> +	if (!ext4_has_inline_data(dir)) {
>> +		*has_inline_data = 0;
>> +		goto out;
>> +	}
>> +
>> +	de = ext4_get_inline_entry(dir, &iloc, 0,
>> +				   &inline_pos, &inline_size);
>> +	de1 = ext4_get_inline_entry(dir, &iloc,
>> +			ext4_rec_len_from_disk(de->rec_len, inline_size),
>> +			&inline_pos, &inline_size);
>> +	if (le32_to_cpu(de->inode) != dir->i_ino ||
>> +			!le32_to_cpu(de1->inode) ||
>> +			strcmp(".", de->name) ||
>> +			strcmp("..", de1->name)) {
>> +		ext4_warning(dir->i_sb,
>> +			     "bad directory (dir #%lu) - no `.' or `..'",
>> +			     dir->i_ino);
>> +		ret = 1;
>> +		goto out;
>> +	}
>> +
>> +	offset = ext4_rec_len_from_disk(de->rec_len, inline_size) +
>> +		 ext4_rec_len_from_disk(de1->rec_len, inline_size);
>> +	while (offset < dir->i_size) {
>> +		de = ext4_get_inline_entry(dir, &iloc, offset,
>> +					   &inline_pos, &inline_size);
>> +		if (ext4_check_dir_entry(dir, NULL, de,
>> +					 iloc.bh, inline_pos,
>> +					 inline_size, offset)) {
>> +			ext4_warning(dir->i_sb,
>> +				     "bad inline directory (dir #%lu) - "
>> +				     "inode %u, rec_len %u, name_len %d"
>> +				     "inline size %d\n",
>> +				     dir->i_ino, le32_to_cpu(de->inode),
>> +				     le16_to_cpu(de->rec_len), de->name_len,
>> +				     inline_size);
>> +			ret = 1;
>> +			goto out;
>> +		}
> 
> Most of the code in this function is duplicated with empty_dir().  Instead of
> duplicating everything here, it seems that it is possible to just use the code
> in empty_dir() with a few small changes:
I just checked the codes and it seems a little bit hard for the
integration. the common codes are just the checker, but there are a lot
of difference between the inline dentry and block dentry iteration. So I
would prefer to leave it as-is.

Thanks
Tao
> 
> - a helper function which reads the first directory block instead of ext4_bread() 
>> +		if (le32_to_cpu(de->inode)) {
>> +			ret = 0;
>> +			goto out;
>> +		}
>> +		offset += ext4_rec_len_from_disk(de->rec_len, inline_size);
>> +	}
>> +
>> +out:
>> +	up_read(&EXT4_I(dir)->xattr_sem);
>> +	brelse(iloc.bh);
>> +	return ret;
>> +}
>> +
>> int ext4_destroy_inline_data(handle_t *handle, struct inode *inode)
>> {
>> 	int ret;
>> diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
>> index 9f7002c..b6b718e 100644
>> --- a/fs/ext4/namei.c
>> +++ b/fs/ext4/namei.c
>> @@ -2015,6 +2015,14 @@ static int empty_dir(struct inode *inode)
>> 	struct super_block *sb;
>> 	int err = 0;
>>
>> +	if (ext4_has_inline_data(inode)) {
>> +		int has_inline_data = 1;
>> +
>> +		err = empty_inline_dir(inode, &has_inline_data);
>> +		if (has_inline_data)
>> +			return err;
>> +	}
>> +
>> 	sb = inode->i_sb;
>> 	if (inode->i_size < EXT4_DIR_REC_LEN(1) + EXT4_DIR_REC_LEN(2) ||
>> 	    !(bh = ext4_bread(NULL, inode, 0, 0, &err))) {
>> diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
>> index 9991143..6ccfa8d 100644
>> --- a/fs/ext4/xattr.h
>> +++ b/fs/ext4/xattr.h
>> @@ -176,6 +176,7 @@ extern int ext4_delete_inline_entry(handle_t *handle,
>> 				    struct inode *dir,
>> 				    struct ext4_dir_entry_2 *de_del,
>> 				    struct buffer_head *bh);
>> +extern int empty_inline_dir(struct inode *dir, int *has_inline_data);
>> # else  /* CONFIG_EXT4_FS_XATTR */
>>
>> static inline int
>> @@ -373,6 +374,11 @@ static inline int ext4_delete_inline_entry(handle_t *handle,
>> {
>> 	return 0;
>> }
>> +
>> +static inline int empty_inline_dir(struct inode *dir, int *has_inline_data)
>> +{
>> +	return 0;
>> +}
>> # endif  /* CONFIG_EXT4_FS_XATTR */
>>
>> #ifdef CONFIG_EXT4_FS_SECURITY
>> -- 
>> 1.7.0.4
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
> 
> Cheers, Andreas
> 
> 
> 
> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


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

end of thread, other threads:[~2012-04-17  3:55 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-02-20  7:00 [PATCH V4 00/22] ext4: Add inline data support Tao Ma
2012-02-20  7:01 ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Tao Ma
2012-02-20  7:01   ` [PATCH V4 02/22] ext4: export inline xattr functions Tao Ma
2012-02-21 22:45     ` Andreas Dilger
2012-02-20  7:01   ` [PATCH V4 03/22] ext4: Add the basic function for inline data support Tao Ma
2012-04-07  0:21     ` Andreas Dilger
2012-04-13  2:59       ` Tao Ma
2012-02-20  7:01   ` [PATCH V4 04/22] ext4: Add read support for inline data Tao Ma
2012-02-20  7:01   ` [PATCH V4 05/22] ext4: Add normal write " Tao Ma
2012-02-20  7:01   ` [PATCH V4 06/22] ext4: Add journalled " Tao Ma
2012-02-20  7:01   ` [PATCH V4 07/22] ext4: Add delalloc " Tao Ma
2012-02-20  7:01   ` [PATCH V4 08/22] ext4: Create a new function ext4_init_new_dir Tao Ma
2012-02-20  7:01   ` [PATCH V4 09/22] ext4: Refactor __ext4_check_dir_entry to accepts start and size Tao Ma
2012-02-20  7:01   ` [PATCH V4 10/22] ext4: Create __ext4_insert_dentry for dir entry insertion Tao Ma
2012-02-20  7:01   ` [PATCH V4 11/22] ext4: let add_dir_entry handle inline data properly Tao Ma
2012-02-20  7:01   ` [PATCH V4 12/22] ext4: Let ext4_readdir handle inline data Tao Ma
2012-02-20  7:01   ` [PATCH V4 13/22] ext4: Create a new function search_dir Tao Ma
2012-02-20  7:01   ` [PATCH V4 14/22] ext4: let ext4_find_entry handle inline data Tao Ma
2012-02-20  7:01   ` [PATCH V4 15/22] ext4: make ext4_delete_entry generic Tao Ma
2012-02-20  7:01   ` [PATCH V4 16/22] ext4: let ext4_delete_entry handle inline data Tao Ma
2012-02-20  7:01   ` [PATCH V4 17/22] ext4: let empty_dir handle inline dir Tao Ma
2012-04-07  0:21     ` Andreas Dilger
2012-04-17  3:55       ` Tao Ma
2012-02-20  7:01   ` [PATCH V4 18/22] ext4: let ext4_rename " Tao Ma
2012-02-20  7:01   ` [PATCH V4 19/22] ext4: Let fiemap work with inline data Tao Ma
2012-02-20  7:01   ` [PATCH V4 20/22] ext4: Evict inline data out if we needs to strore xattr in inode Tao Ma
2012-02-20  7:01   ` [PATCH V4 21/22] ext4: let ext4_truncate handle inline data correctly Tao Ma
2012-02-20  7:01   ` [PATCH V4 22/22] ext4: Enable ext4 inline support Tao Ma
2012-02-21  6:51   ` [PATCH V4 01/22] ext4: Move extra inode read to a new function Andreas Dilger
2012-02-21  8:13     ` Tao Ma
2012-02-21 23:44 ` [PATCH V4 00/22] ext4: Add inline data support Andreas Dilger
2012-02-22  1:34   ` Tao Ma

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.