Skip to contentSkip to conversation

e2fsprogs: Parallel fsck support

RFC Applied Ritesh Harjani (IBM) 4 participants 104 messages Nov 7, 2022 – Jan 24, 2023 1 message not in the archive Page 1 of 2

lore
Share & tools
lore
#0 lore raw

cover e2fsprogs: Parallel fsck support

Hello,

This is a RFC patch series which has the list of all pfsck related changes which should also address the problem statements mentioned by Ted and Harshad here [1]

Below points will provide more details on how this patch series is laid out:-

1. This series is rebased on the latest e2fsprogs master branch.

2. Patch series is broken down into logical patches for easier review. The series contains libext2fs changes first, followed by e2fsck changes which utilizes libext2fs apis to add pfsck support to pass1.

3. Changes [0005] to [0012] addresses the problem statement laid out by Ted and Harshad [1] i.e. we now have a libext2fs API for clone and merge of ext2_filsys struct. (ext2fs_clone_fs() & ext2fs_merge_fs())

4. These two changes [0016] & [0026] uses above clone/merge api for test and for use in e2fsck.

5. For a lot of libext2fs changes (for e.g. bitmap merging logic or clone/merge apis), there are some unit tests added for it's functionality verification.

Performance results:
=====================
1. On nvme with 22M 0-byte inodes: 7sec v/s 19sec
2. On nvme with 22M 4KB-32KB inodes: 17sec v/s 30sec
3. On nvme with 11M 32KB inodes: 9sec v/s 15sec
4. On RAID HDD setups: (will get back with perf results for this. Although, I had shared some numbers earlier, but I wanted to run pfsck with different FS layout and collect the numbers once again)

For review: Since these are large number of changes. So if the overall series looks good, then we can start reviewing changes in batches. For e.g. if we could review initial changes in libext2fs (from [001] - [0020]), that would be a good start.

Patch-0073 adds support to all tests/* with "-m 2" mode to test pfsck as well. If pthread support is present, make check then test with "-m 2" fsck mode option as well (for tests which utilizes run_e2fsck script).
This was mainly done for verifying that all existing tests passes with pfsck. In this only few tests are kept disabled by passing SKIP_MT=true (Note: Last min. I have decided to drop this patch from sending. Although this can still be found in the github branch [2] for any testing)

On Threadsan: I had tested this patch series against threadsan as well. It does lists few race conditions when running multi-threading (these doesn't looks to be any major problem). I have fixed a few [0070] and rest might either need some minor fixing or marking it false positive.

Here is the github link with all these commits (pfsck-RFCv1 branch) [2].

[1]: https://lore.kernel.org/linux-ext4/YMN10sXgoTR%[email protected]/
[2]: https://github.com/riteshharjani/e2fsprogs/commits/pfsck-RFCv1

Andreas Dilger (2):
libext2fs: Misc fixes for struct_ext2_filsys
e2fsck: misc cleanups for pfsck

Li Xi (16):
dblist: add dblist merge logic
libext2fs: Add flush cleanup API
libext2fs: merge icounts after thread finishes
e2fsck: add -m option for multithread
e2fsck: copy context when using multi-thread fsck
e2fsck: create logs for multi-threads
e2fsck: configure one pfsck thread
e2fsck: Add asserts in open_channel_fs
e2fsck: add start/end group for thread
e2fsck: split groups to different threads
e2fsck: print thread log properly
e2fsck: do not change global variables
e2fsck: optimize the inserting of dir_info_db
e2fsck: merge dir_info after thread finishes
e2fsck: merge icounts after thread finishes
e2fsck: add debug codes for multiple threads

Ritesh Harjani (IBM) (15):
e2fsck: Fix unbalanced mutex unlock for BOUNCE_MTX
gen_bitmaps: Fix ext2fs_compare_generic_bmap/bitmap logic
blkmap64_ba: Add common helper for bits size calculation
badblocks: Remove unused badblocks_flags
tst_badblocks: Add unit test to verify badblocks list merge api
tst_bitmaps_standalone: Add copy and merge bitmaps test
tst_bitmaps_pthread: Add merge bitmaps test using pthreads
tst_libext2fs_pthread: Add libext2fs merge/clone unit tests
e2fsck: Add e2fsck_pass1_thread_join return value
e2fsck: Use merge/clone apis of libext2fs
e2fsck: Fix io->align assert check
e2fsck: Fix double free of inodes_to_process
e2fsck: Fix and simplify update_mmp in case of pfsck
e2fsck: Make threads call log_out after pthread_join tests/f_multithread: Fix f_multithread related tests

Saranya Muruganandam (3):
libext2fs: dupfs: Add fs clone & merge api
e2fsck: propagate number of threads
e2fsck: Annotating fields in e2fsck_struct

Sebastien Buisson (1):
sec: support encrypted files handling in pfsck mode

Wang Shilong (35):
badblocks: Add badblocks merge logic
libext2fs: Add rbtree bitmap merge logic
libext2fs: Add bitmaps merge ops
libext2fs: merge quota context after threads finish
libext2fs: Add support for ext2fs_test_block_bitmap_range2_valid()
libext2fs: Add support to get average group count
libext2fs: avoid too much memory allocation in case fs_num_threads
e2fsck: Add e2fsck_pass1_merge_bitmap() api
e2fsck: rbtree bitmap for dir
e2fsck: merge counts after threads finish
e2fsck: merge dx_dir_info after threads finish
e2fsck: merge dirs_to_hash when threads finish
e2fsck: merge context flags properly
e2fsck: merge quota context after threads finish
e2fsck: serialize fix operations
e2fsck: move some fixes out of parallel pthreads
e2fsck: split and merge invalid bitmaps
e2fsck: merge EA blocks properly
e2fsck: kickoff mutex lock for block found map
e2fsck: allow admin specify number of threads
e2fsck: adjust number of threads
e2fsck: fix readahead for pfsck of pass1
e2fsck: merge options after threads finish
e2fsck: reset lost_and_found after threads finish
e2fsck: merge extent depth count after threads finish
e2fsck: simplify e2fsck context merging codes
e2fsck: set E2F_FLAG_ALLOC_OK after threads
e2fsck: wait fix thread finish before checking
e2fsck: cleanup e2fsck_pass1_thread_join()
e2fsck: make default smallest RA size to 1M
e2fsck: update mmp block in one thread
e2fsck: reset @inodes_to_rebuild if restart
tests: add pfsck test
e2fsck: fix memory leaks with pfsck enabled
e2fsck: merge casefolded dir lists after thread finish

 e2fsck/dirinfo.c                        |  238 ++-
 e2fsck/dx_dirinfo.c                     |   64 +
 e2fsck/e2fsck.8.in                      |    8 +-
 e2fsck/e2fsck.c                         |   11 +
 e2fsck/e2fsck.h                         |  103 +-
 e2fsck/encrypted_files.c                |  139 ++
 e2fsck/logfile.c                        |   13 +-
 e2fsck/pass1.c                          | 1894 +++++++++++++++++++----
 e2fsck/problem.c                        |   11 +
 e2fsck/problem.h                        |    3 +
 e2fsck/readahead.c                      |    4 +
 e2fsck/unix.c                           |   58 +-
 e2fsck/util.c                           |  193 ++-
 lib/ext2fs/Makefile.in                  |   53 +-
 lib/ext2fs/badblocks.c                  |   81 +-
 lib/ext2fs/bitmaps.c                    |   10 +
 lib/ext2fs/bitops.h                     |    2 +
 lib/ext2fs/blkmap64_ba.c                |   20 +-
 lib/ext2fs/blkmap64_rb.c                |   65 +
 lib/ext2fs/bmap64.h                     |    4 +
 lib/ext2fs/dblist.c                     |   38 +
 lib/ext2fs/dupfs.c                      |  183 +++
 lib/ext2fs/ext2_err.et.in               |    3 +
 lib/ext2fs/ext2_io.h                    |    2 +
 lib/ext2fs/ext2fs.h                     |   66 +-
 lib/ext2fs/ext2fsP.h                    |    1 -
 lib/ext2fs/gen_bitmap.c                 |    9 +-
 lib/ext2fs/gen_bitmap64.c               |   72 +-
 lib/ext2fs/icount.c                     |  107 ++
 lib/ext2fs/tst_badblocks.c              |   61 +-
 lib/ext2fs/tst_bitmaps_pthread.c        |  247 +++
 lib/ext2fs/tst_bitmaps_standalone.c     |  170 ++
 lib/ext2fs/tst_libext2fs_pthread.c      |  315 ++++
 lib/ext2fs/undo_io.c                    |   19 +
 lib/ext2fs/unix_io.c                    |   25 +-
 lib/support/mkquota.c                   |   39 +
 lib/support/quotaio.h                   |    3 +
 tests/f_itable_collision/expect.1       |    3 -
 tests/f_multithread/expect.1            |   25 +
 tests/f_multithread/expect.2            |    7 +
 tests/f_multithread/image.gz            |    1 +
 tests/f_multithread/name                |    1 +
 tests/f_multithread/script              |    4 +
 tests/f_multithread_completion/expect.1 |    2 +
 tests/f_multithread_completion/expect.2 |   23 +
 tests/f_multithread_completion/image.gz |    1 +
 tests/f_multithread_completion/name     |    1 +
 tests/f_multithread_completion/script   |    4 +
 tests/f_multithread_logfile/expect.1    |   25 +
 tests/f_multithread_logfile/image.gz    |    1 +
 tests/f_multithread_logfile/name        |    1 +
 tests/f_multithread_logfile/script      |   32 +
 tests/f_multithread_no/expect.1         |   26 +
 tests/f_multithread_no/expect.2         |   23 +
 tests/f_multithread_no/image.gz         |    1 +
 tests/f_multithread_no/name             |    1 +
 tests/f_multithread_no/script           |    4 +
 tests/f_multithread_ok/expect.1         |   15 +
 tests/f_multithread_ok/image.gz         |  Bin 0 -> 796311 bytes
 tests/f_multithread_ok/name             |    1 +
 tests/f_multithread_ok/script           |    4 +
 tests/f_multithread_preen/expect.1      |   11 +
 tests/f_multithread_preen/expect.2      |   23 +
 tests/f_multithread_preen/image.gz      |    1 +
 tests/f_multithread_preen/name          |    1 +
 tests/f_multithread_preen/script        |    4 +
 tests/f_multithread_yes/expect.1        |    2 +
 tests/f_multithread_yes/expect.2        |   23 +
 tests/f_multithread_yes/image.gz        |    1 +
 tests/f_multithread_yes/name            |    1 +
 tests/f_multithread_yes/script          |    4 +
 tests/test_one.in                       |    8 +
 72 files changed, 4186 insertions(+), 433 deletions(-)
 create mode 100644 lib/ext2fs/tst_bitmaps_pthread.c
 create mode 100644 lib/ext2fs/tst_bitmaps_standalone.c
 create mode 100644 lib/ext2fs/tst_libext2fs_pthread.c
 create mode 100644 tests/f_multithread/expect.1
 create mode 100644 tests/f_multithread/expect.2
 create mode 120000 tests/f_multithread/image.gz
 create mode 100644 tests/f_multithread/name
 create mode 100644 tests/f_multithread/script
 create mode 100644 tests/f_multithread_completion/expect.1
 create mode 100644 tests/f_multithread_completion/expect.2
 create mode 120000 tests/f_multithread_completion/image.gz
 create mode 100644 tests/f_multithread_completion/name
 create mode 100644 tests/f_multithread_completion/script
 create mode 100644 tests/f_multithread_logfile/expect.1
 create mode 120000 tests/f_multithread_logfile/image.gz
 create mode 100644 tests/f_multithread_logfile/name
 create mode 100644 tests/f_multithread_logfile/script
 create mode 100644 tests/f_multithread_no/expect.1
 create mode 100644 tests/f_multithread_no/expect.2
 create mode 120000 tests/f_multithread_no/image.gz
 create mode 100644 tests/f_multithread_no/name
 create mode 100644 tests/f_multithread_no/script
 create mode 100644 tests/f_multithread_ok/expect.1
 create mode 100644 tests/f_multithread_ok/image.gz
 create mode 100644 tests/f_multithread_ok/name
 create mode 100644 tests/f_multithread_ok/script
 create mode 100644 tests/f_multithread_preen/expect.1
 create mode 100644 tests/f_multithread_preen/expect.2
 create mode 120000 tests/f_multithread_preen/image.gz
 create mode 100644 tests/f_multithread_preen/name
 create mode 100644 tests/f_multithread_preen/script
 create mode 100644 tests/f_multithread_yes/expect.1
 create mode 100644 tests/f_multithread_yes/expect.2
 create mode 120000 tests/f_multithread_yes/image.gz
 create mode 100644 tests/f_multithread_yes/name
 create mode 100644 tests/f_multithread_yes/script
Signature · 1 line
2.37.3
#1 lore raw

RFC 1/72 e2fsck: Fix unbalanced mutex unlock for BOUNCE_MTX

f_crashdisk test failed with UNIX_IO_FORCE_BOUNCE=yes due to unbalanced mutex unlock in below path.

This patch fixes it.

---
 lib/ext2fs/unix_io.c | 1 -
 1 file changed, 1 deletion(-)
lib/ext2fs/unix_io.c +0 -1
index e53db333..5b894826 100644
@@ -305,7 +305,6 @@ bounce_read:
 	while (size > 0) {
 		actual = read(data->dev, data->bounce, align_size);
 		if (actual != align_size) {
-			mutex_unlock(data, BOUNCE_MTX);
 			actual = really_read;
 			buf -= really_read;
 			size += really_read;
Signature · 1 line
2.37.3
#9 lore raw

RFC 2/72 gen_bitmaps: Fix ext2fs_compare_generic_bmap/bitmap logic

In reply to Ritesh Harjani (IBM)

Currently this function was not correctly comparing against the right length of the bitmap. Also when we compare bitarray v/s rbtree bitmap the value returned by ext2fs_test_generic_bmap() could be different in these two implementations. Hence only check against boolean value.

---
 lib/ext2fs/gen_bitmap.c   |  9 ++++++---
 lib/ext2fs/gen_bitmap64.c | 10 +++++++---
 2 files changed, 13 insertions(+), 6 deletions(-)
lib/ext2fs/gen_bitmap.c +6 -3
index 1536d4b3..f7764fca 100644
@@ -385,10 +385,13 @@ errcode_t ext2fs_compare_generic_bitmap(errcode_t magic, errcode_t neq,
 		    (size_t) (bm1->end - bm1->start)/8)))
 		return neq;
 
-	for (i = bm1->end - ((bm1->end - bm1->start) % 8); i <= bm1->end; i++)
-		if (ext2fs_fast_test_block_bitmap(gen_bm1, i) !=
-		    ext2fs_fast_test_block_bitmap(gen_bm2, i))
+	for (i = bm1->start; i <= bm1->end; i++) {
+		int ret1, ret2;
+		ret1 = !!ext2fs_fast_test_block_bitmap(gen_bm1, i);
+		ret2 = !!ext2fs_fast_test_block_bitmap(gen_bm2, i);
+		if (ret1 != ret2)
 			return neq;
+	}
 
 	return 0;
 }
lib/ext2fs/gen_bitmap64.c +7 -3
index c860c10e..f7710afd 100644
@@ -629,10 +629,14 @@ errcode_t ext2fs_compare_generic_bmap(errcode_t neq,
 	    (bm1->end != bm2->end))
 		return neq;
 
-	for (i = bm1->end - ((bm1->end - bm1->start) % 8); i <= bm1->end; i++)
-		if (ext2fs_test_generic_bmap(gen_bm1, i) !=
-		    ext2fs_test_generic_bmap(gen_bm2, i))
+	for (i = bm1->start; i < bm1->end; i++) {
+		int ret1, ret2;
+		ret1 = !!ext2fs_test_generic_bmap(gen_bm1, i);
+		ret2 = !!ext2fs_test_generic_bmap(gen_bm2, i);
+		if (ret1 != ret2) {
 			return neq;
+		}
+	}
 
 	return 0;
 }
Signature · 1 line
2.37.3
#12 lore raw

RFC 3/72 blkmap64_ba: Add common helper for bits size calculation

In reply to Ritesh Harjani (IBM)

Just a quick common helper for bits size calculation.

---
 lib/ext2fs/blkmap64_ba.c | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)
lib/ext2fs/blkmap64_ba.c +13 -7
index 5d8f1548..4e7007f0 100644
@@ -40,6 +40,13 @@ struct ext2fs_ba_private_struct {
 
 typedef struct ext2fs_ba_private_struct *ext2fs_ba_private;
 
+#define ba_bits_size(start, end) ((((end) - (start)) / 8 + 1))
+
+static size_t ba_bitmap_size(ext2fs_generic_bitmap_64 bitmap)
+{
+	return (size_t) ba_bits_size(bitmap->start, bitmap->real_end);
+}
+
 static errcode_t ba_alloc_private_data (ext2fs_generic_bitmap_64 bitmap)
 {
 	ext2fs_ba_private bp;
@@ -56,7 +63,7 @@ static errcode_t ba_alloc_private_data (ext2fs_generic_bitmap_64 bitmap)
 	if (retval)
 		return retval;
 
-	size = (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1);
+	size = ba_bitmap_size(bitmap);
 
 	retval = ext2fs_get_mem(size, &bp->bitarray);
 	if (retval) {
@@ -80,7 +87,7 @@ static errcode_t ba_new_bmap(ext2_filsys fs EXT2FS_ATTR((unused)),
 		return retval;
 
 	bp = (ext2fs_ba_private) bitmap->private;
-	size = (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1);
+	size = ba_bitmap_size(bitmap);
 	memset(bp->bitarray, 0, size);
 
 	return 0;
@@ -115,7 +122,7 @@ static errcode_t ba_copy_bmap(ext2fs_generic_bitmap_64 src,
 
 	dest_bp = (ext2fs_ba_private) dest->private;
 
-	size = (size_t) (((src->real_end - src->start) / 8) + 1);
+	size = ba_bitmap_size(src);
 	memcpy (dest_bp->bitarray, src_bp->bitarray, size);
 
 	return 0;
@@ -145,8 +152,8 @@ static errcode_t ba_resize_bmap(ext2fs_generic_bitmap_64 bmap,
 		return 0;
 	}
 
-	size = ((bmap->real_end - bmap->start) / 8) + 1;
-	new_size = ((new_real_end - bmap->start) / 8) + 1;
+	size = ba_bitmap_size(bmap);
+	new_size = ba_bits_size(new_real_end, bmap->start);
 
 	if (size != new_size) {
 		retval = ext2fs_resize_mem(size, new_size, &bp->bitarray);
@@ -306,8 +313,7 @@ static void ba_clear_bmap(ext2fs_generic_bitmap_64 bitmap)
 {
 	ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private;
 
-	memset(bp->bitarray, 0,
-	       (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1));
+	memset(bp->bitarray, 0, ba_bitmap_size(bitmap));
 }
 
 #ifdef ENABLE_BMAP_STATS
Signature · 1 line
2.37.3
#14 lore raw

RFC 4/72 badblocks: Remove unused badblocks_flags

In reply to Ritesh Harjani (IBM)

badblocks_flags is not used anywhere. So just remove it.

---
 lib/ext2fs/badblocks.c | 6 +-----
 lib/ext2fs/ext2fsP.h   | 1 -
 2 files changed, 1 insertion(+), 6 deletions(-)
lib/ext2fs/badblocks.c +1 -5
index a306bc06..345168e0 100644
@@ -81,11 +81,7 @@ errcode_t ext2fs_u32_copy(ext2_u32_list src, ext2_u32_list *dest)
 {
 	errcode_t	retval;
 
-	retval = make_u32_list(src->size, src->num, src->list, dest);
-	if (retval)
-		return retval;
-	(*dest)->badblocks_flags = src->badblocks_flags;
-	return 0;
+	return make_u32_list(src->size, src->num, src->list, dest);
 }
 
 errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src,
lib/ext2fs/ext2fsP.h +0 -1
index a20a0502..d2045af8 100644
@@ -34,7 +34,6 @@ struct ext2_struct_u32_list {
 	int	num;
 	int	size;
 	__u32	*list;
-	int	badblocks_flags;
 };
 
 struct ext2_struct_u32_iterate {
Signature · 1 line
2.37.3
#16 lore raw

RFC 5/72 badblocks: Add badblocks merge logic

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

Add badblocks merge logic

---
 lib/ext2fs/badblocks.c | 75 ++++++++++++++++++++++++++++++++++++++++++
 lib/ext2fs/ext2fs.h    |  2 ++
 2 files changed, 77 insertions(+)
lib/ext2fs/badblocks.c +75 -0
index 345168e0..36794e69 100644
@@ -56,6 +56,74 @@ static errcode_t make_u32_list(int size, int num, __u32 *list,
 	return 0;
 }
 
+static inline int insert_ok(blk_t *array, int cnt, blk_t new)
+{
+   return (cnt == 0 || array[cnt - 1] != new);
+}
+
+/*
+ * Merge list from src to dest
+ */
+static errcode_t merge_u32_list(ext2_u32_list src, ext2_u32_list dest)
+{
+   errcode_t    retval;
+   int      src_count = src->num;
+   int      dest_count = dest->num;
+   int      size = src_count + dest_count;
+   int      size_entry = sizeof(blk_t);
+   blk_t       *array;
+   blk_t       *src_array = src->list;
+   blk_t       *dest_array = dest->list;
+   int      src_index = 0;
+   int      dest_index = 0;
+   int      uniq_cnt = 0;
+
+   if (src->num == 0)
+       return 0;
+
+   retval = ext2fs_get_array(size, size_entry, &array);
+   if (retval)
+       return retval;
+
+   /*
+    * It is possible that src list and dest list could be
+    * duplicated when merging badblocks.
+    */
+   while (src_index < src_count || dest_index < dest_count) {
+       if (src_index >= src_count) {
+           for (; dest_index < dest_count; dest_index++)
+               if (insert_ok(array, uniq_cnt, dest_array[dest_index]))
+                   array[uniq_cnt++] = dest_array[dest_index];
+           break;
+       }
+       if (dest_index >= dest_count) {
+           for (; src_index < src_count; src_index++)
+               if (insert_ok(array, uniq_cnt, src_array[src_index]))
+                   array[uniq_cnt++] = src_array[src_index];
+           break;
+       }
+       if (src_array[src_index] < dest_array[dest_index]) {
+           if (insert_ok(array, uniq_cnt, src_array[src_index]))
+               array[uniq_cnt++] = src_array[src_index];
+           src_index++;
+       } else if (src_array[src_index] > dest_array[dest_index]) {
+           if (insert_ok(array, uniq_cnt, dest_array[dest_index]))
+               array[uniq_cnt++] = dest_array[dest_index];
+           dest_index++;
+       } else {
+           if (insert_ok(array, uniq_cnt, dest_array[dest_index]))
+               array[uniq_cnt++] = dest_array[dest_index];
+           src_index++;
+           dest_index++;
+       }
+   }
+
+   ext2fs_free_mem(&dest->list);
+   dest->list = array;
+   dest->num = uniq_cnt;
+   dest->size = size;
+   return 0;
+}
 
 /*
  * This procedure creates an empty u32 list.
@@ -91,6 +159,13 @@ errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src,
 			       (ext2_u32_list *) dest);
 }
 
+errcode_t ext2fs_badblocks_merge(ext2_badblocks_list src,
+                ext2_badblocks_list dest)
+{
+   return merge_u32_list((ext2_u32_list) src,
+                 (ext2_u32_list) dest);
+}
+
 /*
  * This procedure frees a badblocks list.
  *
lib/ext2fs/ext2fs.h +2 -0
index 9cc994b1..18dddc2c 100644
@@ -845,6 +845,8 @@ extern int ext2fs_badblocks_list_iterate(ext2_badblocks_iterate iter,
 extern void ext2fs_badblocks_list_iterate_end(ext2_badblocks_iterate iter);
 extern errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src,
 				       ext2_badblocks_list *dest);
+extern errcode_t ext2fs_badblocks_merge(ext2_badblocks_list src,
+                   ext2_badblocks_list dest);
 extern int ext2fs_badblocks_equal(ext2_badblocks_list bb1,
 				  ext2_badblocks_list bb2);
 extern int ext2fs_u32_list_count(ext2_u32_list bb);
Signature · 1 line
2.37.3
#18 lore raw

RFC 6/72 dblist: add dblist merge logic

In reply to Ritesh Harjani (IBM)

From: Li Xi <[email protected]>

This adds dblist merge logic.

TODO: Add a unit test for core operations of dblist. Currently there is no such test for this.

---
 lib/ext2fs/dblist.c | 36 ++++++++++++++++++++++++++++++++++++
 lib/ext2fs/ext2fs.h |  1 +
 2 files changed, 37 insertions(+)
lib/ext2fs/dblist.c +36 -0
index bbdb221d..5568b8ec 100644
@@ -119,6 +119,42 @@ errcode_t ext2fs_copy_dblist(ext2_dblist src, ext2_dblist *dest)
 	return 0;
 }
 
+/*
+ * Merge a directory block list @src to @dest
+ */
+errcode_t ext2fs_merge_dblist(ext2_dblist src, ext2_dblist dest)
+{
+   unsigned long long src_count = src->count;
+   unsigned long long dest_count = dest->count;
+   unsigned long long size = src_count + dest_count;
+   size_t size_entry = sizeof(struct ext2_db_entry2);
+   struct ext2_db_entry2 *array, *array2;
+   errcode_t retval;
+
+   if (src_count == 0)
+       return 0;
+
+   if (src->sorted || (dest->sorted && dest_count != 0))
+       return EINVAL;
+
+   retval = ext2fs_get_array(size, size_entry, &array);
+   if (retval)
+       return retval;
+
+   array2 = array;
+   memcpy(array, src->list, src_count * size_entry);
+   array += src_count;
+   memcpy(array, dest->list, dest_count * size_entry);
+   ext2fs_free_mem(&dest->list);
+
+   dest->list = array2;
+   dest->count = src_count + dest_count;
+   dest->size = size;
+   dest->sorted = 0;
+
+   return 0;
+}
+
 /*
  * Close a directory block list
  *
lib/ext2fs/ext2fs.h +1 -0
index 18dddc2c..443f93d2 100644
@@ -1143,6 +1143,7 @@ extern errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ext2_ino_t ino,
 				      blk_t blk, int blockcnt);
 extern errcode_t ext2fs_add_dir_block2(ext2_dblist dblist, ext2_ino_t ino,
 				       blk64_t blk, e2_blkcnt_t blockcnt);
+extern errcode_t ext2fs_merge_dblist(ext2_dblist src, ext2_dblist dest);
 extern void ext2fs_dblist_sort(ext2_dblist dblist,
 			       EXT2_QSORT_TYPE (*sortfunc)(const void *,
 							   const void *));
Signature · 1 line
2.37.3
#20 lore raw

RFC 7/72 libext2fs: Add rbtree bitmap merge logic

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

This adds rbtree bitmap merge logic.

---
 lib/ext2fs/blkmap64_rb.c | 65 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)
lib/ext2fs/blkmap64_rb.c +65 -0
index 0df58dc7..d7c88aef 100644
@@ -977,11 +977,76 @@ static void rb_print_stats(ext2fs_generic_bitmap_64 bitmap EXT2FS_ATTR((unused))
 }
 #endif
 
+static errcode_t rb_merge_bmap(ext2fs_generic_bitmap_64 src,
+			       ext2fs_generic_bitmap_64 dest,
+			       ext2fs_generic_bitmap_64 dup,
+			       ext2fs_generic_bitmap_64 dup_allowed)
+{
+	struct ext2fs_rb_private *src_bp, *dest_bp, *dup_bp = NULL;
+	struct bmap_rb_extent *src_ext;
+	struct rb_node *src_node;
+	errcode_t retval = 0;
+	int dup_found = 0;
+	__u64 i;
+
+	src_bp = (struct ext2fs_rb_private *) src->private;
+	dest_bp = (struct ext2fs_rb_private *) dest->private;
+	if (dup)
+		dup_bp = (struct ext2fs_rb_private *)dup->private;
+	src_bp->rcursor = NULL;
+	dest_bp->rcursor = NULL;
+
+	src_node = ext2fs_rb_first(&src_bp->root);
+	while (src_node) {
+		src_ext = node_to_extent(src_node);
+		retval = rb_test_clear_bmap_extent(dest,
+					src_ext->start + src->start,
+					src_ext->count);
+		if (retval) {
+			rb_insert_extent(src_ext->start, src_ext->count,
+					 dest_bp);
+			goto next;
+		}
+
+		/* unlikely case, do it one by one block */
+		for (i = src_ext->start;
+		     i < src_ext->start + src_ext->count; i++) {
+			retval = rb_test_clear_bmap_extent(dest, i + src->start, 1);
+			if (retval) {
+				rb_insert_extent(i, 1, dest_bp);
+				continue;
+			}
+			if (dup_allowed) {
+				retval = rb_test_clear_bmap_extent(dup_allowed,
+							i + src->start, 1);
+				/* not existed in dup_allowed */
+				if (retval) {
+					dup_found = 1;
+					if (dup_bp)
+						rb_insert_extent(i, 1, dup_bp);
+				} /* else we conside it not duplicated */
+			} else {
+				if (dup_bp)
+					rb_insert_extent(i, 1, dup_bp);
+				dup_found = 1;
+			}
+		}
+next:
+		src_node = ext2fs_rb_next(src_node);
+	}
+
+	if (dup_found && dup)
+		return EEXIST;
+
+	return 0;
+}
+
 struct ext2_bitmap_ops ext2fs_blkmap64_rbtree = {
 	.type = EXT2FS_BMAP64_RBTREE,
 	.new_bmap = rb_new_bmap,
 	.free_bmap = rb_free_bmap,
 	.copy_bmap = rb_copy_bmap,
+	.merge_bmap = rb_merge_bmap,
 	.resize_bmap = rb_resize_bmap,
 	.mark_bmap = rb_mark_bmap,
 	.unmark_bmap = rb_unmark_bmap,
Signature · 1 line
2.37.3
#21 lore raw

RFC 8/72 libext2fs: Add bitmaps merge ops

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

[Note: splitted rb merge tree logic patch such that we seperate out libext2fs changes from e2fsck specific changes]

---
 lib/ext2fs/bitmaps.c      | 10 ++++++++++
 lib/ext2fs/bmap64.h       |  4 ++++
 lib/ext2fs/ext2fs.h       |  8 ++++++++
 lib/ext2fs/gen_bitmap64.c | 29 +++++++++++++++++++++++++++++
 4 files changed, 51 insertions(+)
lib/ext2fs/bitmaps.c +10 -0
index 8bfa24b1..9437331e 100644
@@ -45,6 +45,16 @@ errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src,
 {
 	return (ext2fs_copy_generic_bmap(src, dest));
 }
+
+errcode_t ext2fs_merge_bitmap(ext2fs_generic_bitmap src,
+			      ext2fs_generic_bitmap dest,
+			      ext2fs_generic_bitmap dup,
+			      ext2fs_generic_bitmap dup_allowed)
+{
+	return ext2fs_merge_generic_bmap(src, dest, dup,
+					 dup_allowed);
+}
+
 void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map)
 {
 	ext2fs_set_generic_bmap_padding(map);
lib/ext2fs/bmap64.h +4 -0
index de334548..555193ee 100644
@@ -72,6 +72,10 @@ struct ext2_bitmap_ops {
 	void	(*free_bmap)(ext2fs_generic_bitmap_64 bitmap);
 	errcode_t (*copy_bmap)(ext2fs_generic_bitmap_64 src,
 			     ext2fs_generic_bitmap_64 dest);
+	errcode_t (*merge_bmap)(ext2fs_generic_bitmap_64 src,
+				ext2fs_generic_bitmap_64 dest,
+				ext2fs_generic_bitmap_64 dup,
+				ext2fs_generic_bitmap_64 dup_allowed);
 	errcode_t (*resize_bmap)(ext2fs_generic_bitmap_64 bitmap,
 			       __u64 new_end,
 			       __u64 new_real_end);
lib/ext2fs/ext2fs.h +8 -0
index 443f93d2..54aed5d1 100644
@@ -870,6 +870,10 @@ extern void ext2fs_free_block_bitmap(ext2fs_block_bitmap bitmap);
 extern void ext2fs_free_inode_bitmap(ext2fs_inode_bitmap bitmap);
 extern errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src,
 				    ext2fs_generic_bitmap *dest);
+extern errcode_t ext2fs_merge_bitmap(ext2fs_generic_bitmap src,
+			      ext2fs_generic_bitmap dest,
+			      ext2fs_generic_bitmap dup,
+			      ext2fs_generic_bitmap dup_allowed);
 extern errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs,
 					      const char *descr,
 					      ext2fs_block_bitmap *ret);
@@ -1467,6 +1471,10 @@ void ext2fs_set_generic_bmap_padding(ext2fs_generic_bitmap bmap);
 errcode_t ext2fs_resize_generic_bmap(ext2fs_generic_bitmap bmap,
 				     __u64 new_end,
 				     __u64 new_real_end);
+errcode_t ext2fs_merge_generic_bmap(ext2fs_generic_bitmap gen_src,
+                                    ext2fs_generic_bitmap gen_dest,
+				    ext2fs_generic_bitmap gen_dup,
+				    ext2fs_generic_bitmap dup_allowed);
 errcode_t ext2fs_compare_generic_bmap(errcode_t neq,
 				      ext2fs_generic_bitmap bm1,
 				      ext2fs_generic_bitmap bm2);
lib/ext2fs/gen_bitmap64.c +29 -0
index f7710afd..c31f942f 100644
@@ -346,6 +346,35 @@ errcode_t ext2fs_copy_generic_bmap(ext2fs_generic_bitmap gen_src,
 	return 0;
 }
 
+errcode_t ext2fs_merge_generic_bmap(ext2fs_generic_bitmap gen_src,
+				    ext2fs_generic_bitmap gen_dest,
+				    ext2fs_generic_bitmap gen_dup,
+				    ext2fs_generic_bitmap gen_dup_allowed)
+{
+	ext2fs_generic_bitmap_64 src = (ext2fs_generic_bitmap_64)gen_src;
+	ext2fs_generic_bitmap_64 dest = (ext2fs_generic_bitmap_64)gen_dest;
+	ext2fs_generic_bitmap_64 dup = (ext2fs_generic_bitmap_64)gen_dup;
+	ext2fs_generic_bitmap_64 dup_allowed = (ext2fs_generic_bitmap_64)gen_dup_allowed;
+
+	if (!src || !dest)
+		return EINVAL;
+
+	if (!EXT2FS_IS_64_BITMAP(src) || !EXT2FS_IS_64_BITMAP(dest) ||
+	    (dup && !EXT2FS_IS_64_BITMAP(dup)) ||
+		(dup_allowed && !EXT2FS_IS_64_BITMAP(dup_allowed)))
+		return EINVAL;
+
+	if (src->bitmap_ops != dest->bitmap_ops ||
+	    (dup && src->bitmap_ops != dup->bitmap_ops) ||
+	    (dup_allowed && src->bitmap_ops != dup_allowed->bitmap_ops))
+		return EINVAL;
+
+	if (src->bitmap_ops->merge_bmap == NULL)
+		return EOPNOTSUPP;
+
+	return src->bitmap_ops->merge_bmap(src, dest, dup, dup_allowed);
+}
+
 errcode_t ext2fs_resize_generic_bmap(ext2fs_generic_bitmap gen_bmap,
 				     __u64 new_end,
 				     __u64 new_real_end)
Signature · 1 line
2.37.3
#23 lore raw

RFC 9/72 libext2fs: Add flush cleanup API

In reply to Ritesh Harjani (IBM)

From: Li Xi <[email protected]>

---
 lib/ext2fs/ext2_io.h |  2 ++
 lib/ext2fs/undo_io.c | 19 +++++++++++++++++++
 lib/ext2fs/unix_io.c | 24 +++++++++++++++++++++---
 3 files changed, 42 insertions(+), 3 deletions(-)
lib/ext2fs/ext2_io.h +2 -0
index 8fe5b323..8cc355be 100644
@@ -82,6 +82,7 @@ struct struct_io_manager {
 	errcode_t (*write_blk)(io_channel channel, unsigned long block,
 			       int count, const void *data);
 	errcode_t (*flush)(io_channel channel);
+	errcode_t (*flush_cleanup)(io_channel channel);
 	errcode_t (*write_byte)(io_channel channel, unsigned long offset,
 				int count, const void *data);
 	errcode_t (*set_option)(io_channel channel, const char *option,
@@ -116,6 +117,7 @@ struct struct_io_manager {
 #define io_channel_read_blk(c,b,n,d)	((c)->manager->read_blk((c),b,n,d))
 #define io_channel_write_blk(c,b,n,d)	((c)->manager->write_blk((c),b,n,d))
 #define io_channel_flush(c) 		((c)->manager->flush((c)))
+#define io_channel_flush_cleanup(c) 	((c)->manager->flush_cleanup((c)))
 #define io_channel_bumpcount(c)		((c)->refcount++)
 
 /* io_manager.c */
lib/ext2fs/undo_io.c +19 -0
index f4a6d526..678ff421 100644
@@ -1024,6 +1024,24 @@ static errcode_t undo_flush(io_channel channel)
 	return retval;
 }
 
+/*
+ * Flush data buffers to disk and cleanup the cache.
+ */
+static errcode_t undo_flush_cleanup(io_channel channel)
+{
+	errcode_t	retval = 0;
+	struct undo_private_data *data;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct undo_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+	if (data->real)
+		retval = io_channel_flush_cleanup(data->real);
+
+	return retval;
+}
+
 static errcode_t undo_set_option(io_channel channel, const char *option,
 				 const char *arg)
 {
@@ -1095,6 +1113,7 @@ static struct struct_io_manager struct_undo_manager = {
 	.read_blk	= undo_read_blk,
 	.write_blk	= undo_write_blk,
 	.flush		= undo_flush,
+	.flush_cleanup	= undo_flush_cleanup,
 	.write_byte	= undo_write_byte,
 	.set_option	= undo_set_option,
 	.get_stats	= undo_get_stats,
lib/ext2fs/unix_io.c +21 -3
index 5b894826..8f8118a3 100644
@@ -1173,9 +1173,9 @@ static errcode_t unix_write_byte(io_channel channel, unsigned long offset,
 }
 
 /*
- * Flush data buffers to disk.
+ * Flush data buffers to disk and invalidate cache if needed
  */
-static errcode_t unix_flush(io_channel channel)
+static errcode_t _unix_flush(io_channel channel, int invalidate)
 {
 	struct unix_private_data *data;
 	errcode_t retval = 0;
@@ -1185,7 +1185,7 @@ static errcode_t unix_flush(io_channel channel)
 	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
 
 #ifndef NO_IO_CACHE
-	retval = flush_cached_blocks(channel, data, 0);
+	retval = flush_cached_blocks(channel, data, invalidate);
 #endif
 #ifdef HAVE_FSYNC
 	if (!retval && fsync(data->dev) != 0)
@@ -1194,6 +1194,22 @@ static errcode_t unix_flush(io_channel channel)
 	return retval;
 }
 
+/*
+ * Flush data buffers to disk.
+ */
+static errcode_t unix_flush(io_channel channel)
+{
+	return _unix_flush(channel, 0);
+}
+
+/*
+ * Flush data buffers to disk and invalidate cache.
+ */
+static errcode_t unix_flush_cleanup(io_channel channel)
+{
+	return _unix_flush(channel, 1);
+}
+
 static errcode_t unix_set_option(io_channel channel, const char *option,
 				 const char *arg)
 {
@@ -1383,6 +1399,7 @@ static struct struct_io_manager struct_unix_manager = {
 	.discard	= unix_discard,
 	.cache_readahead	= unix_cache_readahead,
 	.zeroout	= unix_zeroout,
+	.flush_cleanup	= unix_flush_cleanup,
 };
 
 io_manager unix_io_manager = &struct_unix_manager;
@@ -1404,6 +1421,7 @@ static struct struct_io_manager struct_unixfd_manager = {
 	.discard	= unix_discard,
 	.cache_readahead	= unix_cache_readahead,
 	.zeroout	= unix_zeroout,
+	.flush_cleanup	= unix_flush_cleanup,
 };
 
 io_manager unixfd_io_manager = &struct_unixfd_manager;
Signature · 1 line
2.37.3
#25 lore raw

RFC 10/72 libext2fs: merge icounts after thread finishes

In reply to Ritesh Harjani (IBM)

From: Li Xi <[email protected]>

Merge inode_count and inode_link_info properly after threads finish.

[Note: splitted the patch to seperate libext2fs changes from e2fsck]
---
 lib/ext2fs/ext2fs.h |   1 +
 lib/ext2fs/icount.c | 103 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 104 insertions(+)
lib/ext2fs/ext2fs.h +1 -0
index 54aed5d1..139a25fc 100644
@@ -1546,6 +1546,7 @@ extern errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino,
 					 __u16 *ret);
 extern errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino,
 				     __u16 count);
+extern errcode_t ext2fs_icount_merge(ext2_icount_t src, ext2_icount_t dest);
 extern ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount);
 errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *);
 
lib/ext2fs/icount.c +103 -0

115 lines not on this page · raw message

Signature · 1 line
2.37.3
#27 lore raw

RFC 11/72 libext2fs: merge quota context after threads finish

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

Every threads calculate its own quota accounting,
merge them after threads finish.

[Note: splitted the patch to seperate libext2fs changes from e2fsck]
---
 lib/support/mkquota.c | 39 +++++++++++++++++++++++++++++++++++++++
 lib/support/quotaio.h |  3 +++
 2 files changed, 42 insertions(+)
lib/support/mkquota.c +39 -0
index 9339c994..ce1ab4cd 100644
@@ -618,6 +618,45 @@ out:
 	return err;
 }
 
+static errcode_t merge_usage(dict_t *dest, dict_t *src)
+{
+	dnode_t *n;
+	struct dquot *src_dq, *dest_dq;
+
+	for (n = dict_first(src); n; n = dict_next(src, n)) {
+		src_dq = dnode_get(n);
+		if (!src_dq)
+			continue;
+		dest_dq = get_dq(dest, src_dq->dq_id);
+		if (dest_dq == NULL)
+			return -ENOMEM;
+		dest_dq->dq_dqb.dqb_curspace += src_dq->dq_dqb.dqb_curspace;
+		dest_dq->dq_dqb.dqb_curinodes += src_dq->dq_dqb.dqb_curinodes;
+	}
+
+	return 0;
+}
+
+
+errcode_t quota_merge_and_update_usage(quota_ctx_t dest_qctx,
+					quota_ctx_t src_qctx)
+{
+	dict_t *dict;
+	enum quota_type	qtype;
+	errcode_t retval = 0;
+
+	for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+		dict = src_qctx->quota_dict[qtype];
+		if (!dict)
+			continue;
+		retval = merge_usage(dest_qctx->quota_dict[qtype], dict);
+		if (retval)
+			break;
+	}
+
+	return retval;
+}
+
 /*
  * Compares the measured quota in qctx->quota_dict with that in the quota inode
  * on disk and updates the limits in qctx->quota_dict. 'usage_inconsistent' is
lib/support/quotaio.h +3 -0
index 84fac35d..240a0762 100644
@@ -40,6 +40,7 @@
 #include "ext2fs/ext2_fs.h"
 #include "ext2fs/ext2fs.h"
 #include "dqblk_v2.h"
+#include "support/dict.h"
 
 typedef int64_t qsize_t;	/* Type in which we store size limitations */
 
@@ -236,6 +237,8 @@ int quota_file_exists(ext2_filsys fs, enum quota_type qtype);
 void quota_set_sb_inum(ext2_filsys fs, ext2_ino_t ino, enum quota_type qtype);
 errcode_t quota_compare_and_update(quota_ctx_t qctx, enum quota_type qtype,
 				   int *usage_inconsistent);
+errcode_t quota_merge_and_update_usage(quota_ctx_t dest_qctx,
+					quota_ctx_t src_qctx);
 int parse_quota_opts(const char *opts, int (*func)(char *));
 
 /* parse_qtype.c */
Signature · 1 line
2.37.3
#29 lore raw

RFC 12/72 libext2fs: dupfs: Add fs clone & merge api

In reply to Ritesh Harjani (IBM)

From: Saranya Muruganandam <[email protected]>

This patch mainly adds "parent" & "clone_flags" member in ext2_filsys struct for enabling multi-threading. Based on what CLONE flags will be passed from the client of libext2fs down to ext2fs_clone_fs(), those structures/bitmaps will be cloned (thread-aware child copy) and rest will be shared with the parent fs.

The same flags will also help to merge those cloned bitmap structures back into the parent bitmaps when ext2fs_merge_fs() will be called with childfs struct.

---
 lib/ext2fs/dupfs.c  | 183 ++++++++++++++++++++++++++++++++++++++++++++
 lib/ext2fs/ext2fs.h |  23 ++++++
 2 files changed, 206 insertions(+)
lib/ext2fs/dupfs.c +183 -0
index 02721e1a..ecc57cf7 100644
@@ -14,8 +14,12 @@
 #if HAVE_UNISTD_H
 #include <unistd.h>
 #endif
+#if HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
 #include <time.h>
 #include <string.h>
+#include <assert.h>
 
 #include "ext2_fs.h"
 #include "ext2fsP.h"
@@ -120,3 +124,182 @@ errout:
 
 }
 
+#ifdef HAVE_PTHREAD
+errcode_t ext2fs_clone_fs(ext2_filsys fs, ext2_filsys *dest, unsigned int flags)
+{
+	errcode_t retval;
+	ext2_filsys childfs;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &childfs);
+	if (retval)
+		return retval;
+
+	/* make an exact copy implying lists and memory structures are shared */
+	memcpy(childfs, fs, sizeof(struct struct_ext2_filsys));
+	childfs->inode_map = NULL;
+	childfs->block_map = NULL;
+	childfs->badblocks = NULL;
+	childfs->dblist = NULL;
+
+	pthread_mutex_lock(&fs->refcount_mutex);
+	fs->refcount++;
+	pthread_mutex_unlock(&fs->refcount_mutex);
+
+	if ((flags & EXT2FS_CLONE_INODE) && fs->inode_map) {
+		retval = ext2fs_copy_bitmap(fs->inode_map, &childfs->inode_map);
+		if (retval)
+			return retval;
+		childfs->inode_map->fs = childfs;
+	}
+
+	if ((flags & EXT2FS_CLONE_BLOCK) && fs->block_map) {
+		retval = ext2fs_copy_bitmap(fs->block_map, &childfs->block_map);
+		if (retval)
+			return retval;
+		childfs->block_map->fs = childfs;
+	}
+
+	if ((flags & EXT2FS_CLONE_BADBLOCKS) && fs->badblocks) {
+		retval = ext2fs_badblocks_copy(fs->badblocks, &childfs->badblocks);
+		if (retval)
+			return retval;
+	}
+
+	if ((flags & EXT2FS_CLONE_DBLIST) && fs->dblist) {
+		retval = ext2fs_copy_dblist(fs->dblist, &childfs->dblist);
+		if (retval)
+			return retval;
+		childfs->dblist->fs = childfs;
+	}
+
+	/* icache when NULL will be rebuilt if needed */
+	childfs->icache = NULL;
+
+	childfs->clone_flags = flags;
+	childfs->parent = fs;
+	*dest = childfs;
+
+	return 0;
+}
+
+errcode_t ext2fs_merge_fs(ext2_filsys *thread_fs)
+{
+	ext2_filsys fs = *thread_fs;
+	errcode_t retval = 0;
+	ext2_filsys dest = fs->parent;
+	ext2_filsys src = fs;
+	unsigned int flags = fs->clone_flags;
+	struct ext2_inode_cache *icache;
+	io_channel dest_io;
+	io_channel dest_image_io;
+	ext2fs_inode_bitmap inode_map;
+	ext2fs_block_bitmap block_map;
+	ext2_badblocks_list badblocks;
+	ext2_dblist dblist;
+	void *priv_data;
+	int fsflags;
+
+	pthread_mutex_lock(&fs->refcount_mutex);
+	fs->refcount--;
+	assert(fs->refcount >= 0);
+	pthread_mutex_unlock(&fs->refcount_mutex);
+
+	icache = dest->icache;
+	dest_io = dest->io;
+	dest_image_io = dest->image_io;
+	inode_map = dest->inode_map;
+	block_map = dest->block_map;
+	badblocks = dest->badblocks;
+	dblist = dest->dblist;
+	priv_data = dest->priv_data;
+	fsflags = dest->flags;
+
+	memcpy(dest, src, sizeof(struct struct_ext2_filsys));
+
+	dest->io = dest_io;
+	dest->image_io = dest_image_io;
+	dest->icache = icache;
+	dest->inode_map = inode_map;
+	dest->block_map = block_map;
+	dest->badblocks = badblocks;
+	dest->dblist = dblist;
+	dest->priv_data = priv_data;
+	if (dest->dblist)
+		dest->dblist->fs = dest;
+	dest->flags = src->flags | fsflags;
+	if (!(src->flags & EXT2_FLAG_VALID) || !(dest->flags & EXT2_FLAG_VALID))
+		ext2fs_unmark_valid(dest);
+
+	if ((flags & EXT2FS_CLONE_INODE) && src->inode_map) {
+		if (dest->inode_map == NULL) {
+			dest->inode_map = src->inode_map;
+			src->inode_map = NULL;
+		} else {
+			retval = ext2fs_merge_bitmap(src->inode_map, dest->inode_map, NULL, NULL);
+			if (retval)
+				goto out;
+		}
+		dest->inode_map->fs = dest;
+	}
+
+	if ((flags & EXT2FS_CLONE_BLOCK) && src->block_map) {
+		if (dest->block_map == NULL) {
+			dest->block_map = src->block_map;
+			src->block_map = NULL;
+		} else {
+			retval = ext2fs_merge_bitmap(src->block_map, dest->block_map, NULL, NULL);
+			if (retval)
+				goto out;
+		}
+		dest->block_map->fs = dest;
+	}
+
+	if ((flags & EXT2FS_CLONE_BADBLOCKS) && src->badblocks) {
+		if (dest->badblocks == NULL)
+			retval = ext2fs_badblocks_copy(src->badblocks, &dest->badblocks);
+		else
+			retval = ext2fs_badblocks_merge(src->badblocks, dest->badblocks);
+		if (retval)
+			goto out;
+	}
+
+	if ((flags & EXT2FS_CLONE_DBLIST) && src->dblist) {
+		if (dest->dblist == NULL) {
+			dest->dblist = src->dblist;
+			src->dblist = NULL;
+		} else {
+			retval = ext2fs_merge_dblist(src->dblist, dest->dblist);
+			if (retval)
+				goto out;
+		}
+		dest->dblist->fs = dest;
+	}
+
+	if (src->icache) {
+		ext2fs_free_inode_cache(src->icache);
+		src->icache = NULL;
+	}
+
+out:
+	if (src->io)
+		io_channel_close(src->io);
+
+	if ((flags & EXT2FS_CLONE_INODE) && src->inode_map)
+		ext2fs_free_generic_bmap(src->inode_map);
+	if ((flags & EXT2FS_CLONE_BLOCK) && src->block_map)
+		ext2fs_free_generic_bmap(src->block_map);
+	if ((flags & EXT2FS_CLONE_BADBLOCKS) && src->badblocks)
+		ext2fs_badblocks_list_free(src->badblocks);
+	if ((flags & EXT2FS_CLONE_DBLIST) && src->dblist) {
+		ext2fs_free_dblist(src->dblist);
+		src->dblist = NULL;
+	}
+
+	ext2fs_free_mem(&src);
+	*thread_fs = NULL;
+
+	return retval;
+}
+#endif
lib/ext2fs/ext2fs.h +23 -0
index 139a25fc..b1505f95 100644
@@ -12,6 +12,10 @@
 #ifndef _EXT2FS_EXT2FS_H
 #define _EXT2FS_EXT2FS_H
 
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
 #ifdef __GNUC__
 #define EXT2FS_ATTR(x) __attribute__(x)
 #else
@@ -331,6 +335,13 @@ struct struct_ext2_filsys {
 	struct ext2fs_hashmap* block_sha_map;
 
 	const struct ext2fs_nls_table *encoding;
+
+#ifdef HAVE_PTHREAD
+	struct struct_ext2_filsys *parent;
+	size_t refcount;
+	pthread_mutex_t refcount_mutex;
+	unsigned int clone_flags;
+#endif
 };
 
 #if EXT2_FLAT_INCLUDES
@@ -1057,6 +1068,18 @@ extern errcode_t ext2fs_move_blocks(ext2_filsys fs,
 /* check_desc.c */
 extern errcode_t ext2fs_check_desc(ext2_filsys fs);
 
+#ifdef HAVE_PTHREAD
+/* flags for ext2fs_clone_fs */
+#define EXT2FS_CLONE_BLOCK 			0x0001
+#define EXT2FS_CLONE_INODE 			0x0002
+#define EXT2FS_CLONE_BADBLOCKS 		0x0004
+#define EXT2FS_CLONE_DBLIST			0x0008
+
+extern errcode_t ext2fs_clone_fs(ext2_filsys fs, ext2_filsys *dest,
+								 unsigned int flags);
+extern errcode_t ext2fs_merge_fs(ext2_filsys *fs);
+#endif
+
 /* closefs.c */
 extern errcode_t ext2fs_close(ext2_filsys fs);
 extern errcode_t ext2fs_close2(ext2_filsys fs, int flags);
Signature · 1 line
2.37.3
#32 lore raw

RFC 13/72 tst_badblocks: Add unit test to verify badblocks list merge api

In reply to Ritesh Harjani (IBM)

Add unit test to verify badblocks list merge api i.e. ext2fs_badblocks_merge()

---
 lib/ext2fs/tst_badblocks.c | 61 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 59 insertions(+), 2 deletions(-)
lib/ext2fs/tst_badblocks.c +59 -2
index b6e766ab..946de0ae 100644
@@ -119,6 +119,40 @@ static void print_list(badblocks_list bb, int verify)
 	}
 }
 
+static void do_list_merge_verify(badblocks_list bb, badblocks_list bbm, int verify)
+{
+	errcode_t retval;
+	badblocks_iterate iter;
+	blk_t blk;
+	int i, ok;
+
+	retval = ext2fs_badblocks_merge(bb, bbm);
+	if (retval) {
+		com_err("do_list_merge_verify", retval, "while doing list merge");
+		return;
+	}
+
+	if (!verify)
+		return;
+
+	retval = ext2fs_badblocks_list_iterate_begin(bb, &iter);
+	if (retval) {
+		com_err("do_list_merge_verify", retval, "while setting up iterator");
+		return;
+	}
+
+	while (ext2fs_badblocks_list_iterate(iter, &blk)) {
+		retval = ext2fs_badblocks_list_test(bbm, blk);
+		if (retval == 0) {
+			printf(" --- NOT OK\n");
+			test_fail++;
+			return;
+		}
+	}
+	ext2fs_badblocks_list_iterate_end(iter);
+	printf(" --- OK\n");
+}
+
 static void validate_test_seq(badblocks_list bb, blk_t *vec)
 {
 	int	i, match, ok;
@@ -275,13 +309,13 @@ out:
 
 int main(int argc, char **argv)
 {
-	badblocks_list bb1, bb2, bb3, bb4, bb5;
+	badblocks_list bb1, bb2, bb3, bb4, bb5, bbm;
 	int	equal;
 	errcode_t	retval;
 
 	add_error_table(&et_ext2_error_table);
 
-	bb1 = bb2 = bb3 = bb4 = bb5 = 0;
+	bb1 = bb2 = bb3 = bb4 = bb5 = bbm = 0;
 
 	printf("test1: ");
 	retval = create_test_list(test1, &bb1);
@@ -346,6 +380,27 @@ int main(int argc, char **argv)
 		printf("\n");
 	}
 
+	printf("Create merge bb list\n");
+	retval = ext2fs_badblocks_list_create(&bbm, 5);
+	if (retval) {
+		com_err("ext2fs_badblocks_list_create", retval, "while creating list");
+		test_fail++;
+	}
+
+	printf("Merge & Verify all bb{1..5} into bbm\n");
+	if (bb1 && bb2 && bb3 && bb4 && bb5 && bbm) {
+		printf("Merge bb1 into bbm");
+		do_list_merge_verify(bb1, bbm, 1);
+		printf("Merge bb2 into bbm");
+		do_list_merge_verify(bb2, bbm, 1);
+		printf("Merge bb3 into bbm");
+		do_list_merge_verify(bb3, bbm, 1);
+		printf("Merge bb4 into bbm");
+		do_list_merge_verify(bb4, bbm, 1);
+		printf("Merge bb5 into bbm");
+		do_list_merge_verify(bb5, bbm, 1);
+	}
+
 	file_test(bb4);
 
 	file_test_invalid(bb4);
@@ -363,6 +418,8 @@ int main(int argc, char **argv)
 		ext2fs_badblocks_list_free(bb4);
 	if (bb5)
 		ext2fs_badblocks_list_free(bb5);
+	if (bbm)
+		ext2fs_badblocks_list_free(bbm);
 
 	return test_fail;
 
Signature · 1 line
2.37.3
#34 lore raw

RFC 14/72 tst_bitmaps_standalone: Add copy and merge bitmaps test

In reply to Ritesh Harjani (IBM)

This adds a basic copy and merge api test for rbtree bitmap type.

---
 lib/ext2fs/Makefile.in              |  25 +++-
 lib/ext2fs/tst_bitmaps_standalone.c | 170 ++++++++++++++++++++++++++++
 2 files changed, 189 insertions(+), 6 deletions(-)
 create mode 100644 lib/ext2fs/tst_bitmaps_standalone.c
lib/ext2fs/Makefile.in +19 -6
index f6a050a2..1692500e 100644
@@ -227,6 +227,7 @@ SRCS= ext2_err.c \
 	$(srcdir)/write_bb_file.c \
 	$(srcdir)/rbtree.c \
 	$(srcdir)/tst_libext2fs.c \
+	$(srcdir)/tst_bitmaps_standalone.c \
 	$(DEBUG_SRCS)
 
 HFILES= bitops.h ext2fs.h ext2_io.h ext2_fs.h ext2_ext_attr.h ext3_extents.h \
@@ -328,9 +329,9 @@ tst_getsectsize: tst_getsectsize.o getsectsize.o $(STATIC_LIBEXT2FS) \
 		$(ALL_LDFLAGS) $(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR) \
 		$(SYSLIBS)
 
-tst_types.o: $(srcdir)/tst_types.c ext2_types.h 
+tst_types.o: $(srcdir)/tst_types.c ext2_types.h
 
-tst_types: tst_types.o ext2_types.h 
+tst_types: tst_types.o ext2_types.h
 	$(E) "	LD $@"
 	$(Q) $(CC) -o tst_types tst_types.o $(ALL_LDFLAGS) $(SYSLIBS)
 
@@ -362,6 +363,11 @@ tst_sha512: $(srcdir)/sha512.c $(srcdir)/ext2_fs.h
 	$(Q) $(CC) $(ALL_LDFLAGS) $(ALL_CFLAGS) -o tst_sha512 \
 		$(srcdir)/sha512.c -DUNITTEST $(SYSLIBS)
 
+tst_bitmaps_standalone: tst_bitmaps_standalone.o $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
+	$(E) "	LD $@"
+	$(Q) $(CC) -o tst_bitmaps_standalone tst_bitmaps_standalone.o $(ALL_LDFLAGS) \
+		$(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR) $(SYSLIBS)
+
 ext2_tdbtool: tdbtool.o
 	$(E) "	LD $@"
 	$(Q) $(CC) -o ext2_tdbtool tdbtool.o tdb.o $(ALL_LDFLAGS) $(SYSLIBS)
@@ -533,7 +539,7 @@ mkjournal: mkjournal.c $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR)
 fullcheck check:: tst_bitops tst_badblocks tst_iscan tst_types tst_icount \
     tst_super_size tst_types tst_inode_size tst_csum tst_crc32c tst_bitmaps \
     tst_inline tst_inline_data tst_libext2fs tst_sha256 tst_sha512 \
-    tst_digest_encode tst_getsize tst_getsectsize
+    tst_digest_encode tst_getsize tst_getsectsize tst_bitmaps_standalone
 	$(TESTENV) ./tst_bitops
 	$(TESTENV) ./tst_badblocks
 	$(TESTENV) ./tst_iscan
@@ -556,6 +562,7 @@ fullcheck check:: tst_bitops tst_badblocks tst_iscan tst_types tst_icount \
 	$(TESTENV) ./tst_bitmaps -l -f $(srcdir)/tst_bitmaps_cmds > tst_bitmaps_out
 	diff $(srcdir)/tst_bitmaps_exp tst_bitmaps_out
 	$(TESTENV) ./tst_digest_encode
+	$(TESTENV) ./tst_bitmaps_standalone
 
 installdirs::
 	$(E) "	MKDIR_P $(libdir) $(includedir)/ext2fs"
@@ -581,7 +588,7 @@ install:: all $(HFILES) $(HFILES_IN) installdirs ext2fs.pc
 uninstall::
 	$(RM) -f $(DESTDIR)$(libdir)/libext2fs.a \
 		$(DESTDIR)$(pkgconfigdir)/ext2fs.pc
-	$(RM) -rf $(DESTDIR)$(includedir)/ext2fs 
+	$(RM) -rf $(DESTDIR)$(includedir)/ext2fs
 
 clean::
 	$(RM) -f \#* *.s *.o *.a *~ *.bak core profiled/* \
@@ -590,7 +597,7 @@ clean::
 		tst_bitops tst_types tst_icount tst_super_size tst_csum \
 		tst_bitmaps tst_bitmaps_out tst_extents tst_inline \
 		tst_inline_data tst_inode_size tst_bitmaps_cmd.c \
-		tst_digest_encode tst_sha256 tst_sha512 \
+		tst_digest_encode tst_sha256 tst_sha512  tst_bitmaps_standalone \
 		ext2_tdbtool mkjournal debug_cmds.c tst_cmds.c extent_cmds.c \
 		../libext2fs.a ../libext2fs_p.a ../libext2fs_chk.a \
 		crc32c_table.h gen_crc32ctable tst_crc32c tst_libext2fs \
@@ -646,7 +653,7 @@ windows_io.o: $(srcdir)/windows_io.c $(top_builddir)/lib/config.h \
  $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h $(srcdir)/ext2fsP.h
 
 # +++ Dependency line eater +++
-# 
+#
 # Makefile dependencies follow.  This must be the last section in
 # the Makefile.in file
 #
@@ -1156,6 +1163,12 @@ tst_iscan.o: $(srcdir)/tst_iscan.c $(top_builddir)/lib/config.h \
  $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
  $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
  $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+tst_bitmaps_standalone.o: $(srcdir)/tst_bitmaps_standalone.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
 undo_io.o: $(srcdir)/undo_io.c $(top_builddir)/lib/config.h \
  $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
  $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
lib/ext2fs/tst_bitmaps_standalone.c new file +170 -0

170 lines not on this page · raw message

Signature · 1 line
2.37.3
#37 lore raw

RFC 15/72 tst_bitmaps_pthread: Add merge bitmaps test using pthreads

In reply to Ritesh Harjani (IBM)

This patch adds a test to verify the core bitmaps merge APIs for rbtree bitmap type.

---
 lib/ext2fs/Makefile.in           |  17 ++-
 lib/ext2fs/tst_bitmaps_pthread.c | 247 +++++++++++++++++++++++++++++++
 2 files changed, 263 insertions(+), 1 deletion(-)
 create mode 100644 lib/ext2fs/tst_bitmaps_pthread.c
lib/ext2fs/Makefile.in +16 -1
index 1692500e..c0694175 100644
@@ -228,6 +228,7 @@ SRCS= ext2_err.c \
 	$(srcdir)/rbtree.c \
 	$(srcdir)/tst_libext2fs.c \
 	$(srcdir)/tst_bitmaps_standalone.c \
+	$(srcdir)/tst_bitmaps_pthread.c \
 	$(DEBUG_SRCS)
 
 HFILES= bitops.h ext2fs.h ext2_io.h ext2_fs.h ext2_ext_attr.h ext3_extents.h \
@@ -368,6 +369,11 @@ tst_bitmaps_standalone: tst_bitmaps_standalone.o $(STATIC_LIBEXT2FS) $(DEPSTATIC
 	$(Q) $(CC) -o tst_bitmaps_standalone tst_bitmaps_standalone.o $(ALL_LDFLAGS) \
 		$(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR) $(SYSLIBS)
 
+tst_bitmaps_pthread: tst_bitmaps_pthread.o $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
+	$(E) "	LD $@"
+	$(Q) $(CC) -o tst_bitmaps_pthread tst_bitmaps_pthread.o $(ALL_LDFLAGS) \
+		$(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR) $(SYSLIBS)
+
 ext2_tdbtool: tdbtool.o
 	$(E) "	LD $@"
 	$(Q) $(CC) -o ext2_tdbtool tdbtool.o tdb.o $(ALL_LDFLAGS) $(SYSLIBS)
@@ -539,7 +545,8 @@ mkjournal: mkjournal.c $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR)
 fullcheck check:: tst_bitops tst_badblocks tst_iscan tst_types tst_icount \
     tst_super_size tst_types tst_inode_size tst_csum tst_crc32c tst_bitmaps \
     tst_inline tst_inline_data tst_libext2fs tst_sha256 tst_sha512 \
-    tst_digest_encode tst_getsize tst_getsectsize tst_bitmaps_standalone
+    tst_digest_encode tst_getsize tst_getsectsize tst_bitmaps_standalone \
+	tst_bitmaps_pthread
 	$(TESTENV) ./tst_bitops
 	$(TESTENV) ./tst_badblocks
 	$(TESTENV) ./tst_iscan
@@ -563,6 +570,7 @@ fullcheck check:: tst_bitops tst_badblocks tst_iscan tst_types tst_icount \
 	diff $(srcdir)/tst_bitmaps_exp tst_bitmaps_out
 	$(TESTENV) ./tst_digest_encode
 	$(TESTENV) ./tst_bitmaps_standalone
+	$(TESTENV) ./tst_bitmaps_pthread
 
 installdirs::
 	$(E) "	MKDIR_P $(libdir) $(includedir)/ext2fs"
@@ -598,6 +606,7 @@ clean::
 		tst_bitmaps tst_bitmaps_out tst_extents tst_inline \
 		tst_inline_data tst_inode_size tst_bitmaps_cmd.c \
 		tst_digest_encode tst_sha256 tst_sha512  tst_bitmaps_standalone \
+		tst_bitmaps_pthread \
 		ext2_tdbtool mkjournal debug_cmds.c tst_cmds.c extent_cmds.c \
 		../libext2fs.a ../libext2fs_p.a ../libext2fs_chk.a \
 		crc32c_table.h gen_crc32ctable tst_crc32c tst_libext2fs \
@@ -1169,6 +1178,12 @@ tst_bitmaps_standalone.o: $(srcdir)/tst_bitmaps_standalone.c $(top_builddir)/lib
  $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
  $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
  $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+tst_bitmaps_pthread.o: $(srcdir)/tst_bitmaps_pthread.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
 undo_io.o: $(srcdir)/undo_io.c $(top_builddir)/lib/config.h \
  $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
  $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
lib/ext2fs/tst_bitmaps_pthread.c new file +247 -0

247 lines not on this page · raw message

Signature · 1 line
2.37.3
#39 lore raw

RFC 16/72 tst_libext2fs_pthread: Add libext2fs merge/clone unit tests

In reply to Ritesh Harjani (IBM)

This adds a unit tests for libext2fs merge/clone apis and uses pthreads to test the functionality correctly.

TODO:
We can also add EXT2FS_CLONE_BADBLOCKS and EXT2FS_CLONE_DBLIST test as well into it.

---
 lib/ext2fs/Makefile.in             |  17 +-
 lib/ext2fs/tst_libext2fs_pthread.c | 315 +++++++++++++++++++++++++++++
 2 files changed, 330 insertions(+), 2 deletions(-)
 create mode 100644 lib/ext2fs/tst_libext2fs_pthread.c
lib/ext2fs/Makefile.in +15 -2
index c0694175..5fde9900 100644
@@ -229,6 +229,7 @@ SRCS= ext2_err.c \
 	$(srcdir)/tst_libext2fs.c \
 	$(srcdir)/tst_bitmaps_standalone.c \
 	$(srcdir)/tst_bitmaps_pthread.c \
+	$(srcdir)/tst_libext2fs_pthread.c \
 	$(DEBUG_SRCS)
 
 HFILES= bitops.h ext2fs.h ext2_io.h ext2_fs.h ext2_ext_attr.h ext3_extents.h \
@@ -374,6 +375,11 @@ tst_bitmaps_pthread: tst_bitmaps_pthread.o $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCO
 	$(Q) $(CC) -o tst_bitmaps_pthread tst_bitmaps_pthread.o $(ALL_LDFLAGS) \
 		$(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR) $(SYSLIBS)
 
+tst_libext2fs_pthread: tst_libext2fs_pthread.o $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
+	$(E) "	LD $@"
+	$(Q) $(CC) -o tst_libext2fs_pthread tst_libext2fs_pthread.o $(ALL_LDFLAGS) \
+		$(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR) $(SYSLIBS)
+
 ext2_tdbtool: tdbtool.o
 	$(E) "	LD $@"
 	$(Q) $(CC) -o ext2_tdbtool tdbtool.o tdb.o $(ALL_LDFLAGS) $(SYSLIBS)
@@ -546,7 +552,7 @@ fullcheck check:: tst_bitops tst_badblocks tst_iscan tst_types tst_icount \
     tst_super_size tst_types tst_inode_size tst_csum tst_crc32c tst_bitmaps \
     tst_inline tst_inline_data tst_libext2fs tst_sha256 tst_sha512 \
     tst_digest_encode tst_getsize tst_getsectsize tst_bitmaps_standalone \
-	tst_bitmaps_pthread
+	tst_bitmaps_pthread tst_libext2fs_pthread
 	$(TESTENV) ./tst_bitops
 	$(TESTENV) ./tst_badblocks
 	$(TESTENV) ./tst_iscan
@@ -571,6 +577,7 @@ fullcheck check:: tst_bitops tst_badblocks tst_iscan tst_types tst_icount \
 	$(TESTENV) ./tst_digest_encode
 	$(TESTENV) ./tst_bitmaps_standalone
 	$(TESTENV) ./tst_bitmaps_pthread
+	$(TESTENV) ./tst_libext2fs_pthread
 
 installdirs::
 	$(E) "	MKDIR_P $(libdir) $(includedir)/ext2fs"
@@ -606,7 +613,7 @@ clean::
 		tst_bitmaps tst_bitmaps_out tst_extents tst_inline \
 		tst_inline_data tst_inode_size tst_bitmaps_cmd.c \
 		tst_digest_encode tst_sha256 tst_sha512  tst_bitmaps_standalone \
-		tst_bitmaps_pthread \
+		tst_bitmaps_pthread tst_libext2fs_pthread \
 		ext2_tdbtool mkjournal debug_cmds.c tst_cmds.c extent_cmds.c \
 		../libext2fs.a ../libext2fs_p.a ../libext2fs_chk.a \
 		crc32c_table.h gen_crc32ctable tst_crc32c tst_libext2fs \
@@ -1184,6 +1191,12 @@ tst_bitmaps_pthread.o: $(srcdir)/tst_bitmaps_pthread.c $(top_builddir)/lib/confi
  $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
  $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
  $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+tst_libext2fs_pthread.o: $(srcdir)/tst_libext2fs_pthread.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
 undo_io.o: $(srcdir)/undo_io.c $(top_builddir)/lib/config.h \
  $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
  $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
lib/ext2fs/tst_libext2fs_pthread.c new file +315 -0

315 lines not on this page · raw message

Signature · 1 line
2.37.3
#41 lore raw

RFC 17/72 libext2fs: Add support for ext2fs_test_block_bitmap_range2_valid()

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

This adds the support in libext2fs to query whether the block range is valid or not (within range) given the block bitmap.
Also to avoid duplicate warning messages in case of invalid blocks.

This will be later used in pass1 of e2fsck is_blocks_used() function to check whether the given block range is valid or not to avoid duplicate warning resulting from ext2fs_test_block_bitmap_range2()

---
 lib/ext2fs/bitops.h       |  2 ++
 lib/ext2fs/gen_bitmap64.c | 33 +++++++++++++++++++++++++++++++++
 2 files changed, 35 insertions(+)
lib/ext2fs/bitops.h +2 -0
index 505b3c9c..1facc8dd 100644
@@ -120,6 +120,8 @@ extern int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap,
 extern void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map);
 extern __u32 ext2fs_get_generic_bitmap_start(ext2fs_generic_bitmap bitmap);
 extern __u32 ext2fs_get_generic_bitmap_end(ext2fs_generic_bitmap bitmap);
+extern int ext2fs_test_block_bitmap_range2_valid(ext2fs_block_bitmap bitmap,
+						blk64_t block, unsigned int num);
 
 /* 64-bit versions */
 
lib/ext2fs/gen_bitmap64.c +33 -0
index c31f942f..a9637cb5 100644
@@ -731,6 +731,39 @@ int ext2fs_test_block_bitmap_range2(ext2fs_block_bitmap gen_bmap,
 	return bmap->bitmap_ops->test_clear_bmap_extent(bmap, block, num);
 }
 
+int ext2fs_test_block_bitmap_range2_valid(ext2fs_block_bitmap bitmap,
+					  blk64_t block, unsigned int num)
+{
+	ext2fs_generic_bitmap_64 bmap = (ext2fs_generic_bitmap_64)bitmap;
+	__u64	end = block + num;
+
+	if (!bmap)
+		return 0;
+
+	if (EXT2FS_IS_32_BITMAP(bmap)) {
+		if ((block & ~0xffffffffULL) ||
+		    ((block+num-1) & ~0xffffffffULL)) {
+			return 0;
+		}
+	}
+
+	if (!EXT2FS_IS_64_BITMAP(bmap))
+		return 0;
+
+	/* convert to clusters if necessary */
+	block >>= bmap->cluster_bits;
+	end += (1 << bmap->cluster_bits) - 1;
+	end >>= bmap->cluster_bits;
+	num = end - block;
+
+	if ((block < bmap->start) || (block > bmap->end) ||
+	    (block+num-1 > bmap->end))
+		return 0;
+
+	return 1;
+}
+
+
 void ext2fs_mark_block_bitmap_range2(ext2fs_block_bitmap gen_bmap,
 				     blk64_t block, unsigned int num)
 {
Signature · 1 line
2.37.3
#43 lore raw

RFC 18/72 libext2fs: Add support to get average group count

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

number of threads in pfsck should not exceed flex bg numbers. This patch adds the support in libext2fs to calculate ext2fs_get_avg_group() which returns an average group count which each thread has to scan.

fs->fs_num_threads will be set by the client, in this case e2fsck. No. of threads will be passed along with -m option while running e2fsck. That will also set fs->fs_num_threads, which will help in controlling the amount of memory consumed to maintain in memory data structures (per thread) in case of multiple parallel threads (pfsck) to avoid oom.

---
 lib/ext2fs/ext2fs.h | 32 +++++++++++++++++++++++++++++++-
 1 file changed, 31 insertions(+), 1 deletion(-)
lib/ext2fs/ext2fs.h +31 -1
index b1505f95..6b4926ce 100644
@@ -279,10 +279,11 @@ struct struct_ext2_filsys {
 	int				cluster_ratio_bits;
 	__u16				default_bitmap_type;
 	__u16				pad;
+	__u32				fs_num_threads;
 	/*
 	 * Reserved for future expansion
 	 */
-	__u32				reserved[5];
+	__u32				reserved[4];
 
 	/*
 	 * Reserved for the use of the calling application.
@@ -2231,6 +2232,35 @@ ext2fs_orphan_block_tail(ext2_filsys fs, char *buf)
 		sizeof(struct ext4_orphan_block_tail));
 }
 
+static dgrp_t ext2fs_get_avg_group(ext2_filsys fs)
+{
+#ifdef HAVE_PTHREAD
+	dgrp_t average_group;
+	unsigned flexbg_size;
+
+	if (fs->fs_num_threads <= 1)
+		return fs->group_desc_count;
+
+	average_group = fs->group_desc_count / fs->fs_num_threads;
+	if (average_group <= 1)
+		return 1;
+
+	if (ext2fs_has_feature_flex_bg(fs->super)) {
+		int times = 1;
+
+		flexbg_size = 1 << fs->super->s_log_groups_per_flex;
+		if (average_group % flexbg_size) {
+			times = average_group / flexbg_size;
+			average_group = times * flexbg_size;
+		}
+	}
+
+	return average_group;
+#else
+	return fs->group_desc_count;
+#endif
+}
+
 #undef _INLINE_
 #endif
 
Signature · 1 line
2.37.3
#45 lore raw

RFC 19/72 libext2fs: Misc fixes for struct_ext2_filsys

In reply to Ritesh Harjani (IBM)

From: Andreas Dilger <[email protected]>

Move ext2_filsys fs_num_threads to fit into the __u16 "pad" field to avoid consuming one of the few remaining __u32 reserved fields.

---
 lib/ext2fs/ext2fs.h | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
lib/ext2fs/ext2fs.h +2 -3
index 6b4926ce..950ab042 100644
@@ -278,12 +278,11 @@ struct struct_ext2_filsys {
 	time_t				now;
 	int				cluster_ratio_bits;
 	__u16				default_bitmap_type;
-	__u16				pad;
-	__u32				fs_num_threads;
+	__u16				fs_num_threads;
 	/*
 	 * Reserved for future expansion
 	 */
-	__u32				reserved[4];
+	__u32				reserved[5];
 
 	/*
 	 * Reserved for the use of the calling application.
Signature · 1 line
2.37.3
#47 lore raw

RFC 20/72 libext2fs: avoid too much memory allocation in case fs_num_threads

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

e2fsck init memory according to filesystem inodes/dir numbers recorded in the superblock, this should be aware of filesystem number of threads, otherwise, oom can happen.

So in case of fs->fs_num_threads, this patch controls the amount of memory consumed for running multiple threads in e2fsck.

---
 lib/ext2fs/dblist.c | 2 ++
 lib/ext2fs/icount.c | 4 ++++
 2 files changed, 6 insertions(+)
lib/ext2fs/dblist.c +2 -0
index 5568b8ec..c19e17bc 100644
@@ -58,6 +58,8 @@ static errcode_t make_dblist(ext2_filsys fs, ext2_ino_t size,
 		if (retval)
 			goto cleanup;
 		dblist->size = (num_dirs * 2) + 12;
+		if (fs->fs_num_threads)
+			dblist->size /= fs->fs_num_threads;
 	}
 	len = (size_t) sizeof(struct ext2_db_entry2) * dblist->size;
 	dblist->count = count;
lib/ext2fs/icount.c +4 -0
index 766eccca..48665c7e 100644
@@ -237,6 +237,8 @@ errcode_t ext2fs_create_icount_tdb(ext2_filsys fs EXT2FS_NO_TDB_UNUSED,
 	 * value.
 	 */
 	num_inodes = fs->super->s_inodes_count - fs->super->s_free_inodes_count;
+	if (fs->fs_num_threads)
+		num_inodes /= fs->fs_num_threads;
 
 	icount->tdb = tdb_open(fn, num_inodes, TDB_NOLOCK | TDB_NOSYNC,
 			       O_RDWR | O_CREAT | O_TRUNC, 0600);
@@ -288,6 +290,8 @@ errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, unsigned int size,
 		if (retval)
 			goto errout;
 		icount->size += fs->super->s_inodes_count / 50;
+		if (fs->fs_num_threads)
+			icount->size /= fs->fs_num_threads;
 	}
 
 	bytes = (size_t) (icount->size * sizeof(struct ext2_icount_el));
Signature · 1 line
2.37.3
#49 lore raw

RFC 21/72 e2fsck: add -m option for multithread

In reply to Ritesh Harjani (IBM)

From: Li Xi <[email protected]>

-m option is added but no actual functionality is added. This patch only adds the logic that when -m is specified, one of -p/-y/-n options should be specified. And when -m is specified, -C shouldn't be specified and the completion progress report won't be triggered by sending SIGUSR1/SIGUSR2 signals. This simplifies the implementation of multi-thread fsck in the future.

Completion progress support with multi-thread fsck will be added back after multi-thread fsck implementation is finished. Right now, disable it to simplify the implementation of multi-thread fsck.

---
 e2fsck/e2fsck.h                         |  1 +
 e2fsck/unix.c                           | 31 ++++++++++++++++++++-----
 tests/f_multithread/expect.1            | 23 ++++++++++++++++++
 tests/f_multithread/expect.2            |  7 ++++++
 tests/f_multithread/image.gz            |  1 +
 tests/f_multithread/name                |  1 +
 tests/f_multithread/script              |  4 ++++
 tests/f_multithread_completion/expect.1 |  2 ++
 tests/f_multithread_completion/expect.2 | 23 ++++++++++++++++++
 tests/f_multithread_completion/image.gz |  1 +
 tests/f_multithread_completion/name     |  1 +
 tests/f_multithread_completion/script   |  4 ++++
 tests/f_multithread_no/expect.1         | 24 +++++++++++++++++++
 tests/f_multithread_no/expect.2         | 23 ++++++++++++++++++
 tests/f_multithread_no/image.gz         |  1 +
 tests/f_multithread_no/name             |  1 +
 tests/f_multithread_no/script           |  4 ++++
 tests/f_multithread_preen/expect.1      | 11 +++++++++
 tests/f_multithread_preen/expect.2      | 23 ++++++++++++++++++
 tests/f_multithread_preen/image.gz      |  1 +
 tests/f_multithread_preen/name          |  1 +
 tests/f_multithread_preen/script        |  4 ++++
 tests/f_multithread_yes/expect.1        |  2 ++
 tests/f_multithread_yes/expect.2        | 23 ++++++++++++++++++
 tests/f_multithread_yes/image.gz        |  1 +
 tests/f_multithread_yes/name            |  1 +
 tests/f_multithread_yes/script          |  4 ++++
 27 files changed, 217 insertions(+), 6 deletions(-)
 create mode 100644 tests/f_multithread/expect.1
 create mode 100644 tests/f_multithread/expect.2
 create mode 120000 tests/f_multithread/image.gz
 create mode 100644 tests/f_multithread/name
 create mode 100644 tests/f_multithread/script
 create mode 100644 tests/f_multithread_completion/expect.1
 create mode 100644 tests/f_multithread_completion/expect.2
 create mode 120000 tests/f_multithread_completion/image.gz
 create mode 100644 tests/f_multithread_completion/name
 create mode 100644 tests/f_multithread_completion/script
 create mode 100644 tests/f_multithread_no/expect.1
 create mode 100644 tests/f_multithread_no/expect.2
 create mode 120000 tests/f_multithread_no/image.gz
 create mode 100644 tests/f_multithread_no/name
 create mode 100644 tests/f_multithread_no/script
 create mode 100644 tests/f_multithread_preen/expect.1
 create mode 100644 tests/f_multithread_preen/expect.2
 create mode 120000 tests/f_multithread_preen/image.gz
 create mode 100644 tests/f_multithread_preen/name
 create mode 100644 tests/f_multithread_preen/script
 create mode 100644 tests/f_multithread_yes/expect.1
 create mode 100644 tests/f_multithread_yes/expect.2
 create mode 120000 tests/f_multithread_yes/image.gz
 create mode 100644 tests/f_multithread_yes/name
 create mode 100644 tests/f_multithread_yes/script
e2fsck/e2fsck.h +1 -0
index 252a17db..35428717 100644
@@ -186,6 +186,7 @@ struct resource_track {
 #define E2F_OPT_UNSHARE_BLOCKS  0x40000
 #define E2F_OPT_CLEAR_UNINIT	0x80000 /* Hack to clear the uninit bit */
 #define E2F_OPT_CHECK_ENCODING  0x100000 /* Force verification of encoded filenames */
+#define E2F_OPT_MULTITHREAD	0x200000 /* Use multiple threads to speedup */
 
 /*
  * E2fsck flags
e2fsck/unix.c +25 -6
index e5b672a2..1ee27f6a 100644
@@ -76,13 +76,14 @@ int journal_enable_debug = -1;
 static void usage(e2fsck_t ctx)
 {
 	fprintf(stderr,
-		_("Usage: %s [-panyrcdfktvDFV] [-b superblock] [-B blocksize]\n"
+		_("Usage: %s [-pamnyrcdfktvDFV] [-b superblock] [-B blocksize]\n"
 		"\t\t[-l|-L bad_blocks_file] [-C fd] [-j external_journal]\n"
 		"\t\t[-E extended-options] [-z undo_file] device\n"),
 		ctx->program_name ? ctx->program_name : "e2fsck");
 
 	fprintf(stderr, "%s", _("\nEmergency help:\n"
 		" -p                   Automatic repair (no questions)\n"
+		" -m                   multiple threads to speedup fsck\n"
 		" -n                   Make no changes to the filesystem\n"
 		" -y                   Assume \"yes\" to all questions\n"
 		" -c                   Check for bad blocks and add them to the badblock list\n"
@@ -854,7 +855,8 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
 
 	phys_mem_kb = get_memory_size() / 1024;
 	ctx->readahead_kb = ~0ULL;
-	while ((c = getopt(argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDkz:")) != EOF)
+
+	while ((c = getopt(argc, argv, "pamnyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDkz:")) != EOF)
 		switch (c) {
 		case 'C':
 			ctx->progress = e2fsck_update_progress;
@@ -895,6 +897,9 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
 			}
 			ctx->options |= E2F_OPT_PREEN;
 			break;
+		case 'm':
+			ctx->options |= E2F_OPT_MULTITHREAD;
+			break;
 		case 'n':
 			if (ctx->options & (E2F_OPT_YES|E2F_OPT_PREEN))
 				goto conflict_opt;
@@ -1014,6 +1019,18 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
 			_("The -n and -l/-L options are incompatible."));
 		fatal_error(ctx, 0);
 	}
+	if (ctx->options & E2F_OPT_MULTITHREAD) {
+		if ((ctx->options & (E2F_OPT_YES|E2F_OPT_NO|E2F_OPT_PREEN)) == 0) {
+			com_err(ctx->program_name, 0, "%s",
+				_("The -m option should be used together with one of -p/-y/-n options."));
+			fatal_error(ctx, 0);
+		}
+		if (ctx->progress) {
+			com_err(ctx->program_name, 0, "%s",
+				_("Only one of the options -C or -m may be specified."));
+			fatal_error(ctx, 0);
+		}
+	}
 	if (ctx->options & E2F_OPT_NO)
 		ctx->options |= E2F_OPT_READONLY;
 
@@ -1120,10 +1137,12 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
 #ifdef SA_RESTART
 	sa.sa_flags = SA_RESTART;
 #endif
-	sa.sa_handler = signal_progress_on;
-	sigaction(SIGUSR1, &sa, 0);
-	sa.sa_handler = signal_progress_off;
-	sigaction(SIGUSR2, &sa, 0);
+	if ((ctx->options & E2F_OPT_MULTITHREAD) == 0) {
+		sa.sa_handler = signal_progress_on;
+		sigaction(SIGUSR1, &sa, 0);
+		sa.sa_handler = signal_progress_off;
+		sigaction(SIGUSR2, &sa, 0);
+	}
 #endif
 
 	/* Update our PATH to include /sbin if we need to run badblocks  */
tests/f_multithread/expect.1 new file +23 -0
new file mode 100644
index 00000000..e2b954d0
@@ -0,0 +1,23 @@
+ext2fs_open2: Bad magic number in super-block
+../e2fsck/e2fsck: Superblock invalid, trying backup blocks...
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+Free blocks count wrong for group #0 (7987, counted=7982).
+Fix? yes
+
+Free blocks count wrong (11602, counted=11597).
+Fix? yes
+
+Free inodes count wrong for group #0 (1493, counted=1488).
+Fix? yes
+
+Free inodes count wrong (2997, counted=2992).
+Fix? yes
+
+
+test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
+test_filesys: 16/3008 files (0.0% non-contiguous), 403/12000 blocks
+Exit status is 1
tests/f_multithread/expect.2 new file +7 -0
new file mode 100644
index 00000000..a833aefc
@@ -0,0 +1,7 @@
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+test_filesys: 16/3008 files (0.0% non-contiguous), 403/12000 blocks
+Exit status is 0
tests/f_multithread/image.gz new file +1 -0
new file mode 120000
index 00000000..0fd40018
@@ -0,0 +1 @@
+../f_zero_super/image.gz
\ No newline at end of file
tests/f_multithread/name new file +1 -0
new file mode 100644
index 00000000..df838ea6
@@ -0,0 +1 @@
+test "e2fsck -m" option
\ No newline at end of file
tests/f_multithread/script new file +4 -0
new file mode 100644
index 00000000..0fe96cd0
@@ -0,0 +1,4 @@
+FSCK_OPT="-fy -m"
+SECOND_FSCK_OPT=-yf
+
+. $cmd_dir/run_e2fsck
tests/f_multithread_completion/expect.1 new file +2 -0
new file mode 100644
index 00000000..61cac9bb
@@ -0,0 +1,2 @@
+../e2fsck/e2fsck: Only one of the options -C or -m may be specified.
+Exit status is 8
tests/f_multithread_completion/expect.2 new file +23 -0
new file mode 100644
index 00000000..e2b954d0
@@ -0,0 +1,23 @@
+ext2fs_open2: Bad magic number in super-block
+../e2fsck/e2fsck: Superblock invalid, trying backup blocks...
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+Free blocks count wrong for group #0 (7987, counted=7982).
+Fix? yes
+
+Free blocks count wrong (11602, counted=11597).
+Fix? yes
+
+Free inodes count wrong for group #0 (1493, counted=1488).
+Fix? yes
+
+Free inodes count wrong (2997, counted=2992).
+Fix? yes
+
+
+test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
+test_filesys: 16/3008 files (0.0% non-contiguous), 403/12000 blocks
+Exit status is 1
tests/f_multithread_completion/image.gz new file +1 -0
new file mode 120000
index 00000000..0fd40018
@@ -0,0 +1 @@
+../f_zero_super/image.gz
\ No newline at end of file
tests/f_multithread_completion/name new file +1 -0
new file mode 100644
index 00000000..a959045d
@@ -0,0 +1 @@
+test "e2fsck -m" option conflicts with "-C"
\ No newline at end of file
tests/f_multithread_completion/script new file +4 -0
new file mode 100644
index 00000000..bf23cd61
@@ -0,0 +1,4 @@
+FSCK_OPT="-fy -m -C 1"
+SECOND_FSCK_OPT=-yf
+
+. $cmd_dir/run_e2fsck
tests/f_multithread_no/expect.1 new file +24 -0
new file mode 100644
index 00000000..d14c4083
@@ -0,0 +1,24 @@
+ext2fs_open2: Bad magic number in super-block
+../e2fsck/e2fsck: Superblock invalid, trying backup blocks...
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+Free blocks count wrong for group #0 (7987, counted=7982).
+Fix? no
+
+Free blocks count wrong (11602, counted=11597).
+Fix? no
+
+Free inodes count wrong for group #0 (1493, counted=1488).
+Fix? no
+
+Free inodes count wrong (2997, counted=2992).
+Fix? no
+
+
+test_filesys: ********** WARNING: Filesystem still has errors **********
+
+test_filesys: 11/3008 files (0.0% non-contiguous), 398/12000 blocks
+Exit status is 4
tests/f_multithread_no/expect.2 new file +23 -0
new file mode 100644
index 00000000..e2b954d0
@@ -0,0 +1,23 @@
+ext2fs_open2: Bad magic number in super-block
+../e2fsck/e2fsck: Superblock invalid, trying backup blocks...
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+Free blocks count wrong for group #0 (7987, counted=7982).
+Fix? yes
+
+Free blocks count wrong (11602, counted=11597).
+Fix? yes
+
+Free inodes count wrong for group #0 (1493, counted=1488).
+Fix? yes
+
+Free inodes count wrong (2997, counted=2992).
+Fix? yes
+
+
+test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
+test_filesys: 16/3008 files (0.0% non-contiguous), 403/12000 blocks
+Exit status is 1
tests/f_multithread_no/image.gz new file +1 -0
new file mode 120000
index 00000000..0fd40018
@@ -0,0 +1 @@
+../f_zero_super/image.gz
\ No newline at end of file
tests/f_multithread_no/name new file +1 -0
new file mode 100644
index 00000000..fa49692e
@@ -0,0 +1 @@
+test "e2fsck -m" option works with "-n"
\ No newline at end of file
tests/f_multithread_no/script new file +4 -0
new file mode 100644
index 00000000..b93deb3a
@@ -0,0 +1,4 @@
+FSCK_OPT="-fn -m"
+SECOND_FSCK_OPT=-yf
+
+. $cmd_dir/run_e2fsck
tests/f_multithread_preen/expect.1 new file +11 -0
new file mode 100644
index 00000000..b4b0cd9a
@@ -0,0 +1,11 @@
+../e2fsck/e2fsck: Bad magic number in super-block while trying to open test.img
+test_filesys: 
+The superblock could not be read or does not describe a valid ext2/ext3/ext4
+filesystem.  If the device is valid and it really contains an ext2/ext3/ext4
+filesystem (and not swap or ufs or something else), then the superblock
+is corrupt, and you might try running e2fsck with an alternate superblock:
+    e2fsck -b 8193 <device>
+ or
+    e2fsck -b 32768 <device>
+
+Exit status is 8
tests/f_multithread_preen/expect.2 new file +23 -0
new file mode 100644
index 00000000..e2b954d0
@@ -0,0 +1,23 @@
+ext2fs_open2: Bad magic number in super-block
+../e2fsck/e2fsck: Superblock invalid, trying backup blocks...
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+Free blocks count wrong for group #0 (7987, counted=7982).
+Fix? yes
+
+Free blocks count wrong (11602, counted=11597).
+Fix? yes
+
+Free inodes count wrong for group #0 (1493, counted=1488).
+Fix? yes
+
+Free inodes count wrong (2997, counted=2992).
+Fix? yes
+
+
+test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
+test_filesys: 16/3008 files (0.0% non-contiguous), 403/12000 blocks
+Exit status is 1
tests/f_multithread_preen/image.gz new file +1 -0
new file mode 120000
index 00000000..0fd40018
@@ -0,0 +1 @@
+../f_zero_super/image.gz
\ No newline at end of file
tests/f_multithread_preen/name new file +1 -0
new file mode 100644
index 00000000..90d199df
@@ -0,0 +1 @@
+test "e2fsck -m" option works with "-p"
\ No newline at end of file
tests/f_multithread_preen/script new file +4 -0
new file mode 100644
index 00000000..ecb79cd6
@@ -0,0 +1,4 @@
+FSCK_OPT="-fp -m"
+SECOND_FSCK_OPT=-yf
+
+. $cmd_dir/run_e2fsck
tests/f_multithread_yes/expect.1 new file +2 -0
new file mode 100644
index 00000000..8b780ecf
@@ -0,0 +1,2 @@
+../e2fsck/e2fsck: The -m option should be used together with one of -p/-y/-n options.
+Exit status is 8
tests/f_multithread_yes/expect.2 new file +23 -0
new file mode 100644
index 00000000..e2b954d0
@@ -0,0 +1,23 @@
+ext2fs_open2: Bad magic number in super-block
+../e2fsck/e2fsck: Superblock invalid, trying backup blocks...
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+Free blocks count wrong for group #0 (7987, counted=7982).
+Fix? yes
+
+Free blocks count wrong (11602, counted=11597).
+Fix? yes
+
+Free inodes count wrong for group #0 (1493, counted=1488).
+Fix? yes
+
+Free inodes count wrong (2997, counted=2992).
+Fix? yes
+
+
+test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
+test_filesys: 16/3008 files (0.0% non-contiguous), 403/12000 blocks
+Exit status is 1
tests/f_multithread_yes/image.gz new file +1 -0
new file mode 120000
index 00000000..0fd40018
@@ -0,0 +1 @@
+../f_zero_super/image.gz
\ No newline at end of file
tests/f_multithread_yes/name new file +1 -0
new file mode 100644
index 00000000..3a703195
@@ -0,0 +1 @@
+test "e2fsck -m" option works with "-y"
\ No newline at end of file
tests/f_multithread_yes/script new file +4 -0
new file mode 100644
index 00000000..38891f6a
@@ -0,0 +1,4 @@
+FSCK_OPT="-f -m"
+SECOND_FSCK_OPT=-yf
+
+. $cmd_dir/run_e2fsck
Signature · 1 line
2.37.3
#51 lore raw

RFC 22/72 e2fsck: copy context when using multi-thread fsck

In reply to Ritesh Harjani (IBM)

From: Li Xi <[email protected]>

This patch only copy the context to a new one when -m is enabled. It doesn't actually start any thread. When pass1 test finishes, the new context is copied back to the original context.

Since the signal handler only changes the original context, so add global_ctx in "struct e2fsck_struct" and use that to check whether there is any signal of canceling.

This patch handles the long jump properly so that all the existing tests can be passed even the context has been copied. Otherwise, test f_expisize_ea_del would fail when aborting.

---
 e2fsck/e2fsck.h |   2 +
 e2fsck/pass1.c  | 117 ++++++++++++++++++++++++++++++++++++++++++++----
 e2fsck/unix.c   |   1 +
 3 files changed, 112 insertions(+), 8 deletions(-)
e2fsck/e2fsck.h +2 -0
index 35428717..f1259728 100644
@@ -262,6 +262,8 @@ struct e2fsck_fc_replay_state {
 };
 
 struct e2fsck_struct {
+	/* Global context to get the cancel flag */
+	e2fsck_t global_ctx;
 	ext2_filsys fs;
 	const char *program_name;
 	char *filesystem_name;
e2fsck/pass1.c +109 -8

171 lines not on this page · raw message

e2fsck/unix.c +1 -0
index 1ee27f6a..a77041b0 100644
@@ -1461,6 +1461,7 @@ int main (int argc, char *argv[])
 	}
 	reserve_stdio_fds();
 
+	ctx->global_ctx = NULL;
 	set_up_logging(ctx);
 	if (ctx->logf) {
 		int i;
Signature · 1 line
2.37.3
#52 lore raw

RFC 23/72 e2fsck: create logs for multi-threads

In reply to Ritesh Harjani (IBM)

From: Li Xi <[email protected]>

When multi-threads are used, different logs should be created for different threads. Each thread has log files with suffix of ".$THREAD_INDEX".

And this patch adds f_multithread_logfile test case.

---
 e2fsck/e2fsck.h                      |  6 ++++--
 e2fsck/logfile.c                     | 10 ++++++++-
 e2fsck/pass1.c                       | 12 +++++++++++
 tests/f_multithread_logfile/expect.1 | 23 ++++++++++++++++++++
 tests/f_multithread_logfile/image.gz |  1 +
 tests/f_multithread_logfile/name     |  1 +
 tests/f_multithread_logfile/script   | 32 ++++++++++++++++++++++++++++
 7 files changed, 82 insertions(+), 3 deletions(-)
 create mode 100644 tests/f_multithread_logfile/expect.1
 create mode 120000 tests/f_multithread_logfile/image.gz
 create mode 100644 tests/f_multithread_logfile/name
 create mode 100644 tests/f_multithread_logfile/script
e2fsck/e2fsck.h +4 -2
index f1259728..284bc52e 100644
@@ -342,8 +342,10 @@ struct e2fsck_struct {
 	/*
 	 * For pass1_check_directory and pass1_get_blocks
 	 */
-	ext2_ino_t stashed_ino;
-	struct ext2_inode *stashed_inode;
+	ext2_ino_t		stashed_ino;
+	struct ext2_inode	*stashed_inode;
+	/* Thread index, if global_ctx is null, this field is unused */
+	int			thread_index;
 
 	/*
 	 * Location of the lost and found directory
e2fsck/logfile.c +9 -1
index 9d79eed2..83dbceff 100644
@@ -16,6 +16,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
+#include <assert.h>
 
 #include "e2fsck.h"
 #include <pwd.h>
@@ -294,6 +295,8 @@ static FILE *set_up_log_file(e2fsck_t ctx, const char *key, const char *fn)
 	struct string s, s1, s2;
 	char *s0 = 0, *log_dir = 0, *log_fn = 0;
 	int log_dir_wait = 0;
+	int string_size;
+	char string_index[10];
 
 	s.s = s1.s = s2.s = 0;
 
@@ -310,6 +313,12 @@ static FILE *set_up_log_file(e2fsck_t ctx, const char *key, const char *fn)
 		goto out;
 
 	expand_logfn(ctx, log_fn, &s);
+	if (ctx->global_ctx) {
+		sprintf(string_index, "%d", ctx->thread_index);
+		append_string(&s, ".", 1);
+		append_string(&s, string_index, 0);
+	}
+
 	if ((log_fn[0] == '/') || !log_dir || !log_dir[0])
 		s0 = s.s;
 
@@ -328,7 +337,6 @@ static FILE *set_up_log_file(e2fsck_t ctx, const char *key, const char *fn)
 		append_string(&s2, log_dir, 0);
 		append_string(&s2, "/", 1);
 		append_string(&s2, s.s, 0);
-		printf("%s\n", s2.s);
 	}
 
 	if (s0)
e2fsck/pass1.c +12 -0
index 972265b8..2d4e62ca 100644
@@ -2164,6 +2164,8 @@ static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thre
 	thread_context->fs->priv_data = thread_context;
 	thread_context->global_ctx = global_ctx;
 
+	thread_context->thread_index = 0;
+	set_up_logging(thread_context);
 	*thread_ctx = thread_context;
 	return 0;
 }
@@ -2171,6 +2173,8 @@ static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thre
 static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
 {
 	int flags = global_ctx->flags;
+	FILE *global_logf = global_ctx->logf;
+	FILE *global_problem_logf = global_ctx->problem_logf;
 #ifdef HAVE_SETJMP_H
 	jmp_buf old_jmp;
 
@@ -2185,6 +2189,14 @@ static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
 			     (global_ctx->flags & E2F_FLAG_SIGNAL_MASK);
 
 	global_ctx->fs->priv_data = global_ctx;
+	global_ctx->logf = global_logf;
+	global_ctx->problem_logf = global_problem_logf;
+	if (thread_ctx->logf)
+		fclose(thread_ctx->logf);
+	if (thread_ctx->problem_logf) {
+		fputs("</problem_log>\n", thread_ctx->problem_logf);
+		fclose(thread_ctx->problem_logf);
+	}
 	ext2fs_free_mem(&thread_ctx);
 	return 0;
 }
tests/f_multithread_logfile/expect.1 new file +23 -0
new file mode 100644
index 00000000..e2b954d0
@@ -0,0 +1,23 @@
+ext2fs_open2: Bad magic number in super-block
+../e2fsck/e2fsck: Superblock invalid, trying backup blocks...
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+Free blocks count wrong for group #0 (7987, counted=7982).
+Fix? yes
+
+Free blocks count wrong (11602, counted=11597).
+Fix? yes
+
+Free inodes count wrong for group #0 (1493, counted=1488).
+Fix? yes
+
+Free inodes count wrong (2997, counted=2992).
+Fix? yes
+
+
+test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
+test_filesys: 16/3008 files (0.0% non-contiguous), 403/12000 blocks
+Exit status is 1
tests/f_multithread_logfile/image.gz new file +1 -0
new file mode 120000
index 00000000..0fd40018
@@ -0,0 +1 @@
+../f_zero_super/image.gz
\ No newline at end of file
tests/f_multithread_logfile/name new file +1 -0
new file mode 100644
index 00000000..faaabc3b
@@ -0,0 +1 @@
+test "e2fsck -m" option works with "-E log_filename="
tests/f_multithread_logfile/script new file +32 -0
new file mode 100644
index 00000000..4f9ca6f8
@@ -0,0 +1,32 @@
+LOG_FNAME="f_multithread_logfile_xxx"
+FSCK_OPT="-fy -m -y -E log_filename=$LOG_FNAME"
+SKIP_VERIFY="true"
+ONE_PASS_ONLY="true"
+SKIP_CLEANUP="true"
+
+rm -f $LOG_FNAME.* $LOG_FNAME
+
+. $cmd_dir/run_e2fsck
+
+rm -f $test_name.ok $test_name.failed
+cmp -s $OUT1 $EXP1
+status1=$?
+
+if [ "$status1" -eq 0 ]; then
+	if [ ! -f $LOG_FNAME -o ! -f $LOG_FNAME.0 ]; then
+		echo "$LOG_FNAME or $LOG_FNAME.0 is not created" > $test_name.failed
+		echo "$test_name: $test_description: failed"
+	else
+		echo "$test_name: $test_description: ok"
+		touch $test_name.ok
+	fi
+else
+	diff $DIFF_OPTS $test_dir/expect.1 \
+		$test_name.1.log >> $test_name.failed
+        echo "$test_name: $test_description: failed"
+fi
+
+unset IMAGE FSCK_OPT SECOND_FSCK_OPT OUT1 OUT2 EXP1 EXP2
+unset SKIP_VERIFY SKIP_CLEANUP SKIP_GUNZIP ONE_PASS_ONLY PREP_CMD
+unset DESCRIPTION SKIP_UNLINK AFTER_CMD PASS_ZERO
+unset LOG_FINAME
Signature · 1 line
2.37.3
#53 lore raw

RFC 24/72 e2fsck: configure one pfsck thread

In reply to Ritesh Harjani (IBM)

From: Li Xi <[email protected]>

This patch creates only one thread to do pass1 check if pthreads are enabled. The same codes can be used to create multiple threads, but other functions need to be modified to get ready for that.

---
 e2fsck/e2fsck.h   |  13 ++++
 e2fsck/logfile.c  |   2 +
 e2fsck/pass1.c    | 152 ++++++++++++++++++++++++++++++++++++++++------
 e2fsck/unix.c     |  10 +++
 tests/test_one.in |   8 +++
 5 files changed, 166 insertions(+), 19 deletions(-)
e2fsck/e2fsck.h +13 -0
index 284bc52e..5bc24c3f 100644
@@ -465,6 +465,19 @@ struct e2fsck_struct {
 	struct e2fsck_fc_replay_state fc_replay_state;
 };
 
+#ifdef HAVE_PTHREAD
+struct e2fsck_thread_info {
+	/* ID returned by pthread_create() */
+	pthread_t		 eti_thread_id;
+	/* Application-defined thread index */
+	int			 eti_thread_index;
+	/* Thread has been started */
+	int			 eti_started;
+	/* Context used for this thread */
+	e2fsck_t		 eti_thread_ctx;
+};
+#endif
+
 /* Data structures to evaluate whether an extent tree needs rebuilding. */
 struct extent_tree_level {
 	unsigned int	num_extents;
e2fsck/logfile.c +2 -0
index 83dbceff..74781f80 100644
@@ -313,11 +313,13 @@ static FILE *set_up_log_file(e2fsck_t ctx, const char *key, const char *fn)
 		goto out;
 
 	expand_logfn(ctx, log_fn, &s);
+#ifdef HAVE_PTHREAD
 	if (ctx->global_ctx) {
 		sprintf(string_index, "%d", ctx->thread_index);
 		append_string(&s, ".", 1);
 		append_string(&s, string_index, 0);
 	}
+#endif
 
 	if ((log_fn[0] == '/') || !log_dir || !log_dir[0])
 		s0 = s.s;
e2fsck/pass1.c +133 -19

198 lines not on this page · raw message

e2fsck/unix.c +10 -0
index a77041b0..af2457e3 100644
@@ -83,7 +83,9 @@ static void usage(e2fsck_t ctx)
 
 	fprintf(stderr, "%s", _("\nEmergency help:\n"
 		" -p                   Automatic repair (no questions)\n"
+#ifdef HAVE_PTHREAD
 		" -m                   multiple threads to speedup fsck\n"
+#endif
 		" -n                   Make no changes to the filesystem\n"
 		" -y                   Assume \"yes\" to all questions\n"
 		" -c                   Check for bad blocks and add them to the badblock list\n"
@@ -856,7 +858,11 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
 	phys_mem_kb = get_memory_size() / 1024;
 	ctx->readahead_kb = ~0ULL;
 
+#ifdef HAVE_PTHREAD
 	while ((c = getopt(argc, argv, "pamnyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDkz:")) != EOF)
+#else
+	while ((c = getopt(argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDkz:")) != EOF)
+#endif
 		switch (c) {
 		case 'C':
 			ctx->progress = e2fsck_update_progress;
@@ -897,9 +903,11 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
 			}
 			ctx->options |= E2F_OPT_PREEN;
 			break;
+#ifdef HAVE_PTHREAD
 		case 'm':
 			ctx->options |= E2F_OPT_MULTITHREAD;
 			break;
+#endif
 		case 'n':
 			if (ctx->options & (E2F_OPT_YES|E2F_OPT_PREEN))
 				goto conflict_opt;
@@ -1019,6 +1027,7 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
 			_("The -n and -l/-L options are incompatible."));
 		fatal_error(ctx, 0);
 	}
+#ifdef HAVE_PTHREAD
 	if (ctx->options & E2F_OPT_MULTITHREAD) {
 		if ((ctx->options & (E2F_OPT_YES|E2F_OPT_NO|E2F_OPT_PREEN)) == 0) {
 			com_err(ctx->program_name, 0, "%s",
@@ -1031,6 +1040,7 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
 			fatal_error(ctx, 0);
 		}
 	}
+#endif
 	if (ctx->options & E2F_OPT_NO)
 		ctx->options |= E2F_OPT_READONLY;
 
tests/test_one.in +8 -0
index 78499ad0..a1d46c9c 100644
@@ -27,6 +27,7 @@ esac
 
 test_dir=$1
 cmd_dir=$SRCDIR
+pfsck_enabled="no"
 
 if test "$TEST_CONFIG"x = x; then
 	TEST_CONFIG=$SRCDIR/test_config
@@ -52,6 +53,13 @@ else
 	test_description=
 fi
 
+$FSCK --help 2>&1 | grep -q -w -- -m && pfsck_enabled=yes
+if [ "$pfsck_enabled" != "yes" ] ; then
+	echo "$test_dir" | grep -q multithread &&
+	echo "$test_name: $test_description: skipped (pfsck disabled)" &&
+	exit 0
+fi
+
 if [ -n "$SKIP_SLOW_TESTS" -a -f $test_dir/is_slow_test ]; then
     echo "$test_name: $test_description: skipped (slow test)"
     exit 0
Signature · 1 line
2.37.3
#54 lore raw

RFC 25/72 e2fsck: Add e2fsck_pass1_thread_join return value

In reply to Ritesh Harjani (IBM)

This adds the return value to e2fsck_pass1_thread_join() to check for any error in pass1 threads join operation.

---
 e2fsck/pass1.c | 21 ++++++++++++++++++---
 1 file changed, 18 insertions(+), 3 deletions(-)
e2fsck/pass1.c +18 -3
index 596096d1..4b165600 100644
@@ -2174,8 +2174,9 @@ static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thre
 	return 0;
 }
 
-static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
+static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx)
 {
+	errcode_t retval = 0;
 	int flags = global_ctx->flags;
 	FILE *global_logf = global_ctx->logf;
 	FILE *global_problem_logf = global_ctx->problem_logf;
@@ -2195,6 +2196,14 @@ static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
 	global_ctx->fs->priv_data = global_ctx;
 	global_ctx->logf = global_logf;
 	global_ctx->problem_logf = global_problem_logf;
+	return retval;
+}
+
+static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
+{
+	errcode_t retval;
+
+	retval = e2fsck_pass1_thread_join_one(global_ctx, thread_ctx);
 	if (thread_ctx->logf)
 		fclose(thread_ctx->logf);
 	if (thread_ctx->problem_logf) {
@@ -2202,7 +2211,7 @@ static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
 		fclose(thread_ctx->problem_logf);
 	}
 	ext2fs_free_mem(&thread_ctx);
-	return 0;
+	return retval;
 }
 
 static int e2fsck_pass1_threads_join(struct e2fsck_thread_info *infos,
@@ -2226,7 +2235,13 @@ static int e2fsck_pass1_threads_join(struct e2fsck_thread_info *infos,
 			if (ret == 0)
 				ret = rc;
 		}
-		e2fsck_pass1_thread_join(global_ctx, infos[i].eti_thread_ctx);
+		rc = e2fsck_pass1_thread_join(global_ctx, infos[i].eti_thread_ctx);
+		if (rc) {
+			com_err(global_ctx->program_name, rc,
+				_("while joining pass1 thread\n"));
+			if (ret == 0)
+				ret = rc;
+		}
 	}
 	free(infos);
 
Signature · 1 line
2.37.3
#55 lore raw

RFC 26/72 e2fsck: Use merge/clone apis of libext2fs

In reply to Ritesh Harjani (IBM)

This patch makes use of libext2fs merge/clone apis in e2fsck to first clone the global_fs based on the passed CLONE_FLAGS. Then once the parallel e2fsck operation finishes, it calls for merge fs api which merges it into parent fs.

---
 e2fsck/pass1.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 55 insertions(+), 3 deletions(-)
e2fsck/pass1.c +55 -3
index 4b165600..040c58ce 100644
@@ -2154,10 +2154,36 @@ endit:
 }
 
 #ifdef HAVE_PTHREAD
+static errcode_t e2fsck_open_channel_fs(ext2_filsys dest, e2fsck_t dest_context, ext2_filsys src)
+{
+	errcode_t retval;
+
+	io_channel_flush_cleanup(src->io);
+	retval = dest->io->manager->open(dest->device_name,
+									 IO_FLAG_RW | IO_FLAG_THREADS, &dest->io);
+	if (retval)
+		return retval;
+	dest->image_io = dest->io;
+	dest->io->app_data = dest;
+	/* Block size might not be default */
+	io_channel_set_blksize(dest->io, src->io->block_size);
+	ehandler_init(dest->io);
+
+	dest->priv_data = dest_context;
+	dest_context->fs = dest;
+	/* The data should be written to disk immediately */
+	dest->io->flags |= CHANNEL_FLAGS_WRITETHROUGH;
+	/* icache will be rebuilt if needed, so do not copy from @src */
+	src->icache = NULL;
+	return 0;
+}
+
 static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thread_ctx)
 {
 	errcode_t retval;
 	e2fsck_t thread_context;
+	ext2_filsys thread_fs;
+	ext2_filsys global_fs = global_ctx->fs;
 
 	retval = ext2fs_get_mem(sizeof(struct e2fsck_struct), &thread_context);
 	if (retval) {
@@ -2165,13 +2191,30 @@ static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thre
 		return retval;
 	}
 	memcpy(thread_context, global_ctx, sizeof(struct e2fsck_struct));
-	thread_context->fs->priv_data = thread_context;
 	thread_context->global_ctx = global_ctx;
+	retval = ext2fs_clone_fs(global_fs, &thread_fs,
+							 EXT2FS_CLONE_BLOCK | EXT2FS_CLONE_INODE |
+							 EXT2FS_CLONE_BADBLOCKS | EXT2FS_CLONE_DBLIST);
+	if (retval) {
+		com_err(global_ctx->program_name, retval, "while allocating memory");
+		goto out_context;
+	}
+
+	retval = e2fsck_open_channel_fs(thread_fs, thread_context, global_fs);
+	if (retval) {
+		com_err(global_ctx->program_name, retval, "while copying fs");
+		goto out_fs;
+	}
 
 	thread_context->thread_index = 0;
 	set_up_logging(thread_context);
 	*thread_ctx = thread_context;
 	return 0;
+out_fs:
+	ext2fs_merge_fs(&thread_fs);
+out_context:
+	ext2fs_free_mem(&thread_context);
+	return retval;
 }
 
 static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx)
@@ -2180,6 +2223,8 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 	int flags = global_ctx->flags;
 	FILE *global_logf = global_ctx->logf;
 	FILE *global_problem_logf = global_ctx->problem_logf;
+	ext2_filsys thread_fs = thread_ctx->fs;
+	ext2_filsys global_fs = global_ctx->fs;
 #ifdef HAVE_SETJMP_H
 	jmp_buf old_jmp;
 
@@ -2192,10 +2237,17 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 	/* Keep the global singal flags*/
 	global_ctx->flags |= (flags & E2F_FLAG_SIGNAL_MASK) |
 			     (global_ctx->flags & E2F_FLAG_SIGNAL_MASK);
-
-	global_ctx->fs->priv_data = global_ctx;
 	global_ctx->logf = global_logf;
 	global_ctx->problem_logf = global_problem_logf;
+
+	global_fs->priv_data = global_ctx;
+	global_ctx->fs = global_fs;
+
+	retval = ext2fs_merge_fs(&(thread_ctx->fs));
+	if (retval) {
+		com_err(global_ctx->program_name, 0, _("while merging fs\n"));
+		return retval;
+	}
 	return retval;
 }
 
Signature · 1 line
2.37.3
#56 lore raw

RFC 27/72 e2fsck: Add e2fsck_pass1_merge_bitmap() api

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

This adds e2fsck_pass1_merge_bitmap() which uses libext2fs merge api (ext2fs_merge_bitmap()) to merge all the bitmaps after threads finishes.

---
 e2fsck/pass1.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 128 insertions(+)
e2fsck/pass1.c +128 -0
index 040c58ce..9a273515 100644
@@ -50,6 +50,7 @@
 #ifdef HAVE_PTHREAD
 #include <pthread.h>
 #endif
+#include <assert.h>
 
 #include "e2fsck.h"
 #include <ext2fs/ext2_ext_attr.h>
@@ -2154,6 +2155,26 @@ endit:
 }
 
 #ifdef HAVE_PTHREAD
+static errcode_t e2fsck_pass1_merge_bitmap(ext2_filsys fs, ext2fs_generic_bitmap *src,
+					  ext2fs_generic_bitmap *dest)
+{
+	errcode_t ret = 0;
+
+	if (*src) {
+		if (*dest == NULL) {
+			*dest = *src;
+			*src = NULL;
+		} else {
+			ret = ext2fs_merge_bitmap(*src, *dest, NULL, NULL);
+			if (ret)
+				return ret;
+		}
+		(*dest)->fs = fs;
+	}
+
+	return 0;
+}
+
 static errcode_t e2fsck_open_channel_fs(ext2_filsys dest, e2fsck_t dest_context, ext2_filsys src)
 {
 	errcode_t retval;
@@ -2185,6 +2206,19 @@ static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thre
 	ext2_filsys thread_fs;
 	ext2_filsys global_fs = global_ctx->fs;
 
+	assert(global_ctx->inode_used_map == NULL);
+	assert(global_ctx->inode_dir_map == NULL);
+	assert(global_ctx->inode_bb_map == NULL);
+	assert(global_ctx->inode_imagic_map == NULL);
+	assert(global_ctx->inode_reg_map == NULL);
+	assert(global_ctx->inodes_to_rebuild == NULL);
+
+	assert(global_ctx->block_found_map == NULL);
+	assert(global_ctx->block_dup_map == NULL);
+	assert(global_ctx->block_ea_map == NULL);
+	assert(global_ctx->block_metadata_map == NULL);
+	assert(global_ctx->fs->dblist == NULL);
+
 	retval = ext2fs_get_mem(sizeof(struct e2fsck_struct), &thread_context);
 	if (retval) {
 		com_err(global_ctx->program_name, retval, "while allocating memory");
@@ -2225,6 +2259,18 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 	FILE *global_problem_logf = global_ctx->problem_logf;
 	ext2_filsys thread_fs = thread_ctx->fs;
 	ext2_filsys global_fs = global_ctx->fs;
+	ext2fs_inode_bitmap inode_bad_map = global_ctx->inode_bad_map;
+	ext2fs_inode_bitmap inode_used_map = global_ctx->inode_used_map;
+	ext2fs_inode_bitmap inode_dir_map = global_ctx->inode_dir_map;
+	ext2fs_inode_bitmap inode_bb_map = global_ctx->inode_bb_map;
+	ext2fs_inode_bitmap inode_imagic_map = global_ctx->inode_imagic_map;
+	ext2fs_inode_bitmap inode_reg_map = global_ctx->inode_reg_map;
+	ext2fs_block_bitmap block_found_map = global_ctx->block_found_map;
+	ext2fs_block_bitmap block_dup_map = global_ctx->block_dup_map;
+	ext2fs_block_bitmap block_ea_map = global_ctx->block_ea_map;
+	ext2fs_block_bitmap block_metadata_map = global_ctx->block_metadata_map;
+	ext2fs_block_bitmap inodes_to_rebuild = global_ctx->inodes_to_rebuild;
+
 #ifdef HAVE_SETJMP_H
 	jmp_buf old_jmp;
 
@@ -2234,6 +2280,19 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 #ifdef HAVE_SETJMP_H
 	memcpy(global_ctx->abort_loc, old_jmp, sizeof(jmp_buf));
 #endif
+
+	global_ctx->inode_used_map = inode_used_map;
+	global_ctx->inode_bad_map = inode_bad_map;
+	global_ctx->inode_dir_map = inode_dir_map;
+	global_ctx->inode_bb_map = inode_bb_map;
+	global_ctx->inode_imagic_map = inode_imagic_map;
+	global_ctx->inodes_to_rebuild = inodes_to_rebuild;
+	global_ctx->inode_reg_map = inode_reg_map;
+	global_ctx->block_found_map = block_found_map;
+	global_ctx->block_dup_map = block_dup_map;
+	global_ctx->block_ea_map = block_ea_map;
+	global_ctx->block_metadata_map = block_metadata_map;
+
 	/* Keep the global singal flags*/
 	global_ctx->flags |= (flags & E2F_FLAG_SIGNAL_MASK) |
 			     (global_ctx->flags & E2F_FLAG_SIGNAL_MASK);
@@ -2248,6 +2307,63 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 		com_err(global_ctx->program_name, 0, _("while merging fs\n"));
 		return retval;
 	}
+	retval = e2fsck_pass1_merge_bitmap(global_fs,
+				&thread_ctx->inode_used_map,
+				&global_ctx->inode_used_map);
+	if (retval)
+		return retval;
+
+	retval = e2fsck_pass1_merge_bitmap(global_fs,
+				&thread_ctx->inode_bad_map,
+				&global_ctx->inode_bad_map);
+	if (retval)
+		return retval;
+	retval = e2fsck_pass1_merge_bitmap(global_fs,
+					&thread_ctx->inode_dir_map,
+					&global_ctx->inode_dir_map);
+	if (retval)
+		return retval;
+	retval = e2fsck_pass1_merge_bitmap(global_fs,
+				&thread_ctx->inode_bb_map,
+				&global_ctx->inode_bb_map);
+	if (retval)
+		return retval;
+	retval = e2fsck_pass1_merge_bitmap(global_fs,
+				&thread_ctx->inode_imagic_map,
+				&global_ctx->inode_imagic_map);
+	if (retval)
+		return retval;
+	retval = e2fsck_pass1_merge_bitmap(global_fs,
+				&thread_ctx->inode_reg_map,
+				&global_ctx->inode_reg_map);
+	if (retval)
+		return retval;
+	retval = e2fsck_pass1_merge_bitmap(global_fs,
+				&thread_ctx->inodes_to_rebuild,
+				&global_ctx->inodes_to_rebuild);
+	if (retval)
+		return retval;
+	retval = e2fsck_pass1_merge_bitmap(global_fs,
+				&thread_ctx->block_found_map,
+				&global_ctx->block_found_map);
+	if (retval)
+		return retval;
+	retval = e2fsck_pass1_merge_bitmap(global_fs,
+				&thread_ctx->block_dup_map,
+				&global_ctx->block_dup_map);
+	if (retval)
+		return retval;
+	retval = e2fsck_pass1_merge_bitmap(global_fs,
+				&thread_ctx->block_ea_map,
+				&global_ctx->block_ea_map);
+	if (retval)
+		return retval;
+	retval = e2fsck_pass1_merge_bitmap(global_fs,
+				&thread_ctx->block_metadata_map,
+				&global_ctx->block_metadata_map);
+	if (retval)
+		return retval;
+
 	return retval;
 }
 
@@ -2256,6 +2372,18 @@ static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
 	errcode_t retval;
 
 	retval = e2fsck_pass1_thread_join_one(global_ctx, thread_ctx);
+	e2fsck_pass1_free_bitmap(&thread_ctx->inode_used_map);
+	e2fsck_pass1_free_bitmap(&thread_ctx->inode_bad_map);
+	e2fsck_pass1_free_bitmap(&thread_ctx->inode_dir_map);
+	e2fsck_pass1_free_bitmap(&thread_ctx->inode_bb_map);
+	e2fsck_pass1_free_bitmap(&thread_ctx->inode_imagic_map);
+	e2fsck_pass1_free_bitmap(&thread_ctx->inode_reg_map);
+	e2fsck_pass1_free_bitmap(&thread_ctx->inodes_to_rebuild);
+	e2fsck_pass1_free_bitmap(&thread_ctx->block_found_map);
+	e2fsck_pass1_free_bitmap(&thread_ctx->block_dup_map);
+	e2fsck_pass1_free_bitmap(&thread_ctx->block_ea_map);
+	e2fsck_pass1_free_bitmap(&thread_ctx->block_metadata_map);
+
 	if (thread_ctx->logf)
 		fclose(thread_ctx->logf);
 	if (thread_ctx->problem_logf) {
Signature · 1 line
2.37.3
#57 lore raw

RFC 28/72 e2fsck: Add asserts in open_channel_fs

In reply to Ritesh Harjani (IBM)

From: Li Xi <[email protected]>

---
 e2fsck/pass1.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)
e2fsck/pass1.c +12 -0
index 9a273515..ea432ff2 100644
@@ -2190,6 +2190,18 @@ static errcode_t e2fsck_open_channel_fs(ext2_filsys dest, e2fsck_t dest_context,
 	io_channel_set_blksize(dest->io, src->io->block_size);
 	ehandler_init(dest->io);
 
+	assert(dest->io->magic == src->io->magic);
+	assert(dest->io->manager == src->io->manager);
+	assert(strcmp(dest->io->name, src->io->name) == 0);
+	assert(dest->io->block_size == src->io->block_size);
+	assert(dest->io->read_error == src->io->read_error);
+	assert(dest->io->write_error == src->io->write_error);
+	assert(dest->io->refcount == src->io->refcount);
+	assert(dest->io->flags == src->io->flags);
+	assert(dest->io->app_data == dest);
+	assert(src->io->app_data == src);
+	assert(dest->io->align == src->io->align);
+
 	dest->priv_data = dest_context;
 	dest_context->fs = dest;
 	/* The data should be written to disk immediately */
Signature · 1 line
2.37.3
#58 lore raw

RFC 29/72 e2fsck: add start/end group for thread

In reply to Ritesh Harjani (IBM)

From: Li Xi <[email protected]>

When multi-threads are used for check, each thread needs to jump to different group in pass1 check. This patch adds the group jumping support. But still, only one thread is used to check.

---
 e2fsck/e2fsck.h           | 23 ++++++++++++++++++--
 e2fsck/logfile.c          |  3 ++-
 e2fsck/pass1.c            | 44 ++++++++++++++++++++++++++++++++++++---
 e2fsck/problem.c          |  5 +++++
 e2fsck/problem.h          |  3 +++
 lib/ext2fs/ext2_err.et.in |  3 +++
 6 files changed, 75 insertions(+), 6 deletions(-)
e2fsck/e2fsck.h +21 -2
index 5bc24c3f..639f4e80 100644
@@ -261,6 +261,22 @@ struct e2fsck_fc_replay_state {
 	__u16 fc_super_state;
 };
 
+#ifdef HAVE_PTHREAD
+/*
+ * Fields that used for multi-thread
+ */
+struct e2fsck_thread {
+	/* Thread index */
+	int		et_thread_index;
+	/* The start group number for this thread */
+	dgrp_t		et_group_start;
+	/* The end (not included) group number for this thread*/
+	dgrp_t		et_group_end;
+	/* The next group number to check */
+	dgrp_t		et_group_next;
+};
+#endif
+
 struct e2fsck_struct {
 	/* Global context to get the cancel flag */
 	e2fsck_t global_ctx;
@@ -344,8 +360,11 @@ struct e2fsck_struct {
 	 */
 	ext2_ino_t		stashed_ino;
 	struct ext2_inode	*stashed_inode;
-	/* Thread index, if global_ctx is null, this field is unused */
-	int			thread_index;
+
+	/* if @global_ctx is null, this field is unused */
+#ifdef HAVE_PTHREAD
+	struct e2fsck_thread	 thread_info;
+#endif
 
 	/*
 	 * Location of the lost and found directory
e2fsck/logfile.c +2 -1
index 74781f80..cc811d5a 100644
@@ -315,7 +315,8 @@ static FILE *set_up_log_file(e2fsck_t ctx, const char *key, const char *fn)
 	expand_logfn(ctx, log_fn, &s);
 #ifdef HAVE_PTHREAD
 	if (ctx->global_ctx) {
-		sprintf(string_index, "%d", ctx->thread_index);
+		sprintf(string_index, "%d",
+			ctx->thread_info.et_thread_index);
 		append_string(&s, ".", 1);
 		append_string(&s, string_index, 0);
 	}
e2fsck/pass1.c +41 -3

92 lines not on this page · raw message

e2fsck/problem.c +5 -0
index 6ad6fb84..d5452441 100644
@@ -1309,6 +1309,11 @@ static struct e2fsck_problem problem_table[] = {
 	  N_("Orphan file @i %i is not in use, but contains data.  "),
 	  PROMPT_CLEAR, PR_PREEN_OK },
 
+	/* Failed to goto block group */
+	{ PR_1_SCAN_GOTO,
+	  N_("failed to goto block group"),
+          PROMPT_NONE, PR_FATAL, 0, 0, 0 },
+
 	/* Pass 1b errors */
 
 	/* Pass 1B: Rescan for duplicate/bad blocks */
e2fsck/problem.h +3 -0
index b47b0c63..a6207428 100644
@@ -734,6 +734,9 @@ struct problem_context {
 /* Orphan file inode is not in use, but contains data */
 #define PR_1_ORPHAN_FILE_NOT_CLEAR		0x010090
 
+/* Failed to goto block group */
+#define PR_1_SCAN_GOTO				0x0100A0
+
 /*
  * Pass 1b errors
  */
lib/ext2fs/ext2_err.et.in +3 -0
index de140198..b8f45ae2 100644
@@ -557,4 +557,7 @@ ec	EXT2_ET_EXTENT_CYCLE,
 ec	EXT2_ET_EXTERNAL_JOURNAL_NOSUPP,
 	"Operation not supported on an external journal"
 
+ec	EXT2_ET_SCAN_FINISHED,
+	"Scanning finished"
+
 	end
Signature · 1 line
2.37.3
#59 lore raw

RFC 30/72 e2fsck: split groups to different threads

In reply to Ritesh Harjani (IBM)

From: Li Xi <[email protected]>

The start/end groups of a thread is calculated according to the thread number. But still, only one thread is used to check.

---
 e2fsck/pass1.c | 23 +++++++++++++++++------
 1 file changed, 17 insertions(+), 6 deletions(-)
e2fsck/pass1.c +17 -6
index 3bb87669..3b411b70 100644
@@ -2231,13 +2231,14 @@ static errcode_t e2fsck_open_channel_fs(ext2_filsys dest, e2fsck_t dest_context,
 }
 
 static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thread_ctx,
-					     int thread_index)
+					     int thread_index, int num_threads)
 {
 	errcode_t retval;
 	e2fsck_t thread_context;
 	ext2_filsys thread_fs;
 	ext2_filsys global_fs = global_ctx->fs;
 	struct e2fsck_thread *tinfo;
+	dgrp_t average_group;
 
 	assert(global_ctx->inode_used_map == NULL);
 	assert(global_ctx->inode_dir_map == NULL);
@@ -2276,11 +2277,20 @@ static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thre
 	thread_context->thread_info.et_thread_index = thread_index;
 	set_up_logging(thread_context);
 
-	assert(thread_index == 0);
+	/*
+	 * Distribute work to multiple threads:
+	 * Each thread work on fs->group_desc_count / nthread groups.
+	 */
 	tinfo = &thread_context->thread_info;
-	tinfo->et_group_start = 0;
-	tinfo->et_group_next = 0;
-	tinfo->et_group_end = thread_fs->group_desc_count;
+	average_group = thread_fs->group_desc_count / num_threads;
+	if (average_group == 0)
+		average_group = 1;
+	tinfo->et_group_start = average_group * thread_index;
+	if (thread_index == num_threads - 1)
+		tinfo->et_group_end = thread_fs->group_desc_count;
+	else
+		tinfo->et_group_end = average_group * (thread_index + 1);
+	tinfo->et_group_next = tinfo->et_group_start;
 
 	*thread_ctx = thread_context;
 	return 0;
@@ -2523,7 +2533,8 @@ static int e2fsck_pass1_threads_start(struct e2fsck_thread_info **pinfo,
 	for (i = 0; i < num_threads; i++) {
 		tmp_pinfo = &infos[i];
 		tmp_pinfo->eti_thread_index = i;
-		retval = e2fsck_pass1_thread_prepare(global_ctx, &thread_ctx, i);
+		retval = e2fsck_pass1_thread_prepare(global_ctx, &thread_ctx,
+						     i, num_threads);
 		if (retval) {
 			com_err(global_ctx->program_name, retval,
 				_("while preparing pass1 thread\n"));
Signature · 1 line
2.37.3
#60 lore raw

RFC 31/72 e2fsck: print thread log properly

In reply to Ritesh Harjani (IBM)

From: Li Xi <[email protected]>

When multi-thread fsck is enabled, logs printed from multiple threads could overlap with each other. The overlap sometimes makes the logs unreadable because log_out() is used multiple times for a single line.

This patch adds leading [Thread XXX] to each logs if multi-thread is enabed by -m option.

This patch also adds message to show the group ranges and inode numbers for each thread, which is useful for debuging multi-thread check.

---
 e2fsck/e2fsck.h                      |  4 ++
 e2fsck/pass1.c                       | 21 ++++++++++
 e2fsck/problem.c                     |  6 +++
 e2fsck/util.c                        | 61 ++++++++++++++++++++++++++--
 tests/f_multithread/expect.1         |  4 +-
 tests/f_multithread_logfile/expect.1 |  4 +-
 tests/f_multithread_no/expect.1      |  4 +-
 7 files changed, 98 insertions(+), 6 deletions(-)
e2fsck/e2fsck.h +4 -0
index 639f4e80..cdd158cc 100644
@@ -274,6 +274,10 @@ struct e2fsck_thread {
 	dgrp_t		et_group_end;
 	/* The next group number to check */
 	dgrp_t		et_group_next;
+	/* Scanned inode number */
+	ext2_ino_t	et_inode_number;
+	char		et_log_buf[2048];
+	char		et_log_length;
 };
 #endif
 
e2fsck/pass1.c +21 -0
index 3b411b70..a2c13be5 100644
@@ -1460,6 +1460,10 @@ void e2fsck_pass1_run(e2fsck_t ctx)
 		}
 		if (!ino)
 			break;
+#ifdef HAVE_PTHREAD
+		if (ctx->global_ctx)
+		        ctx->thread_info.et_inode_number++;
+#endif
 		pctx.ino = ino;
 		pctx.inode = inode;
 		ctx->stashed_ino = ino;
@@ -2292,6 +2296,12 @@ static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thre
 		tinfo->et_group_end = average_group * (thread_index + 1);
 	tinfo->et_group_next = tinfo->et_group_start;
 
+	tinfo->et_inode_number = 0;
+	tinfo->et_log_buf[0] = '\0';
+	tinfo->et_log_length = 0;
+	if (thread_context->options & E2F_OPT_MULTITHREAD)
+		log_out(thread_context, _("Scan group range [%d, %d)\n"),
+			tinfo->et_group_start, tinfo->et_group_end);
 	*thread_ctx = thread_context;
 	return 0;
 out_fs:
@@ -2348,6 +2358,7 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 			     (global_ctx->flags & E2F_FLAG_SIGNAL_MASK);
 	global_ctx->logf = global_logf;
 	global_ctx->problem_logf = global_problem_logf;
+	global_ctx->global_ctx = NULL;
 
 	global_fs->priv_data = global_ctx;
 	global_ctx->fs = global_fs;
@@ -2500,6 +2511,12 @@ static void *e2fsck_pass1_thread(void *arg)
 	e2fsck_pass1_run(thread_ctx);
 
 out:
+	if (thread_ctx->options & E2F_OPT_MULTITHREAD)
+		log_out(thread_ctx,
+			_("Scanned group range [%lu, %lu), inodes %lu\n"),
+			thread_ctx->thread_info.et_group_start,
+			thread_ctx->thread_info.et_group_end,
+			thread_ctx->thread_info.et_inode_number);
 	return NULL;
 }
 
@@ -2635,6 +2652,10 @@ static errcode_t scan_callback(ext2_filsys fs,
 	if (ctx->global_ctx) {
 		tinfo = &ctx->thread_info;
 		tinfo->et_group_next++;
+		if (ctx->options & E2F_OPT_DEBUG &&
+		    ctx->options & E2F_OPT_MULTITHREAD)
+			log_out(ctx, _("group %d finished\n"),
+				tinfo->et_group_next);
 		if (tinfo->et_group_next >= tinfo->et_group_end)
 			return EXT2_ET_SCAN_FINISHED;
 	}
e2fsck/problem.c +6 -0
index d5452441..9ee3914e 100644
@@ -2594,6 +2594,12 @@ int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx)
 	if (*message)
 		message = _(message);
 	if (!suppress) {
+#ifdef	HAVE_PTHREAD
+		if ((ctx->options & E2F_OPT_MULTITHREAD) && ctx->global_ctx)
+			printf("[Thread %d] ",
+			       ctx->thread_info.et_thread_index);
+#endif
+
 		if ((ctx->options & E2F_OPT_PREEN) &&
 		    !(ptr->flags & PR_PREEN_NOHDR)) {
 			printf("%s: ", ctx->device_name ?
e2fsck/util.c +58 -3

77 lines not on this page · raw message

tests/f_multithread/expect.1 +3 -1
index e2b954d0..8d2acd2b 100644
@@ -1,6 +1,8 @@
 ext2fs_open2: Bad magic number in super-block
 ../e2fsck/e2fsck: Superblock invalid, trying backup blocks...
-Pass 1: Checking inodes, blocks, and sizes
+[Thread 0] Scan group range [0, 2)
+[Thread 0] Pass 1: Checking inodes, blocks, and sizes
+[Thread 0] Scanned group range [0, 2), inodes 3008
 Pass 2: Checking directory structure
 Pass 3: Checking directory connectivity
 Pass 4: Checking reference counts
tests/f_multithread_logfile/expect.1 +3 -1
index e2b954d0..8d2acd2b 100644
@@ -1,6 +1,8 @@
 ext2fs_open2: Bad magic number in super-block
 ../e2fsck/e2fsck: Superblock invalid, trying backup blocks...
-Pass 1: Checking inodes, blocks, and sizes
+[Thread 0] Scan group range [0, 2)
+[Thread 0] Pass 1: Checking inodes, blocks, and sizes
+[Thread 0] Scanned group range [0, 2), inodes 3008
 Pass 2: Checking directory structure
 Pass 3: Checking directory connectivity
 Pass 4: Checking reference counts
tests/f_multithread_no/expect.1 +3 -1
index d14c4083..f85a3382 100644
@@ -1,6 +1,8 @@
 ext2fs_open2: Bad magic number in super-block
 ../e2fsck/e2fsck: Superblock invalid, trying backup blocks...
-Pass 1: Checking inodes, blocks, and sizes
+[Thread 0] Scan group range [0, 2)
+[Thread 0] Pass 1: Checking inodes, blocks, and sizes
+[Thread 0] Scanned group range [0, 2), inodes 3008
 Pass 2: Checking directory structure
 Pass 3: Checking directory connectivity
 Pass 4: Checking reference counts
Signature · 1 line
2.37.3
#61 lore raw

RFC 32/72 e2fsck: do not change global variables

In reply to Ritesh Harjani (IBM)

From: Li Xi <[email protected]>

Global variables used in pass1 check are changed to local variables in this patch. This will avoid conflict between threads.

---
 e2fsck/pass1.c | 75 +++++++++++++++++++++++++++++++-------------------
 1 file changed, 47 insertions(+), 28 deletions(-)
e2fsck/pass1.c +47 -28
index a2c13be5..d5c01dc7 100644
@@ -86,7 +86,6 @@ static void alloc_imagic_map(e2fsck_t ctx);
 static void mark_inode_bad(e2fsck_t ctx, ext2_ino_t ino);
 static void add_casefolded_dir(e2fsck_t ctx, ext2_ino_t ino);
 static void handle_fs_bad_blocks(e2fsck_t ctx);
-static void process_inodes(e2fsck_t ctx, char *block_buf);
 static EXT2_QSORT_TYPE process_inode_cmp(const void *a, const void *b);
 static errcode_t scan_callback(ext2_filsys fs, ext2_inode_scan scan,
 				  dgrp_t group, void * priv_data);
@@ -121,15 +120,15 @@ struct process_inode_block {
 };
 
 struct scan_callback_struct {
-	e2fsck_t	ctx;
-	char		*block_buf;
+	e2fsck_t			 ctx;
+	char				*block_buf;
+	struct process_inode_block	*inodes_to_process;
+	int				*process_inode_count;
 };
 
-/*
- * For the inodes to process list.
- */
-static struct process_inode_block *inodes_to_process;
-static int process_inode_count;
+static void process_inodes(e2fsck_t ctx, char *block_buf,
+			   struct process_inode_block *inodes_to_process,
+			   int *process_inode_count);
 
 static __u64 ext2_max_sizes[EXT2_MAX_BLOCK_LOG_SIZE -
 			    EXT2_MIN_BLOCK_LOG_SIZE + 1];
@@ -1173,7 +1172,6 @@ static int e2fsck_should_abort(e2fsck_t ctx)
 void e2fsck_pass1_run(e2fsck_t ctx)
 {
 	int	i;
-	__u64	max_sizes;
 	ext2_filsys fs = ctx->fs;
 	ext2_ino_t	ino = 0;
 	struct ext2_inode *inode = NULL;
@@ -1196,6 +1194,8 @@ void e2fsck_pass1_run(e2fsck_t ctx)
 	ext2_ino_t	ino_threshold = 0;
 	dgrp_t		ra_group = 0;
 	struct ea_quota	ea_ibody_quota;
+	struct process_inode_block *inodes_to_process;
+	int process_inode_count;
 
 	init_resource_track(&rtrack, ctx->fs->io);
 	clear_problem_context(&pctx);
@@ -1220,17 +1220,6 @@ void e2fsck_pass1_run(e2fsck_t ctx)
 	mtrace_print("Pass 1");
 #endif
 
-#define EXT2_BPP(bits) (1ULL << ((bits) - 2))
-
-	for (i = EXT2_MIN_BLOCK_LOG_SIZE; i <= EXT2_MAX_BLOCK_LOG_SIZE; i++) {
-		max_sizes = EXT2_NDIR_BLOCKS + EXT2_BPP(i);
-		max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i);
-		max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i) * EXT2_BPP(i);
-		max_sizes = (max_sizes * (1UL << i));
-		ext2_max_sizes[i - EXT2_MIN_BLOCK_LOG_SIZE] = max_sizes;
-	}
-#undef EXT2_BPP
-
 	imagic_fs = ext2fs_has_feature_imagic_inodes(sb);
 	extent_fs = ext2fs_has_feature_extents(sb);
 	inlinedata_fs = ext2fs_has_feature_inline_data(sb);
@@ -1368,6 +1357,8 @@ void e2fsck_pass1_run(e2fsck_t ctx)
 	ctx->stashed_inode = inode;
 	scan_struct.ctx = ctx;
 	scan_struct.block_buf = block_buf;
+	scan_struct.inodes_to_process = inodes_to_process;
+	scan_struct.process_inode_count = &process_inode_count;
 	ext2fs_set_inode_callback(scan, scan_callback, &scan_struct);
 	if (ctx->progress && ((ctx->progress)(ctx, 1, 0,
 					      ctx->fs->group_desc_count)))
@@ -2048,13 +2039,15 @@ void e2fsck_pass1_run(e2fsck_t ctx)
 			goto endit;
 
 		if (process_inode_count >= ctx->process_inode_size) {
-			process_inodes(ctx, block_buf);
+			process_inodes(ctx, block_buf, inodes_to_process,
+				       &process_inode_count);
 
 			if (e2fsck_should_abort(ctx))
 				goto endit;
 		}
 	}
-	process_inodes(ctx, block_buf);
+	process_inodes(ctx, block_buf, inodes_to_process,
+		       &process_inode_count);
 	ext2fs_close_inode_scan(scan);
 	scan = NULL;
 
@@ -2177,6 +2170,27 @@ endit:
 		ctx->invalid_bitmaps++;
 }
 
+static void init_ext2_max_sizes()
+{
+	int i;
+	__u64 max_sizes;
+
+	/*
+	 * Init ext2_max_sizes which will be immutable and shared between
+	 * threads
+	 */
+#define EXT2_BPP(bits) (1ULL << ((bits) - 2))
+
+	for (i = EXT2_MIN_BLOCK_LOG_SIZE; i <= EXT2_MAX_BLOCK_LOG_SIZE; i++) {
+		max_sizes = EXT2_NDIR_BLOCKS + EXT2_BPP(i);
+		max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i);
+		max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i) * EXT2_BPP(i);
+		max_sizes = (max_sizes * (1UL << i));
+		ext2_max_sizes[i - EXT2_MIN_BLOCK_LOG_SIZE] = max_sizes;
+	}
+#undef EXT2_BPP
+}
+
 #ifdef HAVE_PTHREAD
 static errcode_t e2fsck_pass1_merge_bitmap(ext2_filsys fs, ext2fs_generic_bitmap *src,
 					  ext2fs_generic_bitmap *dest)
@@ -2616,6 +2630,7 @@ out_abort:
 
 void e2fsck_pass1(e2fsck_t ctx)
 {
+	init_ext2_max_sizes();
 #ifdef HAVE_PTHREAD
 	if (ctx->options & E2F_OPT_MULTITHREAD)
 		e2fsck_pass1_multithread(ctx);
@@ -2641,7 +2656,9 @@ static errcode_t scan_callback(ext2_filsys fs,
 	scan_struct = (struct scan_callback_struct *) priv_data;
 	ctx = scan_struct->ctx;
 
-	process_inodes((e2fsck_t) fs->priv_data, scan_struct->block_buf);
+	process_inodes((e2fsck_t) fs->priv_data, scan_struct->block_buf,
+		       scan_struct->inodes_to_process,
+		       scan_struct->process_inode_count);
 
 	if (ctx->progress)
 		if ((ctx->progress)(ctx, 1, group+1,
@@ -2667,7 +2684,9 @@ static errcode_t scan_callback(ext2_filsys fs,
 /*
  * Process the inodes in the "inodes to process" list.
  */
-static void process_inodes(e2fsck_t ctx, char *block_buf)
+static void process_inodes(e2fsck_t ctx, char *block_buf,
+			   struct process_inode_block *inodes_to_process,
+			   int *process_inode_count)
 {
 	int			i;
 	struct ext2_inode	*old_stashed_inode;
@@ -2679,15 +2698,15 @@ static void process_inodes(e2fsck_t ctx, char *block_buf)
 #if 0
 	printf("begin process_inodes: ");
 #endif
-	if (process_inode_count == 0)
+	if (*process_inode_count == 0)
 		return;
 	old_operation = ehandler_operation(0);
 	old_stashed_inode = ctx->stashed_inode;
 	old_stashed_ino = ctx->stashed_ino;
-	qsort(inodes_to_process, process_inode_count,
+	qsort(inodes_to_process, *process_inode_count,
 		      sizeof(struct process_inode_block), process_inode_cmp);
 	clear_problem_context(&pctx);
-	for (i=0; i < process_inode_count; i++) {
+	for (i=0; i < *process_inode_count; i++) {
 		pctx.inode = ctx->stashed_inode =
 			(struct ext2_inode *) &inodes_to_process[i].inode;
 		pctx.ino = ctx->stashed_ino = inodes_to_process[i].ino;
@@ -2705,7 +2724,7 @@ static void process_inodes(e2fsck_t ctx, char *block_buf)
 	}
 	ctx->stashed_inode = old_stashed_inode;
 	ctx->stashed_ino = old_stashed_ino;
-	process_inode_count = 0;
+	*process_inode_count = 0;
 #if 0
 	printf("end process inodes\n");
 #endif
Signature · 1 line
2.37.3
#62 lore raw

RFC 33/72 e2fsck: optimize the inserting of dir_info_db

In reply to Ritesh Harjani (IBM)

From: Li Xi <[email protected]>

Binary search is now used when inserting an dir info to the array. Memmove is now used when moving array. Both of them improves the performance of inserting.

This patch is also a prepartion for the merging of two dir db arrays.

---
 e2fsck/dirinfo.c | 172 ++++++++++++++++++++++++++++++-----------------
 1 file changed, 112 insertions(+), 60 deletions(-)
e2fsck/dirinfo.c +112 -60
index 49d624c5..5c360a90 100644
@@ -7,6 +7,7 @@
 
 #undef DIRINFO_DEBUG
 
+#include <assert.h>
 #include "config.h"
 #include "e2fsck.h"
 #include <sys/stat.h>
@@ -122,6 +123,104 @@ static void setup_db(e2fsck_t ctx)
 				       "directory map");
 }
 
+/*
+ * Return the min index that has ino larger or equal to @ino
+ * If not found, return -ENOENT
+ */
+static int
+e2fsck_dir_info_min_larger_equal(struct dir_info_db *dir_info,
+				 ext2_ino_t ino, ext2_ino_t *index)
+{
+	ext2_ino_t low = 0;
+	ext2_ino_t mid, high;
+	ext2_ino_t tmp_ino;
+	int found = 0;
+
+	if (dir_info->count == 0)
+		return -ENOENT;
+
+	high = dir_info->count - 1;
+	while (low <= high) {
+		/* sum may overflow, but result will fit into mid again */
+		mid = (unsigned long long)(low + high) / 2;
+		tmp_ino = dir_info->array[mid].ino;
+		if (ino == tmp_ino) {
+			*index = mid;
+			found = 1;
+			return 0;
+		} else if (ino < tmp_ino) {
+			/*
+			 * The mid ino is larger than @ino, remember the index
+			 * here so we won't miss this ino
+			 */
+			*index = mid;
+			found = 1;
+			if (mid == 0)
+				break;
+			high = mid - 1;
+		} else {
+			low = mid + 1;
+		}
+	}
+
+	if (found)
+		return 0;
+
+	return -ENOENT;
+}
+
+/*
+ *
+ * Insert an inode into the sorted array. The array should have at least one
+ * free slot.
+ *
+ * Normally, add_dir_info is called with each inode in
+ * sequential order; but once in a while (like when pass 3
+ * needs to recreate the root directory or lost+found
+ * directory) it is called out of order.  In those cases, we
+ * need to move the dir_info entries down to make room, since
+ * the dir_info array needs to be sorted by inode number for
+ * get_dir_info()'s sake.
+ */
+static void e2fsck_insert_dir_info(struct dir_info_db *dir_info, ext2_ino_t ino, ext2_ino_t parent)
+{
+	ext2_ino_t index;
+	struct dir_info *dir;
+	size_t dir_size = sizeof(*dir);
+	struct dir_info *array = dir_info->array;
+	ext2_ino_t array_count = dir_info->count;
+	int err;
+
+	/*
+	 * Removing this check won't break anything. But since seqential ino
+	 * inserting happens a lot, this check avoids binary search.
+	 */
+	if (array_count == 0 || array[array_count - 1].ino < ino) {
+		dir = &array[array_count];
+		dir_info->count++;
+		goto out;
+	}
+
+	err = e2fsck_dir_info_min_larger_equal(dir_info, ino, &index);
+	if (err >= 0 && array[index].ino == ino) {
+		dir = &array[index];
+		goto out;
+	}
+	if (err < 0) {
+		dir = &array[array_count];
+		dir_info->count++;
+		goto out;
+	}
+
+	dir = &array[index];
+	memmove((char *)dir + dir_size, dir, dir_size * (array_count - index));
+	dir_info->count++;
+out:
+	dir->ino = ino;
+	dir->dotdot = parent;
+	dir->parent = parent;
+}
+
 /*
  * This subroutine is called during pass1 to create a directory info
  * entry.  During pass1, the passed-in parent is 0; it will get filled
@@ -171,30 +270,7 @@ void e2fsck_add_dir_info(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent)
 	}
 #endif
 
-	/*
-	 * Normally, add_dir_info is called with each inode in
-	 * sequential order; but once in a while (like when pass 3
-	 * needs to recreate the root directory or lost+found
-	 * directory) it is called out of order.  In those cases, we
-	 * need to move the dir_info entries down to make room, since
-	 * the dir_info array needs to be sorted by inode number for
-	 * get_dir_info()'s sake.
-	 */
-	if (ctx->dir_info->count &&
-	    ctx->dir_info->array[ctx->dir_info->count-1].ino >= ino) {
-		for (i = ctx->dir_info->count-1; i > 0; i--)
-			if (ctx->dir_info->array[i-1].ino < ino)
-				break;
-		dir = &ctx->dir_info->array[i];
-		if (dir->ino != ino)
-			for (j = ctx->dir_info->count++; j > i; j--)
-				ctx->dir_info->array[j] = ctx->dir_info->array[j-1];
-	} else
-		dir = &ctx->dir_info->array[ctx->dir_info->count++];
-
-	dir->ino = ino;
-	dir->dotdot = parent;
-	dir->parent = parent;
+	e2fsck_insert_dir_info(ctx->dir_info, ino, parent);
 }
 
 /*
@@ -204,7 +280,8 @@ void e2fsck_add_dir_info(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent)
 static struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino)
 {
 	struct dir_info_db	*db = ctx->dir_info;
-	ext2_ino_t low, high, mid;
+	ext2_ino_t		index;
+	int			err;
 
 	if (!db)
 		return 0;
@@ -245,44 +322,19 @@ static struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino)
 	if (db->last_lookup && db->last_lookup->ino == ino)
 		return db->last_lookup;
 
-	low = 0;
-	high = ctx->dir_info->count - 1;
-	if (ino == ctx->dir_info->array[low].ino) {
+	err = e2fsck_dir_info_min_larger_equal(ctx->dir_info, ino, &index);
+	if (err < 0)
+		return NULL;
+	assert(ino <= ctx->dir_info->array[index].ino);
+	if (ino == ctx->dir_info->array[index].ino) {
 #ifdef DIRINFO_DEBUG
-		printf("(%u,%u,%u)\n", ino,
-		       ctx->dir_info->array[low].dotdot,
-		       ctx->dir_info->array[low].parent);
+		printf("(%d,%d,%d)\n", ino,
+		       ctx->dir_info->array[index].dotdot,
+		       ctx->dir_info->array[index].parent);
 #endif
-		return &ctx->dir_info->array[low];
+		return &ctx->dir_info->array[index];
 	}
-	if (ino == ctx->dir_info->array[high].ino) {
-#ifdef DIRINFO_DEBUG
-		printf("(%u,%u,%u)\n", ino,
-		       ctx->dir_info->array[high].dotdot,
-		       ctx->dir_info->array[high].parent);
-#endif
-		return &ctx->dir_info->array[high];
-	}
-
-	while (low < high) {
-		/* sum may overflow, but result will fit into mid again */
-		mid = (unsigned long long)(low + high) / 2;
-		if (mid == low || mid == high)
-			break;
-		if (ino == ctx->dir_info->array[mid].ino) {
-#ifdef DIRINFO_DEBUG
-			printf("(%u,%u,%u)\n", ino,
-			       ctx->dir_info->array[mid].dotdot,
-			       ctx->dir_info->array[mid].parent);
-#endif
-			return &ctx->dir_info->array[mid];
-		}
-		if (ino < ctx->dir_info->array[mid].ino)
-			high = mid;
-		else
-			low = mid;
-	}
-	return 0;
+	return NULL;
 }
 
 static void e2fsck_put_dir_info(e2fsck_t ctx EXT2FS_NO_TDB_UNUSED,
Signature · 1 line
2.37.3
#63 lore raw

RFC 34/72 e2fsck: merge dir_info after thread finishes

In reply to Ritesh Harjani (IBM)

From: Li Xi <[email protected]>

dir_info need be merged after thread finish.

---
 e2fsck/dirinfo.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++
 e2fsck/e2fsck.h  |  2 ++
 e2fsck/pass1.c   | 53 +++++++++++++++++++++++++++++++++++++-
 3 files changed, 120 insertions(+), 1 deletion(-)
e2fsck/dirinfo.c +66 -0
index 5c360a90..ee8d8a69 100644
@@ -169,6 +169,72 @@ e2fsck_dir_info_min_larger_equal(struct dir_info_db *dir_info,
 	return -ENOENT;
 }
 
+/*
+ * Merge two sorted dir info to @dest
+ */
+void e2fsck_merge_dir_info(e2fsck_t ctx, struct dir_info_db *src,
+			   struct dir_info_db *dest)
+{
+	size_t		 size_dir_info = sizeof(struct dir_info);
+	ext2_ino_t	 size = dest->size;
+	struct dir_info	 *src_array = src->array;
+	struct dir_info	 *dest_array = dest->array;
+	ext2_ino_t	 src_count = src->count;
+	ext2_ino_t	 dest_count = dest->count;
+	ext2_ino_t	 total_count = src_count + dest_count;
+	struct dir_info	*tmp_array;
+	struct dir_info	*array_ptr;
+	ext2_ino_t	 src_index = 0;
+	ext2_ino_t	 dest_index = 0;
+
+	if (src->count == 0)
+		return;
+
+	if (size < total_count)
+		size = total_count;
+
+	if (size < src->size)
+		size = src->size;
+
+	tmp_array = e2fsck_allocate_memory(ctx, size * size_dir_info,
+					    "directory map");
+	array_ptr = tmp_array;
+	/*
+	 * This can be improved by binary search and memcpy, but codes
+	 * would be more complex. And if the groups distributed to each
+	 * thread are strided, this implementation won't be too bad
+	 * comparing to the optimiztion.
+	 */
+	while (src_index < src_count || dest_index < dest_count) {
+		if (src_index >= src_count) {
+			memcpy(array_ptr, &dest_array[dest_index],
+			       (dest_count - dest_index) * size_dir_info);
+			break;
+		}
+		if (dest_index >= dest_count) {
+			memcpy(array_ptr, &src_array[src_index],
+			       (src_count - src_index) * size_dir_info);
+			break;
+		}
+		if (src_array[src_index].ino < dest_array[dest_index].ino) {
+			*array_ptr = src_array[src_index];
+			src_index++;
+		} else {
+			assert(src_array[src_index].ino >
+			       dest_array[dest_index].ino);
+			*array_ptr = dest_array[dest_index];
+			dest_index++;
+		}
+		array_ptr++;
+	}
+
+	if (dest->array)
+		ext2fs_free_mem(&dest->array);
+	dest->array = tmp_array;
+	dest->size = size;
+	dest->count = total_count;
+}
+
 /*
  *
  * Insert an inode into the sorted array. The array should have at least one
e2fsck/e2fsck.h +2 -0
index cdd158cc..2ee37f78 100644
@@ -546,6 +546,8 @@ extern void read_bad_blocks_file(e2fsck_t ctx, const char *bad_blocks_file,
 
 /* dirinfo.c */
 extern void e2fsck_add_dir_info(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent);
+void e2fsck_merge_dir_info(e2fsck_t ctx, struct dir_info_db *src,
+                           struct dir_info_db *dest);
 extern void e2fsck_free_dir_info(e2fsck_t ctx);
 extern int e2fsck_get_num_dirinfo(e2fsck_t ctx);
 extern struct dir_info_iter *e2fsck_dir_info_iter_begin(e2fsck_t ctx);
e2fsck/pass1.c +52 -1

87 lines not on this page · raw message

Signature · 1 line
2.37.3
#64 lore raw

RFC 35/72 e2fsck: rbtree bitmap for dir

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

Only rbtree support merge operation now, use it for bitmaps.

---
 e2fsck/pass1.c | 1 +
 1 file changed, 1 insertion(+)
e2fsck/pass1.c +1 -0
index 57003d8c..4d98c467 100644
@@ -1240,6 +1240,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
 	}
 	pctx.errcode = e2fsck_allocate_inode_bitmap(fs,
 			_("directory inode map"),
+			ctx->global_ctx ? EXT2FS_BMAP64_RBTREE :
 			EXT2FS_BMAP64_AUTODIR,
 			"inode_dir_map", &ctx->inode_dir_map);
 	if (pctx.errcode) {
Signature · 1 line
2.37.3
#65 lore raw

RFC 36/72 e2fsck: merge icounts after thread finishes

In reply to Ritesh Harjani (IBM)

From: Li Xi <[email protected]>

Merge inode_count and inode_link_info properly after threads finish.

---
 e2fsck/pass1.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)
e2fsck/pass1.c +48 -0
index 4d98c467..18bf7efd 100644
@@ -2341,6 +2341,41 @@ static void e2fsck_pass1_merge_dir_info(e2fsck_t global_ctx, e2fsck_t thread_ctx
 			      global_ctx->dir_info);
 }
 
+static inline errcode_t
+e2fsck_pass1_merge_icount(ext2_icount_t *dest_icount,
+			  ext2_icount_t *src_icount)
+{
+	if (*src_icount) {
+		if (*dest_icount == NULL) {
+			*dest_icount = *src_icount;
+			*src_icount = NULL;
+		} else {
+			errcode_t ret;
+
+			ret = ext2fs_icount_merge(*src_icount,
+						  *dest_icount);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
+static errcode_t e2fsck_pass1_merge_icounts(e2fsck_t global_ctx, e2fsck_t thread_ctx)
+{
+	errcode_t ret;
+
+	ret = e2fsck_pass1_merge_icount(&global_ctx->inode_count,
+					&thread_ctx->inode_count);
+	if (ret)
+		return ret;
+	ret = e2fsck_pass1_merge_icount(&global_ctx->inode_link_info,
+					&thread_ctx->inode_link_info);
+
+	return ret;
+}
+
 static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx)
 {
 	errcode_t retval = 0;
@@ -2361,6 +2396,8 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 	ext2fs_block_bitmap block_ea_map = global_ctx->block_ea_map;
 	ext2fs_block_bitmap block_metadata_map = global_ctx->block_metadata_map;
 	ext2fs_block_bitmap inodes_to_rebuild = global_ctx->inodes_to_rebuild;
+	ext2_icount_t inode_count = global_ctx->inode_count;
+	ext2_icount_t inode_link_info = global_ctx->inode_link_info;
 
 #ifdef HAVE_SETJMP_H
 	jmp_buf old_jmp;
@@ -2385,6 +2422,8 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 	global_ctx->block_metadata_map = block_metadata_map;
 	global_ctx->dir_info = dir_info;
 	e2fsck_pass1_merge_dir_info(global_ctx, thread_ctx);
+	global_ctx->inode_count = inode_count;
+	global_ctx->inode_link_info = inode_link_info;
 
 	/* Keep the global singal flags*/
 	global_ctx->flags |= (flags & E2F_FLAG_SIGNAL_MASK) |
@@ -2396,6 +2435,13 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 	global_fs->priv_data = global_ctx;
 	global_ctx->fs = global_fs;
 
+	retval = e2fsck_pass1_merge_icounts(global_ctx, thread_ctx);
+	if (retval) {
+		com_err(global_ctx->program_name, 0,
+			_("while merging icounts\n"));
+		return retval;
+	}
+
 	retval = ext2fs_merge_fs(&(thread_ctx->fs));
 	if (retval) {
 		com_err(global_ctx->program_name, 0, _("while merging fs\n"));
@@ -2478,6 +2524,8 @@ static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
 	e2fsck_pass1_free_bitmap(&thread_ctx->block_ea_map);
 	e2fsck_pass1_free_bitmap(&thread_ctx->block_metadata_map);
 	e2fsck_free_dir_info(thread_ctx);
+	ext2fs_free_icount(thread_ctx->inode_count);
+	ext2fs_free_icount(thread_ctx->inode_link_info);
 
 	if (thread_ctx->logf)
 		fclose(thread_ctx->logf);
Signature · 1 line
2.37.3
#66 lore raw

RFC 37/72 e2fsck: add debug codes for multiple threads

In reply to Ritesh Harjani (IBM)

From: Li Xi <[email protected]>

These debug codes are added to run the multiple pass1 check thread one by one in order. If all the codes are correct, fsck of multiple threads should have exactly the same outcome with single thread.

---
 e2fsck/e2fsck.h | 16 ++++++++++++++++
 e2fsck/pass1.c  | 29 +++++++++++++++++++++++++++++
 2 files changed, 45 insertions(+)
e2fsck/e2fsck.h +16 -0
index 2ee37f78..9b0f5067 100644
@@ -489,6 +489,18 @@ struct e2fsck_struct {
 };
 
 #ifdef HAVE_PTHREAD
+#ifdef DEBUG_THREADS
+/*
+ * Enabling DEBUG_THREADS would cause the parallel
+ * fsck threads run sequentially.
+ */
+struct e2fsck_thread_debug {
+	pthread_mutex_t	etd_mutex;
+	pthread_cond_t	etd_cond;
+	int		etd_finished_threads;
+};
+#endif
+
 struct e2fsck_thread_info {
 	/* ID returned by pthread_create() */
 	pthread_t		 eti_thread_id;
@@ -498,7 +510,11 @@ struct e2fsck_thread_info {
 	int			 eti_started;
 	/* Context used for this thread */
 	e2fsck_t		 eti_thread_ctx;
+#ifdef DEBUG_THREADS
+	struct e2fsck_thread_debug	*eti_debug;
+#endif
 };
+
 #endif
 
 /* Data structures to evaluate whether an extent tree needs rebuilding. */
e2fsck/pass1.c +29 -0
index 18bf7efd..752dca03 100644
@@ -2575,6 +2575,18 @@ static void *e2fsck_pass1_thread(void *arg)
 {
 	struct e2fsck_thread_info *info = arg;
 	e2fsck_t thread_ctx = info->eti_thread_ctx;
+#ifdef DEBUG_THREADS
+	struct e2fsck_thread_debug *thread_debug = info->eti_debug;
+#endif
+
+#ifdef DEBUG_THREADS
+	pthread_mutex_lock(&thread_debug->etd_mutex);
+	while (info->eti_thread_index > thread_debug->etd_finished_threads) {
+		pthread_cond_wait(&thread_debug->etd_cond,
+				  &thread_debug->etd_mutex);
+	}
+	pthread_mutex_unlock(&thread_debug->etd_mutex);
+#endif
 
 #ifdef HAVE_SETJMP_H
 	/*
@@ -2599,6 +2611,14 @@ out:
 			thread_ctx->thread_info.et_group_start,
 			thread_ctx->thread_info.et_group_end,
 			thread_ctx->thread_info.et_inode_number);
+
+#ifdef DEBUG_THREADS
+	pthread_mutex_lock(&thread_debug->etd_mutex);
+	thread_debug->etd_finished_threads++;
+	pthread_cond_broadcast(&thread_debug->etd_cond);
+	pthread_mutex_unlock(&thread_debug->etd_mutex);
+#endif
+
 	return NULL;
 }
 
@@ -2612,6 +2632,12 @@ static int e2fsck_pass1_threads_start(struct e2fsck_thread_info **pinfo,
 	struct e2fsck_thread_info *tmp_pinfo;
 	int i;
 	e2fsck_t thread_ctx;
+#ifdef DEBUG_THREADS
+	struct e2fsck_thread_debug thread_debug =
+		{PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};
+
+	thread_debug.etd_finished_threads = 0;
+#endif
 
 	retval = pthread_attr_init(&attr);
 	if (retval) {
@@ -2632,6 +2658,9 @@ static int e2fsck_pass1_threads_start(struct e2fsck_thread_info **pinfo,
 	for (i = 0; i < num_threads; i++) {
 		tmp_pinfo = &infos[i];
 		tmp_pinfo->eti_thread_index = i;
+#ifdef DEBUG_THREADS
+		tmp_pinfo->eti_debug = &thread_debug;
+#endif
 		retval = e2fsck_pass1_thread_prepare(global_ctx, &thread_ctx,
 						     i, num_threads);
 		if (retval) {
Signature · 1 line
2.37.3
#67 lore raw

RFC 38/72 e2fsck: merge counts after threads finish

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

Merge counts properly.

---
 e2fsck/pass1.c | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)
e2fsck/pass1.c +34 -0
index 752dca03..8b502307 100644
@@ -2398,6 +2398,23 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 	ext2fs_block_bitmap inodes_to_rebuild = global_ctx->inodes_to_rebuild;
 	ext2_icount_t inode_count = global_ctx->inode_count;
 	ext2_icount_t inode_link_info = global_ctx->inode_link_info;
+	__u32 fs_directory_count = global_ctx->fs_directory_count;
+	__u32 fs_regular_count = global_ctx->fs_regular_count;
+	__u32 fs_blockdev_count = global_ctx->fs_blockdev_count;
+	__u32 fs_chardev_count = global_ctx->fs_chardev_count;
+	__u32 fs_links_count = global_ctx->fs_links_count;
+	__u32 fs_symlinks_count = global_ctx->fs_symlinks_count;
+	__u32 fs_fast_symlinks_count = global_ctx->fs_fast_symlinks_count;
+	__u32 fs_fifo_count = global_ctx->fs_fifo_count;
+	__u32 fs_total_count = global_ctx->fs_total_count;
+	__u32 fs_badblocks_count = global_ctx->fs_badblocks_count;
+	__u32 fs_sockets_count = global_ctx->fs_sockets_count;
+	__u32 fs_ind_count = global_ctx->fs_ind_count;
+	__u32 fs_dind_count = global_ctx->fs_dind_count;
+	__u32 fs_tind_count = global_ctx->fs_tind_count;
+	__u32 fs_fragmented = global_ctx->fs_fragmented;
+	__u32 fs_fragmented_dir = global_ctx->fs_fragmented_dir;
+	__u32 large_files = global_ctx->large_files;
 
 #ifdef HAVE_SETJMP_H
 	jmp_buf old_jmp;
@@ -2424,6 +2441,23 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 	e2fsck_pass1_merge_dir_info(global_ctx, thread_ctx);
 	global_ctx->inode_count = inode_count;
 	global_ctx->inode_link_info = inode_link_info;
+	global_ctx->fs_directory_count += fs_directory_count;
+	global_ctx->fs_regular_count += fs_regular_count;
+	global_ctx->fs_blockdev_count += fs_blockdev_count;
+	global_ctx->fs_chardev_count += fs_chardev_count;
+	global_ctx->fs_links_count += fs_links_count;
+	global_ctx->fs_symlinks_count += fs_symlinks_count;
+	global_ctx->fs_fast_symlinks_count += fs_fast_symlinks_count;
+	global_ctx->fs_fifo_count += fs_fifo_count;
+	global_ctx->fs_total_count += fs_total_count;
+	global_ctx->fs_badblocks_count += fs_badblocks_count;
+	global_ctx->fs_sockets_count += fs_sockets_count;
+	global_ctx->fs_ind_count += fs_ind_count;
+	global_ctx->fs_dind_count += fs_dind_count;
+	global_ctx->fs_tind_count += fs_tind_count;
+	global_ctx->fs_fragmented += fs_fragmented;
+	global_ctx->fs_fragmented_dir += fs_fragmented_dir;
+	global_ctx->large_files += large_files;
 
 	/* Keep the global singal flags*/
 	global_ctx->flags |= (flags & E2F_FLAG_SIGNAL_MASK) |
Signature · 1 line
2.37.3
#68 lore raw

RFC 39/72 e2fsck: merge dx_dir_info after threads finish

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

Merge properly.

---
 e2fsck/dx_dirinfo.c | 64 +++++++++++++++++++++++++++++++++++++++++++++
 e2fsck/e2fsck.h     |  1 +
 e2fsck/pass1.c      | 23 ++++++++++++++++
 3 files changed, 88 insertions(+)
e2fsck/dx_dirinfo.c +64 -0
index caca3e30..91954572 100644
@@ -5,6 +5,7 @@
  * under the terms of the GNU Public License.
  */
 
+#include <assert.h>
 #include "config.h"
 #include "e2fsck.h"
 
@@ -79,6 +80,69 @@ void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, struct ext2_inode *inode,
 				       "dx_block info array");
 }
 
+/*
+ * Merge two sorted dir info to @dest
+ */
+void e2fsck_merge_dx_dir(e2fsck_t global_ctx, e2fsck_t thread_ctx)
+{
+	struct dx_dir_info *src_array = thread_ctx->dx_dir_info;
+	struct dx_dir_info *dest_array = global_ctx->dx_dir_info;
+	size_t size_dx_info = sizeof(struct dx_dir_info);
+	ext2_ino_t size = global_ctx->dx_dir_info_size;
+	ext2_ino_t src_count = thread_ctx->dx_dir_info_count;
+	ext2_ino_t dest_count = global_ctx->dx_dir_info_count;
+	ext2_ino_t total_count = src_count + dest_count;
+	struct dx_dir_info *array;
+	struct dx_dir_info *array_ptr;
+	ext2_ino_t src_index = 0, dest_index = 0;
+
+	if (thread_ctx->dx_dir_info_count == 0)
+		return;
+
+	if (size < total_count)
+		size = total_count;
+
+	array = e2fsck_allocate_memory(global_ctx, size * size_dx_info,
+				       "directory map");
+	array_ptr = array;
+	/*
+	 * This can be improved by binary search and memcpy, but codes
+	 * would be more complex. And if the groups distributed to each
+	 * thread are strided, this implementation won't be too bad
+	 * comparing to the optimiztion.
+	 */
+	while (src_index < src_count || dest_index < dest_count) {
+		if (src_index >= src_count) {
+			memcpy(array_ptr, &dest_array[dest_index],
+			       (dest_count - dest_index) * size_dx_info);
+			break;
+		}
+		if (dest_index >= dest_count) {
+			memcpy(array_ptr, &src_array[src_index],
+			       (src_count - src_index) * size_dx_info);
+			break;
+		}
+		if (src_array[src_index].ino < dest_array[dest_index].ino) {
+			*array_ptr = src_array[src_index];
+			src_index++;
+		} else {
+			assert(src_array[src_index].ino >
+			       dest_array[dest_index].ino);
+			*array_ptr = dest_array[dest_index];
+			dest_index++;
+		}
+		array_ptr++;
+	}
+
+	if (global_ctx->dx_dir_info)
+		ext2fs_free_mem(&global_ctx->dx_dir_info);
+	if (thread_ctx->dx_dir_info)
+		ext2fs_free_mem(&thread_ctx->dx_dir_info);
+	global_ctx->dx_dir_info = array;
+	global_ctx->dx_dir_info_size = size;
+	global_ctx->dx_dir_info_count = total_count;
+}
+
 /*
  * get_dx_dir_info() --- given an inode number, try to find the directory
  * information entry for it.
e2fsck/e2fsck.h +1 -0
index 9b0f5067..26c3b8a5 100644
@@ -578,6 +578,7 @@ extern int e2fsck_dir_info_get_parent(e2fsck_t ctx, ext2_ino_t ino,
 				      ext2_ino_t *parent);
 extern int e2fsck_dir_info_get_dotdot(e2fsck_t ctx, ext2_ino_t ino,
 				      ext2_ino_t *dotdot);
+extern void e2fsck_merge_dx_dir(e2fsck_t global_ctx, e2fsck_t thread_ctx);
 
 /* dx_dirinfo.c */
 extern void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino,
e2fsck/pass1.c +23 -0
index 8b502307..f998590e 100644
@@ -2341,6 +2341,22 @@ static void e2fsck_pass1_merge_dir_info(e2fsck_t global_ctx, e2fsck_t thread_ctx
 			      global_ctx->dir_info);
 }
 
+static void e2fsck_pass1_merge_dx_dir(e2fsck_t global_ctx, e2fsck_t thread_ctx)
+{
+	if (thread_ctx->dx_dir_info == NULL)
+		return;
+
+	if (global_ctx->dx_dir_info == NULL) {
+		global_ctx->dx_dir_info = thread_ctx->dx_dir_info;
+		global_ctx->dx_dir_info_size = thread_ctx->dx_dir_info_size;
+		global_ctx->dx_dir_info_count = thread_ctx->dx_dir_info_count;
+		thread_ctx->dx_dir_info = NULL;
+		return;
+	}
+
+	e2fsck_merge_dx_dir(global_ctx, thread_ctx);
+}
+
 static inline errcode_t
 e2fsck_pass1_merge_icount(ext2_icount_t *dest_icount,
 			  ext2_icount_t *src_icount)
@@ -2386,6 +2402,7 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 	ext2_filsys global_fs = global_ctx->fs;
 	ext2fs_inode_bitmap inode_bad_map = global_ctx->inode_bad_map;
 	struct dir_info_db *dir_info = global_ctx->dir_info;
+	struct dx_dir_info *dx_dir_info = global_ctx->dx_dir_info;
 	ext2fs_inode_bitmap inode_used_map = global_ctx->inode_used_map;
 	ext2fs_inode_bitmap inode_dir_map = global_ctx->inode_dir_map;
 	ext2fs_inode_bitmap inode_bb_map = global_ctx->inode_bb_map;
@@ -2415,6 +2432,8 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 	__u32 fs_fragmented = global_ctx->fs_fragmented;
 	__u32 fs_fragmented_dir = global_ctx->fs_fragmented_dir;
 	__u32 large_files = global_ctx->large_files;
+	ext2_ino_t dx_dir_info_size = global_ctx->dx_dir_info_size;
+	ext2_ino_t dx_dir_info_count = global_ctx->dx_dir_info_count;
 
 #ifdef HAVE_SETJMP_H
 	jmp_buf old_jmp;
@@ -2439,6 +2458,10 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 	global_ctx->block_metadata_map = block_metadata_map;
 	global_ctx->dir_info = dir_info;
 	e2fsck_pass1_merge_dir_info(global_ctx, thread_ctx);
+	global_ctx->dx_dir_info = dx_dir_info;
+	global_ctx->dx_dir_info_count = dx_dir_info_count;
+	global_ctx->dx_dir_info_size = dx_dir_info_size;
+	e2fsck_pass1_merge_dx_dir(global_ctx, thread_ctx);
 	global_ctx->inode_count = inode_count;
 	global_ctx->inode_link_info = inode_link_info;
 	global_ctx->fs_directory_count += fs_directory_count;
Signature · 1 line
2.37.3
#69 lore raw

RFC 40/72 e2fsck: merge dirs_to_hash when threads finish

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

@dirs_to_hash list need be merged after threads finish, test covered by t_dangerous.

---
 e2fsck/pass1.c | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)
e2fsck/pass1.c +30 -0
index f998590e..a3f13402 100644
@@ -2392,6 +2392,25 @@ static errcode_t e2fsck_pass1_merge_icounts(e2fsck_t global_ctx, e2fsck_t thread
 	return ret;
 }
 
+static errcode_t e2fsck_pass1_merge_dirs_to_hash(e2fsck_t global_ctx,
+						 e2fsck_t thread_ctx)
+{
+	errcode_t retval = 0;
+
+	if (!thread_ctx->dirs_to_hash)
+		return 0;
+
+	if (!global_ctx->dirs_to_hash)
+		retval = ext2fs_badblocks_copy(thread_ctx->dirs_to_hash,
+					       &global_ctx->dirs_to_hash);
+	else
+		retval = ext2fs_badblocks_merge(thread_ctx->dirs_to_hash,
+						global_ctx->dirs_to_hash);
+
+	return retval;
+}
+
+
 static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx)
 {
 	errcode_t retval = 0;
@@ -2434,6 +2453,7 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 	__u32 large_files = global_ctx->large_files;
 	ext2_ino_t dx_dir_info_size = global_ctx->dx_dir_info_size;
 	ext2_ino_t dx_dir_info_count = global_ctx->dx_dir_info_count;
+	ext2_u32_list dirs_to_hash = global_ctx->dirs_to_hash;
 
 #ifdef HAVE_SETJMP_H
 	jmp_buf old_jmp;
@@ -2504,6 +2524,14 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 		com_err(global_ctx->program_name, 0, _("while merging fs\n"));
 		return retval;
 	}
+	global_ctx->dirs_to_hash = dirs_to_hash;
+	retval = e2fsck_pass1_merge_dirs_to_hash(global_ctx, thread_ctx);
+	if (retval) {
+		com_err(global_ctx->program_name, 0,
+			_("while merging dirs to hash\n"));
+		return retval;
+	}
+
 	retval = e2fsck_pass1_merge_bitmap(global_fs,
 				&thread_ctx->inode_used_map,
 				&global_ctx->inode_used_map);
@@ -2583,6 +2611,8 @@ static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
 	e2fsck_free_dir_info(thread_ctx);
 	ext2fs_free_icount(thread_ctx->inode_count);
 	ext2fs_free_icount(thread_ctx->inode_link_info);
+	if (thread_ctx->dirs_to_hash)
+		ext2fs_badblocks_list_free(thread_ctx->dirs_to_hash);
 
 	if (thread_ctx->logf)
 		fclose(thread_ctx->logf);
Signature · 1 line
2.37.3
#70 lore raw

RFC 41/72 e2fsck: merge context flags properly

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

e2fsck might restart after pass1, so we should keep flags if possible, this patch try to fix f_illitable_flexbg failure

---
 e2fsck/pass1.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)
e2fsck/pass1.c +1 -3
index a3f13402..7e167189 100644
@@ -2502,9 +2502,7 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 	global_ctx->fs_fragmented_dir += fs_fragmented_dir;
 	global_ctx->large_files += large_files;
 
-	/* Keep the global singal flags*/
-	global_ctx->flags |= (flags & E2F_FLAG_SIGNAL_MASK) |
-			     (global_ctx->flags & E2F_FLAG_SIGNAL_MASK);
+	global_ctx->flags |= flags;
 	global_ctx->logf = global_logf;
 	global_ctx->problem_logf = global_problem_logf;
 	global_ctx->global_ctx = NULL;
Signature · 1 line
2.37.3
#71 lore raw

RFC 42/72 e2fsck: merge quota context after threads finish

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

Every threads calculate its own quota accounting,
merge them after threads finish.

---
 e2fsck/pass1.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
e2fsck/pass1.c +14 -0
index 7e167189..213c1a51 100644
@@ -2317,6 +2317,12 @@ static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thre
 	if (thread_context->options & E2F_OPT_MULTITHREAD)
 		log_out(thread_context, _("Scan group range [%d, %d)\n"),
 			tinfo->et_group_start, tinfo->et_group_end);
+	retval = quota_init_context(&thread_context->qctx, thread_fs, 0);
+	if (retval) {
+		com_err(global_ctx->program_name, retval,
+			"while init quota context");
+		goto out_fs;
+	}
 	*thread_ctx = thread_context;
 	return 0;
 out_fs:
@@ -2454,6 +2460,7 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 	ext2_ino_t dx_dir_info_size = global_ctx->dx_dir_info_size;
 	ext2_ino_t dx_dir_info_count = global_ctx->dx_dir_info_count;
 	ext2_u32_list dirs_to_hash = global_ctx->dirs_to_hash;
+	quota_ctx_t qctx = global_ctx->qctx;
 
 #ifdef HAVE_SETJMP_H
 	jmp_buf old_jmp;
@@ -2530,6 +2537,12 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 		return retval;
 	}
 
+	global_ctx->qctx = qctx;
+	retval = quota_merge_and_update_usage(global_ctx->qctx,
+					      thread_ctx->qctx);
+	if (retval)
+		return retval;
+
 	retval = e2fsck_pass1_merge_bitmap(global_fs,
 				&thread_ctx->inode_used_map,
 				&global_ctx->inode_used_map);
@@ -2611,6 +2624,7 @@ static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
 	ext2fs_free_icount(thread_ctx->inode_link_info);
 	if (thread_ctx->dirs_to_hash)
 		ext2fs_badblocks_list_free(thread_ctx->dirs_to_hash);
+	quota_release_context(&thread_ctx->qctx);
 
 	if (thread_ctx->logf)
 		fclose(thread_ctx->logf);
Signature · 1 line
2.37.3
#72 lore raw

RFC 43/72 e2fsck: serialize fix operations

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

Allow different threads to fix at the same time could be dangerous and error-prone now, and most of time parallel scanning and checking is important.

So this patch adds a mutex to serialize
fix operations during pass1.

And the good benefit of this, we don't need block allocations and free, superblock updates protection any more, since only fix operations during pass1 could touch them.

---
 e2fsck/e2fsck.h |  6 +++++
 e2fsck/pass1.c  | 63 +++++++++++++++++++++++++++++++++++++++++++------
 e2fsck/util.c   | 38 +++++++++++++++++++++++++++++
 3 files changed, 100 insertions(+), 7 deletions(-)
e2fsck/e2fsck.h +6 -0
index 26c3b8a5..5356e172 100644
@@ -484,6 +484,10 @@ struct e2fsck_struct {
 	/* Undo file */
 	char *undo_file;
 
+#ifdef HAVE_PTHREAD
+	/* serialize fix operation for multiple threads */
+	pthread_mutex_t		 fs_fix_mutex;
+#endif
 	/* Fast commit replay state */
 	struct e2fsck_fc_replay_state fc_replay_state;
 };
@@ -787,6 +791,8 @@ extern errcode_t e2fsck_allocate_subcluster_bitmap(ext2_filsys fs,
 						   const char *profile_name,
 						   ext2fs_block_bitmap *ret);
 unsigned long long get_memory_size(void);
+extern void e2fsck_pass1_fix_lock(e2fsck_t ctx);
+extern void e2fsck_pass1_fix_unlock(e2fsck_t ctx);
 
 /* unix.c */
 extern void e2fsck_clear_progbar(e2fsck_t ctx);
e2fsck/pass1.c +56 -7

217 lines not on this page · raw message

e2fsck/util.c +38 -0
index 254b4d04..9470068f 100644
@@ -38,6 +38,10 @@
 #include <errno.h>
 #endif
 
+#ifdef HAVE_PTHREAD
+#include <pthread.h>
+#endif
+
 #include "e2fsck.h"
 
 extern e2fsck_t e2fsck_global_ctx;   /* Try your very best not to use this! */
@@ -570,13 +574,45 @@ void e2fsck_read_inode_full(e2fsck_t ctx, unsigned long ino,
 	}
 }
 
+#ifdef HAVE_PTHREAD
+void e2fsck_pass1_fix_lock(e2fsck_t ctx)
+{
+	e2fsck_t global_ctx = ctx->global_ctx;
+	if (!global_ctx)
+		global_ctx = ctx;
+
+	pthread_mutex_lock(&global_ctx->fs_fix_mutex);
+}
+
+void e2fsck_pass1_fix_unlock(e2fsck_t ctx)
+{
+	e2fsck_t global_ctx = ctx->global_ctx;
+	if (!global_ctx)
+		global_ctx = ctx;
+
+	pthread_mutex_unlock(&global_ctx->fs_fix_mutex);
+}
+#else
+void e2fsck_pass1_fix_lock(e2fsck_t ctx)
+{
+
+}
+
+void e2fsck_pass1_fix_unlock(e2fsck_t ctx)
+{
+
+}
+#endif
+
 void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
 			     struct ext2_inode * inode, int bufsize,
 			     const char *proc)
 {
 	errcode_t retval;
 
+	e2fsck_pass1_fix_lock(ctx);
 	retval = ext2fs_write_inode_full(ctx->fs, ino, inode, bufsize);
+	e2fsck_pass1_fix_unlock(ctx);
 	if (retval) {
 		com_err("ext2fs_write_inode", retval,
 			_("while writing inode %lu in %s"), ino, proc);
@@ -589,7 +625,9 @@ void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
 {
 	errcode_t retval;
 
+	e2fsck_pass1_fix_lock(ctx);
 	retval = ext2fs_write_inode(ctx->fs, ino, inode);
+	e2fsck_pass1_fix_unlock(ctx);
 	if (retval) {
 		com_err("ext2fs_write_inode", retval,
 			_("while writing inode %lu in %s"), ino, proc);
Signature · 1 line
2.37.3
#73 lore raw

RFC 44/72 e2fsck: move some fixes out of parallel pthreads

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

We could only use @found_map_block to find free blocks after we have collectd all used blocks, so something like handle_fs_bad_blocks(), ext2fs_create_resize_inode(), e2fsck_pass1_dupblocks() really should be handled after all threads has been finished.

---
 e2fsck/e2fsck.h                      |   4 +
 e2fsck/pass1.c                       | 296 ++++++++++++++++-----------
 e2fsck/util.c                        |  36 +++-
 tests/f_multithread/expect.1         |   2 +-
 tests/f_multithread_logfile/expect.1 |   2 +-
 tests/f_multithread_no/expect.1      |   2 +-
 6 files changed, 210 insertions(+), 132 deletions(-)
e2fsck/e2fsck.h +4 -0
index 5356e172..55f75042 100644
@@ -487,6 +487,8 @@ struct e2fsck_struct {
 #ifdef HAVE_PTHREAD
 	/* serialize fix operation for multiple threads */
 	pthread_mutex_t		 fs_fix_mutex;
+	/* protect block_found_map, block_dup_map */
+	pthread_mutex_t		 fs_block_map_mutex;
 #endif
 	/* Fast commit replay state */
 	struct e2fsck_fc_replay_state fc_replay_state;
@@ -793,6 +795,8 @@ extern errcode_t e2fsck_allocate_subcluster_bitmap(ext2_filsys fs,
 unsigned long long get_memory_size(void);
 extern void e2fsck_pass1_fix_lock(e2fsck_t ctx);
 extern void e2fsck_pass1_fix_unlock(e2fsck_t ctx);
+extern void e2fsck_pass1_block_map_lock(e2fsck_t ctx);
+extern void e2fsck_pass1_block_map_unlock(e2fsck_t ctx);
 
 /* unix.c */
 extern void e2fsck_clear_progbar(e2fsck_t ctx);
e2fsck/pass1.c +175 -121

533 lines not on this page · raw message

e2fsck/util.c +28 -8
index 9470068f..93cd96c7 100644
@@ -575,23 +575,34 @@ void e2fsck_read_inode_full(e2fsck_t ctx, unsigned long ino,
 }
 
 #ifdef HAVE_PTHREAD
+#define e2fsck_get_lock_context(ctx)		\
+	e2fsck_t global_ctx = ctx->global_ctx;	\
+	if (!global_ctx)			\
+		global_ctx = ctx;		\
+
 void e2fsck_pass1_fix_lock(e2fsck_t ctx)
 {
-	e2fsck_t global_ctx = ctx->global_ctx;
-	if (!global_ctx)
-		global_ctx = ctx;
-
+	e2fsck_get_lock_context(ctx);
 	pthread_mutex_lock(&global_ctx->fs_fix_mutex);
 }
 
 void e2fsck_pass1_fix_unlock(e2fsck_t ctx)
 {
-	e2fsck_t global_ctx = ctx->global_ctx;
-	if (!global_ctx)
-		global_ctx = ctx;
-
+	e2fsck_get_lock_context(ctx);
 	pthread_mutex_unlock(&global_ctx->fs_fix_mutex);
 }
+
+void e2fsck_pass1_block_map_lock(e2fsck_t ctx)
+{
+	e2fsck_get_lock_context(ctx);
+	pthread_mutex_lock(&global_ctx->fs_block_map_mutex);
+}
+
+void e2fsck_pass1_block_map_unlock(e2fsck_t ctx)
+{
+	e2fsck_get_lock_context(ctx);
+	pthread_mutex_unlock(&global_ctx->fs_block_map_mutex);
+}
 #else
 void e2fsck_pass1_fix_lock(e2fsck_t ctx)
 {
@@ -601,6 +612,15 @@ void e2fsck_pass1_fix_lock(e2fsck_t ctx)
 void e2fsck_pass1_fix_unlock(e2fsck_t ctx)
 {
 
+}
+
+void e2fsck_pass1_block_map_lock(e2fsck_t ctx)
+{
+
+}
+
+void e2fsck_pass1_block_map_unlock(e2fsck_t ctx)
+{
 }
 #endif
 
tests/f_multithread/expect.1 +1 -1
index 8d2acd2b..4db68d9e 100644
@@ -1,7 +1,7 @@
 ext2fs_open2: Bad magic number in super-block
 ../e2fsck/e2fsck: Superblock invalid, trying backup blocks...
+Pass 1: Checking inodes, blocks, and sizes
 [Thread 0] Scan group range [0, 2)
-[Thread 0] Pass 1: Checking inodes, blocks, and sizes
 [Thread 0] Scanned group range [0, 2), inodes 3008
 Pass 2: Checking directory structure
 Pass 3: Checking directory connectivity
tests/f_multithread_logfile/expect.1 +1 -1
index 8d2acd2b..4db68d9e 100644
@@ -1,7 +1,7 @@
 ext2fs_open2: Bad magic number in super-block
 ../e2fsck/e2fsck: Superblock invalid, trying backup blocks...
+Pass 1: Checking inodes, blocks, and sizes
 [Thread 0] Scan group range [0, 2)
-[Thread 0] Pass 1: Checking inodes, blocks, and sizes
 [Thread 0] Scanned group range [0, 2), inodes 3008
 Pass 2: Checking directory structure
 Pass 3: Checking directory connectivity
tests/f_multithread_no/expect.1 +1 -1
index f85a3382..eda2fcac 100644
@@ -1,7 +1,7 @@
 ext2fs_open2: Bad magic number in super-block
 ../e2fsck/e2fsck: Superblock invalid, trying backup blocks...
+Pass 1: Checking inodes, blocks, and sizes
 [Thread 0] Scan group range [0, 2)
-[Thread 0] Pass 1: Checking inodes, blocks, and sizes
 [Thread 0] Scanned group range [0, 2), inodes 3008
 Pass 2: Checking directory structure
 Pass 3: Checking directory connectivity
Signature · 1 line
2.37.3
#74 lore raw

RFC 45/72 e2fsck: split and merge invalid bitmaps

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

Invalid bitmaps are splitted per thread, and we
should merge them after thread finish.

---
 e2fsck/pass1.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 71 insertions(+)
e2fsck/pass1.c +71 -0
index f156d4e1..e268441a 100644
@@ -2309,6 +2309,62 @@ static errcode_t e2fsck_open_channel_fs(ext2_filsys dest, e2fsck_t dest_context,
 	return 0;
 }
 
+static void e2fsck_pass1_copy_invalid_bitmaps(e2fsck_t global_ctx,
+					      e2fsck_t thread_ctx)
+{
+	dgrp_t i, j;
+	dgrp_t grp_start = thread_ctx->thread_info.et_group_start;
+	dgrp_t grp_end = thread_ctx->thread_info.et_group_end;
+	dgrp_t total = grp_end - grp_start;
+
+	thread_ctx->invalid_inode_bitmap_flag =
+			e2fsck_allocate_memory(global_ctx, sizeof(int) * total,
+						"invalid_inode_bitmap");
+	thread_ctx->invalid_block_bitmap_flag =
+			e2fsck_allocate_memory(global_ctx, sizeof(int) * total,
+					       "invalid_block_bitmap");
+	thread_ctx->invalid_inode_table_flag =
+			e2fsck_allocate_memory(global_ctx, sizeof(int) * total,
+					       "invalid_inode_table");
+
+	memcpy(thread_ctx->invalid_block_bitmap_flag,
+	       &global_ctx->invalid_block_bitmap_flag[grp_start],
+	       total * sizeof(int));
+	memcpy(thread_ctx->invalid_inode_bitmap_flag,
+	       &global_ctx->invalid_inode_bitmap_flag[grp_start],
+	       total * sizeof(int));
+	memcpy(thread_ctx->invalid_inode_table_flag,
+	       &global_ctx->invalid_inode_table_flag[grp_start],
+	       total * sizeof(int));
+
+	thread_ctx->invalid_bitmaps = 0;
+	for (i = grp_start, j = 0; i < grp_end; i++, j++) {
+		if (thread_ctx->invalid_block_bitmap_flag[j])
+			thread_ctx->invalid_bitmaps++;
+		if (thread_ctx->invalid_inode_bitmap_flag[j])
+			thread_ctx->invalid_bitmaps++;
+		if (thread_ctx->invalid_inode_table_flag[j])
+			thread_ctx->invalid_bitmaps++;
+	}
+}
+
+static void e2fsck_pass1_merge_invalid_bitmaps(e2fsck_t global_ctx,
+					       e2fsck_t thread_ctx)
+{
+	dgrp_t i, j;
+	dgrp_t grp_start = thread_ctx->thread_info.et_group_start;
+	dgrp_t grp_end = thread_ctx->thread_info.et_group_end;
+	dgrp_t total = grp_end - grp_start;
+
+	memcpy(&global_ctx->invalid_block_bitmap_flag[grp_start],
+	       thread_ctx->invalid_block_bitmap_flag, total * sizeof(int));
+	memcpy(&global_ctx->invalid_inode_bitmap_flag[grp_start],
+	       thread_ctx->invalid_inode_bitmap_flag, total * sizeof(int));
+	memcpy(&global_ctx->invalid_inode_table_flag[grp_start],
+	       thread_ctx->invalid_inode_table_flag, total * sizeof(int));
+	global_ctx->invalid_bitmaps += thread_ctx->invalid_bitmaps;
+}
+
 static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thread_ctx,
 					     int thread_index, int num_threads)
 {
@@ -2384,6 +2440,7 @@ static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thre
 		goto out_fs;
 	}
 	*thread_ctx = thread_context;
+	e2fsck_pass1_copy_invalid_bitmaps(global_ctx, thread_context);
 	return 0;
 out_fs:
 	ext2fs_merge_fs(&thread_fs);
@@ -2519,6 +2576,10 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 	ext2_ino_t dx_dir_info_count = global_ctx->dx_dir_info_count;
 	ext2_u32_list dirs_to_hash = global_ctx->dirs_to_hash;
 	quota_ctx_t qctx = global_ctx->qctx;
+	int *invalid_block_bitmap_flag = global_ctx->invalid_block_bitmap_flag;
+	int *invalid_inode_bitmap_flag = global_ctx->invalid_inode_bitmap_flag;
+	int *invalid_inode_table_flag  = global_ctx->invalid_inode_table_flag;
+	int invalid_bitmaps = global_ctx->invalid_bitmaps;
 
 #ifdef HAVE_SETJMP_H
 	jmp_buf old_jmp;
@@ -2598,6 +2659,11 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 					      thread_ctx->qctx);
 	if (retval)
 		return retval;
+	global_ctx->invalid_block_bitmap_flag = invalid_block_bitmap_flag;
+	global_ctx->invalid_inode_bitmap_flag = invalid_inode_bitmap_flag;
+	global_ctx->invalid_inode_table_flag = invalid_inode_table_flag;
+	global_ctx->invalid_bitmaps = invalid_bitmaps;
+	e2fsck_pass1_merge_invalid_bitmaps(global_ctx, thread_ctx);
 
 	retval = e2fsck_pass1_merge_bitmap(global_fs,
 				&thread_ctx->inode_used_map,
@@ -2663,6 +2729,9 @@ static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
 	if (thread_ctx->dirs_to_hash)
 		ext2fs_badblocks_list_free(thread_ctx->dirs_to_hash);
 	quota_release_context(&thread_ctx->qctx);
+	ext2fs_free_mem(&thread_ctx->invalid_block_bitmap_flag);
+	ext2fs_free_mem(&thread_ctx->invalid_inode_bitmap_flag);
+	ext2fs_free_mem(&thread_ctx->invalid_inode_table_flag);
 
 	if (thread_ctx->logf)
 		fclose(thread_ctx->logf);
@@ -2682,6 +2751,8 @@ static int e2fsck_pass1_threads_join(struct e2fsck_thread_info *infos,
 	int i;
 	struct e2fsck_thread_info *pinfo;
 
+	/* merge invalid bitmaps will recalculate it */
+	global_ctx->invalid_bitmaps = 0;
 	for (i = 0; i < num_threads; i++) {
 		pinfo = &infos[i];
 
Signature · 1 line
2.37.3
#75 lore raw

RFC 46/72 e2fsck: merge EA blocks properly

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

EA blocks might be shared, merge them carefully.

---
 e2fsck/e2fsck.h |   1 +
 e2fsck/pass1.c  | 244 +++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 212 insertions(+), 33 deletions(-)
e2fsck/e2fsck.h +1 -0
index 55f75042..beac7054 100644
@@ -333,6 +333,7 @@ struct e2fsck_struct {
 
 	ext2_refcount_t	refcount;
 	ext2_refcount_t refcount_extra;
+	ext2_refcount_t refcount_orig;
 
 	/*
 	 * Quota blocks and inodes to be charged for each ea block.
e2fsck/pass1.c +211 -33

341 lines not on this page · raw message

Signature · 1 line
2.37.3
#76 lore raw

RFC 47/72 e2fsck: kickoff mutex lock for block found map

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

Now @block_found_map is no longer shared by multiple threads, and @block_dup_map need be checked again after threads finish.

---
 e2fsck/e2fsck.h                   |   9 +-
 e2fsck/pass1.c                    | 171 ++++++++++++++++++------------
 e2fsck/util.c                     |  34 ++++--
 tests/f_itable_collision/expect.1 |   3 -
 4 files changed, 137 insertions(+), 80 deletions(-)
e2fsck/e2fsck.h +6 -3
index beac7054..23e8d2ed 100644
@@ -211,6 +211,7 @@ struct resource_track {
 #define E2F_FLAG_TIME_INSANE	0x2000 /* Time is insane */
 #define E2F_FLAG_PROBLEMS_FIXED	0x4000 /* At least one problem was fixed */
 #define E2F_FLAG_ALLOC_OK	0x8000 /* Can we allocate blocks? */
+#define E2F_FLAG_DUP_BLOCK	0x20000 /* dup block found during pass1 */
 
 #define E2F_RESET_FLAGS (E2F_FLAG_TIME_INSANE | E2F_FLAG_PROBLEMS_FIXED)
 
@@ -489,7 +490,7 @@ struct e2fsck_struct {
 	/* serialize fix operation for multiple threads */
 	pthread_mutex_t		 fs_fix_mutex;
 	/* protect block_found_map, block_dup_map */
-	pthread_mutex_t		 fs_block_map_mutex;
+	pthread_rwlock_t	 fs_block_map_rwlock;
 #endif
 	/* Fast commit replay state */
 	struct e2fsck_fc_replay_state fc_replay_state;
@@ -796,8 +797,10 @@ extern errcode_t e2fsck_allocate_subcluster_bitmap(ext2_filsys fs,
 unsigned long long get_memory_size(void);
 extern void e2fsck_pass1_fix_lock(e2fsck_t ctx);
 extern void e2fsck_pass1_fix_unlock(e2fsck_t ctx);
-extern void e2fsck_pass1_block_map_lock(e2fsck_t ctx);
-extern void e2fsck_pass1_block_map_unlock(e2fsck_t ctx);
+extern void e2fsck_pass1_block_map_w_lock(e2fsck_t ctx);
+extern void e2fsck_pass1_block_map_w_unlock(e2fsck_t ctx);
+extern void e2fsck_pass1_block_map_r_lock(e2fsck_t ctx);
+extern void e2fsck_pass1_block_map_r_unlock(e2fsck_t ctx);
 
 /* unix.c */
 extern void e2fsck_clear_progbar(e2fsck_t ctx);
e2fsck/pass1.c +103 -68

350 lines not on this page · raw message

e2fsck/util.c +28 -6
index 93cd96c7..5714576a 100644
@@ -592,17 +592,29 @@ void e2fsck_pass1_fix_unlock(e2fsck_t ctx)
 	pthread_mutex_unlock(&global_ctx->fs_fix_mutex);
 }
 
-void e2fsck_pass1_block_map_lock(e2fsck_t ctx)
+void e2fsck_pass1_block_map_w_lock(e2fsck_t ctx)
 {
 	e2fsck_get_lock_context(ctx);
-	pthread_mutex_lock(&global_ctx->fs_block_map_mutex);
+	pthread_rwlock_wrlock(&global_ctx->fs_block_map_rwlock);
 }
 
-void e2fsck_pass1_block_map_unlock(e2fsck_t ctx)
+void e2fsck_pass1_block_map_w_unlock(e2fsck_t ctx)
 {
 	e2fsck_get_lock_context(ctx);
-	pthread_mutex_unlock(&global_ctx->fs_block_map_mutex);
+	pthread_rwlock_unlock(&global_ctx->fs_block_map_rwlock);
 }
+
+void e2fsck_pass1_block_map_r_lock(e2fsck_t ctx)
+{
+	e2fsck_get_lock_context(ctx);
+	pthread_rwlock_rdlock(&global_ctx->fs_block_map_rwlock);
+}
+
+void e2fsck_pass1_block_map_r_unlock(e2fsck_t ctx)
+{
+	e2fsck_get_lock_context(ctx);
+	pthread_rwlock_unlock(&global_ctx->fs_block_map_rwlock);
+ }
 #else
 void e2fsck_pass1_fix_lock(e2fsck_t ctx)
 {
@@ -613,14 +625,24 @@ void e2fsck_pass1_fix_unlock(e2fsck_t ctx)
 {
 
 }
+void e2fsck_pass1_block_map_w_lock(e2fsck_t ctx)
+{
 
-void e2fsck_pass1_block_map_lock(e2fsck_t ctx)
+}
+
+void e2fsck_pass1_block_map_w_unlock(e2fsck_t ctx)
 {
 
 }
 
-void e2fsck_pass1_block_map_unlock(e2fsck_t ctx)
+void e2fsck_pass1_block_map_r_lock(e2fsck_t ctx)
 {
+
+}
+
+void e2fsck_pass1_block_map_r_unlock(e2fsck_t ctx)
+{
+
 }
 #endif
 
tests/f_itable_collision/expect.1 +0 -3
index 01c85d4d..7e98baa8 100644
@@ -1,6 +1,5 @@
 Pass 1: Checking inodes, blocks, and sizes
 Inode 12 block 37 conflicts with critical metadata, skipping block checks.
-Illegal block number passed to ext2fs_test_block_bitmap #268435455 for in-use block map
 Illegal block number passed to ext2fs_mark_block_bitmap #268435455 for in-use block map
 Inode 12, i_blocks is 48, should be 56.  Fix? yes
 
@@ -27,9 +26,7 @@ Clear inode? yes
 Restarting e2fsck from the beginning...
 Pass 1: Checking inodes, blocks, and sizes
 Inode 12 block 37 conflicts with critical metadata, skipping block checks.
-Illegal block number passed to ext2fs_test_block_bitmap #4294967294 for in-use block map
 Illegal block number passed to ext2fs_mark_block_bitmap #4294967294 for in-use block map
-Illegal block number passed to ext2fs_test_block_bitmap #268435455 for in-use block map
 Illegal block number passed to ext2fs_mark_block_bitmap #268435455 for in-use block map
 
 Running additional passes to resolve blocks claimed by more than one inode...
Signature · 1 line
2.37.3
#77 lore raw

RFC 48/72 e2fsck: allow admin specify number of threads

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

-m option is introduced to specify number of threads for pfsck.

---
 e2fsck/e2fsck.h                       |   2 +
 e2fsck/pass1.c                        | 154 ++++++++++++++++----------
 e2fsck/unix.c                         |  17 ++-
 tests/f_multithread/script            |   2 +-
 tests/f_multithread_completion/script |   2 +-
 tests/f_multithread_logfile/script    |   2 +-
 tests/f_multithread_no/script         |   2 +-
 tests/f_multithread_preen/script      |   2 +-
 tests/f_multithread_yes/script        |   2 +-
 9 files changed, 122 insertions(+), 63 deletions(-)
e2fsck/e2fsck.h +2 -0
index 23e8d2ed..e3276924 100644
@@ -487,6 +487,7 @@ struct e2fsck_struct {
 	char *undo_file;
 
 #ifdef HAVE_PTHREAD
+	__u32			 fs_num_threads;
 	/* serialize fix operation for multiple threads */
 	pthread_mutex_t		 fs_fix_mutex;
 	/* protect block_found_map, block_dup_map */
@@ -727,6 +728,7 @@ void check_resize_inode(e2fsck_t ctx);
 int check_init_orphan_file(e2fsck_t ctx);
 
 /* util.c */
+#define E2FSCK_MAX_THREADS	(65536)
 extern void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned long size,
 				    const char *description);
 extern int ask(e2fsck_t ctx, const char * string, int def);
e2fsck/pass1.c +98 -56

192 lines not on this page · raw message

e2fsck/unix.c +16 -1
index af2457e3..dfa3f897 100644
@@ -826,6 +826,10 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
 	int 		res;		/* result of sscanf */
 #ifdef CONFIG_JBD_DEBUG
 	char 		*jbd_debug;
+#endif
+#ifdef HAVE_PTHREAD
+	char		*pm;
+	unsigned long	thread_num;
 #endif
 	unsigned long long phys_mem_kb, blk;
 
@@ -859,7 +863,7 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
 	ctx->readahead_kb = ~0ULL;
 
 #ifdef HAVE_PTHREAD
-	while ((c = getopt(argc, argv, "pamnyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDkz:")) != EOF)
+	while ((c = getopt(argc, argv, "pam:nyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDkz:")) != EOF)
 #else
 	while ((c = getopt(argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDkz:")) != EOF)
 #endif
@@ -905,7 +909,18 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
 			break;
 #ifdef HAVE_PTHREAD
 		case 'm':
+			thread_num = strtoul(optarg, &pm, 0);
+			if (*pm)
+				fatal_error(ctx,
+					_("Invalid multiple thread num.\n"));
+			if (thread_num > E2FSCK_MAX_THREADS) {
+				fprintf(stderr,
+					_("threads %lu too large (max %lu)\n"),
+					thread_num, E2FSCK_MAX_THREADS);
+				fatal_error(ctx, 0);
+			}
 			ctx->options |= E2F_OPT_MULTITHREAD;
+			ctx->fs_num_threads = thread_num;
 			break;
 #endif
 		case 'n':
tests/f_multithread/script +1 -1
index 0fe96cd0..83cd0f03 100644
@@ -1,4 +1,4 @@
-FSCK_OPT="-fy -m"
+FSCK_OPT="-fy -m1"
 SECOND_FSCK_OPT=-yf
 
 . $cmd_dir/run_e2fsck
tests/f_multithread_completion/script +1 -1
index bf23cd61..0ec13816 100644
@@ -1,4 +1,4 @@
-FSCK_OPT="-fy -m -C 1"
+FSCK_OPT="-fy -m1 -C 1"
 SECOND_FSCK_OPT=-yf
 
 . $cmd_dir/run_e2fsck
tests/f_multithread_logfile/script +1 -1
index 4f9ca6f8..dbb65319 100644
@@ -1,5 +1,5 @@
 LOG_FNAME="f_multithread_logfile_xxx"
-FSCK_OPT="-fy -m -y -E log_filename=$LOG_FNAME"
+FSCK_OPT="-fy -m1 -y -E log_filename=$LOG_FNAME"
 SKIP_VERIFY="true"
 ONE_PASS_ONLY="true"
 SKIP_CLEANUP="true"
tests/f_multithread_no/script +1 -1
index b93deb3a..db791e11 100644
@@ -1,4 +1,4 @@
-FSCK_OPT="-fn -m"
+FSCK_OPT="-fn -m1"
 SECOND_FSCK_OPT=-yf
 
 . $cmd_dir/run_e2fsck
tests/f_multithread_preen/script +1 -1
index ecb79cd6..8965f4a7 100644
@@ -1,4 +1,4 @@
-FSCK_OPT="-fp -m"
+FSCK_OPT="-fp -m1"
 SECOND_FSCK_OPT=-yf
 
 . $cmd_dir/run_e2fsck
tests/f_multithread_yes/script +1 -1
index 38891f6a..8b4aa9b8 100644
@@ -1,4 +1,4 @@
-FSCK_OPT="-f -m"
+FSCK_OPT="-f -m1"
 SECOND_FSCK_OPT=-yf
 
 . $cmd_dir/run_e2fsck
Signature · 1 line
2.37.3
#78 lore raw

RFC 49/72 e2fsck: adjust number of threads

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

number of threads should not exceed flex bg numbers, and output messages if we adjust threads number.

---
 e2fsck/pass1.c | 32 +++++++++++++++-----------------
 1 file changed, 15 insertions(+), 17 deletions(-)
e2fsck/pass1.c +15 -17
index 5bf6980b..1d4f576c 100644
@@ -1287,6 +1287,7 @@ static void e2fsck_pass1_set_thread_num(e2fsck_t ctx)
 	}
 out:
 	ctx->fs_num_threads = num_threads;
+	ctx->fs->fs_num_threads = num_threads;
 }
 #endif
 
@@ -2481,14 +2482,14 @@ static void e2fsck_pass1_merge_invalid_bitmaps(e2fsck_t global_ctx,
 }
 
 static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thread_ctx,
-					     int thread_index, int num_threads)
+					     int thread_index, int num_threads,
+					     dgrp_t average_group)
 {
 	errcode_t retval;
 	e2fsck_t thread_context;
 	ext2_filsys thread_fs;
 	ext2_filsys global_fs = global_ctx->fs;
 	struct e2fsck_thread *tinfo;
-	dgrp_t average_group;
 
 	assert(global_ctx->inode_used_map == NULL);
 	assert(global_ctx->inode_dir_map == NULL);
@@ -2535,16 +2536,9 @@ static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thre
 	thread_context->thread_info.et_thread_index = thread_index;
 	set_up_logging(thread_context);
 
-	/*
-	 * Distribute work to multiple threads:
-	 * Each thread work on fs->group_desc_count / nthread groups.
-	 */
 	tinfo = &thread_context->thread_info;
-	average_group = thread_fs->group_desc_count / num_threads;
-	if (average_group == 0)
-		average_group = 1;
 	tinfo->et_group_start = average_group * thread_index;
-	if (thread_index == num_threads - 1)
+	if (thread_index == global_fs->fs_num_threads - 1)
 		tinfo->et_group_end = thread_fs->group_desc_count;
 	else
 		tinfo->et_group_end = average_group * (thread_index + 1);
@@ -3060,12 +3054,13 @@ static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
 }
 
 static int e2fsck_pass1_threads_join(struct e2fsck_thread_info *infos,
-				      int num_threads, e2fsck_t global_ctx)
+				     e2fsck_t global_ctx)
 {
 	errcode_t rc;
 	errcode_t ret = 0;
 	int i;
 	struct e2fsck_thread_info *pinfo;
+	int num_threads = global_ctx->fs_num_threads;
 
 	/* merge invalid bitmaps will recalculate it */
 	global_ctx->invalid_bitmaps = 0;
@@ -3147,7 +3142,7 @@ out:
 }
 
 static int e2fsck_pass1_threads_start(struct e2fsck_thread_info **pinfo,
-				      int num_threads, e2fsck_t global_ctx)
+				      e2fsck_t global_ctx)
 {
 	struct e2fsck_thread_info *infos;
 	pthread_attr_t attr;
@@ -3156,6 +3151,8 @@ static int e2fsck_pass1_threads_start(struct e2fsck_thread_info **pinfo,
 	struct e2fsck_thread_info *tmp_pinfo;
 	int i;
 	e2fsck_t thread_ctx;
+	dgrp_t average_group;
+	int num_threads = global_ctx->fs_num_threads;
 #ifdef DEBUG_THREADS
 	struct e2fsck_thread_debug thread_debug =
 		{PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};
@@ -3179,6 +3176,7 @@ static int e2fsck_pass1_threads_start(struct e2fsck_thread_info **pinfo,
 		return retval;
 	}
 
+	average_group = ext2fs_get_avg_group(global_ctx->fs);
 	for (i = 0; i < num_threads; i++) {
 		tmp_pinfo = &infos[i];
 		tmp_pinfo->eti_thread_index = i;
@@ -3186,7 +3184,8 @@ static int e2fsck_pass1_threads_start(struct e2fsck_thread_info **pinfo,
 		tmp_pinfo->eti_debug = &thread_debug;
 #endif
 		retval = e2fsck_pass1_thread_prepare(global_ctx, &thread_ctx,
-						     i, num_threads);
+						     i, num_threads,
+						     average_group);
 		if (retval) {
 			com_err(global_ctx->program_name, retval,
 				_("while preparing pass1 thread\n"));
@@ -3216,7 +3215,7 @@ static int e2fsck_pass1_threads_start(struct e2fsck_thread_info **pinfo,
 	}
 
 	if (retval) {
-		e2fsck_pass1_threads_join(infos, num_threads, global_ctx);
+		e2fsck_pass1_threads_join(infos, global_ctx);
 		return retval;
 	}
 	*pinfo = infos;
@@ -3226,17 +3225,16 @@ static int e2fsck_pass1_threads_start(struct e2fsck_thread_info **pinfo,
 static void e2fsck_pass1_multithread(e2fsck_t global_ctx)
 {
 	struct e2fsck_thread_info *infos = NULL;
-	int num_threads = global_ctx->fs_num_threads;
 	errcode_t retval;
 
-	retval = e2fsck_pass1_threads_start(&infos, num_threads, global_ctx);
+	retval = e2fsck_pass1_threads_start(&infos, global_ctx);
 	if (retval) {
 		com_err(global_ctx->program_name, retval,
 			_("while starting pass1 threads\n"));
 		goto out_abort;
 	}
 
-	retval = e2fsck_pass1_threads_join(infos, num_threads, global_ctx);
+	retval = e2fsck_pass1_threads_join(infos, global_ctx);
 	if (retval) {
 		com_err(global_ctx->program_name, retval,
 			_("while joining pass1 threads\n"));
Signature · 1 line
2.37.3
#79 lore raw

RFC 50/72 e2fsck: fix readahead for pfsck of pass1

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

Several improvments for this patch:

1) move readahead_kb detection to preparing phase.
2) inode readahead should be aware of thread block group boundary.
3) make readahead_kb aware of multiple threads.

---
 e2fsck/pass1.c | 29 ++++++++++++++++++++---------
 1 file changed, 20 insertions(+), 9 deletions(-)
e2fsck/pass1.c +20 -9
index 1d4f576c..5d07daec 100644
@@ -1098,16 +1098,20 @@ out:
 static void pass1_readahead(e2fsck_t ctx, dgrp_t *group, ext2_ino_t *next_ino)
 {
 	ext2_ino_t inodes_in_group = 0, inodes_per_block, inodes_per_buffer;
-	dgrp_t start = *group, grp;
+	dgrp_t start = *group, grp, grp_end = ctx->fs->group_desc_count;
 	blk64_t blocks_to_read = 0;
 	errcode_t err = EXT2_ET_INVALID_ARGUMENT;
 
+#ifdef HAVE_PTHREAD
+	if (ctx->fs->fs_num_threads > 1)
+		grp_end = ctx->thread_info.et_group_end;
+#endif
 	if (ctx->readahead_kb == 0)
 		goto out;
 
 	/* Keep iterating groups until we have enough to readahead */
 	inodes_per_block = EXT2_INODES_PER_BLOCK(ctx->fs->super);
-	for (grp = start; grp < ctx->fs->group_desc_count; grp++) {
+	for (grp = start; grp < grp_end; grp++) {
 		if (ext2fs_bg_flags_test(ctx->fs, grp, EXT2_BG_INODE_UNINIT))
 			continue;
 		inodes_in_group = ctx->fs->super->s_inodes_per_group -
@@ -1300,12 +1304,25 @@ static errcode_t e2fsck_pass1_prepare(e2fsck_t ctx)
 {
 	struct problem_context pctx;
 	ext2_filsys fs = ctx->fs;
+	unsigned long long readahead_kb;
 
 	init_ext2_max_sizes();
-#ifdef	HAVE_PTHREAD
+#ifdef HAVE_PTHREAD
 	e2fsck_pass1_set_thread_num(ctx);
 #endif
+	/* If we can do readahead, figure out how many groups to pull in. */
+	if (!e2fsck_can_readahead(ctx->fs))
+		ctx->readahead_kb = 0;
+	else if (ctx->readahead_kb == ~0ULL)
+		ctx->readahead_kb = e2fsck_guess_readahead(ctx->fs);
 
+#ifdef HAVE_PTHREAD
+	/* don't use more than 1/10 of memory for threads checking */
+	readahead_kb = get_memory_size() / (10 * ctx->fs_num_threads);
+	/* maybe better disable RA if this is too small? */
+	if (ctx->readahead_kb > readahead_kb)
+		ctx->readahead_kb = readahead_kb;
+#endif
 	clear_problem_context(&pctx);
 	if (!(ctx->options & E2F_OPT_PREEN))
 		fix_problem(ctx, PR_1_PASS_HEADER, &pctx);
@@ -1482,13 +1499,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
 	init_resource_track(&rtrack, ctx->fs->io);
 	clear_problem_context(&pctx);
 
-	/* If we can do readahead, figure out how many groups to pull in. */
-	if (!e2fsck_can_readahead(ctx->fs))
-		ctx->readahead_kb = 0;
-	else if (ctx->readahead_kb == ~0ULL)
-		ctx->readahead_kb = e2fsck_guess_readahead(ctx->fs);
 	pass1_readahead(ctx, &ra_group, &ino_threshold);
-
 	if (ext2fs_has_feature_dir_index(fs->super) &&
 	    !(ctx->options & E2F_OPT_NO)) {
 		if (ext2fs_u32_list_create(&ctx->dirs_to_hash, 50))
Signature · 1 line
2.37.3
#80 lore raw

RFC 51/72 e2fsck: merge options after threads finish

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

It will be possible that threads might append E2F_OPT_YES, so we need merge options to global, test f_yesall cover this.

---
 e2fsck/pass1.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
e2fsck/pass1.c +4 -1
index 5d07daec..59ff888f 100644
@@ -2866,6 +2866,7 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 	ext2_refcount_t ea_inode_refs = global_ctx->ea_inode_refs;
 	ext2fs_block_bitmap  block_found_map = global_ctx->block_found_map;
 	ext2fs_block_bitmap  block_dup_map = global_ctx->block_dup_map;
+	int options = global_ctx->options;
 
 #ifdef HAVE_SETJMP_H
 	jmp_buf old_jmp;
@@ -2918,7 +2919,8 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 	global_ctx->fs_fragmented += fs_fragmented;
 	global_ctx->fs_fragmented_dir += fs_fragmented_dir;
 	global_ctx->large_files += large_files;
-
+	/* threads might enable E2F_OPT_YES */
+	global_ctx->options |= options;
 	global_ctx->flags |= flags;
 	global_ctx->logf = global_logf;
 	global_ctx->problem_logf = global_problem_logf;
@@ -2954,6 +2956,7 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 					      thread_ctx->qctx);
 	if (retval)
 		return retval;
+
 	global_ctx->invalid_block_bitmap_flag = invalid_block_bitmap_flag;
 	global_ctx->invalid_inode_bitmap_flag = invalid_inode_bitmap_flag;
 	global_ctx->invalid_inode_table_flag = invalid_inode_table_flag;
Signature · 1 line
2.37.3
#81 lore raw

RFC 52/72 e2fsck: reset lost_and_found after threads finish

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

This should not be kept, the reaons is similar to what e2fsck_pass1 has done before.

---
 e2fsck/pass1.c | 5 +++++
 1 file changed, 5 insertions(+)
e2fsck/pass1.c +5 -0
index 59ff888f..1a5fcf66 100644
@@ -2925,6 +2925,11 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 	global_ctx->logf = global_logf;
 	global_ctx->problem_logf = global_problem_logf;
 	global_ctx->global_ctx = NULL;
+	/*
+	 * The l+f inode may have been cleared, so zap it now and
+	 * later passes will recalculate it if necessary
+	 */
+	global_ctx->lost_and_found = 0;
 
 	global_fs->priv_data = global_ctx;
 	global_ctx->fs = global_fs;
Signature · 1 line
2.37.3
#82 lore raw

RFC 53/72 e2fsck: merge extent depth count after threads finish

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

tests covered by f_extent_htree.

---
 e2fsck/pass1.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)
e2fsck/pass1.c +10 -1
index 1a5fcf66..c89c424d 100644
@@ -2866,8 +2866,11 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 	ext2_refcount_t ea_inode_refs = global_ctx->ea_inode_refs;
 	ext2fs_block_bitmap  block_found_map = global_ctx->block_found_map;
 	ext2fs_block_bitmap  block_dup_map = global_ctx->block_dup_map;
-	int options = global_ctx->options;
+	int options = global_ctx->options, i;
+	__u32 extent_depth_count[MAX_EXTENT_DEPTH_COUNT];
 
+	memcpy(extent_depth_count, global_ctx->extent_depth_count,
+	       sizeof(extent_depth_count));
 #ifdef HAVE_SETJMP_H
 	jmp_buf old_jmp;
 
@@ -2930,6 +2933,12 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 	 * later passes will recalculate it if necessary
 	 */
 	global_ctx->lost_and_found = 0;
+	memcpy(global_ctx->extent_depth_count, extent_depth_count,
+	       sizeof(extent_depth_count));
+	/* merge extent depth count */
+	for (i = 0; i < MAX_EXTENT_DEPTH_COUNT; i++)
+		global_ctx->extent_depth_count[i] +=
+			thread_ctx->extent_depth_count[i];
 
 	global_fs->priv_data = global_ctx;
 	global_ctx->fs = global_fs;
Signature · 1 line
2.37.3
#83 lore raw

RFC 54/72 e2fsck: simplify e2fsck context merging codes

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

We tried to copy thread context to global context directly and then copy back some saved variables before merging.

Since we have finished almost all necessary variables in the e2fsck context, we could simplify codes, and this could help us understand what is missing rather than hide problems.

---
 e2fsck/pass1.c | 158 ++++++++++---------------------------------------
 1 file changed, 31 insertions(+), 127 deletions(-)
e2fsck/pass1.c +31 -127
index c89c424d..8e85f70f 100644
@@ -2813,136 +2813,51 @@ static errcode_t e2fsck_pass1_merge_ea_refcount(e2fsck_t global_ctx,
 	return retval;
 }
 
-static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx)
+static errcode_t e2fsck_pass1_merge_context(e2fsck_t global_ctx,
+					    e2fsck_t thread_ctx)
 {
-	errcode_t retval = 0;
-	int flags = global_ctx->flags;
-	FILE *global_logf = global_ctx->logf;
-	FILE *global_problem_logf = global_ctx->problem_logf;
-	ext2_filsys thread_fs = thread_ctx->fs;
 	ext2_filsys global_fs = global_ctx->fs;
-	ext2fs_inode_bitmap inode_bad_map = global_ctx->inode_bad_map;
-	struct dir_info_db *dir_info = global_ctx->dir_info;
-	struct dx_dir_info *dx_dir_info = global_ctx->dx_dir_info;
-	ext2fs_inode_bitmap inode_used_map = global_ctx->inode_used_map;
-	ext2fs_inode_bitmap inode_dir_map = global_ctx->inode_dir_map;
-	ext2fs_inode_bitmap inode_bb_map = global_ctx->inode_bb_map;
-	ext2fs_inode_bitmap inode_imagic_map = global_ctx->inode_imagic_map;
-	ext2fs_inode_bitmap inode_reg_map = global_ctx->inode_reg_map;
-	ext2fs_block_bitmap inodes_to_rebuild = global_ctx->inodes_to_rebuild;
-	ext2_icount_t inode_count = global_ctx->inode_count;
-	ext2_icount_t inode_link_info = global_ctx->inode_link_info;
-	__u32 fs_directory_count = global_ctx->fs_directory_count;
-	__u32 fs_regular_count = global_ctx->fs_regular_count;
-	__u32 fs_blockdev_count = global_ctx->fs_blockdev_count;
-	__u32 fs_chardev_count = global_ctx->fs_chardev_count;
-	__u32 fs_links_count = global_ctx->fs_links_count;
-	__u32 fs_symlinks_count = global_ctx->fs_symlinks_count;
-	__u32 fs_fast_symlinks_count = global_ctx->fs_fast_symlinks_count;
-	__u32 fs_fifo_count = global_ctx->fs_fifo_count;
-	__u32 fs_total_count = global_ctx->fs_total_count;
-	__u32 fs_badblocks_count = global_ctx->fs_badblocks_count;
-	__u32 fs_sockets_count = global_ctx->fs_sockets_count;
-	__u32 fs_ind_count = global_ctx->fs_ind_count;
-	__u32 fs_dind_count = global_ctx->fs_dind_count;
-	__u32 fs_tind_count = global_ctx->fs_tind_count;
-	__u32 fs_fragmented = global_ctx->fs_fragmented;
-	__u32 fs_fragmented_dir = global_ctx->fs_fragmented_dir;
-	__u32 large_files = global_ctx->large_files;
-	ext2_ino_t dx_dir_info_size = global_ctx->dx_dir_info_size;
-	ext2_ino_t dx_dir_info_count = global_ctx->dx_dir_info_count;
-	ext2_u32_list dirs_to_hash = global_ctx->dirs_to_hash;
-	quota_ctx_t qctx = global_ctx->qctx;
-	int *invalid_block_bitmap_flag = global_ctx->invalid_block_bitmap_flag;
-	int *invalid_inode_bitmap_flag = global_ctx->invalid_inode_bitmap_flag;
-	int *invalid_inode_table_flag  = global_ctx->invalid_inode_table_flag;
-	int invalid_bitmaps = global_ctx->invalid_bitmaps;
-	ext2_refcount_t refcount = global_ctx->refcount;
-	ext2_refcount_t refcount_extra = global_ctx->refcount_extra;
-	ext2_refcount_t refcount_orig = global_ctx->refcount_orig;
-	ext2_refcount_t ea_block_quota_blocks = global_ctx->ea_block_quota_blocks;
-	ext2_refcount_t ea_block_quota_inodes = global_ctx->ea_block_quota_inodes;
-	ext2fs_block_bitmap block_ea_map = global_ctx->block_ea_map;
-	ext2_refcount_t ea_inode_refs = global_ctx->ea_inode_refs;
-	ext2fs_block_bitmap  block_found_map = global_ctx->block_found_map;
-	ext2fs_block_bitmap  block_dup_map = global_ctx->block_dup_map;
-	int options = global_ctx->options, i;
-	__u32 extent_depth_count[MAX_EXTENT_DEPTH_COUNT];
-
-	memcpy(extent_depth_count, global_ctx->extent_depth_count,
-	       sizeof(extent_depth_count));
-#ifdef HAVE_SETJMP_H
-	jmp_buf old_jmp;
-
-	memcpy(old_jmp, global_ctx->abort_loc, sizeof(jmp_buf));
-#endif
-	memcpy(global_ctx, thread_ctx, sizeof(struct e2fsck_struct));
-#ifdef HAVE_SETJMP_H
-	memcpy(global_ctx->abort_loc, old_jmp, sizeof(jmp_buf));
-#endif
+	errcode_t retval = 0;
+	int i;
 
-	global_ctx->inode_used_map = inode_used_map;
-	global_ctx->inode_bad_map = inode_bad_map;
-	global_ctx->inode_dir_map = inode_dir_map;
-	global_ctx->inode_bb_map = inode_bb_map;
-	global_ctx->inode_imagic_map = inode_imagic_map;
-	global_ctx->inodes_to_rebuild = inodes_to_rebuild;
-	global_ctx->inode_reg_map = inode_reg_map;
-	global_ctx->block_dup_map = block_dup_map;
-	global_ctx->block_found_map = block_found_map;
-	global_ctx->dir_info = dir_info;
-	e2fsck_pass1_merge_dir_info(global_ctx, thread_ctx);
-	global_ctx->dx_dir_info = dx_dir_info;
-	global_ctx->dx_dir_info_count = dx_dir_info_count;
-	global_ctx->dx_dir_info_size = dx_dir_info_size;
-	e2fsck_pass1_merge_dx_dir(global_ctx, thread_ctx);
-	global_ctx->inode_count = inode_count;
-	global_ctx->inode_link_info = inode_link_info;
-	global_ctx->refcount = refcount;
-	global_ctx->refcount_extra = refcount_extra;
-	global_ctx->refcount_orig = refcount_orig;
-	global_ctx->ea_block_quota_blocks = ea_block_quota_blocks;
-	global_ctx->ea_block_quota_inodes = ea_block_quota_inodes;
-	global_ctx->block_ea_map = block_ea_map;
-	global_ctx->ea_inode_refs = ea_inode_refs;
-	global_ctx->fs_directory_count += fs_directory_count;
-	global_ctx->fs_regular_count += fs_regular_count;
-	global_ctx->fs_blockdev_count += fs_blockdev_count;
-	global_ctx->fs_chardev_count += fs_chardev_count;
-	global_ctx->fs_links_count += fs_links_count;
-	global_ctx->fs_symlinks_count += fs_symlinks_count;
-	global_ctx->fs_fast_symlinks_count += fs_fast_symlinks_count;
-	global_ctx->fs_fifo_count += fs_fifo_count;
-	global_ctx->fs_total_count += fs_total_count;
-	global_ctx->fs_badblocks_count += fs_badblocks_count;
-	global_ctx->fs_sockets_count += fs_sockets_count;
-	global_ctx->fs_ind_count += fs_ind_count;
-	global_ctx->fs_dind_count += fs_dind_count;
-	global_ctx->fs_tind_count += fs_tind_count;
-	global_ctx->fs_fragmented += fs_fragmented;
-	global_ctx->fs_fragmented_dir += fs_fragmented_dir;
-	global_ctx->large_files += large_files;
+	global_ctx->fs_directory_count += thread_ctx->fs_directory_count;
+	global_ctx->fs_regular_count += thread_ctx->fs_regular_count;
+	global_ctx->fs_blockdev_count += thread_ctx->fs_blockdev_count;
+	global_ctx->fs_chardev_count += thread_ctx->fs_chardev_count;
+	global_ctx->fs_links_count += thread_ctx->fs_links_count;
+	global_ctx->fs_symlinks_count += thread_ctx->fs_symlinks_count;
+	global_ctx->fs_fast_symlinks_count += thread_ctx->fs_fast_symlinks_count;
+	global_ctx->fs_fifo_count += thread_ctx->fs_fifo_count;
+	global_ctx->fs_total_count += thread_ctx->fs_total_count;
+	global_ctx->fs_badblocks_count += thread_ctx->fs_badblocks_count;
+	global_ctx->fs_sockets_count += thread_ctx->fs_sockets_count;
+	global_ctx->fs_ind_count += thread_ctx->fs_ind_count;
+	global_ctx->fs_dind_count += thread_ctx->fs_dind_count;
+	global_ctx->fs_tind_count += thread_ctx->fs_tind_count;
+	global_ctx->fs_fragmented += thread_ctx->fs_fragmented;
+	global_ctx->fs_fragmented_dir += thread_ctx->fs_fragmented_dir;
+	global_ctx->large_files += thread_ctx->large_files;
 	/* threads might enable E2F_OPT_YES */
-	global_ctx->options |= options;
-	global_ctx->flags |= flags;
-	global_ctx->logf = global_logf;
-	global_ctx->problem_logf = global_problem_logf;
-	global_ctx->global_ctx = NULL;
+	global_ctx->options |= thread_ctx->options;
+	global_ctx->flags |= thread_ctx->flags;
 	/*
 	 * The l+f inode may have been cleared, so zap it now and
 	 * later passes will recalculate it if necessary
 	 */
 	global_ctx->lost_and_found = 0;
-	memcpy(global_ctx->extent_depth_count, extent_depth_count,
-	       sizeof(extent_depth_count));
 	/* merge extent depth count */
 	for (i = 0; i < MAX_EXTENT_DEPTH_COUNT; i++)
 		global_ctx->extent_depth_count[i] +=
 			thread_ctx->extent_depth_count[i];
 
-	global_fs->priv_data = global_ctx;
-	global_ctx->fs = global_fs;
+	e2fsck_pass1_merge_dir_info(global_ctx, thread_ctx);
+	e2fsck_pass1_merge_dx_dir(global_ctx, thread_ctx);
 
+	retval = ext2fs_merge_fs(&(thread_ctx->fs));
+	if (retval) {
+		com_err(global_ctx->program_name, 0, _("while merging fs\n"));
+		return retval;
+	}
 	retval = e2fsck_pass1_merge_icounts(global_ctx, thread_ctx);
 	if (retval) {
 		com_err(global_ctx->program_name, 0,
@@ -2950,12 +2865,6 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 		return retval;
 	}
 
-	retval = ext2fs_merge_fs(&(thread_ctx->fs));
-	if (retval) {
-		com_err(global_ctx->program_name, 0, _("while merging fs\n"));
-		return retval;
-	}
-	global_ctx->dirs_to_hash = dirs_to_hash;
 	retval = e2fsck_pass1_merge_dirs_to_hash(global_ctx, thread_ctx);
 	if (retval) {
 		com_err(global_ctx->program_name, 0,
@@ -2965,16 +2874,11 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
 
 	e2fsck_pass1_merge_ea_inode_refs(global_ctx, thread_ctx);
 	e2fsck_pass1_merge_ea_refcount(global_ctx, thread_ctx);
-	global_ctx->qctx = qctx;
 	retval = quota_merge_and_update_usage(global_ctx->qctx,
 					      thread_ctx->qctx);
 	if (retval)
 		return retval;
 
-	global_ctx->invalid_block_bitmap_flag = invalid_block_bitmap_flag;
-	global_ctx->invalid_inode_bitmap_flag = invalid_inode_bitmap_flag;
-	global_ctx->invalid_inode_table_flag = invalid_inode_table_flag;
-	global_ctx->invalid_bitmaps = invalid_bitmaps;
 	e2fsck_pass1_merge_invalid_bitmaps(global_ctx, thread_ctx);
 
 	retval = e2fsck_pass1_merge_bitmap(global_fs,
@@ -3043,7 +2947,7 @@ static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
 {
 	errcode_t retval;
 
-	retval = e2fsck_pass1_thread_join_one(global_ctx, thread_ctx);
+	retval = e2fsck_pass1_merge_context(global_ctx, thread_ctx);
 	e2fsck_pass1_free_bitmap(&thread_ctx->inode_used_map);
 	e2fsck_pass1_free_bitmap(&thread_ctx->inode_bad_map);
 	e2fsck_pass1_free_bitmap(&thread_ctx->inode_dir_map);
Signature · 1 line
2.37.3
#84 lore raw

RFC 55/72 e2fsck: set E2F_FLAG_ALLOC_OK after threads

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

Only flag ALLOC OK after all threads finished without problem.

---
 e2fsck/pass1.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
e2fsck/pass1.c +8 -2
index 8e85f70f..a5dc6e44 100644
@@ -1385,9 +1385,12 @@ static void e2fsck_pass1_post(e2fsck_t ctx)
 {
 	struct problem_context pctx;
 	ext2_filsys fs = ctx->fs;
+	char *block_buf;
 
-	char *block_buf =
-		(char *)e2fsck_allocate_memory(ctx, ctx->fs->blocksize * 3,
+	if (e2fsck_should_abort(ctx))
+		return;
+
+	block_buf = (char *)e2fsck_allocate_memory(ctx, ctx->fs->blocksize * 3,
 					      "block interate buffer");
 	reserve_block_for_root_repair(ctx);
 	reserve_block_for_lnf_repair(ctx);
@@ -1465,6 +1468,8 @@ static void e2fsck_pass1_post(e2fsck_t ctx)
 		ext2fs_free_mem(&block_buf);
 		ctx->flags &= ~E2F_FLAG_DUP_BLOCK;
 	}
+
+	ctx->flags |= E2F_FLAG_ALLOC_OK;
 }
 
 
@@ -2355,6 +2360,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
 	}
 
 	ctx->flags |= E2F_FLAG_ALLOC_OK;
+	ext2fs_free_mem(&inodes_to_process);
 endit:
 	e2fsck_use_inode_shortcuts(ctx, 0);
 	ext2fs_free_mem(&inodes_to_process);
Signature · 1 line
2.37.3
#85 lore raw

RFC 56/72 e2fsck: wait fix thread finish before checking

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

Before proceeding next inodes, waitting existed
fixing finished.

[unlock from Jan's orphan inode path as well]
---
 e2fsck/e2fsck.c |  3 +++
 e2fsck/e2fsck.h |  5 +++-
 e2fsck/pass1.c  | 70 +++++++++++++++++++++++++++++++++++++++++++------
 e2fsck/util.c   | 56 ++++++++++++++++++++++++++++++++++++---
 4 files changed, 122 insertions(+), 12 deletions(-)
e2fsck/e2fsck.c +3 -0
index 1e295e3e..a5150dab 100644
@@ -187,6 +187,9 @@ errcode_t e2fsck_reset_context(e2fsck_t ctx)
 	ctx->fs_fragmented_dir = 0;
 	ctx->large_files = 0;
 	ctx->large_dirs = 0;
+#ifdef HAVE_PTHREAD
+	ctx->fs_need_locking = 0;
+#endif
 
 	for (i=0; i < MAX_EXTENT_DEPTH_COUNT; i++)
 		ctx->extent_depth_count[i] = 0;
e2fsck/e2fsck.h +4 -1
index e3276924..01bd9d01 100644
@@ -488,8 +488,9 @@ struct e2fsck_struct {
 
 #ifdef HAVE_PTHREAD
 	__u32			 fs_num_threads;
+	int			 fs_need_locking;
 	/* serialize fix operation for multiple threads */
-	pthread_mutex_t		 fs_fix_mutex;
+	pthread_rwlock_t	 fs_fix_rwlock;
 	/* protect block_found_map, block_dup_map */
 	pthread_rwlock_t	 fs_block_map_rwlock;
 #endif
@@ -553,6 +554,8 @@ extern int e2fsck_strnlen(const char * s, int count);
 
 extern void e2fsck_pass1(e2fsck_t ctx);
 extern void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf);
+extern void e2fsck_pass1_check_lock(e2fsck_t ctx);
+extern void e2fsck_pass1_check_unlock(e2fsck_t ctx);
 extern void e2fsck_pass2(e2fsck_t ctx);
 extern void e2fsck_pass3(e2fsck_t ctx);
 extern void e2fsck_pass4(e2fsck_t ctx);
e2fsck/pass1.c +62 -8

246 lines not on this page · raw message

e2fsck/util.c +53 -3

104 lines not on this page · raw message

Signature · 1 line
2.37.3
#86 lore raw

RFC 57/72 e2fsck: cleanup e2fsck_pass1_thread_join()

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

Use e2fsck_reset_context() to free memory to simpify codes.

---
 e2fsck/pass1.c | 33 +++++++--------------------------
 1 file changed, 7 insertions(+), 26 deletions(-)
e2fsck/pass1.c +7 -26
index 29333acf..93cff80e 100644
@@ -3008,39 +3008,20 @@ static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
 	errcode_t retval;
 
 	retval = e2fsck_pass1_merge_context(global_ctx, thread_ctx);
-	e2fsck_pass1_free_bitmap(&thread_ctx->inode_used_map);
-	e2fsck_pass1_free_bitmap(&thread_ctx->inode_bad_map);
-	e2fsck_pass1_free_bitmap(&thread_ctx->inode_dir_map);
-	e2fsck_pass1_free_bitmap(&thread_ctx->inode_bb_map);
-	e2fsck_pass1_free_bitmap(&thread_ctx->inode_imagic_map);
-	e2fsck_pass1_free_bitmap(&thread_ctx->inode_reg_map);
-	e2fsck_pass1_free_bitmap(&thread_ctx->inodes_to_rebuild);
-	e2fsck_pass1_free_bitmap(&thread_ctx->block_found_map);
-	e2fsck_pass1_free_bitmap(&thread_ctx->block_ea_map);
-	if (thread_ctx->refcount)
-		ea_refcount_free(thread_ctx->refcount);
-	if (thread_ctx->refcount_extra)
-		ea_refcount_free(thread_ctx->refcount_extra);
-	if (thread_ctx->ea_inode_refs)
-		ea_refcount_free(thread_ctx->ea_inode_refs);
-	if (thread_ctx->refcount_orig)
-		ea_refcount_free(thread_ctx->refcount_orig);
-	e2fsck_free_dir_info(thread_ctx);
-	ext2fs_free_icount(thread_ctx->inode_count);
-	ext2fs_free_icount(thread_ctx->inode_link_info);
-	if (thread_ctx->dirs_to_hash)
-		ext2fs_badblocks_list_free(thread_ctx->dirs_to_hash);
-	quota_release_context(&thread_ctx->qctx);
-	ext2fs_free_mem(&thread_ctx->invalid_block_bitmap_flag);
-	ext2fs_free_mem(&thread_ctx->invalid_inode_bitmap_flag);
-	ext2fs_free_mem(&thread_ctx->invalid_inode_table_flag);
 
+	/*
+	 * @block_metadata_map and @block_dup_map are
+	 * shared, so we don't free them.
+	 */
+	thread_ctx->block_metadata_map = NULL;
+	thread_ctx->block_dup_map = NULL;
 	if (thread_ctx->logf)
 		fclose(thread_ctx->logf);
 	if (thread_ctx->problem_logf) {
 		fputs("</problem_log>\n", thread_ctx->problem_logf);
 		fclose(thread_ctx->problem_logf);
 	}
+	e2fsck_reset_context(thread_ctx);
 	ext2fs_free_mem(&thread_ctx);
 	return retval;
 }
Signature · 1 line
2.37.3
#87 lore raw

RFC 58/72 e2fsck: make default smallest RA size to 1M

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

If we have a smaller inodes per group, default ra size could be very small(etc 128KiB), this hurts performances.

Tune above 128K to 1M, i see pass1 time drop down from 677.12 seconds to 246 secons with 32 threads.

---
 e2fsck/readahead.c | 4 ++++
 1 file changed, 4 insertions(+)
e2fsck/readahead.c +4 -0
index 38d4ec42..40b73664 100644
@@ -234,6 +234,8 @@ int e2fsck_can_readahead(ext2_filsys fs)
 	return err != EXT2_ET_OP_NOT_SUPPORTED;
 }
 
+#define MIN_DEFAULT_RA	(1024 * 1024)
+
 unsigned long long e2fsck_guess_readahead(ext2_filsys fs)
 {
 	unsigned long long guess;
@@ -245,6 +247,8 @@ unsigned long long e2fsck_guess_readahead(ext2_filsys fs)
 	 * in e2fsck runtime.
 	 */
 	guess = 2ULL * fs->blocksize * fs->inode_blocks_per_group;
+	if (guess < MIN_DEFAULT_RA)
+		guess = MIN_DEFAULT_RA;
 
 	/* Disable RA if it'd use more 1/50th of RAM. */
 	if (get_memory_size() > (guess * 50))
Signature · 1 line
2.37.3
#88 lore raw

RFC 59/72 e2fsck: update mmp block in one thread

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

For multiple threads, different threads will try to update mmp block at the same time, only allow one thread to update it.

---
 e2fsck/e2fsck.h |  1 +
 e2fsck/pass1.c  | 34 ++++++++++++++++++++++++++++++++--
 2 files changed, 33 insertions(+), 2 deletions(-)
e2fsck/e2fsck.h +1 -0
index 01bd9d01..2dd7ba27 100644
@@ -488,6 +488,7 @@ struct e2fsck_struct {
 
 #ifdef HAVE_PTHREAD
 	__u32			 fs_num_threads;
+	__u32			 mmp_update_thread;
 	int			 fs_need_locking;
 	/* serialize fix operation for multiple threads */
 	pthread_rwlock_t	 fs_fix_rwlock;
e2fsck/pass1.c +32 -2
index 93cff80e..ed4275c3 100644
@@ -1509,7 +1509,8 @@ void e2fsck_pass1_run(e2fsck_t ctx)
 	dgrp_t		ra_group = 0;
 	struct ea_quota	ea_ibody_quota;
 	struct process_inode_block *inodes_to_process;
-	int process_inode_count;
+	int process_inode_count, check_mmp;
+	e2fsck_t global_ctx = ctx->global_ctx ? ctx->global_ctx : ctx;
 
 	init_resource_track(&rtrack, ctx->fs->io);
 	clear_problem_context(&pctx);
@@ -1672,8 +1673,30 @@ void e2fsck_pass1_run(e2fsck_t ctx)
 #endif
 
 	while (1) {
+		check_mmp = 0;
 		e2fsck_pass1_check_lock(ctx);
-		if (ino % (fs->super->s_inodes_per_group * 4) == 1) {
+#ifdef	HAVE_PTHREAD
+		if (!global_ctx->mmp_update_thread) {
+			e2fsck_pass1_block_map_w_lock(ctx);
+			if (!global_ctx->mmp_update_thread) {
+				global_ctx->mmp_update_thread =
+					ctx->thread_info.et_thread_index + 1;
+				check_mmp = 1;
+			}
+			e2fsck_pass1_block_map_w_unlock(ctx);
+		}
+
+		/* only one active thread could update mmp block. */
+		e2fsck_pass1_block_map_r_lock(ctx);
+		if (global_ctx->mmp_update_thread ==
+		    ctx->thread_info.et_thread_index + 1)
+			check_mmp = 1;
+		e2fsck_pass1_block_map_r_unlock(ctx);
+#else
+		check_mmp = 1;
+#endif
+
+		if (check_mmp && (ino % (fs->super->s_inodes_per_group * 4) == 1)) {
 			if (e2fsck_mmp_update(fs))
 				fatal_error(ctx, 0);
 		}
@@ -2437,6 +2460,13 @@ endit:
 		print_resource_track(ctx, _("Pass 1"), &rtrack, ctx->fs->io);
 	else
 		ctx->invalid_bitmaps++;
+#ifdef	HAVE_PTHREAD
+	/* reset update_thread after this thread exit */
+	e2fsck_pass1_block_map_w_lock(ctx);
+	if (check_mmp)
+		global_ctx->mmp_update_thread = 0;
+	e2fsck_pass1_block_map_w_unlock(ctx);
+#endif
 }
 
 #ifdef HAVE_PTHREAD
Signature · 1 line
2.37.3
#89 lore raw

RFC 60/72 e2fsck: reset @inodes_to_rebuild if restart

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

Verify multiple thread on a corrupted images hit following bug:

pass1.c:2902: e2fsck_pass1_thread_prepare:
Assertion `global_ctx->inodes_to_rebuild == NULL' failed. Signal (6) SIGABRT si_code=SI_TKILL ./e2fsck/e2fsck[0x43829e] /lib64/libpthread.so.0(+0x14b20)[0x7f3b45135b20] /lib64/libc.so.6(gsignal+0x145)[0x7f3b44f2c625] /lib64/libc.so.6(abort+0x12b)[0x7f3b44f158d9] /lib64/libc.so.6(+0x257a9)[0x7f3b44f157a9] /lib64/libc.so.6(+0x34a66)[0x7f3b44f24a66] ./e2fsck/e2fsck(e2fsck_pass1+0x1662)[0x423572] ./e2fsck/e2fsck(e2fsck_run+0x5a)[0x41611a] ./e2fsck/e2fsck(main+0x1608)[0x4121b8] /lib64/libc.so.6(__libc_start_main+0xf3)[0x7f3b44f171a3] ./e2fsck/e2fsck(_start+0x2e)[0x413dde]

@inodes_to_rebuild could be not NULL after we restart pass1

---
 e2fsck/e2fsck.c | 4 ++++
 1 file changed, 4 insertions(+)
e2fsck/e2fsck.c +4 -0
index a5150dab..53af8905 100644
@@ -79,6 +79,10 @@ errcode_t e2fsck_reset_context(e2fsck_t ctx)
 		ext2fs_free_block_bitmap(ctx->inode_casefold_map);
 		ctx->inode_casefold_map = 0;
 	}
+	if (ctx->inodes_to_rebuild) {
+		ext2fs_free_inode_bitmap(ctx->inodes_to_rebuild);
+		ctx->inodes_to_rebuild = 0;
+	}
 	if (ctx->inode_link_info) {
 		ext2fs_free_icount(ctx->inode_link_info);
 		ctx->inode_link_info = 0;
Signature · 1 line
2.37.3
#90 lore raw

RFC 61/72 tests: add pfsck test

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

pfsck run on a clean fs should not return any errors.

Generate an image with possible features enabled,
especially EA shared blocks etc.

---
 tests/f_multithread_ok/expect.1 |   7 +++++++
 tests/f_multithread_ok/image.gz | Bin 0 -> 796311 bytes
 tests/f_multithread_ok/name     |   1 +
 tests/f_multithread_ok/script   |  21 +++++++++++++++++++++
 4 files changed, 29 insertions(+)
 create mode 100644 tests/f_multithread_ok/expect.1
 create mode 100644 tests/f_multithread_ok/image.gz
 create mode 100644 tests/f_multithread_ok/name
 create mode 100644 tests/f_multithread_ok/script
tests/f_multithread_ok/expect.1 new file +7 -0
new file mode 100644
index 00000000..4742f408
@@ -0,0 +1,7 @@
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+test_filesys: 10251/32768 files (0.0% non-contiguous), 2218/131072 blocks
+Exit status is 0
tests/f_multithread_ok/image.gz new file binary +0 -0
new file mode 100644
index 0000000000000000000000000000000000000000..42622c3786a81f348f2935d4f9cc2812bcf539d4
GIT binary patch
tests/f_multithread_ok/name new file +1 -0
new file mode 100644
index 00000000..45dd561d
@@ -0,0 +1 @@
+multiple threads checking on clean fs
tests/f_multithread_ok/script new file +21 -0
new file mode 100644
index 00000000..7334cde6
@@ -0,0 +1,21 @@
+FSCK_OPT="-fym4"
+SKIP_VERIFY="true"
+ONE_PASS_ONLY="true"
+SKIP_CLEANUP="true"
+
+. $cmd_dir/run_e2fsck
+
+grep -v Thread $OUT1 > $OUT1.tmp
+cmp -s $EXP1 $OUT1.tmp
+status1=$?
+if [ "$status1" -eq 0 ]; then
+	echo "$test_name: $test_description: ok"
+	touch $test_name.ok
+else
+	echo "$test_name: $test_description: failed"
+	diff $DIFF_OPTS $EXP1 $OUT1.tmp > $test_name.failed
+fi
+
+unset IMAGE FSCK_OPT SECOND_FSCK_OPT OUT1 OUT2 EXP1 EXP2
+unset SKIP_VERIFY SKIP_CLEANUP SKIP_GUNZIP ONE_PASS_ONLY PREP_CMD
+unset DESCRIPTION SKIP_UNLINK AFTER_CMD PASS_ZERO
Signature · 1 line
2.37.3
#91 lore raw

RFC 62/72 e2fsck: fix memory leaks with pfsck enabled

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

valgrind detected two memory leaks:

1) quota context is not released after merging.
2) @refcount_orig should be released

---
 e2fsck/e2fsck.c | 4 ++++
 e2fsck/pass1.c  | 1 +
 2 files changed, 5 insertions(+)
e2fsck/e2fsck.c +4 -0
index 53af8905..db0a5059 100644
@@ -106,6 +106,10 @@ errcode_t e2fsck_reset_context(e2fsck_t ctx)
 		ea_refcount_free(ctx->refcount_extra);
 		ctx->refcount_extra = 0;
 	}
+	if (ctx->refcount_orig) {
+		ea_refcount_free(ctx->refcount_orig);
+		ctx->refcount_orig = 0;
+	}
 	if (ctx->ea_block_quota_blocks) {
 		ea_refcount_free(ctx->ea_block_quota_blocks);
 		ctx->ea_block_quota_blocks = 0;
e2fsck/pass1.c +1 -0
index ed4275c3..d745699d 100644
@@ -3039,6 +3039,7 @@ static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
 
 	retval = e2fsck_pass1_merge_context(global_ctx, thread_ctx);
 
+	quota_release_context(&thread_ctx->qctx);
 	/*
 	 * @block_metadata_map and @block_dup_map are
 	 * shared, so we don't free them.
Signature · 1 line
2.37.3
#92 lore raw

RFC 63/72 e2fsck: misc cleanups for pfsck

In reply to Ritesh Harjani (IBM)

From: Andreas Dilger <[email protected]>

Add -m option description to e2fsck.8 man page.

Rename e2fsck_struct fs_num_threads to pfs_num_threads to avoid confusion with the ext2_filsys fs_num_threads field, and move thread_info to be together with the other HAVE_PTHREAD fields.

Move ext2_filsys fs_num_threads to fit into the __u16 "pad" field to avoid consuming one of the few remaining __u32 reserved fields.

Fix a few print format warnings.

---
 e2fsck/e2fsck.8.in |  8 +++++++-
 e2fsck/e2fsck.h    | 13 +++++--------
 e2fsck/pass1.c     | 20 +++++++++++---------
 e2fsck/unix.c      |  4 ++--
 4 files changed, 25 insertions(+), 20 deletions(-)
e2fsck/e2fsck.8.in +7 -1
index dc6a5856..37dc8936 100644
@@ -8,7 +8,7 @@ e2fsck \- check a Linux ext2/ext3/ext4 file system
 .SH SYNOPSIS
 .B e2fsck
 [
-.B \-pacnyrdfkvtDFV
+.B \-pacnyrdfkmvtDFV
 ]
 [
 .B \-b
@@ -333,6 +333,12 @@ Set the bad blocks list to be the list of blocks specified by
 option, except the bad blocks list is cleared before the blocks listed
 in the file are added to the bad blocks list.)
 .TP
+.B \-m " threads"
+Run e2fsck with up to the specified number of
+.IR threads .
+The actual number of threads may be lower, if the filesystem does not
+have enough block groups to effectively parallelize the workload.
+.TP
 .B \-n
 Open the file system read-only, and assume an answer of `no' to all
 questions.  Allows
e2fsck/e2fsck.h +5 -8
index 2dd7ba27..33866316 100644
@@ -277,8 +277,8 @@ struct e2fsck_thread {
 	dgrp_t		et_group_next;
 	/* Scanned inode number */
 	ext2_ino_t	et_inode_number;
-	char		et_log_buf[2048];
 	char		et_log_length;
+	char		et_log_buf[2048];
 };
 #endif
 
@@ -367,11 +367,6 @@ struct e2fsck_struct {
 	ext2_ino_t		stashed_ino;
 	struct ext2_inode	*stashed_inode;
 
-	/* if @global_ctx is null, this field is unused */
-#ifdef HAVE_PTHREAD
-	struct e2fsck_thread	 thread_info;
-#endif
-
 	/*
 	 * Location of the lost and found directory
 	 */
@@ -487,7 +482,9 @@ struct e2fsck_struct {
 	char *undo_file;
 
 #ifdef HAVE_PTHREAD
-	__u32			 fs_num_threads;
+	/* if @global_ctx is null, this field is unused */
+	struct e2fsck_thread	 thread_info;
+	__u32			 pfs_num_threads;
 	__u32			 mmp_update_thread;
 	int			 fs_need_locking;
 	/* serialize fix operation for multiple threads */
@@ -732,7 +729,7 @@ void check_resize_inode(e2fsck_t ctx);
 int check_init_orphan_file(e2fsck_t ctx);
 
 /* util.c */
-#define E2FSCK_MAX_THREADS	(65536)
+#define E2FSCK_MAX_THREADS	(65535)
 extern void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned long size,
 				    const char *description);
 extern int ask(e2fsck_t ctx, const char * string, int def);
e2fsck/pass1.c +11 -9
index d745699d..8a6cdd8f 100644
@@ -1265,7 +1265,7 @@ static void e2fsck_pass1_set_thread_num(e2fsck_t ctx)
 {
 	unsigned flexbg_size = 1;
 	ext2_filsys fs = ctx->fs;
-	int num_threads = ctx->fs_num_threads;
+	int num_threads = ctx->pfs_num_threads;
 	int max_threads;
 
 	if (num_threads < 1) {
@@ -1285,6 +1285,8 @@ static void e2fsck_pass1_set_thread_num(e2fsck_t ctx)
 	max_threads = fs->group_desc_count / flexbg_size;
 	if (max_threads == 0)
 		max_threads = 1;
+	if (max_threads > E2FSCK_MAX_THREADS)
+		max_threads = E2FSCK_MAX_THREADS;
 
 	if (num_threads > max_threads) {
 		fprintf(stderr, "Use max possible thread num: %d instead\n",
@@ -1292,7 +1294,7 @@ static void e2fsck_pass1_set_thread_num(e2fsck_t ctx)
 		num_threads = max_threads;
 	}
 out:
-	ctx->fs_num_threads = num_threads;
+	ctx->pfs_num_threads = num_threads;
 	ctx->fs->fs_num_threads = num_threads;
 }
 #endif
@@ -1320,7 +1322,7 @@ static errcode_t e2fsck_pass1_prepare(e2fsck_t ctx)
 
 #ifdef HAVE_PTHREAD
 	/* don't use more than 1/10 of memory for threads checking */
-	readahead_kb = get_memory_size() / (10 * ctx->fs_num_threads);
+	readahead_kb = get_memory_size() / (10 * ctx->pfs_num_threads);
 	/* maybe better disable RA if this is too small? */
 	if (ctx->readahead_kb > readahead_kb)
 		ctx->readahead_kb = readahead_kb;
@@ -1378,7 +1380,7 @@ static errcode_t e2fsck_pass1_prepare(e2fsck_t ctx)
 #ifdef	HAVE_PTHREAD
 	pthread_rwlock_init(&ctx->fs_fix_rwlock, NULL);
 	pthread_rwlock_init(&ctx->fs_block_map_rwlock, NULL);
-	if (ctx->fs_num_threads > 1)
+	if (ctx->pfs_num_threads > 1)
 		ctx->fs_need_locking = 1;
 	else
 		ctx->fs_need_locking = 0;
@@ -1659,7 +1661,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
 	if (ctx->global_ctx) {
 		if (ctx->options & E2F_OPT_DEBUG &&
 		    ctx->options & E2F_OPT_MULTITHREAD)
-			fprintf(stderr, "thread %d jumping to group %d\n",
+			fprintf(stderr, "thread %d jumping to group %u\n",
 					ctx->thread_info.et_thread_index,
 					ctx->thread_info.et_group_start);
 		pctx.errcode = ext2fs_inode_scan_goto_blockgroup(scan,
@@ -3064,7 +3066,7 @@ static int e2fsck_pass1_threads_join(struct e2fsck_thread_info *infos,
 	errcode_t ret = 0;
 	int i;
 	struct e2fsck_thread_info *pinfo;
-	int num_threads = global_ctx->fs_num_threads;
+	int num_threads = global_ctx->pfs_num_threads;
 
 	/* merge invalid bitmaps will recalculate it */
 	global_ctx->invalid_bitmaps = 0;
@@ -3130,7 +3132,7 @@ static void *e2fsck_pass1_thread(void *arg)
 out:
 	if (thread_ctx->options & E2F_OPT_MULTITHREAD)
 		log_out(thread_ctx,
-			_("Scanned group range [%lu, %lu), inodes %lu\n"),
+			_("Scanned group range [%u, %u), inodes %u\n"),
 			thread_ctx->thread_info.et_group_start,
 			thread_ctx->thread_info.et_group_end,
 			thread_ctx->thread_info.et_inode_number);
@@ -3156,7 +3158,7 @@ static int e2fsck_pass1_threads_start(struct e2fsck_thread_info **pinfo,
 	int i;
 	e2fsck_t thread_ctx;
 	dgrp_t average_group;
-	int num_threads = global_ctx->fs_num_threads;
+	int num_threads = global_ctx->pfs_num_threads;
 #ifdef DEBUG_THREADS
 	struct e2fsck_thread_debug thread_debug =
 		{PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};
@@ -3259,7 +3261,7 @@ void e2fsck_pass1(e2fsck_t ctx)
 	if (retval)
 		return;
 #ifdef HAVE_PTHREAD
-	if (ctx->options & E2F_OPT_MULTITHREAD || ctx->fs_num_threads > 1)
+	if (ctx->options & E2F_OPT_MULTITHREAD || ctx->pfs_num_threads > 1)
 		e2fsck_pass1_multithread(ctx);
 	else
 #endif
e2fsck/unix.c +2 -2
index dfa3f897..461ab8cb 100644
@@ -915,12 +915,12 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
 					_("Invalid multiple thread num.\n"));
 			if (thread_num > E2FSCK_MAX_THREADS) {
 				fprintf(stderr,
-					_("threads %lu too large (max %lu)\n"),
+					_("threads %lu too large (max %u)\n"),
 					thread_num, E2FSCK_MAX_THREADS);
 				fatal_error(ctx, 0);
 			}
 			ctx->options |= E2F_OPT_MULTITHREAD;
-			ctx->fs_num_threads = thread_num;
+			ctx->pfs_num_threads = thread_num;
 			break;
 #endif
 		case 'n':
Signature · 1 line
2.37.3
#93 lore raw

RFC 64/72 e2fsck: propagate number of threads

In reply to Ritesh Harjani (IBM)

From: Saranya Muruganandam <[email protected]>

Sometimes, such as in orphan_inode case, e2fsck_pass1 is called after reading the block bitmaps. This results in reading the block bitmap sequentially and multithreading only gets kicked in later. Fix the thread count earlier while setting up the file system.

---
 e2fsck/unix.c | 3 +++
 1 file changed, 3 insertions(+)
e2fsck/unix.c +3 -0
index 461ab8cb..fb0df85a 100644
@@ -1745,6 +1745,9 @@ failure:
 
 	ctx->fs = fs;
 	fs->now = ctx->now;
+#ifdef HAVE_PTHREAD
+	fs->fs_num_threads = ctx->pfs_num_threads;
+#endif
 	sb = fs->super;
 
 	if (sb->s_rev_level > E2FSCK_CURRENT_REV) {
Signature · 1 line
2.37.3
#94 lore raw

RFC 65/72 e2fsck: Annotating fields in e2fsck_struct

In reply to Ritesh Harjani (IBM)

From: Saranya Muruganandam <[email protected]>

Adding information on fields in e2fsck_struct
on how they are used when running parallel fsck.

---
 e2fsck/e2fsck.h | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)
e2fsck/e2fsck.h +14 -6
index 33866316..1e82b048 100644
@@ -285,7 +285,7 @@ struct e2fsck_thread {
 struct e2fsck_struct {
 	/* Global context to get the cancel flag */
 	e2fsck_t global_ctx;
-	ext2_filsys fs;
+	ext2_filsys fs; /* [fs_fix_rwlock] */
 	const char *program_name;
 	char *filesystem_name;
 	char *device_name;
@@ -294,7 +294,9 @@ struct e2fsck_struct {
 	char	*log_fn;
 	FILE	*problem_logf;
 	char	*problem_log_fn;
-	int	flags;		/* E2fsck internal flags */
+	/* E2fsck internal flags.
+	 * shared by different threads for pass1 [fs_fix_rwlock] */
+	int	flags;
 	int	options;
 	unsigned blocksize;	/* blocksize */
 	blk64_t	use_superblock;	/* sb requested by user */
@@ -314,6 +316,7 @@ struct e2fsck_struct {
 	int (*progress)(e2fsck_t ctx, int pass, unsigned long cur,
 			unsigned long max);
 
+	/* The following inode bitmaps are separately used in thread_ctx Pass1*/
 	ext2fs_inode_bitmap inode_used_map; /* Inodes which are in use */
 	ext2fs_inode_bitmap inode_bad_map; /* Inodes which are bad somehow */
 	ext2fs_inode_bitmap inode_dir_map; /* Inodes which are directories */
@@ -322,12 +325,14 @@ struct e2fsck_struct {
 	ext2fs_inode_bitmap inode_reg_map; /* Inodes which are regular files*/
 	ext2fs_inode_bitmap inode_casefold_map; /* Inodes which are casefolded */
 
+	/* Following 3 protected by [fs_block_map_rwlock] */
 	ext2fs_block_bitmap block_found_map; /* Blocks which are in use */
 	ext2fs_block_bitmap block_dup_map; /* Blks referenced more than once */
 	ext2fs_block_bitmap block_ea_map; /* Blocks which are used by EA's */
 
 	/*
-	 * Inode count arrays
+	 * Inode count arrays.
+	 * Separately used in thread_ctx, pass1
 	 */
 	ext2_icount_t	inode_count;
 	ext2_icount_t inode_link_info;
@@ -349,7 +354,8 @@ struct e2fsck_struct {
 
 	/*
 	 * Array of flags indicating whether an inode bitmap, block
-	 * bitmap, or inode table is invalid
+	 * bitmap, or inode table is invalid.
+	 * Separately used in thread_ctx, pass1
 	 */
 	int *invalid_inode_bitmap_flag;
 	int *invalid_block_bitmap_flag;
@@ -362,7 +368,8 @@ struct e2fsck_struct {
 	char *block_buf;
 
 	/*
-	 * For pass1_check_directory and pass1_get_blocks
+	 * For pass1_check_directory and pass1_get_blocks.
+	 * Separately used in thread_ctx in pass1
 	 */
 	ext2_ino_t		stashed_ino;
 	struct ext2_inode	*stashed_inode;
@@ -421,6 +428,7 @@ struct e2fsck_struct {
 
 	/*
 	 * How we display the progress update (for unix)
+	 * shared by different threads for pass1 [fs_fix_rwlock]
 	 */
 	int progress_fd;
 	int progress_pos;
@@ -429,7 +437,7 @@ struct e2fsck_struct {
 	int interactive;	/* Are we connected directly to a tty? */
 	char start_meta[2], stop_meta[2];
 
-	/* File counts */
+	/* File counts. Separately used in thread_ctx, pass1 */
 	__u32 fs_directory_count;
 	__u32 fs_regular_count;
 	__u32 fs_blockdev_count;
Signature · 1 line
2.37.3
#95 lore raw

RFC 66/72 e2fsck: merge casefolded dir lists after thread finish

In reply to Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

This is missed and should be done after threads finish.

Original commit:
18538b27 ("LU-8465 e2fsck: merge encrypted dir lists after thread finish") Upstream is now using e2fsck_struct->casefolded_dirs instead of encrypted_dirs.

Only for the correctness of pfsck, we won't use casefolded feature on ldiskfs.

---
 e2fsck/pass1.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)
e2fsck/pass1.c +26 -0
index 8a6cdd8f..7345c96d 100644
@@ -2614,6 +2614,7 @@ static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thre
 	}
 	memcpy(thread_context, global_ctx, sizeof(struct e2fsck_struct));
 	thread_context->block_dup_map = NULL;
+	thread_context->casefolded_dirs = NULL;
 
 	retval = e2fsck_allocate_block_bitmap(global_ctx->fs,
 				_("in-use block map"), EXT2FS_BMAP64_RBTREE,
@@ -2905,6 +2906,24 @@ static errcode_t e2fsck_pass1_merge_ea_refcount(e2fsck_t global_ctx,
 	return retval;
 }
 
+static errcode_t e2fsck_pass1_merge_casefolded_dirs(e2fsck_t global_ctx,
+						   e2fsck_t thread_ctx)
+{
+	errcode_t retval = 0;
+
+	if (!thread_ctx->casefolded_dirs)
+		return 0;
+
+	if (!global_ctx->casefolded_dirs)
+		retval = ext2fs_badblocks_copy(thread_ctx->casefolded_dirs,
+					       &global_ctx->casefolded_dirs);
+	else
+		retval = ext2fs_badblocks_merge(thread_ctx->casefolded_dirs,
+						global_ctx->casefolded_dirs);
+
+	return retval;
+}
+
 static errcode_t e2fsck_pass1_merge_context(e2fsck_t global_ctx,
 					    e2fsck_t thread_ctx)
 {
@@ -2971,6 +2990,13 @@ static errcode_t e2fsck_pass1_merge_context(e2fsck_t global_ctx,
 	if (retval)
 		return retval;
 
+	retval = e2fsck_pass1_merge_casefolded_dirs(global_ctx, thread_ctx);
+	if (retval) {
+		com_err(global_ctx->program_name, 0,
+			_("while merging casefolded dirs\n"));
+		return retval;
+	}
+
 	e2fsck_pass1_merge_invalid_bitmaps(global_ctx, thread_ctx);
 
 	retval = e2fsck_pass1_merge_bitmap(global_fs,
Signature · 1 line
2.37.3
#99 lore raw

RFC 69/72 e2fsck: Fix double free of inodes_to_process

In reply to Ritesh Harjani (IBM)

Found during code review, this fixes the double free of inodes_to_process in e2fsck_pass1_run.

---
 e2fsck/pass1.c | 1 -
 1 file changed, 1 deletion(-)
e2fsck/pass1.c +0 -1
index c934b021..4168a45d 100644
@@ -2436,7 +2436,6 @@ void e2fsck_pass1_run(e2fsck_t ctx)
 	}
 
 	ctx->flags |= E2F_FLAG_ALLOC_OK;
-	ext2fs_free_mem(&inodes_to_process);
 endit:
 	e2fsck_use_inode_shortcuts(ctx, 0);
 	ext2fs_free_mem(&inodes_to_process);
Signature · 1 line
2.37.3
#98 lore raw

RFC 68/72 e2fsck: Fix io->align assert check

In reply to Ritesh Harjani (IBM)

dest->io->align might still get set later in case of non-aligned read done by any thread in raw_read_blk(). Hence remove this assert check.

---
 e2fsck/pass1.c | 1 -
 1 file changed, 1 deletion(-)
e2fsck/pass1.c +0 -1
index e7dc017c..c934b021 100644
@@ -2514,7 +2514,6 @@ static errcode_t e2fsck_open_channel_fs(ext2_filsys dest, e2fsck_t dest_context,
 	assert(dest->io->flags == src->io->flags);
 	assert(dest->io->app_data == dest);
 	assert(src->io->app_data == src);
-	assert(dest->io->align == src->io->align);
 
 	dest->priv_data = dest_context;
 	dest_context->fs = dest;
Signature · 1 line
2.37.3
#100 lore raw

RFC 70/72 e2fsck: Fix and simplify update_mmp in case of pfsck

In reply to Ritesh Harjani (IBM)

This adds pass1_update_mmp_enter() & pass1_update_mmp_exit() routines to update mmp block. This also fixes a data race reported by threadsan because of reading and writing to mmp_update_thread variable.

---
 e2fsck/pass1.c | 97 ++++++++++++++++++++++++++++++++------------------
 1 file changed, 63 insertions(+), 34 deletions(-)
e2fsck/pass1.c +63 -34
index 4168a45d..2ff83fcb 100644
@@ -1097,6 +1097,66 @@ out:
 	return 0;
 }
 
+static void pass1_update_mmp_enter(e2fsck_t ctx, ext2_ino_t	ino)
+{
+	ext2_filsys fs = ctx->fs;
+	e2fsck_t global_ctx = ctx->global_ctx ? ctx->global_ctx : ctx;
+	int check_mmp = 0;
+	int set_mmp = 0;
+
+#ifdef HAVE_PTHREAD
+	/* only one active thread could update mmp block. */
+	e2fsck_pass1_block_map_r_lock(ctx);
+	if (!global_ctx->mmp_update_thread)
+		set_mmp = 1;
+	if (global_ctx->mmp_update_thread == ctx->thread_info.et_thread_index + 1)
+		check_mmp = 1;
+	e2fsck_pass1_block_map_r_unlock(ctx);
+
+	if (!check_mmp && !set_mmp)
+		return;
+
+	if (set_mmp) {
+		e2fsck_pass1_block_map_w_lock(ctx);
+		if (!global_ctx->mmp_update_thread) {
+			global_ctx->mmp_update_thread =
+				ctx->thread_info.et_thread_index + 1;
+			check_mmp = 1;
+		}
+		e2fsck_pass1_block_map_w_unlock(ctx);
+	}
+#else
+	check_mmp = 1;
+#endif
+
+	if (check_mmp && (ino % (fs->super->s_inodes_per_group * 4) == 1)) {
+		if (e2fsck_mmp_update(fs))
+			fatal_error(ctx, 0);
+	}
+}
+
+static void pass1_update_mmp_exit(e2fsck_t ctx)
+{
+	ext2_filsys fs = ctx->fs;
+	e2fsck_t global_ctx = ctx->global_ctx ? ctx->global_ctx : ctx;
+	int set_mmp = 0;
+
+#ifdef HAVE_PTHREAD
+	e2fsck_pass1_block_map_r_lock(ctx);
+	if (global_ctx->mmp_update_thread == ctx->thread_info.et_thread_index + 1)
+		set_mmp = 1;
+	e2fsck_pass1_block_map_r_unlock(ctx);
+
+	if (!set_mmp)
+		return;
+
+	/* reset update_thread after this thread exit */
+	e2fsck_pass1_block_map_w_lock(ctx);
+	global_ctx->mmp_update_thread = 0;
+	e2fsck_pass1_block_map_w_unlock(ctx);
+#endif
+}
+
 static void pass1_readahead(e2fsck_t ctx, dgrp_t *group, ext2_ino_t *next_ino)
 {
 	ext2_ino_t inodes_in_group = 0, inodes_per_block, inodes_per_buffer;
@@ -1511,7 +1571,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
 	dgrp_t		ra_group = 0;
 	struct ea_quota	ea_ibody_quota;
 	struct process_inode_block *inodes_to_process;
-	int process_inode_count, check_mmp;
+	int process_inode_count;
 	e2fsck_t global_ctx = ctx->global_ctx ? ctx->global_ctx : ctx;
 
 	init_resource_track(&rtrack, ctx->fs->io);
@@ -1675,33 +1735,8 @@ void e2fsck_pass1_run(e2fsck_t ctx)
 #endif
 
 	while (1) {
-		check_mmp = 0;
 		e2fsck_pass1_check_lock(ctx);
-#ifdef	HAVE_PTHREAD
-		if (!global_ctx->mmp_update_thread) {
-			e2fsck_pass1_block_map_w_lock(ctx);
-			if (!global_ctx->mmp_update_thread) {
-				global_ctx->mmp_update_thread =
-					ctx->thread_info.et_thread_index + 1;
-				check_mmp = 1;
-			}
-			e2fsck_pass1_block_map_w_unlock(ctx);
-		}
-
-		/* only one active thread could update mmp block. */
-		e2fsck_pass1_block_map_r_lock(ctx);
-		if (global_ctx->mmp_update_thread ==
-		    ctx->thread_info.et_thread_index + 1)
-			check_mmp = 1;
-		e2fsck_pass1_block_map_r_unlock(ctx);
-#else
-		check_mmp = 1;
-#endif
-
-		if (check_mmp && (ino % (fs->super->s_inodes_per_group * 4) == 1)) {
-			if (e2fsck_mmp_update(fs))
-				fatal_error(ctx, 0);
-		}
+		pass1_update_mmp_enter(ctx, ino);
 		old_op = ehandler_operation(eop_next_inode);
 		pctx.errcode = ext2fs_get_next_inode_full(scan, &ino,
 							  inode, inode_size);
@@ -2458,13 +2493,7 @@ endit:
 		print_resource_track(ctx, _("Pass 1"), &rtrack, ctx->fs->io);
 	else
 		ctx->invalid_bitmaps++;
-#ifdef	HAVE_PTHREAD
-	/* reset update_thread after this thread exit */
-	e2fsck_pass1_block_map_w_lock(ctx);
-	if (check_mmp)
-		global_ctx->mmp_update_thread = 0;
-	e2fsck_pass1_block_map_w_unlock(ctx);
-#endif
+	pass1_update_mmp_exit(ctx);
 }
 
 #ifdef HAVE_PTHREAD
Signature · 1 line
2.37.3
#96 lore raw

RFC 67/72 sec: support encrypted files handling in pfsck mode

In reply to Ritesh Harjani (IBM)

From: Sebastien Buisson <[email protected]>

e2fsck needs to be improved in order to support encrypted files handling in parallel fsck mode. The e2fsck_merge_encrypted_info() function is added to merge encrypted inodes info collected from different threads in pass1, so that it can be used in pass2.

---
 e2fsck/e2fsck.h          |   2 +
 e2fsck/encrypted_files.c | 139 +++++++++++++++++++++++++++++++++++++++
 e2fsck/pass1.c           |  26 +++++++-
 3 files changed, 164 insertions(+), 3 deletions(-)
e2fsck/e2fsck.h +2 -0
index 1e82b048..e4fb782a 100644
@@ -642,6 +642,8 @@ __u32 find_encryption_policy(e2fsck_t ctx, ext2_ino_t ino);
 
 void destroy_encryption_policy_map(e2fsck_t ctx);
 void destroy_encrypted_file_info(e2fsck_t ctx);
+int e2fsck_merge_encrypted_info(e2fsck_t ctx, struct encrypted_file_info *src,
+				struct encrypted_file_info *dest);
 
 /* extents.c */
 errcode_t e2fsck_rebuild_extents_later(e2fsck_t ctx, ext2_ino_t ino);
e2fsck/encrypted_files.c +139 -0

142 lines not on this page · raw message

e2fsck/pass1.c +23 -3
index 7345c96d..e7dc017c 100644
@@ -2411,9 +2411,6 @@ void e2fsck_pass1_run(e2fsck_t ctx)
 		ctx->ea_block_quota_inodes = 0;
 	}
 
-	/* We don't need the encryption policy => ID map any more */
-	destroy_encryption_policy_map(ctx);
-
 	if (ctx->flags & E2F_FLAG_RESTART) {
 		/*
 		 * Only the master copy of the superblock and block
@@ -2703,6 +2700,23 @@ static void e2fsck_pass1_merge_dx_dir(e2fsck_t global_ctx, e2fsck_t thread_ctx)
 	e2fsck_merge_dx_dir(global_ctx, thread_ctx);
 }
 
+static int e2fsck_pass1_merge_encrypted_info(e2fsck_t global_ctx,
+					      e2fsck_t thread_ctx)
+{
+	if (thread_ctx->encrypted_files == NULL)
+		return 0;
+
+	if (global_ctx->encrypted_files == NULL) {
+		global_ctx->encrypted_files = thread_ctx->encrypted_files;
+		thread_ctx->encrypted_files = NULL;
+		return 0;
+	}
+
+	return e2fsck_merge_encrypted_info(global_ctx,
+					   thread_ctx->encrypted_files,
+					   global_ctx->encrypted_files);
+}
+
 static inline errcode_t
 e2fsck_pass1_merge_icount(ext2_icount_t *dest_icount,
 			  ext2_icount_t *src_icount)
@@ -2963,6 +2977,12 @@ static errcode_t e2fsck_pass1_merge_context(e2fsck_t global_ctx,
 
 	e2fsck_pass1_merge_dir_info(global_ctx, thread_ctx);
 	e2fsck_pass1_merge_dx_dir(global_ctx, thread_ctx);
+	retval = e2fsck_pass1_merge_encrypted_info(global_ctx, thread_ctx);
+	if (retval) {
+		com_err(global_ctx->program_name, 0,
+			_("while merging encrypted info\n"));
+		return retval;
+	}
 
 	retval = ext2fs_merge_fs(&(thread_ctx->fs));
 	if (retval) {
Signature · 1 line
2.37.3
#101 lore raw

RFC 71/72 e2fsck: Make threads call log_out after pthread_join

In reply to Ritesh Harjani (IBM)

All fsck threads will call for log_out prints after the pass1 their respective pass1 scanning is completed. This patch moves the log_out print from to after the pthread_join operation.
This makes the threads always print the info in order.

---
 e2fsck/pass1.c | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)
e2fsck/pass1.c +6 -7
index 2ff83fcb..90adc419 100644
@@ -3112,6 +3112,12 @@ static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
 {
 	errcode_t retval;
 
+	log_out(thread_ctx,
+			_("Scanned group range [%u, %u), inodes %u\n"),
+			thread_ctx->thread_info.et_group_start,
+			thread_ctx->thread_info.et_group_end,
+			thread_ctx->thread_info.et_inode_number);
+
 	retval = e2fsck_pass1_merge_context(global_ctx, thread_ctx);
 
 	quota_release_context(&thread_ctx->qctx);
@@ -3203,13 +3209,6 @@ static void *e2fsck_pass1_thread(void *arg)
 	e2fsck_pass1_run(thread_ctx);
 
 out:
-	if (thread_ctx->options & E2F_OPT_MULTITHREAD)
-		log_out(thread_ctx,
-			_("Scanned group range [%u, %u), inodes %u\n"),
-			thread_ctx->thread_info.et_group_start,
-			thread_ctx->thread_info.et_group_end,
-			thread_ctx->thread_info.et_inode_number);
-
 #ifdef DEBUG_THREADS
 	pthread_mutex_lock(&thread_debug->etd_mutex);
 	thread_debug->etd_finished_threads++;
Signature · 1 line
2.37.3
#102 lore raw

RFC 72/72 tests/f_multithread: Fix f_multithread related tests

In reply to Ritesh Harjani (IBM)

With log_out() function now changed to print the message after the pthread join operation, it is safe to also add "Scan group range" related messages in expect files to compare against the pfsck output.

---
 tests/f_multithread_ok/expect.1 |  8 ++++++++
 tests/f_multithread_ok/script   | 17 -----------------
 2 files changed, 8 insertions(+), 17 deletions(-)
tests/f_multithread_ok/expect.1 +8 -0
index 4742f408..cecc11db 100644
@@ -1,4 +1,12 @@
 Pass 1: Checking inodes, blocks, and sizes
+[Thread 0] Scan group range [0, 1)
+[Thread 1] Scan group range [1, 2)
+[Thread 2] Scan group range [2, 3)
+[Thread 3] Scan group range [3, 4)
+[Thread 0] Scanned group range [0, 1), inodes 8192
+[Thread 1] Scanned group range [1, 2), inodes 8192
+[Thread 2] Scanned group range [2, 3), inodes 8192
+[Thread 3] Scanned group range [3, 4), inodes 8192
 Pass 2: Checking directory structure
 Pass 3: Checking directory connectivity
 Pass 4: Checking reference counts
tests/f_multithread_ok/script +0 -17
index 7334cde6..f14034cf 100644
@@ -1,21 +1,4 @@
 FSCK_OPT="-fym4"
-SKIP_VERIFY="true"
 ONE_PASS_ONLY="true"
-SKIP_CLEANUP="true"
 
 . $cmd_dir/run_e2fsck
-
-grep -v Thread $OUT1 > $OUT1.tmp
-cmp -s $EXP1 $OUT1.tmp
-status1=$?
-if [ "$status1" -eq 0 ]; then
-	echo "$test_name: $test_description: ok"
-	touch $test_name.ok
-else
-	echo "$test_name: $test_description: failed"
-	diff $DIFF_OPTS $EXP1 $OUT1.tmp > $test_name.failed
-fi
-
-unset IMAGE FSCK_OPT SECOND_FSCK_OPT OUT1 OUT2 EXP1 EXP2
-unset SKIP_VERIFY SKIP_CLEANUP SKIP_GUNZIP ONE_PASS_ONLY PREP_CMD
-unset DESCRIPTION SKIP_UNLINK AFTER_CMD PASS_ZERO
Signature · 1 line
2.37.3
#97 lore raw

Re: [RFCv1 67/72] sec: support encrypted files handling in pfsck mode

In reply to Ritesh Harjani (IBM)
Quoted 2 lines · Ritesh Harjani (IBM)

sec: support encrypted files handling in pfsck mode

"sec:" => "e2fsck:".

Quoted 22 lines
e2fsck/encrypted_files.c +22 -0
+/**
+ * Search policy matching @policy in @info->policies
+ * @ctx: e2fsck context
+ * @info: encrypted_file_info to look into
+ * @policy: the policy we are looking for
+ * @parent: (out) last known parent, useful to insert a new leaf
+ *	    in @info->policies
+ *
+ * Return: id of found policy on success, -1 if no matching policy found.
+ */
+static inline int search_policy(e2fsck_t ctx, struct encrypted_file_info *info,
+				union fscrypt_policy policy,
+				struct rb_node **parent)
+{
+	struct rb_node *n = info->policies.rb_node;
+	struct policy_map_entry *entry;
+
+	while (n) {
+		int res;
+
+		*parent = n;
+		entry = ext2fs_rb_entry(n, struct policy_map_entry, node);

e2fsck/encrypted_files.c void destroy_encrypted_file_info(e2fsck_t ctx) · line 491

Quoted 10 lines
e2fsck/encrypted_files.c +10 -0
+		res = cmp_fscrypt_policies(ctx, &policy, &entry->policy);
+		if (res < 0)
+			n = n->rb_left;
+		else if (res > 0)
+			n = n->rb_right;
+		else
+			return entry->policy_id;
+	}
+	return -1;
+}

Can this rbtree search code be reused by get_encryption_policy_id()?

Also, please use the existing constant NO_ENCRYPTION_POLICY instead of -1.

e2fsck/encrypted_files.c void destroy_encrypted_file_info(e2fsck_t ctx) · line 504

Quoted 12 lines
e2fsck/encrypted_files.c +12 -0
+/*
+ * Merge @src encrypted info into @dest
+ */
+int e2fsck_merge_encrypted_info(e2fsck_t ctx, struct encrypted_file_info *src,
+				 struct encrypted_file_info *dest)
+{
+	struct rb_root *src_policies = &src->policies;
+	__u32 *policy_trans;
+	int i, rc = 0;
+
+	if (dest->file_ranges[src->file_ranges_count - 1].last_ino >
+	    src->file_ranges[0].first_ino) {

There is an off-by-one error here. How about writing it like the following so that it looks like the check in append_ino_and_policy_id():

        if (src->file_ranges[0].first_ino <=
            dest->file_ranges[src->file_ranges_count - 1].last_ino) {

e2fsck/encrypted_files.c void destroy_encrypted_file_info(e2fsck_t ctx) · line 514

Quoted 1 line
e2fsck/encrypted_files.c +1 -0
+	/* First, deal with the encryption policy => ID map.

My understanding is that e2fsprogs follows the kernel coding style, where block comments are formatted like the following:

	/*
	 * text
	 */

e2fsck/encrypted_files.c void destroy_encrypted_file_info(e2fsck_t ctx) · line 516

Quoted 2 lines
e2fsck/encrypted_files.c +2 -0
+	 * Compare encryption policies in src with policies already recorded
+	 * in dest. It can be similar policies, but recorded with a different

"similar" => "the same"

e2fsck/encrypted_files.c void destroy_encrypted_file_info(e2fsck_t ctx) · line 531

Quoted 10 lines
e2fsck/encrypted_files.c +10 -0
+	while (!ext2fs_rb_empty_root(src_policies)) {
+		struct policy_map_entry *entry, *newentry;
+		struct rb_node *new, *parent = NULL;
+		int existing_polid;
+
+		entry = ext2fs_rb_entry(src_policies->rb_node,
+					struct policy_map_entry, node);
+		existing_polid = search_policy(ctx, dest,
+					       entry->policy, &parent);
+		if (existing_polid >= 0) {

existing_polid != NO_ENCRYPTION_POLICY

e2fsck/encrypted_files.c void destroy_encrypted_file_info(e2fsck_t ctx) · line 543

Quoted 12 lines
e2fsck/encrypted_files.c +12 -0
+			/* The policy in src is already recorded in dest,
+			 * so just update its id.
+			 */
+			policy_trans[entry->policy_id] = existing_polid;
+		} else {
+			/* The policy in src is new to dest, so insert it
+			 * with the next available id (its original id could
+			 * be already used in dest).
+			 */
+			rc = ext2fs_get_mem(sizeof(*newentry), &newentry);
+			if (rc)
+				goto out_merge;

Use handle_nomem() for memory allocation failures?

e2fsck/encrypted_files.c void destroy_encrypted_file_info(e2fsck_t ctx) · line 549

Quoted 6 lines
e2fsck/encrypted_files.c +6 -0
+                     newentry->policy_id = dest->next_policy_id++;
+                     newentry->policy = entry->policy;
+                     ext2fs_rb_link_node(&newentry->node, parent, &new);
+                     ext2fs_rb_insert_color(&newentry->node,
+                                            &dest->policies);
+                     policy_trans[entry->policy_id] = newentry->policy_id;

This code also appears in get_encryption_policy_id(). Should it be refactored into a helper function?

Quoted 20 lines
e2fsck/encrypted_files.c +20 -0
+	/* Second, deal with the inode number => encryption policy ID map. */
+	if (dest->file_ranges_capacity <
+	    dest->file_ranges_count + src->file_ranges_count) {
+		/* dest->file_ranges is too short, increase its capacity. */
+		size_t new_capacity = dest->file_ranges_count +
+			src->file_ranges_count;
+
+		/* Make sure we at least double the capacity. */
+		if (new_capacity < (dest->file_ranges_capacity * 2))
+			new_capacity = dest->file_ranges_capacity * 2;
+
+		/* We won't need more than the filesystem's inode count. */
+		if (new_capacity > ctx->fs->super->s_inodes_count)
+			new_capacity = ctx->fs->super->s_inodes_count;
+
+		rc = ext2fs_resize_mem(dest->file_ranges_capacity *
+				       sizeof(struct encrypted_file_range),
+				       new_capacity *
+				       sizeof(struct encrypted_file_range),
+				       &dest->file_ranges);

e2fsck/encrypted_files.c void destroy_encrypted_file_info(e2fsck_t ctx) · line 584

Quoted 10 lines
e2fsck/encrypted_files.c +10 -0
+		if (rc) {
+			fix_problem(ctx, PR_1_ALLOCATE_ENCRYPTED_INODE_LIST,
+				    NULL);
+			/* Should never get here */
+			ctx->flags |= E2F_FLAG_ABORT;
+			goto out_merge;
+		}
+
+		dest->file_ranges_capacity = new_capacity;
+	}

The exact allocation size that is needed is 'dest->file_ranges_count + src->file_ranges_count', so much of the above logic is unnecessary. Just reallocate that exact amount.

Also, handle_nomem() should be used.

e2fsck/encrypted_files.c void destroy_encrypted_file_info(e2fsck_t ctx) · line 592

Quoted 8 lines
e2fsck/encrypted_files.c +8 -0
+	/* Copy file ranges from src to dest. */
+	for (i = 0; i < src->file_ranges_count; i++) {
+		/* Make sure to convert policy ids in src. */
+		src->file_ranges[i].policy_id =
+			policy_trans[src->file_ranges[i].policy_id];
+		dest->file_ranges[dest->file_ranges_count++] =
+			src->file_ranges[i];
+	}

This needs to handle UNRECOGNIZED_ENCRYPTION_POLICY as a special case.

Also, shouldn't src->file_ranges be freed after this?

Review

Quoted 2 lines
(quoted diff) +2 -0
+
+out_merge:

I guess the "_merge" in "out_merge:" comes from the name of the containing function? It's a bit confusing. Maybe just use "out:".

e2fsck/pass1.c void e2fsck_pass1_run(e2fsck_t ctx) · line 2415

Quoted 10 lines
e2fsck/pass1.c +0 -2
index 7345c96d..e7dc017c 100644
@@ -2411,9 +2411,6 @@ void e2fsck_pass1_run(e2fsck_t ctx)
 		ctx->ea_block_quota_inodes = 0;
 	}
 
-	/* We don't need the encryption policy => ID map any more */
-	destroy_encryption_policy_map(ctx);

With this change, there are no callers of destroy_encryption_policy_map() outside encrypted_files.c. So absent any other changes, destroy_encryption_policy_map() should be made into a static function and the 'if (info)' check inside it removed.

But, isn't there still some point where pass 1 is fully done and the encryption policy ID map can be destroyed? Maybe e2fsck_pass1_merge_encrypted_info() should be calling destroy_encryption_policy_map() on the global_ctx after doing the merge?

- Eric

#3 lore raw

Re: [RFCv1 01/72] e2fsck: Fix unbalanced mutex unlock for BOUNCE_MTX

Quoted 9 lines · Theodore Ts'o
Quoted 6 lines · Ritesh Harjani (IBM)

f_crashdisk test failed with UNIX_IO_FORCE_BOUNCE=yes due to unbalanced mutex unlock in below path.

This patch fixes it.

This has been fixed in upstream (for a while, actually);

Hello Ted,

I think you are speaking about this patch here [1], which prevents the race of using the fd by multiple threads at a time, by using BOUNCE_MTX lock.

The current patch on the other hand fixes the unbalanced mutex_unlock(), which can be reproduced with f_crashdisk test with UNIX_IO_FORCE_BOUNCE=yes. (when tested with --enable-threadsan)

Below is the threadsan warning that I see.

WARNING: ThreadSanitizer: unlock of an unlocked mutex (or by a wrong thread) (pid=135059)

    #0 pthread_mutex_unlock <null> (libtsan.so.0+0x3b64a)
    #1 mutex_unlock ../../../lib/ext2fs/unix_io.c:161 (e2fsck+0x5b392b)
    #2 raw_read_blk ../../../lib/ext2fs/unix_io.c:331 (e2fsck+0x5b392b)
    #3 unix_read_blk64 ../../../lib/ext2fs/unix_io.c:1001 (e2fsck+0x5b4981)
    #4 unix_read_blk ../../../lib/ext2fs/unix_io.c:1053 (e2fsck+0x5b5171)
    #5 ext2fs_open2 ../../../lib/ext2fs/openfs.c:228 (e2fsck+0x579e5c)
    #6 try_open_fs ../../e2fsck/unix.c:1242 (e2fsck+0x424e63)
    #7 main ../../e2fsck/unix.c:1604 (e2fsck+0x41a1d2)

Location is heap block of size 376 at 0x7b4800000000 allocated by main thread:

    #0 malloc <null> (libtsan.so.0+0x32919)
    #1 ext2fs_get_mem ../../../lib/ext2fs/ext2fs.h:1944 (e2fsck+0x5aec30)
    #2 unix_open_channel ../../../lib/ext2fs/unix_io.c:698 (e2fsck+0x5aec30)
    #3 unix_open ../../../lib/ext2fs/unix_io.c:910 (e2fsck+0x5afd67)
    #4 ext2fs_open2 ../../../lib/ext2fs/openfs.c:175 (e2fsck+0x579918)
    #5 try_open_fs ../../e2fsck/unix.c:1242 (e2fsck+0x424e63)
    #6 main ../../e2fsck/unix.c:1604 (e2fsck+0x41a1d2)

Mutex M22 (0x7b4800000128) created at:

    #0 pthread_mutex_init <null> (libtsan.so.0+0x49603)
    #1 unix_open_channel ../../../lib/ext2fs/unix_io.c:827 (e2fsck+0x5af5ef)
    #2 unix_open ../../../lib/ext2fs/unix_io.c:910 (e2fsck+0x5afd67)
    #3 ext2fs_open2 ../../../lib/ext2fs/openfs.c:175 (e2fsck+0x579918)
    #4 try_open_fs ../../e2fsck/unix.c:1242 (e2fsck+0x424e63)
    #5 main ../../e2fsck/unix.c:1604 (e2fsck+0x41a1d2)

SUMMARY: ThreadSanitizer: unlock of an unlocked mutex (or by a wrong thread) (/lib64/libtsan.so.0+0x3b64a) in pthread_mutex_unlock

[1]: https://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git/commit/?id=d557b9659ba97e093f842dcc7e2cfe9a7022c674

Quoted 1 line

what commit is your patches based upon?

1. This series is rebased on the latest e2fsprogs master branch.
https://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git/log/

Cover letter might provide some more details on how the patch series is broken into. The github link of the series can be found at
https://github.com/riteshharjani/e2fsprogs/commits/pfsck-RFCv1

Note: I have provided some details in the cover letter about Patch-073 i.e. ("tests: Add multi-thread tests framework"). But putting it once again here... You can ignore this ^^^ last patch in the github link.
This was added to tests all existing e2fsck test with pfsck mode. But later I realized that it will require more work then how it is in the current form. Since I didn't want to hold the patch series any longer and I wanted this series to go through the initial round of review, before it becomes any bigger and difficult to review, hence I decided to drop the last patch from sending it to mailing list.

Please let me know if you have any queries on any patch/series.

-ritesh

#4 lore raw

Re: [RFCv1 01/72] e2fsck: Fix unbalanced mutex unlock for BOUNCE_MTX

In reply to Ritesh Harjani (IBM)
Quoted 16 lines · Ritesh Harjani (IBM)

f_crashdisk test failed with UNIX_IO_FORCE_BOUNCE=yes due to unbalanced mutex unlock in below path.

This patch fixes it.

---
lib/ext2fs/unix_io.c | 1 -

1 file changed, 1 deletion(-)
lib/ext2fs/unix_io.c +0 -0
index e53db333..5b894826 100644

lib/ext2fs/unix_io.c bounce_read: · line 308

Quoted 4 lines
lib/ext2fs/unix_io.c +0 -1
@@ -305,7 +305,6 @@ bounce_read:
   while (size > 0) {
       actual = read(data->dev, data->bounce, align_size);
       if (actual != align_size) {
-            mutex_unlock(data, BOUNCE_MTX);

This patch doesn't show enough context, but AFAIK this is jumping before mutex_down() is called, so this *should* be correct as is?

Cheers, Andreas

Quoted 6 lines
lib/ext2fs/unix_io.c +0 -0
           actual = really_read;
           buf -= really_read;
           size += really_read;
Signature · 1 line
2.37.3
Reviewed-by on v1 3/72
#13 lore raw

Re: [RFCv1 03/72] blkmap64_ba: Add common helper for bits size calculation

In reply to Ritesh Harjani (IBM)
Quoted 4 lines · Ritesh Harjani (IBM)

Just a quick common helper for bits size calculation.

Quoted 72 lines

---
lib/ext2fs/blkmap64_ba.c | 20 +++++++++++++-------

1 file changed, 13 insertions(+), 7 deletions(-)
lib/ext2fs/blkmap64_ba.c +13 -7
index 5d8f1548..4e7007f0 100644
@@ -40,6 +40,13 @@ struct ext2fs_ba_private_struct {
 
 typedef struct ext2fs_ba_private_struct *ext2fs_ba_private;
 
+#define ba_bits_size(start, end) ((((end) - (start)) / 8 + 1))
+
+static size_t ba_bitmap_size(ext2fs_generic_bitmap_64 bitmap)
+{
+    return (size_t) ba_bits_size(bitmap->start, bitmap->real_end);
+}
+
 static errcode_t ba_alloc_private_data (ext2fs_generic_bitmap_64 bitmap)
 {
  ext2fs_ba_private bp;
@@ -56,7 +63,7 @@ static errcode_t ba_alloc_private_data (ext2fs_generic_bitmap_64 bitmap)
  if (retval)
      return retval;
 
-    size = (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1);
+    size = ba_bitmap_size(bitmap);
 
  retval = ext2fs_get_mem(size, &bp->bitarray);
  if (retval) {
@@ -80,7 +87,7 @@ static errcode_t ba_new_bmap(ext2_filsys fs EXT2FS_ATTR((unused)),
      return retval;
 
  bp = (ext2fs_ba_private) bitmap->private;
-    size = (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1);
+    size = ba_bitmap_size(bitmap);
  memset(bp->bitarray, 0, size);
 
  return 0;
@@ -115,7 +122,7 @@ static errcode_t ba_copy_bmap(ext2fs_generic_bitmap_64 src,
 
  dest_bp = (ext2fs_ba_private) dest->private;
 
-    size = (size_t) (((src->real_end - src->start) / 8) + 1);
+    size = ba_bitmap_size(src);
  memcpy (dest_bp->bitarray, src_bp->bitarray, size);
 
  return 0;
@@ -145,8 +152,8 @@ static errcode_t ba_resize_bmap(ext2fs_generic_bitmap_64 bmap,
      return 0;
  }
 
-    size = ((bmap->real_end - bmap->start) / 8) + 1;
-    new_size = ((new_real_end - bmap->start) / 8) + 1;
+    size = ba_bitmap_size(bmap);
+    new_size = ba_bits_size(new_real_end, bmap->start);
 
  if (size != new_size) {
      retval = ext2fs_resize_mem(size, new_size, &bp->bitarray);
@@ -306,8 +313,7 @@ static void ba_clear_bmap(ext2fs_generic_bitmap_64 bitmap)
 {
  ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private;
 
-    memset(bp->bitarray, 0,
-           (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1));
+    memset(bp->bitarray, 0, ba_bitmap_size(bitmap));
 }
 
 #ifdef ENABLE_BMAP_STATS
Signature · 1 line
2.37.3
#5 lore raw

Re: [RFCv1 01/72] e2fsck: Fix unbalanced mutex unlock for BOUNCE_MTX

In reply to Andreas Dilger
Quoted 12 lines · Andreas Dilger
Quoted 11 lines · Ritesh Harjani (IBM)

f_crashdisk test failed with UNIX_IO_FORCE_BOUNCE=yes due to unbalanced mutex unlock in below path.

This patch fixes it.

---
lib/ext2fs/unix_io.c | 1 -

1 file changed, 1 deletion(-)
Quoted 12 lines

lib/ext2fs/unix_io.c bounce_read: · line 308

Quoted 9 lines
lib/ext2fs/unix_io.c +0 -1
index e53db333..5b894826 100644
@@ -305,7 +305,6 @@ bounce_read:
   while (size > 0) {
       actual = read(data->dev, data->bounce, align_size);
       if (actual != align_size) {
-            mutex_unlock(data, BOUNCE_MTX);

This patch doesn't show enough context, but AFAIK this is jumping before mutex_down() is called, so this *should* be correct as is?

Thanks for the review, Andreas.

Yeah, the patch diff above is not sufficient since it doesn't share enuf context.
But essentially when "actual" is not equal to "align_size", then in this if condition it goes to label "short_read:", which always goto error_unlock, where we anyways call mutex_unlock()

Looking at a lot of labels in this function, this definitely looks like something which can be cleaned up ("raw_read_blk()").
I will add that to my list of todos.

I have also shared the threadsan warning which detects the unbalanced mutex unlock here [1]

[1]: https://lore.kernel.org/linux-ext4/[email protected]/T/#md75b3ccb146e4433653bc2d7dd01329a9757ea26

-ritesh

#6 lore raw

Re: [RFCv1 01/72] e2fsck: Fix unbalanced mutex unlock for BOUNCE_MTX

Quoted 35 lines · Ritesh Harjani (IBM)
Quoted 12 lines · Andreas Dilger
Quoted 12 lines · Ritesh Harjani (IBM)

f_crashdisk test failed with UNIX_IO_FORCE_BOUNCE=yes due to unbalanced mutex unlock in below path.

This patch fixes it.

---
lib/ext2fs/unix_io.c | 1 -

1 file changed, 1 deletion(-)
Quoted 12 lines

lib/ext2fs/unix_io.c bounce_read: · line 308

Quoted 9 lines
lib/ext2fs/unix_io.c +0 -1
index e53db333..5b894826 100644
@@ -305,7 +305,6 @@ bounce_read:
  while (size > 0) {
      actual = read(data->dev, data->bounce, align_size);
      if (actual != align_size) {
-            mutex_unlock(data, BOUNCE_MTX);

This patch doesn't show enough context, but AFAIK this is jumping before mutex_down() is called, so this *should* be correct as is?

Thanks for the review, Andreas.

Yeah, the patch diff above is not sufficient since it doesn't share enuf context.
But essentially when "actual" is not equal to "align_size", then in this if condition it goes to label "short_read:", which always goto error_unlock, where we anyways call mutex_unlock()

Quoted 3 lines

Looking at a lot of labels in this function, this definitely looks like something which can be cleaned up ("raw_read_blk()").
I will add that to my list of todos.

You are correct, and it means this code is just not very clear to the reader. I think it would make more sense to move the "short_read:" label to the end of the code:

                  actual = read(...);
                  if (actual != size)
                          goto error_short_read;
                  goto success_unlock;
        :
                 actual = read(...);
                 if (actual != align_size) {
                           actual = really_read;
                           buf -= really_read;
                           size += really_read;
                           goto error_short_read;
                 }
        :
success_unlock:
        mutex_unlock(...);
        return 0;

error_short_read:

        if (actual < 0) {
                 retval = errno;
                 actual = 0;
        } else {
                 retval = EXT2_ET_SHORT_READ;
        }
error_unlock:
        mutex_unlock(...);

That way the code follows the normal error handling convention and is less likely to be surprising to the reader.

Cheers, Andreas

Reviewed-by on v1 4/72
#15 lore raw

Re: [RFCv1 04/72] badblocks: Remove unused badblocks_flags

In reply to Ritesh Harjani (IBM)
Quoted 4 lines · Ritesh Harjani (IBM)

badblocks_flags is not used anywhere. So just remove it.

Quoted 37 lines

---
lib/ext2fs/badblocks.c | 6 +-----
lib/ext2fs/ext2fsP.h | 1 -

2 files changed, 1 insertion(+), 6 deletions(-)
lib/ext2fs/badblocks.c +1 -5
index a306bc06..345168e0 100644
@@ -81,11 +81,7 @@ errcode_t ext2fs_u32_copy(ext2_u32_list src, ext2_u32_list *dest)
 {
   errcode_t    retval;
 
-    retval = make_u32_list(src->size, src->num, src->list, dest);
-    if (retval)
-        return retval;
-    (*dest)->badblocks_flags = src->badblocks_flags;
-    return 0;
+    return make_u32_list(src->size, src->num, src->list, dest);
 }
 
 errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src,
lib/ext2fs/ext2fsP.h +0 -1
index a20a0502..d2045af8 100644
@@ -34,7 +34,6 @@ struct ext2_struct_u32_list {
   int    num;
   int    size;
   __u32    *list;
-    int    badblocks_flags;
 };
 
 struct ext2_struct_u32_iterate {
Signature · 1 line
2.37.3
Reviewed-by on v1 5/72
#17 lore raw

Re: [RFCv1 05/72] badblocks: Add badblocks merge logic

In reply to Ritesh Harjani (IBM)
Quoted 7 lines · Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

Add badblocks merge logic

Quoted 114 lines

---
lib/ext2fs/badblocks.c | 75 ++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2fs.h | 2 ++

2 files changed, 77 insertions(+)
lib/ext2fs/badblocks.c +75 -0
index 345168e0..36794e69 100644
@@ -56,6 +56,74 @@ static errcode_t make_u32_list(int size, int num, __u32 *list,
   return 0;
 }
 
+static inline int insert_ok(blk_t *array, int cnt, blk_t new)
+{
+   return (cnt == 0 || array[cnt - 1] != new);
+}
+
+/*
+ * Merge list from src to dest
+ */
+static errcode_t merge_u32_list(ext2_u32_list src, ext2_u32_list dest)
+{
+   errcode_t    retval;
+   int      src_count = src->num;
+   int      dest_count = dest->num;
+   int      size = src_count + dest_count;
+   int      size_entry = sizeof(blk_t);
+   blk_t       *array;
+   blk_t       *src_array = src->list;
+   blk_t       *dest_array = dest->list;
+   int      src_index = 0;
+   int      dest_index = 0;
+   int      uniq_cnt = 0;
+
+   if (src->num == 0)
+       return 0;
+
+   retval = ext2fs_get_array(size, size_entry, &array);
+   if (retval)
+       return retval;
+
+   /*
+    * It is possible that src list and dest list could be
+    * duplicated when merging badblocks.
+    */
+   while (src_index < src_count || dest_index < dest_count) {
+       if (src_index >= src_count) {
+           for (; dest_index < dest_count; dest_index++)
+               if (insert_ok(array, uniq_cnt, dest_array[dest_index]))
+                   array[uniq_cnt++] = dest_array[dest_index];
+           break;
+       }
+       if (dest_index >= dest_count) {
+           for (; src_index < src_count; src_index++)
+               if (insert_ok(array, uniq_cnt, src_array[src_index]))
+                   array[uniq_cnt++] = src_array[src_index];
+           break;
+       }
+       if (src_array[src_index] < dest_array[dest_index]) {
+           if (insert_ok(array, uniq_cnt, src_array[src_index]))
+               array[uniq_cnt++] = src_array[src_index];
+           src_index++;
+       } else if (src_array[src_index] > dest_array[dest_index]) {
+           if (insert_ok(array, uniq_cnt, dest_array[dest_index]))
+               array[uniq_cnt++] = dest_array[dest_index];
+           dest_index++;
+       } else {
+           if (insert_ok(array, uniq_cnt, dest_array[dest_index]))
+               array[uniq_cnt++] = dest_array[dest_index];
+           src_index++;
+           dest_index++;
+       }
+   }
+
+   ext2fs_free_mem(&dest->list);
+   dest->list = array;
+   dest->num = uniq_cnt;
+   dest->size = size;
+   return 0;
+}
 
 /*
 * This procedure creates an empty u32 list.
@@ -91,6 +159,13 @@ errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src,
                  (ext2_u32_list *) dest);
 }
 
+errcode_t ext2fs_badblocks_merge(ext2_badblocks_list src,
+                ext2_badblocks_list dest)
+{
+   return merge_u32_list((ext2_u32_list) src,
+                 (ext2_u32_list) dest);
+}
+
 /*
 * This procedure frees a badblocks list.
 *
lib/ext2fs/ext2fs.h +2 -0
index 9cc994b1..18dddc2c 100644
@@ -845,6 +845,8 @@ extern int ext2fs_badblocks_list_iterate(ext2_badblocks_iterate iter,
 extern void ext2fs_badblocks_list_iterate_end(ext2_badblocks_iterate iter);
 extern errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src,
                      ext2_badblocks_list *dest);
+extern errcode_t ext2fs_badblocks_merge(ext2_badblocks_list src,
+                   ext2_badblocks_list dest);
 extern int ext2fs_badblocks_equal(ext2_badblocks_list bb1,
                 ext2_badblocks_list bb2);
 extern int ext2fs_u32_list_count(ext2_u32_list bb);
Signature · 1 line
2.37.3
Reviewed-by on v1 6/72
#19 lore raw

Re: [RFCv1 06/72] dblist: add dblist merge logic

In reply to Ritesh Harjani (IBM)
Quoted 10 lines · Ritesh Harjani (IBM)

From: Li Xi <[email protected]>

This adds dblist merge logic.

TODO: Add a unit test for core operations of dblist. Currently there is no such test for this.

Quoted 67 lines

---
lib/ext2fs/dblist.c | 36 ++++++++++++++++++++++++++++++++++++ lib/ext2fs/ext2fs.h | 1 +

2 files changed, 37 insertions(+)
lib/ext2fs/dblist.c +36 -0
index bbdb221d..5568b8ec 100644
@@ -119,6 +119,42 @@ errcode_t ext2fs_copy_dblist(ext2_dblist src, ext2_dblist *dest)
   return 0;
 }
 
+/*
+ * Merge a directory block list @src to @dest
+ */
+errcode_t ext2fs_merge_dblist(ext2_dblist src, ext2_dblist dest)
+{
+   unsigned long long src_count = src->count;
+   unsigned long long dest_count = dest->count;
+   unsigned long long size = src_count + dest_count;
+   size_t size_entry = sizeof(struct ext2_db_entry2);
+   struct ext2_db_entry2 *array, *array2;
+   errcode_t retval;
+
+   if (src_count == 0)
+       return 0;
+
+   if (src->sorted || (dest->sorted && dest_count != 0))
+       return EINVAL;
+
+   retval = ext2fs_get_array(size, size_entry, &array);
+   if (retval)
+       return retval;
+
+   array2 = array;
+   memcpy(array, src->list, src_count * size_entry);
+   array += src_count;
+   memcpy(array, dest->list, dest_count * size_entry);
+   ext2fs_free_mem(&dest->list);
+
+   dest->list = array2;
+   dest->count = src_count + dest_count;
+   dest->size = size;
+   dest->sorted = 0;
+
+   return 0;
+}
+
 /*
 * Close a directory block list
 *
lib/ext2fs/ext2fs.h +1 -0
index 18dddc2c..443f93d2 100644
@@ -1143,6 +1143,7 @@ extern errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ext2_ino_t ino,
                     blk_t blk, int blockcnt);
 extern errcode_t ext2fs_add_dir_block2(ext2_dblist dblist, ext2_ino_t ino,
                      blk64_t blk, e2_blkcnt_t blockcnt);
+extern errcode_t ext2fs_merge_dblist(ext2_dblist src, ext2_dblist dest);
 extern void ext2fs_dblist_sort(ext2_dblist dblist,
                  EXT2_QSORT_TYPE (*sortfunc)(const void *,
                              const void *));
Signature · 1 line
2.37.3
Reviewed-by on v1 8/72
#22 lore raw

Re: [RFCv1 08/72] libext2fs: Add bitmaps merge ops

In reply to Ritesh Harjani (IBM)
Quoted 8 lines · Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

[Note: splitted rb merge tree logic patch such that we seperate out libext2fs changes from e2fsck specific changes]

Quoted 112 lines

---

lib/ext2fs/bitmaps.c      | 10 ++++++++++
lib/ext2fs/bmap64.h       |  4 ++++
lib/ext2fs/ext2fs.h       |  8 ++++++++
lib/ext2fs/gen_bitmap64.c | 29 +++++++++++++++++++++++++++++
4 files changed, 51 insertions(+)
lib/ext2fs/bitmaps.c +10 -0
index 8bfa24b1..9437331e 100644
@@ -45,6 +45,16 @@ errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src,
 {
   return (ext2fs_copy_generic_bmap(src, dest));
 }
+
+errcode_t ext2fs_merge_bitmap(ext2fs_generic_bitmap src,
+                  ext2fs_generic_bitmap dest,
+                  ext2fs_generic_bitmap dup,
+                  ext2fs_generic_bitmap dup_allowed)
+{
+    return ext2fs_merge_generic_bmap(src, dest, dup,
+                     dup_allowed);
+}
+
 void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map)
 {
   ext2fs_set_generic_bmap_padding(map);
lib/ext2fs/bmap64.h +4 -0
index de334548..555193ee 100644
@@ -72,6 +72,10 @@ struct ext2_bitmap_ops {
   void    (*free_bmap)(ext2fs_generic_bitmap_64 bitmap);
   errcode_t (*copy_bmap)(ext2fs_generic_bitmap_64 src,
                ext2fs_generic_bitmap_64 dest);
+    errcode_t (*merge_bmap)(ext2fs_generic_bitmap_64 src,
+                ext2fs_generic_bitmap_64 dest,
+                ext2fs_generic_bitmap_64 dup,
+                ext2fs_generic_bitmap_64 dup_allowed);
   errcode_t (*resize_bmap)(ext2fs_generic_bitmap_64 bitmap,
                  __u64 new_end,
                  __u64 new_real_end);
lib/ext2fs/ext2fs.h +8 -0
index 443f93d2..54aed5d1 100644
@@ -870,6 +870,10 @@ extern void ext2fs_free_block_bitmap(ext2fs_block_bitmap bitmap);
 extern void ext2fs_free_inode_bitmap(ext2fs_inode_bitmap bitmap);
 extern errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src,
                   ext2fs_generic_bitmap *dest);
+extern errcode_t ext2fs_merge_bitmap(ext2fs_generic_bitmap src,
+                  ext2fs_generic_bitmap dest,
+                  ext2fs_generic_bitmap dup,
+                  ext2fs_generic_bitmap dup_allowed);
 extern errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs,
                         const char *descr,
                         ext2fs_block_bitmap *ret);
@@ -1467,6 +1471,10 @@ void ext2fs_set_generic_bmap_padding(ext2fs_generic_bitmap bmap);
 errcode_t ext2fs_resize_generic_bmap(ext2fs_generic_bitmap bmap,
                    __u64 new_end,
                    __u64 new_real_end);
+errcode_t ext2fs_merge_generic_bmap(ext2fs_generic_bitmap gen_src,
+                                    ext2fs_generic_bitmap gen_dest,
+                    ext2fs_generic_bitmap gen_dup,
+                    ext2fs_generic_bitmap dup_allowed);
 errcode_t ext2fs_compare_generic_bmap(errcode_t neq,
                     ext2fs_generic_bitmap bm1,
                     ext2fs_generic_bitmap bm2);
lib/ext2fs/gen_bitmap64.c +29 -0
index f7710afd..c31f942f 100644
@@ -346,6 +346,35 @@ errcode_t ext2fs_copy_generic_bmap(ext2fs_generic_bitmap gen_src,
   return 0;
 }
 
+errcode_t ext2fs_merge_generic_bmap(ext2fs_generic_bitmap gen_src,
+                    ext2fs_generic_bitmap gen_dest,
+                    ext2fs_generic_bitmap gen_dup,
+                    ext2fs_generic_bitmap gen_dup_allowed)
+{
+    ext2fs_generic_bitmap_64 src = (ext2fs_generic_bitmap_64)gen_src;
+    ext2fs_generic_bitmap_64 dest = (ext2fs_generic_bitmap_64)gen_dest;
+    ext2fs_generic_bitmap_64 dup = (ext2fs_generic_bitmap_64)gen_dup;
+    ext2fs_generic_bitmap_64 dup_allowed = (ext2fs_generic_bitmap_64)gen_dup_allowed;
+
+    if (!src || !dest)
+        return EINVAL;
+
+    if (!EXT2FS_IS_64_BITMAP(src) || !EXT2FS_IS_64_BITMAP(dest) ||
+        (dup && !EXT2FS_IS_64_BITMAP(dup)) ||
+        (dup_allowed && !EXT2FS_IS_64_BITMAP(dup_allowed)))
+        return EINVAL;
+
+    if (src->bitmap_ops != dest->bitmap_ops ||
+        (dup && src->bitmap_ops != dup->bitmap_ops) ||
+        (dup_allowed && src->bitmap_ops != dup_allowed->bitmap_ops))
+        return EINVAL;
+
+    if (src->bitmap_ops->merge_bmap == NULL)
+        return EOPNOTSUPP;
+
+    return src->bitmap_ops->merge_bmap(src, dest, dup, dup_allowed);
+}
+
 errcode_t ext2fs_resize_generic_bmap(ext2fs_generic_bitmap gen_bmap,
                    __u64 new_end,
                    __u64 new_real_end)
Signature · 1 line
2.37.3
Reviewed-by on v1 20/72
#48 lore raw

Re: [RFCv1 20/72] libext2fs: avoid too much memory allocation in case fs_num_threads

In reply to Ritesh Harjani (IBM)
Quoted 7 lines · Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

e2fsck init memory according to filesystem inodes/dir numbers recorded in the superblock, this should be aware of filesystem number of threads, otherwise, oom can happen.

Quoted 5 lines

So in case of fs->fs_num_threads, this patch controls the amount of memory consumed for running multiple threads in e2fsck.

Quoted 43 lines

---
lib/ext2fs/dblist.c | 2 ++
lib/ext2fs/icount.c | 4 ++++

2 files changed, 6 insertions(+)
lib/ext2fs/dblist.c +2 -0
index 5568b8ec..c19e17bc 100644
@@ -58,6 +58,8 @@ static errcode_t make_dblist(ext2_filsys fs, ext2_ino_t size,
       if (retval)
           goto cleanup;
       dblist->size = (num_dirs * 2) + 12;
+        if (fs->fs_num_threads)
+            dblist->size /= fs->fs_num_threads;
   }
   len = (size_t) sizeof(struct ext2_db_entry2) * dblist->size;
   dblist->count = count;
lib/ext2fs/icount.c +4 -0
index 766eccca..48665c7e 100644
@@ -237,6 +237,8 @@ errcode_t ext2fs_create_icount_tdb(ext2_filsys fs EXT2FS_NO_TDB_UNUSED,
    * value.
    */
   num_inodes = fs->super->s_inodes_count - fs->super->s_free_inodes_count;
+    if (fs->fs_num_threads)
+        num_inodes /= fs->fs_num_threads;
 
   icount->tdb = tdb_open(fn, num_inodes, TDB_NOLOCK | TDB_NOSYNC,
                  O_RDWR | O_CREAT | O_TRUNC, 0600);
@@ -288,6 +290,8 @@ errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, unsigned int size,
       if (retval)
           goto errout;
       icount->size += fs->super->s_inodes_count / 50;
+        if (fs->fs_num_threads)
+            icount->size /= fs->fs_num_threads;
   }
 
   bytes = (size_t) (icount->size * sizeof(struct ext2_icount_el));
Signature · 1 line
2.37.3
Reviewed-by on v1 9/72
#24 lore raw

Re: [RFCv1 09/72] libext2fs: Add flush cleanup API

In reply to Ritesh Harjani (IBM)
Quoted 6 lines · Ritesh Harjani (IBM)

From: Li Xi <[email protected]>

Quoted 130 lines

---

lib/ext2fs/ext2_io.h |  2 ++
lib/ext2fs/undo_io.c | 19 +++++++++++++++++++
lib/ext2fs/unix_io.c | 24 +++++++++++++++++++++---
3 files changed, 42 insertions(+), 3 deletions(-)
lib/ext2fs/ext2_io.h +2 -0
index 8fe5b323..8cc355be 100644
@@ -82,6 +82,7 @@ struct struct_io_manager {
   errcode_t (*write_blk)(io_channel channel, unsigned long block,
                  int count, const void *data);
   errcode_t (*flush)(io_channel channel);
+    errcode_t (*flush_cleanup)(io_channel channel);
   errcode_t (*write_byte)(io_channel channel, unsigned long offset,
               int count, const void *data);
   errcode_t (*set_option)(io_channel channel, const char *option,
@@ -116,6 +117,7 @@ struct struct_io_manager {
 #define io_channel_read_blk(c,b,n,d)    ((c)->manager->read_blk((c),b,n,d))
 #define io_channel_write_blk(c,b,n,d)    ((c)->manager->write_blk((c),b,n,d))
 #define io_channel_flush(c)        ((c)->manager->flush((c)))
+#define io_channel_flush_cleanup(c)    ((c)->manager->flush_cleanup((c)))
 #define io_channel_bumpcount(c)        ((c)->refcount++)
 
 /* io_manager.c */
lib/ext2fs/undo_io.c +19 -0
index f4a6d526..678ff421 100644
@@ -1024,6 +1024,24 @@ static errcode_t undo_flush(io_channel channel)
   return retval;
 }
 
+/*
+ * Flush data buffers to disk and cleanup the cache.
+ */
+static errcode_t undo_flush_cleanup(io_channel channel)
+{
+    errcode_t    retval = 0;
+    struct undo_private_data *data;
+
+    EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+    data = (struct undo_private_data *) channel->private_data;
+    EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+    if (data->real)
+        retval = io_channel_flush_cleanup(data->real);
+
+    return retval;
+}
+
 static errcode_t undo_set_option(io_channel channel, const char *option,
                const char *arg)
 {
@@ -1095,6 +1113,7 @@ static struct struct_io_manager struct_undo_manager = {
   .read_blk    = undo_read_blk,
   .write_blk    = undo_write_blk,
   .flush        = undo_flush,
+    .flush_cleanup    = undo_flush_cleanup,
   .write_byte    = undo_write_byte,
   .set_option    = undo_set_option,
   .get_stats    = undo_get_stats,
lib/ext2fs/unix_io.c +21 -3
index 5b894826..8f8118a3 100644
@@ -1173,9 +1173,9 @@ static errcode_t unix_write_byte(io_channel channel, unsigned long offset,
 }
 
 /*
- * Flush data buffers to disk.
+ * Flush data buffers to disk and invalidate cache if needed
 */
-static errcode_t unix_flush(io_channel channel)
+static errcode_t _unix_flush(io_channel channel, int invalidate)
 {
   struct unix_private_data *data;
   errcode_t retval = 0;
@@ -1185,7 +1185,7 @@ static errcode_t unix_flush(io_channel channel)
   EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
 
 #ifndef NO_IO_CACHE
-    retval = flush_cached_blocks(channel, data, 0);
+    retval = flush_cached_blocks(channel, data, invalidate);
 #endif
 #ifdef HAVE_FSYNC
   if (!retval && fsync(data->dev) != 0)
@@ -1194,6 +1194,22 @@ static errcode_t unix_flush(io_channel channel)
   return retval;
 }
 
+/*
+ * Flush data buffers to disk.
+ */
+static errcode_t unix_flush(io_channel channel)
+{
+    return _unix_flush(channel, 0);
+}
+
+/*
+ * Flush data buffers to disk and invalidate cache.
+ */
+static errcode_t unix_flush_cleanup(io_channel channel)
+{
+    return _unix_flush(channel, 1);
+}
+
 static errcode_t unix_set_option(io_channel channel, const char *option,
                const char *arg)
 {
@@ -1383,6 +1399,7 @@ static struct struct_io_manager struct_unix_manager = {
   .discard    = unix_discard,
   .cache_readahead    = unix_cache_readahead,
   .zeroout    = unix_zeroout,
+    .flush_cleanup    = unix_flush_cleanup,
 };
 
 io_manager unix_io_manager = &struct_unix_manager;
@@ -1404,6 +1421,7 @@ static struct struct_io_manager struct_unixfd_manager = {
   .discard    = unix_discard,
   .cache_readahead    = unix_cache_readahead,
   .zeroout    = unix_zeroout,
+    .flush_cleanup    = unix_flush_cleanup,
 };
 
 io_manager unixfd_io_manager = &struct_unixfd_manager;
Signature · 1 line
2.37.3
Reviewed-by on v1 10/72
#26 lore raw

Re: [RFCv1 10/72] libext2fs: merge icounts after thread finishes

In reply to Ritesh Harjani (IBM)
Quoted 10 lines · Ritesh Harjani (IBM)

From: Li Xi <[email protected]>

Merge inode_count and inode_link_info properly after threads finish.

[Note: splitted the patch to seperate libext2fs changes from e2fsck]
Quoted 141 lines

---
lib/ext2fs/ext2fs.h | 1 +
lib/ext2fs/icount.c | 103 ++++++++++++++++++++++++++++++++++++++++++++

2 files changed, 104 insertions(+)
lib/ext2fs/ext2fs.h +1 -0
index 54aed5d1..139a25fc 100644
@@ -1546,6 +1546,7 @@ extern errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino,
                    __u16 *ret);
 extern errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino,
                    __u16 count);
+extern errcode_t ext2fs_icount_merge(ext2_icount_t src, ext2_icount_t dest);
 extern ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount);
 errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *);
 
lib/ext2fs/icount.c +103 -0
index 888a90b2..766eccca 100644
@@ -13,6 +13,7 @@
 #if HAVE_UNISTD_H
 #include <unistd.h>
 #endif
+#include <assert.h>
 #include <string.h>
 #include <stdio.h>
 #include <sys/stat.h>
@@ -701,6 +702,108 @@ errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino,
   return 0;
 }
 
+errcode_t ext2fs_icount_merge_full_map(ext2_icount_t src, ext2_icount_t dest)
+{
+    /* TODO: add the support for full map */
+    return EOPNOTSUPP;
+}
+
+errcode_t ext2fs_icount_merge_el(ext2_icount_t src, ext2_icount_t dest)
+{
+    int             src_count = src->count;
+    int             dest_count = dest->count;
+    int             size = src_count + dest_count;
+    int             size_entry = sizeof(struct ext2_icount_el);
+    struct ext2_icount_el    *array;
+    struct ext2_icount_el    *array_ptr;
+    struct ext2_icount_el    *src_array = src->list;
+    struct ext2_icount_el    *dest_array = dest->list;
+    int             src_index = 0;
+    int             dest_index = 0;
+    errcode_t         retval;
+
+    if (src_count == 0)
+        return 0;
+
+    retval = ext2fs_get_array(size, size_entry, &array);
+    if (retval)
+        return retval;
+
+    array_ptr = array;
+    /*
+     * This can be improved by binary search and memcpy, but codes
+     * would be more complex. And if number of bad blocks is small,
+     * the optimization won't improve performance a lot.
+     */
+    while (src_index < src_count || dest_index < dest_count) {
+        if (src_index >= src_count) {
+            memcpy(array_ptr, &dest_array[dest_index],
+                   (dest_count - dest_index) * size_entry);
+            break;
+        }
+        if (dest_index >= dest_count) {
+            memcpy(array_ptr, &src_array[src_index],
+                   (src_count - src_index) * size_entry);
+            break;
+        }
+        if (src_array[src_index].ino < dest_array[dest_index].ino) {
+            *array_ptr = src_array[src_index];
+            src_index++;
+        } else {
+            assert(src_array[src_index].ino >
+                   dest_array[dest_index].ino);
+            *array_ptr = dest_array[dest_index];
+            dest_index++;
+        }
+        array_ptr++;
+    }
+
+    ext2fs_free_mem(&dest->list);
+    dest->list = array;
+    dest->count = src_count + dest_count;
+    dest->size = size;
+    dest->last_lookup = NULL;
+    return 0;
+}
+
+errcode_t ext2fs_icount_merge(ext2_icount_t src, ext2_icount_t dest)
+{
+    errcode_t    retval;
+
+    if (src->fullmap && !dest->fullmap)
+        return EINVAL;
+
+    if (!src->fullmap && dest->fullmap)
+        return EINVAL;
+
+    if (src->multiple && !dest->multiple)
+        return EINVAL;
+
+    if (!src->multiple && dest->multiple)
+        return EINVAL;
+
+    if (src->fullmap)
+        return ext2fs_icount_merge_full_map(src, dest);
+
+    retval = ext2fs_merge_bitmap(src->single, dest->single, NULL,
+                     NULL);
+    if (retval)
+        return retval;
+
+    if (src->multiple) {
+        retval = ext2fs_merge_bitmap(src->multiple, dest->multiple,
+                         NULL, NULL);
+        if (retval)
+            return retval;
+    }
+
+    retval = ext2fs_icount_merge_el(src, dest);
+    if (retval)
+        return retval;
+
+    return 0;
+}
+
 ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount)
 {
   if (!icount || icount->magic != EXT2_ET_MAGIC_ICOUNT)
Signature · 1 line
2.37.3
Reviewed-by on v1 11/72
#28 lore raw

Re: [RFCv1 11/72] libext2fs: merge quota context after threads finish

In reply to Ritesh Harjani (IBM)
Quoted 9 lines · Ritesh Harjani (IBM)

From: Wang Shilong <[email protected]>

Every threads calculate its own quota accounting,
merge them after threads finish.

[Note: splitted the patch to seperate libext2fs changes from e2fsck]
Quoted 79 lines

---
lib/support/mkquota.c | 39 +++++++++++++++++++++++++++++++++++++++ lib/support/quotaio.h | 3 +++

2 files changed, 42 insertions(+)
lib/support/mkquota.c +39 -0
index 9339c994..ce1ab4cd 100644
@@ -618,6 +618,45 @@ out:
   return err;
 }
 
+static errcode_t merge_usage(dict_t *dest, dict_t *src)
+{
+    dnode_t *n;
+    struct dquot *src_dq, *dest_dq;
+
+    for (n = dict_first(src); n; n = dict_next(src, n)) {
+        src_dq = dnode_get(n);
+        if (!src_dq)
+            continue;
+        dest_dq = get_dq(dest, src_dq->dq_id);
+        if (dest_dq == NULL)
+            return -ENOMEM;
+        dest_dq->dq_dqb.dqb_curspace += src_dq->dq_dqb.dqb_curspace;
+        dest_dq->dq_dqb.dqb_curinodes += src_dq->dq_dqb.dqb_curinodes;
+    }
+
+    return 0;
+}
+
+
+errcode_t quota_merge_and_update_usage(quota_ctx_t dest_qctx,
+                    quota_ctx_t src_qctx)
+{
+    dict_t *dict;
+    enum quota_type    qtype;
+    errcode_t retval = 0;
+
+    for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+        dict = src_qctx->quota_dict[qtype];
+        if (!dict)
+            continue;
+        retval = merge_usage(dest_qctx->quota_dict[qtype], dict);
+        if (retval)
+            break;
+    }
+
+    return retval;
+}
+
 /*
 * Compares the measured quota in qctx->quota_dict with that in the quota inode
 * on disk and updates the limits in qctx->quota_dict. 'usage_inconsistent' is
lib/support/quotaio.h +1 -0
index 84fac35d..240a0762 100644
@@ -40,6 +40,7 @@
 #include "ext2fs/ext2_fs.h"
 #include "ext2fs/ext2fs.h"
 #include "dqblk_v2.h"
+#include "support/dict.h"
 

typedef int64_t qsize_t; /* Type in which we store size limitations */

lib/support/quotaio.h +2 -0
@@ -236,6 +237,8 @@ int quota_file_exists(ext2_filsys fs, enum quota_type qtype);
 void quota_set_sb_inum(ext2_filsys fs, ext2_ino_t ino, enum quota_type qtype);
 errcode_t quota_compare_and_update(quota_ctx_t qctx, enum quota_type qtype,
                  int *usage_inconsistent);
+errcode_t quota_merge_and_update_usage(quota_ctx_t dest_qctx,
+                    quota_ctx_t src_qctx);
 int parse_quota_opts(const char *opts, int (*func)(char *));
 
 /* parse_qtype.c */
Signature · 1 line
2.37.3
#30 lore raw

Re: [RFCv1 12/72] libext2fs: dupfs: Add fs clone & merge api

In reply to Ritesh Harjani (IBM)
Quoted 51 lines · Ritesh Harjani (IBM)

From: Saranya Muruganandam <[email protected]>

This patch mainly adds "parent" & "clone_flags" member in ext2_filsys struct for enabling multi-threading. Based on what CLONE flags will be passed from the client of libext2fs down to ext2fs_clone_fs(), those structures/bitmaps will be cloned (thread-aware child copy) and rest will be shared with the parent fs.

The same flags will also help to merge those cloned bitmap structures back into the parent bitmaps when ext2fs_merge_fs() will be called with childfs struct.

---
lib/ext2fs/dupfs.c | 183 ++++++++++++++++++++++++++++++++++++++++++++ lib/ext2fs/ext2fs.h | 23 ++++++

2 files changed, 206 insertions(+)
lib/ext2fs/dupfs.c +16 -0
index 02721e1a..ecc57cf7 100644
@@ -14,8 +14,12 @@
 #if HAVE_UNISTD_H
 #include <unistd.h>
 #endif
+#if HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
 #include <time.h>
 #include <string.h>
+#include <assert.h>
 
 #include "ext2_fs.h"
 #include "ext2fsP.h"
@@ -120,3 +124,182 @@ errout:
 
 }
 
+#ifdef HAVE_PTHREAD
+errcode_t ext2fs_clone_fs(ext2_filsys fs, ext2_filsys *dest, unsigned int flags)
+{
+    errcode_t retval;
+    ext2_filsys childfs;
+
+    EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+    retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &childfs);
+    if (retval)
+        return retval;
+

lib/ext2fs/dupfs.c errout: · line 148

Quoted 10 lines
lib/ext2fs/dupfs.c +10 -0
+    /* make an exact copy implying lists and memory structures are shared */
+    memcpy(childfs, fs, sizeof(struct struct_ext2_filsys));
+    childfs->inode_map = NULL;
+    childfs->block_map = NULL;
+    childfs->badblocks = NULL;
+    childfs->dblist = NULL;
+
+    pthread_mutex_lock(&fs->refcount_mutex);
+    fs->refcount++;
+    pthread_mutex_unlock(&fs->refcount_mutex);

The locking her doesn't make sense. Why is the mutex only protecting the "refcount", and not the rest of the fields in fs?

It would make more sense to hold the mutex over the whole copy process, from up before the memcpy(), until down after the last structure is cloned. Otherwise, once fs has been cloned once and given to another thread it would be possible for them to change these structures.

Quoted 19 lines
lib/ext2fs/dupfs.c +19 -0
+    if ((flags & EXT2FS_CLONE_INODE) && fs->inode_map) {
+        retval = ext2fs_copy_bitmap(fs->inode_map, &childfs->inode_map);
+        if (retval)
+            return retval;
+        childfs->inode_map->fs = childfs;
+    }
+
+    if ((flags & EXT2FS_CLONE_BLOCK) && fs->block_map) {
+        retval = ext2fs_copy_bitmap(fs->block_map, &childfs->block_map);
+        if (retval)
+            return retval;
+        childfs->block_map->fs = childfs;
+    }
+
+    if ((flags & EXT2FS_CLONE_BADBLOCKS) && fs->badblocks) {
+        retval = ext2fs_badblocks_copy(fs->badblocks, &childfs->badblocks);
+        if (retval)
+            return retval;
+    }

lib/ext2fs/dupfs.c errout: · line 178

Quoted 10 lines
lib/ext2fs/dupfs.c +10 -0
+
+    if ((flags & EXT2FS_CLONE_DBLIST) && fs->dblist) {
+        retval = ext2fs_copy_dblist(fs->dblist, &childfs->dblist);
+        if (retval)
+            return retval;
+        childfs->dblist->fs = childfs;
+    }
+
+    /* icache when NULL will be rebuilt if needed */
+    childfs->icache = NULL;

This should be up where the other NULL values are set.

Quoted 19 lines
lib/ext2fs/dupfs.c +19 -0
+
+    childfs->clone_flags = flags;
+    childfs->parent = fs;
+    *dest = childfs;
+
+    return 0;
+}
+
+errcode_t ext2fs_merge_fs(ext2_filsys *thread_fs)
+{
+    ext2_filsys fs = *thread_fs;
+    errcode_t retval = 0;
+    ext2_filsys dest = fs->parent;
+    ext2_filsys src = fs;
+    unsigned int flags = fs->clone_flags;
+    struct ext2_inode_cache *icache;
+    io_channel dest_io;
+    io_channel dest_image_io;
+    ext2fs_inode_bitmap inode_map;

lib/ext2fs/dupfs.c errout: · line 207

Quoted 10 lines
lib/ext2fs/dupfs.c +10 -0
+    ext2fs_block_bitmap block_map;
+    ext2_badblocks_list badblocks;
+    ext2_dblist dblist;
+    void *priv_data;
+    int fsflags;
+
+    pthread_mutex_lock(&fs->refcount_mutex);
+    fs->refcount--;
+    assert(fs->refcount >= 0);
+    pthread_mutex_unlock(&fs->refcount_mutex);

Same here. The mutex should be held over the whole copy process.

Cheers, Andreas

Quoted 148 lines
lib/ext2fs/dupfs.c +97 -0
+    icache = dest->icache;
+    dest_io = dest->io;
+    dest_image_io = dest->image_io;
+    inode_map = dest->inode_map;
+    block_map = dest->block_map;
+    badblocks = dest->badblocks;
+    dblist = dest->dblist;
+    priv_data = dest->priv_data;
+    fsflags = dest->flags;
+
+    memcpy(dest, src, sizeof(struct struct_ext2_filsys));
+
+    dest->io = dest_io;
+    dest->image_io = dest_image_io;
+    dest->icache = icache;
+    dest->inode_map = inode_map;
+    dest->block_map = block_map;
+    dest->badblocks = badblocks;
+    dest->dblist = dblist;
+    dest->priv_data = priv_data;
+    if (dest->dblist)
+        dest->dblist->fs = dest;
+    dest->flags = src->flags | fsflags;
+    if (!(src->flags & EXT2_FLAG_VALID) || !(dest->flags & EXT2_FLAG_VALID))
+        ext2fs_unmark_valid(dest);
+
+    if ((flags & EXT2FS_CLONE_INODE) && src->inode_map) {
+        if (dest->inode_map == NULL) {
+            dest->inode_map = src->inode_map;
+            src->inode_map = NULL;
+        } else {
+            retval = ext2fs_merge_bitmap(src->inode_map, dest->inode_map, NULL, NULL);
+            if (retval)
+                goto out;
+        }
+        dest->inode_map->fs = dest;
+    }
+
+    if ((flags & EXT2FS_CLONE_BLOCK) && src->block_map) {
+        if (dest->block_map == NULL) {
+            dest->block_map = src->block_map;
+            src->block_map = NULL;
+        } else {
+            retval = ext2fs_merge_bitmap(src->block_map, dest->block_map, NULL, NULL);
+            if (retval)
+                goto out;
+        }
+        dest->block_map->fs = dest;
+    }
+
+    if ((flags & EXT2FS_CLONE_BADBLOCKS) && src->badblocks) {
+        if (dest->badblocks == NULL)
+            retval = ext2fs_badblocks_copy(src->badblocks, &dest->badblocks);
+        else
+            retval = ext2fs_badblocks_merge(src->badblocks, dest->badblocks);
+        if (retval)
+            goto out;
+    }
+
+    if ((flags & EXT2FS_CLONE_DBLIST) && src->dblist) {
+        if (dest->dblist == NULL) {
+            dest->dblist = src->dblist;
+            src->dblist = NULL;
+        } else {
+            retval = ext2fs_merge_dblist(src->dblist, dest->dblist);
+            if (retval)
+                goto out;
+        }
+        dest->dblist->fs = dest;
+    }
+
+    if (src->icache) {
+        ext2fs_free_inode_cache(src->icache);
+        src->icache = NULL;
+    }
+
+out:
+    if (src->io)
+        io_channel_close(src->io);
+
+    if ((flags & EXT2FS_CLONE_INODE) && src->inode_map)
+        ext2fs_free_generic_bmap(src->inode_map);
+    if ((flags & EXT2FS_CLONE_BLOCK) && src->block_map)
+        ext2fs_free_generic_bmap(src->block_map);
+    if ((flags & EXT2FS_CLONE_BADBLOCKS) && src->badblocks)
+        ext2fs_badblocks_list_free(src->badblocks);
+    if ((flags & EXT2FS_CLONE_DBLIST) && src->dblist) {
+        ext2fs_free_dblist(src->dblist);
+        src->dblist = NULL;
+    }
+
+    ext2fs_free_mem(&src);
+    *thread_fs = NULL;
+
+    return retval;
+}
+#endif
lib/ext2fs/ext2fs.h +23 -0
index 139a25fc..b1505f95 100644
@@ -12,6 +12,10 @@
 #ifndef _EXT2FS_EXT2FS_H
 #define _EXT2FS_EXT2FS_H
 
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
 #ifdef __GNUC__
 #define EXT2FS_ATTR(x) __attribute__(x)
 #else
@@ -331,6 +335,13 @@ struct struct_ext2_filsys {
   struct ext2fs_hashmap* block_sha_map;
 
   const struct ext2fs_nls_table *encoding;
+
+#ifdef HAVE_PTHREAD
+    struct struct_ext2_filsys *parent;
+    size_t refcount;
+    pthread_mutex_t refcount_mutex;
+    unsigned int clone_flags;
+#endif
 };
 
 #if EXT2_FLAT_INCLUDES
@@ -1057,6 +1068,18 @@ extern errcode_t ext2fs_move_blocks(ext2_filsys fs,
 /* check_desc.c */
 extern errcode_t ext2fs_check_desc(ext2_filsys fs);
 
+#ifdef HAVE_PTHREAD
+/* flags for ext2fs_clone_fs */
+#define EXT2FS_CLONE_BLOCK            0x0001
+#define EXT2FS_CLONE_INODE            0x0002
+#define EXT2FS_CLONE_BADBLOCKS        0x0004
+#define EXT2FS_CLONE_DBLIST            0x0008
+
+extern errcode_t ext2fs_clone_fs(ext2_filsys fs, ext2_filsys *dest,
+                                 unsigned int flags);
+extern errcode_t ext2fs_merge_fs(ext2_filsys *fs);
+#endif
+
 /* closefs.c */
 extern errcode_t ext2fs_close(ext2_filsys fs);
 extern errcode_t ext2fs_close2(ext2_filsys fs, int flags);
Signature · 1 line
2.37.3
#7 lore raw

Re: [RFCv1 01/72] e2fsck: Fix unbalanced mutex unlock for BOUNCE_MTX

In reply to Andreas Dilger
Quoted 70 lines · Andreas Dilger
Quoted 35 lines · Ritesh Harjani (IBM)
Quoted 12 lines · Andreas Dilger
Quoted 12 lines · Ritesh Harjani (IBM)

f_crashdisk test failed with UNIX_IO_FORCE_BOUNCE=yes due to unbalanced mutex unlock in below path.

This patch fixes it.

---
lib/ext2fs/unix_io.c | 1 -

1 file changed, 1 deletion(-)
Quoted 12 lines

lib/ext2fs/unix_io.c bounce_read: · line 308

Quoted 9 lines
lib/ext2fs/unix_io.c +0 -1
index e53db333..5b894826 100644
@@ -305,7 +305,6 @@ bounce_read:
  while (size > 0) {
      actual = read(data->dev, data->bounce, align_size);
      if (actual != align_size) {
-            mutex_unlock(data, BOUNCE_MTX);

This patch doesn't show enough context, but AFAIK this is jumping before mutex_down() is called, so this *should* be correct as is?

Thanks for the review, Andreas.

Yeah, the patch diff above is not sufficient since it doesn't share enuf context.
But essentially when "actual" is not equal to "align_size", then in this if condition it goes to label "short_read:", which always goto error_unlock, where we anyways call mutex_unlock()

Quoted 3 lines

Looking at a lot of labels in this function, this definitely looks like something which can be cleaned up ("raw_read_blk()").
I will add that to my list of todos.

You are correct, and it means this code is just not very clear to the reader. I think it would make more sense to move the "short_read:" label to the end of the code:

                  actual = read(...);
                  if (actual != size)
                          goto error_short_read;
                  goto success_unlock;
        :
                 actual = read(...);
                 if (actual != align_size) {
                           actual = really_read;
                           buf -= really_read;
                           size += really_read;
                           goto error_short_read;
                 }
        :
success_unlock:
        mutex_unlock(...);
        return 0;

error_short_read:

        if (actual < 0) {
                 retval = errno;
                 actual = 0;
        } else {
                 retval = EXT2_ET_SHORT_READ;
        }
error_unlock:
        mutex_unlock(...);
Quoted 2 lines

That way the code follows the normal error handling convention and is less likely to be surprising to the reader.

Yes, you are right. I will do the change in the next rev.

-ritesh