14f8ab
From ddb0038de77a4269fa7eed1bb217bfb6bed1b7ba Mon Sep 17 00:00:00 2001
14f8ab
From: N Balachandran <nbalacha@redhat.com>
14f8ab
Date: Fri, 9 Aug 2019 14:34:22 +0530
14f8ab
Subject: [PATCH 337/344] fuse: Set limit on invalidate queue size
14f8ab
14f8ab
If the glusterfs fuse client process is unable to
14f8ab
process the invalidate requests quickly enough, the
14f8ab
number of such requests quickly grows large enough
14f8ab
to use a significant amount of memory.
14f8ab
We are now introducing another option to set an upper
14f8ab
limit on these to prevent runaway memory usage.
14f8ab
14f8ab
> Upstream https://review.gluster.org/23187
14f8ab
> Change-Id: Iddfff1ee2de1466223e6717f7abd4b28ed947788
14f8ab
> Fixes: bz#1732717
14f8ab
> Signed-off-by: N Balachandran <nbalacha@redhat.com>
14f8ab
14f8ab
BUG: 1763208
14f8ab
Change-Id: I666cdf6c70999a0f0bc79969e8df0a9dde93b6e4
14f8ab
Signed-off-by: Csaba Henk <csaba@redhat.com>
14f8ab
Reviewed-on: https://code.engineering.redhat.com/gerrit/187529
14f8ab
Tested-by: RHGS Build Bot <nigelb@redhat.com>
14f8ab
Reviewed-by: Sunil Kumar Heggodu Gopala Acharya <sheggodu@redhat.com>
14f8ab
---
14f8ab
 doc/mount.glusterfs.8                       |  5 +++
14f8ab
 glusterfsd/src/glusterfsd.c                 | 21 ++++++++++
14f8ab
 glusterfsd/src/glusterfsd.h                 |  3 +-
14f8ab
 libglusterfs/src/glusterfs/glusterfs.h      |  1 +
14f8ab
 libglusterfs/src/glusterfs/inode.h          |  1 +
14f8ab
 libglusterfs/src/inode.c                    | 31 +++++++++++----
14f8ab
 xlators/mount/fuse/src/fuse-bridge.c        | 60 ++++++++++++++++++++++-------
14f8ab
 xlators/mount/fuse/src/fuse-bridge.h        |  3 +-
14f8ab
 xlators/mount/fuse/utils/mount.glusterfs.in |  7 ++++
14f8ab
 9 files changed, 108 insertions(+), 24 deletions(-)
14f8ab
14f8ab
diff --git a/doc/mount.glusterfs.8 b/doc/mount.glusterfs.8
14f8ab
index 286631b..b35b362 100644
14f8ab
--- a/doc/mount.glusterfs.8
14f8ab
+++ b/doc/mount.glusterfs.8
14f8ab
@@ -126,6 +126,11 @@ Provide list of backup volfile servers in the following format [default: None]
14f8ab
 Set fuse module's limit for number of inodes kept in LRU list to N [default: 131072]
14f8ab
 .TP
14f8ab
 .TP
14f8ab
+\fBinvalidate-limit=\fRN
14f8ab
+Suspend fuse invalidations implied by 'lru-limit' if  number of outstanding
14f8ab
+invalidations reaches N
14f8ab
+.TP
14f8ab
+.TP
14f8ab
 \fBbackground-qlen=\fRN
14f8ab
 Set fuse module's background queue length to N [default: 64]
14f8ab
 .TP
14f8ab
diff --git a/glusterfsd/src/glusterfsd.c b/glusterfsd/src/glusterfsd.c
14f8ab
index 5b5e996..0856471 100644
14f8ab
--- a/glusterfsd/src/glusterfsd.c
14f8ab
+++ b/glusterfsd/src/glusterfsd.c
14f8ab
@@ -212,6 +212,9 @@ static struct argp_option gf_options[] = {
14f8ab
     {"lru-limit", ARGP_FUSE_LRU_LIMIT_KEY, "N", 0,
14f8ab
      "Set fuse module's limit for number of inodes kept in LRU list to N "
14f8ab
      "[default: 131072]"},
14f8ab
+    {"invalidate-limit", ARGP_FUSE_INVALIDATE_LIMIT_KEY, "N", 0,
14f8ab
+     "Suspend inode invalidations implied by 'lru-limit' if the number of "
14f8ab
+     "outstanding invalidations reaches N"},
14f8ab
     {"background-qlen", ARGP_FUSE_BACKGROUND_QLEN_KEY, "N", 0,
14f8ab
      "Set fuse module's background queue length to N "
14f8ab
      "[default: 64]"},
14f8ab
@@ -504,6 +507,16 @@ set_fuse_mount_options(glusterfs_ctx_t *ctx, dict_t *options)
14f8ab
         }
14f8ab
     }
14f8ab
 
14f8ab
+    if (cmd_args->invalidate_limit >= 0) {
14f8ab
+        ret = dict_set_int32(options, "invalidate-limit",
14f8ab
+                             cmd_args->invalidate_limit);
14f8ab
+        if (ret < 0) {
14f8ab
+            gf_msg("glusterfsd", GF_LOG_ERROR, 0, glusterfsd_msg_4,
14f8ab
+                   "invalidate-limit");
14f8ab
+            goto err;
14f8ab
+        }
14f8ab
+    }
14f8ab
+
14f8ab
     if (cmd_args->background_qlen) {
14f8ab
         ret = dict_set_int32(options, "background-qlen",
14f8ab
                              cmd_args->background_qlen);
14f8ab
@@ -1283,6 +1296,14 @@ parse_opts(int key, char *arg, struct argp_state *state)
14f8ab
             argp_failure(state, -1, 0, "unknown LRU limit option %s", arg);
14f8ab
             break;
14f8ab
 
14f8ab
+        case ARGP_FUSE_INVALIDATE_LIMIT_KEY:
14f8ab
+            if (!gf_string2int32(arg, &cmd_args->invalidate_limit))
14f8ab
+                break;
14f8ab
+
14f8ab
+            argp_failure(state, -1, 0, "unknown invalidate limit option %s",
14f8ab
+                         arg);
14f8ab
+            break;
14f8ab
+
14f8ab
         case ARGP_FUSE_BACKGROUND_QLEN_KEY:
14f8ab
             if (!gf_string2int(arg, &cmd_args->background_qlen))
14f8ab
                 break;
14f8ab
diff --git a/glusterfsd/src/glusterfsd.h b/glusterfsd/src/glusterfsd.h
14f8ab
index fa55789..ee655f0 100644
14f8ab
--- a/glusterfsd/src/glusterfsd.h
14f8ab
+++ b/glusterfsd/src/glusterfsd.h
14f8ab
@@ -111,7 +111,8 @@ enum argp_option_keys {
14f8ab
     ARGP_FUSE_FLUSH_HANDLE_INTERRUPT_KEY = 189,
14f8ab
     ARGP_FUSE_LRU_LIMIT_KEY = 190,
14f8ab
     ARGP_FUSE_AUTO_INVAL_KEY = 191,
14f8ab
-    ARGP_BRICK_MUX_KEY = 192
14f8ab
+    ARGP_BRICK_MUX_KEY = 192,
14f8ab
+    ARGP_FUSE_INVALIDATE_LIMIT_KEY = 195,
14f8ab
 };
14f8ab
 
14f8ab
 struct _gfd_vol_top_priv {
14f8ab
diff --git a/libglusterfs/src/glusterfs/glusterfs.h b/libglusterfs/src/glusterfs/glusterfs.h
14f8ab
index 79c93ae..3b594c0 100644
14f8ab
--- a/libglusterfs/src/glusterfs/glusterfs.h
14f8ab
+++ b/libglusterfs/src/glusterfs/glusterfs.h
14f8ab
@@ -541,6 +541,7 @@ struct _cmd_args {
14f8ab
     int client_pid_set;
14f8ab
     unsigned uid_map_root;
14f8ab
     int32_t lru_limit;
14f8ab
+    int32_t invalidate_limit;
14f8ab
     int background_qlen;
14f8ab
     int congestion_threshold;
14f8ab
     char *fuse_mountopts;
14f8ab
diff --git a/libglusterfs/src/glusterfs/inode.h b/libglusterfs/src/glusterfs/inode.h
14f8ab
index 52efdd8..4421c47 100644
14f8ab
--- a/libglusterfs/src/glusterfs/inode.h
14f8ab
+++ b/libglusterfs/src/glusterfs/inode.h
14f8ab
@@ -107,6 +107,7 @@ struct _inode {
14f8ab
     struct list_head list;        /* active/lru/purge */
14f8ab
 
14f8ab
     struct _inode_ctx *_ctx; /* replacement for dict_t *(inode->ctx) */
14f8ab
+    bool in_invalidate_list; /* Set if inode is in table invalidate list */
14f8ab
     bool invalidate_sent;    /* Set it if invalidator_fn is called for inode */
14f8ab
 };
14f8ab
 
14f8ab
diff --git a/libglusterfs/src/inode.c b/libglusterfs/src/inode.c
14f8ab
index 96ddea5..5331e93 100644
14f8ab
--- a/libglusterfs/src/inode.c
14f8ab
+++ b/libglusterfs/src/inode.c
14f8ab
@@ -558,8 +558,8 @@ __inode_unref(inode_t *inode, bool clear)
14f8ab
 
14f8ab
     this = THIS;
14f8ab
 
14f8ab
-    if (clear && inode->invalidate_sent) {
14f8ab
-        inode->invalidate_sent = false;
14f8ab
+    if (clear && inode->in_invalidate_list) {
14f8ab
+        inode->in_invalidate_list = false;
14f8ab
         inode->table->invalidate_size--;
14f8ab
         __inode_activate(inode);
14f8ab
     }
14f8ab
@@ -573,7 +573,7 @@ __inode_unref(inode_t *inode, bool clear)
14f8ab
         inode->_ctx[index].ref--;
14f8ab
     }
14f8ab
 
14f8ab
-    if (!inode->ref && !inode->invalidate_sent) {
14f8ab
+    if (!inode->ref && !inode->in_invalidate_list) {
14f8ab
         inode->table->active_size--;
14f8ab
 
14f8ab
         nlookup = GF_ATOMIC_GET(inode->nlookup);
14f8ab
@@ -609,14 +609,14 @@ __inode_ref(inode_t *inode, bool is_invalidate)
14f8ab
         return inode;
14f8ab
 
14f8ab
     if (!inode->ref) {
14f8ab
-        if (inode->invalidate_sent) {
14f8ab
-            inode->invalidate_sent = false;
14f8ab
+        if (inode->in_invalidate_list) {
14f8ab
+            inode->in_invalidate_list = false;
14f8ab
             inode->table->invalidate_size--;
14f8ab
         } else {
14f8ab
             inode->table->lru_size--;
14f8ab
         }
14f8ab
         if (is_invalidate) {
14f8ab
-            inode->invalidate_sent = true;
14f8ab
+            inode->in_invalidate_list = true;
14f8ab
             inode->table->invalidate_size++;
14f8ab
             list_move_tail(&inode->list, &inode->table->invalidate);
14f8ab
         } else {
14f8ab
@@ -1609,6 +1609,7 @@ static int
14f8ab
 inode_table_prune(inode_table_t *table)
14f8ab
 {
14f8ab
     int ret = 0;
14f8ab
+    int ret1 = 0;
14f8ab
     struct list_head purge = {
14f8ab
         0,
14f8ab
     };
14f8ab
@@ -1647,6 +1648,10 @@ inode_table_prune(inode_table_t *table)
14f8ab
                 /* check for valid inode with 'nlookup' */
14f8ab
                 nlookup = GF_ATOMIC_GET(entry->nlookup);
14f8ab
                 if (nlookup) {
14f8ab
+                    if (entry->invalidate_sent) {
14f8ab
+                        list_move_tail(&entry->list, &table->lru);
14f8ab
+                        continue;
14f8ab
+                    }
14f8ab
                     __inode_ref(entry, true);
14f8ab
                     tmp = entry;
14f8ab
                     break;
14f8ab
@@ -1668,9 +1673,19 @@ inode_table_prune(inode_table_t *table)
14f8ab
     if (tmp) {
14f8ab
         xlator_t *old_THIS = THIS;
14f8ab
         THIS = table->invalidator_xl;
14f8ab
-        table->invalidator_fn(table->invalidator_xl, tmp);
14f8ab
+        ret1 = table->invalidator_fn(table->invalidator_xl, tmp);
14f8ab
         THIS = old_THIS;
14f8ab
-        inode_unref(tmp);
14f8ab
+        pthread_mutex_lock(&table->lock);
14f8ab
+        {
14f8ab
+            if (!ret1) {
14f8ab
+                tmp->invalidate_sent = true;
14f8ab
+                __inode_unref(tmp, false);
14f8ab
+            } else {
14f8ab
+                /* Move this back to the lru list*/
14f8ab
+                __inode_unref(tmp, true);
14f8ab
+            }
14f8ab
+        }
14f8ab
+        pthread_mutex_unlock(&table->lock);
14f8ab
     }
14f8ab
 
14f8ab
     /* Just so that if purge list is handled too, then clear it off */
14f8ab
diff --git a/xlators/mount/fuse/src/fuse-bridge.c b/xlators/mount/fuse/src/fuse-bridge.c
14f8ab
index 1c946a2..8b2e7f0 100644
14f8ab
--- a/xlators/mount/fuse/src/fuse-bridge.c
14f8ab
+++ b/xlators/mount/fuse/src/fuse-bridge.c
14f8ab
@@ -26,7 +26,7 @@ static int gf_fuse_xattr_enotsup_log;
14f8ab
 void
14f8ab
 fini(xlator_t *this_xl);
14f8ab
 
14f8ab
-static void
14f8ab
+static int32_t
14f8ab
 fuse_invalidate_inode(xlator_t *this, uint64_t fuse_ino);
14f8ab
 
14f8ab
 /*
14f8ab
@@ -312,7 +312,7 @@ send_fuse_data(xlator_t *this, fuse_in_header_t *finh, void *data, size_t size)
14f8ab
 #define send_fuse_obj(this, finh, obj)                                         \
14f8ab
     send_fuse_data(this, finh, obj, sizeof(*(obj)))
14f8ab
 
14f8ab
-static void
14f8ab
+static int32_t
14f8ab
 fuse_invalidate_entry(xlator_t *this, uint64_t fuse_ino)
14f8ab
 {
14f8ab
 #if FUSE_KERNEL_MINOR_VERSION >= 11
14f8ab
@@ -328,17 +328,22 @@ fuse_invalidate_entry(xlator_t *this, uint64_t fuse_ino)
14f8ab
 
14f8ab
     priv = this->private;
14f8ab
     if (!priv->reverse_fuse_thread_started)
14f8ab
-        return;
14f8ab
+        return -1;
14f8ab
+
14f8ab
+    if (priv->invalidate_limit &&
14f8ab
+        (priv->invalidate_count >= priv->invalidate_limit)) {
14f8ab
+        return -1;
14f8ab
+    }
14f8ab
 
14f8ab
     inode = (inode_t *)(unsigned long)fuse_ino;
14f8ab
     if (inode == NULL)
14f8ab
-        return;
14f8ab
+        return -1;
14f8ab
 
14f8ab
     list_for_each_entry_safe(dentry, tmp, &inode->dentry_list, inode_list)
14f8ab
     {
14f8ab
         node = GF_CALLOC(1, sizeof(*node), gf_fuse_mt_invalidate_node_t);
14f8ab
         if (node == NULL)
14f8ab
-            break;
14f8ab
+            return -1;
14f8ab
 
14f8ab
         INIT_LIST_HEAD(&node->next);
14f8ab
 
14f8ab
@@ -375,20 +380,21 @@ fuse_invalidate_entry(xlator_t *this, uint64_t fuse_ino)
14f8ab
         pthread_mutex_lock(&priv->invalidate_mutex);
14f8ab
         {
14f8ab
             list_add_tail(&node->next, &priv->invalidate_list);
14f8ab
+            priv->invalidate_count++;
14f8ab
             pthread_cond_signal(&priv->invalidate_cond);
14f8ab
         }
14f8ab
         pthread_mutex_unlock(&priv->invalidate_mutex);
14f8ab
     }
14f8ab
 
14f8ab
 #endif
14f8ab
-    return;
14f8ab
+    return 0;
14f8ab
 }
14f8ab
 
14f8ab
 /*
14f8ab
  * Send an inval inode notification to fuse. This causes an invalidation of the
14f8ab
  * entire page cache mapping on the inode.
14f8ab
  */
14f8ab
-static void
14f8ab
+static int32_t
14f8ab
 fuse_invalidate_inode(xlator_t *this, uint64_t fuse_ino)
14f8ab
 {
14f8ab
 #if FUSE_KERNEL_MINOR_VERSION >= 11
14f8ab
@@ -401,15 +407,20 @@ fuse_invalidate_inode(xlator_t *this, uint64_t fuse_ino)
14f8ab
     priv = this->private;
14f8ab
 
14f8ab
     if (!priv->reverse_fuse_thread_started)
14f8ab
-        return;
14f8ab
+        return -1;
14f8ab
+
14f8ab
+    if (priv->invalidate_limit &&
14f8ab
+        (priv->invalidate_count >= priv->invalidate_limit)) {
14f8ab
+        return -1;
14f8ab
+    }
14f8ab
 
14f8ab
     inode = (inode_t *)(unsigned long)fuse_ino;
14f8ab
     if (inode == NULL)
14f8ab
-        return;
14f8ab
+        return -1;
14f8ab
 
14f8ab
     node = GF_CALLOC(1, sizeof(*node), gf_fuse_mt_invalidate_node_t);
14f8ab
     if (node == NULL)
14f8ab
-        return;
14f8ab
+        return -1;
14f8ab
 
14f8ab
     INIT_LIST_HEAD(&node->next);
14f8ab
 
14f8ab
@@ -435,6 +446,7 @@ fuse_invalidate_inode(xlator_t *this, uint64_t fuse_ino)
14f8ab
     pthread_mutex_lock(&priv->invalidate_mutex);
14f8ab
     {
14f8ab
         list_add_tail(&node->next, &priv->invalidate_list);
14f8ab
+        priv->invalidate_count++;
14f8ab
         pthread_cond_signal(&priv->invalidate_cond);
14f8ab
     }
14f8ab
     pthread_mutex_unlock(&priv->invalidate_mutex);
14f8ab
@@ -443,7 +455,7 @@ fuse_invalidate_inode(xlator_t *this, uint64_t fuse_ino)
14f8ab
     gf_log("glusterfs-fuse", GF_LOG_WARNING,
14f8ab
            "fuse_invalidate_inode not implemented on this system");
14f8ab
 #endif
14f8ab
-    return;
14f8ab
+    return 0;
14f8ab
 }
14f8ab
 
14f8ab
 #if FUSE_KERNEL_MINOR_VERSION >= 11
14f8ab
@@ -451,8 +463,9 @@ fuse_invalidate_inode(xlator_t *this, uint64_t fuse_ino)
14f8ab
 static int32_t
14f8ab
 fuse_inode_invalidate_fn(xlator_t *this, inode_t *inode)
14f8ab
 {
14f8ab
-    fuse_invalidate_entry(this, (uint64_t)(uintptr_t)inode);
14f8ab
-    return 0;
14f8ab
+    int32_t ret = 0;
14f8ab
+    ret = fuse_invalidate_entry(this, (uint64_t)(uintptr_t)inode);
14f8ab
+    return ret;
14f8ab
 }
14f8ab
 #endif
14f8ab
 
14f8ab
@@ -4003,7 +4016,9 @@ fuse_setxattr(xlator_t *this, fuse_in_header_t *finh, void *msg,
14f8ab
         gf_log("fuse", GF_LOG_TRACE, "got request to invalidate %" PRIu64,
14f8ab
                finh->nodeid);
14f8ab
 #if FUSE_KERNEL_MINOR_VERSION >= 11
14f8ab
-        fuse_invalidate_entry(this, finh->nodeid);
14f8ab
+        ret = fuse_invalidate_entry(this, finh->nodeid);
14f8ab
+        if (ret)
14f8ab
+            op_errno = EBUSY;
14f8ab
 #endif
14f8ab
         goto done;
14f8ab
     }
14f8ab
@@ -4812,6 +4827,7 @@ notify_kernel_loop(void *data)
14f8ab
                               fuse_invalidate_node_t, next);
14f8ab
 
14f8ab
             list_del_init(&node->next);
14f8ab
+            priv->invalidate_count--;
14f8ab
         }
14f8ab
         pthread_mutex_unlock(&priv->invalidate_mutex);
14f8ab
 
14f8ab
@@ -4855,6 +4871,7 @@ notify_kernel_loop(void *data)
14f8ab
             list_del_init(&node->next);
14f8ab
             GF_FREE(node);
14f8ab
         }
14f8ab
+        priv->invalidate_count = 0;
14f8ab
     }
14f8ab
     pthread_mutex_unlock(&priv->invalidate_mutex);
14f8ab
 
14f8ab
@@ -6080,6 +6097,9 @@ fuse_priv_dump(xlator_t *this)
14f8ab
                        (int)private->timed_response_fuse_thread_started);
14f8ab
     gf_proc_dump_write("reverse_thread_started", "%d",
14f8ab
                        (int)private->reverse_fuse_thread_started);
14f8ab
+    gf_proc_dump_write("invalidate_limit", "%u", private->invalidate_limit);
14f8ab
+    gf_proc_dump_write("invalidate_queue_length", "%" PRIu64,
14f8ab
+                       private->invalidate_count);
14f8ab
     gf_proc_dump_write("use_readdirp", "%d", private->use_readdirp);
14f8ab
 
14f8ab
     return 0;
14f8ab
@@ -6619,6 +6639,9 @@ init(xlator_t *this_xl)
14f8ab
 
14f8ab
     GF_OPTION_INIT("lru-limit", priv->lru_limit, uint32, cleanup_exit);
14f8ab
 
14f8ab
+    GF_OPTION_INIT("invalidate-limit", priv->invalidate_limit, uint32,
14f8ab
+                   cleanup_exit);
14f8ab
+
14f8ab
     GF_OPTION_INIT("event-history", priv->event_history, bool, cleanup_exit);
14f8ab
 
14f8ab
     GF_OPTION_INIT("thin-client", priv->thin_client, bool, cleanup_exit);
14f8ab
@@ -6955,6 +6978,15 @@ struct volume_options options[] = {
14f8ab
                        "reaching this limit (0 means 'unlimited')",
14f8ab
     },
14f8ab
     {
14f8ab
+        .key = {"invalidate-limit"},
14f8ab
+        .type = GF_OPTION_TYPE_INT,
14f8ab
+        .default_value = "0",
14f8ab
+        .min = 0,
14f8ab
+        .description = "suspend invalidations as of 'lru-limit' if the number "
14f8ab
+                       "of outstanding invalidations reaches this limit "
14f8ab
+                       "(0 means 'unlimited')",
14f8ab
+    },
14f8ab
+    {
14f8ab
         .key = {"auto-invalidation"},
14f8ab
         .type = GF_OPTION_TYPE_BOOL,
14f8ab
         .default_value = "true",
14f8ab
diff --git a/xlators/mount/fuse/src/fuse-bridge.h b/xlators/mount/fuse/src/fuse-bridge.h
14f8ab
index 697bd88..2311582 100644
14f8ab
--- a/xlators/mount/fuse/src/fuse-bridge.h
14f8ab
+++ b/xlators/mount/fuse/src/fuse-bridge.h
14f8ab
@@ -139,7 +139,7 @@ struct fuse_private {
14f8ab
     pthread_cond_t invalidate_cond;
14f8ab
     pthread_mutex_t invalidate_mutex;
14f8ab
     gf_boolean_t reverse_fuse_thread_started;
14f8ab
-
14f8ab
+    uint64_t invalidate_count;
14f8ab
     /* For communicating with separate mount thread. */
14f8ab
     int status_pipe[2];
14f8ab
 
14f8ab
@@ -191,6 +191,7 @@ struct fuse_private {
14f8ab
 
14f8ab
     /* LRU Limit, if not set, default is 128k for now */
14f8ab
     uint32_t lru_limit;
14f8ab
+    uint32_t invalidate_limit;
14f8ab
 };
14f8ab
 typedef struct fuse_private fuse_private_t;
14f8ab
 
14f8ab
diff --git a/xlators/mount/fuse/utils/mount.glusterfs.in b/xlators/mount/fuse/utils/mount.glusterfs.in
14f8ab
index cbde42d..61d7422 100755
14f8ab
--- a/xlators/mount/fuse/utils/mount.glusterfs.in
14f8ab
+++ b/xlators/mount/fuse/utils/mount.glusterfs.in
14f8ab
@@ -257,6 +257,10 @@ start_glusterfs ()
14f8ab
         cmd_line=$(echo "$cmd_line --lru-limit=$lru_limit");
14f8ab
     fi
14f8ab
 
14f8ab
+    if [ -n "$invalidate_limit" ]; then
14f8ab
+        cmd_line=$(echo "$cmd_line --invalidate-limit=$invalidate_limit");
14f8ab
+    fi
14f8ab
+
14f8ab
     if [ -n "$bg_qlen" ]; then
14f8ab
         cmd_line=$(echo "$cmd_line --background-qlen=$bg_qlen");
14f8ab
     fi
14f8ab
@@ -505,6 +509,9 @@ with_options()
14f8ab
         "lru-limit")
14f8ab
             lru_limit=$value
14f8ab
             ;;
14f8ab
+        "invalidate-limit")
14f8ab
+            invalidate_limit=$value
14f8ab
+            ;;
14f8ab
         "background-qlen")
14f8ab
             bg_qlen=$value
14f8ab
             ;;
14f8ab
-- 
14f8ab
1.8.3.1
14f8ab