diff --git a/drivers/cam_cdm/cam_cdm_hw_core.c b/drivers/cam_cdm/cam_cdm_hw_core.c index 013b9f3267ed..37f2a0a10967 100644 --- a/drivers/cam_cdm/cam_cdm_hw_core.c +++ b/drivers/cam_cdm/cam_cdm_hw_core.c @@ -26,6 +26,7 @@ #include "camera_main.h" #include "cam_trace.h" #include "cam_req_mgr_workq.h" +#include "cam_common_util.h" #define CAM_CDM_BL_FIFO_WAIT_TIMEOUT 2000 #define CAM_CDM_DBG_GEN_IRQ_USR_DATA 0xff @@ -1215,8 +1216,10 @@ static void cam_hw_cdm_work(struct work_struct *work) return; } - cam_req_mgr_thread_switch_delay_detect( - payload->workq_scheduled_ts); + cam_common_util_thread_switch_delay_detect( + "CDM workq schedule", + payload->workq_scheduled_ts, + CAM_WORKQ_SCHEDULE_TIME_THRESHOLD); CAM_DBG(CAM_CDM, "IRQ status=0x%x", payload->irq_status); if (payload->irq_status & diff --git a/drivers/cam_cdm/cam_cdm_virtual_core.c b/drivers/cam_cdm/cam_cdm_virtual_core.c index 481d37616c48..3f5a9a3e90f3 100644 --- a/drivers/cam_cdm/cam_cdm_virtual_core.c +++ b/drivers/cam_cdm/cam_cdm_virtual_core.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. */ #include @@ -20,6 +20,7 @@ #include "cam_cdm_soc.h" #include "cam_io_util.h" #include "cam_req_mgr_workq.h" +#include "cam_common_util.h" #define CAM_CDM_VIRTUAL_NAME "qcom,cam_virtual_cdm" @@ -34,8 +35,10 @@ static void cam_virtual_cdm_work(struct work_struct *work) cdm_hw = payload->hw; core = (struct cam_cdm *)cdm_hw->core_info; - cam_req_mgr_thread_switch_delay_detect( - payload->workq_scheduled_ts); + cam_common_util_thread_switch_delay_detect( + "Virtual CDM workq schedule", + payload->workq_scheduled_ts, + CAM_WORKQ_SCHEDULE_TIME_THRESHOLD); if (payload->irq_status & 0x2) { struct cam_cdm_bl_cb_request_entry *node; diff --git a/drivers/cam_cpas/cpas_top/cam_cpastop_hw.c b/drivers/cam_cpas/cpas_top/cam_cpastop_hw.c index 01a376dd51c1..15c96c66ae59 100644 --- a/drivers/cam_cpas/cpas_top/cam_cpastop_hw.c +++ b/drivers/cam_cpas/cpas_top/cam_cpastop_hw.c @@ -33,6 +33,7 @@ #include "cpastop_v680_100.h" #include "cpastop_v165_100.h" #include "cam_req_mgr_workq.h" +#include "cam_common_util.h" struct cam_camnoc_info *camnoc_info; struct cam_cpas_camnoc_qchannel *qchannel_info; @@ -598,8 +599,10 @@ static void cam_cpastop_work(struct work_struct *work) return; } - cam_req_mgr_thread_switch_delay_detect( - payload->workq_scheduled_ts); + cam_common_util_thread_switch_delay_detect( + "CPAS workq schedule", + payload->workq_scheduled_ts, + CAM_WORKQ_SCHEDULE_TIME_THRESHOLD); cpas_hw = payload->hw; cpas_core = (struct cam_cpas *) cpas_hw->core_info; diff --git a/drivers/cam_isp/isp_hw_mgr/hw_utils/cam_tasklet_util.c b/drivers/cam_isp/isp_hw_mgr/hw_utils/cam_tasklet_util.c index 356c9a0a8919..cd9bd82c7938 100644 --- a/drivers/cam_isp/isp_hw_mgr/hw_utils/cam_tasklet_util.c +++ b/drivers/cam_isp/isp_hw_mgr/hw_utils/cam_tasklet_util.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2019, 2021 The Linux Foundation. All rights reserved. */ #include @@ -11,8 +11,16 @@ #include "cam_tasklet_util.h" #include "cam_irq_controller.h" #include "cam_debug_util.h" +#include "cam_common_util.h" -#define CAM_TASKLETQ_SIZE 256 + +/* Threshold for scheduling delay in ms */ +#define CAM_TASKLET_SCHED_TIME_THRESHOLD 5 + +/* Threshold for execution delay in ms */ +#define CAM_TASKLET_EXE_TIME_THRESHOLD 10 + +#define CAM_TASKLETQ_SIZE 256 static void cam_tasklet_action(unsigned long data); @@ -27,6 +35,7 @@ static void cam_tasklet_action(unsigned long data); * @handler_priv: Private data passed at event subscribe * @bottom_half_handler: Function pointer for event handler in bottom * half context + * @tasklet_enqueue_ts: enqueue time of tasklet * */ struct cam_tasklet_queue_cmd { @@ -34,6 +43,7 @@ struct cam_tasklet_queue_cmd { void *payload; void *handler_priv; CAM_IRQ_HANDLER_BOTTOM_HALF bottom_half_handler; + ktime_t tasklet_enqueue_ts; }; /** @@ -209,6 +219,7 @@ void cam_tasklet_enqueue_cmd( tasklet_cmd->bottom_half_handler = bottom_half_handler; tasklet_cmd->payload = evt_payload_priv; tasklet_cmd->handler_priv = handler_priv; + tasklet_cmd->tasklet_enqueue_ts = ktime_get(); spin_lock_irqsave(&tasklet->tasklet_lock, flags); list_add_tail(&tasklet_cmd->list, &tasklet->used_cmd_list); @@ -322,12 +333,24 @@ static void cam_tasklet_action(unsigned long data) { struct cam_tasklet_info *tasklet_info = NULL; struct cam_tasklet_queue_cmd *tasklet_cmd = NULL; + ktime_t curr_time; tasklet_info = (struct cam_tasklet_info *)data; while (!cam_tasklet_dequeue_cmd(tasklet_info, &tasklet_cmd)) { + cam_common_util_thread_switch_delay_detect( + "Tasklet schedule", + tasklet_cmd->tasklet_enqueue_ts, + CAM_TASKLET_SCHED_TIME_THRESHOLD); + curr_time = ktime_get(); + tasklet_cmd->bottom_half_handler(tasklet_cmd->handler_priv, tasklet_cmd->payload); + + cam_common_util_thread_switch_delay_detect( + "Tasklet execution", + curr_time, + CAM_TASKLET_EXE_TIME_THRESHOLD); cam_tasklet_put_cmd(tasklet_info, (void **)(&tasklet_cmd)); } } diff --git a/drivers/cam_req_mgr/cam_req_mgr_workq.c b/drivers/cam_req_mgr/cam_req_mgr_workq.c index c2ed7f0d2c00..cfa7df88eef3 100644 --- a/drivers/cam_req_mgr/cam_req_mgr_workq.c +++ b/drivers/cam_req_mgr/cam_req_mgr_workq.c @@ -1,10 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. */ #include "cam_req_mgr_workq.h" #include "cam_debug_util.h" +#include "cam_common_util.h" #define WORKQ_ACQUIRE_LOCK(workq, flags) {\ if ((workq)->in_irq) \ @@ -94,6 +95,7 @@ void cam_req_mgr_process_workq(struct work_struct *w) struct crm_workq_task *task; int32_t i = CRM_TASK_PRIORITY_0; unsigned long flags = 0; + ktime_t curr_time; if (!w) { CAM_ERR(CAM_CRM, "NULL task pointer can not schedule"); @@ -102,7 +104,11 @@ void cam_req_mgr_process_workq(struct work_struct *w) workq = (struct cam_req_mgr_core_workq *) container_of(w, struct cam_req_mgr_core_workq, work); - cam_req_mgr_thread_switch_delay_detect(workq->workq_scheduled_ts); + cam_common_util_thread_switch_delay_detect( + "CRM workq schedule", + workq->workq_scheduled_ts, + CAM_WORKQ_SCHEDULE_TIME_THRESHOLD); + curr_time = ktime_get(); while (i < CRM_TASK_PRIORITY_MAX) { WORKQ_ACQUIRE_LOCK(workq, flags); while (!list_empty(&workq->task.process_head[i])) { @@ -119,6 +125,10 @@ void cam_req_mgr_process_workq(struct work_struct *w) WORKQ_RELEASE_LOCK(workq, flags); i++; } + cam_common_util_thread_switch_delay_detect( + "CRM workq execution", + curr_time, + CAM_WORKQ_EXE_TIME_THRESHOLD); } int cam_req_mgr_workq_enqueue_task(struct crm_workq_task *task, @@ -165,8 +175,8 @@ int cam_req_mgr_workq_enqueue_task(struct crm_workq_task *task, CAM_DBG(CAM_CRM, "enq task %pK pending_cnt %d", task, atomic_read(&workq->task.pending_cnt)); - workq->workq_scheduled_ts = ktime_get(); queue_work(workq->job, &workq->work); + workq->workq_scheduled_ts = ktime_get(); WORKQ_RELEASE_LOCK(workq, flags); end: return rc; @@ -267,25 +277,3 @@ void cam_req_mgr_workq_destroy(struct cam_req_mgr_core_workq **crm_workq) *crm_workq = NULL; } } - -void cam_req_mgr_thread_switch_delay_detect(ktime_t workq_scheduled) -{ - uint64_t diff; - ktime_t cur_time; - struct timespec64 cur_ts; - struct timespec64 workq_scheduled_ts; - - cur_time = ktime_get(); - diff = ktime_ms_delta(cur_time, workq_scheduled); - workq_scheduled_ts = ktime_to_timespec64(workq_scheduled); - cur_ts = ktime_to_timespec64(cur_time); - - if (diff > CAM_WORKQ_RESPONSE_TIME_THRESHOLD) { - CAM_WARN_RATE_LIMIT(CAM_CRM, - "Workq delay detected %ld:%06ld %ld:%06ld %ld:", - workq_scheduled_ts.tv_sec, - workq_scheduled_ts.tv_nsec/NSEC_PER_USEC, - cur_ts.tv_sec, cur_ts.tv_nsec/NSEC_PER_USEC, - diff); - } -} diff --git a/drivers/cam_req_mgr/cam_req_mgr_workq.h b/drivers/cam_req_mgr/cam_req_mgr_workq.h index 01e600ac3cb6..caef3c990586 100644 --- a/drivers/cam_req_mgr/cam_req_mgr_workq.h +++ b/drivers/cam_req_mgr/cam_req_mgr_workq.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. */ #ifndef _CAM_REQ_MGR_WORKQ_H_ @@ -16,6 +16,12 @@ #include "cam_req_mgr_core.h" +/* Threshold for scheduling delay in ms */ +#define CAM_WORKQ_SCHEDULE_TIME_THRESHOLD 5 + +/* Threshold for execution delay in ms */ +#define CAM_WORKQ_EXE_TIME_THRESHOLD 10 + /* Flag to create a high priority workq */ #define CAM_WORKQ_FLAG_HIGH_PRIORITY (1 << 0) @@ -26,13 +32,6 @@ */ #define CAM_WORKQ_FLAG_SERIAL (1 << 1) -/* - * Response time threshold in ms beyond which it is considered - * as workq scheduling/processing delay. - */ -#define CAM_WORKQ_RESPONSE_TIME_THRESHOLD 5 - - /* Task priorities, lower the number higher the priority*/ enum crm_task_priority { CRM_TASK_PRIORITY_0, @@ -76,6 +75,7 @@ struct crm_workq_task { * @job : workqueue internal job struct * @lock_bh : lock for task structs * @in_irq : set true if workque can be used in irq context + * @workq_scheduled_ts: enqueue time of workq * task - * @lock : Current task's lock handle * @pending_cnt : # of tasks left in queue @@ -150,14 +150,6 @@ void cam_req_mgr_workq_destroy(struct cam_req_mgr_core_workq **workq); int cam_req_mgr_workq_enqueue_task(struct crm_workq_task *task, void *priv, int32_t prio); -/** - * cam_req_mgr_thread_switch_delay_detect() - * @brief: Detects if workq delay has occurred or not - * @timestamp: workq scheduled timestamp - */ -void cam_req_mgr_thread_switch_delay_detect( - ktime_t timestamp); - /** * cam_req_mgr_workq_get_task() * @brief: Returns empty task pointer for use diff --git a/drivers/cam_sensor_module/cam_cci/cam_cci_core.c b/drivers/cam_sensor_module/cam_cci/cam_cci_core.c index e4e185bb28c1..a9ff832eb5c2 100644 --- a/drivers/cam_sensor_module/cam_cci/cam_cci_core.c +++ b/drivers/cam_sensor_module/cam_cci/cam_cci_core.c @@ -7,6 +7,7 @@ #include "cam_cci_core.h" #include "cam_cci_dev.h" #include "cam_req_mgr_workq.h" +#include "cam_common_util.h" static int32_t cam_cci_convert_type_to_num_bytes( enum camera_sensor_i2c_type type) @@ -1498,8 +1499,10 @@ static void cam_cci_write_async_helper(struct work_struct *work) enum cci_i2c_master_t master; struct cam_cci_master_info *cci_master_info; - cam_req_mgr_thread_switch_delay_detect( - write_async->workq_scheduled_ts); + cam_common_util_thread_switch_delay_detect( + "CCI workq schedule", + write_async->workq_scheduled_ts, + CAM_WORKQ_SCHEDULE_TIME_THRESHOLD); cci_dev = write_async->cci_dev; i2c_msg = &write_async->c_ctrl.cfg.cci_i2c_write_cfg; master = write_async->c_ctrl.cci_info->cci_i2c_master; diff --git a/drivers/cam_sync/cam_sync_util.c b/drivers/cam_sync/cam_sync_util.c index 84d885c8109e..43e9709fca05 100644 --- a/drivers/cam_sync/cam_sync_util.c +++ b/drivers/cam_sync/cam_sync_util.c @@ -1,10 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2017-2018, 2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018, 2020-2021 The Linux Foundation. All rights reserved. */ #include "cam_sync_util.h" #include "cam_req_mgr_workq.h" +#include "cam_common_util.h" int cam_sync_util_find_and_set_empty_row(struct sync_device *sync_dev, long *idx) @@ -294,8 +295,10 @@ void cam_sync_util_cb_dispatch(struct work_struct *cb_dispatch_work) cb_dispatch_work); sync_callback sync_data = cb_info->callback_func; - cam_req_mgr_thread_switch_delay_detect( - cb_info->workq_scheduled_ts); + cam_common_util_thread_switch_delay_detect( + "CAM-SYNC workq schedule", + cb_info->workq_scheduled_ts, + CAM_WORKQ_SCHEDULE_TIME_THRESHOLD); sync_data(cb_info->sync_obj, cb_info->status, cb_info->cb_data); kfree(cb_info); diff --git a/drivers/cam_utils/cam_common_util.c b/drivers/cam_utils/cam_common_util.c index dbcb31d7bf0e..23681509f490 100644 --- a/drivers/cam_utils/cam_common_util.c +++ b/drivers/cam_utils/cam_common_util.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2019, 2021 The Linux Foundation. All rights reserved. */ #include @@ -48,3 +48,27 @@ uint32_t cam_common_util_remove_duplicate_arr(int32_t *arr, uint32_t num) return wr_idx; } + +void cam_common_util_thread_switch_delay_detect( + const char *token, ktime_t scheduled_time, uint32_t threshold) +{ + uint64_t diff; + ktime_t cur_time; + struct timespec64 cur_ts; + struct timespec64 scheduled_ts; + + cur_time = ktime_get(); + diff = ktime_ms_delta(cur_time, scheduled_time); + + if (diff > threshold) { + scheduled_ts = ktime_to_timespec64(scheduled_time); + cur_ts = ktime_to_timespec64(cur_time); + CAM_WARN_RATE_LIMIT_CUSTOM(CAM_UTIL, 1, 1, + "%s delay detected %ld:%06ld cur %ld:%06ld diff %ld: threshold %d", + token, scheduled_ts.tv_sec, + scheduled_ts.tv_nsec/NSEC_PER_USEC, + cur_ts.tv_sec, cur_ts.tv_nsec/NSEC_PER_USEC, + diff, threshold); + } + +} diff --git a/drivers/cam_utils/cam_common_util.h b/drivers/cam_utils/cam_common_util.h index 5bf20501af2e..60fd2f629c3d 100644 --- a/drivers/cam_utils/cam_common_util.h +++ b/drivers/cam_utils/cam_common_util.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. */ #ifndef _CAM_COMMON_UTIL_H_ @@ -64,4 +64,17 @@ int cam_common_util_get_string_index(const char **strings, uint32_t cam_common_util_remove_duplicate_arr(int32_t *array, uint32_t num); +/** + * cam_common_util_thread_switch_delay_detect() + * + * @brief Detect if there is any scheduling delay + * + * @token: String identifier to print workq name or tasklet + * @scheduled_time: Time when workq or tasklet was scheduled + * @threshold: Threshold time + * + */ +void cam_common_util_thread_switch_delay_detect(const char *token, + ktime_t scheduled_time, uint32_t threshold); + #endif /* _CAM_COMMON_UTIL_H_ */