A lot of bug fixes and cleanups for ext4, including:

* Fix performance problems found in dioread_nolock now that it is the
   default, caused by transaction leaks.
 * Clean up fiemap handling in ext4
 * Clean up and refactor multiple block allocator (mballoc) code
 * Fix a problem with mballoc with a smaller file systems running out
   of blocks because they couldn't properly use blocks that had been
   reserved by inode preallocation.
 * Fixed a race in ext4_sync_parent() versus rename()
 * Simplify the error handling in the extent manipulation code
 * Make sure all metadata I/O errors are felected to ext4_ext_dirty()'s and
   ext4_make_inode_dirty()'s callers.
 * Avoid passing an error pointer to brelse in ext4_xattr_set()
 * Fix race which could result to freeing an inode on the dirty last
   in data=journal mode.
 * Fix refcount handling if ext4_iget() fails
 * Fix a crash in generic/019 caused by a corrupted extent node
 -----BEGIN PGP SIGNATURE-----
 
 iQEyBAABCAAdFiEEK2m5VNv+CHkogTfJ8vlZVpUNgaMFAl7Ze8kACgkQ8vlZVpUN
 gaNChAf4xn0ytFSrweI/S2Sp05G/2L/ocZ2TZZk2ZdGeN1E+ABdSIv/zIF9zuFgZ
 /pY/C+fyEZWt4E3FlNO8gJzoEedkzMCMnUhSIfI+wZbcclyTOSNMJtnrnJKAEtVH
 HOvGZJmg357jy407RCGhZpJ773nwU2xhBTr5OFxvSf9mt/vzebxIOnw5D7HPlC1V
 Fgm6Du8q+tRrPsyjv1Yu4pUEVXMJ7qUcvt326AXVM3kCZO1Aa5GrURX0w3J4mzW1
 tc1tKmtbLcVVYTo9CwHXhk/edbxrhAydSP2iACand3tK6IJuI6j9x+bBJnxXitnr
 vsxsfTYMG18+2SxrJ9LwmagqmrRq
 =HMTs
 -----END PGP SIGNATURE-----

Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4

Pull ext4 updates from Ted Ts'o:
 "A lot of bug fixes and cleanups for ext4, including:

   - Fix performance problems found in dioread_nolock now that it is the
     default, caused by transaction leaks.

   - Clean up fiemap handling in ext4

   - Clean up and refactor multiple block allocator (mballoc) code

   - Fix a problem with mballoc with a smaller file systems running out
     of blocks because they couldn't properly use blocks that had been
     reserved by inode preallocation.

   - Fixed a race in ext4_sync_parent() versus rename()

   - Simplify the error handling in the extent manipulation code

   - Make sure all metadata I/O errors are felected to
     ext4_ext_dirty()'s and ext4_make_inode_dirty()'s callers.

   - Avoid passing an error pointer to brelse in ext4_xattr_set()

   - Fix race which could result to freeing an inode on the dirty last
     in data=journal mode.

   - Fix refcount handling if ext4_iget() fails

   - Fix a crash in generic/019 caused by a corrupted extent node"

* tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (58 commits)
  ext4: avoid unnecessary transaction starts during writeback
  ext4: don't block for O_DIRECT if IOCB_NOWAIT is set
  ext4: remove the access_ok() check in ext4_ioctl_get_es_cache
  fs: remove the access_ok() check in ioctl_fiemap
  fs: handle FIEMAP_FLAG_SYNC in fiemap_prep
  fs: move fiemap range validation into the file systems instances
  iomap: fix the iomap_fiemap prototype
  fs: move the fiemap definitions out of fs.h
  fs: mark __generic_block_fiemap static
  ext4: remove the call to fiemap_check_flags in ext4_fiemap
  ext4: split _ext4_fiemap
  ext4: fix fiemap size checks for bitmap files
  ext4: fix EXT4_MAX_LOGICAL_BLOCK macro
  add comment for ext4_dir_entry_2 file_type member
  jbd2: avoid leaking transaction credits when unreserving handle
  ext4: drop ext4_journal_free_reserved()
  ext4: mballoc: use lock for checking free blocks while retrying
  ext4: mballoc: refactor ext4_mb_good_group()
  ext4: mballoc: introduce pcpu seqcnt for freeing PA to improve ENOSPC handling
  ext4: mballoc: refactor ext4_mb_discard_preallocations()
  ...
This commit is contained in:
Linus Torvalds 2020-06-05 16:19:28 -07:00
commit 0b166a57e6
47 changed files with 906 additions and 664 deletions

View File

@ -206,16 +206,18 @@ EINTR once fatal signal received.
Flag checking should be done at the beginning of the ->fiemap callback via the Flag checking should be done at the beginning of the ->fiemap callback via the
fiemap_check_flags() helper:: fiemap_prep() helper::
int fiemap_check_flags(struct fiemap_extent_info *fieinfo, u32 fs_flags); int fiemap_prep(struct inode *inode, struct fiemap_extent_info *fieinfo,
u64 start, u64 *len, u32 supported_flags);
The struct fieinfo should be passed in as received from ioctl_fiemap(). The The struct fieinfo should be passed in as received from ioctl_fiemap(). The
set of fiemap flags which the fs understands should be passed via fs_flags. If set of fiemap flags which the fs understands should be passed via fs_flags. If
fiemap_check_flags finds invalid user flags, it will place the bad values in fiemap_prep finds invalid user flags, it will place the bad values in
fieinfo->fi_flags and return -EBADR. If the file system gets -EBADR, from fieinfo->fi_flags and return -EBADR. If the file system gets -EBADR, from
fiemap_check_flags(), it should immediately exit, returning that error back to fiemap_prep(), it should immediately exit, returning that error back to
ioctl_fiemap(). ioctl_fiemap(). Additionally the range is validate against the supported
maximum file size.
For each extent in the request range, the file system should call For each extent in the request range, the file system should call

View File

@ -15,6 +15,7 @@
#include <linux/time.h> #include <linux/time.h>
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/fiemap.h>
static int bad_file_open(struct inode *inode, struct file *filp) static int bad_file_open(struct inode *inode, struct file *filp)
{ {

View File

@ -5,6 +5,7 @@
#include <linux/rbtree.h> #include <linux/rbtree.h>
#include <linux/refcount.h> #include <linux/refcount.h>
#include <linux/fiemap.h>
#include "ulist.h" #include "ulist.h"
/* /*

View File

@ -7828,14 +7828,12 @@ const struct iomap_dio_ops btrfs_dops = {
.submit_io = btrfs_submit_direct, .submit_io = btrfs_submit_direct,
}; };
#define BTRFS_FIEMAP_FLAGS (FIEMAP_FLAG_SYNC)
static int btrfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, static int btrfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
__u64 start, __u64 len) __u64 start, __u64 len)
{ {
int ret; int ret;
ret = fiemap_check_flags(fieinfo, BTRFS_FIEMAP_FLAGS); ret = fiemap_prep(inode, fieinfo, start, &len, 0);
if (ret) if (ret)
return ret; return ret;

View File

@ -25,6 +25,7 @@
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/sched/signal.h> #include <linux/sched/signal.h>
#include <linux/wait_bit.h> #include <linux/wait_bit.h>
#include <linux/fiemap.h>
#include <asm/div64.h> #include <asm/div64.h>
#include "cifsfs.h" #include "cifsfs.h"

View File

@ -12,6 +12,7 @@
#include <linux/uuid.h> #include <linux/uuid.h>
#include <linux/sort.h> #include <linux/sort.h>
#include <crypto/aead.h> #include <crypto/aead.h>
#include <linux/fiemap.h>
#include "cifsfs.h" #include "cifsfs.h"
#include "cifsglob.h" #include "cifsglob.h"
#include "smb2pdu.h" #include "smb2pdu.h"
@ -3407,8 +3408,9 @@ static int smb3_fiemap(struct cifs_tcon *tcon,
int i, num, rc, flags, last_blob; int i, num, rc, flags, last_blob;
u64 next; u64 next;
if (fiemap_check_flags(fei, FIEMAP_FLAG_SYNC)) rc = fiemap_prep(d_inode(cfile->dentry), fei, start, &len, 0);
return -EBADR; if (rc)
return rc;
xid = get_xid(); xid = get_xid();
again: again:

View File

@ -36,6 +36,7 @@
#include <linux/iomap.h> #include <linux/iomap.h>
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/uio.h> #include <linux/uio.h>
#include <linux/fiemap.h>
#include "ext2.h" #include "ext2.h"
#include "acl.h" #include "acl.h"
#include "xattr.h" #include "xattr.h"

View File

@ -99,8 +99,7 @@ config EXT4_DEBUG
Enables run-time debugging support for the ext4 filesystem. Enables run-time debugging support for the ext4 filesystem.
If you select Y here, then you will be able to turn on debugging If you select Y here, then you will be able to turn on debugging
with a command such as: using dynamic debug control for mb_debug() / ext_debug() msgs.
echo 1 > /sys/module/ext4/parameters/mballoc_debug
config EXT4_KUNIT_TESTS config EXT4_KUNIT_TESTS
tristate "KUnit tests for ext4" tristate "KUnit tests for ext4"

View File

@ -215,9 +215,8 @@ __ext4_set_acl(handle_t *handle, struct inode *inode, int type,
value, size, xattr_flags); value, size, xattr_flags);
kfree(value); kfree(value);
if (!error) { if (!error)
set_cached_acl(inode, type, acl); set_cached_acl(inode, type, acl);
}
return error; return error;
} }
@ -256,7 +255,7 @@ retry:
if (!error && update_mode) { if (!error && update_mode) {
inode->i_mode = mode; inode->i_mode = mode;
inode->i_ctime = current_time(inode); inode->i_ctime = current_time(inode);
ext4_mark_inode_dirty(handle, inode); error = ext4_mark_inode_dirty(handle, inode);
} }
out_stop: out_stop:
ext4_journal_stop(handle); ext4_journal_stop(handle);

View File

@ -903,10 +903,11 @@ ext4_fsblk_t ext4_inode_to_goal_block(struct inode *inode)
return bg_start; return bg_start;
if (bg_start + EXT4_BLOCKS_PER_GROUP(inode->i_sb) <= last_block) if (bg_start + EXT4_BLOCKS_PER_GROUP(inode->i_sb) <= last_block)
colour = (current->pid % 16) * colour = (task_pid_nr(current) % 16) *
(EXT4_BLOCKS_PER_GROUP(inode->i_sb) / 16); (EXT4_BLOCKS_PER_GROUP(inode->i_sb) / 16);
else else
colour = (current->pid % 16) * ((last_block - bg_start) / 16); colour = (task_pid_nr(current) % 16) *
((last_block - bg_start) / 16);
return bg_start + colour; return bg_start + colour;
} }

View File

@ -36,6 +36,7 @@
#include <crypto/hash.h> #include <crypto/hash.h>
#include <linux/falloc.h> #include <linux/falloc.h>
#include <linux/percpu-rwsem.h> #include <linux/percpu-rwsem.h>
#include <linux/fiemap.h>
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/compat.h> #include <linux/compat.h>
#endif #endif
@ -80,14 +81,22 @@
#define ext4_debug(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #define ext4_debug(fmt, ...) no_printk(fmt, ##__VA_ARGS__)
#endif #endif
/* /*
* Turn on EXT_DEBUG to get lots of info about extents operations. * Turn on EXT_DEBUG to enable ext4_ext_show_path/leaf/move in extents.c
*/ */
#define EXT_DEBUG__ #define EXT_DEBUG__
#ifdef EXT_DEBUG
#define ext_debug(fmt, ...) printk(fmt, ##__VA_ARGS__) /*
* Dynamic printk for controlled extents debugging.
*/
#ifdef CONFIG_EXT4_DEBUG
#define ext_debug(ino, fmt, ...) \
pr_debug("[%s/%d] EXT4-fs (%s): ino %lu: (%s, %d): %s:" fmt, \
current->comm, task_pid_nr(current), \
ino->i_sb->s_id, ino->i_ino, __FILE__, __LINE__, \
__func__, ##__VA_ARGS__)
#else #else
#define ext_debug(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #define ext_debug(ino, fmt, ...) no_printk(fmt, ##__VA_ARGS__)
#endif #endif
/* data type for block offset of block group */ /* data type for block offset of block group */
@ -142,6 +151,8 @@ enum SHIFT_DIRECTION {
#define EXT4_MB_USE_ROOT_BLOCKS 0x1000 #define EXT4_MB_USE_ROOT_BLOCKS 0x1000
/* Use blocks from reserved pool */ /* Use blocks from reserved pool */
#define EXT4_MB_USE_RESERVED 0x2000 #define EXT4_MB_USE_RESERVED 0x2000
/* Do strict check for free blocks while retrying block allocation */
#define EXT4_MB_STRICT_CHECK 0x4000
struct ext4_allocation_request { struct ext4_allocation_request {
/* target inode for block we're allocating */ /* target inode for block we're allocating */
@ -171,10 +182,10 @@ struct ext4_allocation_request {
* well as to store the information returned by ext4_map_blocks(). It * well as to store the information returned by ext4_map_blocks(). It
* takes less room on the stack than a struct buffer_head. * takes less room on the stack than a struct buffer_head.
*/ */
#define EXT4_MAP_NEW (1 << BH_New) #define EXT4_MAP_NEW BIT(BH_New)
#define EXT4_MAP_MAPPED (1 << BH_Mapped) #define EXT4_MAP_MAPPED BIT(BH_Mapped)
#define EXT4_MAP_UNWRITTEN (1 << BH_Unwritten) #define EXT4_MAP_UNWRITTEN BIT(BH_Unwritten)
#define EXT4_MAP_BOUNDARY (1 << BH_Boundary) #define EXT4_MAP_BOUNDARY BIT(BH_Boundary)
#define EXT4_MAP_FLAGS (EXT4_MAP_NEW | EXT4_MAP_MAPPED |\ #define EXT4_MAP_FLAGS (EXT4_MAP_NEW | EXT4_MAP_MAPPED |\
EXT4_MAP_UNWRITTEN | EXT4_MAP_BOUNDARY) EXT4_MAP_UNWRITTEN | EXT4_MAP_BOUNDARY)
@ -417,7 +428,7 @@ struct flex_groups {
/* 0x00400000 was formerly EXT4_EOFBLOCKS_FL */ /* 0x00400000 was formerly EXT4_EOFBLOCKS_FL */
#define EXT4_INLINE_DATA_FL 0x10000000 /* Inode has inline data. */ #define EXT4_INLINE_DATA_FL 0x10000000 /* Inode has inline data. */
#define EXT4_PROJINHERIT_FL 0x20000000 /* Create with parents projid */ #define EXT4_PROJINHERIT_FL 0x20000000 /* Create with parents projid */
#define EXT4_CASEFOLD_FL 0x40000000 /* Casefolded file */ #define EXT4_CASEFOLD_FL 0x40000000 /* Casefolded directory */
#define EXT4_RESERVED_FL 0x80000000 /* reserved for ext4 lib */ #define EXT4_RESERVED_FL 0x80000000 /* reserved for ext4 lib */
#define EXT4_FL_USER_VISIBLE 0x705BDFFF /* User visible flags */ #define EXT4_FL_USER_VISIBLE 0x705BDFFF /* User visible flags */
@ -490,6 +501,7 @@ enum {
/* 22 was formerly EXT4_INODE_EOFBLOCKS */ /* 22 was formerly EXT4_INODE_EOFBLOCKS */
EXT4_INODE_INLINE_DATA = 28, /* Data in inode. */ EXT4_INODE_INLINE_DATA = 28, /* Data in inode. */
EXT4_INODE_PROJINHERIT = 29, /* Create with parents projid */ EXT4_INODE_PROJINHERIT = 29, /* Create with parents projid */
EXT4_INODE_CASEFOLD = 30, /* Casefolded directory */
EXT4_INODE_RESERVED = 31, /* reserved for ext4 lib */ EXT4_INODE_RESERVED = 31, /* reserved for ext4 lib */
}; };
@ -535,6 +547,7 @@ static inline void ext4_check_flag_values(void)
CHECK_FLAG_VALUE(EA_INODE); CHECK_FLAG_VALUE(EA_INODE);
CHECK_FLAG_VALUE(INLINE_DATA); CHECK_FLAG_VALUE(INLINE_DATA);
CHECK_FLAG_VALUE(PROJINHERIT); CHECK_FLAG_VALUE(PROJINHERIT);
CHECK_FLAG_VALUE(CASEFOLD);
CHECK_FLAG_VALUE(RESERVED); CHECK_FLAG_VALUE(RESERVED);
} }
@ -609,8 +622,6 @@ enum {
#define EXT4_GET_BLOCKS_METADATA_NOFAIL 0x0020 #define EXT4_GET_BLOCKS_METADATA_NOFAIL 0x0020
/* Don't normalize allocation size (used for fallocate) */ /* Don't normalize allocation size (used for fallocate) */
#define EXT4_GET_BLOCKS_NO_NORMALIZE 0x0040 #define EXT4_GET_BLOCKS_NO_NORMALIZE 0x0040
/* Request will not result in inode size update (user for fallocate) */
#define EXT4_GET_BLOCKS_KEEP_SIZE 0x0080
/* Convert written extents to unwritten */ /* Convert written extents to unwritten */
#define EXT4_GET_BLOCKS_CONVERT_UNWRITTEN 0x0100 #define EXT4_GET_BLOCKS_CONVERT_UNWRITTEN 0x0100
/* Write zeros to newly created written extents */ /* Write zeros to newly created written extents */
@ -632,6 +643,7 @@ enum {
*/ */
#define EXT4_EX_NOCACHE 0x40000000 #define EXT4_EX_NOCACHE 0x40000000
#define EXT4_EX_FORCE_CACHE 0x20000000 #define EXT4_EX_FORCE_CACHE 0x20000000
#define EXT4_EX_NOFAIL 0x10000000
/* /*
* Flags used by ext4_free_blocks * Flags used by ext4_free_blocks
@ -2051,7 +2063,7 @@ struct ext4_dir_entry_2 {
__le32 inode; /* Inode number */ __le32 inode; /* Inode number */
__le16 rec_len; /* Directory entry length */ __le16 rec_len; /* Directory entry length */
__u8 name_len; /* Name length */ __u8 name_len; /* Name length */
__u8 file_type; __u8 file_type; /* See file type macros EXT4_FT_* below */
char name[EXT4_NAME_LEN]; /* File name */ char name[EXT4_NAME_LEN]; /* File name */
}; };
@ -3354,7 +3366,7 @@ struct ext4_extent;
*/ */
#define EXT_MAX_BLOCKS 0xffffffff #define EXT_MAX_BLOCKS 0xffffffff
extern int ext4_ext_tree_init(handle_t *handle, struct inode *); extern void ext4_ext_tree_init(handle_t *handle, struct inode *inode);
extern int ext4_ext_index_trans_blocks(struct inode *inode, int extents); extern int ext4_ext_index_trans_blocks(struct inode *inode, int extents);
extern int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, extern int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
struct ext4_map_blocks *map, int flags); struct ext4_map_blocks *map, int flags);

View File

@ -170,10 +170,13 @@ struct partial_cluster {
(EXT_FIRST_EXTENT((__hdr__)) + le16_to_cpu((__hdr__)->eh_entries) - 1) (EXT_FIRST_EXTENT((__hdr__)) + le16_to_cpu((__hdr__)->eh_entries) - 1)
#define EXT_LAST_INDEX(__hdr__) \ #define EXT_LAST_INDEX(__hdr__) \
(EXT_FIRST_INDEX((__hdr__)) + le16_to_cpu((__hdr__)->eh_entries) - 1) (EXT_FIRST_INDEX((__hdr__)) + le16_to_cpu((__hdr__)->eh_entries) - 1)
#define EXT_MAX_EXTENT(__hdr__) \ #define EXT_MAX_EXTENT(__hdr__) \
(EXT_FIRST_EXTENT((__hdr__)) + le16_to_cpu((__hdr__)->eh_max) - 1) ((le16_to_cpu((__hdr__)->eh_max)) ? \
((EXT_FIRST_EXTENT((__hdr__)) + le16_to_cpu((__hdr__)->eh_max) - 1)) \
: 0)
#define EXT_MAX_INDEX(__hdr__) \ #define EXT_MAX_INDEX(__hdr__) \
(EXT_FIRST_INDEX((__hdr__)) + le16_to_cpu((__hdr__)->eh_max) - 1) ((le16_to_cpu((__hdr__)->eh_max)) ? \
((EXT_FIRST_INDEX((__hdr__)) + le16_to_cpu((__hdr__)->eh_max) - 1)) : 0)
static inline struct ext4_extent_header *ext_inode_hdr(struct inode *inode) static inline struct ext4_extent_header *ext_inode_hdr(struct inode *inode)
{ {

View File

@ -222,7 +222,10 @@ ext4_mark_iloc_dirty(handle_t *handle,
int ext4_reserve_inode_write(handle_t *handle, struct inode *inode, int ext4_reserve_inode_write(handle_t *handle, struct inode *inode,
struct ext4_iloc *iloc); struct ext4_iloc *iloc);
int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode); #define ext4_mark_inode_dirty(__h, __i) \
__ext4_mark_inode_dirty((__h), (__i), __func__, __LINE__)
int __ext4_mark_inode_dirty(handle_t *handle, struct inode *inode,
const char *func, unsigned int line);
int ext4_expand_extra_isize(struct inode *inode, int ext4_expand_extra_isize(struct inode *inode,
unsigned int new_extra_isize, unsigned int new_extra_isize,
@ -335,12 +338,6 @@ static inline handle_t *__ext4_journal_start(struct inode *inode,
handle_t *__ext4_journal_start_reserved(handle_t *handle, unsigned int line, handle_t *__ext4_journal_start_reserved(handle_t *handle, unsigned int line,
int type); int type);
static inline void ext4_journal_free_reserved(handle_t *handle)
{
if (ext4_handle_valid(handle))
jbd2_journal_free_reserved(handle);
}
static inline handle_t *ext4_journal_current_handle(void) static inline handle_t *ext4_journal_current_handle(void)
{ {
return journal_current_handle(); return journal_current_handle();

File diff suppressed because it is too large Load Diff

View File

@ -1054,7 +1054,7 @@ static void count_rsvd(struct inode *inode, ext4_lblk_t lblk, long len,
end = (end > ext4_es_end(es)) ? ext4_es_end(es) : end; end = (end > ext4_es_end(es)) ? ext4_es_end(es) : end;
/* record the first block of the first delonly extent seen */ /* record the first block of the first delonly extent seen */
if (rc->first_do_lblk_found == false) { if (!rc->first_do_lblk_found) {
rc->first_do_lblk = i; rc->first_do_lblk = i;
rc->first_do_lblk_found = true; rc->first_do_lblk_found = true;
} }

View File

@ -287,6 +287,7 @@ static ssize_t ext4_handle_inode_extension(struct inode *inode, loff_t offset,
bool truncate = false; bool truncate = false;
u8 blkbits = inode->i_blkbits; u8 blkbits = inode->i_blkbits;
ext4_lblk_t written_blk, end_blk; ext4_lblk_t written_blk, end_blk;
int ret;
/* /*
* Note that EXT4_I(inode)->i_disksize can get extended up to * Note that EXT4_I(inode)->i_disksize can get extended up to
@ -327,8 +328,14 @@ static ssize_t ext4_handle_inode_extension(struct inode *inode, loff_t offset,
goto truncate; goto truncate;
} }
if (ext4_update_inode_size(inode, offset + written)) if (ext4_update_inode_size(inode, offset + written)) {
ext4_mark_inode_dirty(handle, inode); ret = ext4_mark_inode_dirty(handle, inode);
if (unlikely(ret)) {
written = ret;
ext4_journal_stop(handle);
goto truncate;
}
}
/* /*
* We may need to truncate allocated but not written blocks beyond EOF. * We may need to truncate allocated but not written blocks beyond EOF.
@ -495,6 +502,12 @@ static ssize_t ext4_dio_write_iter(struct kiocb *iocb, struct iov_iter *from)
if (ret <= 0) if (ret <= 0)
return ret; return ret;
/* if we're going to block and IOCB_NOWAIT is set, return -EAGAIN */
if ((iocb->ki_flags & IOCB_NOWAIT) && (unaligned_io || extend)) {
ret = -EAGAIN;
goto out;
}
offset = iocb->ki_pos; offset = iocb->ki_pos;
count = ret; count = ret;

View File

@ -44,30 +44,28 @@
*/ */
static int ext4_sync_parent(struct inode *inode) static int ext4_sync_parent(struct inode *inode)
{ {
struct dentry *dentry = NULL; struct dentry *dentry, *next;
struct inode *next;
int ret = 0; int ret = 0;
if (!ext4_test_inode_state(inode, EXT4_STATE_NEWENTRY)) if (!ext4_test_inode_state(inode, EXT4_STATE_NEWENTRY))
return 0; return 0;
inode = igrab(inode); dentry = d_find_any_alias(inode);
if (!dentry)
return 0;
while (ext4_test_inode_state(inode, EXT4_STATE_NEWENTRY)) { while (ext4_test_inode_state(inode, EXT4_STATE_NEWENTRY)) {
ext4_clear_inode_state(inode, EXT4_STATE_NEWENTRY); ext4_clear_inode_state(inode, EXT4_STATE_NEWENTRY);
dentry = d_find_any_alias(inode);
if (!dentry) next = dget_parent(dentry);
break;
next = igrab(d_inode(dentry->d_parent));
dput(dentry); dput(dentry);
if (!next) dentry = next;
break; inode = dentry->d_inode;
iput(inode);
inode = next;
/* /*
* The directory inode may have gone through rmdir by now. But * The directory inode may have gone through rmdir by now. But
* the inode itself and its blocks are still allocated (we hold * the inode itself and its blocks are still allocated (we hold
* a reference to the inode so it didn't go through * a reference to the inode via its dentry), so it didn't go
* ext4_evict_inode()) and so we are safe to flush metadata * through ext4_evict_inode()) and so we are safe to flush
* blocks and the inode. * metadata blocks and the inode.
*/ */
ret = sync_mapping_buffers(inode->i_mapping); ret = sync_mapping_buffers(inode->i_mapping);
if (ret) if (ret)
@ -76,7 +74,7 @@ static int ext4_sync_parent(struct inode *inode)
if (ret) if (ret)
break; break;
} }
iput(inode); dput(dentry);
return ret; return ret;
} }

View File

@ -1246,6 +1246,7 @@ struct inode *ext4_orphan_get(struct super_block *sb, unsigned long ino)
ext4_error_err(sb, -err, ext4_error_err(sb, -err,
"couldn't read orphan inode %lu (err %d)", "couldn't read orphan inode %lu (err %d)",
ino, err); ino, err);
brelse(bitmap_bh);
return inode; return inode;
} }

View File

@ -467,7 +467,9 @@ static int ext4_splice_branch(handle_t *handle,
/* /*
* OK, we spliced it into the inode itself on a direct block. * OK, we spliced it into the inode itself on a direct block.
*/ */
ext4_mark_inode_dirty(handle, ar->inode); err = ext4_mark_inode_dirty(handle, ar->inode);
if (unlikely(err))
goto err_out;
jbd_debug(5, "splicing direct\n"); jbd_debug(5, "splicing direct\n");
} }
return err; return err;

View File

@ -1260,7 +1260,7 @@ out:
int ext4_try_add_inline_entry(handle_t *handle, struct ext4_filename *fname, int ext4_try_add_inline_entry(handle_t *handle, struct ext4_filename *fname,
struct inode *dir, struct inode *inode) struct inode *dir, struct inode *inode)
{ {
int ret, inline_size, no_expand; int ret, ret2, inline_size, no_expand;
void *inline_start; void *inline_start;
struct ext4_iloc iloc; struct ext4_iloc iloc;
@ -1314,7 +1314,9 @@ int ext4_try_add_inline_entry(handle_t *handle, struct ext4_filename *fname,
out: out:
ext4_write_unlock_xattr(dir, &no_expand); ext4_write_unlock_xattr(dir, &no_expand);
ext4_mark_inode_dirty(handle, dir); ret2 = ext4_mark_inode_dirty(handle, dir);
if (unlikely(ret2 && !ret))
ret = ret2;
brelse(iloc.bh); brelse(iloc.bh);
return ret; return ret;
} }

View File

@ -220,6 +220,16 @@ void ext4_evict_inode(struct inode *inode)
ext4_begin_ordered_truncate(inode, 0); ext4_begin_ordered_truncate(inode, 0);
truncate_inode_pages_final(&inode->i_data); truncate_inode_pages_final(&inode->i_data);
/*
* For inodes with journalled data, transaction commit could have
* dirtied the inode. Flush worker is ignoring it because of I_FREEING
* flag but we still need to remove the inode from the writeback lists.
*/
if (!list_empty_careful(&inode->i_io_list)) {
WARN_ON_ONCE(!ext4_should_journal_data(inode));
inode_io_list_del(inode);
}
/* /*
* Protect us against freezing - iput() caller didn't have to have any * Protect us against freezing - iput() caller didn't have to have any
* protection against it * protection against it
@ -432,11 +442,9 @@ static void ext4_map_blocks_es_recheck(handle_t *handle,
*/ */
down_read(&EXT4_I(inode)->i_data_sem); down_read(&EXT4_I(inode)->i_data_sem);
if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) { if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
retval = ext4_ext_map_blocks(handle, inode, map, flags & retval = ext4_ext_map_blocks(handle, inode, map, 0);
EXT4_GET_BLOCKS_KEEP_SIZE);
} else { } else {
retval = ext4_ind_map_blocks(handle, inode, map, flags & retval = ext4_ind_map_blocks(handle, inode, map, 0);
EXT4_GET_BLOCKS_KEEP_SIZE);
} }
up_read((&EXT4_I(inode)->i_data_sem)); up_read((&EXT4_I(inode)->i_data_sem));
@ -493,9 +501,8 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
#endif #endif
map->m_flags = 0; map->m_flags = 0;
ext_debug("ext4_map_blocks(): inode %lu, flag %d, max_blocks %u," ext_debug(inode, "flag 0x%x, max_blocks %u, logical block %lu\n",
"logical block %lu\n", inode->i_ino, flags, map->m_len, flags, map->m_len, (unsigned long) map->m_lblk);
(unsigned long) map->m_lblk);
/* /*
* ext4_map_blocks returns an int, and m_len is an unsigned int * ext4_map_blocks returns an int, and m_len is an unsigned int
@ -541,11 +548,9 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
*/ */
down_read(&EXT4_I(inode)->i_data_sem); down_read(&EXT4_I(inode)->i_data_sem);
if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) { if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
retval = ext4_ext_map_blocks(handle, inode, map, flags & retval = ext4_ext_map_blocks(handle, inode, map, 0);
EXT4_GET_BLOCKS_KEEP_SIZE);
} else { } else {
retval = ext4_ind_map_blocks(handle, inode, map, flags & retval = ext4_ind_map_blocks(handle, inode, map, 0);
EXT4_GET_BLOCKS_KEEP_SIZE);
} }
if (retval > 0) { if (retval > 0) {
unsigned int status; unsigned int status;
@ -726,6 +731,9 @@ out_sem:
return ret; return ret;
} }
} }
if (retval < 0)
ext_debug(inode, "failed with err %d\n", retval);
return retval; return retval;
} }
@ -1296,7 +1304,7 @@ static int ext4_write_end(struct file *file,
* filesystems. * filesystems.
*/ */
if (i_size_changed || inline_data) if (i_size_changed || inline_data)
ext4_mark_inode_dirty(handle, inode); ret = ext4_mark_inode_dirty(handle, inode);
if (pos + len > inode->i_size && !verity && ext4_can_truncate(inode)) if (pos + len > inode->i_size && !verity && ext4_can_truncate(inode))
/* if we have allocated more blocks and copied /* if we have allocated more blocks and copied
@ -1526,6 +1534,7 @@ struct mpage_da_data {
struct ext4_map_blocks map; struct ext4_map_blocks map;
struct ext4_io_submit io_submit; /* IO submission data */ struct ext4_io_submit io_submit; /* IO submission data */
unsigned int do_map:1; unsigned int do_map:1;
unsigned int scanned_until_end:1;
}; };
static void mpage_release_unused_pages(struct mpage_da_data *mpd, static void mpage_release_unused_pages(struct mpage_da_data *mpd,
@ -1541,6 +1550,7 @@ static void mpage_release_unused_pages(struct mpage_da_data *mpd,
if (mpd->first_page >= mpd->next_page) if (mpd->first_page >= mpd->next_page)
return; return;
mpd->scanned_until_end = 0;
index = mpd->first_page; index = mpd->first_page;
end = mpd->next_page - 1; end = mpd->next_page - 1;
if (invalidate) { if (invalidate) {
@ -1681,8 +1691,7 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock,
invalid_block = ~0; invalid_block = ~0;
map->m_flags = 0; map->m_flags = 0;
ext_debug("ext4_da_map_blocks(): inode %lu, max_blocks %u," ext_debug(inode, "max_blocks %u, logical block %lu\n", map->m_len,
"logical block %lu\n", inode->i_ino, map->m_len,
(unsigned long) map->m_lblk); (unsigned long) map->m_lblk);
/* Lookup extent status tree firstly */ /* Lookup extent status tree firstly */
@ -2078,7 +2087,7 @@ static int mpage_submit_page(struct mpage_da_data *mpd, struct page *page)
return err; return err;
} }
#define BH_FLAGS ((1 << BH_Unwritten) | (1 << BH_Delay)) #define BH_FLAGS (BIT(BH_Unwritten) | BIT(BH_Delay))
/* /*
* mballoc gives us at most this number of blocks... * mballoc gives us at most this number of blocks...
@ -2188,7 +2197,11 @@ static int mpage_process_page_bufs(struct mpage_da_data *mpd,
if (err < 0) if (err < 0)
return err; return err;
} }
return lblk < blocks; if (lblk >= blocks) {
mpd->scanned_until_end = 1;
return 0;
}
return 1;
} }
/* /*
@ -2311,7 +2324,7 @@ static int mpage_map_and_submit_buffers(struct mpage_da_data *mpd)
* mapping, or maybe the page was submitted for IO. * mapping, or maybe the page was submitted for IO.
* So we return to call further extent mapping. * So we return to call further extent mapping.
*/ */
if (err < 0 || map_bh == true) if (err < 0 || map_bh)
goto out; goto out;
/* Page fully mapped - let IO run! */ /* Page fully mapped - let IO run! */
err = mpage_submit_page(mpd, page); err = mpage_submit_page(mpd, page);
@ -2358,7 +2371,7 @@ static int mpage_map_one_extent(handle_t *handle, struct mpage_da_data *mpd)
dioread_nolock = ext4_should_dioread_nolock(inode); dioread_nolock = ext4_should_dioread_nolock(inode);
if (dioread_nolock) if (dioread_nolock)
get_blocks_flags |= EXT4_GET_BLOCKS_IO_CREATE_EXT; get_blocks_flags |= EXT4_GET_BLOCKS_IO_CREATE_EXT;
if (map->m_flags & (1 << BH_Delay)) if (map->m_flags & BIT(BH_Delay))
get_blocks_flags |= EXT4_GET_BLOCKS_DELALLOC_RESERVE; get_blocks_flags |= EXT4_GET_BLOCKS_DELALLOC_RESERVE;
err = ext4_map_blocks(handle, inode, map, get_blocks_flags); err = ext4_map_blocks(handle, inode, map, get_blocks_flags);
@ -2546,7 +2559,7 @@ static int mpage_prepare_extent_to_map(struct mpage_da_data *mpd)
nr_pages = pagevec_lookup_range_tag(&pvec, mapping, &index, end, nr_pages = pagevec_lookup_range_tag(&pvec, mapping, &index, end,
tag); tag);
if (nr_pages == 0) if (nr_pages == 0)
goto out; break;
for (i = 0; i < nr_pages; i++) { for (i = 0; i < nr_pages; i++) {
struct page *page = pvec.pages[i]; struct page *page = pvec.pages[i];
@ -2601,6 +2614,7 @@ static int mpage_prepare_extent_to_map(struct mpage_da_data *mpd)
pagevec_release(&pvec); pagevec_release(&pvec);
cond_resched(); cond_resched();
} }
mpd->scanned_until_end = 1;
return 0; return 0;
out: out:
pagevec_release(&pvec); pagevec_release(&pvec);
@ -2619,7 +2633,6 @@ static int ext4_writepages(struct address_space *mapping,
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
int needed_blocks, rsv_blocks = 0, ret = 0; int needed_blocks, rsv_blocks = 0, ret = 0;
struct ext4_sb_info *sbi = EXT4_SB(mapping->host->i_sb); struct ext4_sb_info *sbi = EXT4_SB(mapping->host->i_sb);
bool done;
struct blk_plug plug; struct blk_plug plug;
bool give_up_on_write = false; bool give_up_on_write = false;
@ -2705,7 +2718,6 @@ static int ext4_writepages(struct address_space *mapping,
retry: retry:
if (wbc->sync_mode == WB_SYNC_ALL || wbc->tagged_writepages) if (wbc->sync_mode == WB_SYNC_ALL || wbc->tagged_writepages)
tag_pages_for_writeback(mapping, mpd.first_page, mpd.last_page); tag_pages_for_writeback(mapping, mpd.first_page, mpd.last_page);
done = false;
blk_start_plug(&plug); blk_start_plug(&plug);
/* /*
@ -2715,6 +2727,7 @@ retry:
* started. * started.
*/ */
mpd.do_map = 0; mpd.do_map = 0;
mpd.scanned_until_end = 0;
mpd.io_submit.io_end = ext4_init_io_end(inode, GFP_KERNEL); mpd.io_submit.io_end = ext4_init_io_end(inode, GFP_KERNEL);
if (!mpd.io_submit.io_end) { if (!mpd.io_submit.io_end) {
ret = -ENOMEM; ret = -ENOMEM;
@ -2730,7 +2743,7 @@ retry:
if (ret < 0) if (ret < 0)
goto unplug; goto unplug;
while (!done && mpd.first_page <= mpd.last_page) { while (!mpd.scanned_until_end && wbc->nr_to_write > 0) {
/* For each extent of pages we use new io_end */ /* For each extent of pages we use new io_end */
mpd.io_submit.io_end = ext4_init_io_end(inode, GFP_KERNEL); mpd.io_submit.io_end = ext4_init_io_end(inode, GFP_KERNEL);
if (!mpd.io_submit.io_end) { if (!mpd.io_submit.io_end) {
@ -2765,20 +2778,9 @@ retry:
trace_ext4_da_write_pages(inode, mpd.first_page, mpd.wbc); trace_ext4_da_write_pages(inode, mpd.first_page, mpd.wbc);
ret = mpage_prepare_extent_to_map(&mpd); ret = mpage_prepare_extent_to_map(&mpd);
if (!ret) { if (!ret && mpd.map.m_len)
if (mpd.map.m_len) ret = mpage_map_and_submit_extent(handle, &mpd,
ret = mpage_map_and_submit_extent(handle, &mpd,
&give_up_on_write); &give_up_on_write);
else {
/*
* We scanned the whole range (or exhausted
* nr_to_write), submitted what was mapped and
* didn't find anything needing mapping. We are
* done.
*/
done = true;
}
}
/* /*
* Caution: If the handle is synchronous, * Caution: If the handle is synchronous,
* ext4_journal_stop() can wait for transaction commit * ext4_journal_stop() can wait for transaction commit
@ -3077,7 +3079,7 @@ static int ext4_da_write_end(struct file *file,
* new_i_size is less that inode->i_size * new_i_size is less that inode->i_size
* bu greater than i_disksize.(hint delalloc) * bu greater than i_disksize.(hint delalloc)
*/ */
ext4_mark_inode_dirty(handle, inode); ret = ext4_mark_inode_dirty(handle, inode);
} }
} }
@ -3094,7 +3096,7 @@ static int ext4_da_write_end(struct file *file,
if (ret2 < 0) if (ret2 < 0)
ret = ret2; ret = ret2;
ret2 = ext4_journal_stop(handle); ret2 = ext4_journal_stop(handle);
if (!ret) if (unlikely(ret2 && !ret))
ret = ret2; ret = ret2;
return ret ? ret : copied; return ret ? ret : copied;
@ -3883,6 +3885,8 @@ int ext4_update_disksize_before_punch(struct inode *inode, loff_t offset,
loff_t len) loff_t len)
{ {
handle_t *handle; handle_t *handle;
int ret;
loff_t size = i_size_read(inode); loff_t size = i_size_read(inode);
WARN_ON(!inode_is_locked(inode)); WARN_ON(!inode_is_locked(inode));
@ -3896,10 +3900,10 @@ int ext4_update_disksize_before_punch(struct inode *inode, loff_t offset,
if (IS_ERR(handle)) if (IS_ERR(handle))
return PTR_ERR(handle); return PTR_ERR(handle);
ext4_update_i_disksize(inode, size); ext4_update_i_disksize(inode, size);
ext4_mark_inode_dirty(handle, inode); ret = ext4_mark_inode_dirty(handle, inode);
ext4_journal_stop(handle); ext4_journal_stop(handle);
return 0; return ret;
} }
static void ext4_wait_dax_page(struct ext4_inode_info *ei) static void ext4_wait_dax_page(struct ext4_inode_info *ei)
@ -3951,7 +3955,7 @@ int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length)
loff_t first_block_offset, last_block_offset; loff_t first_block_offset, last_block_offset;
handle_t *handle; handle_t *handle;
unsigned int credits; unsigned int credits;
int ret = 0; int ret = 0, ret2 = 0;
trace_ext4_punch_hole(inode, offset, length, 0); trace_ext4_punch_hole(inode, offset, length, 0);
@ -4074,7 +4078,9 @@ int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length)
ext4_handle_sync(handle); ext4_handle_sync(handle);
inode->i_mtime = inode->i_ctime = current_time(inode); inode->i_mtime = inode->i_ctime = current_time(inode);
ext4_mark_inode_dirty(handle, inode); ret2 = ext4_mark_inode_dirty(handle, inode);
if (unlikely(ret2))
ret = ret2;
if (ret >= 0) if (ret >= 0)
ext4_update_inode_fsync_trans(handle, inode, 1); ext4_update_inode_fsync_trans(handle, inode, 1);
out_stop: out_stop:
@ -4143,7 +4149,7 @@ int ext4_truncate(struct inode *inode)
{ {
struct ext4_inode_info *ei = EXT4_I(inode); struct ext4_inode_info *ei = EXT4_I(inode);
unsigned int credits; unsigned int credits;
int err = 0; int err = 0, err2;
handle_t *handle; handle_t *handle;
struct address_space *mapping = inode->i_mapping; struct address_space *mapping = inode->i_mapping;
@ -4231,7 +4237,9 @@ out_stop:
ext4_orphan_del(handle, inode); ext4_orphan_del(handle, inode);
inode->i_mtime = inode->i_ctime = current_time(inode); inode->i_mtime = inode->i_ctime = current_time(inode);
ext4_mark_inode_dirty(handle, inode); err2 = ext4_mark_inode_dirty(handle, inode);
if (unlikely(err2 && !err))
err = err2;
ext4_journal_stop(handle); ext4_journal_stop(handle);
trace_ext4_truncate_exit(inode); trace_ext4_truncate_exit(inode);
@ -5289,6 +5297,8 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
inode->i_gid = attr->ia_gid; inode->i_gid = attr->ia_gid;
error = ext4_mark_inode_dirty(handle, inode); error = ext4_mark_inode_dirty(handle, inode);
ext4_journal_stop(handle); ext4_journal_stop(handle);
if (unlikely(error))
return error;
} }
if (attr->ia_valid & ATTR_SIZE) { if (attr->ia_valid & ATTR_SIZE) {
@ -5774,7 +5784,8 @@ out_unlock:
* Whenever the user wants stuff synced (sys_sync, sys_msync, sys_fsync) * Whenever the user wants stuff synced (sys_sync, sys_msync, sys_fsync)
* we start and wait on commits. * we start and wait on commits.
*/ */
int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode) int __ext4_mark_inode_dirty(handle_t *handle, struct inode *inode,
const char *func, unsigned int line)
{ {
struct ext4_iloc iloc; struct ext4_iloc iloc;
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
@ -5784,13 +5795,18 @@ int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode)
trace_ext4_mark_inode_dirty(inode, _RET_IP_); trace_ext4_mark_inode_dirty(inode, _RET_IP_);
err = ext4_reserve_inode_write(handle, inode, &iloc); err = ext4_reserve_inode_write(handle, inode, &iloc);
if (err) if (err)
return err; goto out;
if (EXT4_I(inode)->i_extra_isize < sbi->s_want_extra_isize) if (EXT4_I(inode)->i_extra_isize < sbi->s_want_extra_isize)
ext4_try_to_expand_extra_isize(inode, sbi->s_want_extra_isize, ext4_try_to_expand_extra_isize(inode, sbi->s_want_extra_isize,
iloc, handle); iloc, handle);
return ext4_mark_iloc_dirty(handle, inode, &iloc); err = ext4_mark_iloc_dirty(handle, inode, &iloc);
out:
if (unlikely(err))
ext4_error_inode_err(inode, func, line, 0, err,
"mark_inode_dirty error");
return err;
} }
/* /*

View File

@ -754,14 +754,6 @@ static int ext4_ioctl_get_es_cache(struct file *filp, unsigned long arg)
fieinfo.fi_extents_max = fiemap.fm_extent_count; fieinfo.fi_extents_max = fiemap.fm_extent_count;
fieinfo.fi_extents_start = ufiemap->fm_extents; fieinfo.fi_extents_start = ufiemap->fm_extents;
if (fiemap.fm_extent_count != 0 &&
!access_ok(fieinfo.fi_extents_start,
fieinfo.fi_extents_max * sizeof(struct fiemap_extent)))
return -EFAULT;
if (fieinfo.fi_flags & FIEMAP_FLAG_SYNC)
filemap_write_and_wait(inode->i_mapping);
error = ext4_get_es_cache(inode, &fieinfo, fiemap.fm_start, error = ext4_get_es_cache(inode, &fieinfo, fiemap.fm_start,
fiemap.fm_length); fiemap.fm_length);
fiemap.fm_flags = fieinfo.fi_flags; fiemap.fm_flags = fieinfo.fi_flags;

File diff suppressed because it is too large Load Diff

View File

@ -24,19 +24,15 @@
#include "ext4.h" #include "ext4.h"
/* /*
* mb_debug() dynamic printk msgs could be used to debug mballoc code.
*/ */
#ifdef CONFIG_EXT4_DEBUG #ifdef CONFIG_EXT4_DEBUG
extern ushort ext4_mballoc_debug; #define mb_debug(sb, fmt, ...) \
pr_debug("[%s/%d] EXT4-fs (%s): (%s, %d): %s: " fmt, \
#define mb_debug(n, fmt, ...) \ current->comm, task_pid_nr(current), sb->s_id, \
do { \ __FILE__, __LINE__, __func__, ##__VA_ARGS__)
if ((n) <= ext4_mballoc_debug) { \
printk(KERN_DEBUG "(%s, %d): %s: " fmt, \
__FILE__, __LINE__, __func__, ##__VA_ARGS__); \
} \
} while (0)
#else #else
#define mb_debug(n, fmt, ...) no_printk(fmt, ##__VA_ARGS__) #define mb_debug(sb, fmt, ...) no_printk(fmt, ##__VA_ARGS__)
#endif #endif
#define EXT4_MB_HISTORY_ALLOC 1 /* allocation */ #define EXT4_MB_HISTORY_ALLOC 1 /* allocation */

View File

@ -287,7 +287,7 @@ static int free_ind_block(handle_t *handle, struct inode *inode, __le32 *i_data)
static int ext4_ext_swap_inode_data(handle_t *handle, struct inode *inode, static int ext4_ext_swap_inode_data(handle_t *handle, struct inode *inode,
struct inode *tmp_inode) struct inode *tmp_inode)
{ {
int retval; int retval, retval2 = 0;
__le32 i_data[3]; __le32 i_data[3];
struct ext4_inode_info *ei = EXT4_I(inode); struct ext4_inode_info *ei = EXT4_I(inode);
struct ext4_inode_info *tmp_ei = EXT4_I(tmp_inode); struct ext4_inode_info *tmp_ei = EXT4_I(tmp_inode);
@ -342,7 +342,9 @@ static int ext4_ext_swap_inode_data(handle_t *handle, struct inode *inode,
* i_blocks when freeing the indirect meta-data blocks * i_blocks when freeing the indirect meta-data blocks
*/ */
retval = free_ind_block(handle, inode, i_data); retval = free_ind_block(handle, inode, i_data);
ext4_mark_inode_dirty(handle, inode); retval2 = ext4_mark_inode_dirty(handle, inode);
if (unlikely(retval2 && !retval))
retval = retval2;
err_out: err_out:
return retval; return retval;
@ -601,7 +603,7 @@ int ext4_ind_migrate(struct inode *inode)
ext4_lblk_t start, end; ext4_lblk_t start, end;
ext4_fsblk_t blk; ext4_fsblk_t blk;
handle_t *handle; handle_t *handle;
int ret; int ret, ret2 = 0;
if (!ext4_has_feature_extents(inode->i_sb) || if (!ext4_has_feature_extents(inode->i_sb) ||
(!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) (!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)))
@ -655,7 +657,9 @@ int ext4_ind_migrate(struct inode *inode)
memset(ei->i_data, 0, sizeof(ei->i_data)); memset(ei->i_data, 0, sizeof(ei->i_data));
for (i = start; i <= end; i++) for (i = start; i <= end; i++)
ei->i_data[i] = cpu_to_le32(blk++); ei->i_data[i] = cpu_to_le32(blk++);
ext4_mark_inode_dirty(handle, inode); ret2 = ext4_mark_inode_dirty(handle, inode);
if (unlikely(ret2 && !ret))
ret = ret2;
errout: errout:
ext4_journal_stop(handle); ext4_journal_stop(handle);
up_write(&EXT4_I(inode)->i_data_sem); up_write(&EXT4_I(inode)->i_data_sem);

View File

@ -1993,7 +1993,7 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname,
{ {
unsigned int blocksize = dir->i_sb->s_blocksize; unsigned int blocksize = dir->i_sb->s_blocksize;
int csum_size = 0; int csum_size = 0;
int err; int err, err2;
if (ext4_has_metadata_csum(inode->i_sb)) if (ext4_has_metadata_csum(inode->i_sb))
csum_size = sizeof(struct ext4_dir_entry_tail); csum_size = sizeof(struct ext4_dir_entry_tail);
@ -2028,12 +2028,12 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname,
dir->i_mtime = dir->i_ctime = current_time(dir); dir->i_mtime = dir->i_ctime = current_time(dir);
ext4_update_dx_flag(dir); ext4_update_dx_flag(dir);
inode_inc_iversion(dir); inode_inc_iversion(dir);
ext4_mark_inode_dirty(handle, dir); err2 = ext4_mark_inode_dirty(handle, dir);
BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata"); BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
err = ext4_handle_dirty_dirblock(handle, dir, bh); err = ext4_handle_dirty_dirblock(handle, dir, bh);
if (err) if (err)
ext4_std_error(dir->i_sb, err); ext4_std_error(dir->i_sb, err);
return 0; return err ? err : err2;
} }
/* /*
@ -2223,7 +2223,9 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
} }
ext4_clear_inode_flag(dir, EXT4_INODE_INDEX); ext4_clear_inode_flag(dir, EXT4_INODE_INDEX);
dx_fallback++; dx_fallback++;
ext4_mark_inode_dirty(handle, dir); retval = ext4_mark_inode_dirty(handle, dir);
if (unlikely(retval))
goto out;
} }
blocks = dir->i_size >> sb->s_blocksize_bits; blocks = dir->i_size >> sb->s_blocksize_bits;
for (block = 0; block < blocks; block++) { for (block = 0; block < blocks; block++) {
@ -2576,12 +2578,12 @@ static int ext4_add_nondir(handle_t *handle,
struct inode *inode = *inodep; struct inode *inode = *inodep;
int err = ext4_add_entry(handle, dentry, inode); int err = ext4_add_entry(handle, dentry, inode);
if (!err) { if (!err) {
ext4_mark_inode_dirty(handle, inode); err = ext4_mark_inode_dirty(handle, inode);
if (IS_DIRSYNC(dir)) if (IS_DIRSYNC(dir))
ext4_handle_sync(handle); ext4_handle_sync(handle);
d_instantiate_new(dentry, inode); d_instantiate_new(dentry, inode);
*inodep = NULL; *inodep = NULL;
return 0; return err;
} }
drop_nlink(inode); drop_nlink(inode);
ext4_orphan_add(handle, inode); ext4_orphan_add(handle, inode);
@ -2775,7 +2777,7 @@ static int ext4_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
{ {
handle_t *handle; handle_t *handle;
struct inode *inode; struct inode *inode;
int err, credits, retries = 0; int err, err2 = 0, credits, retries = 0;
if (EXT4_DIR_LINK_MAX(dir)) if (EXT4_DIR_LINK_MAX(dir))
return -EMLINK; return -EMLINK;
@ -2808,7 +2810,9 @@ out_clear_inode:
clear_nlink(inode); clear_nlink(inode);
ext4_orphan_add(handle, inode); ext4_orphan_add(handle, inode);
unlock_new_inode(inode); unlock_new_inode(inode);
ext4_mark_inode_dirty(handle, inode); err2 = ext4_mark_inode_dirty(handle, inode);
if (unlikely(err2))
err = err2;
ext4_journal_stop(handle); ext4_journal_stop(handle);
iput(inode); iput(inode);
goto out_retry; goto out_retry;
@ -3148,10 +3152,12 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry)
inode->i_size = 0; inode->i_size = 0;
ext4_orphan_add(handle, inode); ext4_orphan_add(handle, inode);
inode->i_ctime = dir->i_ctime = dir->i_mtime = current_time(inode); inode->i_ctime = dir->i_ctime = dir->i_mtime = current_time(inode);
ext4_mark_inode_dirty(handle, inode); retval = ext4_mark_inode_dirty(handle, inode);
if (retval)
goto end_rmdir;
ext4_dec_count(handle, dir); ext4_dec_count(handle, dir);
ext4_update_dx_flag(dir); ext4_update_dx_flag(dir);
ext4_mark_inode_dirty(handle, dir); retval = ext4_mark_inode_dirty(handle, dir);
#ifdef CONFIG_UNICODE #ifdef CONFIG_UNICODE
/* VFS negative dentries are incompatible with Encoding and /* VFS negative dentries are incompatible with Encoding and
@ -3221,7 +3227,9 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry)
goto end_unlink; goto end_unlink;
dir->i_ctime = dir->i_mtime = current_time(dir); dir->i_ctime = dir->i_mtime = current_time(dir);
ext4_update_dx_flag(dir); ext4_update_dx_flag(dir);
ext4_mark_inode_dirty(handle, dir); retval = ext4_mark_inode_dirty(handle, dir);
if (retval)
goto end_unlink;
if (inode->i_nlink == 0) if (inode->i_nlink == 0)
ext4_warning_inode(inode, "Deleting file '%.*s' with no links", ext4_warning_inode(inode, "Deleting file '%.*s' with no links",
dentry->d_name.len, dentry->d_name.name); dentry->d_name.len, dentry->d_name.name);
@ -3230,7 +3238,7 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry)
if (!inode->i_nlink) if (!inode->i_nlink)
ext4_orphan_add(handle, inode); ext4_orphan_add(handle, inode);
inode->i_ctime = current_time(inode); inode->i_ctime = current_time(inode);
ext4_mark_inode_dirty(handle, inode); retval = ext4_mark_inode_dirty(handle, inode);
#ifdef CONFIG_UNICODE #ifdef CONFIG_UNICODE
/* VFS negative dentries are incompatible with Encoding and /* VFS negative dentries are incompatible with Encoding and
@ -3419,7 +3427,7 @@ retry:
err = ext4_add_entry(handle, dentry, inode); err = ext4_add_entry(handle, dentry, inode);
if (!err) { if (!err) {
ext4_mark_inode_dirty(handle, inode); err = ext4_mark_inode_dirty(handle, inode);
/* this can happen only for tmpfile being /* this can happen only for tmpfile being
* linked the first time * linked the first time
*/ */
@ -3531,7 +3539,7 @@ static int ext4_rename_dir_finish(handle_t *handle, struct ext4_renament *ent,
static int ext4_setent(handle_t *handle, struct ext4_renament *ent, static int ext4_setent(handle_t *handle, struct ext4_renament *ent,
unsigned ino, unsigned file_type) unsigned ino, unsigned file_type)
{ {
int retval; int retval, retval2;
BUFFER_TRACE(ent->bh, "get write access"); BUFFER_TRACE(ent->bh, "get write access");
retval = ext4_journal_get_write_access(handle, ent->bh); retval = ext4_journal_get_write_access(handle, ent->bh);
@ -3543,19 +3551,19 @@ static int ext4_setent(handle_t *handle, struct ext4_renament *ent,
inode_inc_iversion(ent->dir); inode_inc_iversion(ent->dir);
ent->dir->i_ctime = ent->dir->i_mtime = ent->dir->i_ctime = ent->dir->i_mtime =
current_time(ent->dir); current_time(ent->dir);
ext4_mark_inode_dirty(handle, ent->dir); retval = ext4_mark_inode_dirty(handle, ent->dir);
BUFFER_TRACE(ent->bh, "call ext4_handle_dirty_metadata"); BUFFER_TRACE(ent->bh, "call ext4_handle_dirty_metadata");
if (!ent->inlined) { if (!ent->inlined) {
retval = ext4_handle_dirty_dirblock(handle, ent->dir, ent->bh); retval2 = ext4_handle_dirty_dirblock(handle, ent->dir, ent->bh);
if (unlikely(retval)) { if (unlikely(retval2)) {
ext4_std_error(ent->dir->i_sb, retval); ext4_std_error(ent->dir->i_sb, retval2);
return retval; return retval2;
} }
} }
brelse(ent->bh); brelse(ent->bh);
ent->bh = NULL; ent->bh = NULL;
return 0; return retval;
} }
static int ext4_find_delete_entry(handle_t *handle, struct inode *dir, static int ext4_find_delete_entry(handle_t *handle, struct inode *dir,
@ -3790,7 +3798,9 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
EXT4_FT_CHRDEV); EXT4_FT_CHRDEV);
if (retval) if (retval)
goto end_rename; goto end_rename;
ext4_mark_inode_dirty(handle, whiteout); retval = ext4_mark_inode_dirty(handle, whiteout);
if (unlikely(retval))
goto end_rename;
} }
if (!new.bh) { if (!new.bh) {
retval = ext4_add_entry(handle, new.dentry, old.inode); retval = ext4_add_entry(handle, new.dentry, old.inode);
@ -3811,7 +3821,9 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
* rename. * rename.
*/ */
old.inode->i_ctime = current_time(old.inode); old.inode->i_ctime = current_time(old.inode);
ext4_mark_inode_dirty(handle, old.inode); retval = ext4_mark_inode_dirty(handle, old.inode);
if (unlikely(retval))
goto end_rename;
if (!whiteout) { if (!whiteout) {
/* /*
@ -3840,12 +3852,18 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
} else { } else {
ext4_inc_count(handle, new.dir); ext4_inc_count(handle, new.dir);
ext4_update_dx_flag(new.dir); ext4_update_dx_flag(new.dir);
ext4_mark_inode_dirty(handle, new.dir); retval = ext4_mark_inode_dirty(handle, new.dir);
if (unlikely(retval))
goto end_rename;
} }
} }
ext4_mark_inode_dirty(handle, old.dir); retval = ext4_mark_inode_dirty(handle, old.dir);
if (unlikely(retval))
goto end_rename;
if (new.inode) { if (new.inode) {
ext4_mark_inode_dirty(handle, new.inode); retval = ext4_mark_inode_dirty(handle, new.inode);
if (unlikely(retval))
goto end_rename;
if (!new.inode->i_nlink) if (!new.inode->i_nlink)
ext4_orphan_add(handle, new.inode); ext4_orphan_add(handle, new.inode);
} }
@ -3979,8 +3997,12 @@ static int ext4_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
ctime = current_time(old.inode); ctime = current_time(old.inode);
old.inode->i_ctime = ctime; old.inode->i_ctime = ctime;
new.inode->i_ctime = ctime; new.inode->i_ctime = ctime;
ext4_mark_inode_dirty(handle, old.inode); retval = ext4_mark_inode_dirty(handle, old.inode);
ext4_mark_inode_dirty(handle, new.inode); if (unlikely(retval))
goto end_rename;
retval = ext4_mark_inode_dirty(handle, new.inode);
if (unlikely(retval))
goto end_rename;
if (old.dir_bh) { if (old.dir_bh) {
retval = ext4_rename_dir_finish(handle, &old, new.dir->i_ino); retval = ext4_rename_dir_finish(handle, &old, new.dir->i_ino);

View File

@ -3718,7 +3718,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
int blocksize, clustersize; int blocksize, clustersize;
unsigned int db_count; unsigned int db_count;
unsigned int i; unsigned int i;
int needs_recovery, has_huge_files, has_bigalloc; int needs_recovery, has_huge_files;
__u64 blocks_count; __u64 blocks_count;
int err = 0; int err = 0;
unsigned int journal_ioprio = DEFAULT_JOURNAL_IOPRIO; unsigned int journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
@ -4010,17 +4010,13 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA) { if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA) {
printk_once(KERN_WARNING "EXT4-fs: Warning: mounting with data=journal disables delayed allocation, dioread_nolock, and O_DIRECT support!\n"); printk_once(KERN_WARNING "EXT4-fs: Warning: mounting with data=journal disables delayed allocation, dioread_nolock, and O_DIRECT support!\n");
/* can't mount with both data=journal and dioread_nolock. */
clear_opt(sb, DIOREAD_NOLOCK); clear_opt(sb, DIOREAD_NOLOCK);
if (test_opt2(sb, EXPLICIT_DELALLOC)) { if (test_opt2(sb, EXPLICIT_DELALLOC)) {
ext4_msg(sb, KERN_ERR, "can't mount with " ext4_msg(sb, KERN_ERR, "can't mount with "
"both data=journal and delalloc"); "both data=journal and delalloc");
goto failed_mount; goto failed_mount;
} }
if (test_opt(sb, DIOREAD_NOLOCK)) {
ext4_msg(sb, KERN_ERR, "can't mount with "
"both data=journal and dioread_nolock");
goto failed_mount;
}
if (test_opt(sb, DAX)) { if (test_opt(sb, DAX)) {
ext4_msg(sb, KERN_ERR, "can't mount with " ext4_msg(sb, KERN_ERR, "can't mount with "
"both data=journal and dax"); "both data=journal and dax");
@ -4237,8 +4233,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
/* Handle clustersize */ /* Handle clustersize */
clustersize = BLOCK_SIZE << le32_to_cpu(es->s_log_cluster_size); clustersize = BLOCK_SIZE << le32_to_cpu(es->s_log_cluster_size);
has_bigalloc = ext4_has_feature_bigalloc(sb); if (ext4_has_feature_bigalloc(sb)) {
if (has_bigalloc) {
if (clustersize < blocksize) { if (clustersize < blocksize) {
ext4_msg(sb, KERN_ERR, ext4_msg(sb, KERN_ERR,
"cluster size (%d) smaller than " "cluster size (%d) smaller than "
@ -5925,7 +5920,7 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id,
EXT4_I(inode)->i_flags |= EXT4_NOATIME_FL | EXT4_IMMUTABLE_FL; EXT4_I(inode)->i_flags |= EXT4_NOATIME_FL | EXT4_IMMUTABLE_FL;
inode_set_flags(inode, S_NOATIME | S_IMMUTABLE, inode_set_flags(inode, S_NOATIME | S_IMMUTABLE,
S_NOATIME | S_IMMUTABLE); S_NOATIME | S_IMMUTABLE);
ext4_mark_inode_dirty(handle, inode); err = ext4_mark_inode_dirty(handle, inode);
ext4_journal_stop(handle); ext4_journal_stop(handle);
unlock_inode: unlock_inode:
inode_unlock(inode); inode_unlock(inode);
@ -6027,12 +6022,14 @@ static int ext4_quota_off(struct super_block *sb, int type)
* this is not a hard failure and quotas are already disabled. * this is not a hard failure and quotas are already disabled.
*/ */
handle = ext4_journal_start(inode, EXT4_HT_QUOTA, 1); handle = ext4_journal_start(inode, EXT4_HT_QUOTA, 1);
if (IS_ERR(handle)) if (IS_ERR(handle)) {
err = PTR_ERR(handle);
goto out_unlock; goto out_unlock;
}
EXT4_I(inode)->i_flags &= ~(EXT4_NOATIME_FL | EXT4_IMMUTABLE_FL); EXT4_I(inode)->i_flags &= ~(EXT4_NOATIME_FL | EXT4_IMMUTABLE_FL);
inode_set_flags(inode, 0, S_NOATIME | S_IMMUTABLE); inode_set_flags(inode, 0, S_NOATIME | S_IMMUTABLE);
inode->i_mtime = inode->i_ctime = current_time(inode); inode->i_mtime = inode->i_ctime = current_time(inode);
ext4_mark_inode_dirty(handle, inode); err = ext4_mark_inode_dirty(handle, inode);
ext4_journal_stop(handle); ext4_journal_stop(handle);
out_unlock: out_unlock:
inode_unlock(inode); inode_unlock(inode);
@ -6090,7 +6087,7 @@ static ssize_t ext4_quota_write(struct super_block *sb, int type,
{ {
struct inode *inode = sb_dqopt(sb)->files[type]; struct inode *inode = sb_dqopt(sb)->files[type];
ext4_lblk_t blk = off >> EXT4_BLOCK_SIZE_BITS(sb); ext4_lblk_t blk = off >> EXT4_BLOCK_SIZE_BITS(sb);
int err, offset = off & (sb->s_blocksize - 1); int err = 0, err2 = 0, offset = off & (sb->s_blocksize - 1);
int retries = 0; int retries = 0;
struct buffer_head *bh; struct buffer_head *bh;
handle_t *handle = journal_current_handle(); handle_t *handle = journal_current_handle();
@ -6138,9 +6135,11 @@ out:
if (inode->i_size < off + len) { if (inode->i_size < off + len) {
i_size_write(inode, off + len); i_size_write(inode, off + len);
EXT4_I(inode)->i_disksize = inode->i_size; EXT4_I(inode)->i_disksize = inode->i_size;
ext4_mark_inode_dirty(handle, inode); err2 = ext4_mark_inode_dirty(handle, inode);
if (unlikely(err2 && !err))
err = err2;
} }
return len; return err ? err : len;
} }
#endif #endif

View File

@ -1327,7 +1327,7 @@ static int ext4_xattr_inode_write(handle_t *handle, struct inode *ea_inode,
int blocksize = ea_inode->i_sb->s_blocksize; int blocksize = ea_inode->i_sb->s_blocksize;
int max_blocks = (bufsize + blocksize - 1) >> ea_inode->i_blkbits; int max_blocks = (bufsize + blocksize - 1) >> ea_inode->i_blkbits;
int csize, wsize = 0; int csize, wsize = 0;
int ret = 0; int ret = 0, ret2 = 0;
int retries = 0; int retries = 0;
retry: retry:
@ -1385,7 +1385,9 @@ retry:
ext4_update_i_disksize(ea_inode, wsize); ext4_update_i_disksize(ea_inode, wsize);
inode_unlock(ea_inode); inode_unlock(ea_inode);
ext4_mark_inode_dirty(handle, ea_inode); ret2 = ext4_mark_inode_dirty(handle, ea_inode);
if (unlikely(ret2 && !ret))
ret = ret2;
out: out:
brelse(bh); brelse(bh);
@ -1800,8 +1802,11 @@ ext4_xattr_block_find(struct inode *inode, struct ext4_xattr_info *i,
if (EXT4_I(inode)->i_file_acl) { if (EXT4_I(inode)->i_file_acl) {
/* The inode already has an extended attribute block. */ /* The inode already has an extended attribute block. */
bs->bh = ext4_sb_bread(sb, EXT4_I(inode)->i_file_acl, REQ_PRIO); bs->bh = ext4_sb_bread(sb, EXT4_I(inode)->i_file_acl, REQ_PRIO);
if (IS_ERR(bs->bh)) if (IS_ERR(bs->bh)) {
return PTR_ERR(bs->bh); error = PTR_ERR(bs->bh);
bs->bh = NULL;
return error;
}
ea_bdebug(bs->bh, "b_count=%d, refcount=%d", ea_bdebug(bs->bh, "b_count=%d, refcount=%d",
atomic_read(&(bs->bh->b_count)), atomic_read(&(bs->bh->b_count)),
le32_to_cpu(BHDR(bs->bh)->h_refcount)); le32_to_cpu(BHDR(bs->bh)->h_refcount));

View File

@ -19,6 +19,7 @@
#include <linux/uio.h> #include <linux/uio.h>
#include <linux/cleancache.h> #include <linux/cleancache.h>
#include <linux/sched/signal.h> #include <linux/sched/signal.h>
#include <linux/fiemap.h>
#include "f2fs.h" #include "f2fs.h"
#include "node.h" #include "node.h"
@ -1824,7 +1825,7 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
return ret; return ret;
} }
ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR); ret = fiemap_prep(inode, fieinfo, start, &len, FIEMAP_FLAG_XATTR);
if (ret) if (ret)
return ret; return ret;

View File

@ -8,6 +8,7 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/f2fs_fs.h> #include <linux/f2fs_fs.h>
#include <linux/fiemap.h>
#include "f2fs.h" #include "f2fs.h"
#include "node.h" #include "node.h"

View File

@ -1125,6 +1125,7 @@ void inode_io_list_del(struct inode *inode)
inode_io_list_del_locked(inode, wb); inode_io_list_del_locked(inode, wb);
spin_unlock(&wb->list_lock); spin_unlock(&wb->list_lock);
} }
EXPORT_SYMBOL(inode_io_list_del);
/* /*
* mark an inode as under writeback on the sb * mark an inode as under writeback on the sb

View File

@ -17,6 +17,7 @@
#include <linux/crc32.h> #include <linux/crc32.h>
#include <linux/iomap.h> #include <linux/iomap.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/fiemap.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include "gfs2.h" #include "gfs2.h"

View File

@ -9,6 +9,7 @@
#include "hpfs_fn.h" #include "hpfs_fn.h"
#include <linux/mpage.h> #include <linux/mpage.h>
#include <linux/fiemap.h>
#define BLOCKS(size) (((size) + 511) >> 9) #define BLOCKS(size) (((size) + 511) >> 9)

View File

@ -142,8 +142,6 @@ extern int dentry_needs_remove_privs(struct dentry *dentry);
/* /*
* fs-writeback.c * fs-writeback.c
*/ */
extern void inode_io_list_del(struct inode *inode);
extern long get_nr_dirty_inodes(void); extern long get_nr_dirty_inodes(void);
extern int invalidate_inodes(struct super_block *, bool); extern int invalidate_inodes(struct super_block *, bool);

View File

@ -18,6 +18,7 @@
#include <linux/buffer_head.h> #include <linux/buffer_head.h>
#include <linux/falloc.h> #include <linux/falloc.h>
#include <linux/sched/signal.h> #include <linux/sched/signal.h>
#include <linux/fiemap.h>
#include "internal.h" #include "internal.h"
@ -148,61 +149,55 @@ int fiemap_fill_next_extent(struct fiemap_extent_info *fieinfo, u64 logical,
EXPORT_SYMBOL(fiemap_fill_next_extent); EXPORT_SYMBOL(fiemap_fill_next_extent);
/** /**
* fiemap_check_flags - check validity of requested flags for fiemap * fiemap_prep - check validity of requested flags for fiemap
* @inode: Inode to operate on
* @fieinfo: Fiemap context passed into ->fiemap * @fieinfo: Fiemap context passed into ->fiemap
* @fs_flags: Set of fiemap flags that the file system understands * @start: Start of the mapped range
* @len: Length of the mapped range, can be truncated by this function.
* @supported_flags: Set of fiemap flags that the file system understands
* *
* Called from file system ->fiemap callback. This will compute the * This function must be called from each ->fiemap instance to validate the
* intersection of valid fiemap flags and those that the fs supports. That * fiemap request against the file system parameters.
* value is then compared against the user supplied flags. In case of bad user
* flags, the invalid values will be written into the fieinfo structure, and
* -EBADR is returned, which tells ioctl_fiemap() to return those values to
* userspace. For this reason, a return code of -EBADR should be preserved.
* *
* Returns 0 on success, -EBADR on bad flags. * Returns 0 on success, or a negative error on failure.
*/ */
int fiemap_check_flags(struct fiemap_extent_info *fieinfo, u32 fs_flags) int fiemap_prep(struct inode *inode, struct fiemap_extent_info *fieinfo,
u64 start, u64 *len, u32 supported_flags)
{ {
u64 maxbytes = inode->i_sb->s_maxbytes;
u32 incompat_flags; u32 incompat_flags;
int ret = 0;
incompat_flags = fieinfo->fi_flags & ~(FIEMAP_FLAGS_COMPAT & fs_flags); if (*len == 0)
if (incompat_flags) {
fieinfo->fi_flags = incompat_flags;
return -EBADR;
}
return 0;
}
EXPORT_SYMBOL(fiemap_check_flags);
static int fiemap_check_ranges(struct super_block *sb,
u64 start, u64 len, u64 *new_len)
{
u64 maxbytes = (u64) sb->s_maxbytes;
*new_len = len;
if (len == 0)
return -EINVAL; return -EINVAL;
if (start > maxbytes) if (start > maxbytes)
return -EFBIG; return -EFBIG;
/* /*
* Shrink request scope to what the fs can actually handle. * Shrink request scope to what the fs can actually handle.
*/ */
if (len > maxbytes || (maxbytes - len) < start) if (*len > maxbytes || (maxbytes - *len) < start)
*new_len = maxbytes - start; *len = maxbytes - start;
return 0; supported_flags |= FIEMAP_FLAG_SYNC;
supported_flags &= FIEMAP_FLAGS_COMPAT;
incompat_flags = fieinfo->fi_flags & ~supported_flags;
if (incompat_flags) {
fieinfo->fi_flags = incompat_flags;
return -EBADR;
}
if (fieinfo->fi_flags & FIEMAP_FLAG_SYNC)
ret = filemap_write_and_wait(inode->i_mapping);
return ret;
} }
EXPORT_SYMBOL(fiemap_prep);
static int ioctl_fiemap(struct file *filp, struct fiemap __user *ufiemap) static int ioctl_fiemap(struct file *filp, struct fiemap __user *ufiemap)
{ {
struct fiemap fiemap; struct fiemap fiemap;
struct fiemap_extent_info fieinfo = { 0, }; struct fiemap_extent_info fieinfo = { 0, };
struct inode *inode = file_inode(filp); struct inode *inode = file_inode(filp);
struct super_block *sb = inode->i_sb;
u64 len;
int error; int error;
if (!inode->i_op->fiemap) if (!inode->i_op->fiemap)
@ -214,24 +209,13 @@ static int ioctl_fiemap(struct file *filp, struct fiemap __user *ufiemap)
if (fiemap.fm_extent_count > FIEMAP_MAX_EXTENTS) if (fiemap.fm_extent_count > FIEMAP_MAX_EXTENTS)
return -EINVAL; return -EINVAL;
error = fiemap_check_ranges(sb, fiemap.fm_start, fiemap.fm_length,
&len);
if (error)
return error;
fieinfo.fi_flags = fiemap.fm_flags; fieinfo.fi_flags = fiemap.fm_flags;
fieinfo.fi_extents_max = fiemap.fm_extent_count; fieinfo.fi_extents_max = fiemap.fm_extent_count;
fieinfo.fi_extents_start = ufiemap->fm_extents; fieinfo.fi_extents_start = ufiemap->fm_extents;
if (fiemap.fm_extent_count != 0 && error = inode->i_op->fiemap(inode, &fieinfo, fiemap.fm_start,
!access_ok(fieinfo.fi_extents_start, fiemap.fm_length);
fieinfo.fi_extents_max * sizeof(struct fiemap_extent)))
return -EFAULT;
if (fieinfo.fi_flags & FIEMAP_FLAG_SYNC)
filemap_write_and_wait(inode->i_mapping);
error = inode->i_op->fiemap(inode, &fieinfo, fiemap.fm_start, len);
fiemap.fm_flags = fieinfo.fi_flags; fiemap.fm_flags = fieinfo.fi_flags;
fiemap.fm_mapped_extents = fieinfo.fi_extents_mapped; fiemap.fm_mapped_extents = fieinfo.fi_extents_mapped;
if (copy_to_user(ufiemap, &fiemap, sizeof(fiemap))) if (copy_to_user(ufiemap, &fiemap, sizeof(fiemap)))
@ -307,8 +291,7 @@ static inline loff_t blk_to_logical(struct inode *inode, sector_t blk)
* If you use this function directly, you need to do your own locking. Use * If you use this function directly, you need to do your own locking. Use
* generic_block_fiemap if you want the locking done for you. * generic_block_fiemap if you want the locking done for you.
*/ */
static int __generic_block_fiemap(struct inode *inode,
int __generic_block_fiemap(struct inode *inode,
struct fiemap_extent_info *fieinfo, loff_t start, struct fiemap_extent_info *fieinfo, loff_t start,
loff_t len, get_block_t *get_block) loff_t len, get_block_t *get_block)
{ {
@ -320,7 +303,7 @@ int __generic_block_fiemap(struct inode *inode,
bool past_eof = false, whole_file = false; bool past_eof = false, whole_file = false;
int ret = 0; int ret = 0;
ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC); ret = fiemap_prep(inode, fieinfo, start, &len, FIEMAP_FLAG_SYNC);
if (ret) if (ret)
return ret; return ret;
@ -453,7 +436,6 @@ int __generic_block_fiemap(struct inode *inode,
return ret; return ret;
} }
EXPORT_SYMBOL(__generic_block_fiemap);
/** /**
* generic_block_fiemap - FIEMAP for block based inodes * generic_block_fiemap - FIEMAP for block based inodes

View File

@ -6,6 +6,7 @@
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/iomap.h> #include <linux/iomap.h>
#include <linux/fiemap.h>
struct fiemap_ctx { struct fiemap_ctx {
struct fiemap_extent_info *fi; struct fiemap_extent_info *fi;
@ -65,7 +66,7 @@ iomap_fiemap_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
} }
int iomap_fiemap(struct inode *inode, struct fiemap_extent_info *fi, int iomap_fiemap(struct inode *inode, struct fiemap_extent_info *fi,
loff_t start, loff_t len, const struct iomap_ops *ops) u64 start, u64 len, const struct iomap_ops *ops)
{ {
struct fiemap_ctx ctx; struct fiemap_ctx ctx;
loff_t ret; loff_t ret;
@ -74,16 +75,10 @@ int iomap_fiemap(struct inode *inode, struct fiemap_extent_info *fi,
ctx.fi = fi; ctx.fi = fi;
ctx.prev.type = IOMAP_HOLE; ctx.prev.type = IOMAP_HOLE;
ret = fiemap_check_flags(fi, FIEMAP_FLAG_SYNC); ret = fiemap_prep(inode, fi, start, &len, 0);
if (ret) if (ret)
return ret; return ret;
if (fi->fi_flags & FIEMAP_FLAG_SYNC) {
ret = filemap_write_and_wait(inode->i_mapping);
if (ret)
return ret;
}
while (len > 0) { while (len > 0) {
ret = iomap_apply(inode, start, len, IOMAP_REPORT, ops, &ctx, ret = iomap_apply(inode, start, len, IOMAP_REPORT, ops, &ctx,
iomap_fiemap_actor); iomap_fiemap_actor);

View File

@ -541,17 +541,24 @@ handle_t *jbd2_journal_start(journal_t *journal, int nblocks)
} }
EXPORT_SYMBOL(jbd2_journal_start); EXPORT_SYMBOL(jbd2_journal_start);
static void __jbd2_journal_unreserve_handle(handle_t *handle) static void __jbd2_journal_unreserve_handle(handle_t *handle, transaction_t *t)
{ {
journal_t *journal = handle->h_journal; journal_t *journal = handle->h_journal;
WARN_ON(!handle->h_reserved); WARN_ON(!handle->h_reserved);
sub_reserved_credits(journal, handle->h_total_credits); sub_reserved_credits(journal, handle->h_total_credits);
if (t)
atomic_sub(handle->h_total_credits, &t->t_outstanding_credits);
} }
void jbd2_journal_free_reserved(handle_t *handle) void jbd2_journal_free_reserved(handle_t *handle)
{ {
__jbd2_journal_unreserve_handle(handle); journal_t *journal = handle->h_journal;
/* Get j_state_lock to pin running transaction if it exists */
read_lock(&journal->j_state_lock);
__jbd2_journal_unreserve_handle(handle, journal->j_running_transaction);
read_unlock(&journal->j_state_lock);
jbd2_free_handle(handle); jbd2_free_handle(handle);
} }
EXPORT_SYMBOL(jbd2_journal_free_reserved); EXPORT_SYMBOL(jbd2_journal_free_reserved);
@ -722,7 +729,8 @@ static void stop_this_handle(handle_t *handle)
atomic_sub(handle->h_total_credits, atomic_sub(handle->h_total_credits,
&transaction->t_outstanding_credits); &transaction->t_outstanding_credits);
if (handle->h_rsv_handle) if (handle->h_rsv_handle)
__jbd2_journal_unreserve_handle(handle->h_rsv_handle); __jbd2_journal_unreserve_handle(handle->h_rsv_handle,
transaction);
if (atomic_dec_and_test(&transaction->t_updates)) if (atomic_dec_and_test(&transaction->t_updates))
wake_up(&journal->j_wait_updates); wake_up(&journal->j_wait_updates);

View File

@ -14,6 +14,7 @@
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/writeback.h> #include <linux/writeback.h>
#include <linux/uio.h> #include <linux/uio.h>
#include <linux/fiemap.h>
#include "nilfs.h" #include "nilfs.h"
#include "btnode.h" #include "btnode.h"
#include "segment.h" #include "segment.h"
@ -996,7 +997,7 @@ int nilfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
unsigned int blkbits = inode->i_blkbits; unsigned int blkbits = inode->i_blkbits;
int ret, n; int ret, n;
ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC); ret = fiemap_prep(inode, fieinfo, start, &len, 0);
if (ret) if (ret)
return ret; return ret;

View File

@ -733,8 +733,6 @@ static int ocfs2_fiemap_inline(struct inode *inode, struct buffer_head *di_bh,
return 0; return 0;
} }
#define OCFS2_FIEMAP_FLAGS (FIEMAP_FLAG_SYNC)
int ocfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, int ocfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
u64 map_start, u64 map_len) u64 map_start, u64 map_len)
{ {
@ -746,7 +744,7 @@ int ocfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
struct buffer_head *di_bh = NULL; struct buffer_head *di_bh = NULL;
struct ocfs2_extent_rec rec; struct ocfs2_extent_rec rec;
ret = fiemap_check_flags(fieinfo, OCFS2_FIEMAP_FLAGS); ret = fiemap_prep(inode, fieinfo, map_start, &map_len, 0);
if (ret) if (ret)
return ret; return ret;

View File

@ -10,6 +10,7 @@
#include <linux/xattr.h> #include <linux/xattr.h>
#include <linux/posix_acl.h> #include <linux/posix_acl.h>
#include <linux/ratelimit.h> #include <linux/ratelimit.h>
#include <linux/fiemap.h>
#include "overlayfs.h" #include "overlayfs.h"
@ -479,10 +480,6 @@ static int ovl_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
return -EOPNOTSUPP; return -EOPNOTSUPP;
old_cred = ovl_override_creds(inode->i_sb); old_cred = ovl_override_creds(inode->i_sb);
if (fieinfo->fi_flags & FIEMAP_FLAG_SYNC)
filemap_write_and_wait(realinode->i_mapping);
err = realinode->i_op->fiemap(realinode, fieinfo, start, len); err = realinode->i_op->fiemap(realinode, fieinfo, start, len);
revert_creds(old_cred); revert_creds(old_cred);

View File

@ -25,6 +25,7 @@
#include <linux/posix_acl.h> #include <linux/posix_acl.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/iversion.h> #include <linux/iversion.h>
#include <linux/fiemap.h>
/* /*
* Directories have different lock order w.r.t. mmap_sem compared to regular * Directories have different lock order w.r.t. mmap_sem compared to regular

25
include/linux/fiemap.h Normal file
View File

@ -0,0 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_FIEMAP_H
#define _LINUX_FIEMAP_H 1
#include <uapi/linux/fiemap.h>
#include <linux/fs.h>
struct fiemap_extent_info {
unsigned int fi_flags; /* Flags as passed from user */
unsigned int fi_extents_mapped; /* Number of mapped extents */
unsigned int fi_extents_max; /* Size of fiemap_extent array */
struct fiemap_extent __user *fi_extents_start; /* Start of
fiemap_extent array */
};
int fiemap_prep(struct inode *inode, struct fiemap_extent_info *fieinfo,
u64 start, u64 *len, u32 supported_flags);
int fiemap_fill_next_extent(struct fiemap_extent_info *info, u64 logical,
u64 phys, u64 len, u32 flags);
int generic_block_fiemap(struct inode *inode,
struct fiemap_extent_info *fieinfo, u64 start, u64 len,
get_block_t *get_block);
#endif /* _LINUX_FIEMAP_H 1 */

View File

@ -24,7 +24,6 @@
#include <linux/capability.h> #include <linux/capability.h>
#include <linux/semaphore.h> #include <linux/semaphore.h>
#include <linux/fcntl.h> #include <linux/fcntl.h>
#include <linux/fiemap.h>
#include <linux/rculist_bl.h> #include <linux/rculist_bl.h>
#include <linux/atomic.h> #include <linux/atomic.h>
#include <linux/shrinker.h> #include <linux/shrinker.h>
@ -48,6 +47,7 @@ struct backing_dev_info;
struct bdi_writeback; struct bdi_writeback;
struct bio; struct bio;
struct export_operations; struct export_operations;
struct fiemap_extent_info;
struct hd_geometry; struct hd_geometry;
struct iovec; struct iovec;
struct kiocb; struct kiocb;
@ -1755,19 +1755,6 @@ extern long compat_ptr_ioctl(struct file *file, unsigned int cmd,
extern void inode_init_owner(struct inode *inode, const struct inode *dir, extern void inode_init_owner(struct inode *inode, const struct inode *dir,
umode_t mode); umode_t mode);
extern bool may_open_dev(const struct path *path); extern bool may_open_dev(const struct path *path);
/*
* VFS FS_IOC_FIEMAP helper definitions.
*/
struct fiemap_extent_info {
unsigned int fi_flags; /* Flags as passed from user */
unsigned int fi_extents_mapped; /* Number of mapped extents */
unsigned int fi_extents_max; /* Size of fiemap_extent array */
struct fiemap_extent __user *fi_extents_start; /* Start of
fiemap_extent array */
};
int fiemap_fill_next_extent(struct fiemap_extent_info *info, u64 logical,
u64 phys, u64 len, u32 flags);
int fiemap_check_flags(struct fiemap_extent_info *fieinfo, u32 fs_flags);
/* /*
* This is the "filldir" function type, used by readdir() to let * This is the "filldir" function type, used by readdir() to let
@ -3323,14 +3310,6 @@ static inline int vfs_fstat(int fd, struct kstat *stat)
extern const char *vfs_get_link(struct dentry *, struct delayed_call *); extern const char *vfs_get_link(struct dentry *, struct delayed_call *);
extern int vfs_readlink(struct dentry *, char __user *, int); extern int vfs_readlink(struct dentry *, char __user *, int);
extern int __generic_block_fiemap(struct inode *inode,
struct fiemap_extent_info *fieinfo,
loff_t start, loff_t len,
get_block_t *get_block);
extern int generic_block_fiemap(struct inode *inode,
struct fiemap_extent_info *fieinfo, u64 start,
u64 len, get_block_t *get_block);
extern struct file_system_type *get_filesystem(struct file_system_type *fs); extern struct file_system_type *get_filesystem(struct file_system_type *fs);
extern void put_filesystem(struct file_system_type *fs); extern void put_filesystem(struct file_system_type *fs);
extern struct file_system_type *get_fs_type(const char *name); extern struct file_system_type *get_fs_type(const char *name);

View File

@ -177,7 +177,7 @@ int iomap_truncate_page(struct inode *inode, loff_t pos, bool *did_zero,
vm_fault_t iomap_page_mkwrite(struct vm_fault *vmf, vm_fault_t iomap_page_mkwrite(struct vm_fault *vmf,
const struct iomap_ops *ops); const struct iomap_ops *ops);
int iomap_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, int iomap_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
loff_t start, loff_t len, const struct iomap_ops *ops); u64 start, u64 len, const struct iomap_ops *ops);
loff_t iomap_seek_hole(struct inode *inode, loff_t offset, loff_t iomap_seek_hole(struct inode *inode, loff_t offset,
const struct iomap_ops *ops); const struct iomap_ops *ops);
loff_t iomap_seek_data(struct inode *inode, loff_t offset, loff_t iomap_seek_data(struct inode *inode, loff_t offset,

View File

@ -197,6 +197,7 @@ void wakeup_flusher_threads(enum wb_reason reason);
void wakeup_flusher_threads_bdi(struct backing_dev_info *bdi, void wakeup_flusher_threads_bdi(struct backing_dev_info *bdi,
enum wb_reason reason); enum wb_reason reason);
void inode_wait_for_writeback(struct inode *inode); void inode_wait_for_writeback(struct inode *inode);
void inode_io_list_del(struct inode *inode);
/* writeback.h requires fs.h; it, too, is not included from here. */ /* writeback.h requires fs.h; it, too, is not included from here. */
static inline void wait_on_inode(struct inode *inode) static inline void wait_on_inode(struct inode *inode)

View File

@ -35,7 +35,8 @@ struct partial_cluster;
{ EXT4_MB_DELALLOC_RESERVED, "DELALLOC_RESV" }, \ { EXT4_MB_DELALLOC_RESERVED, "DELALLOC_RESV" }, \
{ EXT4_MB_STREAM_ALLOC, "STREAM_ALLOC" }, \ { EXT4_MB_STREAM_ALLOC, "STREAM_ALLOC" }, \
{ EXT4_MB_USE_ROOT_BLOCKS, "USE_ROOT_BLKS" }, \ { EXT4_MB_USE_ROOT_BLOCKS, "USE_ROOT_BLKS" }, \
{ EXT4_MB_USE_RESERVED, "USE_RESV" }) { EXT4_MB_USE_RESERVED, "USE_RESV" }, \
{ EXT4_MB_STRICT_CHECK, "STRICT_CHECK" })
#define show_map_flags(flags) __print_flags(flags, "|", \ #define show_map_flags(flags) __print_flags(flags, "|", \
{ EXT4_GET_BLOCKS_CREATE, "CREATE" }, \ { EXT4_GET_BLOCKS_CREATE, "CREATE" }, \
@ -45,8 +46,10 @@ struct partial_cluster;
{ EXT4_GET_BLOCKS_CONVERT, "CONVERT" }, \ { EXT4_GET_BLOCKS_CONVERT, "CONVERT" }, \
{ EXT4_GET_BLOCKS_METADATA_NOFAIL, "METADATA_NOFAIL" }, \ { EXT4_GET_BLOCKS_METADATA_NOFAIL, "METADATA_NOFAIL" }, \
{ EXT4_GET_BLOCKS_NO_NORMALIZE, "NO_NORMALIZE" }, \ { EXT4_GET_BLOCKS_NO_NORMALIZE, "NO_NORMALIZE" }, \
{ EXT4_GET_BLOCKS_KEEP_SIZE, "KEEP_SIZE" }, \ { EXT4_GET_BLOCKS_CONVERT_UNWRITTEN, "CONVERT_UNWRITTEN" }, \
{ EXT4_GET_BLOCKS_ZERO, "ZERO" }) { EXT4_GET_BLOCKS_ZERO, "ZERO" }, \
{ EXT4_GET_BLOCKS_IO_SUBMIT, "IO_SUBMIT" }, \
{ EXT4_EX_NOCACHE, "EX_NOCACHE" })
/* /*
* __print_flags() requires that all enum values be wrapped in the * __print_flags() requires that all enum values be wrapped in the

View File

@ -9,8 +9,8 @@
* Andreas Dilger <adilger@sun.com> * Andreas Dilger <adilger@sun.com>
*/ */
#ifndef _LINUX_FIEMAP_H #ifndef _UAPI_LINUX_FIEMAP_H
#define _LINUX_FIEMAP_H #define _UAPI_LINUX_FIEMAP_H
#include <linux/types.h> #include <linux/types.h>
@ -67,4 +67,4 @@ struct fiemap {
#define FIEMAP_EXTENT_SHARED 0x00002000 /* Space shared with other #define FIEMAP_EXTENT_SHARED 0x00002000 /* Space shared with other
* files. */ * files. */
#endif /* _LINUX_FIEMAP_H */ #endif /* _UAPI_LINUX_FIEMAP_H */