@@ -341,9 +341,41 @@ xfs_find_trim_cow_extent(
341
341
return 0 ;
342
342
}
343
343
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 (
347
379
struct xfs_inode * ip ,
348
380
struct xfs_bmbt_irec * imap ,
349
381
struct xfs_bmbt_irec * cmap ,
@@ -352,25 +384,12 @@ xfs_reflink_allocate_cow(
352
384
bool convert_now )
353
385
{
354
386
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 ;
357
387
struct xfs_trans * tp ;
358
- int nimaps , error = 0 ;
359
- bool found ;
360
388
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 ;
374
393
375
394
resaligned = xfs_aligned_fsb_count (imap -> br_startoff ,
376
395
imap -> br_blockcount , xfs_get_cowextsz_hint (ip ));
@@ -386,17 +405,17 @@ xfs_reflink_allocate_cow(
386
405
387
406
* lockmode = XFS_ILOCK_EXCL ;
388
407
389
- /*
390
- * Check for an overlapping extent again now that we dropped the ilock.
391
- */
392
408
error = xfs_find_trim_cow_extent (ip , imap , cmap , shared , & found );
393
409
if (error || !* shared )
394
410
goto out_trans_cancel ;
411
+
395
412
if (found ) {
396
413
xfs_trans_cancel (tp );
397
414
goto convert ;
398
415
}
399
416
417
+ ASSERT (cmap -> br_startoff > imap -> br_startoff );
418
+
400
419
/* Allocate the entire reservation as unwritten blocks. */
401
420
nimaps = 1 ;
402
421
error = xfs_bmapi_write (tp , ip , imap -> br_startoff , imap -> br_blockcount ,
@@ -416,26 +435,135 @@ xfs_reflink_allocate_cow(
416
435
*/
417
436
if (nimaps == 0 )
418
437
return - ENOSPC ;
438
+
419
439
convert :
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 );
432
444
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 );
433
511
434
512
out_trans_cancel :
435
513
xfs_trans_cancel (tp );
436
514
return error ;
437
515
}
438
516
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
+
439
567
/*
440
568
* Cancel CoW reservations for some block range of an inode.
441
569
*
0 commit comments