2019-09-11 02:46:15

by Huazhong Tan

[permalink] [raw]
Subject: [PATCH V2 net-next 0/7] net: hns3: add a feature & bugfixes & cleanups

This patch-set includes a VF feature, bugfixes and cleanups for the HNS3
ethernet controller driver.

[patch 01/07] adds ethtool_ops.set_channels support for HNS3 VF driver

[patch 02/07] adds a recovery for setting channel fail.

[patch 03/07] fixes an error related to shaper parameter algorithm.

[patch 04/07] fixes an error related to ksetting.

[patch 05/07] adds cleanups for some log pinting.

[patch 06/07] adds a NULL pointer check before function calling.

[patch 07/07] adds some debugging information for reset issue.

Change log:
V1->V2: fixes comment from David Miller.

Guangbin Huang (4):
net: hns3: add ethtool_ops.set_channels support for HNS3 VF driver
net: hns3: fix port setting handle for fibre port
net: hns3: modify some logs format
net: hns3: check NULL pointer before use

Huazhong Tan (1):
net: hns3: add some DFX info for reset issue

Peng Li (1):
net: hns3: revert to old channel when setting new channel num fail

Yonglong Liu (1):
net: hns3: fix shaper parameter algorithm

drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c | 7 +-
drivers/net/ethernet/hisilicon/hns3/hns3_enet.c | 54 ++++++++++----
drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c | 16 +++++
.../net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c | 2 +-
.../ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c | 32 ++++++---
.../ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 13 ++--
.../ethernet/hisilicon/hns3/hns3pf/hclge_main.h | 2 +-
.../net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c | 11 ++-
.../ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c | 83 ++++++++++++++++++++--
9 files changed, 174 insertions(+), 46 deletions(-)

--
2.7.4


2019-09-11 02:46:29

by Huazhong Tan

[permalink] [raw]
Subject: [PATCH V2 net-next 1/7] net: hns3: add ethtool_ops.set_channels support for HNS3 VF driver

From: Guangbin Huang <[email protected]>

This patch adds ethtool_ops.set_channels support for HNS3 VF driver,
and updates related TQP information and RSS information, to support
modification of VF TQP number, and uses current rss_size instead of
max_rss_size to initialize RSS.

Also, fixes a format error in hclgevf_get_rss().

Signed-off-by: Guangbin Huang <[email protected]>
Signed-off-by: Huazhong Tan <[email protected]>
---
drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c | 1 +
.../ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c | 83 ++++++++++++++++++++--
2 files changed, 79 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
index aa692b1..f5a681d 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
@@ -1397,6 +1397,7 @@ static const struct ethtool_ops hns3vf_ethtool_ops = {
.set_rxfh = hns3_set_rss,
.get_link_ksettings = hns3_get_link_ksettings,
.get_channels = hns3_get_channels,
+ .set_channels = hns3_set_channels,
.get_coalesce = hns3_get_coalesce,
.set_coalesce = hns3_set_coalesce,
.get_regs_len = hns3_get_regs_len,
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
index 594cae8..e3090b3 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
@@ -743,7 +743,7 @@ static int hclgevf_get_rss(struct hnae3_handle *handle, u32 *indir, u8 *key,
}

static int hclgevf_set_rss(struct hnae3_handle *handle, const u32 *indir,
- const u8 *key, const u8 hfunc)
+ const u8 *key, const u8 hfunc)
{
struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
struct hclgevf_rss_cfg *rss_cfg = &hdev->rss_cfg;
@@ -2060,9 +2060,10 @@ static int hclgevf_config_gro(struct hclgevf_dev *hdev, bool en)
static int hclgevf_rss_init_hw(struct hclgevf_dev *hdev)
{
struct hclgevf_rss_cfg *rss_cfg = &hdev->rss_cfg;
- int i, ret;
+ int ret;
+ u32 i;

- rss_cfg->rss_size = hdev->rss_size_max;
+ rss_cfg->rss_size = hdev->nic.kinfo.rss_size;

if (hdev->pdev->revision >= 0x21) {
rss_cfg->hash_algo = HCLGEVF_RSS_HASH_ALGO_SIMPLE;
@@ -2099,13 +2100,13 @@ static int hclgevf_rss_init_hw(struct hclgevf_dev *hdev)

/* Initialize RSS indirect table */
for (i = 0; i < HCLGEVF_RSS_IND_TBL_SIZE; i++)
- rss_cfg->rss_indirection_tbl[i] = i % hdev->rss_size_max;
+ rss_cfg->rss_indirection_tbl[i] = i % rss_cfg->rss_size;

ret = hclgevf_set_rss_indir_table(hdev);
if (ret)
return ret;

- return hclgevf_set_rss_tc_mode(hdev, hdev->rss_size_max);
+ return hclgevf_set_rss_tc_mode(hdev, rss_cfg->rss_size);
}

static int hclgevf_init_vlan_config(struct hclgevf_dev *hdev)
@@ -2835,6 +2836,77 @@ static void hclgevf_get_tqps_and_rss_info(struct hnae3_handle *handle,
*max_rss_size = hdev->rss_size_max;
}

+static void hclgevf_update_rss_size(struct hnae3_handle *handle,
+ u32 new_tqps_num)
+{
+ struct hnae3_knic_private_info *kinfo = &handle->kinfo;
+ struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+ u16 max_rss_size;
+
+ kinfo->req_rss_size = new_tqps_num;
+
+ max_rss_size = min_t(u16, hdev->rss_size_max,
+ hdev->num_tqps / kinfo->num_tc);
+
+ /* Use the user's configuration when it is not larger than
+ * max_rss_size, otherwise, use the maximum specification value.
+ */
+ if (kinfo->req_rss_size != kinfo->rss_size && kinfo->req_rss_size &&
+ kinfo->req_rss_size <= max_rss_size)
+ kinfo->rss_size = kinfo->req_rss_size;
+ else if (kinfo->rss_size > max_rss_size ||
+ (!kinfo->req_rss_size && kinfo->rss_size < max_rss_size))
+ kinfo->rss_size = max_rss_size;
+
+ kinfo->num_tqps = kinfo->num_tc * kinfo->rss_size;
+}
+
+static int hclgevf_set_channels(struct hnae3_handle *handle, u32 new_tqps_num,
+ bool rxfh_configured)
+{
+ struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+ struct hnae3_knic_private_info *kinfo = &handle->kinfo;
+ u16 cur_rss_size = kinfo->rss_size;
+ u16 cur_tqps = kinfo->num_tqps;
+ u32 *rss_indir;
+ unsigned int i;
+ int ret;
+
+ hclgevf_update_rss_size(handle, new_tqps_num);
+
+ ret = hclgevf_set_rss_tc_mode(hdev, kinfo->rss_size);
+ if (ret)
+ return ret;
+
+ /* RSS indirection table has been configuared by user */
+ if (rxfh_configured)
+ goto out;
+
+ /* Reinitializes the rss indirect table according to the new RSS size */
+ rss_indir = kcalloc(HCLGEVF_RSS_IND_TBL_SIZE, sizeof(u32), GFP_KERNEL);
+ if (!rss_indir)
+ return -ENOMEM;
+
+ for (i = 0; i < HCLGEVF_RSS_IND_TBL_SIZE; i++)
+ rss_indir[i] = i % kinfo->rss_size;
+
+ ret = hclgevf_set_rss(handle, rss_indir, NULL, 0);
+ if (ret)
+ dev_err(&hdev->pdev->dev, "set rss indir table fail, ret=%d\n",
+ ret);
+
+ kfree(rss_indir);
+
+out:
+ if (!ret)
+ dev_info(&hdev->pdev->dev,
+ "Channels changed, rss_size from %u to %u, tqps from %u to %u",
+ cur_rss_size, kinfo->rss_size,
+ cur_tqps, kinfo->rss_size * kinfo->num_tc);
+
+ return ret;
+}
+
static int hclgevf_get_status(struct hnae3_handle *handle)
{
struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
@@ -3042,6 +3114,7 @@ static const struct hnae3_ae_ops hclgevf_ops = {
.enable_hw_strip_rxvtag = hclgevf_en_hw_strip_rxvtag,
.reset_event = hclgevf_reset_event,
.set_default_reset_request = hclgevf_set_def_reset_request,
+ .set_channels = hclgevf_set_channels,
.get_channels = hclgevf_get_channels,
.get_tqps_and_rss_info = hclgevf_get_tqps_and_rss_info,
.get_regs_len = hclgevf_get_regs_len,
--
2.7.4

2019-09-11 02:47:14

by Huazhong Tan

[permalink] [raw]
Subject: [PATCH V2 net-next 3/7] net: hns3: fix shaper parameter algorithm

From: Yonglong Liu <[email protected]>

Currently when hns3 driver configures the tm shaper to limit
bandwidth below 20Mbit using the parameters calculated by
hclge_shaper_para_calc(), the actual bandwidth limited by tm
hardware module is not accurate enough, for example, 1.28 Mbit
when the user is configuring 1 Mbit.

This patch adjusts the ir_calc to be closer to ir, and
always calculate the ir_b parameter when user is configuring
a small bandwidth. Also, removes an unnecessary parenthesis
when calculating denominator.

Fixes: 848440544b41 ("net: hns3: Add support of TX Scheduler & Shaper to HNS3 driver")
Signed-off-by: Yonglong Liu <[email protected]>
Signed-off-by: Huazhong Tan <[email protected]>
---
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c
index e829101..9f0e35f 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c
@@ -81,16 +81,13 @@ static int hclge_shaper_para_calc(u32 ir, u8 shaper_level,
return 0;
} else if (ir_calc > ir) {
/* Increasing the denominator to select ir_s value */
- while (ir_calc > ir) {
+ while (ir_calc >= ir && ir) {
ir_s_calc++;
ir_calc = DIVISOR_IR_B_126 / (tick * (1 << ir_s_calc));
}

- if (ir_calc == ir)
- *ir_b = 126;
- else
- *ir_b = (ir * tick * (1 << ir_s_calc) +
- (DIVISOR_CLK >> 1)) / DIVISOR_CLK;
+ *ir_b = (ir * tick * (1 << ir_s_calc) + (DIVISOR_CLK >> 1)) /
+ DIVISOR_CLK;
} else {
/* Increasing the numerator to select ir_u value */
u32 numerator;
@@ -104,7 +101,7 @@ static int hclge_shaper_para_calc(u32 ir, u8 shaper_level,
if (ir_calc == ir) {
*ir_b = 126;
} else {
- u32 denominator = (DIVISOR_CLK * (1 << --ir_u_calc));
+ u32 denominator = DIVISOR_CLK * (1 << --ir_u_calc);
*ir_b = (ir * tick + (denominator >> 1)) / denominator;
}
}
--
2.7.4

2019-09-11 02:47:23

by Huazhong Tan

[permalink] [raw]
Subject: [PATCH V2 net-next 6/7] net: hns3: check NULL pointer before use

From: Guangbin Huang <[email protected]>

This patch checks ops->set_default_reset_request whether is NULL
before using it in function hns3_slot_reset.

Signed-off-by: Guangbin Huang <[email protected]>
Signed-off-by: Huazhong Tan <[email protected]>
---
drivers/net/ethernet/hisilicon/hns3/hns3_enet.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
index 8dbaf36..616cad0 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
@@ -2006,7 +2006,8 @@ static pci_ers_result_t hns3_slot_reset(struct pci_dev *pdev)

ops = ae_dev->ops;
/* request the reset */
- if (ops->reset_event && ops->get_reset_level) {
+ if (ops->reset_event && ops->get_reset_level &&
+ ops->set_default_reset_request) {
if (ae_dev->hw_err_reset_req) {
reset_type = ops->get_reset_level(ae_dev,
&ae_dev->hw_err_reset_req);
--
2.7.4

2019-09-11 08:11:20

by David Miller

[permalink] [raw]
Subject: Re: [PATCH V2 net-next 0/7] net: hns3: add a feature & bugfixes & cleanups

From: Huazhong Tan <[email protected]>
Date: Wed, 11 Sep 2019 10:40:32 +0800

> This patch-set includes a VF feature, bugfixes and cleanups for the HNS3
> ethernet controller driver.

Series applied.

2019-09-12 06:34:47

by Michal Kubecek

[permalink] [raw]
Subject: Re: [PATCH V2 net-next 1/7] net: hns3: add ethtool_ops.set_channels support for HNS3 VF driver

On Wed, Sep 11, 2019 at 10:40:33AM +0800, Huazhong Tan wrote:
> From: Guangbin Huang <[email protected]>
>
> This patch adds ethtool_ops.set_channels support for HNS3 VF driver,
> and updates related TQP information and RSS information, to support
> modification of VF TQP number, and uses current rss_size instead of
> max_rss_size to initialize RSS.
>
> Also, fixes a format error in hclgevf_get_rss().
>
> Signed-off-by: Guangbin Huang <[email protected]>
> Signed-off-by: Huazhong Tan <[email protected]>
> ---
> drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c | 1 +
> .../ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c | 83 ++++++++++++++++++++--
> 2 files changed, 79 insertions(+), 5 deletions(-)
>
...
> diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
> index 594cae8..e3090b3 100644
> --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
> +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
...
> +static void hclgevf_update_rss_size(struct hnae3_handle *handle,
> + u32 new_tqps_num)
> +{
> + struct hnae3_knic_private_info *kinfo = &handle->kinfo;
> + struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
> + u16 max_rss_size;
> +
> + kinfo->req_rss_size = new_tqps_num;
> +
> + max_rss_size = min_t(u16, hdev->rss_size_max,
> + hdev->num_tqps / kinfo->num_tc);
> +
> + /* Use the user's configuration when it is not larger than
> + * max_rss_size, otherwise, use the maximum specification value.
> + */
> + if (kinfo->req_rss_size != kinfo->rss_size && kinfo->req_rss_size &&
> + kinfo->req_rss_size <= max_rss_size)
> + kinfo->rss_size = kinfo->req_rss_size;
> + else if (kinfo->rss_size > max_rss_size ||
> + (!kinfo->req_rss_size && kinfo->rss_size < max_rss_size))
> + kinfo->rss_size = max_rss_size;

I don't think requested channel count can be larger than max_rss_size
here. In ethtool_set_channels(), we check that requested channel counts
do not exceed maximum channel counts as reported by ->get_channels().
And hclgevf_get_max_channels() cannot return more than max_rss_size.

> +
> + kinfo->num_tqps = kinfo->num_tc * kinfo->rss_size;
> +}
> +
> +static int hclgevf_set_channels(struct hnae3_handle *handle, u32 new_tqps_num,
> + bool rxfh_configured)
> +{
> + struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
> + struct hnae3_knic_private_info *kinfo = &handle->kinfo;
> + u16 cur_rss_size = kinfo->rss_size;
> + u16 cur_tqps = kinfo->num_tqps;
> + u32 *rss_indir;
> + unsigned int i;
> + int ret;
> +
> + hclgevf_update_rss_size(handle, new_tqps_num);
> +
> + ret = hclgevf_set_rss_tc_mode(hdev, kinfo->rss_size);
> + if (ret)
> + return ret;
> +
> + /* RSS indirection table has been configuared by user */
> + if (rxfh_configured)
> + goto out;
> +
> + /* Reinitializes the rss indirect table according to the new RSS size */
> + rss_indir = kcalloc(HCLGEVF_RSS_IND_TBL_SIZE, sizeof(u32), GFP_KERNEL);
> + if (!rss_indir)
> + return -ENOMEM;
> +
> + for (i = 0; i < HCLGEVF_RSS_IND_TBL_SIZE; i++)
> + rss_indir[i] = i % kinfo->rss_size;
> +
> + ret = hclgevf_set_rss(handle, rss_indir, NULL, 0);
> + if (ret)
> + dev_err(&hdev->pdev->dev, "set rss indir table fail, ret=%d\n",
> + ret);
> +
> + kfree(rss_indir);
> +
> +out:
> + if (!ret)
> + dev_info(&hdev->pdev->dev,
> + "Channels changed, rss_size from %u to %u, tqps from %u to %u",
> + cur_rss_size, kinfo->rss_size,
> + cur_tqps, kinfo->rss_size * kinfo->num_tc);
> +
> + return ret;
> +}

IIRC David asked you not to issue this log message in v1 review.

Michal Kubecek

2019-09-12 08:24:21

by Huazhong Tan

[permalink] [raw]
Subject: Re: [PATCH V2 net-next 1/7] net: hns3: add ethtool_ops.set_channels support for HNS3 VF driver

Hi, Michal

On 2019/9/12 14:23, Michal Kubecek wrote:
> On Wed, Sep 11, 2019 at 10:40:33AM +0800, Huazhong Tan wrote:
>> From: Guangbin Huang <[email protected]>
>>
>> This patch adds ethtool_ops.set_channels support for HNS3 VF driver,
>> and updates related TQP information and RSS information, to support
>> modification of VF TQP number, and uses current rss_size instead of
>> max_rss_size to initialize RSS.
>>
>> Also, fixes a format error in hclgevf_get_rss().
>>
>> Signed-off-by: Guangbin Huang <[email protected]>
>> Signed-off-by: Huazhong Tan <[email protected]>
>> ---
>> drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c | 1 +
>> .../ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c | 83 ++++++++++++++++++++--
>> 2 files changed, 79 insertions(+), 5 deletions(-)
>>
> ...
>> diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
>> index 594cae8..e3090b3 100644
>> --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
>> +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
> ...
>> +static void hclgevf_update_rss_size(struct hnae3_handle *handle,
>> + u32 new_tqps_num)
>> +{
>> + struct hnae3_knic_private_info *kinfo = &handle->kinfo;
>> + struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
>> + u16 max_rss_size;
>> +
>> + kinfo->req_rss_size = new_tqps_num;
>> +
>> + max_rss_size = min_t(u16, hdev->rss_size_max,
>> + hdev->num_tqps / kinfo->num_tc);
>> +
>> + /* Use the user's configuration when it is not larger than
>> + * max_rss_size, otherwise, use the maximum specification value.
>> + */
>> + if (kinfo->req_rss_size != kinfo->rss_size && kinfo->req_rss_size &&
>> + kinfo->req_rss_size <= max_rss_size)
>> + kinfo->rss_size = kinfo->req_rss_size;
>> + else if (kinfo->rss_size > max_rss_size ||
>> + (!kinfo->req_rss_size && kinfo->rss_size < max_rss_size))
>> + kinfo->rss_size = max_rss_size;
>
> I don't think requested channel count can be larger than max_rss_size
> here. In ethtool_set_channels(), we check that requested channel counts
> do not exceed maximum channel counts as reported by ->get_channels().
> And hclgevf_get_max_channels() cannot return more than max_rss_size.
>

When we can modify the TC number (which PF has already supported, VF may
implement in the future) using lldptool or tc cmd,
hclgevf_update_rss_size will be called to update the rss information,
which may also change max_rss_size, so we will use max_rss_size instead
if the kinfo->rss_size configured using ethtool is bigger than max_rss_size.

>> +
>> + kinfo->num_tqps = kinfo->num_tc * kinfo->rss_size;
>> +}
>> +
>> +static int hclgevf_set_channels(struct hnae3_handle *handle, u32 new_tqps_num,
>> + bool rxfh_configured)
>> +{
>> + struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
>> + struct hnae3_knic_private_info *kinfo = &handle->kinfo;
>> + u16 cur_rss_size = kinfo->rss_size;
>> + u16 cur_tqps = kinfo->num_tqps;
>> + u32 *rss_indir;
>> + unsigned int i;
>> + int ret;
>> +
>> + hclgevf_update_rss_size(handle, new_tqps_num);
>> +
>> + ret = hclgevf_set_rss_tc_mode(hdev, kinfo->rss_size);
>> + if (ret)
>> + return ret;
>> +
>> + /* RSS indirection table has been configuared by user */
>> + if (rxfh_configured)
>> + goto out;
>> +
>> + /* Reinitializes the rss indirect table according to the new RSS size */
>> + rss_indir = kcalloc(HCLGEVF_RSS_IND_TBL_SIZE, sizeof(u32), GFP_KERNEL);
>> + if (!rss_indir)
>> + return -ENOMEM;
>> +
>> + for (i = 0; i < HCLGEVF_RSS_IND_TBL_SIZE; i++)
>> + rss_indir[i] = i % kinfo->rss_size;
>> +
>> + ret = hclgevf_set_rss(handle, rss_indir, NULL, 0);
>> + if (ret)
>> + dev_err(&hdev->pdev->dev, "set rss indir table fail, ret=%d\n",
>> + ret);
>> +
>> + kfree(rss_indir);
>> +
>> +out:
>> + if (!ret)
>> + dev_info(&hdev->pdev->dev,
>> + "Channels changed, rss_size from %u to %u, tqps from %u to %u",
>> + cur_rss_size, kinfo->rss_size,
>> + cur_tqps, kinfo->rss_size * kinfo->num_tc);
>> +
>> + return ret;
>> +}
>
> IIRC David asked you not to issue this log message in v1 review.
>
> Michal Kubecek
>

Sorry for missing this log.

Thanks.

> .
>