1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#include "xfs.h"
21#include "xfs_fs.h"
22#include "xfs_shared.h"
23#include "xfs_format.h"
24#include "xfs_log_format.h"
25#include "xfs_trans_resv.h"
26#include "xfs_sb.h"
27#include "xfs_mount.h"
28#include "xfs_defer.h"
29#include "xfs_btree.h"
30#include "xfs_bmap.h"
31#include "xfs_refcount_btree.h"
32#include "xfs_alloc.h"
33#include "xfs_errortag.h"
34#include "xfs_error.h"
35#include "xfs_trace.h"
36#include "xfs_cksum.h"
37#include "xfs_trans.h"
38#include "xfs_bit.h"
39#include "xfs_refcount.h"
40#include "xfs_rmap.h"
41
42
43enum xfs_refc_adjust_op {
44 XFS_REFCOUNT_ADJUST_INCREASE = 1,
45 XFS_REFCOUNT_ADJUST_DECREASE = -1,
46 XFS_REFCOUNT_ADJUST_COW_ALLOC = 0,
47 XFS_REFCOUNT_ADJUST_COW_FREE = -1,
48};
49
50STATIC int __xfs_refcount_cow_alloc(struct xfs_btree_cur *rcur,
51 xfs_agblock_t agbno, xfs_extlen_t aglen,
52 struct xfs_defer_ops *dfops);
53STATIC int __xfs_refcount_cow_free(struct xfs_btree_cur *rcur,
54 xfs_agblock_t agbno, xfs_extlen_t aglen,
55 struct xfs_defer_ops *dfops);
56
57
58
59
60
61int
62xfs_refcount_lookup_le(
63 struct xfs_btree_cur *cur,
64 xfs_agblock_t bno,
65 int *stat)
66{
67 trace_xfs_refcount_lookup(cur->bc_mp, cur->bc_private.a.agno, bno,
68 XFS_LOOKUP_LE);
69 cur->bc_rec.rc.rc_startblock = bno;
70 cur->bc_rec.rc.rc_blockcount = 0;
71 return xfs_btree_lookup(cur, XFS_LOOKUP_LE, stat);
72}
73
74
75
76
77
78int
79xfs_refcount_lookup_ge(
80 struct xfs_btree_cur *cur,
81 xfs_agblock_t bno,
82 int *stat)
83{
84 trace_xfs_refcount_lookup(cur->bc_mp, cur->bc_private.a.agno, bno,
85 XFS_LOOKUP_GE);
86 cur->bc_rec.rc.rc_startblock = bno;
87 cur->bc_rec.rc.rc_blockcount = 0;
88 return xfs_btree_lookup(cur, XFS_LOOKUP_GE, stat);
89}
90
91
92static inline void
93xfs_refcount_btrec_to_irec(
94 union xfs_btree_rec *rec,
95 struct xfs_refcount_irec *irec)
96{
97 irec->rc_startblock = be32_to_cpu(rec->refc.rc_startblock);
98 irec->rc_blockcount = be32_to_cpu(rec->refc.rc_blockcount);
99 irec->rc_refcount = be32_to_cpu(rec->refc.rc_refcount);
100}
101
102
103
104
105int
106xfs_refcount_get_rec(
107 struct xfs_btree_cur *cur,
108 struct xfs_refcount_irec *irec,
109 int *stat)
110{
111 union xfs_btree_rec *rec;
112 int error;
113
114 error = xfs_btree_get_rec(cur, &rec, stat);
115 if (!error && *stat == 1) {
116 xfs_refcount_btrec_to_irec(rec, irec);
117 trace_xfs_refcount_get(cur->bc_mp, cur->bc_private.a.agno,
118 irec);
119 }
120 return error;
121}
122
123
124
125
126
127
128STATIC int
129xfs_refcount_update(
130 struct xfs_btree_cur *cur,
131 struct xfs_refcount_irec *irec)
132{
133 union xfs_btree_rec rec;
134 int error;
135
136 trace_xfs_refcount_update(cur->bc_mp, cur->bc_private.a.agno, irec);
137 rec.refc.rc_startblock = cpu_to_be32(irec->rc_startblock);
138 rec.refc.rc_blockcount = cpu_to_be32(irec->rc_blockcount);
139 rec.refc.rc_refcount = cpu_to_be32(irec->rc_refcount);
140 error = xfs_btree_update(cur, &rec);
141 if (error)
142 trace_xfs_refcount_update_error(cur->bc_mp,
143 cur->bc_private.a.agno, error, _RET_IP_);
144 return error;
145}
146
147
148
149
150
151
152STATIC int
153xfs_refcount_insert(
154 struct xfs_btree_cur *cur,
155 struct xfs_refcount_irec *irec,
156 int *i)
157{
158 int error;
159
160 trace_xfs_refcount_insert(cur->bc_mp, cur->bc_private.a.agno, irec);
161 cur->bc_rec.rc.rc_startblock = irec->rc_startblock;
162 cur->bc_rec.rc.rc_blockcount = irec->rc_blockcount;
163 cur->bc_rec.rc.rc_refcount = irec->rc_refcount;
164 error = xfs_btree_insert(cur, i);
165 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, *i == 1, out_error);
166out_error:
167 if (error)
168 trace_xfs_refcount_insert_error(cur->bc_mp,
169 cur->bc_private.a.agno, error, _RET_IP_);
170 return error;
171}
172
173
174
175
176
177
178
179STATIC int
180xfs_refcount_delete(
181 struct xfs_btree_cur *cur,
182 int *i)
183{
184 struct xfs_refcount_irec irec;
185 int found_rec;
186 int error;
187
188 error = xfs_refcount_get_rec(cur, &irec, &found_rec);
189 if (error)
190 goto out_error;
191 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
192 trace_xfs_refcount_delete(cur->bc_mp, cur->bc_private.a.agno, &irec);
193 error = xfs_btree_delete(cur, i);
194 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, *i == 1, out_error);
195 if (error)
196 goto out_error;
197 error = xfs_refcount_lookup_ge(cur, irec.rc_startblock, &found_rec);
198out_error:
199 if (error)
200 trace_xfs_refcount_delete_error(cur->bc_mp,
201 cur->bc_private.a.agno, error, _RET_IP_);
202 return error;
203}
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283static inline xfs_agblock_t
284xfs_refc_next(
285 struct xfs_refcount_irec *rc)
286{
287 return rc->rc_startblock + rc->rc_blockcount;
288}
289
290
291
292
293STATIC int
294xfs_refcount_split_extent(
295 struct xfs_btree_cur *cur,
296 xfs_agblock_t agbno,
297 bool *shape_changed)
298{
299 struct xfs_refcount_irec rcext, tmp;
300 int found_rec;
301 int error;
302
303 *shape_changed = false;
304 error = xfs_refcount_lookup_le(cur, agbno, &found_rec);
305 if (error)
306 goto out_error;
307 if (!found_rec)
308 return 0;
309
310 error = xfs_refcount_get_rec(cur, &rcext, &found_rec);
311 if (error)
312 goto out_error;
313 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
314 if (rcext.rc_startblock == agbno || xfs_refc_next(&rcext) <= agbno)
315 return 0;
316
317 *shape_changed = true;
318 trace_xfs_refcount_split_extent(cur->bc_mp, cur->bc_private.a.agno,
319 &rcext, agbno);
320
321
322 tmp = rcext;
323 tmp.rc_startblock = agbno;
324 tmp.rc_blockcount -= (agbno - rcext.rc_startblock);
325 error = xfs_refcount_update(cur, &tmp);
326 if (error)
327 goto out_error;
328
329
330 tmp = rcext;
331 tmp.rc_blockcount = agbno - rcext.rc_startblock;
332 error = xfs_refcount_insert(cur, &tmp, &found_rec);
333 if (error)
334 goto out_error;
335 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
336 return error;
337
338out_error:
339 trace_xfs_refcount_split_extent_error(cur->bc_mp,
340 cur->bc_private.a.agno, error, _RET_IP_);
341 return error;
342}
343
344
345
346
347STATIC int
348xfs_refcount_merge_center_extents(
349 struct xfs_btree_cur *cur,
350 struct xfs_refcount_irec *left,
351 struct xfs_refcount_irec *center,
352 struct xfs_refcount_irec *right,
353 unsigned long long extlen,
354 xfs_extlen_t *aglen)
355{
356 int error;
357 int found_rec;
358
359 trace_xfs_refcount_merge_center_extents(cur->bc_mp,
360 cur->bc_private.a.agno, left, center, right);
361
362
363
364
365
366
367
368
369
370 error = xfs_refcount_lookup_ge(cur, center->rc_startblock,
371 &found_rec);
372 if (error)
373 goto out_error;
374 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
375
376 error = xfs_refcount_delete(cur, &found_rec);
377 if (error)
378 goto out_error;
379 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
380
381 if (center->rc_refcount > 1) {
382 error = xfs_refcount_delete(cur, &found_rec);
383 if (error)
384 goto out_error;
385 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1,
386 out_error);
387 }
388
389
390 error = xfs_refcount_lookup_le(cur, left->rc_startblock,
391 &found_rec);
392 if (error)
393 goto out_error;
394 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
395
396 left->rc_blockcount = extlen;
397 error = xfs_refcount_update(cur, left);
398 if (error)
399 goto out_error;
400
401 *aglen = 0;
402 return error;
403
404out_error:
405 trace_xfs_refcount_merge_center_extents_error(cur->bc_mp,
406 cur->bc_private.a.agno, error, _RET_IP_);
407 return error;
408}
409
410
411
412
413STATIC int
414xfs_refcount_merge_left_extent(
415 struct xfs_btree_cur *cur,
416 struct xfs_refcount_irec *left,
417 struct xfs_refcount_irec *cleft,
418 xfs_agblock_t *agbno,
419 xfs_extlen_t *aglen)
420{
421 int error;
422 int found_rec;
423
424 trace_xfs_refcount_merge_left_extent(cur->bc_mp,
425 cur->bc_private.a.agno, left, cleft);
426
427
428 if (cleft->rc_refcount > 1) {
429 error = xfs_refcount_lookup_le(cur, cleft->rc_startblock,
430 &found_rec);
431 if (error)
432 goto out_error;
433 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1,
434 out_error);
435
436 error = xfs_refcount_delete(cur, &found_rec);
437 if (error)
438 goto out_error;
439 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1,
440 out_error);
441 }
442
443
444 error = xfs_refcount_lookup_le(cur, left->rc_startblock,
445 &found_rec);
446 if (error)
447 goto out_error;
448 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
449
450 left->rc_blockcount += cleft->rc_blockcount;
451 error = xfs_refcount_update(cur, left);
452 if (error)
453 goto out_error;
454
455 *agbno += cleft->rc_blockcount;
456 *aglen -= cleft->rc_blockcount;
457 return error;
458
459out_error:
460 trace_xfs_refcount_merge_left_extent_error(cur->bc_mp,
461 cur->bc_private.a.agno, error, _RET_IP_);
462 return error;
463}
464
465
466
467
468STATIC int
469xfs_refcount_merge_right_extent(
470 struct xfs_btree_cur *cur,
471 struct xfs_refcount_irec *right,
472 struct xfs_refcount_irec *cright,
473 xfs_extlen_t *aglen)
474{
475 int error;
476 int found_rec;
477
478 trace_xfs_refcount_merge_right_extent(cur->bc_mp,
479 cur->bc_private.a.agno, cright, right);
480
481
482
483
484
485 if (cright->rc_refcount > 1) {
486 error = xfs_refcount_lookup_le(cur, cright->rc_startblock,
487 &found_rec);
488 if (error)
489 goto out_error;
490 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1,
491 out_error);
492
493 error = xfs_refcount_delete(cur, &found_rec);
494 if (error)
495 goto out_error;
496 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1,
497 out_error);
498 }
499
500
501 error = xfs_refcount_lookup_le(cur, right->rc_startblock,
502 &found_rec);
503 if (error)
504 goto out_error;
505 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
506
507 right->rc_startblock -= cright->rc_blockcount;
508 right->rc_blockcount += cright->rc_blockcount;
509 error = xfs_refcount_update(cur, right);
510 if (error)
511 goto out_error;
512
513 *aglen -= cright->rc_blockcount;
514 return error;
515
516out_error:
517 trace_xfs_refcount_merge_right_extent_error(cur->bc_mp,
518 cur->bc_private.a.agno, error, _RET_IP_);
519 return error;
520}
521
522#define XFS_FIND_RCEXT_SHARED 1
523#define XFS_FIND_RCEXT_COW 2
524
525
526
527
528STATIC int
529xfs_refcount_find_left_extents(
530 struct xfs_btree_cur *cur,
531 struct xfs_refcount_irec *left,
532 struct xfs_refcount_irec *cleft,
533 xfs_agblock_t agbno,
534 xfs_extlen_t aglen,
535 int flags)
536{
537 struct xfs_refcount_irec tmp;
538 int error;
539 int found_rec;
540
541 left->rc_startblock = cleft->rc_startblock = NULLAGBLOCK;
542 error = xfs_refcount_lookup_le(cur, agbno - 1, &found_rec);
543 if (error)
544 goto out_error;
545 if (!found_rec)
546 return 0;
547
548 error = xfs_refcount_get_rec(cur, &tmp, &found_rec);
549 if (error)
550 goto out_error;
551 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
552
553 if (xfs_refc_next(&tmp) != agbno)
554 return 0;
555 if ((flags & XFS_FIND_RCEXT_SHARED) && tmp.rc_refcount < 2)
556 return 0;
557 if ((flags & XFS_FIND_RCEXT_COW) && tmp.rc_refcount > 1)
558 return 0;
559
560 *left = tmp;
561
562 error = xfs_btree_increment(cur, 0, &found_rec);
563 if (error)
564 goto out_error;
565 if (found_rec) {
566 error = xfs_refcount_get_rec(cur, &tmp, &found_rec);
567 if (error)
568 goto out_error;
569 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1,
570 out_error);
571
572
573 if (tmp.rc_startblock == agbno)
574 *cleft = tmp;
575 else {
576
577
578
579
580
581
582
583
584 cleft->rc_startblock = agbno;
585 cleft->rc_blockcount = min(aglen,
586 tmp.rc_startblock - agbno);
587 cleft->rc_refcount = 1;
588 }
589 } else {
590
591
592
593
594 cleft->rc_startblock = agbno;
595 cleft->rc_blockcount = aglen;
596 cleft->rc_refcount = 1;
597 }
598 trace_xfs_refcount_find_left_extent(cur->bc_mp, cur->bc_private.a.agno,
599 left, cleft, agbno);
600 return error;
601
602out_error:
603 trace_xfs_refcount_find_left_extent_error(cur->bc_mp,
604 cur->bc_private.a.agno, error, _RET_IP_);
605 return error;
606}
607
608
609
610
611
612STATIC int
613xfs_refcount_find_right_extents(
614 struct xfs_btree_cur *cur,
615 struct xfs_refcount_irec *right,
616 struct xfs_refcount_irec *cright,
617 xfs_agblock_t agbno,
618 xfs_extlen_t aglen,
619 int flags)
620{
621 struct xfs_refcount_irec tmp;
622 int error;
623 int found_rec;
624
625 right->rc_startblock = cright->rc_startblock = NULLAGBLOCK;
626 error = xfs_refcount_lookup_ge(cur, agbno + aglen, &found_rec);
627 if (error)
628 goto out_error;
629 if (!found_rec)
630 return 0;
631
632 error = xfs_refcount_get_rec(cur, &tmp, &found_rec);
633 if (error)
634 goto out_error;
635 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
636
637 if (tmp.rc_startblock != agbno + aglen)
638 return 0;
639 if ((flags & XFS_FIND_RCEXT_SHARED) && tmp.rc_refcount < 2)
640 return 0;
641 if ((flags & XFS_FIND_RCEXT_COW) && tmp.rc_refcount > 1)
642 return 0;
643
644 *right = tmp;
645
646 error = xfs_btree_decrement(cur, 0, &found_rec);
647 if (error)
648 goto out_error;
649 if (found_rec) {
650 error = xfs_refcount_get_rec(cur, &tmp, &found_rec);
651 if (error)
652 goto out_error;
653 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1,
654 out_error);
655
656
657 if (xfs_refc_next(&tmp) == agbno + aglen)
658 *cright = tmp;
659 else {
660
661
662
663
664
665
666
667
668 cright->rc_startblock = max(agbno, xfs_refc_next(&tmp));
669 cright->rc_blockcount = right->rc_startblock -
670 cright->rc_startblock;
671 cright->rc_refcount = 1;
672 }
673 } else {
674
675
676
677
678 cright->rc_startblock = agbno;
679 cright->rc_blockcount = aglen;
680 cright->rc_refcount = 1;
681 }
682 trace_xfs_refcount_find_right_extent(cur->bc_mp, cur->bc_private.a.agno,
683 cright, right, agbno + aglen);
684 return error;
685
686out_error:
687 trace_xfs_refcount_find_right_extent_error(cur->bc_mp,
688 cur->bc_private.a.agno, error, _RET_IP_);
689 return error;
690}
691
692
693static inline bool
694xfs_refc_valid(
695 struct xfs_refcount_irec *rc)
696{
697 return rc->rc_startblock != NULLAGBLOCK;
698}
699
700
701
702
703STATIC int
704xfs_refcount_merge_extents(
705 struct xfs_btree_cur *cur,
706 xfs_agblock_t *agbno,
707 xfs_extlen_t *aglen,
708 enum xfs_refc_adjust_op adjust,
709 int flags,
710 bool *shape_changed)
711{
712 struct xfs_refcount_irec left = {0}, cleft = {0};
713 struct xfs_refcount_irec cright = {0}, right = {0};
714 int error;
715 unsigned long long ulen;
716 bool cequal;
717
718 *shape_changed = false;
719
720
721
722
723
724 error = xfs_refcount_find_left_extents(cur, &left, &cleft, *agbno,
725 *aglen, flags);
726 if (error)
727 return error;
728 error = xfs_refcount_find_right_extents(cur, &right, &cright, *agbno,
729 *aglen, flags);
730 if (error)
731 return error;
732
733
734 if (!xfs_refc_valid(&left) && !xfs_refc_valid(&right))
735 return 0;
736
737 cequal = (cleft.rc_startblock == cright.rc_startblock) &&
738 (cleft.rc_blockcount == cright.rc_blockcount);
739
740
741 ulen = (unsigned long long)left.rc_blockcount + cleft.rc_blockcount +
742 right.rc_blockcount;
743 if (xfs_refc_valid(&left) && xfs_refc_valid(&right) &&
744 xfs_refc_valid(&cleft) && xfs_refc_valid(&cright) && cequal &&
745 left.rc_refcount == cleft.rc_refcount + adjust &&
746 right.rc_refcount == cleft.rc_refcount + adjust &&
747 ulen < MAXREFCEXTLEN) {
748 *shape_changed = true;
749 return xfs_refcount_merge_center_extents(cur, &left, &cleft,
750 &right, ulen, aglen);
751 }
752
753
754 ulen = (unsigned long long)left.rc_blockcount + cleft.rc_blockcount;
755 if (xfs_refc_valid(&left) && xfs_refc_valid(&cleft) &&
756 left.rc_refcount == cleft.rc_refcount + adjust &&
757 ulen < MAXREFCEXTLEN) {
758 *shape_changed = true;
759 error = xfs_refcount_merge_left_extent(cur, &left, &cleft,
760 agbno, aglen);
761 if (error)
762 return error;
763
764
765
766
767
768 if (cequal)
769 return 0;
770 }
771
772
773 ulen = (unsigned long long)right.rc_blockcount + cright.rc_blockcount;
774 if (xfs_refc_valid(&right) && xfs_refc_valid(&cright) &&
775 right.rc_refcount == cright.rc_refcount + adjust &&
776 ulen < MAXREFCEXTLEN) {
777 *shape_changed = true;
778 return xfs_refcount_merge_right_extent(cur, &right, &cright,
779 aglen);
780 }
781
782 return error;
783}
784
785
786
787
788
789
790
791static bool
792xfs_refcount_still_have_space(
793 struct xfs_btree_cur *cur)
794{
795 unsigned long overhead;
796
797 overhead = cur->bc_private.a.priv.refc.shape_changes *
798 xfs_allocfree_log_count(cur->bc_mp, 1);
799 overhead *= cur->bc_mp->m_sb.sb_blocksize;
800
801
802
803
804
805 if (cur->bc_private.a.priv.refc.nr_ops > 2 &&
806 XFS_TEST_ERROR(false, cur->bc_mp,
807 XFS_ERRTAG_REFCOUNT_CONTINUE_UPDATE))
808 return false;
809
810 if (cur->bc_private.a.priv.refc.nr_ops == 0)
811 return true;
812 else if (overhead > cur->bc_tp->t_log_res)
813 return false;
814 return cur->bc_tp->t_log_res - overhead >
815 cur->bc_private.a.priv.refc.nr_ops * XFS_REFCOUNT_ITEM_OVERHEAD;
816}
817
818
819
820
821
822
823
824STATIC int
825xfs_refcount_adjust_extents(
826 struct xfs_btree_cur *cur,
827 xfs_agblock_t *agbno,
828 xfs_extlen_t *aglen,
829 enum xfs_refc_adjust_op adj,
830 struct xfs_defer_ops *dfops,
831 struct xfs_owner_info *oinfo)
832{
833 struct xfs_refcount_irec ext, tmp;
834 int error;
835 int found_rec, found_tmp;
836 xfs_fsblock_t fsbno;
837
838
839 if (*aglen == 0)
840 return 0;
841
842 error = xfs_refcount_lookup_ge(cur, *agbno, &found_rec);
843 if (error)
844 goto out_error;
845
846 while (*aglen > 0 && xfs_refcount_still_have_space(cur)) {
847 error = xfs_refcount_get_rec(cur, &ext, &found_rec);
848 if (error)
849 goto out_error;
850 if (!found_rec) {
851 ext.rc_startblock = cur->bc_mp->m_sb.sb_agblocks;
852 ext.rc_blockcount = 0;
853 ext.rc_refcount = 0;
854 }
855
856
857
858
859
860
861 if (ext.rc_startblock != *agbno) {
862 tmp.rc_startblock = *agbno;
863 tmp.rc_blockcount = min(*aglen,
864 ext.rc_startblock - *agbno);
865 tmp.rc_refcount = 1 + adj;
866 trace_xfs_refcount_modify_extent(cur->bc_mp,
867 cur->bc_private.a.agno, &tmp);
868
869
870
871
872
873 if (tmp.rc_refcount) {
874 error = xfs_refcount_insert(cur, &tmp,
875 &found_tmp);
876 if (error)
877 goto out_error;
878 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp,
879 found_tmp == 1, out_error);
880 cur->bc_private.a.priv.refc.nr_ops++;
881 } else {
882 fsbno = XFS_AGB_TO_FSB(cur->bc_mp,
883 cur->bc_private.a.agno,
884 tmp.rc_startblock);
885 xfs_bmap_add_free(cur->bc_mp, dfops, fsbno,
886 tmp.rc_blockcount, oinfo);
887 }
888
889 (*agbno) += tmp.rc_blockcount;
890 (*aglen) -= tmp.rc_blockcount;
891
892 error = xfs_refcount_lookup_ge(cur, *agbno,
893 &found_rec);
894 if (error)
895 goto out_error;
896 }
897
898
899 if (*aglen == 0 || !xfs_refcount_still_have_space(cur))
900 break;
901
902
903
904
905
906 if (ext.rc_refcount == MAXREFCOUNT)
907 goto skip;
908 ext.rc_refcount += adj;
909 trace_xfs_refcount_modify_extent(cur->bc_mp,
910 cur->bc_private.a.agno, &ext);
911 if (ext.rc_refcount > 1) {
912 error = xfs_refcount_update(cur, &ext);
913 if (error)
914 goto out_error;
915 cur->bc_private.a.priv.refc.nr_ops++;
916 } else if (ext.rc_refcount == 1) {
917 error = xfs_refcount_delete(cur, &found_rec);
918 if (error)
919 goto out_error;
920 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp,
921 found_rec == 1, out_error);
922 cur->bc_private.a.priv.refc.nr_ops++;
923 goto advloop;
924 } else {
925 fsbno = XFS_AGB_TO_FSB(cur->bc_mp,
926 cur->bc_private.a.agno,
927 ext.rc_startblock);
928 xfs_bmap_add_free(cur->bc_mp, dfops, fsbno,
929 ext.rc_blockcount, oinfo);
930 }
931
932skip:
933 error = xfs_btree_increment(cur, 0, &found_rec);
934 if (error)
935 goto out_error;
936
937advloop:
938 (*agbno) += ext.rc_blockcount;
939 (*aglen) -= ext.rc_blockcount;
940 }
941
942 return error;
943out_error:
944 trace_xfs_refcount_modify_extent_error(cur->bc_mp,
945 cur->bc_private.a.agno, error, _RET_IP_);
946 return error;
947}
948
949
950STATIC int
951xfs_refcount_adjust(
952 struct xfs_btree_cur *cur,
953 xfs_agblock_t agbno,
954 xfs_extlen_t aglen,
955 xfs_agblock_t *new_agbno,
956 xfs_extlen_t *new_aglen,
957 enum xfs_refc_adjust_op adj,
958 struct xfs_defer_ops *dfops,
959 struct xfs_owner_info *oinfo)
960{
961 bool shape_changed;
962 int shape_changes = 0;
963 int error;
964
965 *new_agbno = agbno;
966 *new_aglen = aglen;
967 if (adj == XFS_REFCOUNT_ADJUST_INCREASE)
968 trace_xfs_refcount_increase(cur->bc_mp, cur->bc_private.a.agno,
969 agbno, aglen);
970 else
971 trace_xfs_refcount_decrease(cur->bc_mp, cur->bc_private.a.agno,
972 agbno, aglen);
973
974
975
976
977 error = xfs_refcount_split_extent(cur, agbno, &shape_changed);
978 if (error)
979 goto out_error;
980 if (shape_changed)
981 shape_changes++;
982
983 error = xfs_refcount_split_extent(cur, agbno + aglen, &shape_changed);
984 if (error)
985 goto out_error;
986 if (shape_changed)
987 shape_changes++;
988
989
990
991
992 error = xfs_refcount_merge_extents(cur, new_agbno, new_aglen, adj,
993 XFS_FIND_RCEXT_SHARED, &shape_changed);
994 if (error)
995 goto out_error;
996 if (shape_changed)
997 shape_changes++;
998 if (shape_changes)
999 cur->bc_private.a.priv.refc.shape_changes++;
1000
1001
1002 error = xfs_refcount_adjust_extents(cur, new_agbno, new_aglen,
1003 adj, dfops, oinfo);
1004 if (error)
1005 goto out_error;
1006
1007 return 0;
1008
1009out_error:
1010 trace_xfs_refcount_adjust_error(cur->bc_mp, cur->bc_private.a.agno,
1011 error, _RET_IP_);
1012 return error;
1013}
1014
1015
1016void
1017xfs_refcount_finish_one_cleanup(
1018 struct xfs_trans *tp,
1019 struct xfs_btree_cur *rcur,
1020 int error)
1021{
1022 struct xfs_buf *agbp;
1023
1024 if (rcur == NULL)
1025 return;
1026 agbp = rcur->bc_private.a.agbp;
1027 xfs_btree_del_cursor(rcur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
1028 if (error)
1029 xfs_trans_brelse(tp, agbp);
1030}
1031
1032
1033
1034
1035
1036
1037
1038
1039int
1040xfs_refcount_finish_one(
1041 struct xfs_trans *tp,
1042 struct xfs_defer_ops *dfops,
1043 enum xfs_refcount_intent_type type,
1044 xfs_fsblock_t startblock,
1045 xfs_extlen_t blockcount,
1046 xfs_fsblock_t *new_fsb,
1047 xfs_extlen_t *new_len,
1048 struct xfs_btree_cur **pcur)
1049{
1050 struct xfs_mount *mp = tp->t_mountp;
1051 struct xfs_btree_cur *rcur;
1052 struct xfs_buf *agbp = NULL;
1053 int error = 0;
1054 xfs_agnumber_t agno;
1055 xfs_agblock_t bno;
1056 xfs_agblock_t new_agbno;
1057 unsigned long nr_ops = 0;
1058 int shape_changes = 0;
1059
1060 agno = XFS_FSB_TO_AGNO(mp, startblock);
1061 ASSERT(agno != NULLAGNUMBER);
1062 bno = XFS_FSB_TO_AGBNO(mp, startblock);
1063
1064 trace_xfs_refcount_deferred(mp, XFS_FSB_TO_AGNO(mp, startblock),
1065 type, XFS_FSB_TO_AGBNO(mp, startblock),
1066 blockcount);
1067
1068 if (XFS_TEST_ERROR(false, mp,
1069 XFS_ERRTAG_REFCOUNT_FINISH_ONE))
1070 return -EIO;
1071
1072
1073
1074
1075
1076 rcur = *pcur;
1077 if (rcur != NULL && rcur->bc_private.a.agno != agno) {
1078 nr_ops = rcur->bc_private.a.priv.refc.nr_ops;
1079 shape_changes = rcur->bc_private.a.priv.refc.shape_changes;
1080 xfs_refcount_finish_one_cleanup(tp, rcur, 0);
1081 rcur = NULL;
1082 *pcur = NULL;
1083 }
1084 if (rcur == NULL) {
1085 error = xfs_alloc_read_agf(tp->t_mountp, tp, agno,
1086 XFS_ALLOC_FLAG_FREEING, &agbp);
1087 if (error)
1088 return error;
1089 if (!agbp)
1090 return -EFSCORRUPTED;
1091
1092 rcur = xfs_refcountbt_init_cursor(mp, tp, agbp, agno, dfops);
1093 if (!rcur) {
1094 error = -ENOMEM;
1095 goto out_cur;
1096 }
1097 rcur->bc_private.a.priv.refc.nr_ops = nr_ops;
1098 rcur->bc_private.a.priv.refc.shape_changes = shape_changes;
1099 }
1100 *pcur = rcur;
1101
1102 switch (type) {
1103 case XFS_REFCOUNT_INCREASE:
1104 error = xfs_refcount_adjust(rcur, bno, blockcount, &new_agbno,
1105 new_len, XFS_REFCOUNT_ADJUST_INCREASE, dfops, NULL);
1106 *new_fsb = XFS_AGB_TO_FSB(mp, agno, new_agbno);
1107 break;
1108 case XFS_REFCOUNT_DECREASE:
1109 error = xfs_refcount_adjust(rcur, bno, blockcount, &new_agbno,
1110 new_len, XFS_REFCOUNT_ADJUST_DECREASE, dfops, NULL);
1111 *new_fsb = XFS_AGB_TO_FSB(mp, agno, new_agbno);
1112 break;
1113 case XFS_REFCOUNT_ALLOC_COW:
1114 *new_fsb = startblock + blockcount;
1115 *new_len = 0;
1116 error = __xfs_refcount_cow_alloc(rcur, bno, blockcount, dfops);
1117 break;
1118 case XFS_REFCOUNT_FREE_COW:
1119 *new_fsb = startblock + blockcount;
1120 *new_len = 0;
1121 error = __xfs_refcount_cow_free(rcur, bno, blockcount, dfops);
1122 break;
1123 default:
1124 ASSERT(0);
1125 error = -EFSCORRUPTED;
1126 }
1127 if (!error && *new_len > 0)
1128 trace_xfs_refcount_finish_one_leftover(mp, agno, type,
1129 bno, blockcount, new_agbno, *new_len);
1130 return error;
1131
1132out_cur:
1133 xfs_trans_brelse(tp, agbp);
1134
1135 return error;
1136}
1137
1138
1139
1140
1141static int
1142__xfs_refcount_add(
1143 struct xfs_mount *mp,
1144 struct xfs_defer_ops *dfops,
1145 enum xfs_refcount_intent_type type,
1146 xfs_fsblock_t startblock,
1147 xfs_extlen_t blockcount)
1148{
1149 struct xfs_refcount_intent *ri;
1150
1151 trace_xfs_refcount_defer(mp, XFS_FSB_TO_AGNO(mp, startblock),
1152 type, XFS_FSB_TO_AGBNO(mp, startblock),
1153 blockcount);
1154
1155 ri = kmem_alloc(sizeof(struct xfs_refcount_intent),
1156 KM_SLEEP | KM_NOFS);
1157 INIT_LIST_HEAD(&ri->ri_list);
1158 ri->ri_type = type;
1159 ri->ri_startblock = startblock;
1160 ri->ri_blockcount = blockcount;
1161
1162 xfs_defer_add(dfops, XFS_DEFER_OPS_TYPE_REFCOUNT, &ri->ri_list);
1163 return 0;
1164}
1165
1166
1167
1168
1169int
1170xfs_refcount_increase_extent(
1171 struct xfs_mount *mp,
1172 struct xfs_defer_ops *dfops,
1173 struct xfs_bmbt_irec *PREV)
1174{
1175 if (!xfs_sb_version_hasreflink(&mp->m_sb))
1176 return 0;
1177
1178 return __xfs_refcount_add(mp, dfops, XFS_REFCOUNT_INCREASE,
1179 PREV->br_startblock, PREV->br_blockcount);
1180}
1181
1182
1183
1184
1185int
1186xfs_refcount_decrease_extent(
1187 struct xfs_mount *mp,
1188 struct xfs_defer_ops *dfops,
1189 struct xfs_bmbt_irec *PREV)
1190{
1191 if (!xfs_sb_version_hasreflink(&mp->m_sb))
1192 return 0;
1193
1194 return __xfs_refcount_add(mp, dfops, XFS_REFCOUNT_DECREASE,
1195 PREV->br_startblock, PREV->br_blockcount);
1196}
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206int
1207xfs_refcount_find_shared(
1208 struct xfs_btree_cur *cur,
1209 xfs_agblock_t agbno,
1210 xfs_extlen_t aglen,
1211 xfs_agblock_t *fbno,
1212 xfs_extlen_t *flen,
1213 bool find_end_of_shared)
1214{
1215 struct xfs_refcount_irec tmp;
1216 int i;
1217 int have;
1218 int error;
1219
1220 trace_xfs_refcount_find_shared(cur->bc_mp, cur->bc_private.a.agno,
1221 agbno, aglen);
1222
1223
1224 *fbno = NULLAGBLOCK;
1225 *flen = 0;
1226
1227
1228 error = xfs_refcount_lookup_le(cur, agbno, &have);
1229 if (error)
1230 goto out_error;
1231 if (!have) {
1232
1233 error = xfs_btree_increment(cur, 0, &have);
1234 if (error)
1235 goto out_error;
1236 if (!have)
1237 goto done;
1238 }
1239 error = xfs_refcount_get_rec(cur, &tmp, &i);
1240 if (error)
1241 goto out_error;
1242 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, i == 1, out_error);
1243
1244
1245 if (tmp.rc_startblock + tmp.rc_blockcount <= agbno) {
1246 error = xfs_btree_increment(cur, 0, &have);
1247 if (error)
1248 goto out_error;
1249 if (!have)
1250 goto done;
1251 error = xfs_refcount_get_rec(cur, &tmp, &i);
1252 if (error)
1253 goto out_error;
1254 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, i == 1, out_error);
1255 }
1256
1257
1258 if (tmp.rc_startblock >= agbno + aglen)
1259 goto done;
1260
1261
1262 if (tmp.rc_startblock < agbno) {
1263 tmp.rc_blockcount -= (agbno - tmp.rc_startblock);
1264 tmp.rc_startblock = agbno;
1265 }
1266
1267 *fbno = tmp.rc_startblock;
1268 *flen = min(tmp.rc_blockcount, agbno + aglen - *fbno);
1269 if (!find_end_of_shared)
1270 goto done;
1271
1272
1273 while (*fbno + *flen < agbno + aglen) {
1274 error = xfs_btree_increment(cur, 0, &have);
1275 if (error)
1276 goto out_error;
1277 if (!have)
1278 break;
1279 error = xfs_refcount_get_rec(cur, &tmp, &i);
1280 if (error)
1281 goto out_error;
1282 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, i == 1, out_error);
1283 if (tmp.rc_startblock >= agbno + aglen ||
1284 tmp.rc_startblock != *fbno + *flen)
1285 break;
1286 *flen = min(*flen + tmp.rc_blockcount, agbno + aglen - *fbno);
1287 }
1288
1289done:
1290 trace_xfs_refcount_find_shared_result(cur->bc_mp,
1291 cur->bc_private.a.agno, *fbno, *flen);
1292
1293out_error:
1294 if (error)
1295 trace_xfs_refcount_find_shared_error(cur->bc_mp,
1296 cur->bc_private.a.agno, error, _RET_IP_);
1297 return error;
1298}
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352STATIC int
1353xfs_refcount_adjust_cow_extents(
1354 struct xfs_btree_cur *cur,
1355 xfs_agblock_t agbno,
1356 xfs_extlen_t aglen,
1357 enum xfs_refc_adjust_op adj)
1358{
1359 struct xfs_refcount_irec ext, tmp;
1360 int error;
1361 int found_rec, found_tmp;
1362
1363 if (aglen == 0)
1364 return 0;
1365
1366
1367 error = xfs_refcount_lookup_ge(cur, agbno, &found_rec);
1368 if (error)
1369 goto out_error;
1370 error = xfs_refcount_get_rec(cur, &ext, &found_rec);
1371 if (error)
1372 goto out_error;
1373 if (!found_rec) {
1374 ext.rc_startblock = cur->bc_mp->m_sb.sb_agblocks +
1375 XFS_REFC_COW_START;
1376 ext.rc_blockcount = 0;
1377 ext.rc_refcount = 0;
1378 }
1379
1380 switch (adj) {
1381 case XFS_REFCOUNT_ADJUST_COW_ALLOC:
1382
1383 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp,
1384 ext.rc_startblock >= agbno + aglen, out_error);
1385
1386 tmp.rc_startblock = agbno;
1387 tmp.rc_blockcount = aglen;
1388 tmp.rc_refcount = 1;
1389 trace_xfs_refcount_modify_extent(cur->bc_mp,
1390 cur->bc_private.a.agno, &tmp);
1391
1392 error = xfs_refcount_insert(cur, &tmp,
1393 &found_tmp);
1394 if (error)
1395 goto out_error;
1396 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp,
1397 found_tmp == 1, out_error);
1398 break;
1399 case XFS_REFCOUNT_ADJUST_COW_FREE:
1400
1401 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp,
1402 ext.rc_startblock == agbno, out_error);
1403 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp,
1404 ext.rc_blockcount == aglen, out_error);
1405 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp,
1406 ext.rc_refcount == 1, out_error);
1407
1408 ext.rc_refcount = 0;
1409 trace_xfs_refcount_modify_extent(cur->bc_mp,
1410 cur->bc_private.a.agno, &ext);
1411 error = xfs_refcount_delete(cur, &found_rec);
1412 if (error)
1413 goto out_error;
1414 XFS_WANT_CORRUPTED_GOTO(cur->bc_mp,
1415 found_rec == 1, out_error);
1416 break;
1417 default:
1418 ASSERT(0);
1419 }
1420
1421 return error;
1422out_error:
1423 trace_xfs_refcount_modify_extent_error(cur->bc_mp,
1424 cur->bc_private.a.agno, error, _RET_IP_);
1425 return error;
1426}
1427
1428
1429
1430
1431STATIC int
1432xfs_refcount_adjust_cow(
1433 struct xfs_btree_cur *cur,
1434 xfs_agblock_t agbno,
1435 xfs_extlen_t aglen,
1436 enum xfs_refc_adjust_op adj)
1437{
1438 bool shape_changed;
1439 int error;
1440
1441 agbno += XFS_REFC_COW_START;
1442
1443
1444
1445
1446 error = xfs_refcount_split_extent(cur, agbno, &shape_changed);
1447 if (error)
1448 goto out_error;
1449
1450 error = xfs_refcount_split_extent(cur, agbno + aglen, &shape_changed);
1451 if (error)
1452 goto out_error;
1453
1454
1455
1456
1457 error = xfs_refcount_merge_extents(cur, &agbno, &aglen, adj,
1458 XFS_FIND_RCEXT_COW, &shape_changed);
1459 if (error)
1460 goto out_error;
1461
1462
1463 error = xfs_refcount_adjust_cow_extents(cur, agbno, aglen, adj);
1464 if (error)
1465 goto out_error;
1466
1467 return 0;
1468
1469out_error:
1470 trace_xfs_refcount_adjust_cow_error(cur->bc_mp, cur->bc_private.a.agno,
1471 error, _RET_IP_);
1472 return error;
1473}
1474
1475
1476
1477
1478STATIC int
1479__xfs_refcount_cow_alloc(
1480 struct xfs_btree_cur *rcur,
1481 xfs_agblock_t agbno,
1482 xfs_extlen_t aglen,
1483 struct xfs_defer_ops *dfops)
1484{
1485 trace_xfs_refcount_cow_increase(rcur->bc_mp, rcur->bc_private.a.agno,
1486 agbno, aglen);
1487
1488
1489 return xfs_refcount_adjust_cow(rcur, agbno, aglen,
1490 XFS_REFCOUNT_ADJUST_COW_ALLOC);
1491}
1492
1493
1494
1495
1496STATIC int
1497__xfs_refcount_cow_free(
1498 struct xfs_btree_cur *rcur,
1499 xfs_agblock_t agbno,
1500 xfs_extlen_t aglen,
1501 struct xfs_defer_ops *dfops)
1502{
1503 trace_xfs_refcount_cow_decrease(rcur->bc_mp, rcur->bc_private.a.agno,
1504 agbno, aglen);
1505
1506
1507 return xfs_refcount_adjust_cow(rcur, agbno, aglen,
1508 XFS_REFCOUNT_ADJUST_COW_FREE);
1509}
1510
1511
1512int
1513xfs_refcount_alloc_cow_extent(
1514 struct xfs_mount *mp,
1515 struct xfs_defer_ops *dfops,
1516 xfs_fsblock_t fsb,
1517 xfs_extlen_t len)
1518{
1519 int error;
1520
1521 if (!xfs_sb_version_hasreflink(&mp->m_sb))
1522 return 0;
1523
1524 error = __xfs_refcount_add(mp, dfops, XFS_REFCOUNT_ALLOC_COW,
1525 fsb, len);
1526 if (error)
1527 return error;
1528
1529
1530 return xfs_rmap_alloc_extent(mp, dfops, XFS_FSB_TO_AGNO(mp, fsb),
1531 XFS_FSB_TO_AGBNO(mp, fsb), len, XFS_RMAP_OWN_COW);
1532}
1533
1534
1535int
1536xfs_refcount_free_cow_extent(
1537 struct xfs_mount *mp,
1538 struct xfs_defer_ops *dfops,
1539 xfs_fsblock_t fsb,
1540 xfs_extlen_t len)
1541{
1542 int error;
1543
1544 if (!xfs_sb_version_hasreflink(&mp->m_sb))
1545 return 0;
1546
1547
1548 error = xfs_rmap_free_extent(mp, dfops, XFS_FSB_TO_AGNO(mp, fsb),
1549 XFS_FSB_TO_AGBNO(mp, fsb), len, XFS_RMAP_OWN_COW);
1550 if (error)
1551 return error;
1552
1553 return __xfs_refcount_add(mp, dfops, XFS_REFCOUNT_FREE_COW,
1554 fsb, len);
1555}
1556
1557struct xfs_refcount_recovery {
1558 struct list_head rr_list;
1559 struct xfs_refcount_irec rr_rrec;
1560};
1561
1562
1563STATIC int
1564xfs_refcount_recover_extent(
1565 struct xfs_btree_cur *cur,
1566 union xfs_btree_rec *rec,
1567 void *priv)
1568{
1569 struct list_head *debris = priv;
1570 struct xfs_refcount_recovery *rr;
1571
1572 if (be32_to_cpu(rec->refc.rc_refcount) != 1)
1573 return -EFSCORRUPTED;
1574
1575 rr = kmem_alloc(sizeof(struct xfs_refcount_recovery), KM_SLEEP);
1576 xfs_refcount_btrec_to_irec(rec, &rr->rr_rrec);
1577 list_add_tail(&rr->rr_list, debris);
1578
1579 return 0;
1580}
1581
1582
1583int
1584xfs_refcount_recover_cow_leftovers(
1585 struct xfs_mount *mp,
1586 xfs_agnumber_t agno)
1587{
1588 struct xfs_trans *tp;
1589 struct xfs_btree_cur *cur;
1590 struct xfs_buf *agbp;
1591 struct xfs_refcount_recovery *rr, *n;
1592 struct list_head debris;
1593 union xfs_btree_irec low;
1594 union xfs_btree_irec high;
1595 struct xfs_defer_ops dfops;
1596 xfs_fsblock_t fsb;
1597 xfs_agblock_t agbno;
1598 int error;
1599
1600 if (mp->m_sb.sb_agblocks >= XFS_REFC_COW_START)
1601 return -EOPNOTSUPP;
1602
1603 INIT_LIST_HEAD(&debris);
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615 error = xfs_trans_alloc_empty(mp, &tp);
1616 if (error)
1617 return error;
1618
1619 error = xfs_alloc_read_agf(mp, tp, agno, 0, &agbp);
1620 if (error)
1621 goto out_trans;
1622 if (!agbp) {
1623 error = -ENOMEM;
1624 goto out_trans;
1625 }
1626 cur = xfs_refcountbt_init_cursor(mp, tp, agbp, agno, NULL);
1627
1628
1629 memset(&low, 0, sizeof(low));
1630 memset(&high, 0, sizeof(high));
1631 low.rc.rc_startblock = XFS_REFC_COW_START;
1632 high.rc.rc_startblock = -1U;
1633 error = xfs_btree_query_range(cur, &low, &high,
1634 xfs_refcount_recover_extent, &debris);
1635 if (error)
1636 goto out_cursor;
1637 xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
1638 xfs_trans_brelse(tp, agbp);
1639 xfs_trans_cancel(tp);
1640
1641
1642 list_for_each_entry_safe(rr, n, &debris, rr_list) {
1643
1644 error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, 0, 0, 0, &tp);
1645 if (error)
1646 goto out_free;
1647
1648 trace_xfs_refcount_recover_extent(mp, agno, &rr->rr_rrec);
1649
1650
1651 xfs_defer_init(&dfops, &fsb);
1652 agbno = rr->rr_rrec.rc_startblock - XFS_REFC_COW_START;
1653 fsb = XFS_AGB_TO_FSB(mp, agno, agbno);
1654 error = xfs_refcount_free_cow_extent(mp, &dfops, fsb,
1655 rr->rr_rrec.rc_blockcount);
1656 if (error)
1657 goto out_defer;
1658
1659
1660 xfs_bmap_add_free(mp, &dfops, fsb,
1661 rr->rr_rrec.rc_blockcount, NULL);
1662
1663 error = xfs_defer_finish(&tp, &dfops);
1664 if (error)
1665 goto out_defer;
1666
1667 error = xfs_trans_commit(tp);
1668 if (error)
1669 goto out_free;
1670
1671 list_del(&rr->rr_list);
1672 kmem_free(rr);
1673 }
1674
1675 return error;
1676out_defer:
1677 xfs_defer_cancel(&dfops);
1678out_trans:
1679 xfs_trans_cancel(tp);
1680out_free:
1681
1682 list_for_each_entry_safe(rr, n, &debris, rr_list) {
1683 list_del(&rr->rr_list);
1684 kmem_free(rr);
1685 }
1686 return error;
1687
1688out_cursor:
1689 xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
1690 xfs_trans_brelse(tp, agbp);
1691 goto out_trans;
1692}
1693
1694
1695int
1696xfs_refcount_has_record(
1697 struct xfs_btree_cur *cur,
1698 xfs_agblock_t bno,
1699 xfs_extlen_t len,
1700 bool *exists)
1701{
1702 union xfs_btree_irec low;
1703 union xfs_btree_irec high;
1704
1705 memset(&low, 0, sizeof(low));
1706 low.rc.rc_startblock = bno;
1707 memset(&high, 0xFF, sizeof(high));
1708 high.rc.rc_startblock = bno + len - 1;
1709
1710 return xfs_btree_has_record(cur, &low, &high, exists);
1711}
1712