[Ocfs2-tools-devel] [PATCH 19/44] tunefs.ocfs2: Add refcount feature disable/enable.
Tao Ma
tao.ma at oracle.com
Mon Dec 28 01:01:04 PST 2009
The enable part is very simple.
As to disable refcount feature, it will use ocfs2_refcount_cow
to CoW the refcounted clusters.
This patch only for the inode extents, the xattr part will be
implemented in the later patch.
Signed-off-by: Tao Ma <tao.ma at oracle.com>
---
tunefs.ocfs2/Makefile | 1 +
tunefs.ocfs2/feature_refcount.c | 475 +++++++++++++++++++++++++++++++++++++++
tunefs.ocfs2/op_features.c | 2 +
3 files changed, 478 insertions(+), 0 deletions(-)
create mode 100644 tunefs.ocfs2/feature_refcount.c
diff --git a/tunefs.ocfs2/Makefile b/tunefs.ocfs2/Makefile
index 8806f52..dad7034 100644
--- a/tunefs.ocfs2/Makefile
+++ b/tunefs.ocfs2/Makefile
@@ -22,6 +22,7 @@ OCFS2NE_FEATURES = \
feature_inline_data \
feature_local \
feature_metaecc \
+ feature_refcount \
feature_sparse_files \
feature_unwritten_extents \
feature_xattr \
diff --git a/tunefs.ocfs2/feature_refcount.c b/tunefs.ocfs2/feature_refcount.c
new file mode 100644
index 0000000..3f2af9e
--- /dev/null
+++ b/tunefs.ocfs2/feature_refcount.c
@@ -0,0 +1,475 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * feature_refcount.c
+ *
+ * ocfs2 tune utility for enabling and disabling the refcount feature.
+ *
+ * Copyright (C) 2009 Oracle. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "ocfs2/kernel-rbtree.h"
+#include "ocfs2-kernel/kernel-list.h"
+#include "ocfs2/ocfs2.h"
+
+#include "libocfs2ne.h"
+
+struct refcount_file {
+ struct list_head list;
+ uint64_t blkno;
+};
+
+struct refcount_block {
+ struct rb_node ref_node;
+ uint64_t blkno;
+ struct list_head files_list;
+};
+
+struct disable_refcount_ctxt {
+ errcode_t ret;
+ struct tools_progress *prog;
+ uint32_t more_clusters;
+ uint32_t more_ebs;
+ struct rb_root ref_blknos;
+ int files_count;
+};
+
+/* See if the recount_file rbtree has the given ref_blkno. */
+static struct refcount_block*
+refcount_block_lookup(struct disable_refcount_ctxt *ctxt,
+ uint64_t ref_blkno)
+{
+ struct rb_node *p = ctxt->ref_blknos.rb_node;
+ struct refcount_block *ref_blk;
+
+ while (p) {
+ ref_blk = rb_entry(p, struct refcount_block, ref_node);
+ if (ref_blkno < ref_blk->blkno)
+ p = p->rb_left;
+ else if (ref_blkno > ref_blk->blkno)
+ p = p->rb_right;
+ else
+ return ref_blk;
+ }
+
+ return NULL;
+}
+
+static void refcount_block_insert(struct disable_refcount_ctxt *ctxt,
+ struct refcount_block *insert_rb)
+{
+ struct rb_node **p = &ctxt->ref_blknos.rb_node;
+ struct rb_node *parent = NULL;
+ struct refcount_block *ref_blk = NULL;
+
+ while (*p) {
+ parent = *p;
+ ref_blk = rb_entry(parent, struct refcount_block, ref_node);
+ if (insert_rb->blkno < ref_blk->blkno)
+ p = &(*p)->rb_left;
+ else if (insert_rb->blkno > ref_blk->blkno)
+ p = &(*p)->rb_right;
+ else
+ assert(0); /* Caller checked */
+ }
+
+ rb_link_node(&insert_rb->ref_node, parent, p);
+ rb_insert_color(&insert_rb->ref_node, &ctxt->ref_blknos);
+}
+
+static void empty_refcount_file_context(struct disable_refcount_ctxt *ctxt)
+{
+ struct refcount_block *ref_blk;
+ struct refcount_file *file;
+ struct rb_node *node;
+ struct list_head *p, *next;
+
+ while ((node = rb_first(&ctxt->ref_blknos)) != NULL) {
+ ref_blk = rb_entry(node, struct refcount_block, ref_node);
+
+ list_for_each_safe(p, next, &ref_blk->files_list) {
+ file = list_entry(p, struct refcount_file, list);
+ list_del(&file->list);
+ ocfs2_free(&file);
+ }
+
+ rb_erase(&ref_blk->ref_node, &ctxt->ref_blknos);
+ ocfs2_free(&ref_blk);
+ }
+}
+
+static errcode_t ocfs2_find_refcounted_clusters(ocfs2_filesys *fs,
+ uint64_t blkno,
+ uint32_t *clusters)
+{
+ errcode_t ret;
+ ocfs2_cached_inode *ci = NULL;
+ uint32_t cpos, len, p_cluster, num_clusters;
+ uint16_t ext_flags;
+
+ ret = ocfs2_read_cached_inode(fs, blkno, &ci);
+ if (ret)
+ goto out;
+
+ *clusters = 0;
+ cpos = 0;
+ len = ocfs2_clusters_in_bytes(fs, ci->ci_inode->i_size);
+ while (len) {
+ ret = ocfs2_get_clusters(ci, cpos, &p_cluster,
+ &num_clusters, &ext_flags);
+ if (ret)
+ break;
+
+ if (ext_flags & OCFS2_EXT_REFCOUNTED)
+ *clusters += num_clusters;
+
+ len -= num_clusters;
+ cpos += num_clusters;
+ }
+out:
+ if (ci)
+ ocfs2_free_cached_inode(fs, ci);
+
+ return ret;
+}
+
+static errcode_t refcount_iterate(ocfs2_filesys *fs, struct ocfs2_dinode *di,
+ void *user_data)
+{
+ errcode_t ret = 0;
+ uint32_t clusters;
+ uint64_t blk_num;
+ struct refcount_file *file = NULL;
+ struct refcount_block *ref_blk = NULL;
+ struct disable_refcount_ctxt *ctxt = user_data;
+ uint32_t recs_per_eb = ocfs2_extent_recs_per_eb(fs->fs_blocksize);
+
+ if (!S_ISREG(di->i_mode))
+ goto bail;
+
+ if (di->i_flags & OCFS2_SYSTEM_FL)
+ goto bail;
+
+ if (di->i_dyn_features & OCFS2_INLINE_DATA_FL)
+ goto bail;
+
+ if (!(di->i_dyn_features & OCFS2_HAS_REFCOUNT_FL))
+ goto bail;
+
+ ret = ocfs2_find_refcounted_clusters(fs, di->i_blkno, &clusters);
+ if (ret)
+ goto bail;
+
+ ret = ocfs2_malloc0(sizeof(struct refcount_file), &file);
+ if (ret)
+ goto bail;
+
+ file->blkno = di->i_blkno;
+ INIT_LIST_HEAD(&file->list);
+
+ ref_blk = refcount_block_lookup(ctxt, di->i_refcount_loc);
+ if (!ref_blk) {
+ ret = ocfs2_malloc0(sizeof(struct refcount_block), &ref_blk);
+ if (ret)
+ goto bail;
+
+ ref_blk->blkno = di->i_refcount_loc;
+ INIT_LIST_HEAD(&ref_blk->files_list);
+ refcount_block_insert(ctxt, ref_blk);
+ }
+ list_add_tail(&file->list, &ref_blk->files_list);
+
+ ctxt->more_clusters += clusters;
+ blk_num = (clusters + recs_per_eb - 1) / recs_per_eb;
+ ctxt->more_ebs += ocfs2_clusters_in_blocks(fs, blk_num);
+ ctxt->files_count++;
+
+ tools_progress_step(ctxt->prog, 1);
+
+ return 0;
+
+bail:
+ if (file)
+ ocfs2_free(&file);
+
+ return ret;
+}
+
+static errcode_t find_refcounted_files(ocfs2_filesys *fs,
+ struct disable_refcount_ctxt *ctxt)
+{
+ errcode_t ret;
+ uint32_t free_clusters = 0;
+
+ ctxt->prog = tools_progress_start("Scanning filesystem", "scanning",
+ 0);
+ if (!ctxt->prog) {
+ ret = TUNEFS_ET_NO_MEMORY;
+ goto bail;
+ }
+
+ ret = tunefs_foreach_inode(fs, refcount_iterate, ctxt);
+ if (ret)
+ goto bail;
+
+ ret = tunefs_get_free_clusters(fs, &free_clusters);
+ if (ret)
+ goto bail;
+
+ verbosef(VL_APP,
+ "We have %u clusters free, and need %u clusters to fill "
+ "every sparse file and %u clusters for more extent "
+ "blocks\n",
+ free_clusters, ctxt->more_clusters,
+ ctxt->more_ebs);
+
+ if (free_clusters < (ctxt->more_clusters + ctxt->more_ebs))
+ ret = OCFS2_ET_NO_SPACE;
+
+bail:
+ if (ctxt->prog) {
+ tools_progress_stop(ctxt->prog);
+ ctxt->prog = NULL;
+ }
+
+ return ret;
+}
+
+static errcode_t refcount_one_file(ocfs2_filesys *fs,
+ struct refcount_file *file)
+{
+ errcode_t ret;
+ ocfs2_cached_inode *ci = NULL;
+ uint32_t len;
+
+ ret = ocfs2_read_cached_inode(fs, file->blkno, &ci);
+ if (ret)
+ goto out;
+
+ len = ocfs2_clusters_in_bytes(fs, ci->ci_inode->i_size);
+
+ ret = ocfs2_refcount_cow(ci, 0, len, UINT_MAX);
+ if (ret)
+ goto out;
+
+ ci->ci_inode->i_dyn_features &= ~OCFS2_HAS_REFCOUNT_FL;
+ ci->ci_inode->i_refcount_loc = 0;
+
+ ret = ocfs2_write_cached_inode(fs, ci);
+
+out:
+ if (ci)
+ ocfs2_free_cached_inode(fs, ci);
+
+ return ret;
+}
+
+static errcode_t free_refcount_tree(ocfs2_filesys *fs,
+ struct refcount_block *ref_blk)
+{
+ errcode_t ret;
+ char *buf = NULL;
+ struct ocfs2_refcount_block *rb = NULL;
+
+ ret = ocfs2_malloc_block(fs->fs_io, &buf);
+ if (ret)
+ goto out;
+
+ ret = ocfs2_read_refcount_block(fs, ref_blk->blkno, buf);
+ if (ret)
+ goto out;
+
+ rb = (struct ocfs2_refcount_block *)buf;
+
+ /* Now the refcount tree should be an empty leaf one. */
+ assert(!(rb->rf_flags & OCFS2_REFCOUNT_TREE_FL));
+ assert(!rb->rf_records.rl_used);
+
+ ret = ocfs2_delete_refcount_block(fs, ref_blk->blkno);
+
+out:
+ if (buf)
+ ocfs2_free(&buf);
+ return ret;
+}
+
+static errcode_t replace_refcounted_files(ocfs2_filesys *fs,
+ struct disable_refcount_ctxt *ctxt)
+{
+ errcode_t ret = 0;
+ struct tools_progress *prog;
+ struct refcount_block *ref_blk;
+ struct refcount_file *file;
+ struct rb_node *node;
+ struct list_head *p, *next;
+
+ prog = tools_progress_start("Replacing files", "replacing",
+ ctxt->files_count);
+ if (!prog) {
+ ret = TUNEFS_ET_NO_MEMORY;
+ goto out;
+ }
+
+ while ((node = rb_first(&ctxt->ref_blknos)) != NULL) {
+ ref_blk = rb_entry(node, struct refcount_block, ref_node);
+
+ list_for_each_safe(p, next, &ref_blk->files_list) {
+ file = list_entry(p, struct refcount_file, list);
+ ret = refcount_one_file(fs, file);
+ if (ret)
+ goto out;
+ tools_progress_step(prog, 1);
+ }
+
+ ret = free_refcount_tree(fs, ref_blk);
+ if (ret)
+ goto out;
+ rb_erase(&ref_blk->ref_node, &ctxt->ref_blknos);
+ ocfs2_free(&ref_blk);
+ }
+
+out:
+ if (prog)
+ tools_progress_stop(prog);
+
+ return ret;
+}
+
+static int disable_refcount(ocfs2_filesys *fs, int flags)
+{
+ errcode_t ret = 0;
+ struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super);
+ struct disable_refcount_ctxt ctxt;
+ struct tools_progress *prog = NULL;
+
+ if (!ocfs2_refcount_tree(super)) {
+ verbosef(VL_APP,
+ "Refcount feature is not enabled; "
+ "nothing to disable\n");
+ goto out;
+ }
+
+ if (!tools_interact("Disable the refcount feature on device "
+ "\"%s\"? ", fs->fs_devname))
+ goto out;
+
+ prog = tools_progress_start("Disabling refcount", "norefcount", 3);
+ if (!prog) {
+ ret = TUNEFS_ET_NO_MEMORY;
+ tcom_err(ret, "while initializing the progress display");
+ goto out;
+ }
+
+ memset(&ctxt, 0, sizeof(ctxt));
+ ctxt.ref_blknos = RB_ROOT;
+
+ ret = find_refcounted_files(fs, &ctxt);
+ if (ret) {
+ if (ret == OCFS2_ET_NO_SPACE)
+ errorf("There is not enough space to fill all of "
+ "the refcounted files on device \"%s\"\n",
+ fs->fs_devname);
+ else
+ tcom_err(ret, "while trying to find refcounted files");
+ goto out_cleanup;
+ }
+ tools_progress_step(prog, 1);
+
+ ret = replace_refcounted_files(fs, &ctxt);
+ if (ret) {
+ tcom_err(ret,
+ "while trying to replace refcounted files on device "
+ "\"%s\"", fs->fs_devname);
+ goto out_cleanup;
+ }
+ tools_progress_step(prog, 1);
+
+ OCFS2_CLEAR_INCOMPAT_FEATURE(super,
+ OCFS2_FEATURE_INCOMPAT_REFCOUNT_TREE);
+ tunefs_block_signals();
+ ret = ocfs2_write_super(fs);
+ tunefs_unblock_signals();
+ if (ret)
+ tcom_err(ret, "while writing out the superblock");
+
+ tools_progress_step(prog, 1);
+
+out_cleanup:
+ empty_refcount_file_context(&ctxt);
+
+out:
+ if (prog)
+ tools_progress_stop(prog);
+
+ return ret;
+}
+
+static int enable_refcount(ocfs2_filesys *fs, int flags)
+{
+ errcode_t ret = 0;
+ struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super);
+ struct tools_progress *prog;
+
+ if (ocfs2_refcount_tree(super)) {
+ verbosef(VL_APP,
+ "Refcount feature is already enabled; "
+ "nothing to enable\n");
+ goto out;
+ }
+
+ if (!tools_interact("Enable the refcount feature on "
+ "device \"%s\"? ",
+ fs->fs_devname))
+ goto out;
+
+ prog = tools_progress_start("Enable refcount", "refcount", 1);
+ if (!prog) {
+ ret = TUNEFS_ET_NO_MEMORY;
+ tcom_err(ret, "while initializing the progress display");
+ goto out;
+ }
+
+ OCFS2_SET_INCOMPAT_FEATURE(super,
+ OCFS2_FEATURE_INCOMPAT_REFCOUNT_TREE);
+ tunefs_block_signals();
+ ret = ocfs2_write_super(fs);
+ tunefs_unblock_signals();
+ if (ret)
+ tcom_err(ret, "while writing out the superblock");
+
+ tools_progress_step(prog, 1);
+ tools_progress_stop(prog);
+out:
+ return ret;
+}
+
+DEFINE_TUNEFS_FEATURE_INCOMPAT(refcount,
+ OCFS2_FEATURE_INCOMPAT_REFCOUNT_TREE,
+ TUNEFS_FLAG_RW | TUNEFS_FLAG_ALLOCATION |
+ TUNEFS_FLAG_LARGECACHE,
+ enable_refcount,
+ disable_refcount);
+
+#ifdef DEBUG_EXE
+int main(int argc, char *argv[])
+{
+ return tunefs_feature_main(argc, argv, &refcount_feature);
+}
+#endif
diff --git a/tunefs.ocfs2/op_features.c b/tunefs.ocfs2/op_features.c
index afcc1f4..91abca1 100644
--- a/tunefs.ocfs2/op_features.c
+++ b/tunefs.ocfs2/op_features.c
@@ -43,6 +43,7 @@ extern struct tunefs_feature unwritten_extents_feature;
extern struct tunefs_feature xattr_feature;
extern struct tunefs_feature usrquota_feature;
extern struct tunefs_feature grpquota_feature;
+extern struct tunefs_feature refcount_feature;
/* List of features supported by ocfs2ne */
static struct tunefs_feature *features[] = {
@@ -56,6 +57,7 @@ static struct tunefs_feature *features[] = {
&xattr_feature,
&usrquota_feature,
&grpquota_feature,
+ &refcount_feature,
NULL,
};
--
1.5.5
More information about the Ocfs2-tools-devel
mailing list