From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C3C63CA9EA0 for ; Tue, 22 Oct 2019 13:30:24 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 7EB4E21872 for ; Tue, 22 Oct 2019 13:30:24 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="fZneTrzF" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388732AbfJVNaY (ORCPT ); Tue, 22 Oct 2019 09:30:24 -0400 Received: from us-smtp-1.mimecast.com ([205.139.110.61]:48504 "EHLO us-smtp-delivery-1.mimecast.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2388675AbfJVNaX (ORCPT ); Tue, 22 Oct 2019 09:30:23 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1571751022; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=O/fE5wgsw+LuumGvx+l/wdiOi/YBJWxEuegNxxIYllI=; b=fZneTrzFNFXLIEy+6CN3CiA3imVnzJnyLyECwQVEud1+YDY+nxP9We6+J1AM7NGBw/b81j 0OrgfQ2FMCBQUBuCoSqpyQc/CPapZVTh7/yj+bLzfTu1gbzfF+7DAomJalFN6Snn3ybChX RqukKujd3cTnyEYH5G/as7b0N8yMWSU= Received: from mail-wm1-f69.google.com (mail-wm1-f69.google.com [209.85.128.69]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-368-zJc7HUyvPY-zDMZatjlRAg-1; Tue, 22 Oct 2019 09:30:20 -0400 Received: by mail-wm1-f69.google.com with SMTP id o128so8504180wmo.1 for ; Tue, 22 Oct 2019 06:30:20 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:from:to:cc:date:message-id:in-reply-to :references:user-agent:mime-version:content-transfer-encoding; bh=92l4Deg/Xs3bLfE6mE7mSCINegjRRNU/LCyZWA9Nr7I=; b=BAC0XkSTkw3KHl7Ig/iMWAS7Q7YI22aCpqKC/bObHA+joXTShIsvKvy1FcZM9Z/KHg RVif7yq3Z5L7w/8fayCLSrczDLBP9ZrFrhKtIh16z00Nm95UyM0DzyQQ1jGAAciaqJs6 4dRaP9JvD4sheA35ATHlqBF+lawurCsxH16CKtNl+cQFKeufSjsC8wxoO40K2zG3iGuU x0cuxv1yusihXuq4AYvYFceLwGAzFP3eDUEF5Lt2lKzvfODT2O8CQCuC/bIfjaTI5upW HSn2pEFeLr8ra3SyCP147p5LR5ZjvkSAc8kdYR8M5I6upHsY79kbT7LFD+9wmELvy119 j63g== X-Gm-Message-State: APjAAAWm74OH03SRvPgdzNy3zn6VdI9Sv8iDGfM1PCMkV+3ZudJ3+VaT cNr3huKBJSF2nGlYOwvNyuHlr8bpKjcyPmWZV2EvB4+XXg5eTsc7tfho1oK72llURfLru21E/dU dKOpivCFjT8dKLCPi68Gc1IH4RHs= X-Received: by 2002:a1c:5458:: with SMTP id p24mr2938501wmi.32.1571751019035; Tue, 22 Oct 2019 06:30:19 -0700 (PDT) X-Google-Smtp-Source: APXvYqxAt3XbgkjShZCoWnfec7brIuYpJKlR/291nOG2QiDE7BbS93bWF+4VsR2+Tfd7AKPLntV7Tw== X-Received: by 2002:a1c:5458:: with SMTP id p24mr2938450wmi.32.1571751018481; Tue, 22 Oct 2019 06:30:18 -0700 (PDT) Received: from alrua-x1.borgediget.toke.dk (borgediget.toke.dk. [85.204.121.218]) by smtp.gmail.com with ESMTPSA id y13sm27620558wrg.8.2019.10.22.06.30.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 22 Oct 2019 06:30:17 -0700 (PDT) Received: by alrua-x1.borgediget.toke.dk (Postfix, from userid 1000) id 70E021804B1; Tue, 22 Oct 2019 15:30:17 +0200 (CEST) Subject: [PATCH v5 2/4] mac80211: Import airtime calculation code from mt76 From: =?utf-8?q?Toke_H=C3=B8iland-J=C3=B8rgensen?= To: Johannes Berg Cc: linux-wireless@vger.kernel.org, make-wifi-fast@lists.bufferbloat.net, ath10k@lists.infradead.org, John Crispin , Lorenzo Bianconi , Felix Fietkau , Kan Yan , Rajkumar Manoharan , Kevin Hayes Date: Tue, 22 Oct 2019 15:30:17 +0200 Message-ID: <157175101738.104114.14883771055318054924.stgit@toke.dk> In-Reply-To: <157175101518.104114.6722791270722911023.stgit@toke.dk> References: <157175101518.104114.6722791270722911023.stgit@toke.dk> User-Agent: StGit/0.20 MIME-Version: 1.0 X-MC-Unique: zJc7HUyvPY-zDMZatjlRAg-1 X-Mimecast-Spam-Score: 0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Toke H=C3=B8iland-J=C3=B8rgensen Felix recently added code to calculate airtime of packets to the mt76 driver. Import this into mac80211 so we can use it for airtime queue limit calculations later. The airtime.c file is copied verbatim from the mt76 driver, and adjusted to use mac80211 data structures instead (which is fairly straight forward). The per-rate TX rate calculation is split out to its own function (ieee80211_calc_tx_airtime_rate()) so it can be used directly for the AQL calculations added in a subsequent patch. The only thing that it was not possible to port directly was the bit that read the internal driver flags of struct ieee80211_rate to determine whether a rate is using CCK or OFDM encoding. Instead, just look at the rate index, since at least mt76 and ath10k both seem to have the same number of CCK rates (4) in their tables. Signed-off-by: Toke H=C3=B8iland-J=C3=B8rgensen --- include/net/mac80211.h | 29 +++ net/mac80211/Makefile | 3=20 net/mac80211/airtime.c | 390 ++++++++++++++++++++++++++++++++++++++++= ++++ net/mac80211/ieee80211_i.h | 4=20 4 files changed, 425 insertions(+), 1 deletion(-) create mode 100644 net/mac80211/airtime.c diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 4288ace72c2b..8a3e0544a026 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -6424,4 +6424,33 @@ void ieee80211_nan_func_match(struct ieee80211_vif *= vif, =09=09=09 struct cfg80211_nan_match_params *match, =09=09=09 gfp_t gfp); =20 +/** + * ieee80211_calc_rx_airtime - calculate estimated transmission airtime fo= r RX. + * + * This function calculates the estimated airtime usage of a frame based o= n the + * rate information in the RX status struct and the frame length. + * + * @hw: pointer as obtained from ieee80211_alloc_hw() + * @status: &struct ieee80211_rx_status containing the transmission rate + * information. + * @len: frame length in bytes + */ +u32 ieee80211_calc_rx_airtime(struct ieee80211_hw *hw, +=09=09=09 struct ieee80211_rx_status *status, +=09=09=09 int len); + +/** + * ieee80211_calc_tx_airtime - calculate estimated transmission airtime fo= r TX. + * + * This function calculates the estimated airtime usage of a frame based o= n the + * rate information in the TX info struct and the frame length. + * + * @hw: pointer as obtained from ieee80211_alloc_hw() + * @info: &struct ieee80211_tx_info of the frame. + * @len: frame length in bytes + */ +u32 ieee80211_calc_tx_airtime(struct ieee80211_hw *hw, +=09=09=09 struct ieee80211_tx_info *info, +=09=09=09 int len); + #endif /* MAC80211_H */ diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 4f03ebe732fa..6cbb1286d6c0 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -32,7 +32,8 @@ mac80211-y :=3D \ =09chan.o \ =09trace.o mlme.o \ =09tdls.o \ -=09ocb.o +=09ocb.o \ +=09airtime.o =20 mac80211-$(CONFIG_MAC80211_LEDS) +=3D led.o mac80211-$(CONFIG_MAC80211_DEBUGFS) +=3D \ diff --git a/net/mac80211/airtime.c b/net/mac80211/airtime.c new file mode 100644 index 000000000000..a521f3730381 --- /dev/null +++ b/net/mac80211/airtime.c @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (C) 2019 Felix Fietkau + */ + +#include +#include "ieee80211_i.h" +#include "sta_info.h" + +#define AVG_PKT_SIZE=091024 + +/* Number of bits for an average sized packet */ +#define MCS_NBITS (AVG_PKT_SIZE << 3) + +/* Number of symbols for a packet with (bps) bits per symbol */ +#define MCS_NSYMS(bps) DIV_ROUND_UP(MCS_NBITS, (bps)) + +/* Transmission time (1024 usec) for a packet containing (syms) * symbols = */ +#define MCS_SYMBOL_TIME(sgi, syms)=09=09=09=09=09\ +=09(sgi ?=09=09=09=09=09=09=09=09\ +=09 ((syms) * 18 * 1024 + 4 * 1024) / 5 :=09/* syms * 3.6 us */=09\ +=09 ((syms) * 1024) << 2=09=09=09/* syms * 4 us */=09\ +=09) + +/* Transmit duration for the raw data part of an average sized packet */ +#define MCS_DURATION(streams, sgi, bps) \ +=09MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps))) + +#define BW_20=09=09=090 +#define BW_40=09=09=091 +#define BW_80=09=09=092 + +/* + * Define group sort order: HT40 -> SGI -> #streams + */ +#define IEEE80211_MAX_STREAMS=09=094 +#define IEEE80211_HT_STREAM_GROUPS=094 /* BW(=3D2) * SGI(=3D2) */ +#define IEEE80211_VHT_STREAM_GROUPS=096 /* BW(=3D3) * SGI(=3D2) */ + +#define IEEE80211_HT_GROUPS_NB=09(IEEE80211_MAX_STREAMS *=09\ +=09=09=09=09 IEEE80211_HT_STREAM_GROUPS) +#define IEEE80211_VHT_GROUPS_NB=09(IEEE80211_MAX_STREAMS *=09\ +=09=09=09=09=09 IEEE80211_VHT_STREAM_GROUPS) +#define IEEE80211_GROUPS_NB=09(IEEE80211_HT_GROUPS_NB +=09\ +=09=09=09=09 IEEE80211_VHT_GROUPS_NB) + +#define IEEE80211_HT_GROUP_0=090 +#define IEEE80211_VHT_GROUP_0=09(IEEE80211_HT_GROUP_0 + IEEE80211_HT_GROUP= S_NB) + +#define MCS_GROUP_RATES=09=0910 +#define CCK_NUM_RATES=09=09=094 + +#define HT_GROUP_IDX(_streams, _sgi, _ht40)=09\ +=09IEEE80211_HT_GROUP_0 +=09=09=09\ +=09IEEE80211_MAX_STREAMS * 2 * _ht40 +=09\ +=09IEEE80211_MAX_STREAMS * _sgi +=09=09\ +=09_streams - 1 + +#define _MAX(a, b) (((a)>(b))?(a):(b)) + +#define GROUP_SHIFT(duration)=09=09=09=09=09=09\ +=09_MAX(0, 16 - __builtin_clz(duration)) + +/* MCS rate information for an MCS group */ +#define __MCS_GROUP(_streams, _sgi, _ht40, _s)=09=09=09=09\ +=09[HT_GROUP_IDX(_streams, _sgi, _ht40)] =3D {=09=09=09\ +=09.shift =3D _s,=09=09=09=09=09=09=09\ +=09.duration =3D {=09=09=09=09=09=09=09\ +=09=09MCS_DURATION(_streams, _sgi, _ht40 ? 54 : 26) >> _s,=09\ +=09=09MCS_DURATION(_streams, _sgi, _ht40 ? 108 : 52) >> _s,=09\ +=09=09MCS_DURATION(_streams, _sgi, _ht40 ? 162 : 78) >> _s,=09\ +=09=09MCS_DURATION(_streams, _sgi, _ht40 ? 216 : 104) >> _s,=09\ +=09=09MCS_DURATION(_streams, _sgi, _ht40 ? 324 : 156) >> _s,=09\ +=09=09MCS_DURATION(_streams, _sgi, _ht40 ? 432 : 208) >> _s,=09\ +=09=09MCS_DURATION(_streams, _sgi, _ht40 ? 486 : 234) >> _s,=09\ +=09=09MCS_DURATION(_streams, _sgi, _ht40 ? 540 : 260) >> _s=09\ +=09}=09=09=09=09=09=09=09=09\ +} + +#define MCS_GROUP_SHIFT(_streams, _sgi, _ht40)=09=09=09=09\ +=09GROUP_SHIFT(MCS_DURATION(_streams, _sgi, _ht40 ? 54 : 26)) + +#define MCS_GROUP(_streams, _sgi, _ht40)=09=09=09=09\ +=09__MCS_GROUP(_streams, _sgi, _ht40,=09=09=09=09\ +=09=09 MCS_GROUP_SHIFT(_streams, _sgi, _ht40)) + +#define VHT_GROUP_IDX(_streams, _sgi, _bw)=09=09=09=09\ +=09(IEEE80211_VHT_GROUP_0 +=09=09=09=09=09\ +=09 IEEE80211_MAX_STREAMS * 2 * (_bw) +=09=09=09=09\ +=09 IEEE80211_MAX_STREAMS * (_sgi) +=09=09=09=09\ +=09 (_streams) - 1) + +#define BW2VBPS(_bw, r3, r2, r1)=09=09=09=09=09\ +=09(_bw =3D=3D BW_80 ? r3 : _bw =3D=3D BW_40 ? r2 : r1) + +#define __VHT_GROUP(_streams, _sgi, _bw, _s)=09=09=09=09\ +=09[VHT_GROUP_IDX(_streams, _sgi, _bw)] =3D {=09=09=09\ +=09.shift =3D _s,=09=09=09=09=09=09=09\ +=09.duration =3D {=09=09=09=09=09=09=09\ +=09=09MCS_DURATION(_streams, _sgi,=09=09=09=09\ +=09=09=09 BW2VBPS(_bw, 117, 54, 26)) >> _s,=09\ +=09=09MCS_DURATION(_streams, _sgi,=09=09=09=09\ +=09=09=09 BW2VBPS(_bw, 234, 108, 52)) >> _s,=09\ +=09=09MCS_DURATION(_streams, _sgi,=09=09=09=09\ +=09=09=09 BW2VBPS(_bw, 351, 162, 78)) >> _s,=09\ +=09=09MCS_DURATION(_streams, _sgi,=09=09=09=09\ +=09=09=09 BW2VBPS(_bw, 468, 216, 104)) >> _s,=09\ +=09=09MCS_DURATION(_streams, _sgi,=09=09=09=09\ +=09=09=09 BW2VBPS(_bw, 702, 324, 156)) >> _s,=09\ +=09=09MCS_DURATION(_streams, _sgi,=09=09=09=09\ +=09=09=09 BW2VBPS(_bw, 936, 432, 208)) >> _s,=09\ +=09=09MCS_DURATION(_streams, _sgi,=09=09=09=09\ +=09=09=09 BW2VBPS(_bw, 1053, 486, 234)) >> _s,=09\ +=09=09MCS_DURATION(_streams, _sgi,=09=09=09=09\ +=09=09=09 BW2VBPS(_bw, 1170, 540, 260)) >> _s,=09\ +=09=09MCS_DURATION(_streams, _sgi,=09=09=09=09\ +=09=09=09 BW2VBPS(_bw, 1404, 648, 312)) >> _s,=09\ +=09=09MCS_DURATION(_streams, _sgi,=09=09=09=09\ +=09=09=09 BW2VBPS(_bw, 1560, 720, 346)) >> _s=09\ +=09}=09=09=09=09=09=09=09=09\ +} + +#define VHT_GROUP_SHIFT(_streams, _sgi, _bw)=09=09=09=09\ +=09GROUP_SHIFT(MCS_DURATION(_streams, _sgi,=09=09=09\ +=09=09=09=09 BW2VBPS(_bw, 117, 54, 26))) + +#define VHT_GROUP(_streams, _sgi, _bw)=09=09=09=09=09\ +=09__VHT_GROUP(_streams, _sgi, _bw,=09=09=09=09\ +=09=09 VHT_GROUP_SHIFT(_streams, _sgi, _bw)) + +struct mcs_group { +=09u8 shift; +=09u16 duration[MCS_GROUP_RATES]; +}; + +static const struct mcs_group airtime_mcs_groups[] =3D { +=09MCS_GROUP(1, 0, BW_20), +=09MCS_GROUP(2, 0, BW_20), +=09MCS_GROUP(3, 0, BW_20), +=09MCS_GROUP(4, 0, BW_20), + +=09MCS_GROUP(1, 1, BW_20), +=09MCS_GROUP(2, 1, BW_20), +=09MCS_GROUP(3, 1, BW_20), +=09MCS_GROUP(4, 1, BW_20), + +=09MCS_GROUP(1, 0, BW_40), +=09MCS_GROUP(2, 0, BW_40), +=09MCS_GROUP(3, 0, BW_40), +=09MCS_GROUP(4, 0, BW_40), + +=09MCS_GROUP(1, 1, BW_40), +=09MCS_GROUP(2, 1, BW_40), +=09MCS_GROUP(3, 1, BW_40), +=09MCS_GROUP(4, 1, BW_40), + +=09VHT_GROUP(1, 0, BW_20), +=09VHT_GROUP(2, 0, BW_20), +=09VHT_GROUP(3, 0, BW_20), +=09VHT_GROUP(4, 0, BW_20), + +=09VHT_GROUP(1, 1, BW_20), +=09VHT_GROUP(2, 1, BW_20), +=09VHT_GROUP(3, 1, BW_20), +=09VHT_GROUP(4, 1, BW_20), + +=09VHT_GROUP(1, 0, BW_40), +=09VHT_GROUP(2, 0, BW_40), +=09VHT_GROUP(3, 0, BW_40), +=09VHT_GROUP(4, 0, BW_40), + +=09VHT_GROUP(1, 1, BW_40), +=09VHT_GROUP(2, 1, BW_40), +=09VHT_GROUP(3, 1, BW_40), +=09VHT_GROUP(4, 1, BW_40), + +=09VHT_GROUP(1, 0, BW_80), +=09VHT_GROUP(2, 0, BW_80), +=09VHT_GROUP(3, 0, BW_80), +=09VHT_GROUP(4, 0, BW_80), + +=09VHT_GROUP(1, 1, BW_80), +=09VHT_GROUP(2, 1, BW_80), +=09VHT_GROUP(3, 1, BW_80), +=09VHT_GROUP(4, 1, BW_80), +}; + +static u32 +ieee80211_calc_legacy_rate_duration(u16 bitrate, bool short_pre, +=09=09=09=09 bool cck, int len) +{ +=09u32 duration; + +=09if (cck) { +=09=09duration =3D 144 + 48; /* preamble + PLCP */ +=09=09if (short_pre) +=09=09=09duration >>=3D 1; + +=09=09duration +=3D 10; /* SIFS */ +=09} else { +=09=09duration =3D 20 + 16; /* premable + SIFS */ +=09} + +=09len <<=3D 3; +=09duration +=3D (len * 10) / bitrate; + +=09return duration; +} + +u32 ieee80211_calc_rx_airtime(struct ieee80211_hw *hw, +=09=09=09 struct ieee80211_rx_status *status, +=09=09=09 int len) +{ +=09struct ieee80211_supported_band *sband; +=09const struct ieee80211_rate *rate; +=09bool sgi =3D status->enc_flags & RX_ENC_FLAG_SHORT_GI; +=09bool sp =3D status->enc_flags & RX_ENC_FLAG_SHORTPRE; +=09int bw, streams; +=09u32 duration; +=09int group, idx; +=09bool cck; + +=09switch (status->bw) { +=09case RATE_INFO_BW_20: +=09=09bw =3D BW_20; +=09=09break; +=09case RATE_INFO_BW_40: +=09=09bw =3D BW_40; +=09=09break; +=09case RATE_INFO_BW_80: +=09=09bw =3D BW_80; +=09=09break; +=09default: +=09=09WARN_ON_ONCE(1); +=09=09return 0; +=09} + +=09switch (status->encoding) { +=09case RX_ENC_LEGACY: +=09=09if (WARN_ON_ONCE(status->band > NL80211_BAND_5GHZ)) +=09=09=09return 0; + +=09=09sband =3D hw->wiphy->bands[status->band]; +=09=09if (!sband || status->rate_idx > sband->n_bitrates) +=09=09=09return 0; + +=09=09rate =3D &sband->bitrates[status->rate_idx]; +=09=09cck =3D (status->rate_idx < CCK_NUM_RATES); + +=09=09return ieee80211_calc_legacy_rate_duration(rate->bitrate, sp, +=09=09=09=09=09=09=09 cck, len); + +=09case RX_ENC_VHT: +=09=09streams =3D status->nss; +=09=09idx =3D status->rate_idx; +=09=09group =3D VHT_GROUP_IDX(streams, sgi, bw); +=09=09break; +=09case RX_ENC_HT: +=09=09streams =3D ((status->rate_idx >> 3) & 3) + 1; +=09=09idx =3D status->rate_idx & 7; +=09=09group =3D HT_GROUP_IDX(streams, sgi, bw); +=09=09break; +=09default: +=09=09WARN_ON_ONCE(1); +=09=09return 0; +=09} + +=09if (WARN_ON_ONCE(streams > 4)) +=09=09return 0; + +=09duration =3D airtime_mcs_groups[group].duration[idx]; +=09duration <<=3D airtime_mcs_groups[group].shift; +=09duration *=3D len; +=09duration /=3D AVG_PKT_SIZE; +=09duration /=3D 1024; + +=09duration +=3D 36 + (streams << 2); + +=09return duration; +} +EXPORT_SYMBOL_GPL(ieee80211_calc_rx_airtime); + +static u32 ieee80211_calc_tx_airtime_rate(struct ieee80211_hw *hw, +=09=09=09=09=09 struct ieee80211_tx_rate *rate, +=09=09=09=09=09 u8 band, int len) +{ +=09struct ieee80211_rx_status stat =3D { +=09=09.band =3D band, +=09}; + +=09if (rate->idx < 0 || !rate->count) +=09=09return 0; + +=09if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH) +=09=09stat.bw =3D RATE_INFO_BW_80; +=09else if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) +=09=09stat.bw =3D RATE_INFO_BW_40; +=09else +=09=09stat.bw =3D RATE_INFO_BW_20; + +=09stat.enc_flags =3D 0; +=09if (rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) +=09=09stat.enc_flags |=3D RX_ENC_FLAG_SHORTPRE; +=09if (rate->flags & IEEE80211_TX_RC_SHORT_GI) +=09=09stat.enc_flags |=3D RX_ENC_FLAG_SHORT_GI; + +=09stat.rate_idx =3D rate->idx; +=09if (rate->flags & IEEE80211_TX_RC_VHT_MCS) { +=09=09stat.encoding =3D RX_ENC_VHT; +=09=09stat.rate_idx =3D ieee80211_rate_get_vht_mcs(rate); +=09=09stat.nss =3D ieee80211_rate_get_vht_nss(rate); +=09} else if (rate->flags & IEEE80211_TX_RC_MCS) { +=09=09stat.encoding =3D RX_ENC_HT; +=09} else { +=09=09stat.encoding =3D RX_ENC_LEGACY; +=09} + +=09return ieee80211_calc_rx_airtime(hw, &stat, len); +} + +u32 ieee80211_calc_tx_airtime(struct ieee80211_hw *hw, +=09=09=09 struct ieee80211_tx_info *info, +=09=09=09 int len) +{ +=09u32 duration =3D 0; +=09int i; + +=09for (i =3D 0; i < ARRAY_SIZE(info->status.rates); i++) { +=09=09struct ieee80211_tx_rate *rate =3D &info->status.rates[i]; +=09=09u32 cur_duration; + +=09=09cur_duration =3D ieee80211_calc_tx_airtime_rate(hw, rate, +=09=09=09=09=09=09=09 info->band, len); +=09=09if (!cur_duration) +=09=09=09break; + +=09=09duration +=3D cur_duration * rate->count; +=09} + +=09return duration; +} +EXPORT_SYMBOL_GPL(ieee80211_calc_tx_airtime); + +u32 ieee80211_calc_expected_tx_airtime(struct ieee80211_hw *hw, +=09=09=09=09 struct ieee80211_vif *vif, +=09=09=09=09 struct ieee80211_sta *pubsta, +=09=09=09=09 int len) +{ +=09struct ieee80211_supported_band *sband; +=09struct ieee80211_chanctx_conf *conf; +=09int rateidx, shift =3D 0; +=09bool cck, short_pream; +=09u32 basic_rates; +=09u8 band =3D 0; +=09u16 rate; + +=09len +=3D 38; /* Ethernet header length */ + +=09conf =3D rcu_dereference(vif->chanctx_conf); +=09if (conf) { +=09=09band =3D conf->def.chan->band; +=09=09shift =3D ieee80211_chandef_get_shift(&conf->def); +=09} + +=09if (pubsta) { +=09=09struct sta_info *sta =3D container_of(pubsta, struct sta_info, +=09=09=09=09=09=09 sta); + +=09=09return ieee80211_calc_tx_airtime_rate(hw, +=09=09=09=09=09=09 &sta->tx_stats.last_rate, +=09=09=09=09=09=09 band, len); +=09} + +=09if (!conf) +=09=09return 0; + +=09/* No station to get latest rate from, so calculate the worst-case +=09 * duration using the lowest configured basic rate. +=09 */ +=09sband =3D hw->wiphy->bands[band]; + +=09basic_rates =3D vif->bss_conf.basic_rates; +=09short_pream =3D vif->bss_conf.use_short_preamble; + +=09rateidx =3D basic_rates ? ffs(basic_rates) - 1 : 0; +=09rate =3D sband->bitrates[rateidx].bitrate << shift; +=09cck =3D (rateidx < CCK_NUM_RATES); + +=09return ieee80211_calc_legacy_rate_duration(rate, short_pream, cck, len)= ; +} diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 05406e9c05b3..225ea4e3cd76 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2249,6 +2249,10 @@ const char *ieee80211_get_reason_code_string(u16 rea= son_code); =20 extern const struct ethtool_ops ieee80211_ethtool_ops; =20 +u32 ieee80211_calc_expected_tx_airtime(struct ieee80211_hw *hw, +=09=09=09=09 struct ieee80211_vif *vif, +=09=09=09=09 struct ieee80211_sta *pubsta, +=09=09=09=09 int len); #ifdef CONFIG_MAC80211_NOINLINE #define debug_noinline noinline #else From mboxrd@z Thu Jan 1 00:00:00 1970 Return-path: Received: from us-smtp-1.mimecast.com ([207.211.31.81]) by bombadil.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1iMuF0-0008GS-Dj for ath10k@lists.infradead.org; Tue, 22 Oct 2019 13:30:30 +0000 Received: by mail-wm1-f69.google.com with SMTP id g13so444289wme.0 for ; Tue, 22 Oct 2019 06:30:20 -0700 (PDT) Subject: [PATCH v5 2/4] mac80211: Import airtime calculation code from mt76 From: =?utf-8?q?Toke_H=C3=B8iland-J=C3=B8rgensen?= Date: Tue, 22 Oct 2019 15:30:17 +0200 Message-ID: <157175101738.104114.14883771055318054924.stgit@toke.dk> In-Reply-To: <157175101518.104114.6722791270722911023.stgit@toke.dk> References: <157175101518.104114.6722791270722911023.stgit@toke.dk> MIME-Version: 1.0 List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: base64 Sender: "ath10k" Errors-To: ath10k-bounces+kvalo=adurom.com@lists.infradead.org To: Johannes Berg Cc: Kan Yan , Rajkumar Manoharan , Kevin Hayes , make-wifi-fast@lists.bufferbloat.net, linux-wireless@vger.kernel.org, ath10k@lists.infradead.org, John Crispin , Lorenzo Bianconi , Felix Fietkau RnJvbTogVG9rZSBIw7hpbGFuZC1Kw7hyZ2Vuc2VuIDx0b2tlQHJlZGhhdC5jb20+CgpGZWxpeCBy ZWNlbnRseSBhZGRlZCBjb2RlIHRvIGNhbGN1bGF0ZSBhaXJ0aW1lIG9mIHBhY2tldHMgdG8gdGhl IG10NzYKZHJpdmVyLiBJbXBvcnQgdGhpcyBpbnRvIG1hYzgwMjExIHNvIHdlIGNhbiB1c2UgaXQg Zm9yIGFpcnRpbWUgcXVldWUgbGltaXQKY2FsY3VsYXRpb25zIGxhdGVyLgoKVGhlIGFpcnRpbWUu YyBmaWxlIGlzIGNvcGllZCB2ZXJiYXRpbSBmcm9tIHRoZSBtdDc2IGRyaXZlciwgYW5kIGFkanVz dGVkIHRvCnVzZSBtYWM4MDIxMSBkYXRhIHN0cnVjdHVyZXMgaW5zdGVhZCAod2hpY2ggaXMgZmFp cmx5IHN0cmFpZ2h0IGZvcndhcmQpLgpUaGUgcGVyLXJhdGUgVFggcmF0ZSBjYWxjdWxhdGlvbiBp cyBzcGxpdCBvdXQgdG8gaXRzIG93bgpmdW5jdGlvbiAoaWVlZTgwMjExX2NhbGNfdHhfYWlydGlt ZV9yYXRlKCkpIHNvIGl0IGNhbiBiZSB1c2VkIGRpcmVjdGx5IGZvcgp0aGUgQVFMIGNhbGN1bGF0 aW9ucyBhZGRlZCBpbiBhIHN1YnNlcXVlbnQgcGF0Y2guCgpUaGUgb25seSB0aGluZyB0aGF0IGl0 IHdhcyBub3QgcG9zc2libGUgdG8gcG9ydCBkaXJlY3RseSB3YXMgdGhlIGJpdCB0aGF0CnJlYWQg dGhlIGludGVybmFsIGRyaXZlciBmbGFncyBvZiBzdHJ1Y3QgaWVlZTgwMjExX3JhdGUgdG8gZGV0 ZXJtaW5lCndoZXRoZXIgYSByYXRlIGlzIHVzaW5nIENDSyBvciBPRkRNIGVuY29kaW5nLiBJbnN0 ZWFkLCBqdXN0IGxvb2sgYXQgdGhlCnJhdGUgaW5kZXgsIHNpbmNlIGF0IGxlYXN0IG10NzYgYW5k IGF0aDEwayBib3RoIHNlZW0gdG8gaGF2ZSB0aGUgc2FtZQpudW1iZXIgb2YgQ0NLIHJhdGVzICg0 KSBpbiB0aGVpciB0YWJsZXMuCgpTaWduZWQtb2ZmLWJ5OiBUb2tlIEjDuGlsYW5kLUrDuHJnZW5z ZW4gPHRva2VAcmVkaGF0LmNvbT4KLS0tCiBpbmNsdWRlL25ldC9tYWM4MDIxMS5oICAgICB8ICAg MjkgKysrCiBuZXQvbWFjODAyMTEvTWFrZWZpbGUgICAgICB8ICAgIDMgCiBuZXQvbWFjODAyMTEv YWlydGltZS5jICAgICB8ICAzOTAgKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysr KysrKysrKysKIG5ldC9tYWM4MDIxMS9pZWVlODAyMTFfaS5oIHwgICAgNCAKIDQgZmlsZXMgY2hh bmdlZCwgNDI1IGluc2VydGlvbnMoKyksIDEgZGVsZXRpb24oLSkKIGNyZWF0ZSBtb2RlIDEwMDY0 NCBuZXQvbWFjODAyMTEvYWlydGltZS5jCgpkaWZmIC0tZ2l0IGEvaW5jbHVkZS9uZXQvbWFjODAy MTEuaCBiL2luY2x1ZGUvbmV0L21hYzgwMjExLmgKaW5kZXggNDI4OGFjZTcyYzJiLi44YTNlMDU0 NGEwMjYgMTAwNjQ0Ci0tLSBhL2luY2x1ZGUvbmV0L21hYzgwMjExLmgKKysrIGIvaW5jbHVkZS9u ZXQvbWFjODAyMTEuaApAQCAtNjQyNCw0ICs2NDI0LDMzIEBAIHZvaWQgaWVlZTgwMjExX25hbl9m dW5jX21hdGNoKHN0cnVjdCBpZWVlODAyMTFfdmlmICp2aWYsCiAJCQkgICAgICBzdHJ1Y3QgY2Zn ODAyMTFfbmFuX21hdGNoX3BhcmFtcyAqbWF0Y2gsCiAJCQkgICAgICBnZnBfdCBnZnApOwogCisv KioKKyAqIGllZWU4MDIxMV9jYWxjX3J4X2FpcnRpbWUgLSBjYWxjdWxhdGUgZXN0aW1hdGVkIHRy YW5zbWlzc2lvbiBhaXJ0aW1lIGZvciBSWC4KKyAqCisgKiBUaGlzIGZ1bmN0aW9uIGNhbGN1bGF0 ZXMgdGhlIGVzdGltYXRlZCBhaXJ0aW1lIHVzYWdlIG9mIGEgZnJhbWUgYmFzZWQgb24gdGhlCisg KiByYXRlIGluZm9ybWF0aW9uIGluIHRoZSBSWCBzdGF0dXMgc3RydWN0IGFuZCB0aGUgZnJhbWUg bGVuZ3RoLgorICoKKyAqIEBodzogcG9pbnRlciBhcyBvYnRhaW5lZCBmcm9tIGllZWU4MDIxMV9h bGxvY19odygpCisgKiBAc3RhdHVzOiAmc3RydWN0IGllZWU4MDIxMV9yeF9zdGF0dXMgY29udGFp bmluZyB0aGUgdHJhbnNtaXNzaW9uIHJhdGUKKyAqICAgICAgICAgIGluZm9ybWF0aW9uLgorICog QGxlbjogZnJhbWUgbGVuZ3RoIGluIGJ5dGVzCisgKi8KK3UzMiBpZWVlODAyMTFfY2FsY19yeF9h aXJ0aW1lKHN0cnVjdCBpZWVlODAyMTFfaHcgKmh3LAorCQkJICAgICAgc3RydWN0IGllZWU4MDIx MV9yeF9zdGF0dXMgKnN0YXR1cywKKwkJCSAgICAgIGludCBsZW4pOworCisvKioKKyAqIGllZWU4 MDIxMV9jYWxjX3R4X2FpcnRpbWUgLSBjYWxjdWxhdGUgZXN0aW1hdGVkIHRyYW5zbWlzc2lvbiBh aXJ0aW1lIGZvciBUWC4KKyAqCisgKiBUaGlzIGZ1bmN0aW9uIGNhbGN1bGF0ZXMgdGhlIGVzdGlt YXRlZCBhaXJ0aW1lIHVzYWdlIG9mIGEgZnJhbWUgYmFzZWQgb24gdGhlCisgKiByYXRlIGluZm9y bWF0aW9uIGluIHRoZSBUWCBpbmZvIHN0cnVjdCBhbmQgdGhlIGZyYW1lIGxlbmd0aC4KKyAqCisg KiBAaHc6IHBvaW50ZXIgYXMgb2J0YWluZWQgZnJvbSBpZWVlODAyMTFfYWxsb2NfaHcoKQorICog QGluZm86ICZzdHJ1Y3QgaWVlZTgwMjExX3R4X2luZm8gb2YgdGhlIGZyYW1lLgorICogQGxlbjog ZnJhbWUgbGVuZ3RoIGluIGJ5dGVzCisgKi8KK3UzMiBpZWVlODAyMTFfY2FsY190eF9haXJ0aW1l KHN0cnVjdCBpZWVlODAyMTFfaHcgKmh3LAorCQkJICAgICAgc3RydWN0IGllZWU4MDIxMV90eF9p bmZvICppbmZvLAorCQkJICAgICAgaW50IGxlbik7CisKICNlbmRpZiAvKiBNQUM4MDIxMV9IICov CmRpZmYgLS1naXQgYS9uZXQvbWFjODAyMTEvTWFrZWZpbGUgYi9uZXQvbWFjODAyMTEvTWFrZWZp bGUKaW5kZXggNGYwM2ViZTczMmZhLi42Y2JiMTI4NmQ2YzAgMTAwNjQ0Ci0tLSBhL25ldC9tYWM4 MDIxMS9NYWtlZmlsZQorKysgYi9uZXQvbWFjODAyMTEvTWFrZWZpbGUKQEAgLTMyLDcgKzMyLDgg QEAgbWFjODAyMTEteSA6PSBcCiAJY2hhbi5vIFwKIAl0cmFjZS5vIG1sbWUubyBcCiAJdGRscy5v IFwKLQlvY2IubworCW9jYi5vIFwKKwlhaXJ0aW1lLm8KIAogbWFjODAyMTEtJChDT05GSUdfTUFD ODAyMTFfTEVEUykgKz0gbGVkLm8KIG1hYzgwMjExLSQoQ09ORklHX01BQzgwMjExX0RFQlVHRlMp ICs9IFwKZGlmZiAtLWdpdCBhL25ldC9tYWM4MDIxMS9haXJ0aW1lLmMgYi9uZXQvbWFjODAyMTEv YWlydGltZS5jCm5ldyBmaWxlIG1vZGUgMTAwNjQ0CmluZGV4IDAwMDAwMDAwMDAwMC4uYTUyMWYz NzMwMzgxCi0tLSAvZGV2L251bGwKKysrIGIvbmV0L21hYzgwMjExL2FpcnRpbWUuYwpAQCAtMCww ICsxLDM5MCBAQAorLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IElTQworLyoKKyAqIENvcHly aWdodCAoQykgMjAxOSBGZWxpeCBGaWV0a2F1IDxuYmRAbmJkLm5hbWU+CisgKi8KKworI2luY2x1 ZGUgPG5ldC9tYWM4MDIxMS5oPgorI2luY2x1ZGUgImllZWU4MDIxMV9pLmgiCisjaW5jbHVkZSAi c3RhX2luZm8uaCIKKworI2RlZmluZSBBVkdfUEtUX1NJWkUJMTAyNAorCisvKiBOdW1iZXIgb2Yg Yml0cyBmb3IgYW4gYXZlcmFnZSBzaXplZCBwYWNrZXQgKi8KKyNkZWZpbmUgTUNTX05CSVRTIChB VkdfUEtUX1NJWkUgPDwgMykKKworLyogTnVtYmVyIG9mIHN5bWJvbHMgZm9yIGEgcGFja2V0IHdp dGggKGJwcykgYml0cyBwZXIgc3ltYm9sICovCisjZGVmaW5lIE1DU19OU1lNUyhicHMpIERJVl9S T1VORF9VUChNQ1NfTkJJVFMsIChicHMpKQorCisvKiBUcmFuc21pc3Npb24gdGltZSAoMTAyNCB1 c2VjKSBmb3IgYSBwYWNrZXQgY29udGFpbmluZyAoc3ltcykgKiBzeW1ib2xzICovCisjZGVmaW5l IE1DU19TWU1CT0xfVElNRShzZ2ksIHN5bXMpCQkJCQlcCisJKHNnaSA/CQkJCQkJCQlcCisJICAo KHN5bXMpICogMTggKiAxMDI0ICsgNCAqIDEwMjQpIC8gNSA6CS8qIHN5bXMgKiAzLjYgdXMgKi8J XAorCSAgKChzeW1zKSAqIDEwMjQpIDw8IDIJCQkvKiBzeW1zICogNCB1cyAqLwlcCisJKQorCisv KiBUcmFuc21pdCBkdXJhdGlvbiBmb3IgdGhlIHJhdyBkYXRhIHBhcnQgb2YgYW4gYXZlcmFnZSBz aXplZCBwYWNrZXQgKi8KKyNkZWZpbmUgTUNTX0RVUkFUSU9OKHN0cmVhbXMsIHNnaSwgYnBzKSBc CisJTUNTX1NZTUJPTF9USU1FKHNnaSwgTUNTX05TWU1TKChzdHJlYW1zKSAqIChicHMpKSkKKwor I2RlZmluZSBCV18yMAkJCTAKKyNkZWZpbmUgQldfNDAJCQkxCisjZGVmaW5lIEJXXzgwCQkJMgor CisvKgorICogRGVmaW5lIGdyb3VwIHNvcnQgb3JkZXI6IEhUNDAgLT4gU0dJIC0+ICNzdHJlYW1z CisgKi8KKyNkZWZpbmUgSUVFRTgwMjExX01BWF9TVFJFQU1TCQk0CisjZGVmaW5lIElFRUU4MDIx MV9IVF9TVFJFQU1fR1JPVVBTCTQgLyogQlcoPTIpICogU0dJKD0yKSAqLworI2RlZmluZSBJRUVF ODAyMTFfVkhUX1NUUkVBTV9HUk9VUFMJNiAvKiBCVyg9MykgKiBTR0koPTIpICovCisKKyNkZWZp bmUgSUVFRTgwMjExX0hUX0dST1VQU19OQgkoSUVFRTgwMjExX01BWF9TVFJFQU1TICoJXAorCQkJ CSBJRUVFODAyMTFfSFRfU1RSRUFNX0dST1VQUykKKyNkZWZpbmUgSUVFRTgwMjExX1ZIVF9HUk9V UFNfTkIJKElFRUU4MDIxMV9NQVhfU1RSRUFNUyAqCVwKKwkJCQkJIElFRUU4MDIxMV9WSFRfU1RS RUFNX0dST1VQUykKKyNkZWZpbmUgSUVFRTgwMjExX0dST1VQU19OQgkoSUVFRTgwMjExX0hUX0dS T1VQU19OQiArCVwKKwkJCQkgSUVFRTgwMjExX1ZIVF9HUk9VUFNfTkIpCisKKyNkZWZpbmUgSUVF RTgwMjExX0hUX0dST1VQXzAJMAorI2RlZmluZSBJRUVFODAyMTFfVkhUX0dST1VQXzAJKElFRUU4 MDIxMV9IVF9HUk9VUF8wICsgSUVFRTgwMjExX0hUX0dST1VQU19OQikKKworI2RlZmluZSBNQ1Nf R1JPVVBfUkFURVMJCTEwCisjZGVmaW5lIENDS19OVU1fUkFURVMJCQk0CisKKyNkZWZpbmUgSFRf R1JPVVBfSURYKF9zdHJlYW1zLCBfc2dpLCBfaHQ0MCkJXAorCUlFRUU4MDIxMV9IVF9HUk9VUF8w ICsJCQlcCisJSUVFRTgwMjExX01BWF9TVFJFQU1TICogMiAqIF9odDQwICsJXAorCUlFRUU4MDIx MV9NQVhfU1RSRUFNUyAqIF9zZ2kgKwkJXAorCV9zdHJlYW1zIC0gMQorCisjZGVmaW5lIF9NQVgo YSwgYikgKCgoYSk+KGIpKT8oYSk6KGIpKQorCisjZGVmaW5lIEdST1VQX1NISUZUKGR1cmF0aW9u KQkJCQkJCVwKKwlfTUFYKDAsIDE2IC0gX19idWlsdGluX2NseihkdXJhdGlvbikpCisKKy8qIE1D UyByYXRlIGluZm9ybWF0aW9uIGZvciBhbiBNQ1MgZ3JvdXAgKi8KKyNkZWZpbmUgX19NQ1NfR1JP VVAoX3N0cmVhbXMsIF9zZ2ksIF9odDQwLCBfcykJCQkJXAorCVtIVF9HUk9VUF9JRFgoX3N0cmVh bXMsIF9zZ2ksIF9odDQwKV0gPSB7CQkJXAorCS5zaGlmdCA9IF9zLAkJCQkJCQlcCisJLmR1cmF0 aW9uID0gewkJCQkJCQlcCisJCU1DU19EVVJBVElPTihfc3RyZWFtcywgX3NnaSwgX2h0NDAgPyA1 NCA6IDI2KSA+PiBfcywJXAorCQlNQ1NfRFVSQVRJT04oX3N0cmVhbXMsIF9zZ2ksIF9odDQwID8g MTA4IDogNTIpID4+IF9zLAlcCisJCU1DU19EVVJBVElPTihfc3RyZWFtcywgX3NnaSwgX2h0NDAg PyAxNjIgOiA3OCkgPj4gX3MsCVwKKwkJTUNTX0RVUkFUSU9OKF9zdHJlYW1zLCBfc2dpLCBfaHQ0 MCA/IDIxNiA6IDEwNCkgPj4gX3MsCVwKKwkJTUNTX0RVUkFUSU9OKF9zdHJlYW1zLCBfc2dpLCBf aHQ0MCA/IDMyNCA6IDE1NikgPj4gX3MsCVwKKwkJTUNTX0RVUkFUSU9OKF9zdHJlYW1zLCBfc2dp LCBfaHQ0MCA/IDQzMiA6IDIwOCkgPj4gX3MsCVwKKwkJTUNTX0RVUkFUSU9OKF9zdHJlYW1zLCBf c2dpLCBfaHQ0MCA/IDQ4NiA6IDIzNCkgPj4gX3MsCVwKKwkJTUNTX0RVUkFUSU9OKF9zdHJlYW1z LCBfc2dpLCBfaHQ0MCA/IDU0MCA6IDI2MCkgPj4gX3MJXAorCX0JCQkJCQkJCVwKK30KKworI2Rl ZmluZSBNQ1NfR1JPVVBfU0hJRlQoX3N0cmVhbXMsIF9zZ2ksIF9odDQwKQkJCQlcCisJR1JPVVBf U0hJRlQoTUNTX0RVUkFUSU9OKF9zdHJlYW1zLCBfc2dpLCBfaHQ0MCA/IDU0IDogMjYpKQorCisj ZGVmaW5lIE1DU19HUk9VUChfc3RyZWFtcywgX3NnaSwgX2h0NDApCQkJCVwKKwlfX01DU19HUk9V UChfc3RyZWFtcywgX3NnaSwgX2h0NDAsCQkJCVwKKwkJICAgIE1DU19HUk9VUF9TSElGVChfc3Ry ZWFtcywgX3NnaSwgX2h0NDApKQorCisjZGVmaW5lIFZIVF9HUk9VUF9JRFgoX3N0cmVhbXMsIF9z Z2ksIF9idykJCQkJXAorCShJRUVFODAyMTFfVkhUX0dST1VQXzAgKwkJCQkJXAorCSBJRUVFODAy MTFfTUFYX1NUUkVBTVMgKiAyICogKF9idykgKwkJCQlcCisJIElFRUU4MDIxMV9NQVhfU1RSRUFN UyAqIChfc2dpKSArCQkJCVwKKwkgKF9zdHJlYW1zKSAtIDEpCisKKyNkZWZpbmUgQlcyVkJQUyhf YncsIHIzLCByMiwgcjEpCQkJCQlcCisJKF9idyA9PSBCV184MCA/IHIzIDogX2J3ID09IEJXXzQw ID8gcjIgOiByMSkKKworI2RlZmluZSBfX1ZIVF9HUk9VUChfc3RyZWFtcywgX3NnaSwgX2J3LCBf cykJCQkJXAorCVtWSFRfR1JPVVBfSURYKF9zdHJlYW1zLCBfc2dpLCBfYncpXSA9IHsJCQlcCisJ LnNoaWZ0ID0gX3MsCQkJCQkJCVwKKwkuZHVyYXRpb24gPSB7CQkJCQkJCVwKKwkJTUNTX0RVUkFU SU9OKF9zdHJlYW1zLCBfc2dpLAkJCQlcCisJCQkgICAgIEJXMlZCUFMoX2J3LCAgMTE3LCAgNTQs ICAyNikpID4+IF9zLAlcCisJCU1DU19EVVJBVElPTihfc3RyZWFtcywgX3NnaSwJCQkJXAorCQkJ ICAgICBCVzJWQlBTKF9idywgIDIzNCwgMTA4LCAgNTIpKSA+PiBfcywJXAorCQlNQ1NfRFVSQVRJ T04oX3N0cmVhbXMsIF9zZ2ksCQkJCVwKKwkJCSAgICAgQlcyVkJQUyhfYncsICAzNTEsIDE2Miwg IDc4KSkgPj4gX3MsCVwKKwkJTUNTX0RVUkFUSU9OKF9zdHJlYW1zLCBfc2dpLAkJCQlcCisJCQkg ICAgIEJXMlZCUFMoX2J3LCAgNDY4LCAyMTYsIDEwNCkpID4+IF9zLAlcCisJCU1DU19EVVJBVElP Tihfc3RyZWFtcywgX3NnaSwJCQkJXAorCQkJICAgICBCVzJWQlBTKF9idywgIDcwMiwgMzI0LCAx NTYpKSA+PiBfcywJXAorCQlNQ1NfRFVSQVRJT04oX3N0cmVhbXMsIF9zZ2ksCQkJCVwKKwkJCSAg ICAgQlcyVkJQUyhfYncsICA5MzYsIDQzMiwgMjA4KSkgPj4gX3MsCVwKKwkJTUNTX0RVUkFUSU9O KF9zdHJlYW1zLCBfc2dpLAkJCQlcCisJCQkgICAgIEJXMlZCUFMoX2J3LCAxMDUzLCA0ODYsIDIz NCkpID4+IF9zLAlcCisJCU1DU19EVVJBVElPTihfc3RyZWFtcywgX3NnaSwJCQkJXAorCQkJICAg ICBCVzJWQlBTKF9idywgMTE3MCwgNTQwLCAyNjApKSA+PiBfcywJXAorCQlNQ1NfRFVSQVRJT04o X3N0cmVhbXMsIF9zZ2ksCQkJCVwKKwkJCSAgICAgQlcyVkJQUyhfYncsIDE0MDQsIDY0OCwgMzEy KSkgPj4gX3MsCVwKKwkJTUNTX0RVUkFUSU9OKF9zdHJlYW1zLCBfc2dpLAkJCQlcCisJCQkgICAg IEJXMlZCUFMoX2J3LCAxNTYwLCA3MjAsIDM0NikpID4+IF9zCVwKKwl9CQkJCQkJCQlcCit9CisK KyNkZWZpbmUgVkhUX0dST1VQX1NISUZUKF9zdHJlYW1zLCBfc2dpLCBfYncpCQkJCVwKKwlHUk9V UF9TSElGVChNQ1NfRFVSQVRJT04oX3N0cmVhbXMsIF9zZ2ksCQkJXAorCQkJCSBCVzJWQlBTKF9i dywgIDExNywgIDU0LCAgMjYpKSkKKworI2RlZmluZSBWSFRfR1JPVVAoX3N0cmVhbXMsIF9zZ2ks IF9idykJCQkJCVwKKwlfX1ZIVF9HUk9VUChfc3RyZWFtcywgX3NnaSwgX2J3LAkJCQlcCisJCSAg ICBWSFRfR1JPVVBfU0hJRlQoX3N0cmVhbXMsIF9zZ2ksIF9idykpCisKK3N0cnVjdCBtY3NfZ3Jv dXAgeworCXU4IHNoaWZ0OworCXUxNiBkdXJhdGlvbltNQ1NfR1JPVVBfUkFURVNdOworfTsKKwor c3RhdGljIGNvbnN0IHN0cnVjdCBtY3NfZ3JvdXAgYWlydGltZV9tY3NfZ3JvdXBzW10gPSB7CisJ TUNTX0dST1VQKDEsIDAsIEJXXzIwKSwKKwlNQ1NfR1JPVVAoMiwgMCwgQldfMjApLAorCU1DU19H Uk9VUCgzLCAwLCBCV18yMCksCisJTUNTX0dST1VQKDQsIDAsIEJXXzIwKSwKKworCU1DU19HUk9V UCgxLCAxLCBCV18yMCksCisJTUNTX0dST1VQKDIsIDEsIEJXXzIwKSwKKwlNQ1NfR1JPVVAoMywg MSwgQldfMjApLAorCU1DU19HUk9VUCg0LCAxLCBCV18yMCksCisKKwlNQ1NfR1JPVVAoMSwgMCwg QldfNDApLAorCU1DU19HUk9VUCgyLCAwLCBCV180MCksCisJTUNTX0dST1VQKDMsIDAsIEJXXzQw KSwKKwlNQ1NfR1JPVVAoNCwgMCwgQldfNDApLAorCisJTUNTX0dST1VQKDEsIDEsIEJXXzQwKSwK KwlNQ1NfR1JPVVAoMiwgMSwgQldfNDApLAorCU1DU19HUk9VUCgzLCAxLCBCV180MCksCisJTUNT X0dST1VQKDQsIDEsIEJXXzQwKSwKKworCVZIVF9HUk9VUCgxLCAwLCBCV18yMCksCisJVkhUX0dS T1VQKDIsIDAsIEJXXzIwKSwKKwlWSFRfR1JPVVAoMywgMCwgQldfMjApLAorCVZIVF9HUk9VUCg0 LCAwLCBCV18yMCksCisKKwlWSFRfR1JPVVAoMSwgMSwgQldfMjApLAorCVZIVF9HUk9VUCgyLCAx LCBCV18yMCksCisJVkhUX0dST1VQKDMsIDEsIEJXXzIwKSwKKwlWSFRfR1JPVVAoNCwgMSwgQldf MjApLAorCisJVkhUX0dST1VQKDEsIDAsIEJXXzQwKSwKKwlWSFRfR1JPVVAoMiwgMCwgQldfNDAp LAorCVZIVF9HUk9VUCgzLCAwLCBCV180MCksCisJVkhUX0dST1VQKDQsIDAsIEJXXzQwKSwKKwor CVZIVF9HUk9VUCgxLCAxLCBCV180MCksCisJVkhUX0dST1VQKDIsIDEsIEJXXzQwKSwKKwlWSFRf R1JPVVAoMywgMSwgQldfNDApLAorCVZIVF9HUk9VUCg0LCAxLCBCV180MCksCisKKwlWSFRfR1JP VVAoMSwgMCwgQldfODApLAorCVZIVF9HUk9VUCgyLCAwLCBCV184MCksCisJVkhUX0dST1VQKDMs IDAsIEJXXzgwKSwKKwlWSFRfR1JPVVAoNCwgMCwgQldfODApLAorCisJVkhUX0dST1VQKDEsIDEs IEJXXzgwKSwKKwlWSFRfR1JPVVAoMiwgMSwgQldfODApLAorCVZIVF9HUk9VUCgzLCAxLCBCV184 MCksCisJVkhUX0dST1VQKDQsIDEsIEJXXzgwKSwKK307CisKK3N0YXRpYyB1MzIKK2llZWU4MDIx MV9jYWxjX2xlZ2FjeV9yYXRlX2R1cmF0aW9uKHUxNiBiaXRyYXRlLCBib29sIHNob3J0X3ByZSwK KwkJCQkgICAgYm9vbCBjY2ssIGludCBsZW4pCit7CisJdTMyIGR1cmF0aW9uOworCisJaWYgKGNj aykgeworCQlkdXJhdGlvbiA9IDE0NCArIDQ4OyAvKiBwcmVhbWJsZSArIFBMQ1AgKi8KKwkJaWYg KHNob3J0X3ByZSkKKwkJCWR1cmF0aW9uID4+PSAxOworCisJCWR1cmF0aW9uICs9IDEwOyAvKiBT SUZTICovCisJfSBlbHNlIHsKKwkJZHVyYXRpb24gPSAyMCArIDE2OyAvKiBwcmVtYWJsZSArIFNJ RlMgKi8KKwl9CisKKwlsZW4gPDw9IDM7CisJZHVyYXRpb24gKz0gKGxlbiAqIDEwKSAvIGJpdHJh dGU7CisKKwlyZXR1cm4gZHVyYXRpb247Cit9CisKK3UzMiBpZWVlODAyMTFfY2FsY19yeF9haXJ0 aW1lKHN0cnVjdCBpZWVlODAyMTFfaHcgKmh3LAorCQkJICAgICAgc3RydWN0IGllZWU4MDIxMV9y eF9zdGF0dXMgKnN0YXR1cywKKwkJCSAgICAgIGludCBsZW4pCit7CisJc3RydWN0IGllZWU4MDIx MV9zdXBwb3J0ZWRfYmFuZCAqc2JhbmQ7CisJY29uc3Qgc3RydWN0IGllZWU4MDIxMV9yYXRlICpy YXRlOworCWJvb2wgc2dpID0gc3RhdHVzLT5lbmNfZmxhZ3MgJiBSWF9FTkNfRkxBR19TSE9SVF9H STsKKwlib29sIHNwID0gc3RhdHVzLT5lbmNfZmxhZ3MgJiBSWF9FTkNfRkxBR19TSE9SVFBSRTsK KwlpbnQgYncsIHN0cmVhbXM7CisJdTMyIGR1cmF0aW9uOworCWludCBncm91cCwgaWR4OworCWJv b2wgY2NrOworCisJc3dpdGNoIChzdGF0dXMtPmJ3KSB7CisJY2FzZSBSQVRFX0lORk9fQldfMjA6 CisJCWJ3ID0gQldfMjA7CisJCWJyZWFrOworCWNhc2UgUkFURV9JTkZPX0JXXzQwOgorCQlidyA9 IEJXXzQwOworCQlicmVhazsKKwljYXNlIFJBVEVfSU5GT19CV184MDoKKwkJYncgPSBCV184MDsK KwkJYnJlYWs7CisJZGVmYXVsdDoKKwkJV0FSTl9PTl9PTkNFKDEpOworCQlyZXR1cm4gMDsKKwl9 CisKKwlzd2l0Y2ggKHN0YXR1cy0+ZW5jb2RpbmcpIHsKKwljYXNlIFJYX0VOQ19MRUdBQ1k6CisJ CWlmIChXQVJOX09OX09OQ0Uoc3RhdHVzLT5iYW5kID4gTkw4MDIxMV9CQU5EXzVHSFopKQorCQkJ cmV0dXJuIDA7CisKKwkJc2JhbmQgPSBody0+d2lwaHktPmJhbmRzW3N0YXR1cy0+YmFuZF07CisJ CWlmICghc2JhbmQgfHwgc3RhdHVzLT5yYXRlX2lkeCA+IHNiYW5kLT5uX2JpdHJhdGVzKQorCQkJ cmV0dXJuIDA7CisKKwkJcmF0ZSA9ICZzYmFuZC0+Yml0cmF0ZXNbc3RhdHVzLT5yYXRlX2lkeF07 CisJCWNjayA9IChzdGF0dXMtPnJhdGVfaWR4IDwgQ0NLX05VTV9SQVRFUyk7CisKKwkJcmV0dXJu IGllZWU4MDIxMV9jYWxjX2xlZ2FjeV9yYXRlX2R1cmF0aW9uKHJhdGUtPmJpdHJhdGUsIHNwLAor CQkJCQkJCSAgIGNjaywgbGVuKTsKKworCWNhc2UgUlhfRU5DX1ZIVDoKKwkJc3RyZWFtcyA9IHN0 YXR1cy0+bnNzOworCQlpZHggPSBzdGF0dXMtPnJhdGVfaWR4OworCQlncm91cCA9IFZIVF9HUk9V UF9JRFgoc3RyZWFtcywgc2dpLCBidyk7CisJCWJyZWFrOworCWNhc2UgUlhfRU5DX0hUOgorCQlz dHJlYW1zID0gKChzdGF0dXMtPnJhdGVfaWR4ID4+IDMpICYgMykgKyAxOworCQlpZHggPSBzdGF0 dXMtPnJhdGVfaWR4ICYgNzsKKwkJZ3JvdXAgPSBIVF9HUk9VUF9JRFgoc3RyZWFtcywgc2dpLCBi dyk7CisJCWJyZWFrOworCWRlZmF1bHQ6CisJCVdBUk5fT05fT05DRSgxKTsKKwkJcmV0dXJuIDA7 CisJfQorCisJaWYgKFdBUk5fT05fT05DRShzdHJlYW1zID4gNCkpCisJCXJldHVybiAwOworCisJ ZHVyYXRpb24gPSBhaXJ0aW1lX21jc19ncm91cHNbZ3JvdXBdLmR1cmF0aW9uW2lkeF07CisJZHVy YXRpb24gPDw9IGFpcnRpbWVfbWNzX2dyb3Vwc1tncm91cF0uc2hpZnQ7CisJZHVyYXRpb24gKj0g bGVuOworCWR1cmF0aW9uIC89IEFWR19QS1RfU0laRTsKKwlkdXJhdGlvbiAvPSAxMDI0OworCisJ ZHVyYXRpb24gKz0gMzYgKyAoc3RyZWFtcyA8PCAyKTsKKworCXJldHVybiBkdXJhdGlvbjsKK30K K0VYUE9SVF9TWU1CT0xfR1BMKGllZWU4MDIxMV9jYWxjX3J4X2FpcnRpbWUpOworCitzdGF0aWMg dTMyIGllZWU4MDIxMV9jYWxjX3R4X2FpcnRpbWVfcmF0ZShzdHJ1Y3QgaWVlZTgwMjExX2h3ICpo dywKKwkJCQkJICBzdHJ1Y3QgaWVlZTgwMjExX3R4X3JhdGUgKnJhdGUsCisJCQkJCSAgdTggYmFu ZCwgaW50IGxlbikKK3sKKwlzdHJ1Y3QgaWVlZTgwMjExX3J4X3N0YXR1cyBzdGF0ID0geworCQku YmFuZCA9IGJhbmQsCisJfTsKKworCWlmIChyYXRlLT5pZHggPCAwIHx8ICFyYXRlLT5jb3VudCkK KwkJcmV0dXJuIDA7CisKKwlpZiAocmF0ZS0+ZmxhZ3MgJiBJRUVFODAyMTFfVFhfUkNfODBfTUha X1dJRFRIKQorCQlzdGF0LmJ3ID0gUkFURV9JTkZPX0JXXzgwOworCWVsc2UgaWYgKHJhdGUtPmZs YWdzICYgSUVFRTgwMjExX1RYX1JDXzQwX01IWl9XSURUSCkKKwkJc3RhdC5idyA9IFJBVEVfSU5G T19CV180MDsKKwllbHNlCisJCXN0YXQuYncgPSBSQVRFX0lORk9fQldfMjA7CisKKwlzdGF0LmVu Y19mbGFncyA9IDA7CisJaWYgKHJhdGUtPmZsYWdzICYgSUVFRTgwMjExX1RYX1JDX1VTRV9TSE9S VF9QUkVBTUJMRSkKKwkJc3RhdC5lbmNfZmxhZ3MgfD0gUlhfRU5DX0ZMQUdfU0hPUlRQUkU7CisJ aWYgKHJhdGUtPmZsYWdzICYgSUVFRTgwMjExX1RYX1JDX1NIT1JUX0dJKQorCQlzdGF0LmVuY19m bGFncyB8PSBSWF9FTkNfRkxBR19TSE9SVF9HSTsKKworCXN0YXQucmF0ZV9pZHggPSByYXRlLT5p ZHg7CisJaWYgKHJhdGUtPmZsYWdzICYgSUVFRTgwMjExX1RYX1JDX1ZIVF9NQ1MpIHsKKwkJc3Rh dC5lbmNvZGluZyA9IFJYX0VOQ19WSFQ7CisJCXN0YXQucmF0ZV9pZHggPSBpZWVlODAyMTFfcmF0 ZV9nZXRfdmh0X21jcyhyYXRlKTsKKwkJc3RhdC5uc3MgPSBpZWVlODAyMTFfcmF0ZV9nZXRfdmh0 X25zcyhyYXRlKTsKKwl9IGVsc2UgaWYgKHJhdGUtPmZsYWdzICYgSUVFRTgwMjExX1RYX1JDX01D UykgeworCQlzdGF0LmVuY29kaW5nID0gUlhfRU5DX0hUOworCX0gZWxzZSB7CisJCXN0YXQuZW5j b2RpbmcgPSBSWF9FTkNfTEVHQUNZOworCX0KKworCXJldHVybiBpZWVlODAyMTFfY2FsY19yeF9h aXJ0aW1lKGh3LCAmc3RhdCwgbGVuKTsKK30KKwordTMyIGllZWU4MDIxMV9jYWxjX3R4X2FpcnRp bWUoc3RydWN0IGllZWU4MDIxMV9odyAqaHcsCisJCQkgICAgICBzdHJ1Y3QgaWVlZTgwMjExX3R4 X2luZm8gKmluZm8sCisJCQkgICAgICBpbnQgbGVuKQoreworCXUzMiBkdXJhdGlvbiA9IDA7CisJ aW50IGk7CisKKwlmb3IgKGkgPSAwOyBpIDwgQVJSQVlfU0laRShpbmZvLT5zdGF0dXMucmF0ZXMp OyBpKyspIHsKKwkJc3RydWN0IGllZWU4MDIxMV90eF9yYXRlICpyYXRlID0gJmluZm8tPnN0YXR1 cy5yYXRlc1tpXTsKKwkJdTMyIGN1cl9kdXJhdGlvbjsKKworCQljdXJfZHVyYXRpb24gPSBpZWVl ODAyMTFfY2FsY190eF9haXJ0aW1lX3JhdGUoaHcsIHJhdGUsCisJCQkJCQkJICAgICAgaW5mby0+ YmFuZCwgbGVuKTsKKwkJaWYgKCFjdXJfZHVyYXRpb24pCisJCQlicmVhazsKKworCQlkdXJhdGlv biArPSBjdXJfZHVyYXRpb24gKiByYXRlLT5jb3VudDsKKwl9CisKKwlyZXR1cm4gZHVyYXRpb247 Cit9CitFWFBPUlRfU1lNQk9MX0dQTChpZWVlODAyMTFfY2FsY190eF9haXJ0aW1lKTsKKwordTMy IGllZWU4MDIxMV9jYWxjX2V4cGVjdGVkX3R4X2FpcnRpbWUoc3RydWN0IGllZWU4MDIxMV9odyAq aHcsCisJCQkJICAgICAgIHN0cnVjdCBpZWVlODAyMTFfdmlmICp2aWYsCisJCQkJICAgICAgIHN0 cnVjdCBpZWVlODAyMTFfc3RhICpwdWJzdGEsCisJCQkJICAgICAgIGludCBsZW4pCit7CisJc3Ry dWN0IGllZWU4MDIxMV9zdXBwb3J0ZWRfYmFuZCAqc2JhbmQ7CisJc3RydWN0IGllZWU4MDIxMV9j aGFuY3R4X2NvbmYgKmNvbmY7CisJaW50IHJhdGVpZHgsIHNoaWZ0ID0gMDsKKwlib29sIGNjaywg c2hvcnRfcHJlYW07CisJdTMyIGJhc2ljX3JhdGVzOworCXU4IGJhbmQgPSAwOworCXUxNiByYXRl OworCisJbGVuICs9IDM4OyAvKiBFdGhlcm5ldCBoZWFkZXIgbGVuZ3RoICovCisKKwljb25mID0g cmN1X2RlcmVmZXJlbmNlKHZpZi0+Y2hhbmN0eF9jb25mKTsKKwlpZiAoY29uZikgeworCQliYW5k ID0gY29uZi0+ZGVmLmNoYW4tPmJhbmQ7CisJCXNoaWZ0ID0gaWVlZTgwMjExX2NoYW5kZWZfZ2V0 X3NoaWZ0KCZjb25mLT5kZWYpOworCX0KKworCWlmIChwdWJzdGEpIHsKKwkJc3RydWN0IHN0YV9p bmZvICpzdGEgPSBjb250YWluZXJfb2YocHVic3RhLCBzdHJ1Y3Qgc3RhX2luZm8sCisJCQkJCQkg ICAgc3RhKTsKKworCQlyZXR1cm4gaWVlZTgwMjExX2NhbGNfdHhfYWlydGltZV9yYXRlKGh3LAor CQkJCQkJICAgICAgJnN0YS0+dHhfc3RhdHMubGFzdF9yYXRlLAorCQkJCQkJICAgICAgYmFuZCwg bGVuKTsKKwl9CisKKwlpZiAoIWNvbmYpCisJCXJldHVybiAwOworCisJLyogTm8gc3RhdGlvbiB0 byBnZXQgbGF0ZXN0IHJhdGUgZnJvbSwgc28gY2FsY3VsYXRlIHRoZSB3b3JzdC1jYXNlCisJICog ZHVyYXRpb24gdXNpbmcgdGhlIGxvd2VzdCBjb25maWd1cmVkIGJhc2ljIHJhdGUuCisJICovCisJ c2JhbmQgPSBody0+d2lwaHktPmJhbmRzW2JhbmRdOworCisJYmFzaWNfcmF0ZXMgPSB2aWYtPmJz c19jb25mLmJhc2ljX3JhdGVzOworCXNob3J0X3ByZWFtID0gdmlmLT5ic3NfY29uZi51c2Vfc2hv cnRfcHJlYW1ibGU7CisKKwlyYXRlaWR4ID0gYmFzaWNfcmF0ZXMgPyBmZnMoYmFzaWNfcmF0ZXMp IC0gMSA6IDA7CisJcmF0ZSA9IHNiYW5kLT5iaXRyYXRlc1tyYXRlaWR4XS5iaXRyYXRlIDw8IHNo aWZ0OworCWNjayA9IChyYXRlaWR4IDwgQ0NLX05VTV9SQVRFUyk7CisKKwlyZXR1cm4gaWVlZTgw MjExX2NhbGNfbGVnYWN5X3JhdGVfZHVyYXRpb24ocmF0ZSwgc2hvcnRfcHJlYW0sIGNjaywgbGVu KTsKK30KZGlmZiAtLWdpdCBhL25ldC9tYWM4MDIxMS9pZWVlODAyMTFfaS5oIGIvbmV0L21hYzgw MjExL2llZWU4MDIxMV9pLmgKaW5kZXggMDU0MDZlOWMwNWIzLi4yMjVlYTRlM2NkNzYgMTAwNjQ0 Ci0tLSBhL25ldC9tYWM4MDIxMS9pZWVlODAyMTFfaS5oCisrKyBiL25ldC9tYWM4MDIxMS9pZWVl ODAyMTFfaS5oCkBAIC0yMjQ5LDYgKzIyNDksMTAgQEAgY29uc3QgY2hhciAqaWVlZTgwMjExX2dl dF9yZWFzb25fY29kZV9zdHJpbmcodTE2IHJlYXNvbl9jb2RlKTsKIAogZXh0ZXJuIGNvbnN0IHN0 cnVjdCBldGh0b29sX29wcyBpZWVlODAyMTFfZXRodG9vbF9vcHM7CiAKK3UzMiBpZWVlODAyMTFf Y2FsY19leHBlY3RlZF90eF9haXJ0aW1lKHN0cnVjdCBpZWVlODAyMTFfaHcgKmh3LAorCQkJCSAg ICAgICBzdHJ1Y3QgaWVlZTgwMjExX3ZpZiAqdmlmLAorCQkJCSAgICAgICBzdHJ1Y3QgaWVlZTgw MjExX3N0YSAqcHVic3RhLAorCQkJCSAgICAgICBpbnQgbGVuKTsKICNpZmRlZiBDT05GSUdfTUFD ODAyMTFfTk9JTkxJTkUKICNkZWZpbmUgZGVidWdfbm9pbmxpbmUgbm9pbmxpbmUKICNlbHNlCgoK X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18KYXRoMTBrIG1h aWxpbmcgbGlzdAphdGgxMGtAbGlzdHMuaW5mcmFkZWFkLm9yZwpodHRwOi8vbGlzdHMuaW5mcmFk ZWFkLm9yZy9tYWlsbWFuL2xpc3RpbmZvL2F0aDEwawo=