hulk inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9732 ---------------------------------------- Memory-semantic interconnects such as CXL 3.0 and Huawei United Bus make remote memory directly addressable. This raises a question for Linux: should cross-node memory become another storage tier that can be exposed through existing POSIX filesystem interfaces? To explore this question, we built XMFS, an in-kernel prototype that exports cross-node shared memory through a standard POSIX filesystem interface. Initial experiments with metadata-intensive workloads and container image sharing suggest the approach is practical. Signed-off-by: Yifan Qiao <qiaoyifan4@huawei.com> --- fs/xmfs/xmfs_i.h | 1098 ++++++++++++++++++++ fs/xmfs/dir.c | 160 +++ fs/xmfs/file.c | 2261 +++++++++++++++++++++++++++++++++++++++++ fs/xmfs/gc.c | 953 +++++++++++++++++ fs/xmfs/inode.c | 344 +++++++ fs/xmfs/namei.c | 1794 ++++++++++++++++++++++++++++++++ fs/xmfs/replay.c | 907 +++++++++++++++++ fs/xmfs/super.c | 1677 ++++++++++++++++++++++++++++++ fs/xmfs/symlink.c | 87 ++ fs/xmfs/urma.c | 2482 +++++++++++++++++++++++++++++++++++++++++++++ fs/xmfs/xattr.c | 591 +++++++++++ fs/Kconfig | 1 + fs/Makefile | 1 + fs/xmfs/Kconfig | 21 + fs/xmfs/Makefile | 16 + 15 files changed, 12393 insertions(+) create mode 100644 fs/xmfs/xmfs_i.h create mode 100644 fs/xmfs/dir.c create mode 100644 fs/xmfs/file.c create mode 100644 fs/xmfs/gc.c create mode 100644 fs/xmfs/inode.c create mode 100644 fs/xmfs/namei.c create mode 100644 fs/xmfs/replay.c create mode 100644 fs/xmfs/super.c create mode 100644 fs/xmfs/symlink.c create mode 100644 fs/xmfs/urma.c create mode 100644 fs/xmfs/xattr.c create mode 100644 fs/xmfs/Kconfig create mode 100644 fs/xmfs/Makefile diff --git a/fs/xmfs/xmfs_i.h b/fs/xmfs/xmfs_i.h new file mode 100644 index 000000000000..7f58b2f64f7b --- /dev/null +++ b/fs/xmfs/xmfs_i.h @@ -0,0 +1,1098 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * XMFS: Log Structured Filesystem for UB + * Copyright (C) 2024. Huawei Technologies Co., Ltd + * + * This program can be distributed under the terms of the GNU GPL. + * See the file COPYING. + */ +#ifndef _XMFS_H +#define _XMFS_H + +#include <linux/atomic.h> +#include <linux/fs.h> +#include <linux/xarray.h> +#include <linux/spinlock.h> +#include <linux/mm.h> +#include <linux/ratelimit.h> +#include <linux/xattr.h> +#include <linux/vmalloc.h> +#include <linux/workqueue.h> + +#include <linux/pgtable.h> +#include <linux/atomic.h> +#include <linux/libnvdimm.h> + +#ifdef CONFIG_XMFS_FS_URMA +#include <ub/urma/ubcore_types.h> +#include <ub/urma/ubcore_uapi.h> +#include <ub/urma/ubcore_api.h> +#endif + +/* #define PER_FS 1 */ + +#define XMFS_MAGIC 0x53464D58 + +#define TOTAL_SIZE (8UL << 30) +#define DATA_ALIGN_KB 4UL + +#ifdef PER_FS +#define MAX_LOG_PER_TRUNK 18724 +#else +#define MAX_LOG_PER_TRUNK 6096 +#endif + +#define ALIGN_TO_MB(size) \ + (((size) + (2 * 1024 * 1024 - 1)) & ~(2 * 1024 * 1024 - 1)) + +#define ALIGN_TO_2MB_LOWER(size) ((size) & ~(2 * 1024 * 1024 - 1)) + +/* sb is 4MB, supports up to ~8TB */ +#define SB_OFFSET 0 +#define SB_SIZE (4UL << 20) + +/* mount msg is 2MB */ +#define MOUNT_MSG_OFFSET (SB_OFFSET + SB_SIZE) +#define MOUNT_MSG_SIZE (1UL << 21) + +/* msg ring is 2MB */ +#define MSG_RING_OFFSET (MOUNT_MSG_OFFSET + MOUNT_MSG_SIZE) +#define MSG_RING_SIZE (1 << 21) + +#define INODE_TABLE_OFFSET (MSG_RING_OFFSET + MSG_RING_SIZE) +#define INODE_TABLE_SIZE (2UL << 21) +#define XMFS_INODE_TABLE_MAGIC 0x494E4F44UL + +#define DATA_AREA_OFFSET (INODE_TABLE_OFFSET + INODE_TABLE_SIZE) + +/* XMFS trunk is 2MB */ +#define XMFS_DEFAULT_CHUNK_SIZE (1UL << 21) + +/* XMFS data align is 64KB */ +#define XMFS_DATA_SHIFT 16 + +/* XMFS log ops */ +#define XMFS_CREATE (1 << 0) +#define XMFS_DELETE (1 << 1) +#define XMFS_UPDATE (1 << 2) +#define XMFS_LINK (1 << 3) +#define XMFS_MKDIR (1 << 4) +#define XMFS_RENAME (1 << 5) +#define XMFS_SETATTR (1 << 6) +#define XMFS_SYMLINK (1 << 7) +#define XMFS_XATTR (1 << 8) +#define XMFS_ROOT (1 << 9) +#define XMFS_GC (1 << 10) + +#define RENAME_HOLD (1 << 4) + +#define XMFS_DATA_TRUNK_MAGIC 0x44415441UL +#define XMFS_GC_INTERVAL_DEFAULT 5 +#define XMFS_GC_BATCH 51200 +#define XMFS_BITMAP_CAPACITY (SB_SIZE - offsetof(struct xmfs_sb_index, bitmap)) + +enum xmfs_rw_msg_type { + MSG_RW_REQUEST = 1, + MSG_RW_GRANT, + MSG_RW_COMPLETE, + MSG_RW_ERROR, +}; + +struct xmfs_data_trunk_header { + unsigned long magic; + unsigned long live_page_count; + unsigned int total_page_count; + unsigned long last_modify_version; +}; + +struct xmfs_gc_ctl { + spinlock_t gc_lock; + wait_queue_head_t wait; + unsigned long gc_cursor; + unsigned long gc_batch; + bool gc_running; + bool need_replay; +}; + +struct xmfs_rw_msg { + enum xmfs_rw_msg_type type; + int magic; + int slave_id; + unsigned long request_id; + unsigned int ops; + unsigned long data_pages; + unsigned long log_entries; + umode_t mode; + unsigned long p_ino; + unsigned long i_ino; + char name[256]; + loff_t size; + unsigned long log_trunk_addr; + int log_slot_start; + int log_slot_count; + unsigned long data_addr; + unsigned long data_page_count; + unsigned long second_data_addr; + unsigned long second_data_pages; + unsigned long assigned_ino; + int status; + unsigned long version; +}; + +struct xmfs_notify_msg { + int slave_id; + unsigned long request_id; + int msg_type; + int _pad[5]; +}; + +extern const struct xattr_handler *xmfs_host_xattr_handlers[]; +extern const struct xattr_handler *xmfs_slave_xattr_handlers[]; + +enum xmfs_msg_type { + MSG_STOP_READ = 1, + MSG_READ, +}; + +enum xmfs_mode { + XMFS_SHARED_MEM_MODE, + XMFS_URMA_MODE, + XMFS_HYBRID_MODE, +}; + +enum xmfs_param { + Opt_host, + Opt_replay, + Opt_addr, + Opt_size, + Opt_freq, + Opt_log, + Opt_debug, + Opt_always_hot, + Opt_fallocate, + Opt_mode, +#ifdef CONFIG_XMFS_FS_URMA + Opt_urma_va, + Opt_urma_eid_subnet_prefix, + Opt_urma_eid_interface_id, + Opt_urma_jetty_id, + Opt_urma_token_id, + Opt_urma_dev, +#endif +}; + +/* 4KB */ +struct xmfs_msg_queue { + unsigned short len; + unsigned short type; + unsigned long addr; + char pad[4072]; +}; + +struct xmfs_fs_context { + bool host; + bool replay; + unsigned long addr; + unsigned long size; + unsigned long fallocate_size; + int freq; + int log_size; + int mode; + bool debug; + bool always_hot; +#ifdef CONFIG_XMFS_FS_URMA + uint64_t urma_va; + uint64_t urma_eid_subnet_prefix; + uint64_t urma_eid_interface_id; + uint32_t urma_jetty_id; + uint32_t urma_token_id; + char dev_name[100]; +#endif +}; + +#ifdef PER_FS +struct xmfs_sb_index { + unsigned long count; + unsigned long log_start; + unsigned long log_current; + unsigned long data_current; + unsigned long data_count; + unsigned long last_update; + char pad[4048]; +}; + +struct xmfs_log { + unsigned long index; + unsigned long end_index; + unsigned long data_offset; + loff_t size; + unsigned int ops; + umode_t mode; + char name[256]; + unsigned long p_ino; + unsigned long i_ino; + unsigned long offset; + unsigned long version; + unsigned long writer_id; +}; + +struct xmfs_log_trunk { + unsigned long used; + unsigned long next_trunk; + unsigned long version_base; + unsigned long version_max; + char pad[1192]; + struct xmfs_log log[MAX_LOG_PER_TRUNK]; +}; + +#else +struct xmfs_sb_index { + unsigned long magic; + /* High-water mark: next slot index to allocate. */ + unsigned long count; + /* Actual number of used trunks (bitmap != 'u'). */ + unsigned long used_trunk_count; + /* Log of start inode. */ + unsigned long log_start; + /* Log of last inode. */ + unsigned long log_current; + /* Current address of data. */ + unsigned long data_current; + /* Number of 4 KiB data blocks in the current trunk. */ + unsigned long data_count; + /* Timestamp. */ + unsigned long last_update; + /* Filesystem ID. */ + __kernel_fsid_t fsid; + int log_size; + unsigned long len; + int bitmap_size; + char bitmap[]; +}; + +struct xmfs_log { + unsigned long index; + unsigned long end_index; + unsigned long data_offset; + loff_t size; + unsigned int ops; + umode_t mode; + char name[256]; + unsigned long p_ino; + unsigned long i_ino; + unsigned long offset; + unsigned long version; + unsigned long writer_id; +}; /* 336 */ + +struct xmfs_xattr { + unsigned long size; + unsigned long name_size; + char *name; + char value[]; +}; + +#define MAX_LOG (4096 / (sizeof(struct xmfs_log))) + +struct xmfs_log_trunk { + unsigned long used; + unsigned long next_trunk; + unsigned long version_base; + unsigned long version_max; + struct xmfs_log log[]; +}; +#endif + +#define XMLT_INDEX_SIZE (offsetof(struct xmfs_log_trunk, log[0])) + +enum xmfs_urma_ops { + XMFS_URMA_READ, + XMFS_URMA_WRITE, + XMFS_URMA_CAS, + XMFS_URMA_SEND, +}; + +struct xmfs_urma_msg { + enum xmfs_urma_ops ops; + uint64_t offset; + uint64_t len; + void *local_addr; + void *write_buffer; + int wr_cnt; + int slot; + uint64_t cas_cmp_data; + uint64_t cas_swap_data; +}; + +struct xmfs_urma_pool { + void *base; + size_t total_size; + size_t slot_size; + u32 nr_slots; + u32 free_slots; + unsigned long *bitmap; + spinlock_t lock; + wait_queue_head_t waitq; +}; + +struct urma_info { + uint64_t urma_remote_va; + uint64_t urma_send_va; + uint64_t urma_recv_va; + uint64_t urma_eid_subnet_prefix; + uint64_t urma_eid_interface_id; + uint32_t urma_jetty_id; + uint32_t urma_token_id; + uint32_t urma_send_token_id; + uint32_t urma_recv_token_id; +}; + +struct xmfs_grant_wait { + struct completion done; + struct xmfs_rw_msg grant; +}; + +#ifdef CONFIG_XMFS_FS_URMA + +struct xmfs_urma_ctl { + struct ubcore_client ubc_client; + int ubc_index; + struct ubcore_device *ubc_dev; + struct ubcore_jetty *jetty[5]; + struct ubcore_jetty *send_jetty; + struct ubcore_jfc *jfc; + struct ubcore_jfc *send_jfc; + struct ubcore_jfc *recv_jfc; + struct ubcore_jfr *jfr; + struct ubcore_jfr *send_jfr; + struct ubcore_target_seg *local_seg; + struct ubcore_target_seg *send_seg; + struct ubcore_target_seg *recv_seg; + struct ubcore_tjetty *tjetty; + struct ubcore_tjetty *send_tjettys[64]; + struct ubcore_tjetty *tjettys[64]; + struct ubcore_target_seg *remote_seg; + struct xmfs_urma_pool pool; + struct xmfs_urma_pool send_pool; + struct xarray grant_waits; + struct urma_info own_info; + struct urma_info send_info; + void *urma_local_va; + void *recv_buf; + void *send_buf; + uint64_t urma_remote_va; + uint64_t urma_eid_subnet_prefix; + uint64_t urma_eid_interface_id; + uint32_t urma_jetty_id; + uint32_t urma_token_id; + uint64_t last_log_start; + uint64_t last_data_start; +}; + +#define XMFS_URMA_DEFAULT_WR_SIZE 104857600 /* 100 MiB */ +#else +struct xmfs_urma_ctl { + struct xmfs_urma_pool pool; + struct xmfs_urma_pool send_pool; + struct xarray grant_waits; + struct urma_info own_info; + struct urma_info send_info; + int ubc_index; + void *urma_local_va; + uint64_t urma_remote_va; + uint64_t urma_eid_subnet_prefix; + uint64_t urma_eid_interface_id; + uint32_t urma_jetty_id; + uint32_t urma_token_id; + uint64_t last_log_start; + uint64_t last_data_start; +}; +#endif + +struct xmfs_inode_entry { + unsigned long i_ino; + unsigned long p_ino; + umode_t mode; + unsigned int nlink; + loff_t size; + struct timespec64 ctime; + struct timespec64 mtime; + struct timespec64 atime; + char name[256]; +}; + +struct xmfs_inode_table { + unsigned long magic; + unsigned long count; + struct xmfs_inode_entry entries[]; +}; + +struct xmfs_sb_info { + int magic; + int id; + struct super_block *sb; + void *kaddr; + phys_addr_t start; + unsigned long len; + struct task_struct *thread; + struct file *dax_filp; + struct dax_device *dax_dev; + struct bdev_handle *handlep; + unsigned long used_ino; + unsigned long max_chunk; + dev_t dev; + int freq; + int log_size; + bool debug; + bool always_hot; + unsigned long trunk_size; + int mode; + struct xarray pinned_inodes; + char dev_name[100]; + + /* Ratelimit XMFS diagnostics. */ + struct ratelimit_state s_error_ratelimit_state; + struct ratelimit_state s_warning_ratelimit_state; + struct ratelimit_state s_msg_ratelimit_state; + atomic_t s_error_count; + atomic_t s_warning_count; + atomic_t s_msg_count; + + /* host uses */ + spinlock_t space_lock; + struct rw_semaphore chain_rwsem; + spinlock_t version_lock; + struct mutex replay_lock; + bool full; + unsigned long log_current; + unsigned long fallocate_size; + bool during_replay; + struct xmfs_gc_ctl gc_ctl; + struct task_struct *host_worker; + int max_slave_id; + struct task_struct *gc_thread; + unsigned long gc_interval; + int mount_slave; + + /* slave uses */ + int stop_cnt; + unsigned long addr; + unsigned long last_update; + unsigned long log_start; + unsigned long last_replayed_slot; + unsigned long skip_no_ops; + spinlock_t local_ring_lock; + unsigned long rw_request_id; + + /* urma related */ + struct xmfs_urma_ctl urma_cfg; + + struct workqueue_struct *prefetch_wq; + struct workqueue_struct *send_wq; +}; + +static inline struct xmfs_sb_info *XMFS_SB(struct super_block *sb) +{ + return sb->s_fs_info; +} + +void xmfs_init_log_ratelimits(struct xmfs_sb_info *sbi); + +__printf(5, 6) void __xmfs_error(struct super_block *sb, const char *function, + unsigned int line, int error, const char *fmt, + ...); +__printf(5, 6) void __xmfs_error_inode(struct inode *inode, + const char *function, unsigned int line, + int error, const char *fmt, ...); +__printf(5, 6) void __xmfs_error_file(struct file *file, const char *function, + unsigned int line, int error, + const char *fmt, ...); +__printf(4, 5) void __xmfs_warning(struct super_block *sb, const char *function, + unsigned int line, const char *fmt, ...); +__printf(4, 5) void __xmfs_warning_inode(const struct inode *inode, + const char *function, + unsigned int line, const char *fmt, + ...); +__printf(2, 3) void __xmfs_msg(struct super_block *sb, const char *fmt, ...); + +#ifdef CONFIG_PRINTK +#define xmfs_error(sb, fmt, ...) \ + __xmfs_error((sb), __func__, __LINE__, 0, (fmt), ##__VA_ARGS__) +#define xmfs_error_err(sb, err, fmt, ...) \ + __xmfs_error((sb), __func__, __LINE__, (err), (fmt), ##__VA_ARGS__) +#define xmfs_error_inode(inode, fmt, ...) \ + __xmfs_error_inode((inode), __func__, __LINE__, 0, (fmt), ##__VA_ARGS__) +#define xmfs_error_inode_err(inode, err, fmt, ...) \ + __xmfs_error_inode((inode), __func__, __LINE__, (err), (fmt), \ + ##__VA_ARGS__) +#define xmfs_error_file(file, fmt, ...) \ + __xmfs_error_file((file), __func__, __LINE__, 0, (fmt), ##__VA_ARGS__) +#define xmfs_warning(sb, fmt, ...) \ + __xmfs_warning((sb), __func__, __LINE__, (fmt), ##__VA_ARGS__) +#define xmfs_warning_inode(inode, fmt, ...) \ + __xmfs_warning_inode((inode), __func__, __LINE__, (fmt), ##__VA_ARGS__) +#define xmfs_msg(sb, fmt, ...) \ + __xmfs_msg((sb), (fmt), ##__VA_ARGS__) +#else +#define xmfs_error(sb, fmt, ...) no_printk((fmt), ##__VA_ARGS__) +#define xmfs_error_err(sb, err, fmt, ...) no_printk((fmt), ##__VA_ARGS__) +#define xmfs_error_inode(inode, fmt, ...) no_printk((fmt), ##__VA_ARGS__) +#define xmfs_error_inode_err(inode, err, fmt, ...) \ + no_printk((fmt), ##__VA_ARGS__) +#define xmfs_error_file(file, fmt, ...) no_printk((fmt), ##__VA_ARGS__) +#define xmfs_warning(sb, fmt, ...) no_printk((fmt), ##__VA_ARGS__) +#define xmfs_warning_inode(inode, fmt, ...) no_printk((fmt), ##__VA_ARGS__) +#define xmfs_msg(sb, fmt, ...) no_printk((fmt), ##__VA_ARGS__) +#endif + +#define xmfs_info(sb, fmt, ...) xmfs_msg((sb), KERN_INFO fmt, ##__VA_ARGS__) + +struct mount_msg_info { + atomic_t id; + atomic_t host_mount; + /* 'D' for done, 'C' for unmount, 'T' for during mount. */ + char msg[256]; + /* Each slave writes its last_update here. */ + unsigned long slave_versions[64]; + struct urma_info urma_infos[64]; + struct urma_info send_infos[64]; +}; + +struct page_freq { + atomic_t read_count; + bool in_cache; + bool never_in_cache; + bool already_in_cache; +}; + +struct xmfs_cache_work { + struct work_struct work; + struct inode *inode; + pgoff_t index; + pgoff_t end_index; +}; + +struct xmfs_write_work { + struct work_struct work; + struct inode *inode; + pgoff_t index; + pgoff_t end_index; + size_t size; +}; + +struct xmfs_send_work { + struct work_struct work; + struct xmfs_rw_msg grant; + struct xmfs_sb_info *sbi; +}; + +struct xmfs_inode_info { + unsigned long i_ino; + struct inode vfs_inode; + /* Inode log start address. */ + unsigned long log_start; + /* Log trunk where the last replay ends. */ + unsigned long log_replay; + /* Symlink name address. */ + unsigned long symname; + struct xarray shared_pages; + struct simple_xattrs xattrs; + struct timespec64 btime; +#ifdef CONFIG_XMFS_FS_URMA + struct ubcore_jfs_wr *wr; + void *write_buffer; + int wr_cnt; + int *slots; +#endif + struct xarray page_freq; + unsigned long start_write; + unsigned long second_write; + unsigned long second_write_count; + unsigned long count; + unsigned long data_start; + unsigned long data_size; + bool leaf; + bool tmp; + bool die; + bool fallocate; + bool pinned; + unsigned long inode_table_slot; + struct workqueue_struct *write_wq; +}; + +struct xmfs_read_extent { + pgoff_t start_index; + unsigned long start_offset; + unsigned int nr_pages; +}; + +struct host_mount_args { + struct xmfs_sb_info *sbi; + int id; +}; + +void xmfs_init_file_inode(struct inode *inode); +void xmfs_init_file_mapping(struct inode *inode); +void xmfs_init_dir_inode(struct inode *inode, bool host); +void xmfs_init_dir_file(struct inode *inode); +void xmfs_init_dentry_operations(struct super_block *sb); +void xmfs_init_symlink_mapping(struct inode *inode); +void xmfs_init_symlink_operations(struct inode *inode); +void xmfs_init_special_inode_operations(struct inode *inode); +int xmfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, + struct iattr *attr); +int xmfs_getattr(struct mnt_idmap *idmap, const struct path *path, + struct kstat *stat, u32 request_mask, unsigned int flags); +int xmfs_slave_setattr(struct mnt_idmap *idmap, struct dentry *dentry, + struct iattr *attr); +int xmfs_slave_getattr(struct mnt_idmap *idmap, const struct path *path, + struct kstat *stat, u32 request_mask, + unsigned int flags); +int xmfs_update_time(struct inode *inode, int flags); +struct inode *xmfs_get_root(struct super_block *sb, int host_id); +int xmfs_send_req_wait_reply(struct xmfs_sb_info *sbi, int type); +void arch_invalidate_pmem(void *addr, size_t size); +void xmfs_mark_page_dead(struct xmfs_sb_info *sbi, unsigned long data_offset); +void xmfs_mark_page_live(struct xmfs_sb_info *sbi, unsigned long data_offset); + +struct xmfs_data_reserve_result { + unsigned long data_addr; + unsigned long data_page_count; + unsigned long second_data_addr; + unsigned long second_data_pages; + unsigned long trunk_addrs[4]; + int trunk_count; +}; + +unsigned long xmfs_add_log_v3(struct xmfs_log *xm_log, struct xmfs_sb_info *sbi, + size_t namelen, unsigned long ino, + bool update_version); +long xmfs_find_free_slot(struct xmfs_sb_index *xmsi, struct xmfs_sb_info *sbi); +long xmfs_alloc_and_mark_slot(struct xmfs_sb_index *xmsi, + struct xmfs_sb_info *sbi, char type); +int xmfs_gc_thread_fn(void *data); +int xmfs_gc_thread_slave_fn(void *data); +int xmfs_quiesce_wait(struct xmfs_sb_info *sbi); +int xmfs_host_worker_fn(void *data); +void xmfs_host_process_ring(struct xmfs_sb_info *sbi); +int xmfs_slave_request_write(struct xmfs_sb_info *sbi, struct xmfs_rw_msg *req); +int xmfs_slave_wait_grant(struct xmfs_sb_info *sbi, struct xmfs_rw_msg *grant, + unsigned long grant_slot, unsigned long request_id); +void xmfs_slave_send_complete(struct xmfs_sb_info *sbi, + unsigned long request_id, loff_t comp_size, + struct xmfs_rw_msg *orig_req, + struct xmfs_rw_msg *grant); +int xmfs_pmem_write(struct xmfs_sb_info *sbi, unsigned long offset, + const void *buf, unsigned long size); +int xmfs_pmem_write_multi_pages(struct xmfs_sb_info *sbi, unsigned long offset, + struct page **pages, unsigned long page_num); +int xmfs_pmem_read_multi_pages(struct xmfs_sb_info *sbi, unsigned long *offsets, + struct page **pages, unsigned long page_num); +int xmfs_pmem_read(struct xmfs_sb_info *sbi, unsigned long offset, void *buf, + unsigned long size); +int xmfs_pmem_cas(struct xmfs_sb_info *sbi, unsigned long offset, + uint64_t old_val, uint64_t new_val); +int xmfs_import_jetty(struct xmfs_sb_info *sbi, struct urma_info info, + int index, bool is_send); +int xmfs_send_request(struct xmfs_sb_info *sbi, struct xmfs_rw_msg *req, + struct xmfs_grant_wait *gw, int id); +#ifdef CONFIG_XMFS_FS_URMA +int xmfs_urma_send_write_pages(struct xmfs_sb_info *sbi, + struct ubcore_jfs_wr *wr); +#endif +void *urma_pool_acquire_nslots(struct xmfs_urma_pool *pool, int nr, u32 *slots); +void urma_pool_release_nslots(struct xmfs_urma_pool *pool, int nr, + const u32 *slots); +int xmfs_urma_read_ubuf(struct xmfs_sb_info *sbi, void __user *buf, + struct xmfs_read_extent *exts, int ext_cnt, + size_t size); +void xmfs_prefetch_worker(struct work_struct *work); + +static inline int xmfs_data_write(struct xmfs_sb_info *sbi, + unsigned long offset, const void *buf, + unsigned long size) +{ + if (sbi->mode != XMFS_URMA_MODE) { + memcpy(sbi->kaddr + offset, buf, size); + return 0; + } + return xmfs_pmem_write(sbi, offset, buf, size); +} + +static inline int xmfs_data_read(struct xmfs_sb_info *sbi, unsigned long offset, + void *buf, unsigned long size) +{ + if (sbi->mode != XMFS_URMA_MODE) { + memcpy(buf, sbi->kaddr + offset, size); + return 0; + } + return xmfs_pmem_read(sbi, offset, buf, size); +} + +int xmfs_gc_main(struct xmfs_sb_info *sbi); +int xmfs_inode_eq(struct inode *inode, void *_ino); +int xmfs_inode_set(struct inode *inode, void *_ino); +unsigned int xmfs_inode_table_sync_nlink(struct xmfs_inode_table *tbl, + unsigned long ino); +bool xmfs_inode_table_remove(struct xmfs_inode_table *tbl, unsigned long ino, + unsigned long p_ino, const char *name); +void xmfs_replay_plays_gc(struct xmfs_sb_info *sbi); +void xmfs_clear_inode_bits(struct xmfs_sb_info *sbi, unsigned long ino); +ssize_t xmfs_listxattr(struct dentry *dentry, char *buffer, size_t size); +int xmfs_initxattrs(struct inode *inode, const struct xattr *xattr_array, + void *fs_info); +int xmfs_replay_new_entries(struct xmfs_sb_info *sbi, struct dentry *dentry, + unsigned long last_update); +void xmfs_populate_from_inode_table(struct xmfs_sb_info *sbi); +ssize_t xmfs_simple_xattr_list(struct inode *inode, + struct simple_xattrs *xattrs, char *buffer, + size_t size); +int xmfs_xattr_list_one(char **buffer, ssize_t *remaining_size, + const char *name); +int xmfs_simple_xattr_get(struct simple_xattrs *xattrs, const char *name, + void *buffer, size_t size); +struct simple_xattr *xmfs_simple_xattr_set(struct simple_xattrs *xattrs, + const char *name, const void *value, + size_t size, int flags); +struct simple_xattr *xmfs_simple_xattr_alloc(const void *value, size_t size); +void xmfs_simple_xattrs_init(struct simple_xattrs *xattrs); +void xmfs_simple_xattrs_free(struct simple_xattrs *xattrs, size_t *freed_space); +size_t xmfs_simple_xattr_space(const char *name, size_t size); +void xmfs_simple_xattr_free(struct simple_xattr *xattr); +long xmfs_common_fallocate(struct inode *inode, int mode, loff_t offset, + loff_t len); +int xmfs_urma_register(struct xmfs_sb_info *sbi, struct xmfs_fs_context *ctx); +void xmfs_urma_unregister(struct xmfs_sb_info *sbi); +int xmfs_urma_import(struct xmfs_sb_info *sbi); +void xmfs_urma_unimport(struct xmfs_sb_info *sbi); +int xmfs_urma_send(struct xmfs_sb_info *sbi, struct xmfs_urma_msg *msg); +void xmfs_host_handle_urma_request(struct xmfs_sb_info *sbi, int slave_i, + struct xmfs_rw_msg *req, + struct xmfs_rw_msg *grant); +#ifdef CONFIG_XMFS_FS_URMA +int xmfs_urma_fill_write_page(struct xmfs_sb_info *sbi, + struct ubcore_jfs_wr *wr, + struct xmfs_urma_msg *msg, int slot); +#endif +int xmfs_urma_write_ubuf(struct xmfs_sb_info *sbi, void __user *buf, + size_t size, unsigned long offset); + +static inline struct xmfs_inode_info *XMFS_I(struct inode *inode) +{ + return container_of(inode, struct xmfs_inode_info, vfs_inode); +} + +static inline int local_update_log_and_data(struct xmfs_sb_info *sbi, + unsigned long offset, + unsigned long size) +{ + switch (sbi->mode) { + case XMFS_SHARED_MEM_MODE: + case XMFS_HYBRID_MODE: + return 0; + case XMFS_URMA_MODE: + return xmfs_pmem_read(sbi, offset, sbi->urma_cfg.urma_local_va, + size); + default: + return -EINVAL; + } +} + +static inline int get_meta_and_copy(struct xmfs_sb_info *sbi, + unsigned long offset, void *addr, + unsigned long size, void *local_addr, + unsigned long local_offset) +{ + int err; + struct xmfs_urma_msg msg; + + if (addr == NULL) + return -EINVAL; + if (offset > sbi->len - 2097152) + return -EINVAL; + + switch (sbi->mode) { + case XMFS_SHARED_MEM_MODE: + case XMFS_HYBRID_MODE: + return copy_mc_to_kernel(addr, sbi->kaddr + offset, size); + case XMFS_URMA_MODE: + if (!local_addr) { + msg.ops = XMFS_URMA_READ; + msg.offset = offset; + msg.len = size; + msg.local_addr = addr; + err = xmfs_urma_send(sbi, &msg); + return err; + } else { + return copy_mc_to_kernel(addr, + local_addr + offset - + local_offset, + size); + } + default: + return -EINVAL; + } +} + +static inline int get_data_and_copy(struct xmfs_sb_info *sbi, + unsigned long offset, void *addr, + unsigned long size, void *local_addr, + unsigned long local_offset) +{ + int err; + struct xmfs_urma_msg msg; + + if (addr == NULL) + return -EINVAL; + if (offset > sbi->len - 2097152) + return -EINVAL; + + switch (sbi->mode) { + case XMFS_SHARED_MEM_MODE: + return copy_mc_to_kernel(addr, sbi->kaddr + offset, size); + case XMFS_URMA_MODE: + case XMFS_HYBRID_MODE: + if (!local_addr) { + msg.ops = XMFS_URMA_READ; + msg.offset = offset; + msg.len = size; + msg.local_addr = addr; + err = xmfs_urma_send(sbi, &msg); + return err; + } else { + return copy_mc_to_kernel(addr, + local_addr + offset - + local_offset, + size); + } + default: + return -EINVAL; + } +} + +static inline void *get_meta(struct xmfs_sb_info *sbi, unsigned long offset, + unsigned long size) +{ + struct xmfs_urma_msg msg; + int err = 0; + void *local_addr; + + if (offset > sbi->len - 2097152) + return NULL; + + switch (sbi->mode) { + case XMFS_SHARED_MEM_MODE: + case XMFS_HYBRID_MODE: + return sbi->kaddr + offset; + case XMFS_URMA_MODE: + local_addr = vmalloc(size); + if (!local_addr) + return local_addr; + msg.ops = XMFS_URMA_READ; + msg.offset = offset; + msg.len = size; + msg.local_addr = local_addr; + err = xmfs_urma_send(sbi, &msg); + if (err) { + xmfs_info(sbi->sb, "get meta err out %d", err); + vfree(local_addr); + local_addr = NULL; + } + return local_addr; + default: + return NULL; + } +} + +static inline void put_meta(struct xmfs_sb_info *sbi, void *local_addr) +{ + if (sbi->mode == XMFS_URMA_MODE) + vfree(local_addr); +} + +static inline void *get_data(struct xmfs_sb_info *sbi, unsigned long offset, + unsigned long size) +{ + struct xmfs_urma_msg msg; + int err = 0; + void *local_addr; + + if (offset > sbi->len - 2097152) + return NULL; + + switch (sbi->mode) { + case XMFS_SHARED_MEM_MODE: + return sbi->kaddr + offset; + case XMFS_URMA_MODE: + case XMFS_HYBRID_MODE: + local_addr = vmalloc(size); + if (!local_addr) + return local_addr; + msg.ops = XMFS_URMA_READ; + msg.offset = offset; + msg.len = size; + msg.local_addr = local_addr; + err = xmfs_urma_send(sbi, &msg); + if (err) { + vfree(local_addr); + local_addr = NULL; + } + return local_addr; + default: + return NULL; + } +} + +static inline void put_data(struct xmfs_sb_info *sbi, void *local_addr) +{ + if (sbi->mode == XMFS_URMA_MODE || sbi->mode == XMFS_HYBRID_MODE) + vfree(local_addr); +} + +static inline struct xmfs_sb_index *get_xmsi(struct xmfs_sb_info *sbi) +{ + return (struct xmfs_sb_index *)get_meta(sbi, SB_OFFSET, + sizeof(struct xmfs_sb_index)); +} + +static inline void put_xmsi(struct xmfs_sb_info *sbi, void *local_buf) +{ + put_meta(sbi, local_buf); +} + +static inline int get_xmsi_and_copy(struct xmfs_sb_info *sbi, + struct xmfs_sb_index *xmsi) +{ + return get_meta_and_copy(sbi, SB_OFFSET, xmsi, + sizeof(struct xmfs_sb_index), NULL, 0); +} + +static inline struct xmfs_log *get_log(struct xmfs_sb_info *sbi, + unsigned long offset) +{ + return (struct xmfs_log *)get_meta(sbi, offset, + sizeof(struct xmfs_log)); +} + +static inline void put_log(struct xmfs_sb_info *sbi, void *local_buf) +{ + put_meta(sbi, local_buf); +} + +static inline int get_log_and_copy(struct xmfs_sb_info *sbi, + unsigned long offset, struct xmfs_log *log) +{ + return get_meta_and_copy(sbi, offset, log, sizeof(struct xmfs_log), + NULL, 0); +} + +static inline struct xmfs_log_trunk *get_log_trunk(struct xmfs_sb_info *sbi, + unsigned long offset) +{ + return (struct xmfs_log_trunk *)get_meta(sbi, offset, 2097152); +} + +static inline struct xmfs_log_trunk * +get_log_trunk_meta(struct xmfs_sb_info *sbi, unsigned long offset) +{ + return (struct xmfs_log_trunk *)get_meta(sbi, offset, + sizeof(struct xmfs_log_trunk)); +} + +static inline struct xmfs_log_trunk * +get_log_trunk_data(struct xmfs_sb_info *sbi, unsigned long offset, + unsigned long idx) +{ + return (struct xmfs_log_trunk *)get_meta(sbi, offset, + sizeof(struct xmfs_log_trunk) + + idx * sizeof(struct xmfs_log)); +} + +static inline void put_log_trunk(struct xmfs_sb_info *sbi, void *local_buf) +{ + put_meta(sbi, local_buf); +} + +static inline int get_log_trunk_and_copy(struct xmfs_sb_info *sbi, + unsigned long offset, + struct xmfs_log_trunk *xmlt, + bool get_full_trunk) +{ + if (get_full_trunk) + return get_meta_and_copy(sbi, offset, xmlt, sbi->trunk_size, + NULL, 0); + return get_meta_and_copy(sbi, offset, xmlt, + sizeof(struct xmfs_log_trunk), NULL, 0); +} + +static inline struct mount_msg_info *get_mnt_msg(struct xmfs_sb_info *sbi) +{ + return (struct mount_msg_info *)get_meta(sbi, MOUNT_MSG_OFFSET, + sizeof(struct mount_msg_info)); +} + +static inline void put_mnt_msg(struct xmfs_sb_info *sbi, void *local_buf) +{ + put_meta(sbi, local_buf); +} + +static inline int get_mnt_msg_and_copy(struct xmfs_sb_info *sbi, + struct mount_msg_info *mm_info) +{ + return get_meta_and_copy(sbi, MOUNT_MSG_OFFSET, mm_info, + sizeof(struct mount_msg_info), NULL, 0); +} + +static inline int mnt_msg_write(struct xmfs_sb_info *sbi, int id, char c) +{ + switch (sbi->mode) { + case XMFS_SHARED_MEM_MODE: + case XMFS_HYBRID_MODE: { + struct mount_msg_info *mm_info = sbi->kaddr + MOUNT_MSG_OFFSET; + + return mm_info->msg[id] = c; + } + case XMFS_URMA_MODE: + return xmfs_pmem_write(sbi, + MOUNT_MSG_OFFSET + + offsetof(struct mount_msg_info, + msg) + + id, + &c, 1) ? + -1 : + c; + default: + return -1; + } +} + +static inline int xmfs_cas(struct xmfs_sb_info *sbi, int old, int new) +{ + switch (sbi->mode) { + case XMFS_SHARED_MEM_MODE: + case XMFS_HYBRID_MODE: { + struct mount_msg_info *mm_info = sbi->kaddr + MOUNT_MSG_OFFSET; + + return atomic_cmpxchg(&mm_info->id, old, new); + } + case XMFS_URMA_MODE: { + int ret = xmfs_pmem_cas(sbi, + MOUNT_MSG_OFFSET + + offsetof(struct mount_msg_info, + id), + (uint64_t)old, (uint64_t)new); + + return (ret == 0) ? old : 0; + } + default: + return 0; + } +} + +static inline int xmfs_atomic_read(struct xmfs_sb_info *sbi, + struct mount_msg_info *mm_info) +{ + return atomic_read(&mm_info->id); +} + +#endif diff --git a/fs/xmfs/dir.c b/fs/xmfs/dir.c new file mode 100644 index 000000000000..eb6f23d48e67 --- /dev/null +++ b/fs/xmfs/dir.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * XMFS: Log Structured Filesystem for UB + * Copyright (C) 2024. Huawei Technologies Co., Ltd + * + * This program can be distributed under the terms of the GNU GPL. + * See the file COPYING. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "xmfs_i.h" + +#include <linux/file.h> +#include <linux/kernel.h> +#include <linux/slab.h> + +static int xmfs_revalidate(struct dentry *dentry, unsigned int flags) +{ + struct xmfs_sb_info *sbi = XMFS_SB(dentry->d_sb); + struct xmfs_sb_index *xmsi = kmalloc(sizeof(*xmsi), GFP_ATOMIC); + int err = get_xmsi_and_copy(sbi, xmsi); + + if (unlikely(err)) { + xmfs_error(sbi->sb, + "hardware memory error when reading superblock during revalidate"); + kfree(xmsi); + return err; + } + /* + * xmfs_info(sbi->sb, "qyf revalidate %ld %ld", + * xmsi->last_update, sbi->last_update); + */ + if (xmsi->last_update != sbi->last_update) { + kfree(xmsi); + return 0; + } + kfree(xmsi); + return 1; +} + +static const struct dentry_operations xmfs_dentry_operations = { + .d_delete = always_delete_dentry, + .d_revalidate = xmfs_revalidate, +}; + +static int xmfs_iterate(struct file *file, struct dir_context *ctx) +{ + struct xmfs_sb_info *sbi = XMFS_SB(file->f_inode->i_sb); + struct inode *dir = file->f_inode; + struct xmfs_inode_table *tbl; + unsigned long max_entries, i __maybe_unused; + + if (!dir_emit_dots(file, ctx)) + return 0; + + { + struct xmfs_sb_index *xmsi = kmalloc(sizeof(*xmsi), GFP_KERNEL); + + if (xmsi) { + int err = get_xmsi_and_copy(sbi, xmsi); + + if (!err && xmsi->last_update != sbi->last_update) + xmfs_replay_new_entries(sbi, NULL, + xmsi->last_update); + kfree(xmsi); + } + } + + if (sbi->mode == XMFS_SHARED_MEM_MODE || sbi->mode == XMFS_HYBRID_MODE) + tbl = (struct xmfs_inode_table *)(sbi->kaddr + + INODE_TABLE_OFFSET); + else { + tbl = (struct xmfs_inode_table *)get_meta(sbi, + INODE_TABLE_OFFSET, + INODE_TABLE_SIZE); + /* + * tbl = (struct xmfs_inode_table *)get_meta( + * sbi, INODE_TABLE_OFFSET, + * max(sizeof(struct xmfs_inode_table) + + * tbl->count * sizeof(struct xmfs_inode_entry), + * INODE_TABLE_SIZE)); + */ + } + + if (!tbl || tbl->magic != XMFS_INODE_TABLE_MAGIC) { + if (tbl && sbi->mode == XMFS_URMA_MODE) + put_meta(sbi, tbl); + return 0; + } + /* xmfs_info(sbi->sb, "qyf iterate %d %d", tbl->count, ctx->pos); */ + /* + * for (i = 0; i < tbl->count; i++) { + * struct xmfs_inode_entry *e = &tbl->entries[i]; + * + * xmfs_info(sbi->sb, + * "tbl id %d ino %d pino %d name %s size %d", + * i, e->i_ino, e->p_ino, e->name, e->size); + * } + */ + if (ctx->pos - 2 >= tbl->count) + return 0; + max_entries = + (INODE_TABLE_SIZE - 4096) / sizeof(struct xmfs_inode_entry); + for (; ctx->pos - 2 < tbl->count && ctx->pos - 2 < max_entries; + ctx->pos++) { + struct xmfs_inode_entry *e = &tbl->entries[ctx->pos - 2]; + unsigned char d_type; + /* + * xmfs_info(sbi->sb, + * "qyf iterate iter %d %d name: %s size %ld", + * e->i_ino, e->p_ino, e->name, e->size); + */ + if (e->i_ino == 0 || e->p_ino != dir->i_ino) + continue; + + if (S_ISREG(e->mode)) + d_type = DT_REG; + else if (S_ISDIR(e->mode)) + d_type = DT_DIR; + else if (S_ISLNK(e->mode)) + d_type = DT_LNK; + else if (S_ISCHR(e->mode)) + d_type = DT_CHR; + else if (S_ISBLK(e->mode)) + d_type = DT_BLK; + else if (S_ISFIFO(e->mode)) + d_type = DT_FIFO; + else if (S_ISSOCK(e->mode)) + d_type = DT_SOCK; + else + d_type = DT_UNKNOWN; + + if (!dir_emit(ctx, e->name, strlen(e->name), e->i_ino, d_type)) + break; + } + + if (sbi->mode == XMFS_URMA_MODE) + put_meta(sbi, tbl); + return 0; +} + +static const struct file_operations xmfs_dir_operations = { + .iterate_shared = xmfs_iterate, + .open = dcache_dir_open, + .release = dcache_dir_close, + .llseek = dcache_dir_lseek, + .read = generic_read_dir, + .fsync = noop_fsync, +}; + +void xmfs_init_dir_file(struct inode *inode) +{ + inode->i_fop = &xmfs_dir_operations; +} + +void xmfs_init_dentry_operations(struct super_block *sb) +{ + sb->s_d_op = &xmfs_dentry_operations; +} diff --git a/fs/xmfs/file.c b/fs/xmfs/file.c new file mode 100644 index 000000000000..ef45c2b31f47 --- /dev/null +++ b/fs/xmfs/file.c @@ -0,0 +1,2261 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * XMFS: Log Structured Filesystem for UB + * Copyright (C) 2024. Huawei Technologies Co., Ltd + * + * This program can be distributed under the terms of the GNU GPL. + * See the file COPYING. + */ + +#include "xmfs_i.h" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/swap.h> +#include <linux/uio.h> +#include <linux/file.h> +#include <linux/seq_file.h> +#include <linux/pagemap.h> +#include <linux/fadvise.h> +#include <linux/splice.h> +#include <linux/falloc.h> + +#ifdef CONFIG_XMFS_FS_URMA +static ssize_t xmfs_file_write_iter_slave(struct kiocb *iocb, + struct iov_iter *from); +static ssize_t xmfs_file_write_ubuf_slave(struct kiocb *iocb, + struct iov_iter *from); +#else +static ssize_t xmfs_file_write_iter_slave(struct kiocb *iocb, + struct iov_iter *from) +{ + return -EOPNOTSUPP; +} +#endif +static ssize_t xmfs_file_read_ubuf_slave(struct kiocb *iocb, + struct iov_iter *to); +static int xmfs_read_page(struct file *file, struct page *page) +{ + struct inode *inode = page->mapping->host; + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + struct xmfs_inode_info *xmfs_inode = XMFS_I(inode); + unsigned long mapped_offset = + (unsigned long)xa_load(&xmfs_inode->shared_pages, page->index); + void *kaddr = kmap_local_page(page); + + if (mapped_offset) { + memcpy(kaddr, get_data(sbi, mapped_offset, PAGE_SIZE), + PAGE_SIZE); + } + kunmap_local(kaddr); + + return 0; +} + +#ifdef CONFIG_XMFS_FS_URMA +static void xmfs_write_worker(struct work_struct *work) +{ + struct xmfs_write_work *ww = + container_of(work, struct xmfs_write_work, work); + struct inode *inode = ww->inode; + struct address_space *mapping = inode->i_mapping; + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + struct page *page; + struct folio *folio; + struct xmfs_rw_msg grant = {}; + struct xmfs_rw_msg req = {}; + unsigned long end_index = ww->end_index; + unsigned long start_index = ww->index; + unsigned long page_num = end_index - start_index; + struct ubcore_jfs_wr *wrs; + int *slots; + void *write_buffer; + int wr_cnt = 0; + unsigned long mapped_offset; + struct xmfs_urma_msg msg = {}; + char *addr; + struct ubcore_jfs_wr *wr; + int err; + unsigned long log_base; + /* + * xmfs_info(sbi->sb, "qyf write work %d %d %d", start_index, + * end_index, ww->size); + */ + + req.ops = XMFS_UPDATE; + req.i_ino = inode->i_ino; + req.data_pages = page_num; + req.log_entries = 1; + req.size = ww->size; + req.type = MSG_RW_REQUEST; + req.slave_id = sbi->id; + spin_lock(&sbi->local_ring_lock); + req.request_id = ++sbi->rw_request_id; + spin_unlock(&sbi->local_ring_lock); + + struct xmfs_grant_wait gw; + + init_completion(&gw.done); + xa_store(&sbi->urma_cfg.grant_waits, req.request_id, &gw, GFP_KERNEL); + if (xmfs_quiesce_wait(sbi)) + return; + err = xmfs_send_request(sbi, &req, &gw, 0); + if (err) { + iput(inode); + return; + } + grant = gw.grant; + log_base = + grant.log_trunk_addr + offsetof(struct xmfs_log_trunk, log[0]); + /* + * xmfs_info(sbi->sb, "grant %d %d %d %d %d", + * grant.log_slot_count, grant.data_addr, + * grant.data_page_count, grant.second_data_addr, + * grant.log_slot_start); + */ + wrs = vzalloc(page_num * sizeof(struct ubcore_jfs_wr)); + if (!wrs) { + iput(inode); + return; + } + slots = vzalloc(sizeof(int) * page_num); + if (!slots) { + vfree(wrs); + iput(inode); + return; + } + + write_buffer = + urma_pool_acquire_nslots(&sbi->urma_cfg.pool, page_num, slots); + if (!write_buffer) { + vfree(wrs); + vfree(slots); + iput(inode); + return; + } + + unsigned long start_write = grant.data_addr; + unsigned long count = grant.data_page_count; + unsigned long second_write = grant.second_data_addr; + + for (int i = start_index; i < end_index; i++) { + if (count > 0) { + mapped_offset = start_write; + count--; + start_write += PAGE_SIZE; + if (start_write % XMFS_DEFAULT_CHUNK_SIZE == 0) + start_write += PAGE_SIZE; + } else if (second_write != 0) { + mapped_offset = second_write; + second_write += PAGE_SIZE; + if (second_write % XMFS_DEFAULT_CHUNK_SIZE == 0) + second_write += PAGE_SIZE; + } + + folio = filemap_get_folio(mapping, i); + if (IS_ERR(folio)) { + xmfs_warning(sbi->sb, "write work cannot get folio %d", + i); + continue; + } + xa_store(&(XMFS_I(inode)->shared_pages), i, + (void *)mapped_offset, GFP_KERNEL); + page = folio_page(folio, 0); + addr = kmap_local_page(page); + msg.ops = XMFS_URMA_WRITE; + msg.offset = mapped_offset; + msg.len = PAGE_SIZE; + msg.write_buffer = write_buffer + wr_cnt * PAGE_SIZE; + msg.local_addr = addr; + wr = &wrs[wr_cnt]; + err = xmfs_urma_fill_write_page(sbi, wr, &msg, slots[wr_cnt]); + if (err) { + xmfs_info(sbi->sb, + "write work fill write page failed %d folio", + i); + kunmap_local(addr); + folio_put(folio); + continue; + } + if (wr_cnt > 0) + wrs[wr_cnt - 1].next = wr; + wr_cnt++; + kunmap_local(addr); + folio_put(folio); + } + + if (wr_cnt > 0) { + wrs[wr_cnt - 1].next = NULL; + xmfs_urma_send_write_pages(sbi, wrs); + } + + urma_pool_release_nslots(&sbi->urma_cfg.pool, page_num, slots); + vfree(slots); + + for (int s = 0; s < grant.log_slot_count; s++) { + struct xmfs_log xm_log = {}; + + xm_log.ops = XMFS_UPDATE; + xm_log.i_ino = inode->i_ino; + xm_log.version = grant.version + s; + xm_log.writer_id = sbi->id; + if (s == 0) { + xm_log.index = start_index; + xm_log.end_index = start_index + grant.data_page_count; + xm_log.data_offset = grant.data_addr; + xm_log.size = grant.size; + } else { + xm_log.index = start_index + grant.data_page_count; + xm_log.end_index = end_index; + xm_log.data_offset = grant.second_data_addr; + xm_log.size = grant.size; + } + + xmfs_data_write(sbi, + log_base + (grant.log_slot_start + s) * + sizeof(struct xmfs_log), + &xm_log, sizeof(xm_log)); + /* + * xmfs_info(sbi->sb, "write work log %ld %ld %ld", + * log_base + (grant.log_slot_start + s) * + * sizeof(struct xmfs_log), + * xm_log.index, xm_log.end_index); + */ + } + iput(inode); + kfree(ww); +} +#endif + +void xmfs_prefetch_worker(struct work_struct *work) +{ + struct xmfs_cache_work *cw = + container_of(work, struct xmfs_cache_work, work); + struct inode *inode = cw->inode; + struct address_space *mapping = inode->i_mapping; + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + struct folio *folio; + unsigned long *offsets; + struct page **pages; + unsigned long mapped_offset; + int i; + int count = 0; + + offsets = + kzalloc((cw->end_index - cw->index + 1) * sizeof(unsigned long), + GFP_KERNEL); + if (!offsets) + return; + + pages = kzalloc((cw->end_index - cw->index + 1) * sizeof(struct page *), + GFP_KERNEL); + if (!pages) { + kfree(offsets); + return; + } + + for (i = cw->index; i < cw->end_index; i++) { + mapped_offset = + (unsigned long)xa_load(&XMFS_I(inode)->shared_pages, i); + if (!mapped_offset) + continue; + + folio = filemap_grab_folio(mapping, i); + + if (IS_ERR(folio)) + break; + + offsets[count] = mapped_offset; + pages[count] = &folio->page; + count++; + } + + if (count > 0) { + /* + * xmfs_info(sbi->sb, "qyf backgrount cp page %ld to %ld", + * cw->index, cw->end_index); + */ + xmfs_pmem_read_multi_pages(sbi, offsets, pages, count); + for (i = 0; i < count; i++) { + folio = page_folio(pages[i]); + + folio_mark_uptodate(folio); + folio_mark_dirty(folio); + folio_unlock(folio); + folio_put(folio); + } + } + kfree(offsets); + kfree(pages); + + kfree(cw); +} + +static int xmfs_write_begin(struct file *filp, struct address_space *mapping, + loff_t pos, unsigned int len, struct page **pagep, + void **fsdata) +{ + struct inode *inode = mapping->host; + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + int err = simple_write_begin(filp, mapping, pos, len, pagep, fsdata); + + if (err || sbi->id != 0 || sbi->during_replay || PageUptodate(*pagep) || + len == PAGE_SIZE) + return err; + return xmfs_read_page(filp, *pagep); +} + +unsigned long xmfs_get_data_offset_nolock(struct inode *inode, + struct xmfs_sb_info *sbi, + struct page *page) +{ + char *kaddr; + + if (XMFS_I(inode)->count > 0) { + unsigned long ret_addr; + + if (sbi->id == 0) { + kaddr = kmap_local_page(page); + xmfs_data_write(sbi, XMFS_I(inode)->start_write, kaddr, + PAGE_SIZE); + kunmap_local(kaddr); + } + ret_addr = XMFS_I(inode)->start_write; + XMFS_I(inode)->count--; + XMFS_I(inode)->start_write += PAGE_SIZE; + if (XMFS_I(inode)->start_write % XMFS_DEFAULT_CHUNK_SIZE == 0) + XMFS_I(inode)->start_write += PAGE_SIZE; + return ret_addr; + } else if (XMFS_I(inode)->second_write != 0) { + unsigned long ret_addr; + + if (sbi->id == 0) { + kaddr = kmap_local_page(page); + xmfs_data_write(sbi, XMFS_I(inode)->second_write, kaddr, + PAGE_SIZE); + kunmap_local(kaddr); + } + ret_addr = XMFS_I(inode)->second_write; + XMFS_I(inode)->second_write += PAGE_SIZE; + if (XMFS_I(inode)->second_write % XMFS_DEFAULT_CHUNK_SIZE == 0) + XMFS_I(inode)->second_write += PAGE_SIZE; + return ret_addr; + } + /* + * xmfs_error(inode->i_sb, + * "no reserved space for inode %ld", inode->i_ino); + */ + return 0; +} + +unsigned long xmfs_get_data_offset_fallocate(struct inode *inode, + struct xmfs_sb_info *sbi, + struct page *page) +{ + unsigned long index = page->index; + unsigned long data_start = XMFS_I(inode)->data_start; + unsigned long len = XMFS_I(inode)->data_size; + + if (index * PAGE_SIZE >= len) { + xmfs_error(sbi->sb, + "Not support write more than fallocate size now"); + return -1; + } + + char *kaddr = kmap_local_page(page); + + xmfs_data_write(sbi, data_start + index * PAGE_SIZE, kaddr, PAGE_SIZE); + kunmap_local(kaddr); + + return data_start + index * PAGE_SIZE; +} + +static int xmfs_write_end(struct file *file, struct address_space *mapping, + loff_t pos, unsigned int len, unsigned int copied, + struct page *page, void *fsdata) +{ + struct folio *folio = page_folio(page); + struct inode *inode = folio->mapping->host; + loff_t last_pos = pos + copied; + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + + if (!folio_test_uptodate(folio)) { + /* zero the stale part of the folio if we did a short copy */ + if (copied < len) { + size_t from = offset_in_folio(folio, pos); + + folio_zero_range(folio, from + copied, len - copied); + } + folio_mark_uptodate(folio); + } + folio_mark_dirty(folio); + + if (i_size_read(inode) < last_pos) + i_size_write(inode, last_pos); + + unsigned long offset; + + if (XMFS_I(inode)->fallocate) + offset = xmfs_get_data_offset_fallocate(inode, sbi, page); + else + offset = xmfs_get_data_offset_nolock(inode, sbi, page); + if (offset == 0) { + folio_unlock(folio); + folio_put(folio); + return copied; + } else if (offset < 0) { + folio_unlock(folio); + folio_put(folio); + return offset; + } + /* + * xmfs_info(sbi->sb, "write end index %d offset %ld", + * page->index, offset); + */ + /* + * if (sbi->id != 0) { + * folio_mark_dirty(folio); + * struct xmfs_urma_msg msg = {}; + * char *addr = kmap_local_page(page); + * struct ubcore_jfs_wr *wr; + * + * msg.ops = XMFS_URMA_WRITE; + * msg.offset = offset; + * msg.len = PAGE_SIZE; + * msg.write_buffer = XMFS_I(inode)->write_buffer + + * XMFS_I(inode)->wr_cnt * PAGE_SIZE; + * msg.local_addr = addr; + * wr = &XMFS_I(inode)->wr[XMFS_I(inode)->wr_cnt]; + * xmfs_urma_fill_write_page( + * sbi, wr, &msg, + * XMFS_I(inode)->slots[XMFS_I(inode)->wr_cnt]); + * if (XMFS_I(inode)->wr_cnt > 0) + * XMFS_I(inode)->wr[XMFS_I(inode)->wr_cnt - 1].next = wr; + * XMFS_I(inode)->wr_cnt++; + * kunmap_local(addr); + * } + */ + + void *old_ret = xa_store(&(XMFS_I(inode)->shared_pages), folio->index, + (void *)offset, GFP_KERNEL); + if (xa_err(old_ret)) { + xmfs_error(sbi->sb, "cannot store shared pages for page %ld", + folio->index); + } + if (sbi->id == 0) { + xmfs_mark_page_dead(sbi, (unsigned long)old_ret); + xmfs_mark_page_live(sbi, offset); + } + + folio_unlock(folio); + folio_put(folio); + + return copied; +} + +static ssize_t xmfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) +{ + ssize_t ret; + struct file *file = iocb->ki_filp; + struct inode *inode = file_inode(file); + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + + if (sbi->id != 0) { +#ifdef CONFIG_XMFS_FS_URMA + if (iter_is_ubuf(from)) + return xmfs_file_write_ubuf_slave(iocb, from); +#endif + return xmfs_file_write_iter_slave(iocb, from); + } + inode_lock(inode); + + if (xmfs_quiesce_wait(sbi)) + return -EINTR; + if (XMFS_I(inode)->die) { + xmfs_info(sbi->sb, + "inode logs have been interrupted before write iter"); + inode_unlock(inode); + return -EINVAL; + } + + struct xmfs_log xm_log = {}; + + unsigned long start_index = iocb->ki_pos / PAGE_SIZE; + unsigned long end_index = + (iov_iter_count(from) + iocb->ki_pos + PAGE_SIZE - 1) / + PAGE_SIZE; + unsigned long start_offset = iocb->ki_pos; + unsigned long xmlt_addr; + struct xmfs_rw_msg req = {}, grant = {}; + + ret = generic_write_checks(iocb, from); + if (ret > 0) { + start_index = iocb->ki_pos / PAGE_SIZE; + end_index = + (iov_iter_count(from) + iocb->ki_pos + PAGE_SIZE - 1) / + PAGE_SIZE; + start_offset = iocb->ki_pos; + if (XMFS_I(inode)->fallocate && + end_index > XMFS_I(inode)->data_size / PAGE_SIZE) { + xmfs_error(sbi->sb, + "write iter do not support write more than fallocate size"); + inode_unlock(inode); + return -EOPNOTSUPP; + } + + ret = file_remove_privs(file); + if (ret) { + xmfs_error(sbi->sb, + "write iter remove privs failed %ld", ret); + inode_unlock(inode); + return ret; + } + + ret = file_update_time(file); + if (ret) { + xmfs_error(sbi->sb, "write iter update time failed %ld", + ret); + inode_unlock(inode); + return ret; + } + + if (XMFS_I(inode)->fallocate) + goto start_write; + + req.ops = XMFS_UPDATE; + req.i_ino = inode->i_ino; + req.data_pages = end_index - start_index; + req.log_entries = 1; + req.size = iocb->ki_pos + iov_iter_count(from); + req.type = MSG_RW_REQUEST; + req.slave_id = sbi->id; + spin_lock(&sbi->local_ring_lock); + req.request_id = ++sbi->rw_request_id; + spin_unlock(&sbi->local_ring_lock); + xmfs_host_handle_urma_request(sbi, sbi->id, &req, &grant); + XMFS_I(inode)->start_write = grant.data_addr; + XMFS_I(inode)->count = grant.data_page_count; + XMFS_I(inode)->second_write = grant.second_data_addr; + XMFS_I(inode)->second_write_count = 0; + +start_write: + /* + * xmfs_info(sbi->sb, "write start %ld %ld %d %ld", + * XMFS_I(inode)->start_write, + * XMFS_I(inode)->second_write, XMFS_I(inode)->count, + * XMFS_I(inode)->second_write_count); + */ + ret = generic_perform_write(iocb, from); + } else { + inode_unlock(inode); + return ret; + } + /* + * xmfs_info(sbi->sb, + * "start offset %ld index %ld end index %ld pos %ld %ld", + * start_offset, start_index, end_index, iocb->ki_pos, + * (iocb->ki_pos + PAGE_SIZE - 1) / PAGE_SIZE); + */ + XMFS_I(inode)->start_write = 0; + XMFS_I(inode)->second_write = 0; + XMFS_I(inode)->count = 0; + XMFS_I(inode)->second_write_count = 0; + if (ret <= 0) { + inode_unlock(inode); + return ret; + } + + for (int s = 0; s < grant.log_slot_count; s++) { + xm_log.ops = XMFS_UPDATE; + xm_log.i_ino = inode->i_ino; + xm_log.version = grant.version + s; + xm_log.writer_id = sbi->id; + if (s == 0) { + xm_log.index = start_index; + xm_log.end_index = start_index + grant.data_page_count; + xm_log.data_offset = grant.data_addr; + xm_log.size = req.size; + } else { + xm_log.index = start_index + grant.data_page_count; + xm_log.end_index = end_index; + xm_log.data_offset = grant.second_data_addr; + xm_log.size = req.size; + } + xmlt_addr = + xmfs_add_log_v3(&xm_log, sbi, 0, inode->i_ino, true); + } + + inode_unlock(inode); + return ret; +} + +const struct pipe_buf_operations xmfs_pipe_buf_ops = { + .release = generic_pipe_buf_release, + .try_steal = generic_pipe_buf_try_steal, + .get = generic_pipe_buf_get, +}; + +static size_t splice_folio_into_pipe(struct pipe_inode_info *pipe, + struct folio *folio, loff_t fpos, + size_t size) +{ + struct page *page; + size_t spliced = 0, offset = offset_in_folio(folio, fpos); + + page = folio_page(folio, offset / PAGE_SIZE); + size = min(size, folio_size(folio) - offset); + offset %= PAGE_SIZE; + + while (spliced < size && + !pipe_full(pipe->head, pipe->tail, pipe->max_usage)) { + struct pipe_buffer *buf = pipe_head_buf(pipe); + size_t part = min_t(size_t, PAGE_SIZE - offset, size - spliced); + + *buf = (struct pipe_buffer) { + .ops = &xmfs_pipe_buf_ops, + .page = page, + .offset = offset, + .len = part, + }; + folio_get(folio); + pipe->head++; + page++; + spliced += part; + offset = 0; + } + + return spliced; +} + +static bool zero_pipe_buf_get(struct pipe_inode_info *pipe, + struct pipe_buffer *buf) +{ + return true; +} + +static void zero_pipe_buf_release(struct pipe_inode_info *pipe, + struct pipe_buffer *buf) +{ +} + +static bool zero_pipe_buf_try_steal(struct pipe_inode_info *pipe, + struct pipe_buffer *buf) +{ + return false; +} + +static const struct pipe_buf_operations zero_pipe_buf_ops = { + .release = zero_pipe_buf_release, + .try_steal = zero_pipe_buf_try_steal, + .get = zero_pipe_buf_get, +}; + +static size_t splice_zeropage_into_pipe(struct pipe_inode_info *pipe, + loff_t fpos, size_t size) +{ + size_t offset = fpos & ~PAGE_MASK; + + size = min_t(size_t, size, PAGE_SIZE - offset); + + if (!pipe_full(pipe->head, pipe->tail, pipe->max_usage)) { + struct pipe_buffer *buf = pipe_head_buf(pipe); + + *buf = (struct pipe_buffer) { + .ops = &zero_pipe_buf_ops, + .page = ZERO_PAGE(0), + .offset = offset, + .len = size, + }; + pipe->head++; + } + + return size; +} + +static ssize_t xmfs_file_read_iter_slave(struct kiocb *iocb, + struct iov_iter *to) +{ + struct file *file = iocb->ki_filp; + struct inode *inode = file_inode(file); + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + struct xmfs_inode_info *xmfs_inode = XMFS_I(inode); + pgoff_t index; + pgoff_t end_index; + unsigned long offset; + unsigned long *offsets; + int error = 0; + int count = 0; + ssize_t retval = 0; + loff_t *ppos = &iocb->ki_pos; + loff_t i_size = iov_iter_count(to); + struct page **pages; + /* xmfs_info(sbi->sb, "read iter slave"); */ + + /* + * struct xmfs_sb_index *xmsi = + * kmalloc(sizeof(struct xmfs_sb_index), GFP_KERNEL); + * int err = get_xmsi_and_copy(sbi, xmsi); + * + * if (unlikely(err)) { + * xmfs_error(sbi->sb, + * "hardware memory error when reading superblock during slave lookup"); + * kfree(xmsi); + * return err; + * } + * + * if (xmsi->last_update != sbi->last_update) { + * if (!sbi->debug) + * xmfs_replay_new_entries(sbi, NULL, xmsi->last_update); + * else + * xmfs_replay_new_entries(sbi, NULL, xmsi->last_update); + * } + * + * kfree(xmsi); + */ + + index = *ppos >> PAGE_SHIFT; + offset = *ppos & ~PAGE_MASK; + end_index = (i_size + *ppos) / PAGE_SIZE; + offsets = kzalloc((end_index - index + 1) * sizeof(unsigned long), + GFP_KERNEL); + if (!offsets) + return -ENOMEM; + + pages = kzalloc((end_index - index + 1) * sizeof(struct page *), + GFP_KERNEL); + if (!pages) { + kfree(offsets); + return -ENOMEM; + } + for (;;) { + struct folio *folio = NULL; + unsigned long nr, ret; + + if (index > end_index) + break; + if (index == end_index) { + nr = (i_size + *ppos) & ~PAGE_MASK; + if (nr <= offset) + break; + } else { + nr = PAGE_SIZE; + } + nr -= offset; + + folio = filemap_get_folio(inode->i_mapping, index); + if (IS_ERR(folio)) + folio = NULL; + + if (folio) { + folio_put(folio); + } else { + /* + * Copy to user tends to be so well optimized, but + * clear_user() not so much, that it is noticeably + * faster to copy the zero page instead of clearing. + */ + /* xmfs_error(sbi->sb, "qyf bad things happened"); */ + unsigned long mapped_offset = + (unsigned long)xa_load(&xmfs_inode->shared_pages, + index); + if (mapped_offset) { + folio = __filemap_get_folio(inode->i_mapping, index, + FGP_LOCK | FGP_WRITE | + FGP_CREAT, + inode->i_mapping->gfp_mask); + if (IS_ERR(folio)) { + xmfs_info(sbi->sb, + "splice cannot get the folio"); + error = PTR_ERR(folio); + folio = NULL; + break; + } + offsets[count] = mapped_offset; + pages[count] = &folio->page; + count++; + } else { + xmfs_info(sbi->sb, "no offset for %ld", index); + } + } + ret = nr; + offset += ret; + index += offset >> PAGE_SHIFT; + offset &= ~PAGE_MASK; + } + + if (count > 0) { + int idx = 0; + + while (count > 0) { + int num = min(512, count); + + count -= num; + xmfs_pmem_read_multi_pages(sbi, offsets + idx, + pages + idx, num); + idx += num; + } + for (int i = 0; i < idx; i++) { + struct folio *folio = page_folio(pages[i]); + + folio_mark_uptodate(folio); + folio_unlock(folio); + folio_put(folio); + } + } + kfree(offsets); + kfree(pages); + index = *ppos >> PAGE_SHIFT; + offset = *ppos & ~PAGE_MASK; + i_size = i_size_read(inode); + end_index = i_size >> PAGE_SHIFT; + + for (;;) { + struct folio *folio = NULL; + struct page *page = NULL; + unsigned long nr, ret; + + if (index > end_index) + break; + if (index == end_index) { + nr = i_size & ~PAGE_MASK; + if (nr <= offset) + break; + } else { + nr = PAGE_SIZE; + } + nr -= offset; + + folio = filemap_get_folio(inode->i_mapping, index); + if (!IS_ERR(folio)) { + bool uptodate; + + folio_lock(folio); + uptodate = folio_test_uptodate(folio); + folio_unlock(folio); + if (!uptodate) { + folio_put(folio); + folio = NULL; + } + } else { + folio = NULL; + } + if (IS_ERR(folio)) + folio = NULL; + + if (folio) { + page = folio_page(folio, 0); + ret = copy_page_to_iter(page, offset, nr, to); + folio_put(folio); + } else { + ret = copy_page_to_iter(ZERO_PAGE(0), offset, nr, to); + } + + retval += ret; + offset += ret; + index += offset >> PAGE_SHIFT; + offset &= ~PAGE_MASK; + + if (!iov_iter_count(to)) + break; + if (ret < nr) { + error = -EFAULT; + break; + } + } + + *ppos = ((loff_t)index << PAGE_SHIFT) + offset; + file_accessed(file); + return retval ? retval : error; +} + +int xmfs_collect_read_extents(struct xmfs_inode_info *inode, pgoff_t start, + pgoff_t end, struct xmfs_read_extent *exts, + unsigned int max_exts) +{ + XA_STATE(xas, &inode->shared_pages, start); + + void *entry; + unsigned long offset; + pgoff_t index; + unsigned int nr_exts = 0; + bool in_extent = false; + + pgoff_t cur_index = 0; + unsigned long cur_offset = 0; + unsigned int cur_pages = 0; + + xas_lock(&xas); + + xas_for_each(&xas, entry, end - 1) { + offset = (unsigned long)entry; + + index = xas.xa_index; + + /* + * xa_load() 返回 NULL 时不会进入这里, + * xas_for_each() 已经跳过空洞。 + */ + + if (!in_extent) { + cur_index = index; + cur_offset = offset; + cur_pages = 1; + in_extent = true; + continue; + } + + /* + * 是否仍然连续。 + */ + if (index == cur_index + cur_pages && + offset == cur_offset + ((unsigned long)cur_pages + << PAGE_SHIFT)) { + cur_pages++; + continue; + } + + /* + * 保存上一段 extent。 + */ + if (nr_exts >= max_exts) { + xas_unlock(&xas); + return -ENOSPC; + } + exts[nr_exts].start_index = cur_index; + exts[nr_exts].start_offset = cur_offset; + exts[nr_exts].nr_pages = cur_pages; + nr_exts++; + /* + * 新 extent。 + */ + cur_index = index; + cur_offset = offset; + cur_pages = 1; + } + + xas_unlock(&xas); + + /* + * Flush 最后一个 extent。 + */ + if (in_extent) { + if (nr_exts >= max_exts) + return -ENOSPC; + exts[nr_exts].start_index = cur_index; + exts[nr_exts].start_offset = cur_offset; + exts[nr_exts].nr_pages = cur_pages; + nr_exts++; + } + + return nr_exts; +} + +static ssize_t xmfs_file_read_ubuf_slave(struct kiocb *iocb, + struct iov_iter *to) +{ + struct file *file = iocb->ki_filp; + struct inode *inode = file_inode(file); + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + struct xmfs_inode_info *xmfs_inode = XMFS_I(inode); + pgoff_t index; + pgoff_t end_index; + unsigned long offset; + int err = 0; + int count = 0; + ssize_t retval = 0; + loff_t *ppos = &iocb->ki_pos; + loff_t i_size = iov_iter_count(to); + loff_t end_pos = min(i_size + iocb->ki_pos, inode->i_size); + struct xmfs_read_extent *exts; + + index = *ppos >> PAGE_SHIFT; + offset = *ppos & ~PAGE_MASK; + end_index = (end_pos + PAGE_SIZE - 1) / PAGE_SIZE; + + count = end_index - index + 1; + + exts = vzalloc(count * sizeof(struct xmfs_read_extent)); + /* + * xmfs_info(sbi->sb, "qyf before count is %d end is %ld %ld %ld", + * count, inode->i_size, *ppos, i_size); + */ + count = xmfs_collect_read_extents(xmfs_inode, index, end_index, exts, + count); + /* xmfs_info(sbi->sb, "qyf count is %d %ld", count, i_size); */ + for (int i = 0; i < count; i++) + retval += exts[i].nr_pages * PAGE_SIZE; + + retval = min(retval, end_pos - *ppos); + if (count != 0 && retval != 0) { + exts[0].start_offset += offset; + err = xmfs_urma_read_ubuf(sbi, to->ubuf, exts, count, retval); + } + vfree(exts); + iocb->ki_pos += retval; + file_accessed(file); + /* xmfs_info(sbi->sb, "qyf read return %ld %ld", err, retval); */ + return err ? err : retval; +} + +static ssize_t xmfs_file_splice_read(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags) +{ + struct inode *inode = file_inode(in); + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + struct xmfs_inode_info *xmfs_inode = XMFS_I(inode); + struct address_space *mapping = inode->i_mapping; + struct folio *folio = NULL; + size_t total_spliced = 0, used, npages, n, part; + loff_t isize; + int error = 0; + + struct xmfs_sb_index *xmsi = + kmalloc(sizeof(struct xmfs_sb_index), GFP_KERNEL); + int err = get_xmsi_and_copy(sbi, xmsi); + + if (unlikely(err)) { + xmfs_error(sbi->sb, + "hardware memory error when reading superblock during splice read"); + kfree(xmsi); + return err; + } + + if (xmsi->last_update != sbi->last_update) { + if (!sbi->debug) + xmfs_replay_new_entries(sbi, NULL, xmsi->last_update); + else + xmfs_replay_new_entries(sbi, NULL, xmsi->last_update); + } + kfree(xmsi); + + used = pipe_occupancy(pipe->head, pipe->tail); + npages = max_t(ssize_t, pipe->max_usage - used, 0); + len = min_t(size_t, len, (npages * PAGE_SIZE)); + + do { + if (*ppos >= i_size_read(inode)) + break; + + folio = filemap_get_folio(inode->i_mapping, *ppos / PAGE_SIZE); + if (!IS_ERR(folio)) { + bool uptodate; + + folio_lock(folio); + uptodate = folio_test_uptodate(folio); + folio_unlock(folio); + if (!uptodate) { + folio_put(folio); + folio = NULL; + } + } else { + folio = NULL; + } + + /* + * i_size must be checked after we know the pages are Uptodate. + * + * Checking i_size after the check allows us to calculate + * the correct value for "nr", which means the zero-filled + * part of the page is not copied back to userspace (unless + * another truncate extends the file - this is desired though). + */ + isize = i_size_read(inode); + if (unlikely(*ppos >= isize)) + break; + part = min_t(loff_t, isize - *ppos, len); + + if (folio) { + /* + * If users can be writing to this page using arbitrary + * virtual addresses, take care about potential aliasing + * before reading the page on the kernel side. + */ + if (mapping_writably_mapped(mapping)) + flush_dcache_folio(folio); + folio_mark_accessed(folio); + /* + * Ok, we have the page, and it's up-to-date, so we can + * now splice it into the pipe. + */ + n = splice_folio_into_pipe(pipe, folio, *ppos, part); + folio_put(folio); + folio = NULL; + } else { + unsigned long mapped_offset = + (unsigned long)xa_load(&xmfs_inode->shared_pages, + *ppos / PAGE_SIZE); + if (!mapped_offset) { + n = splice_zeropage_into_pipe(pipe, *ppos, + part); + } else { + folio = __filemap_get_folio(inode->i_mapping, + *ppos / PAGE_SIZE, + FGP_LOCK | FGP_WRITE | + FGP_CREAT, + inode->i_mapping->gfp_mask); + if (IS_ERR(folio)) { + xmfs_info(sbi->sb, + "splice cannot get the folio"); + error = PTR_ERR(folio); + folio = NULL; + break; + } + void *kaddr = + kmap_local_page(folio_file_page(folio, + *ppos / PAGE_SIZE)); + error = get_data_and_copy(sbi, mapped_offset, + kaddr, PAGE_SIZE, + NULL, 0); + if (unlikely(error)) { + xmfs_error(sbi->sb, + "hardware memory error when reading data during splice read"); + folio_unlock(folio); + folio_put(folio); + folio = NULL; + break; + } + kunmap_local(kaddr); + folio_mark_uptodate(folio); + n = splice_folio_into_pipe(pipe, folio, *ppos, + part); + folio_unlock(folio); + folio_put(folio); + folio = NULL; + } + } + + if (!n) + break; + len -= n; + total_spliced += n; + *ppos += n; + in->f_ra.prev_pos = *ppos; + if (pipe_full(pipe->head, pipe->tail, pipe->max_usage)) + break; + + } while (len); + + if (folio) + folio_put(folio); + + file_accessed(in); + return total_spliced ? total_spliced : error; +} + +static ssize_t xmfs_file_read_iter(struct kiocb *iocb, struct iov_iter *to) +{ + struct file *file = iocb->ki_filp; + struct inode *inode = file_inode(file); + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + + if (sbi->id != 0) { + if (iter_is_ubuf(to)) + return xmfs_file_read_ubuf_slave(iocb, to); + return xmfs_file_read_iter_slave(iocb, to); + } + + struct xmfs_inode_info *xmfs_inode = XMFS_I(inode); + pgoff_t index; + unsigned long offset; + int error = 0; + ssize_t retval = 0; + loff_t *ppos = &iocb->ki_pos; + + struct xmfs_sb_index *xmsi = + kmalloc(sizeof(struct xmfs_sb_index), GFP_KERNEL); + int err = get_xmsi_and_copy(sbi, xmsi); + + if (unlikely(err)) { + xmfs_error(sbi->sb, + "hardware memory error when reading superblock during read iter"); + kfree(xmsi); + return err; + } + + /* + * if (xmsi->last_update != sbi->last_update) { + * if (!sbi->debug) + * xmfs_replay_new_entries(sbi, NULL, xmsi->last_update); + * else + * xmfs_replay_new_entries(sbi, NULL, xmsi->last_update); + * } + */ + + kfree(xmsi); + + index = *ppos >> PAGE_SHIFT; + offset = *ppos & ~PAGE_MASK; + + for (;;) { + struct folio *folio = NULL; + struct page *page = NULL; + pgoff_t end_index; + unsigned long nr, ret; + loff_t i_size = i_size_read(inode); + + end_index = i_size >> PAGE_SHIFT; + if (index > end_index) + break; + if (index == end_index) { + nr = i_size & ~PAGE_MASK; + if (nr <= offset) + break; + } else { + nr = PAGE_SIZE; + } + nr -= offset; + + folio = filemap_get_folio(inode->i_mapping, index); + if (!IS_ERR(folio)) { + bool uptodate; + + folio_lock(folio); + uptodate = folio_test_uptodate(folio); + folio_unlock(folio); + if (!uptodate) { + folio_put(folio); + folio = NULL; + } + } else { + folio = NULL; + } + if (IS_ERR(folio)) + folio = NULL; + + if (folio) { + page = folio_page(folio, 0); + ret = copy_page_to_iter(page, offset, nr, to); + folio_put(folio); + } else { + /* + * Copy to user tends to be so well optimized, but + * clear_user() not so much, that it is noticeably + * faster to copy the zero page instead of clearing. + */ + /* xmfs_error(sbi->sb, "qyf bad things happened"); */ + unsigned long mapped_offset = + (unsigned long)xa_load(&xmfs_inode->shared_pages, + index); + if (!mapped_offset) { + ret = copy_page_to_iter(ZERO_PAGE(0), offset, + nr, to); + } else { + void *data; + + data = get_data(sbi, mapped_offset + offset, + nr); + if (IS_ERR(data)) { + error = PTR_ERR(data); + break; + } + ret = _copy_to_iter(data, nr, to); + put_data(sbi, data); + } + } + + retval += ret; + offset += ret; + index += offset >> PAGE_SHIFT; + offset &= ~PAGE_MASK; + + if (!iov_iter_count(to)) + break; + if (ret < nr) { + error = -EFAULT; + break; + } + } + + *ppos = ((loff_t)index << PAGE_SHIFT) + offset; + file_accessed(file); + return retval ? retval : error; +} + +#ifdef CONFIG_XMFS_FS_URMA +static ssize_t xmfs_file_write_iter_slave(struct kiocb *iocb, + struct iov_iter *from) +{ + struct inode *inode = file_inode(iocb->ki_filp); + struct xmfs_inode_info *xmfs_inode = XMFS_I(inode); + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + unsigned long start_index = iocb->ki_pos / PAGE_SIZE; + unsigned long end_index; + unsigned long page_num; + ssize_t ret; + + ret = generic_write_checks(iocb, from); + if (ret <= 0) + return ret; + + end_index = (iov_iter_count(from) + iocb->ki_pos + PAGE_SIZE - 1) / + PAGE_SIZE; + page_num = end_index - start_index; + + /* + * req.ops = XMFS_UPDATE; + * req.i_ino = inode->i_ino; + * req.data_pages = page_num; + * req.log_entries = 1; + * req.size = iocb->ki_pos + iov_iter_count(from); + * req.type = MSG_RW_REQUEST; + * req.slave_id = sbi->id; + * spin_lock(&sbi->local_ring_lock); + * req.request_id = ++sbi->rw_request_id; + * spin_unlock(&sbi->local_ring_lock); + * + * struct xmfs_grant_wait gw; + * init_completion(&gw.done); + * xa_store(&sbi->urma_cfg.grant_waits, req.request_id, &gw, + * GFP_KERNEL); + * err = xmfs_send_request(sbi, &req, &gw, 0); + * if (err) + * return err; + * grant = gw.grant; + * log_base = grant.log_trunk_addr + + * offsetof(struct xmfs_log_trunk, log[0]); + */ + + inode_lock(inode); + /* + * xmfs_inode->start_write = grant.data_addr; + * xmfs_inode->count = grant.data_page_count; + * xmfs_inode->second_write = grant.second_data_addr; + * xmfs_inode->second_write_count = 0; + */ + + /* + * xmfs_inode->wr = vzalloc(page_num * sizeof(struct ubcore_jfs_wr)); + * xmfs_inode->slots = vzalloc(sizeof(int) * page_num); + * xmfs_inode->write_buffer = urma_pool_acquire_nslots( + * &sbi->urma_cfg.pool, page_num, xmfs_inode->slots); + * xmfs_inode->wr_cnt = 0; + */ + + ret = generic_perform_write(iocb, from); + + struct xmfs_write_work *ww; + + ww = kmalloc(sizeof(*ww), GFP_KERNEL); + if (!ww) { + inode_unlock(inode); + return ret; + } + + ww->inode = inode; + ww->index = start_index; + ww->end_index = end_index; + ww->size = inode->i_size; + ihold(inode); + INIT_WORK(&ww->work, xmfs_write_worker); + if (xmfs_inode->write_wq) + queue_work(xmfs_inode->write_wq, &ww->work); + else + queue_work(sbi->prefetch_wq, &ww->work); + + /* + * for (s = 0; s < grant.log_slot_count; s++) { + * struct xmfs_log xm_log = {}; + * + * xm_log.ops = XMFS_UPDATE; + * xm_log.i_ino = inode->i_ino; + * xm_log.version = grant.version + s; + * xm_log.writer_id = sbi->id; + * if (s == 0) { + * xm_log.index = start_index; + * xm_log.end_index = + * start_index + grant.data_page_count; + * xm_log.data_offset = grant.data_addr; + * xm_log.size = req.size; + * } else { + * xm_log.index = start_index + grant.data_page_count; + * xm_log.end_index = end_index; + * xm_log.data_offset = grant.second_data_addr; + * xm_log.size = req.size; + * } + * xmfs_data_write( + * sbi, log_base + (grant.log_slot_start + s) * + * sizeof(struct xmfs_log), + * &xm_log, sizeof(xm_log)); + * + * pages = kzalloc((xm_log.end_index - xm_log.index) * + * sizeof(struct page *), GFP_KERNEL); + * for (int i = xm_log.index; i < xm_log.end_index; i++) { + * struct folio *folio = + * filemap_get_folio(inode->i_mapping, i); + * + * if (IS_ERR(folio)) { + * xm_log.end_index = i; + * break; + * } + * pages[i - xm_log.index] = &folio->page; + * } + * xmfs_pmem_write_multi_pages( + * sbi, xm_log.data_offset, pages, + * xm_log.end_index - xm_log.index); + * for (int i = xm_log.index; i < xm_log.end_index; i++) { + * struct folio *folio = + * page_folio(pages[i - xm_log.index]); + * + * folio_unlock(folio); + * folio_put(folio); + * } + * kfree(pages); + * } + */ + /* + * xmfs_urma_send_write_pages(sbi, xmfs_inode->wr); + * + * urma_pool_release_nslots(&sbi->urma_cfg.pool, page_num, + * xmfs_inode->slots); + * vfree(xmfs_inode->slots); + * xmfs_inode->start_write = 0; + * xmfs_inode->second_write = 0; + * xmfs_inode->count = 0; + * xmfs_inode->second_write_count = 0; + */ + inode_unlock(inode); + + return ret; +} + +static ssize_t xmfs_file_write_ubuf_slave(struct kiocb *iocb, + struct iov_iter *from) +{ + /* xmfs_info(sbi->sb, "qyf start write ubuf"); */ + struct inode *inode = file_inode(iocb->ki_filp); + struct xmfs_inode_info *xmfs_inode = XMFS_I(inode); + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + struct xmfs_rw_msg req = {}, grant = {}; + unsigned long start_index = iocb->ki_pos / PAGE_SIZE; + unsigned long end_index; + unsigned long page_num; + unsigned long log_base; + int err, s; + ssize_t ret; + + ret = generic_write_checks(iocb, from); + if (ret <= 0) + return ret; + + end_index = (iov_iter_count(from) + iocb->ki_pos + PAGE_SIZE - 1) / + PAGE_SIZE; + page_num = end_index - start_index; + + req.ops = XMFS_UPDATE; + req.i_ino = inode->i_ino; + req.data_pages = page_num; + req.log_entries = 1; + req.size = iocb->ki_pos + iov_iter_count(from); + req.type = MSG_RW_REQUEST; + req.slave_id = sbi->id; + spin_lock(&sbi->local_ring_lock); + req.request_id = ++sbi->rw_request_id; + spin_unlock(&sbi->local_ring_lock); + + struct xmfs_grant_wait gw; + + init_completion(&gw.done); + xa_store(&sbi->urma_cfg.grant_waits, req.request_id, &gw, GFP_KERNEL); + err = xmfs_send_request(sbi, &req, &gw, 0); + if (err) + return err; + grant = gw.grant; + log_base = + grant.log_trunk_addr + offsetof(struct xmfs_log_trunk, log[0]); + + inode_lock(inode); + xmfs_inode->start_write = grant.data_addr; + xmfs_inode->count = grant.data_page_count; + xmfs_inode->second_write = grant.second_data_addr; + xmfs_inode->second_write_count = 0; + /* xmfs_info(sbi->sb, "qyf start write ubuf data"); */ + xmfs_urma_write_ubuf(sbi, from->ubuf, grant.data_page_count * PAGE_SIZE, + grant.data_addr); + if (grant.log_slot_count > 1 && grant.second_data_addr != 0 && + grant.second_data_pages != 0) { + xmfs_urma_write_ubuf(sbi, + from->ubuf + + grant.data_page_count * PAGE_SIZE, + iov_iter_count(from) - + grant.data_page_count * PAGE_SIZE, + grant.second_data_addr); + } + /* xmfs_info(sbi->sb, "qyf start end ubuf data"); */ + for (s = 0; s < grant.log_slot_count; s++) { + struct xmfs_log xm_log = {}; + + xm_log.ops = XMFS_UPDATE; + xm_log.i_ino = inode->i_ino; + xm_log.version = grant.version + s; + xm_log.writer_id = sbi->id; + if (s == 0) { + xm_log.index = start_index; + xm_log.end_index = start_index + grant.data_page_count; + xm_log.data_offset = grant.data_addr; + xm_log.size = req.size; + int chunk_header_cnt = 0; + + for (int i = 0; i < grant.data_page_count; i++) { + unsigned long offset = + grant.data_addr + i * PAGE_SIZE + + chunk_header_cnt * PAGE_SIZE; + if (offset % XMFS_DEFAULT_CHUNK_SIZE == 0) { + chunk_header_cnt++; + offset = grant.data_addr + + i * PAGE_SIZE + + chunk_header_cnt * PAGE_SIZE; + } + xa_store(&(XMFS_I(inode)->shared_pages), + i + start_index, (void *)offset, + GFP_KERNEL); + } + } else { + xm_log.index = start_index + grant.data_page_count; + xm_log.end_index = end_index; + xm_log.data_offset = grant.second_data_addr; + xm_log.size = req.size; + int chunk_header_cnt = 0; + + for (int i = 0; i < grant.second_data_pages; i++) { + unsigned long offset = + grant.second_data_addr + i * PAGE_SIZE + + chunk_header_cnt * PAGE_SIZE; + if (offset % XMFS_DEFAULT_CHUNK_SIZE == 0) { + chunk_header_cnt++; + offset = grant.second_data_addr + + i * PAGE_SIZE + + chunk_header_cnt * PAGE_SIZE; + } + xa_store(&(XMFS_I(inode)->shared_pages), + i + start_index + + grant.data_page_count, + (void *)offset, GFP_KERNEL); + } + } + xmfs_data_write(sbi, + log_base + (grant.log_slot_start + s) * + sizeof(struct xmfs_log), + &xm_log, sizeof(xm_log)); + } + /* xmfs_info(sbi->sb, "qyf start write ubuf log"); */ + iocb->ki_pos += iov_iter_count(from); + if (i_size_read(inode) < iocb->ki_pos) + i_size_write(inode, iocb->ki_pos); + inode_unlock(inode); + + /* + * struct xmfs_cache_work *cw; + * + * cw = kmalloc(sizeof(*cw), GFP_KERNEL); + * if (!cw) + * return ret; + * + * cw->inode = inode; + * cw->index = start_index; + * cw->end_index = end_index; + * INIT_WORK(&cw->work, xmfs_prefetch_worker); + * queue_work(sbi->prefetch_wq, &cw->work); + */ + /* xmfs_info(sbi->sb, "qyf end write ubuf"); */ + return ret; +} +#endif + +static loff_t xmfs_seek_block(struct file *file, loff_t offset, int whence) +{ + struct inode *inode = file->f_mapping->host; + struct xmfs_inode_info *xmfs_inode = XMFS_I(inode); + loff_t maxbytes = inode->i_sb->s_maxbytes; + pgoff_t pgofs, preofs; + loff_t data_ofs = offset; + loff_t isize; + + inode_lock_shared(inode); + + isize = i_size_read(inode); + + if (offset >= isize) { + inode_unlock_shared(inode); + return -ENXIO; + } + + pgofs = (pgoff_t)(offset >> PAGE_SHIFT); + preofs = pgofs; + unsigned long i; + void *ptr; + + if (whence == SEEK_DATA) { + bool found = false; + + xa_for_each_start(&xmfs_inode->shared_pages, i, ptr, pgofs) { + if (i == pgofs) { + /* + * 场景 A:offset 刚好落在一个包含数据的页内, + * 直接返回原 offset。 + */ + data_ofs = offset; + } else { + /* + * 场景 B:offset 落在空洞内,下一个数据页在 i, + * 返回新数据页的起始地址。 + */ + data_ofs = (loff_t)i << PAGE_SHIFT; + } + found = true; + /* 找到第一个数据块即可,退出循环。 */ + break; + } + if (!found) { + /* 之后没有数据了。 */ + inode_unlock_shared(inode); + return -ENXIO; + } + } else if (whence == SEEK_HOLE) { + unsigned long expected = pgofs; + bool in_hole = true; + + xa_for_each_start(&xmfs_inode->shared_pages, i, ptr, pgofs) { + if (i > expected) { + /* + * 发现断层:本来期望页号是 expected,但拿到的 i + * 却比它大,说明 expected 处存在一个洞, + * 跳出循环。 + */ + break; + } + /* + * 如果走到这里,说明 i == expected,该页有数据。 + * 既然第一页有数据,说明 offset 不在洞里。 + */ + in_hole = false; + /* 期望下一页。 */ + expected++; + } + if (in_hole) { + /* + * 场景 A:offset 所在的起始页没有数据(或者没有后续 + * 数据),所以 offset 本身就在空洞里, + * 直接返回原 offset。 + */ + data_ofs = offset; + } else { + /* + * 场景 B:遇到连续的数据页, + * 在期望页 expected 发生断层, + * 返回断层起始地址。 + */ + data_ofs = (loff_t)expected << PAGE_SHIFT; + } + /* + * 场景 C:隐式文件尾空洞。如果算出的洞偏移超过文件大小, + * 则取文件大小。 + */ + if (data_ofs > isize) + data_ofs = isize; + } else { + inode_unlock_shared(inode); + return -EINVAL; + } + inode_unlock_shared(inode); + return vfs_setpos(file, data_ofs, maxbytes); +} + +static loff_t xmfs_file_llseek(struct file *file, loff_t offset, int whence) +{ + loff_t size = i_size_read(file->f_mapping->host); + + switch (whence) { + case SEEK_END: + case SEEK_CUR: + case SEEK_SET: + return generic_file_llseek(file, offset, whence); + case SEEK_DATA: + case SEEK_HOLE: + if (offset < 0 || offset >= size) + return -ENXIO; + return xmfs_seek_block(file, offset, whence); + } + + return -EINVAL; +} + +static vm_fault_t xmfs_fault(struct vm_fault *vmf) +{ + struct inode *inode = file_inode(vmf->vma->vm_file); + struct xmfs_inode_info *xmfs_inode = XMFS_I(inode); + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + vm_fault_t ret = 0; + unsigned long offset = vmf->pgoff << PAGE_SHIFT; + pgoff_t index = vmf->pgoff; + unsigned long mapped_offset; + int err; + + if (offset >= i_size_read(inode)) + return VM_FAULT_SIGBUS; + + filemap_invalidate_lock_shared(inode->i_mapping); + + struct folio *folio = __filemap_get_folio(inode->i_mapping, index, + FGP_CREAT | FGP_FOR_MMAP, + vmf->gfp_mask); + if (IS_ERR(folio)) { + filemap_invalidate_unlock_shared(inode->i_mapping); + return vmf_error(PTR_ERR(folio)); + } + + folio_lock(folio); + + void *kaddr = kmap_local_page(folio_file_page(folio, index)); + + mapped_offset = + (unsigned long)xa_load(&xmfs_inode->shared_pages, index); + if (mapped_offset) { + err = get_data_and_copy(sbi, mapped_offset, kaddr, PAGE_SIZE, NULL, + 0); + if (unlikely(err)) { + kunmap_local(kaddr); + folio_unlock(folio); + folio_put(folio); + filemap_invalidate_unlock_shared(inode->i_mapping); + return VM_FAULT_SIGBUS; + } + } else { + memset(kaddr, 0, PAGE_SIZE); + } + kunmap_local(kaddr); + folio_mark_uptodate(folio); + + vmf->page = folio_file_page(folio, index); + filemap_invalidate_unlock_shared(inode->i_mapping); + ret |= VM_FAULT_LOCKED; + return ret; +} + +static vm_fault_t xmfs_slave_fault(struct vm_fault *vmf) +{ + struct inode *inode = file_inode(vmf->vma->vm_file); + struct xmfs_inode_info *xmfs_inode = XMFS_I(inode); + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + vm_fault_t ret = 0; + unsigned long offset = vmf->pgoff << PAGE_SHIFT; + pgoff_t index = vmf->pgoff; + struct page_freq *page_freq; + + if (offset >= i_size_read(inode)) { + xmfs_info(sbi->sb, "offset is larger than size"); + return VM_FAULT_SIGBUS; + } + if (!sbi->always_hot) { + page_freq = (struct page_freq *)xa_load(&xmfs_inode->page_freq, + index); + if (!page_freq) { + page_freq = + kzalloc(sizeof(struct page_freq), GFP_KERNEL); + if (!page_freq) { + xmfs_info(sbi->sb, "fault page freq no"); + return VM_FAULT_SIGBUS; + } + void *store_ret = xa_store(&xmfs_inode->page_freq, + index, (void *)page_freq, + GFP_KERNEL); + if (xa_err(store_ret)) { + xmfs_error(sbi->sb, + "cannot store shared pages for page %lu", + index); + } + } + page_freq->in_cache = true; + page_freq->never_in_cache = false; + } + filemap_invalidate_lock_shared(inode->i_mapping); + struct folio *folio = __filemap_get_folio(inode->i_mapping, index, + FGP_CREAT | FGP_FOR_MMAP, + vmf->gfp_mask); + if (IS_ERR(folio)) { + filemap_invalidate_unlock_shared(inode->i_mapping); + xmfs_info(sbi->sb, "fault oom %ld %d", PTR_ERR(folio), + vmf_error(PTR_ERR(folio))); + return vmf_error(PTR_ERR(folio)); + } + folio_lock(folio); + + void *kaddr = kmap_local_page(folio_file_page(folio, index)); + unsigned long mapped_offset = + (unsigned long)xa_load(&xmfs_inode->shared_pages, index); + int err = get_data_and_copy(sbi, mapped_offset, kaddr, PAGE_SIZE, NULL, + 0); + if (unlikely(err)) + xmfs_error(sbi->sb, + "hardware memory error when reading data during slave page fault"); + kunmap_local(kaddr); + folio_mark_uptodate(folio); + + vmf->page = folio_file_page(folio, index); + filemap_invalidate_unlock_shared(inode->i_mapping); + if (!sbi->always_hot) + page_freq->already_in_cache = true; + + ret |= VM_FAULT_LOCKED; + return ret; +} + +static vm_fault_t xmfs_map_pages(struct vm_fault *vmf, pgoff_t start_pgoff, + pgoff_t end_pgoff) +{ + vm_fault_t ret = filemap_map_pages(vmf, start_pgoff, end_pgoff); + return ret; +} + +static vm_fault_t xmfs_mkwrite(struct vm_fault *vmf) +{ + vm_fault_t ret = filemap_page_mkwrite(vmf); + return ret; +} + +static const struct vm_operations_struct xmfs_file_vm_ops = { + .fault = xmfs_fault, + .map_pages = xmfs_map_pages, + .page_mkwrite = xmfs_mkwrite, +}; + +static const struct vm_operations_struct xmfs_slave_file_vm_ops = { + .fault = xmfs_slave_fault, + .map_pages = xmfs_map_pages, + .page_mkwrite = xmfs_mkwrite, +}; + +static int xmfs_file_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct xmfs_sb_info *sbi = XMFS_SB(file_inode(file)->i_sb); + + file_accessed(file); + if (sbi->id) + vma->vm_ops = &xmfs_slave_file_vm_ops; + else + vma->vm_ops = &xmfs_file_vm_ops; + return 0; +} + +static int xmfs_slave_writepage(struct page *page, + struct writeback_control *wbc) +{ + struct address_space *mapping = page->mapping; + struct inode *inode = mapping->host; + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + struct xmfs_inode_info *xmfs_inode = XMFS_I(inode); + unsigned long mapped_offset; + + if (!(inode->i_sb->s_flags & SB_ACTIVE)) { + unlock_page(page); + return 0; + } + + mapped_offset = + (unsigned long)xa_load(&xmfs_inode->shared_pages, page->index); + if (mapped_offset) { + unlock_page(page); + return 0; + } + { + struct xmfs_rw_msg req = {}, grant = {}; + unsigned long log_off; + int err; + + req.ops = XMFS_UPDATE; + req.i_ino = inode->i_ino; + req.data_pages = 1; + req.log_entries = 1; + req.size = i_size_read(inode); + req.type = MSG_RW_REQUEST; + req.slave_id = sbi->id; + spin_lock(&sbi->local_ring_lock); + req.request_id = ++sbi->rw_request_id; + spin_unlock(&sbi->local_ring_lock); + struct xmfs_grant_wait gw; + + init_completion(&gw.done); + xa_store(&sbi->urma_cfg.grant_waits, req.request_id, &gw, + GFP_KERNEL); + err = xmfs_send_request(sbi, &req, &gw, 0); + if (err) { + if (!(inode->i_sb->s_flags & SB_ACTIVE)) { + unlock_page(page); + return 0; + } + redirty_page_for_writepage(wbc, page); + unlock_page(page); + return err; + } + grant = gw.grant; + if (grant.type == MSG_RW_ERROR) { + if (!(inode->i_sb->s_flags & SB_ACTIVE)) { + unlock_page(page); + return 0; + } + redirty_page_for_writepage(wbc, page); + unlock_page(page); + return -ENOSPC; + } + { + char *kaddr = kmap_local_page(page); + + xmfs_data_write(sbi, grant.data_addr, kaddr, PAGE_SIZE); + kunmap_local(kaddr); + } + log_off = grant.log_trunk_addr + + offsetof(struct xmfs_log_trunk, log[0]) + + grant.log_slot_start * sizeof(struct xmfs_log); + { + struct xmfs_log xm_log = {}; + + xm_log.index = page->index; + xm_log.end_index = page->index + 1; + xm_log.ops = XMFS_UPDATE; + xm_log.size = i_size_read(inode); + xm_log.data_offset = grant.data_addr; + xm_log.version = grant.version; + xm_log.writer_id = sbi->id; + xmfs_data_write(sbi, log_off, &xm_log, sizeof(xm_log)); + } + /* xmfs_mark_page_live(sbi, grant.data_addr); */ + { + void *old_ret = + xa_store(&xmfs_inode->shared_pages, page->index, + (void *)grant.data_addr, GFP_KERNEL); + /* xmfs_mark_page_dead(sbi, (unsigned long)old_ret); */ + if (xa_err(old_ret)) + xmfs_error(sbi->sb, + "slave writepage cannot store shared pages for page %ld", + page->index); + } + unlock_page(page); + return 0; + } +} + +static int xmfs_writepage(struct page *page, struct writeback_control *wbc) +{ + struct address_space *mapping = page->mapping; + struct inode *inode = mapping->host; + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + + if (sbi->id != 0) + return xmfs_slave_writepage(page, wbc); + + if (sbi->mode == XMFS_URMA_MODE) { + redirty_page_for_writepage(wbc, page); + unlock_page(page); + return 0; + } + + if (inode_is_locked(inode)) { + redirty_page_for_writepage(wbc, page); + unlock_page(page); + return 0; + } + if (!inode || !XMFS_I(inode)) { + xmfs_info(sbi->sb, "no inode now for write page"); + unlock_page(page); + return -EINVAL; + } + struct xmfs_log xm_log; + + if (sbi == NULL || sbi->kaddr == NULL) { + unlock_page(page); + return 0; + } + unsigned long xmlt_addr; + + if (!spin_trylock(&sbi->space_lock)) { + redirty_page_for_writepage(wbc, page); + unlock_page(page); + return -EAGAIN; + } + if (XMFS_I(inode)->die) { + xmfs_info(sbi->sb, + "inode logs have been interrupted before writepage"); + spin_unlock(&sbi->space_lock); + unlock_page(page); + return -EINVAL; + } + + if (XMFS_I(inode)->fallocate && + page->index > XMFS_I(inode)->data_size / PAGE_SIZE) { + xmfs_error(sbi->sb, + "writepage do not support write more than fallocate size"); + spin_unlock(&sbi->space_lock); + unlock_page(page); + return -EOPNOTSUPP; + } + + struct xmfs_sb_index *xmsi = get_xmsi(sbi); + + if (!xmsi) { + spin_unlock(&sbi->space_lock); + unlock_page(page); + return -EINVAL; + } + unsigned long data_current = xmsi->data_current; + unsigned long data_count = xmsi->data_count; + unsigned long data_offset; + + int logcount = 1; + int need_data_trunk = (data_count == 511 || data_current == 0) ? 1 : 0; + + if (logcount + xmsi->used_trunk_count + need_data_trunk > + sbi->max_chunk) { + sbi->full = true; + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + unlock_page(page); + return -ENOSPC; + } + if (data_current == 0 || data_count == 511) { + long slot = xmfs_alloc_and_mark_slot(xmsi, sbi, 'd'); + + if (slot < 0) { + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + unlock_page(page); + return -ENOSPC; + } + data_offset = + DATA_AREA_OFFSET + slot * sbi->trunk_size + PAGE_SIZE; + xmsi->data_current = data_offset; + xmsi->data_count = 0; + } else { + data_offset = data_current + PAGE_SIZE; + xmsi->data_current = data_offset; + xmsi->data_count = data_count + 1; + } + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + + loff_t pos = (xm_log.index + 1) * PAGE_SIZE; + + xm_log.index = page->index; + xm_log.end_index = xm_log.index + 1; + xm_log.ops = XMFS_UPDATE; + xm_log.p_ino = 0; + xm_log.size = min(i_size_read(inode), pos); + xm_log.offset = xm_log.index * PAGE_SIZE; + xm_log.writer_id = sbi->id; + if (XMFS_I(inode)->fallocate) { + xm_log.data_offset = + xmfs_get_data_offset_fallocate(inode, sbi, page); + } else { + char *kaddr = kmap_local_page(page); + + xmfs_data_write(sbi, data_offset, kaddr, PAGE_SIZE); + arch_invalidate_pmem(sbi->kaddr + data_offset, PAGE_SIZE); + kunmap_local(kaddr); + xm_log.data_offset = data_offset; + } + + void *old_ret = xa_store(&(XMFS_I(inode)->shared_pages), page->index, + (void *)xm_log.data_offset, GFP_KERNEL); + if (xa_err(old_ret)) { + xmfs_error(sbi->sb, "wb cannot store shared pages for page %ld", + page->index); + } + xmfs_mark_page_dead(sbi, (unsigned long)old_ret); + xmfs_mark_page_live(sbi, xm_log.data_offset); + + xmlt_addr = xmfs_add_log_v3(&xm_log, sbi, 0, inode->i_ino, true); + if (xmlt_addr < 0) { + xmfs_info(sbi->sb, "writepage wrong"); + XMFS_I(inode)->die = true; + unlock_page(page); + return -EINVAL; + } + unlock_page(page); + + return 0; +} + +static int xmfs_fsync(struct file *file, loff_t start, loff_t end, int datasync) +{ + return write_inode_now(file->f_mapping->host, 1); +} + +static int xmfs_release(struct inode *inode, struct file *file) +{ + if (XMFS_I(inode)->tmp) { + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + + struct xmfs_log xm_log = { 0 }; + unsigned long xmlt_addr; + + if (inode->__i_nlink > 1) + xm_log.index = -1; + else + xm_log.index = 0; + xm_log.end_index = -1; + xm_log.ops = XMFS_DELETE; + xm_log.data_offset = 0; + memcpy(xm_log.name, file->f_path.dentry->d_name.name, + min(strlen(file->f_path.dentry->d_name.name), + 255UL)); + xm_log.p_ino = parent_ino(file->f_path.dentry); + + struct xmfs_sb_index *xmsi; + + spin_lock(&sbi->space_lock); + xmsi = get_xmsi(sbi); + if (!xmsi) { + spin_unlock(&sbi->space_lock); + return -EINVAL; + } + int logcount = 1; + + if (logcount + xmsi->used_trunk_count > sbi->max_chunk) { + sbi->full = true; + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + return -ENOSPC; + } + if (XMFS_I(inode)->die) { + xmfs_info(sbi->sb, + "inode logs have been interrupted before close tmpfile"); + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + return -EINVAL; + } + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + xmlt_addr = xmfs_add_log_v3(&xm_log, sbi, + min(strlen(file->f_path.dentry->d_name.name), + 255UL), + inode->i_ino, true); + if (xmlt_addr < 0) { + XMFS_I(inode)->die = true; + return -EINVAL; + } + } + return 0; +} + +long xmfs_common_fallocate(struct inode *inode, int mode, loff_t offset, + loff_t len) +{ + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + + if (xmfs_quiesce_wait(sbi)) + return -EINTR; + /* per-FS log: data_size/data_start now in inode_info */ + while (XMFS_I(inode)->fallocate && XMFS_I(inode)->data_size != 0) { + struct xmfs_log_trunk *xmlt_local = + kmalloc(sizeof(struct xmfs_log_trunk), GFP_KERNEL); + int err = get_log_trunk_and_copy(sbi, XMFS_I(inode)->log_start, + xmlt_local, false); + if (unlikely(err)) { + xmfs_error(sbi->sb, + "hardware memory error when reading xm log trunk during fallocate."); + kfree(xmlt_local); + break; + } + XMFS_I(inode)->fallocate = true; + /* data_start from inode_info */ + /* data_size from inode_info */ + kfree(xmlt_local); + return 0; + } + struct xmfs_sb_index *xmsi = get_xmsi(sbi); + + if (!xmsi) + return 0; + long first_slot = -1, slot; + int ti; + unsigned long num_trunks = + (len + sbi->trunk_size - 1) / sbi->trunk_size; + if (xmsi->used_trunk_count + num_trunks > sbi->max_chunk) { + xmfs_info(sbi->sb, "fallocate more space %lu %lu than total", + (unsigned long)len, num_trunks); + put_xmsi(sbi, xmsi); + return -ENOSPC; + } + XMFS_I(inode)->fallocate = true; + for (ti = 0; ti < (int)num_trunks; ti++) { + slot = xmfs_alloc_and_mark_slot(xmsi, sbi, 'd'); + if (slot < 0) { + put_xmsi(sbi, xmsi); + return -ENOSPC; + } + if (first_slot < 0) + first_slot = slot; + } + XMFS_I(inode)->data_start = + DATA_AREA_OFFSET + first_slot * sbi->trunk_size; + XMFS_I(inode)->data_size = len; + XMFS_I(inode)->data_start = XMFS_I(inode)->data_start; + XMFS_I(inode)->data_size = XMFS_I(inode)->data_size; + xmsi->data_current = DATA_AREA_OFFSET + first_slot * sbi->trunk_size + + num_trunks * sbi->trunk_size; + xmsi->data_count = 0; + + put_xmsi(sbi, xmsi); + + return 0; +} + +static long xmfs_file_fallocate(struct file *file, int mode, loff_t offset, + loff_t len) +{ + if ((mode & FALLOC_FL_KEEP_SIZE) == 0 || offset != 0) + return -EOPNOTSUPP; + + struct inode *inode = file_inode(file); + + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + + if (xmfs_quiesce_wait(sbi)) + return -EINTR; + if (!sbi->debug) + return -EOPNOTSUPP; + + spin_lock(&sbi->space_lock); + long err = xmfs_common_fallocate(inode, mode, offset, len); + + spin_unlock(&sbi->space_lock); + return err; +} + +static const struct address_space_operations xmfs_aops = { + .writepage = xmfs_writepage, + .write_begin = xmfs_write_begin, + .write_end = xmfs_write_end, + .dirty_folio = filemap_dirty_folio, + /* .free_folio = xmfs_free_folio, */ +}; + +static const struct file_operations xmfs_file_operations = { + .release = xmfs_release, + .read_iter = xmfs_file_read_iter, + .write_iter = xmfs_file_write_iter, + .splice_read = xmfs_file_splice_read, + .splice_write = iter_file_splice_write, + .mmap = xmfs_file_mmap, + .fsync = xmfs_fsync, + .llseek = xmfs_file_llseek, + .fallocate = xmfs_file_fallocate, +}; + +static const struct inode_operations xmfs_file_inode_operations = { + .getattr = xmfs_getattr, + .setattr = xmfs_setattr, + .listxattr = xmfs_listxattr, + .update_time = xmfs_update_time, +}; + +void xmfs_init_file_mapping(struct inode *inode) +{ + inode->i_mapping->a_ops = &xmfs_aops; +} + +void xmfs_init_file_inode(struct inode *inode) +{ + inode->i_op = &xmfs_file_inode_operations; + xmfs_init_file_mapping(inode); + inode->i_fop = &xmfs_file_operations; +} diff --git a/fs/xmfs/gc.c b/fs/xmfs/gc.c new file mode 100644 index 000000000000..7df7d8a1c6fe --- /dev/null +++ b/fs/xmfs/gc.c @@ -0,0 +1,953 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include "xmfs_i.h" +#include <linux/dcache.h> +#include <linux/kthread.h> +#include <linux/delay.h> +#include <linux/ktime.h> +#include <linux/cacheflush.h> + +void xmfs_mark_page_dead(struct xmfs_sb_info *sbi, unsigned long data_offset) +{ + unsigned long trunk_offset; + + if (data_offset == 0 || data_offset < DATA_AREA_OFFSET) + return; + + trunk_offset = data_offset & ~(sbi->trunk_size - 1); + if (trunk_offset < DATA_AREA_OFFSET) + return; + + if (sbi->mode != XMFS_URMA_MODE) { + struct xmfs_data_trunk_header *header; + + header = (struct xmfs_data_trunk_header *)(sbi->kaddr + + trunk_offset); + if (header->magic != XMFS_DATA_TRUNK_MAGIC) + return; + if (header->live_page_count == 0) + return; + header->live_page_count--; + } else { + unsigned long magic_off = trunk_offset; + unsigned long magic_val; + unsigned long count_off = + trunk_offset + offsetof(struct xmfs_data_trunk_header, + live_page_count); + uint64_t old_count, new_count; + + xmfs_pmem_read(sbi, magic_off, &magic_val, sizeof(magic_val)); + if (magic_val != XMFS_DATA_TRUNK_MAGIC) + return; + + do { + xmfs_pmem_read(sbi, count_off, &old_count, + sizeof(old_count)); + new_count = old_count - 1; + } while (xmfs_pmem_cas(sbi, count_off, old_count, new_count) != + 0); + } +} +EXPORT_SYMBOL(xmfs_mark_page_dead); + +void xmfs_mark_page_live(struct xmfs_sb_info *sbi, unsigned long data_offset) +{ + unsigned long trunk_offset; + + if (data_offset == 0 || data_offset < DATA_AREA_OFFSET) + return; + + trunk_offset = data_offset & ~(sbi->trunk_size - 1); + if (trunk_offset < DATA_AREA_OFFSET) + return; + + if (sbi->mode != XMFS_URMA_MODE) { + struct xmfs_data_trunk_header *header; + + header = (void *)(sbi->kaddr + trunk_offset); + if (header->magic != XMFS_DATA_TRUNK_MAGIC) + return; + header->live_page_count++; + /* cmpxchg(&header->last_modify_version, (unsigned long)-1, 0); */ + } else { + unsigned long magic_off = trunk_offset; + unsigned long magic_val; + /* + * unsigned long lmv_off = trunk_offset + + * offsetof(struct xmfs_data_trunk_header, + * last_modify_version); + */ + unsigned long count_off = + trunk_offset + offsetof(struct xmfs_data_trunk_header, + live_page_count); + uint64_t old_count, new_count; + + xmfs_pmem_read(sbi, magic_off, &magic_val, sizeof(magic_val)); + if (magic_val != XMFS_DATA_TRUNK_MAGIC) + return; + + do { + xmfs_pmem_read(sbi, count_off, &old_count, + sizeof(old_count)); + new_count = old_count + 1; + } while (xmfs_pmem_cas(sbi, count_off, old_count, new_count) != + 0); + + /* xmfs_pmem_cas(sbi, lmv_off, (uint64_t)-1, 0); */ + } +} +EXPORT_SYMBOL(xmfs_mark_page_live); + +int xmfs_quiesce_wait(struct xmfs_sb_info *sbi) +{ + int ret; + + ret = wait_event_interruptible(sbi->gc_ctl.wait, + !READ_ONCE(sbi->gc_ctl.gc_running)); + + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(xmfs_quiesce_wait); + +long xmfs_find_free_slot(struct xmfs_sb_index *xmsi, struct xmfs_sb_info *sbi) +{ + unsigned long i; + + if (xmsi->count < sbi->max_chunk) + return xmsi->count; + + for (i = 0; i < xmsi->count && i < XMFS_BITMAP_CAPACITY; i++) { + if (xmsi->bitmap[i] == 'u') + return i; + } + + return -1; +} + +long xmfs_alloc_and_mark_slot(struct xmfs_sb_index *xmsi, + struct xmfs_sb_info *sbi, char type) +{ + long slot = xmfs_find_free_slot(xmsi, sbi); + + if (slot < 0) + return -1; + if (slot < (long)xmsi->count) + memset(sbi->kaddr + DATA_AREA_OFFSET + slot * sbi->trunk_size, + 0, sbi->trunk_size); + xmsi->bitmap[slot] = type; + if (type == 'd') { + struct xmfs_data_trunk_header *hdr; + + hdr = (void *)(sbi->kaddr + DATA_AREA_OFFSET + + slot * sbi->trunk_size); + hdr->magic = XMFS_DATA_TRUNK_MAGIC; + hdr->last_modify_version = 0; + hdr->live_page_count = 0; + } + if (slot == (long)xmsi->count) + xmsi->count = slot + 1; + xmsi->used_trunk_count++; + return slot; +} + +static void write_log_entry_fields(struct xmfs_log_trunk *xmlt, + struct xmfs_log *xm_log, unsigned long ino, + unsigned long version, int slot, + size_t namelen) +{ + xmlt->log[slot].index = xm_log->index; + xmlt->log[slot].end_index = xm_log->end_index; + xmlt->log[slot].ops = xm_log->ops; + xmlt->log[slot].data_offset = xm_log->data_offset; + xmlt->log[slot].mode = xm_log->mode; + xmlt->log[slot].size = xm_log->size; + xmlt->log[slot].i_ino = ino; + xmlt->log[slot].offset = xm_log->offset; + xmlt->log[slot].version = version; + xmlt->log[slot].writer_id = xm_log->writer_id; + + if (xm_log->ops != XMFS_UPDATE) { + memcpy(xmlt->log[slot].name, xm_log->name, namelen); + xmlt->log[slot].name[namelen] = '\0'; + xmlt->log[slot].p_ino = xm_log->p_ino; + } +} + +unsigned long xmfs_add_log_v3(struct xmfs_log *xm_log, struct xmfs_sb_info *sbi, + size_t namelen, unsigned long ino, + bool update_version) +{ + unsigned long version; + struct xmfs_sb_index *xmsi; + struct xmfs_log_trunk *xmlt; + unsigned long addr; + long slot; + + down_read(&sbi->chain_rwsem); + + spin_lock(&sbi->version_lock); + sbi->last_update++; + version = sbi->last_update; + spin_unlock(&sbi->version_lock); + + spin_lock(&sbi->space_lock); + + xmsi = get_xmsi(sbi); + if (!xmsi) { + spin_unlock(&sbi->space_lock); + up_read(&sbi->chain_rwsem); + return (unsigned long)-1; + } + + xm_log->writer_id = 0; + + if (xmsi->log_current == 0 || xmsi->count == 0) { + slot = xmfs_alloc_and_mark_slot(xmsi, sbi, 'l'); + if (slot < 0) { + put_xmsi(sbi, xmsi); + spin_unlock(&sbi->space_lock); + up_read(&sbi->chain_rwsem); + return (unsigned long)-1; + } + addr = DATA_AREA_OFFSET + slot * sbi->trunk_size; + xmlt = get_log_trunk(sbi, addr); + xmlt->used = 0; + xmlt->next_trunk = 0; + xmlt->version_base = version; + xmlt->version_max = 0; + + xmsi->log_start = addr; + xmsi->log_current = addr; + } else { + xmlt = get_log_trunk(sbi, xmsi->log_current); + } + + if (xmlt->used >= MAX_LOG_PER_TRUNK) { + xmlt->version_max = version - 1; + + slot = xmfs_alloc_and_mark_slot(xmsi, sbi, 'l'); + if (slot < 0) { + put_xmsi(sbi, xmsi); + spin_unlock(&sbi->space_lock); + up_read(&sbi->chain_rwsem); + return (unsigned long)-1; + } + addr = DATA_AREA_OFFSET + slot * sbi->trunk_size; + xmlt->next_trunk = addr; + put_log_trunk(sbi, xmlt); + xmlt = get_log_trunk(sbi, addr); + xmlt->used = 0; + xmlt->next_trunk = 0; + xmlt->version_base = version; + xmlt->version_max = 0; + xmsi->log_current = addr; + } + + write_log_entry_fields(xmlt, xm_log, ino, version, xmlt->used, namelen); + xmlt->used++; + + addr = xmsi->log_current; + + if (xm_log->ops == XMFS_UPDATE) { + unsigned long trunk_off = xm_log->data_offset & + ~(sbi->trunk_size - 1); + struct xmfs_data_trunk_header *hdr = + (void *)(sbi->kaddr + trunk_off); + if (hdr->magic == XMFS_DATA_TRUNK_MAGIC && + version > hdr->last_modify_version) + hdr->last_modify_version = version; + } else if (xm_log->ops == XMFS_XATTR && + xm_log->data_offset >= DATA_AREA_OFFSET) { + unsigned long trunk_off = xm_log->data_offset & + ~(sbi->trunk_size - 1); + struct xmfs_data_trunk_header *hdr = + (void *)(sbi->kaddr + trunk_off); + if (hdr->magic == XMFS_DATA_TRUNK_MAGIC && + version > hdr->last_modify_version) + hdr->last_modify_version = version; + } else if (xm_log->ops == XMFS_SYMLINK && + xm_log->end_index >= DATA_AREA_OFFSET) { + unsigned long trunk_off = xm_log->end_index & + ~(sbi->trunk_size - 1); + struct xmfs_data_trunk_header *hdr = + (void *)(sbi->kaddr + trunk_off); + if (hdr->magic == XMFS_DATA_TRUNK_MAGIC && + version > hdr->last_modify_version) + hdr->last_modify_version = version; + } + + if (update_version) { + spin_lock(&sbi->version_lock); + if (version > xmsi->last_update) + xmsi->last_update = version; + spin_unlock(&sbi->version_lock); + } + + put_log_trunk(sbi, xmlt); + put_xmsi(sbi, xmsi); + spin_unlock(&sbi->space_lock); + up_read(&sbi->chain_rwsem); + + return addr + offsetof(struct xmfs_log_trunk, log[0]) + + (xmlt->used - 1) * sizeof(struct xmfs_log); +} + +static void gc_tail_compact(struct xmfs_sb_index *xmsi) +{ + while (xmsi->count > 0 && xmsi->count <= XMFS_BITMAP_CAPACITY && + xmsi->bitmap[xmsi->count - 1] == 'u') + xmsi->count--; +} + +static unsigned long gc_reclaim_log_trunks(struct xmfs_sb_info *sbi, + unsigned long safe_version) +{ + struct xmfs_sb_index *xmsi; + struct xmfs_log_trunk *xmlt; + unsigned long trunk_offset; + unsigned long next_trunk_offset; + unsigned long max_ver_reclaimed = 0; + unsigned long reclaimed = 0; + unsigned long first_surviving_trunk = 0; + + xmsi = get_xmsi(sbi); + if (!xmsi) + return 0; + if (safe_version == 0) { + put_xmsi(sbi, xmsi); + return 0; + } + + trunk_offset = xmsi->log_start; + + while (trunk_offset != 0) { + xmlt = get_log_trunk(sbi, trunk_offset); + if (!xmlt) + break; + + if (xmlt->version_max == 0) { + if (first_surviving_trunk == 0) + first_surviving_trunk = trunk_offset; + put_log_trunk(sbi, xmlt); + break; + } + + if (xmlt->version_max > safe_version) { + if (first_surviving_trunk == 0) + first_surviving_trunk = trunk_offset; + put_log_trunk(sbi, xmlt); + break; + } + + if (xmlt->version_max > max_ver_reclaimed) + max_ver_reclaimed = xmlt->version_max; + + next_trunk_offset = xmlt->next_trunk; + put_log_trunk(sbi, xmlt); + + spin_lock(&sbi->space_lock); + { + unsigned long idx = (trunk_offset - DATA_AREA_OFFSET) / + sbi->trunk_size; + memset(sbi->kaddr + trunk_offset, 0, sbi->trunk_size); + if (idx < XMFS_BITMAP_CAPACITY) + xmsi->bitmap[idx] = 'u'; + if (xmsi->used_trunk_count > 0) + xmsi->used_trunk_count--; + sbi->full = false; + } + spin_unlock(&sbi->space_lock); + reclaimed++; + + trunk_offset = next_trunk_offset; + } + + if (first_surviving_trunk != 0) + xmsi->log_start = first_surviving_trunk; + xmfs_info(sbi->sb, "gc log ends, gc to %ld, release %ld", + first_surviving_trunk, reclaimed); + spin_lock(&sbi->space_lock); + while (xmsi->count > 0 && xmsi->count <= XMFS_BITMAP_CAPACITY && + xmsi->bitmap[xmsi->count - 1] == 'u') + xmsi->count--; + spin_unlock(&sbi->space_lock); + + put_xmsi(sbi, xmsi); + return max_ver_reclaimed; +} + +static void gc_scan_data_trunks(struct xmfs_sb_info *sbi, + unsigned long safe_version) +{ + struct xmfs_sb_index *xmsi; + struct xmfs_data_trunk_header *header; + unsigned long trunk_offset; + unsigned long trunk_index; + unsigned long batch_count = 0; + + xmsi = get_xmsi(sbi); + if (!xmsi) + return; + + for (trunk_index = sbi->gc_ctl.gc_cursor; + trunk_index < + sbi->max_chunk /*&& batch_count < sbi->gc_ctl.gc_batch*/; + trunk_index++) { + if (trunk_index < XMFS_BITMAP_CAPACITY && + xmsi->bitmap[trunk_index] != 'd') + continue; + + trunk_offset = DATA_AREA_OFFSET + trunk_index * sbi->trunk_size; + header = (struct xmfs_data_trunk_header *)(sbi->kaddr + + trunk_offset); + if (header->magic != XMFS_DATA_TRUNK_MAGIC) + continue; + + if (header->live_page_count > 0) + continue; + + if (header->last_modify_version > safe_version) + continue; + + xmfs_info(sbi->sb, + "gc: reclaiming data trunk[%lu] live_pages=%lu ver=%lu", + trunk_index, header->live_page_count, + header->last_modify_version); + spin_lock(&sbi->space_lock); + memset(sbi->kaddr + trunk_offset, 0, sbi->trunk_size); + if (trunk_index < XMFS_BITMAP_CAPACITY) + xmsi->bitmap[trunk_index] = 'u'; + if (xmsi->used_trunk_count > 0) + xmsi->used_trunk_count--; + sbi->full = false; + batch_count++; + spin_unlock(&sbi->space_lock); + } + + spin_lock(&sbi->space_lock); + sbi->gc_ctl.gc_cursor = trunk_index; + if (sbi->gc_ctl.gc_cursor >= sbi->max_chunk) + sbi->gc_ctl.gc_cursor = 0; + gc_tail_compact(xmsi); + spin_unlock(&sbi->space_lock); + + put_xmsi(sbi, xmsi); +} + +static unsigned long gc_compute_safe_version(struct xmfs_sb_info *sbi) +{ + struct mount_msg_info *mm_info; + unsigned long safe_version; + int i; + + safe_version = sbi->last_update; + + if (sbi->mode == XMFS_SHARED_MEM_MODE || + sbi->mode == XMFS_HYBRID_MODE) { + mm_info = sbi->kaddr + MOUNT_MSG_OFFSET; + } else { + mm_info = get_mnt_msg(sbi); + if (!mm_info) + return safe_version; + } + + for (i = 1; i <= sbi->max_slave_id && i < 64; i++) { + if (mm_info->msg[i] == 'D') { + unsigned long sv = mm_info->slave_versions[i]; + + if (sv < safe_version) + safe_version = sv; + } + } + + if (sbi->mode == XMFS_URMA_MODE) + put_mnt_msg(sbi, mm_info); + + xmfs_info(sbi->sb, + "gc: safe_version=%lu (last_update=%lu, max_slave=%d)", + safe_version, sbi->last_update, sbi->max_slave_id); + return safe_version; +} + +static void xmfs_gc_run(struct xmfs_sb_info *sbi) +{ + unsigned long safe_version; + struct mount_msg_info *mm_info; +#ifdef CONFIG_XMFS_FS_URMA + struct xmfs_rw_msg req; +#endif + bool all_caught_up; + int i; + + if (sbi->id != 0) + return; + + xmfs_replay_new_entries(sbi, NULL, -1); + + spin_lock(&sbi->gc_ctl.gc_lock); + if (sbi->gc_ctl.gc_running) { + spin_unlock(&sbi->gc_ctl.gc_lock); + return; + } + WRITE_ONCE(sbi->gc_ctl.gc_running, true); + spin_unlock(&sbi->gc_ctl.gc_lock); + + safe_version = gc_compute_safe_version(sbi); + + mm_info = get_mnt_msg(sbi); + + if (mm_info) { +#ifdef CONFIG_XMFS_FS_URMA + for (i = 1; i < 16; i++) { + if (sbi->urma_cfg.send_tjettys[i] == NULL) + continue; + req.ops = XMFS_GC; + req.status = 2; + xmfs_send_request(sbi, &req, NULL, i); + } +#endif + all_caught_up = true; + for (i = 1; i <= sbi->max_slave_id && i < 64; i++) { + if (mm_info->msg[i] == 'D' && + mm_info->slave_versions[i] < safe_version) { + all_caught_up = false; + break; + } + } + + if (!all_caught_up) { + xmfs_info(sbi->sb, + "gc: waiting for slaves to catch up to version %lu", + safe_version); + schedule_timeout_interruptible(10 * HZ); + mm_info = get_mnt_msg(sbi); + if (mm_info) { + all_caught_up = true; + for (i = 1; i <= sbi->max_slave_id && i < 64; + i++) { + if (mm_info->msg[i] == 'D' && + mm_info->slave_versions[i] < + safe_version) { + all_caught_up = false; + break; + } + } + put_mnt_msg(sbi, mm_info); + } + } + } + + down_write(&sbi->chain_rwsem); + if (all_caught_up) { +#ifdef CONFIG_XMFS_FS_URMA + for (i = 1; i < 16; i++) { + if (sbi->urma_cfg.send_tjettys[i] == NULL) + continue; + req.ops = XMFS_GC; + req.status = 1; + xmfs_send_request(sbi, &req, NULL, i); + } +#endif + gc_reclaim_log_trunks(sbi, safe_version); +#ifdef CONFIG_XMFS_FS_URMA + for (i = 1; i < 16; i++) { + if (sbi->urma_cfg.send_tjettys[i] == NULL) + continue; + req.ops = XMFS_GC; + req.status = 0; + xmfs_send_request(sbi, &req, NULL, i); + } +#endif + } + gc_scan_data_trunks(sbi, safe_version); + up_write(&sbi->chain_rwsem); + + spin_lock(&sbi->gc_ctl.gc_lock); + WRITE_ONCE(sbi->gc_ctl.gc_running, false); + wake_up_all(&sbi->gc_ctl.wait); + spin_unlock(&sbi->gc_ctl.gc_lock); +} + +int xmfs_gc_main(struct xmfs_sb_info *sbi) +{ + xmfs_gc_run(sbi); + return 0; +} + +int xmfs_gc_thread_fn(void *data) +{ + struct xmfs_sb_info *sbi = data; + struct xmfs_sb_index *xmsi; + unsigned long interval; + + interval = sbi->gc_interval ? sbi->gc_interval : + XMFS_GC_INTERVAL_DEFAULT; + + while (!kthread_should_stop()) { + xmsi = get_xmsi(sbi); + /* + * xmfs_info(sbi->sb, "gc %d %d %d", + * xmsi->used_trunk_count, + * sbi->max_chunk * 80 / 100, sbi->max_chunk); + */ + if (xmsi && + (xmsi->used_trunk_count >= sbi->max_chunk * 80 / 100 || + sbi->full)) + xmfs_gc_run(sbi); + if (xmsi) + put_xmsi(sbi, xmsi); + schedule_timeout_interruptible(interval * HZ); + } + + return 0; +} + +int xmfs_gc_thread_slave_fn(void *data) +{ + struct xmfs_sb_info *sbi = data; + unsigned long interval; + + interval = sbi->gc_interval ? sbi->gc_interval : + XMFS_GC_INTERVAL_DEFAULT; + + while (!kthread_should_stop()) { + if (sbi->gc_ctl.need_replay) { + xmfs_info(sbi->sb, "gc need to replay"); + xmfs_replay_new_entries(sbi, NULL, -1); + xmfs_info(sbi->sb, "gc need to replay done"); + sbi->gc_ctl.need_replay = false; + } + schedule_timeout_interruptible(interval * HZ); + } + + return 0; +} + +static int xmfs_host_reserve_data_space(struct xmfs_sb_info *sbi, + unsigned long page_count, + struct xmfs_data_reserve_result *result) +{ + struct xmfs_sb_index *xmsi = get_xmsi(sbi); + unsigned long data_current, data_count; + long slot; + struct xmfs_data_trunk_header *hdr; + unsigned long remaining; + int trunk_idx; + bool first_partial = false; + + if (!xmsi) + return -ENOSPC; + + memset(result, 0, sizeof(*result)); + trunk_idx = 0; + + data_current = xmsi->data_current; + data_count = xmsi->data_count; + remaining = 511 - data_count; + if (remaining + 511 * (sbi->max_chunk - xmsi->count) < page_count) { + xmfs_info(sbi->sb, + "try to allocate more than usable space %lu > %lu", + page_count, + remaining + 511 * (sbi->max_chunk - xmsi->count)); + return -ENOSPC; + } + + if (data_current != 0 && remaining > 0) { + unsigned long use = min(page_count, remaining); + + hdr = (struct xmfs_data_trunk_header *)(sbi->kaddr + + (data_current & + ~(sbi->trunk_size - + 1))); + result->data_addr = data_current; + result->data_page_count = use; + result->trunk_addrs[trunk_idx] = data_current & + ~(sbi->trunk_size - 1); + trunk_idx++; + + xmsi->data_current = data_current + use * PAGE_SIZE; + xmsi->data_count = data_count + use; + hdr->total_page_count = data_count + use; + /* hdr->live_page_count += use; */ + + page_count -= use; + if (page_count == 0) { + put_xmsi(sbi, xmsi); + result->trunk_count = trunk_idx; + return 0; + } + first_partial = true; + } + + while (page_count > 0) { + unsigned long use; + + slot = xmfs_alloc_and_mark_slot(xmsi, sbi, 'd'); + if (slot < 0) { + put_xmsi(sbi, xmsi); + return -ENOSPC; + } + data_current = DATA_AREA_OFFSET + slot * sbi->trunk_size; + hdr = (struct xmfs_data_trunk_header *)(sbi->kaddr + + data_current); + memset(hdr, 0, PAGE_SIZE); + hdr->magic = XMFS_DATA_TRUNK_MAGIC; + hdr->last_modify_version = 0; + + use = min_t(unsigned long, page_count, 511); + + if (!first_partial && trunk_idx == 0) { + result->data_addr = data_current + PAGE_SIZE; + result->data_page_count = use; + } else if (result->second_data_addr == 0) { + result->second_data_addr = data_current + PAGE_SIZE; + result->second_data_pages = use; + } else { + result->second_data_pages += use; + } + result->trunk_addrs[trunk_idx] = data_current; + trunk_idx++; + + hdr->total_page_count = use; + /* hdr->live_page_count = use; */ + + if (use < 511) { + xmsi->data_current = + data_current + PAGE_SIZE + use * PAGE_SIZE; + xmsi->data_count = use; + } else { + xmsi->data_current = 0; + xmsi->data_count = 511; + } + + page_count -= use; + } + + result->trunk_count = trunk_idx; + put_xmsi(sbi, xmsi); + return 0; +} + +static unsigned long xmfs_host_reserve_log_space(struct xmfs_sb_info *sbi, + unsigned long ino, + int entry_count) +{ + struct xmfs_sb_index *xmsi = get_xmsi(sbi); + struct xmfs_log_trunk *xmlt; + unsigned long addr; + long slot; + + if (!xmsi) + return 0; + + xmlt = get_log_trunk(sbi, xmsi->log_current); + if (!xmlt || xmlt->used + entry_count > MAX_LOG_PER_TRUNK) { + if (xmlt) { + xmlt->version_max = sbi->last_update; + put_log_trunk(sbi, xmlt); + } + slot = xmfs_alloc_and_mark_slot(xmsi, sbi, 'l'); + if (slot < 0 || slot >= sbi->max_chunk) { + xmfs_info(sbi->sb, + "cannot get a correct slot %ld, maybe space is full", + slot); + put_xmsi(sbi, xmsi); + return 0; + } + addr = DATA_AREA_OFFSET + slot * sbi->trunk_size; + xmlt = get_log_trunk(sbi, addr); + xmlt->used = 0; + xmlt->next_trunk = 0; + xmlt->version_base = sbi->last_update + 1; + xmlt->version_max = 0; + put_log_trunk(sbi, xmlt); + { + struct xmfs_log_trunk *old = + get_log_trunk(sbi, xmsi->log_current); + if (old) { + old->next_trunk = addr; + put_log_trunk(sbi, old); + } + } + xmsi->log_current = addr; + sbi->log_current = + addr + offsetof(struct xmfs_log_trunk, log[0]); + xmlt = get_log_trunk(sbi, addr); + } + + addr = xmsi->log_current; + put_log_trunk(sbi, xmlt); + put_xmsi(sbi, xmsi); + return addr; +} + +void xmfs_host_handle_urma_request(struct xmfs_sb_info *sbi, int slave_i, + struct xmfs_rw_msg *req, + struct xmfs_rw_msg *grant) +{ + struct xmfs_log_trunk *xmlt; + struct xmfs_data_reserve_result dr; + unsigned long log_addr; + int log_needed; + int data_err; + + if (xmfs_quiesce_wait(sbi)) + return; + + if (req->ops == XMFS_SETATTR) { + spin_lock(&sbi->space_lock); + struct xmfs_inode_table *tbl = + (void *)(sbi->kaddr + INODE_TABLE_OFFSET); + unsigned long ci; + struct iattr *attr = (struct iattr *)req->name; + time64_t ts_sec = ktime_get_real_seconds(); + + for (ci = 0; ci < tbl->count; ci++) { + if (tbl->entries[ci].i_ino == req->i_ino) { + if (attr->ia_valid & ATTR_SIZE) + tbl->entries[ci].size = attr->ia_size; + tbl->entries[ci].ctime.tv_sec = ts_sec; + tbl->entries[ci].mtime.tv_sec = ts_sec; + tbl->entries[ci].atime.tv_sec = ts_sec; + break; + } + } + spin_unlock(&sbi->space_lock); + return; + } + + grant->type = MSG_RW_GRANT; + grant->request_id = req->request_id; + grant->slave_id = req->slave_id; + grant->ops = req->ops; + spin_lock(&sbi->space_lock); + if (req->data_pages > 0) { + data_err = + xmfs_host_reserve_data_space(sbi, req->data_pages, &dr); + if (data_err) { + spin_unlock(&sbi->space_lock); + grant->type = MSG_RW_ERROR; + return; + } + grant->data_addr = dr.data_addr; + grant->data_page_count = dr.data_page_count; + grant->second_data_addr = dr.second_data_addr; + grant->second_data_pages = dr.second_data_pages; + } else { + data_err = 0; + grant->data_addr = 0; + grant->data_page_count = 0; + } + + log_needed = req->log_entries; + if (req->ops == XMFS_UPDATE && grant->second_data_addr != 0) + log_needed = max(log_needed, 2); + if (req->ops == XMFS_RENAME) + log_needed = max(log_needed, 2); + if (req->ops == XMFS_SYMLINK) + log_needed = max(log_needed, 2); + + if (log_needed > 0 && slave_i != 0) { + log_addr = xmfs_host_reserve_log_space(sbi, req->i_ino, + log_needed); + if (log_addr == 0) { + spin_unlock(&sbi->space_lock); + grant->type = MSG_RW_ERROR; + return; + } + + xmlt = get_log_trunk(sbi, log_addr); + if (xmlt) { + grant->log_trunk_addr = log_addr; + grant->log_slot_start = xmlt->used; + grant->log_slot_count = log_needed; + xmlt->used += log_needed; + put_log_trunk(sbi, xmlt); + } + } else { + grant->log_trunk_addr = 0; + grant->log_slot_start = 0; + grant->log_slot_count = log_needed; + } + + if (req->i_ino == 0) + grant->assigned_ino = get_next_ino(); + else + grant->assigned_ino = req->i_ino; + + { + struct xmfs_sb_index *xmsi = get_xmsi(sbi); + + if (xmsi) { + grant->version = xmsi->last_update + 1; + xmsi->last_update += max(log_needed, 1); + put_xmsi(sbi, xmsi); + } else { + grant->version = 1; + } + } + + if (req->ops == XMFS_CREATE || req->ops == XMFS_MKDIR || + req->ops == XMFS_SYMLINK || req->ops == XMFS_LINK) { + struct xmfs_inode_table *tbl = + (void *)(sbi->kaddr + INODE_TABLE_OFFSET); + struct timespec64 ts; + unsigned int nlink; + { + struct xmfs_inode_entry *e = &tbl->entries[tbl->count]; + + memset(e, 0, sizeof(*e)); + e->i_ino = grant->assigned_ino; + e->p_ino = req->p_ino; + e->mode = req->mode; + if (req->ops != XMFS_LINK) + e->nlink = 1; + memcpy(e->name, req->name, strnlen(req->name, 255)); + e->name[strnlen(req->name, 255)] = '\0'; + e->size = req->size; + ktime_get_real_ts64(&ts); + e->ctime = ts; + e->mtime = ts; + e->atime = ts; + } + tbl->count++; + if (req->ops == XMFS_LINK) { + nlink = xmfs_inode_table_sync_nlink(tbl, req->i_ino); + if (nlink == 1) + xmfs_warning(sbi->sb, + "LINK inode %lu not found in table", + req->i_ino); + } + } + + if (req->ops == XMFS_DELETE) { + struct xmfs_inode_table *tbl = + (void *)(sbi->kaddr + INODE_TABLE_OFFSET); + + xmfs_inode_table_remove(tbl, req->i_ino, req->p_ino, + req->name); + xmfs_inode_table_sync_nlink(tbl, req->i_ino); + } + + if (req->ops == XMFS_UPDATE) { + struct xmfs_inode_table *ctbl = + (void *)(sbi->kaddr + INODE_TABLE_OFFSET); + unsigned long ci; + /* xmfs_info(sbi->sb, "write update size %ld", req->size); */ + for (ci = 0; ci < ctbl->count; ci++) { + if (ctbl->entries[ci].i_ino == req->i_ino) { + /* + * xmfs_info(sbi->sb, + * "write find inode to update size %ld", + * req->size); + */ + ctbl->entries[ci].size = req->size; + break; + } + } + } + + spin_unlock(&sbi->space_lock); +} diff --git a/fs/xmfs/inode.c b/fs/xmfs/inode.c new file mode 100644 index 000000000000..9f066782545a --- /dev/null +++ b/fs/xmfs/inode.c @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * XMFS: Log Structured Filesystem for UB + * Copyright (C) 2024. Huawei Technologies Co., Ltd + * + * This program can be distributed under the terms of the GNU GPL. + * See the file COPYING. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "xmfs_i.h" + +#include <linux/kernel.h> +#include <linux/namei.h> +#include <linux/pagemap.h> +#include <linux/slab.h> + +int xmfs_inode_eq(struct inode *inode, void *_ino) +{ + u64 ino = *(u64 *)_ino; + + if (XMFS_I(inode)->i_ino == ino && XMFS_I(inode)->pinned && + !XMFS_I(inode)->die) + return 1; + else + return 0; +} + +int xmfs_inode_set(struct inode *inode, void *_ino) +{ + u64 ino = *(u64 *)_ino; + + XMFS_I(inode)->i_ino = ino; + inode->i_ino = ino; + XMFS_I(inode)->leaf = true; + XMFS_I(inode)->tmp = false; + XMFS_I(inode)->die = false; + XMFS_I(inode)->fallocate = false; + XMFS_I(inode)->pinned = false; + XMFS_I(inode)->start_write = 0; + XMFS_I(inode)->second_write = 0; + XMFS_I(inode)->count = 0; + xa_init(&XMFS_I(inode)->shared_pages); + xmfs_simple_xattrs_init(&XMFS_I(inode)->xattrs); + XMFS_I(inode)->write_wq = NULL; + + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + + if (!sbi->always_hot) + xa_init(&XMFS_I(inode)->page_freq); + + sbi->used_ino++; + return 0; +} + +unsigned int xmfs_inode_table_sync_nlink(struct xmfs_inode_table *tbl, + unsigned long ino) +{ + unsigned long i; + unsigned int nlink = 0; + + for (i = 0; i < tbl->count; i++) { + if (tbl->entries[i].i_ino == ino) + nlink++; + } + for (i = 0; i < tbl->count; i++) { + if (tbl->entries[i].i_ino == ino) + tbl->entries[i].nlink = nlink; + } + + return nlink; +} + +bool xmfs_inode_table_remove(struct xmfs_inode_table *tbl, unsigned long ino, + unsigned long p_ino, const char *name) +{ + unsigned long i; + + for (i = 0; i < tbl->count; i++) { + struct xmfs_inode_entry *entry = &tbl->entries[i]; + + if (entry->i_ino != ino || entry->p_ino != p_ino || + strcmp(entry->name, name)) + continue; + + tbl->count--; + if (i < tbl->count) + tbl->entries[i] = tbl->entries[tbl->count]; + memset(&tbl->entries[tbl->count], 0, + sizeof(struct xmfs_inode_entry)); + return true; + } + + return false; +} + +int xmfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, + struct iattr *attr) +{ + void *ret; + struct inode *inode = dentry->d_inode; + int err; + + if (inode->i_ino == 0) + return simple_setattr(idmap, dentry, attr); + + struct xmfs_inode_info *xmfs_inode = XMFS_I(inode); + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + + if (sbi->id != 0) + return xmfs_slave_setattr(idmap, dentry, attr); + + err = setattr_prepare(idmap, dentry, attr); + if (err) { + xmfs_info(inode->i_sb, "set attr wrong %d %d %d", err, + attr->ia_mode, attr->ia_valid); + return err; + } + setattr_copy(idmap, d_inode(dentry), attr); + mark_inode_dirty(inode); + unsigned int ia_valid = attr->ia_valid; + + if (ia_valid & ATTR_SIZE) { + loff_t new_size = attr->ia_size; + + if (new_size > sbi->len) + return -EFBIG; + + if (i_size_read(inode) > new_size) { + for (unsigned long i = + (new_size + PAGE_SIZE - 1) >> PAGE_SHIFT; + i <= (i_size_read(inode) >> PAGE_SHIFT); i++) { + ret = xa_erase(&xmfs_inode->shared_pages, i); + if (ret) + inode->i_blocks--; + + } + i_size_write(inode, new_size); + truncate_pagecache(inode, new_size); + + } else { + i_size_write(inode, new_size); + } + { + struct xmfs_inode_table *tbl = + (void *)(sbi->kaddr + INODE_TABLE_OFFSET); + unsigned long ci; + + for (ci = 0; ci < tbl->count; ci++) { + if (tbl->entries[ci].i_ino == inode->i_ino) { + tbl->entries[ci].size = + i_size_read(inode); + tbl->entries[ci].ctime = + inode_get_ctime(inode); + tbl->entries[ci].mtime = inode->i_mtime; + break; + } + } + } + } + + return err; +} + +int xmfs_getattr(struct mnt_idmap *idmap, const struct path *path, + struct kstat *stat, u32 request_mask, unsigned int query_flags) +{ + struct inode *inode = d_inode(path->dentry); + + generic_fillattr(idmap, request_mask, inode, stat); + if (request_mask & STATX_BTIME) { + stat->result_mask |= STATX_BTIME; + stat->btime = XMFS_I(inode)->btime; + } + return 0; +} + +int xmfs_slave_getattr(struct mnt_idmap *idmap, const struct path *path, + struct kstat *stat, u32 request_mask, + unsigned int query_flags) +{ + struct inode *inode = d_inode(path->dentry); + + generic_fillattr(idmap, request_mask, inode, stat); + if (request_mask & STATX_BTIME) { + stat->result_mask |= STATX_BTIME; + stat->btime = XMFS_I(inode)->btime; + } + return 0; +} + +int xmfs_update_time(struct inode *inode, int flags) +{ + struct iattr attr = {}; + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + struct xmfs_log xm_log; + + if (inode->i_ino == 0) + return 0; + + if (flags == S_ATIME) + return 0; + + if ((flags & (S_MTIME | S_CTIME | S_ATIME)) == 0) + return 0; + + generic_update_time(inode, flags); + return 0; + if (inode->i_ino == 0) + return 0; + + if (flags == S_ATIME) + return 0; + + int err; + + if ((flags & (S_MTIME | S_CTIME | S_ATIME)) == 0) + return err; + + if (flags & S_MTIME) { + attr.ia_mtime.tv_sec = inode->i_mtime.tv_sec; + attr.ia_mtime.tv_nsec = inode->i_mtime.tv_nsec; + attr.ia_valid |= ATTR_MTIME; + } + if (flags & S_CTIME) { + attr.ia_ctime.tv_sec = inode->__i_ctime.tv_sec; + attr.ia_ctime.tv_nsec = inode->__i_ctime.tv_nsec; + attr.ia_valid |= ATTR_CTIME; + } + if (flags & S_ATIME) { + attr.ia_atime.tv_sec = inode->i_atime.tv_sec; + attr.ia_atime.tv_nsec = inode->i_atime.tv_nsec; + attr.ia_valid |= ATTR_ATIME; + } + + struct xmfs_inode_info *xmfs_inode = XMFS_I(inode); + + if (xmfs_inode->die) { + xmfs_info(inode->i_sb, + "inode logs have been interrupted before update time"); + return -EINVAL; + } + + struct xmfs_sb_index *xmsi; + + spin_lock(&sbi->space_lock); + xmsi = get_xmsi(sbi); + if (!xmsi) { + spin_unlock(&sbi->space_lock); + return -EINVAL; + } + err = 0; + int logcount = 1; + + if (logcount + xmsi->used_trunk_count > sbi->max_chunk) { + sbi->full = true; + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + return -ENOSPC; + } + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + + xm_log.ops = XMFS_SETATTR; + memcpy(xm_log.name, &attr, sizeof(struct iattr)); + + unsigned long xmlt_addr = xmfs_add_log_v3(&xm_log, sbi, + sizeof(struct iattr), + inode->i_ino, true); + if (xmlt_addr < 0) { + xmfs_inode->die = true; + return -EINVAL; + } + + return 0; +} + +static const struct inode_operations xmfs_special_inode_operations = { + .getattr = xmfs_getattr, + .setattr = xmfs_setattr, + .listxattr = xmfs_listxattr, + .update_time = xmfs_update_time, +}; + +void xmfs_init_special_inode_operations(struct inode *inode) +{ + inode->i_op = &xmfs_special_inode_operations; +} + +int xmfs_slave_setattr(struct mnt_idmap *idmap, struct dentry *dentry, + struct iattr *attr) +{ + struct inode *inode = d_inode(dentry); + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + struct xmfs_rw_msg req = {}; + int err; + + err = setattr_prepare(idmap, dentry, attr); + if (err) + return err; + + req.ops = XMFS_SETATTR; + req.i_ino = inode->i_ino; + req.p_ino = 0; + req.data_pages = 0; + req.log_entries = 0; + memcpy(req.name, attr, sizeof(struct iattr)); + + req.type = MSG_RW_REQUEST; + req.slave_id = sbi->id; + spin_lock(&sbi->local_ring_lock); + req.request_id = ++sbi->rw_request_id; + spin_unlock(&sbi->local_ring_lock); + err = xmfs_send_request(sbi, &req, NULL, 0); + if (err) + return err; + + setattr_copy(idmap, inode, attr); + + unsigned int ia_valid = attr->ia_valid; + + if (ia_valid & ATTR_SIZE) { + xmfs_info(inode->i_sb, "slave setattr size %lld", + (long long)attr->ia_size); + loff_t new_size = attr->ia_size; + + if (new_size > sbi->len) + return -EFBIG; + + if (i_size_read(inode) > new_size) { + for (unsigned long i = + (new_size + PAGE_SIZE - 1) >> PAGE_SHIFT; + i <= (i_size_read(inode) >> PAGE_SHIFT); i++) { + xa_erase(&XMFS_I(inode)->shared_pages, i); + } + i_size_write(inode, new_size); + truncate_pagecache(inode, new_size); + } else { + i_size_write(inode, new_size); + } + } + return 0; +} diff --git a/fs/xmfs/namei.c b/fs/xmfs/namei.c new file mode 100644 index 000000000000..7c725bc9b654 --- /dev/null +++ b/fs/xmfs/namei.c @@ -0,0 +1,1794 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * XMFS: Log Structured Filesystem for UB + * Copyright (C) 2024. Huawei Technologies Co., Ltd + * + * This program can be distributed under the terms of the GNU GPL. + * See the file COPYING. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "xmfs_i.h" + +#include <linux/file.h> +#include <linux/kernel.h> +#include <linux/namei.h> +#include <linux/pagemap.h> +#include <linux/security.h> +#include <linux/slab.h> + +static const struct inode_operations xmfs_host_dir_inode_operations; +static const struct inode_operations xmfs_dir_inode_operations; + +static int xmfs_do_create_slave(struct xmfs_sb_info *sbi, struct inode *dir, + struct dentry *dentry, umode_t mode); +static int xmfs_do_mkdir_slave(struct xmfs_sb_info *sbi, struct inode *dir, + struct dentry *dentry, umode_t mode); +static int xmfs_do_rename_slave(struct xmfs_sb_info *sbi, struct inode *old_dir, + struct dentry *old_dentry, + struct inode *new_dir, + struct dentry *new_dentry, unsigned int flags); +static int xmfs_do_mknod_slave(struct xmfs_sb_info *sbi, struct inode *dir, + struct dentry *dentry, umode_t mode, dev_t dev); +static int xmfs_do_unlink_slave(struct xmfs_sb_info *sbi, struct inode *dir, + struct dentry *dentry); +static int xmfs_do_symlink_slave(struct xmfs_sb_info *sbi, struct inode *dir, + struct dentry *dentry, const char *symname); +static int xmfs_do_link_slave(struct xmfs_sb_info *sbi, + struct dentry *old_dentry, struct inode *dir, + struct dentry *dentry); + +static void xmfs_unpin_inode(struct xmfs_sb_info *sbi, struct inode *inode) +{ + if (!XMFS_I(inode)->pinned) + return; + + XMFS_I(inode)->pinned = false; + xa_erase(&sbi->pinned_inodes, inode->i_ino); + iput(inode); +} + +static unsigned long xmfs_symlink_write_symname(struct inode *inode, + const char *name, + struct xmfs_log *xm_log) +{ + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + struct xmfs_sb_index *xmsi; + unsigned long data_current, data_count; + long slot; + int err = 0; + + spin_lock(&sbi->space_lock); + xmsi = get_xmsi(sbi); + if (!xmsi) { + spin_unlock(&sbi->space_lock); + return -EINVAL; + } + + data_current = xmsi->data_current; + data_count = xmsi->data_count; + + WARN_ON(xmsi->count == 0); + + if (data_current == 0 || data_count == 511) { + slot = xmfs_alloc_and_mark_slot(xmsi, sbi, 'd'); + if (slot < 0) { + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + return -ENOSPC; + } + xmsi->data_current = + DATA_AREA_OFFSET + slot * sbi->trunk_size + PAGE_SIZE; + xmsi->data_count = 0; + } else { + xmsi->data_current = data_current + PAGE_SIZE; + } + + xmfs_data_write(sbi, xmsi->data_current, name, + min(strlen(name) + 1, PATH_MAX)); + xmfs_mark_page_live(sbi, xmsi->data_current); + + xmsi->data_count = xmsi->data_count + 1; + xm_log->end_index = xmsi->data_current; + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + return err; +} + +static int xmfs_symlink(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, const char *symname) +{ + struct xmfs_sb_info *sbi = XMFS_SB(dir->i_sb); + + if (xmfs_quiesce_wait(sbi)) + return -EINTR; + if (sbi->id != 0) + return xmfs_do_symlink_slave(sbi, dir, dentry, symname); + struct xmfs_inode_info *xmfs_inode; + unsigned int len = strlen(symname) + 1; + + if (len > PAGE_SIZE) + return -ENAMETOOLONG; + + const char *name = dentry->d_name.name; + struct inode *inode; + struct xmfs_log xm_log; + unsigned long xmlt_addr; + + spin_lock(&sbi->space_lock); + if (sbi->full) { + spin_unlock(&sbi->space_lock); + return -ENOSPC; + } + + struct xmfs_sb_index *xmsi = get_xmsi(sbi); + + if (!xmsi) { + spin_unlock(&sbi->space_lock); + return -EINVAL; + } + int err = 0; + + int logcount = 1; + int need_data_trunk = + (xmsi->data_count == 511 || xmsi->data_current == 0) ? 1 : 0; + if (len >= 256 && xmsi->used_trunk_count + logcount + need_data_trunk > + sbi->max_chunk) { + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + return -ENOSPC; + } else if (len < 256 && + xmsi->used_trunk_count + logcount > sbi->max_chunk) { + sbi->full = true; + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + return -ENOSPC; + } + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + + unsigned long ino = get_next_ino(); + + inode = iget5_locked(dir->i_sb, ino, xmfs_inode_eq, xmfs_inode_set, + &ino); + if (!inode) + return -ENOMEM; + + xmfs_inode = XMFS_I(inode); + inode->i_ino = ino; + + i_size_write(inode, 0); + inode_init_owner(idmap, inode, dir, S_IFLNK | 0777); + + xmfs_init_symlink_operations(inode); + err = security_inode_init_security(inode, dir, &dentry->d_name, + xmfs_initxattrs, NULL); + if (err) { + unlock_new_inode(inode); + iput(inode); + return err; + } + inode_nohighmem(inode); + xmfs_init_symlink_mapping(inode); + + xmfs_inode->btime = inode->i_atime = inode->i_mtime = + inode_set_ctime_current(inode); + dir->i_mtime = inode_set_ctime_current(dir); + unlock_new_inode(inode); + ihold(inode); + xa_store(&sbi->pinned_inodes, inode->i_ino, inode, GFP_KERNEL); + XMFS_I(inode)->pinned = true; + + xm_log.index = 0; + xm_log.end_index = 0; + xm_log.ops = XMFS_SYMLINK; + xm_log.data_offset = xmfs_inode->btime.tv_sec; + xm_log.mode = S_IFLNK | 0777; + memcpy(xm_log.name, name, min(strlen(name), 255)); + xm_log.p_ino = parent_ino(dentry); + + struct xmfs_log xm_log1; + + xm_log1.index = 1; + xm_log1.ops = XMFS_SYMLINK; + xm_log1.data_offset = xmfs_inode->btime.tv_nsec; + xm_log1.mode = S_IFLNK | 0777; + xm_log1.p_ino = parent_ino(dentry); + + xmlt_addr = xmfs_add_log_v3(&xm_log, sbi, min(strlen(name), 255), + inode->i_ino, false); + if (xmlt_addr < 0) { + xmfs_inode->die = true; + clear_nlink(inode); + iput(inode); + return -EINVAL; + } + + if (len <= 255) { + memcpy(xm_log1.name, symname, min(strlen(symname), 255)); + xm_log1.name[min(strlen(symname), 255)] = '\0'; + xm_log1.end_index = 0; + xmlt_addr = xmfs_add_log_v3(&xm_log1, sbi, + min(strlen(symname), 255), + inode->i_ino, true); + } else { + err = xmfs_symlink_write_symname(inode, symname, &xm_log1); + xm_log1.index = len; + if (err) { + xmfs_inode->die = true; + clear_nlink(inode); + iput(inode); + return err; + } + xmlt_addr = + xmfs_add_log_v3(&xm_log1, sbi, 0, inode->i_ino, true); + } + if (xmlt_addr < 0) { + xmfs_inode->die = true; + clear_nlink(inode); + iput(inode); + return -EINVAL; + } + xmfs_inode->symname = xmlt_addr; + + err = page_symlink(inode, symname, len); + if (err) { + clear_nlink(inode); + iput(inode); + return err; + } + + d_instantiate(dentry, inode); + { + struct xmfs_inode_table *tbl = + (void *)(sbi->kaddr + INODE_TABLE_OFFSET); + unsigned long slot = tbl->count; + + tbl->count++; + { + struct xmfs_inode_entry *e = &tbl->entries[slot]; + + memset(e, 0, sizeof(*e)); + e->i_ino = inode->i_ino; + e->p_ino = dir->i_ino; + e->mode = S_IFLNK | 0777; + e->nlink = 1; + e->size = 0; + e->ctime = inode_get_ctime(inode); + e->mtime = inode->i_mtime; + e->atime = inode->i_atime; + memcpy(e->name, dentry->d_name.name, + dentry->d_name.len); + if (dentry->d_name.len < 256) + e->name[dentry->d_name.len] = '\0'; + } + } + + return 0; +} + +static int xmfs_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *dentry) +{ + struct xmfs_sb_info *sbi = XMFS_SB(dir->i_sb); + + if (xmfs_quiesce_wait(sbi)) + return -EINTR; + if (sbi->id != 0) + return xmfs_do_link_slave(sbi, old_dentry, dir, dentry); + const char *name = dentry->d_name.name; + struct inode *inode = d_inode(old_dentry); + struct xmfs_log xm_log; + unsigned long xmlt_addr; + + if (XMFS_I(inode)->die) { + xmfs_info(sbi->sb, + "inode logs have been interrupted before link"); + return -EINVAL; + } + struct xmfs_sb_index *xmsi; + + spin_lock(&sbi->space_lock); + xmsi = get_xmsi(sbi); + if (!xmsi) { + spin_unlock(&sbi->space_lock); + return -EINVAL; + } + int logcount = 1; + + if (logcount + xmsi->used_trunk_count > sbi->max_chunk) { + sbi->full = true; + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + return -ENOSPC; + } + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + + dir->i_mtime = + inode_set_ctime_to_ts(dir, inode_set_ctime_current(inode)); + inc_nlink(inode); + ihold(inode); + + d_instantiate(dentry, inode); + + { + struct xmfs_inode_table *tbl = + (void *)(sbi->kaddr + INODE_TABLE_OFFSET); + unsigned long slot; + + spin_lock(&sbi->space_lock); + slot = tbl->count; + tbl->count++; + { + struct xmfs_inode_entry *e = &tbl->entries[slot]; + + memset(e, 0, sizeof(*e)); + e->i_ino = inode->i_ino; + e->p_ino = dir->i_ino; + e->mode = inode->i_mode; + e->nlink = inode->i_nlink; + e->size = i_size_read(inode); + e->ctime = inode_get_ctime(inode); + e->mtime = inode->i_mtime; + e->atime = inode->i_atime; + memcpy(e->name, dentry->d_name.name, + dentry->d_name.len); + if (dentry->d_name.len < 256) + e->name[dentry->d_name.len] = '\0'; + } + xmfs_inode_table_sync_nlink(tbl, inode->i_ino); + spin_unlock(&sbi->space_lock); + } + + xm_log.index = 0; + xm_log.end_index = inode->i_ino; + xm_log.ops = XMFS_LINK; + xm_log.data_offset = dir->i_mtime.tv_sec; + xm_log.mode = inode->i_mode; + xm_log.offset = dir->i_mtime.tv_nsec; + memcpy(xm_log.name, name, min(strlen(name), 255UL)); + xm_log.p_ino = parent_ino(dentry); + + xmlt_addr = xmfs_add_log_v3(&xm_log, sbi, + min(strlen(name), 255UL), + inode->i_ino, true); + if (xmlt_addr < 0) { + struct xmfs_inode_table *tbl = + (void *)(sbi->kaddr + INODE_TABLE_OFFSET); + + spin_lock(&sbi->space_lock); + xmfs_inode_table_remove(tbl, inode->i_ino, dir->i_ino, name); + xmfs_inode_table_sync_nlink(tbl, inode->i_ino); + spin_unlock(&sbi->space_lock); + XMFS_I(inode)->die = true; + drop_nlink(inode); + iput(inode); + dput(dentry); + return -EINVAL; + } + + return 0; +} + +static int xmfs_do_create_slave(struct xmfs_sb_info *sbi, struct inode *dir, + struct dentry *dentry, umode_t mode) +{ + struct xmfs_rw_msg req = {}, grant = {}; + struct inode *inode; + struct xmfs_inode_info *xmfs_inode; + int err; + struct xmfs_log xm_log; + unsigned long log_off; + + req.ops = XMFS_CREATE; + req.mode = mode | S_IFREG; + req.p_ino = dir->i_ino; + req.i_ino = 0; + req.data_pages = 0; + req.log_entries = 1; + memcpy(req.name, dentry->d_name.name, dentry->d_name.len + 1); + req.type = MSG_RW_REQUEST; + req.slave_id = sbi->id; + spin_lock(&sbi->local_ring_lock); + req.request_id = ++sbi->rw_request_id; + spin_unlock(&sbi->local_ring_lock); + struct xmfs_grant_wait gw; + + init_completion(&gw.done); + xa_store(&sbi->urma_cfg.grant_waits, req.request_id, &gw, GFP_KERNEL); + err = xmfs_send_request(sbi, &req, &gw, 0); + if (err) + return err; + grant = gw.grant; + + log_off = grant.log_trunk_addr + + offsetof(struct xmfs_log_trunk, log[0]) + + grant.log_slot_start * sizeof(struct xmfs_log); + xm_log.index = 0; + xm_log.end_index = 0; + xm_log.ops = XMFS_CREATE; + xm_log.data_offset = ktime_get_real_seconds(); + xm_log.mode = mode | S_IFREG; + xm_log.offset = ktime_get_real_ns() % NSEC_PER_SEC; + xm_log.version = grant.version; + memcpy(xm_log.name, dentry->d_name.name, + min(dentry->d_name.len, 255UL)); + if (dentry->d_name.len < 256) + xm_log.name[dentry->d_name.len] = '\0'; + xm_log.p_ino = dir->i_ino; + xm_log.i_ino = grant.assigned_ino; + xm_log.writer_id = sbi->id; + xm_log.size = 0; + xmfs_data_write(sbi, log_off, &xm_log, sizeof(xm_log)); + + inode = iget5_locked(dir->i_sb, grant.assigned_ino, xmfs_inode_eq, + xmfs_inode_set, &grant.assigned_ino); + if (!inode) + return -ENOMEM; + + if (!(inode->i_state & I_NEW)) + return 0; + + xmfs_inode = XMFS_I(inode); + i_size_write(inode, 0); + inode->i_ino = grant.assigned_ino; + inode_init_owner(&nop_mnt_idmap, inode, dir, mode | S_IFREG); + xmfs_init_file_inode(inode); + /* + * xmfs_inode->write_wq = alloc_workqueue("xmfs-prefetch-inode", + * WQ_UNBOUND | WQ_MEM_RECLAIM, + * 8); + */ + security_inode_init_security(inode, dir, &dentry->d_name, + xmfs_initxattrs, NULL); + xmfs_inode->btime = inode->i_mtime = inode->i_atime = + inode_set_ctime_current(inode); + dir->i_mtime = inode_set_ctime_current(dir); + unlock_new_inode(inode); + d_instantiate(dentry, inode); + ihold(inode); + xa_store(&sbi->pinned_inodes, inode->i_ino, inode, GFP_KERNEL); + XMFS_I(inode)->pinned = true; + + return 0; +} + +static int xmfs_do_mkdir_slave(struct xmfs_sb_info *sbi, struct inode *dir, + struct dentry *dentry, umode_t mode) +{ + struct xmfs_rw_msg req = {}, grant = {}; + struct inode *inode; + struct xmfs_inode_info *xmfs_inode; + int err; + struct xmfs_log xm_log; + unsigned long log_off; + + req.ops = XMFS_MKDIR; + req.mode = mode | S_IFDIR; + req.p_ino = dir->i_ino; + req.i_ino = 0; + req.data_pages = 0; + req.log_entries = 1; + req.size = 4096; + memcpy(req.name, dentry->d_name.name, dentry->d_name.len + 1); + + req.type = MSG_RW_REQUEST; + req.slave_id = sbi->id; + spin_lock(&sbi->local_ring_lock); + req.request_id = ++sbi->rw_request_id; + spin_unlock(&sbi->local_ring_lock); + struct xmfs_grant_wait gw; + + init_completion(&gw.done); + xa_store(&sbi->urma_cfg.grant_waits, req.request_id, &gw, GFP_KERNEL); + err = xmfs_send_request(sbi, &req, &gw, 0); + if (err) + return err; + grant = gw.grant; + + log_off = grant.log_trunk_addr + + offsetof(struct xmfs_log_trunk, log[0]) + + grant.log_slot_start * sizeof(struct xmfs_log); + xm_log.index = 0; + xm_log.end_index = 0; + xm_log.ops = XMFS_MKDIR; + xm_log.data_offset = ktime_get_real_seconds(); + xm_log.mode = mode | S_IFDIR; + xm_log.offset = ktime_get_real_ns() % NSEC_PER_SEC; + xm_log.version = grant.version; + memcpy(xm_log.name, dentry->d_name.name, + min(dentry->d_name.len, 255UL)); + if (dentry->d_name.len < 256) + xm_log.name[dentry->d_name.len] = '\0'; + xm_log.p_ino = dir->i_ino; + xm_log.i_ino = grant.assigned_ino; + xm_log.writer_id = sbi->id; + xm_log.size = 4096; + xmfs_data_write(sbi, log_off, &xm_log, sizeof(xm_log)); + + inode = iget5_locked(dir->i_sb, grant.assigned_ino, xmfs_inode_eq, + xmfs_inode_set, &grant.assigned_ino); + if (!inode) + return -ENOMEM; + + if (!(inode->i_state & I_NEW)) + return 0; + + xmfs_inode = XMFS_I(inode); + i_size_write(inode, 4096); + inode->i_ino = grant.assigned_ino; + inode_init_owner(&nop_mnt_idmap, inode, dir, S_IFDIR | mode); + xmfs_init_dir_inode(inode, false); + xmfs_inode->btime = inode->i_mtime = inode->i_atime = + inode_set_ctime_current(inode); + dir->i_mtime = inode_set_ctime_current(dir); + inc_nlink(dir); + inc_nlink(inode); + unlock_new_inode(inode); + d_instantiate(dentry, inode); + ihold(inode); + xa_store(&sbi->pinned_inodes, inode->i_ino, inode, GFP_KERNEL); + XMFS_I(inode)->pinned = true; + + return 0; +} + +static int xmfs_do_unlink_slave(struct xmfs_sb_info *sbi, struct inode *dir, + struct dentry *dentry) +{ + struct xmfs_rw_msg req = {}, grant = {}; + int err; + struct xmfs_log xm_log; + unsigned long log_off; + + req.ops = XMFS_DELETE; + req.p_ino = dir->i_ino; + req.i_ino = dentry->d_inode->i_ino; + req.data_pages = 0; + req.log_entries = 1; + memcpy(req.name, dentry->d_name.name, dentry->d_name.len + 1); + + req.type = MSG_RW_REQUEST; + req.slave_id = sbi->id; + spin_lock(&sbi->local_ring_lock); + req.request_id = ++sbi->rw_request_id; + spin_unlock(&sbi->local_ring_lock); + struct xmfs_grant_wait gw; + + init_completion(&gw.done); + xa_store(&sbi->urma_cfg.grant_waits, req.request_id, &gw, GFP_KERNEL); + err = xmfs_send_request(sbi, &req, &gw, 0); + if (err) + return err; + grant = gw.grant; + + log_off = grant.log_trunk_addr + + offsetof(struct xmfs_log_trunk, log[0]) + + grant.log_slot_start * sizeof(struct xmfs_log); + xm_log.ops = XMFS_DELETE; + xm_log.i_ino = dentry->d_inode->i_ino; + xm_log.p_ino = dir->i_ino; + xm_log.version = grant.version; + memcpy(xm_log.name, dentry->d_name.name, + min(dentry->d_name.len, 255UL)); + if (dentry->d_name.len < 256) + xm_log.name[dentry->d_name.len] = '\0'; + xm_log.writer_id = sbi->id; + xmfs_data_write(sbi, log_off, &xm_log, sizeof(xm_log)); + + if (d_inode(dentry)->i_nlink > 0) + drop_nlink(d_inode(dentry)); + if (!d_inode(dentry)->i_nlink) + xmfs_unpin_inode(sbi, d_inode(dentry)); + return 0; +} + +static int xmfs_do_symlink_slave(struct xmfs_sb_info *sbi, struct inode *dir, + struct dentry *dentry, const char *symname) +{ + struct xmfs_rw_msg req = {}; + struct inode *inode; + struct xmfs_inode_info *xmfs_inode; + unsigned int len = strlen(symname) + 1; + struct xmfs_log xm_log; + unsigned long log_off; + int err; + + if (len > PAGE_SIZE) + return -ENAMETOOLONG; + + req.ops = XMFS_SYMLINK; + req.mode = S_IFLNK | 0777; + req.p_ino = dir->i_ino; + req.i_ino = 0; + req.data_pages = (len > 255) ? 1 : 0; + req.log_entries = 2; + memcpy(req.name, dentry->d_name.name, dentry->d_name.len + 1); + + req.type = MSG_RW_REQUEST; + req.slave_id = sbi->id; + spin_lock(&sbi->local_ring_lock); + req.request_id = ++sbi->rw_request_id; + spin_unlock(&sbi->local_ring_lock); + struct xmfs_grant_wait gw; + + init_completion(&gw.done); + xa_store(&sbi->urma_cfg.grant_waits, req.request_id, &gw, GFP_KERNEL); + err = xmfs_send_request(sbi, &req, &gw, 0); + if (err) + return err; + + inode = iget5_locked(dir->i_sb, gw.grant.assigned_ino, xmfs_inode_eq, + xmfs_inode_set, &gw.grant.assigned_ino); + if (!inode) + return -ENOMEM; + + if (!(inode->i_state & I_NEW)) + return 0; + + xmfs_inode = XMFS_I(inode); + inode->i_ino = gw.grant.assigned_ino; + i_size_write(inode, 0); + inode_init_owner(&nop_mnt_idmap, inode, dir, S_IFLNK | 0777); + xmfs_init_symlink_operations(inode); + inode_nohighmem(inode); + xmfs_init_symlink_mapping(inode); + xmfs_inode->btime = inode->i_atime = inode->i_mtime = + inode_set_ctime_current(inode); + dir->i_mtime = inode_set_ctime_current(dir); + unlock_new_inode(inode); + ihold(inode); + xa_store(&sbi->pinned_inodes, inode->i_ino, inode, GFP_KERNEL); + XMFS_I(inode)->pinned = true; + + log_off = gw.grant.log_trunk_addr + + offsetof(struct xmfs_log_trunk, log[0]) + + gw.grant.log_slot_start * sizeof(struct xmfs_log); + xm_log.index = 0; + xm_log.end_index = 0; + xm_log.ops = XMFS_SYMLINK; + xm_log.data_offset = ktime_get_real_seconds(); + xm_log.mode = S_IFLNK | 0777; + xm_log.version = gw.grant.version; + memcpy(xm_log.name, dentry->d_name.name, + min(dentry->d_name.len, 255UL)); + if (dentry->d_name.len < 256) + xm_log.name[dentry->d_name.len] = '\0'; + xm_log.p_ino = dir->i_ino; + xm_log.i_ino = gw.grant.assigned_ino; + xm_log.writer_id = sbi->id; + xmfs_data_write(sbi, log_off, &xm_log, sizeof(xm_log)); + + if (gw.grant.log_slot_count >= 2) { + struct xmfs_log xm_log1 = {}; + + xm_log1.index = 1; + xm_log1.ops = XMFS_SYMLINK; + xm_log1.version = gw.grant.version + 1; + xm_log1.i_ino = gw.grant.assigned_ino; + xm_log1.p_ino = dir->i_ino; + xm_log1.writer_id = sbi->id; + if (len <= 255) { + memcpy(xm_log1.name, symname, + min(len, 255UL)); + xm_log1.name[min(len - 1, 254UL)] = '\0'; + xm_log1.end_index = 0; + } else { + xm_log1.end_index = gw.grant.data_addr; + xm_log1.index = len; + XMFS_I(inode)->start_write = gw.grant.data_addr; + XMFS_I(inode)->count = gw.grant.data_page_count; + } + log_off = gw.grant.log_trunk_addr + + offsetof(struct xmfs_log_trunk, log[0]) + + (gw.grant.log_slot_start + 1) * sizeof(struct xmfs_log); + xmfs_inode->symname = log_off; + xmfs_data_write(sbi, log_off, &xm_log1, sizeof(xm_log1)); + } + + if (len > 255 && gw.grant.data_addr != 0) { + xmfs_data_write(sbi, gw.grant.data_addr, symname, + min(len, (unsigned long)PAGE_SIZE)); + } + + err = page_symlink(inode, symname, len); + if (err) { + clear_nlink(inode); + iput(inode); + return err; + } + + d_instantiate(dentry, inode); + + return 0; +} + +static int xmfs_do_link_slave(struct xmfs_sb_info *sbi, + struct dentry *old_dentry, struct inode *dir, + struct dentry *dentry) +{ + struct xmfs_rw_msg req = {}, grant = {}; + struct inode *inode = d_inode(old_dentry); + int err; + struct xmfs_log xm_log; + unsigned long log_off; + + req.ops = XMFS_LINK; + req.p_ino = dir->i_ino; + req.i_ino = inode->i_ino; + req.mode = inode->i_mode; + req.data_pages = 0; + req.log_entries = 1; + memcpy(req.name, dentry->d_name.name, dentry->d_name.len + 1); + + req.type = MSG_RW_REQUEST; + req.slave_id = sbi->id; + spin_lock(&sbi->local_ring_lock); + req.request_id = ++sbi->rw_request_id; + spin_unlock(&sbi->local_ring_lock); + struct xmfs_grant_wait gw; + + init_completion(&gw.done); + xa_store(&sbi->urma_cfg.grant_waits, req.request_id, &gw, GFP_KERNEL); + err = xmfs_send_request(sbi, &req, &gw, 0); + if (err) + return err; + grant = gw.grant; + + log_off = grant.log_trunk_addr + + offsetof(struct xmfs_log_trunk, log[0]) + + grant.log_slot_start * sizeof(struct xmfs_log); + xm_log.index = 0; + xm_log.end_index = inode->i_ino; + xm_log.ops = XMFS_LINK; + xm_log.data_offset = ktime_get_real_seconds(); + xm_log.mode = inode->i_mode; + xm_log.offset = ktime_get_real_ns() % NSEC_PER_SEC; + xm_log.version = grant.version; + memcpy(xm_log.name, dentry->d_name.name, + min(dentry->d_name.len, 255UL)); + if (dentry->d_name.len < 256) + xm_log.name[dentry->d_name.len] = '\0'; + xm_log.p_ino = dir->i_ino; + xm_log.i_ino = inode->i_ino; + xm_log.writer_id = sbi->id; + xmfs_data_write(sbi, log_off, &xm_log, sizeof(xm_log)); + + inc_nlink(inode); + ihold(inode); + + d_instantiate(dentry, inode); + + return 0; +} + +static int xmfs_create(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, umode_t mode, bool excl) +{ + struct xmfs_sb_info *sbi = XMFS_SB(dir->i_sb); + + if (xmfs_quiesce_wait(sbi)) + return -EINTR; + if (sbi->id != 0) + return xmfs_do_create_slave(sbi, dir, dentry, mode); + struct xmfs_log xm_log; + unsigned long xmlt_addr; + struct xmfs_sb_index *xmsi; + int err = 0; + struct xmfs_inode_info *xmfs_inode; + const char *name = dentry->d_name.name; + struct inode *inode; + + spin_lock(&sbi->space_lock); + if (sbi->full) { + spin_unlock(&sbi->space_lock); + return -ENOSPC; + } + + xmsi = get_xmsi(sbi); + if (!xmsi) { + spin_unlock(&sbi->space_lock); + return -EINVAL; + } + + int logcount = 1; + + if (logcount + xmsi->used_trunk_count > sbi->max_chunk) { + sbi->full = true; + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + return -ENOSPC; + } + + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + unsigned long ino = get_next_ino(); + + inode = iget5_locked(dir->i_sb, ino, xmfs_inode_eq, xmfs_inode_set, + &ino); + if (!inode) + return -ENOMEM; + + xmfs_inode = XMFS_I(inode); + i_size_write(inode, 0); + + inode->i_ino = ino; + inode_init_owner(idmap, inode, dir, mode | S_IFREG); + + xmfs_init_file_inode(inode); + /* + * xmfs_inode->write_wq = alloc_workqueue("xmfs-prefetch-inode", + * WQ_UNBOUND | WQ_MEM_RECLAIM, + * 8); + */ + err = security_inode_init_security(inode, dir, &dentry->d_name, + xmfs_initxattrs, NULL); + if (err) { + unlock_new_inode(inode); + iput(inode); + return err; + } + xmfs_inode->btime = inode->i_mtime = inode->i_atime = + inode_set_ctime_current(inode); + + dir->i_mtime = inode_set_ctime_current(dir); + unlock_new_inode(inode); + d_instantiate(dentry, inode); + ihold(inode); + xa_store(&sbi->pinned_inodes, inode->i_ino, inode, GFP_KERNEL); + XMFS_I(inode)->pinned = true; + + { + struct xmfs_inode_table *tbl = + (void *)(sbi->kaddr + INODE_TABLE_OFFSET); + unsigned long slot = tbl->count; + + tbl->count++; + { + struct xmfs_inode_entry *e = &tbl->entries[slot]; + + memset(e, 0, sizeof(*e)); + e->i_ino = inode->i_ino; + e->p_ino = dir->i_ino; + e->mode = mode | S_IFREG; + e->nlink = 1; + e->size = 0; + e->ctime = inode_get_ctime(inode); + e->mtime = inode->i_mtime; + e->atime = inode->i_atime; + memcpy(e->name, dentry->d_name.name, + dentry->d_name.len); + if (dentry->d_name.len < 256) + e->name[dentry->d_name.len] = '\0'; + } + } + + xm_log.index = 0; + xm_log.end_index = 0; + xm_log.ops = XMFS_CREATE; + xm_log.data_offset = inode->i_mtime.tv_sec; + xm_log.mode = mode | S_IFREG; + xm_log.offset = inode->i_mtime.tv_nsec; + memcpy(xm_log.name, name, min(strlen(name), 255UL)); + xm_log.p_ino = parent_ino(dentry); + xm_log.writer_id = sbi->id; + xm_log.size = 0; + + xmlt_addr = xmfs_add_log_v3(&xm_log, sbi, + min(strlen(name), 255UL), + inode->i_ino, true); + if (xmlt_addr < 0) { + xmfs_inode->die = true; + iput(inode); + dput(dentry); + return -EINVAL; + } + + if (sbi->fallocate_size) + xmfs_common_fallocate(inode, 0x01, 0, sbi->fallocate_size); + + return 0; +} + +static int xmfs_do_mknod_slave(struct xmfs_sb_info *sbi, struct inode *dir, + struct dentry *dentry, umode_t mode, dev_t dev) +{ + struct xmfs_rw_msg req = {}, grant = {}; + struct inode *inode; + struct xmfs_inode_info *xmfs_inode; + int err; + struct xmfs_log xm_log; + unsigned long log_off; + + req.ops = XMFS_CREATE; + req.mode = mode; + req.p_ino = dir->i_ino; + req.i_ino = 0; + req.data_pages = 0; + req.log_entries = 1; + memcpy(req.name, dentry->d_name.name, dentry->d_name.len + 1); + + req.type = MSG_RW_REQUEST; + req.slave_id = sbi->id; + spin_lock(&sbi->local_ring_lock); + req.request_id = ++sbi->rw_request_id; + spin_unlock(&sbi->local_ring_lock); + struct xmfs_grant_wait gw; + + init_completion(&gw.done); + xa_store(&sbi->urma_cfg.grant_waits, req.request_id, &gw, GFP_KERNEL); + err = xmfs_send_request(sbi, &req, &gw, 0); + if (err) + return err; + grant = gw.grant; + + log_off = grant.log_trunk_addr + + offsetof(struct xmfs_log_trunk, log[0]) + + grant.log_slot_start * sizeof(struct xmfs_log); + memset(&xm_log, 0, sizeof(xm_log)); + xm_log.ops = XMFS_CREATE; + xm_log.mode = mode; + xm_log.p_ino = dir->i_ino; + xm_log.i_ino = grant.assigned_ino; + xm_log.index = 1; + xm_log.end_index = dev; + xm_log.data_offset = ktime_get_real_seconds(); + xm_log.offset = ktime_get_real_ns() % NSEC_PER_SEC; + xm_log.version = grant.version; + memcpy(xm_log.name, dentry->d_name.name, + min(dentry->d_name.len, 255UL)); + if (dentry->d_name.len < 256) + xm_log.name[dentry->d_name.len] = '\0'; + xm_log.writer_id = sbi->id; + xmfs_data_write(sbi, log_off, &xm_log, sizeof(xm_log)); + + inode = iget5_locked(dir->i_sb, grant.assigned_ino, xmfs_inode_eq, + xmfs_inode_set, &grant.assigned_ino); + if (!inode) + return -ENOMEM; + + if (!(inode->i_state & I_NEW)) + return 0; + + xmfs_inode = XMFS_I(inode); + i_size_write(inode, 0); + inode->i_ino = grant.assigned_ino; + inode_init_owner(&nop_mnt_idmap, inode, dir, mode); + init_special_inode(inode, mode, dev); + xmfs_init_special_inode_operations(inode); + xmfs_inode->btime = inode->i_mtime = inode->i_atime = + inode_set_ctime_current(inode); + unlock_new_inode(inode); + d_instantiate(dentry, inode); + ihold(inode); + xa_store(&sbi->pinned_inodes, inode->i_ino, inode, GFP_KERNEL); + XMFS_I(inode)->pinned = true; + + return 0; +} + +static int xmfs_mknod(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, umode_t mode, dev_t dev) +{ + struct xmfs_sb_info *sbi = XMFS_SB(dir->i_sb); + + if (xmfs_quiesce_wait(sbi)) + return -EINTR; + if (sbi->id != 0) + return xmfs_do_mknod_slave(sbi, dir, dentry, mode, dev); + struct xmfs_log xm_log; + unsigned long xmlt_addr; + struct xmfs_sb_index *xmsi; + int err = 0; + struct xmfs_inode_info *xmfs_inode; + const char *name = dentry->d_name.name; + struct inode *inode; + + spin_lock(&sbi->space_lock); + if (sbi->full) { + spin_unlock(&sbi->space_lock); + return -ENOSPC; + } + + xmsi = get_xmsi(sbi); + if (!xmsi) { + spin_unlock(&sbi->space_lock); + return -EINVAL; + } + int logcount = 1; + + if (logcount + xmsi->used_trunk_count > sbi->max_chunk) { + sbi->full = true; + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + return -ENOSPC; + } + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + unsigned long ino = get_next_ino(); + + inode = iget5_locked(dir->i_sb, ino, xmfs_inode_eq, xmfs_inode_set, + &ino); + if (!inode) + return -ENOMEM; + + xmfs_inode = XMFS_I(inode); + i_size_write(inode, 0); + + inode->i_ino = ino; + inode_init_owner(idmap, inode, dir, mode); + init_special_inode(inode, mode, dev); + xmfs_init_special_inode_operations(inode); + err = security_inode_init_security(inode, dir, &dentry->d_name, + xmfs_initxattrs, NULL); + if (err) { + unlock_new_inode(inode); + iput(inode); + return err; + } + xmfs_inode->btime = inode->i_mtime = inode->i_atime = + inode_set_ctime_current(inode); + + dir->i_mtime = inode_set_ctime_current(dir); + unlock_new_inode(inode); + d_instantiate(dentry, inode); + ihold(inode); + xa_store(&sbi->pinned_inodes, inode->i_ino, inode, GFP_KERNEL); + XMFS_I(inode)->pinned = true; + + { + struct xmfs_inode_table *tbl = + (void *)(sbi->kaddr + INODE_TABLE_OFFSET); + unsigned long slot = tbl->count; + + tbl->count++; + { + struct xmfs_inode_entry *e = &tbl->entries[slot]; + + memset(e, 0, sizeof(*e)); + e->i_ino = inode->i_ino; + e->p_ino = dir->i_ino; + e->mode = mode; + e->nlink = 1; + e->size = 0; + e->ctime = inode_get_ctime(inode); + e->mtime = inode->i_mtime; + e->atime = inode->i_atime; + memcpy(e->name, dentry->d_name.name, + dentry->d_name.len); + if (dentry->d_name.len < 256) + e->name[dentry->d_name.len] = '\0'; + } + } + + xm_log.index = 1; + xm_log.end_index = dev; + xm_log.ops = XMFS_CREATE; + xm_log.data_offset = inode->i_mtime.tv_sec; + xm_log.mode = mode; + xm_log.offset = inode->i_mtime.tv_nsec; + memcpy(xm_log.name, name, min(strlen(name), 255UL)); + xm_log.p_ino = parent_ino(dentry); + + xmlt_addr = xmfs_add_log_v3(&xm_log, sbi, + min(strlen(name), 255UL), + inode->i_ino, true); + if (xmlt_addr < 0) { + xmfs_inode->die = true; + iput(inode); + dput(dentry); + return -EINVAL; + } + + return 0; +} + +static int xmfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, umode_t mode) +{ + struct xmfs_sb_info *sbi = XMFS_SB(dir->i_sb); + + if (xmfs_quiesce_wait(sbi)) + return -EINTR; + if (sbi->id != 0) + return xmfs_do_mkdir_slave(sbi, dir, dentry, mode); + struct xmfs_log xm_log; + struct xmfs_sb_index *xmsi; + int err = 0; + struct xmfs_inode_info *xmfs_inode; + const char *name = dentry->d_name.name; + struct inode *inode; + unsigned long xmlt_addr; + + spin_lock(&sbi->space_lock); + if (sbi->full) { + spin_unlock(&sbi->space_lock); + return -ENOSPC; + } + + xmsi = get_xmsi(sbi); + if (!xmsi) { + spin_unlock(&sbi->space_lock); + return -EINVAL; + } + int logcount = 1; + + if (logcount + xmsi->used_trunk_count > sbi->max_chunk) { + sbi->full = true; + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + return -ENOSPC; + } + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + + unsigned long ino = get_next_ino(); + + inode = iget5_locked(dir->i_sb, ino, xmfs_inode_eq, xmfs_inode_set, + &ino); + if (!inode) + return -ENOMEM; + + xmfs_inode = XMFS_I(inode); + i_size_write(inode, 4096); + + inode->i_ino = ino; + inode_init_owner(idmap, inode, dir, S_IFDIR | mode); + xmfs_init_dir_inode(inode, true); + + xmfs_inode->btime = inode->i_mtime = inode->i_atime = + inode_set_ctime_current(inode); + + dir->i_mtime = inode_set_ctime_current(dir); + + err = security_inode_init_security(inode, dir, &dentry->d_name, + xmfs_initxattrs, NULL); + if (err) { + unlock_new_inode(inode); + iput(inode); + return err; + } + inc_nlink(dir); + inc_nlink(inode); + unlock_new_inode(inode); + d_instantiate(dentry, inode); + ihold(inode); + xa_store(&sbi->pinned_inodes, inode->i_ino, inode, GFP_KERNEL); + XMFS_I(inode)->pinned = true; + + { + struct xmfs_inode_table *tbl = + (void *)(sbi->kaddr + INODE_TABLE_OFFSET); + unsigned long slot = tbl->count; + + tbl->count++; + { + struct xmfs_inode_entry *e = &tbl->entries[slot]; + + memset(e, 0, sizeof(*e)); + e->i_ino = inode->i_ino; + e->p_ino = dir->i_ino; + e->mode = mode | S_IFDIR; + e->nlink = 1; + e->size = 4096; + e->ctime = inode_get_ctime(inode); + e->mtime = inode->i_mtime; + e->atime = inode->i_atime; + memcpy(e->name, dentry->d_name.name, + dentry->d_name.len); + if (dentry->d_name.len < 256) + e->name[dentry->d_name.len] = '\0'; + } + } + + xm_log.index = 0; + xm_log.end_index = 0; + xm_log.ops = XMFS_MKDIR; + xm_log.data_offset = dir->i_mtime.tv_sec; + xm_log.mode = mode | S_IFDIR; + xm_log.offset = dir->i_mtime.tv_nsec; + memcpy(xm_log.name, name, min(strlen(name), 255UL)); + xm_log.p_ino = parent_ino(dentry); + xm_log.size = 4096; + + xmlt_addr = xmfs_add_log_v3(&xm_log, sbi, + min(strlen(name), 255UL), + inode->i_ino, true); + if (xmlt_addr < 0) { + xmfs_inode->die = true; + drop_nlink(dir); + drop_nlink(inode); + iput(inode); + dput(dentry); + return 0; + } + + return 0; +} + +static int xmfs_do_rename_slave(struct xmfs_sb_info *sbi, struct inode *old_dir, + struct dentry *old_dentry, + struct inode *new_dir, + struct dentry *new_dentry, unsigned int flags) +{ + struct xmfs_rw_msg req = {}, grant = {}; + const char *old_name = old_dentry->d_name.name; + const char *new_name = new_dentry->d_name.name; + unsigned long old_len = old_dentry->d_name.len; + unsigned long new_len = new_dentry->d_name.len; + int err; + struct xmfs_log xm_log; + unsigned long log_off; + + if (old_len + 1 + new_len + 1 > 256) + return -ENAMETOOLONG; + if (flags & ~(RENAME_NOREPLACE)) + return -EINVAL; + + req.ops = XMFS_RENAME; + req.p_ino = old_dir->i_ino; + req.i_ino = old_dentry->d_inode->i_ino; + req.data_pages = 0; + req.log_entries = 2; + memcpy(req.name, old_name, old_len + 1); + memcpy(req.name + old_len + 1, new_name, new_len + 1); + + req.type = MSG_RW_REQUEST; + req.slave_id = sbi->id; + spin_lock(&sbi->local_ring_lock); + req.request_id = ++sbi->rw_request_id; + spin_unlock(&sbi->local_ring_lock); + struct xmfs_grant_wait gw; + + init_completion(&gw.done); + xa_store(&sbi->urma_cfg.grant_waits, req.request_id, &gw, GFP_KERNEL); + err = xmfs_send_request(sbi, &req, &gw, 0); + if (err) + return err; + grant = gw.grant; + + log_off = grant.log_trunk_addr + + offsetof(struct xmfs_log_trunk, log[0]) + + grant.log_slot_start * sizeof(struct xmfs_log); + memset(&xm_log, 0, sizeof(xm_log)); + xm_log.ops = XMFS_RENAME; + xm_log.p_ino = old_dir->i_ino; + xm_log.i_ino = old_dentry->d_inode->i_ino; + xm_log.end_index = -1; + xm_log.version = grant.version; + memcpy(xm_log.name, old_name, min(old_len, 255UL)); + if (old_len < 256) + xm_log.name[old_len] = '\0'; + xm_log.writer_id = sbi->id; + xmfs_data_write(sbi, log_off, &xm_log, sizeof(xm_log)); + + log_off = grant.log_trunk_addr + + offsetof(struct xmfs_log_trunk, log[0]) + + (grant.log_slot_start + 1) * sizeof(struct xmfs_log); + memset(&xm_log, 0, sizeof(xm_log)); + xm_log.ops = XMFS_RENAME; + xm_log.p_ino = new_dir->i_ino; + xm_log.i_ino = old_dentry->d_inode->i_ino; + xm_log.version = grant.version + 1; + memcpy(xm_log.name, new_name, min(new_len, 255UL)); + if (new_len < 256) + xm_log.name[new_len] = '\0'; + xm_log.writer_id = sbi->id; + xmfs_data_write(sbi, log_off, &xm_log, sizeof(xm_log)); + + err = simple_rename(&nop_mnt_idmap, old_dir, old_dentry, new_dir, + new_dentry, flags); + if (err) + return err; + + return 0; +} + +static int xmfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, + struct dentry *old_dentry, struct inode *new_dir, + struct dentry *new_dentry, unsigned int flags) +{ + /* RENAME_EXCHANGE is disabled because d_exchange() is not exported. */ + if (flags & ~(RENAME_NOREPLACE)) + return -EINVAL; + struct xmfs_sb_info *sbi = XMFS_SB(old_dir->i_sb); + + if (xmfs_quiesce_wait(sbi)) + return -EINTR; + if (sbi->id != 0) + return xmfs_do_rename_slave(sbi, old_dir, old_dentry, new_dir, + new_dentry, flags); + if (XMFS_I(old_dentry->d_inode)->die) { + xmfs_info(sbi->sb, + "inode logs have been interrupted before rename"); + return -EINVAL; + } + struct xmfs_sb_index *xmsi; + int err = 0; + + spin_lock(&sbi->space_lock); + xmsi = get_xmsi(sbi); + if (!xmsi) { + spin_unlock(&sbi->space_lock); + return -EINVAL; + } + int logcount = 1; + + if (logcount + xmsi->used_trunk_count > sbi->max_chunk) { + sbi->full = true; + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + return -ENOSPC; + } + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + + err = simple_rename(idmap, old_dir, old_dentry, new_dir, new_dentry, + flags); + { + struct xmfs_inode_table *tbl = + (void *)(sbi->kaddr + INODE_TABLE_OFFSET); + unsigned long j; + + for (j = 0; j < tbl->count; j++) { + if (tbl->entries[j].i_ino == + old_dentry->d_inode->i_ino && + tbl->entries[j].p_ino == old_dir->i_ino && + strcmp(tbl->entries[j].name, + old_dentry->d_name.name) == 0) { + tbl->entries[j].p_ino = new_dir->i_ino; + memset(tbl->entries[j].name, 0, 256); + memcpy(tbl->entries[j].name, + new_dentry->d_name.name, + min(new_dentry->d_name.len, + 255UL)); + if (new_dentry->d_name.len < 256) + tbl->entries[j] + .name[new_dentry->d_name.len] = + '\0'; + break; + } + } + } + if (err < 0) + return err; + + struct xmfs_log xm_log_old; + + xm_log_old.index = flags; + xm_log_old.ops = XMFS_RENAME; + xm_log_old.data_offset = old_dir->i_mtime.tv_sec; + xm_log_old.end_index = -1; + + const char *name = old_dentry->d_name.name; + + memcpy(xm_log_old.name, name, min(strlen(name), 255UL)); + xm_log_old.p_ino = old_dir->i_ino; + + struct xmfs_log xm_log_new; + + xm_log_new.ops = XMFS_RENAME; + xm_log_new.data_offset = old_dir->i_mtime.tv_nsec; + xm_log_new.p_ino = new_dir->i_ino; + xm_log_new.end_index = old_dentry->d_inode->i_ino; + + const char *new_name = new_dentry->d_name.name; + + memcpy(xm_log_new.name, new_name, + min(strlen(new_name), 255UL)); + + unsigned long addr; + + addr = xmfs_add_log_v3(&xm_log_old, sbi, + min(strlen(name), 255UL), + old_dentry->d_inode->i_ino, false); + if (addr < 0) { + XMFS_I(old_dentry->d_inode)->die = true; + return 0; + } + addr = xmfs_add_log_v3(&xm_log_new, sbi, + min(strlen(new_name), 255UL), + old_dentry->d_inode->i_ino, true); + if (addr < 0) { + XMFS_I(old_dentry->d_inode)->die = true; + return 0; + } + + return 0; +} + +static int xmfs_unlink(struct inode *dir, struct dentry *dentry) +{ + struct xmfs_sb_info *sbi = XMFS_SB(dir->i_sb); + + if (xmfs_quiesce_wait(sbi)) + return -EINTR; + if (sbi->id != 0) + return xmfs_do_unlink_slave(sbi, dir, dentry); + if (XMFS_I(dentry->d_inode)->die) { + xmfs_info(sbi->sb, + "inode logs have been interrupted before unlink"); + return -EINVAL; + } + struct xmfs_sb_index *xmsi; + + struct xmfs_log xm_log = { 0 }; + const char *name = dentry->d_name.name; + unsigned long ino = d_inode(dentry)->i_ino; + + spin_lock(&sbi->space_lock); + xmsi = get_xmsi(sbi); + if (!xmsi) { + spin_unlock(&sbi->space_lock); + return -EINVAL; + } + /* + * int logcount = 0; + * + * if (logcount + xmsi->used_trunk_count > sbi->max_chunk) { + * sbi->full = true; + * spin_unlock(&sbi->space_lock); + * put_xmsi(sbi, xmsi); + * return -ENOSPC; + * } + */ + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + + if (d_inode(dentry)->__i_nlink > 1) + xm_log.index = -1; + else + xm_log.index = 0; + xm_log.end_index = -1; + xm_log.ops = XMFS_DELETE; + xm_log.data_offset = dir->i_mtime.tv_sec; + xm_log.offset = dir->i_mtime.tv_nsec; + memcpy(xm_log.name, name, min(strlen(name), 255UL)); + xm_log.p_ino = parent_ino(dentry); + + unsigned long addr = + xmfs_add_log_v3(&xm_log, sbi, + min(strlen(name), 255UL), ino, + true); + if (addr < 0) { + XMFS_I(d_inode(dentry))->die = true; + return 0; + } + + spin_lock(&sbi->space_lock); + xmsi = get_xmsi(sbi); + if (xmsi) + put_xmsi(sbi, xmsi); + + spin_unlock(&sbi->space_lock); + + if (d_inode(dentry)->i_nlink > 0) + drop_nlink(d_inode(dentry)); + { + struct xmfs_inode_table *tbl = + (void *)(sbi->kaddr + INODE_TABLE_OFFSET); + bool removed; + + spin_lock(&sbi->space_lock); + removed = xmfs_inode_table_remove(tbl, ino, dir->i_ino, name); + xmfs_inode_table_sync_nlink(tbl, ino); + spin_unlock(&sbi->space_lock); + if (!removed) + xmfs_warning(sbi->sb, "inode %lu entry %s not found", ino, + name); + } + if (!d_inode(dentry)->i_nlink) + xmfs_unpin_inode(sbi, d_inode(dentry)); + + return 0; +} + +static int xmfs_rmdir(struct inode *dir, struct dentry *dentry) +{ + if (!simple_empty(dentry)) + return -ENOTEMPTY; + + drop_nlink(d_inode(dentry)); + int err = xmfs_unlink(dir, dentry); + + if (!err) + drop_nlink(dir); + + return err; +} + +static struct dentry *xmfs_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags) +{ + struct xmfs_sb_info *sbi = XMFS_SB(dir->i_sb); + struct xmfs_sb_index *xmsi; + struct dentry *p_dentry, *res = NULL; + struct xmfs_inode_table *tbl; + unsigned long max_entries, i; + struct inode *inode; + + if (dentry->d_name.len > NAME_MAX) + return ERR_PTR(-ENAMETOOLONG); + + xmsi = kmalloc(sizeof(struct xmfs_sb_index), GFP_KERNEL); + if (get_xmsi_and_copy(sbi, xmsi)) { + kfree(xmsi); + return ERR_PTR(-EIO); + } + /* + * xmfs_info(sbi->sb, "qyf id %ld lookup %ld %ld", sbi->id, + * xmsi->last_update, sbi->last_update); + */ + sbi->stop_cnt++; + if (xmsi->last_update != sbi->last_update) + xmfs_replay_new_entries(sbi, NULL, xmsi->last_update); + kfree(xmsi); + + struct qstr qname = QSTR_INIT(dentry->d_name.name, dentry->d_name.len); + + hlist_for_each_entry(p_dentry, &dir->i_dentry, d_u.d_alias) { + res = d_hash_and_lookup(p_dentry, &qname); + if (res) { + sbi->stop_cnt--; + return res; + } + } + + if (sbi->mode == XMFS_SHARED_MEM_MODE || sbi->mode == XMFS_HYBRID_MODE) + tbl = (struct xmfs_inode_table *)(sbi->kaddr + + INODE_TABLE_OFFSET); + else { + tbl = (struct xmfs_inode_table *)get_meta(sbi, + INODE_TABLE_OFFSET, + INODE_TABLE_SIZE); + /* + * tbl = (struct xmfs_inode_table *)get_meta( + * sbi, INODE_TABLE_OFFSET, + * max(sizeof(struct xmfs_inode_table) + + * tbl->count * sizeof(struct xmfs_inode_entry), + * INODE_TABLE_SIZE)); + */ + } + /* + * xmfs_info(sbi->sb, "qyf slave lookup find inode table %d", + * tbl->count); + */ + if (tbl && tbl->magic == XMFS_INODE_TABLE_MAGIC) { + max_entries = (INODE_TABLE_SIZE - 4096) / + sizeof(struct xmfs_inode_entry); + for (i = 0; i < tbl->count && i < max_entries; i++) { + struct xmfs_inode_entry *e = &tbl->entries[i]; + /* + * xmfs_info(sbi->sb, + * "qyf slave lookup iter inode table %d %ld %ld %s %s", + * i, e->p_ino, dir->i_ino, e->name, + * dentry->d_name.name); + */ + if (e->p_ino != dir->i_ino || + strcmp(e->name, dentry->d_name.name) != 0) + continue; + inode = ilookup5(sbi->sb, e->i_ino, xmfs_inode_eq, + &e->i_ino); + /* xmfs_info(sbi->sb, "qyf find inode? %p", inode); */ + if (!inode) + inode = iget5_locked(sbi->sb, e->i_ino, + xmfs_inode_eq, + xmfs_inode_set, &e->i_ino); + if (inode && (inode->i_state & I_NEW)) { + struct xmfs_inode_info *xmfs_inode = + XMFS_I(inode); + xmfs_inode->leaf = (e->i_ino != 0); + inode->i_ino = e->i_ino; + inode->i_mode = e->mode; + if (S_ISREG(e->mode)) { + /* + * xmfs_inode->write_wq = alloc_workqueue( + * "xmfs-prefetch-inode", + * WQ_UNBOUND | WQ_MEM_RECLAIM, 8); + */ + xmfs_init_file_inode(inode); + } else if (S_ISDIR(e->mode)) { + xmfs_init_dir_inode(inode, + sbi->id == 0); + } else if (S_ISLNK(e->mode)) { + xmfs_init_symlink_operations(inode); + } else { + init_special_inode(inode, e->mode, 0); + } + unlock_new_inode(inode); + ihold(inode); + xa_store(&sbi->pinned_inodes, inode->i_ino, + inode, GFP_KERNEL); + XMFS_I(inode)->pinned = true; + } + + if (inode) { + inode_lock(inode); + set_nlink(inode, e->nlink); + i_size_write(inode, e->size); + inode->__i_ctime.tv_sec = e->ctime.tv_sec; + inode->__i_ctime.tv_nsec = e->ctime.tv_nsec; + inode->i_mtime.tv_sec = e->mtime.tv_sec; + inode->i_mtime.tv_nsec = e->mtime.tv_nsec; + inode->i_atime.tv_sec = e->atime.tv_sec; + inode->i_atime.tv_nsec = e->atime.tv_nsec; + inode_unlock(inode); + res = d_splice_alias(inode, dentry); + } + break; + } + } + if (tbl && sbi->mode == XMFS_URMA_MODE) + put_meta(sbi, tbl); + + sbi->stop_cnt--; + return res; +} + +static const struct inode_operations xmfs_dir_inode_operations = { + .create = xmfs_create, + .lookup = xmfs_lookup, + .link = xmfs_link, + .unlink = xmfs_unlink, + .symlink = xmfs_symlink, + .mkdir = xmfs_mkdir, + .rmdir = xmfs_rmdir, + .mknod = xmfs_mknod, + .rename = xmfs_rename, + .setattr = xmfs_slave_setattr, + .getattr = xmfs_slave_getattr, + .update_time = xmfs_update_time, + .listxattr = xmfs_listxattr, +}; + +static int xmfs_tmpfile(struct mnt_idmap *idmap, struct inode *dir, + struct file *file, umode_t mode) +{ + struct xmfs_sb_info *sbi = XMFS_SB(dir->i_sb); + struct xmfs_sb_index *xmsi; + int err = 0; + struct xmfs_inode_info *xmfs_inode; + struct dentry *dentry = file->f_path.dentry; + const char *name = dentry->d_name.name; + struct xmfs_log xm_log; + struct inode *inode; + + spin_lock(&sbi->space_lock); + if (sbi->full) { + spin_unlock(&sbi->space_lock); + return -ENOSPC; + } + + xmsi = get_xmsi(sbi); + if (!xmsi) { + spin_unlock(&sbi->space_lock); + return -EINVAL; + } + int logcount = 1; + + if (logcount + xmsi->used_trunk_count > sbi->max_chunk) { + sbi->full = true; + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + return -ENOSPC; + } + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + unsigned long ino = get_next_ino(); + + inode = iget5_locked(dir->i_sb, ino, xmfs_inode_eq, xmfs_inode_set, + &ino); + if (!inode) + return -ENOMEM; + + xmfs_inode = XMFS_I(inode); + xmfs_inode->tmp = true; + + i_size_write(inode, 0); + + inode->i_ino = ino; + inode_init_owner(idmap, inode, dir, mode); + xmfs_init_file_inode(inode); + /* + * xmfs_inode->write_wq = alloc_workqueue("xmfs-prefetch-inode", + * WQ_UNBOUND | WQ_MEM_RECLAIM, + * 8); + */ + err = security_inode_init_security(inode, dir, &dentry->d_name, + xmfs_initxattrs, NULL); + if (err) { + unlock_new_inode(inode); + iput(inode); + return err; + } + xmfs_inode->btime = inode->i_mtime = inode->i_atime = + inode_set_ctime_current(inode); + dir->i_mtime = inode_set_ctime_current(dir); + unlock_new_inode(inode); + d_tmpfile(file, inode); + ihold(inode); + xa_store(&sbi->pinned_inodes, inode->i_ino, inode, GFP_KERNEL); + XMFS_I(inode)->pinned = true; + + err = finish_open_simple(file, 0); + + xm_log.index = 0; + xm_log.end_index = 0; + xm_log.ops = XMFS_CREATE; + xm_log.data_offset = dir->i_mtime.tv_sec; + xm_log.mode = mode; + xm_log.offset = dir->i_mtime.tv_nsec; + memcpy(xm_log.name, name, min(strlen(name), 255UL)); + xm_log.p_ino = parent_ino(dentry); + + unsigned long xmlt_addr = + xmfs_add_log_v3(&xm_log, sbi, + min(strlen(name), 255UL), + inode->i_ino, true); + if (xmlt_addr < 0) { + xmfs_inode->die = true; + iput(inode); + return -EINVAL; + } + + return err; +} + +static const struct inode_operations xmfs_host_dir_inode_operations = { + .create = xmfs_create, + .lookup = xmfs_lookup, + .link = xmfs_link, + .unlink = xmfs_unlink, + .symlink = xmfs_symlink, + .mkdir = xmfs_mkdir, + .rmdir = xmfs_rmdir, + .mknod = xmfs_mknod, + .rename = xmfs_rename, + .setattr = xmfs_setattr, + .tmpfile = xmfs_tmpfile, + .listxattr = xmfs_listxattr, + .getattr = xmfs_getattr, + .update_time = xmfs_update_time, +}; + +struct inode *xmfs_get_root(struct super_block *sb, int host_id) +{ + struct inode *inode; + + inode = new_inode(sb); + if (inode) { + inode->i_ino = 0; + inode->i_mode = S_IFDIR | 0755; + inode->i_uid = make_kuid(current_user_ns(), 0); + inode->i_gid = make_kgid(current_user_ns(), 0); + XMFS_I(inode)->btime = inode->i_atime = inode->i_mtime = + inode_set_ctime_current(inode); + if (host_id == 0) + xmfs_init_dir_inode(inode, true); + else + xmfs_init_dir_inode(inode, false); + + /* directory inodes start off with i_nlink == 2 (for "." entry) */ + inc_nlink(inode); + lockdep_annotate_inode_mutex_key(inode); + XMFS_I(inode)->leaf = false; + XMFS_I(inode)->i_ino = 0; + XMFS_I(inode)->tmp = false; + XMFS_I(inode)->die = false; + XMFS_I(inode)->log_start = 0; + + xmfs_simple_xattrs_init(&XMFS_I(inode)->xattrs); + + i_size_write(inode, 4096); + } + return inode; +} + +void xmfs_init_dir_inode(struct inode *inode, bool host) +{ + inode->i_op = host ? &xmfs_host_dir_inode_operations : + &xmfs_dir_inode_operations; + xmfs_init_dir_file(inode); +} diff --git a/fs/xmfs/replay.c b/fs/xmfs/replay.c new file mode 100644 index 000000000000..1d75c9780632 --- /dev/null +++ b/fs/xmfs/replay.c @@ -0,0 +1,907 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * XMFS: Log Structured Filesystem for UB + * Copyright (C) 2024. Huawei Technologies Co., Ltd + * + * This program can be distributed under the terms of the GNU GPL. + * See the file COPYING. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "xmfs_i.h" + +#include <linux/kernel.h> +#include <linux/namei.h> +#include <linux/pagemap.h> +#include <linux/slab.h> + +static struct inode *xmfs_get_inode_from_table(struct xmfs_sb_info *sbi, + unsigned long ino) +{ + struct xmfs_inode_table *tbl; + struct xmfs_inode_entry *e; + struct inode *inode; + unsigned long max_entries, i; + + inode = ilookup5(sbi->sb, ino, xmfs_inode_eq, &ino); + if (inode) + return inode; + + if (sbi->mode == XMFS_SHARED_MEM_MODE || sbi->mode == XMFS_HYBRID_MODE) + tbl = (struct xmfs_inode_table *)(sbi->kaddr + + INODE_TABLE_OFFSET); + else { + tbl = (struct xmfs_inode_table *)get_meta(sbi, + INODE_TABLE_OFFSET, + INODE_TABLE_SIZE); + /* + * tbl = (struct xmfs_inode_table *)get_meta( + * sbi, INODE_TABLE_OFFSET, + * max(sizeof(struct xmfs_inode_table) + + * tbl->count * sizeof(struct xmfs_inode_entry), + * INODE_TABLE_SIZE)); + */ + } + + if (!tbl || tbl->magic != XMFS_INODE_TABLE_MAGIC) { + if (tbl && sbi->mode == XMFS_URMA_MODE) + put_meta(sbi, tbl); + return NULL; + } + + max_entries = + (INODE_TABLE_SIZE - 4096) / sizeof(struct xmfs_inode_entry); + e = NULL; + for (i = 0; i < tbl->count && i < max_entries; i++) { + if (tbl->entries[i].i_ino == ino) { + e = &tbl->entries[i]; + break; + } + } + + if (!e) { + if (sbi->mode == XMFS_URMA_MODE) + put_meta(sbi, tbl); + return NULL; + } + + inode = iget5_locked(sbi->sb, ino, xmfs_inode_eq, xmfs_inode_set, &ino); + if (!inode) { + if (sbi->mode == XMFS_URMA_MODE) + put_meta(sbi, tbl); + return NULL; + } + + if (inode->i_state & I_NEW) { + struct xmfs_inode_info *xmfs_inode = XMFS_I(inode); + + xmfs_inode->leaf = (e->i_ino != 0); + inode->i_ino = e->i_ino; + inode->i_mode = e->mode; + set_nlink(inode, e->nlink); + i_size_write(inode, e->size); + inode->__i_ctime.tv_sec = e->ctime.tv_sec; + inode->__i_ctime.tv_nsec = e->ctime.tv_nsec; + inode->i_mtime.tv_sec = e->mtime.tv_sec; + inode->i_mtime.tv_nsec = e->mtime.tv_nsec; + inode->i_atime.tv_sec = e->atime.tv_sec; + inode->i_atime.tv_nsec = e->atime.tv_nsec; + if (S_ISREG(e->mode)) { + /* + * xmfs_inode->write_wq = alloc_workqueue( + * "xmfs-prefetch-inode", + * WQ_UNBOUND | WQ_MEM_RECLAIM, 8); + */ + xmfs_init_file_inode(inode); + } else if (S_ISDIR(e->mode)) { + xmfs_init_dir_inode(inode, sbi->id == 0); + } else if (S_ISLNK(e->mode)) { + xmfs_init_symlink_operations(inode); + } else { + init_special_inode(inode, e->mode, 0); + } + unlock_new_inode(inode); + /* iput(inode); */ + ihold(inode); + xa_store(&sbi->pinned_inodes, inode->i_ino, inode, GFP_KERNEL); + XMFS_I(inode)->pinned = true; + } + + if (sbi->mode == XMFS_URMA_MODE) + put_meta(sbi, tbl); + return inode; +} + +static int xmfs_replay_link(struct inode *dir, struct dentry *pdentry, + struct xmfs_log log, unsigned long ino) +{ + struct xmfs_sb_info *sbi = XMFS_SB(dir->i_sb); + struct inode *inode; + + if (log.writer_id == sbi->id) + return 0; + + inode = ilookup5(dir->i_sb, ino, xmfs_inode_eq, &ino); + if (inode) { + inc_nlink(inode); + iput(inode); + } + + return 0; +} + +static int xmfs_replay_mkdir(struct inode *dir, struct dentry *pdentry, + struct xmfs_log log, unsigned long replay, + unsigned long ino) +{ + struct xmfs_sb_info *sbi = XMFS_SB(dir->i_sb); + + struct inode *inode; + + if (log.writer_id == sbi->id) + return 0; + + inode = ilookup5(dir->i_sb, ino, xmfs_inode_eq, &ino); + if (!inode) { + inode = iget5_locked(dir->i_sb, ino, xmfs_inode_eq, + xmfs_inode_set, &ino); + if (inode && (inode->i_state & I_NEW)) { + struct xmfs_inode_info *xmfs_inode = XMFS_I(inode); + + inode->i_ino = ino; + inode_init_owner(&nop_mnt_idmap, inode, dir, log.mode); + xmfs_init_dir_inode(inode, false); + i_size_write(inode, log.size); + inode->__i_ctime.tv_sec = log.data_offset; + inode->__i_ctime.tv_nsec = log.offset; + xmfs_inode->btime = inode->i_mtime = inode->i_atime = + inode->__i_ctime; + unlock_new_inode(inode); + xa_store(&sbi->pinned_inodes, inode->i_ino, inode, + GFP_KERNEL); + XMFS_I(inode)->pinned = true; + } + } else { + iput(inode); + } + + return 0; +} + +static int xmfs_replay_symlink(struct inode *dir, struct dentry *pdentry, + struct xmfs_log o_log, struct xmfs_log n_log, + unsigned long symname_addr) +{ + struct xmfs_sb_info *sbi = XMFS_SB(dir->i_sb); + + struct inode *inode; + + if (o_log.writer_id == sbi->id) + return 0; + + inode = ilookup5(dir->i_sb, o_log.i_ino, xmfs_inode_eq, &o_log.i_ino); + if (!inode) { + inode = iget5_locked(dir->i_sb, o_log.i_ino, xmfs_inode_eq, + xmfs_inode_set, &o_log.i_ino); + if (inode && (inode->i_state & I_NEW)) { + struct xmfs_inode_info *xmfs_inode = XMFS_I(inode); + + xmfs_inode->symname = symname_addr; + inode->i_ino = o_log.i_ino; + inode_init_owner(&nop_mnt_idmap, inode, dir, + S_IFLNK | 0777); + xmfs_init_symlink_mapping(inode); + i_size_write(inode, o_log.size); + inode->__i_ctime.tv_sec = o_log.data_offset; + inode->__i_ctime.tv_nsec = o_log.offset; + xmfs_inode->btime = inode->i_mtime = inode->i_atime = + inode->__i_ctime; + unlock_new_inode(inode); + xa_store(&sbi->pinned_inodes, inode->i_ino, inode, + GFP_KERNEL); + XMFS_I(inode)->pinned = true; + } + } else { + iput(inode); + } + + return 0; +} + +static int xmfs_replay_rename(struct inode *dir, struct dentry *pdentry, + struct xmfs_log o_log, struct xmfs_log n_log) +{ + struct xmfs_sb_info *sbi = XMFS_SB(dir->i_sb); + + if (o_log.writer_id == sbi->id) + return 0; + + { + struct inode *old_parent = ilookup5(sbi->sb, o_log.p_ino, + xmfs_inode_eq, + &o_log.p_ino); + if (old_parent) { + struct dentry *pd = d_find_alias(old_parent); + + if (pd) { + struct qstr qname = + QSTR_INIT(o_log.name, + strlen(o_log.name)); + struct dentry *d = d_lookup(pd, &qname); + + if (d) { + d_drop(d); + dput(d); + } + dput(pd); + } + iput(old_parent); + } + } + return 0; +} + +static int xmfs_replay_create(struct inode *dir, struct dentry *pdentry, + struct xmfs_log log, unsigned long replay, + unsigned long ino) +{ + struct xmfs_sb_info *sbi = XMFS_SB(dir->i_sb); + + struct inode *inode; + /* + * xmfs_info(sbi->sb, "qyf replay create before check %ld %d %s", + * log.writer_id, sbi->id, log.name); + */ + if (log.writer_id == sbi->id) + return 0; + /* + * xmfs_info(sbi->sb, "qyf replay create %ld %s size %lld", + * log.i_ino, log.name, log.size); + */ + inode = ilookup5(dir->i_sb, ino, xmfs_inode_eq, &ino); + if (!inode) { + inode = iget5_locked(dir->i_sb, ino, xmfs_inode_eq, + xmfs_inode_set, &ino); + if (inode && (inode->i_state & I_NEW)) { + struct xmfs_inode_info *xmfs_inode = XMFS_I(inode); + + inode->i_ino = ino; + inode_init_owner(&nop_mnt_idmap, inode, dir, log.mode); + if (log.index == 1) + init_special_inode(inode, log.mode, + log.end_index); + else if (S_ISREG(log.mode)) { + /* + * xmfs_inode->write_wq = alloc_workqueue( + * "xmfs-prefetch-inode", + * WQ_UNBOUND | WQ_MEM_RECLAIM, 8); + */ + xmfs_init_file_inode(inode); + } else if (S_ISDIR(log.mode)) { + xmfs_init_dir_inode(inode, false); + } + i_size_write(inode, log.size); + inode->__i_ctime.tv_sec = log.data_offset; + inode->__i_ctime.tv_nsec = log.offset; + xmfs_inode->btime = inode->i_mtime = inode->i_atime = + inode->__i_ctime; + unlock_new_inode(inode); + xa_store(&sbi->pinned_inodes, inode->i_ino, inode, + GFP_KERNEL); + XMFS_I(inode)->pinned = true; + } + } else { + iput(inode); + } + + return 0; +} + +static int xmfs_replay_delete(struct inode *dir, struct dentry *pdentry, + struct xmfs_log log) +{ + struct xmfs_sb_info *sbi = XMFS_SB(dir->i_sb); + + if (log.writer_id == sbi->id) + return 0; + + { + struct inode *parent = + ilookup5(sbi->sb, log.p_ino, xmfs_inode_eq, &log.p_ino); + if (parent) { + struct dentry *pd = d_find_alias(parent); + + if (pd) { + struct qstr qname = + QSTR_INIT(log.name, strlen(log.name)); + struct dentry *d = d_lookup(pd, &qname); + + if (d) { + d_drop(d); + dput(d); + } + dput(pd); + } + iput(parent); + } + } + struct inode *inode; + + inode = ilookup5(sbi->sb, log.i_ino, xmfs_inode_eq, &log.i_ino); + if (!inode) + return 0; + if (inode->i_nlink > 0) + drop_nlink(inode); + if (!inode->i_nlink && XMFS_I(inode)->pinned) { + XMFS_I(inode)->pinned = false; + unsigned long _idx; + void *_entry; + + xa_for_each(&XMFS_I(inode)->shared_pages, _idx, _entry) { + xmfs_mark_page_dead(sbi, (unsigned long)_entry); + } + xa_erase(&sbi->pinned_inodes, inode->i_ino); + inode->i_state &= ~I_DIRTY_TIME; + /* Release pin reference. */ + iput(inode); + } + iput(inode); + return 0; +} + +static int xmfs_replay_update(struct inode *dir, struct dentry *pdentry, + struct xmfs_log log, unsigned long *data_addr, + unsigned long ino) +{ + void *ret; + unsigned long index = log.index; + unsigned long end_index = log.end_index; + unsigned long offset = log.data_offset; + + struct xmfs_sb_info *sbi = XMFS_SB(dir->i_sb); + + if (log.writer_id == sbi->id) + return 0; + + struct inode *inode = xmfs_get_inode_from_table(sbi, ino); + + if (!inode) { + xmfs_info(sbi->sb, + "cannot find inode in icache and inode table %ld", + ino); + return -ENOENT; + } + struct xmfs_inode_info *xmfs_inode = XMFS_I(inode); + + int err = 0; + int chunk_header_cnt = 0; + unsigned long i = 0; + unsigned long page_off; + /* + * if (!sbi->always_hot) { + * invalidate_inode_pages2_range(inode->i_mapping, index, + * end_index - 1); + * truncate_pagecache_range(inode, index << PAGE_SHIFT, + * (end_index - 1) << PAGE_SHIFT); + * } + */ + /* + * if (sbi->mode == 0) + * arch_invalidate_pmem(sbi->kaddr + offset, + * (end_index - index + 1) * PAGE_SIZE); + */ + /* + * xmfs_info(sbi->sb, "replay update index %ld to %ld", + * index, end_index); + */ + for (i = index; i < end_index; i++) { + page_off = offset + (i - index) * PAGE_SIZE + + chunk_header_cnt * PAGE_SIZE; + if (page_off % XMFS_DEFAULT_CHUNK_SIZE == 0) { + chunk_header_cnt++; + page_off = offset + (i - index) * PAGE_SIZE + + chunk_header_cnt * PAGE_SIZE; + } + /* + * xmfs_info(sbi->sb, "replay update index %ld offset %ld", + * i, page_off); + */ + ret = xa_store(&xmfs_inode->shared_pages, i, (void *)page_off, + GFP_KERNEL); + if (!xa_err(ret)) { + inode->i_blocks++; + if (sbi->id == 0) { + xmfs_mark_page_dead(sbi, (unsigned long)ret); + xmfs_mark_page_live(sbi, page_off); + unsigned long trunk_off = + log.data_offset & + ~(sbi->trunk_size - 1); + struct xmfs_data_trunk_header *hdr = + (void *)(sbi->kaddr + trunk_off); + if (hdr->magic == XMFS_DATA_TRUNK_MAGIC && + log.version > hdr->last_modify_version) + hdr->last_modify_version = log.version; + } + } else { + xmfs_info(sbi->sb, "replay cannot store %lu %lu", i, + page_off); + } + if (sbi->always_hot) { + struct folio *folio = + filemap_grab_folio(inode->i_mapping, i); + if (IS_ERR(folio)) { + err = -ENOMEM; + goto done; + } + char *kaddr = kmap_local_page(folio_page(folio, 0)); + + get_data_and_copy(sbi, offset + (i - index) * PAGE_SIZE, + kaddr, PAGE_SIZE, NULL, 0); + kunmap_local(kaddr); + folio_mark_uptodate(folio); + folio_unlock(folio); + folio_put(folio); + } + } + +done: + + if (log.size > i_size_read(inode)) + i_size_write(inode, log.size); + + /* return start of data trunk */ + *data_addr = (offset + (end_index - index) * PAGE_SIZE + + (sbi->trunk_size - 1)) & + ~(sbi->trunk_size - 1); + + iput(inode); + + return err; +} + +static int xmfs_replay_xattr(struct inode *dir, struct dentry *pdentry, + struct xmfs_log xm_log, unsigned long ino) +{ + struct inode *inode; + struct xmfs_sb_info *sbi = XMFS_SB(dir->i_sb); + + if (xm_log.writer_id == sbi->id) + return 0; + + if (ino != 0) + inode = xmfs_get_inode_from_table(XMFS_SB(dir->i_sb), ino); + else + inode = dir; + if (!inode) { + xmfs_info(sbi->sb, "xattr cannot find inode xattr %ld", ino); + return -ENOENT; + } + struct xmfs_inode_info *xmfs_inode = XMFS_I(inode); + + size_t size = xm_log.index; + size_t len = xm_log.end_index; + int flags = xm_log.size; + unsigned long offset1 = xm_log.data_offset; + + char *name = kmalloc(len + 1, GFP_KERNEL); + + int err = copy_mc_to_kernel(name, xm_log.name, len); + + if (unlikely(err)) { + xmfs_error(sbi->sb, + "hardware memory error when reading superblock during slave xattr copy name."); + kfree(name); + if (ino != 0) + iput(inode); + goto set_replay; + } + name[len] = '\0'; + if (flags != 0) { + struct simple_xattr *old_xattr = + xmfs_simple_xattr_set(&xmfs_inode->xattrs, name, NULL, + size, flags); + if (!IS_ERR(old_xattr)) { + xmfs_simple_xattr_free(old_xattr); + old_xattr = NULL; + inode->__i_ctime.tv_sec = xm_log.p_ino; + inode->__i_ctime.tv_nsec = xm_log.offset; + } + kfree(name); + if (ino != 0) + iput(inode); + goto set_replay; + } + + char *value = kmalloc(size, GFP_KERNEL); + + err = get_data_and_copy(sbi, offset1, value, size, NULL, 0); + if (unlikely(err)) { + xmfs_error(sbi->sb, + "hardware memory error when reading superblock during slave xattr copy value."); + kfree(name); + kfree(value); + if (ino != 0) + iput(inode); + goto set_replay; + } + + struct simple_xattr *old_xattr = + xmfs_simple_xattr_set(&xmfs_inode->xattrs, name, (void *)value, + size, flags); + if (!IS_ERR(old_xattr)) { + xmfs_simple_xattr_free(old_xattr); + old_xattr = NULL; + inode->__i_ctime.tv_sec = xm_log.data_offset; + inode->__i_ctime.tv_nsec = xm_log.offset; + } + kfree(name); + kfree(value); + if (ino != 0) + iput(inode); + +set_replay: + if (sbi->id == 0) { + xmfs_inode->log_start = sbi->log_start & ~(sbi->log_size - 1); + sbi->log_current = sbi->log_start; + } + return 0; +} + +static int xmfs_replay_root(struct inode *dir, struct dentry *pdentry, + struct xmfs_log xm_log, unsigned long ino) +{ + return 0; +} + +int xmfs_replay_new_entries(struct xmfs_sb_info *sbi, struct dentry *dentry, + unsigned long last_update) +{ + struct super_block *sb = sbi->sb; + struct xmfs_sb_index *xmsi; + struct xmfs_log_trunk *xmlt; + struct inode *dir; + unsigned long data_addr; + unsigned long trunk_offset; + unsigned long start_slot; + unsigned long target; + int err = 0; + struct xmfs_log *tmp_log; + + if (!mutex_trylock(&sbi->replay_lock)) + return 0; + + xmsi = kmalloc(sizeof(*xmsi), GFP_KERNEL); + err = get_xmsi_and_copy(sbi, xmsi); + if (unlikely(err)) { + kfree(xmsi); + mutex_unlock(&sbi->replay_lock); + return err; + } + + if (!dentry) + dentry = sb->s_root; + dir = dentry->d_inode; + data_addr = sbi->addr; + tmp_log = kmalloc(sizeof(struct xmfs_log), GFP_KERNEL); + + if (sbi->last_update >= xmsi->last_update) { + kfree(xmsi); + kfree(tmp_log); + mutex_unlock(&sbi->replay_lock); + return 0; + } + + target = (last_update != -1) ? last_update : xmsi->last_update; + if (target > xmsi->last_update) + target = xmsi->last_update; + if (target < sbi->last_update) { + sbi->last_update = target; + kfree(tmp_log); + mutex_unlock(&sbi->replay_lock); + return 0; + } + + if (sbi->last_replayed_slot == (unsigned long)-1) { + trunk_offset = xmsi->log_start; + start_slot = 0; + sbi->log_start = xmsi->log_start; + } else if (sbi->last_replayed_slot < MAX_LOG_PER_TRUNK) { + trunk_offset = sbi->log_start; + start_slot = sbi->last_replayed_slot + 1; + } else { + struct xmfs_log_trunk *cur = + get_log_trunk_meta(sbi, sbi->log_start); + trunk_offset = (cur && cur->next_trunk) ? cur->next_trunk : 0; + if (cur) + put_log_trunk(sbi, cur); + sbi->log_start = trunk_offset; + start_slot = 0; + sbi->last_replayed_slot = (unsigned long)-1; + } + kfree(xmsi); + + down_read(&sbi->chain_rwsem); + + while (trunk_offset != 0) { + unsigned long next_trunk; + unsigned long i; + int no_ops = 0; + + xmlt = get_log_trunk(sbi, trunk_offset); + if (!xmlt) + break; + + next_trunk = xmlt->next_trunk; + if (xmlt->version_base > target) { + put_log_trunk(sbi, xmlt); + break; + } + /* put_log_trunk(sbi, xmlt); */ + /* xmlt = get_log_trunk_data(sbi, trunk_offset, xmlt->used); */ + + for (i = start_slot; i < xmlt->used; i++) { + struct xmfs_log *replay_log = &xmlt->log[i]; + + switch (replay_log->ops) { + case XMFS_CREATE: + err = xmfs_replay_create(dir, dentry, + *replay_log, 0, + replay_log->i_ino); + break; + case XMFS_DELETE: + err = xmfs_replay_delete(dir, dentry, + *replay_log); + break; + case XMFS_UPDATE: + err = xmfs_replay_update(dir, dentry, + *replay_log, + &data_addr, + replay_log->i_ino); + break; + case XMFS_LINK: + err = xmfs_replay_link(dir, dentry, *replay_log, + replay_log->i_ino); + break; + case XMFS_MKDIR: + err = xmfs_replay_mkdir(dir, dentry, + *replay_log, 0, + replay_log->i_ino); + break; + case XMFS_RENAME: { + if (i + 1 < xmlt->used) { + *tmp_log = xmlt->log[i + 1]; + } else if (xmlt->next_trunk != 0) { + struct xmfs_log_trunk *next_xmlt = + get_log_trunk_meta(sbi, + xmlt->next_trunk); + if (next_xmlt && next_xmlt->used > 0) { + *tmp_log = next_xmlt->log[0]; + put_log_trunk(sbi, next_xmlt); + } else if (next_xmlt) { + put_log_trunk(sbi, next_xmlt); + break; + } else { + break; + } + } else { + break; + } + err = xmfs_replay_rename(dir, dentry, + *replay_log, *tmp_log); + i++; + break; + } + case XMFS_SYMLINK: { + if (i + 1 < xmlt->used) { + *tmp_log = xmlt->log[i + 1]; + } else if (xmlt->next_trunk != 0) { + struct xmfs_log_trunk *next_xmlt = + get_log_trunk_meta(sbi, + xmlt->next_trunk); + if (next_xmlt && next_xmlt->used > 0) { + *tmp_log = next_xmlt->log[0]; + put_log_trunk(sbi, next_xmlt); + } else if (next_xmlt) { + put_log_trunk(sbi, next_xmlt); + break; + } else { + break; + } + } else { + break; + } + { + unsigned long symname_addr; + + if (i + 1 < xmlt->used) + symname_addr = + trunk_offset + + offsetof(struct xmfs_log_trunk, + log[0]) + + (i + + 1) * sizeof(struct xmfs_log); + else + symname_addr = + xmlt->next_trunk + + offsetof(struct xmfs_log_trunk, + log[0]); + err = xmfs_replay_symlink(dir, dentry, + *replay_log, + *tmp_log, + symname_addr); + } + i++; + break; + } + case XMFS_SETATTR: + break; + case XMFS_XATTR: + err = xmfs_replay_xattr(dir, dentry, + *replay_log, + replay_log->i_ino); + break; + case XMFS_ROOT: + err = xmfs_replay_root(dir, dentry, *replay_log, + replay_log->i_ino); + break; + default: + no_ops = 1; + sbi->skip_no_ops++; + /* + * xmfs_error(sbi->sb, + * "qyf xmfs v2: unknown ops %d index %d %d", + * replay_log->ops, i, + * sbi->last_replayed_slot); + */ + if (sbi->skip_no_ops >= 50) { + sbi->skip_no_ops = 0; + xmfs_error(sbi->sb, + "replay fault: unknown ops %d, tried 50 times. This may happened when writing logs is too slow", + replay_log->ops); + } else { + target = replay_log->version; + goto done; + } + } + + if (err != 0) { + xmfs_error(sbi->sb, + "slave %d v2 play log ops %d err %d", + sbi->id, replay_log->ops, -err); + target = replay_log->version; + goto done; + } + err = 0; + sbi->last_replayed_slot = i; + if (replay_log->version > target) { + put_log_trunk(sbi, xmlt); + goto done; + } + /* + * xmfs_info(sbi->sb, + * "qyf replay sbi %d trunk %d replay ops %d " + * "name %s slot %d version %d owner %d " + * "total used %d ino %d index %d " + * "endindex %d size %d", + * sbi->id, xmlt->version_base, replay_log->ops, + * replay_log->name, sbi->last_replayed_slot, + * replay_log->version, replay_log->writer_id, + * xmlt->used, replay_log->i_ino, + * replay_log->index, replay_log->end_index, + * replay_log->size); + */ + } + + /* xmfs_info(sbi->sb, "qyf need to go to next trunk"); */ + start_slot = 0; + if (next_trunk != 0) { + sbi->log_start = next_trunk; + sbi->last_replayed_slot = (unsigned long)-1; + } + put_log_trunk(sbi, xmlt); + trunk_offset = next_trunk; + } + +done: + up_read(&sbi->chain_rwsem); + sbi->last_update = target; + if (sbi->id != 0) { + if (sbi->mode != XMFS_URMA_MODE) { + struct mount_msg_info *mm_info = + sbi->kaddr + MOUNT_MSG_OFFSET; + mm_info->slave_versions[sbi->id] = sbi->last_update; + } else { + unsigned long ver = sbi->last_update; + + xmfs_pmem_write(sbi, + MOUNT_MSG_OFFSET + + offsetof(struct mount_msg_info, + slave_versions) + + sbi->id * sizeof(unsigned long), + &ver, sizeof(ver)); + } + } + + kfree(tmp_log); + mutex_unlock(&sbi->replay_lock); + return 0; +} + +void xmfs_populate_from_inode_table(struct xmfs_sb_info *sbi) +{ + struct xmfs_inode_table *tbl; + struct inode *inode; + unsigned long i; + unsigned long max_entries; + + xmfs_info(sbi->sb, "xmfs_populate_from_inode_table"); + if (sbi->mode == XMFS_SHARED_MEM_MODE || sbi->mode == XMFS_HYBRID_MODE) + tbl = (struct xmfs_inode_table *)(sbi->kaddr + + INODE_TABLE_OFFSET); + else + tbl = (struct xmfs_inode_table *)get_meta(sbi, + INODE_TABLE_OFFSET, + INODE_TABLE_SIZE); + + if (!tbl || tbl->magic != XMFS_INODE_TABLE_MAGIC) { + xmfs_info(sbi->sb, "no valid inode table, skipping populate"); + if (tbl && sbi->mode == XMFS_URMA_MODE) + put_meta(sbi, tbl); + return; + } + + max_entries = + (INODE_TABLE_SIZE - 4096) / sizeof(struct xmfs_inode_entry); + xmfs_info(sbi->sb, "populating from inode table, %lu entries", + tbl->count); + + for (i = 0; i < tbl->count && i < max_entries; i++) { + struct xmfs_inode_entry *e = &tbl->entries[i]; + + if (e->i_ino == 0) + continue; + + inode = ilookup5(sbi->sb, e->i_ino, xmfs_inode_eq, &e->i_ino); + if (inode) { + iput(inode); + continue; + } + + inode = iget5_locked(sbi->sb, e->i_ino, xmfs_inode_eq, + xmfs_inode_set, &e->i_ino); + if (!inode) + continue; + + if (inode->i_state & I_NEW) { + struct xmfs_inode_info *xmfs_inode = XMFS_I(inode); + + xmfs_inode->leaf = (e->i_ino != 0); + inode->i_ino = e->i_ino; + inode->i_mode = e->mode; + set_nlink(inode, e->nlink); + i_size_write(inode, e->size); + if (S_ISREG(e->mode)) { + /* + * xmfs_inode->write_wq = alloc_workqueue( + * "xmfs-prefetch-inode", + * WQ_UNBOUND | WQ_MEM_RECLAIM, 8); + */ + xmfs_init_file_inode(inode); + } else if (S_ISDIR(e->mode)) { + xmfs_init_dir_inode(inode, sbi->id == 0); + } else if (S_ISLNK(e->mode)) { + xmfs_init_symlink_operations(inode); + } else { + init_special_inode(inode, e->mode, 0); + } + unlock_new_inode(inode); + xa_store(&sbi->pinned_inodes, inode->i_ino, inode, + GFP_KERNEL); + XMFS_I(inode)->pinned = true; + } + } + + if (sbi->mode == XMFS_URMA_MODE) + put_meta(sbi, tbl); +} diff --git a/fs/xmfs/super.c b/fs/xmfs/super.c new file mode 100644 index 000000000000..f048fa8844ab --- /dev/null +++ b/fs/xmfs/super.c @@ -0,0 +1,1677 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * XMFS: Log Structured Filesystem for UB + * Copyright (C) 2026. Huawei Technologies Co., Ltd + * + * This program can be distributed under the terms of the GNU GPL. + * See the file COPYING. + */ + +#include "xmfs_i.h" + +#include <linux/blkdev.h> +#include <linux/dax.h> +#include <linux/file.h> +#include <linux/fs_context.h> +#include <linux/fs_parser.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/kthread.h> +/* open CONFIG_ZONE_DEVICE */ +#include <linux/memremap.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/mount.h> +#include <linux/namei.h> +#include <linux/pagemap.h> +#include <linux/ratelimit.h> +#include <linux/sched.h> +#include <linux/seq_file.h> +#include <linux/slab.h> +#include <linux/statfs.h> +#include <linux/swap.h> +#include <linux/uio.h> + +#include "../../drivers/dax/dax-private.h" +#include "../../drivers/nvdimm/pmem.h" + +#include <linux/cacheflush.h> +#include <asm/set_memory.h> +#include <asm/tlbflush.h> + +static struct kmem_cache *xmfs_inode_cachep; + +#define XMFS_RATELIMIT_INTERVAL (5 * HZ) +#define XMFS_RATELIMIT_BURST 10 + +void xmfs_init_log_ratelimits(struct xmfs_sb_info *sbi) +{ + ratelimit_state_init(&sbi->s_error_ratelimit_state, + XMFS_RATELIMIT_INTERVAL, XMFS_RATELIMIT_BURST); + ratelimit_state_init(&sbi->s_warning_ratelimit_state, + XMFS_RATELIMIT_INTERVAL, XMFS_RATELIMIT_BURST); + ratelimit_state_init(&sbi->s_msg_ratelimit_state, + XMFS_RATELIMIT_INTERVAL, XMFS_RATELIMIT_BURST); + atomic_set(&sbi->s_error_count, 0); + atomic_set(&sbi->s_warning_count, 0); + atomic_set(&sbi->s_msg_count, 0); +} + +static bool xmfs_ratelimit(struct super_block *sb, + struct ratelimit_state *state, atomic_t *count, + const char *name) +{ + if (!sb || !XMFS_SB(sb)) + return true; + + atomic_inc(count); + return ___ratelimit(state, name); +} + +void __xmfs_msg(struct super_block *sb, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + int level; + + if (sb && XMFS_SB(sb) && + !xmfs_ratelimit(sb, &XMFS_SB(sb)->s_msg_ratelimit_state, + &XMFS_SB(sb)->s_msg_count, "XMFS-fs")) + return; + + level = printk_get_level(fmt); + + va_start(args, fmt); + vaf.fmt = printk_skip_level(fmt); + vaf.va = &args; + if (sb) + printk("%c%cXMFS-fs (%s): %pV\n", KERN_SOH_ASCII, level, sb->s_id, &vaf); + else + printk("%c%cXMFS-fs: %pV\n", KERN_SOH_ASCII, level, &vaf); + va_end(args); +} + +void __xmfs_error(struct super_block *sb, const char *function, + unsigned int line, int error, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + if (sb && XMFS_SB(sb) && + !xmfs_ratelimit(sb, &XMFS_SB(sb)->s_error_ratelimit_state, + &XMFS_SB(sb)->s_error_count, "XMFS-fs error")) + return; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + if (sb) { + if (error) + printk(KERN_ERR "XMFS-fs error (%s): %s:%u: comm %s: error %d: %pV\n", + sb->s_id, function, line, current->comm, error, &vaf); + else + printk(KERN_ERR "XMFS-fs error (%s): %s:%u: comm %s: %pV\n", + sb->s_id, function, line, current->comm, &vaf); + } else if (error) { + printk(KERN_ERR "XMFS-fs error: %s:%u: comm %s: error %d: %pV\n", + function, line, current->comm, error, &vaf); + } else { + printk(KERN_ERR "XMFS-fs error: %s:%u: comm %s: %pV\n", + function, line, current->comm, &vaf); + } + va_end(args); +} + +void __xmfs_error_inode(struct inode *inode, const char *function, + unsigned int line, int error, const char *fmt, ...) +{ + struct super_block *sb = inode->i_sb; + struct va_format vaf; + va_list args; + + if (XMFS_SB(sb) && + !xmfs_ratelimit(sb, &XMFS_SB(sb)->s_error_ratelimit_state, + &XMFS_SB(sb)->s_error_count, "XMFS-fs error")) + return; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + if (error) + printk(KERN_ERR "XMFS-fs error (%s): %s:%u: inode #%lu: comm %s: error %d: %pV\n", + sb->s_id, function, line, inode->i_ino, current->comm, error, &vaf); + else + printk(KERN_ERR "XMFS-fs error (%s): %s:%u: inode #%lu: comm %s: %pV\n", + sb->s_id, function, line, inode->i_ino, current->comm, &vaf); + va_end(args); +} + +void __xmfs_error_file(struct file *file, const char *function, + unsigned int line, int error, const char *fmt, ...) +{ + struct inode *inode = file_inode(file); + struct super_block *sb = inode->i_sb; + char pathname[80]; + struct va_format vaf; + const char *path; + va_list args; + + if (XMFS_SB(sb) && + !xmfs_ratelimit(sb, &XMFS_SB(sb)->s_error_ratelimit_state, + &XMFS_SB(sb)->s_error_count, "XMFS-fs error")) + return; + + path = file_path(file, pathname, sizeof(pathname)); + if (IS_ERR(path)) + path = "(unknown)"; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + if (error) + printk(KERN_ERR "XMFS-fs error (%s): %s:%u: inode #%lu: comm %s: path %s: error %d: %pV\n", + sb->s_id, function, line, inode->i_ino, current->comm, path, + error, &vaf); + else + printk(KERN_ERR "XMFS-fs error (%s): %s:%u: inode #%lu: comm %s: path %s: %pV\n", + sb->s_id, function, line, inode->i_ino, current->comm, path, &vaf); + va_end(args); +} + +void __xmfs_warning(struct super_block *sb, const char *function, + unsigned int line, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + if (sb && XMFS_SB(sb) && + !xmfs_ratelimit(sb, &XMFS_SB(sb)->s_warning_ratelimit_state, + &XMFS_SB(sb)->s_warning_count, "XMFS-fs warning")) + return; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + if (sb) + printk(KERN_WARNING "XMFS-fs warning (%s): %s:%u: %pV\n", + sb->s_id, function, line, &vaf); + else + printk(KERN_WARNING "XMFS-fs warning: %s:%u: %pV\n", + function, line, &vaf); + va_end(args); +} + +void __xmfs_warning_inode(const struct inode *inode, const char *function, + unsigned int line, const char *fmt, ...) +{ + struct super_block *sb = inode->i_sb; + struct va_format vaf; + va_list args; + + if (XMFS_SB(sb) && + !xmfs_ratelimit(sb, &XMFS_SB(sb)->s_warning_ratelimit_state, + &XMFS_SB(sb)->s_warning_count, "XMFS-fs warning")) + return; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + printk(KERN_WARNING "XMFS-fs warning (%s): %s:%u: inode #%lu: comm %s: %pV\n", + sb->s_id, function, line, inode->i_ino, current->comm, &vaf); + va_end(args); +} + +#if defined(__arm__) || defined(__aarch64__) +static int change_memory_cache(pte_t *ptep, unsigned long addr, void *data) +{ + pte_t pte = READ_ONCE(*ptep); + pte_t newpte; + bool cacheable = *(bool *)data; + + if (cacheable) + pte_val(newpte) = (pte_val(pte) & ~PTE_ATTRINDX_MASK) | + PTE_ATTRINDX(MT_NORMAL); + else + pte_val(newpte) = (pte_val(pte) & ~PTE_ATTRINDX_MASK) | + PTE_ATTRINDX(MT_NORMAL_NC); + + if (pte_val(pte) == pte_val(newpte)) + return 0; + + pte_clear(current->mm, addr, ptep); + flush_tlb_kernel_range(addr, addr + 4096); + set_pte(ptep, newpte); + + return 0; +} + +static void set_memory_noncacheable(unsigned long addr, size_t size) +{ + bool cache = false; + int ret = apply_to_page_range(current->mm, addr, size, + change_memory_cache, &cache); + if (ret) + xmfs_error(NULL, "apply noncache for write err %d", ret); +} +#endif + +static struct inode *xmfs_alloc_inode(struct super_block *sb) +{ + struct xmfs_inode_info *p; + + p = alloc_inode_sb(sb, xmfs_inode_cachep, GFP_KERNEL); + if (unlikely(!p)) + return NULL; + return &p->vfs_inode; +} + +static void xmfs_destroy_inode(struct inode *inode) +{ + struct xmfs_inode_info *xmfs_inode = XMFS_I(inode); + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + + if (xmfs_inode == NULL) + return; + + xmfs_simple_xattrs_free(&xmfs_inode->xattrs, NULL); + + if (!(xmfs_inode->leaf)) + return; + + sbi->used_ino--; + if (xmfs_inode->write_wq) { + flush_workqueue(xmfs_inode->write_wq); + destroy_workqueue(xmfs_inode->write_wq); + xmfs_inode->write_wq = NULL; + } + /* + * if (sbi->id == 0) { + * unsigned long _idx; + * void *_entry; + * + * xa_for_each(&xmfs_inode->shared_pages, _idx, _entry) { + * xmfs_info(sbi->sb, "destroy inode mark dead %d", _idx); + * xmfs_mark_page_dead(sbi, (unsigned long)_entry); + * } + * } + */ + xa_destroy(&xmfs_inode->shared_pages); + + if (sbi->always_hot || sbi->freq == 0) + return; + + unsigned long i; + void *ptr; + struct page_freq *page_freq; + + xa_for_each(&xmfs_inode->page_freq, i, ptr) { + page_freq = (struct page_freq *)ptr; + xa_erase(&xmfs_inode->page_freq, i); + kfree(page_freq); + } + xa_destroy(&xmfs_inode->page_freq); +} + +static void xmfs_free_inode(struct inode *inode) +{ + kmem_cache_free(xmfs_inode_cachep, XMFS_I(inode)); +} + +static int xmfs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + struct xmfs_sb_info *sbi = XMFS_SB(dentry->d_sb); + struct xmfs_sb_index *xmsi = get_xmsi(sbi); + + if (!xmsi) { + xmfs_error(NULL, + "hardware memory error when reading superblock during statfs."); + return 0; + } + sbi->stop_cnt++; + if (xmsi->last_update != sbi->last_update) { + if (!sbi->debug) + xmfs_replay_new_entries(sbi, NULL, xmsi->last_update); + else + xmfs_replay_new_entries(sbi, NULL, xmsi->last_update); + } + sbi->stop_cnt--; + + buf->f_type = XMFS_MAGIC; + buf->f_bsize = sbi->trunk_size; + buf->f_namelen = NAME_MAX; + buf->f_blocks = sbi->max_chunk + DATA_AREA_OFFSET / sbi->trunk_size + 1; + buf->f_bfree = sbi->max_chunk - xmsi->used_trunk_count; + buf->f_bavail = sbi->max_chunk - xmsi->used_trunk_count; + if (xmsi->used_trunk_count > sbi->max_chunk) { + buf->f_bfree = 0; + buf->f_bavail = 0; + } + buf->f_files = sbi->max_chunk * (sbi->trunk_size / sbi->log_size); + buf->f_ffree = sbi->max_chunk * (sbi->trunk_size / sbi->log_size) - + sbi->used_ino; + if (sbi->id == 0) + buf->f_fsid = uuid_to_fsid(dentry->d_sb->s_uuid.b); + else + buf->f_fsid = xmsi->fsid; + put_xmsi(sbi, xmsi); + return 0; +} + +static int xmfs_show_options(struct seq_file *seq, struct dentry *root) +{ + struct xmfs_sb_info *sbi = XMFS_SB(root->d_sb); + +#ifdef CONFIG_XMFS_FS_URMA + if (!sbi->id) + seq_printf(seq, + ",id=%d,log_size=%d,size=%ld,addr=%lld,mode=%d,urma_dev_name=%s,urma_va=%llu,urma_eid_subnet_prefix=%llu, urma_eid_interface_id=%llu,urma_token_id=%u,urma_jetty_id=%u", + sbi->id, sbi->log_size, sbi->len, sbi->start, + sbi->mode, sbi->dev_name, + sbi->urma_cfg.urma_remote_va, + sbi->urma_cfg.urma_eid_subnet_prefix, + sbi->urma_cfg.urma_eid_interface_id, + sbi->urma_cfg.urma_token_id, + sbi->urma_cfg.urma_jetty_id); + else + seq_printf(seq, + ",id=%d,log_size=%d,size=%ld,addr=%lld,mode=%d,urma_dev_name=%s,urma_va=%llu,urma_eid_subnet_prefix=%llu, urma_eid_interface_id=%llu,urma_token_id=%u,urma_jetty_id=%u", + sbi->id, sbi->log_size, sbi->len, sbi->start, + sbi->mode, sbi->dev_name, + sbi->urma_cfg.own_info.urma_remote_va, + sbi->urma_cfg.own_info.urma_eid_subnet_prefix, + sbi->urma_cfg.own_info.urma_eid_interface_id, + sbi->urma_cfg.own_info.urma_token_id, + sbi->urma_cfg.own_info.urma_jetty_id); +#else + if (!sbi->id) + seq_printf(seq, ",id=%d,log_size=%d,size=%ld,addr=%lld,mode=%d", + sbi->id, sbi->log_size, sbi->len, sbi->start, + sbi->mode); + else + seq_printf(seq, ",id=%d,size=%ld,addr=%lld,mode=%d", sbi->id, + sbi->len, sbi->start, sbi->mode); +#endif + return 0; +} + +static int xmfs_syncfs(struct super_block *sb, int wait) +{ + return 0; +} + +static int xmfs_write_inode(struct inode *inode, struct writeback_control *wbc) +{ + return 0; +} + +static void xmfs_evict_inode(struct inode *inode) +{ + truncate_inode_pages_final(&inode->i_data); + clear_inode(inode); +} + +static const struct super_operations xmfs_ops = { + .alloc_inode = xmfs_alloc_inode, + .free_inode = xmfs_free_inode, + .destroy_inode = xmfs_destroy_inode, + .statfs = xmfs_statfs, + .show_options = xmfs_show_options, + .sync_fs = xmfs_syncfs, + .write_inode = xmfs_write_inode, + .drop_inode = generic_drop_inode, + .evict_inode = xmfs_evict_inode, +}; + +static int xmfs_find_index(struct xmfs_sb_info *sbi, + struct mount_msg_info *mm_info, bool mount_fn) +{ + int i; + + for (i = 1; i < 256; i++) { + if (mount_fn && mm_info->msg[i] == 'T') { + return i; + } else if (!mount_fn && mm_info->msg[i] != 'D' && + mm_info->msg[i] != 'T') { + /* mm_info_real->msg[i] = 'T'; */ + xmfs_info(NULL, + "------> %s write index %d", __func__, i); + mnt_msg_write(sbi, i, 'T'); + return i; + } + } + /* + * if (mount_fn && mm_info->msg[i - 1] != 'D') + * return -2; + */ + return -1; +} + +static int xmfs_host_mount_fn(void *data) +{ + struct xmfs_sb_info *sbi = data; + struct mount_msg_info *mm_info = + kmalloc(sizeof(struct mount_msg_info), GFP_KERNEL); + struct mount_msg_info *mm_info_real = get_mnt_msg(sbi); + + int id = 0; + int new, i, index; + int err; + + atomic_set(&mm_info_real->id, 0); + while (!kthread_should_stop()) { + err = copy_mc_to_kernel(mm_info, get_mnt_msg(sbi), + sizeof(struct mount_msg_info)); + if (unlikely(err)) { + xmfs_error(NULL, + "hardware memory error when host checking mount msgs"); + kfree(mm_info); + return err; + } + if (mm_info_real->msg[0] == 'T') + mm_info_real->msg[0] = 'D'; + + new = atomic_read(&mm_info->id); + if (new == id) { + schedule_timeout_interruptible(msecs_to_jiffies(100)); + continue; + } + xmfs_info(NULL, + "-------------------->got work to do %c %c %c %d %d", + mm_info_real->msg[1], mm_info_real->msg[2], + mm_info_real->msg[3], id, new); + /* + * print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_ADDRESS, 16, 1, + * mm_info_real, sizeof(struct mount_msg_info), true); + */ + for (i = id + 1; i <= new; i++) { + index = xmfs_find_index(sbi, mm_info, true); + xmfs_info(NULL, "-------------------->index :%d", + index); + if (index == -1) { + xmfs_info(NULL, + "There is a race, which means there alreayd have 255 read nodes or there are more than one write nodes mounted parallely."); + break; + } else if (index == -2) { + id++; + break; + } + mm_info_real->msg[index] = 'D'; + id++; + break; + } + + while (index > 0 && + (mm_info_real->urma_infos[index].urma_jetty_id == 0 || + mm_info_real->urma_infos[index].urma_remote_va == 0 || + mm_info_real->urma_infos[index].urma_token_id == 0 || + mm_info_real->send_infos[index].urma_jetty_id == 0 || + mm_info_real->send_infos[index].urma_send_token_id == + 0 || + mm_info_real->send_infos[index].urma_recv_token_id == + 0)) { + schedule_timeout_interruptible(msecs_to_jiffies(10)); + } + if (index > 0) { + sbi->mount_slave++; + xmfs_import_jetty(sbi, mm_info_real->urma_infos[index], + index, false); + xmfs_import_jetty(sbi, mm_info_real->send_infos[index], + index, true); + } + } + kfree(mm_info); + + return 0; +} + +static int xmfs_slave_monitor_fn(void *data) +{ + struct xmfs_sb_info *sbi = data; + int threshold = sbi->freq; + + xmfs_info(NULL, "threshold is %d", threshold); + struct inode *inode; + unsigned long index; + struct page_freq *page_freq; + int cnt; + + while (!kthread_should_stop()) { + spin_lock(&sbi->sb->s_inode_list_lock); + list_for_each_entry(inode, &sbi->sb->s_inodes, i_sb_list) { + if (S_ISDIR(inode->i_mode)) + continue; + + xa_for_each(&(XMFS_I(inode)->page_freq), index, + page_freq) { + if (page_freq->in_cache || + page_freq->never_in_cache || + page_freq->already_in_cache) { + continue; + } + cnt = atomic_read(&page_freq->read_count); + if (cnt >= threshold) + page_freq->in_cache = true; + } + } + spin_unlock(&sbi->sb->s_inode_list_lock); + + schedule_timeout_interruptible(msecs_to_jiffies(10000)); + } + + return 0; +} + +static int xmfs_init_msg_queue(struct xmfs_sb_info *sbi) +{ + struct task_struct *thread; + + if (sbi->id == 0) + thread = kthread_run(xmfs_host_mount_fn, sbi, "msg"); + else if (!sbi->always_hot && sbi->freq) + thread = kthread_run(xmfs_slave_monitor_fn, sbi, + "slave_monitor"); + else + return 0; + + if (IS_ERR(thread)) + return PTR_ERR(thread); + sbi->thread = thread; + + return 0; +} + +static int xmfs_blk_dax_notify_failure(struct dax_device *dax_devp, u64 offset, + u64 len, int mf_flags) +{ + xmfs_error(NULL, "%s: dax_devp %llx offset %llx len %lld mf_flags %x", + __func__, (u64)dax_devp, (u64)offset, (u64)len, mf_flags); + return -EOPNOTSUPP; +} + +static const struct dax_holder_operations xmfs_blk_dax_holder_ops = { + .notify_failure = xmfs_blk_dax_notify_failure, +}; + +static void xmfs_stop_threads(struct xmfs_sb_info *sbi) +{ + if (sbi->thread) { + kthread_stop(sbi->thread); + sbi->thread = NULL; + } + if (sbi->host_worker) { + kthread_stop(sbi->host_worker); + sbi->host_worker = NULL; + } + if (sbi->gc_thread) { + kthread_stop(sbi->gc_thread); + sbi->gc_thread = NULL; + } +} + +static void xmfs_destroy_workqueue(struct workqueue_struct **workqueue) +{ + if (!*workqueue) + return; + + flush_workqueue(*workqueue); + destroy_workqueue(*workqueue); + *workqueue = NULL; +} + +static void xmfs_release_backing_resources(struct xmfs_sb_info *sbi) +{ + if (sbi->dax_filp) { + filp_close(sbi->dax_filp, NULL); + sbi->dax_filp = NULL; + } + if (sbi->dax_dev) { + fs_put_dax(sbi->dax_dev, sbi); + sbi->dax_dev = NULL; + } + if (sbi->handlep) { + bdev_release(sbi->handlep); + sbi->handlep = NULL; + } +} + +static void xmfs_restore_anon_dev(struct super_block *sb, + struct xmfs_sb_info *sbi) +{ + if (sbi->dev) + sb->s_dev = sbi->dev; +} + +static int xmfs_ctx_validate(struct fs_context *fc) +{ + struct xmfs_fs_context *ctx = fc->fs_private; + const char *source = fc->source; + + if (!source) + return invalf(fc, "xmfs: source is required"); + if (!strstr(source, "/dev/pmem") && + !strstr(source, "/dev/obmm") && + !strstr(source, "/dev/loop")) + return invalf(fc, "xmfs: unsupported source %s", source); + + if (ctx->mode < XMFS_SHARED_MEM_MODE || + ctx->mode > XMFS_HYBRID_MODE) + return invalf(fc, "xmfs: invalid mode %d", ctx->mode); + if (ctx->host && ctx->mode != XMFS_SHARED_MEM_MODE) { + errorf(fc, "xmfs: host requires shared-memory mode"); + return -EOPNOTSUPP; + } + if (!IS_ENABLED(CONFIG_XMFS_FS_URMA) && + ctx->mode != XMFS_SHARED_MEM_MODE) { + errorf(fc, "xmfs: mode %d requires URMA support", ctx->mode); + return -EOPNOTSUPP; + } + return 0; +} + +static int xmfs_prepare_slave_transport(struct xmfs_sb_info *sbi, + struct xmfs_fs_context *ctx) +{ + int err; + + if (ctx->host || ctx->mode == XMFS_SHARED_MEM_MODE) + return 0; + + err = xmfs_urma_register(sbi, ctx); + if (err) + goto unregister; + + err = xmfs_urma_import(sbi); + if (!err) + return 0; + + sbi->magic = 0; + xmfs_urma_unimport(sbi); +unregister: + xmfs_urma_unregister(sbi); + return err; +} + +static int xmfs_prepare_host_transport(struct xmfs_sb_info *sbi, + struct xmfs_fs_context *ctx) +{ + if (!ctx->host) + return 0; + +#ifdef CONFIG_XMFS_FS_URMA + return xmfs_urma_register(sbi, ctx); +#else + return 0; +#endif +} + +static int xmfs_map_pmem_device(struct super_block *sb, struct fs_context *fc) +{ + struct xmfs_fs_context *ctx = fc->fs_private; + struct xmfs_sb_info *sbi = XMFS_SB(sb); + struct bdev_handle *handlep; + struct dax_device *dax_dev; + struct pmem_device *pmem; + u64 start_off = 0; + int err; + + handlep = bdev_open_by_path(fc->source, FMODE_READ | FMODE_WRITE, + sbi, NULL); + if (!handlep || IS_ERR(handlep)) + return handlep ? PTR_ERR(handlep) : -ENODEV; + if (!handlep->bdev || IS_ERR(handlep->bdev)) { + xmfs_error(NULL, "%s: failed blkdev_get_by_path(%s)", + __func__, fc->source); + err = handlep->bdev ? PTR_ERR(handlep->bdev) : -EINVAL; + bdev_release(handlep); + return err; + } + + sbi->dev = sb->s_dev; + sb->s_dev = handlep->bdev->bd_dev; + dax_dev = fs_dax_get_by_bdev(handlep->bdev, &start_off, + sbi /* holder */, + &xmfs_blk_dax_holder_ops); + if (IS_ERR(dax_dev) || !dax_dev) { + xmfs_error(NULL, + "%s: unable to get pmem dax dev from handlep->bdev", + __func__); + bdev_release(handlep); + return -ENODEV; + } + + pmem = dax_get_private(dax_dev); + if (IS_ERR(pmem) || !pmem) { + xmfs_error(NULL, + "%s: unable to get pmem dev from handlep->bdev", + __func__); + fs_put_dax(dax_dev, sbi); + bdev_release(handlep); + return -ENODEV; + } + + xmfs_info(NULL, "pmem %ld %lld", pmem->size, pmem->data_offset); + if (!ctx->size || ctx->size > pmem->size - pmem->data_offset) + ctx->size = pmem->size - pmem->data_offset; + + sbi->start = pmem->phys_addr + pmem->data_offset; + sbi->len = ctx->size; + sbi->kaddr = (void *)ioremap_cache(sbi->start, ctx->size); + xmfs_info(NULL, "pmem %p", sbi->kaddr); + /* sbi->dax_dev = dax_dev; */ + sbi->handlep = handlep; + fs_put_dax(dax_dev, sbi); + /* bdev_release(handlep); */ + + return 0; +} + +static int xmfs_probe_slave_backing(struct xmfs_sb_info *sbi, + struct xmfs_fs_context *ctx, + struct xmfs_sb_index *xmsi) +{ + int err; + + err = get_xmsi_and_copy(sbi, xmsi); + if (unlikely(err)) + return err; + if (xmsi->magic != XMFS_MAGIC) { + xmfs_info(NULL, "no host mount yet"); + return -EINVAL; + } + if (xmsi->len != ctx->size) { + xmfs_info(NULL, + "slave mount uses size %ld should use same size %ld as host", + ctx->size, xmsi->len); + ctx->size = xmsi->len; + } + + return 0; +} + +static int xmfs_map_pmem_address(struct xmfs_sb_info *sbi, + struct xmfs_fs_context *ctx, + struct xmfs_sb_index *xmsi) +{ + int err; + + if (!ctx->host) { + sbi->kaddr = (void *)ioremap_cache(ctx->addr, SB_SIZE); + err = xmfs_probe_slave_backing(sbi, ctx, xmsi); + if (err) { + xmfs_error(NULL, + "hardware memory error when reading xmsi during slave mount 1."); + return err; + } + iounmap(sbi->kaddr); + sbi->kaddr = NULL; + } + + sbi->kaddr = (void *)ioremap_cache(ctx->addr, ctx->size); + sbi->start = ctx->addr; + sbi->len = ctx->size; + xmfs_info(NULL, "pmem addr %p", sbi->kaddr); + + return 0; +} + +static int xmfs_map_obmm(struct xmfs_sb_info *sbi, + struct xmfs_fs_context *ctx, + struct xmfs_sb_index *xmsi) +{ + int err; + + if (!ctx->host) { + sbi->kaddr = (void *)memremap(ctx->addr, SB_SIZE, MEMREMAP_WB); + sbi->debug = ctx->debug; + err = xmfs_probe_slave_backing(sbi, ctx, xmsi); + if (err) { + xmfs_error(NULL, + "hardware memory error when reading xmsi during slave mount 2."); + return err; + } + iounmap(sbi->kaddr); + sbi->kaddr = NULL; + } + + sbi->kaddr = memremap(ctx->addr, ctx->size, MEMREMAP_WB); + sbi->start = ctx->addr; + sbi->len = ctx->size; + xmfs_info(NULL, "obmm addr %p", sbi->kaddr); + + return 0; +} + +static int xmfs_map_loop(struct xmfs_sb_info *sbi, + struct xmfs_fs_context *ctx, + struct xmfs_sb_index *xmsi) +{ + int err; + + if (ctx->host) { + sbi->kaddr = vzalloc(ctx->size); + xmfs_info(NULL, "mem addr %p", sbi->kaddr); + sbi->len = ctx->size; + return 0; + } + + sbi->debug = ctx->debug; + err = get_xmsi_and_copy(sbi, xmsi); + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_ADDRESS, 16, 1, + xmsi, sizeof(struct xmfs_sb_index), true); + if (unlikely(err)) { + xmfs_error(NULL, + "hardware memory error when reading xmsi during slave mount 1."); + return err; + } + if (xmsi->magic != XMFS_MAGIC) { + xmfs_info(NULL, "no host mount yet"); + return -EINVAL; + } + if (xmsi->len != ctx->size) + ctx->size = xmsi->len; + + sbi->kaddr = sbi; + return 0; +} + +static int xmfs_map_backing(struct super_block *sb, struct fs_context *fc, + struct xmfs_sb_index *xmsi) +{ + struct xmfs_fs_context *ctx = fc->fs_private; + struct xmfs_sb_info *sbi = XMFS_SB(sb); + const char *source = fc->source; + int err; + + if (!source) + return -EINVAL; + + if (strstr(source, "/dev/pmem")) { + if (ctx->addr) + err = xmfs_map_pmem_address(sbi, ctx, xmsi); + else + err = xmfs_map_pmem_device(sb, fc); + } else if (strstr(source, "/dev/obmm")) { + err = xmfs_map_obmm(sbi, ctx, xmsi); + } else if (strstr(source, "/dev/loop")) { + err = xmfs_map_loop(sbi, ctx, xmsi); + } else { + xmfs_info(NULL, "other"); + return -EINVAL; + } + if (err) + return err; + + if (IS_ERR_OR_NULL(sbi->kaddr)) { + xmfs_error(NULL, "map error %ld", PTR_ERR(sbi->kaddr)); + return sbi->kaddr ? PTR_ERR(sbi->kaddr) : -EINVAL; + } + + if (ctx->host) + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_ADDRESS, 16, 1, + sbi->kaddr, 512, true); + + return 0; +} + +static int xmfs_prepare_host_mount(struct xmfs_sb_info *sbi, + struct xmfs_fs_context *ctx) +{ + struct mount_msg_info *mm_info_real = get_mnt_msg(sbi); + struct mount_msg_info *mm_info = kmalloc(sizeof(*mm_info), GFP_KERNEL); + int loop_count = 0; + int old, tmp; + int err; + +host_get_id: + err = copy_mc_to_kernel(mm_info, get_mnt_msg(sbi), sizeof(*mm_info)); + if (unlikely(err)) { + xmfs_error(NULL, + "hardware memory error when reading superblock at beginning of mounting host and getting id"); + goto free_msg; + } + + old = atomic_read(&mm_info->host_mount); + if (old < 0 || old > 1) { + kfree(mm_info); + goto host_continue; + } else if (old == 1) { + schedule_timeout_interruptible(msecs_to_jiffies(10)); + goto host_get_id; + } + + tmp = atomic_cmpxchg(&mm_info_real->host_mount, old, 1); + if (tmp == old) { + mm_info_real->msg[0] = 'T'; + } else { + xmfs_info(NULL, + "exchange failed, someone else mounting parallel, skip this mount"); + err = -EINVAL; + goto free_msg; + } + + do { + err = copy_mc_to_kernel(mm_info, get_mnt_msg(sbi), + sizeof(*mm_info)); + if (unlikely(err)) { + xmfs_error(NULL, + "hardware memory error when reading superblock at beginning of mounting"); + goto free_msg; + } + loop_count++; + schedule_timeout_interruptible(msecs_to_jiffies(10)); + } while (mm_info->msg[0] != 'D' && loop_count <= 10); + + if (mm_info->msg[0] == 'D') { + mm_info_real->msg[0] = 'C'; + tmp = atomic_cmpxchg(&mm_info_real->host_mount, 1, old); + xmfs_info(NULL, + "host msg returns meaning host already mounted %d %d", + tmp, old); + err = -EINVAL; + goto free_msg; + } + + tmp = atomic_cmpxchg(&mm_info_real->host_mount, 1, old); + if (tmp != 1) + xmfs_info(NULL, + "when checking mounting host done, there is a bug when exchange host mount value to %d", + old); + kfree(mm_info); + + if (ctx->replay) { + sbi->id = 0; + /* + * memset(get_mnt_msg(sbi), 0, + * sizeof(struct mount_msg_info)); + */ + return 0; + } + +host_continue: + memset(sbi->kaddr, 0, DATA_AREA_OFFSET); + sbi->id = 0; + memcpy(sbi->kaddr + MOUNT_MSG_OFFSET + + offsetof(struct mount_msg_info, send_infos[0]), + &sbi->urma_cfg.send_info, sizeof(struct urma_info)); +#ifdef CONFIG_X86 + xmfs_info(NULL, "x86 mount"); + /* + * err = set_memory_uc((unsigned long)sbi->kaddr, + * DATA_AREA_OFFSET >> PAGE_SHIFT); + */ + /* xmfs_info(sbi->sb, "qyf set memory return err %d", err); */ +#elif defined(CONFIG_ARM) || defined(CONFIG_ARM64) + xmfs_info(NULL, "arm mount host"); + /* + * set_memory_noncacheable((unsigned long)sbi->kaddr, + * DATA_AREA_OFFSET); + */ +#endif + return 0; + +free_msg: + kfree(mm_info); + return err; +} + +static int xmfs_prepare_slave_mount(struct xmfs_sb_info *sbi, + struct xmfs_fs_context *ctx, + struct xmfs_sb_index *xmsi) +{ + struct mount_msg_info *mm_info = kmalloc(sizeof(*mm_info), GFP_KERNEL); + int loop_count = 0; + int old, new; + int index; + int err; + + err = get_mnt_msg_and_copy(sbi, mm_info); + if (unlikely(err)) { + xmfs_error(NULL, + "hardware memory error when reading superblock at beginning of mounting slave"); + goto free_msg; + } + +slave_get_id: + old = xmfs_atomic_read(sbi, mm_info); + new = old + 1; + xmfs_info(NULL, "------> cas target %d", new); + if (xmfs_cas(sbi, old, new) == old) { + index = xmfs_find_index(sbi, mm_info, false); + xmfs_info(NULL, "------> slave index %d", index); + if (index == -1) { + xmfs_info(NULL, + "Already have 255 mount nodes, cannot mount more"); + err = -EINVAL; + goto free_msg; + } + sbi->id = index; + xmfs_info(NULL, "------> slave id is %d", index); + } else { + err = get_mnt_msg_and_copy(sbi, mm_info); + if (unlikely(err)) { + xmfs_error(NULL, + "hardware memory error when reading superblock during mount slave"); + goto free_msg; + } + goto slave_get_id; + } + + while (mm_info->msg[sbi->id] != 'D' && loop_count < 10) { + loop_count++; + schedule_timeout_interruptible(msecs_to_jiffies(1000)); + err = get_mnt_msg_and_copy(sbi, mm_info); + if (unlikely(err)) { + xmfs_error(NULL, + "hardware memory error when reading superblock during mount slave %d", + index); + goto free_msg; + } + } + if (loop_count == 10) { + xmfs_info(NULL, "try 10 times to check msg mount but failed"); + err = -EINVAL; + goto free_msg; + } + + err = get_xmsi_and_copy(sbi, xmsi); + if (unlikely(err)) { + xmfs_error(NULL, + "hardware memory error when reading xmsi during slave mount."); + goto free_msg; + } + if (xmsi->magic != XMFS_MAGIC || xmsi->len != ctx->size) { + xmfs_info(NULL, + "using the wrong size %ld to mount slave, should use %ld", + ctx->size, xmsi->len); + err = -EINVAL; + goto free_msg; + } + + xmfs_info(NULL, "mount slave id %d", sbi->id); + sbi->log_start = xmsi->log_start; + sbi->last_replayed_slot = (unsigned long)-1; + get_mnt_msg_and_copy(sbi, mm_info); + xmfs_info(NULL, "host send info %lld %lld %lld %lld %d %d %d", + mm_info->send_infos[0].urma_send_va, + mm_info->send_infos[0].urma_recv_va, + mm_info->send_infos[0].urma_eid_subnet_prefix, + mm_info->send_infos[0].urma_eid_interface_id, + mm_info->send_infos[0].urma_jetty_id, + mm_info->send_infos[0].urma_send_token_id, + mm_info->send_infos[0].urma_recv_token_id); + xmfs_import_jetty(sbi, mm_info->send_infos[0], 0, true); + xmfs_pmem_write(sbi, + MOUNT_MSG_OFFSET + + offsetof(struct mount_msg_info, urma_infos[index]), + &sbi->urma_cfg.own_info, sizeof(struct urma_info)); + xmfs_pmem_write(sbi, + MOUNT_MSG_OFFSET + + offsetof(struct mount_msg_info, send_infos[index]), + &sbi->urma_cfg.send_info, sizeof(struct urma_info)); + +free_msg: + kfree(mm_info); + return err; +} + +static void xmfs_init_runtime_state(struct xmfs_sb_info *sbi, + const struct xmfs_fs_context *ctx) +{ + sbi->last_update = 0; + sbi->last_replayed_slot = (unsigned long)-1; + sbi->debug = ctx->debug; + sbi->fallocate_size = ctx->fallocate_size; + spin_lock_init(&sbi->space_lock); + init_rwsem(&sbi->chain_rwsem); + spin_lock_init(&sbi->version_lock); + mutex_init(&sbi->replay_lock); + spin_lock_init(&sbi->local_ring_lock); + xa_init(&sbi->pinned_inodes); +#ifdef CONFIG_XMFS_FS_URMA + memcpy(sbi->dev_name, ctx->dev_name, sizeof(ctx->dev_name)); +#endif + sbi->host_worker = NULL; + sbi->max_slave_id = 0; + + spin_lock_init(&sbi->gc_ctl.gc_lock); + init_waitqueue_head(&sbi->gc_ctl.wait); + sbi->gc_ctl.gc_running = false; + sbi->gc_ctl.gc_batch = XMFS_GC_BATCH; + sbi->gc_ctl.gc_cursor = 0; + sbi->gc_interval = XMFS_GC_INTERVAL_DEFAULT; + sbi->mount_slave = 0; +} + +static void xmfs_init_log_geometry(struct xmfs_sb_info *sbi, int log_size) +{ + if (!log_size) { + sbi->log_size = XMFS_DEFAULT_CHUNK_SIZE; + sbi->trunk_size = XMFS_DEFAULT_CHUNK_SIZE; + } else if (log_size > XMFS_DEFAULT_CHUNK_SIZE) { + sbi->trunk_size = log_size; + sbi->log_size = log_size; + } else { + sbi->trunk_size = XMFS_DEFAULT_CHUNK_SIZE; + sbi->log_size = log_size; + } +} + +static int xmfs_configure_super(struct super_block *sb, + struct xmfs_fs_context *ctx, + struct xmfs_sb_index *xmsi) +{ + struct xmfs_sb_info *sbi = XMFS_SB(sb); + int err; + + xmfs_init_runtime_state(sbi, ctx); + if (ctx->host) + xmfs_init_log_geometry(sbi, ctx->log_size); + + err = get_xmsi_and_copy(sbi, xmsi); + if (unlikely(err)) { + xmfs_error(NULL, + "hardware memory error when reading xmsi during host mount 2."); + return err; + } + + if (ctx->host && ctx->replay && xmsi->magic == XMFS_MAGIC) { + xmfs_info(NULL, "replay"); + if (sbi->log_size != xmsi->log_size || ctx->size != xmsi->len) { + memset(sbi->kaddr, 0, DATA_AREA_OFFSET); + sbi->id = 0; + ctx->replay = false; + } + } + if (ctx->host && ctx->replay && xmsi->magic != XMFS_MAGIC) { + memset(sbi->kaddr, 0, DATA_AREA_OFFSET); + sbi->id = 0; + ctx->replay = false; + } + + sb->s_maxbytes = MAX_LFS_FILESIZE; + sb->s_blocksize = PAGE_SIZE; + sb->s_blocksize_bits = PAGE_SHIFT; + sb->s_magic = XMFS_MAGIC; + sb->s_op = &xmfs_ops; + xmfs_init_dentry_operations(sb); + sb->s_time_gran = 1; + if (sbi->id == 0) + uuid_gen(&sb->s_uuid); + if (ctx->host) { + sb->s_xattr = xmfs_host_xattr_handlers; + } else { + sb->s_xattr = xmfs_slave_xattr_handlers; + sbi->log_size = xmsi->log_size; + ctx->log_size = sbi->log_size; + xmfs_init_log_geometry(sbi, ctx->log_size); + } + + sbi->max_chunk = + (ctx->size - DATA_AREA_OFFSET) / sbi->trunk_size - 1; + xmfs_info(NULL, "max chunk %ld %ld %ld %ld", sbi->max_chunk, + sizeof(struct xmfs_log_trunk), sizeof(struct mount_msg_info), + DATA_AREA_OFFSET); + + return 0; +} + +static int xmfs_create_root(struct super_block *sb) +{ + struct xmfs_sb_info *sbi = XMFS_SB(sb); + + sb->s_root = d_make_root(xmfs_get_root(sb, sbi->id)); + if (!sb->s_root) + return -ENOMEM; + + return 0; +} + +static int xmfs_alloc_workqueues(struct xmfs_sb_info *sbi) +{ + sbi->prefetch_wq = alloc_workqueue("xmfs-prefetch", + WQ_UNBOUND | WQ_MEM_RECLAIM, 16); + if (!sbi->prefetch_wq) + return -ENOMEM; + + sbi->send_wq = alloc_workqueue("xmfs-send-req", + WQ_UNBOUND | WQ_MEM_RECLAIM, 128); + if (!sbi->send_wq) + return -ENOMEM; + + return 0; +} + +static int xmfs_start_workers(struct xmfs_sb_info *sbi) +{ + struct xmfs_inode_table *tbl; + int err; + + err = xmfs_init_msg_queue(sbi); + if (sbi->id == 0) { + sbi->gc_thread = kthread_run(xmfs_gc_thread_fn, sbi, "xmfs_gc"); + if (IS_ERR(sbi->gc_thread)) + sbi->gc_thread = NULL; + sbi->max_slave_id = 63; + + tbl = (struct xmfs_inode_table *)get_meta(sbi, + INODE_TABLE_OFFSET, + INODE_TABLE_SIZE); + tbl->magic = XMFS_INODE_TABLE_MAGIC; + } else { + sbi->gc_thread = kthread_run(xmfs_gc_thread_slave_fn, sbi, + "xmfs_slave_gc"); + if (IS_ERR(sbi->gc_thread)) + sbi->gc_thread = NULL; + } + + return err; +} + +static int xmfs_finalize_host_mount(struct super_block *sb, + struct xmfs_fs_context *ctx, + struct xmfs_sb_index *xmsi) +{ + struct xmfs_sb_info *sbi = XMFS_SB(sb); + struct xmfs_sb_index *xmsi_real; + + if (ctx->replay && xmsi->magic == XMFS_MAGIC) { + xmfs_info(NULL, "replay"); + if (sbi->log_size != xmsi->log_size) { + xmfs_info(NULL, + "mount host again with wrong log size %d should be %d", + sbi->log_size, xmsi->log_size); + return -EINVAL; + } + if (ctx->size != xmsi->len) { + xmfs_info(NULL, + "mount host again with wrong size %ld should be %ld", + ctx->size, xmsi->len); + return -EINVAL; + } + sbi->during_replay = true; + xmfs_replay_new_entries(sbi, NULL, -1); + sbi->during_replay = false; + } + + if (ctx->replay) { + struct xmfs_sb_index *xmsi_r = get_xmsi(sbi); + + if (xmsi_r) { + unsigned long used_trunks = 0; + unsigned long i; + + for (i = 0; + i < xmsi_r->count && i < XMFS_BITMAP_CAPACITY; + i++) { + if (xmsi_r->bitmap[i] != 'u') + used_trunks++; + } + xmsi_r->used_trunk_count = used_trunks; + put_xmsi(sbi, xmsi_r); + } + } + + xmsi_real = get_xmsi(sbi); + xmsi_real->magic = XMFS_MAGIC; + xmsi_real->log_size = sbi->log_size; + xmsi_real->len = ctx->size; + xmsi_real->fsid = uuid_to_fsid(sb->s_uuid.b); + if (!ctx->replay) { + memset(xmsi_real->bitmap, 'u', XMFS_BITMAP_CAPACITY); + xmsi_real->bitmap_size = 0; + xmsi_real->used_trunk_count = 0; + } + + /* xmfs_populate_from_inode_table(sbi); */ + if (!ctx->replay && XMFS_I(sb->s_root->d_inode)->log_start == 0) { + struct xmfs_log xm_log; + unsigned long xmlt_addr; + + xm_log.ops = XMFS_ROOT; + xmfs_info(NULL, "root 1 %p", XMFS_I(sb->s_root->d_inode)); + xmlt_addr = xmfs_add_log_v3(&xm_log, sbi, 0, + sb->s_root->d_inode->i_ino, true); + if (xmlt_addr < 0) + return -EINVAL; + sbi->last_update = 0; + xmfs_info(NULL, "log start %ld", + XMFS_I(sb->s_root->d_inode)->log_start); + } + /* + * sbi->xmfs_log_wq = alloc_ordered_workqueue("xmfs_log_wq", + * WQ_MEM_RECLAIM); + */ + + return 0; +} + +static int xmfs_fill_super(struct super_block *sb, struct fs_context *fc) +{ + struct xmfs_fs_context *ctx = fc->fs_private; + struct xmfs_sb_index *xmsi; + struct xmfs_sb_info *sbi; + int err; + + sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); + if (!sbi) + return -ENOMEM; + sb->s_fs_info = sbi; + xmfs_init_log_ratelimits(sbi); + sbi->mode = ctx->mode; + sbi->magic = XMFS_MAGIC; + xmsi = kmalloc(sizeof(*xmsi), GFP_KERNEL); + + err = xmfs_prepare_slave_transport(sbi, ctx); + if (err) + goto free_sbi; + + err = xmfs_map_backing(sb, fc, xmsi); + if (err) + goto free_kaddr; + + err = xmfs_prepare_host_transport(sbi, ctx); + if (err) + goto release_dev; + + if (ctx->host) + err = xmfs_prepare_host_mount(sbi, ctx); + else + err = xmfs_prepare_slave_mount(sbi, ctx, xmsi); + if (err) + goto release_dev; + + err = xmfs_configure_super(sb, ctx, xmsi); + if (err) + goto stop_thread; + + err = xmfs_create_root(sb); + if (err) + goto release_dev; + + err = xmfs_alloc_workqueues(sbi); + if (err) + goto release_root; + + err = security_inode_init_security(sb->s_root->d_inode, + sb->s_root->d_parent->d_inode, + &sb->s_root->d_name, xmfs_initxattrs, + NULL); + if (err) + goto release_root; + + sbi->sb = sb; + sbi->used_ino = 0; + sbi->freq = ctx->freq; + sbi->always_hot = ctx->always_hot; + err = xmfs_start_workers(sbi); + if (err) + goto stop_thread; + + if (ctx->host) { + err = super_setup_bdi(sb); + if (err) { + xmfs_info(NULL, "setup bdi failed"); + goto stop_thread; + } + + err = xmfs_finalize_host_mount(sb, ctx, xmsi); + if (err) + goto stop_thread; + } + + kfree(xmsi); + if (ctx->host) + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_ADDRESS, 16, 1, + sbi->kaddr, 400, true); + return 0; + +stop_thread: + xmfs_stop_threads(sbi); +release_root: + dput(sb->s_root); + sb->s_root = NULL; + xmfs_destroy_workqueue(&sbi->prefetch_wq); + xmfs_destroy_workqueue(&sbi->send_wq); +release_dev: + xmfs_release_backing_resources(sbi); +free_kaddr: + if (ctx->host) + vfree(sbi->kaddr); + sbi->magic = 0; + xmfs_urma_unimport(sbi); + xmfs_urma_unregister(sbi); +free_sbi: + xmfs_restore_anon_dev(sb, sbi); + if (sb->s_fs_info == sbi) + sb->s_fs_info = NULL; + kfree(sbi); + kfree(xmsi); + return err; +} + +static int xmfs_get_tree(struct fs_context *fc) +{ + int err; + + err = xmfs_ctx_validate(fc); + if (err) + return err; + + return get_tree_nodev(fc, xmfs_fill_super); +} + +static const struct fs_parameter_spec xmfs_param_specs[] = { + fsparam_bool("host", Opt_host), + fsparam_bool("replay", Opt_replay), + fsparam_bool("debug", Opt_debug), + fsparam_bool("hot", Opt_always_hot), + fsparam_u64("addr", Opt_addr), + fsparam_u64("size", Opt_size), + fsparam_u64("fallocate", Opt_fallocate), + fsparam_s32("freq", Opt_freq), + fsparam_s32("log", Opt_log), + fsparam_s32("mode", Opt_mode), +#ifdef CONFIG_XMFS_FS_URMA + fsparam_string("urma_dev_name", Opt_urma_dev), + fsparam_u64("urma_va", Opt_urma_va), + fsparam_u64("urma_eid_subnet_prefix", Opt_urma_eid_subnet_prefix), + fsparam_u64("urma_eid_interface_id", Opt_urma_eid_interface_id), + fsparam_u32("urma_jetty_id", Opt_urma_jetty_id), + fsparam_u32("urma_token_id", Opt_urma_token_id), +#endif + {} +}; + +static int xmfs_parse_param(struct fs_context *fc, struct fs_parameter *param) +{ + struct xmfs_fs_context *ctx = fc->fs_private; + struct fs_parse_result result; + int opt; + + opt = fs_parse(fc, xmfs_param_specs, param, &result); + if (opt < 0) + return opt; + + switch (opt) { + case Opt_host: + ctx->host = result.boolean; + break; + case Opt_replay: + ctx->replay = result.boolean; + break; + case Opt_addr: + ctx->addr = result.uint_64; + break; + case Opt_size: + ctx->size = result.uint_64; + break; + case Opt_freq: + ctx->freq = result.int_32; + break; + case Opt_log: + ctx->log_size = result.int_32; + break; + case Opt_debug: + ctx->debug = result.boolean; + break; + case Opt_always_hot: + ctx->always_hot = result.boolean; + break; + case Opt_fallocate: + ctx->fallocate_size = result.uint_64; + break; + case Opt_mode: + ctx->mode = result.int_32; + break; +#ifdef CONFIG_XMFS_FS_URMA + case Opt_urma_va: + ctx->urma_va = result.uint_64; + break; + case Opt_urma_eid_subnet_prefix: + ctx->urma_eid_subnet_prefix = result.uint_64; + break; + case Opt_urma_eid_interface_id: + ctx->urma_eid_interface_id = result.uint_64; + break; + case Opt_urma_jetty_id: + ctx->urma_jetty_id = result.uint_32; + break; + case Opt_urma_token_id: + ctx->urma_token_id = result.uint_32; + break; + case Opt_urma_dev: + memcpy(ctx->dev_name, param->string, strlen(param->string)); + break; +#endif + default: + return -ENOPARAM; + } + + return 0; +} + +static void xmfs_fs_context_free(struct fs_context *fc) +{ + kfree(fc->fs_private); +} + +static const struct fs_context_operations xmfs_fs_context_ops = { + .get_tree = xmfs_get_tree, + .parse_param = xmfs_parse_param, + .free = xmfs_fs_context_free, +}; + +static int xmfs_init_fs_context(struct fs_context *fc) +{ + struct xmfs_fs_context *ctx; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + fc->fs_private = ctx; + fc->ops = &xmfs_fs_context_ops; + + return 0; +} + +static void xmfs_unpin_all_inodes(struct xmfs_sb_info *sbi) +{ + struct inode *inode; + unsigned long index; + + /* Each xarray entry retains one inode reference until it is erased here. */ + xa_for_each(&sbi->pinned_inodes, index, inode) { + inode = xa_erase(&sbi->pinned_inodes, index); + if (!inode) + continue; + + iput(inode); + cond_resched(); + } + + xa_destroy(&sbi->pinned_inodes); +} + +static void xmfs_shutdown_sb(struct super_block *sb, + struct xmfs_sb_info *sbi) +{ + mnt_msg_write(sbi, sbi->id, 'C'); + sbi->magic = 0; + + xmfs_stop_threads(sbi); + xmfs_destroy_workqueue(&sbi->prefetch_wq); + xmfs_destroy_workqueue(&sbi->send_wq); + + xmfs_restore_anon_dev(sb, sbi); + xmfs_unpin_all_inodes(sbi); +} + +static void xmfs_release_sb_info(struct xmfs_sb_info *sbi) +{ + xmfs_release_backing_resources(sbi); + if (sbi->kaddr != sbi) + vfree(sbi->kaddr); + sbi->kaddr = NULL; + xmfs_urma_unimport(sbi); + xmfs_urma_unregister(sbi); + kfree(sbi); +} + +static void xmfs_kill_sb(struct super_block *sb) +{ + struct xmfs_sb_info *sbi = XMFS_SB(sb); + + if (sbi) + xmfs_shutdown_sb(sb, sbi); + + kill_litter_super(sb); + + if (sbi) + xmfs_release_sb_info(sbi); +} + +static struct file_system_type xmfs_fs_type = { + .name = "xmfs", + .owner = THIS_MODULE, + .init_fs_context = xmfs_init_fs_context, + .parameters = xmfs_param_specs, + .kill_sb = xmfs_kill_sb, +}; + +static void xmfs_init_once(void *data) +{ + struct xmfs_inode_info *xmfs_inode = data; + + inode_init_once(&xmfs_inode->vfs_inode); +} + +static int __init init_xmfs(void) +{ + int err; + + xmfs_inode_cachep = kmem_cache_create("xmfs_inode_cache", + sizeof(struct xmfs_inode_info), 0, + SLAB_ACCOUNT, xmfs_init_once); + if (!xmfs_inode_cachep) + return -ENOMEM; + + err = register_filesystem(&xmfs_fs_type); + if (err) + kmem_cache_destroy(xmfs_inode_cachep); + + return err; +} + +static void __exit exit_xmfs(void) +{ + unregister_filesystem(&xmfs_fs_type); + kmem_cache_destroy(xmfs_inode_cachep); +} + +module_init(init_xmfs); +module_exit(exit_xmfs); + +MODULE_AUTHOR("Yifan Qiao <qiaoyifan4@huawei.com>"); +MODULE_DESCRIPTION("Log Structured Filesystem for UB"); +MODULE_LICENSE("GPL"); diff --git a/fs/xmfs/symlink.c b/fs/xmfs/symlink.c new file mode 100644 index 000000000000..20870b18f325 --- /dev/null +++ b/fs/xmfs/symlink.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * XMFS: Log Structured Filesystem for UB + * Copyright (C) 2024. Huawei Technologies Co., Ltd + * + * This program can be distributed under the terms of the GNU GPL. + * See the file COPYING. + */ + +#include "xmfs_i.h" + +#include <linux/pagemap.h> + +static void xmfs_put_link(void *arg) +{ + /* xmfs_info(NULL, "qyf put folio %p", arg); */ + folio_put(arg); +} + +static void xmfs_put_page(void *arg) +{ + /* xmfs_info(NULL, "qyf put page %p", page_address(arg)); */ + put_page(arg); +} + +static const char *xmfs_get_link(struct dentry *dentry, struct inode *inode, + struct delayed_call *done) +{ + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + + if (!dentry) { + struct folio *folio = filemap_get_folio(inode->i_mapping, 0); + + if (IS_ERR(folio)) + return ERR_PTR(-ECHILD); + set_delayed_call(done, xmfs_put_link, folio); + return folio_address(folio); + } + struct folio *folio = filemap_get_folio(inode->i_mapping, 0); + + if (IS_ERR(folio)) { + struct xmfs_log *ulog = get_log(sbi, XMFS_I(inode)->symname); + struct page *page = alloc_page(GFP_KERNEL); + char *ret = kmap_local_page(page); + + if (ulog->end_index > sbi->len) { + xmfs_info(inode->i_sb, "get link wrong %ld %ld", + inode->i_ino, XMFS_I(inode)->symname); + } + if (!ulog->end_index) { + memcpy(ret, ulog->name, strlen(ulog->name) + 1); + } else { + memcpy(ret, get_data(sbi, ulog->end_index, ulog->index), + ulog->index); + } + kunmap_local(ret); + set_page_dirty(page); + + set_delayed_call(done, xmfs_put_page, page); + if (!ret || !page_address(page)) { + xmfs_info(inode->i_sb, "get link 1 return null %s", + dentry->d_name.name); + } + return page_address(page); + } + + set_delayed_call(done, xmfs_put_link, folio); + return folio_address(folio); +} + +static const struct inode_operations xmfs_symlink_inode_operations = { + .setattr = xmfs_setattr, + .getattr = xmfs_getattr, + .get_link = xmfs_get_link, + .listxattr = xmfs_listxattr, + .update_time = xmfs_update_time, +}; + +void xmfs_init_symlink_mapping(struct inode *inode) +{ + xmfs_init_file_mapping(inode); +} + +void xmfs_init_symlink_operations(struct inode *inode) +{ + inode->i_op = &xmfs_symlink_inode_operations; +} diff --git a/fs/xmfs/urma.c b/fs/xmfs/urma.c new file mode 100644 index 000000000000..d73b825bdbfe --- /dev/null +++ b/fs/xmfs/urma.c @@ -0,0 +1,2482 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include "xmfs_i.h" + +#ifdef CONFIG_XMFS_FS_URMA + +#include <ub/urma/ubcore_types.h> +#include <ub/urma/ubcore_uapi.h> +#include <ub/urma/ubcore_api.h> +#include <linux/hashtable.h> +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/completion.h> +#include <linux/atomic.h> +#include <linux/kthread.h> +#include <linux/delay.h> +#include <linux/jiffies.h> +#include <linux/interrupt.h> +#include <linux/cacheflush.h> + +static struct ubcore_device *g_urma_dev[10]; +static char g_dev_name[100]; + +enum xmfs_urma_status { + URMA_ISSUING = 1, + URMA_COMPLETE, + URMA_ERROR, +}; + +struct wr_status { + int magic; + refcount_t refs; + int ret; + int ops; + int slot; + enum xmfs_urma_status status; + struct completion done; +}; + +#define XMFS_ACCESS_LOCAL_ONLY 0x1 +#define XMFS_ACCESS_READ (0x1 << 1) +#define XMFS_ACCESS_WRITE (0x1 << 2) +#define XMFS_ACCESS_ATOMIC (0x1 << 3) + +#define XMFS_DEPTH 1024 +#define XMFS_POST_RETRY_CNT 100 + +static void xmfs_wrs_get(struct wr_status *wrs) +{ + refcount_inc(&wrs->refs); +} + +static void xmfs_wrs_put(struct wr_status **wrs) +{ + if (refcount_dec_and_test(&(*wrs)->refs)) { + /* xmfs_info(NULL, "qyf FREE 1 wrs=%px", *wrs); */ + kfree(*wrs); + *wrs = NULL; + } +} + +static int xmfs_add_udma_device(struct ubcore_device *ubc_dev) +{ + xmfs_info(NULL, "add device %p %s %p %p %d", ubc_dev, ubc_dev->dev_name, + ubc_dev->ops->register_seg, ubc_dev->ops->unregister_seg, + IS_ERR_OR_NULL(ubc_dev->eid_table.eid_entries)); + xmfs_info(NULL, "hooks check %p %p %p", ubc_dev->ops->import_jetty, + ubc_dev->ops->import_jetty_ex, ubc_dev->ops->import_seg); + if (ubc_dev == NULL) { + xmfs_error(NULL, + "Param ubc_dev is null, add udma device fail."); + return -EINVAL; + } + + if (strcmp(ubc_dev->dev_name, g_dev_name) == 0) { + xmfs_info(NULL, "find udma dev %s", ubc_dev->dev_name); + g_urma_dev[0] = ubc_dev; + xmfs_error(NULL, "--------->g_urma_dev set"); + } + return 0; +} + +static void xmfs_remove_udma_device(struct ubcore_device *ubc_dev, + void *client_ctx) +{ + xmfs_error(NULL, "ubma device removed %s, all in-flight ops will fail", + ubc_dev->dev_name); +} + +static int xmfs_urma_pool_init(struct xmfs_urma_pool *pool, void *base, + size_t total_size, size_t slot_size) +{ + pool->base = base; + pool->total_size = total_size; + pool->slot_size = slot_size; + + if (slot_size == PAGE_SIZE) + pool->nr_slots = total_size / slot_size; + else + pool->nr_slots = XMFS_DEPTH; + pool->free_slots = pool->nr_slots; + pool->bitmap = bitmap_zalloc(pool->nr_slots, GFP_KERNEL); + if (!pool->bitmap) + return -ENOMEM; + xmfs_info(NULL, "pool init nr %d slot_size %ld total %ld", + pool->nr_slots, pool->slot_size, pool->total_size); + spin_lock_init(&pool->lock); + init_waitqueue_head(&pool->waitq); + + return 0; +} + +static void xmfs_urma_pool_destroy(struct xmfs_urma_pool *pool) +{ + bitmap_free(pool->bitmap); +} + +void xmfs_urma_unregister(struct xmfs_sb_info *sbi) +{ + if (!sbi) + return; + for (int i = 0; i < 5; i++) + if (sbi->urma_cfg.jetty[i]) + ubcore_delete_jetty(sbi->urma_cfg.jetty[i]); + if (sbi->urma_cfg.jfr) + ubcore_delete_jfr(sbi->urma_cfg.jfr); + if (sbi->urma_cfg.jfc) + ubcore_delete_jfc(sbi->urma_cfg.jfc); + if (sbi->urma_cfg.local_seg) + ubcore_unregister_seg(sbi->urma_cfg.local_seg); + if (sbi->urma_cfg.urma_local_va) + vfree(sbi->urma_cfg.urma_local_va); + if (sbi->urma_cfg.ubc_dev) + ubcore_unregister_client(&sbi->urma_cfg.ubc_client); + xmfs_urma_pool_destroy(&sbi->urma_cfg.pool); + xmfs_urma_pool_destroy(&sbi->urma_cfg.send_pool); + if (sbi->urma_cfg.send_jetty) + ubcore_delete_jetty(sbi->urma_cfg.send_jetty); + if (sbi->urma_cfg.send_jfr) + ubcore_delete_jfr(sbi->urma_cfg.send_jfr); + if (sbi->urma_cfg.send_jfc) + ubcore_delete_jfc(sbi->urma_cfg.send_jfc); + if (sbi->urma_cfg.recv_jfc) + ubcore_delete_jfc(sbi->urma_cfg.recv_jfc); + if (sbi->urma_cfg.send_seg) + ubcore_unregister_seg(sbi->urma_cfg.send_seg); + if (sbi->urma_cfg.recv_seg) + ubcore_unregister_seg(sbi->urma_cfg.recv_seg); + if (sbi->urma_cfg.recv_buf) + vfree(sbi->urma_cfg.recv_buf); + if (sbi->urma_cfg.send_buf) + vfree(sbi->urma_cfg.send_buf); + xa_destroy(&sbi->urma_cfg.grant_waits); + /* g_urma_client = NULL; */ +} + +static void *urma_pool_acquire(struct xmfs_urma_pool *pool, u32 *slot) +{ + unsigned long flags; + u32 idx; + int ret; + + for (;;) { + spin_lock_irqsave(&pool->lock, flags); + + idx = find_first_zero_bit(pool->bitmap, pool->nr_slots); + + if (idx < pool->nr_slots) { + __set_bit(idx, pool->bitmap); + pool->free_slots--; + spin_unlock_irqrestore(&pool->lock, flags); + + if (slot) + *slot = idx; + return pool->base + idx * pool->slot_size; + } + + spin_unlock_irqrestore(&pool->lock, flags); + + ret = wait_event_interruptible_timeout(pool->waitq, + READ_ONCE(pool->free_slots) >= + 1, + msecs_to_jiffies(25000)); + if (ret <= 0) + return NULL; + } + + return NULL; +} + +void *urma_pool_acquire_nslots(struct xmfs_urma_pool *pool, int nr, u32 *slots) +{ + unsigned long flags; + u32 idx, pos; + int found; + int ret; + + if (unlikely(nr <= 0 || nr > pool->nr_slots)) + return NULL; + + for (;;) { + found = 0; + pos = 0; + + spin_lock_irqsave(&pool->lock, flags); + + /* 空闲slot数量都不够,直接等待 */ + if (pool->free_slots < nr) + goto retry; + + /* 找到 nr 个空闲slot */ + while (found < nr) { + idx = find_next_zero_bit(pool->bitmap, pool->nr_slots, + pos); + + if (idx >= pool->nr_slots) + break; + + __set_bit(idx, pool->bitmap); + slots[found++] = idx; + pos = idx + 1; + } + + /* 成功拿到全部slot */ + if (likely(found == nr)) { + void *addr; + + pool->free_slots -= nr; + + addr = pool->base + slots[0] * pool->slot_size; + + spin_unlock_irqrestore(&pool->lock, flags); + return addr; + } + + /* + * 理论上不会进入这里: + * free_slots >= nr,但扫描过程中没找到足够slot。 + * 为了健壮性仍然回滚。 + */ + while (found > 0) + __clear_bit(slots[--found], pool->bitmap); + +retry: + spin_unlock_irqrestore(&pool->lock, flags); + + ret = wait_event_interruptible_timeout(pool->waitq, + READ_ONCE(pool->free_slots) >= + nr, + msecs_to_jiffies(25000)); + if (ret <= 0) + return NULL; + } +} + +void urma_pool_release_nslots(struct xmfs_urma_pool *pool, int nr, + const u32 *slots) +{ + unsigned long flags; + int i; + + spin_lock_irqsave(&pool->lock, flags); + + for (i = 0; i < nr; i++) { + WARN_ON(!test_bit(slots[i], pool->bitmap)); + __clear_bit(slots[i], pool->bitmap); + } + + pool->free_slots += nr; + + spin_unlock_irqrestore(&pool->lock, flags); + + wake_up_all(&pool->waitq); +} + +void urma_pool_release(struct xmfs_urma_pool *pool, u32 slot) +{ + unsigned long flags; + + if (WARN_ON(slot >= pool->nr_slots || slot < 0)) + return; + + spin_lock_irqsave(&pool->lock, flags); + + if (!test_bit(slot, pool->bitmap)) + xmfs_info(NULL, "err release find bit %d empty", slot); + + __clear_bit(slot, pool->bitmap); + pool->free_slots++; + spin_unlock_irqrestore(&pool->lock, flags); + + wake_up(&pool->waitq); +} + +static void *xmfs_get_recv_buf(struct xmfs_sb_info *sbi, int idx) +{ + return (char *)sbi->urma_cfg.recv_buf + + idx * sizeof(struct xmfs_rw_msg); +} + +static int xmfs_post_recv(struct xmfs_sb_info *sbi, int idx) +{ + struct ubcore_jfr_wr *bad_wr = NULL; + struct ubcore_jfr_wr wr = {}; + struct ubcore_sge sge = {}; + int ret; + + sge.addr = (uint64_t)xmfs_get_recv_buf(sbi, idx); + sge.len = sizeof(struct xmfs_rw_msg); + sge.tseg = sbi->urma_cfg.recv_seg; + + wr.src.sge = &sge; + wr.src.num_sge = 1; + wr.user_ctx = idx; + wr.next = NULL; + + ret = ubcore_post_jetty_recv_wr(sbi->urma_cfg.send_jetty, &wr, &bad_wr); + if (ret) + xmfs_info(NULL, "err xmfs post recv buffer failed ret %d", ret); + + return ret; +} + +static void xmfs_process_recv(struct xmfs_sb_info *sbi, struct ubcore_cr *cr) +{ + struct wr_status *wrs; + + if (cr->status != UBCORE_CR_SUCCESS) { + xmfs_warning(sbi->sb, + "JFC receive completion failed: status %d opcode %d", + cr->status, cr->opcode); + if (cr->status == UBCORE_CR_WR_FLUSH_ERR_DONE) + return; + } + wrs = (struct wr_status *)(uintptr_t)cr->user_ctx; + if (!virt_addr_valid(wrs)) { + xmfs_info(NULL, "jfc callback got an invalid wrs %p", wrs); + return; + } + /* xmfs_info(sbi->sb, "qyf COMP user_ctx=%llx", cr->user_ctx); */ + if (!wrs || wrs->magic != XMFS_MAGIC) { + xmfs_info(NULL, + "err cannot get a correct wrs during process recv %p", + wrs); + return; + } + if (cr->status == UBCORE_CR_SUCCESS) + wrs->status = URMA_COMPLETE; + else { + wrs->status = URMA_ERROR; + wrs->ret = cr->status; + } + if (wrs->ops == XMFS_URMA_SEND) + xmfs_info(NULL, "err slave send jfc callback"); + + if (wrs->ops == XMFS_URMA_WRITE && wrs->slot != -1) { + if (wrs->slot >= sbi->urma_cfg.pool.nr_slots || wrs->slot < 0) + xmfs_info(NULL, + "err jfc process write got wrong slot %d %d", + wrs->slot, sbi->urma_cfg.pool.nr_slots); + else + urma_pool_release(&sbi->urma_cfg.pool, wrs->slot); + } + complete(&wrs->done); + xmfs_wrs_put(&wrs); +} + +static void xmfs_jfc_callback(struct ubcore_jfc *jfc) +{ + struct ubcore_cr cr[8]; + + if (!jfc) + return; + struct xmfs_sb_info *sbi = jfc->jfc_cfg.jfc_context; + int ret, i; + + if (!jfc || !sbi || sbi->magic != XMFS_MAGIC) + return; + + if (jfc != sbi->urma_cfg.jfc) { + xmfs_error(NULL, "err foreign jfc cr, drop it"); + ubcore_rearm_jfc(jfc, false); + return; + } + + /* xmfs_info(sbi->sb, "qyf jfc callback"); */ + + do { + ret = ubcore_poll_jfc(jfc, 1, cr); + if (ret < 0) { + xmfs_info(NULL, "err jfc poll failed %d", ret); + break; + } + + for (i = 0; i < ret; i++) + xmfs_process_recv(sbi, &cr[i]); + } while (ret > 0 && jfc && sbi && sbi->magic == XMFS_MAGIC); + + ret = ubcore_rearm_jfc(jfc, false); + if (ret < 0) + xmfs_info(NULL, "err jfc rearm failed %d", ret); +} + +static int xmfs_host_send(struct xmfs_sb_info *sbi, struct ubcore_cr *cr) +{ + u32 idx; + + idx = (u32)cr->user_ctx; + if (cr->status != UBCORE_CR_SUCCESS) + /* if (cr->status != 9 && cr->status != 10) */ + xmfs_warning(sbi->sb, + "send completion failed: node %d status %d index %d", + sbi->id, cr->status, idx); + if (cr->status == UBCORE_CR_WR_FLUSH_ERR_DONE) + return 0; + /* + * if (sbi->id != 0) + * xmfs_info(sbi->sb, + * "qyf send request callback send pool slot %d release", + * idx); + */ + urma_pool_release(&sbi->urma_cfg.send_pool, idx); + return 0; +} + +static void xmfs_host_reply_worker(struct work_struct *work) +{ + struct xmfs_send_work *sw = + container_of(work, struct xmfs_send_work, work); + struct xmfs_rw_msg grant = sw->grant; + struct xmfs_rw_msg *buf; + struct xmfs_sb_info *sbi = sw->sbi; + struct ubcore_jfs_wr *bad_send_wr = NULL; + struct ubcore_jfs_wr send_wr = {}; + struct ubcore_sge sge = {}; + int slot, ret; + + buf = (struct xmfs_rw_msg *)urma_pool_acquire(&sbi->urma_cfg.send_pool, + &slot); + if (!buf) { + xmfs_info(NULL, "cannot get the buf during host process recv"); + kfree(sw); + return; + } + memcpy(buf, &grant, sizeof(struct xmfs_rw_msg)); + + sge.addr = (uint64_t)buf; + sge.len = sizeof(grant); + sge.tseg = sbi->urma_cfg.send_seg; + + send_wr.opcode = UBCORE_OPC_SEND; + send_wr.tjetty = sbi->urma_cfg.send_tjettys[grant.slave_id]; + send_wr.send.src.sge = &sge; + send_wr.send.src.num_sge = 1; + send_wr.user_ctx = slot; + send_wr.flag.value = 0; + send_wr.flag.bs.complete_enable = 1; + send_wr.next = NULL; + + ret = ubcore_post_jetty_send_wr(sbi->urma_cfg.send_jetty, &send_wr, + &bad_send_wr); + if (ret) { + urma_pool_release(&sbi->urma_cfg.send_pool, slot); + xmfs_info(NULL, "err host send post err %d", ret); + } + kfree(sw); +} + +static int xmfs_host_recv(struct xmfs_sb_info *sbi, struct ubcore_cr *cr) +{ + struct xmfs_rw_msg *req; + struct xmfs_rw_msg grant; + u32 idx; + + if (cr->status != UBCORE_CR_SUCCESS) { + idx = (u32)cr->user_ctx; + /* if (cr->status != 9 && cr->status != 10) */ + xmfs_warning(sbi->sb, + "receive completion failed: node %d status %d opcode %d index %d", + sbi->id, cr->status, cr->opcode, idx); + xmfs_post_recv(sbi, idx); + return -EINVAL; + } + + idx = (u32)cr->user_ctx; + + if (sbi->id == 0) { + if (idx >= XMFS_DEPTH || idx < 0) { + xmfs_info(NULL, "err host recv idx %d is not correct", + idx); + return -EINVAL; + } + req = (struct xmfs_rw_msg *)xmfs_get_recv_buf(sbi, idx); + int slave_id = req->slave_id; + struct xmfs_send_work *sw = kmalloc(sizeof(*sw), GFP_ATOMIC); + + if (!sw) { + xmfs_post_recv(sbi, idx); + return 0; + } + /* + * xmfs_info(sbi->sb, + * "qyf host recv callback recv pool slot %d req id %d req ops %d", + * idx, req->request_id, req->ops); + */ + xmfs_host_handle_urma_request(sbi, slave_id, req, &sw->grant); + sw->sbi = sbi; + xmfs_post_recv(sbi, idx); + /* + * xmfs_info(sbi->sb, "host process req %d %d %d", + * req->request_id, req->ops, grant.log_slot_start); + */ + if (req->ops == XMFS_SETATTR) { + kfree(sw); + return 0; + } + + INIT_WORK(&sw->work, xmfs_host_reply_worker); + queue_work(sbi->send_wq, &sw->work); + /* + * buf = (struct xmfs_rw_msg *)urma_pool_acquire( + * &sbi->urma_cfg.send_pool, &slot); + * if (!buf) { + * xmfs_info(sbi->sb, + * "cannot get the buf during host process recv"); + * xmfs_post_recv(sbi, idx); + * return 0; + * } + * memcpy(buf, &grant, sizeof(struct xmfs_rw_msg)); + * xmfs_info(sbi->sb, + * "qyf host recv callback recv pool slot %d " + * "req id %d req ops %d send pool slot %d", + * idx, req->request_id, req->ops, slot); + * xmfs_post_recv(sbi, idx); + * + * sge.addr = (uint64_t)buf; + * sge.len = sizeof(grant); + * sge.tseg = sbi->urma_cfg.send_seg; + * + * send_wr.opcode = UBCORE_OPC_SEND; + * send_wr.tjetty = sbi->urma_cfg.send_tjettys[slave_id]; + * send_wr.send.src.sge = &sge; + * send_wr.send.src.num_sge = 1; + * send_wr.user_ctx = slot; + * send_wr.flag.value = 0; + * send_wr.flag.bs.complete_enable = 1; + * send_wr.next = NULL; + * + * ret = ubcore_post_jetty_send_wr(sbi->urma_cfg.send_jetty, + * &send_wr, &bad_send_wr); + * if (ret) { + * urma_pool_release(&sbi->urma_cfg.send_pool, slot); + * xmfs_info(sbi->sb, "err host send post err %d", ret); + * } + */ + } else { + if (idx >= XMFS_DEPTH || idx < 0) { + xmfs_info(NULL, "err slave recv idx %d is not correct", + idx); + return -EINVAL; + } + req = (struct xmfs_rw_msg *)xmfs_get_recv_buf(sbi, idx); + /* + * xmfs_info(sbi->sb, + * "qyf slave recv callback recv pool slot %d " + * "req id %d req ops %d type %d log addr %ld " + * "data addr %ld %ld data num %d %d version %ld", + * idx, req->request_id, req->ops, req->type, + * req->log_trunk_addr, req->data_addr, + * req->second_data_addr, req->data_page_count, + * req->second_data_pages, req->version); + */ + if (req->ops == XMFS_GC) { + xmfs_info(NULL, "slave receive gc req %d", req->status); + if (req->status == 1) + sbi->gc_ctl.gc_running = true; + else if (req->status == 0) { + sbi->gc_ctl.gc_running = false; + wake_up_all(&sbi->gc_ctl.wait); + } else if (req->status == 2) + sbi->gc_ctl.need_replay = true; + xmfs_post_recv(sbi, idx); + return 0; + } + + memcpy(&grant, req, sizeof(struct xmfs_rw_msg)); + xmfs_post_recv(sbi, idx); + /* xmfs_info(sbi->sb, "qyf slave post recv %d", idx); */ + struct xmfs_grant_wait *gw = + xa_load(&sbi->urma_cfg.grant_waits, grant.request_id); + if (gw) { + memcpy(&gw->grant, &grant, sizeof(struct xmfs_rw_msg)); + complete(&gw->done); + } else { + xmfs_error(NULL, "err cannot get the grant wait %ld", + grant.request_id); + } + } + + return 0; +} + +static void xmfs_send_jfc_callback(struct ubcore_jfc *jfc) +{ + struct ubcore_cr cr[8]; + + if (!jfc) + return; + struct xmfs_sb_info *sbi = jfc->jfc_cfg.jfc_context; + int ret, i; + + if (!jfc || !sbi || sbi->magic != XMFS_MAGIC) + return; + + if (jfc != sbi->urma_cfg.recv_jfc && jfc != sbi->urma_cfg.send_jfc) { + xmfs_error(NULL, "err foreign send jfc cr, drop it"); + ubcore_rearm_jfc(jfc, false); + return; + } + + /* xmfs_info(sbi->sb, "send callback %d", sbi->id); */ + do { + ret = ubcore_poll_jfc(jfc, 1, cr); + if (ret < 0) { + xmfs_info(NULL, "err jfc send poll failed %d", ret); + break; + } + + for (i = 0; i < ret; i++) + xmfs_host_send(sbi, &cr[i]); + } while (ret > 0 && jfc && sbi && sbi->magic == XMFS_MAGIC); + + ret = ubcore_rearm_jfc(jfc, false); + if (ret < 0) + xmfs_info(NULL, "err jfc recv rearm failed %d", ret); +} + +static void xmfs_recv_jfc_callback(struct ubcore_jfc *jfc) +{ + struct ubcore_cr cr[8]; + + if (!jfc) + return; + struct xmfs_sb_info *sbi = jfc->jfc_cfg.jfc_context; + int ret, i; + + if (!jfc || !sbi || sbi->magic != XMFS_MAGIC) + return; + + if (jfc != sbi->urma_cfg.recv_jfc && jfc != sbi->urma_cfg.send_jfc) { + xmfs_error(NULL, "err foreign recv jfc cr, drop it"); + ubcore_rearm_jfc(jfc, false); + return; + } + /* xmfs_info(sbi->sb, "recv callback %d", sbi->id); */ + do { + ret = ubcore_poll_jfc(jfc, 1, cr); + if (ret < 0) { + xmfs_info(NULL, "err jfc recv poll failed %d", ret); + break; + } + + for (i = 0; i < ret; i++) + xmfs_host_recv(sbi, &cr[i]); + } while (ret > 0 && jfc && sbi && sbi->magic == XMFS_MAGIC); + + ret = ubcore_rearm_jfc(jfc, false); + if (ret < 0) + xmfs_info(NULL, "err jfc recv rearm failed %d", ret); +} + +static int xmfs_register_send_recv(struct xmfs_sb_info *sbi, + struct xmfs_fs_context *ctx) +{ + struct ubcore_seg_cfg seg_cfg = { 0 }; + union ubcore_reg_seg_flag seg_flag = { 0 }; + struct ubcore_target_seg *seg; + struct ubcore_jfc_cfg jfc_cfg = { 0 }; + struct ubcore_jfc *jfc; + struct ubcore_jfr_cfg jfr_cfg = { 0 }; + struct ubcore_jfr *jfr; + struct ubcore_jetty *jetty; + struct ubcore_jetty_cfg jetty_cfg = { 0 }; + struct ubcore_eid_info *eid_info; + int err, cnt = 0; + + seg_flag.bs.token_policy = 0; + if (ctx->debug) + seg_flag.bs.access = XMFS_ACCESS_READ | XMFS_ACCESS_WRITE | + XMFS_ACCESS_ATOMIC | + XMFS_ACCESS_LOCAL_ONLY; + else + seg_flag.bs.access = XMFS_ACCESS_READ | XMFS_ACCESS_WRITE | + XMFS_ACCESS_ATOMIC; + seg_flag.bs.cacheable = UBCORE_NON_CACHEABLE; + + sbi->urma_cfg.recv_buf = + vzalloc(PAGE_ALIGN(XMFS_DEPTH * sizeof(struct xmfs_rw_msg))); + if (!sbi->urma_cfg.recv_buf) + return -ENOMEM; + + seg_cfg.va = (uint64_t)sbi->urma_cfg.recv_buf; + seg_cfg.len = PAGE_ALIGN(XMFS_DEPTH * sizeof(struct xmfs_rw_msg)); + seg_cfg.flag = seg_flag; + + seg = ubcore_register_seg(sbi->urma_cfg.ubc_dev, &seg_cfg, NULL); + if (IS_ERR_OR_NULL(seg)) { + xmfs_error(NULL, + "ubcore_register_seg failed during register recv"); + return -EOPNOTSUPP; + } + sbi->urma_cfg.recv_seg = seg; + + seg_flag.bs.token_policy = 0; + seg_flag.bs.access = XMFS_ACCESS_LOCAL_ONLY; + seg_flag.bs.cacheable = UBCORE_NON_CACHEABLE; + + sbi->urma_cfg.send_buf = + vzalloc(PAGE_ALIGN(XMFS_DEPTH * sizeof(struct xmfs_rw_msg))); + if (!sbi->urma_cfg.send_buf) + return -ENOMEM; + + seg_cfg.va = (uint64_t)sbi->urma_cfg.send_buf; + seg_cfg.len = PAGE_ALIGN(XMFS_DEPTH * sizeof(struct xmfs_rw_msg)); + seg_cfg.flag = seg_flag; + xmfs_urma_pool_init(&sbi->urma_cfg.send_pool, (void *)seg_cfg.va, + seg_cfg.len, sizeof(struct xmfs_rw_msg)); + + seg = ubcore_register_seg(sbi->urma_cfg.ubc_dev, &seg_cfg, NULL); + if (IS_ERR_OR_NULL(seg)) { + xmfs_error(NULL, + "ubcore_register_seg failed during register send"); + return -EOPNOTSUPP; + } + sbi->urma_cfg.send_seg = seg; + + jfc_cfg.depth = XMFS_DEPTH; + jfc_cfg.jfc_context = sbi; + jfc = ubcore_create_jfc(sbi->urma_cfg.ubc_dev, &jfc_cfg, + xmfs_recv_jfc_callback, NULL, NULL); + if (IS_ERR_OR_NULL(jfc)) { + xmfs_error(NULL, + "ubcore_create_jfc failed during register recv"); + return -EOPNOTSUPP; + } + sbi->urma_cfg.recv_jfc = jfc; + + jfc = ubcore_create_jfc(sbi->urma_cfg.ubc_dev, &jfc_cfg, + xmfs_send_jfc_callback, NULL, NULL); + if (IS_ERR_OR_NULL(jfc)) { + xmfs_error(NULL, + "ubcore_create_jfc failed during register send"); + return -EOPNOTSUPP; + } + sbi->urma_cfg.send_jfc = jfc; + + err = ubcore_rearm_jfc(sbi->urma_cfg.send_jfc, false); + if (err) + return -EOPNOTSUPP; + err = ubcore_rearm_jfc(sbi->urma_cfg.recv_jfc, false); + if (err) + return -EOPNOTSUPP; + + jfr_cfg.depth = XMFS_DEPTH; + jfr_cfg.trans_mode = UBCORE_TP_RM; + jfr_cfg.min_rnr_timer = (u8)19U; + jfr_cfg.max_sge = 1; + jfr_cfg.jfc = sbi->urma_cfg.recv_jfc; + jfr = ubcore_create_jfr(sbi->urma_cfg.ubc_dev, &jfr_cfg, NULL, NULL); + if (IS_ERR_OR_NULL(jfr)) { + xmfs_error(NULL, + "ubcore_create_jfr failed during register send"); + return -EOPNOTSUPP; + } + sbi->urma_cfg.send_jfr = jfr; + + jetty_cfg.trans_mode = UBCORE_TP_RM; + jetty_cfg.eid_index = ctx->host ? 0 : 1; + jetty_cfg.max_send_sge = 1; + jetty_cfg.max_recv_sge = 1; + jetty_cfg.rnr_retry = 7; + jetty_cfg.min_rnr_timer = 20U; + jetty_cfg.err_timeout = 20; + jetty_cfg.jfs_depth = XMFS_DEPTH; + jetty_cfg.jfr_depth = XMFS_DEPTH; + jetty_cfg.send_jfc = sbi->urma_cfg.send_jfc; + jetty_cfg.recv_jfc = sbi->urma_cfg.recv_jfc; + jetty_cfg.jfr = jfr; + jetty_cfg.flag.bs.share_jfr = 1; + jetty = ubcore_create_jetty(sbi->urma_cfg.ubc_dev, &jetty_cfg, NULL, + NULL); + if (IS_ERR_OR_NULL(jetty)) { + xmfs_error(NULL, + "ubcore_create_jetty failed during register send"); + return -EOPNOTSUPP; + } + sbi->urma_cfg.send_jetty = jetty; + + eid_info = ubcore_get_eid_list(sbi->urma_cfg.ubc_dev, &cnt); + if (!eid_info) { + xmfs_error(NULL, + "ubcore_get_eid_list failed during register send"); + return -EOPNOTSUPP; + } + + sbi->urma_cfg.send_info.urma_send_va = + (u64)(uintptr_t)sbi->urma_cfg.send_buf; + sbi->urma_cfg.send_info.urma_recv_va = + (u64)(uintptr_t)sbi->urma_cfg.recv_buf; + sbi->urma_cfg.send_info.urma_eid_subnet_prefix = + eid_info->eid.in6.subnet_prefix; + sbi->urma_cfg.send_info.urma_eid_interface_id = + eid_info->eid.in6.interface_id; + sbi->urma_cfg.send_info.urma_jetty_id = jetty->jetty_id.id; + sbi->urma_cfg.send_info.urma_send_token_id = + sbi->urma_cfg.send_seg->seg.token_id; + sbi->urma_cfg.send_info.urma_recv_token_id = + sbi->urma_cfg.recv_seg->seg.token_id; + xmfs_info(NULL, "send info %lld %lld %lld %lld %d %d %d", + sbi->urma_cfg.send_info.urma_send_va, + sbi->urma_cfg.send_info.urma_recv_va, + sbi->urma_cfg.send_info.urma_eid_subnet_prefix, + sbi->urma_cfg.send_info.urma_eid_interface_id, + sbi->urma_cfg.send_info.urma_jetty_id, + sbi->urma_cfg.send_info.urma_send_token_id, + sbi->urma_cfg.send_info.urma_recv_token_id); + + ubcore_free_eid_list(eid_info); + + for (int i = 0; i < XMFS_DEPTH; i++) + xmfs_post_recv(sbi, i); + + return 0; +} + +int xmfs_urma_register(struct xmfs_sb_info *sbi, struct xmfs_fs_context *ctx) +{ + struct ubcore_seg_cfg seg_cfg = { 0 }; + union ubcore_reg_seg_flag seg_flag = { 0 }; + struct ubcore_target_seg *seg; + struct ubcore_jfc_cfg jfc_cfg = { 0 }; + struct ubcore_jfc *jfc; + struct ubcore_jfr_cfg jfr_cfg = { 0 }; + struct ubcore_jfr *jfr; + struct ubcore_jetty *jetty; + struct ubcore_jetty_cfg jetty_cfg = { 0 }; + struct ubcore_eid_info *eid_info; + int err, cnt = 0; + + if (!ctx->host) { + sbi->urma_cfg.urma_remote_va = ctx->urma_va; + sbi->urma_cfg.urma_eid_subnet_prefix = + ctx->urma_eid_subnet_prefix; + sbi->urma_cfg.urma_eid_interface_id = + ctx->urma_eid_interface_id; + sbi->urma_cfg.urma_jetty_id = ctx->urma_jetty_id; + sbi->urma_cfg.urma_token_id = ctx->urma_token_id; + sbi->urma_cfg.last_log_start = XMFS_DEFAULT_CHUNK_SIZE * 3; + sbi->urma_cfg.last_data_start = XMFS_DEFAULT_CHUNK_SIZE * 3; + } + + xmfs_info(NULL, "ctx name %ld %s", strlen(ctx->dev_name), + ctx->dev_name); + memcpy(g_dev_name, ctx->dev_name, strlen(ctx->dev_name)); + xmfs_info(NULL, "g dev name %s", g_dev_name); + + INIT_LIST_HEAD(&sbi->urma_cfg.ubc_client.list_node); + sbi->urma_cfg.ubc_client.client_name = "xmfs_urma_client"; + sbi->urma_cfg.ubc_client.add = xmfs_add_udma_device; + sbi->urma_cfg.ubc_client.remove = xmfs_remove_udma_device; + xa_init(&sbi->urma_cfg.grant_waits); + + err = ubcore_register_client(&sbi->urma_cfg.ubc_client); + if (err) { + xmfs_error(NULL, "ubcore_register_client failed"); + return -EOPNOTSUPP; + } + xmfs_info(NULL, "register client done %d %d %d %d", XMFS_ACCESS_READ, + XMFS_ACCESS_WRITE, XMFS_ACCESS_ATOMIC, + XMFS_ACCESS_LOCAL_ONLY); + + seg_flag.bs.token_policy = 0; + if (ctx->debug) + seg_flag.bs.access = XMFS_ACCESS_READ | XMFS_ACCESS_WRITE | + XMFS_ACCESS_ATOMIC | + XMFS_ACCESS_LOCAL_ONLY; + else + seg_flag.bs.access = XMFS_ACCESS_READ | XMFS_ACCESS_WRITE | + XMFS_ACCESS_ATOMIC; + seg_flag.bs.non_pin = 1; + + if (ctx->host) { + seg_cfg.va = (u64)(uintptr_t)sbi->kaddr; + seg_cfg.len = sbi->len; + } else { + sbi->urma_cfg.urma_local_va = vzalloc(21474836480); + if (!sbi->urma_cfg.urma_local_va) { + xmfs_error(NULL, "vzalloc local va failed"); + return -ENOMEM; + } + seg_cfg.va = (u64)(uintptr_t)sbi->urma_cfg.urma_local_va; + seg_cfg.len = 21474836480; + } + xmfs_urma_pool_init(&sbi->urma_cfg.pool, (void *)seg_cfg.va, + seg_cfg.len, PAGE_SIZE); + seg_cfg.flag = seg_flag; + sbi->urma_cfg.ubc_dev = g_urma_dev[0]; + seg = ubcore_register_seg(sbi->urma_cfg.ubc_dev, &seg_cfg, NULL); + if (IS_ERR_OR_NULL(seg)) { + xmfs_error(NULL, "ubcore_register_seg failed"); + return -EOPNOTSUPP; + } + sbi->urma_cfg.local_seg = seg; + + if (ctx->debug) + jfc_cfg.depth = 2048; + else + jfc_cfg.depth = 524288; + jfc_cfg.jfc_context = sbi; + jfc = ubcore_create_jfc(sbi->urma_cfg.ubc_dev, &jfc_cfg, + xmfs_jfc_callback, NULL, NULL); + if (IS_ERR_OR_NULL(jfc)) { + xmfs_error(NULL, "ubcore_create_jfc failed"); + return -EOPNOTSUPP; + } + sbi->urma_cfg.jfc = jfc; + + err = ubcore_rearm_jfc(jfc, false); + if (err) { + xmfs_error(NULL, "ubcore_rearm_jfc failed"); + return -EOPNOTSUPP; + } + + if (ctx->debug) + jfr_cfg.depth = 2048; + else + jfr_cfg.depth = 16384; + jfr_cfg.trans_mode = UBCORE_TP_RM; + jfr_cfg.min_rnr_timer = (u8)19U; + jfr_cfg.max_sge = 1; + jfr_cfg.jfc = jfc; + jfr = ubcore_create_jfr(sbi->urma_cfg.ubc_dev, &jfr_cfg, NULL, NULL); + if (IS_ERR_OR_NULL(jfr)) { + xmfs_error(NULL, "ubcore_create_jfr failed"); + return -EOPNOTSUPP; + } + sbi->urma_cfg.jfr = jfr; + + jetty_cfg.trans_mode = UBCORE_TP_RM; + jetty_cfg.eid_index = ctx->host ? 0 : 1; + jetty_cfg.max_send_sge = 1; + jetty_cfg.max_recv_sge = 1; + jetty_cfg.rnr_retry = 7; + jetty_cfg.min_rnr_timer = 20U; + jetty_cfg.err_timeout = 20; + if (ctx->debug) + jetty_cfg.jfs_depth = 8192; + else + jetty_cfg.jfs_depth = 2048; + jetty_cfg.jfr_depth = jfr_cfg.depth; + jetty_cfg.send_jfc = jfc; + jetty_cfg.recv_jfc = jfc; + jetty_cfg.jfr = jfr; + jetty_cfg.flag.bs.share_jfr = 1; + for (int i = 0; i < 5; i++) { + jetty = ubcore_create_jetty(sbi->urma_cfg.ubc_dev, &jetty_cfg, + NULL, NULL); + if (IS_ERR_OR_NULL(jetty)) { + xmfs_error(NULL, "ubcore_create_jetty failed"); + return -EOPNOTSUPP; + } + sbi->urma_cfg.jetty[i] = jetty; + } + + eid_info = ubcore_get_eid_list(sbi->urma_cfg.ubc_dev, &cnt); + if (!eid_info) { + xmfs_error(NULL, "ubcore_get_eid_list failed"); + return -EOPNOTSUPP; + } + if (ctx->host) { + sbi->urma_cfg.urma_remote_va = (u64)(uintptr_t)sbi->kaddr; + sbi->urma_cfg.urma_eid_subnet_prefix = + eid_info->eid.in6.subnet_prefix; + sbi->urma_cfg.urma_eid_interface_id = + eid_info->eid.in6.interface_id; + sbi->urma_cfg.urma_jetty_id = jetty->jetty_id.id; + sbi->urma_cfg.urma_token_id = seg->seg.token_id; + } else { + sbi->urma_cfg.own_info.urma_remote_va = + (u64)(uintptr_t)sbi->urma_cfg.urma_local_va; + sbi->urma_cfg.own_info.urma_eid_subnet_prefix = + eid_info->eid.in6.subnet_prefix; + sbi->urma_cfg.own_info.urma_eid_interface_id = + eid_info->eid.in6.interface_id; + sbi->urma_cfg.own_info.urma_jetty_id = jetty->jetty_id.id; + sbi->urma_cfg.own_info.urma_token_id = seg->seg.token_id; + } + + ubcore_free_eid_list(eid_info); + + xmfs_info(NULL, "register done"); + return xmfs_register_send_recv(sbi, ctx); +} + +void xmfs_urma_unimport(struct xmfs_sb_info *sbi) +{ + if (!sbi) + return; + if (sbi->urma_cfg.remote_seg) + ubcore_unimport_seg(sbi->urma_cfg.remote_seg); + if (sbi->urma_cfg.tjetty) { + /* ubcore_unbind_jetty(sbi->urma_cfg.jetty); */ + ubcore_unimport_jetty(sbi->urma_cfg.tjetty); + } + for (int i = 0; i < 64; i++) { + if (sbi->urma_cfg.tjettys[i]) + ubcore_unimport_jetty(sbi->urma_cfg.tjettys[i]); + if (sbi->urma_cfg.send_tjettys[i]) + ubcore_unimport_jetty(sbi->urma_cfg.send_tjettys[i]); + } +} + +int xmfs_import_jetty(struct xmfs_sb_info *sbi, struct urma_info info, + int index, bool is_send) +{ + struct ubcore_tjetty_cfg cfg = { 0 }; + struct ubcore_get_tp_cfg tp_flag = { 0 }; + struct ubcore_active_tp_cfg active_tp_cfg = { 0 }; + struct ubcore_tp_info tpid_info = { 0 }; + struct ubcore_tjetty *tjetty; + struct ubcore_eid_info *eid_info; + uint32_t tp_cnt = 1; + int err, cnt = 0; + + eid_info = ubcore_get_eid_list(sbi->urma_cfg.ubc_dev, &cnt); + if (!eid_info) { + xmfs_error(NULL, "ubcore_get_eid_list failed"); + return -EOPNOTSUPP; + } + xmfs_info(NULL, "get eid list done"); + for (int i = 0; i < cnt; i++) { + xmfs_info(NULL, "eid get during import %lld %lld", + eid_info->eid.in6.subnet_prefix, + eid_info->eid.in6.interface_id); + } + memcpy(&cfg.id.eid, &eid_info->eid, sizeof(union ubcore_eid)); + cfg.id.eid.in6.subnet_prefix = info.urma_eid_subnet_prefix; + cfg.id.eid.in6.interface_id = info.urma_eid_interface_id; + cfg.id.id = info.urma_jetty_id; + cfg.trans_mode = UBCORE_TP_RM; + cfg.type = UBCORE_JETTY; + cfg.eid_index = 1; + + tp_flag.flag.bs.ctp = 1; + tp_flag.trans_mode = UBCORE_TP_RM; + memcpy(&tp_flag.peer_eid, &eid_info->eid, sizeof(union ubcore_eid)); + tp_flag.peer_eid.in6.subnet_prefix = info.urma_eid_subnet_prefix; + tp_flag.peer_eid.in6.interface_id = info.urma_eid_interface_id; + memcpy(&tp_flag.local_eid, &eid_info->eid, sizeof(union ubcore_eid)); + xmfs_info(NULL, "node %d import jetty %lld %lld %d", sbi->id, + info.urma_eid_interface_id, info.urma_eid_subnet_prefix, + info.urma_jetty_id); + err = ubcore_get_tp_list(sbi->urma_cfg.ubc_dev, &tp_flag, &tp_cnt, + &tpid_info, NULL); + if (err) { + xmfs_error(NULL, "ubcore_get_tp_list failed."); + return -EOPNOTSUPP; + } + xmfs_info(NULL, "get tp list done"); + active_tp_cfg.tp_handle = tpid_info.tp_handle; + if (sbi->urma_cfg.ubc_dev->ops->import_jetty) + tjetty = ubcore_import_jetty(sbi->urma_cfg.ubc_dev, &cfg, NULL); + else + tjetty = ubcore_import_jetty_ex(sbi->urma_cfg.ubc_dev, &cfg, + &active_tp_cfg, NULL); + if (IS_ERR_OR_NULL(tjetty)) { + xmfs_error(NULL, "ubcore_import_jetty_ex failed."); + return -EOPNOTSUPP; + } + if (!is_send) { + if (sbi->urma_cfg.tjettys[index]) { + xmfs_info(NULL, "unimport tjetty %d", index); + err = ubcore_unimport_jetty(sbi->urma_cfg.tjettys[index]); + xmfs_info(NULL, "unimport tjetty %d ret %d", index, + err); + } + sbi->urma_cfg.tjettys[index] = tjetty; + } else { + if (sbi->urma_cfg.send_tjettys[index]) { + xmfs_info(NULL, "unimport send tjetty %d", index); + err = ubcore_unimport_jetty(sbi->urma_cfg.send_tjettys[index]); + xmfs_info(NULL, "unimport send tjetty %d ret %d", index, + err); + } + sbi->urma_cfg.send_tjettys[index] = tjetty; + } + + ubcore_free_eid_list(eid_info); + xmfs_info(NULL, "import jetty done"); + + return 0; +} + +int xmfs_urma_import(struct xmfs_sb_info *sbi) +{ + struct ubcore_tjetty_cfg cfg = { 0 }; + struct ubcore_get_tp_cfg tp_flag = { 0 }; + struct ubcore_active_tp_cfg active_tp_cfg = { 0 }; + struct ubcore_tp_info tpid_info = { 0 }; + struct ubcore_target_seg_cfg seg_cfg = { 0 }; + struct ubcore_tjetty *tjetty; + struct ubcore_target_seg *tseg; + struct ubcore_eid_info *eid_info; + uint32_t tp_cnt = 1; + int err, cnt = 0; + + xmfs_info(NULL, "start import"); + eid_info = ubcore_get_eid_list(sbi->urma_cfg.ubc_dev, &cnt); + if (!eid_info) { + xmfs_error(NULL, "ubcore_get_eid_list failed"); + return -EOPNOTSUPP; + } + for (int i = 0; i < cnt; i++) { + xmfs_info(NULL, "eid get during import %lld %lld", + eid_info->eid.in6.subnet_prefix, + eid_info->eid.in6.interface_id); + } + xmfs_info(NULL, "get eid list done %lld %lld", + sbi->urma_cfg.urma_eid_subnet_prefix, + sbi->urma_cfg.urma_eid_interface_id); + memcpy(&cfg.id.eid, &eid_info->eid, sizeof(union ubcore_eid)); + cfg.id.eid.in6.subnet_prefix = sbi->urma_cfg.urma_eid_subnet_prefix; + cfg.id.eid.in6.interface_id = sbi->urma_cfg.urma_eid_interface_id; + cfg.id.id = sbi->urma_cfg.urma_jetty_id; + cfg.trans_mode = UBCORE_TP_RM; + cfg.type = UBCORE_JETTY; + cfg.eid_index = 1; + + tp_flag.flag.bs.ctp = 1; + tp_flag.trans_mode = UBCORE_TP_RM; + memcpy(&tp_flag.peer_eid, &eid_info->eid, sizeof(union ubcore_eid)); + tp_flag.peer_eid.in6.subnet_prefix = + sbi->urma_cfg.urma_eid_subnet_prefix; + tp_flag.peer_eid.in6.interface_id = sbi->urma_cfg.urma_eid_interface_id; + memcpy(&tp_flag.local_eid, &eid_info->eid, sizeof(union ubcore_eid)); + + err = ubcore_get_tp_list(sbi->urma_cfg.ubc_dev, &tp_flag, &tp_cnt, + &tpid_info, NULL); + if (err) { + xmfs_error(NULL, "ubcore_get_tp_list failed."); + return -EOPNOTSUPP; + } + xmfs_info(NULL, "get tp list done"); + active_tp_cfg.tp_handle = tpid_info.tp_handle; + if (sbi->urma_cfg.ubc_dev->ops->import_jetty) + tjetty = ubcore_import_jetty(sbi->urma_cfg.ubc_dev, &cfg, NULL); + else + tjetty = ubcore_import_jetty_ex(sbi->urma_cfg.ubc_dev, &cfg, + &active_tp_cfg, NULL); + if (IS_ERR_OR_NULL(tjetty)) { + xmfs_error(NULL, "ubcore_import_jetty_ex failed."); + return -EOPNOTSUPP; + } + sbi->urma_cfg.tjetty = tjetty; + xmfs_info(NULL, "import jetty done"); + /* + * err = ubcore_bind_jetty_ex(sbi->urma_cfg.jetty, tjetty, + * &active_tp_cfg, NULL); + * if (err) { + * xmfs_error(sbi->sb, "ubcore_bind_jetty_ex failed."); + * return -EOPNOTSUPP; + * } + */ + + memcpy(&seg_cfg.seg.ubva.eid, &eid_info->eid, sizeof(union ubcore_eid)); + seg_cfg.seg.ubva.eid.in6.subnet_prefix = + sbi->urma_cfg.urma_eid_subnet_prefix; + seg_cfg.seg.ubva.eid.in6.interface_id = + sbi->urma_cfg.urma_eid_interface_id; + ubcore_free_eid_list(eid_info); + seg_cfg.seg.ubva.va = sbi->urma_cfg.urma_remote_va; + seg_cfg.seg.len = sbi->len; + seg_cfg.seg.token_id = sbi->urma_cfg.urma_token_id; + tseg = ubcore_import_seg(sbi->urma_cfg.ubc_dev, &seg_cfg, NULL); + if (IS_ERR_OR_NULL(tseg)) { + xmfs_error(NULL, "ubcore_import_seg failed."); + return -EOPNOTSUPP; + } + xmfs_info(NULL, "import seg done"); + sbi->urma_cfg.remote_seg = tseg; + + return 0; +} + +static int xmfs_urma_send_and_wait(struct xmfs_sb_info *sbi, + struct ubcore_jfs_wr *wr, + struct xmfs_urma_msg *msg) +{ + int err = 0; + struct ubcore_jfs_wr *bad_wr = NULL; + struct ubcore_jfs_wr *tmp; + struct ubcore_jfs_wr *retry_wr; + struct ubcore_jfs_wr *failed_head = NULL; + int ret = 0; + int retry; + int timeout; + unsigned int delay = 50; + unsigned int multi = 1; + + if (sbi->debug) + multi = 1000; + + if (sbi->debug) + timeout = msecs_to_jiffies(20000); + else + timeout = msecs_to_jiffies(20000); + + retry_wr = wr; + for (retry = 0; retry < XMFS_POST_RETRY_CNT && retry_wr; retry++) { + bad_wr = NULL; + err = ubcore_post_jetty_send_wr(sbi->urma_cfg.jetty[jiffies % 5], + retry_wr, &bad_wr); + if (!err) { + retry_wr = NULL; + break; + } + + /* + * xmfs_error(sbi->sb, + * "qyf post wr failed ret=%d retry=%d bad_wr=%px", + * err, retry, bad_wr); + */ + + if (bad_wr) + retry_wr = bad_wr; + usleep_range(delay * multi, delay * multi + 50 * multi); + delay = min(delay * 2, 5000U); + } + failed_head = retry_wr; + if (failed_head) { + xmfs_error(NULL, "still have unposted wr after retry=%d", + XMFS_POST_RETRY_CNT); + + for (tmp = failed_head; tmp; tmp = tmp->next) { + struct wr_status *wrs = + (struct wr_status *)(uintptr_t)tmp->user_ctx; + + kfree(wrs); + wrs = NULL; + } + + ret = -EAGAIN; + } + + for (tmp = wr; tmp != NULL && tmp != failed_head; tmp = tmp->next) { + struct wr_status *wrs = + (struct wr_status *)(uintptr_t)tmp->user_ctx; + err = wait_for_completion_interruptible_timeout(&wrs->done, + timeout); + if (err <= 0) { + xmfs_warning(sbi->sb, + "request wait failed: %d, op %d, status %d, offset %lld, length %lld", + err, wrs->ops, wrs->ret, msg->offset, + msg->len); + ret = -EAGAIN; + } + + if (wrs->status == URMA_ERROR) { + xmfs_error(NULL, + "urma send get err %d ret %d ops %d len %d %d", + wrs->status, wrs->ret, wrs->ops, + wr->rw.src.sge->len, wr->rw.dst.sge->len); + ret = -EAGAIN; + } + xmfs_wrs_put(&wrs); + } + + return ret; +} + +static int xmfs_urma_get_read_wr(struct xmfs_sb_info *sbi, + struct ubcore_jfs_wr **wr, + struct xmfs_urma_msg *msg) +{ + int trunk_size; + + if (sbi->debug) + trunk_size = 4096; + else + trunk_size = XMFS_URMA_DEFAULT_WR_SIZE; + + uint64_t wr_count = DIV_ROUND_UP(msg->len, trunk_size); + struct ubcore_jfs_wr *wr_ptr, *wr_pre = NULL; + *wr = vzalloc(wr_count * sizeof(struct ubcore_jfs_wr)); + if (!*wr) + return -ENOMEM; + + wr_ptr = *wr; + for (uint64_t read_count = 0; read_count < msg->len; + read_count += trunk_size) { + struct wr_status *wrs = + kzalloc(sizeof(struct wr_status), GFP_KERNEL); + if (!wrs) + return -ENOMEM; + + wr_ptr->rw.src.sge = vzalloc(sizeof(struct ubcore_sge)); + wr_ptr->rw.dst.sge = vzalloc(sizeof(struct ubcore_sge)); + if (!wr_ptr->rw.src.sge || !wr_ptr->rw.dst.sge) + return -ENOMEM; + + wrs->status = URMA_ISSUING; + init_completion(&wrs->done); + wrs->ret = 1; + wrs->ops = XMFS_URMA_READ; + wrs->magic = XMFS_MAGIC; + refcount_set(&wrs->refs, 1); + xmfs_wrs_get(wrs); + uint64_t read_len = read_count + trunk_size < msg->len ? + trunk_size : + msg->len - read_count; + if (wr_pre) + wr_pre->next = wr_ptr; + wr_ptr->opcode = UBCORE_OPC_READ; + wr_ptr->flag.bs.complete_enable = 1; + wr_ptr->tjetty = sbi->urma_cfg.tjetty; + wr_ptr->rw.src.sge->addr = + sbi->urma_cfg.urma_remote_va + msg->offset + read_count; + wr_ptr->rw.src.sge->len = read_len; + wr_ptr->rw.src.sge->tseg = sbi->urma_cfg.remote_seg; + wr_ptr->rw.src.num_sge = 1; + wr_ptr->rw.dst.sge->addr = + (uint64_t)msg->local_addr + read_count; + wr_ptr->rw.dst.sge->len = read_len; + wr_ptr->rw.dst.sge->tseg = sbi->urma_cfg.local_seg; + wr_ptr->rw.dst.num_sge = 1; + wr_ptr->user_ctx = (uint64_t)(uintptr_t)wrs; + wr_ptr->next = NULL; + wr_pre = wr_ptr++; + } + msg->wr_cnt = wr_count; + return 0; +} + +static int xmfs_urma_get_write_wr(struct xmfs_sb_info *sbi, + struct ubcore_jfs_wr **wr, + struct xmfs_urma_msg *msg) +{ + *wr = vzalloc(sizeof(struct ubcore_jfs_wr)); + if (!*wr) + return -ENOMEM; + + struct wr_status *wrs = kzalloc(sizeof(struct wr_status), GFP_KERNEL); + + if (!wrs) + return -ENOMEM; + + (*wr)->rw.src.sge = vzalloc(sizeof(struct ubcore_sge)); + (*wr)->rw.dst.sge = vzalloc(sizeof(struct ubcore_sge)); + if (!(*wr)->rw.src.sge || !(*wr)->rw.dst.sge) + return -ENOMEM; + + wrs->status = URMA_ISSUING; + init_completion(&wrs->done); + wrs->ret = 2; + wrs->ops = XMFS_URMA_WRITE; + wrs->magic = XMFS_MAGIC; + wrs->slot = -1; /* msg.slot */ + refcount_set(&wrs->refs, 1); + xmfs_wrs_get(wrs); + memcpy(msg->write_buffer, msg->local_addr, msg->len); + (*wr)->opcode = UBCORE_OPC_WRITE; + (*wr)->flag.bs.complete_enable = 1; + (*wr)->tjetty = sbi->urma_cfg.tjetty; + (*wr)->rw.src.sge->addr = (uint64_t)msg->write_buffer; + (*wr)->rw.src.sge->len = msg->len; + (*wr)->rw.src.sge->tseg = sbi->urma_cfg.local_seg; + (*wr)->rw.src.num_sge = 1; + (*wr)->rw.dst.sge->addr = sbi->urma_cfg.urma_remote_va + msg->offset; + (*wr)->rw.dst.sge->len = msg->len; + (*wr)->rw.dst.sge->tseg = sbi->urma_cfg.remote_seg; + (*wr)->rw.dst.num_sge = 1; + (*wr)->user_ctx = (uint64_t)(uintptr_t)wrs; + (*wr)->next = NULL; + msg->wr_cnt = 1; + return 0; +} + +static int xmfs_urma_get_cas_wr(struct xmfs_sb_info *sbi, + struct ubcore_jfs_wr **wr, + struct xmfs_urma_msg *msg) +{ + *wr = vzalloc(sizeof(struct ubcore_jfs_wr)); + if (!*wr) + return -ENOMEM; + + struct wr_status *wrs = kzalloc(sizeof(struct wr_status), GFP_KERNEL); + + if (!wrs) + return -ENOMEM; + + (*wr)->cas.src = vzalloc(sizeof(struct ubcore_sge)); + (*wr)->cas.dst = vzalloc(sizeof(struct ubcore_sge)); + if (!(*wr)->cas.src || !(*wr)->cas.dst) + return -ENOMEM; + + wrs->status = URMA_ISSUING; + init_completion(&wrs->done); + wrs->ret = 3; + wrs->ops = XMFS_URMA_CAS; + wrs->magic = XMFS_MAGIC; + refcount_set(&wrs->refs, 1); + xmfs_wrs_get(wrs); + /* msg->local_addr = sbi->urma_cfg.urma_local_va; */ + (*wr)->opcode = UBCORE_OPC_CAS; + (*wr)->flag.bs.complete_enable = 1; + (*wr)->tjetty = sbi->urma_cfg.tjetty; + (*wr)->cas.src->addr = (uint64_t)msg->local_addr; + (*wr)->cas.src->len = msg->len; + (*wr)->cas.src->tseg = sbi->urma_cfg.local_seg; + (*wr)->cas.dst->addr = sbi->urma_cfg.urma_remote_va + msg->offset; + (*wr)->cas.dst->len = msg->len; + (*wr)->cas.dst->tseg = sbi->urma_cfg.remote_seg; + (*wr)->cas.cmp_data = msg->cas_cmp_data; + (*wr)->cas.swap_data = msg->cas_swap_data; + (*wr)->user_ctx = (uint64_t)(uintptr_t)wrs; + (*wr)->next = NULL; + msg->wr_cnt = 1; + return 0; +} + +static void xmfs_urma_free_wr(struct xmfs_urma_msg *msg, + struct ubcore_jfs_wr *wr, int err) +{ + struct ubcore_jfs_wr *first_wr = wr; + struct wr_status *wrs; + + if (!wr) + return; + while (wr) { + switch (msg->ops) { + case XMFS_URMA_READ: + case XMFS_URMA_WRITE: + vfree(wr->rw.src.sge); + vfree(wr->rw.dst.sge); + break; + case XMFS_URMA_CAS: + vfree(wr->cas.src); + vfree(wr->cas.dst); + break; + default: + break; + } + wrs = (struct wr_status *)wr->user_ctx; + if (wrs != NULL && err != -EAGAIN && err != 0) + kfree(wrs); + + wr = wr->next; + } + vfree(first_wr); +} + +int xmfs_pmem_write(struct xmfs_sb_info *sbi, unsigned long offset, + const void *buf, unsigned long size) +{ + switch (sbi->mode) { + case XMFS_SHARED_MEM_MODE: + case XMFS_HYBRID_MODE: + memcpy(sbi->kaddr + offset, buf, size); + return 0; +#ifdef CONFIG_XMFS_FS_URMA + case XMFS_URMA_MODE: { + unsigned long written = 0; + u32 slot; + + while (written < size) { + unsigned long chunk = size - written; + struct xmfs_urma_msg msg; + int err; + + if (chunk > PAGE_SIZE) + chunk = PAGE_SIZE; + memset(&msg, 0, sizeof(msg)); + msg.ops = XMFS_URMA_WRITE; + msg.offset = offset + written; + msg.len = chunk; + msg.local_addr = (void *)(buf + written); + msg.write_buffer = + urma_pool_acquire(&sbi->urma_cfg.pool, &slot); + if (!msg.write_buffer) { + xmfs_info(NULL, + "pmem write cannot get a buffer to write"); + return -EINVAL; + } + msg.slot = slot; + err = xmfs_urma_send(sbi, &msg); + urma_pool_release(&sbi->urma_cfg.pool, slot); + if (err) + return err; + + written += chunk; + } + return 0; + } +#endif + default: + return -ENODEV; + } +} +EXPORT_SYMBOL(xmfs_pmem_write); + +static int +xmfs_urma_get_read_wr_pages(struct xmfs_sb_info *sbi, struct ubcore_jfs_wr **wr, + struct xmfs_urma_msg *msg, unsigned long *offsets, + struct page **pages, unsigned long page_num, + void **kaddrs, int *cnt) +{ + uint64_t wr_count = DIV_ROUND_UP(msg->len, PAGE_SIZE); + struct ubcore_jfs_wr *wr_ptr, *wr_pre = NULL; + *wr = vzalloc(wr_count * sizeof(struct ubcore_jfs_wr)); + if (!*wr) + return -ENOMEM; + + wr_ptr = *wr; + for (uint64_t read_count = 0; read_count < page_num; read_count++) { + struct wr_status *wrs = + kzalloc(sizeof(struct wr_status), GFP_KERNEL); + if (!wrs) + return -ENOMEM; + + wr_ptr->rw.src.sge = vzalloc(sizeof(struct ubcore_sge)); + wr_ptr->rw.dst.sge = vzalloc(sizeof(struct ubcore_sge)); + if (!wr_ptr->rw.src.sge || !wr_ptr->rw.dst.sge) + return -ENOMEM; + + wrs->status = URMA_ISSUING; + init_completion(&wrs->done); + wrs->ret = 4; + wrs->ops = XMFS_URMA_READ; + wrs->magic = XMFS_MAGIC; + refcount_set(&wrs->refs, 1); + xmfs_wrs_get(wrs); + uint64_t read_len = PAGE_SIZE; + + if (wr_pre) + wr_pre->next = wr_ptr; + + kaddrs[read_count] = kmap_local_page(pages[read_count]); + *cnt = read_count + 1; + wr_ptr->opcode = UBCORE_OPC_READ; + wr_ptr->flag.bs.complete_enable = 1; + wr_ptr->tjetty = sbi->urma_cfg.tjetty; + wr_ptr->rw.src.sge->addr = + sbi->urma_cfg.urma_remote_va + offsets[read_count]; + wr_ptr->rw.src.sge->len = read_len; + wr_ptr->rw.src.sge->tseg = sbi->urma_cfg.remote_seg; + wr_ptr->rw.src.num_sge = 1; + wr_ptr->rw.dst.sge->addr = (uint64_t)kaddrs[read_count]; + wr_ptr->rw.dst.sge->len = read_len; + wr_ptr->rw.dst.sge->tseg = sbi->urma_cfg.local_seg; + wr_ptr->rw.dst.num_sge = 1; + wr_ptr->user_ctx = (uint64_t)(uintptr_t)wrs; + wr_ptr->next = NULL; + wr_pre = wr_ptr++; + } + return 0; +} + +static int xmfs_urma_get_write_wr_pages(struct xmfs_sb_info *sbi, + struct ubcore_jfs_wr **wr, + struct xmfs_urma_msg *msg, int *slots, + struct page **pages, + unsigned long page_num) +{ + uint64_t wr_count = DIV_ROUND_UP(msg->len, PAGE_SIZE); + struct ubcore_jfs_wr *wr_ptr, *wr_pre = NULL; + *wr = vzalloc(wr_count * sizeof(struct ubcore_jfs_wr)); + if (!*wr) + return -ENOMEM; + + wr_ptr = *wr; + void *write_buffer = + urma_pool_acquire_nslots(&sbi->urma_cfg.pool, page_num, slots); + if (!write_buffer) { + xmfs_info(NULL, "%s get write buffer failed", __func__); + return -EAGAIN; + } + for (uint64_t read_count = 0; read_count < page_num; read_count++) { + struct wr_status *wrs = + kzalloc(sizeof(struct wr_status), GFP_KERNEL); + if (!wrs) + return -ENOMEM; + + wr_ptr->rw.src.sge = vzalloc(sizeof(struct ubcore_sge)); + wr_ptr->rw.dst.sge = vzalloc(sizeof(struct ubcore_sge)); + if (!wr_ptr->rw.src.sge || !wr_ptr->rw.dst.sge) + return -ENOMEM; + + wrs->status = URMA_ISSUING; + init_completion(&wrs->done); + wrs->ret = 5; + wrs->ops = XMFS_URMA_WRITE; + wrs->magic = XMFS_MAGIC; + refcount_set(&wrs->refs, 1); + xmfs_wrs_get(wrs); + wrs->slot = slots[read_count]; + uint64_t read_len = PAGE_SIZE; + + if (wr_pre) + wr_pre->next = wr_ptr; + + void *kaddr = kmap_local_page(pages[read_count]); + + memcpy(write_buffer + read_count * PAGE_SIZE, kaddr, PAGE_SIZE); + kunmap_local(kaddr); + + wr_ptr->opcode = UBCORE_OPC_WRITE; + wr_ptr->flag.bs.complete_enable = 1; + wr_ptr->tjetty = sbi->urma_cfg.tjetty; + wr_ptr->rw.dst.sge->addr = sbi->urma_cfg.urma_remote_va + + msg->offset + read_count * PAGE_SIZE; + wr_ptr->rw.dst.sge->len = read_len; + wr_ptr->rw.dst.sge->tseg = sbi->urma_cfg.remote_seg; + wr_ptr->rw.dst.num_sge = 1; + wr_ptr->rw.src.sge->addr = + (uint64_t)(write_buffer + read_count * PAGE_SIZE); + wr_ptr->rw.src.sge->len = read_len; + wr_ptr->rw.src.sge->tseg = sbi->urma_cfg.local_seg; + wr_ptr->rw.src.num_sge = 1; + wr_ptr->user_ctx = (uint64_t)(uintptr_t)wrs; + wr_ptr->next = NULL; + wr_pre = wr_ptr++; + } + return 0; +} + +int xmfs_urma_send_pages(struct xmfs_sb_info *sbi, struct xmfs_urma_msg *msg, + unsigned long *offsets, struct page **pages, + unsigned long page_num) +{ + struct ubcore_jfs_wr *wr = NULL; + int *slots = vzalloc(sizeof(int) * page_num); + + if (!slots) + return -ENOMEM; + void **kaddrs = vzalloc(sizeof(void *) * page_num); + + if (!kaddrs) { + vfree(slots); + return -ENOMEM; + } + int err = 0; + int cnt = 0; + + switch (msg->ops) { + case XMFS_URMA_READ: + err = xmfs_urma_get_read_wr_pages(sbi, &wr, msg, offsets, pages, + page_num, kaddrs, &cnt); + break; + case XMFS_URMA_WRITE: + err = xmfs_urma_get_write_wr_pages(sbi, &wr, msg, slots, pages, + page_num); + break; + default: + xmfs_error(NULL, "What exactly are you going to do?"); + err = -EOPNOTSUPP; + } + + if (err) { + xmfs_error(NULL, "failed to alloc wr or local buffer %ld %d", + page_num, err); + goto free_ret; + } + + err = xmfs_urma_send_and_wait(sbi, wr, msg); + +free_ret: + if (msg->ops == XMFS_URMA_READ) { + for (int i = 0; i < cnt; i++) + kunmap_local(kaddrs[i]); + } + vfree(kaddrs); + xmfs_urma_free_wr(msg, wr, err); + vfree(slots); + return err; +} + +int xmfs_pmem_write_multi_pages(struct xmfs_sb_info *sbi, unsigned long offset, + struct page **pages, unsigned long page_num) +{ + switch (sbi->mode) { + case XMFS_SHARED_MEM_MODE: + case XMFS_HYBRID_MODE: + /* memcpy(sbi->kaddr + offset, buf, size); */ + return 0; +#ifdef CONFIG_XMFS_FS_URMA + case XMFS_URMA_MODE: { + if (page_num == 0) + return 0; + unsigned long chunk = PAGE_SIZE * page_num; + struct xmfs_urma_msg msg; + int err; + + memset(&msg, 0, sizeof(msg)); + msg.ops = XMFS_URMA_WRITE; + msg.offset = offset; + msg.len = chunk; + err = xmfs_urma_send_pages(sbi, &msg, &offset, pages, page_num); + if (err) + return err; + + return 0; + } +#endif + default: + return -ENODEV; + } +} +EXPORT_SYMBOL(xmfs_pmem_write_multi_pages); + +int xmfs_pmem_read_multi_pages(struct xmfs_sb_info *sbi, unsigned long *offsets, + struct page **pages, unsigned long page_num) +{ + switch (sbi->mode) { + case XMFS_SHARED_MEM_MODE: + case XMFS_HYBRID_MODE: + /* memcpy(sbi->kaddr + offset, buf, size); */ + return 0; +#ifdef CONFIG_XMFS_FS_URMA + case XMFS_URMA_MODE: { + if (page_num == 0) + return 0; + unsigned long chunk = PAGE_SIZE * page_num; + struct xmfs_urma_msg msg; + int err; + + memset(&msg, 0, sizeof(msg)); + msg.ops = XMFS_URMA_READ; + msg.len = chunk; + err = xmfs_urma_send_pages(sbi, &msg, offsets, pages, page_num); + if (err) + return err; + + return 0; + } +#endif + default: + return -ENODEV; + } +} +EXPORT_SYMBOL(xmfs_pmem_read_multi_pages); + +int xmfs_send_request(struct xmfs_sb_info *sbi, struct xmfs_rw_msg *req, + struct xmfs_grant_wait *gw, int id) +{ + struct ubcore_jfs_wr *bad_send_wr = NULL; + struct ubcore_jfs_wr send_wr = {}; + struct ubcore_sge sge = {}; + int ret, slot; + struct xmfs_rw_msg *buf; + + int timeout; + + if (sbi->debug) + timeout = msecs_to_jiffies(20000); + else + timeout = msecs_to_jiffies(20000); + + buf = urma_pool_acquire(&sbi->urma_cfg.send_pool, &slot); + if (!buf) { + xmfs_info(NULL, "cannot get buf to send request"); + return -ENOMEM; + } + /* xmfs_info(sbi->sb, "qyf acquire slot %d", slot); */ + memcpy(buf, req, sizeof(struct xmfs_rw_msg)); + sge.addr = (uint64_t)buf; + sge.len = sizeof(*req); + sge.tseg = sbi->urma_cfg.send_seg; + + send_wr.opcode = UBCORE_OPC_SEND; + send_wr.tjetty = sbi->urma_cfg.send_tjettys[id]; + send_wr.send.src.sge = &sge; + send_wr.send.src.num_sge = 1; + send_wr.flag.value = 0; + send_wr.flag.bs.complete_enable = 1; + send_wr.user_ctx = slot; + send_wr.next = NULL; + /* + * xmfs_info(sbi->sb, "qyf send request send pool slot %d id %d", + * slot, req->request_id); + */ + if (req->ops == XMFS_GC) + xmfs_info(NULL, "gc req send %d", req->status); + ret = ubcore_post_jetty_send_wr(sbi->urma_cfg.send_jetty, &send_wr, + &bad_send_wr); + if (ret) { + xmfs_info(NULL, "err send request failed %d, release %d", ret, + slot); + urma_pool_release(&sbi->urma_cfg.send_pool, slot); + if (req->ops != XMFS_SETATTR && req->ops != XMFS_GC) + xa_erase(&sbi->urma_cfg.grant_waits, req->request_id); + return ret; + } + + if (req->ops == XMFS_SETATTR || req->ops == XMFS_GC) + return 0; + + ret = wait_for_completion_interruptible_timeout(&gw->done, timeout); + xa_erase(&sbi->urma_cfg.grant_waits, req->request_id); + /* + * xmfs_info(sbi->sb, "qyf send request done send pool slot %d id %d", + * slot, req->request_id); + */ + if (ret <= 0) { + if (ret == 0) + ret = -ETIMEDOUT; + xmfs_warning(sbi->sb, + "slave request wait failed: %d, request ID %ld", + ret, req->request_id); + return ret; + } + if (gw->grant.type != MSG_RW_GRANT) { + xmfs_warning(sbi->sb, + "unexpected grant: type %d, request ID %ld, op %d", + gw->grant.type, gw->grant.request_id, + gw->grant.ops); + return -EINVAL; + } + + return 0; +} + +/* + * static int xmfs_urma_post_wr(struct xmfs_sb_info *sbi, + * struct ubcore_jfs_wr *wr) + * { + * struct ubcore_jfs_wr *bad_wr = NULL; + * int err = ubcore_post_jetty_send_wr(sbi->urma_cfg.jetty, wr, + * &bad_wr); + * + * if (err) + * xmfs_error(sbi->sb, + * "ubcore_post_jetty_send_wr failed %d", err); + * return err; + * } + * + * static int xmfs_urma_wait_chain(struct ubcore_jfs_wr *wr) + * { + * struct ubcore_jfs_wr *tmp; + * int ret = 0; + * + * for (tmp = wr; tmp != NULL; tmp = tmp->next) { + * struct wr_status *wrs = + * (struct wr_status *)(uintptr_t)tmp->user_ctx; + * int err; + * + * if (wrs->status != URMA_ISSUING) + * goto skip_wait; + * err = wait_for_completion_interruptible_timeout( + * &wrs->done, msecs_to_jiffies(20000)); + * if (err <= 0) { + * xmfs_info(NULL, + * "wait for wr timeout or interrupted %d ops %d wrs->ret %d", + * err, wrs->ops, wrs->ret); + * ret = -EAGAIN; + * } + * skip_wait: + * if (wrs->status == URMA_ERROR) { + * xmfs_error(NULL, + * "urma send get err status=%d ret=%d ops=%d len src=%d dst=%d", + * wrs->status, wrs->ret, wrs->ops, + * tmp->rw.src.sge->len, + * tmp->rw.dst.sge->len); + * ret = -EAGAIN; + * } + * xmfs_wrs_put(&wrs); + * } + * return ret; + * } + * + * static int xmfs_urma_send_and_wait_new(struct xmfs_sb_info *sbi, + * struct ubcore_jfs_wr *wr, + * struct xmfs_urma_msg *msg) + * { + * int err = xmfs_urma_post_wr(sbi, wr); + * + * if (err) + * return err; + * return xmfs_urma_wait_chain(wr); + * } + * + * struct xmfs_urma_pending { + * struct ubcore_jfs_wr *wr; + * void *write_buffer; + * int *slots; + * int nr_slots; + * struct xmfs_urma_msg msg; + * }; + * + * static void wr_chain_append(struct ubcore_jfs_wr **chain_head, + * struct ubcore_jfs_wr **tail, + * struct ubcore_jfs_wr *wr) + * { + * wr->next = NULL; + * if (*chain_head == NULL) { + * *chain_head = wr; + * *tail = wr; + * } else { + * (*tail)->next = wr; + * *tail = wr; + * } + * } + */ + +static int xmfs_urma_get_write_wr_ubuf(struct xmfs_sb_info *sbi, + struct ubcore_jfs_wr **wr, + struct xmfs_urma_msg *msg) +{ + *wr = vzalloc(sizeof(struct ubcore_jfs_wr)); + if (!*wr) + return -ENOMEM; + + struct wr_status *wrs = kzalloc(sizeof(struct wr_status), GFP_KERNEL); + + if (!wrs) + return -ENOMEM; + + (*wr)->rw.src.sge = vzalloc(sizeof(struct ubcore_sge)); + (*wr)->rw.dst.sge = vzalloc(sizeof(struct ubcore_sge)); + if (!(*wr)->rw.src.sge || !(*wr)->rw.dst.sge) + return -ENOMEM; + + wrs->status = URMA_ISSUING; + init_completion(&wrs->done); + wrs->ret = 7; + wrs->ops = XMFS_URMA_WRITE; + wrs->magic = XMFS_MAGIC; + wrs->slot = -1; /* msg.slot */ + refcount_set(&wrs->refs, 1); + xmfs_wrs_get(wrs); + (*wr)->opcode = UBCORE_OPC_WRITE; + (*wr)->flag.bs.complete_enable = 1; + (*wr)->tjetty = sbi->urma_cfg.tjetty; + (*wr)->rw.src.sge->addr = (uint64_t)msg->write_buffer; + (*wr)->rw.src.sge->len = msg->len; + (*wr)->rw.src.sge->tseg = sbi->urma_cfg.local_seg; + (*wr)->rw.src.num_sge = 1; + (*wr)->rw.dst.sge->addr = sbi->urma_cfg.urma_remote_va + msg->offset; + (*wr)->rw.dst.sge->len = msg->len; + (*wr)->rw.dst.sge->tseg = sbi->urma_cfg.remote_seg; + (*wr)->rw.dst.num_sge = 1; + (*wr)->user_ctx = (uint64_t)(uintptr_t)wrs; + (*wr)->next = NULL; + msg->wr_cnt = 1; + return 0; +} + +static int xmfs_urma_get_read_wr_ubuf(struct xmfs_sb_info *sbi, + struct ubcore_jfs_wr **wr, + struct xmfs_urma_msg *msg) +{ + int trunk_size; + + if (sbi->debug) + trunk_size = 4096; + else + trunk_size = msg->len; + + uint64_t wr_count = DIV_ROUND_UP(msg->len, trunk_size); + struct ubcore_jfs_wr *wr_ptr, *wr_pre = NULL; + *wr = vzalloc(wr_count * sizeof(struct ubcore_jfs_wr)); + if (!*wr) + return -ENOMEM; + + wr_ptr = *wr; + for (uint64_t read_count = 0; read_count < msg->len; + read_count += trunk_size) { + struct wr_status *wrs = + kzalloc(sizeof(struct wr_status), GFP_KERNEL); + if (!wrs) + return -ENOMEM; + + wr_ptr->rw.src.sge = vzalloc(sizeof(struct ubcore_sge)); + wr_ptr->rw.dst.sge = vzalloc(sizeof(struct ubcore_sge)); + if (!wr_ptr->rw.src.sge || !wr_ptr->rw.dst.sge) + return -ENOMEM; + + wrs->status = URMA_ISSUING; + init_completion(&wrs->done); + wrs->ret = 1; + wrs->ops = XMFS_URMA_READ; + wrs->magic = XMFS_MAGIC; + refcount_set(&wrs->refs, 1); + xmfs_wrs_get(wrs); + uint64_t read_len = read_count + trunk_size < msg->len ? + trunk_size : + msg->len - read_count; + if (wr_pre) + wr_pre->next = wr_ptr; + wr_ptr->opcode = UBCORE_OPC_READ; + wr_ptr->flag.bs.complete_enable = 1; + wr_ptr->tjetty = sbi->urma_cfg.tjetty; + wr_ptr->rw.src.sge->addr = + sbi->urma_cfg.urma_remote_va + msg->offset + read_count; + wr_ptr->rw.src.sge->len = read_len; + wr_ptr->rw.src.sge->tseg = sbi->urma_cfg.remote_seg; + wr_ptr->rw.src.num_sge = 1; + wr_ptr->rw.dst.sge->addr = + (uint64_t)msg->local_addr + read_count; + wr_ptr->rw.dst.sge->len = read_len; + wr_ptr->rw.dst.sge->tseg = sbi->urma_cfg.local_seg; + wr_ptr->rw.dst.num_sge = 1; + wr_ptr->user_ctx = (uint64_t)(uintptr_t)wrs; + wr_ptr->next = NULL; + wr_pre = wr_ptr++; + } + msg->wr_cnt = wr_count; + + /* + * *wr = (struct ubcore_jfs_wr *)vzalloc( + * sizeof(struct ubcore_jfs_wr)); + * if (!*wr) + * return -ENOMEM; + * struct wr_status *wrs = vzalloc(sizeof(struct wr_status)); + * if (!wrs) + * return -ENOMEM; + * (*wr)->rw.src.sge = vzalloc(sizeof(struct ubcore_sge)); + * (*wr)->rw.dst.sge = vzalloc(sizeof(struct ubcore_sge)); + * if (!(*wr)->rw.src.sge || !(*wr)->rw.dst.sge) + * return -ENOMEM; + * wrs->status = URMA_ISSUING; + * init_completion(&wrs->done); + * wrs->ret = 8; + * wrs->ops = XMFS_URMA_READ; + * wrs->magic = XMFS_MAGIC; + * wrs->slot = -1; + * refcount_set(&wrs->refs, 1); + * xmfs_wrs_get(wrs); + * (*wr)->opcode = UBCORE_OPC_READ; + * (*wr)->flag.bs.complete_enable = 1; + * (*wr)->tjetty = sbi->urma_cfg.tjetty; + * (*wr)->rw.dst.sge->addr = (uint64_t)msg->local_addr; + * (*wr)->rw.dst.sge->len = msg->len; + * (*wr)->rw.dst.sge->tseg = sbi->urma_cfg.local_seg; + * (*wr)->rw.dst.num_sge = 1; + * (*wr)->rw.src.sge->addr = + * sbi->urma_cfg.urma_remote_va + msg->offset; + * (*wr)->rw.src.sge->len = msg->len; + * (*wr)->rw.src.sge->tseg = sbi->urma_cfg.remote_seg; + * (*wr)->rw.src.num_sge = 1; + * (*wr)->user_ctx = (uint64_t)(uintptr_t)wrs; + * (*wr)->next = NULL; + * msg->wr_cnt = 1; + */ + return 0; +} + +static int xmfs_urma_send_ubuf(struct xmfs_sb_info *sbi, + struct xmfs_urma_msg *msg) +{ + struct ubcore_jfs_wr *wr = NULL; + int err = 0; + + switch (msg->ops) { + case XMFS_URMA_READ: + err = xmfs_urma_get_read_wr_ubuf(sbi, &wr, msg); + break; + case XMFS_URMA_WRITE: + err = xmfs_urma_get_write_wr_ubuf(sbi, &wr, msg); + break; + default: + xmfs_error(NULL, "What exactly are you going to do?"); + return -EOPNOTSUPP; + } + + if (err) { + xmfs_error(NULL, "failed to alloc wr or local buffer"); + goto free_ret; + } + + err = xmfs_urma_send_and_wait(sbi, wr, msg); + +free_ret: + xmfs_urma_free_wr(msg, wr, err); + return err; +} + +int xmfs_urma_write_ubuf(struct xmfs_sb_info *sbi, void __user *buf, + size_t size, unsigned long offset) +{ + int chunk_size; + + if (sbi->debug) + chunk_size = PAGE_SIZE; + else + chunk_size = XMFS_DEFAULT_CHUNK_SIZE - PAGE_SIZE; + int *slots = vzalloc(sizeof(int) * (size + PAGE_SIZE - 1) / PAGE_SIZE); + + if (!slots) + return -ENOMEM; + int loop_cnt = 0; + int chunk_header_cnt = 0; + size_t written = 0; + int err; + /* + * xmfs_info(sbi->sb, "qyf before acquire nslots %d", + * (size + PAGE_SIZE * 16 - 1) / (PAGE_SIZE * 16)); + */ + void *write_buffer = + urma_pool_acquire_nslots(&sbi->urma_cfg.pool, + (size + PAGE_SIZE - 1) / PAGE_SIZE, + slots); + if (!write_buffer) { + vfree(slots); + xmfs_info(NULL, "write ubuf cannot get a buffer to write"); + return -EINVAL; + } + /* xmfs_info(sbi->sb, "qyf before copy from user"); */ + err = copy_from_user(write_buffer, buf, size); + if (err) { + xmfs_error(NULL, "write ubuf cannot copy from, size is %ld", size); + urma_pool_release_nslots(&sbi->urma_cfg.pool, + (size + PAGE_SIZE - 1) / PAGE_SIZE, slots); + vfree(slots); + return err; + } + /* xmfs_info(sbi->sb, "qyf end copy from user"); */ + while (written < size) { + unsigned long chunk = size - written; + struct xmfs_urma_msg msg; + + if (chunk > chunk_size) + chunk = chunk_size; + msg.ops = XMFS_URMA_WRITE; + msg.len = chunk_size; + msg.offset = offset + loop_cnt * chunk_size + + chunk_header_cnt * PAGE_SIZE; + if (msg.offset % XMFS_DEFAULT_CHUNK_SIZE == 0) { + chunk_header_cnt++; + msg.offset = offset + loop_cnt * chunk_size + + chunk_header_cnt * PAGE_SIZE; + } + /* + * xmfs_info(sbi->sb, + * "write ubuf msg len %d loopcnt %d offset %ld header cnt %d", + * chunk_size, loop_cnt, msg.offset, chunk_header_cnt); + */ + msg.write_buffer = write_buffer + written; + /* TODO */ + err = xmfs_urma_send_ubuf(sbi, &msg); + if (err) + break; + + written += chunk; + loop_cnt++; + /* xmfs_info(sbi->sb, "qyf end send and wait"); */ + } + urma_pool_release_nslots(&sbi->urma_cfg.pool, + (size + PAGE_SIZE - 1) / PAGE_SIZE, slots); + /* xmfs_info(sbi->sb, "qyf end release"); */ + vfree(slots); + return err; +} + +int xmfs_urma_read_ubuf(struct xmfs_sb_info *sbi, void __user *buf, + struct xmfs_read_extent *exts, int ext_cnt, size_t size) +{ + pgoff_t start_index; + unsigned long start_offset; + unsigned int nr_pages; + unsigned long offset = 0; + struct xmfs_urma_msg msg; + void *local_buf = vzalloc(size); + int err; + + msg.ops = XMFS_URMA_READ; + + for (int i = 0; i < ext_cnt; i++) { + start_index = exts[i].start_index; + start_offset = exts[i].start_offset; + nr_pages = exts[i].nr_pages; + msg.local_addr = local_buf + offset; + msg.len = min(nr_pages * PAGE_SIZE, size - offset); + msg.offset = start_offset; + /* + * xmfs_info(sbi->sb, + * "qyf urma read ubuf from %ld read %ld " + * "real len %ld offset %ld size %ld", + * start_offset, nr_pages * PAGE_SIZE, msg.len, offset, + * size); + */ + err = xmfs_urma_send_ubuf(sbi, &msg); + /* xmfs_info(sbi->sb, "qyf urma send ubuf done %d", err); */ + offset += msg.len; + } + err = copy_to_user(buf, local_buf, size); + if (err) + xmfs_error(NULL, "read ubuf cannot copy to, size is %ld", size); + + vfree(local_buf); + return err; +} + +int xmfs_urma_fill_write_page(struct xmfs_sb_info *sbi, + struct ubcore_jfs_wr *wr, + struct xmfs_urma_msg *msg, int slot) +{ + struct wr_status *wrs = kzalloc(sizeof(struct wr_status), GFP_KERNEL); + + if (!wrs) + return -ENOMEM; + wr->rw.src.sge = vzalloc(sizeof(struct ubcore_sge)); + wr->rw.dst.sge = vzalloc(sizeof(struct ubcore_sge)); + if (!wr->rw.src.sge || !wr->rw.dst.sge) { + kfree(wrs); + if (wr->rw.src.sge) + vfree(wr->rw.src.sge); + if (wr->rw.dst.sge) + vfree(wr->rw.dst.sge); + return -ENOMEM; + } + + wrs->status = URMA_ISSUING; + init_completion(&wrs->done); + wrs->ret = 6; + wrs->ops = XMFS_URMA_WRITE; + wrs->magic = XMFS_MAGIC; + wrs->slot = -1; /* slot */ + refcount_set(&wrs->refs, 1); + xmfs_wrs_get(wrs); + memcpy(msg->write_buffer, msg->local_addr, msg->len); + + wr->opcode = UBCORE_OPC_WRITE; + wr->flag.bs.complete_enable = 1; + wr->tjetty = sbi->urma_cfg.tjetty; + wr->rw.src.sge->addr = (uint64_t)msg->write_buffer; + wr->rw.src.sge->len = msg->len; + wr->rw.src.sge->tseg = sbi->urma_cfg.local_seg; + wr->rw.src.num_sge = 1; + wr->rw.dst.sge->addr = sbi->urma_cfg.urma_remote_va + msg->offset; + wr->rw.dst.sge->len = msg->len; + wr->rw.dst.sge->tseg = sbi->urma_cfg.remote_seg; + wr->rw.dst.num_sge = 1; + wr->user_ctx = (uint64_t)(uintptr_t)wrs; + wr->next = NULL; + msg->wr_cnt = 1; + return 0; +} + +int xmfs_urma_send_write_pages(struct xmfs_sb_info *sbi, + struct ubcore_jfs_wr *wr) +{ + struct ubcore_jfs_wr *cur; + struct wr_status *wrs; + int err; + + if (!wr) + return -EINVAL; + struct xmfs_urma_msg msg; + + msg.offset = 0; + msg.len = 0; + err = xmfs_urma_send_and_wait(sbi, &wr[0], &msg); + + cur = &wr[0]; + while (cur) { + vfree(cur->rw.src.sge); + vfree(cur->rw.dst.sge); + wrs = (struct wr_status *)cur->user_ctx; + if (wrs != NULL && err != -EAGAIN && err != 0) { + /* xmfs_info(sbi->sb, "qyf FREE 4 wrs=%px", wrs); */ + kfree(wrs); + } + cur = cur->next; + } + + vfree(wr); + + return err; +} + +int xmfs_pmem_read(struct xmfs_sb_info *sbi, unsigned long offset, void *buf, + unsigned long size) +{ + switch (sbi->mode) { + case XMFS_SHARED_MEM_MODE: + case XMFS_HYBRID_MODE: + memcpy(buf, sbi->kaddr + offset, size); + return 0; +#ifdef CONFIG_XMFS_FS_URMA + case XMFS_URMA_MODE: + return get_meta_and_copy(sbi, offset, buf, size, NULL, 0); +#endif + default: + return -ENODEV; + } +} +EXPORT_SYMBOL(xmfs_pmem_read); + +int xmfs_pmem_cas(struct xmfs_sb_info *sbi, unsigned long offset, + uint64_t old_val, uint64_t new_val) +{ + switch (sbi->mode) { + case XMFS_SHARED_MEM_MODE: + case XMFS_HYBRID_MODE: { + uint64_t *p = (uint64_t *)(sbi->kaddr + offset); + + return atomic64_cmpxchg((atomic64_t *)p, old_val, new_val) == + old_val ? + 0 : + -EAGAIN; + } +#ifdef CONFIG_XMFS_FS_URMA + case XMFS_URMA_MODE: { + struct xmfs_urma_msg msg; + uint64_t result; + int err; + + memset(&msg, 0, sizeof(msg)); + msg.ops = XMFS_URMA_CAS; + msg.offset = offset; + msg.len = 8; + msg.local_addr = &result; + msg.cas_cmp_data = old_val; + msg.cas_swap_data = new_val; + err = xmfs_urma_send(sbi, &msg); + if (err) + return err; + return result == old_val ? 0 : -EAGAIN; + } +#endif + default: + return -ENODEV; + } +} +EXPORT_SYMBOL(xmfs_pmem_cas); + +int xmfs_urma_send(struct xmfs_sb_info *sbi, struct xmfs_urma_msg *msg) +{ + struct ubcore_jfs_wr *wr = NULL; + int err = 0; + + if (msg->ops == XMFS_URMA_WRITE && msg->len > PAGE_SIZE) { + xmfs_error(NULL, + "write more than PAGE_SIZE is now unsupported"); + return -EOPNOTSUPP; + } + + switch (msg->ops) { + case XMFS_URMA_READ: + err = xmfs_urma_get_read_wr(sbi, &wr, msg); + break; + case XMFS_URMA_WRITE: + err = xmfs_urma_get_write_wr(sbi, &wr, msg); + break; + case XMFS_URMA_CAS: + err = xmfs_urma_get_cas_wr(sbi, &wr, msg); + break; + default: + xmfs_error(NULL, "What exactly are you going to do?"); + return -EOPNOTSUPP; + } + + if (err) { + xmfs_error(NULL, "failed to alloc wr or local buffer"); + goto free_ret; + } + + err = xmfs_urma_send_and_wait(sbi, wr, msg); + +free_ret: + xmfs_urma_free_wr(msg, wr, err); + return err; +} + +#else + +void xmfs_urma_unregister(struct xmfs_sb_info *sbi) +{ +} + +int xmfs_urma_register(struct xmfs_sb_info *sbi, struct xmfs_fs_context *ctx) +{ + return -EOPNOTSUPP; +} + +void xmfs_urma_unimport(struct xmfs_sb_info *sbi) +{ +} + +int xmfs_urma_import(struct xmfs_sb_info *sbi) +{ + return -EOPNOTSUPP; +} + +int xmfs_import_jetty(struct xmfs_sb_info *sbi, struct urma_info info, + int index, bool is_send) +{ + return -EOPNOTSUPP; +} + +int xmfs_send_request(struct xmfs_sb_info *sbi, struct xmfs_rw_msg *req, + struct xmfs_grant_wait *gw, int id) +{ + return -EOPNOTSUPP; +} + +int xmfs_urma_send(struct xmfs_sb_info *sbi, struct xmfs_urma_msg *msg) +{ + return -EOPNOTSUPP; +} + +int xmfs_pmem_write_multi_pages(struct xmfs_sb_info *sbi, unsigned long offset, + struct page **pages, unsigned long page_num) +{ + return -EOPNOTSUPP; +} + +int xmfs_pmem_read_multi_pages(struct xmfs_sb_info *sbi, unsigned long *offsets, + struct page **pages, unsigned long page_num) +{ + return -EOPNOTSUPP; +} + +int xmfs_urma_write_ubuf(struct xmfs_sb_info *sbi, void __user *buf, + size_t size, unsigned long offset) +{ + return -EOPNOTSUPP; +} + +int xmfs_urma_read_ubuf(struct xmfs_sb_info *sbi, void __user *buf, + struct xmfs_read_extent *exts, int ext_cnt, size_t size) +{ + return -EOPNOTSUPP; +} + +int xmfs_pmem_write(struct xmfs_sb_info *sbi, unsigned long offset, + const void *buf, unsigned long size) +{ + switch (sbi->mode) { + case XMFS_SHARED_MEM_MODE: + case XMFS_HYBRID_MODE: + memcpy(sbi->kaddr + offset, buf, size); + return 0; + default: + return -EOPNOTSUPP; + } +} +EXPORT_SYMBOL(xmfs_pmem_write); + +int xmfs_pmem_read(struct xmfs_sb_info *sbi, unsigned long offset, void *buf, + unsigned long size) +{ + switch (sbi->mode) { + case XMFS_SHARED_MEM_MODE: + case XMFS_HYBRID_MODE: + memcpy(buf, sbi->kaddr + offset, size); + return 0; + default: + return -EOPNOTSUPP; + } +} +EXPORT_SYMBOL(xmfs_pmem_read); + +int xmfs_pmem_cas(struct xmfs_sb_info *sbi, unsigned long offset, + uint64_t old_val, uint64_t new_val) +{ + switch (sbi->mode) { + case XMFS_SHARED_MEM_MODE: + case XMFS_HYBRID_MODE: { + uint64_t *p = (uint64_t *)(sbi->kaddr + offset); + + return atomic64_cmpxchg((atomic64_t *)p, old_val, new_val) == + old_val ? + 0 : + -EAGAIN; + } + default: + return -EOPNOTSUPP; + } +} +EXPORT_SYMBOL(xmfs_pmem_cas); + +#endif diff --git a/fs/xmfs/xattr.c b/fs/xmfs/xattr.c new file mode 100644 index 000000000000..96e1325f63f0 --- /dev/null +++ b/fs/xmfs/xattr.c @@ -0,0 +1,591 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * XMFS: Log Structured Filesystem for UB + * Copyright (C) 2024. Huawei Technologies Co., Ltd + * + * This program can be distributed under the terms of the GNU GPL. + * See the file COPYING. + */ + +#include "xmfs_i.h" + +void xmfs_simple_xattr_free(struct simple_xattr *xattr) +{ + if (xattr) + kfree(xattr->name); + kvfree(xattr); +} + +size_t xmfs_simple_xattr_space(const char *name, size_t size) +{ + /* + * Use "40" instead of sizeof(struct simple_xattr), to return the + * same result on 32-bit and 64-bit, and even if simple_xattr grows. + */ + return 40 + size + strlen(name); +} + +void xmfs_simple_xattrs_free(struct simple_xattrs *xattrs, size_t *freed_space) +{ + struct rb_node *rbp; + + if (freed_space) + *freed_space = 0; + rbp = rb_first(&xattrs->rb_root); + while (rbp) { + struct simple_xattr *xattr; + struct rb_node *rbp_next; + + rbp_next = rb_next(rbp); + xattr = rb_entry(rbp, struct simple_xattr, rb_node); + rb_erase(&xattr->rb_node, &xattrs->rb_root); + if (freed_space) + *freed_space += xmfs_simple_xattr_space(xattr->name, + xattr->size); + xmfs_simple_xattr_free(xattr); + rbp = rbp_next; + } +} + +void xmfs_simple_xattrs_init(struct simple_xattrs *xattrs) +{ + xattrs->rb_root = RB_ROOT; + rwlock_init(&xattrs->lock); +} + +struct simple_xattr *xmfs_simple_xattr_alloc(const void *value, size_t size) +{ + struct simple_xattr *new_xattr; + size_t len; + + /* wrap around? */ + len = sizeof(*new_xattr) + size; + if (len < sizeof(*new_xattr)) + return NULL; + + new_xattr = kvmalloc(len, GFP_KERNEL_ACCOUNT); + if (!new_xattr) + return NULL; + + new_xattr->size = size; + memcpy(new_xattr->value, value, size); + return new_xattr; +} + +static int rbtree_simple_xattr_cmp(const void *key, const struct rb_node *node) +{ + const char *xattr_name = key; + const struct simple_xattr *xattr; + + xattr = rb_entry(node, struct simple_xattr, rb_node); + return strcmp(xattr->name, xattr_name); +} + +struct simple_xattr *xmfs_simple_xattr_set(struct simple_xattrs *xattrs, + const char *name, const void *value, + size_t size, int flags) +{ + struct simple_xattr *old_xattr = NULL, *new_xattr = NULL; + struct rb_node *parent = NULL, **rbp; + int err = 0, ret; + + /* value == NULL means remove */ + if (value) { + new_xattr = xmfs_simple_xattr_alloc(value, size); + if (!new_xattr) + return ERR_PTR(-ENOMEM); + + new_xattr->name = kstrdup(name, GFP_KERNEL_ACCOUNT); + if (!new_xattr->name) { + xmfs_simple_xattr_free(new_xattr); + return ERR_PTR(-ENOMEM); + } + } + + write_lock(&xattrs->lock); + rbp = &xattrs->rb_root.rb_node; + while (*rbp) { + parent = *rbp; + ret = rbtree_simple_xattr_cmp(name, *rbp); + if (ret < 0) + rbp = &(*rbp)->rb_left; + else if (ret > 0) + rbp = &(*rbp)->rb_right; + else + old_xattr = + rb_entry(*rbp, struct simple_xattr, rb_node); + if (old_xattr) + break; + } + + if (old_xattr) { + /* Fail if XATTR_CREATE is requested and the xattr exists. */ + if (flags & XATTR_CREATE) { + err = -EEXIST; + goto out_unlock; + } + + if (new_xattr) + rb_replace_node(&old_xattr->rb_node, + &new_xattr->rb_node, &xattrs->rb_root); + else + rb_erase(&old_xattr->rb_node, &xattrs->rb_root); + } else { + /* Fail if XATTR_REPLACE is requested but no xattr is found. */ + if (flags & XATTR_REPLACE) { + err = -ENODATA; + goto out_unlock; + } + + /* + * If XATTR_CREATE or no flags are specified together with a + * new value simply insert it. + */ + if (new_xattr) { + rb_link_node(&new_xattr->rb_node, parent, rbp); + rb_insert_color(&new_xattr->rb_node, &xattrs->rb_root); + } + + /* + * If XATTR_CREATE or no flags are specified and neither an + * old or new xattr exist then we don't need to do anything. + */ + } + +out_unlock: + write_unlock(&xattrs->lock); + if (!err) + return old_xattr; + xmfs_simple_xattr_free(new_xattr); + return ERR_PTR(err); +} + +int xmfs_simple_xattr_get(struct simple_xattrs *xattrs, const char *name, + void *buffer, size_t size) +{ + struct simple_xattr *xattr = NULL; + struct rb_node *rbp; + int ret = -ENODATA; + + read_lock(&xattrs->lock); + rbp = rb_find(name, &xattrs->rb_root, rbtree_simple_xattr_cmp); + if (rbp) { + xattr = rb_entry(rbp, struct simple_xattr, rb_node); + ret = xattr->size; + if (buffer) { + if (size < xattr->size) + ret = -ERANGE; + else + memcpy(buffer, xattr->value, xattr->size); + } + } + read_unlock(&xattrs->lock); + return ret; +} + +static bool xattr_is_trusted(const char *name) +{ + return !strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN); +} + +int xmfs_xattr_list_one(char **buffer, ssize_t *remaining_size, + const char *name) +{ + size_t len; + + len = strlen(name) + 1; + if (*buffer) { + if (*remaining_size < len) + return -ERANGE; + memcpy(*buffer, name, len); + *buffer += len; + } + *remaining_size -= len; + return 0; +} + +ssize_t xmfs_simple_xattr_list(struct inode *inode, + struct simple_xattrs *xattrs, char *buffer, + size_t size) +{ + bool trusted = ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN); + struct simple_xattr *xattr; + struct rb_node *rbp; + ssize_t remaining_size = size; + int err = 0; + + read_lock(&xattrs->lock); + for (rbp = rb_first(&xattrs->rb_root); rbp; rbp = rb_next(rbp)) { + xattr = rb_entry(rbp, struct simple_xattr, rb_node); + + /* skip "trusted." attributes for unprivileged callers */ + if (!trusted && xattr_is_trusted(xattr->name)) + continue; + + err = xmfs_xattr_list_one(&buffer, &remaining_size, + xattr->name); + if (err) + break; + } + read_unlock(&xattrs->lock); + + return err ? err : size - remaining_size; +} + +static unsigned long xmfs_xattr_write_value(struct inode *inode, + const char *name, const void *value, + size_t size, + struct xmfs_log *xm_log, + struct xmfs_sb_index *xmsi) +{ + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + + if (xmfs_quiesce_wait(sbi)) + return 0; + unsigned long data_current = xmsi->data_current; + unsigned long data_count = xmsi->data_count; + long slot; + + WARN_ON(xmsi->count == 0); + + if (data_current == 0 || data_count == 511) { + slot = xmfs_alloc_and_mark_slot(xmsi, sbi, 'd'); + if (slot < 0) + return -ENOSPC; + xmsi->data_current = + DATA_AREA_OFFSET + slot * sbi->trunk_size + PAGE_SIZE; + xmsi->data_count = 0; + } else { + xmsi->data_current = data_current + PAGE_SIZE; + } + xm_log->data_offset = xmsi->data_current; + xmsi->data_count = xmsi->data_count + 1; + + xmfs_data_write(sbi, xmsi->data_current, value, size); + xmfs_mark_page_live(sbi, xmsi->data_current); + + return 0; +} + +static int xmfs_xattr_set_log(struct inode *inode, const char *name, + const void *value, size_t size, int flags) +{ + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + + if (xmfs_quiesce_wait(sbi)) + return -EINTR; + struct xmfs_inode_info *xmfs_inode = XMFS_I(inode); + struct xmfs_log xm_log = { 0 }; + + xm_log.index = size; + xm_log.end_index = strlen(name); + xm_log.ops = XMFS_XATTR; + xm_log.size = flags; + xm_log.p_ino = inode->__i_ctime.tv_sec; + xm_log.offset = inode->__i_ctime.tv_nsec; + memcpy(xm_log.name, name, strlen(name)); + + spin_lock(&sbi->space_lock); + if (xmfs_inode->die) { + xmfs_info(inode->i_sb, + "inode logs have been interrupted before set xattr"); + spin_unlock(&sbi->space_lock); + return -EINVAL; + } + + struct xmfs_sb_index *xmsi = get_xmsi(sbi); + + if (!xmsi) { + spin_unlock(&sbi->space_lock); + return -EINVAL; + } + int logcount = 1; + int need_data_trunk = + (xmsi->data_count == 511 || xmsi->data_current == 0) ? 1 : 0; + if (xmsi->used_trunk_count + logcount + need_data_trunk > + sbi->max_chunk) { + sbi->full = true; + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + return -ENOSPC; + } + + xmfs_xattr_write_value(inode, name, value, size, &xm_log, xmsi); + spin_unlock(&sbi->space_lock); + put_xmsi(sbi, xmsi); + unsigned long xmlt_addr = + xmfs_add_log_v3(&xm_log, sbi, strlen(name), inode->i_ino, true); + if (xmlt_addr < 0) { + xmfs_inode->die = true; + return -EINVAL; + } + + return 0; +} + +int xmfs_initxattrs(struct inode *inode, const struct xattr *xattr_array, + void *fs_info) +{ + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + const struct xattr *xattr; + struct simple_xattr *new_xattr; + struct xmfs_inode_info *info = XMFS_I(inode); + size_t len; + int err = 0; + + for (xattr = xattr_array; xattr->name != NULL; xattr++) { + new_xattr = + xmfs_simple_xattr_alloc(xattr->value, xattr->value_len); + if (!new_xattr) + break; + + len = strlen(xattr->name) + 1; + if ((len + XATTR_SECURITY_PREFIX_LEN) > 255 || + new_xattr->size > PAGE_SIZE) { + continue; + } + new_xattr->name = kmalloc(XATTR_SECURITY_PREFIX_LEN + len, + GFP_KERNEL_ACCOUNT); + if (!new_xattr->name) { + kvfree(new_xattr); + break; + } + + memcpy(new_xattr->name, XATTR_SECURITY_PREFIX, + XATTR_SECURITY_PREFIX_LEN); + memcpy(new_xattr->name + XATTR_SECURITY_PREFIX_LEN, xattr->name, + len); + + new_xattr = xmfs_simple_xattr_set(&info->xattrs, + new_xattr->name, + new_xattr->value, + new_xattr->size, 0); + if (!IS_ERR(new_xattr)) { + inode_set_ctime_current(inode); + if (sbi->id == 0) { + err = xmfs_xattr_set_log(inode, new_xattr->name, + new_xattr->value, + new_xattr->size, 0); + if (err < 0) { + xmfs_simple_xattr_free(new_xattr); + break; + } + } + xmfs_simple_xattr_free(new_xattr); + } else { + break; + } + } + + if (xattr->name != NULL) { + xmfs_simple_xattrs_free(&info->xattrs, NULL); + return -ENOMEM; + } + + return err; +} + +static int xmfs_xattr_slave_get(const struct xattr_handler *handler, + struct dentry *unused, struct inode *inode, + const char *name, void *buffer, size_t size) +{ + struct xmfs_inode_info *info = XMFS_I(inode); + + if (inode->i_ino == 0) { + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + struct xmfs_sb_index *xmsi = + kmalloc(sizeof(struct xmfs_sb_index), GFP_KERNEL); + int err = get_xmsi_and_copy(sbi, xmsi); + + if (unlikely(err)) { + xmfs_error(inode->i_sb, + "hardware memory error when reading superblock during slave dir iterate"); + kfree(xmsi); + return err; + } + + sbi->stop_cnt++; + if (xmsi->last_update != sbi->last_update) { + if (!sbi->debug) + xmfs_replay_new_entries(sbi, NULL, + xmsi->last_update); + else + xmfs_replay_new_entries(sbi, NULL, + xmsi->last_update); + } + kfree(xmsi); + } + name = xattr_full_name(handler, name); + return xmfs_simple_xattr_get(&info->xattrs, name, buffer, size); +} + +static int xmfs_xattr_get(const struct xattr_handler *handler, + struct dentry *unused, struct inode *inode, + const char *name, void *buffer, size_t size) +{ + struct xmfs_inode_info *info = XMFS_I(inode); + + name = xattr_full_name(handler, name); + return xmfs_simple_xattr_get(&info->xattrs, name, buffer, size); +} + +static int xmfs_xattr_set(const struct xattr_handler *handler, + struct mnt_idmap *idmap, struct dentry *unused, + struct inode *inode, const char *name, + const void *value, size_t size, int flags) +{ + struct simple_xattr *old_xattr; + struct xmfs_inode_info *info = XMFS_I(inode); + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + int err = 0; + + name = xattr_full_name(handler, name); + if (strlen(name) > 255 || size > PAGE_SIZE) + return -EINVAL; + + old_xattr = + xmfs_simple_xattr_set(&info->xattrs, name, value, size, flags); + if (!IS_ERR(old_xattr)) { + inode_set_ctime_current(inode); + if (sbi->id == 0) + err = xmfs_xattr_set_log(inode, name, value, size, + flags); + xmfs_simple_xattr_free(old_xattr); + old_xattr = NULL; + } + + return err == 0 ? PTR_ERR(old_xattr) : err; +} + +ssize_t xmfs_listxattr(struct dentry *dentry, char *buffer, size_t size) +{ + struct xmfs_inode_info *info = XMFS_I(d_inode(dentry)); + + return xmfs_simple_xattr_list(d_inode(dentry), &info->xattrs, buffer, + size); +} + +static const struct xattr_handler xmfs_security_xattr_handler = { + .prefix = XATTR_SECURITY_PREFIX, + .get = xmfs_xattr_get, + .set = xmfs_xattr_set, +}; + +static const struct xattr_handler xmfs_trusted_xattr_handler = { + .prefix = XATTR_TRUSTED_PREFIX, + .get = xmfs_xattr_get, + .set = xmfs_xattr_set, +}; + +static const struct xattr_handler xmfs_user_xattr_handler = { + .prefix = XATTR_USER_PREFIX, + .get = xmfs_xattr_get, + .set = xmfs_xattr_set, +}; + +static int xmfs_xattr_set_slave(const struct xattr_handler *handler, + struct mnt_idmap *idmap, struct dentry *dentry, + struct inode *inode, const char *name, + const void *value, size_t size, int flags) +{ + struct xmfs_sb_info *sbi = XMFS_SB(inode->i_sb); + struct xmfs_rw_msg req = {}, grant = {}; + struct xmfs_log xm_log; + unsigned long log_off; + unsigned long pages; + int err; + + if (!sbi || sbi->id == 0) + return -EINVAL; + + if (value && size > 0) + pages = (size + PAGE_SIZE - 1) / PAGE_SIZE; + else + pages = 0; + + req.ops = XMFS_XATTR; + req.i_ino = inode->i_ino; + req.data_pages = pages; + req.log_entries = 1; + req.size = size; + req.mode = flags; + snprintf(req.name, 256, "%s%s", handler->prefix, name); + + req.type = MSG_RW_REQUEST; + req.slave_id = sbi->id; + spin_lock(&sbi->local_ring_lock); + req.request_id = ++sbi->rw_request_id; + spin_unlock(&sbi->local_ring_lock); + struct xmfs_grant_wait gw; + + init_completion(&gw.done); + xa_store(&sbi->urma_cfg.grant_waits, req.request_id, &gw, GFP_KERNEL); + err = xmfs_send_request(sbi, &req, &gw, 0); + if (err) + return err; + grant = gw.grant; + + if (value && size > 0 && grant.data_addr != 0) + xmfs_data_write(sbi, grant.data_addr, (void *)value, size); + + log_off = grant.log_trunk_addr + + offsetof(struct xmfs_log_trunk, log[0]) + + grant.log_slot_start * sizeof(struct xmfs_log); + memset(&xm_log, 0, sizeof(xm_log)); + xm_log.ops = XMFS_XATTR; + xm_log.i_ino = inode->i_ino; + xm_log.version = grant.version; + xm_log.data_offset = grant.data_addr; + xm_log.size = size; + xm_log.mode = flags; + xm_log.writer_id = sbi->id; + snprintf(xm_log.name, 256, "%s%s", handler->prefix, name); + xmfs_data_write(sbi, log_off, &xm_log, sizeof(xm_log)); + + { + const char *full_name = xattr_full_name(handler, name); + struct simple_xattr *old_xattr; + + old_xattr = xmfs_simple_xattr_set(&XMFS_I(inode)->xattrs, + full_name, value, size, + flags); + if (IS_ERR(old_xattr)) + err = PTR_ERR(old_xattr); + else + xmfs_simple_xattr_free(old_xattr); + } + + return err; +} + +static const struct xattr_handler xmfs_slave_security_xattr_handler = { + .prefix = XATTR_SECURITY_PREFIX, + .get = xmfs_xattr_slave_get, + .set = xmfs_xattr_set_slave, +}; + +static const struct xattr_handler xmfs_slave_trusted_xattr_handler = { + .prefix = XATTR_TRUSTED_PREFIX, + .get = xmfs_xattr_slave_get, + .set = xmfs_xattr_set_slave, +}; + +static const struct xattr_handler xmfs_slave_user_xattr_handler = { + .prefix = XATTR_USER_PREFIX, + .get = xmfs_xattr_slave_get, + .set = xmfs_xattr_set_slave, +}; + +const struct xattr_handler *xmfs_host_xattr_handlers[] = { + &xmfs_security_xattr_handler, + &xmfs_trusted_xattr_handler, + &xmfs_user_xattr_handler, + NULL +}; + +const struct xattr_handler *xmfs_slave_xattr_handlers[] = { + &xmfs_slave_security_xattr_handler, + &xmfs_slave_trusted_xattr_handler, + &xmfs_slave_user_xattr_handler, + NULL +}; diff --git a/fs/Kconfig b/fs/Kconfig index d80f39d6632f..9a6fd57368a6 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -139,6 +139,7 @@ source "fs/quota/Kconfig" source "fs/autofs/Kconfig" source "fs/fuse/Kconfig" source "fs/overlayfs/Kconfig" +source "fs/xmfs/Kconfig" menu "Caches" diff --git a/fs/Makefile b/fs/Makefile index 49a03a43d7db..c908f403d73e 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -111,6 +111,7 @@ obj-$(CONFIG_AUTOFS_FS) += autofs/ obj-$(CONFIG_ADFS_FS) += adfs/ obj-$(CONFIG_FUSE_FS) += fuse/ obj-$(CONFIG_OVERLAY_FS) += overlayfs/ +obj-$(CONFIG_XMFS_FS) += xmfs/ obj-$(CONFIG_ORANGEFS_FS) += orangefs/ obj-$(CONFIG_UDF_FS) += udf/ obj-$(CONFIG_SUN_OPENPROMFS) += openpromfs/ diff --git a/fs/xmfs/Kconfig b/fs/xmfs/Kconfig new file mode 100644 index 000000000000..2fb10c42b9c5 --- /dev/null +++ b/fs/xmfs/Kconfig @@ -0,0 +1,21 @@ +config XMFS_FS + tristate "XMFS (Log Structured Filesystem for UB) support" + help + XMFS is a high-performance log-structured filesystem specifically + designed for distributed storage environments. It leverages advanced + hardware and transport technologies such as UB, URMA, CXL, PMEM, + and HCCS to achieve ultra-low latency and high throughput. + + It provides robust crash consistency, efficient log management, and + optimized data placement strategies for modern storage architectures. + + If unsure, say N. + +config XMFS_FS_URMA + bool "XMFS URMA support" + depends on XMFS_FS && CONFIG_UB_URMA && CONFIG_UB_UBASE + help + Say Y here if you want to enable URMA (Unified Remote Memory Access) + transport support for XMFS, allowing high-speed network communication + and remote memory operations. + default n diff --git a/fs/xmfs/Makefile b/fs/xmfs/Makefile new file mode 100644 index 000000000000..09544186e5c4 --- /dev/null +++ b/fs/xmfs/Makefile @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the linux xmfs-filesystem routines. +# + +obj-$(CONFIG_XMFS_FS) += xmfs.o +xmfs-y += super.o +xmfs-y += inode.o +xmfs-y += replay.o +xmfs-y += file.o +xmfs-y += dir.o +xmfs-y += namei.o +xmfs-y += symlink.o +xmfs-y += xattr.o +xmfs-y += urma.o +xmfs-y += gc.o -- 2.52.0