qcacmn: Add changes to Affine away taken CPUs

Add changes to affinie away IRQ from the CPU taken
by audio driver during Pro audio use case.

Change-Id: I881c42e9f951fbf965be6d6a71994fd97791ee48
CRs-Fixed: 3502637
This commit is contained in:
Amit Mehta
2023-05-15 06:29:34 -07:00
committed by Rahul Choudhary
parent 40d067432f
commit 8a4a8722b0
12 changed files with 592 additions and 54 deletions

View File

@@ -1136,6 +1136,38 @@ static inline void hif_latency_detect_init(struct hif_softc *scn)
static inline void hif_latency_detect_deinit(struct hif_softc *scn)
{}
#endif
#ifdef WLAN_FEATURE_AFFINITY_MGR
#define AFFINITY_THRESHOLD 5000000
static inline void
hif_affinity_mgr_init(struct hif_softc *scn, struct wlan_objmgr_psoc *psoc)
{
unsigned int cpus;
qdf_cpu_mask allowed_mask;
scn->affinity_mgr_supported =
(cfg_get(psoc, CFG_IRQ_AFFINE_AUDIO_USE_CASE) &&
qdf_walt_get_cpus_taken_supported());
hif_info("Affinity Manager supported: %d", scn->affinity_mgr_supported);
if (!scn->affinity_mgr_supported)
return;
scn->time_threshold = AFFINITY_THRESHOLD;
qdf_for_each_possible_cpu(cpus)
if (qdf_topology_physical_package_id(cpus) ==
CPU_CLUSTER_TYPE_LITTLE)
qdf_cpumask_set_cpu(cpus, &allowed_mask);
qdf_cpumask_copy(&scn->allowed_mask, &allowed_mask);
}
#else
static inline void
hif_affinity_mgr_init(struct hif_softc *scn, struct wlan_objmgr_psoc *psoc)
{
}
#endif
struct hif_opaque_softc *hif_open(qdf_device_t qdf_ctx,
uint32_t mode,
enum qdf_bus_type bus_type,
@@ -1184,6 +1216,7 @@ struct hif_opaque_softc *hif_open(qdf_device_t qdf_ctx,
hif_cpuhp_register(scn);
hif_latency_detect_init(scn);
hif_affinity_mgr_init(scn, psoc);
out:
return GET_HIF_OPAQUE_HDL(scn);
@@ -2495,3 +2528,333 @@ int hif_system_pm_state_check(struct hif_opaque_softc *hif)
return 0;
}
#endif
#ifdef WLAN_FEATURE_AFFINITY_MGR
/*
* hif_audio_cpu_affinity_allowed() - Check if audio cpu affinity allowed
*
* @scn: hif handle
* @cfg: hif affinity manager configuration for IRQ
* @audio_taken_cpu: Current CPUs which are taken by audio.
* @current_time: Current system time.
*
* This API checks for 2 conditions
* 1) Last audio taken mask and current taken mask are different
* 2) Last time when IRQ was affined away due to audio taken CPUs is
* more than time threshold (5 Seconds in current case).
* If both condition satisfies then only return true.
*
* Return: bool: true if it is allowed to affine away audio taken cpus.
*/
static inline bool
hif_audio_cpu_affinity_allowed(struct hif_softc *scn,
struct hif_cpu_affinity *cfg,
qdf_cpu_mask audio_taken_cpu,
uint64_t current_time)
{
if (!qdf_cpumask_equal(&audio_taken_cpu, &cfg->walt_taken_mask) &&
(qdf_log_timestamp_to_usecs(current_time -
cfg->last_affined_away)
< scn->time_threshold))
return false;
return true;
}
/*
* hif_affinity_mgr_check_update_mask() - Check if cpu mask need to be updated
*
* @scn: hif handle
* @cfg: hif affinity manager configuration for IRQ
* @audio_taken_cpu: Current CPUs which are taken by audio.
* @cpu_mask: CPU mask which need to be updated.
* @current_time: Current system time.
*
* This API checks if Pro audio use case is running and if cpu_mask need
* to be updated
*
* Return: QDF_STATUS
*/
static inline QDF_STATUS
hif_affinity_mgr_check_update_mask(struct hif_softc *scn,
struct hif_cpu_affinity *cfg,
qdf_cpu_mask audio_taken_cpu,
qdf_cpu_mask *cpu_mask,
uint64_t current_time)
{
qdf_cpu_mask allowed_mask;
/*
* Case 1: audio_taken_mask is empty
* Check if passed cpu_mask and wlan_requested_mask is same or not.
* If both mask are different copy wlan_requested_mask(IRQ affinity
* mask requested by WLAN) to cpu_mask.
*
* Case 2: audio_taken_mask is not empty
* 1. Only allow update if last time when IRQ was affined away due to
* audio taken CPUs is more than 5 seconds or update is requested
* by WLAN
* 2. Only allow silver cores to be affined away.
* 3. Check if any allowed CPUs for audio use case is set in cpu_mask.
* i. If any CPU mask is set, mask out that CPU from the cpu_mask
* ii. If after masking out audio taken cpu(Silver cores) cpu_mask
* is empty, set mask to all cpu except cpus taken by audio.
* Example:
*| Audio mask | mask allowed | cpu_mask | WLAN req mask | new cpu_mask|
*| 0x00 | 0x00 | 0x0C | 0x0C | 0x0C |
*| 0x00 | 0x00 | 0x03 | 0x03 | 0x03 |
*| 0x00 | 0x00 | 0xFC | 0x03 | 0x03 |
*| 0x00 | 0x00 | 0x03 | 0x0C | 0x0C |
*| 0x0F | 0x03 | 0x0C | 0x0C | 0x0C |
*| 0x0F | 0x03 | 0x03 | 0x03 | 0xFC |
*| 0x03 | 0x03 | 0x0C | 0x0C | 0x0C |
*| 0x03 | 0x03 | 0x03 | 0x03 | 0xFC |
*| 0x03 | 0x03 | 0xFC | 0x03 | 0xFC |
*| 0xF0 | 0x00 | 0x0C | 0x0C | 0x0C |
*| 0xF0 | 0x00 | 0x03 | 0x03 | 0x03 |
*/
/* Check if audio taken mask is empty*/
if (qdf_likely(qdf_cpumask_empty(&audio_taken_cpu))) {
/* If CPU mask requested by WLAN for the IRQ and
* cpu_mask passed CPU mask set for IRQ is different
* Copy requested mask into cpu_mask and return
*/
if (qdf_unlikely(!qdf_cpumask_equal(cpu_mask,
&cfg->wlan_requested_mask))) {
qdf_cpumask_copy(cpu_mask, &cfg->wlan_requested_mask);
return QDF_STATUS_SUCCESS;
}
return QDF_STATUS_E_ALREADY;
}
if (!(hif_audio_cpu_affinity_allowed(scn, cfg, audio_taken_cpu,
current_time) ||
cfg->update_requested))
return QDF_STATUS_E_AGAIN;
/* Only allow Silver cores to be affine away */
qdf_cpumask_and(&allowed_mask, &scn->allowed_mask, &audio_taken_cpu);
if (qdf_cpumask_intersects(cpu_mask, &allowed_mask)) {
/* If any of taken CPU(Silver cores) mask is set in cpu_mask,
* mask out the audio taken CPUs from the cpu_mask.
*/
qdf_cpumask_andnot(cpu_mask, &cfg->wlan_requested_mask,
&allowed_mask);
/* If cpu_mask is empty set it to all CPUs
* except taken by audio(Silver cores)
*/
if (qdf_unlikely(qdf_cpumask_empty(cpu_mask)))
qdf_cpumask_complement(cpu_mask, &allowed_mask);
return QDF_STATUS_SUCCESS;
}
return QDF_STATUS_E_ALREADY;
}
static inline QDF_STATUS
hif_check_and_affine_irq(struct hif_softc *scn, struct hif_cpu_affinity *cfg,
qdf_cpu_mask audio_taken_cpu, qdf_cpu_mask cpu_mask,
uint64_t current_time)
{
QDF_STATUS status;
status = hif_affinity_mgr_check_update_mask(scn, cfg,
audio_taken_cpu,
&cpu_mask,
current_time);
/* Set IRQ affinity if CPU mask was updated */
if (QDF_IS_STATUS_SUCCESS(status)) {
status = hif_irq_set_affinity_hint(cfg->irq,
&cpu_mask);
if (QDF_IS_STATUS_SUCCESS(status)) {
/* Store audio taken CPU mask */
qdf_cpumask_copy(&cfg->walt_taken_mask,
&audio_taken_cpu);
/* Store CPU mask which was set for IRQ*/
qdf_cpumask_copy(&cfg->current_irq_mask,
&cpu_mask);
/* Set time when IRQ affinity was updated */
cfg->last_updated = current_time;
if (hif_audio_cpu_affinity_allowed(scn, cfg,
audio_taken_cpu,
current_time))
/* If CPU mask was updated due to CPU
* taken by audio, update
* last_affined_away time
*/
cfg->last_affined_away = current_time;
}
}
return status;
}
void hif_affinity_mgr_affine_irq(struct hif_softc *scn)
{
bool audio_affinity_allowed = false;
int i, j, ce_id;
uint64_t current_time;
char cpu_str[10];
QDF_STATUS status;
qdf_cpu_mask cpu_mask, audio_taken_cpu;
struct HIF_CE_state *hif_state;
struct hif_exec_context *hif_ext_group;
struct CE_attr *host_ce_conf;
struct HIF_CE_state *ce_sc;
struct hif_cpu_affinity *cfg;
if (!scn->affinity_mgr_supported)
return;
current_time = hif_get_log_timestamp();
/* Get CPU mask for audio taken CPUs */
audio_taken_cpu = qdf_walt_get_cpus_taken();
ce_sc = HIF_GET_CE_STATE(scn);
host_ce_conf = ce_sc->host_ce_config;
for (ce_id = 0; ce_id < scn->ce_count; ce_id++) {
if (host_ce_conf[ce_id].flags & CE_ATTR_DISABLE_INTR)
continue;
cfg = &scn->ce_irq_cpu_mask[ce_id];
qdf_cpumask_copy(&cpu_mask, &cfg->current_irq_mask);
status =
hif_check_and_affine_irq(scn, cfg, audio_taken_cpu,
cpu_mask, current_time);
if (QDF_IS_STATUS_SUCCESS(status))
audio_affinity_allowed = true;
}
hif_state = HIF_GET_CE_STATE(scn);
for (i = 0; i < hif_state->hif_num_extgroup; i++) {
hif_ext_group = hif_state->hif_ext_group[i];
for (j = 0; j < hif_ext_group->numirq; j++) {
cfg = &scn->irq_cpu_mask[hif_ext_group->grp_id][j];
qdf_cpumask_copy(&cpu_mask, &cfg->current_irq_mask);
status =
hif_check_and_affine_irq(scn, cfg, audio_taken_cpu,
cpu_mask, current_time);
if (QDF_IS_STATUS_SUCCESS(status)) {
qdf_atomic_set(&hif_ext_group->force_napi_complete, -1);
audio_affinity_allowed = true;
}
}
}
if (audio_affinity_allowed) {
qdf_thread_cpumap_print_to_pagebuf(false, cpu_str,
&audio_taken_cpu);
hif_info("Audio taken CPU mask: %s", cpu_str);
}
}
static inline QDF_STATUS
hif_affinity_mgr_set_irq_affinity(struct hif_softc *scn, uint32_t irq,
struct hif_cpu_affinity *cfg,
qdf_cpu_mask *cpu_mask)
{
uint64_t current_time;
char cpu_str[10];
QDF_STATUS status, mask_updated;
qdf_cpu_mask audio_taken_cpu = qdf_walt_get_cpus_taken();
current_time = hif_get_log_timestamp();
qdf_cpumask_copy(&cfg->wlan_requested_mask, cpu_mask);
cfg->update_requested = true;
mask_updated = hif_affinity_mgr_check_update_mask(scn, cfg,
audio_taken_cpu,
cpu_mask,
current_time);
status = hif_irq_set_affinity_hint(irq, cpu_mask);
if (QDF_IS_STATUS_SUCCESS(status)) {
qdf_cpumask_copy(&cfg->walt_taken_mask, &audio_taken_cpu);
qdf_cpumask_copy(&cfg->current_irq_mask, cpu_mask);
if (QDF_IS_STATUS_SUCCESS(mask_updated)) {
cfg->last_updated = current_time;
if (hif_audio_cpu_affinity_allowed(scn, cfg,
audio_taken_cpu,
current_time)) {
cfg->last_affined_away = current_time;
qdf_thread_cpumap_print_to_pagebuf(false,
cpu_str,
&audio_taken_cpu);
hif_info_rl("Audio taken CPU mask: %s",
cpu_str);
}
}
}
cfg->update_requested = false;
return status;
}
QDF_STATUS
hif_affinity_mgr_set_qrg_irq_affinity(struct hif_softc *scn, uint32_t irq,
uint32_t grp_id, uint32_t irq_index,
qdf_cpu_mask *cpu_mask)
{
struct hif_cpu_affinity *cfg;
if (!scn->affinity_mgr_supported)
return hif_irq_set_affinity_hint(irq, cpu_mask);
cfg = &scn->irq_cpu_mask[grp_id][irq_index];
return hif_affinity_mgr_set_irq_affinity(scn, irq, cfg, cpu_mask);
}
QDF_STATUS
hif_affinity_mgr_set_ce_irq_affinity(struct hif_softc *scn, uint32_t irq,
uint32_t ce_id, qdf_cpu_mask *cpu_mask)
{
struct hif_cpu_affinity *cfg;
if (!scn->affinity_mgr_supported)
return hif_irq_set_affinity_hint(irq, cpu_mask);
cfg = &scn->ce_irq_cpu_mask[ce_id];
return hif_affinity_mgr_set_irq_affinity(scn, irq, cfg, cpu_mask);
}
void
hif_affinity_mgr_init_ce_irq(struct hif_softc *scn, int id, int irq)
{
unsigned int cpus;
qdf_cpu_mask cpu_mask;
struct hif_cpu_affinity *cfg = NULL;
if (!scn->affinity_mgr_supported)
return;
/* Set CPU Mask to all possible CPUs */
qdf_for_each_possible_cpu(cpus)
qdf_cpumask_set_cpu(cpus, &cpu_mask);
cfg = &scn->ce_irq_cpu_mask[id];
qdf_cpumask_copy(&cfg->current_irq_mask, &cpu_mask);
qdf_cpumask_copy(&cfg->wlan_requested_mask, &cpu_mask);
cfg->irq = irq;
cfg->last_updated = 0;
cfg->last_affined_away = 0;
cfg->update_requested = false;
}
void
hif_affinity_mgr_init_grp_irq(struct hif_softc *scn, int grp_id,
int irq_num, int irq)
{
unsigned int cpus;
qdf_cpu_mask cpu_mask;
struct hif_cpu_affinity *cfg = NULL;
if (!scn->affinity_mgr_supported)
return;
/* Set CPU Mask to all possible CPUs */
qdf_for_each_possible_cpu(cpus)
qdf_cpumask_set_cpu(cpus, &cpu_mask);
cfg = &scn->irq_cpu_mask[grp_id][irq_num];
qdf_cpumask_copy(&cfg->current_irq_mask, &cpu_mask);
qdf_cpumask_copy(&cfg->wlan_requested_mask, &cpu_mask);
cfg->irq = irq;
cfg->last_updated = 0;
cfg->last_affined_away = 0;
cfg->update_requested = false;
}
#endif