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 951A23E120D for ; Tue, 19 May 2026 09:16:24 +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=1779182187; cv=none; b=p6pvsvlHTL3SxJAfMRXXGCCewFZ9SFkxywD7TLQQi/Y+SFl6dHr+pWICUFi5En1CEsAF8CZdyaTkQnCb4nd/FmXFTNi+wBjLHpLJP8H5McrMZJXAg4zhBD8w5viOCP1IePBgrMh+eT4hk6mPoMzJVqfcqkkZYZTXZJktdfutfPM= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779182187; c=relaxed/simple; bh=YjsY/sou4lbgiqyLuDWZ//youuwEg/BCXJLuuXNsftw=; h=Message-ID:Subject:From:To:Cc:Date:In-Reply-To:References: Content-Type:MIME-Version; b=W3zc9FrrQUD8toFIh+bZCkgvbYPJQKuX0kbB8KIVX3PPaBdQyn5odnZ3wYipegMTvfVop8MUPDVFZnGd1i33ftndbcKGN2ma7PLvxVY6Uum4aiBAwvLZAX4mWQ1g1fhURn+8Sye1uHuxyvoMrVDbYtj38HFUKQQrCMf6NHM4uFc= 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=OF0MdbNG; 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="OF0MdbNG" 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=YjsY/sou4lbgiqyLuDWZ//youuwEg/BCXJLuuXNsftw=; b=OF0MdbNGmNItuX+SMDlP6Hfo0C 4jciTBc7jwkTBAfSGSfuZDieGhvxWUIFdYMp6U9oRmfFDPHdI/cHyE/GCFTQmkAh+F8A0T7HAMPDu 8HQDTWY7DXG8vNEKTm6HZK+S1/GkY0uKvLrqNG8jNcsTg3acx1BIWbtEZ2tEvXOgrrJgvXW8fsY7p EBtNF8GRHNa00Is8nBCjd1xSXcEXvFdbLNkHOBQifQyKIcYnCW8Gfmol2ts8GJWCpnxRyTieBA9qG Voys5xsnWHszfx5ZcMyjj5yT1yRaPfY3/nacK8V5Y4MB3mkEL2wcqkGSIwFPNCFKPNxyz2jZXMJZb n91KaI3A==; Received: from 54-240-197-233.amazon.com ([54.240.197.233] helo=u09cd745991455d.ant.amazon.com) by casper.infradead.org with esmtpsa (Exim 4.99.1 #2 (Red Hat Linux)) id 1wPGYa-00000005hkW-3dww; Tue, 19 May 2026 09:16:09 +0000 Message-ID: <112efe3ec75c3a0bf3ea9d53034a7dea9bd553c7.camel@infradead.org> Subject: Re: [RFC PATCH 1/4] timekeeping: Remove xtime_remainder from ntp_error accumulation From: David Woodhouse To: John Stultz Cc: Miroslav Lichvar , 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: Tue, 19 May 2026 10:16:07 +0100 In-Reply-To: References: <20260513210157.3410814-1-dwmw2@infradead.org> <20260513210157.3410814-2-dwmw2@infradead.org> <5b8a8117a5c45482a515d9fcf026b2a1ae7c5176.camel@infradead.org> Content-Type: multipart/signed; micalg="sha-256"; protocol="application/pkcs7-signature"; boundary="=-Wr3x245+ONWvxiwazedh" 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 --=-Wr3x245+ONWvxiwazedh Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Mon, 2026-05-18 at 18:37 -0700, John Stultz wrote: > On Sat, May 16, 2026 at 1:25=E2=80=AFAM David Woodhouse wrote: > > Thanks. This has been making my brain hurt for most of the last week, > > but I think I finally have a handle on it. > >=20 > > It looks like we track three different times (ignoring units): > >=20 > > =C2=A0=E2=80=A2 A: The xtime that we actually output to the vDSO/etc. > >=20 > > =C2=A0=E2=80=A2 B: xtime+ntp_error is the time we *want* to be outputti= ng right now, > > =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 but the mult dithering and monotonicity = clawback keep us from it. >=20 > I think of B, or specifically ntp_error, as the delta between our time > and what *NTP* (well, the in-kernel ntp machinery) wants the system to > be right now. Right. I think we're entirely on the same page there. The "right now" is the key. > As an aside, apologies if I'm asking obvious questions here,=C2=A0 some o= f > your terminology is unfamiliar. While it's not used around the code or > patches, I can understand the dithering metaphor for the long-term > error adjustments to effectively allow for sub-integer mult > adjustments over time (similar to b&w dithering to approximate levels > of grey), but there is also the common use of indecisively dithering > time away or the astrophotography sense of intentionlally adding > noise, which I worry might cause some confusion to others as to what > you mean. Indeed. The two adjacent values of 'mult' will effectively make xtime proceed slightly faster than, or slightly slower than, the actual desired rate. It is exactly 'dithering' in the sense of approximating levels of grey, tracking ntp_error precisely in order to choose between mult vs. mult+1 for the next tick. > Also I'm not sure its very clear what you mean by "monotonicity clawback"= . This is the offset applied by timekeeping_apply_adjustment() in order to ensure that the observed xtime remains monotonic when the dithering switches back from 'mult+1' to 'mult' and a consumer may have seen a 'later' time than it's about to set in {cycle_last,mult}. (In fact it also prevents a forward jump, but IIUC that's less critical.) That offset was not being accounted in ntp_error, and was accumulating over time in my tests. > > =C2=A0=E2=80=A2 C: xtime+ntp_error+time_offset is the time we *eventual= ly* want to > > =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 output, once we've finished skewing towa= rds it. >=20 > And yeah, I'd say C is where the userland NTP wants it to slew towards. >=20 >=20 > > On each tick: > >=20 > > =C2=A0=E2=80=A2 xtime (A) advances by xtime_interval (and the clawback;= there's > > =C2=A0=C2=A0 another patch in my tree to account for that in ntp_error = now). > >=20 > > =C2=A0=E2=80=A2 (B) advances by whatever tick_length happens to be at t= he moment > > =C2=A0=C2=A0 (adjusted by second_overflow to effect a skew). > >=20 > > =C2=A0=E2=80=A2 (C) advances by the original tick_length_base actually = set according > > =C2=A0=C2=A0 to the adjtimex frequency. > >=20 > > 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). >=20 > Again, I'm sure it could be miscalculated, or be misapplied, but as I > mentioned previously, the xtime_remainder is trying to address a > granularity error that is effectively baked into the delta between > xtime_interval and the initial ntp interval (essentially the initial > ntp_tick), which doesn't seem to be addressed here. My understanding is that xtime_remainder *is* the delta between the initial xtime_interval and the initial ntp_interval. Calculated once at boot and then permanently out of sync when mult and thus xtime_interval actually change. By simply adding ntp_interval and then subtracting the correct current value of xtime_interval, logarithmic_accumulation doesn't *need* a separate variable which tracks that delta. I don't quite understand why it ever did add it in the first place. > For fine-grained clocksources like the TSC its not likely a big issue, > but for coarser grained clocksources it seems like just removing this > would be a regression. I think it should be fine with coarser grained clocksources. The dithering sawtooth around the reference line will have a higher amplitude, but=C2=A0adding ntp_interval and subtracting xtime_interval for the mult value currently in effect is still the right thing to do. > > 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. > >=20 > > 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). > >=20 > > In my test tree, xtime now correctly dithers precisely around the > > desired time (B) and doesn't continually drift from it any more. > >=20 > > And I can even inject, say, a 10=CE=BCs delta via time_offset and it'll= skew > > by *exactly* 10,000ns and stay there, still with with single digit > > nanosecond divergence from where it's meant to be. >=20 > Sounds like very nice results! >=20 > > 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.) >=20 > I'm excited your efforts seem to be doing well! The ntp/timekeeping > core has been pretty static for awhile now, outside of some of the ptp > work, so its nice to see new major contributions. That said, it being > in place for so long, and apparently working for folks, means changes > do need close consideration to make sure we don't introduce subtle > regressions. >=20 Absolutely. And I do definitely also need to do more testing with coarser grained clock sources. I've obviously *tried* to ensure that nothing I've done makes assumptions based around my test environment which happens to be a 2400MHz TSC and CONFIG_HZ=3D1000, but we'll see how it fares in actual testing. I'll also need to test a nohz setup. As I see it, the timekeeping code already has a fairly clean split between the core timekeeper and the ntp adjustment. The former is *mostly* supposed to just keep the time progressing along a clean y=3Dmx+c relationship from counter to observed xtime, in the common case where time_offset is zero. And setting time_offset should change the value of 'c' in that formula and exponentially eventually converge to *precisely* that delta away from the link it was previously keeping. All I've done here, or so I believe, is make the timekeeping code better at following that line it's given. The way that the NTP side *gives* it that line to follow is fairly much untouched. --=-Wr3x245+ONWvxiwazedh 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 ggE3MBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTI2MDUxOTA5MTYw N1owLwYJKoZIhvcNAQkEMSIEIKQ28hieAual4BXTXxDp6mXA5AxKVWCGLr+3t7OrnZw6MGQGCSsG AQQBgjcQBDFXMFUwQTELMAkGA1UEBhMCQVUxEDAOBgNVBAoTB1Zlcm9rZXkxIDAeBgNVBAMTF1Zl cm9rZXkgU2VjdXJlIEVtYWlsIEcyAhAH5JEPagNRXYDiRPdlc1vgMGYGCyqGSIb3DQEJEAILMVeg VTBBMQswCQYDVQQGEwJBVTEQMA4GA1UEChMHVmVyb2tleTEgMB4GA1UEAxMXVmVyb2tleSBTZWN1 cmUgRW1haWwgRzICEAfkkQ9qA1FdgOJE92VzW+AwDQYJKoZIhvcNAQEBBQAEggIAlrhHbI72Ki7R INBIFsJcQRS6cW3lTgVLCL+eYp0sVmRtIHwGwXCMv40ZZBuKENkO+YuEJRTRHLKt80HZUNwwzXLs oQektDx+Ml1dkxKOSnut8obErpmFaoegsS5HtPG2koi1I0knTGurC2TYRfGhS+9oemAGKnMxSCzo EGzWCfD8FR3GqAT1L/6woDujk+fwwoE+pXcc4bUdXpeXhmE8qtkICBpIsMYWjcQc7u6hHP8S3dfl 0MhJ0ji0eceeZc36aWiVNniak1n7UWDPpQ3R4EhfB8DhFsnJ84FfcTr8xpr7aeCg9iyXvc+9wtZR ZU3zBNQimR0lqql/WuaNmN8piqUXZlwqNFkSQfCExAr7yF6wLJ8pI1a0OmupRYE0v9sk+n+kWiHn eWE6ZY9rGqpQYAD55aMLGHkjPiQgX79z5OhjZM37svyHYG/CA5DSOGKaLcba0dcreN4IGEvCUTSH hf0Gp8fv4ZIiuuI3JDGPhhgTO36Poee0TKLuzBTq8OJxQvyBU+4teWMbPD+eBUJqpp6IRam7HGj5 25AEiiU4ywGjRptgYCB9VthQxayhLyKViQLo90uHNOgwcTRAe01lMS7lKxGCHbYF9e/JdZNod4gJ ItiPtzS+Wv31+UkU+4jduCHbRwAb/AuI0rlG8a41VtsaTlGFN+GgTsbeL18zXTIAAAAAAAA= --=-Wr3x245+ONWvxiwazedh--