Received: by 10.223.164.202 with SMTP id h10csp308072wrb; Tue, 14 Nov 2017 15:57:47 -0800 (PST) X-Google-Smtp-Source: AGs4zMYIItKtx78foQ61SEwKBNlHZurIx5mj3dtbKBSSHs/MloTAEfERikuoAhiSmetiTZ2t1k3S X-Received: by 10.101.100.76 with SMTP id s12mr9736807pgv.173.1510703867175; Tue, 14 Nov 2017 15:57:47 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1510703867; cv=none; d=google.com; s=arc-20160816; b=dj7fq4upL0snIa2uig4escQXl6X9lAtohygAFaEA/BSDTF+KUeFbkebpIbG9Z1YTm3 4iUq2r+cT+gv7PKBFPdr1kVS/0WGyGBJHYI9ZI/3A8C1azQ1Au2Cgiv19KmS0bezithF 7wrfM2Kr/0329PzXMPpVEeWDu2mhln2b4fVeg0r00gPV2roVWpg/8YttL1SgfIplJiEP k9GfSob6qnpI8Xc5ikNKyjuN+ZvgWyIlwheLogwKErUCJJthr5i8AfNjFawO5H4/PsXe SLJMpZQKbPzQe+pDIy2W3smwlKcLMQGwjosvoVfKeC32fctcbsMCWE3vOhlQ2RHJJO2w FqtQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:message-id:date:subject:cc:to:from :dkim-signature:arc-authentication-results; bh=JDqExgjVHThDPJvHyqV9Ue87nkDor7rxioTIROn/wWE=; b=psI4l5WpkMFRWV6W//CM58Ox3KzarH5CQR1NTwZw2NwT/IE3XMa4wGs4xVP8uUFxlH DchFOWFD/cl5QNo5ELTDYoYKqTulPBWkRUcVPvHI3U4JUhB/ijNgxZixf+Hj5ZOuD8QO KAzZYKVbOMVni1ApGPprPYsIPvP72Cl7XfdXCicRZvNS9OCAEogYl/bT8AMLjbxJgaKH 2x9VnoUkSDWvUazBR9aQ1ZM/RIFE9ILRP9G7WML+hJT7RlVNUGP9QBi/Dp3NUz5WvG1r Nf9P7wmm6WfBNJYOzLwI98RkvicoquaC/f47z11lnGIo7iDtXVpX9eGdcxe14b6ppAo+ hL4w== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b=V40kBAD0; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 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 vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 207si18342134pfz.5.2017.11.14.15.57.34; Tue, 14 Nov 2017 15:57:46 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b=V40kBAD0; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755317AbdKNXOG (ORCPT + 87 others); Tue, 14 Nov 2017 18:14:06 -0500 Received: from mail-pg0-f68.google.com ([74.125.83.68]:44361 "EHLO mail-pg0-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756866AbdKNXNu (ORCPT ); Tue, 14 Nov 2017 18:13:50 -0500 Received: by mail-pg0-f68.google.com with SMTP id 4so7643417pge.1 for ; Tue, 14 Nov 2017 15:13:49 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=from:to:cc:subject:date:message-id; bh=JDqExgjVHThDPJvHyqV9Ue87nkDor7rxioTIROn/wWE=; b=V40kBAD0IfHQf6wQIdNtna9RZhE5jXmTNWWMz4p+h5HWs6ZlbCaYHxCviGYncQcm1y OtzRDSOq7M2hDa7Pgag6o4ZF5Umt30HEtThZHijviP63dhIcUEfetmQaESq4lIvebLjO AGo60RCekm0nQ2dWhNhT0ApdQzt7fIYSsHBb95L85fvVkbGHHrDYAPaoCHNwvQXW9NBu 1EvV1a8DdWnLHyZTGNG/IazXziZbbCB3OD8V965roZVARm43RkeZEuDbVFjQjwxMYmRw KkPz41vCj+1zLsAhvZ7VQDJV+0eisZ71/pyc/HJ5ky+KopQoo0CW2yGLDL+yiiBtyQn8 ckgg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=JDqExgjVHThDPJvHyqV9Ue87nkDor7rxioTIROn/wWE=; b=KYAzKObRT/pPLdvqo+1irGoanP3xXyJy4VGz725w0h1QGEoKXia70LLhcuU7Ago40l ZuIPS9Tr46NMMs583sSmljvabf8nTmtarKZeVDIdkOFz4IojuRT0zhP9cVx8Q7HcTUhZ bJMxlX4C204yFG9XpFM0Cb8Pix2VZvP2nAw/QxhZc29ECs8AIaJhX5OnsfaKcHX5kLmx jnEvdosVuB0iXAgYTQ+KHh01m9ch7KZ5/qYO1qx5+oEa/744iGg8B59uiUeyQo3mnHyW kvVRXmFCj2fx3TXv0jVKDxdEZFcPL70LGpfRW4eVhsFKVM8aOnVl2nXtXsuQjt93Q78n BfwQ== X-Gm-Message-State: AJaThX4U/r/Vl4VRylPEsWIEIOM8hebzh48UjviBiQ6KeTbD6ktXqxVn RgfbHSlpboeZKhw1VBnMYhBq7A== X-Received: by 10.101.91.137 with SMTP id i9mr8308847pgr.313.1510701228986; Tue, 14 Nov 2017 15:13:48 -0800 (PST) Received: from khazhy.svl.corp.google.com ([100.116.144.80]) by smtp.gmail.com with ESMTPSA id l23sm42018692pfg.83.2017.11.14.15.13.47 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 14 Nov 2017 15:13:48 -0800 (PST) From: Khazhismel Kumykov To: axobe@kernel.dk, shli@fb.com, vgoyal@redhat.com, tj@kernel.org Cc: linux-kernel@vger.kernel.org, linux-block@vger.kernel.org, Khazhismel Kumykov Subject: [RFC PATCH] blk-throttle: add burst allowance. Date: Tue, 14 Nov 2017 15:10:22 -0800 Message-Id: <20171114231022.42961-1-khazhy@google.com> X-Mailer: git-send-email 2.15.0.448.gf294e3d99a-goog Content-Type: multipart/signed; protocol="application/pkcs7-signature"; micalg=sha-256; boundary="089e0823831c291c29055df98abe" Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org --089e0823831c291c29055df98abe Allows configuration additional bytes or ios before a throttle is triggered. This allows implementation of a bucket style rate-limit/throttle on a block device. Previously, bursting to a device was limited to allowance granted in a single throtl_slice (similar to a bucket with limit N and refill rate N/slice). Additional parameters bytes/io_burst_conf defined for tg, which define a number of bytes/ios that must be depleted before throttling happens. A tg that does not deplete this allowance functions as though it has no configured limits. tgs earn additional allowance at rate defined by bps/iops for the tg. Once a tg has *_disp > *_burst_conf, throttling kicks in. If a tg is idle for a while, it will again have some burst allowance before it gets throttled again. slice_end for a tg is extended until io_disp/byte_disp would fall to 0, when all "used" burst allowance would be earned back. trim_slice still does progress slice_start as before and decrements *_disp as before, and tgs continue to get bytes/ios in throtl_slice intervals. Signed-off-by: Khazhismel Kumykov --- block/Kconfig | 11 +++ block/blk-throttle.c | 192 +++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 189 insertions(+), 14 deletions(-) diff --git a/block/Kconfig b/block/Kconfig index 28ec55752b68..fbd05b419f93 100644 --- a/block/Kconfig +++ b/block/Kconfig @@ -128,6 +128,17 @@ config BLK_DEV_THROTTLING_LOW Note, this is an experimental interface and could be changed someday. +config BLK_DEV_THROTTLING_BURST + bool "Block throttling .burst allowance interface" + depends on BLK_DEV_THROTTLING + default n + ---help--- + Add .burst allowance for block throttling. Burst allowance allows for + additional unthrottled usage, while still limiting speed for sustained + usage. + + If in doubt, say N. + config BLK_CMDLINE_PARSER bool "Block device command line partition parser" default n diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 96ad32623427..27c084312772 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -157,6 +157,11 @@ struct throtl_grp { /* Number of bio's dispatched in current slice */ unsigned int io_disp[2]; +#ifdef CONFIG_BLK_DEV_THROTTLING_BURST + uint64_t bytes_burst_conf[2]; + unsigned int io_burst_conf[2]; +#endif + unsigned long last_low_overflow_time[2]; uint64_t last_bytes_disp[2]; @@ -507,6 +512,12 @@ static struct blkg_policy_data *throtl_pd_alloc(gfp_t gfp, int node) tg->bps_conf[WRITE][LIMIT_MAX] = U64_MAX; tg->iops_conf[READ][LIMIT_MAX] = UINT_MAX; tg->iops_conf[WRITE][LIMIT_MAX] = UINT_MAX; +#ifdef CONFIG_BLK_DEV_THROTTLING_BURST + tg->bytes_burst_conf[READ] = 0; + tg->bytes_burst_conf[WRITE] = 0; + tg->io_burst_conf[READ] = 0; + tg->io_burst_conf[WRITE] = 0; +#endif /* LIMIT_LOW will have default value 0 */ tg->latency_target = DFL_LATENCY_TARGET; @@ -800,6 +811,26 @@ static inline void throtl_start_new_slice(struct throtl_grp *tg, bool rw) tg->slice_end[rw], jiffies); } +/* + * When current slice should end. + * + * With CONFIG_BLK_DEV_THROTTLING_BURST, we will wait longer than min_wait + * for slice to recover used burst allowance. (*_disp -> 0). Setting slice_end + * before this would result in tg receiving additional burst allowance. + */ +static inline unsigned long throtl_slice_wait(struct throtl_grp *tg, bool rw, + unsigned long min_wait) +{ + unsigned long bytes_wait = 0, io_wait = 0; +#ifdef CONFIG_BLK_DEV_THROTTLING_BURST + if (tg->bytes_burst_conf[rw]) + bytes_wait = (tg->bytes_disp[rw] * HZ) / tg_bps_limit(tg, rw); + if (tg->io_burst_conf[rw]) + io_wait = (tg->io_disp[rw] * HZ) / tg_iops_limit(tg, rw); +#endif + return max(min_wait, max(bytes_wait, io_wait)); +} + static inline void throtl_set_slice_end(struct throtl_grp *tg, bool rw, unsigned long jiffy_end) { @@ -849,7 +880,8 @@ static inline void throtl_trim_slice(struct throtl_grp *tg, bool rw) * is bad because it does not allow new slice to start. */ - throtl_set_slice_end(tg, rw, jiffies + tg->td->throtl_slice); + throtl_set_slice_end(tg, rw, + jiffies + throtl_slice_wait(tg, rw, tg->td->throtl_slice)); time_elapsed = jiffies - tg->slice_start[rw]; @@ -889,7 +921,7 @@ static bool tg_with_in_iops_limit(struct throtl_grp *tg, struct bio *bio, unsigned long *wait) { bool rw = bio_data_dir(bio); - unsigned int io_allowed; + unsigned int io_allowed, io_disp; unsigned long jiffy_elapsed, jiffy_wait, jiffy_elapsed_rnd; u64 tmp; @@ -908,6 +940,17 @@ static bool tg_with_in_iops_limit(struct throtl_grp *tg, struct bio *bio, * have been trimmed. */ + io_disp = tg->io_disp[rw]; + +#ifdef CONFIG_BLK_DEV_THROTTLING_BURST + if (tg->io_disp[rw] < tg->io_burst_conf[rw]) { + if (wait) + *wait = 0; + return true; + } + io_disp -= tg->io_burst_conf[rw]; +#endif + tmp = (u64)tg_iops_limit(tg, rw) * jiffy_elapsed_rnd; do_div(tmp, HZ); @@ -916,14 +959,14 @@ static bool tg_with_in_iops_limit(struct throtl_grp *tg, struct bio *bio, else io_allowed = tmp; - if (tg->io_disp[rw] + 1 <= io_allowed) { + if (io_disp + 1 <= io_allowed) { if (wait) *wait = 0; return true; } /* Calc approx time to dispatch */ - jiffy_wait = ((tg->io_disp[rw] + 1) * HZ) / tg_iops_limit(tg, rw) + 1; + jiffy_wait = ((io_disp + 1) * HZ) / tg_iops_limit(tg, rw) + 1; if (jiffy_wait > jiffy_elapsed) jiffy_wait = jiffy_wait - jiffy_elapsed; @@ -939,7 +982,7 @@ static bool tg_with_in_bps_limit(struct throtl_grp *tg, struct bio *bio, unsigned long *wait) { bool rw = bio_data_dir(bio); - u64 bytes_allowed, extra_bytes, tmp; + u64 bytes_allowed, extra_bytes, bytes_disp, tmp; unsigned long jiffy_elapsed, jiffy_wait, jiffy_elapsed_rnd; unsigned int bio_size = throtl_bio_data_size(bio); @@ -951,18 +994,28 @@ static bool tg_with_in_bps_limit(struct throtl_grp *tg, struct bio *bio, jiffy_elapsed_rnd = roundup(jiffy_elapsed_rnd, tg->td->throtl_slice); + bytes_disp = tg->bytes_disp[rw]; +#ifdef CONFIG_BLK_DEV_THROTTLING_BURST + if (tg->bytes_disp[rw] < tg->bytes_burst_conf[rw]) { + if (wait) + *wait = 0; + return true; + } + bytes_disp -= tg->bytes_burst_conf[rw]; +#endif + tmp = tg_bps_limit(tg, rw) * jiffy_elapsed_rnd; do_div(tmp, HZ); bytes_allowed = tmp; - if (tg->bytes_disp[rw] + bio_size <= bytes_allowed) { + if (bytes_disp + bio_size <= bytes_allowed) { if (wait) *wait = 0; return true; } /* Calc approx time to dispatch */ - extra_bytes = tg->bytes_disp[rw] + bio_size - bytes_allowed; + extra_bytes = bytes_disp + bio_size - bytes_allowed; jiffy_wait = div64_u64(extra_bytes * HZ, tg_bps_limit(tg, rw)); if (!jiffy_wait) @@ -986,7 +1039,7 @@ static bool tg_may_dispatch(struct throtl_grp *tg, struct bio *bio, unsigned long *wait) { bool rw = bio_data_dir(bio); - unsigned long bps_wait = 0, iops_wait = 0, max_wait = 0; + unsigned long bps_wait = 0, iops_wait = 0, max_wait = 0, disp_time; /* * Currently whole state machine of group depends on first bio @@ -1015,10 +1068,10 @@ static bool tg_may_dispatch(struct throtl_grp *tg, struct bio *bio, if (throtl_slice_used(tg, rw) && !(tg->service_queue.nr_queued[rw])) throtl_start_new_slice(tg, rw); else { - if (time_before(tg->slice_end[rw], - jiffies + tg->td->throtl_slice)) - throtl_extend_slice(tg, rw, - jiffies + tg->td->throtl_slice); + disp_time = jiffies + throtl_slice_wait( + tg, rw, tg->td->throtl_slice); + if (time_before(tg->slice_end[rw], disp_time)) + throtl_extend_slice(tg, rw, disp_time); } if (tg_with_in_bps_limit(tg, bio, &bps_wait) && @@ -1033,8 +1086,9 @@ static bool tg_may_dispatch(struct throtl_grp *tg, struct bio *bio, if (wait) *wait = max_wait; - if (time_before(tg->slice_end[rw], jiffies + max_wait)) - throtl_extend_slice(tg, rw, jiffies + max_wait); + disp_time = jiffies + throtl_slice_wait(tg, rw, max_wait); + if (time_before(tg->slice_end[rw], disp_time)) + throtl_extend_slice(tg, rw, disp_time); return 0; } @@ -1705,6 +1759,108 @@ static ssize_t tg_set_limit(struct kernfs_open_file *of, return ret ?: nbytes; } +#ifdef CONFIG_BLK_DEV_THROTTLING_BURST +static u64 tg_prfill_burst(struct seq_file *sf, struct blkg_policy_data *pd, + int data) +{ + struct throtl_grp *tg = pd_to_tg(pd); + const char *dname = blkg_dev_name(pd->blkg); + char bufs[4][21]; + + if (!dname) + return 0; + + if (tg->bytes_burst_conf[READ] == 0 && + tg->bytes_burst_conf[WRITE] == 0 && + tg->io_burst_conf[READ] == 0 && + tg->io_burst_conf[WRITE] == 0) + return 0; + + snprintf(bufs[0], sizeof(bufs[0]), "%llu", + tg->bytes_burst_conf[READ]); + snprintf(bufs[1], sizeof(bufs[1]), "%llu", + tg->bytes_burst_conf[WRITE]); + snprintf(bufs[2], sizeof(bufs[2]), "%u", + tg->io_burst_conf[READ]); + snprintf(bufs[3], sizeof(bufs[3]), "%u", + tg->io_burst_conf[WRITE]); + + seq_printf(sf, "%s brbyte=%s bwbyte=%s brio=%s bwio=%s\n", + dname, bufs[0], bufs[1], bufs[2], bufs[3]); + return 0; +} + +static int tg_print_burst(struct seq_file *sf, void *v) +{ + blkcg_print_blkgs(sf, css_to_blkcg(seq_css(sf)), tg_prfill_burst, + &blkcg_policy_throtl, 0, false); + return 0; +} + +static ssize_t tg_set_burst(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off) +{ + struct blkcg *blkcg = css_to_blkcg(of_css(of)); + struct blkg_conf_ctx ctx; + struct throtl_grp *tg; + u64 v[4]; + int ret; + + ret = blkg_conf_prep(blkcg, &blkcg_policy_throtl, buf, &ctx); + if (ret) + return ret; + + tg = blkg_to_tg(ctx.blkg); + + v[0] = tg->bytes_burst_conf[READ]; + v[1] = tg->bytes_burst_conf[WRITE]; + v[2] = tg->io_burst_conf[READ]; + v[3] = tg->io_burst_conf[WRITE]; + + while (true) { + char tok[28]; /* bwbyte=18446744073709551616 */ + char *p; + u64 val = U64_MAX; + int len; + + if (sscanf(ctx.body, "%27s%n", tok, &len) != 1) + break; + if (tok[0] == '\0') + break; + ctx.body += len; + + ret = -EINVAL; + p = tok; + strsep(&p, "="); + if (!p || (kstrtoull(p, 0, &val) != 0 && strcmp(p, "max"))) + goto out_finish; + + ret = -EINVAL; + if (!strcmp(tok, "brbyte")) + v[0] = val; + else if (!strcmp(tok, "bwbyte")) + v[1] = val; + else if (!strcmp(tok, "brio")) + v[2] = min_t(u64, val, UINT_MAX); + else if (!strcmp(tok, "bwio")) + v[3] = min_t(u64, val, UINT_MAX); + else + goto out_finish; + } + + tg->bytes_burst_conf[READ] = v[0]; + tg->bytes_burst_conf[WRITE] = v[1]; + tg->io_burst_conf[READ] = v[2]; + tg->io_burst_conf[WRITE] = v[3]; + + tg_conf_updated(tg, false); + ret = 0; +out_finish: + blkg_conf_finish(&ctx); + return ret ?: nbytes; +} +#endif + static struct cftype throtl_files[] = { #ifdef CONFIG_BLK_DEV_THROTTLING_LOW { @@ -1714,6 +1870,14 @@ static struct cftype throtl_files[] = { .write = tg_set_limit, .private = LIMIT_LOW, }, +#endif +#ifdef CONFIG_BLK_DEV_THROTTLING_BURST + { + .name = "burst", + .flags = CFTYPE_NOT_ON_ROOT, + .seq_show = tg_print_burst, + .write = tg_set_burst, + }, #endif { .name = "max", -- 2.15.0.448.gf294e3d99a-goog --089e0823831c291c29055df98abe Content-Type: application/pkcs7-signature; name="smime.p7s" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="smime.p7s" Content-Description: S/MIME Cryptographic Signature MIIS5wYJKoZIhvcNAQcCoIIS2DCCEtQCAQExDzANBglghkgBZQMEAgEFADALBgkqhkiG9w0BBwGg ghBNMIIEXDCCA0SgAwIBAgIOSBtqDm4P/739RPqw/wcwDQYJKoZIhvcNAQELBQAwZDELMAkGA1UE BhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExOjA4BgNVBAMTMUdsb2JhbFNpZ24gUGVy c29uYWxTaWduIFBhcnRuZXJzIENBIC0gU0hBMjU2IC0gRzIwHhcNMTYwNjE1MDAwMDAwWhcNMjEw NjE1MDAwMDAwWjBMMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEiMCAG A1UEAxMZR2xvYmFsU2lnbiBIViBTL01JTUUgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC AQoCggEBALR23lKtjlZW/17kthzYcMHHKFgywfc4vLIjfq42NmMWbXkNUabIgS8KX4PnIFsTlD6F GO2fqnsTygvYPFBSMX4OCFtJXoikP2CQlEvO7WooyE94tqmqD+w0YtyP2IB5j4KvOIeNv1Gbnnes BIUWLFxs1ERvYDhmk+OrvW7Vd8ZfpRJj71Rb+QQsUpkyTySaqALXnyztTDp1L5d1bABJN/bJbEU3 Hf5FLrANmognIu+Npty6GrA6p3yKELzTsilOFmYNWg7L838NS2JbFOndl+ce89gM36CW7vyhszi6 6LqqzJL8MsmkP53GGhf11YMP9EkmawYouMDP/PwQYhIiUO0CAwEAAaOCASIwggEeMA4GA1UdDwEB /wQEAwIBBjAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwEgYDVR0TAQH/BAgwBgEB/wIB ADAdBgNVHQ4EFgQUyzgSsMeZwHiSjLMhleb0JmLA4D8wHwYDVR0jBBgwFoAUJiSSix/TRK+xsBtt r+500ox4AAMwSwYDVR0fBEQwQjBAoD6gPIY6aHR0cDovL2NybC5nbG9iYWxzaWduLmNvbS9ncy9n c3BlcnNvbmFsc2lnbnB0bnJzc2hhMmcyLmNybDBMBgNVHSAERTBDMEEGCSsGAQQBoDIBKDA0MDIG CCsGAQUFBwIBFiZodHRwczovL3d3dy5nbG9iYWxzaWduLmNvbS9yZXBvc2l0b3J5LzANBgkqhkiG 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 BQUHMAKGNGh0dHA6Ly9zZWN1cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L2dzaHZzbWltZWNhMS5j cnQwHQYDVR0OBBYEFMnO7tLwRUm/Kh/G63DTEdz9N5wmMB8GA1UdIwQYMBaAFMs4ErDHmcB4koyz IZXm9CZiwOA/MEwGA1UdIARFMEMwQQYJKwYBBAGgMgEoMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8v d3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMDsGA1UdHwQ0MDIwMKAuoCyGKmh0dHA6Ly9j cmwuZ2xvYmFsc2lnbi5jb20vZ3NodnNtaW1lY2ExLmNybDAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0l BBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMEMA0GCSqGSIb3DQEBCwUAA4IBAQA5gzhiP9g5DzgYyM4K /OtFFFKyrluiKx9OmOb1Mx9UCxEi9vzRrG5j1rFMAwNAx+xEESoq1JVNe8fJKBimOsKpWstAhYlO Cg6Qm43dzb+5CcPWDC3j6XxfsUIKvektE79/IeVhdRVj+Op1gSEGaBJQP2c0/MeXPPhQKPjAPVQW bEOJaemCXr1UIoEHMoisd0Smdm1NjxLYLk3bK1RDgO0RTu2hNmVAT9WypS9uiquOQWeK3u9QBuUK BhOZjgo70YosoRVRBIKNqStZ++IpaDEWfDme3EH4H8tlOzwCvAiO8c1uF7ZX68wXWJPjq6uxu1cZ 5lT83BZ34AElNAzFvsLhMYICXjCCAloCAQEwXDBMMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xv YmFsU2lnbiBudi1zYTEiMCAGA1UEAxMZR2xvYmFsU2lnbiBIViBTL01JTUUgQ0EgMQIMPycjokgk Gdp8HTY2MA0GCWCGSAFlAwQCAQUAoIHUMC8GCSqGSIb3DQEJBDEiBCBNUL9D4mG24fNzJOnR8UNb QcaGRPAxz6zopvgkxDp54jAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEP Fw0xNzExMTQyMzEzNDlaMGkGCSqGSIb3DQEJDzFcMFowCwYJYIZIAWUDBAEqMAsGCWCGSAFlAwQB FjALBglghkgBZQMEAQIwCgYIKoZIhvcNAwcwCwYJKoZIhvcNAQEKMAsGCSqGSIb3DQEBBzALBglg hkgBZQMEAgEwDQYJKoZIhvcNAQEBBQAEggEAsA8QNxVQns43/LWJtiuCdxnK5jrWYJCnLLWtEOWS dN78VHk5W11QB/ZJsz6mF7SP/swKrDKTBLU4mVYheeHMCEMnU7cI3CHvE1Q95IAxHK3gY9IfCKYs T41bkOiKKR5+jjCs+TqcxvbPFYae+aogDc4aM0Y/0/IMktigbeQA5kmDiR9OclMfbLIKdf7Lh3rj zFt8uzl3yJlKB27wxOLPf3xRjR9RdEqLJz13pn8RYkFNy8LIHg5marLhsAxSa8A8fI0OXjccL3dL IrY/kDUtJal2rdEPN3XvdyeESe91i139i79AUYgSn6zecGiz3DbWfdh1B9FQFiQW/QGqrcFlEw== --089e0823831c291c29055df98abe-- From 1586018567991693577@xxx Wed Dec 06 07:26:13 +0000 2017 X-GM-THRID: 1586018567991693577 X-Gmail-Labels: Inbox,Category Forums,HistoricalUnread