mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-28 07:33:45 -04:00
Merge tag 'vfs-7.3-rc1.sync' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
Pull vfs writeback updates from Christian Brauner:
"This makes sync_inode_metadata() and writeback_single_inode() persist
not only the inode but all metadata associated with it.
A new .sync_inode_metadata superblock operation is called from
__writeback_single_inode(). Alongside it a new I_METADATA_WRITEBACK
state flag is added.
Filesystems no longer need their own mmb_fsync() implementations and
can just use simple_fsync(). All metadata is now written for IS_SYNC
and IS_DIRSYNC inodes. Races where several fsyncs raced and mmb_sync()
could return before all buffers were really persisted are fixed since
I_SYNC now serializes properly.
The I_METADATA_WRITEBACK scheme also fixes the case where a
WB_SYNC_NONE writeback landing between write(2) and fsync(2) left
fsync(2) failing to persist the inode. That problem is not specific to
filesystems using the generic metadata bh tracking, and the ones that
do not are left alone.
ext2, udf, bfs, minix, fat and ext4 in nojournal mode have their data
integrity writeout fixed and are converted. affs drops metadata bh
tracking and mmb_fsync() is removed.
A few other fixes came out of this:
- a UAF in mark_buffer_write_io_error()
- missed inode writeback when racing with __writeback_single_inode()
- ext4 allocating the mapping_metadata_bhs struct on demand
- three fat fixes: a lost inode update in do_msdos_rename() with
DIRSYNC, inode buffer write errors not propagating out of
fat_sync_inode_metadata() and directory entries not being
persisted on fsync(2) of the root directory"
* tag 'vfs-7.3-rc1.sync' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (24 commits)
writeback: Export __inode_attach_wb()
fat: Fix persisting directory entries on fsync(2) of the root directory
fat: Propagate inode buffer write errors from fat_sync_inode_metadata()
fat: Fix lost inode update in do_msdos_rename() with DIRSYNC
vfs: Remove mmb_fsync()
fat: Replace fat_sync_inode() with sync_inode_metadata()
fat: Fix missed inode writeback during fsync(2)
ext4: Fix data integrity writeout issues in nojournal mode
minix: Fix data integrity writeout issues
bfs: Fix data integrity writeout issues
udf: Fold udf_update_inode() into udf_write_inode()
udf: Use sync_inode_metadata() in udf_evict_inode()
udf: Drop udf_sync_inode()
udf: Use sync_inode_metadata() to writeout IS_SYNC inode
udf: Fix data integrity writeout issues
ext2: Fix data integrity writeout issues
ext2: Avoid unnecessary inode buffer writeback for sync(2)
ext2: Drop __ext2_write_inode()
ext2: Fix lost inode updates for IS_SYNC inodes
fs: Provide way for filesystem to wait for metadata writeback
...
This commit is contained in:
@@ -44,7 +44,6 @@ struct affs_inode_info {
|
||||
struct mutex i_link_lock; /* Protects internal inode access. */
|
||||
struct mutex i_ext_lock; /* Protects internal inode access. */
|
||||
#define i_hash_lock i_ext_lock
|
||||
struct mapping_metadata_bhs i_metadata_bhs;
|
||||
u32 i_blkcnt; /* block count */
|
||||
u32 i_extcnt; /* extended block count */
|
||||
u32 *i_lc; /* linear cache of extended blocks */
|
||||
@@ -152,7 +151,6 @@ extern bool affs_nofilenametruncate(const struct dentry *dentry);
|
||||
extern int affs_check_name(const unsigned char *name, int len,
|
||||
bool notruncate);
|
||||
extern int affs_copy_name(unsigned char *bstr, struct dentry *dentry);
|
||||
struct mapping_metadata_bhs *affs_get_metadata_bhs(struct inode *inode);
|
||||
|
||||
/* bitmap. c */
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ affs_insert_hash(struct inode *dir, struct buffer_head *bh)
|
||||
AFFS_TAIL(sb, dir_bh)->hash_chain = cpu_to_be32(ino);
|
||||
|
||||
affs_adjust_checksum(dir_bh, ino);
|
||||
mmb_mark_buffer_dirty(dir_bh, &AFFS_I(dir)->i_metadata_bhs);
|
||||
mark_buffer_dirty(dir_bh);
|
||||
affs_brelse(dir_bh);
|
||||
|
||||
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
|
||||
@@ -100,7 +100,7 @@ affs_remove_hash(struct inode *dir, struct buffer_head *rem_bh)
|
||||
else
|
||||
AFFS_TAIL(sb, bh)->hash_chain = ino;
|
||||
affs_adjust_checksum(bh, be32_to_cpu(ino) - hash_ino);
|
||||
mmb_mark_buffer_dirty(bh, &AFFS_I(dir)->i_metadata_bhs);
|
||||
mark_buffer_dirty(bh);
|
||||
AFFS_TAIL(sb, rem_bh)->parent = 0;
|
||||
retval = 0;
|
||||
break;
|
||||
@@ -180,7 +180,7 @@ affs_remove_link(struct dentry *dentry)
|
||||
affs_unlock_dir(dir);
|
||||
goto done;
|
||||
}
|
||||
mmb_mark_buffer_dirty(link_bh, &AFFS_I(inode)->i_metadata_bhs);
|
||||
mark_buffer_dirty(link_bh);
|
||||
|
||||
memcpy(AFFS_TAIL(sb, bh)->name, AFFS_TAIL(sb, link_bh)->name, 32);
|
||||
retval = affs_insert_hash(dir, bh);
|
||||
@@ -188,7 +188,7 @@ affs_remove_link(struct dentry *dentry)
|
||||
affs_unlock_dir(dir);
|
||||
goto done;
|
||||
}
|
||||
mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
|
||||
mark_buffer_dirty(bh);
|
||||
|
||||
affs_unlock_dir(dir);
|
||||
iput(dir);
|
||||
@@ -203,7 +203,7 @@ affs_remove_link(struct dentry *dentry)
|
||||
__be32 ino2 = AFFS_TAIL(sb, link_bh)->link_chain;
|
||||
AFFS_TAIL(sb, bh)->link_chain = ino2;
|
||||
affs_adjust_checksum(bh, be32_to_cpu(ino2) - link_ino);
|
||||
mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
|
||||
mark_buffer_dirty(bh);
|
||||
retval = 0;
|
||||
/* Fix the link count, if bh is a normal header block without links */
|
||||
switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
|
||||
@@ -306,7 +306,7 @@ affs_remove_header(struct dentry *dentry)
|
||||
retval = affs_remove_hash(dir, bh);
|
||||
if (retval)
|
||||
goto done_unlock;
|
||||
mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
|
||||
mark_buffer_dirty(bh);
|
||||
|
||||
affs_unlock_dir(dir);
|
||||
|
||||
|
||||
@@ -140,14 +140,14 @@ affs_alloc_extblock(struct inode *inode, struct buffer_head *bh, u32 ext)
|
||||
AFFS_TAIL(sb, new_bh)->parent = cpu_to_be32(inode->i_ino);
|
||||
affs_fix_checksum(sb, new_bh);
|
||||
|
||||
mmb_mark_buffer_dirty(new_bh, &AFFS_I(inode)->i_metadata_bhs);
|
||||
mark_buffer_dirty(new_bh);
|
||||
|
||||
tmp = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
|
||||
if (tmp)
|
||||
affs_warning(sb, "alloc_ext", "previous extension set (%x)", tmp);
|
||||
AFFS_TAIL(sb, bh)->extension = cpu_to_be32(blocknr);
|
||||
affs_adjust_checksum(bh, blocknr - tmp);
|
||||
mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
|
||||
mark_buffer_dirty(bh);
|
||||
|
||||
AFFS_I(inode)->i_extcnt++;
|
||||
mark_inode_dirty(inode);
|
||||
@@ -581,7 +581,7 @@ affs_extent_file_ofs(struct inode *inode, u32 newsize)
|
||||
memset(AFFS_DATA(bh) + boff, 0, tmp);
|
||||
be32_add_cpu(&AFFS_DATA_HEAD(bh)->size, tmp);
|
||||
affs_fix_checksum(sb, bh);
|
||||
mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
|
||||
mark_buffer_dirty(bh);
|
||||
size += tmp;
|
||||
bidx++;
|
||||
} else if (bidx) {
|
||||
@@ -603,7 +603,7 @@ affs_extent_file_ofs(struct inode *inode, u32 newsize)
|
||||
AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp);
|
||||
affs_fix_checksum(sb, bh);
|
||||
bh->b_state &= ~(1UL << BH_New);
|
||||
mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
|
||||
mark_buffer_dirty(bh);
|
||||
if (prev_bh) {
|
||||
u32 tmp_next = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next);
|
||||
|
||||
@@ -613,8 +613,7 @@ affs_extent_file_ofs(struct inode *inode, u32 newsize)
|
||||
bidx, tmp_next);
|
||||
AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr);
|
||||
affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp_next);
|
||||
mmb_mark_buffer_dirty(prev_bh,
|
||||
&AFFS_I(inode)->i_metadata_bhs);
|
||||
mark_buffer_dirty(prev_bh);
|
||||
affs_brelse(prev_bh);
|
||||
}
|
||||
size += bsize;
|
||||
@@ -733,7 +732,7 @@ static int affs_write_end_ofs(const struct kiocb *iocb,
|
||||
AFFS_DATA_HEAD(bh)->size = cpu_to_be32(
|
||||
max(boff + tmp, be32_to_cpu(AFFS_DATA_HEAD(bh)->size)));
|
||||
affs_fix_checksum(sb, bh);
|
||||
mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
|
||||
mark_buffer_dirty(bh);
|
||||
written += tmp;
|
||||
from += tmp;
|
||||
bidx++;
|
||||
@@ -766,13 +765,12 @@ static int affs_write_end_ofs(const struct kiocb *iocb,
|
||||
bidx, tmp_next);
|
||||
AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr);
|
||||
affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp_next);
|
||||
mmb_mark_buffer_dirty(prev_bh,
|
||||
&AFFS_I(inode)->i_metadata_bhs);
|
||||
mark_buffer_dirty(prev_bh);
|
||||
}
|
||||
}
|
||||
affs_brelse(prev_bh);
|
||||
affs_fix_checksum(sb, bh);
|
||||
mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
|
||||
mark_buffer_dirty(bh);
|
||||
written += bsize;
|
||||
from += bsize;
|
||||
bidx++;
|
||||
@@ -801,14 +799,13 @@ static int affs_write_end_ofs(const struct kiocb *iocb,
|
||||
bidx, tmp_next);
|
||||
AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr);
|
||||
affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp_next);
|
||||
mmb_mark_buffer_dirty(prev_bh,
|
||||
&AFFS_I(inode)->i_metadata_bhs);
|
||||
mark_buffer_dirty(prev_bh);
|
||||
}
|
||||
} else if (be32_to_cpu(AFFS_DATA_HEAD(bh)->size) < tmp)
|
||||
AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp);
|
||||
affs_brelse(prev_bh);
|
||||
affs_fix_checksum(sb, bh);
|
||||
mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
|
||||
mark_buffer_dirty(bh);
|
||||
written += tmp;
|
||||
from += tmp;
|
||||
bidx++;
|
||||
@@ -945,7 +942,7 @@ affs_truncate(struct inode *inode)
|
||||
}
|
||||
AFFS_TAIL(sb, ext_bh)->extension = 0;
|
||||
affs_fix_checksum(sb, ext_bh);
|
||||
mmb_mark_buffer_dirty(ext_bh, &AFFS_I(inode)->i_metadata_bhs);
|
||||
mark_buffer_dirty(ext_bh);
|
||||
affs_brelse(ext_bh);
|
||||
|
||||
if (inode->i_size) {
|
||||
|
||||
@@ -206,7 +206,7 @@ affs_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
}
|
||||
}
|
||||
affs_fix_checksum(sb, bh);
|
||||
mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
|
||||
mark_buffer_dirty(bh);
|
||||
affs_brelse(bh);
|
||||
affs_free_prealloc(inode);
|
||||
return 0;
|
||||
@@ -266,11 +266,8 @@ affs_evict_inode(struct inode *inode)
|
||||
if (!inode->i_nlink) {
|
||||
inode->i_size = 0;
|
||||
affs_truncate(inode);
|
||||
} else {
|
||||
mmb_sync(&AFFS_I(inode)->i_metadata_bhs);
|
||||
}
|
||||
|
||||
mmb_invalidate(&AFFS_I(inode)->i_metadata_bhs);
|
||||
clear_inode(inode);
|
||||
affs_free_prealloc(inode);
|
||||
cache_page = (unsigned long)AFFS_I(inode)->i_lc;
|
||||
@@ -305,7 +302,7 @@ affs_new_inode(struct inode *dir)
|
||||
bh = affs_getzeroblk(sb, block);
|
||||
if (!bh)
|
||||
goto err_bh;
|
||||
mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
|
||||
mark_buffer_dirty(bh);
|
||||
affs_brelse(bh);
|
||||
|
||||
inode->i_uid = current_fsuid();
|
||||
@@ -393,17 +390,17 @@ affs_add_entry(struct inode *dir, struct inode *inode, struct dentry *dentry, s3
|
||||
AFFS_TAIL(sb, bh)->link_chain = chain;
|
||||
AFFS_TAIL(sb, inode_bh)->link_chain = cpu_to_be32(block);
|
||||
affs_adjust_checksum(inode_bh, block - be32_to_cpu(chain));
|
||||
mmb_mark_buffer_dirty(inode_bh, &AFFS_I(inode)->i_metadata_bhs);
|
||||
mark_buffer_dirty(inode_bh);
|
||||
set_nlink(inode, 2);
|
||||
ihold(inode);
|
||||
}
|
||||
affs_fix_checksum(sb, bh);
|
||||
mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
|
||||
mark_buffer_dirty(bh);
|
||||
dentry->d_fsdata = (void *)(long)bh->b_blocknr;
|
||||
|
||||
affs_lock_dir(dir);
|
||||
retval = affs_insert_hash(dir, bh);
|
||||
mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
|
||||
mark_buffer_dirty(bh);
|
||||
affs_unlock_dir(dir);
|
||||
affs_unlock_link(inode);
|
||||
|
||||
|
||||
@@ -373,7 +373,7 @@ affs_symlink(struct mnt_idmap *idmap, struct inode *dir,
|
||||
}
|
||||
*p = 0;
|
||||
inode->i_size = i + 1;
|
||||
mmb_mark_buffer_dirty(bh, &AFFS_I(inode)->i_metadata_bhs);
|
||||
mark_buffer_dirty(bh);
|
||||
affs_brelse(bh);
|
||||
mark_inode_dirty(inode);
|
||||
|
||||
@@ -443,8 +443,7 @@ affs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
/* TODO: move it back to old_dir, if error? */
|
||||
|
||||
done:
|
||||
mmb_mark_buffer_dirty(bh,
|
||||
&AFFS_I(retval ? old_dir : new_dir)->i_metadata_bhs);
|
||||
mark_buffer_dirty(bh);
|
||||
affs_brelse(bh);
|
||||
return retval;
|
||||
}
|
||||
@@ -497,8 +496,8 @@ affs_xrename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
retval = affs_insert_hash(old_dir, bh_new);
|
||||
affs_unlock_dir(old_dir);
|
||||
done:
|
||||
mmb_mark_buffer_dirty(bh_old, &AFFS_I(new_dir)->i_metadata_bhs);
|
||||
mmb_mark_buffer_dirty(bh_new, &AFFS_I(old_dir)->i_metadata_bhs);
|
||||
mark_buffer_dirty(bh_old);
|
||||
mark_buffer_dirty(bh_new);
|
||||
affs_brelse(bh_old);
|
||||
affs_brelse(bh_new);
|
||||
return retval;
|
||||
|
||||
@@ -108,7 +108,6 @@ static struct inode *affs_alloc_inode(struct super_block *sb)
|
||||
i->i_lc = NULL;
|
||||
i->i_ext_bh = NULL;
|
||||
i->i_pa_cnt = 0;
|
||||
mmb_init(&i->i_metadata_bhs, &i->vfs_inode.i_data);
|
||||
|
||||
return &i->vfs_inode;
|
||||
}
|
||||
|
||||
@@ -68,17 +68,10 @@ static int bfs_readdir(struct file *f, struct dir_context *ctx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bfs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
||||
{
|
||||
return mmb_fsync(file,
|
||||
&BFS_I(file->f_mapping->host)->i_metadata_bhs,
|
||||
start, end, datasync);
|
||||
}
|
||||
|
||||
const struct file_operations bfs_dir_operations = {
|
||||
.read = generic_read_dir,
|
||||
.iterate_shared = bfs_readdir,
|
||||
.fsync = bfs_fsync,
|
||||
.fsync = simple_fsync,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
|
||||
@@ -136,7 +136,6 @@ static int bfs_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
unsigned long i_sblock;
|
||||
struct bfs_inode *di;
|
||||
struct buffer_head *bh;
|
||||
int err = 0;
|
||||
|
||||
dprintf("ino=%08x\n", ino);
|
||||
|
||||
@@ -165,13 +164,31 @@ static int bfs_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
di->i_eoffset = cpu_to_le32(i_sblock * BFS_BSIZE + inode->i_size - 1);
|
||||
|
||||
mark_buffer_dirty(bh);
|
||||
if (wbc->sync_mode == WB_SYNC_ALL) {
|
||||
sync_dirty_buffer(bh);
|
||||
if (buffer_req(bh) && !buffer_uptodate(bh))
|
||||
err = -EIO;
|
||||
}
|
||||
brelse(bh);
|
||||
mutex_unlock(&info->bfs_lock);
|
||||
set_inode_metadata_writeback(inode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bfs_sync_inode_metadata(struct inode *inode,
|
||||
struct writeback_control *wbc)
|
||||
{
|
||||
int err = 0;
|
||||
struct bfs_inode *di;
|
||||
struct buffer_head *bh;
|
||||
|
||||
di = find_inode(inode->i_sb, (u16)inode->i_ino, &bh);
|
||||
if (IS_ERR(di))
|
||||
return PTR_ERR(di);
|
||||
|
||||
sync_dirty_buffer(bh);
|
||||
if (buffer_write_io_error(bh)) {
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
err = mmb_sync(&BFS_I(inode)->i_metadata_bhs);
|
||||
out:
|
||||
brelse(bh);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -302,6 +319,7 @@ static const struct super_operations bfs_sops = {
|
||||
.alloc_inode = bfs_alloc_inode,
|
||||
.free_inode = bfs_free_inode,
|
||||
.write_inode = bfs_write_inode,
|
||||
.sync_inode_metadata = bfs_sync_inode_metadata,
|
||||
.evict_inode = bfs_evict_inode,
|
||||
.put_super = bfs_put_super,
|
||||
.statfs = bfs_statfs,
|
||||
|
||||
83
fs/buffer.c
83
fs/buffer.c
@@ -628,79 +628,6 @@ int mmb_sync(struct mapping_metadata_bhs *mmb)
|
||||
}
|
||||
EXPORT_SYMBOL(mmb_sync);
|
||||
|
||||
/**
|
||||
* mmb_fsync_noflush - fsync implementation for simple filesystems with
|
||||
* metadata buffers list
|
||||
*
|
||||
* @file: file to synchronize
|
||||
* @mmb: list of metadata bhs to flush
|
||||
* @start: start offset in bytes
|
||||
* @end: end offset in bytes (inclusive)
|
||||
* @datasync: only synchronize essential metadata if true
|
||||
*
|
||||
* This is an implementation of the fsync method for simple filesystems which
|
||||
* track all non-inode metadata in the buffers list hanging off the @mmb
|
||||
* structure.
|
||||
*/
|
||||
int mmb_fsync_noflush(struct file *file, struct mapping_metadata_bhs *mmb,
|
||||
loff_t start, loff_t end, bool datasync)
|
||||
{
|
||||
struct inode *inode = file->f_mapping->host;
|
||||
int err;
|
||||
int ret = 0;
|
||||
|
||||
err = file_write_and_wait_range(file, start, end);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (mmb)
|
||||
ret = mmb_sync(mmb);
|
||||
if (!(inode_state_read_once(inode) & I_DIRTY_ALL))
|
||||
goto out;
|
||||
if (datasync && !(inode_state_read_once(inode) & I_DIRTY_DATASYNC))
|
||||
goto out;
|
||||
|
||||
err = sync_inode_metadata(inode, 1);
|
||||
if (ret == 0)
|
||||
ret = err;
|
||||
|
||||
out:
|
||||
/* check and advance again to catch errors after syncing out buffers */
|
||||
err = file_check_and_advance_wb_err(file);
|
||||
if (ret == 0)
|
||||
ret = err;
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mmb_fsync_noflush);
|
||||
|
||||
/**
|
||||
* mmb_fsync - fsync implementation for simple filesystems with metadata
|
||||
* buffers list
|
||||
*
|
||||
* @file: file to synchronize
|
||||
* @mmb: list of metadata bhs to flush
|
||||
* @start: start offset in bytes
|
||||
* @end: end offset in bytes (inclusive)
|
||||
* @datasync: only synchronize essential metadata if true
|
||||
*
|
||||
* This is an implementation of the fsync method for simple filesystems which
|
||||
* track all non-inode metadata in the buffers list hanging off the @mmb
|
||||
* structure. This also makes sure that a device cache flush operation is
|
||||
* called at the end.
|
||||
*/
|
||||
int mmb_fsync(struct file *file, struct mapping_metadata_bhs *mmb,
|
||||
loff_t start, loff_t end, bool datasync)
|
||||
{
|
||||
struct inode *inode = file->f_mapping->host;
|
||||
int ret;
|
||||
|
||||
ret = mmb_fsync_noflush(file, mmb, start, end, datasync);
|
||||
if (!ret)
|
||||
ret = blkdev_issue_flush(inode->i_sb->s_bdev);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mmb_fsync);
|
||||
|
||||
/*
|
||||
* Called when we've recently written block `bblock', and it is known that
|
||||
* `bblock' was for a buffer_boundary() buffer. This means that the block at
|
||||
@@ -1123,12 +1050,18 @@ EXPORT_SYMBOL(mark_buffer_dirty);
|
||||
|
||||
void mark_buffer_write_io_error(struct buffer_head *bh)
|
||||
{
|
||||
struct mapping_metadata_bhs *mmb;
|
||||
|
||||
set_buffer_write_io_error(bh);
|
||||
/* FIXME: do we need to set this in both places? */
|
||||
if (bh->b_folio && bh->b_folio->mapping)
|
||||
mapping_set_error(bh->b_folio->mapping, -EIO);
|
||||
if (bh->b_mmb)
|
||||
mapping_set_error(bh->b_mmb->mapping, -EIO);
|
||||
/* Protect us from mmb & inode getting freed while we work on it */
|
||||
rcu_read_lock();
|
||||
mmb = READ_ONCE(bh->b_mmb);
|
||||
if (mmb)
|
||||
mapping_set_error(mmb->mapping, -EIO);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
EXPORT_SYMBOL(mark_buffer_write_io_error);
|
||||
|
||||
|
||||
@@ -734,6 +734,6 @@ const struct file_operations ext2_dir_operations = {
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = ext2_compat_ioctl,
|
||||
#endif
|
||||
.fsync = ext2_fsync,
|
||||
.fsync = simple_fsync,
|
||||
.setlease = generic_setlease,
|
||||
};
|
||||
|
||||
@@ -735,6 +735,7 @@ extern unsigned long ext2_count_free (struct buffer_head *, unsigned);
|
||||
/* inode.c */
|
||||
extern struct inode *ext2_iget (struct super_block *, unsigned long);
|
||||
extern int ext2_write_inode (struct inode *, struct writeback_control *);
|
||||
extern int ext2_sync_inode_metadata(struct inode *, struct writeback_control *);
|
||||
extern void ext2_evict_inode(struct inode *);
|
||||
void ext2_write_failed(struct address_space *mapping, loff_t to);
|
||||
extern int ext2_get_block(struct inode *, sector_t, struct buffer_head *, int);
|
||||
@@ -772,8 +773,6 @@ extern void ext2_sync_super(struct super_block *sb, struct ext2_super_block *es,
|
||||
extern const struct file_operations ext2_dir_operations;
|
||||
|
||||
/* file.c */
|
||||
extern int ext2_fsync(struct file *file, loff_t start, loff_t end,
|
||||
int datasync);
|
||||
extern const struct inode_operations ext2_file_inode_operations;
|
||||
extern const struct file_operations ext2_file_operations;
|
||||
|
||||
|
||||
@@ -47,21 +47,6 @@ static int ext2_release_file (struct inode * inode, struct file * filp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ext2_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
||||
{
|
||||
int ret;
|
||||
struct inode *inode = file->f_mapping->host;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
|
||||
ret = mmb_fsync(file, &EXT2_I(inode)->i_metadata_bhs,
|
||||
start, end, datasync);
|
||||
if (ret == -EIO)
|
||||
/* We don't really know where the IO error happened... */
|
||||
ext2_error(sb, __func__,
|
||||
"detected IO error when writing metadata buffers");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ext2_dio_read_iter(struct kiocb *iocb, struct iov_iter *to)
|
||||
{
|
||||
struct file *file = iocb->ki_filp;
|
||||
@@ -213,7 +198,7 @@ const struct file_operations ext2_file_operations = {
|
||||
.mmap_prepare = generic_file_mmap_prepare,
|
||||
.open = ext2_file_open,
|
||||
.release = ext2_release_file,
|
||||
.fsync = ext2_fsync,
|
||||
.fsync = simple_fsync,
|
||||
.get_unmapped_area = thp_get_unmapped_area,
|
||||
.splice_read = filemap_splice_read,
|
||||
.splice_write = iter_file_splice_write,
|
||||
|
||||
@@ -39,8 +39,6 @@
|
||||
#include "acl.h"
|
||||
#include "xattr.h"
|
||||
|
||||
static int __ext2_write_inode(struct inode *inode, int do_sync);
|
||||
|
||||
/*
|
||||
* Test whether an inode is a fast symlink.
|
||||
*/
|
||||
@@ -87,7 +85,7 @@ void ext2_evict_inode(struct inode * inode)
|
||||
/* set dtime */
|
||||
EXT2_I(inode)->i_dtime = ktime_get_real_seconds();
|
||||
mark_inode_dirty(inode);
|
||||
__ext2_write_inode(inode, inode_needs_sync(inode));
|
||||
sync_inode_metadata(inode, inode_needs_sync(inode));
|
||||
/* truncate to 0 */
|
||||
inode->i_size = 0;
|
||||
if (inode->i_blocks)
|
||||
@@ -1260,12 +1258,9 @@ static int ext2_setsize(struct inode *inode, loff_t newsize)
|
||||
filemap_invalidate_unlock(inode->i_mapping);
|
||||
|
||||
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
|
||||
if (inode_needs_sync(inode)) {
|
||||
mmb_sync(&EXT2_I(inode)->i_metadata_bhs);
|
||||
mark_inode_dirty(inode);
|
||||
if (inode_needs_sync(inode))
|
||||
sync_inode_metadata(inode, 1);
|
||||
} else {
|
||||
mark_inode_dirty(inode);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1471,7 +1466,7 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static int __ext2_write_inode(struct inode *inode, int do_sync)
|
||||
int ext2_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
{
|
||||
struct ext2_inode_info *ei = EXT2_I(inode);
|
||||
struct super_block *sb = inode->i_sb;
|
||||
@@ -1562,22 +1557,38 @@ static int __ext2_write_inode(struct inode *inode, int do_sync)
|
||||
} else for (n = 0; n < EXT2_N_BLOCKS; n++)
|
||||
raw_inode->i_block[n] = ei->i_data[n];
|
||||
mark_buffer_dirty(bh);
|
||||
if (do_sync) {
|
||||
sync_dirty_buffer(bh);
|
||||
if (buffer_req(bh) && !buffer_uptodate(bh)) {
|
||||
printk ("IO error syncing ext2 inode [%s:%08lx]\n",
|
||||
sb->s_id, (unsigned long) ino);
|
||||
err = -EIO;
|
||||
}
|
||||
}
|
||||
ei->i_state &= ~EXT2_STATE_NEW;
|
||||
brelse (bh);
|
||||
set_inode_metadata_writeback(inode);
|
||||
return err;
|
||||
}
|
||||
|
||||
int ext2_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
int ext2_sync_inode_metadata(struct inode *inode, struct writeback_control *wbc)
|
||||
{
|
||||
return __ext2_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
|
||||
struct buffer_head *bh;
|
||||
struct ext2_inode *raw_inode = ext2_get_inode(inode->i_sb, inode->i_ino,
|
||||
&bh);
|
||||
int err = 0;
|
||||
|
||||
if (IS_ERR(raw_inode))
|
||||
return -EIO;
|
||||
err = mmb_sync(&EXT2_I(inode)->i_metadata_bhs);
|
||||
if (err) {
|
||||
ext2_error(inode->i_sb, __func__,
|
||||
"Error syncing inode metadata ino=%lu\n",
|
||||
(unsigned long)inode->i_ino);
|
||||
goto out;
|
||||
}
|
||||
sync_dirty_buffer(bh);
|
||||
if (buffer_write_io_error(bh)) {
|
||||
ext2_error(inode->i_sb, __func__,
|
||||
"IO error syncing inode %lu\n",
|
||||
(unsigned long)inode->i_ino);
|
||||
err = -EIO;
|
||||
}
|
||||
out:
|
||||
brelse(bh);
|
||||
return err;
|
||||
}
|
||||
|
||||
int ext2_getattr(struct mnt_idmap *idmap, const struct path *path,
|
||||
|
||||
@@ -362,6 +362,7 @@ static const struct super_operations ext2_sops = {
|
||||
.alloc_inode = ext2_alloc_inode,
|
||||
.free_inode = ext2_free_in_core_inode,
|
||||
.write_inode = ext2_write_inode,
|
||||
.sync_inode_metadata = ext2_sync_inode_metadata,
|
||||
.evict_inode = ext2_evict_inode,
|
||||
.put_super = ext2_put_super,
|
||||
.sync_fs = ext2_sync_fs,
|
||||
|
||||
@@ -777,6 +777,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh,
|
||||
/* Update the inode. */
|
||||
EXT2_I(inode)->i_file_acl = new_bh ? new_bh->b_blocknr : 0;
|
||||
inode_set_ctime_current(inode);
|
||||
mark_inode_dirty(inode);
|
||||
if (IS_SYNC(inode)) {
|
||||
error = sync_inode_metadata(inode, 1);
|
||||
/* In case sync failed due to ENOSPC the inode was actually
|
||||
@@ -789,8 +790,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh,
|
||||
}
|
||||
goto cleanup;
|
||||
}
|
||||
} else
|
||||
mark_inode_dirty(inode);
|
||||
}
|
||||
|
||||
error = 0;
|
||||
if (old_bh && old_bh != new_bh) {
|
||||
|
||||
@@ -1151,7 +1151,7 @@ struct ext4_inode_info {
|
||||
struct rw_semaphore i_data_sem;
|
||||
struct inode vfs_inode;
|
||||
struct jbd2_inode *jinode;
|
||||
struct mapping_metadata_bhs i_metadata_bhs;
|
||||
struct mapping_metadata_bhs *i_metadata_bhs;
|
||||
|
||||
/*
|
||||
* File creation time. Its function is same as that of
|
||||
@@ -2126,6 +2126,17 @@ static inline bool ext4_inode_orphan_tracked(struct inode *inode)
|
||||
!list_empty(&EXT4_I(inode)->i_orphan);
|
||||
}
|
||||
|
||||
static inline struct mapping_metadata_bhs *ext4_i_metadata_bhs(
|
||||
struct inode *inode)
|
||||
{
|
||||
/*
|
||||
* i_metadata_bhs is set in ext4_inode_attach_mmb() using cmpxchg().
|
||||
* We use READ_ONCE when accessing i_metadata_bhs to make sure we get
|
||||
* consistent view for all accesses.
|
||||
*/
|
||||
return READ_ONCE(EXT4_I(inode)->i_metadata_bhs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Codes for operating systems
|
||||
*/
|
||||
@@ -3155,6 +3166,7 @@ extern struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
|
||||
__ext4_iget((sb), (ino), (flags), __func__, __LINE__)
|
||||
|
||||
extern int ext4_write_inode(struct inode *, struct writeback_control *);
|
||||
extern int ext4_sync_inode_metadata(struct inode *, struct writeback_control *);
|
||||
extern int ext4_setattr(struct mnt_idmap *, struct dentry *,
|
||||
struct iattr *);
|
||||
extern u32 ext4_dio_alignment(struct inode *inode);
|
||||
|
||||
@@ -350,6 +350,21 @@ int __ext4_journal_get_create_access(const char *where, unsigned int line,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ext4_inode_attach_mmb(struct inode *inode)
|
||||
{
|
||||
struct mapping_metadata_bhs *mmb;
|
||||
|
||||
/*
|
||||
* It's difficult to handle failure when marking buffer dirty without
|
||||
* leaving filesystem corrupted
|
||||
*/
|
||||
mmb = kmalloc_obj(*mmb, GFP_NOFS | __GFP_NOFAIL | __GFP_ACCOUNT);
|
||||
mmb_init(mmb, &inode->i_data);
|
||||
/* Someone swapped another mmb before us? */
|
||||
if (cmpxchg(&EXT4_I(inode)->i_metadata_bhs, NULL, mmb))
|
||||
kfree(mmb);
|
||||
}
|
||||
|
||||
int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
|
||||
handle_t *handle, struct inode *inode,
|
||||
struct buffer_head *bh)
|
||||
@@ -389,11 +404,13 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
|
||||
err);
|
||||
}
|
||||
} else {
|
||||
if (inode)
|
||||
mmb_mark_buffer_dirty(bh,
|
||||
&EXT4_I(inode)->i_metadata_bhs);
|
||||
else
|
||||
if (inode) {
|
||||
if (!ext4_i_metadata_bhs(inode))
|
||||
ext4_inode_attach_mmb(inode);
|
||||
mmb_mark_buffer_dirty(bh, ext4_i_metadata_bhs(inode));
|
||||
} else {
|
||||
mark_buffer_dirty(bh);
|
||||
}
|
||||
if (inode && inode_needs_sync(inode)) {
|
||||
sync_dirty_buffer(bh);
|
||||
if (buffer_req(bh) && !buffer_uptodate(bh)) {
|
||||
|
||||
@@ -68,9 +68,6 @@ static int ext4_sync_parent(struct inode *inode)
|
||||
* through ext4_evict_inode()) and so we are safe to flush
|
||||
* metadata blocks and the inode.
|
||||
*/
|
||||
ret = mmb_sync(&EXT4_I(inode)->i_metadata_bhs);
|
||||
if (ret)
|
||||
break;
|
||||
ret = sync_inode_metadata(inode, 1);
|
||||
if (ret)
|
||||
break;
|
||||
@@ -83,22 +80,11 @@ static int ext4_fsync_nojournal(struct file *file, loff_t start, loff_t end,
|
||||
int datasync, bool *needs_barrier)
|
||||
{
|
||||
struct inode *inode = file->f_inode;
|
||||
struct writeback_control wbc = {
|
||||
.sync_mode = WB_SYNC_ALL,
|
||||
.nr_to_write = 0,
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = mmb_fsync_noflush(file, &EXT4_I(inode)->i_metadata_bhs,
|
||||
start, end, datasync);
|
||||
ret = sync_inode_metadata(inode, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Force writeout of inode table buffer to disk */
|
||||
ret = ext4_write_inode(inode, &wbc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ext4_sync_parent(inode);
|
||||
|
||||
if (test_opt(inode->i_sb, BARRIER))
|
||||
@@ -156,6 +142,10 @@ int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
|
||||
if (sb_rdonly(inode->i_sb))
|
||||
goto out;
|
||||
|
||||
ret = file_write_and_wait_range(file, start, end);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (!EXT4_SB(inode->i_sb)->s_journal) {
|
||||
ret = ext4_fsync_nojournal(file, start, end, datasync,
|
||||
&needs_barrier);
|
||||
@@ -164,10 +154,6 @@ int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = file_write_and_wait_range(file, start, end);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* The caller's filemap_fdatawrite()/wait will sync the data.
|
||||
* Metadata is in the journal, we wait for proper transaction to
|
||||
|
||||
110
fs/ext4/inode.c
110
fs/ext4/inode.c
@@ -186,6 +186,8 @@ void ext4_evict_inode(struct inode *inode)
|
||||
if (EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)
|
||||
ext4_evict_ea_inode(inode);
|
||||
if (inode->i_nlink) {
|
||||
struct mapping_metadata_bhs *mmb;
|
||||
|
||||
/*
|
||||
* If there's dirty page will lead to data loss, user
|
||||
* could see stale data.
|
||||
@@ -195,9 +197,9 @@ void ext4_evict_inode(struct inode *inode)
|
||||
ext4_warning_inode(inode, "data will be lost");
|
||||
|
||||
truncate_inode_pages_final(&inode->i_data);
|
||||
/* Avoid mballoc special inode which has no proper iops */
|
||||
if (!EXT4_SB(inode->i_sb)->s_journal)
|
||||
mmb_sync(&EXT4_I(inode)->i_metadata_bhs);
|
||||
mmb = ext4_i_metadata_bhs(inode);
|
||||
if (mmb)
|
||||
mmb_sync(mmb);
|
||||
goto no_delete;
|
||||
}
|
||||
|
||||
@@ -3452,6 +3454,7 @@ static bool ext4_release_folio(struct folio *folio, gfp_t wait)
|
||||
static bool ext4_inode_datasync_dirty(struct inode *inode)
|
||||
{
|
||||
journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;
|
||||
struct mapping_metadata_bhs *mmb;
|
||||
|
||||
if (journal) {
|
||||
if (jbd2_transaction_committed(journal,
|
||||
@@ -3462,8 +3465,9 @@ static bool ext4_inode_datasync_dirty(struct inode *inode)
|
||||
return true;
|
||||
}
|
||||
|
||||
mmb = ext4_i_metadata_bhs(inode);
|
||||
/* Any metadata buffers to write? */
|
||||
if (mmb_has_buffers(&EXT4_I(inode)->i_metadata_bhs))
|
||||
if (mmb && mmb_has_buffers(mmb))
|
||||
return true;
|
||||
return inode_state_read_once(inode) & I_DIRTY_DATASYNC;
|
||||
}
|
||||
@@ -5799,6 +5803,10 @@ static int ext4_do_update_inode(handle_t *handle,
|
||||
* ext4_mark_inode_dirty(). This is a correctness thing for WB_SYNC_ALL
|
||||
* writeback.
|
||||
*
|
||||
* For nojournal mode all the work is done in ext4_sync_inode_metadata()
|
||||
* because inode content is already copied into raw inode buffer and inode
|
||||
* is marked with I_METADATA_WRITEBACK.
|
||||
*
|
||||
* Note that we are absolutely dependent upon all inode dirtiers doing the
|
||||
* right thing: they *must* call mark_inode_dirty() after dirtying info in
|
||||
* which we are interested.
|
||||
@@ -5824,42 +5832,54 @@ int ext4_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
if (unlikely(err))
|
||||
return err;
|
||||
|
||||
if (EXT4_SB(inode->i_sb)->s_journal) {
|
||||
if (ext4_journal_current_handle()) {
|
||||
ext4_debug("called recursively, non-PF_MEMALLOC!\n");
|
||||
dump_stack();
|
||||
return -EIO;
|
||||
}
|
||||
if (!EXT4_SB(inode->i_sb)->s_journal)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* No need to force transaction in WB_SYNC_NONE mode. Also
|
||||
* ext4_sync_fs() will force the commit after everything is
|
||||
* written.
|
||||
*/
|
||||
if (wbc->sync_mode != WB_SYNC_ALL || wbc->for_sync)
|
||||
return 0;
|
||||
|
||||
err = ext4_fc_commit(EXT4_SB(inode->i_sb)->s_journal,
|
||||
EXT4_I(inode)->i_sync_tid);
|
||||
} else {
|
||||
struct ext4_iloc iloc;
|
||||
|
||||
err = __ext4_get_inode_loc_noinmem(inode, &iloc);
|
||||
if (err)
|
||||
return err;
|
||||
/*
|
||||
* sync(2) will flush the whole buffer cache. No need to do
|
||||
* it here separately for each inode.
|
||||
*/
|
||||
if (wbc->sync_mode == WB_SYNC_ALL && !wbc->for_sync)
|
||||
sync_dirty_buffer(iloc.bh);
|
||||
if (buffer_req(iloc.bh) && !buffer_uptodate(iloc.bh)) {
|
||||
ext4_error_inode_block(inode, iloc.bh->b_blocknr, EIO,
|
||||
"IO error syncing inode");
|
||||
err = -EIO;
|
||||
}
|
||||
brelse(iloc.bh);
|
||||
if (ext4_journal_current_handle()) {
|
||||
ext4_debug("called recursively, non-PF_MEMALLOC!\n");
|
||||
dump_stack();
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* No need to force transaction in WB_SYNC_NONE mode. Also
|
||||
* ext4_sync_fs() will force the commit after everything is
|
||||
* written.
|
||||
*/
|
||||
if (wbc->sync_mode != WB_SYNC_ALL || wbc->for_sync)
|
||||
return 0;
|
||||
|
||||
return ext4_fc_commit(EXT4_SB(inode->i_sb)->s_journal,
|
||||
EXT4_I(inode)->i_sync_tid);
|
||||
}
|
||||
|
||||
int ext4_sync_inode_metadata(struct inode *inode, struct writeback_control *wbc)
|
||||
{
|
||||
struct ext4_iloc iloc;
|
||||
struct mapping_metadata_bhs *mmb;
|
||||
int err;
|
||||
|
||||
/* We should only get here in nojournal mode */
|
||||
if (WARN_ON_ONCE(EXT4_SB(inode->i_sb)->s_journal))
|
||||
return -EFSCORRUPTED;
|
||||
|
||||
err = __ext4_get_inode_loc_noinmem(inode, &iloc);
|
||||
if (err)
|
||||
return err;
|
||||
mmb = READ_ONCE(EXT4_I(inode)->i_metadata_bhs);
|
||||
if (mmb) {
|
||||
err = mmb_sync(mmb);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
sync_dirty_buffer(iloc.bh);
|
||||
if (buffer_write_io_error(iloc.bh)) {
|
||||
ext4_error_inode_block(inode, iloc.bh->b_blocknr, EIO,
|
||||
"IO error syncing inode");
|
||||
err = -EIO;
|
||||
}
|
||||
out:
|
||||
brelse(iloc.bh);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -6407,6 +6427,20 @@ int ext4_mark_iloc_dirty(handle_t *handle,
|
||||
/* ext4_do_update_inode() does jbd2_journal_dirty_metadata */
|
||||
err = ext4_do_update_inode(handle, inode, iloc);
|
||||
put_bh(iloc->bh);
|
||||
/*
|
||||
* Mark that there's metadata writeout pending for the inode so that it
|
||||
* gets properly flushed on fsync(2) and similar.
|
||||
*/
|
||||
if (!EXT4_SB(inode->i_sb)->s_journal) {
|
||||
/*
|
||||
* Inode didn't need to go through dirtying, make sure it is
|
||||
* attached to wb so that writeback can handle it.
|
||||
*/
|
||||
spin_lock(&inode->i_lock);
|
||||
inode_attach_wb(inode, NULL);
|
||||
spin_unlock(&inode->i_lock);
|
||||
set_inode_metadata_writeback(inode);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@@ -1430,7 +1430,7 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
|
||||
INIT_WORK(&ei->i_rsv_conversion_work, ext4_end_io_rsv_work);
|
||||
ext4_fc_init_inode(&ei->vfs_inode);
|
||||
spin_lock_init(&ei->i_fc_lock);
|
||||
mmb_init(&ei->i_metadata_bhs, &ei->vfs_inode.i_data);
|
||||
ei->i_metadata_bhs = NULL;
|
||||
#ifdef CONFIG_LOCKDEP
|
||||
lockdep_set_subclass(&ei->i_data_sem, I_DATA_SEM_NORMAL);
|
||||
#endif
|
||||
@@ -1451,6 +1451,7 @@ static int ext4_drop_inode(struct inode *inode)
|
||||
static void ext4_free_in_core_inode(struct inode *inode)
|
||||
{
|
||||
fscrypt_free_inode(inode);
|
||||
kfree(ext4_i_metadata_bhs(inode));
|
||||
if (!list_empty(&(EXT4_I(inode)->i_fc_list))) {
|
||||
pr_warn("%s: inode %llu still in fc list",
|
||||
__func__, inode->i_ino);
|
||||
@@ -1529,9 +1530,11 @@ static void destroy_inodecache(void)
|
||||
|
||||
void ext4_clear_inode(struct inode *inode)
|
||||
{
|
||||
struct mapping_metadata_bhs *mmb = ext4_i_metadata_bhs(inode);
|
||||
|
||||
ext4_fc_del(inode);
|
||||
if (!EXT4_SB(inode->i_sb)->s_journal)
|
||||
mmb_invalidate(&EXT4_I(inode)->i_metadata_bhs);
|
||||
if (mmb)
|
||||
mmb_invalidate(mmb);
|
||||
clear_inode(inode);
|
||||
ext4_discard_preallocations(inode);
|
||||
/*
|
||||
@@ -1605,9 +1608,13 @@ static int ext4_nfs_commit_metadata(struct inode *inode)
|
||||
struct writeback_control wbc = {
|
||||
.sync_mode = WB_SYNC_ALL
|
||||
};
|
||||
int ret;
|
||||
|
||||
trace_ext4_nfs_commit_metadata(inode);
|
||||
return ext4_write_inode(inode, &wbc);
|
||||
ret = ext4_write_inode(inode, &wbc);
|
||||
if (!ret && inode_state_read_once(inode) & I_METADATA_WRITEBACK)
|
||||
ret = ext4_sync_inode_metadata(inode, &wbc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_QUOTA
|
||||
@@ -1664,6 +1671,7 @@ static const struct super_operations ext4_sops = {
|
||||
.free_inode = ext4_free_in_core_inode,
|
||||
.destroy_inode = ext4_destroy_inode,
|
||||
.write_inode = ext4_write_inode,
|
||||
.sync_inode_metadata = ext4_sync_inode_metadata,
|
||||
.dirty_inode = ext4_dirty_inode,
|
||||
.drop_inode = ext4_drop_inode,
|
||||
.evict_inode = ext4_evict_inode,
|
||||
|
||||
@@ -1109,10 +1109,10 @@ int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo)
|
||||
}
|
||||
|
||||
fat_truncate_time(dir, NULL, FAT_UPDATE_ATIME | FAT_UPDATE_CMTIME);
|
||||
err = 0;
|
||||
mark_inode_dirty(dir);
|
||||
if (IS_DIRSYNC(dir))
|
||||
(void)fat_sync_inode(dir);
|
||||
else
|
||||
mark_inode_dirty(dir);
|
||||
(void)sync_inode_metadata(dir, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -421,7 +421,6 @@ extern void fat_detach(struct inode *inode);
|
||||
extern struct inode *fat_iget(struct super_block *sb, loff_t i_pos);
|
||||
extern struct inode *fat_build_inode(struct super_block *sb,
|
||||
struct msdos_dir_entry *de, loff_t i_pos);
|
||||
extern int fat_sync_inode(struct inode *inode);
|
||||
extern int fat_fill_super(struct super_block *sb, struct fs_context *fc,
|
||||
void (*setup)(struct super_block *));
|
||||
extern int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de);
|
||||
|
||||
@@ -190,8 +190,7 @@ int fat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
|
||||
struct inode *fat_inode = MSDOS_SB(inode->i_sb)->fat_inode;
|
||||
int err;
|
||||
|
||||
err = mmb_fsync_noflush(filp, &MSDOS_I(inode)->i_metadata_bhs,
|
||||
start, end, datasync);
|
||||
err = simple_fsync_noflush(filp, start, end, datasync);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -332,15 +331,15 @@ static int fat_free(struct inode *inode, int skip)
|
||||
}
|
||||
MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
|
||||
fat_truncate_time(inode, NULL, FAT_UPDATE_CMTIME);
|
||||
mark_inode_dirty(inode);
|
||||
if (wait) {
|
||||
err = fat_sync_inode(inode);
|
||||
err = sync_inode_metadata(inode, 1);
|
||||
if (err) {
|
||||
MSDOS_I(inode)->i_start = i_start;
|
||||
MSDOS_I(inode)->i_logstart = i_logstart;
|
||||
return err;
|
||||
}
|
||||
} else
|
||||
mark_inode_dirty(inode);
|
||||
}
|
||||
|
||||
/* Write a new EOF, and get the remaining cluster chain for freeing. */
|
||||
if (skip) {
|
||||
|
||||
@@ -623,7 +623,41 @@ struct inode *fat_build_inode(struct super_block *sb,
|
||||
|
||||
EXPORT_SYMBOL_GPL(fat_build_inode);
|
||||
|
||||
static int __fat_write_inode(struct inode *inode, int wait);
|
||||
static int __fat_write_inode(struct inode *inode);
|
||||
|
||||
static int fat_sync_inode_metadata(struct inode *inode,
|
||||
struct writeback_control *wbc)
|
||||
{
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
|
||||
struct buffer_head *bh;
|
||||
loff_t i_pos;
|
||||
sector_t blocknr;
|
||||
int offset;
|
||||
|
||||
/* The root directory has no directory entry of its own. */
|
||||
if (inode->i_ino == MSDOS_ROOT_INO)
|
||||
goto sync_bhs;
|
||||
i_pos = fat_i_pos_read(sbi, inode);
|
||||
if (!i_pos)
|
||||
goto sync_bhs;
|
||||
|
||||
fat_get_blknr_offset(sbi, i_pos, &blocknr, &offset);
|
||||
bh = sb_find_get_block_nonatomic(inode->i_sb, blocknr);
|
||||
/*
|
||||
* Buffer present? We leave buffer_dirty check for sync_dirty_buffer()
|
||||
* for proper synchronization with ongoing IO.
|
||||
*/
|
||||
if (bh && buffer_uptodate(bh)) {
|
||||
sync_dirty_buffer(bh);
|
||||
if (buffer_write_io_error(bh)) {
|
||||
brelse(bh);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
brelse(bh);
|
||||
sync_bhs:
|
||||
return mmb_sync(&MSDOS_I(inode)->i_metadata_bhs);
|
||||
}
|
||||
|
||||
static void fat_free_eofblocks(struct inode *inode)
|
||||
{
|
||||
@@ -640,7 +674,7 @@ static void fat_free_eofblocks(struct inode *inode)
|
||||
* any corruption on the next access to the cluster
|
||||
* chain for the file.
|
||||
*/
|
||||
err = __fat_write_inode(inode, inode_needs_sync(inode));
|
||||
err = sync_inode_metadata(inode, inode_needs_sync(inode));
|
||||
if (err) {
|
||||
fat_msg(inode->i_sb, KERN_WARNING, "Failed to "
|
||||
"update on disk inode for unused "
|
||||
@@ -854,7 +888,7 @@ static int fat_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __fat_write_inode(struct inode *inode, int wait)
|
||||
static int __fat_write_inode(struct inode *inode)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
||||
@@ -863,10 +897,13 @@ static int __fat_write_inode(struct inode *inode, int wait)
|
||||
struct timespec64 mtime;
|
||||
loff_t i_pos;
|
||||
sector_t blocknr;
|
||||
int err, offset;
|
||||
int offset;
|
||||
|
||||
if (inode->i_ino == MSDOS_ROOT_INO)
|
||||
if (inode->i_ino == MSDOS_ROOT_INO) {
|
||||
/* No entry to update but the metadata bh list may need syncing. */
|
||||
set_inode_metadata_writeback(inode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
retry:
|
||||
i_pos = fat_i_pos_read(sbi, inode);
|
||||
@@ -907,11 +944,9 @@ static int __fat_write_inode(struct inode *inode, int wait)
|
||||
}
|
||||
spin_unlock(&sbi->inode_hash_lock);
|
||||
mark_buffer_dirty(bh);
|
||||
err = 0;
|
||||
if (wait)
|
||||
err = sync_dirty_buffer(bh);
|
||||
brelse(bh);
|
||||
return err;
|
||||
set_inode_metadata_writeback(inode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fat_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
@@ -925,23 +960,17 @@ static int fat_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
err = fat_clusters_flush(sb);
|
||||
mutex_unlock(&MSDOS_SB(sb)->s_lock);
|
||||
} else
|
||||
err = __fat_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
|
||||
err = __fat_write_inode(inode);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int fat_sync_inode(struct inode *inode)
|
||||
{
|
||||
return __fat_write_inode(inode, 1);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(fat_sync_inode);
|
||||
|
||||
static int fat_show_options(struct seq_file *m, struct dentry *root);
|
||||
static const struct super_operations fat_sops = {
|
||||
.alloc_inode = fat_alloc_inode,
|
||||
.free_inode = fat_free_inode,
|
||||
.write_inode = fat_write_inode,
|
||||
.sync_inode_metadata = fat_sync_inode_metadata,
|
||||
.evict_inode = fat_evict_inode,
|
||||
.put_super = fat_put_super,
|
||||
.statfs = fat_statfs,
|
||||
|
||||
@@ -146,16 +146,17 @@ int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster)
|
||||
} else {
|
||||
MSDOS_I(inode)->i_start = new_dclus;
|
||||
MSDOS_I(inode)->i_logstart = new_dclus;
|
||||
mark_inode_dirty(inode);
|
||||
/*
|
||||
* Since generic_write_sync() synchronizes regular files later,
|
||||
* we sync here only directories.
|
||||
*/
|
||||
if (S_ISDIR(inode->i_mode) && IS_DIRSYNC(inode)) {
|
||||
ret = fat_sync_inode(inode);
|
||||
ret = sync_inode_metadata(inode, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else
|
||||
mark_inode_dirty(inode);
|
||||
}
|
||||
|
||||
}
|
||||
if (new_fclus != (inode->i_blocks >> (sbi->cluster_bits - 9))) {
|
||||
fat_fs_error_ratelimit(
|
||||
|
||||
@@ -255,10 +255,9 @@ static int msdos_add_entry(struct inode *dir, const unsigned char *name,
|
||||
return err;
|
||||
|
||||
fat_truncate_time(dir, ts, FAT_UPDATE_CMTIME);
|
||||
mark_inode_dirty(dir);
|
||||
if (IS_DIRSYNC(dir))
|
||||
(void)fat_sync_inode(dir);
|
||||
else
|
||||
mark_inode_dirty(dir);
|
||||
(void)sync_inode_metadata(dir, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -476,21 +475,20 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
|
||||
MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
|
||||
else
|
||||
MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
|
||||
mark_inode_dirty(old_inode);
|
||||
if (IS_DIRSYNC(old_dir)) {
|
||||
err = fat_sync_inode(old_inode);
|
||||
err = sync_inode_metadata(old_inode, 1);
|
||||
if (err) {
|
||||
MSDOS_I(old_inode)->i_attrs = old_attrs;
|
||||
goto out;
|
||||
}
|
||||
} else
|
||||
mark_inode_dirty(old_inode);
|
||||
}
|
||||
|
||||
inode_inc_iversion(old_dir);
|
||||
fat_truncate_time(old_dir, NULL, FAT_UPDATE_CMTIME);
|
||||
mark_inode_dirty(old_dir);
|
||||
if (IS_DIRSYNC(old_dir))
|
||||
(void)fat_sync_inode(old_dir);
|
||||
else
|
||||
mark_inode_dirty(old_dir);
|
||||
(void)sync_inode_metadata(old_dir, 1);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -521,12 +519,12 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
|
||||
MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
|
||||
else
|
||||
MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
|
||||
mark_inode_dirty(old_inode);
|
||||
if (IS_DIRSYNC(new_dir)) {
|
||||
err = fat_sync_inode(old_inode);
|
||||
err = sync_inode_metadata(old_inode, 1);
|
||||
if (err)
|
||||
goto error_inode;
|
||||
} else
|
||||
mark_inode_dirty(old_inode);
|
||||
}
|
||||
|
||||
if (update_dotdot) {
|
||||
fat_set_start(dotdot_de, MSDOS_I(new_dir)->i_logstart);
|
||||
@@ -548,10 +546,9 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
|
||||
goto error_dotdot;
|
||||
inode_inc_iversion(old_dir);
|
||||
fat_truncate_time(old_dir, &ts, FAT_UPDATE_CMTIME);
|
||||
mark_inode_dirty(old_dir);
|
||||
if (IS_DIRSYNC(old_dir))
|
||||
(void)fat_sync_inode(old_dir);
|
||||
else
|
||||
mark_inode_dirty(old_dir);
|
||||
(void)sync_inode_metadata(old_dir, 1);
|
||||
|
||||
if (new_inode) {
|
||||
drop_nlink(new_inode);
|
||||
@@ -580,8 +577,10 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
|
||||
MSDOS_I(old_inode)->i_attrs = old_attrs;
|
||||
if (new_inode) {
|
||||
fat_attach(new_inode, new_i_pos);
|
||||
if (corrupt)
|
||||
corrupt |= fat_sync_inode(new_inode);
|
||||
if (corrupt) {
|
||||
mark_inode_dirty(new_inode);
|
||||
corrupt |= sync_inode_metadata(new_inode, 1);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* If new entry was not sharing the data cluster, it
|
||||
|
||||
@@ -678,10 +678,9 @@ static int vfat_add_entry(struct inode *dir, const struct qstr *qname,
|
||||
|
||||
/* update timestamp */
|
||||
fat_truncate_time(dir, ts, FAT_UPDATE_CMTIME);
|
||||
mark_inode_dirty(dir);
|
||||
if (IS_DIRSYNC(dir))
|
||||
(void)fat_sync_inode(dir);
|
||||
else
|
||||
mark_inode_dirty(dir);
|
||||
(void)sync_inode_metadata(dir, 1);
|
||||
cleanup:
|
||||
kfree(slots);
|
||||
return err;
|
||||
@@ -904,9 +903,9 @@ static int vfat_get_dotdot_de(struct inode *inode, struct buffer_head **bh,
|
||||
|
||||
static int vfat_sync_ipos(struct inode *dir, struct inode *inode)
|
||||
{
|
||||
if (IS_DIRSYNC(dir))
|
||||
return fat_sync_inode(inode);
|
||||
mark_inode_dirty(inode);
|
||||
if (IS_DIRSYNC(dir))
|
||||
return sync_inode_metadata(inode, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -925,10 +924,9 @@ static void vfat_update_dir_metadata(struct inode *dir, struct timespec64 *ts)
|
||||
{
|
||||
inode_inc_iversion(dir);
|
||||
fat_truncate_time(dir, ts, FAT_UPDATE_CMTIME);
|
||||
mark_inode_dirty(dir);
|
||||
if (IS_DIRSYNC(dir))
|
||||
(void)fat_sync_inode(dir);
|
||||
else
|
||||
mark_inode_dirty(dir);
|
||||
(void)sync_inode_metadata(dir, 1);
|
||||
}
|
||||
|
||||
static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
@@ -1024,8 +1022,10 @@ static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
fat_attach(old_inode, old_sinfo.i_pos);
|
||||
if (new_inode) {
|
||||
fat_attach(new_inode, new_i_pos);
|
||||
if (corrupt)
|
||||
corrupt |= fat_sync_inode(new_inode);
|
||||
if (corrupt) {
|
||||
mark_inode_dirty(new_inode);
|
||||
corrupt |= sync_inode_metadata(new_inode, 1);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* If new entry was not sharing the data cluster, it
|
||||
|
||||
@@ -299,6 +299,7 @@ void __inode_attach_wb(struct inode *inode, struct folio *folio)
|
||||
if (unlikely(cmpxchg(&inode->i_wb, NULL, wb)))
|
||||
wb_put(wb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__inode_attach_wb);
|
||||
|
||||
/**
|
||||
* inode_cgwb_move_to_attached - put the inode onto wb->b_attached list
|
||||
@@ -1851,6 +1852,22 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
if (ret == 0)
|
||||
ret = err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do we need to wait for inode metadata IO possibly submitted
|
||||
* by previous WB_SYNC_NONE writeback?
|
||||
*/
|
||||
if (wbc->sync_mode == WB_SYNC_ALL && !wbc->for_sync &&
|
||||
inode_state_read_once(inode) & I_METADATA_WRITEBACK) {
|
||||
int err;
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
inode_state_clear(inode, I_METADATA_WRITEBACK);
|
||||
spin_unlock(&inode->i_lock);
|
||||
err = inode->i_sb->s_op->sync_inode_metadata(inode, wbc);
|
||||
if (ret == 0)
|
||||
ret = err;
|
||||
}
|
||||
wbc->unpinned_netfs_wb = false;
|
||||
trace_writeback_single_inode(inode, wbc, nr_to_write);
|
||||
return ret;
|
||||
@@ -1892,14 +1909,17 @@ static int writeback_single_inode(struct inode *inode,
|
||||
/*
|
||||
* If the inode is already fully clean, then there's nothing to do.
|
||||
*
|
||||
* For data-integrity syncs we also need to check whether any pages are
|
||||
* still under writeback, e.g. due to prior WB_SYNC_NONE writeback. If
|
||||
* there are any such pages, we'll need to wait for them.
|
||||
* For data-integrity syncs we also need to check whether any folios or
|
||||
* metadata are still under writeback, e.g. due to prior WB_SYNC_NONE
|
||||
* writeback. If there, we'll need to wait for them.
|
||||
*/
|
||||
if (!(inode_state_read(inode) & I_DIRTY_ALL) &&
|
||||
(wbc->sync_mode != WB_SYNC_ALL ||
|
||||
!mapping_tagged(inode->i_mapping, PAGECACHE_TAG_WRITEBACK)))
|
||||
goto out;
|
||||
if (!(inode_state_read(inode) & I_DIRTY_ALL)) {
|
||||
if (wbc->sync_mode != WB_SYNC_ALL)
|
||||
goto out;
|
||||
if (!mapping_tagged(inode->i_mapping, PAGECACHE_TAG_WRITEBACK) &&
|
||||
!(inode_state_read(inode) & I_METADATA_WRITEBACK))
|
||||
goto out;
|
||||
}
|
||||
inode_state_set(inode, I_SYNC);
|
||||
wbc_attach_and_unlock_inode(wbc, inode);
|
||||
|
||||
|
||||
@@ -1559,9 +1559,12 @@ int simple_fsync_noflush(struct file *file, loff_t start, loff_t end,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!(inode_state_read_once(inode) & I_DIRTY_ALL))
|
||||
if (!(inode_state_read_once(inode) &
|
||||
(I_DIRTY_ALL | I_SYNC | I_METADATA_WRITEBACK)))
|
||||
goto out;
|
||||
if (datasync && !(inode_state_read_once(inode) & I_DIRTY_DATASYNC))
|
||||
if (datasync &&
|
||||
!(inode_state_read_once(inode) &
|
||||
(I_DIRTY_DATASYNC | I_SYNC | I_METADATA_WRITEBACK)))
|
||||
goto out;
|
||||
|
||||
ret = sync_inode_metadata(inode, 1);
|
||||
|
||||
@@ -23,7 +23,7 @@ const struct file_operations minix_dir_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read = generic_read_dir,
|
||||
.iterate_shared = minix_readdir,
|
||||
.fsync = minix_fsync,
|
||||
.fsync = simple_fsync,
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -10,13 +10,6 @@
|
||||
#include <linux/buffer_head.h>
|
||||
#include "minix.h"
|
||||
|
||||
int minix_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
||||
{
|
||||
return mmb_fsync(file,
|
||||
&minix_i(file->f_mapping->host)->i_metadata_bhs,
|
||||
start, end, datasync);
|
||||
}
|
||||
|
||||
/*
|
||||
* We have mostly NULLs here: the current defaults are OK for
|
||||
* the minix filesystem.
|
||||
@@ -26,7 +19,7 @@ const struct file_operations minix_file_operations = {
|
||||
.read_iter = generic_file_read_iter,
|
||||
.write_iter = generic_file_write_iter,
|
||||
.mmap_prepare = generic_file_mmap_prepare,
|
||||
.fsync = minix_fsync,
|
||||
.fsync = simple_fsync,
|
||||
.splice_read = filemap_splice_read,
|
||||
};
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
static int minix_write_inode(struct inode *inode,
|
||||
struct writeback_control *wbc);
|
||||
static int minix_sync_inode_metadata(struct inode *inode,
|
||||
struct writeback_control *wbc);
|
||||
static int minix_statfs(struct dentry *dentry, struct kstatfs *buf);
|
||||
|
||||
void __minix_error_inode(struct inode *inode, const char *function,
|
||||
@@ -128,6 +130,7 @@ static const struct super_operations minix_sops = {
|
||||
.alloc_inode = minix_alloc_inode,
|
||||
.free_inode = minix_free_in_core_inode,
|
||||
.write_inode = minix_write_inode,
|
||||
.sync_inode_metadata = minix_sync_inode_metadata,
|
||||
.evict_inode = minix_evict_inode,
|
||||
.put_super = minix_put_super,
|
||||
.statfs = minix_statfs,
|
||||
@@ -630,7 +633,7 @@ struct inode *minix_iget(struct super_block *sb, unsigned long ino)
|
||||
/*
|
||||
* The minix V1 function to synchronize an inode.
|
||||
*/
|
||||
static struct buffer_head * V1_minix_update_inode(struct inode * inode)
|
||||
static int V1_minix_update_inode(struct inode * inode)
|
||||
{
|
||||
struct buffer_head * bh;
|
||||
struct minix_inode * raw_inode;
|
||||
@@ -639,7 +642,7 @@ static struct buffer_head * V1_minix_update_inode(struct inode * inode)
|
||||
|
||||
raw_inode = minix_V1_raw_inode(inode->i_sb, inode->i_ino, &bh);
|
||||
if (!raw_inode)
|
||||
return NULL;
|
||||
return -EIO;
|
||||
raw_inode->i_mode = inode->i_mode;
|
||||
raw_inode->i_uid = fs_high2lowuid(i_uid_read(inode));
|
||||
raw_inode->i_gid = fs_high2lowgid(i_gid_read(inode));
|
||||
@@ -651,13 +654,15 @@ static struct buffer_head * V1_minix_update_inode(struct inode * inode)
|
||||
else for (i = 0; i < 9; i++)
|
||||
raw_inode->i_zone[i] = minix_inode->u.i1_data[i];
|
||||
mark_buffer_dirty(bh);
|
||||
return bh;
|
||||
brelse(bh);
|
||||
set_inode_metadata_writeback(inode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The minix V2 function to synchronize an inode.
|
||||
*/
|
||||
static struct buffer_head * V2_minix_update_inode(struct inode * inode)
|
||||
static int V2_minix_update_inode(struct inode * inode)
|
||||
{
|
||||
struct buffer_head * bh;
|
||||
struct minix2_inode * raw_inode;
|
||||
@@ -666,7 +671,7 @@ static struct buffer_head * V2_minix_update_inode(struct inode * inode)
|
||||
|
||||
raw_inode = minix_V2_raw_inode(inode->i_sb, inode->i_ino, &bh);
|
||||
if (!raw_inode)
|
||||
return NULL;
|
||||
return -EIO;
|
||||
raw_inode->i_mode = inode->i_mode;
|
||||
raw_inode->i_uid = fs_high2lowuid(i_uid_read(inode));
|
||||
raw_inode->i_gid = fs_high2lowgid(i_gid_read(inode));
|
||||
@@ -680,29 +685,42 @@ static struct buffer_head * V2_minix_update_inode(struct inode * inode)
|
||||
else for (i = 0; i < 10; i++)
|
||||
raw_inode->i_zone[i] = minix_inode->u.i2_data[i];
|
||||
mark_buffer_dirty(bh);
|
||||
return bh;
|
||||
brelse(bh);
|
||||
set_inode_metadata_writeback(inode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int minix_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
{
|
||||
if (INODE_VERSION(inode) == MINIX_V1)
|
||||
return V1_minix_update_inode(inode);
|
||||
return V2_minix_update_inode(inode);
|
||||
}
|
||||
|
||||
static int minix_sync_inode_metadata(struct inode *inode,
|
||||
struct writeback_control *wbc)
|
||||
{
|
||||
int err = 0;
|
||||
struct buffer_head *bh;
|
||||
void *raw_inode;
|
||||
|
||||
if (INODE_VERSION(inode) == MINIX_V1)
|
||||
bh = V1_minix_update_inode(inode);
|
||||
raw_inode = minix_V1_raw_inode(inode->i_sb, inode->i_ino, &bh);
|
||||
else
|
||||
bh = V2_minix_update_inode(inode);
|
||||
if (!bh)
|
||||
raw_inode = minix_V2_raw_inode(inode->i_sb, inode->i_ino, &bh);
|
||||
if (!raw_inode)
|
||||
return -EIO;
|
||||
if (wbc->sync_mode == WB_SYNC_ALL && buffer_dirty(bh)) {
|
||||
sync_dirty_buffer(bh);
|
||||
if (buffer_req(bh) && !buffer_uptodate(bh)) {
|
||||
printk("IO error syncing minix inode [%s:%08llx]\n",
|
||||
inode->i_sb->s_id, inode->i_ino);
|
||||
err = -EIO;
|
||||
}
|
||||
err = mmb_sync(&minix_i(inode)->i_metadata_bhs);
|
||||
if (err)
|
||||
goto out;
|
||||
sync_dirty_buffer(bh);
|
||||
if (buffer_write_io_error(bh)) {
|
||||
printk("IO error syncing minix inode [%s:%08llx]\n",
|
||||
inode->i_sb->s_id, inode->i_ino);
|
||||
err = -EIO;
|
||||
}
|
||||
brelse (bh);
|
||||
out:
|
||||
brelse(bh);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,6 @@ int minix_getattr(struct mnt_idmap *, const struct path *,
|
||||
struct kstat *, u32, unsigned int);
|
||||
int minix_prepare_chunk(struct folio *folio, loff_t pos, unsigned len);
|
||||
struct mapping_metadata_bhs *minix_get_metadata_bhs(struct inode *inode);
|
||||
int minix_fsync(struct file *file, loff_t start, loff_t end, int datasync);
|
||||
|
||||
extern void V1_minix_truncate(struct inode *);
|
||||
extern void V2_minix_truncate(struct inode *);
|
||||
|
||||
@@ -157,6 +157,6 @@ const struct file_operations udf_dir_operations = {
|
||||
.read = generic_read_dir,
|
||||
.iterate_shared = udf_readdir,
|
||||
.unlocked_ioctl = udf_ioctl,
|
||||
.fsync = udf_fsync,
|
||||
.fsync = simple_fsync,
|
||||
.setlease = generic_setlease,
|
||||
};
|
||||
|
||||
@@ -198,13 +198,6 @@ static int udf_file_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int udf_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
||||
{
|
||||
return mmb_fsync(file,
|
||||
&UDF_I(file->f_mapping->host)->i_metadata_bhs,
|
||||
start, end, datasync);
|
||||
}
|
||||
|
||||
const struct file_operations udf_file_operations = {
|
||||
.read_iter = generic_file_read_iter,
|
||||
.unlocked_ioctl = udf_ioctl,
|
||||
@@ -212,7 +205,7 @@ const struct file_operations udf_file_operations = {
|
||||
.mmap = udf_file_mmap,
|
||||
.write_iter = udf_file_write_iter,
|
||||
.release = udf_release_file,
|
||||
.fsync = udf_fsync,
|
||||
.fsync = simple_fsync,
|
||||
.splice_read = filemap_splice_read,
|
||||
.splice_write = iter_file_splice_write,
|
||||
.llseek = generic_file_llseek,
|
||||
@@ -253,6 +246,8 @@ static int udf_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
|
||||
setattr_copy(&nop_mnt_idmap, inode, attr);
|
||||
mark_inode_dirty(inode);
|
||||
if (IS_SYNC(inode))
|
||||
sync_inode_metadata(inode, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,8 +51,6 @@
|
||||
struct udf_map_rq;
|
||||
|
||||
static umode_t udf_convert_permissions(struct fileEntry *);
|
||||
static int udf_update_inode(struct inode *, int);
|
||||
static int udf_sync_inode(struct inode *inode);
|
||||
static int udf_alloc_i_data(struct inode *inode, size_t size);
|
||||
static int inode_getblk(struct inode *inode, struct udf_map_rq *map);
|
||||
static int udf_insert_aext(struct inode *, struct extent_position,
|
||||
@@ -142,7 +140,7 @@ void udf_evict_inode(struct inode *inode)
|
||||
if (!inode->i_nlink) {
|
||||
want_delete = 1;
|
||||
udf_setsize(inode, 0);
|
||||
udf_update_inode(inode, IS_SYNC(inode));
|
||||
sync_inode_metadata(inode, IS_SYNC(inode));
|
||||
}
|
||||
if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB &&
|
||||
inode->i_size != iinfo->i_lenExtents) {
|
||||
@@ -936,10 +934,7 @@ static int inode_getblk(struct inode *inode, struct udf_map_rq *map)
|
||||
iinfo->i_next_alloc_goal = newblocknum + 1;
|
||||
inode_set_ctime_current(inode);
|
||||
|
||||
if (IS_SYNC(inode))
|
||||
udf_sync_inode(inode);
|
||||
else
|
||||
mark_inode_dirty(inode);
|
||||
mark_inode_dirty(inode);
|
||||
ret = 0;
|
||||
out_free:
|
||||
brelse(prev_epos.bh);
|
||||
@@ -1326,10 +1321,7 @@ int udf_setsize(struct inode *inode, loff_t newsize)
|
||||
}
|
||||
update_time:
|
||||
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
|
||||
if (IS_SYNC(inode))
|
||||
udf_sync_inode(inode);
|
||||
else
|
||||
mark_inode_dirty(inode);
|
||||
mark_inode_dirty(inode);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1705,14 +1697,28 @@ void udf_update_extra_perms(struct inode *inode, umode_t mode)
|
||||
iinfo->i_extraPerms |= FE_PERM_O_DELETE;
|
||||
}
|
||||
|
||||
int udf_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
int udf_sync_inode_metadata(struct inode *inode, struct writeback_control *wbc)
|
||||
{
|
||||
return udf_update_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
|
||||
}
|
||||
struct buffer_head *bh;
|
||||
int err = 0;
|
||||
|
||||
static int udf_sync_inode(struct inode *inode)
|
||||
{
|
||||
return udf_update_inode(inode, 1);
|
||||
bh = sb_getblk(inode->i_sb,
|
||||
udf_get_lb_pblock(inode->i_sb,
|
||||
&UDF_I(inode)->i_location, 0));
|
||||
if (!bh)
|
||||
return -EIO;
|
||||
|
||||
sync_dirty_buffer(bh);
|
||||
if (buffer_write_io_error(bh)) {
|
||||
udf_warn(inode->i_sb, "IO error syncing udf inode [%08llx]\n",
|
||||
inode->i_ino);
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
err = mmb_sync(&UDF_I(inode)->i_metadata_bhs);
|
||||
out:
|
||||
brelse(bh);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void udf_adjust_time(struct udf_inode_info *iinfo, struct timespec64 time)
|
||||
@@ -1723,7 +1729,7 @@ static void udf_adjust_time(struct udf_inode_info *iinfo, struct timespec64 time
|
||||
iinfo->i_crtime = time;
|
||||
}
|
||||
|
||||
static int udf_update_inode(struct inode *inode, int do_sync)
|
||||
int udf_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
{
|
||||
struct buffer_head *bh = NULL;
|
||||
struct fileEntry *fe;
|
||||
@@ -1732,7 +1738,6 @@ static int udf_update_inode(struct inode *inode, int do_sync)
|
||||
uint32_t udfperms;
|
||||
uint16_t icbflags;
|
||||
uint16_t crclen;
|
||||
int err = 0;
|
||||
struct udf_sb_info *sbi = UDF_SB(inode->i_sb);
|
||||
unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits;
|
||||
struct udf_inode_info *iinfo = UDF_I(inode);
|
||||
@@ -1937,17 +1942,10 @@ static int udf_update_inode(struct inode *inode, int do_sync)
|
||||
|
||||
/* write the data blocks */
|
||||
mark_buffer_dirty(bh);
|
||||
if (do_sync) {
|
||||
sync_dirty_buffer(bh);
|
||||
if (buffer_write_io_error(bh)) {
|
||||
udf_warn(inode->i_sb, "IO error syncing udf inode [%08llx]\n",
|
||||
inode->i_ino);
|
||||
err = -EIO;
|
||||
}
|
||||
}
|
||||
brelse(bh);
|
||||
set_inode_metadata_writeback(inode);
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct inode *__udf_iget(struct super_block *sb, struct kernel_lb_addr *ino,
|
||||
|
||||
@@ -211,6 +211,7 @@ static const struct super_operations udf_sb_ops = {
|
||||
.alloc_inode = udf_alloc_inode,
|
||||
.free_inode = udf_free_in_core_inode,
|
||||
.write_inode = udf_write_inode,
|
||||
.sync_inode_metadata = udf_sync_inode_metadata,
|
||||
.evict_inode = udf_evict_inode,
|
||||
.put_super = udf_put_super,
|
||||
.sync_fs = udf_sync_fs,
|
||||
|
||||
@@ -137,7 +137,6 @@ static inline unsigned int udf_dir_entry_len(struct fileIdentDesc *cfi)
|
||||
|
||||
/* file.c */
|
||||
extern long udf_ioctl(struct file *, unsigned int, unsigned long);
|
||||
int udf_fsync(struct file *file, loff_t start, loff_t end, int datasync);
|
||||
|
||||
/* inode.c */
|
||||
extern struct inode *__udf_iget(struct super_block *, struct kernel_lb_addr *,
|
||||
@@ -158,6 +157,7 @@ extern struct buffer_head *udf_bread(struct inode *inode, udf_pblk_t block,
|
||||
extern int udf_setsize(struct inode *, loff_t);
|
||||
extern void udf_evict_inode(struct inode *);
|
||||
extern int udf_write_inode(struct inode *, struct writeback_control *wbc);
|
||||
int udf_sync_inode_metadata(struct inode *, struct writeback_control *wbc);
|
||||
extern int inode_bmap(struct inode *inode, sector_t block,
|
||||
struct extent_position *pos, struct kernel_lb_addr *eloc,
|
||||
uint32_t *elen, sector_t *offset, int8_t *etype);
|
||||
|
||||
@@ -210,10 +210,6 @@ void bh_end_async_write(struct bio *bio);
|
||||
|
||||
/* Things to do with metadata buffers list */
|
||||
void mmb_mark_buffer_dirty(struct buffer_head *bh, struct mapping_metadata_bhs *mmb);
|
||||
int mmb_fsync_noflush(struct file *file, struct mapping_metadata_bhs *mmb,
|
||||
loff_t start, loff_t end, bool datasync);
|
||||
int mmb_fsync(struct file *file, struct mapping_metadata_bhs *mmb,
|
||||
loff_t start, loff_t end, bool datasync);
|
||||
void clean_bdev_aliases(struct block_device *bdev, sector_t block,
|
||||
sector_t len);
|
||||
static inline void clean_bdev_bh_alias(struct buffer_head *bh)
|
||||
|
||||
@@ -740,7 +740,8 @@ enum inode_state_flags_enum {
|
||||
I_CREATING = (1U << 15),
|
||||
I_DONTCACHE = (1U << 16),
|
||||
I_SYNC_QUEUED = (1U << 17),
|
||||
I_PINNING_NETFS_WB = (1U << 18)
|
||||
I_PINNING_NETFS_WB = (1U << 18),
|
||||
I_METADATA_WRITEBACK = (1U << 19),
|
||||
};
|
||||
|
||||
#define I_DIRTY_INODE (I_DIRTY_SYNC | I_DIRTY_DATASYNC)
|
||||
@@ -2211,6 +2212,13 @@ static inline void mark_inode_dirty_sync(struct inode *inode)
|
||||
__mark_inode_dirty(inode, I_DIRTY_SYNC);
|
||||
}
|
||||
|
||||
static inline void set_inode_metadata_writeback(struct inode *inode)
|
||||
{
|
||||
spin_lock(&inode->i_lock);
|
||||
inode_state_set(inode, I_METADATA_WRITEBACK);
|
||||
spin_unlock(&inode->i_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* returns the refcount on the inode. it can change arbitrarily.
|
||||
*/
|
||||
|
||||
@@ -87,6 +87,8 @@ struct super_operations {
|
||||
void (*free_inode)(struct inode *inode);
|
||||
void (*dirty_inode)(struct inode *inode, int flags);
|
||||
int (*write_inode)(struct inode *inode, struct writeback_control *wbc);
|
||||
int (*sync_inode_metadata)(struct inode *inode,
|
||||
struct writeback_control *wbc);
|
||||
int (*drop_inode)(struct inode *inode);
|
||||
void (*evict_inode)(struct inode *inode);
|
||||
void (*put_super)(struct super_block *sb);
|
||||
|
||||
Reference in New Issue
Block a user