162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * fs/hmdfs/server_writeback.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <linux/fs.h>
1062306a36Sopenharmony_ci#include <linux/backing-dev.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "hmdfs.h"
1362306a36Sopenharmony_ci#include "hmdfs_trace.h"
1462306a36Sopenharmony_ci#include "server_writeback.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define HMDFS_SRV_WB_DEF_DIRTY_THRESH	50UL
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic void hmdfs_srv_wb_handler(struct work_struct *work)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	struct hmdfs_server_writeback *hswb = container_of(work,
2162306a36Sopenharmony_ci					      struct hmdfs_server_writeback,
2262306a36Sopenharmony_ci					      dirty_sb_writeback_work);
2362306a36Sopenharmony_ci	struct super_block *lower_sb = hswb->sbi->lower_sb;
2462306a36Sopenharmony_ci	int dirty_pages;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	if (writeback_in_progress(&lower_sb->s_bdi->wb) ||
2762306a36Sopenharmony_ci	    !down_read_trylock(&lower_sb->s_umount))
2862306a36Sopenharmony_ci		return;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	dirty_pages = hswb->dirty_nr_pages_to_wb;
3162306a36Sopenharmony_ci	writeback_inodes_sb_nr(lower_sb, dirty_pages, WB_REASON_FS_FREE_SPACE);
3262306a36Sopenharmony_ci	up_read(&lower_sb->s_umount);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	trace_hmdfs_start_srv_wb(hswb->sbi, dirty_pages, hswb->dirty_thresh_pg);
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_civoid hmdfs_server_check_writeback(struct hmdfs_server_writeback *hswb)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	unsigned long old_time, now;
4062306a36Sopenharmony_ci	int dirty_nr_pages;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	old_time = hswb->last_reset_time;
4362306a36Sopenharmony_ci	now = jiffies;
4462306a36Sopenharmony_ci	dirty_nr_pages = atomic_inc_return(&hswb->dirty_nr_pages);
4562306a36Sopenharmony_ci	if (time_after(now, old_time + HZ) &&
4662306a36Sopenharmony_ci	    cmpxchg(&hswb->last_reset_time, old_time, now) == old_time) {
4762306a36Sopenharmony_ci		/*
4862306a36Sopenharmony_ci		 * We calculate the speed of page dirting to handle
4962306a36Sopenharmony_ci		 * following situations:
5062306a36Sopenharmony_ci		 *
5162306a36Sopenharmony_ci		 * 1. Dense writing, average page writing speed
5262306a36Sopenharmony_ci		 *    exceeds @hswb->dirty_thresh_pg:
5362306a36Sopenharmony_ci		 *    0-1s 100MB
5462306a36Sopenharmony_ci		 * 2. Sporadic writing, average page writing speed
5562306a36Sopenharmony_ci		 *    belows @hswb->dirty_thresh_pg:
5662306a36Sopenharmony_ci		 *    0-0.1s	40MB
5762306a36Sopenharmony_ci		 *    3.1-3.2	20MB
5862306a36Sopenharmony_ci		 */
5962306a36Sopenharmony_ci		unsigned int writepage_speed;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci		writepage_speed = dirty_nr_pages / ((now - old_time) / HZ);
6262306a36Sopenharmony_ci		if (writepage_speed >= hswb->dirty_thresh_pg) {
6362306a36Sopenharmony_ci			/*
6462306a36Sopenharmony_ci			 * Writeback @hswb->dirty_nr_pages_to_wb pages in
6562306a36Sopenharmony_ci			 * server-writeback work. If work is delayed after
6662306a36Sopenharmony_ci			 * 1s, @hswb->dirty_nr_pages_to_wb could be assigned
6762306a36Sopenharmony_ci			 * another new value (eg. 60MB), the old value (eg.
6862306a36Sopenharmony_ci			 * 80MB) will be overwritten, which means 80MB data
6962306a36Sopenharmony_ci			 * will be omitted to writeback. We can tolerate this
7062306a36Sopenharmony_ci			 * situation, The writeback pressure is too high if
7162306a36Sopenharmony_ci			 * the previous work is not completed, so it's
7262306a36Sopenharmony_ci			 * meaningless to continue subsequent work.
7362306a36Sopenharmony_ci			 */
7462306a36Sopenharmony_ci			hswb->dirty_nr_pages_to_wb = dirty_nr_pages;
7562306a36Sopenharmony_ci			/*
7662306a36Sopenharmony_ci			 * There are 3 conditions to trigger queuing work:
7762306a36Sopenharmony_ci			 *
7862306a36Sopenharmony_ci			 * A. Server successfully handles writepage for client
7962306a36Sopenharmony_ci			 * B. Every 1 second interval
8062306a36Sopenharmony_ci			 * C. Speed for page dirting exceeds @dirty_thresh_pg
8162306a36Sopenharmony_ci			 */
8262306a36Sopenharmony_ci			queue_work(hswb->dirty_writeback_wq,
8362306a36Sopenharmony_ci				   &hswb->dirty_sb_writeback_work);
8462306a36Sopenharmony_ci		}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		/*
8762306a36Sopenharmony_ci		 * There is no need to account the number of dirty pages
8862306a36Sopenharmony_ci		 * from remote client very accurately. Allow the missing
8962306a36Sopenharmony_ci		 * count to increase by other process in the gap between
9062306a36Sopenharmony_ci		 * increment and zero out.
9162306a36Sopenharmony_ci		 */
9262306a36Sopenharmony_ci		atomic_set(&hswb->dirty_nr_pages, 0);
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_civoid hmdfs_destroy_server_writeback(struct hmdfs_sb_info *sbi)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	if (!sbi->h_swb)
9962306a36Sopenharmony_ci		return;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	flush_work(&sbi->h_swb->dirty_sb_writeback_work);
10262306a36Sopenharmony_ci	destroy_workqueue(sbi->h_swb->dirty_writeback_wq);
10362306a36Sopenharmony_ci	kfree(sbi->h_swb);
10462306a36Sopenharmony_ci	sbi->h_swb = NULL;
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ciint hmdfs_init_server_writeback(struct hmdfs_sb_info *sbi)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	struct hmdfs_server_writeback *hswb;
11062306a36Sopenharmony_ci	char name[HMDFS_WQ_NAME_LEN];
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	hswb = kzalloc(sizeof(struct hmdfs_server_writeback), GFP_KERNEL);
11362306a36Sopenharmony_ci	if (!hswb)
11462306a36Sopenharmony_ci		return -ENOMEM;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	hswb->sbi = sbi;
11762306a36Sopenharmony_ci	hswb->dirty_writeback_control = true;
11862306a36Sopenharmony_ci	hswb->dirty_thresh_pg = HMDFS_SRV_WB_DEF_DIRTY_THRESH <<
11962306a36Sopenharmony_ci				HMDFS_MB_TO_PAGE_SHIFT;
12062306a36Sopenharmony_ci	atomic_set(&hswb->dirty_nr_pages, 0);
12162306a36Sopenharmony_ci	hswb->last_reset_time = jiffies;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	snprintf(name, sizeof(name), "dfs_srv_wb%u", sbi->seq);
12462306a36Sopenharmony_ci	hswb->dirty_writeback_wq = create_singlethread_workqueue(name);
12562306a36Sopenharmony_ci	if (!hswb->dirty_writeback_wq) {
12662306a36Sopenharmony_ci		hmdfs_err("Failed to create server writeback workqueue!");
12762306a36Sopenharmony_ci		kfree(hswb);
12862306a36Sopenharmony_ci		return -ENOMEM;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci	INIT_WORK(&hswb->dirty_sb_writeback_work, hmdfs_srv_wb_handler);
13162306a36Sopenharmony_ci	sbi->h_swb = hswb;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	return 0;
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
136