142bf4
From f5d23169d737f6c6fe123dbaeae053227c744457 Mon Sep 17 00:00:00 2001
b80fe6
From: Yu Watanabe <watanabe.yu+github@gmail.com>
b80fe6
Date: Mon, 2 Sep 2024 11:41:57 +0900
b80fe6
Subject: [PATCH 01/10] network/qdisc: introduce qdisc_ref() and qdisc_unref()
b80fe6
b80fe6
No functional change, just refactoring and preparation for later change.
142bf4
142bf4
(cherry picked from commit 9b294afa2dbdb05695925ded55a8f1bfa70bb087)
b80fe6
---
b80fe6
 src/network/networkd-network.c |   2 +-
b80fe6
 src/network/tc/cake.c          |  20 +++----
b80fe6
 src/network/tc/codel.c         |   6 +-
b80fe6
 src/network/tc/ets.c           |   6 +-
b80fe6
 src/network/tc/fifo.c          |   4 +-
b80fe6
 src/network/tc/fq-codel.c      |   8 +--
b80fe6
 src/network/tc/fq-pie.c        |   2 +-
b80fe6
 src/network/tc/fq.c            |  10 ++--
b80fe6
 src/network/tc/gred.c          |   4 +-
b80fe6
 src/network/tc/hhf.c           |   2 +-
b80fe6
 src/network/tc/htb.c           |   4 +-
b80fe6
 src/network/tc/netem.c         |   6 +-
b80fe6
 src/network/tc/pie.c           |   2 +-
b80fe6
 src/network/tc/qdisc.c         | 103 +++++++++++++++++++++++----------
b80fe6
 src/network/tc/qdisc.h         |   7 ++-
b80fe6
 src/network/tc/sfb.c           |   2 +-
b80fe6
 src/network/tc/sfq.c           |   2 +-
b80fe6
 src/network/tc/tbf.c           |   6 +-
b80fe6
 src/network/tc/teql.c          |   2 +-
b80fe6
 19 files changed, 122 insertions(+), 76 deletions(-)
b80fe6
b80fe6
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
142bf4
index 8232db06c934c..155e9568e2a27 100644
b80fe6
--- a/src/network/networkd-network.c
b80fe6
+++ b/src/network/networkd-network.c
142bf4
@@ -799,7 +799,7 @@ static Network *network_free(Network *network) {
142bf4
         hashmap_free_with_destructor(network->rules_by_section, routing_policy_rule_free);
b80fe6
         hashmap_free_with_destructor(network->dhcp_static_leases_by_section, dhcp_static_lease_free);
b80fe6
         ordered_hashmap_free_with_destructor(network->sr_iov_by_section, sr_iov_free);
b80fe6
-        hashmap_free_with_destructor(network->qdiscs_by_section, qdisc_free);
b80fe6
+        hashmap_free(network->qdiscs_by_section);
b80fe6
         hashmap_free_with_destructor(network->tclasses_by_section, tclass_free);
b80fe6
 
b80fe6
         return mfree(network);
b80fe6
diff --git a/src/network/tc/cake.c b/src/network/tc/cake.c
b80fe6
index c495fafda4cc8..704e527b46a1f 100644
b80fe6
--- a/src/network/tc/cake.c
b80fe6
+++ b/src/network/tc/cake.c
b80fe6
@@ -150,7 +150,7 @@ int config_parse_cake_bandwidth(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         CommonApplicationsKeptEnhanced *c;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         uint64_t k;
b80fe6
@@ -204,7 +204,7 @@ int config_parse_cake_overhead(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         CommonApplicationsKeptEnhanced *c;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         int32_t v;
b80fe6
@@ -263,7 +263,7 @@ int config_parse_cake_mpu(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         CommonApplicationsKeptEnhanced *c;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         uint32_t v;
b80fe6
@@ -321,7 +321,7 @@ int config_parse_cake_tristate(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         CommonApplicationsKeptEnhanced *c;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         int *dest, r;
b80fe6
@@ -386,7 +386,7 @@ int config_parse_cake_compensation_mode(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         CommonApplicationsKeptEnhanced *c;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         CakeCompensationMode mode;
b80fe6
@@ -451,7 +451,7 @@ int config_parse_cake_flow_isolation_mode(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         CommonApplicationsKeptEnhanced *c;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         CakeFlowIsolationMode mode;
b80fe6
@@ -513,7 +513,7 @@ int config_parse_cake_priority_queueing_preset(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         CommonApplicationsKeptEnhanced *c;
b80fe6
         CakePriorityQueueingPreset preset;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
@@ -565,7 +565,7 @@ int config_parse_cake_fwmark(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         CommonApplicationsKeptEnhanced *c;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         uint32_t fwmark;
b80fe6
@@ -623,7 +623,7 @@ int config_parse_cake_rtt(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         CommonApplicationsKeptEnhanced *c;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         usec_t t;
b80fe6
@@ -689,7 +689,7 @@ int config_parse_cake_ack_filter(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         CommonApplicationsKeptEnhanced *c;
b80fe6
         CakeAckFilter ack_filter;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
diff --git a/src/network/tc/codel.c b/src/network/tc/codel.c
b80fe6
index e21252394c03e..53ccf66e419e6 100644
b80fe6
--- a/src/network/tc/codel.c
b80fe6
+++ b/src/network/tc/codel.c
b80fe6
@@ -86,7 +86,7 @@ int config_parse_controlled_delay_u32(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         ControlledDelay *cd;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         int r;
b80fe6
@@ -138,7 +138,7 @@ int config_parse_controlled_delay_usec(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         ControlledDelay *cd;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         usec_t *p;
b80fe6
@@ -203,7 +203,7 @@ int config_parse_controlled_delay_bool(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         ControlledDelay *cd;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         int r;
b80fe6
diff --git a/src/network/tc/ets.c b/src/network/tc/ets.c
b80fe6
index 730b0a10c3cfb..4af750834cb30 100644
b80fe6
--- a/src/network/tc/ets.c
b80fe6
+++ b/src/network/tc/ets.c
b80fe6
@@ -88,7 +88,7 @@ int config_parse_ets_u8(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         EnhancedTransmissionSelection *ets;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         uint8_t v, *p;
b80fe6
@@ -154,7 +154,7 @@ int config_parse_ets_quanta(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         EnhancedTransmissionSelection *ets;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         int r;
b80fe6
@@ -237,7 +237,7 @@ int config_parse_ets_prio(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         EnhancedTransmissionSelection *ets;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         int r;
b80fe6
diff --git a/src/network/tc/fifo.c b/src/network/tc/fifo.c
b80fe6
index 940fa0062f001..9638be8ff9caa 100644
b80fe6
--- a/src/network/tc/fifo.c
b80fe6
+++ b/src/network/tc/fifo.c
b80fe6
@@ -52,7 +52,7 @@ int config_parse_pfifo_size(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         FirstInFirstOut *fifo;
b80fe6
         int r;
b80fe6
@@ -112,7 +112,7 @@ int config_parse_bfifo_size(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         FirstInFirstOut *fifo;
b80fe6
         uint64_t u;
b80fe6
diff --git a/src/network/tc/fq-codel.c b/src/network/tc/fq-codel.c
b80fe6
index 124faf73e72a4..9255cde4650d9 100644
b80fe6
--- a/src/network/tc/fq-codel.c
b80fe6
+++ b/src/network/tc/fq-codel.c
b80fe6
@@ -106,7 +106,7 @@ int config_parse_fair_queueing_controlled_delay_u32(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         FairQueueingControlledDelay *fqcd;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         uint32_t *p;
b80fe6
@@ -166,7 +166,7 @@ int config_parse_fair_queueing_controlled_delay_usec(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         FairQueueingControlledDelay *fqcd;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         usec_t *p;
b80fe6
@@ -231,7 +231,7 @@ int config_parse_fair_queueing_controlled_delay_bool(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         FairQueueingControlledDelay *fqcd;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         int r;
b80fe6
@@ -276,7 +276,7 @@ int config_parse_fair_queueing_controlled_delay_size(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         FairQueueingControlledDelay *fqcd;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         uint64_t sz;
b80fe6
diff --git a/src/network/tc/fq-pie.c b/src/network/tc/fq-pie.c
b80fe6
index c8b2e7b7ee908..8f4f7c431c441 100644
b80fe6
--- a/src/network/tc/fq-pie.c
b80fe6
+++ b/src/network/tc/fq-pie.c
b80fe6
@@ -49,7 +49,7 @@ int config_parse_fq_pie_packet_limit(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         FlowQueuePIE *fq_pie;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         uint32_t val;
b80fe6
diff --git a/src/network/tc/fq.c b/src/network/tc/fq.c
b80fe6
index 74785c980ae4b..ea55e5cb0a122 100644
b80fe6
--- a/src/network/tc/fq.c
b80fe6
+++ b/src/network/tc/fq.c
b80fe6
@@ -115,7 +115,7 @@ int config_parse_fair_queueing_u32(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         FairQueueing *fq;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         uint32_t *p;
b80fe6
@@ -179,7 +179,7 @@ int config_parse_fair_queueing_size(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         FairQueueing *fq;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         uint64_t sz;
b80fe6
@@ -247,7 +247,7 @@ int config_parse_fair_queueing_bool(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         FairQueueing *fq;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         int r;
b80fe6
@@ -293,7 +293,7 @@ int config_parse_fair_queueing_usec(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         FairQueueing *fq;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         usec_t sec;
b80fe6
@@ -353,7 +353,7 @@ int config_parse_fair_queueing_max_rate(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         FairQueueing *fq;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         uint64_t sz;
b80fe6
diff --git a/src/network/tc/gred.c b/src/network/tc/gred.c
b80fe6
index 2efb02c345f04..198905a1521ff 100644
b80fe6
--- a/src/network/tc/gred.c
b80fe6
+++ b/src/network/tc/gred.c
b80fe6
@@ -77,7 +77,7 @@ int config_parse_generic_random_early_detection_u32(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         GenericRandomEarlyDetection *gred;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         uint32_t *p;
b80fe6
@@ -143,7 +143,7 @@ int config_parse_generic_random_early_detection_bool(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         GenericRandomEarlyDetection *gred;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         int r;
b80fe6
diff --git a/src/network/tc/hhf.c b/src/network/tc/hhf.c
b80fe6
index d44522f98cc92..9ddb7ef9063d5 100644
b80fe6
--- a/src/network/tc/hhf.c
b80fe6
+++ b/src/network/tc/hhf.c
b80fe6
@@ -49,7 +49,7 @@ int config_parse_heavy_hitter_filter_packet_limit(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         HeavyHitterFilter *hhf;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         int r;
b80fe6
diff --git a/src/network/tc/htb.c b/src/network/tc/htb.c
b80fe6
index eb2c8cfff4cd1..8f1faa1dc5d45 100644
b80fe6
--- a/src/network/tc/htb.c
b80fe6
+++ b/src/network/tc/htb.c
b80fe6
@@ -57,7 +57,7 @@ int config_parse_hierarchy_token_bucket_default_class(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         HierarchyTokenBucket *htb;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         int r;
b80fe6
@@ -109,7 +109,7 @@ int config_parse_hierarchy_token_bucket_u32(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         HierarchyTokenBucket *htb;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         int r;
b80fe6
diff --git a/src/network/tc/netem.c b/src/network/tc/netem.c
b80fe6
index 6a63221c3ac30..51039de1f4e7b 100644
b80fe6
--- a/src/network/tc/netem.c
b80fe6
+++ b/src/network/tc/netem.c
b80fe6
@@ -60,7 +60,7 @@ int config_parse_network_emulator_delay(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         NetworkEmulator *ne;
b80fe6
         usec_t u;
b80fe6
@@ -121,7 +121,7 @@ int config_parse_network_emulator_rate(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         NetworkEmulator *ne;
b80fe6
         uint32_t rate;
b80fe6
@@ -181,7 +181,7 @@ int config_parse_network_emulator_packet_limit(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         NetworkEmulator *ne;
b80fe6
         int r;
b80fe6
diff --git a/src/network/tc/pie.c b/src/network/tc/pie.c
b80fe6
index c9b171baf11d7..c482f19787abc 100644
b80fe6
--- a/src/network/tc/pie.c
b80fe6
+++ b/src/network/tc/pie.c
b80fe6
@@ -49,7 +49,7 @@ int config_parse_pie_packet_limit(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         ProportionalIntegralControllerEnhanced *pie;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         int r;
b80fe6
diff --git a/src/network/tc/qdisc.c b/src/network/tc/qdisc.c
b80fe6
index 38dee2c00157f..d372481d6280b 100644
b80fe6
--- a/src/network/tc/qdisc.c
b80fe6
+++ b/src/network/tc/qdisc.c
b80fe6
@@ -42,8 +42,54 @@ const QDiscVTable * const qdisc_vtable[_QDISC_KIND_MAX] = {
b80fe6
         [QDISC_KIND_TEQL]            = &teql_vtable,
b80fe6
 };
b80fe6
 
b80fe6
+static QDisc* qdisc_detach_impl(QDisc *qdisc) {
b80fe6
+        assert(qdisc);
b80fe6
+        assert(!qdisc->link || !qdisc->network);
b80fe6
+
b80fe6
+        if (qdisc->network) {
b80fe6
+                assert(qdisc->section);
b80fe6
+                hashmap_remove(qdisc->network->qdiscs_by_section, qdisc->section);
b80fe6
+
b80fe6
+                qdisc->network = NULL;
b80fe6
+                return qdisc;
b80fe6
+        }
b80fe6
+
b80fe6
+        if (qdisc->link) {
b80fe6
+                set_remove(qdisc->link->qdiscs, qdisc);
b80fe6
+
b80fe6
+                qdisc->link = NULL;
b80fe6
+                return qdisc;
b80fe6
+        }
b80fe6
+
b80fe6
+        return NULL;
b80fe6
+}
b80fe6
+
b80fe6
+static void qdisc_detach(QDisc *qdisc) {
b80fe6
+        assert(qdisc);
b80fe6
+
b80fe6
+        qdisc_unref(qdisc_detach_impl(qdisc));
b80fe6
+}
b80fe6
+
b80fe6
+static void qdisc_hash_func(const QDisc *qdisc, struct siphash *state);
b80fe6
+static int qdisc_compare_func(const QDisc *a, const QDisc *b);
b80fe6
+
b80fe6
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
b80fe6
+        qdisc_hash_ops,
b80fe6
+        QDisc,
b80fe6
+        qdisc_hash_func,
b80fe6
+        qdisc_compare_func,
b80fe6
+        qdisc_detach);
b80fe6
+
b80fe6
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
b80fe6
+        qdisc_section_hash_ops,
b80fe6
+        ConfigSection,
b80fe6
+        config_section_hash_func,
b80fe6
+        config_section_compare_func,
b80fe6
+        QDisc,
b80fe6
+        qdisc_detach);
b80fe6
+
b80fe6
 static int qdisc_new(QDiscKind kind, QDisc **ret) {
b80fe6
-        _cleanup_(qdisc_freep) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unrefp) QDisc *qdisc = NULL;
b80fe6
         int r;
b80fe6
 
b80fe6
         if (kind == _QDISC_KIND_INVALID) {
b80fe6
@@ -52,6 +98,7 @@ static int qdisc_new(QDiscKind kind, QDisc **ret) {
b80fe6
                         return -ENOMEM;
b80fe6
 
b80fe6
                 *qdisc = (QDisc) {
b80fe6
+                        .n_ref = 1,
b80fe6
                         .parent = TC_H_ROOT,
b80fe6
                         .kind = kind,
b80fe6
                 };
b80fe6
@@ -61,6 +108,7 @@ static int qdisc_new(QDiscKind kind, QDisc **ret) {
b80fe6
                 if (!qdisc)
b80fe6
                         return -ENOMEM;
b80fe6
 
b80fe6
+                qdisc->n_ref = 1;
b80fe6
                 qdisc->parent = TC_H_ROOT;
b80fe6
                 qdisc->kind = kind;
b80fe6
 
b80fe6
@@ -78,7 +126,7 @@ static int qdisc_new(QDiscKind kind, QDisc **ret) {
b80fe6
 
b80fe6
 int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, unsigned section_line, QDisc **ret) {
b80fe6
         _cleanup_(config_section_freep) ConfigSection *n = NULL;
b80fe6
-        _cleanup_(qdisc_freep) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unrefp) QDisc *qdisc = NULL;
b80fe6
         QDisc *existing;
b80fe6
         int r;
b80fe6
 
b80fe6
@@ -113,14 +161,14 @@ int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, uns
b80fe6
                 qdisc->parent = existing->parent;
b80fe6
                 qdisc->tca_kind = TAKE_PTR(existing->tca_kind);
b80fe6
 
b80fe6
-                qdisc_free(existing);
b80fe6
+                qdisc_detach(existing);
b80fe6
         }
b80fe6
 
b80fe6
         qdisc->network = network;
b80fe6
         qdisc->section = TAKE_PTR(n);
b80fe6
         qdisc->source = NETWORK_CONFIG_SOURCE_STATIC;
b80fe6
 
b80fe6
-        r = hashmap_ensure_put(&network->qdiscs_by_section, &config_section_hash_ops, qdisc->section, qdisc);
b80fe6
+        r = hashmap_ensure_put(&network->qdiscs_by_section, &qdisc_section_hash_ops, qdisc->section, qdisc);
b80fe6
         if (r < 0)
b80fe6
                 return r;
b80fe6
 
b80fe6
@@ -128,22 +176,20 @@ int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, uns
b80fe6
         return 0;
b80fe6
 }
b80fe6
 
b80fe6
-QDisc* qdisc_free(QDisc *qdisc) {
b80fe6
+static QDisc* qdisc_free(QDisc *qdisc) {
b80fe6
         if (!qdisc)
b80fe6
                 return NULL;
b80fe6
 
b80fe6
-        if (qdisc->network && qdisc->section)
b80fe6
-                hashmap_remove(qdisc->network->qdiscs_by_section, qdisc->section);
b80fe6
+        qdisc_detach_impl(qdisc);
b80fe6
 
b80fe6
         config_section_free(qdisc->section);
b80fe6
 
b80fe6
-        if (qdisc->link)
b80fe6
-                set_remove(qdisc->link->qdiscs, qdisc);
b80fe6
-
b80fe6
         free(qdisc->tca_kind);
b80fe6
         return mfree(qdisc);
b80fe6
 }
b80fe6
 
b80fe6
+DEFINE_TRIVIAL_REF_UNREF_FUNC(QDisc, qdisc, qdisc_free);
b80fe6
+
b80fe6
 static const char *qdisc_get_tca_kind(const QDisc *qdisc) {
b80fe6
         assert(qdisc);
b80fe6
 
b80fe6
@@ -177,13 +223,6 @@ static int qdisc_compare_func(const QDisc *a, const QDisc *b) {
b80fe6
         return strcmp_ptr(qdisc_get_tca_kind(a), qdisc_get_tca_kind(b));
b80fe6
 }
b80fe6
 
b80fe6
-DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
b80fe6
-        qdisc_hash_ops,
b80fe6
-        QDisc,
b80fe6
-        qdisc_hash_func,
b80fe6
-        qdisc_compare_func,
b80fe6
-        qdisc_free);
b80fe6
-
b80fe6
 static int qdisc_get(Link *link, const QDisc *in, QDisc **ret) {
b80fe6
         QDisc *existing;
b80fe6
 
b80fe6
@@ -199,11 +238,13 @@ static int qdisc_get(Link *link, const QDisc *in, QDisc **ret) {
b80fe6
         return 0;
b80fe6
 }
b80fe6
 
b80fe6
-static int qdisc_add(Link *link, QDisc *qdisc) {
b80fe6
+static int qdisc_attach(Link *link, QDisc *qdisc) {
b80fe6
         int r;
b80fe6
 
b80fe6
         assert(link);
b80fe6
         assert(qdisc);
b80fe6
+        assert(!qdisc->link);
b80fe6
+        assert(!qdisc->network);
b80fe6
 
b80fe6
         r = set_ensure_put(&link->qdiscs, &qdisc_hash_ops, qdisc);
b80fe6
         if (r < 0)
b80fe6
@@ -212,11 +253,12 @@ static int qdisc_add(Link *link, QDisc *qdisc) {
b80fe6
                 return -EEXIST;
b80fe6
 
b80fe6
         qdisc->link = link;
b80fe6
+        qdisc_ref(qdisc);
b80fe6
         return 0;
b80fe6
 }
b80fe6
 
b80fe6
 static int qdisc_dup(const QDisc *src, QDisc **ret) {
b80fe6
-        _cleanup_(qdisc_freep) QDisc *dst = NULL;
b80fe6
+        _cleanup_(qdisc_unrefp) QDisc *dst = NULL;
b80fe6
 
b80fe6
         assert(src);
b80fe6
         assert(ret);
b80fe6
@@ -228,7 +270,8 @@ static int qdisc_dup(const QDisc *src, QDisc **ret) {
b80fe6
         if (!dst)
b80fe6
                 return -ENOMEM;
b80fe6
 
b80fe6
-        /* clear all pointers */
b80fe6
+        /* clear the reference counter and all pointers */
b80fe6
+        dst->n_ref = 1;
b80fe6
         dst->network = NULL;
b80fe6
         dst->section = NULL;
b80fe6
         dst->link = NULL;
b80fe6
@@ -319,7 +362,7 @@ void link_qdisc_drop_marked(Link *link) {
b80fe6
 
b80fe6
                 if (qdisc->state == 0) {
b80fe6
                         log_qdisc_debug(qdisc, link, "Forgetting");
b80fe6
-                        qdisc_free(qdisc);
b80fe6
+                        qdisc_detach(qdisc);
b80fe6
                 } else
b80fe6
                         log_qdisc_debug(qdisc, link, "Removed");
b80fe6
         }
b80fe6
@@ -443,17 +486,17 @@ int link_request_qdisc(Link *link, QDisc *qdisc) {
b80fe6
         assert(qdisc);
b80fe6
 
b80fe6
         if (qdisc_get(link, qdisc, &existing) < 0) {
b80fe6
-                _cleanup_(qdisc_freep) QDisc *tmp = NULL;
b80fe6
+                _cleanup_(qdisc_unrefp) QDisc *tmp = NULL;
b80fe6
 
b80fe6
                 r = qdisc_dup(qdisc, &tmp);
b80fe6
                 if (r < 0)
b80fe6
                         return log_oom();
b80fe6
 
b80fe6
-                r = qdisc_add(link, tmp);
b80fe6
+                r = qdisc_attach(link, tmp);
b80fe6
                 if (r < 0)
b80fe6
                         return log_link_warning_errno(link, r, "Failed to store QDisc: %m");
b80fe6
 
b80fe6
-                existing = TAKE_PTR(tmp);
b80fe6
+                existing = tmp;
b80fe6
         } else
b80fe6
                 existing->source = qdisc->source;
b80fe6
 
b80fe6
@@ -476,7 +519,7 @@ int link_request_qdisc(Link *link, QDisc *qdisc) {
b80fe6
 }
b80fe6
 
b80fe6
 int manager_rtnl_process_qdisc(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
b80fe6
-        _cleanup_(qdisc_freep) QDisc *tmp = NULL;
b80fe6
+        _cleanup_(qdisc_unrefp) QDisc *tmp = NULL;
b80fe6
         QDisc *qdisc = NULL;
b80fe6
         Link *link;
b80fe6
         uint16_t type;
b80fe6
@@ -551,13 +594,13 @@ int manager_rtnl_process_qdisc(sd_netlink *rtnl, sd_netlink_message *message, Ma
b80fe6
                         qdisc_enter_configured(tmp);
b80fe6
                         log_qdisc_debug(tmp, link, "Received new");
b80fe6
 
b80fe6
-                        r = qdisc_add(link, tmp);
b80fe6
+                        r = qdisc_attach(link, tmp);
b80fe6
                         if (r < 0) {
b80fe6
                                 log_link_warning_errno(link, r, "Failed to remember QDisc, ignoring: %m");
b80fe6
                                 return 0;
b80fe6
                         }
b80fe6
 
b80fe6
-                        qdisc = TAKE_PTR(tmp);
b80fe6
+                        qdisc = tmp;
b80fe6
                 }
b80fe6
 
b80fe6
                 if (!m->enumerating) {
b80fe6
@@ -628,7 +671,7 @@ void network_drop_invalid_qdisc(Network *network) {
b80fe6
 
b80fe6
         HASHMAP_FOREACH(qdisc, network->qdiscs_by_section)
b80fe6
                 if (qdisc_section_verify(qdisc, &has_root, &has_clsact) < 0)
b80fe6
-                        qdisc_free(qdisc);
b80fe6
+                        qdisc_detach(qdisc);
b80fe6
 }
b80fe6
 
b80fe6
 int config_parse_qdisc_parent(
b80fe6
@@ -643,7 +686,7 @@ int config_parse_qdisc_parent(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         int r;
b80fe6
 
b80fe6
@@ -702,7 +745,7 @@ int config_parse_qdisc_handle(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         uint16_t n;
b80fe6
         int r;
b80fe6
diff --git a/src/network/tc/qdisc.h b/src/network/tc/qdisc.h
b80fe6
index cbba1bef71199..3989ad7651531 100644
b80fe6
--- a/src/network/tc/qdisc.h
b80fe6
+++ b/src/network/tc/qdisc.h
b80fe6
@@ -42,6 +42,8 @@ typedef struct QDisc {
b80fe6
         NetworkConfigSource source;
b80fe6
         NetworkConfigState state;
b80fe6
 
b80fe6
+        unsigned n_ref;
b80fe6
+
b80fe6
         uint32_t handle;
b80fe6
         uint32_t parent;
b80fe6
 
b80fe6
@@ -74,7 +76,8 @@ extern const QDiscVTable * const qdisc_vtable[_QDISC_KIND_MAX];
b80fe6
 
b80fe6
 DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(QDisc, qdisc);
b80fe6
 
b80fe6
-QDisc* qdisc_free(QDisc *qdisc);
b80fe6
+QDisc* qdisc_ref(QDisc *qdisc);
b80fe6
+QDisc* qdisc_unref(QDisc *qdisc);
b80fe6
 int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, unsigned section_line, QDisc **ret);
b80fe6
 
b80fe6
 void qdisc_mark_recursive(QDisc *qdisc);
b80fe6
@@ -89,7 +92,7 @@ void network_drop_invalid_qdisc(Network *network);
b80fe6
 
b80fe6
 int manager_rtnl_process_qdisc(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);
b80fe6
 
b80fe6
-DEFINE_SECTION_CLEANUP_FUNCTIONS(QDisc, qdisc_free);
b80fe6
+DEFINE_SECTION_CLEANUP_FUNCTIONS(QDisc, qdisc_unref);
b80fe6
 
b80fe6
 CONFIG_PARSER_PROTOTYPE(config_parse_qdisc_parent);
b80fe6
 CONFIG_PARSER_PROTOTYPE(config_parse_qdisc_handle);
b80fe6
diff --git a/src/network/tc/sfb.c b/src/network/tc/sfb.c
b80fe6
index 861c5fe2a0c0b..07fac6700f693 100644
b80fe6
--- a/src/network/tc/sfb.c
b80fe6
+++ b/src/network/tc/sfb.c
b80fe6
@@ -60,7 +60,7 @@ int config_parse_stochastic_fair_blue_u32(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         StochasticFairBlue *sfb;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         int r;
b80fe6
diff --git a/src/network/tc/sfq.c b/src/network/tc/sfq.c
b80fe6
index 92dbae1166a66..78778653439cb 100644
b80fe6
--- a/src/network/tc/sfq.c
b80fe6
+++ b/src/network/tc/sfq.c
b80fe6
@@ -44,7 +44,7 @@ int config_parse_stochastic_fairness_queueing_perturb_period(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         StochasticFairnessQueueing *sfq;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         int r;
b80fe6
diff --git a/src/network/tc/tbf.c b/src/network/tc/tbf.c
b80fe6
index 647fc8cb1eb8a..3e7a3098dab3f 100644
b80fe6
--- a/src/network/tc/tbf.c
b80fe6
+++ b/src/network/tc/tbf.c
b80fe6
@@ -122,7 +122,7 @@ int config_parse_token_bucket_filter_size(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         TokenBucketFilter *tbf;
b80fe6
         uint64_t k;
b80fe6
@@ -195,7 +195,7 @@ int config_parse_token_bucket_filter_rate(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         TokenBucketFilter *tbf;
b80fe6
         uint64_t k, *p;
b80fe6
@@ -256,7 +256,7 @@ int config_parse_token_bucket_filter_latency(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         TokenBucketFilter *tbf;
b80fe6
         usec_t u;
b80fe6
diff --git a/src/network/tc/teql.c b/src/network/tc/teql.c
b80fe6
index dcb149dbe2a5b..f4fa331f523c2 100644
b80fe6
--- a/src/network/tc/teql.c
b80fe6
+++ b/src/network/tc/teql.c
b80fe6
@@ -50,7 +50,7 @@ int config_parse_trivial_link_equalizer_id(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
+        _cleanup_(qdisc_unref_or_set_invalidp) QDisc *qdisc = NULL;
b80fe6
         TrivialLinkEqualizer *teql;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         unsigned id;
b80fe6
142bf4
From 51800c582a03cfb58de19020b4fd81cf6754c17f Mon Sep 17 00:00:00 2001
b80fe6
From: Yu Watanabe <watanabe.yu+github@gmail.com>
b80fe6
Date: Mon, 2 Sep 2024 11:59:51 +0900
b80fe6
Subject: [PATCH 02/10] network/tclass: introduce tclass_ref() and
b80fe6
 tclass_unref()
b80fe6
b80fe6
No functional change, just refactoring and preparation for later change.
142bf4
142bf4
(cherry picked from commit 541d0ed20a4e8572536d3f5268e6ffa84c9faa04)
b80fe6
---
b80fe6
 src/network/networkd-network.c |   2 +-
b80fe6
 src/network/tc/drr.c           |   2 +-
b80fe6
 src/network/tc/htb.c           |   6 +-
b80fe6
 src/network/tc/qfq.c           |   4 +-
b80fe6
 src/network/tc/tclass.c        | 101 +++++++++++++++++++++++----------
b80fe6
 src/network/tc/tclass.h        |   7 ++-
b80fe6
 6 files changed, 84 insertions(+), 38 deletions(-)
b80fe6
b80fe6
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
142bf4
index 155e9568e2a27..d4161a608dae3 100644
b80fe6
--- a/src/network/networkd-network.c
b80fe6
+++ b/src/network/networkd-network.c
142bf4
@@ -800,7 +800,7 @@ static Network *network_free(Network *network) {
b80fe6
         hashmap_free_with_destructor(network->dhcp_static_leases_by_section, dhcp_static_lease_free);
b80fe6
         ordered_hashmap_free_with_destructor(network->sr_iov_by_section, sr_iov_free);
b80fe6
         hashmap_free(network->qdiscs_by_section);
b80fe6
-        hashmap_free_with_destructor(network->tclasses_by_section, tclass_free);
b80fe6
+        hashmap_free(network->tclasses_by_section);
b80fe6
 
b80fe6
         return mfree(network);
b80fe6
 }
b80fe6
diff --git a/src/network/tc/drr.c b/src/network/tc/drr.c
b80fe6
index 373911bc70f31..5d754101de2af 100644
b80fe6
--- a/src/network/tc/drr.c
b80fe6
+++ b/src/network/tc/drr.c
b80fe6
@@ -54,7 +54,7 @@ int config_parse_drr_size(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
b80fe6
+        _cleanup_(tclass_unref_or_set_invalidp) TClass *tclass = NULL;
b80fe6
         DeficitRoundRobinSchedulerClass *drr;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         uint64_t u;
b80fe6
diff --git a/src/network/tc/htb.c b/src/network/tc/htb.c
b80fe6
index 8f1faa1dc5d45..39f436a804d2a 100644
b80fe6
--- a/src/network/tc/htb.c
b80fe6
+++ b/src/network/tc/htb.c
b80fe6
@@ -251,7 +251,7 @@ int config_parse_hierarchy_token_bucket_class_u32(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
b80fe6
+        _cleanup_(tclass_unref_or_set_invalidp) TClass *tclass = NULL;
b80fe6
         HierarchyTokenBucketClass *htb;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         uint32_t v;
b80fe6
@@ -304,7 +304,7 @@ int config_parse_hierarchy_token_bucket_class_size(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
b80fe6
+        _cleanup_(tclass_unref_or_set_invalidp) TClass *tclass = NULL;
b80fe6
         HierarchyTokenBucketClass *htb;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         uint64_t v;
b80fe6
@@ -387,7 +387,7 @@ int config_parse_hierarchy_token_bucket_class_rate(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
b80fe6
+        _cleanup_(tclass_unref_or_set_invalidp) TClass *tclass = NULL;
b80fe6
         HierarchyTokenBucketClass *htb;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         uint64_t *v;
b80fe6
diff --git a/src/network/tc/qfq.c b/src/network/tc/qfq.c
b80fe6
index 7702e6ff6e733..0da53a89e432e 100644
b80fe6
--- a/src/network/tc/qfq.c
b80fe6
+++ b/src/network/tc/qfq.c
b80fe6
@@ -62,7 +62,7 @@ int config_parse_quick_fair_queueing_weight(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
b80fe6
+        _cleanup_(tclass_unref_or_set_invalidp) TClass *tclass = NULL;
b80fe6
         QuickFairQueueingClass *qfq;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         uint32_t v;
b80fe6
@@ -122,7 +122,7 @@ int config_parse_quick_fair_queueing_max_packet(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
b80fe6
+        _cleanup_(tclass_unref_or_set_invalidp) TClass *tclass = NULL;
b80fe6
         QuickFairQueueingClass *qfq;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         uint64_t v;
b80fe6
diff --git a/src/network/tc/tclass.c b/src/network/tc/tclass.c
b80fe6
index fcbe8cbcf46bd..168d93e1c521f 100644
b80fe6
--- a/src/network/tc/tclass.c
b80fe6
+++ b/src/network/tc/tclass.c
b80fe6
@@ -24,8 +24,54 @@ const TClassVTable * const tclass_vtable[_TCLASS_KIND_MAX] = {
b80fe6
         [TCLASS_KIND_QFQ] = &qfq_tclass_vtable,
b80fe6
 };
b80fe6
 
b80fe6
+static TClass* tclass_detach_impl(TClass *tclass) {
b80fe6
+        assert(tclass);
b80fe6
+        assert(!tclass->link || !tclass->network);
b80fe6
+
b80fe6
+        if (tclass->network) {
b80fe6
+                assert(tclass->section);
b80fe6
+                hashmap_remove(tclass->network->tclasses_by_section, tclass->section);
b80fe6
+
b80fe6
+                tclass->network = NULL;
b80fe6
+                return tclass;
b80fe6
+        }
b80fe6
+
b80fe6
+        if (tclass->link) {
b80fe6
+                set_remove(tclass->link->tclasses, tclass);
b80fe6
+
b80fe6
+                tclass->link = NULL;
b80fe6
+                return tclass;
b80fe6
+        }
b80fe6
+
b80fe6
+        return NULL;
b80fe6
+}
b80fe6
+
b80fe6
+static void tclass_detach(TClass *tclass) {
b80fe6
+        assert(tclass);
b80fe6
+
b80fe6
+        tclass_unref(tclass_detach_impl(tclass));
b80fe6
+}
b80fe6
+
b80fe6
+static void tclass_hash_func(const TClass *tclass, struct siphash *state);
b80fe6
+static int tclass_compare_func(const TClass *a, const TClass *b);
b80fe6
+
b80fe6
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
b80fe6
+        tclass_hash_ops,
b80fe6
+        TClass,
b80fe6
+        tclass_hash_func,
b80fe6
+        tclass_compare_func,
b80fe6
+        tclass_detach);
b80fe6
+
b80fe6
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
b80fe6
+        tclass_section_hash_ops,
b80fe6
+        ConfigSection,
b80fe6
+        config_section_hash_func,
b80fe6
+        config_section_compare_func,
b80fe6
+        TClass,
b80fe6
+        tclass_detach);
b80fe6
+
b80fe6
 static int tclass_new(TClassKind kind, TClass **ret) {
b80fe6
-        _cleanup_(tclass_freep) TClass *tclass = NULL;
b80fe6
+        _cleanup_(tclass_unrefp) TClass *tclass = NULL;
b80fe6
         int r;
b80fe6
 
b80fe6
         if (kind == _TCLASS_KIND_INVALID) {
b80fe6
@@ -34,6 +80,7 @@ static int tclass_new(TClassKind kind, TClass **ret) {
b80fe6
                         return -ENOMEM;
b80fe6
 
b80fe6
                 *tclass = (TClass) {
b80fe6
+                        .n_ref = 1,
b80fe6
                         .parent = TC_H_ROOT,
b80fe6
                         .kind = kind,
b80fe6
                 };
b80fe6
@@ -43,6 +90,7 @@ static int tclass_new(TClassKind kind, TClass **ret) {
b80fe6
                 if (!tclass)
b80fe6
                         return -ENOMEM;
b80fe6
 
b80fe6
+                tclass->n_ref = 1;
b80fe6
                 tclass->parent = TC_H_ROOT;
b80fe6
                 tclass->kind = kind;
b80fe6
 
b80fe6
@@ -60,7 +108,7 @@ static int tclass_new(TClassKind kind, TClass **ret) {
b80fe6
 
b80fe6
 int tclass_new_static(TClassKind kind, Network *network, const char *filename, unsigned section_line, TClass **ret) {
b80fe6
         _cleanup_(config_section_freep) ConfigSection *n = NULL;
b80fe6
-        _cleanup_(tclass_freep) TClass *tclass = NULL;
b80fe6
+        _cleanup_(tclass_unrefp) TClass *tclass = NULL;
b80fe6
         TClass *existing;
b80fe6
         int r;
b80fe6
 
b80fe6
@@ -90,7 +138,7 @@ int tclass_new_static(TClassKind kind, Network *network, const char *filename, u
b80fe6
         tclass->section = TAKE_PTR(n);
b80fe6
         tclass->source = NETWORK_CONFIG_SOURCE_STATIC;
b80fe6
 
b80fe6
-        r = hashmap_ensure_put(&network->tclasses_by_section, &config_section_hash_ops, tclass->section, tclass);
b80fe6
+        r = hashmap_ensure_put(&network->tclasses_by_section, &tclass_section_hash_ops, tclass->section, tclass);
b80fe6
         if (r < 0)
b80fe6
                 return r;
b80fe6
 
b80fe6
@@ -98,22 +146,20 @@ int tclass_new_static(TClassKind kind, Network *network, const char *filename, u
b80fe6
         return 0;
b80fe6
 }
b80fe6
 
b80fe6
-TClass* tclass_free(TClass *tclass) {
b80fe6
+static TClass* tclass_free(TClass *tclass) {
b80fe6
         if (!tclass)
b80fe6
                 return NULL;
b80fe6
 
b80fe6
-        if (tclass->network && tclass->section)
b80fe6
-                hashmap_remove(tclass->network->tclasses_by_section, tclass->section);
b80fe6
+        tclass_detach_impl(tclass);
b80fe6
 
b80fe6
         config_section_free(tclass->section);
b80fe6
 
b80fe6
-        if (tclass->link)
b80fe6
-                set_remove(tclass->link->tclasses, tclass);
b80fe6
-
b80fe6
         free(tclass->tca_kind);
b80fe6
         return mfree(tclass);
b80fe6
 }
b80fe6
 
b80fe6
+DEFINE_TRIVIAL_REF_UNREF_FUNC(TClass, tclass, tclass_free);
b80fe6
+
b80fe6
 static const char *tclass_get_tca_kind(const TClass *tclass) {
b80fe6
         assert(tclass);
b80fe6
 
b80fe6
@@ -147,13 +193,6 @@ static int tclass_compare_func(const TClass *a, const TClass *b) {
b80fe6
         return strcmp_ptr(tclass_get_tca_kind(a), tclass_get_tca_kind(b));
b80fe6
 }
b80fe6
 
b80fe6
-DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
b80fe6
-        tclass_hash_ops,
b80fe6
-        TClass,
b80fe6
-        tclass_hash_func,
b80fe6
-        tclass_compare_func,
b80fe6
-        tclass_free);
b80fe6
-
b80fe6
 static int tclass_get(Link *link, const TClass *in, TClass **ret) {
b80fe6
         TClass *existing;
b80fe6
 
b80fe6
@@ -169,11 +208,13 @@ static int tclass_get(Link *link, const TClass *in, TClass **ret) {
b80fe6
         return 0;
b80fe6
 }
b80fe6
 
b80fe6
-static int tclass_add(Link *link, TClass *tclass) {
b80fe6
+static int tclass_attach(Link *link, TClass *tclass) {
b80fe6
         int r;
b80fe6
 
b80fe6
         assert(link);
b80fe6
         assert(tclass);
b80fe6
+        assert(!tclass->link);
b80fe6
+        assert(!tclass->network);
b80fe6
 
b80fe6
         r = set_ensure_put(&link->tclasses, &tclass_hash_ops, tclass);
b80fe6
         if (r < 0)
b80fe6
@@ -182,11 +223,12 @@ static int tclass_add(Link *link, TClass *tclass) {
b80fe6
                 return -EEXIST;
b80fe6
 
b80fe6
         tclass->link = link;
b80fe6
+        tclass_ref(tclass);
b80fe6
         return 0;
b80fe6
 }
b80fe6
 
b80fe6
 static int tclass_dup(const TClass *src, TClass **ret) {
b80fe6
-        _cleanup_(tclass_freep) TClass *dst = NULL;
b80fe6
+        _cleanup_(tclass_unrefp) TClass *dst = NULL;
b80fe6
 
b80fe6
         assert(src);
b80fe6
         assert(ret);
b80fe6
@@ -198,7 +240,8 @@ static int tclass_dup(const TClass *src, TClass **ret) {
b80fe6
         if (!dst)
b80fe6
                 return -ENOMEM;
b80fe6
 
b80fe6
-        /* clear all pointers */
b80fe6
+        /* clear the reference counter and all pointers */
b80fe6
+        dst->n_ref = 1;
b80fe6
         dst->network = NULL;
b80fe6
         dst->section = NULL;
b80fe6
         dst->link = NULL;
b80fe6
@@ -286,7 +329,7 @@ void link_tclass_drop_marked(Link *link) {
b80fe6
 
b80fe6
                 if (tclass->state == 0) {
b80fe6
                         log_tclass_debug(tclass, link, "Forgetting");
b80fe6
-                        tclass_free(tclass);
b80fe6
+                        tclass_detach(tclass);
b80fe6
                 } else
b80fe6
                         log_tclass_debug(tclass, link, "Removed");
b80fe6
         }
b80fe6
@@ -393,17 +436,17 @@ int link_request_tclass(Link *link, TClass *tclass) {
b80fe6
         assert(tclass);
b80fe6
 
b80fe6
         if (tclass_get(link, tclass, &existing) < 0) {
b80fe6
-                _cleanup_(tclass_freep) TClass *tmp = NULL;
b80fe6
+                _cleanup_(tclass_unrefp) TClass *tmp = NULL;
b80fe6
 
b80fe6
                 r = tclass_dup(tclass, &tmp);
b80fe6
                 if (r < 0)
b80fe6
                         return log_oom();
b80fe6
 
b80fe6
-                r = tclass_add(link, tmp);
b80fe6
+                r = tclass_attach(link, tmp);
b80fe6
                 if (r < 0)
b80fe6
                         return log_link_warning_errno(link, r, "Failed to store TClass: %m");
b80fe6
 
b80fe6
-                existing = TAKE_PTR(tmp);
b80fe6
+                existing = tmp;
b80fe6
         } else
b80fe6
                 existing->source = tclass->source;
b80fe6
 
b80fe6
@@ -426,7 +469,7 @@ int link_request_tclass(Link *link, TClass *tclass) {
b80fe6
 }
b80fe6
 
b80fe6
 int manager_rtnl_process_tclass(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
b80fe6
-        _cleanup_(tclass_freep) TClass *tmp = NULL;
b80fe6
+        _cleanup_(tclass_unrefp) TClass *tmp = NULL;
b80fe6
         TClass *tclass = NULL;
b80fe6
         Link *link;
b80fe6
         uint16_t type;
b80fe6
@@ -501,13 +544,13 @@ int manager_rtnl_process_tclass(sd_netlink *rtnl, sd_netlink_message *message, M
b80fe6
                         tclass_enter_configured(tmp);
b80fe6
                         log_tclass_debug(tmp, link, "Received new");
b80fe6
 
b80fe6
-                        r = tclass_add(link, tmp);
b80fe6
+                        r = tclass_attach(link, tmp);
b80fe6
                         if (r < 0) {
b80fe6
                                 log_link_warning_errno(link, r, "Failed to remember TClass, ignoring: %m");
b80fe6
                                 return 0;
b80fe6
                         }
b80fe6
 
b80fe6
-                        tclass = TAKE_PTR(tmp);
b80fe6
+                        tclass = tmp;
b80fe6
                 }
b80fe6
 
b80fe6
                 break;
b80fe6
@@ -566,7 +609,7 @@ void network_drop_invalid_tclass(Network *network) {
b80fe6
 
b80fe6
         HASHMAP_FOREACH(tclass, network->tclasses_by_section)
b80fe6
                 if (tclass_section_verify(tclass) < 0)
b80fe6
-                        tclass_free(tclass);
b80fe6
+                        tclass_detach(tclass);
b80fe6
 }
b80fe6
 
b80fe6
 int config_parse_tclass_parent(
b80fe6
@@ -581,7 +624,7 @@ int config_parse_tclass_parent(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
b80fe6
+        _cleanup_(tclass_unref_or_set_invalidp) TClass *tclass = NULL;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         int r;
b80fe6
 
b80fe6
@@ -627,7 +670,7 @@ int config_parse_tclass_classid(
b80fe6
                 void *data,
b80fe6
                 void *userdata) {
b80fe6
 
b80fe6
-        _cleanup_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
b80fe6
+        _cleanup_(tclass_unref_or_set_invalidp) TClass *tclass = NULL;
b80fe6
         Network *network = ASSERT_PTR(data);
b80fe6
         int r;
b80fe6
 
b80fe6
diff --git a/src/network/tc/tclass.h b/src/network/tc/tclass.h
b80fe6
index 85df57d42c21e..44f71814501dc 100644
b80fe6
--- a/src/network/tc/tclass.h
b80fe6
+++ b/src/network/tc/tclass.h
b80fe6
@@ -24,6 +24,8 @@ typedef struct TClass {
b80fe6
         NetworkConfigSource source;
b80fe6
         NetworkConfigState state;
b80fe6
 
b80fe6
+        unsigned n_ref;
b80fe6
+
b80fe6
         uint32_t classid;
b80fe6
         uint32_t parent;
b80fe6
 
b80fe6
@@ -55,7 +57,8 @@ extern const TClassVTable * const tclass_vtable[_TCLASS_KIND_MAX];
b80fe6
 
b80fe6
 DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(TClass, tclass);
b80fe6
 
b80fe6
-TClass* tclass_free(TClass *tclass);
b80fe6
+TClass* tclass_ref(TClass *tclass);
b80fe6
+TClass* tclass_unref(TClass *tclass);
b80fe6
 int tclass_new_static(TClassKind kind, Network *network, const char *filename, unsigned section_line, TClass **ret);
b80fe6
 
b80fe6
 void tclass_mark_recursive(TClass *tclass);
b80fe6
@@ -71,7 +74,7 @@ void network_drop_invalid_tclass(Network *network);
b80fe6
 int manager_rtnl_process_tclass(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);
b80fe6
 int link_enumerate_tclass(Link *link, uint32_t parent);
b80fe6
 
b80fe6
-DEFINE_SECTION_CLEANUP_FUNCTIONS(TClass, tclass_free);
b80fe6
+DEFINE_SECTION_CLEANUP_FUNCTIONS(TClass, tclass_unref);
b80fe6
 
b80fe6
 CONFIG_PARSER_PROTOTYPE(config_parse_tclass_parent);
b80fe6
 CONFIG_PARSER_PROTOTYPE(config_parse_tclass_classid);
b80fe6
142bf4
From 7594bd35d1756a5ce938763a26450c5ba0c99df5 Mon Sep 17 00:00:00 2001
b80fe6
From: Yu Watanabe <watanabe.yu+github@gmail.com>
b80fe6
Date: Mon, 2 Sep 2024 13:03:09 +0900
b80fe6
Subject: [PATCH 03/10] network/neighbor: skip requesting neighbor if it is
b80fe6
 already requested
b80fe6
142bf4
(cherry picked from commit 4bdc3d85aee073cb7d22b53bde13fd878333b12c)
b80fe6
---
b80fe6
 src/network/networkd-neighbor.c | 3 +++
b80fe6
 1 file changed, 3 insertions(+)
b80fe6
b80fe6
diff --git a/src/network/networkd-neighbor.c b/src/network/networkd-neighbor.c
b80fe6
index 6b81950f96c63..3377bb056e589 100644
b80fe6
--- a/src/network/networkd-neighbor.c
b80fe6
+++ b/src/network/networkd-neighbor.c
b80fe6
@@ -343,6 +343,9 @@ static int link_request_neighbor(Link *link, const Neighbor *neighbor) {
b80fe6
                 return 0;
b80fe6
         }
b80fe6
 
b80fe6
+        if (neighbor_get_request(link, neighbor, NULL) >= 0)
b80fe6
+                return 0; /* already requested, skipping. */
b80fe6
+
b80fe6
         r = neighbor_dup(neighbor, &tmp);
b80fe6
         if (r < 0)
b80fe6
                 return r;
b80fe6
142bf4
From b7ca4295dd22d7998ecbe87ef5883c106b1e7182 Mon Sep 17 00:00:00 2001
b80fe6
From: Yu Watanabe <watanabe.yu+github@gmail.com>
b80fe6
Date: Mon, 2 Sep 2024 12:55:59 +0900
b80fe6
Subject: [PATCH 04/10] network/qdisc: skip requesting qdisc if it is already
b80fe6
 requested
b80fe6
142bf4
(cherry picked from commit 998973e313a680a3434c2dd23384e86ff01726f3)
b80fe6
---
b80fe6
 src/network/tc/qdisc.c | 27 +++++++++++++++++++++++++++
b80fe6
 1 file changed, 27 insertions(+)
b80fe6
b80fe6
diff --git a/src/network/tc/qdisc.c b/src/network/tc/qdisc.c
b80fe6
index d372481d6280b..1475b4aced4de 100644
b80fe6
--- a/src/network/tc/qdisc.c
b80fe6
+++ b/src/network/tc/qdisc.c
b80fe6
@@ -238,6 +238,30 @@ static int qdisc_get(Link *link, const QDisc *in, QDisc **ret) {
b80fe6
         return 0;
b80fe6
 }
b80fe6
 
b80fe6
+static int qdisc_get_request(Link *link, const QDisc *qdisc, Request **ret) {
b80fe6
+        Request *req;
b80fe6
+
b80fe6
+        assert(link);
b80fe6
+        assert(link->manager);
b80fe6
+        assert(qdisc);
b80fe6
+
b80fe6
+        req = ordered_set_get(
b80fe6
+                        link->manager->request_queue,
b80fe6
+                        &(Request) {
b80fe6
+                                .link = link,
b80fe6
+                                .type = REQUEST_TYPE_TC_QDISC,
b80fe6
+                                .userdata = (void*) qdisc,
b80fe6
+                                .hash_func = (hash_func_t) qdisc_hash_func,
b80fe6
+                                .compare_func = (compare_func_t) qdisc_compare_func,
b80fe6
+                        });
b80fe6
+        if (!req)
b80fe6
+                return -ENOENT;
b80fe6
+
b80fe6
+        if (ret)
b80fe6
+                *ret = req;
b80fe6
+        return 0;
b80fe6
+}
b80fe6
+
b80fe6
 static int qdisc_attach(Link *link, QDisc *qdisc) {
b80fe6
         int r;
b80fe6
 
b80fe6
@@ -485,6 +509,9 @@ int link_request_qdisc(Link *link, QDisc *qdisc) {
b80fe6
         assert(link);
b80fe6
         assert(qdisc);
b80fe6
 
b80fe6
+        if (qdisc_get_request(link, qdisc, NULL) >= 0)
b80fe6
+                return 0; /* already requested, skipping. */
b80fe6
+
b80fe6
         if (qdisc_get(link, qdisc, &existing) < 0) {
b80fe6
                 _cleanup_(qdisc_unrefp) QDisc *tmp = NULL;
b80fe6
 
b80fe6
142bf4
From e51b9362c36f787e231e41e485dbdf0cebf94ded Mon Sep 17 00:00:00 2001
b80fe6
From: Yu Watanabe <watanabe.yu+github@gmail.com>
b80fe6
Date: Mon, 2 Sep 2024 13:06:54 +0900
b80fe6
Subject: [PATCH 05/10] network/tclass: skip requesting tclass if it is already
b80fe6
 requested
b80fe6
142bf4
(cherry picked from commit e3f91033aea354c28011e841b7043549a1a5226b)
b80fe6
---
b80fe6
 src/network/tc/tclass.c | 27 +++++++++++++++++++++++++++
b80fe6
 1 file changed, 27 insertions(+)
b80fe6
b80fe6
diff --git a/src/network/tc/tclass.c b/src/network/tc/tclass.c
b80fe6
index 168d93e1c521f..926ec69adf0fe 100644
b80fe6
--- a/src/network/tc/tclass.c
b80fe6
+++ b/src/network/tc/tclass.c
b80fe6
@@ -208,6 +208,30 @@ static int tclass_get(Link *link, const TClass *in, TClass **ret) {
b80fe6
         return 0;
b80fe6
 }
b80fe6
 
b80fe6
+static int tclass_get_request(Link *link, const TClass *tclass, Request **ret) {
b80fe6
+        Request *req;
b80fe6
+
b80fe6
+        assert(link);
b80fe6
+        assert(link->manager);
b80fe6
+        assert(tclass);
b80fe6
+
b80fe6
+        req = ordered_set_get(
b80fe6
+                        link->manager->request_queue,
b80fe6
+                        &(Request) {
b80fe6
+                                .link = link,
b80fe6
+                                .type = REQUEST_TYPE_TC_CLASS,
b80fe6
+                                .userdata = (void*) tclass,
b80fe6
+                                .hash_func = (hash_func_t) tclass_hash_func,
b80fe6
+                                .compare_func = (compare_func_t) tclass_compare_func,
b80fe6
+                        });
b80fe6
+        if (!req)
b80fe6
+                return -ENOENT;
b80fe6
+
b80fe6
+        if (ret)
b80fe6
+                *ret = req;
b80fe6
+        return 0;
b80fe6
+}
b80fe6
+
b80fe6
 static int tclass_attach(Link *link, TClass *tclass) {
b80fe6
         int r;
b80fe6
 
b80fe6
@@ -435,6 +459,9 @@ int link_request_tclass(Link *link, TClass *tclass) {
b80fe6
         assert(link);
b80fe6
         assert(tclass);
b80fe6
 
b80fe6
+        if (tclass_get_request(link, tclass, NULL) >= 0)
b80fe6
+                return 0; /* already requested, skipping. */
b80fe6
+
b80fe6
         if (tclass_get(link, tclass, &existing) < 0) {
b80fe6
                 _cleanup_(tclass_unrefp) TClass *tmp = NULL;
b80fe6
 
b80fe6
142bf4
From 36e35ff4583b0f4b609a82e1a7178c3c810da4f9 Mon Sep 17 00:00:00 2001
b80fe6
From: Yu Watanabe <watanabe.yu+github@gmail.com>
b80fe6
Date: Mon, 2 Sep 2024 13:20:59 +0900
b80fe6
Subject: [PATCH 06/10] network/qdisc: make qdisc_drop() static
b80fe6
b80fe6
This also drops unused constant return value.
142bf4
142bf4
(cherry picked from commit 4a31c768a9f4e1ff957880a5c4f52d36d768e4a4)
b80fe6
---
b80fe6
 src/network/tc/qdisc.c | 4 +---
b80fe6
 src/network/tc/qdisc.h | 1 -
b80fe6
 2 files changed, 1 insertion(+), 4 deletions(-)
b80fe6
b80fe6
diff --git a/src/network/tc/qdisc.c b/src/network/tc/qdisc.c
b80fe6
index 1475b4aced4de..851f624c45954 100644
b80fe6
--- a/src/network/tc/qdisc.c
b80fe6
+++ b/src/network/tc/qdisc.c
b80fe6
@@ -392,7 +392,7 @@ void link_qdisc_drop_marked(Link *link) {
b80fe6
         }
b80fe6
 }
b80fe6
 
b80fe6
-QDisc* qdisc_drop(QDisc *qdisc) {
b80fe6
+static void qdisc_drop(QDisc *qdisc) {
b80fe6
         assert(qdisc);
b80fe6
         assert(qdisc->link);
b80fe6
 
b80fe6
@@ -401,8 +401,6 @@ QDisc* qdisc_drop(QDisc *qdisc) {
b80fe6
         /* link_qdisc_drop_marked() may invalidate qdisc, so run link_tclass_drop_marked() first. */
b80fe6
         link_tclass_drop_marked(qdisc->link);
b80fe6
         link_qdisc_drop_marked(qdisc->link);
b80fe6
-
b80fe6
-        return NULL;
b80fe6
 }
b80fe6
 
b80fe6
 static int qdisc_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, QDisc *qdisc) {
b80fe6
diff --git a/src/network/tc/qdisc.h b/src/network/tc/qdisc.h
b80fe6
index 3989ad7651531..75351fb8ada1f 100644
b80fe6
--- a/src/network/tc/qdisc.h
b80fe6
+++ b/src/network/tc/qdisc.h
b80fe6
@@ -81,7 +81,6 @@ QDisc* qdisc_unref(QDisc *qdisc);
b80fe6
 int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, unsigned section_line, QDisc **ret);
b80fe6
 
b80fe6
 void qdisc_mark_recursive(QDisc *qdisc);
b80fe6
-QDisc* qdisc_drop(QDisc *qdisc);
b80fe6
 void link_qdisc_drop_marked(Link *link);
b80fe6
 
b80fe6
 int link_find_qdisc(Link *link, uint32_t handle, const char *kind, QDisc **qdisc);
b80fe6
142bf4
From 29aa394b2b267d993ae7ea713396315b0fe239ff Mon Sep 17 00:00:00 2001
b80fe6
From: Yu Watanabe <watanabe.yu+github@gmail.com>
b80fe6
Date: Mon, 2 Sep 2024 13:22:41 +0900
b80fe6
Subject: [PATCH 07/10] network/tclass: make tclass_drop() static
b80fe6
b80fe6
This also drops unused constant return value.
142bf4
142bf4
(cherry picked from commit 3d7f26d9f2d2e1129dfd64f786ae04d75d546a61)
b80fe6
---
b80fe6
 src/network/tc/tclass.c | 6 ++----
b80fe6
 src/network/tc/tclass.h | 1 -
b80fe6
 2 files changed, 2 insertions(+), 5 deletions(-)
b80fe6
b80fe6
diff --git a/src/network/tc/tclass.c b/src/network/tc/tclass.c
b80fe6
index 926ec69adf0fe..3c74f95992ce2 100644
b80fe6
--- a/src/network/tc/tclass.c
b80fe6
+++ b/src/network/tc/tclass.c
b80fe6
@@ -359,7 +359,7 @@ void link_tclass_drop_marked(Link *link) {
b80fe6
         }
b80fe6
 }
b80fe6
 
b80fe6
-TClass* tclass_drop(TClass *tclass) {
b80fe6
+static void tclass_drop(TClass *tclass) {
b80fe6
         assert(tclass);
b80fe6
 
b80fe6
         tclass_mark_recursive(tclass);
b80fe6
@@ -367,8 +367,6 @@ TClass* tclass_drop(TClass *tclass) {
b80fe6
         /* link_tclass_drop_marked() may invalidate tclass, so run link_qdisc_drop_marked() first. */
b80fe6
         link_qdisc_drop_marked(tclass->link);
b80fe6
         link_tclass_drop_marked(tclass->link);
b80fe6
-
b80fe6
-        return NULL;
b80fe6
 }
b80fe6
 
b80fe6
 static int tclass_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, TClass *tclass) {
b80fe6
@@ -584,7 +582,7 @@ int manager_rtnl_process_tclass(sd_netlink *rtnl, sd_netlink_message *message, M
b80fe6
 
b80fe6
         case RTM_DELTCLASS:
b80fe6
                 if (tclass)
b80fe6
-                        (void) tclass_drop(tclass);
b80fe6
+                        tclass_drop(tclass);
b80fe6
                 else
b80fe6
                         log_tclass_debug(tmp, link, "Kernel removed unknown");
b80fe6
 
b80fe6
diff --git a/src/network/tc/tclass.h b/src/network/tc/tclass.h
b80fe6
index 44f71814501dc..ce51b2499ecc8 100644
b80fe6
--- a/src/network/tc/tclass.h
b80fe6
+++ b/src/network/tc/tclass.h
b80fe6
@@ -62,7 +62,6 @@ TClass* tclass_unref(TClass *tclass);
b80fe6
 int tclass_new_static(TClassKind kind, Network *network, const char *filename, unsigned section_line, TClass **ret);
b80fe6
 
b80fe6
 void tclass_mark_recursive(TClass *tclass);
b80fe6
-TClass* tclass_drop(TClass *tclass);
b80fe6
 void link_tclass_drop_marked(Link *link);
b80fe6
 
b80fe6
 int link_find_tclass(Link *link, uint32_t classid, TClass **ret);
b80fe6
142bf4
From 02d55715180e4d2002fe2e1f14f2945e48674a22 Mon Sep 17 00:00:00 2001
b80fe6
From: Yu Watanabe <watanabe.yu+github@gmail.com>
b80fe6
Date: Mon, 2 Sep 2024 12:27:04 +0900
b80fe6
Subject: [PATCH 08/10] network/qdisc: do not save qdisc to Link before it is
b80fe6
 configured
b80fe6
b80fe6
Otherwise, if the same kind of qdisc is already assigned, parameters
b80fe6
configured in .network file will not be used. So, let's first copy the
b80fe6
qdisc and put it on Request, then on success generate a new copy based
b80fe6
on the netlink notification and store it to Link.
b80fe6
b80fe6
This is the same as 0a0c2672dbd22dc85d660e5baa7e1bef701beb88,
b80fe6
65f5f581568448d6098358b704cae10a656d09f0, and friends, but for qdisc.
b80fe6
b80fe6
Preparation for fixing #31226.
142bf4
142bf4
(cherry picked from commit 0a35458f5eff59225aaa53734e6194f3e548ba03)
b80fe6
---
b80fe6
 src/network/tc/qdisc.c | 115 ++++++++++++++++++++++-------------------
b80fe6
 src/network/tc/qdisc.h |   2 +-
b80fe6
 src/network/tc/tc.c    |   2 +-
b80fe6
 3 files changed, 65 insertions(+), 54 deletions(-)
b80fe6
b80fe6
diff --git a/src/network/tc/qdisc.c b/src/network/tc/qdisc.c
b80fe6
index 851f624c45954..0f89d844f585a 100644
b80fe6
--- a/src/network/tc/qdisc.c
b80fe6
+++ b/src/network/tc/qdisc.c
b80fe6
@@ -378,11 +378,15 @@ void link_qdisc_drop_marked(Link *link) {
b80fe6
         assert(link);
b80fe6
 
b80fe6
         SET_FOREACH(qdisc, link->qdiscs) {
b80fe6
+                Request *req;
b80fe6
+
b80fe6
                 if (!qdisc_is_marked(qdisc))
b80fe6
                         continue;
b80fe6
 
b80fe6
                 qdisc_unmark(qdisc);
b80fe6
                 qdisc_enter_removed(qdisc);
b80fe6
+                if (qdisc_get_request(link, qdisc, &req) >= 0)
b80fe6
+                        qdisc_enter_removed(req->userdata);
b80fe6
 
b80fe6
                 if (qdisc->state == 0) {
b80fe6
                         log_qdisc_debug(qdisc, link, "Forgetting");
b80fe6
@@ -483,6 +487,7 @@ static bool qdisc_is_ready_to_configure(QDisc *qdisc, Link *link) {
b80fe6
 }
b80fe6
 
b80fe6
 static int qdisc_process_request(Request *req, Link *link, QDisc *qdisc) {
b80fe6
+        QDisc *existing;
b80fe6
         int r;
b80fe6
 
b80fe6
         assert(req);
b80fe6
@@ -497,54 +502,56 @@ static int qdisc_process_request(Request *req, Link *link, QDisc *qdisc) {
b80fe6
                 return log_link_warning_errno(link, r, "Failed to configure QDisc: %m");
b80fe6
 
b80fe6
         qdisc_enter_configuring(qdisc);
b80fe6
+        if (qdisc_get(link, qdisc, &existing) >= 0)
b80fe6
+                qdisc_enter_configuring(existing);
b80fe6
+
b80fe6
         return 1;
b80fe6
 }
b80fe6
 
b80fe6
-int link_request_qdisc(Link *link, QDisc *qdisc) {
b80fe6
-        QDisc *existing;
b80fe6
+int link_request_qdisc(Link *link, const QDisc *qdisc) {
b80fe6
+        _cleanup_(qdisc_unrefp) QDisc *tmp = NULL;
b80fe6
+        QDisc *existing = NULL;
b80fe6
         int r;
b80fe6
 
b80fe6
         assert(link);
b80fe6
         assert(qdisc);
b80fe6
+        assert(qdisc->source != NETWORK_CONFIG_SOURCE_FOREIGN);
b80fe6
 
b80fe6
         if (qdisc_get_request(link, qdisc, NULL) >= 0)
b80fe6
                 return 0; /* already requested, skipping. */
b80fe6
 
b80fe6
-        if (qdisc_get(link, qdisc, &existing) < 0) {
b80fe6
-                _cleanup_(qdisc_unrefp) QDisc *tmp = NULL;
b80fe6
-
b80fe6
-                r = qdisc_dup(qdisc, &tmp);
b80fe6
-                if (r < 0)
b80fe6
-                        return log_oom();
b80fe6
-
b80fe6
-                r = qdisc_attach(link, tmp);
b80fe6
-                if (r < 0)
b80fe6
-                        return log_link_warning_errno(link, r, "Failed to store QDisc: %m");
b80fe6
+        r = qdisc_dup(qdisc, &tmp);
b80fe6
+        if (r < 0)
b80fe6
+                return r;
b80fe6
 
b80fe6
-                existing = tmp;
b80fe6
-        } else
b80fe6
-                existing->source = qdisc->source;
b80fe6
+        if (qdisc_get(link, qdisc, &existing) >= 0)
b80fe6
+                /* Copy state for logging below. */
b80fe6
+                tmp->state = existing->state;
b80fe6
 
b80fe6
-        log_qdisc_debug(existing, link, "Requesting");
b80fe6
+        log_qdisc_debug(tmp, link, "Requesting");
b80fe6
         r = link_queue_request_safe(link, REQUEST_TYPE_TC_QDISC,
b80fe6
-                                    existing, NULL,
b80fe6
+                                    tmp,
b80fe6
+                                    qdisc_unref,
b80fe6
                                     qdisc_hash_func,
b80fe6
                                     qdisc_compare_func,
b80fe6
                                     qdisc_process_request,
b80fe6
                                     &link->tc_messages,
b80fe6
                                     qdisc_handler,
b80fe6
                                     NULL);
b80fe6
-        if (r < 0)
b80fe6
-                return log_link_warning_errno(link, r, "Failed to request QDisc: %m");
b80fe6
-        if (r == 0)
b80fe6
-                return 0;
b80fe6
+        if (r <= 0)
b80fe6
+                return r;
b80fe6
 
b80fe6
-        qdisc_enter_requesting(existing);
b80fe6
+        qdisc_enter_requesting(tmp);
b80fe6
+        if (existing)
b80fe6
+                qdisc_enter_requesting(existing);
b80fe6
+
b80fe6
+        TAKE_PTR(tmp);
b80fe6
         return 1;
b80fe6
 }
b80fe6
 
b80fe6
 int manager_rtnl_process_qdisc(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
b80fe6
         _cleanup_(qdisc_unrefp) QDisc *tmp = NULL;
b80fe6
+        Request *req = NULL;
b80fe6
         QDisc *qdisc = NULL;
b80fe6
         Link *link;
b80fe6
         uint16_t type;
b80fe6
@@ -609,45 +616,49 @@ int manager_rtnl_process_qdisc(sd_netlink *rtnl, sd_netlink_message *message, Ma
b80fe6
         }
b80fe6
 
b80fe6
         (void) qdisc_get(link, tmp, &qdisc);
b80fe6
+        (void) qdisc_get_request(link, tmp, &req;;
b80fe6
 
b80fe6
-        switch (type) {
b80fe6
-        case RTM_NEWQDISC:
b80fe6
-                if (qdisc) {
b80fe6
-                        qdisc_enter_configured(qdisc);
b80fe6
-                        log_qdisc_debug(qdisc, link, "Received remembered");
b80fe6
-                } else {
b80fe6
-                        qdisc_enter_configured(tmp);
b80fe6
-                        log_qdisc_debug(tmp, link, "Received new");
b80fe6
+        if (type == RTM_DELQDISC) {
b80fe6
+                if (qdisc)
b80fe6
+                        qdisc_drop(qdisc);
b80fe6
+                else
b80fe6
+                        log_qdisc_debug(tmp, link, "Kernel removed unknown");
b80fe6
 
b80fe6
-                        r = qdisc_attach(link, tmp);
b80fe6
-                        if (r < 0) {
b80fe6
-                                log_link_warning_errno(link, r, "Failed to remember QDisc, ignoring: %m");
b80fe6
-                                return 0;
b80fe6
-                        }
b80fe6
+                return 0;
b80fe6
+        }
b80fe6
 
b80fe6
-                        qdisc = tmp;
b80fe6
+        bool is_new = false;
b80fe6
+        if (!qdisc) {
b80fe6
+                /* If we did not know the qdisc, then save it. */
b80fe6
+                r = qdisc_attach(link, tmp);
b80fe6
+                if (r < 0) {
b80fe6
+                        log_link_warning_errno(link, r, "Failed to remember QDisc, ignoring: %m");
b80fe6
+                        return 0;
b80fe6
                 }
b80fe6
 
b80fe6
-                if (!m->enumerating) {
b80fe6
-                        /* Some kind of QDisc (e.g. tbf) also create an implicit class under the qdisc, but
b80fe6
-                         * the kernel may not notify about the class. Hence, we need to enumerate classes. */
b80fe6
-                        r = link_enumerate_tclass(link, qdisc->handle);
b80fe6
-                        if (r < 0)
b80fe6
-                                log_link_warning_errno(link, r, "Failed to enumerate TClass, ignoring: %m");
b80fe6
-                }
b80fe6
+                qdisc = tmp;
b80fe6
+                is_new = true;
b80fe6
+        }
b80fe6
 
b80fe6
-                break;
b80fe6
+        /* Also update information that cannot be obtained through netlink notification. */
b80fe6
+        if (req && req->waiting_reply) {
b80fe6
+                QDisc *q = ASSERT_PTR(req->userdata);
b80fe6
 
b80fe6
-        case RTM_DELQDISC:
b80fe6
-                if (qdisc)
b80fe6
-                        qdisc_drop(qdisc);
b80fe6
-                else
b80fe6
-                        log_qdisc_debug(tmp, link, "Kernel removed unknown");
b80fe6
+                qdisc->source = q->source;
b80fe6
+        }
b80fe6
 
b80fe6
-                break;
b80fe6
+        qdisc_enter_configured(qdisc);
b80fe6
+        if (req)
b80fe6
+                qdisc_enter_configured(req->userdata);
b80fe6
 
b80fe6
-        default:
b80fe6
-                assert_not_reached();
b80fe6
+        log_qdisc_debug(qdisc, link, is_new ? "Remembering" : "Received remembered");
b80fe6
+
b80fe6
+        if (!m->enumerating) {
b80fe6
+                /* Some kind of QDisc (e.g. tbf) also create an implicit class under the qdisc, but
b80fe6
+                 * the kernel may not notify about the class. Hence, we need to enumerate classes. */
b80fe6
+                r = link_enumerate_tclass(link, qdisc->handle);
b80fe6
+                if (r < 0)
b80fe6
+                        log_link_warning_errno(link, r, "Failed to enumerate TClass, ignoring: %m");
b80fe6
         }
b80fe6
 
b80fe6
         return 1;
b80fe6
diff --git a/src/network/tc/qdisc.h b/src/network/tc/qdisc.h
b80fe6
index 75351fb8ada1f..50a8f4ead1951 100644
b80fe6
--- a/src/network/tc/qdisc.h
b80fe6
+++ b/src/network/tc/qdisc.h
b80fe6
@@ -85,7 +85,7 @@ void link_qdisc_drop_marked(Link *link);
b80fe6
 
b80fe6
 int link_find_qdisc(Link *link, uint32_t handle, const char *kind, QDisc **qdisc);
b80fe6
 
b80fe6
-int link_request_qdisc(Link *link, QDisc *qdisc);
b80fe6
+int link_request_qdisc(Link *link, const QDisc *qdisc);
b80fe6
 
b80fe6
 void network_drop_invalid_qdisc(Network *network);
b80fe6
 
b80fe6
diff --git a/src/network/tc/tc.c b/src/network/tc/tc.c
b80fe6
index 8a1c5b3a3b3a1..37e30441e163f 100644
b80fe6
--- a/src/network/tc/tc.c
b80fe6
+++ b/src/network/tc/tc.c
b80fe6
@@ -20,7 +20,7 @@ int link_request_traffic_control(Link *link) {
b80fe6
         HASHMAP_FOREACH(qdisc, link->network->qdiscs_by_section) {
b80fe6
                 r = link_request_qdisc(link, qdisc);
b80fe6
                 if (r < 0)
b80fe6
-                        return r;
b80fe6
+                        return log_link_warning_errno(link, r, "Failed to request QDisc: %m");
b80fe6
         }
b80fe6
 
b80fe6
         HASHMAP_FOREACH(tclass, link->network->tclasses_by_section) {
b80fe6
142bf4
From ae6eaa526e47e520da69ad98b74bde43e7fe914f Mon Sep 17 00:00:00 2001
b80fe6
From: Yu Watanabe <watanabe.yu+github@gmail.com>
b80fe6
Date: Mon, 2 Sep 2024 13:15:49 +0900
b80fe6
Subject: [PATCH 09/10] network/tclass: do not save tclass to Link before it is
b80fe6
 configured
b80fe6
b80fe6
Otherwise, if the same kind of tclass is already assigned, parameters
b80fe6
configured in .network file will not be used. So, let's first copy the
b80fe6
tclass and put it on Request, then on success generate a new copy based
b80fe6
on the netlink notification and store it to Link.
b80fe6
b80fe6
This is the same as 0a0c2672dbd22dc85d660e5baa7e1bef701beb88,
b80fe6
65f5f581568448d6098358b704cae10a656d09f0, and friends, but for tclass.
142bf4
142bf4
(cherry picked from commit 304e2419c79778bf8811fbb5ec672a8c1197dbb3)
b80fe6
---
b80fe6
 src/network/tc/tc.c     |   2 +-
b80fe6
 src/network/tc/tclass.c | 102 ++++++++++++++++++++++------------------
b80fe6
 src/network/tc/tclass.h |   2 +-
b80fe6
 3 files changed, 58 insertions(+), 48 deletions(-)
b80fe6
b80fe6
diff --git a/src/network/tc/tc.c b/src/network/tc/tc.c
b80fe6
index 37e30441e163f..4679e86ac9789 100644
b80fe6
--- a/src/network/tc/tc.c
b80fe6
+++ b/src/network/tc/tc.c
b80fe6
@@ -26,7 +26,7 @@ int link_request_traffic_control(Link *link) {
b80fe6
         HASHMAP_FOREACH(tclass, link->network->tclasses_by_section) {
b80fe6
                 r = link_request_tclass(link, tclass);
b80fe6
                 if (r < 0)
b80fe6
-                        return r;
b80fe6
+                        return log_link_warning_errno(link, r, "Failed to request TClass: %m");
b80fe6
         }
b80fe6
 
b80fe6
         if (link->tc_messages == 0) {
b80fe6
diff --git a/src/network/tc/tclass.c b/src/network/tc/tclass.c
b80fe6
index 3c74f95992ce2..684f917c50562 100644
b80fe6
--- a/src/network/tc/tclass.c
b80fe6
+++ b/src/network/tc/tclass.c
b80fe6
@@ -345,11 +345,15 @@ void link_tclass_drop_marked(Link *link) {
b80fe6
         assert(link);
b80fe6
 
b80fe6
         SET_FOREACH(tclass, link->tclasses) {
b80fe6
+                Request *req;
b80fe6
+
b80fe6
                 if (!tclass_is_marked(tclass))
b80fe6
                         continue;
b80fe6
 
b80fe6
                 tclass_unmark(tclass);
b80fe6
                 tclass_enter_removed(tclass);
b80fe6
+                if (tclass_get_request(link, tclass, &req) >= 0)
b80fe6
+                        tclass_enter_removed(req->userdata);
b80fe6
 
b80fe6
                 if (tclass->state == 0) {
b80fe6
                         log_tclass_debug(tclass, link, "Forgetting");
b80fe6
@@ -433,6 +437,7 @@ static bool tclass_is_ready_to_configure(TClass *tclass, Link *link) {
b80fe6
 }
b80fe6
 
b80fe6
 static int tclass_process_request(Request *req, Link *link, TClass *tclass) {
b80fe6
+        TClass *existing;
b80fe6
         int r;
b80fe6
 
b80fe6
         assert(req);
b80fe6
@@ -447,54 +452,56 @@ static int tclass_process_request(Request *req, Link *link, TClass *tclass) {
b80fe6
                 return log_link_warning_errno(link, r, "Failed to configure TClass: %m");
b80fe6
 
b80fe6
         tclass_enter_configuring(tclass);
b80fe6
+        if (tclass_get(link, tclass, &existing) >= 0)
b80fe6
+                tclass_enter_configuring(existing);
b80fe6
+
b80fe6
         return 1;
b80fe6
 }
b80fe6
 
b80fe6
-int link_request_tclass(Link *link, TClass *tclass) {
b80fe6
-        TClass *existing;
b80fe6
+int link_request_tclass(Link *link, const TClass *tclass) {
b80fe6
+        _cleanup_(tclass_unrefp) TClass *tmp = NULL;
b80fe6
+        TClass *existing = NULL;
b80fe6
         int r;
b80fe6
 
b80fe6
         assert(link);
b80fe6
         assert(tclass);
b80fe6
+        assert(tclass->source != NETWORK_CONFIG_SOURCE_FOREIGN);
b80fe6
 
b80fe6
         if (tclass_get_request(link, tclass, NULL) >= 0)
b80fe6
                 return 0; /* already requested, skipping. */
b80fe6
 
b80fe6
-        if (tclass_get(link, tclass, &existing) < 0) {
b80fe6
-                _cleanup_(tclass_unrefp) TClass *tmp = NULL;
b80fe6
-
b80fe6
-                r = tclass_dup(tclass, &tmp);
b80fe6
-                if (r < 0)
b80fe6
-                        return log_oom();
b80fe6
-
b80fe6
-                r = tclass_attach(link, tmp);
b80fe6
-                if (r < 0)
b80fe6
-                        return log_link_warning_errno(link, r, "Failed to store TClass: %m");
b80fe6
+        r = tclass_dup(tclass, &tmp);
b80fe6
+        if (r < 0)
b80fe6
+                return r;
b80fe6
 
b80fe6
-                existing = tmp;
b80fe6
-        } else
b80fe6
-                existing->source = tclass->source;
b80fe6
+        if (tclass_get(link, tclass, &existing) >= 0)
b80fe6
+                /* Copy state for logging below. */
b80fe6
+                tmp->state = existing->state;
b80fe6
 
b80fe6
-        log_tclass_debug(existing, link, "Requesting");
b80fe6
+        log_tclass_debug(tmp, link, "Requesting");
b80fe6
         r = link_queue_request_safe(link, REQUEST_TYPE_TC_CLASS,
b80fe6
-                                    existing, NULL,
b80fe6
+                                    tmp,
b80fe6
+                                    tclass_unref,
b80fe6
                                     tclass_hash_func,
b80fe6
                                     tclass_compare_func,
b80fe6
                                     tclass_process_request,
b80fe6
                                     &link->tc_messages,
b80fe6
                                     tclass_handler,
b80fe6
                                     NULL);
b80fe6
-        if (r < 0)
b80fe6
-                return log_link_warning_errno(link, r, "Failed to request TClass: %m");
b80fe6
-        if (r == 0)
b80fe6
-                return 0;
b80fe6
+        if (r <= 0)
b80fe6
+                return r;
b80fe6
 
b80fe6
-        tclass_enter_requesting(existing);
b80fe6
+        tclass_enter_requesting(tmp);
b80fe6
+        if (existing)
b80fe6
+                tclass_enter_requesting(existing);
b80fe6
+
b80fe6
+        TAKE_PTR(tmp);
b80fe6
         return 1;
b80fe6
 }
b80fe6
 
b80fe6
 int manager_rtnl_process_tclass(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
b80fe6
         _cleanup_(tclass_unrefp) TClass *tmp = NULL;
b80fe6
+        Request *req = NULL;
b80fe6
         TClass *tclass = NULL;
b80fe6
         Link *link;
b80fe6
         uint16_t type;
b80fe6
@@ -559,39 +566,42 @@ int manager_rtnl_process_tclass(sd_netlink *rtnl, sd_netlink_message *message, M
b80fe6
         }
b80fe6
 
b80fe6
         (void) tclass_get(link, tmp, &tclass);
b80fe6
+        (void) tclass_get_request(link, tmp, &req;;
b80fe6
 
b80fe6
-        switch (type) {
b80fe6
-        case RTM_NEWTCLASS:
b80fe6
-                if (tclass) {
b80fe6
-                        tclass_enter_configured(tclass);
b80fe6
-                        log_tclass_debug(tclass, link, "Received remembered");
b80fe6
-                } else {
b80fe6
-                        tclass_enter_configured(tmp);
b80fe6
-                        log_tclass_debug(tmp, link, "Received new");
b80fe6
-
b80fe6
-                        r = tclass_attach(link, tmp);
b80fe6
-                        if (r < 0) {
b80fe6
-                                log_link_warning_errno(link, r, "Failed to remember TClass, ignoring: %m");
b80fe6
-                                return 0;
b80fe6
-                        }
b80fe6
-
b80fe6
-                        tclass = tmp;
b80fe6
-                }
b80fe6
-
b80fe6
-                break;
b80fe6
-
b80fe6
-        case RTM_DELTCLASS:
b80fe6
+        if (type == RTM_DELTCLASS) {
b80fe6
                 if (tclass)
b80fe6
                         tclass_drop(tclass);
b80fe6
                 else
b80fe6
                         log_tclass_debug(tmp, link, "Kernel removed unknown");
b80fe6
 
b80fe6
-                break;
b80fe6
+                return 0;
b80fe6
+        }
b80fe6
 
b80fe6
-        default:
b80fe6
-                assert_not_reached();
b80fe6
+        bool is_new = false;
b80fe6
+        if (!tclass) {
b80fe6
+                /* If we did not know the tclass, then save it. */
b80fe6
+                r = tclass_attach(link, tmp);
b80fe6
+                if (r < 0) {
b80fe6
+                        log_link_warning_errno(link, r, "Failed to remember TClass, ignoring: %m");
b80fe6
+                        return 0;
b80fe6
+                }
b80fe6
+
b80fe6
+                tclass = tmp;
b80fe6
+                is_new = true;
b80fe6
         }
b80fe6
 
b80fe6
+        /* Also update information that cannot be obtained through netlink notification. */
b80fe6
+        if (req && req->waiting_reply) {
b80fe6
+                TClass *t = ASSERT_PTR(req->userdata);
b80fe6
+
b80fe6
+                tclass->source = t->source;
b80fe6
+        }
b80fe6
+
b80fe6
+        tclass_enter_configured(tclass);
b80fe6
+        if (req)
b80fe6
+                tclass_enter_configured(req->userdata);
b80fe6
+
b80fe6
+        log_tclass_debug(tclass, link, is_new ? "Remembering" : "Received remembered");
b80fe6
         return 1;
b80fe6
 }
b80fe6
 
b80fe6
diff --git a/src/network/tc/tclass.h b/src/network/tc/tclass.h
b80fe6
index ce51b2499ecc8..31374d6da8dcc 100644
b80fe6
--- a/src/network/tc/tclass.h
b80fe6
+++ b/src/network/tc/tclass.h
b80fe6
@@ -66,7 +66,7 @@ void link_tclass_drop_marked(Link *link);
b80fe6
 
b80fe6
 int link_find_tclass(Link *link, uint32_t classid, TClass **ret);
b80fe6
 
b80fe6
-int link_request_tclass(Link *link, TClass *tclass);
b80fe6
+int link_request_tclass(Link *link, const TClass *tclass);
b80fe6
 
b80fe6
 void network_drop_invalid_tclass(Network *network);
b80fe6
 
b80fe6
142bf4
From 91823bc0cdb41df6e8b66e58ae5cecee2f83b4d4 Mon Sep 17 00:00:00 2001
b80fe6
From: Daan De Meyer <daan.j.demeyer@gmail.com>
b80fe6
Date: Thu, 1 Aug 2024 14:38:05 +0200
b80fe6
Subject: [PATCH 10/10] networkd: Replace existing objects instead of doing
b80fe6
 nothing if they exist
b80fe6
b80fe6
Currently, if for example a traffic control object already exist, networkd
b80fe6
will silently do nothing, even if the settings in the network file for the
b80fe6
traffic control object have changed. Let's instead replace the object if it
b80fe6
already exists so that new settings from the network file are applied as
b80fe6
expected.
b80fe6
b80fe6
Fixes #31226
142bf4
142bf4
(cherry picked from commit 21d9eeb5e6177544b32a5e25c355373573a3ccee)
b80fe6
---
b80fe6
 src/libsystemd/sd-netlink/netlink-message-rtnl.c |  6 +++---
b80fe6
 test/test-network/systemd-networkd-tests.py      | 11 +++++++++++
b80fe6
 2 files changed, 14 insertions(+), 3 deletions(-)
b80fe6
b80fe6
diff --git a/src/libsystemd/sd-netlink/netlink-message-rtnl.c b/src/libsystemd/sd-netlink/netlink-message-rtnl.c
b80fe6
index fb11c7e02bb28..9184c43fecced 100644
b80fe6
--- a/src/libsystemd/sd-netlink/netlink-message-rtnl.c
b80fe6
+++ b/src/libsystemd/sd-netlink/netlink-message-rtnl.c
b80fe6
@@ -892,7 +892,7 @@ int sd_rtnl_message_new_addrlabel(
b80fe6
                 return r;
b80fe6
 
b80fe6
         if (nlmsg_type == RTM_NEWADDRLABEL)
b80fe6
-                (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
b80fe6
+                (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
b80fe6
 
b80fe6
         addrlabel = NLMSG_DATA((*ret)->hdr);
b80fe6
 
b80fe6
@@ -1143,7 +1143,7 @@ int sd_rtnl_message_new_traffic_control(
b80fe6
                 return r;
b80fe6
 
b80fe6
         if (IN_SET(nlmsg_type, RTM_NEWQDISC, RTM_NEWTCLASS))
b80fe6
-                (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
b80fe6
+                (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
b80fe6
 
b80fe6
         tcm = NLMSG_DATA((*ret)->hdr);
b80fe6
         tcm->tcm_ifindex = ifindex;
b80fe6
@@ -1212,7 +1212,7 @@ int sd_rtnl_message_new_mdb(
b80fe6
                 return r;
b80fe6
 
b80fe6
         if (nlmsg_type == RTM_NEWMDB)
b80fe6
-                (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
b80fe6
+                (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
b80fe6
 
b80fe6
         bpm = NLMSG_DATA((*ret)->hdr);
b80fe6
         bpm->family = AF_BRIDGE;
b80fe6
diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py
142bf4
index ba8e65ee90bfc..83a98c2902104 100755
b80fe6
--- a/test/test-network/systemd-networkd-tests.py
b80fe6
+++ b/test/test-network/systemd-networkd-tests.py
142bf4
@@ -4424,6 +4424,17 @@ def test_qdisc_cake(self):
b80fe6
         self.assertIn('rtt 1s', output)
b80fe6
         self.assertIn('ack-filter-aggressive', output)
b80fe6
 
b80fe6
+        # Test for replacing existing qdisc. See #31226.
b80fe6
+        with open(os.path.join(network_unit_dir, '25-qdisc-cake.network'), mode='a', encoding='utf-8') as f:
b80fe6
+            f.write('Bandwidth=250M\n')
b80fe6
+
b80fe6
+        networkctl_reload()
b80fe6
+        self.wait_online('dummy98:routable')
b80fe6
+
b80fe6
+        output = check_output('tc qdisc show dev dummy98')
b80fe6
+        print(output)
b80fe6
+        self.assertIn('bandwidth 250Mbit', output)
b80fe6
+
b80fe6
     @expectedFailureIfModuleIsNotAvailable('sch_codel')
b80fe6
     def test_qdisc_codel(self):
b80fe6
         copy_network_unit('25-qdisc-codel.network', '12-dummy.netdev')