@@ -1185,9 +1185,24 @@ int btrfs_quota_disable(struct btrfs_fs_info *fs_info)
1185
1185
struct btrfs_trans_handle * trans = NULL ;
1186
1186
int ret = 0 ;
1187
1187
1188
+ /*
1189
+ * We need to have subvol_sem write locked, to prevent races between
1190
+ * concurrent tasks trying to disable quotas, because we will unlock
1191
+ * and relock qgroup_ioctl_lock across BTRFS_FS_QUOTA_ENABLED changes.
1192
+ */
1193
+ lockdep_assert_held_write (& fs_info -> subvol_sem );
1194
+
1188
1195
mutex_lock (& fs_info -> qgroup_ioctl_lock );
1189
1196
if (!fs_info -> quota_root )
1190
1197
goto out ;
1198
+
1199
+ /*
1200
+ * Request qgroup rescan worker to complete and wait for it. This wait
1201
+ * must be done before transaction start for quota disable since it may
1202
+ * deadlock with transaction by the qgroup rescan worker.
1203
+ */
1204
+ clear_bit (BTRFS_FS_QUOTA_ENABLED , & fs_info -> flags );
1205
+ btrfs_qgroup_wait_for_completion (fs_info , false);
1191
1206
mutex_unlock (& fs_info -> qgroup_ioctl_lock );
1192
1207
1193
1208
/*
@@ -1205,14 +1220,13 @@ int btrfs_quota_disable(struct btrfs_fs_info *fs_info)
1205
1220
if (IS_ERR (trans )) {
1206
1221
ret = PTR_ERR (trans );
1207
1222
trans = NULL ;
1223
+ set_bit (BTRFS_FS_QUOTA_ENABLED , & fs_info -> flags );
1208
1224
goto out ;
1209
1225
}
1210
1226
1211
1227
if (!fs_info -> quota_root )
1212
1228
goto out ;
1213
1229
1214
- clear_bit (BTRFS_FS_QUOTA_ENABLED , & fs_info -> flags );
1215
- btrfs_qgroup_wait_for_completion (fs_info , false);
1216
1230
spin_lock (& fs_info -> qgroup_lock );
1217
1231
quota_root = fs_info -> quota_root ;
1218
1232
fs_info -> quota_root = NULL ;
@@ -3383,6 +3397,9 @@ qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid,
3383
3397
btrfs_warn (fs_info ,
3384
3398
"qgroup rescan init failed, qgroup is not enabled" );
3385
3399
ret = - EINVAL ;
3400
+ } else if (!test_bit (BTRFS_FS_QUOTA_ENABLED , & fs_info -> flags )) {
3401
+ /* Quota disable is in progress */
3402
+ ret = - EBUSY ;
3386
3403
}
3387
3404
3388
3405
if (ret ) {
0 commit comments