output modules simplify locking for mixer reset and load

- fixes the deadlock in px4io ioctl mixer reset
   - px4io Run() locks (CDev semaphore)
   - mixer load goes through px4io ioctl MIXERIOCRESET which calls MixingOutput::resetMixerThreadSafe()
   - MixingOutput::resetMixerThreadSafe() stores a Command::Type::resetMixer command in an atomic variable, schedules a work queue cycle, then sleep spins until the command is cleared
   - the execution of the cycle eventually calls back into PX4IO::updateOutputs(), which tries to lock (and waits forever)
This commit is contained in:
Daniel Agar
2022-04-19 08:52:17 +02:00
committed by Beat Küng
parent 5d95cc001f
commit 5e05d98fe2
11 changed files with 72 additions and 172 deletions
-90
View File
@@ -331,7 +331,6 @@ bool MixingOutput::updateSubscriptionsStaticMixer(bool allow_wq_switch, bool lim
return true;
}
void MixingOutput::cleanupFunctions()
{
if (_subscription_callback) {
@@ -645,7 +644,6 @@ bool MixingOutput::update()
bool MixingOutput::updateStaticMixer()
{
if (!_mixers) {
handleCommands();
// do nothing until we have a valid mixer
return false;
}
@@ -707,7 +705,6 @@ bool MixingOutput::updateStaticMixer()
setAndPublishActuatorOutputs(num_motor_test, actuator_outputs);
}
handleCommands();
return true;
}
}
@@ -749,8 +746,6 @@ bool MixingOutput::updateStaticMixer()
updateLatencyPerfCounter(actuator_outputs);
}
handleCommands();
return true;
}
@@ -1248,88 +1243,3 @@ int MixingOutput::loadMixer(const char *buf, unsigned len)
_interface.mixerChanged();
return ret;
}
void MixingOutput::handleCommands()
{
if ((Command::Type)_command.command.load() == Command::Type::None) {
return;
}
switch ((Command::Type)_command.command.load()) {
case Command::Type::loadMixer:
_command.result = loadMixer(_command.mixer_buf, _command.mixer_buf_length);
break;
case Command::Type::resetMixer:
resetMixer();
_command.result = 0;
break;
default:
break;
}
// mark as done
_command.command.store((int)Command::Type::None);
}
void MixingOutput::resetMixerThreadSafe()
{
if (_use_dynamic_mixing) {
PX4_ERR("mixer reset unavailable, not using static mixers");
return;
}
if ((Command::Type)_command.command.load() != Command::Type::None) {
// Cannot happen, because we expect only one other thread to call this.
// But as a safety precaution we return here.
PX4_ERR("Command not None");
return;
}
lock();
_command.command.store((int)Command::Type::resetMixer);
_interface.ScheduleNow();
unlock();
// wait until processed
while ((Command::Type)_command.command.load() != Command::Type::None) {
usleep(1000);
}
}
int MixingOutput::loadMixerThreadSafe(const char *buf, unsigned len)
{
if (_use_dynamic_mixing) {
PX4_ERR("mixer load unavailable, not using static mixers");
return -1;
}
if ((Command::Type)_command.command.load() != Command::Type::None) {
// Cannot happen, because we expect only one other thread to call this.
// But as a safety precaution we return here.
PX4_ERR("Command not None");
return -1;
}
lock();
_command.mixer_buf = buf;
_command.mixer_buf_length = len;
_command.command.store((int)Command::Type::loadMixer);
_interface.ScheduleNow();
unlock();
// wait until processed
while ((Command::Type)_command.command.load() != Command::Type::None) {
usleep(1000);
}
return _command.result;
}
+2 -24
View File
@@ -40,7 +40,6 @@
#include <drivers/drv_pwm_output.h>
#include <lib/mixer/MixerGroup.hpp>
#include <lib/perf/perf_counter.h>
#include <px4_platform_common/atomic.h>
#include <px4_platform_common/module_params.h>
#include <px4_platform_common/px4_work_queue/ScheduledWorkItem.hpp>
#include <uORB/Publication.hpp>
@@ -157,20 +156,14 @@ public:
void setMaxTopicUpdateRate(unsigned max_topic_update_interval_us);
/**
* Reset (unload) the complete mixer, called from another thread.
* This is thread-safe, as long as only one other thread at a time calls this.
* Reset (unload) the complete mixer.
*/
void resetMixerThreadSafe();
void resetMixer();
/**
* Load (append) a new mixer from a buffer, called from another thread.
* This is thread-safe, as long as only one other thread at a time calls this.
* Load (append) a new mixer from a buffer.
* @return 0 on success, <0 error otherwise
*/
int loadMixerThreadSafe(const char *buf, unsigned len);
int loadMixer(const char *buf, unsigned len);
const actuator_armed_s &armed() const { return _armed; }
@@ -230,8 +223,6 @@ private:
bool updateStaticMixer();
bool updateDynamicMixer();
void handleCommands();
bool armNoThrottle() const
{
return (_armed.prearmed && !_armed.armed) || _armed.in_esc_calibration_mode;
@@ -270,19 +261,6 @@ private:
Betaflight = 1
};
struct Command {
enum class Type : int {
None,
resetMixer,
loadMixer
};
px4::atomic<int> command{(int)Type::None};
const char *mixer_buf;
unsigned mixer_buf_length;
int result;
};
Command _command; ///< incoming commands (from another thread)
/**
* Reorder outputs according to _param_mot_ordering
* @param values values to reorder