@@ -341,9 +341,41 @@ xfs_find_trim_cow_extent(
341341 return 0 ;
342342}
343343
344- /* Allocate all CoW reservations covering a range of blocks in a file. */
345- int
346- xfs_reflink_allocate_cow (
344+ static int
345+ xfs_reflink_convert_unwritten (
346+ struct xfs_inode * ip ,
347+ struct xfs_bmbt_irec * imap ,
348+ struct xfs_bmbt_irec * cmap ,
349+ bool convert_now )
350+ {
351+ xfs_fileoff_t offset_fsb = imap -> br_startoff ;
352+ xfs_filblks_t count_fsb = imap -> br_blockcount ;
353+ int error ;
354+
355+ /*
356+ * cmap might larger than imap due to cowextsize hint.
357+ */
358+ xfs_trim_extent (cmap , offset_fsb , count_fsb );
359+
360+ /*
361+ * COW fork extents are supposed to remain unwritten until we're ready
362+ * to initiate a disk write. For direct I/O we are going to write the
363+ * data and need the conversion, but for buffered writes we're done.
364+ */
365+ if (!convert_now || cmap -> br_state == XFS_EXT_NORM )
366+ return 0 ;
367+
368+ trace_xfs_reflink_convert_cow (ip , cmap );
369+
370+ error = xfs_reflink_convert_cow_locked (ip , offset_fsb , count_fsb );
371+ if (!error )
372+ cmap -> br_state = XFS_EXT_NORM ;
373+
374+ return error ;
375+ }
376+
377+ static int
378+ xfs_reflink_fill_cow_hole (
347379 struct xfs_inode * ip ,
348380 struct xfs_bmbt_irec * imap ,
349381 struct xfs_bmbt_irec * cmap ,
@@ -352,25 +384,12 @@ xfs_reflink_allocate_cow(
352384 bool convert_now )
353385{
354386 struct xfs_mount * mp = ip -> i_mount ;
355- xfs_fileoff_t offset_fsb = imap -> br_startoff ;
356- xfs_filblks_t count_fsb = imap -> br_blockcount ;
357387 struct xfs_trans * tp ;
358- int nimaps , error = 0 ;
359- bool found ;
360388 xfs_filblks_t resaligned ;
361- xfs_extlen_t resblks = 0 ;
362-
363- ASSERT (xfs_isilocked (ip , XFS_ILOCK_EXCL ));
364- if (!ip -> i_cowfp ) {
365- ASSERT (!xfs_is_reflink_inode (ip ));
366- xfs_ifork_init_cow (ip );
367- }
368-
369- error = xfs_find_trim_cow_extent (ip , imap , cmap , shared , & found );
370- if (error || !* shared )
371- return error ;
372- if (found )
373- goto convert ;
389+ xfs_extlen_t resblks ;
390+ int nimaps ;
391+ int error ;
392+ bool found ;
374393
375394 resaligned = xfs_aligned_fsb_count (imap -> br_startoff ,
376395 imap -> br_blockcount , xfs_get_cowextsz_hint (ip ));
@@ -386,17 +405,17 @@ xfs_reflink_allocate_cow(
386405
387406 * lockmode = XFS_ILOCK_EXCL ;
388407
389- /*
390- * Check for an overlapping extent again now that we dropped the ilock.
391- */
392408 error = xfs_find_trim_cow_extent (ip , imap , cmap , shared , & found );
393409 if (error || !* shared )
394410 goto out_trans_cancel ;
411+
395412 if (found ) {
396413 xfs_trans_cancel (tp );
397414 goto convert ;
398415 }
399416
417+ ASSERT (cmap -> br_startoff > imap -> br_startoff );
418+
400419 /* Allocate the entire reservation as unwritten blocks. */
401420 nimaps = 1 ;
402421 error = xfs_bmapi_write (tp , ip , imap -> br_startoff , imap -> br_blockcount ,
@@ -416,26 +435,135 @@ xfs_reflink_allocate_cow(
416435 */
417436 if (nimaps == 0 )
418437 return - ENOSPC ;
438+
419439convert :
420- xfs_trim_extent (cmap , offset_fsb , count_fsb );
421- /*
422- * COW fork extents are supposed to remain unwritten until we're ready
423- * to initiate a disk write. For direct I/O we are going to write the
424- * data and need the conversion, but for buffered writes we're done.
425- */
426- if (!convert_now || cmap -> br_state == XFS_EXT_NORM )
427- return 0 ;
428- trace_xfs_reflink_convert_cow (ip , cmap );
429- error = xfs_reflink_convert_cow_locked (ip , offset_fsb , count_fsb );
430- if (!error )
431- cmap -> br_state = XFS_EXT_NORM ;
440+ return xfs_reflink_convert_unwritten (ip , imap , cmap , convert_now );
441+
442+ out_trans_cancel :
443+ xfs_trans_cancel (tp );
432444 return error ;
445+ }
446+
447+ static int
448+ xfs_reflink_fill_delalloc (
449+ struct xfs_inode * ip ,
450+ struct xfs_bmbt_irec * imap ,
451+ struct xfs_bmbt_irec * cmap ,
452+ bool * shared ,
453+ uint * lockmode ,
454+ bool convert_now )
455+ {
456+ struct xfs_mount * mp = ip -> i_mount ;
457+ struct xfs_trans * tp ;
458+ int nimaps ;
459+ int error ;
460+ bool found ;
461+
462+ do {
463+ xfs_iunlock (ip , * lockmode );
464+ * lockmode = 0 ;
465+
466+ error = xfs_trans_alloc_inode (ip , & M_RES (mp )-> tr_write , 0 , 0 ,
467+ false, & tp );
468+ if (error )
469+ return error ;
470+
471+ * lockmode = XFS_ILOCK_EXCL ;
472+
473+ error = xfs_find_trim_cow_extent (ip , imap , cmap , shared ,
474+ & found );
475+ if (error || !* shared )
476+ goto out_trans_cancel ;
477+
478+ if (found ) {
479+ xfs_trans_cancel (tp );
480+ break ;
481+ }
482+
483+ ASSERT (isnullstartblock (cmap -> br_startblock ) ||
484+ cmap -> br_startblock == DELAYSTARTBLOCK );
485+
486+ /*
487+ * Replace delalloc reservation with an unwritten extent.
488+ */
489+ nimaps = 1 ;
490+ error = xfs_bmapi_write (tp , ip , cmap -> br_startoff ,
491+ cmap -> br_blockcount ,
492+ XFS_BMAPI_COWFORK | XFS_BMAPI_PREALLOC , 0 ,
493+ cmap , & nimaps );
494+ if (error )
495+ goto out_trans_cancel ;
496+
497+ xfs_inode_set_cowblocks_tag (ip );
498+ error = xfs_trans_commit (tp );
499+ if (error )
500+ return error ;
501+
502+ /*
503+ * Allocation succeeded but the requested range was not even
504+ * partially satisfied? Bail out!
505+ */
506+ if (nimaps == 0 )
507+ return - ENOSPC ;
508+ } while (cmap -> br_startoff + cmap -> br_blockcount <= imap -> br_startoff );
509+
510+ return xfs_reflink_convert_unwritten (ip , imap , cmap , convert_now );
433511
434512out_trans_cancel :
435513 xfs_trans_cancel (tp );
436514 return error ;
437515}
438516
517+ /* Allocate all CoW reservations covering a range of blocks in a file. */
518+ int
519+ xfs_reflink_allocate_cow (
520+ struct xfs_inode * ip ,
521+ struct xfs_bmbt_irec * imap ,
522+ struct xfs_bmbt_irec * cmap ,
523+ bool * shared ,
524+ uint * lockmode ,
525+ bool convert_now )
526+ {
527+ int error ;
528+ bool found ;
529+
530+ ASSERT (xfs_isilocked (ip , XFS_ILOCK_EXCL ));
531+ if (!ip -> i_cowfp ) {
532+ ASSERT (!xfs_is_reflink_inode (ip ));
533+ xfs_ifork_init_cow (ip );
534+ }
535+
536+ error = xfs_find_trim_cow_extent (ip , imap , cmap , shared , & found );
537+ if (error || !* shared )
538+ return error ;
539+
540+ /* CoW fork has a real extent */
541+ if (found )
542+ return xfs_reflink_convert_unwritten (ip , imap , cmap ,
543+ convert_now );
544+
545+ /*
546+ * CoW fork does not have an extent and data extent is shared.
547+ * Allocate a real extent in the CoW fork.
548+ */
549+ if (cmap -> br_startoff > imap -> br_startoff )
550+ return xfs_reflink_fill_cow_hole (ip , imap , cmap , shared ,
551+ lockmode , convert_now );
552+
553+ /*
554+ * CoW fork has a delalloc reservation. Replace it with a real extent.
555+ * There may or may not be a data fork mapping.
556+ */
557+ if (isnullstartblock (cmap -> br_startblock ) ||
558+ cmap -> br_startblock == DELAYSTARTBLOCK )
559+ return xfs_reflink_fill_delalloc (ip , imap , cmap , shared ,
560+ lockmode , convert_now );
561+
562+ /* Shouldn't get here. */
563+ ASSERT (0 );
564+ return - EFSCORRUPTED ;
565+ }
566+
439567/*
440568 * Cancel CoW reservations for some block range of an inode.
441569 *
0 commit comments