GNU Linux-libre 4.9.309-gnu1
[releases.git] / fs / xfs / xfs_trans_refcount.c
1 /*
2  * Copyright (C) 2016 Oracle.  All Rights Reserved.
3  *
4  * Author: Darrick J. Wong <darrick.wong@oracle.com>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it would be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write the Free Software Foundation,
18  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
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_mount.h"
27 #include "xfs_defer.h"
28 #include "xfs_trans.h"
29 #include "xfs_trans_priv.h"
30 #include "xfs_refcount_item.h"
31 #include "xfs_alloc.h"
32 #include "xfs_refcount.h"
33
34 /*
35  * This routine is called to allocate a "refcount update done"
36  * log item.
37  */
38 struct xfs_cud_log_item *
39 xfs_trans_get_cud(
40         struct xfs_trans                *tp,
41         struct xfs_cui_log_item         *cuip)
42 {
43         struct xfs_cud_log_item         *cudp;
44
45         cudp = xfs_cud_init(tp->t_mountp, cuip);
46         xfs_trans_add_item(tp, &cudp->cud_item);
47         return cudp;
48 }
49
50 /*
51  * Finish an refcount update and log it to the CUD. Note that the
52  * transaction is marked dirty regardless of whether the refcount
53  * update succeeds or fails to support the CUI/CUD lifecycle rules.
54  */
55 int
56 xfs_trans_log_finish_refcount_update(
57         struct xfs_trans                *tp,
58         struct xfs_cud_log_item         *cudp,
59         struct xfs_defer_ops            *dop,
60         enum xfs_refcount_intent_type   type,
61         xfs_fsblock_t                   startblock,
62         xfs_extlen_t                    blockcount,
63         xfs_fsblock_t                   *new_fsb,
64         xfs_extlen_t                    *new_len,
65         struct xfs_btree_cur            **pcur)
66 {
67         int                             error;
68
69         error = xfs_refcount_finish_one(tp, dop, type, startblock,
70                         blockcount, new_fsb, new_len, pcur);
71
72         /*
73          * Mark the transaction dirty, even on error. This ensures the
74          * transaction is aborted, which:
75          *
76          * 1.) releases the CUI and frees the CUD
77          * 2.) shuts down the filesystem
78          */
79         tp->t_flags |= XFS_TRANS_DIRTY;
80         cudp->cud_item.li_desc->lid_flags |= XFS_LID_DIRTY;
81
82         return error;
83 }
84
85 /* Sort refcount intents by AG. */
86 static int
87 xfs_refcount_update_diff_items(
88         void                            *priv,
89         struct list_head                *a,
90         struct list_head                *b)
91 {
92         struct xfs_mount                *mp = priv;
93         struct xfs_refcount_intent      *ra;
94         struct xfs_refcount_intent      *rb;
95
96         ra = container_of(a, struct xfs_refcount_intent, ri_list);
97         rb = container_of(b, struct xfs_refcount_intent, ri_list);
98         return  XFS_FSB_TO_AGNO(mp, ra->ri_startblock) -
99                 XFS_FSB_TO_AGNO(mp, rb->ri_startblock);
100 }
101
102 /* Get an CUI. */
103 STATIC void *
104 xfs_refcount_update_create_intent(
105         struct xfs_trans                *tp,
106         unsigned int                    count)
107 {
108         struct xfs_cui_log_item         *cuip;
109
110         ASSERT(tp != NULL);
111         ASSERT(count > 0);
112
113         cuip = xfs_cui_init(tp->t_mountp, count);
114         ASSERT(cuip != NULL);
115
116         /*
117          * Get a log_item_desc to point at the new item.
118          */
119         xfs_trans_add_item(tp, &cuip->cui_item);
120         return cuip;
121 }
122
123 /* Set the phys extent flags for this reverse mapping. */
124 static void
125 xfs_trans_set_refcount_flags(
126         struct xfs_phys_extent          *refc,
127         enum xfs_refcount_intent_type   type)
128 {
129         refc->pe_flags = 0;
130         switch (type) {
131         case XFS_REFCOUNT_INCREASE:
132         case XFS_REFCOUNT_DECREASE:
133         case XFS_REFCOUNT_ALLOC_COW:
134         case XFS_REFCOUNT_FREE_COW:
135                 refc->pe_flags |= type;
136                 break;
137         default:
138                 ASSERT(0);
139         }
140 }
141
142 /* Log refcount updates in the intent item. */
143 STATIC void
144 xfs_refcount_update_log_item(
145         struct xfs_trans                *tp,
146         void                            *intent,
147         struct list_head                *item)
148 {
149         struct xfs_cui_log_item         *cuip = intent;
150         struct xfs_refcount_intent      *refc;
151         uint                            next_extent;
152         struct xfs_phys_extent          *ext;
153
154         refc = container_of(item, struct xfs_refcount_intent, ri_list);
155
156         tp->t_flags |= XFS_TRANS_DIRTY;
157         cuip->cui_item.li_desc->lid_flags |= XFS_LID_DIRTY;
158
159         /*
160          * atomic_inc_return gives us the value after the increment;
161          * we want to use it as an array index so we need to subtract 1 from
162          * it.
163          */
164         next_extent = atomic_inc_return(&cuip->cui_next_extent) - 1;
165         ASSERT(next_extent < cuip->cui_format.cui_nextents);
166         ext = &cuip->cui_format.cui_extents[next_extent];
167         ext->pe_startblock = refc->ri_startblock;
168         ext->pe_len = refc->ri_blockcount;
169         xfs_trans_set_refcount_flags(ext, refc->ri_type);
170 }
171
172 /* Get an CUD so we can process all the deferred refcount updates. */
173 STATIC void *
174 xfs_refcount_update_create_done(
175         struct xfs_trans                *tp,
176         void                            *intent,
177         unsigned int                    count)
178 {
179         return xfs_trans_get_cud(tp, intent);
180 }
181
182 /* Process a deferred refcount update. */
183 STATIC int
184 xfs_refcount_update_finish_item(
185         struct xfs_trans                *tp,
186         struct xfs_defer_ops            *dop,
187         struct list_head                *item,
188         void                            *done_item,
189         void                            **state)
190 {
191         struct xfs_refcount_intent      *refc;
192         xfs_fsblock_t                   new_fsb;
193         xfs_extlen_t                    new_aglen;
194         int                             error;
195
196         refc = container_of(item, struct xfs_refcount_intent, ri_list);
197         error = xfs_trans_log_finish_refcount_update(tp, done_item, dop,
198                         refc->ri_type,
199                         refc->ri_startblock,
200                         refc->ri_blockcount,
201                         &new_fsb, &new_aglen,
202                         (struct xfs_btree_cur **)state);
203         /* Did we run out of reservation?  Requeue what we didn't finish. */
204         if (!error && new_aglen > 0) {
205                 ASSERT(refc->ri_type == XFS_REFCOUNT_INCREASE ||
206                        refc->ri_type == XFS_REFCOUNT_DECREASE);
207                 refc->ri_startblock = new_fsb;
208                 refc->ri_blockcount = new_aglen;
209                 return -EAGAIN;
210         }
211         kmem_free(refc);
212         return error;
213 }
214
215 /* Clean up after processing deferred refcounts. */
216 STATIC void
217 xfs_refcount_update_finish_cleanup(
218         struct xfs_trans        *tp,
219         void                    *state,
220         int                     error)
221 {
222         struct xfs_btree_cur    *rcur = state;
223
224         xfs_refcount_finish_one_cleanup(tp, rcur, error);
225 }
226
227 /* Abort all pending CUIs. */
228 STATIC void
229 xfs_refcount_update_abort_intent(
230         void                            *intent)
231 {
232         xfs_cui_release(intent);
233 }
234
235 /* Cancel a deferred refcount update. */
236 STATIC void
237 xfs_refcount_update_cancel_item(
238         struct list_head                *item)
239 {
240         struct xfs_refcount_intent      *refc;
241
242         refc = container_of(item, struct xfs_refcount_intent, ri_list);
243         kmem_free(refc);
244 }
245
246 static const struct xfs_defer_op_type xfs_refcount_update_defer_type = {
247         .type           = XFS_DEFER_OPS_TYPE_REFCOUNT,
248         .max_items      = XFS_CUI_MAX_FAST_EXTENTS,
249         .diff_items     = xfs_refcount_update_diff_items,
250         .create_intent  = xfs_refcount_update_create_intent,
251         .abort_intent   = xfs_refcount_update_abort_intent,
252         .log_item       = xfs_refcount_update_log_item,
253         .create_done    = xfs_refcount_update_create_done,
254         .finish_item    = xfs_refcount_update_finish_item,
255         .finish_cleanup = xfs_refcount_update_finish_cleanup,
256         .cancel_item    = xfs_refcount_update_cancel_item,
257 };
258
259 /* Register the deferred op type. */
260 void
261 xfs_refcount_update_init_defer_op(void)
262 {
263         xfs_defer_init_op_type(&xfs_refcount_update_defer_type);
264 }