[XFS] Ondisk format extension for extended attributes (attr2).	Basically,
the data/attr forks now grow up/down from either end of the literal area,
rather than dividing the literal area into two chunks and growing both
upward.  Means we can now make much more efficient use of the attribute
space, incl. fitting DMF attributes inline in 256 byte inodes, and large
jumps in dbench3 performance numbers.  It is self enabling, but can be
forced on/off via the attr2/noattr2 mount options.

SGI-PV: 941645
SGI-Modid: xfs-linux:xfs-kern:23835a

Signed-off-by: Nathan Scott <nathans@sgi.com>
diff --git a/fs/xfs/xfs_attr.c b/fs/xfs/xfs_attr.c
index ce9f673..c2939b8 100644
--- a/fs/xfs/xfs_attr.c
+++ b/fs/xfs/xfs_attr.c
@@ -200,7 +200,7 @@
 	return(error);
 }
 
-int
+STATIC int
 xfs_attr_set_int(xfs_inode_t *dp, char *name, int namelen,
 		 char *value, int valuelen, int flags)
 {
@@ -220,12 +220,18 @@
 		return (error);
 
 	/*
+	 * Determine space new attribute will use, and if it would be
+	 * "local" or "remote" (note: local != inline).
+	 */
+	size = xfs_attr_leaf_newentsize(namelen, valuelen,
+					mp->m_sb.sb_blocksize, &local);
+
+	/*
 	 * If the inode doesn't have an attribute fork, add one.
 	 * (inode must not be locked when we call this routine)
 	 */
 	if (XFS_IFORK_Q(dp) == 0) {
-		error = xfs_bmap_add_attrfork(dp, rsvd);
-		if (error)
+		if ((error = xfs_bmap_add_attrfork(dp, size, rsvd)))
 			return(error);
 	}
 
@@ -243,14 +249,9 @@
 	args.firstblock = &firstblock;
 	args.flist = &flist;
 	args.whichfork = XFS_ATTR_FORK;
+	args.addname = 1;
 	args.oknoent = 1;
 
-	/* Determine space new attribute will use, and if it will be inline
-	 * or out of line.
-	 */
-	size = xfs_attr_leaf_newentsize(namelen, valuelen,
-					mp->m_sb.sb_blocksize, &local);
-
 	nblks = XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK);
 	if (local) {
 		if (size > (mp->m_sb.sb_blocksize >> 1)) {
@@ -322,7 +323,7 @@
 		 * Build initial attribute list (if required).
 		 */
 		if (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS)
-			(void)xfs_attr_shortform_create(&args);
+			xfs_attr_shortform_create(&args);
 
 		/*
 		 * Try to add the attr to the attribute list in
@@ -467,7 +468,7 @@
  * Generic handler routine to remove a name from an attribute list.
  * Transitions attribute list from Btree to shortform as necessary.
  */
-int
+STATIC int
 xfs_attr_remove_int(xfs_inode_t *dp, char *name, int namelen, int flags)
 {
 	xfs_da_args_t	args;
@@ -523,7 +524,6 @@
 				      XFS_ATTRRM_LOG_COUNT))) {
 		xfs_trans_cancel(args.trans, 0);
 		return(error);
-
 	}
 
 	xfs_ilock(dp, XFS_ILOCK_EXCL);
@@ -822,7 +822,7 @@
 STATIC int
 xfs_attr_shortform_addname(xfs_da_args_t *args)
 {
-	int newsize, retval;
+	int newsize, forkoff, retval;
 
 	retval = xfs_attr_shortform_lookup(args);
 	if ((args->flags & ATTR_REPLACE) && (retval == ENOATTR)) {
@@ -834,16 +834,18 @@
 		ASSERT(retval == 0);
 	}
 
+	if (args->namelen >= XFS_ATTR_SF_ENTSIZE_MAX ||
+	    args->valuelen >= XFS_ATTR_SF_ENTSIZE_MAX)
+		return(XFS_ERROR(ENOSPC));
+
 	newsize = XFS_ATTR_SF_TOTSIZE(args->dp);
 	newsize += XFS_ATTR_SF_ENTSIZE_BYNAME(args->namelen, args->valuelen);
-	if ((newsize <= XFS_IFORK_ASIZE(args->dp)) &&
-	    (args->namelen < XFS_ATTR_SF_ENTSIZE_MAX) &&
-	    (args->valuelen < XFS_ATTR_SF_ENTSIZE_MAX)) {
-		retval = xfs_attr_shortform_add(args);
-		ASSERT(retval == 0);
-	} else {
+
+	forkoff = xfs_attr_shortform_bytesfit(args->dp, newsize);
+	if (!forkoff)
 		return(XFS_ERROR(ENOSPC));
-	}
+
+	xfs_attr_shortform_add(args, forkoff);
 	return(0);
 }
 
@@ -863,7 +865,7 @@
 {
 	xfs_inode_t *dp;
 	xfs_dabuf_t *bp;
-	int retval, error, committed;
+	int retval, error, committed, forkoff;
 
 	/*
 	 * Read the (only) block in the attribute list in.
@@ -1006,9 +1008,9 @@
 		/*
 		 * If the result is small enough, shrink it all into the inode.
 		 */
-		if (xfs_attr_shortform_allfit(bp, dp)) {
+		if ((forkoff = xfs_attr_shortform_allfit(bp, dp))) {
 			XFS_BMAP_INIT(args->flist, args->firstblock);
-			error = xfs_attr_leaf_to_shortform(bp, args);
+			error = xfs_attr_leaf_to_shortform(bp, args, forkoff);
 			/* bp is gone due to xfs_da_shrink_inode */
 			if (!error) {
 				error = xfs_bmap_finish(&args->trans,
@@ -1060,8 +1062,7 @@
 {
 	xfs_inode_t *dp;
 	xfs_dabuf_t *bp;
-	int committed;
-	int error;
+	int error, committed, forkoff;
 
 	/*
 	 * Remove the attribute.
@@ -1086,9 +1087,9 @@
 	/*
 	 * If the result is small enough, shrink it all into the inode.
 	 */
-	if (xfs_attr_shortform_allfit(bp, dp)) {
+	if ((forkoff = xfs_attr_shortform_allfit(bp, dp))) {
 		XFS_BMAP_INIT(args->flist, args->firstblock);
-		error = xfs_attr_leaf_to_shortform(bp, args);
+		error = xfs_attr_leaf_to_shortform(bp, args, forkoff);
 		/* bp is gone due to xfs_da_shrink_inode */
 		if (!error) {
 			error = xfs_bmap_finish(&args->trans, args->flist,
@@ -1459,7 +1460,7 @@
 	xfs_da_state_blk_t *blk;
 	xfs_inode_t *dp;
 	xfs_dabuf_t *bp;
-	int retval, error, committed;
+	int retval, error, committed, forkoff;
 
 	/*
 	 * Tie a string around our finger to remind us where we are.
@@ -1580,9 +1581,9 @@
 				      bp->data)->hdr.info.magic, ARCH_CONVERT)
 						       == XFS_ATTR_LEAF_MAGIC);
 
-		if (xfs_attr_shortform_allfit(bp, dp)) {
+		if ((forkoff = xfs_attr_shortform_allfit(bp, dp))) {
 			XFS_BMAP_INIT(args->flist, args->firstblock);
-			error = xfs_attr_leaf_to_shortform(bp, args);
+			error = xfs_attr_leaf_to_shortform(bp, args, forkoff);
 			/* bp is gone due to xfs_da_shrink_inode */
 			if (!error) {
 				error = xfs_bmap_finish(&args->trans,
diff --git a/fs/xfs/xfs_attr_leaf.c b/fs/xfs/xfs_attr_leaf.c
index e13eaa5..50598b1 100644
--- a/fs/xfs/xfs_attr_leaf.c
+++ b/fs/xfs/xfs_attr_leaf.c
@@ -118,13 +118,82 @@
 
 
 /*========================================================================
- * External routines when dirsize < XFS_LITINO(mp).
+ * External routines when attribute fork size < XFS_LITINO(mp).
  *========================================================================*/
 
 /*
+ * Query whether the requested number of additional bytes of extended
+ * attribute space will be able to fit inline.
+ * Returns zero if not, else the di_forkoff fork offset to be used in the
+ * literal area for attribute data once the new bytes have been added.
+ *
+ * di_forkoff must be 8 byte aligned, hence is stored as a >>3 value;
+ * special case for dev/uuid inodes, they have fixed size data forks.
+ */
+int
+xfs_attr_shortform_bytesfit(xfs_inode_t *dp, int bytes)
+{
+	int offset;
+	int minforkoff;	/* lower limit on valid forkoff locations */
+	int maxforkoff;	/* upper limit on valid forkoff locations */
+	xfs_mount_t *mp = dp->i_mount;
+
+	if (unlikely(mp->m_flags & XFS_MOUNT_COMPAT_ATTR)) {
+		if (bytes <= XFS_IFORK_ASIZE(dp))
+			return mp->m_attroffset >> 3;
+		return 0;
+	}
+
+	offset = (XFS_LITINO(mp) - bytes) >> 3; /* rounded down */
+
+	switch (dp->i_d.di_format) {
+	case XFS_DINODE_FMT_DEV:
+		minforkoff = roundup(sizeof(xfs_dev_t), 8) >> 3;
+		return (offset >= minforkoff) ? minforkoff : 0;
+	case XFS_DINODE_FMT_UUID:
+		minforkoff = roundup(sizeof(uuid_t), 8) >> 3;
+		return (offset >= minforkoff) ? minforkoff : 0;
+	}
+
+	/* data fork btree root can have at least this many key/ptr pairs */
+	minforkoff = MAX(dp->i_df.if_bytes, XFS_BMDR_SPACE_CALC(MINDBTPTRS));
+	minforkoff = roundup(minforkoff, 8) >> 3;
+
+	/* attr fork btree root can have at least this many key/ptr pairs */
+	maxforkoff = XFS_LITINO(mp) - XFS_BMDR_SPACE_CALC(MINABTPTRS);
+	maxforkoff = maxforkoff >> 3;	/* rounded down */
+
+	if (offset >= minforkoff && offset < maxforkoff)
+		return offset;
+	if (offset >= maxforkoff)
+		return maxforkoff;
+	return 0;
+}
+
+/*
+ * Switch on the ATTR2 superblock bit (implies also FEATURES2)
+ */
+STATIC void
+xfs_sbversion_add_attr2(xfs_mount_t *mp, xfs_trans_t *tp)
+{
+	unsigned long s;
+
+	if (!(mp->m_flags & XFS_MOUNT_COMPAT_ATTR) &&
+	    !(XFS_SB_VERSION_HASATTR2(&mp->m_sb))) {
+		s = XFS_SB_LOCK(mp);
+		if (!XFS_SB_VERSION_HASATTR2(&mp->m_sb)) {
+			XFS_SB_VERSION_ADDATTR2(&mp->m_sb);
+			XFS_SB_UNLOCK(mp, s);
+			xfs_mod_sb(tp, XFS_SB_VERSIONNUM | XFS_SB_FEATURES2);
+		} else
+			XFS_SB_UNLOCK(mp, s);
+	}
+}
+
+/*
  * Create the initial contents of a shortform attribute list.
  */
-int
+void
 xfs_attr_shortform_create(xfs_da_args_t *args)
 {
 	xfs_attr_sf_hdr_t *hdr;
@@ -148,29 +217,37 @@
 	hdr->count = 0;
 	INT_SET(hdr->totsize, ARCH_CONVERT, sizeof(*hdr));
 	xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA);
-	return(0);
 }
 
 /*
  * Add a name/value pair to the shortform attribute list.
  * Overflow from the inode has already been checked for.
  */
-int
-xfs_attr_shortform_add(xfs_da_args_t *args)
+void
+xfs_attr_shortform_add(xfs_da_args_t *args, int forkoff)
 {
 	xfs_attr_shortform_t *sf;
 	xfs_attr_sf_entry_t *sfe;
 	int i, offset, size;
+	xfs_mount_t *mp;
 	xfs_inode_t *dp;
 	xfs_ifork_t *ifp;
 
 	dp = args->dp;
+	mp = dp->i_mount;
+	dp->i_d.di_forkoff = forkoff;
+	dp->i_df.if_ext_max =
+		XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
+	dp->i_afp->if_ext_max =
+		XFS_IFORK_ASIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
+
 	ifp = dp->i_afp;
 	ASSERT(ifp->if_flags & XFS_IFINLINE);
 	sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data;
 	sfe = &sf->list[0];
 	for (i = 0; i < INT_GET(sf->hdr.count, ARCH_CONVERT);
 				sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
+#ifdef DEBUG
 		if (sfe->namelen != args->namelen)
 			continue;
 		if (memcmp(args->name, sfe->nameval, args->namelen) != 0)
@@ -181,7 +258,8 @@
 		if (((args->flags & ATTR_ROOT) != 0) !=
 		    ((sfe->flags & XFS_ATTR_ROOT) != 0))
 			continue;
-		return(XFS_ERROR(EEXIST));
+		ASSERT(0);
+#endif
 	}
 
 	offset = (char *)sfe - (char *)sf;
@@ -200,11 +278,11 @@
 	INT_MOD(sf->hdr.totsize, ARCH_CONVERT, size);
 	xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA);
 
-	return(0);
+	xfs_sbversion_add_attr2(mp, args->trans);
 }
 
 /*
- * Remove a name from the shortform attribute list structure.
+ * Remove an attribute from the shortform attribute list structure.
  */
 int
 xfs_attr_shortform_remove(xfs_da_args_t *args)
@@ -212,17 +290,16 @@
 	xfs_attr_shortform_t *sf;
 	xfs_attr_sf_entry_t *sfe;
 	int base, size=0, end, totsize, i;
+	xfs_mount_t *mp;
 	xfs_inode_t *dp;
 
-	/*
-	 * Remove the attribute.
-	 */
 	dp = args->dp;
+	mp = dp->i_mount;
 	base = sizeof(xfs_attr_sf_hdr_t);
 	sf = (xfs_attr_shortform_t *)dp->i_afp->if_u1.if_data;
 	sfe = &sf->list[0];
-	for (i = 0; i < INT_GET(sf->hdr.count, ARCH_CONVERT);
-				sfe = XFS_ATTR_SF_NEXTENTRY(sfe),
+	end = INT_GET(sf->hdr.count, ARCH_CONVERT);
+	for (i = 0; i < end; sfe = XFS_ATTR_SF_NEXTENTRY(sfe),
 					base += size, i++) {
 		size = XFS_ATTR_SF_ENTSIZE(sfe);
 		if (sfe->namelen != args->namelen)
@@ -237,19 +314,51 @@
 			continue;
 		break;
 	}
-	if (i == INT_GET(sf->hdr.count, ARCH_CONVERT))
+	if (i == end)
 		return(XFS_ERROR(ENOATTR));
 
+	/*
+	 * Fix up the attribute fork data, covering the hole
+	 */
 	end = base + size;
 	totsize = INT_GET(sf->hdr.totsize, ARCH_CONVERT);
-	if (end != totsize) {
-		memmove(&((char *)sf)[base], &((char *)sf)[end],
-							totsize - end);
-	}
+	if (end != totsize)
+		memmove(&((char *)sf)[base], &((char *)sf)[end], totsize - end);
 	INT_MOD(sf->hdr.count, ARCH_CONVERT, -1);
 	INT_MOD(sf->hdr.totsize, ARCH_CONVERT, -size);
-	xfs_idata_realloc(dp, -size, XFS_ATTR_FORK);
-	xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA);
+
+	/*
+	 * Fix up the start offset of the attribute fork
+	 */
+	totsize -= size;
+	if (totsize == sizeof(xfs_attr_sf_hdr_t) && !args->addname) {
+		/*
+		 * Last attribute now removed, revert to original
+		 * inode format making all literal area available
+		 * to the data fork once more.
+		 */
+		xfs_idestroy_fork(dp, XFS_ATTR_FORK);
+		dp->i_d.di_forkoff = 0;
+		dp->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS;
+		ASSERT(dp->i_d.di_anextents == 0);
+		ASSERT(dp->i_afp == NULL);
+		dp->i_df.if_ext_max =
+			XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
+		xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE);
+	} else {
+		xfs_idata_realloc(dp, -size, XFS_ATTR_FORK);
+		dp->i_d.di_forkoff = xfs_attr_shortform_bytesfit(dp, totsize);
+		ASSERT(dp->i_d.di_forkoff);
+		ASSERT(totsize > sizeof(xfs_attr_sf_hdr_t) || args->addname);
+		dp->i_afp->if_ext_max =
+			XFS_IFORK_ASIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
+		dp->i_df.if_ext_max =
+			XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
+		xfs_trans_log_inode(args->trans, dp,
+					XFS_ILOG_CORE | XFS_ILOG_ADATA);
+	}
+
+	xfs_sbversion_add_attr2(mp, args->trans);
 
 	return(0);
 }
@@ -649,14 +758,16 @@
 				+ name_loc->namelen
 				+ INT_GET(name_loc->valuelen, ARCH_CONVERT);
 	}
-	return( bytes < XFS_IFORK_ASIZE(dp) );
+	if (bytes == sizeof(struct xfs_attr_sf_hdr))
+		return(-1);
+	return(xfs_attr_shortform_bytesfit(dp, bytes));
 }
 
 /*
  * Convert a leaf attribute list to shortform attribute list
  */
 int
-xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t *args)
+xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t *args, int forkoff)
 {
 	xfs_attr_leafblock_t *leaf;
 	xfs_attr_leaf_entry_t *entry;
@@ -683,9 +794,25 @@
 	error = xfs_da_shrink_inode(args, 0, bp);
 	if (error)
 		goto out;
-	error = xfs_attr_shortform_create(args);
-	if (error)
+
+	if (forkoff == -1) {
+		/*
+		 * Last attribute was removed, revert to original
+		 * inode format making all literal area available
+		 * to the data fork once more.
+		 */
+		xfs_idestroy_fork(dp, XFS_ATTR_FORK);
+		dp->i_d.di_forkoff = 0;
+		dp->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS;
+		ASSERT(dp->i_d.di_anextents == 0);
+		ASSERT(dp->i_afp == NULL);
+		dp->i_df.if_ext_max =
+			XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
+		xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE);
 		goto out;
+	}
+
+	xfs_attr_shortform_create(args);
 
 	/*
 	 * Copy the attributes
@@ -713,7 +840,7 @@
 		nargs.hashval = INT_GET(entry->hashval, ARCH_CONVERT);
 		nargs.flags = (entry->flags & XFS_ATTR_SECURE) ? ATTR_SECURE :
 			      ((entry->flags & XFS_ATTR_ROOT) ? ATTR_ROOT : 0);
-		xfs_attr_shortform_add(&nargs);
+		xfs_attr_shortform_add(&nargs, forkoff);
 	}
 	error = 0;
 
diff --git a/fs/xfs/xfs_attr_leaf.h b/fs/xfs/xfs_attr_leaf.h
index b99f049..326802f 100644
--- a/fs/xfs/xfs_attr_leaf.h
+++ b/fs/xfs/xfs_attr_leaf.h
@@ -238,23 +238,25 @@
  *========================================================================*/
 
 /*
- * Internal routines when dirsize < XFS_LITINO(mp).
+ * Internal routines when attribute fork size < XFS_LITINO(mp).
  */
-int	xfs_attr_shortform_create(struct xfs_da_args *args);
-int	xfs_attr_shortform_add(struct xfs_da_args *add);
+void	xfs_attr_shortform_create(struct xfs_da_args *args);
+void	xfs_attr_shortform_add(struct xfs_da_args *args, int forkoff);
 int	xfs_attr_shortform_lookup(struct xfs_da_args *args);
 int	xfs_attr_shortform_getvalue(struct xfs_da_args *args);
 int	xfs_attr_shortform_to_leaf(struct xfs_da_args *args);
-int	xfs_attr_shortform_remove(struct xfs_da_args *remove);
+int	xfs_attr_shortform_remove(struct xfs_da_args *args);
 int	xfs_attr_shortform_list(struct xfs_attr_list_context *context);
 int	xfs_attr_shortform_allfit(struct xfs_dabuf *bp, struct xfs_inode *dp);
+int	xfs_attr_shortform_bytesfit(xfs_inode_t *dp, int bytes);
+
 
 /*
- * Internal routines when dirsize == XFS_LBSIZE(mp).
+ * Internal routines when attribute fork size == XFS_LBSIZE(mp).
  */
 int	xfs_attr_leaf_to_node(struct xfs_da_args *args);
 int	xfs_attr_leaf_to_shortform(struct xfs_dabuf *bp,
-					  struct xfs_da_args *args);
+				   struct xfs_da_args *args, int forkoff);
 int	xfs_attr_leaf_clearflag(struct xfs_da_args *args);
 int	xfs_attr_leaf_setflag(struct xfs_da_args *args);
 int	xfs_attr_leaf_flipflags(xfs_da_args_t *args);
diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c
index 26645d2..3e01353 100644
--- a/fs/xfs/xfs_bmap.c
+++ b/fs/xfs/xfs_bmap.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
@@ -62,6 +62,7 @@
 #include "xfs_error.h"
 #include "xfs_da_btree.h"
 #include "xfs_dir_leaf.h"
+#include "xfs_attr_leaf.h"
 #include "xfs_bit.h"
 #include "xfs_rw.h"
 #include "xfs_quota.h"
@@ -3337,6 +3338,29 @@
 }
 
 /*
+ * Helper routine to reset inode di_forkoff field when switching
+ * attribute fork from local to extent format - we reset it where
+ * possible to make space available for inline data fork extents.
+ */
+STATIC void
+xfs_bmap_forkoff_reset(
+	xfs_mount_t	*mp,
+	xfs_inode_t	*ip,
+	int		whichfork)
+{
+	if (whichfork == XFS_ATTR_FORK &&
+	    (ip->i_d.di_format != XFS_DINODE_FMT_DEV) &&
+	    (ip->i_d.di_format != XFS_DINODE_FMT_UUID) &&
+	    ((mp->m_attroffset >> 3) > ip->i_d.di_forkoff)) {
+		ip->i_d.di_forkoff = mp->m_attroffset >> 3;
+		ip->i_df.if_ext_max = XFS_IFORK_DSIZE(ip) /
+					(uint)sizeof(xfs_bmbt_rec_t);
+		ip->i_afp->if_ext_max = XFS_IFORK_ASIZE(ip) /
+					(uint)sizeof(xfs_bmbt_rec_t);
+	}
+}
+
+/*
  * Convert a local file to an extents file.
  * This code is out of bounds for data forks of regular files,
  * since the file data needs to get logged so things will stay consistent.
@@ -3403,6 +3427,7 @@
 		memcpy((char *)XFS_BUF_PTR(bp), ifp->if_u1.if_data,
 			ifp->if_bytes);
 		xfs_trans_log_buf(tp, bp, 0, ifp->if_bytes - 1);
+		xfs_bmap_forkoff_reset(args.mp, ip, whichfork);
 		xfs_idata_realloc(ip, -ifp->if_bytes, whichfork);
 		xfs_iext_realloc(ip, 1, whichfork);
 		ep = ifp->if_u1.if_extents;
@@ -3413,8 +3438,10 @@
 		XFS_TRANS_MOD_DQUOT_BYINO(args.mp, tp, ip,
 			XFS_TRANS_DQ_BCOUNT, 1L);
 		flags |= XFS_ILOG_FEXT(whichfork);
-	} else
+	} else {
 		ASSERT(XFS_IFORK_NEXTENTS(ip, whichfork) == 0);
+		xfs_bmap_forkoff_reset(ip->i_mount, ip, whichfork);
+	}
 	ifp->if_flags &= ~XFS_IFINLINE;
 	ifp->if_flags |= XFS_IFEXTENTS;
 	XFS_IFORK_FMT_SET(ip, whichfork, XFS_DINODE_FMT_EXTENTS);
@@ -3796,22 +3823,24 @@
 int						/* error code */
 xfs_bmap_add_attrfork(
 	xfs_inode_t		*ip,		/* incore inode pointer */
-	int			rsvd)		/* OK to allocated reserved blocks in trans */
+	int			size,		/* space new attribute needs */
+	int			rsvd)		/* xact may use reserved blks */
 {
-	int			blks;		/* space reservation */
-	int			committed;	/* xaction was committed */
-	int			error;		/* error return value */
 	xfs_fsblock_t		firstblock;	/* 1st block/ag allocated */
 	xfs_bmap_free_t		flist;		/* freed extent list */
-	int			logflags;	/* logging flags */
 	xfs_mount_t		*mp;		/* mount structure */
-	unsigned long		s;		/* spinlock spl value */
 	xfs_trans_t		*tp;		/* transaction pointer */
+	unsigned long		s;		/* spinlock spl value */
+	int			blks;		/* space reservation */
+	int			version = 1;	/* superblock attr version */
+	int			committed;	/* xaction was committed */
+	int			logflags;	/* logging flags */
+	int			error;		/* error return value */
 
+	ASSERT(XFS_IFORK_Q(ip) == 0);
 	ASSERT(ip->i_df.if_ext_max ==
 	       XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t));
-	if (XFS_IFORK_Q(ip))
-		return 0;
+
 	mp = ip->i_mount;
 	ASSERT(!XFS_NOT_DQATTACHED(mp, ip));
 	tp = xfs_trans_alloc(mp, XFS_TRANS_ADDAFORK);
@@ -3853,7 +3882,11 @@
 	case XFS_DINODE_FMT_LOCAL:
 	case XFS_DINODE_FMT_EXTENTS:
 	case XFS_DINODE_FMT_BTREE:
-		ip->i_d.di_forkoff = mp->m_attroffset >> 3;
+		ip->i_d.di_forkoff = xfs_attr_shortform_bytesfit(ip, size);
+		if (!ip->i_d.di_forkoff)
+			ip->i_d.di_forkoff = mp->m_attroffset >> 3;
+		else if (!(mp->m_flags & XFS_MOUNT_COMPAT_ATTR))
+			version = 2;
 		break;
 	default:
 		ASSERT(0);
@@ -3890,12 +3923,21 @@
 		xfs_trans_log_inode(tp, ip, logflags);
 	if (error)
 		goto error2;
-	if (!XFS_SB_VERSION_HASATTR(&mp->m_sb)) {
+	if (!XFS_SB_VERSION_HASATTR(&mp->m_sb) ||
+	   (!XFS_SB_VERSION_HASATTR2(&mp->m_sb) && version == 2)) {
+		logflags = 0;
 		s = XFS_SB_LOCK(mp);
 		if (!XFS_SB_VERSION_HASATTR(&mp->m_sb)) {
 			XFS_SB_VERSION_ADDATTR(&mp->m_sb);
+			logflags |= XFS_SB_VERSIONNUM;
+		}
+		if (!XFS_SB_VERSION_HASATTR2(&mp->m_sb) && version == 2) {
+			XFS_SB_VERSION_ADDATTR2(&mp->m_sb);
+			logflags |= (XFS_SB_VERSIONNUM | XFS_SB_FEATURES2);
+		}
+		if (logflags) {
 			XFS_SB_UNLOCK(mp, s);
-			xfs_mod_sb(tp, XFS_SB_VERSIONNUM);
+			xfs_mod_sb(tp, logflags);
 		} else
 			XFS_SB_UNLOCK(mp, s);
 	}
@@ -3988,13 +4030,19 @@
 	 * (a signed 32-bit number, xfs_extnum_t), or by di_anextents
 	 * (a signed 16-bit number, xfs_aextnum_t).
 	 */
-	maxleafents = (whichfork == XFS_DATA_FORK) ? MAXEXTNUM : MAXAEXTNUM;
+	if (whichfork == XFS_DATA_FORK) {
+		maxleafents = MAXEXTNUM;
+		sz = (mp->m_flags & XFS_MOUNT_COMPAT_ATTR) ?
+			mp->m_attroffset : XFS_BMDR_SPACE_CALC(MINDBTPTRS);
+	} else {
+		maxleafents = MAXAEXTNUM;
+		sz = (mp->m_flags & XFS_MOUNT_COMPAT_ATTR) ?
+			mp->m_sb.sb_inodesize - mp->m_attroffset :
+			XFS_BMDR_SPACE_CALC(MINABTPTRS);
+	}
+	maxrootrecs = (int)XFS_BTREE_BLOCK_MAXRECS(sz, xfs_bmdr, 0);
 	minleafrecs = mp->m_bmap_dmnr[0];
 	minnoderecs = mp->m_bmap_dmnr[1];
-	sz = (whichfork == XFS_DATA_FORK) ?
-		mp->m_attroffset :
-		mp->m_sb.sb_inodesize - mp->m_attroffset;
-	maxrootrecs = (int)XFS_BTREE_BLOCK_MAXRECS(sz, xfs_bmdr, 0);
 	maxblocks = (maxleafents + minleafrecs - 1) / minleafrecs;
 	for (level = 1; maxblocks > 1; level++) {
 		if (maxblocks <= maxrootrecs)
diff --git a/fs/xfs/xfs_bmap.h b/fs/xfs/xfs_bmap.h
index e6d22ec..e42d1b7 100644
--- a/fs/xfs/xfs_bmap.h
+++ b/fs/xfs/xfs_bmap.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2003 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
@@ -156,7 +156,8 @@
 int					/* error code */
 xfs_bmap_add_attrfork(
 	struct xfs_inode	*ip,	/* incore inode pointer */
-	int					rsvd);	/* flag for reserved block allocation */
+	int			size,	/* space needed for new attribute */
+	int			rsvd);	/* flag for reserved block allocation */
 
 /*
  * Add the extent to the list of extents to be free at transaction end.
diff --git a/fs/xfs/xfs_dir.c b/fs/xfs/xfs_dir.c
index ba30bc7..53787f3 100644
--- a/fs/xfs/xfs_dir.c
+++ b/fs/xfs/xfs_dir.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2001 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
@@ -192,11 +192,23 @@
 	uint shortcount, leafcount, count;
 
 	mp->m_dirversion = 1;
-	shortcount = (mp->m_attroffset - (uint)sizeof(xfs_dir_sf_hdr_t)) /
-		     (uint)sizeof(xfs_dir_sf_entry_t);
-	leafcount = (XFS_LBSIZE(mp) - (uint)sizeof(xfs_dir_leaf_hdr_t)) /
-		    ((uint)sizeof(xfs_dir_leaf_entry_t) +
-		     (uint)sizeof(xfs_dir_leaf_name_t));
+	if (mp->m_flags & XFS_MOUNT_COMPAT_ATTR) {
+		shortcount = (mp->m_attroffset -
+				(uint)sizeof(xfs_dir_sf_hdr_t)) /
+				 (uint)sizeof(xfs_dir_sf_entry_t);
+		leafcount = (XFS_LBSIZE(mp) -
+				(uint)sizeof(xfs_dir_leaf_hdr_t)) /
+				 ((uint)sizeof(xfs_dir_leaf_entry_t) +
+				  (uint)sizeof(xfs_dir_leaf_name_t));
+	} else {
+		shortcount = (XFS_BMDR_SPACE_CALC(MINABTPTRS) -
+			      (uint)sizeof(xfs_dir_sf_hdr_t)) /
+			       (uint)sizeof(xfs_dir_sf_entry_t);
+		leafcount = (XFS_LBSIZE(mp) -
+			    (uint)sizeof(xfs_dir_leaf_hdr_t)) /
+			     ((uint)sizeof(xfs_dir_leaf_entry_t) +
+			      (uint)sizeof(xfs_dir_leaf_name_t));
+	}
 	count = shortcount > leafcount ? shortcount : leafcount;
 	mp->m_dircook_elog = xfs_da_log2_roundup(count + 1);
 	ASSERT(mp->m_dircook_elog <= mp->m_sb.sb_blocklog);
diff --git a/fs/xfs/xfs_fs.h b/fs/xfs/xfs_fs.h
index 095af0a..7bf2e92 100644
--- a/fs/xfs/xfs_fs.h
+++ b/fs/xfs/xfs_fs.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1995-2003 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 1995-2005 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * 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
@@ -251,6 +251,7 @@
 #define XFS_FSOP_GEOM_FLAGS_DIRV2	0x0080	/* directory version 2	*/
 #define XFS_FSOP_GEOM_FLAGS_LOGV2	0x0100	/* log format version 2	*/
 #define XFS_FSOP_GEOM_FLAGS_SECTOR	0x0200	/* sector sizes >1BB	*/
+#define XFS_FSOP_GEOM_FLAGS_ATTR2	0x0400	/* inline attributes rework */
 
 
 /*
diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index ca535d6..67522f2 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2002 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
@@ -110,7 +110,9 @@
 			(XFS_SB_VERSION_HASDIRV2(&mp->m_sb) ?
 				XFS_FSOP_GEOM_FLAGS_DIRV2 : 0) |
 			(XFS_SB_VERSION_HASSECTOR(&mp->m_sb) ?
-				XFS_FSOP_GEOM_FLAGS_SECTOR : 0);
+				XFS_FSOP_GEOM_FLAGS_SECTOR : 0) |
+			(XFS_SB_VERSION_HASATTR2(&mp->m_sb) ?
+				XFS_FSOP_GEOM_FLAGS_ATTR2 : 0);
 		geo->logsectsize = XFS_SB_VERSION_HASSECTOR(&mp->m_sb) ?
 				mp->m_sb.sb_logsectsize : BBSIZE;
 		geo->rtsectsize = mp->m_sb.sb_blocksize;
diff --git a/fs/xfs/xfs_macros.c b/fs/xfs/xfs_macros.c
index 698c2cd..c715da1 100644
--- a/fs/xfs/xfs_macros.c
+++ b/fs/xfs/xfs_macros.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2003 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
@@ -1995,6 +1995,14 @@
 }
 #endif
 
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_ADDATTR2)
+void
+xfs_sb_version_addattr2(xfs_sb_t *sbp)
+{
+	XFS_SB_VERSION_ADDATTR2(sbp);
+}
+#endif
+
 #if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_HASALIGN)
 int
 xfs_sb_version_hasalign(xfs_sb_t *sbp)
@@ -2139,3 +2147,10 @@
 }
 #endif
 
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_HASATTR2)
+int
+xfs_sb_version_hasattr2(xfs_sb_t *sbp)
+{
+	return XFS_SB_VERSION_HASATTR2(sbp);
+}
+#endif
diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c
index 12f10d5..a93ef80 100644
--- a/fs/xfs/xfs_mount.c
+++ b/fs/xfs/xfs_mount.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
@@ -584,12 +584,13 @@
 	ASSERT(sbp->sb_inodesize >= 256 && sbp->sb_inodesize <= 2048);
 	switch (sbp->sb_inodesize) {
 	case 256:
-		mp->m_attroffset = XFS_LITINO(mp) - XFS_BMDR_SPACE_CALC(2);
+		mp->m_attroffset = XFS_LITINO(mp) -
+				   XFS_BMDR_SPACE_CALC(MINABTPTRS);
 		break;
 	case 512:
 	case 1024:
 	case 2048:
-		mp->m_attroffset = XFS_BMDR_SPACE_CALC(12);
+		mp->m_attroffset = XFS_BMDR_SPACE_CALC(6 * MINABTPTRS);
 		break;
 	default:
 		ASSERT(0);
diff --git a/fs/xfs/xfs_sb.h b/fs/xfs/xfs_sb.h
index ad090a8..01c5a5f 100644
--- a/fs/xfs/xfs_sb.h
+++ b/fs/xfs/xfs_sb.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2001 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
@@ -72,7 +72,8 @@
 	 XFS_SB_VERSION_DALIGNBIT | \
 	 XFS_SB_VERSION_SHAREDBIT | \
 	 XFS_SB_VERSION_LOGV2BIT | \
-	 XFS_SB_VERSION_SECTORBIT)
+	 XFS_SB_VERSION_SECTORBIT | \
+	 XFS_SB_VERSION_MOREBITSBIT)
 #define	XFS_SB_VERSION_OKSASHBITS	\
 	(XFS_SB_VERSION_NUMBITS | \
 	 XFS_SB_VERSION_REALFBITS | \
@@ -103,12 +104,15 @@
  */
 #define XFS_SB_VERSION2_REALFBITS	0x00ffffff	/* Mask: features */
 #define XFS_SB_VERSION2_RESERVED1BIT	0x00000001
+#define XFS_SB_VERSION2_RESERVED2BIT	0x00000002
+#define XFS_SB_VERSION2_RESERVED4BIT	0x00000004
+#define XFS_SB_VERSION2_ATTR2BIT	0x00000008	/* Inline attr rework */
 #define XFS_SB_VERSION2_SASHFBITS	0xff000000	/* Mask: features that
 							   require changing
 							   PROM and SASH */
 
 #define	XFS_SB_VERSION2_OKREALFBITS	\
-	(0)
+	(XFS_SB_VERSION2_ATTR2BIT)
 #define	XFS_SB_VERSION2_OKSASHFBITS	\
 	(0)
 #define XFS_SB_VERSION2_OKREALBITS	\
@@ -118,8 +122,7 @@
 /*
  * mkfs macro to set up sb_features2 word
  */
-#define	XFS_SB_VERSION2_MKFS(xyz)	\
-	((xyz) ? 0 : 0)
+#define	XFS_SB_VERSION2_MKFS(resvd1, sbcntr)	0
 
 typedef struct xfs_sb
 {
@@ -176,7 +179,7 @@
 	__uint8_t	sb_logsectlog;	/* log2 of the log sector size */
 	__uint16_t	sb_logsectsize;	/* sector size for the log, bytes */
 	__uint32_t	sb_logsunit;	/* stripe unit size for the log */
-	__uint32_t	sb_features2;	/* additonal feature bits */
+	__uint32_t	sb_features2;	/* additional feature bits */
 } xfs_sb_t;
 
 /*
@@ -216,12 +219,15 @@
 #define XFS_SB_SHARED_VN	XFS_SB_MVAL(SHARED_VN)
 #define XFS_SB_UNIT		XFS_SB_MVAL(UNIT)
 #define XFS_SB_WIDTH		XFS_SB_MVAL(WIDTH)
+#define XFS_SB_FEATURES2	XFS_SB_MVAL(FEATURES2)
 #define	XFS_SB_NUM_BITS		((int)XFS_SBS_FIELDCOUNT)
 #define	XFS_SB_ALL_BITS		((1LL << XFS_SB_NUM_BITS) - 1)
 #define	XFS_SB_MOD_BITS		\
 	(XFS_SB_UUID | XFS_SB_ROOTINO | XFS_SB_RBMINO | XFS_SB_RSUMINO | \
 	 XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO | XFS_SB_GQUOTINO | \
-	 XFS_SB_QFLAGS | XFS_SB_SHARED_VN | XFS_SB_UNIT | XFS_SB_WIDTH)
+	 XFS_SB_QFLAGS | XFS_SB_SHARED_VN | XFS_SB_UNIT | XFS_SB_WIDTH | \
+	 XFS_SB_FEATURES2)
+
 
 /*
  * Misc. Flags - warning - these will be cleared by xfs_repair unless
@@ -500,13 +506,31 @@
 /*
  * sb_features2 bit version macros.
  *
- * For example, for a bit defined as XFS_SB_VERSION2_YBIT, has a macro:
+ * For example, for a bit defined as XFS_SB_VERSION2_FUNBIT, has a macro:
  *
- * SB_VERSION_HASYBIT(xfs_sb_t *sbp)
+ * SB_VERSION_HASFUNBIT(xfs_sb_t *sbp)
  *	((XFS_SB_VERSION_HASMOREBITS(sbp) &&
- *	 ((sbp)->sb_versionnum & XFS_SB_VERSION2_YBIT)
+ *	 ((sbp)->sb_features2 & XFS_SB_VERSION2_FUNBIT)
  */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASATTR2)
+int xfs_sb_version_hasattr2(xfs_sb_t *sbp);
+#define XFS_SB_VERSION_HASATTR2(sbp)	xfs_sb_version_hasattr2(sbp)
+#else
+#define XFS_SB_VERSION_HASATTR2(sbp)	\
+	((XFS_SB_VERSION_HASMOREBITS(sbp)) &&	\
+	 ((sbp)->sb_features2 & XFS_SB_VERSION2_ATTR2BIT))
+#endif
 
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_ADDATTR2)
+void xfs_sb_version_addattr2(xfs_sb_t *sbp);
+#define XFS_SB_VERSION_ADDATTR2(sbp)	xfs_sb_version_addattr2(sbp)
+#else
+#define XFS_SB_VERSION_ADDATTR2(sbp)	\
+	((sbp)->sb_versionnum =	\
+		((sbp)->sb_versionnum | XFS_SB_VERSION_MOREBITSBIT),	\
+	((sbp)->sb_features2 =	\
+		((sbp)->sb_features2 | XFS_SB_VERSION2_ATTR2BIT)))
+#endif
 /*
  * end of superblock version macros
  */
diff --git a/fs/xfs/xfs_types.h b/fs/xfs/xfs_types.h
index 16f5371..33a888e 100644
--- a/fs/xfs/xfs_types.h
+++ b/fs/xfs/xfs_types.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2003 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.  All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
@@ -154,6 +154,12 @@
 #define	MAXAEXTNUM	((xfs_aextnum_t)0x7fff)		/* signed short */
 
 /*
+ * Min numbers of data/attr fork btree root pointers.
+ */
+#define MINDBTPTRS	3
+#define MINABTPTRS	2
+
+/*
  * MAXNAMELEN is the length (including the terminating null) of
  * the longest permissible file (component) name.
  */
diff --git a/fs/xfs/xfs_vfsops.c b/fs/xfs/xfs_vfsops.c
index 7227bae..07779c5 100644
--- a/fs/xfs/xfs_vfsops.c
+++ b/fs/xfs/xfs_vfsops.c
@@ -268,19 +268,14 @@
 #endif
 	if (ap->flags & XFSMNT_NOATIME)
 		mp->m_flags |= XFS_MOUNT_NOATIME;
-
 	if (ap->flags & XFSMNT_RETERR)
 		mp->m_flags |= XFS_MOUNT_RETERR;
-
 	if (ap->flags & XFSMNT_NOALIGN)
 		mp->m_flags |= XFS_MOUNT_NOALIGN;
-
 	if (ap->flags & XFSMNT_SWALLOC)
 		mp->m_flags |= XFS_MOUNT_SWALLOC;
-
 	if (ap->flags & XFSMNT_OSYNCISOSYNC)
 		mp->m_flags |= XFS_MOUNT_OSYNCISOSYNC;
-
 	if (ap->flags & XFSMNT_32BITINODES)
 		mp->m_flags |= (XFS_MOUNT_32BITINODES | XFS_MOUNT_32BITINOOPT);
 
@@ -300,15 +295,14 @@
 
 	if (ap->flags & XFSMNT_IHASHSIZE)
 		mp->m_flags |= XFS_MOUNT_IHASHSIZE;
-
 	if (ap->flags & XFSMNT_IDELETE)
 		mp->m_flags |= XFS_MOUNT_IDELETE;
-
 	if (ap->flags & XFSMNT_DIRSYNC)
 		mp->m_flags |= XFS_MOUNT_DIRSYNC;
-
 	if (ap->flags & XFSMNT_COMPAT_IOSIZE)
 		mp->m_flags |= XFS_MOUNT_COMPAT_IOSIZE;
+	if (ap->flags & XFSMNT_COMPAT_ATTR)
+		mp->m_flags |= XFS_MOUNT_COMPAT_ATTR;
 
 	/*
 	 * no recovery flag requires a read-only mount
@@ -1643,7 +1637,7 @@
 #define MNTOPT_IHASHSIZE    "ihashsize"    /* size of inode hash table */
 #define MNTOPT_NORECOVERY   "norecovery"   /* don't run XFS recovery */
 #define MNTOPT_BARRIER	"barrier"	/* use writer barriers for log write and
-					   unwritten extent conversion */
+					 * unwritten extent conversion */
 #define MNTOPT_OSYNCISOSYNC "osyncisosync" /* o_sync is REALLY o_sync */
 #define MNTOPT_64BITINODE   "inode64"	/* inodes can be allocated anywhere */
 #define MNTOPT_IKEEP	"ikeep"		/* do not free empty inode clusters */
@@ -1651,6 +1645,8 @@
 #define MNTOPT_LARGEIO	   "largeio"	/* report large I/O sizes in stat() */
 #define MNTOPT_NOLARGEIO   "nolargeio"	/* do not report large I/O sizes
 					 * in stat(). */
+#define MNTOPT_ATTR2	"attr2"		/* do use attr2 attribute format */
+#define MNTOPT_NOATTR2	"noattr2"	/* do not use attr2 attribute format */
 
 STATIC unsigned long
 suffix_strtoul(const char *cp, char **endp, unsigned int base)
@@ -1820,6 +1816,10 @@
 			args->flags &= ~XFSMNT_COMPAT_IOSIZE;
 		} else if (!strcmp(this_char, MNTOPT_NOLARGEIO)) {
 			args->flags |= XFSMNT_COMPAT_IOSIZE;
+		} else if (!strcmp(this_char, MNTOPT_ATTR2)) {
+			args->flags &= ~XFSMNT_COMPAT_ATTR;
+		} else if (!strcmp(this_char, MNTOPT_NOATTR2)) {
+			args->flags |= XFSMNT_COMPAT_ATTR;
 		} else if (!strcmp(this_char, "osyncisdsync")) {
 			/* no-op, this is now the default */
 printk("XFS: osyncisdsync is now the default, option is deprecated.\n");