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