9ae3f9
From 866a4c49ad9c5a9125814a9f843d4c7fd967ab2b Mon Sep 17 00:00:00 2001
9ae3f9
From: Kotresh HR <khiremat@redhat.com>
9ae3f9
Date: Mon, 3 Feb 2020 18:10:17 +0530
9ae3f9
Subject: [PATCH 446/449] bitrot: Make number of signer threads configurable
9ae3f9
9ae3f9
The number of signing process threads (glfs_brpobj)
9ae3f9
is set to 4 by default. The recommendation is to set
9ae3f9
it to number of cores available. This patch makes it
9ae3f9
configurable as follows
9ae3f9
9ae3f9
gluster vol bitrot <volname> signer-threads <count>
9ae3f9
9ae3f9
> fixes: bz#1797869
9ae3f9
> Change-Id: Ia883b3e5e34e0bc8d095243508d320c9c9c58adc
9ae3f9
> Signed-off-by: Kotresh HR <khiremat@redhat.com>
9ae3f9
> (Cherry pick from commit 8fad76650bd85463708f59d2518f5b764ae4c702)
9ae3f9
> (Reviewed on upstream link https://review.gluster.org/#/c/glusterfs/+/24091/)
9ae3f9
9ae3f9
BUG: 1790336
9ae3f9
Change-Id: Ia883b3e5e34e0bc8d095243508d320c9c9c58adc
9ae3f9
Signed-off-by: Mohit Agrawal <moagrawa@redhat.com>
9ae3f9
Reviewed-on: https://code.engineering.redhat.com/gerrit/202780
9ae3f9
Reviewed-by: Sunil Kumar Heggodu Gopala Acharya <sheggodu@redhat.com>
9ae3f9
---
9ae3f9
 cli/src/cli-cmd-parser.c                           | 29 +++++++-
9ae3f9
 cli/src/cli-cmd-volume.c                           | 12 +++
9ae3f9
 doc/gluster.8                                      |  6 ++
9ae3f9
 libglusterfs/src/glusterfs/common-utils.h          |  1 +
9ae3f9
 rpc/xdr/src/cli1-xdr.x                             |  1 +
9ae3f9
 tests/bitrot/br-signer-threads-config-1797869.t    | 73 +++++++++++++++++++
9ae3f9
 xlators/features/bit-rot/src/bitd/bit-rot.c        | 45 +++++++++---
9ae3f9
 xlators/features/bit-rot/src/bitd/bit-rot.h        | 20 ++---
9ae3f9
 .../bit-rot/src/stub/bit-rot-stub-mem-types.h      |  1 +
9ae3f9
 xlators/mgmt/glusterd/src/glusterd-bitrot.c        | 85 ++++++++++++++++++++++
9ae3f9
 xlators/mgmt/glusterd/src/glusterd-volgen.c        | 16 ++--
9ae3f9
 xlators/mgmt/glusterd/src/glusterd-volume-set.c    |  9 +++
9ae3f9
 12 files changed, 270 insertions(+), 28 deletions(-)
9ae3f9
 create mode 100644 tests/bitrot/br-signer-threads-config-1797869.t
9ae3f9
9ae3f9
diff --git a/cli/src/cli-cmd-parser.c b/cli/src/cli-cmd-parser.c
9ae3f9
index 7446b95..5fd05f4 100644
9ae3f9
--- a/cli/src/cli-cmd-parser.c
9ae3f9
+++ b/cli/src/cli-cmd-parser.c
9ae3f9
@@ -5661,7 +5661,7 @@ cli_cmd_bitrot_parse(const char **words, int wordcount, dict_t **options)
9ae3f9
     char *volname = NULL;
9ae3f9
     char *opwords[] = {
9ae3f9
         "enable",       "disable", "scrub-throttle", "scrub-frequency", "scrub",
9ae3f9
-        "signing-time", NULL};
9ae3f9
+        "signing-time", "signer-threads", NULL};
9ae3f9
     char *scrub_throt_values[] = {"lazy", "normal", "aggressive", NULL};
9ae3f9
     char *scrub_freq_values[] = {"hourly",  "daily",  "weekly", "biweekly",
9ae3f9
                                  "monthly", "minute", NULL};
9ae3f9
@@ -5669,6 +5669,7 @@ cli_cmd_bitrot_parse(const char **words, int wordcount, dict_t **options)
9ae3f9
     dict_t *dict = NULL;
9ae3f9
     gf_bitrot_type type = GF_BITROT_OPTION_TYPE_NONE;
9ae3f9
     int32_t expiry_time = 0;
9ae3f9
+    int32_t signer_th_count = 0;
9ae3f9
 
9ae3f9
     GF_ASSERT(words);
9ae3f9
     GF_ASSERT(options);
9ae3f9
@@ -5849,6 +5850,31 @@ cli_cmd_bitrot_parse(const char **words, int wordcount, dict_t **options)
9ae3f9
             }
9ae3f9
             goto set_type;
9ae3f9
         }
9ae3f9
+    } else if (!strcmp(words[3], "signer-threads")) {
9ae3f9
+        if (!words[4]) {
9ae3f9
+            cli_err(
9ae3f9
+                "Missing signer-thread value for bitrot "
9ae3f9
+                "option");
9ae3f9
+            ret = -1;
9ae3f9
+            goto out;
9ae3f9
+        } else {
9ae3f9
+            type = GF_BITROT_OPTION_TYPE_SIGNER_THREADS;
9ae3f9
+
9ae3f9
+            signer_th_count = strtol(words[4], NULL, 0);
9ae3f9
+            if (signer_th_count < 1) {
9ae3f9
+                cli_err("signer-thread count should not be less than 1");
9ae3f9
+                ret = -1;
9ae3f9
+                goto out;
9ae3f9
+            }
9ae3f9
+
9ae3f9
+            ret = dict_set_uint32(dict, "signer-threads",
9ae3f9
+                                  (unsigned int)signer_th_count);
9ae3f9
+            if (ret) {
9ae3f9
+                cli_out("Failed to set dict for bitrot");
9ae3f9
+                goto out;
9ae3f9
+            }
9ae3f9
+            goto set_type;
9ae3f9
+        }
9ae3f9
     } else {
9ae3f9
         cli_err(
9ae3f9
             "Invalid option %s for bitrot. Please enter valid "
9ae3f9
@@ -5857,7 +5883,6 @@ cli_cmd_bitrot_parse(const char **words, int wordcount, dict_t **options)
9ae3f9
         ret = -1;
9ae3f9
         goto out;
9ae3f9
     }
9ae3f9
-
9ae3f9
 set_type:
9ae3f9
     ret = dict_set_int32(dict, "type", type);
9ae3f9
     if (ret < 0)
9ae3f9
diff --git a/cli/src/cli-cmd-volume.c b/cli/src/cli-cmd-volume.c
9ae3f9
index f33fc99..72504ca 100644
9ae3f9
--- a/cli/src/cli-cmd-volume.c
9ae3f9
+++ b/cli/src/cli-cmd-volume.c
9ae3f9
@@ -3236,6 +3236,16 @@ struct cli_cmd bitrot_cmds[] = {
9ae3f9
     {"volume bitrot <VOLNAME> {enable|disable}", NULL, /*cli_cmd_bitrot_cbk,*/
9ae3f9
      "Enable/disable bitrot for volume <VOLNAME>"},
9ae3f9
 
9ae3f9
+    {"volume bitrot <VOLNAME> signing-time <time-in-secs>",
9ae3f9
+     NULL, /*cli_cmd_bitrot_cbk,*/
9ae3f9
+     "Waiting time for an object after last fd is closed to start signing "
9ae3f9
+     "process"},
9ae3f9
+
9ae3f9
+    {"volume bitrot <VOLNAME> signer-threads <count>",
9ae3f9
+     NULL, /*cli_cmd_bitrot_cbk,*/
9ae3f9
+     "Number of signing process threads. Usually set to number of available "
9ae3f9
+     "cores"},
9ae3f9
+
9ae3f9
     {"volume bitrot <VOLNAME> scrub-throttle {lazy|normal|aggressive}",
9ae3f9
      NULL, /*cli_cmd_bitrot_cbk,*/
9ae3f9
      "Set the speed of the scrubber for volume <VOLNAME>"},
9ae3f9
@@ -3251,6 +3261,8 @@ struct cli_cmd bitrot_cmds[] = {
9ae3f9
      "the scrubber. ondemand starts the scrubber immediately."},
9ae3f9
 
9ae3f9
     {"volume bitrot <VOLNAME> {enable|disable}\n"
9ae3f9
+     "volume bitrot <VOLNAME> signing-time <time-in-secs>\n"
9ae3f9
+     "volume bitrot <VOLNAME> signer-threads <count>\n"
9ae3f9
      "volume bitrot <volname> scrub-throttle {lazy|normal|aggressive}\n"
9ae3f9
      "volume bitrot <volname> scrub-frequency {hourly|daily|weekly|biweekly"
9ae3f9
      "|monthly}\n"
9ae3f9
diff --git a/doc/gluster.8 b/doc/gluster.8
9ae3f9
index 66bdb48..084346d 100644
9ae3f9
--- a/doc/gluster.8
9ae3f9
+++ b/doc/gluster.8
9ae3f9
@@ -244,6 +244,12 @@ Use "!<OPTION>" to reset option <OPTION> to default value.
9ae3f9
 \fB\ volume bitrot <VOLNAME> {enable|disable} \fR
9ae3f9
 Enable/disable bitrot for volume <VOLNAME>
9ae3f9
 .TP
9ae3f9
+\fB\ volume bitrot <VOLNAME> signing-time <time-in-secs> \fR
9ae3f9
+Waiting time for an object after last fd is closed to start signing process.
9ae3f9
+.TP
9ae3f9
+\fB\ volume bitrot <VOLNAME> signer-threads <count> \fR
9ae3f9
+Number of signing process threads. Usually set to number of available cores.
9ae3f9
+.TP
9ae3f9
 \fB\ volume bitrot <VOLNAME> scrub-throttle {lazy|normal|aggressive} \fR
9ae3f9
 Scrub-throttle value is a measure of how fast or slow the scrubber scrubs the filesystem for volume <VOLNAME>
9ae3f9
 .TP
9ae3f9
diff --git a/libglusterfs/src/glusterfs/common-utils.h b/libglusterfs/src/glusterfs/common-utils.h
9ae3f9
index 0e2ecc7..f0a0a41 100644
9ae3f9
--- a/libglusterfs/src/glusterfs/common-utils.h
9ae3f9
+++ b/libglusterfs/src/glusterfs/common-utils.h
9ae3f9
@@ -126,6 +126,7 @@ trap(void);
9ae3f9
 
9ae3f9
 /* Default value of signing waiting time to sign a file for bitrot */
9ae3f9
 #define SIGNING_TIMEOUT "120"
9ae3f9
+#define BR_WORKERS "4"
9ae3f9
 
9ae3f9
 /* xxhash */
9ae3f9
 #define GF_XXH64_DIGEST_LENGTH 8
9ae3f9
diff --git a/rpc/xdr/src/cli1-xdr.x b/rpc/xdr/src/cli1-xdr.x
9ae3f9
index a32c864..777cb00 100644
9ae3f9
--- a/rpc/xdr/src/cli1-xdr.x
9ae3f9
+++ b/rpc/xdr/src/cli1-xdr.x
9ae3f9
@@ -68,6 +68,7 @@ enum gf_bitrot_type {
9ae3f9
         GF_BITROT_OPTION_TYPE_EXPIRY_TIME,
9ae3f9
         GF_BITROT_CMD_SCRUB_STATUS,
9ae3f9
         GF_BITROT_CMD_SCRUB_ONDEMAND,
9ae3f9
+        GF_BITROT_OPTION_TYPE_SIGNER_THREADS,
9ae3f9
         GF_BITROT_OPTION_TYPE_MAX
9ae3f9
 };
9ae3f9
 
9ae3f9
diff --git a/tests/bitrot/br-signer-threads-config-1797869.t b/tests/bitrot/br-signer-threads-config-1797869.t
9ae3f9
new file mode 100644
9ae3f9
index 0000000..657ef3e
9ae3f9
--- /dev/null
9ae3f9
+++ b/tests/bitrot/br-signer-threads-config-1797869.t
9ae3f9
@@ -0,0 +1,73 @@
9ae3f9
+#!/bin/bash
9ae3f9
+
9ae3f9
+. $(dirname $0)/../include.rc
9ae3f9
+. $(dirname $0)/../volume.rc
9ae3f9
+. $(dirname $0)/../cluster.rc
9ae3f9
+
9ae3f9
+function get_bitd_count_1 {
9ae3f9
+        ps auxww | grep glusterfs | grep bitd.pid | grep -v grep | grep $H1 | wc -l
9ae3f9
+}
9ae3f9
+
9ae3f9
+function get_bitd_count_2 {
9ae3f9
+        ps auxww | grep glusterfs | grep bitd.pid | grep -v grep | grep $H2 | wc -l
9ae3f9
+}
9ae3f9
+
9ae3f9
+function get_bitd_pid_1 {
9ae3f9
+        ps auxww | grep glusterfs | grep bitd.pid | grep -v grep | grep $H1 | awk '{print $2}'
9ae3f9
+}
9ae3f9
+
9ae3f9
+function get_bitd_pid_2 {
9ae3f9
+        ps auxww | grep glusterfs | grep bitd.pid | grep -v grep | grep $H2 | awk '{print $2}'
9ae3f9
+}
9ae3f9
+
9ae3f9
+function get_signer_th_count_1 {
9ae3f9
+        ps -eL | grep $(get_bitd_pid_1) | grep glfs_brpobj | wc -l
9ae3f9
+}
9ae3f9
+
9ae3f9
+function get_signer_th_count_2 {
9ae3f9
+        ps -eL | grep $(get_bitd_pid_2) | grep glfs_brpobj | wc -l
9ae3f9
+}
9ae3f9
+
9ae3f9
+cleanup;
9ae3f9
+
9ae3f9
+TEST launch_cluster 2
9ae3f9
+
9ae3f9
+TEST $CLI_1 peer probe $H2;
9ae3f9
+EXPECT_WITHIN $PROBE_TIMEOUT 1 peer_count;
9ae3f9
+
9ae3f9
+TEST $CLI_1 volume create $V0 $H1:$B1
9ae3f9
+TEST $CLI_1 volume create $V1 $H2:$B2
9ae3f9
+EXPECT 'Created' volinfo_field_1 $V0 'Status';
9ae3f9
+EXPECT 'Created' volinfo_field_1 $V1 'Status';
9ae3f9
+
9ae3f9
+TEST $CLI_1 volume start $V0
9ae3f9
+TEST $CLI_1 volume start $V1
9ae3f9
+EXPECT 'Started' volinfo_field_1 $V0 'Status';
9ae3f9
+EXPECT 'Started' volinfo_field_1 $V1 'Status';
9ae3f9
+
9ae3f9
+#Enable bitrot
9ae3f9
+TEST $CLI_1 volume bitrot $V0 enable
9ae3f9
+TEST $CLI_1 volume bitrot $V1 enable
9ae3f9
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" get_bitd_count_1
9ae3f9
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" get_bitd_count_2
9ae3f9
+
9ae3f9
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "4" get_signer_th_count_1
9ae3f9
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "4" get_signer_th_count_2
9ae3f9
+
9ae3f9
+old_bitd_pid_1=$(get_bitd_pid_1)
9ae3f9
+old_bitd_pid_2=$(get_bitd_pid_2)
9ae3f9
+TEST $CLI_1 volume bitrot $V0 signer-threads 1
9ae3f9
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" get_signer_th_count_1
9ae3f9
+EXPECT_NOT "$old_bitd_pid_1" get_bitd_pid_1;
9ae3f9
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "4" get_signer_th_count_2
9ae3f9
+EXPECT "$old_bitd_pid_2" get_bitd_pid_2;
9ae3f9
+
9ae3f9
+old_bitd_pid_1=$(get_bitd_pid_1)
9ae3f9
+old_bitd_pid_2=$(get_bitd_pid_2)
9ae3f9
+TEST $CLI_1 volume bitrot $V1 signer-threads 2
9ae3f9
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "2" get_signer_th_count_2
9ae3f9
+EXPECT_NOT "$old_bitd_pid_2" get_bitd_pid_2;
9ae3f9
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" get_signer_th_count_1
9ae3f9
+EXPECT "$old_bitd_pid_1" get_bitd_pid_1;
9ae3f9
+
9ae3f9
+cleanup;
9ae3f9
diff --git a/xlators/features/bit-rot/src/bitd/bit-rot.c b/xlators/features/bit-rot/src/bitd/bit-rot.c
9ae3f9
index 7b1c5dc..b8feef7 100644
9ae3f9
--- a/xlators/features/bit-rot/src/bitd/bit-rot.c
9ae3f9
+++ b/xlators/features/bit-rot/src/bitd/bit-rot.c
9ae3f9
@@ -1734,22 +1734,26 @@ out:
9ae3f9
     return 0;
9ae3f9
 }
9ae3f9
 
9ae3f9
-/**
9ae3f9
- * Initialize signer specific structures, spawn worker threads.
9ae3f9
- */
9ae3f9
-
9ae3f9
 static void
9ae3f9
 br_fini_signer(xlator_t *this, br_private_t *priv)
9ae3f9
 {
9ae3f9
     int i = 0;
9ae3f9
 
9ae3f9
-    for (; i < BR_WORKERS; i++) {
9ae3f9
+    if (priv == NULL)
9ae3f9
+        return;
9ae3f9
+
9ae3f9
+    for (; i < priv->signer_th_count; i++) {
9ae3f9
         (void)gf_thread_cleanup_xint(priv->obj_queue->workers[i]);
9ae3f9
     }
9ae3f9
+    GF_FREE(priv->obj_queue->workers);
9ae3f9
 
9ae3f9
     pthread_cond_destroy(&priv->object_cond);
9ae3f9
 }
9ae3f9
 
9ae3f9
+/**
9ae3f9
+ * Initialize signer specific structures, spawn worker threads.
9ae3f9
+ */
9ae3f9
+
9ae3f9
 static int32_t
9ae3f9
 br_init_signer(xlator_t *this, br_private_t *priv)
9ae3f9
 {
9ae3f9
@@ -1769,7 +1773,12 @@ br_init_signer(xlator_t *this, br_private_t *priv)
9ae3f9
         goto cleanup_cond;
9ae3f9
     INIT_LIST_HEAD(&priv->obj_queue->objects);
9ae3f9
 
9ae3f9
-    for (i = 0; i < BR_WORKERS; i++) {
9ae3f9
+    priv->obj_queue->workers = GF_CALLOC(
9ae3f9
+        priv->signer_th_count, sizeof(pthread_t), gf_br_mt_br_worker_t);
9ae3f9
+    if (!priv->obj_queue->workers)
9ae3f9
+        goto cleanup_obj_queue;
9ae3f9
+
9ae3f9
+    for (i = 0; i < priv->signer_th_count; i++) {
9ae3f9
         ret = gf_thread_create(&priv->obj_queue->workers[i], NULL,
9ae3f9
                                br_process_object, this, "brpobj");
9ae3f9
         if (ret != 0) {
9ae3f9
@@ -1787,7 +1796,9 @@ cleanup_threads:
9ae3f9
     for (i--; i >= 0; i--) {
9ae3f9
         (void)gf_thread_cleanup_xint(priv->obj_queue->workers[i]);
9ae3f9
     }
9ae3f9
+    GF_FREE(priv->obj_queue->workers);
9ae3f9
 
9ae3f9
+cleanup_obj_queue:
9ae3f9
     GF_FREE(priv->obj_queue);
9ae3f9
 
9ae3f9
 cleanup_cond:
9ae3f9
@@ -1840,7 +1851,7 @@ br_rate_limit_signer(xlator_t *this, int child_count, int numbricks)
9ae3f9
     if (contribution == 0)
9ae3f9
         contribution = 1;
9ae3f9
     spec.rate = BR_HASH_CALC_READ_SIZE * contribution;
9ae3f9
-    spec.maxlimit = BR_WORKERS * BR_HASH_CALC_READ_SIZE;
9ae3f9
+    spec.maxlimit = priv->signer_th_count * BR_HASH_CALC_READ_SIZE;
9ae3f9
 
9ae3f9
 #endif
9ae3f9
 
9ae3f9
@@ -1860,11 +1871,16 @@ br_rate_limit_signer(xlator_t *this, int child_count, int numbricks)
9ae3f9
 static int32_t
9ae3f9
 br_signer_handle_options(xlator_t *this, br_private_t *priv, dict_t *options)
9ae3f9
 {
9ae3f9
-    if (options)
9ae3f9
+    if (options) {
9ae3f9
         GF_OPTION_RECONF("expiry-time", priv->expiry_time, options, uint32,
9ae3f9
                          error_return);
9ae3f9
-    else
9ae3f9
+        GF_OPTION_RECONF("signer-threads", priv->signer_th_count, options,
9ae3f9
+                         uint32, error_return);
9ae3f9
+    } else {
9ae3f9
         GF_OPTION_INIT("expiry-time", priv->expiry_time, uint32, error_return);
9ae3f9
+        GF_OPTION_INIT("signer-threads", priv->signer_th_count, uint32,
9ae3f9
+                       error_return);
9ae3f9
+    }
9ae3f9
 
9ae3f9
     return 0;
9ae3f9
 
9ae3f9
@@ -1880,6 +1896,8 @@ br_signer_init(xlator_t *this, br_private_t *priv)
9ae3f9
 
9ae3f9
     GF_OPTION_INIT("expiry-time", priv->expiry_time, uint32, error_return);
9ae3f9
     GF_OPTION_INIT("brick-count", numbricks, int32, error_return);
9ae3f9
+    GF_OPTION_INIT("signer-threads", priv->signer_th_count, uint32,
9ae3f9
+                   error_return);
9ae3f9
 
9ae3f9
     ret = br_rate_limit_signer(this, priv->child_count, numbricks);
9ae3f9
     if (ret)
9ae3f9
@@ -2210,6 +2228,15 @@ struct volume_options options[] = {
9ae3f9
         .description = "Pause/Resume scrub. Upon resume, scrubber "
9ae3f9
                        "continues from where it left off.",
9ae3f9
     },
9ae3f9
+    {
9ae3f9
+        .key = {"signer-threads"},
9ae3f9
+        .type = GF_OPTION_TYPE_INT,
9ae3f9
+        .default_value = BR_WORKERS,
9ae3f9
+        .op_version = {GD_OP_VERSION_7_0},
9ae3f9
+        .flags = OPT_FLAG_SETTABLE,
9ae3f9
+        .description = "Number of signing process threads. As a best "
9ae3f9
+                       "practice, set this to the number of processor cores",
9ae3f9
+    },
9ae3f9
     {.key = {NULL}},
9ae3f9
 };
9ae3f9
 
9ae3f9
diff --git a/xlators/features/bit-rot/src/bitd/bit-rot.h b/xlators/features/bit-rot/src/bitd/bit-rot.h
9ae3f9
index a4d4fd7..8ac7dcd 100644
9ae3f9
--- a/xlators/features/bit-rot/src/bitd/bit-rot.h
9ae3f9
+++ b/xlators/features/bit-rot/src/bitd/bit-rot.h
9ae3f9
@@ -30,12 +30,6 @@
9ae3f9
 
9ae3f9
 #include <openssl/sha.h>
9ae3f9
 
9ae3f9
-/**
9ae3f9
- * TODO: make this configurable. As a best practice, set this to the
9ae3f9
- * number of processor cores.
9ae3f9
- */
9ae3f9
-#define BR_WORKERS 4
9ae3f9
-
9ae3f9
 typedef enum scrub_throttle {
9ae3f9
     BR_SCRUB_THROTTLE_VOID = -1,
9ae3f9
     BR_SCRUB_THROTTLE_LAZY = 0,
9ae3f9
@@ -108,12 +102,12 @@ struct br_child {
9ae3f9
 typedef struct br_child br_child_t;
9ae3f9
 
9ae3f9
 struct br_obj_n_workers {
9ae3f9
-    struct list_head objects;      /* queue of objects expired from the
9ae3f9
-                                      timer wheel and ready to be picked
9ae3f9
-                                      up for signing */
9ae3f9
-    pthread_t workers[BR_WORKERS]; /* Threads which pick up the objects
9ae3f9
-                                      from the above queue and start
9ae3f9
-                                      signing each object */
9ae3f9
+    struct list_head objects; /* queue of objects expired from the
9ae3f9
+                                 timer wheel and ready to be picked
9ae3f9
+                                 up for signing */
9ae3f9
+    pthread_t *workers;       /* Threads which pick up the objects
9ae3f9
+                                 from the above queue and start
9ae3f9
+                                 signing each object */
9ae3f9
 };
9ae3f9
 
9ae3f9
 struct br_scrubber {
9ae3f9
@@ -209,6 +203,8 @@ struct br_private {
9ae3f9
 
9ae3f9
     uint32_t expiry_time; /* objects "wait" time */
9ae3f9
 
9ae3f9
+    uint32_t signer_th_count; /* Number of signing process threads */
9ae3f9
+
9ae3f9
     tbf_t *tbf; /* token bucket filter */
9ae3f9
 
9ae3f9
     gf_boolean_t iamscrubber; /* function as a fs scrubber */
9ae3f9
diff --git a/xlators/features/bit-rot/src/stub/bit-rot-stub-mem-types.h b/xlators/features/bit-rot/src/stub/bit-rot-stub-mem-types.h
9ae3f9
index 40bcda1..9d93caf 100644
9ae3f9
--- a/xlators/features/bit-rot/src/stub/bit-rot-stub-mem-types.h
9ae3f9
+++ b/xlators/features/bit-rot/src/stub/bit-rot-stub-mem-types.h
9ae3f9
@@ -29,6 +29,7 @@ enum br_mem_types {
9ae3f9
     gf_br_stub_mt_sigstub_t,
9ae3f9
     gf_br_mt_br_child_event_t,
9ae3f9
     gf_br_stub_mt_misc,
9ae3f9
+    gf_br_mt_br_worker_t,
9ae3f9
     gf_br_stub_mt_end,
9ae3f9
 };
9ae3f9
 
9ae3f9
diff --git a/xlators/mgmt/glusterd/src/glusterd-bitrot.c b/xlators/mgmt/glusterd/src/glusterd-bitrot.c
9ae3f9
index c653249..f79af2d 100644
9ae3f9
--- a/xlators/mgmt/glusterd/src/glusterd-bitrot.c
9ae3f9
+++ b/xlators/mgmt/glusterd/src/glusterd-bitrot.c
9ae3f9
@@ -34,6 +34,7 @@ const char *gd_bitrot_op_list[GF_BITROT_OPTION_TYPE_MAX] = {
9ae3f9
     [GF_BITROT_OPTION_TYPE_SCRUB_FREQ] = "scrub-frequency",
9ae3f9
     [GF_BITROT_OPTION_TYPE_SCRUB] = "scrub",
9ae3f9
     [GF_BITROT_OPTION_TYPE_EXPIRY_TIME] = "expiry-time",
9ae3f9
+    [GF_BITROT_OPTION_TYPE_SIGNER_THREADS] = "signer-threads",
9ae3f9
 };
9ae3f9
 
9ae3f9
 int
9ae3f9
@@ -354,6 +355,81 @@ out:
9ae3f9
     return ret;
9ae3f9
 }
9ae3f9
 
9ae3f9
+static gf_boolean_t
9ae3f9
+is_bitd_configure_noop(xlator_t *this, glusterd_volinfo_t *volinfo)
9ae3f9
+{
9ae3f9
+    gf_boolean_t noop = _gf_true;
9ae3f9
+    glusterd_brickinfo_t *brickinfo = NULL;
9ae3f9
+
9ae3f9
+    if (!glusterd_is_bitrot_enabled(volinfo))
9ae3f9
+        goto out;
9ae3f9
+    else if (volinfo->status != GLUSTERD_STATUS_STARTED)
9ae3f9
+        goto out;
9ae3f9
+    else {
9ae3f9
+        cds_list_for_each_entry(brickinfo, &volinfo->bricks, brick_list)
9ae3f9
+        {
9ae3f9
+            if (!glusterd_is_local_brick(this, volinfo, brickinfo))
9ae3f9
+                continue;
9ae3f9
+            noop = _gf_false;
9ae3f9
+            return noop;
9ae3f9
+        }
9ae3f9
+    }
9ae3f9
+out:
9ae3f9
+    return noop;
9ae3f9
+}
9ae3f9
+
9ae3f9
+static int
9ae3f9
+glusterd_bitrot_signer_threads(glusterd_volinfo_t *volinfo, dict_t *dict,
9ae3f9
+                               char *key, char **op_errstr)
9ae3f9
+{
9ae3f9
+    int32_t ret = -1;
9ae3f9
+    uint32_t signer_th_count = 0;
9ae3f9
+    uint32_t existing_th_count = 0;
9ae3f9
+    xlator_t *this = NULL;
9ae3f9
+    glusterd_conf_t *priv = NULL;
9ae3f9
+    char dkey[32] = {
9ae3f9
+        0,
9ae3f9
+    };
9ae3f9
+
9ae3f9
+    this = THIS;
9ae3f9
+    GF_ASSERT(this);
9ae3f9
+
9ae3f9
+    priv = this->private;
9ae3f9
+    GF_VALIDATE_OR_GOTO(this->name, priv, out);
9ae3f9
+
9ae3f9
+    ret = dict_get_uint32(dict, "signer-threads", &signer_th_count);
9ae3f9
+    if (ret) {
9ae3f9
+        gf_msg(this->name, GF_LOG_ERROR, errno, GD_MSG_DICT_GET_FAILED,
9ae3f9
+               "Unable to get bitrot signer thread count.");
9ae3f9
+        goto out;
9ae3f9
+    }
9ae3f9
+
9ae3f9
+    ret = dict_get_uint32(volinfo->dict, key, &existing_th_count);
9ae3f9
+    if (ret == 0 && signer_th_count == existing_th_count) {
9ae3f9
+        goto out;
9ae3f9
+    }
9ae3f9
+
9ae3f9
+    snprintf(dkey, sizeof(dkey), "%d", signer_th_count);
9ae3f9
+    ret = dict_set_dynstr_with_alloc(volinfo->dict, key, dkey);
9ae3f9
+    if (ret) {
9ae3f9
+        gf_msg(this->name, GF_LOG_ERROR, errno, GD_MSG_DICT_SET_FAILED,
9ae3f9
+               "Failed to set option %s", key);
9ae3f9
+        goto out;
9ae3f9
+    }
9ae3f9
+
9ae3f9
+    if (!is_bitd_configure_noop(this, volinfo)) {
9ae3f9
+        ret = priv->bitd_svc.manager(&(priv->bitd_svc), NULL,
9ae3f9
+                                     PROC_START_NO_WAIT);
9ae3f9
+        if (ret) {
9ae3f9
+            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_BITDSVC_RECONF_FAIL,
9ae3f9
+                   "Failed to reconfigure bitrot services");
9ae3f9
+            goto out;
9ae3f9
+        }
9ae3f9
+    }
9ae3f9
+out:
9ae3f9
+    return ret;
9ae3f9
+}
9ae3f9
+
9ae3f9
 static int
9ae3f9
 glusterd_bitrot_enable(glusterd_volinfo_t *volinfo, char **op_errstr)
9ae3f9
 {
9ae3f9
@@ -594,6 +670,15 @@ glusterd_op_bitrot(dict_t *dict, char **op_errstr, dict_t *rsp_dict)
9ae3f9
                 volinfo, dict, "features.expiry-time", op_errstr);
9ae3f9
             if (ret)
9ae3f9
                 goto out;
9ae3f9
+            break;
9ae3f9
+
9ae3f9
+        case GF_BITROT_OPTION_TYPE_SIGNER_THREADS:
9ae3f9
+            ret = glusterd_bitrot_signer_threads(
9ae3f9
+                volinfo, dict, "features.signer-threads", op_errstr);
9ae3f9
+            if (ret)
9ae3f9
+                goto out;
9ae3f9
+            break;
9ae3f9
+
9ae3f9
         case GF_BITROT_CMD_SCRUB_STATUS:
9ae3f9
         case GF_BITROT_CMD_SCRUB_ONDEMAND:
9ae3f9
             break;
9ae3f9
diff --git a/xlators/mgmt/glusterd/src/glusterd-volgen.c b/xlators/mgmt/glusterd/src/glusterd-volgen.c
9ae3f9
index 13f84ea..094a71f 100644
9ae3f9
--- a/xlators/mgmt/glusterd/src/glusterd-volgen.c
9ae3f9
+++ b/xlators/mgmt/glusterd/src/glusterd-volgen.c
9ae3f9
@@ -4658,6 +4658,12 @@ bitrot_option_handler(volgen_graph_t *graph, struct volopt_map_entry *vme,
9ae3f9
             return -1;
9ae3f9
     }
9ae3f9
 
9ae3f9
+    if (!strcmp(vme->option, "signer-threads")) {
9ae3f9
+        ret = xlator_set_fixed_option(xl, "signer-threads", vme->value);
9ae3f9
+        if (ret)
9ae3f9
+            return -1;
9ae3f9
+    }
9ae3f9
+
9ae3f9
     return ret;
9ae3f9
 }
9ae3f9
 
9ae3f9
@@ -4940,18 +4946,18 @@ glusterd_prepare_shd_volume_options_for_tier(glusterd_volinfo_t *volinfo,
9ae3f9
                                              dict_t *set_dict)
9ae3f9
 {
9ae3f9
     int ret = -1;
9ae3f9
-    char           *key             = NULL;
9ae3f9
+    char *key = NULL;
9ae3f9
 
9ae3f9
-    key = volgen_get_shd_key (volinfo->tier_info.cold_type);
9ae3f9
+    key = volgen_get_shd_key(volinfo->tier_info.cold_type);
9ae3f9
     if (key) {
9ae3f9
-        ret = dict_set_str (set_dict, key, "enable");
9ae3f9
+        ret = dict_set_str(set_dict, key, "enable");
9ae3f9
         if (ret)
9ae3f9
             goto out;
9ae3f9
     }
9ae3f9
 
9ae3f9
-    key = volgen_get_shd_key (volinfo->tier_info.hot_type);
9ae3f9
+    key = volgen_get_shd_key(volinfo->tier_info.hot_type);
9ae3f9
     if (key) {
9ae3f9
-        ret = dict_set_str (set_dict, key, "enable");
9ae3f9
+        ret = dict_set_str(set_dict, key, "enable");
9ae3f9
         if (ret)
9ae3f9
             goto out;
9ae3f9
     }
9ae3f9
diff --git a/xlators/mgmt/glusterd/src/glusterd-volume-set.c b/xlators/mgmt/glusterd/src/glusterd-volume-set.c
9ae3f9
index 9001b88..62acadf 100644
9ae3f9
--- a/xlators/mgmt/glusterd/src/glusterd-volume-set.c
9ae3f9
+++ b/xlators/mgmt/glusterd/src/glusterd-volume-set.c
9ae3f9
@@ -3379,6 +3379,15 @@ struct volopt_map_entry glusterd_volopt_map[] = {
9ae3f9
         .op_version = GD_OP_VERSION_3_7_0,
9ae3f9
         .type = NO_DOC,
9ae3f9
     },
9ae3f9
+    {
9ae3f9
+        .key = "features.signer-threads",
9ae3f9
+        .voltype = "features/bit-rot",
9ae3f9
+        .value = BR_WORKERS,
9ae3f9
+        .option = "signer-threads",
9ae3f9
+        .op_version = GD_OP_VERSION_7_0,
9ae3f9
+        .type = NO_DOC,
9ae3f9
+    },
9ae3f9
+    /* Upcall translator options */
9ae3f9
     /* Upcall translator options */
9ae3f9
     {
9ae3f9
         .key = "features.cache-invalidation",
9ae3f9
-- 
9ae3f9
1.8.3.1
9ae3f9