diff --git a/Kbuild b/Kbuild index a6d732064807..02a1718ce3c7 100644 --- a/Kbuild +++ b/Kbuild @@ -119,6 +119,7 @@ ifeq ($(KERNEL_BUILD), 0) endif ifeq ($(CONFIG_ARCH_MSM8998), y) CONFIG_QCACLD_FEATURE_GREEN_AP := y + CONFIG_QCACLD_FEATURE_METERING := y endif ifeq ($(CONFIG_ARCH_SDM660), y) @@ -1903,6 +1904,11 @@ ifeq ($(CONFIG_QCACLD_FEATURE_GREEN_AP),y) CDEFINES += -DFEATURE_GREEN_AP endif +#Stats & Quota Metering feature +ifeq ($(CONFIG_QCACLD_FEATURE_METERING),y) +CDEFINES += -DFEATURE_METERING +endif + #Enable RX Full re-order OL feature only "LL and NON-MDM9630 platform" ifneq ($(CONFIG_ARCH_MDM9630), y) ifeq ($(CONFIG_HIF_PCI), 1) diff --git a/core/dp/htt/htt_h2t.c b/core/dp/htt/htt_h2t.c index a25fca295fd2..0a770f12898d 100644 --- a/core/dp/htt/htt_h2t.c +++ b/core/dp/htt/htt_h2t.c @@ -1330,4 +1330,143 @@ int htt_h2t_ipa_uc_get_stats(struct htt_pdev_t *pdev) HTT_SEND_HTC_PKT(pdev, pkt); return A_OK; } + +/** + * htt_h2t_ipa_uc_get_share_stats() - WDI UC wifi sharing state request to FW + * @pdev: handle to the HTT instance + * + * Return: A_OK success + * A_NO_MEMORY No memory fail + */ +int htt_h2t_ipa_uc_get_share_stats(struct htt_pdev_t *pdev, uint8_t reset_stats) +{ + struct htt_htc_pkt *pkt; + qdf_nbuf_t msg; + uint32_t *msg_word; + + pkt = htt_htc_pkt_alloc(pdev); + if (!pkt) + return -A_NO_MEMORY; + + /* show that this is not a tx frame download + * (not required, but helpful) + */ + pkt->msdu_id = HTT_TX_COMPL_INV_MSDU_ID; + pkt->pdev_ctxt = NULL; /* not used during send-done callback */ + + /* reserve room for HTC header */ + msg = qdf_nbuf_alloc(pdev->osdev, + HTT_MSG_BUF_SIZE(HTT_WDI_IPA_OP_REQUEST_SZ)+ + HTT_MSG_BUF_SIZE(HTT_WDI_IPA_OP_REQ_GET_SHARING_STATS_SZ), + HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING, 4, false); + if (!msg) { + htt_htc_pkt_free(pdev, pkt); + return -A_NO_MEMORY; + } + /* set the length of the message */ + qdf_nbuf_put_tail(msg, HTT_WDI_IPA_OP_REQUEST_SZ+ + HTT_WDI_IPA_OP_REQ_GET_SHARING_STATS_SZ); + + /* fill in the message contents */ + msg_word = (uint32_t *) qdf_nbuf_data(msg); + + /* rewind beyond alignment pad to get to the HTC header reserved area */ + qdf_nbuf_push_head(msg, HTC_HDR_ALIGNMENT_PADDING); + + *msg_word = 0; + HTT_WDI_IPA_OP_REQUEST_OP_CODE_SET(*msg_word, + HTT_WDI_IPA_OPCODE_GET_SHARING_STATS); + HTT_H2T_MSG_TYPE_SET(*msg_word, HTT_H2T_MSG_TYPE_WDI_IPA_OP_REQ); + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_OP_REQ_GET_SHARING_STATS_RESET_STATS_SET(*msg_word, + reset_stats); + + SET_HTC_PACKET_INFO_TX(&pkt->htc_pkt, + htt_h2t_send_complete_free_netbuf, + qdf_nbuf_data(msg), + qdf_nbuf_len(msg), + pdev->htc_tx_endpoint, + 1); /* tag - not relevant here */ + + SET_HTC_PACKET_NET_BUF_CONTEXT(&pkt->htc_pkt, msg); + HTT_SEND_HTC_PKT(pdev, pkt); + return A_OK; +} + +/** + * htt_h2t_ipa_uc_set_quota() - WDI UC state query request to firmware + * @pdev: handle to the HTT instance + * + * Return: A_OK success + * A_NO_MEMORY No memory fail + */ +int htt_h2t_ipa_uc_set_quota(struct htt_pdev_t *pdev, uint64_t quota_bytes) +{ + struct htt_htc_pkt *pkt; + qdf_nbuf_t msg; + uint32_t *msg_word; + + pkt = htt_htc_pkt_alloc(pdev); + if (!pkt) + return -A_NO_MEMORY; + + /* show that this is not a tx frame download + * (not required, but helpful) + */ + pkt->msdu_id = HTT_TX_COMPL_INV_MSDU_ID; + pkt->pdev_ctxt = NULL; /* not used during send-done callback */ + + /* reserve room for HTC header */ + msg = qdf_nbuf_alloc(pdev->osdev, + HTT_MSG_BUF_SIZE(HTT_WDI_IPA_OP_REQUEST_SZ)+ + HTT_MSG_BUF_SIZE(HTT_WDI_IPA_OP_REQ_SET_QUOTA_SZ), + HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING, 4, false); + if (!msg) { + htt_htc_pkt_free(pdev, pkt); + return -A_NO_MEMORY; + } + /* set the length of the message */ + qdf_nbuf_put_tail(msg, HTT_WDI_IPA_OP_REQUEST_SZ+ + HTT_WDI_IPA_OP_REQ_SET_QUOTA_SZ); + + /* fill in the message contents */ + msg_word = (uint32_t *) qdf_nbuf_data(msg); + + /* rewind beyond alignment pad to get to the HTC header reserved area */ + qdf_nbuf_push_head(msg, HTC_HDR_ALIGNMENT_PADDING); + + *msg_word = 0; + HTT_WDI_IPA_OP_REQUEST_OP_CODE_SET(*msg_word, + HTT_WDI_IPA_OPCODE_SET_QUOTA); + HTT_H2T_MSG_TYPE_SET(*msg_word, HTT_H2T_MSG_TYPE_WDI_IPA_OP_REQ); + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_OP_REQ_SET_QUOTA_SET_QUOTA_SET(*msg_word, quota_bytes > 0); + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_OP_REQ_SET_QUOTA_QUOTA_LO_SET(*msg_word, + (uint32_t)(quota_bytes & + HTT_WDI_IPA_OP_REQ_SET_QUOTA_QUOTA_LO_M)); + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_OP_REQ_SET_QUOTA_QUOTA_HI_SET(*msg_word, + (uint32_t)(quota_bytes>>32 & + HTT_WDI_IPA_OP_REQ_SET_QUOTA_QUOTA_HI_M)); + + SET_HTC_PACKET_INFO_TX(&pkt->htc_pkt, + htt_h2t_send_complete_free_netbuf, + qdf_nbuf_data(msg), + qdf_nbuf_len(msg), + pdev->htc_tx_endpoint, + 1); /* tag - not relevant here */ + + SET_HTC_PACKET_NET_BUF_CONTEXT(&pkt->htc_pkt, msg); + HTT_SEND_HTC_PKT(pdev, pkt); + return A_OK; +} #endif /* IPA_OFFLOAD */ diff --git a/core/dp/ol/inc/ol_htt_api.h b/core/dp/ol/inc/ol_htt_api.h index c19827e9aa2c..ba4c67db110c 100644 --- a/core/dp/ol/inc/ol_htt_api.h +++ b/core/dp/ol/inc/ol_htt_api.h @@ -243,6 +243,11 @@ htt_h2t_ipa_uc_set_active(struct htt_pdev_t *pdev, bool uc_active, bool is_tx); int htt_h2t_ipa_uc_get_stats(struct htt_pdev_t *pdev); +int htt_h2t_ipa_uc_get_share_stats(struct htt_pdev_t *pdev, + uint8_t reset_stats); + +int htt_h2t_ipa_uc_set_quota(struct htt_pdev_t *pdev, uint64_t quota_bytes); + int htt_ipa_uc_attach(struct htt_pdev_t *pdev); void htt_ipa_uc_detach(struct htt_pdev_t *pdev); @@ -340,6 +345,30 @@ static inline int htt_h2t_ipa_uc_get_stats(struct htt_pdev_t *pdev) return 0; } +/** + * htt_h2t_ipa_uc_get_share_stats() - WDI UC wifi sharing state request to FW + * @pdev: handle to the HTT instance + * + * Return: 0 success + */ +static inline int htt_h2t_ipa_uc_get_share_stats(struct htt_pdev_t *pdev, + uint8_t reset_stats) +{ + return 0; +} + +/** + * htt_h2t_ipa_uc_set_quota() - WDI UC set quota request to firmware + * @pdev: handle to the HTT instance + * + * Return: 0 success + */ +static inline int htt_h2t_ipa_uc_set_quota(struct htt_pdev_t *pdev, + uint64_t quota_bytes) +{ + return 0; +} + /** * htt_ipa_uc_attach() - Allocate UC data path resources * @pdev: handle to the HTT instance diff --git a/core/dp/txrx/ol_txrx.c b/core/dp/txrx/ol_txrx.c index dc7632e3a24e..f5e2e2c40f0a 100644 --- a/core/dp/txrx/ol_txrx.c +++ b/core/dp/txrx/ol_txrx.c @@ -4368,6 +4368,20 @@ static void ol_txrx_ipa_uc_get_stat(struct cdp_pdev *ppdev) struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)ppdev; htt_h2t_ipa_uc_get_stats(pdev->htt_pdev); } + +static void ol_txrx_ipa_uc_get_share_stats(struct cdp_pdev *ppdev, + uint8_t reset_stats) +{ + struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)ppdev; + htt_h2t_ipa_uc_get_share_stats(pdev->htt_pdev, reset_stats); +} + +static void ol_txrx_ipa_uc_set_quota(struct cdp_pdev *ppdev, + uint64_t quota_bytes) +{ + struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)ppdev; + htt_h2t_ipa_uc_set_quota(pdev->htt_pdev, quota_bytes); +} #endif /* IPA_UC_OFFLOAD */ /* @@ -5357,7 +5371,9 @@ static struct cdp_ipa_ops ol_ops_ipa = { .ipa_register_op_cb = ol_txrx_ipa_uc_register_op_cb, .ipa_get_stat = ol_txrx_ipa_uc_get_stat, .ipa_tx_data_frame = ol_tx_send_ipa_data_frame, - .ipa_set_uc_tx_partition_base = ol_cfg_set_ipa_uc_tx_partition_base + .ipa_set_uc_tx_partition_base = ol_cfg_set_ipa_uc_tx_partition_base, + .ipa_uc_get_share_stats = ol_txrx_ipa_uc_get_share_stats, + .ipa_uc_set_quota = ol_txrx_ipa_uc_set_quota #endif /* IPA_OFFLOAD */ }; diff --git a/core/hdd/inc/wlan_hdd_ipa.h b/core/hdd/inc/wlan_hdd_ipa.h index 63250fb20b96..506669d0233d 100644 --- a/core/hdd/inc/wlan_hdd_ipa.h +++ b/core/hdd/inc/wlan_hdd_ipa.h @@ -98,6 +98,10 @@ void hdd_ipa_uc_stat_query(hdd_context_t *hdd_ctx, uint32_t *ipa_tx_diff, uint32_t *ipa_rx_diff); void hdd_ipa_uc_rt_debug_host_dump(hdd_context_t *hdd_ctx); void hdd_ipa_uc_stat_request(hdd_adapter_t *adapter, uint8_t reason); +void hdd_ipa_uc_sharing_stats_request(hdd_adapter_t *adapter, + uint8_t reset_stats); +void hdd_ipa_uc_set_quota(hdd_adapter_t *adapter, uint8_t set_quota, + uint64_t quota_bytes); bool hdd_ipa_is_enabled(hdd_context_t *pHddCtx); bool hdd_ipa_uc_is_enabled(hdd_context_t *pHddCtx); #ifndef QCA_LL_TX_FLOW_CONTROL_V2 diff --git a/core/hdd/src/wlan_hdd_ipa.c b/core/hdd/src/wlan_hdd_ipa.c index 902fbc34360c..48253791b8ae 100644 --- a/core/hdd/src/wlan_hdd_ipa.c +++ b/core/hdd/src/wlan_hdd_ipa.c @@ -97,6 +97,11 @@ enum hdd_ipa_uc_op_code { HDD_IPA_UC_OPCODE_RX_SUSPEND = 2, HDD_IPA_UC_OPCODE_RX_RESUME = 3, HDD_IPA_UC_OPCODE_STATS = 4, +#ifdef FEATURE_METERING + HDD_IPA_UC_OPCODE_SHARING_STATS = 5, + HDD_IPA_UC_OPCODE_QUOTA_RSP = 6, + HDD_IPA_UC_OPCODE_QUOTA_IND = 7, +#endif HDD_IPA_UC_OPCODE_UC_READY = 8, /* keep this last */ HDD_IPA_UC_OPCODE_MAX @@ -375,6 +380,30 @@ struct uc_rt_debug_info { uint64_t rx_destructor_call; }; +#ifdef FEATURE_METERING +struct ipa_uc_sharing_stats { + uint64_t ipv4_rx_packets; + uint64_t ipv4_rx_bytes; + uint64_t ipv6_rx_packets; + uint64_t ipv6_rx_bytes; + uint64_t ipv4_tx_packets; + uint64_t ipv4_tx_bytes; + uint64_t ipv6_tx_packets; + uint64_t ipv6_tx_bytes; +}; + +struct ipa_uc_quota_rsp { + uint8_t success; + uint8_t reserved[3]; + uint32_t quota_lo; /* quota limit low bytes */ + uint32_t quota_hi; /* quota limit high bytes */ +}; + +struct ipa_uc_quota_ind { + uint64_t quota_bytes; /* quota limit in bytes */ +}; +#endif + struct hdd_ipa_priv { struct hdd_ipa_sys_pipe sys_pipe[HDD_IPA_MAX_SYSBAM_PIPE]; struct hdd_ipa_iface_context iface_context[HDD_IPA_MAX_IFACE]; @@ -454,9 +483,15 @@ struct hdd_ipa_priv { /* IPA UC doorbell registers paddr */ qdf_dma_addr_t tx_comp_doorbell_paddr; qdf_dma_addr_t rx_ready_doorbell_paddr; - uint8_t vdev_to_iface[CSR_ROAM_SESSION_MAX]; bool vdev_offload_enabled[CSR_ROAM_SESSION_MAX]; +#ifdef FEATURE_METERING + struct ipa_uc_sharing_stats ipa_sharing_stats; + struct ipa_uc_quota_rsp ipa_quota_rsp; + struct ipa_uc_quota_ind ipa_quota_ind; + struct completion ipa_uc_sharing_stats_comp; + struct completion ipa_uc_set_quota_comp; +#endif }; /** @@ -596,6 +631,11 @@ static struct hdd_ipa_tx_hdr ipa_tx_hdr = { } }; +#ifdef FEATURE_METERING +#define IPA_UC_SHARING_STATES_WAIT_TIME 500 +#define IPA_UC_SET_QUOTA_WAIT_TIME 500 +#endif + static struct hdd_ipa_priv *ghdd_ipa; /* Local Function Prototypes */ @@ -802,20 +842,13 @@ static inline bool hdd_ipa_uc_sta_is_enabled(hdd_context_t *hdd_ctx) * * Return: None */ -#ifdef IPA_UC_STA_OFFLOAD static inline void hdd_ipa_uc_sta_reset_sta_connected( struct hdd_ipa_priv *hdd_ipa) { - vos_lock_acquire(&hdd_ipa->event_lock); + qdf_mutex_acquire(&hdd_ipa->ipa_lock); hdd_ipa->sta_connected = 0; - vos_lock_release(&hdd_ipa->event_lock); + qdf_mutex_release(&hdd_ipa->ipa_lock); } -#else -static inline void hdd_ipa_uc_sta_reset_sta_connected( - struct hdd_ipa_priv *hdd_ipa) -{ -} -#endif /** * hdd_ipa_is_pre_filter_enabled() - Is IPA pre-filter enabled? @@ -1019,7 +1052,8 @@ static void hdd_ipa_uc_rt_debug_handler(void *ctext) if (!dummy_ptr) { hdd_ipa_uc_rt_debug_host_dump(hdd_ctx); hdd_ipa_uc_stat_request( - hdd_get_adapter(hdd_ctx, QDF_SAP_MODE), 1); + hdd_get_adapter(hdd_ctx, QDF_SAP_MODE), + HDD_IPA_UC_STAT_REASON_DEBUG); } else { kfree(dummy_ptr); } @@ -1735,9 +1769,8 @@ static void __hdd_ipa_uc_stat_request(hdd_adapter_t *adapter, uint8_t reason) hdd_context_t *hdd_ctx; struct hdd_ipa_priv *hdd_ipa; - if (!adapter) { + if (!adapter) return; - } hdd_ctx = (hdd_context_t *)adapter->pHddCtx; @@ -1755,12 +1788,14 @@ static void __hdd_ipa_uc_stat_request(hdd_adapter_t *adapter, uint8_t reason) if ((HDD_IPA_UC_NUM_WDI_PIPE == hdd_ipa->activated_fw_pipe) && (false == hdd_ipa->resource_loading)) { hdd_ipa->stat_req_reason = reason; + qdf_mutex_release(&hdd_ipa->ipa_lock); wma_cli_set_command( (int)adapter->sessionId, (int)WMA_VDEV_TXRX_GET_IPA_UC_FW_STATS_CMDID, 0, VDEV_CMD); + } else { + qdf_mutex_release(&hdd_ipa->ipa_lock); } - qdf_mutex_release(&hdd_ipa->ipa_lock); } /** @@ -1777,6 +1812,85 @@ void hdd_ipa_uc_stat_request(hdd_adapter_t *adapter, uint8_t reason) cds_ssr_unprotect(__func__); } +#ifdef FEATURE_METERING +/** + * hdd_ipa_uc_sharing_stats_request() - Get IPA stats from IPA. + * @adapter: network adapter + * @reset_stats: reset stat countis after response + * + * Return: None + */ +void hdd_ipa_uc_sharing_stats_request(hdd_adapter_t *adapter, + uint8_t reset_stats) +{ + hdd_context_t *pHddCtx; + struct hdd_ipa_priv *hdd_ipa; + + if (!adapter) + return; + + pHddCtx = adapter->pHddCtx; + hdd_ipa = pHddCtx->hdd_ipa; + if (!hdd_ipa_is_enabled(pHddCtx) || + !(hdd_ipa_uc_is_enabled(pHddCtx))) { + return; + } + + HDD_IPA_LOG(LOG1, "SHARING_STATS: reset_stats=%d", reset_stats); + qdf_mutex_acquire(&hdd_ipa->ipa_lock); + if ((HDD_IPA_UC_NUM_WDI_PIPE == hdd_ipa->activated_fw_pipe) && + (false == hdd_ipa->resource_loading)) { + qdf_mutex_release(&hdd_ipa->ipa_lock); + wma_cli_set_command( + (int)adapter->sessionId, + (int)WMA_VDEV_TXRX_GET_IPA_UC_SHARING_STATS_CMDID, + reset_stats, VDEV_CMD); + } else { + qdf_mutex_release(&hdd_ipa->ipa_lock); + } +} + +/** + * hdd_ipa_uc_set_quota() - Set quota limit bytes from IPA. + * @adapter: network adapter + * @set_quota: when 1, FW starts quota monitoring + * @quota_bytes: quota limit in bytes + * + * Return: None + */ +void hdd_ipa_uc_set_quota(hdd_adapter_t *adapter, uint8_t set_quota, + uint64_t quota_bytes) +{ + hdd_context_t *pHddCtx; + struct hdd_ipa_priv *hdd_ipa; + + if (!adapter) + return; + + pHddCtx = adapter->pHddCtx; + hdd_ipa = pHddCtx->hdd_ipa; + if (!hdd_ipa_is_enabled(pHddCtx) || + !(hdd_ipa_uc_is_enabled(pHddCtx))) { + return; + } + + HDD_IPA_LOG(LOG1, "SET_QUOTA: set_quota=%d, quota_bytes=%llu", + set_quota, quota_bytes); + + qdf_mutex_acquire(&hdd_ipa->ipa_lock); + if ((HDD_IPA_UC_NUM_WDI_PIPE == hdd_ipa->activated_fw_pipe) && + (false == hdd_ipa->resource_loading)) { + qdf_mutex_release(&hdd_ipa->ipa_lock); + wma_cli_set_command( + (int)adapter->sessionId, + (int)WMA_VDEV_TXRX_SET_IPA_UC_QUOTA_CMDID, + (set_quota ? quota_bytes : 0), VDEV_CMD); + } else { + qdf_mutex_release(&hdd_ipa->ipa_lock); + } +} +#endif + /** * hdd_ipa_uc_find_add_assoc_sta() - Find associated station * @hdd_ipa: Global HDD IPA context @@ -2139,6 +2253,97 @@ static void hdd_ipa_uc_loaded_handler(struct hdd_ipa_priv *ipa_ctxt) } } +/** + * hdd_ipa_uc_op_metering() - IPA uC operation for stats and quota limit + * @hdd_ctx: Global HDD context + * @op_msg: operation message received from firmware + * + * Return: QDF_STATUS enumeration + */ +#ifdef FEATURE_METERING +static QDF_STATUS hdd_ipa_uc_op_metering(hdd_context_t *hdd_ctx, + struct op_msg_type *op_msg) +{ + struct op_msg_type *msg = op_msg; + struct ipa_uc_sharing_stats *uc_sharing_stats; + struct ipa_uc_quota_rsp *uc_quota_rsp; + struct ipa_uc_quota_ind *uc_quota_ind; + struct hdd_ipa_priv *hdd_ipa; + hdd_adapter_t *adapter; + + hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa; + + if (HDD_IPA_UC_OPCODE_SHARING_STATS == msg->op_code) { + /* fill-up ipa_uc_sharing_stats structure from FW */ + uc_sharing_stats = (struct ipa_uc_sharing_stats *) + ((uint8_t *)op_msg + sizeof(struct op_msg_type)); + + memcpy(&(hdd_ipa->ipa_sharing_stats), uc_sharing_stats, + sizeof(struct ipa_uc_sharing_stats)); + + complete(&hdd_ipa->ipa_uc_sharing_stats_comp); + + HDD_IPA_DP_LOG(QDF_TRACE_LEVEL_DEBUG, + "%s: %llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu", + "HDD_IPA_UC_OPCODE_SHARING_STATS", + hdd_ipa->ipa_sharing_stats.ipv4_rx_packets, + hdd_ipa->ipa_sharing_stats.ipv4_rx_bytes, + hdd_ipa->ipa_sharing_stats.ipv6_rx_packets, + hdd_ipa->ipa_sharing_stats.ipv6_rx_bytes, + hdd_ipa->ipa_sharing_stats.ipv4_tx_packets, + hdd_ipa->ipa_sharing_stats.ipv4_tx_bytes, + hdd_ipa->ipa_sharing_stats.ipv6_tx_packets, + hdd_ipa->ipa_sharing_stats.ipv6_tx_bytes); + } else if (HDD_IPA_UC_OPCODE_QUOTA_RSP == msg->op_code) { + /* received set quota response */ + uc_quota_rsp = (struct ipa_uc_quota_rsp *) + ((uint8_t *)op_msg + sizeof(struct op_msg_type)); + + memcpy(&(hdd_ipa->ipa_quota_rsp), uc_quota_rsp, + sizeof(struct ipa_uc_quota_rsp)); + + complete(&hdd_ipa->ipa_uc_set_quota_comp); + HDD_IPA_DP_LOG(QDF_TRACE_LEVEL_DEBUG, + "%s: success=%d, quota_bytes=%llu", + "HDD_IPA_UC_OPCODE_QUOTA_RSP", + hdd_ipa->ipa_quota_rsp.success, + ((uint64_t)(hdd_ipa->ipa_quota_rsp.quota_hi)<<32)| + hdd_ipa->ipa_quota_rsp.quota_lo); + } else if (HDD_IPA_UC_OPCODE_QUOTA_IND == msg->op_code) { + /* hit quota limit */ + uc_quota_ind = (struct ipa_uc_quota_ind *) + ((uint8_t *)op_msg + sizeof(struct op_msg_type)); + + hdd_ipa->ipa_quota_ind.quota_bytes = + uc_quota_ind->quota_bytes; + + /* send quota exceeded indication to IPA */ + HDD_IPA_DP_LOG(QDF_TRACE_LEVEL_DEBUG, + "OPCODE_QUOTA_IND: quota exceed! (quota_bytes=%llu)", + hdd_ipa->ipa_quota_ind.quota_bytes); + + adapter = hdd_get_adapter(hdd_ipa->hdd_ctx, QDF_STA_MODE); + if (adapter) + ipa_broadcast_wdi_quota_reach_ind( + adapter->dev->ifindex, + uc_quota_ind->quota_bytes); + else + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "Failed quota_reach_ind: NULL adapter"); + } else { + return QDF_STATUS_E_INVAL; + } + + return QDF_STATUS_SUCCESS; +} +#else +static QDF_STATUS hdd_ipa_uc_op_metering(hdd_context_t *hdd_ctx, + struct op_msg_type *op_msg) +{ + return QDF_STATUS_E_INVAL; +} +#endif + /** * hdd_ipa_uc_op_cb() - IPA uC operation callback * @op_msg: operation message received from firmware @@ -2426,9 +2631,9 @@ static void hdd_ipa_uc_op_cb(struct op_msg_type *op_msg, void *usr_ctxt) qdf_mutex_acquire(&hdd_ipa->ipa_lock); hdd_ipa_uc_loaded_handler(hdd_ipa); qdf_mutex_release(&hdd_ipa->ipa_lock); - } else { - HDD_IPA_LOG(LOGE, "INVALID REASON %d", - hdd_ipa->stat_req_reason); + } else if (hdd_ipa_uc_op_metering(hdd_ctx, op_msg)) { + HDD_IPA_LOG(LOGE, "Invalid message: op_code=%d, reason=%d", + msg->op_code, hdd_ipa->stat_req_reason); } qdf_mem_free(op_msg); } @@ -2583,11 +2788,165 @@ end: * Return: none */ static void hdd_ipa_init_uc_op_work(struct work_struct *work, - work_func_t work_handler) + work_func_t work_handler) { INIT_WORK(work, work_handler); } +#ifdef FEATURE_METERING +/** + * __hdd_ipa_wdi_meter_notifier_cb() - WLAN to IPA callback handler. + * IPA calls to get WLAN stats or set quota limit. + * @priv: pointer to private data registered with IPA (we register a + *» pointer to the global IPA context) + * @evt: the IPA event which triggered the callback + * @data: data associated with the event + * + * Return: None + */ +static void __hdd_ipa_wdi_meter_notifier_cb(enum ipa_wdi_meter_evt_type evt, + void *data) +{ + struct hdd_ipa_priv *hdd_ipa = ghdd_ipa; + hdd_adapter_t *adapter = NULL; + struct ipa_get_wdi_sap_stats *wdi_sap_stats; + struct ipa_set_wifi_quota *ipa_set_quota; + int ret = 0; + + if (wlan_hdd_validate_context(hdd_ipa->hdd_ctx)) + return; + + adapter = hdd_get_adapter(hdd_ipa->hdd_ctx, QDF_STA_MODE); + + HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "event=%d", evt); + + switch (evt) { + case IPA_GET_WDI_SAP_STATS: + /* fill-up ipa_get_wdi_sap_stats structure after getting + ipa_uc_fw_stats from FW */ + wdi_sap_stats = data; + + if (!adapter) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "IPA uC share stats failed - no adapter"); + wdi_sap_stats->stats_valid = 0; + return; + } + + INIT_COMPLETION(hdd_ipa->ipa_uc_sharing_stats_comp); + INIT_COMPLETION(hdd_ipa->ipa_uc_set_quota_comp); + hdd_ipa_uc_sharing_stats_request(adapter, + wdi_sap_stats->reset_stats); + ret = wait_for_completion_timeout( + &hdd_ipa->ipa_uc_sharing_stats_comp, + msecs_to_jiffies(IPA_UC_SHARING_STATES_WAIT_TIME)); + if (!ret) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "IPA uC share stats request timed out"); + wdi_sap_stats->stats_valid = 0; + } else { + wdi_sap_stats->stats_valid = 1; + + wdi_sap_stats->ipv4_rx_packets = + hdd_ipa->ipa_sharing_stats.ipv4_rx_packets; + wdi_sap_stats->ipv4_rx_bytes = + hdd_ipa->ipa_sharing_stats.ipv4_rx_bytes; + wdi_sap_stats->ipv6_rx_packets = + hdd_ipa->ipa_sharing_stats.ipv6_rx_packets; + wdi_sap_stats->ipv6_rx_bytes = + hdd_ipa->ipa_sharing_stats.ipv6_rx_bytes; + wdi_sap_stats->ipv4_tx_packets = + hdd_ipa->ipa_sharing_stats.ipv4_tx_packets; + wdi_sap_stats->ipv4_tx_bytes = + hdd_ipa->ipa_sharing_stats.ipv4_tx_bytes; + wdi_sap_stats->ipv6_tx_packets = + hdd_ipa->ipa_sharing_stats.ipv6_tx_packets; + wdi_sap_stats->ipv6_tx_bytes = + hdd_ipa->ipa_sharing_stats.ipv6_tx_bytes; + HDD_IPA_DP_LOG(QDF_TRACE_LEVEL_DEBUG, + "%s:%d,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu", + "IPA_GET_WDI_SAP_STATS", + wdi_sap_stats->stats_valid, + wdi_sap_stats->ipv4_rx_packets, + wdi_sap_stats->ipv4_rx_bytes, + wdi_sap_stats->ipv6_rx_packets, + wdi_sap_stats->ipv6_rx_bytes, + wdi_sap_stats->ipv4_tx_packets, + wdi_sap_stats->ipv4_tx_bytes, + wdi_sap_stats->ipv6_tx_packets, + wdi_sap_stats->ipv6_tx_bytes); + } + break; + case IPA_SET_WIFI_QUOTA: + /* get ipa_set_wifi_quota structure from IPA and pass to FW + through quota_exceeded field in ipa_uc_fw_stats */ + ipa_set_quota = data; + + if (!adapter) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "IPA uC set quota failed - no adapter"); + ipa_set_quota->set_valid = 0; + return; + } + + hdd_ipa_uc_set_quota(adapter, ipa_set_quota->set_quota, + ipa_set_quota->quota_bytes); + + ret = wait_for_completion_timeout( + &hdd_ipa->ipa_uc_set_quota_comp, + msecs_to_jiffies(IPA_UC_SET_QUOTA_WAIT_TIME)); + if (!ret) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "IPA uC set quota request timed out"); + ipa_set_quota->set_valid = 0; + } else { + ipa_set_quota->quota_bytes = + ((uint64_t)(hdd_ipa->ipa_quota_rsp.quota_hi) + <<32)|hdd_ipa->ipa_quota_rsp.quota_lo; + ipa_set_quota->set_valid = + hdd_ipa->ipa_quota_rsp.success; + } + + HDD_IPA_DP_LOG(QDF_TRACE_LEVEL_DEBUG, "SET_QUOTA: %llu, %d", + ipa_set_quota->quota_bytes, + ipa_set_quota->set_valid); + break; + } +} + +/** + * hdd_ipa_wdi_meter_notifier_cb() - WLAN to IPA callback handler. + * IPA calls to get WLAN stats or set quota limit. + * @priv: pointer to private data registered with IPA (we register a + *» pointer to the global IPA context) + * @evt: the IPA event which triggered the callback + * @data: data associated with the event + * + * Return: None + */ +static void hdd_ipa_wdi_meter_notifier_cb(enum ipa_wdi_meter_evt_type evt, + void *data) +{ + cds_ssr_protect(__func__); + __hdd_ipa_wdi_meter_notifier_cb(evt, data); + cds_ssr_unprotect(__func__); +} + +static void hdd_ipa_init_metering(struct hdd_ipa_priv *ipa_ctxt, + struct ipa_wdi_in_params *pipe_in) +{ + pipe_in->wdi_notify = hdd_ipa_wdi_meter_notifier_cb; + + init_completion(&ipa_ctxt->ipa_uc_sharing_stats_comp); + init_completion(&ipa_ctxt->ipa_uc_set_quota_comp); +} +#else +static void hdd_ipa_init_metering(struct hdd_ipa_priv *ipa_ctxt, + struct ipa_wdi_in_params *pipe_in) +{ +} +#endif + /** * hdd_ipa_uc_ol_init() - Initialize IPA uC offload * @hdd_ctx: Global HDD context @@ -2682,25 +3041,32 @@ QDF_STATUS hdd_ipa_uc_ol_init(hdd_context_t *hdd_ctx) stat = QDF_STATUS_E_FAILURE; goto fail_return; } + /* Micro Controller Doorbell register */ HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, - "CONS DB pipe out 0x%x TX PIPE Handle 0x%x", - (unsigned int)pipe_out.uc_door_bell_pa, - ipa_ctxt->tx_pipe_handle); - + "CONS DB pipe out 0x%x TX PIPE Handle 0x%x", + (unsigned int)pipe_out.uc_door_bell_pa, + ipa_ctxt->tx_pipe_handle); ipa_ctxt->tx_comp_doorbell_paddr = pipe_out.uc_door_bell_pa; /* WLAN TX PIPE Handle */ ipa_ctxt->tx_pipe_handle = pipe_out.clnt_hdl; HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, - "TX : 0x%x, %d, 0x%x, 0x%x, %d, %d, 0x%x", - (unsigned int)pipe_in.u.dl.comp_ring_base_pa, - pipe_in.u.dl.comp_ring_size, - (unsigned int)pipe_in.u.dl.ce_ring_base_pa, - (unsigned int)pipe_in.u.dl.ce_door_bell_pa, - pipe_in.u.dl.ce_ring_size, - pipe_in.u.dl.num_tx_buffers, - (unsigned int)ipa_ctxt->tx_comp_doorbell_paddr); + "TX: %s 0x%x, %s %d, %s 0x%x, %s 0x%x, %s %d, %s %d, %s 0x%x", + "comp_ring_base_pa", + (unsigned int)pipe_in.u.dl.comp_ring_base_pa, + "comp_ring_size", + pipe_in.u.dl.comp_ring_size, + "ce_ring_base_pa", + (unsigned int)pipe_in.u.dl.ce_ring_base_pa, + "ce_door_bell_pa", + (unsigned int)pipe_in.u.dl.ce_door_bell_pa, + "ce_ring_size", + pipe_in.u.dl.ce_ring_size, + "num_tx_buffers", + pipe_in.u.dl.num_tx_buffers, + "tx_comp_doorbell_paddr", + (unsigned int)ipa_ctxt->tx_comp_doorbell_paddr); } /* RX PIPE */ @@ -2727,6 +3093,8 @@ QDF_STATUS hdd_ipa_uc_ol_init(hdd_context_t *hdd_ctx) ipa_ctxt->ipa_resource.rx_proc_done_idx_paddr; HDD_IPA_WDI2_SET(pipe_in, ipa_ctxt); + hdd_ipa_init_metering(ipa_ctxt, &pipe_in); + qdf_mem_copy(&ipa_ctxt->prod_pipe_in, &pipe_in, sizeof(struct ipa_wdi_in_params)); hdd_ipa_uc_get_db_paddr(&ipa_ctxt->rx_ready_doorbell_paddr, @@ -2749,10 +3117,14 @@ QDF_STATUS hdd_ipa_uc_ol_init(hdd_context_t *hdd_ctx) (unsigned int)pipe_out.uc_door_bell_pa, ipa_ctxt->tx_pipe_handle); HDD_IPA_LOG(QDF_TRACE_LEVEL_DEBUG, - "RX : RRBPA 0x%x, RRS %d, PDIPA 0x%x, RDY_DB_PAD 0x%x", + "RX: %s 0x%x, %s %d, %s 0x%x, %s 0x%x", + "rdy_ring_base_pa", (unsigned int)pipe_in.u.ul.rdy_ring_base_pa, + "rdy_ring_size", pipe_in.u.ul.rdy_ring_size, + "rdy_ring_rp_pa", (unsigned int)pipe_in.u.ul.rdy_ring_rp_pa, + "rx_ready_doorbell_paddr", (unsigned int)ipa_ctxt->rx_ready_doorbell_paddr); } @@ -3865,7 +4237,7 @@ static enum hdd_ipa_forward_type hdd_ipa_intrabss_forward( } /** - * hdd_ipa_w2i_cb() - WLAN to IPA callback handler + * __hdd_ipa_w2i_cb() - WLAN to IPA callback handler * @priv: pointer to private data registered with IPA (we register a * pointer to the global IPA context) * @evt: the IPA event which triggered the callback diff --git a/core/wma/inc/wma.h b/core/wma/inc/wma.h index 5bbb7305a37f..604ad78e6961 100644 --- a/core/wma/inc/wma.h +++ b/core/wma/inc/wma.h @@ -1750,6 +1750,8 @@ typedef struct { * @WMA_VDEV_IBSS_PS_SET_1RX_CHAIN_IN_ATIM_WINDOW: set IBSS power save ATIM * @WMA_VDEV_DFS_CONTROL_CMDID: DFS control command * @WMA_VDEV_TXRX_GET_IPA_UC_FW_STATS_CMDID: get IPA microcontroller fw stats + * @WMA_VDEV_TXRX_GET_IPA_UC_SHARING_STATS_CMDID: get IPA uC wifi-sharing stats + * @WMA_VDEV_TXRX_SET_IPA_UC_QUOTA_CMDID: set IPA uC quota limit * * wma command ids for configuration request which * does not involve sending a wmi command. @@ -1769,6 +1771,8 @@ enum wma_cfg_cmd_id { WMA_VDEV_IBSS_PS_SET_1RX_CHAIN_IN_ATIM_WINDOW, WMA_VDEV_DFS_CONTROL_CMDID, WMA_VDEV_TXRX_GET_IPA_UC_FW_STATS_CMDID, + WMA_VDEV_TXRX_GET_IPA_UC_SHARING_STATS_CMDID, + WMA_VDEV_TXRX_SET_IPA_UC_QUOTA_CMDID, WMA_CMD_ID_MAX }; diff --git a/core/wma/src/wma_main.c b/core/wma/src/wma_main.c index 4d3b9db1a064..5eae24ee19c3 100644 --- a/core/wma/src/wma_main.c +++ b/core/wma/src/wma_main.c @@ -786,6 +786,7 @@ static int32_t wma_set_priv_cfg(tp_wma_handle wma_handle, case WMA_VDEV_TXRX_GET_IPA_UC_FW_STATS_CMDID: { + void *soc = cds_get_context(QDF_MODULE_ID_SOC); struct cdp_pdev *pdev; pdev = cds_get_context(QDF_MODULE_ID_TXRX); @@ -793,8 +794,43 @@ static int32_t wma_set_priv_cfg(tp_wma_handle wma_handle, WMA_LOGE("pdev NULL for uc stat"); return -EINVAL; } - cdp_ipa_get_stat(cds_get_context(QDF_MODULE_ID_SOC), - pdev); + cdp_ipa_get_stat(soc, pdev); + } + break; + + case WMA_VDEV_TXRX_GET_IPA_UC_SHARING_STATS_CMDID: + { + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct cdp_pdev *pdev; + uint8_t reset_stats = privcmd->param_value; + + WMA_LOGE("%s: reset_stats=%d", + "WMA_VDEV_TXRX_GET_IPA_UC_SHARING_STATS_CMDID", + reset_stats); + pdev = cds_get_context(QDF_MODULE_ID_TXRX); + if (!pdev) { + WMA_LOGE("pdev NULL for uc stat"); + return -EINVAL; + } + cdp_ipa_uc_get_share_stats(soc, pdev, reset_stats); + } + break; + + case WMA_VDEV_TXRX_SET_IPA_UC_QUOTA_CMDID: + { + void *soc = cds_get_context(QDF_MODULE_ID_SOC); + struct cdp_pdev *pdev; + uint64_t quota_bytes = privcmd->param_value; + + WMA_LOGE("%s: quota_bytes=%llu", + "WMA_VDEV_TXRX_SET_IPA_UC_QUOTA_CMDID", + quota_bytes); + pdev = cds_get_context(QDF_MODULE_ID_TXRX); + if (!pdev) { + WMA_LOGE("pdev NULL for uc stat"); + return -EINVAL; + } + cdp_ipa_uc_set_quota(soc, pdev, quota_bytes); } break;