aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2016-11-23 07:35:14 -0500
committerJan Kara <jack@suse.cz>2016-11-24 09:27:16 -0500
commit9d1ccbe70e0b14545caad12dc73adb3605447df0 (patch)
tree9a8b6122cf02f099996bf5137bbec4d9e4e8799d
parent7d6cd73d33b62021111a469b6a454ec357be295f (diff)
quota: Use s_umount protection for quota operations
Writeback quota is protected by s_umount semaphore held for reading because every writeback must be protected by that lock (grabbed either by the generic writeback code or by quotactl handler). Getting next available ID in quota file, querying quota state, setting quota information, getting quota format are all quotactl operations protected by s_umount semaphore held for reading grabbed in quotactl handler. This also fixes lockdep splat about possible deadlock during filesystem freezing where sync_filesystem() is called with page-faults already blocked but sync_filesystem() calls into dquot_writeback_dquots() which grabs dqonoff_mutex which ranks above i_mutex (vfs_load_quota_inode() grabs i_mutex under dqonoff_mutex) which clearly ranks below page fault freeze protection (e.g. via mmap_sem dependencies). The reported problem is not a real deadlock possibility since during quota on we check whether filesystem freezing is not in progress but still it is good to have this fixed. Reported-by: Ted Tso <tytso@mit.edu> Reported-by: Eric Whitney <enwlinux@gmail.com> Signed-off-by: Jan Kara <jack@suse.cz>
-rw-r--r--fs/quota/dquot.c39
-rw-r--r--fs/quota/quota.c6
2 files changed, 11 insertions, 34 deletions
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index 047afb966420..2a9dc3fb491c 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -617,7 +617,8 @@ int dquot_writeback_dquots(struct super_block *sb, int type)
617 int cnt; 617 int cnt;
618 int err, ret = 0; 618 int err, ret = 0;
619 619
620 mutex_lock(&dqopt->dqonoff_mutex); 620 WARN_ON_ONCE(!rwsem_is_locked(&sb->s_umount));
621
621 for (cnt = 0; cnt < MAXQUOTAS; cnt++) { 622 for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
622 if (type != -1 && cnt != type) 623 if (type != -1 && cnt != type)
623 continue; 624 continue;
@@ -653,7 +654,6 @@ int dquot_writeback_dquots(struct super_block *sb, int type)
653 && info_dirty(&dqopt->info[cnt])) 654 && info_dirty(&dqopt->info[cnt]))
654 sb->dq_op->write_info(sb, cnt); 655 sb->dq_op->write_info(sb, cnt);
655 dqstats_inc(DQST_SYNCS); 656 dqstats_inc(DQST_SYNCS);
656 mutex_unlock(&dqopt->dqonoff_mutex);
657 657
658 return ret; 658 return ret;
659} 659}
@@ -683,7 +683,6 @@ int dquot_quota_sync(struct super_block *sb, int type)
683 * Now when everything is written we can discard the pagecache so 683 * Now when everything is written we can discard the pagecache so
684 * that userspace sees the changes. 684 * that userspace sees the changes.
685 */ 685 */
686 mutex_lock(&dqopt->dqonoff_mutex);
687 for (cnt = 0; cnt < MAXQUOTAS; cnt++) { 686 for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
688 if (type != -1 && cnt != type) 687 if (type != -1 && cnt != type)
689 continue; 688 continue;
@@ -693,7 +692,6 @@ int dquot_quota_sync(struct super_block *sb, int type)
693 truncate_inode_pages(&dqopt->files[cnt]->i_data, 0); 692 truncate_inode_pages(&dqopt->files[cnt]->i_data, 0);
694 inode_unlock(dqopt->files[cnt]); 693 inode_unlock(dqopt->files[cnt]);
695 } 694 }
696 mutex_unlock(&dqopt->dqonoff_mutex);
697 695
698 return 0; 696 return 0;
699} 697}
@@ -2050,21 +2048,13 @@ int dquot_get_next_id(struct super_block *sb, struct kqid *qid)
2050 struct quota_info *dqopt = sb_dqopt(sb); 2048 struct quota_info *dqopt = sb_dqopt(sb);
2051 int err; 2049 int err;
2052 2050
2053 mutex_lock(&dqopt->dqonoff_mutex); 2051 if (!sb_has_quota_active(sb, qid->type))
2054 if (!sb_has_quota_active(sb, qid->type)) { 2052 return -ESRCH;
2055 err = -ESRCH; 2053 if (!dqopt->ops[qid->type]->get_next_id)
2056 goto out; 2054 return -ENOSYS;
2057 }
2058 if (!dqopt->ops[qid->type]->get_next_id) {
2059 err = -ENOSYS;
2060 goto out;
2061 }
2062 mutex_lock(&dqopt->dqio_mutex); 2055 mutex_lock(&dqopt->dqio_mutex);
2063 err = dqopt->ops[qid->type]->get_next_id(sb, qid); 2056 err = dqopt->ops[qid->type]->get_next_id(sb, qid);
2064 mutex_unlock(&dqopt->dqio_mutex); 2057 mutex_unlock(&dqopt->dqio_mutex);
2065out:
2066 mutex_unlock(&dqopt->dqonoff_mutex);
2067
2068 return err; 2058 return err;
2069} 2059}
2070EXPORT_SYMBOL(dquot_get_next_id); 2060EXPORT_SYMBOL(dquot_get_next_id);
@@ -2762,7 +2752,6 @@ int dquot_get_state(struct super_block *sb, struct qc_state *state)
2762 struct quota_info *dqopt = sb_dqopt(sb); 2752 struct quota_info *dqopt = sb_dqopt(sb);
2763 int type; 2753 int type;
2764 2754
2765 mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
2766 memset(state, 0, sizeof(*state)); 2755 memset(state, 0, sizeof(*state));
2767 for (type = 0; type < MAXQUOTAS; type++) { 2756 for (type = 0; type < MAXQUOTAS; type++) {
2768 if (!sb_has_quota_active(sb, type)) 2757 if (!sb_has_quota_active(sb, type))
@@ -2784,7 +2773,6 @@ int dquot_get_state(struct super_block *sb, struct qc_state *state)
2784 tstate->nextents = 1; /* We don't know... */ 2773 tstate->nextents = 1; /* We don't know... */
2785 spin_unlock(&dq_data_lock); 2774 spin_unlock(&dq_data_lock);
2786 } 2775 }
2787 mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
2788 return 0; 2776 return 0;
2789} 2777}
2790EXPORT_SYMBOL(dquot_get_state); 2778EXPORT_SYMBOL(dquot_get_state);
@@ -2798,18 +2786,13 @@ int dquot_set_dqinfo(struct super_block *sb, int type, struct qc_info *ii)
2798 if ((ii->i_fieldmask & QC_WARNS_MASK) || 2786 if ((ii->i_fieldmask & QC_WARNS_MASK) ||
2799 (ii->i_fieldmask & QC_RT_SPC_TIMER)) 2787 (ii->i_fieldmask & QC_RT_SPC_TIMER))
2800 return -EINVAL; 2788 return -EINVAL;
2801 mutex_lock(&sb_dqopt(sb)->dqonoff_mutex); 2789 if (!sb_has_quota_active(sb, type))
2802 if (!sb_has_quota_active(sb, type)) { 2790 return -ESRCH;
2803 err = -ESRCH;
2804 goto out;
2805 }
2806 mi = sb_dqopt(sb)->info + type; 2791 mi = sb_dqopt(sb)->info + type;
2807 if (ii->i_fieldmask & QC_FLAGS) { 2792 if (ii->i_fieldmask & QC_FLAGS) {
2808 if ((ii->i_flags & QCI_ROOT_SQUASH && 2793 if ((ii->i_flags & QCI_ROOT_SQUASH &&
2809 mi->dqi_format->qf_fmt_id != QFMT_VFS_OLD)) { 2794 mi->dqi_format->qf_fmt_id != QFMT_VFS_OLD))
2810 err = -EINVAL; 2795 return -EINVAL;
2811 goto out;
2812 }
2813 } 2796 }
2814 spin_lock(&dq_data_lock); 2797 spin_lock(&dq_data_lock);
2815 if (ii->i_fieldmask & QC_SPC_TIMER) 2798 if (ii->i_fieldmask & QC_SPC_TIMER)
@@ -2826,8 +2809,6 @@ int dquot_set_dqinfo(struct super_block *sb, int type, struct qc_info *ii)
2826 mark_info_dirty(sb, type); 2809 mark_info_dirty(sb, type);
2827 /* Force write to disk */ 2810 /* Force write to disk */
2828 sb->dq_op->write_info(sb, type); 2811 sb->dq_op->write_info(sb, type);
2829out:
2830 mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
2831 return err; 2812 return err;
2832} 2813}
2833EXPORT_SYMBOL(dquot_set_dqinfo); 2814EXPORT_SYMBOL(dquot_set_dqinfo);
diff --git a/fs/quota/quota.c b/fs/quota/quota.c
index 6ce6f4b6826b..413c36cca462 100644
--- a/fs/quota/quota.c
+++ b/fs/quota/quota.c
@@ -104,13 +104,9 @@ static int quota_getfmt(struct super_block *sb, int type, void __user *addr)
104{ 104{
105 __u32 fmt; 105 __u32 fmt;
106 106
107 mutex_lock(&sb_dqopt(sb)->dqonoff_mutex); 107 if (!sb_has_quota_active(sb, type))
108 if (!sb_has_quota_active(sb, type)) {
109 mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
110 return -ESRCH; 108 return -ESRCH;
111 }
112 fmt = sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id; 109 fmt = sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id;
113 mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
114 if (copy_to_user(addr, &fmt, sizeof(fmt))) 110 if (copy_to_user(addr, &fmt, sizeof(fmt)))
115 return -EFAULT; 111 return -EFAULT;
116 return 0; 112 return 0;