From 379b9f7247a4daac9545e3dec79d3c2660111d8d Mon Sep 17 00:00:00 2001 From: Hari Gowtham Date: Mon, 8 Apr 2019 11:32:09 +0530 Subject: [PATCH 085/124] Revert "all: remove code which is not being considered in build" This reverts most part of commit 8293d21280fd6ddfc9bb54068cf87794fc6be207. It adds in the changes for tier and CTR with the neccesary changes for building it. Label: DOWNSTREAM ONLY Change-Id: I8f7978618f2a6a949b09dbcfd25722494cb8f1cd Signed-off-by: Hari Gowtham Reviewed-on: https://code.engineering.redhat.com/gerrit/166245 Reviewed-by: Nithya Balachandran Tested-by: RHGS Build Bot Reviewed-by: Sunil Kumar Heggodu Gopala Acharya --- Makefile.am | 8 +- configure.ac | 34 + glusterfs.spec.in | 19 + libglusterfs/Makefile.am | 4 +- libglusterfs/src/glusterfs/mem-types.h | 1 + xlators/cluster/dht/src/Makefile.am | 14 +- xlators/cluster/dht/src/dht-rebalance.c | 12 + xlators/cluster/dht/src/tier-common.c | 1199 ++++++++ xlators/cluster/dht/src/tier-common.h | 55 + xlators/cluster/dht/src/tier.c | 3105 ++++++++++++++++++++ xlators/cluster/dht/src/tier.h | 110 + xlators/features/Makefile.am | 2 +- xlators/features/changetimerecorder/Makefile.am | 3 + .../features/changetimerecorder/src/Makefile.am | 26 + .../changetimerecorder/src/changetimerecorder.c | 2371 +++++++++++++++ .../changetimerecorder/src/changetimerecorder.h | 21 + .../features/changetimerecorder/src/ctr-helper.c | 293 ++ .../features/changetimerecorder/src/ctr-helper.h | 854 ++++++ .../features/changetimerecorder/src/ctr-messages.h | 61 + .../changetimerecorder/src/ctr-xlator-ctx.c | 362 +++ .../changetimerecorder/src/ctr-xlator-ctx.h | 68 + .../changetimerecorder/src/ctr_mem_types.h | 22 + 22 files changed, 8637 insertions(+), 7 deletions(-) create mode 100644 xlators/cluster/dht/src/tier-common.c create mode 100644 xlators/cluster/dht/src/tier-common.h create mode 100644 xlators/cluster/dht/src/tier.c create mode 100644 xlators/cluster/dht/src/tier.h create mode 100644 xlators/features/changetimerecorder/Makefile.am create mode 100644 xlators/features/changetimerecorder/src/Makefile.am create mode 100644 xlators/features/changetimerecorder/src/changetimerecorder.c create mode 100644 xlators/features/changetimerecorder/src/changetimerecorder.h create mode 100644 xlators/features/changetimerecorder/src/ctr-helper.c create mode 100644 xlators/features/changetimerecorder/src/ctr-helper.h create mode 100644 xlators/features/changetimerecorder/src/ctr-messages.h create mode 100644 xlators/features/changetimerecorder/src/ctr-xlator-ctx.c create mode 100644 xlators/features/changetimerecorder/src/ctr-xlator-ctx.h create mode 100644 xlators/features/changetimerecorder/src/ctr_mem_types.h diff --git a/Makefile.am b/Makefile.am index e0c795f..613382f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3,7 +3,7 @@ SOURCES = site.h EXTRA_DIST = autogen.sh \ COPYING-GPLV2 COPYING-LGPLV3 COMMITMENT \ INSTALL README.md AUTHORS THANKS NEWS \ - glusterfs.spec glusterfs-api.pc.in libgfchangelog.pc.in \ + glusterfs.spec glusterfs-api.pc.in libgfchangelog.pc.in libgfdb.pc.in \ run-tests.sh \ build-aux/pkg-version \ contrib/umountd \ @@ -15,8 +15,12 @@ SUBDIRS = $(ARGP_STANDALONE_DIR) rpc/xdr/gen libglusterfs rpc api xlators \ pkgconfigdir = @pkgconfigdir@ pkgconfig_DATA = glusterfs-api.pc libgfchangelog.pc +if USE_GFDB +pkgconfig_DATA += libgfdb.pc +endif -CLEANFILES = glusterfs-api.pc libgfchangelog.pc contrib/umountd/Makefile +CLEANFILES = glusterfs-api.pc libgfchangelog.pc libgfdb.pc \ + contrib/umountd/Makefile gitclean: distclean find . -name Makefile.in -exec rm -f {} \; diff --git a/configure.ac b/configure.ac index baa811a..633e850 100644 --- a/configure.ac +++ b/configure.ac @@ -30,6 +30,7 @@ AC_CONFIG_HEADERS([config.h site.h]) AC_CONFIG_FILES([Makefile libglusterfs/Makefile libglusterfs/src/Makefile + libglusterfs/src/gfdb/Makefile geo-replication/src/peer_gsec_create geo-replication/src/peer_mountbroker geo-replication/src/peer_mountbroker.py @@ -121,6 +122,8 @@ AC_CONFIG_FILES([Makefile xlators/features/changelog/src/Makefile xlators/features/changelog/lib/Makefile xlators/features/changelog/lib/src/Makefile + xlators/features/changetimerecorder/Makefile + xlators/features/changetimerecorder/src/Makefile xlators/features/locks/Makefile xlators/features/locks/src/Makefile xlators/features/quota/Makefile @@ -237,6 +240,7 @@ AC_CONFIG_FILES([Makefile contrib/umountd/Makefile glusterfs-api.pc libgfchangelog.pc + libgfdb.pc api/Makefile api/src/Makefile api/examples/Makefile @@ -866,6 +870,33 @@ AM_CONDITIONAL([USE_FIREWALLD],test ["x${BUILD_FIREWALLD}" = "xyes"]) #endof firewald section +# Data tiering requires sqlite +AC_ARG_ENABLE([tiering], + AC_HELP_STRING([--disable-tiering], + [Disable data classification/tiering]), + [BUILD_GFDB="${enableval}"], [BUILD_GFDB="yes"]) + +case $host_os in + darwin*) + SQLITE_LIBS="-lsqlite3" + AC_CHECK_HEADERS([sqlite3.h], AC_DEFINE(USE_GFDB, 1)) + ;; + *) + if test "x${BUILD_GFDB}" = "xyes"; then + PKG_CHECK_MODULES([SQLITE], [sqlite3], + AC_DEFINE(USE_GFDB, 1), + AC_MSG_ERROR([pass --disable-tiering to build without sqlite])) + else + AC_DEFINE(USE_GFDB, 0, [no sqlite, gfdb is disabled]) + fi + ;; +esac + +AC_SUBST(SQLITE_CFLAGS) +AC_SUBST(SQLITE_LIBS) +AM_CONDITIONAL(BUILD_GFDB, test "x${with_server}" = "xyes" -a "x${BUILD_GFDB}" = "xyes") +AM_CONDITIONAL(USE_GFDB, test "x${with_server}" = "xyes" -a "x${BUILD_GFDB}" = "xyes") + # xml-output AC_ARG_ENABLE([xml-output], AC_HELP_STRING([--disable-xml-output], @@ -1544,6 +1575,8 @@ GFAPI_VERSION="7."${PACKAGE_VERSION} LIBGFCHANGELOG_VERSION="0.0.1" AC_SUBST(GFAPI_VERSION) AC_SUBST(LIBGFCHANGELOG_VERSION) +LIBGFDB_VERSION="0.0.1" +AC_SUBST(LIBGFDB_VERSION) dnl libtool versioning LIBGFXDR_LT_VERSION="0:1:0" @@ -1584,6 +1617,7 @@ echo "XML output : $BUILD_XML_OUTPUT" echo "Unit Tests : $BUILD_UNITTEST" echo "Track priv ports : $TRACK_PRIVPORTS" echo "POSIX ACLs : $BUILD_POSIX_ACLS" +echo "Data Classification : $BUILD_GFDB" echo "firewalld-config : $BUILD_FIREWALLD" echo "Events : $BUILD_EVENTS" echo "EC dynamic support : $EC_DYNAMIC_SUPPORT" diff --git a/glusterfs.spec.in b/glusterfs.spec.in index 2149f86..e0607ba 100644 --- a/glusterfs.spec.in +++ b/glusterfs.spec.in @@ -154,6 +154,7 @@ %global _without_events --disable-events %global _without_georeplication --disable-georeplication %global _with_gnfs %{nil} +%global _without_tiering --disable-tiering %global _without_ocf --without-ocf %endif @@ -287,6 +288,9 @@ BuildRequires: libuuid-devel %if ( 0%{?_with_cmocka:1} ) BuildRequires: libcmocka-devel >= 1.0.1 %endif +%if ( 0%{!?_without_tiering:1} ) +BuildRequires: sqlite-devel +%endif %if ( 0%{!?_without_georeplication:1} ) BuildRequires: libattr-devel %endif @@ -797,6 +801,7 @@ export LDFLAGS %{?_without_rdma} \ %{?_without_server} \ %{?_without_syslog} \ + %{?_without_tiering} \ %{?_with_ipv6default} \ %{?_without_libtirpc} @@ -1232,9 +1237,15 @@ exit 0 %if ( 0%{?_without_server:1} ) %exclude %{_libdir}/pkgconfig/libgfchangelog.pc %exclude %{_libdir}/libgfchangelog.so +%if ( 0%{!?_without_tiering:1} ) +%{_libdir}/pkgconfig/libgfdb.pc +%endif %else %{_libdir}/pkgconfig/libgfchangelog.pc %{_libdir}/libgfchangelog.so +%if ( 0%{!?_without_tiering:1} ) +%{_libdir}/pkgconfig/libgfdb.pc +%endif %endif %files client-xlators @@ -1330,6 +1341,10 @@ exit 0 %files libs %{_libdir}/*.so.* %exclude %{_libdir}/libgfapi.* +%if ( 0%{!?_without_tiering:1} ) +# libgfdb is only needed server-side +%exclude %{_libdir}/libgfdb.* +%endif %files -n python%{_pythonver}-gluster # introducing glusterfs module in site packages. @@ -1417,6 +1432,10 @@ exit 0 %{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/bit-rot.so %{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/bitrot-stub.so %{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/sdfs.so +%if ( 0%{!?_without_tiering:1} ) + %{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/changetimerecorder.so + %{_libdir}/libgfdb.so.* +%endif %{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/index.so %{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/locks.so %{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/posix* diff --git a/libglusterfs/Makefile.am b/libglusterfs/Makefile.am index d471a3f..7e72f61 100644 --- a/libglusterfs/Makefile.am +++ b/libglusterfs/Makefile.am @@ -1,3 +1,3 @@ -SUBDIRS = src +SUBDIRS = src src/gfdb -CLEANFILES = +CLEANFILES = diff --git a/libglusterfs/src/glusterfs/mem-types.h b/libglusterfs/src/glusterfs/mem-types.h index 832f68c..92730a9 100644 --- a/libglusterfs/src/glusterfs/mem-types.h +++ b/libglusterfs/src/glusterfs/mem-types.h @@ -138,6 +138,7 @@ enum gf_common_mem_types_ { gf_common_volfile_t, gf_common_mt_mgmt_v3_lock_timer_t, /* used only in one location */ gf_common_mt_server_cmdline_t, /* used only in one location */ + gf_mt_gfdb_query_record_t, gf_common_mt_end }; #endif diff --git a/xlators/cluster/dht/src/Makefile.am b/xlators/cluster/dht/src/Makefile.am index 56f1f2a..5532047 100644 --- a/xlators/cluster/dht/src/Makefile.am +++ b/xlators/cluster/dht/src/Makefile.am @@ -1,4 +1,7 @@ xlator_LTLIBRARIES = dht.la nufa.la switch.la +if BUILD_GFDB + xlator_LTLIBRARIES += tier.la +endif AM_CFLAGS = -Wall $(GF_CFLAGS) @@ -13,6 +16,7 @@ dht_la_SOURCES = $(dht_common_source) dht.c nufa_la_SOURCES = $(dht_common_source) nufa.c switch_la_SOURCES = $(dht_common_source) switch.c +tier_la_SOURCES = $(dht_common_source) tier.c tier-common.c dht_la_LDFLAGS = -module $(GF_XLATOR_DEFAULT_LDFLAGS) dht_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la @@ -23,15 +27,21 @@ nufa_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la switch_la_LDFLAGS = -module $(GF_XLATOR_DEFAULT_LDFLAGS) switch_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la +tier_la_LDFLAGS = -module $(GF_XLATOR_DEFAULT_LDFLAGS) $(LIB_DL) +tier_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la + noinst_HEADERS = dht-common.h dht-mem-types.h dht-messages.h \ - dht-lock.h $(top_builddir)/xlators/lib/src/libxlator.h + dht-lock.h tier-common.h tier.h \ + $(top_builddir)/xlators/lib/src/libxlator.h AM_CPPFLAGS = $(GF_CPPFLAGS) -I$(top_srcdir)/libglusterfs/src \ + -I$(top_srcdir)/libglusterfs/src/gfdb \ -I$(top_srcdir)/rpc/xdr/src -I$(top_builddir)/rpc/xdr/src \ -I$(top_srcdir)/rpc/rpc-lib/src \ -I$(top_srcdir)/xlators/lib/src \ -DDATADIR=\"$(localstatedir)\" \ - -DLIBDIR=\"$(libdir)\" + -DLIBDIR=\"$(libdir)\" \ + -DLIBGFDB_VERSION=\"$(LIBGFDB_VERSION)\" CLEANFILES = diff --git a/xlators/cluster/dht/src/dht-rebalance.c b/xlators/cluster/dht/src/dht-rebalance.c index e0f25b1..efbe8a4 100644 --- a/xlators/cluster/dht/src/dht-rebalance.c +++ b/xlators/cluster/dht/src/dht-rebalance.c @@ -8,6 +8,7 @@ cases as published by the Free Software Foundation. */ +#include "tier.h" #include "dht-common.h" #include #include @@ -2134,6 +2135,17 @@ dht_migrate_file(xlator_t *this, loc_t *loc, xlator_t *from, xlator_t *to, } } + /* store size of previous migrated file */ + if (defrag && defrag->tier_conf.is_tier) { + if (from != TIER_HASHED_SUBVOL) { + defrag->tier_conf.st_last_promoted_size = stbuf.ia_size; + } else { + /* Don't delete the linkto file on the hashed subvol */ + delete_src_linkto = _gf_false; + defrag->tier_conf.st_last_demoted_size = stbuf.ia_size; + } + } + /* The src file is being unlinked after this so we don't need to clean it up */ clean_src = _gf_false; diff --git a/xlators/cluster/dht/src/tier-common.c b/xlators/cluster/dht/src/tier-common.c new file mode 100644 index 0000000..b22f477 --- /dev/null +++ b/xlators/cluster/dht/src/tier-common.c @@ -0,0 +1,1199 @@ +/* + Copyright (c) 2015 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#include +#include +#include "libxlator.h" +#include "dht-common.h" +#include +#include "tier-common.h" +#include "tier.h" + +int +dht_link_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, + int op_errno, inode_t *inode, struct iatt *stbuf, + struct iatt *preparent, struct iatt *postparent, dict_t *xdata); + +int +tier_link_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, + int op_errno, inode_t *inode, struct iatt *stbuf, + struct iatt *preparent, struct iatt *postparent, dict_t *xdata) +{ + dht_local_t *local = NULL; + loc_t *oldloc = NULL; + loc_t *newloc = NULL; + + local = frame->local; + + oldloc = &local->loc; + newloc = &local->loc2; + + if (op_ret == -1) { + /* No continuation on DHT inode missing errors, as we should + * then have a good stbuf that states P2 happened. We would + * get inode missing if, the file completed migrated between + * the lookup and the link call */ + goto out; + } + + if (local->call_cnt != 1) { + goto out; + } + + local->call_cnt = 2; + + /* Do this on the hot tier now */ + + STACK_WIND(frame, tier_link_cbk, local->cached_subvol, + local->cached_subvol->fops->link, oldloc, newloc, xdata); + + return 0; + +out: + DHT_STRIP_PHASE1_FLAGS(stbuf); + + DHT_STACK_UNWIND(link, frame, op_ret, op_errno, inode, stbuf, preparent, + postparent, NULL); + + return 0; +} + +int +tier_link(call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc, + dict_t *xdata) +{ + xlator_t *cached_subvol = NULL; + xlator_t *hashed_subvol = NULL; + int op_errno = -1; + int ret = -1; + dht_local_t *local = NULL; + dht_conf_t *conf = NULL; + + VALIDATE_OR_GOTO(frame, err); + VALIDATE_OR_GOTO(this, err); + VALIDATE_OR_GOTO(oldloc, err); + VALIDATE_OR_GOTO(newloc, err); + + conf = this->private; + + local = dht_local_init(frame, oldloc, NULL, GF_FOP_LINK); + if (!local) { + op_errno = ENOMEM; + goto err; + } + local->call_cnt = 1; + + cached_subvol = local->cached_subvol; + + if (!cached_subvol) { + gf_msg_debug(this->name, 0, "no cached subvolume for path=%s", + oldloc->path); + op_errno = ENOENT; + goto err; + } + + hashed_subvol = TIER_HASHED_SUBVOL; + + ret = loc_copy(&local->loc2, newloc); + if (ret == -1) { + op_errno = ENOMEM; + goto err; + } + + if (hashed_subvol == cached_subvol) { + STACK_WIND(frame, dht_link_cbk, cached_subvol, + cached_subvol->fops->link, oldloc, newloc, xdata); + return 0; + } + + /* Create hardlinks to both the data file on the hot tier + and the linkto file on the cold tier */ + + gf_uuid_copy(local->gfid, oldloc->inode->gfid); + + STACK_WIND(frame, tier_link_cbk, hashed_subvol, hashed_subvol->fops->link, + oldloc, newloc, xdata); + + return 0; +err: + op_errno = (op_errno == -1) ? errno : op_errno; + DHT_STACK_UNWIND(link, frame, -1, op_errno, NULL, NULL, NULL, NULL, NULL); + return 0; +} + +int +tier_create_unlink_stale_linkto_cbk(call_frame_t *frame, void *cookie, + xlator_t *this, int op_ret, int op_errno, + struct iatt *preparent, + struct iatt *postparent, dict_t *xdata) +{ + dht_local_t *local = NULL; + + local = frame->local; + + if (local->params) { + dict_del(local->params, GLUSTERFS_INTERNAL_FOP_KEY); + } + + DHT_STACK_UNWIND(create, frame, -1, local->op_errno, NULL, NULL, NULL, NULL, + NULL, NULL); + + return 0; +} + +int +tier_create_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, + int op_errno, fd_t *fd, inode_t *inode, struct iatt *stbuf, + struct iatt *preparent, struct iatt *postparent, dict_t *xdata) +{ + xlator_t *prev = NULL; + int ret = -1; + dht_local_t *local = NULL; + xlator_t *hashed_subvol = NULL; + dht_conf_t *conf = NULL; + + local = frame->local; + conf = this->private; + + hashed_subvol = TIER_HASHED_SUBVOL; + + if (!local) { + op_ret = -1; + op_errno = EINVAL; + goto out; + } + + if (op_ret == -1) { + if (local->linked == _gf_true && local->xattr_req) { + local->op_errno = op_errno; + local->op_ret = op_ret; + ret = dht_fill_dict_to_avoid_unlink_of_migrating_file( + local->xattr_req); + if (ret) { + gf_msg(this->name, GF_LOG_WARNING, 0, DHT_MSG_DICT_SET_FAILED, + "Failed to set dictionary value to " + "unlink of migrating file"); + goto out; + } + + STACK_WIND(frame, tier_create_unlink_stale_linkto_cbk, + hashed_subvol, hashed_subvol->fops->unlink, &local->loc, + 0, local->xattr_req); + return 0; + } + goto out; + } + + prev = cookie; + + if (local->loc.parent) { + dht_inode_ctx_time_update(local->loc.parent, this, preparent, 0); + + dht_inode_ctx_time_update(local->loc.parent, this, postparent, 1); + } + + ret = dht_layout_preset(this, prev, inode); + if (ret != 0) { + gf_msg_debug(this->name, 0, "could not set preset layout for subvol %s", + prev->name); + op_ret = -1; + op_errno = EINVAL; + goto out; + } + + local->op_errno = op_errno; + + if (local->linked == _gf_true) { + local->stbuf = *stbuf; + dht_linkfile_attr_heal(frame, this); + } +out: + if (local) { + if (local->xattr_req) { + dict_del(local->xattr_req, TIER_LINKFILE_GFID); + } + } + + DHT_STRIP_PHASE1_FLAGS(stbuf); + + DHT_STACK_UNWIND(create, frame, op_ret, op_errno, fd, inode, stbuf, + preparent, postparent, xdata); + + return 0; +} + +int +tier_create_linkfile_create_cbk(call_frame_t *frame, void *cookie, + xlator_t *this, int32_t op_ret, + int32_t op_errno, inode_t *inode, + struct iatt *stbuf, struct iatt *preparent, + struct iatt *postparent, dict_t *xdata) +{ + dht_local_t *local = NULL; + xlator_t *cached_subvol = NULL; + dht_conf_t *conf = NULL; + int ret = -1; + unsigned char *gfid = NULL; + + local = frame->local; + if (!local) { + op_errno = EINVAL; + goto err; + } + + if (op_ret == -1) { + local->op_errno = op_errno; + goto err; + } + + conf = this->private; + if (!conf) { + local->op_errno = EINVAL; + op_errno = EINVAL; + goto err; + } + + cached_subvol = TIER_UNHASHED_SUBVOL; + + if (local->params) { + dict_del(local->params, conf->link_xattr_name); + dict_del(local->params, GLUSTERFS_INTERNAL_FOP_KEY); + } + + /* + * We will delete the linkfile if data file creation fails. + * When deleting this stale linkfile, there is a possibility + * for a race between this linkfile deletion and a stale + * linkfile deletion triggered by another lookup from different + * client. + * + * For eg: + * + * Client 1 Client 2 + * + * 1 linkfile created for foo + * + * 2 data file creation failed + * + * 3 creating a file with same name + * + * 4 lookup before creation deleted + * the linkfile created by client1 + * considering as a stale linkfile. + * + * 5 New linkfile created for foo + * with different gfid. + * + * 6 Trigger linkfile deletion as + * data file creation failed. + * + * 7 Linkfile deleted which is + * created by client2. + * + * 8 Data file created. + * + * With this race, we will end up having a file in a non-hashed subvol + * without a linkfile in hashed subvol. + * + * To avoid this, we store the gfid of linkfile created by client, So + * If we delete the linkfile , we validate gfid of existing file with + * stored value from posix layer. + * + * Storing this value in local->xattr_req as local->params was also used + * to create the data file. During the linkfile deletion we will use + * local->xattr_req dictionary. + */ + if (!local->xattr_req) { + local->xattr_req = dict_new(); + if (!local->xattr_req) { + local->op_errno = ENOMEM; + op_errno = ENOMEM; + goto err; + } + } + + gfid = GF_MALLOC(sizeof(uuid_t), gf_common_mt_char); + if (!gfid) { + local->op_errno = ENOMEM; + op_errno = ENOMEM; + goto err; + } + + gf_uuid_copy(gfid, stbuf->ia_gfid); + ret = dict_set_dynptr(local->xattr_req, TIER_LINKFILE_GFID, gfid, + sizeof(uuid_t)); + if (ret) { + GF_FREE(gfid); + gf_msg(this->name, GF_LOG_WARNING, 0, DHT_MSG_DICT_SET_FAILED, + "Failed to set dictionary value" + " : key = %s", + TIER_LINKFILE_GFID); + } + + STACK_WIND_COOKIE(frame, tier_create_cbk, cached_subvol, cached_subvol, + cached_subvol->fops->create, &local->loc, local->flags, + local->mode, local->umask, local->fd, local->params); + + return 0; +err: + DHT_STACK_UNWIND(create, frame, -1, op_errno, NULL, NULL, NULL, NULL, NULL, + NULL); + return 0; +} + +gf_boolean_t +tier_is_hot_tier_decommissioned(xlator_t *this) +{ + dht_conf_t *conf = NULL; + xlator_t *hot_tier = NULL; + int i = 0; + + conf = this->private; + hot_tier = conf->subvolumes[1]; + + if (conf->decommission_subvols_cnt) { + for (i = 0; i < conf->subvolume_cnt; i++) { + if (conf->decommissioned_bricks[i] && + conf->decommissioned_bricks[i] == hot_tier) + return _gf_true; + } + } + + return _gf_false; +} + +int +tier_create(call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags, + mode_t mode, mode_t umask, fd_t *fd, dict_t *params) +{ + int op_errno = -1; + dht_local_t *local = NULL; + dht_conf_t *conf = NULL; + xlator_t *hot_subvol = NULL; + xlator_t *cold_subvol = NULL; + + VALIDATE_OR_GOTO(frame, err); + VALIDATE_OR_GOTO(this, err); + VALIDATE_OR_GOTO(loc, err); + + conf = this->private; + + dht_get_du_info(frame, this, loc); + + local = dht_local_init(frame, loc, fd, GF_FOP_CREATE); + if (!local) { + op_errno = ENOMEM; + goto err; + } + + cold_subvol = TIER_HASHED_SUBVOL; + hot_subvol = TIER_UNHASHED_SUBVOL; + + if (conf->subvolumes[0] != cold_subvol) { + hot_subvol = conf->subvolumes[0]; + } + /* + * if hot tier full, write to cold. + * Also if hot tier is full, create in cold + */ + if (dht_is_subvol_filled(this, hot_subvol) || + tier_is_hot_tier_decommissioned(this)) { + gf_msg_debug(this->name, 0, "creating %s on %s", loc->path, + cold_subvol->name); + + STACK_WIND_COOKIE(frame, tier_create_cbk, cold_subvol, cold_subvol, + cold_subvol->fops->create, loc, flags, mode, umask, + fd, params); + } else { + local->params = dict_ref(params); + local->flags = flags; + local->mode = mode; + local->umask = umask; + local->cached_subvol = hot_subvol; + local->hashed_subvol = cold_subvol; + + gf_msg_debug(this->name, 0, "creating %s on %s (link at %s)", loc->path, + hot_subvol->name, cold_subvol->name); + + dht_linkfile_create(frame, tier_create_linkfile_create_cbk, this, + hot_subvol, cold_subvol, loc); + + goto out; + } +out: + return 0; + +err: + + op_errno = (op_errno == -1) ? errno : op_errno; + DHT_STACK_UNWIND(create, frame, -1, op_errno, NULL, NULL, NULL, NULL, NULL, + NULL); + + return 0; +} + +int +tier_unlink_nonhashed_linkfile_cbk(call_frame_t *frame, void *cookie, + xlator_t *this, int op_ret, int op_errno, + struct iatt *preparent, + struct iatt *postparent, dict_t *xdata) +{ + dht_local_t *local = NULL; + xlator_t *prev = NULL; + + local = frame->local; + prev = cookie; + + LOCK(&frame->lock); + { + if ((op_ret == -1) && (op_errno != ENOENT)) { + local->op_errno = op_errno; + local->op_ret = op_ret; + gf_msg_debug(this->name, op_errno, + "Unlink link: subvolume %s" + " returned -1", + prev->name); + goto unlock; + } + + local->op_ret = 0; + } +unlock: + UNLOCK(&frame->lock); + + if (local->op_ret == -1) + goto err; + DHT_STACK_UNWIND(unlink, frame, local->op_ret, local->op_errno, + &local->preparent, &local->postparent, NULL); + + return 0; + +err: + DHT_STACK_UNWIND(unlink, frame, -1, local->op_errno, NULL, NULL, NULL); + return 0; +} + +int +tier_unlink_lookup_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno, inode_t *inode, + struct iatt *preparent, dict_t *xdata, + struct iatt *postparent) +{ + dht_local_t *local = NULL; + xlator_t *prev = NULL; + dht_conf_t *conf = NULL; + xlator_t *hot_subvol = NULL; + + local = frame->local; + prev = cookie; + conf = this->private; + hot_subvol = TIER_UNHASHED_SUBVOL; + + if (!op_ret) { + /* + * linkfile present on hot tier. unlinking the linkfile + */ + STACK_WIND_COOKIE(frame, tier_unlink_nonhashed_linkfile_cbk, hot_subvol, + hot_subvol, hot_subvol->fops->unlink, &local->loc, + local->flags, NULL); + return 0; + } + + LOCK(&frame->lock); + { + if (op_errno == ENOENT) { + local->op_ret = 0; + local->op_errno = op_errno; + } else { + local->op_ret = op_ret; + local->op_errno = op_errno; + } + gf_msg_debug(this->name, op_errno, "Lookup : subvolume %s returned -1", + prev->name); + } + + UNLOCK(&frame->lock); + + DHT_STACK_UNWIND(unlink, frame, local->op_ret, local->op_errno, + &local->preparent, &local->postparent, xdata); + + return 0; +} + +int +tier_unlink_linkfile_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno, struct iatt *preparent, + struct iatt *postparent, dict_t *xdata) +{ + dht_local_t *local = NULL; + xlator_t *prev = NULL; + + local = frame->local; + prev = cookie; + + LOCK(&frame->lock); + { + /* Ignore EINVAL for tier to ignore error when the file + does not exist on the other tier */ + if ((op_ret == -1) && !((op_errno == ENOENT) || (op_errno == EINVAL))) { + local->op_errno = op_errno; + local->op_ret = op_ret; + gf_msg_debug(this->name, op_errno, + "Unlink link: subvolume %s" + " returned -1", + prev->name); + goto unlock; + } + + local->op_ret = 0; + } +unlock: + UNLOCK(&frame->lock); + + if (local->op_ret == -1) + goto err; + + DHT_STACK_UNWIND(unlink, frame, local->op_ret, local->op_errno, + &local->preparent, &local->postparent, xdata); + + return 0; + +err: + DHT_STACK_UNWIND(unlink, frame, -1, local->op_errno, NULL, NULL, NULL); + return 0; +} + +int32_t +tier_unlink_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, + int op_errno, struct iatt *preparent, struct iatt *postparent, + dict_t *xdata) +{ + dht_local_t *local = NULL; + xlator_t *prev = NULL; + struct iatt *stbuf = NULL; + dht_conf_t *conf = NULL; + int ret = -1; + xlator_t *hot_tier = NULL; + xlator_t *cold_tier = NULL; + + local = frame->local; + prev = cookie; + conf = this->private; + + cold_tier = TIER_HASHED_SUBVOL; + hot_tier = TIER_UNHASHED_SUBVOL; + + LOCK(&frame->lock); + { + if (op_ret == -1) { + if (op_errno == ENOENT) { + local->op_ret = 0; + } else { + local->op_ret = -1; + local->op_errno = op_errno; + } + gf_msg_debug(this->name, op_errno, + "Unlink: subvolume %s returned -1" + " with errno = %d", + prev->name, op_errno); + goto unlock; + } + + local->op_ret = 0; + + local->postparent = *postparent; + local->preparent = *preparent; + + if (local->loc.parent) { + dht_inode_ctx_time_update(local->loc.parent, this, + &local->preparent, 0); + dht_inode_ctx_time_update(local->loc.parent, this, + &local->postparent, 1); + } + } +unlock: + UNLOCK(&frame->lock); + + if (local->op_ret) + goto out; + + if (cold_tier != local->cached_subvol) { + /* + * File is present in hot tier, so there will be + * a link file on cold tier, deleting the linkfile + * from cold tier + */ + STACK_WIND_COOKIE(frame, tier_unlink_linkfile_cbk, cold_tier, cold_tier, + cold_tier->fops->unlink, &local->loc, local->flags, + xdata); + return 0; + } + + ret = dict_get_bin(xdata, DHT_IATT_IN_XDATA_KEY, (void **)&stbuf); + if (!ret && stbuf && + ((IS_DHT_MIGRATION_PHASE2(stbuf)) || IS_DHT_MIGRATION_PHASE1(stbuf))) { + /* + * File is migrating from cold to hot tier. + * Delete the destination linkfile. + */ + STACK_WIND_COOKIE(frame, tier_unlink_lookup_cbk, hot_tier, hot_tier, + hot_tier->fops->lookup, &local->loc, NULL); + return 0; + } + +out: + DHT_STACK_UNWIND(unlink, frame, local->op_ret, local->op_errno, + &local->preparent, &local->postparent, xdata); + + return 0; +} + +int +tier_unlink(call_frame_t *frame, xlator_t *this, loc_t *loc, int xflag, + dict_t *xdata) +{ + xlator_t *cached_subvol = NULL; + xlator_t *hashed_subvol = NULL; + dht_conf_t *conf = NULL; + int op_errno = -1; + dht_local_t *local = NULL; + int ret = -1; + + VALIDATE_OR_GOTO(frame, err); + VALIDATE_OR_GOTO(this, err); + VALIDATE_OR_GOTO(loc, err); + + conf = this->private; + + local = dht_local_init(frame, loc, NULL, GF_FOP_UNLINK); + if (!local) { + op_errno = ENOMEM; + + goto err; + } + + hashed_subvol = TIER_HASHED_SUBVOL; + + cached_subvol = local->cached_subvol; + if (!cached_subvol) { + gf_msg_debug(this->name, 0, "no cached subvolume for path=%s", + loc->path); + op_errno = EINVAL; + goto err; + } + + local->flags = xflag; + if (IA_ISREG(loc->inode->ia_type) && (hashed_subvol == cached_subvol)) { + /* + * File resides in cold tier. We need to stat + * the file to see if it is being promoted. + * If yes we need to delete the destination + * file as well. + * + * Currently we are doing this check only for + * regular files. + */ + xdata = xdata ? dict_ref(xdata) : dict_new(); + if (xdata) { + ret = dict_set_int8(xdata, DHT_IATT_IN_XDATA_KEY, 1); + if (ret) { + gf_msg_debug(this->name, 0, "Failed to set dictionary key %s", + DHT_IATT_IN_XDATA_KEY); + } + } + } + + /* + * File is on hot tier, delete the data file first, then + * linkfile from cold. + */ + STACK_WIND_COOKIE(frame, tier_unlink_cbk, cached_subvol, cached_subvol, + cached_subvol->fops->unlink, loc, xflag, xdata); + if (xdata) + dict_unref(xdata); + return 0; +err: + op_errno = (op_errno == -1) ? errno : op_errno; + DHT_STACK_UNWIND(unlink, frame, -1, op_errno, NULL, NULL, NULL); + + return 0; +} + +int +tier_readdir_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, + int op_errno, gf_dirent_t *orig_entries, dict_t *xdata) +{ + gf_dirent_t entries; + gf_dirent_t *orig_entry = NULL; + gf_dirent_t *entry = NULL; + int count = 0; + + INIT_LIST_HEAD(&entries.list); + + if (op_ret < 0) + goto unwind; + + list_for_each_entry(orig_entry, (&orig_entries->list), list) + { + entry = gf_dirent_for_name(orig_entry->d_name); + if (!entry) { + gf_msg(this->name, GF_LOG_ERROR, ENOMEM, DHT_MSG_NO_MEMORY, + "Memory allocation failed "); + goto unwind; + } + + entry->d_off = orig_entry->d_off; + entry->d_ino = orig_entry->d_ino; + entry->d_type = orig_entry->d_type; + entry->d_len = orig_entry->d_len; + + list_add_tail(&entry->list, &entries.list); + count++; + } + op_ret = count; + +unwind: + if (op_ret < 0) + op_ret = 0; + + DHT_STACK_UNWIND(readdir, frame, op_ret, op_errno, &entries, NULL); + + gf_dirent_free(&entries); + + return 0; +} + +int +tier_readdirp_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, + int op_errno, gf_dirent_t *orig_entries, dict_t *xdata) +{ + dht_local_t *local = NULL; + gf_dirent_t entries; + gf_dirent_t *orig_entry = NULL; + gf_dirent_t *entry = NULL; + xlator_t *prev = NULL; + xlator_t *next_subvol = NULL; + off_t next_offset = 0; + int count = 0; + dht_conf_t *conf = NULL; + int ret = 0; + inode_table_t *itable = NULL; + inode_t *inode = NULL; + + INIT_LIST_HEAD(&entries.list); + prev = cookie; + local = frame->local; + itable = local->fd ? local->fd->inode->table : NULL; + + conf = this->private; + GF_VALIDATE_OR_GOTO(this->name, conf, unwind); + + if (op_ret < 0) + goto done; + + list_for_each_entry(orig_entry, (&orig_entries->list), list) + { + next_offset = orig_entry->d_off; + + if (IA_ISINVAL(orig_entry->d_stat.ia_type)) { + /*stat failed somewhere- ignore this entry*/ + continue; + } + + entry = gf_dirent_for_name(orig_entry->d_name); + if (!entry) { + goto unwind; + } + + entry->d_off = orig_entry->d_off; + entry->d_stat = orig_entry->d_stat; + entry->d_ino = orig_entry->d_ino; + entry->d_type = orig_entry->d_type; + entry->d_len = orig_entry->d_len; + + if (orig_entry->dict) + entry->dict = dict_ref(orig_entry->dict); + + if (check_is_linkfile(NULL, (&orig_entry->d_stat), orig_entry->dict, + conf->link_xattr_name)) { + goto entries; + + } else if (IA_ISDIR(entry->d_stat.ia_type)) { + if (orig_entry->inode) { + dht_inode_ctx_time_update(orig_entry->inode, this, + &entry->d_stat, 1); + } + } else { + if (orig_entry->inode) { + ret = dht_layout_preset(this, prev, orig_entry->inode); + if (ret) + gf_msg(this->name, GF_LOG_WARNING, 0, + DHT_MSG_LAYOUT_SET_FAILED, + "failed to link the layout " + "in inode"); + + entry->inode = inode_ref(orig_entry->inode); + } else if (itable) { + /* + * orig_entry->inode might be null if any upper + * layer xlators below client set to null, to + * force a lookup on the inode even if the inode + * is present in the inode table. In that case + * we just update the ctx to make sure we didn't + * missed anything. + */ + inode = inode_find(itable, orig_entry->d_stat.ia_gfid); + if (inode) { + ret = dht_layout_preset(this, TIER_HASHED_SUBVOL, inode); + if (ret) + gf_msg(this->name, GF_LOG_WARNING, 0, + DHT_MSG_LAYOUT_SET_FAILED, + "failed to link the layout" + " in inode"); + inode_unref(inode); + inode = NULL; + } + } + } + + entries: + list_add_tail(&entry->list, &entries.list); + count++; + } + op_ret = count; + +done: + if (count == 0) { + /* non-zero next_offset means that + EOF is not yet hit on the current subvol + */ + if (next_offset != 0) { + next_subvol = prev; + } else { + goto unwind; + } + + STACK_WIND_COOKIE(frame, tier_readdirp_cbk, next_subvol, next_subvol, + next_subvol->fops->readdirp, local->fd, local->size, + next_offset, local->xattr); + return 0; + } + +unwind: + if (op_ret < 0) + op_ret = 0; + + DHT_STACK_UNWIND(readdirp, frame, op_ret, op_errno, &entries, NULL); + + gf_dirent_free(&entries); + + return 0; +} + +int +tier_do_readdir(call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, + off_t yoff, int whichop, dict_t *dict) +{ + dht_local_t *local = NULL; + int op_errno = -1; + xlator_t *hashed_subvol = NULL; + int ret = 0; + dht_conf_t *conf = NULL; + + VALIDATE_OR_GOTO(frame, err); + VALIDATE_OR_GOTO(this, err); + VALIDATE_OR_GOTO(fd, err); + VALIDATE_OR_GOTO(this->private, err); + + conf = this->private; + + local = dht_local_init(frame, NULL, NULL, whichop); + if (!local) { + op_errno = ENOMEM; + goto err; + } + + local->fd = fd_ref(fd); + local->size = size; + local->xattr_req = (dict) ? dict_ref(dict) : NULL; + + hashed_subvol = TIER_HASHED_SUBVOL; + + /* TODO: do proper readdir */ + if (whichop == GF_FOP_READDIRP) { + if (dict) + local->xattr = dict_ref(dict); + else + local->xattr = dict_new(); + + if (local->xattr) { + ret = dict_set_uint32(local->xattr, conf->link_xattr_name, 256); + if (ret) + gf_msg(this->name, GF_LOG_WARNING, 0, DHT_MSG_DICT_SET_FAILED, + "Failed to set dictionary value" + " : key = %s", + conf->link_xattr_name); + } + + STACK_WIND_COOKIE(frame, tier_readdirp_cbk, hashed_subvol, + hashed_subvol, hashed_subvol->fops->readdirp, fd, + size, yoff, local->xattr); + + } else { + STACK_WIND_COOKIE(frame, tier_readdir_cbk, hashed_subvol, hashed_subvol, + hashed_subvol->fops->readdir, fd, size, yoff, + local->xattr); + } + + return 0; + +err: + op_errno = (op_errno == -1) ? errno : op_errno; + DHT_STACK_UNWIND(readdir, frame, -1, op_errno, NULL, NULL); + + return 0; +} + +int +tier_readdir(call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, + off_t yoff, dict_t *xdata) +{ + int op = GF_FOP_READDIR; + dht_conf_t *conf = NULL; + int i = 0; + + conf = this->private; + if (!conf) + goto out; + + for (i = 0; i < conf->subvolume_cnt; i++) { + if (!conf->subvolume_status[i]) { + op = GF_FOP_READDIRP; + break; + } + } + + if (conf->use_readdirp) + op = GF_FOP_READDIRP; + +out: + tier_do_readdir(frame, this, fd, size, yoff, op, 0); + return 0; +} + +int +tier_readdirp(call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, + off_t yoff, dict_t *dict) +{ + tier_do_readdir(frame, this, fd, size, yoff, GF_FOP_READDIRP, dict); + return 0; +} + +int +tier_statfs_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, + int op_errno, struct statvfs *statvfs, dict_t *xdata) +{ + gf_boolean_t event = _gf_false; + qdstatfs_action_t action = qdstatfs_action_OFF; + dht_local_t *local = NULL; + int this_call_cnt = 0; + int bsize = 0; + int frsize = 0; + GF_UNUSED int ret = 0; + unsigned long new_usage = 0; + unsigned long cur_usage = 0; + xlator_t *prev = NULL; + dht_conf_t *conf = NULL; + tier_statvfs_t *tier_stat = NULL; + + prev = cookie; + local = frame->local; + GF_ASSERT(local); + + conf = this->private; + + if (xdata) + ret = dict_get_int8(xdata, "quota-deem-statfs", (int8_t *)&event); + + tier_stat = &local->tier_statvfs; + + LOCK(&frame->lock); + { + if (op_ret == -1) { + local->op_errno = op_errno; + goto unlock; + } + if (!statvfs) { + op_errno = EINVAL; + local->op_ret = -1; + goto unlock; + } + local->op_ret = 0; + + if (local->quota_deem_statfs) { + if (event == _gf_true) { + action = qdstatfs_action_COMPARE; + } else { + action = qdstatfs_action_NEGLECT; + } + } else { + if (event == _gf_true) { + action = qdstatfs_action_REPLACE; + local->quota_deem_statfs = _gf_true; + } + } + + if (local->quota_deem_statfs) { + switch (action) { + case qdstatfs_action_NEGLECT: + goto unlock; + + case qdstatfs_action_REPLACE: + local->statvfs = *statvfs; + goto unlock; + + case qdstatfs_action_COMPARE: + new_usage = statvfs->f_blocks - statvfs->f_bfree; + cur_usage = local->statvfs.f_blocks - + local->statvfs.f_bfree; + + /* Take the max of the usage from subvols */ + if (new_usage >= cur_usage) + local->statvfs = *statvfs; + goto unlock; + + default: + break; + } + } + + if (local->statvfs.f_bsize != 0) { + bsize = max(local->statvfs.f_bsize, statvfs->f_bsize); + frsize = max(local->statvfs.f_frsize, statvfs->f_frsize); + dht_normalize_stats(&local->statvfs, bsize, frsize); + dht_normalize_stats(statvfs, bsize, frsize); + } else { + local->statvfs.f_bsize = statvfs->f_bsize; + local->statvfs.f_frsize = statvfs->f_frsize; + } + + if (prev == TIER_HASHED_SUBVOL) { + local->statvfs.f_blocks = statvfs->f_blocks; + local->statvfs.f_files = statvfs->f_files; + local->statvfs.f_fsid = statvfs->f_fsid; + local->statvfs.f_flag = statvfs->f_flag; + local->statvfs.f_namemax = statvfs->f_namemax; + tier_stat->blocks_used = (statvfs->f_blocks - statvfs->f_bfree); + tier_stat->pblocks_used = (statvfs->f_blocks - statvfs->f_bavail); + tier_stat->files_used = (statvfs->f_files - statvfs->f_ffree); + tier_stat->pfiles_used = (statvfs->f_files - statvfs->f_favail); + tier_stat->hashed_fsid = statvfs->f_fsid; + } else { + tier_stat->unhashed_fsid = statvfs->f_fsid; + tier_stat->unhashed_blocks_used = (statvfs->f_blocks - + statvfs->f_bfree); + tier_stat->unhashed_pblocks_used = (statvfs->f_blocks - + statvfs->f_bavail); + tier_stat->unhashed_files_used = (statvfs->f_files - + statvfs->f_ffree); + tier_stat->unhashed_pfiles_used = (statvfs->f_files - + statvfs->f_favail); + } + } +unlock: + UNLOCK(&frame->lock); + + this_call_cnt = dht_frame_return(frame); + if (is_last_call(this_call_cnt)) { + if (tier_stat->unhashed_fsid != tier_stat->hashed_fsid) { + tier_stat->blocks_used += tier_stat->unhashed_blocks_used; + tier_stat->pblocks_used += tier_stat->unhashed_pblocks_used; + tier_stat->files_used += tier_stat->unhashed_files_used; + tier_stat->pfiles_used += tier_stat->unhashed_pfiles_used; + } + local->statvfs.f_bfree = local->statvfs.f_blocks - + tier_stat->blocks_used; + local->statvfs.f_bavail = local->statvfs.f_blocks - + tier_stat->pblocks_used; + local->statvfs.f_ffree = local->statvfs.f_files - tier_stat->files_used; + local->statvfs.f_favail = local->statvfs.f_files - + tier_stat->pfiles_used; + DHT_STACK_UNWIND(statfs, frame, local->op_ret, local->op_errno, + &local->statvfs, xdata); + } + + return 0; +} + +int +tier_statfs(call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xdata) +{ + dht_local_t *local = NULL; + dht_conf_t *conf = NULL; + int op_errno = -1; + int i = -1; + inode_t *inode = NULL; + inode_table_t *itable = NULL; + uuid_t root_gfid = { + 0, + }; + loc_t newloc = { + 0, + }; + + VALIDATE_OR_GOTO(frame, err); + VALIDATE_OR_GOTO(this, err); + VALIDATE_OR_GOTO(loc, err); + VALIDATE_OR_GOTO(this->private, err); + + conf = this->private; + + local = dht_local_init(frame, NULL, NULL, GF_FOP_STATFS); + if (!local) { + op_errno = ENOMEM; + goto err; + } + + if (loc->inode && !IA_ISDIR(loc->inode->ia_type)) { + itable = loc->inode->table; + if (!itable) { + op_errno = EINVAL; + goto err; + } + + loc = &local->loc2; + root_gfid[15] = 1; + + inode = inode_find(itable, root_gfid); + if (!inode) { + op_errno = EINVAL; + goto err; + } + + dht_build_root_loc(inode, &newloc); + loc = &newloc; + } + + local->call_cnt = conf->subvolume_cnt; + + for (i = 0; i < conf->subvolume_cnt; i++) { + STACK_WIND_COOKIE(frame, tier_statfs_cbk, conf->subvolumes[i], + conf->subvolumes[i], + conf->subvolumes[i]->fops->statfs, loc, xdata); + } + + return 0; + +err: + op_errno = (op_errno == -1) ? errno : op_errno; + DHT_STACK_UNWIND(statfs, frame, -1, op_errno, NULL, NULL); + + return 0; +} diff --git a/xlators/cluster/dht/src/tier-common.h b/xlators/cluster/dht/src/tier-common.h new file mode 100644 index 0000000..b1ebaa8 --- /dev/null +++ b/xlators/cluster/dht/src/tier-common.h @@ -0,0 +1,55 @@ +/* + Copyright (c) 2015 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#ifndef _TIER_COMMON_H_ +#define _TIER_COMMON_H_ +/* Function definitions */ +int +tier_create_unlink_stale_linkto_cbk(call_frame_t *frame, void *cookie, + xlator_t *this, int op_ret, int op_errno, + struct iatt *preparent, + struct iatt *postparent, dict_t *xdata); + +int +tier_create_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, + int op_errno, fd_t *fd, inode_t *inode, struct iatt *stbuf, + struct iatt *preparent, struct iatt *postparent, dict_t *xdata); + +int +tier_create_linkfile_create_cbk(call_frame_t *frame, void *cookie, + xlator_t *this, int32_t op_ret, + int32_t op_errno, inode_t *inode, + struct iatt *stbuf, struct iatt *preparent, + struct iatt *postparent, dict_t *xdata); + +int +tier_create(call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags, + mode_t mode, mode_t umask, fd_t *fd, dict_t *params); + +int32_t +tier_unlink(call_frame_t *frame, xlator_t *this, loc_t *loc, int xflag, + dict_t *xdata); + +int32_t +tier_readdirp(call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, + off_t off, dict_t *dict); + +int +tier_readdir(call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, + off_t yoff, dict_t *xdata); + +int +tier_link(call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc, + dict_t *xdata); + +int +tier_statfs(call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xdata); + +#endif diff --git a/xlators/cluster/dht/src/tier.c b/xlators/cluster/dht/src/tier.c new file mode 100644 index 0000000..94b4c63 --- /dev/null +++ b/xlators/cluster/dht/src/tier.c @@ -0,0 +1,3105 @@ +/* + Copyright (c) 2015 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#include + +#include "dht-common.h" +#include "tier.h" +#include "tier-common.h" +#include +#include +#include "tier-ctr-interface.h" + +/*Hard coded DB info*/ +static gfdb_db_type_t dht_tier_db_type = GFDB_SQLITE3; +/*Hard coded DB info*/ + +/*Mutex for updating the data movement stats*/ +static pthread_mutex_t dm_stat_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* Stores the path location of promotion query files */ +static char *promotion_qfile; +/* Stores the path location of demotion query files */ +static char *demotion_qfile; + +static void *libhandle; +static gfdb_methods_t gfdb_methods; + +#define DB_QUERY_RECORD_SIZE 4096 + +/* + * Closes all the fds and frees the qfile_array + * */ +static void +qfile_array_free(tier_qfile_array_t *qfile_array) +{ + ssize_t i = 0; + + if (qfile_array) { + if (qfile_array->fd_array) { + for (i = 0; i < qfile_array->array_size; i++) { + if (qfile_array->fd_array[i] != -1) { + sys_close(qfile_array->fd_array[i]); + } + } + } + GF_FREE(qfile_array->fd_array); + } + GF_FREE(qfile_array); +} + +/* Create a new query file list with given size */ +static tier_qfile_array_t * +qfile_array_new(ssize_t array_size) +{ + int ret = -1; + tier_qfile_array_t *qfile_array = NULL; + ssize_t i = 0; + + GF_VALIDATE_OR_GOTO("tier", (array_size > 0), out); + + qfile_array = GF_CALLOC(1, sizeof(tier_qfile_array_t), + gf_tier_mt_qfile_array_t); + if (!qfile_array) { + gf_msg("tier", GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Failed to allocate memory for tier_qfile_array_t"); + goto out; + } + + qfile_array->fd_array = GF_MALLOC(array_size * sizeof(int), + gf_dht_mt_int32_t); + if (!qfile_array->fd_array) { + gf_msg("tier", GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Failed to allocate memory for " + "tier_qfile_array_t->fd_array"); + goto out; + } + + /* Init all the fds to -1 */ + for (i = 0; i < array_size; i++) { + qfile_array->fd_array[i] = -1; + } + + qfile_array->array_size = array_size; + qfile_array->next_index = 0; + + /* Set exhausted count to list size as the list is empty */ + qfile_array->exhausted_count = qfile_array->array_size; + + ret = 0; +out: + if (ret) { + qfile_array_free(qfile_array); + qfile_array = NULL; + } + return qfile_array; +} + +/* Checks if the query file list is empty or totally exhausted. */ +static gf_boolean_t +is_qfile_array_empty(tier_qfile_array_t *qfile_array) +{ + return (qfile_array->exhausted_count == qfile_array->array_size) + ? _gf_true + : _gf_false; +} + +/* Shifts the next_fd pointer to the next available fd in the list */ +static void +shift_next_index(tier_qfile_array_t *qfile_array) +{ + int qfile_fd = 0; + int spin_count = 0; + + if (is_qfile_array_empty(qfile_array)) { + return; + } + + do { + /* change next_index in a rotional manner */ + (qfile_array->next_index == (qfile_array->array_size - 1)) + ? qfile_array->next_index = 0 + : qfile_array->next_index++; + + qfile_fd = (qfile_array->fd_array[qfile_array->next_index]); + + spin_count++; + + } while ((qfile_fd == -1) && (spin_count < qfile_array->array_size)); +} + +/* + * This is a non-thread safe function to read query records + * from a list of query files in a Round-Robin manner. + * As in when the query files get exhuasted they are closed. + * Returns: + * 0 if all the query records in all the query files of the list are + * exhausted. + * > 0 if a query record is successfully read. Indicates the size of the query + * record read. + * < 0 if there was failure + * */ +static int +read_query_record_list(tier_qfile_array_t *qfile_array, + gfdb_query_record_t **query_record) +{ + int ret = -1; + int qfile_fd = 0; + + GF_VALIDATE_OR_GOTO("tier", qfile_array, out); + GF_VALIDATE_OR_GOTO("tier", qfile_array->fd_array, out); + + do { + if (is_qfile_array_empty(qfile_array)) { + ret = 0; + break; + } + + qfile_fd = qfile_array->fd_array[qfile_array->next_index]; + ret = gfdb_methods.gfdb_read_query_record(qfile_fd, query_record); + if (ret <= 0) { + /*The qfile_fd has reached EOF or + * there was an error. + * 1. Close the exhausted fd + * 2. increment the exhausted count + * 3. shift next_qfile to next qfile + **/ + sys_close(qfile_fd); + qfile_array->fd_array[qfile_array->next_index] = -1; + qfile_array->exhausted_count++; + /* shift next_qfile to next qfile */ + shift_next_index(qfile_array); + continue; + } else { + /* shift next_qfile to next qfile */ + shift_next_index(qfile_array); + break; + } + } while (1); +out: + return ret; +} + +/* Check and update the watermark every WM_INTERVAL seconds */ +#define WM_INTERVAL 5 +#define WM_INTERVAL_EMERG 1 + +static int +tier_check_same_node(xlator_t *this, loc_t *loc, gf_defrag_info_t *defrag) +{ + int ret = -1; + dict_t *dict = NULL; + char *uuid_str = NULL; + uuid_t node_uuid = { + 0, + }; + + GF_VALIDATE_OR_GOTO("tier", this, out); + GF_VALIDATE_OR_GOTO(this->name, loc, out); + GF_VALIDATE_OR_GOTO(this->name, defrag, out); + + if (syncop_getxattr(this, loc, &dict, GF_XATTR_NODE_UUID_KEY, NULL, NULL)) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Unable to get NODE_UUID_KEY %s %s\n", loc->name, loc->path); + goto out; + } + + if (dict_get_str(dict, GF_XATTR_NODE_UUID_KEY, &uuid_str) < 0) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Failed to get node-uuids for %s", loc->path); + goto out; + } + + if (gf_uuid_parse(uuid_str, node_uuid)) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "uuid_parse failed for %s", loc->path); + goto out; + } + + if (gf_uuid_compare(node_uuid, defrag->node_uuid)) { + gf_msg_debug(this->name, 0, "%s does not belong to this node", + loc->path); + ret = 1; + goto out; + } + + ret = 0; +out: + if (dict) + dict_unref(dict); + + return ret; +} + +int +tier_get_fs_stat(xlator_t *this, loc_t *root_loc) +{ + int ret = 0; + gf_defrag_info_t *defrag = NULL; + dht_conf_t *conf = NULL; + dict_t *xdata = NULL; + struct statvfs statfs = { + 0, + }; + gf_tier_conf_t *tier_conf = NULL; + + conf = this->private; + if (!conf) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_STATUS, + "conf is NULL"); + ret = -1; + goto exit; + } + + defrag = conf->defrag; + if (!defrag) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_STATUS, + "defrag is NULL"); + ret = -1; + goto exit; + } + + tier_conf = &defrag->tier_conf; + + xdata = dict_new(); + if (!xdata) { + gf_msg(this->name, GF_LOG_ERROR, ENOMEM, DHT_MSG_NO_MEMORY, + "failed to allocate dictionary"); + ret = -1; + goto exit; + } + + ret = dict_set_int8(xdata, GF_INTERNAL_IGNORE_DEEM_STATFS, 1); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_DICT_SET_FAILED, + "Failed to set " GF_INTERNAL_IGNORE_DEEM_STATFS " in dict"); + ret = -1; + goto exit; + } + + /* Find how much free space is on the hot subvolume. + * Then see if that value */ + /* is less than or greater than user defined watermarks. + * Stash results in */ + /* the tier_conf data structure. */ + + ret = syncop_statfs(conf->subvolumes[1], root_loc, &statfs, xdata, NULL); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, -ret, DHT_MSG_LOG_TIER_STATUS, + "Unable to obtain statfs."); + goto exit; + } + + pthread_mutex_lock(&dm_stat_mutex); + + tier_conf->block_size = statfs.f_bsize; + tier_conf->blocks_total = statfs.f_blocks; + tier_conf->blocks_used = statfs.f_blocks - statfs.f_bfree; + + tier_conf->percent_full = GF_PERCENTAGE(tier_conf->blocks_used, + statfs.f_blocks); + pthread_mutex_unlock(&dm_stat_mutex); + +exit: + if (xdata) + dict_unref(xdata); + return ret; +} + +static void +tier_send_watermark_event(const char *volname, tier_watermark_op_t old_wm, + tier_watermark_op_t new_wm) +{ + if (old_wm == TIER_WM_LOW || old_wm == TIER_WM_NONE) { + if (new_wm == TIER_WM_MID) { + gf_event(EVENT_TIER_WATERMARK_RAISED_TO_MID, "vol=%s", volname); + } else if (new_wm == TIER_WM_HI) { + gf_event(EVENT_TIER_WATERMARK_HI, "vol=%s", volname); + } + } else if (old_wm == TIER_WM_MID) { + if (new_wm == TIER_WM_LOW) { + gf_event(EVENT_TIER_WATERMARK_DROPPED_TO_LOW, "vol=%s", volname); + } else if (new_wm == TIER_WM_HI) { + gf_event(EVENT_TIER_WATERMARK_HI, "vol=%s", volname); + } + } else if (old_wm == TIER_WM_HI) { + if (new_wm == TIER_WM_MID) { + gf_event(EVENT_TIER_WATERMARK_DROPPED_TO_MID, "vol=%s", volname); + } else if (new_wm == TIER_WM_LOW) { + gf_event(EVENT_TIER_WATERMARK_DROPPED_TO_LOW, "vol=%s", volname); + } + } +} + +int +tier_check_watermark(xlator_t *this) +{ + int ret = -1; + gf_defrag_info_t *defrag = NULL; + dht_conf_t *conf = NULL; + gf_tier_conf_t *tier_conf = NULL; + tier_watermark_op_t wm = TIER_WM_NONE; + + conf = this->private; + if (!conf) + goto exit; + + defrag = conf->defrag; + if (!defrag) + goto exit; + + tier_conf = &defrag->tier_conf; + + if (tier_conf->percent_full < tier_conf->watermark_low) { + wm = TIER_WM_LOW; + + } else if (tier_conf->percent_full < tier_conf->watermark_hi) { + wm = TIER_WM_MID; + + } else { + wm = TIER_WM_HI; + } + + if (wm != tier_conf->watermark_last) { + tier_send_watermark_event(tier_conf->volname, tier_conf->watermark_last, + wm); + + tier_conf->watermark_last = wm; + gf_msg(this->name, GF_LOG_INFO, 0, DHT_MSG_LOG_TIER_STATUS, + "Tier watermark now %d", wm); + } + + ret = 0; + +exit: + return ret; +} + +static gf_boolean_t +is_hot_tier_full(gf_tier_conf_t *tier_conf) +{ + if (tier_conf && (tier_conf->mode == TIER_MODE_WM) && + (tier_conf->watermark_last == TIER_WM_HI)) + return _gf_true; + + return _gf_false; +} + +int +tier_do_migration(xlator_t *this, int promote) +{ + gf_defrag_info_t *defrag = NULL; + dht_conf_t *conf = NULL; + long rand = 0; + int migrate = 0; + gf_tier_conf_t *tier_conf = NULL; + + conf = this->private; + if (!conf) + goto exit; + + defrag = conf->defrag; + if (!defrag) + goto exit; + + if (tier_check_watermark(this) != 0) { + gf_msg(this->name, GF_LOG_CRITICAL, errno, DHT_MSG_LOG_TIER_ERROR, + "Failed to get watermark"); + goto exit; + } + + tier_conf = &defrag->tier_conf; + + switch (tier_conf->watermark_last) { + case TIER_WM_LOW: + migrate = promote ? 1 : 0; + break; + case TIER_WM_HI: + migrate = promote ? 0 : 1; + break; + case TIER_WM_MID: + /* coverity[DC.WEAK_CRYPTO] */ + rand = random() % 100; + if (promote) { + migrate = (rand > tier_conf->percent_full); + } else { + migrate = (rand <= tier_conf->percent_full); + } + break; + } + +exit: + return migrate; +} + +int +tier_migrate(xlator_t *this, int is_promotion, dict_t *migrate_data, loc_t *loc, + gf_tier_conf_t *tier_conf) +{ + int ret = -1; + + pthread_mutex_lock(&tier_conf->pause_mutex); + if (is_promotion) + tier_conf->promote_in_progress = 1; + else + tier_conf->demote_in_progress = 1; + pthread_mutex_unlock(&tier_conf->pause_mutex); + + /* Data migration */ + ret = syncop_setxattr(this, loc, migrate_data, 0, NULL, NULL); + + pthread_mutex_lock(&tier_conf->pause_mutex); + if (is_promotion) + tier_conf->promote_in_progress = 0; + else + tier_conf->demote_in_progress = 0; + pthread_mutex_unlock(&tier_conf->pause_mutex); + + return ret; +} + +/* returns _gf_true: if file can be promoted + * returns _gf_false: if file cannot be promoted + */ +static gf_boolean_t +tier_can_promote_file(xlator_t *this, char const *file_name, + struct iatt *current, gf_defrag_info_t *defrag) +{ + gf_boolean_t ret = _gf_false; + fsblkcnt_t estimated_usage = 0; + + if (defrag->tier_conf.tier_max_promote_size && + (current->ia_size > defrag->tier_conf.tier_max_promote_size)) { + gf_msg(this->name, GF_LOG_INFO, 0, DHT_MSG_LOG_TIER_STATUS, + "File %s (gfid:%s) with size (%" PRIu64 + ") exceeds maxsize " + "(%d) for promotion. File will not be promoted.", + file_name, uuid_utoa(current->ia_gfid), current->ia_size, + defrag->tier_conf.tier_max_promote_size); + goto err; + } + + /* bypass further validations for TEST mode */ + if (defrag->tier_conf.mode != TIER_MODE_WM) { + ret = _gf_true; + goto err; + } + + /* convert the file size to blocks as per the block size of the + * destination tier + * NOTE: add (block_size - 1) to get the correct block size when + * there is a remainder after a modulo + */ + estimated_usage = ((current->ia_size + defrag->tier_conf.block_size - 1) / + defrag->tier_conf.block_size) + + defrag->tier_conf.blocks_used; + + /* test if the estimated block usage goes above HI watermark */ + if (GF_PERCENTAGE(estimated_usage, defrag->tier_conf.blocks_total) >= + defrag->tier_conf.watermark_hi) { + gf_msg(this->name, GF_LOG_INFO, 0, DHT_MSG_LOG_TIER_STATUS, + "Estimated block count consumption on " + "hot tier (%" PRIu64 + ") exceeds hi watermark (%d%%). " + "File will not be promoted.", + estimated_usage, defrag->tier_conf.watermark_hi); + goto err; + } + ret = _gf_true; +err: + return ret; +} + +static int +tier_set_migrate_data(dict_t *migrate_data) +{ + int failed = 1; + + failed = dict_set_str(migrate_data, GF_XATTR_FILE_MIGRATE_KEY, "force"); + if (failed) { + goto bail_out; + } + + /* Flag to suggest the xattr call is from migrator */ + failed = dict_set_str(migrate_data, "from.migrator", "yes"); + if (failed) { + goto bail_out; + } + + /* Flag to suggest its a tiering migration + * The reason for this dic key-value is that + * promotions and demotions are multithreaded + * so the original frame from gf_defrag_start() + * is not carried. A new frame will be created when + * we do syncop_setxattr(). This does not have the + * frame->root->pid of the original frame. So we pass + * this dic key-value when we do syncop_setxattr() to do + * data migration and set the frame->root->pid to + * GF_CLIENT_PID_TIER_DEFRAG in dht_setxattr() just before + * calling dht_start_rebalance_task() */ + failed = dict_set_str(migrate_data, TIERING_MIGRATION_KEY, "yes"); + if (failed) { + goto bail_out; + } + + failed = 0; + +bail_out: + return failed; +} + +static char * +tier_get_parent_path(xlator_t *this, loc_t *p_loc, struct iatt *par_stbuf, + int *per_link_status) +{ + int ret = -1; + char *parent_path = NULL; + dict_t *xdata_request = NULL; + dict_t *xdata_response = NULL; + + xdata_request = dict_new(); + if (!xdata_request) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Failed to create xdata_request dict"); + goto err; + } + ret = dict_set_int32(xdata_request, GET_ANCESTRY_PATH_KEY, 42); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Failed to set value to dict : key %s \n", + GET_ANCESTRY_PATH_KEY); + goto err; + } + + ret = syncop_lookup(this, p_loc, par_stbuf, NULL, xdata_request, + &xdata_response); + /* When the parent gfid is a stale entry, the lookup + * will fail and stop the demotion process. + * The parent gfid can be stale when a huge folder is + * deleted while the files within it are being migrated + */ + if (ret == -ESTALE) { + gf_msg(this->name, GF_LOG_WARNING, -ret, DHT_MSG_STALE_LOOKUP, + "Stale entry in parent lookup for %s", uuid_utoa(p_loc->gfid)); + *per_link_status = 1; + goto err; + } else if (ret) { + gf_msg(this->name, GF_LOG_ERROR, -ret, DHT_MSG_LOG_TIER_ERROR, + "Error in parent lookup for %s", uuid_utoa(p_loc->gfid)); + *per_link_status = -1; + goto err; + } + ret = dict_get_str(xdata_response, GET_ANCESTRY_PATH_KEY, &parent_path); + if (ret || !parent_path) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Failed to get parent path for %s", uuid_utoa(p_loc->gfid)); + *per_link_status = -1; + goto err; + } + +err: + if (xdata_request) { + dict_unref(xdata_request); + } + + if (xdata_response) { + dict_unref(xdata_response); + xdata_response = NULL; + } + + return parent_path; +} + +static int +tier_get_file_name_and_path(xlator_t *this, uuid_t gfid, + gfdb_link_info_t *link_info, + char const *parent_path, loc_t *loc, + int *per_link_status) +{ + int ret = -1; + + loc->name = gf_strdup(link_info->file_name); + if (!loc->name) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Memory " + "allocation failed for %s", + uuid_utoa(gfid)); + *per_link_status = -1; + goto err; + } + ret = gf_asprintf((char **)&(loc->path), "%s/%s", parent_path, loc->name); + if (ret < 0) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Failed to " + "construct file path for %s %s\n", + parent_path, loc->name); + *per_link_status = -1; + goto err; + } + + ret = 0; + +err: + return ret; +} + +static int +tier_lookup_file(xlator_t *this, loc_t *p_loc, loc_t *loc, struct iatt *current, + int *per_link_status) +{ + int ret = -1; + + ret = syncop_lookup(this, loc, current, NULL, NULL, NULL); + + /* The file may be deleted even when the parent + * is available and the lookup will + * return a stale entry which would stop the + * migration. so if its a stale entry, then skip + * the file and keep migrating. + */ + if (ret == -ESTALE) { + gf_msg(this->name, GF_LOG_WARNING, -ret, DHT_MSG_STALE_LOOKUP, + "Stale lookup for %s", uuid_utoa(p_loc->gfid)); + *per_link_status = 1; + goto err; + } else if (ret) { + gf_msg(this->name, GF_LOG_ERROR, -ret, DHT_MSG_LOG_TIER_ERROR, + "Failed to " + "lookup file %s\n", + loc->name); + *per_link_status = -1; + goto err; + } + ret = 0; + +err: + return ret; +} + +static gf_boolean_t +tier_is_file_already_at_destination(xlator_t *src_subvol, + query_cbk_args_t *query_cbk_args, + dht_conf_t *conf, int *per_link_status) +{ + gf_boolean_t at_destination = _gf_true; + + if (src_subvol == NULL) { + *per_link_status = 1; + goto err; + } + if (query_cbk_args->is_promotion && src_subvol == conf->subvolumes[1]) { + *per_link_status = 1; + goto err; + } + + if (!query_cbk_args->is_promotion && src_subvol == conf->subvolumes[0]) { + *per_link_status = 1; + goto err; + } + at_destination = _gf_false; + +err: + return at_destination; +} + +static void +tier_update_migration_counters(query_cbk_args_t *query_cbk_args, + gf_defrag_info_t *defrag, + uint64_t *total_migrated_bytes, int *total_files) +{ + if (query_cbk_args->is_promotion) { + defrag->total_files_promoted++; + *total_migrated_bytes += defrag->tier_conf.st_last_promoted_size; + pthread_mutex_lock(&dm_stat_mutex); + defrag->tier_conf.blocks_used += defrag->tier_conf + .st_last_promoted_size; + pthread_mutex_unlock(&dm_stat_mutex); + } else { + defrag->total_files_demoted++; + *total_migrated_bytes += defrag->tier_conf.st_last_demoted_size; + pthread_mutex_lock(&dm_stat_mutex); + defrag->tier_conf.blocks_used -= defrag->tier_conf.st_last_demoted_size; + pthread_mutex_unlock(&dm_stat_mutex); + } + if (defrag->tier_conf.blocks_total) { + pthread_mutex_lock(&dm_stat_mutex); + defrag->tier_conf.percent_full = GF_PERCENTAGE( + defrag->tier_conf.blocks_used, defrag->tier_conf.blocks_total); + pthread_mutex_unlock(&dm_stat_mutex); + } + + (*total_files)++; +} + +static int +tier_migrate_link(xlator_t *this, dht_conf_t *conf, uuid_t gfid, + gfdb_link_info_t *link_info, gf_defrag_info_t *defrag, + query_cbk_args_t *query_cbk_args, dict_t *migrate_data, + int *per_link_status, int *total_files, + uint64_t *total_migrated_bytes) +{ + int ret = -1; + struct iatt current = { + 0, + }; + struct iatt par_stbuf = { + 0, + }; + loc_t p_loc = { + 0, + }; + loc_t loc = { + 0, + }; + xlator_t *src_subvol = NULL; + inode_t *linked_inode = NULL; + char *parent_path = NULL; + + /* Lookup for parent and get the path of parent */ + gf_uuid_copy(p_loc.gfid, link_info->pargfid); + p_loc.inode = inode_new(defrag->root_inode->table); + if (!p_loc.inode) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Failed to create reference to inode" + " for %s", + uuid_utoa(p_loc.gfid)); + + *per_link_status = -1; + goto err; + } + + parent_path = tier_get_parent_path(this, &p_loc, &par_stbuf, + per_link_status); + if (!parent_path) { + goto err; + } + + linked_inode = inode_link(p_loc.inode, NULL, NULL, &par_stbuf); + inode_unref(p_loc.inode); + p_loc.inode = linked_inode; + + /* Preparing File Inode */ + gf_uuid_copy(loc.gfid, gfid); + loc.inode = inode_new(defrag->root_inode->table); + gf_uuid_copy(loc.pargfid, link_info->pargfid); + loc.parent = inode_ref(p_loc.inode); + + /* Get filename and Construct file path */ + if (tier_get_file_name_and_path(this, gfid, link_info, parent_path, &loc, + per_link_status) != 0) { + goto err; + } + gf_uuid_copy(loc.parent->gfid, link_info->pargfid); + + /* lookup file inode */ + if (tier_lookup_file(this, &p_loc, &loc, ¤t, per_link_status) != 0) { + goto err; + } + + if (query_cbk_args->is_promotion) { + if (!tier_can_promote_file(this, link_info->file_name, ¤t, + defrag)) { + *per_link_status = 1; + goto err; + } + } + + linked_inode = inode_link(loc.inode, NULL, NULL, ¤t); + inode_unref(loc.inode); + loc.inode = linked_inode; + + /* + * Do not promote/demote if file already is where it + * should be. It means another brick moved the file + * so is not an error. So we set per_link_status = 1 + * so that we ignore counting this. + */ + src_subvol = dht_subvol_get_cached(this, loc.inode); + + if (tier_is_file_already_at_destination(src_subvol, query_cbk_args, conf, + per_link_status)) { + goto err; + } + + gf_msg_debug(this->name, 0, "Tier %s: src_subvol %s file %s", + (query_cbk_args->is_promotion ? "promote" : "demote"), + src_subvol->name, loc.path); + + ret = tier_check_same_node(this, &loc, defrag); + if (ret != 0) { + if (ret < 0) { + *per_link_status = -1; + goto err; + } + ret = 0; + /* By setting per_link_status to 1 we are + * ignoring this status and will not be counting + * this file for migration */ + *per_link_status = 1; + goto err; + } + + gf_uuid_copy(loc.gfid, loc.inode->gfid); + + if (gf_defrag_get_pause_state(&defrag->tier_conf) != TIER_RUNNING) { + gf_msg(this->name, GF_LOG_INFO, 0, DHT_MSG_LOG_TIER_STATUS, + "Tiering paused. " + "Exiting tier_migrate_link"); + goto err; + } + + ret = tier_migrate(this, query_cbk_args->is_promotion, migrate_data, &loc, + &defrag->tier_conf); + + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, -ret, DHT_MSG_LOG_TIER_ERROR, + "Failed to " + "migrate %s ", + loc.path); + *per_link_status = -1; + goto err; + } + + tier_update_migration_counters(query_cbk_args, defrag, total_migrated_bytes, + total_files); + + ret = 0; + +err: + GF_FREE((char *)loc.name); + loc.name = NULL; + loc_wipe(&loc); + loc_wipe(&p_loc); + + if ((*total_files >= defrag->tier_conf.max_migrate_files) || + (*total_migrated_bytes > defrag->tier_conf.max_migrate_bytes)) { + gf_msg(this->name, GF_LOG_INFO, 0, DHT_MSG_LOG_TIER_STATUS, + "Reached cycle migration limit." + "migrated bytes %" PRId64 " files %d", + *total_migrated_bytes, *total_files); + ret = -1; + } + + return ret; +} + +static int +tier_migrate_using_query_file(void *_args) +{ + int ret = -1; + query_cbk_args_t *query_cbk_args = (query_cbk_args_t *)_args; + xlator_t *this = NULL; + gf_defrag_info_t *defrag = NULL; + gfdb_query_record_t *query_record = NULL; + gfdb_link_info_t *link_info = NULL; + dict_t *migrate_data = NULL; + /* + * per_file_status and per_link_status + * 0 : success + * -1 : failure + * 1 : ignore the status and don't count for migration + * */ + int per_file_status = 0; + int per_link_status = 0; + int total_status = 0; + dht_conf_t *conf = NULL; + uint64_t total_migrated_bytes = 0; + int total_files = 0; + loc_t root_loc = {0}; + gfdb_time_t start_time = {0}; + gfdb_time_t current_time = {0}; + int total_time = 0; + int max_time = 0; + gf_boolean_t emergency_demote_mode = _gf_false; + + GF_VALIDATE_OR_GOTO("tier", query_cbk_args, out); + GF_VALIDATE_OR_GOTO("tier", query_cbk_args->this, out); + this = query_cbk_args->this; + GF_VALIDATE_OR_GOTO(this->name, query_cbk_args->defrag, out); + GF_VALIDATE_OR_GOTO(this->name, query_cbk_args->qfile_array, out); + GF_VALIDATE_OR_GOTO(this->name, this->private, out); + + conf = this->private; + + defrag = query_cbk_args->defrag; + migrate_data = dict_new(); + if (!migrate_data) + goto out; + + emergency_demote_mode = (!query_cbk_args->is_promotion && + is_hot_tier_full(&defrag->tier_conf)); + + if (tier_set_migrate_data(migrate_data) != 0) { + goto out; + } + + dht_build_root_loc(defrag->root_inode, &root_loc); + + ret = gettimeofday(&start_time, NULL); + if (query_cbk_args->is_promotion) { + max_time = defrag->tier_conf.tier_promote_frequency; + } else { + max_time = defrag->tier_conf.tier_demote_frequency; + } + + /* Per file */ + while ((ret = read_query_record_list(query_cbk_args->qfile_array, + &query_record)) != 0) { + if (ret < 0) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Failed to fetch query record " + "from query file"); + goto out; + } + + if (defrag->defrag_status != GF_DEFRAG_STATUS_STARTED) { + ret = -1; + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Exiting tier migration as" + "defrag status is not started"); + goto out; + } + + ret = gettimeofday(¤t_time, NULL); + if (ret < 0) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Could not get current time."); + goto out; + } + + total_time = current_time.tv_sec - start_time.tv_sec; + if (total_time > max_time) { + gf_msg(this->name, GF_LOG_INFO, 0, DHT_MSG_LOG_TIER_STATUS, + "Max cycle time reached. Exiting migration."); + goto out; + } + + per_file_status = 0; + per_link_status = 0; + + if (gf_defrag_get_pause_state(&defrag->tier_conf) != TIER_RUNNING) { + gf_msg(this->name, GF_LOG_INFO, 0, DHT_MSG_LOG_TIER_STATUS, + "Tiering paused. " + "Exiting tier_migrate_using_query_file"); + break; + } + + if (defrag->tier_conf.mode == TIER_MODE_WM) { + ret = tier_get_fs_stat(this, &root_loc); + if (ret != 0) { + gfdb_methods.gfdb_query_record_free(query_record); + query_record = NULL; + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_STATUS, + "tier_get_fs_stat() FAILED ... " + "skipping file migrations until next cycle"); + break; + } + + if (!tier_do_migration(this, query_cbk_args->is_promotion)) { + gfdb_methods.gfdb_query_record_free(query_record); + query_record = NULL; + + /* We have crossed the high watermark. Stop processing + * files if this is a promotion cycle so demotion gets + * a chance to start if not already running*/ + + if (query_cbk_args->is_promotion && + is_hot_tier_full(&defrag->tier_conf)) { + gf_msg(this->name, GF_LOG_INFO, 0, DHT_MSG_LOG_TIER_STATUS, + "High watermark crossed during " + "promotion. Exiting " + "tier_migrate_using_query_file"); + break; + } + continue; + } + } + + per_link_status = 0; + + /* For now we only support single link migration. And we will + * ignore other hard links in the link info list of query record + * TODO: Multiple hard links migration */ + if (!list_empty(&query_record->link_list)) { + link_info = list_first_entry(&query_record->link_list, + gfdb_link_info_t, list); + } + if (link_info != NULL) { + if (tier_migrate_link(this, conf, query_record->gfid, link_info, + defrag, query_cbk_args, migrate_data, + &per_link_status, &total_files, + &total_migrated_bytes) != 0) { + gf_msg( + this->name, GF_LOG_INFO, 0, DHT_MSG_LOG_TIER_STATUS, + "%s failed for %s(gfid:%s)", + (query_cbk_args->is_promotion ? "Promotion" : "Demotion"), + link_info->file_name, uuid_utoa(query_record->gfid)); + } + } + per_file_status = per_link_status; + + if (per_file_status < 0) { /* Failure */ + pthread_mutex_lock(&dm_stat_mutex); + defrag->total_failures++; + pthread_mutex_unlock(&dm_stat_mutex); + } else if (per_file_status == 0) { /* Success */ + pthread_mutex_lock(&dm_stat_mutex); + defrag->total_files++; + pthread_mutex_unlock(&dm_stat_mutex); + } else if (per_file_status == 1) { /* Ignore */ + per_file_status = 0; + /* Since this attempt was ignored we + * decrement the lookup count*/ + pthread_mutex_lock(&dm_stat_mutex); + defrag->num_files_lookedup--; + pthread_mutex_unlock(&dm_stat_mutex); + } + total_status = total_status + per_file_status; + per_link_status = 0; + per_file_status = 0; + + gfdb_methods.gfdb_query_record_free(query_record); + query_record = NULL; + + /* If we are demoting and the entry watermark was HI, then + * we are done with emergency demotions if the current + * watermark has fallen below hi-watermark level + */ + if (emergency_demote_mode) { + if (tier_check_watermark(this) == 0) { + if (!is_hot_tier_full(&defrag->tier_conf)) { + break; + } + } + } + } + +out: + if (migrate_data) + dict_unref(migrate_data); + + gfdb_methods.gfdb_query_record_free(query_record); + query_record = NULL; + + return total_status; +} + +/* This is the call back function per record/file from data base */ +static int +tier_gf_query_callback(gfdb_query_record_t *gfdb_query_record, void *_args) +{ + int ret = -1; + query_cbk_args_t *query_cbk_args = _args; + + GF_VALIDATE_OR_GOTO("tier", query_cbk_args, out); + GF_VALIDATE_OR_GOTO("tier", query_cbk_args->defrag, out); + GF_VALIDATE_OR_GOTO("tier", (query_cbk_args->query_fd > 0), out); + + ret = gfdb_methods.gfdb_write_query_record(query_cbk_args->query_fd, + gfdb_query_record); + if (ret) { + gf_msg("tier", GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Failed writing query record to query file"); + goto out; + } + + pthread_mutex_lock(&dm_stat_mutex); + query_cbk_args->defrag->num_files_lookedup++; + pthread_mutex_unlock(&dm_stat_mutex); + + ret = 0; +out: + return ret; +} + +/* Create query file in tier process */ +static int +tier_process_self_query(tier_brick_list_t *local_brick, void *args) +{ + int ret = -1; + char *db_path = NULL; + query_cbk_args_t *query_cbk_args = NULL; + xlator_t *this = NULL; + gfdb_conn_node_t *conn_node = NULL; + dict_t *params_dict = NULL; + dict_t *ctr_ipc_dict = NULL; + gfdb_brick_info_t *gfdb_brick_info = args; + + /*Init of all the essentials*/ + GF_VALIDATE_OR_GOTO("tier", gfdb_brick_info, out); + query_cbk_args = gfdb_brick_info->_query_cbk_args; + + GF_VALIDATE_OR_GOTO("tier", query_cbk_args->this, out); + this = query_cbk_args->this; + + GF_VALIDATE_OR_GOTO(this->name, gfdb_brick_info->_query_cbk_args, out); + + GF_VALIDATE_OR_GOTO(this->name, local_brick, out); + + GF_VALIDATE_OR_GOTO(this->name, local_brick->xlator, out); + + GF_VALIDATE_OR_GOTO(this->name, local_brick->brick_db_path, out); + + db_path = local_brick->brick_db_path; + + /*Preparing DB parameters before init_db i.e getting db connection*/ + params_dict = dict_new(); + if (!params_dict) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "DB Params cannot initialized"); + goto out; + } + SET_DB_PARAM_TO_DICT(this->name, params_dict, + (char *)gfdb_methods.get_db_path_key(), db_path, ret, + out); + + /*Get the db connection*/ + conn_node = gfdb_methods.init_db((void *)params_dict, dht_tier_db_type); + if (!conn_node) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "FATAL: Failed initializing db operations"); + goto out; + } + + /* Query for eligible files from db */ + query_cbk_args->query_fd = open(local_brick->qfile_path, + O_WRONLY | O_CREAT | O_APPEND, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (query_cbk_args->query_fd < 0) { + gf_msg(this->name, GF_LOG_ERROR, errno, DHT_MSG_LOG_TIER_ERROR, + "Failed to open query file %s", local_brick->qfile_path); + goto out; + } + if (!gfdb_brick_info->_gfdb_promote) { + if (query_cbk_args->defrag->tier_conf.watermark_last == TIER_WM_HI) { + /* emergency demotion mode */ + ret = gfdb_methods.find_all( + conn_node, tier_gf_query_callback, (void *)query_cbk_args, + query_cbk_args->defrag->tier_conf.query_limit); + } else { + if (query_cbk_args->defrag->write_freq_threshold == 0 && + query_cbk_args->defrag->read_freq_threshold == 0) { + ret = gfdb_methods.find_unchanged_for_time( + conn_node, tier_gf_query_callback, (void *)query_cbk_args, + gfdb_brick_info->time_stamp); + } else { + ret = gfdb_methods.find_unchanged_for_time_freq( + conn_node, tier_gf_query_callback, (void *)query_cbk_args, + gfdb_brick_info->time_stamp, + query_cbk_args->defrag->write_freq_threshold, + query_cbk_args->defrag->read_freq_threshold, _gf_false); + } + } + } else { + if (query_cbk_args->defrag->write_freq_threshold == 0 && + query_cbk_args->defrag->read_freq_threshold == 0) { + ret = gfdb_methods.find_recently_changed_files( + conn_node, tier_gf_query_callback, (void *)query_cbk_args, + gfdb_brick_info->time_stamp); + } else { + ret = gfdb_methods.find_recently_changed_files_freq( + conn_node, tier_gf_query_callback, (void *)query_cbk_args, + gfdb_brick_info->time_stamp, + query_cbk_args->defrag->write_freq_threshold, + query_cbk_args->defrag->read_freq_threshold, _gf_false); + } + } + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "FATAL: query from db failed"); + goto out; + } + + /*Clear the heat on the DB entries*/ + /*Preparing ctr_ipc_dict*/ + ctr_ipc_dict = dict_new(); + if (!ctr_ipc_dict) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "ctr_ipc_dict cannot initialized"); + goto out; + } + + SET_DB_PARAM_TO_DICT(this->name, ctr_ipc_dict, GFDB_IPC_CTR_KEY, + GFDB_IPC_CTR_CLEAR_OPS, ret, out); + + ret = syncop_ipc(local_brick->xlator, GF_IPC_TARGET_CTR, ctr_ipc_dict, + NULL); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Failed clearing the heat " + "on db %s error %d", + local_brick->brick_db_path, ret); + goto out; + } + + ret = 0; +out: + if (params_dict) { + dict_unref(params_dict); + params_dict = NULL; + } + + if (ctr_ipc_dict) { + dict_unref(ctr_ipc_dict); + ctr_ipc_dict = NULL; + } + + if (query_cbk_args && query_cbk_args->query_fd >= 0) { + sys_close(query_cbk_args->query_fd); + query_cbk_args->query_fd = -1; + } + gfdb_methods.fini_db(conn_node); + + return ret; +} + +/*Ask CTR to create the query file*/ +static int +tier_process_ctr_query(tier_brick_list_t *local_brick, void *args) +{ + int ret = -1; + query_cbk_args_t *query_cbk_args = NULL; + xlator_t *this = NULL; + dict_t *ctr_ipc_in_dict = NULL; + dict_t *ctr_ipc_out_dict = NULL; + gfdb_brick_info_t *gfdb_brick_info = args; + gfdb_ipc_ctr_params_t *ipc_ctr_params = NULL; + int count = 0; + + /*Init of all the essentials*/ + GF_VALIDATE_OR_GOTO("tier", gfdb_brick_info, out); + query_cbk_args = gfdb_brick_info->_query_cbk_args; + + GF_VALIDATE_OR_GOTO("tier", query_cbk_args->this, out); + this = query_cbk_args->this; + + GF_VALIDATE_OR_GOTO(this->name, gfdb_brick_info->_query_cbk_args, out); + + GF_VALIDATE_OR_GOTO(this->name, local_brick, out); + + GF_VALIDATE_OR_GOTO(this->name, local_brick->xlator, out); + + GF_VALIDATE_OR_GOTO(this->name, local_brick->brick_db_path, out); + + /*Preparing ctr_ipc_in_dict*/ + ctr_ipc_in_dict = dict_new(); + if (!ctr_ipc_in_dict) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "ctr_ipc_in_dict cannot initialized"); + goto out; + } + + ipc_ctr_params = GF_CALLOC(1, sizeof(gfdb_ipc_ctr_params_t), + gf_tier_mt_ipc_ctr_params_t); + if (!ipc_ctr_params) { + goto out; + } + + /* set all the query params*/ + ipc_ctr_params->is_promote = gfdb_brick_info->_gfdb_promote; + + ipc_ctr_params->write_freq_threshold = query_cbk_args->defrag + ->write_freq_threshold; + + ipc_ctr_params->read_freq_threshold = query_cbk_args->defrag + ->read_freq_threshold; + + ipc_ctr_params->query_limit = query_cbk_args->defrag->tier_conf.query_limit; + + ipc_ctr_params->emergency_demote = (!gfdb_brick_info->_gfdb_promote && + query_cbk_args->defrag->tier_conf + .watermark_last == TIER_WM_HI); + + memcpy(&ipc_ctr_params->time_stamp, gfdb_brick_info->time_stamp, + sizeof(gfdb_time_t)); + + SET_DB_PARAM_TO_DICT(this->name, ctr_ipc_in_dict, GFDB_IPC_CTR_KEY, + GFDB_IPC_CTR_QUERY_OPS, ret, out); + + SET_DB_PARAM_TO_DICT(this->name, ctr_ipc_in_dict, + GFDB_IPC_CTR_GET_QFILE_PATH, local_brick->qfile_path, + ret, out); + + ret = dict_set_bin(ctr_ipc_in_dict, GFDB_IPC_CTR_GET_QUERY_PARAMS, + ipc_ctr_params, sizeof(*ipc_ctr_params)); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, LG_MSG_SET_PARAM_FAILED, + "Failed setting %s to params dictionary", + GFDB_IPC_CTR_GET_QUERY_PARAMS); + GF_FREE(ipc_ctr_params); + goto out; + } + ipc_ctr_params = NULL; + + ret = syncop_ipc(local_brick->xlator, GF_IPC_TARGET_CTR, ctr_ipc_in_dict, + &ctr_ipc_out_dict); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_IPC_TIER_ERROR, + "Failed query on %s ret %d", local_brick->brick_db_path, ret); + goto out; + } + + ret = dict_get_int32(ctr_ipc_out_dict, GFDB_IPC_CTR_RET_QUERY_COUNT, + &count); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Failed getting count " + "of records on %s", + local_brick->brick_db_path); + goto out; + } + + if (count < 0) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Failed query on %s", local_brick->brick_db_path); + ret = -1; + goto out; + } + + pthread_mutex_lock(&dm_stat_mutex); + query_cbk_args->defrag->num_files_lookedup = count; + pthread_mutex_unlock(&dm_stat_mutex); + + ret = 0; +out: + + if (ctr_ipc_in_dict) { + dict_unref(ctr_ipc_in_dict); + ctr_ipc_in_dict = NULL; + } + + if (ctr_ipc_out_dict) { + dict_unref(ctr_ipc_out_dict); + ctr_ipc_out_dict = NULL; + } + + GF_FREE(ipc_ctr_params); + + return ret; +} + +/* This is the call back function for each brick from hot/cold bricklist + * It picks up each bricks db and queries for eligible files for migration. + * The list of eligible files are populated in appropriate query files*/ +static int +tier_process_brick(tier_brick_list_t *local_brick, void *args) +{ + int ret = -1; + dict_t *ctr_ipc_in_dict = NULL; + dict_t *ctr_ipc_out_dict = NULL; + char *strval = NULL; + + GF_VALIDATE_OR_GOTO("tier", local_brick, out); + + GF_VALIDATE_OR_GOTO("tier", local_brick->xlator, out); + + if (dht_tier_db_type == GFDB_SQLITE3) { + /*Preparing ctr_ipc_in_dict*/ + ctr_ipc_in_dict = dict_new(); + if (!ctr_ipc_in_dict) { + gf_msg("tier", GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "ctr_ipc_in_dict cannot initialized"); + goto out; + } + + ret = dict_set_str(ctr_ipc_in_dict, GFDB_IPC_CTR_KEY, + GFDB_IPC_CTR_GET_DB_PARAM_OPS); + if (ret) { + gf_msg("tier", GF_LOG_ERROR, 0, LG_MSG_SET_PARAM_FAILED, + "Failed to set %s " + "to params dictionary", + GFDB_IPC_CTR_KEY); + goto out; + } + + ret = dict_set_str(ctr_ipc_in_dict, GFDB_IPC_CTR_GET_DB_PARAM_OPS, ""); + if (ret) { + gf_msg("tier", GF_LOG_ERROR, 0, LG_MSG_SET_PARAM_FAILED, + "Failed to set %s " + "to params dictionary", + GFDB_IPC_CTR_GET_DB_PARAM_OPS); + goto out; + } + + ret = dict_set_str(ctr_ipc_in_dict, GFDB_IPC_CTR_GET_DB_KEY, + "journal_mode"); + if (ret) { + gf_msg("tier", GF_LOG_ERROR, 0, LG_MSG_SET_PARAM_FAILED, + "Failed to set %s " + "to params dictionary", + GFDB_IPC_CTR_GET_DB_KEY); + goto out; + } + + ret = syncop_ipc(local_brick->xlator, GF_IPC_TARGET_CTR, + ctr_ipc_in_dict, &ctr_ipc_out_dict); + if (ret || ctr_ipc_out_dict == NULL) { + gf_msg("tier", GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Failed to get " + "journal_mode of sql db %s", + local_brick->brick_db_path); + goto out; + } + + ret = dict_get_str(ctr_ipc_out_dict, "journal_mode", &strval); + if (ret) { + gf_msg("tier", GF_LOG_ERROR, 0, LG_MSG_GET_PARAM_FAILED, + "Failed to get %s " + "from params dictionary" + "journal_mode", + strval); + goto out; + } + + if (strval && (strncmp(strval, "wal", SLEN("wal")) == 0)) { + ret = tier_process_self_query(local_brick, args); + if (ret) { + goto out; + } + } else { + ret = tier_process_ctr_query(local_brick, args); + if (ret) { + goto out; + } + } + ret = 0; + + } else { + ret = tier_process_self_query(local_brick, args); + if (ret) { + goto out; + } + } + + ret = 0; +out: + if (ctr_ipc_in_dict) + dict_unref(ctr_ipc_in_dict); + + if (ctr_ipc_out_dict) + dict_unref(ctr_ipc_out_dict); + + return ret; +} + +static int +tier_build_migration_qfile(migration_args_t *args, + query_cbk_args_t *query_cbk_args, + gf_boolean_t is_promotion) +{ + gfdb_time_t current_time; + gfdb_brick_info_t gfdb_brick_info; + gfdb_time_t time_in_past; + int ret = -1; + tier_brick_list_t *local_brick = NULL; + int i = 0; + time_in_past.tv_sec = args->freq_time; + time_in_past.tv_usec = 0; + + ret = gettimeofday(¤t_time, NULL); + if (ret == -1) { + gf_msg(args->this->name, GF_LOG_ERROR, errno, + DHT_MSG_SYS_CALL_GET_TIME_FAILED, "Failed to get current time"); + goto out; + } + time_in_past.tv_sec = current_time.tv_sec - time_in_past.tv_sec; + + /* The migration daemon may run a varying numberof usec after the */ + /* sleep call triggers. A file may be registered in CTR some number */ + /* of usec X after the daemon started and missed in the subsequent */ + /* cycle if the daemon starts Y usec after the period in seconds */ + /* where Y>X. Normalize away this problem by always setting usec */ + /* to 0. */ + time_in_past.tv_usec = 0; + + gfdb_brick_info.time_stamp = &time_in_past; + gfdb_brick_info._gfdb_promote = is_promotion; + gfdb_brick_info._query_cbk_args = query_cbk_args; + + list_for_each_entry(local_brick, args->brick_list, list) + { + /* Construct query file path for this brick + * i.e + * /var/run/gluster/xlator_name/ + * {promote/demote}-brickname-indexinbricklist + * So that no two query files will have same path even + * bricks have the same name + * */ + snprintf(local_brick->qfile_path, PATH_MAX, "%s-%s-%d", + GET_QFILE_PATH(gfdb_brick_info._gfdb_promote), + local_brick->brick_name, i); + + /* Delete any old query files for this brick */ + sys_unlink(local_brick->qfile_path); + + ret = tier_process_brick(local_brick, &gfdb_brick_info); + if (ret) { + gf_msg(args->this->name, GF_LOG_ERROR, 0, + DHT_MSG_BRICK_QUERY_FAILED, "Brick %s query failed\n", + local_brick->brick_db_path); + } + i++; + } + ret = 0; +out: + return ret; +} + +static int +tier_migrate_files_using_qfile(migration_args_t *comp, + query_cbk_args_t *query_cbk_args) +{ + int ret = -1; + tier_brick_list_t *local_brick = NULL; + tier_brick_list_t *temp = NULL; + gfdb_time_t current_time = { + 0, + }; + ssize_t qfile_array_size = 0; + int count = 0; + int temp_fd = 0; + gf_tier_conf_t *tier_conf = NULL; + + tier_conf = &(query_cbk_args->defrag->tier_conf); + + /* Time for error query files */ + gettimeofday(¤t_time, NULL); + + /* Build the qfile list */ + list_for_each_entry_safe(local_brick, temp, comp->brick_list, list) + { + qfile_array_size++; + } + query_cbk_args->qfile_array = qfile_array_new(qfile_array_size); + if (!query_cbk_args->qfile_array) { + gf_msg("tier", GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Failed to create new " + "qfile_array"); + goto out; + } + + /*Open all qfiles*/ + count = 0; + query_cbk_args->qfile_array->exhausted_count = 0; + list_for_each_entry_safe(local_brick, temp, comp->brick_list, list) + { + temp_fd = query_cbk_args->qfile_array->fd_array[count]; + temp_fd = open(local_brick->qfile_path, O_RDONLY, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (temp_fd < 0) { + gf_msg("tier", GF_LOG_ERROR, errno, DHT_MSG_LOG_TIER_ERROR, + "Failed to open " + "%s to the query file", + local_brick->qfile_path); + query_cbk_args->qfile_array->exhausted_count++; + } + query_cbk_args->qfile_array->fd_array[count] = temp_fd; + count++; + } + + /* Moving the query file index to the next, so that we won't the same + * query file every cycle as the first one */ + query_cbk_args->qfile_array + ->next_index = (query_cbk_args->is_promotion) + ? tier_conf->last_promote_qfile_index + : tier_conf->last_demote_qfile_index; + shift_next_index(query_cbk_args->qfile_array); + if (query_cbk_args->is_promotion) { + tier_conf->last_promote_qfile_index = query_cbk_args->qfile_array + ->next_index; + } else { + tier_conf->last_demote_qfile_index = query_cbk_args->qfile_array + ->next_index; + } + + /* Migrate files using query file list */ + ret = tier_migrate_using_query_file((void *)query_cbk_args); +out: + qfile_array_free(query_cbk_args->qfile_array); + + /* If there is an error rename all the query files to .err files + * with a timestamp for better debugging */ + if (ret) { + struct tm tm = { + 0, + }; + char time_str[128] = { + 0, + }; + char query_file_path_err[PATH_MAX] = { + 0, + }; + int32_t len = 0; + + /* Time format for error query files */ + gmtime_r(¤t_time.tv_sec, &tm); + strftime(time_str, sizeof(time_str), "%F-%T", &tm); + + list_for_each_entry_safe(local_brick, temp, comp->brick_list, list) + { + /* rename error qfile*/ + len = snprintf(query_file_path_err, sizeof(query_file_path_err), + "%s-%s.err", local_brick->qfile_path, time_str); + if ((len >= 0) && (len < sizeof(query_file_path_err))) { + if (sys_rename(local_brick->qfile_path, query_file_path_err) == + -1) + gf_msg_debug("tier", 0, + "rename " + "failed"); + } + } + } + + query_cbk_args->qfile_array = NULL; + + return ret; +} + +int +tier_demote(migration_args_t *demotion_args) +{ + query_cbk_args_t query_cbk_args; + int ret = -1; + + GF_VALIDATE_OR_GOTO("tier", demotion_args, out); + GF_VALIDATE_OR_GOTO("tier", demotion_args->this, out); + GF_VALIDATE_OR_GOTO(demotion_args->this->name, demotion_args->brick_list, + out); + GF_VALIDATE_OR_GOTO(demotion_args->this->name, demotion_args->defrag, out); + + THIS = demotion_args->this; + + query_cbk_args.this = demotion_args->this; + query_cbk_args.defrag = demotion_args->defrag; + query_cbk_args.is_promotion = 0; + + /*Build the query file using bricklist*/ + ret = tier_build_migration_qfile(demotion_args, &query_cbk_args, _gf_false); + if (ret) + goto out; + + /* Migrate files using the query file */ + ret = tier_migrate_files_using_qfile(demotion_args, &query_cbk_args); + if (ret) + goto out; + +out: + demotion_args->return_value = ret; + return ret; +} + +int +tier_promote(migration_args_t *promotion_args) +{ + int ret = -1; + query_cbk_args_t query_cbk_args; + + GF_VALIDATE_OR_GOTO("tier", promotion_args->this, out); + GF_VALIDATE_OR_GOTO(promotion_args->this->name, promotion_args->brick_list, + out); + GF_VALIDATE_OR_GOTO(promotion_args->this->name, promotion_args->defrag, + out); + + THIS = promotion_args->this; + + query_cbk_args.this = promotion_args->this; + query_cbk_args.defrag = promotion_args->defrag; + query_cbk_args.is_promotion = 1; + + /*Build the query file using bricklist*/ + ret = tier_build_migration_qfile(promotion_args, &query_cbk_args, _gf_true); + if (ret) + goto out; + + /* Migrate files using the query file */ + ret = tier_migrate_files_using_qfile(promotion_args, &query_cbk_args); + if (ret) + goto out; + +out: + promotion_args->return_value = ret; + return ret; +} + +/* + * Command the CTR on a brick to compact the local database using an IPC + */ +static int +tier_process_self_compact(tier_brick_list_t *local_brick, void *args) +{ + int ret = -1; + char *db_path = NULL; + query_cbk_args_t *query_cbk_args = NULL; + xlator_t *this = NULL; + gfdb_conn_node_t *conn_node = NULL; + dict_t *params_dict = NULL; + dict_t *ctr_ipc_dict = NULL; + gfdb_brick_info_t *gfdb_brick_info = args; + + /*Init of all the essentials*/ + GF_VALIDATE_OR_GOTO("tier", gfdb_brick_info, out); + query_cbk_args = gfdb_brick_info->_query_cbk_args; + + GF_VALIDATE_OR_GOTO("tier", query_cbk_args->this, out); + this = query_cbk_args->this; + + GF_VALIDATE_OR_GOTO(this->name, gfdb_brick_info->_query_cbk_args, out); + + GF_VALIDATE_OR_GOTO(this->name, local_brick, out); + + GF_VALIDATE_OR_GOTO(this->name, local_brick->xlator, out); + + GF_VALIDATE_OR_GOTO(this->name, local_brick->brick_db_path, out); + + db_path = local_brick->brick_db_path; + + /*Preparing DB parameters before init_db i.e getting db connection*/ + params_dict = dict_new(); + if (!params_dict) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "DB Params cannot initialized"); + goto out; + } + SET_DB_PARAM_TO_DICT(this->name, params_dict, + (char *)gfdb_methods.get_db_path_key(), db_path, ret, + out); + + /*Get the db connection*/ + conn_node = gfdb_methods.init_db((void *)params_dict, dht_tier_db_type); + if (!conn_node) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "FATAL: Failed initializing db operations"); + goto out; + } + + ret = 0; + + /*Preparing ctr_ipc_dict*/ + ctr_ipc_dict = dict_new(); + if (!ctr_ipc_dict) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "ctr_ipc_dict cannot initialized"); + goto out; + } + + ret = dict_set_int32(ctr_ipc_dict, "compact_active", + query_cbk_args->defrag->tier_conf.compact_active); + + if (ret) { + gf_msg("tier", GF_LOG_ERROR, 0, LG_MSG_SET_PARAM_FAILED, + "Failed to set %s " + "to params dictionary", + "compact_active"); + goto out; + } + + ret = dict_set_int32( + ctr_ipc_dict, "compact_mode_switched", + query_cbk_args->defrag->tier_conf.compact_mode_switched); + + if (ret) { + gf_msg("tier", GF_LOG_ERROR, 0, LG_MSG_SET_PARAM_FAILED, + "Failed to set %s " + "to params dictionary", + "compact_mode_switched"); + goto out; + } + + SET_DB_PARAM_TO_DICT(this->name, ctr_ipc_dict, GFDB_IPC_CTR_KEY, + GFDB_IPC_CTR_SET_COMPACT_PRAGMA, ret, out); + + gf_msg(this->name, GF_LOG_TRACE, 0, DHT_MSG_LOG_TIER_STATUS, + "Starting Compaction IPC"); + + ret = syncop_ipc(local_brick->xlator, GF_IPC_TARGET_CTR, ctr_ipc_dict, + NULL); + + gf_msg(this->name, GF_LOG_TRACE, 0, DHT_MSG_LOG_TIER_STATUS, + "Ending Compaction IPC"); + + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Failed compaction " + "on db %s error %d", + local_brick->brick_db_path, ret); + goto out; + } + + gf_msg(this->name, GF_LOG_TRACE, 0, DHT_MSG_LOG_TIER_STATUS, + "SUCCESS: %s Compaction", local_brick->brick_name); + + ret = 0; +out: + if (params_dict) { + dict_unref(params_dict); + params_dict = NULL; + } + + if (ctr_ipc_dict) { + dict_unref(ctr_ipc_dict); + ctr_ipc_dict = NULL; + } + + gfdb_methods.fini_db(conn_node); + + return ret; +} + +/* + * This is the call back function for each brick from hot/cold bricklist. + * It determines the database type on each brick and calls the corresponding + * function to prepare the compaction IPC. + */ +static int +tier_compact_db_brick(tier_brick_list_t *local_brick, void *args) +{ + int ret = -1; + + GF_VALIDATE_OR_GOTO("tier", local_brick, out); + + GF_VALIDATE_OR_GOTO("tier", local_brick->xlator, out); + + ret = tier_process_self_compact(local_brick, args); + if (ret) { + gf_msg("tier", GF_LOG_INFO, 0, DHT_MSG_LOG_TIER_STATUS, + "Brick %s did not compact", local_brick->brick_name); + goto out; + } + + ret = 0; + +out: + + return ret; +} + +static int +tier_send_compact(migration_args_t *args, query_cbk_args_t *query_cbk_args) +{ + gfdb_time_t current_time; + gfdb_brick_info_t gfdb_brick_info; + gfdb_time_t time_in_past; + int ret = -1; + tier_brick_list_t *local_brick = NULL; + + time_in_past.tv_sec = args->freq_time; + time_in_past.tv_usec = 0; + + ret = gettimeofday(¤t_time, NULL); + if (ret == -1) { + gf_msg(args->this->name, GF_LOG_ERROR, errno, + DHT_MSG_SYS_CALL_GET_TIME_FAILED, "Failed to get current time"); + goto out; + } + time_in_past.tv_sec = current_time.tv_sec - time_in_past.tv_sec; + + /* The migration daemon may run a varying numberof usec after the sleep + call triggers. A file may be registered in CTR some number of usec X + after the daemon started and missed in the subsequent cycle if the + daemon starts Y usec after the period in seconds where Y>X. Normalize + away this problem by always setting usec to 0. */ + time_in_past.tv_usec = 0; + + gfdb_brick_info.time_stamp = &time_in_past; + + /* This is meant to say we are always compacting at this point */ + /* We simply borrow the promotion flag to do this */ + gfdb_brick_info._gfdb_promote = 1; + + gfdb_brick_info._query_cbk_args = query_cbk_args; + + list_for_each_entry(local_brick, args->brick_list, list) + { + gf_msg(args->this->name, GF_LOG_TRACE, 0, DHT_MSG_LOG_TIER_STATUS, + "Start compaction for %s", local_brick->brick_name); + + ret = tier_compact_db_brick(local_brick, &gfdb_brick_info); + if (ret) { + gf_msg(args->this->name, GF_LOG_ERROR, 0, + DHT_MSG_BRICK_QUERY_FAILED, "Brick %s compaction failed\n", + local_brick->brick_db_path); + } + + gf_msg(args->this->name, GF_LOG_TRACE, 0, DHT_MSG_LOG_TIER_STATUS, + "End compaction for %s", local_brick->brick_name); + } + ret = 0; +out: + return ret; +} + +static int +tier_compact(void *args) +{ + int ret = -1; + query_cbk_args_t query_cbk_args; + migration_args_t *compaction_args = args; + + GF_VALIDATE_OR_GOTO("tier", compaction_args->this, out); + GF_VALIDATE_OR_GOTO(compaction_args->this->name, + compaction_args->brick_list, out); + GF_VALIDATE_OR_GOTO(compaction_args->this->name, compaction_args->defrag, + out); + + THIS = compaction_args->this; + + query_cbk_args.this = compaction_args->this; + query_cbk_args.defrag = compaction_args->defrag; + query_cbk_args.is_compaction = 1; + + /* Send the compaction pragma out to all the bricks on the bricklist. */ + /* tier_get_bricklist ensures all bricks on the list are local to */ + /* this node. */ + ret = tier_send_compact(compaction_args, &query_cbk_args); + if (ret) + goto out; + + ret = 0; +out: + compaction_args->return_value = ret; + return ret; +} + +static int +tier_get_bricklist(xlator_t *xl, struct list_head *local_bricklist_head) +{ + xlator_list_t *child = NULL; + char *rv = NULL; + char *rh = NULL; + char *brickname = NULL; + char db_name[PATH_MAX] = ""; + int ret = 0; + tier_brick_list_t *local_brick = NULL; + int32_t len = 0; + + GF_VALIDATE_OR_GOTO("tier", xl, out); + GF_VALIDATE_OR_GOTO("tier", local_bricklist_head, out); + + /* + * This function obtains remote subvolumes and filters out only + * those running on the same node as the tier daemon. + */ + if (strcmp(xl->type, "protocol/client") == 0) { + ret = dict_get_str(xl->options, "remote-host", &rh); + if (ret < 0) + goto out; + + if (gf_is_local_addr(rh)) { + local_brick = GF_CALLOC(1, sizeof(tier_brick_list_t), + gf_tier_mt_bricklist_t); + if (!local_brick) { + goto out; + } + + ret = dict_get_str(xl->options, "remote-subvolume", &rv); + if (ret < 0) + goto out; + + brickname = strrchr(rv, '/') + 1; + snprintf(db_name, sizeof(db_name), "%s.db", brickname); + + local_brick->brick_db_path = GF_MALLOC(PATH_MAX, gf_common_mt_char); + if (!local_brick->brick_db_path) { + gf_msg("tier", GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_STATUS, + "Failed to allocate memory for" + " bricklist."); + ret = -1; + goto out; + } + + len = snprintf(local_brick->brick_db_path, PATH_MAX, "%s/%s/%s", rv, + GF_HIDDEN_PATH, db_name); + if ((len < 0) || (len >= PATH_MAX)) { + gf_msg("tier", GF_LOG_ERROR, EINVAL, DHT_MSG_LOG_TIER_STATUS, + "DB path too long"); + ret = -1; + goto out; + } + + local_brick->xlator = xl; + + snprintf(local_brick->brick_name, NAME_MAX, "%s", brickname); + + list_add_tail(&(local_brick->list), local_bricklist_head); + + ret = 0; + goto out; + } + } + + for (child = xl->children; child; child = child->next) { + ret = tier_get_bricklist(child->xlator, local_bricklist_head); + if (ret) { + goto out; + } + } + + ret = 0; +out: + + if (ret) { + if (local_brick) { + GF_FREE(local_brick->brick_db_path); + } + GF_FREE(local_brick); + } + + return ret; +} + +int +tier_get_freq_demote(gf_tier_conf_t *tier_conf) +{ + if ((tier_conf->mode == TIER_MODE_WM) && + (tier_conf->watermark_last == TIER_WM_HI)) + return DEFAULT_DEMOTE_DEGRADED; + else + return tier_conf->tier_demote_frequency; +} + +int +tier_get_freq_promote(gf_tier_conf_t *tier_conf) +{ + return tier_conf->tier_promote_frequency; +} + +int +tier_get_freq_compact_hot(gf_tier_conf_t *tier_conf) +{ + return tier_conf->tier_compact_hot_frequency; +} + +int +tier_get_freq_compact_cold(gf_tier_conf_t *tier_conf) +{ + return tier_conf->tier_compact_cold_frequency; +} + +static int +tier_check_demote(gfdb_time_t current_time, int freq) +{ + return ((current_time.tv_sec % freq) == 0) ? _gf_true : _gf_false; +} + +static gf_boolean_t +tier_check_promote(gf_tier_conf_t *tier_conf, gfdb_time_t current_time, + int freq) +{ + if ((tier_conf->mode == TIER_MODE_WM) && + (tier_conf->watermark_last == TIER_WM_HI)) + return _gf_false; + + else + return ((current_time.tv_sec % freq) == 0) ? _gf_true : _gf_false; +} + +static gf_boolean_t +tier_check_compact(gf_tier_conf_t *tier_conf, gfdb_time_t current_time, + int freq_compact) +{ + if (!(tier_conf->compact_active || tier_conf->compact_mode_switched)) + return _gf_false; + + return ((current_time.tv_sec % freq_compact) == 0) ? _gf_true : _gf_false; +} + +void +clear_bricklist(struct list_head *brick_list) +{ + tier_brick_list_t *local_brick = NULL; + tier_brick_list_t *temp = NULL; + + if (list_empty(brick_list)) { + return; + } + + list_for_each_entry_safe(local_brick, temp, brick_list, list) + { + list_del(&local_brick->list); + GF_FREE(local_brick->brick_db_path); + GF_FREE(local_brick); + } +} + +static void +set_brick_list_qpath(struct list_head *brick_list, gf_boolean_t is_cold) +{ + tier_brick_list_t *local_brick = NULL; + int i = 0; + + GF_VALIDATE_OR_GOTO("tier", brick_list, out); + + list_for_each_entry(local_brick, brick_list, list) + { + /* Construct query file path for this brick + * i.e + * /var/run/gluster/xlator_name/ + * {promote/demote}-brickname-indexinbricklist + * So that no two query files will have same path even + * bricks have the same name + * */ + snprintf(local_brick->qfile_path, PATH_MAX, "%s-%s-%d", + GET_QFILE_PATH(is_cold), local_brick->brick_name, i); + i++; + } +out: + return; +} + +static int +tier_prepare_compact(migration_args_t *args, gfdb_time_t current_time) +{ + xlator_t *this = NULL; + dht_conf_t *conf = NULL; + gf_defrag_info_t *defrag = NULL; + gf_tier_conf_t *tier_conf = NULL; + gf_boolean_t is_hot_tier = args->is_hot_tier; + int freq = 0; + int ret = -1; + const char *tier_type = is_hot_tier ? "hot" : "cold"; + + this = args->this; + + conf = this->private; + + defrag = conf->defrag; + + tier_conf = &defrag->tier_conf; + + freq = is_hot_tier ? tier_get_freq_compact_hot(tier_conf) + : tier_get_freq_compact_cold(tier_conf); + + defrag->tier_conf.compact_mode_switched = + is_hot_tier ? defrag->tier_conf.compact_mode_switched_hot + : defrag->tier_conf.compact_mode_switched_cold; + + gf_msg(this->name, GF_LOG_TRACE, 0, DHT_MSG_LOG_TIER_STATUS, + "Compact mode %i", defrag->tier_conf.compact_mode_switched); + + if (tier_check_compact(tier_conf, current_time, freq)) { + gf_msg(this->name, GF_LOG_INFO, 0, DHT_MSG_LOG_TIER_STATUS, + "Start compaction on %s tier", tier_type); + + args->freq_time = freq; + ret = tier_compact(args); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Compaction failed on " + "%s tier", + tier_type); + goto out; + } + + gf_msg(this->name, GF_LOG_INFO, 0, DHT_MSG_LOG_TIER_STATUS, + "End compaction on %s tier", tier_type); + + if (is_hot_tier) { + defrag->tier_conf.compact_mode_switched_hot = _gf_false; + } else { + defrag->tier_conf.compact_mode_switched_cold = _gf_false; + } + } + +out: + return ret; +} + +static int +tier_get_wm_interval(tier_mode_t mode, tier_watermark_op_t wm) +{ + if (mode == TIER_MODE_WM && wm == TIER_WM_HI) + return WM_INTERVAL_EMERG; + + return WM_INTERVAL; +} + +/* + * Main tiering loop. This is called from the promotion and the + * demotion threads spawned in tier_start(). + * + * Every second, wake from sleep to perform tasks. + * 1. Check trigger to migrate data. + * 2. Check for state changes (pause, unpause, stop). + */ +static void * +tier_run(void *in_args) +{ + dht_conf_t *conf = NULL; + gfdb_time_t current_time = {0}; + int freq = 0; + int ret = 0; + xlator_t *any = NULL; + xlator_t *xlator = NULL; + gf_tier_conf_t *tier_conf = NULL; + loc_t root_loc = {0}; + int check_watermark = 0; + gf_defrag_info_t *defrag = NULL; + xlator_t *this = NULL; + migration_args_t *args = in_args; + GF_VALIDATE_OR_GOTO("tier", args, out); + GF_VALIDATE_OR_GOTO("tier", args->brick_list, out); + + this = args->this; + GF_VALIDATE_OR_GOTO("tier", this, out); + + conf = this->private; + GF_VALIDATE_OR_GOTO("tier", conf, out); + + defrag = conf->defrag; + GF_VALIDATE_OR_GOTO("tier", defrag, out); + + if (list_empty(args->brick_list)) { + gf_msg(this->name, GF_LOG_INFO, 0, DHT_MSG_LOG_TIER_ERROR, + "Brick list for tier is empty. Exiting."); + goto out; + } + + defrag->defrag_status = GF_DEFRAG_STATUS_STARTED; + tier_conf = &defrag->tier_conf; + + dht_build_root_loc(defrag->root_inode, &root_loc); + + while (1) { + /* + * Check if a graph switch occurred. If so, stop migration + * thread. It will need to be restarted manually. + */ + any = THIS->ctx->active->first; + xlator = xlator_search_by_name(any, this->name); + + if (xlator != this) { + gf_msg(this->name, GF_LOG_INFO, 0, DHT_MSG_LOG_TIER_STATUS, + "Detected graph switch. Exiting migration " + "daemon."); + goto out; + } + + gf_defrag_check_pause_tier(tier_conf); + + sleep(1); + + if (defrag->defrag_status != GF_DEFRAG_STATUS_STARTED) { + ret = 1; + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "defrag->defrag_status != " + "GF_DEFRAG_STATUS_STARTED"); + goto out; + } + + if (defrag->cmd == GF_DEFRAG_CMD_START_DETACH_TIER || + defrag->cmd == GF_DEFRAG_CMD_DETACH_START) { + ret = 0; + defrag->defrag_status = GF_DEFRAG_STATUS_COMPLETE; + gf_msg(this->name, GF_LOG_DEBUG, 0, DHT_MSG_LOG_TIER_ERROR, + "defrag->defrag_cmd == " + "GF_DEFRAG_CMD_START_DETACH_TIER"); + goto out; + } + + if (gf_defrag_get_pause_state(&defrag->tier_conf) != TIER_RUNNING) + continue; + + /* To have proper synchronization amongst all + * brick holding nodes, so that promotion and demotions + * start atomically w.r.t promotion/demotion frequency + * period, all nodes should have their system time + * in-sync with each other either manually set or + * using a NTP server*/ + ret = gettimeofday(¤t_time, NULL); + if (ret == -1) { + gf_msg(this->name, GF_LOG_ERROR, errno, + DHT_MSG_SYS_CALL_GET_TIME_FAILED, + "Failed to get current time"); + goto out; + } + + check_watermark++; + + /* emergency demotion requires frequent watermark monitoring */ + if (check_watermark >= + tier_get_wm_interval(tier_conf->mode, tier_conf->watermark_last)) { + check_watermark = 0; + if (tier_conf->mode == TIER_MODE_WM) { + ret = tier_get_fs_stat(this, &root_loc); + if (ret != 0) { + continue; + } + ret = tier_check_watermark(this); + if (ret != 0) { + gf_msg(this->name, GF_LOG_CRITICAL, errno, + DHT_MSG_LOG_TIER_ERROR, "Failed to get watermark"); + continue; + } + } + } + + if (args->is_promotion) { + freq = tier_get_freq_promote(tier_conf); + + if (tier_check_promote(tier_conf, current_time, freq)) { + args->freq_time = freq; + ret = tier_promote(args); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Promotion failed"); + } + } + } else if (args->is_compaction) { + tier_prepare_compact(args, current_time); + } else { + freq = tier_get_freq_demote(tier_conf); + + if (tier_check_demote(current_time, freq)) { + args->freq_time = freq; + ret = tier_demote(args); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Demotion failed"); + } + } + } + + /* Check the statfs immediately after the processing threads + return */ + check_watermark = WM_INTERVAL; + } + + ret = 0; +out: + + args->return_value = ret; + + return NULL; +} + +int +tier_start(xlator_t *this, gf_defrag_info_t *defrag) +{ + pthread_t promote_thread; + pthread_t demote_thread; + pthread_t hot_compact_thread; + pthread_t cold_compact_thread; + int ret = -1; + struct list_head bricklist_hot = {0}; + struct list_head bricklist_cold = {0}; + migration_args_t promotion_args = {0}; + migration_args_t demotion_args = {0}; + migration_args_t hot_compaction_args = {0}; + migration_args_t cold_compaction_args = {0}; + dht_conf_t *conf = NULL; + + INIT_LIST_HEAD((&bricklist_hot)); + INIT_LIST_HEAD((&bricklist_cold)); + + conf = this->private; + + tier_get_bricklist(conf->subvolumes[1], &bricklist_hot); + set_brick_list_qpath(&bricklist_hot, _gf_false); + + demotion_args.this = this; + demotion_args.brick_list = &bricklist_hot; + demotion_args.defrag = defrag; + demotion_args.is_promotion = _gf_false; + demotion_args.is_compaction = _gf_false; + + ret = gf_thread_create(&demote_thread, NULL, &tier_run, &demotion_args, + "tierdem"); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Failed to start demotion thread."); + defrag->defrag_status = GF_DEFRAG_STATUS_FAILED; + goto cleanup; + } + + tier_get_bricklist(conf->subvolumes[0], &bricklist_cold); + set_brick_list_qpath(&bricklist_cold, _gf_true); + + promotion_args.this = this; + promotion_args.brick_list = &bricklist_cold; + promotion_args.defrag = defrag; + promotion_args.is_promotion = _gf_true; + + ret = gf_thread_create(&promote_thread, NULL, &tier_run, &promotion_args, + "tierpro"); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Failed to start promotion thread."); + defrag->defrag_status = GF_DEFRAG_STATUS_FAILED; + goto waitforspawned; + } + + hot_compaction_args.this = this; + hot_compaction_args.brick_list = &bricklist_hot; + hot_compaction_args.defrag = defrag; + hot_compaction_args.is_promotion = _gf_false; + hot_compaction_args.is_compaction = _gf_true; + hot_compaction_args.is_hot_tier = _gf_true; + + ret = gf_thread_create(&hot_compact_thread, NULL, &tier_run, + &hot_compaction_args, "tierhcom"); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Failed to start compaction thread."); + defrag->defrag_status = GF_DEFRAG_STATUS_FAILED; + goto waitforspawnedpromote; + } + + cold_compaction_args.this = this; + cold_compaction_args.brick_list = &bricklist_cold; + cold_compaction_args.defrag = defrag; + cold_compaction_args.is_promotion = _gf_false; + cold_compaction_args.is_compaction = _gf_true; + cold_compaction_args.is_hot_tier = _gf_false; + + ret = gf_thread_create(&cold_compact_thread, NULL, &tier_run, + &cold_compaction_args, "tierccom"); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Failed to start compaction thread."); + defrag->defrag_status = GF_DEFRAG_STATUS_FAILED; + goto waitforspawnedhotcompact; + } + pthread_join(cold_compact_thread, NULL); + +waitforspawnedhotcompact: + pthread_join(hot_compact_thread, NULL); + +waitforspawnedpromote: + pthread_join(promote_thread, NULL); + +waitforspawned: + pthread_join(demote_thread, NULL); + +cleanup: + clear_bricklist(&bricklist_cold); + clear_bricklist(&bricklist_hot); + return ret; +} + +int32_t +tier_migration_needed(xlator_t *this) +{ + gf_defrag_info_t *defrag = NULL; + dht_conf_t *conf = NULL; + int ret = 0; + + conf = this->private; + + GF_VALIDATE_OR_GOTO(this->name, conf, out); + GF_VALIDATE_OR_GOTO(this->name, conf->defrag, out); + + defrag = conf->defrag; + + if ((defrag->cmd == GF_DEFRAG_CMD_START_TIER) || + (defrag->cmd == GF_DEFRAG_CMD_START_DETACH_TIER)) + ret = 1; +out: + return ret; +} + +int32_t +tier_migration_get_dst(xlator_t *this, dht_local_t *local) +{ + dht_conf_t *conf = NULL; + int32_t ret = -1; + gf_defrag_info_t *defrag = NULL; + + GF_VALIDATE_OR_GOTO("tier", this, out); + GF_VALIDATE_OR_GOTO(this->name, this->private, out); + + conf = this->private; + + defrag = conf->defrag; + + if (defrag && defrag->cmd == GF_DEFRAG_CMD_START_DETACH_TIER) { + local->rebalance.target_node = conf->subvolumes[0]; + + } else if (conf->subvolumes[0] == local->cached_subvol) + local->rebalance.target_node = conf->subvolumes[1]; + else + local->rebalance.target_node = conf->subvolumes[0]; + + if (local->rebalance.target_node) + ret = 0; + +out: + return ret; +} + +xlator_t * +tier_search(xlator_t *this, dht_layout_t *layout, const char *name) +{ + xlator_t *subvol = NULL; + dht_conf_t *conf = NULL; + + GF_VALIDATE_OR_GOTO("tier", this, out); + GF_VALIDATE_OR_GOTO(this->name, this->private, out); + + conf = this->private; + + subvol = TIER_HASHED_SUBVOL; + +out: + return subvol; +} + +static int +tier_load_externals(xlator_t *this) +{ + int ret = -1; + char *libpathfull = (LIBDIR "/libgfdb.so.0"); + get_gfdb_methods_t get_gfdb_methods; + + GF_VALIDATE_OR_GOTO("this", this, out); + + libhandle = dlopen(libpathfull, RTLD_NOW); + if (!libhandle) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Error loading libgfdb.so %s\n", dlerror()); + ret = -1; + goto out; + } + + get_gfdb_methods = dlsym(libhandle, "get_gfdb_methods"); + if (!get_gfdb_methods) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Error loading get_gfdb_methods()"); + ret = -1; + goto out; + } + + get_gfdb_methods(&gfdb_methods); + + ret = 0; + +out: + if (ret && libhandle) + dlclose(libhandle); + + return ret; +} + +static tier_mode_t +tier_validate_mode(char *mode) +{ + int ret = -1; + + if (strcmp(mode, "test") == 0) { + ret = TIER_MODE_TEST; + } else { + ret = TIER_MODE_WM; + } + + return ret; +} + +static gf_boolean_t +tier_validate_compact_mode(char *mode) +{ + gf_boolean_t ret = _gf_false; + + gf_msg("tier", GF_LOG_INFO, 0, DHT_MSG_LOG_TIER_STATUS, + "tier_validate_compact_mode: mode = %s", mode); + + if (!strcmp(mode, "on")) { + ret = _gf_true; + } else { + ret = _gf_false; + } + + gf_msg("tier", GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_STATUS, + "tier_validate_compact_mode: ret = %i", ret); + + return ret; +} + +int +tier_init_methods(xlator_t *this) +{ + int ret = -1; + dht_conf_t *conf = NULL; + dht_methods_t *methods = NULL; + + GF_VALIDATE_OR_GOTO("tier", this, err); + + conf = this->private; + + methods = &(conf->methods); + + methods->migration_get_dst_subvol = tier_migration_get_dst; + methods->migration_other = tier_start; + methods->migration_needed = tier_migration_needed; + methods->layout_search = tier_search; + + ret = 0; +err: + return ret; +} + +static void +tier_save_vol_name(xlator_t *this) +{ + dht_conf_t *conf = NULL; + gf_defrag_info_t *defrag = NULL; + char *suffix = NULL; + int name_len = 0; + + conf = this->private; + defrag = conf->defrag; + + suffix = strstr(this->name, "-tier-dht"); + + if (suffix) + name_len = suffix - this->name; + else + name_len = strlen(this->name); + + if (name_len > GD_VOLUME_NAME_MAX) + name_len = GD_VOLUME_NAME_MAX; + + strncpy(defrag->tier_conf.volname, this->name, name_len); + defrag->tier_conf.volname[name_len] = 0; +} + +int +tier_init(xlator_t *this) +{ + int ret = -1; + int freq = 0; + int maxsize = 0; + dht_conf_t *conf = NULL; + gf_defrag_info_t *defrag = NULL; + char *voldir = NULL; + char *mode = NULL; + char *paused = NULL; + tier_mode_t tier_mode = DEFAULT_TIER_MODE; + gf_boolean_t compact_mode = _gf_false; + + ret = dht_init(this); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "tier_init failed"); + goto out; + } + + conf = this->private; + + ret = tier_init_methods(this); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "tier_init_methods failed"); + goto out; + } + + if (conf->subvolume_cnt != 2) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Invalid number of subvolumes %d", conf->subvolume_cnt); + goto out; + } + + /* if instatiated from client side initialization is complete. */ + if (!conf->defrag) { + ret = 0; + goto out; + } + + /* if instatiated from server side, load db libraries */ + ret = tier_load_externals(this); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "Could not load externals. Aborting"); + goto out; + } + + defrag = conf->defrag; + + defrag->tier_conf.last_demote_qfile_index = 0; + defrag->tier_conf.last_promote_qfile_index = 0; + + defrag->tier_conf.is_tier = 1; + defrag->this = this; + + ret = dict_get_int32(this->options, "tier-max-promote-file-size", &maxsize); + if (ret) { + maxsize = 0; + } + + defrag->tier_conf.tier_max_promote_size = maxsize; + + ret = dict_get_int32(this->options, "tier-promote-frequency", &freq); + if (ret) { + freq = DEFAULT_PROMOTE_FREQ_SEC; + } + + defrag->tier_conf.tier_promote_frequency = freq; + + ret = dict_get_int32(this->options, "tier-demote-frequency", &freq); + if (ret) { + freq = DEFAULT_DEMOTE_FREQ_SEC; + } + + defrag->tier_conf.tier_demote_frequency = freq; + + ret = dict_get_int32(this->options, "tier-hot-compact-frequency", &freq); + if (ret) { + freq = DEFAULT_HOT_COMPACT_FREQ_SEC; + } + + defrag->tier_conf.tier_compact_hot_frequency = freq; + + ret = dict_get_int32(this->options, "tier-cold-compact-frequency", &freq); + if (ret) { + freq = DEFAULT_COLD_COMPACT_FREQ_SEC; + } + + defrag->tier_conf.tier_compact_cold_frequency = freq; + + ret = dict_get_int32(this->options, "watermark-hi", &freq); + if (ret) { + freq = DEFAULT_WM_HI; + } + + defrag->tier_conf.watermark_hi = freq; + + ret = dict_get_int32(this->options, "watermark-low", &freq); + if (ret) { + freq = DEFAULT_WM_LOW; + } + + defrag->tier_conf.watermark_low = freq; + + ret = dict_get_int32(this->options, "write-freq-threshold", &freq); + if (ret) { + freq = DEFAULT_WRITE_FREQ_SEC; + } + + defrag->write_freq_threshold = freq; + + ret = dict_get_int32(this->options, "read-freq-threshold", &freq); + if (ret) { + freq = DEFAULT_READ_FREQ_SEC; + } + + defrag->read_freq_threshold = freq; + + ret = dict_get_int32(this->options, "tier-max-mb", &freq); + if (ret) { + freq = DEFAULT_TIER_MAX_MIGRATE_MB; + } + + defrag->tier_conf.max_migrate_bytes = (uint64_t)freq * 1024 * 1024; + + ret = dict_get_int32(this->options, "tier-max-files", &freq); + if (ret) { + freq = DEFAULT_TIER_MAX_MIGRATE_FILES; + } + + defrag->tier_conf.max_migrate_files = freq; + + ret = dict_get_int32(this->options, "tier-query-limit", + &(defrag->tier_conf.query_limit)); + if (ret) { + defrag->tier_conf.query_limit = DEFAULT_TIER_QUERY_LIMIT; + } + + ret = dict_get_str(this->options, "tier-compact", &mode); + + if (ret) { + defrag->tier_conf.compact_active = DEFAULT_COMP_MODE; + } else { + compact_mode = tier_validate_compact_mode(mode); + /* If compaction is now active, we need to inform the bricks on + the hot and cold tier of this. See dht-common.h for more. */ + defrag->tier_conf.compact_active = compact_mode; + if (compact_mode) { + defrag->tier_conf.compact_mode_switched_hot = _gf_true; + defrag->tier_conf.compact_mode_switched_cold = _gf_true; + } + } + + ret = dict_get_str(this->options, "tier-mode", &mode); + if (ret) { + defrag->tier_conf.mode = DEFAULT_TIER_MODE; + } else { + tier_mode = tier_validate_mode(mode); + defrag->tier_conf.mode = tier_mode; + } + + pthread_mutex_init(&defrag->tier_conf.pause_mutex, 0); + + gf_defrag_set_pause_state(&defrag->tier_conf, TIER_RUNNING); + + ret = dict_get_str(this->options, "tier-pause", &paused); + + if (paused && strcmp(paused, "on") == 0) + gf_defrag_set_pause_state(&defrag->tier_conf, TIER_REQUEST_PAUSE); + + ret = gf_asprintf(&voldir, "%s/%s", DEFAULT_VAR_RUN_DIRECTORY, this->name); + if (ret < 0) + goto out; + + ret = mkdir_p(voldir, 0777, _gf_true); + if (ret == -1 && errno != EEXIST) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "tier_init failed"); + + GF_FREE(voldir); + goto out; + } + + GF_FREE(voldir); + + ret = gf_asprintf(&promotion_qfile, "%s/%s/promote", + DEFAULT_VAR_RUN_DIRECTORY, this->name); + if (ret < 0) + goto out; + + ret = gf_asprintf(&demotion_qfile, "%s/%s/demote", + DEFAULT_VAR_RUN_DIRECTORY, this->name); + if (ret < 0) { + GF_FREE(promotion_qfile); + goto out; + } + + gf_msg(this->name, GF_LOG_INFO, 0, DHT_MSG_LOG_TIER_STATUS, + "Promote/demote frequency %d/%d " + "Write/Read freq thresholds %d/%d", + defrag->tier_conf.tier_promote_frequency, + defrag->tier_conf.tier_demote_frequency, + defrag->write_freq_threshold, defrag->read_freq_threshold); + + tier_save_vol_name(this); + + ret = 0; + +out: + + return ret; +} + +int +tier_cli_pause_done(int op_ret, call_frame_t *sync_frame, void *data) +{ + gf_msg("tier", GF_LOG_INFO, 0, DHT_MSG_TIER_PAUSED, + "Migrate file paused with op_ret %d", op_ret); + + return op_ret; +} + +int +tier_cli_pause(void *data) +{ + gf_defrag_info_t *defrag = NULL; + xlator_t *this = NULL; + dht_conf_t *conf = NULL; + int ret = -1; + + this = data; + + conf = this->private; + GF_VALIDATE_OR_GOTO(this->name, conf, exit); + + defrag = conf->defrag; + GF_VALIDATE_OR_GOTO(this->name, defrag, exit); + + gf_defrag_pause_tier(this, defrag); + + ret = 0; +exit: + return ret; +} + +int +tier_reconfigure(xlator_t *this, dict_t *options) +{ + dht_conf_t *conf = NULL; + gf_defrag_info_t *defrag = NULL; + char *mode = NULL; + int migrate_mb = 0; + gf_boolean_t req_pause = _gf_false; + int ret = 0; + call_frame_t *frame = NULL; + gf_boolean_t last_compact_setting = _gf_false; + + conf = this->private; + + if (conf->defrag) { + defrag = conf->defrag; + GF_OPTION_RECONF("tier-max-promote-file-size", + defrag->tier_conf.tier_max_promote_size, options, + int32, out); + + GF_OPTION_RECONF("tier-promote-frequency", + defrag->tier_conf.tier_promote_frequency, options, + int32, out); + + GF_OPTION_RECONF("tier-demote-frequency", + defrag->tier_conf.tier_demote_frequency, options, + int32, out); + + GF_OPTION_RECONF("write-freq-threshold", defrag->write_freq_threshold, + options, int32, out); + + GF_OPTION_RECONF("read-freq-threshold", defrag->read_freq_threshold, + options, int32, out); + + GF_OPTION_RECONF("watermark-hi", defrag->tier_conf.watermark_hi, + options, int32, out); + + GF_OPTION_RECONF("watermark-low", defrag->tier_conf.watermark_low, + options, int32, out); + + last_compact_setting = defrag->tier_conf.compact_active; + + GF_OPTION_RECONF("tier-compact", defrag->tier_conf.compact_active, + options, bool, out); + + if (last_compact_setting != defrag->tier_conf.compact_active) { + defrag->tier_conf.compact_mode_switched_hot = _gf_true; + defrag->tier_conf.compact_mode_switched_cold = _gf_true; + gf_msg(this->name, GF_LOG_INFO, 0, DHT_MSG_LOG_TIER_STATUS, + "compact mode switched"); + } + + GF_OPTION_RECONF("tier-hot-compact-frequency", + defrag->tier_conf.tier_compact_hot_frequency, options, + int32, out); + + GF_OPTION_RECONF("tier-cold-compact-frequency", + defrag->tier_conf.tier_compact_cold_frequency, options, + int32, out); + + GF_OPTION_RECONF("tier-mode", mode, options, str, out); + defrag->tier_conf.mode = tier_validate_mode(mode); + + GF_OPTION_RECONF("tier-max-mb", migrate_mb, options, int32, out); + defrag->tier_conf.max_migrate_bytes = (uint64_t)migrate_mb * 1024 * + 1024; + + GF_OPTION_RECONF("tier-max-files", defrag->tier_conf.max_migrate_files, + options, int32, out); + + GF_OPTION_RECONF("tier-query-limit", defrag->tier_conf.query_limit, + options, int32, out); + + GF_OPTION_RECONF("tier-pause", req_pause, options, bool, out); + + if (req_pause == _gf_true) { + frame = create_frame(this, this->ctx->pool); + if (!frame) + goto out; + + frame->root->pid = GF_CLIENT_PID_DEFRAG; + + ret = synctask_new(this->ctx->env, tier_cli_pause, + tier_cli_pause_done, frame, this); + + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "pause tier failed on reconfigure"); + } + } else { + ret = gf_defrag_resume_tier(this, defrag); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, DHT_MSG_LOG_TIER_ERROR, + "resume tier failed on reconfigure"); + } + } + } + +out: + return dht_reconfigure(this, options); +} + +void +tier_fini(xlator_t *this) +{ + if (libhandle) + dlclose(libhandle); + + GF_FREE(demotion_qfile); + GF_FREE(promotion_qfile); + + dht_fini(this); +} + +struct xlator_fops fops = { + + .lookup = dht_lookup, + .create = tier_create, + .mknod = dht_mknod, + + .open = dht_open, + .statfs = tier_statfs, + .opendir = dht_opendir, + .readdir = tier_readdir, + .readdirp = tier_readdirp, + .fsyncdir = dht_fsyncdir, + .symlink = dht_symlink, + .unlink = tier_unlink, + .link = tier_link, + .mkdir = dht_mkdir, + .rmdir = dht_rmdir, + .rename = dht_rename, + .entrylk = dht_entrylk, + .fentrylk = dht_fentrylk, + + /* Inode read operations */ + .stat = dht_stat, + .fstat = dht_fstat, + .access = dht_access, + .readlink = dht_readlink, + .getxattr = dht_getxattr, + .fgetxattr = dht_fgetxattr, + .readv = dht_readv, + .flush = dht_flush, + .fsync = dht_fsync, + .inodelk = dht_inodelk, + .finodelk = dht_finodelk, + .lk = dht_lk, + + /* Inode write operations */ + .fremovexattr = dht_fremovexattr, + .removexattr = dht_removexattr, + .setxattr = dht_setxattr, + .fsetxattr = dht_fsetxattr, + .truncate = dht_truncate, + .ftruncate = dht_ftruncate, + .writev = dht_writev, + .xattrop = dht_xattrop, + .fxattrop = dht_fxattrop, + .setattr = dht_setattr, + .fsetattr = dht_fsetattr, + .fallocate = dht_fallocate, + .discard = dht_discard, + .zerofill = dht_zerofill, +}; + +struct xlator_cbks cbks = {.release = dht_release, .forget = dht_forget}; + +extern int32_t +mem_acct_init(xlator_t *this); + +extern struct volume_options dht_options[]; + +xlator_api_t xlator_api = { + .init = tier_init, + .fini = tier_fini, + .notify = dht_notify, + .reconfigure = tier_reconfigure, + .mem_acct_init = mem_acct_init, + .op_version = {GD_OP_VERSION_3_7_0}, /* Present from the initial version */ + .fops = &fops, + .cbks = &cbks, + .options = dht_options, + .identifier = "tier", + .category = GF_MAINTAINED, +}; + diff --git a/xlators/cluster/dht/src/tier.h b/xlators/cluster/dht/src/tier.h new file mode 100644 index 0000000..a20b1db --- /dev/null +++ b/xlators/cluster/dht/src/tier.h @@ -0,0 +1,110 @@ +/* + Copyright (c) 2015 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#ifndef _TIER_H_ +#define _TIER_H_ + +/******************************************************************************/ +/* This is from dht-rebalancer.c as we don't have dht-rebalancer.h */ +#include "dht-common.h" +#include +#include +#include +#include + +/* + * Size of timer wheel. We would not promote or demote less + * frequently than this number. + */ +#define TIMER_SECS 3600 + +#include "gfdb_data_store.h" +#include +#include + +#define PROMOTION_QFILE "promotequeryfile" +#define DEMOTION_QFILE "demotequeryfile" + +#define TIER_HASHED_SUBVOL conf->subvolumes[0] +#define TIER_UNHASHED_SUBVOL conf->subvolumes[1] + +#define GET_QFILE_PATH(is_promotion) \ + (is_promotion) ? promotion_qfile : demotion_qfile + +typedef struct tier_qfile_array { + int *fd_array; + ssize_t array_size; + ssize_t next_index; + /* Indicate the number of exhuasted FDs*/ + ssize_t exhausted_count; +} tier_qfile_array_t; + +typedef struct _query_cbk_args { + xlator_t *this; + gf_defrag_info_t *defrag; + /* This is write */ + int query_fd; + int is_promotion; + int is_compaction; + /* This is for read */ + tier_qfile_array_t *qfile_array; +} query_cbk_args_t; + +int +gf_run_tier(xlator_t *this, gf_defrag_info_t *defrag); + +typedef struct gfdb_brick_info { + gfdb_time_t *time_stamp; + gf_boolean_t _gfdb_promote; + query_cbk_args_t *_query_cbk_args; +} gfdb_brick_info_t; + +typedef struct brick_list { + xlator_t *xlator; + char *brick_db_path; + char brick_name[NAME_MAX]; + char qfile_path[PATH_MAX]; + struct list_head list; +} tier_brick_list_t; + +typedef struct _dm_thread_args { + xlator_t *this; + gf_defrag_info_t *defrag; + struct list_head *brick_list; + int freq_time; + int return_value; + int is_promotion; + int is_compaction; + gf_boolean_t is_hot_tier; +} migration_args_t; + +typedef enum tier_watermark_op_ { + TIER_WM_NONE = 0, + TIER_WM_LOW, + TIER_WM_HI, + TIER_WM_MID +} tier_watermark_op_t; + +#define DEFAULT_PROMOTE_FREQ_SEC 120 +#define DEFAULT_DEMOTE_FREQ_SEC 120 +#define DEFAULT_HOT_COMPACT_FREQ_SEC 604800 +#define DEFAULT_COLD_COMPACT_FREQ_SEC 604800 +#define DEFAULT_DEMOTE_DEGRADED 1 +#define DEFAULT_WRITE_FREQ_SEC 0 +#define DEFAULT_READ_FREQ_SEC 0 +#define DEFAULT_WM_LOW 75 +#define DEFAULT_WM_HI 90 +#define DEFAULT_TIER_MODE TIER_MODE_TEST +#define DEFAULT_COMP_MODE _gf_true +#define DEFAULT_TIER_MAX_MIGRATE_MB 1000 +#define DEFAULT_TIER_MAX_MIGRATE_FILES 5000 +#define DEFAULT_TIER_QUERY_LIMIT 100 + +#endif diff --git a/xlators/features/Makefile.am b/xlators/features/Makefile.am index 194634b..545c02b 100644 --- a/xlators/features/Makefile.am +++ b/xlators/features/Makefile.am @@ -5,6 +5,6 @@ endif SUBDIRS = locks quota read-only quiesce marker index barrier arbiter upcall \ compress changelog gfid-access snapview-client snapview-server trash \ shard bit-rot leases selinux sdfs namespace $(CLOUDSYNC_DIR) thin-arbiter \ - utime + utime changetimerecorder CLEANFILES = diff --git a/xlators/features/changetimerecorder/Makefile.am b/xlators/features/changetimerecorder/Makefile.am new file mode 100644 index 0000000..a985f42 --- /dev/null +++ b/xlators/features/changetimerecorder/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = src + +CLEANFILES = diff --git a/xlators/features/changetimerecorder/src/Makefile.am b/xlators/features/changetimerecorder/src/Makefile.am new file mode 100644 index 0000000..620017e --- /dev/null +++ b/xlators/features/changetimerecorder/src/Makefile.am @@ -0,0 +1,26 @@ +xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/features + +# changetimerecorder can only get build when libgfdb is enabled +if BUILD_GFDB + xlator_LTLIBRARIES = changetimerecorder.la +endif + +changetimerecorder_la_LDFLAGS = -module $(GF_XLATOR_DEFAULT_LDFLAGS) + +changetimerecorder_la_SOURCES = changetimerecorder.c \ + ctr-helper.c ctr-xlator-ctx.c + +changetimerecorder_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la\ + $(top_builddir)/libglusterfs/src/gfdb/libgfdb.la + +noinst_HEADERS = ctr-messages.h changetimerecorder.h ctr_mem_types.h \ + ctr-helper.h ctr-xlator-ctx.h + +AM_CPPFLAGS = $(GF_CPPFLAGS) -I$(top_srcdir)/libglusterfs/src \ + -I$(top_srcdir)/libglusterfs/src/gfdb \ + -I$(top_srcdir)/rpc/xdr/src -I$(top_builddir)/rpc/xdr/src \ + -DDATADIR=\"$(localstatedir)\" + +AM_CFLAGS = -Wall $(GF_CFLAGS) $(SQLITE_CFLAGS) + +CLEANFILES = diff --git a/xlators/features/changetimerecorder/src/changetimerecorder.c b/xlators/features/changetimerecorder/src/changetimerecorder.c new file mode 100644 index 0000000..f2aa4a9 --- /dev/null +++ b/xlators/features/changetimerecorder/src/changetimerecorder.c @@ -0,0 +1,2371 @@ +/* + Copyright (c) 2015 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ +#include +#include + +#include "gfdb_sqlite3.h" +#include "ctr-helper.h" +#include "ctr-messages.h" +#include + +#include "changetimerecorder.h" +#include "tier-ctr-interface.h" + +/*******************************inode forget***********************************/ +int +ctr_forget(xlator_t *this, inode_t *inode) +{ + fini_ctr_xlator_ctx(this, inode); + return 0; +} + +/************************** Look up heal **************************************/ +/* +Problem: The CTR xlator records file meta (heat/hardlinks) +into the data. This works fine for files which are created +after ctr xlator is switched ON. But for files which were +created before CTR xlator is ON, CTR xlator is not able to +record either of the meta i.e heat or hardlinks. Thus making +those files immune to promotions/demotions. + +Solution: The solution that is implemented in this patch is +do ctr-db heal of all those pre-existent files, using named lookup. +For this purpose we use the inode-xlator context variable option +in gluster. +The inode-xlator context variable for ctr xlator will have the +following, + a. A Lock for the context variable + b. A hardlink list: This list represents the successful looked + up hardlinks. +These are the scenarios when the hardlink list is updated: +1) Named-Lookup: Whenever a named lookup happens on a file, in the + wind path we copy all required hardlink and inode information to + ctr_db_record structure, which resides in the frame->local variable. + We don't update the database in wind. During the unwind, we read the + information from the ctr_db_record and , + Check if the inode context variable is created, if not we create it. + Check if the hard link is there in the hardlink list. + If its not there we add it to the list and send a update to the + database using libgfdb. + Please note: The database transaction can fail(and we ignore) as there + already might be a record in the db. This update to the db is to heal + if its not there. + If its there in the list we ignore it. +2) Inode Forget: Whenever an inode forget hits we clear the hardlink list in + the inode context variable and delete the inode context variable. + Please note: An inode forget may happen for two reason, + a. when the inode is delete. + b. the in-memory inode is evicted from the inode table due to cache limits. +3) create: whenever a create happens we create the inode context variable and + add the hardlink. The database updation is done as usual by ctr. +4) link: whenever a hardlink is created for the inode, we create the inode + context variable, if not present, and add the hardlink to the list. +5) unlink: whenever a unlink happens we delete the hardlink from the list. +6) mknod: same as create. +7) rename: whenever a rename happens we update the hardlink in list. if the + hardlink was not present for updation, we add the hardlink to the list. + +What is pending: +1) This solution will only work for named lookups. +2) We don't track afr-self-heal/dht-rebalancer traffic for healing. + +*/ + +/* This function does not write anything to the db, + * just created the local variable + * for the frame and sets values for the ctr_db_record */ +static int +ctr_lookup_wind(call_frame_t *frame, xlator_t *this, + gf_ctr_inode_context_t *ctr_inode_cx) +{ + int ret = -1; + gf_ctr_private_t *_priv = NULL; + gf_ctr_local_t *ctr_local = NULL; + + GF_ASSERT(frame); + GF_ASSERT(frame->root); + GF_ASSERT(this); + IS_CTR_INODE_CX_SANE(ctr_inode_cx); + + _priv = this->private; + GF_ASSERT(_priv); + + if (_priv->ctr_record_wind && ctr_inode_cx->ia_type != IA_IFDIR) { + frame->local = init_ctr_local_t(this); + if (!frame->local) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_CREATE_CTR_LOCAL_ERROR_WIND, + "WIND: Error while creating ctr local"); + goto out; + }; + ctr_local = frame->local; + /*Definitely no internal fops will reach here*/ + ctr_local->is_internal_fop = _gf_false; + /*Don't record counters*/ + CTR_DB_REC(ctr_local).do_record_counters = _gf_false; + /*Don't record time at all*/ + CTR_DB_REC(ctr_local).do_record_times = _gf_false; + + /* Copy gfid into db record*/ + gf_uuid_copy(CTR_DB_REC(ctr_local).gfid, *(ctr_inode_cx->gfid)); + + /* Set fop_path and fop_type, required by libgfdb to make + * decision while inserting the record */ + CTR_DB_REC(ctr_local).gfdb_fop_path = ctr_inode_cx->fop_path; + CTR_DB_REC(ctr_local).gfdb_fop_type = ctr_inode_cx->fop_type; + + /* Copy hard link info*/ + gf_uuid_copy(CTR_DB_REC(ctr_local).pargfid, + *((NEW_LINK_CX(ctr_inode_cx))->pargfid)); + if (snprintf(CTR_DB_REC(ctr_local).file_name, + sizeof(CTR_DB_REC(ctr_local).file_name), "%s", + NEW_LINK_CX(ctr_inode_cx)->basename) >= + sizeof(CTR_DB_REC(ctr_local).file_name)) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_CREATE_CTR_LOCAL_ERROR_WIND, + "WIND: Error copying filename of ctr local"); + goto out; + } + /* Since we are in lookup we can ignore errors while + * Inserting in the DB, because there may be many + * to write to the DB attempts for healing. + * We don't want to log all failed attempts and + * bloat the log*/ + ctr_local->gfdb_db_record.ignore_errors = _gf_true; + } + + ret = 0; + +out: + + if (ret) { + free_ctr_local(ctr_local); + frame->local = NULL; + } + + return ret; +} + +/* This function inserts the ctr_db_record populated by ctr_lookup_wind + * in to the db. It also destroys the frame->local created by ctr_lookup_wind */ +static int +ctr_lookup_unwind(call_frame_t *frame, xlator_t *this) +{ + int ret = -1; + gf_ctr_private_t *_priv = NULL; + gf_ctr_local_t *ctr_local = NULL; + + GF_ASSERT(frame); + GF_ASSERT(this); + + _priv = this->private; + GF_ASSERT(_priv); + + GF_ASSERT(_priv->_db_conn); + + ctr_local = frame->local; + + if (ctr_local && (ctr_local->ia_inode_type != IA_IFDIR)) { + ret = insert_record(_priv->_db_conn, &ctr_local->gfdb_db_record); + if (ret == -1) { + gf_msg(this->name, + _gfdb_log_level(GF_LOG_ERROR, + ctr_local->gfdb_db_record.ignore_errors), + 0, CTR_MSG_FILL_CTR_LOCAL_ERROR_UNWIND, + "UNWIND: Error filling ctr local"); + goto out; + } + } + ret = 0; +out: + free_ctr_local(ctr_local); + frame->local = NULL; + return ret; +} + +/****************************************************************************** + * + * FOPS HANDLING BELOW + * + * ***************************************************************************/ + +/****************************LOOKUP********************************************/ + +int32_t +ctr_lookup_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *buf, dict_t *dict, struct iatt *postparent) +{ + int ret = -1; + ctr_xlator_ctx_t *ctr_xlator_ctx = NULL; + gf_ctr_local_t *ctr_local = NULL; + ctr_heal_ret_val_t ret_val = CTR_CTX_ERROR; + gf_boolean_t _is_heal_needed = _gf_false; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + + /* if the lookup failed lookup don't do anything*/ + if (op_ret == -1) { + gf_msg_trace(this->name, 0, "lookup failed with %s", + strerror(op_errno)); + goto out; + } + + /* Ignore directory lookups */ + if (inode->ia_type == IA_IFDIR) { + goto out; + } + + /* if frame local was not set by the ctr_lookup() + * so don't so anything*/ + if (!frame->local) { + goto out; + } + + /* if the lookup is for dht link donot record*/ + if (dht_is_linkfile(buf, dict)) { + gf_msg_trace(this->name, 0, + "Ignoring Lookup " + "for dht link file"); + goto out; + } + + ctr_local = frame->local; + /*Assign the proper inode type*/ + ctr_local->ia_inode_type = inode->ia_type; + + /* Copy gfid directly from inode */ + gf_uuid_copy(CTR_DB_REC(ctr_local).gfid, inode->gfid); + + /* Checking if gfid and parent gfid is valid */ + if (gf_uuid_is_null(CTR_DB_REC(ctr_local).gfid) || + gf_uuid_is_null(CTR_DB_REC(ctr_local).pargfid)) { + gf_msg_trace(this->name, 0, "Invalid GFID"); + goto out; + } + + /* if its a first entry + * then mark the ctr_record for create + * A create will attempt a file and a hard link created in the db*/ + ctr_xlator_ctx = get_ctr_xlator_ctx(this, inode); + if (!ctr_xlator_ctx) { + /* This marks inode heal */ + CTR_DB_REC(ctr_local).gfdb_fop_type = GFDB_FOP_CREATE_WRITE; + _is_heal_needed = _gf_true; + } + + /* Copy the correct gfid from resolved inode */ + gf_uuid_copy(CTR_DB_REC(ctr_local).gfid, inode->gfid); + + /* Add hard link to the list */ + ret_val = add_hard_link_ctx(frame, this, inode); + if (ret_val == CTR_CTX_ERROR) { + gf_msg_trace(this->name, 0, "Failed adding hardlink to list"); + goto out; + } + /* If inode needs healing then heal the hardlink also */ + else if (ret_val & CTR_TRY_INODE_HEAL) { + /* This marks inode heal */ + CTR_DB_REC(ctr_local).gfdb_fop_type = GFDB_FOP_CREATE_WRITE; + _is_heal_needed = _gf_true; + } + /* If hardlink needs healing */ + else if (ret_val & CTR_TRY_HARDLINK_HEAL) { + _is_heal_needed = _gf_true; + } + + /* If lookup heal needed */ + if (!_is_heal_needed) + goto out; + + /* FINALLY HEAL : Inserts the ctr_db_record populated by ctr_lookup_wind + * in to the db. It also destroys the frame->local + * created by ctr_lookup_wind */ + ret = ctr_lookup_unwind(frame, this); + if (ret) { + gf_msg_trace(this->name, 0, "Failed healing/inserting link"); + } + +out: + free_ctr_local((gf_ctr_local_t *)frame->local); + frame->local = NULL; + + STACK_UNWIND_STRICT(lookup, frame, op_ret, op_errno, inode, buf, dict, + postparent); + + return 0; +} + +int32_t +ctr_lookup(call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xdata) +{ + gf_ctr_inode_context_t ctr_inode_cx; + gf_ctr_inode_context_t *_inode_cx = &ctr_inode_cx; + gf_ctr_link_context_t ctr_link_cx; + gf_ctr_link_context_t *_link_cx = &ctr_link_cx; + int ret = -1; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_INTERNAL_FOP_THEN_GOTO(frame, xdata, out); + + GF_ASSERT(frame); + GF_ASSERT(frame->root); + + /* Don't handle nameless lookups*/ + if (!loc->parent || !loc->name) + goto out; + + /*fill ctr link context*/ + FILL_CTR_LINK_CX(_link_cx, loc->parent->gfid, loc->name, out); + + /* Fill ctr inode context*/ + /* IA_IFREG : We assume its a file in the wind + * but in the unwind we are sure what the inode is a file + * or directory + * gfid: we are just filling loc->gfid which is not correct. + * In unwind we fill the correct gfid for successful lookup*/ + FILL_CTR_INODE_CONTEXT(_inode_cx, IA_IFREG, loc->gfid, _link_cx, NULL, + GFDB_FOP_DENTRY_WRITE, GFDB_FOP_WIND); + + /* Create the frame->local and populate ctr_db_record + * No writing to the db yet */ + ret = ctr_lookup_wind(frame, this, _inode_cx); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_LINK_WIND_FAILED, + "Failed to insert link wind"); + } + +out: + STACK_WIND(frame, ctr_lookup_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->lookup, loc, xdata); + return 0; +} + +/****************************WRITEV********************************************/ +int32_t +ctr_writev_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *prebuf, + struct iatt *postbuf, dict_t *xdata) +{ + int ret = -1; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_FOP_FAILED_THEN_GOTO(this, op_ret, op_errno, out); + + ret = ctr_insert_unwind(frame, this, GFDB_FOP_INODE_WRITE, GFDB_FOP_UNWIND); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_WRITEV_UNWIND_FAILED, + "Failed to insert writev unwind"); + } + +out: + ctr_free_frame_local(frame); + + STACK_UNWIND_STRICT(writev, frame, op_ret, op_errno, prebuf, postbuf, + xdata); + + return 0; +} + +int32_t +ctr_writev(call_frame_t *frame, xlator_t *this, fd_t *fd, struct iovec *vector, + int32_t count, off_t off, uint32_t flags, struct iobref *iobref, + dict_t *xdata) +{ + int ret = -1; + gf_ctr_inode_context_t ctr_inode_cx; + gf_ctr_inode_context_t *_inode_cx = &ctr_inode_cx; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_INTERNAL_FOP_THEN_GOTO(frame, xdata, out); + + /*Fill ctr inode context*/ + FILL_CTR_INODE_CONTEXT(_inode_cx, fd->inode->ia_type, fd->inode->gfid, NULL, + NULL, GFDB_FOP_INODE_WRITE, GFDB_FOP_WIND); + + /*record into the database*/ + ret = ctr_insert_wind(frame, this, _inode_cx); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_WRITEV_WIND_FAILED, + "Failed to insert writev wind"); + } + +out: + STACK_WIND(frame, ctr_writev_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->writev, fd, vector, count, off, flags, + iobref, xdata); + + return 0; +} + +/******************************setattr*****************************************/ + +int32_t +ctr_setattr_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *preop_stbuf, + struct iatt *postop_stbuf, dict_t *xdata) +{ + int ret = -1; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_FOP_FAILED_THEN_GOTO(this, op_ret, op_errno, out); + + ret = ctr_insert_unwind(frame, this, GFDB_FOP_INODE_WRITE, GFDB_FOP_UNWIND); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_INSERT_SETATTR_UNWIND_FAILED, + "Failed to insert setattr unwind"); + } + +out: + ctr_free_frame_local(frame); + + STACK_UNWIND_STRICT(setattr, frame, op_ret, op_errno, preop_stbuf, + postop_stbuf, xdata); + + return 0; +} + +int32_t +ctr_setattr(call_frame_t *frame, xlator_t *this, loc_t *loc, struct iatt *stbuf, + int32_t valid, dict_t *xdata) +{ + int ret = -1; + gf_ctr_inode_context_t ctr_inode_cx; + gf_ctr_inode_context_t *_inode_cx = &ctr_inode_cx; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_INTERNAL_FOP_THEN_GOTO(frame, xdata, out); + CTR_RECORD_METADATA_HEAT_IS_DISABLED_THEN_GOTO(this, out); + + /*Fill ctr inode context*/ + FILL_CTR_INODE_CONTEXT(_inode_cx, loc->inode->ia_type, loc->inode->gfid, + NULL, NULL, GFDB_FOP_INODE_WRITE, GFDB_FOP_WIND); + + /*record into the database*/ + ret = ctr_insert_wind(frame, this, _inode_cx); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_SETATTR_WIND_FAILED, + "Failed to insert setattr wind"); + } +out: + + STACK_WIND(frame, ctr_setattr_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->setattr, loc, stbuf, valid, xdata); + + return 0; +} + +/*************************** fsetattr ***************************************/ +int32_t +ctr_fsetattr_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *preop_stbuf, + struct iatt *postop_stbuf, dict_t *xdata) +{ + int ret = -1; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_FOP_FAILED_THEN_GOTO(this, op_ret, op_errno, out); + + ret = ctr_insert_unwind(frame, this, GFDB_FOP_INODE_WRITE, GFDB_FOP_UNWIND); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_INSERT_SETATTR_UNWIND_FAILED, + "Failed to insert fsetattr unwind"); + } + +out: + ctr_free_frame_local(frame); + + STACK_UNWIND_STRICT(fsetattr, frame, op_ret, op_errno, preop_stbuf, + postop_stbuf, xdata); + + return 0; +} + +int32_t +ctr_fsetattr(call_frame_t *frame, xlator_t *this, fd_t *fd, struct iatt *stbuf, + int32_t valid, dict_t *xdata) +{ + int ret = -1; + gf_ctr_inode_context_t ctr_inode_cx; + gf_ctr_inode_context_t *_inode_cx = &ctr_inode_cx; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_INTERNAL_FOP_THEN_GOTO(frame, xdata, out); + CTR_RECORD_METADATA_HEAT_IS_DISABLED_THEN_GOTO(this, out); + + /*Fill ctr inode context*/ + FILL_CTR_INODE_CONTEXT(_inode_cx, fd->inode->ia_type, fd->inode->gfid, NULL, + NULL, GFDB_FOP_INODE_WRITE, GFDB_FOP_WIND); + + /*record into the database*/ + ret = ctr_insert_wind(frame, this, _inode_cx); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_SETATTR_WIND_FAILED, + "Failed to insert fsetattr wind"); + } +out: + STACK_WIND(frame, ctr_fsetattr_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->fsetattr, fd, stbuf, valid, xdata); + + return 0; +} +/****************************fremovexattr************************************/ + +int32_t +ctr_fremovexattr_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, dict_t *xdata) +{ + int ret = -1; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_FOP_FAILED_THEN_GOTO(this, op_ret, op_errno, out); + + ret = ctr_insert_unwind(frame, this, GFDB_FOP_INODE_WRITE, GFDB_FOP_UNWIND); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_INSERT_FREMOVEXATTR_UNWIND_FAILED, + "Failed to insert fremovexattr unwind"); + } + +out: + ctr_free_frame_local(frame); + + STACK_UNWIND_STRICT(fremovexattr, frame, op_ret, op_errno, xdata); + + return 0; +} + +int32_t +ctr_fremovexattr(call_frame_t *frame, xlator_t *this, fd_t *fd, + const char *name, dict_t *xdata) +{ + int ret = -1; + gf_ctr_inode_context_t ctr_inode_cx; + gf_ctr_inode_context_t *_inode_cx = &ctr_inode_cx; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_INTERNAL_FOP_THEN_GOTO(frame, xdata, out); + CTR_RECORD_METADATA_HEAT_IS_DISABLED_THEN_GOTO(this, out); + + /*Fill ctr inode context*/ + FILL_CTR_INODE_CONTEXT(_inode_cx, fd->inode->ia_type, fd->inode->gfid, NULL, + NULL, GFDB_FOP_INODE_WRITE, GFDB_FOP_WIND); + + /*record into the database*/ + ret = ctr_insert_wind(frame, this, _inode_cx); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_INSERT_FREMOVEXATTR_WIND_FAILED, + "Failed to insert fremovexattr wind"); + } + +out: + STACK_WIND(frame, ctr_fremovexattr_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->fremovexattr, fd, name, xdata); + return 0; +} + +/****************************removexattr*************************************/ + +int32_t +ctr_removexattr_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, dict_t *xdata) +{ + int ret = -1; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_FOP_FAILED_THEN_GOTO(this, op_ret, op_errno, out); + CTR_IF_INTERNAL_FOP_THEN_GOTO(frame, xdata, out); + + ret = ctr_insert_unwind(frame, this, GFDB_FOP_INODE_WRITE, GFDB_FOP_UNWIND); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_INSERT_REMOVEXATTR_UNWIND_FAILED, + "Failed to insert removexattr unwind"); + } + +out: + ctr_free_frame_local(frame); + + STACK_UNWIND_STRICT(removexattr, frame, op_ret, op_errno, xdata); + + return 0; +} + +int32_t +ctr_removexattr(call_frame_t *frame, xlator_t *this, loc_t *loc, + const char *name, dict_t *xdata) +{ + int ret = -1; + gf_ctr_inode_context_t ctr_inode_cx; + gf_ctr_inode_context_t *_inode_cx = &ctr_inode_cx; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_INTERNAL_FOP_THEN_GOTO(frame, xdata, out); + CTR_RECORD_METADATA_HEAT_IS_DISABLED_THEN_GOTO(this, out); + + /*Fill ctr inode context*/ + FILL_CTR_INODE_CONTEXT(_inode_cx, loc->inode->ia_type, loc->inode->gfid, + NULL, NULL, GFDB_FOP_INODE_WRITE, GFDB_FOP_WIND); + + /*record into the database*/ + ret = ctr_insert_wind(frame, this, _inode_cx); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_INSERT_REMOVEXATTR_WIND_FAILED, + "Failed to insert removexattr wind"); + } + +out: + STACK_WIND(frame, ctr_removexattr_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->removexattr, loc, name, xdata); + return 0; +} + +/****************************truncate****************************************/ + +int32_t +ctr_truncate_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *prebuf, + struct iatt *postbuf, dict_t *xdata) +{ + int ret = -1; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_FOP_FAILED_THEN_GOTO(this, op_ret, op_errno, out); + + ret = ctr_insert_unwind(frame, this, GFDB_FOP_INODE_WRITE, GFDB_FOP_UNWIND); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_INSERT_TRUNCATE_UNWIND_FAILED, + "Failed to insert truncate unwind"); + } + +out: + ctr_free_frame_local(frame); + + STACK_UNWIND_STRICT(truncate, frame, op_ret, op_errno, prebuf, postbuf, + xdata); + + return 0; +} + +int32_t +ctr_truncate(call_frame_t *frame, xlator_t *this, loc_t *loc, off_t offset, + dict_t *xdata) +{ + int ret = -1; + gf_ctr_inode_context_t ctr_inode_cx; + gf_ctr_inode_context_t *_inode_cx = &ctr_inode_cx; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_INTERNAL_FOP_THEN_GOTO(frame, xdata, out); + + /*Fill ctr inode context*/ + FILL_CTR_INODE_CONTEXT(_inode_cx, loc->inode->ia_type, loc->inode->gfid, + NULL, NULL, GFDB_FOP_INODE_WRITE, GFDB_FOP_WIND); + + /*record into the database*/ + ret = ctr_insert_wind(frame, this, _inode_cx); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_TRUNCATE_WIND_FAILED, + "Failed to insert truncate wind"); + } +out: + STACK_WIND(frame, ctr_truncate_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->truncate, loc, offset, xdata); + return 0; +} + +/****************************ftruncate***************************************/ + +int32_t +ctr_ftruncate_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *prebuf, + struct iatt *postbuf, dict_t *xdata) +{ + int ret = -1; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_FOP_FAILED_THEN_GOTO(this, op_ret, op_errno, out); + + ret = ctr_insert_unwind(frame, this, GFDB_FOP_INODE_WRITE, GFDB_FOP_UNWIND); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_INSERT_FTRUNCATE_UNWIND_FAILED, + "Failed to insert ftruncate unwind"); + } + +out: + ctr_free_frame_local(frame); + + STACK_UNWIND_STRICT(ftruncate, frame, op_ret, op_errno, prebuf, postbuf, + xdata); + + return 0; +} + +int32_t +ctr_ftruncate(call_frame_t *frame, xlator_t *this, fd_t *fd, off_t offset, + dict_t *xdata) +{ + int ret = -1; + gf_ctr_inode_context_t ctr_inode_cx; + gf_ctr_inode_context_t *_inode_cx = &ctr_inode_cx; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_INTERNAL_FOP_THEN_GOTO(frame, xdata, out); + + /*Fill ctr inode context*/ + FILL_CTR_INODE_CONTEXT(_inode_cx, fd->inode->ia_type, fd->inode->gfid, NULL, + NULL, GFDB_FOP_INODE_WRITE, GFDB_FOP_WIND); + + /*record into the database*/ + ret = ctr_insert_wind(frame, this, _inode_cx); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_INSERT_FTRUNCATE_WIND_FAILED, + "Failed to insert ftruncate wind"); + } + +out: + STACK_WIND(frame, ctr_ftruncate_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->ftruncate, fd, offset, xdata); + return 0; +} + +/****************************rename******************************************/ +int32_t +ctr_rename_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *buf, + struct iatt *preoldparent, struct iatt *postoldparent, + struct iatt *prenewparent, struct iatt *postnewparent, + dict_t *xdata) +{ + int ret = -1; + uint32_t remaining_links = -1; + gf_ctr_local_t *ctr_local = NULL; + gfdb_fop_type_t fop_type = GFDB_FOP_INVALID_OP; + gfdb_fop_path_t fop_path = GFDB_FOP_INVALID; + + GF_ASSERT(frame); + GF_ASSERT(this); + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_FOP_FAILED_THEN_GOTO(this, op_ret, op_errno, out); + + ret = ctr_insert_unwind(frame, this, GFDB_FOP_DENTRY_WRITE, + GFDB_FOP_UNWIND); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_RENAME_UNWIND_FAILED, + "Failed to insert rename unwind"); + goto out; + } + + if (!xdata) + goto out; + /* + * + * Extracting GF_RESPONSE_LINK_COUNT_XDATA from POSIX Xlator + * This is only set when we are overwriting hardlinks. + * + * */ + ret = dict_get_uint32(xdata, GF_RESPONSE_LINK_COUNT_XDATA, + &remaining_links); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_GET_CTR_RESPONSE_LINK_COUNT_XDATA_FAILED, + "Failed to getting GF_RESPONSE_LINK_COUNT_XDATA"); + remaining_links = -1; + goto out; + } + + ctr_local = frame->local; + if (!ctr_local) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_NULL_LOCAL, + "ctr_local is NULL."); + goto out; + } + + /* This is not the only link */ + if (remaining_links > 1) { + fop_type = GFDB_FOP_DENTRY_WRITE; + fop_path = GFDB_FOP_UNDEL; + } + /* Last link that was deleted */ + else if (remaining_links == 1) { + fop_type = GFDB_FOP_DENTRY_WRITE; + fop_path = GFDB_FOP_UNDEL_ALL; + } else { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_RENAME_UNWIND_FAILED, + "Invalid link count from posix"); + goto out; + } + + ret = ctr_delete_hard_link_from_db( + this, CTR_DB_REC(ctr_local).old_gfid, CTR_DB_REC(ctr_local).pargfid, + CTR_DB_REC(ctr_local).file_name, fop_type, fop_path); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_UNLINK_UNWIND_FAILED, + "Failed to delete records of %s", + CTR_DB_REC(ctr_local).old_file_name); + } + +out: + ctr_free_frame_local(frame); + + STACK_UNWIND_STRICT(rename, frame, op_ret, op_errno, buf, preoldparent, + postoldparent, prenewparent, postnewparent, xdata); + + return 0; +} + +int32_t +ctr_rename(call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc, + dict_t *xdata) +{ + int ret = -1; + gf_ctr_inode_context_t ctr_inode_cx; + gf_ctr_inode_context_t *_inode_cx = &ctr_inode_cx; + gf_ctr_link_context_t new_link_cx, old_link_cx; + gf_ctr_link_context_t *_nlink_cx = &new_link_cx; + gf_ctr_link_context_t *_olink_cx = &old_link_cx; + int is_dict_created = 0; + ctr_xlator_ctx_t *ctr_xlator_ctx = NULL; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_INTERNAL_FOP_THEN_GOTO(frame, xdata, out); + + /*Fill old link context*/ + FILL_CTR_LINK_CX(_olink_cx, oldloc->pargfid, oldloc->name, out); + + /*Fill new link context*/ + FILL_CTR_LINK_CX(_nlink_cx, newloc->pargfid, newloc->name, out); + + /*Fill ctr inode context*/ + FILL_CTR_INODE_CONTEXT(_inode_cx, oldloc->inode->ia_type, + oldloc->inode->gfid, _nlink_cx, _olink_cx, + GFDB_FOP_DENTRY_WRITE, GFDB_FOP_WIND); + + /* If the rename is a overwrite of hardlink + * rename ("file1", "file2") + * file1 is hardlink for gfid say 00000000-0000-0000-0000-00000000000A + * file2 is hardlink for gfid say 00000000-0000-0000-0000-00000000000B + * so we are saving file2 gfid in old_gfid so that we delete entries + * from the db during rename callback if the fop is successful + * */ + if (newloc->inode) { + /* This is the GFID from where the newloc hardlink will be + * unlinked */ + _inode_cx->old_gfid = &newloc->inode->gfid; + } + + /* Is a metatdata fop */ + _inode_cx->is_metadata_fop = _gf_true; + + /*record into the database*/ + ret = ctr_insert_wind(frame, this, _inode_cx); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_RENAME_WIND_FAILED, + "Failed to insert rename wind"); + } else { + /* We are doing updation of hard link in inode context in wind + * As we don't get the "inode" in the call back for rename */ + ret = update_hard_link_ctx(frame, this, oldloc->inode); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_UPDATE_HARDLINK_FAILED, + "Failed " + "updating hard link in ctr inode context"); + goto out; + } + + /* If the newloc has an inode. i.e acquiring hardlink of an + * exisitng file i.e overwritting a file. + * */ + if (newloc->inode) { + /* Getting the ctr inode context variable for + * inode whose hardlink will be acquired during + * the rename + * */ + ctr_xlator_ctx = get_ctr_xlator_ctx(this, newloc->inode); + if (!ctr_xlator_ctx) { + /* Since there is no ctr inode context + * so nothing more to do */ + ret = 0; + goto out; + } + + /* Deleting hardlink from context variable */ + ret = ctr_delete_hard_link(this, ctr_xlator_ctx, newloc->pargfid, + newloc->name); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_DELETE_HARDLINK_FAILED, + "Failed to delete hard link"); + goto out; + } + + /* Requesting for number of hardlinks on the newloc + * inode from POSIX. + * */ + is_dict_created = set_posix_link_request(this, &xdata); + if (is_dict_created == -1) { + ret = -1; + goto out; + } + } + } + +out: + STACK_WIND(frame, ctr_rename_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->rename, oldloc, newloc, xdata); + + if (is_dict_created == 1) { + dict_unref(xdata); + } + + return 0; +} + +/****************************unlink******************************************/ +int32_t +ctr_unlink_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *preparent, + struct iatt *postparent, dict_t *xdata) +{ + int ret = -1; + uint32_t remaining_links = -1; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_FOP_FAILED_THEN_GOTO(this, op_ret, op_errno, out); + + if (!xdata) + goto out; + + /* + * + * Extracting GF_RESPONSE_LINK_COUNT_XDATA from POSIX Xlator + * + * */ + ret = dict_get_uint32(xdata, GF_RESPONSE_LINK_COUNT_XDATA, + &remaining_links); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_GET_CTR_RESPONSE_LINK_COUNT_XDATA_FAILED, + "Failed to getting GF_RESPONSE_LINK_COUNT_XDATA"); + remaining_links = -1; + } + + /*This is not the only link*/ + if (remaining_links != 1) { + ret = ctr_insert_unwind(frame, this, GFDB_FOP_DENTRY_WRITE, + GFDB_FOP_UNDEL); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_INSERT_UNLINK_UNWIND_FAILED, + "Failed to insert unlink unwind"); + } + } + /*Last link that was deleted*/ + else if (remaining_links == 1) { + ret = ctr_insert_unwind(frame, this, GFDB_FOP_DENTRY_WRITE, + GFDB_FOP_UNDEL_ALL); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_INSERT_UNLINK_UNWIND_FAILED, + "Failed to insert unlink unwind"); + } + } + +out: + ctr_free_frame_local(frame); + + STACK_UNWIND_STRICT(unlink, frame, op_ret, op_errno, preparent, postparent, + xdata); + + return 0; +} + +int32_t +ctr_unlink(call_frame_t *frame, xlator_t *this, loc_t *loc, int xflag, + dict_t *xdata) +{ + int ret = -1; + gf_ctr_inode_context_t ctr_inode_cx; + gf_ctr_inode_context_t *_inode_cx = &ctr_inode_cx; + gf_ctr_link_context_t ctr_link_cx; + gf_ctr_link_context_t *_link_cx = &ctr_link_cx; + gf_boolean_t is_xdata_created = _gf_false; + struct iatt dummy_stat = {0}; + + GF_ASSERT(frame); + + CTR_IS_DISABLED_THEN_GOTO(this, out); + + /*Fill link context*/ + FILL_CTR_LINK_CX(_link_cx, loc->pargfid, loc->name, out); + + /*Fill ctr inode context*/ + FILL_CTR_INODE_CONTEXT(_inode_cx, loc->inode->ia_type, loc->inode->gfid, + _link_cx, NULL, GFDB_FOP_DENTRY_WRITE, + GFDB_FOP_WDEL); + + /*Internal FOP*/ + _inode_cx->is_internal_fop = is_internal_fop(frame, xdata); + + /* Is a metadata FOP */ + _inode_cx->is_metadata_fop = _gf_true; + + /* If its a internal FOP and dht link file donot record*/ + if (_inode_cx->is_internal_fop && dht_is_linkfile(&dummy_stat, xdata)) { + goto out; + } + + /*record into the database*/ + ret = ctr_insert_wind(frame, this, _inode_cx); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_UNLINK_UNWIND_FAILED, + "Failed to insert unlink wind"); + } else { + /* We are doing delete of hard link in inode context in wind + * As we don't get the "inode" in the call back for rename */ + ret = delete_hard_link_ctx(frame, this, loc->inode); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_DELETE_HARDLINK_FAILED, + "Failed " + "deleting hard link from ctr inode context"); + } + } + + /* + * + * Sending GF_REQUEST_LINK_COUNT_XDATA + * to POSIX Xlator to send link count in unwind path + * + * */ + /*create xdata if NULL*/ + if (!xdata) { + xdata = dict_new(); + is_xdata_created = (xdata) ? _gf_true : _gf_false; + } + if (!xdata) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_XDATA_NULL, + "xdata is NULL :Cannot send " + "GF_REQUEST_LINK_COUNT_XDATA to posix"); + goto out; + } + + ret = dict_set_int32(xdata, GF_REQUEST_LINK_COUNT_XDATA, 1); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_SET_CTR_RESPONSE_LINK_COUNT_XDATA_FAILED, + "Failed setting GF_REQUEST_LINK_COUNT_XDATA"); + if (is_xdata_created) { + dict_unref(xdata); + } + goto out; + } + +out: + STACK_WIND(frame, ctr_unlink_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->unlink, loc, xflag, xdata); + + if (is_xdata_created) + dict_unref(xdata); + + return 0; +} + +/****************************fsync******************************************/ +int32_t +ctr_fsync_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, + int32_t op_errno, struct iatt *prebuf, struct iatt *postbuf, + dict_t *xdata) +{ + int ret = -1; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_FOP_FAILED_THEN_GOTO(this, op_ret, op_errno, out); + + ret = ctr_insert_unwind(frame, this, GFDB_FOP_INODE_WRITE, GFDB_FOP_UNWIND); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_FSYNC_UNWIND_FAILED, + "Failed to insert fsync unwind"); + } + +out: + ctr_free_frame_local(frame); + + STACK_UNWIND_STRICT(fsync, frame, op_ret, op_errno, prebuf, postbuf, xdata); + + return 0; +} + +int32_t +ctr_fsync(call_frame_t *frame, xlator_t *this, fd_t *fd, int32_t flags, + dict_t *xdata) +{ + int ret = -1; + gf_ctr_inode_context_t ctr_inode_cx; + gf_ctr_inode_context_t *_inode_cx = &ctr_inode_cx; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_INTERNAL_FOP_THEN_GOTO(frame, xdata, out); + + /*Fill ctr inode context*/ + FILL_CTR_INODE_CONTEXT(_inode_cx, fd->inode->ia_type, fd->inode->gfid, NULL, + NULL, GFDB_FOP_INODE_WRITE, GFDB_FOP_WIND); + + /*record into the database*/ + ret = ctr_insert_wind(frame, this, _inode_cx); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_FSYNC_WIND_FAILED, + "Failed to insert fsync wind"); + } + +out: + STACK_WIND(frame, ctr_fsync_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->fsync, fd, flags, xdata); + return 0; +} + +/****************************setxattr****************************************/ + +int +ctr_setxattr_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, dict_t *xdata) +{ + int ret = -1; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + + ret = ctr_insert_unwind(frame, this, GFDB_FOP_INODE_WRITE, GFDB_FOP_UNWIND); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_FSYNC_UNWIND_FAILED, + "Failed to insert setxattr unwind"); + } + +out: + ctr_free_frame_local(frame); + + STACK_UNWIND_STRICT(setxattr, frame, op_ret, op_errno, xdata); + + return 0; +} + +int +ctr_setxattr(call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xattr, + int flags, dict_t *xdata) +{ + int ret = -1; + gf_ctr_inode_context_t ctr_inode_cx; + gf_ctr_inode_context_t *_inode_cx = &ctr_inode_cx; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_INTERNAL_FOP_THEN_GOTO(frame, xdata, out); + CTR_RECORD_METADATA_HEAT_IS_DISABLED_THEN_GOTO(this, out); + + /*Fill ctr inode context*/ + FILL_CTR_INODE_CONTEXT(_inode_cx, loc->inode->ia_type, loc->inode->gfid, + NULL, NULL, GFDB_FOP_INODE_WRITE, GFDB_FOP_WIND); + + /*record into the database*/ + ret = ctr_insert_wind(frame, this, _inode_cx); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_SETATTR_WIND_FAILED, + "Failed to insert setxattr wind"); + } + +out: + STACK_WIND(frame, ctr_setxattr_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->setxattr, loc, xattr, flags, xdata); + return 0; +} +/**************************** fsetxattr *************************************/ +int32_t +ctr_fsetxattr_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, dict_t *xdata) +{ + int ret = -1; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_FOP_FAILED_THEN_GOTO(this, op_ret, op_errno, out); + + ret = ctr_insert_unwind(frame, this, GFDB_FOP_INODE_WRITE, GFDB_FOP_UNWIND); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_FSYNC_UNWIND_FAILED, + "Failed to insert fsetxattr unwind"); + } + +out: + ctr_free_frame_local(frame); + + STACK_UNWIND_STRICT(fsetxattr, frame, op_ret, op_errno, xdata); + + return 0; +} + +int32_t +ctr_fsetxattr(call_frame_t *frame, xlator_t *this, fd_t *fd, dict_t *dict, + int32_t flags, dict_t *xdata) +{ + int ret = -1; + gf_ctr_inode_context_t ctr_inode_cx; + gf_ctr_inode_context_t *_inode_cx = &ctr_inode_cx; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_INTERNAL_FOP_THEN_GOTO(frame, xdata, out); + CTR_RECORD_METADATA_HEAT_IS_DISABLED_THEN_GOTO(this, out); + + /*Fill ctr inode context*/ + FILL_CTR_INODE_CONTEXT(_inode_cx, fd->inode->ia_type, fd->inode->gfid, NULL, + NULL, GFDB_FOP_INODE_WRITE, GFDB_FOP_WIND); + + /*record into the database*/ + ret = ctr_insert_wind(frame, this, _inode_cx); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_SETATTR_WIND_FAILED, + "Failed to insert fsetxattr wind"); + } + +out: + STACK_WIND(frame, ctr_fsetxattr_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->fsetxattr, fd, dict, flags, xdata); + return 0; +} +/****************************mknod*******************************************/ + +int32_t +ctr_mknod_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, + int32_t op_errno, inode_t *inode, struct iatt *buf, + struct iatt *preparent, struct iatt *postparent, dict_t *xdata) +{ + int ret = -1; + ctr_heal_ret_val_t ret_val = CTR_CTX_ERROR; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_FOP_FAILED_THEN_GOTO(this, op_ret, op_errno, out); + + /* Add hard link to the list */ + ret_val = add_hard_link_ctx(frame, this, inode); + if (ret_val == CTR_CTX_ERROR) { + gf_msg_trace(this->name, 0, "Failed adding hard link"); + } + + ret = ctr_insert_unwind(frame, this, GFDB_FOP_CREATE_WRITE, + GFDB_FOP_UNWIND); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_MKNOD_UNWIND_FAILED, + "Failed to insert mknod unwind"); + } + +out: + ctr_free_frame_local(frame); + + STACK_UNWIND_STRICT(mknod, frame, op_ret, op_errno, inode, buf, preparent, + postparent, xdata); + + return 0; +} + +int +ctr_mknod(call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode, + dev_t rdev, mode_t umask, dict_t *xdata) +{ + int ret = -1; + gf_ctr_inode_context_t ctr_inode_cx; + gf_ctr_inode_context_t *_inode_cx = &ctr_inode_cx; + gf_ctr_link_context_t ctr_link_cx; + gf_ctr_link_context_t *_link_cx = &ctr_link_cx; + uuid_t gfid = { + 0, + }; + uuid_t *ptr_gfid = &gfid; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_INTERNAL_FOP_THEN_GOTO(frame, xdata, out); + + GF_ASSERT(frame); + GF_ASSERT(frame->root); + + /*get gfid from xdata dict*/ + ret = dict_get_gfuuid(xdata, "gfid-req", &gfid); + if (ret) { + gf_msg_debug(this->name, 0, "failed to get gfid from dict"); + goto out; + } + + /*fill ctr link context*/ + FILL_CTR_LINK_CX(_link_cx, loc->pargfid, loc->name, out); + + /*Fill ctr inode context*/ + FILL_CTR_INODE_CONTEXT(_inode_cx, loc->inode->ia_type, *ptr_gfid, _link_cx, + NULL, GFDB_FOP_CREATE_WRITE, GFDB_FOP_WIND); + + /*record into the database*/ + ret = ctr_insert_wind(frame, this, _inode_cx); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_MKNOD_WIND_FAILED, + "Failed to insert mknod wind"); + } + +out: + STACK_WIND(frame, ctr_mknod_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->mknod, loc, mode, rdev, umask, xdata); + return 0; +} + +/****************************create******************************************/ +int +ctr_create_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, + int op_errno, fd_t *fd, inode_t *inode, struct iatt *stbuf, + struct iatt *preparent, struct iatt *postparent, dict_t *xdata) +{ + int ret = -1; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_FOP_FAILED_THEN_GOTO(this, op_ret, op_errno, out); + + ret = add_hard_link_ctx(frame, this, inode); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_ADD_HARDLINK_FAILED, + "Failed adding hard link"); + } + + ret = ctr_insert_unwind(frame, this, GFDB_FOP_CREATE_WRITE, + GFDB_FOP_UNWIND); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_CREATE_UNWIND_FAILED, + "Failed to insert create unwind"); + } + +out: + ctr_free_frame_local(frame); + + STACK_UNWIND_STRICT(create, frame, op_ret, op_errno, fd, inode, stbuf, + preparent, postparent, xdata); + + return 0; +} + +int +ctr_create(call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags, + mode_t mode, mode_t umask, fd_t *fd, dict_t *xdata) +{ + int ret = -1; + gf_ctr_inode_context_t ctr_inode_cx; + gf_ctr_inode_context_t *_inode_cx = &ctr_inode_cx; + gf_ctr_link_context_t ctr_link_cx; + gf_ctr_link_context_t *_link_cx = &ctr_link_cx; + uuid_t gfid = { + 0, + }; + uuid_t *ptr_gfid = &gfid; + struct iatt dummy_stat = {0}; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + + GF_ASSERT(frame); + GF_ASSERT(frame->root); + + /*Get GFID from Xdata dict*/ + ret = dict_get_gfuuid(xdata, "gfid-req", &gfid); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_GET_GFID_FROM_DICT_FAILED, + "failed to get gfid from dict"); + goto out; + } + + /*fill ctr link context*/ + FILL_CTR_LINK_CX(_link_cx, loc->pargfid, loc->name, out); + + /*Fill ctr inode context*/ + FILL_CTR_INODE_CONTEXT(_inode_cx, loc->inode->ia_type, *ptr_gfid, _link_cx, + NULL, GFDB_FOP_CREATE_WRITE, GFDB_FOP_WIND); + + /*Internal FOP*/ + _inode_cx->is_internal_fop = is_internal_fop(frame, xdata); + + /* If its a internal FOP and dht link file donot record*/ + if (_inode_cx->is_internal_fop && dht_is_linkfile(&dummy_stat, xdata)) { + goto out; + } + + /*record into the database*/ + ret = ctr_insert_wind(frame, this, &ctr_inode_cx); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_CREATE_WIND_FAILED, + "Failed to insert create wind"); + } +out: + STACK_WIND(frame, ctr_create_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->create, loc, flags, mode, umask, fd, + xdata); + return 0; +} + +/****************************link********************************************/ + +int +ctr_link_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, + int op_errno, inode_t *inode, struct iatt *stbuf, + struct iatt *preparent, struct iatt *postparent, dict_t *xdata) +{ + int ret = -1; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_FOP_FAILED_THEN_GOTO(this, op_ret, op_errno, out); + + /* Add hard link to the list */ + ret = add_hard_link_ctx(frame, this, inode); + if (ret) { + gf_msg_trace(this->name, 0, "Failed adding hard link"); + } + + ret = ctr_insert_unwind(frame, this, GFDB_FOP_DENTRY_WRITE, + GFDB_FOP_UNWIND); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_CREATE_UNWIND_FAILED, + "Failed to insert create unwind"); + } + +out: + ctr_free_frame_local(frame); + + STACK_UNWIND_STRICT(link, frame, op_ret, op_errno, inode, stbuf, preparent, + postparent, xdata); + return 0; +} + +int +ctr_link(call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc, + dict_t *xdata) +{ + int ret = -1; + gf_ctr_inode_context_t ctr_inode_cx; + gf_ctr_inode_context_t *_inode_cx = &ctr_inode_cx; + gf_ctr_link_context_t ctr_link_cx; + gf_ctr_link_context_t *_link_cx = &ctr_link_cx; + struct iatt dummy_stat = {0}; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + + GF_ASSERT(frame); + GF_ASSERT(frame->root); + + /*fill ctr link context*/ + FILL_CTR_LINK_CX(_link_cx, newloc->pargfid, newloc->name, out); + + /*Fill ctr inode context*/ + FILL_CTR_INODE_CONTEXT(_inode_cx, oldloc->inode->ia_type, + oldloc->inode->gfid, _link_cx, NULL, + GFDB_FOP_DENTRY_WRITE, GFDB_FOP_WIND); + + /*Internal FOP*/ + _inode_cx->is_internal_fop = is_internal_fop(frame, xdata); + + /* Is a metadata fop */ + _inode_cx->is_metadata_fop = _gf_true; + + /* If its a internal FOP and dht link file donot record*/ + if (_inode_cx->is_internal_fop && dht_is_linkfile(&dummy_stat, xdata)) { + goto out; + } + + /*record into the database*/ + ret = ctr_insert_wind(frame, this, _inode_cx); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_LINK_WIND_FAILED, + "Failed to insert link wind"); + } + +out: + STACK_WIND(frame, ctr_link_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->link, oldloc, newloc, xdata); + return 0; +} + +/******************************readv*****************************************/ +int +ctr_readv_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, + int op_errno, struct iovec *vector, int count, struct iatt *stbuf, + struct iobref *iobref, dict_t *xdata) +{ + int ret = -1; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_FOP_FAILED_THEN_GOTO(this, op_ret, op_errno, out); + + ret = ctr_insert_unwind(frame, this, GFDB_FOP_INODE_READ, GFDB_FOP_UNWIND); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_CREATE_UNWIND_FAILED, + "Failed to insert create unwind"); + } + +out: + ctr_free_frame_local(frame); + + STACK_UNWIND_STRICT(readv, frame, op_ret, op_errno, vector, count, stbuf, + iobref, xdata); + return 0; +} + +int +ctr_readv(call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, off_t off, + uint32_t flags, dict_t *xdata) +{ + int ret = -1; + gf_ctr_inode_context_t ctr_inode_cx; + gf_ctr_inode_context_t *_inode_cx = &ctr_inode_cx; + + CTR_IS_DISABLED_THEN_GOTO(this, out); + CTR_IF_INTERNAL_FOP_THEN_GOTO(frame, xdata, out); + + /*Fill ctr inode context*/ + FILL_CTR_INODE_CONTEXT(_inode_cx, fd->inode->ia_type, fd->inode->gfid, NULL, + NULL, GFDB_FOP_INODE_READ, GFDB_FOP_WIND); + + /*record into the database*/ + ret = ctr_insert_wind(frame, this, _inode_cx); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_READV_WIND_FAILED, + "Failed to insert readv wind"); + } + +out: + STACK_WIND(frame, ctr_readv_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->readv, fd, size, off, flags, xdata); + return 0; +} + +/*******************************ctr_ipc****************************************/ + +/*This is the call back function per record/file from data base*/ +static int +ctr_db_query_callback(gfdb_query_record_t *gfdb_query_record, void *args) +{ + int ret = -1; + ctr_query_cbk_args_t *query_cbk_args = args; + + GF_VALIDATE_OR_GOTO("ctr", query_cbk_args, out); + + ret = gfdb_write_query_record(query_cbk_args->query_fd, gfdb_query_record); + if (ret) { + gf_msg("ctr", GF_LOG_ERROR, 0, CTR_MSG_FATAL_ERROR, + "Failed to write to query file"); + goto out; + } + + query_cbk_args->count++; + + ret = 0; +out: + return ret; +} + +/* This function does all the db queries related to tiering and + * generates/populates new/existing query file + * inputs: + * xlator_t *this : CTR Translator + * void *conn_node : Database connection + * char *query_file: the query file that needs to be updated + * gfdb_ipc_ctr_params_t *ipc_ctr_params: the query parameters + * Return: + * On success 0 + * On failure -1 + * */ +int +ctr_db_query(xlator_t *this, void *conn_node, char *query_file, + gfdb_ipc_ctr_params_t *ipc_ctr_params) +{ + int ret = -1; + ctr_query_cbk_args_t query_cbk_args = {0}; + + GF_VALIDATE_OR_GOTO("ctr", this, out); + GF_VALIDATE_OR_GOTO(this->name, conn_node, out); + GF_VALIDATE_OR_GOTO(this->name, query_file, out); + GF_VALIDATE_OR_GOTO(this->name, ipc_ctr_params, out); + + /*Query for eligible files from db*/ + query_cbk_args.query_fd = open(query_file, O_WRONLY | O_CREAT | O_APPEND, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (query_cbk_args.query_fd < 0) { + gf_msg(this->name, GF_LOG_ERROR, errno, CTR_MSG_FATAL_ERROR, + "Failed to open query file %s", query_file); + goto out; + } + if (!ipc_ctr_params->is_promote) { + if (ipc_ctr_params->emergency_demote) { + /* emergency demotion mode */ + ret = find_all(conn_node, ctr_db_query_callback, + (void *)&query_cbk_args, + ipc_ctr_params->query_limit); + } else { + if (ipc_ctr_params->write_freq_threshold == 0 && + ipc_ctr_params->read_freq_threshold == 0) { + ret = find_unchanged_for_time(conn_node, ctr_db_query_callback, + (void *)&query_cbk_args, + &ipc_ctr_params->time_stamp); + } else { + ret = find_unchanged_for_time_freq( + conn_node, ctr_db_query_callback, (void *)&query_cbk_args, + &ipc_ctr_params->time_stamp, + ipc_ctr_params->write_freq_threshold, + ipc_ctr_params->read_freq_threshold, _gf_false); + } + } + } else { + if (ipc_ctr_params->write_freq_threshold == 0 && + ipc_ctr_params->read_freq_threshold == 0) { + ret = find_recently_changed_files(conn_node, ctr_db_query_callback, + (void *)&query_cbk_args, + &ipc_ctr_params->time_stamp); + } else { + ret = find_recently_changed_files_freq( + conn_node, ctr_db_query_callback, (void *)&query_cbk_args, + &ipc_ctr_params->time_stamp, + ipc_ctr_params->write_freq_threshold, + ipc_ctr_params->read_freq_threshold, _gf_false); + } + } + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_FATAL_ERROR, + "FATAL: query from db failed"); + goto out; + } + + ret = clear_files_heat(conn_node); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_FATAL_ERROR, + "FATAL: Failed to clear db entries"); + goto out; + } + + ret = 0; +out: + + if (!ret) + ret = query_cbk_args.count; + + if (query_cbk_args.query_fd >= 0) { + sys_close(query_cbk_args.query_fd); + query_cbk_args.query_fd = -1; + } + + return ret; +} + +void * +ctr_compact_thread(void *args) +{ + int ret = -1; + void *db_conn = NULL; + + xlator_t *this = NULL; + gf_ctr_private_t *priv = NULL; + gf_boolean_t compact_active = _gf_false; + gf_boolean_t compact_mode_switched = _gf_false; + + this = (xlator_t *)args; + + GF_VALIDATE_OR_GOTO("ctr", this, out); + + priv = this->private; + + db_conn = priv->_db_conn; + compact_active = priv->compact_active; + compact_mode_switched = priv->compact_mode_switched; + + gf_msg("ctr-compact", GF_LOG_INFO, 0, CTR_MSG_SET, "Starting compaction"); + + ret = compact_db(db_conn, compact_active, compact_mode_switched); + + if (ret) { + gf_msg("ctr-compact", GF_LOG_ERROR, 0, CTR_MSG_SET, + "Failed to perform the compaction"); + } + + ret = pthread_mutex_lock(&priv->compact_lock); + + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_SET, + "Failed to acquire lock"); + goto out; + } + + /* We are done compaction on this brick. Set all flags to false */ + priv->compact_active = _gf_false; + priv->compact_mode_switched = _gf_false; + + ret = pthread_mutex_unlock(&priv->compact_lock); + + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_SET, + "Failed to release lock"); + goto out; + } + +out: + return NULL; +} + +int +ctr_ipc_helper(xlator_t *this, dict_t *in_dict, dict_t *out_dict) +{ + int ret = -1; + char *ctr_ipc_ops = NULL; + gf_ctr_private_t *priv = NULL; + char *db_version = NULL; + char *db_param_key = NULL; + char *db_param = NULL; + char *query_file = NULL; + gfdb_ipc_ctr_params_t *ipc_ctr_params = NULL; + int result = 0; + pthread_t compact_thread; + + GF_VALIDATE_OR_GOTO("ctr", this, out); + GF_VALIDATE_OR_GOTO(this->name, this->private, out); + priv = this->private; + GF_VALIDATE_OR_GOTO(this->name, priv->_db_conn, out); + GF_VALIDATE_OR_GOTO(this->name, in_dict, out); + GF_VALIDATE_OR_GOTO(this->name, out_dict, out); + + GET_DB_PARAM_FROM_DICT(this->name, in_dict, GFDB_IPC_CTR_KEY, ctr_ipc_ops, + out); + + /*if its a db clear operation */ + if (strncmp(ctr_ipc_ops, GFDB_IPC_CTR_CLEAR_OPS, + SLEN(GFDB_IPC_CTR_CLEAR_OPS)) == 0) { + ret = clear_files_heat(priv->_db_conn); + if (ret) + goto out; + + } /* if its a query operation, in which case its query + clear db*/ + else if (strncmp(ctr_ipc_ops, GFDB_IPC_CTR_QUERY_OPS, + SLEN(GFDB_IPC_CTR_QUERY_OPS)) == 0) { + ret = dict_get_str(in_dict, GFDB_IPC_CTR_GET_QFILE_PATH, &query_file); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_SET, + "Failed extracting query file path"); + goto out; + } + + ret = dict_get_bin(in_dict, GFDB_IPC_CTR_GET_QUERY_PARAMS, + (void *)&ipc_ctr_params); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_SET, + "Failed extracting query parameters"); + goto out; + } + + ret = ctr_db_query(this, priv->_db_conn, query_file, ipc_ctr_params); + + ret = dict_set_int32(out_dict, GFDB_IPC_CTR_RET_QUERY_COUNT, ret); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_SET, + "Failed setting query reply"); + goto out; + } + + } /* if its a query for db version */ + else if (strncmp(ctr_ipc_ops, GFDB_IPC_CTR_GET_DB_VERSION_OPS, + SLEN(GFDB_IPC_CTR_GET_DB_VERSION_OPS)) == 0) { + ret = get_db_version(priv->_db_conn, &db_version); + if (ret == -1 || !db_version) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_SET, + "Failed extracting db version "); + goto out; + } + + SET_DB_PARAM_TO_DICT(this->name, out_dict, GFDB_IPC_CTR_RET_DB_VERSION, + db_version, ret, error); + + } /* if its a query for a db setting */ + else if (strncmp(ctr_ipc_ops, GFDB_IPC_CTR_GET_DB_PARAM_OPS, + SLEN(GFDB_IPC_CTR_GET_DB_PARAM_OPS)) == 0) { + ret = dict_get_str(in_dict, GFDB_IPC_CTR_GET_DB_KEY, &db_param_key); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_SET, + "Failed extracting db param key"); + goto out; + } + + ret = get_db_params(priv->_db_conn, db_param_key, &db_param); + if (ret == -1 || !db_param) { + goto out; + } + + SET_DB_PARAM_TO_DICT(this->name, out_dict, db_param_key, db_param, ret, + error); + } /* if its an attempt to compact the database */ + else if (strncmp(ctr_ipc_ops, GFDB_IPC_CTR_SET_COMPACT_PRAGMA, + SLEN(GFDB_IPC_CTR_SET_COMPACT_PRAGMA)) == 0) { + ret = pthread_mutex_lock(&priv->compact_lock); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_SET, + "Failed to acquire lock for compaction"); + goto out; + } + + if ((priv->compact_active || priv->compact_mode_switched)) { + /* Compaction in progress. LEAVE */ + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_SET, + "Compaction already in progress."); + pthread_mutex_unlock(&priv->compact_lock); + goto out; + } + /* At this point, we should be the only one on the brick */ + /* compacting */ + + /* Grab the arguments from the dictionary */ + ret = dict_get_int32(in_dict, "compact_active", &result); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_SET, + "Failed to get compaction type"); + goto out; + } + + if (result) { + priv->compact_active = _gf_true; + } + + ret = dict_get_int32(in_dict, "compact_mode_switched", &result); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_SET, + "Failed to see if compaction switched"); + goto out; + } + + if (result) { + priv->compact_mode_switched = _gf_true; + gf_msg("ctr-compact", GF_LOG_TRACE, 0, CTR_MSG_SET, + "Pre-thread: Compact mode switch is true"); + } else { + gf_msg("ctr-compact", GF_LOG_TRACE, 0, CTR_MSG_SET, + "Pre-thread: Compact mode switch is false"); + } + + ret = pthread_mutex_unlock(&priv->compact_lock); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_SET, + "Failed to release lock for compaction"); + goto out; + } + + ret = gf_thread_create(&compact_thread, NULL, ctr_compact_thread, + (void *)this, "ctrcomp"); + + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_SET, + "Failed to spawn compaction thread"); + goto out; + } + + goto out; + } /* default case */ + else { + goto out; + } + + ret = 0; + goto out; +error: + GF_FREE(db_param_key); + GF_FREE(db_param); + GF_FREE(db_version); +out: + return ret; +} + +/* IPC Call from tier migrator to clear the heat on the DB */ +int32_t +ctr_ipc(call_frame_t *frame, xlator_t *this, int32_t op, dict_t *in_dict) +{ + int ret = -1; + gf_ctr_private_t *priv = NULL; + dict_t *out_dict = NULL; + + GF_ASSERT(this); + priv = this->private; + GF_ASSERT(priv); + GF_ASSERT(priv->_db_conn); + GF_VALIDATE_OR_GOTO(this->name, in_dict, wind); + + if (op != GF_IPC_TARGET_CTR) + goto wind; + + out_dict = dict_new(); + if (!out_dict) { + goto out; + } + + ret = ctr_ipc_helper(this, in_dict, out_dict); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_SET, + "Failed in ctr_ipc_helper"); + } +out: + + STACK_UNWIND_STRICT(ipc, frame, ret, 0, out_dict); + + if (out_dict) + dict_unref(out_dict); + + return 0; + +wind: + STACK_WIND(frame, default_ipc_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->ipc, op, in_dict); + + return 0; +} + +/* Call to initialize db for ctr xlator while ctr is enabled */ +int32_t +initialize_ctr_resource(xlator_t *this, gf_ctr_private_t *priv) +{ + int ret_db = -1; + dict_t *params_dict = NULL; + + if (!priv) + goto error; + + /* For compaction */ + priv->compact_active = _gf_false; + priv->compact_mode_switched = _gf_false; + ret_db = pthread_mutex_init(&priv->compact_lock, NULL); + + if (ret_db) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_FATAL_ERROR, + "FATAL: Failed initializing compaction mutex"); + goto error; + } + + params_dict = dict_new(); + if (!params_dict) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INIT_DB_PARAMS_FAILED, + "DB Params cannot initialized!"); + goto error; + } + + /*Extract db params options*/ + ret_db = extract_db_params(this, params_dict, priv->gfdb_db_type); + if (ret_db) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_EXTRACT_DB_PARAM_OPTIONS_FAILED, + "Failed extracting db params options"); + goto error; + } + + /*Create a memory pool for ctr xlator*/ + this->local_pool = mem_pool_new(gf_ctr_local_t, 64); + if (!this->local_pool) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_CREATE_LOCAL_MEMORY_POOL_FAILED, + "failed to create local memory pool"); + goto error; + } + + /*Initialize Database Connection*/ + priv->_db_conn = init_db(params_dict, priv->gfdb_db_type); + if (!priv->_db_conn) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_FATAL_ERROR, + "FATAL: Failed initializing data base"); + goto error; + } + + ret_db = 0; + goto out; + +error: + if (this) + mem_pool_destroy(this->local_pool); + + if (priv) { + GF_FREE(priv->ctr_db_path); + } + GF_FREE(priv); + ret_db = -1; +out: + if (params_dict) + dict_unref(params_dict); + + return ret_db; +} + +/******************************************************************************/ +int +reconfigure(xlator_t *this, dict_t *options) +{ + char *temp_str = NULL; + int ret = 0; + gf_ctr_private_t *priv = NULL; + + priv = this->private; + + if (dict_get_str(options, "changetimerecorder.frequency", &temp_str)) { + gf_msg(this->name, GF_LOG_TRACE, 0, CTR_MSG_SET, "set"); + } + + GF_OPTION_RECONF("ctr-enabled", priv->enabled, options, bool, out); + if (!priv->enabled) { + gf_msg(GFDB_DATA_STORE, GF_LOG_INFO, 0, CTR_MSG_XLATOR_DISABLED, + "CTR Xlator is not enabled so skip ctr reconfigure"); + goto out; + } + + /* If ctr is enabled after skip init for ctr xlator then call + initialize_ctr_resource during reconfigure phase to allocate resources + for xlator + */ + if (priv->enabled && !priv->_db_conn) { + ret = initialize_ctr_resource(this, priv); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_FATAL_ERROR, + "FATAL: Failed ctr initialize resource"); + goto out; + } + } + + GF_OPTION_RECONF("record-counters", priv->ctr_record_counter, options, bool, + out); + + GF_OPTION_RECONF("ctr-record-metadata-heat", priv->ctr_record_metadata_heat, + options, bool, out); + + GF_OPTION_RECONF("ctr_link_consistency", priv->ctr_link_consistency, + options, bool, out); + + GF_OPTION_RECONF("ctr_lookupheal_inode_timeout", + priv->ctr_lookupheal_inode_timeout, options, uint64, out); + + GF_OPTION_RECONF("ctr_lookupheal_link_timeout", + priv->ctr_lookupheal_link_timeout, options, uint64, out); + + GF_OPTION_RECONF("record-exit", priv->ctr_record_unwind, options, bool, + out); + + GF_OPTION_RECONF("record-entry", priv->ctr_record_wind, options, bool, out); + + /* If database is sqlite */ + if (priv->gfdb_db_type == GFDB_SQLITE3) { + /* AUTOCHECKPOINT */ + if (dict_get_str(options, GFDB_SQL_PARAM_WAL_AUTOCHECK, &temp_str) == + 0) { + ret = set_db_params(priv->_db_conn, "wal_autocheckpoint", temp_str); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_SET_VALUE_TO_SQL_PARAM_FAILED, + "Failed to set %s", GFDB_SQL_PARAM_WAL_AUTOCHECK); + } + } + + /* CACHE_SIZE */ + if (dict_get_str(options, GFDB_SQL_PARAM_CACHE_SIZE, &temp_str) == 0) { + ret = set_db_params(priv->_db_conn, "cache_size", temp_str); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_SET_VALUE_TO_SQL_PARAM_FAILED, + "Failed to set %s", GFDB_SQL_PARAM_CACHE_SIZE); + } + } + } + + ret = 0; + +out: + + return ret; +} + +/****************************init********************************************/ + +int32_t +init(xlator_t *this) +{ + gf_ctr_private_t *priv = NULL; + int ret_db = -1; + + if (!this) { + gf_msg("ctr", GF_LOG_ERROR, 0, CTR_MSG_FATAL_ERROR, + "FATAL: ctr this is not initialized"); + return -1; + } + + if (!this->children || this->children->next) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_FATAL_ERROR, + "FATAL: ctr should have exactly one child"); + return -1; + } + + if (!this->parents) { + gf_msg(this->name, GF_LOG_WARNING, 0, CTR_MSG_DANGLING_VOLUME, + "dangling volume. check volfile "); + } + + priv = GF_CALLOC(1, sizeof(*priv), gf_ctr_mt_private_t); + if (!priv) { + gf_msg(this->name, GF_LOG_ERROR, ENOMEM, CTR_MSG_CALLOC_FAILED, + "Calloc did not work!!!"); + return -1; + } + + /*Default values for the translator*/ + priv->ctr_record_wind = _gf_true; + priv->ctr_record_unwind = _gf_false; + priv->ctr_hot_brick = _gf_false; + priv->gfdb_db_type = GFDB_SQLITE3; + priv->gfdb_sync_type = GFDB_DB_SYNC; + priv->_db_conn = NULL; + priv->ctr_lookupheal_link_timeout = CTR_DEFAULT_HARDLINK_EXP_PERIOD; + priv->ctr_lookupheal_inode_timeout = CTR_DEFAULT_INODE_EXP_PERIOD; + + /*Extract ctr xlator options*/ + ret_db = extract_ctr_options(this, priv); + if (ret_db) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_EXTRACT_CTR_XLATOR_OPTIONS_FAILED, + "Failed extracting ctr xlator options"); + GF_FREE(priv); + return -1; + } + + if (!priv->enabled) { + gf_msg(GFDB_DATA_STORE, GF_LOG_INFO, 0, CTR_MSG_XLATOR_DISABLED, + "CTR Xlator is not enabled so skip ctr init"); + goto out; + } + + ret_db = initialize_ctr_resource(this, priv); + if (ret_db) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_FATAL_ERROR, + "FATAL: Failed ctr initialize resource"); + return -1; + } + +out: + this->private = (void *)priv; + return 0; +} + +int +notify(xlator_t *this, int event, void *data, ...) +{ + gf_ctr_private_t *priv = NULL; + int ret = 0; + + priv = this->private; + + if (!priv) + goto out; + + ret = default_notify(this, event, data); + +out: + return ret; +} + +int32_t +mem_acct_init(xlator_t *this) +{ + int ret = -1; + + GF_VALIDATE_OR_GOTO("ctr", this, out); + + ret = xlator_mem_acct_init(this, gf_ctr_mt_end + 1); + + if (ret != 0) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_MEM_ACC_INIT_FAILED, + "Memory accounting init" + "failed"); + return ret; + } +out: + return ret; +} + +void +fini(xlator_t *this) +{ + gf_ctr_private_t *priv = NULL; + + priv = this->private; + + if (priv && priv->enabled) { + if (fini_db(priv->_db_conn)) { + gf_msg(this->name, GF_LOG_WARNING, 0, CTR_MSG_CLOSE_DB_CONN_FAILED, + "Failed closing " + "db connection"); + } + + if (priv->_db_conn) + priv->_db_conn = NULL; + + GF_FREE(priv->ctr_db_path); + if (pthread_mutex_destroy(&priv->compact_lock)) { + gf_msg(this->name, GF_LOG_WARNING, 0, CTR_MSG_CLOSE_DB_CONN_FAILED, + "Failed to " + "destroy the compaction mutex"); + } + } + GF_FREE(priv); + mem_pool_destroy(this->local_pool); + this->local_pool = NULL; + + return; +} + +struct xlator_fops fops = { + /*lookup*/ + .lookup = ctr_lookup, + /*write fops */ + .mknod = ctr_mknod, + .create = ctr_create, + .truncate = ctr_truncate, + .ftruncate = ctr_ftruncate, + .setxattr = ctr_setxattr, + .fsetxattr = ctr_fsetxattr, + .removexattr = ctr_removexattr, + .fremovexattr = ctr_fremovexattr, + .unlink = ctr_unlink, + .link = ctr_link, + .rename = ctr_rename, + .writev = ctr_writev, + .setattr = ctr_setattr, + .fsetattr = ctr_fsetattr, + /*read fops*/ + .readv = ctr_readv, + /* IPC call*/ + .ipc = ctr_ipc}; + +struct xlator_cbks cbks = {.forget = ctr_forget}; + +struct volume_options options[] = { + {.key = + { + "ctr-enabled", + }, + .type = GF_OPTION_TYPE_BOOL, + .value = {"on", "off"}, + .default_value = "off", + .description = "Enables the CTR", + .flags = OPT_FLAG_SETTABLE}, + {.key = {"record-entry"}, + .type = GF_OPTION_TYPE_BOOL, + .value = {"on", "off"}, + .default_value = "on"}, + {.key = {"record-exit"}, + .type = GF_OPTION_TYPE_BOOL, + .value = {"on", "off"}, + .default_value = "off"}, + {.key = {"record-counters"}, + .type = GF_OPTION_TYPE_BOOL, + .value = {"on", "off"}, + .default_value = "off", + .op_version = {GD_OP_VERSION_3_7_0}, + .flags = OPT_FLAG_SETTABLE, + .tags = {}}, + {.key = {"ctr-record-metadata-heat"}, + .type = GF_OPTION_TYPE_BOOL, + .value = {"on", "off"}, + .default_value = "off", + .flags = OPT_FLAG_SETTABLE, + .op_version = {GD_OP_VERSION_3_7_0}, + .tags = {}}, + {.key = {"ctr_link_consistency"}, + .type = GF_OPTION_TYPE_BOOL, + .value = {"on", "off"}, + .default_value = "off", + .flags = OPT_FLAG_SETTABLE, + .op_version = {GD_OP_VERSION_3_7_0}, + .tags = {}}, + {.key = {"ctr_lookupheal_link_timeout"}, + .type = GF_OPTION_TYPE_INT, + .default_value = "300", + .flags = OPT_FLAG_SETTABLE, + .op_version = {GD_OP_VERSION_3_7_2}, + .tags = {}}, + {.key = {"ctr_lookupheal_inode_timeout"}, + .type = GF_OPTION_TYPE_INT, + .default_value = "300", + .flags = OPT_FLAG_SETTABLE, + .op_version = {GD_OP_VERSION_3_7_2}, + .tags = {}}, + {.key = {"hot-brick"}, + .type = GF_OPTION_TYPE_BOOL, + .value = {"on", "off"}, + .default_value = "off"}, + {.key = {"db-type"}, + .type = GF_OPTION_TYPE_STR, + .value = {"hashfile", "rocksdb", "changelog", "sqlite3", "hyperdex"}, + .default_value = "sqlite3", + .op_version = {GD_OP_VERSION_3_7_0}, + .flags = OPT_FLAG_SETTABLE, + .tags = {}}, + {.key = {"db-sync"}, + .type = GF_OPTION_TYPE_STR, + .value = {"sync", "async"}, + .default_value = "sync"}, + {.key = {"db-path"}, .type = GF_OPTION_TYPE_PATH}, + {.key = {"db-name"}, .type = GF_OPTION_TYPE_STR}, + {.key = {GFDB_SQL_PARAM_SYNC}, + .type = GF_OPTION_TYPE_STR, + .value = {"off", "normal", "full"}, + .default_value = "normal"}, + {.key = {GFDB_SQL_PARAM_JOURNAL_MODE}, + .type = GF_OPTION_TYPE_STR, + .value = {"delete", "truncate", "persist", "memory", "wal", "off"}, + .default_value = "wal", + .flags = OPT_FLAG_SETTABLE, + .op_version = {GD_OP_VERSION_3_7_0}, + .tags = {}}, + {.key = {GFDB_SQL_PARAM_AUTO_VACUUM}, + .type = GF_OPTION_TYPE_STR, + .value = {"off", "full", "incr"}, + .default_value = "off", + .flags = OPT_FLAG_SETTABLE, + .op_version = {GD_OP_VERSION_3_7_0}, + .tags = {}}, + {.key = {GFDB_SQL_PARAM_WAL_AUTOCHECK}, + .type = GF_OPTION_TYPE_INT, + .default_value = "25000", + .flags = OPT_FLAG_SETTABLE, + .op_version = {GD_OP_VERSION_3_7_0}, + .tags = {}}, + {.key = {GFDB_SQL_PARAM_CACHE_SIZE}, + .type = GF_OPTION_TYPE_INT, + .default_value = "12500", + .flags = OPT_FLAG_SETTABLE, + .op_version = {GD_OP_VERSION_3_7_0}, + .tags = {}}, + {.key = {GFDB_SQL_PARAM_PAGE_SIZE}, + .type = GF_OPTION_TYPE_INT, + .default_value = "4096", + .flags = OPT_FLAG_SETTABLE, + .op_version = {GD_OP_VERSION_3_7_0}, + .tags = {}}, + {.key = {NULL}}, +}; + +xlator_api_t xlator_api = { + .init = init, + .fini = fini, + .notify = notify, + .reconfigure = reconfigure, + .mem_acct_init = mem_acct_init, + .op_version = {GD_OP_VERSION_3_7_0}, /* Present from the initial version */ + .fops = &fops, + .cbks = &cbks, + .identifier = "changetimerecorder", + .category = GF_MAINTAINED, + .options = options, +}; diff --git a/xlators/features/changetimerecorder/src/changetimerecorder.h b/xlators/features/changetimerecorder/src/changetimerecorder.h new file mode 100644 index 0000000..0150a1c --- /dev/null +++ b/xlators/features/changetimerecorder/src/changetimerecorder.h @@ -0,0 +1,21 @@ +/* + Copyright (c) 2006-2015 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#ifndef __CTR_H +#define __CTR_H + +#include +#include +#include +#include +#include "ctr_mem_types.h" +#include "ctr-helper.h" + +#endif /* __CTR_H */ diff --git a/xlators/features/changetimerecorder/src/ctr-helper.c b/xlators/features/changetimerecorder/src/ctr-helper.c new file mode 100644 index 0000000..e1e6573 --- /dev/null +++ b/xlators/features/changetimerecorder/src/ctr-helper.c @@ -0,0 +1,293 @@ +/* + Copyright (c) 2015 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#include "gfdb_sqlite3.h" +#include "ctr-helper.h" +#include "ctr-messages.h" + +/******************************************************************************* + * + * Fill unwind into db record + * + ******************************************************************************/ +int +fill_db_record_for_unwind(xlator_t *this, gf_ctr_local_t *ctr_local, + gfdb_fop_type_t fop_type, gfdb_fop_path_t fop_path) +{ + int ret = -1; + gfdb_time_t *ctr_uwtime = NULL; + gf_ctr_private_t *_priv = NULL; + + GF_ASSERT(this); + _priv = this->private; + GF_ASSERT(_priv); + + GF_ASSERT(ctr_local); + + /*If not unwind path error*/ + if (!isunwindpath(fop_path)) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_WRONG_FOP_PATH, + "Wrong fop_path. Should be unwind"); + goto out; + } + + ctr_uwtime = &CTR_DB_REC(ctr_local).gfdb_unwind_change_time; + CTR_DB_REC(ctr_local).gfdb_fop_path = fop_path; + CTR_DB_REC(ctr_local).gfdb_fop_type = fop_type; + + ret = gettimeofday(ctr_uwtime, NULL); + if (ret == -1) { + gf_msg(this->name, GF_LOG_ERROR, errno, + CTR_MSG_FILL_UNWIND_TIME_REC_ERROR, + "Error " + "filling unwind time record %s", + strerror(errno)); + goto out; + } + + /* Special case i.e if its a tier rebalance + * + cold tier brick + * + its a create/mknod FOP + * we record unwind time as zero */ + if (ctr_local->client_pid == GF_CLIENT_PID_TIER_DEFRAG && + (!_priv->ctr_hot_brick) && isdentrycreatefop(fop_type)) { + memset(ctr_uwtime, 0, sizeof(*ctr_uwtime)); + } + ret = 0; +out: + return ret; +} + +/******************************************************************************* + * + * Fill wind into db record + * + ******************************************************************************/ +int +fill_db_record_for_wind(xlator_t *this, gf_ctr_local_t *ctr_local, + gf_ctr_inode_context_t *ctr_inode_cx) +{ + int ret = -1; + gfdb_time_t *ctr_wtime = NULL; + gf_ctr_private_t *_priv = NULL; + + GF_ASSERT(this); + _priv = this->private; + GF_ASSERT(_priv); + GF_ASSERT(ctr_local); + IS_CTR_INODE_CX_SANE(ctr_inode_cx); + + /*if not wind path error!*/ + if (!iswindpath(ctr_inode_cx->fop_path)) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_WRONG_FOP_PATH, + "Wrong fop_path. Should be wind"); + goto out; + } + + ctr_wtime = &CTR_DB_REC(ctr_local).gfdb_wind_change_time; + CTR_DB_REC(ctr_local).gfdb_fop_path = ctr_inode_cx->fop_path; + CTR_DB_REC(ctr_local).gfdb_fop_type = ctr_inode_cx->fop_type; + CTR_DB_REC(ctr_local).link_consistency = _priv->ctr_link_consistency; + + ret = gettimeofday(ctr_wtime, NULL); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, errno, + CTR_MSG_FILL_UNWIND_TIME_REC_ERROR, + "Error filling wind time record %s", strerror(errno)); + goto out; + } + + /* Special case i.e if its a tier rebalance + * + cold tier brick + * + its a create/mknod FOP + * we record wind time as zero */ + if (ctr_local->client_pid == GF_CLIENT_PID_TIER_DEFRAG && + (!_priv->ctr_hot_brick) && isdentrycreatefop(ctr_inode_cx->fop_type)) { + memset(ctr_wtime, 0, sizeof(*ctr_wtime)); + } + + /* Copy gfid into db record */ + gf_uuid_copy(CTR_DB_REC(ctr_local).gfid, *(ctr_inode_cx->gfid)); + + /* Copy older gfid if any */ + if (ctr_inode_cx->old_gfid && + (!gf_uuid_is_null(*(ctr_inode_cx->old_gfid)))) { + gf_uuid_copy(CTR_DB_REC(ctr_local).old_gfid, *(ctr_inode_cx->old_gfid)); + } + + /*Hard Links*/ + if (isdentryfop(ctr_inode_cx->fop_type)) { + /*new link fop*/ + if (NEW_LINK_CX(ctr_inode_cx)) { + gf_uuid_copy(CTR_DB_REC(ctr_local).pargfid, + *((NEW_LINK_CX(ctr_inode_cx))->pargfid)); + strcpy(CTR_DB_REC(ctr_local).file_name, + NEW_LINK_CX(ctr_inode_cx)->basename); + } + /*rename fop*/ + if (OLD_LINK_CX(ctr_inode_cx)) { + gf_uuid_copy(CTR_DB_REC(ctr_local).old_pargfid, + *((OLD_LINK_CX(ctr_inode_cx))->pargfid)); + strcpy(CTR_DB_REC(ctr_local).old_file_name, + OLD_LINK_CX(ctr_inode_cx)->basename); + } + } + + ret = 0; +out: + /*On error roll back and clean the record*/ + if (ret == -1) { + CLEAR_CTR_DB_RECORD(ctr_local); + } + return ret; +} + +/****************************************************************************** + * + * CTR xlator init related functions + * + * + * ****************************************************************************/ +static int +extract_sql_params(xlator_t *this, dict_t *params_dict) +{ + int ret = -1; + char *db_path = NULL; + char *db_name = NULL; + char *db_full_path = NULL; + + GF_ASSERT(this); + GF_ASSERT(params_dict); + + /*Extract the path of the db*/ + db_path = NULL; + GET_DB_PARAM_FROM_DICT_DEFAULT(this->name, this->options, "db-path", + db_path, "/var/run/gluster/"); + + /*Extract the name of the db*/ + db_name = NULL; + GET_DB_PARAM_FROM_DICT_DEFAULT(this->name, this->options, "db-name", + db_name, "gf_ctr_db.db"); + + /*Construct full path of the db*/ + ret = gf_asprintf(&db_full_path, "%s/%s", db_path, db_name); + if (ret < 0) { + gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, + CTR_MSG_CONSTRUCT_DB_PATH_FAILED, + "Construction of full db path failed!"); + goto out; + } + + /*Setting the SQL DB Path*/ + SET_DB_PARAM_TO_DICT(this->name, params_dict, GFDB_SQL_PARAM_DBPATH, + db_full_path, ret, out); + + /*Extract rest of the sql params*/ + ret = gfdb_set_sql_params(this->name, this->options, params_dict); + if (ret) { + gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, + CTR_MSG_SET_VALUE_TO_SQL_PARAM_FAILED, + "Failed setting values to sql param dict!"); + } + + ret = 0; + +out: + if (ret) + GF_FREE(db_full_path); + return ret; +} + +int +extract_db_params(xlator_t *this, dict_t *params_dict, gfdb_db_type_t db_type) +{ + int ret = -1; + + GF_ASSERT(this); + GF_ASSERT(params_dict); + + switch (db_type) { + case GFDB_SQLITE3: + ret = extract_sql_params(this, params_dict); + if (ret) + goto out; + break; + case GFDB_ROCKS_DB: + case GFDB_HYPERDEX: + case GFDB_HASH_FILE_STORE: + case GFDB_INVALID_DB: + case GFDB_DB_END: + goto out; + } + ret = 0; +out: + return ret; +} + +int +extract_ctr_options(xlator_t *this, gf_ctr_private_t *_priv) +{ + int ret = -1; + char *_val_str = NULL; + + GF_ASSERT(this); + GF_ASSERT(_priv); + + /*Checking if the CTR Translator is enabled. By default its disabled*/ + _priv->enabled = _gf_false; + GF_OPTION_INIT("ctr-enabled", _priv->enabled, bool, out); + if (!_priv->enabled) { + gf_msg(GFDB_DATA_STORE, GF_LOG_INFO, 0, CTR_MSG_XLATOR_DISABLED, + "CTR Xlator is disabled."); + ret = 0; + goto out; + } + + /*Extract db type*/ + GF_OPTION_INIT("db-type", _val_str, str, out); + _priv->gfdb_db_type = gf_string2gfdbdbtype(_val_str); + + /*Extract flag for record on wind*/ + GF_OPTION_INIT("record-entry", _priv->ctr_record_wind, bool, out); + + /*Extract flag for record on unwind*/ + GF_OPTION_INIT("record-exit", _priv->ctr_record_unwind, bool, out); + + /*Extract flag for record on counters*/ + GF_OPTION_INIT("record-counters", _priv->ctr_record_counter, bool, out); + + /* Extract flag for record metadata heat */ + GF_OPTION_INIT("ctr-record-metadata-heat", _priv->ctr_record_metadata_heat, + bool, out); + + /*Extract flag for link consistency*/ + GF_OPTION_INIT("ctr_link_consistency", _priv->ctr_link_consistency, bool, + out); + + /*Extract ctr_lookupheal_inode_timeout */ + GF_OPTION_INIT("ctr_lookupheal_inode_timeout", + _priv->ctr_lookupheal_inode_timeout, uint64, out); + + /*Extract ctr_lookupheal_link_timeout*/ + GF_OPTION_INIT("ctr_lookupheal_link_timeout", + _priv->ctr_lookupheal_link_timeout, uint64, out); + + /*Extract flag for hot tier brick*/ + GF_OPTION_INIT("hot-brick", _priv->ctr_hot_brick, bool, out); + + /*Extract flag for sync mode*/ + GF_OPTION_INIT("db-sync", _val_str, str, out); + _priv->gfdb_sync_type = gf_string2gfdbdbsync(_val_str); + + ret = 0; + +out: + return ret; +} diff --git a/xlators/features/changetimerecorder/src/ctr-helper.h b/xlators/features/changetimerecorder/src/ctr-helper.h new file mode 100644 index 0000000..517fbb0 --- /dev/null +++ b/xlators/features/changetimerecorder/src/ctr-helper.h @@ -0,0 +1,854 @@ +/* + Copyright (c) 2015 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#ifndef __CTR_HELPER_H +#define __CTR_HELPER_H + +#include +#include "ctr_mem_types.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gfdb_data_store.h" +#include "ctr-xlator-ctx.h" +#include "ctr-messages.h" + +#define CTR_DEFAULT_HARDLINK_EXP_PERIOD 300 /* Five mins */ +#define CTR_DEFAULT_INODE_EXP_PERIOD 300 /* Five mins */ + +typedef struct ctr_query_cbk_args { + int query_fd; + int count; +} ctr_query_cbk_args_t; + +/*CTR Xlator Private structure*/ +typedef struct gf_ctr_private { + gf_boolean_t enabled; + char *ctr_db_path; + gf_boolean_t ctr_hot_brick; + gf_boolean_t ctr_record_wind; + gf_boolean_t ctr_record_unwind; + gf_boolean_t ctr_record_counter; + gf_boolean_t ctr_record_metadata_heat; + gf_boolean_t ctr_link_consistency; + gfdb_db_type_t gfdb_db_type; + gfdb_sync_type_t gfdb_sync_type; + gfdb_conn_node_t *_db_conn; + uint64_t ctr_lookupheal_link_timeout; + uint64_t ctr_lookupheal_inode_timeout; + gf_boolean_t compact_active; + gf_boolean_t compact_mode_switched; + pthread_mutex_t compact_lock; +} gf_ctr_private_t; + +/* + * gf_ctr_local_t is the ctr xlator local data structure that is stored in + * the call_frame of each FOP. + * + * gfdb_db_record: The gf_ctr_local contains a gfdb_db_record object, which is + * used by the insert_record() api from the libgfdb. The gfdb_db_record object + * will contain all the inode and hardlink(only for dentry fops: create, + * mknod,link, unlink, rename).The ctr_local is keep alive till the unwind + * call and will be release during the unwind. The same gfdb_db_record will + * used for the unwind insert_record() api, to record unwind in the database. + * + * ia_inode_type in gf_ctr_local will tell the type of the inode. This is + * important for during the unwind path. As we will not have the inode during + * the unwind path. We would have include this in the gfdb_db_record itself + * but currently we record only file inode information. + * + * is_internal_fop in gf_ctr_local will tell us if this is a internal fop and + * take special/no action. We don't record change/access times or increement + * heat counter for internal fops from rebalancer. + * */ +typedef struct gf_ctr_local { + gfdb_db_record_t gfdb_db_record; + ia_type_t ia_inode_type; + gf_boolean_t is_internal_fop; + gf_special_pid_t client_pid; +} gf_ctr_local_t; +/* + * Easy access of gfdb_db_record of ctr_local + * */ +#define CTR_DB_REC(ctr_local) (ctr_local->gfdb_db_record) + +/*Clear db record*/ +#define CLEAR_CTR_DB_RECORD(ctr_local) \ + do { \ + ctr_local->gfdb_db_record.gfdb_fop_path = GFDB_FOP_INVALID; \ + memset(&(ctr_local->gfdb_db_record.gfdb_wind_change_time), 0, \ + sizeof(gfdb_time_t)); \ + memset(&(ctr_local->gfdb_db_record.gfdb_unwind_change_time), 0, \ + sizeof(gfdb_time_t)); \ + gf_uuid_clear(ctr_local->gfdb_db_record.gfid); \ + gf_uuid_clear(ctr_local->gfdb_db_record.pargfid); \ + memset(ctr_local->gfdb_db_record.file_name, 0, GF_NAME_MAX + 1); \ + memset(ctr_local->gfdb_db_record.old_file_name, 0, GF_NAME_MAX + 1); \ + ctr_local->gfdb_db_record.gfdb_fop_type = GFDB_FOP_INVALID_OP; \ + ctr_local->ia_inode_type = IA_INVAL; \ + } while (0) + +static gf_ctr_local_t * +init_ctr_local_t(xlator_t *this) +{ + gf_ctr_local_t *ctr_local = NULL; + + GF_ASSERT(this); + + ctr_local = mem_get0(this->local_pool); + if (!ctr_local) { + gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, + CTR_MSG_CREATE_CTR_LOCAL_ERROR_WIND, + "Error while creating ctr local"); + goto out; + } + + CLEAR_CTR_DB_RECORD(ctr_local); +out: + return ctr_local; +} + +static void +free_ctr_local(gf_ctr_local_t *ctr_local) +{ + if (ctr_local) + mem_put(ctr_local); +} + +/****************************************************************************** + * + * + * Context Carrier Structures + * + * + * ****************************************************************************/ + +/* + * Context Carrier structures are used to carry relevant information about + * inodes and links from the fops calls to the ctr_insert_wind. + * These structure just have pointers to the original data and donot + * do a deep copy of any data. This info is deep copied to + * ctr_local->gfdb_db_record and passed to insert_record() api of libgfdb. This + * info remains persistent for the unwind in ctr_local->gfdb_db_record + * and once used will be destroyed. + * + * gf_ctr_link_context_t : Context structure for hard links + * gf_ctr_inode_context_t : Context structure for inodes + * + * */ + +/*Context Carrier Structure for hard links*/ +typedef struct gf_ctr_link_context { + uuid_t *pargfid; + const char *basename; +} gf_ctr_link_context_t; + +/*Context Carrier Structure for inodes*/ +typedef struct gf_ctr_inode_context { + ia_type_t ia_type; + uuid_t *gfid; + uuid_t *old_gfid; + gf_ctr_link_context_t *new_link_cx; + gf_ctr_link_context_t *old_link_cx; + gfdb_fop_type_t fop_type; + gfdb_fop_path_t fop_path; + gf_boolean_t is_internal_fop; + /* Indicating metadata fops */ + gf_boolean_t is_metadata_fop; +} gf_ctr_inode_context_t; + +/*******************Util Macros for Context Carrier Structures*****************/ + +/*Checks if ctr_link_cx is sane!*/ +#define IS_CTR_LINK_CX_SANE(ctr_link_cx) \ + do { \ + if (ctr_link_cx) { \ + if (ctr_link_cx->pargfid) \ + GF_ASSERT(*(ctr_link_cx->pargfid)); \ + GF_ASSERT(ctr_link_cx->basename); \ + }; \ + } while (0) + +/*Clear and fill the ctr_link_context with values*/ +#define FILL_CTR_LINK_CX(ctr_link_cx, _pargfid, _basename, label) \ + do { \ + GF_VALIDATE_OR_GOTO("ctr", ctr_link_cx, label); \ + GF_VALIDATE_OR_GOTO("ctr", _pargfid, label); \ + GF_VALIDATE_OR_GOTO("ctr", _basename, label); \ + memset(ctr_link_cx, 0, sizeof(*ctr_link_cx)); \ + ctr_link_cx->pargfid = &_pargfid; \ + ctr_link_cx->basename = _basename; \ + } while (0) + +#define NEW_LINK_CX(ctr_inode_cx) ctr_inode_cx->new_link_cx + +#define OLD_LINK_CX(ctr_inode_cx) ctr_inode_cx->old_link_cx + +/*Checks if ctr_inode_cx is sane!*/ +#define IS_CTR_INODE_CX_SANE(ctr_inode_cx) \ + do { \ + GF_ASSERT(ctr_inode_cx); \ + GF_ASSERT(ctr_inode_cx->gfid); \ + GF_ASSERT(*(ctr_inode_cx->gfid)); \ + GF_ASSERT(ctr_inode_cx->fop_type != GFDB_FOP_INVALID_OP); \ + GF_ASSERT(ctr_inode_cx->fop_path != GFDB_FOP_INVALID); \ + IS_CTR_LINK_CX_SANE(NEW_LINK_CX(ctr_inode_cx)); \ + IS_CTR_LINK_CX_SANE(OLD_LINK_CX(ctr_inode_cx)); \ + } while (0) + +/*Clear and fill the ctr_inode_context with values*/ +#define FILL_CTR_INODE_CONTEXT(ctr_inode_cx, _ia_type, _gfid, _new_link_cx, \ + _old_link_cx, _fop_type, _fop_path) \ + do { \ + GF_ASSERT(ctr_inode_cx); \ + GF_ASSERT(_gfid); \ + GF_ASSERT(_fop_type != GFDB_FOP_INVALID_OP); \ + GF_ASSERT(_fop_path != GFDB_FOP_INVALID); \ + memset(ctr_inode_cx, 0, sizeof(*ctr_inode_cx)); \ + ctr_inode_cx->ia_type = _ia_type; \ + ctr_inode_cx->gfid = &_gfid; \ + IS_CTR_LINK_CX_SANE(NEW_LINK_CX(ctr_inode_cx)); \ + if (_new_link_cx) \ + NEW_LINK_CX(ctr_inode_cx) = _new_link_cx; \ + IS_CTR_LINK_CX_SANE(OLD_LINK_CX(ctr_inode_cx)); \ + if (_old_link_cx) \ + OLD_LINK_CX(ctr_inode_cx) = _old_link_cx; \ + ctr_inode_cx->fop_type = _fop_type; \ + ctr_inode_cx->fop_path = _fop_path; \ + } while (0) + +/****************************************************************************** + * + * Util functions or macros used by + * insert wind and insert unwind + * + * ****************************************************************************/ +/* Free ctr frame local */ +static inline void +ctr_free_frame_local(call_frame_t *frame) +{ + if (frame) { + free_ctr_local((gf_ctr_local_t *)frame->local); + frame->local = NULL; + } +} + +/* Setting GF_REQUEST_LINK_COUNT_XDATA in dict + * that has to be sent to POSIX Xlator to send + * link count in unwind path. + * return 0 for success with not creation of dict + * return 1 for success with creation of dict + * return -1 for failure. + * */ +static inline int +set_posix_link_request(xlator_t *this, dict_t **xdata) +{ + int ret = -1; + gf_boolean_t is_created = _gf_false; + + GF_VALIDATE_OR_GOTO("ctr", this, out); + GF_VALIDATE_OR_GOTO(this->name, xdata, out); + + /*create xdata if NULL*/ + if (!*xdata) { + *xdata = dict_new(); + is_created = _gf_true; + ret = 1; + } else { + ret = 0; + } + + if (!*xdata) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_XDATA_NULL, + "xdata is NULL :Cannot send " + "GF_REQUEST_LINK_COUNT_XDATA to posix"); + ret = -1; + goto out; + } + + ret = dict_set_int32(*xdata, GF_REQUEST_LINK_COUNT_XDATA, 1); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_SET_CTR_RESPONSE_LINK_COUNT_XDATA_FAILED, + "Failed setting GF_REQUEST_LINK_COUNT_XDATA"); + ret = -1; + goto out; + } + ret = 0; +out: + if (ret == -1) { + if (*xdata && is_created) { + dict_unref(*xdata); + } + } + return ret; +} + +/* + * If a bitrot fop + * */ +#define BITROT_FOP(frame) \ + (frame->root->pid == GF_CLIENT_PID_BITD || \ + frame->root->pid == GF_CLIENT_PID_SCRUB) + +/* + * If a rebalancer fop + * */ +#define REBALANCE_FOP(frame) (frame->root->pid == GF_CLIENT_PID_DEFRAG) + +/* + * If its a tiering rebalancer fop + * */ +#define TIER_REBALANCE_FOP(frame) \ + (frame->root->pid == GF_CLIENT_PID_TIER_DEFRAG) + +/* + * If its a AFR SELF HEAL + * */ +#define AFR_SELF_HEAL_FOP(frame) (frame->root->pid == GF_CLIENT_PID_SELF_HEALD) + +/* + * if a rebalancer fop goto + * */ +#define CTR_IF_REBALANCE_FOP_THEN_GOTO(frame, label) \ + do { \ + if (REBALANCE_FOP(frame)) \ + goto label; \ + } while (0) + +/* + * Internal fop + * + * */ +static inline gf_boolean_t +is_internal_fop(call_frame_t *frame, dict_t *xdata) +{ + gf_boolean_t ret = _gf_false; + + GF_ASSERT(frame); + GF_ASSERT(frame->root); + + if (AFR_SELF_HEAL_FOP(frame)) { + ret = _gf_true; + } + if (BITROT_FOP(frame)) { + ret = _gf_true; + } + if (REBALANCE_FOP(frame) || TIER_REBALANCE_FOP(frame)) { + ret = _gf_true; + if (xdata && dict_get(xdata, CTR_ATTACH_TIER_LOOKUP)) { + ret = _gf_false; + } + } + if (xdata && dict_get(xdata, GLUSTERFS_INTERNAL_FOP_KEY)) { + ret = _gf_true; + } + + return ret; +} + +#define CTR_IF_INTERNAL_FOP_THEN_GOTO(frame, dict, label) \ + do { \ + if (is_internal_fop(frame, dict)) \ + goto label; \ + } while (0) + +/* if fop has failed exit */ +#define CTR_IF_FOP_FAILED_THEN_GOTO(this, op_ret, op_errno, label) \ + do { \ + if (op_ret == -1) { \ + gf_msg_trace(this->name, 0, "Failed fop with %s", \ + strerror(op_errno)); \ + goto label; \ + }; \ + } while (0) + +/* + * IS CTR Xlator is disabled then goto to label + * */ +#define CTR_IS_DISABLED_THEN_GOTO(this, label) \ + do { \ + gf_ctr_private_t *_priv = NULL; \ + GF_ASSERT(this); \ + GF_ASSERT(this->private); \ + _priv = this->private; \ + if (!_priv->_db_conn) \ + goto label; \ + } while (0) + +/* + * IS CTR record metadata heat is disabled then goto to label + * */ +#define CTR_RECORD_METADATA_HEAT_IS_DISABLED_THEN_GOTO(this, label) \ + do { \ + gf_ctr_private_t *_priv = NULL; \ + GF_ASSERT(this); \ + GF_ASSERT(this->private); \ + _priv = this->private; \ + if (!_priv->ctr_record_metadata_heat) \ + goto label; \ + } while (0) + +int +fill_db_record_for_unwind(xlator_t *this, gf_ctr_local_t *ctr_local, + gfdb_fop_type_t fop_type, gfdb_fop_path_t fop_path); + +int +fill_db_record_for_wind(xlator_t *this, gf_ctr_local_t *ctr_local, + gf_ctr_inode_context_t *ctr_inode_cx); + +/******************************************************************************* + * CTR INSERT WIND + * ***************************************************************************** + * Function used to insert/update record into the database during a wind fop + * This function creates ctr_local structure into the frame of the fop + * call. + * ****************************************************************************/ + +static inline int +ctr_insert_wind(call_frame_t *frame, xlator_t *this, + gf_ctr_inode_context_t *ctr_inode_cx) +{ + int ret = -1; + gf_ctr_private_t *_priv = NULL; + gf_ctr_local_t *ctr_local = NULL; + + GF_ASSERT(frame); + GF_ASSERT(frame->root); + GF_ASSERT(this); + IS_CTR_INODE_CX_SANE(ctr_inode_cx); + + _priv = this->private; + GF_ASSERT(_priv); + + GF_ASSERT(_priv->_db_conn); + + /*If record_wind option of CTR is on record wind for + * regular files only*/ + if (_priv->ctr_record_wind && ctr_inode_cx->ia_type != IA_IFDIR) { + frame->local = init_ctr_local_t(this); + if (!frame->local) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_CREATE_CTR_LOCAL_ERROR_WIND, + "WIND: Error while creating ctr local"); + goto out; + }; + ctr_local = frame->local; + ctr_local->client_pid = frame->root->pid; + ctr_local->is_internal_fop = ctr_inode_cx->is_internal_fop; + + /* Decide whether to record counters or not */ + CTR_DB_REC(ctr_local).do_record_counters = _gf_false; + /* If record counter is enabled */ + if (_priv->ctr_record_counter) { + /* If not a internal fop */ + if (!(ctr_local->is_internal_fop)) { + /* If its a metadata fop AND + * record metadata heat + * OR + * its NOT a metadata fop */ + if ((ctr_inode_cx->is_metadata_fop && + _priv->ctr_record_metadata_heat) || + (!ctr_inode_cx->is_metadata_fop)) { + CTR_DB_REC(ctr_local).do_record_counters = _gf_true; + } + } + } + + /* Decide whether to record times or not + * For non internal FOPS record times as usual*/ + CTR_DB_REC(ctr_local).do_record_times = _gf_false; + if (!ctr_local->is_internal_fop) { + /* If its a metadata fop AND + * record metadata heat + * OR + * its NOT a metadata fop */ + if ((ctr_inode_cx->is_metadata_fop && + _priv->ctr_record_metadata_heat) || + (!ctr_inode_cx->is_metadata_fop)) { + CTR_DB_REC(ctr_local).do_record_times = + (_priv->ctr_record_wind || _priv->ctr_record_unwind); + } + } + /* when its a internal FOPS*/ + else { + /* Record times only for create + * i.e when the inode is created */ + CTR_DB_REC(ctr_local).do_record_times = (isdentrycreatefop( + ctr_inode_cx->fop_type)) + ? _gf_true + : _gf_false; + } + + /*Fill the db record for insertion*/ + ret = fill_db_record_for_wind(this, ctr_local, ctr_inode_cx); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_FILL_CTR_LOCAL_ERROR_WIND, + "WIND: Error filling ctr local"); + goto out; + } + + /*Insert the db record*/ + ret = insert_record(_priv->_db_conn, &ctr_local->gfdb_db_record); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_INSERT_RECORD_WIND_FAILED, + "WIND: Inserting of record failed!"); + goto out; + } + } + ret = 0; +out: + + if (ret) { + free_ctr_local(ctr_local); + frame->local = NULL; + } + + return ret; +} + +/******************************************************************************* + * CTR INSERT UNWIND + * ***************************************************************************** + * Function used to insert/update record into the database during a unwind fop + * This function destroys ctr_local structure into the frame of the fop + * call at the end. + * ****************************************************************************/ +static inline int +ctr_insert_unwind(call_frame_t *frame, xlator_t *this, gfdb_fop_type_t fop_type, + gfdb_fop_path_t fop_path) +{ + int ret = -1; + gf_ctr_private_t *_priv = NULL; + gf_ctr_local_t *ctr_local = NULL; + + GF_ASSERT(frame); + GF_ASSERT(this); + + _priv = this->private; + GF_ASSERT(_priv); + + GF_ASSERT(_priv->_db_conn); + + ctr_local = frame->local; + + if (ctr_local && (_priv->ctr_record_unwind || isdentryfop(fop_type)) && + (ctr_local->ia_inode_type != IA_IFDIR)) { + CTR_DB_REC(ctr_local).do_record_uwind_time = _priv->ctr_record_unwind; + + ret = fill_db_record_for_unwind(this, ctr_local, fop_type, fop_path); + if (ret == -1) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_FILL_CTR_LOCAL_ERROR_UNWIND, + "UNWIND: Error filling ctr local"); + goto out; + } + + ret = insert_record(_priv->_db_conn, &ctr_local->gfdb_db_record); + if (ret == -1) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_FILL_CTR_LOCAL_ERROR_UNWIND, + "UNWIND: Error filling ctr local"); + goto out; + } + } + ret = 0; +out: + return ret; +} + +/****************************************************************************** + * Delete file/flink record/s from db + * ****************************************************************************/ +static inline int +ctr_delete_hard_link_from_db(xlator_t *this, uuid_t gfid, uuid_t pargfid, + char *basename, gfdb_fop_type_t fop_type, + gfdb_fop_path_t fop_path) +{ + int ret = -1; + gfdb_db_record_t gfdb_db_record; + gf_ctr_private_t *_priv = NULL; + + _priv = this->private; + GF_VALIDATE_OR_GOTO(this->name, _priv, out); + GF_VALIDATE_OR_GOTO(this->name, (!gf_uuid_is_null(gfid)), out); + GF_VALIDATE_OR_GOTO(this->name, (!gf_uuid_is_null(pargfid)), out); + GF_VALIDATE_OR_GOTO(this->name, (fop_type == GFDB_FOP_DENTRY_WRITE), out); + GF_VALIDATE_OR_GOTO( + this->name, (fop_path == GFDB_FOP_UNDEL || GFDB_FOP_UNDEL_ALL), out); + + /* Set gfdb_db_record to 0 */ + memset(&gfdb_db_record, 0, sizeof(gfdb_db_record)); + + /* Copy basename */ + if (snprintf(gfdb_db_record.file_name, GF_NAME_MAX, "%s", basename) >= + GF_NAME_MAX) + goto out; + + /* Copy gfid into db record */ + gf_uuid_copy(gfdb_db_record.gfid, gfid); + + /* Copy pargid into db record */ + gf_uuid_copy(gfdb_db_record.pargfid, pargfid); + + gfdb_db_record.gfdb_fop_path = fop_path; + gfdb_db_record.gfdb_fop_type = fop_type; + + /*send delete request to db*/ + ret = insert_record(_priv->_db_conn, &gfdb_db_record); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_RECORD_WIND_FAILED, + "Failed to delete record. %s", basename); + goto out; + } + + ret = 0; +out: + return ret; +} + +/******************************* Hard link function ***************************/ + +static inline gf_boolean_t +__is_inode_expired(ctr_xlator_ctx_t *ctr_xlator_ctx, gf_ctr_private_t *_priv, + gfdb_time_t *current_time) +{ + gf_boolean_t ret = _gf_false; + uint64_t time_diff = 0; + + GF_ASSERT(ctr_xlator_ctx); + GF_ASSERT(_priv); + GF_ASSERT(current_time); + + time_diff = current_time->tv_sec - ctr_xlator_ctx->inode_heal_period; + + ret = (time_diff >= _priv->ctr_lookupheal_inode_timeout) ? _gf_true + : _gf_false; + return ret; +} + +static inline gf_boolean_t +__is_hardlink_expired(ctr_hard_link_t *ctr_hard_link, gf_ctr_private_t *_priv, + gfdb_time_t *current_time) +{ + gf_boolean_t ret = _gf_false; + uint64_t time_diff = 0; + + GF_ASSERT(ctr_hard_link); + GF_ASSERT(_priv); + GF_ASSERT(current_time); + + time_diff = current_time->tv_sec - ctr_hard_link->hardlink_heal_period; + + ret = ret || (time_diff >= _priv->ctr_lookupheal_link_timeout) ? _gf_true + : _gf_false; + + return ret; +} + +/* Return values of heal*/ +typedef enum ctr_heal_ret_val { + CTR_CTX_ERROR = -1, + /* No healing required */ + CTR_TRY_NO_HEAL = 0, + /* Try healing hard link */ + CTR_TRY_HARDLINK_HEAL = 1, + /* Try healing inode */ + CTR_TRY_INODE_HEAL = 2, +} ctr_heal_ret_val_t; + +/** + * @brief Function to add hard link to the inode context variable. + * The inode context maintainences a in-memory list. This is used + * smart healing of database. + * @param frame of the FOP + * @param this is the Xlator instant + * @param inode + * @return Return ctr_heal_ret_val_t + */ + +static inline ctr_heal_ret_val_t +add_hard_link_ctx(call_frame_t *frame, xlator_t *this, inode_t *inode) +{ + ctr_heal_ret_val_t ret_val = CTR_TRY_NO_HEAL; + int ret = -1; + gf_ctr_local_t *ctr_local = NULL; + ctr_xlator_ctx_t *ctr_xlator_ctx = NULL; + ctr_hard_link_t *ctr_hard_link = NULL; + gf_ctr_private_t *_priv = NULL; + gfdb_time_t current_time = {0}; + + GF_ASSERT(frame); + GF_ASSERT(this); + GF_ASSERT(inode); + GF_ASSERT(this->private); + + _priv = this->private; + + ctr_local = frame->local; + if (!ctr_local) { + goto out; + } + + ctr_xlator_ctx = init_ctr_xlator_ctx(this, inode); + if (!ctr_xlator_ctx) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_ACCESS_CTR_INODE_CONTEXT_FAILED, + "Failed accessing ctr inode context"); + goto out; + } + + LOCK(&ctr_xlator_ctx->lock); + + /* Check if the hard link already exists + * in the ctr inode context*/ + ctr_hard_link = ctr_search_hard_link_ctx(this, ctr_xlator_ctx, + CTR_DB_REC(ctr_local).pargfid, + CTR_DB_REC(ctr_local).file_name); + /* if there then ignore */ + if (ctr_hard_link) { + ret = gettimeofday(¤t_time, NULL); + if (ret == -1) { + gf_log(this->name, GF_LOG_ERROR, "Failed to get current time"); + ret_val = CTR_CTX_ERROR; + goto unlock; + } + + if (__is_hardlink_expired(ctr_hard_link, _priv, ¤t_time)) { + ctr_hard_link->hardlink_heal_period = current_time.tv_sec; + ret_val = ret_val | CTR_TRY_HARDLINK_HEAL; + } + + if (__is_inode_expired(ctr_xlator_ctx, _priv, ¤t_time)) { + ctr_xlator_ctx->inode_heal_period = current_time.tv_sec; + ret_val = ret_val | CTR_TRY_INODE_HEAL; + } + + goto unlock; + } + + /* Add the hard link to the list*/ + ret = ctr_add_hard_link(this, ctr_xlator_ctx, CTR_DB_REC(ctr_local).pargfid, + CTR_DB_REC(ctr_local).file_name); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_ADD_HARDLINK_TO_CTR_INODE_CONTEXT_FAILED, + "Failed to add hardlink to the ctr inode context"); + ret_val = CTR_CTX_ERROR; + goto unlock; + } + + ret_val = CTR_TRY_NO_HEAL; +unlock: + UNLOCK(&ctr_xlator_ctx->lock); +out: + return ret_val; +} + +static inline int +delete_hard_link_ctx(call_frame_t *frame, xlator_t *this, inode_t *inode) +{ + int ret = -1; + ctr_xlator_ctx_t *ctr_xlator_ctx = NULL; + gf_ctr_local_t *ctr_local = NULL; + + GF_ASSERT(frame); + GF_ASSERT(this); + GF_ASSERT(inode); + + ctr_local = frame->local; + if (!ctr_local) { + goto out; + } + + ctr_xlator_ctx = get_ctr_xlator_ctx(this, inode); + if (!ctr_xlator_ctx) { + /* Since there is no ctr inode context so nothing more to do */ + ret = 0; + goto out; + } + + ret = ctr_delete_hard_link(this, ctr_xlator_ctx, + CTR_DB_REC(ctr_local).pargfid, + CTR_DB_REC(ctr_local).file_name); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_DELETE_HARDLINK_FAILED, + "Failed to delete hard link"); + goto out; + } + + ret = 0; + +out: + return ret; +} + +static inline int +update_hard_link_ctx(call_frame_t *frame, xlator_t *this, inode_t *inode) +{ + int ret = -1; + ctr_xlator_ctx_t *ctr_xlator_ctx = NULL; + gf_ctr_local_t *ctr_local = NULL; + + GF_ASSERT(frame); + GF_ASSERT(this); + GF_ASSERT(inode); + + ctr_local = frame->local; + if (!ctr_local) { + goto out; + } + + ctr_xlator_ctx = init_ctr_xlator_ctx(this, inode); + if (!ctr_xlator_ctx) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_ACCESS_CTR_INODE_CONTEXT_FAILED, + "Failed accessing ctr inode context"); + goto out; + } + + ret = ctr_update_hard_link( + this, ctr_xlator_ctx, CTR_DB_REC(ctr_local).pargfid, + CTR_DB_REC(ctr_local).file_name, CTR_DB_REC(ctr_local).old_pargfid, + CTR_DB_REC(ctr_local).old_file_name); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_DELETE_HARDLINK_FAILED, + "Failed to delete hard link"); + goto out; + } + + ret = 0; + +out: + return ret; +} + +/****************************************************************************** + * + * CTR xlator init related functions + * + * + * ****************************************************************************/ +int +extract_db_params(xlator_t *this, dict_t *params_dict, gfdb_db_type_t db_type); + +int +extract_ctr_options(xlator_t *this, gf_ctr_private_t *_priv); + +#endif diff --git a/xlators/features/changetimerecorder/src/ctr-messages.h b/xlators/features/changetimerecorder/src/ctr-messages.h new file mode 100644 index 0000000..23adf0a --- /dev/null +++ b/xlators/features/changetimerecorder/src/ctr-messages.h @@ -0,0 +1,61 @@ +/* + Copyright (c) 2013 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. + */ + +#ifndef _CTR_MESSAGES_H_ +#define _CTR_MESSAGES_H_ + +#include + +/* To add new message IDs, append new identifiers at the end of the list. + * + * Never remove a message ID. If it's not used anymore, you can rename it or + * leave it as it is, but not delete it. This is to prevent reutilization of + * IDs by other messages. + * + * The component name must match one of the entries defined in + * glfs-message-id.h. + */ + +GLFS_MSGID( + CTR, CTR_MSG_CREATE_CTR_LOCAL_ERROR_WIND, + CTR_MSG_FILL_CTR_LOCAL_ERROR_UNWIND, CTR_MSG_FILL_CTR_LOCAL_ERROR_WIND, + CTR_MSG_INSERT_LINK_WIND_FAILED, CTR_MSG_INSERT_WRITEV_WIND_FAILED, + CTR_MSG_INSERT_WRITEV_UNWIND_FAILED, CTR_MSG_INSERT_SETATTR_WIND_FAILED, + CTR_MSG_INSERT_SETATTR_UNWIND_FAILED, + CTR_MSG_INSERT_FREMOVEXATTR_UNWIND_FAILED, + CTR_MSG_INSERT_FREMOVEXATTR_WIND_FAILED, + CTR_MSG_INSERT_REMOVEXATTR_WIND_FAILED, + CTR_MSG_INSERT_REMOVEXATTR_UNWIND_FAILED, + CTR_MSG_INSERT_TRUNCATE_WIND_FAILED, CTR_MSG_INSERT_TRUNCATE_UNWIND_FAILED, + CTR_MSG_INSERT_FTRUNCATE_UNWIND_FAILED, + CTR_MSG_INSERT_FTRUNCATE_WIND_FAILED, CTR_MSG_INSERT_RENAME_WIND_FAILED, + CTR_MSG_INSERT_RENAME_UNWIND_FAILED, + CTR_MSG_ACCESS_CTR_INODE_CONTEXT_FAILED, CTR_MSG_ADD_HARDLINK_FAILED, + CTR_MSG_DELETE_HARDLINK_FAILED, CTR_MSG_UPDATE_HARDLINK_FAILED, + CTR_MSG_GET_CTR_RESPONSE_LINK_COUNT_XDATA_FAILED, + CTR_MSG_SET_CTR_RESPONSE_LINK_COUNT_XDATA_FAILED, + CTR_MSG_INSERT_UNLINK_UNWIND_FAILED, CTR_MSG_INSERT_UNLINK_WIND_FAILED, + CTR_MSG_XDATA_NULL, CTR_MSG_INSERT_FSYNC_WIND_FAILED, + CTR_MSG_INSERT_FSYNC_UNWIND_FAILED, CTR_MSG_INSERT_MKNOD_UNWIND_FAILED, + CTR_MSG_INSERT_MKNOD_WIND_FAILED, CTR_MSG_INSERT_CREATE_WIND_FAILED, + CTR_MSG_INSERT_CREATE_UNWIND_FAILED, CTR_MSG_INSERT_RECORD_WIND_FAILED, + CTR_MSG_INSERT_READV_WIND_FAILED, CTR_MSG_GET_GFID_FROM_DICT_FAILED, + CTR_MSG_SET, CTR_MSG_FATAL_ERROR, CTR_MSG_DANGLING_VOLUME, + CTR_MSG_CALLOC_FAILED, CTR_MSG_EXTRACT_CTR_XLATOR_OPTIONS_FAILED, + CTR_MSG_INIT_DB_PARAMS_FAILED, CTR_MSG_CREATE_LOCAL_MEMORY_POOL_FAILED, + CTR_MSG_MEM_ACC_INIT_FAILED, CTR_MSG_CLOSE_DB_CONN_FAILED, + CTR_MSG_FILL_UNWIND_TIME_REC_ERROR, CTR_MSG_WRONG_FOP_PATH, + CTR_MSG_CONSTRUCT_DB_PATH_FAILED, CTR_MSG_SET_VALUE_TO_SQL_PARAM_FAILED, + CTR_MSG_XLATOR_DISABLED, CTR_MSG_HARDLINK_MISSING_IN_LIST, + CTR_MSG_ADD_HARDLINK_TO_LIST_FAILED, CTR_MSG_INIT_LOCK_FAILED, + CTR_MSG_COPY_FAILED, CTR_MSG_EXTRACT_DB_PARAM_OPTIONS_FAILED, + CTR_MSG_ADD_HARDLINK_TO_CTR_INODE_CONTEXT_FAILED, CTR_MSG_NULL_LOCAL); + +#endif /* !_CTR_MESSAGES_H_ */ diff --git a/xlators/features/changetimerecorder/src/ctr-xlator-ctx.c b/xlators/features/changetimerecorder/src/ctr-xlator-ctx.c new file mode 100644 index 0000000..b6b66d5 --- /dev/null +++ b/xlators/features/changetimerecorder/src/ctr-xlator-ctx.c @@ -0,0 +1,362 @@ +/* + Copyright (c) 2015 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#include "ctr-xlator-ctx.h" +#include "ctr-messages.h" +#include +#include + +#define IS_THE_ONLY_HARDLINK(ctr_hard_link) \ + (ctr_hard_link->list.next == ctr_hard_link->list.prev) + +static void +fini_ctr_hard_link(ctr_hard_link_t **ctr_hard_link) +{ + GF_ASSERT(ctr_hard_link); + + if (*ctr_hard_link) + return; + GF_FREE((*ctr_hard_link)->base_name); + GF_FREE(*ctr_hard_link); + *ctr_hard_link = NULL; +} + +/* Please lock the ctr_xlator_ctx before using this function */ +ctr_hard_link_t * +ctr_search_hard_link_ctx(xlator_t *this, ctr_xlator_ctx_t *ctr_xlator_ctx, + uuid_t pgfid, const char *base_name) +{ + ctr_hard_link_t *_hard_link = NULL; + ctr_hard_link_t *searched_hardlink = NULL; + + GF_ASSERT(this); + GF_ASSERT(ctr_xlator_ctx); + + if (pgfid == NULL || base_name == NULL) + goto out; + + /*linear search*/ + list_for_each_entry(_hard_link, &ctr_xlator_ctx->hardlink_list, list) + { + if (gf_uuid_compare(_hard_link->pgfid, pgfid) == 0 && + _hard_link->base_name && + strcmp(_hard_link->base_name, base_name) == 0) { + searched_hardlink = _hard_link; + break; + } + } + +out: + return searched_hardlink; +} + +/* Please lock the ctr_xlator_ctx before using this function */ +int +ctr_add_hard_link(xlator_t *this, ctr_xlator_ctx_t *ctr_xlator_ctx, + uuid_t pgfid, const char *base_name) +{ + int ret = -1; + ctr_hard_link_t *ctr_hard_link = NULL; + struct timeval current_time = {0}; + + GF_ASSERT(this); + GF_ASSERT(ctr_xlator_ctx); + + if (pgfid == NULL || base_name == NULL) + goto out; + + ctr_hard_link = GF_CALLOC(1, sizeof(*ctr_hard_link), gf_ctr_mt_hard_link_t); + if (!ctr_hard_link) { + gf_msg(this->name, GF_LOG_ERROR, ENOMEM, CTR_MSG_CALLOC_FAILED, + "Failed allocating " + "ctr_hard_link"); + goto out; + } + + /*Initialize the ctr_hard_link object and + * Assign the values : parent GFID and basename*/ + INIT_LIST_HEAD(&ctr_hard_link->list); + gf_uuid_copy(ctr_hard_link->pgfid, pgfid); + ret = gf_asprintf(&ctr_hard_link->base_name, "%s", base_name); + if (ret < 0) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_COPY_FAILED, + "Failed copying basename" + "to ctr_hard_link"); + goto error; + } + + ret = gettimeofday(¤t_time, NULL); + if (ret == -1) { + gf_log(this->name, GF_LOG_ERROR, "Failed to get current time"); + goto error; + } + + /*Add the hard link to the list*/ + list_add_tail(&ctr_hard_link->list, &ctr_xlator_ctx->hardlink_list); + + ctr_hard_link->hardlink_heal_period = current_time.tv_sec; + + /*aal izz well!*/ + ret = 0; + goto out; +error: + GF_FREE(ctr_hard_link); +out: + return ret; +} + +static void +__delete_hard_link_from_list(ctr_hard_link_t **ctr_hard_link) +{ + GF_ASSERT(ctr_hard_link); + GF_ASSERT(*ctr_hard_link); + + /*Remove hard link from list*/ + list_del(&(*ctr_hard_link)->list); + fini_ctr_hard_link(ctr_hard_link); +} + +int +ctr_delete_hard_link(xlator_t *this, ctr_xlator_ctx_t *ctr_xlator_ctx, + uuid_t pgfid, const char *base_name) +{ + int ret = -1; + ctr_hard_link_t *ctr_hard_link = NULL; + + GF_ASSERT(this); + GF_ASSERT(ctr_xlator_ctx); + + LOCK(&ctr_xlator_ctx->lock); + + /*Check if the hard link is present */ + ctr_hard_link = ctr_search_hard_link_ctx(this, ctr_xlator_ctx, pgfid, + base_name); + if (!ctr_hard_link) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_HARDLINK_MISSING_IN_LIST, + "Hard link doesn't exist in the list"); + goto out; + } + + __delete_hard_link_from_list(&ctr_hard_link); + ctr_hard_link = NULL; + + ret = 0; +out: + UNLOCK(&ctr_xlator_ctx->lock); + + return ret; +} + +int +ctr_update_hard_link(xlator_t *this, ctr_xlator_ctx_t *ctr_xlator_ctx, + uuid_t pgfid, const char *base_name, uuid_t old_pgfid, + const char *old_base_name) +{ + int ret = -1; + ctr_hard_link_t *ctr_hard_link = NULL; + struct timeval current_time = {0}; + + GF_ASSERT(this); + GF_ASSERT(ctr_xlator_ctx); + + LOCK(&ctr_xlator_ctx->lock); + + /*Check if the hard link is present */ + ctr_hard_link = ctr_search_hard_link_ctx(this, ctr_xlator_ctx, old_pgfid, + old_base_name); + if (!ctr_hard_link) { + gf_msg_trace(this->name, 0, + "Hard link doesn't exist" + " in the list"); + /* Since the hard link is not present in the list + * we add it to the list */ + ret = ctr_add_hard_link(this, ctr_xlator_ctx, pgfid, base_name); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, + CTR_MSG_ADD_HARDLINK_TO_LIST_FAILED, + "Failed adding hard link to the list"); + goto out; + } + ret = 0; + goto out; + } + + /* update the hard link */ + gf_uuid_copy(ctr_hard_link->pgfid, pgfid); + GF_FREE(ctr_hard_link->base_name); + ret = gf_asprintf(&ctr_hard_link->base_name, "%s", base_name); + if (ret < 0) { + gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_COPY_FAILED, + "Failed copying basename" + "to ctr_hard_link"); + /* delete the corrupted entry */ + __delete_hard_link_from_list(&ctr_hard_link); + ctr_hard_link = NULL; + goto out; + } + + ret = gettimeofday(¤t_time, NULL); + if (ret == -1) { + gf_log(this->name, GF_LOG_ERROR, "Failed to get current time"); + ctr_hard_link->hardlink_heal_period = 0; + } else { + ctr_hard_link->hardlink_heal_period = current_time.tv_sec; + } + + ret = 0; + +out: + UNLOCK(&ctr_xlator_ctx->lock); + + return ret; +} + +/* Delete all hardlinks */ +static int +ctr_delete_all_hard_link(xlator_t *this, ctr_xlator_ctx_t *ctr_xlator_ctx) +{ + int ret = -1; + ctr_hard_link_t *ctr_hard_link = NULL; + ctr_hard_link_t *tmp = NULL; + + GF_ASSERT(ctr_xlator_ctx); + + LOCK(&ctr_xlator_ctx->lock); + + list_for_each_entry_safe(ctr_hard_link, tmp, &ctr_xlator_ctx->hardlink_list, + list) + { + /*Remove hard link from list*/ + __delete_hard_link_from_list(&ctr_hard_link); + ctr_hard_link = NULL; + } + + UNLOCK(&ctr_xlator_ctx->lock); + + ret = 0; + + return ret; +} + +/* Please lock the inode before using this function */ +static ctr_xlator_ctx_t * +__get_ctr_xlator_ctx(xlator_t *this, inode_t *inode) +{ + int ret = 0; + uint64_t _addr = 0; + ctr_xlator_ctx_t *ctr_xlator_ctx = NULL; + + GF_ASSERT(this); + GF_ASSERT(inode); + + ret = __inode_ctx_get(inode, this, &_addr); + if (ret < 0) + _addr = 0; + if (_addr != 0) { + ctr_xlator_ctx = (ctr_xlator_ctx_t *)(long)_addr; + } + + return ctr_xlator_ctx; +} + +ctr_xlator_ctx_t * +init_ctr_xlator_ctx(xlator_t *this, inode_t *inode) +{ + int ret = -1; + uint64_t _addr = 0; + ctr_xlator_ctx_t *ctr_xlator_ctx = NULL; + struct timeval current_time = {0}; + + GF_ASSERT(this); + GF_ASSERT(inode); + + LOCK(&inode->lock); + { + ctr_xlator_ctx = __get_ctr_xlator_ctx(this, inode); + if (ctr_xlator_ctx) { + ret = 0; + goto out; + } + ctr_xlator_ctx = GF_CALLOC(1, sizeof(*ctr_xlator_ctx), + gf_ctr_mt_xlator_ctx); + if (!ctr_xlator_ctx) + goto out; + + ret = LOCK_INIT(&ctr_xlator_ctx->lock); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, ret, CTR_MSG_INIT_LOCK_FAILED, + "Failed init lock %s", strerror(ret)); + goto out; + } + _addr = (uint64_t)(uintptr_t)ctr_xlator_ctx; + + ret = __inode_ctx_set(inode, this, &_addr); + if (ret) { + goto out; + } + + INIT_LIST_HEAD(&ctr_xlator_ctx->hardlink_list); + + ret = gettimeofday(¤t_time, NULL); + if (ret == -1) { + gf_log(this->name, GF_LOG_ERROR, "Failed to get current time"); + goto out; + } + + ctr_xlator_ctx->inode_heal_period = current_time.tv_sec; + } + ret = 0; +out: + if (ret) { + GF_FREE(ctr_xlator_ctx); + ctr_xlator_ctx = NULL; + } + + UNLOCK(&inode->lock); + + return ctr_xlator_ctx; +} + +void +fini_ctr_xlator_ctx(xlator_t *this, inode_t *inode) +{ + int ret = 0; + uint64_t _addr = 0; + ctr_xlator_ctx_t *ctr_xlator_ctx = NULL; + + inode_ctx_del(inode, this, &_addr); + if (!_addr) + return; + + ctr_xlator_ctx = (ctr_xlator_ctx_t *)(long)_addr; + + ret = ctr_delete_all_hard_link(this, ctr_xlator_ctx); + if (ret) { + gf_msg(this->name, GF_LOG_WARNING, 0, CTR_MSG_DELETE_HARDLINK_FAILED, + "Failed deleting all " + "hard links from inode context"); + } + + LOCK_DESTROY(&ctr_xlator_ctx->lock); + + GF_FREE(ctr_xlator_ctx); +} + +ctr_xlator_ctx_t * +get_ctr_xlator_ctx(xlator_t *this, inode_t *inode) +{ + ctr_xlator_ctx_t *ctr_xlator_ctx = NULL; + + LOCK(&inode->lock); + ctr_xlator_ctx = __get_ctr_xlator_ctx(this, inode); + UNLOCK(&inode->lock); + + return ctr_xlator_ctx; +} diff --git a/xlators/features/changetimerecorder/src/ctr-xlator-ctx.h b/xlators/features/changetimerecorder/src/ctr-xlator-ctx.h new file mode 100644 index 0000000..4e3bf7e --- /dev/null +++ b/xlators/features/changetimerecorder/src/ctr-xlator-ctx.h @@ -0,0 +1,68 @@ +/* + Copyright (c) 2015 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#ifndef __CTR_XLATOR_CTX_H +#define __CTR_XLATOR_CTX_H + +#include +#include "ctr_mem_types.h" +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct ctr_hard_link { + uuid_t pgfid; + char *base_name; + /* Hardlink expiry : Defines the expiry period after which a + * database heal is attempted. */ + uint64_t hardlink_heal_period; + struct list_head list; +} ctr_hard_link_t; + +typedef struct ctr_xlator_ctx { + /* This represents the looked up hardlinks + * NOTE: This doesn't represent all physical hardlinks of the inode*/ + struct list_head hardlink_list; + uint64_t inode_heal_period; + gf_lock_t lock; +} ctr_xlator_ctx_t; + +ctr_hard_link_t * +ctr_search_hard_link_ctx(xlator_t *this, ctr_xlator_ctx_t *ctr_xlator_ctx, + uuid_t pgfid, const char *base_name); + +int +ctr_add_hard_link(xlator_t *this, ctr_xlator_ctx_t *ctr_xlator_ctx, + uuid_t pgfid, const char *base_name); + +int +ctr_delete_hard_link(xlator_t *this, ctr_xlator_ctx_t *ctr_xlator_ctx, + uuid_t pgfid, const char *base_name); + +int +ctr_update_hard_link(xlator_t *this, ctr_xlator_ctx_t *ctr_xlator_ctx, + uuid_t pgfid, const char *base_name, uuid_t old_pgfid, + const char *old_base_name); + +ctr_xlator_ctx_t * +get_ctr_xlator_ctx(xlator_t *this, inode_t *inode); + +ctr_xlator_ctx_t * +init_ctr_xlator_ctx(xlator_t *this, inode_t *inode); + +void +fini_ctr_xlator_ctx(xlator_t *this, inode_t *inode); + +#endif diff --git a/xlators/features/changetimerecorder/src/ctr_mem_types.h b/xlators/features/changetimerecorder/src/ctr_mem_types.h new file mode 100644 index 0000000..7b8f531 --- /dev/null +++ b/xlators/features/changetimerecorder/src/ctr_mem_types.h @@ -0,0 +1,22 @@ +/* + Copyright (c) 2008-2015 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#ifndef __CTR_MEM_TYPES_H__ +#define __CTR_MEM_TYPES_H__ + +#include "gfdb_mem-types.h" + +enum gf_ctr_mem_types_ { + gf_ctr_mt_private_t = gfdb_mt_end + 1, + gf_ctr_mt_xlator_ctx, + gf_ctr_mt_hard_link_t, + gf_ctr_mt_end +}; +#endif -- 1.8.3.1