diff options
author | James Feist <james.feist@linux.intel.com> | 2019-03-12 11:20:16 -0700 |
---|---|---|
committer | Patrick Venture <venture@google.com> | 2019-04-07 16:34:02 +0000 |
commit | ce6a3f36cedc2f822fb446bc5094eaeab47eb4af (patch) | |
tree | ca2173de814e58c85e5399406213333701948a9b | |
parent | 5782ab81367e22e87d719c9fef6e85ecdc6cf95e (diff) | |
download | phosphor-pid-control-ce6a3f36cedc2f822fb446bc5094eaeab47eb4af.tar.gz phosphor-pid-control-ce6a3f36cedc2f822fb446bc5094eaeab47eb4af.zip |
Remove threads
This converts phosphor-pid-control into an async
single threaded application. The reason for doing this
is on our systems phosphor-pid-control had the largest
VSZ when viewed under top. Before this patch the VSZ
was at 50720, after it is at 7760.
Tested-by: Could still interact with all interfaces
under rest-dbus and sensor override worked to ramp fans
when changing cpu temps.
Change-Id: Ie0a837bdf0d1b1df61dc7aff87e5d503b9e0e875
Signed-off-by: James Feist <james.feist@linux.intel.com>
-rw-r--r-- | Makefile.am | 5 | ||||
-rw-r--r-- | configure.ac | 17 | ||||
-rw-r--r-- | main.cpp | 45 | ||||
-rw-r--r-- | pid/pidloop.cpp | 117 | ||||
-rw-r--r-- | pid/pidloop.hpp | 18 | ||||
-rw-r--r-- | pid/pidthread.cpp | 109 | ||||
-rw-r--r-- | pid/pidthread.hpp | 6 | ||||
-rw-r--r-- | threads/busthread.cpp | 36 | ||||
-rw-r--r-- | threads/busthread.hpp | 11 |
9 files changed, 167 insertions, 197 deletions
diff --git a/Makefile.am b/Makefile.am index 9375ea6..241878c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -55,11 +55,9 @@ setsensor_CXXFLAGS = \ swampd_SOURCES = main.cpp util.cpp swampd_LDADD = \ $(SDBUSPLUS_LIBS) \ - $(PTHREAD_LIBS) \ libswampd.la swampd_CXXFLAGS = \ $(SDBUSPLUS_CFLAGS) \ - $(PTHREAD_CFLAGS) \ $(CODE_COVERAGE_CXXFLAGS) noinst_LTLIBRARIES = libswampd.la @@ -102,9 +100,8 @@ libswampd_la_SOURCES = \ pid/buildjson.cpp \ pid/zone.cpp \ pid/util.cpp \ - pid/pidthread.cpp \ + pid/pidloop.cpp \ pid/tuning.cpp \ - threads/busthread.cpp \ build/buildjson.cpp \ experiments/drive.cpp diff --git a/configure.ac b/configure.ac index df031b0..b216837 100644 --- a/configure.ac +++ b/configure.ac @@ -56,7 +56,22 @@ AC_CHECK_HEADERS( [], [AC_MSG_ERROR([Could not find CLI11 CLI/CLI.hpp])] ) -AX_PTHREAD([], [AC_MSG_ERROR(["pthread required and not found"])]) + +# check for boost headers +AC_CHECK_HEADER( + boost/asio/io_context.hpp, + [], + [AC_MSG_ERROR([Could not find boost/asio/io_context.hpp])] +) +AC_CHECK_HEADER( + boost/asio/steady_timer.hpp, + [], + [AC_MSG_ERROR([Could not find boost/asio/steady_timer.hpp])] +) +AX_APPEND_COMPILE_FLAGS(["-DBOOST_ASIO_DISABLE_THREADS"], [CPPFLAGS]) +AX_APPEND_COMPILE_FLAGS(["-DBOOST_ERROR_CODE_HEADER_ONLY"], [CPPFLAGS]) +AX_APPEND_COMPILE_FLAGS(["-DBOOST_SYSTEM_NO_DEPRECATED"], [CPPFLAGS]) +AX_APPEND_COMPILE_FLAGS(["-DBOOST_ASIO_NO_DEPRECATED"], [CPPFLAGS]) # Checks for library functions. LT_INIT # Required for systemd linking @@ -21,20 +21,23 @@ #include "interfaces.hpp" #include "pid/builder.hpp" #include "pid/buildjson.hpp" -#include "pid/pidthread.hpp" +#include "pid/pidloop.hpp" #include "pid/tuning.hpp" #include "pid/zone.hpp" #include "sensors/builder.hpp" #include "sensors/buildjson.hpp" #include "sensors/manager.hpp" -#include "threads/busthread.hpp" #include "util.hpp" #include <CLI/CLI.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/steady_timer.hpp> #include <chrono> #include <iostream> +#include <list> #include <map> #include <memory> +#include <sdbusplus/asio/connection.hpp> #include <sdbusplus/bus.hpp> #include <thread> #include <unordered_map> @@ -123,42 +126,24 @@ int main(int argc, char* argv[]) auto& hostSensorBus = mgmr.getHostBus(); auto& passiveListeningBus = mgmr.getPassiveBus(); - std::cerr << "Starting threads\n"; + boost::asio::io_context io; + sdbusplus::asio::connection passiveBus(io, passiveListeningBus.release()); - /* TODO(venture): Ask SensorManager if we have any passive sensors. */ - struct ThreadParams p = {std::ref(passiveListeningBus), ""}; - std::thread l(busThread, std::ref(p)); + sdbusplus::asio::connection hostBus(io, hostSensorBus.release()); + hostBus.request_name("xyz.openbmc_project.Hwmon.external"); - /* TODO(venture): Ask SensorManager if we have any host sensors. */ - static constexpr auto hostBus = "xyz.openbmc_project.Hwmon.external"; - struct ThreadParams e = {std::ref(hostSensorBus), hostBus}; - std::thread te(busThread, std::ref(e)); + sdbusplus::asio::connection modeBus(io, modeControlBus.release()); + modeBus.request_name("xyz.openbmc_project.State.FanCtrl"); - static constexpr auto modeBus = "xyz.openbmc_project.State.FanCtrl"; - struct ThreadParams m = {std::ref(modeControlBus), modeBus}; - std::thread tm(busThread, std::ref(m)); + std::list<boost::asio::steady_timer> timers; - std::vector<std::thread> zoneThreads; - - /* TODO(venture): This was designed to have one thread per zone, but really - * it could have one thread for all the zones and iterate through each - * sequentially as it goes -- and it'd probably be fast enough to do that, - * however, a system isn't likely going to have more than a couple zones. - * If it only has a couple zones, then this is fine. - */ for (const auto& i : zones) { + auto& timer = timers.emplace_back(io); std::cerr << "pushing zone" << std::endl; - zoneThreads.push_back(std::thread(pidControlThread, i.second.get())); - } - - l.join(); - te.join(); - tm.join(); - for (auto& t : zoneThreads) - { - t.join(); + pidControlLoop(i.second.get(), timer); } + io.run(); return rc; } diff --git a/pid/pidloop.cpp b/pid/pidloop.cpp new file mode 100644 index 0000000..b684747 --- /dev/null +++ b/pid/pidloop.cpp @@ -0,0 +1,117 @@ +/** + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "pidloop.hpp" + +#include "pid/pidcontroller.hpp" +#include "pid/tuning.hpp" +#include "sensors/sensor.hpp" + +#include <boost/asio/steady_timer.hpp> +#include <chrono> +#include <map> +#include <memory> +#include <thread> +#include <vector> + +static void processThermals(PIDZone* zone) +{ + // Get the latest margins. + zone->updateSensors(); + // Zero out the RPM set point goals. + zone->clearRPMSetPoints(); + zone->clearRPMCeilings(); + // Run the margin PIDs. + zone->processThermals(); + // Get the maximum RPM setpoint. + zone->determineMaxRPMRequest(); +} + +void pidControlLoop(PIDZone* zone, boost::asio::steady_timer& timer, bool first, + int ms100cnt) +{ + if (first) + { + if (tuningLoggingEnabled) + { + zone->initializeLog(); + } + + zone->initializeCache(); + processThermals(zone); + } + + timer.expires_after(std::chrono::milliseconds(100)); + timer.async_wait( + [zone, &timer, ms100cnt](const boost::system::error_code& ec) mutable { + /* + * This should sleep on the conditional wait for the listen thread + * to tell us it's in sync. But then we also need a timeout option + * in case phosphor-hwmon is down, we can go into some weird failure + * more. + * + * Another approach would be to start all sensors in worst-case + * values, and fail-safe mode and then clear out of fail-safe mode + * once we start getting values. Which I think it is a solid + * approach. + * + * For now this runs before it necessarily has any sensor values. + * For the host sensors they start out in fail-safe mode. For the + * fans, they start out as 0 as input and then are adjusted once + * they have values. + * + * If a fan has failed, it's value will be whatever we're told or + * however we retrieve it. This program disregards fan values of 0, + * so any code providing a fan speed can set to 0 on failure and + * that fan value will be effectively ignored. The PID algorithm + * will be unhappy but nothing bad will happen. + * + * TODO(venture): If the fan value is 0 should that loop just be + * skipped? Right now, a 0 value is ignored in + * FanController::inputProc() + */ + + // Check if we should just go back to sleep. + if (zone->getManualMode()) + { + pidControlLoop(zone, timer, false, ms100cnt); + return; + } + + // Get the latest fan speeds. + zone->updateFanTelemetry(); + + if (10 <= ms100cnt) + { + ms100cnt = 0; + + processThermals(zone); + } + + // Run the fan PIDs every iteration. + zone->processFans(); + + if (tuningLoggingEnabled) + { + zone->getLogHandle() << "," << zone->getFailSafeMode(); + zone->getLogHandle() << std::endl; + } + + ms100cnt += 1; + + pidControlLoop(zone, timer, false, ms100cnt); + }); +} diff --git a/pid/pidloop.hpp b/pid/pidloop.hpp new file mode 100644 index 0000000..3a67954 --- /dev/null +++ b/pid/pidloop.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include "pid/zone.hpp" + +#include <boost/asio/steady_timer.hpp> + +/** + * Main pid control loop for a given zone. + * This function calls itself indefinitely in an async loop to calculate + * fan outputs based on thermal inputs. + * + * @param[in] zone - ptr to the PIDZone for this loop. + * @param[in] timer - boost timer used for async callback. + * @param[in] first - boolean to denote if initialization needs to be run. + * @param[in] ms100cnt - loop timer counter. + */ +void pidControlLoop(PIDZone* zone, boost::asio::steady_timer& timer, + bool first = true, int ms100cnt = 0); diff --git a/pid/pidthread.cpp b/pid/pidthread.cpp deleted file mode 100644 index f8d7fd9..0000000 --- a/pid/pidthread.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Copyright 2017 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "pidthread.hpp" - -#include "pid/pidcontroller.hpp" -#include "pid/tuning.hpp" -#include "sensors/sensor.hpp" - -#include <chrono> -#include <map> -#include <memory> -#include <thread> -#include <vector> - -static void processThermals(PIDZone* zone) -{ - // Get the latest margins. - zone->updateSensors(); - // Zero out the RPM set point goals. - zone->clearRPMSetPoints(); - zone->clearRPMCeilings(); - // Run the margin PIDs. - zone->processThermals(); - // Get the maximum RPM setpoint. - zone->determineMaxRPMRequest(); -} - -void pidControlThread(PIDZone* zone) -{ - int ms100cnt = 0; - /* - * This should sleep on the conditional wait for the listen thread to tell - * us it's in sync. But then we also need a timeout option in case - * phosphor-hwmon is down, we can go into some weird failure more. - * - * Another approach would be to start all sensors in worst-case values, - * and fail-safe mode and then clear out of fail-safe mode once we start - * getting values. Which I think it is a solid approach. - * - * For now this runs before it necessarily has any sensor values. For the - * host sensors they start out in fail-safe mode. For the fans, they start - * out as 0 as input and then are adjusted once they have values. - * - * If a fan has failed, it's value will be whatever we're told or however - * we retrieve it. This program disregards fan values of 0, so any code - * providing a fan speed can set to 0 on failure and that fan value will be - * effectively ignored. The PID algorithm will be unhappy but nothing bad - * will happen. - * - * TODO(venture): If the fan value is 0 should that loop just be skipped? - * Right now, a 0 value is ignored in FanController::inputProc() - */ - if (tuningLoggingEnabled) - { - zone->initializeLog(); - } - - zone->initializeCache(); - processThermals(zone); - - while (true) - { - using namespace std::literals::chrono_literals; - std::this_thread::sleep_for(0.1s); - - // Check if we should just go back to sleep. - if (zone->getManualMode()) - { - continue; - } - - // Get the latest fan speeds. - zone->updateFanTelemetry(); - - if (10 <= ms100cnt) - { - ms100cnt = 0; - - processThermals(zone); - } - - // Run the fan PIDs every iteration. - zone->processFans(); - - if (tuningLoggingEnabled) - { - zone->getLogHandle() << "," << zone->getFailSafeMode(); - zone->getLogHandle() << std::endl; - } - - ms100cnt += 1; - } - - return; -} diff --git a/pid/pidthread.hpp b/pid/pidthread.hpp deleted file mode 100644 index 94865c0..0000000 --- a/pid/pidthread.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include "pid/zone.hpp" - -/* Given a zone, run through the loops. */ -void pidControlThread(PIDZone* zone); diff --git a/threads/busthread.cpp b/threads/busthread.cpp deleted file mode 100644 index 6cfb08c..0000000 --- a/threads/busthread.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright 2017 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "busthread.hpp" - -#include <string> - -void busThread(struct ThreadParams& params) -{ - if (params.name.length() > 0) - { - params.bus.request_name(params.name.c_str()); - } - - while (true) - { - params.bus.process_discard(); - /* block indefinitely for updates. */ - params.bus.wait(); - } - - return; -} diff --git a/threads/busthread.hpp b/threads/busthread.hpp deleted file mode 100644 index df875f5..0000000 --- a/threads/busthread.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include <sdbusplus/bus.hpp> - -struct ThreadParams -{ - sdbusplus::bus::bus& bus; - std::string name; -}; - -void busThread(struct ThreadParams& params); |