2017-10-15 20:53:46

by Sergey Matyukevich

[permalink] [raw]
Subject: [PATCH 0/8] qtnfmac: misc small features and fixes

Hello Kalle, Igor, and all

This patch series includes a number of small features and fixes
for qtnfmac driver.

Igor Mitsyanko (1):
qtnfmac: advertise support of inactivity timeout

Sergey Matyukevich (4):
qtnfmac: modify full Tx queue error reporting
qtnfmac: enable registration of more mgmt frames
qtnfmac: drop nonexistent function declaration
qtnfmac: modify full Tx queue recovery


drivers/net/wireless/quantenna/qtnfmac/cfg80211.c | 17 +++++++++++++++--
drivers/net/wireless/quantenna/qtnfmac/commands.c | 5 +++--
drivers/net/wireless/quantenna/qtnfmac/core.c | 27 +++++++++++++++++++++++++++
drivers/net/wireless/quantenna/qtnfmac/core.h | 4 +---
drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c | 15 +++++++++------
drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h | 1 +
drivers/net/wireless/quantenna/qtnfmac/qlink.h | 11 ++++++++++-
7 files changed, 66 insertions(+), 14 deletions(-)


2017-10-15 20:53:49

by Sergey Matyukevich

[permalink] [raw]
Subject: [PATCH 2/5] qtnfmac: enable registration of more mgmt frames

Support registration for more mgmt frame types
for debug and monitoring purposes.

Signed-off-by: Sergey Matyukevich <[email protected]>
---
drivers/net/wireless/quantenna/qtnfmac/cfg80211.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
index 028bed1acd82..b4c2fa7934fd 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
@@ -73,7 +73,10 @@ qtnf_mgmt_stypes[NUM_NL80211_IFTYPES] = {
[NL80211_IFTYPE_AP] = {
.tx = BIT(IEEE80211_STYPE_ACTION >> 4),
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
- BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4),
},
};

@@ -349,6 +352,13 @@ qtnf_mgmt_frame_register(struct wiphy *wiphy, struct wireless_dev *wdev,
return;

switch (frame_type & IEEE80211_FCTL_STYPE) {
+ case IEEE80211_STYPE_REASSOC_REQ:
+ case IEEE80211_STYPE_ASSOC_REQ:
+ qlink_frame_type = QLINK_MGMT_FRAME_ASSOC_REQ;
+ break;
+ case IEEE80211_STYPE_AUTH:
+ qlink_frame_type = QLINK_MGMT_FRAME_AUTH;
+ break;
case IEEE80211_STYPE_PROBE_REQ:
qlink_frame_type = QLINK_MGMT_FRAME_PROBE_REQ;
break;
--
2.11.0

2017-10-29 15:32:34

by Sergey Matyukevich

[permalink] [raw]
Subject: Re: [1/5] qtnfmac: modify full Tx queue error reporting

Hello Kalle,

> Failed to apply:
>
> fatal: sha1 information is lacking or useless (drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c).
> error: could not build fake ancestor
> Applying: qtnfmac: modify full Tx queue recovery
> Patch failed at 0001 qtnfmac: modify full Tx queue recovery
> The copy of the patch that failed is found in: .git/rebase-apply/patch
>
> 5 patches set to Changes Requested.
>
> 10007281 [1/5] qtnfmac: modify full Tx queue error reporting
> 10007279 [2/5] qtnfmac: enable registration of more mgmt frames
> 10007283 [3/5] qtnfmac: drop nonexistent function declaration
> 10007285 [4/5] qtnfmac: modify full Tx queue recovery
> 10007287 [5/5] qtnfmac: advertise support of inactivity timeout

My assumption is that by default all the patches should cleanly apply to wireless-drivers-next.
I could apply the patch in question to wireless-drivers-next without any issues.
Rebase of the whole series on top of wireless-drivers-next looks good as well.
I will resend rebased patches as v2. Meanwhile do you have any idea what could go wrong ?
The error message looks scary...

Regards,
Sergey

2017-10-16 19:22:22

by Igor Mitsyanko

[permalink] [raw]
Subject: Re: [PATCH 0/8] qtnfmac: misc small features and fixes

On 10/15/2017 01:53 PM, Sergey Matyukevich wrote:
> Hello Kalle, Igor, and all
>
> This patch series includes a number of small features and fixes
> for qtnfmac driver.
>
> Igor Mitsyanko (1):
> qtnfmac: advertise support of inactivity timeout
>
> Sergey Matyukevich (4):
> qtnfmac: modify full Tx queue error reporting
> qtnfmac: enable registration of more mgmt frames
> qtnfmac: drop nonexistent function declaration
> qtnfmac: modify full Tx queue recovery
>
>
> drivers/net/wireless/quantenna/qtnfmac/cfg80211.c | 17 +++++++++++++++--
> drivers/net/wireless/quantenna/qtnfmac/commands.c | 5 +++--
> drivers/net/wireless/quantenna/qtnfmac/core.c | 27 +++++++++++++++++++++++++++
> drivers/net/wireless/quantenna/qtnfmac/core.h | 4 +---
> drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c | 15 +++++++++------
> drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h | 1 +
> drivers/net/wireless/quantenna/qtnfmac/qlink.h | 11 ++++++++++-
> 7 files changed, 66 insertions(+), 14 deletions(-)
>

Reviewed-by: Igor Mitsyanko <[email protected]>

2017-10-15 20:53:50

by Sergey Matyukevich

[permalink] [raw]
Subject: [PATCH 3/5] qtnfmac: drop nonexistent function declaration

Function qtnf_classify_skb_no_mbss has been used for debug
during early stage of development. Drop its declaration.

Signed-off-by: Sergey Matyukevich <[email protected]>
---
drivers/net/wireless/quantenna/qtnfmac/core.h | 3 ---
1 file changed, 3 deletions(-)

diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.h b/drivers/net/wireless/quantenna/qtnfmac/core.h
index 44a2cbb19310..49ae700f66f0 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/core.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/core.h
@@ -153,9 +153,6 @@ int qtnf_cmd_send_get_phy_params(struct qtnf_wmac *mac);

struct qtnf_wmac *qtnf_core_get_mac(const struct qtnf_bus *bus, u8 macid);
struct net_device *qtnf_classify_skb(struct qtnf_bus *bus, struct sk_buff *skb);
-struct net_device *qtnf_classify_skb_no_mbss(struct qtnf_bus *bus,
- struct sk_buff *skb);
-
void qtnf_virtual_intf_cleanup(struct net_device *ndev);

void qtnf_netdev_updown(struct net_device *ndev, bool up);
--
2.11.0

2017-10-30 10:31:34

by Kalle Valo

[permalink] [raw]
Subject: Re: [1/5] qtnfmac: modify full Tx queue error reporting

Sergey Matyukevich <[email protected]> writes:

>> > My assumption is that by default all the patches should cleanly apply
>> > to wireless-drivers-next. I could apply the patch in question to
>> > wireless-drivers-next without any issues.
>>
>> Odd. How did you apply it? My script uses 'git am -s -3' individually
>> for each patch in the series, but to my knowledge that shouldn't cause
>> any problems.
>
> It turns out I mechanically rebased my branch on top of w-d-n before writing
> the previous email. Now I could reproduce the same failure when using this
> command for the original patches downloaded from patchwork.

Ok, that makes sense. Most likely when git-rebase does a 3-way merge
automatically and that's why you were able to successfully apply them.
But as git-am was not able to use 3-way merge due to incorrect sha1
information it failed.

> The problem was in conflict with qtnfmac fixes pulled to w-d-n from
> w-d.

Ok, thanks for investigating the root cause.

--
Kalle Valo

2017-10-15 20:53:53

by Sergey Matyukevich

[permalink] [raw]
Subject: [PATCH 4/5] qtnfmac: modify full Tx queue recovery

Current recovery approach is to wake s/w Tx queues for skb->dev netdevice.
However this approach doesn't cover the case when h/w queue is full of
packets from a single wireless interface. Suppose xmit attempt from the
second wireless interface fails due to failed reclaim. Then the second
interface will not have a chance to recover even if subsequent reclaims
succeed. Possible solution is to attempt to wake all the s/w queues
belonging to driver interfaces.

Signed-off-by: Sergey Matyukevich <[email protected]>
---
drivers/net/wireless/quantenna/qtnfmac/core.c | 27 ++++++++++++++++++++++
drivers/net/wireless/quantenna/qtnfmac/core.h | 1 +
.../net/wireless/quantenna/qtnfmac/pearl/pcie.c | 13 +++++++----
.../quantenna/qtnfmac/pearl/pcie_bus_priv.h | 1 +
4 files changed, 37 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.c b/drivers/net/wireless/quantenna/qtnfmac/core.c
index 5e60180482d1..5fd1a9c8b733 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/core.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/core.c
@@ -618,6 +618,33 @@ struct net_device *qtnf_classify_skb(struct qtnf_bus *bus, struct sk_buff *skb)
}
EXPORT_SYMBOL_GPL(qtnf_classify_skb);

+void qtnf_wake_all_queues(struct net_device *ndev)
+{
+ struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev);
+ struct qtnf_wmac *mac;
+ struct qtnf_bus *bus;
+ int macid;
+ int i;
+
+ if (unlikely(!vif || !vif->mac || !vif->mac->bus))
+ return;
+
+ bus = vif->mac->bus;
+
+ for (macid = 0; macid < QTNF_MAX_MAC; macid++) {
+ if (!(bus->hw_info.mac_bitmap & BIT(macid)))
+ continue;
+
+ mac = bus->mac[macid];
+ for (i = 0; i < QTNF_MAX_INTF; i++) {
+ vif = &mac->iflist[i];
+ if (vif->netdev && netif_queue_stopped(vif->netdev))
+ netif_tx_wake_all_queues(vif->netdev);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(qtnf_wake_all_queues);
+
MODULE_AUTHOR("Quantenna Communications");
MODULE_DESCRIPTION("Quantenna 802.11 wireless LAN FullMAC driver.");
MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.h b/drivers/net/wireless/quantenna/qtnfmac/core.h
index 49ae700f66f0..da2c24e2271d 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/core.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/core.h
@@ -153,6 +153,7 @@ int qtnf_cmd_send_get_phy_params(struct qtnf_wmac *mac);

struct qtnf_wmac *qtnf_core_get_mac(const struct qtnf_bus *bus, u8 macid);
struct net_device *qtnf_classify_skb(struct qtnf_bus *bus, struct sk_buff *skb);
+void qtnf_wake_all_queues(struct net_device *ndev);
void qtnf_virtual_intf_cleanup(struct net_device *ndev);

void qtnf_netdev_updown(struct net_device *ndev, bool up);
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c
index a8f2c46f3a25..3d88a0b2b5f5 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c
@@ -617,9 +617,10 @@ static void qtnf_pcie_data_tx_reclaim(struct qtnf_pcie_bus_priv *priv)
if (skb->dev) {
skb->dev->stats.tx_packets++;
skb->dev->stats.tx_bytes += skb->len;
-
- if (netif_queue_stopped(skb->dev))
- netif_wake_queue(skb->dev);
+ if (unlikely(priv->tx_stopped)) {
+ qtnf_wake_all_queues(skb->dev);
+ priv->tx_stopped = 0;
+ }
}

dev_kfree_skb_any(skb);
@@ -666,8 +667,10 @@ static int qtnf_pcie_data_tx(struct qtnf_bus *bus, struct sk_buff *skb)
int ret = 0;

if (!qtnf_tx_queue_ready(priv)) {
- if (skb->dev)
- netif_stop_queue(skb->dev);
+ if (skb->dev) {
+ netif_tx_stop_all_queues(skb->dev);
+ priv->tx_stopped = 1;
+ }

return NETDEV_TX_BUSY;
}
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h
index e76a23716ee0..e477abec5f3b 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h
@@ -35,6 +35,7 @@ struct qtnf_pcie_bus_priv {
/* lock for tx reclaim operations */
spinlock_t tx_reclaim_lock;
u8 msi_enabled;
+ u8 tx_stopped;
int mps;

struct workqueue_struct *workqueue;
--
2.11.0

2017-10-30 09:45:12

by Sergey Matyukevich

[permalink] [raw]
Subject: Re: [1/5] qtnfmac: modify full Tx queue error reporting

> > My assumption is that by default all the patches should cleanly apply
> > to wireless-drivers-next. I could apply the patch in question to
> > wireless-drivers-next without any issues.
>
> Odd. How did you apply it? My script uses 'git am -s -3' individually
> for each patch in the series, but to my knowledge that shouldn't cause
> any problems.

It turns out I mechanically rebased my branch on top of w-d-n before writing
the previous email. Now I could reproduce the same failure when using this
command for the original patches downloaded from patchwork. The problem
was in conflict with qtnfmac fixes pulled to w-d-n from w-d.

Regards,
Sergey

2017-10-15 20:53:54

by Sergey Matyukevich

[permalink] [raw]
Subject: [PATCH 5/5] qtnfmac: advertise support of inactivity timeout

From: Igor Mitsyanko <[email protected]>

Wireless device may implement a logic to kick-out STA due to inactivity
for a certain period of time. This feature needs to be advertised to
higher layers if supported. Timeout value is still taken from
parameters to START_AP command, nothing changes here.

Signed-off-by: Igor Mitsyanko <[email protected]>
---
drivers/net/wireless/quantenna/qtnfmac/cfg80211.c | 5 ++++-
drivers/net/wireless/quantenna/qtnfmac/commands.c | 5 +++--
drivers/net/wireless/quantenna/qtnfmac/qlink.h | 11 ++++++++++-
3 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
index b4c2fa7934fd..b9841c0d952f 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
@@ -955,7 +955,10 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)

ether_addr_copy(wiphy->perm_addr, mac->macaddr);

- if (hw_info->hw_capab & QLINK_HW_SUPPORTS_REG_UPDATE) {
+ if (hw_info->hw_capab & QLINK_HW_CAPAB_STA_INACT_TIMEOUT)
+ wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER;
+
+ if (hw_info->hw_capab & QLINK_HW_CAPAB_REG_UPDATE) {
wiphy->regulatory_flags |= REGULATORY_STRICT_REG |
REGULATORY_CUSTOM_REG;
wiphy->reg_notifier = qtnf_cfg80211_reg_notifier;
diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c
index babdc600c193..b81f81bd1411 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/commands.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c
@@ -975,10 +975,11 @@ qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus,
return -EINVAL;
}

- pr_info("fw_version=%d, MACs map %#x, alpha2=\"%c%c\", chains Tx=%u Rx=%u\n",
+ pr_info("fw_version=%d, MACs map %#x, alpha2=\"%c%c\", chains Tx=%u Rx=%u, capab=0x%x\n",
hwinfo->fw_ver, hwinfo->mac_bitmap,
hwinfo->rd->alpha2[0], hwinfo->rd->alpha2[1],
- hwinfo->total_tx_chain, hwinfo->total_rx_chain);
+ hwinfo->total_tx_chain, hwinfo->total_rx_chain,
+ hwinfo->hw_capab);

return 0;
}
diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink.h b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
index 7b313d38c30b..0f582782682f 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/qlink.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
@@ -61,8 +61,17 @@ struct qlink_msg_header {
/* Generic definitions of data and information carried in QLINK messages
*/

+/**
+ * enum qlink_hw_capab - device capabilities.
+ *
+ * @QLINK_HW_CAPAB_REG_UPDATE: device can update it's regulatory region.
+ * @QLINK_HW_CAPAB_STA_INACT_TIMEOUT: device implements a logic to kick-out
+ * associated STAs due to inactivity. Inactivity timeout period is taken
+ * from QLINK_CMD_START_AP parameters.
+ */
enum qlink_hw_capab {
- QLINK_HW_SUPPORTS_REG_UPDATE = BIT(0),
+ QLINK_HW_CAPAB_REG_UPDATE = BIT(0),
+ QLINK_HW_CAPAB_STA_INACT_TIMEOUT = BIT(1),
};

enum qlink_phy_mode {
--
2.11.0

2017-10-30 08:02:12

by Kalle Valo

[permalink] [raw]
Subject: Re: [1/5] qtnfmac: modify full Tx queue error reporting

Sergey Matyukevich <[email protected]> writes:

> Hello Kalle,
>
>> Failed to apply:
>>
>> fatal: sha1 information is lacking or useless
>> (drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c).
>> error: could not build fake ancestor
>> Applying: qtnfmac: modify full Tx queue recovery
>> Patch failed at 0001 qtnfmac: modify full Tx queue recovery
>> The copy of the patch that failed is found in: .git/rebase-apply/patch
>>
>> 5 patches set to Changes Requested.
>>
>> 10007281 [1/5] qtnfmac: modify full Tx queue error reporting
>> 10007279 [2/5] qtnfmac: enable registration of more mgmt frames
>> 10007283 [3/5] qtnfmac: drop nonexistent function declaration
>> 10007285 [4/5] qtnfmac: modify full Tx queue recovery
>> 10007287 [5/5] qtnfmac: advertise support of inactivity timeout
>
> My assumption is that by default all the patches should cleanly apply
> to wireless-drivers-next. I could apply the patch in question to
> wireless-drivers-next without any issues.

Odd. How did you apply it? My script uses 'git am -s -3' individually
for each patch in the series, but to my knowledge that shouldn't cause
any problems.

> Rebase of the whole series on top of wireless-drivers-next looks good
> as well. I will resend rebased patches as v2.

Thanks, sending v2 is the easiest for me. If there are problems again
I'll investigate in detail what's going on.

> Meanwhile do you have any idea what could go wrong ? The error message
> looks scary...

You mean the "sha1 information is lacking", right? It means that git was
not able to find a common ancestor for the file which it could use to
create the 3-way merge. Usually that happens when people have
out-of-tree patches on the branch they are submitting from.

And that's why I recommend to use the w-d-next master branch as the
baseline when submitting patches, and not have any other custom patches
applied on the branch. This should keep sha1 information correct and
make it possible for git to use 3-way merge.

--
Kalle Valo

2017-10-27 08:48:15

by Kalle Valo

[permalink] [raw]
Subject: Re: [1/5] qtnfmac: modify full Tx queue error reporting

Sergey Matyukevich <[email protected]> wrote:

> Under heavy load it is normal that h/w Tx queue is almost full all the time
> and reclaim should be done before transmitting next packet. Warning still
> should be reported as well as s/w Tx queues should be stopped in the
> case when reclaim failed.
>
> Signed-off-by: Sergey Matyukevich <[email protected]>

Failed to apply:

fatal: sha1 information is lacking or useless (drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c).
error: could not build fake ancestor
Applying: qtnfmac: modify full Tx queue recovery
Patch failed at 0001 qtnfmac: modify full Tx queue recovery
The copy of the patch that failed is found in: .git/rebase-apply/patch

5 patches set to Changes Requested.

10007281 [1/5] qtnfmac: modify full Tx queue error reporting
10007279 [2/5] qtnfmac: enable registration of more mgmt frames
10007283 [3/5] qtnfmac: drop nonexistent function declaration
10007285 [4/5] qtnfmac: modify full Tx queue recovery
10007287 [5/5] qtnfmac: advertise support of inactivity timeout

--
https://patchwork.kernel.org/patch/10007281/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

2017-10-15 20:53:47

by Sergey Matyukevich

[permalink] [raw]
Subject: [PATCH 1/5] qtnfmac: modify full Tx queue error reporting

Under heavy load it is normal that h/w Tx queue is almost full all the time
and reclaim should be done before transmitting next packet. Warning still
should be reported as well as s/w Tx queues should be stopped in the
case when reclaim failed.

Signed-off-by: Sergey Matyukevich <[email protected]>
---
drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c
index 502e72b7cdcc..a8f2c46f3a25 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c
@@ -643,11 +643,11 @@ static int qtnf_tx_queue_ready(struct qtnf_pcie_bus_priv *priv)
{
if (!CIRC_SPACE(priv->tx_bd_w_index, priv->tx_bd_r_index,
priv->tx_bd_num)) {
- pr_err_ratelimited("reclaim full Tx queue\n");
qtnf_pcie_data_tx_reclaim(priv);

if (!CIRC_SPACE(priv->tx_bd_w_index, priv->tx_bd_r_index,
priv->tx_bd_num)) {
+ pr_warn_ratelimited("reclaim full Tx queue\n");
priv->tx_full_count++;
return 0;
}
--
2.11.0