diff options
Diffstat (limited to 'fs/xfs/xfs_inode.c')
-rw-r--r-- | fs/xfs/xfs_inode.c | 715 |
1 files changed, 710 insertions, 5 deletions
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 | } | ||