Commit e93b1cc8 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs

Pull quota, fsnotify and ext2 updates from Jan Kara:
 "Changes to locking of some quota operations from dedicated quota mutex
  to s_umount semaphore, a fsnotify fix and a simple ext2 fix"

* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs:
  quota: Fix bogus warning in dquot_disable()
  fsnotify: Fix possible use-after-free in inode iteration on umount
  ext2: reject inodes with negative size
  quota: Remove dqonoff_mutex
  ocfs2: Use s_umount for quota recovery protection
  quota: Remove dqonoff_mutex from dquot_scan_active()
  ocfs2: Protect periodic quota syncing with s_umount semaphore
  quota: Use s_umount protection for quota operations
  quota: Hold s_umount in exclusive mode when enabling / disabling quotas
  fs: Provide function to get superblock with exclusive s_umount
parents 45d36906 2700e606
...@@ -1478,6 +1478,10 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino) ...@@ -1478,6 +1478,10 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino)
inode->i_size |= ((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32; inode->i_size |= ((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32;
else else
ei->i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl); ei->i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl);
if (i_size_read(inode) < 0) {
ret = -EFSCORRUPTED;
goto bad_inode;
}
ei->i_dtime = 0; ei->i_dtime = 0;
inode->i_generation = le32_to_cpu(raw_inode->i_generation); inode->i_generation = le32_to_cpu(raw_inode->i_generation);
ei->i_state = 0; ei->i_state = 0;
......
...@@ -150,12 +150,10 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark, ...@@ -150,12 +150,10 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
*/ */
void fsnotify_unmount_inodes(struct super_block *sb) void fsnotify_unmount_inodes(struct super_block *sb)
{ {
struct inode *inode, *next_i, *need_iput = NULL; struct inode *inode, *iput_inode = NULL;
spin_lock(&sb->s_inode_list_lock); spin_lock(&sb->s_inode_list_lock);
list_for_each_entry_safe(inode, next_i, &sb->s_inodes, i_sb_list) { list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
struct inode *need_iput_tmp;
/* /*
* We cannot __iget() an inode in state I_FREEING, * We cannot __iget() an inode in state I_FREEING,
* I_WILL_FREE, or I_NEW which is fine because by that point * I_WILL_FREE, or I_NEW which is fine because by that point
...@@ -178,49 +176,24 @@ void fsnotify_unmount_inodes(struct super_block *sb) ...@@ -178,49 +176,24 @@ void fsnotify_unmount_inodes(struct super_block *sb)
continue; continue;
} }
need_iput_tmp = need_iput;
need_iput = NULL;
/* In case fsnotify_inode_delete() drops a reference. */
if (inode != need_iput_tmp)
__iget(inode); __iget(inode);
else
need_iput_tmp = NULL;
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
/* In case the dropping of a reference would nuke next_i. */
while (&next_i->i_sb_list != &sb->s_inodes) {
spin_lock(&next_i->i_lock);
if (!(next_i->i_state & (I_FREEING | I_WILL_FREE)) &&
atomic_read(&next_i->i_count)) {
__iget(next_i);
need_iput = next_i;
spin_unlock(&next_i->i_lock);
break;
}
spin_unlock(&next_i->i_lock);
next_i = list_next_entry(next_i, i_sb_list);
}
/*
* We can safely drop s_inode_list_lock here because either
* we actually hold references on both inode and next_i or
* end of list. Also no new inodes will be added since the
* umount has begun.
*/
spin_unlock(&sb->s_inode_list_lock); spin_unlock(&sb->s_inode_list_lock);
if (need_iput_tmp) if (iput_inode)
iput(need_iput_tmp); iput(iput_inode);
/* for each watch, send FS_UNMOUNT and then remove it */ /* for each watch, send FS_UNMOUNT and then remove it */
fsnotify(inode, FS_UNMOUNT, inode, FSNOTIFY_EVENT_INODE, NULL, 0); fsnotify(inode, FS_UNMOUNT, inode, FSNOTIFY_EVENT_INODE, NULL, 0);
fsnotify_inode_delete(inode); fsnotify_inode_delete(inode);
iput(inode); iput_inode = inode;
spin_lock(&sb->s_inode_list_lock); spin_lock(&sb->s_inode_list_lock);
} }
spin_unlock(&sb->s_inode_list_lock); spin_unlock(&sb->s_inode_list_lock);
if (iput_inode)
iput(iput_inode);
} }
...@@ -634,7 +634,15 @@ static void qsync_work_fn(struct work_struct *work) ...@@ -634,7 +634,15 @@ static void qsync_work_fn(struct work_struct *work)
dqi_sync_work.work); dqi_sync_work.work);
struct super_block *sb = oinfo->dqi_gqinode->i_sb; struct super_block *sb = oinfo->dqi_gqinode->i_sb;
/*
* We have to be careful here not to deadlock on s_umount as umount
* disabling quotas may be in progress and it waits for this work to
* complete. If trylock fails, we'll do the sync next time...
*/
if (down_read_trylock(&sb->s_umount)) {
dquot_scan_active(sb, ocfs2_sync_dquot_helper, oinfo->dqi_type); dquot_scan_active(sb, ocfs2_sync_dquot_helper, oinfo->dqi_type);
up_read(&sb->s_umount);
}
schedule_delayed_work(&oinfo->dqi_sync_work, schedule_delayed_work(&oinfo->dqi_sync_work,
msecs_to_jiffies(oinfo->dqi_syncms)); msecs_to_jiffies(oinfo->dqi_syncms));
} }
......
...@@ -454,7 +454,7 @@ struct ocfs2_quota_recovery *ocfs2_begin_quota_recovery( ...@@ -454,7 +454,7 @@ struct ocfs2_quota_recovery *ocfs2_begin_quota_recovery(
/* Sync changes in local quota file into global quota file and /* Sync changes in local quota file into global quota file and
* reinitialize local quota file. * reinitialize local quota file.
* The function expects local quota file to be already locked and * The function expects local quota file to be already locked and
* dqonoff_mutex locked. */ * s_umount locked in shared mode. */
static int ocfs2_recover_local_quota_file(struct inode *lqinode, static int ocfs2_recover_local_quota_file(struct inode *lqinode,
int type, int type,
struct ocfs2_quota_recovery *rec) struct ocfs2_quota_recovery *rec)
...@@ -597,7 +597,7 @@ int ocfs2_finish_quota_recovery(struct ocfs2_super *osb, ...@@ -597,7 +597,7 @@ int ocfs2_finish_quota_recovery(struct ocfs2_super *osb,
printk(KERN_NOTICE "ocfs2: Finishing quota recovery on device (%s) for " printk(KERN_NOTICE "ocfs2: Finishing quota recovery on device (%s) for "
"slot %u\n", osb->dev_str, slot_num); "slot %u\n", osb->dev_str, slot_num);
mutex_lock(&sb_dqopt(sb)->dqonoff_mutex); down_read(&sb->s_umount);
for (type = 0; type < OCFS2_MAXQUOTAS; type++) { for (type = 0; type < OCFS2_MAXQUOTAS; type++) {
if (list_empty(&(rec->r_list[type]))) if (list_empty(&(rec->r_list[type])))
continue; continue;
...@@ -674,7 +674,7 @@ int ocfs2_finish_quota_recovery(struct ocfs2_super *osb, ...@@ -674,7 +674,7 @@ int ocfs2_finish_quota_recovery(struct ocfs2_super *osb,
break; break;
} }
out: out:
mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex); up_read(&sb->s_umount);
kfree(rec); kfree(rec);
return status; return status;
} }
...@@ -840,7 +840,10 @@ static int ocfs2_local_free_info(struct super_block *sb, int type) ...@@ -840,7 +840,10 @@ static int ocfs2_local_free_info(struct super_block *sb, int type)
} }
ocfs2_release_local_quota_bitmaps(&oinfo->dqi_chunk); ocfs2_release_local_quota_bitmaps(&oinfo->dqi_chunk);
/* dqonoff_mutex protects us against racing with recovery thread... */ /*
* s_umount held in exclusive mode protects us against racing with
* recovery thread...
*/
if (oinfo->dqi_rec) { if (oinfo->dqi_rec) {
ocfs2_free_quota_recovery(oinfo->dqi_rec); ocfs2_free_quota_recovery(oinfo->dqi_rec);
mark_clean = 0; mark_clean = 0;
......
...@@ -985,7 +985,6 @@ static void ocfs2_disable_quotas(struct ocfs2_super *osb) ...@@ -985,7 +985,6 @@ static void ocfs2_disable_quotas(struct ocfs2_super *osb)
for (type = 0; type < OCFS2_MAXQUOTAS; type++) { for (type = 0; type < OCFS2_MAXQUOTAS; type++) {
if (!sb_has_quota_loaded(sb, type)) if (!sb_has_quota_loaded(sb, type))
continue; continue;
/* Cancel periodic syncing before we grab dqonoff_mutex */
oinfo = sb_dqinfo(sb, type)->dqi_priv; oinfo = sb_dqinfo(sb, type)->dqi_priv;
cancel_delayed_work_sync(&oinfo->dqi_sync_work); cancel_delayed_work_sync(&oinfo->dqi_sync_work);
inode = igrab(sb->s_dquot.files[type]); inode = igrab(sb->s_dquot.files[type]);
......
...@@ -119,8 +119,7 @@ ...@@ -119,8 +119,7 @@
* spinlock to internal buffers before writing. * spinlock to internal buffers before writing.
* *
* Lock ordering (including related VFS locks) is the following: * Lock ordering (including related VFS locks) is the following:
* dqonoff_mutex > i_mutex > journal_lock > dquot->dq_lock > dqio_mutex * s_umount > i_mutex > journal_lock > dquot->dq_lock > dqio_mutex
* dqonoff_mutex > i_mutex comes from dquot_quota_sync, dquot_enable, etc.
*/ */
static __cacheline_aligned_in_smp DEFINE_SPINLOCK(dq_list_lock); static __cacheline_aligned_in_smp DEFINE_SPINLOCK(dq_list_lock);
...@@ -572,7 +571,8 @@ int dquot_scan_active(struct super_block *sb, ...@@ -572,7 +571,8 @@ int dquot_scan_active(struct super_block *sb,
struct dquot *dquot, *old_dquot = NULL; struct dquot *dquot, *old_dquot = NULL;
int ret = 0; int ret = 0;
mutex_lock(&sb_dqopt(sb)->dqonoff_mutex); WARN_ON_ONCE(!rwsem_is_locked(&sb->s_umount));
spin_lock(&dq_list_lock); spin_lock(&dq_list_lock);
list_for_each_entry(dquot, &inuse_list, dq_inuse) { list_for_each_entry(dquot, &inuse_list, dq_inuse) {
if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags))
...@@ -603,7 +603,6 @@ int dquot_scan_active(struct super_block *sb, ...@@ -603,7 +603,6 @@ int dquot_scan_active(struct super_block *sb,
spin_unlock(&dq_list_lock); spin_unlock(&dq_list_lock);
out: out:
dqput(old_dquot); dqput(old_dquot);
mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
return ret; return ret;
} }
EXPORT_SYMBOL(dquot_scan_active); EXPORT_SYMBOL(dquot_scan_active);
...@@ -617,7 +616,8 @@ int dquot_writeback_dquots(struct super_block *sb, int type) ...@@ -617,7 +616,8 @@ int dquot_writeback_dquots(struct super_block *sb, int type)
int cnt; int cnt;
int err, ret = 0; int err, ret = 0;
mutex_lock(&dqopt->dqonoff_mutex); WARN_ON_ONCE(!rwsem_is_locked(&sb->s_umount));
for (cnt = 0; cnt < MAXQUOTAS; cnt++) { for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
if (type != -1 && cnt != type) if (type != -1 && cnt != type)
continue; continue;
...@@ -653,7 +653,6 @@ int dquot_writeback_dquots(struct super_block *sb, int type) ...@@ -653,7 +653,6 @@ int dquot_writeback_dquots(struct super_block *sb, int type)
&& info_dirty(&dqopt->info[cnt])) && info_dirty(&dqopt->info[cnt]))
sb->dq_op->write_info(sb, cnt); sb->dq_op->write_info(sb, cnt);
dqstats_inc(DQST_SYNCS); dqstats_inc(DQST_SYNCS);
mutex_unlock(&dqopt->dqonoff_mutex);
return ret; return ret;
} }
...@@ -683,7 +682,6 @@ int dquot_quota_sync(struct super_block *sb, int type) ...@@ -683,7 +682,6 @@ int dquot_quota_sync(struct super_block *sb, int type)
* Now when everything is written we can discard the pagecache so * Now when everything is written we can discard the pagecache so
* that userspace sees the changes. * that userspace sees the changes.
*/ */
mutex_lock(&dqopt->dqonoff_mutex);
for (cnt = 0; cnt < MAXQUOTAS; cnt++) { for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
if (type != -1 && cnt != type) if (type != -1 && cnt != type)
continue; continue;
...@@ -693,7 +691,6 @@ int dquot_quota_sync(struct super_block *sb, int type) ...@@ -693,7 +691,6 @@ int dquot_quota_sync(struct super_block *sb, int type)
truncate_inode_pages(&dqopt->files[cnt]->i_data, 0); truncate_inode_pages(&dqopt->files[cnt]->i_data, 0);
inode_unlock(dqopt->files[cnt]); inode_unlock(dqopt->files[cnt]);
} }
mutex_unlock(&dqopt->dqonoff_mutex);
return 0; return 0;
} }
...@@ -935,7 +932,7 @@ static int dqinit_needed(struct inode *inode, int type) ...@@ -935,7 +932,7 @@ static int dqinit_needed(struct inode *inode, int type)
return 0; return 0;
} }
/* This routine is guarded by dqonoff_mutex mutex */ /* This routine is guarded by s_umount semaphore */
static void add_dquot_ref(struct super_block *sb, int type) static void add_dquot_ref(struct super_block *sb, int type)
{ {
struct inode *inode, *old_inode = NULL; struct inode *inode, *old_inode = NULL;
...@@ -2050,21 +2047,13 @@ int dquot_get_next_id(struct super_block *sb, struct kqid *qid) ...@@ -2050,21 +2047,13 @@ int dquot_get_next_id(struct super_block *sb, struct kqid *qid)
struct quota_info *dqopt = sb_dqopt(sb); struct quota_info *dqopt = sb_dqopt(sb);
int err; int err;
mutex_lock(&dqopt->dqonoff_mutex); if (!sb_has_quota_active(sb, qid->type))
if (!sb_has_quota_active(sb, qid->type)) { return -ESRCH;
err = -ESRCH; if (!dqopt->ops[qid->type]->get_next_id)
goto out; return -ENOSYS;
}
if (!dqopt->ops[qid->type]->get_next_id) {
err = -ENOSYS;
goto out;
}
mutex_lock(&dqopt->dqio_mutex); mutex_lock(&dqopt->dqio_mutex);
err = dqopt->ops[qid->type]->get_next_id(sb, qid); err = dqopt->ops[qid->type]->get_next_id(sb, qid);
mutex_unlock(&dqopt->dqio_mutex); mutex_unlock(&dqopt->dqio_mutex);
out:
mutex_unlock(&dqopt->dqonoff_mutex);
return err; return err;
} }
EXPORT_SYMBOL(dquot_get_next_id); EXPORT_SYMBOL(dquot_get_next_id);
...@@ -2107,6 +2096,10 @@ int dquot_disable(struct super_block *sb, int type, unsigned int flags) ...@@ -2107,6 +2096,10 @@ int dquot_disable(struct super_block *sb, int type, unsigned int flags)
struct quota_info *dqopt = sb_dqopt(sb); struct quota_info *dqopt = sb_dqopt(sb);
struct inode *toputinode[MAXQUOTAS]; struct inode *toputinode[MAXQUOTAS];
/* s_umount should be held in exclusive mode */
if (WARN_ON_ONCE(down_read_trylock(&sb->s_umount)))
up_read(&sb->s_umount);
/* Cannot turn off usage accounting without turning off limits, or /* Cannot turn off usage accounting without turning off limits, or
* suspend quotas and simultaneously turn quotas off. */ * suspend quotas and simultaneously turn quotas off. */
if ((flags & DQUOT_USAGE_ENABLED && !(flags & DQUOT_LIMITS_ENABLED)) if ((flags & DQUOT_USAGE_ENABLED && !(flags & DQUOT_LIMITS_ENABLED))
...@@ -2114,18 +2107,14 @@ int dquot_disable(struct super_block *sb, int type, unsigned int flags) ...@@ -2114,18 +2107,14 @@ int dquot_disable(struct super_block *sb, int type, unsigned int flags)
DQUOT_USAGE_ENABLED))) DQUOT_USAGE_ENABLED)))
return -EINVAL; return -EINVAL;
/* We need to serialize quota_off() for device */
mutex_lock(&dqopt->dqonoff_mutex);
/* /*
* Skip everything if there's nothing to do. We have to do this because * Skip everything if there's nothing to do. We have to do this because
* 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_loaded(sb)) { if (!sb_any_quota_loaded(sb))
mutex_unlock(&dqopt->dqonoff_mutex);
return 0; return 0;
}
for (cnt = 0; cnt < MAXQUOTAS; cnt++) { for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
toputinode[cnt] = NULL; toputinode[cnt] = NULL;
if (type != -1 && cnt != type) if (type != -1 && cnt != type)
...@@ -2179,7 +2168,6 @@ int dquot_disable(struct super_block *sb, int type, unsigned int flags) ...@@ -2179,7 +2168,6 @@ int dquot_disable(struct super_block *sb, int type, unsigned int flags)
dqopt->info[cnt].dqi_bgrace = 0; dqopt->info[cnt].dqi_bgrace = 0;
dqopt->ops[cnt] = NULL; dqopt->ops[cnt] = NULL;
} }
mutex_unlock(&dqopt->dqonoff_mutex);
/* Skip syncing and setting flags if quota files are hidden */ /* Skip syncing and setting flags if quota files are hidden */
if (dqopt->flags & DQUOT_QUOTA_SYS_FILE) if (dqopt->flags & DQUOT_QUOTA_SYS_FILE)
...@@ -2196,21 +2184,15 @@ int dquot_disable(struct super_block *sb, int type, unsigned int flags) ...@@ -2196,21 +2184,15 @@ int dquot_disable(struct super_block *sb, int type, unsigned int flags)
* must also discard the blockdev buffers so that we see the * must also discard the blockdev buffers so that we see the
* changes done by userspace on the next quotaon() */ * changes done by userspace on the next quotaon() */
for (cnt = 0; cnt < MAXQUOTAS; cnt++) for (cnt = 0; cnt < MAXQUOTAS; cnt++)
if (toputinode[cnt]) { /* This can happen when suspending quotas on remount-ro... */
mutex_lock(&dqopt->dqonoff_mutex); if (toputinode[cnt] && !sb_has_quota_loaded(sb, cnt)) {
/* If quota was reenabled in the meantime, we have
* nothing to do */
if (!sb_has_quota_loaded(sb, cnt)) {
inode_lock(toputinode[cnt]); inode_lock(toputinode[cnt]);
toputinode[cnt]->i_flags &= ~(S_IMMUTABLE | toputinode[cnt]->i_flags &= ~(S_IMMUTABLE |
S_NOATIME | S_NOQUOTA); S_NOATIME | S_NOQUOTA);
truncate_inode_pages(&toputinode[cnt]->i_data, truncate_inode_pages(&toputinode[cnt]->i_data, 0);
0);
inode_unlock(toputinode[cnt]); inode_unlock(toputinode[cnt]);
mark_inode_dirty_sync(toputinode[cnt]); mark_inode_dirty_sync(toputinode[cnt]);
} }
mutex_unlock(&dqopt->dqonoff_mutex);
}
if (sb->s_bdev) if (sb->s_bdev)
invalidate_bdev(sb->s_bdev); invalidate_bdev(sb->s_bdev);
put_inodes: put_inodes:
...@@ -2281,6 +2263,10 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id, ...@@ -2281,6 +2263,10 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id,
error = -EINVAL; error = -EINVAL;
goto out_fmt; goto out_fmt;
} }
if (sb_has_quota_loaded(sb, type)) {
error = -EBUSY;
goto out_fmt;
}
if (!(dqopt->flags & DQUOT_QUOTA_SYS_FILE)) { if (!(dqopt->flags & DQUOT_QUOTA_SYS_FILE)) {
/* As we bypass the pagecache we must now flush all the /* As we bypass the pagecache we must now flush all the
...@@ -2292,11 +2278,6 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id, ...@@ -2292,11 +2278,6 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id,
sync_filesystem(sb); sync_filesystem(sb);
invalidate_bdev(sb->s_bdev); invalidate_bdev(sb->s_bdev);
} }
mutex_lock(&dqopt->dqonoff_mutex);
if (sb_has_quota_loaded(sb, type)) {
error = -EBUSY;
goto out_lock;
}
if (!(dqopt->flags & DQUOT_QUOTA_SYS_FILE)) { if (!(dqopt->flags & DQUOT_QUOTA_SYS_FILE)) {
/* We don't want quota and atime on quota files (deadlocks /* We don't want quota and atime on quota files (deadlocks
...@@ -2317,7 +2298,7 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id, ...@@ -2317,7 +2298,7 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id,
error = -EIO; error = -EIO;
dqopt->files[type] = igrab(inode); dqopt->files[type] = igrab(inode);
if (!dqopt->files[type]) if (!dqopt->files[type])
goto out_lock; goto out_file_flags;
error = -EINVAL; error = -EINVAL;
if (!fmt->qf_ops->check_quota_file(sb, type)) if (!fmt->qf_ops->check_quota_file(sb, type))
goto out_file_init; goto out_file_init;
...@@ -2340,14 +2321,13 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id, ...@@ -2340,14 +2321,13 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id,
spin_unlock(&dq_state_lock); spin_unlock(&dq_state_lock);
add_dquot_ref(sb, type); add_dquot_ref(sb, type);
mutex_unlock(&dqopt->dqonoff_mutex);
return 0; return 0;
out_file_init: out_file_init:
dqopt->files[type] = NULL; dqopt->files[type] = NULL;
iput(inode); iput(inode);
out_lock: out_file_flags:
if (oldflags != -1) { if (oldflags != -1) {
inode_lock(inode); inode_lock(inode);
/* Set the flags back (in the case of accidental quotaon() /* Set the flags back (in the case of accidental quotaon()
...@@ -2356,7 +2336,6 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id, ...@@ -2356,7 +2336,6 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id,
inode->i_flags |= oldflags; inode->i_flags |= oldflags;
inode_unlock(inode); inode_unlock(inode);
} }
mutex_unlock(&dqopt->dqonoff_mutex);
out_fmt: out_fmt:
put_quota_format(fmt); put_quota_format(fmt);
...@@ -2371,15 +2350,16 @@ int dquot_resume(struct super_block *sb, int type) ...@@ -2371,15 +2350,16 @@ int dquot_resume(struct super_block *sb, int type)
int ret = 0, cnt; int ret = 0, cnt;
unsigned int flags; unsigned int flags;
/* s_umount should be held in exclusive mode */
if (WARN_ON_ONCE(down_read_trylock(&sb->s_umount)))
up_read(&sb->s_umount);
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_suspended(sb, cnt))
mutex_lock(&dqopt->dqonoff_mutex);
if (!sb_has_quota_suspended(sb, cnt)) {
mutex_unlock(&dqopt->dqonoff_mutex);
continue; continue;
}
inode = dqopt->files[cnt]; inode = dqopt->files[cnt];
dqopt->files[cnt] = NULL; dqopt->files[cnt] = NULL;
spin_lock(&dq_state_lock); spin_lock(&dq_state_lock);
...@@ -2388,7 +2368,6 @@ int dquot_resume(struct super_block *sb, int type) ...@@ -2388,7 +2368,6 @@ int dquot_resume(struct super_block *sb, int type)
cnt); cnt);
dqopt->flags &= ~dquot_state_flag(DQUOT_STATE_FLAGS, cnt); dqopt->flags &= ~dquot_state_flag(DQUOT_STATE_FLAGS, cnt);
spin_unlock(&dq_state_lock); spin_unlock(&dq_state_lock);
mutex_unlock(&dqopt->dqonoff_mutex);
flags = dquot_generic_flag(flags, cnt); flags = dquot_generic_flag(flags, cnt);
ret = vfs_load_quota_inode(inode, cnt, ret = vfs_load_quota_inode(inode, cnt,
...@@ -2424,42 +2403,30 @@ EXPORT_SYMBOL(dquot_quota_on); ...@@ -2424,42 +2403,30 @@ EXPORT_SYMBOL(dquot_quota_on);
int dquot_enable(struct inode *inode, int type, int format_id, int dquot_enable(struct inode *inode, int type, int format_id,
unsigned int flags) unsigned int flags)
{ {
int ret = 0;
struct super_block *sb = inode->i_sb; struct super_block *sb = inode->i_sb;
struct quota_info *dqopt = sb_dqopt(sb);
/* Just unsuspend quotas? */ /* Just unsuspend quotas? */
BUG_ON(flags & DQUOT_SUSPENDED); BUG_ON(flags & DQUOT_SUSPENDED);
/* s_umount should be held in exclusive mode */
if (WARN_ON_ONCE(down_read_trylock(&sb->s_umount)))
up_read(&sb->s_umount);
if (!flags) if (!flags)
return 0; return 0;
/* Just updating flags needed? */ /* Just updating flags needed? */
if (sb_has_quota_loaded(sb, type)) { 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 && if (flags & DQUOT_USAGE_ENABLED &&
sb_has_quota_usage_enabled(sb, type)) { sb_has_quota_usage_enabled(sb, type))
ret = -EBUSY; return -EBUSY;
goto out_lock;
}
if (flags & DQUOT_LIMITS_ENABLED && if (flags & DQUOT_LIMITS_ENABLED &&
sb_has_quota_limits_enabled(sb, type)) { sb_has_quota_limits_enabled(sb, type))
ret = -EBUSY; return -EBUSY;
goto out_lock;
}
spin_lock(&dq_state_lock); spin_lock(&dq_state_lock);
sb_dqopt(sb)->flags |= dquot_state_flag(flags, type); sb_dqopt(sb)->flags |= dquot_state_flag(flags, type);
spin_unlock(&dq_state_lock); spin_unlock(&dq_state_lock);
out_lock: return 0;
mutex_unlock(&dqopt->dqonoff_mutex);
return ret;
} }
load_quota:
return vfs_load_quota_inode(inode, type, format_id, flags); return vfs_load_quota_inode(inode, type, format_id, flags);
} }
EXPORT_SYMBOL(dquot_enable); EXPORT_SYMBOL(dquot_enable);
...@@ -2751,7 +2718,6 @@ int dquot_get_state(struct super_block *sb, struct qc_state *state) ...@@ -2751,7 +2718,6 @@ int dquot_get_state(struct super_block *sb, struct qc_state *state)
struct quota_info *dqopt = sb_dqopt(sb); struct quota_info *dqopt = sb_dqopt(sb);
int type; int type;
mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
memset(state, 0, sizeof(*state)); memset(state, 0, sizeof(*state));
for (type = 0; type < MAXQUOTAS; type++) { for (type = 0; type < MAXQUOTAS; type++) {
if (!sb_has_quota_active(sb, type)) if (!sb_has_quota_active(sb, type))
...@@ -2773,7 +2739,6 @@ int dquot_get_state(struct super_block *sb, struct qc_state *state) ...@@ -2773,7 +2739,6 @@ int dquot_get_state(struct super_block *sb, struct qc_state *state)
tstate->nextents = 1; /* We don't know... */ tstate->nextents = 1; /* We don't know... */
spin_unlock(&dq_data_lock); spin_unlock(&dq_data_lock);
} }
mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
return 0; return 0;
} }
EXPORT_SYMBOL(dquot_get_state); EXPORT_SYMBOL(dquot_get_state);
...@@ -2787,18 +2752,13 @@ int dquot_set_dqinfo(struct super_block *sb, int type, struct qc_info *ii) ...@@ -2787,18 +2752,13 @@ int dquot_set_dqinfo(struct super_block *sb, int type, struct qc_info *ii)
if ((ii->i_fieldmask & QC_WARNS_MASK) || if ((ii->i_fieldmask & QC_WARNS_MASK) ||
(ii->i_fieldmask & QC_RT_SPC_TIMER)) (ii->i_fieldmask & QC_RT_SPC_TIMER))
return -EINVAL; return -EINVAL;
mutex_lock(&sb_dqopt(sb)->dqonoff_mutex); if (!sb_has_quota_active(sb, type))
if (!sb_has_quota_active(sb, type)) { return -ESRCH;
err = -ESRCH;
goto out;
}
mi = sb_dqopt(sb)->info + type; mi = sb_dqopt(sb)->info + type;
if (ii->i_fieldmask & QC_FLAGS) { if (ii->i_fieldmask & QC_FLAGS) {
if ((ii->i_flags & QCI_ROOT_SQUASH && if ((ii->i_flags & QCI_ROOT_SQUASH &&
mi->dqi_format->qf_fmt_id != QFMT_VFS_OLD)) { mi->dqi_format->qf_fmt_id != QFMT_VFS_OLD))
err = -EINVAL; return -EINVAL;
goto out;
}
} }
spin_lock(&dq_data_lock); spin_lock(&dq_data_lock);
if (ii->i_fieldmask & QC_SPC_TIMER) if (ii->i_fieldmask & QC_SPC_TIMER)
...@@ -2815,8 +2775,6 @@ int dquot_set_dqinfo(struct super_block *sb, int type, struct qc_info *ii) ...@@ -2815,8 +2775,6 @@ int dquot_set_dqinfo(struct super_block *sb, int type, struct qc_info *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);
return err; return err;
} }
EXPORT_SYMBOL(dquot_set_dqinfo); EXPORT_SYMBOL(dquot_set_dqinfo);
......
...@@ -104,13 +104,9 @@ static int quota_getfmt(struct super_block *sb, int type, void __user *addr) ...@@ -104,13 +104,9 @@ static int quota_getfmt(struct super_block *sb, int type, void __user *addr)
{ {
__u32 fmt; __u32 fmt;
mutex_lock(&sb_dqopt(sb)->dqonoff_mutex); if (!sb_has_quota_active(sb, type))
if (!sb_has_quota_active(sb, type)) {
mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
return -ESRCH; return -ESRCH;
}
fmt = sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id; fmt = sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id;
mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
if (copy_to_user(addr, &fmt, sizeof(fmt))) if (copy_to_user(addr, &fmt, sizeof(fmt)))
return -EFAULT; return -EFAULT;
return 0; return 0;
...@@ -789,9 +785,14 @@ static int quotactl_cmd_write(int cmd) ...@@ -789,9 +785,14 @@ static int quotactl_cmd_write(int cmd)
} }
return 1; return 1;
} }
#endif /* CONFIG_BLOCK */ #endif /* CONFIG_BLOCK */
/* Return true if quotactl command is manipulating quota on/off state */
static bool quotactl_cmd_onoff(int cmd)
{
return (cmd == Q_QUOTAON) || (cmd == Q_QUOTAOFF);
}
/* /*
* look up a superblock on which quota ops will be performed * look up a superblock on which quota ops will be performed
* - use the name of a block device to find the superblock thereon * - use the name of a block device to find the superblock thereon
...@@ -809,7 +810,9 @@ static struct super_block *quotactl_block(const char __user *special, int cmd) ...@@ -809,7 +810,9 @@ static struct super_block *quotactl_block(const char __user *special, int cmd)
putname(tmp); putname(tmp);
if (IS_ERR(bdev)) if (IS_ERR(bdev))
return ERR_CAST(bdev); return ERR_CAST(bdev);
if (quotactl_cmd_write(cmd)) if (quotactl_cmd_onoff(cmd))
sb = get_super_exclusive_thawed(bdev);
else if (quotactl_cmd_write(cmd))
sb = get_super_thawed(bdev); sb = get_super_thawed(bdev);
else else
sb = get_super(bdev); sb = get_super(bdev);
...@@ -872,7 +875,10 @@ SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special, ...@@ -872,7 +875,10 @@ SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special,
ret = do_quotactl(sb, type, cmds, id, addr, pathp); ret = do_quotactl(sb, type, cmds, id, addr, pathp);
if (!quotactl_cmd_onoff(cmds))
drop_super(sb); drop_super(sb);
else
drop_super_exclusive(sb);
out: out:
if (pathp && !IS_ERR(pathp)) if (pathp && !IS_ERR(pathp))
path_put(pathp); path_put(pathp);
......
...@@ -244,7 +244,6 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags, ...@@ -244,7 +244,6 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags,
mutex_init(&s->s_vfs_rename_mutex); mutex_init(&s->s_vfs_rename_mutex);
lockdep_set_class(&s->s_vfs_rename_mutex, &type->s_vfs_rename_key); lockdep_set_class(&s->s_vfs_rename_mutex, &type->s_vfs_rename_key);
mutex_init(&s->s_dquot.dqio_mutex); mutex_init(&s->s_dquot.dqio_mutex);
mutex_init(&s->s_dquot.dqonoff_mutex);
s->s_maxbytes = MAX_NON_LFS; s->s_maxbytes = MAX_NON_LFS;
s->s_op = &default_op; s->s_op = &default_op;
s->s_time_gran = 1000000000; s->s_time_gran = 1000000000;
...@@ -558,6 +557,13 @@ void drop_super(struct super_block *sb) ...@@ -558,6 +557,13 @@ void drop_super(struct super_block *sb)
EXPORT_SYMBOL(drop_super); EXPORT_SYMBOL(drop_super);
void drop_super_exclusive(struct super_block *sb)
{
up_write(&sb->s_umount);
put_super(sb);
}
EXPORT_SYMBOL(drop_super_exclusive);
/** /**
* iterate_supers - call function for all active superblocks * iterate_supers - call function for all active superblocks
* @f: function to call * @f: function to call
...@@ -628,15 +634,7 @@ void iterate_supers_type(struct file_system_type *type, ...@@ -628,15 +634,7 @@ void iterate_supers_type(struct file_system_type *type,
EXPORT_SYMBOL(iterate_supers_type); EXPORT_SYMBOL(iterate_supers_type);
/** static struct super_block *__get_super(struct block_device *bdev, bool excl)
* get_super - get the superblock of a device
* @bdev: device to get the superblock for
*
* Scans the superblock list and finds the superblock of the file system
* mounted on the device given. %NULL is returned if no match is found.
*/
struct super_block *get_super(struct block_device *bdev)
{ {
struct super_block *sb; struct super_block *sb;
...@@ -651,11 +649,17 @@ struct super_block *get_super(struct block_device *bdev) ...@@ -651,11 +649,17 @@ struct super_block *get_super(struct block_device *bdev)
if (sb->s_bdev == bdev) { if (sb->s_bdev == bdev) {
sb->s_count++; sb->s_count++;
spin_unlock(&sb_lock); spin_unlock(&sb_lock);
if (!excl)
down_read(&sb->s_umount); down_read(&sb->s_umount);
else
down_write(&sb->s_umount);
/* still alive? */ /* still alive? */
if (sb->s_root && (sb->s_flags & MS_BORN)) if (sb->s_root && (sb->s_flags & MS_BORN))
return sb; return sb;
if (!excl)
up_read(&sb->s_umount); up_read(&sb->s_umount);
else
up_write(&sb->s_umount);
/* nope, got unmounted */ /* nope, got unmounted */
spin_lock(&sb_lock); spin_lock(&sb_lock);
__put_super(sb); __put_super(sb);
...@@ -666,31 +670,66 @@ struct super_block *get_super(struct block_device *bdev) ...@@ -666,31 +670,66 @@ struct super_block *get_super(struct block_device *bdev)
return NULL; return NULL;
} }
EXPORT_SYMBOL(get_super);
/** /**
* get_super_thawed - get thawed superblock of a device * get_super - get the superblock of a device
* @bdev: device to get the superblock for * @bdev: device to get the superblock for
* *
* Scans the superblock list and finds the superblock of the file system * Scans the superblock list and finds the superblock of the file system
* mounted on the device. The superblock is returned once it is thawed * mounted on the device given. %NULL is returned if no match is found.
* (or immediately if it was not frozen). %NULL is returned if no match
* is found.
*/ */
struct super_block *get_super_thawed(struct block_device *bdev) struct super_block *get_super(struct block_device *bdev)
{
return __get_super(bdev, false);
}
EXPORT_SYMBOL(get_super);
static struct super_block *__get_super_thawed(struct block_device *bdev,
bool excl)
{ {
while (1) { while (1) {
struct super_block *s = get_super(bdev); struct super_block *s = __get_super(bdev, excl);
if (!s || s->s_writers.frozen == SB_UNFROZEN) if (!s || s->s_writers.frozen == SB_UNFROZEN)
return s; return s;
if (!excl)
up_read(&s->s_umount); up_read(&s->s_umount);
else
up_write(&s->s_umount);
wait_event(s->s_writers.wait_unfrozen, wait_event(s->s_writers.wait_unfrozen,
s->s_writers.frozen == SB_UNFROZEN); s->s_writers.frozen == SB_UNFROZEN);
put_super(s); put_super(s);
} }
} }
/**
* get_super_thawed - get thawed superblock of a device
* @bdev: device to get the superblock for
*
* Scans the superblock list and finds the superblock of the file system
* mounted on the device. The superblock is returned once it is thawed
* (or immediately if it was not frozen). %NULL is returned if no match
* is found.
*/
struct super_block *get_super_thawed(struct block_device *bdev)
{
return __get_super_thawed(bdev, false);
}
EXPORT_SYMBOL(get_super_thawed); EXPORT_SYMBOL(get_super_thawed);
/**
* get_super_exclusive_thawed - get thawed superblock of a device
* @bdev: device to get the superblock for
*
* Scans the superblock list and finds the superblock of the file system
* mounted on the device. The superblock is returned once it is thawed
* (or immediately if it was not frozen) and s_umount semaphore is held
* in exclusive mode. %NULL is returned if no match is found.
*/
struct super_block *get_super_exclusive_thawed(struct block_device *bdev)
{
return __get_super_thawed(bdev, true);
}
EXPORT_SYMBOL(get_super_exclusive_thawed);
/** /**
* get_active_super - get an active reference to the superblock of a device * get_active_super - get an active reference to the superblock of a device
* @bdev: device to get the superblock for * @bdev: device to get the superblock for
......
...@@ -2903,8 +2903,10 @@ extern void put_filesystem(struct file_system_type *fs); ...@@ -2903,8 +2903,10 @@ extern void put_filesystem(struct file_system_type *fs);
extern struct file_system_type *get_fs_type(const char *name); extern struct file_system_type *get_fs_type(const char *name);
extern struct super_block *get_super(struct block_device *); extern struct super_block *get_super(struct block_device *);
extern struct super_block *get_super_thawed(struct block_device *); extern struct super_block *get_super_thawed(struct block_device *);
extern struct super_block *get_super_exclusive_thawed(struct block_device *bdev);
extern struct super_block *get_active_super(struct block_device *bdev); extern struct super_block *get_active_super(struct block_device *bdev);
extern void drop_super(struct super_block *sb); extern void drop_super(struct super_block *sb);
extern void drop_super_exclusive(struct super_block *sb);
extern void iterate_supers(void (*)(struct super_block *, void *), void *); extern void iterate_supers(void (*)(struct super_block *, void *), void *);
extern void iterate_supers_type(struct file_system_type *, extern void iterate_supers_type(struct file_system_type *,
void (*)(struct super_block *, void *), void *); void (*)(struct super_block *, void *), void *);
......
...@@ -520,7 +520,6 @@ static inline void quota_send_warning(struct kqid qid, dev_t dev, ...@@ -520,7 +520,6 @@ static inline void quota_send_warning(struct kqid qid, dev_t dev,
struct quota_info { struct quota_info {
unsigned int flags; /* Flags for diskquotas on this device */ unsigned int flags; /* Flags for diskquotas on this device */
struct mutex dqio_mutex; /* lock device while I/O in progress */ struct mutex dqio_mutex; /* lock device while I/O in progress */
struct mutex dqonoff_mutex; /* Serialize quotaon & quotaoff */
struct inode *files[MAXQUOTAS]; /* inodes of quotafiles */ struct inode *files[MAXQUOTAS]; /* inodes of quotafiles */
struct mem_dqinfo info[MAXQUOTAS]; /* Information for each quota type */ struct mem_dqinfo info[MAXQUOTAS]; /* Information for each quota type */
const struct quota_format_ops *ops[MAXQUOTAS]; /* Operations for each type */ const struct quota_format_ops *ops[MAXQUOTAS]; /* Operations for each type */
......
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