aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_attr_leaf.c
diff options
context:
space:
mode:
authorNathan Scott <nathans@sgi.com>2005-11-01 18:34:53 -0500
committerNathan Scott <nathans@sgi.com>2005-11-01 18:34:53 -0500
commitd8cc890d4095f1eaa7f411a85051015b21262b12 (patch)
treee13bec06a94a8dc5552dab733e90ef4f4d41fb8b /fs/xfs/xfs_attr_leaf.c
parentaa82daa06198b27963fe3d6ee8035855b29f6524 (diff)
[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>
Diffstat (limited to 'fs/xfs/xfs_attr_leaf.c')
-rw-r--r--fs/xfs/xfs_attr_leaf.c177
1 files changed, 152 insertions, 25 deletions
diff --git a/fs/xfs/xfs_attr_leaf.c b/fs/xfs/xfs_attr_leaf.c
index e13eaa521436..50598b121683 100644
--- a/fs/xfs/xfs_attr_leaf.c
+++ b/fs/xfs/xfs_attr_leaf.c
@@ -118,13 +118,82 @@ STATIC int xfs_attr_put_listent(xfs_attr_list_context_t *context,
118 118
119 119
120/*======================================================================== 120/*========================================================================
121 * External routines when dirsize < XFS_LITINO(mp). 121 * External routines when attribute fork size < XFS_LITINO(mp).
122 *========================================================================*/ 122 *========================================================================*/
123 123
124/* 124/*
125 * Create the initial contents of a shortform attribute list. 125 * Query whether the requested number of additional bytes of extended
126 * attribute space will be able to fit inline.
127 * Returns zero if not, else the di_forkoff fork offset to be used in the
128 * literal area for attribute data once the new bytes have been added.
129 *
130 * di_forkoff must be 8 byte aligned, hence is stored as a >>3 value;
131 * special case for dev/uuid inodes, they have fixed size data forks.
126 */ 132 */
127int 133int
134xfs_attr_shortform_bytesfit(xfs_inode_t *dp, int bytes)
135{
136 int offset;
137 int minforkoff; /* lower limit on valid forkoff locations */
138 int maxforkoff; /* upper limit on valid forkoff locations */
139 xfs_mount_t *mp = dp->i_mount;
140
141 if (unlikely(mp->m_flags & XFS_MOUNT_COMPAT_ATTR)) {
142 if (bytes <= XFS_IFORK_ASIZE(dp))
143 return mp->m_attroffset >> 3;
144 return 0;
145 }
146
147 offset = (XFS_LITINO(mp) - bytes) >> 3; /* rounded down */
148
149 switch (dp->i_d.di_format) {
150 case XFS_DINODE_FMT_DEV:
151 minforkoff = roundup(sizeof(xfs_dev_t), 8) >> 3;
152 return (offset >= minforkoff) ? minforkoff : 0;
153 case XFS_DINODE_FMT_UUID:
154 minforkoff = roundup(sizeof(uuid_t), 8) >> 3;
155 return (offset >= minforkoff) ? minforkoff : 0;
156 }
157
158 /* data fork btree root can have at least this many key/ptr pairs */
159 minforkoff = MAX(dp->i_df.if_bytes, XFS_BMDR_SPACE_CALC(MINDBTPTRS));
160 minforkoff = roundup(minforkoff, 8) >> 3;
161
162 /* attr fork btree root can have at least this many key/ptr pairs */
163 maxforkoff = XFS_LITINO(mp) - XFS_BMDR_SPACE_CALC(MINABTPTRS);
164 maxforkoff = maxforkoff >> 3; /* rounded down */
165
166 if (offset >= minforkoff && offset < maxforkoff)
167 return offset;
168 if (offset >= maxforkoff)
169 return maxforkoff;
170 return 0;
171}
172
173/*
174 * Switch on the ATTR2 superblock bit (implies also FEATURES2)
175 */
176STATIC void
177xfs_sbversion_add_attr2(xfs_mount_t *mp, xfs_trans_t *tp)
178{
179 unsigned long s;
180
181 if (!(mp->m_flags & XFS_MOUNT_COMPAT_ATTR) &&
182 !(XFS_SB_VERSION_HASATTR2(&mp->m_sb))) {
183 s = XFS_SB_LOCK(mp);
184 if (!XFS_SB_VERSION_HASATTR2(&mp->m_sb)) {
185 XFS_SB_VERSION_ADDATTR2(&mp->m_sb);
186 XFS_SB_UNLOCK(mp, s);
187 xfs_mod_sb(tp, XFS_SB_VERSIONNUM | XFS_SB_FEATURES2);
188 } else
189 XFS_SB_UNLOCK(mp, s);
190 }
191}
192
193/*
194 * Create the initial contents of a shortform attribute list.
195 */
196void
128xfs_attr_shortform_create(xfs_da_args_t *args) 197xfs_attr_shortform_create(xfs_da_args_t *args)
129{ 198{
130 xfs_attr_sf_hdr_t *hdr; 199 xfs_attr_sf_hdr_t *hdr;
@@ -148,29 +217,37 @@ xfs_attr_shortform_create(xfs_da_args_t *args)
148 hdr->count = 0; 217 hdr->count = 0;
149 INT_SET(hdr->totsize, ARCH_CONVERT, sizeof(*hdr)); 218 INT_SET(hdr->totsize, ARCH_CONVERT, sizeof(*hdr));
150 xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA); 219 xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA);
151 return(0);
152} 220}
153 221
154/* 222/*
155 * Add a name/value pair to the shortform attribute list. 223 * Add a name/value pair to the shortform attribute list.
156 * Overflow from the inode has already been checked for. 224 * Overflow from the inode has already been checked for.
157 */ 225 */
158int 226void
159xfs_attr_shortform_add(xfs_da_args_t *args) 227xfs_attr_shortform_add(xfs_da_args_t *args, int forkoff)
160{ 228{
161 xfs_attr_shortform_t *sf; 229 xfs_attr_shortform_t *sf;
162 xfs_attr_sf_entry_t *sfe; 230 xfs_attr_sf_entry_t *sfe;
163 int i, offset, size; 231 int i, offset, size;
232 xfs_mount_t *mp;
164 xfs_inode_t *dp; 233 xfs_inode_t *dp;
165 xfs_ifork_t *ifp; 234 xfs_ifork_t *ifp;
166 235
167 dp = args->dp; 236 dp = args->dp;
237 mp = dp->i_mount;
238 dp->i_d.di_forkoff = forkoff;
239 dp->i_df.if_ext_max =
240 XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
241 dp->i_afp->if_ext_max =
242 XFS_IFORK_ASIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
243
168 ifp = dp->i_afp; 244 ifp = dp->i_afp;
169 ASSERT(ifp->if_flags & XFS_IFINLINE); 245 ASSERT(ifp->if_flags & XFS_IFINLINE);
170 sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data; 246 sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data;
171 sfe = &sf->list[0]; 247 sfe = &sf->list[0];
172 for (i = 0; i < INT_GET(sf->hdr.count, ARCH_CONVERT); 248 for (i = 0; i < INT_GET(sf->hdr.count, ARCH_CONVERT);
173 sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) { 249 sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
250#ifdef DEBUG
174 if (sfe->namelen != args->namelen) 251 if (sfe->namelen != args->namelen)
175 continue; 252 continue;
176 if (memcmp(args->name, sfe->nameval, args->namelen) != 0) 253 if (memcmp(args->name, sfe->nameval, args->namelen) != 0)
@@ -181,7 +258,8 @@ xfs_attr_shortform_add(xfs_da_args_t *args)
181 if (((args->flags & ATTR_ROOT) != 0) != 258 if (((args->flags & ATTR_ROOT) != 0) !=
182 ((sfe->flags & XFS_ATTR_ROOT) != 0)) 259 ((sfe->flags & XFS_ATTR_ROOT) != 0))
183 continue; 260 continue;
184 return(XFS_ERROR(EEXIST)); 261 ASSERT(0);
262#endif
185 } 263 }
186 264
187 offset = (char *)sfe - (char *)sf; 265 offset = (char *)sfe - (char *)sf;
@@ -200,11 +278,11 @@ xfs_attr_shortform_add(xfs_da_args_t *args)
200 INT_MOD(sf->hdr.totsize, ARCH_CONVERT, size); 278 INT_MOD(sf->hdr.totsize, ARCH_CONVERT, size);
201 xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA); 279 xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA);
202 280
203 return(0); 281 xfs_sbversion_add_attr2(mp, args->trans);
204} 282}
205 283
206/* 284/*
207 * Remove a name from the shortform attribute list structure. 285 * Remove an attribute from the shortform attribute list structure.
208 */ 286 */
209int 287int
210xfs_attr_shortform_remove(xfs_da_args_t *args) 288xfs_attr_shortform_remove(xfs_da_args_t *args)
@@ -212,17 +290,16 @@ xfs_attr_shortform_remove(xfs_da_args_t *args)
212 xfs_attr_shortform_t *sf; 290 xfs_attr_shortform_t *sf;
213 xfs_attr_sf_entry_t *sfe; 291 xfs_attr_sf_entry_t *sfe;
214 int base, size=0, end, totsize, i; 292 int base, size=0, end, totsize, i;
293 xfs_mount_t *mp;
215 xfs_inode_t *dp; 294 xfs_inode_t *dp;
216 295
217 /*
218 * Remove the attribute.
219 */
220 dp = args->dp; 296 dp = args->dp;
297 mp = dp->i_mount;
221 base = sizeof(xfs_attr_sf_hdr_t); 298 base = sizeof(xfs_attr_sf_hdr_t);
222 sf = (xfs_attr_shortform_t *)dp->i_afp->if_u1.if_data; 299 sf = (xfs_attr_shortform_t *)dp->i_afp->if_u1.if_data;
223 sfe = &sf->list[0]; 300 sfe = &sf->list[0];
224 for (i = 0; i < INT_GET(sf->hdr.count, ARCH_CONVERT); 301 end = INT_GET(sf->hdr.count, ARCH_CONVERT);
225 sfe = XFS_ATTR_SF_NEXTENTRY(sfe), 302 for (i = 0; i < end; sfe = XFS_ATTR_SF_NEXTENTRY(sfe),
226 base += size, i++) { 303 base += size, i++) {
227 size = XFS_ATTR_SF_ENTSIZE(sfe); 304 size = XFS_ATTR_SF_ENTSIZE(sfe);
228 if (sfe->namelen != args->namelen) 305 if (sfe->namelen != args->namelen)
@@ -237,19 +314,51 @@ xfs_attr_shortform_remove(xfs_da_args_t *args)
237 continue; 314 continue;
238 break; 315 break;
239 } 316 }
240 if (i == INT_GET(sf->hdr.count, ARCH_CONVERT)) 317 if (i == end)
241 return(XFS_ERROR(ENOATTR)); 318 return(XFS_ERROR(ENOATTR));
242 319
320 /*
321 * Fix up the attribute fork data, covering the hole
322 */
243 end = base + size; 323 end = base + size;
244 totsize = INT_GET(sf->hdr.totsize, ARCH_CONVERT); 324 totsize = INT_GET(sf->hdr.totsize, ARCH_CONVERT);
245 if (end != totsize) { 325 if (end != totsize)
246 memmove(&((char *)sf)[base], &((char *)sf)[end], 326 memmove(&((char *)sf)[base], &((char *)sf)[end], totsize - end);
247 totsize - end);
248 }
249 INT_MOD(sf->hdr.count, ARCH_CONVERT, -1); 327 INT_MOD(sf->hdr.count, ARCH_CONVERT, -1);
250 INT_MOD(sf->hdr.totsize, ARCH_CONVERT, -size); 328 INT_MOD(sf->hdr.totsize, ARCH_CONVERT, -size);
251 xfs_idata_realloc(dp, -size, XFS_ATTR_FORK); 329
252 xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA); 330 /*
331 * Fix up the start offset of the attribute fork
332 */
333 totsize -= size;
334 if (totsize == sizeof(xfs_attr_sf_hdr_t) && !args->addname) {
335 /*
336 * Last attribute now removed, revert to original
337 * inode format making all literal area available
338 * to the data fork once more.
339 */
340 xfs_idestroy_fork(dp, XFS_ATTR_FORK);
341 dp->i_d.di_forkoff = 0;
342 dp->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS;
343 ASSERT(dp->i_d.di_anextents == 0);
344 ASSERT(dp->i_afp == NULL);
345 dp->i_df.if_ext_max =
346 XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
347 xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE);
348 } else {
349 xfs_idata_realloc(dp, -size, XFS_ATTR_FORK);
350 dp->i_d.di_forkoff = xfs_attr_shortform_bytesfit(dp, totsize);
351 ASSERT(dp->i_d.di_forkoff);
352 ASSERT(totsize > sizeof(xfs_attr_sf_hdr_t) || args->addname);
353 dp->i_afp->if_ext_max =
354 XFS_IFORK_ASIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
355 dp->i_df.if_ext_max =
356 XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
357 xfs_trans_log_inode(args->trans, dp,
358 XFS_ILOG_CORE | XFS_ILOG_ADATA);
359 }
360
361 xfs_sbversion_add_attr2(mp, args->trans);
253 362
254 return(0); 363 return(0);
255} 364}
@@ -649,14 +758,16 @@ xfs_attr_shortform_allfit(xfs_dabuf_t *bp, xfs_inode_t *dp)
649 + name_loc->namelen 758 + name_loc->namelen
650 + INT_GET(name_loc->valuelen, ARCH_CONVERT); 759 + INT_GET(name_loc->valuelen, ARCH_CONVERT);
651 } 760 }
652 return( bytes < XFS_IFORK_ASIZE(dp) ); 761 if (bytes == sizeof(struct xfs_attr_sf_hdr))
762 return(-1);
763 return(xfs_attr_shortform_bytesfit(dp, bytes));
653} 764}
654 765
655/* 766/*
656 * Convert a leaf attribute list to shortform attribute list 767 * Convert a leaf attribute list to shortform attribute list
657 */ 768 */
658int 769int
659xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t *args) 770xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t *args, int forkoff)
660{ 771{
661 xfs_attr_leafblock_t *leaf; 772 xfs_attr_leafblock_t *leaf;
662 xfs_attr_leaf_entry_t *entry; 773 xfs_attr_leaf_entry_t *entry;
@@ -683,9 +794,25 @@ xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t *args)
683 error = xfs_da_shrink_inode(args, 0, bp); 794 error = xfs_da_shrink_inode(args, 0, bp);
684 if (error) 795 if (error)
685 goto out; 796 goto out;
686 error = xfs_attr_shortform_create(args); 797
687 if (error) 798 if (forkoff == -1) {
799 /*
800 * Last attribute was removed, revert to original
801 * inode format making all literal area available
802 * to the data fork once more.
803 */
804 xfs_idestroy_fork(dp, XFS_ATTR_FORK);
805 dp->i_d.di_forkoff = 0;
806 dp->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS;
807 ASSERT(dp->i_d.di_anextents == 0);
808 ASSERT(dp->i_afp == NULL);
809 dp->i_df.if_ext_max =
810 XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
811 xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE);
688 goto out; 812 goto out;
813 }
814
815 xfs_attr_shortform_create(args);
689 816
690 /* 817 /*
691 * Copy the attributes 818 * Copy the attributes
@@ -713,7 +840,7 @@ xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t *args)
713 nargs.hashval = INT_GET(entry->hashval, ARCH_CONVERT); 840 nargs.hashval = INT_GET(entry->hashval, ARCH_CONVERT);
714 nargs.flags = (entry->flags & XFS_ATTR_SECURE) ? ATTR_SECURE : 841 nargs.flags = (entry->flags & XFS_ATTR_SECURE) ? ATTR_SECURE :
715 ((entry->flags & XFS_ATTR_ROOT) ? ATTR_ROOT : 0); 842 ((entry->flags & XFS_ATTR_ROOT) ? ATTR_ROOT : 0);
716 xfs_attr_shortform_add(&nargs); 843 xfs_attr_shortform_add(&nargs, forkoff);
717 } 844 }
718 error = 0; 845 error = 0;
719 846