]>
Commit | Line | Data |
---|---|---|
1 | // SPDX-License-Identifier: GPL-2.0 | |
2 | /* | |
3 | * Interface between ext4 and JBD | |
4 | */ | |
5 | ||
6 | #include "ext4_jbd2.h" | |
7 | ||
8 | #include <trace/events/ext4.h> | |
9 | ||
10 | int ext4_inode_journal_mode(struct inode *inode) | |
11 | { | |
12 | if (EXT4_JOURNAL(inode) == NULL) | |
13 | return EXT4_INODE_WRITEBACK_DATA_MODE; /* writeback */ | |
14 | /* We do not support data journalling with delayed allocation */ | |
15 | if (!S_ISREG(inode->i_mode) || | |
16 | ext4_test_inode_flag(inode, EXT4_INODE_EA_INODE) || | |
17 | test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA || | |
18 | (ext4_test_inode_flag(inode, EXT4_INODE_JOURNAL_DATA) && | |
19 | !test_opt(inode->i_sb, DELALLOC))) { | |
20 | /* We do not support data journalling for encrypted data */ | |
21 | if (S_ISREG(inode->i_mode) && IS_ENCRYPTED(inode)) | |
22 | return EXT4_INODE_ORDERED_DATA_MODE; /* ordered */ | |
23 | return EXT4_INODE_JOURNAL_DATA_MODE; /* journal data */ | |
24 | } | |
25 | if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_ORDERED_DATA) | |
26 | return EXT4_INODE_ORDERED_DATA_MODE; /* ordered */ | |
27 | if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_WRITEBACK_DATA) | |
28 | return EXT4_INODE_WRITEBACK_DATA_MODE; /* writeback */ | |
29 | BUG(); | |
30 | } | |
31 | ||
32 | /* Just increment the non-pointer handle value */ | |
33 | static handle_t *ext4_get_nojournal(void) | |
34 | { | |
35 | handle_t *handle = current->journal_info; | |
36 | unsigned long ref_cnt = (unsigned long)handle; | |
37 | ||
38 | BUG_ON(ref_cnt >= EXT4_NOJOURNAL_MAX_REF_COUNT); | |
39 | ||
40 | ref_cnt++; | |
41 | handle = (handle_t *)ref_cnt; | |
42 | ||
43 | current->journal_info = handle; | |
44 | return handle; | |
45 | } | |
46 | ||
47 | ||
48 | /* Decrement the non-pointer handle value */ | |
49 | static void ext4_put_nojournal(handle_t *handle) | |
50 | { | |
51 | unsigned long ref_cnt = (unsigned long)handle; | |
52 | ||
53 | BUG_ON(ref_cnt == 0); | |
54 | ||
55 | ref_cnt--; | |
56 | handle = (handle_t *)ref_cnt; | |
57 | ||
58 | current->journal_info = handle; | |
59 | } | |
60 | ||
61 | /* | |
62 | * Wrappers for jbd2_journal_start/end. | |
63 | */ | |
64 | static int ext4_journal_check_start(struct super_block *sb) | |
65 | { | |
66 | journal_t *journal; | |
67 | ||
68 | might_sleep(); | |
69 | ||
70 | if (unlikely(ext4_forced_shutdown(sb))) | |
71 | return -EIO; | |
72 | ||
73 | if (WARN_ON_ONCE(sb_rdonly(sb))) | |
74 | return -EROFS; | |
75 | ||
76 | WARN_ON(sb->s_writers.frozen == SB_FREEZE_COMPLETE); | |
77 | journal = EXT4_SB(sb)->s_journal; | |
78 | /* | |
79 | * Special case here: if the journal has aborted behind our | |
80 | * backs (eg. EIO in the commit thread), then we still need to | |
81 | * take the FS itself readonly cleanly. | |
82 | */ | |
83 | if (journal && is_journal_aborted(journal)) { | |
84 | ext4_abort(sb, -journal->j_errno, "Detected aborted journal"); | |
85 | return -EROFS; | |
86 | } | |
87 | return 0; | |
88 | } | |
89 | ||
90 | handle_t *__ext4_journal_start_sb(struct inode *inode, | |
91 | struct super_block *sb, unsigned int line, | |
92 | int type, int blocks, int rsv_blocks, | |
93 | int revoke_creds) | |
94 | { | |
95 | journal_t *journal; | |
96 | int err; | |
97 | if (inode) | |
98 | trace_ext4_journal_start_inode(inode, blocks, rsv_blocks, | |
99 | revoke_creds, type, | |
100 | _RET_IP_); | |
101 | else | |
102 | trace_ext4_journal_start_sb(sb, blocks, rsv_blocks, | |
103 | revoke_creds, type, | |
104 | _RET_IP_); | |
105 | err = ext4_journal_check_start(sb); | |
106 | if (err < 0) | |
107 | return ERR_PTR(err); | |
108 | ||
109 | journal = EXT4_SB(sb)->s_journal; | |
110 | if (!journal || (EXT4_SB(sb)->s_mount_state & EXT4_FC_REPLAY)) | |
111 | return ext4_get_nojournal(); | |
112 | return jbd2__journal_start(journal, blocks, rsv_blocks, revoke_creds, | |
113 | GFP_NOFS, type, line); | |
114 | } | |
115 | ||
116 | int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle) | |
117 | { | |
118 | struct super_block *sb; | |
119 | int err; | |
120 | int rc; | |
121 | ||
122 | if (!ext4_handle_valid(handle)) { | |
123 | ext4_put_nojournal(handle); | |
124 | return 0; | |
125 | } | |
126 | ||
127 | err = handle->h_err; | |
128 | if (!handle->h_transaction) { | |
129 | rc = jbd2_journal_stop(handle); | |
130 | return err ? err : rc; | |
131 | } | |
132 | ||
133 | sb = handle->h_transaction->t_journal->j_private; | |
134 | rc = jbd2_journal_stop(handle); | |
135 | ||
136 | if (!err) | |
137 | err = rc; | |
138 | if (err) | |
139 | __ext4_std_error(sb, where, line, err); | |
140 | return err; | |
141 | } | |
142 | ||
143 | handle_t *__ext4_journal_start_reserved(handle_t *handle, unsigned int line, | |
144 | int type) | |
145 | { | |
146 | struct super_block *sb; | |
147 | int err; | |
148 | ||
149 | if (!ext4_handle_valid(handle)) | |
150 | return ext4_get_nojournal(); | |
151 | ||
152 | sb = handle->h_journal->j_private; | |
153 | trace_ext4_journal_start_reserved(sb, | |
154 | jbd2_handle_buffer_credits(handle), _RET_IP_); | |
155 | err = ext4_journal_check_start(sb); | |
156 | if (err < 0) { | |
157 | jbd2_journal_free_reserved(handle); | |
158 | return ERR_PTR(err); | |
159 | } | |
160 | ||
161 | err = jbd2_journal_start_reserved(handle, type, line); | |
162 | if (err < 0) | |
163 | return ERR_PTR(err); | |
164 | return handle; | |
165 | } | |
166 | ||
167 | int __ext4_journal_ensure_credits(handle_t *handle, int check_cred, | |
168 | int extend_cred, int revoke_cred) | |
169 | { | |
170 | if (!ext4_handle_valid(handle)) | |
171 | return 0; | |
172 | if (is_handle_aborted(handle)) | |
173 | return -EROFS; | |
174 | if (jbd2_handle_buffer_credits(handle) >= check_cred && | |
175 | handle->h_revoke_credits >= revoke_cred) | |
176 | return 0; | |
177 | extend_cred = max(0, extend_cred - jbd2_handle_buffer_credits(handle)); | |
178 | revoke_cred = max(0, revoke_cred - handle->h_revoke_credits); | |
179 | return ext4_journal_extend(handle, extend_cred, revoke_cred); | |
180 | } | |
181 | ||
182 | static void ext4_journal_abort_handle(const char *caller, unsigned int line, | |
183 | const char *err_fn, | |
184 | struct buffer_head *bh, | |
185 | handle_t *handle, int err) | |
186 | { | |
187 | char nbuf[16]; | |
188 | const char *errstr = ext4_decode_error(NULL, err, nbuf); | |
189 | ||
190 | BUG_ON(!ext4_handle_valid(handle)); | |
191 | ||
192 | if (bh) | |
193 | BUFFER_TRACE(bh, "abort"); | |
194 | ||
195 | if (!handle->h_err) | |
196 | handle->h_err = err; | |
197 | ||
198 | if (is_handle_aborted(handle)) | |
199 | return; | |
200 | ||
201 | printk(KERN_ERR "EXT4-fs: %s:%d: aborting transaction: %s in %s\n", | |
202 | caller, line, errstr, err_fn); | |
203 | ||
204 | jbd2_journal_abort_handle(handle); | |
205 | } | |
206 | ||
207 | static void ext4_check_bdev_write_error(struct super_block *sb) | |
208 | { | |
209 | struct address_space *mapping = sb->s_bdev->bd_mapping; | |
210 | struct ext4_sb_info *sbi = EXT4_SB(sb); | |
211 | int err; | |
212 | ||
213 | /* | |
214 | * If the block device has write error flag, it may have failed to | |
215 | * async write out metadata buffers in the background. In this case, | |
216 | * we could read old data from disk and write it out again, which | |
217 | * may lead to on-disk filesystem inconsistency. | |
218 | */ | |
219 | if (errseq_check(&mapping->wb_err, READ_ONCE(sbi->s_bdev_wb_err))) { | |
220 | spin_lock(&sbi->s_bdev_wb_lock); | |
221 | err = errseq_check_and_advance(&mapping->wb_err, &sbi->s_bdev_wb_err); | |
222 | spin_unlock(&sbi->s_bdev_wb_lock); | |
223 | if (err) | |
224 | ext4_error_err(sb, -err, | |
225 | "Error while async write back metadata"); | |
226 | } | |
227 | } | |
228 | ||
229 | int __ext4_journal_get_write_access(const char *where, unsigned int line, | |
230 | handle_t *handle, struct super_block *sb, | |
231 | struct buffer_head *bh, | |
232 | enum ext4_journal_trigger_type trigger_type) | |
233 | { | |
234 | int err; | |
235 | ||
236 | might_sleep(); | |
237 | ||
238 | if (ext4_handle_valid(handle)) { | |
239 | err = jbd2_journal_get_write_access(handle, bh); | |
240 | if (err) { | |
241 | ext4_journal_abort_handle(where, line, __func__, bh, | |
242 | handle, err); | |
243 | return err; | |
244 | } | |
245 | } else | |
246 | ext4_check_bdev_write_error(sb); | |
247 | if (trigger_type == EXT4_JTR_NONE || !ext4_has_metadata_csum(sb)) | |
248 | return 0; | |
249 | BUG_ON(trigger_type >= EXT4_JOURNAL_TRIGGER_COUNT); | |
250 | jbd2_journal_set_triggers(bh, | |
251 | &EXT4_SB(sb)->s_journal_triggers[trigger_type].tr_triggers); | |
252 | return 0; | |
253 | } | |
254 | ||
255 | /* | |
256 | * The ext4 forget function must perform a revoke if we are freeing data | |
257 | * which has been journaled. Metadata (eg. indirect blocks) must be | |
258 | * revoked in all cases. | |
259 | * | |
260 | * "bh" may be NULL: a metadata block may have been freed from memory | |
261 | * but there may still be a record of it in the journal, and that record | |
262 | * still needs to be revoked. | |
263 | */ | |
264 | int __ext4_forget(const char *where, unsigned int line, handle_t *handle, | |
265 | int is_metadata, struct inode *inode, | |
266 | struct buffer_head *bh, ext4_fsblk_t blocknr) | |
267 | { | |
268 | int err; | |
269 | ||
270 | might_sleep(); | |
271 | ||
272 | trace_ext4_forget(inode, is_metadata, blocknr); | |
273 | BUFFER_TRACE(bh, "enter"); | |
274 | ||
275 | ext4_debug("forgetting bh %p: is_metadata=%d, mode %o, data mode %x\n", | |
276 | bh, is_metadata, inode->i_mode, | |
277 | test_opt(inode->i_sb, DATA_FLAGS)); | |
278 | ||
279 | /* In the no journal case, we can just do a bforget and return */ | |
280 | if (!ext4_handle_valid(handle)) { | |
281 | bforget(bh); | |
282 | return 0; | |
283 | } | |
284 | ||
285 | /* Never use the revoke function if we are doing full data | |
286 | * journaling: there is no need to, and a V1 superblock won't | |
287 | * support it. Otherwise, only skip the revoke on un-journaled | |
288 | * data blocks. */ | |
289 | ||
290 | if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA || | |
291 | (!is_metadata && !ext4_should_journal_data(inode))) { | |
292 | if (bh) { | |
293 | BUFFER_TRACE(bh, "call jbd2_journal_forget"); | |
294 | err = jbd2_journal_forget(handle, bh); | |
295 | if (err) | |
296 | ext4_journal_abort_handle(where, line, __func__, | |
297 | bh, handle, err); | |
298 | return err; | |
299 | } | |
300 | return 0; | |
301 | } | |
302 | ||
303 | /* | |
304 | * data!=journal && (is_metadata || should_journal_data(inode)) | |
305 | */ | |
306 | BUFFER_TRACE(bh, "call jbd2_journal_revoke"); | |
307 | err = jbd2_journal_revoke(handle, blocknr, bh); | |
308 | if (err) { | |
309 | ext4_journal_abort_handle(where, line, __func__, | |
310 | bh, handle, err); | |
311 | __ext4_error(inode->i_sb, where, line, true, -err, 0, | |
312 | "error %d when attempting revoke", err); | |
313 | } | |
314 | BUFFER_TRACE(bh, "exit"); | |
315 | return err; | |
316 | } | |
317 | ||
318 | int __ext4_journal_get_create_access(const char *where, unsigned int line, | |
319 | handle_t *handle, struct super_block *sb, | |
320 | struct buffer_head *bh, | |
321 | enum ext4_journal_trigger_type trigger_type) | |
322 | { | |
323 | int err; | |
324 | ||
325 | if (!ext4_handle_valid(handle)) | |
326 | return 0; | |
327 | ||
328 | err = jbd2_journal_get_create_access(handle, bh); | |
329 | if (err) { | |
330 | ext4_journal_abort_handle(where, line, __func__, bh, handle, | |
331 | err); | |
332 | return err; | |
333 | } | |
334 | if (trigger_type == EXT4_JTR_NONE || !ext4_has_metadata_csum(sb)) | |
335 | return 0; | |
336 | BUG_ON(trigger_type >= EXT4_JOURNAL_TRIGGER_COUNT); | |
337 | jbd2_journal_set_triggers(bh, | |
338 | &EXT4_SB(sb)->s_journal_triggers[trigger_type].tr_triggers); | |
339 | return 0; | |
340 | } | |
341 | ||
342 | int __ext4_handle_dirty_metadata(const char *where, unsigned int line, | |
343 | handle_t *handle, struct inode *inode, | |
344 | struct buffer_head *bh) | |
345 | { | |
346 | int err = 0; | |
347 | ||
348 | might_sleep(); | |
349 | ||
350 | set_buffer_meta(bh); | |
351 | set_buffer_prio(bh); | |
352 | set_buffer_uptodate(bh); | |
353 | if (ext4_handle_valid(handle)) { | |
354 | err = jbd2_journal_dirty_metadata(handle, bh); | |
355 | /* Errors can only happen due to aborted journal or a nasty bug */ | |
356 | if (!is_handle_aborted(handle) && WARN_ON_ONCE(err)) { | |
357 | ext4_journal_abort_handle(where, line, __func__, bh, | |
358 | handle, err); | |
359 | if (inode == NULL) { | |
360 | pr_err("EXT4: jbd2_journal_dirty_metadata " | |
361 | "failed: handle type %u started at " | |
362 | "line %u, credits %u/%u, errcode %d", | |
363 | handle->h_type, | |
364 | handle->h_line_no, | |
365 | handle->h_requested_credits, | |
366 | jbd2_handle_buffer_credits(handle), err); | |
367 | return err; | |
368 | } | |
369 | ext4_error_inode(inode, where, line, | |
370 | bh->b_blocknr, | |
371 | "journal_dirty_metadata failed: " | |
372 | "handle type %u started at line %u, " | |
373 | "credits %u/%u, errcode %d", | |
374 | handle->h_type, | |
375 | handle->h_line_no, | |
376 | handle->h_requested_credits, | |
377 | jbd2_handle_buffer_credits(handle), | |
378 | err); | |
379 | } | |
380 | } else { | |
381 | if (inode) | |
382 | mark_buffer_dirty_inode(bh, inode); | |
383 | else | |
384 | mark_buffer_dirty(bh); | |
385 | if (inode && inode_needs_sync(inode)) { | |
386 | sync_dirty_buffer(bh); | |
387 | if (buffer_req(bh) && !buffer_uptodate(bh)) { | |
388 | ext4_error_inode_err(inode, where, line, | |
389 | bh->b_blocknr, EIO, | |
390 | "IO error syncing itable block"); | |
391 | err = -EIO; | |
392 | } | |
393 | } | |
394 | } | |
395 | return err; | |
396 | } |