diff options
| -rw-r--r-- | fs/btrfs/ctree.h | 6 | ||||
| -rw-r--r-- | fs/btrfs/disk-io.c | 1 | ||||
| -rw-r--r-- | fs/btrfs/qgroup.c | 43 | ||||
| -rw-r--r-- | fs/btrfs/transaction.c | 1 | ||||
| -rw-r--r-- | fs/btrfs/transaction.h | 14 | 
5 files changed, 63 insertions, 2 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 5474ef14d6e6..ec84e2dabb04 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -714,6 +714,12 @@ struct btrfs_delayed_root;   */  #define BTRFS_FS_EXCL_OP			16 +/* + * To info transaction_kthread we need an immediate commit so it doesn't + * need to wait for commit_interval + */ +#define BTRFS_FS_NEED_ASYNC_COMMIT		17 +  struct btrfs_fs_info {  	u8 fsid[BTRFS_FSID_SIZE];  	u8 chunk_tree_uuid[BTRFS_UUID_SIZE]; diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 4ac8b1d21baf..60caa68c3618 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1824,6 +1824,7 @@ static int transaction_kthread(void *arg)  		now = get_seconds();  		if (cur->state < TRANS_STATE_BLOCKED && +		    !test_bit(BTRFS_FS_NEED_ASYNC_COMMIT, &fs_info->flags) &&  		    (now < cur->start_time ||  		     now - cur->start_time < fs_info->commit_interval)) {  			spin_unlock(&fs_info->trans_lock); diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index 09c7e4fd550f..9fb758d5077a 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -11,6 +11,7 @@  #include <linux/slab.h>  #include <linux/workqueue.h>  #include <linux/btrfs.h> +#include <linux/sizes.h>  #include "ctree.h"  #include "transaction.h" @@ -2375,8 +2376,21 @@ out:  	return ret;  } -static bool qgroup_check_limits(const struct btrfs_qgroup *qg, u64 num_bytes) +/* + * Two limits to commit transaction in advance. + * + * For RATIO, it will be 1/RATIO of the remaining limit + * (excluding data and prealloc meta) as threshold. + * For SIZE, it will be in byte unit as threshold. + */ +#define QGROUP_PERTRANS_RATIO		32 +#define QGROUP_PERTRANS_SIZE		SZ_32M +static bool qgroup_check_limits(struct btrfs_fs_info *fs_info, +				const struct btrfs_qgroup *qg, u64 num_bytes)  { +	u64 limit; +	u64 threshold; +  	if ((qg->lim_flags & BTRFS_QGROUP_LIMIT_MAX_RFER) &&  	    qgroup_rsv_total(qg) + (s64)qg->rfer + num_bytes > qg->max_rfer)  		return false; @@ -2385,6 +2399,31 @@ static bool qgroup_check_limits(const struct btrfs_qgroup *qg, u64 num_bytes)  	    qgroup_rsv_total(qg) + (s64)qg->excl + num_bytes > qg->max_excl)  		return false; +	/* +	 * Even if we passed the check, it's better to check if reservation +	 * for meta_pertrans is pushing us near limit. +	 * If there is too much pertrans reservation or it's near the limit, +	 * let's try commit transaction to free some, using transaction_kthread +	 */ +	if ((qg->lim_flags & (BTRFS_QGROUP_LIMIT_MAX_RFER | +			      BTRFS_QGROUP_LIMIT_MAX_EXCL))) { +		if (qg->lim_flags & BTRFS_QGROUP_LIMIT_MAX_EXCL) +			limit = qg->max_excl; +		else +			limit = qg->max_rfer; +		threshold = (limit - qg->rsv.values[BTRFS_QGROUP_RSV_DATA] - +			    qg->rsv.values[BTRFS_QGROUP_RSV_META_PREALLOC]) / +			    QGROUP_PERTRANS_RATIO; +		threshold = min_t(u64, threshold, QGROUP_PERTRANS_SIZE); + +		/* +		 * Use transaction_kthread to commit transaction, so we no +		 * longer need to bother nested transaction nor lock context. +		 */ +		if (qg->rsv.values[BTRFS_QGROUP_RSV_META_PERTRANS] > threshold) +			btrfs_commit_transaction_locksafe(fs_info); +	} +  	return true;  } @@ -2434,7 +2473,7 @@ static int qgroup_reserve(struct btrfs_root *root, u64 num_bytes, bool enforce,  		qg = unode_aux_to_qgroup(unode); -		if (enforce && !qgroup_check_limits(qg, num_bytes)) { +		if (enforce && !qgroup_check_limits(fs_info, qg, num_bytes)) {  			ret = -EDQUOT;  			goto out;  		} diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 63fdcab64b01..c944b4769e3c 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -2267,6 +2267,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans)  	 */  	cur_trans->state = TRANS_STATE_COMPLETED;  	wake_up(&cur_trans->commit_wait); +	clear_bit(BTRFS_FS_NEED_ASYNC_COMMIT, &fs_info->flags);  	spin_lock(&fs_info->trans_lock);  	list_del_init(&cur_trans->list); diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h index c88fccd80bc5..d8c0826bc2c7 100644 --- a/fs/btrfs/transaction.h +++ b/fs/btrfs/transaction.h @@ -199,6 +199,20 @@ int btrfs_clean_one_deleted_snapshot(struct btrfs_root *root);  int btrfs_commit_transaction(struct btrfs_trans_handle *trans);  int btrfs_commit_transaction_async(struct btrfs_trans_handle *trans,  				   int wait_for_unblock); + +/* + * Try to commit transaction asynchronously, so this is safe to call + * even holding a spinlock. + * + * It's done by informing transaction_kthread to commit transaction without + * waiting for commit interval. + */ +static inline void btrfs_commit_transaction_locksafe( +		struct btrfs_fs_info *fs_info) +{ +	set_bit(BTRFS_FS_NEED_ASYNC_COMMIT, &fs_info->flags); +	wake_up_process(fs_info->transaction_kthread); +}  int btrfs_end_transaction_throttle(struct btrfs_trans_handle *trans);  int btrfs_should_end_transaction(struct btrfs_trans_handle *trans);  void btrfs_throttle(struct btrfs_fs_info *fs_info);  | 
