Commit f55abc0f authored by Jan Kara's avatar Jan Kara Committed by Mark Fasheh

quota: Allow to separately enable quota accounting and enforcing limits

Split DQUOT_USR_ENABLED (and DQUOT_GRP_ENABLED) into DQUOT_USR_USAGE_ENABLED
and DQUOT_USR_LIMITS_ENABLED. This way we are able to separately enable /
disable whether we should:
1) ignore quotas completely
2) just keep uptodate information about usage
3) actually enforce quota limits

This is going to be useful when quota is treated as filesystem metadata - we
then want to keep quota information uptodate all the time and just enable /
disable limits enforcement.
Signed-off-by: default avatarJan Kara <jack@suse.cz>
Signed-off-by: default avatarMark Fasheh <mfasheh@suse.com>
parent e4bc7b4b
...@@ -489,7 +489,7 @@ int vfs_quota_sync(struct super_block *sb, int type) ...@@ -489,7 +489,7 @@ int vfs_quota_sync(struct super_block *sb, int type)
for (cnt = 0; cnt < MAXQUOTAS; cnt++) { for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
if (type != -1 && cnt != type) if (type != -1 && cnt != type)
continue; continue;
if (!sb_has_quota_enabled(sb, cnt)) if (!sb_has_quota_active(sb, cnt))
continue; continue;
spin_lock(&dq_list_lock); spin_lock(&dq_list_lock);
dirty = &dqopt->info[cnt].dqi_dirty_list; dirty = &dqopt->info[cnt].dqi_dirty_list;
...@@ -514,7 +514,7 @@ int vfs_quota_sync(struct super_block *sb, int type) ...@@ -514,7 +514,7 @@ int vfs_quota_sync(struct super_block *sb, int type)
} }
for (cnt = 0; cnt < MAXQUOTAS; cnt++) for (cnt = 0; cnt < MAXQUOTAS; cnt++)
if ((cnt == type || type == -1) && sb_has_quota_enabled(sb, cnt) if ((cnt == type || type == -1) && sb_has_quota_active(sb, cnt)
&& info_dirty(&dqopt->info[cnt])) && info_dirty(&dqopt->info[cnt]))
sb->dq_op->write_info(sb, cnt); sb->dq_op->write_info(sb, cnt);
spin_lock(&dq_list_lock); spin_lock(&dq_list_lock);
...@@ -594,7 +594,7 @@ static void dqput(struct dquot *dquot) ...@@ -594,7 +594,7 @@ static void dqput(struct dquot *dquot)
/* We have more than one user... nothing to do */ /* We have more than one user... nothing to do */
atomic_dec(&dquot->dq_count); atomic_dec(&dquot->dq_count);
/* Releasing dquot during quotaoff phase? */ /* Releasing dquot during quotaoff phase? */
if (!sb_has_quota_enabled(dquot->dq_sb, dquot->dq_type) && if (!sb_has_quota_active(dquot->dq_sb, dquot->dq_type) &&
atomic_read(&dquot->dq_count) == 1) atomic_read(&dquot->dq_count) == 1)
wake_up(&dquot->dq_wait_unused); wake_up(&dquot->dq_wait_unused);
spin_unlock(&dq_list_lock); spin_unlock(&dq_list_lock);
...@@ -670,7 +670,7 @@ static struct dquot *dqget(struct super_block *sb, unsigned int id, int type) ...@@ -670,7 +670,7 @@ static struct dquot *dqget(struct super_block *sb, unsigned int id, int type)
unsigned int hashent = hashfn(sb, id, type); unsigned int hashent = hashfn(sb, id, type);
struct dquot *dquot, *empty = NODQUOT; struct dquot *dquot, *empty = NODQUOT;
if (!sb_has_quota_enabled(sb, type)) if (!sb_has_quota_active(sb, type))
return NODQUOT; return NODQUOT;
we_slept: we_slept:
spin_lock(&dq_list_lock); spin_lock(&dq_list_lock);
...@@ -1041,7 +1041,8 @@ static inline char ignore_hardlimit(struct dquot *dquot) ...@@ -1041,7 +1041,8 @@ static inline char ignore_hardlimit(struct dquot *dquot)
static int check_idq(struct dquot *dquot, qsize_t inodes, char *warntype) static int check_idq(struct dquot *dquot, qsize_t inodes, char *warntype)
{ {
*warntype = QUOTA_NL_NOWARN; *warntype = QUOTA_NL_NOWARN;
if (test_bit(DQ_FAKE_B, &dquot->dq_flags)) if (!sb_has_quota_limits_enabled(dquot->dq_sb, dquot->dq_type) ||
test_bit(DQ_FAKE_B, &dquot->dq_flags))
return QUOTA_OK; return QUOTA_OK;
if (dquot->dq_dqb.dqb_ihardlimit && if (dquot->dq_dqb.dqb_ihardlimit &&
...@@ -1073,7 +1074,8 @@ static int check_idq(struct dquot *dquot, qsize_t inodes, char *warntype) ...@@ -1073,7 +1074,8 @@ static int check_idq(struct dquot *dquot, qsize_t inodes, char *warntype)
static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *warntype) static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *warntype)
{ {
*warntype = QUOTA_NL_NOWARN; *warntype = QUOTA_NL_NOWARN;
if (test_bit(DQ_FAKE_B, &dquot->dq_flags)) if (!sb_has_quota_limits_enabled(dquot->dq_sb, dquot->dq_type) ||
test_bit(DQ_FAKE_B, &dquot->dq_flags))
return QUOTA_OK; return QUOTA_OK;
if (dquot->dq_dqb.dqb_bhardlimit && if (dquot->dq_dqb.dqb_bhardlimit &&
...@@ -1114,7 +1116,8 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *war ...@@ -1114,7 +1116,8 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *war
static int info_idq_free(struct dquot *dquot, qsize_t inodes) static int info_idq_free(struct dquot *dquot, qsize_t inodes)
{ {
if (test_bit(DQ_FAKE_B, &dquot->dq_flags) || if (test_bit(DQ_FAKE_B, &dquot->dq_flags) ||
dquot->dq_dqb.dqb_curinodes <= dquot->dq_dqb.dqb_isoftlimit) dquot->dq_dqb.dqb_curinodes <= dquot->dq_dqb.dqb_isoftlimit ||
!sb_has_quota_limits_enabled(dquot->dq_sb, dquot->dq_type))
return QUOTA_NL_NOWARN; return QUOTA_NL_NOWARN;
if (dquot->dq_dqb.dqb_curinodes - inodes <= dquot->dq_dqb.dqb_isoftlimit) if (dquot->dq_dqb.dqb_curinodes - inodes <= dquot->dq_dqb.dqb_isoftlimit)
...@@ -1508,7 +1511,7 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr) ...@@ -1508,7 +1511,7 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr)
/* Wrapper for transferring ownership of an inode */ /* Wrapper for transferring ownership of an inode */
int vfs_dq_transfer(struct inode *inode, struct iattr *iattr) int vfs_dq_transfer(struct inode *inode, struct iattr *iattr)
{ {
if (sb_any_quota_enabled(inode->i_sb) && !IS_NOQUOTA(inode)) { if (sb_any_quota_active(inode->i_sb) && !IS_NOQUOTA(inode)) {
vfs_dq_init(inode); vfs_dq_init(inode);
if (inode->i_sb->dq_op->transfer(inode, iattr) == NO_QUOTA) if (inode->i_sb->dq_op->transfer(inode, iattr) == NO_QUOTA)
return 1; return 1;
...@@ -1551,53 +1554,22 @@ struct dquot_operations dquot_operations = { ...@@ -1551,53 +1554,22 @@ struct dquot_operations dquot_operations = {
.destroy_dquot = dquot_destroy, .destroy_dquot = dquot_destroy,
}; };
static inline void set_enable_flags(struct quota_info *dqopt, int type)
{
switch (type) {
case USRQUOTA:
dqopt->flags |= DQUOT_USR_ENABLED;
dqopt->flags &= ~DQUOT_USR_SUSPENDED;
break;
case GRPQUOTA:
dqopt->flags |= DQUOT_GRP_ENABLED;
dqopt->flags &= ~DQUOT_GRP_SUSPENDED;
break;
}
}
static inline void reset_enable_flags(struct quota_info *dqopt, int type,
int remount)
{
switch (type) {
case USRQUOTA:
if (remount)
dqopt->flags |= DQUOT_USR_SUSPENDED;
else {
dqopt->flags &= ~DQUOT_USR_ENABLED;
dqopt->flags &= ~DQUOT_USR_SUSPENDED;
}
break;
case GRPQUOTA:
if (remount)
dqopt->flags |= DQUOT_GRP_SUSPENDED;
else {
dqopt->flags &= ~DQUOT_GRP_ENABLED;
dqopt->flags &= ~DQUOT_GRP_SUSPENDED;
}
break;
}
}
/* /*
* Turn quota off on a device. type == -1 ==> quotaoff for all types (umount) * Turn quota off on a device. type == -1 ==> quotaoff for all types (umount)
*/ */
int vfs_quota_off(struct super_block *sb, int type, int remount) int vfs_quota_disable(struct super_block *sb, int type, unsigned int flags)
{ {
int cnt, ret = 0; int cnt, ret = 0;
struct quota_info *dqopt = sb_dqopt(sb); struct quota_info *dqopt = sb_dqopt(sb);
struct inode *toputinode[MAXQUOTAS]; struct inode *toputinode[MAXQUOTAS];
/* Cannot turn off usage accounting without turning off limits, or
* suspend quotas and simultaneously turn quotas off. */
if ((flags & DQUOT_USAGE_ENABLED && !(flags & DQUOT_LIMITS_ENABLED))
|| (flags & DQUOT_SUSPENDED && flags & (DQUOT_LIMITS_ENABLED |
DQUOT_USAGE_ENABLED)))
return -EINVAL;
/* We need to serialize quota_off() for device */ /* We need to serialize quota_off() for device */
mutex_lock(&dqopt->dqonoff_mutex); mutex_lock(&dqopt->dqonoff_mutex);
...@@ -1606,7 +1578,7 @@ int vfs_quota_off(struct super_block *sb, int type, int remount) ...@@ -1606,7 +1578,7 @@ int vfs_quota_off(struct super_block *sb, int type, int remount)
* sometimes we are called when fill_super() failed and calling * sometimes we are called when fill_super() failed and calling
* sync_fs() in such cases does no good. * sync_fs() in such cases does no good.
*/ */
if (!sb_any_quota_enabled(sb) && !sb_any_quota_suspended(sb)) { if (!sb_any_quota_loaded(sb)) {
mutex_unlock(&dqopt->dqonoff_mutex); mutex_unlock(&dqopt->dqonoff_mutex);
return 0; return 0;
} }
...@@ -1614,17 +1586,28 @@ int vfs_quota_off(struct super_block *sb, int type, int remount) ...@@ -1614,17 +1586,28 @@ int vfs_quota_off(struct super_block *sb, int type, int remount)
toputinode[cnt] = NULL; toputinode[cnt] = NULL;
if (type != -1 && cnt != type) if (type != -1 && cnt != type)
continue; continue;
/* If we keep inodes of quota files after remount and quotaoff if (!sb_has_quota_loaded(sb, cnt))
* is called, drop kept inodes. */ continue;
if (!remount && sb_has_quota_suspended(sb, cnt)) {
if (flags & DQUOT_SUSPENDED) {
dqopt->flags |=
dquot_state_flag(DQUOT_SUSPENDED, cnt);
} else {
dqopt->flags &= ~dquot_state_flag(flags, cnt);
/* Turning off suspended quotas? */
if (!sb_has_quota_loaded(sb, cnt) &&
sb_has_quota_suspended(sb, cnt)) {
dqopt->flags &= ~dquot_state_flag(
DQUOT_SUSPENDED, cnt);
iput(dqopt->files[cnt]); iput(dqopt->files[cnt]);
dqopt->files[cnt] = NULL; dqopt->files[cnt] = NULL;
reset_enable_flags(dqopt, cnt, 0);
continue; continue;
} }
if (!sb_has_quota_enabled(sb, cnt)) }
/* We still have to keep quota loaded? */
if (sb_has_quota_loaded(sb, cnt) && !(flags & DQUOT_SUSPENDED))
continue; continue;
reset_enable_flags(dqopt, cnt, remount);
/* Note: these are blocking operations */ /* Note: these are blocking operations */
drop_dquot_ref(sb, cnt); drop_dquot_ref(sb, cnt);
...@@ -1640,7 +1623,7 @@ int vfs_quota_off(struct super_block *sb, int type, int remount) ...@@ -1640,7 +1623,7 @@ int vfs_quota_off(struct super_block *sb, int type, int remount)
put_quota_format(dqopt->info[cnt].dqi_format); put_quota_format(dqopt->info[cnt].dqi_format);
toputinode[cnt] = dqopt->files[cnt]; toputinode[cnt] = dqopt->files[cnt];
if (!remount) if (!sb_has_quota_loaded(sb, cnt))
dqopt->files[cnt] = NULL; dqopt->files[cnt] = NULL;
dqopt->info[cnt].dqi_flags = 0; dqopt->info[cnt].dqi_flags = 0;
dqopt->info[cnt].dqi_igrace = 0; dqopt->info[cnt].dqi_igrace = 0;
...@@ -1663,7 +1646,7 @@ int vfs_quota_off(struct super_block *sb, int type, int remount) ...@@ -1663,7 +1646,7 @@ int vfs_quota_off(struct super_block *sb, int type, int remount)
mutex_lock(&dqopt->dqonoff_mutex); mutex_lock(&dqopt->dqonoff_mutex);
/* If quota was reenabled in the meantime, we have /* If quota was reenabled in the meantime, we have
* nothing to do */ * nothing to do */
if (!sb_has_quota_enabled(sb, cnt)) { if (!sb_has_quota_loaded(sb, cnt)) {
mutex_lock_nested(&toputinode[cnt]->i_mutex, I_MUTEX_QUOTA); mutex_lock_nested(&toputinode[cnt]->i_mutex, I_MUTEX_QUOTA);
toputinode[cnt]->i_flags &= ~(S_IMMUTABLE | toputinode[cnt]->i_flags &= ~(S_IMMUTABLE |
S_NOATIME | S_NOQUOTA); S_NOATIME | S_NOQUOTA);
...@@ -1673,10 +1656,13 @@ int vfs_quota_off(struct super_block *sb, int type, int remount) ...@@ -1673,10 +1656,13 @@ int vfs_quota_off(struct super_block *sb, int type, int remount)
} }
mutex_unlock(&dqopt->dqonoff_mutex); mutex_unlock(&dqopt->dqonoff_mutex);
/* On remount RO, we keep the inode pointer so that we /* On remount RO, we keep the inode pointer so that we
* can reenable quota on the subsequent remount RW. * can reenable quota on the subsequent remount RW. We
* But we have better not keep inode pointer when there * have to check 'flags' variable and not use sb_has_
* is pending delete on the quota file... */ * function because another quotaon / quotaoff could
if (!remount) * change global state before we got here. We refuse
* to suspend quotas when there is pending delete on
* the quota file... */
if (!(flags & DQUOT_SUSPENDED))
iput(toputinode[cnt]); iput(toputinode[cnt]);
else if (!toputinode[cnt]->i_nlink) else if (!toputinode[cnt]->i_nlink)
ret = -EBUSY; ret = -EBUSY;
...@@ -1686,12 +1672,22 @@ int vfs_quota_off(struct super_block *sb, int type, int remount) ...@@ -1686,12 +1672,22 @@ int vfs_quota_off(struct super_block *sb, int type, int remount)
return ret; return ret;
} }
int vfs_quota_off(struct super_block *sb, int type, int remount)
{
return vfs_quota_disable(sb, type, remount ? DQUOT_SUSPENDED :
(DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED));
}
/* /*
* Turn quotas on on a device * Turn quotas on on a device
*/ */
/* Helper function when we already have the inode */ /*
static int vfs_quota_on_inode(struct inode *inode, int type, int format_id) * Helper function to turn quotas on when we already have the inode of
* quota file and no quota information is loaded.
*/
static int vfs_load_quota_inode(struct inode *inode, int type, int format_id,
unsigned int flags)
{ {
struct quota_format_type *fmt = find_quota_format(format_id); struct quota_format_type *fmt = find_quota_format(format_id);
struct super_block *sb = inode->i_sb; struct super_block *sb = inode->i_sb;
...@@ -1713,6 +1709,11 @@ static int vfs_quota_on_inode(struct inode *inode, int type, int format_id) ...@@ -1713,6 +1709,11 @@ static int vfs_quota_on_inode(struct inode *inode, int type, int format_id)
error = -EINVAL; error = -EINVAL;
goto out_fmt; goto out_fmt;
} }
/* Usage always has to be set... */
if (!(flags & DQUOT_USAGE_ENABLED)) {
error = -EINVAL;
goto out_fmt;
}
/* As we bypass the pagecache we must now flush the inode so that /* As we bypass the pagecache we must now flush the inode so that
* we see all the changes from userspace... */ * we see all the changes from userspace... */
...@@ -1721,8 +1722,7 @@ static int vfs_quota_on_inode(struct inode *inode, int type, int format_id) ...@@ -1721,8 +1722,7 @@ static int vfs_quota_on_inode(struct inode *inode, int type, int format_id)
invalidate_bdev(sb->s_bdev); invalidate_bdev(sb->s_bdev);
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
mutex_lock(&dqopt->dqonoff_mutex); mutex_lock(&dqopt->dqonoff_mutex);
if (sb_has_quota_enabled(sb, type) || if (sb_has_quota_loaded(sb, type)) {
sb_has_quota_suspended(sb, type)) {
error = -EBUSY; error = -EBUSY;
goto out_lock; goto out_lock;
} }
...@@ -1754,7 +1754,7 @@ static int vfs_quota_on_inode(struct inode *inode, int type, int format_id) ...@@ -1754,7 +1754,7 @@ static int vfs_quota_on_inode(struct inode *inode, int type, int format_id)
} }
mutex_unlock(&dqopt->dqio_mutex); mutex_unlock(&dqopt->dqio_mutex);
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
set_enable_flags(dqopt, type); dqopt->flags |= dquot_state_flag(flags, type);
add_dquot_ref(sb, type); add_dquot_ref(sb, type);
mutex_unlock(&dqopt->dqonoff_mutex); mutex_unlock(&dqopt->dqonoff_mutex);
...@@ -1787,20 +1787,23 @@ static int vfs_quota_on_remount(struct super_block *sb, int type) ...@@ -1787,20 +1787,23 @@ static int vfs_quota_on_remount(struct super_block *sb, int type)
struct quota_info *dqopt = sb_dqopt(sb); struct quota_info *dqopt = sb_dqopt(sb);
struct inode *inode; struct inode *inode;
int ret; int ret;
unsigned int flags;
mutex_lock(&dqopt->dqonoff_mutex); mutex_lock(&dqopt->dqonoff_mutex);
if (!sb_has_quota_suspended(sb, type)) { if (!sb_has_quota_suspended(sb, type)) {
mutex_unlock(&dqopt->dqonoff_mutex); mutex_unlock(&dqopt->dqonoff_mutex);
return 0; return 0;
} }
BUG_ON(sb_has_quota_enabled(sb, type));
inode = dqopt->files[type]; inode = dqopt->files[type];
dqopt->files[type] = NULL; dqopt->files[type] = NULL;
reset_enable_flags(dqopt, type, 0); flags = dqopt->flags & dquot_state_flag(DQUOT_USAGE_ENABLED |
DQUOT_LIMITS_ENABLED, type);
dqopt->flags &= ~dquot_state_flag(DQUOT_STATE_FLAGS, type);
mutex_unlock(&dqopt->dqonoff_mutex); mutex_unlock(&dqopt->dqonoff_mutex);
ret = vfs_quota_on_inode(inode, type, dqopt->info[type].dqi_fmt_id); flags = dquot_generic_flag(flags, type);
ret = vfs_load_quota_inode(inode, type, dqopt->info[type].dqi_fmt_id,
flags);
iput(inode); iput(inode);
return ret; return ret;
...@@ -1816,12 +1819,12 @@ int vfs_quota_on_path(struct super_block *sb, int type, int format_id, ...@@ -1816,12 +1819,12 @@ int vfs_quota_on_path(struct super_block *sb, int type, int format_id,
if (path->mnt->mnt_sb != sb) if (path->mnt->mnt_sb != sb)
error = -EXDEV; error = -EXDEV;
else else
error = vfs_quota_on_inode(path->dentry->d_inode, type, error = vfs_load_quota_inode(path->dentry->d_inode, type,
format_id); format_id, DQUOT_USAGE_ENABLED |
DQUOT_LIMITS_ENABLED);
return error; return error;
} }
/* Actual function called from quotactl() */
int vfs_quota_on(struct super_block *sb, int type, int format_id, char *name, int vfs_quota_on(struct super_block *sb, int type, int format_id, char *name,
int remount) int remount)
{ {
...@@ -1839,6 +1842,50 @@ int vfs_quota_on(struct super_block *sb, int type, int format_id, char *name, ...@@ -1839,6 +1842,50 @@ int vfs_quota_on(struct super_block *sb, int type, int format_id, char *name,
return error; return error;
} }
/*
* More powerful function for turning on quotas allowing setting
* of individual quota flags
*/
int vfs_quota_enable(struct inode *inode, int type, int format_id,
unsigned int flags)
{
int ret = 0;
struct super_block *sb = inode->i_sb;
struct quota_info *dqopt = sb_dqopt(sb);
/* Just unsuspend quotas? */
if (flags & DQUOT_SUSPENDED)
return vfs_quota_on_remount(sb, type);
if (!flags)
return 0;
/* Just updating flags needed? */
if (sb_has_quota_loaded(sb, type)) {
mutex_lock(&dqopt->dqonoff_mutex);
/* Now do a reliable test... */
if (!sb_has_quota_loaded(sb, type)) {
mutex_unlock(&dqopt->dqonoff_mutex);
goto load_quota;
}
if (flags & DQUOT_USAGE_ENABLED &&
sb_has_quota_usage_enabled(sb, type)) {
ret = -EBUSY;
goto out_lock;
}
if (flags & DQUOT_LIMITS_ENABLED &&
sb_has_quota_limits_enabled(sb, type)) {
ret = -EBUSY;
goto out_lock;
}
sb_dqopt(sb)->flags |= dquot_state_flag(flags, type);
out_lock:
mutex_unlock(&dqopt->dqonoff_mutex);
return ret;
}
load_quota:
return vfs_load_quota_inode(inode, type, format_id, flags);
}
/* /*
* This function is used when filesystem needs to initialize quotas * This function is used when filesystem needs to initialize quotas
* during mount time. * during mount time.
...@@ -1860,7 +1907,8 @@ int vfs_quota_on_mount(struct super_block *sb, char *qf_name, ...@@ -1860,7 +1907,8 @@ int vfs_quota_on_mount(struct super_block *sb, char *qf_name,
error = security_quota_on(dentry); error = security_quota_on(dentry);
if (!error) if (!error)
error = vfs_quota_on_inode(dentry->d_inode, type, format_id); error = vfs_load_quota_inode(dentry->d_inode, type, format_id,
DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED);
out: out:
dput(dentry); dput(dentry);
...@@ -1997,12 +2045,14 @@ int vfs_set_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *d ...@@ -1997,12 +2045,14 @@ int vfs_set_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *d
int rc; int rc;
mutex_lock(&sb_dqopt(sb)->dqonoff_mutex); mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
if (!(dquot = dqget(sb, id, type))) { dquot = dqget(sb, id, type);
mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex); if (!dquot) {
return -ESRCH; rc = -ESRCH;
goto out;
} }
rc = do_set_dqblk(dquot, di); rc = do_set_dqblk(dquot, di);
dqput(dquot); dqput(dquot);
out:
mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex); mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
return rc; return rc;
} }
...@@ -2013,7 +2063,7 @@ int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii) ...@@ -2013,7 +2063,7 @@ int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
struct mem_dqinfo *mi; struct mem_dqinfo *mi;
mutex_lock(&sb_dqopt(sb)->dqonoff_mutex); mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
if (!sb_has_quota_enabled(sb, type)) { if (!sb_has_quota_active(sb, type)) {
mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex); mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
return -ESRCH; return -ESRCH;
} }
...@@ -2032,11 +2082,12 @@ int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii) ...@@ -2032,11 +2082,12 @@ int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii) int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
{ {
struct mem_dqinfo *mi; struct mem_dqinfo *mi;
int err = 0;
mutex_lock(&sb_dqopt(sb)->dqonoff_mutex); mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
if (!sb_has_quota_enabled(sb, type)) { if (!sb_has_quota_active(sb, type)) {
mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex); err = -ESRCH;
return -ESRCH; goto out;
} }
mi = sb_dqopt(sb)->info + type; mi = sb_dqopt(sb)->info + type;
spin_lock(&dq_data_lock); spin_lock(&dq_data_lock);
...@@ -2050,8 +2101,9 @@ int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii) ...@@ -2050,8 +2101,9 @@ int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
mark_info_dirty(sb, type); mark_info_dirty(sb, type);
/* Force write to disk */ /* Force write to disk */
sb->dq_op->write_info(sb, type); sb->dq_op->write_info(sb, type);
out:
mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex); mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
return 0; return err;
} }
struct quotactl_ops vfs_quotactl_ops = { struct quotactl_ops vfs_quotactl_ops = {
...@@ -2213,9 +2265,11 @@ EXPORT_SYMBOL(register_quota_format); ...@@ -2213,9 +2265,11 @@ EXPORT_SYMBOL(register_quota_format);
EXPORT_SYMBOL(unregister_quota_format); EXPORT_SYMBOL(unregister_quota_format);
EXPORT_SYMBOL(dqstats); EXPORT_SYMBOL(dqstats);
EXPORT_SYMBOL(dq_data_lock); EXPORT_SYMBOL(dq_data_lock);
EXPORT_SYMBOL(vfs_quota_enable);
EXPORT_SYMBOL(vfs_quota_on); EXPORT_SYMBOL(vfs_quota_on);
EXPORT_SYMBOL(vfs_quota_on_path); EXPORT_SYMBOL(vfs_quota_on_path);
EXPORT_SYMBOL(vfs_quota_on_mount); EXPORT_SYMBOL(vfs_quota_on_mount);
EXPORT_SYMBOL(vfs_quota_disable);
EXPORT_SYMBOL(vfs_quota_off); EXPORT_SYMBOL(vfs_quota_off);
EXPORT_SYMBOL(vfs_quota_sync); EXPORT_SYMBOL(vfs_quota_sync);
EXPORT_SYMBOL(vfs_get_dqinfo); EXPORT_SYMBOL(vfs_get_dqinfo);
......
...@@ -73,7 +73,7 @@ static int generic_quotactl_valid(struct super_block *sb, int type, int cmd, qid ...@@ -73,7 +73,7 @@ static int generic_quotactl_valid(struct super_block *sb, int type, int cmd, qid
case Q_SETQUOTA: case Q_SETQUOTA:
case Q_GETQUOTA: case Q_GETQUOTA:
/* This is just informative test so we are satisfied without a lock */ /* This is just informative test so we are satisfied without a lock */
if (!sb_has_quota_enabled(sb, type)) if (!sb_has_quota_active(sb, type))
return -ESRCH; return -ESRCH;
} }
...@@ -175,7 +175,7 @@ static void quota_sync_sb(struct super_block *sb, int type) ...@@ -175,7 +175,7 @@ static void quota_sync_sb(struct super_block *sb, int type)
for (cnt = 0; cnt < MAXQUOTAS; cnt++) { for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
if (type != -1 && cnt != type) if (type != -1 && cnt != type)
continue; continue;
if (!sb_has_quota_enabled(sb, cnt)) if (!sb_has_quota_active(sb, cnt))
continue; continue;
mutex_lock_nested(&sb_dqopt(sb)->files[cnt]->i_mutex, I_MUTEX_QUOTA); mutex_lock_nested(&sb_dqopt(sb)->files[cnt]->i_mutex, I_MUTEX_QUOTA);
truncate_inode_pages(&sb_dqopt(sb)->files[cnt]->i_data, 0); truncate_inode_pages(&sb_dqopt(sb)->files[cnt]->i_data, 0);
...@@ -201,7 +201,7 @@ void sync_dquots(struct super_block *sb, int type) ...@@ -201,7 +201,7 @@ void sync_dquots(struct super_block *sb, int type)
for (cnt = 0; cnt < MAXQUOTAS; cnt++) { for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
if (type != -1 && type != cnt) if (type != -1 && type != cnt)
continue; continue;
if (!sb_has_quota_enabled(sb, cnt)) if (!sb_has_quota_active(sb, cnt))
continue; continue;
if (!info_dirty(&sb_dqopt(sb)->info[cnt]) && if (!info_dirty(&sb_dqopt(sb)->info[cnt]) &&
list_empty(&sb_dqopt(sb)->info[cnt].dqi_dirty_list)) list_empty(&sb_dqopt(sb)->info[cnt].dqi_dirty_list))
...@@ -245,7 +245,7 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, void ...@@ -245,7 +245,7 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, void
__u32 fmt; __u32 fmt;
down_read(&sb_dqopt(sb)->dqptr_sem); down_read(&sb_dqopt(sb)->dqptr_sem);
if (!sb_has_quota_enabled(sb, type)) { if (!sb_has_quota_active(sb, type)) {
up_read(&sb_dqopt(sb)->dqptr_sem); up_read(&sb_dqopt(sb)->dqptr_sem);
return -ESRCH; return -ESRCH;
} }
......
...@@ -318,12 +318,34 @@ struct quota_format_type { ...@@ -318,12 +318,34 @@ struct quota_format_type {
struct quota_format_type *qf_next; struct quota_format_type *qf_next;
}; };
#define DQUOT_USR_ENABLED 0x01 /* User diskquotas enabled */ /* Quota state flags - they actually come in two flavors - for users and groups */
#define DQUOT_GRP_ENABLED 0x02 /* Group diskquotas enabled */ enum {
#define DQUOT_USR_SUSPENDED 0x04 /* User diskquotas are off, but _DQUOT_USAGE_ENABLED = 0, /* Track disk usage for users */
_DQUOT_LIMITS_ENABLED, /* Enforce quota limits for users */
_DQUOT_SUSPENDED, /* User diskquotas are off, but
* we have necessary info in * we have necessary info in
* memory to turn them on */ * memory to turn them on */
#define DQUOT_GRP_SUSPENDED 0x08 /* The same for group quotas */ _DQUOT_STATE_FLAGS
};
#define DQUOT_USAGE_ENABLED (1 << _DQUOT_USAGE_ENABLED)
#define DQUOT_LIMITS_ENABLED (1 << _DQUOT_LIMITS_ENABLED)
#define DQUOT_SUSPENDED (1 << _DQUOT_SUSPENDED)
#define DQUOT_STATE_FLAGS (DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED | \
DQUOT_SUSPENDED)
static inline unsigned int dquot_state_flag(unsigned int flags, int type)
{
if (type == USRQUOTA)
return flags;
return flags << _DQUOT_STATE_FLAGS;
}
static inline unsigned int dquot_generic_flag(unsigned int flags, int type)
{
if (type == USRQUOTA)
return flags;
return flags >> _DQUOT_STATE_FLAGS;
}
struct quota_info { struct quota_info {
unsigned int flags; /* Flags for diskquotas on this device */ unsigned int flags; /* Flags for diskquotas on this device */
......
...@@ -40,11 +40,14 @@ int dquot_mark_dquot_dirty(struct dquot *dquot); ...@@ -40,11 +40,14 @@ int dquot_mark_dquot_dirty(struct dquot *dquot);
int vfs_quota_on(struct super_block *sb, int type, int format_id, int vfs_quota_on(struct super_block *sb, int type, int format_id,
char *path, int remount); char *path, int remount);
int vfs_quota_enable(struct inode *inode, int type, int format_id,
unsigned int flags);
int vfs_quota_on_path(struct super_block *sb, int type, int format_id, int vfs_quota_on_path(struct super_block *sb, int type, int format_id,
struct path *path); struct path *path);
int vfs_quota_on_mount(struct super_block *sb, char *qf_name, int vfs_quota_on_mount(struct super_block *sb, char *qf_name,
int format_id, int type); int format_id, int type);
int vfs_quota_off(struct super_block *sb, int type, int remount); int vfs_quota_off(struct super_block *sb, int type, int remount);
int vfs_quota_disable(struct super_block *sb, int type, unsigned int flags);
int vfs_quota_sync(struct super_block *sb, int type); int vfs_quota_sync(struct super_block *sb, int type);
int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii); int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii);
int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii); int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii);
...@@ -64,26 +67,22 @@ static inline struct mem_dqinfo *sb_dqinfo(struct super_block *sb, int type) ...@@ -64,26 +67,22 @@ static inline struct mem_dqinfo *sb_dqinfo(struct super_block *sb, int type)
* Functions for checking status of quota * Functions for checking status of quota
*/ */
static inline int sb_has_quota_enabled(struct super_block *sb, int type) static inline int sb_has_quota_usage_enabled(struct super_block *sb, int type)
{ {
if (type == USRQUOTA) return sb_dqopt(sb)->flags &
return (sb_dqopt(sb)->flags & DQUOT_USR_ENABLED) dquot_state_flag(DQUOT_USAGE_ENABLED, type);
&& !(sb_dqopt(sb)->flags & DQUOT_USR_SUSPENDED);
return (sb_dqopt(sb)->flags & DQUOT_GRP_ENABLED)
&& !(sb_dqopt(sb)->flags & DQUOT_GROUP_SUSPENDED);
} }
static inline int sb_any_quota_enabled(struct super_block *sb) static inline int sb_has_quota_limits_enabled(struct super_block *sb, int type)
{ {
return sb_has_quota_enabled(sb, USRQUOTA) || return sb_dqopt(sb)->flags &
sb_has_quota_enabled(sb, GRPQUOTA); dquot_state_flag(DQUOT_LIMITS_ENABLED, type);
} }
static inline int sb_has_quota_suspended(struct super_block *sb, int type) static inline int sb_has_quota_suspended(struct super_block *sb, int type)
{ {
if (type == USRQUOTA) return sb_dqopt(sb)->flags &
return sb_dqopt(sb)->flags & DQUOT_USR_SUSPENDED; dquot_state_flag(DQUOT_SUSPENDED, type);
return sb_dqopt(sb)->flags & DQUOT_GRP_SUSPENDED;
} }
static inline int sb_any_quota_suspended(struct super_block *sb) static inline int sb_any_quota_suspended(struct super_block *sb)
...@@ -92,6 +91,34 @@ static inline int sb_any_quota_suspended(struct super_block *sb) ...@@ -92,6 +91,34 @@ static inline int sb_any_quota_suspended(struct super_block *sb)
sb_has_quota_suspended(sb, GRPQUOTA); sb_has_quota_suspended(sb, GRPQUOTA);
} }
/* Does kernel know about any quota information for given sb + type? */
static inline int sb_has_quota_loaded(struct super_block *sb, int type)
{
/* Currently if anything is on, then quota usage is on as well */
return sb_has_quota_usage_enabled(sb, type);
}
static inline int sb_any_quota_loaded(struct super_block *sb)
{
return sb_has_quota_loaded(sb, USRQUOTA) ||
sb_has_quota_loaded(sb, GRPQUOTA);
}
static inline int sb_has_quota_active(struct super_block *sb, int type)
{
return sb_has_quota_loaded(sb, type) &&
!sb_has_quota_suspended(sb, type);
}
static inline int sb_any_quota_active(struct super_block *sb)
{
return sb_has_quota_active(sb, USRQUOTA) ||
sb_has_quota_active(sb, GRPQUOTA);
}
/* For backward compatibility until we remove all users */
#define sb_any_quota_enabled(sb) sb_any_quota_active(sb)
/* /*
* Operations supported for diskquotas. * Operations supported for diskquotas.
*/ */
...@@ -106,7 +133,7 @@ extern struct quotactl_ops vfs_quotactl_ops; ...@@ -106,7 +133,7 @@ extern struct quotactl_ops vfs_quotactl_ops;
static inline void vfs_dq_init(struct inode *inode) static inline void vfs_dq_init(struct inode *inode)
{ {
BUG_ON(!inode->i_sb); BUG_ON(!inode->i_sb);
if (sb_any_quota_enabled(inode->i_sb) && !IS_NOQUOTA(inode)) if (sb_any_quota_active(inode->i_sb) && !IS_NOQUOTA(inode))
inode->i_sb->dq_op->initialize(inode, -1); inode->i_sb->dq_op->initialize(inode, -1);
} }
...@@ -114,7 +141,7 @@ static inline void vfs_dq_init(struct inode *inode) ...@@ -114,7 +141,7 @@ static inline void vfs_dq_init(struct inode *inode)
* a transaction (deadlocks possible otherwise) */ * a transaction (deadlocks possible otherwise) */
static inline int vfs_dq_prealloc_space_nodirty(struct inode *inode, qsize_t nr) static inline int vfs_dq_prealloc_space_nodirty(struct inode *inode, qsize_t nr)
{ {
if (sb_any_quota_enabled(inode->i_sb)) { if (sb_any_quota_active(inode->i_sb)) {
/* Used space is updated in alloc_space() */ /* Used space is updated in alloc_space() */
if (inode->i_sb->dq_op->alloc_space(inode, nr, 1) == NO_QUOTA) if (inode->i_sb->dq_op->alloc_space(inode, nr, 1) == NO_QUOTA)
return 1; return 1;
...@@ -134,7 +161,7 @@ static inline int vfs_dq_prealloc_space(struct inode *inode, qsize_t nr) ...@@ -134,7 +161,7 @@ static inline int vfs_dq_prealloc_space(struct inode *inode, qsize_t nr)
static inline int vfs_dq_alloc_space_nodirty(struct inode *inode, qsize_t nr) static inline int vfs_dq_alloc_space_nodirty(struct inode *inode, qsize_t nr)
{ {
if (sb_any_quota_enabled(inode->i_sb)) { if (sb_any_quota_active(inode->i_sb)) {
/* Used space is updated in alloc_space() */ /* Used space is updated in alloc_space() */
if (inode->i_sb->dq_op->alloc_space(inode, nr, 0) == NO_QUOTA) if (inode->i_sb->dq_op->alloc_space(inode, nr, 0) == NO_QUOTA)
return 1; return 1;
...@@ -154,7 +181,7 @@ static inline int vfs_dq_alloc_space(struct inode *inode, qsize_t nr) ...@@ -154,7 +181,7 @@ static inline int vfs_dq_alloc_space(struct inode *inode, qsize_t nr)
static inline int vfs_dq_alloc_inode(struct inode *inode) static inline int vfs_dq_alloc_inode(struct inode *inode)
{ {
if (sb_any_quota_enabled(inode->i_sb)) { if (sb_any_quota_active(inode->i_sb)) {
vfs_dq_init(inode); vfs_dq_init(inode);
if (inode->i_sb->dq_op->alloc_inode(inode, 1) == NO_QUOTA) if (inode->i_sb->dq_op->alloc_inode(inode, 1) == NO_QUOTA)
return 1; return 1;
...@@ -164,7 +191,7 @@ static inline int vfs_dq_alloc_inode(struct inode *inode) ...@@ -164,7 +191,7 @@ static inline int vfs_dq_alloc_inode(struct inode *inode)
static inline void vfs_dq_free_space_nodirty(struct inode *inode, qsize_t nr) static inline void vfs_dq_free_space_nodirty(struct inode *inode, qsize_t nr)
{ {
if (sb_any_quota_enabled(inode->i_sb)) if (sb_any_quota_active(inode->i_sb))
inode->i_sb->dq_op->free_space(inode, nr); inode->i_sb->dq_op->free_space(inode, nr);
else else
inode_sub_bytes(inode, nr); inode_sub_bytes(inode, nr);
...@@ -178,7 +205,7 @@ static inline void vfs_dq_free_space(struct inode *inode, qsize_t nr) ...@@ -178,7 +205,7 @@ static inline void vfs_dq_free_space(struct inode *inode, qsize_t nr)
static inline void vfs_dq_free_inode(struct inode *inode) static inline void vfs_dq_free_inode(struct inode *inode)
{ {
if (sb_any_quota_enabled(inode->i_sb)) if (sb_any_quota_active(inode->i_sb))
inode->i_sb->dq_op->free_inode(inode, 1); inode->i_sb->dq_op->free_inode(inode, 1);
} }
...@@ -199,12 +226,12 @@ static inline int vfs_dq_off(struct super_block *sb, int remount) ...@@ -199,12 +226,12 @@ static inline int vfs_dq_off(struct super_block *sb, int remount)
#else #else
static inline int sb_has_quota_enabled(struct super_block *sb, int type) static inline int sb_has_quota_usage_enabled(struct super_block *sb, int type)
{ {
return 0; return 0;
} }
static inline int sb_any_quota_enabled(struct super_block *sb) static inline int sb_has_quota_limits_enabled(struct super_block *sb, int type)
{ {
return 0; return 0;
} }
...@@ -219,6 +246,30 @@ static inline int sb_any_quota_suspended(struct super_block *sb) ...@@ -219,6 +246,30 @@ static inline int sb_any_quota_suspended(struct super_block *sb)
return 0; return 0;
} }
/* Does kernel know about any quota information for given sb + type? */
static inline int sb_has_quota_loaded(struct super_block *sb, int type)
{
return 0;
}
static inline int sb_any_quota_loaded(struct super_block *sb)
{
return 0;
}
static inline int sb_has_quota_active(struct super_block *sb, int type)
{
return 0;
}
static inline int sb_any_quota_active(struct super_block *sb)
{
return 0;
}
/* For backward compatibility until we remove all users */
#define sb_any_quota_enabled(sb) sb_any_quota_active(sb)
/* /*
* NO-OP when quota not configured. * NO-OP when quota not configured.
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment