问题:
硬件报了个手电筒开关500次之后,无法打开,测量硬件对应的GPIO口一直被拉低。只有重新开机才可以恢复
我本地写了个脚本复现了几遍发现是必现的问题,在开关这个操作执行1000次左右是必现的。
脚本本内容:
#!/bin/bash
cnt=0
while true
do
sleep 0.8
adb shell input tap 338 298
echo cnt = $cnt
let cnt++
done
分析过程:
从我抓的log的时间点看是出现了out of memory,出现一次之后后面操作手电筒,都是在报这个错误,只有重新开机才会恢复。
06-04 06:24:29.953 24817 24858 E CamX : [ERROR][CSL ] camxcslhwinternal.cpp:3239 CSLHwInternalDefaultIoctl2() Ioctl returned -1, failed for device /dev/video0 (Type:CSLHwRequestManager, FD:10, Index:-1)with error reason Out of memory
06-04 06:24:29.953 24817 24858 E CamX : [ERROR][CSL ] camxcslhw.cpp:2474 CSLAllocHW() Allocating CSL Buffer failed for len = 32
06-04 06:24:29.953 24817 24858 E CamX : [ERROR][MEMMGR ] camxcmdbuffermanager.cpp:417 InitializePool() Buffer Info 0xb40000770a2849d0, CamxResult: 1
06-04 06:24:29.953 24817 24858 E CamX : [ERROR][UTILS ] camxcmdbuffermanager.cpp:435 InitializePool() Out of memory
06-04 06:24:29.953 24817 24858 E CamX : [ERROR][CORE ] camxnode.cpp:5287 CreateCmdBufferManager() Failed to Create command Buffer Manager(CBM_TorchPipeline_(null)0_FlashRERCmdManager)
06-04 06:24:29.953 24817 24858 E CamX : [ERROR][SENSOR ] camxtorchnode.cpp:324 ProcessingNodeFinalizeInitialization() Fail to create FlashRERCmdManager
06-04 06:24:29.953 24817 24858 E CamX : [ERROR][HAL ] camxsession.cpp:2204 InitializeNewPipelines() FinalizePipeline(0) failed!
06-04 06:24:29.953 24817 24858 E CamX : [ERROR][HAL ] camxchisession.cpp:79 Create() CHISession Initialize failed!
06-04 06:24:29.953 24817 24858 E CamX : [ERROR][CORE ] camxsession.cpp:690 Flush() Flush is called before session 0xb40000761a657110 is initialized
06-04 06:24:29.954 24817 24858 I CamX : [CONFIG][CORE ] camxsession.cpp:1118 Destroy() Session (0xb40000761a657110) Destroy
06-04 06:24:29.954 24817 24858 E CamX : [ERROR][CHI ] camxchicontext.cpp:4110 CreateSession() Unable to create session
看到这个报错之后就感觉是发生了内存泄漏,在对应的kernel也可以发现对应的报错。
06-04 06:24:17.629 24858 24858 I CAM_ERR : CAM-MEM: cam_mem_mgr_alloc_and_map: 852 Failed in getting mem slot, idx=-12
06-04 06:24:19.399 24858 24858 I CAM_ERR : CAM-MEM: cam_mem_mgr_alloc_and_map: 852 Failed in getting mem slot, idx=-12
06-04 06:24:21.150 24858 24858 I CAM_ERR : CAM-MEM: cam_mem_mgr_alloc_and_map: 852 Failed in getting mem slot, idx=-12
06-04 06:24:29.968 24858 24858 I CAM_ERR : CAM-MEM: cam_mem_mgr_alloc_and_map: 852 Failed in getting mem slot, idx=-12
我们平时hal使用的buffer其实是通过hal层的buffer pool、buffer manager去申请释放的,但是真正申请释放的地方是在kernel。
申请:
static int32_t cam_mem_get_slot(void)
{
int32_t idx;
mutex_lock(&tbl.m_lock);
idx = find_first_zero_bit(tbl.bitmap, tbl.bits);
if (idx >= CAM_MEM_BUFQ_MAX || idx <= 0) {
mutex_unlock(&tbl.m_lock);
return -ENOMEM;
}
set_bit(idx, tbl.bitmap);
tbl.bufq[idx].active = true;
mutex_init(&tbl.bufq[idx].q_lock);
mutex_unlock(&tbl.m_lock);
return idx;
}
释放:
int cam_mem_mgr_release(struct cam_mem_mgr_release_cmd *cmd)
{
int idx;
int rc = 0;
if (!atomic_read(&cam_mem_mgr_state)) {
CAM_ERR(CAM_MEM, "failed. mem_mgr not initialized");
return -EINVAL;
}
if (!cmd) {
CAM_ERR(CAM_MEM, "Invalid argument");
return -EINVAL;
}
idx = CAM_MEM_MGR_GET_HDL_IDX(cmd->buf_handle);
if (idx >= CAM_MEM_BUFQ_MAX || idx <= 0) {
CAM_ERR(CAM_MEM, "Incorrect index %d extracted from mem handle",
idx);
return -EINVAL;
}
if (!tbl.bufq[idx].active) {
CAM_ERR(CAM_MEM, "Released buffer state should be active");
return -EINVAL;
}
if (tbl.bufq[idx].buf_handle != cmd->buf_handle) {
CAM_ERR(CAM_MEM,
"Released buf handle %d not matching within table %d, idx=%d",
cmd->buf_handle, tbl.bufq[idx].buf_handle, idx);
return -EINVAL;
}
CAM_DBG(CAM_MEM, "Releasing hdl = %x, idx = %d", cmd->buf_handle, idx);
if (kref_put(&tbl.bufq[idx].krefcount, cam_mem_util_unmap))
CAM_DBG(CAM_MEM,
"Called unmap from here, buf_handle: %u, idx: %d",
cmd->buf_handle, idx);
return rc;
}
其实buffer要整整的释放是需要执行cam_mem_util_unmap这个函数,但是要执行这个函数需要满足if (kref_put(&tbl.bufq[idx].krefcount, cam_mem_util_unmap))这个条件,这里就要简单介绍一下kref这个相关的函数
kerf的浅析:
struct kref结构体是一个引用计数器,它被嵌套进其它的结构体中,记录所嵌套结构的引用计数。引用计数用于检测内核中有多少地方使用了某个对象,每当内核的一个部分需要某个对象所包含的信息时,则该对象的引用计数加1,如果不需要相应的信息,则对该对象的引用计数减1,当引用计数为0时,内核知道不再需要该对象,将从内存中释放该对象。
struct kref {
refcount_t refcount;
};
对应的函数:
kref_init(struct kref *kref) 会初始化为1
kref_read(const struct kref *kref) 返回当前引用的次数
kref_get(struct kref *kref) 对引用的次数进行+1的操作
kref_put(struct kref *kref, void (*release)(struct kref *kref)) 对引用的次数进行-1的操作,当引用计数器的值为0时,则调用release函数
通过打印log发现打log发现在打开手电筒和关闭手电筒在hal调用的个数和次数的流程是没有问题的。
#define CAM_MEM_BUFQ_MAX 1024
struct cam_mem_table {
struct mutex m_lock;
void *bitmap;
size_t bits;
struct cam_mem_buf_queue bufq[CAM_MEM_BUFQ_MAX];
struct dentry *dentry;
bool alloc_profile_enable;
size_t dbg_buf_idx;
bool force_cache_allocs;
#if IS_REACHABLE(CONFIG_DMABUF_HEAPS)
struct dma_heap *system_heap;
struct dma_heap *system_uncached_heap;
struct dma_heap *camera_heap;
struct dma_heap *camera_uncached_heap;
struct dma_heap *secure_display_heap;
#endif
};
这是kernel对应的memory定义,有个结构体数组存放1024个单元,如果申请的数量超过1024个就会出现问题
在kernel对应的get 和 unmap对应的个数对应不上,打开的时候会申请9个buffer,在释放的时候只释放了8个buffer。
在前面的地方提到过kref_put这个函数要krefcount这个数值为0才会去调用unmap这个函数。我在调用前后发现有一个buffer在退出的时候krefcount这个数值不为0,说明在执行的过程中,对buffer的引用操作出现了异常。
因为这个只使用了flash的硬件,所以只需要在flash的驱动里面去排查操作krefcount的地方。最后添加log定位到是有个地方使用buffer的时候只krefcount进行了+1的操作,使用完成之后没有进行-1的操作。导致退出的时候这个bufer的krefcount异常,没有进行正常的关闭。
static int32_t cam_flash_platform_probe(struct platform_device *pdev)
{
fctrl->func_tbl.parser = cam_flash_i2c_pkt_parser;
fctrl->func_tbl.apply_setting = cam_flash_i2c_apply_setting;
fctrl->func_tbl.power_ops = cam_flash_i2c_power_ops;
fctrl->func_tbl.flush_req = cam_flash_i2c_flush_request;
}
static int32_t cam_flash_driver_cmd(struct cam_flash_ctrl *fctrl,
void *arg, struct cam_flash_private_soc *soc_private)
{
case CAM_CONFIG_DEV: {
CAM_DBG(CAM_FLASH, "CAM_CONFIG_DEV");
rc = fctrl->func_tbl.parser(fctrl, arg);
if (rc) {
CAM_ERR(CAM_FLASH, "Failed Flash Config: rc=%d\n", rc);
goto release_mutex;
}
break;
}
int cam_flash_i2c_pkt_parser(struct cam_flash_ctrl *fctrl, void *arg)
{
cam_mem_get_cpu_buf(config.packet_handle,&generic_ptr, &len_of_buffer); //会对引用的数值+1
cam_mem_put_cpu_buf(config.packet_handle); //会对引用的数值-1
}
经过这个问题对hal的buffer manager是有很大的联系的,krefcount这个定义在hal是有对应上的,这个buffer的流转在手电筒还是比较简单的,后面有机会的话打算对camera预览场景这些把buffer流转研究一下。