/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) 2021. Huawei Technologies Co., Ltd. All rights reserved. * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef EUFS_DEP_H #define EUFS_DEP_H #include #include #include #include "euler.h" #include "alloc_interface.h" /** * Dep type: * - diradd (for create/symlink/link/mknod) * - dirrem */ enum fsync_type { FSYNC_DEP, FSYNC_RENAME, FSYNC_SYSCALL, }; extern int disable_persisters; extern int persist_period; extern int persisters_per_socket; #define eufs_dep_seq_after(a, b) ((s32)((b) - (a)) < 0) #define eufs_dep_seq_after_eq(a, b) ((s32)((a) - (b)) >= 0) void eufs_dir_fsync_oneshot(struct inode *dir); void fsync_on_draining(struct inode *dir, struct inode *inode); void fsync_rename_inodes(struct inode *old_inode, struct inode *new_inode, struct inode **locked_inodes); void fsync_oneshot(struct inode *inode); enum dep_type { DEP_DIRADD, /* Hard link is detected by checking inode->i_nlink */ DEP_DIRREM, DEP_TYPE_COUNT, }; struct dep_node { struct list_head node; struct list_head owner_node; u32 seq; /* Type of the dependency */ enum dep_type type; /* Previous dentry */ struct nv_dict_entry *prevde; /* header of the list */ u64 *nv_header; /* Related Dentry, which also points to an inode */ struct nv_dict_entry __pmem *de; /* inode for de->pi */ struct inode *inode; struct inode *dir; } __aligned(CACHELINE_SIZE); int dep_init(struct super_block *sb); void dep_fini(struct super_block *sb); static __always_inline void request_persistence(struct inode *inode) { struct eufs_sb_info *sbi = EUFS_SB(inode->i_sb); struct eufs_inode_info *vi = EUFS_I(inode); int cpu; BUG_ON(!inode_is_locked(inode)); if (!vi->i_is_dirty) vi->i_is_dirty = true; if (vi->i_is_persisting) return; cpu = get_cpu(); llist_add(&vi->i_persistee_node, per_cpu_ptr(sbi->persistee_list, cpu)); put_cpu(); eufs_dbg_vlimit("sbi->s_nr_dirty_inodes=%d ++ vi=%px @cpu=%d\n", atomic_read(&sbi->s_nr_dirty_inodes), vi, cpu); if (atomic_inc_return(&sbi->s_nr_dirty_inodes) > max_dirty_inodes && !sbi->s_draining) sbi->s_draining = true; vi->i_is_persisting = true; ihold(inode); } /* precondition: dir inode is mutex-locked */ static __always_inline void dep_insert(struct inode *dir, struct dep_node *dep) { struct eufs_inode_info *dir_vi = EUFS_I(dir); struct eufs_inode_info *child_vi = EUFS_I(dep->inode); struct eufs_sb_info *sbi = EUFS_SB(dir->i_sb); inode_dep_lock(dir); inode_header_unlock(dir); list_add_tail(&dep->node, &dir_vi->i_dep_list); spin_lock(&child_vi->i_owner_lock); list_add_tail(&dep->owner_node, &child_vi->i_owner_list); spin_unlock(&child_vi->i_owner_lock); inode_dep_unlock(dir); eufs_dbg_vlimit("sbi->s_nr_dep_nodes=%d ++\n", atomic_read(&sbi->s_nr_dep_nodes)); if (atomic_inc_return(&sbi->s_nr_dep_nodes) > max_dep_nodes && !sbi->s_draining) { sbi->s_draining = true; } /* Request a persistence */ request_persistence(dir); } static __always_inline bool eufs_valid_inode_in_de(struct nv_dict_entry *de, struct inode *inode) { return (le64_to_cpu(de->inode) == inode->i_ino); } static __always_inline void dep_new_insert(struct dep_node *dep, struct inode *dir, enum dep_type type, struct nv_dict_entry *prevde, u64 *nv_header, struct nv_dict_entry *de, struct inode *inode, u32 seq) { dep->type = type; dep->prevde = prevde; dep->nv_header = nv_header; dep->de = de; dep->inode = inode; dep->dir = dir; dep->seq = seq; NV_ASSERT(eufs_valid_inode_in_de(dep->de, dep->inode)); ihold(dep->inode); dep_insert(dir, dep); } static __always_inline void persist_dentry(struct nv_dict_entry *de) { NV_ASSERT(de); NV_ASSERT((u64)de % CACHELINE_SIZE == 0); NV_ASSERT(sizeof(de) <= CACHELINE_SIZE); eufs_flush_cacheline(de); } static __always_inline void persist_pinode(struct eufs_inode *pi) { WARN_ON(!EUFS_IS_HEAD_PI(pi)); NV_ASSERT(pi); NV_ASSERT((u64)pi % CACHELINE_SIZE == 0); NV_ASSERT(sizeof(pi) <= EUFS_INODE_SIZE); eufs_flush_cacheline(EUFS_FRESH_PI(pi)); eufs_flush_cacheline(&EUFS_FRESH_PI(pi)->i_fresh); } static __always_inline void persist_name(struct super_block *sb, const struct nv_dict_entry *de, struct alloc_batch *ab) { size_t len = HASHLEN_LEN(de->hv); struct nv_name_ext *next; const char *name; if (likely(len <= FIRST_LEN)) { /* embedded in de */ return; } next = s2p(sb, de->nextname); len -= FIRST_LEN; name = next->name; eufs_alloc_batch_add(sb, ab, (void *)name); while (len > FOLLOW_LEN) { next = s2p(sb, next->nextname); eufs_flush_cacheline(name); len -= FOLLOW_LEN; name = next->name; eufs_alloc_batch_add(sb, ab, (void *)name); } eufs_flush_cacheline(name); } static __always_inline void persist_symlink(void *root) { u64 len; NV_ASSERT(root); NV_ASSERT(((u64)root) % PAGE_SIZE == 0); len = EUFS_SYMLINK_HASHLEN_LEN(*((u64 *)root)); NV_ASSERT(len <= EUFS_MAX_SYMLINK_LEN); BUG_ON(len > EUFS_MAX_SYMLINK_LEN); eufs_flush_range(root, EUFS_SYMLINK_SIZE(len)); } static __always_inline void persist_page(const char *page) { NV_ASSERT(page); NV_ASSERT(((u64)page) % PAGE_SIZE == 0); eufs_flush_page(page); } #endif /* EUFS_DEP_H */