]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
8984d137 AM |
2 | /* |
3 | * Interface between ext4 and JBD | |
4 | */ | |
5 | ||
3dcf5451 | 6 | #include "ext4_jbd2.h" |
8984d137 | 7 | |
d6797d14 TT |
8 | #include <trace/events/ext4.h> |
9 | ||
722887dd TT |
10 | /* Just increment the non-pointer handle value */ |
11 | static handle_t *ext4_get_nojournal(void) | |
12 | { | |
13 | handle_t *handle = current->journal_info; | |
14 | unsigned long ref_cnt = (unsigned long)handle; | |
15 | ||
16 | BUG_ON(ref_cnt >= EXT4_NOJOURNAL_MAX_REF_COUNT); | |
17 | ||
18 | ref_cnt++; | |
19 | handle = (handle_t *)ref_cnt; | |
20 | ||
21 | current->journal_info = handle; | |
22 | return handle; | |
23 | } | |
24 | ||
25 | ||
26 | /* Decrement the non-pointer handle value */ | |
27 | static void ext4_put_nojournal(handle_t *handle) | |
28 | { | |
29 | unsigned long ref_cnt = (unsigned long)handle; | |
30 | ||
31 | BUG_ON(ref_cnt == 0); | |
32 | ||
33 | ref_cnt--; | |
34 | handle = (handle_t *)ref_cnt; | |
35 | ||
36 | current->journal_info = handle; | |
37 | } | |
38 | ||
39 | /* | |
40 | * Wrappers for jbd2_journal_start/end. | |
41 | */ | |
5fe2fe89 | 42 | static int ext4_journal_check_start(struct super_block *sb) |
722887dd TT |
43 | { |
44 | journal_t *journal; | |
45 | ||
b10a44c3 | 46 | might_sleep(); |
0db1ff22 TT |
47 | |
48 | if (unlikely(ext4_forced_shutdown(EXT4_SB(sb)))) | |
49 | return -EIO; | |
50 | ||
bc98a42c | 51 | if (sb_rdonly(sb)) |
5fe2fe89 | 52 | return -EROFS; |
722887dd TT |
53 | WARN_ON(sb->s_writers.frozen == SB_FREEZE_COMPLETE); |
54 | journal = EXT4_SB(sb)->s_journal; | |
722887dd TT |
55 | /* |
56 | * Special case here: if the journal has aborted behind our | |
57 | * backs (eg. EIO in the commit thread), then we still need to | |
58 | * take the FS itself readonly cleanly. | |
59 | */ | |
5fe2fe89 | 60 | if (journal && is_journal_aborted(journal)) { |
722887dd | 61 | ext4_abort(sb, "Detected aborted journal"); |
5fe2fe89 | 62 | return -EROFS; |
722887dd | 63 | } |
5fe2fe89 JK |
64 | return 0; |
65 | } | |
66 | ||
67 | handle_t *__ext4_journal_start_sb(struct super_block *sb, unsigned int line, | |
68 | int type, int blocks, int rsv_blocks) | |
69 | { | |
70 | journal_t *journal; | |
71 | int err; | |
72 | ||
73 | trace_ext4_journal_start(sb, blocks, rsv_blocks, _RET_IP_); | |
74 | err = ext4_journal_check_start(sb); | |
75 | if (err < 0) | |
76 | return ERR_PTR(err); | |
77 | ||
78 | journal = EXT4_SB(sb)->s_journal; | |
79 | if (!journal) | |
80 | return ext4_get_nojournal(); | |
81 | return jbd2__journal_start(journal, blocks, rsv_blocks, GFP_NOFS, | |
82 | type, line); | |
722887dd TT |
83 | } |
84 | ||
85 | int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle) | |
86 | { | |
87 | struct super_block *sb; | |
88 | int err; | |
89 | int rc; | |
90 | ||
91 | if (!ext4_handle_valid(handle)) { | |
92 | ext4_put_nojournal(handle); | |
93 | return 0; | |
94 | } | |
9d506594 | 95 | |
6934da92 | 96 | err = handle->h_err; |
9d506594 | 97 | if (!handle->h_transaction) { |
6934da92 LC |
98 | rc = jbd2_journal_stop(handle); |
99 | return err ? err : rc; | |
9d506594 LC |
100 | } |
101 | ||
722887dd | 102 | sb = handle->h_transaction->t_journal->j_private; |
722887dd TT |
103 | rc = jbd2_journal_stop(handle); |
104 | ||
105 | if (!err) | |
106 | err = rc; | |
107 | if (err) | |
108 | __ext4_std_error(sb, where, line, err); | |
109 | return err; | |
110 | } | |
111 | ||
5fe2fe89 JK |
112 | handle_t *__ext4_journal_start_reserved(handle_t *handle, unsigned int line, |
113 | int type) | |
114 | { | |
115 | struct super_block *sb; | |
116 | int err; | |
117 | ||
118 | if (!ext4_handle_valid(handle)) | |
119 | return ext4_get_nojournal(); | |
120 | ||
121 | sb = handle->h_journal->j_private; | |
122 | trace_ext4_journal_start_reserved(sb, handle->h_buffer_credits, | |
123 | _RET_IP_); | |
124 | err = ext4_journal_check_start(sb); | |
125 | if (err < 0) { | |
126 | jbd2_journal_free_reserved(handle); | |
127 | return ERR_PTR(err); | |
128 | } | |
129 | ||
130 | err = jbd2_journal_start_reserved(handle, type, line); | |
131 | if (err < 0) | |
132 | return ERR_PTR(err); | |
133 | return handle; | |
134 | } | |
135 | ||
a4130367 JK |
136 | int __ext4_journal_ensure_credits(handle_t *handle, int check_cred, |
137 | int extend_cred) | |
138 | { | |
139 | if (!ext4_handle_valid(handle)) | |
140 | return 0; | |
141 | if (handle->h_buffer_credits >= check_cred) | |
142 | return 0; | |
143 | return ext4_journal_extend(handle, | |
144 | extend_cred - handle->h_buffer_credits); | |
145 | } | |
146 | ||
c197855e SH |
147 | static void ext4_journal_abort_handle(const char *caller, unsigned int line, |
148 | const char *err_fn, | |
149 | struct buffer_head *bh, | |
150 | handle_t *handle, int err) | |
722887dd TT |
151 | { |
152 | char nbuf[16]; | |
153 | const char *errstr = ext4_decode_error(NULL, err, nbuf); | |
154 | ||
155 | BUG_ON(!ext4_handle_valid(handle)); | |
156 | ||
157 | if (bh) | |
158 | BUFFER_TRACE(bh, "abort"); | |
159 | ||
160 | if (!handle->h_err) | |
161 | handle->h_err = err; | |
162 | ||
163 | if (is_handle_aborted(handle)) | |
164 | return; | |
165 | ||
166 | printk(KERN_ERR "EXT4-fs: %s:%d: aborting transaction: %s in %s\n", | |
167 | caller, line, errstr, err_fn); | |
168 | ||
169 | jbd2_journal_abort_handle(handle); | |
170 | } | |
171 | ||
90c7201b TT |
172 | int __ext4_journal_get_write_access(const char *where, unsigned int line, |
173 | handle_t *handle, struct buffer_head *bh) | |
8984d137 | 174 | { |
0390131b FM |
175 | int err = 0; |
176 | ||
b10a44c3 TT |
177 | might_sleep(); |
178 | ||
0390131b FM |
179 | if (ext4_handle_valid(handle)) { |
180 | err = jbd2_journal_get_write_access(handle, bh); | |
181 | if (err) | |
90c7201b | 182 | ext4_journal_abort_handle(where, line, __func__, bh, |
0390131b FM |
183 | handle, err); |
184 | } | |
8984d137 AM |
185 | return err; |
186 | } | |
187 | ||
d6797d14 TT |
188 | /* |
189 | * The ext4 forget function must perform a revoke if we are freeing data | |
190 | * which has been journaled. Metadata (eg. indirect blocks) must be | |
191 | * revoked in all cases. | |
192 | * | |
193 | * "bh" may be NULL: a metadata block may have been freed from memory | |
194 | * but there may still be a record of it in the journal, and that record | |
195 | * still needs to be revoked. | |
196 | * | |
197 | * If the handle isn't valid we're not journaling, but we still need to | |
198 | * call into ext4_journal_revoke() to put the buffer head. | |
199 | */ | |
90c7201b TT |
200 | int __ext4_forget(const char *where, unsigned int line, handle_t *handle, |
201 | int is_metadata, struct inode *inode, | |
202 | struct buffer_head *bh, ext4_fsblk_t blocknr) | |
d6797d14 TT |
203 | { |
204 | int err; | |
205 | ||
206 | might_sleep(); | |
207 | ||
208 | trace_ext4_forget(inode, is_metadata, blocknr); | |
209 | BUFFER_TRACE(bh, "enter"); | |
210 | ||
211 | jbd_debug(4, "forgetting bh %p: is_metadata = %d, mode %o, " | |
212 | "data mode %x\n", | |
213 | bh, is_metadata, inode->i_mode, | |
214 | test_opt(inode->i_sb, DATA_FLAGS)); | |
215 | ||
e4684b3f TT |
216 | /* In the no journal case, we can just do a bforget and return */ |
217 | if (!ext4_handle_valid(handle)) { | |
218 | bforget(bh); | |
219 | return 0; | |
220 | } | |
221 | ||
d6797d14 TT |
222 | /* Never use the revoke function if we are doing full data |
223 | * journaling: there is no need to, and a V1 superblock won't | |
224 | * support it. Otherwise, only skip the revoke on un-journaled | |
225 | * data blocks. */ | |
226 | ||
227 | if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA || | |
228 | (!is_metadata && !ext4_should_journal_data(inode))) { | |
229 | if (bh) { | |
230 | BUFFER_TRACE(bh, "call jbd2_journal_forget"); | |
b7e57e7c TT |
231 | err = jbd2_journal_forget(handle, bh); |
232 | if (err) | |
90c7201b TT |
233 | ext4_journal_abort_handle(where, line, __func__, |
234 | bh, handle, err); | |
b7e57e7c | 235 | return err; |
d6797d14 TT |
236 | } |
237 | return 0; | |
238 | } | |
239 | ||
240 | /* | |
241 | * data!=journal && (is_metadata || should_journal_data(inode)) | |
242 | */ | |
e4684b3f TT |
243 | BUFFER_TRACE(bh, "call jbd2_journal_revoke"); |
244 | err = jbd2_journal_revoke(handle, blocknr, bh); | |
245 | if (err) { | |
90c7201b TT |
246 | ext4_journal_abort_handle(where, line, __func__, |
247 | bh, handle, err); | |
c398eda0 TT |
248 | __ext4_abort(inode->i_sb, where, line, |
249 | "error %d when attempting revoke", err); | |
e4684b3f | 250 | } |
d6797d14 TT |
251 | BUFFER_TRACE(bh, "exit"); |
252 | return err; | |
253 | } | |
254 | ||
90c7201b | 255 | int __ext4_journal_get_create_access(const char *where, unsigned int line, |
8984d137 AM |
256 | handle_t *handle, struct buffer_head *bh) |
257 | { | |
0390131b FM |
258 | int err = 0; |
259 | ||
260 | if (ext4_handle_valid(handle)) { | |
261 | err = jbd2_journal_get_create_access(handle, bh); | |
262 | if (err) | |
90c7201b TT |
263 | ext4_journal_abort_handle(where, line, __func__, |
264 | bh, handle, err); | |
0390131b | 265 | } |
8984d137 AM |
266 | return err; |
267 | } | |
268 | ||
90c7201b TT |
269 | int __ext4_handle_dirty_metadata(const char *where, unsigned int line, |
270 | handle_t *handle, struct inode *inode, | |
271 | struct buffer_head *bh) | |
8984d137 | 272 | { |
0390131b FM |
273 | int err = 0; |
274 | ||
b10a44c3 TT |
275 | might_sleep(); |
276 | ||
13fca323 TT |
277 | set_buffer_meta(bh); |
278 | set_buffer_prio(bh); | |
0390131b FM |
279 | if (ext4_handle_valid(handle)) { |
280 | err = jbd2_journal_dirty_metadata(handle, bh); | |
c5d31192 DM |
281 | /* Errors can only happen due to aborted journal or a nasty bug */ |
282 | if (!is_handle_aborted(handle) && WARN_ON_ONCE(err)) { | |
91aa11fa JK |
283 | ext4_journal_abort_handle(where, line, __func__, bh, |
284 | handle, err); | |
66a4cb18 TT |
285 | if (inode == NULL) { |
286 | pr_err("EXT4: jbd2_journal_dirty_metadata " | |
287 | "failed: handle type %u started at " | |
288 | "line %u, credits %u/%u, errcode %d", | |
289 | handle->h_type, | |
290 | handle->h_line_no, | |
291 | handle->h_requested_credits, | |
292 | handle->h_buffer_credits, err); | |
293 | return err; | |
294 | } | |
ae1495b1 TT |
295 | ext4_error_inode(inode, where, line, |
296 | bh->b_blocknr, | |
297 | "journal_dirty_metadata failed: " | |
298 | "handle type %u started at line %u, " | |
299 | "credits %u/%u, errcode %d", | |
300 | handle->h_type, | |
301 | handle->h_line_no, | |
302 | handle->h_requested_credits, | |
303 | handle->h_buffer_credits, err); | |
9ea7a0df | 304 | } |
0390131b | 305 | } else { |
73b50c1c | 306 | if (inode) |
fe188c0e TT |
307 | mark_buffer_dirty_inode(bh, inode); |
308 | else | |
309 | mark_buffer_dirty(bh); | |
0390131b FM |
310 | if (inode && inode_needs_sync(inode)) { |
311 | sync_dirty_buffer(bh); | |
312 | if (buffer_req(bh) && !buffer_uptodate(bh)) { | |
1c13d5c0 TT |
313 | struct ext4_super_block *es; |
314 | ||
315 | es = EXT4_SB(inode->i_sb)->s_es; | |
316 | es->s_last_error_block = | |
317 | cpu_to_le64(bh->b_blocknr); | |
c398eda0 TT |
318 | ext4_error_inode(inode, where, line, |
319 | bh->b_blocknr, | |
320 | "IO error syncing itable block"); | |
0390131b FM |
321 | err = -EIO; |
322 | } | |
323 | } | |
324 | } | |
8984d137 AM |
325 | return err; |
326 | } | |
a0375156 | 327 | |
90c7201b | 328 | int __ext4_handle_dirty_super(const char *where, unsigned int line, |
b50924c2 | 329 | handle_t *handle, struct super_block *sb) |
a0375156 TT |
330 | { |
331 | struct buffer_head *bh = EXT4_SB(sb)->s_sbh; | |
332 | int err = 0; | |
333 | ||
06db49e6 | 334 | ext4_superblock_csum_set(sb); |
a0375156 TT |
335 | if (ext4_handle_valid(handle)) { |
336 | err = jbd2_journal_dirty_metadata(handle, bh); | |
337 | if (err) | |
90c7201b TT |
338 | ext4_journal_abort_handle(where, line, __func__, |
339 | bh, handle, err); | |
06db49e6 | 340 | } else |
a9c47317 | 341 | mark_buffer_dirty(bh); |
a0375156 TT |
342 | return err; |
343 | } |