diff options
-rw-r--r-- | fs/xfs/xfs_bmap.c | 59 | ||||
-rw-r--r-- | fs/xfs/xfs_bmap.h | 18 | ||||
-rw-r--r-- | fs/xfs/xfs_bmap_btree.h | 8 | ||||
-rw-r--r-- | fs/xfs/xfs_inode.c | 715 | ||||
-rw-r--r-- | fs/xfs/xfs_inode.h | 52 |
5 files changed, 826 insertions, 26 deletions
diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c index 53c47a181f87..81a95b684b6b 100644 --- a/fs/xfs/xfs_bmap.c +++ b/fs/xfs/xfs_bmap.c | |||
@@ -3423,6 +3423,7 @@ xfs_bmap_local_to_extents( | |||
3423 | xfs_bmap_forkoff_reset(args.mp, ip, whichfork); | 3423 | xfs_bmap_forkoff_reset(args.mp, ip, whichfork); |
3424 | xfs_idata_realloc(ip, -ifp->if_bytes, whichfork); | 3424 | xfs_idata_realloc(ip, -ifp->if_bytes, whichfork); |
3425 | xfs_iext_add(ifp, 0, 1); | 3425 | xfs_iext_add(ifp, 0, 1); |
3426 | ASSERT((ifp->if_flags & (XFS_IFEXTENTS|XFS_IFEXTIREC)) == XFS_IFEXTENTS); | ||
3426 | ep = xfs_iext_get_ext(ifp, 0); | 3427 | ep = xfs_iext_get_ext(ifp, 0); |
3427 | xfs_bmbt_set_allf(ep, 0, args.fsbno, 1, XFS_EXT_NORM); | 3428 | xfs_bmbt_set_allf(ep, 0, args.fsbno, 1, XFS_EXT_NORM); |
3428 | xfs_bmap_trace_post_update(fname, "new", ip, 0, whichfork); | 3429 | xfs_bmap_trace_post_update(fname, "new", ip, 0, whichfork); |
@@ -3552,6 +3553,54 @@ xfs_bmap_do_search_extents( | |||
3552 | } | 3553 | } |
3553 | 3554 | ||
3554 | /* | 3555 | /* |
3556 | * Call xfs_bmap_do_search_extents() to search for the extent | ||
3557 | * record containing block bno. If in multi-level in-core extent | ||
3558 | * allocation mode, find and extract the target extent buffer, | ||
3559 | * otherwise just use the direct extent list. | ||
3560 | */ | ||
3561 | xfs_bmbt_rec_t * /* pointer to found extent entry */ | ||
3562 | xfs_bmap_search_multi_extents( | ||
3563 | xfs_ifork_t *ifp, /* inode fork pointer */ | ||
3564 | xfs_fileoff_t bno, /* block number searched for */ | ||
3565 | int *eofp, /* out: end of file found */ | ||
3566 | xfs_extnum_t *lastxp, /* out: last extent index */ | ||
3567 | xfs_bmbt_irec_t *gotp, /* out: extent entry found */ | ||
3568 | xfs_bmbt_irec_t *prevp) /* out: previous extent entry found */ | ||
3569 | { | ||
3570 | xfs_bmbt_rec_t *base; /* base of extent records */ | ||
3571 | xfs_bmbt_rec_t *ep; /* extent record pointer */ | ||
3572 | xfs_ext_irec_t *erp = NULL; /* indirection array pointer */ | ||
3573 | xfs_extnum_t lastx; /* last extent index */ | ||
3574 | xfs_extnum_t nextents; /* number of file extents */ | ||
3575 | |||
3576 | /* | ||
3577 | * For multi-level extent allocation mode, find the | ||
3578 | * target extent list and pass only the contiguous | ||
3579 | * list to xfs_bmap_do_search_extents. Convert lastx | ||
3580 | * from a file extent index to an index within the | ||
3581 | * target extent list. | ||
3582 | */ | ||
3583 | if (ifp->if_flags & XFS_IFEXTIREC) { | ||
3584 | int erp_idx = 0; | ||
3585 | erp = xfs_iext_bno_to_irec(ifp, bno, &erp_idx); | ||
3586 | base = erp->er_extbuf; | ||
3587 | nextents = erp->er_extcount; | ||
3588 | lastx = ifp->if_lastex - erp->er_extoff; | ||
3589 | } else { | ||
3590 | base = &ifp->if_u1.if_extents[0]; | ||
3591 | nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t); | ||
3592 | lastx = ifp->if_lastex; | ||
3593 | } | ||
3594 | ep = xfs_bmap_do_search_extents(base, lastx, nextents, bno, | ||
3595 | eofp, lastxp, gotp, prevp); | ||
3596 | /* Convert lastx back to file-based index */ | ||
3597 | if (ifp->if_flags & XFS_IFEXTIREC) { | ||
3598 | *lastxp += erp->er_extoff; | ||
3599 | } | ||
3600 | return ep; | ||
3601 | } | ||
3602 | |||
3603 | /* | ||
3555 | * Search the extents list for the inode, for the extent containing bno. | 3604 | * Search the extents list for the inode, for the extent containing bno. |
3556 | * If bno lies in a hole, point to the next entry. If bno lies past eof, | 3605 | * If bno lies in a hole, point to the next entry. If bno lies past eof, |
3557 | * *eofp will be set, and *prevp will contain the last entry (null if none). | 3606 | * *eofp will be set, and *prevp will contain the last entry (null if none). |
@@ -3569,20 +3618,14 @@ xfs_bmap_search_extents( | |||
3569 | xfs_bmbt_irec_t *prevp) /* out: previous extent entry found */ | 3618 | xfs_bmbt_irec_t *prevp) /* out: previous extent entry found */ |
3570 | { | 3619 | { |
3571 | xfs_ifork_t *ifp; /* inode fork pointer */ | 3620 | xfs_ifork_t *ifp; /* inode fork pointer */ |
3572 | xfs_bmbt_rec_t *base; /* base of extent list */ | ||
3573 | xfs_extnum_t lastx; /* last extent index used */ | ||
3574 | xfs_extnum_t nextents; /* number of file extents */ | ||
3575 | xfs_bmbt_rec_t *ep; /* extent record pointer */ | 3621 | xfs_bmbt_rec_t *ep; /* extent record pointer */ |
3576 | int rt; /* realtime flag */ | 3622 | int rt; /* realtime flag */ |
3577 | 3623 | ||
3578 | XFS_STATS_INC(xs_look_exlist); | 3624 | XFS_STATS_INC(xs_look_exlist); |
3579 | ifp = XFS_IFORK_PTR(ip, whichfork); | 3625 | ifp = XFS_IFORK_PTR(ip, whichfork); |
3580 | lastx = ifp->if_lastex; | ||
3581 | nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t); | ||
3582 | base = &ifp->if_u1.if_extents[0]; | ||
3583 | 3626 | ||
3584 | ep = xfs_bmap_do_search_extents(base, lastx, nextents, bno, eofp, | 3627 | ep = xfs_bmap_search_multi_extents(ifp, bno, eofp, lastxp, gotp, prevp); |
3585 | lastxp, gotp, prevp); | 3628 | |
3586 | rt = (whichfork == XFS_DATA_FORK) && XFS_IS_REALTIME_INODE(ip); | 3629 | rt = (whichfork == XFS_DATA_FORK) && XFS_IS_REALTIME_INODE(ip); |
3587 | if (unlikely(!rt && !gotp->br_startblock && (*lastxp != NULLEXTNUM))) { | 3630 | if (unlikely(!rt && !gotp->br_startblock && (*lastxp != NULLEXTNUM))) { |
3588 | cmn_err(CE_PANIC,"Access to block zero: fs: <%s> inode: %lld " | 3631 | cmn_err(CE_PANIC,"Access to block zero: fs: <%s> inode: %lld " |
diff --git a/fs/xfs/xfs_bmap.h b/fs/xfs/xfs_bmap.h index 4c05f95452c2..011ccaa9a1c0 100644 --- a/fs/xfs/xfs_bmap.h +++ b/fs/xfs/xfs_bmap.h | |||
@@ -352,6 +352,24 @@ xfs_check_nostate_extents( | |||
352 | xfs_extnum_t idx, | 352 | xfs_extnum_t idx, |
353 | xfs_extnum_t num); | 353 | xfs_extnum_t num); |
354 | 354 | ||
355 | /* | ||
356 | * Call xfs_bmap_do_search_extents() to search for the extent | ||
357 | * record containing block bno. If in multi-level in-core extent | ||
358 | * allocation mode, find and extract the target extent buffer, | ||
359 | * otherwise just use the direct extent list. | ||
360 | */ | ||
361 | xfs_bmbt_rec_t * | ||
362 | xfs_bmap_search_multi_extents(struct xfs_ifork *, xfs_fileoff_t, int *, | ||
363 | xfs_extnum_t *, xfs_bmbt_irec_t *, xfs_bmbt_irec_t *); | ||
364 | |||
365 | /* | ||
366 | * Search an extent list for the extent which includes block | ||
367 | * bno. | ||
368 | */ | ||
369 | xfs_bmbt_rec_t *xfs_bmap_do_search_extents(xfs_bmbt_rec_t *, | ||
370 | xfs_extnum_t, xfs_extnum_t, xfs_fileoff_t, int *, | ||
371 | xfs_extnum_t *, xfs_bmbt_irec_t *, xfs_bmbt_irec_t *); | ||
372 | |||
355 | #endif /* __KERNEL__ */ | 373 | #endif /* __KERNEL__ */ |
356 | 374 | ||
357 | #endif /* __XFS_BMAP_H__ */ | 375 | #endif /* __XFS_BMAP_H__ */ |
diff --git a/fs/xfs/xfs_bmap_btree.h b/fs/xfs/xfs_bmap_btree.h index e095a2d344ae..6478cfa0e539 100644 --- a/fs/xfs/xfs_bmap_btree.h +++ b/fs/xfs/xfs_bmap_btree.h | |||
@@ -372,14 +372,6 @@ extern int xfs_bmbt_get_rec(struct xfs_btree_cur *, xfs_fileoff_t *, | |||
372 | xfs_exntst_t *, int *); | 372 | xfs_exntst_t *, int *); |
373 | #endif | 373 | #endif |
374 | 374 | ||
375 | /* | ||
376 | * Search an extent list for the extent which includes block | ||
377 | * bno. | ||
378 | */ | ||
379 | xfs_bmbt_rec_t *xfs_bmap_do_search_extents(xfs_bmbt_rec_t *, | ||
380 | xfs_extnum_t, xfs_extnum_t, xfs_fileoff_t, int *, | ||
381 | xfs_extnum_t *, xfs_bmbt_irec_t *, xfs_bmbt_irec_t *); | ||
382 | |||
383 | #endif /* __KERNEL__ */ | 375 | #endif /* __KERNEL__ */ |
384 | 376 | ||
385 | #endif /* __XFS_BMAP_BTREE_H__ */ | 377 | #endif /* __XFS_BMAP_BTREE_H__ */ |
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 6459395a0e40..580fa0758039 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c | |||
@@ -2630,8 +2630,9 @@ xfs_idestroy_fork( | |||
2630 | ifp->if_real_bytes = 0; | 2630 | ifp->if_real_bytes = 0; |
2631 | } | 2631 | } |
2632 | } else if ((ifp->if_flags & XFS_IFEXTENTS) && | 2632 | } else if ((ifp->if_flags & XFS_IFEXTENTS) && |
2633 | (ifp->if_u1.if_extents != NULL) && | 2633 | ((ifp->if_flags & XFS_IFEXTIREC) || |
2634 | (ifp->if_u1.if_extents != ifp->if_u2.if_inline_ext)) { | 2634 | ((ifp->if_u1.if_extents != NULL) && |
2635 | (ifp->if_u1.if_extents != ifp->if_u2.if_inline_ext)))) { | ||
2635 | ASSERT(ifp->if_real_bytes != 0); | 2636 | ASSERT(ifp->if_real_bytes != 0); |
2636 | xfs_iext_destroy(ifp); | 2637 | xfs_iext_destroy(ifp); |
2637 | } | 2638 | } |
@@ -3622,7 +3623,16 @@ xfs_iext_get_ext( | |||
3622 | xfs_extnum_t idx) /* index of target extent */ | 3623 | xfs_extnum_t idx) /* index of target extent */ |
3623 | { | 3624 | { |
3624 | ASSERT(idx >= 0); | 3625 | ASSERT(idx >= 0); |
3625 | if (ifp->if_bytes) { | 3626 | if ((ifp->if_flags & XFS_IFEXTIREC) && (idx == 0)) { |
3627 | return ifp->if_u1.if_ext_irec->er_extbuf; | ||
3628 | } else if (ifp->if_flags & XFS_IFEXTIREC) { | ||
3629 | xfs_ext_irec_t *erp; /* irec pointer */ | ||
3630 | int erp_idx = 0; /* irec index */ | ||
3631 | xfs_extnum_t page_idx = idx; /* ext index in target list */ | ||
3632 | |||
3633 | erp = xfs_iext_idx_to_irec(ifp, &page_idx, &erp_idx, 0); | ||
3634 | return &erp->er_extbuf[page_idx]; | ||
3635 | } else if (ifp->if_bytes) { | ||
3626 | return &ifp->if_u1.if_extents[idx]; | 3636 | return &ifp->if_u1.if_extents[idx]; |
3627 | } else { | 3637 | } else { |
3628 | return NULL; | 3638 | return NULL; |
@@ -3691,6 +3701,7 @@ xfs_iext_add( | |||
3691 | } | 3701 | } |
3692 | ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext; | 3702 | ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext; |
3693 | ifp->if_real_bytes = 0; | 3703 | ifp->if_real_bytes = 0; |
3704 | ifp->if_lastex = nextents + ext_diff; | ||
3694 | } | 3705 | } |
3695 | /* | 3706 | /* |
3696 | * Otherwise use a linear (direct) extent list. | 3707 | * Otherwise use a linear (direct) extent list. |
@@ -3698,7 +3709,7 @@ xfs_iext_add( | |||
3698 | * xfs_iext_realloc_direct will switch us from | 3709 | * xfs_iext_realloc_direct will switch us from |
3699 | * inline to direct extent allocation mode. | 3710 | * inline to direct extent allocation mode. |
3700 | */ | 3711 | */ |
3701 | else { | 3712 | else if (nextents + ext_diff <= XFS_LINEAR_EXTS) { |
3702 | xfs_iext_realloc_direct(ifp, new_size); | 3713 | xfs_iext_realloc_direct(ifp, new_size); |
3703 | if (idx < nextents) { | 3714 | if (idx < nextents) { |
3704 | memmove(&ifp->if_u1.if_extents[idx + ext_diff], | 3715 | memmove(&ifp->if_u1.if_extents[idx + ext_diff], |
@@ -3707,14 +3718,182 @@ xfs_iext_add( | |||
3707 | memset(&ifp->if_u1.if_extents[idx], 0, byte_diff); | 3718 | memset(&ifp->if_u1.if_extents[idx], 0, byte_diff); |
3708 | } | 3719 | } |
3709 | } | 3720 | } |
3721 | /* Indirection array */ | ||
3722 | else { | ||
3723 | xfs_ext_irec_t *erp; | ||
3724 | int erp_idx = 0; | ||
3725 | int page_idx = idx; | ||
3726 | |||
3727 | ASSERT(nextents + ext_diff > XFS_LINEAR_EXTS); | ||
3728 | if (ifp->if_flags & XFS_IFEXTIREC) { | ||
3729 | erp = xfs_iext_idx_to_irec(ifp, &page_idx, &erp_idx, 1); | ||
3730 | } else { | ||
3731 | xfs_iext_irec_init(ifp); | ||
3732 | ASSERT(ifp->if_flags & XFS_IFEXTIREC); | ||
3733 | erp = ifp->if_u1.if_ext_irec; | ||
3734 | } | ||
3735 | /* Extents fit in target extent page */ | ||
3736 | if (erp && erp->er_extcount + ext_diff <= XFS_LINEAR_EXTS) { | ||
3737 | if (page_idx < erp->er_extcount) { | ||
3738 | memmove(&erp->er_extbuf[page_idx + ext_diff], | ||
3739 | &erp->er_extbuf[page_idx], | ||
3740 | (erp->er_extcount - page_idx) * | ||
3741 | sizeof(xfs_bmbt_rec_t)); | ||
3742 | memset(&erp->er_extbuf[page_idx], 0, byte_diff); | ||
3743 | } | ||
3744 | erp->er_extcount += ext_diff; | ||
3745 | xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, ext_diff); | ||
3746 | } | ||
3747 | /* Insert a new extent page */ | ||
3748 | else if (erp) { | ||
3749 | xfs_iext_add_indirect_multi(ifp, | ||
3750 | erp_idx, page_idx, ext_diff); | ||
3751 | } | ||
3752 | /* | ||
3753 | * If extent(s) are being appended to the last page in | ||
3754 | * the indirection array and the new extent(s) don't fit | ||
3755 | * in the page, then erp is NULL and erp_idx is set to | ||
3756 | * the next index needed in the indirection array. | ||
3757 | */ | ||
3758 | else { | ||
3759 | int count = ext_diff; | ||
3760 | |||
3761 | while (count) { | ||
3762 | erp = xfs_iext_irec_new(ifp, erp_idx); | ||
3763 | erp->er_extcount = count; | ||
3764 | count -= MIN(count, (int)XFS_LINEAR_EXTS); | ||
3765 | if (count) { | ||
3766 | erp_idx++; | ||
3767 | } | ||
3768 | } | ||
3769 | } | ||
3770 | } | ||
3710 | ifp->if_bytes = new_size; | 3771 | ifp->if_bytes = new_size; |
3711 | } | 3772 | } |
3712 | 3773 | ||
3713 | /* | 3774 | /* |
3775 | * This is called when incore extents are being added to the indirection | ||
3776 | * array and the new extents do not fit in the target extent list. The | ||
3777 | * erp_idx parameter contains the irec index for the target extent list | ||
3778 | * in the indirection array, and the idx parameter contains the extent | ||
3779 | * index within the list. The number of extents being added is stored | ||
3780 | * in the count parameter. | ||
3781 | * | ||
3782 | * |-------| |-------| | ||
3783 | * | | | | idx - number of extents before idx | ||
3784 | * | idx | | count | | ||
3785 | * | | | | count - number of extents being inserted at idx | ||
3786 | * |-------| |-------| | ||
3787 | * | count | | nex2 | nex2 - number of extents after idx + count | ||
3788 | * |-------| |-------| | ||
3789 | */ | ||
3790 | void | ||
3791 | xfs_iext_add_indirect_multi( | ||
3792 | xfs_ifork_t *ifp, /* inode fork pointer */ | ||
3793 | int erp_idx, /* target extent irec index */ | ||
3794 | xfs_extnum_t idx, /* index within target list */ | ||
3795 | int count) /* new extents being added */ | ||
3796 | { | ||
3797 | int byte_diff; /* new bytes being added */ | ||
3798 | xfs_ext_irec_t *erp; /* pointer to irec entry */ | ||
3799 | xfs_extnum_t ext_diff; /* number of extents to add */ | ||
3800 | xfs_extnum_t ext_cnt; /* new extents still needed */ | ||
3801 | xfs_extnum_t nex2; /* extents after idx + count */ | ||
3802 | xfs_bmbt_rec_t *nex2_ep = NULL; /* temp list for nex2 extents */ | ||
3803 | int nlists; /* number of irec's (lists) */ | ||
3804 | |||
3805 | ASSERT(ifp->if_flags & XFS_IFEXTIREC); | ||
3806 | erp = &ifp->if_u1.if_ext_irec[erp_idx]; | ||
3807 | nex2 = erp->er_extcount - idx; | ||
3808 | nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ; | ||
3809 | |||
3810 | /* | ||
3811 | * Save second part of target extent list | ||
3812 | * (all extents past */ | ||
3813 | if (nex2) { | ||
3814 | byte_diff = nex2 * sizeof(xfs_bmbt_rec_t); | ||
3815 | nex2_ep = (xfs_bmbt_rec_t *) kmem_alloc(byte_diff, KM_SLEEP); | ||
3816 | memmove(nex2_ep, &erp->er_extbuf[idx], byte_diff); | ||
3817 | erp->er_extcount -= nex2; | ||
3818 | xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, -nex2); | ||
3819 | memset(&erp->er_extbuf[idx], 0, byte_diff); | ||
3820 | } | ||
3821 | |||
3822 | /* | ||
3823 | * Add the new extents to the end of the target | ||
3824 | * list, then allocate new irec record(s) and | ||
3825 | * extent buffer(s) as needed to store the rest | ||
3826 | * of the new extents. | ||
3827 | */ | ||
3828 | ext_cnt = count; | ||
3829 | ext_diff = MIN(ext_cnt, (int)XFS_LINEAR_EXTS - erp->er_extcount); | ||
3830 | if (ext_diff) { | ||
3831 | erp->er_extcount += ext_diff; | ||
3832 | xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, ext_diff); | ||
3833 | ext_cnt -= ext_diff; | ||
3834 | } | ||
3835 | while (ext_cnt) { | ||
3836 | erp_idx++; | ||
3837 | erp = xfs_iext_irec_new(ifp, erp_idx); | ||
3838 | ext_diff = MIN(ext_cnt, (int)XFS_LINEAR_EXTS); | ||
3839 | erp->er_extcount = ext_diff; | ||
3840 | xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, ext_diff); | ||
3841 | ext_cnt -= ext_diff; | ||
3842 | } | ||
3843 | |||
3844 | /* Add nex2 extents back to indirection array */ | ||
3845 | if (nex2) { | ||
3846 | xfs_extnum_t ext_avail; | ||
3847 | int i; | ||
3848 | |||
3849 | byte_diff = nex2 * sizeof(xfs_bmbt_rec_t); | ||
3850 | ext_avail = XFS_LINEAR_EXTS - erp->er_extcount; | ||
3851 | i = 0; | ||
3852 | /* | ||
3853 | * If nex2 extents fit in the current page, append | ||
3854 | * nex2_ep after the new extents. | ||
3855 | */ | ||
3856 | if (nex2 <= ext_avail) { | ||
3857 | i = erp->er_extcount; | ||
3858 | } | ||
3859 | /* | ||
3860 | * Otherwise, check if space is available in the | ||
3861 | * next page. | ||
3862 | */ | ||
3863 | else if ((erp_idx < nlists - 1) && | ||
3864 | (nex2 <= (ext_avail = XFS_LINEAR_EXTS - | ||
3865 | ifp->if_u1.if_ext_irec[erp_idx+1].er_extcount))) { | ||
3866 | erp_idx++; | ||
3867 | erp++; | ||
3868 | /* Create a hole for nex2 extents */ | ||
3869 | memmove(&erp->er_extbuf[nex2], erp->er_extbuf, | ||
3870 | erp->er_extcount * sizeof(xfs_bmbt_rec_t)); | ||
3871 | } | ||
3872 | /* | ||
3873 | * Final choice, create a new extent page for | ||
3874 | * nex2 extents. | ||
3875 | */ | ||
3876 | else { | ||
3877 | erp_idx++; | ||
3878 | erp = xfs_iext_irec_new(ifp, erp_idx); | ||
3879 | } | ||
3880 | memmove(&erp->er_extbuf[i], nex2_ep, byte_diff); | ||
3881 | kmem_free(nex2_ep, byte_diff); | ||
3882 | erp->er_extcount += nex2; | ||
3883 | xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, nex2); | ||
3884 | } | ||
3885 | } | ||
3886 | |||
3887 | /* | ||
3714 | * This is called when the amount of space required for incore file | 3888 | * This is called when the amount of space required for incore file |
3715 | * extents needs to be decreased. The ext_diff parameter stores the | 3889 | * extents needs to be decreased. The ext_diff parameter stores the |
3716 | * number of extents to be removed and the idx parameter contains | 3890 | * number of extents to be removed and the idx parameter contains |
3717 | * the extent index where the extents will be removed from. | 3891 | * the extent index where the extents will be removed from. |
3892 | * | ||
3893 | * If the amount of space needed has decreased below the linear | ||
3894 | * limit, XFS_IEXT_BUFSZ, then switch to using the contiguous | ||
3895 | * extent array. Otherwise, use kmem_realloc() to adjust the | ||
3896 | * size to what is needed. | ||
3718 | */ | 3897 | */ |
3719 | void | 3898 | void |
3720 | xfs_iext_remove( | 3899 | xfs_iext_remove( |
@@ -3731,6 +3910,8 @@ xfs_iext_remove( | |||
3731 | 3910 | ||
3732 | if (new_size == 0) { | 3911 | if (new_size == 0) { |
3733 | xfs_iext_destroy(ifp); | 3912 | xfs_iext_destroy(ifp); |
3913 | } else if (ifp->if_flags & XFS_IFEXTIREC) { | ||
3914 | xfs_iext_remove_indirect(ifp, idx, ext_diff); | ||
3734 | } else if (ifp->if_real_bytes) { | 3915 | } else if (ifp->if_real_bytes) { |
3735 | xfs_iext_remove_direct(ifp, idx, ext_diff); | 3916 | xfs_iext_remove_direct(ifp, idx, ext_diff); |
3736 | } else { | 3917 | } else { |
@@ -3751,6 +3932,7 @@ xfs_iext_remove_inline( | |||
3751 | { | 3932 | { |
3752 | int nextents; /* number of extents in file */ | 3933 | int nextents; /* number of extents in file */ |
3753 | 3934 | ||
3935 | ASSERT(!(ifp->if_flags & XFS_IFEXTIREC)); | ||
3754 | ASSERT(idx < XFS_INLINE_EXTS); | 3936 | ASSERT(idx < XFS_INLINE_EXTS); |
3755 | nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t); | 3937 | nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t); |
3756 | ASSERT(((nextents - ext_diff) > 0) && | 3938 | ASSERT(((nextents - ext_diff) > 0) && |
@@ -3788,6 +3970,7 @@ xfs_iext_remove_direct( | |||
3788 | xfs_extnum_t nextents; /* number of extents in file */ | 3970 | xfs_extnum_t nextents; /* number of extents in file */ |
3789 | int new_size; /* size of extents after removal */ | 3971 | int new_size; /* size of extents after removal */ |
3790 | 3972 | ||
3973 | ASSERT(!(ifp->if_flags & XFS_IFEXTIREC)); | ||
3791 | new_size = ifp->if_bytes - | 3974 | new_size = ifp->if_bytes - |
3792 | (ext_diff * sizeof(xfs_bmbt_rec_t)); | 3975 | (ext_diff * sizeof(xfs_bmbt_rec_t)); |
3793 | nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t); | 3976 | nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t); |
@@ -3816,6 +3999,84 @@ xfs_iext_remove_direct( | |||
3816 | } | 3999 | } |
3817 | 4000 | ||
3818 | /* | 4001 | /* |
4002 | * This is called when incore extents are being removed from the | ||
4003 | * indirection array and the extents being removed span multiple extent | ||
4004 | * buffers. The idx parameter contains the file extent index where we | ||
4005 | * want to begin removing extents, and the count parameter contains | ||
4006 | * how many extents need to be removed. | ||
4007 | * | ||
4008 | * |-------| |-------| | ||
4009 | * | nex1 | | | nex1 - number of extents before idx | ||
4010 | * |-------| | count | | ||
4011 | * | | | | count - number of extents being removed at idx | ||
4012 | * | count | |-------| | ||
4013 | * | | | nex2 | nex2 - number of extents after idx + count | ||
4014 | * |-------| |-------| | ||
4015 | */ | ||
4016 | void | ||
4017 | xfs_iext_remove_indirect( | ||
4018 | xfs_ifork_t *ifp, /* inode fork pointer */ | ||
4019 | xfs_extnum_t idx, /* index to begin removing extents */ | ||
4020 | int count) /* number of extents to remove */ | ||
4021 | { | ||
4022 | xfs_ext_irec_t *erp; /* indirection array pointer */ | ||
4023 | int erp_idx = 0; /* indirection array index */ | ||
4024 | xfs_extnum_t ext_cnt; /* extents left to remove */ | ||
4025 | xfs_extnum_t ext_diff; /* extents to remove in current list */ | ||
4026 | xfs_extnum_t nex1; /* number of extents before idx */ | ||
4027 | xfs_extnum_t nex2; /* extents after idx + count */ | ||
4028 | int nlists; /* entries in indirecton array */ | ||
4029 | int page_idx = idx; /* index in target extent list */ | ||
4030 | |||
4031 | ASSERT(ifp->if_flags & XFS_IFEXTIREC); | ||
4032 | erp = xfs_iext_idx_to_irec(ifp, &page_idx, &erp_idx, 0); | ||
4033 | ASSERT(erp != NULL); | ||
4034 | nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ; | ||
4035 | nex1 = page_idx; | ||
4036 | ext_cnt = count; | ||
4037 | while (ext_cnt) { | ||
4038 | nex2 = MAX((erp->er_extcount - (nex1 + ext_cnt)), 0); | ||
4039 | ext_diff = MIN(ext_cnt, (erp->er_extcount - nex1)); | ||
4040 | /* | ||
4041 | * Check for deletion of entire list; | ||
4042 | * xfs_iext_irec_remove() updates extent offsets. | ||
4043 | */ | ||
4044 | if (ext_diff == erp->er_extcount) { | ||
4045 | xfs_iext_irec_remove(ifp, erp_idx); | ||
4046 | ext_cnt -= ext_diff; | ||
4047 | nex1 = 0; | ||
4048 | if (ext_cnt) { | ||
4049 | ASSERT(erp_idx < ifp->if_real_bytes / | ||
4050 | XFS_IEXT_BUFSZ); | ||
4051 | erp = &ifp->if_u1.if_ext_irec[erp_idx]; | ||
4052 | nex1 = 0; | ||
4053 | continue; | ||
4054 | } else { | ||
4055 | break; | ||
4056 | } | ||
4057 | } | ||
4058 | /* Move extents up (if needed) */ | ||
4059 | if (nex2) { | ||
4060 | memmove(&erp->er_extbuf[nex1], | ||
4061 | &erp->er_extbuf[nex1 + ext_diff], | ||
4062 | nex2 * sizeof(xfs_bmbt_rec_t)); | ||
4063 | } | ||
4064 | /* Zero out rest of page */ | ||
4065 | memset(&erp->er_extbuf[nex1 + nex2], 0, (XFS_IEXT_BUFSZ - | ||
4066 | ((nex1 + nex2) * sizeof(xfs_bmbt_rec_t)))); | ||
4067 | /* Update remaining counters */ | ||
4068 | erp->er_extcount -= ext_diff; | ||
4069 | xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, -ext_diff); | ||
4070 | ext_cnt -= ext_diff; | ||
4071 | nex1 = 0; | ||
4072 | erp_idx++; | ||
4073 | erp++; | ||
4074 | } | ||
4075 | ifp->if_bytes -= count * sizeof(xfs_bmbt_rec_t); | ||
4076 | xfs_iext_irec_compact(ifp); | ||
4077 | } | ||
4078 | |||
4079 | /* | ||
3819 | * Create, destroy, or resize a linear (direct) block of extents. | 4080 | * Create, destroy, or resize a linear (direct) block of extents. |
3820 | */ | 4081 | */ |
3821 | void | 4082 | void |
@@ -3827,6 +4088,10 @@ xfs_iext_realloc_direct( | |||
3827 | 4088 | ||
3828 | rnew_size = new_size; | 4089 | rnew_size = new_size; |
3829 | 4090 | ||
4091 | ASSERT(!(ifp->if_flags & XFS_IFEXTIREC) || | ||
4092 | ((new_size >= 0) && (new_size <= XFS_IEXT_BUFSZ) && | ||
4093 | (new_size != ifp->if_real_bytes))); | ||
4094 | |||
3830 | /* Free extent records */ | 4095 | /* Free extent records */ |
3831 | if (new_size == 0) { | 4096 | if (new_size == 0) { |
3832 | xfs_iext_destroy(ifp); | 4097 | xfs_iext_destroy(ifp); |
@@ -3920,13 +4185,76 @@ xfs_iext_inline_to_direct( | |||
3920 | } | 4185 | } |
3921 | 4186 | ||
3922 | /* | 4187 | /* |
4188 | * Resize an extent indirection array to new_size bytes. | ||
4189 | */ | ||
4190 | void | ||
4191 | xfs_iext_realloc_indirect( | ||
4192 | xfs_ifork_t *ifp, /* inode fork pointer */ | ||
4193 | int new_size) /* new indirection array size */ | ||
4194 | { | ||
4195 | int nlists; /* number of irec's (ex lists) */ | ||
4196 | int size; /* current indirection array size */ | ||
4197 | |||
4198 | ASSERT(ifp->if_flags & XFS_IFEXTIREC); | ||
4199 | nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ; | ||
4200 | size = nlists * sizeof(xfs_ext_irec_t); | ||
4201 | ASSERT(ifp->if_real_bytes); | ||
4202 | ASSERT((new_size >= 0) && (new_size != size)); | ||
4203 | if (new_size == 0) { | ||
4204 | xfs_iext_destroy(ifp); | ||
4205 | } else { | ||
4206 | ifp->if_u1.if_ext_irec = (xfs_ext_irec_t *) | ||
4207 | kmem_realloc(ifp->if_u1.if_ext_irec, | ||
4208 | new_size, size, KM_SLEEP); | ||
4209 | } | ||
4210 | } | ||
4211 | |||
4212 | /* | ||
4213 | * Switch from indirection array to linear (direct) extent allocations. | ||
4214 | */ | ||
4215 | void | ||
4216 | xfs_iext_indirect_to_direct( | ||
4217 | xfs_ifork_t *ifp) /* inode fork pointer */ | ||
4218 | { | ||
4219 | xfs_bmbt_rec_t *ep; /* extent record pointer */ | ||
4220 | xfs_extnum_t nextents; /* number of extents in file */ | ||
4221 | int size; /* size of file extents */ | ||
4222 | |||
4223 | ASSERT(ifp->if_flags & XFS_IFEXTIREC); | ||
4224 | nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t); | ||
4225 | ASSERT(nextents <= XFS_LINEAR_EXTS); | ||
4226 | size = nextents * sizeof(xfs_bmbt_rec_t); | ||
4227 | |||
4228 | xfs_iext_irec_compact_full(ifp); | ||
4229 | ASSERT(ifp->if_real_bytes == XFS_IEXT_BUFSZ); | ||
4230 | |||
4231 | ep = ifp->if_u1.if_ext_irec->er_extbuf; | ||
4232 | kmem_free(ifp->if_u1.if_ext_irec, sizeof(xfs_ext_irec_t)); | ||
4233 | ifp->if_flags &= ~XFS_IFEXTIREC; | ||
4234 | ifp->if_u1.if_extents = ep; | ||
4235 | ifp->if_bytes = size; | ||
4236 | if (nextents < XFS_LINEAR_EXTS) { | ||
4237 | xfs_iext_realloc_direct(ifp, size); | ||
4238 | } | ||
4239 | } | ||
4240 | |||
4241 | /* | ||
3923 | * Free incore file extents. | 4242 | * Free incore file extents. |
3924 | */ | 4243 | */ |
3925 | void | 4244 | void |
3926 | xfs_iext_destroy( | 4245 | xfs_iext_destroy( |
3927 | xfs_ifork_t *ifp) /* inode fork pointer */ | 4246 | xfs_ifork_t *ifp) /* inode fork pointer */ |
3928 | { | 4247 | { |
3929 | if (ifp->if_real_bytes) { | 4248 | if (ifp->if_flags & XFS_IFEXTIREC) { |
4249 | int erp_idx; | ||
4250 | int nlists; | ||
4251 | |||
4252 | nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ; | ||
4253 | for (erp_idx = nlists - 1; erp_idx >= 0 ; erp_idx--) { | ||
4254 | xfs_iext_irec_remove(ifp, erp_idx); | ||
4255 | } | ||
4256 | ifp->if_flags &= ~XFS_IFEXTIREC; | ||
4257 | } else if (ifp->if_real_bytes) { | ||
3930 | kmem_free(ifp->if_u1.if_extents, ifp->if_real_bytes); | 4258 | kmem_free(ifp->if_u1.if_extents, ifp->if_real_bytes); |
3931 | } else if (ifp->if_bytes) { | 4259 | } else if (ifp->if_bytes) { |
3932 | memset(ifp->if_u2.if_inline_ext, 0, XFS_INLINE_EXTS * | 4260 | memset(ifp->if_u2.if_inline_ext, 0, XFS_INLINE_EXTS * |
@@ -3936,3 +4264,380 @@ xfs_iext_destroy( | |||
3936 | ifp->if_real_bytes = 0; | 4264 | ifp->if_real_bytes = 0; |
3937 | ifp->if_bytes = 0; | 4265 | ifp->if_bytes = 0; |
3938 | } | 4266 | } |
4267 | |||
4268 | /* | ||
4269 | * Return a pointer to the indirection array entry containing the | ||
4270 | * extent record for filesystem block bno. Store the index of the | ||
4271 | * target irec in *erp_idxp. | ||
4272 | */ | ||
4273 | xfs_ext_irec_t * | ||
4274 | xfs_iext_bno_to_irec( | ||
4275 | xfs_ifork_t *ifp, /* inode fork pointer */ | ||
4276 | xfs_fileoff_t bno, /* block number to search for */ | ||
4277 | int *erp_idxp) /* irec index of target ext list */ | ||
4278 | { | ||
4279 | xfs_ext_irec_t *erp = NULL; /* indirection array pointer */ | ||
4280 | xfs_ext_irec_t *erp_next; /* next indirection array entry */ | ||
4281 | xfs_extnum_t erp_idx; /* indirection array index */ | ||
4282 | int nlists; /* number of extent irec's (lists) */ | ||
4283 | int high; /* binary search upper limit */ | ||
4284 | int low; /* binary search lower limit */ | ||
4285 | |||
4286 | ASSERT(ifp->if_flags & XFS_IFEXTIREC); | ||
4287 | nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ; | ||
4288 | erp_idx = 0; | ||
4289 | low = 0; | ||
4290 | high = nlists - 1; | ||
4291 | while (low <= high) { | ||
4292 | erp_idx = (low + high) >> 1; | ||
4293 | erp = &ifp->if_u1.if_ext_irec[erp_idx]; | ||
4294 | erp_next = erp_idx < nlists - 1 ? erp + 1 : NULL; | ||
4295 | if (bno < xfs_bmbt_get_startoff(erp->er_extbuf)) { | ||
4296 | high = erp_idx - 1; | ||
4297 | } else if (erp_next && bno >= | ||
4298 | xfs_bmbt_get_startoff(erp_next->er_extbuf)) { | ||
4299 | low = erp_idx + 1; | ||
4300 | } else { | ||
4301 | break; | ||
4302 | } | ||
4303 | } | ||
4304 | *erp_idxp = erp_idx; | ||
4305 | return erp; | ||
4306 | } | ||
4307 | |||
4308 | /* | ||
4309 | * Return a pointer to the indirection array entry containing the | ||
4310 | * extent record at file extent index *idxp. Store the index of the | ||
4311 | * target irec in *erp_idxp and store the page index of the target | ||
4312 | * extent record in *idxp. | ||
4313 | */ | ||
4314 | xfs_ext_irec_t * | ||
4315 | xfs_iext_idx_to_irec( | ||
4316 | xfs_ifork_t *ifp, /* inode fork pointer */ | ||
4317 | xfs_extnum_t *idxp, /* extent index (file -> page) */ | ||
4318 | int *erp_idxp, /* pointer to target irec */ | ||
4319 | int realloc) /* new bytes were just added */ | ||
4320 | { | ||
4321 | xfs_ext_irec_t *prev; /* pointer to previous irec */ | ||
4322 | xfs_ext_irec_t *erp = NULL; /* pointer to current irec */ | ||
4323 | int erp_idx; /* indirection array index */ | ||
4324 | int nlists; /* number of irec's (ex lists) */ | ||
4325 | int high; /* binary search upper limit */ | ||
4326 | int low; /* binary search lower limit */ | ||
4327 | xfs_extnum_t page_idx = *idxp; /* extent index in target list */ | ||
4328 | |||
4329 | ASSERT(ifp->if_flags & XFS_IFEXTIREC); | ||
4330 | ASSERT(page_idx >= 0 && page_idx <= | ||
4331 | ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t)); | ||
4332 | nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ; | ||
4333 | erp_idx = 0; | ||
4334 | low = 0; | ||
4335 | high = nlists - 1; | ||
4336 | |||
4337 | /* Binary search extent irec's */ | ||
4338 | while (low <= high) { | ||
4339 | erp_idx = (low + high) >> 1; | ||
4340 | erp = &ifp->if_u1.if_ext_irec[erp_idx]; | ||
4341 | prev = erp_idx > 0 ? erp - 1 : NULL; | ||
4342 | if (page_idx < erp->er_extoff || (page_idx == erp->er_extoff && | ||
4343 | realloc && prev && prev->er_extcount < XFS_LINEAR_EXTS)) { | ||
4344 | high = erp_idx - 1; | ||
4345 | } else if (page_idx > erp->er_extoff + erp->er_extcount || | ||
4346 | (page_idx == erp->er_extoff + erp->er_extcount && | ||
4347 | !realloc)) { | ||
4348 | low = erp_idx + 1; | ||
4349 | } else if (page_idx == erp->er_extoff + erp->er_extcount && | ||
4350 | erp->er_extcount == XFS_LINEAR_EXTS) { | ||
4351 | ASSERT(realloc); | ||
4352 | page_idx = 0; | ||
4353 | erp_idx++; | ||
4354 | erp = erp_idx < nlists ? erp + 1 : NULL; | ||
4355 | break; | ||
4356 | } else { | ||
4357 | page_idx -= erp->er_extoff; | ||
4358 | break; | ||
4359 | } | ||
4360 | } | ||
4361 | *idxp = page_idx; | ||
4362 | *erp_idxp = erp_idx; | ||
4363 | return(erp); | ||
4364 | } | ||
4365 | |||
4366 | /* | ||
4367 | * Allocate and initialize an indirection array once the space needed | ||
4368 | * for incore extents increases above XFS_IEXT_BUFSZ. | ||
4369 | */ | ||
4370 | void | ||
4371 | xfs_iext_irec_init( | ||
4372 | xfs_ifork_t *ifp) /* inode fork pointer */ | ||
4373 | { | ||
4374 | xfs_ext_irec_t *erp; /* indirection array pointer */ | ||
4375 | xfs_extnum_t nextents; /* number of extents in file */ | ||
4376 | |||
4377 | ASSERT(!(ifp->if_flags & XFS_IFEXTIREC)); | ||
4378 | nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t); | ||
4379 | ASSERT(nextents <= XFS_LINEAR_EXTS); | ||
4380 | |||
4381 | erp = (xfs_ext_irec_t *) | ||
4382 | kmem_alloc(sizeof(xfs_ext_irec_t), KM_SLEEP); | ||
4383 | |||
4384 | if (nextents == 0) { | ||
4385 | ifp->if_u1.if_extents = (xfs_bmbt_rec_t *) | ||
4386 | kmem_alloc(XFS_IEXT_BUFSZ, KM_SLEEP); | ||
4387 | } else if (!ifp->if_real_bytes) { | ||
4388 | xfs_iext_inline_to_direct(ifp, XFS_IEXT_BUFSZ); | ||
4389 | } else if (ifp->if_real_bytes < XFS_IEXT_BUFSZ) { | ||
4390 | xfs_iext_realloc_direct(ifp, XFS_IEXT_BUFSZ); | ||
4391 | } | ||
4392 | erp->er_extbuf = ifp->if_u1.if_extents; | ||
4393 | erp->er_extcount = nextents; | ||
4394 | erp->er_extoff = 0; | ||
4395 | |||
4396 | ifp->if_flags |= XFS_IFEXTIREC; | ||
4397 | ifp->if_real_bytes = XFS_IEXT_BUFSZ; | ||
4398 | ifp->if_bytes = nextents * sizeof(xfs_bmbt_rec_t); | ||
4399 | ifp->if_u1.if_ext_irec = erp; | ||
4400 | |||
4401 | return; | ||
4402 | } | ||
4403 | |||
4404 | /* | ||
4405 | * Allocate and initialize a new entry in the indirection array. | ||
4406 | */ | ||
4407 | xfs_ext_irec_t * | ||
4408 | xfs_iext_irec_new( | ||
4409 | xfs_ifork_t *ifp, /* inode fork pointer */ | ||
4410 | int erp_idx) /* index for new irec */ | ||
4411 | { | ||
4412 | xfs_ext_irec_t *erp; /* indirection array pointer */ | ||
4413 | int i; /* loop counter */ | ||
4414 | int nlists; /* number of irec's (ex lists) */ | ||
4415 | |||
4416 | ASSERT(ifp->if_flags & XFS_IFEXTIREC); | ||
4417 | nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ; | ||
4418 | |||
4419 | /* Resize indirection array */ | ||
4420 | xfs_iext_realloc_indirect(ifp, ++nlists * | ||
4421 | sizeof(xfs_ext_irec_t)); | ||
4422 | /* | ||
4423 | * Move records down in the array so the | ||
4424 | * new page can use erp_idx. | ||
4425 | */ | ||
4426 | erp = ifp->if_u1.if_ext_irec; | ||
4427 | for (i = nlists - 1; i > erp_idx; i--) { | ||
4428 | memmove(&erp[i], &erp[i-1], sizeof(xfs_ext_irec_t)); | ||
4429 | } | ||
4430 | ASSERT(i == erp_idx); | ||
4431 | |||
4432 | /* Initialize new extent record */ | ||
4433 | erp = ifp->if_u1.if_ext_irec; | ||
4434 | erp[erp_idx].er_extbuf = (xfs_bmbt_rec_t *) | ||
4435 | kmem_alloc(XFS_IEXT_BUFSZ, KM_SLEEP); | ||
4436 | ifp->if_real_bytes = nlists * XFS_IEXT_BUFSZ; | ||
4437 | memset(erp[erp_idx].er_extbuf, 0, XFS_IEXT_BUFSZ); | ||
4438 | erp[erp_idx].er_extcount = 0; | ||
4439 | erp[erp_idx].er_extoff = erp_idx > 0 ? | ||
4440 | erp[erp_idx-1].er_extoff + erp[erp_idx-1].er_extcount : 0; | ||
4441 | return (&erp[erp_idx]); | ||
4442 | } | ||
4443 | |||
4444 | /* | ||
4445 | * Remove a record from the indirection array. | ||
4446 | */ | ||
4447 | void | ||
4448 | xfs_iext_irec_remove( | ||
4449 | xfs_ifork_t *ifp, /* inode fork pointer */ | ||
4450 | int erp_idx) /* irec index to remove */ | ||
4451 | { | ||
4452 | xfs_ext_irec_t *erp; /* indirection array pointer */ | ||
4453 | int i; /* loop counter */ | ||
4454 | int nlists; /* number of irec's (ex lists) */ | ||
4455 | |||
4456 | ASSERT(ifp->if_flags & XFS_IFEXTIREC); | ||
4457 | nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ; | ||
4458 | erp = &ifp->if_u1.if_ext_irec[erp_idx]; | ||
4459 | if (erp->er_extbuf) { | ||
4460 | xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, | ||
4461 | -erp->er_extcount); | ||
4462 | kmem_free(erp->er_extbuf, XFS_IEXT_BUFSZ); | ||
4463 | } | ||
4464 | /* Compact extent records */ | ||
4465 | erp = ifp->if_u1.if_ext_irec; | ||
4466 | for (i = erp_idx; i < nlists - 1; i++) { | ||
4467 | memmove(&erp[i], &erp[i+1], sizeof(xfs_ext_irec_t)); | ||
4468 | } | ||
4469 | /* | ||
4470 | * Manually free the last extent record from the indirection | ||
4471 | * array. A call to xfs_iext_realloc_indirect() with a size | ||
4472 | * of zero would result in a call to xfs_iext_destroy() which | ||
4473 | * would in turn call this function again, creating a nasty | ||
4474 | * infinite loop. | ||
4475 | */ | ||
4476 | if (--nlists) { | ||
4477 | xfs_iext_realloc_indirect(ifp, | ||
4478 | nlists * sizeof(xfs_ext_irec_t)); | ||
4479 | } else { | ||
4480 | kmem_free(ifp->if_u1.if_ext_irec, | ||
4481 | sizeof(xfs_ext_irec_t)); | ||
4482 | } | ||
4483 | ifp->if_real_bytes = nlists * XFS_IEXT_BUFSZ; | ||
4484 | } | ||
4485 | |||
4486 | /* | ||
4487 | * This is called to clean up large amounts of unused memory allocated | ||
4488 | * by the indirection array. Before compacting anything though, verify | ||
4489 | * that the indirection array is still needed and switch back to the | ||
4490 | * linear extent list (or even the inline buffer) if possible. The | ||
4491 | * compaction policy is as follows: | ||
4492 | * | ||
4493 | * Full Compaction: Extents fit into a single page (or inline buffer) | ||
4494 | * Full Compaction: Extents occupy less than 10% of allocated space | ||
4495 | * Partial Compaction: Extents occupy > 10% and < 50% of allocated space | ||
4496 | * No Compaction: Extents occupy at least 50% of allocated space | ||
4497 | */ | ||
4498 | void | ||
4499 | xfs_iext_irec_compact( | ||
4500 | xfs_ifork_t *ifp) /* inode fork pointer */ | ||
4501 | { | ||
4502 | xfs_extnum_t nextents; /* number of extents in file */ | ||
4503 | int nlists; /* number of irec's (ex lists) */ | ||
4504 | |||
4505 | ASSERT(ifp->if_flags & XFS_IFEXTIREC); | ||
4506 | nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ; | ||
4507 | nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t); | ||
4508 | |||
4509 | if (nextents == 0) { | ||
4510 | xfs_iext_destroy(ifp); | ||
4511 | } else if (nextents <= XFS_INLINE_EXTS) { | ||
4512 | xfs_iext_indirect_to_direct(ifp); | ||
4513 | xfs_iext_direct_to_inline(ifp, nextents); | ||
4514 | } else if (nextents <= XFS_LINEAR_EXTS) { | ||
4515 | xfs_iext_indirect_to_direct(ifp); | ||
4516 | } else if (nextents < (nlists * XFS_LINEAR_EXTS) >> 3) { | ||
4517 | xfs_iext_irec_compact_full(ifp); | ||
4518 | } else if (nextents < (nlists * XFS_LINEAR_EXTS) >> 1) { | ||
4519 | xfs_iext_irec_compact_pages(ifp); | ||
4520 | } | ||
4521 | } | ||
4522 | |||
4523 | /* | ||
4524 | * Combine extents from neighboring extent pages. | ||
4525 | */ | ||
4526 | void | ||
4527 | xfs_iext_irec_compact_pages( | ||
4528 | xfs_ifork_t *ifp) /* inode fork pointer */ | ||
4529 | { | ||
4530 | xfs_ext_irec_t *erp, *erp_next;/* pointers to irec entries */ | ||
4531 | int erp_idx = 0; /* indirection array index */ | ||
4532 | int nlists; /* number of irec's (ex lists) */ | ||
4533 | |||
4534 | ASSERT(ifp->if_flags & XFS_IFEXTIREC); | ||
4535 | nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ; | ||
4536 | while (erp_idx < nlists - 1) { | ||
4537 | erp = &ifp->if_u1.if_ext_irec[erp_idx]; | ||
4538 | erp_next = erp + 1; | ||
4539 | if (erp_next->er_extcount <= | ||
4540 | (XFS_LINEAR_EXTS - erp->er_extcount)) { | ||
4541 | memmove(&erp->er_extbuf[erp->er_extcount], | ||
4542 | erp_next->er_extbuf, erp_next->er_extcount * | ||
4543 | sizeof(xfs_bmbt_rec_t)); | ||
4544 | erp->er_extcount += erp_next->er_extcount; | ||
4545 | /* | ||
4546 | * Free page before removing extent record | ||
4547 | * so er_extoffs don't get modified in | ||
4548 | * xfs_iext_irec_remove. | ||
4549 | */ | ||
4550 | kmem_free(erp_next->er_extbuf, XFS_IEXT_BUFSZ); | ||
4551 | erp_next->er_extbuf = NULL; | ||
4552 | xfs_iext_irec_remove(ifp, erp_idx + 1); | ||
4553 | nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ; | ||
4554 | } else { | ||
4555 | erp_idx++; | ||
4556 | } | ||
4557 | } | ||
4558 | } | ||
4559 | |||
4560 | /* | ||
4561 | * Fully compact the extent records managed by the indirection array. | ||
4562 | */ | ||
4563 | void | ||
4564 | xfs_iext_irec_compact_full( | ||
4565 | xfs_ifork_t *ifp) /* inode fork pointer */ | ||
4566 | { | ||
4567 | xfs_bmbt_rec_t *ep, *ep_next; /* extent record pointers */ | ||
4568 | xfs_ext_irec_t *erp, *erp_next; /* extent irec pointers */ | ||
4569 | int erp_idx = 0; /* extent irec index */ | ||
4570 | int ext_avail; /* empty entries in ex list */ | ||
4571 | int ext_diff; /* number of exts to add */ | ||
4572 | int nlists; /* number of irec's (ex lists) */ | ||
4573 | |||
4574 | ASSERT(ifp->if_flags & XFS_IFEXTIREC); | ||
4575 | nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ; | ||
4576 | erp = ifp->if_u1.if_ext_irec; | ||
4577 | ep = &erp->er_extbuf[erp->er_extcount]; | ||
4578 | erp_next = erp + 1; | ||
4579 | ep_next = erp_next->er_extbuf; | ||
4580 | while (erp_idx < nlists - 1) { | ||
4581 | ext_avail = XFS_LINEAR_EXTS - erp->er_extcount; | ||
4582 | ext_diff = MIN(ext_avail, erp_next->er_extcount); | ||
4583 | memcpy(ep, ep_next, ext_diff * sizeof(xfs_bmbt_rec_t)); | ||
4584 | erp->er_extcount += ext_diff; | ||
4585 | erp_next->er_extcount -= ext_diff; | ||
4586 | /* Remove next page */ | ||
4587 | if (erp_next->er_extcount == 0) { | ||
4588 | /* | ||
4589 | * Free page before removing extent record | ||
4590 | * so er_extoffs don't get modified in | ||
4591 | * xfs_iext_irec_remove. | ||
4592 | */ | ||
4593 | kmem_free(erp_next->er_extbuf, | ||
4594 | erp_next->er_extcount * sizeof(xfs_bmbt_rec_t)); | ||
4595 | erp_next->er_extbuf = NULL; | ||
4596 | xfs_iext_irec_remove(ifp, erp_idx + 1); | ||
4597 | erp = &ifp->if_u1.if_ext_irec[erp_idx]; | ||
4598 | nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ; | ||
4599 | /* Update next page */ | ||
4600 | } else { | ||
4601 | /* Move rest of page up to become next new page */ | ||
4602 | memmove(erp_next->er_extbuf, ep_next, | ||
4603 | erp_next->er_extcount * sizeof(xfs_bmbt_rec_t)); | ||
4604 | ep_next = erp_next->er_extbuf; | ||
4605 | memset(&ep_next[erp_next->er_extcount], 0, | ||
4606 | (XFS_LINEAR_EXTS - erp_next->er_extcount) * | ||
4607 | sizeof(xfs_bmbt_rec_t)); | ||
4608 | } | ||
4609 | if (erp->er_extcount == XFS_LINEAR_EXTS) { | ||
4610 | erp_idx++; | ||
4611 | if (erp_idx < nlists) | ||
4612 | erp = &ifp->if_u1.if_ext_irec[erp_idx]; | ||
4613 | else | ||
4614 | break; | ||
4615 | } | ||
4616 | ep = &erp->er_extbuf[erp->er_extcount]; | ||
4617 | erp_next = erp + 1; | ||
4618 | ep_next = erp_next->er_extbuf; | ||
4619 | } | ||
4620 | } | ||
4621 | |||
4622 | /* | ||
4623 | * This is called to update the er_extoff field in the indirection | ||
4624 | * array when extents have been added or removed from one of the | ||
4625 | * extent lists. erp_idx contains the irec index to begin updating | ||
4626 | * at and ext_diff contains the number of extents that were added | ||
4627 | * or removed. | ||
4628 | */ | ||
4629 | void | ||
4630 | xfs_iext_irec_update_extoffs( | ||
4631 | xfs_ifork_t *ifp, /* inode fork pointer */ | ||
4632 | int erp_idx, /* irec index to update */ | ||
4633 | int ext_diff) /* number of new extents */ | ||
4634 | { | ||
4635 | int i; /* loop counter */ | ||
4636 | int nlists; /* number of irec's (ex lists */ | ||
4637 | |||
4638 | ASSERT(ifp->if_flags & XFS_IFEXTIREC); | ||
4639 | nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ; | ||
4640 | for (i = erp_idx; i < nlists; i++) { | ||
4641 | ifp->if_u1.if_ext_irec[i].er_extoff += ext_diff; | ||
4642 | } | ||
4643 | } | ||
diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index 740b73fabd2f..3c1df1d642fa 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h | |||
@@ -25,10 +25,37 @@ | |||
25 | #define XFS_ATTR_FORK 1 | 25 | #define XFS_ATTR_FORK 1 |
26 | 26 | ||
27 | /* | 27 | /* |
28 | * The following xfs_ext_irec_t struct introduces a second (top) level | ||
29 | * to the in-core extent allocation scheme. These structs are allocated | ||
30 | * in a contiguous block, creating an indirection array where each entry | ||
31 | * (irec) contains a pointer to a buffer of in-core extent records which | ||
32 | * it manages. Each extent buffer is 4k in size, since 4k is the system | ||
33 | * page size on Linux i386 and systems with larger page sizes don't seem | ||
34 | * to gain much, if anything, by using their native page size as the | ||
35 | * extent buffer size. Also, using 4k extent buffers everywhere provides | ||
36 | * a consistent interface for CXFS across different platforms. | ||
37 | * | ||
38 | * There is currently no limit on the number of irec's (extent lists) | ||
39 | * allowed, so heavily fragmented files may require an indirection array | ||
40 | * which spans multiple system pages of memory. The number of extents | ||
41 | * which would require this amount of contiguous memory is very large | ||
42 | * and should not cause problems in the foreseeable future. However, | ||
43 | * if the memory needed for the contiguous array ever becomes a problem, | ||
44 | * it is possible that a third level of indirection may be required. | ||
45 | */ | ||
46 | typedef struct xfs_ext_irec { | ||
47 | xfs_bmbt_rec_t *er_extbuf; /* block of extent records */ | ||
48 | xfs_extnum_t er_extoff; /* extent offset in file */ | ||
49 | xfs_extnum_t er_extcount; /* number of extents in page/block */ | ||
50 | } xfs_ext_irec_t; | ||
51 | |||
52 | /* | ||
28 | * File incore extent information, present for each of data & attr forks. | 53 | * File incore extent information, present for each of data & attr forks. |
29 | */ | 54 | */ |
30 | #define XFS_INLINE_EXTS 2 | 55 | #define XFS_IEXT_BUFSZ 4096 |
31 | #define XFS_INLINE_DATA 32 | 56 | #define XFS_LINEAR_EXTS (XFS_IEXT_BUFSZ / (uint)sizeof(xfs_bmbt_rec_t)) |
57 | #define XFS_INLINE_EXTS 2 | ||
58 | #define XFS_INLINE_DATA 32 | ||
32 | typedef struct xfs_ifork { | 59 | typedef struct xfs_ifork { |
33 | int if_bytes; /* bytes in if_u1 */ | 60 | int if_bytes; /* bytes in if_u1 */ |
34 | int if_real_bytes; /* bytes allocated in if_u1 */ | 61 | int if_real_bytes; /* bytes allocated in if_u1 */ |
@@ -39,6 +66,7 @@ typedef struct xfs_ifork { | |||
39 | xfs_extnum_t if_lastex; /* last if_extents used */ | 66 | xfs_extnum_t if_lastex; /* last if_extents used */ |
40 | union { | 67 | union { |
41 | xfs_bmbt_rec_t *if_extents; /* linear map file exts */ | 68 | xfs_bmbt_rec_t *if_extents; /* linear map file exts */ |
69 | xfs_ext_irec_t *if_ext_irec; /* irec map file exts */ | ||
42 | char *if_data; /* inline file data */ | 70 | char *if_data; /* inline file data */ |
43 | } if_u1; | 71 | } if_u1; |
44 | union { | 72 | union { |
@@ -61,9 +89,10 @@ typedef struct xfs_ifork { | |||
61 | /* | 89 | /* |
62 | * Per-fork incore inode flags. | 90 | * Per-fork incore inode flags. |
63 | */ | 91 | */ |
64 | #define XFS_IFINLINE 0x0001 /* Inline data is read in */ | 92 | #define XFS_IFINLINE 0x01 /* Inline data is read in */ |
65 | #define XFS_IFEXTENTS 0x0002 /* All extent pointers are read in */ | 93 | #define XFS_IFEXTENTS 0x02 /* All extent pointers are read in */ |
66 | #define XFS_IFBROOT 0x0004 /* i_broot points to the bmap b-tree root */ | 94 | #define XFS_IFBROOT 0x04 /* i_broot points to the bmap b-tree root */ |
95 | #define XFS_IFEXTIREC 0x08 /* Indirection array of extent blocks */ | ||
67 | 96 | ||
68 | /* | 97 | /* |
69 | * Flags for xfs_imap() and xfs_dilocate(). | 98 | * Flags for xfs_imap() and xfs_dilocate(). |
@@ -438,13 +467,26 @@ xfs_bmbt_rec_t *xfs_iext_get_ext(xfs_ifork_t *, xfs_extnum_t); | |||
438 | void xfs_iext_insert(xfs_ifork_t *, xfs_extnum_t, xfs_extnum_t, | 467 | void xfs_iext_insert(xfs_ifork_t *, xfs_extnum_t, xfs_extnum_t, |
439 | xfs_bmbt_irec_t *); | 468 | xfs_bmbt_irec_t *); |
440 | void xfs_iext_add(xfs_ifork_t *, xfs_extnum_t, int); | 469 | void xfs_iext_add(xfs_ifork_t *, xfs_extnum_t, int); |
470 | void xfs_iext_add_indirect_multi(xfs_ifork_t *, int, xfs_extnum_t, int); | ||
441 | void xfs_iext_remove(xfs_ifork_t *, xfs_extnum_t, int); | 471 | void xfs_iext_remove(xfs_ifork_t *, xfs_extnum_t, int); |
442 | void xfs_iext_remove_inline(xfs_ifork_t *, xfs_extnum_t, int); | 472 | void xfs_iext_remove_inline(xfs_ifork_t *, xfs_extnum_t, int); |
443 | void xfs_iext_remove_direct(xfs_ifork_t *, xfs_extnum_t, int); | 473 | void xfs_iext_remove_direct(xfs_ifork_t *, xfs_extnum_t, int); |
474 | void xfs_iext_remove_indirect(xfs_ifork_t *, xfs_extnum_t, int); | ||
444 | void xfs_iext_realloc_direct(xfs_ifork_t *, int); | 475 | void xfs_iext_realloc_direct(xfs_ifork_t *, int); |
476 | void xfs_iext_realloc_indirect(xfs_ifork_t *, int); | ||
477 | void xfs_iext_indirect_to_direct(xfs_ifork_t *); | ||
445 | void xfs_iext_direct_to_inline(xfs_ifork_t *, xfs_extnum_t); | 478 | void xfs_iext_direct_to_inline(xfs_ifork_t *, xfs_extnum_t); |
446 | void xfs_iext_inline_to_direct(xfs_ifork_t *, int); | 479 | void xfs_iext_inline_to_direct(xfs_ifork_t *, int); |
447 | void xfs_iext_destroy(xfs_ifork_t *); | 480 | void xfs_iext_destroy(xfs_ifork_t *); |
481 | xfs_ext_irec_t *xfs_iext_bno_to_irec(xfs_ifork_t *, xfs_fileoff_t, int *); | ||
482 | xfs_ext_irec_t *xfs_iext_idx_to_irec(xfs_ifork_t *, xfs_extnum_t *, int *, int); | ||
483 | void xfs_iext_irec_init(xfs_ifork_t *); | ||
484 | xfs_ext_irec_t *xfs_iext_irec_new(xfs_ifork_t *, int); | ||
485 | void xfs_iext_irec_remove(xfs_ifork_t *, int); | ||
486 | void xfs_iext_irec_compact(xfs_ifork_t *); | ||
487 | void xfs_iext_irec_compact_pages(xfs_ifork_t *); | ||
488 | void xfs_iext_irec_compact_full(xfs_ifork_t *); | ||
489 | void xfs_iext_irec_update_extoffs(xfs_ifork_t *, int, int); | ||
448 | 490 | ||
449 | #define xfs_ipincount(ip) ((unsigned int) atomic_read(&ip->i_pincount)) | 491 | #define xfs_ipincount(ip) ((unsigned int) atomic_read(&ip->i_pincount)) |
450 | 492 | ||