162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * fs/hmdfs/stash.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/fs.h>
1062306a36Sopenharmony_ci#include <linux/file.h>
1162306a36Sopenharmony_ci#include <linux/dcache.h>
1262306a36Sopenharmony_ci#include <linux/namei.h>
1362306a36Sopenharmony_ci#include <linux/mount.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/list.h>
1662306a36Sopenharmony_ci#include <linux/pagemap.h>
1762306a36Sopenharmony_ci#include <linux/sched/mm.h>
1862306a36Sopenharmony_ci#include <linux/sched/task.h>
1962306a36Sopenharmony_ci#include <linux/errseq.h>
2062306a36Sopenharmony_ci#include <linux/crc32.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "stash.h"
2362306a36Sopenharmony_ci#include "comm/node_cb.h"
2462306a36Sopenharmony_ci#include "comm/protocol.h"
2562306a36Sopenharmony_ci#include "comm/connection.h"
2662306a36Sopenharmony_ci#include "file_remote.h"
2762306a36Sopenharmony_ci#include "hmdfs_dentryfile.h"
2862306a36Sopenharmony_ci#include "authority/authentication.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/* Head magic used to identify a stash file */
3162306a36Sopenharmony_ci#define HMDFS_STASH_FILE_HEAD_MAGIC 0xF7AB06C3
3262306a36Sopenharmony_ci/* Head and path in stash file are aligned with HMDFS_STASH_BLK_SIZE */
3362306a36Sopenharmony_ci#define HMDFS_STASH_BLK_SIZE 4096
3462306a36Sopenharmony_ci#define HMDFS_STASH_BLK_SHIFT 12
3562306a36Sopenharmony_ci#define HMDFS_STASH_PAGE_TO_SECTOR_SHIFT 3
3662306a36Sopenharmony_ci#define HMDFS_STASH_DIR_NAME "stash"
3762306a36Sopenharmony_ci#define HMDFS_STASH_FMT_DIR_NAME "v1"
3862306a36Sopenharmony_ci#define HMDFS_STASH_WORK_DIR_NAME \
3962306a36Sopenharmony_ci	(HMDFS_STASH_DIR_NAME "/" HMDFS_STASH_FMT_DIR_NAME)
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define HMDFS_STASH_FILE_NAME_LEN 20
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define HMDFS_STASH_FLUSH_CNT 2
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define HMDFS_STASH_PATH_LEN (HMDFS_CID_SIZE + HMDFS_STASH_FILE_NAME_LEN + 1)
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistruct hmdfs_cache_file_head {
4862306a36Sopenharmony_ci	__le32 magic;
4962306a36Sopenharmony_ci	__le32 crc_offset;
5062306a36Sopenharmony_ci	__le64 ino;
5162306a36Sopenharmony_ci	__le64 size;
5262306a36Sopenharmony_ci	__le64 blocks;
5362306a36Sopenharmony_ci	__le64 last_write_pos;
5462306a36Sopenharmony_ci	__le64 ctime;
5562306a36Sopenharmony_ci	__le32 ctime_nsec;
5662306a36Sopenharmony_ci	__le32 change_detect_cap;
5762306a36Sopenharmony_ci	__le64 ichange_count;
5862306a36Sopenharmony_ci	__le32 path_offs;
5962306a36Sopenharmony_ci	__le32 path_len;
6062306a36Sopenharmony_ci	__le32 path_cnt;
6162306a36Sopenharmony_ci	__le32 data_offs;
6262306a36Sopenharmony_ci	/* Attention: expand new fields in here to compatible with old ver */
6362306a36Sopenharmony_ci	__le32 crc32;
6462306a36Sopenharmony_ci} __packed;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistruct hmdfs_stash_work {
6762306a36Sopenharmony_ci	struct hmdfs_peer *conn;
6862306a36Sopenharmony_ci	struct list_head *list;
6962306a36Sopenharmony_ci	struct work_struct work;
7062306a36Sopenharmony_ci	struct completion done;
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistruct hmdfs_inode_tbl {
7462306a36Sopenharmony_ci	unsigned int cnt;
7562306a36Sopenharmony_ci	unsigned int max;
7662306a36Sopenharmony_ci	uint64_t inodes[0];
7762306a36Sopenharmony_ci};
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistruct hmdfs_stash_dir_context {
8062306a36Sopenharmony_ci	struct dir_context dctx;
8162306a36Sopenharmony_ci	char name[NAME_MAX + 1];
8262306a36Sopenharmony_ci	struct hmdfs_inode_tbl *tbl;
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistruct hmdfs_restore_stats {
8662306a36Sopenharmony_ci	unsigned int succeed;
8762306a36Sopenharmony_ci	unsigned int fail;
8862306a36Sopenharmony_ci	unsigned int keep;
8962306a36Sopenharmony_ci	unsigned long long ok_pages;
9062306a36Sopenharmony_ci	unsigned long long fail_pages;
9162306a36Sopenharmony_ci};
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistruct hmdfs_stash_stats {
9462306a36Sopenharmony_ci	unsigned int succeed;
9562306a36Sopenharmony_ci	unsigned int donothing;
9662306a36Sopenharmony_ci	unsigned int fail;
9762306a36Sopenharmony_ci	unsigned long long ok_pages;
9862306a36Sopenharmony_ci	unsigned long long fail_pages;
9962306a36Sopenharmony_ci};
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistruct hmdfs_file_restore_ctx {
10262306a36Sopenharmony_ci	struct hmdfs_peer *conn;
10362306a36Sopenharmony_ci	struct path src_dir_path;
10462306a36Sopenharmony_ci	struct path dst_root_path;
10562306a36Sopenharmony_ci	char *dst;
10662306a36Sopenharmony_ci	char *page;
10762306a36Sopenharmony_ci	struct file *src_filp;
10862306a36Sopenharmony_ci	uint64_t inum;
10962306a36Sopenharmony_ci	uint64_t pages;
11062306a36Sopenharmony_ci	unsigned int seq;
11162306a36Sopenharmony_ci	unsigned int data_offs;
11262306a36Sopenharmony_ci	/* output */
11362306a36Sopenharmony_ci	bool keep;
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistruct hmdfs_copy_args {
11762306a36Sopenharmony_ci	struct file *src;
11862306a36Sopenharmony_ci	struct file *dst;
11962306a36Sopenharmony_ci	void *buf;
12062306a36Sopenharmony_ci	size_t buf_len;
12162306a36Sopenharmony_ci	unsigned int seq;
12262306a36Sopenharmony_ci	unsigned int data_offs;
12362306a36Sopenharmony_ci	uint64_t inum;
12462306a36Sopenharmony_ci};
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistruct hmdfs_copy_ctx {
12762306a36Sopenharmony_ci	struct hmdfs_copy_args args;
12862306a36Sopenharmony_ci	loff_t src_pos;
12962306a36Sopenharmony_ci	loff_t dst_pos;
13062306a36Sopenharmony_ci	/* output */
13162306a36Sopenharmony_ci	size_t copied;
13262306a36Sopenharmony_ci	bool eof;
13362306a36Sopenharmony_ci};
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistruct hmdfs_rebuild_stats {
13662306a36Sopenharmony_ci	unsigned int succeed;
13762306a36Sopenharmony_ci	unsigned int total;
13862306a36Sopenharmony_ci	unsigned int fail;
13962306a36Sopenharmony_ci	unsigned int invalid;
14062306a36Sopenharmony_ci};
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistruct hmdfs_check_work {
14362306a36Sopenharmony_ci	struct hmdfs_peer *conn;
14462306a36Sopenharmony_ci	struct work_struct work;
14562306a36Sopenharmony_ci	struct completion done;
14662306a36Sopenharmony_ci};
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_citypedef int (*stash_operation_func)(struct hmdfs_peer *,
14962306a36Sopenharmony_ci				    unsigned int,
15062306a36Sopenharmony_ci				    struct path *,
15162306a36Sopenharmony_ci				    const struct hmdfs_inode_tbl *,
15262306a36Sopenharmony_ci				    void *);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic struct dentry *hmdfs_do_vfs_mkdir(struct dentry *parent,
15562306a36Sopenharmony_ci					 const char *name, int namelen,
15662306a36Sopenharmony_ci					 umode_t mode)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	struct inode *dir = d_inode(parent);
15962306a36Sopenharmony_ci	struct dentry *child = NULL;
16062306a36Sopenharmony_ci	int err;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	inode_lock_nested(dir, I_MUTEX_PARENT);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	child = lookup_one_len(name, parent, namelen);
16562306a36Sopenharmony_ci	if (IS_ERR(child))
16662306a36Sopenharmony_ci		goto out;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if (d_is_positive(child)) {
16962306a36Sopenharmony_ci		if (d_can_lookup(child))
17062306a36Sopenharmony_ci			goto out;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci		dput(child);
17362306a36Sopenharmony_ci		child = ERR_PTR(-EINVAL);
17462306a36Sopenharmony_ci		goto out;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	err = vfs_mkdir(&nop_mnt_idmap, dir, child, mode);
17862306a36Sopenharmony_ci	if (err) {
17962306a36Sopenharmony_ci		dput(child);
18062306a36Sopenharmony_ci		child = ERR_PTR(err);
18162306a36Sopenharmony_ci		goto out;
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ciout:
18562306a36Sopenharmony_ci	inode_unlock(dir);
18662306a36Sopenharmony_ci	return child;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistruct dentry *hmdfs_stash_new_work_dir(struct dentry *parent)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	struct dentry *base = NULL;
19262306a36Sopenharmony_ci	struct dentry *work = NULL;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	base = hmdfs_do_vfs_mkdir(parent, HMDFS_STASH_DIR_NAME,
19562306a36Sopenharmony_ci				   strlen(HMDFS_STASH_DIR_NAME), 0700);
19662306a36Sopenharmony_ci	if (IS_ERR(base))
19762306a36Sopenharmony_ci		return base;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	work = hmdfs_do_vfs_mkdir(base, HMDFS_STASH_FMT_DIR_NAME,
20062306a36Sopenharmony_ci				  strlen(HMDFS_STASH_FMT_DIR_NAME), 0700);
20162306a36Sopenharmony_ci	dput(base);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	return work;
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic struct file *hmdfs_new_stash_file(struct path *d_path, const char *cid)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	struct dentry *parent = NULL;
20962306a36Sopenharmony_ci	struct file *filp = NULL;
21062306a36Sopenharmony_ci	struct path stash;
21162306a36Sopenharmony_ci	int err;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	parent = hmdfs_do_vfs_mkdir(d_path->dentry, cid, strlen(cid), 0700);
21462306a36Sopenharmony_ci	if (IS_ERR(parent)) {
21562306a36Sopenharmony_ci		err = PTR_ERR(parent);
21662306a36Sopenharmony_ci		hmdfs_err("mkdir error %d", err);
21762306a36Sopenharmony_ci		goto mkdir_err;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	stash.mnt = d_path->mnt;
22162306a36Sopenharmony_ci	stash.dentry = parent;
22262306a36Sopenharmony_ci	filp = kernel_tmpfile_open(&nop_mnt_idmap, &stash, S_IFREG | 0600,
22362306a36Sopenharmony_ci	                         O_LARGEFILE | O_WRONLY, current_cred());
22462306a36Sopenharmony_ci	if (IS_ERR(filp)) {
22562306a36Sopenharmony_ci		err = PTR_ERR(filp);
22662306a36Sopenharmony_ci		hmdfs_err("open stash file error %d", err);
22762306a36Sopenharmony_ci		goto open_err;
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	dput(parent);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	return filp;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ciopen_err:
23562306a36Sopenharmony_ci	dput(parent);
23662306a36Sopenharmony_cimkdir_err:
23762306a36Sopenharmony_ci	return ERR_PTR(err);
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic inline bool hmdfs_is_dir(struct dentry *child)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	return d_is_positive(child) && d_can_lookup(child);
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic inline bool hmdfs_is_reg(struct dentry *child)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	return d_is_positive(child) && d_is_reg(child);
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic void hmdfs_set_stash_file_head(const struct hmdfs_cache_info *cache,
25162306a36Sopenharmony_ci				      uint64_t ino,
25262306a36Sopenharmony_ci				      struct hmdfs_cache_file_head *head)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	long long blocks;
25562306a36Sopenharmony_ci	unsigned int crc_offset;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	memset(head, 0, sizeof(*head));
25862306a36Sopenharmony_ci	head->magic = cpu_to_le32(HMDFS_STASH_FILE_HEAD_MAGIC);
25962306a36Sopenharmony_ci	head->ino = cpu_to_le64(ino);
26062306a36Sopenharmony_ci	head->size = cpu_to_le64(i_size_read(file_inode(cache->cache_file)));
26162306a36Sopenharmony_ci	blocks = atomic64_read(&cache->written_pgs) <<
26262306a36Sopenharmony_ci			       HMDFS_STASH_PAGE_TO_SECTOR_SHIFT;
26362306a36Sopenharmony_ci	head->blocks = cpu_to_le64(blocks);
26462306a36Sopenharmony_ci	head->path_offs = cpu_to_le32(cache->path_offs);
26562306a36Sopenharmony_ci	head->path_len = cpu_to_le32(cache->path_len);
26662306a36Sopenharmony_ci	head->path_cnt = cpu_to_le32(cache->path_cnt);
26762306a36Sopenharmony_ci	head->data_offs = cpu_to_le32(cache->data_offs);
26862306a36Sopenharmony_ci	crc_offset = offsetof(struct hmdfs_cache_file_head, crc32);
26962306a36Sopenharmony_ci	head->crc_offset = cpu_to_le32(crc_offset);
27062306a36Sopenharmony_ci	head->crc32 = cpu_to_le32(crc32(0, head, crc_offset));
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic int hmdfs_flush_stash_file_metadata(struct hmdfs_inode_info *info)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct hmdfs_cache_info *cache = NULL;
27662306a36Sopenharmony_ci	struct hmdfs_peer *conn = info->conn;
27762306a36Sopenharmony_ci	struct hmdfs_cache_file_head cache_head;
27862306a36Sopenharmony_ci	size_t written;
27962306a36Sopenharmony_ci	loff_t pos;
28062306a36Sopenharmony_ci	unsigned int head_size;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	/* No metadata if no cache file info */
28362306a36Sopenharmony_ci	cache = info->cache;
28462306a36Sopenharmony_ci	if (!cache)
28562306a36Sopenharmony_ci		return -EINVAL;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	if (strlen(cache->path) == 0) {
28862306a36Sopenharmony_ci		long long to_write_pgs = atomic64_read(&cache->to_write_pgs);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci		/* Nothing to stash. No need to flush meta data. */
29162306a36Sopenharmony_ci		if (to_write_pgs == 0)
29262306a36Sopenharmony_ci			return 0;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		hmdfs_err("peer 0x%x:0x%llx inode 0x%llx lost %lld pages due to no path",
29562306a36Sopenharmony_ci			  conn->owner, conn->device_id,
29662306a36Sopenharmony_ci			  info->remote_ino, to_write_pgs);
29762306a36Sopenharmony_ci		return -EINVAL;
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	hmdfs_set_stash_file_head(cache, info->remote_ino, &cache_head);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	/* Write head */
30362306a36Sopenharmony_ci	pos = 0;
30462306a36Sopenharmony_ci	head_size = sizeof(cache_head);
30562306a36Sopenharmony_ci	written = kernel_write(cache->cache_file, &cache_head, head_size, &pos);
30662306a36Sopenharmony_ci	if (written != head_size) {
30762306a36Sopenharmony_ci		hmdfs_err("stash peer 0x%x:0x%llx ino 0x%llx write head len %u err %zd",
30862306a36Sopenharmony_ci			   conn->owner, conn->device_id, info->remote_ino,
30962306a36Sopenharmony_ci			   head_size, written);
31062306a36Sopenharmony_ci		return -EIO;
31162306a36Sopenharmony_ci	}
31262306a36Sopenharmony_ci	/* Write path */
31362306a36Sopenharmony_ci	pos = (loff_t)cache->path_offs << HMDFS_STASH_BLK_SHIFT;
31462306a36Sopenharmony_ci	written = kernel_write(cache->cache_file, cache->path, cache->path_len,
31562306a36Sopenharmony_ci			       &pos);
31662306a36Sopenharmony_ci	if (written != cache->path_len) {
31762306a36Sopenharmony_ci		hmdfs_err("stash peer 0x%x:0x%llx ino 0x%llx write path len %u err %zd",
31862306a36Sopenharmony_ci			   conn->owner, conn->device_id, info->remote_ino,
31962306a36Sopenharmony_ci			   cache->path_len, written);
32062306a36Sopenharmony_ci		return -EIO;
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	return 0;
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci/* Mainly from inode_wait_for_writeback() */
32762306a36Sopenharmony_cistatic void hmdfs_wait_remote_writeback_once(struct hmdfs_peer *conn,
32862306a36Sopenharmony_ci					     struct hmdfs_inode_info *info)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	struct inode *inode = &info->vfs_inode;
33162306a36Sopenharmony_ci	DEFINE_WAIT_BIT(wq, &inode->i_state, __I_SYNC);
33262306a36Sopenharmony_ci	wait_queue_head_t *wq_head = NULL;
33362306a36Sopenharmony_ci	bool in_sync = false;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	spin_lock(&inode->i_lock);
33662306a36Sopenharmony_ci	in_sync = inode->i_state & I_SYNC;
33762306a36Sopenharmony_ci	spin_unlock(&inode->i_lock);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	if (!in_sync)
34062306a36Sopenharmony_ci		return;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	hmdfs_info("peer 0x%x:0x%llx ino 0x%llx wait for wb once",
34362306a36Sopenharmony_ci		   conn->owner, conn->device_id, info->remote_ino);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	wq_head = bit_waitqueue(&inode->i_state, __I_SYNC);
34662306a36Sopenharmony_ci	__wait_on_bit(wq_head, &wq, bit_wait, TASK_UNINTERRUPTIBLE);
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic void hmdfs_reset_remote_write_err(struct hmdfs_peer *conn,
35062306a36Sopenharmony_ci					 struct hmdfs_inode_info *info)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	struct address_space *mapping = info->vfs_inode.i_mapping;
35362306a36Sopenharmony_ci	int flags_err;
35462306a36Sopenharmony_ci	errseq_t old;
35562306a36Sopenharmony_ci	int wb_err;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	flags_err = filemap_check_errors(mapping);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	old = errseq_sample(&mapping->wb_err);
36062306a36Sopenharmony_ci	wb_err = errseq_check_and_advance(&mapping->wb_err, &old);
36162306a36Sopenharmony_ci	if (flags_err || wb_err)
36262306a36Sopenharmony_ci		hmdfs_warning("peer 0x%x:0x%llx inode 0x%llx wb error %d %d before stash",
36362306a36Sopenharmony_ci			      conn->owner, conn->device_id, info->remote_ino,
36462306a36Sopenharmony_ci			      flags_err, wb_err);
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistatic bool hmdfs_is_mapping_clean(struct address_space *mapping)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	bool clean = false;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	/* b93b016313b3b ("page cache: use xa_lock") introduces i_pages */
37262306a36Sopenharmony_ci#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
37362306a36Sopenharmony_ci	xa_lock_irq(&mapping->i_pages);
37462306a36Sopenharmony_ci#else
37562306a36Sopenharmony_ci	spin_lock_irq(&mapping->tree_lock);
37662306a36Sopenharmony_ci#endif
37762306a36Sopenharmony_ci	clean = !mapping_tagged(mapping, PAGECACHE_TAG_DIRTY) &&
37862306a36Sopenharmony_ci		!mapping_tagged(mapping, PAGECACHE_TAG_WRITEBACK);
37962306a36Sopenharmony_ci#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
38062306a36Sopenharmony_ci	xa_unlock_irq(&mapping->i_pages);
38162306a36Sopenharmony_ci#else
38262306a36Sopenharmony_ci	spin_unlock_irq(&mapping->tree_lock);
38362306a36Sopenharmony_ci#endif
38462306a36Sopenharmony_ci	return clean;
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_cistatic int hmdfs_flush_stash_file_data(struct hmdfs_peer *conn,
38862306a36Sopenharmony_ci				       struct hmdfs_inode_info *info)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	struct inode *inode = &info->vfs_inode;
39162306a36Sopenharmony_ci	struct address_space *mapping = inode->i_mapping;
39262306a36Sopenharmony_ci	bool all_clean = true;
39362306a36Sopenharmony_ci	int err = 0;
39462306a36Sopenharmony_ci	int i;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	/* Wait for the completion of write syscall */
39762306a36Sopenharmony_ci	inode_lock(inode);
39862306a36Sopenharmony_ci	inode_unlock(inode);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	all_clean = hmdfs_is_mapping_clean(mapping);
40162306a36Sopenharmony_ci	if (all_clean) {
40262306a36Sopenharmony_ci		hmdfs_reset_remote_write_err(conn, info);
40362306a36Sopenharmony_ci		return 0;
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	/*
40762306a36Sopenharmony_ci	 * No-sync_all writeback during offline may have not seen
40862306a36Sopenharmony_ci	 * the setting of stash_status as HMDFS_REMOTE_INODE_STASHING
40962306a36Sopenharmony_ci	 * and will call mapping_set_error() after we just reset
41062306a36Sopenharmony_ci	 * the previous error. So waiting for these writeback once,
41162306a36Sopenharmony_ci	 * and the following writeback will do local write.
41262306a36Sopenharmony_ci	 */
41362306a36Sopenharmony_ci	hmdfs_wait_remote_writeback_once(conn, info);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	/* Need to clear previous error ? */
41662306a36Sopenharmony_ci	hmdfs_reset_remote_write_err(conn, info);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	/*
41962306a36Sopenharmony_ci	 * 1. dirty page: do write back
42062306a36Sopenharmony_ci	 * 2. writeback page: wait for its completion
42162306a36Sopenharmony_ci	 * 3. writeback -> redirty page: do filemap_write_and_wait()
42262306a36Sopenharmony_ci	 *    twice, so 2th writeback should not allow
42362306a36Sopenharmony_ci	 *    writeback -> redirty transition
42462306a36Sopenharmony_ci	 */
42562306a36Sopenharmony_ci	for (i = 0; i < HMDFS_STASH_FLUSH_CNT; i++) {
42662306a36Sopenharmony_ci		err = filemap_write_and_wait(mapping);
42762306a36Sopenharmony_ci		if (err) {
42862306a36Sopenharmony_ci			hmdfs_err("peer 0x%x:0x%llx inode 0x%llx #%d stash flush error %d",
42962306a36Sopenharmony_ci				  conn->owner, conn->device_id,
43062306a36Sopenharmony_ci				  info->remote_ino, i, err);
43162306a36Sopenharmony_ci			return err;
43262306a36Sopenharmony_ci		}
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (!hmdfs_is_mapping_clean(mapping))
43662306a36Sopenharmony_ci		hmdfs_err("peer 0x%x:0x%llx inode 0x%llx is still dirty dt %d wb %d",
43762306a36Sopenharmony_ci			  conn->owner, conn->device_id, info->remote_ino,
43862306a36Sopenharmony_ci			  !!mapping_tagged(mapping, PAGECACHE_TAG_DIRTY),
43962306a36Sopenharmony_ci			  !!mapping_tagged(mapping, PAGECACHE_TAG_WRITEBACK));
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	return 0;
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic int hmdfs_flush_stash_file(struct hmdfs_inode_info *info)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	int err;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	err = hmdfs_flush_stash_file_data(info->conn, info);
44962306a36Sopenharmony_ci	if (!err)
45062306a36Sopenharmony_ci		err = hmdfs_flush_stash_file_metadata(info);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	return err;
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic int hmdfs_enable_stash_file(struct hmdfs_inode_info *info,
45662306a36Sopenharmony_ci				   struct dentry *stash)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	char name[HMDFS_STASH_FILE_NAME_LEN];
45962306a36Sopenharmony_ci	struct dentry *parent = NULL;
46062306a36Sopenharmony_ci	struct inode *dir = NULL;
46162306a36Sopenharmony_ci	struct dentry *child = NULL;
46262306a36Sopenharmony_ci	int err = 0;
46362306a36Sopenharmony_ci	bool retried = false;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	snprintf(name, sizeof(name), "0x%llx", info->remote_ino);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	parent = lock_parent(stash);
46862306a36Sopenharmony_ci	dir = d_inode(parent);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cilookup_again:
47162306a36Sopenharmony_ci	child = lookup_one_len(name, parent, strlen(name));
47262306a36Sopenharmony_ci	if (IS_ERR(child)) {
47362306a36Sopenharmony_ci		err = PTR_ERR(child);
47462306a36Sopenharmony_ci		child = NULL;
47562306a36Sopenharmony_ci		hmdfs_err("lookup %s err %d", name, err);
47662306a36Sopenharmony_ci		goto out;
47762306a36Sopenharmony_ci	}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	if (d_is_positive(child)) {
48062306a36Sopenharmony_ci		hmdfs_warning("%s exists (mode 0%o)",
48162306a36Sopenharmony_ci			      name, d_inode(child)->i_mode);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci		err = vfs_unlink(&nop_mnt_idmap, dir, child, NULL);
48462306a36Sopenharmony_ci		if (err) {
48562306a36Sopenharmony_ci			hmdfs_err("unlink %s err %d", name, err);
48662306a36Sopenharmony_ci			goto out;
48762306a36Sopenharmony_ci		}
48862306a36Sopenharmony_ci		if (retried) {
48962306a36Sopenharmony_ci			err = -EEXIST;
49062306a36Sopenharmony_ci			goto out;
49162306a36Sopenharmony_ci		}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci		retried = true;
49462306a36Sopenharmony_ci		dput(child);
49562306a36Sopenharmony_ci		goto lookup_again;
49662306a36Sopenharmony_ci	}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	err = vfs_link(stash, &nop_mnt_idmap, dir, child, NULL);
49962306a36Sopenharmony_ci	if (err) {
50062306a36Sopenharmony_ci		hmdfs_err("link stash file to %s err %d", name, err);
50162306a36Sopenharmony_ci		goto out;
50262306a36Sopenharmony_ci	}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ciout:
50562306a36Sopenharmony_ci	unlock_dir(parent);
50662306a36Sopenharmony_ci	if (child)
50762306a36Sopenharmony_ci		dput(child);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	return err;
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci/* Return 1 if stash is done, 0 if nothing is stashed */
51362306a36Sopenharmony_cistatic int hmdfs_close_stash_file(struct hmdfs_peer *conn,
51462306a36Sopenharmony_ci				  struct hmdfs_inode_info *info)
51562306a36Sopenharmony_ci{
51662306a36Sopenharmony_ci	struct file *cache_file = info->cache->cache_file;
51762306a36Sopenharmony_ci	struct dentry *c_dentry = file_dentry(cache_file);
51862306a36Sopenharmony_ci	struct inode *c_inode = d_inode(c_dentry);
51962306a36Sopenharmony_ci	long long to_write_pgs = atomic64_read(&info->cache->to_write_pgs);
52062306a36Sopenharmony_ci	int err;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	hmdfs_info("peer 0x%x:0x%llx inode 0x%llx stashed bytes %lld pages %lld",
52362306a36Sopenharmony_ci		   conn->owner, conn->device_id, info->remote_ino,
52462306a36Sopenharmony_ci		   i_size_read(c_inode), to_write_pgs);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	if (to_write_pgs == 0)
52762306a36Sopenharmony_ci		return 0;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	err = vfs_fsync(cache_file, 0);
53062306a36Sopenharmony_ci	if (!err)
53162306a36Sopenharmony_ci		err = hmdfs_enable_stash_file(info, c_dentry);
53262306a36Sopenharmony_ci	else
53362306a36Sopenharmony_ci		hmdfs_err("fsync stash file err %d", err);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	return err < 0 ? err : 1;
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cistatic void hmdfs_del_file_cache(struct hmdfs_cache_info *cache)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	if (!cache)
54162306a36Sopenharmony_ci		return;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	fput(cache->cache_file);
54462306a36Sopenharmony_ci	kfree(cache->path_buf);
54562306a36Sopenharmony_ci	kfree(cache);
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_cistatic struct hmdfs_cache_info *
54962306a36Sopenharmony_cihmdfs_new_file_cache(struct hmdfs_peer *conn, struct hmdfs_inode_info *info)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	struct hmdfs_cache_info *cache = NULL;
55262306a36Sopenharmony_ci	struct dentry *stash_dentry = NULL;
55362306a36Sopenharmony_ci	int err;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	cache = kzalloc(sizeof(*cache), GFP_KERNEL);
55662306a36Sopenharmony_ci	if (!cache)
55762306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	atomic64_set(&cache->to_write_pgs, 0);
56062306a36Sopenharmony_ci	atomic64_set(&cache->written_pgs, 0);
56162306a36Sopenharmony_ci	cache->path_buf = kmalloc(PATH_MAX, GFP_KERNEL);
56262306a36Sopenharmony_ci	if (!cache->path_buf) {
56362306a36Sopenharmony_ci		err = -ENOMEM;
56462306a36Sopenharmony_ci		goto free_cache;
56562306a36Sopenharmony_ci	}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	/* Need to handle "hardlink" ? */
56862306a36Sopenharmony_ci	stash_dentry = d_find_any_alias(&info->vfs_inode);
56962306a36Sopenharmony_ci	if (stash_dentry) {
57062306a36Sopenharmony_ci		/* Needs full path in hmdfs, will be a device-view path */
57162306a36Sopenharmony_ci		cache->path = dentry_path_raw(stash_dentry, cache->path_buf,
57262306a36Sopenharmony_ci					      PATH_MAX);
57362306a36Sopenharmony_ci		dput(stash_dentry);
57462306a36Sopenharmony_ci		if (IS_ERR(cache->path)) {
57562306a36Sopenharmony_ci			err = PTR_ERR(cache->path);
57662306a36Sopenharmony_ci			hmdfs_err("peer 0x%x:0x%llx inode 0x%llx gen path err %d",
57762306a36Sopenharmony_ci				  conn->owner, conn->device_id,
57862306a36Sopenharmony_ci				  info->remote_ino, err);
57962306a36Sopenharmony_ci			goto free_path;
58062306a36Sopenharmony_ci		}
58162306a36Sopenharmony_ci	} else {
58262306a36Sopenharmony_ci		/* Write-opened file was closed before finding dentry */
58362306a36Sopenharmony_ci		hmdfs_info("peer 0x%x:0x%llx inode 0x%llx no dentry found",
58462306a36Sopenharmony_ci			   conn->owner, conn->device_id, info->remote_ino);
58562306a36Sopenharmony_ci		cache->path_buf[0] = '\0';
58662306a36Sopenharmony_ci		cache->path = cache->path_buf;
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	cache->path_cnt = 1;
59062306a36Sopenharmony_ci	cache->path_len = strlen(cache->path) + 1;
59162306a36Sopenharmony_ci	cache->path_offs = DIV_ROUND_UP(sizeof(struct hmdfs_cache_file_head),
59262306a36Sopenharmony_ci					HMDFS_STASH_BLK_SIZE);
59362306a36Sopenharmony_ci	cache->data_offs = cache->path_offs + DIV_ROUND_UP(cache->path_len,
59462306a36Sopenharmony_ci					HMDFS_STASH_BLK_SIZE);
59562306a36Sopenharmony_ci	cache->cache_file = hmdfs_new_stash_file(&conn->sbi->stash_work_dir,
59662306a36Sopenharmony_ci						 conn->cid);
59762306a36Sopenharmony_ci	if (IS_ERR(cache->cache_file)) {
59862306a36Sopenharmony_ci		err = PTR_ERR(cache->cache_file);
59962306a36Sopenharmony_ci		goto free_path;
60062306a36Sopenharmony_ci	}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	return cache;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_cifree_path:
60562306a36Sopenharmony_ci	kfree(cache->path_buf);
60662306a36Sopenharmony_cifree_cache:
60762306a36Sopenharmony_ci	kfree(cache);
60862306a36Sopenharmony_ci	return ERR_PTR(err);
60962306a36Sopenharmony_ci}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_cistatic void hmdfs_init_stash_file_cache(struct hmdfs_peer *conn,
61262306a36Sopenharmony_ci					struct hmdfs_inode_info *info)
61362306a36Sopenharmony_ci{
61462306a36Sopenharmony_ci	struct hmdfs_cache_info *cache = NULL;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	cache = hmdfs_new_file_cache(conn, info);
61762306a36Sopenharmony_ci	if (IS_ERR(cache))
61862306a36Sopenharmony_ci		/*
61962306a36Sopenharmony_ci		 * Continue even creating stash info failed.
62062306a36Sopenharmony_ci		 * We need to ensure there is no dirty pages
62162306a36Sopenharmony_ci		 * after stash completes
62262306a36Sopenharmony_ci		 */
62362306a36Sopenharmony_ci		cache = NULL;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	/* Make write() returns */
62662306a36Sopenharmony_ci	spin_lock(&info->stash_lock);
62762306a36Sopenharmony_ci	info->cache = cache;
62862306a36Sopenharmony_ci	info->stash_status = HMDFS_REMOTE_INODE_STASHING;
62962306a36Sopenharmony_ci	spin_unlock(&info->stash_lock);
63062306a36Sopenharmony_ci}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_cistatic void hmdfs_update_stash_stats(struct hmdfs_stash_stats *stats,
63362306a36Sopenharmony_ci				     const struct hmdfs_cache_info *cache,
63462306a36Sopenharmony_ci				     int err)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	unsigned long long ok_pages, fail_pages;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	if (cache) {
63962306a36Sopenharmony_ci		ok_pages = err > 0 ? atomic64_read(&cache->written_pgs) : 0;
64062306a36Sopenharmony_ci		fail_pages = atomic64_read(&cache->to_write_pgs) - ok_pages;
64162306a36Sopenharmony_ci		stats->ok_pages += ok_pages;
64262306a36Sopenharmony_ci		stats->fail_pages += fail_pages;
64362306a36Sopenharmony_ci	}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	if (err > 0)
64662306a36Sopenharmony_ci		stats->succeed++;
64762306a36Sopenharmony_ci	else if (!err)
64862306a36Sopenharmony_ci		stats->donothing++;
64962306a36Sopenharmony_ci	else
65062306a36Sopenharmony_ci		stats->fail++;
65162306a36Sopenharmony_ci}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci/* Return 1 if stash is done, 0 if nothing is stashed */
65462306a36Sopenharmony_cistatic int hmdfs_stash_remote_inode(struct hmdfs_inode_info *info,
65562306a36Sopenharmony_ci				    struct hmdfs_stash_stats *stats)
65662306a36Sopenharmony_ci{
65762306a36Sopenharmony_ci	struct hmdfs_cache_info *cache = info->cache;
65862306a36Sopenharmony_ci	struct hmdfs_peer *conn = info->conn;
65962306a36Sopenharmony_ci	unsigned int status;
66062306a36Sopenharmony_ci	int err = 0;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	hmdfs_info("stash peer 0x%x:0x%llx ino 0x%llx",
66362306a36Sopenharmony_ci		   conn->owner, conn->device_id, info->remote_ino);
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	err = hmdfs_flush_stash_file(info);
66662306a36Sopenharmony_ci	if (!err)
66762306a36Sopenharmony_ci		err = hmdfs_close_stash_file(conn, info);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	if (err <= 0)
67062306a36Sopenharmony_ci		set_bit(HMDFS_FID_NEED_OPEN, &info->fid_flags);
67162306a36Sopenharmony_ci	status = err > 0 ? HMDFS_REMOTE_INODE_RESTORING :
67262306a36Sopenharmony_ci			   HMDFS_REMOTE_INODE_NONE;
67362306a36Sopenharmony_ci	spin_lock(&info->stash_lock);
67462306a36Sopenharmony_ci	info->cache = NULL;
67562306a36Sopenharmony_ci	/*
67662306a36Sopenharmony_ci	 * Use smp_store_release() to ensure order between HMDFS_FID_NEED_OPEN
67762306a36Sopenharmony_ci	 * and HMDFS_REMOTE_INODE_NONE.
67862306a36Sopenharmony_ci	 */
67962306a36Sopenharmony_ci	smp_store_release(&info->stash_status, status);
68062306a36Sopenharmony_ci	spin_unlock(&info->stash_lock);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	hmdfs_update_stash_stats(stats, cache, err);
68362306a36Sopenharmony_ci	hmdfs_del_file_cache(cache);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	return err;
68662306a36Sopenharmony_ci}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_cistatic void hmdfs_init_cache_for_stash_files(struct hmdfs_peer *conn,
68962306a36Sopenharmony_ci					     struct list_head *list)
69062306a36Sopenharmony_ci{
69162306a36Sopenharmony_ci	const struct cred *old_cred = NULL;
69262306a36Sopenharmony_ci	struct hmdfs_inode_info *info = NULL;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	/* For file creation under stash_work_dir */
69562306a36Sopenharmony_ci	old_cred = hmdfs_override_creds(conn->sbi->cred);
69662306a36Sopenharmony_ci	list_for_each_entry(info, list, stash_node)
69762306a36Sopenharmony_ci		hmdfs_init_stash_file_cache(conn, info);
69862306a36Sopenharmony_ci	hmdfs_revert_creds(old_cred);
69962306a36Sopenharmony_ci}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_cistatic void hmdfs_init_stash_cache_work_fn(struct work_struct *base)
70262306a36Sopenharmony_ci{
70362306a36Sopenharmony_ci	struct hmdfs_stash_work *work =
70462306a36Sopenharmony_ci		container_of(base, struct hmdfs_stash_work, work);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	hmdfs_init_cache_for_stash_files(work->conn, work->list);
70762306a36Sopenharmony_ci	complete(&work->done);
70862306a36Sopenharmony_ci}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_cistatic void hmdfs_init_cache_for_stash_files_by_work(struct hmdfs_peer *conn,
71162306a36Sopenharmony_ci						     struct list_head *list)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	struct hmdfs_stash_work work = {
71462306a36Sopenharmony_ci		.conn = conn,
71562306a36Sopenharmony_ci		.list = list,
71662306a36Sopenharmony_ci		.done = COMPLETION_INITIALIZER_ONSTACK(work.done),
71762306a36Sopenharmony_ci	};
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	INIT_WORK_ONSTACK(&work.work, hmdfs_init_stash_cache_work_fn);
72062306a36Sopenharmony_ci	schedule_work(&work.work);
72162306a36Sopenharmony_ci	wait_for_completion(&work.done);
72262306a36Sopenharmony_ci}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_cistatic void hmdfs_stash_fetch_ready_files(struct hmdfs_peer *conn,
72562306a36Sopenharmony_ci					  bool check, struct list_head *list)
72662306a36Sopenharmony_ci{
72762306a36Sopenharmony_ci	struct hmdfs_inode_info *info = NULL;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	spin_lock(&conn->wr_opened_inode_lock);
73062306a36Sopenharmony_ci	list_for_each_entry(info, &conn->wr_opened_inode_list, wr_opened_node) {
73162306a36Sopenharmony_ci		int status;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci		/* Paired with *_release() in hmdfs_reset_stashed_inode() */
73462306a36Sopenharmony_ci		status = smp_load_acquire(&info->stash_status);
73562306a36Sopenharmony_ci		if (status == HMDFS_REMOTE_INODE_NONE) {
73662306a36Sopenharmony_ci			list_add_tail(&info->stash_node, list);
73762306a36Sopenharmony_ci			/*
73862306a36Sopenharmony_ci			 * Prevent close() removing the inode from
73962306a36Sopenharmony_ci			 * writeable-opened inode list
74062306a36Sopenharmony_ci			 */
74162306a36Sopenharmony_ci			hmdfs_remote_add_wr_opened_inode_nolock(conn, info);
74262306a36Sopenharmony_ci			/* Prevent the inode from eviction */
74362306a36Sopenharmony_ci			ihold(&info->vfs_inode);
74462306a36Sopenharmony_ci		} else if (check && status == HMDFS_REMOTE_INODE_STASHING) {
74562306a36Sopenharmony_ci			hmdfs_warning("peer 0x%x:0x%llx inode 0x%llx unexpected stash status %d",
74662306a36Sopenharmony_ci				      conn->owner, conn->device_id,
74762306a36Sopenharmony_ci				      info->remote_ino, status);
74862306a36Sopenharmony_ci		}
74962306a36Sopenharmony_ci	}
75062306a36Sopenharmony_ci	spin_unlock(&conn->wr_opened_inode_lock);
75162306a36Sopenharmony_ci}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_cistatic void hmdfs_stash_offline_prepare(struct hmdfs_peer *conn, int evt,
75462306a36Sopenharmony_ci					unsigned int seq)
75562306a36Sopenharmony_ci{
75662306a36Sopenharmony_ci	LIST_HEAD(preparing);
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	if (!hmdfs_is_stash_enabled(conn->sbi))
75962306a36Sopenharmony_ci		return;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	mutex_lock(&conn->offline_cb_lock);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	hmdfs_stash_fetch_ready_files(conn, true, &preparing);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	if (list_empty(&preparing))
76662306a36Sopenharmony_ci		goto out;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	hmdfs_init_cache_for_stash_files_by_work(conn, &preparing);
76962306a36Sopenharmony_ciout:
77062306a36Sopenharmony_ci	mutex_unlock(&conn->offline_cb_lock);
77162306a36Sopenharmony_ci}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_cistatic void hmdfs_track_inode_locked(struct hmdfs_peer *conn,
77462306a36Sopenharmony_ci				     struct hmdfs_inode_info *info)
77562306a36Sopenharmony_ci{
77662306a36Sopenharmony_ci	spin_lock(&conn->stashed_inode_lock);
77762306a36Sopenharmony_ci	list_add_tail(&info->stash_node, &conn->stashed_inode_list);
77862306a36Sopenharmony_ci	conn->stashed_inode_nr++;
77962306a36Sopenharmony_ci	spin_unlock(&conn->stashed_inode_lock);
78062306a36Sopenharmony_ci}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_cistatic void
78362306a36Sopenharmony_cihmdfs_update_peer_stash_stats(struct hmdfs_stash_statistics *stash_stats,
78462306a36Sopenharmony_ci			      const struct hmdfs_stash_stats *stats)
78562306a36Sopenharmony_ci{
78662306a36Sopenharmony_ci	stash_stats->cur_ok = stats->succeed;
78762306a36Sopenharmony_ci	stash_stats->cur_nothing = stats->donothing;
78862306a36Sopenharmony_ci	stash_stats->cur_fail = stats->fail;
78962306a36Sopenharmony_ci	stash_stats->total_ok += stats->succeed;
79062306a36Sopenharmony_ci	stash_stats->total_nothing += stats->donothing;
79162306a36Sopenharmony_ci	stash_stats->total_fail += stats->fail;
79262306a36Sopenharmony_ci	stash_stats->ok_pages += stats->ok_pages;
79362306a36Sopenharmony_ci	stash_stats->fail_pages += stats->fail_pages;
79462306a36Sopenharmony_ci}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_cistatic void hmdfs_stash_remote_inodes(struct hmdfs_peer *conn,
79762306a36Sopenharmony_ci				      struct list_head *list)
79862306a36Sopenharmony_ci{
79962306a36Sopenharmony_ci	const struct cred *old_cred = NULL;
80062306a36Sopenharmony_ci	struct hmdfs_inode_info *info = NULL;
80162306a36Sopenharmony_ci	struct hmdfs_inode_info *next = NULL;
80262306a36Sopenharmony_ci	struct hmdfs_stash_stats stats;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	/* For file creation, write and relink under stash_work_dir */
80562306a36Sopenharmony_ci	old_cred = hmdfs_override_creds(conn->sbi->cred);
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	memset(&stats, 0, sizeof(stats));
80862306a36Sopenharmony_ci	list_for_each_entry_safe(info, next, list, stash_node) {
80962306a36Sopenharmony_ci		int err;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci		list_del_init(&info->stash_node);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci		err = hmdfs_stash_remote_inode(info, &stats);
81462306a36Sopenharmony_ci		if (err > 0)
81562306a36Sopenharmony_ci			hmdfs_track_inode_locked(conn, info);
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci		hmdfs_remote_del_wr_opened_inode(conn, info);
81862306a36Sopenharmony_ci		if (err <= 0)
81962306a36Sopenharmony_ci			iput(&info->vfs_inode);
82062306a36Sopenharmony_ci	}
82162306a36Sopenharmony_ci	hmdfs_revert_creds(old_cred);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	hmdfs_update_peer_stash_stats(&conn->stats.stash, &stats);
82462306a36Sopenharmony_ci	hmdfs_info("peer 0x%x:0x%llx total stashed %u cur ok %u none %u fail %u",
82562306a36Sopenharmony_ci		   conn->owner, conn->device_id, conn->stashed_inode_nr,
82662306a36Sopenharmony_ci		   stats.succeed, stats.donothing, stats.fail);
82762306a36Sopenharmony_ci}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_cistatic void hmdfs_stash_offline_do_stash(struct hmdfs_peer *conn, int evt,
83062306a36Sopenharmony_ci					 unsigned int seq)
83162306a36Sopenharmony_ci{
83262306a36Sopenharmony_ci	struct hmdfs_inode_info *info = NULL;
83362306a36Sopenharmony_ci	LIST_HEAD(preparing);
83462306a36Sopenharmony_ci	LIST_HEAD(stashing);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	if (!hmdfs_is_stash_enabled(conn->sbi))
83762306a36Sopenharmony_ci		return;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	/* release seq_lock to prevent blocking no-offline sync cb */
84062306a36Sopenharmony_ci	mutex_unlock(&conn->seq_lock);
84162306a36Sopenharmony_ci	/* acquire offline_cb_lock to serialized with offline sync cb */
84262306a36Sopenharmony_ci	mutex_lock(&conn->offline_cb_lock);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	hmdfs_stash_fetch_ready_files(conn, false, &preparing);
84562306a36Sopenharmony_ci	if (!list_empty(&preparing))
84662306a36Sopenharmony_ci		hmdfs_init_cache_for_stash_files(conn, &preparing);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	spin_lock(&conn->wr_opened_inode_lock);
84962306a36Sopenharmony_ci	list_for_each_entry(info, &conn->wr_opened_inode_list, wr_opened_node) {
85062306a36Sopenharmony_ci		int status = READ_ONCE(info->stash_status);
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci		if (status == HMDFS_REMOTE_INODE_STASHING)
85362306a36Sopenharmony_ci			list_add_tail(&info->stash_node, &stashing);
85462306a36Sopenharmony_ci	}
85562306a36Sopenharmony_ci	spin_unlock(&conn->wr_opened_inode_lock);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	if (list_empty(&stashing))
85862306a36Sopenharmony_ci		goto unlock;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	hmdfs_stash_remote_inodes(conn, &stashing);
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ciunlock:
86362306a36Sopenharmony_ci	mutex_unlock(&conn->offline_cb_lock);
86462306a36Sopenharmony_ci	mutex_lock(&conn->seq_lock);
86562306a36Sopenharmony_ci}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_cistatic struct hmdfs_inode_info *
86862306a36Sopenharmony_cihmdfs_lookup_stash_inode(struct hmdfs_peer *conn, uint64_t inum)
86962306a36Sopenharmony_ci{
87062306a36Sopenharmony_ci	struct hmdfs_inode_info *info = NULL;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	list_for_each_entry(info, &conn->stashed_inode_list, stash_node) {
87362306a36Sopenharmony_ci		if (info->remote_ino == inum)
87462306a36Sopenharmony_ci			return info;
87562306a36Sopenharmony_ci	}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	return NULL;
87862306a36Sopenharmony_ci}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_cistatic void hmdfs_untrack_stashed_inode(struct hmdfs_peer *conn,
88162306a36Sopenharmony_ci					struct hmdfs_inode_info *info)
88262306a36Sopenharmony_ci{
88362306a36Sopenharmony_ci	list_del_init(&info->stash_node);
88462306a36Sopenharmony_ci	iput(&info->vfs_inode);
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	conn->stashed_inode_nr--;
88762306a36Sopenharmony_ci}
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_cistatic void hmdfs_reset_stashed_inode(struct hmdfs_peer *conn,
89062306a36Sopenharmony_ci				      struct hmdfs_inode_info *info)
89162306a36Sopenharmony_ci{
89262306a36Sopenharmony_ci	struct inode *ino = &info->vfs_inode;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	/*
89562306a36Sopenharmony_ci	 * For updating stash_status after iput()
89662306a36Sopenharmony_ci	 * in hmdfs_untrack_stashed_inode()
89762306a36Sopenharmony_ci	 */
89862306a36Sopenharmony_ci	ihold(ino);
89962306a36Sopenharmony_ci	hmdfs_untrack_stashed_inode(conn, info);
90062306a36Sopenharmony_ci	/*
90162306a36Sopenharmony_ci	 * Ensure the order of stash_node and stash_status:
90262306a36Sopenharmony_ci	 * only update stash_status to NONE after removal of
90362306a36Sopenharmony_ci	 * stash_node is completed.
90462306a36Sopenharmony_ci	 */
90562306a36Sopenharmony_ci	smp_store_release(&info->stash_status,
90662306a36Sopenharmony_ci			  HMDFS_REMOTE_INODE_NONE);
90762306a36Sopenharmony_ci	iput(ino);
90862306a36Sopenharmony_ci}
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_cistatic void hmdfs_drop_stashed_inodes(struct hmdfs_peer *conn)
91162306a36Sopenharmony_ci{
91262306a36Sopenharmony_ci	struct hmdfs_inode_info *info = NULL;
91362306a36Sopenharmony_ci	struct hmdfs_inode_info *next = NULL;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	if (list_empty(&conn->stashed_inode_list))
91662306a36Sopenharmony_ci		return;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	hmdfs_warning("peer 0x%x:0x%llx drop unrestorable file %u",
91962306a36Sopenharmony_ci		      conn->owner, conn->device_id, conn->stashed_inode_nr);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	list_for_each_entry_safe(info, next,
92262306a36Sopenharmony_ci				 &conn->stashed_inode_list, stash_node) {
92362306a36Sopenharmony_ci		hmdfs_warning("peer 0x%x:0x%llx inode 0x%llx unrestorable status %u",
92462306a36Sopenharmony_ci			      conn->owner, conn->device_id, info->remote_ino,
92562306a36Sopenharmony_ci			      READ_ONCE(info->stash_status));
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci		hmdfs_reset_stashed_inode(conn, info);
92862306a36Sopenharmony_ci	}
92962306a36Sopenharmony_ci}
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_cistatic struct file *hmdfs_open_stash_dir(struct path *d_path, const char *cid)
93262306a36Sopenharmony_ci{
93362306a36Sopenharmony_ci	int err = 0;
93462306a36Sopenharmony_ci	struct dentry *parent = d_path->dentry;
93562306a36Sopenharmony_ci	struct inode *dir = d_inode(parent);
93662306a36Sopenharmony_ci	struct dentry *child = NULL;
93762306a36Sopenharmony_ci	struct path peer_path;
93862306a36Sopenharmony_ci	struct file *filp = NULL;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	inode_lock_nested(dir, I_MUTEX_PARENT);
94162306a36Sopenharmony_ci	child = lookup_one_len(cid, parent, strlen(cid));
94262306a36Sopenharmony_ci	if (!IS_ERR(child)) {
94362306a36Sopenharmony_ci		if (!hmdfs_is_dir(child)) {
94462306a36Sopenharmony_ci			if (d_is_positive(child)) {
94562306a36Sopenharmony_ci				hmdfs_err("invalid stash dir mode 0%o", d_inode(child)->i_mode);
94662306a36Sopenharmony_ci				err = -EINVAL;
94762306a36Sopenharmony_ci			} else {
94862306a36Sopenharmony_ci				err = -ENOENT;
94962306a36Sopenharmony_ci			}
95062306a36Sopenharmony_ci			dput(child);
95162306a36Sopenharmony_ci		}
95262306a36Sopenharmony_ci	} else {
95362306a36Sopenharmony_ci		err = PTR_ERR(child);
95462306a36Sopenharmony_ci		hmdfs_err("lookup stash dir err %d", err);
95562306a36Sopenharmony_ci	}
95662306a36Sopenharmony_ci	inode_unlock(dir);
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	if (err)
95962306a36Sopenharmony_ci		return ERR_PTR(err);
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	peer_path.mnt = d_path->mnt;
96262306a36Sopenharmony_ci	peer_path.dentry = child;
96362306a36Sopenharmony_ci	filp = dentry_open(&peer_path, O_RDONLY | O_DIRECTORY, current_cred());
96462306a36Sopenharmony_ci	if (IS_ERR(filp))
96562306a36Sopenharmony_ci		hmdfs_err("open err %d", (int)PTR_ERR(filp));
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	dput(child);
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	return filp;
97062306a36Sopenharmony_ci}
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_cistatic int hmdfs_new_inode_tbl(struct hmdfs_inode_tbl **tbl)
97362306a36Sopenharmony_ci{
97462306a36Sopenharmony_ci	struct hmdfs_inode_tbl *new = NULL;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	new = kmalloc(PAGE_SIZE, GFP_KERNEL);
97762306a36Sopenharmony_ci	if (!new)
97862306a36Sopenharmony_ci		return -ENOMEM;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	new->cnt = 0;
98162306a36Sopenharmony_ci	new->max = (PAGE_SIZE - offsetof(struct hmdfs_inode_tbl, inodes)) /
98262306a36Sopenharmony_ci		   sizeof(new->inodes[0]);
98362306a36Sopenharmony_ci	*tbl = new;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	return 0;
98662306a36Sopenharmony_ci}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_cistatic int hmdfs_parse_stash_file_name(struct dir_context *dctx,
98962306a36Sopenharmony_ci					const char *name,
99062306a36Sopenharmony_ci					int namelen,
99162306a36Sopenharmony_ci					unsigned int d_type,
99262306a36Sopenharmony_ci					uint64_t *stash_inum)
99362306a36Sopenharmony_ci{
99462306a36Sopenharmony_ci	struct hmdfs_stash_dir_context *ctx = NULL;
99562306a36Sopenharmony_ci	int err;
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	if (d_type != DT_UNKNOWN && d_type != DT_REG)
99862306a36Sopenharmony_ci		return 0;
99962306a36Sopenharmony_ci	if (namelen > NAME_MAX)
100062306a36Sopenharmony_ci		return 0;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	ctx = container_of(dctx, struct hmdfs_stash_dir_context, dctx);
100362306a36Sopenharmony_ci	memcpy(ctx->name, name, namelen);
100462306a36Sopenharmony_ci	ctx->name[namelen] = '\0';
100562306a36Sopenharmony_ci	err = kstrtoull(ctx->name, 16, stash_inum);
100662306a36Sopenharmony_ci	if (err) {
100762306a36Sopenharmony_ci		hmdfs_err("unexpected stash file err %d", err);
100862306a36Sopenharmony_ci		return 0;
100962306a36Sopenharmony_ci	}
101062306a36Sopenharmony_ci	return 1;
101162306a36Sopenharmony_ci}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_cistatic bool hmdfs_has_stash_file(struct dir_context *dctx, const char *name,
101462306a36Sopenharmony_ci				int namelen, loff_t offset,
101562306a36Sopenharmony_ci				u64 inum, unsigned int d_type)
101662306a36Sopenharmony_ci{
101762306a36Sopenharmony_ci	struct hmdfs_stash_dir_context *ctx = NULL;
101862306a36Sopenharmony_ci	uint64_t stash_inum;
101962306a36Sopenharmony_ci	int err;
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	ctx = container_of(dctx, struct hmdfs_stash_dir_context, dctx);
102262306a36Sopenharmony_ci	err = hmdfs_parse_stash_file_name(dctx, name, namelen,
102362306a36Sopenharmony_ci					   d_type, &stash_inum);
102462306a36Sopenharmony_ci	if (!err)
102562306a36Sopenharmony_ci		return true;
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	ctx->tbl->cnt++;
102862306a36Sopenharmony_ci	return false;
102962306a36Sopenharmony_ci}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_cistatic bool hmdfs_fill_stash_file(struct dir_context *dctx, const char *name,
103262306a36Sopenharmony_ci				 int namelen, loff_t offset,
103362306a36Sopenharmony_ci				 u64 inum, unsigned int d_type)
103462306a36Sopenharmony_ci{
103562306a36Sopenharmony_ci	struct hmdfs_stash_dir_context *ctx = NULL;
103662306a36Sopenharmony_ci	uint64_t stash_inum;
103762306a36Sopenharmony_ci	int err;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	ctx = container_of(dctx, struct hmdfs_stash_dir_context, dctx);
104062306a36Sopenharmony_ci	err = hmdfs_parse_stash_file_name(dctx, name, namelen,
104162306a36Sopenharmony_ci					   d_type, &stash_inum);
104262306a36Sopenharmony_ci	if (!err)
104362306a36Sopenharmony_ci		return true;
104462306a36Sopenharmony_ci	if (ctx->tbl->cnt >= ctx->tbl->max)
104562306a36Sopenharmony_ci		return false;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	ctx->tbl->inodes[ctx->tbl->cnt++] = stash_inum;
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	return true;
105062306a36Sopenharmony_ci}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_cistatic int hmdfs_del_stash_file(struct dentry *parent, struct dentry *child)
105362306a36Sopenharmony_ci{
105462306a36Sopenharmony_ci	struct inode *dir = d_inode(parent);
105562306a36Sopenharmony_ci	int err = 0;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	/* Prevent d_delete() from calling dentry_unlink_inode() */
105862306a36Sopenharmony_ci	dget(child);
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	inode_lock_nested(dir, I_MUTEX_PARENT);
106162306a36Sopenharmony_ci	err = vfs_unlink(&nop_mnt_idmap, dir, child, NULL);
106262306a36Sopenharmony_ci	if (err)
106362306a36Sopenharmony_ci		hmdfs_err("remove stash file err %d", err);
106462306a36Sopenharmony_ci	inode_unlock(dir);
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	dput(child);
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	return err;
106962306a36Sopenharmony_ci}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_cistatic inline bool hmdfs_is_node_offlined(const struct hmdfs_peer *conn,
107262306a36Sopenharmony_ci					  unsigned int seq)
107362306a36Sopenharmony_ci{
107462306a36Sopenharmony_ci	/*
107562306a36Sopenharmony_ci	 * open()/fsync() may fail due to "status = NODE_STAT_OFFLINE"
107662306a36Sopenharmony_ci	 * in hmdfs_disconnect_node().
107762306a36Sopenharmony_ci	 * Pair with smp_mb() in hmdfs_disconnect_node() to ensure
107862306a36Sopenharmony_ci	 * getting the newest event sequence.
107962306a36Sopenharmony_ci	 */
108062306a36Sopenharmony_ci	smp_mb__before_atomic();
108162306a36Sopenharmony_ci	return hmdfs_node_evt_seq(conn) != seq;
108262306a36Sopenharmony_ci}
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_cistatic int hmdfs_verify_restore_file_head(struct hmdfs_file_restore_ctx *ctx,
108562306a36Sopenharmony_ci				    const struct hmdfs_cache_file_head *head)
108662306a36Sopenharmony_ci{
108762306a36Sopenharmony_ci	struct inode *inode = file_inode(ctx->src_filp);
108862306a36Sopenharmony_ci	struct hmdfs_peer *conn = ctx->conn;
108962306a36Sopenharmony_ci	unsigned int crc, read_crc, crc_offset;
109062306a36Sopenharmony_ci	loff_t path_offs, data_offs, isize;
109162306a36Sopenharmony_ci	int err = 0;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	if (le32_to_cpu(head->magic) != HMDFS_STASH_FILE_HEAD_MAGIC) {
109462306a36Sopenharmony_ci		err = -EUCLEAN;
109562306a36Sopenharmony_ci		hmdfs_err("peer 0x%x:0x%llx ino 0x%llx invalid magic: got 0x%x, exp 0x%x",
109662306a36Sopenharmony_ci			  conn->owner, conn->device_id, ctx->inum,
109762306a36Sopenharmony_ci			  le32_to_cpu(head->magic),
109862306a36Sopenharmony_ci			  HMDFS_STASH_FILE_HEAD_MAGIC);
109962306a36Sopenharmony_ci		goto out;
110062306a36Sopenharmony_ci	}
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	crc_offset = le32_to_cpu(head->crc_offset);
110362306a36Sopenharmony_ci	read_crc = le32_to_cpu(*((__le32 *)((char *)head + crc_offset)));
110462306a36Sopenharmony_ci	crc = crc32(0, head, crc_offset);
110562306a36Sopenharmony_ci	if (read_crc != crc) {
110662306a36Sopenharmony_ci		err = -EUCLEAN;
110762306a36Sopenharmony_ci		hmdfs_err("peer 0x%x:0x%llx ino 0x%llx invalid crc: got 0x%x, exp 0x%x",
110862306a36Sopenharmony_ci			  conn->owner, conn->device_id, ctx->inum,
110962306a36Sopenharmony_ci			  read_crc, crc);
111062306a36Sopenharmony_ci		goto out;
111162306a36Sopenharmony_ci	}
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	if (le64_to_cpu(head->ino) != ctx->inum) {
111462306a36Sopenharmony_ci		err = -EUCLEAN;
111562306a36Sopenharmony_ci		hmdfs_err("peer 0x%x:0x%llx ino 0x%llx invalid ino: got %llu, exp %llu",
111662306a36Sopenharmony_ci			  conn->owner, conn->device_id, ctx->inum,
111762306a36Sopenharmony_ci			  le64_to_cpu(head->ino), ctx->inum);
111862306a36Sopenharmony_ci		goto out;
111962306a36Sopenharmony_ci	}
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	path_offs = (loff_t)le32_to_cpu(head->path_offs) <<
112262306a36Sopenharmony_ci		    HMDFS_STASH_BLK_SHIFT;
112362306a36Sopenharmony_ci	if (path_offs <= 0 || path_offs >= i_size_read(inode)) {
112462306a36Sopenharmony_ci		err = -EUCLEAN;
112562306a36Sopenharmony_ci		hmdfs_err("peer 0x%x:0x%llx ino 0x%llx invalid path_offs %d, stash file size %llu",
112662306a36Sopenharmony_ci			  conn->owner, conn->device_id, ctx->inum,
112762306a36Sopenharmony_ci			  le32_to_cpu(head->path_offs), i_size_read(inode));
112862306a36Sopenharmony_ci		goto out;
112962306a36Sopenharmony_ci	}
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	data_offs = (loff_t)le32_to_cpu(head->data_offs) <<
113262306a36Sopenharmony_ci		    HMDFS_STASH_BLK_SHIFT;
113362306a36Sopenharmony_ci	if (path_offs >= data_offs) {
113462306a36Sopenharmony_ci		err = -EUCLEAN;
113562306a36Sopenharmony_ci		hmdfs_err("peer 0x%x:0x%llx ino 0x%llx invalid data_offs %d, path_offs %d",
113662306a36Sopenharmony_ci			  conn->owner, conn->device_id, ctx->inum,
113762306a36Sopenharmony_ci			  le32_to_cpu(head->data_offs),
113862306a36Sopenharmony_ci			  le32_to_cpu(head->path_offs));
113962306a36Sopenharmony_ci		goto out;
114062306a36Sopenharmony_ci	}
114162306a36Sopenharmony_ci	if (data_offs <= 0 || data_offs >= i_size_read(inode)) {
114262306a36Sopenharmony_ci		err = -EUCLEAN;
114362306a36Sopenharmony_ci		hmdfs_err("peer 0x%x:0x%llx ino 0x%llx invalid data_offs %d, stash file size %llu",
114462306a36Sopenharmony_ci			  conn->owner, conn->device_id, ctx->inum,
114562306a36Sopenharmony_ci			  le32_to_cpu(head->data_offs), i_size_read(inode));
114662306a36Sopenharmony_ci		goto out;
114762306a36Sopenharmony_ci	}
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	isize = le64_to_cpu(head->size);
115062306a36Sopenharmony_ci	if (isize != i_size_read(inode)) {
115162306a36Sopenharmony_ci		err = -EUCLEAN;
115262306a36Sopenharmony_ci		hmdfs_err("peer 0x%x:0x%llx ino 0x%llx invalid isize: got %llu, exp %llu",
115362306a36Sopenharmony_ci			  conn->owner, conn->device_id, ctx->inum,
115462306a36Sopenharmony_ci			  le64_to_cpu(head->size), i_size_read(inode));
115562306a36Sopenharmony_ci		goto out;
115662306a36Sopenharmony_ci	}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	if (le32_to_cpu(head->path_cnt) < 1) {
115962306a36Sopenharmony_ci		err = -EUCLEAN;
116062306a36Sopenharmony_ci		hmdfs_err("peer 0x%x:0x%llx ino 0x%llx invalid path_cnt %d",
116162306a36Sopenharmony_ci			  conn->owner, conn->device_id, ctx->inum,
116262306a36Sopenharmony_ci			  le32_to_cpu(head->path_cnt));
116362306a36Sopenharmony_ci		goto out;
116462306a36Sopenharmony_ci	}
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ciout:
116762306a36Sopenharmony_ci	return err;
116862306a36Sopenharmony_ci}
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_cistatic int hmdfs_get_restore_file_metadata(struct hmdfs_file_restore_ctx *ctx)
117162306a36Sopenharmony_ci{
117262306a36Sopenharmony_ci	struct hmdfs_cache_file_head head;
117362306a36Sopenharmony_ci	struct hmdfs_peer *conn = ctx->conn;
117462306a36Sopenharmony_ci	unsigned int head_size, read_size, head_crc_offset;
117562306a36Sopenharmony_ci	loff_t pos;
117662306a36Sopenharmony_ci	ssize_t rd;
117762306a36Sopenharmony_ci	int err = 0;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	head_size = sizeof(struct hmdfs_cache_file_head);
118062306a36Sopenharmony_ci	memset(&head, 0, head_size);
118162306a36Sopenharmony_ci	/* Read part head */
118262306a36Sopenharmony_ci	pos = 0;
118362306a36Sopenharmony_ci	read_size = offsetof(struct hmdfs_cache_file_head, crc_offset) +
118462306a36Sopenharmony_ci		    sizeof(head.crc_offset);
118562306a36Sopenharmony_ci	rd = kernel_read(ctx->src_filp, &head, read_size, &pos);
118662306a36Sopenharmony_ci	if (rd != read_size) {
118762306a36Sopenharmony_ci		err = rd < 0 ? rd : -ENODATA;
118862306a36Sopenharmony_ci		hmdfs_err("peer 0x%x:0x%llx ino 0x%llx read part head err %d",
118962306a36Sopenharmony_ci			  conn->owner, conn->device_id, ctx->inum, err);
119062306a36Sopenharmony_ci		goto out;
119162306a36Sopenharmony_ci	}
119262306a36Sopenharmony_ci	head_crc_offset = le32_to_cpu(head.crc_offset);
119362306a36Sopenharmony_ci	if (head_crc_offset + sizeof(head.crc32) < head_crc_offset ||
119462306a36Sopenharmony_ci	    head_crc_offset + sizeof(head.crc32) > head_size) {
119562306a36Sopenharmony_ci		err = -EUCLEAN;
119662306a36Sopenharmony_ci		hmdfs_err("peer 0x%x:0x%llx ino 0x%llx got bad head: Too long crc_offset %u which exceeds head size %u",
119762306a36Sopenharmony_ci			  conn->owner, conn->device_id, ctx->inum,
119862306a36Sopenharmony_ci			  head_crc_offset, head_size);
119962306a36Sopenharmony_ci		goto out;
120062306a36Sopenharmony_ci	}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	/* Read full head */
120362306a36Sopenharmony_ci	pos = 0;
120462306a36Sopenharmony_ci	read_size = le32_to_cpu(head.crc_offset) + sizeof(head.crc32);
120562306a36Sopenharmony_ci	rd = kernel_read(ctx->src_filp, &head, read_size, &pos);
120662306a36Sopenharmony_ci	if (rd != read_size) {
120762306a36Sopenharmony_ci		err = rd < 0 ? rd : -ENODATA;
120862306a36Sopenharmony_ci		hmdfs_err("peer 0x%x:0x%llx ino 0x%llx read full head err %d",
120962306a36Sopenharmony_ci			  conn->owner, conn->device_id, ctx->inum, err);
121062306a36Sopenharmony_ci		goto out;
121162306a36Sopenharmony_ci	}
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	err = hmdfs_verify_restore_file_head(ctx, &head);
121462306a36Sopenharmony_ci	if (err)
121562306a36Sopenharmony_ci		goto out;
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	ctx->pages = le64_to_cpu(head.blocks) >>
121862306a36Sopenharmony_ci		     HMDFS_STASH_PAGE_TO_SECTOR_SHIFT;
121962306a36Sopenharmony_ci	ctx->data_offs = le32_to_cpu(head.data_offs);
122062306a36Sopenharmony_ci	/* Read path */
122162306a36Sopenharmony_ci	read_size = min_t(unsigned int, le32_to_cpu(head.path_len), PATH_MAX);
122262306a36Sopenharmony_ci	pos = (loff_t)le32_to_cpu(head.path_offs) << HMDFS_STASH_BLK_SHIFT;
122362306a36Sopenharmony_ci	rd = kernel_read(ctx->src_filp, ctx->dst, read_size, &pos);
122462306a36Sopenharmony_ci	if (rd != read_size) {
122562306a36Sopenharmony_ci		err = rd < 0 ? rd : -ENODATA;
122662306a36Sopenharmony_ci		hmdfs_err("peer 0x%x:0x%llx ino 0x%llx read path err %d",
122762306a36Sopenharmony_ci			  conn->owner, conn->device_id, ctx->inum, err);
122862306a36Sopenharmony_ci		goto out;
122962306a36Sopenharmony_ci	}
123062306a36Sopenharmony_ci	if (strnlen(ctx->dst, read_size) >= read_size) {
123162306a36Sopenharmony_ci		err = -EUCLEAN;
123262306a36Sopenharmony_ci		hmdfs_err("peer 0x%x:0x%llx ino 0x%llx read path not end with \\0",
123362306a36Sopenharmony_ci			  conn->owner, conn->device_id, ctx->inum);
123462306a36Sopenharmony_ci		goto out;
123562306a36Sopenharmony_ci	}
123662306a36Sopenharmony_ci	/* TODO: Pick a valid path from all paths */
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ciout:
123962306a36Sopenharmony_ci	return err;
124062306a36Sopenharmony_ci}
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_cistatic int hmdfs_open_restore_dst_file(struct hmdfs_file_restore_ctx *ctx,
124362306a36Sopenharmony_ci				       unsigned int rw_flag, struct file **filp)
124462306a36Sopenharmony_ci{
124562306a36Sopenharmony_ci	struct hmdfs_peer *conn = ctx->conn;
124662306a36Sopenharmony_ci	struct file *dst = NULL;
124762306a36Sopenharmony_ci	int err = 0;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	err = hmdfs_get_restore_file_metadata(ctx);
125062306a36Sopenharmony_ci	if (err)
125162306a36Sopenharmony_ci		goto out;
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	/* Error comes from connection or server ? */
125462306a36Sopenharmony_ci	dst = file_open_root(&ctx->dst_root_path,
125562306a36Sopenharmony_ci			     ctx->dst, O_LARGEFILE | rw_flag, 0);
125662306a36Sopenharmony_ci	if (IS_ERR(dst)) {
125762306a36Sopenharmony_ci		err = PTR_ERR(dst);
125862306a36Sopenharmony_ci		hmdfs_err("open remote file ino 0x%llx err %d", ctx->inum, err);
125962306a36Sopenharmony_ci		if (hmdfs_is_node_offlined(conn, ctx->seq))
126062306a36Sopenharmony_ci			err = -ESHUTDOWN;
126162306a36Sopenharmony_ci		goto out;
126262306a36Sopenharmony_ci	}
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	*filp = dst;
126562306a36Sopenharmony_ciout:
126662306a36Sopenharmony_ci	return err;
126762306a36Sopenharmony_ci}
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_cistatic bool hmdfs_need_abort_restore(struct hmdfs_file_restore_ctx *ctx,
127062306a36Sopenharmony_ci				     struct hmdfs_inode_info *pinned,
127162306a36Sopenharmony_ci				     struct file *opened_file)
127262306a36Sopenharmony_ci{
127362306a36Sopenharmony_ci	struct hmdfs_inode_info *opened = hmdfs_i(file_inode(opened_file));
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	if (opened->inode_type != HMDFS_LAYER_OTHER_REMOTE)
127662306a36Sopenharmony_ci		goto abort;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	if (opened == pinned)
127962306a36Sopenharmony_ci		return false;
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ciabort:
128262306a36Sopenharmony_ci	hmdfs_warning("peer 0x%x:0x%llx inode 0x%llx invalid remote file",
128362306a36Sopenharmony_ci		      ctx->conn->owner, ctx->conn->device_id, ctx->inum);
128462306a36Sopenharmony_ci	hmdfs_warning("got: peer 0x%x:0x%llx inode 0x%llx type %d status %d",
128562306a36Sopenharmony_ci		      opened->conn ? opened->conn->owner : 0,
128662306a36Sopenharmony_ci		      opened->conn ? opened->conn->device_id : 0,
128762306a36Sopenharmony_ci		      opened->remote_ino, opened->inode_type,
128862306a36Sopenharmony_ci		      opened->stash_status);
128962306a36Sopenharmony_ci	hmdfs_warning("pinned: peer 0x%x:0x%llx inode 0x%llx type %d status %d",
129062306a36Sopenharmony_ci		      pinned->conn->owner, pinned->conn->device_id,
129162306a36Sopenharmony_ci		      pinned->remote_ino, pinned->inode_type,
129262306a36Sopenharmony_ci		      pinned->stash_status);
129362306a36Sopenharmony_ci	return true;
129462306a36Sopenharmony_ci}
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_cistatic void hmdfs_init_copy_args(const struct hmdfs_file_restore_ctx *ctx,
129762306a36Sopenharmony_ci				 struct file *dst, struct hmdfs_copy_args *args)
129862306a36Sopenharmony_ci{
129962306a36Sopenharmony_ci	args->src = ctx->src_filp;
130062306a36Sopenharmony_ci	args->dst = dst;
130162306a36Sopenharmony_ci	args->buf = ctx->page;
130262306a36Sopenharmony_ci	args->buf_len = PAGE_SIZE;
130362306a36Sopenharmony_ci	args->seq = ctx->seq;
130462306a36Sopenharmony_ci	args->data_offs = ctx->data_offs;
130562306a36Sopenharmony_ci	args->inum = ctx->inum;
130662306a36Sopenharmony_ci}
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_cistatic ssize_t hmdfs_write_dst(struct hmdfs_peer *conn, struct file *filp,
130962306a36Sopenharmony_ci			       void *buf, size_t len, loff_t pos)
131062306a36Sopenharmony_ci{
131162306a36Sopenharmony_ci	struct kiocb kiocb;
131262306a36Sopenharmony_ci	struct iovec iov;
131362306a36Sopenharmony_ci	struct iov_iter iter;
131462306a36Sopenharmony_ci	ssize_t wr;
131562306a36Sopenharmony_ci	int err = 0;
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	file_start_write(filp);
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	init_sync_kiocb(&kiocb, filp);
132062306a36Sopenharmony_ci	kiocb.ki_pos = pos;
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	iov.iov_base = buf;
132362306a36Sopenharmony_ci	iov.iov_len = len;
132462306a36Sopenharmony_ci	iov_iter_init(&iter, WRITE, &iov, 1, len);
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	wr = hmdfs_file_write_iter_remote_nocheck(&kiocb, &iter);
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	file_end_write(filp);
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	if (wr != len) {
133162306a36Sopenharmony_ci		struct hmdfs_inode_info *info = hmdfs_i(file_inode(filp));
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci		hmdfs_err("peer 0x%x:0x%llx ino 0x%llx short write ret %zd exp %zu",
133462306a36Sopenharmony_ci			  conn->owner, conn->device_id, info->remote_ino,
133562306a36Sopenharmony_ci			  wr, len);
133662306a36Sopenharmony_ci		err = wr < 0 ? (int)wr : -EFAULT;
133762306a36Sopenharmony_ci	}
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	return err;
134062306a36Sopenharmony_ci}
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_cistatic int hmdfs_rd_src_wr_dst(struct hmdfs_peer *conn,
134362306a36Sopenharmony_ci			       struct hmdfs_copy_ctx *ctx)
134462306a36Sopenharmony_ci{
134562306a36Sopenharmony_ci	const struct hmdfs_copy_args *args = NULL;
134662306a36Sopenharmony_ci	int err = 0;
134762306a36Sopenharmony_ci	loff_t rd_pos;
134862306a36Sopenharmony_ci	ssize_t rd;
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	ctx->eof = false;
135162306a36Sopenharmony_ci	ctx->copied = 0;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	args = &ctx->args;
135462306a36Sopenharmony_ci	rd_pos = ctx->src_pos;
135562306a36Sopenharmony_ci	rd = kernel_read(args->src, args->buf, args->buf_len, &rd_pos);
135662306a36Sopenharmony_ci	if (rd < 0) {
135762306a36Sopenharmony_ci		err = (int)rd;
135862306a36Sopenharmony_ci		hmdfs_err("peer 0x%x:0x%llx ino 0x%llx short read err %d",
135962306a36Sopenharmony_ci			  conn->owner, conn->device_id, args->inum, err);
136062306a36Sopenharmony_ci		goto out;
136162306a36Sopenharmony_ci	} else if (rd == 0) {
136262306a36Sopenharmony_ci		ctx->eof = true;
136362306a36Sopenharmony_ci		goto out;
136462306a36Sopenharmony_ci	}
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	err = hmdfs_write_dst(conn, args->dst, args->buf, rd, ctx->dst_pos);
136762306a36Sopenharmony_ci	if (!err)
136862306a36Sopenharmony_ci		ctx->copied = rd;
136962306a36Sopenharmony_ci	else if (hmdfs_is_node_offlined(conn, args->seq))
137062306a36Sopenharmony_ci		err = -ESHUTDOWN;
137162306a36Sopenharmony_ciout:
137262306a36Sopenharmony_ci	return err;
137362306a36Sopenharmony_ci}
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_cistatic int hmdfs_copy_src_to_dst(struct hmdfs_peer *conn,
137662306a36Sopenharmony_ci				 const struct hmdfs_copy_args *args)
137762306a36Sopenharmony_ci{
137862306a36Sopenharmony_ci	int err = 0;
137962306a36Sopenharmony_ci	struct file *src = NULL;
138062306a36Sopenharmony_ci	struct hmdfs_copy_ctx ctx;
138162306a36Sopenharmony_ci	loff_t seek_pos, data_init_pos;
138262306a36Sopenharmony_ci	loff_t src_size;
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	ctx.args = *args;
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	src = ctx.args.src;
138762306a36Sopenharmony_ci	data_init_pos = (loff_t)ctx.args.data_offs << HMDFS_STASH_BLK_SHIFT;
138862306a36Sopenharmony_ci	seek_pos = data_init_pos;
138962306a36Sopenharmony_ci	src_size = i_size_read(file_inode(src));
139062306a36Sopenharmony_ci	while (true) {
139162306a36Sopenharmony_ci		loff_t data_pos;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci		data_pos = vfs_llseek(src, seek_pos, SEEK_DATA);
139462306a36Sopenharmony_ci		if (data_pos > seek_pos) {
139562306a36Sopenharmony_ci			seek_pos = data_pos;
139662306a36Sopenharmony_ci			continue;
139762306a36Sopenharmony_ci		} else if (data_pos < 0) {
139862306a36Sopenharmony_ci			if (data_pos == -ENXIO) {
139962306a36Sopenharmony_ci				loff_t src_blks = file_inode(src)->i_blocks;
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci				hmdfs_info("peer 0x%x:0x%llx ino 0x%llx end at 0x%llx (sz 0x%llx blk 0x%llx)",
140262306a36Sopenharmony_ci					   conn->owner, conn->device_id,
140362306a36Sopenharmony_ci					   args->inum, seek_pos,
140462306a36Sopenharmony_ci					   src_size, src_blks);
140562306a36Sopenharmony_ci			} else {
140662306a36Sopenharmony_ci				err = (int)data_pos;
140762306a36Sopenharmony_ci				hmdfs_err("peer 0x%x:0x%llx ino 0x%llx seek pos 0x%llx err %d",
140862306a36Sopenharmony_ci					  conn->owner, conn->device_id,
140962306a36Sopenharmony_ci					  args->inum, seek_pos, err);
141062306a36Sopenharmony_ci			}
141162306a36Sopenharmony_ci			break;
141262306a36Sopenharmony_ci		}
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci		hmdfs_debug("peer 0x%x:0x%llx ino 0x%llx seek to 0x%llx",
141562306a36Sopenharmony_ci			    conn->owner, conn->device_id, args->inum, data_pos);
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci		ctx.src_pos = data_pos;
141862306a36Sopenharmony_ci		ctx.dst_pos = data_pos - data_init_pos;
141962306a36Sopenharmony_ci		err = hmdfs_rd_src_wr_dst(conn, &ctx);
142062306a36Sopenharmony_ci		if (err || ctx.eof)
142162306a36Sopenharmony_ci			break;
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci		seek_pos += ctx.copied;
142462306a36Sopenharmony_ci		if (seek_pos >= src_size)
142562306a36Sopenharmony_ci			break;
142662306a36Sopenharmony_ci	}
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	return err;
142962306a36Sopenharmony_ci}
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_cistatic int hmdfs_restore_src_to_dst(struct hmdfs_file_restore_ctx *ctx,
143262306a36Sopenharmony_ci				    struct file *dst)
143362306a36Sopenharmony_ci{
143462306a36Sopenharmony_ci	struct file *src = ctx->src_filp;
143562306a36Sopenharmony_ci	struct hmdfs_copy_args args;
143662306a36Sopenharmony_ci	int err;
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	hmdfs_init_copy_args(ctx, dst, &args);
143962306a36Sopenharmony_ci	err = hmdfs_copy_src_to_dst(ctx->conn, &args);
144062306a36Sopenharmony_ci	if (err)
144162306a36Sopenharmony_ci		goto out;
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	err = vfs_fsync(dst, 0);
144462306a36Sopenharmony_ci	if (err) {
144562306a36Sopenharmony_ci		hmdfs_err("fsync remote file ino 0x%llx err %d", ctx->inum, err);
144662306a36Sopenharmony_ci		if (hmdfs_is_node_offlined(ctx->conn, ctx->seq))
144762306a36Sopenharmony_ci			err = -ESHUTDOWN;
144862306a36Sopenharmony_ci	}
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ciout:
145162306a36Sopenharmony_ci	if (err)
145262306a36Sopenharmony_ci		truncate_inode_pages(file_inode(dst)->i_mapping, 0);
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	/* Remove the unnecessary cache */
145562306a36Sopenharmony_ci	invalidate_mapping_pages(file_inode(src)->i_mapping, 0, -1);
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	return err;
145862306a36Sopenharmony_ci}
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_cistatic int hmdfs_restore_file(struct hmdfs_file_restore_ctx *ctx)
146262306a36Sopenharmony_ci{
146362306a36Sopenharmony_ci	struct hmdfs_peer *conn = ctx->conn;
146462306a36Sopenharmony_ci	uint64_t inum = ctx->inum;
146562306a36Sopenharmony_ci	struct hmdfs_inode_info *pinned_info = NULL;
146662306a36Sopenharmony_ci	struct file *dst_filp = NULL;
146762306a36Sopenharmony_ci	int err = 0;
146862306a36Sopenharmony_ci	bool keep = false;
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	hmdfs_info("peer 0x%x:0x%llx ino 0x%llx do restore",
147162306a36Sopenharmony_ci		   conn->owner, conn->device_id, inum);
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	pinned_info = hmdfs_lookup_stash_inode(conn, inum);
147462306a36Sopenharmony_ci	if (pinned_info) {
147562306a36Sopenharmony_ci		unsigned int status = READ_ONCE(pinned_info->stash_status);
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci		if (status != HMDFS_REMOTE_INODE_RESTORING) {
147862306a36Sopenharmony_ci			hmdfs_err("peer 0x%x:0x%llx ino 0x%llx invalid status %u",
147962306a36Sopenharmony_ci				  conn->owner, conn->device_id, inum, status);
148062306a36Sopenharmony_ci			err = -EINVAL;
148162306a36Sopenharmony_ci			goto clean;
148262306a36Sopenharmony_ci		}
148362306a36Sopenharmony_ci	} else {
148462306a36Sopenharmony_ci		hmdfs_warning("peer 0x%x:0x%llx ino 0x%llx doesn't being pinned",
148562306a36Sopenharmony_ci			      conn->owner, conn->device_id, inum);
148662306a36Sopenharmony_ci		err = -EINVAL;
148762306a36Sopenharmony_ci		goto clean;
148862306a36Sopenharmony_ci	}
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci	set_bit(HMDFS_FID_NEED_OPEN, &pinned_info->fid_flags);
149162306a36Sopenharmony_ci	err = hmdfs_open_restore_dst_file(ctx, O_RDWR, &dst_filp);
149262306a36Sopenharmony_ci	if (err) {
149362306a36Sopenharmony_ci		if (err == -ESHUTDOWN)
149462306a36Sopenharmony_ci			keep = true;
149562306a36Sopenharmony_ci		goto clean;
149662306a36Sopenharmony_ci	}
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	if (hmdfs_need_abort_restore(ctx, pinned_info, dst_filp))
149962306a36Sopenharmony_ci		goto abort;
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	err = hmdfs_restore_src_to_dst(ctx, dst_filp);
150262306a36Sopenharmony_ci	if (err == -ESHUTDOWN)
150362306a36Sopenharmony_ci		keep = true;
150462306a36Sopenharmony_ciabort:
150562306a36Sopenharmony_ci	fput(dst_filp);
150662306a36Sopenharmony_ciclean:
150762306a36Sopenharmony_ci	if (pinned_info && !keep)
150862306a36Sopenharmony_ci		hmdfs_reset_stashed_inode(conn, pinned_info);
150962306a36Sopenharmony_ci	ctx->keep = keep;
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci	hmdfs_info("peer 0x%x:0x%llx ino 0x%llx restore err %d keep %d",
151262306a36Sopenharmony_ci		   conn->owner, conn->device_id, inum, err, ctx->keep);
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	return err;
151562306a36Sopenharmony_ci}
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_cistatic int hmdfs_init_file_restore_ctx(struct hmdfs_peer *conn,
151862306a36Sopenharmony_ci				       unsigned int seq, struct path *src_dir,
151962306a36Sopenharmony_ci				       struct hmdfs_file_restore_ctx *ctx)
152062306a36Sopenharmony_ci{
152162306a36Sopenharmony_ci	struct hmdfs_sb_info *sbi = conn->sbi;
152262306a36Sopenharmony_ci	struct path dst_root;
152362306a36Sopenharmony_ci	char *dst = NULL;
152462306a36Sopenharmony_ci	char *page = NULL;
152562306a36Sopenharmony_ci	int err = 0;
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	err = hmdfs_get_path_in_sb(sbi->sb, sbi->real_dst, LOOKUP_DIRECTORY,
152862306a36Sopenharmony_ci				   &dst_root);
152962306a36Sopenharmony_ci	if (err)
153062306a36Sopenharmony_ci		return err;
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci	dst = kmalloc(PATH_MAX, GFP_KERNEL);
153362306a36Sopenharmony_ci	if (!dst) {
153462306a36Sopenharmony_ci		err = -ENOMEM;
153562306a36Sopenharmony_ci		goto put_path;
153662306a36Sopenharmony_ci	}
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	page = kmalloc(PAGE_SIZE, GFP_KERNEL);
153962306a36Sopenharmony_ci	if (!page) {
154062306a36Sopenharmony_ci		err = -ENOMEM;
154162306a36Sopenharmony_ci		goto free_dst;
154262306a36Sopenharmony_ci	}
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	ctx->conn = conn;
154562306a36Sopenharmony_ci	ctx->src_dir_path = *src_dir;
154662306a36Sopenharmony_ci	ctx->dst_root_path = dst_root;
154762306a36Sopenharmony_ci	ctx->dst = dst;
154862306a36Sopenharmony_ci	ctx->page = page;
154962306a36Sopenharmony_ci	ctx->seq = seq;
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	return 0;
155262306a36Sopenharmony_cifree_dst:
155362306a36Sopenharmony_ci	kfree(dst);
155462306a36Sopenharmony_ciput_path:
155562306a36Sopenharmony_ci	path_put(&dst_root);
155662306a36Sopenharmony_ci	return err;
155762306a36Sopenharmony_ci}
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_cistatic void hmdfs_exit_file_restore_ctx(struct hmdfs_file_restore_ctx *ctx)
156062306a36Sopenharmony_ci{
156162306a36Sopenharmony_ci	path_put(&ctx->dst_root_path);
156262306a36Sopenharmony_ci	kfree(ctx->dst);
156362306a36Sopenharmony_ci	kfree(ctx->page);
156462306a36Sopenharmony_ci}
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_cistatic struct file *hmdfs_open_stash_file(struct path *p_path, char *name)
156762306a36Sopenharmony_ci{
156862306a36Sopenharmony_ci	struct dentry *parent = NULL;
156962306a36Sopenharmony_ci	struct inode *dir = NULL;
157062306a36Sopenharmony_ci	struct dentry *child = NULL;
157162306a36Sopenharmony_ci	struct file *filp = NULL;
157262306a36Sopenharmony_ci	struct path c_path;
157362306a36Sopenharmony_ci	int err = 0;
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci	parent = p_path->dentry;
157662306a36Sopenharmony_ci	dir = d_inode(parent);
157762306a36Sopenharmony_ci	inode_lock_nested(dir, I_MUTEX_PARENT);
157862306a36Sopenharmony_ci	child = lookup_one_len(name, parent, strlen(name));
157962306a36Sopenharmony_ci	if (!IS_ERR(child) && !hmdfs_is_reg(child)) {
158062306a36Sopenharmony_ci		if (d_is_positive(child)) {
158162306a36Sopenharmony_ci			hmdfs_err("invalid stash file (mode 0%o)",
158262306a36Sopenharmony_ci				  d_inode(child)->i_mode);
158362306a36Sopenharmony_ci			err = -EINVAL;
158462306a36Sopenharmony_ci		} else {
158562306a36Sopenharmony_ci			hmdfs_err("missing stash file");
158662306a36Sopenharmony_ci			err = -ENOENT;
158762306a36Sopenharmony_ci		}
158862306a36Sopenharmony_ci		dput(child);
158962306a36Sopenharmony_ci	} else if (IS_ERR(child)) {
159062306a36Sopenharmony_ci		err = PTR_ERR(child);
159162306a36Sopenharmony_ci		hmdfs_err("lookup stash file err %d", err);
159262306a36Sopenharmony_ci	}
159362306a36Sopenharmony_ci	inode_unlock(dir);
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	if (err)
159662306a36Sopenharmony_ci		return ERR_PTR(err);
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci	c_path.mnt = p_path->mnt;
159962306a36Sopenharmony_ci	c_path.dentry = child;
160062306a36Sopenharmony_ci	filp = dentry_open(&c_path, O_RDONLY | O_LARGEFILE, current_cred());
160162306a36Sopenharmony_ci	if (IS_ERR(filp))
160262306a36Sopenharmony_ci		hmdfs_err("open stash file err %d", (int)PTR_ERR(filp));
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci	dput(child);
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci	return filp;
160762306a36Sopenharmony_ci}
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_cistatic void hmdfs_update_restore_stats(struct hmdfs_restore_stats *stats,
161062306a36Sopenharmony_ci				       bool keep, uint64_t pages, int err)
161162306a36Sopenharmony_ci{
161262306a36Sopenharmony_ci	if (!err) {
161362306a36Sopenharmony_ci		stats->succeed++;
161462306a36Sopenharmony_ci		stats->ok_pages += pages;
161562306a36Sopenharmony_ci	} else if (keep) {
161662306a36Sopenharmony_ci		stats->keep++;
161762306a36Sopenharmony_ci	} else {
161862306a36Sopenharmony_ci		stats->fail++;
161962306a36Sopenharmony_ci		stats->fail_pages += pages;
162062306a36Sopenharmony_ci	}
162162306a36Sopenharmony_ci}
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_cistatic int hmdfs_restore_files(struct hmdfs_peer *conn,
162462306a36Sopenharmony_ci			       unsigned int seq, struct path *dir,
162562306a36Sopenharmony_ci			       const struct hmdfs_inode_tbl *tbl,
162662306a36Sopenharmony_ci			       void *priv)
162762306a36Sopenharmony_ci{
162862306a36Sopenharmony_ci	unsigned int i;
162962306a36Sopenharmony_ci	struct hmdfs_file_restore_ctx ctx;
163062306a36Sopenharmony_ci	int err = 0;
163162306a36Sopenharmony_ci	struct hmdfs_restore_stats *stats = priv;
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	err = hmdfs_init_file_restore_ctx(conn, seq, dir, &ctx);
163462306a36Sopenharmony_ci	if (err)
163562306a36Sopenharmony_ci		return err;
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	for (i = 0; i < tbl->cnt; i++) {
163862306a36Sopenharmony_ci		char name[HMDFS_STASH_FILE_NAME_LEN];
163962306a36Sopenharmony_ci		struct file *filp = NULL;
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci		snprintf(name, sizeof(name), "0x%llx", tbl->inodes[i]);
164262306a36Sopenharmony_ci		filp = hmdfs_open_stash_file(dir, name);
164362306a36Sopenharmony_ci		/* Continue to restore if any error */
164462306a36Sopenharmony_ci		if (IS_ERR(filp)) {
164562306a36Sopenharmony_ci			stats->fail++;
164662306a36Sopenharmony_ci			continue;
164762306a36Sopenharmony_ci		}
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci		ctx.inum = tbl->inodes[i];
165062306a36Sopenharmony_ci		ctx.src_filp = filp;
165162306a36Sopenharmony_ci		ctx.keep = false;
165262306a36Sopenharmony_ci		ctx.pages = 0;
165362306a36Sopenharmony_ci		err = hmdfs_restore_file(&ctx);
165462306a36Sopenharmony_ci		hmdfs_update_restore_stats(stats, ctx.keep, ctx.pages, err);
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci		if (!ctx.keep)
165762306a36Sopenharmony_ci			hmdfs_del_stash_file(dir->dentry,
165862306a36Sopenharmony_ci					     file_dentry(ctx.src_filp));
165962306a36Sopenharmony_ci		fput(ctx.src_filp);
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci		/* Continue to restore */
166262306a36Sopenharmony_ci		if (err == -ESHUTDOWN)
166362306a36Sopenharmony_ci			break;
166462306a36Sopenharmony_ci		err = 0;
166562306a36Sopenharmony_ci	}
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci	hmdfs_exit_file_restore_ctx(&ctx);
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci	return err;
167062306a36Sopenharmony_ci}
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_cistatic bool hmdfs_is_valid_stash_status(struct hmdfs_inode_info *inode_info,
167362306a36Sopenharmony_ci					uint64_t ino)
167462306a36Sopenharmony_ci{
167562306a36Sopenharmony_ci	return (inode_info->inode_type == HMDFS_LAYER_OTHER_REMOTE &&
167662306a36Sopenharmony_ci		inode_info->stash_status == HMDFS_REMOTE_INODE_RESTORING &&
167762306a36Sopenharmony_ci		inode_info->remote_ino == ino);
167862306a36Sopenharmony_ci}
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_cistatic int hmdfs_rebuild_stash_list(struct hmdfs_peer *conn,
168162306a36Sopenharmony_ci				    unsigned int seq,
168262306a36Sopenharmony_ci				    struct path *dir,
168362306a36Sopenharmony_ci				    const struct hmdfs_inode_tbl *tbl,
168462306a36Sopenharmony_ci				    void *priv)
168562306a36Sopenharmony_ci{
168662306a36Sopenharmony_ci	struct hmdfs_file_restore_ctx ctx;
168762306a36Sopenharmony_ci	unsigned int i;
168862306a36Sopenharmony_ci	int err;
168962306a36Sopenharmony_ci	struct hmdfs_rebuild_stats *stats = priv;
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_ci	err = hmdfs_init_file_restore_ctx(conn, seq, dir, &ctx);
169262306a36Sopenharmony_ci	if (err)
169362306a36Sopenharmony_ci		return err;
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci	stats->total += tbl->cnt;
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	for (i = 0; i < tbl->cnt; i++) {
169862306a36Sopenharmony_ci		char name[HMDFS_STASH_FILE_NAME_LEN];
169962306a36Sopenharmony_ci		struct file *src_filp = NULL;
170062306a36Sopenharmony_ci		struct file *dst_filp = NULL;
170162306a36Sopenharmony_ci		struct hmdfs_inode_info *inode_info = NULL;
170262306a36Sopenharmony_ci		bool is_valid = true;
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci		snprintf(name, sizeof(name), "0x%llx", tbl->inodes[i]);
170562306a36Sopenharmony_ci		src_filp = hmdfs_open_stash_file(dir, name);
170662306a36Sopenharmony_ci		if (IS_ERR(src_filp)) {
170762306a36Sopenharmony_ci			stats->fail++;
170862306a36Sopenharmony_ci			continue;
170962306a36Sopenharmony_ci		}
171062306a36Sopenharmony_ci		ctx.inum = tbl->inodes[i];
171162306a36Sopenharmony_ci		ctx.src_filp = src_filp;
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci		/* No need to track the open which only needs meta info */
171462306a36Sopenharmony_ci		err = hmdfs_open_restore_dst_file(&ctx, O_RDONLY, &dst_filp);
171562306a36Sopenharmony_ci		if (err) {
171662306a36Sopenharmony_ci			fput(src_filp);
171762306a36Sopenharmony_ci			if (err == -ESHUTDOWN)
171862306a36Sopenharmony_ci				break;
171962306a36Sopenharmony_ci			stats->fail++;
172062306a36Sopenharmony_ci			err = 0;
172162306a36Sopenharmony_ci			continue;
172262306a36Sopenharmony_ci		}
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci		inode_info = hmdfs_i(file_inode(dst_filp));
172562306a36Sopenharmony_ci		is_valid = hmdfs_is_valid_stash_status(inode_info,
172662306a36Sopenharmony_ci						       ctx.inum);
172762306a36Sopenharmony_ci		if (is_valid) {
172862306a36Sopenharmony_ci			stats->succeed++;
172962306a36Sopenharmony_ci		} else {
173062306a36Sopenharmony_ci			hmdfs_err("peer 0x%x:0x%llx inode 0x%llx invalid state: type: %d, status: %u, inode: %llu",
173162306a36Sopenharmony_ci				  conn->owner, conn->device_id, ctx.inum,
173262306a36Sopenharmony_ci				  inode_info->inode_type,
173362306a36Sopenharmony_ci				  READ_ONCE(inode_info->stash_status),
173462306a36Sopenharmony_ci				  inode_info->remote_ino);
173562306a36Sopenharmony_ci			stats->invalid++;
173662306a36Sopenharmony_ci		}
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci		fput(ctx.src_filp);
173962306a36Sopenharmony_ci		fput(dst_filp);
174062306a36Sopenharmony_ci	}
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ci	hmdfs_exit_file_restore_ctx(&ctx);
174362306a36Sopenharmony_ci	return err;
174462306a36Sopenharmony_ci}
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_cistatic int hmdfs_iter_stash_file(struct hmdfs_peer *conn,
174762306a36Sopenharmony_ci				 unsigned int seq,
174862306a36Sopenharmony_ci				 struct file *filp,
174962306a36Sopenharmony_ci				 stash_operation_func op,
175062306a36Sopenharmony_ci				 void *priv)
175162306a36Sopenharmony_ci{
175262306a36Sopenharmony_ci	int err = 0;
175362306a36Sopenharmony_ci	struct hmdfs_stash_dir_context ctx = {
175462306a36Sopenharmony_ci		.dctx.actor = hmdfs_fill_stash_file,
175562306a36Sopenharmony_ci	};
175662306a36Sopenharmony_ci	struct hmdfs_inode_tbl *tbl = NULL;
175762306a36Sopenharmony_ci	struct path dir;
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci	err = hmdfs_new_inode_tbl(&tbl);
176062306a36Sopenharmony_ci	if (err)
176162306a36Sopenharmony_ci		goto out;
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci	dir.mnt = filp->f_path.mnt;
176462306a36Sopenharmony_ci	dir.dentry = file_dentry(filp);
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci	ctx.tbl = tbl;
176762306a36Sopenharmony_ci	ctx.dctx.pos = 0;
176862306a36Sopenharmony_ci	do {
176962306a36Sopenharmony_ci		tbl->cnt = 0;
177062306a36Sopenharmony_ci		err = iterate_dir(filp, &ctx.dctx);
177162306a36Sopenharmony_ci		if (err || !tbl->cnt) {
177262306a36Sopenharmony_ci			if (err)
177362306a36Sopenharmony_ci				hmdfs_err("iterate stash dir err %d", err);
177462306a36Sopenharmony_ci			break;
177562306a36Sopenharmony_ci		}
177662306a36Sopenharmony_ci		err = op(conn, seq, &dir, tbl, priv);
177762306a36Sopenharmony_ci	} while (!err);
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ciout:
178062306a36Sopenharmony_ci	kfree(tbl);
178162306a36Sopenharmony_ci	return err;
178262306a36Sopenharmony_ci}
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_cistatic void hmdfs_rebuild_check_work_fn(struct work_struct *base)
178562306a36Sopenharmony_ci{
178662306a36Sopenharmony_ci	struct hmdfs_check_work *work =
178762306a36Sopenharmony_ci		container_of(base, struct hmdfs_check_work, work);
178862306a36Sopenharmony_ci	struct hmdfs_peer *conn = work->conn;
178962306a36Sopenharmony_ci	struct hmdfs_sb_info *sbi = conn->sbi;
179062306a36Sopenharmony_ci	struct file *filp = NULL;
179162306a36Sopenharmony_ci	const struct cred *old_cred = NULL;
179262306a36Sopenharmony_ci	struct hmdfs_stash_dir_context ctx = {
179362306a36Sopenharmony_ci		.dctx.actor = hmdfs_has_stash_file,
179462306a36Sopenharmony_ci	};
179562306a36Sopenharmony_ci	struct hmdfs_inode_tbl tbl;
179662306a36Sopenharmony_ci	int err;
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci	old_cred = hmdfs_override_creds(sbi->cred);
179962306a36Sopenharmony_ci	filp = hmdfs_open_stash_dir(&sbi->stash_work_dir, conn->cid);
180062306a36Sopenharmony_ci	if (IS_ERR(filp))
180162306a36Sopenharmony_ci		goto out;
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci	memset(&tbl, 0, sizeof(tbl));
180462306a36Sopenharmony_ci	ctx.tbl = &tbl;
180562306a36Sopenharmony_ci	err = iterate_dir(filp, &ctx.dctx);
180662306a36Sopenharmony_ci	if (!err && ctx.tbl->cnt > 0)
180762306a36Sopenharmony_ci		conn->need_rebuild_stash_list = true;
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	fput(filp);
181062306a36Sopenharmony_ciout:
181162306a36Sopenharmony_ci	hmdfs_revert_creds(old_cred);
181262306a36Sopenharmony_ci	hmdfs_info("peer 0x%x:0x%llx %sneed to rebuild stash list",
181362306a36Sopenharmony_ci		   conn->owner, conn->device_id,
181462306a36Sopenharmony_ci		   conn->need_rebuild_stash_list ? "" : "don't ");
181562306a36Sopenharmony_ci	complete(&work->done);
181662306a36Sopenharmony_ci}
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_cistatic void hmdfs_stash_add_do_check(struct hmdfs_peer *conn, int evt,
181962306a36Sopenharmony_ci				     unsigned int seq)
182062306a36Sopenharmony_ci{
182162306a36Sopenharmony_ci	struct hmdfs_sb_info *sbi = conn->sbi;
182262306a36Sopenharmony_ci	struct hmdfs_check_work work = {
182362306a36Sopenharmony_ci		.conn = conn,
182462306a36Sopenharmony_ci		.done = COMPLETION_INITIALIZER_ONSTACK(work.done),
182562306a36Sopenharmony_ci	};
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	if (!hmdfs_is_stash_enabled(sbi))
182862306a36Sopenharmony_ci		return;
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci	INIT_WORK_ONSTACK(&work.work, hmdfs_rebuild_check_work_fn);
183162306a36Sopenharmony_ci	schedule_work(&work.work);
183262306a36Sopenharmony_ci	wait_for_completion(&work.done);
183362306a36Sopenharmony_ci}
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_cistatic void
183662306a36Sopenharmony_cihmdfs_update_peer_rebuild_stats(struct hmdfs_rebuild_statistics *rebuild_stats,
183762306a36Sopenharmony_ci				const struct hmdfs_rebuild_stats *stats)
183862306a36Sopenharmony_ci{
183962306a36Sopenharmony_ci	rebuild_stats->cur_ok = stats->succeed;
184062306a36Sopenharmony_ci	rebuild_stats->cur_fail = stats->fail;
184162306a36Sopenharmony_ci	rebuild_stats->cur_invalid = stats->invalid;
184262306a36Sopenharmony_ci	rebuild_stats->total_ok += stats->succeed;
184362306a36Sopenharmony_ci	rebuild_stats->total_fail += stats->fail;
184462306a36Sopenharmony_ci	rebuild_stats->total_invalid += stats->invalid;
184562306a36Sopenharmony_ci}
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_ci/* rebuild stash inode list */
184862306a36Sopenharmony_cistatic void hmdfs_stash_online_prepare(struct hmdfs_peer *conn, int evt,
184962306a36Sopenharmony_ci				       unsigned int seq)
185062306a36Sopenharmony_ci{
185162306a36Sopenharmony_ci	struct hmdfs_sb_info *sbi = conn->sbi;
185262306a36Sopenharmony_ci	struct file *filp = NULL;
185362306a36Sopenharmony_ci	const struct cred *old_cred = NULL;
185462306a36Sopenharmony_ci	int err;
185562306a36Sopenharmony_ci	struct hmdfs_rebuild_stats stats;
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ci	if (!hmdfs_is_stash_enabled(sbi) ||
185862306a36Sopenharmony_ci	    !conn->need_rebuild_stash_list)
185962306a36Sopenharmony_ci		return;
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ci	/* release seq_lock to prevent blocking no-online sync cb */
186262306a36Sopenharmony_ci	mutex_unlock(&conn->seq_lock);
186362306a36Sopenharmony_ci	old_cred = hmdfs_override_creds(sbi->cred);
186462306a36Sopenharmony_ci	filp = hmdfs_open_stash_dir(&sbi->stash_work_dir, conn->cid);
186562306a36Sopenharmony_ci	if (IS_ERR(filp))
186662306a36Sopenharmony_ci		goto out;
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci	memset(&stats, 0, sizeof(stats));
186962306a36Sopenharmony_ci	err = hmdfs_iter_stash_file(conn, seq, filp,
187062306a36Sopenharmony_ci				    hmdfs_rebuild_stash_list, &stats);
187162306a36Sopenharmony_ci	if (err == -ESHUTDOWN) {
187262306a36Sopenharmony_ci		hmdfs_info("peer 0x%x:0x%llx offline again during rebuild",
187362306a36Sopenharmony_ci			   conn->owner, conn->device_id);
187462306a36Sopenharmony_ci	} else {
187562306a36Sopenharmony_ci		WRITE_ONCE(conn->need_rebuild_stash_list, false);
187662306a36Sopenharmony_ci		if (err)
187762306a36Sopenharmony_ci			hmdfs_warning("partial rebuild fail err %d", err);
187862306a36Sopenharmony_ci	}
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci	hmdfs_update_peer_rebuild_stats(&conn->stats.rebuild, &stats);
188162306a36Sopenharmony_ci	hmdfs_info("peer 0x%x:0x%llx rebuild stashed-file total %u succeed %u fail %u invalid %u",
188262306a36Sopenharmony_ci		   conn->owner, conn->device_id, stats.total, stats.succeed,
188362306a36Sopenharmony_ci		   stats.fail, stats.invalid);
188462306a36Sopenharmony_ci	fput(filp);
188562306a36Sopenharmony_ciout:
188662306a36Sopenharmony_ci	conn->stats.rebuild.time++;
188762306a36Sopenharmony_ci	hmdfs_revert_creds(old_cred);
188862306a36Sopenharmony_ci	if (!READ_ONCE(conn->need_rebuild_stash_list)) {
188962306a36Sopenharmony_ci		/*
189062306a36Sopenharmony_ci		 * Use smp_mb__before_atomic() to ensure order between
189162306a36Sopenharmony_ci		 * writing @conn->need_rebuild_stash_list and
189262306a36Sopenharmony_ci		 * reading conn->rebuild_inode_status_nr.
189362306a36Sopenharmony_ci		 */
189462306a36Sopenharmony_ci		smp_mb__before_atomic();
189562306a36Sopenharmony_ci		/*
189662306a36Sopenharmony_ci		 * Wait until all inodes finish rebuilding stash status before
189762306a36Sopenharmony_ci		 * accessing @conn->stashed_inode_list in restoring.
189862306a36Sopenharmony_ci		 */
189962306a36Sopenharmony_ci		wait_event(conn->rebuild_inode_status_wq,
190062306a36Sopenharmony_ci			   !atomic_read(&conn->rebuild_inode_status_nr));
190162306a36Sopenharmony_ci	}
190262306a36Sopenharmony_ci	mutex_lock(&conn->seq_lock);
190362306a36Sopenharmony_ci}
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_cistatic void
190662306a36Sopenharmony_cihmdfs_update_peer_restore_stats(struct hmdfs_restore_statistics *restore_stats,
190762306a36Sopenharmony_ci				const struct hmdfs_restore_stats *stats)
190862306a36Sopenharmony_ci{
190962306a36Sopenharmony_ci	restore_stats->cur_ok = stats->succeed;
191062306a36Sopenharmony_ci	restore_stats->cur_fail = stats->fail;
191162306a36Sopenharmony_ci	restore_stats->cur_keep = stats->keep;
191262306a36Sopenharmony_ci	restore_stats->total_ok += stats->succeed;
191362306a36Sopenharmony_ci	restore_stats->total_fail += stats->fail;
191462306a36Sopenharmony_ci	restore_stats->total_keep += stats->keep;
191562306a36Sopenharmony_ci	restore_stats->ok_pages += stats->ok_pages;
191662306a36Sopenharmony_ci	restore_stats->fail_pages += stats->fail_pages;
191762306a36Sopenharmony_ci}
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_cistatic void hmdfs_stash_online_do_restore(struct hmdfs_peer *conn, int evt,
192062306a36Sopenharmony_ci					  unsigned int seq)
192162306a36Sopenharmony_ci{
192262306a36Sopenharmony_ci	struct hmdfs_sb_info *sbi = conn->sbi;
192362306a36Sopenharmony_ci	struct file *filp = NULL;
192462306a36Sopenharmony_ci	const struct cred *old_cred = NULL;
192562306a36Sopenharmony_ci	struct hmdfs_restore_stats stats;
192662306a36Sopenharmony_ci	int err = 0;
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_ci	if (!hmdfs_is_stash_enabled(sbi) || conn->need_rebuild_stash_list) {
192962306a36Sopenharmony_ci		if (conn->need_rebuild_stash_list)
193062306a36Sopenharmony_ci			hmdfs_info("peer 0x%x:0x%llx skip restoring due to rebuild-need",
193162306a36Sopenharmony_ci				   conn->owner, conn->device_id);
193262306a36Sopenharmony_ci		return;
193362306a36Sopenharmony_ci	}
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci	/* release seq_lock to prevent blocking no-online sync cb */
193662306a36Sopenharmony_ci	mutex_unlock(&conn->seq_lock);
193762306a36Sopenharmony_ci	/* For dir iteration, file read and unlink */
193862306a36Sopenharmony_ci	old_cred = hmdfs_override_creds(conn->sbi->cred);
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_ci	memset(&stats, 0, sizeof(stats));
194162306a36Sopenharmony_ci	filp = hmdfs_open_stash_dir(&sbi->stash_work_dir, conn->cid);
194262306a36Sopenharmony_ci	if (IS_ERR(filp)) {
194362306a36Sopenharmony_ci		err = PTR_ERR(filp);
194462306a36Sopenharmony_ci		goto out;
194562306a36Sopenharmony_ci	}
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci	err = hmdfs_iter_stash_file(conn, seq, filp,
194862306a36Sopenharmony_ci				    hmdfs_restore_files, &stats);
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_ci	fput(filp);
195162306a36Sopenharmony_ciout:
195262306a36Sopenharmony_ci	hmdfs_revert_creds(old_cred);
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_ci	/* offline again ? */
195562306a36Sopenharmony_ci	if (err != -ESHUTDOWN)
195662306a36Sopenharmony_ci		hmdfs_drop_stashed_inodes(conn);
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	hmdfs_update_peer_restore_stats(&conn->stats.restore, &stats);
195962306a36Sopenharmony_ci	hmdfs_info("peer 0x%x:0x%llx restore stashed-file ok %u fail %u keep %u",
196062306a36Sopenharmony_ci		   conn->owner, conn->device_id,
196162306a36Sopenharmony_ci		   stats.succeed, stats.fail, stats.keep);
196262306a36Sopenharmony_ci
196362306a36Sopenharmony_ci	mutex_lock(&conn->seq_lock);
196462306a36Sopenharmony_ci}
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_cistatic void hmdfs_stash_del_do_cleanup(struct hmdfs_peer *conn, int evt,
196762306a36Sopenharmony_ci				       unsigned int seq)
196862306a36Sopenharmony_ci{
196962306a36Sopenharmony_ci	struct hmdfs_inode_info *info = NULL;
197062306a36Sopenharmony_ci	struct hmdfs_inode_info *next = NULL;
197162306a36Sopenharmony_ci	unsigned int preparing;
197262306a36Sopenharmony_ci
197362306a36Sopenharmony_ci	if (!hmdfs_is_stash_enabled(conn->sbi))
197462306a36Sopenharmony_ci		return;
197562306a36Sopenharmony_ci
197662306a36Sopenharmony_ci	/* Async cb is cancelled */
197762306a36Sopenharmony_ci	preparing = 0;
197862306a36Sopenharmony_ci	list_for_each_entry_safe(info, next, &conn->wr_opened_inode_list,
197962306a36Sopenharmony_ci				 wr_opened_node) {
198062306a36Sopenharmony_ci		int status = READ_ONCE(info->stash_status);
198162306a36Sopenharmony_ci
198262306a36Sopenharmony_ci		if (status == HMDFS_REMOTE_INODE_STASHING) {
198362306a36Sopenharmony_ci			struct hmdfs_cache_info *cache = NULL;
198462306a36Sopenharmony_ci
198562306a36Sopenharmony_ci			spin_lock(&info->stash_lock);
198662306a36Sopenharmony_ci			cache = info->cache;
198762306a36Sopenharmony_ci			info->cache = NULL;
198862306a36Sopenharmony_ci			info->stash_status = HMDFS_REMOTE_INODE_NONE;
198962306a36Sopenharmony_ci			spin_unlock(&info->stash_lock);
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_ci			hmdfs_remote_del_wr_opened_inode(conn, info);
199262306a36Sopenharmony_ci			hmdfs_del_file_cache(cache);
199362306a36Sopenharmony_ci			/* put inode after all access are completed */
199462306a36Sopenharmony_ci			iput(&info->vfs_inode);
199562306a36Sopenharmony_ci			preparing++;
199662306a36Sopenharmony_ci		}
199762306a36Sopenharmony_ci	}
199862306a36Sopenharmony_ci	hmdfs_info("release %u preparing inodes", preparing);
199962306a36Sopenharmony_ci
200062306a36Sopenharmony_ci	hmdfs_info("release %u pinned inodes", conn->stashed_inode_nr);
200162306a36Sopenharmony_ci	if (list_empty(&conn->stashed_inode_list))
200262306a36Sopenharmony_ci		return;
200362306a36Sopenharmony_ci
200462306a36Sopenharmony_ci	list_for_each_entry_safe(info, next,
200562306a36Sopenharmony_ci				 &conn->stashed_inode_list, stash_node)
200662306a36Sopenharmony_ci		hmdfs_untrack_stashed_inode(conn, info);
200762306a36Sopenharmony_ci}
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_civoid hmdfs_exit_stash(struct hmdfs_sb_info *sbi)
201062306a36Sopenharmony_ci{
201162306a36Sopenharmony_ci	if (!sbi->s_offline_stash)
201262306a36Sopenharmony_ci		return;
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_ci	if (sbi->stash_work_dir.dentry) {
201562306a36Sopenharmony_ci		path_put(&sbi->stash_work_dir);
201662306a36Sopenharmony_ci		sbi->stash_work_dir.dentry = NULL;
201762306a36Sopenharmony_ci	}
201862306a36Sopenharmony_ci}
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_ciint hmdfs_init_stash(struct hmdfs_sb_info *sbi)
202162306a36Sopenharmony_ci{
202262306a36Sopenharmony_ci	int err = 0;
202362306a36Sopenharmony_ci	struct path parent;
202462306a36Sopenharmony_ci	struct dentry *child = NULL;
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_ci	if (!sbi->s_offline_stash)
202762306a36Sopenharmony_ci		return 0;
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci	err = kern_path(sbi->cache_dir, LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
203062306a36Sopenharmony_ci			&parent);
203162306a36Sopenharmony_ci	if (err) {
203262306a36Sopenharmony_ci		hmdfs_err("invalid cache dir err %d", err);
203362306a36Sopenharmony_ci		goto out;
203462306a36Sopenharmony_ci	}
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci	child = hmdfs_stash_new_work_dir(parent.dentry);
203762306a36Sopenharmony_ci	if (!IS_ERR(child)) {
203862306a36Sopenharmony_ci		sbi->stash_work_dir.mnt = mntget(parent.mnt);
203962306a36Sopenharmony_ci		sbi->stash_work_dir.dentry = child;
204062306a36Sopenharmony_ci	} else {
204162306a36Sopenharmony_ci		err = PTR_ERR(child);
204262306a36Sopenharmony_ci		hmdfs_err("create stash work dir err %d", err);
204362306a36Sopenharmony_ci	}
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_ci	path_put(&parent);
204662306a36Sopenharmony_ciout:
204762306a36Sopenharmony_ci	return err;
204862306a36Sopenharmony_ci}
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_cistatic int hmdfs_stash_write_local_file(struct hmdfs_peer *conn,
205162306a36Sopenharmony_ci					struct hmdfs_inode_info *info,
205262306a36Sopenharmony_ci					struct hmdfs_writepage_context *ctx,
205362306a36Sopenharmony_ci					struct hmdfs_cache_info *cache)
205462306a36Sopenharmony_ci{
205562306a36Sopenharmony_ci	struct page *page = ctx->page;
205662306a36Sopenharmony_ci	const struct cred *old_cred = NULL;
205762306a36Sopenharmony_ci	void *buf = NULL;
205862306a36Sopenharmony_ci	loff_t pos;
205962306a36Sopenharmony_ci	unsigned int flags;
206062306a36Sopenharmony_ci	ssize_t written;
206162306a36Sopenharmony_ci	int err = 0;
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci	buf = kmap(page);
206462306a36Sopenharmony_ci	pos = (loff_t)page->index << PAGE_SHIFT;
206562306a36Sopenharmony_ci	/* enable NOFS for memory allocation */
206662306a36Sopenharmony_ci	flags = memalloc_nofs_save();
206762306a36Sopenharmony_ci	old_cred = hmdfs_override_creds(conn->sbi->cred);
206862306a36Sopenharmony_ci	pos += cache->data_offs << HMDFS_STASH_BLK_SHIFT;
206962306a36Sopenharmony_ci	written = kernel_write(cache->cache_file, buf, ctx->count, &pos);
207062306a36Sopenharmony_ci	hmdfs_revert_creds(old_cred);
207162306a36Sopenharmony_ci	memalloc_nofs_restore(flags);
207262306a36Sopenharmony_ci	kunmap(page);
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci	if (written != ctx->count) {
207562306a36Sopenharmony_ci		hmdfs_err("stash peer 0x%x:0x%llx ino 0x%llx page 0x%lx data_offs 0x%x len %u err %zd",
207662306a36Sopenharmony_ci			  conn->owner, conn->device_id, info->remote_ino,
207762306a36Sopenharmony_ci			  page->index, cache->data_offs, ctx->count, written);
207862306a36Sopenharmony_ci		err = -EIO;
207962306a36Sopenharmony_ci	}
208062306a36Sopenharmony_ci
208162306a36Sopenharmony_ci	return err;
208262306a36Sopenharmony_ci}
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ciint hmdfs_stash_writepage(struct hmdfs_peer *conn,
208562306a36Sopenharmony_ci			  struct hmdfs_writepage_context *ctx)
208662306a36Sopenharmony_ci{
208762306a36Sopenharmony_ci	struct inode *inode = ctx->page->mapping->host;
208862306a36Sopenharmony_ci	struct hmdfs_inode_info *info = hmdfs_i(inode);
208962306a36Sopenharmony_ci	struct hmdfs_cache_info *cache = NULL;
209062306a36Sopenharmony_ci	int err;
209162306a36Sopenharmony_ci
209262306a36Sopenharmony_ci	/* e.g. fail to create stash file */
209362306a36Sopenharmony_ci	cache = info->cache;
209462306a36Sopenharmony_ci	if (!cache)
209562306a36Sopenharmony_ci		return -EIO;
209662306a36Sopenharmony_ci
209762306a36Sopenharmony_ci	err = hmdfs_stash_write_local_file(conn, info, ctx, cache);
209862306a36Sopenharmony_ci	if (!err) {
209962306a36Sopenharmony_ci		hmdfs_client_writepage_done(info, ctx);
210062306a36Sopenharmony_ci		atomic64_inc(&cache->written_pgs);
210162306a36Sopenharmony_ci		put_task_struct(ctx->caller);
210262306a36Sopenharmony_ci		kfree(ctx);
210362306a36Sopenharmony_ci	}
210462306a36Sopenharmony_ci	atomic64_inc(&cache->to_write_pgs);
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_ci	return err;
210762306a36Sopenharmony_ci}
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_cistatic void hmdfs_stash_rebuild_status(struct hmdfs_peer *conn,
211062306a36Sopenharmony_ci				       struct inode *inode)
211162306a36Sopenharmony_ci{
211262306a36Sopenharmony_ci	char *path_str = NULL;
211362306a36Sopenharmony_ci	struct hmdfs_inode_info *info = NULL;
211462306a36Sopenharmony_ci	const struct cred *old_cred = NULL;
211562306a36Sopenharmony_ci	struct path path;
211662306a36Sopenharmony_ci	struct path *stash_path = NULL;
211762306a36Sopenharmony_ci	int err = 0;
211862306a36Sopenharmony_ci
211962306a36Sopenharmony_ci	path_str = kmalloc(HMDFS_STASH_PATH_LEN, GFP_KERNEL);
212062306a36Sopenharmony_ci	if (!path_str) {
212162306a36Sopenharmony_ci		err = -ENOMEM;
212262306a36Sopenharmony_ci		return;
212362306a36Sopenharmony_ci	}
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci	info = hmdfs_i(inode);
212662306a36Sopenharmony_ci	err = snprintf(path_str, HMDFS_STASH_PATH_LEN, "%s/0x%llx",
212762306a36Sopenharmony_ci		       conn->cid, info->remote_ino);
212862306a36Sopenharmony_ci	if (err >= HMDFS_STASH_PATH_LEN) {
212962306a36Sopenharmony_ci		kfree(path_str);
213062306a36Sopenharmony_ci		hmdfs_err("peer 0x%x:0x%llx inode 0x%llx too long name len",
213162306a36Sopenharmony_ci			  conn->owner, conn->device_id, info->remote_ino);
213262306a36Sopenharmony_ci		return;
213362306a36Sopenharmony_ci	}
213462306a36Sopenharmony_ci	old_cred = hmdfs_override_creds(conn->sbi->cred);
213562306a36Sopenharmony_ci	stash_path = &conn->sbi->stash_work_dir;
213662306a36Sopenharmony_ci	err = vfs_path_lookup(stash_path->dentry, stash_path->mnt,
213762306a36Sopenharmony_ci			      path_str, 0, &path);
213862306a36Sopenharmony_ci	hmdfs_revert_creds(old_cred);
213962306a36Sopenharmony_ci	if (!err) {
214062306a36Sopenharmony_ci		if (hmdfs_is_reg(path.dentry)) {
214162306a36Sopenharmony_ci			WRITE_ONCE(info->stash_status,
214262306a36Sopenharmony_ci				   HMDFS_REMOTE_INODE_RESTORING);
214362306a36Sopenharmony_ci			ihold(&info->vfs_inode);
214462306a36Sopenharmony_ci			hmdfs_track_inode_locked(conn, info);
214562306a36Sopenharmony_ci		} else {
214662306a36Sopenharmony_ci			hmdfs_info("peer 0x%x:0x%llx inode 0x%llx unexpected stashed file mode 0%o",
214762306a36Sopenharmony_ci				    conn->owner, conn->device_id,
214862306a36Sopenharmony_ci				    info->remote_ino,
214962306a36Sopenharmony_ci				    d_inode(path.dentry)->i_mode);
215062306a36Sopenharmony_ci		}
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ci		path_put(&path);
215362306a36Sopenharmony_ci	} else if (err && err != -ENOENT) {
215462306a36Sopenharmony_ci		hmdfs_err("peer 0x%x:0x%llx inode 0x%llx find %s err %d",
215562306a36Sopenharmony_ci			   conn->owner, conn->device_id, info->remote_ino,
215662306a36Sopenharmony_ci			   path_str, err);
215762306a36Sopenharmony_ci	}
215862306a36Sopenharmony_ci
215962306a36Sopenharmony_ci	kfree(path_str);
216062306a36Sopenharmony_ci}
216162306a36Sopenharmony_ci
216262306a36Sopenharmony_cistatic inline bool
216362306a36Sopenharmony_cihmdfs_need_rebuild_inode_stash_status(struct hmdfs_peer *conn, umode_t mode)
216462306a36Sopenharmony_ci{
216562306a36Sopenharmony_ci	return hmdfs_is_stash_enabled(conn->sbi) &&
216662306a36Sopenharmony_ci	       READ_ONCE(conn->need_rebuild_stash_list) &&
216762306a36Sopenharmony_ci	       (S_ISREG(mode) || S_ISLNK(mode));
216862306a36Sopenharmony_ci}
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_civoid hmdfs_remote_init_stash_status(struct hmdfs_peer *conn,
217162306a36Sopenharmony_ci				    struct inode *inode, umode_t mode)
217262306a36Sopenharmony_ci{
217362306a36Sopenharmony_ci	if (!hmdfs_need_rebuild_inode_stash_status(conn, mode))
217462306a36Sopenharmony_ci		return;
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci	atomic_inc(&conn->rebuild_inode_status_nr);
217762306a36Sopenharmony_ci	/*
217862306a36Sopenharmony_ci	 * Use smp_mb__after_atomic() to ensure order between writing
217962306a36Sopenharmony_ci	 * @conn->rebuild_inode_status_nr and reading
218062306a36Sopenharmony_ci	 * @conn->need_rebuild_stash_list.
218162306a36Sopenharmony_ci	 */
218262306a36Sopenharmony_ci	smp_mb__after_atomic();
218362306a36Sopenharmony_ci	if (READ_ONCE(conn->need_rebuild_stash_list))
218462306a36Sopenharmony_ci		hmdfs_stash_rebuild_status(conn, inode);
218562306a36Sopenharmony_ci	if (atomic_dec_and_test(&conn->rebuild_inode_status_nr))
218662306a36Sopenharmony_ci		wake_up(&conn->rebuild_inode_status_wq);
218762306a36Sopenharmony_ci}
218862306a36Sopenharmony_ci
218962306a36Sopenharmony_cistatic struct hmdfs_node_cb_desc stash_cb[] = {
219062306a36Sopenharmony_ci	{
219162306a36Sopenharmony_ci		.evt = NODE_EVT_OFFLINE,
219262306a36Sopenharmony_ci		.sync = true,
219362306a36Sopenharmony_ci		.fn = hmdfs_stash_offline_prepare,
219462306a36Sopenharmony_ci	},
219562306a36Sopenharmony_ci	{
219662306a36Sopenharmony_ci		.evt = NODE_EVT_OFFLINE,
219762306a36Sopenharmony_ci		.sync = false,
219862306a36Sopenharmony_ci		.fn = hmdfs_stash_offline_do_stash,
219962306a36Sopenharmony_ci	},
220062306a36Sopenharmony_ci	{
220162306a36Sopenharmony_ci		.evt = NODE_EVT_ADD,
220262306a36Sopenharmony_ci		.sync = true,
220362306a36Sopenharmony_ci		.fn = hmdfs_stash_add_do_check,
220462306a36Sopenharmony_ci	},
220562306a36Sopenharmony_ci	{
220662306a36Sopenharmony_ci		.evt = NODE_EVT_ONLINE,
220762306a36Sopenharmony_ci		.sync = false,
220862306a36Sopenharmony_ci		.fn = hmdfs_stash_online_prepare,
220962306a36Sopenharmony_ci	},
221062306a36Sopenharmony_ci	{
221162306a36Sopenharmony_ci		.evt = NODE_EVT_ONLINE,
221262306a36Sopenharmony_ci		.sync = false,
221362306a36Sopenharmony_ci		.fn = hmdfs_stash_online_do_restore,
221462306a36Sopenharmony_ci	},
221562306a36Sopenharmony_ci	{
221662306a36Sopenharmony_ci		.evt = NODE_EVT_DEL,
221762306a36Sopenharmony_ci		.sync = true,
221862306a36Sopenharmony_ci		.fn = hmdfs_stash_del_do_cleanup,
221962306a36Sopenharmony_ci	},
222062306a36Sopenharmony_ci};
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_civoid __init hmdfs_stash_add_node_evt_cb(void)
222362306a36Sopenharmony_ci{
222462306a36Sopenharmony_ci	hmdfs_node_add_evt_cb(stash_cb, ARRAY_SIZE(stash_cb));
222562306a36Sopenharmony_ci}
222662306a36Sopenharmony_ci
2227