summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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;