【openbmc3】时间相关
文章目录
- 1.时间转换
- 1.1 案例
- 2.sel时间设置
- 3.rtc&ntp
- 3.1 rtc时间异常
- 3.2 ntp
- 4.sel时区设置
- 5.phosphor-time-manager模块
- 6.deadline_timer(闹钟)与steady_timer(秒表)
1.时间转换
reboot启动systemd时systemd/src/core/main.c文件中initialize_clock函数会将系统时间改变,所以要注释掉。如下时间戳=time_t
,typedef long time_t(time_t编译时被替换成long):time_t不符合习惯需转成表示时间结构体即struct_time=struct tm。
如下time函数作用是将当前时间的时间戳赋值给tnow变量。
struct tm //time.h
{int tm_sec; /* 秒,范围从 0 到 59 */int tm_min; /* 分,范围从 0 到 59 */int tm_hour; /* 小时,范围从 0 到 23 */int tm_mday; /* 一月中的第几天,范围从 1 到 31 */int tm_mon; /* 月,范围从 0 到 11, 0代表1月*/int tm_year; /* 自 1900 年起的年数 */int tm_wday; /* 一周中的第几天,范围从 0 到 6,0代表星期天 */int tm_yday; /* 一年中的第几天,范围从 0 到 365,0代表1月1日 */int tm_isdst; /* 夏令时 */long int tm_gmtoff; /*指定了日期变更线东面时区中UTC东部时区正秒数或UTC西部时区的负秒数*/const char *tm_zone; /*当前时区的名字(与环境变量TZ有关)*/
};
bmc_sys_data = remote.command("date +%s")
bmc_date_string = remote.command("hwclock | awk '{print $1, $2, $3, $4, $5}'")
bmc_date_obj = datetime.datetime.strptime(bmc_date_string.strip(), "%a %b %d %H:%M:%S %Y")
bmc_rtc_data = int(time.mktime(bmc_date_obj.timetuple()))
if abs(int(bmc_sys_data) - bmc_rtc_data) > 5:ret = E.EFAILself.fail_reason.append("bmc sys time is {}s and bmc rtc time is is {}s, the difference is over 5 seconds.".format(bmc_sys_data, bmc_rtc_data))
elif abs(int(time.time()) - int(bmc_sys_data)) > 5:ret = E.EFAILself.fail_reason.append("cpu sys time is {}s and bmc sys time is is {}s, the difference is over 5 seconds.".format(int(time.time()), bmc_sys_data))
else:self.logger.log_info("bmc sys time ({}s) and bmc rtc time ({}s) and cpu sys time ({}s) is normal, the difference is less than 5 seconds.".format(bmc_sys_data, bmc_rtc_data , int(time.time())), also_print_console)
1.1 案例
# ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 修改为CST(reboot后恢复)。
# UTC【比GMT正式】和GMT都是世界标准时(0经度线【竖】)。 北京时间(CST)位于东八区=世界时+8小时。
# date -s 2022.05.05-09:47:00 # date -s "2021-01-01 1:00:00" # date +%w 星期几
# busctl get-property xyz.openbmc_project.Time.Manager /xyz/openbmc_project/time/bmc xyz.openbmc_project.Time.EpochTime Elapsed
# t 1651744027250894 (去除6个0就是时间戳即秒)# date +"%Y %m %d %H %M %S" (加号前面有空格,后面没有) # 2022(年) 08(月) 05(日) 02(小时) 43(分) 56(秒)
# date +"%s" # 1659667469(转换时间时注意选择转成北京时还是标准时)
# date -d @1686908998 # Fri Jun 16 09:49:58 UTC 2023
# date(date命令默认将时间戳转为UTC显示)#include<stdio.h>
#include<time.h>
int main()
{struct tm *info;time_t curtime;time( &curtime ); # 返回当前时间戳printf("时间戳: %ld\n",curtime);info = localtime( &curtime ); # 时间戳转为tm结构体指针printf("结构体:时:%d\n",info->tm_hour);char tmp[64];strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S",info); # 将tm结构体转换为时间字符串printf("时间字符串:%s\n",tmp);struct tm mytm;char tmp1[50] = "2022-02-16 15:20:00";# strptime:将时间字符串转换为tm结构体 # 如果不匹配"%Y%m-%d %H:%M:%S" ,返回0int ret=strptime(tmp1, "%Y-%m-%d %H:%M:%S", &mytm); printf("strptime返回值: %d\n",ret);printf("结构体:时:%d\n",mytm.tm_hour);
}
// time.cpp
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>void GetSystemTime(char *buffer)
{struct tm *td;struct tm tdres;struct timeval now; // 包含秒数和微秒数gettimeofday(&now, 0);td = localtime_r((time_t *)&(now.tv_sec), &tdres);if(td == NULL){return;}sprintf(buffer, "%2.2d—%2.2d—%2.2d %2.2d:%2.2d:%2.2d:3.3d", (td->tm_year+1900), (td->tm_mon+1), td->tmday, td->tm_hour, td->tm_min, td->tm_sec, (long)(now.tv_usec/1000));
}int main()
{while(1) {char dataTime[1024] = {0};GetSystemTime(dataTime);printf("dataTime:%s\n", dataTime);sleep(1); //每隔一秒打印一次当前时间}return 0;
}
#include <stdio.h>
#include <time.h>
int main() {time_t timestamp = time(NULL);char buffer[26];ctime_r(×tamp, buffer);printf("Current time: %s", buffer); // Current time: Mon Jul 31 13:24:49 2023return 0;
}void writeTimestampToBuffer(bufStore *buf) {struct tm *info;time_t cur_time;int years;char dateBuff[10];time(&cur_time);info = localtime( &cur_time );years = 1900 + info->tm_year;sprintf(dateBuff, "%d ", years); // 只记录年,因为映射成local3时用rsyslog.conf中template中没有年 (local3.* :omfile:$consolefile_channel 【/var/log/console.log】;)writeData(buf->buf_fd, dateBuff, strlen(dateBuff), "xxxx"); // buf->buf_fd = /var/log/console.log,将dateBuff写入文件
}
2.sel时间设置
Network Function Codes: // RAW Commands: raw <netfn> <cmd> [data]VAL HEX STRING0 0x00 Chassis2 0x02 Bridge4 0x04 SensorEvent6 0x06 Application8 0x08 Firmware10 0x0a Storage12 0x0c Transport
// ipmitool sel time set "12/25/2024 13:41:08" (UTC)
// ipmitool 【-I lanplus -C 17 -U root -P 0penBmc -H ip】 sel time get
// ipmitool sel time set "now"
// 出现Set SEL Time command failed: Invalid command说明用的ipmi-oem包并删除build下tmp(或bin替换)
// ipmi_sel.c
static int ipmi_sel_set_time(struct ipmi_intf * intf, const char * time_string)
{struct ipmi_rs *rsp;struct ipmi_rq req;struct tm tm = {0}; // tm结构体time_t t; // 时间戳time_t始终是UTC时间uint32_t timei;const char * time_format = "%m/%d/%Y %H:%M:%S";memset(&req, 0, sizeof(req));req.msg.netfn = IPMI_NETFN_STORAGE; // 0xareq.msg.cmd = IPMI_SET_SEL_TIME; // 0x49if (strncasecmp(time_string, "now", 3) == 0) {t = time(NULL); // 当前时间的时间戳}else {if (strptime(time_string, time_format, &tm) == 0) {lprintf(LOG_ERR, "Specified time could not be parsed");return -1;}tm.tm_isdst = (-1); // 结合下一行,自动处理时区t = mktime(&tm); // t就是UTC时间戳if (t < 0) {lprintf(LOG_ERR, "Specified time could not be parsed");return -1;}}timei = (uint32_t)t;
#if WORDS_BIGENDIANtimei = BSWAP_32(timei);
#endifreq.msg.data = (void *)&timei;req.msg.data_len = 4; // ipmitool -I lanplus -C 17 -U root -P 0penBmc -H 10.75.137.81 raw 0x0a 0x49 0xce 0x8c 0xeb 0x62 # 时间戳62EB8CCE转十进制rsp = intf->sendrecv(intf, &req);if (!rsp || rsp->ccode) {lprintf(LOG_ERR, "Set SEL Time command failed: %s",rsp? val2str(rsp->ccode, completion_code_vals): "Unknown"); // rsp不为空则将rsp->ccode转换为相应字符串,否则打印Unknownreturn -1;}ipmi_sel_get_time(intf);return 0;
}// ipmi_strings.c
const struct valstr completion_code_vals[] = {{ 0x00, "Command completed normally" },{ 0xc0, "Node busy" },{ 0xc1, "Invalid command" }...// storagecommands.cpp
ipmi::RspType<> ipmiStorageSetSELTime(uint32_t selTime) // selTime是传来的时间戳,一定要加这参数,不然ipmitool返回有问题
{struct timespec ts = {};ts.tv_sec = selTime; // 秒数即时间戳ts.tv_nsec = 0;if (clock_settime(CLOCK_REALTIME, &ts) < 0) // == 0 成功{return ipmi::responseUnspecifiedError();}return ipmi::responseSuccess();
}
time_t str2stamp(std::string str_time)
{time_t t;struct tm tm = {0}; strptime....tm.tm_isdst = (-1);t = mktime(&tm);struct tm *tm_tmp = {0};int gt_year,gt_yday,gt_hour,gt_min,lt_year,lt_yday,lt_hour,lt_min;int delta_hour;tm_tmp=gmtime(&t); // 返回的是 UTC时间的结构体gt_year=tm_tmp->tm_year;gt_yday=tm_tmp->tm_yday;gt_hour=tm_tmp->tm_hour;gt_min=tm_tmp->tm_min;memset(&*tm_tmp,0,sizeof(struct tm));tm_tmp=localtime(&t); // 返回的是 UTC时间的结构体lt_year=tm_tmp->tm_year;lt_yday=tm_tmp->tm_yday;lt_hour=tm_tmp->tm_hour;lt_min=tm_tmp->tm_min;delta_hour=lt_hour - gt_hour;if ( (lt_year > gt_year) || ((lt_year == gt_year) && (lt_yday > gt_yday)) )delta_hour += 24;if ( (lt_year < gt_year) || ((lt_year == gt_year) && (lt_yday < gt_yday)) )delta_hour -= 24;t += (delta_hour * 60 * 60) + (lt_min -gt_min) * 60; // 当前UTC时间是1622505600(对应2021-06-01 00:00:00 UTC),而用户位于UTC+8时区,本地时间戳应该是1622505600 + 8*3600 = 1622534400return t;
}
3.rtc&ntp
不加驱动hwclock -f /dev/rtc查不了硬件时间,timedatectl show,如下上电注册RTC驱动时probe会将RTC时间写入系统时间,后面系统时间NTP同步并写入RTC。
&i2c2 {status = "okay";rtc@51{ # rtc随便命名compatible = "nxp,pcf85063";reg = <0x51>;};
};
/etc/ntp.conf:restrict (什么可访问我并可修改我的时间)10.75.92.0 mask 255.255.255.0 nomodify(nomodify不能修改我,只能访问同步走)。修改后隔10分钟才去同步一次,重启ntp进程立马同步。
ntp协议在linux-aspeed/drivers/rtc/systohc.c中rtc_set_ntp_time函数将系统时间写入rtc ,不用下面write_rtc。
write_rtc()
{if [ ! -d /etc/cron/crontabs ];thenmkdir -p /etc/cron/crontabsfiecho "0 0 * * * hwclock --systohc" >> /etc/cron/crontabs/root # 同hwclock -w:将系统时间写入RTC # hwclock --hctosys:将RTC写入系统时间/usr/sbin/crond -c /etc/cron/crontabs &
}
当系统时间和rtc一致时,systemd-timesyncd失效,如下关闭timesyncd,新基线如下_
改为:
。
如下是bmc内部rtc。
如下如果系统是UTC时区,则不用加-u(UTC)。
3.1 rtc时间异常
越界异常,导致主备来回切换,不能进系统。info等级改为debug。
如下模拟时间戳就是上面2040年,出现上面打印,因为时间戳超出32位整数范围。
RTC芯片手册,如下就是2040年,芯片里异常时间来源问厂家。
如下对越界的时间会做异常处理。
3.2 ntp
第一个ntp server ip生效后,第二个和后面不生效,依此类推(无法手动设置时间)。
根据web前端提示” Error saving date and time settings.”定位到如下代码:
openbmc-ocp/build/sr6115/workspace/sources/webui-vue/src/locales/en-US.json。
"toast": {"errorSaveDateTime": "Error saving date and time settings.","successSaveDateTime": "Successfully saved date and time settings."}
搜索errorSaveDateTime 定位到如下代码:
openbmc-ocp/build/sr6115/workspace/sources/webui-vue/src/store/modules/Settings/DateTimeStore.js。
const DateTimeStore = {...actions: {async getNtpData({ commit }) {return await api.get('/redfish/v1/Managers/bmc/NetworkProtocol').then((response) => {const ntpServers = response.data.NTP.NTPServers;const isNtpProtocolEnabled = response.data.NTP.ProtocolEnabled;commit('setNtpServers', ntpServers);commit('setIsNtpProtocolEnabled', isNtpProtocolEnabled);}).catch((error) => {console.log(error);});},async updateDateTime({ state }, dateTimeForm) {const ntpData = {NTP: {ProtocolEnabled: dateTimeForm.ntpProtocolEnabled,},};// console.log(dateTimeForm.ntpProtocolEnabled); // 前端按Fn + F12看到控制台打印为trueif (dateTimeForm.ntpProtocolEnabled) { ntpData.NTP.NTPServers = dateTimeForm.ntpServersArray;// console.log(ntpData.NTP.NTPServers); //打印出['10.10.10.1', '10.10.10.2'] 即web页面填写的ntp server ip}return await api.patch(`/redfish/v1/Managers/bmc/NetworkProtocol`, ntpData).then(async () => {if (!dateTimeForm.ntpProtocolEnabled) { // web页面手动设置时间// ...下面包含errorSaveDateTime
根据/redfish/v1/Managers/bmc/NetworkProtocol定位到如下代码:
openbmc-ocp/build/sr6115/workspace/sources/bmcweb/redfish-core/lib/network_protocol.hpp。
inline void requestRoutesNetworkProtocol(App& app)
{BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/NetworkProtocol/").privileges(redfish::privileges::patchManagerNetworkProtocol).methods(boost::beast::http::verb::patch)([&app](const crow::Request& req, // patch方法const std::shared_ptr<bmcweb::AsyncResp>&asyncResp) {std::optional<std::vector<std::string>> ntpServers; // 注意是optional// sprintf(temp_log_0, " '%s' ", ntpServers?"true":"false"); 不为空则为truestd::optional<bool> ntpEnabled;std::optional<bool> ipmiEnabled;std::optional<bool> sshEnabled;// clang-format offif (!json_util::readJsonPatch(req, asyncResp->res,"HostName", newHostName,"NTP/NTPServers", ntpServers,"NTP/ProtocolEnabled", ntpEnabled,"IPMI/ProtocolEnabled", ipmiEnabled,"SSH/ProtocolEnabled", sshEnabled)){return;}...std::vector<std::string>& ntpServers_t = *ntpServers; // 注意是vectorfor (unsigned int ii=0;ii<ntpServers_t.size();ii++){char temp_log_0[100] = {0};sprintf(temp_log_0, " %s ", ntpServers_t[ii].c_str()); char temp_log_1[100] = "echo ";strcat(temp_log_1, temp_log_0);char temp_log_2[100] = " >> /var/log/a.log"; //server ipstrcat(temp_log_1, temp_log_2);if(-1 == std::system(temp_log_1)){};}if (ntpServers){stl_utils::removeDuplicate(*ntpServers);handleNTPServersPatch(asyncResp, *ntpServers); // patch方法具体调用的函数}...BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/NetworkProtocol/").privileges(redfish::privileges::getManagerNetworkProtocol).methods(boost::beast::http::verb::get)( // 这里是get方法 [&app](const crow::Request& req,const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)){return;}getNetworkData(asyncResp, req);});
}
} // namespace redfish// 定位到如下具体的方法:
inline void handleNTPServersPatch(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,std::vector<std::string>& ntpServers)
{...crow::connections::systemBus->async_method_call([asyncResp,ntpServers](boost::system::error_code ec,const dbus::utility::MapperGetSubTreeResponse& subtree) {if (ec){BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", "<< ec.message();messages::internalError(asyncResp->res);return;}for (const auto& [objectPath, serviceMap] : subtree){for (const auto& [service, interfaces] : serviceMap){for (const auto& interface : interfaces){if (interface !="xyz.openbmc_project.Network.EthernetInterface"){continue;}/* char temp_log_0[100] = {0};sprintf(temp_log_0, " %s %s", service.c_str(), objectPath.c_str());char temp_log_1[100] = "echo ";strcat(temp_log_1, temp_log_0);char temp_log_2[100] = " >> /var/log/b.log";strcat(temp_log_1, temp_log_2);if(-1 == std::system(temp_log_1)){}; // xyz.openbmc_project.Network /xyz/openbmc_project/network/eth0 */crow::connections::systemBus->async_method_call([asyncResp](const boost::system::error_code ec) {if (ec){messages::internalError(asyncResp->res);return;}},service, objectPath,"org.freedesktop.DBus.Properties", "Set", interface, // set方法"NTPServers",dbus::utility::DbusVariantType{ntpServers});}}}},"xyz.openbmc_project.ObjectMapper","/xyz/openbmc_project/object_mapper","xyz.openbmc_project.ObjectMapper", "GetSubTree", // get方法"/xyz/openbmc_project", 0,std::array<const char*, 1>{"xyz.openbmc_project.Network.EthernetInterface"});
}
root@sr6115:~# busctl introspect xyz.openbmc_project.Network /xyz/openbmc_project/network/eth0 xyz.openbmc_project.Network.EthernetInterface
...
.NICEnabled property b true emits-change writable
.NTPServers property as emits-change
.Nameservers property as 2 "192.168.130.50" "192.168.30.50" emits-change writable.StaticNTPServers property as 0 emits-change writable
.StaticNameServers property as 0 emits-change writable
发现NTPServers属性是没办法writable,无法写,github搜索EthernetInterface定位到如下phosphor-networkd模块,devtool modify phosphor-networkd发现无法下载,search network相关模块后devtool modify phosphor-network
可下载该模块。
如下发现web前端填写ntp server ip后并保存触发,并没有调用到ntpServers函数,但是当上电启动后会调用getNTPServersFromConf函数,打印出confpath为/etc/systemd/network/00-bmc-eth0.network
,发现getNTPServersFromConf函数里和writeConfigurationFile函数里的组成confPath代码段相同,手动改这文件,增加[Network] NTP=10.10.10.1 字段后systemctl restart systemd-networkd.service,date查看时间后后发现时间已同步到远程时间。
// phosphor-network/src/ethernet_interface.hpp
ServerList EthernetInterface::getNTPServersFromConf()
{fs::path confPath = manager.getConfDir();std::string fileName = systemd::config::networkFilePrefix +interfaceName() + systemd::config::networkFileSuffix;confPath /= fileName;ServerList servers;config::Parser parser(confPath.string());auto rc = config::ReturnCode::SUCCESS;std::tie(rc, servers) = parser.getValues("Network", "NTP");if (rc != config::ReturnCode::SUCCESS){log<level::DEBUG>("Unable to get the value for Network[NTP]",entry("rc=%d", rc));}return servers;
}ServerList EthernetInterface::ntpServers(ServerList servers)
{auto ntpServers = EthernetInterfaceIntf::ntpServers(servers);writeConfigurationFile();manager.reloadConfigs();return ntpServers;
}void EthernetInterface::writeConfigurationFile()
{
...fs::path confPath = manager.getConfDir();std::string fileName = systemd::config::networkFilePrefix +interfaceName() + systemd::config::networkFileSuffix;confPath /= fileName;std::fstream stream;
...// Add the NTP server ////////////////////////////////////for (const auto& ntp : EthernetInterfaceIntf::ntpServers()){stream << "NTP=" << ntp << "\n";}
...
}
浏览器登录https://10.75.137.56/#/settings/date-time,ip地址改为开发板ip,如下设置NTP服务ip地址。设置完后点击下Save settings按钮,到开发板上date命令或ipmitool sel time get命令查看时间是否与NTP server 时间一样。
// redfish接口调用测试方法:
root@sr:~# curl -k -H "Content-Type: application/json" -X POST https://10.75.137.51/login -d '{"username" : "root", "password" : "0penBmc"}' | grep token | awk '{print $2;}' | tr -d '"'% Total % Received % Xferd Average Speed Time Time Time CurrentDload Upload Total Spent Left Speed
100 84 100 37 100 47 111 141 --:--:-- --:--:-- --:--:-- 253
uMaeeLkbARcYgINNx7kFroot@sr:~# curl -k -H "X-Auth-Token: uMaeeLkbARcYgINNx7kF" -X GET https://10.75.137.51/redfish/v1/Managers/bmc/NetworkProtocol/
{
..."NTP": {"NTPServers": [],"ProtocolEnabled": true},
...
}root@sr:~# curl -k -H "X-Auth-Token: uMaeeLkbARcYgINNx7kF" -X PATCH -d '{"NTP":{"NTPServers": ["10.10.10.1"], "ProtocolEnabled": true}}' https://10.75.137.51/redfish/v1/Managers/bmc/NetworkProtocol/ root@sr:~# curl -k -H "X-Auth-Token: uMaeeLkbARcYgINNx7kF" -X GET https://10.75.137.51/redfish/v1/Managers/bmc/NetworkProtocol/
{"@odata.id": "/redfish/v1/Managers/bmc/NetworkProtocol","@odata.type": "#ManagerNetworkProtocol.v1_5_0.ManagerNetworkProtocol","Description": "Manager Network Service","FQDN": "sr6115","HTTP": {"Port": 0,"ProtocolEnabled": false},"HTTPS": {"Certificates": {"@odata.id": "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"},"Port": 443,"ProtocolEnabled": true},"HostName": "sr6115","IPMI": {"Port": 623,"ProtocolEnabled": true},"Id": "NetworkProtocol","NTP": { ///////////"NTPServers": ["10.10.10.1"],"ProtocolEnabled": true},"Name": "Manager Network Protocol","SSH": {"Port": 22,"ProtocolEnabled": true},"Status": {"Health": "OK","HealthRollup": "OK","State": "Enabled"}
}
4.sel时区设置
改完时区,rtc和系统的时区和时间都会变。
// workspace/sources/phosphor-ipmi-host/include/ipmid/api-types.hpp
constexpr Cmd cmdGetSelTimeUtcOffset = 0x5C;
constexpr Cmd cmdSetSelTimeUtcOffset = 0x5D;// workspace/sources/phosphor-ipmi-host/dbus-sdr/storagecommands.cpp
void registerStorageFunctions() {// <Set SEL Time>// <Get SEL Time UTC OFFSET>ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,ipmi::storage::cmdGetSelTimeUtcOffset, ipmi::Privilege::User,ipmiStorageGetSelTimeUtcOffset);// <Set SEL Time UTC OFFSET>ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,ipmi::storage::cmdSetSelTimeUtcOffset,ipmi::Privilege::Operator, ipmiStorageSetSelTimeUtcOffset); }
Yocto添加时区软件包在image.bb添加tzdata,产生/usr/share/zoneinfo对应文件夹,注意在openbmc-ocp/build/sr6115/conf/local.conf 或openbmc-ocp/meta-huaqin/meta-sr6115/conf/local.conf.sample添加DEFAULT_TIMEZONE = “Asia/Shanghai” 都无法产生时区相关文件。
// workspace/sources/phosphor-ipmi-host
#define TIME_ZONE_PATH "/usr/share/zoneinfo/"
#define ZONEINFO_DIR "/usr/share/zoneinfo/Etc"
#define LOCALTIME "/etc/localtime"
#define ZONEINFO_FILENAME_LEN 128
#define SEL_UTC_MIN_RANGE -1440
#define SEL_UTC_MAX_RANGE 1440 // 1440min/60=24hipmi::RspType<int16_t> ipmiStorageGetSelTimeUtcOffset()
{time_t timep;struct tm* gmTime;struct tm* localTime;time(&timep);localTime = localtime(&timep); // 时间戳转换为本地时间auto validLocalTime = mktime(localTime);gmTime = gmtime(&timep); // 时间戳转换为协调世界时(UTC)auto validGmTime = mktime(gmTime);auto timeEquation = (validLocalTime - validGmTime) / 60; // 计算本地时间和UTC时间之间的偏移量, 秒/60 = minreturn ipmi::responseSuccess(timeEquation);
}ipmi::RspType<> ipmiStorageSetSelTimeUtcOffset(int16_t offsetTime) // offsetTime:min: 8*60
{int hrs = 0;char ZoneInfoFile[ZONEINFO_FILENAME_LEN], file[32];if ((SEL_UTC_MIN_RANGE > offsetTime) || (SEL_UTC_MAX_RANGE < offsetTime)){return ipmi::responseUnspecifiedError();}hrs = offsetTime / 60; // 小时if (0 > offsetTime){if (snprintf(file,sizeof(file), "GMT+%d", -hrs) >= (long int)sizeof(file)){return ipmi::responseUnspecifiedError();}}else{if (snprintf(file,sizeof(file), "GMT-%d", hrs) >= (long int)sizeof(file)){return ipmi::responseUnspecifiedError();}}if (snprintf (ZoneInfoFile, ZONEINFO_FILENAME_LEN, "%s/%s", ZONEINFO_DIR, file) >= ZONEINFO_FILENAME_LEN){return ipmi::responseUnspecifiedError();}if((unlink(LOCALTIME)) < 0){return ipmi::responseUnspecifiedError();}if(symlink(ZoneInfoFile, LOCALTIME) < 0){return ipmi::responseUnspecifiedError();}return ipmi::responseSuccess();
}
5.phosphor-time-manager模块
busctl set-property xyz.openbmc_project.Settings /xyz/openbmc_project/time/sync_method xyz.openbmc_project.Time.Synchronization TimeSyncMethod s “xyz.openbmc_project.Time.Synchronization.Method.Manual”
# 注意必须是换成Manual模式(还有ntp模式),否则下面无法设置。
busctl set-property xyz.openbmc_project.Time.Manager /xyz/openbmc_project/time/bmc xyz.openbmc_project.Time.EpochTime Elapsed t 微秒(比秒多6个0)
6.deadline_timer(闹钟)与steady_timer(秒表)
系统时间调整导致deadline_timer失效,替换用steady_timer。如下dbus-sensors中cpusensor app一直保持在一个固定值,可以响应 IO 事件(dbus)即进程处于alive状态。peci读取没有出现异常数据。
cat /proc/460/stack # 查看进程stack, 进程停留在poll地方, 并且可以正常使用dbus访问, 表示进程io正常, 没卡在奇怪的地方
[<0>] do_epoll_wait+0x5dc/0x710
[<0>] sys_epoll_wait+0x68/0xa4
[<0>] __sys_trace_return_nosave+0x10/0x10
[<0>] 0x7ea400d8# 分析代码不存在逻辑陷阱导致读取的timer被取消了,用strace查看进程的系统调用是否正常,是不是有访问驱动文件出错
strace -p 460 -tttT -s 2048 -o ./strace.log
# 等待了1min 左右(代码里最长的timer大概10s), log如下: 发现没有任何系统调用, 很奇怪因为如果timer还活着的话会产生io的系统调用事件, 用于触发回调
1686908998.016880 epoll_wait(5, <detached ...>
# 初步结论: timer被设置的时间特别长或者timer被取消了(strace中无timerfd相关调用:定时器未正确初始化或已取消)