Merge tag 'firewire-update' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394...
[linux-2.6-block.git] / fs / xfs / xfs_trans_refcount.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2016 Oracle.  All Rights Reserved.
4  * Author: Darrick J. Wong <darrick.wong@oracle.com>
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_trans.h"
15 #include "xfs_trans_priv.h"
16 #include "xfs_refcount_item.h"
17 #include "xfs_alloc.h"
18 #include "xfs_refcount.h"
19 #include "xfs_defer.h"
20
21 /*
22  * This routine is called to allocate a "refcount update done"
23  * log item.
24  */
25 struct xfs_cud_log_item *
26 xfs_trans_get_cud(
27         struct xfs_trans                *tp,
28         struct xfs_cui_log_item         *cuip)
29 {
30         struct xfs_cud_log_item         *cudp;
31
32         cudp = xfs_cud_init(tp->t_mountp, cuip);
33         xfs_trans_add_item(tp, &cudp->cud_item);
34         return cudp;
35 }
36
37 /*
38  * Finish an refcount update and log it to the CUD. Note that the
39  * transaction is marked dirty regardless of whether the refcount
40  * update succeeds or fails to support the CUI/CUD lifecycle rules.
41  */
42 int
43 xfs_trans_log_finish_refcount_update(
44         struct xfs_trans                *tp,
45         struct xfs_cud_log_item         *cudp,
46         enum xfs_refcount_intent_type   type,
47         xfs_fsblock_t                   startblock,
48         xfs_extlen_t                    blockcount,
49         xfs_fsblock_t                   *new_fsb,
50         xfs_extlen_t                    *new_len,
51         struct xfs_btree_cur            **pcur)
52 {
53         int                             error;
54
55         error = xfs_refcount_finish_one(tp, type, startblock,
56                         blockcount, new_fsb, new_len, pcur);
57
58         /*
59          * Mark the transaction dirty, even on error. This ensures the
60          * transaction is aborted, which:
61          *
62          * 1.) releases the CUI and frees the CUD
63          * 2.) shuts down the filesystem
64          */
65         tp->t_flags |= XFS_TRANS_DIRTY;
66         set_bit(XFS_LI_DIRTY, &cudp->cud_item.li_flags);
67
68         return error;
69 }
70
71 /* Sort refcount intents by AG. */
72 static int
73 xfs_refcount_update_diff_items(
74         void                            *priv,
75         struct list_head                *a,
76         struct list_head                *b)
77 {
78         struct xfs_mount                *mp = priv;
79         struct xfs_refcount_intent      *ra;
80         struct xfs_refcount_intent      *rb;
81
82         ra = container_of(a, struct xfs_refcount_intent, ri_list);
83         rb = container_of(b, struct xfs_refcount_intent, ri_list);
84         return  XFS_FSB_TO_AGNO(mp, ra->ri_startblock) -
85                 XFS_FSB_TO_AGNO(mp, rb->ri_startblock);
86 }
87
88 /* Get an CUI. */
89 STATIC void *
90 xfs_refcount_update_create_intent(
91         struct xfs_trans                *tp,
92         unsigned int                    count)
93 {
94         struct xfs_cui_log_item         *cuip;
95
96         ASSERT(tp != NULL);
97         ASSERT(count > 0);
98
99         cuip = xfs_cui_init(tp->t_mountp, count);
100         ASSERT(cuip != NULL);
101
102         /*
103          * Get a log_item_desc to point at the new item.
104          */
105         xfs_trans_add_item(tp, &cuip->cui_item);
106         return cuip;
107 }
108
109 /* Set the phys extent flags for this reverse mapping. */
110 static void
111 xfs_trans_set_refcount_flags(
112         struct xfs_phys_extent          *refc,
113         enum xfs_refcount_intent_type   type)
114 {
115         refc->pe_flags = 0;
116         switch (type) {
117         case XFS_REFCOUNT_INCREASE:
118         case XFS_REFCOUNT_DECREASE:
119         case XFS_REFCOUNT_ALLOC_COW:
120         case XFS_REFCOUNT_FREE_COW:
121                 refc->pe_flags |= type;
122                 break;
123         default:
124                 ASSERT(0);
125         }
126 }
127
128 /* Log refcount updates in the intent item. */
129 STATIC void
130 xfs_refcount_update_log_item(
131         struct xfs_trans                *tp,
132         void                            *intent,
133         struct list_head                *item)
134 {
135         struct xfs_cui_log_item         *cuip = intent;
136         struct xfs_refcount_intent      *refc;
137         uint                            next_extent;
138         struct xfs_phys_extent          *ext;
139
140         refc = container_of(item, struct xfs_refcount_intent, ri_list);
141
142         tp->t_flags |= XFS_TRANS_DIRTY;
143         set_bit(XFS_LI_DIRTY, &cuip->cui_item.li_flags);
144
145         /*
146          * atomic_inc_return gives us the value after the increment;
147          * we want to use it as an array index so we need to subtract 1 from
148          * it.
149          */
150         next_extent = atomic_inc_return(&cuip->cui_next_extent) - 1;
151         ASSERT(next_extent < cuip->cui_format.cui_nextents);
152         ext = &cuip->cui_format.cui_extents[next_extent];
153         ext->pe_startblock = refc->ri_startblock;
154         ext->pe_len = refc->ri_blockcount;
155         xfs_trans_set_refcount_flags(ext, refc->ri_type);
156 }
157
158 /* Get an CUD so we can process all the deferred refcount updates. */
159 STATIC void *
160 xfs_refcount_update_create_done(
161         struct xfs_trans                *tp,
162         void                            *intent,
163         unsigned int                    count)
164 {
165         return xfs_trans_get_cud(tp, intent);
166 }
167
168 /* Process a deferred refcount update. */
169 STATIC int
170 xfs_refcount_update_finish_item(
171         struct xfs_trans                *tp,
172         struct list_head                *item,
173         void                            *done_item,
174         void                            **state)
175 {
176         struct xfs_refcount_intent      *refc;
177         xfs_fsblock_t                   new_fsb;
178         xfs_extlen_t                    new_aglen;
179         int                             error;
180
181         refc = container_of(item, struct xfs_refcount_intent, ri_list);
182         error = xfs_trans_log_finish_refcount_update(tp, done_item,
183                         refc->ri_type,
184                         refc->ri_startblock,
185                         refc->ri_blockcount,
186                         &new_fsb, &new_aglen,
187                         (struct xfs_btree_cur **)state);
188         /* Did we run out of reservation?  Requeue what we didn't finish. */
189         if (!error && new_aglen > 0) {
190                 ASSERT(refc->ri_type == XFS_REFCOUNT_INCREASE ||
191                        refc->ri_type == XFS_REFCOUNT_DECREASE);
192                 refc->ri_startblock = new_fsb;
193                 refc->ri_blockcount = new_aglen;
194                 return -EAGAIN;
195         }
196         kmem_free(refc);
197         return error;
198 }
199
200 /* Clean up after processing deferred refcounts. */
201 STATIC void
202 xfs_refcount_update_finish_cleanup(
203         struct xfs_trans        *tp,
204         void                    *state,
205         int                     error)
206 {
207         struct xfs_btree_cur    *rcur = state;
208
209         xfs_refcount_finish_one_cleanup(tp, rcur, error);
210 }
211
212 /* Abort all pending CUIs. */
213 STATIC void
214 xfs_refcount_update_abort_intent(
215         void                            *intent)
216 {
217         xfs_cui_release(intent);
218 }
219
220 /* Cancel a deferred refcount update. */
221 STATIC void
222 xfs_refcount_update_cancel_item(
223         struct list_head                *item)
224 {
225         struct xfs_refcount_intent      *refc;
226
227         refc = container_of(item, struct xfs_refcount_intent, ri_list);
228         kmem_free(refc);
229 }
230
231 const struct xfs_defer_op_type xfs_refcount_update_defer_type = {
232         .max_items      = XFS_CUI_MAX_FAST_EXTENTS,
233         .diff_items     = xfs_refcount_update_diff_items,
234         .create_intent  = xfs_refcount_update_create_intent,
235         .abort_intent   = xfs_refcount_update_abort_intent,
236         .log_item       = xfs_refcount_update_log_item,
237         .create_done    = xfs_refcount_update_create_done,
238         .finish_item    = xfs_refcount_update_finish_item,
239         .finish_cleanup = xfs_refcount_update_finish_cleanup,
240         .cancel_item    = xfs_refcount_update_cancel_item,
241 };