From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from NAM02-SN1-obe.outbound.protection.outlook.com (mail-sn1nam02on0073.outbound.protection.outlook.com [104.47.36.73]) by dpdk.org (Postfix) with ESMTP id 9A54F1BA1D for ; Mon, 9 Apr 2018 23:01:30 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=CAVIUMNETWORKS.onmicrosoft.com; s=selector1-cavium-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version; bh=J/iO8SLX/4bHEmZgONas808BLCZ3LYg4mNrv/FKpQfc=; b=N4I2lj5mYB6LyB7rISOMDtb1ancbU6b3f4COqDfXj35djmdFusbefZl3vPkfjGjMkw6/l/iFaV7KZiHBT+BsmH7REk9JQbtHEkqJ3C3gn2QJot3O39Cu7AG3tf0cvWuj/JCst86zvxXKNRTfuI+Opzb6722+mPbPmqsWf+MY8Sc= Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=Pavan.Bhagavatula@cavium.com; Received: from localhost.localdomain (111.93.218.67) by MWHPR07MB3470.namprd07.prod.outlook.com (2603:10b6:301:63::21) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P256) id 15.20.653.12; Mon, 9 Apr 2018 21:01:27 +0000 From: Pavan Nikhilesh To: jerin.jacob@caviumnetworks.com, santosh.shukla@caviumnetworks.com, erik.g.carrillo@intel.com Cc: dev@dpdk.org, Pavan Nikhilesh Date: Tue, 10 Apr 2018 02:30:28 +0530 Message-Id: <20180409210035.23278-5-pbhagavatula@caviumnetworks.com> X-Mailer: git-send-email 2.17.0 In-Reply-To: <20180409210035.23278-1-pbhagavatula@caviumnetworks.com> References: <20180216213700.3415-1-pbhagavatula@caviumnetworks.com> <20180409210035.23278-1-pbhagavatula@caviumnetworks.com> MIME-Version: 1.0 Content-Type: text/plain X-Originating-IP: [111.93.218.67] X-ClientProxiedBy: BM1PR01CA0088.INDPRD01.PROD.OUTLOOK.COM (2603:1096:b00:1::28) To MWHPR07MB3470.namprd07.prod.outlook.com (2603:10b6:301:63::21) X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 79d230cf-1204-4ff4-fe64-08d59e5d10bf X-Microsoft-Antispam: UriScan:; BCL:0; PCL:0; RULEID:(7020095)(4652020)(5600026)(4604075)(2017052603328)(7153060)(7193020); SRVR:MWHPR07MB3470; X-Microsoft-Exchange-Diagnostics: 1; MWHPR07MB3470; 3:bdxB/0eTBeDGvV5O9i8NnV1pYWf9grEIyRcCa/w0or7GHi2RsVcI8mHYqYo6e52JPMtPNyc9D8wq6wfEVzIFDMOgIpRzuI+qc+zO4pGs2Gcjj72o95itAbw7t5+JOMoYS2LpYBPZkS49i/2swChMygRs/GJ2yeGDBFaGGv5oIpv+timljkgOkXfH/fO1YzyzJnX0QoMZMKMl/xt0P28tCgbF343XDXy5pGA1cQDyy9e68qJyd34iM7eJ8g7632IF; 25:b/14YuZzYKPufRv4gogN1fD00c+PIVPKJsBhLQ/fxx3BJ5FhSjlWkfEiWTKzs6D9aZLLCJZhYNoSjMdEWFiWwxMH7y3+iRnwIpmIq8FVgxEPaWTNkJX1TIp1uPqTmnPZzBeOr248BndgSmhAfZupuh87FFX6Mp5L/vlH5cbjSexPUin1eAK2nCSg7VqQm2d4crfrWi5j83Eiyb1pvnSmWrPxLqrK/RCOv1UtJInsLHNxuTdze9hk7sR5IzsFETYWpmX1ol+LsMkUnTlSJf68hPZZsF1n/VW3lnsW71r6xyRmWmLaDvXBa00zABSGUH8lbxmHuXtyL5y3g2iBXuMitQ==; 31:s60JJiiT/W5VlAjrazwjOIZlRyUZ6gQ8BWgHS4koP6UY8XXg+UyPBmofDvPc5xEuThuObznX72E1mRuaxSasnLRA8hy+oBUKtbghoqdKDtXfmlIL3J5y+m7F9l4Dpa5wY9pIQpVt2iA3nxgjm03Gm+Lu7szisKsH4DGpM8+sZsRNGAxeP7lRKC2JDrfzzXUO3yTao2JuHETQGMNTDLP40+3pHrpxRbYXwuBGuYt4tPU= X-MS-TrafficTypeDiagnostic: MWHPR07MB3470: X-Microsoft-Exchange-Diagnostics: 1; MWHPR07MB3470; 20:uM1rUtjUv4n/PRG0XOw0a0pAOfXoRAdOIQQ6iXZ+YSgdN3b8/Vq0z07LzoVMNL5dSXKIl9Qcqw3LHZyvVycWN0elhLyAHBbSk6dV47zyfo+34QX2mWnZVy7wTcIUu9RXkO68A+0hMnOdMoyN3TerA0LPkqfLlG6D4sgs67DMlBPONuYowiUYd657GlaKMV+juC+RcMlQBN6zMdkX0pMHtl7XV73Sbn88/uHRFCOVo5XfrKyjdcd9CAaYpWzJYAmKhr/6Nsh4ome88/xrWT9/fSE88XVgV0PIH+jcBMGsi794y0qibAeOt2ww6/u9Jvax8t395cYI49CF13dBt99rZO3BWKdRcLJAxpJ1sbP9sLd87A0gqdufd1qOtxlSqL6AYgBVJdTRy3ACylxkFFhzaxY04R75RSaVpBQPri5+vnK9ikF4jksSjvpTbMhUzV82wBFjtYWRrHQPO3rou8kYN9hs3Cvf08wLNXI9/XRF24HZYiIWg2aHvBNW9hGlCSsyDJs3OAU9yR+v/DBtH5WIbUCFO79ETgOlr4Lj74hdKYc7Y4JM0XCsmJ8hCGqNH7xSF2iMIHrPYXIiKToxx9a2wfJJqdK4Y7XY5bdtcgovUo0=; 4:qYl4y1XkHfDF+V8rmHQoY4OHdwIWrEEd3ESVFZr0f3dvzK3B0bnz5aQVspqHEzWrvssbaY4AwPy9biCNiLAT3oPLZnx0tGXOXpgF1Q0bTiXOdaryRLpaIXJmwBKcU0VzPLLXoXQYpVnsmp4y/LpWRag8F3PWjTkiRyMaLAnePvGxRb1+oUtnASK3RhkDAewTXPYusVlbflp9FueVZTbTrl1Lg1d2fN9tsiPjwi0jQm/9zYeyYk+yiylWIkOujECn3vLu3I5UOlGHpWjs9QovAA== X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(8211001083)(6040522)(2401047)(5005006)(8121501046)(3231221)(944501327)(52105095)(3002001)(10201501046)(93006095)(6041310)(20161123558120)(20161123562045)(20161123564045)(201703131423095)(201702281528075)(20161123555045)(201703061421075)(201703061406153)(20161123560045)(6072148)(201708071742011); SRVR:MWHPR07MB3470; BCL:0; PCL:0; RULEID:; SRVR:MWHPR07MB3470; X-Forefront-PRVS: 0637FCE711 X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10009020)(6069001)(366004)(376002)(346002)(39860400002)(396003)(39380400002)(189003)(199004)(107886003)(48376002)(446003)(1857600001)(50466002)(97736004)(575784001)(66066001)(2906002)(53936002)(6512007)(42882007)(59450400001)(81166006)(956004)(81156014)(25786009)(16526019)(6116002)(2616005)(476003)(5660300001)(6486002)(3846002)(11346002)(8936002)(26005)(50226002)(8676002)(486006)(47776003)(1076002)(478600001)(105586002)(76176011)(68736007)(72206003)(7736002)(106356001)(5009440100003)(305945005)(4326008)(6506007)(316002)(16586007)(386003)(51416003)(52116002)(36756003)(42262002); DIR:OUT; SFP:1101; SCL:1; SRVR:MWHPR07MB3470; H:localhost.localdomain; FPR:; SPF:None; LANG:en; PTR:InfoNoRecords; MX:1; A:1; Received-SPF: None (protection.outlook.com: cavium.com does not designate permitted sender hosts) X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; MWHPR07MB3470; 23:hQCGXn5vNl3sajQdUcsKvSa+y4pIs2jk1R9vlnbfH?= =?us-ascii?Q?hxZGsmrbhqlnrjpE6oPP5fShUodqEEy3azjmMVJwhMWYWLxqYStaaP5Fcd3B?= =?us-ascii?Q?/BRBcRMU/X0dS3l1to/8lHJhabxBfShFGRpGRasJIO80HmeITDVk7y6dGXBa?= =?us-ascii?Q?Lt28m8aOAdYMkZxOhyNzJ2r5YJFF/90hEcamolzL+9vzP8SSRQk9UhpGqpDF?= =?us-ascii?Q?PHbpX2xFrNs1EY0oOreTq/VToKmsgkt8lojqnRaxC7ydJ/gE0Fe9SjNHOCAk?= =?us-ascii?Q?T2iHvZzjI194FCL/AaWVUSuahRsAEW2FRBqTf3mLR3Sn3ETv40hzUjMcbWCh?= =?us-ascii?Q?NubstH2TOGNlcT5mELEKnWkYzH9mciELQpxauz7DqSpxcycs0I/RcM2GMB9+?= =?us-ascii?Q?+SPgWBJbnT0uQA740RylNMpVAUWsCnhTjRJA6NasyRQhc0OhMqLzWWtNosD0?= =?us-ascii?Q?umP72mSYufuf+9EX7OlCKH6l/zAf4v/e780jrAE322qsIoXAA4w9f86sy37A?= =?us-ascii?Q?Zxj6aCgVU5Rha4Mkiztt1WbKhUuToeACp+tOyg2MH8J5FLseU6e9IGjI5zUP?= =?us-ascii?Q?UGVVdoJVoxZgdV9a2cbURIbz03xSo/YSHUcv6sSaRIBjLWkRYNt5Ilf0tBPD?= =?us-ascii?Q?/sm6uw2OsPYL9/VuBUZMZ61ZwvW59s90efutn4Isa943DsDDKNXsP0RKXj7F?= =?us-ascii?Q?sbX2I2aesP6JIc5mLACv+4gO4pEd+oLPdJlD3VAw77i4/pmQq1kA4jDOlW0D?= =?us-ascii?Q?TsJqfTfc4q+zkNsTrmreFA4qrxEUSESodXjmrCaYRKO9/M3iewRDGFGb86L/?= =?us-ascii?Q?N7yGT9z7b6x5irqsOWWwmdhD4CGVn8eGXws0F/fgjnJ8kuRRtSvKICOIHJyo?= =?us-ascii?Q?9ZOc+LnnT0ldxFxiKeb0ZzhWcjFM4wENl1d5Eis1XgufLn24OL5CkK0oONl9?= =?us-ascii?Q?Pfx40OcPhW/jca5gPP8mUN8RQUloBT3NwDT4mmG3IdCC+Cc80vezq2Ois7Hi?= =?us-ascii?Q?1eS2DUbkgB8z62MAZpgFRBAzGQ0tXcGljWEQdJMgMM5WSOxpuIqMTNAQMX6s?= =?us-ascii?Q?vWpoOCKmEiwnbC/5ZWw8d8XX53hkdllTo8ShBrgqRx8B+3myLfBtzo0VgAKc?= =?us-ascii?Q?TfbgGHkqAcaZSKs5k/4Ni70xs50itLSktl0twhQskRNiiC7rZQig0mW3yFhx?= =?us-ascii?Q?TgaKNXx1UD20tE4B7hz1P8SPVx/yzROxxElrC70byq1vTmWCaCByPeRAOTmH?= =?us-ascii?Q?Q1y8ZPa+yuP2gz1a8qX59IAoAf63qVcPDkr9nEqOB6RthWCEcec6iXepJeo0?= =?us-ascii?Q?hkujewCtcUEmhWTt5VV6jOJUZdh++taGE9cLjjFKG+u?= X-Microsoft-Antispam-Message-Info: WGeET2nb/sU9LzbXyLcxsq7qbc6yeCyUhzhSNID8fsTF+tBvTCVd5R61KrixLpdGaeQwax96ONCe6iSRfW/4WT6CH8ysCYus60M5tQjoLE2RC6gesQicVj8rNIUky3GxbiFLPmjXN18H/gHc1o9Zk1htr6FvFLzXpfWgIpfsVhy/P1AWAvrt4jMXaNDkNwFc X-Microsoft-Exchange-Diagnostics: 1; MWHPR07MB3470; 6:O8i69Wi9QqlCMOQ4f5PyaaS5h4giL+sCgy5WGf1yWRUI/2qIDlFQiCwaZCYhQl9hqXTUtEkgS0/BlqzL2DTXADgthvbQ5Jarqip8Y4rq8lu5f9+Iy8+QKusk8MatwHmcSR+y4sZwwH/f9Z1Caz9P7jY5SJVASen0GlfbjYbzdwyQZvOh0fWsFLOq69qCrYV3FpSyWt+1EF1LebqHyVOulPvpiX+v6M1i0uIEWiI4CKIdRPnzsofeLijBziqtQg6gmHdqCLXiFWhep7FoBlrbSWEVl2n//mfu/+SlfVEOO/Y+Dfp92hf8gPQHPncSetnrGrRoGp6Tj3xYirzxUJexf2dwKhb5jMftNdU7ojfdSV9Q2LQkLfHWWVrLjD6QJ1vbE2Hcy/0Rg2bSFcG0A02c+jnJz4qGtst9sColvrJsJLLDtG/9Wn5ThW7IRa23nOpyrsOSQ3HoUEadQeGCoGhuLA==; 5:u2flZ9n4RO/gCGqRH1q7Yz2ecroiTjJdatRlokCv1vfVzEe8j/onLRlF02eSRLRw+XIMfD1Tw0RIuOVY6ycWv9cUElLMHfN3ptridr4xHXY6z2AAriffwulx8D0lb0GkRHXxf/CtzPzTYcrgeaoihvvDVNr9ggF0A5MhJ0bWnuw=; 24:ru543x1TbR6IYyuq5n33Ay4pEmea3wSEpvfvTBVKchggQ0X6VkWnmBt89Iq2K6IhVvmLzhbE36D/Lm4ZX4jtRVtZoB4dY2omNP1bYOHEq/Y= SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-Microsoft-Exchange-Diagnostics: 1; MWHPR07MB3470; 7:swmNEBAGmj05kIa0tAAjylAgrciG17PyV/Hl1xR0RD0RskfnIaBiM88ksPRGNFQh6965kAnkjNqIbPBtNV4FII9aOZeXemRps4nygThtZwGIsrk6ojTgYcFianNHJZFLME+Q7oggvHn+OeDXYrm6/06Y4DYIowWx1xonsTFKWTap+pLYuW19xkBwe8onvuHelHzxM7WSp+ByjHQav7DhGB+I8FfAJdrm0umfRtFMsac6J/zi1cnZz9nn+HZm7kpY X-OriginatorOrg: caviumnetworks.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 09 Apr 2018 21:01:27.2021 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 79d230cf-1204-4ff4-fe64-08d59e5d10bf X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 711e4ccf-2e9b-4bcf-a551-4094005b6194 X-MS-Exchange-Transport-CrossTenantHeadersStamped: MWHPR07MB3470 Subject: [dpdk-dev] [PATCH v4 04/11] event/octeontx: add support to start and stop timer device X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 09 Apr 2018 21:01:31 -0000 When application requests to start the timer adapter through `rte_event_timer_adapter_start`, Octeontx TIMvf ring does the following: - Uses mbox to communicate TIMpf driver about, * SCLK frequency used to convert ns<->cycles. * program the ring control parameters and start the ring. * get the exact cycle at which the TIMvf ring has started which can be used to estimate the bucket position. On `rte_event_timer_adapter_stop` i.e stop, Octeontx TIMvf ring does the following: - Use mbox to communicate TIMpf driver about, * reset the ring control parameters and stop the ring. Signed-off-by: Pavan Nikhilesh --- drivers/event/octeontx/timvf_evdev.c | 144 +++++++++++++++++++++++++++ drivers/event/octeontx/timvf_evdev.h | 5 + 2 files changed, 149 insertions(+) diff --git a/drivers/event/octeontx/timvf_evdev.c b/drivers/event/octeontx/timvf_evdev.c index 5728d4944..84c6a511e 100644 --- a/drivers/event/octeontx/timvf_evdev.c +++ b/drivers/event/octeontx/timvf_evdev.c @@ -16,6 +16,32 @@ otx_timvf_init_log(void) rte_log_set_level(otx_logtype_timvf, RTE_LOG_NOTICE); } +struct __rte_packed timvf_mbox_dev_info { + uint64_t ring_active[4]; + uint64_t clk_freq; +}; + +/* Response messages */ +enum { + MBOX_RET_SUCCESS, + MBOX_RET_INVALID, + MBOX_RET_INTERNAL_ERR, +}; + +static int +timvf_mbox_dev_info_get(struct timvf_mbox_dev_info *info) +{ + struct octeontx_mbox_hdr hdr = {0}; + uint16_t len = sizeof(struct timvf_mbox_dev_info); + + hdr.coproc = TIM_COPROC; + hdr.msg = TIM_GET_DEV_INFO; + hdr.vfid = 0; /* TIM DEV is always 0. TIM RING ID changes. */ + + memset(info, 0, len); + return octeontx_mbox_send(&hdr, NULL, 0, info, len); +} + static void timvf_ring_info_get(const struct rte_event_timer_adapter *adptr, struct rte_event_timer_adapter_info *adptr_info) @@ -27,6 +53,122 @@ timvf_ring_info_get(const struct rte_event_timer_adapter *adptr, sizeof(struct rte_event_timer_adapter_conf)); } +static int +timvf_ring_conf_set(struct timvf_ctrl_reg *rctl, uint8_t ring_id) +{ + struct octeontx_mbox_hdr hdr = {0}; + uint16_t len = sizeof(struct timvf_ctrl_reg); + int ret; + + hdr.coproc = TIM_COPROC; + hdr.msg = TIM_SET_RING_INFO; + hdr.vfid = ring_id; + + ret = octeontx_mbox_send(&hdr, rctl, len, NULL, 0); + if (ret < 0 || hdr.res_code != MBOX_RET_SUCCESS) + return -EACCES; + return 0; +} + +static int +timvf_get_start_cyc(uint64_t *now, uint8_t ring_id) +{ + struct octeontx_mbox_hdr hdr = {0}; + + hdr.coproc = TIM_COPROC; + hdr.msg = TIM_RING_START_CYC_GET; + hdr.vfid = ring_id; + *now = 0; + return octeontx_mbox_send(&hdr, NULL, 0, now, sizeof(uint64_t)); +} + +static int +timvf_ring_start(const struct rte_event_timer_adapter *adptr) +{ + int ret; + uint64_t interval; + struct timvf_ctrl_reg rctrl; + struct timvf_mbox_dev_info dinfo; + struct timvf_ring *timr = adptr->data->adapter_priv; + + ret = timvf_mbox_dev_info_get(&dinfo); + if (ret < 0 || ret != sizeof(struct timvf_mbox_dev_info)) + return -EINVAL; + + /* Calculate the interval cycles according to clock source. */ + switch (timr->clk_src) { + case TIM_CLK_SRC_SCLK: + interval = NSEC2CLK(timr->tck_nsec, dinfo.clk_freq); + break; + case TIM_CLK_SRC_GPIO: + /* GPIO doesn't work on tck_nsec. */ + interval = 0; + break; + case TIM_CLK_SRC_GTI: + interval = NSEC2CLK(timr->tck_nsec, dinfo.clk_freq); + break; + case TIM_CLK_SRC_PTP: + interval = NSEC2CLK(timr->tck_nsec, dinfo.clk_freq); + break; + default: + timvf_log_err("Unsupported clock source configured %d", + timr->clk_src); + return -EINVAL; + } + + /*CTRL0 register.*/ + rctrl.rctrl0 = interval; + + /*CTRL1 register.*/ + rctrl.rctrl1 = (uint64_t)(timr->clk_src) << 51 | + 1ull << 48 /* LOCK_EN (Enable hw bucket lock mechanism) */ | + 1ull << 47 /* ENA */ | + 1ull << 44 /* ENA_LDWB */ | + (timr->nb_bkts - 1); + + rctrl.rctrl2 = (uint64_t)(TIM_CHUNK_SIZE / 16) << 40; + + timvf_write64((uintptr_t)timr->bkt, + (uint8_t *)timr->vbar0 + TIM_VRING_BASE); + if (timvf_ring_conf_set(&rctrl, timr->tim_ring_id)) { + ret = -EACCES; + goto error; + } + + if (timvf_get_start_cyc(&timr->ring_start_cyc, + timr->tim_ring_id) < 0) { + ret = -EACCES; + goto error; + } + timr->tck_int = NSEC2CLK(timr->tck_nsec, rte_get_timer_hz()); + timr->fast_div = rte_reciprocal_value_u64(timr->tck_int); + timvf_log_info("nb_bkts %d min_ns %"PRIu64" min_cyc %"PRIu64"" + " maxtmo %"PRIu64"\n", + timr->nb_bkts, timr->tck_nsec, interval, + timr->max_tout); + + return 0; +error: + rte_free(timr->bkt); + rte_mempool_free(timr->chunk_pool); + return ret; +} + +static int +timvf_ring_stop(const struct rte_event_timer_adapter *adptr) +{ + struct timvf_ring *timr = adptr->data->adapter_priv; + struct timvf_ctrl_reg rctrl = {0}; + rctrl.rctrl0 = timvf_read64((uint8_t *)timr->vbar0 + TIM_VRING_CTL0); + rctrl.rctrl1 = timvf_read64((uint8_t *)timr->vbar0 + TIM_VRING_CTL1); + rctrl.rctrl1 &= ~(1ull << 47); /* Disable */ + rctrl.rctrl2 = timvf_read64((uint8_t *)timr->vbar0 + TIM_VRING_CTL2); + + if (timvf_ring_conf_set(&rctrl, timr->tim_ring_id)) + return -EACCES; + return 0; +} + static int timvf_ring_create(struct rte_event_timer_adapter *adptr) { @@ -129,6 +271,8 @@ timvf_ring_free(struct rte_event_timer_adapter *adptr) static struct rte_event_timer_adapter_ops timvf_ops = { .init = timvf_ring_create, .uninit = timvf_ring_free, + .start = timvf_ring_start, + .stop = timvf_ring_stop, .get_info = timvf_ring_info_get, }; diff --git a/drivers/event/octeontx/timvf_evdev.h b/drivers/event/octeontx/timvf_evdev.h index ef8ec0ced..7663dc375 100644 --- a/drivers/event/octeontx/timvf_evdev.h +++ b/drivers/event/octeontx/timvf_evdev.h @@ -75,6 +75,11 @@ #define TIM_VRING_AURA (0x108) #define TIM_VRING_REL (0x110) + +#define NSEC_PER_SEC 1E9 +#define NSEC2CLK(__ns, __freq) (((__ns) * (__freq)) / NSEC_PER_SEC) +#define CLK2NSEC(__clk, __freq) (((__clk) * NSEC_PER_SEC) / (__freq)) + #define timvf_read64 rte_read64_relaxed #define timvf_write64 rte_write64_relaxed -- 2.17.0