mirror of
https://github.com/armbian/linux.git
synced 2026-01-06 10:13:00 -08:00
Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4
Pull ext4 update from Ted Ts'o: "There are two major features for this merge window. The first is inline data, which allows small files or directories to be stored in the in-inode extended attribute area. (This requires that the file system use inodes which are at least 256 bytes or larger; 128 byte inodes do not have any room for in-inode xattrs.) The second new feature is SEEK_HOLE/SEEK_DATA support. This is enabled by the extent status tree patches, and this infrastructure will be used to further optimize ext4 in the future. Beyond that, we have the usual collection of code cleanups and bug fixes." * tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (63 commits) ext4: zero out inline data using memset() instead of empty_zero_page ext4: ensure Inode flags consistency are checked at build time ext4: Remove CONFIG_EXT4_FS_XATTR ext4: remove unused variable from ext4_ext_in_cache() ext4: remove redundant initialization in ext4_fill_super() ext4: remove redundant code in ext4_alloc_inode() ext4: use sync_inode_metadata() when syncing inode metadata ext4: enable ext4 inline support ext4: let fallocate handle inline data correctly ext4: let ext4_truncate handle inline data correctly ext4: evict inline data out if we need to strore xattr in inode ext4: let fiemap work with inline data ext4: let ext4_rename handle inline dir ext4: let empty_dir handle inline dir ext4: let ext4_delete_entry() handle inline data ext4: make ext4_delete_entry generic ext4: let ext4_find_entry handle inline data ext4: create a new function search_dir ext4: let ext4_readdir handle inline data ext4: let add_dir_entry handle inline data properly ...
This commit is contained in:
@@ -200,12 +200,9 @@ inode_readahead_blks=n This tuning parameter controls the maximum
|
||||
table readahead algorithm will pre-read into
|
||||
the buffer cache. The default value is 32 blocks.
|
||||
|
||||
nouser_xattr Disables Extended User Attributes. If you have extended
|
||||
attribute support enabled in the kernel configuration
|
||||
(CONFIG_EXT4_FS_XATTR), extended attribute support
|
||||
is enabled by default on mount. See the attr(5) manual
|
||||
page and http://acl.bestbits.at/ for more information
|
||||
about extended attributes.
|
||||
nouser_xattr Disables Extended User Attributes. See the
|
||||
attr(5) manual page and http://acl.bestbits.at/
|
||||
for more information about extended attributes.
|
||||
|
||||
noacl This option disables POSIX Access Control List
|
||||
support. If ACL support is enabled in the kernel
|
||||
|
||||
@@ -28,8 +28,8 @@ config FS_MBCACHE
|
||||
tristate
|
||||
default y if EXT2_FS=y && EXT2_FS_XATTR
|
||||
default y if EXT3_FS=y && EXT3_FS_XATTR
|
||||
default y if EXT4_FS=y && EXT4_FS_XATTR
|
||||
default m if EXT2_FS_XATTR || EXT3_FS_XATTR || EXT4_FS_XATTR
|
||||
default y if EXT4_FS=y
|
||||
default m if EXT2_FS_XATTR || EXT3_FS_XATTR || EXT4_FS
|
||||
|
||||
source "fs/reiserfs/Kconfig"
|
||||
source "fs/jfs/Kconfig"
|
||||
|
||||
@@ -39,22 +39,8 @@ config EXT4_USE_FOR_EXT23
|
||||
compiled kernel size by using one file system driver for
|
||||
ext2, ext3, and ext4 file systems.
|
||||
|
||||
config EXT4_FS_XATTR
|
||||
bool "Ext4 extended attributes"
|
||||
depends on EXT4_FS
|
||||
default y
|
||||
help
|
||||
Extended attributes are name:value pairs associated with inodes by
|
||||
the kernel or by users (see the attr(5) manual page, or visit
|
||||
<http://acl.bestbits.at/> for details).
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
You need this for POSIX ACL support on ext4.
|
||||
|
||||
config EXT4_FS_POSIX_ACL
|
||||
bool "Ext4 POSIX Access Control Lists"
|
||||
depends on EXT4_FS_XATTR
|
||||
select FS_POSIX_ACL
|
||||
help
|
||||
POSIX Access Control Lists (ACLs) support permissions for users and
|
||||
@@ -67,7 +53,6 @@ config EXT4_FS_POSIX_ACL
|
||||
|
||||
config EXT4_FS_SECURITY
|
||||
bool "Ext4 Security Labels"
|
||||
depends on EXT4_FS_XATTR
|
||||
help
|
||||
Security labels support alternative access control models
|
||||
implemented by security modules like SELinux. This option
|
||||
|
||||
@@ -7,8 +7,8 @@ obj-$(CONFIG_EXT4_FS) += ext4.o
|
||||
ext4-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \
|
||||
ioctl.o namei.o super.o symlink.o hash.o resize.o extents.o \
|
||||
ext4_jbd2.o migrate.o mballoc.o block_validity.o move_extent.o \
|
||||
mmp.o indirect.o
|
||||
mmp.o indirect.o extents_status.o xattr.o xattr_user.o \
|
||||
xattr_trusted.o inline.o
|
||||
|
||||
ext4-$(CONFIG_EXT4_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o
|
||||
ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o
|
||||
ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o
|
||||
|
||||
@@ -423,8 +423,10 @@ ext4_xattr_set_acl(struct dentry *dentry, const char *name, const void *value,
|
||||
|
||||
retry:
|
||||
handle = ext4_journal_start(inode, EXT4_DATA_TRANS_BLOCKS(inode->i_sb));
|
||||
if (IS_ERR(handle))
|
||||
return PTR_ERR(handle);
|
||||
if (IS_ERR(handle)) {
|
||||
error = PTR_ERR(handle);
|
||||
goto release_and_out;
|
||||
}
|
||||
error = ext4_set_acl(handle, inode, type, acl);
|
||||
ext4_journal_stop(handle);
|
||||
if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
|
||||
|
||||
@@ -27,23 +27,11 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include "ext4.h"
|
||||
|
||||
static unsigned char ext4_filetype_table[] = {
|
||||
DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK
|
||||
};
|
||||
#include "xattr.h"
|
||||
|
||||
static int ext4_dx_readdir(struct file *filp,
|
||||
void *dirent, filldir_t filldir);
|
||||
|
||||
static unsigned char get_dtype(struct super_block *sb, int filetype)
|
||||
{
|
||||
if (!EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FILETYPE) ||
|
||||
(filetype >= EXT4_FT_MAX))
|
||||
return DT_UNKNOWN;
|
||||
|
||||
return (ext4_filetype_table[filetype]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given dir-inode refers to an htree-indexed directory
|
||||
* (or a directory which chould potentially get coverted to use htree
|
||||
@@ -68,11 +56,14 @@ static int is_dx_dir(struct inode *inode)
|
||||
* Return 0 if the directory entry is OK, and 1 if there is a problem
|
||||
*
|
||||
* Note: this is the opposite of what ext2 and ext3 historically returned...
|
||||
*
|
||||
* bh passed here can be an inode block or a dir data block, depending
|
||||
* on the inode inline data flag.
|
||||
*/
|
||||
int __ext4_check_dir_entry(const char *function, unsigned int line,
|
||||
struct inode *dir, struct file *filp,
|
||||
struct ext4_dir_entry_2 *de,
|
||||
struct buffer_head *bh,
|
||||
struct buffer_head *bh, char *buf, int size,
|
||||
unsigned int offset)
|
||||
{
|
||||
const char *error_msg = NULL;
|
||||
@@ -85,9 +76,8 @@ int __ext4_check_dir_entry(const char *function, unsigned int line,
|
||||
error_msg = "rec_len % 4 != 0";
|
||||
else if (unlikely(rlen < EXT4_DIR_REC_LEN(de->name_len)))
|
||||
error_msg = "rec_len is too small for name_len";
|
||||
else if (unlikely(((char *) de - bh->b_data) + rlen >
|
||||
dir->i_sb->s_blocksize))
|
||||
error_msg = "directory entry across blocks";
|
||||
else if (unlikely(((char *) de - buf) + rlen > size))
|
||||
error_msg = "directory entry across range";
|
||||
else if (unlikely(le32_to_cpu(de->inode) >
|
||||
le32_to_cpu(EXT4_SB(dir->i_sb)->s_es->s_inodes_count)))
|
||||
error_msg = "inode out of bounds";
|
||||
@@ -98,14 +88,14 @@ int __ext4_check_dir_entry(const char *function, unsigned int line,
|
||||
ext4_error_file(filp, function, line, bh->b_blocknr,
|
||||
"bad entry in directory: %s - offset=%u(%u), "
|
||||
"inode=%u, rec_len=%d, name_len=%d",
|
||||
error_msg, (unsigned) (offset % bh->b_size),
|
||||
error_msg, (unsigned) (offset % size),
|
||||
offset, le32_to_cpu(de->inode),
|
||||
rlen, de->name_len);
|
||||
else
|
||||
ext4_error_inode(dir, function, line, bh->b_blocknr,
|
||||
"bad entry in directory: %s - offset=%u(%u), "
|
||||
"inode=%u, rec_len=%d, name_len=%d",
|
||||
error_msg, (unsigned) (offset % bh->b_size),
|
||||
error_msg, (unsigned) (offset % size),
|
||||
offset, le32_to_cpu(de->inode),
|
||||
rlen, de->name_len);
|
||||
|
||||
@@ -125,6 +115,14 @@ static int ext4_readdir(struct file *filp,
|
||||
int ret = 0;
|
||||
int dir_has_error = 0;
|
||||
|
||||
if (ext4_has_inline_data(inode)) {
|
||||
int has_inline_data = 1;
|
||||
ret = ext4_read_inline_dir(filp, dirent, filldir,
|
||||
&has_inline_data);
|
||||
if (has_inline_data)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (is_dx_dir(inode)) {
|
||||
err = ext4_dx_readdir(filp, dirent, filldir);
|
||||
if (err != ERR_BAD_DX_DIR) {
|
||||
@@ -221,8 +219,9 @@ revalidate:
|
||||
while (!error && filp->f_pos < inode->i_size
|
||||
&& offset < sb->s_blocksize) {
|
||||
de = (struct ext4_dir_entry_2 *) (bh->b_data + offset);
|
||||
if (ext4_check_dir_entry(inode, filp, de,
|
||||
bh, offset)) {
|
||||
if (ext4_check_dir_entry(inode, filp, de, bh,
|
||||
bh->b_data, bh->b_size,
|
||||
offset)) {
|
||||
/*
|
||||
* On error, skip the f_pos to the next block
|
||||
*/
|
||||
|
||||
163
fs/ext4/ext4.h
163
fs/ext4/ext4.h
@@ -57,6 +57,16 @@
|
||||
#define ext4_debug(fmt, ...) no_printk(fmt, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Turn on EXT_DEBUG to get lots of info about extents operations.
|
||||
*/
|
||||
#define EXT_DEBUG__
|
||||
#ifdef EXT_DEBUG
|
||||
#define ext_debug(fmt, ...) printk(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define ext_debug(fmt, ...) no_printk(fmt, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#define EXT4_ERROR_INODE(inode, fmt, a...) \
|
||||
ext4_error_inode((inode), __func__, __LINE__, 0, (fmt), ## a)
|
||||
|
||||
@@ -392,6 +402,7 @@ struct flex_groups {
|
||||
#define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */
|
||||
#define EXT4_EA_INODE_FL 0x00200000 /* Inode used for large EA */
|
||||
#define EXT4_EOFBLOCKS_FL 0x00400000 /* Blocks allocated beyond EOF */
|
||||
#define EXT4_INLINE_DATA_FL 0x10000000 /* Inode has inline data. */
|
||||
#define EXT4_RESERVED_FL 0x80000000 /* reserved for ext4 lib */
|
||||
|
||||
#define EXT4_FL_USER_VISIBLE 0x004BDFFF /* User visible flags */
|
||||
@@ -448,28 +459,26 @@ enum {
|
||||
EXT4_INODE_EXTENTS = 19, /* Inode uses extents */
|
||||
EXT4_INODE_EA_INODE = 21, /* Inode used for large EA */
|
||||
EXT4_INODE_EOFBLOCKS = 22, /* Blocks allocated beyond EOF */
|
||||
EXT4_INODE_INLINE_DATA = 28, /* Data in inode. */
|
||||
EXT4_INODE_RESERVED = 31, /* reserved for ext4 lib */
|
||||
};
|
||||
|
||||
#define TEST_FLAG_VALUE(FLAG) (EXT4_##FLAG##_FL == (1 << EXT4_INODE_##FLAG))
|
||||
#define CHECK_FLAG_VALUE(FLAG) if (!TEST_FLAG_VALUE(FLAG)) { \
|
||||
printk(KERN_EMERG "EXT4 flag fail: " #FLAG ": %d %d\n", \
|
||||
EXT4_##FLAG##_FL, EXT4_INODE_##FLAG); BUG_ON(1); }
|
||||
|
||||
/*
|
||||
* Since it's pretty easy to mix up bit numbers and hex values, and we
|
||||
* can't do a compile-time test for ENUM values, we use a run-time
|
||||
* test to make sure that EXT4_XXX_FL is consistent with respect to
|
||||
* EXT4_INODE_XXX. If all is well the printk and BUG_ON will all drop
|
||||
* out so it won't cost any extra space in the compiled kernel image.
|
||||
* But it's important that these values are the same, since we are
|
||||
* using EXT4_INODE_XXX to test for the flag values, but EXT4_XX_FL
|
||||
* must be consistent with the values of FS_XXX_FL defined in
|
||||
* include/linux/fs.h and the on-disk values found in ext2, ext3, and
|
||||
* ext4 filesystems, and of course the values defined in e2fsprogs.
|
||||
* Since it's pretty easy to mix up bit numbers and hex values, we use a
|
||||
* build-time check to make sure that EXT4_XXX_FL is consistent with respect to
|
||||
* EXT4_INODE_XXX. If all is well, the macros will be dropped, so, it won't cost
|
||||
* any extra space in the compiled kernel image, otherwise, the build will fail.
|
||||
* It's important that these values are the same, since we are using
|
||||
* EXT4_INODE_XXX to test for flag values, but EXT4_XXX_FL must be consistent
|
||||
* with the values of FS_XXX_FL defined in include/linux/fs.h and the on-disk
|
||||
* values found in ext2, ext3 and ext4 filesystems, and of course the values
|
||||
* defined in e2fsprogs.
|
||||
*
|
||||
* It's not paranoia if the Murphy's Law really *is* out to get you. :-)
|
||||
*/
|
||||
#define TEST_FLAG_VALUE(FLAG) (EXT4_##FLAG##_FL == (1 << EXT4_INODE_##FLAG))
|
||||
#define CHECK_FLAG_VALUE(FLAG) BUILD_BUG_ON(!TEST_FLAG_VALUE(FLAG))
|
||||
|
||||
static inline void ext4_check_flag_values(void)
|
||||
{
|
||||
CHECK_FLAG_VALUE(SECRM);
|
||||
@@ -494,6 +503,7 @@ static inline void ext4_check_flag_values(void)
|
||||
CHECK_FLAG_VALUE(EXTENTS);
|
||||
CHECK_FLAG_VALUE(EA_INODE);
|
||||
CHECK_FLAG_VALUE(EOFBLOCKS);
|
||||
CHECK_FLAG_VALUE(INLINE_DATA);
|
||||
CHECK_FLAG_VALUE(RESERVED);
|
||||
}
|
||||
|
||||
@@ -811,6 +821,8 @@ struct ext4_ext_cache {
|
||||
__u32 ec_len; /* must be 32bit to return holes */
|
||||
};
|
||||
|
||||
#include "extents_status.h"
|
||||
|
||||
/*
|
||||
* fourth extended file system inode data in memory
|
||||
*/
|
||||
@@ -833,7 +845,6 @@ struct ext4_inode_info {
|
||||
#endif
|
||||
unsigned long i_flags;
|
||||
|
||||
#ifdef CONFIG_EXT4_FS_XATTR
|
||||
/*
|
||||
* Extended attributes can be read independently of the main file
|
||||
* data. Taking i_mutex even when reading would cause contention
|
||||
@@ -842,7 +853,6 @@ struct ext4_inode_info {
|
||||
* EAs.
|
||||
*/
|
||||
struct rw_semaphore xattr_sem;
|
||||
#endif
|
||||
|
||||
struct list_head i_orphan; /* unlinked but open inodes */
|
||||
|
||||
@@ -888,6 +898,10 @@ struct ext4_inode_info {
|
||||
struct list_head i_prealloc_list;
|
||||
spinlock_t i_prealloc_lock;
|
||||
|
||||
/* extents status tree */
|
||||
struct ext4_es_tree i_es_tree;
|
||||
rwlock_t i_es_lock;
|
||||
|
||||
/* ialloc */
|
||||
ext4_group_t i_last_alloc_group;
|
||||
|
||||
@@ -902,6 +916,10 @@ struct ext4_inode_info {
|
||||
/* on-disk additional length */
|
||||
__u16 i_extra_isize;
|
||||
|
||||
/* Indicate the inline data space. */
|
||||
u16 i_inline_off;
|
||||
u16 i_inline_size;
|
||||
|
||||
#ifdef CONFIG_QUOTA
|
||||
/* quota space reservation, managed internally by quota code */
|
||||
qsize_t i_reserved_quota;
|
||||
@@ -1360,6 +1378,7 @@ enum {
|
||||
EXT4_STATE_DELALLOC_RESERVED, /* blks already reserved for delalloc */
|
||||
EXT4_STATE_DIOREAD_LOCK, /* Disable support for dio read
|
||||
nolocking */
|
||||
EXT4_STATE_MAY_INLINE_DATA, /* may have in-inode data */
|
||||
};
|
||||
|
||||
#define EXT4_INODE_BIT_FNS(name, field, offset) \
|
||||
@@ -1481,7 +1500,7 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
|
||||
#define EXT4_FEATURE_INCOMPAT_DIRDATA 0x1000 /* data in dirent */
|
||||
#define EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM 0x2000 /* use crc32c for bg */
|
||||
#define EXT4_FEATURE_INCOMPAT_LARGEDIR 0x4000 /* >2GB or 3-lvl htree */
|
||||
#define EXT4_FEATURE_INCOMPAT_INLINEDATA 0x8000 /* data in inode */
|
||||
#define EXT4_FEATURE_INCOMPAT_INLINE_DATA 0x8000 /* data in inode */
|
||||
|
||||
#define EXT2_FEATURE_COMPAT_SUPP EXT4_FEATURE_COMPAT_EXT_ATTR
|
||||
#define EXT2_FEATURE_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE| \
|
||||
@@ -1505,7 +1524,8 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
|
||||
EXT4_FEATURE_INCOMPAT_EXTENTS| \
|
||||
EXT4_FEATURE_INCOMPAT_64BIT| \
|
||||
EXT4_FEATURE_INCOMPAT_FLEX_BG| \
|
||||
EXT4_FEATURE_INCOMPAT_MMP)
|
||||
EXT4_FEATURE_INCOMPAT_MMP | \
|
||||
EXT4_FEATURE_INCOMPAT_INLINE_DATA)
|
||||
#define EXT4_FEATURE_RO_COMPAT_SUPP (EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER| \
|
||||
EXT4_FEATURE_RO_COMPAT_LARGE_FILE| \
|
||||
EXT4_FEATURE_RO_COMPAT_GDT_CSUM| \
|
||||
@@ -1592,6 +1612,11 @@ struct ext4_dir_entry_tail {
|
||||
__le32 det_checksum; /* crc32c(uuid+inum+dirblock) */
|
||||
};
|
||||
|
||||
#define EXT4_DIRENT_TAIL(block, blocksize) \
|
||||
((struct ext4_dir_entry_tail *)(((void *)(block)) + \
|
||||
((blocksize) - \
|
||||
sizeof(struct ext4_dir_entry_tail))))
|
||||
|
||||
/*
|
||||
* Ext4 directory file types. Only the low 3 bits are used. The
|
||||
* other bits are reserved for now.
|
||||
@@ -1936,14 +1961,42 @@ ext4_fsblk_t ext4_inode_to_goal_block(struct inode *);
|
||||
extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *,
|
||||
struct file *,
|
||||
struct ext4_dir_entry_2 *,
|
||||
struct buffer_head *, unsigned int);
|
||||
#define ext4_check_dir_entry(dir, filp, de, bh, offset) \
|
||||
struct buffer_head *, char *, int,
|
||||
unsigned int);
|
||||
#define ext4_check_dir_entry(dir, filp, de, bh, buf, size, offset) \
|
||||
unlikely(__ext4_check_dir_entry(__func__, __LINE__, (dir), (filp), \
|
||||
(de), (bh), (offset)))
|
||||
(de), (bh), (buf), (size), (offset)))
|
||||
extern int ext4_htree_store_dirent(struct file *dir_file, __u32 hash,
|
||||
__u32 minor_hash,
|
||||
struct ext4_dir_entry_2 *dirent);
|
||||
extern void ext4_htree_free_dir_info(struct dir_private_info *p);
|
||||
extern int ext4_find_dest_de(struct inode *dir, struct inode *inode,
|
||||
struct buffer_head *bh,
|
||||
void *buf, int buf_size,
|
||||
const char *name, int namelen,
|
||||
struct ext4_dir_entry_2 **dest_de);
|
||||
void ext4_insert_dentry(struct inode *inode,
|
||||
struct ext4_dir_entry_2 *de,
|
||||
int buf_size,
|
||||
const char *name, int namelen);
|
||||
static inline void ext4_update_dx_flag(struct inode *inode)
|
||||
{
|
||||
if (!EXT4_HAS_COMPAT_FEATURE(inode->i_sb,
|
||||
EXT4_FEATURE_COMPAT_DIR_INDEX))
|
||||
ext4_clear_inode_flag(inode, EXT4_INODE_INDEX);
|
||||
}
|
||||
static unsigned char ext4_filetype_table[] = {
|
||||
DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK
|
||||
};
|
||||
|
||||
static inline unsigned char get_dtype(struct super_block *sb, int filetype)
|
||||
{
|
||||
if (!EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FILETYPE) ||
|
||||
(filetype >= EXT4_FT_MAX))
|
||||
return DT_UNKNOWN;
|
||||
|
||||
return ext4_filetype_table[filetype];
|
||||
}
|
||||
|
||||
/* fsync.c */
|
||||
extern int ext4_sync_file(struct file *, loff_t, loff_t, int);
|
||||
@@ -1994,8 +2047,23 @@ struct buffer_head *ext4_getblk(handle_t *, struct inode *,
|
||||
ext4_lblk_t, int, int *);
|
||||
struct buffer_head *ext4_bread(handle_t *, struct inode *,
|
||||
ext4_lblk_t, int, int *);
|
||||
int ext4_get_block_write(struct inode *inode, sector_t iblock,
|
||||
struct buffer_head *bh_result, int create);
|
||||
int ext4_get_block(struct inode *inode, sector_t iblock,
|
||||
struct buffer_head *bh_result, int create);
|
||||
int ext4_da_get_block_prep(struct inode *inode, sector_t iblock,
|
||||
struct buffer_head *bh, int create);
|
||||
int ext4_walk_page_buffers(handle_t *handle,
|
||||
struct buffer_head *head,
|
||||
unsigned from,
|
||||
unsigned to,
|
||||
int *partial,
|
||||
int (*fn)(handle_t *handle,
|
||||
struct buffer_head *bh));
|
||||
int do_journal_get_write_access(handle_t *handle,
|
||||
struct buffer_head *bh);
|
||||
#define FALL_BACK_TO_NONDELALLOC 1
|
||||
#define CONVERT_INLINE_DATA 2
|
||||
|
||||
extern struct inode *ext4_iget(struct super_block *, unsigned long);
|
||||
extern int ext4_write_inode(struct inode *, struct writeback_control *);
|
||||
@@ -2050,6 +2118,20 @@ extern int ext4_orphan_add(handle_t *, struct inode *);
|
||||
extern int ext4_orphan_del(handle_t *, struct inode *);
|
||||
extern int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash,
|
||||
__u32 start_minor_hash, __u32 *next_hash);
|
||||
extern int search_dir(struct buffer_head *bh,
|
||||
char *search_buf,
|
||||
int buf_size,
|
||||
struct inode *dir,
|
||||
const struct qstr *d_name,
|
||||
unsigned int offset,
|
||||
struct ext4_dir_entry_2 **res_dir);
|
||||
extern int ext4_generic_delete_entry(handle_t *handle,
|
||||
struct inode *dir,
|
||||
struct ext4_dir_entry_2 *de_del,
|
||||
struct buffer_head *bh,
|
||||
void *entry_buf,
|
||||
int buf_size,
|
||||
int csum_size);
|
||||
|
||||
/* resize.c */
|
||||
extern int ext4_group_add(struct super_block *sb,
|
||||
@@ -2376,6 +2458,15 @@ extern void ext4_unwritten_wait(struct inode *inode);
|
||||
extern const struct inode_operations ext4_dir_inode_operations;
|
||||
extern const struct inode_operations ext4_special_inode_operations;
|
||||
extern struct dentry *ext4_get_parent(struct dentry *child);
|
||||
extern struct ext4_dir_entry_2 *ext4_init_dot_dotdot(struct inode *inode,
|
||||
struct ext4_dir_entry_2 *de,
|
||||
int blocksize, int csum_size,
|
||||
unsigned int parent_ino, int dotdot_real_len);
|
||||
extern void initialize_dirent_tail(struct ext4_dir_entry_tail *t,
|
||||
unsigned int blocksize);
|
||||
extern int ext4_handle_dirty_dirent_node(handle_t *handle,
|
||||
struct inode *inode,
|
||||
struct buffer_head *bh);
|
||||
|
||||
/* symlink.c */
|
||||
extern const struct inode_operations ext4_symlink_inode_operations;
|
||||
@@ -2393,6 +2484,9 @@ extern int ext4_check_blockref(const char *, unsigned int,
|
||||
struct inode *, __le32 *, unsigned int);
|
||||
|
||||
/* extents.c */
|
||||
struct ext4_ext_path;
|
||||
struct ext4_extent;
|
||||
|
||||
extern int ext4_ext_tree_init(handle_t *handle, struct inode *);
|
||||
extern int ext4_ext_writepage_trans_blocks(struct inode *, int);
|
||||
extern int ext4_ext_index_trans_blocks(struct inode *inode, int nrblocks,
|
||||
@@ -2410,8 +2504,27 @@ extern int ext4_convert_unwritten_extents(struct inode *inode, loff_t offset,
|
||||
ssize_t len);
|
||||
extern int ext4_map_blocks(handle_t *handle, struct inode *inode,
|
||||
struct ext4_map_blocks *map, int flags);
|
||||
extern int ext4_ext_calc_metadata_amount(struct inode *inode,
|
||||
ext4_lblk_t lblocks);
|
||||
extern int ext4_extent_tree_init(handle_t *, struct inode *);
|
||||
extern int ext4_ext_calc_credits_for_single_extent(struct inode *inode,
|
||||
int num,
|
||||
struct ext4_ext_path *path);
|
||||
extern int ext4_can_extents_be_merged(struct inode *inode,
|
||||
struct ext4_extent *ex1,
|
||||
struct ext4_extent *ex2);
|
||||
extern int ext4_ext_insert_extent(handle_t *, struct inode *,
|
||||
struct ext4_ext_path *,
|
||||
struct ext4_extent *, int);
|
||||
extern struct ext4_ext_path *ext4_ext_find_extent(struct inode *, ext4_lblk_t,
|
||||
struct ext4_ext_path *);
|
||||
extern void ext4_ext_drop_refs(struct ext4_ext_path *);
|
||||
extern int ext4_ext_check_inode(struct inode *inode);
|
||||
extern int ext4_find_delalloc_cluster(struct inode *inode, ext4_lblk_t lblk);
|
||||
extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||
__u64 start, __u64 len);
|
||||
|
||||
|
||||
/* move_extent.c */
|
||||
extern int ext4_move_extents(struct file *o_filp, struct file *d_filp,
|
||||
__u64 start_orig, __u64 start_donor,
|
||||
@@ -2445,14 +2558,10 @@ enum ext4_state_bits {
|
||||
* never, ever appear in a buffer_head's state
|
||||
* flag. See EXT4_MAP_FROM_CLUSTER to see where
|
||||
* this is used. */
|
||||
BH_Da_Mapped, /* Delayed allocated block that now has a mapping. This
|
||||
* flag is set when ext4_map_blocks is called on a
|
||||
* delayed allocated block to get its real mapping. */
|
||||
};
|
||||
|
||||
BUFFER_FNS(Uninit, uninit)
|
||||
TAS_BUFFER_FNS(Uninit, uninit)
|
||||
BUFFER_FNS(Da_Mapped, da_mapped)
|
||||
|
||||
/*
|
||||
* Add new method to test whether block and inode bitmaps are properly
|
||||
@@ -2503,6 +2612,4 @@ extern void ext4_resize_end(struct super_block *sb);
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#include "ext4_extents.h"
|
||||
|
||||
#endif /* _EXT4_H */
|
||||
|
||||
@@ -42,16 +42,6 @@
|
||||
*/
|
||||
#define CHECK_BINSEARCH__
|
||||
|
||||
/*
|
||||
* Turn on EXT_DEBUG to get lots of info about extents operations.
|
||||
*/
|
||||
#define EXT_DEBUG__
|
||||
#ifdef EXT_DEBUG
|
||||
#define ext_debug(fmt, ...) printk(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define ext_debug(fmt, ...) no_printk(fmt, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If EXT_STATS is defined then stats numbers are collected.
|
||||
* These number will be displayed at umount time.
|
||||
@@ -143,20 +133,6 @@ struct ext4_ext_path {
|
||||
* structure for external API
|
||||
*/
|
||||
|
||||
/*
|
||||
* to be called by ext4_ext_walk_space()
|
||||
* negative retcode - error
|
||||
* positive retcode - signal for ext4_ext_walk_space(), see below
|
||||
* callback must return valid extent (passed or newly created)
|
||||
*/
|
||||
typedef int (*ext_prepare_callback)(struct inode *, ext4_lblk_t,
|
||||
struct ext4_ext_cache *,
|
||||
struct ext4_extent *, void *);
|
||||
|
||||
#define EXT_CONTINUE 0
|
||||
#define EXT_BREAK 1
|
||||
#define EXT_REPEAT 2
|
||||
|
||||
/*
|
||||
* Maximum number of logical blocks in a file; ext4_extent's ee_block is
|
||||
* __le32.
|
||||
@@ -300,21 +276,5 @@ static inline void ext4_idx_store_pblock(struct ext4_extent_idx *ix,
|
||||
0xffff);
|
||||
}
|
||||
|
||||
extern int ext4_ext_calc_metadata_amount(struct inode *inode,
|
||||
ext4_lblk_t lblocks);
|
||||
extern int ext4_extent_tree_init(handle_t *, struct inode *);
|
||||
extern int ext4_ext_calc_credits_for_single_extent(struct inode *inode,
|
||||
int num,
|
||||
struct ext4_ext_path *path);
|
||||
extern int ext4_can_extents_be_merged(struct inode *inode,
|
||||
struct ext4_extent *ex1,
|
||||
struct ext4_extent *ex2);
|
||||
extern int ext4_ext_insert_extent(handle_t *, struct inode *, struct ext4_ext_path *, struct ext4_extent *, int);
|
||||
extern struct ext4_ext_path *ext4_ext_find_extent(struct inode *, ext4_lblk_t,
|
||||
struct ext4_ext_path *);
|
||||
extern void ext4_ext_drop_refs(struct ext4_ext_path *);
|
||||
extern int ext4_ext_check_inode(struct inode *inode);
|
||||
extern int ext4_find_delalloc_cluster(struct inode *inode, ext4_lblk_t lblk,
|
||||
int search_hint_reverse);
|
||||
#endif /* _EXT4_EXTENTS */
|
||||
|
||||
|
||||
@@ -254,13 +254,6 @@ static inline void ext4_handle_sync(handle_t *handle)
|
||||
handle->h_sync = 1;
|
||||
}
|
||||
|
||||
static inline void ext4_handle_release_buffer(handle_t *handle,
|
||||
struct buffer_head *bh)
|
||||
{
|
||||
if (ext4_handle_valid(handle))
|
||||
jbd2_journal_release_buffer(handle, bh);
|
||||
}
|
||||
|
||||
static inline int ext4_handle_is_aborted(handle_t *handle)
|
||||
{
|
||||
if (ext4_handle_valid(handle))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
500
fs/ext4/extents_status.c
Normal file
500
fs/ext4/extents_status.c
Normal file
File diff suppressed because it is too large
Load Diff
45
fs/ext4/extents_status.h
Normal file
45
fs/ext4/extents_status.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* fs/ext4/extents_status.h
|
||||
*
|
||||
* Written by Yongqiang Yang <xiaoqiangnk@gmail.com>
|
||||
* Modified by
|
||||
* Allison Henderson <achender@linux.vnet.ibm.com>
|
||||
* Zheng Liu <wenqing.lz@taobao.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _EXT4_EXTENTS_STATUS_H
|
||||
#define _EXT4_EXTENTS_STATUS_H
|
||||
|
||||
/*
|
||||
* Turn on ES_DEBUG__ to get lots of info about extent status operations.
|
||||
*/
|
||||
#ifdef ES_DEBUG__
|
||||
#define es_debug(fmt, ...) printk(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define es_debug(fmt, ...) no_printk(fmt, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
struct extent_status {
|
||||
struct rb_node rb_node;
|
||||
ext4_lblk_t start; /* first block extent covers */
|
||||
ext4_lblk_t len; /* length of extent in block */
|
||||
};
|
||||
|
||||
struct ext4_es_tree {
|
||||
struct rb_root root;
|
||||
struct extent_status *cache_es; /* recently accessed extent */
|
||||
};
|
||||
|
||||
extern int __init ext4_init_es(void);
|
||||
extern void ext4_exit_es(void);
|
||||
extern void ext4_es_init_tree(struct ext4_es_tree *tree);
|
||||
|
||||
extern int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t start,
|
||||
ext4_lblk_t len);
|
||||
extern int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t start,
|
||||
ext4_lblk_t len);
|
||||
extern ext4_lblk_t ext4_es_find_extent(struct inode *inode,
|
||||
struct extent_status *es);
|
||||
|
||||
#endif /* _EXT4_EXTENTS_STATUS_H */
|
||||
336
fs/ext4/file.c
336
fs/ext4/file.c
@@ -24,6 +24,7 @@
|
||||
#include <linux/mount.h>
|
||||
#include <linux/path.h>
|
||||
#include <linux/quotaops.h>
|
||||
#include <linux/pagevec.h>
|
||||
#include "ext4.h"
|
||||
#include "ext4_jbd2.h"
|
||||
#include "xattr.h"
|
||||
@@ -285,6 +286,324 @@ static int ext4_file_open(struct inode * inode, struct file * filp)
|
||||
return dquot_file_open(inode, filp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Here we use ext4_map_blocks() to get a block mapping for a extent-based
|
||||
* file rather than ext4_ext_walk_space() because we can introduce
|
||||
* SEEK_DATA/SEEK_HOLE for block-mapped and extent-mapped file at the same
|
||||
* function. When extent status tree has been fully implemented, it will
|
||||
* track all extent status for a file and we can directly use it to
|
||||
* retrieve the offset for SEEK_DATA/SEEK_HOLE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* When we retrieve the offset for SEEK_DATA/SEEK_HOLE, we would need to
|
||||
* lookup page cache to check whether or not there has some data between
|
||||
* [startoff, endoff] because, if this range contains an unwritten extent,
|
||||
* we determine this extent as a data or a hole according to whether the
|
||||
* page cache has data or not.
|
||||
*/
|
||||
static int ext4_find_unwritten_pgoff(struct inode *inode,
|
||||
int origin,
|
||||
struct ext4_map_blocks *map,
|
||||
loff_t *offset)
|
||||
{
|
||||
struct pagevec pvec;
|
||||
unsigned int blkbits;
|
||||
pgoff_t index;
|
||||
pgoff_t end;
|
||||
loff_t endoff;
|
||||
loff_t startoff;
|
||||
loff_t lastoff;
|
||||
int found = 0;
|
||||
|
||||
blkbits = inode->i_sb->s_blocksize_bits;
|
||||
startoff = *offset;
|
||||
lastoff = startoff;
|
||||
endoff = (map->m_lblk + map->m_len) << blkbits;
|
||||
|
||||
index = startoff >> PAGE_CACHE_SHIFT;
|
||||
end = endoff >> PAGE_CACHE_SHIFT;
|
||||
|
||||
pagevec_init(&pvec, 0);
|
||||
do {
|
||||
int i, num;
|
||||
unsigned long nr_pages;
|
||||
|
||||
num = min_t(pgoff_t, end - index, PAGEVEC_SIZE);
|
||||
nr_pages = pagevec_lookup(&pvec, inode->i_mapping, index,
|
||||
(pgoff_t)num);
|
||||
if (nr_pages == 0) {
|
||||
if (origin == SEEK_DATA)
|
||||
break;
|
||||
|
||||
BUG_ON(origin != SEEK_HOLE);
|
||||
/*
|
||||
* If this is the first time to go into the loop and
|
||||
* offset is not beyond the end offset, it will be a
|
||||
* hole at this offset
|
||||
*/
|
||||
if (lastoff == startoff || lastoff < endoff)
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this is the first time to go into the loop and
|
||||
* offset is smaller than the first page offset, it will be a
|
||||
* hole at this offset.
|
||||
*/
|
||||
if (lastoff == startoff && origin == SEEK_HOLE &&
|
||||
lastoff < page_offset(pvec.pages[0])) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < nr_pages; i++) {
|
||||
struct page *page = pvec.pages[i];
|
||||
struct buffer_head *bh, *head;
|
||||
|
||||
/*
|
||||
* If the current offset is not beyond the end of given
|
||||
* range, it will be a hole.
|
||||
*/
|
||||
if (lastoff < endoff && origin == SEEK_HOLE &&
|
||||
page->index > end) {
|
||||
found = 1;
|
||||
*offset = lastoff;
|
||||
goto out;
|
||||
}
|
||||
|
||||
lock_page(page);
|
||||
|
||||
if (unlikely(page->mapping != inode->i_mapping)) {
|
||||
unlock_page(page);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!page_has_buffers(page)) {
|
||||
unlock_page(page);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (page_has_buffers(page)) {
|
||||
lastoff = page_offset(page);
|
||||
bh = head = page_buffers(page);
|
||||
do {
|
||||
if (buffer_uptodate(bh) ||
|
||||
buffer_unwritten(bh)) {
|
||||
if (origin == SEEK_DATA)
|
||||
found = 1;
|
||||
} else {
|
||||
if (origin == SEEK_HOLE)
|
||||
found = 1;
|
||||
}
|
||||
if (found) {
|
||||
*offset = max_t(loff_t,
|
||||
startoff, lastoff);
|
||||
unlock_page(page);
|
||||
goto out;
|
||||
}
|
||||
lastoff += bh->b_size;
|
||||
bh = bh->b_this_page;
|
||||
} while (bh != head);
|
||||
}
|
||||
|
||||
lastoff = page_offset(page) + PAGE_SIZE;
|
||||
unlock_page(page);
|
||||
}
|
||||
|
||||
/*
|
||||
* The no. of pages is less than our desired, that would be a
|
||||
* hole in there.
|
||||
*/
|
||||
if (nr_pages < num && origin == SEEK_HOLE) {
|
||||
found = 1;
|
||||
*offset = lastoff;
|
||||
break;
|
||||
}
|
||||
|
||||
index = pvec.pages[i - 1]->index + 1;
|
||||
pagevec_release(&pvec);
|
||||
} while (index <= end);
|
||||
|
||||
out:
|
||||
pagevec_release(&pvec);
|
||||
return found;
|
||||
}
|
||||
|
||||
/*
|
||||
* ext4_seek_data() retrieves the offset for SEEK_DATA.
|
||||
*/
|
||||
static loff_t ext4_seek_data(struct file *file, loff_t offset, loff_t maxsize)
|
||||
{
|
||||
struct inode *inode = file->f_mapping->host;
|
||||
struct ext4_map_blocks map;
|
||||
struct extent_status es;
|
||||
ext4_lblk_t start, last, end;
|
||||
loff_t dataoff, isize;
|
||||
int blkbits;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
|
||||
isize = i_size_read(inode);
|
||||
if (offset >= isize) {
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
blkbits = inode->i_sb->s_blocksize_bits;
|
||||
start = offset >> blkbits;
|
||||
last = start;
|
||||
end = isize >> blkbits;
|
||||
dataoff = offset;
|
||||
|
||||
do {
|
||||
map.m_lblk = last;
|
||||
map.m_len = end - last + 1;
|
||||
ret = ext4_map_blocks(NULL, inode, &map, 0);
|
||||
if (ret > 0 && !(map.m_flags & EXT4_MAP_UNWRITTEN)) {
|
||||
if (last != start)
|
||||
dataoff = last << blkbits;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is a delay extent at this offset,
|
||||
* it will be as a data.
|
||||
*/
|
||||
es.start = last;
|
||||
(void)ext4_es_find_extent(inode, &es);
|
||||
if (last >= es.start &&
|
||||
last < es.start + es.len) {
|
||||
if (last != start)
|
||||
dataoff = last << blkbits;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is a unwritten extent at this offset,
|
||||
* it will be as a data or a hole according to page
|
||||
* cache that has data or not.
|
||||
*/
|
||||
if (map.m_flags & EXT4_MAP_UNWRITTEN) {
|
||||
int unwritten;
|
||||
unwritten = ext4_find_unwritten_pgoff(inode, SEEK_DATA,
|
||||
&map, &dataoff);
|
||||
if (unwritten)
|
||||
break;
|
||||
}
|
||||
|
||||
last++;
|
||||
dataoff = last << blkbits;
|
||||
} while (last <= end);
|
||||
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
|
||||
if (dataoff > isize)
|
||||
return -ENXIO;
|
||||
|
||||
if (dataoff < 0 && !(file->f_mode & FMODE_UNSIGNED_OFFSET))
|
||||
return -EINVAL;
|
||||
if (dataoff > maxsize)
|
||||
return -EINVAL;
|
||||
|
||||
if (dataoff != file->f_pos) {
|
||||
file->f_pos = dataoff;
|
||||
file->f_version = 0;
|
||||
}
|
||||
|
||||
return dataoff;
|
||||
}
|
||||
|
||||
/*
|
||||
* ext4_seek_hole() retrieves the offset for SEEK_HOLE.
|
||||
*/
|
||||
static loff_t ext4_seek_hole(struct file *file, loff_t offset, loff_t maxsize)
|
||||
{
|
||||
struct inode *inode = file->f_mapping->host;
|
||||
struct ext4_map_blocks map;
|
||||
struct extent_status es;
|
||||
ext4_lblk_t start, last, end;
|
||||
loff_t holeoff, isize;
|
||||
int blkbits;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
|
||||
isize = i_size_read(inode);
|
||||
if (offset >= isize) {
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
blkbits = inode->i_sb->s_blocksize_bits;
|
||||
start = offset >> blkbits;
|
||||
last = start;
|
||||
end = isize >> blkbits;
|
||||
holeoff = offset;
|
||||
|
||||
do {
|
||||
map.m_lblk = last;
|
||||
map.m_len = end - last + 1;
|
||||
ret = ext4_map_blocks(NULL, inode, &map, 0);
|
||||
if (ret > 0 && !(map.m_flags & EXT4_MAP_UNWRITTEN)) {
|
||||
last += ret;
|
||||
holeoff = last << blkbits;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is a delay extent at this offset,
|
||||
* we will skip this extent.
|
||||
*/
|
||||
es.start = last;
|
||||
(void)ext4_es_find_extent(inode, &es);
|
||||
if (last >= es.start &&
|
||||
last < es.start + es.len) {
|
||||
last = es.start + es.len;
|
||||
holeoff = last << blkbits;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is a unwritten extent at this offset,
|
||||
* it will be as a data or a hole according to page
|
||||
* cache that has data or not.
|
||||
*/
|
||||
if (map.m_flags & EXT4_MAP_UNWRITTEN) {
|
||||
int unwritten;
|
||||
unwritten = ext4_find_unwritten_pgoff(inode, SEEK_HOLE,
|
||||
&map, &holeoff);
|
||||
if (!unwritten) {
|
||||
last += ret;
|
||||
holeoff = last << blkbits;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* find a hole */
|
||||
break;
|
||||
} while (last <= end);
|
||||
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
|
||||
if (holeoff > isize)
|
||||
holeoff = isize;
|
||||
|
||||
if (holeoff < 0 && !(file->f_mode & FMODE_UNSIGNED_OFFSET))
|
||||
return -EINVAL;
|
||||
if (holeoff > maxsize)
|
||||
return -EINVAL;
|
||||
|
||||
if (holeoff != file->f_pos) {
|
||||
file->f_pos = holeoff;
|
||||
file->f_version = 0;
|
||||
}
|
||||
|
||||
return holeoff;
|
||||
}
|
||||
|
||||
/*
|
||||
* ext4_llseek() handles both block-mapped and extent-mapped maxbytes values
|
||||
* by calling generic_file_llseek_size() with the appropriate maxbytes
|
||||
@@ -300,8 +619,19 @@ loff_t ext4_llseek(struct file *file, loff_t offset, int origin)
|
||||
else
|
||||
maxbytes = inode->i_sb->s_maxbytes;
|
||||
|
||||
return generic_file_llseek_size(file, offset, origin,
|
||||
maxbytes, i_size_read(inode));
|
||||
switch (origin) {
|
||||
case SEEK_SET:
|
||||
case SEEK_CUR:
|
||||
case SEEK_END:
|
||||
return generic_file_llseek_size(file, offset, origin,
|
||||
maxbytes, i_size_read(inode));
|
||||
case SEEK_DATA:
|
||||
return ext4_seek_data(file, offset, maxbytes);
|
||||
case SEEK_HOLE:
|
||||
return ext4_seek_hole(file, offset, maxbytes);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
const struct file_operations ext4_file_operations = {
|
||||
@@ -326,12 +656,10 @@ const struct file_operations ext4_file_operations = {
|
||||
const struct inode_operations ext4_file_inode_operations = {
|
||||
.setattr = ext4_setattr,
|
||||
.getattr = ext4_getattr,
|
||||
#ifdef CONFIG_EXT4_FS_XATTR
|
||||
.setxattr = generic_setxattr,
|
||||
.getxattr = generic_getxattr,
|
||||
.listxattr = ext4_listxattr,
|
||||
.removexattr = generic_removexattr,
|
||||
#endif
|
||||
.get_acl = ext4_get_acl,
|
||||
.fiemap = ext4_fiemap,
|
||||
};
|
||||
|
||||
@@ -44,7 +44,6 @@
|
||||
*/
|
||||
static int ext4_sync_parent(struct inode *inode)
|
||||
{
|
||||
struct writeback_control wbc;
|
||||
struct dentry *dentry = NULL;
|
||||
struct inode *next;
|
||||
int ret = 0;
|
||||
@@ -66,10 +65,7 @@ static int ext4_sync_parent(struct inode *inode)
|
||||
ret = sync_mapping_buffers(inode->i_mapping);
|
||||
if (ret)
|
||||
break;
|
||||
memset(&wbc, 0, sizeof(wbc));
|
||||
wbc.sync_mode = WB_SYNC_ALL;
|
||||
wbc.nr_to_write = 0; /* only write out the inode */
|
||||
ret = sync_inode(inode, &wbc);
|
||||
ret = sync_inode_metadata(inode, 1);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -762,7 +762,6 @@ got:
|
||||
|
||||
BUFFER_TRACE(block_bitmap_bh, "dirty block bitmap");
|
||||
err = ext4_handle_dirty_metadata(handle, NULL, block_bitmap_bh);
|
||||
brelse(block_bitmap_bh);
|
||||
|
||||
/* recheck and clear flag under lock if we still need to */
|
||||
ext4_lock_group(sb, group);
|
||||
@@ -775,6 +774,7 @@ got:
|
||||
ext4_group_desc_csum_set(sb, group, gdp);
|
||||
}
|
||||
ext4_unlock_group(sb, group);
|
||||
brelse(block_bitmap_bh);
|
||||
|
||||
if (err)
|
||||
goto fail;
|
||||
@@ -902,6 +902,10 @@ got:
|
||||
|
||||
ei->i_extra_isize = EXT4_SB(sb)->s_want_extra_isize;
|
||||
|
||||
ei->i_inline_off = 0;
|
||||
if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_INLINE_DATA))
|
||||
ext4_set_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
|
||||
|
||||
ret = inode;
|
||||
dquot_initialize(inode);
|
||||
err = dquot_alloc_inode(inode);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "ext4_jbd2.h"
|
||||
#include "truncate.h"
|
||||
#include "ext4_extents.h" /* Needed for EXT_MAX_BLOCKS */
|
||||
|
||||
#include <trace/events/ext4.h>
|
||||
|
||||
@@ -755,8 +756,7 @@ cleanup:
|
||||
partial--;
|
||||
}
|
||||
out:
|
||||
trace_ext4_ind_map_blocks_exit(inode, map->m_lblk,
|
||||
map->m_pblk, map->m_len, err);
|
||||
trace_ext4_ind_map_blocks_exit(inode, map, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1412,6 +1412,7 @@ void ext4_ind_truncate(struct inode *inode)
|
||||
down_write(&ei->i_data_sem);
|
||||
|
||||
ext4_discard_preallocations(inode);
|
||||
ext4_es_remove_extent(inode, last_block, EXT_MAX_BLOCKS - last_block);
|
||||
|
||||
/*
|
||||
* The orphan list entry will now protect us from any crash which
|
||||
|
||||
1884
fs/ext4/inline.c
Normal file
1884
fs/ext4/inline.c
Normal file
File diff suppressed because it is too large
Load Diff
639
fs/ext4/inode.c
639
fs/ext4/inode.c
File diff suppressed because it is too large
Load Diff
@@ -1373,7 +1373,7 @@ static int mb_find_extent(struct ext4_buddy *e4b, int block,
|
||||
ex->fe_start += next;
|
||||
|
||||
while (needed > ex->fe_len &&
|
||||
(buddy = mb_find_buddy(e4b, order, &max))) {
|
||||
mb_find_buddy(e4b, order, &max)) {
|
||||
|
||||
if (block + 1 >= max)
|
||||
break;
|
||||
@@ -2607,9 +2607,17 @@ static void ext4_free_data_callback(struct super_block *sb,
|
||||
mb_debug(1, "gonna free %u blocks in group %u (0x%p):",
|
||||
entry->efd_count, entry->efd_group, entry);
|
||||
|
||||
if (test_opt(sb, DISCARD))
|
||||
ext4_issue_discard(sb, entry->efd_group,
|
||||
entry->efd_start_cluster, entry->efd_count);
|
||||
if (test_opt(sb, DISCARD)) {
|
||||
err = ext4_issue_discard(sb, entry->efd_group,
|
||||
entry->efd_start_cluster,
|
||||
entry->efd_count);
|
||||
if (err && err != -EOPNOTSUPP)
|
||||
ext4_msg(sb, KERN_WARNING, "discard request in"
|
||||
" group:%d block:%d count:%d failed"
|
||||
" with %d", entry->efd_group,
|
||||
entry->efd_start_cluster,
|
||||
entry->efd_count, err);
|
||||
}
|
||||
|
||||
err = ext4_mb_load_buddy(sb, entry->efd_group, &e4b);
|
||||
/* we expect to find existing buddy because it's pinned */
|
||||
@@ -4310,8 +4318,10 @@ ext4_fsblk_t ext4_mb_new_blocks(handle_t *handle,
|
||||
repeat:
|
||||
/* allocate space in core */
|
||||
*errp = ext4_mb_regular_allocator(ac);
|
||||
if (*errp)
|
||||
if (*errp) {
|
||||
ext4_discard_allocated_blocks(ac);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* as we've just preallocated more space than
|
||||
* user requested orinally, we store allocated
|
||||
@@ -4333,10 +4343,10 @@ repeat:
|
||||
ac->ac_b_ex.fe_len = 0;
|
||||
ac->ac_status = AC_STATUS_CONTINUE;
|
||||
goto repeat;
|
||||
} else if (*errp)
|
||||
errout:
|
||||
} else if (*errp) {
|
||||
ext4_discard_allocated_blocks(ac);
|
||||
else {
|
||||
goto errout;
|
||||
} else {
|
||||
block = ext4_grp_offs_to_block(sb, &ac->ac_b_ex);
|
||||
ar->len = ac->ac_b_ex.fe_len;
|
||||
}
|
||||
@@ -4347,6 +4357,7 @@ repeat:
|
||||
*errp = -ENOSPC;
|
||||
}
|
||||
|
||||
errout:
|
||||
if (*errp) {
|
||||
ac->ac_b_ex.fe_len = 0;
|
||||
ar->len = 0;
|
||||
@@ -4656,8 +4667,16 @@ do_more:
|
||||
* with group lock held. generate_buddy look at
|
||||
* them with group lock_held
|
||||
*/
|
||||
if (test_opt(sb, DISCARD))
|
||||
ext4_issue_discard(sb, block_group, bit, count);
|
||||
if (test_opt(sb, DISCARD)) {
|
||||
err = ext4_issue_discard(sb, block_group, bit, count);
|
||||
if (err && err != -EOPNOTSUPP)
|
||||
ext4_msg(sb, KERN_WARNING, "discard request in"
|
||||
" group:%d block:%d count:%lu failed"
|
||||
" with %d", block_group, bit, count,
|
||||
err);
|
||||
}
|
||||
|
||||
|
||||
ext4_lock_group(sb, block_group);
|
||||
mb_clear_bits(bitmap_bh->b_data, bit, count_clusters);
|
||||
mb_free_blocks(inode, &e4b, bit, count_clusters);
|
||||
@@ -4851,10 +4870,11 @@ error_return:
|
||||
* one will allocate those blocks, mark it as used in buddy bitmap. This must
|
||||
* be called with under the group lock.
|
||||
*/
|
||||
static void ext4_trim_extent(struct super_block *sb, int start, int count,
|
||||
static int ext4_trim_extent(struct super_block *sb, int start, int count,
|
||||
ext4_group_t group, struct ext4_buddy *e4b)
|
||||
{
|
||||
struct ext4_free_extent ex;
|
||||
int ret = 0;
|
||||
|
||||
trace_ext4_trim_extent(sb, group, start, count);
|
||||
|
||||
@@ -4870,9 +4890,10 @@ static void ext4_trim_extent(struct super_block *sb, int start, int count,
|
||||
*/
|
||||
mb_mark_used(e4b, &ex);
|
||||
ext4_unlock_group(sb, group);
|
||||
ext4_issue_discard(sb, group, start, count);
|
||||
ret = ext4_issue_discard(sb, group, start, count);
|
||||
ext4_lock_group(sb, group);
|
||||
mb_free_blocks(NULL, e4b, start, ex.fe_len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4901,7 +4922,7 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group,
|
||||
void *bitmap;
|
||||
ext4_grpblk_t next, count = 0, free_count = 0;
|
||||
struct ext4_buddy e4b;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
trace_ext4_trim_all_free(sb, group, start, max);
|
||||
|
||||
@@ -4928,8 +4949,11 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group,
|
||||
next = mb_find_next_bit(bitmap, max + 1, start);
|
||||
|
||||
if ((next - start) >= minblocks) {
|
||||
ext4_trim_extent(sb, start,
|
||||
next - start, group, &e4b);
|
||||
ret = ext4_trim_extent(sb, start,
|
||||
next - start, group, &e4b);
|
||||
if (ret && ret != -EOPNOTSUPP)
|
||||
break;
|
||||
ret = 0;
|
||||
count += next - start;
|
||||
}
|
||||
free_count += next - start;
|
||||
@@ -4950,8 +4974,10 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group,
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
if (!ret) {
|
||||
ret = count;
|
||||
EXT4_MB_GRP_SET_TRIMMED(e4b.bd_info);
|
||||
}
|
||||
out:
|
||||
ext4_unlock_group(sb, group);
|
||||
ext4_mb_unload_buddy(&e4b);
|
||||
@@ -4959,7 +4985,7 @@ out:
|
||||
ext4_debug("trimmed %d blocks in the group %d\n",
|
||||
count, group);
|
||||
|
||||
return count;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include "ext4_jbd2.h"
|
||||
#include "ext4_extents.h"
|
||||
|
||||
/*
|
||||
* The contiguous blocks details which can be
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user