blob: d2c136bdbd0be478b5b12a56ad995deeaff03045 [file] [log] [blame]
Carol Wang71230ef2020-02-18 17:39:49 +08001#include "scheduled_host_transition.hpp"
Andrew Geisslere426b582020-05-28 12:40:55 -05002
Carol Wangdc059392020-03-13 17:39:17 +08003#include "utils.hpp"
Andrew Geissler68a8c312021-12-03 14:26:23 -06004#include "xyz/openbmc_project/State/Host/server.hpp"
Carol Wang71230ef2020-02-18 17:39:49 +08005
Andrew Geisslere426b582020-05-28 12:40:55 -05006#include <sys/timerfd.h>
7#include <unistd.h>
8
9#include <cereal/archives/json.hpp>
Carol Wang4ca6f3f2020-02-19 16:28:59 +080010#include <phosphor-logging/elog-errors.hpp>
11#include <phosphor-logging/elog.hpp>
Andrew Geissler8ffdb262021-09-20 15:25:19 -050012#include <phosphor-logging/lg2.hpp>
Carol Wang4ca6f3f2020-02-19 16:28:59 +080013#include <xyz/openbmc_project/ScheduledTime/error.hpp>
Patrick Williams9a286db2024-01-17 06:29:47 -060014#include <xyz/openbmc_project/State/Host/error.hpp>
Andrew Geisslere426b582020-05-28 12:40:55 -050015
Carol Wang4ca6f3f2020-02-19 16:28:59 +080016#include <chrono>
Carol Wang1dbbef42020-03-09 11:51:23 +080017#include <filesystem>
18#include <fstream>
Carol Wangef7abe12020-02-25 16:19:34 +080019
20// Need to do this since its not exported outside of the kernel.
21// Refer : https://gist.github.com/lethean/446cea944b7441228298
22#ifndef TFD_TIMER_CANCEL_ON_SET
23#define TFD_TIMER_CANCEL_ON_SET (1 << 1)
24#endif
25
Carol Wang71230ef2020-02-18 17:39:49 +080026namespace phosphor
27{
28namespace state
29{
30namespace manager
31{
32
Andrew Geissler8ffdb262021-09-20 15:25:19 -050033PHOSPHOR_LOG2_USING;
34
Carol Wang1dbbef42020-03-09 11:51:23 +080035namespace fs = std::filesystem;
36
Carol Wang4ca6f3f2020-02-19 16:28:59 +080037using namespace std::chrono;
38using namespace phosphor::logging;
39using namespace xyz::openbmc_project::ScheduledTime;
40using InvalidTimeError =
41 sdbusplus::xyz::openbmc_project::ScheduledTime::Error::InvalidTime;
Carol Wang71230ef2020-02-18 17:39:49 +080042using HostTransition =
Patrick Williams7e969cb2023-08-23 16:24:23 -050043 sdbusplus::server::xyz::openbmc_project::state::ScheduledHostTransition;
44using HostState = sdbusplus::server::xyz::openbmc_project::state::Host;
Carol Wang71230ef2020-02-18 17:39:49 +080045
Carol Wang6a5db3d2020-02-21 10:12:01 +080046constexpr auto PROPERTY_TRANSITION = "RequestedHostTransition";
Andrew Geissler68a8c312021-12-03 14:26:23 -060047constexpr auto PROPERTY_RESTART_CAUSE = "RestartCause";
Carol Wang6a5db3d2020-02-21 10:12:01 +080048
Carol Wang71230ef2020-02-18 17:39:49 +080049uint64_t ScheduledHostTransition::scheduledTime(uint64_t value)
50{
Manojkiran Eda3ff5a362024-06-17 10:59:07 +053051 info("A scheduled host transition request has been made for {TIME}", "TIME",
Andrew Geissler60a238d2024-01-03 07:29:45 -060052 value);
Carol Wang4ca6f3f2020-02-19 16:28:59 +080053 if (value == 0)
54 {
55 // 0 means the function Scheduled Host Transition is disabled
Carol Wang6a5db3d2020-02-21 10:12:01 +080056 // Stop the timer if it's running
57 if (timer.isEnabled())
58 {
59 timer.setEnabled(false);
Andrew Geisslerad65b2d2021-09-21 12:53:29 -050060 debug(
61 "scheduledTime: The function Scheduled Host Transition is disabled.");
Carol Wang6a5db3d2020-02-21 10:12:01 +080062 }
Carol Wang4ca6f3f2020-02-19 16:28:59 +080063 }
64 else
65 {
Carol Wang6a5db3d2020-02-21 10:12:01 +080066 auto deltaTime = seconds(value) - getTime();
67 if (deltaTime < seconds(0))
68 {
Andrew Geisslerad65b2d2021-09-21 12:53:29 -050069 error(
70 "Scheduled time is earlier than current time. Fail to schedule host transition.");
Carol Wang6a5db3d2020-02-21 10:12:01 +080071 elog<InvalidTimeError>(
72 InvalidTime::REASON("Scheduled time is in the past"));
73 }
74 else
75 {
76 // Start a timer to do host transition at scheduled time
77 timer.restart(deltaTime);
78 }
Carol Wang4ca6f3f2020-02-19 16:28:59 +080079 }
Carol Wang6a5db3d2020-02-21 10:12:01 +080080
Carol Wang1dbbef42020-03-09 11:51:23 +080081 // Set scheduledTime
82 HostTransition::scheduledTime(value);
83 // Store scheduled values
84 serializeScheduledValues();
85
86 return value;
Carol Wang71230ef2020-02-18 17:39:49 +080087}
88
Carol Wang4ca6f3f2020-02-19 16:28:59 +080089seconds ScheduledHostTransition::getTime()
90{
91 auto now = system_clock::now();
92 return duration_cast<seconds>(now.time_since_epoch());
93}
94
Carol Wang6a5db3d2020-02-21 10:12:01 +080095void ScheduledHostTransition::hostTransition()
96{
Amithash Prasasdf566c962024-07-22 20:28:42 -070097 auto hostName = std::string(HostState::namespace_path::host) +
98 std::to_string(id);
99 std::string hostPath =
100 sdbusplus::message::object_path(HostState::namespace_path::value) /
101 hostName;
Carol Wang6a5db3d2020-02-21 10:12:01 +0800102
Andrew Geissler60a238d2024-01-03 07:29:45 -0600103 auto reqTrans = convertForMessage(HostTransition::scheduledTransition());
104
105 info("Trying to set requestedTransition to {REQUESTED_TRANSITION}",
106 "REQUESTED_TRANSITION", reqTrans);
107
Amithash Prasasdf566c962024-07-22 20:28:42 -0700108 utils::setProperty(bus, hostPath, HostState::interface, PROPERTY_TRANSITION,
Andrew Geissler60a238d2024-01-03 07:29:45 -0600109 reqTrans);
110
Andrew Geissler68a8c312021-12-03 14:26:23 -0600111 // Set RestartCause to indicate this transition is occurring due to a
112 // scheduled host transition as long as it's not an off request
113 if (HostTransition::scheduledTransition() != HostState::Transition::Off)
114 {
115 info("Set RestartCause to scheduled power on reason");
116 auto resCause =
117 convertForMessage(HostState::RestartCause::ScheduledPowerOn);
Amithash Prasasdf566c962024-07-22 20:28:42 -0700118 utils::setProperty(bus, hostPath, HostState::interface,
119 PROPERTY_RESTART_CAUSE, resCause);
Andrew Geissler68a8c312021-12-03 14:26:23 -0600120 }
Carol Wang6a5db3d2020-02-21 10:12:01 +0800121}
122
123void ScheduledHostTransition::callback()
124{
125 // Stop timer, since we need to do host transition once only
126 timer.setEnabled(false);
127 hostTransition();
Carol Wang1dbbef42020-03-09 11:51:23 +0800128 // Set scheduledTime to 0 to disable host transition and update scheduled
129 // values
130 scheduledTime(0);
Carol Wang6a5db3d2020-02-21 10:12:01 +0800131}
132
Carol Wangef7abe12020-02-25 16:19:34 +0800133void ScheduledHostTransition::initialize()
134{
135 // Subscribe time change event
Manojkiran Eda3ff5a362024-06-17 10:59:07 +0530136 // Choose the MAX time that is possible to avoid misfires.
Carol Wangef7abe12020-02-25 16:19:34 +0800137 constexpr itimerspec maxTime = {
Andrew Geissler63f7b1a2023-07-24 14:54:58 -0400138 {0, 0}, // it_interval
139 {system_clock::duration::max().count(), 0}, // it_value
Carol Wangef7abe12020-02-25 16:19:34 +0800140 };
141
142 // Create and operate on a timer that delivers timer expiration
143 // notifications via a file descriptor.
144 timeFd = timerfd_create(CLOCK_REALTIME, 0);
145 if (timeFd == -1)
146 {
147 auto eno = errno;
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500148 error("Failed to create timerfd, errno: {ERRNO}, rc: {RC}", "ERRNO",
149 eno, "RC", timeFd);
Carol Wangef7abe12020-02-25 16:19:34 +0800150 throw std::system_error(eno, std::system_category());
151 }
152
153 // Starts the timer referred to by the file descriptor fd.
154 // If TFD_TIMER_CANCEL_ON_SET is specified along with TFD_TIMER_ABSTIME
155 // and the clock for this timer is CLOCK_REALTIME, then mark this timer
156 // as cancelable if the real-time clock undergoes a discontinuous change.
157 // In this way, we can monitor whether BMC time is changed or not.
158 auto r = timerfd_settime(
159 timeFd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &maxTime, nullptr);
160 if (r != 0)
161 {
162 auto eno = errno;
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500163 error("Failed to set timerfd, errno: {ERRNO}, rc: {RC}", "ERRNO", eno,
164 "RC", r);
Carol Wangef7abe12020-02-25 16:19:34 +0800165 throw std::system_error(eno, std::system_category());
166 }
167
168 sd_event_source* es;
169 // Add a new I/O event source to an event loop. onTimeChange will be called
170 // when the event source is triggered.
171 r = sd_event_add_io(event.get(), &es, timeFd, EPOLLIN, onTimeChange, this);
172 if (r < 0)
173 {
174 auto eno = errno;
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500175 error("Failed to add event, errno: {ERRNO}, rc: {RC}", "ERRNO", eno,
176 "RC", r);
Carol Wangef7abe12020-02-25 16:19:34 +0800177 throw std::system_error(eno, std::system_category());
178 }
179 timeChangeEventSource.reset(es);
180}
181
182ScheduledHostTransition::~ScheduledHostTransition()
183{
184 close(timeFd);
185}
186
187void ScheduledHostTransition::handleTimeUpdates()
188{
Carol Wang1dbbef42020-03-09 11:51:23 +0800189 // Stop the timer if it's running.
190 // Don't return directly when timer is stopped, because the timer is always
191 // disabled after the BMC is rebooted.
192 if (timer.isEnabled())
Carol Wangef7abe12020-02-25 16:19:34 +0800193 {
Carol Wang1dbbef42020-03-09 11:51:23 +0800194 timer.setEnabled(false);
Carol Wangef7abe12020-02-25 16:19:34 +0800195 }
Carol Wangef7abe12020-02-25 16:19:34 +0800196
197 // Get scheduled time
198 auto schedTime = HostTransition::scheduledTime();
199
200 if (schedTime == 0)
201 {
Andrew Geisslerad65b2d2021-09-21 12:53:29 -0500202 debug(
203 "handleTimeUpdates: The function Scheduled Host Transition is disabled.");
Carol Wangef7abe12020-02-25 16:19:34 +0800204 return;
205 }
206
207 auto deltaTime = seconds(schedTime) - getTime();
208 if (deltaTime <= seconds(0))
209 {
Andrew Geissler60a238d2024-01-03 07:29:45 -0600210 try
211 {
212 hostTransition();
213 }
214 catch (const sdbusplus::exception_t& e)
215 {
Patrick Williams9a286db2024-01-17 06:29:47 -0600216 using BMCNotReady = sdbusplus::error::xyz::openbmc_project::state::
217 host::BMCNotReady;
Andrew Geissler60a238d2024-01-03 07:29:45 -0600218 // If error indicates BMC is not at Ready error then reschedule for
219 // 60s later
220 if ((e.name() != nullptr) &&
Patrick Williams9a286db2024-01-17 06:29:47 -0600221 (e.name() == std::string_view(BMCNotReady::errName)))
Andrew Geissler60a238d2024-01-03 07:29:45 -0600222 {
223 warning(
224 "BMC is not at ready, reschedule transition request for 60s");
225 timer.restart(seconds(60));
226 return;
227 }
228 else
229 {
230 throw;
231 }
232 }
Carol Wang1dbbef42020-03-09 11:51:23 +0800233 // Set scheduledTime to 0 to disable host transition and update
234 // scheduled values
235 scheduledTime(0);
Carol Wangef7abe12020-02-25 16:19:34 +0800236 }
237 else
238 {
239 // Start a timer to do host transition at scheduled time
240 timer.restart(deltaTime);
241 }
242}
243
Patrick Williams1b2c3c02024-08-16 15:20:29 -0400244int ScheduledHostTransition::onTimeChange(
245 sd_event_source* /* es */, int fd, uint32_t /* revents */, void* userdata)
Carol Wangef7abe12020-02-25 16:19:34 +0800246{
Pavithra Barithaya319eda42024-06-21 11:54:43 -0500247 auto* schedHostTran = static_cast<ScheduledHostTransition*>(userdata);
Carol Wangef7abe12020-02-25 16:19:34 +0800248
249 std::array<char, 64> time{};
250
251 // We are not interested in the data here.
252 // So read until there is no new data here in the FD
253 while (read(fd, time.data(), time.max_size()) > 0)
Pavithra Barithayab594ac12024-06-21 12:09:04 -0500254 {}
Carol Wangef7abe12020-02-25 16:19:34 +0800255
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500256 debug("BMC system time is changed");
Carol Wangef7abe12020-02-25 16:19:34 +0800257 schedHostTran->handleTimeUpdates();
258
259 return 0;
260}
261
Carol Wang1dbbef42020-03-09 11:51:23 +0800262void ScheduledHostTransition::serializeScheduledValues()
263{
264 fs::path path{SCHEDULED_HOST_TRANSITION_PERSIST_PATH};
265 std::ofstream os(path.c_str(), std::ios::binary);
266 cereal::JSONOutputArchive oarchive(os);
267
268 oarchive(HostTransition::scheduledTime(),
269 HostTransition::scheduledTransition());
270}
271
272bool ScheduledHostTransition::deserializeScheduledValues(uint64_t& time,
273 Transition& trans)
274{
275 fs::path path{SCHEDULED_HOST_TRANSITION_PERSIST_PATH};
276
277 try
278 {
279 if (fs::exists(path))
280 {
281 std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
282 cereal::JSONInputArchive iarchive(is);
283 iarchive(time, trans);
284 return true;
285 }
286 }
Patrick Williams8583b3b2021-10-06 12:19:20 -0500287 catch (const std::exception& e)
Carol Wang1dbbef42020-03-09 11:51:23 +0800288 {
Andrew Geissler8ffdb262021-09-20 15:25:19 -0500289 error("deserialize exception: {ERROR}", "ERROR", e);
Carol Wang1dbbef42020-03-09 11:51:23 +0800290 fs::remove(path);
291 }
292
293 return false;
294}
295
296void ScheduledHostTransition::restoreScheduledValues()
297{
298 uint64_t time;
299 Transition trans;
300 if (!deserializeScheduledValues(time, trans))
301 {
302 // set to default value
303 HostTransition::scheduledTime(0);
304 HostTransition::scheduledTransition(Transition::On);
305 }
306 else
307 {
308 HostTransition::scheduledTime(time);
309 HostTransition::scheduledTransition(trans);
310 // Rebooting BMC is something like the BMC time is changed,
311 // so go on with the same process as BMC time changed.
312 handleTimeUpdates();
313 }
314}
315
Carol Wang71230ef2020-02-18 17:39:49 +0800316} // namespace manager
317} // namespace state
318} // namespace phosphor