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