core: record transactions that have seen ordering cycles

This commit is contained in:
Mike Yuan
2025-10-05 01:07:48 +02:00
parent 0d9e79d5ca
commit d3da74696b
4 changed files with 24 additions and 5 deletions

View File

@@ -938,11 +938,12 @@ Defined-By: systemd
Support: %SUPPORT_URL% Support: %SUPPORT_URL%
Documentation: man:systemd(1) Documentation: man:systemd(1)
A unit transaction was initiated that contains an ordering cycle, i.e. some A unit transaction (with ID @TRANSACTION_ID@) was initiated that contains
unit that was requested to be started (either directly, or indirectly due to a an ordering cycle, i.e. some unit that was requested to be started
requirement dependency such as Wants= or Requires=) is ordered before some (either directly, or indirectly due to a requirement dependency such as
other unit (via After=/Before=), but that latter unit is also ordered before Wants= or Requires=) is ordered before some other unit (via After=/Before=),
the former by some dependency (either directly or indirectly). but that latter unit is also ordered before the former by some dependency
(either directly or indirectly).
Ordering cycles consist of at least two units, but might involve many Ordering cycles consist of at least two units, but might involve many
more. They generally indicate a bug in the unit definitions, as a unit more. They generally indicate a bug in the unit definitions, as a unit

View File

@@ -1659,6 +1659,8 @@ static void manager_clear_jobs_and_units(Manager *m) {
m->n_running_jobs = 0; m->n_running_jobs = 0;
m->n_installed_jobs = 0; m->n_installed_jobs = 0;
m->n_failed_jobs = 0; m->n_failed_jobs = 0;
m->transactions_with_cycle = set_free(m->transactions_with_cycle);
} }
Manager* manager_free(Manager *m) { Manager* manager_free(Manager *m) {

View File

@@ -238,6 +238,9 @@ typedef struct Manager {
uint64_t last_transaction_id; uint64_t last_transaction_id;
/* IDs of transactions that once encountered ordering cycle */
Set *transactions_with_cycle;
sd_event_source *run_queue_event_source; sd_event_source *run_queue_event_source;
char *notify_socket; char *notify_socket;

View File

@@ -8,6 +8,7 @@
#include "bus-common-errors.h" #include "bus-common-errors.h"
#include "bus-error.h" #include "bus-error.h"
#include "dbus-unit.h" #include "dbus-unit.h"
#include "hash-funcs.h"
#include "manager.h" #include "manager.h"
#include "set.h" #include "set.h"
#include "slice.h" #include "slice.h"
@@ -15,6 +16,8 @@
#include "strv.h" #include "strv.h"
#include "transaction.h" #include "transaction.h"
#define CYCLIC_TRANSACTIONS_MAX 4096U
static bool job_matters_to_anchor(Job *job); static bool job_matters_to_anchor(Job *job);
static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies); static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies);
@@ -399,6 +402,16 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
LOG_MESSAGE_ID(SD_MESSAGE_UNIT_ORDERING_CYCLE_STR), LOG_MESSAGE_ID(SD_MESSAGE_UNIT_ORDERING_CYCLE_STR),
LOG_ITEM("%s", strempty(unit_ids))); LOG_ITEM("%s", strempty(unit_ids)));
if (set_size(j->manager->transactions_with_cycle) >= CYCLIC_TRANSACTIONS_MAX)
log_warning("Too many transactions with ordering cycle, suppressing record.");
else {
uint64_t *id_buf = newdup(uint64_t, &tr->id, 1);
if (!id_buf)
log_oom_warning();
else
(void) set_ensure_consume(&j->manager->transactions_with_cycle, &uint64_hash_ops_value_free, id_buf);
}
if (delete) { if (delete) {
const char *status; const char *status;
/* logging for j not k here to provide a consistent narrative */ /* logging for j not k here to provide a consistent narrative */