记一次 OpenIPMI core的分析

简介:
  1. 现象 

    gdb对OpenIPMI core的分析表明 domain_id 未被初始化好就被使用,具体信息如下:

    Thread debugging using libthread_db enabled]
    > Using host libthread_db library "/lib64/libthread_db.so.1".
    > Core was generated by `/usr/lsd/bin/tcmeoer'.
    > Program terminated with signal 11, Segmentation fault.
    > #0  0x00007f2e87731ab2 in ipmi_domain_iterate_entities () from 
    > /lib64/libOpenIPMI.so.0
    > Missing separate debuginfos, use: debuginfo-install 
    > OpenIPMI-libs-2.0.19-11.el7.x86_64 gdbm-1.10-8.el7.x86_64 
    > glib2-2.46.2-4.el7.x86_64 glibc-2.17-78.el7.x86_64 
    > hiredis-0.12.1-1.el7.x86_64 keyutils-libs-1.5.8-3.el7.x86_64 
    > krb5-libs-1.12.2-14.el7.x86_64 libcom_err-1.42.9-7.el7.x86_64 
    > libffi-3.0.13-11.el7.x86_64 libgcc-4.8.3-9.el7.x86_64 
    > libselinux-2.2.2-6.el7.x86_64 libstdc++-4.8.3-9.el7.x86_64 
    > openssl-libs-1.0.1e-42.el7.x86_64 pciutils-libs-3.2.1-4.el7.x86_64 
    > pcre-8.32-14.el7.x86_64 python-libs-2.7.5-16.el7.x86_64 
    > xz-libs-5.1.2-9alpha.el7.x86_64 zlib-1.2.7-13.el7.x86_64
    > (gdb) bt
    > #0  0x00007f2e87731ab2 in ipmi_domain_iterate_entities () at 
    > /lib64/libOpenIPMI.so.0
    > #1  0x00000000004e47e3 in sensor_check_timeout (cb_data=0x0, 
    > id=0x2ed5ce0) at src/sysmon.c:300
    > #2  0x00007f2e874e7735 in process_timers () at 
    > /lib64/libOpenIPMIposix.so.0
    > #3  0x00007f2e874e8d0b in sel_select_loop () at 
    > /lib64/libOpenIPMIposix.so.0
    > #4  0x00007f2e872df12d in operation_loop () at 
    > /lib64/libOpenIPMIpthread.so.0
    > #5  0x00000000004e4a40 in sensor_thread (data=0x2ee8100) at 
    > src/sysmon.c:416
    > #6  0x00007f2e88ac8df5 in start_thread () at /lib64/libpthread.so.0
    > #7  0x00007f2e85df61ad in clone () at /lib64/libc.so.6
    > (gdb) p domain_id.domain
    > $1 = (ipmi_domain_t *) 0x0
    > (gdb) p handle_entity
    > $2 = {void (ipmi_entity_t *, void *)} 0x4e477a <handle_entity>
    >
    > == related source code is as below -==
    > static void handle_entity(ipmi_entity_t * ent, void *b_data)


    其中domain_id使用的地方在c文件头就有定义:
    ipmi_domain_id_t domain_id;
    如果它没有初始化话,默认很可能为0. 这个domain_id 它会被后面的timer线程周期调用:

    void sensor_check_timeout(void *cb_data, os_hnd_timer_id_t * id)
    {

        ipmi_domain_iterate_entities(domain_id.domain, handle_entity, NULL);

        os_hnd->start_timer(os_hnd,
                            check_timer, &check_timeout, sensor_check_timeout, NULL);
    }

    其中 ipmi_domain_iterate_entities()必须要求第一个参数domain_id.domain 不为空,否则 domain→entities会产生segment fault.

    int ipmi_domain_iterate_entities(ipmi_domain_t      *domain,
                     ipmi_entity_ptr_cb handler,
                     void               *cb_data)
    {
        CHECK_DOMAIN_LOCK(domain);

        ipmi_entities_iterate_entities(domain->entities, handler, cb_data);
        return 0;
    }

    一般情况正常情况下,domain_id.domain 它在OpenIPMI初始化的时候通过调用setup_done()被初始化,如下面的代码所示:
    rv = ipmi_open_domain("", &con, 1, setup_done, NULL, NULL, NULL, NULL, 0, &domain_id);
        if (rv) {
            LOGLIB_TRACE( "ipmi_init_domain: %s\n", strerror(rv));
            pthread_mutex_unlock(&ipmi_mutex);
            return -4;
        }
    void
    setup_done(ipmi_domain_t * domain,
               int err,
               unsigned int conn_num,
               unsigned int port_num, int still_connected, void *user_data)
    {
        int rv;


        domain_id = ipmi_domain_convert_to_id(domain);
        LOGLIB_TRACE("domain: %p", domain);

        /* Register a callback functin entity_change. When a new entity
           is created, entity_change is called */
        rv = ipmi_domain_add_entity_update_handler(domain, entity_change, domain);
        if (rv) {
            LOGLIB_ERROR("ipmi_domain_add_entity_update_handler return error: %d\n", rv);
            return;
        }
    }


    2. 原因分析

    根据日志,并没有发现有异常提前退出的地方:

     rv = ipmi_parse_args(&curr_arg, argc, argv, &args);
        if (rv) {
            LOGLIB_ERROR(
                    "Error parsing command arguments, argument %d: %s\n",
                    curr_arg, strerror(rv));
            usage();
            return -2;
        }

        pthread_mutex_lock(&ipmi_mutex);
        rv = ipmi_args_setup_con(args, os_hnd, NULL, &con);
        if (rv) {
            LOGLIB_ERROR( "ipmi_ip_setup_con: %s", strerror(rv));
            pthread_mutex_unlock(&ipmi_mutex);
            return -3;
        }

        rv = ipmi_open_domain("", &con, 1, setup_done, NULL, NULL, NULL, NULL, 0, &domain_id);
        if (rv) {
            LOGLIB_TRACE( "ipmi_init_domain: %s\n", strerror(rv));
            pthread_mutex_unlock(&ipmi_mutex);
            return -4;
        }

        LOGLIB_INFO("domain: %p", domain_id.domain);

        rv = init_alert_shmbuf(ACCESS_EVENTS_MEMORY);
        if (rv != 0) {
            LOGLIB_ERROR( "init_alert_shmbuf: %x\n", rv);
            pthread_mutex_unlock(&ipmi_mutex);
            leave();
        }

        rv = os_hnd->alloc_timer(os_hnd, &check_timer);
        if (rv) {
            LOGLIB_ERROR( "alloc_timer: %x\n", rv);
            fprintf(stdout, "alloc_timer: %x\n", rv);
            pthread_mutex_unlock(&ipmi_mutex);
            //rv = ipmi_args_setup_con(args, os_hnd, sel, &con);
            leave();
        }
     /*
         * Wait until setup_done() thread has been ready, otherwise later timer
         * thread sensor_check_timeout may core
         */
        while(domain_id.domain == NULL) {
            LOGLIB_INFO("Waiting ipmi smi connection ready...\n");
            sleep(5);
        }

        /* Extend the delay to pool BMC sensors to ensure the setup_done() finished */
        check_timeout.tv_sec = 10;
        check_timeout.tv_usec = 0;
        os_hnd->start_timer(os_hnd,
                            check_timer, &check_timeout, sensor_check_timeout, NULL);

        rv = os_hnd->create_thread(os_hnd, 0, sensor_thread, os_hnd);
        if (rv) {
            LOGLIB_ERROR( "create sensor_thread: %x\n", rv);
            pthread_mutex_unlock(&ipmi_mutex);
            return -5;
        }

      上面所有可能异常退出的地方都没有发现打印信息,因此可以判断程序没有异常退出。奇怪的是上面gdb信息表明,domain_id.domain这个指针为空,就说明它还没有被初始化。但是setup_done()已经早早让 ipmi_open_domain()去执行了,难道它没有被执行完而timer线程就已经执行了?

    3. 代码证实

    通过研究open_ipmi_domain()函数源代码,可以看到这个函数最后一个参数为空和不为空的处理差异:

    为空的时候: domain_id被放入到一个初始化队列里面,这样就导致setup_done()可能延迟很晚执行。
    之所以这么说,是因为open_ipmi_domain()会调用:
    rv = ipmi_domain_add_connect_change_handler(domain,
                                con_change_handler,
                                con_change_cb_data);

    而 ipmi_domain_add_connect_change_handler()实现如下:
    int ipmi_domain_add_connect_change_handler(ipmi_domain_t      *domain,
                           ipmi_domain_con_cb handler,
                           void               *cb_data)
    {
        if (locked_list_add(domain->con_change_handlers, handler, cb_data))
        return 0;
        else
        return ENOMEM;
    }


可以看到回调函数被放到一个list里面,被统一的scheduler调度执行。因此很可能这个open_ipmi_domain()函数返回之后, setup_done()都没有得到执行。


而当open_ipmi_domain()最后一个参数不为空的时候:

domin_id会在open_ipmi_domain()返回之前初始化完成,这个可以由open_ipmi_domain()中的这两行证实:

if (new_domain)
    *new_domain = ipmi_domain_convert_to_id(domain);

通过上面的分析,再结合我们自己写的openIPMI代码,可以看到确实存在timer线程已经开始执行但setup_done()还没有开始执行的可能,这就会导致段错误。

4.实验验证

根据上面的分析,把上面open_ipmi_domain()的最后一个参数设置成了我们期望用到的domain_id的地址,理论上就不会再出现上面的段错误了。修改完了代码之后,反复重复同样的测试,整晚上都没有重现那个问题。

5.经验教训

虽然上面的这部分代码是参考 标准OpenIPMI-2.0.21/sample/sample2.c实现的,但实际在BMC比较繁忙的时候还会出现段错误。可见,开源的代码并不能(当然也没有义务)保证没有隐患。另外,在调试这个问题的过程中,刚开始由于对OpenIPMI源代码太多的恐惧感和陌生感,迟迟没有深入阅读被引用到的open_ipmi_domain()的具体实现,导致花费了比较多的时间去猜测和使用,但总达不到看完了open_ipmi_domain()之后恍然大悟的快感。因此今后碰到类似问题,一定要深入源代码、勇于阅读相关的代码,哪怕它最不熟悉,它的执行逻辑一定是确定的,虽然我们的猜测可能不确定。





















本文转自存储之厨51CTO博客,原文链接: http://blog.51cto.com/xiamachao/1932723,如需转载请自行联系原作者


相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
相关文章
|
数据采集 存储 NoSQL
Python爬虫Cookies 池的搭建
python爬虫Cookie池架构,实现
427 0
|
1月前
|
机器学习/深度学习 算法 前端开发
别再用均值填充了!MICE算法教你正确处理缺失数据
MICE是一种基于迭代链式方程的缺失值插补方法,通过构建后验分布并生成多个完整数据集,有效量化不确定性。相比简单填补,MICE利用变量间复杂关系,提升插补准确性,适用于多变量关联、缺失率高的场景。本文结合PMM与线性回归,详解其机制并对比效果,验证其在统计推断中的优势。
832 11
别再用均值填充了!MICE算法教你正确处理缺失数据
|
2月前
|
机器学习/深度学习 存储 人工智能
AWQ: 面向设备端大语言模型压缩与加速的激活感知权重量化——论文阅读
AWQ是一种面向设备端大语言模型(LLM)压缩与加速的激活感知权重量化方法。与传统基于权重大小的量化策略不同,AWQ通过分析输入激活分布识别关键权重通道,并采用按通道缩放策略,在保持硬件效率的同时显著提升量化精度。实验表明,AWQ在多种LLM上实现了接近无损的低比特量化(如INT4),并在边缘设备上实现高达3倍以上的推理加速,为大模型的设备端部署提供了高效解决方案。
299 1
|
4月前
|
移动开发 人工智能 JavaScript
基于TypeScript + Vue3 打造以AI驱动的低代码平台
VTJ低代码开发平台(LCDP)是一个支持快速创建和部署应用的多平台开发环境,采用Vue.js与NestJS技术栈,适用于Web、移动H5及UniApp场景。
382 14
|
4月前
|
存储 缓存 固态存储
分区会不会影响固态硬盘的性能呢
固态硬盘(SSD)分区是否影响性能?本文详解分区对SSD的影响,解答“该不该分”“如何分更合理”等问题。分区本质上是逻辑划分,不影响SSD核心性能。只要合理操作,分区不会拖慢速度。建议根据使用场景选择分区策略,如系统与数据分离、游戏分区等,以提升管理效率和使用体验。
|
7月前
|
人工智能 算法 安全
OpenRouter 推出百万 token 上下文 AI 模型!Quasar Alpha:提供完全免费的 API 服务,同时支持联网搜索和多模态交互
Quasar Alpha 是 OpenRouter 推出的预发布 AI 模型,具备百万级 token 上下文处理能力,在代码生成、指令遵循和低延迟响应方面表现卓越,同时支持联网搜索和多模态交互。
631 1
OpenRouter 推出百万 token 上下文 AI 模型!Quasar Alpha:提供完全免费的 API 服务,同时支持联网搜索和多模态交互
|
机器学习/深度学习 数据可视化 数据挖掘
时间序列预测:探索性数据分析和特征工程的实用指南
时间序列分析在数据科学和机器学习中广泛应用于预测,如金融、能源消耗和销售。随着技术发展,除了传统统计模型,机器学习(如树模型)和深度学习(如LSTM、CNN和Transformer)也被应用。探索性数据分析(EDA)是预处理关键步骤,它通过Pandas、Seaborn和Statsmodel等Python库进行。本文展示了时间序列分析模板,包括描述性统计、时间图、季节图、箱形图、时间序列分解和滞后分析。使用Kaggle的小时能耗数据集,展示了如何通过这些方法揭示数据模式、季节性和趋势,为特征工程提供见解。
351 3
|
10月前
|
人工智能 缓存 自然语言处理
阿里云 × 天润融通:基于智能体的企业营销与客户服务实践分享
本次分享由阿里云与天润融通联合呈现,主题为“基于智能体的企业营销与客户服务实践”。主讲人安静波(北京天润融通科技股份有限公司CTO)将介绍天润融通的智能体平台架构及其在企业营销和客服场景中的应用。内容涵盖天润融通的发展历程、基于阿里云的AICC架构、智能体平台的技术细节及优化实践,并通过客户案例展示如何通过智能体提升营销转化率和客户满意度。重点探讨了智能体在实时响应、打断处理等方面的优化措施,以及大模型的应用经验。
651 0
|
移动开发 定位技术 iOS开发
HTML5 Geolocation(地理定位)3
本页介绍如何在地图上显示用户位置,并利用地理定位获取给定位置的详细信息,包括更新本地信息、显示周边兴趣点及实现车载导航。通过 `getCurrentPosition()` 方法可获取用户当前位置的详细数据,如经纬度、精度等。`watchPosition()` 方法则持续跟踪用户位置变化,适用于实时导航。示例代码展示了如何使用这些方法获取并显示位置信息。
|
机器学习/深度学习 Python
深度学习笔记(九):神经网络剪枝(Neural Network Pruning)详细介绍
神经网络剪枝是一种通过移除不重要的权重来减小模型大小并提高效率的技术,同时尽量保持模型性能。
591 0
深度学习笔记(九):神经网络剪枝(Neural Network Pruning)详细介绍