aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_inode.c
diff options
context:
space:
mode:
authorLachlan McIlroy <lachlan@sgi.com>2008-06-22 23:25:02 -0400
committerNiv Sardi <xaiki@debian.org>2008-07-28 02:58:56 -0400
commit6278debdf95b100a516b803f90d6f11b41c34171 (patch)
treeea4021d53cf15a295bee5b83a31134ba2e6a8cb7 /fs/xfs/xfs_inode.c
parent7f871d5d1b9b126c1a0cece737a37c6980c988e3 (diff)
[XFS] fix extent corruption in xfs_iext_irec_compact_full()
This function is used to compact the indirect extent list by moving extents from one page to the previous to fill them up. After we move some extents to an earlier page we need to shuffle the remaining extents to the start of the page. The actual bug here is the second argument to memmove() needs to index past the extents, that were copied to the previous page, and move the remaining extents. For pages that are already full (ie ext_avail == 0) the compaction code has no net effect so don't do it. SGI-PV: 983337 SGI-Modid: xfs-linux-melb:xfs-kern:31332a Signed-off-by: Lachlan McIlroy <lachlan@sgi.com> Signed-off-by: Christoph Hellwig <hch@infradead.org>
Diffstat (limited to 'fs/xfs/xfs_inode.c')
-rw-r--r--fs/xfs/xfs_inode.c70
1 files changed, 47 insertions, 23 deletions
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index fcb1dcc6f036..bedc66163176 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -4532,39 +4532,63 @@ xfs_iext_irec_compact_full(
4532 int nlists; /* number of irec's (ex lists) */ 4532 int nlists; /* number of irec's (ex lists) */
4533 4533
4534 ASSERT(ifp->if_flags & XFS_IFEXTIREC); 4534 ASSERT(ifp->if_flags & XFS_IFEXTIREC);
4535
4535 nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ; 4536 nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
4536 erp = ifp->if_u1.if_ext_irec; 4537 erp = ifp->if_u1.if_ext_irec;
4537 ep = &erp->er_extbuf[erp->er_extcount]; 4538 ep = &erp->er_extbuf[erp->er_extcount];
4538 erp_next = erp + 1; 4539 erp_next = erp + 1;
4539 ep_next = erp_next->er_extbuf; 4540 ep_next = erp_next->er_extbuf;
4541
4540 while (erp_idx < nlists - 1) { 4542 while (erp_idx < nlists - 1) {
4543 /*
4544 * Check how many extent records are available in this irec.
4545 * If there is none skip the whole exercise.
4546 */
4541 ext_avail = XFS_LINEAR_EXTS - erp->er_extcount; 4547 ext_avail = XFS_LINEAR_EXTS - erp->er_extcount;
4542 ext_diff = MIN(ext_avail, erp_next->er_extcount); 4548 if (ext_avail) {
4543 memcpy(ep, ep_next, ext_diff * sizeof(xfs_bmbt_rec_t)); 4549
4544 erp->er_extcount += ext_diff;
4545 erp_next->er_extcount -= ext_diff;
4546 /* Remove next page */
4547 if (erp_next->er_extcount == 0) {
4548 /* 4550 /*
4549 * Free page before removing extent record 4551 * Copy over as many as possible extent records into
4550 * so er_extoffs don't get modified in 4552 * the previous page.
4551 * xfs_iext_irec_remove.
4552 */ 4553 */
4553 kmem_free(erp_next->er_extbuf); 4554 ext_diff = MIN(ext_avail, erp_next->er_extcount);
4554 erp_next->er_extbuf = NULL; 4555 memcpy(ep, ep_next, ext_diff * sizeof(xfs_bmbt_rec_t));
4555 xfs_iext_irec_remove(ifp, erp_idx + 1); 4556 erp->er_extcount += ext_diff;
4556 erp = &ifp->if_u1.if_ext_irec[erp_idx]; 4557 erp_next->er_extcount -= ext_diff;
4557 nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ; 4558
4558 /* Update next page */ 4559 /*
4559 } else { 4560 * If the next irec is empty now we can simply
4560 /* Move rest of page up to become next new page */ 4561 * remove it.
4561 memmove(erp_next->er_extbuf, ep_next, 4562 */
4562 erp_next->er_extcount * sizeof(xfs_bmbt_rec_t)); 4563 if (erp_next->er_extcount == 0) {
4563 ep_next = erp_next->er_extbuf; 4564 /*
4564 memset(&ep_next[erp_next->er_extcount], 0, 4565 * Free page before removing extent record
4565 (XFS_LINEAR_EXTS - erp_next->er_extcount) * 4566 * so er_extoffs don't get modified in
4566 sizeof(xfs_bmbt_rec_t)); 4567 * xfs_iext_irec_remove.
4568 */
4569 kmem_free(erp_next->er_extbuf);
4570 erp_next->er_extbuf = NULL;
4571 xfs_iext_irec_remove(ifp, erp_idx + 1);
4572 erp = &ifp->if_u1.if_ext_irec[erp_idx];
4573 nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
4574
4575 /*
4576 * If the next irec is not empty move up the content
4577 * that has not been copied to the previous page to
4578 * the beggining of this one.
4579 */
4580 } else {
4581 memmove(erp_next->er_extbuf, &ep_next[ext_diff],
4582 erp_next->er_extcount *
4583 sizeof(xfs_bmbt_rec_t));
4584 ep_next = erp_next->er_extbuf;
4585 memset(&ep_next[erp_next->er_extcount], 0,
4586 (XFS_LINEAR_EXTS -
4587 erp_next->er_extcount) *
4588 sizeof(xfs_bmbt_rec_t));
4589 }
4567 } 4590 }
4591
4568 if (erp->er_extcount == XFS_LINEAR_EXTS) { 4592 if (erp->er_extcount == XFS_LINEAR_EXTS) {
4569 erp_idx++; 4593 erp_idx++;
4570 if (erp_idx < nlists) 4594 if (erp_idx < nlists)