From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from casper.infradead.org (casper.infradead.org [90.155.50.34]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C772926158C for ; Sat, 16 May 2026 08:25:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=90.155.50.34 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778919946; cv=none; b=bsZXsypBa5xFEN9hXH1pMO4RvhCoY4urbzlwlg/KswV+fImNrr64qC5cTrqJdecgzerkmGbgGuZV7XOd8tk9HGe14vQ9DhvaBPf0VvsEovEv8hdwlCq6D0h209RHU+U6go0opp6oYaQy76AVMrIPeRyPNKaRO2zeXfTRmILUF3Y= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778919946; c=relaxed/simple; bh=GKLHJo1PqInbNRjDE7o1POO6ZCErlFX0yDfmhT1EPEE=; h=Message-ID:Subject:From:To:Cc:Date:In-Reply-To:References: Content-Type:MIME-Version; b=Jtq6f/EGKNmw8c9gnfT9VMA7mIyedqI0aFP6D6KaOMD76fJp/A/+09FYSSF6ketPmIa8UxyIkXBjXWzZpVFVplNQMpnQd2ai2ArGYNTc8u8g+czAsxzBafhHjU31CROME98RShfjHXpxJuuwdyiLZmoNt6CZ2Bb/0eC0C/r9NLQ= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=infradead.org; spf=none smtp.mailfrom=casper.srs.infradead.org; dkim=pass (2048-bit key) header.d=infradead.org header.i=@infradead.org header.b=WrS64VRT; arc=none smtp.client-ip=90.155.50.34 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=infradead.org Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=casper.srs.infradead.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=infradead.org header.i=@infradead.org header.b="WrS64VRT" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=infradead.org; s=casper.20170209; h=MIME-Version:Content-Type:References: In-Reply-To:Date:Cc:To:From:Subject:Message-ID:Sender:Reply-To: Content-Transfer-Encoding:Content-ID:Content-Description; bh=cWzH0a3/GPyukqM1XKh8/FuQmwKEPVJE3pSwQ6/8Tco=; b=WrS64VRTMjk4ldrT7D18GZSv5Y 0STxdZalnwnyMjPlQgiT6hkT2xc/b3RxejH9bI+Iv67Y/THoCZZ3hExodEvUe5iRx0VducsaiQZ/e m7WHpgeRv/LUC6+pYhzIVXdCz9mc0POb3aGn5Sm5FaqBv/Rf+uigbGcuAb0lMkRMsXO+cm7JSIBXp pwY/OlR75/Xkw+0Ot8o+GvlMDgulozcYSPeon/Vf4jPso4YhGP1Eb0kiC1dtv0sm4HrIefYLm21G/ ZdXtAFFPxca2All26y6XLRhncwysuW5dBjLnzqVOvi5qpRmfVb3DpIyDLbAXovIL3B3z4Yr0D8MbX Yqvt88zQ==; Received: from 54-240-197-239.amazon.com ([54.240.197.239] helo=u09cd745991455d.ant.amazon.com) by casper.infradead.org with esmtpsa (Exim 4.99.1 #2 (Red Hat Linux)) id 1wOAKp-00000002eSC-06Bs; Sat, 16 May 2026 08:25:23 +0000 Message-ID: <5b8a8117a5c45482a515d9fcf026b2a1ae7c5176.camel@infradead.org> Subject: Re: [RFC PATCH 1/4] timekeeping: Remove xtime_remainder from ntp_error accumulation From: David Woodhouse To: John Stultz , Miroslav Lichvar Cc: Richard Cochran , Wen Gu , Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Thomas Gleixner , Stephen Boyd , Anna-Maria Behnsen , Frederic Weisbecker , Shuah Khan , Peter Zijlstra , Thomas =?ISO-8859-1?Q?Wei=DFschuh?= , Arnd Bergmann , linux-kernel@vger.kernel.org Date: Sat, 16 May 2026 09:25:21 +0100 In-Reply-To: References: <20260513210157.3410814-1-dwmw2@infradead.org> <20260513210157.3410814-2-dwmw2@infradead.org> Content-Type: multipart/signed; micalg="sha-256"; protocol="application/pkcs7-signature"; boundary="=-BLPKmqDbyL9Tq39e58ao" User-Agent: Evolution 3.52.3-0ubuntu1.1 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-SRS-Rewrite: SMTP reverse-path rewritten from by casper.infradead.org. See http://www.infradead.org/rpr.html --=-BLPKmqDbyL9Tq39e58ao Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Fri, 2026-05-15 at 15:49 -0700, John Stultz wrote: > On Wed, May 13, 2026 at 2:02=E2=80=AFPM David Woodhouse wrote: > >=20 > > From: David Woodhouse > >=20 > > The ntp_error accumulator tracks the difference between intended and > > actual clock advance. Each tick it adds ntp_tick (the intended advance) > > and subtracts what the clock actually advanced. > >=20 > > The subtraction was (xtime_interval + xtime_remainder), but only > > xtime_interval is actually added to xtime_nsec each tick. > > xtime_remainder was a boot-time constant representing the rounding erro= r > > from converting the tick period to an integer number of counter cycles. > > It was never added to xtime_nsec, so subtracting it from ntp_error > > created a phantom credit that biased the dithering ratio. > >=20 > > The effect is a systematic drift whose magnitude depends on the value o= f > > xtime_remainder and the NTP frequency correction. NTP masks this by > > continuously adjusting the frequency to compensate, but with a fixed > > frequency (or an external reference clock like vmclock), the drift is > > exposed. > >=20 > > Also remove xtime_remainder from the mult computation in > > timekeeping_adjust(), which used it to offset the division for the same > > (incorrect) reason. > >=20 > > Fixes: a386b5af8edd ("time: Compensate for rounding on odd-frequency cl= ocksources") >=20 > Hey, thanks for sending this out! >=20 > Looks like it would be more accurate to say it is a revert then a fix? >=20 > Hrm. So this is maybe too far back for my goldfish brain to really rememb= er. >=20 > My hazy sense of it was that for coarser-grained clocksources, there > will be an inherent granularity error for what we want to use as an > accumulation interval, and the hardware allows. >=20 > So if we want a 1ms ntp interval, taking the hardware freq and finding > cycles/ms, might be something like 10.4 (imaginary example here), so > that rounds 10 cycles per interval which is really gives us a 962us > interval. >=20 > Now, the 10.4 cycles/ms is still accurate, and the resulting > xtime_interval is still correct, but there is a resulting 38us > difference between the xtime_interval and the ntp_tick due to this > granularity error in the calculation., When we start accumulating > ntp_error, it will grow at that 38us/ms rate, causing the internal > logic to try to steer the clock faster to compensate. >=20 > Thus, instead of assuming the ntp-interval is actually 1ms, we want it > to match the actual accumulation interval, so we subtract that error > off of the ntp-interval.=C2=A0 That "phantom credit" is effectively > intended to address this inherent error (which is why it isn't applied > to the other side - since it's already there as part of the > calculation). >=20 > Now, again, it's been far too long, and I may be misremembering > details, and the correction may very well be unnecessary or otherwise > erronious! But I think it deserves a bit more evidence/rationale in > the commit message as to why. Thanks. This has been making my brain hurt for most of the last week, but I think I finally have a handle on it. It looks like we track three different times (ignoring units): =E2=80=A2 A: The xtime that we actually output to the vDSO/etc. =E2=80=A2 B: xtime+ntp_error is the time we *want* to be outputting right = now, but the mult dithering and monotonicity clawback keep us from it. =E2=80=A2 C: xtime+ntp_error+time_offset is the time we *eventually* want = to output, once we've finished skewing towards it. On each tick: =E2=80=A2 xtime (A) advances by xtime_interval (and the clawback; there's another patch in my tree to account for that in ntp_error now). =E2=80=A2 (B) advances by whatever tick_length happens to be at the moment (adjusted by second_overflow to effect a skew). =E2=80=A2 (C) advances by the original tick_length_base actually set accor= ding to the adjtimex frequency. So ntp_error, being the delta between (B) and (A), needs to advance by tick_length - xtime_interval. Before this patch, xtime_remainder was *also* being subtracted from the 'what xtime advanced' side, but it isn't actually added to xtime; it *is* roughly the amount that needed to be accumulated in ntp_error here (except for the fact that xtime_remainder was calculated once at boot time and never updated). I spent a while booting kernels in QEMU with a VMClock reference clock precisely specifying the TSC to real-time relationship for (C), and tracking the *nanosecond* delta of the output from what it was told. I made it redundantly track (B) and (C) above as absolute values, so that I could spot per-tick when the accounting of the existing *deltas* in ntp_error and time_offset went astray (and compare C with the actual refclock, to check that too). In my test tree, xtime now correctly dithers precisely around the desired time (B) and doesn't continually drift from it any more. And I can even inject, say, a 10=CE=BCs delta via time_offset and it'll ske= w by *exactly* 10,000ns and stay there, still with with single digit nanosecond divergence from where it's meant to be. So now I don't *need* the external oracle to drive the dithering per tick for the reference clock (as I had added in patch 3 in this series), because the kernel can actually stay on the y=3Dmx+c line configured by tick_length and ntp_error/time_offset all by itself, so all I have to do is set the *existing* parameters in timekeeping_set_reference().=20 (Arithmetic precision would *eventually* catch up with it, of course, but in reality it won't be following a hard-coded refclock and the reference itself will be periodically adjusted as the real counter varies, and reclamping will occur.) I don't much like the complexity of the way that time_offset skews ntp_tick, and it's somewhat redundant with the mult dithering that came later. I'm playing with folding both time_offset and ntp_error into a single 'phase_error' and doing the larger skew purely by adjusting mult, like the dithering does... but by larger values. That's a WIP at the top of my current working tree: David Woodhouse (6): timekeeping: Remove xtime_remainder from ntp_error accumulation timekeeping: Account for clawback adjustment in ntp_error timekeeping: Clamp time_offset delta to prevent infinite tail timekeeping: Add absolute reference for feed-forward clock discipline ptp_vmclock: Feed reference to timekeeping for feed-forward disciplin= e timekeeping: Unify ntp_error and time_offset into phase_error https://git.infradead.org/?p=3Dusers/dwmw2/linux.git;a=3Dshortlog;h=3Drefs/= heads/ffclock I believe everything in there is correctly parameterised and nothing makes assumptions that are invalid for different CONFIG_HZ and for much lower-rate timers. I'm almost at the point of being happy enough with the overall state that I'm ready to start testing those cases too... --=-BLPKmqDbyL9Tq39e58ao Content-Type: application/pkcs7-signature; name="smime.p7s" Content-Disposition: attachment; filename="smime.p7s" Content-Transfer-Encoding: base64 MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwEAAKCCD9Aw ggSOMIIDdqADAgECAhAOmiw0ECVD4cWj5DqVrT9PMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYT AlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAi BgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yNDAxMzAwMDAwMDBaFw0zMTEx MDkyMzU5NTlaMEExCzAJBgNVBAYTAkFVMRAwDgYDVQQKEwdWZXJva2V5MSAwHgYDVQQDExdWZXJv a2V5IFNlY3VyZSBFbWFpbCBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMjvgLKj jfhCFqxYyRiW8g3cNFAvltDbK5AzcOaR7yVzVGadr4YcCVxjKrEJOgi7WEOH8rUgCNB5cTD8N/Et GfZI+LGqSv0YtNa54T9D1AWJy08ZKkWvfGGIXN9UFAPMJ6OLLH/UUEgFa+7KlrEvMUupDFGnnR06 aDJAwtycb8yXtILj+TvfhLFhafxroXrflspavejQkEiHjNjtHnwbZ+o43g0/yxjwnarGI3kgcak7 nnI9/8Lqpq79tLHYwLajotwLiGTB71AGN5xK+tzB+D4eN9lXayrjcszgbOv2ZCgzExQUAIt98mre 8EggKs9mwtEuKAhYBIP/0K6WsoMnQCcCAwEAAaOCAVwwggFYMBIGA1UdEwEB/wQIMAYBAf8CAQAw HQYDVR0OBBYEFIlICOogTndrhuWByNfhjWSEf/xwMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6en IZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDBAYIKwYBBQUHAwIweQYI KwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYB BQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RD QS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0 QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQELBQADggEB ACiagCqvNVxOfSd0uYfJMiZsOEBXAKIR/kpqRp2YCfrP4Tz7fJogYN4fxNAw7iy/bPZcvpVCfe/H /CCcp3alXL0I8M/rnEnRlv8ItY4MEF+2T/MkdXI3u1vHy3ua8SxBM8eT9LBQokHZxGUX51cE0kwa uEOZ+PonVIOnMjuLp29kcNOVnzf8DGKiek+cT51FvGRjV6LbaxXOm2P47/aiaXrDD5O0RF5SiPo6 xD1/ClkCETyyEAE5LRJlXtx288R598koyFcwCSXijeVcRvBB1cNOLEbg7RMSw1AGq14fNe2cH1HG W7xyduY/ydQt6gv5r21mDOQ5SaZSWC/ZRfLDuEYwggWbMIIEg6ADAgECAhAH5JEPagNRXYDiRPdl c1vgMA0GCSqGSIb3DQEBCwUAMEExCzAJBgNVBAYTAkFVMRAwDgYDVQQKEwdWZXJva2V5MSAwHgYD VQQDExdWZXJva2V5IFNlY3VyZSBFbWFpbCBHMjAeFw0yNDEyMzAwMDAwMDBaFw0yODAxMDQyMzU5 NTlaMB4xHDAaBgNVBAMME2R3bXcyQGluZnJhZGVhZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4IC DwAwggIKAoICAQDali7HveR1thexYXx/W7oMk/3Wpyppl62zJ8+RmTQH4yZeYAS/SRV6zmfXlXaZ sNOE6emg8WXLRS6BA70liot+u0O0oPnIvnx+CsMH0PD4tCKSCsdp+XphIJ2zkC9S7/yHDYnqegqt w4smkqUqf0WX/ggH1Dckh0vHlpoS1OoxqUg+ocU6WCsnuz5q5rzFsHxhD1qGpgFdZEk2/c//ZvUN i12vPWipk8TcJwHw9zoZ/ZrVNybpMCC0THsJ/UEVyuyszPtNYeYZAhOJ41vav1RhZJzYan4a1gU0 kKBPQklcpQEhq48woEu15isvwWh9/+5jjh0L+YNaN0I//nHSp6U9COUG9Z0cvnO8FM6PTqsnSbcc 0j+GchwOHRC7aP2t5v2stVx3KbptaYEzi4MQHxm/0+HQpMEVLLUiizJqS4PWPU6zfQTOMZ9uLQRR ci+c5xhtMEBszlQDOvEQcyEG+hc++fH47K+MmZz21bFNfoBxLP6bjR6xtPXtREF5lLXxp+CJ6KKS blPKeVRg/UtyJHeFKAZXO8Zeco7TZUMVHmK0ZZ1EpnZbnAhKE19Z+FJrQPQrlR0gO3lBzuyPPArV hvWxjlO7S4DmaEhLzarWi/ze7EGwWSuI2eEa/8zU0INUsGI4ywe7vepQz7IqaAovAX0d+f1YjbmC VsAwjhLmveFjNwIDAQABo4IBsDCCAawwHwYDVR0jBBgwFoAUiUgI6iBOd2uG5YHI1+GNZIR//HAw HQYDVR0OBBYEFFxiGptwbOfWOtMk5loHw7uqWUOnMDAGA1UdEQQpMCeBE2R3bXcyQGluZnJhZGVh ZC5vcmeBEGRhdmlkQHdvb2Rob3Uuc2UwFAYDVR0gBA0wCzAJBgdngQwBBQEBMA4GA1UdDwEB/wQE AwIF4DAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwewYDVR0fBHQwcjA3oDWgM4YxaHR0 cDovL2NybDMuZGlnaWNlcnQuY29tL1Zlcm9rZXlTZWN1cmVFbWFpbEcyLmNybDA3oDWgM4YxaHR0 cDovL2NybDQuZGlnaWNlcnQuY29tL1Zlcm9rZXlTZWN1cmVFbWFpbEcyLmNybDB2BggrBgEFBQcB AQRqMGgwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBABggrBgEFBQcwAoY0 aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL1Zlcm9rZXlTZWN1cmVFbWFpbEcyLmNydDANBgkq hkiG9w0BAQsFAAOCAQEAQXc4FPiPLRnTDvmOABEzkIumojfZAe5SlnuQoeFUfi+LsWCKiB8Uextv iBAvboKhLuN6eG/NC6WOzOCppn4mkQxRkOdLNThwMHW0d19jrZFEKtEG/epZ/hw/DdScTuZ2m7im 8ppItAT6GXD3aPhXkXnJpC/zTs85uNSQR64cEcBFjjoQDuSsTeJ5DAWf8EMyhMuD8pcbqx5kRvyt JPsWBQzv1Dsdv2LDPLNd/JUKhHSgr7nbUr4+aAP2PHTXGcEBh8lTeYea9p4d5k969pe0OHYMV5aL xERqTagmSetuIwolkAuBCzA9vulg8Y49Nz2zrpUGfKGOD0FMqenYxdJHgDCCBZswggSDoAMCAQIC EAfkkQ9qA1FdgOJE92VzW+AwDQYJKoZIhvcNAQELBQAwQTELMAkGA1UEBhMCQVUxEDAOBgNVBAoT B1Zlcm9rZXkxIDAeBgNVBAMTF1Zlcm9rZXkgU2VjdXJlIEVtYWlsIEcyMB4XDTI0MTIzMDAwMDAw MFoXDTI4MDEwNDIzNTk1OVowHjEcMBoGA1UEAwwTZHdtdzJAaW5mcmFkZWFkLm9yZzCCAiIwDQYJ KoZIhvcNAQEBBQADggIPADCCAgoCggIBANqWLse95HW2F7FhfH9bugyT/danKmmXrbMnz5GZNAfj Jl5gBL9JFXrOZ9eVdpmw04Tp6aDxZctFLoEDvSWKi367Q7Sg+ci+fH4KwwfQ8Pi0IpIKx2n5emEg nbOQL1Lv/IcNiep6Cq3DiyaSpSp/RZf+CAfUNySHS8eWmhLU6jGpSD6hxTpYKye7PmrmvMWwfGEP WoamAV1kSTb9z/9m9Q2LXa89aKmTxNwnAfD3Ohn9mtU3JukwILRMewn9QRXK7KzM+01h5hkCE4nj W9q/VGFknNhqfhrWBTSQoE9CSVylASGrjzCgS7XmKy/BaH3/7mOOHQv5g1o3Qj/+cdKnpT0I5Qb1 nRy+c7wUzo9OqydJtxzSP4ZyHA4dELto/a3m/ay1XHcpum1pgTOLgxAfGb/T4dCkwRUstSKLMmpL g9Y9TrN9BM4xn24tBFFyL5znGG0wQGzOVAM68RBzIQb6Fz758fjsr4yZnPbVsU1+gHEs/puNHrG0 9e1EQXmUtfGn4InoopJuU8p5VGD9S3Ikd4UoBlc7xl5yjtNlQxUeYrRlnUSmdlucCEoTX1n4UmtA 9CuVHSA7eUHO7I88CtWG9bGOU7tLgOZoSEvNqtaL/N7sQbBZK4jZ4Rr/zNTQg1SwYjjLB7u96lDP sipoCi8BfR35/ViNuYJWwDCOEua94WM3AgMBAAGjggGwMIIBrDAfBgNVHSMEGDAWgBSJSAjqIE53 a4blgcjX4Y1khH/8cDAdBgNVHQ4EFgQUXGIam3Bs59Y60yTmWgfDu6pZQ6cwMAYDVR0RBCkwJ4ET ZHdtdzJAaW5mcmFkZWFkLm9yZ4EQZGF2aWRAd29vZGhvdS5zZTAUBgNVHSAEDTALMAkGB2eBDAEF AQEwDgYDVR0PAQH/BAQDAgXgMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDBDB7BgNVHR8E dDByMDegNaAzhjFodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vVmVyb2tleVNlY3VyZUVtYWlsRzIu Y3JsMDegNaAzhjFodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vVmVyb2tleVNlY3VyZUVtYWlsRzIu Y3JsMHYGCCsGAQUFBwEBBGowaDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29t MEAGCCsGAQUFBzAChjRodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vVmVyb2tleVNlY3VyZUVt YWlsRzIuY3J0MA0GCSqGSIb3DQEBCwUAA4IBAQBBdzgU+I8tGdMO+Y4AETOQi6aiN9kB7lKWe5Ch 4VR+L4uxYIqIHxR7G2+IEC9ugqEu43p4b80LpY7M4KmmfiaRDFGQ50s1OHAwdbR3X2OtkUQq0Qb9 6ln+HD8N1JxO5nabuKbymki0BPoZcPdo+FeRecmkL/NOzzm41JBHrhwRwEWOOhAO5KxN4nkMBZ/w QzKEy4PylxurHmRG/K0k+xYFDO/UOx2/YsM8s138lQqEdKCvudtSvj5oA/Y8dNcZwQGHyVN5h5r2 nh3mT3r2l7Q4dgxXlovERGpNqCZJ624jCiWQC4ELMD2+6WDxjj03PbOulQZ8oY4PQUyp6djF0keA MYIDuzCCA7cCAQEwVTBBMQswCQYDVQQGEwJBVTEQMA4GA1UEChMHVmVyb2tleTEgMB4GA1UEAxMX VmVyb2tleSBTZWN1cmUgRW1haWwgRzICEAfkkQ9qA1FdgOJE92VzW+AwDQYJYIZIAWUDBAIBBQCg ggE3MBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTI2MDUxNjA4MjUy MVowLwYJKoZIhvcNAQkEMSIEIN+qN+CHZvsipeNDXOKzH0CxD3oY6up5OiFjuZlS4yhVMGQGCSsG AQQBgjcQBDFXMFUwQTELMAkGA1UEBhMCQVUxEDAOBgNVBAoTB1Zlcm9rZXkxIDAeBgNVBAMTF1Zl cm9rZXkgU2VjdXJlIEVtYWlsIEcyAhAH5JEPagNRXYDiRPdlc1vgMGYGCyqGSIb3DQEJEAILMVeg VTBBMQswCQYDVQQGEwJBVTEQMA4GA1UEChMHVmVyb2tleTEgMB4GA1UEAxMXVmVyb2tleSBTZWN1 cmUgRW1haWwgRzICEAfkkQ9qA1FdgOJE92VzW+AwDQYJKoZIhvcNAQEBBQAEggIA2Vdew4OoucBq hz/xVLycKpvYgOCkZWWQgIhe6NaYSqHi7MFWGmrFtx2d/cX58rFKwsAr+rn3WBwkeLGYL11w1CQg rtwH0YsUmfYk55ogKkzQ7Tn2fT2Rt+usD1oBOFCB9/848kSCxpUHWxuLumjnimqh8wZvWormS6b1 /HFIIH3KunJlvdN/JnOH8T6kM8sJEu8cUu8ORQSzauAWCgrTHt89zfZG38alIledwxCSHtZ4QxIH pc0eT5t3i6wpWiovKr5J2X6fegGS4f3cj01Alq8+cdlq/X8x1wbXwQog+EhJ7OLICoho5HnNaFrc KhdW8cjqrJcZj5+7NQRyV5AUF1062Vl62vQ3AvFA53Wmy2DsorItaqyCMDfpt/nzqzEGlNEEpW6I VYjsk0Fw/9AO+gHULgGTkQhRwNKPubP5dk4khr3ufxSplD9xRSfZpRnlKPVeVA7b6Xk1eZhxZNiW GZweoPPGEkRhJn76KbtD7ri1pCHUGCsp/fDOZqJ9/3VMmbE+udVHNdQoPXNNMzWGXrxbKEjppGKE vxCihrFHnPB+MtG19zM5LY2i0gPeEMhnqVT6U0vmx+/zBc+aDYok2CRkNRWcWB+FlHU72JowrA53 3ZwFXn3J1gPB+80W+43WsMU/jfgfLosHL66xWHmwFAEVjL8R/DfAKatAXoyNX4cAAAAAAAA= --=-BLPKmqDbyL9Tq39e58ao--