Received: by 2002:a05:7412:8d10:b0:f3:1519:9f41 with SMTP id bj16csp5144442rdb; Tue, 12 Dec 2023 23:14:25 -0800 (PST) X-Google-Smtp-Source: AGHT+IGvdCCHMwVRcP/jX8XV5S16kyITtAU/ratw8RmyqJyp0Ik/JxBOyI8xYRzHVG8c/+5qPSLo X-Received: by 2002:a05:6808:640f:b0:3b8:4db9:b791 with SMTP id fg15-20020a056808640f00b003b84db9b791mr9074330oib.48.1702451665239; Tue, 12 Dec 2023 23:14:25 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1702451665; cv=none; d=google.com; s=arc-20160816; b=yNiTDA+Abj1hA3oMA6TvsWu69sbmvol3Wu5rN2D32OgT6lJ8wVF1Qiqlc1rb87wRzg VyYthX0Qv/gNMLMaPVqPcZMrsno1FAJvXPrkEzfiHdXWGemst0GEKlSzw0zGG/23Mi9Z sZ6RFVA77SpHpUqbHfjf5hB5r6Xjncpje1upK+JdN/LkcExunUOvKDoXKaCjbjQsgaTR JudTC1AZsWNlJoKqFPl2sO/cXZrjqzW8k13JKCUO4ngxLe6p0TR4lUggrZKzyPWeHhQR /x8YJ09h33jXNeJrCkQN+dqY2qV5l9qeNWAI0Ft9MGpg7id5Vj8fiy686uVQvyvROKyG /+VQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:subject:message-id:date:from:in-reply-to :references:mime-version:dkim-signature; bh=ofKUl2GizOp1QuJPlnk+EngBOStr6Eba6g1R8JbUe4U=; fh=SbuWAQnqqCpQwZBw62J8OSzy2jKAmqUxArTloFui9wo=; b=rUrzYTvbT0+ce1b9pOFDGA1ZrzpMTS/mtwiyXH/eXnOzOJhacHvbJE5WZCY4eytG3l Xm+8EIfhiwbHOM8h/1PT/VDd7FOjrGQ7/1gr5vkGwg9jg5h/rxckLfjJIVfdW5BlUczT NWWE/j6kDpQjWfFaw2f/YURh/ORMP3BzkFkOrmvoyPhf7m7IyBjYLP9fo1d0oo2vcU7m ByrDgn3dPPNwHKuZN+cipdFQgD0WBHGwUGygnvTkXRqHvlP6YfxbC0Msdc+ZdSbsmvK/ y8Q5cGySvvLynGwcnPSvqUYHzik47dx/YlKSlUXpAih1p9tGDdY6tVA2t9sZddVtpVmJ Cc+w== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20230601 header.b=MUC7Awgg; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:4 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Return-Path: Received: from howler.vger.email (howler.vger.email. [2620:137:e000::3:4]) by mx.google.com with ESMTPS id f10-20020a056a0022ca00b006ce4d21cc78si9116715pfj.170.2023.12.12.23.14.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 12 Dec 2023 23:14:25 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:4 as permitted sender) client-ip=2620:137:e000::3:4; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20230601 header.b=MUC7Awgg; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:4 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by howler.vger.email (Postfix) with ESMTP id 001028220118; Tue, 12 Dec 2023 23:14:19 -0800 (PST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.11 at howler.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233318AbjLMHNb (ORCPT + 99 others); Wed, 13 Dec 2023 02:13:31 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40644 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233183AbjLMHNa (ORCPT ); Wed, 13 Dec 2023 02:13:30 -0500 Received: from mail-wm1-x32d.google.com (mail-wm1-x32d.google.com [IPv6:2a00:1450:4864:20::32d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C8819D0 for ; Tue, 12 Dec 2023 23:13:33 -0800 (PST) Received: by mail-wm1-x32d.google.com with SMTP id 5b1f17b1804b1-40c2db0ca48so28865e9.1 for ; Tue, 12 Dec 2023 23:13:33 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1702451612; x=1703056412; darn=vger.kernel.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=ofKUl2GizOp1QuJPlnk+EngBOStr6Eba6g1R8JbUe4U=; b=MUC7AwggQzTpjMYTP6ywKpThCx17IMlltG6xXHNdKyVPsjx9nFzkoPHVXS0mqJLggF qXpNjmNiPdgFnUTKxqdtaa/WsbEGVBUgHoD+15E0nnZm1N9o3qPOcUHPiTJraMQbpSOj iRPcPxLc9oGXqQbKEURkka910tYf/Te1zjZe64j9WsO/fuE94v2MNTd1VjKSUunuVKib 8/ptNmP6wpdHHy+vYd5wnlDHw2gmJ8i5OJFuITCbta/n3Kqbu16pmvV0eBSI8FCiFnWp RTEiskSwzpAe03fIDrEWi+Zpyw/kHPkzqvPx0t+Uxn71Moxd6eN6zCHCc43Q4pOf+Pds aG7g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1702451612; x=1703056412; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=ofKUl2GizOp1QuJPlnk+EngBOStr6Eba6g1R8JbUe4U=; b=KaGFxiioNe8pZ5amKon8i7KCD0fNMgeH1UQSsxhT2G2r+ZxgNhS63Jw2DDQoMhjcZ4 nS8rNFSaLSbZsAR6Iq/51mEpwhniYTcABtcgo8Kh8MrGPwHzUDtBRT8P6FapttAm7x/R US08BCaJZcVqLJae8b2lrQh4+N2Ksl4c1d7sf5/R1fLtywpo4R5G83Dxc2G/Of+ZGIA5 Bey2ATFKqvujl9wZCegHQoYuODPpT6aI1EbyIQJupYSTGkE8jabfXyQVbU9ggDLY0qHa SQJLhhWRQrarxKzsNYYRISqXlj9cRyalV1TN1TABpHJFfyRLMFlpMOU09T3C2c1ORLAt 2UcQ== X-Gm-Message-State: AOJu0Yzzgkinv/AZx/b/uMWBjmcDd13/vxHfSBmB3UBeIPqt0COXq0aV mxjpa2CbtXUWgW+G41jPtNf23tfONGJuiw/IGLMkfQ== X-Received: by 2002:a05:600c:3491:b0:40b:33aa:a2b9 with SMTP id a17-20020a05600c349100b0040b33aaa2b9mr407430wmq.4.1702451612168; Tue, 12 Dec 2023 23:13:32 -0800 (PST) MIME-Version: 1.0 References: <20231213010201.1802507-1-rmoar@google.com> <20231213010201.1802507-2-rmoar@google.com> In-Reply-To: <20231213010201.1802507-2-rmoar@google.com> From: David Gow Date: Wed, 13 Dec 2023 15:13:19 +0800 Message-ID: Subject: Re: [PATCH v4 2/6] kunit: add KUNIT_INIT_TABLE to init linker section To: Rae Moar Cc: shuah@kernel.org, dlatypov@google.com, brendan.higgins@linux.dev, sadiyakazi@google.com, keescook@chromium.org, arnd@arndb.de, linux-kselftest@vger.kernel.org, linux-arch@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org Content-Type: multipart/signed; protocol="application/pkcs7-signature"; micalg=sha-256; boundary="0000000000009d9d1d060c5ee7c5" X-Spam-Status: No, score=-8.4 required=5.0 tests=DKIMWL_WL_MED,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE, USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on howler.vger.email Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Greylist: Sender passed SPF test, not delayed by milter-greylist-4.6.4 (howler.vger.email [0.0.0.0]); Tue, 12 Dec 2023 23:14:20 -0800 (PST) --0000000000009d9d1d060c5ee7c5 Content-Type: text/plain; charset="UTF-8" On Wed, 13 Dec 2023 at 09:02, Rae Moar wrote: > > Add KUNIT_INIT_TABLE to the INIT_DATA linker section. > > Alter the KUnit macros to create init tests: > kunit_test_init_section_suites > > Update lib/kunit/executor.c to run both the suites in KUNIT_TABLE and > KUNIT_INIT_TABLE. > > Signed-off-by: Rae Moar > --- > Changes since v3: > - Add to comments in test.h for kunit_test_init_section_suites macro to > note init tests cannot be run after boot and the structs cannot be > marked with __initdata > Thanks -- this is looking good. Reviewed-by: David Gow Cheers, -- David > include/asm-generic/vmlinux.lds.h | 9 ++++- > include/kunit/test.h | 30 +++++++++------ > include/linux/module.h | 2 + > kernel/module/main.c | 3 ++ > lib/kunit/executor.c | 64 ++++++++++++++++++++++++++++--- > lib/kunit/test.c | 26 +++++++++---- > 6 files changed, 109 insertions(+), 25 deletions(-) > > diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h > index 1107905d37fc..5dd3a61d673d 100644 > --- a/include/asm-generic/vmlinux.lds.h > +++ b/include/asm-generic/vmlinux.lds.h > @@ -700,7 +700,8 @@ > THERMAL_TABLE(governor) \ > EARLYCON_TABLE() \ > LSM_TABLE() \ > - EARLY_LSM_TABLE() > + EARLY_LSM_TABLE() \ > + KUNIT_INIT_TABLE() > > #define INIT_TEXT \ > *(.init.text .init.text.*) \ > @@ -926,6 +927,12 @@ > . = ALIGN(8); \ > BOUNDED_SECTION_POST_LABEL(.kunit_test_suites, __kunit_suites, _start, _end) > > +/* Alignment must be consistent with (kunit_suite *) in include/kunit/test.h */ > +#define KUNIT_INIT_TABLE() \ > + . = ALIGN(8); \ > + BOUNDED_SECTION_POST_LABEL(.kunit_init_test_suites, \ > + __kunit_init_suites, _start, _end) > + > #ifdef CONFIG_BLK_DEV_INITRD > #define INIT_RAM_FS \ > . = ALIGN(4); \ > diff --git a/include/kunit/test.h b/include/kunit/test.h > index 20ed9f9275c9..fe79cd736e94 100644 > --- a/include/kunit/test.h > +++ b/include/kunit/test.h > @@ -337,6 +337,9 @@ void __kunit_test_suites_exit(struct kunit_suite **suites, int num_suites); > void kunit_exec_run_tests(struct kunit_suite_set *suite_set, bool builtin); > void kunit_exec_list_tests(struct kunit_suite_set *suite_set, bool include_attr); > > +struct kunit_suite_set kunit_merge_suite_sets(struct kunit_suite_set init_suite_set, > + struct kunit_suite_set suite_set); > + > #if IS_BUILTIN(CONFIG_KUNIT) > int kunit_run_all_tests(void); > #else > @@ -371,6 +374,11 @@ static inline int kunit_run_all_tests(void) > > #define kunit_test_suite(suite) kunit_test_suites(&suite) > > +#define __kunit_init_test_suites(unique_array, ...) \ > + static struct kunit_suite *unique_array[] \ > + __aligned(sizeof(struct kunit_suite *)) \ > + __used __section(".kunit_init_test_suites") = { __VA_ARGS__ } > + > /** > * kunit_test_init_section_suites() - used to register one or more &struct > * kunit_suite containing init functions or > @@ -378,21 +386,21 @@ static inline int kunit_run_all_tests(void) > * > * @__suites: a statically allocated list of &struct kunit_suite. > * > - * This functions identically as kunit_test_suites() except that it suppresses > - * modpost warnings for referencing functions marked __init or data marked > - * __initdata; this is OK because currently KUnit only runs tests upon boot > - * during the init phase or upon loading a module during the init phase. > + * This functions similar to kunit_test_suites() except that it compiles the > + * list of suites during init phase. > + * > + * This macro also suffixes the array and suite declarations it makes with > + * _probe; so that modpost suppresses warnings about referencing init data > + * for symbols named in this manner. > * > - * NOTE TO KUNIT DEVS: If we ever allow KUnit tests to be run after boot, these > - * tests must be excluded. > + * Note: these init tests are not able to be run after boot so there is no > + * "run" debugfs file generated for these tests. > * > - * The only thing this macro does that's different from kunit_test_suites is > - * that it suffixes the array and suite declarations it makes with _probe; > - * modpost suppresses warnings about referencing init data for symbols named in > - * this manner. > + * Also, do not mark the suite or test case structs with __initdata because > + * they will be used after the init phase with debugfs. > */ > #define kunit_test_init_section_suites(__suites...) \ > - __kunit_test_suites(CONCATENATE(__UNIQUE_ID(array), _probe), \ > + __kunit_init_test_suites(CONCATENATE(__UNIQUE_ID(array), _probe), \ > ##__suites) > > #define kunit_test_init_section_suite(suite) \ > diff --git a/include/linux/module.h b/include/linux/module.h > index a98e188cf37b..9cd0009bd050 100644 > --- a/include/linux/module.h > +++ b/include/linux/module.h > @@ -540,6 +540,8 @@ struct module { > struct static_call_site *static_call_sites; > #endif > #if IS_ENABLED(CONFIG_KUNIT) > + int num_kunit_init_suites; > + struct kunit_suite **kunit_init_suites; > int num_kunit_suites; > struct kunit_suite **kunit_suites; > #endif > diff --git a/kernel/module/main.c b/kernel/module/main.c > index 98fedfdb8db5..36681911c05a 100644 > --- a/kernel/module/main.c > +++ b/kernel/module/main.c > @@ -2199,6 +2199,9 @@ static int find_module_sections(struct module *mod, struct load_info *info) > mod->kunit_suites = section_objs(info, ".kunit_test_suites", > sizeof(*mod->kunit_suites), > &mod->num_kunit_suites); > + mod->kunit_init_suites = section_objs(info, ".kunit_init_test_suites", > + sizeof(*mod->kunit_init_suites), > + &mod->num_kunit_init_suites); > #endif > > mod->extable = section_objs(info, "__ex_table", > diff --git a/lib/kunit/executor.c b/lib/kunit/executor.c > index 1236b3cd2fbb..847329c51e91 100644 > --- a/lib/kunit/executor.c > +++ b/lib/kunit/executor.c > @@ -12,6 +12,8 @@ > */ > extern struct kunit_suite * const __kunit_suites_start[]; > extern struct kunit_suite * const __kunit_suites_end[]; > +extern struct kunit_suite * const __kunit_init_suites_start[]; > +extern struct kunit_suite * const __kunit_init_suites_end[]; > > static char *action_param; > > @@ -292,6 +294,33 @@ void kunit_exec_list_tests(struct kunit_suite_set *suite_set, bool include_attr) > } > } > > +struct kunit_suite_set kunit_merge_suite_sets(struct kunit_suite_set init_suite_set, > + struct kunit_suite_set suite_set) > +{ > + struct kunit_suite_set total_suite_set = {NULL, NULL}; > + struct kunit_suite **total_suite_start = NULL; > + size_t init_num_suites, num_suites, suite_size; > + > + init_num_suites = init_suite_set.end - init_suite_set.start; > + num_suites = suite_set.end - suite_set.start; > + suite_size = sizeof(suite_set.start); > + > + /* Allocate memory for array of all kunit suites */ > + total_suite_start = kmalloc_array(init_num_suites + num_suites, suite_size, GFP_KERNEL); > + if (!total_suite_start) > + return total_suite_set; > + > + /* Append init suites and then all other kunit suites */ > + memcpy(total_suite_start, init_suite_set.start, init_num_suites * suite_size); > + memcpy(total_suite_start + init_num_suites, suite_set.start, num_suites * suite_size); > + > + /* Set kunit suite set start and end */ > + total_suite_set.start = total_suite_start; > + total_suite_set.end = total_suite_start + (init_num_suites + num_suites); > + > + return total_suite_set; > +} > + > #if IS_BUILTIN(CONFIG_KUNIT) > > static char *kunit_shutdown; > @@ -313,21 +342,41 @@ static void kunit_handle_shutdown(void) > > int kunit_run_all_tests(void) > { > - struct kunit_suite_set suite_set = { > + struct kunit_suite_set suite_set = {NULL, NULL}; > + struct kunit_suite_set filtered_suite_set = {NULL, NULL}; > + struct kunit_suite_set init_suite_set = { > + __kunit_init_suites_start, __kunit_init_suites_end, > + }; > + struct kunit_suite_set normal_suite_set = { > __kunit_suites_start, __kunit_suites_end, > }; > + size_t init_num_suites = init_suite_set.end - init_suite_set.start; > int err = 0; > + > + if (init_num_suites > 0) { > + suite_set = kunit_merge_suite_sets(init_suite_set, normal_suite_set); > + if (!suite_set.start) > + goto out; > + } else > + suite_set = normal_suite_set; > + > if (!kunit_enabled()) { > pr_info("kunit: disabled\n"); > - goto out; > + goto free_out; > } > > if (filter_glob_param || filter_param) { > - suite_set = kunit_filter_suites(&suite_set, filter_glob_param, > + filtered_suite_set = kunit_filter_suites(&suite_set, filter_glob_param, > filter_param, filter_action_param, &err); > + > + /* Free original suite set before using filtered suite set */ > + if (init_num_suites > 0) > + kfree(suite_set.start); > + suite_set = filtered_suite_set; > + > if (err) { > pr_err("kunit executor: error filtering suites: %d\n", err); > - goto out; > + goto free_out; > } > } > > @@ -340,9 +389,12 @@ int kunit_run_all_tests(void) > else > pr_err("kunit executor: unknown action '%s'\n", action_param); > > - if (filter_glob_param || filter_param) { /* a copy was made of each suite */ > +free_out: > + if (filter_glob_param || filter_param) > kunit_free_suite_set(suite_set); > - } > + else if (init_num_suites > 0) > + /* Don't use kunit_free_suite_set because suites aren't individually allocated */ > + kfree(suite_set.start); > > out: > kunit_handle_shutdown(); > diff --git a/lib/kunit/test.c b/lib/kunit/test.c > index 0308865194bb..6c082911a85f 100644 > --- a/lib/kunit/test.c > +++ b/lib/kunit/test.c > @@ -704,28 +704,40 @@ EXPORT_SYMBOL_GPL(__kunit_test_suites_exit); > #ifdef CONFIG_MODULES > static void kunit_module_init(struct module *mod) > { > - struct kunit_suite_set suite_set = { > + struct kunit_suite_set suite_set, filtered_set; > + struct kunit_suite_set normal_suite_set = { > mod->kunit_suites, mod->kunit_suites + mod->num_kunit_suites, > }; > + struct kunit_suite_set init_suite_set = { > + mod->kunit_init_suites, mod->kunit_init_suites + mod->num_kunit_init_suites, > + }; > const char *action = kunit_action(); > int err = 0; > > - suite_set = kunit_filter_suites(&suite_set, > + if (mod->num_kunit_init_suites > 0) > + suite_set = kunit_merge_suite_sets(init_suite_set, normal_suite_set); > + else > + suite_set = normal_suite_set; > + > + filtered_set = kunit_filter_suites(&suite_set, > kunit_filter_glob() ?: "*.*", > kunit_filter(), kunit_filter_action(), > &err); > if (err) > pr_err("kunit module: error filtering suites: %d\n", err); > > - mod->kunit_suites = (struct kunit_suite **)suite_set.start; > - mod->num_kunit_suites = suite_set.end - suite_set.start; > + mod->kunit_suites = (struct kunit_suite **)filtered_set.start; > + mod->num_kunit_suites = filtered_set.end - filtered_set.start; > + > + if (mod->num_kunit_init_suites > 0) > + kfree(suite_set.start); > > if (!action) > - kunit_exec_run_tests(&suite_set, false); > + kunit_exec_run_tests(&filtered_set, false); > else if (!strcmp(action, "list")) > - kunit_exec_list_tests(&suite_set, false); > + kunit_exec_list_tests(&filtered_set, false); > else if (!strcmp(action, "list_attr")) > - kunit_exec_list_tests(&suite_set, true); > + kunit_exec_list_tests(&filtered_set, true); > else > pr_err("kunit: unknown action '%s'\n", action); > } > -- > 2.43.0.472.g3155946c3a-goog > --0000000000009d9d1d060c5ee7c5 Content-Type: application/pkcs7-signature; name="smime.p7s" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="smime.p7s" Content-Description: S/MIME Cryptographic Signature MIIPnwYJKoZIhvcNAQcCoIIPkDCCD4wCAQExDzANBglghkgBZQMEAgEFADALBgkqhkiG9w0BBwGg ggz5MIIEtjCCA56gAwIBAgIQeAMYYHb81ngUVR0WyMTzqzANBgkqhkiG9w0BAQsFADBMMSAwHgYD VQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UE AxMKR2xvYmFsU2lnbjAeFw0yMDA3MjgwMDAwMDBaFw0yOTAzMTgwMDAwMDBaMFQxCzAJBgNVBAYT AkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMSowKAYDVQQDEyFHbG9iYWxTaWduIEF0bGFz IFIzIFNNSU1FIENBIDIwMjAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvLe9xPU9W dpiHLAvX7kFnaFZPuJLey7LYaMO8P/xSngB9IN73mVc7YiLov12Fekdtn5kL8PjmDBEvTYmWsuQS 6VBo3vdlqqXZ0M9eMkjcKqijrmDRleudEoPDzTumwQ18VB/3I+vbN039HIaRQ5x+NHGiPHVfk6Rx c6KAbYceyeqqfuJEcq23vhTdium/Bf5hHqYUhuJwnBQ+dAUcFndUKMJrth6lHeoifkbw2bv81zxJ I9cvIy516+oUekqiSFGfzAqByv41OrgLV4fLGCDH3yRh1tj7EtV3l2TngqtrDLUs5R+sWIItPa/4 AJXB1Q3nGNl2tNjVpcSn0uJ7aFPbAgMBAAGjggGKMIIBhjAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0l BBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMEMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFHzM CmjXouseLHIb0c1dlW+N+/JjMB8GA1UdIwQYMBaAFI/wS3+oLkUkrk1Q+mOai97i3Ru8MHsGCCsG AQUFBwEBBG8wbTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AyLmdsb2JhbHNpZ24uY29tL3Jvb3Ry MzA7BggrBgEFBQcwAoYvaHR0cDovL3NlY3VyZS5nbG9iYWxzaWduLmNvbS9jYWNlcnQvcm9vdC1y My5jcnQwNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9iYWxzaWduLmNvbS9yb290LXIz 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 ddIn9n5Jt6Z1o79zxraLMEwGA1UdIARFMEMwQQYJKwYBBAGgMgEoMDQwMgYIKwYBBQUHAgEWJmh0 dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMAwGA1UdEwEB/wQCMAAwgZoGCCsG AQUFBwEBBIGNMIGKMD4GCCsGAQUFBzABhjJodHRwOi8vb2NzcC5nbG9iYWxzaWduLmNvbS9jYS9n c2F0bGFzcjNzbWltZWNhMjAyMDBIBggrBgEFBQcwAoY8aHR0cDovL3NlY3VyZS5nbG9iYWxzaWdu LmNvbS9jYWNlcnQvZ3NhdGxhc3Izc21pbWVjYTIwMjAuY3J0MB8GA1UdIwQYMBaAFHzMCmjXouse LHIb0c1dlW+N+/JjMEYGA1UdHwQ/MD0wO6A5oDeGNWh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20v Y2EvZ3NhdGxhc3Izc21pbWVjYTIwMjAuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQBtHFwIgQZjer5K H+4Q+wns10k7qN+4wN2Uf+JsyOYjukaMEgdLErfA1wwtQ9uHkoYQZcWBuVVkQFa5hI+sqI2m1Weq riMCFSiU38s1tADdMX12IMfJRN60Nznhrw+nPyDRZqRhUTW24TwnHorkDnFPW8PHo7fAw4FrpI0n impZAng7ccvvK09K3ZuhwTIxJMsPXCZYsrXWORTw5sczRAP6XvKbPBJnsJoSTe5dFBPBHOQJOGhU qWfEfWnWMJPF3LxSGLpLFQXO3RwQqmxv08avwXfVPouh1xuB3FX7rpDabT8YDhu9JgIZkLEKko7L yQt6zWwng7k8YF/jGbiAta6VMYICajCCAmYCAQEwaDBUMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQ R2xvYmFsU2lnbiBudi1zYTEqMCgGA1UEAxMhR2xvYmFsU2lnbiBBdGxhcyBSMyBTTUlNRSBDQSAy MDIwAhABzgV+4+mJnUzG7XDy6d2uMA0GCWCGSAFlAwQCAQUAoIHUMC8GCSqGSIb3DQEJBDEiBCBG xiWS3SEVkshWO3xjKqJnjCEtrkFsyozx85E8qVvMGjAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcB MBwGCSqGSIb3DQEJBTEPFw0yMzEyMTMwNzEzMzJaMGkGCSqGSIb3DQEJDzFcMFowCwYJYIZIAWUD BAEqMAsGCWCGSAFlAwQBFjALBglghkgBZQMEAQIwCgYIKoZIhvcNAwcwCwYJKoZIhvcNAQEKMAsG CSqGSIb3DQEBBzALBglghkgBZQMEAgEwDQYJKoZIhvcNAQEBBQAEggEAmJx53xZ8GhXUsXqlD+Z/ Yq4WY3MSfI5cypG3Ocod2ZMCAL/u6kwceb0YfIO/b3LkO63CFQtfizlIGUGCR1JGgITP0QNrpjHS z4+GTESTq86yz00Tp9AhSyMqkuSXjFcJMYybNpwxNeZu7RKhTYKmDmN2nerkcoUG8OTyCbNlDFFI mDY9fVz11fRFCkcbvVZKirQC1G/z5Ds8DhxRfchTB2iFqgWMwkIUkkFuVP9tQqr6WUJxBnEbPJ6d IcDtbib3tfDM+X5PuG7J+JhoYXy2zwl0ZKaUQdz8ckSN8e84/YthDTgPP4gscsbBTVDYBrB2x0Gc CWrJARx3+yxzY8LxLA== --0000000000009d9d1d060c5ee7c5--