[Ocfs2-devel] [PATCH 07/15] ocfs2: reserve inline space for extended attribute v2

Mark Fasheh mfasheh at suse.com
Wed Jul 9 18:24:56 PDT 2008


Thanks for breaking this patch up even smaller.

On Fri, Jun 27, 2008 at 03:27:34PM +0800, Tiger Yang wrote:
> This patch reserve some space in inode block for extended attribute.
> That space used to store extent list or inline data.
> 
> Signed-off-by: Tiger Yang <tiger.yang at oracle.com>
> ---
>  fs/ocfs2/alloc.c    |   31 +++++++++++++++++++++++--------
>  fs/ocfs2/ocfs2.h    |    3 +++
>  fs/ocfs2/ocfs2_fs.h |   36 ++++++++++++++++++++++++++++++++++--
>  fs/ocfs2/super.c    |    2 ++
>  4 files changed, 62 insertions(+), 10 deletions(-)
> 
> diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c
> index 3a06271..2df5a7f 100644
> --- a/fs/ocfs2/alloc.c
> +++ b/fs/ocfs2/alloc.c
> @@ -6417,26 +6417,40 @@ out:
>  	return ret;
>  }
>  
> -static void ocfs2_zero_dinode_id2(struct inode *inode, struct ocfs2_dinode *di)
> +static void ocfs2_zero_dinode_id2_with_xattr(struct super_block *sb,
> +					     struct ocfs2_dinode *di,
> +					     int xattrsize)
>  {
> -	unsigned int blocksize = 1 << inode->i_sb->s_blocksize_bits;
> -
> -	memset(&di->id2, 0, blocksize - offsetof(struct ocfs2_dinode, id2));
> +	if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_XATTR_FL)
> +		memset(&di->id2, 0, sb->s_blocksize -
> +				    offsetof(struct ocfs2_dinode, id2) -
> +				    xattrsize);
> +	else
> +		memset(&di->id2, 0, sb->s_blocksize -
> +				    offsetof(struct ocfs2_dinode, id2));
>  }

Wasn't the plan to also store inline xattr size on the inode, when it has
OCFS2_INLINE_XATTR_FL set and only use s_xattr_inline_size when figuring out
if a non-inline-xattr inode has enough space?

struct ocfs2_dinode {
	...
	__u16 i_xattr_inline_size;
	...
};


This way ocfs2_zero_dinode_id2_with_xattr becomes:

static void ocfs2_zero_dinode_id2_with_xattr(struct super_block *sb,
					     struct ocfs2_dinode *di)
{
	unsigned int xattrsize = le16_to_cpu(di->i_xattr_inline_size);

	if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_XATTR_FL)
		memset(&di->id2, 0, sb->s_blocksize -
				    offsetof(struct ocfs2_dinode, id2) -
				    xattrsize);
	else
		memset(&di->id2, 0, sb->s_blocksize -
				    offsetof(struct ocfs2_dinode, id2));
}


Think of this in terms of l_count on the in-inode extent list. We could have
just stored that on the superblock, because lets face it - l_count hasn't
changed for a particular block size, ever. But if we did that, then making
space for inline xattrs wouldn't be as simple as just shrinking l_count
because tons of code which iterates the extent list would assume "oh, every
inode has exactly this many inline extents" instead of just _looking at the
inode_ to see how many inline extents it contains. And really, I think it's
just good practice to have our structures be self-referencial.

It's not enough to just change ocfs2_zero_dinode_id2_with_xattr() by the way
- you need to get this correct for all functions which take inline xattr
size into account. Luckily, your code is written well enough that this
shouldn't be a big deal.


Functions like ocfs2_xattr_has_space_inline() change too, but in a slightly
different way. Those functions deal an inode that does _not_ have inline
xattrs, but instead try to figure out whether they'll fit. In those cases,
you use s_xattr_inline_size as a guideline:


static int ocfs2_xattr_has_space_inline(struct inode *inode,
					struct ocfs2_dinode *di)
{
	struct ocfs2_inode_info *oi = OCFS2_I(inode);
	int xattrsize = OCFS2_SB(inode->i_sb)->s_xattr_inline_size;
	int free;

	if (xattrsize < OCFS2_MIN_XATTR_INLINE_SIZE)
		return 0;

	if (oi->ip_dyn_features & OCFS2_INLINE_DATA_FL) {
		struct ocfs2_inline_data *idata = &di->id2.i_data;
		free = le16_to_cpu(idata->id_count) - le64_to_cpu(di->i_size);
	} else {
		struct ocfs2_extent_list *el = &di->id2.i_list;
		free = (le16_to_cpu(el->l_count) -
			le16_to_cpu(el->l_next_free_rec)) *
			sizeof(struct ocfs2_extent_rec);
	}
	if (free >= xattrsize)
		return 1;

	return 0;
}


And then, of course when you're *setting* inline xattrs for the first time
(generally, this just means "when you set the INLINE_XATTR_FL"), you need to
populate i_xattr_inline_size with something meaningful. I _think_ this
happens in ocfs2_xattr_set_entry() - I haven't gotten to that patch yet:


	if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL) &&
	    (flag & OCFS2_INLINE_XATTR_FL)) {
		/*
		 * Adjust extent record count or inline data size
		 * to reserve space for extended attribute.
		 */
		if (oi->ip_dyn_features & OCFS2_INLINE_DATA_FL) {
			struct ocfs2_inline_data *idata = &di->id2.i_data;
			le16_add_cpu(&idata->id_count, -xattrsize);
		} else {
			struct ocfs2_extent_list *el = &di->id2.i_list;
			le16_add_cpu(&el->l_count, -(xattrsize /
				     sizeof(struct ocfs2_extent_rec)));
		}
		di->i_xattr_inline_size = cpu_to_le16(osb->s_xattr_inline_size);
	}


The test you should use, to figure out what some code should do is ask
yourself "will this break, if the admin changes xattr_inline_size on a
NON-empty file system?" If the answer is "no, i_xattr_inline_size on an
inode with OCFS2_INLINE_XATTR_FL set is independent of the current value of
s_xattr_inline_size" then you got it correct.

Let me know if any of this is unclear!
	--Mark


--
Mark Fasheh



More information about the Ocfs2-devel mailing list