summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSoumya Koduri <skoduri@redhat.com>2017-09-22 16:43:39 +0530
committerAmar Tumballi <amarts@redhat.com>2017-10-31 07:53:50 +0000
commitc0e77f643930499966554cd849a40580e4ff68f9 (patch)
tree5baf2c5b33da77b4567fd23a77d526ca890519b0
parent4216869c724cf19c12d63c0580de88e9427e6467 (diff)
gfapi: Register/Unregister Upcall events' callback
Polling continuously for upcall events is not optimal. Hence new APIs have been added to allow applications to register and unregister upcall events it is interested in along with callback function to be invoked in case of any such upcalls sent by backend server. @TODO: Make changes in upcall xlator so that events are sent to only those clients which either registered callbacks or started polling. Shall be addressed in separate patch. Updates: #315 Change-Id: I40473fd5cf689172ff2d7bb2869756b7fd5bc761 Signed-off-by: Soumya Koduri <skoduri@redhat.com>
-rw-r--r--api/src/gfapi.aliases3
-rw-r--r--api/src/gfapi.map6
-rw-r--r--api/src/glfs-fops.c186
-rw-r--r--api/src/glfs-handleops.c2
-rw-r--r--api/src/glfs-handles.h43
-rw-r--r--api/src/glfs-internal.h7
-rw-r--r--api/src/glfs.c100
-rw-r--r--api/src/glfs.h150
-rw-r--r--tests/basic/gfapi/upcall-register-api.c281
-rwxr-xr-xtests/basic/gfapi/upcall-register-api.t30
10 files changed, 713 insertions, 95 deletions
diff --git a/api/src/gfapi.aliases b/api/src/gfapi.aliases
index 85e8448982d..16b18a1575d 100644
--- a/api/src/gfapi.aliases
+++ b/api/src/gfapi.aliases
@@ -164,3 +164,6 @@ _pub_glfs_xreaddirplus_r_get_object _glfs_xreaddirplus_r_get_object$GFAPI_3.11.0
_pub_glfs_object_copy _glfs_object_copy$GFAPI_3.11.0
_priv_glfs_ipc _glfs_ipc$GFAPI_3.12.0
+
+_pub_glfs_upcall_register _glfs_upcall_register$GFAPI_3.13.0
+_pub_glfs_upcall_unregister _glfs_upcall_unregister$GFAPI_3.13.0
diff --git a/api/src/gfapi.map b/api/src/gfapi.map
index 88673007d25..bb9c0377353 100644
--- a/api/src/gfapi.map
+++ b/api/src/gfapi.map
@@ -214,3 +214,9 @@ GFAPI_PRIVATE_3.12.0 {
global:
glfs_ipc;
} GFAPI_3.11.0;
+
+GFAPI_3.13.0 {
+ global:
+ glfs_upcall_register;
+ glfs_upcall_unregister;
+} GFAPI_PRIVATE_3.12.0;
diff --git a/api/src/glfs-fops.c b/api/src/glfs-fops.c
index 8d1ee9de1d7..72fd6975217 100644
--- a/api/src/glfs-fops.c
+++ b/api/src/glfs-fops.c
@@ -4398,29 +4398,151 @@ invalid_fs:
GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_dup, 3.4.0);
+static void
+glfs_enqueue_upcall_data (struct glfs *fs, struct gf_upcall *upcall_data)
+{
+ int ret = -1;
+ upcall_entry *u_list = NULL;
+
+ if (!fs || !upcall_data)
+ goto out;
+
+ u_list = GF_CALLOC (1, sizeof(*u_list),
+ glfs_mt_upcall_entry_t);
+
+ if (!u_list) {
+ gf_msg (THIS->name, GF_LOG_ERROR, ENOMEM, API_MSG_ALLOC_FAILED,
+ "Upcall entry allocation failed.");
+ goto out;
+ }
+
+ INIT_LIST_HEAD (&u_list->upcall_list);
+
+ gf_uuid_copy (u_list->upcall_data.gfid, upcall_data->gfid);
+ u_list->upcall_data.event_type = upcall_data->event_type;
+
+ switch (upcall_data->event_type) {
+ case GF_UPCALL_CACHE_INVALIDATION:
+ ret = glfs_get_upcall_cache_invalidation (&u_list->upcall_data,
+ upcall_data);
+ break;
+ default:
+ break;
+ }
+
+ if (ret) {
+ gf_msg (THIS->name, GF_LOG_ERROR, errno,
+ API_MSG_INVALID_ENTRY,
+ "Upcall entry validation failed.");
+ goto out;
+ }
+
+ pthread_mutex_lock (&fs->upcall_list_mutex);
+ {
+ list_add_tail (&u_list->upcall_list,
+ &fs->upcall_list);
+ }
+ pthread_mutex_unlock (&fs->upcall_list_mutex);
+
+ ret = 0;
+
+out:
+ if (ret && u_list) {
+ GF_FREE (u_list->upcall_data.data);
+ GF_FREE(u_list);
+ }
+}
+
+static void
+glfs_cbk_upcall_data (struct glfs *fs, struct gf_upcall *upcall_data)
+{
+ int ret = -1;
+ struct glfs_upcall *up_arg = NULL;
+
+ if (!fs || !upcall_data)
+ goto out;
+
+ if (!(fs->upcall_events & upcall_data->event_type)) {
+ /* ignore events which application hasn't registered*/
+ goto out;
+ }
+
+ up_arg = GLFS_CALLOC (1, sizeof (struct gf_upcall),
+ glfs_release_upcall,
+ glfs_mt_upcall_entry_t);
+ if (!up_arg) {
+ gf_msg (THIS->name, GF_LOG_ERROR, ENOMEM, API_MSG_ALLOC_FAILED,
+ "Upcall entry allocation failed.");
+ goto out;
+ }
+
+ switch (upcall_data->event_type) {
+ case GF_UPCALL_CACHE_INVALIDATION:
+ ret = glfs_h_poll_cache_invalidation (fs, up_arg, upcall_data);
+ break;
+ default:
+ errno = EINVAL;
+ }
+
+ if (!ret && (up_arg->reason != GLFS_UPCALL_EVENT_NULL)) {
+ /* It could so happen that the file which got
+ * upcall notification may have got deleted by
+ * the same client. In such cases up_arg->reason
+ * is set to GLFS_UPCALL_EVENT_NULL. No need to
+ * send upcall then */
+ (fs->up_cbk) (up_arg, fs->up_data);
+ } else if (up_arg->reason == GLFS_UPCALL_EVENT_NULL) {
+ gf_msg (THIS->name, GF_LOG_DEBUG, errno,
+ API_MSG_INVALID_ENTRY,
+ "Upcall_EVENT_NULL received. Skipping it.");
+ goto out;
+ } else {
+ gf_msg (THIS->name, GF_LOG_ERROR, errno,
+ API_MSG_INVALID_ENTRY,
+ "Upcall entry validation failed.");
+ goto out;
+ }
+
+ /* application takes care of calling glfs_free on up_arg post
+ * their processing */
+ ret = 0;
+
+out:
+ if (ret && up_arg) {
+ GLFS_FREE (up_arg);
+ }
+
+ return;
+}
+
/*
* This routine is called in case of any notification received
* from the server. All the upcall events are queued up in a list
* to be read by the applications.
*
- * XXX: Applications may register a cbk function for each 'fs'
- * which then needs to be called by this routine incase of any
- * event received. The cbk fn is responsible for notifying the
+ * In case if the application registers a cbk function, that shall
+ * be called by this routine incase of any event received.
+ * The cbk fn is responsible for notifying the
* applications the way it desires for each event queued (for eg.,
* can raise a signal or broadcast a cond variable etc.)
+ *
+ * Otherwise all the upcall events are queued up in a list
+ * to be read/polled by the applications.
*/
void
priv_glfs_process_upcall_event (struct glfs *fs, void *data)
{
- int ret = -1;
- upcall_entry *u_list = NULL;
glusterfs_ctx_t *ctx = NULL;
struct gf_upcall *upcall_data = NULL;
+ DECLARE_OLD_THIS;
+
gf_msg_debug (THIS->name, 0,
"Upcall gfapi callback is called");
- if (!fs || !data)
+ __GLFS_ENTRY_VALIDATE_FS (fs, err);
+
+ if (!data)
goto out;
/* Unlike in I/O path, "glfs_fini" would not have freed
@@ -4441,48 +4563,16 @@ priv_glfs_process_upcall_event (struct glfs *fs, void *data)
}
pthread_mutex_unlock (&fs->mutex);
-
upcall_data = (struct gf_upcall *)data;
- gf_msg_trace (THIS->name, 0, "Upcall gfapi gfid = %s"
- "ret = %d", (char *)(upcall_data->gfid), ret);
+ gf_msg_trace (THIS->name, 0, "Upcall gfapi gfid = %s" ,
+ (char *)(upcall_data->gfid));
- u_list = GF_CALLOC (1, sizeof(*u_list),
- glfs_mt_upcall_entry_t);
-
- if (!u_list) {
- gf_msg (THIS->name, GF_LOG_ERROR, ENOMEM, API_MSG_ALLOC_FAILED,
- "Upcall entry allocation failed.");
- goto out;
- }
-
- INIT_LIST_HEAD (&u_list->upcall_list);
-
- gf_uuid_copy (u_list->upcall_data.gfid, upcall_data->gfid);
- u_list->upcall_data.event_type = upcall_data->event_type;
-
- switch (upcall_data->event_type) {
- case GF_UPCALL_CACHE_INVALIDATION:
- ret = glfs_get_upcall_cache_invalidation (&u_list->upcall_data,
- upcall_data);
- break;
- default:
- goto out;
- }
-
- if (ret) {
- gf_msg (THIS->name, GF_LOG_ERROR, errno,
- API_MSG_INVALID_ENTRY,
- "Upcall entry validation failed.");
- goto out;
- }
-
- pthread_mutex_lock (&fs->upcall_list_mutex);
- {
- list_add_tail (&u_list->upcall_list,
- &fs->upcall_list);
+ if (fs->up_cbk) { /* upcall cbk registered */
+ (void) glfs_cbk_upcall_data (fs, upcall_data);
+ } else {
+ (void) glfs_enqueue_upcall_data (fs, upcall_data);
}
- pthread_mutex_unlock (&fs->upcall_list_mutex);
pthread_mutex_lock (&fs->mutex);
{
@@ -4490,15 +4580,11 @@ priv_glfs_process_upcall_event (struct glfs *fs, void *data)
}
pthread_mutex_unlock (&fs->mutex);
- ret = 0;
out:
- if (ret && u_list) {
- GF_FREE (u_list->upcall_data.data);
- GF_FREE(u_list);
- }
+ __GLFS_EXIT_FS;
+err:
return;
}
-
GFAPI_SYMVER_PRIVATE_DEFAULT(glfs_process_upcall_event, 3.7.0);
ssize_t
diff --git a/api/src/glfs-handleops.c b/api/src/glfs-handleops.c
index 65b2b6a467c..2693c2de306 100644
--- a/api/src/glfs-handleops.c
+++ b/api/src/glfs-handleops.c
@@ -1987,7 +1987,7 @@ out:
return ret;
}
-static void glfs_release_upcall (void *ptr)
+void glfs_release_upcall (void *ptr)
{
struct glfs_upcall *to_free = ptr;
diff --git a/api/src/glfs-handles.h b/api/src/glfs-handles.h
index 9583fab069a..211595933fc 100644
--- a/api/src/glfs-handles.h
+++ b/api/src/glfs-handles.h
@@ -102,49 +102,6 @@ __BEGIN_DECLS
struct glfs_object;
typedef struct glfs_object glfs_object_t;
-/*
- * Applications (currently NFS-Ganesha) can make use of this
- * structure to read upcall notifications sent by server.
- *
- * On success, applications need to check for 'reason' to decide
- * if any upcall event is received.
- *
- * Currently supported upcall_events -
- * GFAPI_INODE_INVALIDATE -
- * 'event_arg' - glfs_upcall_inode
- *
- * After processing the event, applications need to free 'event_arg' with
- * glfs_free().
- *
- * Also similar to I/Os, the application should ideally stop polling
- * before calling glfs_fini(..). Hence making an assumption that
- * 'fs' & ctx structures cannot be freed while in this routine.
- */
-struct glfs_upcall;
-
-struct glfs*
-glfs_upcall_get_fs (struct glfs_upcall *arg) __THROW
- GFAPI_PUBLIC(glfs_upcall_get_fs, 3.7.16);
-
-enum glfs_upcall_reason {
- GLFS_UPCALL_EVENT_NULL = 0,
- GLFS_UPCALL_INODE_INVALIDATE, /* invalidate cache entry */
-};
-
-enum glfs_upcall_reason
-glfs_upcall_get_reason (struct glfs_upcall *arg) __THROW
- GFAPI_PUBLIC(glfs_upcall_get_reason, 3.7.16);
-
-
-/*
- * After processing upcall event, glfs_free() should be called on the
- * glfs_upcall.
- */
-void*
-glfs_upcall_get_event (struct glfs_upcall *arg) __THROW
- GFAPI_PUBLIC(glfs_upcall_get_event, 3.7.16);
-
-
/* Functions for getting details about the glfs_upcall_inode
*
* None of the pointers returned by the below functions should be free()'d,
diff --git a/api/src/glfs-internal.h b/api/src/glfs-internal.h
index 757bc18eb1d..76b0e34f1bd 100644
--- a/api/src/glfs-internal.h
+++ b/api/src/glfs-internal.h
@@ -194,6 +194,12 @@ struct glfs {
uint32_t pin_refcnt;
uint32_t pthread_flags; /* GLFS_INIT_* # defines set this flag */
+
+ uint32_t upcall_events; /* Mask of upcall events application
+ * is interested in */
+ glfs_upcall_cbk up_cbk; /* upcall cbk function to be registered */
+ void *up_data; /* Opaque data provided by application
+ * during upcall registration */
};
/* This enum is used to maintain the state of glfd. In case of async fops
@@ -604,4 +610,5 @@ gf_dirent_to_dirent (gf_dirent_t *gf_dirent, struct dirent *dirent);
int glfs_ipc (glfs_fd_t *fd, int cmd, void *xd_in, void **xd_out) __THROW
GFAPI_PRIVATE(glfs_ipc, 3.12.0);
+void glfs_release_upcall (void *ptr);
#endif /* !_GLFS_INTERNAL_H */
diff --git a/api/src/glfs.c b/api/src/glfs.c
index 72a2c3724a1..b168f682a7d 100644
--- a/api/src/glfs.c
+++ b/api/src/glfs.c
@@ -703,6 +703,9 @@ glfs_new_fs (const char *volname)
goto err;
fs->pin_refcnt = 0;
+ fs->upcall_events = 0;
+ fs->up_cbk = NULL;
+ fs->up_data = NULL;
return fs;
@@ -1551,3 +1554,100 @@ out:
}
GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_sysrq, 3.10.0);
+
+int
+glfs_upcall_register (struct glfs *fs, uint32_t event_list,
+ glfs_upcall_cbk cbk, void *data)
+{
+ int ret = 0;
+
+ /* list of supported upcall events */
+ uint32_t up_events = GLFS_EVENT_INODE_INVALIDATE;
+
+ DECLARE_OLD_THIS;
+ __GLFS_ENTRY_VALIDATE_FS (fs, invalid_fs);
+
+ GF_VALIDATE_OR_GOTO (THIS->name, cbk, out);
+
+ /* Event list should be either GLFS_EVENT_ANY
+ * or list of supported individual events (up_events)
+ */
+ if ((event_list != GLFS_EVENT_ANY) &&
+ (event_list & ~up_events)) {
+ errno = EINVAL;
+ ret = -1;
+ gf_msg (THIS->name, GF_LOG_ERROR, errno, LG_MSG_INVALID_ARG,
+ "invalid event_list (0x%08x)", event_list);
+ goto out;
+ }
+
+ /* incase other thread does unregister */
+ pthread_mutex_lock (&fs->mutex);
+ {
+ if (event_list & GLFS_EVENT_INODE_INVALIDATE) {
+ /* @todo: Check if features.cache-invalidation is
+ * enabled.
+ */
+ fs->upcall_events |= GF_UPCALL_CACHE_INVALIDATION;
+ ret |= GF_UPCALL_CACHE_INVALIDATION;
+ }
+
+ /* Override cbk function if existing */
+ fs->up_cbk = cbk;
+ fs->up_data = data;
+ fs->cache_upcalls = _gf_true;
+ }
+ pthread_mutex_unlock (&fs->mutex);
+
+out:
+ __GLFS_EXIT_FS;
+
+invalid_fs:
+ return ret;
+}
+GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_register, 3.13.0);
+
+int glfs_upcall_unregister (struct glfs *fs, uint32_t event_list)
+{
+ int ret = 0;
+ /* list of supported upcall events */
+ uint32_t up_events = GLFS_EVENT_INODE_INVALIDATE;
+
+ DECLARE_OLD_THIS;
+ __GLFS_ENTRY_VALIDATE_FS (fs, invalid_fs);
+
+ /* Event list should be either GLFS_EVENT_ANY
+ * or list of supported individual events (up_events)
+ */
+ if ((event_list != GLFS_EVENT_ANY) &&
+ (event_list & ~up_events)) {
+ errno = EINVAL;
+ ret = -1;
+ gf_msg (THIS->name, GF_LOG_ERROR, errno, LG_MSG_INVALID_ARG,
+ "invalid event_list (0x%08x)", event_list);
+ goto out;
+ }
+
+ pthread_mutex_lock (&fs->mutex);
+ {
+ if (event_list & GLFS_EVENT_INODE_INVALIDATE) {
+ fs->upcall_events &= ~GF_UPCALL_CACHE_INVALIDATION;
+ ret |= GF_UPCALL_CACHE_INVALIDATION;
+ }
+
+ /* If there are no upcall events registered, reset cbk */
+ if (fs->upcall_events == 0) {
+ fs->up_cbk = NULL;
+ fs->up_data = NULL;
+ fs->cache_upcalls = _gf_false;
+ }
+ }
+ pthread_mutex_unlock (&fs->mutex);
+
+out:
+ __GLFS_EXIT_FS;
+
+invalid_fs:
+ return ret;
+}
+GFAPI_SYMVER_PUBLIC_DEFAULT(glfs_upcall_unregister, 3.13.0);
diff --git a/api/src/glfs.h b/api/src/glfs.h
index ec3258d083a..198e4224a6b 100644
--- a/api/src/glfs.h
+++ b/api/src/glfs.h
@@ -883,6 +883,154 @@ glfs_xreaddirplus_r (struct glfs_fd *glfd, uint32_t flags,
*/
int glfs_fd_set_lkowner (glfs_fd_t *glfd, void *data, int len);
GFAPI_PUBLIC(glfs_fd_set_lkowner, 3.10.7);
-__END_DECLS
+/*
+ * Applications (currently NFS-Ganesha) can make use of this
+ * structure to read upcall notifications sent by server either
+ * by polling or registering a callback function.
+ *
+ * On success, applications need to check for 'reason' to decide
+ * if any upcall event is received.
+ *
+ * Currently supported upcall_events -
+ * GLFS_UPCALL_INODE_INVALIDATE -
+ * 'event_arg' - glfs_upcall_inode
+ *
+ * After processing the event, applications need to free 'event_arg' with
+ * glfs_free().
+ *
+ * Also similar to I/Os, the application should ideally stop polling
+ * or unregister upcall_cbk function before calling glfs_fini(..).
+ * Hence making an assumption that 'fs' & ctx structures cannot be
+ * freed while in this routine.
+ */
+struct glfs_upcall;
+
+struct glfs*
+glfs_upcall_get_fs (struct glfs_upcall *arg) __THROW
+ GFAPI_PUBLIC(glfs_upcall_get_fs, 3.7.16);
+
+enum glfs_upcall_reason {
+ GLFS_UPCALL_EVENT_NULL = 0,
+ GLFS_UPCALL_INODE_INVALIDATE, /* invalidate cache entry */
+};
+
+enum glfs_upcall_reason
+glfs_upcall_get_reason (struct glfs_upcall *arg) __THROW
+ GFAPI_PUBLIC(glfs_upcall_get_reason, 3.7.16);
+
+
+/*
+ * Applications first need to make use of above API i.e,
+ * "glfs_upcall_get_reason" to determine which upcall event it has
+ * received. Post that below API - "glfs_upcall_get_event" should
+ * be used to get corresponding upcall event object.
+ *
+ * Below are the upcall_reason and corresponding upcall_event objects:
+ * ==========================================================
+ * glfs_upcall_reason - event_object
+ * ==========================================================
+ * GLFS_UPCALL_EVENT_NULL - NULL
+ * GLFS_UPCALL_INODE_INVALIDATE - struct glfs_upcall_inode
+ *
+ * After processing upcall event, glfs_free() should be called on the
+ * glfs_upcall.
+ */
+void*
+glfs_upcall_get_event (struct glfs_upcall *arg) __THROW
+ GFAPI_PUBLIC(glfs_upcall_get_event, 3.7.16);
+
+/*
+ * SYNOPSIS
+ *
+ * glfs_upcall_cbk: Upcall callback definition
+ *
+ * This is function type definition of the callback function pointer
+ * which has to be provided by the caller while registering for any
+ * upcall events.
+ *
+ * This function is called whenever any upcall which the application
+ * has registered for is received from the server.
+ *
+ * @up_arg: Upcall structure whose contents need to be interpreted by
+ * making use of glfs_upcall_* helper routines.
+ *
+ * @data: The same context pointer provided by the caller at the time of
+ * registering of upcall events. This may be used by the caller for any
+ * of its internal use while processing upcalls.
+ */
+typedef void (*glfs_upcall_cbk) (struct glfs_upcall *up_arg, void *data);
+
+/*
+ * List of upcall events supported by gluster/gfapi
+ */
+#define GLFS_EVENT_INODE_INVALIDATE 0x00000001 /* invalidate cache entry */
+#define GLFS_EVENT_ANY 0xffffffff /* for all the above events */
+
+/*
+ * SYNOPSIS
+ *
+ * glfs_upcall_register: Register for upcall events
+ *
+ * DESCRIPTION
+ *
+ * This function is used to register for various upcall events application
+ * is interested in and the callback function to be invoked when such
+ * events are triggered.
+ *
+ * Multiple calls of this routine shall override cbk function. That means
+ * only one cbk function can be used for all the upcall events registered
+ * and that shall be the one last updated.
+ *
+ * PARAMETERS:
+ *
+ * INPUT:
+ * @fs: The 'virtual mount' object
+ *
+ * @event_list: List of upcall events to be registered.
+ * Current available values are:
+ * - GFAPI_UPCALL_INODE_INVALIDATE
+ *
+ * @cbk: The cbk routine to be invoked incase of any upcall received
+ * @data: Any opaque pointer provided by caller which shall be using while
+ * making cbk calls. This pointer may be used by caller for any of its
+ * internal use while processing upcalls. Can be NULL.
+ *
+ * RETURN VALUE:
+ * >0: SUCCESS (value contains the events successfully registered)
+ * -1: FAILURE
+ */
+int
+glfs_upcall_register (struct glfs *fs, uint32_t event_list,
+ glfs_upcall_cbk cbk, void *data);
+ GFAPI_PUBLIC(glfs_upcall_register, 3.13.0);
+
+/*
+ * SYNOPSIS
+ *
+ * glfs_upcall_unregister: Unregister for upcall events
+ *
+ * DESCRIPTION
+ *
+ * This function is used to unregister the upcall events application
+ * is not interested in. In case if the caller unregisters all the events
+ * it has registered for, it shall no more receive any upcall event.
+ *
+ * PARAMETERS:
+ *
+ * INPUT:
+ * @fs: The 'virtual mount' object
+ *
+ * @event_list: List of upcall events to be unregistered.
+ * Current available values are:
+ * - GFAPI_UPCALL_INODE_INVALIDATE
+ * RETURN VALUE:
+ * >0: SUCCESS (value contains the events successfully unregistered)
+ * -1: FAILURE
+ */
+int
+glfs_upcall_unregister (struct glfs *fs, uint32_t event_list);
+ GFAPI_PUBLIC(glfs_upcall_unregister, 3.13.0);
+
+__END_DECLS
#endif /* !_GLFS_H */
diff --git a/tests/basic/gfapi/upcall-register-api.c b/tests/basic/gfapi/upcall-register-api.c
new file mode 100644
index 00000000000..56227a93d0a
--- /dev/null
+++ b/tests/basic/gfapi/upcall-register-api.c
@@ -0,0 +1,281 @@
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <limits.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <glusterfs/api/glfs.h>
+#include <glusterfs/api/glfs-handles.h>
+
+#define LOG_ERR(func, ret) do { \
+ if (ret != 0) { \
+ fprintf (stderr, "%s : returned error %d (%s)\n", \
+ func, ret, strerror (errno)); \
+ goto out; \
+ } else { \
+ fprintf (stderr, "%s : returned %d\n", func, ret); \
+ } \
+ } while (0)
+
+int upcall_recv = 0;
+
+void up_async_invalidate (struct glfs_upcall *up_arg, void *data)
+{
+ struct glfs_upcall_inode *in_arg = NULL;
+ enum glfs_upcall_reason reason = 0;
+ struct glfs_object *object = NULL;
+ uint64_t flags = 0;
+ uint64_t expire = 0;
+
+ if (!up_arg)
+ return;
+
+ reason = glfs_upcall_get_reason (up_arg);
+
+ /* Expect 'GLFS_INODE_INVALIDATE' upcall event. */
+
+ if (reason == GLFS_UPCALL_INODE_INVALIDATE) {
+ in_arg = glfs_upcall_get_event (up_arg);
+
+ object = glfs_upcall_inode_get_object (in_arg);
+ flags = glfs_upcall_inode_get_flags (in_arg);
+ expire = glfs_upcall_inode_get_expire (in_arg);
+
+ fprintf (stderr, " upcall event type - %d,"
+ " object(%p), flags(%d), "
+ " expire_time_attr(%d)\n" ,
+ reason, object, flags, expire);
+ upcall_recv++;
+ }
+
+ glfs_free (up_arg);
+ return;
+}
+
+int perform_io (glfs_t *fs, glfs_t *fs2, int cnt)
+{
+ glfs_t *fs_tmp = NULL;
+ glfs_t *fs_tmp2 = NULL;
+ glfs_fd_t *fd_tmp = NULL;
+ glfs_fd_t *fd_tmp2 = NULL;
+ char readbuf[32];
+ char *writebuf = NULL;
+ glfs_fd_t *fd = NULL;
+ glfs_fd_t *fd2 = NULL;
+ char *filename = "file_tmp";
+ int ret = -1;
+
+ if (!fs || !fs2)
+ return -1;
+
+ /* Create file from fs and open it from fs2 */
+ fd = glfs_creat(fs, filename, O_RDWR|O_SYNC, 0644);
+ if (fd <= 0) {
+ ret = -1;
+ LOG_ERR ("glfs_creat", ret);
+ }
+
+ fd2 = glfs_open(fs2, filename, O_SYNC|O_RDWR|O_CREAT);
+ if (fd2 <= 0) {
+ ret = -1;
+ LOG_ERR ("glfs_open-fs2", ret);
+ }
+
+ do {
+ if (cnt%2) {
+ fd_tmp = fd;
+ fs_tmp = fs;
+ fd_tmp2 = fd2;
+ fs_tmp2 = fs2;
+ } else {
+ fd_tmp = fd2;
+ fs_tmp = fs2;
+ fd_tmp2 = fd;
+ fs_tmp2 = fs;
+ }
+
+ /* WRITE on fd_tmp */
+ writebuf = malloc(10);
+ if (writebuf) {
+ memcpy (writebuf, "abcd", 4);
+ ret = glfs_write (fd_tmp, writebuf, 4, 0);
+ if (ret <= 0) {
+ ret = -1;
+ LOG_ERR ("glfs_write", ret);
+ }
+ free(writebuf);
+ } else {
+ fprintf (stderr,
+ "Could not allocate writebuf\n");
+ return -1;
+ }
+
+ /* READ on fd_tmp2 */
+ ret = glfs_lseek (fd_tmp2, 0, SEEK_SET);
+ LOG_ERR ("glfs_lseek", ret);
+
+ memset (readbuf, 0, sizeof(readbuf));
+ ret = glfs_pread (fd_tmp2, readbuf, 4, 0, 0);
+
+ if (ret <= 0) {
+ ret = -1;
+ LOG_ERR ("glfs_pread", ret);
+ }
+
+ sleep(2);
+ } while (--cnt > 0);
+
+ sleep(2);
+
+ ret = 0;
+err:
+ glfs_close(fd);
+
+ glfs_close(fd2);
+
+out:
+ return ret;
+}
+
+int
+main (int argc, char *argv[])
+{
+ glfs_t *fs = NULL;
+ glfs_t *fs2 = NULL;
+ int ret = 0, i;
+ char *vol_id = NULL;
+ unsigned int cnt = 5;
+ struct glfs_upcall *cbk = NULL;
+ char *logfile = NULL;
+ char *volname = NULL;
+ char *hostname = NULL;
+ int up_events = GLFS_EVENT_ANY;
+
+ if (argc != 4) {
+ fprintf (stderr, "Invalid argument\n");
+ exit(1);
+ }
+
+ hostname = argv[1];
+ volname = argv[2];
+ logfile = argv[3];
+
+ /* Initialize fs */
+ fs = glfs_new (volname);
+ if (!fs) {
+ fprintf (stderr, "glfs_new: returned NULL\n");
+ return -1;
+ }
+
+ ret = glfs_set_volfile_server (fs, "tcp", hostname, 24007);
+ LOG_ERR("glfs_set_volfile_server", ret);
+
+ ret = glfs_set_logging (fs, logfile, 7);
+ LOG_ERR("glfs_set_logging", ret);
+
+ ret = glfs_init (fs);
+ LOG_ERR("glfs_init", ret);
+
+ /* Intialize fs2 */
+ fs2 = glfs_new (volname);
+ if (!fs2) {
+ fprintf (stderr, "glfs_new fs2: returned NULL\n");
+ return 1;
+ }
+
+ ret = glfs_set_volfile_server (fs2, "tcp", hostname, 24007);
+ LOG_ERR("glfs_set_volfile_server-fs2", ret);
+
+ ret = glfs_set_logging (fs2, logfile, 7);
+ LOG_ERR("glfs_set_logging-fs2", ret);
+
+ ret = glfs_init (fs2);
+ LOG_ERR("glfs_init-fs2", ret);
+
+ /* Register Upcalls */
+ ret = glfs_upcall_register (fs, up_events, up_async_invalidate, NULL);
+
+ /* Check if the return mask contains the event */
+ if (!(ret & GLFS_EVENT_INODE_INVALIDATE)) {
+ fprintf (stderr, "glfs_upcall_register return doesnt contain"
+ " upcall event\n");
+ return -1;
+ }
+
+ ret = glfs_upcall_register (fs2, up_events, up_async_invalidate, NULL);
+
+ /* Check if the return mask contains the event */
+ if ((ret < 0) || !(ret & GLFS_EVENT_INODE_INVALIDATE)) {
+ fprintf (stderr, "glfs_upcall_register return doesnt contain"
+ " upcall event\n");
+ return -1;
+ }
+
+ /* Perform I/O */
+ ret = perform_io (fs, fs2, cnt);
+ LOG_ERR("perform_io", ret);
+
+ if (upcall_recv == 0) {
+ fprintf (stderr, "Upcalls are not received.\n");
+ ret = -1;
+ } else {
+ fprintf (stderr, "Received %d upcalls as expected\n",
+ upcall_recv);
+ ret = 0;
+ }
+
+ sleep(5); /* to flush out previous upcalls if any */
+
+ /* Now unregister and check there are no upcall events received */
+ ret = glfs_upcall_unregister (fs, up_events);
+
+ /* Check if the return mask contains the event */
+ if ((ret < 0) || !(ret & GLFS_EVENT_INODE_INVALIDATE)) {
+ fprintf (stderr, "glfs_upcall_register return doesnt contain"
+ " upcall event\n");
+ return -1;
+ }
+
+ ret = glfs_upcall_unregister (fs2, up_events);
+
+ /* Check if the return mask contains the event */
+ if ((ret < 0) || !(ret & GLFS_EVENT_INODE_INVALIDATE)) {
+ fprintf (stderr, "glfs_upcall_register return doesnt contain"
+ " upcall event\n");
+ return -1;
+ }
+
+ upcall_recv = 0;
+
+ ret = perform_io (fs, fs2, cnt);
+ LOG_ERR("perform_io", ret);
+
+ if (upcall_recv != 0) {
+ fprintf (stderr, "%d upcalls received even after unregister.\n",
+ upcall_recv);
+ ret = -1;
+ } else {
+ fprintf (stderr, "Post unregister, no upcalls received as"
+ " expected\n");
+ ret = 0;
+ }
+
+out:
+ if (fs) {
+ ret = glfs_fini(fs);
+ fprintf (stderr, "glfs_fini(fs) returned %d\n", ret);
+ }
+
+ if (fs2) {
+ ret = glfs_fini(fs2);
+ fprintf (stderr, "glfs_fini(fs2) returned %d\n", ret);
+ }
+
+ if (ret)
+ exit(1);
+ exit(0);
+}
+
+
diff --git a/tests/basic/gfapi/upcall-register-api.t b/tests/basic/gfapi/upcall-register-api.t
new file mode 100755
index 00000000000..a46234ed7af
--- /dev/null
+++ b/tests/basic/gfapi/upcall-register-api.t
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+. $(dirname $0)/../../include.rc
+. $(dirname $0)/../../volume.rc
+
+cleanup;
+
+TEST glusterd
+
+TEST $CLI volume create $V0 $H0:$B0/brick1;
+EXPECT 'Created' volinfo_field $V0 'Status';
+
+TEST $CLI volume start $V0;
+EXPECT 'Started' volinfo_field $V0 'Status';
+
+logdir=`gluster --print-logdir`
+
+## Enable Upcall cache-invalidation feature
+TEST $CLI volume set $V0 features.cache-invalidation on;
+
+TEST build_tester $(dirname $0)/upcall-register-api.c -lgfapi
+
+TEST ./$(dirname $0)/upcall-register-api $H0 $V0 $logdir/upcall-register-api.log
+
+cleanup_tester $(dirname $0)/upcall-register-api
+
+TEST $CLI volume stop $V0
+TEST $CLI volume delete $V0
+
+cleanup;