qcacmn: Force update HP/TP upon delayed register writes

Currently if HP/TP register updates are delayed due to delayed reg
write work not getting scheduled, although driver has processed
the ring completely, hardware would see HP/TP delta and fires an
interrupt based on interrupt threshold configuration until the
HP/TP updates reach the hardware.

When system is heavily stressed, this delay in HP/TP updates would
result in IRQ storm further stressing the system which is bad.
Force update HP/TP to the hardware under such scenarios to avoid
this problem.

Currently doing this just for CE DST SRNGs, this can be scaled to
other SRNGs on need.

Change-Id: I8a4938dbd4850d7ab6ae5183186237a5e37e1038
CRs-Fixed: 3749078
This commit is contained in:
Manikanta Pubbisetty
2024-03-04 08:19:02 +05:30
committed by Ravindra Konda
parent e454eb5052
commit e2e92aa7d6
4 changed files with 110 additions and 1 deletions

View File

@@ -877,6 +877,27 @@ static inline int hal_get_reg_write_pending_work(void *hal_soc)
}
#endif
#if defined(FEATURE_HAL_DELAYED_REG_WRITE) && defined(QCA_WIFI_QCA6750)
/**
* hal_srng_check_and_update_hptp() - Check and force update HP/TP
* to the hardware
* @hal_soc: HAL soc handle
* @srng: SRNG handle
* @update: Whether or not update is needed
*
* Returns: void
*/
void hal_srng_check_and_update_hptp(struct hal_soc *hal_soc,
struct hal_srng *srng,
bool update);
#else
static inline void
hal_srng_check_and_update_hptp(struct hal_soc *hal_soc, struct hal_srng *srng,
bool update)
{
}
#endif
/**
* hal_read_address_32_mb() - Read 32-bit value from the register
* @soc: soc handle

View File

@@ -868,6 +868,12 @@ struct hal_srng {
/* srng specific delayed write stats */
struct hal_reg_write_srng_stats wstats;
union {
uint32_t updated_hp;
uint32_t updated_tp;
};
uint32_t force_cnt;
#endif
#ifdef WLAN_DP_SRNG_USAGE_WM_TRACKING
struct hal_srng_high_wm_info high_wm;

View File

@@ -673,6 +673,72 @@ int hal_get_reg_write_pending_work(void *hal_soc)
#define HAL_REG_WRITE_QUEUE_LEN 32
#endif
#ifdef QCA_WIFI_QCA6750
#define HAL_DEL_WRITE_FORCE_UPDATE_THRES 5
static inline void hal_srng_update_last_hptp(struct hal_srng *srng)
{
if (srng->ring_dir == HAL_SRNG_SRC_RING)
srng->updated_hp = srng->u.src_ring.hp;
else
srng->updated_tp = srng->u.dst_ring.tp;
srng->force_cnt = 0;
}
/* If HP/TP register updates are delayed due to delayed reg
* write work not getting scheduled, hardware would see HP/TP
* delta and will fire interrupts until the HP/TP updates reach
* the hardware.
*
* When system is heavily stressed, this delay in HP/TP updates
* would result in IRQ storm further stressing the system. Force
* update HP/TP to the hardware under such scenarios to avoid this.
*/
void hal_srng_check_and_update_hptp(struct hal_soc *hal_soc,
struct hal_srng *srng, bool update)
{
uint32_t value;
if (!update)
return;
SRNG_LOCK(&srng->lock);
if (srng->ring_dir == HAL_SRNG_SRC_RING) {
value = srng->u.src_ring.hp;
if (value == srng->updated_hp ||
srng->force_cnt++ < HAL_DEL_WRITE_FORCE_UPDATE_THRES)
goto out_unlock;
hal_write_address_32_mb(hal_soc, srng->u.src_ring.hp_addr,
value, false);
} else {
value = srng->u.dst_ring.tp;
if (value == srng->updated_tp ||
srng->force_cnt++ < HAL_DEL_WRITE_FORCE_UPDATE_THRES)
goto out_unlock;
hal_write_address_32_mb(hal_soc, srng->u.dst_ring.tp_addr,
value, false);
}
hal_srng_update_last_hptp(srng);
hal_srng_reg_his_add(srng, value);
qdf_atomic_inc(&hal_soc->stats.wstats.direct);
srng->wstats.direct++;
out_unlock:
SRNG_UNLOCK(&srng->lock);
}
#else
static inline void hal_srng_update_last_hptp(struct hal_srng *srng)
{
}
#endif /* QCA_WIFI_QCA6750 */
/**
* hal_process_reg_write_q_elem() - process a register write queue element
* @hal: hal_soc pointer
@@ -705,6 +771,8 @@ hal_process_reg_write_q_elem(struct hal_soc *hal,
srng->u.dst_ring.tp, false);
write_val = srng->u.dst_ring.tp;
}
hal_srng_update_last_hptp(srng);
hal_srng_reg_his_add(srng, write_val);
q_elem->valid = 0;
@@ -1100,6 +1168,7 @@ void hal_delayed_reg_write(struct hal_soc *hal_soc,
PLD_MHI_STATE_L0 ==
pld_get_mhi_state(hal_soc->qdf_dev->dev))) {
hal_write_address_32_mb(hal_soc, addr, value, false);
hal_srng_update_last_hptp(srng);
hal_srng_reg_his_add(srng, value);
qdf_atomic_inc(&hal_soc->stats.wstats.direct);
srng->wstats.direct++;
@@ -1136,6 +1205,7 @@ void hal_delayed_reg_write(struct hal_soc *hal_soc,
qdf_atomic_inc(&hal_soc->stats.wstats.direct);
srng->wstats.direct++;
hal_write_address_32_mb(hal_soc, addr, value, false);
hal_srng_update_last_hptp(srng);
hal_srng_reg_his_add(srng, value);
} else {
hal_reg_write_enqueue(hal_soc, srng, addr, value);

View File

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2016-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. 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
@@ -344,6 +344,12 @@ ce_recv_buf_enqueue_srng(struct CE_handle *copyeng,
return QDF_STATUS_E_IO;
}
/* HP/TP update if any should happen only once per interrupt,
* therefore checking for CE receive_count.
*/
hal_srng_check_and_update_hptp(scn->hal_soc, dest_ring->srng_ctx,
!CE_state->receive_count);
if (hal_srng_access_start(scn->hal_soc, dest_ring->srng_ctx)) {
qdf_spin_unlock_bh(&CE_state->ce_index_lock);
return QDF_STATUS_E_FAILURE;
@@ -446,6 +452,12 @@ ce_completed_recv_next_nolock_srng(struct CE_state *CE_state,
int nbytes;
struct ce_srng_dest_status_desc dest_status_info;
/* HP/TP update if any should happen only once per interrupt,
* therefore checking for CE receive_count.
*/
hal_srng_check_and_update_hptp(scn->hal_soc, status_ring->srng_ctx,
!CE_state->receive_count);
if (hal_srng_access_start(scn->hal_soc, status_ring->srng_ctx))
return QDF_STATUS_E_FAILURE;