@@ -815,11 +815,136 @@ xfs_refcount_find_right_extents(
815815/* Is this extent valid? */
816816static inline bool
817817xfs_refc_valid (
818- struct xfs_refcount_irec * rc )
818+ const struct xfs_refcount_irec * rc )
819819{
820820 return rc -> rc_startblock != NULLAGBLOCK ;
821821}
822822
823+ static inline xfs_nlink_t
824+ xfs_refc_merge_refcount (
825+ const struct xfs_refcount_irec * irec ,
826+ enum xfs_refc_adjust_op adjust )
827+ {
828+ /* Once a record hits MAXREFCOUNT, it is pinned there forever */
829+ if (irec -> rc_refcount == MAXREFCOUNT )
830+ return MAXREFCOUNT ;
831+ return irec -> rc_refcount + adjust ;
832+ }
833+
834+ static inline bool
835+ xfs_refc_want_merge_center (
836+ const struct xfs_refcount_irec * left ,
837+ const struct xfs_refcount_irec * cleft ,
838+ const struct xfs_refcount_irec * cright ,
839+ const struct xfs_refcount_irec * right ,
840+ bool cleft_is_cright ,
841+ enum xfs_refc_adjust_op adjust ,
842+ unsigned long long * ulenp )
843+ {
844+ unsigned long long ulen = left -> rc_blockcount ;
845+ xfs_nlink_t new_refcount ;
846+
847+ /*
848+ * To merge with a center record, both shoulder records must be
849+ * adjacent to the record we want to adjust. This is only true if
850+ * find_left and find_right made all four records valid.
851+ */
852+ if (!xfs_refc_valid (left ) || !xfs_refc_valid (right ) ||
853+ !xfs_refc_valid (cleft ) || !xfs_refc_valid (cright ))
854+ return false;
855+
856+ /* There must only be one record for the entire range. */
857+ if (!cleft_is_cright )
858+ return false;
859+
860+ /* The shoulder record refcounts must match the new refcount. */
861+ new_refcount = xfs_refc_merge_refcount (cleft , adjust );
862+ if (left -> rc_refcount != new_refcount )
863+ return false;
864+ if (right -> rc_refcount != new_refcount )
865+ return false;
866+
867+ /*
868+ * The new record cannot exceed the max length. ulen is a ULL as the
869+ * individual record block counts can be up to (u32 - 1) in length
870+ * hence we need to catch u32 addition overflows here.
871+ */
872+ ulen += cleft -> rc_blockcount + right -> rc_blockcount ;
873+ if (ulen >= MAXREFCEXTLEN )
874+ return false;
875+
876+ * ulenp = ulen ;
877+ return true;
878+ }
879+
880+ static inline bool
881+ xfs_refc_want_merge_left (
882+ const struct xfs_refcount_irec * left ,
883+ const struct xfs_refcount_irec * cleft ,
884+ enum xfs_refc_adjust_op adjust )
885+ {
886+ unsigned long long ulen = left -> rc_blockcount ;
887+ xfs_nlink_t new_refcount ;
888+
889+ /*
890+ * For a left merge, the left shoulder record must be adjacent to the
891+ * start of the range. If this is true, find_left made left and cleft
892+ * contain valid contents.
893+ */
894+ if (!xfs_refc_valid (left ) || !xfs_refc_valid (cleft ))
895+ return false;
896+
897+ /* Left shoulder record refcount must match the new refcount. */
898+ new_refcount = xfs_refc_merge_refcount (cleft , adjust );
899+ if (left -> rc_refcount != new_refcount )
900+ return false;
901+
902+ /*
903+ * The new record cannot exceed the max length. ulen is a ULL as the
904+ * individual record block counts can be up to (u32 - 1) in length
905+ * hence we need to catch u32 addition overflows here.
906+ */
907+ ulen += cleft -> rc_blockcount ;
908+ if (ulen >= MAXREFCEXTLEN )
909+ return false;
910+
911+ return true;
912+ }
913+
914+ static inline bool
915+ xfs_refc_want_merge_right (
916+ const struct xfs_refcount_irec * cright ,
917+ const struct xfs_refcount_irec * right ,
918+ enum xfs_refc_adjust_op adjust )
919+ {
920+ unsigned long long ulen = right -> rc_blockcount ;
921+ xfs_nlink_t new_refcount ;
922+
923+ /*
924+ * For a right merge, the right shoulder record must be adjacent to the
925+ * end of the range. If this is true, find_right made cright and right
926+ * contain valid contents.
927+ */
928+ if (!xfs_refc_valid (right ) || !xfs_refc_valid (cright ))
929+ return false;
930+
931+ /* Right shoulder record refcount must match the new refcount. */
932+ new_refcount = xfs_refc_merge_refcount (cright , adjust );
933+ if (right -> rc_refcount != new_refcount )
934+ return false;
935+
936+ /*
937+ * The new record cannot exceed the max length. ulen is a ULL as the
938+ * individual record block counts can be up to (u32 - 1) in length
939+ * hence we need to catch u32 addition overflows here.
940+ */
941+ ulen += cright -> rc_blockcount ;
942+ if (ulen >= MAXREFCEXTLEN )
943+ return false;
944+
945+ return true;
946+ }
947+
823948/*
824949 * Try to merge with any extents on the boundaries of the adjustment range.
825950 */
@@ -861,23 +986,15 @@ xfs_refcount_merge_extents(
861986 (cleft .rc_blockcount == cright .rc_blockcount );
862987
863988 /* Try to merge left, cleft, and right. cleft must == cright. */
864- ulen = (unsigned long long )left .rc_blockcount + cleft .rc_blockcount +
865- right .rc_blockcount ;
866- if (xfs_refc_valid (& left ) && xfs_refc_valid (& right ) &&
867- xfs_refc_valid (& cleft ) && xfs_refc_valid (& cright ) && cequal &&
868- left .rc_refcount == cleft .rc_refcount + adjust &&
869- right .rc_refcount == cleft .rc_refcount + adjust &&
870- ulen < MAXREFCEXTLEN ) {
989+ if (xfs_refc_want_merge_center (& left , & cleft , & cright , & right , cequal ,
990+ adjust , & ulen )) {
871991 * shape_changed = true;
872992 return xfs_refcount_merge_center_extents (cur , & left , & cleft ,
873993 & right , ulen , aglen );
874994 }
875995
876996 /* Try to merge left and cleft. */
877- ulen = (unsigned long long )left .rc_blockcount + cleft .rc_blockcount ;
878- if (xfs_refc_valid (& left ) && xfs_refc_valid (& cleft ) &&
879- left .rc_refcount == cleft .rc_refcount + adjust &&
880- ulen < MAXREFCEXTLEN ) {
997+ if (xfs_refc_want_merge_left (& left , & cleft , adjust )) {
881998 * shape_changed = true;
882999 error = xfs_refcount_merge_left_extent (cur , & left , & cleft ,
8831000 agbno , aglen );
@@ -893,10 +1010,7 @@ xfs_refcount_merge_extents(
8931010 }
8941011
8951012 /* Try to merge cright and right. */
896- ulen = (unsigned long long )right .rc_blockcount + cright .rc_blockcount ;
897- if (xfs_refc_valid (& right ) && xfs_refc_valid (& cright ) &&
898- right .rc_refcount == cright .rc_refcount + adjust &&
899- ulen < MAXREFCEXTLEN ) {
1013+ if (xfs_refc_want_merge_right (& cright , & right , adjust )) {
9001014 * shape_changed = true;
9011015 return xfs_refcount_merge_right_extent (cur , & right , & cright ,
9021016 aglen );
0 commit comments