3604df
From 994fc131f10953679b7bf70d0603f5ec8f878fd2 Mon Sep 17 00:00:00 2001
3604df
From: Poornima G <pgurusid@redhat.com>
3604df
Date: Sun, 4 Sep 2016 08:27:47 +0530
3604df
Subject: [PATCH 134/141] md-cache, afr: Reduce the window of stale read
3604df
3604df
Problem:
3604df
Consider a replica setup, where one mount writes data to a
3604df
file and the other mount reads the file. In afr, read operations
3604df
are not transaction based, a brick(read subvolume) is chosen as
3604df
a part of lookup or other operations, read is always wound only
3604df
to the read subvolume, even if there was write from a different client
3604df
that failed on this brick. This stale read continues until there is
3604df
a lookup or any write operation from the mount point. Currently, this
3604df
is not a major issue, as a lookup is issued before every read and it will
3604df
switch the read subvolume to a correct one. But with the plan of
3604df
increasing md-cache timeout to 600s, the stale read problem will be
3604df
more pronounced, i.e. stale read can continue for 600s(or more if cascaded
3604df
with readdirp), as there will be no lookups.
3604df
3604df
Solution:
3604df
Afr doesn't have any built-in solution for stale read(without affecting
3604df
the performance). The solution that came up, was to use upcall. When a file
3604df
on any brick is marked bad for the first time, upcall sends a notification
3604df
to all the clients that had recently accessed the file. The solution has
3604df
2 parts:
3604df
- Identifying when a file is marked bad, on any of the bricks,
3604df
  for the first time
3604df
- Client side actions on recieving the notifications
3604df
3604df
Identifying when a file is marked bad on any of the bricks for the first time:
3604df
-----------------------------------------------------------------------------
3604df
The idea is to track xattrop in upcall. xattrop currently comes with 2 afr
3604df
xattrs - afr dirty bit and afr pending xattrs.
3604df
   Dirty xattr is set to 1 before every write, and is unset if write succeeds.
3604df
In certain scenarios, dirty xattr can be 0 and still the file could be bad
3604df
copy. Hence do not track dirty xattr.
3604df
   Pending xattr is set on the good copy, indicating the other bricks that have
3604df
bad copy. It is still not as simple as, notifying when any of the pending xattrs
3604df
change. It could lead to flood of notifcations, in case the other brick is
3604df
completely down or consistantly failing. Hence it is important to notify only
3604df
once, the first time a good copy is marked bad.
3604df
3604df
Client side actions on recieving pending xattr change, notification:
3604df
--------------------------------------------------------------------
3604df
md-cache will invalidate the cache of that file, so that further lookup is
3604df
passed down to afr and hence update the read subvolume. Invalidating only in
3604df
md-cache is not enough, consider the folling oder of opertaions:
3604df
- pending xattr invalidation - invalidate md-cache
3604df
- readdirp on the bad read subvolume - fill md-cache
3604df
- lookup (served from md-cache)
3604df
- read - wound to the old read subvol.
3604df
Hence, along with invalidating md-cache, it is very important to reset the
3604df
read subvolume for that file, in afr.
3604df
3604df
Design Credit: Anuradha Talur, Ravishankar N
3604df
3604df
1. xattrop doesn't carry info saying post op/pre op.
3604df
2. Pre xattrop will have 0 value for all pending xattrs,
3604df
   the cbk of pre xattrop carries the on-disk xattr value.
3604df
   Non zero indicated healing is required.
3604df
3. Post xattrop will have non zero value for any of the
3604df
   pending xattrs, if the fop failed on any of the bricks.
3604df
3604df
Change-Id: I469cbc111714c433984fe1c922be2ef113c25804
3604df
BUG: 1284873
3604df
Signed-off-by: Poornima G <pgurusid@redhat.com>
3604df
Reviewed-on: https://code.engineering.redhat.com/gerrit/87047
3604df
Reviewed-by: Rajesh Joseph <rjoseph@redhat.com>
3604df
Tested-by: Rajesh Joseph <rjoseph@redhat.com>
3604df
---
3604df
 libglusterfs/src/glusterfs.h                  |    3 +
3604df
 tests/bugs/md-cache/afr-stale-read.t          |   44 ++++
3604df
 xlators/cluster/afr/src/afr-common.c          |   41 +++-
3604df
 xlators/cluster/afr/src/afr.h                 |    1 -
3604df
 xlators/features/upcall/src/upcall-internal.c |   60 +++++-
3604df
 xlators/features/upcall/src/upcall.c          |  330 ++++++++++++++++---------
3604df
 xlators/features/upcall/src/upcall.h          |    7 +-
3604df
 7 files changed, 370 insertions(+), 116 deletions(-)
3604df
 create mode 100755 tests/bugs/md-cache/afr-stale-read.t
3604df
3604df
diff --git a/libglusterfs/src/glusterfs.h b/libglusterfs/src/glusterfs.h
3604df
index 8206e23..b5ae685 100644
3604df
--- a/libglusterfs/src/glusterfs.h
3604df
+++ b/libglusterfs/src/glusterfs.h
3604df
@@ -169,6 +169,9 @@
3604df
 #define VIRTUAL_QUOTA_XATTR_CLEANUP_KEY "glusterfs.quota-xattr-cleanup"
3604df
 #define QUOTA_READ_ONLY_KEY "trusted.glusterfs.quota.read-only"
3604df
 
3604df
+/* afr related */
3604df
+#define AFR_XATTR_PREFIX "trusted.afr"
3604df
+
3604df
 /* Index xlator related */
3604df
 #define GF_XATTROP_INDEX_GFID "glusterfs.xattrop_index_gfid"
3604df
 #define GF_XATTROP_ENTRY_CHANGES_GFID "glusterfs.xattrop_entry_changes_gfid"
3604df
diff --git a/tests/bugs/md-cache/afr-stale-read.t b/tests/bugs/md-cache/afr-stale-read.t
3604df
new file mode 100755
3604df
index 0000000..7cee5af
3604df
--- /dev/null
3604df
+++ b/tests/bugs/md-cache/afr-stale-read.t
3604df
@@ -0,0 +1,44 @@
3604df
+#!/bin/bash
3604df
+
3604df
+. $(dirname $0)/../../include.rc
3604df
+#. $(dirname $0)/../../volume.rc
3604df
+
3604df
+cleanup;
3604df
+
3604df
+#Basic checks
3604df
+TEST glusterd
3604df
+TEST pidof glusterd
3604df
+TEST $CLI volume info
3604df
+
3604df
+TEST $CLI volume create $V0 replica 2 $H0:$B0/${V0}{1..2};
3604df
+
3604df
+TEST $CLI volume set $V0 features.cache-invalidation on
3604df
+TEST $CLI volume set $V0 features.cache-invalidation-timeout 600
3604df
+TEST $CLI volume set $V0 performance.cache-invalidation on
3604df
+TEST $CLI volume set $V0 performance.md-cache-timeout 600
3604df
+TEST $CLI volume set $V0 performance.cache-samba-metadata on
3604df
+TEST $CLI volume set $V0 cluster.self-heal-daemon off
3604df
+TEST $CLI volume set $V0 read-subvolume $V0-client-0
3604df
+TEST $CLI volume set $V0 performance.quick-read off
3604df
+
3604df
+TEST $CLI volume start $V0
3604df
+
3604df
+TEST glusterfs --volfile-id=/$V0 --volfile-server=$H0 $M0
3604df
+TEST glusterfs --volfile-id=/$V0 --volfile-server=$H0 $M1
3604df
+
3604df
+#Write some data from M0 and read it from M1,
3604df
+#so that M1 selects a read subvol, and caches the lookup
3604df
+TEST `echo "one" > $M0/file1`
3604df
+EXPECT "one" cat $M1/file1
3604df
+
3604df
+#Fail few writes from M0 on brick-0, as a result of this failure
3604df
+#upcall in brick-0 will invalidate the read subvolume of M1.
3604df
+TEST chattr +i $B0/${V0}1/file1
3604df
+TEST `echo "two" > $M0/file1`
3604df
+TEST `echo "three" > $M0/file1`
3604df
+TEST `echo "four" > $M0/file1`
3604df
+TEST `echo "five" > $M0/file1`
3604df
+
3604df
+EXPECT_WITHIN $MDC_TIMEOUT "five" cat $M1/file1
3604df
+TEST chattr -i $B0/${V0}1/file1
3604df
+cleanup;
3604df
diff --git a/xlators/cluster/afr/src/afr-common.c b/xlators/cluster/afr/src/afr-common.c
3604df
index d819a22..c2922fb 100644
3604df
--- a/xlators/cluster/afr/src/afr-common.c
3604df
+++ b/xlators/cluster/afr/src/afr-common.c
3604df
@@ -32,7 +32,7 @@
3604df
 #include "statedump.h"
3604df
 #include "inode.h"
3604df
 #include "events.h"
3604df
-
3604df
+#include "upcall-utils.h"
3604df
 #include "fd.h"
3604df
 
3604df
 #include "afr-inode-read.h"
3604df
@@ -4102,6 +4102,14 @@ afr_ipc (call_frame_t *frame, xlator_t *this, int32_t op, dict_t *xdata)
3604df
                 goto err;
3604df
 
3604df
         call_cnt = local->call_count;
3604df
+
3604df
+        if (xdata) {
3604df
+                for (i = 0; i < priv->child_count; i++) {
3604df
+                        if (dict_set_int8 (xdata, priv->pending_key[i], 0) < 0)
3604df
+                                goto err;
3604df
+                }
3604df
+        }
3604df
+
3604df
         for (i = 0; i < priv->child_count; i++) {
3604df
                 if (!local->child_up[i])
3604df
                         continue;
3604df
@@ -4339,6 +4347,10 @@ afr_notify (xlator_t *this, int32_t event,
3604df
         dict_t          *output             = NULL;
3604df
         gf_boolean_t    had_quorum          = _gf_false;
3604df
         gf_boolean_t    has_quorum          = _gf_false;
3604df
+        struct gf_upcall *up_data           = NULL;
3604df
+        struct gf_upcall_cache_invalidation *up_ci = NULL;
3604df
+        inode_table_t  *itable              = NULL;
3604df
+        inode_t        *inode               = NULL;
3604df
 
3604df
         priv = this->private;
3604df
 
3604df
@@ -4459,7 +4471,34 @@ afr_notify (xlator_t *this, int32_t event,
3604df
                 case GF_EVENT_SOME_CHILD_DOWN:
3604df
                         priv->last_event[idx] = event;
3604df
                         break;
3604df
+                case GF_EVENT_UPCALL:
3604df
+                        up_data = (struct gf_upcall *)data;
3604df
+                        if (up_data->event_type != GF_UPCALL_CACHE_INVALIDATION)
3604df
+                                break;
3604df
+                        up_ci = (struct gf_upcall_cache_invalidation *)up_data->data;
3604df
+
3604df
+                        /* Since md-cache will be aggressively filtering
3604df
+                         * lookups, the stale read issue will be more
3604df
+                         * pronounced. Hence when a pending xattr is set notify
3604df
+                         * all the md-cache clients to invalidate the existing
3604df
+                         * stat cache and send the lookup next time */
3604df
+                        if (up_ci->dict) {
3604df
+                                for (i = 0; i < priv->child_count; i++) {
3604df
+                                        if (dict_get (up_ci->dict, priv->pending_key[i])) {
3604df
+                                                 ret = dict_set_int8 (up_ci->dict,
3604df
+                                                                      MDC_INVALIDATE_IATT , 0);
3604df
+                                                 break;
3604df
+                                        }
3604df
+                                }
3604df
+                        }
3604df
+                        itable = ((xlator_t *)this->graph->top)->itable;
3604df
+                       /*Internal processes may not have itable for top xlator*/
3604df
+                        if (itable)
3604df
+                                inode = inode_find (itable, up_data->gfid);
3604df
+                        if (inode)
3604df
+                                afr_inode_read_subvol_reset (inode, this);
3604df
 
3604df
+                        break;
3604df
                 default:
3604df
                         propagate = 1;
3604df
                         break;
3604df
diff --git a/xlators/cluster/afr/src/afr.h b/xlators/cluster/afr/src/afr.h
3604df
index d04775d..17b997e 100644
3604df
--- a/xlators/cluster/afr/src/afr.h
3604df
+++ b/xlators/cluster/afr/src/afr.h
3604df
@@ -23,7 +23,6 @@
3604df
 #include "afr-self-heald.h"
3604df
 #include "afr-messages.h"
3604df
 
3604df
-#define AFR_XATTR_PREFIX "trusted.afr"
3604df
 #define AFR_PATHINFO_HEADER "REPLICATE:"
3604df
 #define AFR_SH_READDIR_SIZE_KEY "self-heal-readdir-size"
3604df
 #define AFR_SH_DATA_DOMAIN_FMT "%s:self-heal"
3604df
diff --git a/xlators/features/upcall/src/upcall-internal.c b/xlators/features/upcall/src/upcall-internal.c
3604df
index d3c9b91..c7d7dab 100644
3604df
--- a/xlators/features/upcall/src/upcall-internal.c
3604df
+++ b/xlators/features/upcall/src/upcall-internal.c
3604df
@@ -435,7 +435,36 @@ upcall_reaper_thread_init (xlator_t *this)
3604df
         return ret;
3604df
 }
3604df
 
3604df
+
3604df
 int
3604df
+up_compare_afr_xattr (dict_t *d, char *k, data_t *v, void *tmp)
3604df
+{
3604df
+        dict_t *dict = tmp;
3604df
+
3604df
+        if (!strncmp (k, AFR_XATTR_PREFIX, strlen (AFR_XATTR_PREFIX))
3604df
+            && (!is_data_equal (v, dict_get (dict, k))))
3604df
+                return -1;
3604df
+
3604df
+        return 0;
3604df
+}
3604df
+
3604df
+
3604df
+static void
3604df
+up_filter_afr_xattr (dict_t *xattrs, char *xattr, data_t *v)
3604df
+{
3604df
+        /* Filter the afr pending xattrs, with value 0. Ideally this should
3604df
+         * be executed only in case of xattrop and not in set and removexattr,
3604df
+         * butset and remove xattr fops do not come with keys AFR_XATTR_PREFIX
3604df
+         */
3604df
+        if (!strncmp (xattr, AFR_XATTR_PREFIX, strlen (AFR_XATTR_PREFIX))
3604df
+            && (mem_0filled (v->data, v->len) == 0)) {
3604df
+                dict_del (xattrs, xattr);
3604df
+        }
3604df
+        return;
3604df
+}
3604df
+
3604df
+
3604df
+static int
3604df
 up_filter_unregd_xattr (dict_t *xattrs, char *xattr, data_t *v,
3604df
                         void *regd_xattrs)
3604df
 {
3604df
@@ -446,11 +475,40 @@ up_filter_unregd_xattr (dict_t *xattrs, char *xattr, data_t *v,
3604df
                  * send notification for its change
3604df
                  */
3604df
                 dict_del (xattrs, xattr);
3604df
+                goto out;
3604df
         }
3604df
-
3604df
+        up_filter_afr_xattr (xattrs, xattr, v);
3604df
+out:
3604df
         return 0;
3604df
 }
3604df
 
3604df
+
3604df
+int
3604df
+up_filter_xattr (dict_t *xattr, dict_t *regd_xattrs)
3604df
+{
3604df
+        int ret = 0;
3604df
+
3604df
+        /* Remove the xattrs from the dict, if they are not registered for
3604df
+         * cache invalidation */
3604df
+        ret = dict_foreach (xattr, up_filter_unregd_xattr, regd_xattrs);
3604df
+        return ret;
3604df
+}
3604df
+
3604df
+
3604df
+gf_boolean_t
3604df
+up_invalidate_needed (dict_t *xattrs)
3604df
+{
3604df
+        if (dict_key_count (xattrs) == 0) {
3604df
+                gf_msg_trace ("upcall", 0, "None of xattrs requested for"
3604df
+                              " invalidation, were changed. Nothing to "
3604df
+                              "invalidate");
3604df
+                return _gf_false;
3604df
+        }
3604df
+
3604df
+        return _gf_true;
3604df
+}
3604df
+
3604df
+
3604df
 /*
3604df
  * Given a client, first fetch upcall_entry_t from the inode_ctx client list.
3604df
  * Later traverse through the client list of that upcall entry. If this client
3604df
diff --git a/xlators/features/upcall/src/upcall.c b/xlators/features/upcall/src/upcall.c
3604df
index db4aa19..ba5d118 100644
3604df
--- a/xlators/features/upcall/src/upcall.c
3604df
+++ b/xlators/features/upcall/src/upcall.c
3604df
@@ -29,7 +29,7 @@
3604df
 #include "protocol-common.h"
3604df
 #include "defaults.h"
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_open_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
              int32_t op_ret, int32_t op_errno, fd_t *fd, dict_t *xdata)
3604df
 {
3604df
@@ -56,7 +56,7 @@ out:
3604df
 }
3604df
 
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_open (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags,
3604df
          fd_t *fd, dict_t *xdata)
3604df
 {
3604df
@@ -84,7 +84,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_writev_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
                int op_ret, int op_errno, struct iatt *prebuf,
3604df
                struct iatt *postbuf, dict_t *xdata)
3604df
@@ -111,7 +111,7 @@ out:
3604df
 }
3604df
 
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_writev (call_frame_t *frame, xlator_t *this, fd_t *fd,
3604df
            struct iovec *vector, int count, off_t off, uint32_t flags,
3604df
            struct iobref *iobref, dict_t *xdata)
3604df
@@ -141,7 +141,7 @@ err:
3604df
 }
3604df
 
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_readv_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
               int op_ret, int op_errno,
3604df
               struct iovec *vector, int count, struct iatt *stbuf,
3604df
@@ -170,7 +170,7 @@ out:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_readv (call_frame_t *frame, xlator_t *this,
3604df
           fd_t *fd, size_t size, off_t offset,
3604df
           uint32_t flags, dict_t *xdata)
3604df
@@ -200,7 +200,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_lk_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
            int32_t op_ret, int32_t op_errno, struct gf_flock *lock,
3604df
            dict_t *xdata)
3604df
@@ -227,7 +227,7 @@ out:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_lk (call_frame_t *frame, xlator_t *this,
3604df
        fd_t *fd, int32_t cmd, struct gf_flock *flock, dict_t *xdata)
3604df
 {
3604df
@@ -254,7 +254,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_truncate_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
                  int op_ret, int op_errno, struct iatt *prebuf,
3604df
                  struct iatt *postbuf, dict_t *xdata)
3604df
@@ -282,7 +282,7 @@ out:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_truncate (call_frame_t *frame, xlator_t *this, loc_t *loc, off_t offset,
3604df
              dict_t *xdata)
3604df
 {
3604df
@@ -310,7 +310,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_setattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
                 int op_ret, int op_errno, struct iatt *statpre,
3604df
                 struct iatt *statpost, dict_t *xdata)
3604df
@@ -352,7 +352,7 @@ out:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_setattr (call_frame_t *frame, xlator_t *this, loc_t *loc,
3604df
             struct iatt *stbuf, int32_t valid, dict_t *xdata)
3604df
 {
3604df
@@ -381,7 +381,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_rename_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
                int32_t op_ret, int32_t op_errno, struct iatt *stbuf,
3604df
                struct iatt *preoldparent, struct iatt *postoldparent,
3604df
@@ -412,7 +412,7 @@ out:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_rename (call_frame_t *frame, xlator_t *this,
3604df
           loc_t *oldloc, loc_t *newloc, dict_t *xdata)
3604df
 {
3604df
@@ -443,7 +443,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_unlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
                int op_ret, int op_errno, struct iatt *preparent,
3604df
                struct iatt *postparent, dict_t *xdata)
3604df
@@ -471,7 +471,7 @@ out:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_unlink (call_frame_t *frame, xlator_t *this, loc_t *loc, int xflag,
3604df
            dict_t *xdata)
3604df
 {
3604df
@@ -499,7 +499,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_link_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
              int op_ret, int op_errno, inode_t *inode, struct iatt *stbuf,
3604df
              struct iatt *preparent, struct iatt *postparent, dict_t *xdata)
3604df
@@ -527,7 +527,7 @@ out:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_link (call_frame_t *frame, xlator_t *this, loc_t *oldloc,
3604df
          loc_t *newloc, dict_t *xdata)
3604df
 {
3604df
@@ -556,7 +556,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_rmdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
               int op_ret, int op_errno, struct iatt *preparent,
3604df
               struct iatt *postparent, dict_t *xdata)
3604df
@@ -585,7 +585,7 @@ out:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_rmdir (call_frame_t *frame, xlator_t *this, loc_t *loc, int flags,
3604df
           dict_t *xdata)
3604df
 {
3604df
@@ -613,7 +613,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_mkdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
               int op_ret, int op_errno, inode_t *inode,
3604df
               struct iatt *stbuf, struct iatt *preparent,
3604df
@@ -644,7 +644,7 @@ out:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_mkdir (call_frame_t *frame, xlator_t *this,
3604df
           loc_t *loc, mode_t mode, mode_t umask, dict_t *params)
3604df
 {
3604df
@@ -673,7 +673,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_create_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
                int op_ret, int op_errno, fd_t *fd, inode_t *inode,
3604df
                struct iatt *stbuf, struct iatt *preparent,
3604df
@@ -705,7 +705,7 @@ out:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_create (call_frame_t *frame, xlator_t *this,
3604df
            loc_t *loc, int32_t flags, mode_t mode,
3604df
            mode_t umask, fd_t *fd, dict_t *params)
3604df
@@ -736,7 +736,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
                int op_ret, int op_errno,
3604df
                inode_t *inode, struct iatt *stbuf, dict_t *xattr,
3604df
@@ -765,7 +765,7 @@ out:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_lookup (call_frame_t *frame, xlator_t *this,
3604df
            loc_t *loc, dict_t *xattr_req)
3604df
 {
3604df
@@ -794,7 +794,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_stat_cbk (call_frame_t *frame, void *cookie,
3604df
              xlator_t *this, int32_t op_ret, int32_t op_errno,
3604df
              struct iatt *buf, dict_t *xdata)
3604df
@@ -822,7 +822,7 @@ out:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_stat (call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xdata)
3604df
 {
3604df
         int32_t          op_errno        = -1;
3604df
@@ -849,7 +849,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_fstat (call_frame_t *frame, xlator_t *this,
3604df
           fd_t *fd, dict_t *xdata)
3604df
 {
3604df
@@ -877,7 +877,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_ftruncate (call_frame_t *frame, xlator_t *this,
3604df
               fd_t *fd, off_t offset, dict_t *xdata)
3604df
 {
3604df
@@ -906,7 +906,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_access_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
                int op_ret, int op_errno, dict_t *xdata)
3604df
 {
3604df
@@ -932,7 +932,7 @@ out:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_access (call_frame_t *frame, xlator_t *this,
3604df
            loc_t *loc, int32_t mask, dict_t *xdata)
3604df
 {
3604df
@@ -960,7 +960,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_readlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
                  int op_ret, int op_errno, const char *path,
3604df
                  struct iatt *stbuf, dict_t *xdata)
3604df
@@ -988,7 +988,7 @@ out:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_readlink (call_frame_t *frame, xlator_t *this,
3604df
              loc_t *loc, size_t size, dict_t *xdata)
3604df
 {
3604df
@@ -1017,7 +1017,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_mknod_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
               int32_t op_ret, int32_t op_errno, inode_t *inode,
3604df
               struct iatt *buf, struct iatt *preparent,
3604df
@@ -1048,7 +1048,7 @@ out:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_mknod (call_frame_t *frame, xlator_t *this, loc_t *loc,
3604df
           mode_t mode, dev_t rdev, mode_t umask, dict_t *xdata)
3604df
 {
3604df
@@ -1077,7 +1077,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_symlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
                 int32_t op_ret, int32_t op_errno, inode_t *inode,
3604df
                 struct iatt *buf, struct iatt *preparent,
3604df
@@ -1108,7 +1108,7 @@ out:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_symlink (call_frame_t   *frame, xlator_t *this,
3604df
             const char *linkpath, loc_t *loc, mode_t umask,
3604df
             dict_t *xdata)
3604df
@@ -1138,7 +1138,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_opendir_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
                 int32_t op_ret, int32_t op_errno, fd_t *fd,
3604df
                 dict_t *xdata)
3604df
@@ -1165,7 +1165,7 @@ out:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_opendir (call_frame_t *frame, xlator_t *this,
3604df
             loc_t *loc, fd_t *fd, dict_t *xdata)
3604df
 {
3604df
@@ -1193,7 +1193,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_statfs_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
                int32_t op_ret, int32_t op_errno, struct statvfs *buf,
3604df
                dict_t *xdata)
3604df
@@ -1220,7 +1220,7 @@ out:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_statfs (call_frame_t *frame, xlator_t *this,
3604df
            loc_t *loc, dict_t *xdata)
3604df
 {
3604df
@@ -1248,7 +1248,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_readdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
                 int32_t op_ret, int32_t op_errno, gf_dirent_t *entries,
3604df
                 dict_t *xdata)
3604df
@@ -1275,7 +1275,7 @@ out:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_readdir (call_frame_t  *frame, xlator_t *this,
3604df
             fd_t *fd, size_t size, off_t off, dict_t *xdata)
3604df
 {
3604df
@@ -1303,7 +1303,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
                  int32_t op_ret, int32_t op_errno, gf_dirent_t *entries,
3604df
                  dict_t *xdata)
3604df
@@ -1344,7 +1344,7 @@ out:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_readdirp (call_frame_t *frame, xlator_t *this,
3604df
              fd_t *fd, size_t size, off_t off, dict_t *dict)
3604df
 {
3604df
@@ -1372,7 +1372,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_fsetattr (call_frame_t *frame, xlator_t *this, fd_t *fd,
3604df
              struct iatt  *stbuf, int32_t valid, dict_t *xdata)
3604df
 {
3604df
@@ -1401,7 +1401,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_fallocate_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
3604df
                  int32_t op_ret, int32_t op_errno, struct iatt *pre,
3604df
                  struct iatt *post, dict_t *xdata)
3604df
@@ -1429,7 +1429,7 @@ out:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_fallocate(call_frame_t *frame, xlator_t *this, fd_t *fd,
3604df
              int32_t mode, off_t offset, size_t len, dict_t *xdata)
3604df
 {
3604df
@@ -1458,7 +1458,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_discard_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
3604df
                int32_t op_ret, int32_t op_errno, struct iatt *pre,
3604df
                struct iatt *post, dict_t *xdata)
3604df
@@ -1486,7 +1486,7 @@ out:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_discard(call_frame_t *frame, xlator_t *this, fd_t *fd,
3604df
            off_t offset, size_t len, dict_t *xdata)
3604df
 {
3604df
@@ -1515,7 +1515,7 @@ err:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_zerofill_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
3604df
                 int32_t op_ret, int32_t op_errno, struct iatt *pre,
3604df
                 struct iatt *post, dict_t *xdata)
3604df
@@ -1543,7 +1543,7 @@ out:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int
3604df
+static int
3604df
 up_zerofill(call_frame_t *frame, xlator_t *this, fd_t *fd,
3604df
             off_t offset, off_t len, dict_t *xdata)
3604df
 {
3604df
@@ -1573,7 +1573,7 @@ err:
3604df
 }
3604df
 
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_seek_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int op_ret,
3604df
              int op_errno, off_t offset, dict_t *xdata)
3604df
 {
3604df
@@ -1600,7 +1600,7 @@ out:
3604df
 }
3604df
 
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_seek (call_frame_t *frame, xlator_t *this, fd_t *fd, off_t offset,
3604df
          gf_seek_what_t what, dict_t *xdata)
3604df
 {
3604df
@@ -1628,7 +1628,7 @@ err:
3604df
 }
3604df
 
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_setxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
                 int32_t op_ret, int32_t op_errno, dict_t *xdata)
3604df
 {
3604df
@@ -1652,20 +1652,14 @@ up_setxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
         }
3604df
 
3604df
         flags = UP_XATTR;
3604df
-        /* Remove the xattrs from the dict, if they are not registered for
3604df
-         * cache invalidation */
3604df
-        ret = dict_foreach (local->xattr, up_filter_unregd_xattr, priv->xattrs);
3604df
+
3604df
+        ret = up_filter_xattr (local->xattr, priv->xattrs);
3604df
         if (ret < 0) {
3604df
                 op_ret = ret;
3604df
                 goto out;
3604df
         }
3604df
-
3604df
-        if (dict_key_count(local->xattr) == 0) {
3604df
-                gf_msg_trace (this->name, 0, "None of xattrs requested for"
3604df
-                              " invalidation, were changed. Nothing to "
3604df
-                              "invalidate");
3604df
-                goto out; /* nothing to invalidate */
3604df
-        }
3604df
+        if (!up_invalidate_needed (local->xattr))
3604df
+                goto out;
3604df
 
3604df
         ret = syncop_stat (FIRST_CHILD(frame->this), &local->loc, &stbuf,
3604df
                            NULL, NULL);
3604df
@@ -1682,7 +1676,7 @@ out:
3604df
 }
3604df
 
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_setxattr (call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *dict,
3604df
              int32_t flags, dict_t *xdata)
3604df
 {
3604df
@@ -1718,7 +1712,7 @@ err:
3604df
 }
3604df
 
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_fsetxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
                   int32_t op_ret, int32_t op_errno, dict_t *xdata)
3604df
 {
3604df
@@ -1742,20 +1736,14 @@ up_fsetxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
         }
3604df
 
3604df
         flags = UP_XATTR;
3604df
-         /* Remove the xattrs from the dict, if they are not registered for
3604df
-         * cache invalidation */
3604df
-        ret = dict_foreach (local->xattr, up_filter_unregd_xattr, priv->xattrs);
3604df
+
3604df
+        ret = up_filter_xattr (local->xattr, priv->xattrs);
3604df
         if (ret < 0) {
3604df
                 op_ret = ret;
3604df
                 goto out;
3604df
         }
3604df
-
3604df
-        if (dict_key_count(local->xattr) == 0) {
3604df
-                gf_msg_trace (this->name, 0, "None of xattrs requested for"
3604df
-                              " invalidation, were changed. Nothing to "
3604df
-                              "invalidate");
3604df
-                goto out; /* nothing to invalidate */
3604df
-        }
3604df
+        if (!up_invalidate_needed (local->xattr))
3604df
+                goto out;
3604df
 
3604df
         ret = syncop_fstat (FIRST_CHILD(frame->this), local->fd, &stbuf, NULL,
3604df
                             NULL);
3604df
@@ -1772,7 +1760,7 @@ out:
3604df
 }
3604df
 
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_fsetxattr (call_frame_t *frame, xlator_t *this, fd_t *fd, dict_t *dict,
3604df
               int32_t flags, dict_t *xdata)
3604df
 {
3604df
@@ -1808,7 +1796,7 @@ err:
3604df
 }
3604df
 
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_fremovexattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
                      int32_t op_ret, int32_t op_errno, dict_t *xdata)
3604df
 {
3604df
@@ -1832,20 +1820,13 @@ up_fremovexattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
         }
3604df
         flags = UP_XATTR_RM;
3604df
 
3604df
-        /* Remove the xattrs from the dict, if they are not registered for
3604df
-         * cache invalidation */
3604df
-        ret = dict_foreach (local->xattr, up_filter_unregd_xattr, priv->xattrs);
3604df
+        ret = up_filter_xattr (local->xattr, priv->xattrs);
3604df
         if (ret < 0) {
3604df
                 op_ret = ret;
3604df
                 goto out;
3604df
         }
3604df
-
3604df
-        if (dict_key_count(local->xattr) == 0) {
3604df
-                gf_msg_trace (this->name, 0, "None of xattrs requested for"
3604df
-                              " invalidation, were changed. Nothing to "
3604df
-                              "invalidate");
3604df
-                goto out; /* nothing to invalidate */
3604df
-        }
3604df
+        if (!up_invalidate_needed (local->xattr))
3604df
+                goto out;
3604df
 
3604df
         ret = syncop_fstat (FIRST_CHILD(frame->this), local->fd, &stbuf, NULL,
3604df
                             NULL);
3604df
@@ -1862,7 +1843,7 @@ out:
3604df
 }
3604df
 
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_fremovexattr (call_frame_t *frame, xlator_t *this, fd_t *fd,
3604df
                  const char *name, dict_t *xdata)
3604df
 {
3604df
@@ -1897,7 +1878,7 @@ err:
3604df
 }
3604df
 
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_removexattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
                     int32_t op_ret, int32_t op_errno, dict_t *xdata)
3604df
 {
3604df
@@ -1921,20 +1902,13 @@ up_removexattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
         }
3604df
         flags = UP_XATTR_RM;
3604df
 
3604df
-         /* Remove the xattrs from the dict, if they are not registered for
3604df
-         * cache invalidation */
3604df
-        ret = dict_foreach (local->xattr, up_filter_unregd_xattr, priv->xattrs);
3604df
+        ret = up_filter_xattr (local->xattr, priv->xattrs);
3604df
         if (ret < 0) {
3604df
                 op_ret = ret;
3604df
                 goto out;
3604df
         }
3604df
-
3604df
-        if (dict_key_count(local->xattr) == 0) {
3604df
-                gf_msg_trace (this->name, 0, "None of xattrs requested for"
3604df
-                              " invalidation, were changed. Nothing to "
3604df
-                              "invalidate");
3604df
-                goto out; /* nothing to invalidate */
3604df
-        }
3604df
+        if (!up_invalidate_needed (local->xattr))
3604df
+                goto out;
3604df
 
3604df
         ret = syncop_stat (FIRST_CHILD(frame->this), &local->loc, &stbuf, NULL,
3604df
                            NULL);
3604df
@@ -1951,7 +1925,7 @@ out:
3604df
 }
3604df
 
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_removexattr (call_frame_t *frame, xlator_t *this, loc_t *loc,
3604df
                 const char *name, dict_t *xdata)
3604df
 {
3604df
@@ -1986,7 +1960,7 @@ err:
3604df
 }
3604df
 
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_fgetxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
                   int32_t op_ret, int32_t op_errno, dict_t *dict,
3604df
                   dict_t *xdata)
3604df
@@ -2015,7 +1989,7 @@ out:
3604df
 }
3604df
 
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_fgetxattr (call_frame_t *frame, xlator_t *this, fd_t *fd,
3604df
               const char *name, dict_t *xdata)
3604df
 {
3604df
@@ -2042,7 +2016,7 @@ err:
3604df
 }
3604df
 
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_getxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
                  int32_t op_ret, int32_t op_errno, dict_t *dict,
3604df
                  dict_t *xdata)
3604df
@@ -2070,7 +2044,7 @@ out:
3604df
         return 0;
3604df
 }
3604df
 
3604df
-int32_t
3604df
+static int32_t
3604df
 up_getxattr (call_frame_t *frame, xlator_t *this, loc_t *loc,
3604df
              const char *name, dict_t *xdata)
3604df
 {
3604df
@@ -2097,6 +2071,141 @@ err:
3604df
 }
3604df
 
3604df
 
3604df
+/* The xattrops here mainly tracks changes in afr pending xattr.
3604df
+ *    1. xattrop doesn't carry info saying post op/pre op.
3604df
+ *    2. Pre xattrop will have 0 value for all pending xattrs,
3604df
+ *       the cbk of pre xattrop carries the on-disk xattr value.
3604df
+ *       Non zero on-disk xattr indicates pending healing.
3604df
+ *    3. Post xattrop will either have 0 or 1 as value of pending xattrs,
3604df
+ *       0 on success, 1 on failure. But the post xattrop cbk will have
3604df
+ *       0 or 1 or any higher value.
3604df
+ *       0 - if no healing required*
3604df
+ *       1 - if this is the first time pending xattr is being set.
3604df
+ *       n - if there is already a pending xattr set, it will increment
3604df
+ *       the on-disk value and send that in cbk.
3604df
+ * Our aim is to send an invalidation, only the first time a pending
3604df
+ * xattr was set on a file. Below are some of the exceptions in handling
3604df
+ * xattrop:
3604df
+ * - Do not filter unregistered xattrs in the cbk, but in the call path.
3604df
+ *   Else, we will be invalidating on every preop, if the file already has
3604df
+ *   pending xattr set. Filtering unregistered xattrs on the fop path
3604df
+ *   ensures we invalidate only in postop, everytime a postop comes with
3604df
+ *   pending xattr value 1.
3604df
+ * - Consider a brick is down, and the postop sets pending xattrs as long
3604df
+ *   as the other brick is down. But we do not want to invalidate everytime
3604df
+ *   a pending xattr is set, but we wan't to inalidate only the first time
3604df
+ *   a pending xattr is set on any file. Hence, to identify if its the first
3604df
+ *   time a pending xattr is set, we compare the value of pending xattrs that
3604df
+ *   came in postop and postop cbk, if its same then its the first time.
3604df
+ */
3604df
+static int32_t
3604df
+up_xattrop_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
3604df
+                int32_t op_ret, int32_t op_errno, dict_t *dict, dict_t *xdata)
3604df
+{
3604df
+        client_t         *client        = NULL;
3604df
+        upcall_local_t   *local         = NULL;
3604df
+
3604df
+        EXIT_IF_UPCALL_OFF (this, out);
3604df
+
3604df
+        client = frame->root->client;
3604df
+        local = frame->local;
3604df
+
3604df
+        if ((op_ret < 0) || !local) {
3604df
+                goto out;
3604df
+        }
3604df
+
3604df
+        if (up_invalidate_needed (local->xattr)) {
3604df
+                if (dict_foreach (local->xattr, up_compare_afr_xattr, dict) < 0)
3604df
+                        goto out;
3604df
+
3604df
+                upcall_cache_invalidate (frame, this, client, local->inode,
3604df
+                                         UP_XATTR, NULL, NULL, NULL,
3604df
+                                         local->xattr);
3604df
+        }
3604df
+out:
3604df
+        if (frame->root->op == GF_FOP_FXATTROP) {
3604df
+                UPCALL_STACK_UNWIND (fxattrop, frame, op_ret, op_errno, dict,
3604df
+                                     xdata);
3604df
+        } else {
3604df
+                UPCALL_STACK_UNWIND (xattrop, frame, op_ret, op_errno, dict,
3604df
+                xdata);
3604df
+        }
3604df
+        return 0;
3604df
+}
3604df
+
3604df
+
3604df
+static int32_t
3604df
+up_xattrop (call_frame_t *frame, xlator_t *this, loc_t *loc,
3604df
+            gf_xattrop_flags_t optype, dict_t *xattr, dict_t *xdata)
3604df
+{
3604df
+        int32_t          op_errno        = -1;
3604df
+        upcall_local_t   *local          = NULL;
3604df
+        int              ret             = 0;
3604df
+        upcall_private_t *priv           = NULL;
3604df
+
3604df
+        EXIT_IF_UPCALL_OFF (this, out);
3604df
+
3604df
+        priv = this->private;
3604df
+        GF_VALIDATE_OR_GOTO (this->name, priv, out);
3604df
+
3604df
+        local = upcall_local_init (frame, this, loc, NULL, loc->inode, xattr);
3604df
+        if (!local) {
3604df
+                op_errno = ENOMEM;
3604df
+                goto err;
3604df
+        }
3604df
+
3604df
+        ret = up_filter_xattr (local->xattr, priv->xattrs);
3604df
+        if (ret < 0) {
3604df
+                goto err;
3604df
+        }
3604df
+
3604df
+out:
3604df
+        STACK_WIND (frame, up_xattrop_cbk, FIRST_CHILD(this),
3604df
+                    FIRST_CHILD(this)->fops->xattrop, loc, optype, xattr,
3604df
+                    xdata);
3604df
+        return 0;
3604df
+err:
3604df
+        UPCALL_STACK_UNWIND (xattrop, frame, -1, op_errno, NULL, NULL);
3604df
+        return 0;
3604df
+}
3604df
+
3604df
+
3604df
+static int32_t
3604df
+up_fxattrop (call_frame_t *frame, xlator_t *this, fd_t *fd,
3604df
+             gf_xattrop_flags_t optype, dict_t *xattr, dict_t *xdata)
3604df
+{
3604df
+        int32_t          op_errno        = -1;
3604df
+        upcall_local_t   *local          = NULL;
3604df
+        int              ret             = 0;
3604df
+        upcall_private_t *priv           = NULL;
3604df
+
3604df
+        EXIT_IF_UPCALL_OFF (this, out);
3604df
+
3604df
+        priv = this->private;
3604df
+        GF_VALIDATE_OR_GOTO (this->name, priv, out);
3604df
+
3604df
+        local = upcall_local_init (frame, this, NULL, fd, fd->inode, xattr);
3604df
+        if (!local) {
3604df
+                op_errno = ENOMEM;
3604df
+                goto err;
3604df
+        }
3604df
+
3604df
+        ret = up_filter_xattr (local->xattr, priv->xattrs);
3604df
+        if (ret < 0) {
3604df
+                goto err;
3604df
+        }
3604df
+
3604df
+out:
3604df
+        STACK_WIND (frame, up_xattrop_cbk, FIRST_CHILD(this),
3604df
+                    FIRST_CHILD(this)->fops->fxattrop, fd, optype, xattr,
3604df
+                    xdata);
3604df
+        return 0;
3604df
+err:
3604df
+        STACK_UNWIND_STRICT (fxattrop, frame, -1, op_errno, NULL, NULL);
3604df
+        return 0;
3604df
+}
3604df
+
3604df
+
3604df
 int32_t
3604df
 mem_acct_init (xlator_t *this)
3604df
 {
3604df
@@ -2150,7 +2259,7 @@ upcall_local_init (call_frame_t *frame, xlator_t *this, loc_t *loc, fd_t *fd,
3604df
 
3604df
         local->inode = inode_ref (inode);
3604df
         if (xattr)
3604df
-                local->xattr = dict_ref (xattr);
3604df
+                local->xattr = dict_copy_with_ref (xattr, NULL);
3604df
 
3604df
         /* Shall we get inode_ctx and store it here itself? */
3604df
         local->upcall_inode_ctx = upcall_inode_ctx_get (inode, this);
3604df
@@ -2430,6 +2539,8 @@ struct xlator_fops fops = {
3604df
         .fgetxattr   = up_fgetxattr,
3604df
         .fremovexattr = up_fremovexattr,
3604df
         .removexattr = up_removexattr,
3604df
+        .xattrop     = up_xattrop,
3604df
+        .fxattrop    = up_fxattrop,
3604df
 
3604df
 #ifdef NOT_SUPPORTED
3604df
         /* internal lk fops */
3604df
@@ -2444,9 +2555,6 @@ struct xlator_fops fops = {
3604df
         .flush       = up_flush,
3604df
         .fsync       = up_fsync,
3604df
         .fsyncdir    = up_fsyncdir,
3604df
-
3604df
-        .xattrop     = up_xattrop,
3604df
-        .fxattrop    = up_fxattrop,
3604df
 #endif
3604df
 };
3604df
 
3604df
diff --git a/xlators/features/upcall/src/upcall.h b/xlators/features/upcall/src/upcall.h
3604df
index 852f551..4554248 100644
3604df
--- a/xlators/features/upcall/src/upcall.h
3604df
+++ b/xlators/features/upcall/src/upcall.h
3604df
@@ -132,6 +132,9 @@ void upcall_client_cache_invalidate (xlator_t *xl, uuid_t gfid,
3604df
                                      struct iatt *p_stbuf,
3604df
                                      struct iatt *oldp_stbuf, dict_t *xattr);
3604df
 
3604df
-int up_filter_unregd_xattr (dict_t *xattrs, char *xattr, data_t *v,
3604df
-                            void *regd_xattrs);
3604df
+int up_filter_xattr (dict_t *xattr, dict_t *regd_xattrs);
3604df
+
3604df
+int up_compare_afr_xattr (dict_t *d, char *k, data_t *v, void *tmp);
3604df
+
3604df
+gf_boolean_t up_invalidate_needed (dict_t *xattrs);
3604df
 #endif /* __UPCALL_H__ */
3604df
-- 
3604df
1.7.1
3604df