Commit 173f8654 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull ext4 updates from Ted Ts'o:
 "The usual collection of bug fixes and optimizations.  Perhaps of
  greatest note is a speed up for parallel, non-allocating DIO writes,
  since we no longer take the i_mutex lock in that case.

  For bug fixes, we fix an incorrect overhead calculation which caused
  slightly incorrect results for df(1) and statfs(2).  We also fixed
  bugs in the metadata checksum feature."

* tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (23 commits)
  ext4: undo ext4_calc_metadata_amount if we fail to claim space
  ext4: don't let i_reserved_meta_blocks go negative
  ext4: fix hole punch failure when depth is greater than 0
  ext4: remove unnecessary argument from __ext4_handle_dirty_metadata()
  ext4: weed out ext4_write_super
  ext4: remove unnecessary superblock dirtying
  ext4: convert last user of ext4_mark_super_dirty() to ext4_handle_dirty_super()
  ext4: remove useless marking of superblock dirty
  ext4: fix ext4 mismerge back in January
  ext4: remove dynamic array size in ext4_chksum()
  ext4: remove unused variable in ext4_update_super()
  ext4: make quota as first class supported feature
  ext4: don't take the i_mutex lock when doing DIO overwrites
  ext4: add a new nolock flag in ext4_map_blocks
  ext4: split ext4_file_write into buffered IO and direct IO
  ext4: remove an unused statement in ext4_mb_get_buddy_page_lock()
  ext4: fix out-of-date comments in extents.c
  ext4: use s_csum_seed instead of i_csum_seed for xattr block
  ext4: use proper csum calculation in ext4_rename
  ext4: fix overhead calculation used by ext4_statfs()
  ...
parents cea8f46c 03179fe9
...@@ -609,7 +609,8 @@ ext4_fsblk_t ext4_count_free_clusters(struct super_block *sb) ...@@ -609,7 +609,8 @@ ext4_fsblk_t ext4_count_free_clusters(struct super_block *sb)
if (bitmap_bh == NULL) if (bitmap_bh == NULL)
continue; continue;
x = ext4_count_free(bitmap_bh, sb->s_blocksize); x = ext4_count_free(bitmap_bh->b_data,
EXT4_BLOCKS_PER_GROUP(sb) / 8);
printk(KERN_DEBUG "group %u: stored = %d, counted = %u\n", printk(KERN_DEBUG "group %u: stored = %d, counted = %u\n",
i, ext4_free_group_clusters(sb, gdp), x); i, ext4_free_group_clusters(sb, gdp), x);
bitmap_count += x; bitmap_count += x;
......
...@@ -11,24 +11,18 @@ ...@@ -11,24 +11,18 @@
#include <linux/jbd2.h> #include <linux/jbd2.h>
#include "ext4.h" #include "ext4.h"
#ifdef EXT4FS_DEBUG
static const int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0}; static const int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0};
unsigned int ext4_count_free(struct buffer_head *map, unsigned int numchars) unsigned int ext4_count_free(char *bitmap, unsigned int numchars)
{ {
unsigned int i, sum = 0; unsigned int i, sum = 0;
if (!map)
return 0;
for (i = 0; i < numchars; i++) for (i = 0; i < numchars; i++)
sum += nibblemap[map->b_data[i] & 0xf] + sum += nibblemap[bitmap[i] & 0xf] +
nibblemap[(map->b_data[i] >> 4) & 0xf]; nibblemap[(bitmap[i] >> 4) & 0xf];
return sum; return sum;
} }
#endif /* EXT4FS_DEBUG */
int ext4_inode_bitmap_csum_verify(struct super_block *sb, ext4_group_t group, int ext4_inode_bitmap_csum_verify(struct super_block *sb, ext4_group_t group,
struct ext4_group_desc *gdp, struct ext4_group_desc *gdp,
struct buffer_head *bh, int sz) struct buffer_head *bh, int sz)
......
...@@ -571,6 +571,8 @@ enum { ...@@ -571,6 +571,8 @@ enum {
#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) */ /* Request will not result in inode size update (user for fallocate) */
#define EXT4_GET_BLOCKS_KEEP_SIZE 0x0080 #define EXT4_GET_BLOCKS_KEEP_SIZE 0x0080
/* Do not take i_data_sem locking in ext4_map_blocks */
#define EXT4_GET_BLOCKS_NO_LOCK 0x0100
/* /*
* Flags used by ext4_free_blocks * Flags used by ext4_free_blocks
...@@ -1161,8 +1163,7 @@ struct ext4_sb_info { ...@@ -1161,8 +1163,7 @@ struct ext4_sb_info {
unsigned long s_desc_per_block; /* Number of group descriptors per block */ unsigned long s_desc_per_block; /* Number of group descriptors per block */
ext4_group_t s_groups_count; /* Number of groups in the fs */ ext4_group_t s_groups_count; /* Number of groups in the fs */
ext4_group_t s_blockfile_groups;/* Groups acceptable for non-extent files */ ext4_group_t s_blockfile_groups;/* Groups acceptable for non-extent files */
unsigned long s_overhead_last; /* Last calculated overhead */ unsigned long s_overhead; /* # of fs overhead clusters */
unsigned long s_blocks_last; /* Last seen block count */
unsigned int s_cluster_ratio; /* Number of blocks per cluster */ unsigned int s_cluster_ratio; /* Number of blocks per cluster */
unsigned int s_cluster_bits; /* log2 of s_cluster_ratio */ unsigned int s_cluster_bits; /* log2 of s_cluster_ratio */
loff_t s_bitmap_maxbytes; /* max bytes for bitmap files */ loff_t s_bitmap_maxbytes; /* max bytes for bitmap files */
...@@ -1314,6 +1315,8 @@ static inline struct timespec ext4_current_time(struct inode *inode) ...@@ -1314,6 +1315,8 @@ static inline struct timespec ext4_current_time(struct inode *inode)
static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino) static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
{ {
return ino == EXT4_ROOT_INO || return ino == EXT4_ROOT_INO ||
ino == EXT4_USR_QUOTA_INO ||
ino == EXT4_GRP_QUOTA_INO ||
ino == EXT4_JOURNAL_INO || ino == EXT4_JOURNAL_INO ||
ino == EXT4_RESIZE_INO || ino == EXT4_RESIZE_INO ||
(ino >= EXT4_FIRST_INO(sb) && (ino >= EXT4_FIRST_INO(sb) &&
...@@ -1496,7 +1499,8 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei) ...@@ -1496,7 +1499,8 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
EXT4_FEATURE_RO_COMPAT_BTREE_DIR |\ EXT4_FEATURE_RO_COMPAT_BTREE_DIR |\
EXT4_FEATURE_RO_COMPAT_HUGE_FILE |\ EXT4_FEATURE_RO_COMPAT_HUGE_FILE |\
EXT4_FEATURE_RO_COMPAT_BIGALLOC |\ EXT4_FEATURE_RO_COMPAT_BIGALLOC |\
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) EXT4_FEATURE_RO_COMPAT_METADATA_CSUM|\
EXT4_FEATURE_RO_COMPAT_QUOTA)
/* /*
* Default values for user and/or group using reserved blocks * Default values for user and/or group using reserved blocks
...@@ -1663,10 +1667,12 @@ static inline u32 ext4_chksum(struct ext4_sb_info *sbi, u32 crc, ...@@ -1663,10 +1667,12 @@ static inline u32 ext4_chksum(struct ext4_sb_info *sbi, u32 crc,
{ {
struct { struct {
struct shash_desc shash; struct shash_desc shash;
char ctx[crypto_shash_descsize(sbi->s_chksum_driver)]; char ctx[4];
} desc; } desc;
int err; int err;
BUG_ON(crypto_shash_descsize(sbi->s_chksum_driver)!=sizeof(desc.ctx));
desc.shash.tfm = sbi->s_chksum_driver; desc.shash.tfm = sbi->s_chksum_driver;
desc.shash.flags = 0; desc.shash.flags = 0;
*(u32 *)desc.ctx = crc; *(u32 *)desc.ctx = crc;
...@@ -1852,7 +1858,7 @@ struct mmpd_data { ...@@ -1852,7 +1858,7 @@ struct mmpd_data {
# define NORET_AND noreturn, # define NORET_AND noreturn,
/* bitmap.c */ /* bitmap.c */
extern unsigned int ext4_count_free(struct buffer_head *, unsigned); extern unsigned int ext4_count_free(char *bitmap, unsigned numchars);
void ext4_inode_bitmap_csum_set(struct super_block *sb, ext4_group_t group, void ext4_inode_bitmap_csum_set(struct super_block *sb, ext4_group_t group,
struct ext4_group_desc *gdp, struct ext4_group_desc *gdp,
struct buffer_head *bh, int sz); struct buffer_head *bh, int sz);
...@@ -2037,6 +2043,7 @@ extern int ext4_group_extend(struct super_block *sb, ...@@ -2037,6 +2043,7 @@ extern int ext4_group_extend(struct super_block *sb,
extern int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count); extern int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count);
/* super.c */ /* super.c */
extern int ext4_calculate_overhead(struct super_block *sb);
extern int ext4_superblock_csum_verify(struct super_block *sb, extern int ext4_superblock_csum_verify(struct super_block *sb,
struct ext4_super_block *es); struct ext4_super_block *es);
extern void ext4_superblock_csum_set(struct super_block *sb, extern void ext4_superblock_csum_set(struct super_block *sb,
...@@ -2321,15 +2328,6 @@ static inline void ext4_unlock_group(struct super_block *sb, ...@@ -2321,15 +2328,6 @@ static inline void ext4_unlock_group(struct super_block *sb,
spin_unlock(ext4_group_lock_ptr(sb, group)); spin_unlock(ext4_group_lock_ptr(sb, group));
} }
static inline void ext4_mark_super_dirty(struct super_block *sb)
{
struct ext4_super_block *es = EXT4_SB(sb)->s_es;
ext4_superblock_csum_set(sb, es);
if (EXT4_SB(sb)->s_journal == NULL)
sb->s_dirt =1;
}
/* /*
* Block validity checking * Block validity checking
*/ */
......
...@@ -138,8 +138,7 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line, ...@@ -138,8 +138,7 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
} }
int __ext4_handle_dirty_super(const char *where, unsigned int line, int __ext4_handle_dirty_super(const char *where, unsigned int line,
handle_t *handle, struct super_block *sb, handle_t *handle, struct super_block *sb)
int now)
{ {
struct buffer_head *bh = EXT4_SB(sb)->s_sbh; struct buffer_head *bh = EXT4_SB(sb)->s_sbh;
int err = 0; int err = 0;
...@@ -151,11 +150,10 @@ int __ext4_handle_dirty_super(const char *where, unsigned int line, ...@@ -151,11 +150,10 @@ int __ext4_handle_dirty_super(const char *where, unsigned int line,
if (err) if (err)
ext4_journal_abort_handle(where, line, __func__, ext4_journal_abort_handle(where, line, __func__,
bh, handle, err); bh, handle, err);
} else if (now) { } else {
ext4_superblock_csum_set(sb, ext4_superblock_csum_set(sb,
(struct ext4_super_block *)bh->b_data); (struct ext4_super_block *)bh->b_data);
mark_buffer_dirty(bh); mark_buffer_dirty(bh);
} else }
sb->s_dirt = 1;
return err; return err;
} }
...@@ -87,14 +87,20 @@ ...@@ -87,14 +87,20 @@
#ifdef CONFIG_QUOTA #ifdef CONFIG_QUOTA
/* Amount of blocks needed for quota update - we know that the structure was /* Amount of blocks needed for quota update - we know that the structure was
* allocated so we need to update only data block */ * allocated so we need to update only data block */
#define EXT4_QUOTA_TRANS_BLOCKS(sb) (test_opt(sb, QUOTA) ? 1 : 0) #define EXT4_QUOTA_TRANS_BLOCKS(sb) ((test_opt(sb, QUOTA) ||\
EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA)) ?\
1 : 0)
/* Amount of blocks needed for quota insert/delete - we do some block writes /* Amount of blocks needed for quota insert/delete - we do some block writes
* but inode, sb and group updates are done only once */ * but inode, sb and group updates are done only once */
#define EXT4_QUOTA_INIT_BLOCKS(sb) (test_opt(sb, QUOTA) ? (DQUOT_INIT_ALLOC*\ #define EXT4_QUOTA_INIT_BLOCKS(sb) ((test_opt(sb, QUOTA) ||\
(EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)+3+DQUOT_INIT_REWRITE) : 0) EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA)) ?\
(DQUOT_INIT_ALLOC*(EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)\
#define EXT4_QUOTA_DEL_BLOCKS(sb) (test_opt(sb, QUOTA) ? (DQUOT_DEL_ALLOC*\ +3+DQUOT_INIT_REWRITE) : 0)
(EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)+3+DQUOT_DEL_REWRITE) : 0)
#define EXT4_QUOTA_DEL_BLOCKS(sb) ((test_opt(sb, QUOTA) ||\
EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA)) ?\
(DQUOT_DEL_ALLOC*(EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)\
+3+DQUOT_DEL_REWRITE) : 0)
#else #else
#define EXT4_QUOTA_TRANS_BLOCKS(sb) 0 #define EXT4_QUOTA_TRANS_BLOCKS(sb) 0
#define EXT4_QUOTA_INIT_BLOCKS(sb) 0 #define EXT4_QUOTA_INIT_BLOCKS(sb) 0
...@@ -213,8 +219,7 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line, ...@@ -213,8 +219,7 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
struct buffer_head *bh); struct buffer_head *bh);
int __ext4_handle_dirty_super(const char *where, unsigned int line, int __ext4_handle_dirty_super(const char *where, unsigned int line,
handle_t *handle, struct super_block *sb, handle_t *handle, struct super_block *sb);
int now);
#define ext4_journal_get_write_access(handle, bh) \ #define ext4_journal_get_write_access(handle, bh) \
__ext4_journal_get_write_access(__func__, __LINE__, (handle), (bh)) __ext4_journal_get_write_access(__func__, __LINE__, (handle), (bh))
...@@ -226,10 +231,8 @@ int __ext4_handle_dirty_super(const char *where, unsigned int line, ...@@ -226,10 +231,8 @@ int __ext4_handle_dirty_super(const char *where, unsigned int line,
#define ext4_handle_dirty_metadata(handle, inode, bh) \ #define ext4_handle_dirty_metadata(handle, inode, bh) \
__ext4_handle_dirty_metadata(__func__, __LINE__, (handle), (inode), \ __ext4_handle_dirty_metadata(__func__, __LINE__, (handle), (inode), \
(bh)) (bh))
#define ext4_handle_dirty_super_now(handle, sb) \
__ext4_handle_dirty_super(__func__, __LINE__, (handle), (sb), 1)
#define ext4_handle_dirty_super(handle, sb) \ #define ext4_handle_dirty_super(handle, sb) \
__ext4_handle_dirty_super(__func__, __LINE__, (handle), (sb), 0) __ext4_handle_dirty_super(__func__, __LINE__, (handle), (sb))
handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks); handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks);
int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle); int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle);
......
...@@ -1891,11 +1891,10 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode, ...@@ -1891,11 +1891,10 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
nearex->ee_len = newext->ee_len; nearex->ee_len = newext->ee_len;
merge: merge:
/* try to merge extents to the right */ /* try to merge extents */
if (!(flag & EXT4_GET_BLOCKS_PRE_IO)) if (!(flag & EXT4_GET_BLOCKS_PRE_IO))
ext4_ext_try_to_merge(inode, path, nearex); ext4_ext_try_to_merge(inode, path, nearex);
/* try to merge extents to the left */
/* time to correct all indexes above */ /* time to correct all indexes above */
err = ext4_ext_correct_indexes(handle, inode, path); err = ext4_ext_correct_indexes(handle, inode, path);
...@@ -2570,10 +2569,10 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start, ...@@ -2570,10 +2569,10 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
{ {
struct super_block *sb = inode->i_sb; struct super_block *sb = inode->i_sb;
int depth = ext_depth(inode); int depth = ext_depth(inode);
struct ext4_ext_path *path; struct ext4_ext_path *path = NULL;
ext4_fsblk_t partial_cluster = 0; ext4_fsblk_t partial_cluster = 0;
handle_t *handle; handle_t *handle;
int i, err; int i = 0, err;
ext_debug("truncate since %u to %u\n", start, end); ext_debug("truncate since %u to %u\n", start, end);
...@@ -2606,8 +2605,12 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start, ...@@ -2606,8 +2605,12 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
} }
depth = ext_depth(inode); depth = ext_depth(inode);
ex = path[depth].p_ext; ex = path[depth].p_ext;
if (!ex) if (!ex) {
ext4_ext_drop_refs(path);
kfree(path);
path = NULL;
goto cont; goto cont;
}
ee_block = le32_to_cpu(ex->ee_block); ee_block = le32_to_cpu(ex->ee_block);
...@@ -2637,8 +2640,6 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start, ...@@ -2637,8 +2640,6 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
if (err < 0) if (err < 0)
goto out; goto out;
} }
ext4_ext_drop_refs(path);
kfree(path);
} }
cont: cont:
...@@ -2647,7 +2648,14 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start, ...@@ -2647,7 +2648,14 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
* after i_size and walking into the tree depth-wise. * after i_size and walking into the tree depth-wise.
*/ */
depth = ext_depth(inode); depth = ext_depth(inode);
path = kzalloc(sizeof(struct ext4_ext_path) * (depth + 1), GFP_NOFS); if (path) {
int k = i = depth;
while (--k > 0)
path[k].p_block =
le16_to_cpu(path[k].p_hdr->eh_entries)+1;
} else {
path = kzalloc(sizeof(struct ext4_ext_path) * (depth + 1),
GFP_NOFS);
if (path == NULL) { if (path == NULL) {
ext4_journal_stop(handle); ext4_journal_stop(handle);
return -ENOMEM; return -ENOMEM;
...@@ -2659,7 +2667,8 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start, ...@@ -2659,7 +2667,8 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
err = -EIO; err = -EIO;
goto out; goto out;
} }
i = err = 0; }
err = 0;
while (i >= 0 && err == 0) { while (i >= 0 && err == 0) {
if (i == depth) { if (i == depth) {
...@@ -2773,8 +2782,10 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start, ...@@ -2773,8 +2782,10 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
out: out:
ext4_ext_drop_refs(path); ext4_ext_drop_refs(path);
kfree(path); kfree(path);
if (err == -EAGAIN) if (err == -EAGAIN) {
path = NULL;
goto again; goto again;
}
ext4_journal_stop(handle); ext4_journal_stop(handle);
return err; return err;
...@@ -4420,6 +4431,8 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len) ...@@ -4420,6 +4431,8 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
ext4_falloc_update_inode(inode, mode, new_size, ext4_falloc_update_inode(inode, mode, new_size,
(map.m_flags & EXT4_MAP_NEW)); (map.m_flags & EXT4_MAP_NEW));
ext4_mark_inode_dirty(handle, inode); ext4_mark_inode_dirty(handle, inode);
if ((file->f_flags & O_SYNC) && ret >= max_blocks)
ext4_handle_sync(handle);
ret2 = ext4_journal_stop(handle); ret2 = ext4_journal_stop(handle);
if (ret2) if (ret2)
break; break;
......
...@@ -89,12 +89,92 @@ ext4_unaligned_aio(struct inode *inode, const struct iovec *iov, ...@@ -89,12 +89,92 @@ ext4_unaligned_aio(struct inode *inode, const struct iovec *iov,
return 0; return 0;
} }
static ssize_t
ext4_file_dio_write(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
struct file *file = iocb->ki_filp;
struct inode *inode = file->f_mapping->host;
struct blk_plug plug;
int unaligned_aio = 0;
ssize_t ret;
int overwrite = 0;
size_t length = iov_length(iov, nr_segs);
if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS) &&
!is_sync_kiocb(iocb))
unaligned_aio = ext4_unaligned_aio(inode, iov, nr_segs, pos);
/* Unaligned direct AIO must be serialized; see comment above */
if (unaligned_aio) {
static unsigned long unaligned_warn_time;
/* Warn about this once per day */
if (printk_timed_ratelimit(&unaligned_warn_time, 60*60*24*HZ))
ext4_msg(inode->i_sb, KERN_WARNING,
"Unaligned AIO/DIO on inode %ld by %s; "
"performance will be poor.",
inode->i_ino, current->comm);
mutex_lock(ext4_aio_mutex(inode));
ext4_aiodio_wait(inode);
}
BUG_ON(iocb->ki_pos != pos);
mutex_lock(&inode->i_mutex);
blk_start_plug(&plug);
iocb->private = &overwrite;
/* check whether we do a DIO overwrite or not */
if (ext4_should_dioread_nolock(inode) && !unaligned_aio &&
!file->f_mapping->nrpages && pos + length <= i_size_read(inode)) {
struct ext4_map_blocks map;
unsigned int blkbits = inode->i_blkbits;
int err, len;
map.m_lblk = pos >> blkbits;
map.m_len = (EXT4_BLOCK_ALIGN(pos + length, blkbits) >> blkbits)
- map.m_lblk;
len = map.m_len;
err = ext4_map_blocks(NULL, inode, &map, 0);
/*
* 'err==len' means that all of blocks has been preallocated no
* matter they are initialized or not. For excluding
* uninitialized extents, we need to check m_flags. There are
* two conditions that indicate for initialized extents.
* 1) If we hit extent cache, EXT4_MAP_MAPPED flag is returned;
* 2) If we do a real lookup, non-flags are returned.
* So we should check these two conditions.
*/
if (err == len && (map.m_flags & EXT4_MAP_MAPPED))
overwrite = 1;
}
ret = __generic_file_aio_write(iocb, iov, nr_segs, &iocb->ki_pos);
mutex_unlock(&inode->i_mutex);
if (ret > 0 || ret == -EIOCBQUEUED) {
ssize_t err;
err = generic_write_sync(file, pos, ret);
if (err < 0 && ret > 0)
ret = err;
}
blk_finish_plug(&plug);
if (unaligned_aio)
mutex_unlock(ext4_aio_mutex(inode));
return ret;
}
static ssize_t static ssize_t
ext4_file_write(struct kiocb *iocb, const struct iovec *iov, ext4_file_write(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos) unsigned long nr_segs, loff_t pos)
{ {
struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode; struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode;
int unaligned_aio = 0;
ssize_t ret; ssize_t ret;
/* /*
...@@ -114,30 +194,13 @@ ext4_file_write(struct kiocb *iocb, const struct iovec *iov, ...@@ -114,30 +194,13 @@ ext4_file_write(struct kiocb *iocb, const struct iovec *iov,
nr_segs = iov_shorten((struct iovec *)iov, nr_segs, nr_segs = iov_shorten((struct iovec *)iov, nr_segs,
sbi->s_bitmap_maxbytes - pos); sbi->s_bitmap_maxbytes - pos);
} }
} else if (unlikely((iocb->ki_filp->f_flags & O_DIRECT) &&
!is_sync_kiocb(iocb))) {
unaligned_aio = ext4_unaligned_aio(inode, iov, nr_segs, pos);
}
/* Unaligned direct AIO must be serialized; see comment above */
if (unaligned_aio) {
static unsigned long unaligned_warn_time;
/* Warn about this once per day */
if (printk_timed_ratelimit(&unaligned_warn_time, 60*60*24*HZ))
ext4_msg(inode->i_sb, KERN_WARNING,
"Unaligned AIO/DIO on inode %ld by %s; "
"performance will be poor.",
inode->i_ino, current->comm);
mutex_lock(ext4_aio_mutex(inode));
ext4_aiodio_wait(inode);
} }
if (unlikely(iocb->ki_filp->f_flags & O_DIRECT))
ret = ext4_file_dio_write(iocb, iov, nr_segs, pos);
else
ret = generic_file_aio_write(iocb, iov, nr_segs, pos); ret = generic_file_aio_write(iocb, iov, nr_segs, pos);
if (unaligned_aio)
mutex_unlock(ext4_aio_mutex(inode));
return ret; return ret;
} }
...@@ -181,9 +244,21 @@ static int ext4_file_open(struct inode * inode, struct file * filp) ...@@ -181,9 +244,21 @@ static int ext4_file_open(struct inode * inode, struct file * filp)
path.dentry = mnt->mnt_root; path.dentry = mnt->mnt_root;
cp = d_path(&path, buf, sizeof(buf)); cp = d_path(&path, buf, sizeof(buf));
if (!IS_ERR(cp)) { if (!IS_ERR(cp)) {
handle_t *handle;
int err;
handle = ext4_journal_start_sb(sb, 1);
if (IS_ERR(handle))
return PTR_ERR(handle);
err = ext4_journal_get_write_access(handle, sbi->s_sbh);
if (err) {
ext4_journal_stop(handle);
return err;
}
strlcpy(sbi->s_es->s_last_mounted, cp, strlcpy(sbi->s_es->s_last_mounted, cp,
sizeof(sbi->s_es->s_last_mounted)); sizeof(sbi->s_es->s_last_mounted));
ext4_mark_super_dirty(sb); ext4_handle_dirty_super(handle, sb);
ext4_journal_stop(handle);
} }
} }
/* /*
......
...@@ -315,7 +315,6 @@ void ext4_free_inode(handle_t *handle, struct inode *inode) ...@@ -315,7 +315,6 @@ void ext4_free_inode(handle_t *handle, struct inode *inode)
err = ext4_handle_dirty_metadata(handle, NULL, bitmap_bh); err = ext4_handle_dirty_metadata(handle, NULL, bitmap_bh);
if (!fatal) if (!fatal)
fatal = err; fatal = err;
ext4_mark_super_dirty(sb);
} else } else
ext4_error(sb, "bit already cleared for inode %lu", ino); ext4_error(sb, "bit already cleared for inode %lu", ino);
...@@ -830,7 +829,6 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, umode_t mode, ...@@ -830,7 +829,6 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, umode_t mode,
percpu_counter_dec(&sbi->s_freeinodes_counter); percpu_counter_dec(&sbi->s_freeinodes_counter);
if (S_ISDIR(mode)) if (S_ISDIR(mode))
percpu_counter_inc(&sbi->s_dirs_counter); percpu_counter_inc(&sbi->s_dirs_counter);
ext4_mark_super_dirty(sb);
if (sbi->s_log_groups_per_flex) { if (sbi->s_log_groups_per_flex) {
flex_group = ext4_flex_group(sbi, group); flex_group = ext4_flex_group(sbi, group);
...@@ -1054,7 +1052,8 @@ unsigned long ext4_count_free_inodes(struct super_block *sb) ...@@ -1054,7 +1052,8 @@ unsigned long ext4_count_free_inodes(struct super_block *sb)
if (!bitmap_bh) if (!bitmap_bh)
continue; continue;
x = ext4_count_free(bitmap_bh, EXT4_INODES_PER_GROUP(sb) / 8); x = ext4_count_free(bitmap_bh->b_data,
EXT4_INODES_PER_GROUP(sb) / 8);
printk(KERN_DEBUG "group %lu: stored = %d, counted = %lu\n", printk(KERN_DEBUG "group %lu: stored = %d, counted = %lu\n",
(unsigned long) i, ext4_free_inodes_count(sb, gdp), x); (unsigned long) i, ext4_free_inodes_count(sb, gdp), x);
bitmap_count += x; bitmap_count += x;
......
...@@ -346,6 +346,15 @@ void ext4_da_update_reserve_space(struct inode *inode, ...@@ -346,6 +346,15 @@ void ext4_da_update_reserve_space(struct inode *inode,
used = ei->i_reserved_data_blocks; used = ei->i_reserved_data_blocks;
} }
if (unlikely(ei->i_allocated_meta_blocks > ei->i_reserved_meta_blocks)) {
ext4_msg(inode->i_sb, KERN_NOTICE, "%s: ino %lu, allocated %d "
"with only %d reserved metadata blocks\n", __func__,
inode->i_ino, ei->i_allocated_meta_blocks,
ei->i_reserved_meta_blocks);
WARN_ON(1);
ei->i_allocated_meta_blocks = ei->i_reserved_meta_blocks;
}
/* Update per-inode reservations */ /* Update per-inode reservations */
ei->i_reserved_data_blocks -= used; ei->i_reserved_data_blocks -= used;
ei->i_reserved_meta_blocks -= ei->i_allocated_meta_blocks; ei->i_reserved_meta_blocks -= ei->i_allocated_meta_blocks;
...@@ -544,6 +553,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, ...@@ -544,6 +553,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
* Try to see if we can get the block without requesting a new * Try to see if we can get the block without requesting a new
* file system block. * file system block.
*/ */
if (!(flags & EXT4_GET_BLOCKS_NO_LOCK))
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, flags &
...@@ -552,6 +562,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, ...@@ -552,6 +562,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
retval = ext4_ind_map_blocks(handle, inode, map, flags & retval = ext4_ind_map_blocks(handle, inode, map, flags &
EXT4_GET_BLOCKS_KEEP_SIZE); EXT4_GET_BLOCKS_KEEP_SIZE);
} }
if (!(flags & EXT4_GET_BLOCKS_NO_LOCK))
up_read((&EXT4_I(inode)->i_data_sem)); up_read((&EXT4_I(inode)->i_data_sem));
if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) { if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) {
...@@ -1171,6 +1182,17 @@ static int ext4_da_reserve_space(struct inode *inode, ext4_lblk_t lblock) ...@@ -1171,6 +1182,17 @@ static int ext4_da_reserve_space(struct inode *inode, ext4_lblk_t lblock)
struct ext4_inode_info *ei = EXT4_I(inode); struct ext4_inode_info *ei = EXT4_I(inode);
unsigned int md_needed; unsigned int md_needed;
int ret; int ret;
ext4_lblk_t save_last_lblock;
int save_len;
/*
* We will charge metadata quota at writeout time; this saves
* us from metadata over-estimation, though we may go over by
* a small amount in the end. Here we just reserve for data.
*/
ret = dquot_reserve_block(inode, EXT4_C2B(sbi, 1));
if (ret)
return ret;
/* /*
* recalculate the amount of metadata blocks to reserve * recalculate the amount of metadata blocks to reserve
...@@ -1179,32 +1201,31 @@ static int ext4_da_reserve_space(struct inode *inode, ext4_lblk_t lblock) ...@@ -1179,32 +1201,31 @@ static int ext4_da_reserve_space(struct inode *inode, ext4_lblk_t lblock)
*/ */
repeat: repeat:
spin_lock(&ei->i_block_reservation_lock); spin_lock(&ei->i_block_reservation_lock);
/*
* ext4_calc_metadata_amount() has side effects, which we have
* to be prepared undo if we fail to claim space.
*/
save_len = ei->i_da_metadata_calc_len;
save_last_lblock = ei->i_da_metadata_calc_last_lblock;
md_needed = EXT4_NUM_B2C(sbi, md_needed = EXT4_NUM_B2C(sbi,
ext4_calc_metadata_amount(inode, lblock)); ext4_calc_metadata_amount(inode, lblock));
trace_ext4_da_reserve_space(inode, md_needed); trace_ext4_da_reserve_space(inode, md_needed);
spin_unlock(&ei->i_block_reservation_lock);
/*
* We will charge metadata quota at writeout time; this saves
* us from metadata over-estimation, though we may go over by
* a small amount in the end. Here we just reserve for data.
*/
ret = dquot_reserve_block(inode, EXT4_C2B(sbi, 1));
if (ret)
return ret;
/* /*
* We do still charge estimated metadata to the sb though; * We do still charge estimated metadata to the sb though;
* we cannot afford to run out of free blocks. * we cannot afford to run out of free blocks.
*/ */
if (ext4_claim_free_clusters(sbi, md_needed + 1, 0)) { if (ext4_claim_free_clusters(sbi, md_needed + 1, 0)) {
dquot_release_reservation_block(inode, EXT4_C2B(sbi, 1)); ei->i_da_metadata_calc_len = save_len;
ei->i_da_metadata_calc_last_lblock = save_last_lblock;
spin_unlock(&ei->i_block_reservation_lock);
if (ext4_should_retry_alloc(inode->i_sb, &retries)) { if (ext4_should_retry_alloc(inode->i_sb, &retries)) {
yield(); yield();
goto repeat; goto repeat;
} }
dquot_release_reservation_block(inode, EXT4_C2B(sbi, 1));
return -ENOSPC; return -ENOSPC;
} }
spin_lock(&ei->i_block_reservation_lock);
ei->i_reserved_data_blocks++; ei->i_reserved_data_blocks++;
ei->i_reserved_meta_blocks += md_needed; ei->i_reserved_meta_blocks += md_needed;
spin_unlock(&ei->i_block_reservation_lock); spin_unlock(&ei->i_block_reservation_lock);
...@@ -2818,6 +2839,32 @@ static int ext4_get_block_write(struct inode *inode, sector_t iblock, ...@@ -2818,6 +2839,32 @@ static int ext4_get_block_write(struct inode *inode, sector_t iblock,
EXT4_GET_BLOCKS_IO_CREATE_EXT); EXT4_GET_BLOCKS_IO_CREATE_EXT);
} }
static int ext4_get_block_write_nolock(struct inode *inode, sector_t iblock,
struct buffer_head *bh_result, int flags)
{
handle_t *handle = ext4_journal_current_handle();
struct ext4_map_blocks map;
int ret = 0;
ext4_debug("ext4_get_block_write_nolock: inode %lu, flag %d\n",
inode->i_ino, flags);
flags = EXT4_GET_BLOCKS_NO_LOCK;
map.m_lblk = iblock;
map.m_len = bh_result->b_size >> inode->i_blkbits;
ret = ext4_map_blocks(handle, inode, &map, flags);
if (ret > 0) {
map_bh(bh_result, inode->i_sb, map.m_pblk);
bh_result->b_state = (bh_result->b_state & ~EXT4_MAP_FLAGS) |
map.m_flags;
bh_result->b_size = inode->i_sb->s_blocksize * map.m_len;
ret = 0;
}
return ret;
}
static void ext4_end_io_dio(struct kiocb *iocb, loff_t offset, static void ext4_end_io_dio(struct kiocb *iocb, loff_t offset,
ssize_t size, void *private, int ret, ssize_t size, void *private, int ret,
bool is_async) bool is_async)
...@@ -2966,6 +3013,18 @@ static ssize_t ext4_ext_direct_IO(int rw, struct kiocb *iocb, ...@@ -2966,6 +3013,18 @@ static ssize_t ext4_ext_direct_IO(int rw, struct kiocb *iocb,
loff_t final_size = offset + count; loff_t final_size = offset + count;
if (rw == WRITE && final_size <= inode->i_size) { if (rw == WRITE && final_size <= inode->i_size) {
int overwrite = 0;
BUG_ON(iocb->private == NULL);
/* If we do a overwrite dio, i_mutex locking can be released */
overwrite = *((int *)iocb->private);
if (overwrite) {
down_read(&EXT4_I(inode)->i_data_sem);
mutex_unlock(&inode->i_mutex);
}
/* /*
* We could direct write to holes and fallocate. * We could direct write to holes and fallocate.
* *
...@@ -2991,8 +3050,10 @@ static ssize_t ext4_ext_direct_IO(int rw, struct kiocb *iocb, ...@@ -2991,8 +3050,10 @@ static ssize_t ext4_ext_direct_IO(int rw, struct kiocb *iocb,
if (!is_sync_kiocb(iocb)) { if (!is_sync_kiocb(iocb)) {
ext4_io_end_t *io_end = ext4_io_end_t *io_end =
ext4_init_io_end(inode, GFP_NOFS); ext4_init_io_end(inode, GFP_NOFS);
if (!io_end) if (!io_end) {
return -ENOMEM; ret = -ENOMEM;
goto retake_lock;
}
io_end->flag |= EXT4_IO_END_DIRECT; io_end->flag |= EXT4_IO_END_DIRECT;
iocb->private = io_end; iocb->private = io_end;
/* /*
...@@ -3005,6 +3066,15 @@ static ssize_t ext4_ext_direct_IO(int rw, struct kiocb *iocb, ...@@ -3005,6 +3066,15 @@ static ssize_t ext4_ext_direct_IO(int rw, struct kiocb *iocb,
EXT4_I(inode)->cur_aio_dio = iocb->private; EXT4_I(inode)->cur_aio_dio = iocb->private;
} }
if (overwrite)
ret = __blockdev_direct_IO(rw, iocb, inode,
inode->i_sb->s_bdev, iov,
offset, nr_segs,
ext4_get_block_write_nolock,
ext4_end_io_dio,
NULL,
0);
else
ret = __blockdev_direct_IO(rw, iocb, inode, ret = __blockdev_direct_IO(rw, iocb, inode,
inode->i_sb->s_bdev, iov, inode->i_sb->s_bdev, iov,
offset, nr_segs, offset, nr_segs,
...@@ -3031,7 +3101,7 @@ static ssize_t ext4_ext_direct_IO(int rw, struct kiocb *iocb, ...@@ -3031,7 +3101,7 @@ static ssize_t ext4_ext_direct_IO(int rw, struct kiocb *iocb,
if (ret != -EIOCBQUEUED && ret <= 0 && iocb->private) { if (ret != -EIOCBQUEUED && ret <= 0 && iocb->private) {
ext4_free_io_end(iocb->private); ext4_free_io_end(iocb->private);
iocb->private = NULL; iocb->private = NULL;
} else if (ret > 0 && ext4_test_inode_state(inode, } else if (ret > 0 && !overwrite && ext4_test_inode_state(inode,
EXT4_STATE_DIO_UNWRITTEN)) { EXT4_STATE_DIO_UNWRITTEN)) {
int err; int err;
/* /*
...@@ -3044,6 +3114,14 @@ static ssize_t ext4_ext_direct_IO(int rw, struct kiocb *iocb, ...@@ -3044,6 +3114,14 @@ static ssize_t ext4_ext_direct_IO(int rw, struct kiocb *iocb,
ret = err; ret = err;
ext4_clear_inode_state(inode, EXT4_STATE_DIO_UNWRITTEN); ext4_clear_inode_state(inode, EXT4_STATE_DIO_UNWRITTEN);
} }
retake_lock:
/* take i_mutex locking again if we do a ovewrite dio */
if (overwrite) {
up_read(&EXT4_I(inode)->i_data_sem);
mutex_lock(&inode->i_mutex);
}
return ret; return ret;
} }
...@@ -4034,7 +4112,7 @@ static int ext4_do_update_inode(handle_t *handle, ...@@ -4034,7 +4112,7 @@ static int ext4_do_update_inode(handle_t *handle,
EXT4_SET_RO_COMPAT_FEATURE(sb, EXT4_SET_RO_COMPAT_FEATURE(sb,
EXT4_FEATURE_RO_COMPAT_LARGE_FILE); EXT4_FEATURE_RO_COMPAT_LARGE_FILE);
ext4_handle_sync(handle); ext4_handle_sync(handle);
err = ext4_handle_dirty_super_now(handle, sb); err = ext4_handle_dirty_super(handle, sb);
} }
} }
raw_inode->i_generation = cpu_to_le32(inode->i_generation); raw_inode->i_generation = cpu_to_le32(inode->i_generation);
......
...@@ -969,7 +969,6 @@ static int ext4_mb_get_buddy_page_lock(struct super_block *sb, ...@@ -969,7 +969,6 @@ static int ext4_mb_get_buddy_page_lock(struct super_block *sb,
block++; block++;
pnum = block / blocks_per_page; pnum = block / blocks_per_page;
poff = block % blocks_per_page;
page = find_or_create_page(inode->i_mapping, pnum, GFP_NOFS); page = find_or_create_page(inode->i_mapping, pnum, GFP_NOFS);
if (!page) if (!page)
return -EIO; return -EIO;
...@@ -2077,8 +2076,9 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v) ...@@ -2077,8 +2076,9 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v)
struct super_block *sb = seq->private; struct super_block *sb = seq->private;
ext4_group_t group = (ext4_group_t) ((unsigned long) v); ext4_group_t group = (ext4_group_t) ((unsigned long) v);
int i; int i;
int err; int err, buddy_loaded = 0;
struct ext4_buddy e4b; struct ext4_buddy e4b;
struct ext4_group_info *grinfo;
struct sg { struct sg {
struct ext4_group_info info; struct ext4_group_info info;
ext4_grpblk_t counters[16]; ext4_grpblk_t counters[16];
...@@ -2095,14 +2095,20 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v) ...@@ -2095,14 +2095,20 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v)
i = (sb->s_blocksize_bits + 2) * sizeof(sg.info.bb_counters[0]) + i = (sb->s_blocksize_bits + 2) * sizeof(sg.info.bb_counters[0]) +
sizeof(struct ext4_group_info); sizeof(struct ext4_group_info);
grinfo = ext4_get_group_info(sb, group);
/* Load the group info in memory only if not already loaded. */
if (unlikely(EXT4_MB_GRP_NEED_INIT(grinfo))) {
err = ext4_mb_load_buddy(sb, group, &e4b); err = ext4_mb_load_buddy(sb, group, &e4b);
if (err) { if (err) {
seq_printf(seq, "#%-5u: I/O error\n", group); seq_printf(seq, "#%-5u: I/O error\n", group);
return 0; return 0;
} }
ext4_lock_group(sb, group); buddy_loaded = 1;
}
memcpy(&sg, ext4_get_group_info(sb, group), i); memcpy(&sg, ext4_get_group_info(sb, group), i);
ext4_unlock_group(sb, group);
if (buddy_loaded)
ext4_mb_unload_buddy(&e4b); ext4_mb_unload_buddy(&e4b);
seq_printf(seq, "#%-5u: %-5u %-5u %-5u [", group, sg.info.bb_free, seq_printf(seq, "#%-5u: %-5u %-5u %-5u [", group, sg.info.bb_free,
...@@ -2825,7 +2831,6 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac, ...@@ -2825,7 +2831,6 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac,
err = ext4_handle_dirty_metadata(handle, NULL, gdp_bh); err = ext4_handle_dirty_metadata(handle, NULL, gdp_bh);
out_err: out_err:
ext4_mark_super_dirty(sb);
brelse(bitmap_bh); brelse(bitmap_bh);
return err; return err;
} }
...@@ -4694,7 +4699,6 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode, ...@@ -4694,7 +4699,6 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
put_bh(bitmap_bh); put_bh(bitmap_bh);
goto do_more; goto do_more;
} }
ext4_mark_super_dirty(sb);
error_return: error_return:
brelse(bitmap_bh); brelse(bitmap_bh);
ext4_std_error(sb, err); ext4_std_error(sb, err);
......
...@@ -2397,7 +2397,7 @@ int ext4_orphan_add(handle_t *handle, struct inode *inode) ...@@ -2397,7 +2397,7 @@ int ext4_orphan_add(handle_t *handle, struct inode *inode)
/* Insert this inode at the head of the on-disk orphan list... */ /* Insert this inode at the head of the on-disk orphan list... */
NEXT_ORPHAN(inode) = le32_to_cpu(EXT4_SB(sb)->s_es->s_last_orphan); NEXT_ORPHAN(inode) = le32_to_cpu(EXT4_SB(sb)->s_es->s_last_orphan);
EXT4_SB(sb)->s_es->s_last_orphan = cpu_to_le32(inode->i_ino); EXT4_SB(sb)->s_es->s_last_orphan = cpu_to_le32(inode->i_ino);
err = ext4_handle_dirty_super_now(handle, sb); err = ext4_handle_dirty_super(handle, sb);
rc = ext4_mark_iloc_dirty(handle, inode, &iloc); rc = ext4_mark_iloc_dirty(handle, inode, &iloc);
if (!err) if (!err)
err = rc; err = rc;
...@@ -2470,7 +2470,7 @@ int ext4_orphan_del(handle_t *handle, struct inode *inode) ...@@ -2470,7 +2470,7 @@ int ext4_orphan_del(handle_t *handle, struct inode *inode)
if (err) if (err)
goto out_brelse; goto out_brelse;
sbi->s_es->s_last_orphan = cpu_to_le32(ino_next); sbi->s_es->s_last_orphan = cpu_to_le32(ino_next);
err = ext4_handle_dirty_super_now(handle, inode->i_sb); err = ext4_handle_dirty_super(handle, inode->i_sb);
} else { } else {
struct ext4_iloc iloc2; struct ext4_iloc iloc2;
struct inode *i_prev = struct inode *i_prev =
...@@ -2918,8 +2918,15 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -2918,8 +2918,15 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
PARENT_INO(dir_bh->b_data, new_dir->i_sb->s_blocksize) = PARENT_INO(dir_bh->b_data, new_dir->i_sb->s_blocksize) =
cpu_to_le32(new_dir->i_ino); cpu_to_le32(new_dir->i_ino);
BUFFER_TRACE(dir_bh, "call ext4_handle_dirty_metadata"); BUFFER_TRACE(dir_bh, "call ext4_handle_dirty_metadata");
retval = ext4_handle_dirty_dirent_node(handle, old_inode, if (is_dx(old_inode)) {
retval = ext4_handle_dirty_dx_node(handle,
old_inode,
dir_bh); dir_bh);
} else {
retval = ext4_handle_dirty_dirent_node(handle,
old_inode,
dir_bh);
}
if (retval) { if (retval) {
ext4_std_error(old_dir->i_sb, retval); ext4_std_error(old_dir->i_sb, retval);
goto end_rename; goto end_rename;
......
...@@ -798,7 +798,7 @@ static int add_new_gdb(handle_t *handle, struct inode *inode, ...@@ -798,7 +798,7 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
ext4_kvfree(o_group_desc); ext4_kvfree(o_group_desc);
le16_add_cpu(&es->s_reserved_gdt_blocks, -1); le16_add_cpu(&es->s_reserved_gdt_blocks, -1);
err = ext4_handle_dirty_super_now(handle, sb); err = ext4_handle_dirty_super(handle, sb);
if (err) if (err)
ext4_std_error(sb, err); ext4_std_error(sb, err);
...@@ -1272,6 +1272,11 @@ static void ext4_update_super(struct super_block *sb, ...@@ -1272,6 +1272,11 @@ static void ext4_update_super(struct super_block *sb,
&sbi->s_flex_groups[flex_group].free_inodes); &sbi->s_flex_groups[flex_group].free_inodes);
} }
/*
* Update the fs overhead information
*/
ext4_calculate_overhead(sb);
if (test_opt(sb, DEBUG)) if (test_opt(sb, DEBUG))
printk(KERN_DEBUG "EXT4-fs: added group %u:" printk(KERN_DEBUG "EXT4-fs: added group %u:"
"%llu blocks(%llu free %llu reserved)\n", flex_gd->count, "%llu blocks(%llu free %llu reserved)\n", flex_gd->count,
......
...@@ -74,7 +74,6 @@ static const char *ext4_decode_error(struct super_block *sb, int errno, ...@@ -74,7 +74,6 @@ static const char *ext4_decode_error(struct super_block *sb, int errno,
static int ext4_remount(struct super_block *sb, int *flags, char *data); static int ext4_remount(struct super_block *sb, int *flags, char *data);
static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf); static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf);
static int ext4_unfreeze(struct super_block *sb); static int ext4_unfreeze(struct super_block *sb);
static void ext4_write_super(struct super_block *sb);
static int ext4_freeze(struct super_block *sb); static int ext4_freeze(struct super_block *sb);
static struct dentry *ext4_mount(struct file_system_type *fs_type, int flags, static struct dentry *ext4_mount(struct file_system_type *fs_type, int flags,
const char *dev_name, void *data); const char *dev_name, void *data);
...@@ -896,7 +895,7 @@ static void ext4_put_super(struct super_block *sb) ...@@ -896,7 +895,7 @@ static void ext4_put_super(struct super_block *sb)
EXT4_CLEAR_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER); EXT4_CLEAR_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER);
es->s_state = cpu_to_le16(sbi->s_mount_state); es->s_state = cpu_to_le16(sbi->s_mount_state);
} }
if (sb->s_dirt || !(sb->s_flags & MS_RDONLY)) if (!(sb->s_flags & MS_RDONLY))
ext4_commit_super(sb, 1); ext4_commit_super(sb, 1);
if (sbi->s_proc) { if (sbi->s_proc) {
...@@ -1137,12 +1136,18 @@ static int ext4_mark_dquot_dirty(struct dquot *dquot); ...@@ -1137,12 +1136,18 @@ static int ext4_mark_dquot_dirty(struct dquot *dquot);
static int ext4_write_info(struct super_block *sb, int type); static int ext4_write_info(struct super_block *sb, int type);
static int ext4_quota_on(struct super_block *sb, int type, int format_id, static int ext4_quota_on(struct super_block *sb, int type, int format_id,
struct path *path); struct path *path);
static int ext4_quota_on_sysfile(struct super_block *sb, int type,
int format_id);
static int ext4_quota_off(struct super_block *sb, int type); static int ext4_quota_off(struct super_block *sb, int type);
static int ext4_quota_off_sysfile(struct super_block *sb, int type);
static int ext4_quota_on_mount(struct super_block *sb, int type); static int ext4_quota_on_mount(struct super_block *sb, int type);
static ssize_t ext4_quota_read(struct super_block *sb, int type, char *data, static ssize_t ext4_quota_read(struct super_block *sb, int type, char *data,
size_t len, loff_t off); size_t len, loff_t off);
static ssize_t ext4_quota_write(struct super_block *sb, int type, static ssize_t ext4_quota_write(struct super_block *sb, int type,
const char *data, size_t len, loff_t off); const char *data, size_t len, loff_t off);
static int ext4_quota_enable(struct super_block *sb, int type, int format_id,
unsigned int flags);
static int ext4_enable_quotas(struct super_block *sb);
static const struct dquot_operations ext4_quota_operations = { static const struct dquot_operations ext4_quota_operations = {
.get_reserved_space = ext4_get_reserved_space, .get_reserved_space = ext4_get_reserved_space,
...@@ -1164,6 +1169,16 @@ static const struct quotactl_ops ext4_qctl_operations = { ...@@ -1164,6 +1169,16 @@ static const struct quotactl_ops ext4_qctl_operations = {
.get_dqblk = dquot_get_dqblk, .get_dqblk = dquot_get_dqblk,
.set_dqblk = dquot_set_dqblk .set_dqblk = dquot_set_dqblk
}; };
static const struct quotactl_ops ext4_qctl_sysfile_operations = {
.quota_on_meta = ext4_quota_on_sysfile,
.quota_off = ext4_quota_off_sysfile,
.quota_sync = dquot_quota_sync,
.get_info = dquot_get_dqinfo,
.set_info = dquot_set_dqinfo,
.get_dqblk = dquot_get_dqblk,
.set_dqblk = dquot_set_dqblk
};
#endif #endif
static const struct super_operations ext4_sops = { static const struct super_operations ext4_sops = {
...@@ -1194,7 +1209,6 @@ static const struct super_operations ext4_nojournal_sops = { ...@@ -1194,7 +1209,6 @@ static const struct super_operations ext4_nojournal_sops = {
.dirty_inode = ext4_dirty_inode, .dirty_inode = ext4_dirty_inode,
.drop_inode = ext4_drop_inode, .drop_inode = ext4_drop_inode,
.evict_inode = ext4_evict_inode, .evict_inode = ext4_evict_inode,
.write_super = ext4_write_super,
.put_super = ext4_put_super, .put_super = ext4_put_super,
.statfs = ext4_statfs, .statfs = ext4_statfs,
.remount_fs = ext4_remount, .remount_fs = ext4_remount,
...@@ -2661,6 +2675,16 @@ static int ext4_feature_set_ok(struct super_block *sb, int readonly) ...@@ -2661,6 +2675,16 @@ static int ext4_feature_set_ok(struct super_block *sb, int readonly)
"extents feature\n"); "extents feature\n");
return 0; return 0;
} }
#ifndef CONFIG_QUOTA
if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA) &&
!readonly) {
ext4_msg(sb, KERN_ERR,
"Filesystem with quota feature cannot be mounted RDWR "
"without CONFIG_QUOTA");
return 0;
}
#endif /* CONFIG_QUOTA */
return 1; return 1;
} }
...@@ -3085,6 +3109,114 @@ static int set_journal_csum_feature_set(struct super_block *sb) ...@@ -3085,6 +3109,114 @@ static int set_journal_csum_feature_set(struct super_block *sb)
return ret; return ret;
} }
/*
* Note: calculating the overhead so we can be compatible with
* historical BSD practice is quite difficult in the face of
* clusters/bigalloc. This is because multiple metadata blocks from
* different block group can end up in the same allocation cluster.
* Calculating the exact overhead in the face of clustered allocation
* requires either O(all block bitmaps) in memory or O(number of block
* groups**2) in time. We will still calculate the superblock for
* older file systems --- and if we come across with a bigalloc file
* system with zero in s_overhead_clusters the estimate will be close to
* correct especially for very large cluster sizes --- but for newer
* file systems, it's better to calculate this figure once at mkfs
* time, and store it in the superblock. If the superblock value is
* present (even for non-bigalloc file systems), we will use it.
*/
static int count_overhead(struct super_block *sb, ext4_group_t grp,
char *buf)
{
struct ext4_sb_info *sbi = EXT4_SB(sb);
struct ext4_group_desc *gdp;
ext4_fsblk_t first_block, last_block, b;
ext4_group_t i, ngroups = ext4_get_groups_count(sb);
int s, j, count = 0;
first_block = le32_to_cpu(sbi->s_es->s_first_data_block) +
(grp * EXT4_BLOCKS_PER_GROUP(sb));
last_block = first_block + EXT4_BLOCKS_PER_GROUP(sb) - 1;
for (i = 0; i < ngroups; i++) {
gdp = ext4_get_group_desc(sb, i, NULL);
b = ext4_block_bitmap(sb, gdp);
if (b >= first_block && b <= last_block) {
ext4_set_bit(EXT4_B2C(sbi, b - first_block), buf);
count++;
}
b = ext4_inode_bitmap(sb, gdp);
if (b >= first_block && b <= last_block) {
ext4_set_bit(EXT4_B2C(sbi, b - first_block), buf);
count++;
}
b = ext4_inode_table(sb, gdp);
if (b >= first_block && b + sbi->s_itb_per_group <= last_block)
for (j = 0; j < sbi->s_itb_per_group; j++, b++) {
int c = EXT4_B2C(sbi, b - first_block);
ext4_set_bit(c, buf);
count++;
}
if (i != grp)
continue;
s = 0;
if (ext4_bg_has_super(sb, grp)) {
ext4_set_bit(s++, buf);
count++;
}
for (j = ext4_bg_num_gdb(sb, grp); j > 0; j--) {
ext4_set_bit(EXT4_B2C(sbi, s++), buf);
count++;
}
}
if (!count)
return 0;
return EXT4_CLUSTERS_PER_GROUP(sb) -
ext4_count_free(buf, EXT4_CLUSTERS_PER_GROUP(sb) / 8);
}
/*
* Compute the overhead and stash it in sbi->s_overhead
*/
int ext4_calculate_overhead(struct super_block *sb)
{
struct ext4_sb_info *sbi = EXT4_SB(sb);
struct ext4_super_block *es = sbi->s_es;
ext4_group_t i, ngroups = ext4_get_groups_count(sb);
ext4_fsblk_t overhead = 0;
char *buf = (char *) get_zeroed_page(GFP_KERNEL);
memset(buf, 0, PAGE_SIZE);
if (!buf)
return -ENOMEM;
/*
* Compute the overhead (FS structures). This is constant
* for a given filesystem unless the number of block groups
* changes so we cache the previous value until it does.
*/
/*
* All of the blocks before first_data_block are overhead
*/
overhead = EXT4_B2C(sbi, le32_to_cpu(es->s_first_data_block));
/*
* Add the overhead found in each block group
*/
for (i = 0; i < ngroups; i++) {
int blks;
blks = count_overhead(sb, i, buf);
overhead += blks;
if (blks)
memset(buf, 0, PAGE_SIZE);
cond_resched();
}
sbi->s_overhead = overhead;
smp_wmb();
free_page((unsigned long) buf);
return 0;
}
static int ext4_fill_super(struct super_block *sb, void *data, int silent) static int ext4_fill_super(struct super_block *sb, void *data, int silent)
{ {
char *orig_data = kstrdup(data, GFP_KERNEL); char *orig_data = kstrdup(data, GFP_KERNEL);
...@@ -3640,6 +3772,11 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) ...@@ -3640,6 +3772,11 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
#ifdef CONFIG_QUOTA #ifdef CONFIG_QUOTA
sb->s_qcop = &ext4_qctl_operations; sb->s_qcop = &ext4_qctl_operations;
sb->dq_op = &ext4_quota_operations; sb->dq_op = &ext4_quota_operations;
if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA)) {
/* Use qctl operations for hidden quota files. */
sb->s_qcop = &ext4_qctl_sysfile_operations;
}
#endif #endif
memcpy(sb->s_uuid, es->s_uuid, sizeof(es->s_uuid)); memcpy(sb->s_uuid, es->s_uuid, sizeof(es->s_uuid));
...@@ -3734,6 +3871,18 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) ...@@ -3734,6 +3871,18 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
percpu_counter_set(&sbi->s_dirtyclusters_counter, 0); percpu_counter_set(&sbi->s_dirtyclusters_counter, 0);
no_journal: no_journal:
/*
* Get the # of file system overhead blocks from the
* superblock if present.
*/
if (es->s_overhead_clusters)
sbi->s_overhead = le32_to_cpu(es->s_overhead_clusters);
else {
ret = ext4_calculate_overhead(sb);
if (ret)
goto failed_mount_wq;
}
/* /*
* The maximum number of concurrent works can be high and * The maximum number of concurrent works can be high and
* concurrency isn't really necessary. Limit it to 1. * concurrency isn't really necessary. Limit it to 1.
...@@ -3840,6 +3989,16 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) ...@@ -3840,6 +3989,16 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
} else } else
descr = "out journal"; descr = "out journal";
#ifdef CONFIG_QUOTA
/* Enable quota usage during mount. */
if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA) &&
!(sb->s_flags & MS_RDONLY)) {
ret = ext4_enable_quotas(sb);
if (ret)
goto failed_mount7;
}
#endif /* CONFIG_QUOTA */
ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. " ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. "
"Opts: %s%s%s", descr, sbi->s_es->s_mount_opts, "Opts: %s%s%s", descr, sbi->s_es->s_mount_opts,
*sbi->s_es->s_mount_opts ? "; " : "", orig_data); *sbi->s_es->s_mount_opts ? "; " : "", orig_data);
...@@ -4203,7 +4362,6 @@ static int ext4_commit_super(struct super_block *sb, int sync) ...@@ -4203,7 +4362,6 @@ static int ext4_commit_super(struct super_block *sb, int sync)
es->s_free_inodes_count = es->s_free_inodes_count =
cpu_to_le32(percpu_counter_sum_positive( cpu_to_le32(percpu_counter_sum_positive(
&EXT4_SB(sb)->s_freeinodes_counter)); &EXT4_SB(sb)->s_freeinodes_counter));
sb->s_dirt = 0;
BUFFER_TRACE(sbh, "marking dirty"); BUFFER_TRACE(sbh, "marking dirty");
ext4_superblock_csum_set(sb, es); ext4_superblock_csum_set(sb, es);
mark_buffer_dirty(sbh); mark_buffer_dirty(sbh);
...@@ -4310,13 +4468,6 @@ int ext4_force_commit(struct super_block *sb) ...@@ -4310,13 +4468,6 @@ int ext4_force_commit(struct super_block *sb)
return ret; return ret;
} }
static void ext4_write_super(struct super_block *sb)
{
lock_super(sb);
ext4_commit_super(sb, 1);
unlock_super(sb);
}
static int ext4_sync_fs(struct super_block *sb, int wait) static int ext4_sync_fs(struct super_block *sb, int wait)
{ {
int ret = 0; int ret = 0;
...@@ -4567,16 +4718,26 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) ...@@ -4567,16 +4718,26 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
if (sbi->s_journal == NULL) if (sbi->s_journal == NULL)
ext4_commit_super(sb, 1); ext4_commit_super(sb, 1);
unlock_super(sb);
#ifdef CONFIG_QUOTA #ifdef CONFIG_QUOTA
/* Release old quota file names */ /* Release old quota file names */
for (i = 0; i < MAXQUOTAS; i++) for (i = 0; i < MAXQUOTAS; i++)
if (old_opts.s_qf_names[i] && if (old_opts.s_qf_names[i] &&
old_opts.s_qf_names[i] != sbi->s_qf_names[i]) old_opts.s_qf_names[i] != sbi->s_qf_names[i])
kfree(old_opts.s_qf_names[i]); kfree(old_opts.s_qf_names[i]);
#endif if (enable_quota) {
unlock_super(sb); if (sb_any_quota_suspended(sb))
if (enable_quota)
dquot_resume(sb, -1); dquot_resume(sb, -1);
else if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
EXT4_FEATURE_RO_COMPAT_QUOTA)) {
err = ext4_enable_quotas(sb);
if (err) {
lock_super(sb);
goto restore_opts;
}
}
}
#endif
ext4_msg(sb, KERN_INFO, "re-mounted. Opts: %s", orig_data); ext4_msg(sb, KERN_INFO, "re-mounted. Opts: %s", orig_data);
kfree(orig_data); kfree(orig_data);
...@@ -4605,67 +4766,21 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) ...@@ -4605,67 +4766,21 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
return err; return err;
} }
/*
* Note: calculating the overhead so we can be compatible with
* historical BSD practice is quite difficult in the face of
* clusters/bigalloc. This is because multiple metadata blocks from
* different block group can end up in the same allocation cluster.
* Calculating the exact overhead in the face of clustered allocation
* requires either O(all block bitmaps) in memory or O(number of block
* groups**2) in time. We will still calculate the superblock for
* older file systems --- and if we come across with a bigalloc file
* system with zero in s_overhead_clusters the estimate will be close to
* correct especially for very large cluster sizes --- but for newer
* file systems, it's better to calculate this figure once at mkfs
* time, and store it in the superblock. If the superblock value is
* present (even for non-bigalloc file systems), we will use it.
*/
static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf) static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
{ {
struct super_block *sb = dentry->d_sb; struct super_block *sb = dentry->d_sb;
struct ext4_sb_info *sbi = EXT4_SB(sb); struct ext4_sb_info *sbi = EXT4_SB(sb);
struct ext4_super_block *es = sbi->s_es; struct ext4_super_block *es = sbi->s_es;
struct ext4_group_desc *gdp; ext4_fsblk_t overhead = 0;
u64 fsid; u64 fsid;
s64 bfree; s64 bfree;
if (test_opt(sb, MINIX_DF)) { if (!test_opt(sb, MINIX_DF))
sbi->s_overhead_last = 0; overhead = sbi->s_overhead;
} else if (es->s_overhead_clusters) {
sbi->s_overhead_last = le32_to_cpu(es->s_overhead_clusters);
} else if (sbi->s_blocks_last != ext4_blocks_count(es)) {
ext4_group_t i, ngroups = ext4_get_groups_count(sb);
ext4_fsblk_t overhead = 0;
/*
* Compute the overhead (FS structures). This is constant
* for a given filesystem unless the number of block groups
* changes so we cache the previous value until it does.
*/
/*
* All of the blocks before first_data_block are
* overhead
*/
overhead = EXT4_B2C(sbi, le32_to_cpu(es->s_first_data_block));
/*
* Add the overhead found in each block group
*/
for (i = 0; i < ngroups; i++) {
gdp = ext4_get_group_desc(sb, i, NULL);
overhead += ext4_num_overhead_clusters(sb, i, gdp);
cond_resched();
}
sbi->s_overhead_last = overhead;
smp_wmb();
sbi->s_blocks_last = ext4_blocks_count(es);
}
buf->f_type = EXT4_SUPER_MAGIC; buf->f_type = EXT4_SUPER_MAGIC;
buf->f_bsize = sb->s_blocksize; buf->f_bsize = sb->s_blocksize;
buf->f_blocks = (ext4_blocks_count(es) - buf->f_blocks = ext4_blocks_count(es) - EXT4_C2B(sbi, sbi->s_overhead);
EXT4_C2B(sbi, sbi->s_overhead_last));
bfree = percpu_counter_sum_positive(&sbi->s_freeclusters_counter) - bfree = percpu_counter_sum_positive(&sbi->s_freeclusters_counter) -
percpu_counter_sum_positive(&sbi->s_dirtyclusters_counter); percpu_counter_sum_positive(&sbi->s_dirtyclusters_counter);
/* prevent underflow in case that few free space is available */ /* prevent underflow in case that few free space is available */
...@@ -4835,6 +4950,74 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id, ...@@ -4835,6 +4950,74 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id,
return dquot_quota_on(sb, type, format_id, path); return dquot_quota_on(sb, type, format_id, path);
} }
static int ext4_quota_enable(struct super_block *sb, int type, int format_id,
unsigned int flags)
{
int err;
struct inode *qf_inode;
unsigned long qf_inums[MAXQUOTAS] = {
le32_to_cpu(EXT4_SB(sb)->s_es->s_usr_quota_inum),
le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum)
};
BUG_ON(!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA));
if (!qf_inums[type])
return -EPERM;
qf_inode = ext4_iget(sb, qf_inums[type]);
if (IS_ERR(qf_inode)) {
ext4_error(sb, "Bad quota inode # %lu", qf_inums[type]);
return PTR_ERR(qf_inode);
}
err = dquot_enable(qf_inode, type, format_id, flags);
iput(qf_inode);
return err;
}
/* Enable usage tracking for all quota types. */
static int ext4_enable_quotas(struct super_block *sb)
{
int type, err = 0;
unsigned long qf_inums[MAXQUOTAS] = {
le32_to_cpu(EXT4_SB(sb)->s_es->s_usr_quota_inum),
le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum)
};
sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE;
for (type = 0; type < MAXQUOTAS; type++) {
if (qf_inums[type]) {
err = ext4_quota_enable(sb, type, QFMT_VFS_V1,
DQUOT_USAGE_ENABLED);
if (err) {
ext4_warning(sb,
"Failed to enable quota (type=%d) "
"tracking. Please run e2fsck to fix.",
type);
return err;
}
}
}
return 0;
}
/*
* quota_on function that is used when QUOTA feature is set.
*/
static int ext4_quota_on_sysfile(struct super_block *sb, int type,
int format_id)
{
if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA))
return -EINVAL;
/*
* USAGE was enabled at mount time. Only need to enable LIMITS now.
*/
return ext4_quota_enable(sb, type, format_id, DQUOT_LIMITS_ENABLED);
}
static int ext4_quota_off(struct super_block *sb, int type) static int ext4_quota_off(struct super_block *sb, int type)
{ {
struct inode *inode = sb_dqopt(sb)->files[type]; struct inode *inode = sb_dqopt(sb)->files[type];
...@@ -4861,6 +5044,18 @@ static int ext4_quota_off(struct super_block *sb, int type) ...@@ -4861,6 +5044,18 @@ static int ext4_quota_off(struct super_block *sb, int type)
return dquot_quota_off(sb, type); return dquot_quota_off(sb, type);
} }
/*
* quota_off function that is used when QUOTA feature is set.
*/
static int ext4_quota_off_sysfile(struct super_block *sb, int type)
{
if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA))
return -EINVAL;
/* Disable only the limits. */
return dquot_disable(sb, type, DQUOT_LIMITS_ENABLED);
}
/* Read data from quotafile - avoid pagecache and such because we cannot afford /* Read data from quotafile - avoid pagecache and such because we cannot afford
* acquiring the locks... As quota files are never truncated and quota code * acquiring the locks... As quota files are never truncated and quota code
* itself serializes the operations (and no one else should touch the files) * itself serializes the operations (and no one else should touch the files)
......
...@@ -127,19 +127,16 @@ static __le32 ext4_xattr_block_csum(struct inode *inode, ...@@ -127,19 +127,16 @@ static __le32 ext4_xattr_block_csum(struct inode *inode,
struct ext4_xattr_header *hdr) struct ext4_xattr_header *hdr)
{ {
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
struct ext4_inode_info *ei = EXT4_I(inode);
__u32 csum, old; __u32 csum, old;
old = hdr->h_checksum; old = hdr->h_checksum;
hdr->h_checksum = 0; hdr->h_checksum = 0;
if (le32_to_cpu(hdr->h_refcount) != 1) {
block_nr = cpu_to_le64(block_nr); block_nr = cpu_to_le64(block_nr);
csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&block_nr, csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&block_nr,
sizeof(block_nr)); sizeof(block_nr));
} else
csum = ei->i_csum_seed;
csum = ext4_chksum(sbi, csum, (__u8 *)hdr, csum = ext4_chksum(sbi, csum, (__u8 *)hdr,
EXT4_BLOCK_SIZE(inode->i_sb)); EXT4_BLOCK_SIZE(inode->i_sb));
hdr->h_checksum = old; hdr->h_checksum = old;
return cpu_to_le32(csum); return cpu_to_le32(csum);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment