Convert ext4 to use the new mount API, and add support for the
FS_IOC_GETFSLABEL and FS_IOC_SETFSLABEL ioctls. In addition the usual large number of clean ups and bug fixes, in particular for the fast_commit feature. -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEK2m5VNv+CHkogTfJ8vlZVpUNgaMFAmHcr8sACgkQ8vlZVpUN gaOuMwgAgunXEni8yED1QXAp+F1nigxx8K6O6fpjzVjiUFlH/cgc3M5PEZiV7Pgj rIsNliCLQjDQPA7XxInvMuDXEvGRdleaqGS5MCKBR9898acaCTn0eBCK8ZKfhlIs tJus706kOjy8IXRgEJkCrxEdrsJ57rgqHk/bstipNVg4lnfYYSUKH8k0ob4aaoiW irufs1dSo7KmpwL0c/z9yIei3ST4DA2Dc/eBS0hsdgKtr/zMmwSanxqbQJTXz8ra FTZ23HAu/zxWZ9GM4p3DhuVaMWaulm6eEqJKO+loeU9xOeItn0WmSJW260KR0cmj 0nPAflMmpQVBhtO9gjXcSiORbd6/zg== =9HNa -----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: "Convert ext4 to use the new mount API, and add support for the FS_IOC_GETFSLABEL and FS_IOC_SETFSLABEL ioctls. In addition the usual large number of clean ups and bug fixes, in particular for the fast_commit feature" * tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (48 commits) ext4: don't use the orphan list when migrating an inode ext4: use BUG_ON instead of if condition followed by BUG ext4: fix a copy and paste typo ext4: set csum seed in tmp inode while migrating to extents ext4: remove unnecessary 'offset' assignment ext4: remove redundant o_start statement ext4: drop an always true check ext4: remove unused assignments ext4: remove redundant statement ext4: remove useless resetting io_end_size in mpage_process_page() ext4: allow to change s_last_trim_minblks via sysfs ext4: change s_last_trim_minblks type to unsigned long ext4: implement support for get/set fs label ext4: only set EXT4_MOUNT_QUOTA when journalled quota file is specified ext4: don't use kfree() on rcu protected pointer sbi->s_qf_names ext4: avoid trim error on fs with small groups ext4: fix an use-after-free issue about data=journal writeback mode ext4: fix null-ptr-deref in '__ext4_journal_ensure_credits' ext4: initialize err_blk before calling __ext4_get_inode_loc ext4: fix a possible ABBA deadlock due to busy PA ...
This commit is contained in:
commit
1dbfae0113
@ -246,7 +246,6 @@ retry:
|
||||
handle = ext4_journal_start(inode, EXT4_HT_XATTR, credits);
|
||||
if (IS_ERR(handle))
|
||||
return PTR_ERR(handle);
|
||||
ext4_fc_start_update(inode);
|
||||
|
||||
if ((type == ACL_TYPE_ACCESS) && acl) {
|
||||
error = posix_acl_update_mode(mnt_userns, inode, &mode, &acl);
|
||||
@ -264,7 +263,6 @@ retry:
|
||||
}
|
||||
out_stop:
|
||||
ext4_journal_stop(handle);
|
||||
ext4_fc_stop_update(inode);
|
||||
if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
|
||||
goto retry;
|
||||
return error;
|
||||
|
@ -303,7 +303,6 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
|
||||
goto done;
|
||||
brelse(bh);
|
||||
bh = NULL;
|
||||
offset = 0;
|
||||
}
|
||||
done:
|
||||
err = 0;
|
||||
|
@ -1298,6 +1298,8 @@ extern void ext4_set_bits(void *bm, int cur, int len);
|
||||
/* Metadata checksum algorithm codes */
|
||||
#define EXT4_CRC32C_CHKSUM 1
|
||||
|
||||
#define EXT4_LABEL_MAX 16
|
||||
|
||||
/*
|
||||
* Structure of the super block
|
||||
*/
|
||||
@ -1347,7 +1349,7 @@ struct ext4_super_block {
|
||||
/*60*/ __le32 s_feature_incompat; /* incompatible feature set */
|
||||
__le32 s_feature_ro_compat; /* readonly-compatible feature set */
|
||||
/*68*/ __u8 s_uuid[16]; /* 128-bit uuid for volume */
|
||||
/*78*/ char s_volume_name[16]; /* volume name */
|
||||
/*78*/ char s_volume_name[EXT4_LABEL_MAX]; /* volume name */
|
||||
/*88*/ char s_last_mounted[64] __nonstring; /* directory where last mounted */
|
||||
/*C8*/ __le32 s_algorithm_usage_bitmap; /* For compression */
|
||||
/*
|
||||
@ -1661,7 +1663,7 @@ struct ext4_sb_info {
|
||||
struct task_struct *s_mmp_tsk;
|
||||
|
||||
/* record the last minlen when FITRIM is called. */
|
||||
atomic_t s_last_trim_minblks;
|
||||
unsigned long s_last_trim_minblks;
|
||||
|
||||
/* Reference to checksum algorithm driver via cryptoapi */
|
||||
struct crypto_shash *s_chksum_driver;
|
||||
@ -1725,9 +1727,9 @@ struct ext4_sb_info {
|
||||
*/
|
||||
struct work_struct s_error_work;
|
||||
|
||||
/* Ext4 fast commit stuff */
|
||||
/* Ext4 fast commit sub transaction ID */
|
||||
atomic_t s_fc_subtid;
|
||||
atomic_t s_fc_ineligible_updates;
|
||||
|
||||
/*
|
||||
* After commit starts, the main queue gets locked, and the further
|
||||
* updates get added in the staging queue.
|
||||
@ -1747,7 +1749,6 @@ struct ext4_sb_info {
|
||||
spinlock_t s_fc_lock;
|
||||
struct buffer_head *s_fc_bh;
|
||||
struct ext4_fc_stats s_fc_stats;
|
||||
u64 s_fc_avg_commit_time;
|
||||
#ifdef CONFIG_EXT4_DEBUG
|
||||
int s_fc_debug_max_replay;
|
||||
#endif
|
||||
@ -2399,8 +2400,7 @@ ext4_rec_len_from_disk(__le16 dlen, unsigned blocksize)
|
||||
|
||||
static inline __le16 ext4_rec_len_to_disk(unsigned len, unsigned blocksize)
|
||||
{
|
||||
if ((len > blocksize) || (blocksize > (1 << 18)) || (len & 3))
|
||||
BUG();
|
||||
BUG_ON((len > blocksize) || (blocksize > (1 << 18)) || (len & 3));
|
||||
#if (PAGE_SIZE >= 65536)
|
||||
if (len < 65536)
|
||||
return cpu_to_le16(len);
|
||||
@ -2926,8 +2926,6 @@ void __ext4_fc_track_create(handle_t *handle, struct inode *inode,
|
||||
void ext4_fc_track_create(handle_t *handle, struct dentry *dentry);
|
||||
void ext4_fc_track_inode(handle_t *handle, struct inode *inode);
|
||||
void ext4_fc_mark_ineligible(struct super_block *sb, int reason);
|
||||
void ext4_fc_start_ineligible(struct super_block *sb, int reason);
|
||||
void ext4_fc_stop_ineligible(struct super_block *sb);
|
||||
void ext4_fc_start_update(struct inode *inode);
|
||||
void ext4_fc_stop_update(struct inode *inode);
|
||||
void ext4_fc_del(struct inode *inode);
|
||||
@ -2935,6 +2933,7 @@ bool ext4_fc_replay_check_excluded(struct super_block *sb, ext4_fsblk_t block);
|
||||
void ext4_fc_replay_cleanup(struct super_block *sb);
|
||||
int ext4_fc_commit(journal_t *journal, tid_t commit_tid);
|
||||
int __init ext4_fc_init_dentry_cache(void);
|
||||
void ext4_fc_destroy_dentry_cache(void);
|
||||
|
||||
/* mballoc.c */
|
||||
extern const struct seq_operations ext4_mb_seq_groups_ops;
|
||||
@ -3096,6 +3095,9 @@ extern int ext4_group_extend(struct super_block *sb,
|
||||
struct ext4_super_block *es,
|
||||
ext4_fsblk_t n_blocks_count);
|
||||
extern int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count);
|
||||
extern unsigned int ext4_list_backups(struct super_block *sb,
|
||||
unsigned int *three, unsigned int *five,
|
||||
unsigned int *seven);
|
||||
|
||||
/* super.c */
|
||||
extern struct buffer_head *ext4_sb_bread(struct super_block *sb,
|
||||
@ -3110,6 +3112,8 @@ extern int ext4_read_bh_lock(struct buffer_head *bh, int op_flags, bool wait);
|
||||
extern void ext4_sb_breadahead_unmovable(struct super_block *sb, sector_t block);
|
||||
extern int ext4_seq_options_show(struct seq_file *seq, void *offset);
|
||||
extern int ext4_calculate_overhead(struct super_block *sb);
|
||||
extern __le32 ext4_superblock_csum(struct super_block *sb,
|
||||
struct ext4_super_block *es);
|
||||
extern void ext4_superblock_csum_set(struct super_block *sb);
|
||||
extern int ext4_alloc_flex_bg_array(struct super_block *sb,
|
||||
ext4_group_t ngroup);
|
||||
|
@ -162,6 +162,8 @@ int __ext4_journal_ensure_credits(handle_t *handle, int check_cred,
|
||||
{
|
||||
if (!ext4_handle_valid(handle))
|
||||
return 0;
|
||||
if (is_handle_aborted(handle))
|
||||
return -EROFS;
|
||||
if (jbd2_handle_buffer_credits(handle) >= check_cred &&
|
||||
handle->h_revoke_credits >= revoke_cred)
|
||||
return 0;
|
||||
|
@ -1496,8 +1496,7 @@ static int ext4_ext_search_left(struct inode *inode,
|
||||
EXT4_ERROR_INODE(inode,
|
||||
"ix (%d) != EXT_FIRST_INDEX (%d) (depth %d)!",
|
||||
ix != NULL ? le32_to_cpu(ix->ei_block) : 0,
|
||||
EXT_FIRST_INDEX(path[depth].p_hdr) != NULL ?
|
||||
le32_to_cpu(EXT_FIRST_INDEX(path[depth].p_hdr)->ei_block) : 0,
|
||||
le32_to_cpu(EXT_FIRST_INDEX(path[depth].p_hdr)->ei_block),
|
||||
depth);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
@ -2025,7 +2024,6 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
|
||||
+ ext4_ext_get_actual_len(newext));
|
||||
if (unwritten)
|
||||
ext4_ext_mark_unwritten(ex);
|
||||
eh = path[depth].p_hdr;
|
||||
nearex = ex;
|
||||
goto merge;
|
||||
}
|
||||
@ -2054,7 +2052,6 @@ prepend:
|
||||
+ ext4_ext_get_actual_len(newext));
|
||||
if (unwritten)
|
||||
ext4_ext_mark_unwritten(ex);
|
||||
eh = path[depth].p_hdr;
|
||||
nearex = ex;
|
||||
goto merge;
|
||||
}
|
||||
@ -4647,8 +4644,6 @@ static long ext4_zero_range(struct file *file, loff_t offset,
|
||||
ret = ext4_mark_inode_dirty(handle, inode);
|
||||
if (unlikely(ret))
|
||||
goto out_handle;
|
||||
ext4_fc_track_range(handle, inode, offset >> inode->i_sb->s_blocksize_bits,
|
||||
(offset + len - 1) >> inode->i_sb->s_blocksize_bits);
|
||||
/* Zero out partial block at the edges of the range */
|
||||
ret = ext4_zero_partial_blocks(handle, inode, offset, len);
|
||||
if (ret >= 0)
|
||||
@ -4697,8 +4692,6 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
|
||||
FALLOC_FL_INSERT_RANGE))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ext4_fc_start_update(inode);
|
||||
|
||||
if (mode & FALLOC_FL_PUNCH_HOLE) {
|
||||
ret = ext4_punch_hole(inode, offset, len);
|
||||
goto exit;
|
||||
@ -4762,7 +4755,6 @@ out:
|
||||
inode_unlock(inode);
|
||||
trace_ext4_fallocate_exit(inode, offset, max_blocks, ret);
|
||||
exit:
|
||||
ext4_fc_stop_update(inode);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -5344,7 +5336,7 @@ static int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len)
|
||||
ret = PTR_ERR(handle);
|
||||
goto out_mmap;
|
||||
}
|
||||
ext4_fc_start_ineligible(sb, EXT4_FC_REASON_FALLOC_RANGE);
|
||||
ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_FALLOC_RANGE);
|
||||
|
||||
down_write(&EXT4_I(inode)->i_data_sem);
|
||||
ext4_discard_preallocations(inode, 0);
|
||||
@ -5383,7 +5375,6 @@ static int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len)
|
||||
|
||||
out_stop:
|
||||
ext4_journal_stop(handle);
|
||||
ext4_fc_stop_ineligible(sb);
|
||||
out_mmap:
|
||||
filemap_invalidate_unlock(mapping);
|
||||
out_mutex:
|
||||
@ -5485,7 +5476,7 @@ static int ext4_insert_range(struct inode *inode, loff_t offset, loff_t len)
|
||||
ret = PTR_ERR(handle);
|
||||
goto out_mmap;
|
||||
}
|
||||
ext4_fc_start_ineligible(sb, EXT4_FC_REASON_FALLOC_RANGE);
|
||||
ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_FALLOC_RANGE);
|
||||
|
||||
/* Expand file to avoid data loss if there is error while shifting */
|
||||
inode->i_size += len;
|
||||
@ -5560,7 +5551,6 @@ static int ext4_insert_range(struct inode *inode, loff_t offset, loff_t len)
|
||||
|
||||
out_stop:
|
||||
ext4_journal_stop(handle);
|
||||
ext4_fc_stop_ineligible(sb);
|
||||
out_mmap:
|
||||
filemap_invalidate_unlock(mapping);
|
||||
out_mutex:
|
||||
|
@ -65,21 +65,11 @@
|
||||
*
|
||||
* Fast Commit Ineligibility
|
||||
* -------------------------
|
||||
*
|
||||
* Not all operations are supported by fast commits today (e.g extended
|
||||
* attributes). Fast commit ineligibility is marked by calling one of the
|
||||
* two following functions:
|
||||
*
|
||||
* - ext4_fc_mark_ineligible(): This makes next fast commit operation to fall
|
||||
* back to full commit. This is useful in case of transient errors.
|
||||
*
|
||||
* - ext4_fc_start_ineligible() and ext4_fc_stop_ineligible() - This makes all
|
||||
* the fast commits happening between ext4_fc_start_ineligible() and
|
||||
* ext4_fc_stop_ineligible() and one fast commit after the call to
|
||||
* ext4_fc_stop_ineligible() to fall back to full commits. It is important to
|
||||
* make one more fast commit to fall back to full commit after stop call so
|
||||
* that it guaranteed that the fast commit ineligible operation contained
|
||||
* within ext4_fc_start_ineligible() and ext4_fc_stop_ineligible() is
|
||||
* followed by at least 1 full commit.
|
||||
* attributes). Fast commit ineligibility is marked by calling
|
||||
* ext4_fc_mark_ineligible(): This makes next fast commit operation to fall back
|
||||
* to full commit.
|
||||
*
|
||||
* Atomicity of commits
|
||||
* --------------------
|
||||
@ -166,15 +156,13 @@
|
||||
* fast commit recovery even if that area is invalidated by later full
|
||||
* commits.
|
||||
*
|
||||
* 1) Make fast commit atomic updates more fine grained. Today, a fast commit
|
||||
* eligible update must be protected within ext4_fc_start_update() and
|
||||
* ext4_fc_stop_update(). These routines are called at much higher
|
||||
* routines. This can be made more fine grained by combining with
|
||||
* ext4_journal_start().
|
||||
* 1) Fast commit's commit path locks the entire file system during fast
|
||||
* commit. This has significant performance penalty. Instead of that, we
|
||||
* should use ext4_fc_start/stop_update functions to start inode level
|
||||
* updates from ext4_journal_start/stop. Once we do that we can drop file
|
||||
* system locking during commit path.
|
||||
*
|
||||
* 2) Same above for ext4_fc_start_ineligible() and ext4_fc_stop_ineligible()
|
||||
*
|
||||
* 3) Handle more ineligible cases.
|
||||
* 2) Handle more ineligible cases.
|
||||
*/
|
||||
|
||||
#include <trace/events/ext4.h>
|
||||
@ -328,44 +316,6 @@ void ext4_fc_mark_ineligible(struct super_block *sb, int reason)
|
||||
sbi->s_fc_stats.fc_ineligible_reason_count[reason]++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start a fast commit ineligible update. Any commits that happen while
|
||||
* such an operation is in progress fall back to full commits.
|
||||
*/
|
||||
void ext4_fc_start_ineligible(struct super_block *sb, int reason)
|
||||
{
|
||||
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
||||
|
||||
if (!test_opt2(sb, JOURNAL_FAST_COMMIT) ||
|
||||
(EXT4_SB(sb)->s_mount_state & EXT4_FC_REPLAY))
|
||||
return;
|
||||
|
||||
WARN_ON(reason >= EXT4_FC_REASON_MAX);
|
||||
sbi->s_fc_stats.fc_ineligible_reason_count[reason]++;
|
||||
atomic_inc(&sbi->s_fc_ineligible_updates);
|
||||
}
|
||||
|
||||
/*
|
||||
* Stop a fast commit ineligible update. We set EXT4_MF_FC_INELIGIBLE flag here
|
||||
* to ensure that after stopping the ineligible update, at least one full
|
||||
* commit takes place.
|
||||
*/
|
||||
void ext4_fc_stop_ineligible(struct super_block *sb)
|
||||
{
|
||||
if (!test_opt2(sb, JOURNAL_FAST_COMMIT) ||
|
||||
(EXT4_SB(sb)->s_mount_state & EXT4_FC_REPLAY))
|
||||
return;
|
||||
|
||||
ext4_set_mount_flag(sb, EXT4_MF_FC_INELIGIBLE);
|
||||
atomic_dec(&EXT4_SB(sb)->s_fc_ineligible_updates);
|
||||
}
|
||||
|
||||
static inline int ext4_fc_is_ineligible(struct super_block *sb)
|
||||
{
|
||||
return (ext4_test_mount_flag(sb, EXT4_MF_FC_INELIGIBLE) ||
|
||||
atomic_read(&EXT4_SB(sb)->s_fc_ineligible_updates));
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic fast commit tracking function. If this is the first time this we are
|
||||
* called after a full commit, we initialize fast commit fields and then call
|
||||
@ -391,7 +341,7 @@ static int ext4_fc_track_template(
|
||||
(sbi->s_mount_state & EXT4_FC_REPLAY))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (ext4_fc_is_ineligible(inode->i_sb))
|
||||
if (ext4_test_mount_flag(inode->i_sb, EXT4_MF_FC_INELIGIBLE))
|
||||
return -EINVAL;
|
||||
|
||||
tid = handle->h_transaction->t_tid;
|
||||
@ -796,7 +746,6 @@ static bool ext4_fc_add_dentry_tlv(struct super_block *sb, u32 *crc,
|
||||
ext4_fc_memcpy(sb, dst, &fcd, sizeof(fcd), crc);
|
||||
dst += sizeof(fcd);
|
||||
ext4_fc_memcpy(sb, dst, fc_dentry->fcd_name.name, dlen, crc);
|
||||
dst += dlen;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1123,6 +1072,32 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ext4_fc_update_stats(struct super_block *sb, int status,
|
||||
u64 commit_time, int nblks)
|
||||
{
|
||||
struct ext4_fc_stats *stats = &EXT4_SB(sb)->s_fc_stats;
|
||||
|
||||
jbd_debug(1, "Fast commit ended with status = %d", status);
|
||||
if (status == EXT4_FC_STATUS_OK) {
|
||||
stats->fc_num_commits++;
|
||||
stats->fc_numblks += nblks;
|
||||
if (likely(stats->s_fc_avg_commit_time))
|
||||
stats->s_fc_avg_commit_time =
|
||||
(commit_time +
|
||||
stats->s_fc_avg_commit_time * 3) / 4;
|
||||
else
|
||||
stats->s_fc_avg_commit_time = commit_time;
|
||||
} else if (status == EXT4_FC_STATUS_FAILED ||
|
||||
status == EXT4_FC_STATUS_INELIGIBLE) {
|
||||
if (status == EXT4_FC_STATUS_FAILED)
|
||||
stats->fc_failed_commits++;
|
||||
stats->fc_ineligible_commits++;
|
||||
} else {
|
||||
stats->fc_skipped_commits++;
|
||||
}
|
||||
trace_ext4_fc_commit_stop(sb, nblks, status);
|
||||
}
|
||||
|
||||
/*
|
||||
* The main commit entry point. Performs a fast commit for transaction
|
||||
* commit_tid if needed. If it's not possible to perform a fast commit
|
||||
@ -1135,18 +1110,15 @@ int ext4_fc_commit(journal_t *journal, tid_t commit_tid)
|
||||
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
||||
int nblks = 0, ret, bsize = journal->j_blocksize;
|
||||
int subtid = atomic_read(&sbi->s_fc_subtid);
|
||||
int reason = EXT4_FC_REASON_OK, fc_bufs_before = 0;
|
||||
int status = EXT4_FC_STATUS_OK, fc_bufs_before = 0;
|
||||
ktime_t start_time, commit_time;
|
||||
|
||||
trace_ext4_fc_commit_start(sb);
|
||||
|
||||
start_time = ktime_get();
|
||||
|
||||
if (!test_opt2(sb, JOURNAL_FAST_COMMIT) ||
|
||||
(ext4_fc_is_ineligible(sb))) {
|
||||
reason = EXT4_FC_REASON_INELIGIBLE;
|
||||
goto out;
|
||||
}
|
||||
if (!test_opt2(sb, JOURNAL_FAST_COMMIT))
|
||||
return jbd2_complete_transaction(journal, commit_tid);
|
||||
|
||||
restart_fc:
|
||||
ret = jbd2_fc_begin_commit(journal, commit_tid);
|
||||
@ -1155,67 +1127,52 @@ restart_fc:
|
||||
if (atomic_read(&sbi->s_fc_subtid) <= subtid &&
|
||||
commit_tid > journal->j_commit_sequence)
|
||||
goto restart_fc;
|
||||
reason = EXT4_FC_REASON_ALREADY_COMMITTED;
|
||||
goto out;
|
||||
ext4_fc_update_stats(sb, EXT4_FC_STATUS_SKIPPED, 0, 0);
|
||||
return 0;
|
||||
} else if (ret) {
|
||||
sbi->s_fc_stats.fc_ineligible_reason_count[EXT4_FC_COMMIT_FAILED]++;
|
||||
reason = EXT4_FC_REASON_FC_START_FAILED;
|
||||
goto out;
|
||||
/*
|
||||
* Commit couldn't start. Just update stats and perform a
|
||||
* full commit.
|
||||
*/
|
||||
ext4_fc_update_stats(sb, EXT4_FC_STATUS_FAILED, 0, 0);
|
||||
return jbd2_complete_transaction(journal, commit_tid);
|
||||
}
|
||||
|
||||
/*
|
||||
* After establishing journal barrier via jbd2_fc_begin_commit(), check
|
||||
* if we are fast commit ineligible.
|
||||
*/
|
||||
if (ext4_test_mount_flag(sb, EXT4_MF_FC_INELIGIBLE)) {
|
||||
status = EXT4_FC_STATUS_INELIGIBLE;
|
||||
goto fallback;
|
||||
}
|
||||
|
||||
fc_bufs_before = (sbi->s_fc_bytes + bsize - 1) / bsize;
|
||||
ret = ext4_fc_perform_commit(journal);
|
||||
if (ret < 0) {
|
||||
sbi->s_fc_stats.fc_ineligible_reason_count[EXT4_FC_COMMIT_FAILED]++;
|
||||
reason = EXT4_FC_REASON_FC_FAILED;
|
||||
goto out;
|
||||
status = EXT4_FC_STATUS_FAILED;
|
||||
goto fallback;
|
||||
}
|
||||
nblks = (sbi->s_fc_bytes + bsize - 1) / bsize - fc_bufs_before;
|
||||
ret = jbd2_fc_wait_bufs(journal, nblks);
|
||||
if (ret < 0) {
|
||||
sbi->s_fc_stats.fc_ineligible_reason_count[EXT4_FC_COMMIT_FAILED]++;
|
||||
reason = EXT4_FC_REASON_FC_FAILED;
|
||||
goto out;
|
||||
status = EXT4_FC_STATUS_FAILED;
|
||||
goto fallback;
|
||||
}
|
||||
atomic_inc(&sbi->s_fc_subtid);
|
||||
jbd2_fc_end_commit(journal);
|
||||
out:
|
||||
/* Has any ineligible update happened since we started? */
|
||||
if (reason == EXT4_FC_REASON_OK && ext4_fc_is_ineligible(sb)) {
|
||||
sbi->s_fc_stats.fc_ineligible_reason_count[EXT4_FC_COMMIT_FAILED]++;
|
||||
reason = EXT4_FC_REASON_INELIGIBLE;
|
||||
}
|
||||
|
||||
spin_lock(&sbi->s_fc_lock);
|
||||
if (reason != EXT4_FC_REASON_OK &&
|
||||
reason != EXT4_FC_REASON_ALREADY_COMMITTED) {
|
||||
sbi->s_fc_stats.fc_ineligible_commits++;
|
||||
} else {
|
||||
sbi->s_fc_stats.fc_num_commits++;
|
||||
sbi->s_fc_stats.fc_numblks += nblks;
|
||||
}
|
||||
spin_unlock(&sbi->s_fc_lock);
|
||||
nblks = (reason == EXT4_FC_REASON_OK) ? nblks : 0;
|
||||
trace_ext4_fc_commit_stop(sb, nblks, reason);
|
||||
commit_time = ktime_to_ns(ktime_sub(ktime_get(), start_time));
|
||||
ret = jbd2_fc_end_commit(journal);
|
||||
/*
|
||||
* weight the commit time higher than the average time so we don't
|
||||
* react too strongly to vast changes in the commit time
|
||||
* weight the commit time higher than the average time so we
|
||||
* don't react too strongly to vast changes in the commit time
|
||||
*/
|
||||
if (likely(sbi->s_fc_avg_commit_time))
|
||||
sbi->s_fc_avg_commit_time = (commit_time +
|
||||
sbi->s_fc_avg_commit_time * 3) / 4;
|
||||
else
|
||||
sbi->s_fc_avg_commit_time = commit_time;
|
||||
jbd_debug(1,
|
||||
"Fast commit ended with blks = %d, reason = %d, subtid - %d",
|
||||
nblks, reason, subtid);
|
||||
if (reason == EXT4_FC_REASON_FC_FAILED)
|
||||
return jbd2_fc_end_commit_fallback(journal);
|
||||
if (reason == EXT4_FC_REASON_FC_START_FAILED ||
|
||||
reason == EXT4_FC_REASON_INELIGIBLE)
|
||||
return jbd2_complete_transaction(journal, commit_tid);
|
||||
return 0;
|
||||
commit_time = ktime_to_ns(ktime_sub(ktime_get(), start_time));
|
||||
ext4_fc_update_stats(sb, status, commit_time, nblks);
|
||||
return ret;
|
||||
|
||||
fallback:
|
||||
ret = jbd2_fc_end_commit_fallback(journal);
|
||||
ext4_fc_update_stats(sb, status, 0, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1812,11 +1769,14 @@ ext4_fc_replay_del_range(struct super_block *sb, struct ext4_fc_tl *tl,
|
||||
}
|
||||
}
|
||||
|
||||
ret = ext4_punch_hole(inode,
|
||||
le32_to_cpu(lrange.fc_lblk) << sb->s_blocksize_bits,
|
||||
le32_to_cpu(lrange.fc_len) << sb->s_blocksize_bits);
|
||||
if (ret)
|
||||
jbd_debug(1, "ext4_punch_hole returned %d", ret);
|
||||
down_write(&EXT4_I(inode)->i_data_sem);
|
||||
ret = ext4_ext_remove_space(inode, lrange.fc_lblk,
|
||||
lrange.fc_lblk + lrange.fc_len - 1);
|
||||
up_write(&EXT4_I(inode)->i_data_sem);
|
||||
if (ret) {
|
||||
iput(inode);
|
||||
return 0;
|
||||
}
|
||||
ext4_ext_replay_shrink_inode(inode,
|
||||
i_size_read(inode) >> sb->s_blocksize_bits);
|
||||
ext4_mark_inode_dirty(NULL, inode);
|
||||
@ -2173,7 +2133,7 @@ int ext4_fc_info_show(struct seq_file *seq, void *v)
|
||||
"fc stats:\n%ld commits\n%ld ineligible\n%ld numblks\n%lluus avg_commit_time\n",
|
||||
stats->fc_num_commits, stats->fc_ineligible_commits,
|
||||
stats->fc_numblks,
|
||||
div_u64(sbi->s_fc_avg_commit_time, 1000));
|
||||
div_u64(stats->s_fc_avg_commit_time, 1000));
|
||||
seq_puts(seq, "Ineligible reasons:\n");
|
||||
for (i = 0; i < EXT4_FC_REASON_MAX; i++)
|
||||
seq_printf(seq, "\"%s\":\t%d\n", fc_ineligible_reasons[i],
|
||||
@ -2192,3 +2152,8 @@ int __init ext4_fc_init_dentry_cache(void)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ext4_fc_destroy_dentry_cache(void)
|
||||
{
|
||||
kmem_cache_destroy(ext4_fc_dentry_cachep);
|
||||
}
|
||||
|
@ -71,21 +71,19 @@ struct ext4_fc_tail {
|
||||
};
|
||||
|
||||
/*
|
||||
* Fast commit reason codes
|
||||
* Fast commit status codes
|
||||
*/
|
||||
enum {
|
||||
/*
|
||||
* Commit status codes:
|
||||
*/
|
||||
EXT4_FC_REASON_OK = 0,
|
||||
EXT4_FC_REASON_INELIGIBLE,
|
||||
EXT4_FC_REASON_ALREADY_COMMITTED,
|
||||
EXT4_FC_REASON_FC_START_FAILED,
|
||||
EXT4_FC_REASON_FC_FAILED,
|
||||
EXT4_FC_STATUS_OK = 0,
|
||||
EXT4_FC_STATUS_INELIGIBLE,
|
||||
EXT4_FC_STATUS_SKIPPED,
|
||||
EXT4_FC_STATUS_FAILED,
|
||||
};
|
||||
|
||||
/*
|
||||
* Fast commit ineligiblity reasons:
|
||||
*/
|
||||
/*
|
||||
* Fast commit ineligiblity reasons:
|
||||
*/
|
||||
enum {
|
||||
EXT4_FC_REASON_XATTR = 0,
|
||||
EXT4_FC_REASON_CROSS_RENAME,
|
||||
EXT4_FC_REASON_JOURNAL_FLAG_CHANGE,
|
||||
@ -117,7 +115,10 @@ struct ext4_fc_stats {
|
||||
unsigned int fc_ineligible_reason_count[EXT4_FC_REASON_MAX];
|
||||
unsigned long fc_num_commits;
|
||||
unsigned long fc_ineligible_commits;
|
||||
unsigned long fc_failed_commits;
|
||||
unsigned long fc_skipped_commits;
|
||||
unsigned long fc_numblks;
|
||||
u64 s_fc_avg_commit_time;
|
||||
};
|
||||
|
||||
#define EXT4_FC_REPLAY_REALLOC_INCREMENT 4
|
||||
|
@ -259,7 +259,6 @@ static ssize_t ext4_buffered_write_iter(struct kiocb *iocb,
|
||||
if (iocb->ki_flags & IOCB_NOWAIT)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ext4_fc_start_update(inode);
|
||||
inode_lock(inode);
|
||||
ret = ext4_write_checks(iocb, from);
|
||||
if (ret <= 0)
|
||||
@ -271,7 +270,6 @@ static ssize_t ext4_buffered_write_iter(struct kiocb *iocb,
|
||||
|
||||
out:
|
||||
inode_unlock(inode);
|
||||
ext4_fc_stop_update(inode);
|
||||
if (likely(ret > 0)) {
|
||||
iocb->ki_pos += ret;
|
||||
ret = generic_write_sync(iocb, ret);
|
||||
@ -552,9 +550,7 @@ static ssize_t ext4_dio_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ext4_fc_start_update(inode);
|
||||
ret = ext4_orphan_add(handle, inode);
|
||||
ext4_fc_stop_update(inode);
|
||||
if (ret) {
|
||||
ext4_journal_stop(handle);
|
||||
goto out;
|
||||
|
@ -741,10 +741,11 @@ out_sem:
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ext4_fc_track_range(handle, inode, map->m_lblk,
|
||||
map->m_lblk + map->m_len - 1);
|
||||
}
|
||||
|
||||
if (retval > 0 && (map->m_flags & EXT4_MAP_UNWRITTEN ||
|
||||
map->m_flags & EXT4_MAP_MAPPED))
|
||||
ext4_fc_track_range(handle, inode, map->m_lblk,
|
||||
map->m_lblk + map->m_len - 1);
|
||||
if (retval < 0)
|
||||
ext_debug(inode, "failed with err %d\n", retval);
|
||||
return retval;
|
||||
@ -1844,30 +1845,16 @@ int ext4_da_get_block_prep(struct inode *inode, sector_t iblock,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bget_one(handle_t *handle, struct inode *inode,
|
||||
struct buffer_head *bh)
|
||||
{
|
||||
get_bh(bh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bput_one(handle_t *handle, struct inode *inode,
|
||||
struct buffer_head *bh)
|
||||
{
|
||||
put_bh(bh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __ext4_journalled_writepage(struct page *page,
|
||||
unsigned int len)
|
||||
{
|
||||
struct address_space *mapping = page->mapping;
|
||||
struct inode *inode = mapping->host;
|
||||
struct buffer_head *page_bufs = NULL;
|
||||
handle_t *handle = NULL;
|
||||
int ret = 0, err = 0;
|
||||
int inline_data = ext4_has_inline_data(inode);
|
||||
struct buffer_head *inode_bh = NULL;
|
||||
loff_t size;
|
||||
|
||||
ClearPageChecked(page);
|
||||
|
||||
@ -1877,14 +1864,6 @@ static int __ext4_journalled_writepage(struct page *page,
|
||||
inode_bh = ext4_journalled_write_inline_data(inode, len, page);
|
||||
if (inode_bh == NULL)
|
||||
goto out;
|
||||
} else {
|
||||
page_bufs = page_buffers(page);
|
||||
if (!page_bufs) {
|
||||
BUG();
|
||||
goto out;
|
||||
}
|
||||
ext4_walk_page_buffers(handle, inode, page_bufs, 0, len,
|
||||
NULL, bget_one);
|
||||
}
|
||||
/*
|
||||
* We need to release the page lock before we start the
|
||||
@ -1905,7 +1884,8 @@ static int __ext4_journalled_writepage(struct page *page,
|
||||
|
||||
lock_page(page);
|
||||
put_page(page);
|
||||
if (page->mapping != mapping) {
|
||||
size = i_size_read(inode);
|
||||
if (page->mapping != mapping || page_offset(page) > size) {
|
||||
/* The page got truncated from under us */
|
||||
ext4_journal_stop(handle);
|
||||
ret = 0;
|
||||
@ -1915,6 +1895,13 @@ static int __ext4_journalled_writepage(struct page *page,
|
||||
if (inline_data) {
|
||||
ret = ext4_mark_inode_dirty(handle, inode);
|
||||
} else {
|
||||
struct buffer_head *page_bufs = page_buffers(page);
|
||||
|
||||
if (page->index == size >> PAGE_SHIFT)
|
||||
len = size & ~PAGE_MASK;
|
||||
else
|
||||
len = PAGE_SIZE;
|
||||
|
||||
ret = ext4_walk_page_buffers(handle, inode, page_bufs, 0, len,
|
||||
NULL, do_journal_get_write_access);
|
||||
|
||||
@ -1935,9 +1922,6 @@ static int __ext4_journalled_writepage(struct page *page,
|
||||
out:
|
||||
unlock_page(page);
|
||||
out_no_pagelock:
|
||||
if (!inline_data && page_bufs)
|
||||
ext4_walk_page_buffers(NULL, inode, page_bufs, 0, len,
|
||||
NULL, bput_one);
|
||||
brelse(inode_bh);
|
||||
return ret;
|
||||
}
|
||||
@ -2257,7 +2241,6 @@ static int mpage_process_page(struct mpage_da_data *mpd, struct page *page,
|
||||
mpd->map.m_len = 0;
|
||||
mpd->map.m_flags = 0;
|
||||
io_end_vec->size += io_end_size;
|
||||
io_end_size = 0;
|
||||
|
||||
err = mpage_process_page_bufs(mpd, head, bh, lblk);
|
||||
if (err > 0)
|
||||
@ -2282,7 +2265,6 @@ static int mpage_process_page(struct mpage_da_data *mpd, struct page *page,
|
||||
} while (lblk++, (bh = bh->b_this_page) != head);
|
||||
|
||||
io_end_vec->size += io_end_size;
|
||||
io_end_size = 0;
|
||||
*map_bh = false;
|
||||
out:
|
||||
*m_lblk = lblk;
|
||||
@ -4523,7 +4505,7 @@ has_buffer:
|
||||
static int __ext4_get_inode_loc_noinmem(struct inode *inode,
|
||||
struct ext4_iloc *iloc)
|
||||
{
|
||||
ext4_fsblk_t err_blk;
|
||||
ext4_fsblk_t err_blk = 0;
|
||||
int ret;
|
||||
|
||||
ret = __ext4_get_inode_loc(inode->i_sb, inode->i_ino, NULL, iloc,
|
||||
@ -4538,7 +4520,7 @@ static int __ext4_get_inode_loc_noinmem(struct inode *inode,
|
||||
|
||||
int ext4_get_inode_loc(struct inode *inode, struct ext4_iloc *iloc)
|
||||
{
|
||||
ext4_fsblk_t err_blk;
|
||||
ext4_fsblk_t err_blk = 0;
|
||||
int ret;
|
||||
|
||||
ret = __ext4_get_inode_loc(inode->i_sb, inode->i_ino, inode, iloc,
|
||||
@ -5320,7 +5302,7 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
ext4_fc_start_update(inode);
|
||||
|
||||
if ((ia_valid & ATTR_UID && !uid_eq(attr->ia_uid, inode->i_uid)) ||
|
||||
(ia_valid & ATTR_GID && !gid_eq(attr->ia_gid, inode->i_gid))) {
|
||||
handle_t *handle;
|
||||
@ -5344,7 +5326,6 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
|
||||
|
||||
if (error) {
|
||||
ext4_journal_stop(handle);
|
||||
ext4_fc_stop_update(inode);
|
||||
return error;
|
||||
}
|
||||
/* Update corresponding info in inode so that everything is in
|
||||
@ -5356,7 +5337,6 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
|
||||
error = ext4_mark_inode_dirty(handle, inode);
|
||||
ext4_journal_stop(handle);
|
||||
if (unlikely(error)) {
|
||||
ext4_fc_stop_update(inode);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
@ -5370,12 +5350,10 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
|
||||
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
||||
|
||||
if (attr->ia_size > sbi->s_bitmap_maxbytes) {
|
||||
ext4_fc_stop_update(inode);
|
||||
return -EFBIG;
|
||||
}
|
||||
}
|
||||
if (!S_ISREG(inode->i_mode)) {
|
||||
ext4_fc_stop_update(inode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -5427,8 +5405,7 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
|
||||
ext4_fc_track_range(handle, inode,
|
||||
(attr->ia_size > 0 ? attr->ia_size - 1 : 0) >>
|
||||
inode->i_sb->s_blocksize_bits,
|
||||
(oldsize > 0 ? oldsize - 1 : 0) >>
|
||||
inode->i_sb->s_blocksize_bits);
|
||||
EXT_MAX_BLOCKS - 1);
|
||||
else
|
||||
ext4_fc_track_range(
|
||||
handle, inode,
|
||||
@ -5499,7 +5476,6 @@ err_out:
|
||||
ext4_std_error(inode->i_sb, error);
|
||||
if (!error)
|
||||
error = rc;
|
||||
ext4_fc_stop_update(inode);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
324
fs/ext4/ioctl.c
324
fs/ext4/ioctl.c
@ -27,6 +27,248 @@
|
||||
#include "fsmap.h"
|
||||
#include <trace/events/ext4.h>
|
||||
|
||||
typedef void ext4_update_sb_callback(struct ext4_super_block *es,
|
||||
const void *arg);
|
||||
|
||||
/*
|
||||
* Superblock modification callback function for changing file system
|
||||
* label
|
||||
*/
|
||||
static void ext4_sb_setlabel(struct ext4_super_block *es, const void *arg)
|
||||
{
|
||||
/* Sanity check, this should never happen */
|
||||
BUILD_BUG_ON(sizeof(es->s_volume_name) < EXT4_LABEL_MAX);
|
||||
|
||||
memcpy(es->s_volume_name, (char *)arg, EXT4_LABEL_MAX);
|
||||
}
|
||||
|
||||
static
|
||||
int ext4_update_primary_sb(struct super_block *sb, handle_t *handle,
|
||||
ext4_update_sb_callback func,
|
||||
const void *arg)
|
||||
{
|
||||
int err = 0;
|
||||
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
||||
struct buffer_head *bh = sbi->s_sbh;
|
||||
struct ext4_super_block *es = sbi->s_es;
|
||||
|
||||
trace_ext4_update_sb(sb, bh->b_blocknr, 1);
|
||||
|
||||
BUFFER_TRACE(bh, "get_write_access");
|
||||
err = ext4_journal_get_write_access(handle, sb,
|
||||
bh,
|
||||
EXT4_JTR_NONE);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
lock_buffer(bh);
|
||||
func(es, arg);
|
||||
ext4_superblock_csum_set(sb);
|
||||
unlock_buffer(bh);
|
||||
|
||||
if (buffer_write_io_error(bh) || !buffer_uptodate(bh)) {
|
||||
ext4_msg(sbi->s_sb, KERN_ERR, "previous I/O error to "
|
||||
"superblock detected");
|
||||
clear_buffer_write_io_error(bh);
|
||||
set_buffer_uptodate(bh);
|
||||
}
|
||||
|
||||
err = ext4_handle_dirty_metadata(handle, NULL, bh);
|
||||
if (err)
|
||||
goto out_err;
|
||||
err = sync_dirty_buffer(bh);
|
||||
out_err:
|
||||
ext4_std_error(sb, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update one backup superblock in the group 'grp' using the callback
|
||||
* function 'func' and argument 'arg'. If the handle is NULL the
|
||||
* modification is not journalled.
|
||||
*
|
||||
* Returns: 0 when no modification was done (no superblock in the group)
|
||||
* 1 when the modification was successful
|
||||
* <0 on error
|
||||
*/
|
||||
static int ext4_update_backup_sb(struct super_block *sb,
|
||||
handle_t *handle, ext4_group_t grp,
|
||||
ext4_update_sb_callback func, const void *arg)
|
||||
{
|
||||
int err = 0;
|
||||
ext4_fsblk_t sb_block;
|
||||
struct buffer_head *bh;
|
||||
unsigned long offset = 0;
|
||||
struct ext4_super_block *es;
|
||||
|
||||
if (!ext4_bg_has_super(sb, grp))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* For the group 0 there is always 1k padding, so we have
|
||||
* either adjust offset, or sb_block depending on blocksize
|
||||
*/
|
||||
if (grp == 0) {
|
||||
sb_block = 1 * EXT4_MIN_BLOCK_SIZE;
|
||||
offset = do_div(sb_block, sb->s_blocksize);
|
||||
} else {
|
||||
sb_block = ext4_group_first_block_no(sb, grp);
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
trace_ext4_update_sb(sb, sb_block, handle ? 1 : 0);
|
||||
|
||||
bh = ext4_sb_bread(sb, sb_block, 0);
|
||||
if (IS_ERR(bh))
|
||||
return PTR_ERR(bh);
|
||||
|
||||
if (handle) {
|
||||
BUFFER_TRACE(bh, "get_write_access");
|
||||
err = ext4_journal_get_write_access(handle, sb,
|
||||
bh,
|
||||
EXT4_JTR_NONE);
|
||||
if (err)
|
||||
goto out_bh;
|
||||
}
|
||||
|
||||
es = (struct ext4_super_block *) (bh->b_data + offset);
|
||||
lock_buffer(bh);
|
||||
if (ext4_has_metadata_csum(sb) &&
|
||||
es->s_checksum != ext4_superblock_csum(sb, es)) {
|
||||
ext4_msg(sb, KERN_ERR, "Invalid checksum for backup "
|
||||
"superblock %llu\n", sb_block);
|
||||
unlock_buffer(bh);
|
||||
err = -EFSBADCRC;
|
||||
goto out_bh;
|
||||
}
|
||||
func(es, arg);
|
||||
if (ext4_has_metadata_csum(sb))
|
||||
es->s_checksum = ext4_superblock_csum(sb, es);
|
||||
set_buffer_uptodate(bh);
|
||||
unlock_buffer(bh);
|
||||
|
||||
if (err)
|
||||
goto out_bh;
|
||||
|
||||
if (handle) {
|
||||
err = ext4_handle_dirty_metadata(handle, NULL, bh);
|
||||
if (err)
|
||||
goto out_bh;
|
||||
} else {
|
||||
BUFFER_TRACE(bh, "marking dirty");
|
||||
mark_buffer_dirty(bh);
|
||||
}
|
||||
err = sync_dirty_buffer(bh);
|
||||
|
||||
out_bh:
|
||||
brelse(bh);
|
||||
ext4_std_error(sb, err);
|
||||
return (err) ? err : 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update primary and backup superblocks using the provided function
|
||||
* func and argument arg.
|
||||
*
|
||||
* Only the primary superblock and at most two backup superblock
|
||||
* modifications are journalled; the rest is modified without journal.
|
||||
* This is safe because e2fsck will re-write them if there is a problem,
|
||||
* and we're very unlikely to ever need more than two backups.
|
||||
*/
|
||||
static
|
||||
int ext4_update_superblocks_fn(struct super_block *sb,
|
||||
ext4_update_sb_callback func,
|
||||
const void *arg)
|
||||
{
|
||||
handle_t *handle;
|
||||
ext4_group_t ngroups;
|
||||
unsigned int three = 1;
|
||||
unsigned int five = 5;
|
||||
unsigned int seven = 7;
|
||||
int err = 0, ret, i;
|
||||
ext4_group_t grp, primary_grp;
|
||||
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
||||
|
||||
/*
|
||||
* We can't update superblocks while the online resize is running
|
||||
*/
|
||||
if (test_and_set_bit_lock(EXT4_FLAGS_RESIZING,
|
||||
&sbi->s_ext4_flags)) {
|
||||
ext4_msg(sb, KERN_ERR, "Can't modify superblock while"
|
||||
"performing online resize");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/*
|
||||
* We're only going to update primary superblock and two
|
||||
* backup superblocks in this transaction.
|
||||
*/
|
||||
handle = ext4_journal_start_sb(sb, EXT4_HT_MISC, 3);
|
||||
if (IS_ERR(handle)) {
|
||||
err = PTR_ERR(handle);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Update primary superblock */
|
||||
err = ext4_update_primary_sb(sb, handle, func, arg);
|
||||
if (err) {
|
||||
ext4_msg(sb, KERN_ERR, "Failed to update primary "
|
||||
"superblock");
|
||||
goto out_journal;
|
||||
}
|
||||
|
||||
primary_grp = ext4_get_group_number(sb, sbi->s_sbh->b_blocknr);
|
||||
ngroups = ext4_get_groups_count(sb);
|
||||
|
||||
/*
|
||||
* Update backup superblocks. We have to start from group 0
|
||||
* because it might not be where the primary superblock is
|
||||
* if the fs is mounted with -o sb=<backup_sb_block>
|
||||
*/
|
||||
i = 0;
|
||||
grp = 0;
|
||||
while (grp < ngroups) {
|
||||
/* Skip primary superblock */
|
||||
if (grp == primary_grp)
|
||||
goto next_grp;
|
||||
|
||||
ret = ext4_update_backup_sb(sb, handle, grp, func, arg);
|
||||
if (ret < 0) {
|
||||
/* Ignore bad checksum; try to update next sb */
|
||||
if (ret == -EFSBADCRC)
|
||||
goto next_grp;
|
||||
err = ret;
|
||||
goto out_journal;
|
||||
}
|
||||
|
||||
i += ret;
|
||||
if (handle && i > 1) {
|
||||
/*
|
||||
* We're only journalling primary superblock and
|
||||
* two backup superblocks; the rest is not
|
||||
* journalled.
|
||||
*/
|
||||
err = ext4_journal_stop(handle);
|
||||
if (err)
|
||||
goto out;
|
||||
handle = NULL;
|
||||
}
|
||||
next_grp:
|
||||
grp = ext4_list_backups(sb, &three, &five, &seven);
|
||||
}
|
||||
|
||||
out_journal:
|
||||
if (handle) {
|
||||
ret = ext4_journal_stop(handle);
|
||||
if (ret && !err)
|
||||
err = ret;
|
||||
}
|
||||
out:
|
||||
clear_bit_unlock(EXT4_FLAGS_RESIZING, &sbi->s_ext4_flags);
|
||||
smp_mb__after_atomic();
|
||||
return err ? err : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap memory between @a and @b for @len bytes.
|
||||
*
|
||||
@ -169,7 +411,7 @@ static long swap_inode_boot_loader(struct super_block *sb,
|
||||
err = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
ext4_fc_start_ineligible(sb, EXT4_FC_REASON_SWAP_BOOT);
|
||||
ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_SWAP_BOOT);
|
||||
|
||||
/* Protect extent tree against block allocations via delalloc */
|
||||
ext4_double_down_write_data_sem(inode, inode_bl);
|
||||
@ -252,7 +494,6 @@ revert:
|
||||
|
||||
err_out1:
|
||||
ext4_journal_stop(handle);
|
||||
ext4_fc_stop_ineligible(sb);
|
||||
ext4_double_up_write_data_sem(inode, inode_bl);
|
||||
|
||||
err_out:
|
||||
@ -743,7 +984,6 @@ int ext4_fileattr_set(struct user_namespace *mnt_userns,
|
||||
u32 flags = fa->flags;
|
||||
int err = -EOPNOTSUPP;
|
||||
|
||||
ext4_fc_start_update(inode);
|
||||
if (flags & ~EXT4_FL_USER_VISIBLE)
|
||||
goto out;
|
||||
|
||||
@ -764,7 +1004,6 @@ int ext4_fileattr_set(struct user_namespace *mnt_userns,
|
||||
goto out;
|
||||
err = ext4_ioctl_setproject(inode, fa->fsx_projid);
|
||||
out:
|
||||
ext4_fc_stop_update(inode);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -850,6 +1089,64 @@ static int ext4_ioctl_checkpoint(struct file *filp, unsigned long arg)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ext4_ioctl_setlabel(struct file *filp, const char __user *user_label)
|
||||
{
|
||||
size_t len;
|
||||
int ret = 0;
|
||||
char new_label[EXT4_LABEL_MAX + 1];
|
||||
struct super_block *sb = file_inode(filp)->i_sb;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
/*
|
||||
* Copy the maximum length allowed for ext4 label with one more to
|
||||
* find the required terminating null byte in order to test the
|
||||
* label length. The on disk label doesn't need to be null terminated.
|
||||
*/
|
||||
if (copy_from_user(new_label, user_label, EXT4_LABEL_MAX + 1))
|
||||
return -EFAULT;
|
||||
|
||||
len = strnlen(new_label, EXT4_LABEL_MAX + 1);
|
||||
if (len > EXT4_LABEL_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Clear the buffer after the new label
|
||||
*/
|
||||
memset(new_label + len, 0, EXT4_LABEL_MAX - len);
|
||||
|
||||
ret = mnt_want_write_file(filp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ext4_update_superblocks_fn(sb, ext4_sb_setlabel, new_label);
|
||||
|
||||
mnt_drop_write_file(filp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ext4_ioctl_getlabel(struct ext4_sb_info *sbi, char __user *user_label)
|
||||
{
|
||||
char label[EXT4_LABEL_MAX + 1];
|
||||
|
||||
/*
|
||||
* EXT4_LABEL_MAX must always be smaller than FSLABEL_MAX because
|
||||
* FSLABEL_MAX must include terminating null byte, while s_volume_name
|
||||
* does not have to.
|
||||
*/
|
||||
BUILD_BUG_ON(EXT4_LABEL_MAX >= FSLABEL_MAX);
|
||||
|
||||
memset(label, 0, sizeof(label));
|
||||
lock_buffer(sbi->s_sbh);
|
||||
strncpy(label, sbi->s_es->s_volume_name, EXT4_LABEL_MAX);
|
||||
unlock_buffer(sbi->s_sbh);
|
||||
|
||||
if (copy_to_user(user_label, label, sizeof(label)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
@ -1117,8 +1414,6 @@ resizefs_out:
|
||||
sizeof(range)))
|
||||
return -EFAULT;
|
||||
|
||||
range.minlen = max((unsigned int)range.minlen,
|
||||
q->limits.discard_granularity);
|
||||
ret = ext4_trim_fs(sb, &range);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -1266,6 +1561,13 @@ resizefs_out:
|
||||
case EXT4_IOC_CHECKPOINT:
|
||||
return ext4_ioctl_checkpoint(filp, arg);
|
||||
|
||||
case FS_IOC_GETFSLABEL:
|
||||
return ext4_ioctl_getlabel(EXT4_SB(sb), (void __user *)arg);
|
||||
|
||||
case FS_IOC_SETFSLABEL:
|
||||
return ext4_ioctl_setlabel(filp,
|
||||
(const void __user *)arg);
|
||||
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
@ -1273,13 +1575,7 @@ resizefs_out:
|
||||
|
||||
long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
long ret;
|
||||
|
||||
ext4_fc_start_update(file_inode(filp));
|
||||
ret = __ext4_ioctl(filp, cmd, arg);
|
||||
ext4_fc_stop_update(file_inode(filp));
|
||||
|
||||
return ret;
|
||||
return __ext4_ioctl(filp, cmd, arg);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
@ -1347,6 +1643,8 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
case EXT4_IOC_GETSTATE:
|
||||
case EXT4_IOC_GET_ES_CACHE:
|
||||
case EXT4_IOC_CHECKPOINT:
|
||||
case FS_IOC_GETFSLABEL:
|
||||
case FS_IOC_SETFSLABEL:
|
||||
break;
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
|
@ -4814,7 +4814,7 @@ ext4_mb_release_group_pa(struct ext4_buddy *e4b,
|
||||
*/
|
||||
static noinline_for_stack int
|
||||
ext4_mb_discard_group_preallocations(struct super_block *sb,
|
||||
ext4_group_t group, int needed)
|
||||
ext4_group_t group, int *busy)
|
||||
{
|
||||
struct ext4_group_info *grp = ext4_get_group_info(sb, group);
|
||||
struct buffer_head *bitmap_bh = NULL;
|
||||
@ -4822,8 +4822,7 @@ ext4_mb_discard_group_preallocations(struct super_block *sb,
|
||||
struct list_head list;
|
||||
struct ext4_buddy e4b;
|
||||
int err;
|
||||
int busy = 0;
|
||||
int free, free_total = 0;
|
||||
int free = 0;
|
||||
|
||||
mb_debug(sb, "discard preallocation for group %u\n", group);
|
||||
if (list_empty(&grp->bb_prealloc_list))
|
||||
@ -4846,19 +4845,14 @@ ext4_mb_discard_group_preallocations(struct super_block *sb,
|
||||
goto out_dbg;
|
||||
}
|
||||
|
||||
if (needed == 0)
|
||||
needed = EXT4_CLUSTERS_PER_GROUP(sb) + 1;
|
||||
|
||||
INIT_LIST_HEAD(&list);
|
||||
repeat:
|
||||
free = 0;
|
||||
ext4_lock_group(sb, group);
|
||||
list_for_each_entry_safe(pa, tmp,
|
||||
&grp->bb_prealloc_list, pa_group_list) {
|
||||
spin_lock(&pa->pa_lock);
|
||||
if (atomic_read(&pa->pa_count)) {
|
||||
spin_unlock(&pa->pa_lock);
|
||||
busy = 1;
|
||||
*busy = 1;
|
||||
continue;
|
||||
}
|
||||
if (pa->pa_deleted) {
|
||||
@ -4898,22 +4892,13 @@ repeat:
|
||||
call_rcu(&(pa)->u.pa_rcu, ext4_mb_pa_callback);
|
||||
}
|
||||
|
||||
free_total += free;
|
||||
|
||||
/* if we still need more blocks and some PAs were used, try again */
|
||||
if (free_total < needed && busy) {
|
||||
ext4_unlock_group(sb, group);
|
||||
cond_resched();
|
||||
busy = 0;
|
||||
goto repeat;
|
||||
}
|
||||
ext4_unlock_group(sb, group);
|
||||
ext4_mb_unload_buddy(&e4b);
|
||||
put_bh(bitmap_bh);
|
||||
out_dbg:
|
||||
mb_debug(sb, "discarded (%d) blocks preallocated for group %u bb_free (%d)\n",
|
||||
free_total, group, grp->bb_free);
|
||||
return free_total;
|
||||
free, group, grp->bb_free);
|
||||
return free;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -5455,13 +5440,24 @@ static int ext4_mb_discard_preallocations(struct super_block *sb, int needed)
|
||||
{
|
||||
ext4_group_t i, ngroups = ext4_get_groups_count(sb);
|
||||
int ret;
|
||||
int freed = 0;
|
||||
int freed = 0, busy = 0;
|
||||
int retry = 0;
|
||||
|
||||
trace_ext4_mb_discard_preallocations(sb, needed);
|
||||
|
||||
if (needed == 0)
|
||||
needed = EXT4_CLUSTERS_PER_GROUP(sb) + 1;
|
||||
repeat:
|
||||
for (i = 0; i < ngroups && needed > 0; i++) {
|
||||
ret = ext4_mb_discard_group_preallocations(sb, i, needed);
|
||||
ret = ext4_mb_discard_group_preallocations(sb, i, &busy);
|
||||
freed += ret;
|
||||
needed -= ret;
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
if (needed > 0 && busy && ++retry < 3) {
|
||||
busy = 0;
|
||||
goto repeat;
|
||||
}
|
||||
|
||||
return freed;
|
||||
@ -6373,7 +6369,7 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group,
|
||||
ext4_lock_group(sb, group);
|
||||
|
||||
if (!EXT4_MB_GRP_WAS_TRIMMED(e4b.bd_info) ||
|
||||
minblocks < atomic_read(&EXT4_SB(sb)->s_last_trim_minblks)) {
|
||||
minblocks < EXT4_SB(sb)->s_last_trim_minblks) {
|
||||
ret = ext4_try_to_trim_range(sb, &e4b, start, max, minblocks);
|
||||
if (ret >= 0)
|
||||
EXT4_MB_GRP_SET_TRIMMED(e4b.bd_info);
|
||||
@ -6404,6 +6400,7 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group,
|
||||
*/
|
||||
int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range)
|
||||
{
|
||||
struct request_queue *q = bdev_get_queue(sb->s_bdev);
|
||||
struct ext4_group_info *grp;
|
||||
ext4_group_t group, first_group, last_group;
|
||||
ext4_grpblk_t cnt = 0, first_cluster, last_cluster;
|
||||
@ -6422,6 +6419,13 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range)
|
||||
start >= max_blks ||
|
||||
range->len < sb->s_blocksize)
|
||||
return -EINVAL;
|
||||
/* No point to try to trim less than discard granularity */
|
||||
if (range->minlen < q->limits.discard_granularity) {
|
||||
minlen = EXT4_NUM_B2C(EXT4_SB(sb),
|
||||
q->limits.discard_granularity >> sb->s_blocksize_bits);
|
||||
if (minlen > EXT4_CLUSTERS_PER_GROUP(sb))
|
||||
goto out;
|
||||
}
|
||||
if (end >= max_blks)
|
||||
end = max_blks - 1;
|
||||
if (end <= first_data_blk)
|
||||
@ -6474,7 +6478,7 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range)
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
atomic_set(&EXT4_SB(sb)->s_last_trim_minblks, minlen);
|
||||
EXT4_SB(sb)->s_last_trim_minblks = minlen;
|
||||
|
||||
out:
|
||||
range->len = EXT4_C2B(EXT4_SB(sb), trimmed) << sb->s_blocksize_bits;
|
||||
|
@ -437,12 +437,12 @@ int ext4_ext_migrate(struct inode *inode)
|
||||
percpu_down_write(&sbi->s_writepages_rwsem);
|
||||
|
||||
/*
|
||||
* Worst case we can touch the allocation bitmaps, a bgd
|
||||
* block, and a block to link in the orphan list. We do need
|
||||
* need to worry about credits for modifying the quota inode.
|
||||
* Worst case we can touch the allocation bitmaps and a block
|
||||
* group descriptor block. We do need need to worry about
|
||||
* credits for modifying the quota inode.
|
||||
*/
|
||||
handle = ext4_journal_start(inode, EXT4_HT_MIGRATE,
|
||||
4 + EXT4_MAXQUOTAS_TRANS_BLOCKS(inode->i_sb));
|
||||
3 + EXT4_MAXQUOTAS_TRANS_BLOCKS(inode->i_sb));
|
||||
|
||||
if (IS_ERR(handle)) {
|
||||
retval = PTR_ERR(handle);
|
||||
@ -459,6 +459,13 @@ int ext4_ext_migrate(struct inode *inode)
|
||||
ext4_journal_stop(handle);
|
||||
goto out_unlock;
|
||||
}
|
||||
/*
|
||||
* Use the correct seed for checksum (i.e. the seed from 'inode'). This
|
||||
* is so that the metadata blocks will have the correct checksum after
|
||||
* the migration.
|
||||
*/
|
||||
ei = EXT4_I(inode);
|
||||
EXT4_I(tmp_inode)->i_csum_seed = ei->i_csum_seed;
|
||||
i_size_write(tmp_inode, i_size_read(inode));
|
||||
/*
|
||||
* Set the i_nlink to zero so it will be deleted later
|
||||
@ -467,7 +474,6 @@ int ext4_ext_migrate(struct inode *inode)
|
||||
clear_nlink(tmp_inode);
|
||||
|
||||
ext4_ext_tree_init(handle, tmp_inode);
|
||||
ext4_orphan_add(handle, tmp_inode);
|
||||
ext4_journal_stop(handle);
|
||||
|
||||
/*
|
||||
@ -492,17 +498,10 @@ int ext4_ext_migrate(struct inode *inode)
|
||||
|
||||
handle = ext4_journal_start(inode, EXT4_HT_MIGRATE, 1);
|
||||
if (IS_ERR(handle)) {
|
||||
/*
|
||||
* It is impossible to update on-disk structures without
|
||||
* a handle, so just rollback in-core changes and live other
|
||||
* work to orphan_list_cleanup()
|
||||
*/
|
||||
ext4_orphan_del(NULL, tmp_inode);
|
||||
retval = PTR_ERR(handle);
|
||||
goto out_tmp_inode;
|
||||
}
|
||||
|
||||
ei = EXT4_I(inode);
|
||||
i_data = ei->i_data;
|
||||
memset(&lb, 0, sizeof(lb));
|
||||
|
||||
|
@ -632,7 +632,6 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, __u64 orig_blk,
|
||||
/* Check hole before the start pos */
|
||||
if (cur_blk + cur_len - 1 < o_start) {
|
||||
if (next_blk == EXT_MAX_BLOCKS) {
|
||||
o_start = o_end;
|
||||
ret = -ENODATA;
|
||||
goto out;
|
||||
}
|
||||
|
@ -717,12 +717,23 @@ out:
|
||||
* sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ...
|
||||
* For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ...
|
||||
*/
|
||||
static unsigned ext4_list_backups(struct super_block *sb, unsigned *three,
|
||||
unsigned *five, unsigned *seven)
|
||||
unsigned int ext4_list_backups(struct super_block *sb, unsigned int *three,
|
||||
unsigned int *five, unsigned int *seven)
|
||||
{
|
||||
unsigned *min = three;
|
||||
struct ext4_super_block *es = EXT4_SB(sb)->s_es;
|
||||
unsigned int *min = three;
|
||||
int mult = 3;
|
||||
unsigned ret;
|
||||
unsigned int ret;
|
||||
|
||||
if (ext4_has_feature_sparse_super2(sb)) {
|
||||
do {
|
||||
if (*min > 2)
|
||||
return UINT_MAX;
|
||||
ret = le32_to_cpu(es->s_backup_bgs[*min - 1]);
|
||||
*min += 1;
|
||||
} while (!ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!ext4_has_feature_sparse_super(sb)) {
|
||||
ret = *min;
|
||||
|
2097
fs/ext4/super.c
2097
fs/ext4/super.c
File diff suppressed because it is too large
Load Diff
@ -63,7 +63,7 @@ static ssize_t session_write_kbytes_show(struct ext4_sb_info *sbi, char *buf)
|
||||
{
|
||||
struct super_block *sb = sbi->s_buddy_cache->i_sb;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%lu\n",
|
||||
return sysfs_emit(buf, "%lu\n",
|
||||
(part_stat_read(sb->s_bdev, sectors[STAT_WRITE]) -
|
||||
sbi->s_sectors_written_start) >> 1);
|
||||
}
|
||||
@ -72,7 +72,7 @@ static ssize_t lifetime_write_kbytes_show(struct ext4_sb_info *sbi, char *buf)
|
||||
{
|
||||
struct super_block *sb = sbi->s_buddy_cache->i_sb;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%llu\n",
|
||||
return sysfs_emit(buf, "%llu\n",
|
||||
(unsigned long long)(sbi->s_kbytes_written +
|
||||
((part_stat_read(sb->s_bdev, sectors[STAT_WRITE]) -
|
||||
EXT4_SB(sb)->s_sectors_written_start) >> 1)));
|
||||
@ -130,8 +130,8 @@ static ssize_t trigger_test_error(struct ext4_sb_info *sbi,
|
||||
static ssize_t journal_task_show(struct ext4_sb_info *sbi, char *buf)
|
||||
{
|
||||
if (!sbi->s_journal)
|
||||
return snprintf(buf, PAGE_SIZE, "<none>\n");
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
return sysfs_emit(buf, "<none>\n");
|
||||
return sysfs_emit(buf, "%d\n",
|
||||
task_pid_vnr(sbi->s_journal->j_task));
|
||||
}
|
||||
|
||||
@ -245,6 +245,7 @@ EXT4_ATTR(last_error_time, 0444, last_error_time);
|
||||
EXT4_ATTR(journal_task, 0444, journal_task);
|
||||
EXT4_RW_ATTR_SBI_UI(mb_prefetch, s_mb_prefetch);
|
||||
EXT4_RW_ATTR_SBI_UI(mb_prefetch_limit, s_mb_prefetch_limit);
|
||||
EXT4_RW_ATTR_SBI_UL(last_trim_minblks, s_last_trim_minblks);
|
||||
|
||||
static unsigned int old_bump_val = 128;
|
||||
EXT4_ATTR_PTR(max_writeback_mb_bump, 0444, pointer_ui, &old_bump_val);
|
||||
@ -295,6 +296,7 @@ static struct attribute *ext4_attrs[] = {
|
||||
#endif
|
||||
ATTR_LIST(mb_prefetch),
|
||||
ATTR_LIST(mb_prefetch_limit),
|
||||
ATTR_LIST(last_trim_minblks),
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(ext4);
|
||||
@ -357,7 +359,7 @@ static void *calc_ptr(struct ext4_attr *a, struct ext4_sb_info *sbi)
|
||||
|
||||
static ssize_t __print_tstamp(char *buf, __le32 lo, __u8 hi)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%lld\n",
|
||||
return sysfs_emit(buf, "%lld\n",
|
||||
((time64_t)hi << 32) + le32_to_cpu(lo));
|
||||
}
|
||||
|
||||
@ -374,7 +376,7 @@ static ssize_t ext4_attr_show(struct kobject *kobj,
|
||||
|
||||
switch (a->attr_id) {
|
||||
case attr_delayed_allocation_blocks:
|
||||
return snprintf(buf, PAGE_SIZE, "%llu\n",
|
||||
return sysfs_emit(buf, "%llu\n",
|
||||
(s64) EXT4_C2B(sbi,
|
||||
percpu_counter_sum(&sbi->s_dirtyclusters_counter)));
|
||||
case attr_session_write_kbytes:
|
||||
@ -382,11 +384,11 @@ static ssize_t ext4_attr_show(struct kobject *kobj,
|
||||
case attr_lifetime_write_kbytes:
|
||||
return lifetime_write_kbytes_show(sbi, buf);
|
||||
case attr_reserved_clusters:
|
||||
return snprintf(buf, PAGE_SIZE, "%llu\n",
|
||||
return sysfs_emit(buf, "%llu\n",
|
||||
(unsigned long long)
|
||||
atomic64_read(&sbi->s_resv_clusters));
|
||||
case attr_sra_exceeded_retry_limit:
|
||||
return snprintf(buf, PAGE_SIZE, "%llu\n",
|
||||
return sysfs_emit(buf, "%llu\n",
|
||||
(unsigned long long)
|
||||
percpu_counter_sum(&sbi->s_sra_exceeded_retry_limit));
|
||||
case attr_inode_readahead:
|
||||
@ -394,42 +396,42 @@ static ssize_t ext4_attr_show(struct kobject *kobj,
|
||||
if (!ptr)
|
||||
return 0;
|
||||
if (a->attr_ptr == ptr_ext4_super_block_offset)
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||
return sysfs_emit(buf, "%u\n",
|
||||
le32_to_cpup(ptr));
|
||||
else
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||
return sysfs_emit(buf, "%u\n",
|
||||
*((unsigned int *) ptr));
|
||||
case attr_pointer_ul:
|
||||
if (!ptr)
|
||||
return 0;
|
||||
return snprintf(buf, PAGE_SIZE, "%lu\n",
|
||||
return sysfs_emit(buf, "%lu\n",
|
||||
*((unsigned long *) ptr));
|
||||
case attr_pointer_u8:
|
||||
if (!ptr)
|
||||
return 0;
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||
return sysfs_emit(buf, "%u\n",
|
||||
*((unsigned char *) ptr));
|
||||
case attr_pointer_u64:
|
||||
if (!ptr)
|
||||
return 0;
|
||||
if (a->attr_ptr == ptr_ext4_super_block_offset)
|
||||
return snprintf(buf, PAGE_SIZE, "%llu\n",
|
||||
return sysfs_emit(buf, "%llu\n",
|
||||
le64_to_cpup(ptr));
|
||||
else
|
||||
return snprintf(buf, PAGE_SIZE, "%llu\n",
|
||||
return sysfs_emit(buf, "%llu\n",
|
||||
*((unsigned long long *) ptr));
|
||||
case attr_pointer_string:
|
||||
if (!ptr)
|
||||
return 0;
|
||||
return snprintf(buf, PAGE_SIZE, "%.*s\n", a->attr_size,
|
||||
return sysfs_emit(buf, "%.*s\n", a->attr_size,
|
||||
(char *) ptr);
|
||||
case attr_pointer_atomic:
|
||||
if (!ptr)
|
||||
return 0;
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
return sysfs_emit(buf, "%d\n",
|
||||
atomic_read((atomic_t *) ptr));
|
||||
case attr_feature:
|
||||
return snprintf(buf, PAGE_SIZE, "supported\n");
|
||||
return sysfs_emit(buf, "supported\n");
|
||||
case attr_first_error_time:
|
||||
return print_tstamp(buf, sbi->s_es, s_first_error_time);
|
||||
case attr_last_error_time:
|
||||
|
@ -199,6 +199,8 @@ int fs_param_is_bool(struct p_log *log, const struct fs_parameter_spec *p,
|
||||
int b;
|
||||
if (param->type != fs_value_is_string)
|
||||
return fs_param_bad_value(log, param);
|
||||
if (!*param->string && (p->flags & fs_param_can_be_empty))
|
||||
return 0;
|
||||
b = lookup_constant(bool_names, param->string, -1);
|
||||
if (b == -1)
|
||||
return fs_param_bad_value(log, param);
|
||||
@ -211,8 +213,11 @@ int fs_param_is_u32(struct p_log *log, const struct fs_parameter_spec *p,
|
||||
struct fs_parameter *param, struct fs_parse_result *result)
|
||||
{
|
||||
int base = (unsigned long)p->data;
|
||||
if (param->type != fs_value_is_string ||
|
||||
kstrtouint(param->string, base, &result->uint_32) < 0)
|
||||
if (param->type != fs_value_is_string)
|
||||
return fs_param_bad_value(log, param);
|
||||
if (!*param->string && (p->flags & fs_param_can_be_empty))
|
||||
return 0;
|
||||
if (kstrtouint(param->string, base, &result->uint_32) < 0)
|
||||
return fs_param_bad_value(log, param);
|
||||
return 0;
|
||||
}
|
||||
@ -221,8 +226,11 @@ EXPORT_SYMBOL(fs_param_is_u32);
|
||||
int fs_param_is_s32(struct p_log *log, const struct fs_parameter_spec *p,
|
||||
struct fs_parameter *param, struct fs_parse_result *result)
|
||||
{
|
||||
if (param->type != fs_value_is_string ||
|
||||
kstrtoint(param->string, 0, &result->int_32) < 0)
|
||||
if (param->type != fs_value_is_string)
|
||||
return fs_param_bad_value(log, param);
|
||||
if (!*param->string && (p->flags & fs_param_can_be_empty))
|
||||
return 0;
|
||||
if (kstrtoint(param->string, 0, &result->int_32) < 0)
|
||||
return fs_param_bad_value(log, param);
|
||||
return 0;
|
||||
}
|
||||
@ -231,8 +239,11 @@ EXPORT_SYMBOL(fs_param_is_s32);
|
||||
int fs_param_is_u64(struct p_log *log, const struct fs_parameter_spec *p,
|
||||
struct fs_parameter *param, struct fs_parse_result *result)
|
||||
{
|
||||
if (param->type != fs_value_is_string ||
|
||||
kstrtoull(param->string, 0, &result->uint_64) < 0)
|
||||
if (param->type != fs_value_is_string)
|
||||
return fs_param_bad_value(log, param);
|
||||
if (!*param->string && (p->flags & fs_param_can_be_empty))
|
||||
return 0;
|
||||
if (kstrtoull(param->string, 0, &result->uint_64) < 0)
|
||||
return fs_param_bad_value(log, param);
|
||||
return 0;
|
||||
}
|
||||
@ -244,6 +255,8 @@ int fs_param_is_enum(struct p_log *log, const struct fs_parameter_spec *p,
|
||||
const struct constant_table *c;
|
||||
if (param->type != fs_value_is_string)
|
||||
return fs_param_bad_value(log, param);
|
||||
if (!*param->string && (p->flags & fs_param_can_be_empty))
|
||||
return 0;
|
||||
c = __lookup_constant(p->data, param->string);
|
||||
if (!c)
|
||||
return fs_param_bad_value(log, param);
|
||||
@ -255,7 +268,8 @@ EXPORT_SYMBOL(fs_param_is_enum);
|
||||
int fs_param_is_string(struct p_log *log, const struct fs_parameter_spec *p,
|
||||
struct fs_parameter *param, struct fs_parse_result *result)
|
||||
{
|
||||
if (param->type != fs_value_is_string || !*param->string)
|
||||
if (param->type != fs_value_is_string ||
|
||||
(!*param->string && !(p->flags & fs_param_can_be_empty)))
|
||||
return fs_param_bad_value(log, param);
|
||||
return 0;
|
||||
}
|
||||
@ -275,7 +289,8 @@ int fs_param_is_fd(struct p_log *log, const struct fs_parameter_spec *p,
|
||||
{
|
||||
switch (param->type) {
|
||||
case fs_value_is_string:
|
||||
if (kstrtouint(param->string, 0, &result->uint_32) < 0)
|
||||
if ((!*param->string && !(p->flags & fs_param_can_be_empty)) ||
|
||||
kstrtouint(param->string, 0, &result->uint_32) < 0)
|
||||
break;
|
||||
if (result->uint_32 <= INT_MAX)
|
||||
return 0;
|
||||
|
@ -757,6 +757,7 @@ int jbd2_fc_begin_commit(journal_t *journal, tid_t tid)
|
||||
}
|
||||
journal->j_flags |= JBD2_FAST_COMMIT_ONGOING;
|
||||
write_unlock(&journal->j_state_lock);
|
||||
jbd2_journal_lock_updates(journal);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -768,6 +769,7 @@ EXPORT_SYMBOL(jbd2_fc_begin_commit);
|
||||
*/
|
||||
static int __jbd2_fc_end_commit(journal_t *journal, tid_t tid, bool fallback)
|
||||
{
|
||||
jbd2_journal_unlock_updates(journal);
|
||||
if (journal->j_fc_cleanup_callback)
|
||||
journal->j_fc_cleanup_callback(journal, 0);
|
||||
write_lock(&journal->j_state_lock);
|
||||
|
@ -42,7 +42,7 @@ struct fs_parameter_spec {
|
||||
u8 opt; /* Option number (returned by fs_parse()) */
|
||||
unsigned short flags;
|
||||
#define fs_param_neg_with_no 0x0002 /* "noxxx" is negative param */
|
||||
#define fs_param_neg_with_empty 0x0004 /* "xxx=" is negative param */
|
||||
#define fs_param_can_be_empty 0x0004 /* "xxx=" is allowed */
|
||||
#define fs_param_deprecated 0x0008 /* The param is deprecated */
|
||||
const void *data;
|
||||
};
|
||||
|
@ -2837,6 +2837,29 @@ TRACE_EVENT(ext4_fc_track_range,
|
||||
__entry->end)
|
||||
);
|
||||
|
||||
TRACE_EVENT(ext4_update_sb,
|
||||
TP_PROTO(struct super_block *sb, ext4_fsblk_t fsblk,
|
||||
unsigned int flags),
|
||||
|
||||
TP_ARGS(sb, fsblk, flags),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(ext4_fsblk_t, fsblk)
|
||||
__field(unsigned int, flags)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = sb->s_dev;
|
||||
__entry->fsblk = fsblk;
|
||||
__entry->flags = flags;
|
||||
),
|
||||
|
||||
TP_printk("dev %d,%d fsblk %llu flags %u",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
__entry->fsblk, __entry->flags)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_EXT4_H */
|
||||
|
||||
/* This part must be outside protection */
|
||||
|
Loading…
Reference in New Issue
Block a user