74096c
From dc03340654d921916ac3890d713fc84ef4bb1e28 Mon Sep 17 00:00:00 2001
74096c
From: Mohit Agrawal <moagrawal@redhat.com>
74096c
Date: Sat, 29 Sep 2018 13:15:35 +0530
74096c
Subject: [PATCH 445/449] feature/changelog: Avoid thread creation if xlator is
74096c
 not enabled
74096c
74096c
Problem:
74096c
Changelog creates threads even if the changelog is not enabled
74096c
74096c
Background:
74096c
Changelog xlator broadly does two things
74096c
  1. Journalling - Cosumers are geo-rep and glusterfind
74096c
  2. Event Notification for registered events like (open, release etc) -
74096c
     Consumers are bitrot, geo-rep
74096c
74096c
The existing option "changelog.changelog" controls journalling and
74096c
there is no option to control event notification and is enabled by
74096c
default. So when bitrot/geo-rep is not enabled on the volume, threads
74096c
and resources(rpc and rbuf) related to event notifications consumes
74096c
resources and cpu cycle which is unnecessary.
74096c
74096c
Solution:
74096c
The solution is to have two different options as below.
74096c
 1. changelog-notification : Event notifications
74096c
 2. changelog : Journalling
74096c
74096c
This patch introduces the option "changelog-notification" which is
74096c
not exposed to user. When either bitrot or changelog (journalling)
74096c
is enabled, it internally enbales 'changelog-notification'. But
74096c
once the 'changelog-notification' is enabled, it will not be disabled
74096c
for the life time of the brick process even after bitrot and changelog
74096c
is disabled. As of now, rpc resource cleanup has lot of races and is
74096c
difficult to cleanup cleanly. If allowed, it leads to memory leaks
74096c
and crashes on enable/disable of bitrot or changelog (journal) in a
74096c
loop. Hence to be safer, the event notification is not disabled within
74096c
lifetime of process once enabled.
74096c
74096c
> Change-Id: Ifd00286e0966049e8eb9f21567fe407cf11bb02a
74096c
> Updates: #475
74096c
> Signed-off-by: Mohit Agrawal <moagrawal@redhat.com>
74096c
> (Cherry pick from commit 6de80bcd6366778ac34ce58ec496fa08cc02bd0b)
74096c
> (Reviewed on upstream link https://review.gluster.org/#/c/glusterfs/+/21896/)
74096c
74096c
BUG: 1790336
74096c
Change-Id: Ifd00286e0966049e8eb9f21567fe407cf11bb02a
74096c
Signed-off-by: Mohit Agrawal <moagrawal@redhat.com>
74096c
Reviewed-on: https://code.engineering.redhat.com/gerrit/202778
74096c
Tested-by: Mohit Agrawal <moagrawa@redhat.com>
74096c
Tested-by: RHGS Build Bot <nigelb@redhat.com>
74096c
Reviewed-by: Sunil Kumar Heggodu Gopala Acharya <sheggodu@redhat.com>
74096c
---
74096c
 rpc/rpc-lib/src/rpcsvc.c                           |  26 ++--
74096c
 tests/basic/changelog/changelog-history.t          |  12 +-
74096c
 tests/bugs/bitrot/bug-1227996.t                    |   1 -
74096c
 tests/bugs/bitrot/bug-1245981.t                    |   4 +-
74096c
 xlators/features/changelog/src/changelog-helpers.h |   4 +
74096c
 .../features/changelog/src/changelog-rpc-common.c  |   3 +
74096c
 xlators/features/changelog/src/changelog.c         | 149 +++++++++++++++------
74096c
 xlators/mgmt/glusterd/src/glusterd-volgen.c        |  13 ++
74096c
 8 files changed, 154 insertions(+), 58 deletions(-)
74096c
74096c
diff --git a/rpc/rpc-lib/src/rpcsvc.c b/rpc/rpc-lib/src/rpcsvc.c
74096c
index b058932..3f184bf 100644
74096c
--- a/rpc/rpc-lib/src/rpcsvc.c
74096c
+++ b/rpc/rpc-lib/src/rpcsvc.c
74096c
@@ -1865,6 +1865,18 @@ rpcsvc_program_unregister(rpcsvc_t *svc, rpcsvc_program_t *program)
74096c
         goto out;
74096c
     }
74096c
 
74096c
+    pthread_rwlock_rdlock(&svc->rpclock);
74096c
+    {
74096c
+        list_for_each_entry(prog, &svc->programs, program)
74096c
+        {
74096c
+            if ((prog->prognum == program->prognum) &&
74096c
+                (prog->progver == program->progver)) {
74096c
+                break;
74096c
+            }
74096c
+        }
74096c
+    }
74096c
+    pthread_rwlock_unlock(&svc->rpclock);
74096c
+
74096c
     ret = rpcsvc_program_unregister_portmap(program);
74096c
     if (ret == -1) {
74096c
         gf_log(GF_RPCSVC, GF_LOG_ERROR,
74096c
@@ -1881,17 +1893,6 @@ rpcsvc_program_unregister(rpcsvc_t *svc, rpcsvc_program_t *program)
74096c
         goto out;
74096c
     }
74096c
 #endif
74096c
-    pthread_rwlock_rdlock(&svc->rpclock);
74096c
-    {
74096c
-        list_for_each_entry(prog, &svc->programs, program)
74096c
-        {
74096c
-            if ((prog->prognum == program->prognum) &&
74096c
-                (prog->progver == program->progver)) {
74096c
-                break;
74096c
-            }
74096c
-        }
74096c
-    }
74096c
-    pthread_rwlock_unlock(&svc->rpclock);
74096c
 
74096c
     gf_log(GF_RPCSVC, GF_LOG_DEBUG,
74096c
            "Program unregistered: %s, Num: %d,"
74096c
@@ -1912,6 +1913,9 @@ rpcsvc_program_unregister(rpcsvc_t *svc, rpcsvc_program_t *program)
74096c
 
74096c
     ret = 0;
74096c
 out:
74096c
+    if (prog)
74096c
+        GF_FREE(prog);
74096c
+
74096c
     if (ret == -1) {
74096c
         if (program) {
74096c
             gf_log(GF_RPCSVC, GF_LOG_ERROR,
74096c
diff --git a/tests/basic/changelog/changelog-history.t b/tests/basic/changelog/changelog-history.t
74096c
index 3ce4098..b56e247 100644
74096c
--- a/tests/basic/changelog/changelog-history.t
74096c
+++ b/tests/basic/changelog/changelog-history.t
74096c
@@ -5,6 +5,7 @@
74096c
 
74096c
 cleanup;
74096c
 
74096c
+SCRIPT_TIMEOUT=300
74096c
 HISTORY_BIN_PATH=$(dirname $0)/../../utils/changelog
74096c
 build_tester $HISTORY_BIN_PATH/get-history.c -lgfchangelog
74096c
 
74096c
@@ -68,18 +69,21 @@ TEST $CLI volume set $V0 changelog.changelog off
74096c
 sleep 3
74096c
 time_after_disable=$(date '+%s')
74096c
 
74096c
+TEST $CLI volume set $V0 changelog.changelog on
74096c
+sleep 5
74096c
+
74096c
 #Passes, gives the changelogs till continuous changelogs are available
74096c
 # but returns 1
74096c
-EXPECT "1" $HISTORY_BIN_PATH/get-history $time_after_enable1 $time_in_sec_htime2
74096c
+EXPECT_WITHIN 10 "1" $HISTORY_BIN_PATH/get-history $time_after_enable1 $time_in_sec_htime2
74096c
 
74096c
 #Fails as start falls between htime files
74096c
-EXPECT "-3" $HISTORY_BIN_PATH/get-history $time_between_htime $time_in_sec_htime1
74096c
+EXPECT_WITHIN 10 "-3" $HISTORY_BIN_PATH/get-history $time_between_htime $time_in_sec_htime1
74096c
 
74096c
 #Passes as start and end falls in same htime file
74096c
-EXPECT "0" $HISTORY_BIN_PATH/get-history $time_in_sec_htime1 $time_in_sec_htime2
74096c
+EXPECT_WITHIN 10 "0" $HISTORY_BIN_PATH/get-history $time_in_sec_htime1 $time_in_sec_htime2
74096c
 
74096c
 #Passes, gives the changelogs till continuous changelogs are available
74096c
-EXPECT "0" $HISTORY_BIN_PATH/get-history $time_in_sec_htime2 $time_after_disable
74096c
+EXPECT_WITHIN 10 "0" $HISTORY_BIN_PATH/get-history $time_in_sec_htime2 $time_after_disable
74096c
 
74096c
 TEST rm $HISTORY_BIN_PATH/get-history
74096c
 
74096c
diff --git a/tests/bugs/bitrot/bug-1227996.t b/tests/bugs/bitrot/bug-1227996.t
74096c
index 47ebc42..121c7b5 100644
74096c
--- a/tests/bugs/bitrot/bug-1227996.t
74096c
+++ b/tests/bugs/bitrot/bug-1227996.t
74096c
@@ -17,7 +17,6 @@ TEST pidof glusterd;
74096c
 ## Lets create and start the volume
74096c
 TEST $CLI volume create $V0 $H0:$B0/${V0}0 $H0:$B0/${V0}1
74096c
 TEST $CLI volume start $V0
74096c
-
74096c
 ## Enable bitrot on volume $V0
74096c
 TEST $CLI volume bitrot $V0 enable
74096c
 
74096c
diff --git a/tests/bugs/bitrot/bug-1245981.t b/tests/bugs/bitrot/bug-1245981.t
74096c
index 2bed4d9..f395525 100644
74096c
--- a/tests/bugs/bitrot/bug-1245981.t
74096c
+++ b/tests/bugs/bitrot/bug-1245981.t
74096c
@@ -47,9 +47,9 @@ touch $M0/5
74096c
 sleep `expr $SLEEP_TIME \* 2`
74096c
 
74096c
 backpath=$(get_backend_paths $fname)
74096c
-TEST getfattr -m . -n trusted.bit-rot.signature $backpath
74096c
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT 'trusted.bit-rot.signature' check_for_xattr 'trusted.bit-rot.signature' $backpath
74096c
 
74096c
 backpath=$(get_backend_paths $M0/new_file)
74096c
-TEST getfattr -m . -n trusted.bit-rot.signature $backpath
74096c
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT 'trusted.bit-rot.signature' check_for_xattr 'trusted.bit-rot.signature' $backpath
74096c
 
74096c
 cleanup;
74096c
diff --git a/xlators/features/changelog/src/changelog-helpers.h b/xlators/features/changelog/src/changelog-helpers.h
74096c
index 517c4dc..3afacc9 100644
74096c
--- a/xlators/features/changelog/src/changelog-helpers.h
74096c
+++ b/xlators/features/changelog/src/changelog-helpers.h
74096c
@@ -190,8 +190,12 @@ typedef struct changelog_ev_selector {
74096c
 
74096c
 /* changelog's private structure */
74096c
 struct changelog_priv {
74096c
+    /* changelog journalling */
74096c
     gf_boolean_t active;
74096c
 
74096c
+    /* changelog live notifications */
74096c
+    gf_boolean_t rpc_active;
74096c
+
74096c
     /* to generate unique socket file per brick */
74096c
     char *changelog_brick;
74096c
 
74096c
diff --git a/xlators/features/changelog/src/changelog-rpc-common.c b/xlators/features/changelog/src/changelog-rpc-common.c
74096c
index dcdcfb1..f2d1853 100644
74096c
--- a/xlators/features/changelog/src/changelog-rpc-common.c
74096c
+++ b/xlators/features/changelog/src/changelog-rpc-common.c
74096c
@@ -263,6 +263,9 @@ changelog_rpc_server_destroy(xlator_t *this, rpcsvc_t *rpc, char *sockfile,
74096c
     struct rpcsvc_program *prog = NULL;
74096c
     rpc_transport_t *trans = NULL;
74096c
 
74096c
+    if (!rpc)
74096c
+        return;
74096c
+
74096c
     while (*progs) {
74096c
         prog = *progs;
74096c
         (void)rpcsvc_program_unregister(rpc, prog);
74096c
diff --git a/xlators/features/changelog/src/changelog.c b/xlators/features/changelog/src/changelog.c
74096c
index d9025f3..ff06c09 100644
74096c
--- a/xlators/features/changelog/src/changelog.c
74096c
+++ b/xlators/features/changelog/src/changelog.c
74096c
@@ -34,6 +34,12 @@ static struct changelog_bootstrap cb_bootstrap[] = {
74096c
     },
74096c
 };
74096c
 
74096c
+static int
74096c
+changelog_init_rpc(xlator_t *this, changelog_priv_t *priv);
74096c
+
74096c
+static int
74096c
+changelog_init(xlator_t *this, changelog_priv_t *priv);
74096c
+
74096c
 /* Entry operations - TYPE III */
74096c
 
74096c
 /**
74096c
@@ -2008,6 +2014,11 @@ notify(xlator_t *this, int event, void *data, ...)
74096c
     uint64_t clntcnt = 0;
74096c
     changelog_clnt_t *conn = NULL;
74096c
     gf_boolean_t cleanup_notify = _gf_false;
74096c
+    char sockfile[UNIX_PATH_MAX] = {
74096c
+        0,
74096c
+    };
74096c
+    rpcsvc_listener_t *listener = NULL;
74096c
+    rpcsvc_listener_t *next = NULL;
74096c
 
74096c
     INIT_LIST_HEAD(&queue);
74096c
 
74096c
@@ -2021,23 +2032,40 @@ notify(xlator_t *this, int event, void *data, ...)
74096c
                "cleanup changelog rpc connection of brick %s",
74096c
                priv->victim->name);
74096c
 
74096c
-        this->cleanup_starting = 1;
74096c
-        changelog_destroy_rpc_listner(this, priv);
74096c
-        conn = &priv->connections;
74096c
-        if (conn)
74096c
-            changelog_ev_cleanup_connections(this, conn);
74096c
-        xprtcnt = GF_ATOMIC_GET(priv->xprtcnt);
74096c
-        clntcnt = GF_ATOMIC_GET(priv->clntcnt);
74096c
-
74096c
-        if (!xprtcnt && !clntcnt) {
74096c
-            LOCK(&priv->lock);
74096c
-            {
74096c
-                cleanup_notify = priv->notify_down;
74096c
-                priv->notify_down = _gf_true;
74096c
+        if (priv->rpc_active) {
74096c
+            this->cleanup_starting = 1;
74096c
+            changelog_destroy_rpc_listner(this, priv);
74096c
+            conn = &priv->connections;
74096c
+            if (conn)
74096c
+                changelog_ev_cleanup_connections(this, conn);
74096c
+            xprtcnt = GF_ATOMIC_GET(priv->xprtcnt);
74096c
+            clntcnt = GF_ATOMIC_GET(priv->clntcnt);
74096c
+            if (!xprtcnt && !clntcnt) {
74096c
+                LOCK(&priv->lock);
74096c
+                {
74096c
+                    cleanup_notify = priv->notify_down;
74096c
+                    priv->notify_down = _gf_true;
74096c
+                }
74096c
+                UNLOCK(&priv->lock);
74096c
+                list_for_each_entry_safe(listener, next, &priv->rpc->listeners,
74096c
+                                         list)
74096c
+                {
74096c
+                    if (listener->trans) {
74096c
+                        rpc_transport_unref(listener->trans);
74096c
+                    }
74096c
+                }
74096c
+                CHANGELOG_MAKE_SOCKET_PATH(priv->changelog_brick, sockfile,
74096c
+                                           UNIX_PATH_MAX);
74096c
+                sys_unlink(sockfile);
74096c
+                if (priv->rpc) {
74096c
+                    rpcsvc_destroy(priv->rpc);
74096c
+                    priv->rpc = NULL;
74096c
+                }
74096c
+                if (!cleanup_notify)
74096c
+                    default_notify(this, GF_EVENT_PARENT_DOWN, data);
74096c
             }
74096c
-            UNLOCK(&priv->lock);
74096c
-            if (!cleanup_notify)
74096c
-                default_notify(this, GF_EVENT_PARENT_DOWN, data);
74096c
+        } else {
74096c
+            default_notify(this, GF_EVENT_PARENT_DOWN, data);
74096c
         }
74096c
         goto out;
74096c
     }
74096c
@@ -2425,6 +2453,22 @@ changelog_barrier_pthread_destroy(changelog_priv_t *priv)
74096c
     LOCK_DESTROY(&priv->bflags.lock);
74096c
 }
74096c
 
74096c
+static void
74096c
+changelog_cleanup_rpc(xlator_t *this, changelog_priv_t *priv)
74096c
+{
74096c
+    /* terminate rpc server */
74096c
+    if (!this->cleanup_starting)
74096c
+        changelog_destroy_rpc_listner(this, priv);
74096c
+
74096c
+    (void)changelog_cleanup_rpc_threads(this, priv);
74096c
+    /* cleanup rot buffs */
74096c
+    rbuf_dtor(priv->rbuf);
74096c
+
74096c
+    /* cleanup poller thread */
74096c
+    if (priv->poller)
74096c
+        (void)changelog_thread_cleanup(this, priv->poller);
74096c
+}
74096c
+
74096c
 int
74096c
 reconfigure(xlator_t *this, dict_t *options)
74096c
 {
74096c
@@ -2433,6 +2477,9 @@ reconfigure(xlator_t *this, dict_t *options)
74096c
     changelog_priv_t *priv = NULL;
74096c
     gf_boolean_t active_earlier = _gf_true;
74096c
     gf_boolean_t active_now = _gf_true;
74096c
+    gf_boolean_t rpc_active_earlier = _gf_true;
74096c
+    gf_boolean_t rpc_active_now = _gf_true;
74096c
+    gf_boolean_t iniate_rpc = _gf_false;
74096c
     changelog_time_slice_t *slice = NULL;
74096c
     changelog_log_data_t cld = {
74096c
         0,
74096c
@@ -2454,6 +2501,7 @@ reconfigure(xlator_t *this, dict_t *options)
74096c
 
74096c
     ret = -1;
74096c
     active_earlier = priv->active;
74096c
+    rpc_active_earlier = priv->rpc_active;
74096c
 
74096c
     /* first stop the rollover and the fsync thread */
74096c
     changelog_cleanup_helper_threads(this, priv);
74096c
@@ -2487,6 +2535,29 @@ reconfigure(xlator_t *this, dict_t *options)
74096c
         goto out;
74096c
 
74096c
     GF_OPTION_RECONF("changelog", active_now, options, bool, out);
74096c
+    GF_OPTION_RECONF("changelog-notification", rpc_active_now, options, bool,
74096c
+                     out);
74096c
+
74096c
+    /* If journalling is enabled, enable rpc notifications */
74096c
+    if (active_now && !active_earlier) {
74096c
+        if (!rpc_active_earlier)
74096c
+            iniate_rpc = _gf_true;
74096c
+    }
74096c
+
74096c
+    if (rpc_active_now && !rpc_active_earlier) {
74096c
+        iniate_rpc = _gf_true;
74096c
+    }
74096c
+
74096c
+    /* TODO: Disable of changelog-notifications is not supported for now
74096c
+     * as there is no clean way of cleaning up of rpc resources
74096c
+     */
74096c
+
74096c
+    if (iniate_rpc) {
74096c
+        ret = changelog_init_rpc(this, priv);
74096c
+        if (ret)
74096c
+            goto out;
74096c
+        priv->rpc_active = _gf_true;
74096c
+    }
74096c
 
74096c
     /**
74096c
      * changelog_handle_change() handles changes that could possibly
74096c
@@ -2618,6 +2689,7 @@ changelog_init_options(xlator_t *this, changelog_priv_t *priv)
74096c
         goto dealloc_2;
74096c
 
74096c
     GF_OPTION_INIT("changelog", priv->active, bool, dealloc_2);
74096c
+    GF_OPTION_INIT("changelog-notification", priv->rpc_active, bool, dealloc_2);
74096c
     GF_OPTION_INIT("capture-del-path", priv->capture_del_path, bool, dealloc_2);
74096c
 
74096c
     GF_OPTION_INIT("op-mode", tmp, str, dealloc_2);
74096c
@@ -2656,22 +2728,6 @@ error_return:
74096c
     return -1;
74096c
 }
74096c
 
74096c
-static void
74096c
-changelog_cleanup_rpc(xlator_t *this, changelog_priv_t *priv)
74096c
-{
74096c
-    /* terminate rpc server */
74096c
-    if (!this->cleanup_starting)
74096c
-        changelog_destroy_rpc_listner(this, priv);
74096c
-
74096c
-    (void)changelog_cleanup_rpc_threads(this, priv);
74096c
-    /* cleanup rot buffs */
74096c
-    rbuf_dtor(priv->rbuf);
74096c
-
74096c
-    /* cleanup poller thread */
74096c
-    if (priv->poller)
74096c
-        (void)changelog_thread_cleanup(this, priv->poller);
74096c
-}
74096c
-
74096c
 static int
74096c
 changelog_init_rpc(xlator_t *this, changelog_priv_t *priv)
74096c
 {
74096c
@@ -2768,10 +2824,13 @@ init(xlator_t *this)
74096c
     INIT_LIST_HEAD(&priv->queue);
74096c
     priv->barrier_enabled = _gf_false;
74096c
 
74096c
-    /* RPC ball rolling.. */
74096c
-    ret = changelog_init_rpc(this, priv);
74096c
-    if (ret)
74096c
-        goto cleanup_barrier;
74096c
+    if (priv->rpc_active || priv->active) {
74096c
+        /* RPC ball rolling.. */
74096c
+        ret = changelog_init_rpc(this, priv);
74096c
+        if (ret)
74096c
+            goto cleanup_barrier;
74096c
+        priv->rpc_active = _gf_true;
74096c
+    }
74096c
 
74096c
     ret = changelog_init(this, priv);
74096c
     if (ret)
74096c
@@ -2783,7 +2842,9 @@ init(xlator_t *this)
74096c
     return 0;
74096c
 
74096c
 cleanup_rpc:
74096c
-    changelog_cleanup_rpc(this, priv);
74096c
+    if (priv->rpc_active) {
74096c
+        changelog_cleanup_rpc(this, priv);
74096c
+    }
74096c
 cleanup_barrier:
74096c
     changelog_barrier_pthread_destroy(priv);
74096c
 cleanup_options:
74096c
@@ -2808,9 +2869,10 @@ fini(xlator_t *this)
74096c
     priv = this->private;
74096c
 
74096c
     if (priv) {
74096c
-        /* terminate RPC server/threads */
74096c
-        changelog_cleanup_rpc(this, priv);
74096c
-
74096c
+        if (priv->active || priv->rpc_active) {
74096c
+            /* terminate RPC server/threads */
74096c
+            changelog_cleanup_rpc(this, priv);
74096c
+        }
74096c
         /* call barrier_disable to cancel timer */
74096c
         if (priv->barrier_enabled)
74096c
             __chlog_barrier_disable(this, &queue);
74096c
@@ -2879,6 +2941,13 @@ struct volume_options options[] = {
74096c
      .flags = OPT_FLAG_SETTABLE,
74096c
      .level = OPT_STATUS_BASIC,
74096c
      .tags = {"journal", "georep", "glusterfind"}},
74096c
+    {.key = {"changelog-notification"},
74096c
+     .type = GF_OPTION_TYPE_BOOL,
74096c
+     .default_value = "off",
74096c
+     .description = "enable/disable changelog live notification",
74096c
+     .op_version = {3},
74096c
+     .level = OPT_STATUS_BASIC,
74096c
+     .tags = {"bitrot", "georep"}},
74096c
     {.key = {"changelog-brick"},
74096c
      .type = GF_OPTION_TYPE_PATH,
74096c
      .description = "brick path to generate unique socket file name."
74096c
diff --git a/xlators/mgmt/glusterd/src/glusterd-volgen.c b/xlators/mgmt/glusterd/src/glusterd-volgen.c
74096c
index 16346e7..13f84ea 100644
74096c
--- a/xlators/mgmt/glusterd/src/glusterd-volgen.c
74096c
+++ b/xlators/mgmt/glusterd/src/glusterd-volgen.c
74096c
@@ -1876,6 +1876,19 @@ brick_graph_add_changelog(volgen_graph_t *graph, glusterd_volinfo_t *volinfo,
74096c
     ret = xlator_set_fixed_option(xl, "changelog-dir", changelog_basepath);
74096c
     if (ret)
74096c
         goto out;
74096c
+
74096c
+    ret = glusterd_is_bitrot_enabled(volinfo);
74096c
+    if (ret == -1) {
74096c
+        goto out;
74096c
+    } else if (ret) {
74096c
+        ret = xlator_set_fixed_option(xl, "changelog-notification", "on");
74096c
+        if (ret)
74096c
+            goto out;
74096c
+    } else {
74096c
+        ret = xlator_set_fixed_option(xl, "changelog-notification", "off");
74096c
+        if (ret)
74096c
+            goto out;
74096c
+    }
74096c
 out:
74096c
     return ret;
74096c
 }
74096c
-- 
74096c
1.8.3.1
74096c