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 2C0F93806AA for ; Thu, 21 May 2026 09:55:16 +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=1779357320; cv=none; b=qZ4mmFb9BbPPOxliqCSG7/x6flETsVDkgQEzdTSDc0f8Un1yzwTQe8VlJRCxGgLYv4dCPB1MjlV6IUvsTePtFe3qKwoFK1LcjWw7ZGWVENKfVYyvGcGuQBQJsgHVhLi+XOR5/u6ICuz243r1NZqGV0blfb7HixLQkQeShEVBh18= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779357320; c=relaxed/simple; bh=h/LYkhc+jkk2FWp+mLT0Rkrapuvrq19/7iw3uGhIOSI=; h=Message-ID:Subject:From:To:Cc:In-Reply-To:References:Content-Type: Date:MIME-Version; b=TOlUbHnttsOesacJwb2+0TD8VQh1XOcdBE1kfjYq2py6bv28CngplOXGjqt5o+lSGI9ptRaHGe14xFsuHyuCjbvg84v72L3+nDSEybgwFx3OWo4LWjjfkYIXJ0ZTEdogFkwCbhCbnU9YiwezF7ayJap0k/mQDNsyxzJM3sS342o= 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=bIN9tlDn; 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="bIN9tlDn" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=infradead.org; s=casper.20170209; h=MIME-Version:Date:Content-Type: References:In-Reply-To:Cc:To:From:Subject:Message-ID:Sender:Reply-To: Content-Transfer-Encoding:Content-ID:Content-Description; bh=h/LYkhc+jkk2FWp+mLT0Rkrapuvrq19/7iw3uGhIOSI=; b=bIN9tlDn7ofYqZT878qILIwTO9 gIPPR2aiiw5bXVMxIszLXAbYnSvCIWyEnJCOuiYegIcR1jKTBxj7QYf68EqBXqU2dhLdg22Y1s9PR 0nzsRZsHVBBkoGxpksYjQUjqOtu7sV+G3ml2Y9RnZvLoHt2Xm5AFKchPeH4er1CKPfyilwYqQH9pr AZAAdr6cjvfiZJtxIp5inJkOGEyRORoHH2TS4Vp0Lca1wl1dO++wmBL9h5DRd7+hR6iImxq9aAaiK aJ6cvawyuxIVtjzKxdqQE1rhAH12PWIPpv1W6mY01ms8s8U7MX6MSeNlDRewe79ZkR94MPKydRQdk YXLsO3uA==; 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 1wQ07H-00000008MnB-0nHg; Thu, 21 May 2026 09:54:59 +0000 Message-ID: Subject: Re: [RFC PATCH v2 0/8] timekeeping: Fix draft tracking precision and add feed-forward discipline via vmclock From: David Woodhouse To: Miroslav Lichvar Cc: Richard Cochran , Wen Gu , Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , John Stultz , Thomas Gleixner , Stephen Boyd , Anna-Maria Behnsen , Frederic Weisbecker , Shuah Khan , Peter Zijlstra , Thomas =?ISO-8859-1?Q?Wei=DFschuh?= , Arnd Bergmann , Julien Ridoux , Ryan Luu , linux-kernel@vger.kernel.org, Marcelo Tosatti In-Reply-To: References: <20260517220326.4625-1-dwmw2@infradead.org> <0d32da75fa88c92ac0225ef23a9045afdf2ac9fe.camel@infradead.org> Content-Type: multipart/signed; micalg="sha-256"; protocol="application/pkcs7-signature"; boundary="=-aoS+htVuT3UXMPtkdlfk" Date: Thu, 21 May 2026 10:54:41 +0100 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 User-Agent: Evolution 3.52.3-0ubuntu1.1 X-SRS-Rewrite: SMTP reverse-path rewritten from by casper.infradead.org. See http://www.infradead.org/rpr.html --=-aoS+htVuT3UXMPtkdlfk Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Thu, 2026-05-21 at 08:35 +0200, Miroslav Lichvar wrote: > On Wed, May 20, 2026 at 01:21:46PM +0100, David Woodhouse wrote: > > On Wed, 2026-05-20 at 12:39 +0200, Miroslav Lichvar wrote: > > > On Tue, May 19, 2026 at 04:50:41PM +0100, David Woodhouse wrote: > > > > The design has two major purposes: > > >=20 > > > > =C2=A0=E2=80=A2 Avoiding the redundant work of having *hundreds* of= guests on the > > > > =C2=A0=C2=A0 same host *all* calibrating the same underlying oscill= ator, while > > > > =C2=A0=C2=A0 enjoying the added fun of steal time as they're trying= to to so. > > >=20 > > > But isn't that work still duplicated, only moved to the kernel?=C2=A0 > >=20 > > Not the actual calibration of the TSC against real time, no. It is the > > *host* which gets the 1PPS signal and does all the work of tracking and > > smoothing the frequency drift over time. The guest basically gets the > > same as a vDSO, *telling* it a relationship from TSC to real time. >=20 > Ok, but I don't see why the phase corrections of the guest need to be > in the kernel. I'm not sure I understand.=C2=A0 There are no 'phase corrections' as such, except of course that the phase of the guest kernel's clock does get corrected, and naturally that does have to take effect inside the guest kernel. I think the key here is that this is not a feedback loop based on corrections to the existing clock output; this is a feedforward design as described in https://dl.acm.org/doi/pdf/10.1109/TNET.2011.2158443 It seems that when Julien et al lamented that, "Until now, however, there has been a serious practical issue inhibiting feed-forward approaches: a lack of kernel support", the basics were actually there in the kernel's core timekeeping all along. We didn't have to *do* anything to the core timekeeping other than fix a few bugs that the NTP feedback mechanism always masked =E2=80=94 who *car= es* if there's a systematic +5PPM drift due to accounting errors, as NTP can just interpret that as the counter running 5PPM fast and adjust for it? Although I don't think the errors are quite that consistent, as they vary with tick length and even from tick to tick with the mult=C2=B11 dithering and interrupt latency =E2=80=94 so I wouldn't be surprised if the= se fixes made a detectable improvement even in the normal NTP case. > > > I don't like the idea of adding more clock control loops to the kerne= l > > > much. > >=20 > > I completely agree. I am absolutely not planning to add any more clock > > control to the kernel than we already have. As you say, we probably > > have too many already. >=20 > If the vmclock driver is feeding the PLL with the offset between the > host and guest clocks, I think that would count as a loop. It's not an offset; it's a direct feed-forward "when the TSC is the time is " relationship, like a vDSO does. https://uapi-group.org/specifications/specs/vmclock/ The core motivation is for virtual machines (and especially for consistent time across live migration), but hardware implementations should be possible using PCIe PTM. I keep meaning to get my hands on a TimeCard and play, but there are only so many hours in the day... > > I'm not sure what scaling the guest TSC would buy us. Sure, it would > > minimise the frequency step at the moment of migration, but a na=C3=AFv= e > > guest which isn't using vmclock's disruption signal is screwed on live > > migration *anyway*, because there's *also* a step change in the actual > > TSC value which is bounded by the real time synchronization of the > > source and destination host.=20 >=20 > The TSC offset can be corrected too. I thought that was already > happening. Yes, it is. The TSC offset (and the guest's KVM clock, which is a whole different sad story) can be corrected a bit =E2=80=94 but the *accuracy* wi= th which they can be corrected is limited to the accuracy of the source vs. destination hosts' time synchronization. If the guest has been using NTP or a PHC to discipline the counter of the source host that it just came from, carefully tracking not only the perceived time, but also error bounds in order to ensure coherency of, say, a distributed database... there is no way that we can migrate it to a new host and 'fake' the frequency/offset on the new host to sufficiently match. Database corruption ensues. The best thing to do is to advertise a disruption signal ("throw away anything you know about the existing counter"), and provide information on the new host in that {cycle_count, reference time, counter period, error bounds} form to allow the guest to return to service as soon as possible. Which is precisely what vmclock does. > > AFAICT scaling the TSC would just add complexity and wouldn't help > > much. >=20 > I think it's a better place to be solving this kind of problems. It's > compensating for a hardware change. It doesn't need to happen only at > migration. You could adjust the frequency continuously if you really > wanted, kind of like synchronous ethernet is doing for clocks over > network, improving the stability of the physical clock and phase > corrections are done on top of it at a higher level. On the *host* side I might accept a PLL on the actual hardware oscillator and the 1PPS signal... :) > > And TSC scaling is pretty much x86-specific; other architectures have a > > *defined* counter frequency and don't need to support scaling. >=20 > There can be a software fallback if hardware scaling and/or offset is > not supported. Right. This *is* the software fallback, because the hardware scaling and offset aren't sufficient even if we only care about x86 where the former is supported. > > > > > There is a work in progress for chrony to support MONOTONIC_RAW a= s the > > > > > main clock. It would be nice if that could be corrected in migrat= ions. > > > >=20 > > > > Not sure I understand this. I thought the whole point of MONOTONIC_= RAW > > > > is that it *isn't* skewed by NTP? > > >=20 > > > It isn't adjusted, but it can be used as a stable reference avoiding > > > the multiplier-induced jitter, interference from other processes, and > > > synchronization loops, e.g. when an NTP client is synchronizing to an > > > NTP server running on the same system (in different containers).=20 > >=20 > > We could just use the TSC for this, insted of MONOTONIC_RAW, couldn't > > we? >=20 > > (for TSC, read 'arch counter, timebase, etc.' =E2=80=94 none of this is= x86- > > specific but 'TSC' is quicker to type...) >=20 > Meaning userspace would have to duplicate the kernel's handling of > the counter (wrapping and scaling) just to avoid a single > multiplication in the vDSO? Hm yeah, I guess that makes sense. The way I've done it in these proof of concept patches is counter- based, because the interface between host and guest (and from that theoretical hardware implementation) *is* necessarily in terms of the hardware =E2=80=94 we get told the relationship of the actual *counter* to realtime. But as long as the conversions in both directions are quick and accurate there's no fundamental reason why it *couldn't* be expressed in terms of MONOTONIC_RAW as it's being passed around. In my RFC, it's just a call to timekeeping_set_reference() which uses the *existing* mechanisms to just set tick_length and time_offset accordingly. Which naturally takes counter-based units too. But I certainly don't think that doing so *unconditionally* from the vmclock driver in my proof of concept is the right thing to do. Userspace needs to set policy like that. And I wasn't stunningly happy with timekeeping_set_reference() passing fractional seconds in the vmclock (seconds<<64) units instead of the native (nanoseconds<<32) of the timekeeping code. So maybe timekeeping_set_reference() should take its input in MONOTONIC_RAW terms, and the raw information from vmclock should be converted accordingly? I can try that... On the *host* side, I anticipate two modes of operation. A dedicated hosting environment only really cares about disciplining the host kernel's TSC, and absolutely doesn't *care* about the host kernel's timekeeping. That's just for logs. For migrating KVM guests as accurately as possible, we set the guest *TSC* (scaling and) offset based on our understanding of the host TSC on both source and destination. The KVM APIs for doing this based on the kernel's own CLOCK_REALTIME are... a source of sadness. There's a whole 30-patch series in flight to deal with that, which you can look at if if you like pain, but the tl;dr is that we get the host kernel's timekeeping out of the picture as *much* as possible and operate in terms of the TSC. Migrate the guest kernel's TSC as accurately as possible, and everything *else* in the guest is derived from that. So in that dedicated environment, userspace will take our hardware devices which literally latch the *counter* value on a 1PPS signal, or use NTP if they really have to fall back to that, and discipline the *counter*, then use that information to both provide the vmclock for guests, and migrate guests as accurately as possible. All in userspace, *necessarily* in raw counter terms. But hey, it's nice for logs to have good timestamps too, so we can feed it to the kernel's CLOCK_REALTIME as an afterthought. Probably by using a userspace hook for timekeeping_set_reference(). I haven't yet looked at whether the existing adjtimex() can be used/abused/extended to allow for precisely setting tick_length/time_offset like that. And then there's the 'normal' host side, with a host kernel running chrony and a few guests in QEMU. Obviously this mode needs to be properly taken into account as a first class citizen, which is why I've built the support that's already *in* QEMU (disruption signal only) and now the vmclock_host and additional QEMU patch to expose that. Again it needs to be in terms of the guest TSC by the time the VMM actually puts it in the shared page, but I'm entirely open to input on how we get it *out* of the kernel's timekeeping. I do tend to have the opinion that what we should expose to guests is the "intended" clock, with ntpdata->time_offset built in and *not* including the constant =C2=B11 changes to 'mult' from the dithering, but using the *actual* intended frequency from tick_length / cycle_interval. But other than that, I'm prepared to consider the whole of the vmclock_host export part as a straw man, and entirely happy to completely reimplement it however you like, if you have strong opinions. I just needed to get *something* implemented and working, as a starting point. --=-aoS+htVuT3UXMPtkdlfk 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 ggE3MBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTI2MDUyMTA5NTQz OFowLwYJKoZIhvcNAQkEMSIEIH9YO2GVlQigy3XBQ6nfDh1zpyFEajSG/AYnVpoX5uSLMGQGCSsG AQQBgjcQBDFXMFUwQTELMAkGA1UEBhMCQVUxEDAOBgNVBAoTB1Zlcm9rZXkxIDAeBgNVBAMTF1Zl cm9rZXkgU2VjdXJlIEVtYWlsIEcyAhAH5JEPagNRXYDiRPdlc1vgMGYGCyqGSIb3DQEJEAILMVeg VTBBMQswCQYDVQQGEwJBVTEQMA4GA1UEChMHVmVyb2tleTEgMB4GA1UEAxMXVmVyb2tleSBTZWN1 cmUgRW1haWwgRzICEAfkkQ9qA1FdgOJE92VzW+AwDQYJKoZIhvcNAQEBBQAEggIAleG0hC/RajPx NMNGkbKa1vMONdbtKbrIM3Q/C/c9nAALtoEEAmbiZgCpmW3GDi8vASoIhRG3TczWzHyHO9G9jzmh a6hD+XFaxM4GrEekhZEgrofqQsuprGogxKdjY0Sh/RVyttTMVo+0qwNmk2OYdRdGF5lNxR2DMJfQ Bqh7+TcGzlshUj3FkizZ48SYsAh8igdDJnP/O1/KqTZSxuQhAwAERd8bvSRGcKBgpOQFOtDj32w3 rYwCuDCLF7cKu3+Y1bgF1YG0emu1aNVnNvTuefXnpj0b79AyrSV/oDQaDHUfMliYi+pe7EH0CydV 7nz9S8GV34pAyS4iA/YT9hCBrvH7eLmkoMTPFhQ65KQcpibOQWCmCcDPhAJHJkGovdaJYNflf3Ao BJh+/RD0g6L71xLtpacVXYvbWaHbhV1guJdVRZq0fwoU5xJi1WPEa8KQkPOyXp+Km8owFFLFDC1S CKDIqBghnnV1ZbfBdqwwUL12y6jkRh95LlAXJ+q73PYKJdhblwnHI71XWcTEjXhpm9C1AR1AvDsE 6DB80vnB0PgEjzRasLYucbGgMb0ItuEA2hWFMv5KyznEA3uT7Efm4atTYy/vkkAXDqChhTsm5k7N 0I1IExFxxhNHD1qoBcm42Bt7KY2+nMWEY/64pU0A+ohrlxh0xuTMD527Vu2IfWQAAAAAAAA= --=-aoS+htVuT3UXMPtkdlfk--