Commit c99f1b0c authored by Zhao Lei's avatar Zhao Lei Committed by Chris Mason
Browse files

btrfs: Support busy loop of write and delete



Reproduce:
 while true; do
   dd if=/dev/zero of=/mnt/btrfs/file count=[75% fs_size]
   rm /mnt/btrfs/file
 done
 Then we can see above loop failed on NO_SPACE.

It it long-term problem since very beginning, because delayed-iput
after rm are not run.

We already have commit_transaction() in alloc_space code, but it is
not triggered in above case.
This patch trigger commit_transaction() to run delayed-iput and
reflash pinned-space to to make write success.

It is based on previous fix of delayed-iput in commit_transaction(),
need to be applied on top of:
btrfs: Fix NO_SPACE bug caused by delayed-iput

Signed-off-by: default avatarZhao Lei <zhaolei@cn.fujitsu.com>
Signed-off-by: default avatarChris Mason <clm@fb.com>
parent d7c15171
Loading
Loading
Loading
Loading
+14 −12
Original line number Original line Diff line number Diff line
@@ -3858,14 +3858,14 @@ int btrfs_check_data_free_space(struct inode *inode, u64 bytes)
	struct btrfs_fs_info *fs_info = root->fs_info;
	struct btrfs_fs_info *fs_info = root->fs_info;
	u64 used;
	u64 used;
	int ret = 0;
	int ret = 0;
	int committed = 0;
	int need_commit = 2;
	int have_pinned_space = 1;
	int have_pinned_space;


	/* make sure bytes are sectorsize aligned */
	/* make sure bytes are sectorsize aligned */
	bytes = ALIGN(bytes, root->sectorsize);
	bytes = ALIGN(bytes, root->sectorsize);


	if (btrfs_is_free_space_inode(inode)) {
	if (btrfs_is_free_space_inode(inode)) {
		committed = 1;
		need_commit = 0;
		ASSERT(current->journal_info);
		ASSERT(current->journal_info);
	}
	}


@@ -3915,9 +3915,11 @@ int btrfs_check_data_free_space(struct inode *inode, u64 bytes)
			if (ret < 0) {
			if (ret < 0) {
				if (ret != -ENOSPC)
				if (ret != -ENOSPC)
					return ret;
					return ret;
				else
				else {
					have_pinned_space = 1;
					goto commit_trans;
					goto commit_trans;
				}
				}
			}


			if (!data_sinfo)
			if (!data_sinfo)
				data_sinfo = fs_info->data_sinfo;
				data_sinfo = fs_info->data_sinfo;
@@ -3930,23 +3932,23 @@ int btrfs_check_data_free_space(struct inode *inode, u64 bytes)
		 * allocation, and no removed chunk in current transaction,
		 * allocation, and no removed chunk in current transaction,
		 * don't bother committing the transaction.
		 * don't bother committing the transaction.
		 */
		 */
		if (percpu_counter_compare(&data_sinfo->total_bytes_pinned,
		have_pinned_space = percpu_counter_compare(
					   used + bytes -
			&data_sinfo->total_bytes_pinned,
					   data_sinfo->total_bytes) < 0)
			used + bytes - data_sinfo->total_bytes);
			have_pinned_space = 0;
		spin_unlock(&data_sinfo->lock);
		spin_unlock(&data_sinfo->lock);


		/* commit the current transaction and try again */
		/* commit the current transaction and try again */
commit_trans:
commit_trans:
		if (!committed &&
		if (need_commit &&
		    !atomic_read(&root->fs_info->open_ioctl_trans)) {
		    !atomic_read(&root->fs_info->open_ioctl_trans)) {
			committed = 1;
			need_commit--;


			trans = btrfs_join_transaction(root);
			trans = btrfs_join_transaction(root);
			if (IS_ERR(trans))
			if (IS_ERR(trans))
				return PTR_ERR(trans);
				return PTR_ERR(trans);
			if (have_pinned_space ||
			if (have_pinned_space >= 0 ||
			    trans->transaction->have_free_bgs) {
			    trans->transaction->have_free_bgs ||
			    need_commit > 0) {
				ret = btrfs_commit_transaction(trans, root);
				ret = btrfs_commit_transaction(trans, root);
				if (ret)
				if (ret)
					return ret;
					return ret;