Scalability improvements when allocating inodes, and some
miscellaneous bug fixes and cleanups. -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEK2m5VNv+CHkogTfJ8vlZVpUNgaMFAlms4KwACgkQ8vlZVpUN gaMsNwf9EDIaB7uMAFIRw8TzszbYKE3K3T412s18zce+kYea6wZPkQavWH/qMYgU r8jmXfDi3KZJJBI8fFW4qh36qs/fJoOeQOD3BTHcczEJqaaOiahaxfSylTwezDfw fIkEMCfBj6Vyldo0aKrtM4iU07Njj7QmYBtsiJo1kpyAuZ7wuoaiyizCLRb0fhLB AnBOs2ur9fQvn954M03tJIKpxFgmbpofM7yMtJYpW9dHCCWe2G+sIdBy/W6vVTxt sJIzUGyyEFs9Hr8U8THbuo5XgScFh+NEOkj/cf60t5ZYxwZzJvY7+Oj7BQwxEM1p Efcjz2kRLU8qSYHOHxCar0D3MW+oNQ== =E7gL -----END PGP SIGNATURE----- Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4 Pull ext4 updates from Ted Ts'o: "Scalability improvements when allocating inodes, and some miscellaneous bug fixes and cleanups" * tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: ext4: avoid Y2038 overflow in recently_deleted() ext4: fix fault handling when mounted with -o dax,ro ext4: fix quota inconsistency during orphan cleanup for read-only mounts ext4: fix incorrect quotaoff if the quota feature is enabled ext4: remove useless test and assignment in strtohash functions ext4: backward compatibility support for Lustre ea_inode implementation ext4: remove timebomb in ext4_decode_extra_time() ext4: use sizeof(*ptr) ext4: in ext4_seek_{hole,data}, return -ENXIO for negative offsets ext4: reduce lock contention in __ext4_new_inode ext4: cleanup goto next group ext4: do not unnecessarily allocate buffer in recently_deleted()
This commit is contained in:
commit
be6297e9be
@ -411,7 +411,7 @@ static struct dir_private_info *ext4_htree_create_dir_info(struct file *filp,
|
|||||||
{
|
{
|
||||||
struct dir_private_info *p;
|
struct dir_private_info *p;
|
||||||
|
|
||||||
p = kzalloc(sizeof(struct dir_private_info), GFP_KERNEL);
|
p = kzalloc(sizeof(*p), GFP_KERNEL);
|
||||||
if (!p)
|
if (!p)
|
||||||
return NULL;
|
return NULL;
|
||||||
p->curr_hash = pos2maj_hash(filp, pos);
|
p->curr_hash = pos2maj_hash(filp, pos);
|
||||||
|
@ -838,13 +838,11 @@ static inline void ext4_decode_extra_time(struct timespec *time, __le32 extra)
|
|||||||
{
|
{
|
||||||
if (unlikely(sizeof(time->tv_sec) > 4 &&
|
if (unlikely(sizeof(time->tv_sec) > 4 &&
|
||||||
(extra & cpu_to_le32(EXT4_EPOCH_MASK)))) {
|
(extra & cpu_to_le32(EXT4_EPOCH_MASK)))) {
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,20,0)
|
|
||||||
|
#if 1
|
||||||
/* Handle legacy encoding of pre-1970 dates with epoch
|
/* Handle legacy encoding of pre-1970 dates with epoch
|
||||||
* bits 1,1. We assume that by kernel version 4.20,
|
* bits 1,1. (This backwards compatibility may be removed
|
||||||
* everyone will have run fsck over the affected
|
* at the discretion of the ext4 developers.)
|
||||||
* filesystems to correct the problem. (This
|
|
||||||
* backwards compatibility may be removed before this
|
|
||||||
* time, at the discretion of the ext4 developers.)
|
|
||||||
*/
|
*/
|
||||||
u64 extra_bits = le32_to_cpu(extra) & EXT4_EPOCH_MASK;
|
u64 extra_bits = le32_to_cpu(extra) & EXT4_EPOCH_MASK;
|
||||||
if (extra_bits == 3 && ((time->tv_sec) & 0x80000000) != 0)
|
if (extra_bits == 3 && ((time->tv_sec) & 0x80000000) != 0)
|
||||||
@ -1567,6 +1565,7 @@ enum {
|
|||||||
nolocking */
|
nolocking */
|
||||||
EXT4_STATE_MAY_INLINE_DATA, /* may have in-inode data */
|
EXT4_STATE_MAY_INLINE_DATA, /* may have in-inode data */
|
||||||
EXT4_STATE_EXT_PRECACHED, /* extents have been precached */
|
EXT4_STATE_EXT_PRECACHED, /* extents have been precached */
|
||||||
|
EXT4_STATE_LUSTRE_EA_INODE, /* Lustre-style ea_inode */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define EXT4_INODE_BIT_FNS(name, field, offset) \
|
#define EXT4_INODE_BIT_FNS(name, field, offset) \
|
||||||
|
@ -279,7 +279,20 @@ static int ext4_dax_huge_fault(struct vm_fault *vmf,
|
|||||||
handle_t *handle = NULL;
|
handle_t *handle = NULL;
|
||||||
struct inode *inode = file_inode(vmf->vma->vm_file);
|
struct inode *inode = file_inode(vmf->vma->vm_file);
|
||||||
struct super_block *sb = inode->i_sb;
|
struct super_block *sb = inode->i_sb;
|
||||||
bool write = vmf->flags & FAULT_FLAG_WRITE;
|
|
||||||
|
/*
|
||||||
|
* We have to distinguish real writes from writes which will result in a
|
||||||
|
* COW page; COW writes should *not* poke the journal (the file will not
|
||||||
|
* be changed). Doing so would cause unintended failures when mounted
|
||||||
|
* read-only.
|
||||||
|
*
|
||||||
|
* We check for VM_SHARED rather than vmf->cow_page since the latter is
|
||||||
|
* unset for pe_size != PE_SIZE_PTE (i.e. only in do_cow_fault); for
|
||||||
|
* other sizes, dax_iomap_fault will handle splitting / fallback so that
|
||||||
|
* we eventually come back with a COW page.
|
||||||
|
*/
|
||||||
|
bool write = (vmf->flags & FAULT_FLAG_WRITE) &&
|
||||||
|
(vmf->vma->vm_flags & VM_SHARED);
|
||||||
|
|
||||||
if (write) {
|
if (write) {
|
||||||
sb_start_pagefault(sb);
|
sb_start_pagefault(sb);
|
||||||
@ -595,7 +608,7 @@ static loff_t ext4_seek_data(struct file *file, loff_t offset, loff_t maxsize)
|
|||||||
inode_lock(inode);
|
inode_lock(inode);
|
||||||
|
|
||||||
isize = i_size_read(inode);
|
isize = i_size_read(inode);
|
||||||
if (offset >= isize) {
|
if (offset < 0 || offset >= isize) {
|
||||||
inode_unlock(inode);
|
inode_unlock(inode);
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
@ -658,7 +671,7 @@ static loff_t ext4_seek_hole(struct file *file, loff_t offset, loff_t maxsize)
|
|||||||
inode_lock(inode);
|
inode_lock(inode);
|
||||||
|
|
||||||
isize = i_size_read(inode);
|
isize = i_size_read(inode);
|
||||||
if (offset >= isize) {
|
if (offset < 0 || offset >= isize) {
|
||||||
inode_unlock(inode);
|
inode_unlock(inode);
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
|
@ -148,8 +148,6 @@ static void str2hashbuf_signed(const char *msg, int len, __u32 *buf, int num)
|
|||||||
if (len > num*4)
|
if (len > num*4)
|
||||||
len = num * 4;
|
len = num * 4;
|
||||||
for (i = 0; i < len; i++) {
|
for (i = 0; i < len; i++) {
|
||||||
if ((i % 4) == 0)
|
|
||||||
val = pad;
|
|
||||||
val = ((int) scp[i]) + (val << 8);
|
val = ((int) scp[i]) + (val << 8);
|
||||||
if ((i % 4) == 3) {
|
if ((i % 4) == 3) {
|
||||||
*buf++ = val;
|
*buf++ = val;
|
||||||
@ -176,8 +174,6 @@ static void str2hashbuf_unsigned(const char *msg, int len, __u32 *buf, int num)
|
|||||||
if (len > num*4)
|
if (len > num*4)
|
||||||
len = num * 4;
|
len = num * 4;
|
||||||
for (i = 0; i < len; i++) {
|
for (i = 0; i < len; i++) {
|
||||||
if ((i % 4) == 0)
|
|
||||||
val = pad;
|
|
||||||
val = ((int) ucp[i]) + (val << 8);
|
val = ((int) ucp[i]) + (val << 8);
|
||||||
if ((i % 4) == 3) {
|
if ((i % 4) == 3) {
|
||||||
*buf++ = val;
|
*buf++ = val;
|
||||||
|
@ -692,24 +692,25 @@ static int find_group_other(struct super_block *sb, struct inode *parent,
|
|||||||
* somewhat arbitrary...)
|
* somewhat arbitrary...)
|
||||||
*/
|
*/
|
||||||
#define RECENTCY_MIN 5
|
#define RECENTCY_MIN 5
|
||||||
#define RECENTCY_DIRTY 30
|
#define RECENTCY_DIRTY 300
|
||||||
|
|
||||||
static int recently_deleted(struct super_block *sb, ext4_group_t group, int ino)
|
static int recently_deleted(struct super_block *sb, ext4_group_t group, int ino)
|
||||||
{
|
{
|
||||||
struct ext4_group_desc *gdp;
|
struct ext4_group_desc *gdp;
|
||||||
struct ext4_inode *raw_inode;
|
struct ext4_inode *raw_inode;
|
||||||
struct buffer_head *bh;
|
struct buffer_head *bh;
|
||||||
unsigned long dtime, now;
|
int inodes_per_block = EXT4_SB(sb)->s_inodes_per_block;
|
||||||
int inodes_per_block = EXT4_SB(sb)->s_inodes_per_block;
|
int offset, ret = 0;
|
||||||
int offset, ret = 0, recentcy = RECENTCY_MIN;
|
int recentcy = RECENTCY_MIN;
|
||||||
|
u32 dtime, now;
|
||||||
|
|
||||||
gdp = ext4_get_group_desc(sb, group, NULL);
|
gdp = ext4_get_group_desc(sb, group, NULL);
|
||||||
if (unlikely(!gdp))
|
if (unlikely(!gdp))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
bh = sb_getblk(sb, ext4_inode_table(sb, gdp) +
|
bh = sb_find_get_block(sb, ext4_inode_table(sb, gdp) +
|
||||||
(ino / inodes_per_block));
|
(ino / inodes_per_block));
|
||||||
if (unlikely(!bh) || !buffer_uptodate(bh))
|
if (!bh || !buffer_uptodate(bh))
|
||||||
/*
|
/*
|
||||||
* If the block is not in the buffer cache, then it
|
* If the block is not in the buffer cache, then it
|
||||||
* must have been written out.
|
* must have been written out.
|
||||||
@ -718,18 +719,45 @@ static int recently_deleted(struct super_block *sb, ext4_group_t group, int ino)
|
|||||||
|
|
||||||
offset = (ino % inodes_per_block) * EXT4_INODE_SIZE(sb);
|
offset = (ino % inodes_per_block) * EXT4_INODE_SIZE(sb);
|
||||||
raw_inode = (struct ext4_inode *) (bh->b_data + offset);
|
raw_inode = (struct ext4_inode *) (bh->b_data + offset);
|
||||||
|
|
||||||
|
/* i_dtime is only 32 bits on disk, but we only care about relative
|
||||||
|
* times in the range of a few minutes (i.e. long enough to sync a
|
||||||
|
* recently-deleted inode to disk), so using the low 32 bits of the
|
||||||
|
* clock (a 68 year range) is enough, see time_before32() */
|
||||||
dtime = le32_to_cpu(raw_inode->i_dtime);
|
dtime = le32_to_cpu(raw_inode->i_dtime);
|
||||||
now = get_seconds();
|
now = ktime_get_real_seconds();
|
||||||
if (buffer_dirty(bh))
|
if (buffer_dirty(bh))
|
||||||
recentcy += RECENTCY_DIRTY;
|
recentcy += RECENTCY_DIRTY;
|
||||||
|
|
||||||
if (dtime && (dtime < now) && (now < dtime + recentcy))
|
if (dtime && time_before32(dtime, now) &&
|
||||||
|
time_before32(now, dtime + recentcy))
|
||||||
ret = 1;
|
ret = 1;
|
||||||
out:
|
out:
|
||||||
brelse(bh);
|
brelse(bh);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int find_inode_bit(struct super_block *sb, ext4_group_t group,
|
||||||
|
struct buffer_head *bitmap, unsigned long *ino)
|
||||||
|
{
|
||||||
|
next:
|
||||||
|
*ino = ext4_find_next_zero_bit((unsigned long *)
|
||||||
|
bitmap->b_data,
|
||||||
|
EXT4_INODES_PER_GROUP(sb), *ino);
|
||||||
|
if (*ino >= EXT4_INODES_PER_GROUP(sb))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if ((EXT4_SB(sb)->s_journal == NULL) &&
|
||||||
|
recently_deleted(sb, group, *ino)) {
|
||||||
|
*ino = *ino + 1;
|
||||||
|
if (*ino < EXT4_INODES_PER_GROUP(sb))
|
||||||
|
goto next;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* There are two policies for allocating an inode. If the new inode is
|
* There are two policies for allocating an inode. If the new inode is
|
||||||
* a directory, then a forward search is made for a block group with both
|
* a directory, then a forward search is made for a block group with both
|
||||||
@ -892,19 +920,13 @@ got_group:
|
|||||||
/*
|
/*
|
||||||
* Check free inodes count before loading bitmap.
|
* Check free inodes count before loading bitmap.
|
||||||
*/
|
*/
|
||||||
if (ext4_free_inodes_count(sb, gdp) == 0) {
|
if (ext4_free_inodes_count(sb, gdp) == 0)
|
||||||
if (++group == ngroups)
|
goto next_group;
|
||||||
group = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
grp = ext4_get_group_info(sb, group);
|
grp = ext4_get_group_info(sb, group);
|
||||||
/* Skip groups with already-known suspicious inode tables */
|
/* Skip groups with already-known suspicious inode tables */
|
||||||
if (EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) {
|
if (EXT4_MB_GRP_IBITMAP_CORRUPT(grp))
|
||||||
if (++group == ngroups)
|
goto next_group;
|
||||||
group = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
brelse(inode_bitmap_bh);
|
brelse(inode_bitmap_bh);
|
||||||
inode_bitmap_bh = ext4_read_inode_bitmap(sb, group);
|
inode_bitmap_bh = ext4_read_inode_bitmap(sb, group);
|
||||||
@ -912,27 +934,20 @@ got_group:
|
|||||||
if (EXT4_MB_GRP_IBITMAP_CORRUPT(grp) ||
|
if (EXT4_MB_GRP_IBITMAP_CORRUPT(grp) ||
|
||||||
IS_ERR(inode_bitmap_bh)) {
|
IS_ERR(inode_bitmap_bh)) {
|
||||||
inode_bitmap_bh = NULL;
|
inode_bitmap_bh = NULL;
|
||||||
if (++group == ngroups)
|
goto next_group;
|
||||||
group = 0;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
repeat_in_this_group:
|
repeat_in_this_group:
|
||||||
ino = ext4_find_next_zero_bit((unsigned long *)
|
ret2 = find_inode_bit(sb, group, inode_bitmap_bh, &ino);
|
||||||
inode_bitmap_bh->b_data,
|
if (!ret2)
|
||||||
EXT4_INODES_PER_GROUP(sb), ino);
|
|
||||||
if (ino >= EXT4_INODES_PER_GROUP(sb))
|
|
||||||
goto next_group;
|
goto next_group;
|
||||||
if (group == 0 && (ino+1) < EXT4_FIRST_INO(sb)) {
|
|
||||||
|
if (group == 0 && (ino + 1) < EXT4_FIRST_INO(sb)) {
|
||||||
ext4_error(sb, "reserved inode found cleared - "
|
ext4_error(sb, "reserved inode found cleared - "
|
||||||
"inode=%lu", ino + 1);
|
"inode=%lu", ino + 1);
|
||||||
continue;
|
goto next_group;
|
||||||
}
|
|
||||||
if ((EXT4_SB(sb)->s_journal == NULL) &&
|
|
||||||
recently_deleted(sb, group, ino)) {
|
|
||||||
ino++;
|
|
||||||
goto next_inode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!handle) {
|
if (!handle) {
|
||||||
BUG_ON(nblocks <= 0);
|
BUG_ON(nblocks <= 0);
|
||||||
handle = __ext4_journal_start_sb(dir->i_sb, line_no,
|
handle = __ext4_journal_start_sb(dir->i_sb, line_no,
|
||||||
@ -952,11 +967,23 @@ repeat_in_this_group:
|
|||||||
}
|
}
|
||||||
ext4_lock_group(sb, group);
|
ext4_lock_group(sb, group);
|
||||||
ret2 = ext4_test_and_set_bit(ino, inode_bitmap_bh->b_data);
|
ret2 = ext4_test_and_set_bit(ino, inode_bitmap_bh->b_data);
|
||||||
|
if (ret2) {
|
||||||
|
/* Someone already took the bit. Repeat the search
|
||||||
|
* with lock held.
|
||||||
|
*/
|
||||||
|
ret2 = find_inode_bit(sb, group, inode_bitmap_bh, &ino);
|
||||||
|
if (ret2) {
|
||||||
|
ext4_set_bit(ino, inode_bitmap_bh->b_data);
|
||||||
|
ret2 = 0;
|
||||||
|
} else {
|
||||||
|
ret2 = 1; /* we didn't grab the inode */
|
||||||
|
}
|
||||||
|
}
|
||||||
ext4_unlock_group(sb, group);
|
ext4_unlock_group(sb, group);
|
||||||
ino++; /* the inode bitmap is zero-based */
|
ino++; /* the inode bitmap is zero-based */
|
||||||
if (!ret2)
|
if (!ret2)
|
||||||
goto got; /* we grabbed the inode! */
|
goto got; /* we grabbed the inode! */
|
||||||
next_inode:
|
|
||||||
if (ino < EXT4_INODES_PER_GROUP(sb))
|
if (ino < EXT4_INODES_PER_GROUP(sb))
|
||||||
goto repeat_in_this_group;
|
goto repeat_in_this_group;
|
||||||
next_group:
|
next_group:
|
||||||
|
@ -4897,14 +4897,6 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
|
|||||||
brelse(iloc.bh);
|
brelse(iloc.bh);
|
||||||
ext4_set_inode_flags(inode);
|
ext4_set_inode_flags(inode);
|
||||||
|
|
||||||
if (ei->i_flags & EXT4_EA_INODE_FL) {
|
|
||||||
ext4_xattr_inode_set_class(inode);
|
|
||||||
|
|
||||||
inode_lock(inode);
|
|
||||||
inode->i_flags |= S_NOQUOTA;
|
|
||||||
inode_unlock(inode);
|
|
||||||
}
|
|
||||||
|
|
||||||
unlock_new_inode(inode);
|
unlock_new_inode(inode);
|
||||||
return inode;
|
return inode;
|
||||||
|
|
||||||
|
@ -367,7 +367,7 @@ skip:
|
|||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
mmpd_data = kmalloc(sizeof(struct mmpd_data), GFP_KERNEL);
|
mmpd_data = kmalloc(sizeof(*mmpd_data), GFP_KERNEL);
|
||||||
if (!mmpd_data) {
|
if (!mmpd_data) {
|
||||||
ext4_warning(sb, "not enough memory for mmpd_data");
|
ext4_warning(sb, "not enough memory for mmpd_data");
|
||||||
goto failed;
|
goto failed;
|
||||||
|
@ -2404,6 +2404,7 @@ static void ext4_orphan_cleanup(struct super_block *sb,
|
|||||||
unsigned int s_flags = sb->s_flags;
|
unsigned int s_flags = sb->s_flags;
|
||||||
int ret, nr_orphans = 0, nr_truncates = 0;
|
int ret, nr_orphans = 0, nr_truncates = 0;
|
||||||
#ifdef CONFIG_QUOTA
|
#ifdef CONFIG_QUOTA
|
||||||
|
int quota_update = 0;
|
||||||
int i;
|
int i;
|
||||||
#endif
|
#endif
|
||||||
if (!es->s_last_orphan) {
|
if (!es->s_last_orphan) {
|
||||||
@ -2442,14 +2443,32 @@ static void ext4_orphan_cleanup(struct super_block *sb,
|
|||||||
#ifdef CONFIG_QUOTA
|
#ifdef CONFIG_QUOTA
|
||||||
/* Needed for iput() to work correctly and not trash data */
|
/* Needed for iput() to work correctly and not trash data */
|
||||||
sb->s_flags |= MS_ACTIVE;
|
sb->s_flags |= MS_ACTIVE;
|
||||||
/* Turn on quotas so that they are updated correctly */
|
|
||||||
|
/*
|
||||||
|
* Turn on quotas which were not enabled for read-only mounts if
|
||||||
|
* filesystem has quota feature, so that they are updated correctly.
|
||||||
|
*/
|
||||||
|
if (ext4_has_feature_quota(sb) && (s_flags & MS_RDONLY)) {
|
||||||
|
int ret = ext4_enable_quotas(sb);
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
quota_update = 1;
|
||||||
|
else
|
||||||
|
ext4_msg(sb, KERN_ERR,
|
||||||
|
"Cannot turn on quotas: error %d", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Turn on journaled quotas used for old sytle */
|
||||||
for (i = 0; i < EXT4_MAXQUOTAS; i++) {
|
for (i = 0; i < EXT4_MAXQUOTAS; i++) {
|
||||||
if (EXT4_SB(sb)->s_qf_names[i]) {
|
if (EXT4_SB(sb)->s_qf_names[i]) {
|
||||||
int ret = ext4_quota_on_mount(sb, i);
|
int ret = ext4_quota_on_mount(sb, i);
|
||||||
if (ret < 0)
|
|
||||||
|
if (!ret)
|
||||||
|
quota_update = 1;
|
||||||
|
else
|
||||||
ext4_msg(sb, KERN_ERR,
|
ext4_msg(sb, KERN_ERR,
|
||||||
"Cannot turn on journaled "
|
"Cannot turn on journaled "
|
||||||
"quota: error %d", ret);
|
"quota: type %d: error %d", i, ret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -2510,10 +2529,12 @@ static void ext4_orphan_cleanup(struct super_block *sb,
|
|||||||
ext4_msg(sb, KERN_INFO, "%d truncate%s cleaned up",
|
ext4_msg(sb, KERN_INFO, "%d truncate%s cleaned up",
|
||||||
PLURAL(nr_truncates));
|
PLURAL(nr_truncates));
|
||||||
#ifdef CONFIG_QUOTA
|
#ifdef CONFIG_QUOTA
|
||||||
/* Turn quotas off */
|
/* Turn off quotas if they were enabled for orphan cleanup */
|
||||||
for (i = 0; i < EXT4_MAXQUOTAS; i++) {
|
if (quota_update) {
|
||||||
if (sb_dqopt(sb)->files[i])
|
for (i = 0; i < EXT4_MAXQUOTAS; i++) {
|
||||||
dquot_quota_off(sb, i);
|
if (sb_dqopt(sb)->files[i])
|
||||||
|
dquot_quota_off(sb, i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
sb->s_flags = s_flags; /* Restore MS_RDONLY status */
|
sb->s_flags = s_flags; /* Restore MS_RDONLY status */
|
||||||
@ -5512,6 +5533,9 @@ static int ext4_enable_quotas(struct super_block *sb)
|
|||||||
DQUOT_USAGE_ENABLED |
|
DQUOT_USAGE_ENABLED |
|
||||||
(quota_mopt[type] ? DQUOT_LIMITS_ENABLED : 0));
|
(quota_mopt[type] ? DQUOT_LIMITS_ENABLED : 0));
|
||||||
if (err) {
|
if (err) {
|
||||||
|
for (type--; type >= 0; type--)
|
||||||
|
dquot_quota_off(sb, type);
|
||||||
|
|
||||||
ext4_warning(sb,
|
ext4_warning(sb,
|
||||||
"Failed to enable quota tracking "
|
"Failed to enable quota tracking "
|
||||||
"(type=%d, err=%d). Please run "
|
"(type=%d, err=%d). Please run "
|
||||||
|
141
fs/ext4/xattr.c
141
fs/ext4/xattr.c
@ -354,8 +354,10 @@ free_bhs:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define EXT4_XATTR_INODE_GET_PARENT(inode) ((__u32)(inode)->i_mtime.tv_sec)
|
||||||
|
|
||||||
static int ext4_xattr_inode_iget(struct inode *parent, unsigned long ea_ino,
|
static int ext4_xattr_inode_iget(struct inode *parent, unsigned long ea_ino,
|
||||||
struct inode **ea_inode)
|
u32 ea_inode_hash, struct inode **ea_inode)
|
||||||
{
|
{
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
int err;
|
int err;
|
||||||
@ -385,6 +387,24 @@ static int ext4_xattr_inode_iget(struct inode *parent, unsigned long ea_ino,
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ext4_xattr_inode_set_class(inode);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check whether this is an old Lustre-style xattr inode. Lustre
|
||||||
|
* implementation does not have hash validation, rather it has a
|
||||||
|
* backpointer from ea_inode to the parent inode.
|
||||||
|
*/
|
||||||
|
if (ea_inode_hash != ext4_xattr_inode_get_hash(inode) &&
|
||||||
|
EXT4_XATTR_INODE_GET_PARENT(inode) == parent->i_ino &&
|
||||||
|
inode->i_generation == parent->i_generation) {
|
||||||
|
ext4_set_inode_state(inode, EXT4_STATE_LUSTRE_EA_INODE);
|
||||||
|
ext4_xattr_inode_set_ref(inode, 1);
|
||||||
|
} else {
|
||||||
|
inode_lock(inode);
|
||||||
|
inode->i_flags |= S_NOQUOTA;
|
||||||
|
inode_unlock(inode);
|
||||||
|
}
|
||||||
|
|
||||||
*ea_inode = inode;
|
*ea_inode = inode;
|
||||||
return 0;
|
return 0;
|
||||||
error:
|
error:
|
||||||
@ -417,8 +437,6 @@ ext4_xattr_inode_verify_hashes(struct inode *ea_inode,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define EXT4_XATTR_INODE_GET_PARENT(inode) ((__u32)(inode)->i_mtime.tv_sec)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read xattr value from the EA inode.
|
* Read xattr value from the EA inode.
|
||||||
*/
|
*/
|
||||||
@ -431,7 +449,7 @@ ext4_xattr_inode_get(struct inode *inode, struct ext4_xattr_entry *entry,
|
|||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = ext4_xattr_inode_iget(inode, le32_to_cpu(entry->e_value_inum),
|
err = ext4_xattr_inode_iget(inode, le32_to_cpu(entry->e_value_inum),
|
||||||
&ea_inode);
|
le32_to_cpu(entry->e_hash), &ea_inode);
|
||||||
if (err) {
|
if (err) {
|
||||||
ea_inode = NULL;
|
ea_inode = NULL;
|
||||||
goto out;
|
goto out;
|
||||||
@ -449,29 +467,20 @@ ext4_xattr_inode_get(struct inode *inode, struct ext4_xattr_entry *entry,
|
|||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
err = ext4_xattr_inode_verify_hashes(ea_inode, entry, buffer, size);
|
if (!ext4_test_inode_state(ea_inode, EXT4_STATE_LUSTRE_EA_INODE)) {
|
||||||
/*
|
err = ext4_xattr_inode_verify_hashes(ea_inode, entry, buffer,
|
||||||
* Compatibility check for old Lustre ea_inode implementation. Old
|
size);
|
||||||
* version does not have hash validation, but it has a backpointer
|
if (err) {
|
||||||
* from ea_inode to the parent inode.
|
|
||||||
*/
|
|
||||||
if (err == -EFSCORRUPTED) {
|
|
||||||
if (EXT4_XATTR_INODE_GET_PARENT(ea_inode) != inode->i_ino ||
|
|
||||||
ea_inode->i_generation != inode->i_generation) {
|
|
||||||
ext4_warning_inode(ea_inode,
|
ext4_warning_inode(ea_inode,
|
||||||
"EA inode hash validation failed");
|
"EA inode hash validation failed");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
/* Do not add ea_inode to the cache. */
|
|
||||||
ea_inode_cache = NULL;
|
|
||||||
err = 0;
|
|
||||||
} else if (err)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (ea_inode_cache)
|
if (ea_inode_cache)
|
||||||
mb_cache_entry_create(ea_inode_cache, GFP_NOFS,
|
mb_cache_entry_create(ea_inode_cache, GFP_NOFS,
|
||||||
ext4_xattr_inode_get_hash(ea_inode),
|
ext4_xattr_inode_get_hash(ea_inode),
|
||||||
ea_inode->i_ino, true /* reusable */);
|
ea_inode->i_ino, true /* reusable */);
|
||||||
|
}
|
||||||
out:
|
out:
|
||||||
iput(ea_inode);
|
iput(ea_inode);
|
||||||
return err;
|
return err;
|
||||||
@ -838,10 +847,15 @@ static int ext4_xattr_inode_alloc_quota(struct inode *inode, size_t len)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ext4_xattr_inode_free_quota(struct inode *inode, size_t len)
|
static void ext4_xattr_inode_free_quota(struct inode *parent,
|
||||||
|
struct inode *ea_inode,
|
||||||
|
size_t len)
|
||||||
{
|
{
|
||||||
dquot_free_space_nodirty(inode, round_up_cluster(inode, len));
|
if (ea_inode &&
|
||||||
dquot_free_inode(inode);
|
ext4_test_inode_state(ea_inode, EXT4_STATE_LUSTRE_EA_INODE))
|
||||||
|
return;
|
||||||
|
dquot_free_space_nodirty(parent, round_up_cluster(parent, len));
|
||||||
|
dquot_free_inode(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
int __ext4_xattr_set_credits(struct super_block *sb, struct inode *inode,
|
int __ext4_xattr_set_credits(struct super_block *sb, struct inode *inode,
|
||||||
@ -1071,7 +1085,9 @@ static int ext4_xattr_inode_inc_ref_all(handle_t *handle, struct inode *parent,
|
|||||||
if (!entry->e_value_inum)
|
if (!entry->e_value_inum)
|
||||||
continue;
|
continue;
|
||||||
ea_ino = le32_to_cpu(entry->e_value_inum);
|
ea_ino = le32_to_cpu(entry->e_value_inum);
|
||||||
err = ext4_xattr_inode_iget(parent, ea_ino, &ea_inode);
|
err = ext4_xattr_inode_iget(parent, ea_ino,
|
||||||
|
le32_to_cpu(entry->e_hash),
|
||||||
|
&ea_inode);
|
||||||
if (err)
|
if (err)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
err = ext4_xattr_inode_inc_ref(handle, ea_inode);
|
err = ext4_xattr_inode_inc_ref(handle, ea_inode);
|
||||||
@ -1093,7 +1109,9 @@ cleanup:
|
|||||||
if (!entry->e_value_inum)
|
if (!entry->e_value_inum)
|
||||||
continue;
|
continue;
|
||||||
ea_ino = le32_to_cpu(entry->e_value_inum);
|
ea_ino = le32_to_cpu(entry->e_value_inum);
|
||||||
err = ext4_xattr_inode_iget(parent, ea_ino, &ea_inode);
|
err = ext4_xattr_inode_iget(parent, ea_ino,
|
||||||
|
le32_to_cpu(entry->e_hash),
|
||||||
|
&ea_inode);
|
||||||
if (err) {
|
if (err) {
|
||||||
ext4_warning(parent->i_sb,
|
ext4_warning(parent->i_sb,
|
||||||
"cleanup ea_ino %u iget error %d", ea_ino,
|
"cleanup ea_ino %u iget error %d", ea_ino,
|
||||||
@ -1131,7 +1149,9 @@ ext4_xattr_inode_dec_ref_all(handle_t *handle, struct inode *parent,
|
|||||||
if (!entry->e_value_inum)
|
if (!entry->e_value_inum)
|
||||||
continue;
|
continue;
|
||||||
ea_ino = le32_to_cpu(entry->e_value_inum);
|
ea_ino = le32_to_cpu(entry->e_value_inum);
|
||||||
err = ext4_xattr_inode_iget(parent, ea_ino, &ea_inode);
|
err = ext4_xattr_inode_iget(parent, ea_ino,
|
||||||
|
le32_to_cpu(entry->e_hash),
|
||||||
|
&ea_inode);
|
||||||
if (err)
|
if (err)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -1159,7 +1179,7 @@ ext4_xattr_inode_dec_ref_all(handle_t *handle, struct inode *parent,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!skip_quota)
|
if (!skip_quota)
|
||||||
ext4_xattr_inode_free_quota(parent,
|
ext4_xattr_inode_free_quota(parent, ea_inode,
|
||||||
le32_to_cpu(entry->e_value_size));
|
le32_to_cpu(entry->e_value_size));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1591,6 +1611,7 @@ static int ext4_xattr_set_entry(struct ext4_xattr_info *i,
|
|||||||
if (!s->not_found && here->e_value_inum) {
|
if (!s->not_found && here->e_value_inum) {
|
||||||
ret = ext4_xattr_inode_iget(inode,
|
ret = ext4_xattr_inode_iget(inode,
|
||||||
le32_to_cpu(here->e_value_inum),
|
le32_to_cpu(here->e_value_inum),
|
||||||
|
le32_to_cpu(here->e_hash),
|
||||||
&old_ea_inode);
|
&old_ea_inode);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
old_ea_inode = NULL;
|
old_ea_inode = NULL;
|
||||||
@ -1609,7 +1630,7 @@ static int ext4_xattr_set_entry(struct ext4_xattr_info *i,
|
|||||||
&new_ea_inode);
|
&new_ea_inode);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
new_ea_inode = NULL;
|
new_ea_inode = NULL;
|
||||||
ext4_xattr_inode_free_quota(inode, i->value_len);
|
ext4_xattr_inode_free_quota(inode, NULL, i->value_len);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1628,13 +1649,13 @@ static int ext4_xattr_set_entry(struct ext4_xattr_info *i,
|
|||||||
ext4_warning_inode(new_ea_inode,
|
ext4_warning_inode(new_ea_inode,
|
||||||
"dec ref new_ea_inode err=%d",
|
"dec ref new_ea_inode err=%d",
|
||||||
err);
|
err);
|
||||||
ext4_xattr_inode_free_quota(inode,
|
ext4_xattr_inode_free_quota(inode, new_ea_inode,
|
||||||
i->value_len);
|
i->value_len);
|
||||||
}
|
}
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
ext4_xattr_inode_free_quota(inode,
|
ext4_xattr_inode_free_quota(inode, old_ea_inode,
|
||||||
le32_to_cpu(here->e_value_size));
|
le32_to_cpu(here->e_value_size));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1805,8 +1826,10 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
|
|||||||
struct mb_cache_entry *ce = NULL;
|
struct mb_cache_entry *ce = NULL;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
struct mb_cache *ea_block_cache = EA_BLOCK_CACHE(inode);
|
struct mb_cache *ea_block_cache = EA_BLOCK_CACHE(inode);
|
||||||
struct inode *ea_inode = NULL;
|
struct inode *ea_inode = NULL, *tmp_inode;
|
||||||
size_t old_ea_inode_size = 0;
|
size_t old_ea_inode_quota = 0;
|
||||||
|
unsigned int ea_ino;
|
||||||
|
|
||||||
|
|
||||||
#define header(x) ((struct ext4_xattr_header *)(x))
|
#define header(x) ((struct ext4_xattr_header *)(x))
|
||||||
|
|
||||||
@ -1865,12 +1888,24 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
|
|||||||
* like it has an empty value.
|
* like it has an empty value.
|
||||||
*/
|
*/
|
||||||
if (!s->not_found && s->here->e_value_inum) {
|
if (!s->not_found && s->here->e_value_inum) {
|
||||||
/*
|
ea_ino = le32_to_cpu(s->here->e_value_inum);
|
||||||
* Defer quota free call for previous inode
|
error = ext4_xattr_inode_iget(inode, ea_ino,
|
||||||
* until success is guaranteed.
|
le32_to_cpu(s->here->e_hash),
|
||||||
*/
|
&tmp_inode);
|
||||||
old_ea_inode_size = le32_to_cpu(
|
if (error)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (!ext4_test_inode_state(tmp_inode,
|
||||||
|
EXT4_STATE_LUSTRE_EA_INODE)) {
|
||||||
|
/*
|
||||||
|
* Defer quota free call for previous
|
||||||
|
* inode until success is guaranteed.
|
||||||
|
*/
|
||||||
|
old_ea_inode_quota = le32_to_cpu(
|
||||||
s->here->e_value_size);
|
s->here->e_value_size);
|
||||||
|
}
|
||||||
|
iput(tmp_inode);
|
||||||
|
|
||||||
s->here->e_value_inum = 0;
|
s->here->e_value_inum = 0;
|
||||||
s->here->e_value_size = 0;
|
s->here->e_value_size = 0;
|
||||||
}
|
}
|
||||||
@ -1897,8 +1932,6 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
if (i->value && s->here->e_value_inum) {
|
if (i->value && s->here->e_value_inum) {
|
||||||
unsigned int ea_ino;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A ref count on ea_inode has been taken as part of the call to
|
* A ref count on ea_inode has been taken as part of the call to
|
||||||
* ext4_xattr_set_entry() above. We would like to drop this
|
* ext4_xattr_set_entry() above. We would like to drop this
|
||||||
@ -1906,7 +1939,9 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
|
|||||||
* initialized and has its own ref count on the ea_inode.
|
* initialized and has its own ref count on the ea_inode.
|
||||||
*/
|
*/
|
||||||
ea_ino = le32_to_cpu(s->here->e_value_inum);
|
ea_ino = le32_to_cpu(s->here->e_value_inum);
|
||||||
error = ext4_xattr_inode_iget(inode, ea_ino, &ea_inode);
|
error = ext4_xattr_inode_iget(inode, ea_ino,
|
||||||
|
le32_to_cpu(s->here->e_hash),
|
||||||
|
&ea_inode);
|
||||||
if (error) {
|
if (error) {
|
||||||
ea_inode = NULL;
|
ea_inode = NULL;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@ -2056,8 +2091,8 @@ getblk_failed:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (old_ea_inode_size)
|
if (old_ea_inode_quota)
|
||||||
ext4_xattr_inode_free_quota(inode, old_ea_inode_size);
|
ext4_xattr_inode_free_quota(inode, NULL, old_ea_inode_quota);
|
||||||
|
|
||||||
/* Update the inode. */
|
/* Update the inode. */
|
||||||
EXT4_I(inode)->i_file_acl = new_bh ? new_bh->b_blocknr : 0;
|
EXT4_I(inode)->i_file_acl = new_bh ? new_bh->b_blocknr : 0;
|
||||||
@ -2084,7 +2119,7 @@ cleanup:
|
|||||||
|
|
||||||
/* If there was an error, revert the quota charge. */
|
/* If there was an error, revert the quota charge. */
|
||||||
if (error)
|
if (error)
|
||||||
ext4_xattr_inode_free_quota(inode,
|
ext4_xattr_inode_free_quota(inode, ea_inode,
|
||||||
i_size_read(ea_inode));
|
i_size_read(ea_inode));
|
||||||
iput(ea_inode);
|
iput(ea_inode);
|
||||||
}
|
}
|
||||||
@ -2800,6 +2835,7 @@ int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode,
|
|||||||
struct ext4_xattr_ibody_header *header;
|
struct ext4_xattr_ibody_header *header;
|
||||||
struct ext4_iloc iloc = { .bh = NULL };
|
struct ext4_iloc iloc = { .bh = NULL };
|
||||||
struct ext4_xattr_entry *entry;
|
struct ext4_xattr_entry *entry;
|
||||||
|
struct inode *ea_inode;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
error = ext4_xattr_ensure_credits(handle, inode, extra_credits,
|
error = ext4_xattr_ensure_credits(handle, inode, extra_credits,
|
||||||
@ -2854,10 +2890,19 @@ int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode,
|
|||||||
|
|
||||||
if (ext4_has_feature_ea_inode(inode->i_sb)) {
|
if (ext4_has_feature_ea_inode(inode->i_sb)) {
|
||||||
for (entry = BFIRST(bh); !IS_LAST_ENTRY(entry);
|
for (entry = BFIRST(bh); !IS_LAST_ENTRY(entry);
|
||||||
entry = EXT4_XATTR_NEXT(entry))
|
entry = EXT4_XATTR_NEXT(entry)) {
|
||||||
if (entry->e_value_inum)
|
if (!entry->e_value_inum)
|
||||||
ext4_xattr_inode_free_quota(inode,
|
continue;
|
||||||
|
error = ext4_xattr_inode_iget(inode,
|
||||||
|
le32_to_cpu(entry->e_value_inum),
|
||||||
|
le32_to_cpu(entry->e_hash),
|
||||||
|
&ea_inode);
|
||||||
|
if (error)
|
||||||
|
continue;
|
||||||
|
ext4_xattr_inode_free_quota(inode, ea_inode,
|
||||||
le32_to_cpu(entry->e_value_size));
|
le32_to_cpu(entry->e_value_size));
|
||||||
|
iput(ea_inode);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,4 +285,19 @@ static inline bool itimerspec64_valid(const struct itimerspec64 *its)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* time_after32 - compare two 32-bit relative times
|
||||||
|
* @a: the time which may be after @b
|
||||||
|
* @b: the time which may be before @a
|
||||||
|
*
|
||||||
|
* time_after32(a, b) returns true if the time @a is after time @b.
|
||||||
|
* time_before32(b, a) returns true if the time @b is before time @a.
|
||||||
|
*
|
||||||
|
* Similar to time_after(), compare two 32-bit timestamps for relative
|
||||||
|
* times. This is useful for comparing 32-bit seconds values that can't
|
||||||
|
* be converted to 64-bit values (e.g. due to disk format or wire protocol
|
||||||
|
* issues) when it is known that the times are less than 68 years apart.
|
||||||
|
*/
|
||||||
|
#define time_after32(a, b) ((s32)((u32)(b) - (u32)(a)) < 0)
|
||||||
|
#define time_before32(b, a) time_after32(a, b)
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user