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