123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466 |
- /*
- * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved.
- *
- * Permission to use, copy, modify, and/or distribute this software for
- * any purpose with or without fee is hereby granted, provided that the
- * above copyright notice and this permission notice appear in all
- * copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
- * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
- * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
- * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- */
- #include "hal_hw_headers.h"
- #include "dp_types.h"
- #include "dp_peer.h"
- #include "qdf_nbuf.h"
- #include "qdf_atomic.h"
- #include "qdf_types.h"
- #include "dp_tx.h"
- #include "dp_tx_desc.h"
- #include "dp_internal.h"
- #include "dp_txrx_me.h"
- #define MAX_ME_BUF_CHUNK 1424
- #define ME_US_TO_SEC(_x) ((_x) / (1000 * 1000))
- #define ME_CLEAN_WAIT_TIMEOUT (200000) /*200ms*/
- #define ME_CLEAN_WAIT_COUNT 400
- /**
- * dp_tx_me_init():Initialize ME buffer ppol
- * @pdev: DP PDEV handle
- *
- * Return:0 on Succes 1 on failure
- */
- static inline uint16_t
- dp_tx_me_init(struct dp_pdev *pdev)
- {
- uint16_t i, mc_uc_buf_len, num_pool_elems;
- uint32_t pool_size;
- struct dp_tx_me_buf_t *p;
- mc_uc_buf_len = sizeof(struct dp_tx_me_buf_t);
- num_pool_elems = MAX_ME_BUF_CHUNK;
- /* Add flow control buffer count */
- pool_size = (mc_uc_buf_len) * num_pool_elems;
- pdev->me_buf.size = mc_uc_buf_len;
- if (!(pdev->me_buf.vaddr)) {
- qdf_spin_lock_bh(&pdev->tx_mutex);
- pdev->me_buf.vaddr = qdf_mem_malloc(pool_size);
- if (!(pdev->me_buf.vaddr)) {
- qdf_spin_unlock_bh(&pdev->tx_mutex);
- QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO,
- "Error allocating memory pool");
- return 1;
- }
- pdev->me_buf.buf_in_use = 0;
- pdev->me_buf.freelist =
- (struct dp_tx_me_buf_t *)pdev->me_buf.vaddr;
- /*
- * me_buf looks like this
- * |=======+==========================|
- * | ptr | Dst MAC |
- * |=======+==========================|
- */
- p = pdev->me_buf.freelist;
- for (i = 0; i < num_pool_elems - 1; i++) {
- p->next = (struct dp_tx_me_buf_t *)
- ((char *)p + pdev->me_buf.size);
- p = p->next;
- }
- p->next = NULL;
- qdf_spin_unlock_bh(&pdev->tx_mutex);
- QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO,
- "ME Pool successfully initialized vaddr - %x",
- pdev->me_buf.vaddr);
- QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO,
- "paddr - %x\n", (unsigned int)pdev->me_buf.paddr);
- QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO,
- "num_elems = %d", (unsigned int)num_pool_elems);
- QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO,
- "buf_size - %d", (unsigned int)pdev->me_buf.size);
- QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO,
- "pool_size = %d", (unsigned int)pool_size);
- } else {
- QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO,
- "ME Already Enabled!!");
- }
- return 0;
- }
- /**
- * dp_tx_me_alloc_descriptor():Allocate ME descriptor
- * @soc: DP SOC handle
- * @pdev_id: id of DP PDEV handle
- *
- * Return:void
- */
- void dp_tx_me_alloc_descriptor(struct cdp_soc_t *soc, uint8_t pdev_id)
- {
- struct dp_pdev *pdev =
- dp_get_pdev_from_soc_pdev_id_wifi3((struct dp_soc *)soc,
- pdev_id);
- if (!pdev)
- return;
- if (qdf_atomic_read(&pdev->mc_num_vap_attached) == 0) {
- dp_tx_me_init(pdev);
- QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO,
- FL("Enable MCAST_TO_UCAST "));
- }
- qdf_atomic_inc(&pdev->mc_num_vap_attached);
- }
- /**
- * dp_tx_me_exit():Free memory and other cleanup required for
- * multicast unicast conversion
- * @pdev - DP_PDEV handle
- *
- * Return:void
- */
- void
- dp_tx_me_exit(struct dp_pdev *pdev)
- {
- /* Add flow control buffer count */
- uint32_t wait_time = ME_US_TO_SEC(ME_CLEAN_WAIT_TIMEOUT *
- ME_CLEAN_WAIT_COUNT);
- if (pdev->me_buf.vaddr) {
- uint16_t wait_cnt = 0;
- QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO,
- "Disabling Mcastenhance,This may take some time");
- qdf_spin_lock_bh(&pdev->tx_mutex);
- while ((pdev->me_buf.buf_in_use > 0) &&
- (wait_cnt < ME_CLEAN_WAIT_COUNT)) {
- qdf_spin_unlock_bh(&pdev->tx_mutex);
- OS_SLEEP(ME_CLEAN_WAIT_TIMEOUT);
- wait_cnt++;
- qdf_spin_lock_bh(&pdev->tx_mutex);
- }
- if (pdev->me_buf.buf_in_use > 0) {
- QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_FATAL,
- "Tx-comp pending for %d",
- pdev->me_buf.buf_in_use);
- QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_FATAL,
- "ME frames after waiting %ds!!",
- wait_time);
- qdf_assert_always(0);
- }
- qdf_mem_free(pdev->me_buf.vaddr);
- pdev->me_buf.vaddr = NULL;
- pdev->me_buf.freelist = NULL;
- qdf_spin_unlock_bh(&pdev->tx_mutex);
- } else {
- QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO,
- "ME Already Disabled !!!");
- }
- }
- /**
- * dp_tx_me_free_descriptor():free ME descriptor
- * @soc: DP SOC handle
- * @pdev_id: id of DP PDEV handle
- *
- * Return:void
- */
- void
- dp_tx_me_free_descriptor(struct cdp_soc_t *soc, uint8_t pdev_id)
- {
- struct dp_pdev *pdev =
- dp_get_pdev_from_soc_pdev_id_wifi3((struct dp_soc *)soc,
- pdev_id);
- if (!pdev)
- return;
- if (atomic_read(&pdev->mc_num_vap_attached)) {
- if (qdf_atomic_dec_and_test(&pdev->mc_num_vap_attached)) {
- dp_tx_me_exit(pdev);
- QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO,
- "Disable MCAST_TO_UCAST");
- }
- }
- }
- /**
- * dp_tx_prepare_send_me(): Call to the umac to get the list of clients
- * @vdev: DP VDEV handle
- * @nbuf: Multicast buffer
- *
- * Return: no of packets transmitted
- */
- QDF_STATUS
- dp_tx_prepare_send_me(struct dp_vdev *vdev, qdf_nbuf_t nbuf)
- {
- if (dp_me_mcast_convert((struct cdp_soc_t *)(vdev->pdev->soc),
- vdev->vdev_id, vdev->pdev->pdev_id,
- nbuf) > 0)
- return QDF_STATUS_SUCCESS;
- return QDF_STATUS_E_FAILURE;
- }
- /*
- * dp_tx_me_mem_free(): Function to free allocated memory in mcast enahncement
- * pdev: pointer to DP PDEV structure
- * seg_info_head: Pointer to the head of list
- *
- * return: void
- */
- static void dp_tx_me_mem_free(struct dp_pdev *pdev,
- struct dp_tx_seg_info_s *seg_info_head)
- {
- struct dp_tx_me_buf_t *mc_uc_buf;
- struct dp_tx_seg_info_s *seg_info_new = NULL;
- qdf_nbuf_t nbuf = NULL;
- uint64_t phy_addr;
- while (seg_info_head) {
- nbuf = seg_info_head->nbuf;
- mc_uc_buf = (struct dp_tx_me_buf_t *)
- seg_info_head->frags[0].vaddr;
- phy_addr = seg_info_head->frags[0].paddr_hi;
- phy_addr = (phy_addr << 32) | seg_info_head->frags[0].paddr_lo;
- qdf_mem_unmap_nbytes_single(pdev->soc->osdev,
- phy_addr,
- QDF_DMA_TO_DEVICE, QDF_MAC_ADDR_SIZE);
- dp_tx_me_free_buf(pdev, mc_uc_buf);
- qdf_nbuf_free(nbuf);
- seg_info_new = seg_info_head;
- seg_info_head = seg_info_head->next;
- qdf_mem_free(seg_info_new);
- }
- }
- /**
- * dp_tx_me_send_convert_ucast(): function to convert multicast to unicast
- * @soc: Datapath soc handle
- * @vdev_id: vdev id
- * @nbuf: Multicast nbuf
- * @newmac: Table of the clients to which packets have to be sent
- * @new_mac_cnt: No of clients
- *
- * return: no of converted packets
- */
- uint16_t
- dp_tx_me_send_convert_ucast(struct cdp_soc_t *soc, uint8_t vdev_id,
- qdf_nbuf_t nbuf,
- uint8_t newmac[][QDF_MAC_ADDR_SIZE],
- uint8_t new_mac_cnt)
- {
- struct dp_pdev *pdev;
- qdf_ether_header_t *eh;
- uint8_t *data;
- uint16_t len;
- /* reference to frame dst addr */
- uint8_t *dstmac;
- /* copy of original frame src addr */
- uint8_t srcmac[QDF_MAC_ADDR_SIZE];
- /* local index into newmac */
- uint8_t new_mac_idx = 0;
- struct dp_tx_me_buf_t *mc_uc_buf;
- qdf_nbuf_t nbuf_clone;
- struct dp_tx_msdu_info_s msdu_info;
- struct dp_tx_seg_info_s *seg_info_head = NULL;
- struct dp_tx_seg_info_s *seg_info_tail = NULL;
- struct dp_tx_seg_info_s *seg_info_new;
- qdf_dma_addr_t paddr_data;
- qdf_dma_addr_t paddr_mcbuf = 0;
- uint8_t empty_entry_mac[QDF_MAC_ADDR_SIZE] = {0};
- QDF_STATUS status;
- struct dp_vdev *vdev =
- dp_get_vdev_from_soc_vdev_id_wifi3((struct dp_soc *)soc,
- vdev_id);
- if (!vdev) {
- qdf_nbuf_free(nbuf);
- return 1;
- }
- pdev = vdev->pdev;
- if (!pdev) {
- qdf_nbuf_free(nbuf);
- return 1;
- }
- vdev = dp_get_vdev_from_soc_vdev_id_wifi3((struct dp_soc *)soc,
- vdev_id);
- if (!vdev)
- return 1;
- pdev = vdev->pdev;
- if (!pdev)
- return 1;
- qdf_mem_zero(&msdu_info, sizeof(msdu_info));
- dp_tx_get_queue(vdev, nbuf, &msdu_info.tx_queue);
- eh = (qdf_ether_header_t *)nbuf;
- qdf_mem_copy(srcmac, eh->ether_shost, QDF_MAC_ADDR_SIZE);
- len = qdf_nbuf_len(nbuf);
- data = qdf_nbuf_data(nbuf);
- status = qdf_nbuf_map(vdev->osdev, nbuf,
- QDF_DMA_TO_DEVICE);
- if (status) {
- QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
- "Mapping failure Error:%d", status);
- DP_STATS_INC(vdev, tx_i.mcast_en.dropped_map_error, 1);
- qdf_nbuf_free(nbuf);
- return 1;
- }
- paddr_data = qdf_nbuf_mapped_paddr_get(nbuf) + QDF_MAC_ADDR_SIZE;
- for (new_mac_idx = 0; new_mac_idx < new_mac_cnt; new_mac_idx++) {
- dstmac = newmac[new_mac_idx];
- QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO,
- "added mac addr (%pM)", dstmac);
- /* Check for NULL Mac Address */
- if (!qdf_mem_cmp(dstmac, empty_entry_mac, QDF_MAC_ADDR_SIZE))
- continue;
- /* frame to self mac. skip */
- if (!qdf_mem_cmp(dstmac, srcmac, QDF_MAC_ADDR_SIZE))
- continue;
- /*
- * optimize to avoid malloc in per-packet path
- * For eg. seg_pool can be made part of vdev structure
- */
- seg_info_new = qdf_mem_malloc(sizeof(*seg_info_new));
- if (!seg_info_new) {
- QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
- "alloc failed");
- DP_STATS_INC(vdev, tx_i.mcast_en.fail_seg_alloc, 1);
- goto fail_seg_alloc;
- }
- mc_uc_buf = dp_tx_me_alloc_buf(pdev);
- if (!mc_uc_buf)
- goto fail_buf_alloc;
- /*
- * Check if we need to clone the nbuf
- * Or can we just use the reference for all cases
- */
- if (new_mac_idx < (new_mac_cnt - 1)) {
- nbuf_clone = qdf_nbuf_clone((qdf_nbuf_t)nbuf);
- if (!nbuf_clone) {
- DP_STATS_INC(vdev, tx_i.mcast_en.clone_fail, 1);
- goto fail_clone;
- }
- } else {
- /*
- * Update the ref
- * to account for frame sent without cloning
- */
- qdf_nbuf_ref(nbuf);
- nbuf_clone = nbuf;
- }
- qdf_mem_copy(mc_uc_buf->data, dstmac, QDF_MAC_ADDR_SIZE);
- status = qdf_mem_map_nbytes_single(vdev->osdev, mc_uc_buf->data,
- QDF_DMA_TO_DEVICE, QDF_MAC_ADDR_SIZE,
- &paddr_mcbuf);
- if (status) {
- QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
- "Mapping failure Error:%d", status);
- DP_STATS_INC(vdev, tx_i.mcast_en.dropped_map_error, 1);
- goto fail_map;
- }
- seg_info_new->frags[0].vaddr = (uint8_t *)mc_uc_buf;
- seg_info_new->frags[0].paddr_lo = (uint32_t) paddr_mcbuf;
- seg_info_new->frags[0].paddr_hi =
- (uint16_t)((uint64_t)paddr_mcbuf >> 32);
- seg_info_new->frags[0].len = QDF_MAC_ADDR_SIZE;
- /*preparing data fragment*/
- seg_info_new->frags[1].vaddr =
- qdf_nbuf_data(nbuf) + QDF_MAC_ADDR_SIZE;
- seg_info_new->frags[1].paddr_lo = (uint32_t)paddr_data;
- seg_info_new->frags[1].paddr_hi =
- (uint16_t)(((uint64_t)paddr_data) >> 32);
- seg_info_new->frags[1].len = len - QDF_MAC_ADDR_SIZE;
- seg_info_new->nbuf = nbuf_clone;
- seg_info_new->frag_cnt = 2;
- seg_info_new->total_len = len;
- seg_info_new->next = NULL;
- if (!seg_info_head)
- seg_info_head = seg_info_new;
- else
- seg_info_tail->next = seg_info_new;
- seg_info_tail = seg_info_new;
- }
- if (!seg_info_head) {
- goto free_return;
- }
- msdu_info.u.sg_info.curr_seg = seg_info_head;
- msdu_info.num_seg = new_mac_cnt;
- msdu_info.frm_type = dp_tx_frm_me;
- msdu_info.tid = HTT_INVALID_TID;
- if (qdf_unlikely(vdev->mcast_enhancement_en > 0) &&
- qdf_unlikely(pdev->hmmc_tid_override_en))
- msdu_info.tid = pdev->hmmc_tid;
- DP_STATS_INC(vdev, tx_i.mcast_en.ucast, new_mac_cnt);
- dp_tx_send_msdu_multiple(vdev, nbuf, &msdu_info);
- while (seg_info_head->next) {
- seg_info_new = seg_info_head;
- seg_info_head = seg_info_head->next;
- qdf_mem_free(seg_info_new);
- }
- qdf_mem_free(seg_info_head);
- qdf_nbuf_unmap(pdev->soc->osdev, nbuf, QDF_DMA_TO_DEVICE);
- qdf_nbuf_free(nbuf);
- return new_mac_cnt;
- fail_map:
- qdf_nbuf_free(nbuf_clone);
- fail_clone:
- dp_tx_me_free_buf(pdev, mc_uc_buf);
- fail_buf_alloc:
- qdf_mem_free(seg_info_new);
- fail_seg_alloc:
- dp_tx_me_mem_free(pdev, seg_info_head);
- free_return:
- qdf_nbuf_unmap(pdev->soc->osdev, nbuf, QDF_DMA_TO_DEVICE);
- qdf_nbuf_free(nbuf);
- return 1;
- }
|