Skip to content

Commit 569342d

Browse files
matthewbobrowskitytso
authored andcommitted
ext4: move inode extension/truncate code out from ->iomap_end() callback
In preparation for implementing the iomap direct I/O modifications, the inode extension/truncate code needs to be moved out from the ext4_iomap_end() callback. For direct I/O, if the current code remained, it would behave incorrrectly. Updating the inode size prior to converting unwritten extents would potentially allow a racing direct I/O read to find unwritten extents before being converted correctly. The inode extension/truncate code now resides within a new helper ext4_handle_inode_extension(). This function has been designed so that it can accommodate for both DAX and direct I/O extension/truncate operations. Signed-off-by: Matthew Bobrowski <[email protected]> Reviewed-by: Jan Kara <[email protected]> Reviewed-by: Ritesh Harjani <[email protected]> Link: https://lore.kernel.org/r/d41ffa26e20b15b12895812c3cad7c91a6a59bc6.1572949325.git.mbobrowski@mbobrowski.org Signed-off-by: Theodore Ts'o <[email protected]>
1 parent b1b4705 commit 569342d

File tree

2 files changed

+89
-48
lines changed

2 files changed

+89
-48
lines changed

fs/ext4/file.c

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "ext4_jbd2.h"
3434
#include "xattr.h"
3535
#include "acl.h"
36+
#include "truncate.h"
3637

3738
static bool ext4_dio_supported(struct inode *inode)
3839
{
@@ -234,12 +235,95 @@ static ssize_t ext4_write_checks(struct kiocb *iocb, struct iov_iter *from)
234235
return iov_iter_count(from);
235236
}
236237

238+
static ssize_t ext4_handle_inode_extension(struct inode *inode, loff_t offset,
239+
ssize_t written, size_t count)
240+
{
241+
handle_t *handle;
242+
bool truncate = false;
243+
u8 blkbits = inode->i_blkbits;
244+
ext4_lblk_t written_blk, end_blk;
245+
246+
/*
247+
* Note that EXT4_I(inode)->i_disksize can get extended up to
248+
* inode->i_size while the I/O was running due to writeback of delalloc
249+
* blocks. But, the code in ext4_iomap_alloc() is careful to use
250+
* zeroed/unwritten extents if this is possible; thus we won't leave
251+
* uninitialized blocks in a file even if we didn't succeed in writing
252+
* as much as we intended.
253+
*/
254+
WARN_ON_ONCE(i_size_read(inode) < EXT4_I(inode)->i_disksize);
255+
if (offset + count <= EXT4_I(inode)->i_disksize) {
256+
/*
257+
* We need to ensure that the inode is removed from the orphan
258+
* list if it has been added prematurely, due to writeback of
259+
* delalloc blocks.
260+
*/
261+
if (!list_empty(&EXT4_I(inode)->i_orphan) && inode->i_nlink) {
262+
handle = ext4_journal_start(inode, EXT4_HT_INODE, 2);
263+
264+
if (IS_ERR(handle)) {
265+
ext4_orphan_del(NULL, inode);
266+
return PTR_ERR(handle);
267+
}
268+
269+
ext4_orphan_del(handle, inode);
270+
ext4_journal_stop(handle);
271+
}
272+
273+
return written;
274+
}
275+
276+
if (written < 0)
277+
goto truncate;
278+
279+
handle = ext4_journal_start(inode, EXT4_HT_INODE, 2);
280+
if (IS_ERR(handle)) {
281+
written = PTR_ERR(handle);
282+
goto truncate;
283+
}
284+
285+
if (ext4_update_inode_size(inode, offset + written))
286+
ext4_mark_inode_dirty(handle, inode);
287+
288+
/*
289+
* We may need to truncate allocated but not written blocks beyond EOF.
290+
*/
291+
written_blk = ALIGN(offset + written, 1 << blkbits);
292+
end_blk = ALIGN(offset + count, 1 << blkbits);
293+
if (written_blk < end_blk && ext4_can_truncate(inode))
294+
truncate = true;
295+
296+
/*
297+
* Remove the inode from the orphan list if it has been extended and
298+
* everything went OK.
299+
*/
300+
if (!truncate && inode->i_nlink)
301+
ext4_orphan_del(handle, inode);
302+
ext4_journal_stop(handle);
303+
304+
if (truncate) {
305+
truncate:
306+
ext4_truncate_failed_write(inode);
307+
/*
308+
* If the truncate operation failed early, then the inode may
309+
* still be on the orphan list. In that case, we need to try
310+
* remove the inode from the in-memory linked list.
311+
*/
312+
if (inode->i_nlink)
313+
ext4_orphan_del(NULL, inode);
314+
}
315+
316+
return written;
317+
}
318+
237319
#ifdef CONFIG_FS_DAX
238320
static ssize_t
239321
ext4_dax_write_iter(struct kiocb *iocb, struct iov_iter *from)
240322
{
241-
struct inode *inode = file_inode(iocb->ki_filp);
242323
ssize_t ret;
324+
size_t count;
325+
loff_t offset;
326+
struct inode *inode = file_inode(iocb->ki_filp);
243327

244328
if (!inode_trylock(inode)) {
245329
if (iocb->ki_flags & IOCB_NOWAIT)
@@ -256,7 +340,10 @@ ext4_dax_write_iter(struct kiocb *iocb, struct iov_iter *from)
256340
if (ret)
257341
goto out;
258342

343+
offset = iocb->ki_pos;
344+
count = iov_iter_count(from);
259345
ret = dax_iomap_rw(iocb, from, &ext4_iomap_ops);
346+
ret = ext4_handle_inode_extension(inode, offset, ret, count);
260347
out:
261348
inode_unlock(inode);
262349
if (ret > 0)

fs/ext4/inode.c

Lines changed: 1 addition & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3583,53 +3583,7 @@ static int ext4_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
35833583
static int ext4_iomap_end(struct inode *inode, loff_t offset, loff_t length,
35843584
ssize_t written, unsigned flags, struct iomap *iomap)
35853585
{
3586-
int ret = 0;
3587-
handle_t *handle;
3588-
int blkbits = inode->i_blkbits;
3589-
bool truncate = false;
3590-
3591-
if (!(flags & IOMAP_WRITE) || (flags & IOMAP_FAULT))
3592-
return 0;
3593-
3594-
handle = ext4_journal_start(inode, EXT4_HT_INODE, 2);
3595-
if (IS_ERR(handle)) {
3596-
ret = PTR_ERR(handle);
3597-
goto orphan_del;
3598-
}
3599-
if (ext4_update_inode_size(inode, offset + written))
3600-
ext4_mark_inode_dirty(handle, inode);
3601-
/*
3602-
* We may need to truncate allocated but not written blocks beyond EOF.
3603-
*/
3604-
if (iomap->offset + iomap->length >
3605-
ALIGN(inode->i_size, 1 << blkbits)) {
3606-
ext4_lblk_t written_blk, end_blk;
3607-
3608-
written_blk = (offset + written) >> blkbits;
3609-
end_blk = (offset + length) >> blkbits;
3610-
if (written_blk < end_blk && ext4_can_truncate(inode))
3611-
truncate = true;
3612-
}
3613-
/*
3614-
* Remove inode from orphan list if we were extending a inode and
3615-
* everything went fine.
3616-
*/
3617-
if (!truncate && inode->i_nlink &&
3618-
!list_empty(&EXT4_I(inode)->i_orphan))
3619-
ext4_orphan_del(handle, inode);
3620-
ext4_journal_stop(handle);
3621-
if (truncate) {
3622-
ext4_truncate_failed_write(inode);
3623-
orphan_del:
3624-
/*
3625-
* If truncate failed early the inode might still be on the
3626-
* orphan list; we need to make sure the inode is removed from
3627-
* the orphan list in that case.
3628-
*/
3629-
if (inode->i_nlink)
3630-
ext4_orphan_del(NULL, inode);
3631-
}
3632-
return ret;
3586+
return 0;
36333587
}
36343588

36353589
const struct iomap_ops ext4_iomap_ops = {

0 commit comments

Comments
 (0)