d2787b
From b924c8ca8a133fc9413c8ed1407e63f1658c7e79 Mon Sep 17 00:00:00 2001
d2787b
From: Xavi Hernandez <xhernandez@redhat.com>
d2787b
Date: Tue, 12 May 2020 23:54:54 +0200
d2787b
Subject: [PATCH 523/526] open-behind: rewrite of internal logic
d2787b
d2787b
There was a critical flaw in the previous implementation of open-behind.
d2787b
d2787b
When an open is done in the background, it's necessary to take a
d2787b
reference on the fd_t object because once we "fake" the open answer,
d2787b
the fd could be destroyed. However as long as there's a reference,
d2787b
the release function won't be called. So, if the application closes
d2787b
the file descriptor without having actually opened it, there will
d2787b
always remain at least 1 reference, causing a leak.
d2787b
d2787b
To avoid this problem, the previous implementation didn't take a
d2787b
reference on the fd_t, so there were races where the fd could be
d2787b
destroyed while it was still in use.
d2787b
d2787b
To fix this, I've implemented a new xlator cbk that gets called from
d2787b
fuse when the application closes a file descriptor.
d2787b
d2787b
The whole logic of handling background opens have been simplified and
d2787b
it's more efficient now. Only if the fop needs to be delayed until an
d2787b
open completes, a stub is created. Otherwise no memory allocations are
d2787b
needed.
d2787b
d2787b
Correctly handling the close request while the open is still pending
d2787b
has added a bit of complexity, but overall normal operation is simpler.
d2787b
d2787b
Upstream patch:
d2787b
> Upstream-patch-link: https://review.gluster.org/#/c/glusterfs/+/24451
d2787b
> Change-Id: I6376a5491368e0e1c283cc452849032636261592
d2787b
> Fixes: #1225
d2787b
> Signed-off-by: Xavi Hernandez <xhernandez@redhat.com>
d2787b
d2787b
BUG: 1830713
d2787b
Change-Id: I6376a5491368e0e1c283cc452849032636261592
d2787b
Signed-off-by: Xavi Hernandez <xhernandez@redhat.com>
d2787b
Reviewed-on: https://code.engineering.redhat.com/gerrit/224487
d2787b
Tested-by: RHGS Build Bot <nigelb@redhat.com>
d2787b
Reviewed-by: Sunil Kumar Heggodu Gopala Acharya <sheggodu@redhat.com>
d2787b
---
d2787b
 libglusterfs/src/fd.c                              |   26 +
d2787b
 libglusterfs/src/glusterfs/fd.h                    |    3 +
d2787b
 libglusterfs/src/glusterfs/xlator.h                |    4 +
d2787b
 libglusterfs/src/libglusterfs.sym                  |    1 +
d2787b
 tests/basic/open-behind/open-behind.t              |  183 +++
d2787b
 tests/basic/open-behind/tester-fd.c                |   99 ++
d2787b
 tests/basic/open-behind/tester.c                   |  444 +++++++
d2787b
 tests/basic/open-behind/tester.h                   |  145 +++
d2787b
 tests/bugs/glusterfs/bug-873962-spb.t              |    1 +
d2787b
 xlators/mount/fuse/src/fuse-bridge.c               |    2 +
d2787b
 .../open-behind/src/open-behind-messages.h         |    6 +-
d2787b
 xlators/performance/open-behind/src/open-behind.c  | 1302 ++++++++------------
d2787b
 12 files changed, 1393 insertions(+), 823 deletions(-)
d2787b
 create mode 100644 tests/basic/open-behind/open-behind.t
d2787b
 create mode 100644 tests/basic/open-behind/tester-fd.c
d2787b
 create mode 100644 tests/basic/open-behind/tester.c
d2787b
 create mode 100644 tests/basic/open-behind/tester.h
d2787b
d2787b
diff --git a/libglusterfs/src/fd.c b/libglusterfs/src/fd.c
d2787b
index 314546a..e4ec401 100644
d2787b
--- a/libglusterfs/src/fd.c
d2787b
+++ b/libglusterfs/src/fd.c
d2787b
@@ -501,6 +501,32 @@ out:
d2787b
 }
d2787b
 
d2787b
 void
d2787b
+fd_close(fd_t *fd)
d2787b
+{
d2787b
+    xlator_t *xl, *old_THIS;
d2787b
+
d2787b
+    old_THIS = THIS;
d2787b
+
d2787b
+    for (xl = fd->inode->table->xl->graph->first; xl != NULL; xl = xl->next) {
d2787b
+        if (!xl->call_cleanup) {
d2787b
+            THIS = xl;
d2787b
+
d2787b
+            if (IA_ISDIR(fd->inode->ia_type)) {
d2787b
+                if (xl->cbks->fdclosedir != NULL) {
d2787b
+                    xl->cbks->fdclosedir(xl, fd);
d2787b
+                }
d2787b
+            } else {
d2787b
+                if (xl->cbks->fdclose != NULL) {
d2787b
+                    xl->cbks->fdclose(xl, fd);
d2787b
+                }
d2787b
+            }
d2787b
+        }
d2787b
+    }
d2787b
+
d2787b
+    THIS = old_THIS;
d2787b
+}
d2787b
+
d2787b
+void
d2787b
 fd_unref(fd_t *fd)
d2787b
 {
d2787b
     int32_t refcount = 0;
d2787b
diff --git a/libglusterfs/src/glusterfs/fd.h b/libglusterfs/src/glusterfs/fd.h
d2787b
index cdbe289..4d157c4 100644
d2787b
--- a/libglusterfs/src/glusterfs/fd.h
d2787b
+++ b/libglusterfs/src/glusterfs/fd.h
d2787b
@@ -107,6 +107,9 @@ fd_ref(fd_t *fd);
d2787b
 void
d2787b
 fd_unref(fd_t *fd);
d2787b
 
d2787b
+void
d2787b
+fd_close(fd_t *fd);
d2787b
+
d2787b
 fd_t *
d2787b
 fd_create(struct _inode *inode, pid_t pid);
d2787b
 
d2787b
diff --git a/libglusterfs/src/glusterfs/xlator.h b/libglusterfs/src/glusterfs/xlator.h
d2787b
index 8650ccc..273039a 100644
d2787b
--- a/libglusterfs/src/glusterfs/xlator.h
d2787b
+++ b/libglusterfs/src/glusterfs/xlator.h
d2787b
@@ -705,6 +705,8 @@ typedef size_t (*cbk_inodectx_size_t)(xlator_t *this, inode_t *inode);
d2787b
 
d2787b
 typedef size_t (*cbk_fdctx_size_t)(xlator_t *this, fd_t *fd);
d2787b
 
d2787b
+typedef void (*cbk_fdclose_t)(xlator_t *this, fd_t *fd);
d2787b
+
d2787b
 struct xlator_cbks {
d2787b
     cbk_forget_t forget;
d2787b
     cbk_release_t release;
d2787b
@@ -715,6 +717,8 @@ struct xlator_cbks {
d2787b
     cbk_ictxmerge_t ictxmerge;
d2787b
     cbk_inodectx_size_t ictxsize;
d2787b
     cbk_fdctx_size_t fdctxsize;
d2787b
+    cbk_fdclose_t fdclose;
d2787b
+    cbk_fdclose_t fdclosedir;
d2787b
 };
d2787b
 
d2787b
 typedef int32_t (*dumpop_priv_t)(xlator_t *this);
d2787b
diff --git a/libglusterfs/src/libglusterfs.sym b/libglusterfs/src/libglusterfs.sym
d2787b
index bc770e2..0a0862e 100644
d2787b
--- a/libglusterfs/src/libglusterfs.sym
d2787b
+++ b/libglusterfs/src/libglusterfs.sym
d2787b
@@ -456,6 +456,7 @@ event_unregister_close
d2787b
 fd_anonymous
d2787b
 fd_anonymous_with_flags
d2787b
 fd_bind
d2787b
+fd_close
d2787b
 fd_create
d2787b
 fd_create_uint64
d2787b
 __fd_ctx_del
d2787b
diff --git a/tests/basic/open-behind/open-behind.t b/tests/basic/open-behind/open-behind.t
d2787b
new file mode 100644
d2787b
index 0000000..5e865d6
d2787b
--- /dev/null
d2787b
+++ b/tests/basic/open-behind/open-behind.t
d2787b
@@ -0,0 +1,183 @@
d2787b
+#!/bin/bash
d2787b
+
d2787b
+WD="$(dirname "${0}")"
d2787b
+
d2787b
+. ${WD}/../../include.rc
d2787b
+. ${WD}/../../volume.rc
d2787b
+
d2787b
+function assign() {
d2787b
+    local _assign_var="${1}"
d2787b
+    local _assign_value="${2}"
d2787b
+
d2787b
+    printf -v "${_assign_var}" "%s" "${_assign_value}"
d2787b
+}
d2787b
+
d2787b
+function pipe_create() {
d2787b
+    local _pipe_create_var="${1}"
d2787b
+    local _pipe_create_name
d2787b
+    local _pipe_create_fd
d2787b
+
d2787b
+    _pipe_create_name="$(mktemp -u)"
d2787b
+    mkfifo "${_pipe_create_name}"
d2787b
+    exec {_pipe_create_fd}<>"${_pipe_create_name}"
d2787b
+    rm "${_pipe_create_name}"
d2787b
+
d2787b
+    assign "${_pipe_create_var}" "${_pipe_create_fd}"
d2787b
+}
d2787b
+
d2787b
+function pipe_close() {
d2787b
+    local _pipe_close_fd="${!1}"
d2787b
+
d2787b
+    exec {_pipe_close_fd}>&-
d2787b
+}
d2787b
+
d2787b
+function tester_start() {
d2787b
+    declare -ag tester
d2787b
+    local tester_in
d2787b
+    local tester_out
d2787b
+
d2787b
+    pipe_create tester_in
d2787b
+    pipe_create tester_out
d2787b
+
d2787b
+    ${WD}/tester <&${tester_in} >&${tester_out} &
d2787b
+
d2787b
+    tester=("$!" "${tester_in}" "${tester_out}")
d2787b
+}
d2787b
+
d2787b
+function tester_send() {
d2787b
+    declare -ag tester
d2787b
+    local tester_res
d2787b
+    local tester_extra
d2787b
+
d2787b
+    echo "${*}" >&${tester[1]}
d2787b
+
d2787b
+    read -t 3 -u ${tester[2]} tester_res tester_extra
d2787b
+    echo "${tester_res} ${tester_extra}"
d2787b
+    if [[ "${tester_res}" == "OK" ]]; then
d2787b
+        return 0
d2787b
+    fi
d2787b
+
d2787b
+    return 1
d2787b
+}
d2787b
+
d2787b
+function tester_stop() {
d2787b
+    declare -ag tester
d2787b
+    local tester_res
d2787b
+
d2787b
+    tester_send "quit"
d2787b
+
d2787b
+    tester_res=0
d2787b
+    if ! wait ${tester[0]}; then
d2787b
+        tester_res=$?
d2787b
+    fi
d2787b
+
d2787b
+    unset tester
d2787b
+
d2787b
+    return ${tester_res}
d2787b
+}
d2787b
+
d2787b
+function count_open() {
d2787b
+    local file="$(realpath "${B0}/${V0}/${1}")"
d2787b
+    local count="0"
d2787b
+    local inode
d2787b
+    local ref
d2787b
+
d2787b
+    inode="$(stat -c %i "${file}")"
d2787b
+
d2787b
+    for fd in /proc/${BRICK_PID}/fd/*; do
d2787b
+        ref="$(readlink "${fd}")"
d2787b
+        if [[ "${ref}" == "${B0}/${V0}/"* ]]; then
d2787b
+            if [[ "$(stat -c %i "${ref}")" == "${inode}" ]]; then
d2787b
+                count="$((${count} + 1))"
d2787b
+            fi
d2787b
+        fi
d2787b
+    done
d2787b
+
d2787b
+    echo "${count}"
d2787b
+}
d2787b
+
d2787b
+cleanup
d2787b
+
d2787b
+TEST build_tester ${WD}/tester.c ${WD}/tester-fd.c
d2787b
+
d2787b
+TEST glusterd
d2787b
+TEST pidof glusterd
d2787b
+TEST ${CLI} volume create ${V0} ${H0}:${B0}/${V0}
d2787b
+TEST ${CLI} volume set ${V0} flush-behind off
d2787b
+TEST ${CLI} volume set ${V0} write-behind off
d2787b
+TEST ${CLI} volume set ${V0} quick-read off
d2787b
+TEST ${CLI} volume set ${V0} stat-prefetch on
d2787b
+TEST ${CLI} volume set ${V0} io-cache off
d2787b
+TEST ${CLI} volume set ${V0} open-behind on
d2787b
+TEST ${CLI} volume set ${V0} lazy-open off
d2787b
+TEST ${CLI} volume set ${V0} read-after-open off
d2787b
+TEST ${CLI} volume start ${V0}
d2787b
+
d2787b
+TEST ${GFS} --volfile-id=/${V0} --volfile-server=${H0} ${M0};
d2787b
+
d2787b
+BRICK_PID="$(get_brick_pid ${V0} ${H0} ${B0}/${V0})"
d2787b
+
d2787b
+TEST touch "${M0}/test"
d2787b
+
d2787b
+EXPECT_WITHIN $UMOUNT_TIMEOUT "Y" force_umount $M0
d2787b
+TEST ${GFS} --volfile-id=/${V0} --volfile-server=${H0} ${M0};
d2787b
+
d2787b
+TEST tester_start
d2787b
+
d2787b
+TEST tester_send fd open 0 "${M0}/test"
d2787b
+EXPECT_WITHIN 5 "1" count_open "/test"
d2787b
+TEST tester_send fd close 0
d2787b
+EXPECT_WITHIN 5 "0" count_open "/test"
d2787b
+
d2787b
+EXPECT_WITHIN $UMOUNT_TIMEOUT "Y" force_umount $M0
d2787b
+TEST ${CLI} volume set ${V0} lazy-open on
d2787b
+TEST ${GFS} --volfile-id=/${V0} --volfile-server=${H0} ${M0};
d2787b
+
d2787b
+TEST tester_send fd open 0 "${M0}/test"
d2787b
+sleep 2
d2787b
+EXPECT "0" count_open "/test"
d2787b
+TEST tester_send fd write 0 "test"
d2787b
+EXPECT "1" count_open "/test"
d2787b
+TEST tester_send fd close 0
d2787b
+EXPECT_WITHIN 5 "0" count_open "/test"
d2787b
+
d2787b
+EXPECT_WITHIN $UMOUNT_TIMEOUT "Y" force_umount $M0
d2787b
+TEST ${GFS} --volfile-id=/${V0} --volfile-server=${H0} ${M0};
d2787b
+
d2787b
+TEST tester_send fd open 0 "${M0}/test"
d2787b
+EXPECT "0" count_open "/test"
d2787b
+EXPECT "test" tester_send fd read 0 64
d2787b
+# Even though read-after-open is disabled, use-anonymous-fd is also disabled,
d2787b
+# so reads need to open the file first.
d2787b
+EXPECT "1" count_open "/test"
d2787b
+TEST tester_send fd close 0
d2787b
+EXPECT "0" count_open "/test"
d2787b
+
d2787b
+EXPECT_WITHIN $UMOUNT_TIMEOUT "Y" force_umount $M0
d2787b
+TEST ${GFS} --volfile-id=/${V0} --volfile-server=${H0} ${M0};
d2787b
+
d2787b
+TEST tester_send fd open 0 "${M0}/test"
d2787b
+EXPECT "0" count_open "/test"
d2787b
+TEST tester_send fd open 1 "${M0}/test"
d2787b
+EXPECT "2" count_open "/test"
d2787b
+TEST tester_send fd close 0
d2787b
+EXPECT_WITHIN 5 "1" count_open "/test"
d2787b
+TEST tester_send fd close 1
d2787b
+EXPECT_WITHIN 5 "0" count_open "/test"
d2787b
+
d2787b
+EXPECT_WITHIN $UMOUNT_TIMEOUT "Y" force_umount $M0
d2787b
+TEST ${CLI} volume set ${V0} read-after-open on
d2787b
+TEST ${GFS} --volfile-id=/${V0} --volfile-server=${H0} ${M0};
d2787b
+
d2787b
+TEST tester_send fd open 0 "${M0}/test"
d2787b
+EXPECT "0" count_open "/test"
d2787b
+EXPECT "test" tester_send fd read 0 64
d2787b
+EXPECT "1" count_open "/test"
d2787b
+TEST tester_send fd close 0
d2787b
+EXPECT_WITHIN 5 "0" count_open "/test"
d2787b
+
d2787b
+EXPECT_WITHIN $UMOUNT_TIMEOUT "Y" force_umount $M0
d2787b
+
d2787b
+TEST tester_stop
d2787b
+
d2787b
+cleanup
d2787b
diff --git a/tests/basic/open-behind/tester-fd.c b/tests/basic/open-behind/tester-fd.c
d2787b
new file mode 100644
d2787b
index 0000000..00f02bc
d2787b
--- /dev/null
d2787b
+++ b/tests/basic/open-behind/tester-fd.c
d2787b
@@ -0,0 +1,99 @@
d2787b
+/*
d2787b
+  Copyright (c) 2020 Red Hat, Inc. <http://www.redhat.com>
d2787b
+  This file is part of GlusterFS.
d2787b
+
d2787b
+  This file is licensed to you under your choice of the GNU Lesser
d2787b
+  General Public License, version 3 or any later version (LGPLv3 or
d2787b
+  later), or the GNU General Public License, version 2 (GPLv2), in all
d2787b
+  cases as published by the Free Software Foundation.
d2787b
+*/
d2787b
+
d2787b
+#include "tester.h"
d2787b
+
d2787b
+#include <stdlib.h>
d2787b
+#include <unistd.h>
d2787b
+#include <sys/types.h>
d2787b
+#include <sys/stat.h>
d2787b
+#include <fcntl.h>
d2787b
+#include <string.h>
d2787b
+#include <ctype.h>
d2787b
+#include <errno.h>
d2787b
+
d2787b
+static int32_t
d2787b
+fd_open(context_t *ctx, command_t *cmd)
d2787b
+{
d2787b
+    obj_t *obj;
d2787b
+    int32_t fd;
d2787b
+
d2787b
+    obj = cmd->args[0].obj.ref;
d2787b
+
d2787b
+    fd = open(cmd->args[1].str.data, O_RDWR);
d2787b
+    if (fd < 0) {
d2787b
+        return error(errno, "open() failed");
d2787b
+    }
d2787b
+
d2787b
+    obj->type = OBJ_TYPE_FD;
d2787b
+    obj->fd = fd;
d2787b
+
d2787b
+    out_ok("%d", fd);
d2787b
+
d2787b
+    return 0;
d2787b
+}
d2787b
+
d2787b
+static int32_t
d2787b
+fd_close(context_t *ctx, command_t *cmd)
d2787b
+{
d2787b
+    obj_t *obj;
d2787b
+
d2787b
+    obj = cmd->args[0].obj.ref;
d2787b
+    obj->type = OBJ_TYPE_NONE;
d2787b
+
d2787b
+    if (close(obj->fd) != 0) {
d2787b
+        return error(errno, "close() failed");
d2787b
+    }
d2787b
+
d2787b
+    out_ok();
d2787b
+
d2787b
+    return 0;
d2787b
+}
d2787b
+
d2787b
+static int32_t
d2787b
+fd_write(context_t *ctx, command_t *cmd)
d2787b
+{
d2787b
+    ssize_t len, ret;
d2787b
+
d2787b
+    len = strlen(cmd->args[1].str.data);
d2787b
+    ret = write(cmd->args[0].obj.ref->fd, cmd->args[1].str.data, len);
d2787b
+    if (ret < 0) {
d2787b
+        return error(errno, "write() failed");
d2787b
+    }
d2787b
+
d2787b
+    out_ok("%zd", ret);
d2787b
+
d2787b
+    return 0;
d2787b
+}
d2787b
+
d2787b
+static int32_t
d2787b
+fd_read(context_t *ctx, command_t *cmd)
d2787b
+{
d2787b
+    char data[cmd->args[1].num.value + 1];
d2787b
+    ssize_t ret;
d2787b
+
d2787b
+    ret = read(cmd->args[0].obj.ref->fd, data, cmd->args[1].num.value);
d2787b
+    if (ret < 0) {
d2787b
+        return error(errno, "read() failed");
d2787b
+    }
d2787b
+
d2787b
+    data[ret] = 0;
d2787b
+
d2787b
+    out_ok("%zd %s", ret, data);
d2787b
+
d2787b
+    return 0;
d2787b
+}
d2787b
+
d2787b
+command_t fd_commands[] = {
d2787b
+    {"open", fd_open, CMD_ARGS(ARG_VAL(OBJ_TYPE_NONE), ARG_STR(1024))},
d2787b
+    {"close", fd_close, CMD_ARGS(ARG_VAL(OBJ_TYPE_FD))},
d2787b
+    {"write", fd_write, CMD_ARGS(ARG_VAL(OBJ_TYPE_FD), ARG_STR(1024))},
d2787b
+    {"read", fd_read, CMD_ARGS(ARG_VAL(OBJ_TYPE_FD), ARG_NUM(0, 1024))},
d2787b
+    CMD_END};
d2787b
diff --git a/tests/basic/open-behind/tester.c b/tests/basic/open-behind/tester.c
d2787b
new file mode 100644
d2787b
index 0000000..b2da71c
d2787b
--- /dev/null
d2787b
+++ b/tests/basic/open-behind/tester.c
d2787b
@@ -0,0 +1,444 @@
d2787b
+/*
d2787b
+  Copyright (c) 2020 Red Hat, Inc. <http://www.redhat.com>
d2787b
+  This file is part of GlusterFS.
d2787b
+
d2787b
+  This file is licensed to you under your choice of the GNU Lesser
d2787b
+  General Public License, version 3 or any later version (LGPLv3 or
d2787b
+  later), or the GNU General Public License, version 2 (GPLv2), in all
d2787b
+  cases as published by the Free Software Foundation.
d2787b
+*/
d2787b
+
d2787b
+#include "tester.h"
d2787b
+
d2787b
+#include <stdlib.h>
d2787b
+#include <unistd.h>
d2787b
+#include <string.h>
d2787b
+#include <ctype.h>
d2787b
+#include <errno.h>
d2787b
+
d2787b
+static void *
d2787b
+mem_alloc(size_t size)
d2787b
+{
d2787b
+    void *ptr;
d2787b
+
d2787b
+    ptr = malloc(size);
d2787b
+    if (ptr == NULL) {
d2787b
+        error(ENOMEM, "Failed to allocate memory (%zu bytes)", size);
d2787b
+    }
d2787b
+
d2787b
+    return ptr;
d2787b
+}
d2787b
+
d2787b
+static void
d2787b
+mem_free(void *ptr)
d2787b
+{
d2787b
+    free(ptr);
d2787b
+}
d2787b
+
d2787b
+static bool
d2787b
+buffer_create(context_t *ctx, size_t size)
d2787b
+{
d2787b
+    ctx->buffer.base = mem_alloc(size);
d2787b
+    if (ctx->buffer.base == NULL) {
d2787b
+        return false;
d2787b
+    }
d2787b
+
d2787b
+    ctx->buffer.size = size;
d2787b
+    ctx->buffer.len = 0;
d2787b
+    ctx->buffer.pos = 0;
d2787b
+
d2787b
+    return true;
d2787b
+}
d2787b
+
d2787b
+static void
d2787b
+buffer_destroy(context_t *ctx)
d2787b
+{
d2787b
+    mem_free(ctx->buffer.base);
d2787b
+    ctx->buffer.size = 0;
d2787b
+    ctx->buffer.len = 0;
d2787b
+}
d2787b
+
d2787b
+static int32_t
d2787b
+buffer_get(context_t *ctx)
d2787b
+{
d2787b
+    ssize_t len;
d2787b
+
d2787b
+    if (ctx->buffer.pos >= ctx->buffer.len) {
d2787b
+        len = read(0, ctx->buffer.base, ctx->buffer.size);
d2787b
+        if (len < 0) {
d2787b
+            return error(errno, "read() failed");
d2787b
+        }
d2787b
+        if (len == 0) {
d2787b
+            return 0;
d2787b
+        }
d2787b
+
d2787b
+        ctx->buffer.len = len;
d2787b
+        ctx->buffer.pos = 0;
d2787b
+    }
d2787b
+
d2787b
+    return ctx->buffer.base[ctx->buffer.pos++];
d2787b
+}
d2787b
+
d2787b
+static int32_t
d2787b
+str_skip_spaces(context_t *ctx, int32_t current)
d2787b
+{
d2787b
+    while ((current > 0) && (current != '\n') && isspace(current)) {
d2787b
+        current = buffer_get(ctx);
d2787b
+    }
d2787b
+
d2787b
+    return current;
d2787b
+}
d2787b
+
d2787b
+static int32_t
d2787b
+str_token(context_t *ctx, char *buffer, uint32_t size, int32_t current)
d2787b
+{
d2787b
+    uint32_t len;
d2787b
+
d2787b
+    current = str_skip_spaces(ctx, current);
d2787b
+
d2787b
+    len = 0;
d2787b
+    while ((size > 0) && (current > 0) && (current != '\n') &&
d2787b
+           !isspace(current)) {
d2787b
+        len++;
d2787b
+        *buffer++ = current;
d2787b
+        size--;
d2787b
+        current = buffer_get(ctx);
d2787b
+    }
d2787b
+
d2787b
+    if (len == 0) {
d2787b
+        return error(ENODATA, "Expecting a token");
d2787b
+    }
d2787b
+
d2787b
+    if (size == 0) {
d2787b
+        return error(ENOBUFS, "Token too long");
d2787b
+    }
d2787b
+
d2787b
+    *buffer = 0;
d2787b
+
d2787b
+    return current;
d2787b
+}
d2787b
+
d2787b
+static int32_t
d2787b
+str_number(context_t *ctx, uint64_t min, uint64_t max, uint64_t *value,
d2787b
+           int32_t current)
d2787b
+{
d2787b
+    char text[32], *ptr;
d2787b
+    uint64_t num;
d2787b
+
d2787b
+    current = str_token(ctx, text, sizeof(text), current);
d2787b
+    if (current > 0) {
d2787b
+        num = strtoul(text, &ptr, 0);
d2787b
+        if ((*ptr != 0) || (num < min) || (num > max)) {
d2787b
+            return error(ERANGE, "Invalid number");
d2787b
+        }
d2787b
+        *value = num;
d2787b
+    }
d2787b
+
d2787b
+    return current;
d2787b
+}
d2787b
+
d2787b
+static int32_t
d2787b
+str_eol(context_t *ctx, int32_t current)
d2787b
+{
d2787b
+    current = str_skip_spaces(ctx, current);
d2787b
+    if (current != '\n') {
d2787b
+        return error(EINVAL, "Expecting end of command");
d2787b
+    }
d2787b
+
d2787b
+    return current;
d2787b
+}
d2787b
+
d2787b
+static void
d2787b
+str_skip(context_t *ctx, int32_t current)
d2787b
+{
d2787b
+    while ((current > 0) && (current != '\n')) {
d2787b
+        current = buffer_get(ctx);
d2787b
+    }
d2787b
+}
d2787b
+
d2787b
+static int32_t
d2787b
+cmd_parse_obj(context_t *ctx, arg_t *arg, int32_t current)
d2787b
+{
d2787b
+    obj_t *obj;
d2787b
+    uint64_t id;
d2787b
+
d2787b
+    current = str_number(ctx, 0, ctx->obj_count, &id, current);
d2787b
+    if (current <= 0) {
d2787b
+        return current;
d2787b
+    }
d2787b
+
d2787b
+    obj = &ctx->objs[id];
d2787b
+    if (obj->type != arg->obj.type) {
d2787b
+        if (obj->type != OBJ_TYPE_NONE) {
d2787b
+            return error(EBUSY, "Object is in use");
d2787b
+        }
d2787b
+        return error(ENOENT, "Object is not defined");
d2787b
+    }
d2787b
+
d2787b
+    arg->obj.ref = obj;
d2787b
+
d2787b
+    return current;
d2787b
+}
d2787b
+
d2787b
+static int32_t
d2787b
+cmd_parse_num(context_t *ctx, arg_t *arg, int32_t current)
d2787b
+{
d2787b
+    return str_number(ctx, arg->num.min, arg->num.max, &arg->num.value,
d2787b
+                      current);
d2787b
+}
d2787b
+
d2787b
+static int32_t
d2787b
+cmd_parse_str(context_t *ctx, arg_t *arg, int32_t current)
d2787b
+{
d2787b
+    return str_token(ctx, arg->str.data, arg->str.size, current);
d2787b
+}
d2787b
+
d2787b
+static int32_t
d2787b
+cmd_parse_args(context_t *ctx, command_t *cmd, int32_t current)
d2787b
+{
d2787b
+    arg_t *arg;
d2787b
+
d2787b
+    for (arg = cmd->args; arg->type != ARG_TYPE_NONE; arg++) {
d2787b
+        switch (arg->type) {
d2787b
+            case ARG_TYPE_OBJ:
d2787b
+                current = cmd_parse_obj(ctx, arg, current);
d2787b
+                break;
d2787b
+            case ARG_TYPE_NUM:
d2787b
+                current = cmd_parse_num(ctx, arg, current);
d2787b
+                break;
d2787b
+            case ARG_TYPE_STR:
d2787b
+                current = cmd_parse_str(ctx, arg, current);
d2787b
+                break;
d2787b
+            default:
d2787b
+                return error(EINVAL, "Unknown argument type");
d2787b
+        }
d2787b
+    }
d2787b
+
d2787b
+    if (current < 0) {
d2787b
+        return current;
d2787b
+    }
d2787b
+
d2787b
+    current = str_eol(ctx, current);
d2787b
+    if (current <= 0) {
d2787b
+        return error(EINVAL, "Syntax error");
d2787b
+    }
d2787b
+
d2787b
+    return cmd->handler(ctx, cmd);
d2787b
+}
d2787b
+
d2787b
+static int32_t
d2787b
+cmd_parse(context_t *ctx, command_t *cmds)
d2787b
+{
d2787b
+    char text[32];
d2787b
+    command_t *cmd;
d2787b
+    int32_t current;
d2787b
+
d2787b
+    cmd = cmds;
d2787b
+    do {
d2787b
+        current = str_token(ctx, text, sizeof(text), buffer_get(ctx));
d2787b
+        if (current <= 0) {
d2787b
+            return current;
d2787b
+        }
d2787b
+
d2787b
+        while (cmd->name != NULL) {
d2787b
+            if (strcmp(cmd->name, text) == 0) {
d2787b
+                if (cmd->handler != NULL) {
d2787b
+                    return cmd_parse_args(ctx, cmd, current);
d2787b
+                }
d2787b
+                cmd = cmd->cmds;
d2787b
+                break;
d2787b
+            }
d2787b
+            cmd++;
d2787b
+        }
d2787b
+    } while (cmd->name != NULL);
d2787b
+
d2787b
+    str_skip(ctx, current);
d2787b
+
d2787b
+    return error(ENOTSUP, "Unknown command");
d2787b
+}
d2787b
+
d2787b
+static void
d2787b
+cmd_fini(context_t *ctx, command_t *cmds)
d2787b
+{
d2787b
+    command_t *cmd;
d2787b
+    arg_t *arg;
d2787b
+
d2787b
+    for (cmd = cmds; cmd->name != NULL; cmd++) {
d2787b
+        if (cmd->handler == NULL) {
d2787b
+            cmd_fini(ctx, cmd->cmds);
d2787b
+        } else {
d2787b
+            for (arg = cmd->args; arg->type != ARG_TYPE_NONE; arg++) {
d2787b
+                switch (arg->type) {
d2787b
+                    case ARG_TYPE_STR:
d2787b
+                        mem_free(arg->str.data);
d2787b
+                        arg->str.data = NULL;
d2787b
+                        break;
d2787b
+                    default:
d2787b
+                        break;
d2787b
+                }
d2787b
+            }
d2787b
+        }
d2787b
+    }
d2787b
+}
d2787b
+
d2787b
+static bool
d2787b
+cmd_init(context_t *ctx, command_t *cmds)
d2787b
+{
d2787b
+    command_t *cmd;
d2787b
+    arg_t *arg;
d2787b
+
d2787b
+    for (cmd = cmds; cmd->name != NULL; cmd++) {
d2787b
+        if (cmd->handler == NULL) {
d2787b
+            if (!cmd_init(ctx, cmd->cmds)) {
d2787b
+                return false;
d2787b
+            }
d2787b
+        } else {
d2787b
+            for (arg = cmd->args; arg->type != ARG_TYPE_NONE; arg++) {
d2787b
+                switch (arg->type) {
d2787b
+                    case ARG_TYPE_STR:
d2787b
+                        arg->str.data = mem_alloc(arg->str.size);
d2787b
+                        if (arg->str.data == NULL) {
d2787b
+                            return false;
d2787b
+                        }
d2787b
+                        break;
d2787b
+                    default:
d2787b
+                        break;
d2787b
+                }
d2787b
+            }
d2787b
+        }
d2787b
+    }
d2787b
+
d2787b
+    return true;
d2787b
+}
d2787b
+
d2787b
+static bool
d2787b
+objs_create(context_t *ctx, uint32_t count)
d2787b
+{
d2787b
+    uint32_t i;
d2787b
+
d2787b
+    ctx->objs = mem_alloc(sizeof(obj_t) * count);
d2787b
+    if (ctx->objs == NULL) {
d2787b
+        return false;
d2787b
+    }
d2787b
+    ctx->obj_count = count;
d2787b
+
d2787b
+    for (i = 0; i < count; i++) {
d2787b
+        ctx->objs[i].type = OBJ_TYPE_NONE;
d2787b
+    }
d2787b
+
d2787b
+    return true;
d2787b
+}
d2787b
+
d2787b
+static int32_t
d2787b
+objs_destroy(context_t *ctx)
d2787b
+{
d2787b
+    uint32_t i;
d2787b
+    int32_t err;
d2787b
+
d2787b
+    err = 0;
d2787b
+    for (i = 0; i < ctx->obj_count; i++) {
d2787b
+        if (ctx->objs[i].type != OBJ_TYPE_NONE) {
d2787b
+            err = error(ENOTEMPTY, "Objects not destroyed");
d2787b
+            break;
d2787b
+        }
d2787b
+    }
d2787b
+
d2787b
+    mem_free(ctx->objs);
d2787b
+    ctx->objs = NULL;
d2787b
+    ctx->obj_count = 0;
d2787b
+
d2787b
+    return err;
d2787b
+}
d2787b
+
d2787b
+static context_t *
d2787b
+init(size_t size, uint32_t objs, command_t *cmds)
d2787b
+{
d2787b
+    context_t *ctx;
d2787b
+
d2787b
+    ctx = mem_alloc(sizeof(context_t));
d2787b
+    if (ctx == NULL) {
d2787b
+        goto failed;
d2787b
+    }
d2787b
+
d2787b
+    if (!buffer_create(ctx, size)) {
d2787b
+        goto failed_ctx;
d2787b
+    }
d2787b
+
d2787b
+    if (!objs_create(ctx, objs)) {
d2787b
+        goto failed_buffer;
d2787b
+    }
d2787b
+
d2787b
+    if (!cmd_init(ctx, cmds)) {
d2787b
+        goto failed_objs;
d2787b
+    }
d2787b
+
d2787b
+    ctx->active = true;
d2787b
+
d2787b
+    return ctx;
d2787b
+
d2787b
+failed_objs:
d2787b
+    cmd_fini(ctx, cmds);
d2787b
+    objs_destroy(ctx);
d2787b
+failed_buffer:
d2787b
+    buffer_destroy(ctx);
d2787b
+failed_ctx:
d2787b
+    mem_free(ctx);
d2787b
+failed:
d2787b
+    return NULL;
d2787b
+}
d2787b
+
d2787b
+static int32_t
d2787b
+fini(context_t *ctx, command_t *cmds)
d2787b
+{
d2787b
+    int32_t ret;
d2787b
+
d2787b
+    cmd_fini(ctx, cmds);
d2787b
+    buffer_destroy(ctx);
d2787b
+
d2787b
+    ret = objs_destroy(ctx);
d2787b
+
d2787b
+    ctx->active = false;
d2787b
+
d2787b
+    return ret;
d2787b
+}
d2787b
+
d2787b
+static int32_t
d2787b
+exec_quit(context_t *ctx, command_t *cmd)
d2787b
+{
d2787b
+    ctx->active = false;
d2787b
+
d2787b
+    return 0;
d2787b
+}
d2787b
+
d2787b
+static command_t commands[] = {{"fd", NULL, CMD_SUB(fd_commands)},
d2787b
+                               {"quit", exec_quit, CMD_ARGS()},
d2787b
+                               CMD_END};
d2787b
+
d2787b
+int32_t
d2787b
+main(int32_t argc, char *argv[])
d2787b
+{
d2787b
+    context_t *ctx;
d2787b
+    int32_t res;
d2787b
+
d2787b
+    ctx = init(1024, 16, commands);
d2787b
+    if (ctx == NULL) {
d2787b
+        return 1;
d2787b
+    }
d2787b
+
d2787b
+    do {
d2787b
+        res = cmd_parse(ctx, commands);
d2787b
+        if (res < 0) {
d2787b
+            out_err(-res);
d2787b
+        }
d2787b
+    } while (ctx->active);
d2787b
+
d2787b
+    res = fini(ctx, commands);
d2787b
+    if (res >= 0) {
d2787b
+        out_ok();
d2787b
+        return 0;
d2787b
+    }
d2787b
+
d2787b
+    out_err(-res);
d2787b
+
d2787b
+    return 1;
d2787b
+}
d2787b
diff --git a/tests/basic/open-behind/tester.h b/tests/basic/open-behind/tester.h
d2787b
new file mode 100644
d2787b
index 0000000..64e940c
d2787b
--- /dev/null
d2787b
+++ b/tests/basic/open-behind/tester.h
d2787b
@@ -0,0 +1,145 @@
d2787b
+/*
d2787b
+  Copyright (c) 2020 Red Hat, Inc. <http://www.redhat.com>
d2787b
+  This file is part of GlusterFS.
d2787b
+
d2787b
+  This file is licensed to you under your choice of the GNU Lesser
d2787b
+  General Public License, version 3 or any later version (LGPLv3 or
d2787b
+  later), or the GNU General Public License, version 2 (GPLv2), in all
d2787b
+  cases as published by the Free Software Foundation.
d2787b
+*/
d2787b
+
d2787b
+#ifndef __TESTER_H__
d2787b
+#define __TESTER_H__
d2787b
+
d2787b
+#include <stdio.h>
d2787b
+#include <inttypes.h>
d2787b
+#include <stdbool.h>
d2787b
+
d2787b
+enum _obj_type;
d2787b
+typedef enum _obj_type obj_type_t;
d2787b
+
d2787b
+enum _arg_type;
d2787b
+typedef enum _arg_type arg_type_t;
d2787b
+
d2787b
+struct _buffer;
d2787b
+typedef struct _buffer buffer_t;
d2787b
+
d2787b
+struct _obj;
d2787b
+typedef struct _obj obj_t;
d2787b
+
d2787b
+struct _context;
d2787b
+typedef struct _context context_t;
d2787b
+
d2787b
+struct _arg;
d2787b
+typedef struct _arg arg_t;
d2787b
+
d2787b
+struct _command;
d2787b
+typedef struct _command command_t;
d2787b
+
d2787b
+enum _obj_type { OBJ_TYPE_NONE, OBJ_TYPE_FD };
d2787b
+
d2787b
+enum _arg_type { ARG_TYPE_NONE, ARG_TYPE_OBJ, ARG_TYPE_NUM, ARG_TYPE_STR };
d2787b
+
d2787b
+struct _buffer {
d2787b
+    char *base;
d2787b
+    uint32_t size;
d2787b
+    uint32_t len;
d2787b
+    uint32_t pos;
d2787b
+};
d2787b
+
d2787b
+struct _obj {
d2787b
+    obj_type_t type;
d2787b
+    union {
d2787b
+        int32_t fd;
d2787b
+    };
d2787b
+};
d2787b
+
d2787b
+struct _context {
d2787b
+    obj_t *objs;
d2787b
+    buffer_t buffer;
d2787b
+    uint32_t obj_count;
d2787b
+    bool active;
d2787b
+};
d2787b
+
d2787b
+struct _arg {
d2787b
+    arg_type_t type;
d2787b
+    union {
d2787b
+        struct {
d2787b
+            obj_type_t type;
d2787b
+            obj_t *ref;
d2787b
+        } obj;
d2787b
+        struct {
d2787b
+            uint64_t value;
d2787b
+            uint64_t min;
d2787b
+            uint64_t max;
d2787b
+        } num;
d2787b
+        struct {
d2787b
+            uint32_t size;
d2787b
+            char *data;
d2787b
+        } str;
d2787b
+    };
d2787b
+};
d2787b
+
d2787b
+struct _command {
d2787b
+    const char *name;
d2787b
+    int32_t (*handler)(context_t *ctx, command_t *cmd);
d2787b
+    union {
d2787b
+        arg_t *args;
d2787b
+        command_t *cmds;
d2787b
+    };
d2787b
+};
d2787b
+
d2787b
+#define msg(_stream, _fmt, _args...)                                           \
d2787b
+    do {                                                                       \
d2787b
+        fprintf(_stream, _fmt "\n", ##_args);                                  \
d2787b
+        fflush(_stream);                                                       \
d2787b
+    } while (0)
d2787b
+
d2787b
+#define msg_out(_fmt, _args...) msg(stdout, _fmt, ##_args)
d2787b
+#define msg_err(_err, _fmt, _args...)                                          \
d2787b
+    ({                                                                         \
d2787b
+        int32_t __msg_err = (_err);                                            \
d2787b
+        msg(stderr, "[%4u:%-15s] " _fmt, __LINE__, __FUNCTION__, __msg_err,    \
d2787b
+            ##_args);                                                          \
d2787b
+        -__msg_err;                                                            \
d2787b
+    })
d2787b
+
d2787b
+#define error(_err, _fmt, _args...) msg_err(_err, "E(%4d) " _fmt, ##_args)
d2787b
+#define warn(_err, _fmt, _args...) msg_err(_err, "W(%4d) " _fmt, ##_args)
d2787b
+#define info(_err, _fmt, _args...) msg_err(_err, "I(%4d) " _fmt, ##_args)
d2787b
+
d2787b
+#define out_ok(_args...) msg_out("OK " _args)
d2787b
+#define out_err(_err) msg_out("ERR %d", _err)
d2787b
+
d2787b
+#define ARG_END                                                                \
d2787b
+    {                                                                          \
d2787b
+        ARG_TYPE_NONE                                                          \
d2787b
+    }
d2787b
+
d2787b
+#define CMD_ARGS1(_x, _args...)                                                \
d2787b
+    .args = (arg_t[]) { _args }
d2787b
+#define CMD_ARGS(_args...) CMD_ARGS1(, ##_args, ARG_END)
d2787b
+
d2787b
+#define CMD_SUB(_cmds) .cmds = _cmds
d2787b
+
d2787b
+#define CMD_END                                                                \
d2787b
+    {                                                                          \
d2787b
+        NULL, NULL, CMD_SUB(NULL)                                              \
d2787b
+    }
d2787b
+
d2787b
+#define ARG_VAL(_type)                                                         \
d2787b
+    {                                                                          \
d2787b
+        ARG_TYPE_OBJ, .obj = {.type = _type }                                  \
d2787b
+    }
d2787b
+#define ARG_NUM(_min, _max)                                                    \
d2787b
+    {                                                                          \
d2787b
+        ARG_TYPE_NUM, .num = {.min = _min, .max = _max }                       \
d2787b
+    }
d2787b
+#define ARG_STR(_size)                                                         \
d2787b
+    {                                                                          \
d2787b
+        ARG_TYPE_STR, .str = {.size = _size }                                  \
d2787b
+    }
d2787b
+
d2787b
+extern command_t fd_commands[];
d2787b
+
d2787b
+#endif /* __TESTER_H__ */
d2787b
\ No newline at end of file
d2787b
diff --git a/tests/bugs/glusterfs/bug-873962-spb.t b/tests/bugs/glusterfs/bug-873962-spb.t
d2787b
index db84a22..db71cc0 100644
d2787b
--- a/tests/bugs/glusterfs/bug-873962-spb.t
d2787b
+++ b/tests/bugs/glusterfs/bug-873962-spb.t
d2787b
@@ -14,6 +14,7 @@ TEST $CLI volume set $V0 performance.io-cache off
d2787b
 TEST $CLI volume set $V0 performance.write-behind off
d2787b
 TEST $CLI volume set $V0 performance.stat-prefetch off
d2787b
 TEST $CLI volume set $V0 performance.read-ahead off
d2787b
+TEST $CLI volume set $V0 performance.open-behind off
d2787b
 TEST $CLI volume set $V0 cluster.background-self-heal-count 0
d2787b
 TEST $CLI volume start $V0
d2787b
 TEST glusterfs --entry-timeout=0 --attribute-timeout=0 -s $H0 --volfile-id=$V0 $M0 --direct-io-mode=enable
d2787b
diff --git a/xlators/mount/fuse/src/fuse-bridge.c b/xlators/mount/fuse/src/fuse-bridge.c
d2787b
index 919eea3..76b5809 100644
d2787b
--- a/xlators/mount/fuse/src/fuse-bridge.c
d2787b
+++ b/xlators/mount/fuse/src/fuse-bridge.c
d2787b
@@ -3398,6 +3398,8 @@ fuse_release(xlator_t *this, fuse_in_header_t *finh, void *msg,
d2787b
     gf_log("glusterfs-fuse", GF_LOG_TRACE,
d2787b
            "finh->unique: %" PRIu64 ": RELEASE %p", finh->unique, state->fd);
d2787b
 
d2787b
+    fd_close(state->fd);
d2787b
+
d2787b
     fuse_fd_ctx_destroy(this, state->fd);
d2787b
     fd_unref(fd);
d2787b
 
d2787b
diff --git a/xlators/performance/open-behind/src/open-behind-messages.h b/xlators/performance/open-behind/src/open-behind-messages.h
d2787b
index f250824..0e78917 100644
d2787b
--- a/xlators/performance/open-behind/src/open-behind-messages.h
d2787b
+++ b/xlators/performance/open-behind/src/open-behind-messages.h
d2787b
@@ -23,6 +23,10 @@
d2787b
  */
d2787b
 
d2787b
 GLFS_MSGID(OPEN_BEHIND, OPEN_BEHIND_MSG_XLATOR_CHILD_MISCONFIGURED,
d2787b
-           OPEN_BEHIND_MSG_VOL_MISCONFIGURED, OPEN_BEHIND_MSG_NO_MEMORY);
d2787b
+           OPEN_BEHIND_MSG_VOL_MISCONFIGURED, OPEN_BEHIND_MSG_NO_MEMORY,
d2787b
+           OPEN_BEHIND_MSG_FAILED, OPEN_BEHIND_MSG_BAD_STATE);
d2787b
+
d2787b
+#define OPEN_BEHIND_MSG_FAILED_STR "Failed to submit fop"
d2787b
+#define OPEN_BEHIND_MSG_BAD_STATE_STR "Unexpected state"
d2787b
 
d2787b
 #endif /* _OPEN_BEHIND_MESSAGES_H_ */
d2787b
diff --git a/xlators/performance/open-behind/src/open-behind.c b/xlators/performance/open-behind/src/open-behind.c
d2787b
index cbe89ec..e43fe73 100644
d2787b
--- a/xlators/performance/open-behind/src/open-behind.c
d2787b
+++ b/xlators/performance/open-behind/src/open-behind.c
d2787b
@@ -16,6 +16,18 @@
d2787b
 #include "open-behind-messages.h"
d2787b
 #include <glusterfs/glusterfs-acl.h>
d2787b
 
d2787b
+/* Note: The initial design of open-behind was made to cover the simple case
d2787b
+ *       of open, read, close for small files. This pattern combined with
d2787b
+ *       quick-read can do the whole operation without a single request to the
d2787b
+ *       bricks (except the initial lookup).
d2787b
+ *
d2787b
+ *       The way to do this has been improved, but the logic remains the same.
d2787b
+ *       Basically, this means that any operation sent to the fd or the inode
d2787b
+ *       that it's not a read, causes the open request to be sent to the
d2787b
+ *       bricks, and all future operations will be executed synchronously,
d2787b
+ *       including opens (it's reset once all fd's are closed).
d2787b
+ */
d2787b
+
d2787b
 typedef struct ob_conf {
d2787b
     gf_boolean_t use_anonymous_fd; /* use anonymous FDs wherever safe
d2787b
                                       e.g - fstat() readv()
d2787b
@@ -32,1096 +44,754 @@ typedef struct ob_conf {
d2787b
                                         */
d2787b
 } ob_conf_t;
d2787b
 
d2787b
-typedef struct ob_inode {
d2787b
-    inode_t *inode;
d2787b
-    struct list_head resume_fops;
d2787b
-    struct list_head ob_fds;
d2787b
-    int count;
d2787b
-    int op_ret;
d2787b
-    int op_errno;
d2787b
-    gf_boolean_t open_in_progress;
d2787b
-    int unlinked;
d2787b
-} ob_inode_t;
d2787b
+/* A negative state represents an errno value negated. In this case the
d2787b
+ * current operation cannot be processed. */
d2787b
+typedef enum _ob_state {
d2787b
+    /* There are no opens on the inode or the first open is already
d2787b
+     * completed. The current operation can be sent directly. */
d2787b
+    OB_STATE_READY = 0,
d2787b
 
d2787b
-typedef struct ob_fd {
d2787b
-    call_frame_t *open_frame;
d2787b
-    loc_t loc;
d2787b
-    dict_t *xdata;
d2787b
-    int flags;
d2787b
-    int op_errno;
d2787b
-    ob_inode_t *ob_inode;
d2787b
-    fd_t *fd;
d2787b
-    gf_boolean_t opened;
d2787b
-    gf_boolean_t ob_inode_fops_waiting;
d2787b
-    struct list_head list;
d2787b
-    struct list_head ob_fds_on_inode;
d2787b
-} ob_fd_t;
d2787b
+    /* There's an open pending and it has been triggered. The current
d2787b
+     * operation should be "stubbified" and processed with
d2787b
+     * ob_stub_dispatch(). */
d2787b
+    OB_STATE_OPEN_TRIGGERED,
d2787b
 
d2787b
-ob_inode_t *
d2787b
-ob_inode_alloc(inode_t *inode)
d2787b
-{
d2787b
-    ob_inode_t *ob_inode = NULL;
d2787b
+    /* There's an open pending but it has not been triggered. The current
d2787b
+     * operation can be processed directly but using an anonymous fd. */
d2787b
+    OB_STATE_OPEN_PENDING,
d2787b
 
d2787b
-    ob_inode = GF_CALLOC(1, sizeof(*ob_inode), gf_ob_mt_inode_t);
d2787b
-    if (ob_inode == NULL)
d2787b
-        goto out;
d2787b
+    /* The current operation is the first open on the inode. */
d2787b
+    OB_STATE_FIRST_OPEN
d2787b
+} ob_state_t;
d2787b
 
d2787b
-    ob_inode->inode = inode;
d2787b
-    INIT_LIST_HEAD(&ob_inode->resume_fops);
d2787b
-    INIT_LIST_HEAD(&ob_inode->ob_fds);
d2787b
-out:
d2787b
-    return ob_inode;
d2787b
-}
d2787b
-
d2787b
-void
d2787b
-ob_inode_free(ob_inode_t *ob_inode)
d2787b
-{
d2787b
-    if (ob_inode == NULL)
d2787b
-        goto out;
d2787b
+typedef struct ob_inode {
d2787b
+    /* List of stubs pending on the first open. Once the first open is
d2787b
+     * complete, all these stubs will be resubmitted, and dependencies
d2787b
+     * will be checked again. */
d2787b
+    struct list_head resume_fops;
d2787b
 
d2787b
-    list_del_init(&ob_inode->resume_fops);
d2787b
-    list_del_init(&ob_inode->ob_fds);
d2787b
+    /* The inode this object references. */
d2787b
+    inode_t *inode;
d2787b
 
d2787b
-    GF_FREE(ob_inode);
d2787b
-out:
d2787b
-    return;
d2787b
-}
d2787b
+    /* The fd from the first open sent to this inode. It will be set
d2787b
+     * from the moment the open is processed until the open if fully
d2787b
+     * executed or closed before actually opened. It's NULL in all
d2787b
+     * other cases. */
d2787b
+    fd_t *first_fd;
d2787b
+
d2787b
+    /* The stub from the first open operation. When open fop starts
d2787b
+     * being processed, it's assigned the OB_OPEN_PREPARING value
d2787b
+     * until the actual stub is created. This is necessary to avoid
d2787b
+     * creating the stub inside a locked region. Once the stub is
d2787b
+     * successfully created, it's assigned here. This value is set
d2787b
+     * to NULL once the stub is resumed. */
d2787b
+    call_stub_t *first_open;
d2787b
+
d2787b
+    /* The total number of currently open fd's on this inode. */
d2787b
+    int32_t open_count;
d2787b
+
d2787b
+    /* This flag is set as soon as we know that the open will be
d2787b
+     * sent to the bricks, even before the stub is ready. */
d2787b
+    bool triggered;
d2787b
+} ob_inode_t;
d2787b
 
d2787b
-ob_inode_t *
d2787b
-ob_inode_get(xlator_t *this, inode_t *inode)
d2787b
+/* Dummy pointer used temporarily while the actual open stub is being created */
d2787b
+#define OB_OPEN_PREPARING ((call_stub_t *)-1)
d2787b
+
d2787b
+#define OB_POST_COMMON(_fop, _xl, _frame, _fd, _args...)                       \
d2787b
+    case OB_STATE_FIRST_OPEN:                                                  \
d2787b
+        gf_smsg((_xl)->name, GF_LOG_ERROR, EINVAL, OPEN_BEHIND_MSG_BAD_STATE,  \
d2787b
+                "fop=%s", #_fop, "state=%d", __ob_state, NULL);                \
d2787b
+        default_##_fop##_failure_cbk(_frame, EINVAL);                          \
d2787b
+        break;                                                                 \
d2787b
+    case OB_STATE_READY:                                                       \
d2787b
+        default_##_fop(_frame, _xl, ##_args);                                  \
d2787b
+        break;                                                                 \
d2787b
+    case OB_STATE_OPEN_TRIGGERED: {                                            \
d2787b
+        call_stub_t *__ob_stub = fop_##_fop##_stub(_frame, ob_##_fop,          \
d2787b
+                                                   ##_args);                   \
d2787b
+        if (__ob_stub != NULL) {                                               \
d2787b
+            ob_stub_dispatch(_xl, __ob_inode, _fd, __ob_stub);                 \
d2787b
+            break;                                                             \
d2787b
+        }                                                                      \
d2787b
+        __ob_state = -ENOMEM;                                                  \
d2787b
+    }                                                                          \
d2787b
+    default:                                                                   \
d2787b
+        gf_smsg((_xl)->name, GF_LOG_ERROR, -__ob_state,                        \
d2787b
+                OPEN_BEHIND_MSG_FAILED, "fop=%s", #_fop, NULL);                \
d2787b
+        default_##_fop##_failure_cbk(_frame, -__ob_state)
d2787b
+
d2787b
+#define OB_POST_FD(_fop, _xl, _frame, _fd, _trigger, _args...)                 \
d2787b
+    do {                                                                       \
d2787b
+        ob_inode_t *__ob_inode;                                                \
d2787b
+        fd_t *__first_fd;                                                      \
d2787b
+        ob_state_t __ob_state = ob_open_and_resume_fd(                         \
d2787b
+            _xl, _fd, 0, true, _trigger, &__ob_inode, &__first_fd);            \
d2787b
+        switch (__ob_state) {                                                  \
d2787b
+            case OB_STATE_OPEN_PENDING:                                        \
d2787b
+                if (!(_trigger)) {                                             \
d2787b
+                    fd_t *__ob_fd = fd_anonymous_with_flags((_fd)->inode,      \
d2787b
+                                                            (_fd)->flags);     \
d2787b
+                    if (__ob_fd != NULL) {                                     \
d2787b
+                        default_##_fop(_frame, _xl, ##_args);                  \
d2787b
+                        fd_unref(__ob_fd);                                     \
d2787b
+                        break;                                                 \
d2787b
+                    }                                                          \
d2787b
+                    __ob_state = -ENOMEM;                                      \
d2787b
+                }                                                              \
d2787b
+                OB_POST_COMMON(_fop, _xl, _frame, __first_fd, ##_args);        \
d2787b
+        }                                                                      \
d2787b
+    } while (0)
d2787b
+
d2787b
+#define OB_POST_FLUSH(_xl, _frame, _fd, _args...)                              \
d2787b
+    do {                                                                       \
d2787b
+        ob_inode_t *__ob_inode;                                                \
d2787b
+        fd_t *__first_fd;                                                      \
d2787b
+        ob_state_t __ob_state = ob_open_and_resume_fd(                         \
d2787b
+            _xl, _fd, 0, true, false, &__ob_inode, &__first_fd);               \
d2787b
+        switch (__ob_state) {                                                  \
d2787b
+            case OB_STATE_OPEN_PENDING:                                        \
d2787b
+                default_flush_cbk(_frame, NULL, _xl, 0, 0, NULL);              \
d2787b
+                break;                                                         \
d2787b
+                OB_POST_COMMON(flush, _xl, _frame, __first_fd, ##_args);       \
d2787b
+        }                                                                      \
d2787b
+    } while (0)
d2787b
+
d2787b
+#define OB_POST_INODE(_fop, _xl, _frame, _inode, _trigger, _args...)           \
d2787b
+    do {                                                                       \
d2787b
+        ob_inode_t *__ob_inode;                                                \
d2787b
+        fd_t *__first_fd;                                                      \
d2787b
+        ob_state_t __ob_state = ob_open_and_resume_inode(                      \
d2787b
+            _xl, _inode, NULL, 0, true, _trigger, &__ob_inode, &__first_fd);   \
d2787b
+        switch (__ob_state) {                                                  \
d2787b
+            case OB_STATE_OPEN_PENDING:                                        \
d2787b
+                OB_POST_COMMON(_fop, _xl, _frame, __first_fd, ##_args);        \
d2787b
+        }                                                                      \
d2787b
+    } while (0)
d2787b
+
d2787b
+static ob_inode_t *
d2787b
+ob_inode_get_locked(xlator_t *this, inode_t *inode)
d2787b
 {
d2787b
     ob_inode_t *ob_inode = NULL;
d2787b
     uint64_t value = 0;
d2787b
-    int ret = 0;
d2787b
 
d2787b
-    if (!inode)
d2787b
-        goto out;
d2787b
+    if ((__inode_ctx_get(inode, this, &value) == 0) && (value != 0)) {
d2787b
+        return (ob_inode_t *)(uintptr_t)value;
d2787b
+    }
d2787b
 
d2787b
-    LOCK(&inode->lock);
d2787b
-    {
d2787b
-        __inode_ctx_get(inode, this, &value);
d2787b
-        if (value == 0) {
d2787b
-            ob_inode = ob_inode_alloc(inode);
d2787b
-            if (ob_inode == NULL)
d2787b
-                goto unlock;
d2787b
-
d2787b
-            value = (uint64_t)(uintptr_t)ob_inode;
d2787b
-            ret = __inode_ctx_set(inode, this, &value);
d2787b
-            if (ret < 0) {
d2787b
-                ob_inode_free(ob_inode);
d2787b
-                ob_inode = NULL;
d2787b
-            }
d2787b
-        } else {
d2787b
-            ob_inode = (ob_inode_t *)(uintptr_t)value;
d2787b
+    ob_inode = GF_CALLOC(1, sizeof(*ob_inode), gf_ob_mt_inode_t);
d2787b
+    if (ob_inode != NULL) {
d2787b
+        ob_inode->inode = inode;
d2787b
+        INIT_LIST_HEAD(&ob_inode->resume_fops);
d2787b
+
d2787b
+        value = (uint64_t)(uintptr_t)ob_inode;
d2787b
+        if (__inode_ctx_set(inode, this, &value) < 0) {
d2787b
+            GF_FREE(ob_inode);
d2787b
+            ob_inode = NULL;
d2787b
         }
d2787b
     }
d2787b
-unlock:
d2787b
-    UNLOCK(&inode->lock);
d2787b
 
d2787b
-out:
d2787b
     return ob_inode;
d2787b
 }
d2787b
 
d2787b
-ob_fd_t *
d2787b
-__ob_fd_ctx_get(xlator_t *this, fd_t *fd)
d2787b
+static ob_state_t
d2787b
+ob_open_and_resume_inode(xlator_t *xl, inode_t *inode, fd_t *fd,
d2787b
+                         int32_t open_count, bool synchronous, bool trigger,
d2787b
+                         ob_inode_t **pob_inode, fd_t **pfd)
d2787b
 {
d2787b
-    uint64_t value = 0;
d2787b
-    int ret = -1;
d2787b
-    ob_fd_t *ob_fd = NULL;
d2787b
+    ob_conf_t *conf;
d2787b
+    ob_inode_t *ob_inode;
d2787b
+    call_stub_t *open_stub;
d2787b
 
d2787b
-    ret = __fd_ctx_get(fd, this, &value);
d2787b
-    if (ret)
d2787b
-        return NULL;
d2787b
+    if (inode == NULL) {
d2787b
+        return OB_STATE_READY;
d2787b
+    }
d2787b
 
d2787b
-    ob_fd = (void *)((long)value);
d2787b
+    conf = xl->private;
d2787b
 
d2787b
-    return ob_fd;
d2787b
-}
d2787b
+    *pfd = NULL;
d2787b
 
d2787b
-ob_fd_t *
d2787b
-ob_fd_ctx_get(xlator_t *this, fd_t *fd)
d2787b
-{
d2787b
-    ob_fd_t *ob_fd = NULL;
d2787b
-
d2787b
-    LOCK(&fd->lock);
d2787b
+    LOCK(&inode->lock);
d2787b
     {
d2787b
-        ob_fd = __ob_fd_ctx_get(this, fd);
d2787b
-    }
d2787b
-    UNLOCK(&fd->lock);
d2787b
-
d2787b
-    return ob_fd;
d2787b
-}
d2787b
+        ob_inode = ob_inode_get_locked(xl, inode);
d2787b
+        if (ob_inode == NULL) {
d2787b
+            UNLOCK(&inode->lock);
d2787b
 
d2787b
-int
d2787b
-__ob_fd_ctx_set(xlator_t *this, fd_t *fd, ob_fd_t *ob_fd)
d2787b
-{
d2787b
-    uint64_t value = 0;
d2787b
-    int ret = -1;
d2787b
+            return -ENOMEM;
d2787b
+        }
d2787b
+        *pob_inode = ob_inode;
d2787b
+
d2787b
+        ob_inode->open_count += open_count;
d2787b
+
d2787b
+        /* If first_fd is not NULL, it means that there's a previous open not
d2787b
+         * yet completed. */
d2787b
+        if (ob_inode->first_fd != NULL) {
d2787b
+            *pfd = ob_inode->first_fd;
d2787b
+            /* If the current request doesn't trigger the open and it hasn't
d2787b
+             * been triggered yet, we can continue without issuing the open
d2787b
+             * only if the current request belongs to the same fd as the
d2787b
+             * first one. */
d2787b
+            if (!trigger && !ob_inode->triggered &&
d2787b
+                (ob_inode->first_fd == fd)) {
d2787b
+                UNLOCK(&inode->lock);
d2787b
+
d2787b
+                return OB_STATE_OPEN_PENDING;
d2787b
+            }
d2787b
 
d2787b
-    value = (long)((void *)ob_fd);
d2787b
+            /* We need to issue the open. It could have already been triggered
d2787b
+             * before. In this case open_stub will be NULL. Or the initial open
d2787b
+             * may not be completely ready yet. In this case open_stub will be
d2787b
+             * OB_OPEN_PREPARING. */
d2787b
+            open_stub = ob_inode->first_open;
d2787b
+            ob_inode->first_open = NULL;
d2787b
+            ob_inode->triggered = true;
d2787b
 
d2787b
-    ret = __fd_ctx_set(fd, this, value);
d2787b
+            UNLOCK(&inode->lock);
d2787b
 
d2787b
-    return ret;
d2787b
-}
d2787b
+            if ((open_stub != NULL) && (open_stub != OB_OPEN_PREPARING)) {
d2787b
+                call_resume(open_stub);
d2787b
+            }
d2787b
 
d2787b
-int
d2787b
-ob_fd_ctx_set(xlator_t *this, fd_t *fd, ob_fd_t *ob_fd)
d2787b
-{
d2787b
-    int ret = -1;
d2787b
+            return OB_STATE_OPEN_TRIGGERED;
d2787b
+        }
d2787b
 
d2787b
-    LOCK(&fd->lock);
d2787b
-    {
d2787b
-        ret = __ob_fd_ctx_set(this, fd, ob_fd);
d2787b
-    }
d2787b
-    UNLOCK(&fd->lock);
d2787b
+        /* There's no pending open. Only opens can be non synchronous, so all
d2787b
+         * regular fops will be processed directly. For non synchronous opens,
d2787b
+         * we'll still process them normally (i.e. synchornous) if there are
d2787b
+         * more file descriptors open. */
d2787b
+        if (synchronous || (ob_inode->open_count > open_count)) {
d2787b
+            UNLOCK(&inode->lock);
d2787b
 
d2787b
-    return ret;
d2787b
-}
d2787b
+            return OB_STATE_READY;
d2787b
+        }
d2787b
 
d2787b
-ob_fd_t *
d2787b
-ob_fd_new(void)
d2787b
-{
d2787b
-    ob_fd_t *ob_fd = NULL;
d2787b
+        *pfd = fd;
d2787b
 
d2787b
-    ob_fd = GF_CALLOC(1, sizeof(*ob_fd), gf_ob_mt_fd_t);
d2787b
+        /* This is the first open. We keep a reference on the fd and set
d2787b
+         * first_open stub to OB_OPEN_PREPARING until the actual stub can
d2787b
+         * be assigned (we don't create the stub here to avoid doing memory
d2787b
+         * allocations inside the mutex). */
d2787b
+        ob_inode->first_fd = __fd_ref(fd);
d2787b
+        ob_inode->first_open = OB_OPEN_PREPARING;
d2787b
 
d2787b
-    INIT_LIST_HEAD(&ob_fd->list);
d2787b
-    INIT_LIST_HEAD(&ob_fd->ob_fds_on_inode);
d2787b
+        /* If lazy_open is not set, we'll need to immediately send the open,
d2787b
+         * so we set triggered right now. */
d2787b
+        ob_inode->triggered = !conf->lazy_open;
d2787b
+    }
d2787b
+    UNLOCK(&inode->lock);
d2787b
 
d2787b
-    return ob_fd;
d2787b
+    return OB_STATE_FIRST_OPEN;
d2787b
 }
d2787b
 
d2787b
-void
d2787b
-ob_fd_free(ob_fd_t *ob_fd)
d2787b
+static ob_state_t
d2787b
+ob_open_and_resume_fd(xlator_t *xl, fd_t *fd, int32_t open_count,
d2787b
+                      bool synchronous, bool trigger, ob_inode_t **pob_inode,
d2787b
+                      fd_t **pfd)
d2787b
 {
d2787b
-    LOCK(&ob_fd->fd->inode->lock);
d2787b
-    {
d2787b
-        list_del_init(&ob_fd->ob_fds_on_inode);
d2787b
-    }
d2787b
-    UNLOCK(&ob_fd->fd->inode->lock);
d2787b
-
d2787b
-    loc_wipe(&ob_fd->loc);
d2787b
-
d2787b
-    if (ob_fd->xdata)
d2787b
-        dict_unref(ob_fd->xdata);
d2787b
+    uint64_t err;
d2787b
 
d2787b
-    if (ob_fd->open_frame) {
d2787b
-        /* If we sill have a frame it means that background open has never
d2787b
-         * been triggered. We need to release the pending reference. */
d2787b
-        fd_unref(ob_fd->fd);
d2787b
-
d2787b
-        STACK_DESTROY(ob_fd->open_frame->root);
d2787b
+    if ((fd_ctx_get(fd, xl, &err) == 0) && (err != 0)) {
d2787b
+        return (ob_state_t)-err;
d2787b
     }
d2787b
 
d2787b
-    GF_FREE(ob_fd);
d2787b
+    return ob_open_and_resume_inode(xl, fd->inode, fd, open_count, synchronous,
d2787b
+                                    trigger, pob_inode, pfd);
d2787b
 }
d2787b
 
d2787b
-int
d2787b
-ob_wake_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int op_ret,
d2787b
-            int op_errno, fd_t *fd_ret, dict_t *xdata)
d2787b
+static ob_state_t
d2787b
+ob_open_behind(xlator_t *xl, fd_t *fd, int32_t flags, ob_inode_t **pob_inode,
d2787b
+               fd_t **pfd)
d2787b
 {
d2787b
-    fd_t *fd = NULL;
d2787b
-    int count = 0;
d2787b
-    int ob_inode_op_ret = 0;
d2787b
-    int ob_inode_op_errno = 0;
d2787b
-    ob_fd_t *ob_fd = NULL;
d2787b
-    call_stub_t *stub = NULL, *tmp = NULL;
d2787b
-    ob_inode_t *ob_inode = NULL;
d2787b
-    gf_boolean_t ob_inode_fops_waiting = _gf_false;
d2787b
-    struct list_head fops_waiting_on_fd, fops_waiting_on_inode;
d2787b
+    bool synchronous;
d2787b
 
d2787b
-    fd = frame->local;
d2787b
-    frame->local = NULL;
d2787b
-
d2787b
-    INIT_LIST_HEAD(&fops_waiting_on_fd);
d2787b
-    INIT_LIST_HEAD(&fops_waiting_on_inode);
d2787b
+    /* TODO: If O_CREAT, O_APPEND, O_WRONLY or O_DIRECT are specified, shouldn't
d2787b
+     *       we also execute this open synchronously ? */
d2787b
+    synchronous = (flags & O_TRUNC) != 0;
d2787b
 
d2787b
-    ob_inode = ob_inode_get(this, fd->inode);
d2787b
+    return ob_open_and_resume_fd(xl, fd, 1, synchronous, true, pob_inode, pfd);
d2787b
+}
d2787b
 
d2787b
-    LOCK(&fd->lock);
d2787b
+static int32_t
d2787b
+ob_stub_dispatch(xlator_t *xl, ob_inode_t *ob_inode, fd_t *fd,
d2787b
+                 call_stub_t *stub)
d2787b
+{
d2787b
+    LOCK(&ob_inode->inode->lock);
d2787b
     {
d2787b
-        ob_fd = __ob_fd_ctx_get(this, fd);
d2787b
-        ob_fd->opened = _gf_true;
d2787b
-
d2787b
-        ob_inode_fops_waiting = ob_fd->ob_inode_fops_waiting;
d2787b
-
d2787b
-        list_splice_init(&ob_fd->list, &fops_waiting_on_fd);
d2787b
-
d2787b
-        if (op_ret < 0) {
d2787b
-            /* mark fd BAD for ever */
d2787b
-            ob_fd->op_errno = op_errno;
d2787b
-            ob_fd = NULL; /*shouldn't be freed*/
d2787b
-        } else {
d2787b
-            __fd_ctx_del(fd, this, NULL);
d2787b
-        }
d2787b
-    }
d2787b
-    UNLOCK(&fd->lock);
d2787b
-
d2787b
-    if (ob_inode_fops_waiting) {
d2787b
-        LOCK(&fd->inode->lock);
d2787b
-        {
d2787b
-            count = --ob_inode->count;
d2787b
-            if (op_ret < 0) {
d2787b
-                /* TODO: when to reset the error? */
d2787b
-                ob_inode->op_ret = -1;
d2787b
-                ob_inode->op_errno = op_errno;
d2787b
-            }
d2787b
-
d2787b
-            if (count == 0) {
d2787b
-                ob_inode->open_in_progress = _gf_false;
d2787b
-                ob_inode_op_ret = ob_inode->op_ret;
d2787b
-                ob_inode_op_errno = ob_inode->op_errno;
d2787b
-                list_splice_init(&ob_inode->resume_fops,
d2787b
-                                 &fops_waiting_on_inode);
d2787b
-            }
d2787b
+        /* We only queue a stub if the open has not been completed or
d2787b
+         * cancelled. */
d2787b
+        if (ob_inode->first_fd == fd) {
d2787b
+            list_add_tail(&stub->list, &ob_inode->resume_fops);
d2787b
+            stub = NULL;
d2787b
         }
d2787b
-        UNLOCK(&fd->inode->lock);
d2787b
-    }
d2787b
-
d2787b
-    if (ob_fd)
d2787b
-        ob_fd_free(ob_fd);
d2787b
-
d2787b
-    list_for_each_entry_safe(stub, tmp, &fops_waiting_on_fd, list)
d2787b
-    {
d2787b
-        list_del_init(&stub->list);
d2787b
-
d2787b
-        if (op_ret < 0)
d2787b
-            call_unwind_error(stub, -1, op_errno);
d2787b
-        else
d2787b
-            call_resume(stub);
d2787b
     }
d2787b
+    UNLOCK(&ob_inode->inode->lock);
d2787b
 
d2787b
-    list_for_each_entry_safe(stub, tmp, &fops_waiting_on_inode, list)
d2787b
-    {
d2787b
-        list_del_init(&stub->list);
d2787b
-
d2787b
-        if (ob_inode_op_ret < 0)
d2787b
-            call_unwind_error(stub, -1, ob_inode_op_errno);
d2787b
-        else
d2787b
-            call_resume(stub);
d2787b
+    if (stub != NULL) {
d2787b
+        call_resume(stub);
d2787b
     }
d2787b
 
d2787b
-    /* The background open is completed. We can release the 'fd' reference. */
d2787b
-    fd_unref(fd);
d2787b
-
d2787b
-    STACK_DESTROY(frame->root);
d2787b
-
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
-int
d2787b
-ob_fd_wake(xlator_t *this, fd_t *fd, ob_fd_t *ob_fd)
d2787b
+static int32_t
d2787b
+ob_open_dispatch(xlator_t *xl, ob_inode_t *ob_inode, fd_t *fd,
d2787b
+                 call_stub_t *stub)
d2787b
 {
d2787b
-    call_frame_t *frame = NULL;
d2787b
-
d2787b
-    if (ob_fd == NULL) {
d2787b
-        LOCK(&fd->lock);
d2787b
-        {
d2787b
-            ob_fd = __ob_fd_ctx_get(this, fd);
d2787b
-            if (!ob_fd)
d2787b
-                goto unlock;
d2787b
+    bool closed;
d2787b
 
d2787b
-            frame = ob_fd->open_frame;
d2787b
-            ob_fd->open_frame = NULL;
d2787b
-        }
d2787b
-    unlock:
d2787b
-        UNLOCK(&fd->lock);
d2787b
-    } else {
d2787b
-        LOCK(&fd->lock);
d2787b
-        {
d2787b
-            frame = ob_fd->open_frame;
d2787b
-            ob_fd->open_frame = NULL;
d2787b
+    LOCK(&ob_inode->inode->lock);
d2787b
+    {
d2787b
+        closed = ob_inode->first_fd != fd;
d2787b
+        if (!closed) {
d2787b
+            if (ob_inode->triggered) {
d2787b
+                ob_inode->first_open = NULL;
d2787b
+            } else {
d2787b
+                ob_inode->first_open = stub;
d2787b
+                stub = NULL;
d2787b
+            }
d2787b
         }
d2787b
-        UNLOCK(&fd->lock);
d2787b
     }
d2787b
+    UNLOCK(&ob_inode->inode->lock);
d2787b
 
d2787b
-    if (frame) {
d2787b
-        /* We don't need to take a reference here. We already have a reference
d2787b
-         * while the open is pending. */
d2787b
-        frame->local = fd;
d2787b
-
d2787b
-        STACK_WIND(frame, ob_wake_cbk, FIRST_CHILD(this),
d2787b
-                   FIRST_CHILD(this)->fops->open, &ob_fd->loc, ob_fd->flags, fd,
d2787b
-                   ob_fd->xdata);
d2787b
+    if (stub != NULL) {
d2787b
+        if (closed) {
d2787b
+            call_stub_destroy(stub);
d2787b
+            fd_unref(fd);
d2787b
+        } else {
d2787b
+            call_resume(stub);
d2787b
+        }
d2787b
     }
d2787b
 
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
-void
d2787b
-ob_inode_wake(xlator_t *this, struct list_head *ob_fds)
d2787b
+static void
d2787b
+ob_resume_pending(struct list_head *list)
d2787b
 {
d2787b
-    ob_fd_t *ob_fd = NULL, *tmp = NULL;
d2787b
+    call_stub_t *stub;
d2787b
 
d2787b
-    if (!list_empty(ob_fds)) {
d2787b
-        list_for_each_entry_safe(ob_fd, tmp, ob_fds, ob_fds_on_inode)
d2787b
-        {
d2787b
-            ob_fd_wake(this, ob_fd->fd, ob_fd);
d2787b
-            ob_fd_free(ob_fd);
d2787b
-        }
d2787b
-    }
d2787b
-}
d2787b
+    while (!list_empty(list)) {
d2787b
+        stub = list_first_entry(list, call_stub_t, list);
d2787b
+        list_del_init(&stub->list);
d2787b
 
d2787b
-/* called holding inode->lock and fd->lock */
d2787b
-void
d2787b
-ob_fd_copy(ob_fd_t *src, ob_fd_t *dst)
d2787b
-{
d2787b
-    if (!src || !dst)
d2787b
-        goto out;
d2787b
-
d2787b
-    dst->fd = src->fd;
d2787b
-    dst->loc.inode = inode_ref(src->loc.inode);
d2787b
-    gf_uuid_copy(dst->loc.gfid, src->loc.gfid);
d2787b
-    dst->flags = src->flags;
d2787b
-    dst->xdata = dict_ref(src->xdata);
d2787b
-    dst->ob_inode = src->ob_inode;
d2787b
-out:
d2787b
-    return;
d2787b
+        call_resume(stub);
d2787b
+    }
d2787b
 }
d2787b
 
d2787b
-int
d2787b
-open_all_pending_fds_and_resume(xlator_t *this, inode_t *inode,
d2787b
-                                call_stub_t *stub)
d2787b
+static void
d2787b
+ob_open_completed(xlator_t *xl, ob_inode_t *ob_inode, fd_t *fd, int32_t op_ret,
d2787b
+                  int32_t op_errno)
d2787b
 {
d2787b
-    ob_inode_t *ob_inode = NULL;
d2787b
-    ob_fd_t *ob_fd = NULL, *tmp = NULL;
d2787b
-    gf_boolean_t was_open_in_progress = _gf_false;
d2787b
-    gf_boolean_t wait_for_open = _gf_false;
d2787b
-    struct list_head ob_fds;
d2787b
+    struct list_head list;
d2787b
 
d2787b
-    ob_inode = ob_inode_get(this, inode);
d2787b
-    if (ob_inode == NULL)
d2787b
-        goto out;
d2787b
+    INIT_LIST_HEAD(&list);
d2787b
 
d2787b
-    INIT_LIST_HEAD(&ob_fds);
d2787b
+    if (op_ret < 0) {
d2787b
+        fd_ctx_set(fd, xl, op_errno <= 0 ? EIO : op_errno);
d2787b
+    }
d2787b
 
d2787b
-    LOCK(&inode->lock);
d2787b
+    LOCK(&ob_inode->inode->lock);
d2787b
     {
d2787b
-        was_open_in_progress = ob_inode->open_in_progress;
d2787b
-        ob_inode->unlinked = 1;
d2787b
-
d2787b
-        if (was_open_in_progress) {
d2787b
-            list_add_tail(&stub->list, &ob_inode->resume_fops);
d2787b
-            goto inode_unlock;
d2787b
-        }
d2787b
-
d2787b
-        list_for_each_entry(ob_fd, &ob_inode->ob_fds, ob_fds_on_inode)
d2787b
-        {
d2787b
-            LOCK(&ob_fd->fd->lock);
d2787b
-            {
d2787b
-                if (ob_fd->opened)
d2787b
-                    goto fd_unlock;
d2787b
-
d2787b
-                ob_inode->count++;
d2787b
-                ob_fd->ob_inode_fops_waiting = _gf_true;
d2787b
-
d2787b
-                if (ob_fd->open_frame == NULL) {
d2787b
-                    /* open in progress no need of wake */
d2787b
-                } else {
d2787b
-                    tmp = ob_fd_new();
d2787b
-                    tmp->open_frame = ob_fd->open_frame;
d2787b
-                    ob_fd->open_frame = NULL;
d2787b
-
d2787b
-                    ob_fd_copy(ob_fd, tmp);
d2787b
-                    list_add_tail(&tmp->ob_fds_on_inode, &ob_fds);
d2787b
-                }
d2787b
-            }
d2787b
-        fd_unlock:
d2787b
-            UNLOCK(&ob_fd->fd->lock);
d2787b
-        }
d2787b
-
d2787b
-        if (ob_inode->count) {
d2787b
-            wait_for_open = ob_inode->open_in_progress = _gf_true;
d2787b
-            list_add_tail(&stub->list, &ob_inode->resume_fops);
d2787b
+        /* Only update the fields if the file has not been closed before
d2787b
+         * getting here. */
d2787b
+        if (ob_inode->first_fd == fd) {
d2787b
+            list_splice_init(&ob_inode->resume_fops, &list);
d2787b
+            ob_inode->first_fd = NULL;
d2787b
+            ob_inode->first_open = NULL;
d2787b
+            ob_inode->triggered = false;
d2787b
         }
d2787b
     }
d2787b
-inode_unlock:
d2787b
-    UNLOCK(&inode->lock);
d2787b
+    UNLOCK(&ob_inode->inode->lock);
d2787b
 
d2787b
-out:
d2787b
-    if (!was_open_in_progress) {
d2787b
-        if (!wait_for_open) {
d2787b
-            call_resume(stub);
d2787b
-        } else {
d2787b
-            ob_inode_wake(this, &ob_fds);
d2787b
-        }
d2787b
-    }
d2787b
+    ob_resume_pending(&list);
d2787b
 
d2787b
-    return 0;
d2787b
+    fd_unref(fd);
d2787b
 }
d2787b
 
d2787b
-int
d2787b
-open_and_resume(xlator_t *this, fd_t *fd, call_stub_t *stub)
d2787b
+static int32_t
d2787b
+ob_open_cbk(call_frame_t *frame, void *cookie, xlator_t *xl, int32_t op_ret,
d2787b
+            int32_t op_errno, fd_t *fd, dict_t *xdata)
d2787b
 {
d2787b
-    ob_fd_t *ob_fd = NULL;
d2787b
-    int op_errno = 0;
d2787b
-
d2787b
-    if (!fd)
d2787b
-        goto nofd;
d2787b
-
d2787b
-    LOCK(&fd->lock);
d2787b
-    {
d2787b
-        ob_fd = __ob_fd_ctx_get(this, fd);
d2787b
-        if (!ob_fd)
d2787b
-            goto unlock;
d2787b
+    ob_inode_t *ob_inode;
d2787b
 
d2787b
-        if (ob_fd->op_errno) {
d2787b
-            op_errno = ob_fd->op_errno;
d2787b
-            goto unlock;
d2787b
-        }
d2787b
+    ob_inode = frame->local;
d2787b
+    frame->local = NULL;
d2787b
 
d2787b
-        list_add_tail(&stub->list, &ob_fd->list);
d2787b
-    }
d2787b
-unlock:
d2787b
-    UNLOCK(&fd->lock);
d2787b
+    ob_open_completed(xl, ob_inode, cookie, op_ret, op_errno);
d2787b
 
d2787b
-nofd:
d2787b
-    if (op_errno)
d2787b
-        call_unwind_error(stub, -1, op_errno);
d2787b
-    else if (ob_fd)
d2787b
-        ob_fd_wake(this, fd, NULL);
d2787b
-    else
d2787b
-        call_resume(stub);
d2787b
+    STACK_DESTROY(frame->root);
d2787b
 
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
-int
d2787b
-ob_open_behind(call_frame_t *frame, xlator_t *this, loc_t *loc, int flags,
d2787b
+static int32_t
d2787b
+ob_open_resume(call_frame_t *frame, xlator_t *this, loc_t *loc, int flags,
d2787b
                fd_t *fd, dict_t *xdata)
d2787b
 {
d2787b
-    ob_fd_t *ob_fd = NULL;
d2787b
-    int ret = -1;
d2787b
-    ob_conf_t *conf = NULL;
d2787b
-    ob_inode_t *ob_inode = NULL;
d2787b
-    gf_boolean_t open_in_progress = _gf_false;
d2787b
-    int unlinked = 0;
d2787b
-
d2787b
-    conf = this->private;
d2787b
-
d2787b
-    if (flags & O_TRUNC) {
d2787b
-        STACK_WIND(frame, default_open_cbk, FIRST_CHILD(this),
d2787b
-                   FIRST_CHILD(this)->fops->open, loc, flags, fd, xdata);
d2787b
-        return 0;
d2787b
-    }
d2787b
-
d2787b
-    ob_inode = ob_inode_get(this, fd->inode);
d2787b
-
d2787b
-    ob_fd = ob_fd_new();
d2787b
-    if (!ob_fd)
d2787b
-        goto enomem;
d2787b
-
d2787b
-    ob_fd->ob_inode = ob_inode;
d2787b
-
d2787b
-    ob_fd->fd = fd;
d2787b
-
d2787b
-    ob_fd->open_frame = copy_frame(frame);
d2787b
-    if (!ob_fd->open_frame)
d2787b
-        goto enomem;
d2787b
-    ret = loc_copy(&ob_fd->loc, loc);
d2787b
-    if (ret)
d2787b
-        goto enomem;
d2787b
-
d2787b
-    ob_fd->flags = flags;
d2787b
-    if (xdata)
d2787b
-        ob_fd->xdata = dict_ref(xdata);
d2787b
-
d2787b
-    LOCK(&fd->inode->lock);
d2787b
-    {
d2787b
-        open_in_progress = ob_inode->open_in_progress;
d2787b
-        unlinked = ob_inode->unlinked;
d2787b
-        if (!open_in_progress && !unlinked) {
d2787b
-            ret = ob_fd_ctx_set(this, fd, ob_fd);
d2787b
-            if (ret) {
d2787b
-                UNLOCK(&fd->inode->lock);
d2787b
-                goto enomem;
d2787b
-            }
d2787b
-
d2787b
-            list_add(&ob_fd->ob_fds_on_inode, &ob_inode->ob_fds);
d2787b
-        }
d2787b
-    }
d2787b
-    UNLOCK(&fd->inode->lock);
d2787b
-
d2787b
-    /* We take a reference while the background open is pending or being
d2787b
-     * processed. If we finally wind the request in the foreground, then
d2787b
-     * ob_fd_free() will take care of this additional reference. */
d2787b
-    fd_ref(fd);
d2787b
-
d2787b
-    if (!open_in_progress && !unlinked) {
d2787b
-        STACK_UNWIND_STRICT(open, frame, 0, 0, fd, xdata);
d2787b
-
d2787b
-        if (!conf->lazy_open)
d2787b
-            ob_fd_wake(this, fd, NULL);
d2787b
-    } else {
d2787b
-        ob_fd_free(ob_fd);
d2787b
-        STACK_WIND(frame, default_open_cbk, FIRST_CHILD(this),
d2787b
-                   FIRST_CHILD(this)->fops->open, loc, flags, fd, xdata);
d2787b
-    }
d2787b
+    STACK_WIND_COOKIE(frame, ob_open_cbk, fd, FIRST_CHILD(this),
d2787b
+                      FIRST_CHILD(this)->fops->open, loc, flags, fd, xdata);
d2787b
 
d2787b
     return 0;
d2787b
-enomem:
d2787b
-    if (ob_fd) {
d2787b
-        if (ob_fd->open_frame)
d2787b
-            STACK_DESTROY(ob_fd->open_frame->root);
d2787b
-
d2787b
-        loc_wipe(&ob_fd->loc);
d2787b
-        if (ob_fd->xdata)
d2787b
-            dict_unref(ob_fd->xdata);
d2787b
-
d2787b
-        GF_FREE(ob_fd);
d2787b
-    }
d2787b
-
d2787b
-    return -1;
d2787b
 }
d2787b
 
d2787b
-int
d2787b
+static int32_t
d2787b
 ob_open(call_frame_t *frame, xlator_t *this, loc_t *loc, int flags, fd_t *fd,
d2787b
         dict_t *xdata)
d2787b
 {
d2787b
-    fd_t *old_fd = NULL;
d2787b
-    int ret = -1;
d2787b
-    int op_errno = ENOMEM;
d2787b
-    call_stub_t *stub = NULL;
d2787b
-
d2787b
-    old_fd = fd_lookup(fd->inode, 0);
d2787b
-    if (old_fd) {
d2787b
-        /* open-behind only when this is the first FD */
d2787b
-        stub = fop_open_stub(frame, default_open_resume, loc, flags, fd, xdata);
d2787b
-        if (!stub) {
d2787b
-            fd_unref(old_fd);
d2787b
-            goto err;
d2787b
-        }
d2787b
-
d2787b
-        open_and_resume(this, old_fd, stub);
d2787b
+    ob_inode_t *ob_inode;
d2787b
+    call_frame_t *open_frame;
d2787b
+    call_stub_t *stub;
d2787b
+    fd_t *first_fd;
d2787b
+    ob_state_t state;
d2787b
+
d2787b
+    state = ob_open_behind(this, fd, flags, &ob_inode, &first_fd);
d2787b
+    if (state == OB_STATE_READY) {
d2787b
+        /* There's no pending open, but there are other file descriptors opened
d2787b
+         * or the current flags require a synchronous open. */
d2787b
+        return default_open(frame, this, loc, flags, fd, xdata);
d2787b
+    }
d2787b
 
d2787b
-        fd_unref(old_fd);
d2787b
+    if (state == OB_STATE_OPEN_TRIGGERED) {
d2787b
+        /* The first open is in progress (either because it was already issued
d2787b
+         * or because this request triggered it). We try to create a new stub
d2787b
+         * to retry the operation once the initial open completes. */
d2787b
+        stub = fop_open_stub(frame, ob_open, loc, flags, fd, xdata);
d2787b
+        if (stub != NULL) {
d2787b
+            return ob_stub_dispatch(this, ob_inode, first_fd, stub);
d2787b
+        }
d2787b
 
d2787b
-        return 0;
d2787b
+        state = -ENOMEM;
d2787b
     }
d2787b
 
d2787b
-    ret = ob_open_behind(frame, this, loc, flags, fd, xdata);
d2787b
-    if (ret) {
d2787b
-        goto err;
d2787b
-    }
d2787b
+    if (state == OB_STATE_FIRST_OPEN) {
d2787b
+        /* We try to create a stub for the new open. A new frame needs to be
d2787b
+         * used because the current one may be destroyed soon after sending
d2787b
+         * the open's reply. */
d2787b
+        open_frame = copy_frame(frame);
d2787b
+        if (open_frame != NULL) {
d2787b
+            stub = fop_open_stub(open_frame, ob_open_resume, loc, flags, fd,
d2787b
+                                 xdata);
d2787b
+            if (stub != NULL) {
d2787b
+                open_frame->local = ob_inode;
d2787b
 
d2787b
-    return 0;
d2787b
-err:
d2787b
-    gf_msg(this->name, GF_LOG_ERROR, op_errno, OPEN_BEHIND_MSG_NO_MEMORY, "%s",
d2787b
-           loc->path);
d2787b
+                /* TODO: Previous version passed xdata back to the caller, but
d2787b
+                 *       probably this doesn't make sense since it won't contain
d2787b
+                 *       any requested data. I think it would be better to pass
d2787b
+                 *       NULL for xdata. */
d2787b
+                default_open_cbk(frame, NULL, this, 0, 0, fd, xdata);
d2787b
 
d2787b
-    STACK_UNWIND_STRICT(open, frame, -1, op_errno, 0, 0);
d2787b
+                return ob_open_dispatch(this, ob_inode, first_fd, stub);
d2787b
+            }
d2787b
 
d2787b
-    return 0;
d2787b
-}
d2787b
+            STACK_DESTROY(open_frame->root);
d2787b
+        }
d2787b
 
d2787b
-fd_t *
d2787b
-ob_get_wind_fd(xlator_t *this, fd_t *fd, uint32_t *flag)
d2787b
-{
d2787b
-    fd_t *wind_fd = NULL;
d2787b
-    ob_fd_t *ob_fd = NULL;
d2787b
-    ob_conf_t *conf = NULL;
d2787b
+        /* In case of error, simulate a regular completion but with an error
d2787b
+         * code. */
d2787b
+        ob_open_completed(this, ob_inode, first_fd, -1, ENOMEM);
d2787b
 
d2787b
-    conf = this->private;
d2787b
+        state = -ENOMEM;
d2787b
+    }
d2787b
 
d2787b
-    ob_fd = ob_fd_ctx_get(this, fd);
d2787b
+    /* In case of failure we need to decrement the number of open files because
d2787b
+     * ob_fdclose() won't be called. */
d2787b
 
d2787b
-    if (ob_fd && ob_fd->open_frame && conf->use_anonymous_fd) {
d2787b
-        wind_fd = fd_anonymous(fd->inode);
d2787b
-        if ((ob_fd->flags & O_DIRECT) && (flag))
d2787b
-            *flag = *flag | O_DIRECT;
d2787b
-    } else {
d2787b
-        wind_fd = fd_ref(fd);
d2787b
+    LOCK(&fd->inode->lock);
d2787b
+    {
d2787b
+        ob_inode->open_count--;
d2787b
     }
d2787b
+    UNLOCK(&fd->inode->lock);
d2787b
 
d2787b
-    return wind_fd;
d2787b
+    gf_smsg(this->name, GF_LOG_ERROR, -state, OPEN_BEHIND_MSG_FAILED, "fop=%s",
d2787b
+            "open", "path=%s", loc->path, NULL);
d2787b
+
d2787b
+    return default_open_failure_cbk(frame, -state);
d2787b
 }
d2787b
 
d2787b
-int
d2787b
+static int32_t
d2787b
 ob_readv(call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size,
d2787b
          off_t offset, uint32_t flags, dict_t *xdata)
d2787b
 {
d2787b
-    call_stub_t *stub = NULL;
d2787b
-    fd_t *wind_fd = NULL;
d2787b
-    ob_conf_t *conf = NULL;
d2787b
+    ob_conf_t *conf = this->private;
d2787b
+    bool trigger = conf->read_after_open || !conf->use_anonymous_fd;
d2787b
 
d2787b
-    conf = this->private;
d2787b
-
d2787b
-    if (!conf->read_after_open)
d2787b
-        wind_fd = ob_get_wind_fd(this, fd, &flags);
d2787b
-    else
d2787b
-        wind_fd = fd_ref(fd);
d2787b
-
d2787b
-    stub = fop_readv_stub(frame, default_readv_resume, wind_fd, size, offset,
d2787b
-                          flags, xdata);
d2787b
-    fd_unref(wind_fd);
d2787b
-
d2787b
-    if (!stub)
d2787b
-        goto err;
d2787b
-
d2787b
-    open_and_resume(this, wind_fd, stub);
d2787b
-
d2787b
-    return 0;
d2787b
-err:
d2787b
-    STACK_UNWIND_STRICT(readv, frame, -1, ENOMEM, 0, 0, 0, 0, 0);
d2787b
+    OB_POST_FD(readv, this, frame, fd, trigger, fd, size, offset, flags, xdata);
d2787b
 
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
-int
d2787b
+static int32_t
d2787b
 ob_writev(call_frame_t *frame, xlator_t *this, fd_t *fd, struct iovec *iov,
d2787b
           int count, off_t offset, uint32_t flags, struct iobref *iobref,
d2787b
           dict_t *xdata)
d2787b
 {
d2787b
-    call_stub_t *stub = NULL;
d2787b
-
d2787b
-    stub = fop_writev_stub(frame, default_writev_resume, fd, iov, count, offset,
d2787b
-                           flags, iobref, xdata);
d2787b
-    if (!stub)
d2787b
-        goto err;
d2787b
-
d2787b
-    open_and_resume(this, fd, stub);
d2787b
-
d2787b
-    return 0;
d2787b
-err:
d2787b
-    STACK_UNWIND_STRICT(writev, frame, -1, ENOMEM, 0, 0, 0);
d2787b
+    OB_POST_FD(writev, this, frame, fd, true, fd, iov, count, offset, flags,
d2787b
+               iobref, xdata);
d2787b
 
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
-int
d2787b
+static int32_t
d2787b
 ob_fstat(call_frame_t *frame, xlator_t *this, fd_t *fd, dict_t *xdata)
d2787b
 {
d2787b
-    call_stub_t *stub = NULL;
d2787b
-    fd_t *wind_fd = NULL;
d2787b
-
d2787b
-    wind_fd = ob_get_wind_fd(this, fd, NULL);
d2787b
-
d2787b
-    stub = fop_fstat_stub(frame, default_fstat_resume, wind_fd, xdata);
d2787b
+    ob_conf_t *conf = this->private;
d2787b
+    bool trigger = !conf->use_anonymous_fd;
d2787b
 
d2787b
-    fd_unref(wind_fd);
d2787b
-
d2787b
-    if (!stub)
d2787b
-        goto err;
d2787b
-
d2787b
-    open_and_resume(this, wind_fd, stub);
d2787b
-
d2787b
-    return 0;
d2787b
-err:
d2787b
-    STACK_UNWIND_STRICT(fstat, frame, -1, ENOMEM, 0, 0);
d2787b
+    OB_POST_FD(fstat, this, frame, fd, trigger, fd, xdata);
d2787b
 
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
-int
d2787b
+static int32_t
d2787b
 ob_seek(call_frame_t *frame, xlator_t *this, fd_t *fd, off_t offset,
d2787b
         gf_seek_what_t what, dict_t *xdata)
d2787b
 {
d2787b
-    call_stub_t *stub = NULL;
d2787b
-    fd_t *wind_fd = NULL;
d2787b
-
d2787b
-    wind_fd = ob_get_wind_fd(this, fd, NULL);
d2787b
+    ob_conf_t *conf = this->private;
d2787b
+    bool trigger = !conf->use_anonymous_fd;
d2787b
 
d2787b
-    stub = fop_seek_stub(frame, default_seek_resume, wind_fd, offset, what,
d2787b
-                         xdata);
d2787b
-
d2787b
-    fd_unref(wind_fd);
d2787b
-
d2787b
-    if (!stub)
d2787b
-        goto err;
d2787b
-
d2787b
-    open_and_resume(this, wind_fd, stub);
d2787b
-
d2787b
-    return 0;
d2787b
-err:
d2787b
-    STACK_UNWIND_STRICT(fstat, frame, -1, ENOMEM, 0, 0);
d2787b
+    OB_POST_FD(seek, this, frame, fd, trigger, fd, offset, what, xdata);
d2787b
 
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
-int
d2787b
+static int32_t
d2787b
 ob_flush(call_frame_t *frame, xlator_t *this, fd_t *fd, dict_t *xdata)
d2787b
 {
d2787b
-    call_stub_t *stub = NULL;
d2787b
-    ob_fd_t *ob_fd = NULL;
d2787b
-    gf_boolean_t unwind = _gf_false;
d2787b
-
d2787b
-    LOCK(&fd->lock);
d2787b
-    {
d2787b
-        ob_fd = __ob_fd_ctx_get(this, fd);
d2787b
-        if (ob_fd && ob_fd->open_frame)
d2787b
-            /* if open() was never wound to backend,
d2787b
-               no need to wind flush() either.
d2787b
-            */
d2787b
-            unwind = _gf_true;
d2787b
-    }
d2787b
-    UNLOCK(&fd->lock);
d2787b
-
d2787b
-    if (unwind)
d2787b
-        goto unwind;
d2787b
-
d2787b
-    stub = fop_flush_stub(frame, default_flush_resume, fd, xdata);
d2787b
-    if (!stub)
d2787b
-        goto err;
d2787b
-
d2787b
-    open_and_resume(this, fd, stub);
d2787b
-
d2787b
-    return 0;
d2787b
-err:
d2787b
-    STACK_UNWIND_STRICT(flush, frame, -1, ENOMEM, 0);
d2787b
-
d2787b
-    return 0;
d2787b
-
d2787b
-unwind:
d2787b
-    STACK_UNWIND_STRICT(flush, frame, 0, 0, 0);
d2787b
+    OB_POST_FLUSH(this, frame, fd, fd, xdata);
d2787b
 
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
-int
d2787b
+static int32_t
d2787b
 ob_fsync(call_frame_t *frame, xlator_t *this, fd_t *fd, int flag, dict_t *xdata)
d2787b
 {
d2787b
-    call_stub_t *stub = NULL;
d2787b
-
d2787b
-    stub = fop_fsync_stub(frame, default_fsync_resume, fd, flag, xdata);
d2787b
-    if (!stub)
d2787b
-        goto err;
d2787b
-
d2787b
-    open_and_resume(this, fd, stub);
d2787b
-
d2787b
-    return 0;
d2787b
-err:
d2787b
-    STACK_UNWIND_STRICT(fsync, frame, -1, ENOMEM, 0, 0, 0);
d2787b
+    OB_POST_FD(fsync, this, frame, fd, true, fd, flag, xdata);
d2787b
 
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
-int
d2787b
+static int32_t
d2787b
 ob_lk(call_frame_t *frame, xlator_t *this, fd_t *fd, int cmd,
d2787b
       struct gf_flock *flock, dict_t *xdata)
d2787b
 {
d2787b
-    call_stub_t *stub = NULL;
d2787b
-
d2787b
-    stub = fop_lk_stub(frame, default_lk_resume, fd, cmd, flock, xdata);
d2787b
-    if (!stub)
d2787b
-        goto err;
d2787b
-
d2787b
-    open_and_resume(this, fd, stub);
d2787b
-
d2787b
-    return 0;
d2787b
-err:
d2787b
-    STACK_UNWIND_STRICT(lk, frame, -1, ENOMEM, 0, 0);
d2787b
+    OB_POST_FD(lk, this, frame, fd, true, fd, cmd, flock, xdata);
d2787b
 
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
-int
d2787b
+static int32_t
d2787b
 ob_ftruncate(call_frame_t *frame, xlator_t *this, fd_t *fd, off_t offset,
d2787b
              dict_t *xdata)
d2787b
 {
d2787b
-    call_stub_t *stub = NULL;
d2787b
-
d2787b
-    stub = fop_ftruncate_stub(frame, default_ftruncate_resume, fd, offset,
d2787b
-                              xdata);
d2787b
-    if (!stub)
d2787b
-        goto err;
d2787b
-
d2787b
-    open_and_resume(this, fd, stub);
d2787b
-
d2787b
-    return 0;
d2787b
-err:
d2787b
-    STACK_UNWIND_STRICT(ftruncate, frame, -1, ENOMEM, 0, 0, 0);
d2787b
+    OB_POST_FD(ftruncate, this, frame, fd, true, fd, offset, xdata);
d2787b
 
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
-int
d2787b
+static int32_t
d2787b
 ob_fsetxattr(call_frame_t *frame, xlator_t *this, fd_t *fd, dict_t *xattr,
d2787b
              int flags, dict_t *xdata)
d2787b
 {
d2787b
-    call_stub_t *stub = NULL;
d2787b
-
d2787b
-    stub = fop_fsetxattr_stub(frame, default_fsetxattr_resume, fd, xattr, flags,
d2787b
-                              xdata);
d2787b
-    if (!stub)
d2787b
-        goto err;
d2787b
-
d2787b
-    open_and_resume(this, fd, stub);
d2787b
-
d2787b
-    return 0;
d2787b
-err:
d2787b
-    STACK_UNWIND_STRICT(fsetxattr, frame, -1, ENOMEM, 0);
d2787b
+    OB_POST_FD(fsetxattr, this, frame, fd, true, fd, xattr, flags, xdata);
d2787b
 
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
-int
d2787b
+static int32_t
d2787b
 ob_fgetxattr(call_frame_t *frame, xlator_t *this, fd_t *fd, const char *name,
d2787b
              dict_t *xdata)
d2787b
 {
d2787b
-    call_stub_t *stub = NULL;
d2787b
-
d2787b
-    stub = fop_fgetxattr_stub(frame, default_fgetxattr_resume, fd, name, xdata);
d2787b
-    if (!stub)
d2787b
-        goto err;
d2787b
-
d2787b
-    open_and_resume(this, fd, stub);
d2787b
-
d2787b
-    return 0;
d2787b
-err:
d2787b
-    STACK_UNWIND_STRICT(fgetxattr, frame, -1, ENOMEM, 0, 0);
d2787b
+    OB_POST_FD(fgetxattr, this, frame, fd, true, fd, name, xdata);
d2787b
 
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
-int
d2787b
+static int32_t
d2787b
 ob_fremovexattr(call_frame_t *frame, xlator_t *this, fd_t *fd, const char *name,
d2787b
                 dict_t *xdata)
d2787b
 {
d2787b
-    call_stub_t *stub = NULL;
d2787b
-
d2787b
-    stub = fop_fremovexattr_stub(frame, default_fremovexattr_resume, fd, name,
d2787b
-                                 xdata);
d2787b
-    if (!stub)
d2787b
-        goto err;
d2787b
-
d2787b
-    open_and_resume(this, fd, stub);
d2787b
-
d2787b
-    return 0;
d2787b
-err:
d2787b
-    STACK_UNWIND_STRICT(fremovexattr, frame, -1, ENOMEM, 0);
d2787b
+    OB_POST_FD(fremovexattr, this, frame, fd, true, fd, name, xdata);
d2787b
 
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
-int
d2787b
+static int32_t
d2787b
 ob_finodelk(call_frame_t *frame, xlator_t *this, const char *volume, fd_t *fd,
d2787b
             int cmd, struct gf_flock *flock, dict_t *xdata)
d2787b
 {
d2787b
-    call_stub_t *stub = fop_finodelk_stub(frame, default_finodelk_resume,
d2787b
-                                          volume, fd, cmd, flock, xdata);
d2787b
-    if (stub)
d2787b
-        open_and_resume(this, fd, stub);
d2787b
-    else
d2787b
-        STACK_UNWIND_STRICT(finodelk, frame, -1, ENOMEM, 0);
d2787b
+    OB_POST_FD(finodelk, this, frame, fd, true, volume, fd, cmd, flock, xdata);
d2787b
 
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
-int
d2787b
+static int32_t
d2787b
 ob_fentrylk(call_frame_t *frame, xlator_t *this, const char *volume, fd_t *fd,
d2787b
             const char *basename, entrylk_cmd cmd, entrylk_type type,
d2787b
             dict_t *xdata)
d2787b
 {
d2787b
-    call_stub_t *stub = fop_fentrylk_stub(
d2787b
-        frame, default_fentrylk_resume, volume, fd, basename, cmd, type, xdata);
d2787b
-    if (stub)
d2787b
-        open_and_resume(this, fd, stub);
d2787b
-    else
d2787b
-        STACK_UNWIND_STRICT(fentrylk, frame, -1, ENOMEM, 0);
d2787b
+    OB_POST_FD(fentrylk, this, frame, fd, true, volume, fd, basename, cmd, type,
d2787b
+               xdata);
d2787b
 
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
-int
d2787b
+static int32_t
d2787b
 ob_fxattrop(call_frame_t *frame, xlator_t *this, fd_t *fd,
d2787b
             gf_xattrop_flags_t optype, dict_t *xattr, dict_t *xdata)
d2787b
 {
d2787b
-    call_stub_t *stub = fop_fxattrop_stub(frame, default_fxattrop_resume, fd,
d2787b
-                                          optype, xattr, xdata);
d2787b
-    if (stub)
d2787b
-        open_and_resume(this, fd, stub);
d2787b
-    else
d2787b
-        STACK_UNWIND_STRICT(fxattrop, frame, -1, ENOMEM, 0, 0);
d2787b
+    OB_POST_FD(fxattrop, this, frame, fd, true, fd, optype, xattr, xdata);
d2787b
 
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
-int
d2787b
+static int32_t
d2787b
 ob_fsetattr(call_frame_t *frame, xlator_t *this, fd_t *fd, struct iatt *iatt,
d2787b
             int valid, dict_t *xdata)
d2787b
 {
d2787b
-    call_stub_t *stub = NULL;
d2787b
-
d2787b
-    stub = fop_fsetattr_stub(frame, default_fsetattr_resume, fd, iatt, valid,
d2787b
-                             xdata);
d2787b
-    if (!stub)
d2787b
-        goto err;
d2787b
-
d2787b
-    open_and_resume(this, fd, stub);
d2787b
-
d2787b
-    return 0;
d2787b
-err:
d2787b
-    STACK_UNWIND_STRICT(fsetattr, frame, -1, ENOMEM, 0, 0, 0);
d2787b
+    OB_POST_FD(fsetattr, this, frame, fd, true, fd, iatt, valid, xdata);
d2787b
 
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
-int
d2787b
+static int32_t
d2787b
 ob_fallocate(call_frame_t *frame, xlator_t *this, fd_t *fd, int32_t mode,
d2787b
              off_t offset, size_t len, dict_t *xdata)
d2787b
 {
d2787b
-    call_stub_t *stub;
d2787b
-
d2787b
-    stub = fop_fallocate_stub(frame, default_fallocate_resume, fd, mode, offset,
d2787b
-                              len, xdata);
d2787b
-    if (!stub)
d2787b
-        goto err;
d2787b
-
d2787b
-    open_and_resume(this, fd, stub);
d2787b
+    OB_POST_FD(fallocate, this, frame, fd, true, fd, mode, offset, len, xdata);
d2787b
 
d2787b
     return 0;
d2787b
-err:
d2787b
-    STACK_UNWIND_STRICT(fallocate, frame, -1, ENOMEM, NULL, NULL, NULL);
d2787b
-    return 0;
d2787b
 }
d2787b
 
d2787b
-int
d2787b
+static int32_t
d2787b
 ob_discard(call_frame_t *frame, xlator_t *this, fd_t *fd, off_t offset,
d2787b
            size_t len, dict_t *xdata)
d2787b
 {
d2787b
-    call_stub_t *stub;
d2787b
-
d2787b
-    stub = fop_discard_stub(frame, default_discard_resume, fd, offset, len,
d2787b
-                            xdata);
d2787b
-    if (!stub)
d2787b
-        goto err;
d2787b
-
d2787b
-    open_and_resume(this, fd, stub);
d2787b
+    OB_POST_FD(discard, this, frame, fd, true, fd, offset, len, xdata);
d2787b
 
d2787b
     return 0;
d2787b
-err:
d2787b
-    STACK_UNWIND_STRICT(discard, frame, -1, ENOMEM, NULL, NULL, NULL);
d2787b
-    return 0;
d2787b
 }
d2787b
 
d2787b
-int
d2787b
+static int32_t
d2787b
 ob_zerofill(call_frame_t *frame, xlator_t *this, fd_t *fd, off_t offset,
d2787b
             off_t len, dict_t *xdata)
d2787b
 {
d2787b
-    call_stub_t *stub;
d2787b
-
d2787b
-    stub = fop_zerofill_stub(frame, default_zerofill_resume, fd, offset, len,
d2787b
-                             xdata);
d2787b
-    if (!stub)
d2787b
-        goto err;
d2787b
+    OB_POST_FD(zerofill, this, frame, fd, true, fd, offset, len, xdata);
d2787b
 
d2787b
-    open_and_resume(this, fd, stub);
d2787b
-
d2787b
-    return 0;
d2787b
-err:
d2787b
-    STACK_UNWIND_STRICT(zerofill, frame, -1, ENOMEM, NULL, NULL, NULL);
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
-int
d2787b
+static int32_t
d2787b
 ob_unlink(call_frame_t *frame, xlator_t *this, loc_t *loc, int xflags,
d2787b
           dict_t *xdata)
d2787b
 {
d2787b
-    call_stub_t *stub = NULL;
d2787b
-
d2787b
-    stub = fop_unlink_stub(frame, default_unlink_resume, loc, xflags, xdata);
d2787b
-    if (!stub)
d2787b
-        goto err;
d2787b
-
d2787b
-    open_all_pending_fds_and_resume(this, loc->inode, stub);
d2787b
-
d2787b
-    return 0;
d2787b
-err:
d2787b
-    STACK_UNWIND_STRICT(unlink, frame, -1, ENOMEM, 0, 0, 0);
d2787b
+    OB_POST_INODE(unlink, this, frame, loc->inode, true, loc, xflags, xdata);
d2787b
 
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
-int
d2787b
+static int32_t
d2787b
 ob_rename(call_frame_t *frame, xlator_t *this, loc_t *src, loc_t *dst,
d2787b
           dict_t *xdata)
d2787b
 {
d2787b
-    call_stub_t *stub = NULL;
d2787b
-
d2787b
-    stub = fop_rename_stub(frame, default_rename_resume, src, dst, xdata);
d2787b
-    if (!stub)
d2787b
-        goto err;
d2787b
-
d2787b
-    open_all_pending_fds_and_resume(this, dst->inode, stub);
d2787b
-
d2787b
-    return 0;
d2787b
-err:
d2787b
-    STACK_UNWIND_STRICT(rename, frame, -1, ENOMEM, 0, 0, 0, 0, 0, 0);
d2787b
+    OB_POST_INODE(rename, this, frame, dst->inode, true, src, dst, xdata);
d2787b
 
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
-int32_t
d2787b
+static int32_t
d2787b
 ob_setattr(call_frame_t *frame, xlator_t *this, loc_t *loc, struct iatt *stbuf,
d2787b
            int32_t valid, dict_t *xdata)
d2787b
 {
d2787b
-    call_stub_t *stub = NULL;
d2787b
-
d2787b
-    stub = fop_setattr_stub(frame, default_setattr_resume, loc, stbuf, valid,
d2787b
-                            xdata);
d2787b
-    if (!stub)
d2787b
-        goto err;
d2787b
+    OB_POST_INODE(setattr, this, frame, loc->inode, true, loc, stbuf, valid,
d2787b
+                  xdata);
d2787b
 
d2787b
-    open_all_pending_fds_and_resume(this, loc->inode, stub);
d2787b
-
d2787b
-    return 0;
d2787b
-err:
d2787b
-    STACK_UNWIND_STRICT(setattr, frame, -1, ENOMEM, NULL, NULL, NULL);
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
-int32_t
d2787b
+static int32_t
d2787b
 ob_setxattr(call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *dict,
d2787b
             int32_t flags, dict_t *xdata)
d2787b
 {
d2787b
-    call_stub_t *stub = NULL;
d2787b
-    gf_boolean_t access_xattr = _gf_false;
d2787b
-
d2787b
     if (dict_get(dict, POSIX_ACL_DEFAULT_XATTR) ||
d2787b
         dict_get(dict, POSIX_ACL_ACCESS_XATTR) ||
d2787b
-        dict_get(dict, GF_SELINUX_XATTR_KEY))
d2787b
-        access_xattr = _gf_true;
d2787b
-
d2787b
-    if (!access_xattr)
d2787b
+        dict_get(dict, GF_SELINUX_XATTR_KEY)) {
d2787b
         return default_setxattr(frame, this, loc, dict, flags, xdata);
d2787b
+    }
d2787b
 
d2787b
-    stub = fop_setxattr_stub(frame, default_setxattr_resume, loc, dict, flags,
d2787b
-                             xdata);
d2787b
-    if (!stub)
d2787b
-        goto err;
d2787b
-
d2787b
-    open_all_pending_fds_and_resume(this, loc->inode, stub);
d2787b
+    OB_POST_INODE(setxattr, this, frame, loc->inode, true, loc, dict, flags,
d2787b
+                  xdata);
d2787b
 
d2787b
     return 0;
d2787b
-err:
d2787b
-    STACK_UNWIND_STRICT(setxattr, frame, -1, ENOMEM, NULL);
d2787b
-    return 0;
d2787b
 }
d2787b
 
d2787b
-int
d2787b
-ob_release(xlator_t *this, fd_t *fd)
d2787b
+static void
d2787b
+ob_fdclose(xlator_t *this, fd_t *fd)
d2787b
 {
d2787b
-    ob_fd_t *ob_fd = NULL;
d2787b
+    struct list_head list;
d2787b
+    ob_inode_t *ob_inode;
d2787b
+    call_stub_t *stub;
d2787b
+
d2787b
+    INIT_LIST_HEAD(&list);
d2787b
+    stub = NULL;
d2787b
 
d2787b
-    ob_fd = ob_fd_ctx_get(this, fd);
d2787b
+    LOCK(&fd->inode->lock);
d2787b
+    {
d2787b
+        ob_inode = ob_inode_get_locked(this, fd->inode);
d2787b
+        if (ob_inode != NULL) {
d2787b
+            ob_inode->open_count--;
d2787b
+
d2787b
+            /* If this fd is the same as ob_inode->first_fd, it means that
d2787b
+             * the initial open has not fully completed. We'll try to cancel
d2787b
+             * it. */
d2787b
+            if (ob_inode->first_fd == fd) {
d2787b
+                if (ob_inode->first_open == OB_OPEN_PREPARING) {
d2787b
+                    /* In this case ob_open_dispatch() has not been called yet.
d2787b
+                     * We clear first_fd and first_open to allow that function
d2787b
+                     * to know that the open is not really needed. This also
d2787b
+                     * allows other requests to work as expected if they
d2787b
+                     * arrive before the dispatch function is called. If there
d2787b
+                     * are pending fops, we can directly process them here.
d2787b
+                     * (note that there shouldn't be any fd related fops, but
d2787b
+                     * if there are, it's fine if they fail). */
d2787b
+                    ob_inode->first_fd = NULL;
d2787b
+                    ob_inode->first_open = NULL;
d2787b
+                    ob_inode->triggered = false;
d2787b
+                    list_splice_init(&ob_inode->resume_fops, &list);
d2787b
+                } else if (!ob_inode->triggered) {
d2787b
+                    /* If the open has already been dispatched, we can only
d2787b
+                     * cancel it if it has not been triggered. Otherwise we
d2787b
+                     * simply wait until it completes. While it's not triggered,
d2787b
+                     * first_open must be a valid stub and there can't be any
d2787b
+                     * pending fops. */
d2787b
+                    GF_ASSERT((ob_inode->first_open != NULL) &&
d2787b
+                              list_empty(&ob_inode->resume_fops));
d2787b
+
d2787b
+                    ob_inode->first_fd = NULL;
d2787b
+                    stub = ob_inode->first_open;
d2787b
+                    ob_inode->first_open = NULL;
d2787b
+                }
d2787b
+            }
d2787b
+        }
d2787b
+    }
d2787b
+    UNLOCK(&fd->inode->lock);
d2787b
 
d2787b
-    ob_fd_free(ob_fd);
d2787b
+    if (stub != NULL) {
d2787b
+        call_stub_destroy(stub);
d2787b
+        fd_unref(fd);
d2787b
+    }
d2787b
 
d2787b
-    return 0;
d2787b
+    ob_resume_pending(&list);
d2787b
 }
d2787b
 
d2787b
 int
d2787b
 ob_forget(xlator_t *this, inode_t *inode)
d2787b
 {
d2787b
-    ob_inode_t *ob_inode = NULL;
d2787b
+    ob_inode_t *ob_inode;
d2787b
     uint64_t value = 0;
d2787b
 
d2787b
-    inode_ctx_del(inode, this, &value);
d2787b
-
d2787b
-    if (value) {
d2787b
+    if ((inode_ctx_del(inode, this, &value) == 0) && (value != 0)) {
d2787b
         ob_inode = (ob_inode_t *)(uintptr_t)value;
d2787b
-        ob_inode_free(ob_inode);
d2787b
+        GF_FREE(ob_inode);
d2787b
     }
d2787b
 
d2787b
     return 0;
d2787b
@@ -1153,20 +823,18 @@ ob_priv_dump(xlator_t *this)
d2787b
 int
d2787b
 ob_fdctx_dump(xlator_t *this, fd_t *fd)
d2787b
 {
d2787b
-    ob_fd_t *ob_fd = NULL;
d2787b
     char key_prefix[GF_DUMP_MAX_BUF_LEN] = {
d2787b
         0,
d2787b
     };
d2787b
-    int ret = 0;
d2787b
+    uint64_t value = 0;
d2787b
+    int ret = 0, error = 0;
d2787b
 
d2787b
     ret = TRY_LOCK(&fd->lock);
d2787b
     if (ret)
d2787b
         return 0;
d2787b
 
d2787b
-    ob_fd = __ob_fd_ctx_get(this, fd);
d2787b
-    if (!ob_fd) {
d2787b
-        UNLOCK(&fd->lock);
d2787b
-        return 0;
d2787b
+    if ((__fd_ctx_get(fd, this, &value) == 0) && (value != 0)) {
d2787b
+        error = (int32_t)value;
d2787b
     }
d2787b
 
d2787b
     gf_proc_dump_build_key(key_prefix, "xlator.performance.open-behind",
d2787b
@@ -1175,17 +843,7 @@ ob_fdctx_dump(xlator_t *this, fd_t *fd)
d2787b
 
d2787b
     gf_proc_dump_write("fd", "%p", fd);
d2787b
 
d2787b
-    gf_proc_dump_write("open_frame", "%p", ob_fd->open_frame);
d2787b
-
d2787b
-    if (ob_fd->open_frame)
d2787b
-        gf_proc_dump_write("open_frame.root.unique", "%" PRIu64,
d2787b
-                           ob_fd->open_frame->root->unique);
d2787b
-
d2787b
-    gf_proc_dump_write("loc.path", "%s", ob_fd->loc.path);
d2787b
-
d2787b
-    gf_proc_dump_write("loc.ino", "%s", uuid_utoa(ob_fd->loc.gfid));
d2787b
-
d2787b
-    gf_proc_dump_write("flags", "%d", ob_fd->flags);
d2787b
+    gf_proc_dump_write("error", "%d", error);
d2787b
 
d2787b
     UNLOCK(&fd->lock);
d2787b
 
d2787b
@@ -1307,7 +965,7 @@ struct xlator_fops fops = {
d2787b
 };
d2787b
 
d2787b
 struct xlator_cbks cbks = {
d2787b
-    .release = ob_release,
d2787b
+    .fdclose = ob_fdclose,
d2787b
     .forget = ob_forget,
d2787b
 };
d2787b
 
d2787b
-- 
d2787b
1.8.3.1
d2787b