Viewing: jbd2-revoke-rhashtable-rhel9.8.patch

commit c3bb2b778d6b40a5cecb01993b55fcc107305b4a
Author:     Alex Zhuravlev <bzzz@whamcloud.com>
AuthorDate: Tue Oct 11 14:59:48 2022 +0300
Commit:     Oleg Drokin <green@whamcloud.com>
CommitDate: Wed Mar 8 03:27:42 2023 +0000

    LU-14958 kernel: use rhashtable for revoke records in jbd2

    resizable hashtable should improve journal replay time when
    the latter has got million of revoke records. notice that
    rhashtable is used during replay only as removal with list_del()
    is less expensive and it's used a lot during regular processing.

    before:
    1048576 records - 95 seconds
    2097152 records - 580 seconds

    after:
    1048576 records - 2 seconds
    2097152 records - 3 seconds
    4194304 records - 7 seconds

    Signed-off-by: Alex Zhuravlev <bzzz@whamcloud.com>
    Change-Id: I9a9e3801223fa9e36cbf6d2ef5ddbad5dff3e19d
    Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/45122
    Tested-by: jenkins <devops@whamcloud.com>
    Tested-by: Maloo <maloo@whamcloud.com>
    Reviewed-by: jsimmons <jsimmons@infradead.org>
    Reviewed-by: Oleg Drokin <green@whamcloud.com>
    Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
    Reviewed-by: Andrew Perepechko <andrew.perepechko@hpe.com>
Index: linux-5.14.0-687.5.1.el9/fs/jbd2/recovery.c
===================================================================
--- linux-5.14.0-687.5.1.el9.orig/fs/jbd2/recovery.c
+++ linux-5.14.0-687.5.1.el9/fs/jbd2/recovery.c
@@ -19,6 +19,7 @@
 #include <linux/errno.h>
 #include <linux/crc32.h>
 #include <linux/blkdev.h>
+#include <linux/rhashtable.h>
 #endif

 /*
@@ -289,6 +290,10 @@ int jbd2_journal_recover(journal_t *jour

 	memset(&info, 0, sizeof(info));

+	err = jbd2_journal_init_recovery_revoke(journal);
+	if (err)
+		return err;
+
 	/*
 	 * The journal superblock's s_start field (the current log head)
 	 * is always zero if, and only if, the journal was cleanly
Index: linux-4.18.0-425.3.1.el8/fs/jbd2/revoke.c
===================================================================
--- linux-4.18.0-425.3.1.el8.orig/fs/jbd2/revoke.c
+++ linux-4.18.0-425.3.1.el8/fs/jbd2/revoke.c
@@ -90,6 +90,7 @@
 #include <linux/bio.h>
 #include <linux/log2.h>
 #include <linux/hash.h>
+#include <linux/rhashtable.h>
 #endif

 static struct kmem_cache *jbd2_revoke_record_cache;
@@ -101,7 +102,10 @@ static struct kmem_cache *jbd2_revoke_ta

 struct jbd2_revoke_record_s
 {
-	struct list_head  hash;
+	union {
+		struct list_head  hash;
+		struct rhash_head linkage;
+	};
 	tid_t		  sequence;	/* Used for recovery only */
 	unsigned long long	  blocknr;
 };
@@ -680,13 +684,21 @@ static void flush_descriptor(journal_t *
  * single block.
  */

+static const struct rhashtable_params revoke_rhashtable_params = {
+	.key_len     = sizeof(unsigned long long),
+	.key_offset  = offsetof(struct jbd2_revoke_record_s, blocknr),
+	.head_offset = offsetof(struct jbd2_revoke_record_s, linkage),
+};
+
 int jbd2_journal_set_revoke(journal_t *journal,
 		       unsigned long long blocknr,
 		       tid_t sequence)
 {
-	struct jbd2_revoke_record_s *record;
+	struct jbd2_revoke_record_s *record, *old;
+	gfp_t gfp_mask = GFP_NOFS;

-	record = find_revoke_record(journal, blocknr);
+	record = rhashtable_lookup(&journal->j_revoke_rhtable, &blocknr,
+				revoke_rhashtable_params);
 	if (record) {
 		/* If we have multiple occurrences, only record the
 		 * latest sequence number in the hashed record */
@@ -694,7 +706,24 @@ int jbd2_journal_set_revoke(journal_t *j
 			record->sequence = sequence;
 		return 0;
 	}
-	return insert_revoke_hash(journal, blocknr, sequence);
+
+	if (journal_oom_retry)
+		gfp_mask |= __GFP_NOFAIL;
+	record = kmem_cache_alloc(jbd2_revoke_record_cache, gfp_mask);
+	if (!record)
+		return -ENOMEM;
+
+	record->sequence = sequence;
+	record->blocknr = blocknr;
+	old = rhashtable_lookup_get_insert_fast(&journal->j_revoke_rhtable,
+				 &record->linkage, revoke_rhashtable_params);
+	if (IS_ERR(old)) {
+		kmem_cache_free(jbd2_revoke_record_cache, record);
+		return PTR_ERR(old);
+	}
+	BUG_ON(old != NULL);
+
+	return 0;
 }

 /*
@@ -710,7 +739,8 @@ int jbd2_journal_test_revoke(journal_t *
 {
 	struct jbd2_revoke_record_s *record;

-	record = find_revoke_record(journal, blocknr);
+	record = rhashtable_lookup(&journal->j_revoke_rhtable, &blocknr,
+				revoke_rhashtable_params);
 	if (!record)
 		return 0;
 	if (tid_gt(sequence, record->sequence))
@@ -718,6 +748,17 @@ int jbd2_journal_test_revoke(journal_t *
 	return 1;
 }

+int jbd2_journal_init_recovery_revoke(journal_t *journal)
+{
+	return rhashtable_init(&journal->j_revoke_rhtable,
+				&revoke_rhashtable_params);
+}
+
+static void jbd2_revoke_record_free(void *ptr, void *arg)
+{
+	kmem_cache_free(jbd2_revoke_record_cache, ptr);
+}
+
 /*
  * Finally, once recovery is over, we need to clear the revoke table so
  * that it can be reused by the running filesystem.
@@ -725,19 +766,6 @@ int jbd2_journal_test_revoke(journal_t *

 void jbd2_journal_clear_revoke(journal_t *journal)
 {
-	int i;
-	struct list_head *hash_list;
-	struct jbd2_revoke_record_s *record;
-	struct jbd2_revoke_table_s *revoke;
-
-	revoke = journal->j_revoke;
-
-	for (i = 0; i < revoke->hash_size; i++) {
-		hash_list = &revoke->hash_table[i];
-		while (!list_empty(hash_list)) {
-			record = (struct jbd2_revoke_record_s*) hash_list->next;
-			list_del(&record->hash);
-			kmem_cache_free(jbd2_revoke_record_cache, record);
-		}
-	}
+	rhashtable_free_and_destroy(&journal->j_revoke_rhtable,
+					jbd2_revoke_record_free, NULL);
 }
Index: linux-4.18.0-425.3.1.el8/include/linux/jbd2.h
===================================================================
--- linux-4.18.0-425.3.1.el8.orig/include/linux/jbd2.h
+++ linux-4.18.0-425.3.1.el8/include/linux/jbd2.h
@@ -1084,6 +1084,11 @@ struct journal_s
 	struct jbd2_revoke_table_s *j_revoke_table[2];

 	/**
+	 * @j_revoke_rhtable:	rhashtable for revoke records during recovery
+	 */
+	struct rhashtable	j_revoke_rhtable;
+
+	/**
 	 * @j_wbuf: Array of bhs for jbd2_journal_commit_transaction.
 	 */
 	struct buffer_head	**j_wbuf;
@@ -1508,6 +1513,7 @@ extern void	   jbd2_journal_write_revoke
 /* Recovery revoke support */
 extern int	jbd2_journal_set_revoke(journal_t *, unsigned long long, tid_t);
 extern int	jbd2_journal_test_revoke(journal_t *, unsigned long long, tid_t);
+extern int	jbd2_journal_init_recovery_revoke(journal_t *);
 extern void	jbd2_journal_clear_revoke(journal_t *);
 extern void	jbd2_journal_switch_revoke_table(journal_t *journal);
 extern void	jbd2_clear_buffer_revoked_flags(journal_t *journal);