2022-11-07 12:23:23

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 00/72] 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

--
2.37.3



2022-11-07 12:23:26

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 02/72] gen_bitmaps: Fix ext2fs_compare_generic_bmap/bitmap logic

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.

Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
lib/ext2fs/gen_bitmap.c | 9 ++++++---
lib/ext2fs/gen_bitmap64.c | 10 +++++++---
2 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/lib/ext2fs/gen_bitmap.c b/lib/ext2fs/gen_bitmap.c
index 1536d4b3..f7764fca 100644
--- a/lib/ext2fs/gen_bitmap.c
+++ b/lib/ext2fs/gen_bitmap.c
@@ -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;
}
diff --git a/lib/ext2fs/gen_bitmap64.c b/lib/ext2fs/gen_bitmap64.c
index c860c10e..f7710afd 100644
--- a/lib/ext2fs/gen_bitmap64.c
+++ b/lib/ext2fs/gen_bitmap64.c
@@ -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;
}
--
2.37.3


2022-11-07 12:23:39

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 03/72] blkmap64_ba: Add common helper for bits size calculation

Just a quick common helper for bits size calculation.

Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
lib/ext2fs/blkmap64_ba.c | 20 +++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/lib/ext2fs/blkmap64_ba.c b/lib/ext2fs/blkmap64_ba.c
index 5d8f1548..4e7007f0 100644
--- a/lib/ext2fs/blkmap64_ba.c
+++ b/lib/ext2fs/blkmap64_ba.c
@@ -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
--
2.37.3


2022-11-07 12:23:45

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 04/72] badblocks: Remove unused badblocks_flags

badblocks_flags is not used anywhere. So just remove it.

Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
lib/ext2fs/badblocks.c | 6 +-----
lib/ext2fs/ext2fsP.h | 1 -
2 files changed, 1 insertion(+), 6 deletions(-)

diff --git a/lib/ext2fs/badblocks.c b/lib/ext2fs/badblocks.c
index a306bc06..345168e0 100644
--- a/lib/ext2fs/badblocks.c
+++ b/lib/ext2fs/badblocks.c
@@ -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,
diff --git a/lib/ext2fs/ext2fsP.h b/lib/ext2fs/ext2fsP.h
index a20a0502..d2045af8 100644
--- a/lib/ext2fs/ext2fsP.h
+++ b/lib/ext2fs/ext2fsP.h
@@ -34,7 +34,6 @@ struct ext2_struct_u32_list {
int num;
int size;
__u32 *list;
- int badblocks_flags;
};

struct ext2_struct_u32_iterate {
--
2.37.3


2022-11-07 12:23:47

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 01/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.

Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
lib/ext2fs/unix_io.c | 1 -
1 file changed, 1 deletion(-)

diff --git a/lib/ext2fs/unix_io.c b/lib/ext2fs/unix_io.c
index e53db333..5b894826 100644
--- a/lib/ext2fs/unix_io.c
+++ b/lib/ext2fs/unix_io.c
@@ -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;
--
2.37.3


2022-11-07 12:23:52

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 05/72] badblocks: Add badblocks merge logic

From: Wang Shilong <[email protected]>

Add badblocks merge logic

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
lib/ext2fs/badblocks.c | 75 ++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2fs.h | 2 ++
2 files changed, 77 insertions(+)

diff --git a/lib/ext2fs/badblocks.c b/lib/ext2fs/badblocks.c
index 345168e0..36794e69 100644
--- a/lib/ext2fs/badblocks.c
+++ b/lib/ext2fs/badblocks.c
@@ -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.
*
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 9cc994b1..18dddc2c 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -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);
--
2.37.3


2022-11-07 12:23:59

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 06/72] dblist: add dblist merge logic

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.

Signed-off-by: Li Xi <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
lib/ext2fs/dblist.c | 36 ++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2fs.h | 1 +
2 files changed, 37 insertions(+)

diff --git a/lib/ext2fs/dblist.c b/lib/ext2fs/dblist.c
index bbdb221d..5568b8ec 100644
--- a/lib/ext2fs/dblist.c
+++ b/lib/ext2fs/dblist.c
@@ -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
*
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 18dddc2c..443f93d2 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -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 *));
--
2.37.3


2022-11-07 12:24:01

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 07/72] libext2fs: Add rbtree bitmap merge logic

From: Wang Shilong <[email protected]>

This adds rbtree bitmap merge logic.

Signed-off-by: Li Xi <[email protected]>
Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
lib/ext2fs/blkmap64_rb.c | 65 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 65 insertions(+)

diff --git a/lib/ext2fs/blkmap64_rb.c b/lib/ext2fs/blkmap64_rb.c
index 0df58dc7..d7c88aef 100644
--- a/lib/ext2fs/blkmap64_rb.c
+++ b/lib/ext2fs/blkmap64_rb.c
@@ -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,
--
2.37.3


2022-11-07 12:24:06

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 09/72] libext2fs: Add flush cleanup API

From: Li Xi <[email protected]>

Signed-off-by: Li Xi <[email protected]>
Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[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(-)

diff --git a/lib/ext2fs/ext2_io.h b/lib/ext2fs/ext2_io.h
index 8fe5b323..8cc355be 100644
--- a/lib/ext2fs/ext2_io.h
+++ b/lib/ext2fs/ext2_io.h
@@ -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 */
diff --git a/lib/ext2fs/undo_io.c b/lib/ext2fs/undo_io.c
index f4a6d526..678ff421 100644
--- a/lib/ext2fs/undo_io.c
+++ b/lib/ext2fs/undo_io.c
@@ -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,
diff --git a/lib/ext2fs/unix_io.c b/lib/ext2fs/unix_io.c
index 5b894826..8f8118a3 100644
--- a/lib/ext2fs/unix_io.c
+++ b/lib/ext2fs/unix_io.c
@@ -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;
--
2.37.3


2022-11-07 12:24:06

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 10/72] libext2fs: merge icounts after thread finishes

From: Li Xi <[email protected]>

Merge inode_count and inode_link_info properly after
threads finish.

Signed-off-by: Li Xi <[email protected]>
Signed-off-by: Wang Shilong <[email protected]>
[Note: splitted the patch to seperate libext2fs changes from e2fsck]
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
lib/ext2fs/ext2fs.h | 1 +
lib/ext2fs/icount.c | 103 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 104 insertions(+)

diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 54aed5d1..139a25fc 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -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 *);

diff --git a/lib/ext2fs/icount.c b/lib/ext2fs/icount.c
index 888a90b2..766eccca 100644
--- a/lib/ext2fs/icount.c
+++ b/lib/ext2fs/icount.c
@@ -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)
--
2.37.3


2022-11-07 12:24:16

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 11/72] libext2fs: merge quota context after threads finish

From: Wang Shilong <[email protected]>

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

Signed-off-by: Wang Shilong <[email protected]>
[Note: splitted the patch to seperate libext2fs changes from e2fsck]
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
lib/support/mkquota.c | 39 +++++++++++++++++++++++++++++++++++++++
lib/support/quotaio.h | 3 +++
2 files changed, 42 insertions(+)

diff --git a/lib/support/mkquota.c b/lib/support/mkquota.c
index 9339c994..ce1ab4cd 100644
--- a/lib/support/mkquota.c
+++ b/lib/support/mkquota.c
@@ -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
diff --git a/lib/support/quotaio.h b/lib/support/quotaio.h
index 84fac35d..240a0762 100644
--- a/lib/support/quotaio.h
+++ b/lib/support/quotaio.h
@@ -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 */
--
2.37.3


2022-11-07 12:24:23

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 12/72] libext2fs: dupfs: Add fs clone & merge api

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.

Signed-off-by: Saranya Muruganandam <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
lib/ext2fs/dupfs.c | 183 ++++++++++++++++++++++++++++++++++++++++++++
lib/ext2fs/ext2fs.h | 23 ++++++
2 files changed, 206 insertions(+)

diff --git a/lib/ext2fs/dupfs.c b/lib/ext2fs/dupfs.c
index 02721e1a..ecc57cf7 100644
--- a/lib/ext2fs/dupfs.c
+++ b/lib/ext2fs/dupfs.c
@@ -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
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 139a25fc..b1505f95 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -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);
--
2.37.3


2022-11-07 12:24:26

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 08/72] libext2fs: Add bitmaps merge ops

From: Wang Shilong <[email protected]>

Signed-off-by: Li Xi <[email protected]>
Signed-off-by: Wang Shilong <[email protected]>
[Note: splitted rb merge tree logic patch such that we
seperate out libext2fs changes from e2fsck specific changes]
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
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(+)

diff --git a/lib/ext2fs/bitmaps.c b/lib/ext2fs/bitmaps.c
index 8bfa24b1..9437331e 100644
--- a/lib/ext2fs/bitmaps.c
+++ b/lib/ext2fs/bitmaps.c
@@ -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);
diff --git a/lib/ext2fs/bmap64.h b/lib/ext2fs/bmap64.h
index de334548..555193ee 100644
--- a/lib/ext2fs/bmap64.h
+++ b/lib/ext2fs/bmap64.h
@@ -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);
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 443f93d2..54aed5d1 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -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);
diff --git a/lib/ext2fs/gen_bitmap64.c b/lib/ext2fs/gen_bitmap64.c
index f7710afd..c31f942f 100644
--- a/lib/ext2fs/gen_bitmap64.c
+++ b/lib/ext2fs/gen_bitmap64.c
@@ -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)
--
2.37.3


2022-11-07 12:24:41

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 13/72] tst_badblocks: Add unit test to verify badblocks list merge api

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

Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
lib/ext2fs/tst_badblocks.c | 61 ++++++++++++++++++++++++++++++++++++--
1 file changed, 59 insertions(+), 2 deletions(-)

diff --git a/lib/ext2fs/tst_badblocks.c b/lib/ext2fs/tst_badblocks.c
index b6e766ab..946de0ae 100644
--- a/lib/ext2fs/tst_badblocks.c
+++ b/lib/ext2fs/tst_badblocks.c
@@ -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;

--
2.37.3


2022-11-07 12:24:41

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 14/72] tst_bitmaps_standalone: Add copy and merge bitmaps test

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

Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
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

diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in
index f6a050a2..1692500e 100644
--- a/lib/ext2fs/Makefile.in
+++ b/lib/ext2fs/Makefile.in
@@ -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 \
diff --git a/lib/ext2fs/tst_bitmaps_standalone.c b/lib/ext2fs/tst_bitmaps_standalone.c
new file mode 100644
index 00000000..68b598a8
--- /dev/null
+++ b/lib/ext2fs/tst_bitmaps_standalone.c
@@ -0,0 +1,170 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "bmap64.h"
+
+ext2_filsys test_fs;
+ext2fs_block_bitmap block_map_1;
+ext2fs_block_bitmap block_map_2;
+ext2fs_block_bitmap block_map;
+
+static int test_fail = 0;
+
+void dump_bitmap(ext2fs_generic_bitmap bmap, unsigned int start, unsigned num)
+{
+ unsigned char *buf;
+ errcode_t retval;
+ int i, len = (num - start + 7) / 8;
+
+ buf = malloc(len);
+ if (!buf) {
+ com_err("dump_bitmap", 0, "couldn't allocate buffer");
+ return;
+ }
+ memset(buf, 0, len);
+ retval = ext2fs_get_generic_bmap_range(bmap, (__u64) start, num, buf);
+ if (retval) {
+ com_err("dump_bitmap", retval,
+ "while calling ext2fs_generic_bmap_range");
+ free(buf);
+ return;
+ }
+ for (i=len-1; i >= 0; i--)
+ printf("%02x ", buf[i]);
+ printf("\n");
+ printf("bits set: %u\n", ext2fs_bitcount(buf, len));
+ free(buf);
+}
+
+static void test_copy_run()
+{
+ int blocks[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 21, 23, 26, 29, 33, 37, 38};
+ errcode_t ret;
+ char *buf_map = NULL;
+ char *buf_copy_map = NULL;
+
+ assert(ext2fs_allocate_block_bitmap(test_fs, "block bitmap", &block_map_1) == 0);
+
+ for (int i = 0; i < sizeof(blocks)/sizeof(blocks[0]); i++) {
+ ext2fs_mark_block_bitmap2(block_map_1, blocks[i]);
+ }
+
+ assert(ext2fs_copy_bitmap(block_map_1, &block_map) == 0);
+
+ if (ext2fs_compare_block_bitmap(block_map_1, block_map) != 0) {
+ printf("block bitmap copy test failed\n");
+ test_fail++;
+
+ dump_bitmap(block_map_1, test_fs->super->s_first_data_block,
+ test_fs->super->s_blocks_count);
+
+ dump_bitmap(block_map, test_fs->super->s_first_data_block,
+ test_fs->super->s_blocks_count);
+ }
+
+ ext2fs_free_block_bitmap(block_map_1);
+ ext2fs_free_block_bitmap(block_map);
+}
+
+void test_merge_run()
+{
+ int blocks_odd[] = {1, 3, 5, 7, 9, 21, 23, 29, 33, 37};
+ int blocks_even[] = {2, 4, 6, 8, 10, 26, 38};
+ ext2fs_generic_bitmap_64 tmp_map;
+
+ assert(ext2fs_allocate_block_bitmap(test_fs, "block bitmap 1", &block_map_1) == 0);
+ assert(ext2fs_allocate_block_bitmap(test_fs, "block bitmap 2", &block_map_2) == 0);
+ assert(ext2fs_allocate_block_bitmap(test_fs, "block bitmap 2", &block_map) == 0);
+
+ for (int i = 0; i < sizeof(blocks_odd) / sizeof(blocks_odd[0]); i++) {
+ ext2fs_mark_block_bitmap2(block_map_1, blocks_odd[i]);
+ ext2fs_mark_block_bitmap2(block_map, blocks_odd[i]);
+ }
+
+ for (int i = 0; i < sizeof(blocks_even) / sizeof(blocks_even[0]); i++) {
+ ext2fs_mark_block_bitmap2(block_map_2, blocks_even[i]);
+ ext2fs_mark_block_bitmap2(block_map, blocks_even[i]);
+ }
+
+ assert(ext2fs_merge_bitmap(block_map_2, block_map_1, NULL, NULL) == 0);
+ if (ext2fs_compare_block_bitmap(block_map_1, block_map) != 0) {
+ printf("block bitmap merge test failed\n");
+ test_fail++;
+
+ dump_bitmap(block_map_1, test_fs->super->s_first_data_block,
+ test_fs->super->s_blocks_count);
+
+ dump_bitmap(block_map, test_fs->super->s_first_data_block,
+ test_fs->super->s_blocks_count);
+ }
+
+ ext2fs_free_block_bitmap(block_map_1);
+ ext2fs_free_block_bitmap(block_map_2);
+ ext2fs_free_block_bitmap(block_map);
+}
+
+static void setup_filesystem(const char *name, unsigned int blocks,
+ unsigned int inodes, unsigned int type,
+ unsigned int flags)
+{
+ struct ext2_super_block param;
+ errcode_t ret;
+
+ memset(&param, 0, sizeof(param));
+ ext2fs_blocks_count_set(&param, blocks);
+ param.s_inodes_count = inodes;
+
+ ret = ext2fs_initialize(name, flags, &param, test_io_manager,
+ &test_fs);
+ if (ret) {
+ com_err(name, ret, "while initializing filesystem");
+ return;
+ }
+
+ test_fs->default_bitmap_type = type;
+
+ ext2fs_free_block_bitmap(test_fs->block_map);
+ ext2fs_free_block_bitmap(test_fs->inode_map);
+
+ return;
+errout:
+ ext2fs_close_free(&test_fs);
+}
+
+int main(int argc, char **argv)
+{
+ unsigned int blocks = 127;
+ unsigned int inodes = 0;
+ unsigned int type = EXT2FS_BMAP64_RBTREE;
+ unsigned int flags = EXT2_FLAG_64BITS;
+ char *buf = NULL;
+
+ setup_filesystem(argv[0], blocks, inodes, type, flags);
+
+ /* test for EXT2FS_BMAP64_RBTREE */
+ test_copy_run();
+ test_merge_run();
+
+ /* TODO: test for EXT2FS_BMAP64_BITARRAY */
+
+ if (test_fail)
+ printf("%s: Test copy & merge bitmaps -- NOT OK\n", argv[0]);
+ else
+ printf("%s: Test copy & merge bitmaps -- OK\n", argv[0]);
+
+ return test_fail;
+}
--
2.37.3


2022-11-07 12:24:42

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 15/72] tst_bitmaps_pthread: Add merge bitmaps test using pthreads

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

Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
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

diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in
index 1692500e..c0694175 100644
--- a/lib/ext2fs/Makefile.in
+++ b/lib/ext2fs/Makefile.in
@@ -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 \
diff --git a/lib/ext2fs/tst_bitmaps_pthread.c b/lib/ext2fs/tst_bitmaps_pthread.c
new file mode 100644
index 00000000..243810ca
--- /dev/null
+++ b/lib/ext2fs/tst_bitmaps_pthread.c
@@ -0,0 +1,247 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_PTHREAD
+#include <pthread.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * In this test we first setup used_bitmap by setting some random bits.
+ * This used_bitmap is then scanned in parallel by two threads, each scanning
+ * upto nr_bits/2 and setting their respective child_bitmap.
+ * Then once both threads finishes, we merge the child_bitmap_1/2 into
+ * parent_bitmap which then is used to compare against used_bitmap.
+ * In the end used_bitmap bits should match with parent_bitmap.
+ *
+ * Note we use EXT2FS_BMAP64_BITARRAY always for used_bitmap, this is because
+ * EXT2FS_BMAP64_RBTREE does not support parallel scan due to rcursor
+ * optimization.
+ */
+
+int test_fail = 0;
+ext2fs_generic_bitmap child_bitmap1, child_bitmap2, parent_bitmap;
+ext2fs_generic_bitmap used_bitmap;
+pthread_t pthread_infos[2];
+
+#define nr_bits 8192
+int nr_threads = 2;
+int bitmap_type[1] = {EXT2FS_BMAP64_RBTREE};
+
+void dump_bitmap(ext2fs_generic_bitmap bmap, unsigned int start, unsigned num)
+{
+ unsigned char *buf;
+ errcode_t retval;
+ int i, len = (num - start + 7) / 8;
+
+ buf = malloc(len);
+ if (!buf) {
+ com_err("dump_bitmap", 0, "couldn't allocate buffer");
+ return;
+ }
+ memset(buf, 0, len);
+ retval = ext2fs_get_generic_bmap_range(bmap, (__u64) start, num, buf);
+ if (retval) {
+ com_err("dump_bitmap", retval,
+ "while calling ext2fs_generic_bmap_range");
+ free(buf);
+ return;
+ }
+ for (i=len-1; i >= 0; i--)
+ printf("%02x ", buf[i]);
+ printf("\n");
+ printf("bits set: %u\n", ext2fs_bitcount(buf, len));
+ free(buf);
+}
+
+int should_mark_bit()
+{
+ return rand() % 2 == 0;
+}
+
+void alloc_bitmaps(int type)
+{
+ errcode_t retval;
+
+ retval = ext2fs_alloc_generic_bmap(NULL, EXT2_ET_MAGIC_GENERIC_BITMAP64,
+ type, 0, nr_bits, nr_bits,
+ "child bitmap1", &child_bitmap1);
+ if (retval)
+ goto out;
+
+ retval = ext2fs_alloc_generic_bmap(NULL, EXT2_ET_MAGIC_GENERIC_BITMAP64,
+ type, 0, nr_bits, nr_bits,
+ "child bitmap2", &child_bitmap2);
+ if (retval)
+ goto out;
+
+ retval = ext2fs_alloc_generic_bmap(NULL, EXT2_ET_MAGIC_GENERIC_BITMAP64,
+ type, 0, nr_bits, nr_bits,
+ "parent bitmap", &parent_bitmap);
+ if (retval)
+ goto out;
+
+ /*
+ * Note that EXT2FS_BMAP64_RBTREE doesn't support parallel read.
+ * this is due to a optimization of maintaining a read cursor within
+ * rbtree bitmap implementation.
+ */
+ retval = ext2fs_alloc_generic_bmap(NULL, EXT2_ET_MAGIC_GENERIC_BITMAP64,
+ EXT2FS_BMAP64_BITARRAY, 0, nr_bits, nr_bits,
+ "used bitmap", &used_bitmap);
+ if (retval)
+ goto out;
+
+ return;
+out:
+ com_err("alloc_bitmaps", retval, "while allocating bitmaps\n");
+ exit(1);
+}
+
+void setup_bitmaps()
+{
+ int i = 0;
+ errcode_t retval;
+
+ /*
+ * Note we cannot setup used_bitmap in parallel w/o locking.
+ * Hence setting up the used_bitmap (random bits) here before
+ * starting pthreads.
+ */
+ for (i = 0; i < nr_bits; i++) {
+ if (should_mark_bit())
+ ext2fs_mark_generic_bmap(used_bitmap, i);
+ }
+}
+
+static void *run_pthread(void *arg)
+{
+ int i = 0, j = 0, start, end;
+ ext2fs_generic_bitmap test_bitmap;
+ errcode_t retval = 0;
+ pthread_t id = pthread_self();
+
+ if (pthread_equal(pthread_infos[0], id)) {
+ start = 0;
+ end = nr_bits/2;
+ test_bitmap = child_bitmap1;
+ } else {
+ start = nr_bits / 2 + 1;;
+ end = nr_bits - 1;
+ test_bitmap = child_bitmap2;
+ }
+
+ for (i = start; i <= end; i++) {
+ if (ext2fs_test_generic_bmap(used_bitmap, i)) {
+ retval = ext2fs_mark_generic_bmap(test_bitmap, i);
+ if (retval) {
+ com_err("run_pthread", retval, "while marking child bitmaps %d\n", i);
+ test_fail++;
+ pthread_exit(&retval);
+ }
+ }
+ }
+ return NULL;
+}
+
+void run_pthreads()
+{
+ errcode_t retval;
+ void *retp[2];
+ int i;
+
+ for (i = 0; i < nr_threads; i++) {
+ printf("Starting thread (%d)\n", i);
+ retval = pthread_create(&pthread_infos[i], NULL, &run_pthread, NULL);
+ if (retval) {
+ com_err("run_pthreads", retval, "while pthread_create");
+ exit(1);
+ }
+ }
+
+ for (i = 0; i < nr_threads; i++) {
+ void *status;
+ int ret;
+ retval = pthread_join(pthread_infos[i], &status);
+ if (retval) {
+ com_err("run_pthreads", retval, "while joining pthreads");
+ exit(1);
+
+ }
+ ret = status == NULL ? 0 : *(int*)status;
+ if (ret) {
+ com_err("run_pthreads", ret, "pthread returned error");
+ test_fail++;
+ }
+
+ printf("Closing thread (%d), ret(%d)\n", i, ret);
+ }
+
+ assert(ext2fs_merge_bitmap(child_bitmap1, parent_bitmap, NULL, NULL) == 0);
+ assert(ext2fs_merge_bitmap(child_bitmap2, parent_bitmap, NULL, NULL) == 0);
+}
+
+void test_bitmaps(int type)
+{
+ errcode_t retval;
+ retval = ext2fs_compare_generic_bmap(EXT2_ET_NEQ_BLOCK_BITMAP, parent_bitmap,
+ used_bitmap);
+ if (retval) {
+ test_fail++;
+ printf("Bitmaps compare failed for bitmap type %d err %ld\n", type, retval);
+ dump_bitmap(parent_bitmap, 0, nr_bits);
+ dump_bitmap(used_bitmap, 0, nr_bits);
+ }
+}
+
+void free_bitmaps()
+{
+ ext2fs_free_generic_bmap(child_bitmap1);
+ ext2fs_free_generic_bmap(child_bitmap2);
+ ext2fs_free_generic_bmap(parent_bitmap);
+ ext2fs_free_generic_bmap(used_bitmap);
+}
+
+int main(int argc, char *argv[])
+{
+ int i;
+ int ret = 0;
+
+#ifndef HAVE_PTHREAD
+ printf("No PTHREAD support, exiting...\n");
+ return ret;
+#endif
+
+ srand(time(0));
+
+ /* loop to test for bitmap types */
+ for (i = 0; i < 1; i++) {
+ test_fail = 0;
+ alloc_bitmaps(bitmap_type[i]);
+ setup_bitmaps();
+ run_pthreads();
+ test_bitmaps(bitmap_type[i]);
+ free_bitmaps();
+
+ if (test_fail)
+ printf("%s: Test with bitmap (%d) NOT OK!!\n", argv[0], bitmap_type[i]);
+ else
+ printf("%s: Test with bitmap (%d) OK!!\n", argv[0], bitmap_type[i]);
+ ret |= test_fail;
+ }
+
+ return ret;
+}
--
2.37.3


2022-11-07 12:24:47

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 16/72] tst_libext2fs_pthread: Add libext2fs merge/clone unit tests

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.

Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
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

diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in
index c0694175..5fde9900 100644
--- a/lib/ext2fs/Makefile.in
+++ b/lib/ext2fs/Makefile.in
@@ -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 \
diff --git a/lib/ext2fs/tst_libext2fs_pthread.c b/lib/ext2fs/tst_libext2fs_pthread.c
new file mode 100644
index 00000000..a5bb6fcd
--- /dev/null
+++ b/lib/ext2fs/tst_libext2fs_pthread.c
@@ -0,0 +1,315 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_PTHREAD
+#include <pthread.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#ifdef HAVE_PTHREAD
+int test_fail = 0;
+ext2_filsys testfs;
+ext2fs_inode_bitmap inode_used_map;
+ext2fs_block_bitmap block_used_map;
+ext2_filsys childfs[2];
+pthread_t pthread_infos[2];
+
+#define nr_bits 16384
+int nr_threads = 2;
+
+int should_mark_bit()
+{
+ return rand() % 2 == 0;
+}
+
+void setupfs()
+{
+ errcode_t retval;
+ struct ext2_super_block param;
+
+ initialize_ext2_error_table();
+
+ memset(&param, 0, sizeof(param));
+ ext2fs_blocks_count_set(&param, nr_bits);
+ retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, &param,
+ test_io_manager, &testfs);
+ if (retval) {
+ com_err("setup", retval, "while initializing filesystem");
+ exit(1);
+ }
+
+ retval = ext2fs_allocate_tables(testfs);
+ if (retval) {
+ com_err("setup", retval, "while allocating tables for testfs");
+ exit(1);
+ }
+}
+
+void setup_used_bitmaps()
+{
+ int saved_type = testfs->default_bitmap_type;
+ ext2_inode_scan scan;
+ struct ext2_inode inode;
+ ext2_ino_t ino;
+ errcode_t retval;
+ int i;
+
+ testfs->default_bitmap_type = EXT2FS_BMAP64_BITARRAY;
+
+ /* allocate block and inode used bitmaps */
+ retval = ext2fs_allocate_block_bitmap(testfs, "block used map", &block_used_map);
+ if (retval)
+ goto out;
+
+ retval = ext2fs_allocate_inode_bitmap(testfs, "inode used map", &inode_used_map);
+ if (retval)
+ goto out;
+
+ /* setup block and inode used bitmaps */
+ for (i = 1; i < nr_bits; i++) {
+ /*
+ * we check for testfs->block_map as well since there could be some
+ * blocks already set as part of the FS metadata.
+ */
+ if (should_mark_bit() || ext2fs_test_block_bitmap2(testfs->block_map, i)) {
+ ext2fs_mark_block_bitmap2(block_used_map, i);
+ }
+ }
+
+ retval = ext2fs_open_inode_scan(testfs, 8, &scan);
+ if (retval) {
+ com_err("setup_inode_map", retval, "while open inode scan");
+ exit(1);
+ }
+
+ retval = ext2fs_get_next_inode(scan, &ino, &inode);
+ if (retval) {
+ com_err("setup_inode_map", retval, "while getting next inode");
+ exit(1);
+ }
+
+ while (ino) {
+ if (should_mark_bit())
+ ext2fs_mark_inode_bitmap2(inode_used_map, ino);
+
+ retval = ext2fs_get_next_inode(scan, &ino, &inode);
+ if (retval) {
+ com_err("setup_inode_map", retval, "while getting next inode");
+ exit(1);
+ }
+ }
+ ext2fs_close_inode_scan(scan);
+
+ testfs->default_bitmap_type = saved_type;
+ return;
+out:
+ com_err("setup_used_bitmaps", retval, "while setting up bitmaps\n");
+ exit(1);
+}
+
+void setup_childfs()
+{
+ errcode_t retval;
+ int i;
+
+ for (i = 0; i < nr_threads; i++) {
+ retval = ext2fs_clone_fs(testfs, &childfs[i], EXT2FS_CLONE_INODE | EXT2FS_CLONE_BLOCK);
+ if (retval) {
+ com_err("setup_childfs", retval, "while clone testfs for childfs");
+ exit(1);
+ }
+
+ retval = childfs[i]->io->manager->open(childfs[i]->device_name,
+ IO_FLAG_THREADS | IO_FLAG_NOCACHE, &childfs[i]->io);
+ if (retval) {
+ com_err("setup_pthread", retval, "while opening childfs");
+ exit(1);
+ }
+ assert(childfs[i]->parent == testfs);
+ }
+}
+
+static errcode_t scan_callback(ext2_filsys fs,
+ ext2_inode_scan scan EXT2FS_ATTR((unused)),
+ dgrp_t group, void *priv_data)
+{
+ pthread_t id = *((pthread_t *)priv_data);
+
+ printf("%s: Called for group %d via thread %d\n", __func__, group,
+ pthread_equal(pthread_infos[1], id));
+ if (pthread_equal(pthread_infos[0], id)) {
+ if (group >= fs->group_desc_count / 2 - 1)
+ return 1;
+ }
+ return 0;
+}
+
+static void *run_pthread(void *arg)
+{
+ errcode_t retval = 0;
+ int i = 0, start, end;
+ ext2fs_block_bitmap test_block_bitmap;
+ ext2fs_inode_bitmap test_inode_bitmap;
+ ext2_inode_scan scan;
+ struct ext2_inode inode;
+ ext2_ino_t ino;
+ pthread_t id = pthread_self();
+
+ if (pthread_equal(pthread_infos[0], id)) {
+ start = 1;
+ end = nr_bits/2;
+ test_block_bitmap = childfs[0]->block_map;
+ test_inode_bitmap = childfs[0]->inode_map;
+
+ retval = ext2fs_open_inode_scan(childfs[0], 8, &scan);
+ if (retval) {
+ com_err("setup_inode_map", retval, "while open inode scan");
+ exit(1);
+ }
+
+ } else {
+ start = nr_bits / 2 + 1;;
+ end = nr_bits - 1;
+ test_block_bitmap = childfs[1]->block_map;
+ test_inode_bitmap = childfs[1]->inode_map;
+
+ retval = ext2fs_open_inode_scan(childfs[1], 8, &scan);
+ if (retval) {
+ com_err("setup_inode_map", retval, "while open inode scan");
+ exit(1);
+ }
+ ext2fs_inode_scan_goto_blockgroup(scan, testfs->group_desc_count/2);
+ }
+
+ ext2fs_set_inode_callback(scan, scan_callback, &id);
+
+ /* blocks scan */
+ for (i = start; i <= end; i++) {
+ if (ext2fs_test_block_bitmap2(block_used_map, i)) {
+ ext2fs_mark_block_bitmap2(test_block_bitmap, i);
+ }
+ }
+
+ /* inodes scan */
+ retval = ext2fs_get_next_inode(scan, &ino, &inode);
+ if (retval) {
+ com_err("setup_inode_map", retval, "while getting next inode");
+ exit(1);
+ }
+
+ while (ino) {
+ if (ext2fs_test_inode_bitmap2(inode_used_map, ino)) {
+ ext2fs_mark_inode_bitmap2(test_inode_bitmap, ino);
+ }
+
+ retval = ext2fs_get_next_inode(scan, &ino, &inode);
+ if (retval)
+ break;
+ }
+ ext2fs_close_inode_scan(scan);
+ return NULL;
+}
+
+void run_pthreads()
+{
+ errcode_t retval;
+ int i;
+
+ for (i = 0; i < nr_threads; i++) {
+ printf("Starting thread (%d)\n", i);
+ retval = pthread_create(&pthread_infos[i], NULL, &run_pthread, NULL);
+ if (retval) {
+ com_err("run_pthreads", retval, "while pthread_create");
+ exit(1);
+ }
+ }
+
+ for (i = 0; i < nr_threads; i++) {
+ void *status;
+ int ret;
+ retval = pthread_join(pthread_infos[i], &status);
+ if (retval) {
+ com_err("run_pthreads", retval, "while joining pthreads");
+ exit(1);
+
+ }
+ ret = status == NULL ? 0 : *(int*)status;
+ if (ret) {
+ com_err("run_pthreads", ret, "pthread returned error");
+ test_fail++;
+ }
+
+ printf("Closing thread (%d), ret(%d)\n", i, ret);
+ }
+
+ assert(ext2fs_merge_fs(&childfs[0]) == 0);
+ assert(ext2fs_merge_fs(&childfs[1]) == 0);
+}
+
+void test_bitmaps()
+{
+ errcode_t retval;
+ retval = ext2fs_compare_block_bitmap(testfs->block_map, block_used_map);
+ if (retval) {
+ printf("Block bitmap compare -- NOT OK!! (%ld)\n", retval);
+ test_fail++;
+ }
+
+ printf("Block compare bitmap -- OK!!\n");
+ retval = ext2fs_compare_inode_bitmap(testfs->inode_map, inode_used_map);
+ if (retval) {
+ printf("Inode bitmap compare -- NOT OK!! (%ld)\n", retval);
+ test_fail++;
+ }
+ printf("Inode compare bitmap -- OK!!\n");
+}
+
+void free_used_bitmaps()
+{
+ ext2fs_free_block_bitmap(block_used_map);
+ ext2fs_free_inode_bitmap(inode_used_map);
+}
+
+#endif
+
+int main(int argc, char *argv[])
+{
+ int i;
+
+#ifndef HAVE_PTHREAD
+ printf("No PTHREAD support, exiting...\n");
+ return 0;
+#else
+
+ srand(time(0));
+
+ setupfs();
+ setup_used_bitmaps();
+
+ setup_childfs();
+ run_pthreads();
+ test_bitmaps(i);
+
+ if (test_fail)
+ printf("%s: Test libext2fs clone/merge with pthreads NOT OK!!\n", argv[0]);
+ else
+ printf("%s: Test libext2fs clone/merge with pthreads OK!!\n", argv[0]);
+ free_used_bitmaps();
+ ext2fs_free(testfs);
+
+ return test_fail;
+#endif
+}
--
2.37.3


2022-11-07 12:24:50

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 17/72] libext2fs: Add support for ext2fs_test_block_bitmap_range2_valid()

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()

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
lib/ext2fs/bitops.h | 2 ++
lib/ext2fs/gen_bitmap64.c | 33 +++++++++++++++++++++++++++++++++
2 files changed, 35 insertions(+)

diff --git a/lib/ext2fs/bitops.h b/lib/ext2fs/bitops.h
index 505b3c9c..1facc8dd 100644
--- a/lib/ext2fs/bitops.h
+++ b/lib/ext2fs/bitops.h
@@ -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 */

diff --git a/lib/ext2fs/gen_bitmap64.c b/lib/ext2fs/gen_bitmap64.c
index c31f942f..a9637cb5 100644
--- a/lib/ext2fs/gen_bitmap64.c
+++ b/lib/ext2fs/gen_bitmap64.c
@@ -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)
{
--
2.37.3


2022-11-07 12:24:50

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 18/72] libext2fs: Add support to get average group count

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.

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
lib/ext2fs/ext2fs.h | 32 +++++++++++++++++++++++++++++++-
1 file changed, 31 insertions(+), 1 deletion(-)

diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index b1505f95..6b4926ce 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -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

--
2.37.3


2022-11-07 12:25:38

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 19/72] libext2fs: Misc fixes for struct_ext2_filsys

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.

Signed-off-by: Andreas Dilger <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
lib/ext2fs/ext2fs.h | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 6b4926ce..950ab042 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -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.
--
2.37.3


2022-11-07 12:25:47

by Ritesh Harjani

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

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.

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
lib/ext2fs/dblist.c | 2 ++
lib/ext2fs/icount.c | 4 ++++
2 files changed, 6 insertions(+)

diff --git a/lib/ext2fs/dblist.c b/lib/ext2fs/dblist.c
index 5568b8ec..c19e17bc 100644
--- a/lib/ext2fs/dblist.c
+++ b/lib/ext2fs/dblist.c
@@ -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;
diff --git a/lib/ext2fs/icount.c b/lib/ext2fs/icount.c
index 766eccca..48665c7e 100644
--- a/lib/ext2fs/icount.c
+++ b/lib/ext2fs/icount.c
@@ -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));
--
2.37.3


2022-11-07 12:25:56

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 21/72] e2fsck: add -m option for multithread

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.

Signed-off-by: Li Xi <[email protected]>
Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
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

diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 252a17db..35428717 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -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
diff --git a/e2fsck/unix.c b/e2fsck/unix.c
index e5b672a2..1ee27f6a 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -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 */
diff --git a/tests/f_multithread/expect.1 b/tests/f_multithread/expect.1
new file mode 100644
index 00000000..e2b954d0
--- /dev/null
+++ b/tests/f_multithread/expect.1
@@ -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
diff --git a/tests/f_multithread/expect.2 b/tests/f_multithread/expect.2
new file mode 100644
index 00000000..a833aefc
--- /dev/null
+++ b/tests/f_multithread/expect.2
@@ -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
diff --git a/tests/f_multithread/image.gz b/tests/f_multithread/image.gz
new file mode 120000
index 00000000..0fd40018
--- /dev/null
+++ b/tests/f_multithread/image.gz
@@ -0,0 +1 @@
+../f_zero_super/image.gz
\ No newline at end of file
diff --git a/tests/f_multithread/name b/tests/f_multithread/name
new file mode 100644
index 00000000..df838ea6
--- /dev/null
+++ b/tests/f_multithread/name
@@ -0,0 +1 @@
+test "e2fsck -m" option
\ No newline at end of file
diff --git a/tests/f_multithread/script b/tests/f_multithread/script
new file mode 100644
index 00000000..0fe96cd0
--- /dev/null
+++ b/tests/f_multithread/script
@@ -0,0 +1,4 @@
+FSCK_OPT="-fy -m"
+SECOND_FSCK_OPT=-yf
+
+. $cmd_dir/run_e2fsck
diff --git a/tests/f_multithread_completion/expect.1 b/tests/f_multithread_completion/expect.1
new file mode 100644
index 00000000..61cac9bb
--- /dev/null
+++ b/tests/f_multithread_completion/expect.1
@@ -0,0 +1,2 @@
+../e2fsck/e2fsck: Only one of the options -C or -m may be specified.
+Exit status is 8
diff --git a/tests/f_multithread_completion/expect.2 b/tests/f_multithread_completion/expect.2
new file mode 100644
index 00000000..e2b954d0
--- /dev/null
+++ b/tests/f_multithread_completion/expect.2
@@ -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
diff --git a/tests/f_multithread_completion/image.gz b/tests/f_multithread_completion/image.gz
new file mode 120000
index 00000000..0fd40018
--- /dev/null
+++ b/tests/f_multithread_completion/image.gz
@@ -0,0 +1 @@
+../f_zero_super/image.gz
\ No newline at end of file
diff --git a/tests/f_multithread_completion/name b/tests/f_multithread_completion/name
new file mode 100644
index 00000000..a959045d
--- /dev/null
+++ b/tests/f_multithread_completion/name
@@ -0,0 +1 @@
+test "e2fsck -m" option conflicts with "-C"
\ No newline at end of file
diff --git a/tests/f_multithread_completion/script b/tests/f_multithread_completion/script
new file mode 100644
index 00000000..bf23cd61
--- /dev/null
+++ b/tests/f_multithread_completion/script
@@ -0,0 +1,4 @@
+FSCK_OPT="-fy -m -C 1"
+SECOND_FSCK_OPT=-yf
+
+. $cmd_dir/run_e2fsck
diff --git a/tests/f_multithread_no/expect.1 b/tests/f_multithread_no/expect.1
new file mode 100644
index 00000000..d14c4083
--- /dev/null
+++ b/tests/f_multithread_no/expect.1
@@ -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
diff --git a/tests/f_multithread_no/expect.2 b/tests/f_multithread_no/expect.2
new file mode 100644
index 00000000..e2b954d0
--- /dev/null
+++ b/tests/f_multithread_no/expect.2
@@ -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
diff --git a/tests/f_multithread_no/image.gz b/tests/f_multithread_no/image.gz
new file mode 120000
index 00000000..0fd40018
--- /dev/null
+++ b/tests/f_multithread_no/image.gz
@@ -0,0 +1 @@
+../f_zero_super/image.gz
\ No newline at end of file
diff --git a/tests/f_multithread_no/name b/tests/f_multithread_no/name
new file mode 100644
index 00000000..fa49692e
--- /dev/null
+++ b/tests/f_multithread_no/name
@@ -0,0 +1 @@
+test "e2fsck -m" option works with "-n"
\ No newline at end of file
diff --git a/tests/f_multithread_no/script b/tests/f_multithread_no/script
new file mode 100644
index 00000000..b93deb3a
--- /dev/null
+++ b/tests/f_multithread_no/script
@@ -0,0 +1,4 @@
+FSCK_OPT="-fn -m"
+SECOND_FSCK_OPT=-yf
+
+. $cmd_dir/run_e2fsck
diff --git a/tests/f_multithread_preen/expect.1 b/tests/f_multithread_preen/expect.1
new file mode 100644
index 00000000..b4b0cd9a
--- /dev/null
+++ b/tests/f_multithread_preen/expect.1
@@ -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
diff --git a/tests/f_multithread_preen/expect.2 b/tests/f_multithread_preen/expect.2
new file mode 100644
index 00000000..e2b954d0
--- /dev/null
+++ b/tests/f_multithread_preen/expect.2
@@ -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
diff --git a/tests/f_multithread_preen/image.gz b/tests/f_multithread_preen/image.gz
new file mode 120000
index 00000000..0fd40018
--- /dev/null
+++ b/tests/f_multithread_preen/image.gz
@@ -0,0 +1 @@
+../f_zero_super/image.gz
\ No newline at end of file
diff --git a/tests/f_multithread_preen/name b/tests/f_multithread_preen/name
new file mode 100644
index 00000000..90d199df
--- /dev/null
+++ b/tests/f_multithread_preen/name
@@ -0,0 +1 @@
+test "e2fsck -m" option works with "-p"
\ No newline at end of file
diff --git a/tests/f_multithread_preen/script b/tests/f_multithread_preen/script
new file mode 100644
index 00000000..ecb79cd6
--- /dev/null
+++ b/tests/f_multithread_preen/script
@@ -0,0 +1,4 @@
+FSCK_OPT="-fp -m"
+SECOND_FSCK_OPT=-yf
+
+. $cmd_dir/run_e2fsck
diff --git a/tests/f_multithread_yes/expect.1 b/tests/f_multithread_yes/expect.1
new file mode 100644
index 00000000..8b780ecf
--- /dev/null
+++ b/tests/f_multithread_yes/expect.1
@@ -0,0 +1,2 @@
+../e2fsck/e2fsck: The -m option should be used together with one of -p/-y/-n options.
+Exit status is 8
diff --git a/tests/f_multithread_yes/expect.2 b/tests/f_multithread_yes/expect.2
new file mode 100644
index 00000000..e2b954d0
--- /dev/null
+++ b/tests/f_multithread_yes/expect.2
@@ -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
diff --git a/tests/f_multithread_yes/image.gz b/tests/f_multithread_yes/image.gz
new file mode 120000
index 00000000..0fd40018
--- /dev/null
+++ b/tests/f_multithread_yes/image.gz
@@ -0,0 +1 @@
+../f_zero_super/image.gz
\ No newline at end of file
diff --git a/tests/f_multithread_yes/name b/tests/f_multithread_yes/name
new file mode 100644
index 00000000..3a703195
--- /dev/null
+++ b/tests/f_multithread_yes/name
@@ -0,0 +1 @@
+test "e2fsck -m" option works with "-y"
\ No newline at end of file
diff --git a/tests/f_multithread_yes/script b/tests/f_multithread_yes/script
new file mode 100644
index 00000000..38891f6a
--- /dev/null
+++ b/tests/f_multithread_yes/script
@@ -0,0 +1,4 @@
+FSCK_OPT="-f -m"
+SECOND_FSCK_OPT=-yf
+
+. $cmd_dir/run_e2fsck
--
2.37.3


2022-11-07 12:25:57

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 22/72] e2fsck: copy context when using multi-thread fsck

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.

Signed-off-by: Li Xi <[email protected]>
Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/e2fsck.h | 2 +
e2fsck/pass1.c | 117 ++++++++++++++++++++++++++++++++++++++++++++----
e2fsck/unix.c | 1 +
3 files changed, 112 insertions(+), 8 deletions(-)

diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 35428717..f1259728 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -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;
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 73909c39..972265b8 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -1151,7 +1151,22 @@ static int quota_inum_is_reserved(ext2_filsys fs, ext2_ino_t ino)
return 0;
}

-void e2fsck_pass1(e2fsck_t ctx)
+static int e2fsck_should_abort(e2fsck_t ctx)
+{
+ e2fsck_t global_ctx;
+
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ return 1;
+
+ if (ctx->global_ctx) {
+ global_ctx = ctx->global_ctx;
+ if (global_ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ return 1;
+ }
+ return 0;
+}
+
+void e2fsck_pass1_thread(e2fsck_t ctx)
{
int i;
__u64 max_sizes;
@@ -1381,7 +1396,7 @@ void e2fsck_pass1(e2fsck_t ctx)
if (ino > ino_threshold)
pass1_readahead(ctx, &ra_group, &ino_threshold);
ehandler_operation(old_op);
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ if (e2fsck_should_abort(ctx))
goto endit;
if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) {
/*
@@ -2008,7 +2023,7 @@ void e2fsck_pass1(e2fsck_t ctx)
if (process_inode_count >= ctx->process_inode_size) {
process_inodes(ctx, block_buf);

- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ if (e2fsck_should_abort(ctx))
goto endit;
}
}
@@ -2134,6 +2149,92 @@ endit:
else
ctx->invalid_bitmaps++;
}
+
+static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thread_ctx)
+{
+ errcode_t retval;
+ e2fsck_t thread_context;
+
+ retval = ext2fs_get_mem(sizeof(struct e2fsck_struct), &thread_context);
+ if (retval) {
+ com_err(global_ctx->program_name, retval, "while allocating memory");
+ return retval;
+ }
+ memcpy(thread_context, global_ctx, sizeof(struct e2fsck_struct));
+ thread_context->fs->priv_data = thread_context;
+ thread_context->global_ctx = global_ctx;
+
+ *thread_ctx = thread_context;
+ return 0;
+}
+
+static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
+{
+ int flags = global_ctx->flags;
+#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
+ /* 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;
+ ext2fs_free_mem(&thread_ctx);
+ return 0;
+}
+
+void e2fsck_pass1_multithread(e2fsck_t ctx)
+{
+ errcode_t retval;
+ e2fsck_t thread_ctx;
+
+ retval = e2fsck_pass1_thread_prepare(ctx, &thread_ctx);
+ if (retval) {
+ com_err(ctx->program_name, 0,
+ _("while preparing pass1 thread\n"));
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+
+#ifdef HAVE_SETJMP_H
+ /*
+ * When fatal_error() happens, jump to here. The thread
+ * context's flags will be saved, but its abort_loc will
+ * be overwritten by original jump buffer for the later
+ * tests.
+ */
+ if (setjmp(thread_ctx->abort_loc)) {
+ thread_ctx->flags &= ~E2F_FLAG_SETJMP_OK;
+ e2fsck_pass1_thread_join(ctx, thread_ctx);
+ return;
+ }
+ thread_ctx->flags |= E2F_FLAG_SETJMP_OK;
+#endif
+
+ e2fsck_pass1_thread(thread_ctx);
+ retval = e2fsck_pass1_thread_join(ctx, thread_ctx);
+ if (retval) {
+ com_err(ctx->program_name, 0,
+ _("while joining pass1 thread\n"));
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+}
+
+void e2fsck_pass1(e2fsck_t ctx)
+{
+ if (ctx->options & E2F_OPT_MULTITHREAD)
+ e2fsck_pass1_multithread(ctx);
+ else
+ e2fsck_pass1_thread(ctx);
+}
+
#undef FINISH_INODE_LOOP

/*
@@ -2196,7 +2297,7 @@ static void process_inodes(e2fsck_t ctx, char *block_buf)
ehandler_operation(buf);
check_blocks(ctx, &pctx, block_buf,
&inodes_to_process[i].ea_ibody_quota);
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ if (e2fsck_should_abort(ctx))
break;
}
ctx->stashed_inode = old_stashed_inode;
@@ -3424,7 +3525,7 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
inlinedata_fs = ext2fs_has_feature_inline_data(ctx->fs->super);

if (check_ext_attr(ctx, pctx, block_buf, &ea_block_quota)) {
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ if (e2fsck_should_abort(ctx))
goto out;
pb.num_blocks += EXT2FS_B2C(ctx->fs, ea_block_quota.blocks);
}
@@ -3479,7 +3580,7 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
}
end_problem_latch(ctx, PR_LATCH_BLOCK);
end_problem_latch(ctx, PR_LATCH_TOOBIG);
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ if (e2fsck_should_abort(ctx))
goto out;
if (pctx->errcode)
fix_problem(ctx, PR_1_BLOCK_ITERATE, pctx);
@@ -3961,7 +4062,7 @@ static int process_bad_block(ext2_filsys fs,
*block_nr = 0;
return BLOCK_CHANGED;
}
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ if (e2fsck_should_abort(ctx))
return BLOCK_ABORT;
} else
mark_block_used(ctx, blk);
@@ -4058,7 +4159,7 @@ static int process_bad_block(ext2_filsys fs,
*block_nr = 0;
return BLOCK_CHANGED;
}
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ if (e2fsck_should_abort(ctx))
return BLOCK_ABORT;
return 0;
}
diff --git a/e2fsck/unix.c b/e2fsck/unix.c
index 1ee27f6a..a77041b0 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -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;
--
2.37.3


2022-11-07 12:25:58

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 23/72] e2fsck: create logs for multi-threads

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.

Signed-off-by: Li Xi <[email protected]>
Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
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

diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index f1259728..284bc52e 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -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
diff --git a/e2fsck/logfile.c b/e2fsck/logfile.c
index 9d79eed2..83dbceff 100644
--- a/e2fsck/logfile.c
+++ b/e2fsck/logfile.c
@@ -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)
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 972265b8..2d4e62ca 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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;
}
diff --git a/tests/f_multithread_logfile/expect.1 b/tests/f_multithread_logfile/expect.1
new file mode 100644
index 00000000..e2b954d0
--- /dev/null
+++ b/tests/f_multithread_logfile/expect.1
@@ -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
diff --git a/tests/f_multithread_logfile/image.gz b/tests/f_multithread_logfile/image.gz
new file mode 120000
index 00000000..0fd40018
--- /dev/null
+++ b/tests/f_multithread_logfile/image.gz
@@ -0,0 +1 @@
+../f_zero_super/image.gz
\ No newline at end of file
diff --git a/tests/f_multithread_logfile/name b/tests/f_multithread_logfile/name
new file mode 100644
index 00000000..faaabc3b
--- /dev/null
+++ b/tests/f_multithread_logfile/name
@@ -0,0 +1 @@
+test "e2fsck -m" option works with "-E log_filename="
diff --git a/tests/f_multithread_logfile/script b/tests/f_multithread_logfile/script
new file mode 100644
index 00000000..4f9ca6f8
--- /dev/null
+++ b/tests/f_multithread_logfile/script
@@ -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
--
2.37.3


2022-11-07 12:26:08

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 25/72] e2fsck: Add e2fsck_pass1_thread_join return value

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

Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 596096d1..4b165600 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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);

--
2.37.3


2022-11-07 12:26:09

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 26/72] e2fsck: Use merge/clone apis of libext2fs

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.

Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 55 insertions(+), 3 deletions(-)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 4b165600..040c58ce 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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;
}

--
2.37.3


2022-11-07 12:26:19

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 27/72] e2fsck: Add e2fsck_pass1_merge_bitmap() api

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.

Signed-off-by: Li Xi <[email protected]>
Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 128 insertions(+)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 040c58ce..9a273515 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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) {
--
2.37.3


2022-11-07 12:26:24

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 24/72] e2fsck: configure one pfsck thread

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.

Signed-off-by: Li Xi <[email protected]>
Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
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(-)

diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 284bc52e..5bc24c3f 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -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;
diff --git a/e2fsck/logfile.c b/e2fsck/logfile.c
index 83dbceff..74781f80 100644
--- a/e2fsck/logfile.c
+++ b/e2fsck/logfile.c
@@ -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;
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 2d4e62ca..596096d1 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -47,6 +47,9 @@
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
+#ifdef HAVE_PTHREAD
+#include <pthread.h>
+#endif

#include "e2fsck.h"
#include <ext2fs/ext2_ext_attr.h>
@@ -1166,7 +1169,7 @@ static int e2fsck_should_abort(e2fsck_t ctx)
return 0;
}

-void e2fsck_pass1_thread(e2fsck_t ctx)
+void e2fsck_pass1_run(e2fsck_t ctx)
{
int i;
__u64 max_sizes;
@@ -2150,6 +2153,7 @@ endit:
ctx->invalid_bitmaps++;
}

+#ifdef HAVE_PTHREAD
static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thread_ctx)
{
errcode_t retval;
@@ -2201,18 +2205,38 @@ static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
return 0;
}

-void e2fsck_pass1_multithread(e2fsck_t ctx)
+static int e2fsck_pass1_threads_join(struct e2fsck_thread_info *infos,
+ int num_threads, e2fsck_t global_ctx)
{
- errcode_t retval;
- e2fsck_t thread_ctx;
+ errcode_t rc;
+ errcode_t ret = 0;
+ int i;
+ struct e2fsck_thread_info *pinfo;

- retval = e2fsck_pass1_thread_prepare(ctx, &thread_ctx);
- if (retval) {
- com_err(ctx->program_name, 0,
- _("while preparing pass1 thread\n"));
- ctx->flags |= E2F_FLAG_ABORT;
- return;
+ for (i = 0; i < num_threads; i++) {
+ pinfo = &infos[i];
+
+ if (!pinfo->eti_started)
+ continue;
+
+ rc = pthread_join(pinfo->eti_thread_id, NULL);
+ if (rc) {
+ com_err(global_ctx->program_name, rc,
+ _("while joining thread\n"));
+ if (ret == 0)
+ ret = rc;
+ }
+ e2fsck_pass1_thread_join(global_ctx, infos[i].eti_thread_ctx);
}
+ free(infos);
+
+ return ret;
+}
+
+static void *e2fsck_pass1_thread(void *arg)
+{
+ struct e2fsck_thread_info *info = arg;
+ e2fsck_t thread_ctx = info->eti_thread_ctx;

#ifdef HAVE_SETJMP_H
/*
@@ -2223,28 +2247,118 @@ void e2fsck_pass1_multithread(e2fsck_t ctx)
*/
if (setjmp(thread_ctx->abort_loc)) {
thread_ctx->flags &= ~E2F_FLAG_SETJMP_OK;
- e2fsck_pass1_thread_join(ctx, thread_ctx);
- return;
+ goto out;
}
thread_ctx->flags |= E2F_FLAG_SETJMP_OK;
#endif

- e2fsck_pass1_thread(thread_ctx);
- retval = e2fsck_pass1_thread_join(ctx, thread_ctx);
+ e2fsck_pass1_run(thread_ctx);
+
+out:
+ return NULL;
+}
+
+static int e2fsck_pass1_threads_start(struct e2fsck_thread_info **pinfo,
+ int num_threads, e2fsck_t global_ctx)
+{
+ struct e2fsck_thread_info *infos;
+ pthread_attr_t attr;
+ errcode_t retval;
+ errcode_t ret;
+ struct e2fsck_thread_info *tmp_pinfo;
+ int i;
+ e2fsck_t thread_ctx;
+
+ retval = pthread_attr_init(&attr);
if (retval) {
- com_err(ctx->program_name, 0,
- _("while joining pass1 thread\n"));
- ctx->flags |= E2F_FLAG_ABORT;
- return;
+ com_err(global_ctx->program_name, retval,
+ _("while setting pthread attribute\n"));
+ return retval;
+ }
+
+ infos = calloc(num_threads, sizeof(struct e2fsck_thread_info));
+ if (infos == NULL) {
+ retval = -ENOMEM;
+ com_err(global_ctx->program_name, retval,
+ _("while allocating memory for threads\n"));
+ pthread_attr_destroy(&attr);
+ return retval;
+ }
+
+ 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);
+ if (retval) {
+ com_err(global_ctx->program_name, retval,
+ _("while preparing pass1 thread\n"));
+ break;
+ }
+ tmp_pinfo->eti_thread_ctx = thread_ctx;
+
+ retval = pthread_create(&tmp_pinfo->eti_thread_id, &attr,
+ &e2fsck_pass1_thread, tmp_pinfo);
+ if (retval) {
+ com_err(global_ctx->program_name, retval,
+ _("while creating thread\n"));
+ e2fsck_pass1_thread_join(global_ctx, thread_ctx);
+ break;
+ }
+
+ tmp_pinfo->eti_started = 1;
+ }
+
+ /* destroy the thread attribute object, since it is no longer needed */
+ ret = pthread_attr_destroy(&attr);
+ if (ret) {
+ com_err(global_ctx->program_name, ret,
+ _("while destroying thread attribute\n"));
+ if (retval == 0)
+ retval = ret;
+ }
+
+ if (retval) {
+ e2fsck_pass1_threads_join(infos, num_threads, global_ctx);
+ return retval;
}
+ *pinfo = infos;
+ return 0;
}

+static void e2fsck_pass1_multithread(e2fsck_t global_ctx)
+{
+ struct e2fsck_thread_info *infos = NULL;
+ int num_threads = 1;
+ errcode_t retval;
+
+ retval = e2fsck_pass1_threads_start(&infos, num_threads, 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);
+ if (retval) {
+ com_err(global_ctx->program_name, retval,
+ _("while joining pass1 threads\n"));
+ goto out_abort;
+ }
+ return;
+out_abort:
+ global_ctx->flags |= E2F_FLAG_ABORT;
+ return;
+}
+#endif
+
void e2fsck_pass1(e2fsck_t ctx)
{
+#ifdef HAVE_PTHREAD
if (ctx->options & E2F_OPT_MULTITHREAD)
e2fsck_pass1_multithread(ctx);
else
- e2fsck_pass1_thread(ctx);
+#endif
+ e2fsck_pass1_run(ctx);
}

#undef FINISH_INODE_LOOP
diff --git a/e2fsck/unix.c b/e2fsck/unix.c
index a77041b0..af2457e3 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -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;

diff --git a/tests/test_one.in b/tests/test_one.in
index 78499ad0..a1d46c9c 100644
--- a/tests/test_one.in
+++ b/tests/test_one.in
@@ -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
--
2.37.3


2022-11-07 12:26:36

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 28/72] e2fsck: Add asserts in open_channel_fs

From: Li Xi <[email protected]>

Signed-off-by: Li Xi <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 12 ++++++++++++
1 file changed, 12 insertions(+)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 9a273515..ea432ff2 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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 */
--
2.37.3


2022-11-07 12:26:47

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 29/72] e2fsck: add start/end group for thread

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.

Signed-off-by: Li Xi <[email protected]>
Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
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(-)

diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 5bc24c3f..639f4e80 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -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
diff --git a/e2fsck/logfile.c b/e2fsck/logfile.c
index 74781f80..cc811d5a 100644
--- a/e2fsck/logfile.c
+++ b/e2fsck/logfile.c
@@ -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);
}
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index ea432ff2..3bb87669 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -1389,6 +1389,23 @@ void e2fsck_pass1_run(e2fsck_t ctx)
/* Set up ctx->lost_and_found if possible */
(void) e2fsck_get_lost_and_found(ctx, 0);

+#ifdef HAVE_PTHREAD
+ if (ctx->global_ctx) {
+ if (ctx->options & E2F_OPT_DEBUG &&
+ ctx->options & E2F_OPT_MULTITHREAD)
+ fprintf(stderr, "thread %d jumping to group %d\n",
+ ctx->thread_info.et_thread_index,
+ ctx->thread_info.et_group_start);
+ pctx.errcode = ext2fs_inode_scan_goto_blockgroup(scan,
+ ctx->thread_info.et_group_start);
+ if (pctx.errcode) {
+ fix_problem(ctx, PR_1_PASS_HEADER, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ goto endit;
+ }
+ }
+#endif
+
while (1) {
if (ino % (fs->super->s_inodes_per_group * 4) == 1) {
if (e2fsck_mmp_update(fs))
@@ -1432,6 +1449,8 @@ void e2fsck_pass1_run(e2fsck_t ctx)
ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino);
continue;
}
+ if (pctx.errcode == EXT2_ET_SCAN_FINISHED)
+ break;
if (pctx.errcode &&
pctx.errcode != EXT2_ET_INODE_CSUM_INVALID &&
pctx.errcode != EXT2_ET_INODE_IS_GARBAGE) {
@@ -2211,12 +2230,14 @@ static errcode_t e2fsck_open_channel_fs(ext2_filsys dest, e2fsck_t dest_context,
return 0;
}

-static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thread_ctx)
+static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thread_ctx,
+ int thread_index)
{
errcode_t retval;
e2fsck_t thread_context;
ext2_filsys thread_fs;
ext2_filsys global_fs = global_ctx->fs;
+ struct e2fsck_thread *tinfo;

assert(global_ctx->inode_used_map == NULL);
assert(global_ctx->inode_dir_map == NULL);
@@ -2252,8 +2273,15 @@ static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thre
goto out_fs;
}

- thread_context->thread_index = 0;
+ thread_context->thread_info.et_thread_index = thread_index;
set_up_logging(thread_context);
+
+ assert(thread_index == 0);
+ tinfo = &thread_context->thread_info;
+ tinfo->et_group_start = 0;
+ tinfo->et_group_next = 0;
+ tinfo->et_group_end = thread_fs->group_desc_count;
+
*thread_ctx = thread_context;
return 0;
out_fs:
@@ -2495,7 +2523,7 @@ 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);
+ retval = e2fsck_pass1_thread_prepare(global_ctx, &thread_ctx, i);
if (retval) {
com_err(global_ctx->program_name, retval,
_("while preparing pass1 thread\n"));
@@ -2580,6 +2608,7 @@ static errcode_t scan_callback(ext2_filsys fs,
{
struct scan_callback_struct *scan_struct;
e2fsck_t ctx;
+ struct e2fsck_thread *tinfo;

scan_struct = (struct scan_callback_struct *) priv_data;
ctx = scan_struct->ctx;
@@ -2591,6 +2620,15 @@ static errcode_t scan_callback(ext2_filsys fs,
ctx->fs->group_desc_count))
return EXT2_ET_CANCEL_REQUESTED;

+#ifdef HAVE_PTHREAD
+ if (ctx->global_ctx) {
+ tinfo = &ctx->thread_info;
+ tinfo->et_group_next++;
+ if (tinfo->et_group_next >= tinfo->et_group_end)
+ return EXT2_ET_SCAN_FINISHED;
+ }
+#endif
+
return 0;
}

diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index 6ad6fb84..d5452441 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -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 */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index b47b0c63..a6207428 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -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
*/
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index de140198..b8f45ae2 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -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
--
2.37.3


2022-11-07 12:26:52

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 30/72] e2fsck: split groups to different threads

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.

Signed-off-by: Li Xi <[email protected]>
Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 23 +++++++++++++++++------
1 file changed, 17 insertions(+), 6 deletions(-)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 3bb87669..3b411b70 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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"));
--
2.37.3


2022-11-07 12:26:52

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 32/72] e2fsck: do not change global variables

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.

Signed-off-by: Li Xi <[email protected]>
Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 75 +++++++++++++++++++++++++++++++-------------------
1 file changed, 47 insertions(+), 28 deletions(-)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index a2c13be5..d5c01dc7 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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
--
2.37.3


2022-11-07 12:26:57

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 34/72] e2fsck: merge dir_info after thread finishes

From: Li Xi <[email protected]>

dir_info need be merged after thread finish.

Signed-off-by: Li Xi <[email protected]>
Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/dirinfo.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++
e2fsck/e2fsck.h | 2 ++
e2fsck/pass1.c | 53 +++++++++++++++++++++++++++++++++++++-
3 files changed, 120 insertions(+), 1 deletion(-)

diff --git a/e2fsck/dirinfo.c b/e2fsck/dirinfo.c
index 5c360a90..ee8d8a69 100644
--- a/e2fsck/dirinfo.c
+++ b/e2fsck/dirinfo.c
@@ -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
diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index cdd158cc..2ee37f78 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -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);
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index d5c01dc7..57003d8c 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -2325,6 +2325,21 @@ out_context:
return retval;
}

+static void e2fsck_pass1_merge_dir_info(e2fsck_t global_ctx, e2fsck_t thread_ctx)
+{
+ if (thread_ctx->dir_info == NULL)
+ return;
+
+ if (global_ctx->dir_info == NULL) {
+ global_ctx->dir_info = thread_ctx->dir_info;
+ thread_ctx->dir_info = NULL;
+ return;
+ }
+
+ e2fsck_merge_dir_info(global_ctx, thread_ctx->dir_info,
+ global_ctx->dir_info);
+}
+
static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx)
{
errcode_t retval = 0;
@@ -2334,6 +2349,7 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
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;
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;
@@ -2366,6 +2382,8 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
global_ctx->block_dup_map = block_dup_map;
global_ctx->block_ea_map = block_ea_map;
global_ctx->block_metadata_map = block_metadata_map;
+ global_ctx->dir_info = dir_info;
+ e2fsck_pass1_merge_dir_info(global_ctx, thread_ctx);

/* Keep the global singal flags*/
global_ctx->flags |= (flags & E2F_FLAG_SIGNAL_MASK) |
@@ -2458,6 +2476,7 @@ static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
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);
+ e2fsck_free_dir_info(thread_ctx);

if (thread_ctx->logf)
fclose(thread_ctx->logf);
@@ -2628,11 +2647,43 @@ out_abort:
}
#endif

+/* TODO: tdb needs to be handled properly for multiple threads*/
+static int multiple_threads_supported(e2fsck_t ctx)
+{
+#ifdef CONFIG_TDB
+ unsigned int threshold;
+ ext2_ino_t num_dirs;
+ errcode_t retval;
+ char *tdb_dir;
+ int enable;
+
+ profile_get_string(ctx->profile, "scratch_files", "directory", 0, 0,
+ &tdb_dir);
+ profile_get_uint(ctx->profile, "scratch_files",
+ "numdirs_threshold", 0, 0, &threshold);
+ profile_get_boolean(ctx->profile, "scratch_files",
+ "icount", 0, 1, &enable);
+
+ retval = ext2fs_get_num_dirs(ctx->fs, &num_dirs);
+ if (retval)
+ num_dirs = 1024; /* Guess */
+
+ /* tdb is unsupported now */
+ if (enable && tdb_dir && !access(tdb_dir, W_OK) &&
+ (!threshold || num_dirs > threshold)) {
+ fprintf(stderr, "Fall through single thread for pass1 "
+ "because tdb could not handle properly\n");
+ return 0;
+ }
+ #endif
+ return 1;
+}
+
void e2fsck_pass1(e2fsck_t ctx)
{
init_ext2_max_sizes();
#ifdef HAVE_PTHREAD
- if (ctx->options & E2F_OPT_MULTITHREAD)
+ if (ctx->options & E2F_OPT_MULTITHREAD && multiple_threads_supported(ctx))
e2fsck_pass1_multithread(ctx);
else
#endif
--
2.37.3


2022-11-07 12:27:01

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 35/72] e2fsck: rbtree bitmap for dir

From: Wang Shilong <[email protected]>

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

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 1 +
1 file changed, 1 insertion(+)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 57003d8c..4d98c467 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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) {
--
2.37.3


2022-11-07 12:27:08

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 36/72] e2fsck: merge icounts after thread finishes

From: Li Xi <[email protected]>

Merge inode_count and inode_link_info properly after
threads finish.

Signed-off-by: Li Xi <[email protected]>
Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 48 insertions(+)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 4d98c467..18bf7efd 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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);
--
2.37.3


2022-11-07 12:27:18

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 37/72] e2fsck: add debug codes for multiple threads

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.

Signed-off-by: Li Xi <[email protected]>
Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/e2fsck.h | 16 ++++++++++++++++
e2fsck/pass1.c | 29 +++++++++++++++++++++++++++++
2 files changed, 45 insertions(+)

diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 2ee37f78..9b0f5067 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -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. */
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 18bf7efd..752dca03 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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) {
--
2.37.3


2022-11-07 12:27:29

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 38/72] e2fsck: merge counts after threads finish

From: Wang Shilong <[email protected]>

Merge counts properly.

Signed-off-by: Li Xi <[email protected]>
Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 752dca03..8b502307 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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) |
--
2.37.3


2022-11-07 12:27:31

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 39/72] e2fsck: merge dx_dir_info after threads finish

From: Wang Shilong <[email protected]>

Merge properly.

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/dx_dirinfo.c | 64 +++++++++++++++++++++++++++++++++++++++++++++
e2fsck/e2fsck.h | 1 +
e2fsck/pass1.c | 23 ++++++++++++++++
3 files changed, 88 insertions(+)

diff --git a/e2fsck/dx_dirinfo.c b/e2fsck/dx_dirinfo.c
index caca3e30..91954572 100644
--- a/e2fsck/dx_dirinfo.c
+++ b/e2fsck/dx_dirinfo.c
@@ -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.
diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 9b0f5067..26c3b8a5 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -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,
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 8b502307..f998590e 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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;
--
2.37.3


2022-11-07 12:27:35

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 40/72] e2fsck: merge dirs_to_hash when threads finish

From: Wang Shilong <[email protected]>

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

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index f998590e..a3f13402 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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);
--
2.37.3


2022-11-07 12:27:39

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 41/72] e2fsck: merge context flags properly

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

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index a3f13402..7e167189 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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;
--
2.37.3


2022-11-07 12:27:41

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 42/72] e2fsck: merge quota context after threads finish

From: Wang Shilong <[email protected]>

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

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 7e167189..213c1a51 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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);
--
2.37.3


2022-11-07 12:27:45

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 44/72] e2fsck: move some fixes out of parallel pthreads

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.

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
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(-)

diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 5356e172..55f75042 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -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);
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index c68e6957..f156d4e1 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -753,11 +753,15 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx,
if (i >= 4)
not_device++;

+ e2fsck_pass1_block_map_lock(ctx);
if (blk < ctx->fs->super->s_first_data_block ||
blk >= ext2fs_blocks_count(ctx->fs->super) ||
ext2fs_fast_test_block_bitmap2(ctx->block_found_map,
- blk))
+ blk)) {
+ e2fsck_pass1_block_map_unlock(ctx);
return; /* Invalid block, can't be dir */
+ }
+ e2fsck_pass1_block_map_unlock(ctx);
}
blk = inode->i_block[0];
}
@@ -891,19 +895,15 @@ static void reserve_block_for_root_repair(e2fsck_t ctx)
errcode_t err;
ext2_filsys fs = ctx->fs;

- e2fsck_pass1_fix_lock(ctx);
ctx->root_repair_block = 0;
if (ext2fs_test_inode_bitmap2(ctx->inode_used_map, EXT2_ROOT_INO))
- goto out;
+ return;

err = ext2fs_new_block2(fs, 0, ctx->block_found_map, &blk);
if (err)
- goto out;
+ return;
ext2fs_mark_block_bitmap2(ctx->block_found_map, blk);
ctx->root_repair_block = blk;
-out:
- e2fsck_pass1_fix_unlock(ctx);
- return;
}

static void reserve_block_for_lnf_repair(e2fsck_t ctx)
@@ -914,18 +914,15 @@ static void reserve_block_for_lnf_repair(e2fsck_t ctx)
static const char name[] = "lost+found";
ext2_ino_t ino;

- e2fsck_pass1_fix_lock(ctx);
ctx->lnf_repair_block = 0;
if (!ext2fs_lookup(fs, EXT2_ROOT_INO, name, sizeof(name)-1, 0, &ino))
- goto out;
+ return;

err = ext2fs_new_block2(fs, 0, ctx->block_found_map, &blk);
if (err)
- goto out;
+ return;
ext2fs_mark_block_bitmap2(ctx->block_found_map, blk);
ctx->lnf_repair_block = blk;
-out:
- e2fsck_pass1_fix_unlock(ctx);
return;
}

@@ -1182,6 +1179,118 @@ static int e2fsck_should_abort(e2fsck_t ctx)
return 0;
}

+/*
+ * We need call mark_table_blocks() before multiple
+ * thread start, since all known system blocks should be
+ * marked and checked later.
+ */
+static errcode_t e2fsck_pass1_prepare(e2fsck_t ctx)
+{
+ struct problem_context pctx;
+ ext2_filsys fs = ctx->fs;
+
+ clear_problem_context(&pctx);
+ if (!(ctx->options & E2F_OPT_PREEN))
+ fix_problem(ctx, PR_1_PASS_HEADER, &pctx);
+
+ pctx.errcode = e2fsck_allocate_subcluster_bitmap(ctx->fs,
+ _("in-use block map"), EXT2FS_BMAP64_RBTREE,
+ "block_found_map", &ctx->block_found_map);
+ if (pctx.errcode) {
+ pctx.num = 1;
+ fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return pctx.errcode;
+ }
+ pctx.errcode = e2fsck_allocate_block_bitmap(ctx->fs,
+ _("metadata block map"), EXT2FS_BMAP64_RBTREE,
+ "block_metadata_map", &ctx->block_metadata_map);
+ if (pctx.errcode) {
+ pctx.num = 1;
+ fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return pctx.errcode;
+ }
+
+ mark_table_blocks(ctx);
+ pctx.errcode = ext2fs_convert_subcluster_bitmap(ctx->fs,
+ &ctx->block_found_map);
+ if (pctx.errcode) {
+ fix_problem(ctx, PR_1_CONVERT_SUBCLUSTER, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return pctx.errcode;
+ }
+
+ if (ext2fs_has_feature_mmp(fs->super) &&
+ fs->super->s_mmp_block > fs->super->s_first_data_block &&
+ fs->super->s_mmp_block < ext2fs_blocks_count(fs->super))
+ ext2fs_mark_block_bitmap2(ctx->block_found_map,
+ fs->super->s_mmp_block);
+
+ return 0;
+}
+
+static void e2fsck_pass1_post(e2fsck_t ctx)
+{
+ struct problem_context pctx;
+ ext2_filsys fs = ctx->fs;
+ char *block_buf;
+
+ reserve_block_for_root_repair(ctx);
+ reserve_block_for_lnf_repair(ctx);
+
+ if (ctx->invalid_bitmaps)
+ handle_fs_bad_blocks(ctx);
+
+ if (ctx->flags & E2F_FLAG_RESIZE_INODE) {
+ struct ext2_inode *inode;
+ int inode_size = EXT2_INODE_SIZE(fs->super);
+ inode = e2fsck_allocate_memory(ctx, inode_size,
+ "scratch inode");
+
+ clear_problem_context(&pctx);
+ pctx.errcode = ext2fs_create_resize_inode(fs);
+ if (pctx.errcode) {
+ if (!fix_problem(ctx, PR_1_RESIZE_INODE_CREATE,
+ &pctx)) {
+ ctx->flags |= E2F_FLAG_ABORT;
+ ext2fs_free_mem(&inode);
+ ext2fs_free_mem(&block_buf);
+ return;
+ }
+ pctx.errcode = 0;
+ }
+ if (!pctx.errcode) {
+ e2fsck_read_inode(ctx, EXT2_RESIZE_INO, inode,
+ "recreate inode");
+ inode->i_mtime = ctx->now;
+ e2fsck_write_inode(ctx, EXT2_RESIZE_INO, inode,
+ "recreate inode");
+ }
+ ctx->flags &= ~E2F_FLAG_RESIZE_INODE;
+ ext2fs_free_mem(&inode);
+ }
+
+ if (ctx->flags & E2F_FLAG_RESTART) {
+ ext2fs_free_mem(&block_buf);
+ return;
+ }
+
+ if (ctx->block_dup_map) {
+ if (ctx->options & E2F_OPT_PREEN) {
+ clear_problem_context(&pctx);
+ fix_problem(ctx, PR_1_DUP_BLOCKS_PREENSTOP, &pctx);
+ }
+ block_buf =
+ (char *)e2fsck_allocate_memory(ctx,
+ ctx->fs->blocksize * 3,
+ "block interate buffer");
+ e2fsck_pass1_dupblocks(ctx, block_buf);
+ ext2fs_free_mem(&block_buf);
+ }
+}
+
+
void e2fsck_pass1_run(e2fsck_t ctx)
{
int i;
@@ -1220,9 +1329,6 @@ void e2fsck_pass1_run(e2fsck_t ctx)
ctx->readahead_kb = e2fsck_guess_readahead(ctx->fs);
pass1_readahead(ctx, &ra_group, &ino_threshold);

- if (!(ctx->options & E2F_OPT_PREEN))
- fix_problem(ctx, PR_1_PASS_HEADER, &pctx);
-
if (ext2fs_has_feature_dir_index(fs->super) &&
!(ctx->options & E2F_OPT_NO)) {
if (ext2fs_u32_list_create(&ctx->dirs_to_hash, 50))
@@ -1271,24 +1377,6 @@ void e2fsck_pass1_run(e2fsck_t ctx)
ctx->flags |= E2F_FLAG_ABORT;
return;
}
- pctx.errcode = e2fsck_allocate_subcluster_bitmap(fs,
- _("in-use block map"), EXT2FS_BMAP64_RBTREE,
- "block_found_map", &ctx->block_found_map);
- if (pctx.errcode) {
- pctx.num = 1;
- fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- pctx.errcode = e2fsck_allocate_block_bitmap(fs,
- _("metadata block map"), EXT2FS_BMAP64_RBTREE,
- "block_metadata_map", &ctx->block_metadata_map);
- if (pctx.errcode) {
- pctx.num = 1;
- fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
if (casefold_fs) {
pctx.errcode =
e2fsck_allocate_inode_bitmap(fs,
@@ -1344,14 +1432,6 @@ void e2fsck_pass1_run(e2fsck_t ctx)
}
}

- mark_table_blocks(ctx);
- pctx.errcode = ext2fs_convert_subcluster_bitmap(fs,
- &ctx->block_found_map);
- if (pctx.errcode) {
- fix_problem(ctx, PR_1_CONVERT_SUBCLUSTER, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- goto endit;
- }
block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 3,
"block interate buffer");
if (EXT2_INODE_SIZE(fs->super) == EXT2_GOOD_OLD_INODE_SIZE)
@@ -1385,12 +1465,6 @@ void e2fsck_pass1_run(e2fsck_t ctx)
fs->super->s_mkfs_time < fs->super->s_inodes_count))
low_dtime_check = 0;

- if (ext2fs_has_feature_mmp(fs->super) &&
- fs->super->s_mmp_block > fs->super->s_first_data_block &&
- fs->super->s_mmp_block < ext2fs_blocks_count(fs->super))
- ext2fs_mark_block_bitmap2(ctx->block_found_map,
- fs->super->s_mmp_block);
-
/* Set up ctx->lost_and_found if possible */
(void) e2fsck_get_lost_and_found(ctx, 0);

@@ -1721,8 +1795,10 @@ void e2fsck_pass1_run(e2fsck_t ctx)
failed_csum = 0;
}

+ e2fsck_pass1_block_map_lock(ctx);
pctx.errcode = ext2fs_copy_bitmap(ctx->block_found_map,
&pb.fs_meta_blocks);
+ e2fsck_pass1_block_map_unlock(ctx);
if (pctx.errcode) {
pctx.num = 4;
fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
@@ -2069,9 +2145,6 @@ void e2fsck_pass1_run(e2fsck_t ctx)
ext2fs_close_inode_scan(scan);
scan = NULL;

- reserve_block_for_root_repair(ctx);
- reserve_block_for_lnf_repair(ctx);
-
/*
* If any extended attribute blocks' reference counts need to
* be adjusted, either up (ctx->refcount_extra), or down
@@ -2099,11 +2172,6 @@ void e2fsck_pass1_run(e2fsck_t ctx)
ctx->ea_block_quota_inodes = 0;
}

- if (ctx->invalid_bitmaps) {
- e2fsck_pass1_fix_lock(ctx);
- handle_fs_bad_blocks(ctx);
- e2fsck_pass1_fix_unlock(ctx);
- }

/* We don't need the block_ea_map any more */
if (ctx->block_ea_map) {
@@ -2114,29 +2182,6 @@ void e2fsck_pass1_run(e2fsck_t ctx)
/* We don't need the encryption policy => ID map any more */
destroy_encryption_policy_map(ctx);

- if (ctx->flags & E2F_FLAG_RESIZE_INODE) {
- clear_problem_context(&pctx);
- e2fsck_pass1_fix_lock(ctx);
- pctx.errcode = ext2fs_create_resize_inode(fs);
- e2fsck_pass1_fix_unlock(ctx);
- if (pctx.errcode) {
- if (!fix_problem(ctx, PR_1_RESIZE_INODE_CREATE,
- &pctx)) {
- ctx->flags |= E2F_FLAG_ABORT;
- goto endit;
- }
- pctx.errcode = 0;
- }
- if (!pctx.errcode) {
- e2fsck_read_inode(ctx, EXT2_RESIZE_INO, inode,
- "recreate inode");
- inode->i_mtime = ctx->now;
- e2fsck_write_inode(ctx, EXT2_RESIZE_INO, inode,
- "recreate inode");
- }
- ctx->flags &= ~E2F_FLAG_RESIZE_INODE;
- }
-
if (ctx->flags & E2F_FLAG_RESTART) {
/*
* Only the master copy of the superblock and block
@@ -2161,13 +2206,6 @@ void e2fsck_pass1_run(e2fsck_t ctx)
}
}

- if (ctx->block_dup_map) {
- if (ctx->options & E2F_OPT_PREEN) {
- clear_problem_context(&pctx);
- fix_problem(ctx, PR_1_DUP_BLOCKS_PREENSTOP, &pctx);
- }
- e2fsck_pass1_dupblocks(ctx, block_buf);
- }
ctx->flags |= E2F_FLAG_ALLOC_OK;
endit:
e2fsck_use_inode_shortcuts(ctx, 0);
@@ -2288,10 +2326,10 @@ static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thre
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_found_map != NULL);
+ assert(global_ctx->block_metadata_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);
@@ -2455,10 +2493,8 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
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;
ext2_icount_t inode_count = global_ctx->inode_count;
ext2_icount_t inode_link_info = global_ctx->inode_link_info;
@@ -2501,10 +2537,8 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
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;
+ global_ctx->block_dup_map = block_dup_map;
global_ctx->dir_info = dir_info;
e2fsck_pass1_merge_dir_info(global_ctx, thread_ctx);
global_ctx->dx_dir_info = dx_dir_info;
@@ -2601,26 +2635,11 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
&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;
}
@@ -2637,10 +2656,7 @@ static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
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);
e2fsck_free_dir_info(thread_ctx);
ext2fs_free_icount(thread_ctx->inode_count);
ext2fs_free_icount(thread_ctx->inode_link_info);
@@ -2827,6 +2843,7 @@ static void e2fsck_pass1_multithread(e2fsck_t global_ctx)
errcode_t retval;

pthread_mutex_init(&global_ctx->fs_fix_mutex, NULL);
+ pthread_mutex_init(&global_ctx->fs_block_map_mutex, NULL);
retval = e2fsck_pass1_threads_start(&infos, num_threads, global_ctx);
if (retval) {
com_err(global_ctx->program_name, retval,
@@ -2881,13 +2898,19 @@ static int multiple_threads_supported(e2fsck_t ctx)

void e2fsck_pass1(e2fsck_t ctx)
{
+ errcode_t retval;
+
init_ext2_max_sizes();
+ retval = e2fsck_pass1_prepare(ctx);
+ if (retval)
+ return;
#ifdef HAVE_PTHREAD
if (ctx->options & E2F_OPT_MULTITHREAD && multiple_threads_supported(ctx))
e2fsck_pass1_multithread(ctx);
else
#endif
e2fsck_pass1_run(ctx);
+ e2fsck_pass1_post(ctx);
}

#undef FINISH_INODE_LOOP
@@ -3094,9 +3117,14 @@ static void alloc_imagic_map(e2fsck_t ctx)
* WARNING: Assumes checks have already been done to make sure block
* is valid. This is true in both process_block and process_bad_block.
*/
-static _INLINE_ void mark_block_used(e2fsck_t ctx, blk64_t block)
+static _INLINE_ void mark_block_used_unlocked(e2fsck_t ctx, blk64_t block)
{
- struct problem_context pctx;
+ struct problem_context pctx;
+ e2fsck_t global_ctx;
+
+ global_ctx = ctx->global_ctx;
+ if (!global_ctx)
+ global_ctx = ctx;

clear_problem_context(&pctx);

@@ -3105,11 +3133,15 @@ static _INLINE_ void mark_block_used(e2fsck_t ctx, blk64_t block)
!(ctx->options & E2F_OPT_UNSHARE_BLOCKS)) {
return;
}
- if (!ctx->block_dup_map) {
+ /**
+ * this should be safe because this operation has
+ * been serialized by mutex.
+ */
+ if (!global_ctx->block_dup_map) {
pctx.errcode = e2fsck_allocate_block_bitmap(ctx->fs,
_("multiply claimed block map"),
EXT2FS_BMAP64_RBTREE, "block_dup_map",
- &ctx->block_dup_map);
+ &global_ctx->block_dup_map);
if (pctx.errcode) {
pctx.num = 3;
fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR,
@@ -3119,12 +3151,20 @@ static _INLINE_ void mark_block_used(e2fsck_t ctx, blk64_t block)
return;
}
}
- ext2fs_fast_mark_block_bitmap2(ctx->block_dup_map, block);
+ ext2fs_fast_mark_block_bitmap2(global_ctx->block_dup_map, block);
} else {
ext2fs_fast_mark_block_bitmap2(ctx->block_found_map, block);
}
}

+static _INLINE_ void mark_block_used(e2fsck_t ctx, blk64_t block)
+{
+ e2fsck_pass1_block_map_lock(ctx);
+ mark_block_used_unlocked(ctx, block);
+ e2fsck_pass1_block_map_unlock(ctx);
+
+}
+
/*
* When cluster size is greater than one block, it is caller's responsibility
* to make sure block parameter starts at a cluster boundary.
@@ -3132,14 +3172,16 @@ static _INLINE_ void mark_block_used(e2fsck_t ctx, blk64_t block)
static _INLINE_ void mark_blocks_used(e2fsck_t ctx, blk64_t block,
unsigned int num)
{
- if (ext2fs_test_block_bitmap_range2(ctx->block_found_map, block, num))
+ e2fsck_pass1_block_map_lock(ctx);
+ if (ext2fs_test_block_bitmap_range2(ctx->block_found_map, block, num)) {
ext2fs_mark_block_bitmap_range2(ctx->block_found_map, block, num);
- else {
+ } else {
unsigned int i;

for (i = 0; i < num; i += EXT2FS_CLUSTER_RATIO(ctx->fs))
- mark_block_used(ctx, block + i);
+ mark_block_used_unlocked(ctx, block + i);
}
+ e2fsck_pass1_block_map_unlock(ctx);
}

static errcode_t _INLINE_ e2fsck_write_ext_attr3(e2fsck_t ctx, blk64_t block,
@@ -4747,10 +4789,12 @@ static int process_bad_block(ext2_filsys fs,
}

if (blockcnt < 0) {
+ e2fsck_pass1_block_map_lock(ctx);
if (ext2fs_test_block_bitmap2(p->fs_meta_blocks, blk)) {
p->bbcheck = 1;
if (fix_problem(ctx, PR_1_BB_FS_BLOCK, pctx)) {
*block_nr = 0;
+ e2fsck_pass1_block_map_unlock(ctx);
return BLOCK_CHANGED;
}
} else if (ext2fs_test_block_bitmap2(ctx->block_found_map,
@@ -4759,12 +4803,17 @@ static int process_bad_block(ext2_filsys fs,
if (fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK,
pctx)) {
*block_nr = 0;
+ e2fsck_pass1_block_map_unlock(ctx);
return BLOCK_CHANGED;
}
- if (e2fsck_should_abort(ctx))
+ if (e2fsck_should_abort(ctx)) {
+ e2fsck_pass1_block_map_unlock(ctx);
return BLOCK_ABORT;
- } else
- mark_block_used(ctx, blk);
+ }
+ } else {
+ mark_block_used_unlocked(ctx, blk);
+ }
+ e2fsck_pass1_block_map_unlock(ctx);
return 0;
}
#if 0
@@ -4777,10 +4826,13 @@ static int process_bad_block(ext2_filsys fs,
* there's an overlap between the filesystem table blocks
* (bitmaps and inode table) and the bad block list.
*/
+ e2fsck_pass1_block_map_lock(ctx);
if (!ext2fs_test_block_bitmap2(ctx->block_found_map, blk)) {
ext2fs_mark_block_bitmap2(ctx->block_found_map, blk);
+ e2fsck_pass1_block_map_unlock(ctx);
return 0;
}
+ e2fsck_pass1_block_map_unlock(ctx);
/*
* Try to find the where the filesystem block was used...
*/
@@ -4935,6 +4987,7 @@ static void new_table_block(e2fsck_t ctx, blk64_t first_block, dgrp_t group,
fix_problem(ctx, (old_block ? PR_1_RELOC_FROM_TO :
PR_1_RELOC_TO), &pctx);
pctx.blk2 = 0;
+ e2fsck_pass1_block_map_lock(ctx);
for (i = 0; i < num; i++) {
pctx.blk = i;
ext2fs_mark_block_bitmap2(ctx->block_found_map, (*new_block)+i);
@@ -4955,6 +5008,7 @@ static void new_table_block(e2fsck_t ctx, blk64_t first_block, dgrp_t group,
if (pctx.errcode)
fix_problem(ctx, PR_1_RELOC_WRITE_ERR, &pctx);
}
+ e2fsck_pass1_block_map_unlock(ctx);
ext2fs_free_mem(&buf);
}

diff --git a/e2fsck/util.c b/e2fsck/util.c
index 9470068f..93cd96c7 100644
--- a/e2fsck/util.c
+++ b/e2fsck/util.c
@@ -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

diff --git a/tests/f_multithread/expect.1 b/tests/f_multithread/expect.1
index 8d2acd2b..4db68d9e 100644
--- a/tests/f_multithread/expect.1
+++ b/tests/f_multithread/expect.1
@@ -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
diff --git a/tests/f_multithread_logfile/expect.1 b/tests/f_multithread_logfile/expect.1
index 8d2acd2b..4db68d9e 100644
--- a/tests/f_multithread_logfile/expect.1
+++ b/tests/f_multithread_logfile/expect.1
@@ -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
diff --git a/tests/f_multithread_no/expect.1 b/tests/f_multithread_no/expect.1
index f85a3382..eda2fcac 100644
--- a/tests/f_multithread_no/expect.1
+++ b/tests/f_multithread_no/expect.1
@@ -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
--
2.37.3


2022-11-07 12:27:46

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 43/72] e2fsck: serialize fix operations

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.

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/e2fsck.h | 6 +++++
e2fsck/pass1.c | 63 +++++++++++++++++++++++++++++++++++++++++++------
e2fsck/util.c | 38 +++++++++++++++++++++++++++++
3 files changed, 100 insertions(+), 7 deletions(-)

diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 26c3b8a5..5356e172 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -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);
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 213c1a51..c68e6957 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -380,8 +380,10 @@ static problem_t check_large_ea_inode(e2fsck_t ctx,
pctx->num = entry->e_value_inum;
if (fix_problem(ctx, PR_1_ATTR_SET_EA_INODE_FL, pctx)) {
inode.i_flags |= EXT4_EA_INODE_FL;
+ e2fsck_pass1_fix_lock(ctx);
ext2fs_write_inode(ctx->fs, entry->e_value_inum,
&inode);
+ e2fsck_pass1_fix_unlock(ctx);
} else {
return PR_1_ATTR_NO_EA_INODE_FL;
}
@@ -875,8 +877,11 @@ static errcode_t recheck_bad_inode_checksum(ext2_filsys fs, ext2_ino_t ino,
if (!fix_problem(ctx, PR_1_INODE_ONLY_CSUM_INVALID, pctx))
return 0;

+
+ e2fsck_pass1_fix_lock(ctx);
retval = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&inode,
sizeof(inode));
+ e2fsck_pass1_fix_unlock(ctx);
return retval;
}

@@ -886,15 +891,19 @@ static void reserve_block_for_root_repair(e2fsck_t ctx)
errcode_t err;
ext2_filsys fs = ctx->fs;

+ e2fsck_pass1_fix_lock(ctx);
ctx->root_repair_block = 0;
if (ext2fs_test_inode_bitmap2(ctx->inode_used_map, EXT2_ROOT_INO))
- return;
+ goto out;

err = ext2fs_new_block2(fs, 0, ctx->block_found_map, &blk);
if (err)
- return;
+ goto out;
ext2fs_mark_block_bitmap2(ctx->block_found_map, blk);
ctx->root_repair_block = blk;
+out:
+ e2fsck_pass1_fix_unlock(ctx);
+ return;
}

static void reserve_block_for_lnf_repair(e2fsck_t ctx)
@@ -905,15 +914,19 @@ static void reserve_block_for_lnf_repair(e2fsck_t ctx)
static const char name[] = "lost+found";
ext2_ino_t ino;

+ e2fsck_pass1_fix_lock(ctx);
ctx->lnf_repair_block = 0;
if (!ext2fs_lookup(fs, EXT2_ROOT_INO, name, sizeof(name)-1, 0, &ino))
- return;
+ goto out;

err = ext2fs_new_block2(fs, 0, ctx->block_found_map, &blk);
if (err)
- return;
+ goto out;
ext2fs_mark_block_bitmap2(ctx->block_found_map, blk);
ctx->lnf_repair_block = blk;
+out:
+ e2fsck_pass1_fix_unlock(ctx);
+ return;
}

static errcode_t get_inline_data_ea_size(ext2_filsys fs, ext2_ino_t ino,
@@ -1551,8 +1564,10 @@ void e2fsck_pass1_run(e2fsck_t ctx)
&size);
if (!pctx.errcode &&
fix_problem(ctx, PR_1_INLINE_DATA_FEATURE, &pctx)) {
+ e2fsck_pass1_fix_lock(ctx);
ext2fs_set_feature_inline_data(sb);
ext2fs_mark_super_dirty(fs);
+ e2fsck_pass1_fix_unlock(ctx);
inlinedata_fs = 1;
} else if (fix_problem(ctx, PR_1_INLINE_DATA_SET, &pctx)) {
e2fsck_clear_inode(ctx, ino, inode, 0, "pass1");
@@ -1640,9 +1655,11 @@ void e2fsck_pass1_run(e2fsck_t ctx)
if ((ext2fs_extent_header_verify(inode->i_block,
sizeof(inode->i_block)) == 0) &&
fix_problem(ctx, PR_1_EXTENT_FEATURE, &pctx)) {
+ e2fsck_pass1_fix_lock(ctx);
ext2fs_set_feature_extents(sb);
ext2fs_mark_super_dirty(fs);
extent_fs = 1;
+ e2fsck_pass1_fix_unlock(ctx);
} else if (fix_problem(ctx, PR_1_EXTENTS_SET, &pctx)) {
clear_inode:
e2fsck_clear_inode(ctx, ino, inode, 0, "pass1");
@@ -2082,8 +2099,11 @@ void e2fsck_pass1_run(e2fsck_t ctx)
ctx->ea_block_quota_inodes = 0;
}

- if (ctx->invalid_bitmaps)
+ if (ctx->invalid_bitmaps) {
+ e2fsck_pass1_fix_lock(ctx);
handle_fs_bad_blocks(ctx);
+ e2fsck_pass1_fix_unlock(ctx);
+ }

/* We don't need the block_ea_map any more */
if (ctx->block_ea_map) {
@@ -2096,7 +2116,9 @@ void e2fsck_pass1_run(e2fsck_t ctx)

if (ctx->flags & E2F_FLAG_RESIZE_INODE) {
clear_problem_context(&pctx);
+ e2fsck_pass1_fix_lock(ctx);
pctx.errcode = ext2fs_create_resize_inode(fs);
+ e2fsck_pass1_fix_unlock(ctx);
if (pctx.errcode) {
if (!fix_problem(ctx, PR_1_RESIZE_INODE_CREATE,
&pctx)) {
@@ -2804,6 +2826,7 @@ static void e2fsck_pass1_multithread(e2fsck_t global_ctx)
int num_threads = 1;
errcode_t retval;

+ pthread_mutex_init(&global_ctx->fs_fix_mutex, NULL);
retval = e2fsck_pass1_threads_start(&infos, num_threads, global_ctx);
if (retval) {
com_err(global_ctx->program_name, retval,
@@ -3119,6 +3142,18 @@ static _INLINE_ void mark_blocks_used(e2fsck_t ctx, blk64_t block,
}
}

+static errcode_t _INLINE_ e2fsck_write_ext_attr3(e2fsck_t ctx, blk64_t block,
+ void *inbuf, ext2_ino_t inum)
+{
+ errcode_t retval;
+ ext2_filsys fs = ctx->fs;
+
+ e2fsck_pass1_fix_lock(ctx);
+ retval = ext2fs_write_ext_attr3(fs, block, inbuf, inum);
+ e2fsck_pass1_fix_unlock(ctx);
+
+ return retval;
+}
/*
* Adjust the extended attribute block's reference counts at the end
* of pass 1, either by subtracting out references for EA blocks that
@@ -3155,7 +3190,7 @@ static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
pctx.num = should_be;
if (fix_problem(ctx, PR_1_EXTATTR_REFCOUNT, &pctx)) {
header->h_refcount = should_be;
- pctx.errcode = ext2fs_write_ext_attr3(fs, blk,
+ pctx.errcode = e2fsck_write_ext_attr3(ctx, blk,
block_buf,
pctx.ino);
if (pctx.errcode) {
@@ -3387,7 +3422,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
*/
if (failed_csum &&
fix_problem(ctx, PR_1_EA_BLOCK_ONLY_CSUM_INVALID, pctx)) {
- pctx->errcode = ext2fs_write_ext_attr3(fs, blk, block_buf,
+ pctx->errcode = e2fsck_write_ext_attr3(ctx, blk, block_buf,
pctx->ino);
if (pctx->errcode)
return 0;
@@ -3699,10 +3734,12 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
if (try_repairs && is_dir && problem == 0 &&
(extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) &&
fix_problem(ctx, PR_1_UNINIT_DBLOCK, pctx)) {
+ e2fsck_pass1_fix_lock(ctx);
extent.e_flags &= ~EXT2_EXTENT_FLAGS_UNINIT;
pb->inode_modified = 1;
pctx->errcode = ext2fs_extent_replace(ehandle, 0,
&extent);
+ e2fsck_pass1_fix_unlock(ctx);
if (pctx->errcode)
return;
failed_csum = 0;
@@ -3744,15 +3781,19 @@ report_problem:
}
continue;
}
+ e2fsck_pass1_fix_lock(ctx);
e2fsck_read_bitmaps(ctx);
pb->inode_modified = 1;
pctx->errcode =
ext2fs_extent_delete(ehandle, 0);
+ e2fsck_pass1_fix_unlock(ctx);
if (pctx->errcode) {
pctx->str = "ext2fs_extent_delete";
return;
}
+ e2fsck_pass1_fix_lock(ctx);
pctx->errcode = ext2fs_extent_fix_parents(ehandle);
+ e2fsck_pass1_fix_unlock(ctx);
if (pctx->errcode &&
pctx->errcode != EXT2_ET_NO_CURRENT_NODE) {
pctx->str = "ext2fs_extent_fix_parents";
@@ -3821,9 +3862,11 @@ report_problem:
pctx->num = e_info.curr_level - 1;
problem = PR_1_EXTENT_INDEX_START_INVALID;
if (fix_problem(ctx, problem, pctx)) {
+ e2fsck_pass1_fix_lock(ctx);
pb->inode_modified = 1;
pctx->errcode =
ext2fs_extent_fix_parents(ehandle);
+ e2fsck_pass1_fix_unlock(ctx);
if (pctx->errcode) {
pctx->str = "ext2fs_extent_fix_parents";
return;
@@ -3887,15 +3930,19 @@ report_problem:
pctx->blk = extent.e_lblk;
pctx->blk2 = new_lblk;
if (fix_problem(ctx, PR_1_COLLAPSE_DBLOCK, pctx)) {
+ e2fsck_pass1_fix_lock(ctx);
extent.e_lblk = new_lblk;
pb->inode_modified = 1;
pctx->errcode = ext2fs_extent_replace(ehandle,
0, &extent);
+ e2fsck_pass1_fix_unlock(ctx);
if (pctx->errcode) {
pctx->errcode = 0;
goto alloc_later;
}
+ e2fsck_pass1_fix_lock(ctx);
pctx->errcode = ext2fs_extent_fix_parents(ehandle);
+ e2fsck_pass1_fix_unlock(ctx);
if (pctx->errcode)
goto failed_add_dir_block;
pctx->errcode = ext2fs_extent_goto(ehandle,
@@ -3991,8 +4038,10 @@ alloc_later:
/* Failed csum but passes checks? Ask to fix checksum. */
if (failed_csum &&
fix_problem(ctx, PR_1_EXTENT_ONLY_CSUM_INVALID, pctx)) {
+ e2fsck_pass1_fix_lock(ctx);
pb->inode_modified = 1;
pctx->errcode = ext2fs_extent_replace(ehandle, 0, &extent);
+ e2fsck_pass1_fix_unlock(ctx);
if (pctx->errcode)
return;
}
diff --git a/e2fsck/util.c b/e2fsck/util.c
index 254b4d04..9470068f 100644
--- a/e2fsck/util.c
+++ b/e2fsck/util.c
@@ -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);
--
2.37.3


2022-11-07 12:27:55

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 45/72] e2fsck: split and merge invalid bitmaps

From: Wang Shilong <[email protected]>

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

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 71 insertions(+)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index f156d4e1..e268441a 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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];

--
2.37.3


2022-11-07 12:27:56

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 46/72] e2fsck: merge EA blocks properly

From: Wang Shilong <[email protected]>

EA blocks might be shared, merge them carefully.

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/e2fsck.h | 1 +
e2fsck/pass1.c | 244 +++++++++++++++++++++++++++++++++++++++++-------
2 files changed, 212 insertions(+), 33 deletions(-)

diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 55f75042..beac7054 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -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.
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index e268441a..06306a17 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -680,14 +680,14 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx,
LINUX_S_ISLNK(inode->i_mode) || inode->i_block[0] == 0)
return;

- /*
+ /*
* Check the block numbers in the i_block array for validity:
* zero blocks are skipped (but the first one cannot be zero -
* see above), other blocks are checked against the first and
* max data blocks (from the the superblock) and against the
* block bitmap. Any invalid block found means this cannot be
* a directory.
- *
+ *
* If there are non-zero blocks past the fourth entry, then
* this cannot be a device file: we remember that for the next
* check.
@@ -1234,14 +1234,39 @@ 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,
+ "block interate buffer");
reserve_block_for_root_repair(ctx);
reserve_block_for_lnf_repair(ctx);

+ /*
+ * If any extended attribute blocks' reference counts need to
+ * be adjusted, either up (ctx->refcount_extra), or down
+ * (ctx->refcount), then fix them.
+ */
+ if (ctx->refcount) {
+ adjust_extattr_refcount(ctx, ctx->refcount, block_buf, -1);
+ ea_refcount_free(ctx->refcount);
+ ctx->refcount = 0;
+ }
+ if (ctx->refcount_extra) {
+ adjust_extattr_refcount(ctx, ctx->refcount_extra,
+ block_buf, +1);
+ ea_refcount_free(ctx->refcount_extra);
+ ctx->refcount_extra = 0;
+ }
+
if (ctx->invalid_bitmaps)
handle_fs_bad_blocks(ctx);

+ /* We don't need the block_ea_map any more */
+ if (ctx->block_ea_map) {
+ ext2fs_free_block_bitmap(ctx->block_ea_map);
+ ctx->block_ea_map = 0;
+ }
+
if (ctx->flags & E2F_FLAG_RESIZE_INODE) {
struct ext2_inode *inode;
int inode_size = EXT2_INODE_SIZE(fs->super);
@@ -1281,10 +1306,6 @@ static void e2fsck_pass1_post(e2fsck_t ctx)
clear_problem_context(&pctx);
fix_problem(ctx, PR_1_DUP_BLOCKS_PREENSTOP, &pctx);
}
- block_buf =
- (char *)e2fsck_allocate_memory(ctx,
- ctx->fs->blocksize * 3,
- "block interate buffer");
e2fsck_pass1_dupblocks(ctx, block_buf);
ext2fs_free_mem(&block_buf);
}
@@ -2145,23 +2166,6 @@ void e2fsck_pass1_run(e2fsck_t ctx)
ext2fs_close_inode_scan(scan);
scan = NULL;

- /*
- * If any extended attribute blocks' reference counts need to
- * be adjusted, either up (ctx->refcount_extra), or down
- * (ctx->refcount), then fix them.
- */
- if (ctx->refcount) {
- adjust_extattr_refcount(ctx, ctx->refcount, block_buf, -1);
- ea_refcount_free(ctx->refcount);
- ctx->refcount = 0;
- }
- if (ctx->refcount_extra) {
- adjust_extattr_refcount(ctx, ctx->refcount_extra,
- block_buf, +1);
- ea_refcount_free(ctx->refcount_extra);
- ctx->refcount_extra = 0;
- }
-
if (ctx->ea_block_quota_blocks) {
ea_refcount_free(ctx->ea_block_quota_blocks);
ctx->ea_block_quota_blocks = 0;
@@ -2172,13 +2176,6 @@ void e2fsck_pass1_run(e2fsck_t ctx)
ctx->ea_block_quota_inodes = 0;
}

-
- /* We don't need the block_ea_map any more */
- if (ctx->block_ea_map) {
- ext2fs_free_block_bitmap(ctx->block_ea_map);
- ctx->block_ea_map = 0;
- }
-
/* We don't need the encryption policy => ID map any more */
destroy_encryption_policy_map(ctx);

@@ -2533,6 +2530,155 @@ static errcode_t e2fsck_pass1_merge_dirs_to_hash(e2fsck_t global_ctx,
return retval;
}

+static errcode_t e2fsck_pass1_merge_ea_inode_refs(e2fsck_t global_ctx,
+ e2fsck_t thread_ctx)
+{
+ ea_value_t count;
+ blk64_t blk;
+ errcode_t retval;
+
+ if (!thread_ctx->ea_inode_refs)
+ return 0;
+
+ if (!global_ctx->ea_inode_refs) {
+ global_ctx->ea_inode_refs = thread_ctx->ea_inode_refs;
+ thread_ctx->ea_inode_refs = NULL;
+ return 0;
+ }
+
+ ea_refcount_intr_begin(thread_ctx->ea_inode_refs);
+ while (1) {
+ if ((blk = ea_refcount_intr_next(thread_ctx->ea_inode_refs,
+ &count)) == 0)
+ break;
+ if (!global_ctx->block_ea_map ||
+ !ext2fs_fast_test_block_bitmap2(global_ctx->block_ea_map,
+ blk)) {
+ retval = ea_refcount_store(global_ctx->ea_inode_refs,
+ blk, count);
+ if (retval)
+ return retval;
+ }
+ }
+
+ return retval;
+}
+
+static ea_value_t ea_refcount_usage(e2fsck_t ctx, blk64_t blk,
+ ea_value_t *orig)
+{
+ ea_value_t count_cur;
+ ea_value_t count_extra = 0;
+ ea_value_t count_orig;
+
+ ea_refcount_fetch(ctx->refcount_orig, blk, &count_orig);
+ ea_refcount_fetch(ctx->refcount, blk, &count_cur);
+ /* most of time this is not needed */
+ if (ctx->refcount_extra && count_cur == 0)
+ ea_refcount_fetch(ctx->refcount_extra, blk, &count_extra);
+
+ if (!count_orig)
+ count_orig = *orig;
+ else if (orig)
+ *orig = count_orig;
+
+ return count_orig + count_extra - count_cur;
+}
+
+static errcode_t e2fsck_pass1_merge_ea_refcount(e2fsck_t global_ctx,
+ e2fsck_t thread_ctx)
+{
+ ea_value_t count;
+ blk64_t blk;
+ errcode_t retval = 0;
+
+ if (!thread_ctx->refcount)
+ return 0;
+
+ if (!global_ctx->refcount) {
+ global_ctx->refcount = thread_ctx->refcount;
+ thread_ctx->refcount = NULL;
+ global_ctx->refcount_extra = thread_ctx->refcount;
+ thread_ctx->refcount_extra = NULL;
+ return 0;
+ }
+
+ ea_refcount_intr_begin(thread_ctx->refcount);
+ while (1) {
+ if ((blk = ea_refcount_intr_next(thread_ctx->refcount,
+ &count)) == 0)
+ break;
+ /**
+ * this EA has never seen before, so just store its
+ * refcount and refcount_extra into global_ctx if needed.
+ */
+ if (!global_ctx->block_ea_map ||
+ !ext2fs_fast_test_block_bitmap2(global_ctx->block_ea_map,
+ blk)) {
+ ea_value_t extra;
+
+ retval = ea_refcount_store(global_ctx->refcount,
+ blk, count);
+ if (retval)
+ return retval;
+
+ if (count > 0 || !thread_ctx->refcount_extra)
+ continue;
+ ea_refcount_fetch(thread_ctx->refcount_extra, blk,
+ &extra);
+ if (extra == 0)
+ continue;
+
+ if (!global_ctx->refcount_extra) {
+ retval = ea_refcount_create(0,
+ &global_ctx->refcount_extra);
+ if (retval)
+ return retval;
+ }
+ retval = ea_refcount_store(global_ctx->refcount_extra,
+ blk, extra);
+ if (retval)
+ return retval;
+ } else {
+ ea_value_t orig;
+ ea_value_t thread_usage;
+ ea_value_t global_usage;
+ ea_value_t new;
+
+ thread_usage = ea_refcount_usage(thread_ctx,
+ blk, &orig);
+ global_usage = ea_refcount_usage(global_ctx,
+ blk, &orig);
+ if (thread_usage + global_usage <= orig) {
+ new = orig - thread_usage - global_usage;
+ retval = ea_refcount_store(global_ctx->refcount,
+ blk, new);
+ if (retval)
+ return retval;
+ continue;
+ }
+ /* update it is as zero */
+ retval = ea_refcount_store(global_ctx->refcount,
+ blk, 0);
+ if (retval)
+ return retval;
+ /* Ooops, this EA was referenced more than it stated */
+ if (!global_ctx->refcount_extra) {
+ retval = ea_refcount_create(0,
+ &global_ctx->refcount_extra);
+ if (retval)
+ return retval;
+ }
+ new = global_usage + thread_usage - orig;
+ retval = ea_refcount_store(global_ctx->refcount_extra,
+ blk, new);
+ if (retval)
+ return retval;
+ }
+ }
+
+ return retval;
+}

static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx)
{
@@ -2551,7 +2697,6 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
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_dup_map = global_ctx->block_dup_map;
- ext2fs_block_bitmap block_ea_map = global_ctx->block_ea_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;
@@ -2580,6 +2725,13 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
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;

#ifdef HAVE_SETJMP_H
jmp_buf old_jmp;
@@ -2598,7 +2750,6 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
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_ea_map = block_ea_map;
global_ctx->block_dup_map = block_dup_map;
global_ctx->dir_info = dir_info;
e2fsck_pass1_merge_dir_info(global_ctx, thread_ctx);
@@ -2608,6 +2759,13 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
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;
@@ -2654,6 +2812,8 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
return retval;
}

+ 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);
@@ -2723,6 +2883,14 @@ static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
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_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);
@@ -3370,6 +3538,15 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,

/* Create the EA refcount structure if necessary */
if (!ctx->refcount) {
+ pctx->errcode = ea_refcount_create(0,
+ &ctx->refcount_orig);
+ if (pctx->errcode) {
+ pctx->num = 1;
+ fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return 0;
+ }
+
pctx->errcode = ea_refcount_create(0, &ctx->refcount);
if (pctx->errcode) {
pctx->num = 1;
@@ -3575,6 +3752,7 @@ refcount_fail:

inc_ea_inode_refs(ctx, pctx, first, end);
ea_refcount_store(ctx->refcount, blk, header->h_refcount - 1);
+ ea_refcount_store(ctx->refcount_orig, blk, header->h_refcount);
mark_block_used(ctx, blk);
ext2fs_fast_mark_block_bitmap2(ctx->block_ea_map, blk);
return 1;
--
2.37.3


2022-11-07 12:28:04

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 47/72] e2fsck: kickoff mutex lock for block found map

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.

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
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(-)

diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index beac7054..23e8d2ed 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -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);
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 06306a17..fdd1f3d6 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -650,6 +650,31 @@ static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx,

}

+static _INLINE_ int is_blocks_used(e2fsck_t ctx, blk64_t block,
+ unsigned int num)
+{
+ int retval;
+
+ /* used to avoid duplicate output from below */
+ retval = ext2fs_test_block_bitmap_range2_valid(ctx->block_found_map,
+ block, num);
+ if (!retval)
+ return 0;
+
+ retval = ext2fs_test_block_bitmap_range2(ctx->block_found_map, block, num);
+ if (retval) {
+ e2fsck_pass1_block_map_r_lock(ctx);
+ if (ctx->global_ctx)
+ retval = ext2fs_test_block_bitmap_range2(
+ ctx->global_ctx->block_found_map, block, num);
+ e2fsck_pass1_block_map_r_unlock(ctx);
+ if (retval)
+ return 0;
+ }
+
+ return 1;
+}
+
/*
* Check to see if the inode might really be a directory, despite i_mode
*
@@ -753,15 +778,10 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx,
if (i >= 4)
not_device++;

- e2fsck_pass1_block_map_lock(ctx);
if (blk < ctx->fs->super->s_first_data_block ||
blk >= ext2fs_blocks_count(ctx->fs->super) ||
- ext2fs_fast_test_block_bitmap2(ctx->block_found_map,
- blk)) {
- e2fsck_pass1_block_map_unlock(ctx);
+ is_blocks_used(ctx, blk, 1))
return; /* Invalid block, can't be dir */
- }
- e2fsck_pass1_block_map_unlock(ctx);
}
blk = inode->i_block[0];
}
@@ -1221,11 +1241,28 @@ static errcode_t e2fsck_pass1_prepare(e2fsck_t ctx)
return pctx.errcode;
}

+ pctx.errcode = e2fsck_allocate_block_bitmap(ctx->fs,
+ _("multiply claimed block map"),
+ EXT2FS_BMAP64_RBTREE, "block_dup_map",
+ &ctx->block_dup_map);
+ if (pctx.errcode) {
+ pctx.num = 3;
+ fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR,
+ &pctx);
+ /* Should never get here */
+ ctx->flags |= E2F_FLAG_ABORT;
+ return pctx.errcode;
+ }
+
if (ext2fs_has_feature_mmp(fs->super) &&
fs->super->s_mmp_block > fs->super->s_first_data_block &&
fs->super->s_mmp_block < ext2fs_blocks_count(fs->super))
ext2fs_mark_block_bitmap2(ctx->block_found_map,
fs->super->s_mmp_block);
+#ifdef HAVE_PTHREAD
+ pthread_mutex_init(&ctx->fs_fix_mutex, NULL);
+ pthread_rwlock_init(&ctx->fs_block_map_rwlock, NULL);
+#endif

return 0;
}
@@ -1302,12 +1339,17 @@ static void e2fsck_pass1_post(e2fsck_t ctx)
}

if (ctx->block_dup_map) {
+ if (!(ctx->flags & E2F_FLAG_DUP_BLOCK)) {
+ ext2fs_free_mem(&block_buf);
+ return;
+ }
if (ctx->options & E2F_OPT_PREEN) {
clear_problem_context(&pctx);
fix_problem(ctx, PR_1_DUP_BLOCKS_PREENSTOP, &pctx);
}
e2fsck_pass1_dupblocks(ctx, block_buf);
ext2fs_free_mem(&block_buf);
+ ctx->flags &= ~E2F_FLAG_DUP_BLOCK;
}
}

@@ -1816,10 +1858,11 @@ void e2fsck_pass1_run(e2fsck_t ctx)
failed_csum = 0;
}

- e2fsck_pass1_block_map_lock(ctx);
- pctx.errcode = ext2fs_copy_bitmap(ctx->block_found_map,
- &pb.fs_meta_blocks);
- e2fsck_pass1_block_map_unlock(ctx);
+ e2fsck_pass1_block_map_r_lock(ctx);
+ pctx.errcode = ext2fs_copy_bitmap(ctx->global_ctx ?
+ ctx->global_ctx->block_found_map :
+ ctx->block_found_map, &pb.fs_meta_blocks);
+ e2fsck_pass1_block_map_r_unlock(ctx);
if (pctx.errcode) {
pctx.num = 4;
fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
@@ -2381,7 +2424,7 @@ static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thre

assert(global_ctx->block_found_map != NULL);
assert(global_ctx->block_metadata_map != NULL);
- assert(global_ctx->block_dup_map == NULL);
+ assert(global_ctx->block_dup_map != NULL);
assert(global_ctx->block_ea_map == NULL);
assert(global_ctx->fs->dblist == NULL);

@@ -2391,6 +2434,14 @@ 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->block_dup_map = NULL;
+
+ retval = e2fsck_allocate_block_bitmap(global_ctx->fs,
+ _("in-use block map"), EXT2FS_BMAP64_RBTREE,
+ "block_found_map", &thread_context->block_found_map);
+ if (retval)
+ goto out_context;
+
thread_context->global_ctx = global_ctx;
retval = ext2fs_clone_fs(global_fs, &thread_fs,
EXT2FS_CLONE_BLOCK | EXT2FS_CLONE_INODE |
@@ -2442,6 +2493,8 @@ static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thre
out_fs:
ext2fs_merge_fs(&thread_fs);
out_context:
+ if (thread_context->block_found_map)
+ ext2fs_free_mem(&thread_context->block_found_map);
ext2fs_free_mem(&thread_context);
return retval;
}
@@ -2696,7 +2749,6 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
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_dup_map = global_ctx->block_dup_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;
@@ -2732,6 +2784,8 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
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;

#ifdef HAVE_SETJMP_H
jmp_buf old_jmp;
@@ -2751,6 +2805,7 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
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;
@@ -2867,6 +2922,23 @@ static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx
if (retval)
return retval;

+ if (ext2fs_has_feature_shared_blocks(global_fs->super) &&
+ !(global_ctx->options & E2F_OPT_UNSHARE_BLOCKS))
+ return 0;
+ /*
+ * This need be done after merging block_ea_map
+ * because ea block might be shared, we need exclude
+ * them from dup blocks.
+ */
+ e2fsck_pass1_block_map_w_lock(thread_ctx);
+ retval = ext2fs_merge_bitmap(thread_ctx->block_found_map,
+ global_ctx->block_found_map,
+ global_ctx->block_dup_map,
+ global_ctx->block_ea_map);
+ e2fsck_pass1_block_map_w_unlock(thread_ctx);
+ if (retval == EEXIST)
+ global_ctx->flags |= E2F_FLAG_DUP_BLOCK;
+
return retval;
}

@@ -2882,6 +2954,7 @@ static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
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);
@@ -3081,8 +3154,6 @@ static void e2fsck_pass1_multithread(e2fsck_t global_ctx)
int num_threads = 1;
errcode_t retval;

- pthread_mutex_init(&global_ctx->fs_fix_mutex, NULL);
- pthread_mutex_init(&global_ctx->fs_block_map_mutex, NULL);
retval = e2fsck_pass1_threads_start(&infos, num_threads, global_ctx);
if (retval) {
com_err(global_ctx->program_name, retval,
@@ -3356,54 +3427,27 @@ static void alloc_imagic_map(e2fsck_t ctx)
* WARNING: Assumes checks have already been done to make sure block
* is valid. This is true in both process_block and process_bad_block.
*/
-static _INLINE_ void mark_block_used_unlocked(e2fsck_t ctx, blk64_t block)
+static _INLINE_ void mark_block_used(e2fsck_t ctx, blk64_t block)
{
struct problem_context pctx;
- e2fsck_t global_ctx;
-
- global_ctx = ctx->global_ctx;
- if (!global_ctx)
- global_ctx = ctx;
+ e2fsck_t global_ctx = ctx->global_ctx ? ctx->global_ctx : ctx;

clear_problem_context(&pctx);

- if (ext2fs_fast_test_block_bitmap2(ctx->block_found_map, block)) {
+ if (is_blocks_used(ctx, block, 1)) {
if (ext2fs_has_feature_shared_blocks(ctx->fs->super) &&
!(ctx->options & E2F_OPT_UNSHARE_BLOCKS)) {
return;
}
- /**
- * this should be safe because this operation has
- * been serialized by mutex.
- */
- if (!global_ctx->block_dup_map) {
- pctx.errcode = e2fsck_allocate_block_bitmap(ctx->fs,
- _("multiply claimed block map"),
- EXT2FS_BMAP64_RBTREE, "block_dup_map",
- &global_ctx->block_dup_map);
- if (pctx.errcode) {
- pctx.num = 3;
- fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR,
- &pctx);
- /* Should never get here */
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- }
+ ctx->flags |= E2F_FLAG_DUP_BLOCK;
+ e2fsck_pass1_block_map_w_lock(ctx);
ext2fs_fast_mark_block_bitmap2(global_ctx->block_dup_map, block);
+ e2fsck_pass1_block_map_w_unlock(ctx);
} else {
ext2fs_fast_mark_block_bitmap2(ctx->block_found_map, block);
}
}

-static _INLINE_ void mark_block_used(e2fsck_t ctx, blk64_t block)
-{
- e2fsck_pass1_block_map_lock(ctx);
- mark_block_used_unlocked(ctx, block);
- e2fsck_pass1_block_map_unlock(ctx);
-
-}
-
/*
* When cluster size is greater than one block, it is caller's responsibility
* to make sure block parameter starts at a cluster boundary.
@@ -3411,16 +3455,14 @@ static _INLINE_ void mark_block_used(e2fsck_t ctx, blk64_t block)
static _INLINE_ void mark_blocks_used(e2fsck_t ctx, blk64_t block,
unsigned int num)
{
- e2fsck_pass1_block_map_lock(ctx);
- if (ext2fs_test_block_bitmap_range2(ctx->block_found_map, block, num)) {
+ if (!is_blocks_used(ctx, block, num)) {
ext2fs_mark_block_bitmap_range2(ctx->block_found_map, block, num);
} else {
unsigned int i;

for (i = 0; i < num; i += EXT2FS_CLUSTER_RATIO(ctx->fs))
- mark_block_used_unlocked(ctx, block + i);
+ mark_block_used(ctx, block + i);
}
- e2fsck_pass1_block_map_unlock(ctx);
}

static errcode_t _INLINE_ e2fsck_write_ext_attr3(e2fsck_t ctx, blk64_t block,
@@ -3753,7 +3795,12 @@ refcount_fail:
inc_ea_inode_refs(ctx, pctx, first, end);
ea_refcount_store(ctx->refcount, blk, header->h_refcount - 1);
ea_refcount_store(ctx->refcount_orig, blk, header->h_refcount);
- mark_block_used(ctx, blk);
+ /**
+ * It might be racy that this block has been merged in the
+ * global found map.
+ */
+ if (!is_blocks_used(ctx, blk, 1))
+ ext2fs_fast_mark_block_bitmap2(ctx->block_found_map, blk);
ext2fs_fast_mark_block_bitmap2(ctx->block_ea_map, blk);
return 1;

@@ -5038,31 +5085,24 @@ static int process_bad_block(ext2_filsys fs,
}

if (blockcnt < 0) {
- e2fsck_pass1_block_map_lock(ctx);
if (ext2fs_test_block_bitmap2(p->fs_meta_blocks, blk)) {
p->bbcheck = 1;
if (fix_problem(ctx, PR_1_BB_FS_BLOCK, pctx)) {
*block_nr = 0;
- e2fsck_pass1_block_map_unlock(ctx);
return BLOCK_CHANGED;
}
- } else if (ext2fs_test_block_bitmap2(ctx->block_found_map,
- blk)) {
+ } else if (is_blocks_used(ctx, blk, 1)) {
p->bbcheck = 1;
if (fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK,
pctx)) {
*block_nr = 0;
- e2fsck_pass1_block_map_unlock(ctx);
return BLOCK_CHANGED;
}
- if (e2fsck_should_abort(ctx)) {
- e2fsck_pass1_block_map_unlock(ctx);
+ if (e2fsck_should_abort(ctx))
return BLOCK_ABORT;
- }
} else {
- mark_block_used_unlocked(ctx, blk);
+ mark_block_used(ctx, blk);
}
- e2fsck_pass1_block_map_unlock(ctx);
return 0;
}
#if 0
@@ -5075,13 +5115,10 @@ static int process_bad_block(ext2_filsys fs,
* there's an overlap between the filesystem table blocks
* (bitmaps and inode table) and the bad block list.
*/
- e2fsck_pass1_block_map_lock(ctx);
- if (!ext2fs_test_block_bitmap2(ctx->block_found_map, blk)) {
+ if (!is_blocks_used(ctx, blk, 1)) {
ext2fs_mark_block_bitmap2(ctx->block_found_map, blk);
- e2fsck_pass1_block_map_unlock(ctx);
return 0;
}
- e2fsck_pass1_block_map_unlock(ctx);
/*
* Try to find the where the filesystem block was used...
*/
@@ -5236,7 +5273,6 @@ static void new_table_block(e2fsck_t ctx, blk64_t first_block, dgrp_t group,
fix_problem(ctx, (old_block ? PR_1_RELOC_FROM_TO :
PR_1_RELOC_TO), &pctx);
pctx.blk2 = 0;
- e2fsck_pass1_block_map_lock(ctx);
for (i = 0; i < num; i++) {
pctx.blk = i;
ext2fs_mark_block_bitmap2(ctx->block_found_map, (*new_block)+i);
@@ -5257,7 +5293,6 @@ static void new_table_block(e2fsck_t ctx, blk64_t first_block, dgrp_t group,
if (pctx.errcode)
fix_problem(ctx, PR_1_RELOC_WRITE_ERR, &pctx);
}
- e2fsck_pass1_block_map_unlock(ctx);
ext2fs_free_mem(&buf);
}

diff --git a/e2fsck/util.c b/e2fsck/util.c
index 93cd96c7..5714576a 100644
--- a/e2fsck/util.c
+++ b/e2fsck/util.c
@@ -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

diff --git a/tests/f_itable_collision/expect.1 b/tests/f_itable_collision/expect.1
index 01c85d4d..7e98baa8 100644
--- a/tests/f_itable_collision/expect.1
+++ b/tests/f_itable_collision/expect.1
@@ -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...
--
2.37.3


2022-11-07 12:28:13

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 31/72] e2fsck: print thread log properly

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.

Signed-off-by: Li Xi <[email protected]>
Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
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(-)

diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 639f4e80..cdd158cc 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -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

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 3b411b70..a2c13be5 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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;
}
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index d5452441..9ee3914e 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -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 ?
diff --git a/e2fsck/util.c b/e2fsck/util.c
index 42740d9e..254b4d04 100644
--- a/e2fsck/util.c
+++ b/e2fsck/util.c
@@ -11,6 +11,7 @@

#include "config.h"
#include <stdlib.h>
+#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
@@ -84,13 +85,67 @@ out:
exit(exit_value);
}

+#ifdef HAVE_PTHREAD
+static void thread_log_out(struct e2fsck_thread *tinfo)
+{
+ printf("[Thread %d] %s", tinfo->et_thread_index,
+ tinfo->et_log_buf);
+ tinfo->et_log_length = 0;
+ tinfo->et_log_buf[0] = '\0';
+}
+#endif
+
void log_out(e2fsck_t ctx, const char *fmt, ...)
{
va_list pvar;
+ struct e2fsck_thread *tinfo;
+ int buf_size;
+ int msg_size;
+ int left_size;
+ int fmt_length = strlen(fmt);
+
+#ifdef HAVE_PTHREAD
+ if ((ctx->options & E2F_OPT_MULTITHREAD) && ctx->global_ctx) {
+ tinfo = &ctx->thread_info;
+ buf_size = sizeof(tinfo->et_log_buf);
+ left_size = buf_size - tinfo->et_log_length;
+
+ va_start(pvar, fmt);
+ msg_size = vsnprintf(tinfo->et_log_buf + tinfo->et_log_length,
+ left_size, fmt, pvar);
+ va_end(pvar);
+
+ if (msg_size >= left_size) {
+ tinfo->et_log_buf[tinfo->et_log_length] = '\0';
+
+ assert(msg_size < buf_size);
+ if (msg_size < buf_size) {
+ thread_log_out(tinfo);
+
+ va_start(pvar, fmt);
+ msg_size = vsnprintf(tinfo->et_log_buf, buf_size,
+ fmt, pvar);
+ va_end(pvar);
+
+ tinfo->et_log_length += msg_size;
+ tinfo->et_log_buf[tinfo->et_log_length] = '\0';
+ }
+ } else {
+ tinfo->et_log_length += msg_size;
+ tinfo->et_log_buf[tinfo->et_log_length] = '\0';
+ }
+
+ if (tinfo->et_log_length > 0 &&
+ tinfo->et_log_buf[tinfo->et_log_length - 1] == '\n')
+ thread_log_out(tinfo);
+ } else
+#endif
+ {
+ va_start(pvar, fmt);
+ vprintf(fmt, pvar);
+ va_end(pvar);
+ }

- va_start(pvar, fmt);
- vprintf(fmt, pvar);
- va_end(pvar);
if (ctx->logf) {
va_start(pvar, fmt);
vfprintf(ctx->logf, fmt, pvar);
diff --git a/tests/f_multithread/expect.1 b/tests/f_multithread/expect.1
index e2b954d0..8d2acd2b 100644
--- a/tests/f_multithread/expect.1
+++ b/tests/f_multithread/expect.1
@@ -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
diff --git a/tests/f_multithread_logfile/expect.1 b/tests/f_multithread_logfile/expect.1
index e2b954d0..8d2acd2b 100644
--- a/tests/f_multithread_logfile/expect.1
+++ b/tests/f_multithread_logfile/expect.1
@@ -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
diff --git a/tests/f_multithread_no/expect.1 b/tests/f_multithread_no/expect.1
index d14c4083..f85a3382 100644
--- a/tests/f_multithread_no/expect.1
+++ b/tests/f_multithread_no/expect.1
@@ -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
--
2.37.3


2022-11-07 12:28:13

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 48/72] e2fsck: allow admin specify number of threads

From: Wang Shilong <[email protected]>

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

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
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(-)

diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 23e8d2ed..e3276924 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -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);
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index fdd1f3d6..5bf6980b 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -1199,6 +1199,97 @@ static int e2fsck_should_abort(e2fsck_t ctx)
return 0;
}

+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
+/* TODO: tdb needs to be handled properly for multiple threads*/
+static int multiple_threads_supported(e2fsck_t ctx)
+{
+#ifdef CONFIG_TDB
+ unsigned int threshold;
+ ext2_ino_t num_dirs;
+ errcode_t retval;
+ char *tdb_dir;
+ int enable;
+
+ profile_get_string(ctx->profile, "scratch_files", "directory", 0, 0,
+ &tdb_dir);
+ profile_get_uint(ctx->profile, "scratch_files",
+ "numdirs_threshold", 0, 0, &threshold);
+ profile_get_boolean(ctx->profile, "scratch_files",
+ "icount", 0, 1, &enable);
+
+ retval = ext2fs_get_num_dirs(ctx->fs, &num_dirs);
+ if (retval)
+ num_dirs = 1024; /* Guess */
+
+ /* tdb is unsupported now */
+ if (enable && tdb_dir && !access(tdb_dir, W_OK) &&
+ (!threshold || num_dirs > threshold))
+ return 0;
+#endif
+ return 1;
+}
+
+/**
+ * Even though we could specify number of threads,
+ * but it might be more than the whole filesystem
+ * block groups, correct it here.
+ */
+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 max_threads;
+
+ if (num_threads < 1) {
+ num_threads = 1;
+ goto out;
+ }
+
+ if (!multiple_threads_supported(ctx)) {
+ num_threads = 1;
+ fprintf(stderr, "Fall through single thread for pass1 "
+ "because tdb could not handle properly\n");
+ goto out;
+ }
+
+ if (ext2fs_has_feature_flex_bg(fs->super))
+ flexbg_size = 1 << fs->super->s_log_groups_per_flex;
+ max_threads = fs->group_desc_count / flexbg_size;
+ if (max_threads == 0)
+ max_threads = 1;
+
+ if (num_threads > max_threads) {
+ fprintf(stderr, "Use max possible thread num: %d instead\n",
+ max_threads);
+ num_threads = max_threads;
+ }
+out:
+ ctx->fs_num_threads = num_threads;
+}
+#endif
+
/*
* We need call mark_table_blocks() before multiple
* thread start, since all known system blocks should be
@@ -1209,6 +1300,11 @@ static errcode_t e2fsck_pass1_prepare(e2fsck_t ctx)
struct problem_context pctx;
ext2_filsys fs = ctx->fs;

+ init_ext2_max_sizes();
+#ifdef HAVE_PTHREAD
+ e2fsck_pass1_set_thread_num(ctx);
+#endif
+
clear_problem_context(&pctx);
if (!(ctx->options & E2F_OPT_PREEN))
fix_problem(ctx, PR_1_PASS_HEADER, &pctx);
@@ -2271,27 +2367,6 @@ 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)
@@ -3151,7 +3226,7 @@ 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 = 1;
+ int num_threads = global_ctx->fs_num_threads;
errcode_t retval;

retval = e2fsck_pass1_threads_start(&infos, num_threads, global_ctx);
@@ -3174,48 +3249,15 @@ out_abort:
}
#endif

-/* TODO: tdb needs to be handled properly for multiple threads*/
-static int multiple_threads_supported(e2fsck_t ctx)
-{
-#ifdef CONFIG_TDB
- unsigned int threshold;
- ext2_ino_t num_dirs;
- errcode_t retval;
- char *tdb_dir;
- int enable;
-
- profile_get_string(ctx->profile, "scratch_files", "directory", 0, 0,
- &tdb_dir);
- profile_get_uint(ctx->profile, "scratch_files",
- "numdirs_threshold", 0, 0, &threshold);
- profile_get_boolean(ctx->profile, "scratch_files",
- "icount", 0, 1, &enable);
-
- retval = ext2fs_get_num_dirs(ctx->fs, &num_dirs);
- if (retval)
- num_dirs = 1024; /* Guess */
-
- /* tdb is unsupported now */
- if (enable && tdb_dir && !access(tdb_dir, W_OK) &&
- (!threshold || num_dirs > threshold)) {
- fprintf(stderr, "Fall through single thread for pass1 "
- "because tdb could not handle properly\n");
- return 0;
- }
- #endif
- return 1;
-}
-
void e2fsck_pass1(e2fsck_t ctx)
{
errcode_t retval;

- init_ext2_max_sizes();
retval = e2fsck_pass1_prepare(ctx);
if (retval)
return;
#ifdef HAVE_PTHREAD
- if (ctx->options & E2F_OPT_MULTITHREAD && multiple_threads_supported(ctx))
+ if (ctx->options & E2F_OPT_MULTITHREAD || ctx->fs_num_threads > 1)
e2fsck_pass1_multithread(ctx);
else
#endif
diff --git a/e2fsck/unix.c b/e2fsck/unix.c
index af2457e3..dfa3f897 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -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':
diff --git a/tests/f_multithread/script b/tests/f_multithread/script
index 0fe96cd0..83cd0f03 100644
--- a/tests/f_multithread/script
+++ b/tests/f_multithread/script
@@ -1,4 +1,4 @@
-FSCK_OPT="-fy -m"
+FSCK_OPT="-fy -m1"
SECOND_FSCK_OPT=-yf

. $cmd_dir/run_e2fsck
diff --git a/tests/f_multithread_completion/script b/tests/f_multithread_completion/script
index bf23cd61..0ec13816 100644
--- a/tests/f_multithread_completion/script
+++ b/tests/f_multithread_completion/script
@@ -1,4 +1,4 @@
-FSCK_OPT="-fy -m -C 1"
+FSCK_OPT="-fy -m1 -C 1"
SECOND_FSCK_OPT=-yf

. $cmd_dir/run_e2fsck
diff --git a/tests/f_multithread_logfile/script b/tests/f_multithread_logfile/script
index 4f9ca6f8..dbb65319 100644
--- a/tests/f_multithread_logfile/script
+++ b/tests/f_multithread_logfile/script
@@ -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"
diff --git a/tests/f_multithread_no/script b/tests/f_multithread_no/script
index b93deb3a..db791e11 100644
--- a/tests/f_multithread_no/script
+++ b/tests/f_multithread_no/script
@@ -1,4 +1,4 @@
-FSCK_OPT="-fn -m"
+FSCK_OPT="-fn -m1"
SECOND_FSCK_OPT=-yf

. $cmd_dir/run_e2fsck
diff --git a/tests/f_multithread_preen/script b/tests/f_multithread_preen/script
index ecb79cd6..8965f4a7 100644
--- a/tests/f_multithread_preen/script
+++ b/tests/f_multithread_preen/script
@@ -1,4 +1,4 @@
-FSCK_OPT="-fp -m"
+FSCK_OPT="-fp -m1"
SECOND_FSCK_OPT=-yf

. $cmd_dir/run_e2fsck
diff --git a/tests/f_multithread_yes/script b/tests/f_multithread_yes/script
index 38891f6a..8b4aa9b8 100644
--- a/tests/f_multithread_yes/script
+++ b/tests/f_multithread_yes/script
@@ -1,4 +1,4 @@
-FSCK_OPT="-f -m"
+FSCK_OPT="-f -m1"
SECOND_FSCK_OPT=-yf

. $cmd_dir/run_e2fsck
--
2.37.3


2022-11-07 12:28:15

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 33/72] e2fsck: optimize the inserting of dir_info_db

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.

Signed-off-by: Li Xi <[email protected]>
Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/dirinfo.c | 172 ++++++++++++++++++++++++++++++-----------------
1 file changed, 112 insertions(+), 60 deletions(-)

diff --git a/e2fsck/dirinfo.c b/e2fsck/dirinfo.c
index 49d624c5..5c360a90 100644
--- a/e2fsck/dirinfo.c
+++ b/e2fsck/dirinfo.c
@@ -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,
--
2.37.3


2022-11-07 12:28:28

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 49/72] e2fsck: adjust number of threads

From: Wang Shilong <[email protected]>

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

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 32 +++++++++++++++-----------------
1 file changed, 15 insertions(+), 17 deletions(-)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 5bf6980b..1d4f576c 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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"));
--
2.37.3


2022-11-07 12:28:30

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 52/72] e2fsck: reset lost_and_found after threads finish

From: Wang Shilong <[email protected]>

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

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 59ff888f..1a5fcf66 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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;
--
2.37.3


2022-11-07 12:28:31

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 53/72] e2fsck: merge extent depth count after threads finish

From: Wang Shilong <[email protected]>

tests covered by f_extent_htree.

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 1a5fcf66..c89c424d 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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;
--
2.37.3


2022-11-07 12:28:32

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 50/72] e2fsck: fix readahead for pfsck of pass1

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.

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 29 ++++++++++++++++++++---------
1 file changed, 20 insertions(+), 9 deletions(-)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 1d4f576c..5d07daec 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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))
--
2.37.3


2022-11-07 12:28:32

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 51/72] e2fsck: merge options after threads finish

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.

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 5d07daec..59ff888f 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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;
--
2.37.3


2022-11-07 12:28:42

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 54/72] e2fsck: simplify e2fsck context merging codes

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.

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 158 ++++++++++---------------------------------------
1 file changed, 31 insertions(+), 127 deletions(-)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index c89c424d..8e85f70f 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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);
--
2.37.3


2022-11-07 12:28:55

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 55/72] e2fsck: set E2F_FLAG_ALLOC_OK after threads

From: Wang Shilong <[email protected]>

Only flag ALLOC OK after all threads finished without problem.

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 8e85f70f..a5dc6e44 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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);
--
2.37.3


2022-11-07 12:28:58

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 56/72] e2fsck: wait fix thread finish before checking

From: Wang Shilong <[email protected]>

Before proceeding next inodes, waitting existed
fixing finished.

Signed-off-by: Wang Shilong <[email protected]>
[unlock from Jan's orphan inode path as well]
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/e2fsck.c | 3 +++
e2fsck/e2fsck.h | 5 +++-
e2fsck/pass1.c | 70 +++++++++++++++++++++++++++++++++++++++++++------
e2fsck/util.c | 56 ++++++++++++++++++++++++++++++++++++---
4 files changed, 122 insertions(+), 12 deletions(-)

diff --git a/e2fsck/e2fsck.c b/e2fsck/e2fsck.c
index 1e295e3e..a5150dab 100644
--- a/e2fsck/e2fsck.c
+++ b/e2fsck/e2fsck.c
@@ -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;
diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index e3276924..01bd9d01 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -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);
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index a5dc6e44..29333acf 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -991,8 +991,10 @@ static void finish_processing_inode(e2fsck_t ctx, ext2_ino_t ino,
#define FINISH_INODE_LOOP(ctx, ino, pctx, failed_csum) \
do { \
finish_processing_inode((ctx), (ino), (pctx), (failed_csum)); \
- if ((ctx)->flags & E2F_FLAG_ABORT) \
+ if ((ctx)->flags & E2F_FLAG_ABORT) { \
+ e2fsck_pass1_check_unlock(ctx); \
return; \
+ } \
} while (0)

static int could_be_block_map(ext2_filsys fs, struct ext2_inode *inode)
@@ -1374,8 +1376,12 @@ static errcode_t e2fsck_pass1_prepare(e2fsck_t ctx)
ext2fs_mark_block_bitmap2(ctx->block_found_map,
fs->super->s_mmp_block);
#ifdef HAVE_PTHREAD
- pthread_mutex_init(&ctx->fs_fix_mutex, NULL);
+ pthread_rwlock_init(&ctx->fs_fix_rwlock, NULL);
pthread_rwlock_init(&ctx->fs_block_map_rwlock, NULL);
+ if (ctx->fs_num_threads > 1)
+ ctx->fs_need_locking = 1;
+ else
+ ctx->fs_need_locking = 0;
#endif

return 0;
@@ -1387,6 +1393,10 @@ static void e2fsck_pass1_post(e2fsck_t ctx)
ext2_filsys fs = ctx->fs;
char *block_buf;

+#ifdef HAVE_PTHREAD
+ ctx->fs_need_locking = 0;
+#endif
+
if (e2fsck_should_abort(ctx))
return;

@@ -1662,6 +1672,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
#endif

while (1) {
+ e2fsck_pass1_check_lock(ctx);
if (ino % (fs->super->s_inodes_per_group * 4) == 1) {
if (e2fsck_mmp_update(fs))
fatal_error(ctx, 0);
@@ -1672,8 +1683,10 @@ void e2fsck_pass1_run(e2fsck_t ctx)
if (ino > ino_threshold)
pass1_readahead(ctx, &ra_group, &ino_threshold);
ehandler_operation(old_op);
- if (e2fsck_should_abort(ctx))
+ if (e2fsck_should_abort(ctx)) {
+ e2fsck_pass1_check_unlock(ctx);
goto endit;
+ }
if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) {
/*
* If badblocks says badblocks is bad, offer to clear
@@ -1694,27 +1707,45 @@ void e2fsck_pass1_run(e2fsck_t ctx)
fix_problem(ctx, PR_1_ISCAN_ERROR,
&pctx);
ctx->flags |= E2F_FLAG_ABORT;
+ e2fsck_pass1_check_unlock(ctx);
+ goto endit;
} else
ctx->flags |= E2F_FLAG_RESTART;
- goto endit;
+ err = ext2fs_inode_scan_goto_blockgroup(scan,
+ 0);
+ if (err) {
+ fix_problem(ctx, PR_1_ISCAN_ERROR,
+ &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ e2fsck_pass1_check_unlock(ctx);
+ goto endit;
+ }
+ e2fsck_pass1_check_unlock(ctx);
+ continue;
}
if (!ctx->inode_bb_map)
alloc_bb_map(ctx);
ext2fs_mark_inode_bitmap2(ctx->inode_bb_map, ino);
ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino);
+ e2fsck_pass1_check_unlock(ctx);
continue;
}
- if (pctx.errcode == EXT2_ET_SCAN_FINISHED)
+ if (pctx.errcode == EXT2_ET_SCAN_FINISHED) {
+ e2fsck_pass1_check_unlock(ctx);
break;
+ }
if (pctx.errcode &&
pctx.errcode != EXT2_ET_INODE_CSUM_INVALID &&
pctx.errcode != EXT2_ET_INODE_IS_GARBAGE) {
fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
ctx->flags |= E2F_FLAG_ABORT;
+ e2fsck_pass1_check_unlock(ctx);
goto endit;
}
- if (!ino)
+ if (!ino) {
+ e2fsck_pass1_check_unlock(ctx);
break;
+ }
#ifdef HAVE_PTHREAD
if (ctx->global_ctx)
ctx->thread_info.et_inode_number++;
@@ -1767,6 +1798,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
pctx.num = inode->i_links_count;
fix_problem(ctx, PR_1_ICOUNT_STORE, &pctx);
ctx->flags |= E2F_FLAG_ABORT;
+ e2fsck_pass1_check_unlock(ctx);
goto endit;
}
} else if ((ino >= EXT2_FIRST_INODE(fs->super)) &&
@@ -1781,6 +1813,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
}
}
FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+ e2fsck_pass1_check_unlock(ctx);
continue;
}

@@ -1801,6 +1834,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
&pctx);
if (res < 0) {
/* skip FINISH_INODE_LOOP */
+ e2fsck_pass1_check_unlock(ctx);
continue;
}
}
@@ -1822,6 +1856,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
} else if (fix_problem(ctx, PR_1_INLINE_DATA_SET, &pctx)) {
e2fsck_clear_inode(ctx, ino, inode, 0, "pass1");
/* skip FINISH_INODE_LOOP */
+ e2fsck_pass1_check_unlock(ctx);
continue;
}
}
@@ -1866,6 +1901,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
if (err) {
pctx.errcode = err;
ctx->flags |= E2F_FLAG_ABORT;
+ e2fsck_pass1_check_unlock(ctx);
goto endit;
}
inode->i_flags &= ~EXT4_INLINE_DATA_FL;
@@ -1880,6 +1916,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
/* Some other kind of non-xattr error? */
pctx.errcode = err;
ctx->flags |= E2F_FLAG_ABORT;
+ e2fsck_pass1_check_unlock(ctx);
goto endit;
}
}
@@ -1917,6 +1954,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
ext2fs_mark_inode_bitmap2(ctx->inode_used_map,
ino);
/* skip FINISH_INODE_LOOP */
+ e2fsck_pass1_check_unlock(ctx);
continue;
}
}
@@ -1980,6 +2018,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
pctx.num = 4;
fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
ctx->flags |= E2F_FLAG_ABORT;
+ e2fsck_pass1_check_unlock(ctx);
goto endit;
}
pb.ino = EXT2_BAD_INO;
@@ -1997,16 +2036,19 @@ void e2fsck_pass1_run(e2fsck_t ctx)
if (pctx.errcode) {
fix_problem(ctx, PR_1_BLOCK_ITERATE, &pctx);
ctx->flags |= E2F_FLAG_ABORT;
+ e2fsck_pass1_check_unlock(ctx);
goto endit;
}
if (pb.bbcheck)
if (!fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK_PROMPT, &pctx)) {
ctx->flags |= E2F_FLAG_ABORT;
+ e2fsck_pass1_check_unlock(ctx);
goto endit;
}
ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino);
clear_problem_context(&pctx);
FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+ e2fsck_pass1_check_unlock(ctx);
continue;
} else if (ino == EXT2_ROOT_INO) {
/*
@@ -2048,6 +2090,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
}
check_blocks(ctx, &pctx, block_buf, NULL);
FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+ e2fsck_pass1_check_unlock(ctx);
continue;
}
if ((inode->i_links_count ||
@@ -2075,6 +2118,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
}
check_blocks(ctx, &pctx, block_buf, NULL);
FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+ e2fsck_pass1_check_unlock(ctx);
continue;
}
if ((inode->i_links_count ||
@@ -2101,6 +2145,7 @@ void e2fsck_pass1_run(e2fsck_t ctx)
}
check_blocks(ctx, &pctx, block_buf, NULL);
FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+ e2fsck_pass1_check_unlock(ctx);
continue;
}
if ((inode->i_links_count ||
@@ -2139,11 +2184,13 @@ void e2fsck_pass1_run(e2fsck_t ctx)
}
check_blocks(ctx, &pctx, block_buf, NULL);
FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+ e2fsck_pass1_check_unlock(ctx);
continue;
}

if (!inode->i_links_count) {
FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+ e2fsck_pass1_check_unlock(ctx);
continue;
}
/*
@@ -2253,12 +2300,14 @@ void e2fsck_pass1_run(e2fsck_t ctx)
ctx->fs_symlinks_count++;
if (inode->i_flags & EXT4_INLINE_DATA_FL) {
FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+ e2fsck_pass1_check_unlock(ctx);
continue;
} else if (ext2fs_is_fast_symlink(inode)) {
ctx->fs_fast_symlinks_count++;
check_blocks(ctx, &pctx, block_buf,
&ea_ibody_quota);
FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+ e2fsck_pass1_check_unlock(ctx);
continue;
}
}
@@ -2306,16 +2355,21 @@ void e2fsck_pass1_run(e2fsck_t ctx)

FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);

- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ if (e2fsck_should_abort(ctx)) {
+ e2fsck_pass1_check_unlock(ctx);
goto endit;
+ }

if (process_inode_count >= ctx->process_inode_size) {
process_inodes(ctx, block_buf, inodes_to_process,
&process_inode_count);

- if (e2fsck_should_abort(ctx))
+ if (e2fsck_should_abort(ctx)) {
+ e2fsck_pass1_check_unlock(ctx);
goto endit;
+ }
}
+ e2fsck_pass1_check_unlock(ctx);
}
process_inodes(ctx, block_buf, inodes_to_process,
&process_inode_count);
diff --git a/e2fsck/util.c b/e2fsck/util.c
index 5714576a..b7c1e7a5 100644
--- a/e2fsck/util.c
+++ b/e2fsck/util.c
@@ -82,7 +82,8 @@ void fatal_error(e2fsck_t ctx, const char *msg)
}
out:
ctx->flags |= E2F_FLAG_ABORT;
- if (ctx->flags & E2F_FLAG_SETJMP_OK)
+ if (!(ctx->options & E2F_OPT_MULTITHREAD) &&
+ ctx->flags & E2F_FLAG_SETJMP_OK)
longjmp(ctx->abort_loc, 1);
if (ctx->logf)
fprintf(ctx->logf, "Exit status: %d\n", exit_value);
@@ -580,38 +581,79 @@ void e2fsck_read_inode_full(e2fsck_t ctx, unsigned long ino,
if (!global_ctx) \
global_ctx = ctx; \

+/**
+ * before we hold write lock, read lock should
+ * has been held.
+ */
void e2fsck_pass1_fix_lock(e2fsck_t ctx)
{
+ int err;
+
+ if (!ctx->fs_need_locking)
+ return;
+
e2fsck_get_lock_context(ctx);
- pthread_mutex_lock(&global_ctx->fs_fix_mutex);
+ err = pthread_rwlock_trywrlock(&global_ctx->fs_fix_rwlock);
+ assert(err != 0);
+ pthread_rwlock_unlock(&global_ctx->fs_fix_rwlock);
+ pthread_rwlock_wrlock(&global_ctx->fs_fix_rwlock);
}

void e2fsck_pass1_fix_unlock(e2fsck_t ctx)
{
+ if (!ctx->fs_need_locking)
+ return;
e2fsck_get_lock_context(ctx);
- pthread_mutex_unlock(&global_ctx->fs_fix_mutex);
+ /* unlock write lock */
+ pthread_rwlock_unlock(&global_ctx->fs_fix_rwlock);
+ /* get read lock again */
+ pthread_rwlock_rdlock(&global_ctx->fs_fix_rwlock);
+}
+
+void e2fsck_pass1_check_lock(e2fsck_t ctx)
+{
+ if (!ctx->fs_need_locking)
+ return;
+ e2fsck_get_lock_context(ctx);
+ pthread_rwlock_rdlock(&global_ctx->fs_fix_rwlock);
+}
+
+void e2fsck_pass1_check_unlock(e2fsck_t ctx)
+{
+ if (!ctx->fs_need_locking)
+ return;
+ e2fsck_get_lock_context(ctx);
+ pthread_rwlock_unlock(&global_ctx->fs_fix_rwlock);
}

void e2fsck_pass1_block_map_w_lock(e2fsck_t ctx)
{
+ if (!ctx->fs_need_locking)
+ return;
e2fsck_get_lock_context(ctx);
pthread_rwlock_wrlock(&global_ctx->fs_block_map_rwlock);
}

void e2fsck_pass1_block_map_w_unlock(e2fsck_t ctx)
{
+ if (!ctx->fs_need_locking)
+ return;
e2fsck_get_lock_context(ctx);
pthread_rwlock_unlock(&global_ctx->fs_block_map_rwlock);
}

void e2fsck_pass1_block_map_r_lock(e2fsck_t ctx)
{
+ if (!ctx->fs_need_locking)
+ return;
e2fsck_get_lock_context(ctx);
pthread_rwlock_rdlock(&global_ctx->fs_block_map_rwlock);
}

void e2fsck_pass1_block_map_r_unlock(e2fsck_t ctx)
{
+ if (!ctx->fs_need_locking)
+ return;
e2fsck_get_lock_context(ctx);
pthread_rwlock_unlock(&global_ctx->fs_block_map_rwlock);
}
@@ -624,6 +666,14 @@ void e2fsck_pass1_fix_lock(e2fsck_t ctx)
void e2fsck_pass1_fix_unlock(e2fsck_t ctx)
{

+}
+void e2fsck_pass1_check_lock(e2fsck_t ctx)
+{
+
+}
+void e2fsck_pass1_check_unlock(e2fsck_t ctx)
+{
+
}
void e2fsck_pass1_block_map_w_lock(e2fsck_t ctx)
{
--
2.37.3


2022-11-07 12:29:08

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 57/72] e2fsck: cleanup e2fsck_pass1_thread_join()

From: Wang Shilong <[email protected]>

Use e2fsck_reset_context() to free memory to simpify
codes.

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 33 +++++++--------------------------
1 file changed, 7 insertions(+), 26 deletions(-)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 29333acf..93cff80e 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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;
}
--
2.37.3


2022-11-07 12:29:09

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 58/72] e2fsck: make default smallest RA size to 1M

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.

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/readahead.c | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/e2fsck/readahead.c b/e2fsck/readahead.c
index 38d4ec42..40b73664 100644
--- a/e2fsck/readahead.c
+++ b/e2fsck/readahead.c
@@ -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))
--
2.37.3


2022-11-07 12:29:10

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 59/72] e2fsck: update mmp block in one thread

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.

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/e2fsck.h | 1 +
e2fsck/pass1.c | 34 ++++++++++++++++++++++++++++++++--
2 files changed, 33 insertions(+), 2 deletions(-)

diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 01bd9d01..2dd7ba27 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -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;
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 93cff80e..ed4275c3 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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
--
2.37.3


2022-11-07 12:29:20

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 60/72] e2fsck: reset @inodes_to_rebuild if restart

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

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/e2fsck.c | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/e2fsck/e2fsck.c b/e2fsck/e2fsck.c
index a5150dab..53af8905 100644
--- a/e2fsck/e2fsck.c
+++ b/e2fsck/e2fsck.c
@@ -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;
--
2.37.3


2022-11-07 12:29:48

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 62/72] e2fsck: fix memory leaks with pfsck enabled

From: Wang Shilong <[email protected]>

valgrind detected two memory leaks:

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

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/e2fsck.c | 4 ++++
e2fsck/pass1.c | 1 +
2 files changed, 5 insertions(+)

diff --git a/e2fsck/e2fsck.c b/e2fsck/e2fsck.c
index 53af8905..db0a5059 100644
--- a/e2fsck/e2fsck.c
+++ b/e2fsck/e2fsck.c
@@ -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;
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index ed4275c3..d745699d 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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.
--
2.37.3


2022-11-07 12:29:44

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 61/72] tests: add pfsck test

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.

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
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

diff --git a/tests/f_multithread_ok/expect.1 b/tests/f_multithread_ok/expect.1
new file mode 100644
index 00000000..4742f408
--- /dev/null
+++ b/tests/f_multithread_ok/expect.1
@@ -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
diff --git a/tests/f_multithread_ok/image.gz b/tests/f_multithread_ok/image.gz
new file mode 100644
index 0000000000000000000000000000000000000000..42622c3786a81f348f2935d4f9cc2812bcf539d4
GIT binary patch
literal 796311
zcmZsDd0b5U|Np&QLdaH0YS1O5hC*5oq7Vj2v<*@r85O1V*b*usDQ&||l9u77P0Jva
z)<Gq$Bh#X7rhTfJIp_DD%=`5DJ?{6f9*^^w^M1cy`}6gDzfPpU88Zs?Y1BWao^d|m
zX#Yn~p48<>moi2ay?#B-{A1g-MTuXxlc&!%*nZ`Yr&2R^&X_lC!>_;{TW8E#9C_u2
z?X$mT&bu@3*^C+27RxT$Fk|(}1BVYBuI6ReHoL_PRy-a|8Mbk%tQWKj@b<SIMJ~~F
ze$N>vCM&9##cH`ABn8T#VO2Cw9fWDfZ12lq^AW5)8dn$>?Yc*2zkIZCI;rnxhkQ?M
zcfe9Vom}Q)zZ`+mxSgx813C2`7Y5H0+Fgz*D+~7zQ4Z4i8dnqh&!Bv2@R{0QbbMpi
zzR2~<Q=1<?<(TYKwxE6FsHhh2k6eG&H7{#^C}&lzx0g@!xxH<88?Wi%p;B$N*QV`1
z9jZ24EQv7OQjGWVQ=Zx2w%2l*!T^;JYDF~8MTc<Z)`15NWk+$m3t1RY&~jnR(zyXQ
z*4irfAF44OxG1Dyjrbss@5yAFnv<z}i)HT#=-7PM&s0~hv2LmmTbsYMnM5xTaNI_H
z4Ml#B@eUGiVhzbs9Nfuu{NB<YHqecD>EDlwakQk(s8RI4KQ(b*l_}%GXG&h2_*3^-
z-|%A-4qp?$eadlQe4YocN30c4a6hJ`<3EcPu>-OM?El&HZqHsn(oZf5g%oe_uhul5
zTRFJW|7`H7a?#LoF6J?L*Z>thvrqqB?8JW;+xdSiHvFH(7XJ9pVV_6*SMs0#m3+_W
ze<WYi`QODJ{&%sj|6Q!~|5&X1zl%lxcd>#0F1Gf+i_QE$)cEDUYFznWHHP{A$0mP`
z{YUaK0s9j=Ecd_WT#84c9si$G1#p<E_$WR#`K-=~K4gG8hKIar->=7GJaGGpO~oJM
zqXyZ+75(YA{vdA>3SYJl4JLKO=Z8<XRpx*FWNJs=m%DNJYq_Z#Sv~hn`I<o~&ohFa
zJ0&yx8pi1;dp3_ziY4KKjwhXe;$!@zwqDP@S)FLW6Lh>6PZc<MH*}vW5Z%Soo}!|J
zOi?&K&P)>4dIzK99K0s8{2JDo*~G+@=IVXzXQK6BC9G@=5qWEU7w}y-kwd%^F`AI~
z9<1ivjp7V?KkA8(A-GV4^{Xj)F3?2}@$oi#+E#MIo#Fu7686n0=+?W@Pxr(x4itGl
z7G1rx#QhfI6|(3mDdq5;*Wc+Ob@b8J-nqwu?bh_gh3(lOClEarr3-d?1kGaXhd-j%
zG0(aBt5Slq>*q_JQLohT@UB8tyPqoXMQKez!g;@yJzpREvuO0o#E$+EqLnhOU5hfh
zPc5E;SSBvAd(B**hAii7O{nH4nEfzO3p=@PME=AGTi|!H;}+Sk_r|-BBWnwf_K-_^
zNDr6};T$ISSCHhH%<&O{`O(^VzyAAhgTe^W>tKP$zEqq0aQ!W5NSev(^UhY}-aBxy
z_dPt__QRPEKXKuUD<6xFu`+qeTXbZm`Mo)`rsyQP#sRH<z<#S*iR^$vM%JNgSH_$4
z*6dp?(N5WVfl)I_Hvb$RqdY@+I{r{lA@*-MW@?e@FK|pwfEyO~$OtagnVB(3Tj5%<
zb#%*hxgB%x1@H{A5u*3$gTs4PBTQAv>5+SN^y>Z#4DaCWP_1g30>zy-ckJMg#>qn&
z7o;JcUuEXY8#DJU$3Hu+cqlfUnwDT{JFQ1a;Nv_?a<XL(Xlc|oUrl)`FIaLk#s^<4
z9j`KMSwlM@(LVwn4Sz}Um5K4Z19w=PhPTAby?@Z6nHTuEgK9frC@C$0oe&Sw7nGSo
z8EUdv%u-6H{GntWi5~H|#tv$5*9cqqJw!B5;s`a5MVW8eJ*01E*ZCXb1HV0CdKz+a
zsPxZ7U(!=zi`rI0Jo1R{+RI<t{)GCqEXRHocKq{Tfjn{Iq8{cetrN%Nw58#rT8EKL
zgQ}AUT<gDFo#BhMrBB#YiHl|TS3S8B^8AoQ*joFM09^C@E8czss3os*%;?YeM*j}@
zYAuG=qI+Y!*Q|iQA1Us6%3|hD_$2LJ54G;Zm(Kxn%HV29r7u>K`T3Pg6H8{NWC!h2
zBedAd=f1~B9D`a4ju6=|Oqe4RRd;7Q9o;vmm?{oFc7!)Zv%CJwYPaB~ip0O7F7PSi
zDZg&TaG?F8T<NII(#=0dJI})9mX~9V9;LzOE^<m|+~ATJ6LA~m!ou4G7h3%VzR+2t
z+*r8zW389YTjxVr<;wl21<@PUr0);fE;(;zYb@TKfvkFAqz^+ujXTDS`2HneT)5ym
zVVX>b5xQ^Uctrie%n)PvZVQ)gZc&^ZEx%8qIrbwUlu7GRJXjVUqXC>OMnm`f+3~{H
zGn$a!zLe=Wl3;`Ar1li1La`_pSh#4Ds8i=yHhk6Sv8XuE@bb8A^L6+urK}G8tfv$$
zY&Y>Fy93{Dvj0=ldMAB}|KhuW1n*_->~d0~uE6xc;nF)aPimPA!!YH2?2(DA^*uRi
zS_n7P11fX=q}BPk)NJAWx9D%f^FA2h9<86By-@0fR>{#66#H)W9H=v6?t_Z-02_Fx
z|FpEFJccQYS80HUdl)sgBPItWD&zl%#ij3fJwI1hx(IF{<2YiHvAX}9WknlnnZy(5
zKe_Z~$JjY@=Mcv&X+mCWJ^y=7sR>keU>(!%{HN+hmQw7s&mzumr~ivDWS3?p6y;&F
zp+RXt9eHu|vPRpe?fj71iRA2?CFm%4awcCzk!}Rf*2bmt3}f8+16!fP?BZf2gY|<4
z`Fjri!uo#)E{QLsJ{I;n21ffTKNS=^O=Ll=9NQj}P@I{p6H06`HA|4t{jf8NetQ2^
ze9s_~R)ekPVhYFj{;!%7tL96R!_;hnV^M|t*HuCBPzHRN1Acwyb!zekKH<9W8~OB%
z@R5}%OyLS*2crm)0xxE8Q+-3A#PsOuG-N30hC(-a(G!W~Nks-Ye~_v*C0}okL~}^A
z05iLvM;;r{Hz7ng?SOg(Cpb<I8yj2s*YB!;&)^Cul)qHhoqly`72M%(ApggxnKC)E
z<KYg4Jv5!C{4$R5Blsw!r5Wwt^Yoa7PY2Z)st(=A7bOfAJM6Qmz71EKorYGnoJpfO
z55~dO%9@byCFQq$XzX+F99zISGTD)CUG<ltX%ez|NSgw-CoZ67^+o*hL_tSl>qr*a
z!p5KvB@F0Es_O-`je-m3PY372ka^uWh}yg@H$6kW0gtgF4d>p9zP)WKrQC=(?gBn0
z&Bi_s^*h4=QFY_96kiW^GY3ppNM1$mf{<_M9aF1yhOUa<tV7#cBQ2zr?mm|Yg+B7e
z3)4T<{v?}2yU;)Q;+-M?NS8L-3YWlhtW9GYck<=ROPYcuk8mBXNWP{)-RHm>ZCGSB
z0*4-vkt^D$w@YGCTpw7ax^kyN-l~J}{79@f&VG2GQ5^iq5+VzFWfLN;wyNo}OH((j
zF!CU$d{f^y3ynkI27%?+G~PvObgX3%;cSLn5gI<QPQlf+`6RT<Y%Vs|`T0TGV<I(B
zLEvt8F}1?_8y3J{?R7wX;(6ic)zYDxB3HdJ@~MJ<-;Hm8Mj?aTGknL=Fu~1!7EF^Y
zuvmg=?m?BaW3F(qb3h&BO4P0{tp&d71_XE%koqZaJqZe+227lYCg1YzUhfZUM29O6
z7yRfA)84OdM0mYtqiC@Amw7$~J$hSWD@rSuZf)M2E8rBFLmiEkW8?8ha&?}4UPhdb
zS?Y@?+OlfomDP7bGb{lfUO5(Om7$ljgxF(RqiA7FtWUh733Oh6u<M71n{4QqmV)F}
z<}Khy??arvIwZjQ$^`i{^U`4b5~pG!{UEn<(EYP*J<jqg(&6C>!D{xM!^=s@)&UY3
zs32+cIoF1}X~X}(N2Lb1#rktbCDzkm9uIe6qh3S2IWuM1@$cxC18wKhQ5_&bhPU?g
z8mHY*sOiABSmRN07sowSprcOXV@wzJxq-muJ<Ab4=NpAs7|2i#%xup_X(PD41z6v*
z%G>nmG&&6yO=d|RE<ErwrQb^4d7C8jGTLKtJ#8naZ~gnBO-q0QGcEIE$KnZ_E^)yB
zjpnm;hZ_tD8!+0MQCpXWjEHL?5KuEfA$ner>3CS+t_V+S03=8tX9Q&In+V7@^!#*z
z_5~iU82d&g9i<Y>(OUEIjd^92w_Hfvt&+phfD_v5n0<AusDmEDE(eC$WEhV{ZieTz
z%Hrcnv@Er;ALKRAE-63;3mWgNr^X`yd^{&o6fOt!%l?*|*BjLZ-)#Wz%Fbw1iWn@*
zgk_-tl=YXsZ}c`?m<fG)E{OWgZf2y7oyGu_bxnV^R@^`ETAT4&W>c*F310jPN%b_#
z;?s)_zH<=aH0W_Q;4EpYdV`~yBVb~22J5v(3eK;qs1KC*2)iN==N)g)zoAYvHfG=;
zhPILJyrVneS_WV(_M!%)^{g?GxX8dCmL~A2m)D0trIWy;t?aAcr+7ktr~-i+xzXVA
z%F&pqP)Q;Hm`~))b$4C}>pFS`<jPkOMgijn#u67q?}2=gKcKhiXTI(YqD(Fa0>smE
z-MZRV&w`*A*J~8u?iI+=2iDi1pzHZqpxxNrbA}h%*TZKs08Ee%2ze;HJYAw+R5uOz
z`Ch9tk&3(gMZjL9G!JK7j){A(<DLLvD!a`PpVfSEW8#O!P$hOiB`H#Q-L=nV`3pzW
z=gSE~+<17mMc2`DqwyXDmO|d=;k1D|yFUxOV35;6wy|(j4hdmVV7w`(;h(#Z5J&ah
zu%aM^dHBz*UAJG@n88v+0r}EC$Y89mi9~_5jwQyBeV@EHP|q1cUq$HxVHvH)JKRrc
zFVrR|Mmzm)nVl#sgAD|o6N@`p_gy^OE3<$3(3nMyf8AMJsvPq+Xf{y#q8kL&rVnho
zffb{pJjOo*Gx9WGhEY1Jnw}dDe`W+8t+ZlOV)2d8dv}8JvBrp%VI!&#K--4^e`qFq
zYuMvaj?nQH#6cq$BVGOrf@*w28q!s}pY*pM^%mS<A*cd{uF;HMZNXG{js);#dQ~M^
zjU5Spw!j0w7=7VAvtKj__be;NUVWmG_x8RfDy$Yb?d}p%^J0T&qT11cdMkL|8&*8k
zCkhAyWQ>Nxt#RIgp&BsMm;lf=ARTeP9%{gFwh%|m*-)33z5AhikinyTd}Um3g@}N?
ztk!0<g~{QRQ<p$rpn}w8V(jxZ{ksPVIvevRu*^`KYJjqpDYRcq9sWx2fa2uQGYQ`m
zf%I`XHkO<JzCsA04YqY?&tIx}hD$)YT2L(7-<L%#Y=D>?Ef5K7V}cR2oX+?5P?_pL
zCtAnoE0;DAWk4N-Oz<VT6nZ!m>UT462d03!`)%l$4t%Bu-n<gR7#$<W-6JtKe>jQC
zffwy=Q$HGhy$*jg7bN5W)l`R!a#NyA^1x|t3jJzon_wour+VOf8;0$V>@|a$%mHHF
z4zV|`5MrkAtRam8@@Bs<PxqSX@Mm=3S$8#<au4Ko66@0eV)RwUYT~Qt))4cvfyM=6
zsR2z9xGX%Ip$Gog((Kp~i`CF~$zW6S$fg+@{61YmxlC6Uo}TNilGt*ds2|9WX7r0=
zRfVxq7mO^ts0mpp<r||vTC@)ym<QG)+l=cTub%}EjAZet$zHvR)I*fX)j&0R!{DSm
z33g<{Qi+2$NFL7(i!daNKn6|99??jPV+G8!csoGf7EKKtE<UO*5sS^UZy#g+OJ(OD
zz9=nr>GVBo<OFW(1!bG9hi#6gET44Dk{)`M`ikx&fvkL}r;d{&Y(?|F{io>u$6zEi
z0%jt-InPahGfWSNOd8^GsEnsrArQ>{t*8jJ2dx^!RPg>xp<txLex$fEl9TD@B@YBw
zTQ|~SH{!S|v*gQ2`a(ajE_fo)5){Z_4^-?;iH6+I0dGost#kdZ_A^PHBERbi8Gr7T
z`Y|cf4Mrkh;8^XWnzaQ|POJNB^A%%ECm6SRX-xYc4;s~^|AJ8_?=V7rx$s`Xf%Fhp
z7-fO$1qC0P%e2xftf58zDaYQNAh+h#FqB}0_#=&3G*mR!9%K+9FNsBJP#r3)i5cVO
z>BF9exh0MH<<WiYOUWi^5YzR7T2fL3!rA&9k=TRzcN+5JtTC3ACL(^dLEtOXF8fLN
zb}`)Xj01Y$YVNcGw`dL6$&(9usPlyT-w(|)ghvG&TtEtER?YdZ4+-~dix6?f4USy4
zALaYc8)X@0t6BAuSv^e6v7`^`0fo>#Esl>+e05%a$`~z$`Ri&qW|d^RNIhpLbOp5a
zopS8cRL47&b(`qK4q*|Z1Wkv(s<%9XC7-G3feYsyTGts^K=4<~fOn#qN4ZigV#^aF
z0Y?^_>8D;+nsoIKJ(M%n@jD!P6$^=K$tZ0?TEh3-K(en*VKRoFESTur-#3c1+S4C+
zsF=b8W&wiu#@+avdkw7?z#6(b4Q+ccbjY!T^$g+#^pXSm)W&R2W@F=Ip5GK9>gUy}
zyV;{>VL?Op0p83rZST>dqfo(?AYhquHKImDDo{%y8w8|9YG$d%7^`#zY(S||-WX#<
zs%?K*2yF8h1wE`^qpoAd!P3wH0Si~W6SML24e@}c@7^Gw?X9<Z+8WUT3w*b@0-?H}
z!w*cVd`^U@GwPb!boII=w<Mu}M%PjCCYGAUE(O@$vjJM-#Wja1$F6}TgAKY1o?v@e
zag#oErbI{Vh&pBkez{EzW&K0wXd&pd&FM0C$d6#IX0GwT-+1MgrWAj@1{uxeKpzGf
z=C^ycT!rqekcQU%Y6*#6GO(H`m{D$QbkAyP`j<hf{v<;q&BZ(i3tlSR_Jz`0x$-IX
z(LX-#+X;oY60>F{$JE<?-99eKny3>vn)Zl=^xFj#BByUI$CjhThHrm_-z4%E4G;)z
zXAd)YOwK|0t{xXVGh{y;77_&QLSq@=wNI(R+f$~%W6+O}cqlD6pq0-v*))$}r`kYE
z(3=-|H7*DGo5v3>y^vfTsig%wa?}JAy<nNM21hU5gJnca!o}&40(VD2Mty>b8T(YP
zAn!!<bIgTl%vQ2&_l@R~<Y2-Ijt=Pdcg2?u4XVezLpcoW(geqy1L)C|k8fMr?!aEh
z15j3TDb=R5%WpNHAr#Zt44&R_mj|C<0+@QB>A3AOY*Dn4Aem4Z?jZeH#B}w9-j^W~
zUjj^=6gOFTS@?3!96|x;ogDOQI1-yqCirnx58Ow&dHul2nE-c7csDXwPB6A4rc}3T
z-cy!@I}8Dr7E(*kEE=?!bP8`bI$rIS;bL4D5KL^sEAYh@S|;p1UC<Ov#6J%h(s%Qw
z-MyP{N`ylm<e(>|HqE({xSiO8SKNdQ?(u(yWd5^;$W7oLM}BkkoJ4x{|MGDx^sD1$
z)8_khxZE9qu%iHplGTmbPpn7!<co!d)$3#9i-`0jb8>`Ks+YR+hlmuVGBhUP5U*zo
z686+Qa=UZ>%+=2w5^7hb#Sr)OF)x$a6ma0zpoE?d&=eTG-TCIuO!Z0T;Y4Ex5;rR&
zf8(#~M`j2yUpa>)Q9(8fwe`xpWPOgP02|Yg;o(Ql=gUPoo3_&-(RHnkl*{SZJYR7H
zCY6v@Kdc}z#knd(e;*MZ2f-e-0U1g7u}K=lE{l%{k7bDbdClh_T|la6LhLo((4sQo
zg%G}&+ksT~2tQ^#`2bU%9k@24w9c4w&P6zrVBU*ec<ADXa$YZ-oM5(00F-9ejk%kQ
zn}}<H5XX_?a#2RXzX}a7#8ofw(4+LmFKF8ZLeIsn2+rNbR!!il!)^$<pMz@1&s6hV
z+qoO!r@ujh_E@p5C5gb?q1k+-^-M!&b3rsbtxF>doiUzXbx%_YZb?5eqN>gQGPeEt
zZ6aHUnK>ige4CT>(NryoU!b!9?wrvRsdK?u9`1P<gtLO_TlbiHSnY5f2lVHi^G$Qr
zXFY+Di*Mw1uzO3I49<!g&%@%`kuB)>@SdA6HxqK4Ast+LL#WpcYroG*l5C8`c;f{H
z$;we^Gl96L0F>N0+_Ew-h)B>lpyqB(pBlR-#YcUq<W(N1e;S3})3vE1i=YI6vthsc
z7`uDE0TiAJ8i}ZZ9c1QA&o!;ZjNn(_k<dD}`xjo`NvdZ65jiu(M04+*8Arc7l&po@
zgM>|f?jeljslWn|2Jp3pZ;f8JkbvBE<ybvJ<}JLWcZXQaEsKZ9`n}{OHm+RLhm3MU
z&!ywCAdPPls@Uye)<Y15gy*33q}#cNnNuO;)(7go<ska-RzGNmgosgfWur^bOlsGy
zCc=7jfM~QG&G6%F(2CDD>l(5U-=CskIq0`JOD_1d1Y^($wSe%CIdpJ1Gi5PEQq*_g
zW&=xq(Hd$1OMtwlK#SSCW&ICJKf)==KlAa20>;%fNCm{7XiN^ewpU@%(X43@sex8=
zqoVef*W;AyP)UK=bhLh~`@Kwq^HWKNr2^9T%*U6@Hykt~SZn6PI_6m6t*00VQM?9!
zSCogh1Yaw7UU_T_ysl<OgBqR^9?*1D5%MCq*vuu53RIfMycZK%SBntsevHU4`S0Ce
zFsOmvNA!A=?s&sEe3S%CILlnoWqEDNY3Srppsv?2f=*kV*#r%7mMfC!wIw}X@HJox
z>X7HEGI8G)3f7zROaG-)hqMap+?{~24Wd-q4-EAjgj!E;6tEl-l9{0J8{D@W?EB5o
z(5r1gumS#T80;I6ucS2a?XQU>f^|5&LZ_x7Jyoq6qA2<h*f8&V<6|iUc4ZEJ8>UN~
zpLrWlL$!skXEIe`eWn3AF=@0k>&Nz;(5{&v*~~a2xcD_rwRpLNCCdbNi<fl!E^^(g
z;Lq3~Mgn8E7`ks&fyvCd9D68#cJC*j_MCp={*DF;r8@3<jW#j^{!R(RjXU{vh2=uX
z$umQ9(8^0ui!c3(I|^~gEC5TxdHeJQB}AVgX6Bg8^@(jEy{r2|a>BeHh%ayP2Jm=g
z)ZYAugP;F~<~h&B61^Q=j~13dWhMgsHr%~q>SPJ2byNl<4SWylFMh6E0Fjn#9nc{y
z65{4F`UgC)Z#foJyMXRIwA}?h65lIq@=Qpkx{n`&d-`!P9Zc}>N)yp-Oa%-=TWVsy
z+rBjr_oDZ5P`^M=E?1jPfj7ZmiiN&lTh%|V=Y8f91<50e&#n<&tgm}_m55=ayoX#p
zlGtIjSsi9R^hORkE-0q?b%{FWj|(N2Y!|uMZM_Ag&AC$vgc+hKP@#Ied({sGOj?Io
z=8L7pS4wYs)Ej$O5>N<e!=<$I?hTDBCNK-`0<JGEe-6xMnS(ocN&Tk(FvPR1N;9#p
zJMsh)K|t3%t#WLNU-xc86n=!L<tnAJFKqQz;+a{afK>nHa_9M&I4uI(AQj-EC((es
z(|;d<zHkKqmtJ!mVwH}B5OK=`w@b+{XR0Tl1`7ck`I&JlN#vL<P#sIVp~jdCQwUNF
zmg~J0UXbzdDPdnJQ0N_{EBEM)ye}iXl-^;8kBv7S+mxEWmpGzyqbSs_z6Wr_2GRuP
z!GZnOZj7jU@h-m(%n$sAbU2{wIxaOwrWszI<iL=h0A_?1*v>p{7EBym0kmYvjn-7A
z8n|{#xWTAAB0wBQ5{ju2iW@TsM4{R!O1n6cJB=(4Z^b}b9g~T2``ggk*GCyv8*uA3
zXm{o|XRiJv%r9$XA)=EXgxei?s<7WjK-H59$=4n3b{>Q&h^{558en)1KcW)6qbv~O
z{e8yHBQM}E3O<cLNV1f-xscmmSW8%mS=z&D;<aA;rJ4gF6$-2#nm{gJ90`L92?Ylx
z<8Rk*#QuN@auWbb$<41<+n4MhN(Nfk!^}ZHcDF<^t>D!gvr;6i3VQc!$fGtNdL{Ec
zs6cMJ5A~AAVe(>wfrP>C{L5{OKVgtl-FOI@?zp>5I4B1R3ucQSX2dZv%`kU{NC^!D
z&8J;xyuB5k@s}h!;$qV7vz}?kj_0ubW&?5v6OP-<tO|x#L-<`Fmg)3$ls#D)vn6lR
z3wx;aL&D(3q;Pc;Nd;zuTV3q@Pem7jH(U(`ZInmbZCP!@QrK(aQ$e>uMXB>Kk=~+F
zUu|DNCEI|^a+1c#6?hO#9x0now@Ar5yK9Ve1lkl8lbx4*YwC;@VM<0pG7lWW?3l${
z3H7o$&8T5#Voif_IMLnXzFchg5QRG~OiF_8z`Vd`Jy$6?6Q~D|ivv1eKcy~m$r_nZ
z_$!$#Iy(NPX;WEc58;@>pww1APY+J5hGB<;k}s5ESm9;C#KjvR?dah-xv8gKhe2D(
zfS#ZxdpN(?i|D>^3K#2)dew41U<#4*@IZj}p2;Z&%+`7PClQ-W0G!)ngXN#^gZJqC
zsKQ2scDJG0)x|(8zEjvCK$;yNDNZnN^0DVU+6OCWbUBtBNO$IXU7SHAN4E_E72dkW
z2P2*egz7pRMv&2@AK#WReO^r?Hr6l4B3z3zG^8UY>C#+KA8uAPmx_+65g8LTbU;t@
zr#F1dY~;%k;SYu%sw1o#_KYY^My#+Ld-6CbQ+01L#42EDjvjmXd~uVfMTQD29MpSt
zRJ6li$!K%_e&RIS%dys~G>`Fk+2{9uGsq_Z;Qyk#a^z=!3>0`pIp)l-I+S|KU~*GH
z*}c2+-Nt5c6X6!<A8F|RlrEZMSW~{V#I>nGIp}sqzEa(Kcu~OI;(^b$%@rKZoCOWJ
zGD3tc419j`qje*k9wV3ZghY90UJ{uy4!}!x=Atyb=w_$)_E1+C!Ql=gCeO6%quz4f
zDZ{$2;DG1Jc=XIMG$2Ox)}^7lzTNVbPOXKM1i83}c`x&%rAXk!haja)OVDtnWS6N%
zJ#1Q;Qve?mk;y!eauS{uFm|a;150no7^IV+jQ8u%)74L>9=mQksf(2kXmI!{(%v9Z
z?;nzM%hUwCO{ryoa``?TNGXACH=ySzy4s;&HADt$%dv`t>A7y5yH>(SCSbggA&)E_
zuPf#)>nrxnT4&YEDD9z)4i2>_vWKBiGVNTtrq<mZtrd{frv}$CpR|)qzFwaTc?_hq
zhcE4`m**GgNWfxhOdUz@^w5{q=5zvQP_Nga_GdrmIGZ@@!}&%A$kGgSoSUIA73K^l
z7d~smB4k?XItVE6#o!L<7T*6ck4GW~JeZr)@ai^|Vv_ac+k`V;0)0Yrn(8Cp;l3?U
zM;adZqD1enIIBq&V*6n*+|ynW>SA9sN#z&<<Td!7*L1pXG6ogb3$A;I8_Iqe9`Gg#
zGXkbC7FNc*)qW)p+Xo{q_BiT-*Qe?!(5S&#0!QCKEqOU51L%OX@*bJ*IURc&dd&!x
zux|*kDt{Lq&6}Z2M1B-#mEPJ6pQ|miYKF->0I)}~ESB=P=M1>17QmCazpQ&#*h9^U
zhvqh|9(w=pk`C5#7^LiLb!cMQYZ)Q^f6~JvfOn1e>miqptIZNdsR%14h9on8a!J(d
z(koSa^dPB31Kfhzd7E9A?Fm9((E;ywE-=i^OkF}8xd9y6i_uuPJb>YI3*s774>XIl
z8rHcNIxi6^EE6=(0cpscJR9OFdnOlK)3i!Oi+?)=8t5rF-&sJtTI$3SwpEG{-R`wR
z46Bou6Z`6E3P{H0_%rv9y&(kNLjlk9+*;IHed{!I_w`)tSk`FOJSuUezP}tx?7m}L
zFDxpg5O4Cz0XmBb4KL+-`a}1T2KCUiimVz<GrfuopGqy8;IVB0E0ADJpGMWY0B1nh
z!8zzGN8d+7#XI1(Tp-5UU|}QU;=Wm9{Z1<DM)*JS@#^HCVc*9e(}_Gvs_db3hPkXv
z8$3kl2`OwsI32z%foow3W`mBT{S7m6xF?Ye^^^zPte0jHCzlWV7Sj73>SNupvNYNz
zSp0YuB4NN_x~9?o=CK7to;C!HKyP}vDg7=461=h=W|-4h`YZZcm<>&UUDulbOsd`v
zZrzBiNdhYIa?GEVYB))51J3SCjH5N8`~Wg6=pP>Vj)2C~)!luUU?yGUf#;4E1h*#8
z;o(T1dRW%$#~t29*TdMk4zM|Gns2X_w`dh%{p+A-jTs&4eph`J{tgV#rE5_|tNO1$
zARavyIif?%;l6JIHoR940COyBANo02r#^;1D*%fb=(aVO%0`(%sgTGzYKBcygIRaH
zADpNF%A|B>kBy}4w}UZe0xk{>3`TQq{W5{vjL8N;s6%+AF7}~~M<CLW6C)kA-Wo|?
z2Bs0Xfh6uF4t$6SN|^@VEe0S*RXv~P{qvX7gp%4x0t@eEv=zY~p=|7RxgXWbk@?Wq
zAMBq3NT0iPC_inf&NU5XKDgbpI`JAm1A>fS<@&dS0p%+GdH9>9cwU|ZfXwN0mGx*7
z>#C%OqQC1QS+u!VhS_Jr#9b`D1U(vB#ymj)?i~(**89|GmNPl&_7RM3?i_rml)FV+
z5)&x6DJs`{>{#h$1J%L-yugu+3UA)9HB>Tdy+E;7u!%NwcPjBnQ&5X$sZfg&Z!a2k
zOVBP&5Dyb0{$QZ-+YJ0WE58hKn)K2p#8a)Em7Dnu7Daq1xUc@#bTB}^7lLL)jP&}s
z)8^K!GbS9n1KdQvZC|sv=A`YP(C_p;@F3m8edl;1@F-Q;eB7Y+*@3d}>{$@NQo%TY
zw`l*<3O8a<g9-qBLw^2+x;b$~ZQ}s|>vwkR{Wp2Fvml~(1a`D244jT;CC`v(310~s
z)WM5ip7FdOfE7;-*#5n7nMbn^;S?0GY0{X<)^ndv5W&wB=b_t~fz$Mrw@V-a5}1Qd
z57yvzj~i`}NQIvRWoOCZpWIKug%A#lr|H`B53V>{GzqwwV%kb%wvv^)diPbl<sS-|
zWzB9BxqW0-XZ123L57Y6TCy^eJvN>U$m5|az>T2rT<0r>@*_AY%<5=HV>^n!o{{k&
zLI*boJ{YDQo_Nq<9c-+eK<bN>(TJ-4F`p2N46a^oeQjG}@cQ3%(3O_+2Pvx2YMFk=
z$uMi~1es~i_OUC8(+HL0x*+AqqG5b>-93b%|8_vfV+~}v=c6W{foVFo6V^Yj6(Htt
z1dI#@y&KHc40GSCoJYJHUyePJu`plCADlONJYZMbw;u6o{$&I#n3$-W?!PvmW(##D
z@wPU=4`r`Q&-6MT{K2eAHnX1?BbF`4q*R_y+i5%s(<h@<Tu|306!D~?{uqFdeDf=1
zZLqDvB=psOEOKeMY4ER~%_ibBjP`u|;=k54pAC{oaDG5M!I~~@ZdS>M>62Q}grMbf
z=e{M%DXI>jX-jmCT&v;yAXtiVU0|UazJ>SEHIoIk8zc$eo;bB`{Lmb+mzCm;vqxya
zMlZT*q%YNBtD%il0HL03Y2demNE55Ta&9Y}8ogdmA!@O<mcS`l%~ES1IB@a;j{TvB
zI#O8@nQ(|Y$#)z94Z+5|BSR%buAqS&Jw8%vsqmPf`!ij52rF;!^=D+&q&37>VS7}Z
z5+1#T*pPW0bid2N6`TdtLQxVcV(!k%zIr#U7Q$}CA8?PixR<(3WFd>sE(9@Fs6Twf
z(EFtZQD$gb0>(PzFyr$F`89o+(N+1e6MWuz2eh`j1klf-6i9U-fTlKlQM0Ts=HWPO
zYYc$XYu0K<40aOOI}dPtD5-DsI!O(B1&`y0ok?6UC%nxPgD4dN;Grw#OhnG1Eii)o
zK-ca5H2CNC0r(;b45%3X2r<_7Qii=Q@(D;$m#Ju?FPc3KyYcH@(F8mRh!9E31)X>0
z`;(Ty6vYN{ka;a~?y>yWGhvDX!*zp3w2gVMyqCdk=L6Hy4Q0O$fT{|wQ7Itku{bo%
zIkQ=28!VVOm{w0Z?v<9x^nmRG3a$c_W4`LAw9+deL@5RtDH8jhYn*o&1~>=k#QXgD
z*U42!33sOhV6^f4g|d5pYFoj|O>yP3dcLN7L(=|T2~TVQkepkpdC9aqSVAelQP`qW
z-8Vp0g`gLW1cjlIn!-~?CUb)p*p}9NChbS49IOI>w4+oeG>nTz%@l9^9{pp1q8KK8
z?tJcj3j9?E5TPr-j=LxQDSSk_z(aV|IlewstOnTavA~7-65C(5lN039A#SIFaOy0M
zy|uI!4w&HL@w!?||JcYj*i)fmX8fD?)8$Shle~iputPDs>lOCnGNB|#Fb;36^zC)4
zYR&4tfY_X{lo7PN31NnLYYs=%D8V8?2a|-dySy~FI8vd?DWE96yfo8l-q9O`uZIHy
zB(tQhgwc6>{a@Jlr4GPYhJ&1dWBW@^5L5GFrY`GyrT<O*>r6>};W~hqN^XeZB+wFw
z3?ruMN}S%FQV)JkJd+3UFxS|9U!!ZfG&JB^aQ$rkrs29>U=FOiOfW^6qncA_-3#vu
z=q2FelQ<i`sSMGWQ#twgqR}Vbr*DJrX*jcT?*Z8hdnl*>O)C!mQ3UN$3vO+e7;5VO
zv+sxuER%D9!cRPI=@fl~fh4BsqVk6l$6Z83D~4|Zu@K+nZF^aVvj)DZ4Fqs+>#W)A
zX-k36#KU!V<dJ&!Ck8NPG6CIB-FSP3a5U2v=7I*`ZKTHAyM)XA35~PGP&EIkNAS`R
zcoawQ=;gv1$7B8sFBqvj;9NA(ZW6w#4B{(h2l&!So5`#0y8ul}0$#Z)z_@vF=p+g4
z0cbhqxXG#Q!r8>n04+7xAmXaegf#;PmoB`my_k`^=(7wosTOE|i-s}zX7(<k%z2A*
z*JZn{4GbwF1><1Au9c?{HrAam6^1xq>L@kBsX20}`YgDf8USlWT2jg{n;>U`{|OYV
zTp?BCH^90IwZsP3PQha?pZ|TDNMsu^S+`)b%-qM;#7L@)1Nyz|z0qbCQwM(MhKr|)
z+Xn7Eh_Zl`FAlthk6n8@lQ%MX<w_Mty`fG|a~NpOuWMD>Cy7uueh8ag@F1T86Bj?K
zq7iB1&*hh~A8hv%*3SqO=|qT}ooYkwri>3>keD9b?||+prv0O3<xJcKpc)S7kI&jB
z7lW5hW}3!w>|=*_Ncj@c*bMltvIAaF=x@Ap<+>$sx|dZ3iX)PxSM^I-#yME=+Kpwm
z?%Ybewrtm?wO(I;R)2}8Kb*eWB>mdM7i)H@-O{O9b}MoF^%tL)-8{Qjc8S}|o6Fh{
zUtjh-@=!ci(BsXs<X1I$r{q;)KaO=D3!x#*5bri$HvTek>c5XN60U`quPA)oM=l)A
z;d-j*pLUwRwh5_Rd;jOr(Hu?eU<c_&$I|f!vkZ^iUR3;n9o)}CheVFcEo`5{7b`|7
z?kU<A1_y<#1AVXeJli6N*)PVW$kfHZx?^_43yP1eJhW6A^(aDmf^Te_d9~@qMSM%r
zjyrZO)JOeDeTv<V<2iQM;j3++alQQd{chirD#RlNym9xJ>jp({{*mm-KQ1&1&mZ?J
z&o#MzP_n1&VqB?H+mL*e`DhP6ByHjxvI;1^u<Yi?-c-so((f&A_p?fqHV<`X2O7ck
zV}sDogB&9j<%&4T`i%0?gu^k0>x$#F!nh>Iuw5?egw&J5yeSQ{L%clk^^1Ix7p%GK
zkII58^n?qS{ra}79X&cBCAo;5$?BVLld}mbmQgyY>QlE&)9vW{8It#y+8mxHSzGhm
zAyo1nel`fSXwt^YHiu%MsssA@-)&AD?jGv7zxwy;cvlcwo;@}0>s@pol;?Us`SKRO
zi>Ga8!lfdEP;!XeK|QQ_!xH!_&?78qzB|BJvKu~$>JOPQ7hlm|V|-YmK(ZF5L;5EC
zIvi6*ee%O@Lg?=G*z$t?z6!GWw+N-?2chG&T1Bmt+3{aRoZpJD0Ub@M^HA#zBRi;7
zK&7d27{8>tR~>j?JmF5Yj>4%SAxjnA*H13y2NL8ojPozb)RL1NjeR8t-P0EAB;tD<
zev3v=cqi>s6Ot|;!cwP3Nz}uf4_c5o)4it)qWf1vhZO)A9B_W2`<ocq>%`gYKwADY
zbk!)@c$bi2AMmoD-3u2oFK_-6dMN%J+@W2j{p0k(frtPp(7lDM2|aXVGjw;<KL@?*
zt6!(aDg7I|fmRW-o9DQyICkGNi36cw{VX3jr0xB6ej@aC5|F`mxmiJJ_|t3j0|TF=
z+t|_ll=CA#FHL*bKqD|*_>>Oys+*Wf1QZl}lVira^1V@pOEK!iTqfcWxz*1YE=>Ra
z`Kc2TP3#BWpy&U};O_5>hsOX3^PAuLJd2;%3}_b|0FS9$s-yE`EhUM@N#&zC^qBEW
zeXmF0IcT8L7+hA-XmglM2jXvh4s%aLTa;WR6qI>Vn4`Tb=Duybf~+JcoIp?{@%Azu
zY8{~)HGtK8FPw1Lp71yECLY?)Qhu<$%zmpj@r(&Rh}f8C=i^%a`WW>23}6-Oa~Z{r
z-!Dt_jWdDuYQNQOaI!imaji@{q1N4e-3~5hl6DX}4I1T*ceV%X<Y_P_J#_|$e)Qy6
zV*<b1489A9M@xeRD@tu510{Ony&Fx)cU{qv%l3g$b+=JKiFwYF_Ds@(f~poF8EDkl
zm<^w0Bw5SDK`3oEtHXn&c~@dS9DJ^QEZ1*ayG2E!B&gWn>n6*F<0p>0zMleJXf=nF
zjd+|Gpr{e{#FhD?0Jhw^0sX=Gvce3LB=R9~qUM37y`@u+{jBMe?G0#WdOy=@<cD_p
zo{vx?9`-`PXyLlMj4?UMd2uNq0)FoP=$p3CLLy|)Poq+}!2RUO;$2YG^Wfgss|{S(
z2EUM;I0wX4w5@5Jod$WEL{jl5zg8}1&tTetlP8|f;qj}2XgT=2Jw#N+Hu5QM^{bRd
z(-%R-fzkGC+~Ux8+CsPiE}m4=RxiWUZb7%#=FlzB9|IZWCYzfumhoaBg8JYi+T5c%
zVYUd!L7S<${~Rf9Cb9(%<_rvZ50<hSnzF(mTOIO4JOO;H*PWqxd?@)8R5=wG=KgC<
z7k53v{{l8(9okGR#=9#mjL;C@PN*mH;^qAEjUr;1P0vA76Ov0=N<@Lf|7;X-zN)Sq
z2~kUevDVTkGK(wi+Rk6O2D&o~VCyLkjJXVUm_$-^83^!zdz<Q<OAo;KM(1&Ob-c02
zjJy%z8FDQLEuxJX(k`rr7CjC`*tliW#u1E9tgnEN@)+?B>!?PA36=+;_kB%kr}qeN
z5E;-2076&fX!zy&K;l^exFoqP82{1j_U);}@&TX^YAYX3EYyQpJOuQbFZm9Ul;D40
zvbYbd;;j6<aC*EMA;8}p^oe8F%?}zzgnJ-h<kWb$DQkVt_;eV`r@`<CzqwV@@QjK?
z9mt)2T8{n+v(|sk!}Y1_u_%v=jnSEjONnyK1dN%kO;5mQ6>sf0m|~HeIVjgz)WVHF
z0^j`ttYUc1l&%`$w^ic0At3%<`ni=IWL<*4;sC2$dxS4;d_y93DAvNh*myll$lyPP
z{)f)yi>hXXq{MhtQcNVPQ9wRDBglLn(z93Mk_hP9&YXyH_UqSx>xVAJ`}X-a$fefp
zC#+J^$S3(7V;Y<`af6Y!2IM+6YBG>`tryxe5fn(Yo!gh2c@6pt_!`xW<*%n2-U+KY
zsmvFDT9Sh_rC8l3tV!K~{n=~Ey+fPNUkG2-<qKqdJ-BPXwyCe}@2ABb9Yre)aP5D0
z8t?cRY6JJ&!WAVP%hal*G;M_<UGL`~OLO%|ux}xvj9%i4FHftDy%rqq5hrmMNAZBS
zZhC@%90)_30~$<j<>=VE^aD`Lx*(?0W7K}StQ(P+@ugtTw-hb6wL}f2gE`@p&ArYI
z&sM_3F@-Pa;B9Ko)M@3~LJty;6mIhr_y|%VI;68e6}i-pjlRw!0+xBDABn7P+xjj!
zS_H-P2bn1|M`^F6LH8d}LGwYk^X=Jn?&Dr~er8K7o-;E3^j9Ej?mrSS<+U)qJDAI-
z4(@3v(+)1yf6RAF>P0j7WF=rLE$PYI*+~Hqqnaw<Pwu51Ye{(oqX{h=P4Lkbghwme
z5ND--1n1w}x~uhx3KLe)aB%l|t;1yC`CbS_sFkD0_g8JTsk`4pFH-}JpWoaqk=eZ%
z0}6Qxs3)-Y?(}GDNj|_(tv+hWsEoR`*SgM1Rl=P3T`(-<ceyd{qB)enyirt@)pNW0
ztXl{)uo*}Zz2yrH8JC{Gpuo>^`7H0;$4c^+4+-aU0s&>LU#_t<V$$Jxpy71rD(PD@
znG(co{u*COwnmoU0*R|JAAzB>5ksZ)<(-S5Rg^q%>n-lOybF_1f#E3h<y|cA^h%%1
z2~~^nzcPP1Y6kX{Z6gv9_}FapyxzhiuQ$Q0X$%00+xNOSEQAHstL?(XKR5BLyEAKJ
zc1i-91}18jWC^Y7RYlMjP>}l~8#%d@H!?(;$OI5EQ(4}<JOWjgh=8krG?5}8k0xAv
zE~(HI05W=9<mI_8(0=UF(GKf1xxc(l=uNJ*0b9QFtHw?P<w>pef;5pU)#{bq^cI$T
z_WLHJ!b!L}d%vp&R1p~7&-EV2l%fqm42pwq%BL6E%<Z<F%n3QbEJY86qF;U2!wH<k
zIxaM5=Lz$MgBBB&kk_7%M#?=7&v-daR@fL%v0aTD00#ZVm$3a-+O4oUfW$;s3up9f
znLl~ny+wFuoUMh3R{M<b5L>Q5MpmDpe$l{JmdJm;z`K6+r7z?0p?dkC*yP+ETXQuP
zdbAYa(<x=$wmfdB3A8A_4B(=cs8kAeZV{n5nGM)um#H-Ui0TN!2LrNs^wgKWS|^%c
zD#CDB$HnSm+|qVg(j@s0|B#1Y{KxeDXQMEP&qCH?XEGblx3$~LK7o}5SL2JMYMU~3
z4d{}JAB}g@he!iR>1eTm3;EEGe!ludN~*-iRYf7_SM(je*aN@pAlGT0I~BN|`8r_;
zkgsaP&a{{E5TGN#lD$!~(zEuE??^m_1$6qVyYSH4+0avv5)oGLDZempdM#{F#h1YS
z%^INzHmmPS-a`O{a=M#6s?lXEaYH(|0`Rso{rlWn6Uh@4)JKi<wVke~Xft8{JO;p6
zW_s&qA(6`50BUhKn%H>qDl`Kabx(+;I|X&TJp>gA1`eynA2@9b^@Bf>2hFyIL8Djt
zWW5A8U3}6C{XLGu1}y}LLpAuKoHGJbso<zduVF97U)Xa>TgzRc)J(u%HbmtFB-hza
zhjFFnfk&^sKw&N24@Cr1lP#9`zxm-y2%Ln^;*gFc8)x4@+{*re_Cv!2A}M-k+mtdQ
zKA2#3RQ+eEYE8;0yAXdr!BXHppbXu6psbuC^9UArs-w_Iz#Y0Zw)!UFTH1h}3JOlX
zp#3ih56k)D@Q4C)kgpCFW+f`Fg;_-!^eLRSJCx85(6wkF&)lm^I`nZQ5~?i|Tsd1O
zw?1|oIK1*9BpZ0^F<Vr5An2{!9%#C7fhb_lLbVS0OVD|k;1ii(tNiLFM+<vsO%i}&
zDVy^ij@7S*SzHjrEHjy!^jCiEWN64_vC&(}y<wuxmusO#0|4yTDoz^y-VBcx0<y(}
zw}<yx9e6BBGx!DoAQ~ETot(>G-httbF6U!aC9`=!^aj{{-~b!6RQ@`P$<sYy>7cL2
z=I_K`JUW_1tVZU7vAQFIEQ>o&U?&6qodw^L-gPjz`@nljf8O}^F~m+-Wt3FicQ*Z*
zE<6Se_|<f!#Q6<zYA~z2fkOA|UZlTDDXgvXx9vfX(wFP-z1#qZWch5P9JW&dZ=GXx
zHv8{su>F&_6B?vVuf5yo&XkiZ&)mYHySG^OHuh|Ss?Z2RQ$EC#1DeNfz#rY<;9WRS
zhb)pD+Hxdp7S^<)gSr}j5QLinhxC0}k#b!k6yVlOa$`E9Y9<nam^osqad^#~ins5d
z?u4$<v>5+2U#9!{+`W)FB3F!duy&=$FK7yad(P&t4dQ8{p|RR-n1h=X@DC~ST>|G_
z5dV<NMg#0sM^`T8!c0KD+t12xdcEz3zn$M|k*MsquMY&FR+&HNgp!IkLul;19`kIr
zQ}jA=jv$z>_RBace_vgMyBJBF4Bc6TaGIim&*td_?Ll$Z2}XjsfJ0h;AshAaKqQWb
zQi>24*N`Gg0CfPyk_AX^G=s<;<q_<~FsK)B=#iWAO2*7%pemAp_Xjj=oKwfy4yy$!
z?iri(m*F3?o<oOy2>_Ol?-e#@5|i@Vxg_47jCIMM??II<0pERpJ1uuqbxf8BFJCM$
zXE2C6=EuHVe<<-#T%vipdC-DqXSG+JowjYBcl9l$zh1B1<>YVNn|E#5jFZJjP8Pph
zwsK|l*{GKbQs@}Fwy6Ub7<L$T`2EoB(6xxo8}PbJ=XDH<z!#5!YUU@qE{bd4gkEr!
zA|#)7*qrer7eKAX2_j4$T)1tZ35;hO921wrlnE~JAh@&7VQZ_by|#yrXZ_}6L5Au-
zK{qA~8sv%Jf$4S~^Zfd<CM#qoD2cyW&1?OPn77aCub3_;HVFrJCyp_OrlR!Kph{*=
z@S*X5lMAsJ{)3f7hwfvlg_)JoQ~j97O+oQLKf(GTz)I5>I{hW^IwZkD;U|RhZ@iDG
zB#v!^)}<98WuJRD`3mlek>GD1SjQpB)iA!beNos1A#Yn23;ia?=tVqie%D7pn|c-@
zC;30?XIj05p=xnlsHKtd(yXUi>M-~)U_v9fY7qD=h@Gey<M(_n$h=l!f)&3Lgg-tP
z&^@j%fSfcFe53Pl2JW+)YjFwgY8Zr`tx=_StY%(^exS)AefG&?f1f@T!VTbNIYw`N
zU6@Y03DJ`@vF6Z$+9=Iao$?YtL&ZdaSE_CM&@z}(DGGR()sh;SV*(A>nrtdU+FO~!
zWRkAfXMYRe7{s&~-;d&oai9nOZBj<%s7d~k@5@B}bPW3O5>RiS4255<+f2k^E$D97
zW(v!U3caC<YEKFcmb`KPWtOvyuxC1G;@`HPd)Ro+od{0AHz{07KG=J270g|iz^|Uy
zIxp4kp92M8Dd5FU*ja1vO#`$i0`jMk%f1EL7O(>2*ZL`MF>^Css7xd*Y2Ba?q54me
zYdrR4ktBer;{PkyoBd}jqaLPz0hl+Gl{qbUWSP9IFv41%s)kPwLbs=6MtX-u)z5~J
zzmh|J+1uQHA0sft9MCL|k7j)OHIS<V4F_5>_D2)-x54$W!DdJFQ(vDle^Pu=jN&p6
zt`kN9g0c53Tk~Q?%2t@A%s|Gtmt)@b-cudA1)yjKf9**6;PL$6dzq}}<gkgz1M`xe
z-ag$Q6OIvPqE&)xMtr9e&-ykIP?_M;;pImu_0@Vogg0(2LRLPZC{l3O$)AC^Gu&BU
zlyUnE%>Oii))uC|8J^X<3;G7?RD_&%4|Km@HE;+<G3nzdsbR>yuPP$67cxlBAY#v;
z7ltawp*5YsfT8<Q?+|HJD(+)3+xT@Kt$4HpqmIpe8j_JAM#&Q?fmEJ{4>i}%O*|$b
zHekp0ldUt{im#k3BqRk?r%4UJYba~rpS#c@?*eQ$b}?c9pXx9p>j7GN^ke1jJOkp1
z78YA&>z-=ZvJ8fRd=aAW;2r+GQ02vs1RrW#;DmN90HT^IdOJ=l{dG-J4|yQ3)!y+i
z-SRqB4O&@`gAcX*JRg7F1G*pYeIEWW{Sse?j6igN-|44lMCJvUU};SU`+S=N{ZfS9
znwZ}v8UE3hratCR^umPjY;fOk?E|ya1~-?1?mz?QXr|;ZY4DVS2PPFE8&3MNidCe<
z;PJQGK8#u%ZA`JKuJ$&7UgHbm$Gw#olkIhe3|B+IX9<S2daYaM!Nt%bWTeB`_t2qc
zF{v?;579pWFe*a@8^;G{NPdQI1;_Bo*}lN_?sMX2;9vfZ&hD}*&Cw(xTl^97nNGjr
zv*&ssDJ=uO3b#!Z`CRiNPK(^=mr<(A!0&9EhO1#xzmvR?0{-Thj#O)GWFrZ>8xH1N
zZ|iM7-qTe=IBEb0<^By*rFoZOjzqU`*lXGtR}Xf0h>5%jg&@}eiN3q>Nw~~(h?qh^
zaw@HJyRhq#=Hz0awRtRKWH6YrG)Q7)67c$Ajhi<Ge-ST&k3O7~*8iQiTLUJd4FIoR
zZ(g#bx_fk<M7Q|SBDC^ho>53J$%HsR_*a3XBQgr#+`bJyg>K3Kd;-(s=s;0L0Sp&0
z*;L7xHK^AdC~++08W^OO>J2DPkcGWC{T&F6BeC=Uq_oe0z(oAt;a2QcS>U!2qA&VK
zFdR4BucG#_1Y&R!m`P7b@+Xh3aQR!ZJgx}r8Qb3y8*}p@R2i<$VW0M)A8b)6C7vOV
zb5J=s1)Jl0%2z`<7Xcs2($sY|H-Xwn1TD74+48E7xiH$R0N=7r^xA+CyHtFm_1mp<
zfm^W!#jEi7+n|C_FhJ|G-Y1%Rw~Md|3ow&=Wb^k6PB(0Ud+r7QF~O&!`(2kW3}hx4
z<A@C`Qq5RvOf0sMgS%!~?C`&8Q4F}%-<69F#-h#)b>3PT>IhK*%4$LsSMBe!uDLSH
zZ`eO};Y0C7sUx^7klD1V=sdFJPf4an0zmU(rLa&sT9G(0ZVfbtjPJXXi_aw7elUxN
zUe4UnTBXg~za8omq4)Deqn52p83Zo?I;&#xc+|SE0a$X#z<_t>Fy}b?j3`23SAbH8
zbv3+UtN`5FZz-tdqw-Np4R!juEeSo5Tk`P}_VH_OOULdthMO`U=Aa`@QHM?lya;e0
zCgDO%6Jm9aK<t6)c;Ki0+Lz?pJ$@MOIt#dJvp%9eQn-$I(?S#DOQU*uS1jx%pNU7?
zj>WuKBWnN=i3NzFvV?>2LSq;*;xXVW!CLJXU+n#D9h^N|SbKqquTLE+Usu3mZ6yx;
zNPq=QOVldJw}Ra<^KO3#d$S<t&9HOgj}F^oeR>lR>L@^v4S(yNqz-t%P#Ok>u}m>d
zGj;D>V@Q92xvQ4Kh+{FDUC`dNQ^N3E>aakoj$T1nhg2-Wj0ywTGOL%uIAH?jX-P_S
zjAhDfnD!ism|Fv6IyRp5blWTm9aNfwtnB$2-6ZOO8x(^<DP6a&#vd-(P$ij~m_e<e
zLT@uZY7I5K83-`$6U)nRfnn01fWJ~RYA=rNhs`lXe0&eyv~1LKBWW5NZ*Q+HMWr~T
z2m|MBJ`Q9UaWRcNc333y)pr32dJ+$Ot(Nr2@!*hZxI83$J;1km(^jc_)m_kq5-Ark
z-SPgw@HZ?%>~N53XfM@E=0D1U-3^$vz-WHL2YrtVphOEnj>;H0=7nS|g=_g0u?2hY
z+nbFPt=ri-NdBG2r9k)1wK?|4Gg}4zZX0)yQ7%{Jjm3t;{4<Y33e|47Q^t$7B$y!Z
ze+>C0S7ovIRYavgZ-B1|Tl&Jz(6O{BlBh%<_OpK3o-jZDBO?qtf?W_g^eR1R?r^s$
zL}^n2{tBv0Qu8r`Z79A4u;j@W<&QlBkHig)h5tt83xm+E`&Ap#GczCqNdeyq(^sY5
z*6W2A73c$i6*I$~W;N>Vf~U9x9>v^9H`6yJfQooZk8ujW^w@vxq4)es@b7rr#MD*S
zqgyd%%iWcr?CU^lM-8m_`idX{bO04CNVg?@Gax~oc>-MfsYmf^QM646%%I3)V3o0!
zIadcKGXwJ-V9P#eJ0yFj!?cBp|4D`Uoo@G2#m3P0R)Xbnx;d54<`QiWuE`hDP5ArL
z>EV-Y-z*N2ao}o&i|6@sM5y3kUUoE=-T2~^BJ{r`0JVNLsONOILX1NJ_YK|c^La`>
z7o`cD=>%?p?A!~<i*+NR@VdY(joYl!%3)@>>yOU?Ri2!u?od~h0u6c?#7fxP3MYyT
z+*15c)DAVA56L<Vdt`hSSSu}hrUG*njR25nU_ZZP&}jP*b073vP>d3U-<6d8o1ZNp
zp^Akca7UmGU6%Xoz9u7RPVxUrYI+`%SYZS!3?Q@o14bHUKWEOC6~1m#&Od^6g0g>*
z#LY*%+BBeM!3@bUZx@fg#^dm$-i<suZ|?WdH!J0#HOWEfBU4)Mu)!rC8K?<EP!1X1
zeB1mM$aCUX`YEi<7wLFfhc`rV;!ikt)P20sXf*<RUlO>jGPj92n(RY^TNZ;YwRx|Q
zx9?4e`hb^;Fwb4FQw@9Dc@Qt)0K$eon7dBlYcxED2ROejcKq&(WSEr{z+yB{mkT}h
zJBX~0gDV7G1@u?{(-ZJ#6cFfN1xx4V9~2L${0=4rpnUWDDvv|#<`VpwCKfvl)nD)+
zWg*lC6}Uy)ln`4D%#bLdtSmM{yVo0m_L`XhivKs`fH(?Oa+J3`YA&Zsj*Ea%XyZBf
z^3|<$JLoI&fB{VS&~LObQ*=cF;_xVeh?(vbB3RdJ3gH2c0J%tAJ|_!PPvWxp_1`BB
z2PUn}d*EuF>osK>bEIS^BMip@dSO)UcJZ);8J>AkxX^8%?y=D@RVV}#w7;qCi7%!E
z?1$;r;5mk4yVDo!w?Cq=TfD8@nqYriF$f*b5K#g)CJ}8E9u<Usf062VXuQ!N_!_Tn
z(@6RNa9j#KhL?<gT<LfKD!}oC@cL%DLDZ{fo5Bq7-_VvG){i_~<e6HX+5=5N)54lk
znY!}6E$b(L7Tizqd&|i!=3J0;n+6If(|T&Cg^*X`xfa51;N>p%rZ%4r9yih?7#vpR
zs8qG3`-yX5+(}*|CkQ<mwOO7dwdl#$4hiMrwjOJKs-tnK%MDurEu_j}9<Tpm`nV;S
zfG=oBKi^tw-1XBr8N#F7Hej#y=(hXo-Vm4<%$Hp{!I8zEZ6lnx<AhL!G(5UNR4-Cn
z4gMFv1d>F^9DJkG)1+;baQqf=7Pg}>ZjhDj^()VvI5bn9!#fy%<_@Kf{{sA{q~9i&
zS&V<aqRm)Y8~p&bcC6yjgpp>q3Pwc-)ae+2>Dzw|XT4{oJ|$kA5`=SjBwMn+^Fl(=
z)O-EBH)q;{Uqr+b`=ZOie^glbKCxMk)(lBX6#OeEH@#Yl^n;G)&|{<l=q%$Po`$m~
zb&8x6ir9jm4oAPOgJ;<ZZU}khQJp*a1Tv>S>?fO}WhtLZ>Cj2#0VJ->tBT$1>mw~m
zYt-m|zC!e)Zz=q<FjXuF!tW#wqWkau+6p5B-Ccxu>>X_I3mhaWFZE78$$MFM%$uyv
zOqh$}!M}f4*}wnYZ7+B*k`{JQCMjMO6aJhlu|IP*hyJ)Nb7&Ts12bu!0)FowC+@1Y
zb;La5qkc-_mrmPDyao@rdfQ^$cpDF2U`1;&kR(@BTm@~l{QA3=!n#Wp|FiB7xL@n1
zbA*$iA4W-5LoCrDGgU}CSpZzE)S&$Tn7Z;nsJi$6ZIw#eWGUt?NhPU>%5vWpR6?Z)
zF_lonBq8g4OQk|nNh-@!vLz(hvfLtDVv?<FlY|gMmKkQbza!4^`}cC^oM-zy>pAz6
zxfL8t9Iv-Uuz8=`-m%3G{%9K?BH)eRQ0voq^2|pI$eoWtI3Ej?i_sp%`YP43I=y*q
zZ+0%+<CGdOJ5~LL@+t9C#-dmLQB#<+E8(BoW6Ld_Qbi8!jJMkVX1~S#w+)A#!yja<
zjggTF&9FGc83cEC(3y|6liS7nygRgMu8iEip4LZx`J-Ye*Cs^Us4+gQh?(@?RG1AK
z0yaPSC5Uh+zW^Uy4#NF!yR!8&x_3=VmKEepN=grX-$lf6COAkAv%0g$UQ(b!;=+n-
z4(_{b>5a2(VB*M8Vjj5+5cW`gR+>oEdH|B?x31v^#Vq@dsw+=J>><-Kf3%QEQT@k@
z?-fG<?`+}`g7heH7QfFG;%yxWwniP9jLH=-KB9p0T^Aig^}n~@f-gE{ad7<p+WTJQ
zGZCm6%58Wv@4!kLb2VhQdk}t1aJAs(R@8#Y?99izo8IgVeP?qGYKAPRCUsliRmf|D
zE?skrP;!l~cSlJLD!YK`1IxqNXFfHtwUPe_IL{{CeazLos~r&TXabYdpBGrfRzb5^
zU^X&2L&c9%ZF~#**qjS&vX7u@@#~f&H4E4xEYn@%9Vt}2gB+f75TRf3R^eG5#~Zy0
zvZ4Or#gaY2u(L5<66(>;I?by*3QYo(e{OD$F_4w=i-9>gA;}TaDzgut4Do_#6bH1k
z8-`*z14Y*$<vfsCRdvKH|Glj*c+%O4+wlE~T<Q*mo~@9tVL<%YZNK+}4%+ZhK9D5-
zBJ?IHD4qv~#3oASJkWpEp@t5uTZ}{rPJ<hUJurN6=sFY|=0Oh4T^CkA=3@?np8pkN
z=eaj8RL!uPs<_-4Fb>yA=Z9uJfI6@U%mp<)*~Kd&9-#OPy8#kZ!Le4ak5X`F?}PBZ
zY%!ga`o73vP(XW|mvOGBEi8Xzd?sR|$W_Me>=Y!P@&^-DFfpcpKK6shmg{29ORnci
zMtPb39X6zZFvrb*y$J90(Or{yp*WaixDyas9ew~dqv@yk>=BAtQJk4PQVxC4>~q<g
zxF$H?Y8FICaxZB2>9USgghx)*$dy3b)Ymip=h1d3(P$9*oUZSd<LM+`pA-z}-U_?E
z=-0+Ygu@UIy9FMFU1IfUSWa>7F=)cCI4J+Si<XZhV*^SB9HZc-dk95APHp_T<MCZL
zG}lqGMYvAe*E;uemxCc_5>4b^ZBlqj6*X5%>zo1#u2R9sP?{t+ro<!q3PZc9+<1H_
z9tVd=&z&<-x>&UigL0h-!tQ%!N~%t?VVJWA5TCZhfyX!p%h(jqyuO%b8u^pfSBL5j
z*v$dr9-d_MT}2);1RU-&zIK{?vnv$pE)_VaV(o4(o;B*M8Mgv+zZmO$>fmCy;*mZM
zd3{YyxfpG#j@APR7%=Qnp}4)2n^a32bjV)jnqk;xIJ1gH0o8JLS{Sj+a2Don6FK{D
z^Qaog<=mb^N478Pf0NjW`RCxy_Z^mcxw!2{d{YIeONQt@;&`Eaj`2>wVf;z6{-g49
zs{XSaOy-f?n4jGg<SWO|vhL^;@1OJdKnyt<qmYK6*L(f8Lqwgt;{Tv3jUPZgkl66S
zyC$O;NqW5SRFH1Cc!|iAMu6$ijA7@;IZxG)3|#>od!D{)_g<!f8fX|j2Y*c$>A=5?
z&&o_hH>2atf^@r74dL89&~k|Xcmh|tNJX;tcI5O)%O(~cpP%P>W^{ahW&#huz~6Jz
zyUKla+22-7GK;taieP@W7J3(tvtSBN03%)#LG#?W1?JZHW)6q4c(=tYBc)x42e@b(
zW?L9ydREB@@rt+)elBSD&XtW*!8I5Gp$47x{a#4*hYqhCgy8n96Rh8|Nze<FOOx@6
z%ddoy>f`fT6U0gY8rIfl8eU%99{-_)2tL(H+~e`Yr|CAv9<i*00~iFD;CySJKI~9?
zZBlu#M?Aq`=@WNRK~x4DoJ#2s3JZJ08=GYrQ__|xX5qSCyc^G5kQ+#8_aj@ITV>@^
zh-NrCMSv+Zopr3uYvf~QK>gGP(_iGvwooPymT1P$q7t2Mb4RnKkXV;z6Xf^dmnOx7
z6o~q9=W+7s$lZl3hiZg#EO2UFw%DW6tLxAlxPU6?;_v3aoq3VfEDeQq4N%{v;j!<g
z9HIdSC)74jzt#2_QjnZ8!AV3->!+eU#INyrh`&+w_{jHmzx8UJ{nH^bI{{r94szaS
zKNp4waiEr1Slmn@&%aqdAq62>@ebDnTVb-h<OVr2(Hb7(WOOTOC*Mypga$ww6%*&m
z_iOLA9!nNMQoIOEWz4$UGg0s_T-gc;_C{ZolF#0K@KJLR`Ny0&T>025RuaAHAx%#2
zp${)O7B!{Va^U9{lniHQ4bxj-%#s&GrZc;*&Z4jyMIf)}1IFBwj$flvo<YdsxDsT)
z*@yC@a;C8Aj=l3GkJRPR4iA*8prSHX%4IlS%t*m&t)Z_2Urvl`eCgL?aS5`D0-U!D
zKZ{|{5{Ce&n#E?s_K7nVYob{a?PAR4TfETLzWpl9nCYNS`YNaUKhK_wVVJm6+FMB9
zSMLAzJw;=akAm=bW_codzNV<SA_Zm=EZ6?{5$QSu#XtlT5Wp|Ikwxr)i2@F4M(MHh
z-<o!?;n^P2rI^?sDB|y>z5_<u<Vr9OSl%c?5f6qz7WUqkRNHuA?}zW<=m3+VH2H9r
z?JB0BIW}{Wvy388TkRe#eNbKiDaSx)DLYsCaYiC)DS@GQVxIB!2im?@kT^|(aBk%8
z!Y#sPSgIz~z;!n(G@Dd7^(Ub)!NFHzOXhVIm5h){TOh$F7^sE8hh{m5MK7k1>bI_u
z=6!6&^%2>-4O*IWC-;Tl_69@4zxJktg$1Lm>|--VI~{yLsvcqksnv$_d!LwH=)h!e
z!g|7ZY*t@rw(77dYC#g9AEOy6C*YothbqdH!kO#<LdLb#T@CKcwHOoB)2u6m>(B!8
zz-Ch=qIb=`4o#j4zMC?7?RI7S@d(5VgC;ii##enuZYw&?p#pN!&vzfbOh!sD9_Y1A
zFP<6XvlO;%2@pGl<Msy}3Y?KGk`;Mm<7>}zyi6245I^#9FM`a{?lLQZ=_oNi7-^k)
z)F^31z$E2NJH9+_u`uUn4w`Fcfv9@!8(EUeoQ^;dFV7<r!O;yjUDRfwg2tbn_nJ#%
zTcP?8;2zN)3-41tjSNNDnJ3y>$Y8GXkE9z%AQ#zyCebpN!D3SwK$DjTjIFC}4!7hQ
zxIiW70ZK5W8uBb-HH25zcVHtCn?`@6x<jkfkjB~U$N6=_kCYH(5o9R{E(cp2>uDwE
zRltn(g39|*v-l7TXdry?-)TZO8#_4{%2E;3X&aA6H9Ywy12+r4@Iu_sO-bpg9zT#U
z;gxd0vohJ(^c5~6ZZNW92I0THlvIDQuY^R6&%tSON_a2N!g(ih9!JPN*4#C?;O7T~
zHOy8&lKp46%d;~RCI2`$$07GJa*?n1CJFve$D{UZ$Im@t-}@*`9o2b!Puto1efW)Q
zmk9}g^MTiv{MfJZ2&yu(T_`*oF1{qWo;^MiIx+IG8)zhB8ExqIACKvX3^3fNU?Z37
zqSZY$x<LoCfqf(9cSF!Qq)y-wI!ZNEHe_#yRR<CbJf42A?_9R4ARL7`<aa-C`No$I
zL-Yb<LSzVV!<H-4e3K={4=YT#q0JtomiHFTLE$;bV{mXP?nmo(zo;Y#CuD~onc`QN
zUAEP=6AZgfG|6yq#HePp?l@gt6AEz<%+fwnuMAwiEoiQv16yYBZ>x`Sfxva5**@&e
z-v(j8K>}}e(XDFMP1mpr2)Y>1&EAJO|Ja*=<l^r^NpgDNLl#D^{gqVuDQ@0)na<(v
zmt$PX(|nU;9iN_pFll5FSP9o#csHu%k8m3@Lxw%SEiZbR9qFnmI*Cc__gq$z*~vp^
ziVWw$Navaq>G@u1nXS559OMeIsg0rW&&5<K`v+P<@+3c6QQI<N#o%TXPDBNfp+))+
zW>(ID4ou}EH<Pv5@2tb0XayvUz|F#XdW(5?9<G62EdtFb6%8Vi+cgJmI8Taf+b#%e
z=l>cX!=IS=8o%D^@F~xq7<I$=!M?w#85LYn<q%;VMSY<;8OG!5`h=oU*{d?Fr$(<p
zU-R>6Tlc*kb5YbCBaJg2YAfvyFrMl`%qqstRw^ZKiK|Dm2E=?1^7)bd3#buqA(&Ia
z!2NCO{W^KRXp&<)7@fKQX2+())rhEeX)=q#unZaNeN>C6R{}Y7wT)vgZj72CED&+~
ztVz;7$YpMY2y+WGIX>W#v|wLHD49^8iS^<=58n8}V^GAFHvHt*C&QzHL=@vvuMLPt
z_2sQp@B8D0C_O4o4jr}ExK!B%>kd?KN5)7+gB0V(&PipUMh-BwMlZEB%Tr(!!2@$5
ztUlwLS!yt3AZE8QA~YOKS}G!zPNEBLSj>t(l3r0|oaqD;UZRv6H=5k&6H~BoQh9N#
zn3dzkQ0@)Sg$HD)py>RYx^4Jhrg7$z?^O1ipZG^Yy^WzcP+0ToWF#~<aGv&VVC=by
zTcoB0)LSOEljid({rwAn2)1t0<X8Dd*1m7cVONDpA0XT}U$E>i3ui;X?@<*#CGPJt
zAngR|aQia={0P6OF>*ISJG&l)@3~gl7_uMD@@bsn8*5auqB`+=2%*BH$ahfz9(z2z
zBGI$|0WI6=9;Z(>$wEOEg9bs4-q`({njX;8g>Mpym^vRI^3P9&;)c>VE6>;CT1;p(
zG&}6h0M#$!SKrGelI~9-&d@-0LF2W%zSVD3jEl&@|HS2ax3*L_!$p^YDY`ONi`d2?
zIi!usz_ST+Mt&#A!8Roeq$WG`dWS{5?#}f))5Z|S@%;hBp1J1xNL#%ZrY4zoe5N7K
zhe5`lhYUK=CtwwP7L?sAf>;MmtCAVojqKEcsb+oS7U4?8+bi}qE`ixQ3uG#<ODG$>
zN2;bl_Ira#vY#Gn2a5ar;qDiJhE>Uf-Py)^P!T;&^Jlye-?rjt{whRIB#37%LQ6|i
z(z-WI`bZ2MVk|JW|KR=`w)0t!28<tW8q9ONI}2_#2W)jQj;D0KIRNQN1{wZxVPW$l
zJ<cVX)3%QIb!r_q9-5q5@GQ3G;G&&(9xr}T{7>xMtuqr|JT<)eK;3D0Z|$L^BVq~b
zH<ujQxqso~#m{HDoS~cEs?I08Q#e*Vf((v@cckDc$Aa_LH|IEo<0UkXx*wTiYKV%j
zU=B&;fUKbR4%3;vXR=PjImM4=)_isIq5a8(%sAM`!Rt-gb3e%;E`oFDzF&AbV#EUk
z(OA#`Gx7MjOpokn7Sa`VVIEm?iqm(5h1!Gp;7sQZUpkeip95h<AVuCi<iT0Jpb2Ue
zrIKr3X~F2&Y`Y<p%N?H;<bhs(lFVBTyGP@PcqVE+Go;A-71q29b&SS9sIvG!ZEV+}
zulZRuI?yo3>u@<IaADUE_(TOb?KX9h>rE|bQ^QvO6k`d$>t>V$Rv=i!H-U@fW$;a<
znVVsGW+70P_ngnypE!yf>Q8%2<64f6k9d_12D}Z>gBgZ6sPveN2AGr_c?S1|ji2*A
zTXvDSrnmqFH+0ZyANH<Zf(0Qq;@be!7s(Cmb<&?j6m^u~N+j67Yie0lx_*W%JjXkJ
z%z`+MS*UBk<Og33&QF}<re>9-qpTSpPlwZU>5`YITCgZbDdO@(BDL~go<z48fWwxm
zC#r51oG^ykxEVaj7I3QW@{A#Fm0VzB%3lY3>pqzy4@|e==V1|-UVEQ1K`wKA8070A
zMR|5L(-@-7cx~{AyWXXSIdB0{AQIwF&E<LM_>SThp&Z?S;XGhaybd{y5YS`I6^}C7
zm<G?)W7h#m6?TlSV?E4jsGw!QQzd%Z?a!fA!tKUw63!~jFCCIz2*DT&1p??Or93FA
zLHLHtfF|_E7xweQjti$j4^Lh!#(tGo+lSm+1s%Y6EI*{d^z2)Ue$5{Y*cDJaPZ`U?
zfR=8{H=+Fg^f6~^07BzOpuK7v8$n;&024a!0X7arMlGxV`?rctXaFhT&ZBfokCUg<
z4nY-714kZ&{u}MCA3>vSz?PPpY|Lr<^(hefa_}8hOnD!(@}?LZp<#fyx@>UcwWJNm
zn7|Wn3o9eusd@z%kNW_dmR=VosE&g=s{&fwZ?}$>JXaD!3PdgC+Dj%pS~B#{s6IqX
zP&Va8RDA3I1pO*mpUon-it);FcFuzG)dl`*Z>myY&I$pH^uXs4gw^rQW^xN6VH=Ei
z2(G|1CLV9=s*->k0|#;jFXPE2_ms^J{b(B>l%*C31l7G}u5k)<NFx>W_SyZP9k=KB
zqF|Z|va5?lJzif&UF%T;S;d~eWG>xGCF@%NOk$M5J*#i}^^V+K3|T#%>QrZVmN>>X
z!?alOJPTvL-l`JcJ0EE_t5qoM`aFQq=F%Vz65w#R9k*pD>SC`wQd%XDr525bC%D{&
z3)}>U0o4_nUXVK#AcKxJf!0jeC*vmfAIp%ocmO+zD;a%c?wv3M$LYO6pFOMBy<cz)
zzFONf$k@N6@26K20b3x{LTysCKWDkAYnv?E$M|Jfo%FugbZMg)*s#XF=aZYB<2(P&
z(tv>~=nH=8&ka)>I6ZAYb8_$iFV!YJ?a90+S#p!c&d14NQh}sru~|i717C-P)!vgj
zC}iRSax8^Z;o_eJ!rtiJa<vZvfY-DrD!`RF0yeE<sX6}}RSp<yA~?8Q(2VJmRzF)?
zA|ZE3a3{fW-s@ivf0w~WJ)rzqrc|WW*R=lDL_K5tj>;nm);<;kZbdL>VO(I5&UekV
z>OJ5%FX`1cYFz($@yXtDnA+kXS~;Y<KUVC3%H_$No(1YZY3`x(A8w(KE5-fs;XD!y
zi6+KA_;`$Kxeux$IC1)|>9gvu==<pW95cHU^A6G0h`9~(3l<nS?fdclOKoKhavS8l
zzENgMJ7Zskl@+|qg;W6N9kj;|2o&>R7@*+__OC{K>i$>+fyN|@z$J&`gY*NNk4$Jk
z7@iiF%ZmB^=5rUqT^i^bXn3B>ZrUtF&u~yhF8BEupqy*503jK;na><g_SyO4AXFYu
z(hM0fy%t}ADtC?9Y*ysPzEGE+$7Uhj(ad5I%`I&b_jCS3+6iWo)~QGTh%Ir7|87qn
z=4SpO!E~3y*5sv!-5qry(~Lmg^VW1-dg~=*ID{>qGIGl&LpU!VZI=!1P1KIP$LTH|
zWP;;<6B+FIVdiBK{UZ-U|DA=Mzkt#H4n8&mL^$cw{xe1aGL8chnA`40bL-eiaBSHa
zTqkz^v}Gvw4YHHGAiSaW*Na(_k5Ohq&<DgX9nZ0-QK?TvtFoBQSc_z$w(^b$h!Nnb
zTQ%w-?Y9r0m5@8ZwehvldhR(PwWyFn0W`>F6MCip=_0x)po-4<WWLp-=ncf}@k7L+
zhQ6zo{f>u42n<*OwPW<TB04sYeQd_MDD`uj4ox`^n;`Pdyinf|W7~Q(jD|-HFn#Z|
z_Jk5sX~2^4lb<fVN%(!Jlq6^c$z1Fo9Mn0s92WmTih2bfHaMdW@8ZGy!5|^Ur1R`v
zS0pN97ZwqiV5VU=RIvkk*XkfLRw1m%YpUNpKGmmVb(A*p@ddQlM@<}dMpy!aEb8)t
zFH(T_Hj;foxhq_QLN<G8vNp3rZ7=cvS~jlW-CG&wwspL(oDJUvCw>RYXb*=!Erpsp
z2wZ7}-qyL!pZ|kmnhvhu*y~0nSzVAp5hnp|P;@>LOR!#XkZS-%yyy-U-;FE(U+csG
zXw}Zn;%Nq781NbxDk%T;5?igqwf^D)=n<0o;Dd>K&mML+R)X7D(FYEGo|W3f<M*yX
zUKj^)TmfVHAGRXuVS(67$Vqd2yiw^es?vf^gqoP+$g7TT=rr7MU@q(Gd=50WS0JZF
zZ3E_g(Qoi&{|O|R6aePuP6U46M}Lf>M*<u}R_e<r1t)SRg*@*2!|O8Vlkecb7?|}F
zZr6HKd)dj%5{6?m5Hr|DI!fFbn3_M!1S8xmJaM=oe9EITJM&4aw~i-D3T#jpDih4(
z2`QJ?g)W0}!}xb798{wX(__K7^2Gg9><cVn9<9mUoaya^sFFO-BBL%PC6S*8pwe)%
zK)=E6a89kN4}6sb#}9hOW=T+8^3lMk4mf%2q<H0&h5}5+u)pJYEFI3l@v8+UdUrUO
z2lq{D9vm?DZiKA_ygi@nbs<MZ$XhUUAT>P5ny+6^p6>b|6lnm^tZ9C-s@=`M&;VU9
zPw2r6l+|<>j^BDUQP*UFGrzg7pP3k8Xz)-U1j;G;op)|3YQp)4dD5gU`!_q+abOi(
z00UmrS0^!$d^>`G0t5FH?5I)O)|De-b+DgKYaYn_0|t5Q+kegyiHbl?2r$ns=gTj+
z&Hx8%C+_1S19-wn`_SYTxiD)Ou8`w*Jh6||b7iIuK9OMZKd1VKv-osx)X`-Rs1FJP
zR?5Cr%)j(%IWVKZ`^5#soHk;lmYRdfZimo7tZietxE!EA^hDPmfs)xeGM;m)@^QHK
zluYgUiM}4AMX!<=aC>1V_+gQjwW{O^bRosPAiOp2&YhLp<Gi5?8f+Hs!)51!j~Rs{
zj(Beo7CC<El{l8V1i?iNsHwbr@O1fOt>4$KFphE>iBM8C2)AbOKleq(qBT$Q3l(4X
zMDEC%2BRADV)A`c_esB$jv+W2A7;bXPcEBpaNrUhi4+{BEv`4<UGkCJub?AzS~`!M
z3vj`=GvCo8h69r=Z*YvNh2~oxg~k}DcL&6Iz79l|H!8_0Z5HO_%3SLVds{7z{BK$i
zfrluy?(X&*zgg*T{g43qH*~C;c;#jY)#D>jqAlnCy~s)vz=Yr^D1tbrUN)~P7eV#i
zl#g6*#ZQ#4g5W7wNZc$OSUyAkFy^VY5Uzl`6_f8o$*=+<N)CoRDd5{SWjSMqe|$?t
z{H{OESH+j_C~b=ppUkm+!O%tGkM9o;oBJR1nr!3~%K&KU=wzK6o^sxxD5s;nY{5-O
ztEtvi10doTedR3QFEEx702;>Xp6-5TkNhun{Msiu>^JvW@e#BD_@KkNq2M6vypvK$
zE?ywz{qA$R?!jJU-{2Fh_(sJ&ZrGul=-*}skw327D>R`++&~_Z1ul{_xxhM4U~MRh
z#^pg=82LzkapEl0L|_BAFFW1Zx#|)RbpgR03aY<~%gdV4Af1LC-?dnrVm@Mvp`C=E
zXX48|iNzY7KlY-CX&Q(&8bVUIxy=xI$G_F;lXIr#H9l3u19bsP1ASu?fiFeDwuU|U
z`jcGH?R>4N7Rs_9@;Z57MTp?lI;hbGfQ0n7HaTWo&-sV}rXcg}{_<6e2!jA>2^__-
zA6YjR9EEb20rC+a=75Oxj;#wO{RHd!0Oz6Wd7r?ufhgCbm2-`~Y3>*QSjr;DNdx|b
zx|OjocO?7_%>Tf#w-v<pmA3Zqln4&$XW7ECJz%&PF&8uuDZ{V2pI^-hQ-n6Jxk*@3
z&Ukd>*bghjO3f|8olNDMH%EGOXJH8r-=zLVd`2MRaE@H#ov~vD?CP;W!9!loC`wix
zo}nP-wJ~<MeY~IRqKs$|x8wH^wI>`t^o*JykRsl36`e~TJr)k^SkDz_$!<M5KK?@}
zd62(xT0?2d3)qG90MapdTCGaAHG(05hdn>9FV<K*KYwcILM{kDelyF=B+4Dt=SkgS
z;)#2Yw9Zi+pE?^3J~iYitPD~k&q9Z{64bs1&Yt@>6J6g3It;&Dw%V@|BNn3)76w57
zf!V|M?cNm!kbdO>DHc|lr?JFP^#TK*&dC?`Q&x$YD&qP|kpbT>r^(;$^%FyYKrP0H
zBbC#tQw(8DI{uwjh2n~{12qh!hvS!q^;)=wB>t@2*#(D%g+aJ8?`8ITVIfiq$~$fH
z<|Xfzx?~BcbRrj+c<jYsB`HgI-4%9aK>V0c|M(Ff8CW?34PBN_;|{XxMF!-DKS(or
zmlOZ&Xa_gwPh3Pug4@xyyE^S0sS<<#wHsuEMqdi<IHH7lcQ%WV5T_ga9=<sl3*u6s
zEb+ig`&pCb_{EYFA%VO!xw)5<7u6C!ep$~%&F~Pg7|k<>0+Wrcr-t%C{kph?dVfXw
zR2>}@-O|ptEw@fFd~yYh&PVzlVRCpS;n$9pet33ZgUMCBGe31iSB8h%NmtEz8slqU
zx5hQ?j%YSrcO_?KneUu^Yt(j`FWuD_`^b=+pWMUu>+~VVtn0Vek6PoGq6W2$No_6C
zC#t8KH*4LV*D+Q2wshQdLAZ44=S9Ih#TUPVyRj<`*t(3g6^l(PXSNASo-ZtWphy1e
zKt^b)Dvf>qpIZOQYkM=)#^}%cuxr|OVbqLWZNkkHzqK?S-_S@d<(J(3cA^Pe^Y{Nm
z4)NmLKN@@1v7We)50|d`_T?9;Ff6>>$X&}=qx?AwKQQ<1xwAtQ!(K*9aK}>b)w-7l
zgf@q7oHmFir5wnaR*zKst6R^YpEKdvTYPE?5_Ow;)u5Mg?PzDVZe--VV!`CA+Z@Pq
z+jpq=H6KvxqlD~QZ7CxtG2wN7xPK;mw=8;9{OO4C$@0-MqVMKxM8))UT$ruRYIU9?
z!=dk6c~3c5xFmr^n2QKk2r`zSXCd(%ah*4o2d&8lCZaarFiutvH(kkk;Q#quzy!4t
z>9`I<)~2>mM$BjL-=8~KBOEm=xn^?ZcFBL2#V2^TB^^KYaoGd@CJFdotu-w2Wq8(^
z@3-h^!{p;JLF4=4Kvnm!4b$O{NooxTQ|VM$uw(OL_%iDtsAT;38o}y$23MGyj<<BB
zJuk<$r=Iw}`!CnY2VUe8;Z23Ij6;aS#xX)+oYjZJB@ZI2APKU7gtK`Y-qxqZAX4=Y
z@)UozY^i7NM*pUrj{8~UX!Z51LBB@+JH)hfCJNSfj9u6SB@wqs;OkC3w%K-YF_Mma
z0!O^E(%k%BYakNhMF;Z!MRN(auAloiO{yKax0f-OX%?z2l!K(I-VpX>oOIdAezOyD
z1Yc8y<zFlBZYqv(fwVGoeaR<gt=$`~79(~tfYu!<uUonxo<3V=Lem-XY}}8<mL%>p
zZH2GWpN%m8+!_$EXYyvvpOm5N4PkqDY*9{f?{&1_<InKDdX4EWBEs7uXe)B*crtdZ
z$J2d_4EN4oMT|}m40`-{`&`oyat>oOVCmBr|6bp8Tm)_8rcfBHqOfQC%YV^tO{>D9
z2l*dfXZRrommK8T$JdWNnHF00jy_!G6+Nc%yak(sr?y<Exd@F_1rTt>YIy;{`F93o
z!nRI5!!P9a6c^ai)&GNrNii7m&^q|0idH`x$tCO{i{v}merapFeP&W?sQY^v`ZLsL
z9Va4Y!rj`|2oo%9T}K8P8keEMZLeZAoGi9uTC||uraT!j?xba$a}r)qm6~LS*<yhK
zKk8iS;JzvO0h=jSbPAT)E*H$3gdkwipJeiTJ@?3~^+y)c@(u~iOn8d<l_OsnZ5fv#
z3B>gVjK_4&Y#LgJER|R&$b0SGHwUwpK)-g$fo$?ikAC%iE&4Y{AQn@H3qPY5AVT$f
zDLhe4ikau*`B2C>fQN={y)vzDJQX1YOKu8XwCjI=>|1){@4x*dSxd)K;zpRIJ9D(L
zwB1O4Gpg|YSKamtvvhObPudo7w*fPt$8kS(UxU0{?LhvHc&peeIS<N@wrUK+OfyP6
zNoyn+4HGbf`SqSmxF$gENggThA)5%jJ~zVZ-dy-=Ni{;xOlym0x_oPBM6~D;de5ku
zC2u99lw~x;>8Y`&o(}IuvSR-;7RNLDN@kHaeUa5Gny`rQTpD@9Wzp0>J|AK7KhJ1q
zX*Y_)Rjq1-`y4K8I^C5q16mOa7@JMKc!#3MQz$LQE^sMiJ$Z*I?TzT-EEGh`)oqJ#
z?<rV`5W^%vVB^;PilpYF9Rt)C_uF>Qyc%KN*&1Qj*EP+}!|xWM7deXrg*^6N<Dmsp
zQox1<0jbgShS_hCOTzzaz*KuEf&AqDwNl)&lcKaEq<@XjJjDBxi|`;617+1%PH(t}
zmYt0udK8;a-Z{ECnt!Il1uaTnDA1<KewtIpK-hrqsKR@C$`t=F(sgIB##&imf#@1R
z+@bz`tjFTU&<&7M>39<@>VRX$l;XDmvNC_%PxJVn413b4+5@A1Nww>^V-WWadQfa<
zFGD)iI47WjITI=zx!R31cjaC@^+pn@dE5<Qk*3z`a~k>E#E??2NylHy+3LzQUn}NK
z`n<RVfk|#_P@Aq95=~mXV9@KB`zQZrsz@OiPx~-8M@IT9i%>B=9lqs089`ctp!(eV
zZ+G+<Q~Z6<fNAIA6`Yq>&;rC_fvjFGzt&^#4z#(>c!BEH_T&4`dqW0y0ypFCbDIoO
z?0k2LoCq{XtzjYOy5;sqi6IEeiGO`b-kBy(`kPj$BE;TiJlWT4C1)sf5Go-vW`xN&
zs^TS4a{PY~3TZXM)a~w@+m9N<MXAs7@%qoI^{IV5frwJ-nlXC6&*SCQJEmYn>lt48
zy@JRdKEE6OnuG)CBXMb+A2*WqZqml_&|eI)E#08br2YvCZnky{a%uxN>VH_UYw}fG
zXIR+WV(l5zm;@aI_1__n{1Yw;>wVqjAljsu@Zv7ALp#khp=&2Sx&`cyA<45zb9dyR
zuGMfTzx%8!l2WI(yL=DcST|6g`}j5r+Q>D-LMztF(IVNo$q>1T8x5Gv3(0L_7(;xv
z@ZI~=(Z7Bx5CCM`%_~Enbr6Sjdl^|bdYlK>Pf0r5i*)9t^$hfIkeTBF4cMbbzf2=>
zU8tGF)doy==e0#<;ZH6T!~8sr7o?Q~nfr!lJ@__bA#}2wc!7<_fZoLzCXA+V#xF+K
z7fy$({&J+21_>N??uNiCnh|eBp+=K4083OBEHxkQy#+BT0EAuYFSq%88e9dD5Ce|!
zRG4K^wZ~F_2ukEbK!I;T(q)(EDrj6d0II&y&y8IP!{{05|BL35-XWVgn4S3^{Uh*=
zuRm#S*Wf*!bb~}t16C_<@HY454Y(030o?uE*H_0`5c(flHOPz15me?ZNGh0GArS23
zV1M2jmK_3H47io&vpJE(F<cz72?s%OUktB2ZswE8MM(<KuA_c2rh$$~3gneBUR=#b
z@z`_a>t{ig+g^n!boTrIFsYtgRbWqEB@dBdHNx%IwWm5lCB-MSc#IT^lJ!_C{+R^M
zdG2kC`;lEY0YMMc#)w$Zpga_isn_IZJ8j~T<(?&M+{|;#^tSGL5cV8iZ6+gNtr~0G
z=9K)o$ZtA~gp?Kv<af-<k7RqrO^52uS~C_l)@VL9a(qx7K2yFatZG|kE;oP0Rmj6z
zu|x^Z<HbG7TmM$?STtnz;wp?Q8MwbZOfF>({MV0r$Xs7<CC}AvdnU;yB&t|S9;e!$
z+t`LeBc)#ZMfFL1{@UsNG6(}^3S;7LZe%_lt=|hD5xa+&@fy!>nS@SPf=Xn$M9|!D
zBt?d|^PB{HW+p$@c}f3h`mY+1U>|PgJ+(12#W4@j-+ZHZd=~t%`hUh~g1&E>*G}@F
ze$#djc^rFN(cs}X6_{)V)R*n7-Cy67HXEW$&K~m52443xUY+R-C`;vM<R895t*Xix
zWC!cPFWPVzO^hC}6#-?6;Sl3+^_Mke-ABZb$LcxAi~BZhIm62eA`c1T*4#pe*eon_
z8sr*C1N5?Tdunif!>KnH3;YWfw*<>-%AhdBt44TJ&Z7L3ov+%TR@VM6zi7t2jNq+K
zZDAV{XH7~47kP&NsXAa8{?iYlNecKc{@HZ-wDkzse4ml;MezEK$~qH~703$l8PABs
z+R7c%QWT@1@vuQwp!c2~+Nkw%29)~s@o3=P#;k8+94KNIz_r~QzqJp~L1D1S9y}-2
z&&^k_1|cHr<}b`uuia~%Nm}<6xB~0pFKWivdF?ob>Z*4XsotZ;e=}47Df@U0IyQxY
zUeLqPb`8-9^}6?~dhTjVjrP+N&JKCRjxhaK1$_P?b|!Z+2WIc;WqeHRmduyAjvS&P
z@SDFvID+mvJt$JuZeg2w%+9mbv)Blq*!!FD*>M_kezgWew8LGW;m6N=m;7#3Sq?)9
zVpckCr{yyA@bQ$(RZbA3?MOIz`1d8XmdV81Lw+pmw`@zPePPmd<m&J}&G^Qe(SMY+
zlNUi7v;>L#+Kw4<j*Mr}hvAL@vb(#M^6ZaJ{hs1jXG5O*>o?o4LrJLD2+u|~Y|DSD
zhys|*m=R-^D5c?&j?;WZfA0<9?e|88t3+tRg+ZLjtX&(#9Bt`q=(}ei&D@-h|5&zN
z<kM7OO@PcZ!!qY|3f4IlsEa%!hP&D}Ms=mZH`)7o8P5_72P-X+8^-kk3H<m+#$7G3
zh<uM#W4y*@rw68I7o*4|5|jYaZ+W;ma95&dR`G(iqbZ)}0;ZcGd&oJ=>kJ&MP!=h9
zhIA*m0b}W<&k^tSpY8YKVYl+t@dAnm0P-J|%1(Y-Rnvne19)5olr8YOEn7BDN5&+#
zha8fB>P$DWoKkl~F!10ILZ4pAa-D%Zs$x3+F&V$_iz}lI>VT{)_07>z0~Ahio{i8A
zSaqIkt8=rE_RzXHG+Cqck+WMR(Xu*w$oQRDR@|sN^4YAgx8wnb>SE7Nt{xEQI3S*C
z`l2pvQUDQ*MQ^}viaIq*=0ImeVCnen`A%V3Nw1WpU`fTVMworLx^RuFxhCSB$U=dj
zk2!cF_nSD{hjF~XB$Zb}FIPhRB-f?mE6CApZ@pGSM<Q~V7bmD3X|L>s+rWUW`(M$#
zdJ+FB2iZ%$uMs|~d8dkh9a{j+m9ncD?>PQv=+WZ;AU_DqjocT(+U#^l8O0b_^awNR
zV`N08ts9E2@wMsrg#{kBw71FzLv<Z@&v_}DmMug7qT<>Ax_JzVa1T)xyha+j?810K
z^^>eX!(kWX+3}stctc9JOjpxFM1AWX^5JF1Meio%6eYwuc@QM;$gzC)Y8HYM3IM=T
z>E<+IKo2c@7vwLsElQ`2ygU$x^$+pvnM+dmS82!}h=UL)`?;J>fF+9INCh`CyQ)-E
znQbaQ^{NS*lAHCTm#?+}ZErKsue4Y8=h81DDnv|pB^4zldGAxS-@|wv-;j=v<&Ny*
zUs{F`fDRZMmXLQ&#0!Ewu9c3jQXIM+!F?l%XkA$&uy2iQp+~DD?Z+;Aktcc{>&P#j
z4h<UvNwlKK=!3Z9Ju?uTgv1M63*XX8vvu>4Wd|H)Q}~;6Xu@V|v}0UXAWX7|^V4}i
zL$MM?ua~g~A2IO8#1OG%z@fZNCkRU==)VI%0pi4csvSE=0MmJLYZdFlsmlyr*udlh
z!iwf>z2OMlTWfXju2StX>U>q352rWH&kioOZQ6AtcIDNDyXIuOFB<=w_SHMybEGpD
z?pi9d%PwfMyjldar86SVo}E5o&b!R9V5j4bQMnz%A1v6zf?=Km*LXGGzre<8&8J@h
zMFN}Tg=M#|ldHQ0A_G=l-%{2G!e{uVVL_%%k+)*+9QLePI>%SulNBQYJ=t#YnbqIa
zk9{wcSoN){pRtR}(-9QQ@8B?64#H>qxzr5<O@%)MMjQULrX5z}@18@N-X8FszFzG8
z58-CB8>jUPahY`dF^BEQ^E9HY?QflX_xBmDahxBSmT5)Y$tdUTx-35Nf3sT|KP11Y
zuo9Oq<DS2^*B$(tC^@Fu;N@lX<^%dQkdpX^UW@9g(tnkOf121D$TWU73j}T9r{Hg?
zr{f#NW$$>td4esS{ISl3!jbIk89SO9Za^xtYk1-<N&WkUuK$Beb}kaC^yYYLelvXs
zm&8Dj9{kehu%n*m?#bWBBD}~(dWp~R$C)M5DSugu&upc9uRV9-c#4M(<PRMbKZaEB
z4B2%1f0lCS*}GP2$_NKV$>geVzXhU$C1{0P;OE?FtSe&auigbH?Rg#DN`Rt+{d(cz
zd|HeOq(Jg8PqDL}I#*@*-Q=R=Dj-j`-GV<%m1{_zYORbDCTeTbxS|{SD6W4QDGAcT
zmi+g!4n|Xq6b01;J+6X=;|=&GdDwxRAFutPtwh-mk??ecdG+PW1b>lJz-3KrEvP?p
zPS4gFTQ3E)K-d!D<>fldmq~iNtVNV6%^`owClp#Zk<;e+Wc?*R73kIWGHi1%>m*!(
zx{oWVHCoMDsVEKqD={9FukxKPaU1U*Yr;DIs!YlnezmNR{!-JI{%F541%zkXdU*kn
zuR8q}uVg}X(}5{^dOlAN7lAUZKLkwC`QOj~TfSjZ|56nBA#uL*OXuyl26bUu0c-on
zyR~%PkW{EwtbcnM@{Rg&&aOzAv8V<tETSaqxwf<z+J>G3sg<p{rcby7@nZ?_Oy|x%
z>21_G<|7=pJrXzRXYJ{w1XS8AF|ePx3o-MNmvGQ2)n$QOVe!<%YYkYRNr`Uy*<iRm
zP^puyIqh?jBuaLocR}n>9d(;7yL~p~Tq6kLR&6Zxvergh@c{K88X>yVg>&Tzq|6v7
zW>?~pKMP7updSlqz&M49UQNHQAi_3wbJ%km59}92Z(9b{HwTn{1}avEeF;E(GJZB<
zTxj*)rP2mHV(so_80}uun2|t7zh(`z>%zmuUoiVEOF71LuJK2ll8*06Hg=KvMhS%C
zJKfErDXogrt4J?|=7ED4A;aSO(qv3S0*QGBNJ4#Ae<1rWZqOkokcRxHl^AZxI&1#O
zFm=+57$B4`m|g8J$BvqfHnI{dAt85wnI?4|Jv-?@Zu73_k8ho_hD8qKo8*O$>xPQw
zL)@Z({zs=|;-ih)&vre9%*6EaNadejKV>cIMf77v0s-^S6LWW0R>f6ltfC-*6Zo2z
zj%h<sH3kt<f??Xc1*0~T$U?3L_~zZ>nycz~5<Xfw%mLFqmCAgl_7!WPNgJEsq)BVR
zaXc^z*+?7+!;MNj&1;jUHUb1ur#sjb9)&Q&+=0B*H_xC?XA#tz&Ug+HynIpLKzaEl
zv_N$Ni#&c%bCpx~gY)Pi(*)eXopSrUxz0i)F=|357T~2@tZ=jdN{pP|%J3GXjk-&2
z)I(a014m56RT_U3A~*;GH5}O$?fo6B(Ss9q0(_Kh0V`)LF+TneFO|!mG-{<9HvZPx
z^5BpyLTc6cPORBia*RAEg0_+fti;;(ZhB|KH3%;Rkj{z9cHQ@FS`b_D$Of#ZL$6c!
z+lhhENmPCWlC`txUv0`0&LCoG3x%5rxnmLT7q%ev*8p{{#(0I~ZyTWu3E+L`l?JNT
z=cv=6wQg`AkCa<?+iA~%EY3{eP_i}#?;A7TiY$|Tkib8ue%cbiRy~DGlQ8I|u-@1n
z5)>f+#sn$i{e<$s;*4U%V32`7*lM0?=l<~mR8AHMFZ<$@Ue*Us!Bs%N2ey5i`nDK3
zM8UYz>ocW3!h5YZK+i%s$ijJ@-TUd1n~^eM&qwH1A70c`K0>cbE&{r#(rn5*s+lu)
z?j)jn0=VVcxpin%L-vx(TWR&Q-!hqu<46th1KfbyNrZYCIVywr%K*saS7{OVZ2<C;
z2}tN@@3~#P?F3YQ5YHdZNg^FM!){QdI1p)XvTf7j!Mi=k3UMQ@y~F`}TBYMuWHT)t
z-|w5VlDqrl3gnbj05bE6uD@_^gBU?-rsEb09|G`!S<qvUpcO?;2~N?}x)Fd_uF%Tv
z#%-cm%v8i9av!&(;@1Ash$dO2gJvMKZuASW43b#Jy*$Xo4-4@_yw2}AhS}k}(2|e6
z$oI9{;oi^H3n4nNYJ}_hSr>*X+2M#NGN4~&jMMrcMc8r`LDlkZ0qv-nla)#3l;ZDl
zAl16ElaBUUT0WhGJ`$AGn_p(qoU+d^hjL~u5{}NWcS>o<lt=o&7RBjh%I;T1mN_9>
zi5vaKmSY!6dP;q#Xa!9bE6c?K_275YF+0P;J%0m5abTFS;!S5gH>oUwDnUtL;aMT+
zs{P##NY|B@@ma!sDU!V(eOu;@^J?9_<znQitAt6mQI7YqP^8(!wpL2*_rkugqa9EO
zh?%X_8bAD=N5})HD4DDI^+RXhbe^g=+XvM`WgoY3woXg6!&h~*Y|<R^duh6NX5~=P
zbWAC$()#bF4`0Rou9WC-VmwA*^vQbM&(S&=67kw~KnEHWuE!-RABO*q=(`OK`^!~=
zHE7DSk@Gun9V!OvdOvf+s29;rh#-^ARNyy9xUbl|wowWqfGsFiG(Yio7yBdr@1$}Q
z8(LY>jwA0skMuyzpx*8`mVK+%9%D>JMD)%fQ|#Pze##!7jkN3!&@!vv=MU}qx)N^J
z?8yl8^8LbTqDkn-?)J01lFRP?Zo?_pC-scbZlw;lR?GKgE<x6rvWFWWStM&X>bME1
zMW`rQ@G+`H%b*3D3#nfZiZ#dkE?FemmOPo%GHPgl<~fQ>({N5Qlue2rS7bCRa&Q0I
z%TQ-=w*t)LOZbuzn+4F(5wih;{oXGYEQRcqn30Yz4v4sDA<&Y5y0kSx;9A*wWJt&j
zfpYc$!Ha)S+^ysx6hITp((zlXj77B4P|(1-*>AkTWQ%g+7J(Wv{&Wz$kY^ryCrMAK
zHRwO|7Blvp9Z*KJyMfw`pvauLai2ZH<?%Y*a}oRMfcem3anO|5b1BkQ%nb$A1b}6A
z)4j&W+}frPhp|NHSpvQQ&!!{)Na%o`+AgMIWO~7*SnwI3*5ojfSnY4qIc5G=`Z?)M
zJjWs4KS&vf)qIs53y2ekMwKRWE@BnmGX4BpsqcS&O-Gc9fqEgYPPfrr2a1J!-0vZk
z*MIm%rZ$AR<sdN5ZENS>l-#&;QYBf}`csJR54OyrG^EV51Kg4|#<}<Mqh}!&8G-~l
z=H&IBZ((MLMI`928xL;TLIVgfkrWY10D)g-H?$sgf}yuY%n03v?O2wPnF4K(v@9TP
zqYdgKbm&tsAf1lyU}noTXDmT$R)~`Gi3g9CwNR3G-xX(sK5J!N0j*hU+hl2pAgH74
zctP5QMzO|zJtX4H=Of0i*%f!VB^qe2G+l07M7C?%_g@xpXE-P^Hx*@S(L7O5%zE5_
zv76GqkZ;~kCpM9P6x&&V#?TGUys(F1Fj>Ym0Ql~>q?y*inOahT-`l93U;T8nr|}?6
zVKU|UotN5K`})#nAUhrbRi{@%XK~^!loR-DI^$RVx2lu1uihC)MhW(fjw}4g7S3_g
zjTO*}NpTSVhPbOZQ|;mQJy!A4Xc_r5?`$c=vA(N#Jx1nJ?lGo<dOGN6etTqcP;22l
zp8yZT3s=W8ZB39MMp2!!1ZgWEoOnDPp<4!vT%*!TuR*9AmuGIWc0XCQ1X19872ox5
zeZV9C@{=%^AeW@$1^czSHwk|4M&3RLG@=Sy^~+L0pun{JIkI=WI6#Pi;=>y&Pb1#(
zsbDYaDq&E0hxJTFo9X{!a59DUUL<s($;w&&EFov>>iC%6x{lKv$geLitvKj|VnS^Y
z6T0DE_j(V?LD=;LkDL_-cjrfuGg0!8U&CR|UQwZPo)%ElIhkN`qJ=`mGkg=~d)nYr
z7<l5wV6wz}K+V@;F*F+95@Fr)#Zg=7ve%)`O{*DX$Xhd)jO>7+5-V^_*cd!_mh6{P
zjNOneY(uVht`_x<523k?F_}!WL45v2mAZ{UNI?GEJxVX+NmfbFZ6@9vB!r^<>`HQo
zAj?Sx$povtj@+=}<$jw9D_|Q4g!ca8%Pt!!0_0DW5F@Feg6M~DZd{`bUBP(CbH<EY
z0T+Fb&WG6QFG@z+$VsY5y%mGp@&mtJny#Q_KL#nm=KxUF+E8s~_8vwm2f@JKt99S_
z?`#dAvWSC<U9X8kT4grGcNQ31!VNRJ`}=M7q5p~-G5%H@b1!hgF^D(#Ob}CAXzt%`
zCXI4kP@EifzT<YW4cUbFlM%XpI&;HYJIhJS$8Lc(^cJ1K!LC3kg(}clnxo_-c^qZK
zHt}pCX`|he*Q=AJe8A-z;Wx%9lRMiakP4@QAjU2zby%l<CS*h#@YXM_9}S5ZJ3W1;
z@@YT+IUmdj#{Cp*5#nOS5VRK#3o|XsKiY*RAXi7u1leV9d1%i;(!v}mzv5vQ-g6=2
z_=TJ~2+0*ey|q+L)1IP%9{DE-mULU_*nL^933+A={LYKccB@szNFrcZZ=}TRM&q%m
ztT~~E$4VYbIpd)fH{}CX0%6*_kE}E=S3*e`25JUu`QCZfZpc&OJJP`yf)amP#jZxc
zp$a_UpNzwI+vY=f2>?}egTuDuGS(@m&lo5f?7n`qtXuF5Wb~7XR9ZZTa=`0gk?+On
z&|>U?<-44#{drym5eg<Z?DaVgmCKb*TxO?s7b~`7VIU+nPwYNhcBmEx7Z_+4WS6|i
zu|7+OLf%w>^&d@>aWQ5cMbYYbH7>lDvi0`{4%!=Kr#3n2leRm)?L4GFA5f5FY$y_@
zzW|034C7u3&ATMNGSLA#BMj8y-p60Nt?Q?a;v?$6Tn%ydv2yUaa7_*oC9ZP2^L2`)
zyp3IT#+|aC>MJ=5w}85A-^vd%x^p&jcCF36y6}Sd%Aa!cvNg7ZeY3Mn)62-%T#?@y
zA(+;wCD%Ey4ZEZ*mr~M|+1WYT)-xa+5`NmSFGeuK?C%|rq8wg*_+0Fhx$nJO{Cnkg
zgsIRVTj0w7&wyJQd*o-^-w%4xTY<rCKI+PB&pC0IKJlV-8+&Xp`h}l(=ApCXyTAVi
zKJ8kv%pm^30Oz0ey_BrKw=XJXaacZ|6W^9JhQ`0azyJM?Sjh!mN!lC7Q+egJspy#t
zFRt^fS&F)BLTSn0D`mh%egc1&)~$zU=UymUMM?SlRUR#H3r<_6Ol8P|!)VnauE@(4
z;h~EMHieFeAOAZ)U)v<yzUjGLEBLBN19txJ$HD5)^Ey9jI*WGoIGlnsC}-jqQnEsG
zd-Wkdu*;2DQAh1u-1Pc>o=Ecen&gf?0YgGor&8H9W7z|^!f=x?!Gmi<&|}d?Snog?
zs?E{<($*)dh$s8rJGL{xDPmE%j=s(RWU8Y#gPS<44#HCLbg{O_hF-=ivA(~I3eRE_
zPPwKf>te-2qb3>30@vy)`}#GP%F+&ZjZV_G8XT={Up4$^%a-oHgxtr~XOPO^NOaR1
zuW76U(UYtqO^dOoXZ8vT_*nNz$SYQ{FWD=2EOw>*cQRd9$NDd`PWSO>hH`4935)i^
zN4-tLuAk&hx|ziKKLdYd#s+7U=d09jN+N@eUqK0l@Pv&+gJM>N%K#H*dYp~lbIYcc
z2n0`Gbje8%-a3Rf-5i9E_|f&`meajXpv{0Y+ht#?*A9H`L{j2_IeNLEGrupE2Sfhj
zb2<FZYpx-se4306dbb2j-+nVZerqHS((C6;)&&0va^GkS4tr0?T>Lz*B+jY2p!iYL
zF{lS7*(~fAqeU&N<N#7q5pbya>5}mFAg2RR5y*v^_yfPDCi|9@idRrcDd5(r6THj{
z*Eanr($t*E2&>uxoD_lPf)(^KM4Bz_PW#qQH-K7n2yl5+(!z_s)DW#9#St>Uq**9P
z3<y)W(i6rJ=gKRoO-m0MEH_1arn~auhO6t=xfR4uvL3$zjKj||@3>D>0^A+r=ZNvI
zcj{Gz`J93;!pnb+2nW~AHa0an&6Te^2ziH9^fEpuIQh4EPdzH^WkmNjZg(bXmIeCc
z{#8i(Y{B5slR5oqY0x4W?|T_>BDt<?jTrR%x4}MExi#`ICZ`xZjN%C`Ln#gRxuH}1
zG;k)>jDCGTWItu42b@XnNcY3$ZX=NmlWIo&KEizXMW;n@Y6rAZa#beY5jC>!(bv0B
zB}j#047VLL+Gt=siqwhuqlL_7XBurT6M-AXA2niO8MwuzH4o9N63!%l+jG}lBXkB@
zVLgW@vLG~Pn~qhzG!ocWXYxpPgF@~R8q$;cY?5?g*KEyvE03UNIWU!9pDT#kZ^=kr
zv<p1@U#-rMgeGt&48Zdn1I;r^mR>A`3TxIRG&e8Q@UE=vn|c#`X!XGbX1#aeCbViM
zz~i&J^!xH`1T{<m$axyxZ%<QuE+cJPkckUF{*oR%t%Ej~k}WXNc^0-ip>FapJyG6E
z;av9<Wy^M5Lodo@;ybiPT+c1tg&yf-;@6#j%U0#Bkr=)77e6WI!FBkdI@!^bAtY#0
zEE8YG`qhRh-bVc0-NzBnQ2rgBoj*@vl-c%i_^)dqA2ee2x%?R>m7F4|CZud8-X~~h
z_`2*s8)gHg>x}Yi!7d@)#6R>h{>Xzvw>$$3stF08)<K+?lbiVnElh6`o|VS_DUQa_
zuQ9R&)tQ$63Ko2u0WphF-pdfB%|9TuSmHlKt-Uk3Cr<ogMBM}QYc_z4It{s{i#VtL
zLC{Uj0(S@WJZZ{XhX89ZOW>+{_7z!l2tC5vN0=!yuAkN!`wtu~ny_lDb;P)A?rwkK
z@vSqVff)ng^#1sKPLYa4Jql>p?D){5cQN`k25^iio&7Z<iq{d0lfjMHPF_IHe9>sc
zOwCL@UrJU=wKfvcfKSWB>#~E+-{9#iG?=&!5I@gT>>asrHE9My4YI74vD`p!M^y4&
z#19o`QfvM8^*{Pz?jp@(xbiw{%MNAZpJLIPR~j+4(Sko74tnTS@OjPnqOVoV#Ms#<
z5&DM#8yP7)Lmw%F5UB#N>R`dc=kNVVCBNAdp#ljs%snMlBF^*~G!Z3d@}-h&gp7ph
zGGv!T6Hj~rV}bj(280Jh;bJ@T_!*sz9W{w3Hh|D$;+wfrq$7(|uWaqxIr-6SNn5^U
zZi9{vmwp2JHSif-kE3#9k8Ex-ijj9wspM*Eyj-#Pkek3BF@SY~FL^Of=3afS2DAyX
zgqtJQH<XQ2^pOY^q{yQtE7qL89wAs^0L346nr~1n7xi6wYv`>>c#Q!cq0nwqv{PJq
z;N6$O$!H0j_SJv1byuD^JK_fNV&$8JuK6tsAA9*qLCwbXi!tJf@k7U!9LP~Tq!DZX
zXmo3oHN0t@1%ENz7N}TH)9Qd1^CoXcrrvJES{8-uew-P*3L6M{7V_7&BeMjm@h7uv
z>g7XFKo-^s`T!-Zi=ua-xQzhc15Ww6O@U@=h{%<YB0F;gabt>F53fKaAXc?e?wdPX
zbUe*Um||bY2(x}(oToJ#p$xG&6Q7&A>Ym@a5$b^IL=1@sG-9?4{Y#|klpfm_vr_6>
zx)RuG&yebEY{OqPmr5ryej1jbhXBaDu!u#~wyKa(<oYJz=mx*o!g%2%0(#i-^Om^<
z&VO_?3W|d5%2VtLHaAjwwkmbfzmmXqjAEI$zBW%mLO_!+Th+zAB}~N*S)xa46v_Y7
z^ZP&cVd$Evq29v5;s+{Q=INuzIHkCk687KrowfvTIyC=UX>jykqN5`#DawB`?!wKA
ziBn-ku6d242<pcVE=zZ5tGJyIMNFq`7S6h>(wTjo@dVON1>$!EpLw&A(}O>z;Y_Yr
zk@v*!OZDin$v{4(sF&fmao!T+b5{Q${le_{CUPw6Ul;BFL2PRX!ov#W0$2H^AXH#Q
z4j88#S-+lL@lqE_6<iG1CuXEOE6aDMuHR6A?6bf3YxZ2=#SQ-aRS-+=IXM|?6F@e4
zbyh2WhRFmYJ<(}nXl?g&spO_2xLXcDVD!6!&b-7!lW&qsoXOuW-l(|^Mk*ZpiCKj7
zTa0S_9AS=@8weJRK&T)@+9=%Kh=)OmG0^9-ZZu*l;VOwF_u_m6;l)9CX?VZ`g=LH>
zH56?lUv*@)8&T@wAUoLr6*H2R4tKMU-G)JNY?DwlE2kiP@E`;ajkXcy&-T<R`Sm6+
zxT00|GHfG%%(b@;MvjgJa586w>FlIOk^zWzg-ramvDKrHhI<IFm5PZAM*f!=iL;cD
z8D;{rp=A#wVC?4zC9Io-E(eacb6(Fz@=^!s$TttS;zo`c(oGt0b?UbDH7S%F8Hk>^
zrZYKY@oLRLbtPhXUba9rdDtRukuGuynZWq3VD);Rv;db>*ZoNU!v-GLFlR|b*w*Ke
zN36SzSjmifUA9jIa;2<*0p{C3C#+htgVFD6ZWcyiB}w95zl<UEq=_@RwCC;0PWFYV
ze*+H2|HsYN+kxsZ$t31w;#zbCow0Z^loPQ(_A;WDsE?(4M%{ovM=WonEPt4}+2)-!
zvXN$KJm68dhUWp9eb6ANz?<|DqYeCka2O7-{2MWmkBXj7w^%_?&QU=GJM`1@`$NuX
zZ{C}Qo~$>`KYVy%XaPTP{WeP_V-IG4w1ySlh)Hmqls7EdkG3YBiFeO(GPp#RnZoFW
z(>`JxCmv$p<^1hB+#3!)#`3QFXMfZ*v?%yC$JvwljjsMm2cZ#RZEP8nQza47ftfZi
ziTz(D?jG^=(5sK(5YNC#`~laKnlr?6n=V75zz1Z0B*kf_sjyMP^iMIX>sy_bb#^q8
zJ2*ZrUp~Jf^6;DW5~Jc&AJSq}97Ha`nH;X^@toxlx^`y^+CtM6^PG*LC4f(m%=5nZ
zwWlRvDRhnjs>1j0$=`-jez%Z@|I@}$RoNP&drTF+NCyFbaVqa#n2y1F==Jvk;7ZIO
z{@niQ3xSJLlt=bmC=kw)_J`&TK09pBuj%>sz5jm1GJRE{oNoE^;*OFP@8GXV6=P+i
z2OI{r=s;IV0fw>0WWn_su7(68>3tKA;^T*LUk$HB5(Zz=2y4|`FggN0=nCs4S{z|c
z*KTpfT(=mwfPItjW=h1eO*FwtDB%ErVr1=`mmgZ-nq)*HW<GfP&9)8%s5rR2Cr4y~
zeh9I{dnPnca!)bF8EuUY9gIURK-`uuQ1Wr#kW@<1hYpHT+{@TOiOJc{-T`%p+}9)=
z$Y#hjUfPCmp{z+Lz05oJ5}kv*IFL)rQ(NP!xAXi~K<Z6EE&gZI%>Y+P6m~O!o%JqK
zly3_>3^h%rjpCZon!xwfH-f1X2B3{YVaVK**abDMD+n*BephXd)ghz+7cGHOhfl2T
zU78UJvKVE(l=^Uc_oL>}KuI9@Hx?J`;%+a2N@NeBUut;Jo7>bV1gBW|05gKCRp|6}
zJ`xr6JR5(g*?Rok9(nZ4Rf_yAmR>1IXKEtY#Ww=j)zPB7rKyJM?EX_a_%}=fL41fT
zX<L-WHi5K)1SrbAJSdkTP(Z$87C67Z{dnmoQVD4^xvNhgesq<}e9uH##B^N{#n=2i
z^?=8cM5P0L7w|ckvZC*%|3}rA2ST}j|KD!6tCgb2IxQD%D3WBEc9lv<l9)=R5|Xm>
z+@e&-r&K~r%S9zgViKAnRAee!*2$7dV(c@_^8C(}_j`WdKRupj-pe`Xb(Z%z=Y0op
zL^Q}+$?@D%EanQdS|x9R>-N<*SZ}zx7{zIYe7)WNix<219m6ZLgnonD9fIm~>SD2I
zO=S?e{<Mo`wr4L1%jZk3E<XgOPs_Ji(LWu~khF?!O89vDm?S^3y>RO97>PaX+yWYB
zPqEF2J=QwV>ykJ@oYHwZx&jaC1xEan;ee2`HCWu>df1`x;rwN9NO$~NU!yo~*N?+3
z-;<3H$TBZAP#0S@O--sigQiO!^C17*TJe%0rYB%yqV@!rX)>^+fwO55-pgz-x?+WP
z^&g}rg2Hp~5x+WmSUtq{6%E2c{oKY1p;62K4bcUE%P9>E!4o!GD!!u=eDbR4dm-Kc
z1+ck;$qx^{h^FFEqhJvyzHe{;E_L*byad_-jkUU!AxvbhkslhxbsYglS{|jf2&k-D
zA=xd@SKa2k&BbU!^&8ok=(dw}lvlE0;#>~o*UoKM(_g!wbiwlVvUBH45^m>)-SXbF
zEUI#OSo!RtePOo_tH86Axyws>nLKCGHJ#Jc?AAT>Q^`C1bh;>zHxlgZ?D>*$o-l3U
z2Dt1v{829lp969=g)z>1*dNMY#!teB;ZP;~sG@mVV66f=ZMX=z(gqyo%TYK3g=Z}&
z%`OgRmn`I=XgHG|Kt8tmESl1!HFhv`uZ1)m66N)cbI=tJ;b=9a<FW1@lN&C7nQ-nL
zx$k%4uMwPj_;jBMye5aD@^Vl-soB53zgZuhJW?T1yepAX2kBvRaXdeKB0>Iai@wVZ
z!^e9B6v6e_p76QjtCRUQoW12!FBQ`49$t<PJ??xEpLUTtkXF-7+^RdGq{y#1U1F*z
zNh7w>5_XRs_ml59CQVBAt>SSvelh)^9@I7oWvWEWi!W!fs6&ZQ)$lQm3movo#d`1G
zpoGs6Y~dLCNp)6E;FObSn}}QWR3&eElj(|CXnVEcFy{!*o7XiBZxVHL0l~{DiD++p
z#K8x&sq$j~L!Jds^fV$6vB-sRxO?e+)Z->4JP$6E=rs=|dzVGzqJsqTOg(kJd4A-;
z<lp(g6@;Laj22och~qRgCp*aG_)qx#ixq^hjbu~B#8-0ukl6QC=i2@2lq8vci&ey~
zkcP*pc=mRh^tgk5U!>6R>_q$3gHzCf5FK&`uausf(yE0Hgh;g(&(r>HS6;=O#yMC9
zz!!nnbqqX2&VQlvKF%ca65*6{iWPwlu*eCJsJ46W@`k%J5nJfla0w$Xoep>3lm&<w
z1RMpICLBn0WTA8gQWHuj@5Ed(;rXK9B2-{K7eehC-=r3!93&#Ho|@LPJNJY7MRb6(
z7#NX}v;Ol&M|3VjOKxVG?05Ry^R`+Z?Wa|0Gc#|<_TBwwnut4@8==fHx-NNg(kYy8
zL9T)^-znB)PAJ`iP87+%;ZUx~z%6ZZ3_AQIVn)U5y8iuqXWI{SJV`>v$a(&?cRgMV
z>Cqq))^3%*t0ZveRD2S~v}TjeY7WE4cYC4<Lxr{W<E$w1q4y!QP)QEi_WZ2PRyaF^
zbcZei!jxMLRyvsJnCpVcmd&C4n|<lAq3BFqa*B4YCzi4J%SFs|kXf>my<={6m^va0
z7g9T~+i%bgSDl3!9Zpz9OBNUV>~WrfPqvppZReGNMGn(Cr6>N7S`wE1P;FKEZFwx#
za+ApRX}Q<4>@}pDIU}2p<vy0ryf+t(3f0=)NAJ6f`yMSqlp(gl8hW-lk_oDKUBp^`
zX#vgJP-I8Jl1YcXjOFh6kiLEye&!9Qii3Cd*fiN<fP~r^D_&8xnPWCSUiLmlMoxWZ
z*)4du3QrfZ_6$;YO-r5B=Yyq{BQHL)*r@tKR4+ctBsJl%dbIjkz4bUOt&oprY@5HU
z_0DxeOf8vgLZ6vo*LT$ft#kk&BIHHzn>MB!&aWo5TPU`VH+KtJ7C&)L4gsACUQEgs
zrES9n0^~HvRgukz>9JA8qXr!#`MVx=)fnBA#f$KK00r#n9yiE85GOHV^;Pp^SjL(-
z*+!Cu5;i<!Q%m>x7qJNQ-vH7?GdHR7P8{Ai64Fjp6}n2rpJ(DhAt}V)A@xjkTj>y<
zFPYHGd-9@bPG6Js<e3U5p%p3?4JYj|UdX``0+vS}2`ZNIl2w(sZ=`W4v*7@yAXP!E
z+vaMh(#^eu1bS`SMN!t<T(8leMp2UVn(Om&KlL)^2;Fn2?mu%vsEYj8)-ftmBxN5;
zTEZgR9Fs%cr}5q~Cqkxh@D2qQg}V}Oyys@Vpx~KDe1NOF0zN{IkQY;^I!AvDKGw#<
zSv-+^^)NBsJv$R+91&iH)O+6=4!d`iBZgwmYT<s;erNnCTbjP4IgxCC^>0UxIqMeQ
zDaaoTWW8)KJ+MU$50E^O%v!cgr{A8BbAmj#@$=MvHk}*<#fdU|V&Ra!xKQ`T!Yew6
z$7ql!R9EVBHp5q%)3l_8`0ie|%ORJ$7bhYy<_f#rouB=%7ve0IvKeCQD4*=@>1O{Y
z-_MKkxg_J8s0K-5#%jJy)RUtL9ocuLA*5bxBXF%5$#EZFfEg-deK-B4f498S9(*`X
zi|b`w)H~vu*DXEqw*yT&x=--d#TKEar)4+2!NsJVCan-5>>x`my+vi<h_4LNNA~lD
zylVyDj*T2$fVZt0vd<Pgba~?Lk2$9sQc~{hPJN`PWP+0sXrWMtXLb4qXVzr|eZ)5C
z>lA*fLREZ60WHc65+(dyjF=ALCfK@=R1NdPJ&tz$iBTwH8{d&K(A64nTM<ch*L3ln
z+C@QE{90;tF>cg?0*#=(d=mUj9!U-%Q^;m~DSUi|e2z^k8YCYKcQ>k+i9h0O#35MM
zT$Wi%%T&BB)g1!Y9czs&d0LnoOytELZO44&l#`mKA>9l4cv*Aq1@8K;DE$(a)%8O|
zR5J|{YJ>cfHGWmdL;>VJ%d==sdZvMAYyx?DB8nW7qKIuUc33Za^u%G-Gj;=>G5cCQ
zm0aHaEuM6>!eWyEisOBZ^Tk8M_*?GQZhA*UK;W0!VwA`NnCll1nDUI-q>5&&1CX0@
zEw=5pVG^DQWYV$D8aOYDF~jWTJ6GuW_;j~Rt}sgpBVGDLg6G*hIH}HC3QjeE!%Swz
z2Wy<M#(>(QqAQ}pO8T%g!%U}zBKnj=G!Jx(u#9+G@J+5e)Uq?a@58&-0JVgfwf{cb
zlZ+Wak~#NSaeQdJp86kS=tS5<$Bj7srCGk(2y-Rbbmi}wHC))TVHjW)S}3_c<9xUH
z$zvW)oJ5{&@(NXB{DnoIyOAF)ZgxFU?pux5FDcfJ)Hm1Vxct3tEQgYu0#ahk$>OZR
zCW9~3G<W8{TtS(Fsm0yiO|gib+j8BCj|GAq)k|&adG$Y2&O*#hPzxN*yLm@kppFES
z4Pry9n`Wu%O@N6;5&4MHgis2d^|R#3?YMQ)nL$N!Q<+G=_6uGBJEE5%s_3_j{%;@z
z6O{+xRUJc15t_<jd4RMx(}Y!xeQOru4KILNjk+Um&eh6<qlGd-YaBD5-C`3UMa<wy
z<aEw{aiB^SHY}Kw7J5^f@a-j9V}Cf}IHHirz8NvUNOTuZm;&^JI7|QGQ^kld#GDpx
ze(tu_r`jJ_Biw`eTJ@*>8=bdS`XyjoTHn3U?S>d^iKQ*{Yj$p1J3QqqFltD$?U;mm
z+em-{f{0*92|il=<<`7eXnU#9e&oZ8sDX6<qqsmqlKoU$>ZMe#i&sU=Zt?6in*Z_8
zJ`=o0B9NJT@Azgh{eNP$LSC(>eyVG0;#eUaMV<hrEI72bKcyZSw-jj5<2_!hUfXr+
zzhgVZq_%KL$ND$l7pGwkr)=h@lDP}+f6VIH-CpFGe1so8Pw13xvUT#J{)H2<5%|Y3
zvWhT!_rhiYTBU`8*vDx&cvj6LW%Q_Yx_IIEAKNQBN7@%-v?v9)E5^fWSiN8x;sw$x
zjQIK4@U>Ehzsnig(t<5dB$cGZoNU6!f9s^km-17QCC~>oG$PY)9pYlNEc$A`eZIci
zLxHh9-Xm>!@eJdN%Bx&v++>XU(_85FD_!G7;UdiUlB%*O^|`5*)hOYb1XJ#)f!_;n
zQTcSFeI)5||9&=TZLHabSqmty{N&X6p(oDLADb&Q<D8CHUeWX?Ue|*#;@HoIxZE^b
zWCcl*<Jel~MtkUy6GGZ66z}{(<hf9@sJ1liYkSrXyichL;$6nohh@YnMpN*%D?<fG
zP_WO&B<y3m0^8i>sPSwLVG_y#5>(FH|86KGX*^m!3H3sLw<da@NWjUAbyea?H}!d^
zNZx)ds4T$Cy`}yYYh*ewVo<;<G8h);Ki71@(m)5r9lWl?k^2lA#snm$SJa1vC+07s
z;Q5Zg!hFped!B-W{=49|2&$WE^Cwt?&+`A(q0ZW)x8%ttr$6v?*A_C}3)W2;-<^VW
z3iHCK*q7rKp})rnXSqwdMXqz(2)B+uk@99SEG&1{6|-Zr(V(%@#Up8V6$4p1ILj{?
zoW$DG?>eKDl(6gq?<=kHKX!58$_dO%u`|T48EbEpBxmE2C&Cz7G#*hqDjKY|!Q`@s
z7AEcY-u`!XHPQl5>|VNmKtFHtFy@gANE7FX%kISsbEe`wvI`^6g~Z-a8r+QwY{_HC
z$gO*y&pWYGnz(sy68WaM`|hK)A-l&RaWI5%MOnmT2gC6w4gp={yyxvd<a0m)ub_5@
zSoUbPy+3)aKpqb|`-gySrN1ljW!?nLtwWHueZje#w9rIL@Ps0zA&pO+nz|Y1$1sD0
zQq*UoGrK#cpFy+OT}XXo@jd>(?dq66*z5V6%g1wSJl=L0V&?MH5{e3w9!16H$W0Me
zO<Lpjt8kbB)=g2)>B|vaDA+(i3b<@w>6@PC1+vIgXaL*sK~+GSf}6CCjlCUuZuPVd
z-7zvl;>8T@wF=?Bm@BB}4B>H0!9EERpTDp+3$t8IZ?xBFfJ;br2f`6HI6E5m4u~AT
z9mhJGFlwQ25|k5~ePyuD4xC6N9PL_>yKQ#lZ)PPuCyN1=9O~S>ZPp)M(wo)sAKZz_
zgF@=p2O>D<lM|4+P6yp%u+Y&)?zEJBp<XdAhOx>iAa4R%sQ?@fdsa?~`F8voL2aS$
z&-PBRyYmUN5&@asRYOf0d(u}a@-Ket0R<fc_HX9T%a^uyF_8KsRl!LV=g+^JC-rD#
zAO=*LSNu(&ggKg=GbG|(;CbES+`<}UE|ii|pBMy-W-jTsfAJHVi9!$#%(^xgp9$Le
zUnU{y&krN`)AE`es6F*#0VpW2WPqFK@4|5Hn2y;nW)gXy<s4|kuEVwhsop|n9{c2%
zdQxe4HA?v<z{J#j(3cs-!FCJ*<?+EO-R=#28GZ<rQ-N)Q?uD(VuM5O_kh;B_zClj@
zc<N=0=aUNAfubjG$hMxwqJWodYSN_56LsQjFMm@zU>v^n$of0hmkdk)NtluKuIBAC
zog(`s-dnsU8pC<Q)g6=L)HYT<x?=puxa4Jq;AIH2Yg9BKXyTo2Vsb^LNwq`yJ;dJH
zqBa(1sCD3Sq=z&b0%g5FALJF7wtQZJ@xgWyQEQOOka1n_a)!C+&>Np$Wn%XL&o6%2
z@1MB{K~=OhTn^A)eEEsM0t33$y_ckWo~NsIcC_?a>Pxc8W6xdRfnmhLEU3<!Q|k1f
zAVT5)Y{{##JYl#DDs>~Fi@?*b2~Ebf-AHQan?+)$XVn@Ey^0=Oa|UI?B5v3-vxXW%
z>N+X5(BjRF-px&h%J-zeMPh4lDhOtJQS-G*K@5r-N!pq+w+!bjiNi&0l@Nb@IHAXJ
z=!G(tBv)v4YLvzHto!=j9B&f~)R}LOILbX3PYw*KWmja5%Q&FG8a1|_niZ&N@z-HB
zWXX`mKX?>#tL-5xscHxc{lf^s&|Ddj{r@L#^ieVIj$-(t91rS5#J~ffqfQL%*c31w
z(LfKd{P>0~E!CrFYnht>YEDPdP03ZUe<CGCj2jYphAU8$tDL2@*Mx%h7fV+DY$*Q|
zO@s=af14^S^aIsbVMC@2igIiM-kjcw?WZYF(s+})(9rx5RCWH=l^N5;8y2goWjlqK
zNvqP+s(4Ia4khZ#3+!8xZdKxU&H+F7*Y8-;t1ee<swL^~AgMtv4z9e*a@7b$;H?L8
zIb^@5xWEcSHFHfc`PgT7z^3ZQ7+l!@g^+imY&YI%YdVEV69J($ulYq;wOUxFJ;A>$
z{Lz-|JPjc$kx)-<an=^5>|H5~Hu%6X^3kJPak+kVlkksO0`WaX%PS2lq-CwN2cnpc
z0DbY-%B}c4E;JH;Ul5bS@x^FBszRqBBUjBP9~>86*g|)9Yg|}rZ<K{SIx=xoBvX3U
z_R~-;1{@-`m(`hTns;|Vs^B5)={optrPuhZEQHiFXusmXvTE46I}EKs00kq9I=A}y
z`6As&f<4V&=xWK#BmTx}Ri+9qA#YyQ#aOI?)oOSZm;J4@<6x4Cv{Au)h}^1P=?cix
z#o`eC09w{P@Ok`|^?fe_ITHM<l+p_Ss(u8^65Asn<W_9EuwPpKM?e5sV|WhB>#I2?
zjimDV<l2y}ua=_;Q^71;|7v77>(T&PC<(yIZg9HbnA2m5J!=*i8tqw1$6GCMGzWV3
ziNb9|b<b0>uu>v5Tj(d#S`QrEuY%Vw3l5pS(7y6GdzbXy>lt1!U7|FI4c!!ha%_@=
z`64W?(7EfBhuHf>rF$$w4blkkW}FvQX>cNTfY|fS^Tkg8vJT170wpU;-sG`dxOj$m
z#=xT~F>Af`u;oVn-9nz&0Q~xN(~{nT`F|_@YUp9FB@C4>%J?rM&<RO9VOdDkPOG$w
zZEs(UyuFMALIFu*k~^M@j_q}xh*x7biR_9FeO%Y*eg=!YVIey!rD*6)Y*z+44PamD
zweotO*RzkRf+W6r62Tc~8FKMtCRVXf{mUB8eBO8b`$Zb&(Tp|xyz?3_W(Z&Zi3#UE
zk>ub1m~dm@y(cc>)oSr%B-?$uHOs^k6_GF%TPR+G0oLy-HY1RN=#)uERPMLL>OT<k
z5srn_4qiN?GNRuT$3AJ{y^JTO{wCl104{!a%YlHERm{2KkrkduO-SlY8(-WQ^cj6H
z8<{3PVPxT*ye;yAaBP&5S}imcHKE&l0!Kd}Y@+?1dzpRrJf4^pztrQgs3GfSwJ%<~
z?;N2{G~5PzIu-i{s?){Zy2PG6C!Xpnvd22_5E0PQfvPjKU{z!>Z>&&HRbG7MI!_q#
zwfHz%fYCf5Wkpg@ZQlpnP6%pBWpWK)#x@6(Ibl3W08ilF<euC?N;v+x1hCco{OU^j
z@Y$B6p&#pEBO@-s>2K+4GsMnrwp?vp^?!e}1C+~I@#RL0Qk#hzjt<M~g+lW&AtJl!
zy)<Z8IgV5Whjmldg*}FnHVu0|^?GhDAl2Z)h`OH1lH;~x8;T^cW1p7&$q!O_NWn1;
zhs3tSZK@NBD>Gj)UevS@Bb6lisn-|$)$CXAy~T_C$0M3lfWvy&)4|5%u|5iBHtgV1
zom`d#H;iIp>I{hodcH2|)Tlu;@w6x)w+1^EYc9pULpQi){wJ%W28uFYvE;h!?0%I`
zLdA6J+J{5V;^&Ld04C66ZpW0gH|Y&&NC=n;Ewmd2%@3`mTGVMG!92kJVQt@qp_>hd
zbs5KYo5ZHy$J#-+O6;|Azq;e;iAe2154+T|vnsnjEWt|3S5s(a`rfU)^Ylc-+N4Jq
zd1jJ{`<9L27^m2v@&>f42dBA7U8x3%ZGO9^s8|&jV6njo&l_-?P#lE<_=H>`bxGjS
zZege)wy?NR?7uhtV1pp&usi0TKx<N*Zd$yeuUQ*wOG&gS{mH7Rhdwx{Bney9udLpd
z&P<a<yC#6Z`PZly6;5ZRLs&pwdRkX;fT0xf5_|P-*)>!gdw(tfdoVm`T44{JUKI7D
z?)D!*7iw@;d!R3d4jFl|PUYV2U^(+(g~`IVPT{1rSOct^ldPA*Dl96{am{L^V{{E8
z5zj-?Ave3kxZS?-ksN|La^DY8Y3}y%yiqqbgwc`?hDl|r|M+NNlaQPa9)_%6+7<g*
zcpl;MVsTay^Sa6tDYf9z0f!q^AC2=(Fupp2_e%e~U^q`O4cn^(*mmz2Dh}P3aOJ(^
zj);C2)o{8BcHU>eqD<~yY`Sh4g;W_AjLt>X@f*d;P0}LVD<B*JkB<6plf!efm?73p
zY*GuoMO%R*!tC%~_R61UT*UuMdr=7F#W^tw@wqZvrCYunI>h}MuXIk)2pd$h!M4eA
zYso(D<c*YeG+=3eo#F$>jhHXFK<9jxV-EE&ZUzOtg}oUTX<rJT-N0m)G^HyOr9Tqi
zk|qcE{oql%U7yKj)XhMgPY}n*=gWN;d5vZp&O|r~^{cEWi??4flb%*UXe?uNPDyTK
zsni&On>5O^JQUR8yRa!b2bS>mNDO6`^l*6nG4h}DjMMDTzxrtW@>Dj1iTR`I@%Pmy
z9kDP#I~c1^i9XK$bFnNy|2@vP(cuS720niy#}_gSOYW=-EOYC|bf$0RTb+L2HNBx-
z8rlSxE|t7`*+UmYIDJbug)~phnP&D0xBjSv04O6rrbuQA@(`$ry}YQ&r!VfUQbt=u
zDu)pr9lH|CYHz&2o+SmkD1ZGvRFUSh9VZ!VJx1<zt}E708;8(m9iX7!k=iGBg}TT<
zpl{%7+nLv8e=oMdd{i`%+_+8gMF6u-4r%?YN#qg6<0cyuGcU|q<T^NNe$nuON#J0V
zr0H|{wkGcq6G>9Wul{wiEuPu;TN*sPJx}41Q_`i78q|G`!6<$tjX3KLi9$tXjiyGm
z$o3^op;X$Nd*y2Ux4)6GdZLgi>}b)@(IfB?>PQ&T8&PLwZ*~%66u2(}wvT&&L;bZO
z9P%I)3fTp=))`M~95MLPKpW0=3ut&@hzCuD5SvD`_JN&iJQ3RyU^KHH4(d#CXqG|B
zDPkfyyewjLR1c|4i0Xp-$+#EDytR!bz#&HFO=zIV<xQz0XCN|2I$3`3;}tgMAdN+y
zt*5d|dAubB-((R;5&ys%3`2V8{Uv)aX_ydYOUsaXcKl})dXJO`kqMl-cgRQD8z~m>
zxUuZa<8_Ki?@B7ZDcpfC{Fk^tU@SD&X*e2soErFb9R4x;FeWdhb<K>-Nto~Tw1u|O
zj{;MV)BeEt_6d9j|Mq?4UK6l}4fM!+LMDWg*DYpUQe{uY@ia)AdT^yjxj$$+{*Wy%
zZcrIgv)RqPu8qy|Wns*shUoKtKBi;l2ZUKB7gCS4=iFJCWbovbUU#k6{a+mzeZjY!
zv9jE^F=HJ<N9I+y=HX|7KIhFe3|)%~nfqKG$96Hy@hUP>LP!txSKl^<Bia*^7DiZC
zbosg)7)TicTypl+aBZg_7|wP>I!U~7VP8vI3z8a|rHD)oU`AcYX~y<5xvG$wS|ryz
zOGg`nBWc#cZtxy_`@axbJdsjsF4f?C<f0%w&wpm}SIlP$EJiZG&U*Q*x9?Wg!I^kh
zsNm*&_>+Fkb97&yXsnahGzjfB)YbY2=E@=6YXl}<YL(Hz3F!fH0sydU(UDrqbi6M1
zR=7`4#Y9W|dgv1dWly-Ir)j|Dc)C3{dMR)+ENksDe~)ZYt3XnePI6PeGOzbqQ5spS
zOu@TM{INEeVlhelVn4k%$OVtC60i!LxOQ8)moP)EfYX|`<+|-<d0cFzGZO>IIv<zD
zp=k*S=$N4aq;Y0IK4(95NpBHvg__>4K8`V9A^VNQCKW8zxWpZ6H33H;tfXDCy?I&X
z6eNv#(3gt0X?4m50*i{Tg7}6`-nJ9&{3U;2C)O;Cy!G;sLTg?(_FkwL>!~4*f13Tx
zKo%1Tz4n5Z>n=EYz<Lf=LTu1;8C_dX?R~B#4abYZh>z#LDY%FYrFnMaVH2#zYuWu{
zCmjN8!wD?yVs#pJx<kW=R?22>gYdI@1X>scte2q&ucy$P3B0g{VZ?h!MyO@iaSU#x
zb|E{rp<Yf{FTmC(akDpCy~WH}kQjh4MUqw6S)KBiAlOV9ZPNoVOH5ZJe_bbsoC}Jk
z&@zPY_pr29FBNMOBC<DnQ^3-pSJ47QANoc<)ih@5hd%ytyu+2CPTXD%4n`DLA}fjx
zkppJ;;pf6kk5!04qd{+`-%|G4bq{kR6#_GPO(t_!=sd(rphE&l!N8(}`{}3f2v)$`
z-(Qt~&K$s>vOvXX8?3#TViu3%w6<V?H3ijV>E)b5J|s0^RK%sU9o{CI3!Peix6e$S
zMDnN7pPZff%i;bNp=V7O`_!hp2JS7N7K%<yN$9DYbg*1^p>ibBI%?1>rGoq@;MU?m
zisYEcN0*Xpw307P_vB0@BFs(9PkJR{gM-j$q3HXx-XgqxQ$Cro9hIyuIv8Gv=iJ;f
z@$AQ{j}KEihEHxgs+t_Xi1F~jvih60XXm8YOgnosb?3SR3#`{1(>S~S?3{_;RN`|2
z%-C7A1CNICO<dEndi*~X)Hn;so*2=|jI4fvZdcEE7xMYedBsn^6IUCkbbG%3(u4l9
z<M`LM{I^?CWChv2Ki<TumJXf2X!T(Ir9+>{<xowzRn_j$n$0)IUScg33A6$tFE^e%
zF;iJknk>KY#s)I0QNT_m)Dnq<YLTKgn~O8c?vlnm;(=2|H$UD=Sqe?BHEoteh&j1}
z=-S(vu4IhKdHmi4C}DrE){uE%33^1VVi142lxQyJj&Bo>{n`eoV9qMzt29}@evX!K
z5tg7dF<RDadD7OPEx$&H&E*mmjk9`;9;@SD``edvFgQ^7^k!<y!Jm}aUrRNBlH0b#
zNt%q~XT3<T-)o%=)o&U6MZ&lHX7k2g%Yq#FT3%OU*Y*94#NI_$4mb?5I(n!Z^5@N?
zFX6|dAxOI(vF*T#^<vAWeQ1OX*v#Ivi+3{o3cpZwuW!m+GD_q0P@DR-le%yIg~&iJ
z>ZX6xiCGo0RK7$!Hjlq~;@AJpjq%>tzHJ^}|27YDN!qI9+H$*?cW+XElQ5!_%U}*x
ziP8jCbJ2^0E>Ap^T=hW1;ziNdO3(QYgG{Kx&aX|KmghWC%ltRFOd3gYrr2dq3GLm|
zzJ<T$#{Sk!q~*E?)K1=y9&x<bR6oPz`neA45b?S2=)03B;ibC0#Z|;3<}l)g0rr0{
zDN_~bziFS*D3FN>sF*n0Q4x(P(<pLvxDw|z5M40`y|%80Bz$@jI}7~hAp(*e%}n#5
zLpuv!QeCfN(#+tALi3^ykL<12*nobpv{A&#FwDA3A^t>*Cl!DWVo{xjUUk^(RQATF
z-^ptNv1`^z?~Thh(YK*5*-#;E=)V1(J^LIQfl6<nQmih^a-E=q{TNfTb9ousIdKWR
zI`I+xv27w>L%(wE@@^BC<(T+%SXpK2fK8i95u!A;ZP@Bq`p6xVHqI3^1gYmi?#)|C
z6}8Kt9VVgflXcf+RdcX19+lH5O5J&J!lZMTOaGTS#&f)zW%<Mo^3|Vo72Ey5V0-@C
zsVJiD8x7B2=CglGC1=XpF_xhH+e!4BF*k%b`Re{Nw#&qt6hg3!p;#=dEIamysa;8J
zOE?ozpf&mmeR-jiZTPtGYHxC^QVbebuYAy|Iq<;dX1M{(*AYuas!q3t-K$!zqGc!@
z8r2IKtp6-blVNJ2QJ19<pKPpdoHtjMLCj9O)k&d=vJQ3X8HM5@UJvp-Y<YRNQy8&V
z#yE=kPbIM>qns=M;tiG^=G$&MQM`mZ<gAI9(Q=tchN%1|xb`Xa2A(njppZ8C&FF^+
zVG^PcQ;9(g_gso<`XY;jjJdp$Dp)@-%Q>80auv@7gwe!zR~W4c%O?#=_>&gfcZiH<
zYlU357~1s<19%;vghSHqI+yR+jDJy$LF@@EHcMlin1uk9c6e0gRNM@u(Ey{_2rB!b
zK;?eXkHLLb_|fF8<m4G6X`vgnUn2&xhgwgDW6eM1>XQ_x+E0sBexciSfTN`c-81a`
z-QMCiJBC>oANDN_+?B40H~L%ymBA_zB=f@2+A`h`Qu<tZr!z_2Y529MPG)w!YFB}?
z>C?+-vxw8sprYG>Vy*Wlk{1;yf>Fzvo3Rd>;Ew^L+ercE-g(u~P_8GEW<tk<3<`E{
z%WAoU0Y5uez+zagIJa>Zo(K=RIGlf)P-&mUUXS+<x;TWGzc4<+T7oy_MWgs#(sRw0
zwHA2LE-wKm`rq^d4{p<1{3+Bdy*K+3Z*yWMUcvYjrbWS>24UJr;TU(5fbFZQ8Z}h^
zmlcIyTh}8tm=l~=OTB@)%m@lu?i-ur$m+@<QPE=%cTF^Rj;wN*QAVp*g_5Nr+9YdB
zts<n1*kPUY8>XY-t|s-b5NQNnJo=O<-}4#-e>_%|P*Cgd=i(aRVTvbW;6Yv!_LMFo
zs?H(_Fopuk;VtDuF9+u%W)j7T&Bv801RBOoK?G!<?W7F!R5>m5l);?CI5-+8E7#af
z<&Ws<BgLBzweF%%Ju$S)=f*~${tM+vHQL$H!=Y$Q!%C)W|Eu=&E>F!Xh;2NJ23dk{
zCr@5qS0(|TU%HcaXjFz%(V%d>S`|@&`o9M1gjHPk!QM9-2r8LStoKmwr{k^zqt56V
z;~Ti9Z-O^;<)C>m)hnsryAKOWpAk=^@s0$h5UsZx1l|JAFf<cMEpEmE;*#1fCB&Dt
zxuUG+tTUyR`oz33a*@!Pq|W-s*RQjd$skZ<U+JVI$ouymw^2gS!G=vblx=yGc=-n&
zQ#*xt!?m(~>UpjRagAanxu)(zcz|MWINCNSua2UQ8t1-mT6eJ3_7@ru37zC4ry5q&
zo^<JK%LgenL`%yd?jVLbZsDMnT-ObimEX(vqYd$ewi#-y`F#q9|3mtesR}liruu^?
z&&MOB*c2xYj@hFY)tE$t;rYs?5btyI??qSUV20xz92HP^pFZB3K6h!K%j4xMQ+^q7
z>M6wDzHvvBn68xhXrJ<+cG|Wh_Q}(TO0-X;Tn|a#vr%-lyJ0*cEK{kHSz_X;##P=q
z8$UCur2eR+UZ#Fm8i#(43gy(N8!mo*vI{8%kaMf!-`_o}<5~R=ahP5uvwhf2dwgyB
zc}&*QK}t+c`%dBOeOUCx0FkF_I&XV(tdL3|py1beL4cM%`TZ#7>esM_eD2nexXox#
zCX~e8xK|-N><%EuHx)Pz5un)3f?57K4PH0U){!!eJSz2ru~Loa0`!cS$RG;YRJv%x
zHaw3r(5q%=&stVpC+8iKY2xi*bn^|L{e(~rF_R3y(wFwZ|1}lw8{-H{c{e@5T{k7M
ziM}1}8adyCY}ej9Rd`xzIZRI>{Nnb<u26LQ6K!PaR<dzpww`@&uK&eI!I<5A2QZ`S
z^S3vOK-S;p8UehdzS8Jk2&*4Gk^|ZD+i|%3W1BKIbE$_$>4~T3RC@o|iC49MD|xxk
zB_k}DG7;ej73y?lgioLPn!UilO40y;^RLhHI=&LIA#`QoFuv8vdGKE$*rT59<k~-K
zt)3noc^uP)ZXk-SHupD7{~Uxzy#nQ-CZaw2A}X+Qpq&t;dNy<^C)zwlWTr+oQ0LS4
z%Bif>zy^^Xu$G(4_E!F3{N=ry;*qkh=KIWEA6X2npBi~wpF_<T>Qpd3QqMyJo&z2M
z8eQ1$p@Kr^<uzU^TX{MLt*h<1$oAR3$2`v~3?vKGqy}oXFK6R54>`0+l!`$%mrDrN
z6tPcYcCh+7Y!#iyShva@b2JHspY%uh1COkN5r;5AM_&u^4UZ~+u?~UPL@?-i@5?Ui
z@ry+~NUrxF|Iw(JvZrAh;u=D}lG=YHJn$mbA1f&<us*hn3r3YU|K5;CB%A0a9*{G^
zzWo0EN<c<&@Nak#XOQ(|H*=Wz5xNf&8sg3ty4egE|N4?qD=<*ssq4KF&G-Xx-l-H~
zyB+(_=5HGLtI;Gk^$2{&8BdrO{|MnVsp>)I=RNxx%3vc*B>slJNz5)~vpEHGkg{@x
zW>5i{Yc=k5_+9;NiBq5p$92J!X%Rhtp%Is)kR|HwlU*N?Y18qC2BI`IV)yf|JAa|w
z<3Rr&qnDcw)TAO-w}94Qt2cdmDd#zJF8Z;(Da4WL{$d-;Ap2{GumjJzY{NLmCAU5F
z@Q)dYikf8?Y1F>_3vD|ARm6Xr>Gha>#ma#E6Kt96to4OYR-+G<U`2hsR}E9Bi`Jm+
zftu&KmVpC<mxeSkN9c7lLz%Mpu0{Xq*+HZ3|3ru_Z;H=26j)9(p^ZaiB^|+0xjkU1
z>CL&0RhNwi>6e;cmHp-vmKvyLK5E*$ICHxre#Ct)QmA@#N;GVhkC>GS;v4D_yFHj=
zAbkxiudpxr<BEP@zJU&euG^EJdR6L|Vb6m0x|@DF^?g9bg)<0|Y3~NP*X29SG#^Ys
z`<Mp(o|2u6=`>mY7^DcHrw*J;D+MoB`?aX|<cd&n8g$+f4#b*G=8yTFG_a^w4V6XT
zKgm0UR9?on9%}c~_pZ<OPC|YgVagyz^&Wj3N({j=-u0YIoIjm*lh+bA8EIM?tD9bz
zbbWODP3tQd995x=U-yB{kGrz?Th%!6hL}A&WxpiRE+Q(ETF|ntEKQA`GXaqgDw#)4
z+s)!SuG`n@oB#RtrC+fOoksCO^V`SPhWO0V?-}#lJ-@vqJ8e_qt?G*HFr_^~hVtfD
zuidi|jIWnw4;M<p1MG?dvPSRj3yU55$H;H;oF`Yfr+Ri@OVSSv%nHv1te*ly{lX&b
zSP=jv$B%q?9HD|K#sx2+=STKchEv*RG-(PnMjH-F{~G!lNrN`n;<XOhwgzR=4_ljw
z#ltC9tHqz{@tD5eT*}U7jsoqlJlYfjni1`ZeAw3Apn~!CAT&!EOgH(KE^`{340m5)
z`Y0D{_K&w-n2XL!M*zd6DtYKG^Zq0Rkj&^ja>){Vwu?`InLdB8VUwbWXCP|uv`nMU
z3z0=bl2+iie`)`_PG;5XF*}O@Thyo_Up~Z#7>~Gw(hdc&Oxn9Vs&r;EQ;2)|MJj(Q
zx&4X8B)}fiS*wy<^Rn)Lcmx*sE7r}7?~bu5NU@MhA%bwHG`Mu?Ezk~w-QS>5;Gs{{
z>z(wahmN^$^|oLein4!H5JdTM@cpClQ8?0at%2(3yTU);X9*ICQV@yb&uMSRQ4v5?
z0!gRQQ?v~{M)0UgJ>s@2pH96Iox!|Wnk%qM9{KPzViz_RsjmlF1Kh@Zx9*q^Z;@t|
z1n3fN;<cG8)G=vn8wH+o+eE5E?o$x#9rYk1J!=m1z10>YDWQVUO<*|wwRJ=q%gEYF
z-cHZdX^X8rhvb|Jw$i=eJ^tMH$FUPjn);IOR#qQ-A2%B#$rLa?RFr%9&Pw>vDR5Xk
z*a0U#odq*9i8AUqByrErGqZ>9FCX_eJw)~0T~gD7JXPO#IE+cfbUD-`3N;_1edq~c
zOv7q>DtKY~UDc22kMMr5pif#=;j8v{A}kykCj_qB7`11WZcX|JqlO}Qjs3?Hat@Wa
zp|!I>(|-FeXPkuzCMXR!wem`Lt9O3`_Vk%01;mba=e)@*1`2=>iJ<XY>#ZK}m!Sz0
z;FkU=bIPwVUj;oPr}c=s%nvz7`+IxIV1vg9qKP9{YR%RLydsO5a>IFhnZzv>MeqBH
zwxb<4?cN`~uCJ+8v{V_I+38Q6zbv(UF8mdK^VWG|)<WBg_LUB@#yZpB{~a?s1uy7K
zFf#-%9}9e&2tViAffv+RLL*Q(GT3bH+1r_Ay+jC`p>3YCfAD{W3x=cfel_zjhTaVV
z-=arv((xnvTv1HUiaCBC{<y|J^{e9<gd{mf4&G~2CnoH;X!Wb3EeWC4lQhBdpFdkS
z?)*vd{q>$Vz<7ImYNv0}4)n<L1e@5AW7oDSIo}#RBU{Dd)Ew7+?-RmRY%__*Up)_*
z?<AFP<AYVA)}lu&z{&PkT1VX}GB#*fAl=4-re`OaL)-4ZN&Vfm6Z!~wXC-KD|9<fR
z`TX~zaDkXrotWUi<iWr4^k0o>33DEgD&su)QnS}X<ynGKr(fM%!JEmwsd4S}Vf{;@
zw%-kG&3Z(&RvV4vUJ4d&5q@dp#4o<mx|`4D3`bivDdwK@U6G1LApw4}Js1<yribE3
z{|h7?SMMwvd{Hf3E|^`hTV9LZ^%(z+O-5qVPc>J#C2yuwEcW`nq01OV%C@7kRz94%
zCFsR3+K|wHBW*m@(sDF=7M`(oBq1JlqT4kO>fk9ulfMksRF6b{0u?<Y&O9g=zss1>
zdL=N`Q1Y>kjmi}AY$PGNYCQKHIk*vRsB#H)D9Yb`%gD|(=#fJSGiY@~PG+`#!wy6X
z-aeu4vv)@ll$KA^H2=MKYa@y5?_S-7FN`*zxAe;S+QB^QBcpH6Vs_dvo5(2kT_=k9
zyrb1(o9gJ!kwAU%vIhGc)?T|Q=(jkt$y=4QQxhABY%{#nyOG2ZahZChZaH@~+9u}g
zHahJ{fCZbVzcWUEE;QP^CO6q~iLE?-YbAp)56X$LNH5sGvsF;AO#Gl<?=@fM*~kOi
zlSQL=ZkoB!@{-1}N#y$8MI@wM>XXy(FYrEIerzUU6Ari%uKQ#2A2eacmh@hY?k+0Y
zb$uOLJpnDgl#|VgeD{qjuaAiiv<veNW^=4l6YhPX_WWuN%!C%5Yv;c>+}yVseW~|>
zFSzFa#qh}+MLec{B(e8HmEPwdCKd?Zej#OfmqtrN?>79}Q?Nz)sZGOS|5#wzJmf*X
zN&4PV%b1VYfm{r7u0S5@=PThAlr0lGES}=4cRlE<<syU!8DQ(NYkD#p4oLYI)WX2{
zc>~)EqR}3aD&Q6{tLL}W*Kb<X*ZFH%&;rq_Gr~+Ga1bvm;}u^Z@cE%Uw8tOM_fjPJ
z{_o}jed@}(7&JRVg88ee7B4+0j~~GrrmD=kUim=*@dz|0jO$;+{rIKZdS`3pFVK)+
z@8W>~Zk*Y4O!`G_+{|=W3Ny+R@3=r;Jbt^~_k^H-^5igg8G#}e>}3BtkZ6s54Y~pr
zE8MlE6oer;b%aPk{d3yL=kR+E#@LMp?L?;~4O-q`_BR@nboU^`+<%hKk2>LX`odX3
z>ZrYSlLrq21`Vd`70Z0DmN^xD$^;)s@q_yGl_qNVkrso<IUkgm)mr`srb{-=_UV^b
z9}8?>rXVWN0QeZb(;DfsE{x5WN#O}g-QLb&T9C5;q1Q<0;v|ltE*MGPh8dr`Ok8(*
z*Bigu@1ba^0&p3cPM<hlxoAPs7>v^3n4zFd_FcqVwXMYiV<1o6Cv<yrXzJ$KUlh@d
z$#vk9su?|db**&)f3R+WqWE+!n`jvQ^yL1m!1FeU%XzLssB$w|G7x(QPh<$zmCfzF
z{b<jEF+P%Xuysufi5ccqw;(!`HyfxKNrDny@&xo+t*dZ;*H%g<Ib@GH8v4evT-RiV
z(ACt}pt&)jH|4zH5-ndf%)M*~I<luyI?bM#V|Ifu(Mz7Hp;n6+Gqfb8JKA-x|3=2f
zKB4P=ru8pSrb~?GbN3W-_;-;&8%g|X!LkY4#m4cI&4X85I!DZbF5Fxfm%APxurPQQ
znvs1;z3zuI74hn~DU&CwRU@Yb6iml6fuQbk|CK9vq8VtZk~mRBmDe45L3r!PnBM*X
zmcusQbCI=Ci8b0^&)57wty|G)2D-{fW^O<v%G@oe|LkQvCIr9gD*N;P9nvnb9YcKP
ztTsx&Ys(~6*;2v+eaeEyQ75{>-WyLt^5OXb?jlN4p!b&Fn}l`#y4hspBBQn``7dNA
z(9Kng3z^X7_=jHHY*C2ZHU0qm!3t|h>wG43k6(2vzP-A=bv;511~i1MZH@^KUKS{i
z@N%6p$$uBF#gX+qhxnJB)W>uBxM1$78`^l2WJ-t{R<y!!M#E+N8lW#TgKop=U44yz
zCD8olT}gj+N$0oxBwMS*vV^s0W!u3~+vBz8RZ+6-14Kh;bXt1NGCrrJSQqb<({nBv
zlyUmNH{UH7O2Q$6VE3tW(wT4t{4D`pES*hKYrlV-jfQv(ZFfsb%5PG?zB`3w7~)p0
zAwzM_F-8@)(X1(;mC_wocz=1i07HjIBza@7*C97=@f|ch0<5FOhhEB=`5sop2-XAZ
z@M{;y7_HkehG5ZP0Ow|A&5sDcUxMPcZPs1N+4-$03(uH$P*^bD=))ww-$cBnaLRb9
zJLcJ)V5x3d02rL6Z}ea7rGF63-~@|RUY=|6vis5)a1fLd=BuYU)T)~_3<ylUHcCMr
zKYxUJNQhN8HNKA}bYYohD70aT6##3${y0+P5fX-WgbjgLy(;G&kH2<|VEKp8m?zC~
zI=N*zGLFUrzWTi`l;Q5e)(08imr*IczTwJijv@vf3OE=UmvvWFd#^%ECFgjMTUF?4
zz3x(}gHVpm550<{Ortn7LMWUPbctL4mtBNaA#^I_L|0o5tM#uN6CGj!gYX#DP4^lZ
zmB)lG0@2x9^Lb=~A|`_zXvgHZg+nyIS%^nG;VtWkLyObYt?nQYhTvY9;vVP6ePM^N
zgt;h_Pm<=}C?%TzK%hqa)5avq0~c#d4Ss$LFAK<3o|ifA<i<t#(Gd^w_;%;cxPC3P
zDi;9U{I-WTojCT`sCFzNijo!5o*KB~`9>cSE>^nvd{ujhCFV;n=!#i3&aK}}?r{nt
zJ{(>)2S3xw|GWt~3#A|--(FNj2AoD*NnQ>l*F<Z$b%i%?O-5v<o)L)i?kLr!4Aah{
zAA|cUOoE4BoK!Hu^RNPrK9)ArXIX%Wpbb28n{)nHf_8c;+Bb+j?5%&fzb11d0!?io
z75j{;85xj&25-mA5^C>=qpFLk(QPDIR^a71E~Fl||A_&I3L2j7Q#aElV;=gFfVTRD
zmZ{$uo9fqNB7h5YUSoU2_fIN_q{?8Tm>>2o`F<CJ2qcVTrSw=05npoS(65n8N~k`Z
z61q+00W1kjXd+v8pI)Y(h7pYl`klAP{UXzA#C1_0<Dg#PLm_9WxHGxJ<H4}!O7v@z
zSWI*L3&RJG<Ped)l}TH%bAzeq@^QT3<ah>A-`%WOd0+|pk~A%$rnCRoZG2@2(TWa{
zGJd>C$<(@MiRjm+K%nd&SBsDX6wC_ez~k_(Xi&~fnT&sIUI`)o^fd1><!$3;v>^ve
zm{n1cPD5F#zbV>J|L}?cPv_E`?ur+VYUnjhFtd95lD^t7e<IEzA*#t|H)MVLupjT`
z!bq~M;}dhI+fG$X1m!l$EQiycHnDC@?5sfNM!Gd;c$62h8j0xyxE5hMJ66AC7UENT
zuyk!dhR`)Fc-H8xSv(P2X1FK1VVMS+T6H9u6j3DCPJe*y2j=-cUSO;5($*f)cr2y1
z%H%<MW2nZ4(R*WtA`?ywvX#^?J?{M*?PM2#R{pwxT}_YH;SXyK#NYDNtmHgQ6!4<P
z!#qN@)tcp-{>0DbgMVnq&#n(!i}!;HOBgDmRmN<*jmCs527zrf-!niCeJJ5(r99XF
zgg$K(vFi8`No*I7Or<v-#3ZFdi}kB)Qqg`THcm-bh}q8wTFw+k;4vK`jJ%_L_xh$#
z3#?8&;RK1}UTPXIgT)GBoUE@I(H6eMsYuKrK7hMvKJ(n#U$o)n`+`#JyQ=Vdvl$iz
zUszUPD%)4M8&kmvuBhn_ep>ITO{B_GkCQ)B-xEd*qF*j?EA!)Iv{VAx2XBcq|3*e*
zOM%pjB({za+mo-=ok7sUjD?&XkColFZW#Ybb7-d8d0I>NyZSZ$(P^G$c4C3VH*BqY
zpJ-cw9V>~IXcD|)S;`bdQ5j{j&3lh)z}Cf4=;w(Gf#j#?4dm=6KTc>NyQWf^tcrv*
zw{BG&D<HtAt#zrGK)WA-J|!1JkN0Z60k@*W8|gmD{V27Mh043`8ljCO{%)i192j_X
ziv1qbg}F@ZyvCd(Qce1RU0DLck2DQq`sbrkjj#-S!^;I<6)OMI4B1D^nDWc0Ed~EJ
zwYm6NU!EQoBI@gIden$ga7;L2E)s4{(BE}X+apaHXn|lOCv9*4%EXv$hzjJ)HcIcx
zfzSV%3}BL4fl@ft`t4C`H-5x|Q@q1ji(kLgls?iIPx_Gb?ww#x&t%N1Tfr`AxTT=c
zzYOg;+{@s)qdToLxaXU6wh)wjQ2Tf^nucMV)MgO7bf4#(wEhcy=>WE`YJk~^wp=&N
zfGp^w9rUj8+5E~3X)x*)pZeU)yq32|${`y{sCW6=M)GE+c)nQ>?;3hNExI;j<8K>$
zgi6jFB~r<5QYg6&4&2&&wH^Cj4Qv2-&AxPw1gDm7w&<(V(SKk@yE1#3K-(K#lS^vS
zkQ*im9R>{gX=OBhIEy%d`JP`pJD@MFS8R!3T9PBtnML0g&Bd-Nq~H9P)89+`5rxz#
zWb}oGCY{r-)bht14DRMrhSKC!H4_oQF~JPszA8Lwq@i>dsncx`r+s1frPg~q+ByG7
zqTO-fhQoG0&!Lxez)|_p+G(9JZ%YuBX=!!+mm3@#qMN(nsy5<#?g=h+_DQ}mS0f!O
z@BwA=E5BqzX{0W)AxWnaqV%fvhJlWr^~jr%9LXoDMx0A6#-IX0I9A<)j6Y>OmF}Ij
zqDLKQk%ZuQi)KZ?ckbAL78DK<hI!PPtRQ);NlBwN+T#NW@@ozSW8%TJ0A6Be&B|kx
zh1gjnK{eI)=ypO|y)D9z8@Fxw71<ym01~opOE=f%D-#;j`pFFnN@H)mKYDZE=&@zS
z%eQvyIG?;<c3EVH>DKdCik42jw_krr$Cm>Ysh^g*E-Rm#^527n7dNF{*s?)P7l;UP
z9=Gd7Mpwv3!HecvQSfko0RN@=favZRf|HOp)M2zleW^bkZz5e^+_uY8C-H_UhB~&(
zJ93zr>$yWYBT-MHTaR7&yqixg;)jRqdT?AAm#>c*e9BUBX}|aT0HO(pQe2e@=DbPH
zo0+jlK{BD)_?|2~V>wog^UmMV;wj6-I;vbfrJ<A9U?BFF5RR$e`~BBMVQNM!72C~s
zv!B>WFifxWGtC%esxSfs*?{@re-!mIh+DiPnVu(i7e5)X!xW&q3T=I^J-k*R&bowW
z8?j8hfpzQQ%oE3TG2D5T5RE+B#i_xwE+C|&K`U)5VVWOYuXt!tUueIaFjZ)IQjmHe
z<a%VG3-Va?;Oux~*TaC?M;LXv@Az^PAMVn5P(1Vy4{B*3HWumH4}8y^I_AWJKcMv?
zLqoj@yV4M7bNHOHu{mF=f8lC@x<31;FLsr9H_9M;*QCjW;HF_@{-#V?7@e=4nm3qb
zgi&oW$WzP@y@TYnUD7bvh7$7Rxy5>a&6Kr3Qjh`BkX25!tpmDPNzmY)Cie)wU-nAD
zyjk3_prSow%`oSiE6-hn$F<89DHw<~>HA4O5`U%&Pn`9ibNYYEc{HpgBvGlOs>l2v
zsa*aoR5WPwZy~6;+}wqA8W(bb)0|)W8`El1<e7fMm&j`bfP|?jM>Vib2ip)zv&lDC
zvwpNwbuS@8k@I-sHqn3Ecb{{?Gc#`#SPewRW~pw+(oS3qBs-Vg^;o@WJpQ#k(2iZS
zwxIE4!Vl!CFjs(dIHxPktfUkR#zG-48^xO~AJQ|Qn_`AhFCkl!II8^P<TG=;<*~Uu
z`YkJ!e-bZCcekvYp0_N+ukpwkc{cY4lUzs!L=yYvQkhjUfzHS)BZ48*{CPLW;iLq%
ze<{pqv&sLRAFo<6I*P%GS;(jS*qvz|l8~W_y-yyPK_4Tc3I)0-;Le=OAX+<?S0sJW
zKnjyN0~FQE4$6}+<26It8x>#{aPdZYQn)y`OAALHAgPVhTBEhbLPZ*Qf_PWcnvBEk
z%}3XwA0zD`;XQ`;kQFM2nKA(cBj{vhyL<uCA97!ZqiLmoFjZpX@mxJYr(B*8pS;3y
zA`VZmZ#7WY4I2$ib;oK|azBTHs!_E1kc9#Ecy++b`R*|w4C}^3&R8ZM$_cKT7v%I8
zq6^stX56eAeZh%Yn8>>ze*L)0?TQlh$Y!(#Nmd|vD?o==Ivu}P6G;@yr_<zgcTYz^
zzao+d>bA&m-ATo;zy_N;-8ZNI=n5Q*pkC;Uo=8!-Sv{9I3uz@b1n<L+rHjr#nu5cm
zY)DLq&}D2-Y?eXuAh(r}qxBW;ruNsy6nti{90WjG*1EUpKW)HLWd>%UY1Im{@D!e|
zEG(<L>Gqp0B0CxBV~Y~PuJ47Xso2Eq0%D}O;Q0i%MZG<F1#ev9Y;xPUmhO|Z2iRg|
zgUC0Py;N>&*<ynC+zE0s`+p|H&6t9yD!J{0cla;CR=KqoDXZ>mdh+W=Mu=fMsD3nG
z+_`BfmIV(W#GqRsncjw=l3T&Ipv`WcJ9N+tFJgxW+1S_f-Crb-#ZVczRO|%zv(4u5
zCST)s@&rCmQjpmzxdG<blywgk^}^9=A)fTtv#x8qyMm-{$5OE60zY?peO-dbXpSUS
zzj;$(?P+9zqLE<5rx<>}Y9bDC#f$ZX%-NB2-PfBFu=*&15NO}S$8nCMSVrKk5LRjv
zd$QT7?8SJkA#lbw5X5-2c(W%Cm2$z&{j)T0giT1DE->!%_xM=p3_nxF-$Ewe(cas6
zuIUAsJfYyjg;*Zh`*qVy1iliU_<F`HKd(s^6Z<b%gx$}$+1V*F_}75gn{&Sm^xe|H
zzXq3sWF-e@+>gG57a<47o;NIZ_q<jpxR0U~77%rA2;TqNUhCeQ=#jzfrN2%uTY$;j
zc5+P^l=H}iCDp-nORv^`_9~|t)64=)?bq4n4t6ql5DT~zX?$QoH~X3N<Z}}wa4b|#
z8m&oYVAn1hg5b|%T9T|M;VIvPDKE|O9Ig6uLG#$*Z8U5^M~Nukc9@6b4U!x>2Va3^
z&t)t-lrlb@oOhRO8_34k#D)x+0q5)EtQpKsD<pUXgq4!Wpw&GFmiR@8D{+$j$Gf}_
z!1!nNHH5rv*R{s?2VG+m{uV+t0aTTlil+Fa5-v6IfAi(FLufokYst+6w^q%7>Khqs
z_0!9{=~Z1bV%ZdImqKcs+U^zqobWb8VFMT7x(%E=o!Yu`caV;wUWa=fX;Y>Rh!2Z*
zV3w_%P4>FBp4T)#hK&y@s9L-Djpn)oyruX(xGKVbDE96B(Yzh48U4yiP*mj7LIoY{
zR4b>~{7@G`1w@hz<_QyTHXEInPPv%xTH@@_smLqC8`YM}CHLm+TmMDigrB*B_}K5*
zS(7pklhGRjfnxgo3*!wCd=SyF42n;5evmPaG*Zv>@q%`G?);d+yo^t;_m)r}mw&MC
zWVvJgO)urM(-<e8F|4RqwBdd}|18JBOf9%GXUw{#Lp1gbE9yb*>HW(Q7%M?m!tj@y
zcHC#ulT<pK-zO`SD$+#<r(*Fr7fJM{2wYsu%kiO#gp7ULxXpD`X`lqO>Ej_v?1`S5
zJmf*qfU?xGXyTMx^-2`U)&q+vZc5vH)3tb)V!)sC91bhH9)aVJ#49Kq6rWnVBc=`S
zoFy0%XXLa}eB;6p2QVW8$+)MQB?5Kn@@Mw&GGb+KU7JamoJTY)gF7)}oW1(n4BfP>
z`@~}@2a`*f{M?9`d&4|rjuV<~lt8zNYx%4Q98S6vNRCv_U(p_`V2e^8nDGJu<8xeQ
z+=f)-q(g9u*y%gCldYtR4#>&5ZIrslHP4Qd?zWf^@A)*16YlXu>}wP;CEoC5wrx&I
zT9L1hrt79mM#`-!@h_&uZ%119bR^R^;Eb}Dn|v#p2EBx@o!CTeb3VNImzaz&A<73z
zv0sIy);mHmK<_c(zF?v;(O=BzMuU1ne77?@YmcHjiULdW?W(%f>Gqus4j93V48*?8
zy|>g`nah)~HK7Q26>nsc<*kibE*(t9%7Dw<-m{om4N&{SKeGM(qfk61xRK`*xwG~f
zD56EF1Cky*vbMfL$itY+g(SM#>W+X!r&#G31_&^*H%XPq!m-x8=c2Z_mE2}#GUg*+
zfU0wa12Q{T8ARjRmVzqr_Y2wHwh}>!0caApy$8xFw&5>%5Zc(<G{IlwtBLJ0HW;ZR
zMv7ruFJozAm-55icZ)*~pPYu54snv7i}ODEa<20*dUIi=uC7Wcw;cXOoGQtg8!@;#
zxp^v1E1-f<&ikO4EDHJOBA)JCph7<5ZJGC<_|bldku^SFDgGu+ps;w(B)WEbzbm+%
z5P?UfKsfMV&((1vdyLc+u!GCby)%w^A(bqMeN=awUx+8n@sSI}p|ZH|Wc<6N2NCpr
z!U4TQOQspu-~)1M1W3Zm*H6ncdi1cmk)*v@dFTv?f)((D7s92qdlRqP9Qy-5g0#2g
zlbtkn45ZCOKmgd7YMVLhsE(ozKB*@_c@<sG=>4G_i8t;>pIl<q$wNVdm9sEkvb+SY
z??SKhI=b;G6ctPhb;n)Pc2jXW3TazHH4Oav<JLWM%$7<Jb_p&v{1jXB2#35kfJc@4
zT#K73wGWIViE0~l^QjB>Vt^y%+vvMFaaHS@9Vo~zBH`5d*@vw|?#m`1JxF?kc&Li3
zw?uEmgT^Y8jW_qWjX&=$xPZ4r3GBm@?(X%Gukm~Kv&n>`1vUQZ5ApIz$rXGzwnnf6
zkE&xt-V4VrW^uzywZ|brN(0QFooC*;aU9}uX6vxkW^$T_)o@1YSkT50PKbri=DsML
zjk(vfN5GDktGikIQJN72NhSe1n`X-QcW=bXk_`ef$Vrf$<|Oqn;b^&|qCrig?IH>`
zNMd2x%~uQfgNG572?!_6kIwn)IQ=}PXlP&bJLSC7m7Q87ZPe&@>KF;Q)l*Z~ae6)>
zH6at=q;;?<CsJk|GMC|YPuJ>Abv?FES0S*Zf|cbTtG@Xq?YNYW;P$Ofzr*1c4eMtH
zjUJylg*1sIzJ7e3_$jyn3kM1E_dKOD>7E=Ht5_$veNU)q=F@}Gf&f*QG?dq0TfJe-
zP^LmSsjJe^C!CU-8G*^kiX`6q+WWY2bg`RDp6k;a^?O}9PVR+tQ1BJ{JrA}kn(~$*
zP0aNYlybc`o`{M=P|0=`wl!@167%ViG9JLQkxL7&Wp^nAcuKQrWFm<-ZxRAyEZo<R
z&5i^|S24Yjvy4IcofJWGAzV_UV-VnEjLA-&O%B?!x_OOROK}*4qyrfDd=Wp7=*E`G
z8$M+;edLUgLjQ^PmW1leEkd6;`Cm2g?=eAx2M)X*sJ@3jm6Xc5|E+duP|z|5t&;**
znC2I@G|HyQVUK_SIGUs+D<d}vOEMW7NIEPRY$_Q%fF%R2A<i1^oI#&eiZR>lYMjX1
zkNc@Ta<k;MU$^P9mWqvOmrXl}AFr(u2P*9ojwc32{uvs*;erek;zpm9UJ~yjhdLhr
z-V}ZD-0DD$=*9MvkSsI*H{dB?4qL6hs4$Yzu7<aOcC#;|)~sv2UpNoDMG%i$I6w7Z
zVzi$+{(M0jZTX!~@7_*|ld4p@i_oP;?5A&ZbU7yN065u^uT#%5YOr$9E8&;Y?gy=E
zYtOdDuoqz<mgDYS)$MJjjK}z^jnenC@#iCfCXx}ocYMyyDW+{S;m#EpGHf8e@`~<i
z<F*a&Gh`!F=5?>^s;j5qUt8Wrk#7mq?dhghU`410aoD5mq%sN<JNL{baPhpJ86~M|
z>_m`5J~yfSn#6q^WucS3{#=R1Uzt@Ze_1>3-1&3Omgx`wT664y&Z3FgmWG$=KReFM
zZdRF<zUR`4>ih6+%CP3d>?)n*)ttb`{z-A1CSwEFo`Jiy<%S%xr!8P4jwTHGe@tC@
zJXGud|8~3OmZVZii@B{T)fAPi=eD|~nk1BMif|()6v}dLt6NAVNtS7oijpK`nOi~&
zGq;d66JnBOFqy&3ob&t4<UD>a{~dGAvwimE{Y;k@#L3Sa=gsCh2DQx?rYA9=N(wd=
zYFJ9A&rW&u(<Q25UJ$X<3Uu^B51mGjZiv`|NX9vnGQ;&}f0kHfa3%<_M#;|Z6;~pD
z3a?_~X#!bkkH1SFS~>NnfmlrVCXh$JN&Gx2bW`s4{H((g#|mWbN6uG_GPRLyr@qDy
z6R{7O@6_2Gu(9c9V6Zd&-$CAm9DOH0)tl6twg<&w-p4~1d3S}P5w<0ecf0@OH5zN9
zZ^eq(7MAWUQGCX5n!T8Kmq41N{;=qNq~`treXU7WDs7;Dp!W|u@Xolk8+p&;GtYG(
z@DBAWsSlyCXXX66?H~O14Ezk-NmExz1^uR%GV-L`(LBB<kP+-av%^Qc$5Ojn{Y56Y
z^FniXO1BAvnjB|QCdRLe)cnOJ;ty)2cw{Z+Cj6UOjqe){+J?)suFGS2*UEkvCH3k~
zqjs|nR<Z6{TyTcUu;`X{93iX%Kd}mV<A-hL49*GxR&|3Jf%pH%LKE)rAu5JG6gRwB
zwOFPsAP$%J$H|w!FlSzv_#lcpW+88o_vT@&{@Bm?o6vkm2{6AE4in>sps4tI?`XQB
zsJN5$W{~l*S$ALfwqYYbgELux1#NhIj%DU^mYUq?b)=zGQdKE*N4sgzCKG)6>N!f&
z9v!N|0dW+s;>*xbLTNRk5rbA)O7N%to3AKwSVMy$qt_T*kw3ge9gTsDm^Pv7SVi`+
zHnpYHy3+NfDie*4>EGf_8CK+C19vekJTXW&z`-0H*YpH(87pR5!D99Cq_U3L8MkmF
zpU+`D)idbYa$Xp>x|Np2nI^zWpMSnsTQLdk#~J}<pu&H<jS~`&-kg+)mEMtk?%_vw
z@1hQsRn*C}bFTgTY(vwqPU^%@i`Xv`Ge^rVpe1C(Izt+R+9L(-!!>Qr_(}mL?9A1i
zJ@aRVj7#!4gPdoaIRfm!7?UgJCI#j{CnLh82BeTEC(p`RzJ$I9CyYfthq3I6o$(+p
z-h88N>-8TEr{OegGHPhA26+#q$9*-0!=9QmuENFry#kl`IC@bV6#(Mk;3g*?E4^^j
zPrE2wp16+86?qiaM8q5)cEK_@`T}h5V%z%@Q-{M1#l(s-;zn{^t2*6oILH|;cQhn>
zdF5=Imj6-okOx$F$o=FSxAI{fS3`R@$h$O|o+1g}^*8#JzBHYY?yxejvLqIbs3O2p
z^~Nva%^I$XFD8=82o?LFseO*NQ>e4SB1G(>v*UF-F`Iv=vmxyhNC&gMHRRWL>KKH@
zgFJThg$0d7>Po~~jS1w{G+}!j%XTs%rKSWZ<oFTsXhtN(A^fmfPG0%HxA&d2wxooC
z)R)k(9E#~l(!8*nI(%Vi0qNI#-u7doV+8ds?d2e^xwd#-ispLsGhnz+)1BINIl5b@
zMd1GvV3>(`{LVP^Cb?FC#b!@sm^C3*#V(21VWA6yS^T9tsW-_vT--$8V!>BOB)GAE
zMC|VtPW|_5RL4M*O0hG!pNrQW{c&;PGIVr*a`B<oC2_U$tKz962s=XP`vknLd1Hgb
z#l~C#Htvpv>yDt^SJ2+B>=yE$@1f5r^tE4tcFi(@?6g>#_}p(L`rQ#O&h1~Japd-F
zY6SBmgs3OxX?yIoYqzKk!mfze^TOVb#e`?5N3`-mz<z4ooi7^BGG>sDFy^UW-bp|4
ztuCSg7IX0vhw1G94lF~D{_2*qKWq#(ndf=?O9kuB-cDN4AkTq4{zJuzLByoA_k+AT
z{b?o|LpK8{AuG8qH<M1^6Y;tEEHw@6qKM6`+w*RxJYyau+@l0C!TO!aJr)(aC`!<Q
zMSWbyf94*Eh(pJhu)1Z_`iC&6aM*IH!On}=_iUG@r)LiriG<5nftF-i7Uylmrh>A`
zfUdlY#l+h(;>kB}YpbE8M;p))hj8(>sXd~^Nz53u(y4A7vQ8d4j+V8VBCIG^u};?<
zX1U9IKWr&9rY)W>P4OlCWT9E#D(9dxtpL`}x^<$hc=1%;u=2+eMeNw2>VHCxWEmme
zSjWYK^F7@(ovcrzK__tW8?zs2IXyB)kG6C14Rr<AKE3!IJ%YA#^|6A&={1u7p=Zmu
z_`UA5m{m`;C}ME;BQk?Gb$u~Z_pqc^z=qR`jp?|%z$<MBwR%AMk+Asbk6W?>kW3Ny
z@bDGl`|(GfFF+#%cMmdl-a4VJez7X~6oV7#oPm#$$g^$Lt1RACpiNgbBp=MO;=dPc
zHXSx}JK;=q?iS*QqaFK^m4LtLjG`T_z>HVVMDv&|A@LIR!;@A$T}rJ1YnHiNwx2tc
zNnd@FA{#bXFl`!Yqob!I{YP&r#O3nNEn3DGkOIWZ26?RIznqE+sU8tqQ@FUVw#wax
z1SADG0PM5=mSv>8Z#A+Um?eWz%&bM@S~F)_Q9DPT5wUIJc9`5qTd(Lt&Zyxeh}dQp
zH!hCr0)noK^?C?l+6;V*0BbQCdqT~q>MGi}ZCreM>!Inhj14ZKXG^*G+Ai;F@~yf^
zcUCn>_~O;Urp~3&Zpg&YFeJBf<R|Jrcp>^?`x2s{-J*ZvPa}RHiw7CEdZueJa8D$+
za5VvD9iBB|Nk-Zjio0}HN!`~Z@%55-x}#yUfJ;<8TC2_IXmajgbOqLVv>OLe8ifO_
zy06||8l_6<Ir0P-|1@FTwxJ$x&!0kyVCmOzPKwy9;JQGsp=BeRE+`|o=GY+(sUr=I
z8rdL8XB|C0-?%~*oxg?v(;lqfVj}Myu{+`o$tBt8UUt3hi+lZ43lE4#O$e9UuOknc
ztXTK(d**YB{W&orws%&Y>$B|>?@@%pi4?J$Z0pXRQAgG<X&}I?h6>{PoHnDWbb-yH
z`=L=-l*vMxhCC%=f1TeI>m6ZE5dZ^htdyKda7_s!F(Fu@TPh}Qbr**r*+*WfWy>V;
ziB6JIM4rO0vXp_Iiy31&(5oq6Vb*6``Nw$9K|CRBmI=!J66e`H-arjXg4b{|+$g2L
z`{FD7#fbnLNUqh8U@TeH9XmfW^qD@ACZAyy@4K$_z8tZgS?kC_XZPw$_!_i+KJfpo
z7oT_gW8b!+eWwdBj+3x0z->OF05doID{d3EqQKyP3q!y6zq_T#a!VzCL`zj>9?93b
zg$R@brj~z`_EuMo7l>D|e+L<P8%^_nsY6>pgDnUUCHXiqf83-Tn~gwbA(i^Z9-3&}
zfCTb2Yhw7o??@_;L2h`nU^M@o;|cu;>Ua230T#ARa$%^sA{!aE<iDN#qbGdQBjz7K
zyb_ixq&29v&+#qzje3`iN+9z)CnxnE^k-2LiqRrV>8ox!;Iw?$SFOR%NQq;2#)j<n
zlut%1Me2a|bIct2^;tdA^LNUKRhFJZiwaDCqc)VBkxBNIImz=I>ERSD;t5jXWy<FH
zS2S0&54Ti=%XQ#@jMlsoh%Qn_6!x|=YTd_&xa_3~dQ==jcGX1_xtjiHH_5V2_23Mz
zKYm1iKtBVW9``0&|3VcKV_4B3<4)gbm!;_;$Ofs%mEit{rYlpAuR|NMkBgrx$92+d
zS0SEYt|Q|T0(O1BUL-`6jg=1aKAN8XW%^E}5P4r@?$Vl<TW9^Ut2sr-Om5zV@dg=o
z<!^4LXR8l~F^dURuod&B#>qsJM!pZzAU?SN!+K7|&87Ol={CWC1)uye7ZDfUA`@gi
zyOmq0ibNgVmyi9HWVS5hKqzHjGeJrG5xtnVLQ17Qok>?NzK9=wL2K<qbSmRBamlFz
zMVW4uh`l)Pz@Xe%Jh|z-?K#R(AlGp5i3xKq4^@pv)9rv2{6o`<v-yRx-wkqvG}(u>
zM)L2MFQe(Q^rQvjgJ;e8VLKab?s7LgL3k!0+Ct?v<zTt{5TX-_mW66K9*chG#l;(6
zto^>f9{DvK;MH%|cRD8Y3<vm}Nk=#Q#{}W3^Q#0~5VP^z#O{yHj7t38&O`GJDkCnX
zyIqVN3@}E|qzPn#huA4@v`Wcvl-QXRCXfvwJ&pMy4T|o`zeQ|Wd`YXx<8X>i!^()i
zKU$ZV7y7!)M&ic2jObV6MlsUk{-H>mye?vM$7!6Z7Y~R0oJm>&Il&9-^{we0M+s_$
z9?j9z*l5n%@G~&zOycXveFdH$Uk;X|QwANEscUqPJ7gY>_*(#WU`zS+*DM3X2N=-D
zyOd~Cp86(AV&dFEhIfs0Rg?w#75R3Mv4pq8wGWq>p#?Z_@tMJxt5fnsBrI@nq}t^*
z32xfx5j{tMZJf0|a)Q+88agU|0=d1;XiZ4vVrp|kqg*4%V3WV>dSo7occHm9Tz>qz
z+^7ECPKHygVdMqDaQFp3UrT=JdC-0<?*#Hz1+HTCd_9BiFK=(7fd{Y4ONqm4?r;+;
z*C8KZFi!qqGbiryb^3IQsyYA5#4<~+$0fzgMBn3pbU1#eL}gWZciCO)beNZFImXS+
zDy~MG5g}?ii>ni!9sZO*w-xQYs=hRKMYoBu`%wEnMA+dgrK3O1Nt%=EVMgr&M<)}P
z6A%18TAf0cdN|O({x5A~*}+C()bB`Lh_1XaHW(E9Q@cetCy;@U;|1}o#gv^_u#SB2
zA%J*!C02lJRXUtxe6y+yA~q=CG^LD~KKYR96k~rRgYcMIj#u-Kvq>BK(1{1>OO1I!
z&sX{nnNdPQ5tGaOI*=CALfJHmh@8C0EFrt!$pn$b$1-A?VCjl?g9qecX!-9~N=2qd
zd5ime!IMztXq8d9uWIte^5f&sBZ$iF2{xRY^OZpxSp>+#FWKXk^t<1D&2chaw;r0`
zoH^<O%yiK*ao3E0nqiF?X(`nO;`UCAQWr~oSE0#VNFd7!#2Mu4CPWu8pfV0uU3vX0
zlNvUZ**(bHDNIhX=PgH0A2UwG)-aDPA)X+u#2GKZ_UXsEI(md5ngF9IKONT-Hl1`u
zi<QGs^z=k0%!@|nnhL?XoI}U=^q-)#O*))S*izk(r@fbrL^vT1^G2k5O1cmcSoJ|M
z@5Z$!dafH7)H!fUApR&$-5}6rZbd{qU4R)g(~NTbr&DBud%2MhTJmh9LMg4AyE;dZ
z8Psy^_dSo+PudKT+1FD%H(avwP<8g|v_Jglw%4*M59`x^S-W^v?Y5sZZD!EJ^?N2w
zv#46J@He&O9c#3cc_EKUlXPC3A!$fIQSRX9mOuCd0=UNVkdc6y$`PFOf7IA#EeIvA
zP;|*`YsAM7gjma(N?Oqlb@&o3S<R{KJT(+@NH6VVJg)cLymJSOTBwRIQIRq5?)oBC
zTckqMAu{n~qps*h0Fqa%e>)`!tBzS$<^-cdRx>0AR|ixL)<Ok{vaz{_4-tnQ@wQ=)
zLg!GMrU*h*-eT%>rJkqGlMfO&bz1eNguR1nD3jlyNha_qxpB3=0I_&Hc+YFyy!M}*
zf<`RAF4ypwzBl`wpvMl0n{udYunGD!jirYM<$!-9*GXeH7R*H_1%b6_Pp=-W=dPyI
zpkYAG?Nw?S!igvbYyl3>iVHmZ-CEQF6k)&zi3VG|usJA>i(RdykrnE*cqu%zx2*C(
z-su2?v!5p-o@aq&lX0_r>!4^Y;xyL#PG0}}+S!)=^U&002(U+=7TnEum_-drPIJQx
zyRD!9>i=dt@+kS>-DJ3Dr_FrvEA=~$sQ{yuOYaTjIih_tK0;(ZEdO+d@v(5Y6ttMQ
z2w|C!{3`dBAheh1deZq7Itxp*J&=>hKVQrCPvt((*Og2`=6-;i*q|H_vES2ZNAWVT
z0z`<@b9rzaw9h3KKlHX$*!z57(~>6sPO)(|)q^F4pr3}*f+eY~G8Prh!@wb3C5@gp
zbQWoX0CzF{dgD>^?{{^!QVN(4Y`T!s^_%P<iZS2}F8=%xKQ2S>f0TVQIf0x_d!l<(
z7@3G(3>UHOo$@rep&8+5hgohom&xuF4~|4Ea2gkX_pDyS{@G&mGbR`RC8_&u>g7|2
zR-;60@z$W&v1<^C;9!SFd=_XCN9afdR)Znh9Y5Q2Q7DZfE^|F;bQ_()y|D(#HvE5?
ze0rfEV6yIeqyyAJ;P20n=bl$ByhE8X>%opQe1iA%iYY-xgD3&>1K+(N_&J-JEjg)M
zs<N3JFk8*viB9HL8PUFGZSsOWGe@F@5E#8QS0(tA3`xo`Kcb>qy8m#th06Wc4Dak_
zW)}Xd0MlS-Pnhv>7~LXVKHrdx!=$?^(=INSx!d(_R61AYK1Al+1yz2%w>Bb4a|c5G
z*W+g<uA4^DCB9mKxjSJA$I_4{CTDQ*?rPcGW;gKx#Jeuw_yjr)&h2N%P*r&~ig?>h
zAM<mukAH^1AsW`3K%P0e?R{W-<w$%MLjC&Esq1FetymO^wqzFgYDuB1eUALB=<!)5
zW`Ofsbgne`o~aRXCCMFJ+@`^Fdfs2E7bL@ZDaO}O(ogceWz;MWM3)Tkbhq<=B&$5u
zM(P6tl@+v*5$NP}b;RLX-YsR3WrmaG_w~_K>VZcN%U%4JKtH(>k46lZGUHA@&3%wd
zMJE*7<d~gQrZ#cIa8*??u>_W&9k(i<!QG0s$tq5sde6=6V1Djcw8M&;_`lvh_^*aQ
zEL{P+dARF@kx$4WL=XX>OzggGALu@wLUABI7F6t8_bZI-^=MSdI`a71_QU@cl)58J
zmcxTKg$GjiSk2mV6lunl5u#(C<X_uoq4+2XNuu;kN#PS8U!b^#oD1+%>s<kb_(kVz
ziXnL5|9sD2yWD<-toX^9*fFP?!3On_co+|2WCg_A!6s%GifJfJ_N!f*-0l;IHX&&0
zFf(M;kE0`@#o1h(Nb_GJDj5o=WC0c{mGiC$=x4E6e^YbAfTNArPCLabkbwb#V8-4&
z-BD^4bmXtF!6Fh~)qS5AJmNWw0~Tq_wKI8WNGV>dRp##W*|XQ`%46iM`~nfD&-rb0
z43PCp>T~hk*W62jTacp-sd2VdEB<yz-X&yTkbV&M+bPqNmHdK^ZyoUXS?oo3ulp*B
zjOfj>loLIX;})trA%n9xfqXX5vc;`y2_goI>+-4>5xnv0sz`}5-*raKX${bK{nE9J
ziYYKlI?156<oL@P=Z0(XGDuY}WT*3E%&KQma+V2>PG(Ejmt#7t;gVfv(wB?xY<+)`
z3>AAJLS?R!W~MXB#f3AF1m&En<x399jaTL7)*^{v3%b3Do7gaA9GY(`M9ZFh_jbtA
zq{xE<iDzy|X#Zb6tt*hEO*bU9tmnD=rspsbJwpglUtMsp@WVL79m@q6JNt#ES@uu*
zxJqVgbuu9`aLrt@`&0L@(w4EvMWu2kDP$6)I2{txuJOU6EXLpJVJE5ym8niX1aTWn
z2S2v(p>w`^>2zreuv7eXY+#oQbsVgNVsrjOtKW%t1u}~6I1uFzu2u~cJVHLP8y8<n
z*PUUtXv#vO`?j^JqD89{gp<zVh9bVuVLf#o{8xjFz{?wamJ5Q03-z5z0}u(d%r|$K
zGbp;t){|~bF<5SL{A2V6YEbr@PKJZbzNu`m9_=867QH&c&y+?D_i;Fr{@oHRO1&re
ziD1iQYE1H)h#l{=c|I?+X}ASCT)s7d{AxkJ5z0a8mz?N^_v&?B7^AryF*+w&#Qr;_
zc#6keig<8M0oHXTsbf;*aGP|vJP(dc@A*Ktw>tX{#f2P2%D<2&jXF<9Vibo6UpU=_
zX5cvqNl`O5vR2L1A~0<k(r0Y&8;$p-ef?^xvkjT7Bq)vdD%(!XVMS4*N}fQ<ra69>
z$Md!!-jqP1Z{jlf7xtWFB<51UYxFq0)HLT~Aa$m+{bIVh*6u|GmuYAw>mgHiB37;2
z|G@-Aj2jz-_)e33;=V6Vl%{61%5?mn+lCz~Mh2NWWH{#DZRp+{mX8L7C?K!CAT<GR
z-h+63ei_l^?nLMO*c(DkB*lmDOLrR+6<^zm3^H1)tW>n^Y*y4clokU6ve2i|DOne(
zE}9~lVO1<)8nvPf3YJ*Q{@8z{SS9^63h=V`i&ZWrIUVZxGLr4XAaZ}8=v})N&H6TV
zUO1%ZrUmG#(Cbc3!5uo!yi*zj`s>Krf;vIpkR4*<6tD$8XKxdXwhbAspUogwXJU&w
zj>deBosQ<m27A9YK~qLMwuK6bVDCGl9@f10{&Dw6U;~3#$I5h%`Ft&Ee<8fbsD4!S
zX{-wR7FiZT{_b(`Sh|^E7204nWax^P7G^d&SRreaeuU7-el2;KKcX@<4awTNtdcMN
z$>_M~J{<D+vrEh^+w9aR4&jsyGHjmmb1J-$4V4bg!{OcK?+v5mXi%oBSbFsZGt=+#
zNFD@C!v~q&#b3W$QcK`{mFX;ddJR8fjgB4ynOb{3;fI8NSzwTLJndv=kwy?czc^?R
zIi9jSNj1ppyTJ`7>nEcZm$~7eUg%^M6xJdBz@uwvm!B4J(;Qi7i}+y4G=GcLP8g5E
zt{m`J*j2xb6*L5%-zFPl@QF+nyZiAu;uR9#Elu>%Xi@wFVy5>c!uLqOHnjqdQenOr
zxBLPkL0d3^MzcTlw<iu)L>CjYAz6EGL6wDe)oe;GliHbBmnh>TK{H7kE@W`YAk8H5
z4pwjn-BFwNC)FP(#alwj-@7)1@ElK5?xiB?7j8OhyO}l0<Eex5*Wj#{8EX0g)ezu<
zLVQ=>{-S?g(Wtr@1=ctDz%<!%Th@Mxw*<a?&P3G-GaL51&tE#+5>i9zKusG<#r3E*
z+D{TZ*l*J!Pn!>-?Sw=Y&q26ooIZUGO5?CWyVOPR2);%~`c{$KlVrx1baoCST@@4L
z5yEfo-F;45%%&32{@>u3!Kld1Ex+c(MnYAQ{aHBWMv&Q~kvUwg6<P=F4SzeWdo?me
zNigi&Z0!zKF-L4M2-y@gpPFwZL4GMO`+6_i!07DVh-D{%o2%v}XVkFSwQQF6$!jO|
z)x#()gO73}&twJJxh*@4J_pe_{5W~W=9ay7)^Lqyaj3c*`6D9l(Ugma(HQjCoxI#U
zqe{Wh5k#4<%7`}`<aVWmBPwg!d|iIX+9Io$o{_VLlA)mtH@u?Fylz>)5Sa{2aIa~r
z?mTk0S4W?7E(=XJIQ43gdQJ66h|{l(*yrc_>S^Ohc$b+_E0napv7I;l6Ot@=l88OW
zx-NOq!NOrQrEvMeb%6X?zoO^O6g0#Y2s0;Eo3#XwSkJp51o*6L(vZ1b5VCQS<K&5{
zM9RijA|dkPVh@OQ4C+69DCLZ!6b8P}4gX_PWom|3cK`}+DymQZ=}2$e{CF6N#~C+-
zh&uDrlJAKW6pf-pP7vP16=1qxF195|ZMziJa2Mw}LQ$V^J7tnV%}PQ8e_==7NWC2d
z;$$D6dA=UhaYWo}>ntuc9em{ZU09DKoUR{X6}mC|UqLC-15yx;>sFeyoK5paMq27h
zsXlXgwdu3k*e3Ll3AvO_CnT?vOewBp#srba^BmRL)!`_jX4}$;n+4U)U%O}w#rhnh
zOf1B2H;=;!RiV~U2oqkp*7?xKqNF|fRB0i2Cvk-ZCX5nuq}?Sh;vg*dKiwhj@94B{
zfGU@!1=zeCk8}@RPwLlk?3+gKtEd8`E~~*@?tf|aU<ZcsDH2eQzT5a)OixpW5;I=J
z&K@m&G?O%EQZhds5*w%L<s103M%ZvlCKfEZK%9R+;!BUtk+6#joJ3n+RF1gj?_puV
zeOWa$O|&J|tujGV=NnF@@yH{{=LNnW)iS<~67l%&0*p=={+rpb8ZC4PZ2O%vW;mvg
zLZmgCi#yF)yEv~U@g!ya@d3h93wPc1y2l#rM<hgH*LffL9$|(atuG6WN;g(J^epXH
z^febQenoXcdCgS)GsqoQ#N!&0ZruvAM?w#(@&rw14xF#nR!gSL0zQZ!fh^2c%DV=L
zmBT^61nBP3OjnEQpQ#Wy3dF-2J|wCa$+yTHt5qm^z`&`~PTq2{Bhc+vN*EB6xOmAN
zt>d9J4wRJ6hWJQ})}7n@KBT!=#hp?6=1h%|w-;I>y4(ngAjHsea{MB+z1ePJbu8QM
zsE3Ca+RU{H<Uw1lif?vpe<6nUf|OoQ1)d8PxU)MSN7`@b+I?!5#hyu0J5TMZ+nev4
zdbZhe`L^?>Swgi9R;T`}Ieq`qo(I2YmF(O+Gw82LcVE|4?tXX(I=|KkjYXlx?rmj$
zW<PG^whf3y(!g?(8=`)ZXz@f{mx?<<27BfvIUxW?7S#CP^E#Ei$8^;hRMQOYukC&F
zYEt@Fqbl)i=#0-l_E#Y99;)l^(jup2j#Ya<?)bW^)T_8k2_O4j^5n~42P)1shDJ<j
za#6)vF{+Tpp~5`n7xQPB!<3)ws7n418fQZf9c-?mnFB%z7w=E+6G9_@gVcPiYc{HT
z#sP<>wKT%WVX^xHR3FWaAafTT+hXweh1XQ{@Q)JCvDmT?AJWxPFQpLb)fq!)0>x>l
zMS~wxk40OAm@Qwh{LHX_LwtrdlVg2?T2UL-M@EB*2stw}v>g#<CTR-y2V~EP&(u9M
z(rqWvBB7IV)~w)A1pff{Ul7O54m#AOA440G2i-FVV|u$(KhHEr)!Qm9{DQ4<;|?5K
z5jE^tpT!v^<dOaFe&ttFR!9$dsh4GPu<i95?X$z;qX)2DOUr(+zfSf&LY0Wgg7k@`
z4^cX2%VrIGBxeC~4uAjYR<yoz2em44a*Hs!(|BRee_d~f`KL$tK7?$2#7<e@xDSDr
zlmhV4<IPq<as-n?0-|n@?wpi`n%fh%Q3n{>AYm3>3wps3b{;~X;|*l1Z%@#!e>jcW
z-O)41w5VC!p6=eGM#Iq=83sMbca8O5Z7D^~w(8JHcI6vA39nM823QZh5JT(#B5QmX
zi+SI5$)zOVEds)4nV$+H3@YCJH=k2tk0@G;AI;aVcN&Kf=YEqAmPe}7BEpWNn*Y;U
z<lUw7-KAFp*M?Jb13r$j2|X}5%YcU(j8=Bb??|(rBxH}h8Au*c;tu2MB*dx{Zxg;m
zy}yH=K`lS+yP-iybFCq@dfM9pJk4xvw){%&;zsNzW;3lggy;xc!gTZML5+*6TS!@_
zXVYi(Zi33H5OW&@9PLJREXiy<wSHbM51;nW;KSa`nZvg5K-2-*#&^~Unxek`bgE+z
zyVlD)92}Zt+qHODj>$@Wq$z#_vO}z66r_UU763C(jiw6u>tNI<q%ngWx<#KiZ_5tU
zRjOz{wYJy%dS(Q$m<`N88@qf$W6zOMh}+&a2x;s`&lb!79Xg9z9#U2kt@yqdi+?ZB
zq;}W}T7en+4>k)?#{(N66rPTOCV^l*w0J;%S|2H)n-LzyNB{v&-0^_2D<$OHecLIf
zAyuJYtK4ad=;d1UJ7~b)Uel9ReSf4i3%Y<aa~OO63UsA5#2xVP@^)K<*es7im>vq~
zIBXF;^3P7xCZ#~aETdm*HL{;HhD~@uE0SRYpCpOur(aZ*jVlUAl<>=y(0E<%E0{CG
zs5U7@=a<Kr^xp!rw2@>4eC0apeQ{NPwT!1M{vO5sB{WB;TKVTfDg@ob28ldQ)n*QD
z4+6`@;o_}%_37T?&F_{DTL=Ttpe~3%o$6x4MJ<lZf4kUsdp|gxCy<O*h5h!Eues%}
zZH^kCnQ(w9+0_FFCnB*L1vdZ)zGqFwrKs774hXH~lIqr_@rBpMo5`V&ldu45!+*2O
zsaebIklZ3YyXAsAMXTBqjnJ#+J{;U6w0Y?7^|?sq#ex!epVFb>zTkG?&$9PdxE5T#
zREoD``>vx@zbqEoj2`gM-A}9<w(xosw`RO=bvETX)c!$nLQDi+pOWITvD+NRG{F8*
z0vj`n2Xv;gGgs;3REZ|sk-;I;Yfo71Z_TGT#@r7l`QqApo#&z{0m>|r6TRl~Ekphu
z!wn^8NxN<#S*rdIV@@FbB1OA^L?>#^3M92WsW+nlOk`*wza;(JPs|>YqcxBY{iDRr
z`1isesH<xhe2g9xtm!{g1HE;U*=%WgAOGJ0b9ukam=n)J4fdsa#F+f-!&+B4<ESxb
zDOO<V*I~$3v8NcEbG}#N5ftKCe{B`Ds#4hT2A%KHGZHguR4l2Nr(JzKUii-x>R0&t
z0g2;PY_4<nd9OXqIy=-!+d-C_%!>0leiy!Z7R4N#mO_#Ud?Wrghdu?3c~&Z`j<hqr
zkkI&o3TY7bTIAn49Rb^47$9UUVXlxv{}d#U2b)o&vRq_xkHCfglH8nVYFXo;vDZ(p
zH!%G8V(<^YUAeF5-{(N1aEP01<>H$oy{8kqMa-5P!fn!{q&g*5`ENrA&jaGe&nrsN
z*1^3TI&MAPXIhUT0gnUHfhV)ow9r^D0MR}l(2Xn(1<l(XOKCw4uzi$3_z1r<m10#=
z+nbyk6vRBbv(E_$$Dmw!i$r3^)4N42OxQvY$>m~s^vq|71^Lj)dh^6q3IF&rHuX7t
zibNh;#9G)tbaXzo0V)7Q;@)~v;F5}#0Jm^3w*U8Xy(*oCuw?LmdTH6|J-40>j8Pfs
z6^4ZSZg*{upfpoz2y!x5PyJZJ5jlGVB{9lZTH5mV06$?45(lL@LQdM$fCW!~tfWpm
z3XG9K9rF&U>l_5mfCFC5de^AUoZO+sNW{ZsDEiC#ue%E+h^ZOALaf3_o4xw^VrnM1
zUJI@8an*UL>O>mqxx+6=<>>_s#*YhZ1UtiiC6`;+$)etx&q6p}8qh~-V_e%glt>$n
zuQ##Zb<v_ybzH~@N_CN|C4`la&(nbRZt5i12V|#js3dH=AG&A+FmzwHg!7_xM{kH9
zcL_R0K@Ojs`CND5Q(I4@F`$F*Xw4h8Z)C`|4Z~ty6=JK8L>6;0_KO%4*>T>;37=PW
zl@U3ksBu$#gc#PpZqAWsCe*>kf=N;;DocvUq*7i@I3Up+k77TN3mm3O0oGn$sWmXz
z_vXsXx10M=dv}UHaj5)8f~QRgZy%bmZ4Mvn7JqT*oOK87d%7>MJO1Uk&}q%cGia;!
zC|14Y@|cb6M?kK-mc;2<Fb~-ffF`u{H_nXQ=OX|^YS8%mZ0q|h$?MHfun+wT1AMT(
z5;YIaU$GO}0X%pVe#t_Mvj*u55-z?>_Fzt2+}E)X%{CjZ_Y$_XN~f7^L{JT0fMBy%
z458$aPHq4dZ09dD&Fv^=BeLc<HR2@++gWRmBAp2>7jhlvl9-y>5e!6VxOKM@r8au{
zJqABv_#zD8y7{%X8qXuCq~cj}H1t{5A1OCTo`{qS4Rp|EcEp>svJoglyw!}f8Q8t$
zalY=Zo^~i&@8Pw<ljY4r|BN5Gxc~=znT~AM!PUzVd<6-&;n0W>7rU@s6e~fGtU%Od
zAe49_ut)MtJtjJ>AGG86(UE3dt8OV--SBSv#X7_d3Y<uBnk>2Yc_Cs}g>^gkn@#5f
zy^%+7&xqr|^8lN7%h8qtv|~!ot@>NX-EL8ul?3S4hdbZ&^}j<9R0?EAJA)jJeF5$x
zXxOu1B6+Vr_kLSBfl{piZKxr8`+2hbAyVfg0GaJ`6}oQU$+19|7frE-yN#+5cEgGC
zv-somIC*A}Sl$`A85E=<*4M);9}pz4`fpY@6^$S)2!LBN*=Bzh8cqKBIRPmxmB-3N
zp#D|_eeEOfG;RE8<;d80136SG*OZygAblAAf(r!(V_>GKdj|wAvdjyS+^)nCT{);l
zcCYbh8z(w`iK^u#=&OjZdIw%h{gK}(4jnUtED5>Yw`J@eN>u`oAMPDEPCe?RKt!Pf
za3;0zPjruqnp{ytX?wV-gCX^26-KmrQN{`V)qu2<bptz68Xio^2|9F%Td0e4lD(;>
zbIuI7`XL>E01A|n9GZY_ozXbvil2A>aKD=>czf7qUg}Le&UFl>c1b-rwKClq^*2?K
zfE@=QQCYfQ=j=j&Gn@s0sw|i~UV0SWWumx-x9zZQr@><?T}&jy$?E6)`Sn%jD`cSq
zh~FydQqBGW|BaIoLAk>1BNm4?Z@kz~U2noy>PXG^IZg;nQPD-*H*Kl*^=JnKR*M?~
z-si`Y&0CW<BC*5-$QY4NQiz;bf#6W!THbLFCBA*}^`?mXZ2?~Fi9KB^nTXu=^fv?9
z?98W|WHn2vZL@`j{-=5i3pAZpAOK1}#1iVQww)`cBZ!;yvH==-=w7wxPM2jTJLGJ^
z)jxff$rP6pIg-0CA5s|si2@A3sM6@&+EF8(rxcLic006Rx~+yxiBvtp{=4n+=zXTC
z2!baST!_RUdM8<rM<&eeVvh>X&&-RVPM#lCk69UC>oQ(e6^{-O1n#o;jw=ig`;SQZ
zHNe=H_HuDYU8I)mBFMXU)R^b_(r2@g60U=bB6CC+0_^@Da36rzX*rs8T8_Zzz<s_;
zstR6-=A}~*H%ypFf4FVL!<?L7D6hK%G*HWj3)<zQ(L!edRK&kux6S?Bhq&JWB>#2y
z7`?D=6un`A12HB(<d>o?BfkP~q2cd$TRr8whGVHs<jfX!$6tfC(nV>HkjKUu2Y9&L
z!W#)6M^mDNvjnaiEqm0Amm*v-4tNF^zKSAwLHQ$~wrv3W_Pn$@50aMD9W&FxCv-Vl
z^~7xlT7f;t_@WzCNdY0dVyGSGVA*(yL;Cb8{CDaRi32xGw*Ktmu^D|0VD-*-bZS2R
z5qu2sLb^UNE@<a~tG6mbhG2m>46pF`_1?4fl>Wg1d5W{<QQX!3+hfsD!9`V8U#o9~
zFwY<pFB_O4i{-H}s0z^x3y^9)6?jUk^Y$S;3fvepX2D+L!XiC1IZ&(`Ch0o|2}ery
z;J}ob^;K%|yGH~80C9Z!S{b69HG&EN5a!~;shU|245_u_=Aas9IPhEk^fyHd6|N+_
zGW9yb9O@D54Gr8$6Wy5yi4O?fj{_Fk(xaX;zFE(tj*kS`jn&I57w36w8kX972LgZ^
zsz*l-J%`56;k%l!OTAIu)lYpqc$`@j@ES+8g?aGZof3l0Lc6QL<GUZ3Ud;P{VY`w6
zK3T|$?u&njc2aS_$DX)*=ksjWQjn8m5P;SteykL3m_&(sp*~UGSLQK2#&HHU4{|i1
zrfh5TaJt!Z%Z(D*LSWn%#_?f$64B!MaHL&fS#N?1MxlLUK(ryXPVc_Ejq1|gjd`!-
zsWJ?><Li%WwHt}BJbg9pAW;|CdF$bxhf|a9W+iX)dSJI<x#fcp&0jMX{qx9K-I-~z
zI>TyxXU5gvtt}?m7u)Q)WVdbHZEK4M1V?Z~5O>_kR8W>M$ku!u=gAd*)~SBalXW))
z7@!F9A6(qIt?IWMdr(;)2Rgz$`Dt-aO_Il=NH#ZtT(GbzEyG<8#o0Bv_~VOdSxv#q
z(Z_!0;z=QPd4HBLvrxt+04fN`&vobHUQrnqK9q4p*$%|4*gc+#o^y-^7}53p^`3(7
zD36L=uf;RuaYjky0+g7NK)U8l<%^9QenE-rAZQnVFH}wc5Y|D}Mc{Ndj-)PyxoJxj
zN}35FKm2k>zc%Xuf_N~4qN^@T?Az;t6DaNu4bY+<JNZ?9A?n^zKOh#APy75Y-AZNH
zEE9)8E%N8PP3K+>LI+M^NOx8pAIh-oYGtF%k{T}!(~1-d`bO954C$;*Lv>OZq<mkV
zo;7$hsGITAJ_?r~C?gs*ga#M1rlG`)q9fWhgLz_$1SNTBuLpVE7bA<Z^HD{}OGudo
z8GmW(6-7SiVV_==UPa7>vVW7Ala41Zp^k~%<%aL!`Y=sHZ=iZ&(vyqlhs6I{P$1Do
zX@eq2>_5%gdV#kK9p@3~i|s%3z4||t<-peou#j*A3(xEqOQ=G)D5(1B+mQM9%S3c0
z=1^aC*hcg^&Ix5|ps2Fensb)X(A$PK+uRS@cuS|RQYk?P90iG*Pu0s!(}T}Z$rkLI
zR6aVY@6ju^+cT7Tq`mBnTDI?YmM!;^4f<ZG4`Jo%(_v#qNwgXaQl<Bn*DqMJ81>qZ
zOMn1h)$<M2(O$zgQ3iIUmNq??<Mr<5IW%95IQb!qqWZz|;Hr@X?E*La-^Yx)()7W?
zIn?qM9f~z|;>{D!P{ji{)cj|kGAjFX`7jhxG4TKrXj1ReSVn03A7@FEy?u6F?2-_o
ztH5fGi-|p2J;PmGYMnIo+de%5bbb>Qm`tBKr#gxD|Bl!(u0hJanV9JPoPu^PCiX#9
zRVP2U>gENshD@k<B@e{j)e0~gDFlS7&0vq%FUg-Nb|j(Rth8-R@`9;|*x1lYUQW*&
zO58CHmCRsKL8NHG15cU!EgHbGjCj2v@5Qv<G=wUEu|;g(m>0aoy?1A!>9XSFI^X$S
z#M6>36br%GYdo8m6Ht!|7)qg0|F)w#&&>8T%68KCi!~ES8(z;iQ4mU>GT#)CwZS=p
z@`a<&Ou~V@gw?6m8>S=bg(5<S&^q1rGH>)~A+SenWnQ2B<goeI$$U4+XiN&(?ew_=
z6&B%I0?ajB+^_rWsXAIhYZ>v+%nu<gy2nuBj`^~HJXEpZ?w`>kO$2-&LVu<Gw}{5K
zBmHzx9jI@*>PPE92AXb00-2`W!1AdS?Na(BeVp?BBy=Z>W4eU7hJ`dVY*mAR&wUMv
zzq*l*UA0VXZb8f6#s-Zj&pKOx8TN%`oj4ZyKlCcJ7zZC&w(P-|k>1o}WklMV6152{
z;zt)thbx7lP%tG@c=PIsAQjYz3w45wnxgfgj^|MJk@Z#P9?X8(TNHx0+yH8)a|SLo
zTN;c-ZG2D{=<hT`&!u7m+VnsutmMrc(#thOG(Fu7kNh#jq!X<JyO02~HzeN@yy_%8
z5>?9bOA5%r{n95r*$)wqu-qZxXtF@IGtRsSDH)vCi8s7>-DA_={0b3|5Q@RV9A_p~
z=BlERcY>3dQm$orJaE)h;czyW4b33-RYwkX=10$`M1rF3&sZE)mbYX$od|V*aq{w=
zVNn<UBv2_}&~>@AJ4DZ@Wr%i%dK0@`D~y(lzx|7Uq67k|;zl+%Ta@OPwIDv>#7YU0
zXCI}u-0%1VN|umsI@MW;#`F5ab6rq5C=F_07Hu8~@J99HPzo54YPe!<!)Qu}s6aE{
zk3fg^<HnZF+3{<ev+8hAA#*xPb&73J{SMTB>m=8od51M>q1F#_5=0Uu<2RQ$*{wZ^
zcIf~l<hkKx(i}DPXfo7wc$uu4$IwOxdk|_s-IA_a4*j<Y9iq7*Ij&~Sp`HD86o+9Y
zgACr2w>n1FAYL9N<=aT3{0RNWl~?~8HA@GHB@6&XZl_x9P8o|1nhg}^lJ|hfIEyMh
zaH#uwV_z{voD?@w{XR95tW6I~i78w?e5q5o9E#5KXPcUSXJC}LVYkUr{)o|v$?S?l
z1)3zFK257|e?5KH8A{jS*J^3}z}B+-s`sdtgbig4Rv-3Fe)Q1|F$gr=@}Br*>J6Er
z>5@<#)zF@K`rg`MAJ>^21Kl^3i6Y+veTUHoL4}@SU+~Uv5*M^8I&`Lb$_yT)JVe|^
zzUt%v=#>MRC*h#>GCWQy7yMG(w(y|i8q~t7Xv$8WUZ(4~48i)4pns0VZ{8eojYip5
zB>d{6VVkYDiT(w2q|kDEt}BsNe@B~ogwGVfoqY07y3NJGRNV*(eK0ZVoXEX7s2xI4
z%ZH_1<kmDI!+?Y`7fs{hUlW%?%lywG2^@-PvHa-?1M&?}VjBIt2Z$1ySqvD<ehc|9
zQbK3YEu~xR*n53w=SbTJG#gYtD15_tRd^ODceuj>xASIR?P(pU5aJt>?V3+>jz;w%
z$qEeual4(jR@T^uBSFB2hSfX^i=}u4D&J9HgV5TRMLIJ7hxW(}mN0XL$SBx%2@;|z
zptuY1eVgn?5IHt-@uw4*a2=ii4XRZ}j2GJOo&1B#evd}Qnq{RD@%X6SatH&$b&OK-
zi!7z}qi{pV2q2fDNtW|T*UH$57y*ER0+vOBi)sTWy0@W2fQ=gy?X1zZ8tG^pK#-o_
zNZpn$K?(_fCnrRVDBI%Mb;$AnREdUt93xLVd@A90tJwn}tHb3y5Vapec68n`PDRrt
z%jLwr50d75UpjLm+LJMvoE%|$&OcFgh&DL@Dqz`?r4@P{X#%oIrn64;;^xJ$Pl!cu
zs13XB*s=TE#SJfown2$X92ne?+&AgD+0nk}W9VQ_ooA4>HXa+lUx@5b%qHv4L(9bO
z&f*ky{)@T4ubM&41=G*O-(~sF(g<CFTD;h2Yst<*`}l2`QRZbt#Wrp@D}8>vbK`Yn
zL;_ek>jyc=7+e(}67EKovE%-3c)=FyW|!2iS_I#Qsbu0ok{^NT(@&!Xn#Rc=of9`q
z(Xty$iCn`txztVb^Nm$Yyi^gnH~=AZaJ|&*CL=*lj?Ki6fBfO4UEg=>O9fm2Ef%<m
z1%);NK{ao`LelJKl@Ylp6R#8r>aAE&sNarP)Jg9vxg;E|XN#N>JiHcbDe^U*5^ge(
zL^&EFJt@1Yu<*{&EbGO+^)bSNjf#F(9N;8`6IJ~eNj0hJJhHxw2;X>P;ebDRTn9~B
zRz}Q?zPRx9#U#|zC@k*e<OVmaNWXpp5xEc=@k#>%zJAioLwZ*e+L2^MpBuMQkeoFn
zK(=Y6O3HG-4_~HJOq_z+`|k<gdSxZrkPbLJHC%r7x;%9AibdyInMm?NDJ{OSsusH&
z+VBf?;v8slET7e>*26;+V?rbQC0u+jn~e<m+t3)^K)8Il^qo>UIM|@AbEQ|@O@~H^
zVii~>u{FZHwcaDvRWo={d-JjG{hE}Xz@apewOl>WaXIR+VM4LjcGm0$;s|#X#gFv2
zos6ZyUv37h0Ji$sn;gpG;twY>ZnVgS>yfml=}E0-5^-NGgH*<wnJO@=%mMhtK45WL
z$VgK&RAokdKe6uE6J+$`hq(A{Rc);`KJ5q4kWjyO&C2{^Y!>oq>0qY!Zhd<)&kt##
zSQl}rL+`p9!QK1yr~?kAxp4%UiRK}+i%3+E*K28n_-_{<Lk!Uyoi`Z8GcWC!)_5d>
zA}y>*meLf{QlEMh8D&tt$*8`s(OBMys4^YwmYE5HuL*@I3}mL0S4Hf&u$?EiC!9LP
z-~o0I6WT~}EU~l8QxS70I;Ka*>}>t~v_pqlXgUbCn7ouNb4CDU(g9knV%n1S>}{xg
z&(=jO{kqK8=)V@b@o1sY@VG6>G*Q-=ji{ClKA~rw7Jn&o<W;bE9TPJ(XU#U;NO3b8
zY?5!a`_c=8b`3D9SGg;0QWpc}p?h1KU%j~vDh*D5JIK3{Bq-qQz)+xq1C7*qAGzkm
z=@bM5?gb!WTgXnI{Lw#;odZUk`VEKQ@?z8xaZk*|8ug{c`HU1M>WO6n+~XC@VRnpA
zm7*(x4vx9u*WD*_Idmjk)1hskLM8T>StCWu(B;fvTXPnu^dtIU!ATwb;>y2Vj0D*%
zJ*m8UwMAq+DRN!fI9#L32Yxx@@?cDhJ7uvd5D?u<9nYKC4A)A9%b{n=j}IM%@$|>2
z#@eM@#7fe#JxzW_$SnWsCh=`)xz~2zr;iUUj7ndkXbd7R)UvbP1kaxM8l;c34!`P*
z`b)}9Kh&?8Om$)5R?wDGMy&b${uqh^<H4i4^|Jr<WW(XCNew@tmh^p#Rfn_*DZz%T
z!mv}G^Vem(ppby2_P!+Px9ICVAz^NS1Xea!kfG=8ePUa@(PpyB!PxTXeRPWP0vUbT
zK=->+`SV1DD0ah#wxVsUhENA_3V-B#E@E46-!J}q)Ntfa@FD2XSR_2?B^7#6L@fll
za%MayOs&BOfi&Re(3sXhn0lQUkp+K1Q&PF3aIJmU@C9PU#7<}mULHvI;kV`?ooNh_
zla@+T_Ip3eeA2LDC`qYZ0l4r#+vtysy8=W4>QFAc|B<xN@XAM|=>rrA%C$|Q4Jz}8
zZBR0RJV$;cyc5W&Z@*hUDM?WuULc|PM=hs`^F$G$s}cupUxFz#VCvpZb+D)hZ*Gs0
zr`~)m5eMB{@XjTJ1JH)IT=mS4)g+v!&M!z$>Y|hE$~AhXgC2q7IM2wUrt>x>v4!O!
zgqLSw+96M#E9&xw_*}wvZr!p&=tv<t=NEMNd5CQXMW`6)gY8vMr6zb%<TlyA5wEK~
z@Wldbv-Uw|0#4lzC<WY8t7Fy|XVR`Ag2B7x4ACFFoVsd1ik!*+x#1iBBof9LFYBS&
z1BjJwc-{wnznxkiCs3TLsA~Su?`xVAgZ!`45Ple%XWLU(04svPtw{F$MQBmZ^tgpf
zz4y#stY!E%vH4F+pM^8IvvYjtdVBUX+|T*2%ksnQy*ZlR4dEBAL~MUQVTVPeuO(K_
zmlh5|NI3!i)>j)==Z5%)IH@xQEt1o|yJxEsK7bM1#TEGEm?`WX!zCBTD0-ba6M7>C
z+TfCix|Gy}simjLjj%p%zRVz$->K}#R6N8@ir9`u`uLj;B`m?atv<^Jz)DTAOZnwN
zs8U8KA3>w^p?<rU4|jYVzv~eDv2s*J)XZY}_30m{B!|3E{z?a+BKt4fet9Aoqol0p
z=Dj`OVG3HlpS|MN)vt^j?Y!Xfl2%+mZr!%@YvMZP1}f*FC%{sMrd~AMGfJ_i%17X{
zREKLGzuN1lY^QF8GB}wPn5l#LZ~o?MD)pOU1PB{h1=@Uws6J&mz~~O%9HSf-JT$xg
z8UK9L30E?;PI1N;Heh9>h825P#wiuKVkX$>AhIH4Q}3lq|5FH8<+|cP_lk5c8~uoF
zRxp=xW$r+pU!+s4-5)7cqw;Hj^Yn4I@XWD9%K_ea7E3ujzZrz5j(zNehW=y&g$h!B
za0W!h1xsU&9`aZAJOBJ)t^{MKU+pXWtPHX%*Q;G5WCreOzwQuap_GWqQ9y(j=Oxo(
zwki2n@hA#_zI<-;{x*^;SXDVa%_Bs`BTua;o9YlHmnwFE4!*L($)}S;THY!3ui{lE
zM2t`AyRx=>C`FiJ2RKlqer(6PvuT0K)>7p}#uae}zC`;zFjiMu_{y(AX?)-FK5Kd;
zng@XM@lKB_9GBg=i6V)0T)b~+&u_sm8kNqdf)W)?v`d{HUvjuF<s`cwQBo&49w@O(
zJgp%0C-#nF0KBY#Z2!`vS<&aPSz#+Fcd2&*IXE{=?)&QA&1a0CuL6dj+r^Oi-iI&J
zV#MuBm8$}`qAq*s@nto=%It)4)2k-nA5I%A-F#R}X-_Ja%KleQ2%Mt~SP6@j2%^g6
zp93s~v$BnkUGqnH0$OY7Txp6~oHF{Z{24S~UirW}+gg75quI~7od<{Xv8~~wS0A$P
z2a>}_-49U)$xYw=cYZ_Uq&5NHvo|p2>tkzTx#{Vj=LIH|yL0ZtMi-Wi(v?>ZaTzRB
z+lM8uA1k9XI;}jne-&M}b5tJ=rY}E54I6Jr=FZq-YS!w!W@%T2werogfM`uxOL9!q
zDBi)&H>scKI}rJgv>y>kww^4g;Nh5q1veZ|L#dmRO>l^H#-#myX}rC{98l5{Kslup
zf6vbJ9K1ce=eS-GTQ|+B{E0-5uWSZW4hk8Y^XXOF)R}vtsjsoz2TWbuE5|2%P-509
zX$i!C{H)5IPNMwj+jw7oGDITCE5fo~$CXaKDFi7Yy=$~Y^Wh!ppuM@cw&Xzv`&jBk
z#7U+T@R0N={mtao`xewvae5KEDerEQ`CDa{LV5Th3FNP1tE2A!G_92LzyOE9sgmb`
zqp6XAMC&5v=w~v1r=|g5fcp5R)1Bkg`RH5}QM2+;wW4M-FNo9s6ipB!lc-Nj^SWMG
zpag4F5;{c43cF{|2z;r;I8h8r{wBaACbJfjGS}bH;_pl#3rtdG4a)RqCCmMz><xA+
zwiAyK(fC^<qo$TcQYT#sGIww0dA#*%36et~{@Q)Bw+8>GM7&b2RRf^Cy7{YF>}`vO
z=|R!S+Me7PdHqO%GHRjxnH6N~g8RKcJza+=vtj~=fAYH9t@?WEq)9DUgU3e`=|G$t
z8j}YaR=8F4B-K)3K`Z%EFoXDT{JBap_E6bKr(8gih{htPpB#g$A~}x(2;~jz`JbVl
zMI*ew84!9O{Z;$;mJ*p-xpND-_}FaLvAd$yYiLveZ;fl4+=-oVQs$$S!-7Q~ePrFb
z(8fG86Xp><*<h1$tZ|3oOY|;;BLuR7Pm@anl*XGvyrh72B#K_UkFTbTmMFxA0@6`n
zT6fNDDUt>XK*xpSv)_j<EL@I=4mzDG^Z#3s7OaAHZUU@fb2fu@Ng3=?9`!7M=p{cF
z9unL^#{g{k#!<L1>cT+wj|tsMQKjfZPi~qM+-AL=l9}59@+C&~{O7>T(bI^`6BYOc
z(imI?$8o)Uz6K&V2-X>B9*tnoPbp$)iVY`CB~rHg`ouoMKVv?ctS8uO>l&9d$F!p@
zss^BH8X0Pn8-REmhp^M}tpENSTPswCTb0`Z^2cf4tp~U4ch%vQ{(s$9k`2I(Y<k>1
zd+i%dG!-2%QT@MP(N3(5qs$F*N4JzctK!9K#{#6zNOvxNvHaVO{Pb3I_&B&lacVCH
z?_xq_uvWQq(~J0`Cfa9b{Bt-(`q`l6IazsR=6AFT&Jm)8_Gy0MKvy?9XW>fO*79w^
zmn2<lOp)H>Lvs1(Tb0XR8!air$Cit487nE9d0pvgDF-zGpy9%AS~EjN#DR7Z?V9XK
zb-T63t^wYaf1Q-lPzZE$c$4t&kWtjJDq3{6bIi^&iI4G=+#tC<`~i{F%!%`OGj*<q
z&v6_H*8`T?j=QLNsGlR>#Cr`5O=YOxLC2_5#LU{)9hx!9ZwOf`_?w<m$@X?h+76ei
z!%7YZ)&1s%yhz2Y+nlnuXpk`TgA%ODgXmYULdc=$*P||5ELHlp3ev}12i&#0j8=AJ
zcKmF4Ry-7}ltmRTU%B|l;f6nuy2V$?d;(m?CKi5}P7Oh}$x83EfBBm%Kx&l409MFs
zPQu<!C5VBNg8}~IJ4;<Me#QtZ0SJ;rW#W17mMH2}p)*+jmHT}LPi-SQEe>Rg+xq^i
z>HUoKl;5L+GWVkV1H9=$iZx_Ke}w6}4Ri?mXJ`|(xd+hkGo1&b3)h*Lh{~oS&WzOx
z73R5^|H{7)GbscUtv6_;OrQDKq>oE8-hymb+ZlwpYv@d^?}o})qJkta^&;$MsO)}4
z%sXWDaNhL@Jgfqbb_H;>Hd9+7(v#g$ahQ=fuzne$f6obI<dP3A`6UL*n23U(@Wdwd
zefwCQTto9xG;Z(&^0KLV$u<6@lhm#xO~9+@Ya_!=cPk^&%Bf&QY_Uu3Kkl~P7ZIT_
zf$lizn<kJh3n{YGgs|(kSL*trV?Rv>7*ztc`<C$D%dRUz{-ijTGpUGHF;qKX+<y-l
z&6?n<kx5Y-j0@0-DdK2LHqK|L^9Utmu;O5G%X8TL#()i7hW7K2Nw4WL5a0ba;Zjba
zGCHOFozFR3AP8S{V$Kp}d{rT!n5UZPcuMQtZ@Pyx9#9sNA|ys^OFT*s8cVGr7FO}s
z!9BcSHHu+5Q;XQU3QE2{Gaw>vQ~Q*xMdUwXF(w{<F^xKjkQu}q!t>sO{@)<%G)H%*
zrE=JFO&op0qaHmysaE$X3ufOrY_%El=)2B^Ru8CWhUds1-O(RHJ5Hl<O~45mol-m}
zbn*@~20*|yHLZ@)m7-t$BEawvqmZNG-zc_Ahszr-hwA4$z0gmjXhIR+sj}cki1!(s
zrk;_rCg6vU43%}hKth2t1t19P%D&gG!qBhcLFj%>N?hCdV;SO20$_!=#hC{Cx+sGR
zN(}(g)ZV;&>$bG>)F#D1D#?1uqQHQ@+2~Dc=#gzYv3;8&+&^rlK7{b=>+*hS@>FU}
zd=2FH=<Db!B-MWCplt=RQemfwIy)DM;WdC^qj#M6_F@O(*U{_9b|+);mNylH$kl{k
z*Syg1$Zi8Anv;U2Ig13Cs==~17yA&0q!;&K7T*h>2uiUP%5azR_~M&rq3=8`U)_4I
zijHc|IZQN^yE%zp1_j?bkL2@|=D;y6;!DDk@M!NKq~@z#2CT+cJ2odu-#Am|F_YaS
zN%zOMZg2NNGJIkYt=gmIqxrF=l)Z_MKF9HwJbgd*jO^J}iZl%l4j5dNy6r3ZFOWKF
z^@9Ti(@RR$Bvy^0NMzP3*@vy=u|kJf74%47R;xogX+PDSNj-{RDKkwM+Fd^JKpTA?
zoT~LPQK^lgPfk(K0C<AuiQx&CJoVAp=$|9)imiK-l7nth#0x-5GXos7_N6SQUQ_`f
zk>$drLK{_Eq(@kgLd|JObsl=IjPfeP5(}c0r+QQU7Ma>3iDth_roFXl%Gk7;%o}J>
z2(+tYFF4paTZ!7BI2!(qCi;yJK|B6i$fR}y@c($Hojn5@-#Kh7ma;ulNh=vN8^4tf
z)?G?b7XK)<X#FbLfoblKEtdZ<Lf;SSmXMJ%7meLxnsJd@0|#Q5VIk+oEmuaNl&=ET
zpZ>vt6THGT$a>ibrr|!Hjg@!nI}n9H-0YLn=Pw1nqlki{W=$nFcuHonvJYITrlbuL
zI$ecTX4qS-gVYH0V`AAmy(e(is#0eKu{4qMt9ubTClTR0t|OQ9i`#B)i28|isMt#(
zcu!r%@tI2_kOvI$vixjsi$Cuepy}p89Qm%rZjWkDd!+UxUkY)<C8kUEcj_UgRELDU
znY-tP!?Vz%Bmg!1OZGr;tpUxWU<To@NyBFzn>VId|L*o7Mz5qzrlUKk&wjrKNhrX`
zpbzPkHgKd`$TD0Bf$(dM*F@ik4kFPIk}G7|>?>TJzd>33r?mJ&p&9?LY}5Hqen&@z
zbMaSBjgu?;JN?nCqjQ8L!{nKZ@cLVds}v<_Ijf8}x|w`dUZbR7dwdcUO~?Y0AJ-N}
zQ{N-^pTp#@qVj&(Jr-%&Fn!s{eY*ZS!CgonDdKpgPC+I@W}~tWNBKDj<T(i>n+lBJ
znsKOBQJ6C7;IQ^p7D$5QDefSTaB;iJ&(+I&(hDe&q=<-xbPL}f!X{FB5nmu7`HQ?8
zXxdInEKKD9Q;TSmB`;4#v}0XL7!n2HV>g<3bx!LR{<T9k3T=SPI&y2|j@qD`qtUwd
zLZD|YwwLHIL0bZGgHoL45G+J|Y7T(&M<loY#=j#A4F~nnIyX$C0LL~_Bv`61d%M0h
zD)3WX@O3nRB2%>Q*n)>4bVMR>y~&mUlM}2Xmgo@#=E`_MY4txW%#kQ(Ks2bL*F~Ul
zj}my~f^KQ-gO;c;7VfXab64u|sRGP4HQ6nJ@0Wl^g`y5^*W|Hslc{Lu#!KYyPmR+N
zn@L9EVZaQ7%{uRc@PJd)s|xf#H4MwmX>&%iI|OS;I=<9aYO`3ZXoXRP<#^xDrI@P|
zf~nJp3h8V7f&#x!M3{6L&PC=qzNko;d8i}qzUzn}aQDGUQD9ANP`x)B`Rf<}U8Fxw
z>hcd&R;noZ9)R;g?R~pt<s%^i2+H+6Gh>p+%_vz06>f|{2TeWIGBXC80x9U;embZy
zZ>{#G2j^<GRXSy?UFdPB)4p?k<+Up}XTGkxzx=n`yEmNrU3~R^a=X`qDMz=))bC#X
zspRp@ph?zV7e4u7e7|y0nTe1aXXwY)EO2{lNCvPS=$PoeEK~_DB>-gFK*{BQZ<H&9
zh4K-gueGlk`pYi9VZK5@DW7QobkdcO%4~8Gm6GJO$lObA&@}FhMHws}fV)`>Cqxyy
zLTN!Zq?GAj8c9xSPsW?&I@CR{85sC@(JB`mi34k?z`G`R_pEol-&99Z_nJOJlE)No
z;?kIN5EKvRdM$ak`Lo8Y&GV?F3;XpT<8x1RnCBjp2xJ1b+pb!xQC^P|?ove`97y`j
zO<^RTTaKz=^b{4w2`jXwq57p(sDASpwCcPaOvV41FGI+~f)=)~0glqfF=e4-w7tHD
zq;h1$;!Z}c@jyiB*`T;RKUc2fr<gx+_$!T_geqkeC`8HE63dr1Xi^EjRJb9fzTe^h
zn7Z<KDEIIGy|-I#t6M5b%q_PhMUrHl+g@5!iY%E{DltXY!E?(kmF<>nS*A^rkR(~B
z5|U*qqOncZ$vT7?%<}xs49{nN|9W}mc|M=d`JCl_-shbCAt%r~fe8t%X{v2Wrf4>R
z2H-3E$Xvw@z?B_7?`JQ`qvJLX*w%_~GXr<v?g<iMm;N!Sk<YZO&0VzCv0<u2O-Oka
zJ+Jwc{(K)D_a+&M7Er<^-JWHR=2N8yI>vT*j!l7!7eom)!8+$jRJJsE69U{s6ITO1
z|2N6)k^YGZW=OA%bNT8NbP6BG!mu|B!fw+1A5Tn!k#t!!B)^`=I%AB!jRBuAr+IX@
zrMxV$w6{VW*LB=_)5CNeP2j<F!A)bHPApAD95FV;l;^93DgHgV#56QalS2-yx3?7^
z-9(5a_IAv?Hq+@BbyF$8B1!SF0U)kcsk7k97%GyX9K7!9%BBhO>4@mX23&q5R{w<N
zBSa|S0zh&2XJz9*GCJqkMghKs);NQEV6x6INMr=Wp8Y>~Wd}-K4!kABjfT}TfE068
z;Id;M6%nTypbz#QU|DP0H%h@vNwXFlIv&gQG)~{w5VMz%1`0&_{X<4wr9049VBp;p
zUG^09A417q0B}g!bXwV%R@3A}`C{=U$nwX(wtD_<8QKi+V^XXQlHK#s#pVM<pU(25
z?-RI5y#Prv87PYU`2T3+4Zf2UdJfKI|D@D2Am#hZn6YcpS5d^o336~Ab&M^t70(p#
zA^AC`95$e10TPgPvsjD)Dz<(Fvut#H<)De9spY&5n27`FTkUi0A;MEmszkmKhJi3|
zQGPn~sUo_20PtZcceO_^84zDi0w9+8Afd~E+<}_)k~ZRlroCNOf1;jbfpBy5Ofc7u
zTH@xA;o=`Fiu$!Xk%L-xK7^a|8b5e0Duehh{1y%Gcr-HAciB0jZy97@#0r%A$9i{I
z%|ux@AZBcc(}<4EN3==+Ls_u{uFzsp;t5G_mO6}g`u6xg&Mh@rPg62@w{%SLX1=P_
zjgXu*c5{ft?Mpvpg2{SMgIUrG^qwj(_PUC0h<O~oKBqrIT)Q1zHh|`QQdjrZEk}7i
zd=_8C`Ov>@-8fwn^{H|MF|_?;bnJ_88PeHINEo%yxHZMjk~m4K#+kZ;Oi#U4-I*gX
z(%g{rKF@@N=6LFv#ugP~j~E}um<7z69rnsH+8m?-qNdH4$0qy~$$TSx9ejvcqM4VE
zx+2oiI9%Snp%K@gv8G5Ywe<fWiB@o8w?meOj8-!<lkw$~;_?6i7qK;%(7Cpx%BuR|
z10=BNK-fQbN`q1CF8dQKD#ffTc!VA$#dxLL3#_?t#ohWMf1>tLK!px))cfY$SHGaM
z(ueVCV@8pKWhVVHBH6-B@}pt#x{KQ!kQHMCEYaUy=jyax_G#eWK5=0^D#@cDi8KPq
zD%0v=Iju*>cNFqM&ouP=1D>LdEbV2#da`HinL$)JVi+(X^HN^+^KH*W{!nrY*^WGQ
z@!Gt1%_i}P{|@@iD%NT^Zvx<zZY1;oW{RIhU-X=T-bt+xmnXhDbnY%g75xpUfZf-u
z_N97d%k(%KR-c-Fh<7$$)-YEdeCo)2)jD0&>6{F={>G-m3Bf$n%9IX4|3{9pDQEwJ
zRYG-#D)Kp84Xy^()|2yeIeO(u@NO1axHndn79#nh!x@O}zdlp2Me>Kk)ebXHnaWl{
zTscV}zN-RDc8@ihE{baaY!UQzAU)bT-b{EoF;eu9CrJs7v@BK<A4NX~P`rX+fkSoG
z82S?hBDIX;sg(iMPDIs>WTME82Fr4F2Psr0v675Zvl6U!He~QlON)dSYJ?PvjVr`O
z?85^o8Qvmgg8oL2VsX8*Mq^vlBL#>H0?ks#*$T}S`PH)ty$ypBZ(p6_B<Hgb^_=#1
z!1jzJWn~}!7BCMlNzoM_5KvC=@Y-{<_+z*<(JQ@szgCDZ-!BsKJ$&}d2pF3ju#$hx
z+(mNKVWh~SVnk|fmbH5mR8g1c&2aQ|53Jnj^e#emGT!ydkEb0gsEqAoY$NU;JBWpU
z&EfWpEtyEUO0Engnnh{jPakP}k?>Ut?@9moFN@Kb;t`;Zai5qM4^1-hVSYxB)lTO>
z#NE<_qV%(Y-1gh7Gtt%@cciFJKq`Lah{v&{nX{!HzeKDR!6B^4$fgeWTqZm*32lv)
z@ylBUXaGF<0s!AQ)hj}HEBa?5#u2_Mldb0LsD|&AW_+Z|HW8Yhn!dRB^Q|Ge(}@u0
zr+s;^*<*PUc{{+*XwR!THB=-GoTX2m29JgDBsnZfn(vW1N|2=Z?b7*$R()G7(37VP
zaPQKGW8smRchP0l1~WR^-Z@uvm#`KLK<KKwUwv3YZ;-|(lApt-La*~v*}v~;L@JH~
zey)m>P4f|3L{OICY^6TgGxjkW(SS$nH1Kw3W;fG3F3O6YpzFGMr^&Z^{J1*W-&N3w
z@BHT#V-fji^@WfX@~<gLsjowSIt;%1>*Z^6Pya<Y*Dy%8ZF`z?Y*RMktE2!Lka|Av
zzd@^d;+A00^)!<1;_3AkjdZPR7vs*QalE-s1?YY`=?F?yoLEbT8n*PIQ)ayFWn2_o
z41AMmf&NhxEy}<`x}tfusLL(`NX0kq#;+>4U44uAI49su7^PU8`07q*Oy~z_9(X$5
z#D=%+t#q^=m)T1l_piHmr#~j1C_Z3lWnx1$!lU`yOXSc=Nm$%dtMj%p&&havVJy1X
zyC}^-19M0G9q9D8<TYEby+PJJb~c#t7ljSs!8R)sCXE(jX(r=4MRY~Yh0=!xhvI-K
zn%()45aYN_HrDiiP=a7wZ}Qv@4dcW>LE8LQ7+(8)qA#{YKtMq@`;CWE`6k3lsX!=v
z-Dkp^Gvn_;!k^d+W}N&i39peed4i^8I#h@w=~mqlFV3GdN3vQ8&IpzkRmYRj&SBne
zTvxM^;X_0U$96;5Jij;Gt*5jZsbyPW9i5@l|5bM;+;{d}Nc`SdYJQW<MV65P<yULY
zF_Z@#Y<@?<0MNkBUFgo^G=w2NXWNgnx6k^ayzz%j6?j02kKI#>&G1#3F=)^gxYaG`
z;pDBJOXwvA#2f7Q*ZTLaL6?RNm!@LHaj}4Z74hJ>?|Z}YvvgN?aL|Y)aaDzQb=Uz>
zkZCfC%b>{f<M^EGTK;)t^u1c(PrY9MXDL;WFjSb81A{W~;PUiJS-}vD&(O_pqnU5s
zHwy)fBq*CGRSj5HQsondp7nIVYVI}#R56_pfg7L0AyaDo7+cQu|4BHxTtGa-SllEL
zO#wY>UWK`&P3Oi~qhb)4ay0lucl1qqR1p$uFoo$0lv;7uFUQb9`@z(y*>mRXO8$a|
zrASaIk2Tj8IwwdoqEgoYe2-?@`t^qGPDo>bdf3C7cWdbQUuHtrqMmIlB>@pvGPrHv
z`1A}<H53Ct-DqZN?|@@U-5liJ095C1Y+~Hn^9TApF>v<dbX$ey%}Ct<?(Q8&Jrzii
z4?(J10>PbNhwjnbD5Zi(zs>}{ZuG((YQ!{j!7!NUoPV_Pvs^c(bCcL7TmW8G#o|7;
zGaAt+sqM6g^!sn!Gg%~gtO}cVbKAm@O5s1~$v>(vQ==tH1%}9tkTC89kHgBwp6cBs
zMs7h>D95!(ULo{x7z)eD&|Y%~pX}>#5_t#`^zNqfBIQB4tmn2GtX1t&<Jh{o5+s|X
zvR?jK4-dP*aZTi*?f?+nv3c}m+FP_u51}`^!p66rM}9$@2}tWMFT<s0JE3q%*Smy#
z4*QEkR?apbK4Jn3SUo*j(kUtfgIXZLwQY&MgU6O3sg{osHE12aduaGa$x0+9Cu2k%
zs`0V&-P9&KoCJy5qD9lUDekHAJ|IP~q`zmJiCd?7SZppowP&*NU$J;P)DXU6CY`E@
zMdcYlEE<kkr1B|ChtSjy0RXa~ojdow=nNs}bm&JM$}srq=zvDFs_P19E_)IpBPT$1
zsimFofL|^!dCck8nryUKEQZ0sc+`yIzf^X`P4>zN62bHh4Xi(CF#j_O@v5Um+k*Dp
zojcaL4b`FnHj?y{r2E3xgRsYBpie!<$5*~?CfWe<m=L=2X}Z;UqXZ#S;<GOEJF!LX
zS%Q>fCEbh_d@(+%kf9VV8^2b%PQau_k6irwwjz?O(hBh$T5P3RQ!Xy^GBUv#$)ZIv
zZ^_C|p{}(|$;61~bCht$N}+`6VAktK4l5H`OYr`aeljn%m}-D{2t*sD4vHJ_Y;LbF
z&8&4oGBXHA&~L22ZlFe*Mw9*<psKS>HtMHfOVGy|J79@n5849Mvk-}1GE~F0;NRim
zuvYX|ZQzX8HU0Z(_AT@RIO8}QBkeYUl;9x!eVEsqqqvUIRjfHg)D2>D`9jWE*I07S
zZY1G~V6}bve<g9X(2wncA%odA%S*lz&EQVp^Qy2b34e`mh(Qi(B7DB+#PNga)6jF}
zueoIAq8A@@*Eudl0^ZSxGuBkutas@*Bz7_#zQ6oMv2~9CAtUMFHeRTE6n!)%h)|%|
zf=u$Djd(dDKG^&LF`)pU<Ag-*=>>JNF`^0%nCq?VD2H~OHXXh7EA+m;%g}PR>DrBM
zsU9RESkG5yU6kf;q@ErGv&Zk38~t|Fc01wzI~m|NR%NuNEY*xe6{q+G&&O{~*hiJ+
z4?fr&@#tVk{XwVyo-Y4db5J8A(B?`_<e!HNVxK;Uc)BpcHu)2Dx;-)T%6~^r>|L|q
zQRL0U+E3r=4<#oiclV7YI3+lCdV5w?#0c^)z_>DhU3*{G0$~MJhyg5EVY)HBDyfC=
zDFFLq-Zf|JjibyUtSqLHNv3kxY#*2UiE@<=EcUJVG;p%%8!AYC!&A^=0sn0D*3`hG
z{=PG68iircc(Ch9b|X_2NLuD7*ro%<9$!7v#Y7jCsA0>&ZdKZC{IQLK&a1T<&u6(D
za7k*E)yi4v3YZ}x#{(ap??M7_7Ao0j=UwaHFGOnq#OwC8AW<iK-{V%%Bydp&CTi$o
z>u5;wN}@afQ^_QAE*&)cd<&ILOMufV<=ykNMR#Oqq6uG|bUr3AL0%ye6@jpUhIu-^
zY4;AY0y5?Fx4rby`oQv^0jg^f(TjD#q4svP_Db4Id><RY5r*X&rt0_{LODXou>#p^
z+xPeXBi2uK-~~P!G21)N>zzU9OEw%%s9JKfvLk_El7j@I%-uB05|u7gH<Vf-UW4W5
zc8||SBaNtSg}5EB`RVV>HRvO%dd;83zcS>Ec@kb6BY?#nvpHlF9_4iZeb}7b!6SX_
zvR;ykwfA28()RV}D~h2SFZ!g@=0F~Sg<~5y<iYsb$JoI+gh;b>1k}X&6sx`lKPPm%
z^!cJp73-dP>(JOlMj(q$TeZRDhd|JSGzJV9!D{U3cNi<AL<Ff3fTFLv{Nj;jOjenS
zftu<wP;T{=a)_ny6wDM>urqD*dDy0moD&XAfG80MUk{ZHftSC)!11boE8>1^KyoAp
zcaWN!QKr5v=FliJ+Bxj27)A;|n(tDvdy$kNC<$=s<-OY93Qt-5=*~qClGh4~%tspH
za<nl8qTrbX%@cf@JI{@=QRM{*)b{(~HPhvDkphAdAxUNptL53ZS5DNAm2SkR`~Mel
z$Y~i;&oEzj_z2@`-d;C!&Dt8dltP8fHEDQdDssw)qD6xiDJ~50+ZWF0kLG}8P@8$z
z|J{im4u{U#|8hTAA4Xm@4(YPM-F@d@aKFzX&X^2cMMmO{hW5z{<#9-q4C>@<rs4(C
zB&hUMfNJ@4{fDa6;j$raI5gDv7`oDXE^9klyQI{(=pXJp8!MFZAT#{{7V^yAfmgE+
zyiIa^c%+vhsU>M_QX@wrHzEiWRQyX$V}%FIHapj4B0c0GFq3C8^j$9URFU@*CE<tg
zn%_B}B%~ROVNouvai_d5lQq8}LrUB#gzM&VlqIWj`bQv+Va9L+%2yLH3BCY`303tB
z7z0XN7X~P;BS-d~87dMWlSqNl7v$~(`yBWZOFV(+0#9+wtNQXkx0a$O!I`hP7cia9
zHjq+prS7aEMEmweKQg=)OqS23Lz4Wc*Gdmr+XzWc>%LVQU*1t3MW{INl^OZi^}G#6
zQf|8R4E6#CL9<_U|M`&}=#<$&2he_LyrhL9Lw$j0liIIRb@ml17RKS)ioS&Y=84I|
z0D$zi?@-z6m$#76O;8^>bh_iDDV<K9>|h-v@`QR<@7{0i?OW%IhxXD>O3#H0tyOc>
z#ijqu?m(8Ft1Af5dTE&OUmkY{IxGyPE^FpujcqndCYBFAf++K*)6j_^Jz0_hqVazW
z)vHYSPO8MUB7-^kTC5Nsn@G$t!hwo4US(kYtyxwRzLYQKh_sTH1uh|@IZ60h!O6m5
z<9G%AKFH5b=ArNlZ1V0V55v(|nHmUt&1N*#4=2@(Y)e8XO{;Jes;3XA(;M9-CoLVw
zTO2Ji!6>c{KffL)j#|5_mu;Jiv;KJ?)J1o2_gX=~udCvAKUvFPO}tq$O~bW(3deZy
zr7aV(k6mH04fFfPzVVhL2xRv<LG^ayjhM^89CWY;A$h3LqkSn<Iiyv@6jW+UV>12;
zo;hW{TgHWw%9*kk>k5KiJnh`^SSwG;qLT{VE(g4E%g|3Z4_Sf;hGNi;&SuQ_Eks&}
zp`VGx1=e-ckmc=>-KGHT_hX)x;Ls?NDF*cC*jp-$vmR=ZRo&qlRamax%HPk#49W(|
z|Acy!l`D5%UxaWx7&nCf82!b{xH&y^vW^m%)&_-ECmrJok@00L<B+>@tcI*Ah#Ex6
zxZA)LM|<+~RA%61tNiv#E&&5fhLw$-wsC@A2vVK8l3BriCO<ompzSlO5J!L1q_%tM
zp#&elO{o67Q~Rx6dNYbZ@zpRLt4er#yjwQn?IZY}cSlmpx0<70<3LD5vfSu>ND0-*
zaiM`iUD%8%E6kA~L*1wO_+$1w#mUKSLpVQOq}_)l(D3k9u})7F6;i&em3TJRhxbzi
zly8&>7R7%@<c{8MOOPy^;uDyj1J;=Q?ZlX%Uove>GT;mlIJdT_6Y)cJ#7$rT-C+5_
zn^f!%RAG0wLL7K_Jo5M9ksUWkU4DZZ^(3gAyx`4jv~e>-d8(Ne;%XBfZCZ908VO_r
z+`Vk?WmybV+6FkA?)@Lr89EI_dVvC?!MuCS_U&7QqOx4DEweNi{CDsO(Vtw<qa%o2
zlGYmW+HaQzVO%70Ye$T=!z_pf0#jhJgHFMv-o7Lx`^swt)qZBCF_g}~&mz4s8-kpd
zZDj*CXu!B+lI@&_<>#qQsPTeilI@f6=36GUQ7AyiL5dW#Xq?bsbShLyTMO^g0ZEpq
zP8g<XlOpHZYq<}Wy+F4pcC8?tYjUKDDwu`N{V`-5g~Q2Z>RZtFLN(`*)rGOVY_b1M
zLUG2riQC*#d`}GOAoBpNt=8KOzH|f_W3Wp)TMmOt;W<-Qz2BhiC8e!Nq#2v(fWKeK
zzB$&!v69K;j7-KrTjR|ZwF#!61Rf7uD^TO?`^ge}%VyzN#fWm0ZAPbQRq+sF8CfB|
zMbq^Qe-(l{1ZY-aTKS^jI<StDUqS_&PtjWylIPx`L>v{hsF!_kaL;l^tE^`L<s?sT
zeO!kkmolAj_MBrxH;x}?HamNKlYw0;GugV`pEEx<B@rTuyF1`tK9ek~Xhy#gkM;o%
zPHEV#hsiyJ@B|+4DDruBK0j9x-f$pzlYQ1dNQFyL%pd{(6nzetRhjglmSZYl_86q(
zd0x&N2rYmC(?20@-1lHcC_06BXy$Up`fCFN4eHB-#CEs+*Nc^}kk11B-IAy_dW`2K
z=((n7k^A-?hr>v&4;K@v9IEHB!e@4EE_SZ&NKl*LJIZe8Jz#X946bBQZfg9w&Q$F{
zB9kHMEuwx??qyYz74oPs!1?@{Gj=i-2ZDYdv;0MBS!sZ{ju$At3+k6c5eOG};eW^5
zuol!7BELeS8~0kAo4R3<l<z3<SV=%>@M72R>XSLi0)Qw&R#Hl79|r4@oC^TMJdMR)
zyeRXM1uzir%;uvEZ4)HsP(sP7e`)XA{VEqd$%Z*;>Cc;+njRvnF9(S`_amYoL+<EF
zIcP06=zFZ0J~o2PDirZ@auhD#U(iXQEEo_r_a^D@l<&QU0;oVqU7&N(K&0GawD>Sc
z^}Y|d#+s{uULb+U$hil;3o6l{0G8BE^56d@I5q@D13<J>7rFlD*}d-0=+dAQS0w#T
zdGZkUeacs~$b3qChy)YTYzYzl+*c?wo@=Tw<9z#@41<TVsp@lKer)r|!CQ4b#67}*
ztMMi(c59vQs4RPW3vhtEuYYmB%Cv}Qw8%8TYW?hx|7R1mVX%(;nMmAnlLe|`#{row
z<T<OFdi(1l3CpMuZ|cga%v9td)CLO^;AA=Vsd5exs7#s!RfRd&mGDqU)JcH>+E-K&
zRX`2hHYlF-T4C?JeNF{!Oy9n8uv)dr{}~VZMVO23rjH)BXGw{0Qcn};X7%yj%L~h7
z<DD`jR&l|>nyh)mg~K<32N~h5H=bG~YZXQY=H7U})zFH)NC8XcwJjjMnsrn*gH+Ph
zj(f!>k(yI|+e~=HJytbEbP+(Ng4$KW??&hvlEC(1$9sCO$g-e-Yu({Ct@!boRZFB^
zC0r@J&{w;Bc$!FY17h{>9>2=GT{2JE9L9tRUY=L3%R<TkGR2$%nzLw{tYH_h#F)b`
zKaGYhM*C>qS%~KwcaJ=HuY4cfg(T=D(jn~8>A6jWA-DktW@Wr8H~N3g2B8j$;eL)}
zvtP>Ml=x-j1Qt2x(2eI;XoIq}7!HEeZCmuNJR6-S9hy#kpsBp*5h=-XN;CTf%z~av
zvwZxV<&c0>LpZyuD|-EfmFQq*0l?kUP$p25a_FULSZ(m*_ZPa=E4mpTK+>QK@UT9Y
z+snvDh33HC8@sqUYV^F!`GLNC^EdR_Tyn5gL@&*Z5xvE&>-0LCWto<ANK+^`ui+-k
zI<CMm<PZCLohjJegzhB<>MTm3CFZ^xR3dANilmJyBt(bN;r?Hy<Plgct_+KCYEWw_
zx=q|S3FYxifa)g79>n&dm-foD^2H(Trz_Dptbvh+NA{R=dt<3cjnE+oefIoZ`fJ^C
zpMz-qyMO{`me76JOCR+}n+c2PX*O!rAQ2V`DwC5ktmtvMfj$VbTjs#9iaP3dWeXXV
z<FCuah6i-!CeciKQBRJb94;BFzehcDe!Fa>*8F~f<F2WGx=A4@3I+VLcGeHY=g*{k
zbLkB{2&0F61?s(qO%rzZ4(>cn5Czjm4YC=fF)@=cLOFEx!DHqt(Xv$tU&IOsyIm3b
z1-tzf5qwRSOoGSqm8_g)Xs{Wu?$Vd+|Mp-m5}Q>H*yrb)rl|QGRY8UZgBruOs#z7@
zp1%<PC+V?8sa!nYI8~ka6TTQ$@b7H<+?TUv;4zrje^Qytf>0M*zwuJFwBSklCwFkD
zn%Dm|bVio6G0epK<L^EBhr*qO+&Bj42iLlX^Ae7r1T6z%2E(8SCQoN?nh2yM$h|Z7
ze#V*v<kv9)=dFBER5fC3c!$^zeJ~9!+~=>4s~~Fu7@RImIPmU<tgDDVu+nNbt~eo%
zY(ZB%8zM;_`+6B`REt<Awh+SC-DT9Nq*R0yF(O!`adKn)*+aD>wbu^ll&w8*@$bC{
zbkyT+&q`{2lBi^GM@cENv|{c8$2ivqcm6JlndNw>;>LW=^aTqXm$+^%(mA+C_oMNH
z0)`)>$JdW<@5FGze%SZe3#|Qf^2zo+xH0yV+#BV~F+-#9*l&By*I48;s4WH}1g62H
zOuduiJFMmqt{<-DfccdFcRhc_Rl=8N#Io4;8XjKnkIyEMMNB>u8~ortWu4rd?QgjY
z*8eSaOktWi9z&N8j=SI~YsgqoA<l4GHRt19#wA%NPrnLrV}Rqdt1jb{6HkIff*d|h
z5g591cA;AY6|F;`+%9~N-*Su4+ZdFHoiLJ9_X;JtK;oOKuyeCiS`X)-WRg6j$X@V1
zwNKNSsKS#NaYvYq=<oc5YgB-aR319jZeKAgNSrS-PjjoV$B|1C^u?vpB0Q;o6$apN
z@4!I+P#bb#!hj~`^zM2^jU)z>dg^4LcwepCzFoCZPi7LQLw;OrxXy@%tcN)`It9yC
z4ewGxW(O#E=PoH{We$hP(rro6A|?E%ipsZR;{W%@95x<EQi=bRh7!M$YUXMA1|>ai
zQo6kKx?}>2G^sP}#$pB<(FntUGcUD$i)Du*^<e;wp+=PJ46?Kcp=Sc8m`v#i%AcZH
ziZCSxbcyuxY6(hU(UG6NER+1j$ltdUGehQ%0yTLDKTltp7)A_TD;Af-cLebF?Mx=2
zkaIQUX@l+_$vCP@C`t<G@V`a7E}D?`q2?ngk_9v-%-^^S^~A!#Pmk1!j$1@Qq$pM#
zetxxbU)J~+X|abi!31M^4K6M-?0zHjqk4d@<2!b`${f)VFz~fEuPN*n(isetke9UW
zi~3pkk#>ANac}W84h+YEZjDVNat{XRQOf%cNIYhV|Hk*(W1@Up_bRb-m;=vo$7u1t
zzWHZXA<ut_16HaLqoYxjhk^ziV*Sl|>7uqEWUwH=uRQa8i632zhN+74fI-4u97{Jv
zUDt9N1=?=YikBw{)FRR6nE*=kc5!vSzpVm-D{(6aEGH^$Uy#pznV$7?z-#I{_b`*~
zrHx0VNY5{P0lx1(`_dc(6B&w51!9HAK<scDF%PL1$~KsaZLI;<0@3MvLUR0y!)kHo
z0b>Gq<4BmG{j=!YI+(h?Jit+kI^nR*ZMz*G_dY>w%y4x+pXx6L4>pG`>ne%8@0DMD
z<b+3KKu#ty_%NK;Joy`IY+D18F)|P*?aK}3ew|5ZA)E|;v!5~Noia24o$M$F+y&;r
z&R2bg^NAR848Wxx6YV6tAP1>wIw<(`gPS?&w>y!jW5DHp`Y+@)N$8b0xE(%$)Buk=
z$a&!JJSF6GL!rIDv2YD+fV6L);xV2hp5;`?44A%B$l+KHN&OSWQ#{YrdcwRrznXU<
zCOAylWn1g7{mf{+#zm`zZb4HTns){}H6o~$@w@>S;~iv@yX}AM&ocPZzyTXNUN}>2
zoK8XZmhpQg*?au+)#f%sLa>08M{)W3Xxo9I{QbrRdw-}xoGgf4T{U)`xeR$_+hRoC
zR;sJ#7;X5K*fH(30!KDC`!A8lQ9?`kQ{ggve!4qgMx%`kep{4@9eGonxNE^Yq*N)V
zc#aHJ#_C5dO2o6`)(UYbDQlP6SvOy4v6e(lL3=hlz@HW_q-{mp9d(Qsh$&?myPVfW
z&%!+4)lV7Au>PVBbb7l%-bB2c*GZO$<R|Ho<nh*wEBcCb^lk_Q7;dgL5xX@}Oc@1@
zw!OKB4Z<A$lAQ>6h*_ePcV>miZ=i&-*h3?q*-3If=<-4cT&=?u+}j}SBO)~n<!<6?
z>PYt^Odg4cq)ptVFg??gDTI(pFv=id?c+VRFR!7yySqaC(mBRS6fRQ^FymJ)A^wGy
zKShU7j#iK-vb;PpX5H0;L<Pd`whU*#_YY;T(cx&3)5jf;Peq(TwI)#gns3t<_~e|E
z1$xp-l4kiza<v&oUn_r%hkx~~y7v6}3=}_o1t#ywo79R$u0B(U3(k<hVFAhkkBiZE
zK$kf6({|^+e3TJ*08otGab?3;KkW`m1!MvB&E3=DV~mWgk_AZ+T9*S(aYCuCy5l_k
zm2CIj`Bpp!B$$_herS-loY8HJgaIJL`~`V>54O?JpM0R!ikh>gr|2D8!AOi~L8)=`
zhF4rKVkh+>zrXJzFRm;E392&K#6o`P^Q-(<NEEQEG_l*GFIB;*yNuAj=|cly&Et-r
zV#3g~0TA$H7NtF+$>4ZOhp;s1g9{~HEl>6&XsoLKreyzAq@KVHrp{rvk%mvn2=YKT
z%9VRuoXvhN<LCl~p!55Jv#NfA2j8nD=`SjznC!9(=7!6V8bB2@-+5AMuME+Ufa?P#
zmiyW7zHQRO0s2ThPuhr1QX|{3OW-C?Bh&-#4mIj$ZmW8@G5yhoQ`ZUvqr%99!SgaY
z!ta^*`M&lkZ~aFIZx9C*;;xlRbj^=iX{Dgl@UDS0w(H<MssJ)Rlj#%73UO6HoBTGu
zH;K?IsStHKhqsW&N0-W83dhiDy<OOQ20fV_EefjMpu#)Co;8(j3hg5?2<!IMk>_0;
zZ8?q}bcQ@%f!|*}C97l)0#f{9;LMLqVxU<s73j4Q@}V6I8NPSW<ud}9b;wlJlGdq%
ze(gSp5eqYZIMRT`h`UZuF#dAmTw$^t@hC=vCbb>$I~Ql6ESUsoypdx5D%2T`hYtnM
zr0Z5pt}*AY1<c8|z-*{*u<%b3Rxdn_*j-o{i!Ca7W-%Bg>%hlgn&95`{Vz*N#ZcrX
z>6_{aLS*+=cW$wKoUFJ9nEL7GjoOZO%qAq<6Es`Xhs=|)BJXR6j)ntQu368&LUgRp
z8Z8wjFWh|?DO|LWCGEW;bqCZOuy@a%yD3qu5MTtAzs%fLPCyjhROVg)&2-(I1ah7M
zvaT%<oRK{5k0ftK8`lI(w#AopM*4ZObd#i`=K22Oe>jtAj*}U<1^B%qx%0Uc)Z<;!
zZ7<b6xA*gE!Mp{&<C31K(42!4o*AVW=s^T1m^^d|J&~DDi$b3Y&CKZ5Ytn<`qi-Qr
zIzv5^?9$h!nZuUhG&nTuFxirR&DjN&2e2WJ7?g#5{E+WCS%(AwI~58i(?s)q^`-s7
zBpW1wPPP4G;}30IvRr&sXvUK2e=s^0nQd9Mr<tdb3IPJ7dCIan3*~)z2!mme`C?|3
zLb}|)uMk!f1{!JqK**@Qc^Z0Fl7FcXDXS{z-<!DfB<L`@%jTlnV8!1^$TvZN-0xBD
z*cTE@1Vtn$3EIYPI4NH#W93V*QU<9Yo|Y;L7XIiLP>WaKOA@#!DRaM(&px-IvTS8I
zPnM254i{u^<#d;gGGgDxO!8AVoN}9;97qtK0Nb4P%%?QFE|r49Ngx^OC~_Z#3Z-?l
zQtS$vU>2?xyS%AbhXmvy3=iye2{)ou4(1VSAm0^;m{%QhR0AIzm=Ju)P>g*w1s|L$
z3DrY}$~5LQh6CB#D{R8|#+w+;7x4yM6ooTHZ6J5+OVozz>OkE}%Eq*Tm;d?6-~DV`
z6Zk@>1;AO15skU`&;MPUgh&Jc4(4Y)I<|d3x=K(;tp05C{qJ+EW)l2${4eMl9;bDu
z(G!t!)PYQ**w&@2f|`JyfwcX#`TOGCJ`wXd1<<C+k^Au7FB*u_0;9gkE1jC$GQ)l&
zlrc5~IB@or%T%-bO?OOaJp2&MDtQsKRa}5BnofoI&eNg%7ouDOg_aC7v3ZG^u7C2I
znQ*#$mG~jA6b88*Z+VbIEipyS?#WP~{njYxnkN-G;^Yt(ePhnx%+l>-Eo7<z6SVf?
zUj1$$<14TMu+%Fv^7f-5@$+)Ps=C60vYKd>#BewP#MW-<?Lo;=3OA8AO$RzYl&wt@
zvQW?gKnP!6XroB)vp^L=5^hjP!hHU-Ol87KxIzC7##{BDl`TkM+`zn4XK&>t0Ts*7
zG#?MEC&T2KGVyZtS@s7|xQ#*Al9^Re6(N0ybcI8oJKM)mGbk;np{Zn4N0EJD>6g$*
zl)T1Op!XDibQ&cAQN`&H`DD&b49Cx0Au=)yz#d#jKHeD`OC_e4@gzXs=hStdvxq6U
z-xz>gdn%MqTc3`yER1NHxSAt!&CIh~Ep6&1xfs}Vz9@UwHj%MhX%s4vkrWcMKKtLw
za7Jz{;CGp6)?>XLOreak58^n7yqc}!tRTGyLLAC#0ZP`lJp94rFbWp1sT{I-p4BX7
z!O_cv3Bv)yyGcp&>dm=|wzmvCt%R|I7b4`*Tdly`9q%(MtIS7E4+GL53Sl3e*`lZO
zkX>Wk9iktwT}AC{LeYa0<m~i5k&hj;KaFe-P#CSP3O@F}|0+gad>xdwJ2M}wO0bnV
z+3E0(gHca!B?-}&SZu`0G%eQ%TpSLgp94~rD2ViAL(5sC`Gkd)V0|_xr?{3cMD8t6
z>C98Me=VZ07NS^*49MGHS$uctW}*%3LV>=HKzr*N?;LOLVgk-#SM*YiG@ot%`94Kf
zdI=0W^DkYCJLYljq3VfLP{%it4cy&U5@<j+0IoWZEbSeZ61JJj5vp?!q$YS~gHzVi
z2DMtAYX#x<c9e#pv(g4rQuKD81H(Mk-hEklXDu@Hl49S-EcX}6TBul%4T)H}p<j)z
zd2B;<9W<aC)D*hdq!S~Gd(9z{_4q=;n;S~*h+hXRh+RfKDzPk?T89Qcd4aXmLf%Ca
zw7CLc=^n}3^z;6madcMIF(O-xQG2g(L`Ke+ixwTfTBN+MG96tS4E&9ciS}ihZfP>Q
zE%4;loBT9mUaKbbI32Q%oEss=whK_chYS(Mz3=yqH+kqIX02L<c)AWh;KhzPGG5rn
zUhMh#;uICfiM|>VWRC>Rzo|c|aj!Z{QqTv(v<<hPzHM)|3?caix|^HhxYLa~6eQz+
zWs=K>QonaHNHSswOd%jg(5IzqY)~RJQW9vS+x3s^Dj(xTO)=B>UY=BA-k*o)U!Eq0
zM7G|4l)Btz_a9k*d8~rjWb6}RW}jyqyz=SJpUZjifAYR%Klv8$ZOfWXPtSFoyulkw
z=pi$E++bi#Rgk@IT^ucc?6tjrXO&Pku16OV4E4hT;}^{&JrqjNA>Ulu$E3@p69}GU
z1V&qH;qOW;<cs1!xN)<s+Y>*CdLKYV?o4WxRXJ8Uc$YA>lAeX(f^*B4AT&^d&hy`$
z%ibwSS%?6vAovc~@fw!E%gq@HM^v;(gWHtaW^s%d4i3naxUITUJMFxTWuOU1u3=M`
z(tMgsa0M72d*ypCzHspxfWbV~xz3Y>I1pT;4ExWQWh1kIBNt<v;(x0BvTQzCHRMFp
zO;d5-&-zGd=|_tU%+q-CtdZ$Rx9zRMzLc-uS@2&AvVOP~bbUH{_B-x{auGOWYHz;x
zjrs8v@lms(+Iz{TPkHB2u|FFe)?K{qG(R<E<gi2IBJ&Tt?WU<cC=7O7D{u;(?Yu<)
zJ99e{KJG;}Io9IU4XgJ064@UiU+!}}d|(^m{y`R%dH>!&!H-%Atpm9Tby3Khdovw>
zC0r_8AHH98j&H);>x3PWc=gLp-sM%@QXnLhp(_YHoL0X&tG((mdLs!w!zqqc!S+On
z6PyHbPA|(i<FGff=%wJtX|vwZ{`PT0$7~Kot^1tknw&*~M0aK2jB{qY!?r;u4`!Uc
z?F2ssFthtUsjVB9XCo*Thnzwk<>gO99a$+bSwc$Pb4DfMfUIZ-?A4g&%d!vR=Q8LM
z#$Nee%G|rZED@bEBq0Z5t2gBMtR)mI1#DABu~mUGor<g}85CuUcYUG<eS86u;5w*K
zVmDP`!DJ*MlnU{}lxuR!OA61)q8jk`J;bW*1!vWW|6`OggYh^@t?9TINwQJW65jI_
zVv8_C_kj-s+b6S0RlVskjGd*g72S+b){yzOmtwIZ@u5|VC0YYa%~&ohkqfl?h};l*
z5Q~x8(eu4xJB<Jb=&>wH8g?S-UcbyV4M9Fho!1j5bY4yz7|;V5oOklMqb1G#d#>Vp
zD1%}3P)STa&nebQ)|3HCoRd7Zp<>fl{JSis9g7h;tLpVedi8l(p~JSX5bt2|Jcs<}
zBKn=A6W_dtg`xf_D~K(@AkXr2>=Iuiv%^WoK9~(IXmg(`#Sy84nQ%H(|LazPveI}e
zpsg9sVFSmbWce68h(%v7I2BfLRu^e#Ht;su-Y0C*R7Q{>H1ShUv`lZGqKY<i8$e2e
zmr>lTRN~r5F!}RNK1m|+bV5cX*Vl`0Djw?}LB^B<&egf%@fDGwvN}Zkr~_VoO~-F{
z-B}daK&i2>$|vnyzfHuJOAyS_>F(pSSw=Nslcdz?kj@p-MO%`tBBT$@iEnBBFIJYm
zs3wIEbyylkYX{zlej(H^ZkkE%uFAT+%Sw^(<k_(-yzyl1Jy&J`VIwIwS?oNo>QC+Y
zj<On5{2vzGQjc?t{Uv-0f_wm@H!so&u&PN5B0{q-aLZE+A6*b|0toBRfKsoPm@Uh$
zrJ=;xjtX%==0bTt!z{=Z#O(KfB26E?sPL!HR=HNZdM62m1|5)B;dNdJQ^^dRWMgYH
zd$ljAdwZQZCxdFCOGlfI%hvHCJK|u{-}Ew9Tl$?k)_hb}4rq0XCuiliaa~u@adf66
zT|s!P_Ux1+dA|}TsHr3H6HE)cIXhPgRl-C7i^tFZ4(F2r@diF1hj;ZxVxK3kA$AU*
zo=Mhuiw&oCc%wgQWHN>`tRAYp!esm^rA%@suVf&}(T})*l&dUm%V&PWe}RlZB5jWy
zhMZOYNS_9?gLr2;&@76*PYO+YQJ0Z!5E<6F^P5wb5&y&ogYDFO@p5HSSR$dgDUcHR
zOe<YJGKM@b+|2>|Xs*UcSv2Ug1Su}<*L-q0KC0#6zYJYJux-55(PlTAth)^mG3~n6
znfCU|p^cP)t3!R)Z5c<GAQQ-dBoj5^`k6KZ=$RpGGTbb2HzmCr7p&OhHSXe%q#WQ?
zUu(A$J0nC2$nmH<$qV%GTQ_(pQ&uSt0BaZf+1>WKQaPdwh)rjaMlG^G{5$BWj$%NV
z#KC+~ertIr6Ukf@xXZC)TWVgtA~uQ&fmTOJcxIIs5f3xw0!7<AOd}(E#XfX-+0iV<
zgHNwC60kZ%&ZWNV)wVgl|IoKQ=3}IP0n^^Ge)LRtDzkPg`Vx5v(~b&jHdLbiJ`89A
z5zw&L=vyAj%>tgPaQK2x#9&eq>c-#(c}JDI{;O8}k60-Kf+||kLs6|G@=7HQ2cehw
zUrU-)rET1#h!OPPiE(ng^Lhaa1ECI@ZfxSPU33^3MoAfTqM0!I@bLH^q>lmFO!nrL
zHq$FN-!Px(yJQ6?qi0Z(xoIgnEU1E>l_sz_5~ZetEI#1UF+YV#!3Rzfk}sLcK@Y4r
zr(hv#699;%Z8_8QwU(RA*$ah6NseC}tqgKylc%6<eg!KdNh<;cR9vX?=<jqBCM3#g
zRJjl-9azP0{8#@$CprW!)FLIdb^73Wro?^DgHB*C$MaX>Q4`%aC9b<Cq$KK!yshK)
zkwXPcA0~fyTkB>`BI77Q1uwoO)Ar>kA(JgBQBI{+%G(-p9MQg+0IX!5U`Re`J`?Ry
z0@QzXGwxL;8lx)$mC@d!b8p`!<srjEgNnmT7QQ?CQT)e+DzPL##m7G6o2W-dA@p>J
zv}xxT|CH67NwPwk&IdL(RH5G&L7HCPDeIfjOPSmCKj3Xv<o$Kd_2UWjxxj0-&hy+=
zdwUQ)8X#GLYR-C>@}A}BP`5xlW_>oMuIqgwVM54oG<DfgS51B(btc)7D@UUo+sDyF
zJV}9a2itmXyWd#@B9j9I$0YHM1#)CMvh`#@MXMAnul?4E1e){~>er4`9!@slrz5|P
z3_aIl%q_?K^ilDjq_jCJ#ZtXu2nBKyPGSESr~K^3qv$T+%20%&ZEvsOcMgdIjI<a!
z)mi^cAnP7f1n~X0Q%7;TsW6SGwhXHgQ**fVi1Js>J@LC_88E;Flf<t^j?-%pm`W<|
z4eMRMa;e&PmJizBw%1(x?A)k7YTRZMN)-15arlF=)Z5OJh|Qr)jCtEbIi*4q+3Z2k
zQChDx-1k;&M?VH&=OdN+cI>bJ5iU27-n{1}L|;%dLchM%0dM~G@CKTrv$<^70Ql$E
zIfwewWc?ceCDrpeU-nZ2ogNN%(%I~MAL)YMOya0u#IECaR;rax1t`4udg@^b{eTP|
z*GVg=GfCEnICB6pq-l93CrfV0O1qL=+2n>1U)ra;vNT~e7;%GBJASr2Me+uaUk=6J
zF+w>;mhOUs2_Gt{swtK6dI8@XvOj+tyUa(Zj3Yqb569B}eZjenOa%@TYR|e}it;Rw
z@%w-j>J!|cqGE?YDG8OM#F8DZ*V^HNwl@<Lm*9fAUHe*8D?tXQ?tLPyV4*A}0b@Zp
zrv?fKj_UJKOh$)`T9D0iw2RI}<Z|4uU&KsF76hnfqZ|PpaQR!)<Zc%jqgxMn@nD(Z
z{={C{a8Eka5WMI*_LMpsX^^o-K7Gr$>Z&C|of-I9(GOA<2HBUByH)lrdM^9}nQSk3
zW!%f6=3DXT_n}5BGxzJQAKSK}ZBqnnXeHIv^5Dn0Xzf7;_%3#ABmT|{t(^gvIZ7?e
zr->F19zL|W`iN73$p?G>=%;pz!3Gj?DHw1AKYIF?tz@AMmH>h7d8#viie#E#Y9{$r
zn~_FYs+NijYlQneSN-7AS<y%pFkoK5kLjfrrR_)+P(U9)y{j(d^pepCQ4FkBU_#u^
z#`QCZ+W-JxPGR>W{&Z=$M9}tx2gVOwDrpKwx~vX5&vY<cKdP1fI^Wl417P&O0b_G~
z@DhGoHmQvIU09l4rj-6MX&*|Bus`%_f7WSW4U~jeAuGcM-tB<1qi${;(gf!1VzKl1
zy_$PE%qk=!Yyg+u(UQwDoX|mJUl51S$ErLnkRqUhaQWs9?GBaA4cq|qtp)ktF_TVX
zbk2Ph;tgZ+l%2kF{~_Eu2Do)FHpE-8O16>VEN+`s$B9dcDreE+!=VUMMb&QjjPatW
z^n{QT8Ip0kBAQ({yFf;<bb$<G<ZIuKhMp(_0>B{oG?QsRCpaFZZ3;j|#rZCGJB=_#
zF3^(gno>5&h1uOlA4X@f1)qmf%IZpxeI*^^C3Wn6-6?pm3hAv)4h$^DzAh?%q(Kjf
zWgrY-^Jw#FG22b}5*S#W$c(PJA%;ZUBANany36(&9%1Znjq1OkbyUg&gh59NZtP&y
zh3*099S5qgR?nv4XnWGc+Hpk(2D_x2aV^c)8>LHtRu}$rpiWb61NvUzOuKn@rM~%p
zU4p`N1rJ}i=f)2UQS%=<@H)L(jNXoQHlsQbsAWB5<e{E(Nmg%f0eqoRaT--cMpUkb
z3fh!g84pAWC>4bPyMJJ>($&Baq&>*cvbwjBzwEIe${f-^_L5)UuMs6_x-_A-CT?IR
z9yM`&j;RuE4-<e$$MGp)Zrdj3p#r&P=4q#BrE%KFDQL6LR)_~$vD5Zt%1DGT=v5O|
z<Uez@Xe!#o<xqKQ=^uSaFi4%0D!hUI{Wac$i^_4fxEyf&MSTYb!|TDhhRQHWrE0zJ
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

literal 0
HcmV?d00001

diff --git a/tests/f_multithread_ok/name b/tests/f_multithread_ok/name
new file mode 100644
index 00000000..45dd561d
--- /dev/null
+++ b/tests/f_multithread_ok/name
@@ -0,0 +1 @@
+multiple threads checking on clean fs
diff --git a/tests/f_multithread_ok/script b/tests/f_multithread_ok/script
new file mode 100644
index 00000000..7334cde6
--- /dev/null
+++ b/tests/f_multithread_ok/script
@@ -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
--
2.37.3


2022-11-07 12:30:56

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 63/72] e2fsck: misc cleanups for pfsck

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.

Signed-off-by: Andreas Dilger <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/e2fsck.8.in | 8 +++++++-
e2fsck/e2fsck.h | 13 +++++--------
e2fsck/pass1.c | 20 +++++++++++---------
e2fsck/unix.c | 4 ++--
4 files changed, 25 insertions(+), 20 deletions(-)

diff --git a/e2fsck/e2fsck.8.in b/e2fsck/e2fsck.8.in
index dc6a5856..37dc8936 100644
--- a/e2fsck/e2fsck.8.in
+++ b/e2fsck/e2fsck.8.in
@@ -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
diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 2dd7ba27..33866316 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -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);
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index d745699d..8a6cdd8f 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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
diff --git a/e2fsck/unix.c b/e2fsck/unix.c
index dfa3f897..461ab8cb 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -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':
--
2.37.3


2022-11-07 12:31:08

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 66/72] e2fsck: merge casefolded dir lists after thread finish

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.

Signed-off-by: Wang Shilong <[email protected]>
Signed-off-by: Li Dongyang <[email protected]>
Tested-by: Maloo <[email protected]>
Reviewed-by: Andreas Dilger <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 8a6cdd8f..7345c96d 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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,
--
2.37.3


2022-11-07 12:31:15

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 65/72] e2fsck: Annotating fields in e2fsck_struct

From: Saranya Muruganandam <[email protected]>

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

Signed-off-by: Saranya Muruganandam <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/e2fsck.h | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 33866316..1e82b048 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -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;
--
2.37.3


2022-11-07 12:31:21

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 70/72] e2fsck: Fix and simplify update_mmp in case of pfsck

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.

Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 97 ++++++++++++++++++++++++++++++++------------------
1 file changed, 63 insertions(+), 34 deletions(-)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 4168a45d..2ff83fcb 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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
--
2.37.3


2022-11-07 12:31:23

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 69/72] e2fsck: Fix double free of inodes_to_process

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

Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 1 -
1 file changed, 1 deletion(-)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index c934b021..4168a45d 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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);
--
2.37.3


2022-11-07 12:31:29

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 72/72] tests/f_multithread: Fix f_multithread related tests

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.

Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
tests/f_multithread_ok/expect.1 | 8 ++++++++
tests/f_multithread_ok/script | 17 -----------------
2 files changed, 8 insertions(+), 17 deletions(-)

diff --git a/tests/f_multithread_ok/expect.1 b/tests/f_multithread_ok/expect.1
index 4742f408..cecc11db 100644
--- a/tests/f_multithread_ok/expect.1
+++ b/tests/f_multithread_ok/expect.1
@@ -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
diff --git a/tests/f_multithread_ok/script b/tests/f_multithread_ok/script
index 7334cde6..f14034cf 100644
--- a/tests/f_multithread_ok/script
+++ b/tests/f_multithread_ok/script
@@ -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
--
2.37.3


2022-11-07 12:31:30

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 71/72] e2fsck: Make threads call log_out after pthread_join

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.

Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 2ff83fcb..90adc419 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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++;
--
2.37.3


2022-11-07 12:31:34

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 68/72] e2fsck: Fix io->align assert check

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.

Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/pass1.c | 1 -
1 file changed, 1 deletion(-)

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index e7dc017c..c934b021 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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;
--
2.37.3


2022-11-07 12:31:34

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 67/72] sec: support encrypted files handling in pfsck mode

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.

Signed-off-by: Sebastien Buisson <[email protected]>
Tested-by: Maloo <[email protected]>
Reviewed-by: Li Dongyang <[email protected]>
Reviewed-by: Andreas Dilger <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/e2fsck.h | 2 +
e2fsck/encrypted_files.c | 139 +++++++++++++++++++++++++++++++++++++++
e2fsck/pass1.c | 26 +++++++-
3 files changed, 164 insertions(+), 3 deletions(-)

diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 1e82b048..e4fb782a 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -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);
diff --git a/e2fsck/encrypted_files.c b/e2fsck/encrypted_files.c
index 16be2d6d..53e03a62 100644
--- a/e2fsck/encrypted_files.c
+++ b/e2fsck/encrypted_files.c
@@ -456,3 +456,142 @@ void destroy_encrypted_file_info(e2fsck_t ctx)
ctx->encrypted_files = NULL;
}
}
+
+/**
+ * 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);
+ 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;
+}
+
+/*
+ * 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) {
+ /* Should never get here */
+ fatal_error(ctx, "Encrypted inodes processed out of order");
+ }
+
+ rc = ext2fs_get_array(src->next_policy_id, sizeof(__u32),
+ &policy_trans);
+ if (rc)
+ return rc;
+
+ /* First, deal with the encryption policy => ID map.
+ * Compare encryption policies in src with policies already recorded
+ * in dest. It can be similar policies, but recorded with a different
+ * id, so policy_trans array converts policy ids in src to ids in dest.
+ * This loop examines each policy in src->policies rb tree, updates
+ * policy_trans, and removes the entry from src, so that src->policies
+ * rb tree is cleaned up at the end of the loop.
+ */
+ 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) {
+ /* 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;
+ 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;
+ }
+ ext2fs_rb_erase(&entry->node, src_policies);
+ ext2fs_free_mem(&entry);
+ }
+
+ /* 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);
+ 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;
+ }
+ /* 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];
+ }
+
+out_merge:
+ ext2fs_free_mem(&policy_trans);
+ return rc;
+}
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 7345c96d..e7dc017c 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -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) {
--
2.37.3


2022-11-07 12:32:41

by Ritesh Harjani

[permalink] [raw]
Subject: [RFCv1 64/72] e2fsck: propagate number of threads

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.

Signed-off-by: Saranya Muruganandam <[email protected]>
Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
---
e2fsck/unix.c | 3 +++
1 file changed, 3 insertions(+)

diff --git a/e2fsck/unix.c b/e2fsck/unix.c
index 461ab8cb..fb0df85a 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -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) {
--
2.37.3


2022-11-07 19:36:03

by Eric Biggers

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

On Mon, Nov 07, 2022 at 05:51:55PM +0530, Ritesh Harjani (IBM) wrote:

> sec: support encrypted files handling in pfsck mode
>

"sec:" => "e2fsck:".

> +/**
> + * 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);
> + 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.

> +/*
> + * 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) {

> + /* 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
*/

> + * Compare encryption policies in src with policies already recorded
> + * in dest. It can be similar policies, but recorded with a different

"similar" => "the same"

> + 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

> + /* 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?

> + 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?

> + /* 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);
> + 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.

> + /* 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?

> +
> +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:".

> diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
> index 7345c96d..e7dc017c 100644
> --- a/e2fsck/pass1.c
> +++ b/e2fsck/pass1.c
> @@ -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

2022-11-17 16:07:36

by Theodore Ts'o

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

On Mon, Nov 07, 2022 at 05:50:49PM +0530, Ritesh Harjani (IBM) wrote:
> f_crashdisk test failed with UNIX_IO_FORCE_BOUNCE=yes due to unbalanced
> mutex unlock in below path.
>
> This patch fixes it.
>
> Signed-off-by: Ritesh Harjani (IBM) <[email protected]>

This has been fixed in upstream (for a while, actually); what commit
is your patches based upon?

- Ted

2022-11-17 19:01:49

by Ritesh Harjani

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

On 22/11/17 11:02AM, Theodore Ts'o wrote:
> On Mon, Nov 07, 2022 at 05:50:49PM +0530, Ritesh Harjani (IBM) wrote:
> > f_crashdisk test failed with UNIX_IO_FORCE_BOUNCE=yes due to unbalanced
> > mutex unlock in below path.
> >
> > This patch fixes it.
> >
> > Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
>
> 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

> 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

2022-11-18 10:44:27

by Andreas Dilger

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

On Nov 7, 2022, at 06:22, Ritesh Harjani (IBM) <[email protected]> wrote:
>
> f_crashdisk test failed with UNIX_IO_FORCE_BOUNCE=yes due to unbalanced
> mutex unlock in below path.
>
> This patch fixes it.
>
> Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
> ---
> lib/ext2fs/unix_io.c | 1 -
> 1 file changed, 1 deletion(-)
>
> diff --git a/lib/ext2fs/unix_io.c b/lib/ext2fs/unix_io.c
> index e53db333..5b894826 100644
> --- a/lib/ext2fs/unix_io.c
> +++ b/lib/ext2fs/unix_io.c
> @@ -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


> actual = really_read;
> buf -= really_read;
> size += really_read;
> --
> 2.37.3
>

2022-11-18 10:46:20

by Andreas Dilger

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

On Nov 7, 2022, at 06:22, Ritesh Harjani (IBM) <[email protected]> wrote:
>
> Just a quick common helper for bits size calculation.
>
> Signed-off-by: Ritesh Harjani (IBM) <[email protected]>

Reviewed-by: Andreas Dilger <[email protected]>

> ---
> lib/ext2fs/blkmap64_ba.c | 20 +++++++++++++-------
> 1 file changed, 13 insertions(+), 7 deletions(-)
>
> diff --git a/lib/ext2fs/blkmap64_ba.c b/lib/ext2fs/blkmap64_ba.c
> index 5d8f1548..4e7007f0 100644
> --- a/lib/ext2fs/blkmap64_ba.c
> +++ b/lib/ext2fs/blkmap64_ba.c
> @@ -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
> --
> 2.37.3

2022-11-18 11:39:03

by Ritesh Harjani

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

On 22/11/18 04:34AM, Andreas Dilger wrote:
> On Nov 7, 2022, at 06:22, Ritesh Harjani (IBM) <[email protected]> wrote:
> >
> > f_crashdisk test failed with UNIX_IO_FORCE_BOUNCE=yes due to unbalanced
> > mutex unlock in below path.
> >
> > This patch fixes it.
> >
> > Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
> > ---
> > lib/ext2fs/unix_io.c | 1 -
> > 1 file changed, 1 deletion(-)
> >
> > diff --git a/lib/ext2fs/unix_io.c b/lib/ext2fs/unix_io.c
> > index e53db333..5b894826 100644
> > --- a/lib/ext2fs/unix_io.c
> > +++ b/lib/ext2fs/unix_io.c
> > @@ -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

2022-11-18 13:24:49

by Andreas Dilger

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

On Nov 18, 2022, at 05:37, Ritesh Harjani (IBM) <[email protected]> wrote:
>
> On 22/11/18 04:34AM, Andreas Dilger wrote:
>>> On Nov 7, 2022, at 06:22, Ritesh Harjani (IBM) <[email protected]> wrote:
>>>
>>> f_crashdisk test failed with UNIX_IO_FORCE_BOUNCE=yes due to unbalanced
>>> mutex unlock in below path.
>>>
>>> This patch fixes it.
>>>
>>> Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
>>> ---
>>> lib/ext2fs/unix_io.c | 1 -
>>> 1 file changed, 1 deletion(-)
>>>
>>> diff --git a/lib/ext2fs/unix_io.c b/lib/ext2fs/unix_io.c
>>> index e53db333..5b894826 100644
>>> --- a/lib/ext2fs/unix_io.c
>>> +++ b/lib/ext2fs/unix_io.c
>>> @@ -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.

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

2022-11-18 13:31:40

by Andreas Dilger

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

On Nov 7, 2022, at 06:22, Ritesh Harjani (IBM) <[email protected]> wrote:
>
> badblocks_flags is not used anywhere. So just remove it.
>
> Signed-off-by: Ritesh Harjani (IBM) <[email protected]>

Reviewed-by: Andreas Dilger <[email protected]>

> ---
> lib/ext2fs/badblocks.c | 6 +-----
> lib/ext2fs/ext2fsP.h | 1 -
> 2 files changed, 1 insertion(+), 6 deletions(-)
>
> diff --git a/lib/ext2fs/badblocks.c b/lib/ext2fs/badblocks.c
> index a306bc06..345168e0 100644
> --- a/lib/ext2fs/badblocks.c
> +++ b/lib/ext2fs/badblocks.c
> @@ -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,
> diff --git a/lib/ext2fs/ext2fsP.h b/lib/ext2fs/ext2fsP.h
> index a20a0502..d2045af8 100644
> --- a/lib/ext2fs/ext2fsP.h
> +++ b/lib/ext2fs/ext2fsP.h
> @@ -34,7 +34,6 @@ struct ext2_struct_u32_list {
> int num;
> int size;
> __u32 *list;
> - int badblocks_flags;
> };
>
> struct ext2_struct_u32_iterate {
> --
> 2.37.3
>

2022-11-18 13:33:18

by Andreas Dilger

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

On Nov 7, 2022, at 06:22, Ritesh Harjani (IBM) <[email protected]> wrote:
>
> From: Wang Shilong <[email protected]>
>
> Add badblocks merge logic
>
> Signed-off-by: Wang Shilong <[email protected]>
> Signed-off-by: Ritesh Harjani (IBM) <[email protected]>

Reviewed-by: Andreas Dilger <[email protected]>

> ---
> lib/ext2fs/badblocks.c | 75 ++++++++++++++++++++++++++++++++++++++++++
> lib/ext2fs/ext2fs.h | 2 ++
> 2 files changed, 77 insertions(+)
>
> diff --git a/lib/ext2fs/badblocks.c b/lib/ext2fs/badblocks.c
> index 345168e0..36794e69 100644
> --- a/lib/ext2fs/badblocks.c
> +++ b/lib/ext2fs/badblocks.c
> @@ -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.
> *
> diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
> index 9cc994b1..18dddc2c 100644
> --- a/lib/ext2fs/ext2fs.h
> +++ b/lib/ext2fs/ext2fs.h
> @@ -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);
> --
> 2.37.3
>

2022-11-18 13:43:56

by Andreas Dilger

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

On Nov 7, 2022, at 06:22, Ritesh Harjani (IBM) <[email protected]> wrote:
>
> 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.
>
> Signed-off-by: Li Xi <[email protected]>
> Signed-off-by: Ritesh Harjani (IBM) <[email protected]>

Reviewed-by: Andreas Dilger <[email protected]>

> ---
> lib/ext2fs/dblist.c | 36 ++++++++++++++++++++++++++++++++++++
> lib/ext2fs/ext2fs.h | 1 +
> 2 files changed, 37 insertions(+)
>
> diff --git a/lib/ext2fs/dblist.c b/lib/ext2fs/dblist.c
> index bbdb221d..5568b8ec 100644
> --- a/lib/ext2fs/dblist.c
> +++ b/lib/ext2fs/dblist.c
> @@ -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
> *
> diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
> index 18dddc2c..443f93d2 100644
> --- a/lib/ext2fs/ext2fs.h
> +++ b/lib/ext2fs/ext2fs.h
> @@ -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 *));
> --
> 2.37.3
>

2022-11-18 13:57:56

by Andreas Dilger

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

On Nov 7, 2022, at 06:23, Ritesh Harjani (IBM) <[email protected]> wrote:
>
> From: Wang Shilong <[email protected]>
>
> Signed-off-by: Li Xi <[email protected]>
> Signed-off-by: Wang Shilong <[email protected]>
> [Note: splitted rb merge tree logic patch such that we
> seperate out libext2fs changes from e2fsck specific changes]
> Signed-off-by: Ritesh Harjani (IBM) <[email protected]>

Reviewed-by: Andreas Dilger <[email protected]>

> ---
> 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(+)
>
> diff --git a/lib/ext2fs/bitmaps.c b/lib/ext2fs/bitmaps.c
> index 8bfa24b1..9437331e 100644
> --- a/lib/ext2fs/bitmaps.c
> +++ b/lib/ext2fs/bitmaps.c
> @@ -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);
> diff --git a/lib/ext2fs/bmap64.h b/lib/ext2fs/bmap64.h
> index de334548..555193ee 100644
> --- a/lib/ext2fs/bmap64.h
> +++ b/lib/ext2fs/bmap64.h
> @@ -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);
> diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
> index 443f93d2..54aed5d1 100644
> --- a/lib/ext2fs/ext2fs.h
> +++ b/lib/ext2fs/ext2fs.h
> @@ -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);
> diff --git a/lib/ext2fs/gen_bitmap64.c b/lib/ext2fs/gen_bitmap64.c
> index f7710afd..c31f942f 100644
> --- a/lib/ext2fs/gen_bitmap64.c
> +++ b/lib/ext2fs/gen_bitmap64.c
> @@ -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)
> --
> 2.37.3
>

2022-11-18 13:58:16

by Andreas Dilger

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

On Nov 7, 2022, at 06:24, Ritesh Harjani (IBM) <[email protected]> wrote:
>
> 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.
>
> Signed-off-by: Wang Shilong <[email protected]>
> Signed-off-by: Ritesh Harjani (IBM) <[email protected]>

Reviewed-by: Andreas Dilger <[email protected]>

> ---
> lib/ext2fs/dblist.c | 2 ++
> lib/ext2fs/icount.c | 4 ++++
> 2 files changed, 6 insertions(+)
>
> diff --git a/lib/ext2fs/dblist.c b/lib/ext2fs/dblist.c
> index 5568b8ec..c19e17bc 100644
> --- a/lib/ext2fs/dblist.c
> +++ b/lib/ext2fs/dblist.c
> @@ -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;
> diff --git a/lib/ext2fs/icount.c b/lib/ext2fs/icount.c
> index 766eccca..48665c7e 100644
> --- a/lib/ext2fs/icount.c
> +++ b/lib/ext2fs/icount.c
> @@ -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));
> --
> 2.37.3
>

2022-11-18 13:58:51

by Andreas Dilger

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

On Nov 7, 2022, at 06:23, Ritesh Harjani (IBM) <[email protected]> wrote:
>
> From: Li Xi <[email protected]>
>
> Signed-off-by: Li Xi <[email protected]>
> Signed-off-by: Wang Shilong <[email protected]>
> Signed-off-by: Ritesh Harjani (IBM) <[email protected]>

Reviewed-by: Andreas Dilger <[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(-)
>
> diff --git a/lib/ext2fs/ext2_io.h b/lib/ext2fs/ext2_io.h
> index 8fe5b323..8cc355be 100644
> --- a/lib/ext2fs/ext2_io.h
> +++ b/lib/ext2fs/ext2_io.h
> @@ -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 */
> diff --git a/lib/ext2fs/undo_io.c b/lib/ext2fs/undo_io.c
> index f4a6d526..678ff421 100644
> --- a/lib/ext2fs/undo_io.c
> +++ b/lib/ext2fs/undo_io.c
> @@ -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,
> diff --git a/lib/ext2fs/unix_io.c b/lib/ext2fs/unix_io.c
> index 5b894826..8f8118a3 100644
> --- a/lib/ext2fs/unix_io.c
> +++ b/lib/ext2fs/unix_io.c
> @@ -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;
> --
> 2.37.3
>

2022-11-18 13:59:22

by Andreas Dilger

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

MOn Nov 7, 2022, at 06:23, Ritesh Harjani (IBM) <[email protected]> wrote:
>
> From: Li Xi <[email protected]>
>
> Merge inode_count and inode_link_info properly after
> threads finish.
>
> Signed-off-by: Li Xi <[email protected]>
> Signed-off-by: Wang Shilong <[email protected]>
> [Note: splitted the patch to seperate libext2fs changes from e2fsck]
> Signed-off-by: Ritesh Harjani (IBM) <[email protected]>

Reviewed-by: Andreas Dilger <[email protected]>

> ---
> lib/ext2fs/ext2fs.h | 1 +
> lib/ext2fs/icount.c | 103 ++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 104 insertions(+)
>
> diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
> index 54aed5d1..139a25fc 100644
> --- a/lib/ext2fs/ext2fs.h
> +++ b/lib/ext2fs/ext2fs.h
> @@ -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 *);
>
> diff --git a/lib/ext2fs/icount.c b/lib/ext2fs/icount.c
> index 888a90b2..766eccca 100644
> --- a/lib/ext2fs/icount.c
> +++ b/lib/ext2fs/icount.c
> @@ -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)
> --
> 2.37.3
>

2022-11-18 13:59:49

by Andreas Dilger

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

On Nov 7, 2022, at 06:23, Ritesh Harjani (IBM) <[email protected]> wrote:
>
> From: Wang Shilong <[email protected]>
>
> Every threads calculate its own quota accounting,
> merge them after threads finish.
>
> Signed-off-by: Wang Shilong <[email protected]>
> [Note: splitted the patch to seperate libext2fs changes from e2fsck]
> Signed-off-by: Ritesh Harjani (IBM) <[email protected]>

Reviewed-by: Andreas Dilger <[email protected]>

> ---
> lib/support/mkquota.c | 39 +++++++++++++++++++++++++++++++++++++++
> lib/support/quotaio.h | 3 +++
> 2 files changed, 42 insertions(+)
>
> diff --git a/lib/support/mkquota.c b/lib/support/mkquota.c
> index 9339c994..ce1ab4cd 100644
> --- a/lib/support/mkquota.c
> +++ b/lib/support/mkquota.c
> @@ -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
> diff --git a/lib/support/quotaio.h b/lib/support/quotaio.h
> index 84fac35d..240a0762 100644
> --- a/lib/support/quotaio.h
> +++ b/lib/support/quotaio.h
> @@ -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 */
> --
> 2.37.3
>

2022-11-18 19:50:53

by Andreas Dilger

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

On Nov 7, 2022, at 05:23, Ritesh Harjani (IBM) <[email protected]> wrote:
>
> 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.
>
> Signed-off-by: Saranya Muruganandam <[email protected]>
> Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
> ---
> lib/ext2fs/dupfs.c | 183 ++++++++++++++++++++++++++++++++++++++++++++
> lib/ext2fs/ext2fs.h | 23 ++++++
> 2 files changed, 206 insertions(+)
>
> diff --git a/lib/ext2fs/dupfs.c b/lib/ext2fs/dupfs.c
> index 02721e1a..ecc57cf7 100644
> --- a/lib/ext2fs/dupfs.c
> +++ b/lib/ext2fs/dupfs.c
> @@ -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);

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.

> + 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;

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

> +
> + 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);

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

Cheers, Andreas


> + 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
> diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
> index 139a25fc..b1505f95 100644
> --- a/lib/ext2fs/ext2fs.h
> +++ b/lib/ext2fs/ext2fs.h
> @@ -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);
> --
> 2.37.3
>

2022-11-19 03:48:05

by Ritesh Harjani

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

On 22/11/18 07:20AM, Andreas Dilger wrote:
> On Nov 18, 2022, at 05:37, Ritesh Harjani (IBM) <[email protected]> wrote:
> >
> > On 22/11/18 04:34AM, Andreas Dilger wrote:
> >>> On Nov 7, 2022, at 06:22, Ritesh Harjani (IBM) <[email protected]> wrote:
> >>>
> >>> f_crashdisk test failed with UNIX_IO_FORCE_BOUNCE=yes due to unbalanced
> >>> mutex unlock in below path.
> >>>
> >>> This patch fixes it.
> >>>
> >>> Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
> >>> ---
> >>> lib/ext2fs/unix_io.c | 1 -
> >>> 1 file changed, 1 deletion(-)
> >>>
> >>> diff --git a/lib/ext2fs/unix_io.c b/lib/ext2fs/unix_io.c
> >>> index e53db333..5b894826 100644
> >>> --- a/lib/ext2fs/unix_io.c
> >>> +++ b/lib/ext2fs/unix_io.c
> >>> @@ -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.
>
> 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.

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

-ritesh

2022-11-19 05:11:13

by Ritesh Harjani

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

On 22/11/18 12:46PM, Andreas Dilger wrote:
> On Nov 7, 2022, at 05:23, Ritesh Harjani (IBM) <[email protected]> wrote:
> >
> > 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.
> >
> > Signed-off-by: Saranya Muruganandam <[email protected]>
> > Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
> > ---
> > lib/ext2fs/dupfs.c | 183 ++++++++++++++++++++++++++++++++++++++++++++
> > lib/ext2fs/ext2fs.h | 23 ++++++
> > 2 files changed, 206 insertions(+)
> >
> > diff --git a/lib/ext2fs/dupfs.c b/lib/ext2fs/dupfs.c
> > index 02721e1a..ecc57cf7 100644
> > --- a/lib/ext2fs/dupfs.c
> > +++ b/lib/ext2fs/dupfs.c
> > @@ -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);
>
> 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.

Ok, so there are two things here. Let's first discuss it from the e2fsck perspective.
ext2fs_clone_fs() is called in the thread prepare for each thread
sequentially. Also what we are really trying to do here is copy the ext2_filsys
source fs and then all threads will work on their cloned "childfs".

So we don't require any locking, because even when threads will start running,
they will only modify their childfs.

Now.. let's see it from the libext2fs API perspective. So will there be any need
for locking for any other client which might use ext2fs_clone_fs()?
Because the general pthread programming would be like :

start():
for (t = 0; t < num_threads; t++) {
clone_contexts() -> ext2fs_clone_fs()
pthread_run()
<..>
}

finish():
for (t = 0; t < num_threads; t++) {
pthread_join()
merge_contexts() -> ext2fs_merge_fs()
}

So why we don't require any locking here, right?
Unless ofcourse start() phase can also be called in parallel.
But that won't be true no.

>
> > + 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;
>
> This should be up where the other NULL values are set.

ok.

>
> > +
> > + 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);
>
> Same here. The mutex should be held over the whole copy process.

Again same discussion as above. We don't need to have a lock here, because the
ext2fs_merge_fs() will happen post pthread_join() for each thread.

-ritesh

>
> Cheers, Andreas
>
>
> > + 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
> > diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
> > index 139a25fc..b1505f95 100644
> > --- a/lib/ext2fs/ext2fs.h
> > +++ b/lib/ext2fs/ext2fs.h
> > @@ -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);
> > --
> > 2.37.3
> >

2022-11-19 05:41:48

by Ritesh Harjani

[permalink] [raw]
Subject: Re: [RFCv1 00/72] e2fsprogs: Parallel fsck support

On Fri, Nov 18, 2022 at 3:53 PM Andreas Dilger <[email protected]> wrote:
>
> On Nov 7, 2022, at 06:22, Ritesh Harjani (IBM) <[email protected]> wrote:
>
>
> 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)
>
>
> Ritesh,
> Thanks for your work to update this patch series. I've been meaning to look at it, but

Thanks Andreas, I see you have already started reviewing. This is
super helpful :)

> have been traveling the past couple of weeks.
>
> Have you done any performance comparisons against the original pfsck patch
> series?

Yes, I did, for the existing tests that I just mentioned above and
found the perf numbers to be
similar. I am also planning to take a RAID device setup from an
internal team and collect
some performance numbers there.

>
> I did make a patch against our original series to improve the workload balance between
> threads, so that they are assigned a roughly equal number of inodes, instead of an equal
> number of block groups, since my testing showed the processing time of the threads
> was proportional to the inode count. This avoids uneven thread completion and reduces overall processing time.

Yes, based on my testing so far. I too agree that to improve the
workload balance one should assign an equal
number of inodes to different threads.

>
> That patch is available in our Gerrit, and I can push it once this series lands, or it could be
> merged into the original patch that adds the e2fsck_pass1_thread_prepare() code.
>
> https://review.whamcloud.com/48806

Sure, I will keep a note of this patch too in my performance runs.

So, now given the number of patches are quite large, I am hoping that
we could review all libext2fs changes
for getting it in shape for merge. Then while we are at e2fsck change,
we can definitely look at the above patch
too.

Thanks again!!
-ritesh

>
> Cheers, Andreas.
>
> 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
>
> --
> 2.37.3
>

2022-11-23 05:11:19

by Andreas Dilger

[permalink] [raw]
Subject: Re: [RFCv1 02/72] gen_bitmaps: Fix ext2fs_compare_generic_bmap/bitmap logic

On Nov 7, 2022, at 5:20 AM, Ritesh Harjani (IBM) <[email protected]> wrote:
>
> 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.
>
> Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
> ---
> lib/ext2fs/gen_bitmap.c | 9 ++++++---
> lib/ext2fs/gen_bitmap64.c | 10 +++++++---
> 2 files changed, 13 insertions(+), 6 deletions(-)
>
> diff --git a/lib/ext2fs/gen_bitmap.c b/lib/ext2fs/gen_bitmap.c
> index 1536d4b3..f7764fca 100644
> --- a/lib/ext2fs/gen_bitmap.c
> +++ b/lib/ext2fs/gen_bitmap.c
> @@ -385,10 +385,13 @@ errcode_t ext2fs_compare_generic_bitmap(errcode_t magic, errcode_t neq,

if ((bm1->start != bm2->start) ||
(bm1->end != bm2->end) ||
(memcmp(bm1->bitmap, bm2->bitmap,
> (size_t) (bm1->end - bm1->start)/8)))
> return neq;
>
> - for (i = bm1->end - ((bm1->end - bm1->start) % 8); i <= bm1->end; i++)

On first review it appears that this is only comparing at most the last 7 bits
before "end", which appears to be wrong. However, if you include the context
earlier in the function (which I've added above), it is using memcmp() to compare
the majority of the bitmaps in byte-wise order, so this is only needs to compare
the remaining bits, and doing the full bitwise scan is much less efficient.

I was going to say that the "start" value needs to be added to the bitmap offset
for the comparison, but I don't think that is correct either. Looking at the
code (and thinking about it some more), it doesn't make sense to allocate space
for bits before "start", so this must be a virtual offset, and the *actual* bits
are always starting at "bitmap", so the above code is correct, AFAICS.

In summary, I don't think this patch will introduce a visible defect, but it
makes the comparisons orders of magnitude less efficient when comparing each
bit individually instead of using memcmp() that is likely hardware accelerated.

It may well be that the calling convention of this code is such that it is
always called with end=byte-aligned value so this code is never used, but that
doesn't mean it shouldn't be coded properly, in case that isn't true in the future.

> - 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);

Strictly speaking, the !! here is not needed, and ! is enough for comparing
the inequality of the two values. However, this is only used for 0-7 bits
and isn't performance critical since the memcmp() does the heavy lifting.

> + if (ret1 != ret2)
> return neq;
> + }
>
> return 0;
> }
> diff --git a/lib/ext2fs/gen_bitmap64.c b/lib/ext2fs/gen_bitmap64.c
> index c860c10e..f7710afd 100644
> --- a/lib/ext2fs/gen_bitmap64.c
> +++ b/lib/ext2fs/gen_bitmap64.c
> @@ -629,10 +629,14 @@ errcode_t ext2fs_compare_generic_bmap(errcode_t neq,
> (bm1->end != bm2->end))

Conversely, *this* version of the function is *not* doing the memcmp() of
the bulk of the bitmap contents, so it would appear to have a bug that the
patch fixes, but in a very slow manner. It would be better to use memcmp().

> 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);

Cheers, Andreas






Attachments:
signature.asc (890.00 B)
Message signed with OpenPGP

2022-12-12 20:49:53

by Andreas Dilger

[permalink] [raw]
Subject: Re: [RFCv1 13/72] tst_badblocks: Add unit test to verify badblocks list merge api

On Nov 7, 2022, at 5:21 AM, Ritesh Harjani (IBM) <[email protected]> wrote:
>
> Add unit test to verify badblocks list merge api i.e.
> ext2fs_badblocks_merge()
>
> Signed-off-by: Ritesh Harjani (IBM) <[email protected]>

Reviewed-by: Andreas Dilger <[email protected]>

> ---
> lib/ext2fs/tst_badblocks.c | 61 ++++++++++++++++++++++++++++++++++++--
> 1 file changed, 59 insertions(+), 2 deletions(-)
>
> diff --git a/lib/ext2fs/tst_badblocks.c b/lib/ext2fs/tst_badblocks.c
> index b6e766ab..946de0ae 100644
> --- a/lib/ext2fs/tst_badblocks.c
> +++ b/lib/ext2fs/tst_badblocks.c
> @@ -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;
>
> --
> 2.37.3
>


Cheers, Andreas






Attachments:
signature.asc (890.00 B)
Message signed with OpenPGP

2022-12-12 21:01:28

by Andreas Dilger

[permalink] [raw]
Subject: Re: [RFCv1 14/72] tst_bitmaps_standalone: Add copy and merge bitmaps test

On Nov 7, 2022, at 5:21 AM, Ritesh Harjani (IBM) <[email protected]> wrote:
>
> This adds a basic copy and merge api test for rbtree bitmap type.
>
> Signed-off-by: Ritesh Harjani (IBM) <[email protected]>

Thanks for adding this test. It looks like it is only testing the default
rbtree bitmap type. It would be good to also add a test for regular bitmaps.

Reviewed-by: Andreas Dilger <[email protected]>

> ---
> 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
>
> diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in
> index f6a050a2..1692500e 100644
> --- a/lib/ext2fs/Makefile.in
> +++ b/lib/ext2fs/Makefile.in
> @@ -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 \
> diff --git a/lib/ext2fs/tst_bitmaps_standalone.c b/lib/ext2fs/tst_bitmaps_standalone.c
> new file mode 100644
> index 00000000..68b598a8
> --- /dev/null
> +++ b/lib/ext2fs/tst_bitmaps_standalone.c
> @@ -0,0 +1,170 @@
> +#include "config.h"
> +#include <stdio.h>
> +#include <string.h>
> +#include <assert.h>
> +#if HAVE_UNISTD_H
> +#include <unistd.h>
> +#endif
> +#include <fcntl.h>
> +#include <time.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +#if HAVE_ERRNO_H
> +#include <errno.h>
> +#endif
> +
> +#include "ext2_fs.h"
> +#include "ext2fs.h"
> +#include "bmap64.h"
> +
> +ext2_filsys test_fs;
> +ext2fs_block_bitmap block_map_1;
> +ext2fs_block_bitmap block_map_2;
> +ext2fs_block_bitmap block_map;
> +
> +static int test_fail = 0;
> +
> +void dump_bitmap(ext2fs_generic_bitmap bmap, unsigned int start, unsigned num)
> +{
> + unsigned char *buf;
> + errcode_t retval;
> + int i, len = (num - start + 7) / 8;
> +
> + buf = malloc(len);
> + if (!buf) {
> + com_err("dump_bitmap", 0, "couldn't allocate buffer");
> + return;
> + }
> + memset(buf, 0, len);
> + retval = ext2fs_get_generic_bmap_range(bmap, (__u64) start, num, buf);
> + if (retval) {
> + com_err("dump_bitmap", retval,
> + "while calling ext2fs_generic_bmap_range");
> + free(buf);
> + return;
> + }
> + for (i=len-1; i >= 0; i--)
> + printf("%02x ", buf[i]);
> + printf("\n");
> + printf("bits set: %u\n", ext2fs_bitcount(buf, len));
> + free(buf);
> +}
> +
> +static void test_copy_run()
> +{
> + int blocks[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 21, 23, 26, 29, 33, 37, 38};
> + errcode_t ret;
> + char *buf_map = NULL;
> + char *buf_copy_map = NULL;
> +
> + assert(ext2fs_allocate_block_bitmap(test_fs, "block bitmap", &block_map_1) == 0);
> +
> + for (int i = 0; i < sizeof(blocks)/sizeof(blocks[0]); i++) {
> + ext2fs_mark_block_bitmap2(block_map_1, blocks[i]);
> + }
> +
> + assert(ext2fs_copy_bitmap(block_map_1, &block_map) == 0);
> +
> + if (ext2fs_compare_block_bitmap(block_map_1, block_map) != 0) {
> + printf("block bitmap copy test failed\n");
> + test_fail++;
> +
> + dump_bitmap(block_map_1, test_fs->super->s_first_data_block,
> + test_fs->super->s_blocks_count);
> +
> + dump_bitmap(block_map, test_fs->super->s_first_data_block,
> + test_fs->super->s_blocks_count);
> + }
> +
> + ext2fs_free_block_bitmap(block_map_1);
> + ext2fs_free_block_bitmap(block_map);
> +}
> +
> +void test_merge_run()
> +{
> + int blocks_odd[] = {1, 3, 5, 7, 9, 21, 23, 29, 33, 37};
> + int blocks_even[] = {2, 4, 6, 8, 10, 26, 38};
> + ext2fs_generic_bitmap_64 tmp_map;
> +
> + assert(ext2fs_allocate_block_bitmap(test_fs, "block bitmap 1", &block_map_1) == 0);
> + assert(ext2fs_allocate_block_bitmap(test_fs, "block bitmap 2", &block_map_2) == 0);
> + assert(ext2fs_allocate_block_bitmap(test_fs, "block bitmap 2", &block_map) == 0);
> +
> + for (int i = 0; i < sizeof(blocks_odd) / sizeof(blocks_odd[0]); i++) {
> + ext2fs_mark_block_bitmap2(block_map_1, blocks_odd[i]);
> + ext2fs_mark_block_bitmap2(block_map, blocks_odd[i]);
> + }
> +
> + for (int i = 0; i < sizeof(blocks_even) / sizeof(blocks_even[0]); i++) {
> + ext2fs_mark_block_bitmap2(block_map_2, blocks_even[i]);
> + ext2fs_mark_block_bitmap2(block_map, blocks_even[i]);
> + }
> +
> + assert(ext2fs_merge_bitmap(block_map_2, block_map_1, NULL, NULL) == 0);
> + if (ext2fs_compare_block_bitmap(block_map_1, block_map) != 0) {
> + printf("block bitmap merge test failed\n");
> + test_fail++;
> +
> + dump_bitmap(block_map_1, test_fs->super->s_first_data_block,
> + test_fs->super->s_blocks_count);
> +
> + dump_bitmap(block_map, test_fs->super->s_first_data_block,
> + test_fs->super->s_blocks_count);
> + }
> +
> + ext2fs_free_block_bitmap(block_map_1);
> + ext2fs_free_block_bitmap(block_map_2);
> + ext2fs_free_block_bitmap(block_map);
> +}
> +
> +static void setup_filesystem(const char *name, unsigned int blocks,
> + unsigned int inodes, unsigned int type,
> + unsigned int flags)
> +{
> + struct ext2_super_block param;
> + errcode_t ret;
> +
> + memset(&param, 0, sizeof(param));
> + ext2fs_blocks_count_set(&param, blocks);
> + param.s_inodes_count = inodes;
> +
> + ret = ext2fs_initialize(name, flags, &param, test_io_manager,
> + &test_fs);
> + if (ret) {
> + com_err(name, ret, "while initializing filesystem");
> + return;
> + }
> +
> + test_fs->default_bitmap_type = type;
> +
> + ext2fs_free_block_bitmap(test_fs->block_map);
> + ext2fs_free_block_bitmap(test_fs->inode_map);
> +
> + return;
> +errout:
> + ext2fs_close_free(&test_fs);
> +}
> +
> +int main(int argc, char **argv)
> +{
> + unsigned int blocks = 127;
> + unsigned int inodes = 0;
> + unsigned int type = EXT2FS_BMAP64_RBTREE;
> + unsigned int flags = EXT2_FLAG_64BITS;
> + char *buf = NULL;
> +
> + setup_filesystem(argv[0], blocks, inodes, type, flags);
> +
> + /* test for EXT2FS_BMAP64_RBTREE */
> + test_copy_run();
> + test_merge_run();
> +
> + /* TODO: test for EXT2FS_BMAP64_BITARRAY */
> +
> + if (test_fail)
> + printf("%s: Test copy & merge bitmaps -- NOT OK\n", argv[0]);
> + else
> + printf("%s: Test copy & merge bitmaps -- OK\n", argv[0]);
> +
> + return test_fail;
> +}
> --
> 2.37.3
>


Cheers, Andreas






Attachments:
signature.asc (890.00 B)
Message signed with OpenPGP

2022-12-14 05:20:24

by Ritesh Harjani

[permalink] [raw]
Subject: Re: [RFCv1 14/72] tst_bitmaps_standalone: Add copy and merge bitmaps test

On 22/12/12 01:40PM, Andreas Dilger wrote:
> On Nov 7, 2022, at 5:21 AM, Ritesh Harjani (IBM) <[email protected]> wrote:
> >
> > This adds a basic copy and merge api test for rbtree bitmap type.
> >
> > Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
>
> Thanks for adding this test. It looks like it is only testing the default
> rbtree bitmap type. It would be good to also add a test for regular bitmaps.

sure, the test will also requires merge logic for regular bitmap.
I had added some basic support earlier (non-aligned was remaining to be added).
In the next iteration, I will keep a note about this.

>
> Reviewed-by: Andreas Dilger <[email protected]>

Thanks for the review!

>
> > ---
> > 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
> >
> > diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in
> > index f6a050a2..1692500e 100644
> > --- a/lib/ext2fs/Makefile.in
> > +++ b/lib/ext2fs/Makefile.in
> > @@ -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 \
> > diff --git a/lib/ext2fs/tst_bitmaps_standalone.c b/lib/ext2fs/tst_bitmaps_standalone.c
> > new file mode 100644
> > index 00000000..68b598a8
> > --- /dev/null
> > +++ b/lib/ext2fs/tst_bitmaps_standalone.c
> > @@ -0,0 +1,170 @@
> > +#include "config.h"
> > +#include <stdio.h>
> > +#include <string.h>
> > +#include <assert.h>
> > +#if HAVE_UNISTD_H
> > +#include <unistd.h>
> > +#endif
> > +#include <fcntl.h>
> > +#include <time.h>
> > +#include <sys/stat.h>
> > +#include <sys/types.h>
> > +#if HAVE_ERRNO_H
> > +#include <errno.h>
> > +#endif
> > +
> > +#include "ext2_fs.h"
> > +#include "ext2fs.h"
> > +#include "bmap64.h"
> > +
> > +ext2_filsys test_fs;
> > +ext2fs_block_bitmap block_map_1;
> > +ext2fs_block_bitmap block_map_2;
> > +ext2fs_block_bitmap block_map;
> > +
> > +static int test_fail = 0;
> > +
> > +void dump_bitmap(ext2fs_generic_bitmap bmap, unsigned int start, unsigned num)
> > +{
> > + unsigned char *buf;
> > + errcode_t retval;
> > + int i, len = (num - start + 7) / 8;
> > +
> > + buf = malloc(len);
> > + if (!buf) {
> > + com_err("dump_bitmap", 0, "couldn't allocate buffer");
> > + return;
> > + }
> > + memset(buf, 0, len);
> > + retval = ext2fs_get_generic_bmap_range(bmap, (__u64) start, num, buf);
> > + if (retval) {
> > + com_err("dump_bitmap", retval,
> > + "while calling ext2fs_generic_bmap_range");
> > + free(buf);
> > + return;
> > + }
> > + for (i=len-1; i >= 0; i--)
> > + printf("%02x ", buf[i]);
> > + printf("\n");
> > + printf("bits set: %u\n", ext2fs_bitcount(buf, len));
> > + free(buf);
> > +}
> > +
> > +static void test_copy_run()
> > +{
> > + int blocks[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 21, 23, 26, 29, 33, 37, 38};
> > + errcode_t ret;
> > + char *buf_map = NULL;
> > + char *buf_copy_map = NULL;
> > +
> > + assert(ext2fs_allocate_block_bitmap(test_fs, "block bitmap", &block_map_1) == 0);
> > +
> > + for (int i = 0; i < sizeof(blocks)/sizeof(blocks[0]); i++) {
> > + ext2fs_mark_block_bitmap2(block_map_1, blocks[i]);
> > + }
> > +
> > + assert(ext2fs_copy_bitmap(block_map_1, &block_map) == 0);
> > +
> > + if (ext2fs_compare_block_bitmap(block_map_1, block_map) != 0) {
> > + printf("block bitmap copy test failed\n");
> > + test_fail++;
> > +
> > + dump_bitmap(block_map_1, test_fs->super->s_first_data_block,
> > + test_fs->super->s_blocks_count);
> > +
> > + dump_bitmap(block_map, test_fs->super->s_first_data_block,
> > + test_fs->super->s_blocks_count);
> > + }
> > +
> > + ext2fs_free_block_bitmap(block_map_1);
> > + ext2fs_free_block_bitmap(block_map);
> > +}
> > +
> > +void test_merge_run()
> > +{
> > + int blocks_odd[] = {1, 3, 5, 7, 9, 21, 23, 29, 33, 37};
> > + int blocks_even[] = {2, 4, 6, 8, 10, 26, 38};
> > + ext2fs_generic_bitmap_64 tmp_map;
> > +
> > + assert(ext2fs_allocate_block_bitmap(test_fs, "block bitmap 1", &block_map_1) == 0);
> > + assert(ext2fs_allocate_block_bitmap(test_fs, "block bitmap 2", &block_map_2) == 0);
> > + assert(ext2fs_allocate_block_bitmap(test_fs, "block bitmap 2", &block_map) == 0);
> > +
> > + for (int i = 0; i < sizeof(blocks_odd) / sizeof(blocks_odd[0]); i++) {
> > + ext2fs_mark_block_bitmap2(block_map_1, blocks_odd[i]);
> > + ext2fs_mark_block_bitmap2(block_map, blocks_odd[i]);
> > + }
> > +
> > + for (int i = 0; i < sizeof(blocks_even) / sizeof(blocks_even[0]); i++) {
> > + ext2fs_mark_block_bitmap2(block_map_2, blocks_even[i]);
> > + ext2fs_mark_block_bitmap2(block_map, blocks_even[i]);
> > + }
> > +
> > + assert(ext2fs_merge_bitmap(block_map_2, block_map_1, NULL, NULL) == 0);
> > + if (ext2fs_compare_block_bitmap(block_map_1, block_map) != 0) {
> > + printf("block bitmap merge test failed\n");
> > + test_fail++;
> > +
> > + dump_bitmap(block_map_1, test_fs->super->s_first_data_block,
> > + test_fs->super->s_blocks_count);
> > +
> > + dump_bitmap(block_map, test_fs->super->s_first_data_block,
> > + test_fs->super->s_blocks_count);
> > + }
> > +
> > + ext2fs_free_block_bitmap(block_map_1);
> > + ext2fs_free_block_bitmap(block_map_2);
> > + ext2fs_free_block_bitmap(block_map);
> > +}
> > +
> > +static void setup_filesystem(const char *name, unsigned int blocks,
> > + unsigned int inodes, unsigned int type,
> > + unsigned int flags)
> > +{
> > + struct ext2_super_block param;
> > + errcode_t ret;
> > +
> > + memset(&param, 0, sizeof(param));
> > + ext2fs_blocks_count_set(&param, blocks);
> > + param.s_inodes_count = inodes;
> > +
> > + ret = ext2fs_initialize(name, flags, &param, test_io_manager,
> > + &test_fs);
> > + if (ret) {
> > + com_err(name, ret, "while initializing filesystem");
> > + return;
> > + }
> > +
> > + test_fs->default_bitmap_type = type;
> > +
> > + ext2fs_free_block_bitmap(test_fs->block_map);
> > + ext2fs_free_block_bitmap(test_fs->inode_map);
> > +
> > + return;
> > +errout:
> > + ext2fs_close_free(&test_fs);
> > +}
> > +
> > +int main(int argc, char **argv)
> > +{
> > + unsigned int blocks = 127;
> > + unsigned int inodes = 0;
> > + unsigned int type = EXT2FS_BMAP64_RBTREE;
> > + unsigned int flags = EXT2_FLAG_64BITS;
> > + char *buf = NULL;
> > +
> > + setup_filesystem(argv[0], blocks, inodes, type, flags);
> > +
> > + /* test for EXT2FS_BMAP64_RBTREE */
> > + test_copy_run();
> > + test_merge_run();
> > +
> > + /* TODO: test for EXT2FS_BMAP64_BITARRAY */
> > +
> > + if (test_fail)
> > + printf("%s: Test copy & merge bitmaps -- NOT OK\n", argv[0]);
> > + else
> > + printf("%s: Test copy & merge bitmaps -- OK\n", argv[0]);
> > +
> > + return test_fail;
> > +}
> > --
> > 2.37.3
> >
>
>
> Cheers, Andreas
>
>
>
>
>


2022-12-14 21:23:28

by Andreas Dilger

[permalink] [raw]
Subject: Re: [RFCv1 16/72] tst_libext2fs_pthread: Add libext2fs merge/clone unit tests

On Nov 7, 2022, at 5:21 AM, Ritesh Harjani (IBM) <[email protected]> wrote:
>
> 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.
>
> Signed-off-by: Ritesh Harjani (IBM) <[email protected]>

Reviewed-by: Andreas Dilger <[email protected]>

> ---
> 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
>
> diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in
> index c0694175..5fde9900 100644
> --- a/lib/ext2fs/Makefile.in
> +++ b/lib/ext2fs/Makefile.in
> @@ -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 \
> diff --git a/lib/ext2fs/tst_libext2fs_pthread.c b/lib/ext2fs/tst_libext2fs_pthread.c
> new file mode 100644
> index 00000000..a5bb6fcd
> --- /dev/null
> +++ b/lib/ext2fs/tst_libext2fs_pthread.c
> @@ -0,0 +1,315 @@
> +#include "config.h"
> +#include <stdio.h>
> +#include <string.h>
> +#include <assert.h>
> +#if HAVE_UNISTD_H
> +#include <unistd.h>
> +#endif
> +#include <fcntl.h>
> +#include <time.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +#if HAVE_ERRNO_H
> +#include <errno.h>
> +#endif
> +#if HAVE_PTHREAD
> +#include <pthread.h>
> +#endif
> +
> +#include "ext2_fs.h"
> +#include "ext2fs.h"
> +
> +#ifdef HAVE_PTHREAD
> +int test_fail = 0;
> +ext2_filsys testfs;
> +ext2fs_inode_bitmap inode_used_map;
> +ext2fs_block_bitmap block_used_map;
> +ext2_filsys childfs[2];
> +pthread_t pthread_infos[2];
> +
> +#define nr_bits 16384
> +int nr_threads = 2;
> +
> +int should_mark_bit()
> +{
> + return rand() % 2 == 0;
> +}
> +
> +void setupfs()
> +{
> + errcode_t retval;
> + struct ext2_super_block param;
> +
> + initialize_ext2_error_table();
> +
> + memset(&param, 0, sizeof(param));
> + ext2fs_blocks_count_set(&param, nr_bits);
> + retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, &param,
> + test_io_manager, &testfs);
> + if (retval) {
> + com_err("setup", retval, "while initializing filesystem");
> + exit(1);
> + }
> +
> + retval = ext2fs_allocate_tables(testfs);
> + if (retval) {
> + com_err("setup", retval, "while allocating tables for testfs");
> + exit(1);
> + }
> +}
> +
> +void setup_used_bitmaps()
> +{
> + int saved_type = testfs->default_bitmap_type;
> + ext2_inode_scan scan;
> + struct ext2_inode inode;
> + ext2_ino_t ino;
> + errcode_t retval;
> + int i;
> +
> + testfs->default_bitmap_type = EXT2FS_BMAP64_BITARRAY;
> +
> + /* allocate block and inode used bitmaps */
> + retval = ext2fs_allocate_block_bitmap(testfs, "block used map", &block_used_map);
> + if (retval)
> + goto out;
> +
> + retval = ext2fs_allocate_inode_bitmap(testfs, "inode used map", &inode_used_map);
> + if (retval)
> + goto out;
> +
> + /* setup block and inode used bitmaps */
> + for (i = 1; i < nr_bits; i++) {
> + /*
> + * we check for testfs->block_map as well since there could be some
> + * blocks already set as part of the FS metadata.
> + */
> + if (should_mark_bit() || ext2fs_test_block_bitmap2(testfs->block_map, i)) {
> + ext2fs_mark_block_bitmap2(block_used_map, i);
> + }
> + }
> +
> + retval = ext2fs_open_inode_scan(testfs, 8, &scan);
> + if (retval) {
> + com_err("setup_inode_map", retval, "while open inode scan");
> + exit(1);
> + }
> +
> + retval = ext2fs_get_next_inode(scan, &ino, &inode);
> + if (retval) {
> + com_err("setup_inode_map", retval, "while getting next inode");
> + exit(1);
> + }
> +
> + while (ino) {
> + if (should_mark_bit())
> + ext2fs_mark_inode_bitmap2(inode_used_map, ino);
> +
> + retval = ext2fs_get_next_inode(scan, &ino, &inode);
> + if (retval) {
> + com_err("setup_inode_map", retval, "while getting next inode");
> + exit(1);
> + }
> + }
> + ext2fs_close_inode_scan(scan);
> +
> + testfs->default_bitmap_type = saved_type;
> + return;
> +out:
> + com_err("setup_used_bitmaps", retval, "while setting up bitmaps\n");
> + exit(1);
> +}
> +
> +void setup_childfs()
> +{
> + errcode_t retval;
> + int i;
> +
> + for (i = 0; i < nr_threads; i++) {
> + retval = ext2fs_clone_fs(testfs, &childfs[i], EXT2FS_CLONE_INODE | EXT2FS_CLONE_BLOCK);
> + if (retval) {
> + com_err("setup_childfs", retval, "while clone testfs for childfs");
> + exit(1);
> + }
> +
> + retval = childfs[i]->io->manager->open(childfs[i]->device_name,
> + IO_FLAG_THREADS | IO_FLAG_NOCACHE, &childfs[i]->io);
> + if (retval) {
> + com_err("setup_pthread", retval, "while opening childfs");
> + exit(1);
> + }
> + assert(childfs[i]->parent == testfs);
> + }
> +}
> +
> +static errcode_t scan_callback(ext2_filsys fs,
> + ext2_inode_scan scan EXT2FS_ATTR((unused)),
> + dgrp_t group, void *priv_data)
> +{
> + pthread_t id = *((pthread_t *)priv_data);
> +
> + printf("%s: Called for group %d via thread %d\n", __func__, group,
> + pthread_equal(pthread_infos[1], id));
> + if (pthread_equal(pthread_infos[0], id)) {
> + if (group >= fs->group_desc_count / 2 - 1)
> + return 1;
> + }
> + return 0;
> +}
> +
> +static void *run_pthread(void *arg)
> +{
> + errcode_t retval = 0;
> + int i = 0, start, end;
> + ext2fs_block_bitmap test_block_bitmap;
> + ext2fs_inode_bitmap test_inode_bitmap;
> + ext2_inode_scan scan;
> + struct ext2_inode inode;
> + ext2_ino_t ino;
> + pthread_t id = pthread_self();
> +
> + if (pthread_equal(pthread_infos[0], id)) {
> + start = 1;
> + end = nr_bits/2;
> + test_block_bitmap = childfs[0]->block_map;
> + test_inode_bitmap = childfs[0]->inode_map;
> +
> + retval = ext2fs_open_inode_scan(childfs[0], 8, &scan);
> + if (retval) {
> + com_err("setup_inode_map", retval, "while open inode scan");
> + exit(1);
> + }
> +
> + } else {
> + start = nr_bits / 2 + 1;;
> + end = nr_bits - 1;
> + test_block_bitmap = childfs[1]->block_map;
> + test_inode_bitmap = childfs[1]->inode_map;
> +
> + retval = ext2fs_open_inode_scan(childfs[1], 8, &scan);
> + if (retval) {
> + com_err("setup_inode_map", retval, "while open inode scan");
> + exit(1);
> + }
> + ext2fs_inode_scan_goto_blockgroup(scan, testfs->group_desc_count/2);
> + }
> +
> + ext2fs_set_inode_callback(scan, scan_callback, &id);
> +
> + /* blocks scan */
> + for (i = start; i <= end; i++) {
> + if (ext2fs_test_block_bitmap2(block_used_map, i)) {
> + ext2fs_mark_block_bitmap2(test_block_bitmap, i);
> + }
> + }
> +
> + /* inodes scan */
> + retval = ext2fs_get_next_inode(scan, &ino, &inode);
> + if (retval) {
> + com_err("setup_inode_map", retval, "while getting next inode");
> + exit(1);
> + }
> +
> + while (ino) {
> + if (ext2fs_test_inode_bitmap2(inode_used_map, ino)) {
> + ext2fs_mark_inode_bitmap2(test_inode_bitmap, ino);
> + }
> +
> + retval = ext2fs_get_next_inode(scan, &ino, &inode);
> + if (retval)
> + break;
> + }
> + ext2fs_close_inode_scan(scan);
> + return NULL;
> +}
> +
> +void run_pthreads()
> +{
> + errcode_t retval;
> + int i;
> +
> + for (i = 0; i < nr_threads; i++) {
> + printf("Starting thread (%d)\n", i);
> + retval = pthread_create(&pthread_infos[i], NULL, &run_pthread, NULL);
> + if (retval) {
> + com_err("run_pthreads", retval, "while pthread_create");
> + exit(1);
> + }
> + }
> +
> + for (i = 0; i < nr_threads; i++) {
> + void *status;
> + int ret;
> + retval = pthread_join(pthread_infos[i], &status);
> + if (retval) {
> + com_err("run_pthreads", retval, "while joining pthreads");
> + exit(1);
> +
> + }
> + ret = status == NULL ? 0 : *(int*)status;
> + if (ret) {
> + com_err("run_pthreads", ret, "pthread returned error");
> + test_fail++;
> + }
> +
> + printf("Closing thread (%d), ret(%d)\n", i, ret);
> + }
> +
> + assert(ext2fs_merge_fs(&childfs[0]) == 0);
> + assert(ext2fs_merge_fs(&childfs[1]) == 0);
> +}
> +
> +void test_bitmaps()
> +{
> + errcode_t retval;
> + retval = ext2fs_compare_block_bitmap(testfs->block_map, block_used_map);
> + if (retval) {
> + printf("Block bitmap compare -- NOT OK!! (%ld)\n", retval);
> + test_fail++;
> + }
> +
> + printf("Block compare bitmap -- OK!!\n");
> + retval = ext2fs_compare_inode_bitmap(testfs->inode_map, inode_used_map);
> + if (retval) {
> + printf("Inode bitmap compare -- NOT OK!! (%ld)\n", retval);
> + test_fail++;
> + }
> + printf("Inode compare bitmap -- OK!!\n");
> +}
> +
> +void free_used_bitmaps()
> +{
> + ext2fs_free_block_bitmap(block_used_map);
> + ext2fs_free_inode_bitmap(inode_used_map);
> +}
> +
> +#endif
> +
> +int main(int argc, char *argv[])
> +{
> + int i;
> +
> +#ifndef HAVE_PTHREAD
> + printf("No PTHREAD support, exiting...\n");
> + return 0;
> +#else
> +
> + srand(time(0));
> +
> + setupfs();
> + setup_used_bitmaps();
> +
> + setup_childfs();
> + run_pthreads();
> + test_bitmaps(i);
> +
> + if (test_fail)
> + printf("%s: Test libext2fs clone/merge with pthreads NOT OK!!\n", argv[0]);
> + else
> + printf("%s: Test libext2fs clone/merge with pthreads OK!!\n", argv[0]);
> + free_used_bitmaps();
> + ext2fs_free(testfs);
> +
> + return test_fail;
> +#endif
> +}
> --
> 2.37.3
>


Cheers, Andreas






Attachments:
signature.asc (890.00 B)
Message signed with OpenPGP

2022-12-14 21:25:49

by Andreas Dilger

[permalink] [raw]
Subject: Re: [RFCv1 17/72] libext2fs: Add support for ext2fs_test_block_bitmap_range2_valid()

On Nov 7, 2022, at 5:21 AM, Ritesh Harjani (IBM) <[email protected]> wrote:
>
> 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()
>
> Signed-off-by: Wang Shilong <[email protected]>
> Signed-off-by: Ritesh Harjani (IBM) <[email protected]>

I don't think this patch is correct?

> ---
> diff --git a/lib/ext2fs/gen_bitmap64.c b/lib/ext2fs/gen_bitmap64.c
> index c31f942f..a9637cb5 100644
> --- a/lib/ext2fs/gen_bitmap64.c
> +++ b/lib/ext2fs/gen_bitmap64.c
> @@ -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;
> + }
> + }

This is bailing out early if the requested bit is > 2^32, but that is
before cluster conversion below. However, I think the bitmap is actually
stored in clusters, so the 2^32 check seems premature?

> +
> + 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;
> +}

Cheers, Andreas






Attachments:
signature.asc (890.00 B)
Message signed with OpenPGP

2022-12-14 21:27:17

by Andreas Dilger

[permalink] [raw]
Subject: Re: [RFCv1 15/72] tst_bitmaps_pthread: Add merge bitmaps test using pthreads

On Nov 7, 2022, at 5:21 AM, Ritesh Harjani (IBM) <[email protected]> wrote:
>
> This patch adds a test to verify the core bitmaps merge APIs
> for rbtree bitmap type.
>
> Signed-off-by: Ritesh Harjani (IBM) <[email protected]>

Reviewed-by: Andreas Dilger <[email protected]>

> +/*
> + * Note we use EXT2FS_BMAP64_BITARRAY always for used_bitmap, this is because
> + * EXT2FS_BMAP64_RBTREE does not support parallel scan due to rcursor
> + * optimization.
> + */

Is this going to be a problem in the future? I think for pass1/pass5 there
are no rbtree bitmaps that are *source* bitmaps for multi-threaded operation,
but I suspect that once we try to parallelize pass2 this could break.

Is there any check in the code that prevents multi-thread access to an rbtree
bitmap? Making a copy for each thread, or ideally copying the rcursor for
each thread, to allow at least multi-threaded reads. I don't think there is
a high need for multi-threaded write

Cheers, Andreas






Attachments:
signature.asc (890.00 B)
Message signed with OpenPGP

2022-12-14 21:27:42

by Andreas Dilger

[permalink] [raw]
Subject: Re: [RFCv1 19/72] libext2fs: Misc fixes for struct_ext2_filsys

On Nov 7, 2022, at 5:21 AM, Ritesh Harjani (IBM) <[email protected]> wrote:
>
> 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.

This should be merged into the previous patch.

> Signed-off-by: Andreas Dilger <[email protected]>
> Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
> ---
> lib/ext2fs/ext2fs.h | 5 ++---
> 1 file changed, 2 insertions(+), 3 deletions(-)
>
> diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
> index 6b4926ce..950ab042 100644
> --- a/lib/ext2fs/ext2fs.h
> +++ b/lib/ext2fs/ext2fs.h
> @@ -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.
> --
> 2.37.3
>


Cheers, Andreas






Attachments:
signature.asc (890.00 B)
Message signed with OpenPGP

2022-12-14 21:28:15

by Andreas Dilger

[permalink] [raw]
Subject: Re: [RFCv1 18/72] libext2fs: Add support to get average group count

On Nov 7, 2022, at 5:21 AM, Ritesh Harjani (IBM) <[email protected]> wrote:
>
> 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.
>
> Signed-off-by: Wang Shilong <[email protected]>
> Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
> ---
> lib/ext2fs/ext2fs.h | 32 +++++++++++++++++++++++++++++++-
> 1 file changed, 31 insertions(+), 1 deletion(-)
>
> diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
> index b1505f95..6b4926ce 100644
> --- a/lib/ext2fs/ext2fs.h
> +++ b/lib/ext2fs/ext2fs.h
> @@ -279,10 +279,11 @@ struct struct_ext2_filsys {
> int cluster_ratio_bits;
> __u16 default_bitmap_type;
> __u16 pad;
> + __u32 fs_num_threads;

From later cleanup patch, fs_num_threads should just use "__u16 pad" field.

Otherwise, looks OK.


> /*
> * 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
>
> --
> 2.37.3
>


Cheers, Andreas






Attachments:
signature.asc (890.00 B)
Message signed with OpenPGP

2022-12-14 21:33:59

by Andreas Dilger

[permalink] [raw]
Subject: Re: [RFCv1 21/72] e2fsck: add -m option for multithread

On Nov 7, 2022, at 5:21 AM, Ritesh Harjani (IBM) <[email protected]> wrote:
>
> 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.
>
> Signed-off-by: Li Xi <[email protected]>
> Signed-off-by: Wang Shilong <[email protected]>
> Signed-off-by: Ritesh Harjani (IBM) <[email protected]>

Minor nit below, but looks OK otherwise:

Reviewed-by: Andreas Dilger <[email protected]>

> diff --git a/e2fsck/unix.c b/e2fsck/unix.c
> index e5b672a2..1ee27f6a 100644
> --- a/e2fsck/unix.c
> +++ b/e2fsck/unix.c
> @@ -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)

I find it easier to find/add options when these are sorted in alphabetical order, but
that is not solely the fault of this patch. At least 'm' is added in order relative
to most other lower-case options.

Cheers, Andreas






Attachments:
signature.asc (890.00 B)
Message signed with OpenPGP

2023-01-24 16:41:01

by Theodore Ts'o

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

On Mon, Nov 07, 2022 at 05:50:49PM +0530, Ritesh Harjani (IBM) wrote:
> f_crashdisk test failed with UNIX_IO_FORCE_BOUNCE=yes due to unbalanced
> mutex unlock in below path.
>
> This patch fixes it.
>
> Signed-off-by: Ritesh Harjani (IBM) <[email protected]>

Applied, thanks.

- Ted

2023-01-24 17:00:01

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [RFCv1 02/72] gen_bitmaps: Fix ext2fs_compare_generic_bmap/bitmap logic

On Tue, Nov 22, 2022 at 10:04:58PM -0700, Andreas Dilger wrote:
> On Nov 7, 2022, at 5:20 AM, Ritesh Harjani (IBM) <[email protected]> wrote:
> >
> > 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.
> >
> > Signed-off-by: Ritesh Harjani (IBM) <[email protected]>
> > ---
> > lib/ext2fs/gen_bitmap.c | 9 ++++++---

The gen_bitmap.c file supports only the original 32-bit bitmaps, so
there is not rbtree bitmap. That's why using memcmp is safe, and as
near as I can tell, no changes are needed for the
lib/ext2fs/gen_bitmap.c

> > diff --git a/lib/ext2fs/gen_bitmap64.c b/lib/ext2fs/gen_bitmap64.c
> > index c860c10e..f7710afd 100644
> > --- a/lib/ext2fs/gen_bitmap64.c
> > +++ b/lib/ext2fs/gen_bitmap64.c
> > @@ -629,10 +629,14 @@ errcode_t ext2fs_compare_generic_bmap(errcode_t neq,
> > (bm1->end != bm2->end))
>
> Conversely, *this* version of the function is *not* doing the memcmp() of
> the bulk of the bitmap contents, so it would appear to have a bug that the
> patch fixes, but in a very slow manner. It would be better to use memcmp().

This is where we have a problem, and the reason why we can't use
memcmp is because in the case where the bitmap is really encoded using
an rbtree, a memcmp isn't applicable.

We *could* extract the bitmap into a bitarray, but that would require
allocating memory, and in the case of a very large file system/bitmap,
especially when running on a 32-bit NAS box, we might not be able to
*afford* to allocate that much memory (or it might not even be
addressable :-).

So yes, we need a fix here, and yes, comparing bit by bit is very
slow. Fortunately, from what I can tell, no one is actually calling
this function, which is why no once noticed that the 64-bit compare
bitmap fucntion was totally hosed.

So applying Ritesh's fix here makes sense, since it at least fixes the
obvious problems. In the long term, we should probably add proper
unit regression tests so can test whether or not this function is
actually working correctly, and if we *want* a faster version of it,
we could do something faster in the case where both bitmaps are the
same type (e.g., both bitarrays or both rbtrees), and if they could do
something where we look for "runs" where all of the bits are all set
or not set, and then compare that against the other bitmap, etc. But
given that we don't have anyusers of this function any more (we used
to, but it disappeared when we optimzied e2fsck's pass5
implementation), that's probably not an urgent fix.

- Ted