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