From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-gw1-out.broadcom.com (mail-gw1-out.broadcom.com [216.31.210.62]) by dpdk.org (Postfix) with ESMTP id BC829682B for ; Tue, 7 Jun 2016 00:09:29 +0200 (CEST) X-IronPort-AV: E=Sophos;i="5.26,429,1459839600"; d="scan'208";a="96627869" Received: from mail-irv-18.broadcom.com ([10.15.198.37]) by mail-gw1-out.broadcom.com with ESMTP; 06 Jun 2016 16:16:28 -0700 Received: from mail-irva-12.broadcom.com (mail-irva-12.broadcom.com [10.11.16.101]) by mail-irv-18.broadcom.com (Postfix) with ESMTP id 515AC82023; Mon, 6 Jun 2016 15:09:29 -0700 (PDT) Received: from DPDK-C1.broadcom.com (dhcp-10-13-115-104.irv.broadcom.com [10.13.115.104]) by mail-irva-12.broadcom.com (Postfix) with ESMTP id 1B418127634; Mon, 6 Jun 2016 15:09:27 -0700 (PDT) From: Stephen Hurd To: dev@dpdk.org, ajit.khaparde@broadcom.com, bruce.richardson@intel.com Date: Mon, 6 Jun 2016 15:08:15 -0700 Message-Id: <1465250923-78695-11-git-send-email-stephen.hurd@broadcom.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1465250923-78695-1-git-send-email-stephen.hurd@broadcom.com> References: <1465250923-78695-1-git-send-email-stephen.hurd@broadcom.com> Subject: [dpdk-dev] [PATCH v4 11/39] bnxt: add Rx queue create/destroy operations X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: patches and discussions about DPDK List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 06 Jun 2016 22:09:30 -0000 From: Ajit Khaparde Adds initial create/destroy queue code. Still requires RX ring support which will be brought in subsequent patches to be functional. v4: Address review comments and fix issues pointed out by checkpatch. Signed-off-by: Ajit Khaparde Reviewed-by: David Christensen Signed-off-by: Stephen Hurd --- drivers/net/bnxt/Makefile | 1 + drivers/net/bnxt/bnxt.h | 2 + drivers/net/bnxt/bnxt_ethdev.c | 3 + drivers/net/bnxt/bnxt_rxq.c | 288 +++++++++++++++++++++++++++++++++ drivers/net/bnxt/bnxt_rxq.h | 74 +++++++++ drivers/net/bnxt/hsi_struct_def_dpdk.h | 121 ++++++++++++++ 6 files changed, 489 insertions(+) create mode 100644 drivers/net/bnxt/bnxt_rxq.c create mode 100644 drivers/net/bnxt/bnxt_rxq.h diff --git a/drivers/net/bnxt/Makefile b/drivers/net/bnxt/Makefile index 13a90b9..21ed71c 100644 --- a/drivers/net/bnxt/Makefile +++ b/drivers/net/bnxt/Makefile @@ -53,6 +53,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_BNXT_PMD) += bnxt_ethdev.c SRCS-$(CONFIG_RTE_LIBRTE_BNXT_PMD) += bnxt_filter.c SRCS-$(CONFIG_RTE_LIBRTE_BNXT_PMD) += bnxt_hwrm.c SRCS-$(CONFIG_RTE_LIBRTE_BNXT_PMD) += bnxt_ring.c +SRCS-$(CONFIG_RTE_LIBRTE_BNXT_PMD) += bnxt_rxq.c SRCS-$(CONFIG_RTE_LIBRTE_BNXT_PMD) += bnxt_txq.c SRCS-$(CONFIG_RTE_LIBRTE_BNXT_PMD) += bnxt_vnic.c diff --git a/drivers/net/bnxt/bnxt.h b/drivers/net/bnxt/bnxt.h index 49aa38b..f7cf9d1 100644 --- a/drivers/net/bnxt/bnxt.h +++ b/drivers/net/bnxt/bnxt.h @@ -142,6 +142,8 @@ struct bnxt { /* Default completion ring */ struct bnxt_cp_ring_info *def_cp_ring; + unsigned int nr_vnics; + struct bnxt_vnic_info *vnic_info; STAILQ_HEAD(, bnxt_vnic_info) free_vnic_list; diff --git a/drivers/net/bnxt/bnxt_ethdev.c b/drivers/net/bnxt/bnxt_ethdev.c index 77a6d92..7e7d1ab 100644 --- a/drivers/net/bnxt/bnxt_ethdev.c +++ b/drivers/net/bnxt/bnxt_ethdev.c @@ -41,6 +41,7 @@ #include "bnxt.h" #include "bnxt_hwrm.h" +#include "bnxt_rxq.h" #include "bnxt_txq.h" #define DRV_MODULE_NAME "bnxt" @@ -186,6 +187,8 @@ static struct eth_dev_ops bnxt_dev_ops = { .dev_infos_get = bnxt_dev_info_get_op, .dev_close = bnxt_dev_close_op, .dev_configure = bnxt_dev_configure_op, + .rx_queue_setup = bnxt_rx_queue_setup_op, + .rx_queue_release = bnxt_rx_queue_release_op, .tx_queue_setup = bnxt_tx_queue_setup_op, .tx_queue_release = bnxt_tx_queue_release_op, }; diff --git a/drivers/net/bnxt/bnxt_rxq.c b/drivers/net/bnxt/bnxt_rxq.c new file mode 100644 index 0000000..11e9466 --- /dev/null +++ b/drivers/net/bnxt/bnxt_rxq.c @@ -0,0 +1,288 @@ +/*- + * BSD LICENSE + * + * Copyright(c) Broadcom Limited. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Broadcom Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include "bnxt.h" +#include "bnxt_filter.h" +#include "bnxt_hwrm.h" +#include "bnxt_ring.h" +#include "bnxt_rxq.h" +#include "bnxt_vnic.h" +#include "hsi_struct_def_dpdk.h" + +/* + * RX Queues + */ + +void bnxt_free_rxq_stats(struct bnxt_rx_queue *rxq) +{ + struct bnxt_cp_ring_info *cpr = rxq->cp_ring; + + /* 'Unreserve' rte_memzone */ + /* N/A */ + + if (cpr->hw_stats) + cpr->hw_stats = NULL; +} + +int bnxt_mq_rx_configure(struct bnxt *bp) +{ + struct rte_eth_conf *dev_conf = &bp->eth_dev->data->dev_conf; + unsigned int i, j, nb_q_per_grp, ring_idx; + int start_grp_id, end_grp_id, rc = 0; + struct bnxt_vnic_info *vnic; + struct bnxt_filter_info *filter; + struct bnxt_rx_queue *rxq; + + bp->nr_vnics = 0; + + /* Single queue mode */ + if (bp->rx_cp_nr_rings < 2) { + vnic = bnxt_alloc_vnic(bp); + if (!vnic) { + RTE_LOG(ERR, PMD, "VNIC alloc failed\n"); + rc = -ENOMEM; + goto err_out; + } + STAILQ_INSERT_TAIL(&bp->ff_pool[0], vnic, next); + bp->nr_vnics++; + + rxq = bp->eth_dev->data->rx_queues[0]; + rxq->vnic = vnic; + + vnic->func_default = true; + vnic->ff_pool_idx = 0; + vnic->start_grp_id = 1; + vnic->end_grp_id = vnic->start_grp_id + + bp->rx_cp_nr_rings - 1; + filter = bnxt_alloc_filter(bp); + if (!filter) { + RTE_LOG(ERR, PMD, "L2 filter alloc failed\n"); + rc = -ENOMEM; + goto err_out; + } + STAILQ_INSERT_TAIL(&vnic->filter, filter, next); + goto out; + } + + /* Multi-queue mode */ + if (dev_conf->rxmode.mq_mode & ETH_MQ_RX_VMDQ_FLAG) { + /* VMDq ONLY, VMDq+RSS, VMDq+DCB, VMDq+DCB+RSS */ + enum rte_eth_nb_pools pools; + + switch (dev_conf->rxmode.mq_mode) { + case ETH_MQ_RX_VMDQ_RSS: + case ETH_MQ_RX_VMDQ_ONLY: + { + const struct rte_eth_vmdq_rx_conf *conf = + &dev_conf->rx_adv_conf.vmdq_rx_conf; + + /* ETH_8/64_POOLs */ + pools = conf->nb_queue_pools; + break; + } + default: + RTE_LOG(ERR, PMD, "Unsupported mq_mod %d\n", + dev_conf->rxmode.mq_mode); + rc = -EINVAL; + goto err_out; + } + /* For each pool, allocate MACVLAN CFA rule & VNIC */ + if (!pools) { + RTE_LOG(ERR, PMD, + "VMDq pool not set, defaulted to 64\n"); + pools = ETH_64_POOLS; + } + nb_q_per_grp = bp->rx_cp_nr_rings / pools; + start_grp_id = 1; + end_grp_id = start_grp_id + nb_q_per_grp - 1; + + ring_idx = 0; + for (i = 0; i < pools; i++) { + vnic = bnxt_alloc_vnic(bp); + if (!vnic) { + RTE_LOG(ERR, PMD, + "VNIC alloc failed\n"); + rc = -ENOMEM; + goto err_out; + } + STAILQ_INSERT_TAIL(&bp->ff_pool[i], vnic, next); + bp->nr_vnics++; + + for (j = 0; j < nb_q_per_grp; j++, ring_idx++) { + rxq = bp->eth_dev->data->rx_queues[ring_idx]; + rxq->vnic = vnic; + } + if (i == 0) + vnic->func_default = true; + vnic->ff_pool_idx = i; + vnic->start_grp_id = start_grp_id; + vnic->end_grp_id = end_grp_id; + + filter = bnxt_alloc_filter(bp); + if (!filter) { + RTE_LOG(ERR, PMD, + "L2 filter alloc failed\n"); + rc = -ENOMEM; + goto err_out; + } + /* + * TODO: Configure & associate CFA rule for + * each VNIC for each VMDq with MACVLAN, MACVLAN+TC + */ + STAILQ_INSERT_TAIL(&vnic->filter, filter, next); + + start_grp_id = end_grp_id + 1; + end_grp_id += nb_q_per_grp; + } + goto out; + } + + /* Non-VMDq mode - RSS, DCB, RSS+DCB */ + /* Init default VNIC for RSS or DCB only */ + vnic = bnxt_alloc_vnic(bp); + if (!vnic) { + RTE_LOG(ERR, PMD, "VNIC alloc failed\n"); + rc = -ENOMEM; + goto err_out; + } + /* Partition the rx queues for the single pool */ + for (i = 0; i < bp->rx_cp_nr_rings; i++) { + rxq = bp->eth_dev->data->rx_queues[i]; + rxq->vnic = vnic; + } + STAILQ_INSERT_TAIL(&bp->ff_pool[0], vnic, next); + bp->nr_vnics++; + + vnic->func_default = true; + vnic->ff_pool_idx = 0; + vnic->start_grp_id = 1; + vnic->end_grp_id = vnic->start_grp_id + + bp->rx_cp_nr_rings - 1; + filter = bnxt_alloc_filter(bp); + if (!filter) { + RTE_LOG(ERR, PMD, "L2 filter alloc failed\n"); + rc = -ENOMEM; + goto err_out; + } + STAILQ_INSERT_TAIL(&vnic->filter, filter, next); + + if (dev_conf->rxmode.mq_mode & ETH_MQ_RX_RSS_FLAG) + vnic->hash_type = + HWRM_VNIC_RSS_CFG_INPUT_HASH_TYPE_IPV4 | + HWRM_VNIC_RSS_CFG_INPUT_HASH_TYPE_IPV6; + +out: + return rc; + +err_out: + /* Free allocated vnic/filters */ + + return rc; +} + +static void bnxt_rx_queue_release_mbufs(struct bnxt_rx_queue *rxq __rte_unused) +{ + /* TODO: Requires interaction with TX ring */ +} + +void bnxt_free_rx_mbufs(struct bnxt *bp) +{ + struct bnxt_rx_queue *rxq; + int i; + + for (i = 0; i < (int)bp->rx_nr_rings; i++) { + rxq = bp->rx_queues[i]; + bnxt_rx_queue_release_mbufs(rxq); + } +} + +void bnxt_rx_queue_release_op(void *rx_queue) +{ + struct bnxt_rx_queue *rxq = (struct bnxt_rx_queue *)rx_queue; + + if (rxq) { + bnxt_rx_queue_release_mbufs(rxq); + + /* TODO: Free ring and stats here */ + + rte_free(rxq); + } +} + +int bnxt_rx_queue_setup_op(struct rte_eth_dev *eth_dev, + uint16_t queue_idx, + uint16_t nb_desc, + unsigned int socket_id, + const struct rte_eth_rxconf *rx_conf, + struct rte_mempool *mp) +{ + struct bnxt *bp = (struct bnxt *)eth_dev->data->dev_private; + struct bnxt_rx_queue *rxq; + + if (!nb_desc || nb_desc > MAX_RX_DESC_CNT) { + RTE_LOG(ERR, PMD, "nb_desc %d is invalid", nb_desc); + return -EINVAL; + } + + if (eth_dev->data->rx_queues) { + rxq = eth_dev->data->rx_queues[queue_idx]; + if (rxq) + bnxt_rx_queue_release_op(rxq); + } + rxq = rte_zmalloc_socket("bnxt_rx_queue", sizeof(struct bnxt_rx_queue), + RTE_CACHE_LINE_SIZE, socket_id); + if (!rxq) { + RTE_LOG(ERR, PMD, "bnxt_rx_queue allocation failed!"); + return -ENOMEM; + } + rxq->bp = bp; + rxq->mb_pool = mp; + rxq->nb_rx_desc = nb_desc; + rxq->rx_free_thresh = rx_conf->rx_free_thresh; + + /* TODO: Initialize ring structure */ + + rxq->queue_id = queue_idx; + rxq->port_id = eth_dev->data->port_id; + rxq->crc_len = (uint8_t)((eth_dev->data->dev_conf.rxmode.hw_strip_crc) ? + 0 : ETHER_CRC_LEN); + + eth_dev->data->rx_queues[queue_idx] = rxq; + /* TODO: Allocate RX ring hardware descriptors */ + + return 0; +} diff --git a/drivers/net/bnxt/bnxt_rxq.h b/drivers/net/bnxt/bnxt_rxq.h new file mode 100644 index 0000000..9554329 --- /dev/null +++ b/drivers/net/bnxt/bnxt_rxq.h @@ -0,0 +1,74 @@ +/*- + * BSD LICENSE + * + * Copyright(c) Broadcom Limited. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Broadcom Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _BNXT_RQX_H_ +#define _BNXT_RQX_H_ + +struct bnxt; +struct bnxt_rx_ring_info; +struct bnxt_cp_ring_info; +struct bnxt_rx_queue { + struct rte_mempool *mb_pool; /* mbuf pool for RX ring */ + struct rte_mbuf *pkt_first_seg; /* 1st seg of pkt */ + struct rte_mbuf *pkt_last_seg; /* Last seg of pkt */ + uint64_t mbuf_initializer; /* val to init mbuf */ + uint16_t nb_rx_desc; /* num of RX desc */ + uint16_t rx_tail; /* cur val of RDT register */ + uint16_t nb_rx_hold; /* num held free RX desc */ + uint16_t rx_free_thresh; /* max free RX desc to hold */ + uint16_t queue_id; /* RX queue index */ + uint16_t reg_idx; /* RX queue register index */ + uint8_t port_id; /* Device port identifier */ + uint8_t crc_len; /* 0 if CRC stripped, 4 otherwise */ + + struct bnxt *bp; + struct bnxt_vnic_info *vnic; + + uint32_t rx_buf_size; + uint32_t rx_buf_use_size; /* useable size */ + struct bnxt_rx_ring_info *rx_ring; + struct bnxt_cp_ring_info *cp_ring; +}; + +void bnxt_free_rxq_stats(struct bnxt_rx_queue *rxq); +int bnxt_mq_rx_configure(struct bnxt *bp); +void bnxt_rx_queue_release_op(void *rx_queue); +int bnxt_rx_queue_setup_op(struct rte_eth_dev *eth_dev, + uint16_t queue_idx, + uint16_t nb_desc, + unsigned int socket_id, + const struct rte_eth_rxconf *rx_conf, + struct rte_mempool *mp); +void bnxt_free_rx_mbufs(struct bnxt *bp); + +#endif diff --git a/drivers/net/bnxt/hsi_struct_def_dpdk.h b/drivers/net/bnxt/hsi_struct_def_dpdk.h index ca42a87..bbec5bb 100644 --- a/drivers/net/bnxt/hsi_struct_def_dpdk.h +++ b/drivers/net/bnxt/hsi_struct_def_dpdk.h @@ -1897,6 +1897,127 @@ struct hwrm_queue_qportcfg_input { uint16_t unused_0; } __attribute__((packed)); +/* hwrm_vnic_rss_cfg */ +/* Description: This function is used to enable RSS configuration. */ + +/* Input (48 bytes) */ +struct hwrm_vnic_rss_cfg_input { + /* + * This value indicates what type of request this is. The format for the + * rest of the command is determined by this field. + */ + uint16_t req_type; + + /* + * This value indicates the what completion ring the request will be + * optionally completed on. If the value is -1, then no CR completion + * will be generated. Any other value must be a valid CR ring_id value + * for this function. + */ + uint16_t cmpl_ring; + + /* This value indicates the command sequence number. */ + uint16_t seq_id; + + /* + * Target ID of this command. 0x0 - 0xFFF8 - Used for function ids + * 0xFFF8 - 0xFFFE - Reserved for internal processors 0xFFFF - HWRM + */ + uint16_t target_id; + + /* + * This is the host address where the response will be written when the + * request is complete. This area must be 16B aligned and must be + * cleared to zero before the request is made. + */ + uint64_t resp_addr; + + /* + * When this bit is '1', the RSS hash shall be computed over source and + * destination IPv4 addresses of IPv4 packets. + */ + #define HWRM_VNIC_RSS_CFG_INPUT_HASH_TYPE_IPV4 UINT32_C(0x1) + /* + * When this bit is '1', the RSS hash shall be computed over + * source/destination IPv4 addresses and source/destination ports of + * TCP/IPv4 packets. + */ + #define HWRM_VNIC_RSS_CFG_INPUT_HASH_TYPE_TCP_IPV4 UINT32_C(0x2) + /* + * When this bit is '1', the RSS hash shall be computed over + * source/destination IPv4 addresses and source/destination ports of + * UDP/IPv4 packets. + */ + #define HWRM_VNIC_RSS_CFG_INPUT_HASH_TYPE_UDP_IPV4 UINT32_C(0x4) + /* + * When this bit is '1', the RSS hash shall be computed over source and + * destination IPv4 addresses of IPv6 packets. + */ + #define HWRM_VNIC_RSS_CFG_INPUT_HASH_TYPE_IPV6 UINT32_C(0x8) + /* + * When this bit is '1', the RSS hash shall be computed over + * source/destination IPv6 addresses and source/destination ports of + * TCP/IPv6 packets. + */ + #define HWRM_VNIC_RSS_CFG_INPUT_HASH_TYPE_TCP_IPV6 UINT32_C(0x10) + /* + * When this bit is '1', the RSS hash shall be computed over + * source/destination IPv6 addresses and source/destination ports of + * UDP/IPv6 packets. + */ + #define HWRM_VNIC_RSS_CFG_INPUT_HASH_TYPE_UDP_IPV6 UINT32_C(0x20) + uint32_t hash_type; + + uint32_t unused_0; + + /* This is the address for rss ring group table */ + uint64_t ring_grp_tbl_addr; + + /* This is the address for rss hash key table */ + uint64_t hash_key_tbl_addr; + + /* Index to the rss indirection table. */ + uint16_t rss_ctx_idx; + + uint16_t unused_1[3]; +} __attribute__((packed)); + +/* Output (16 bytes) */ +struct hwrm_vnic_rss_cfg_output { + /* + * Pass/Fail or error type Note: receiver to verify the in parameters, + * and fail the call with an error when appropriate + */ + uint16_t error_code; + + /* This field returns the type of original request. */ + uint16_t req_type; + + /* This field provides original sequence number of the command. */ + uint16_t seq_id; + + /* + * This field is the length of the response in bytes. The last byte of + * the response is a valid flag that will read as '1' when the command + * has been completely written to memory. + */ + uint16_t resp_len; + + uint32_t unused_0; + uint8_t unused_1; + uint8_t unused_2; + uint8_t unused_3; + + /* + * This field is used in Output records to indicate that the output is + * completely written to RAM. This field should be read as '1' to + * indicate that the output has been completely written. When writing a + * command completion or response to an internal processor, the order of + * writes has to be such that this field is written last. + */ + uint8_t valid; +} __attribute__((packed)); + /* Output (32 bytes) */ struct hwrm_queue_qportcfg_output { /* -- 1.9.1