mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 00:10:51 +07:00
5fa3ea047a
Signed-off-by: AuxXxilium <info@auxxxilium.tech>
368 lines
8.9 KiB
C
368 lines
8.9 KiB
C
#ifndef MY_ABC_HERE
|
|
#define MY_ABC_HERE
|
|
#endif
|
|
/*
|
|
* Copyright (C) 2020 Synology Inc. All rights reserved.
|
|
*/
|
|
|
|
#include "ctree.h"
|
|
#include "xattr.h"
|
|
#include "transaction.h"
|
|
#include "btrfs_inode.h"
|
|
#include "syno-feat-tree.h"
|
|
#include "disk-io.h"
|
|
#include "locking.h"
|
|
|
|
static inline int syno_feat_tree_update_helper(struct btrfs_trans_handle *trans,
|
|
struct btrfs_root *root,
|
|
struct btrfs_path *path,
|
|
struct btrfs_key *key,
|
|
u32 data_size)
|
|
{
|
|
int ret;
|
|
|
|
ret = btrfs_search_slot(trans, root, key, path, 0, 1);
|
|
if (ret > 0) {
|
|
btrfs_release_path(path);
|
|
// insert new item
|
|
ret = btrfs_insert_empty_item(trans, root, path, key, data_size);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int __btrfs_create_syno_feat_tree(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info)
|
|
{
|
|
int ret;
|
|
struct btrfs_root *syno_feat_root;
|
|
|
|
syno_feat_root = btrfs_create_tree(trans, BTRFS_SYNO_FEATURE_TREE_OBJECTID);
|
|
if (IS_ERR(syno_feat_root)) {
|
|
ret = PTR_ERR(syno_feat_root);
|
|
goto out;
|
|
}
|
|
fs_info->syno_feat_root = syno_feat_root;
|
|
|
|
ret = 0;
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int btrfs_syno_feat_tree_status_update(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info)
|
|
{
|
|
int ret;
|
|
struct btrfs_root *root = fs_info->syno_feat_root;
|
|
struct btrfs_path *path;
|
|
struct btrfs_key key;
|
|
int data_size;
|
|
struct extent_buffer *leaf;
|
|
struct btrfs_syno_feat_tree_status_item *ei;
|
|
|
|
if (!btrfs_syno_check_feat_tree_enable(fs_info))
|
|
return 0;
|
|
|
|
path = btrfs_alloc_path();
|
|
if (!path) {
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
key.objectid = BTRFS_SYNO_FEAT_TREE_STATUS_OBJECTID;
|
|
key.type = SYNO_BTRFS_FEAT_TREE_STATUS_KEY;
|
|
key.offset = 0;
|
|
|
|
data_size = sizeof(struct btrfs_syno_feat_tree_status_item);
|
|
|
|
ret = syno_feat_tree_update_helper(trans, root, path, &key, data_size);
|
|
if (ret < 0)
|
|
goto out;
|
|
|
|
leaf = path->nodes[0];
|
|
ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_syno_feat_tree_status_item);
|
|
btrfs_set_syno_feat_tree_status_version(leaf, ei, fs_info->syno_feat_tree_status.version);
|
|
btrfs_set_syno_feat_tree_status_status(leaf, ei, fs_info->syno_feat_tree_status.status);
|
|
|
|
btrfs_mark_buffer_dirty(path->nodes[0]);
|
|
out:
|
|
if (ret)
|
|
btrfs_abort_transaction(trans, ret);
|
|
btrfs_free_path(path);
|
|
return ret;
|
|
}
|
|
|
|
static int btrfs_create_syno_feat_tree(struct btrfs_fs_info *fs_info)
|
|
{
|
|
struct btrfs_trans_handle *trans = NULL;
|
|
struct btrfs_root *tree_root = fs_info->tree_root;
|
|
int ret;
|
|
|
|
trans = btrfs_start_transaction(tree_root, 3);
|
|
if (IS_ERR(trans)) {
|
|
return PTR_ERR(trans);
|
|
}
|
|
|
|
/* root item x 1 + root node x 1 */
|
|
ret = __btrfs_create_syno_feat_tree(trans, fs_info);
|
|
if (ret) {
|
|
btrfs_err(fs_info, "failed to create syno feature tree, ret=[%d]", ret);
|
|
goto out;
|
|
}
|
|
|
|
btrfs_syno_set_feat_tree_enable(fs_info);
|
|
|
|
/* tree item x 1 */
|
|
ret = btrfs_syno_feat_tree_status_update(trans, fs_info);
|
|
if (ret) {
|
|
btrfs_err(fs_info, "failed to update status of syno feature tree at the first time, ret=[%d]", ret);
|
|
btrfs_syno_set_feat_tree_disable(fs_info);
|
|
goto out;
|
|
}
|
|
|
|
return btrfs_commit_transaction(trans);
|
|
|
|
out:
|
|
btrfs_abort_transaction(trans, ret);
|
|
btrfs_end_transaction(trans);
|
|
return ret;
|
|
}
|
|
|
|
int btrfs_syno_feat_tree_enable(struct btrfs_fs_info *fs_info)
|
|
{
|
|
int ret = -1;
|
|
|
|
if (btrfs_syno_check_feat_tree_enable(fs_info)) {
|
|
ret = 0;
|
|
goto out;
|
|
}
|
|
|
|
#ifdef MY_ABC_HERE
|
|
/*
|
|
* Don't clean up existing feature-tree
|
|
*/
|
|
#else
|
|
btrfs_syno_set_feat_tree_disable(fs_info);
|
|
|
|
// clean up old tree
|
|
if (fs_info->syno_feat_root) {
|
|
btrfs_err(fs_info, "we are going to clean up syno feature tree because the status is not enabled");
|
|
ret = btrfs_syno_feat_tree_disable(fs_info);
|
|
if (ret) {
|
|
btrfs_err(fs_info, "failed to disable and clean up syno feature tree, ret: [%d].", ret);
|
|
goto out;
|
|
}
|
|
}
|
|
#endif /* MY_ABC_HERE */
|
|
|
|
ret = btrfs_create_syno_feat_tree(fs_info);
|
|
if (ret) {
|
|
btrfs_err(fs_info, "failed to create syno feature tree, ret: [%d].", ret);
|
|
goto out;
|
|
}
|
|
|
|
ret = 0;
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static inline int syno_feat_tree_need_stop(struct btrfs_fs_info *fs_info)
|
|
{
|
|
return sb_rdonly(fs_info->sb) || btrfs_fs_closing(fs_info);
|
|
}
|
|
|
|
#ifdef MY_ABC_HERE
|
|
/*
|
|
* locker will store information in feature-tree, and we cannot provide any
|
|
* abilities to remove feature-tree.
|
|
*/
|
|
#else
|
|
static int btrfs_clear_syno_feat_tree(struct btrfs_fs_info *fs_info)
|
|
{
|
|
struct btrfs_trans_handle *trans;
|
|
struct btrfs_root *root = fs_info->syno_feat_root;
|
|
struct btrfs_path *path;
|
|
struct btrfs_key key;
|
|
int nr;
|
|
int ret;
|
|
|
|
path = btrfs_alloc_path();
|
|
if (!path)
|
|
return -ENOMEM;
|
|
|
|
key.objectid = 0;
|
|
key.type = 0;
|
|
key.offset = 0;
|
|
|
|
while (1) {
|
|
trans = btrfs_start_transaction_fallback_global_rsv(root, 1);
|
|
if (IS_ERR(trans)) {
|
|
ret = PTR_ERR(trans);
|
|
goto out;
|
|
}
|
|
ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
|
|
if (ret < 0)
|
|
goto end_trans;
|
|
|
|
nr = btrfs_header_nritems(path->nodes[0]);
|
|
if (!nr)
|
|
break;
|
|
|
|
path->slots[0] = 0;
|
|
ret = btrfs_del_items(trans, root, path, 0, nr);
|
|
if (ret) {
|
|
btrfs_err(root->fs_info, "failed to delete item of syno feature tree, ret: [%d].", ret);
|
|
goto end_trans;
|
|
}
|
|
|
|
btrfs_release_path(path);
|
|
ret = btrfs_end_transaction_throttle(trans);
|
|
if (ret)
|
|
goto out;
|
|
trans = NULL;
|
|
|
|
if (syno_feat_tree_need_stop(root->fs_info)) {
|
|
ret = -EAGAIN;
|
|
btrfs_debug(root->fs_info, "drop tree early exit, ret: [%d].", ret);
|
|
goto out;
|
|
}
|
|
if (fatal_signal_pending(current)) {
|
|
btrfs_debug(root->fs_info, "catch interrupt singal as cleaning up syno feature tree.");
|
|
ret = -EINTR;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
ret = 0;
|
|
end_trans:
|
|
if (trans)
|
|
btrfs_end_transaction_throttle(trans);
|
|
out:
|
|
btrfs_free_path(path);
|
|
return ret;
|
|
}
|
|
|
|
static int btrfs_delete_syno_feat_tree_root(struct btrfs_fs_info *fs_info)
|
|
{
|
|
struct btrfs_trans_handle *trans;
|
|
struct btrfs_root *tree_root = fs_info->tree_root;
|
|
struct btrfs_root *syno_feat_root = fs_info->syno_feat_root;
|
|
int ret;
|
|
|
|
trans = btrfs_start_transaction(tree_root, 0);
|
|
if (IS_ERR(trans))
|
|
return PTR_ERR(trans);
|
|
|
|
fs_info->syno_feat_root = NULL;
|
|
|
|
ret = btrfs_del_root(trans, &syno_feat_root->root_key);
|
|
if (ret) {
|
|
btrfs_err(fs_info, "failed to delete the root item about syno feature tree, ret: [%d].", ret);
|
|
goto abort;
|
|
}
|
|
|
|
list_del(&syno_feat_root->dirty_list);
|
|
|
|
btrfs_tree_lock(syno_feat_root->node);
|
|
btrfs_clean_tree_block(syno_feat_root->node);
|
|
btrfs_tree_unlock(syno_feat_root->node);
|
|
btrfs_free_tree_block(trans, syno_feat_root, syno_feat_root->node, 0, 1);
|
|
|
|
btrfs_put_root(syno_feat_root);
|
|
|
|
return btrfs_commit_transaction(trans);
|
|
|
|
abort:
|
|
btrfs_abort_transaction(trans, ret);
|
|
btrfs_end_transaction(trans);
|
|
return ret;
|
|
}
|
|
|
|
int btrfs_syno_feat_tree_disable(struct btrfs_fs_info *fs_info)
|
|
{
|
|
int ret;
|
|
|
|
if (!fs_info->syno_feat_root) {
|
|
ret = 0;
|
|
goto out;
|
|
}
|
|
|
|
btrfs_syno_set_feat_tree_disable(fs_info);
|
|
|
|
ret = btrfs_clear_syno_feat_tree(fs_info);
|
|
if (ret) {
|
|
btrfs_err(fs_info, "failed to clean up syno feature tree, ret: [%d].", ret);
|
|
goto out;
|
|
}
|
|
|
|
ret = btrfs_delete_syno_feat_tree_root(fs_info);
|
|
if (ret) {
|
|
btrfs_err(fs_info, "failed to delete syno feature tree root, ret: [%d].", ret);
|
|
goto out;
|
|
}
|
|
btrfs_info(fs_info, "have finished to clean up syno feature tree, ret: [%d].", ret);
|
|
|
|
ret = 0;
|
|
out:
|
|
return ret;
|
|
}
|
|
#endif /* MY_ABC_HERE */
|
|
|
|
int btrfs_syno_feat_tree_load_status_from_disk(struct btrfs_fs_info *fs_info)
|
|
{
|
|
struct btrfs_root *root = fs_info->syno_feat_root;
|
|
struct btrfs_path *path;
|
|
struct extent_buffer *leaf;
|
|
struct btrfs_syno_feat_tree_status_item *ei;
|
|
struct btrfs_key key;
|
|
struct btrfs_key found_key;
|
|
int ret;
|
|
|
|
if (!fs_info->syno_feat_root) {
|
|
btrfs_info(fs_info, "root of syno feature tree is null");
|
|
return 0;
|
|
}
|
|
|
|
path = btrfs_alloc_path();
|
|
if (!path) {
|
|
btrfs_err(fs_info, "failed to allocate path, err: [%ld]", PTR_ERR(path));
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
key.objectid = BTRFS_SYNO_FEAT_TREE_STATUS_OBJECTID;
|
|
key.type = SYNO_BTRFS_FEAT_TREE_STATUS_KEY;
|
|
key.offset = 0;
|
|
|
|
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
|
|
if (ret < 0) {
|
|
btrfs_err(fs_info, "failed to search slot on syno feature tree, ret: [%d].", ret);
|
|
goto out;
|
|
} else if (ret > 0) { /* tree empty */
|
|
btrfs_warn(fs_info, "feature tree is empty.");
|
|
ret = 0;
|
|
goto out;
|
|
}
|
|
|
|
leaf = path->nodes[0];
|
|
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
|
|
|
|
ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_syno_feat_tree_status_item);
|
|
fs_info->syno_feat_tree_status.version = btrfs_syno_feat_tree_status_version(leaf, ei);
|
|
fs_info->syno_feat_tree_status.status = btrfs_syno_feat_tree_status_status(leaf, ei);
|
|
|
|
switch(fs_info->syno_feat_tree_status.status) {
|
|
case SYNO_FEAT_TREE_ST_ENABLE:
|
|
btrfs_info(fs_info, "syno feature tree is enabled");
|
|
break;
|
|
case SYNO_FEAT_TREE_ST_DISABLE:
|
|
btrfs_warn(fs_info, "disable status [%llx] has detected. We are going to clean up feautre tree.", fs_info->syno_feat_tree_status.status);
|
|
break;
|
|
default:
|
|
btrfs_err(fs_info, "invalid status [%llx] of syno feature tree has detected.", fs_info->syno_feat_tree_status.status);
|
|
break;
|
|
}
|
|
|
|
ret = 0;
|
|
out:
|
|
btrfs_free_path(path);
|
|
return ret;
|
|
}
|