mirror of
https://github.com/Dasharo/linux.git
synced 2026-03-06 15:25:10 -08:00
Merge tag 'fs_for_v6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs
Pull ext2, isofs, udf, and quota updates from Jan Kara:
"A lot of material this time:
- removal of a lot of GFP_NOFS usage from ext2, udf, quota (either it
was legacy or replaced with scoped memalloc_nofs_*() API)
- removal of BUG_ONs in quota code
- conversion of UDF to the new mount API
- tightening quota on disk format verification
- fix some potentially unsafe use of RCU pointers in quota code and
annotate everything properly to make sparse happy
- a few other small quota, ext2, udf, and isofs fixes"
* tag 'fs_for_v6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs: (26 commits)
udf: remove SLAB_MEM_SPREAD flag usage
quota: remove SLAB_MEM_SPREAD flag usage
isofs: remove SLAB_MEM_SPREAD flag usage
ext2: remove SLAB_MEM_SPREAD flag usage
ext2: mark as deprecated
udf: convert to new mount API
udf: convert novrs to an option flag
MAINTAINERS: add missing git address for ext2 entry
quota: Detect loops in quota tree
quota: Properly annotate i_dquot arrays with __rcu
quota: Fix rcu annotations of inode dquot pointers
isofs: handle CDs with bad root inode but good Joliet root directory
udf: Avoid invalid LVID used on mount
quota: Fix potential NULL pointer dereference
quota: Drop GFP_NOFS instances under dquot->dq_lock and dqio_sem
quota: Set nofs allocation context when acquiring dqio_sem
ext2: Remove GFP_NOFS use in ext2_xattr_cache_insert()
ext2: Drop GFP_NOFS use in ext2_get_blocks()
ext2: Drop GFP_NOFS allocation from ext2_init_block_alloc_info()
udf: Remove GFP_NOFS allocation in udf_expand_file_adinicb()
...
This commit is contained in:
@@ -8021,6 +8021,7 @@ M: Jan Kara <jack@suse.com>
|
||||
L: linux-ext4@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/filesystems/ext2.rst
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs.git
|
||||
F: fs/ext2/
|
||||
F: include/linux/ext2*
|
||||
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config EXT2_FS
|
||||
tristate "Second extended fs support"
|
||||
tristate "Second extended fs support (DEPRECATED)"
|
||||
select BUFFER_HEAD
|
||||
select FS_IOMAP
|
||||
select LEGACY_DIRECT_IO
|
||||
help
|
||||
Ext2 is a standard Linux file system for hard disks.
|
||||
|
||||
To compile this file system support as a module, choose M here: the
|
||||
module will be called ext2.
|
||||
This filesystem driver is deprecated because it does not properly
|
||||
support inode time stamps beyond 03:14:07 UTC on 19 January 2038.
|
||||
|
||||
If unsure, say Y.
|
||||
Ext2 users are advised to use ext4 driver to access their filesystem.
|
||||
The driver is fully compatible, supports filesystems without journal
|
||||
or extents, and also supports larger time stamps if the filesystem
|
||||
is created with at least 256 byte inodes.
|
||||
|
||||
This code is kept as a simple reference for filesystem developers.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config EXT2_FS_XATTR
|
||||
bool "Ext2 extended attributes"
|
||||
|
||||
@@ -412,7 +412,7 @@ void ext2_init_block_alloc_info(struct inode *inode)
|
||||
struct ext2_block_alloc_info *block_i;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
|
||||
block_i = kmalloc(sizeof(*block_i), GFP_NOFS);
|
||||
block_i = kmalloc(sizeof(*block_i), GFP_KERNEL);
|
||||
if (block_i) {
|
||||
struct ext2_reserve_window_node *rsv = &block_i->rsv_window_node;
|
||||
|
||||
|
||||
@@ -674,7 +674,7 @@ struct ext2_inode_info {
|
||||
struct inode vfs_inode;
|
||||
struct list_head i_orphan; /* unlinked but open inodes */
|
||||
#ifdef CONFIG_QUOTA
|
||||
struct dquot *i_dquot[MAXQUOTAS];
|
||||
struct dquot __rcu *i_dquot[MAXQUOTAS];
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -754,7 +754,7 @@ static int ext2_get_blocks(struct inode *inode,
|
||||
*/
|
||||
err = sb_issue_zeroout(inode->i_sb,
|
||||
le32_to_cpu(chain[depth-1].key), count,
|
||||
GFP_NOFS);
|
||||
GFP_KERNEL);
|
||||
if (err) {
|
||||
mutex_unlock(&ei->truncate_mutex);
|
||||
goto cleanup;
|
||||
|
||||
@@ -319,7 +319,7 @@ static ssize_t ext2_quota_read(struct super_block *sb, int type, char *data, siz
|
||||
static ssize_t ext2_quota_write(struct super_block *sb, int type, const char *data, size_t len, loff_t off);
|
||||
static int ext2_quota_on(struct super_block *sb, int type, int format_id,
|
||||
const struct path *path);
|
||||
static struct dquot **ext2_get_dquots(struct inode *inode)
|
||||
static struct dquot __rcu **ext2_get_dquots(struct inode *inode)
|
||||
{
|
||||
return EXT2_I(inode)->i_dquot;
|
||||
}
|
||||
|
||||
@@ -874,7 +874,7 @@ ext2_xattr_cache_insert(struct mb_cache *cache, struct buffer_head *bh)
|
||||
__u32 hash = le32_to_cpu(HDR(bh)->h_hash);
|
||||
int error;
|
||||
|
||||
error = mb_cache_entry_create(cache, GFP_NOFS, hash, bh->b_blocknr,
|
||||
error = mb_cache_entry_create(cache, GFP_KERNEL, hash, bh->b_blocknr,
|
||||
true);
|
||||
if (error) {
|
||||
if (error == -EBUSY) {
|
||||
|
||||
@@ -1158,7 +1158,7 @@ struct ext4_inode_info {
|
||||
tid_t i_datasync_tid;
|
||||
|
||||
#ifdef CONFIG_QUOTA
|
||||
struct dquot *i_dquot[MAXQUOTAS];
|
||||
struct dquot __rcu *i_dquot[MAXQUOTAS];
|
||||
#endif
|
||||
|
||||
/* Precomputed uuid+inum+igen checksum for seeding inode checksums */
|
||||
|
||||
@@ -1599,7 +1599,7 @@ static ssize_t ext4_quota_write(struct super_block *sb, int type,
|
||||
static int ext4_quota_enable(struct super_block *sb, int type, int format_id,
|
||||
unsigned int flags);
|
||||
|
||||
static struct dquot **ext4_get_dquots(struct inode *inode)
|
||||
static struct dquot __rcu **ext4_get_dquots(struct inode *inode)
|
||||
{
|
||||
return EXT4_I(inode)->i_dquot;
|
||||
}
|
||||
|
||||
@@ -830,7 +830,7 @@ struct f2fs_inode_info {
|
||||
spinlock_t i_size_lock; /* protect last_disk_size */
|
||||
|
||||
#ifdef CONFIG_QUOTA
|
||||
struct dquot *i_dquot[MAXQUOTAS];
|
||||
struct dquot __rcu *i_dquot[MAXQUOTAS];
|
||||
|
||||
/* quota space reservation, managed internally by quota code */
|
||||
qsize_t i_reserved_quota;
|
||||
|
||||
@@ -2768,7 +2768,7 @@ int f2fs_dquot_initialize(struct inode *inode)
|
||||
return dquot_initialize(inode);
|
||||
}
|
||||
|
||||
static struct dquot **f2fs_get_dquots(struct inode *inode)
|
||||
static struct dquot __rcu **f2fs_get_dquots(struct inode *inode)
|
||||
{
|
||||
return F2FS_I(inode)->i_dquot;
|
||||
}
|
||||
|
||||
@@ -908,8 +908,22 @@ root_found:
|
||||
* we then decide whether to use the Joliet descriptor.
|
||||
*/
|
||||
inode = isofs_iget(s, sbi->s_firstdatazone, 0);
|
||||
if (IS_ERR(inode))
|
||||
goto out_no_root;
|
||||
|
||||
/*
|
||||
* Fix for broken CDs with a corrupt root inode but a correct Joliet
|
||||
* root directory.
|
||||
*/
|
||||
if (IS_ERR(inode)) {
|
||||
if (joliet_level && sbi->s_firstdatazone != first_data_zone) {
|
||||
printk(KERN_NOTICE
|
||||
"ISOFS: root inode is unusable. "
|
||||
"Disabling Rock Ridge and switching to Joliet.");
|
||||
sbi->s_rock = 0;
|
||||
inode = NULL;
|
||||
} else {
|
||||
goto out_no_root;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Fix for broken CDs with Rock Ridge and empty ISO root directory but
|
||||
|
||||
@@ -92,7 +92,7 @@ struct jfs_inode_info {
|
||||
} link;
|
||||
} u;
|
||||
#ifdef CONFIG_QUOTA
|
||||
struct dquot *i_dquot[MAXQUOTAS];
|
||||
struct dquot __rcu *i_dquot[MAXQUOTAS];
|
||||
#endif
|
||||
u32 dev; /* will die when we get wide dev_t */
|
||||
struct inode vfs_inode;
|
||||
|
||||
@@ -824,7 +824,7 @@ out:
|
||||
return len - towrite;
|
||||
}
|
||||
|
||||
static struct dquot **jfs_get_dquots(struct inode *inode)
|
||||
static struct dquot __rcu **jfs_get_dquots(struct inode *inode)
|
||||
{
|
||||
return JFS_IP(inode)->i_dquot;
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ struct ocfs2_inode_info
|
||||
tid_t i_sync_tid;
|
||||
tid_t i_datasync_tid;
|
||||
|
||||
struct dquot *i_dquot[MAXQUOTAS];
|
||||
struct dquot __rcu *i_dquot[MAXQUOTAS];
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -447,14 +447,17 @@ int ocfs2_global_write_info(struct super_block *sb, int type)
|
||||
int err;
|
||||
struct quota_info *dqopt = sb_dqopt(sb);
|
||||
struct ocfs2_mem_dqinfo *info = dqopt->info[type].dqi_priv;
|
||||
unsigned int memalloc;
|
||||
|
||||
down_write(&dqopt->dqio_sem);
|
||||
memalloc = memalloc_nofs_save();
|
||||
err = ocfs2_qinfo_lock(info, 1);
|
||||
if (err < 0)
|
||||
goto out_sem;
|
||||
err = __ocfs2_global_write_info(sb, type);
|
||||
ocfs2_qinfo_unlock(info, 1);
|
||||
out_sem:
|
||||
memalloc_nofs_restore(memalloc);
|
||||
up_write(&dqopt->dqio_sem);
|
||||
return err;
|
||||
}
|
||||
@@ -601,6 +604,7 @@ static int ocfs2_sync_dquot_helper(struct dquot *dquot, unsigned long type)
|
||||
struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
|
||||
struct ocfs2_super *osb = OCFS2_SB(sb);
|
||||
int status = 0;
|
||||
unsigned int memalloc;
|
||||
|
||||
trace_ocfs2_sync_dquot_helper(from_kqid(&init_user_ns, dquot->dq_id),
|
||||
dquot->dq_id.type,
|
||||
@@ -618,6 +622,7 @@ static int ocfs2_sync_dquot_helper(struct dquot *dquot, unsigned long type)
|
||||
goto out_ilock;
|
||||
}
|
||||
down_write(&sb_dqopt(sb)->dqio_sem);
|
||||
memalloc = memalloc_nofs_save();
|
||||
status = ocfs2_sync_dquot(dquot);
|
||||
if (status < 0)
|
||||
mlog_errno(status);
|
||||
@@ -625,6 +630,7 @@ static int ocfs2_sync_dquot_helper(struct dquot *dquot, unsigned long type)
|
||||
status = ocfs2_local_write_dquot(dquot);
|
||||
if (status < 0)
|
||||
mlog_errno(status);
|
||||
memalloc_nofs_restore(memalloc);
|
||||
up_write(&sb_dqopt(sb)->dqio_sem);
|
||||
ocfs2_commit_trans(osb, handle);
|
||||
out_ilock:
|
||||
@@ -662,6 +668,7 @@ static int ocfs2_write_dquot(struct dquot *dquot)
|
||||
handle_t *handle;
|
||||
struct ocfs2_super *osb = OCFS2_SB(dquot->dq_sb);
|
||||
int status = 0;
|
||||
unsigned int memalloc;
|
||||
|
||||
trace_ocfs2_write_dquot(from_kqid(&init_user_ns, dquot->dq_id),
|
||||
dquot->dq_id.type);
|
||||
@@ -673,7 +680,9 @@ static int ocfs2_write_dquot(struct dquot *dquot)
|
||||
goto out;
|
||||
}
|
||||
down_write(&sb_dqopt(dquot->dq_sb)->dqio_sem);
|
||||
memalloc = memalloc_nofs_save();
|
||||
status = ocfs2_local_write_dquot(dquot);
|
||||
memalloc_nofs_restore(memalloc);
|
||||
up_write(&sb_dqopt(dquot->dq_sb)->dqio_sem);
|
||||
ocfs2_commit_trans(osb, handle);
|
||||
out:
|
||||
@@ -920,6 +929,7 @@ static int ocfs2_mark_dquot_dirty(struct dquot *dquot)
|
||||
struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
|
||||
handle_t *handle;
|
||||
struct ocfs2_super *osb = OCFS2_SB(sb);
|
||||
unsigned int memalloc;
|
||||
|
||||
trace_ocfs2_mark_dquot_dirty(from_kqid(&init_user_ns, dquot->dq_id),
|
||||
type);
|
||||
@@ -946,6 +956,7 @@ static int ocfs2_mark_dquot_dirty(struct dquot *dquot)
|
||||
goto out_ilock;
|
||||
}
|
||||
down_write(&sb_dqopt(sb)->dqio_sem);
|
||||
memalloc = memalloc_nofs_save();
|
||||
status = ocfs2_sync_dquot(dquot);
|
||||
if (status < 0) {
|
||||
mlog_errno(status);
|
||||
@@ -954,6 +965,7 @@ static int ocfs2_mark_dquot_dirty(struct dquot *dquot)
|
||||
/* Now write updated local dquot structure */
|
||||
status = ocfs2_local_write_dquot(dquot);
|
||||
out_dlock:
|
||||
memalloc_nofs_restore(memalloc);
|
||||
up_write(&sb_dqopt(sb)->dqio_sem);
|
||||
ocfs2_commit_trans(osb, handle);
|
||||
out_ilock:
|
||||
|
||||
@@ -470,6 +470,7 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode,
|
||||
int bit, chunk;
|
||||
struct ocfs2_recovery_chunk *rchunk, *next;
|
||||
qsize_t spacechange, inodechange;
|
||||
unsigned int memalloc;
|
||||
|
||||
trace_ocfs2_recover_local_quota_file((unsigned long)lqinode->i_ino, type);
|
||||
|
||||
@@ -521,6 +522,7 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode,
|
||||
goto out_drop_lock;
|
||||
}
|
||||
down_write(&sb_dqopt(sb)->dqio_sem);
|
||||
memalloc = memalloc_nofs_save();
|
||||
spin_lock(&dquot->dq_dqb_lock);
|
||||
/* Add usage from quota entry into quota changes
|
||||
* of our node. Auxiliary variables are important
|
||||
@@ -553,6 +555,7 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode,
|
||||
unlock_buffer(qbh);
|
||||
ocfs2_journal_dirty(handle, qbh);
|
||||
out_commit:
|
||||
memalloc_nofs_restore(memalloc);
|
||||
up_write(&sb_dqopt(sb)->dqio_sem);
|
||||
ocfs2_commit_trans(OCFS2_SB(sb), handle);
|
||||
out_drop_lock:
|
||||
|
||||
@@ -122,7 +122,7 @@ static int ocfs2_susp_quotas(struct ocfs2_super *osb, int unsuspend);
|
||||
static int ocfs2_enable_quotas(struct ocfs2_super *osb);
|
||||
static void ocfs2_disable_quotas(struct ocfs2_super *osb);
|
||||
|
||||
static struct dquot **ocfs2_get_dquots(struct inode *inode)
|
||||
static struct dquot __rcu **ocfs2_get_dquots(struct inode *inode)
|
||||
{
|
||||
return OCFS2_I(inode)->i_dquot;
|
||||
}
|
||||
|
||||
172
fs/quota/dquot.c
172
fs/quota/dquot.c
@@ -399,15 +399,17 @@ int dquot_mark_dquot_dirty(struct dquot *dquot)
|
||||
EXPORT_SYMBOL(dquot_mark_dquot_dirty);
|
||||
|
||||
/* Dirtify all the dquots - this can block when journalling */
|
||||
static inline int mark_all_dquot_dirty(struct dquot * const *dquot)
|
||||
static inline int mark_all_dquot_dirty(struct dquot __rcu * const *dquots)
|
||||
{
|
||||
int ret, err, cnt;
|
||||
struct dquot *dquot;
|
||||
|
||||
ret = err = 0;
|
||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||
if (dquot[cnt])
|
||||
dquot = srcu_dereference(dquots[cnt], &dquot_srcu);
|
||||
if (dquot)
|
||||
/* Even in case of error we have to continue */
|
||||
ret = mark_dquot_dirty(dquot[cnt]);
|
||||
ret = mark_dquot_dirty(dquot);
|
||||
if (!err)
|
||||
err = ret;
|
||||
}
|
||||
@@ -875,10 +877,7 @@ void dqput(struct dquot *dquot)
|
||||
}
|
||||
|
||||
/* Need to release dquot? */
|
||||
#ifdef CONFIG_QUOTA_DEBUG
|
||||
/* sanity check */
|
||||
BUG_ON(!list_empty(&dquot->dq_free));
|
||||
#endif
|
||||
WARN_ON_ONCE(!list_empty(&dquot->dq_free));
|
||||
put_releasing_dquots(dquot);
|
||||
atomic_dec(&dquot->dq_count);
|
||||
spin_unlock(&dq_list_lock);
|
||||
@@ -987,9 +986,8 @@ we_slept:
|
||||
* smp_mb__before_atomic() in dquot_acquire().
|
||||
*/
|
||||
smp_rmb();
|
||||
#ifdef CONFIG_QUOTA_DEBUG
|
||||
BUG_ON(!dquot->dq_sb); /* Has somebody invalidated entry under us? */
|
||||
#endif
|
||||
/* Has somebody invalidated entry under us? */
|
||||
WARN_ON_ONCE(hlist_unhashed(&dquot->dq_hash));
|
||||
out:
|
||||
if (empty)
|
||||
do_destroy_dquot(empty);
|
||||
@@ -998,14 +996,14 @@ out:
|
||||
}
|
||||
EXPORT_SYMBOL(dqget);
|
||||
|
||||
static inline struct dquot **i_dquot(struct inode *inode)
|
||||
static inline struct dquot __rcu **i_dquot(struct inode *inode)
|
||||
{
|
||||
return inode->i_sb->s_op->get_dquots(inode);
|
||||
}
|
||||
|
||||
static int dqinit_needed(struct inode *inode, int type)
|
||||
{
|
||||
struct dquot * const *dquots;
|
||||
struct dquot __rcu * const *dquots;
|
||||
int cnt;
|
||||
|
||||
if (IS_NOQUOTA(inode))
|
||||
@@ -1095,14 +1093,16 @@ static void remove_dquot_ref(struct super_block *sb, int type)
|
||||
*/
|
||||
spin_lock(&dq_data_lock);
|
||||
if (!IS_NOQUOTA(inode)) {
|
||||
struct dquot **dquots = i_dquot(inode);
|
||||
struct dquot *dquot = dquots[type];
|
||||
struct dquot __rcu **dquots = i_dquot(inode);
|
||||
struct dquot *dquot = srcu_dereference_check(
|
||||
dquots[type], &dquot_srcu,
|
||||
lockdep_is_held(&dq_data_lock));
|
||||
|
||||
#ifdef CONFIG_QUOTA_DEBUG
|
||||
if (unlikely(inode_get_rsv_space(inode) > 0))
|
||||
reserved = 1;
|
||||
#endif
|
||||
dquots[type] = NULL;
|
||||
rcu_assign_pointer(dquots[type], NULL);
|
||||
if (dquot)
|
||||
dqput(dquot);
|
||||
}
|
||||
@@ -1455,7 +1455,8 @@ static int inode_quota_active(const struct inode *inode)
|
||||
static int __dquot_initialize(struct inode *inode, int type)
|
||||
{
|
||||
int cnt, init_needed = 0;
|
||||
struct dquot **dquots, *got[MAXQUOTAS] = {};
|
||||
struct dquot __rcu **dquots;
|
||||
struct dquot *got[MAXQUOTAS] = {};
|
||||
struct super_block *sb = inode->i_sb;
|
||||
qsize_t rsv;
|
||||
int ret = 0;
|
||||
@@ -1530,7 +1531,7 @@ static int __dquot_initialize(struct inode *inode, int type)
|
||||
if (!got[cnt])
|
||||
continue;
|
||||
if (!dquots[cnt]) {
|
||||
dquots[cnt] = got[cnt];
|
||||
rcu_assign_pointer(dquots[cnt], got[cnt]);
|
||||
got[cnt] = NULL;
|
||||
/*
|
||||
* Make quota reservation system happy if someone
|
||||
@@ -1538,12 +1539,16 @@ static int __dquot_initialize(struct inode *inode, int type)
|
||||
*/
|
||||
rsv = inode_get_rsv_space(inode);
|
||||
if (unlikely(rsv)) {
|
||||
struct dquot *dquot = srcu_dereference_check(
|
||||
dquots[cnt], &dquot_srcu,
|
||||
lockdep_is_held(&dq_data_lock));
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
/* Get reservation again under proper lock */
|
||||
rsv = __inode_get_rsv_space(inode);
|
||||
spin_lock(&dquots[cnt]->dq_dqb_lock);
|
||||
dquots[cnt]->dq_dqb.dqb_rsvspace += rsv;
|
||||
spin_unlock(&dquots[cnt]->dq_dqb_lock);
|
||||
spin_lock(&dquot->dq_dqb_lock);
|
||||
dquot->dq_dqb.dqb_rsvspace += rsv;
|
||||
spin_unlock(&dquot->dq_dqb_lock);
|
||||
spin_unlock(&inode->i_lock);
|
||||
}
|
||||
}
|
||||
@@ -1565,7 +1570,7 @@ EXPORT_SYMBOL(dquot_initialize);
|
||||
|
||||
bool dquot_initialize_needed(struct inode *inode)
|
||||
{
|
||||
struct dquot **dquots;
|
||||
struct dquot __rcu **dquots;
|
||||
int i;
|
||||
|
||||
if (!inode_quota_active(inode))
|
||||
@@ -1590,13 +1595,14 @@ EXPORT_SYMBOL(dquot_initialize_needed);
|
||||
static void __dquot_drop(struct inode *inode)
|
||||
{
|
||||
int cnt;
|
||||
struct dquot **dquots = i_dquot(inode);
|
||||
struct dquot __rcu **dquots = i_dquot(inode);
|
||||
struct dquot *put[MAXQUOTAS];
|
||||
|
||||
spin_lock(&dq_data_lock);
|
||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||
put[cnt] = dquots[cnt];
|
||||
dquots[cnt] = NULL;
|
||||
put[cnt] = srcu_dereference_check(dquots[cnt], &dquot_srcu,
|
||||
lockdep_is_held(&dq_data_lock));
|
||||
rcu_assign_pointer(dquots[cnt], NULL);
|
||||
}
|
||||
spin_unlock(&dq_data_lock);
|
||||
dqput_all(put);
|
||||
@@ -1604,7 +1610,7 @@ static void __dquot_drop(struct inode *inode)
|
||||
|
||||
void dquot_drop(struct inode *inode)
|
||||
{
|
||||
struct dquot * const *dquots;
|
||||
struct dquot __rcu * const *dquots;
|
||||
int cnt;
|
||||
|
||||
if (IS_NOQUOTA(inode))
|
||||
@@ -1677,7 +1683,8 @@ int __dquot_alloc_space(struct inode *inode, qsize_t number, int flags)
|
||||
int cnt, ret = 0, index;
|
||||
struct dquot_warn warn[MAXQUOTAS];
|
||||
int reserve = flags & DQUOT_SPACE_RESERVE;
|
||||
struct dquot **dquots;
|
||||
struct dquot __rcu **dquots;
|
||||
struct dquot *dquot;
|
||||
|
||||
if (!inode_quota_active(inode)) {
|
||||
if (reserve) {
|
||||
@@ -1697,27 +1704,26 @@ int __dquot_alloc_space(struct inode *inode, qsize_t number, int flags)
|
||||
index = srcu_read_lock(&dquot_srcu);
|
||||
spin_lock(&inode->i_lock);
|
||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||
if (!dquots[cnt])
|
||||
dquot = srcu_dereference(dquots[cnt], &dquot_srcu);
|
||||
if (!dquot)
|
||||
continue;
|
||||
if (reserve) {
|
||||
ret = dquot_add_space(dquots[cnt], 0, number, flags,
|
||||
&warn[cnt]);
|
||||
ret = dquot_add_space(dquot, 0, number, flags, &warn[cnt]);
|
||||
} else {
|
||||
ret = dquot_add_space(dquots[cnt], number, 0, flags,
|
||||
&warn[cnt]);
|
||||
ret = dquot_add_space(dquot, number, 0, flags, &warn[cnt]);
|
||||
}
|
||||
if (ret) {
|
||||
/* Back out changes we already did */
|
||||
for (cnt--; cnt >= 0; cnt--) {
|
||||
if (!dquots[cnt])
|
||||
dquot = srcu_dereference(dquots[cnt], &dquot_srcu);
|
||||
if (!dquot)
|
||||
continue;
|
||||
spin_lock(&dquots[cnt]->dq_dqb_lock);
|
||||
spin_lock(&dquot->dq_dqb_lock);
|
||||
if (reserve)
|
||||
dquot_free_reserved_space(dquots[cnt],
|
||||
number);
|
||||
dquot_free_reserved_space(dquot, number);
|
||||
else
|
||||
dquot_decr_space(dquots[cnt], number);
|
||||
spin_unlock(&dquots[cnt]->dq_dqb_lock);
|
||||
dquot_decr_space(dquot, number);
|
||||
spin_unlock(&dquot->dq_dqb_lock);
|
||||
}
|
||||
spin_unlock(&inode->i_lock);
|
||||
goto out_flush_warn;
|
||||
@@ -1747,7 +1753,8 @@ int dquot_alloc_inode(struct inode *inode)
|
||||
{
|
||||
int cnt, ret = 0, index;
|
||||
struct dquot_warn warn[MAXQUOTAS];
|
||||
struct dquot * const *dquots;
|
||||
struct dquot __rcu * const *dquots;
|
||||
struct dquot *dquot;
|
||||
|
||||
if (!inode_quota_active(inode))
|
||||
return 0;
|
||||
@@ -1758,17 +1765,19 @@ int dquot_alloc_inode(struct inode *inode)
|
||||
index = srcu_read_lock(&dquot_srcu);
|
||||
spin_lock(&inode->i_lock);
|
||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||
if (!dquots[cnt])
|
||||
dquot = srcu_dereference(dquots[cnt], &dquot_srcu);
|
||||
if (!dquot)
|
||||
continue;
|
||||
ret = dquot_add_inodes(dquots[cnt], 1, &warn[cnt]);
|
||||
ret = dquot_add_inodes(dquot, 1, &warn[cnt]);
|
||||
if (ret) {
|
||||
for (cnt--; cnt >= 0; cnt--) {
|
||||
if (!dquots[cnt])
|
||||
dquot = srcu_dereference(dquots[cnt], &dquot_srcu);
|
||||
if (!dquot)
|
||||
continue;
|
||||
/* Back out changes we already did */
|
||||
spin_lock(&dquots[cnt]->dq_dqb_lock);
|
||||
dquot_decr_inodes(dquots[cnt], 1);
|
||||
spin_unlock(&dquots[cnt]->dq_dqb_lock);
|
||||
spin_lock(&dquot->dq_dqb_lock);
|
||||
dquot_decr_inodes(dquot, 1);
|
||||
spin_unlock(&dquot->dq_dqb_lock);
|
||||
}
|
||||
goto warn_put_all;
|
||||
}
|
||||
@@ -1789,7 +1798,8 @@ EXPORT_SYMBOL(dquot_alloc_inode);
|
||||
*/
|
||||
void dquot_claim_space_nodirty(struct inode *inode, qsize_t number)
|
||||
{
|
||||
struct dquot **dquots;
|
||||
struct dquot __rcu **dquots;
|
||||
struct dquot *dquot;
|
||||
int cnt, index;
|
||||
|
||||
if (!inode_quota_active(inode)) {
|
||||
@@ -1805,9 +1815,8 @@ void dquot_claim_space_nodirty(struct inode *inode, qsize_t number)
|
||||
spin_lock(&inode->i_lock);
|
||||
/* Claim reserved quotas to allocated quotas */
|
||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||
if (dquots[cnt]) {
|
||||
struct dquot *dquot = dquots[cnt];
|
||||
|
||||
dquot = srcu_dereference(dquots[cnt], &dquot_srcu);
|
||||
if (dquot) {
|
||||
spin_lock(&dquot->dq_dqb_lock);
|
||||
if (WARN_ON_ONCE(dquot->dq_dqb.dqb_rsvspace < number))
|
||||
number = dquot->dq_dqb.dqb_rsvspace;
|
||||
@@ -1831,7 +1840,8 @@ EXPORT_SYMBOL(dquot_claim_space_nodirty);
|
||||
*/
|
||||
void dquot_reclaim_space_nodirty(struct inode *inode, qsize_t number)
|
||||
{
|
||||
struct dquot **dquots;
|
||||
struct dquot __rcu **dquots;
|
||||
struct dquot *dquot;
|
||||
int cnt, index;
|
||||
|
||||
if (!inode_quota_active(inode)) {
|
||||
@@ -1847,9 +1857,8 @@ void dquot_reclaim_space_nodirty(struct inode *inode, qsize_t number)
|
||||
spin_lock(&inode->i_lock);
|
||||
/* Claim reserved quotas to allocated quotas */
|
||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||
if (dquots[cnt]) {
|
||||
struct dquot *dquot = dquots[cnt];
|
||||
|
||||
dquot = srcu_dereference(dquots[cnt], &dquot_srcu);
|
||||
if (dquot) {
|
||||
spin_lock(&dquot->dq_dqb_lock);
|
||||
if (WARN_ON_ONCE(dquot->dq_dqb.dqb_curspace < number))
|
||||
number = dquot->dq_dqb.dqb_curspace;
|
||||
@@ -1875,7 +1884,8 @@ void __dquot_free_space(struct inode *inode, qsize_t number, int flags)
|
||||
{
|
||||
unsigned int cnt;
|
||||
struct dquot_warn warn[MAXQUOTAS];
|
||||
struct dquot **dquots;
|
||||
struct dquot __rcu **dquots;
|
||||
struct dquot *dquot;
|
||||
int reserve = flags & DQUOT_SPACE_RESERVE, index;
|
||||
|
||||
if (!inode_quota_active(inode)) {
|
||||
@@ -1896,17 +1906,18 @@ void __dquot_free_space(struct inode *inode, qsize_t number, int flags)
|
||||
int wtype;
|
||||
|
||||
warn[cnt].w_type = QUOTA_NL_NOWARN;
|
||||
if (!dquots[cnt])
|
||||
dquot = srcu_dereference(dquots[cnt], &dquot_srcu);
|
||||
if (!dquot)
|
||||
continue;
|
||||
spin_lock(&dquots[cnt]->dq_dqb_lock);
|
||||
wtype = info_bdq_free(dquots[cnt], number);
|
||||
spin_lock(&dquot->dq_dqb_lock);
|
||||
wtype = info_bdq_free(dquot, number);
|
||||
if (wtype != QUOTA_NL_NOWARN)
|
||||
prepare_warning(&warn[cnt], dquots[cnt], wtype);
|
||||
prepare_warning(&warn[cnt], dquot, wtype);
|
||||
if (reserve)
|
||||
dquot_free_reserved_space(dquots[cnt], number);
|
||||
dquot_free_reserved_space(dquot, number);
|
||||
else
|
||||
dquot_decr_space(dquots[cnt], number);
|
||||
spin_unlock(&dquots[cnt]->dq_dqb_lock);
|
||||
dquot_decr_space(dquot, number);
|
||||
spin_unlock(&dquot->dq_dqb_lock);
|
||||
}
|
||||
if (reserve)
|
||||
*inode_reserved_space(inode) -= number;
|
||||
@@ -1930,7 +1941,8 @@ void dquot_free_inode(struct inode *inode)
|
||||
{
|
||||
unsigned int cnt;
|
||||
struct dquot_warn warn[MAXQUOTAS];
|
||||
struct dquot * const *dquots;
|
||||
struct dquot __rcu * const *dquots;
|
||||
struct dquot *dquot;
|
||||
int index;
|
||||
|
||||
if (!inode_quota_active(inode))
|
||||
@@ -1941,16 +1953,16 @@ void dquot_free_inode(struct inode *inode)
|
||||
spin_lock(&inode->i_lock);
|
||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||
int wtype;
|
||||
|
||||
warn[cnt].w_type = QUOTA_NL_NOWARN;
|
||||
if (!dquots[cnt])
|
||||
dquot = srcu_dereference(dquots[cnt], &dquot_srcu);
|
||||
if (!dquot)
|
||||
continue;
|
||||
spin_lock(&dquots[cnt]->dq_dqb_lock);
|
||||
wtype = info_idq_free(dquots[cnt], 1);
|
||||
spin_lock(&dquot->dq_dqb_lock);
|
||||
wtype = info_idq_free(dquot, 1);
|
||||
if (wtype != QUOTA_NL_NOWARN)
|
||||
prepare_warning(&warn[cnt], dquots[cnt], wtype);
|
||||
dquot_decr_inodes(dquots[cnt], 1);
|
||||
spin_unlock(&dquots[cnt]->dq_dqb_lock);
|
||||
prepare_warning(&warn[cnt], dquot, wtype);
|
||||
dquot_decr_inodes(dquot, 1);
|
||||
spin_unlock(&dquot->dq_dqb_lock);
|
||||
}
|
||||
spin_unlock(&inode->i_lock);
|
||||
mark_all_dquot_dirty(dquots);
|
||||
@@ -1976,8 +1988,9 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
|
||||
qsize_t cur_space;
|
||||
qsize_t rsv_space = 0;
|
||||
qsize_t inode_usage = 1;
|
||||
struct dquot __rcu **dquots;
|
||||
struct dquot *transfer_from[MAXQUOTAS] = {};
|
||||
int cnt, ret = 0;
|
||||
int cnt, index, ret = 0;
|
||||
char is_valid[MAXQUOTAS] = {};
|
||||
struct dquot_warn warn_to[MAXQUOTAS];
|
||||
struct dquot_warn warn_from_inodes[MAXQUOTAS];
|
||||
@@ -2008,6 +2021,7 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
|
||||
}
|
||||
cur_space = __inode_get_bytes(inode);
|
||||
rsv_space = __inode_get_rsv_space(inode);
|
||||
dquots = i_dquot(inode);
|
||||
/*
|
||||
* Build the transfer_from list, check limits, and update usage in
|
||||
* the target structures.
|
||||
@@ -2022,7 +2036,8 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
|
||||
if (!sb_has_quota_active(inode->i_sb, cnt))
|
||||
continue;
|
||||
is_valid[cnt] = 1;
|
||||
transfer_from[cnt] = i_dquot(inode)[cnt];
|
||||
transfer_from[cnt] = srcu_dereference_check(dquots[cnt],
|
||||
&dquot_srcu, lockdep_is_held(&dq_data_lock));
|
||||
ret = dquot_add_inodes(transfer_to[cnt], inode_usage,
|
||||
&warn_to[cnt]);
|
||||
if (ret)
|
||||
@@ -2061,13 +2076,21 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
|
||||
rsv_space);
|
||||
spin_unlock(&transfer_from[cnt]->dq_dqb_lock);
|
||||
}
|
||||
i_dquot(inode)[cnt] = transfer_to[cnt];
|
||||
rcu_assign_pointer(dquots[cnt], transfer_to[cnt]);
|
||||
}
|
||||
spin_unlock(&inode->i_lock);
|
||||
spin_unlock(&dq_data_lock);
|
||||
|
||||
mark_all_dquot_dirty(transfer_from);
|
||||
mark_all_dquot_dirty(transfer_to);
|
||||
/*
|
||||
* These arrays are local and we hold dquot references so we don't need
|
||||
* the srcu protection but still take dquot_srcu to avoid warning in
|
||||
* mark_all_dquot_dirty().
|
||||
*/
|
||||
index = srcu_read_lock(&dquot_srcu);
|
||||
mark_all_dquot_dirty((struct dquot __rcu **)transfer_from);
|
||||
mark_all_dquot_dirty((struct dquot __rcu **)transfer_to);
|
||||
srcu_read_unlock(&dquot_srcu, index);
|
||||
|
||||
flush_warnings(warn_to);
|
||||
flush_warnings(warn_from_inodes);
|
||||
flush_warnings(warn_from_space);
|
||||
@@ -2388,7 +2411,8 @@ int dquot_load_quota_sb(struct super_block *sb, int type, int format_id,
|
||||
lockdep_assert_held_write(&sb->s_umount);
|
||||
|
||||
/* Just unsuspend quotas? */
|
||||
BUG_ON(flags & DQUOT_SUSPENDED);
|
||||
if (WARN_ON_ONCE(flags & DQUOT_SUSPENDED))
|
||||
return -EINVAL;
|
||||
|
||||
if (!fmt)
|
||||
return -ESRCH;
|
||||
|
||||
@@ -21,6 +21,12 @@ MODULE_AUTHOR("Jan Kara");
|
||||
MODULE_DESCRIPTION("Quota trie support");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* Maximum quota tree depth we support. Only to limit recursion when working
|
||||
* with the tree.
|
||||
*/
|
||||
#define MAX_QTREE_DEPTH 6
|
||||
|
||||
#define __QUOTA_QT_PARANOIA
|
||||
|
||||
static int __get_index(struct qtree_mem_dqinfo *info, qid_t id, int depth)
|
||||
@@ -108,7 +114,7 @@ static int check_dquot_block_header(struct qtree_mem_dqinfo *info,
|
||||
/* Remove empty block from list and return it */
|
||||
static int get_free_dqblk(struct qtree_mem_dqinfo *info)
|
||||
{
|
||||
char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
|
||||
char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
|
||||
struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
|
||||
int ret, blk;
|
||||
|
||||
@@ -160,7 +166,7 @@ static int put_free_dqblk(struct qtree_mem_dqinfo *info, char *buf, uint blk)
|
||||
static int remove_free_dqentry(struct qtree_mem_dqinfo *info, char *buf,
|
||||
uint blk)
|
||||
{
|
||||
char *tmpbuf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
|
||||
char *tmpbuf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
|
||||
struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
|
||||
uint nextblk = le32_to_cpu(dh->dqdh_next_free);
|
||||
uint prevblk = le32_to_cpu(dh->dqdh_prev_free);
|
||||
@@ -207,7 +213,7 @@ out_buf:
|
||||
static int insert_free_dqentry(struct qtree_mem_dqinfo *info, char *buf,
|
||||
uint blk)
|
||||
{
|
||||
char *tmpbuf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
|
||||
char *tmpbuf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
|
||||
struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
|
||||
int err;
|
||||
|
||||
@@ -255,7 +261,7 @@ static uint find_free_dqentry(struct qtree_mem_dqinfo *info,
|
||||
{
|
||||
uint blk, i;
|
||||
struct qt_disk_dqdbheader *dh;
|
||||
char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
|
||||
char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
|
||||
char *ddquot;
|
||||
|
||||
*err = 0;
|
||||
@@ -327,27 +333,36 @@ out_buf:
|
||||
|
||||
/* Insert reference to structure into the trie */
|
||||
static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
|
||||
uint *treeblk, int depth)
|
||||
uint *blks, int depth)
|
||||
{
|
||||
char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
|
||||
char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
|
||||
int ret = 0, newson = 0, newact = 0;
|
||||
__le32 *ref;
|
||||
uint newblk;
|
||||
int i;
|
||||
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
if (!*treeblk) {
|
||||
if (!blks[depth]) {
|
||||
ret = get_free_dqblk(info);
|
||||
if (ret < 0)
|
||||
goto out_buf;
|
||||
*treeblk = ret;
|
||||
for (i = 0; i < depth; i++)
|
||||
if (ret == blks[i]) {
|
||||
quota_error(dquot->dq_sb,
|
||||
"Free block already used in tree: block %u",
|
||||
ret);
|
||||
ret = -EIO;
|
||||
goto out_buf;
|
||||
}
|
||||
blks[depth] = ret;
|
||||
memset(buf, 0, info->dqi_usable_bs);
|
||||
newact = 1;
|
||||
} else {
|
||||
ret = read_blk(info, *treeblk, buf);
|
||||
ret = read_blk(info, blks[depth], buf);
|
||||
if (ret < 0) {
|
||||
quota_error(dquot->dq_sb, "Can't read tree quota "
|
||||
"block %u", *treeblk);
|
||||
"block %u", blks[depth]);
|
||||
goto out_buf;
|
||||
}
|
||||
}
|
||||
@@ -357,8 +372,20 @@ static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
|
||||
info->dqi_blocks - 1);
|
||||
if (ret)
|
||||
goto out_buf;
|
||||
if (!newblk)
|
||||
if (!newblk) {
|
||||
newson = 1;
|
||||
} else {
|
||||
for (i = 0; i <= depth; i++)
|
||||
if (newblk == blks[i]) {
|
||||
quota_error(dquot->dq_sb,
|
||||
"Cycle in quota tree detected: block %u index %u",
|
||||
blks[depth],
|
||||
get_index(info, dquot->dq_id, depth));
|
||||
ret = -EIO;
|
||||
goto out_buf;
|
||||
}
|
||||
}
|
||||
blks[depth + 1] = newblk;
|
||||
if (depth == info->dqi_qtree_depth - 1) {
|
||||
#ifdef __QUOTA_QT_PARANOIA
|
||||
if (newblk) {
|
||||
@@ -370,16 +397,16 @@ static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
|
||||
goto out_buf;
|
||||
}
|
||||
#endif
|
||||
newblk = find_free_dqentry(info, dquot, &ret);
|
||||
blks[depth + 1] = find_free_dqentry(info, dquot, &ret);
|
||||
} else {
|
||||
ret = do_insert_tree(info, dquot, &newblk, depth+1);
|
||||
ret = do_insert_tree(info, dquot, blks, depth + 1);
|
||||
}
|
||||
if (newson && ret >= 0) {
|
||||
ref[get_index(info, dquot->dq_id, depth)] =
|
||||
cpu_to_le32(newblk);
|
||||
ret = write_blk(info, *treeblk, buf);
|
||||
cpu_to_le32(blks[depth + 1]);
|
||||
ret = write_blk(info, blks[depth], buf);
|
||||
} else if (newact && ret < 0) {
|
||||
put_free_dqblk(info, buf, *treeblk);
|
||||
put_free_dqblk(info, buf, blks[depth]);
|
||||
}
|
||||
out_buf:
|
||||
kfree(buf);
|
||||
@@ -390,7 +417,7 @@ out_buf:
|
||||
static inline int dq_insert_tree(struct qtree_mem_dqinfo *info,
|
||||
struct dquot *dquot)
|
||||
{
|
||||
int tmp = QT_TREEOFF;
|
||||
uint blks[MAX_QTREE_DEPTH] = { QT_TREEOFF };
|
||||
|
||||
#ifdef __QUOTA_QT_PARANOIA
|
||||
if (info->dqi_blocks <= QT_TREEOFF) {
|
||||
@@ -398,7 +425,11 @@ static inline int dq_insert_tree(struct qtree_mem_dqinfo *info,
|
||||
return -EIO;
|
||||
}
|
||||
#endif
|
||||
return do_insert_tree(info, dquot, &tmp, 0);
|
||||
if (info->dqi_qtree_depth >= MAX_QTREE_DEPTH) {
|
||||
quota_error(dquot->dq_sb, "Quota tree depth too big!");
|
||||
return -EIO;
|
||||
}
|
||||
return do_insert_tree(info, dquot, blks, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -410,7 +441,7 @@ int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
|
||||
int type = dquot->dq_id.type;
|
||||
struct super_block *sb = dquot->dq_sb;
|
||||
ssize_t ret;
|
||||
char *ddquot = kmalloc(info->dqi_entry_size, GFP_NOFS);
|
||||
char *ddquot = kmalloc(info->dqi_entry_size, GFP_KERNEL);
|
||||
|
||||
if (!ddquot)
|
||||
return -ENOMEM;
|
||||
@@ -449,7 +480,7 @@ static int free_dqentry(struct qtree_mem_dqinfo *info, struct dquot *dquot,
|
||||
uint blk)
|
||||
{
|
||||
struct qt_disk_dqdbheader *dh;
|
||||
char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
|
||||
char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
|
||||
int ret = 0;
|
||||
|
||||
if (!buf)
|
||||
@@ -511,19 +542,20 @@ out_buf:
|
||||
|
||||
/* Remove reference to dquot from tree */
|
||||
static int remove_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
|
||||
uint *blk, int depth)
|
||||
uint *blks, int depth)
|
||||
{
|
||||
char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
|
||||
char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
|
||||
int ret = 0;
|
||||
uint newblk;
|
||||
__le32 *ref = (__le32 *)buf;
|
||||
int i;
|
||||
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
ret = read_blk(info, *blk, buf);
|
||||
ret = read_blk(info, blks[depth], buf);
|
||||
if (ret < 0) {
|
||||
quota_error(dquot->dq_sb, "Can't read quota data block %u",
|
||||
*blk);
|
||||
blks[depth]);
|
||||
goto out_buf;
|
||||
}
|
||||
newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]);
|
||||
@@ -532,29 +564,38 @@ static int remove_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
|
||||
if (ret)
|
||||
goto out_buf;
|
||||
|
||||
for (i = 0; i <= depth; i++)
|
||||
if (newblk == blks[i]) {
|
||||
quota_error(dquot->dq_sb,
|
||||
"Cycle in quota tree detected: block %u index %u",
|
||||
blks[depth],
|
||||
get_index(info, dquot->dq_id, depth));
|
||||
ret = -EIO;
|
||||
goto out_buf;
|
||||
}
|
||||
if (depth == info->dqi_qtree_depth - 1) {
|
||||
ret = free_dqentry(info, dquot, newblk);
|
||||
newblk = 0;
|
||||
blks[depth + 1] = 0;
|
||||
} else {
|
||||
ret = remove_tree(info, dquot, &newblk, depth+1);
|
||||
blks[depth + 1] = newblk;
|
||||
ret = remove_tree(info, dquot, blks, depth + 1);
|
||||
}
|
||||
if (ret >= 0 && !newblk) {
|
||||
int i;
|
||||
if (ret >= 0 && !blks[depth + 1]) {
|
||||
ref[get_index(info, dquot->dq_id, depth)] = cpu_to_le32(0);
|
||||
/* Block got empty? */
|
||||
for (i = 0; i < (info->dqi_usable_bs >> 2) && !ref[i]; i++)
|
||||
;
|
||||
/* Don't put the root block into the free block list */
|
||||
if (i == (info->dqi_usable_bs >> 2)
|
||||
&& *blk != QT_TREEOFF) {
|
||||
put_free_dqblk(info, buf, *blk);
|
||||
*blk = 0;
|
||||
&& blks[depth] != QT_TREEOFF) {
|
||||
put_free_dqblk(info, buf, blks[depth]);
|
||||
blks[depth] = 0;
|
||||
} else {
|
||||
ret = write_blk(info, *blk, buf);
|
||||
ret = write_blk(info, blks[depth], buf);
|
||||
if (ret < 0)
|
||||
quota_error(dquot->dq_sb,
|
||||
"Can't write quota tree block %u",
|
||||
*blk);
|
||||
blks[depth]);
|
||||
}
|
||||
}
|
||||
out_buf:
|
||||
@@ -565,11 +606,15 @@ out_buf:
|
||||
/* Delete dquot from tree */
|
||||
int qtree_delete_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
|
||||
{
|
||||
uint tmp = QT_TREEOFF;
|
||||
uint blks[MAX_QTREE_DEPTH] = { QT_TREEOFF };
|
||||
|
||||
if (!dquot->dq_off) /* Even not allocated? */
|
||||
return 0;
|
||||
return remove_tree(info, dquot, &tmp, 0);
|
||||
if (info->dqi_qtree_depth >= MAX_QTREE_DEPTH) {
|
||||
quota_error(dquot->dq_sb, "Quota tree depth too big!");
|
||||
return -EIO;
|
||||
}
|
||||
return remove_tree(info, dquot, blks, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(qtree_delete_dquot);
|
||||
|
||||
@@ -577,7 +622,7 @@ EXPORT_SYMBOL(qtree_delete_dquot);
|
||||
static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info,
|
||||
struct dquot *dquot, uint blk)
|
||||
{
|
||||
char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
|
||||
char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
|
||||
loff_t ret = 0;
|
||||
int i;
|
||||
char *ddquot;
|
||||
@@ -613,18 +658,20 @@ out_buf:
|
||||
|
||||
/* Find entry for given id in the tree */
|
||||
static loff_t find_tree_dqentry(struct qtree_mem_dqinfo *info,
|
||||
struct dquot *dquot, uint blk, int depth)
|
||||
struct dquot *dquot, uint *blks, int depth)
|
||||
{
|
||||
char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
|
||||
char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
|
||||
loff_t ret = 0;
|
||||
__le32 *ref = (__le32 *)buf;
|
||||
uint blk;
|
||||
int i;
|
||||
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
ret = read_blk(info, blk, buf);
|
||||
ret = read_blk(info, blks[depth], buf);
|
||||
if (ret < 0) {
|
||||
quota_error(dquot->dq_sb, "Can't read quota tree block %u",
|
||||
blk);
|
||||
blks[depth]);
|
||||
goto out_buf;
|
||||
}
|
||||
ret = 0;
|
||||
@@ -636,8 +683,19 @@ static loff_t find_tree_dqentry(struct qtree_mem_dqinfo *info,
|
||||
if (ret)
|
||||
goto out_buf;
|
||||
|
||||
/* Check for cycles in the tree */
|
||||
for (i = 0; i <= depth; i++)
|
||||
if (blk == blks[i]) {
|
||||
quota_error(dquot->dq_sb,
|
||||
"Cycle in quota tree detected: block %u index %u",
|
||||
blks[depth],
|
||||
get_index(info, dquot->dq_id, depth));
|
||||
ret = -EIO;
|
||||
goto out_buf;
|
||||
}
|
||||
blks[depth + 1] = blk;
|
||||
if (depth < info->dqi_qtree_depth - 1)
|
||||
ret = find_tree_dqentry(info, dquot, blk, depth+1);
|
||||
ret = find_tree_dqentry(info, dquot, blks, depth + 1);
|
||||
else
|
||||
ret = find_block_dqentry(info, dquot, blk);
|
||||
out_buf:
|
||||
@@ -649,7 +707,13 @@ out_buf:
|
||||
static inline loff_t find_dqentry(struct qtree_mem_dqinfo *info,
|
||||
struct dquot *dquot)
|
||||
{
|
||||
return find_tree_dqentry(info, dquot, QT_TREEOFF, 0);
|
||||
uint blks[MAX_QTREE_DEPTH] = { QT_TREEOFF };
|
||||
|
||||
if (info->dqi_qtree_depth >= MAX_QTREE_DEPTH) {
|
||||
quota_error(dquot->dq_sb, "Quota tree depth too big!");
|
||||
return -EIO;
|
||||
}
|
||||
return find_tree_dqentry(info, dquot, blks, 0);
|
||||
}
|
||||
|
||||
int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
|
||||
@@ -684,7 +748,7 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
|
||||
}
|
||||
dquot->dq_off = offset;
|
||||
}
|
||||
ddquot = kmalloc(info->dqi_entry_size, GFP_NOFS);
|
||||
ddquot = kmalloc(info->dqi_entry_size, GFP_KERNEL);
|
||||
if (!ddquot)
|
||||
return -ENOMEM;
|
||||
ret = sb->s_op->quota_read(sb, type, ddquot, info->dqi_entry_size,
|
||||
@@ -728,7 +792,7 @@ EXPORT_SYMBOL(qtree_release_dquot);
|
||||
static int find_next_id(struct qtree_mem_dqinfo *info, qid_t *id,
|
||||
unsigned int blk, int depth)
|
||||
{
|
||||
char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
|
||||
char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
|
||||
__le32 *ref = (__le32 *)buf;
|
||||
ssize_t ret;
|
||||
unsigned int epb = info->dqi_usable_bs >> 2;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user