Compare commits
No commits in common. "da4dd97405eac3f692f7bd032983adc8b780c8b6" and "2e22a54f4e085496088b77085f38b66532da59fb" have entirely different histories.
da4dd97405
...
2e22a54f4e
|
@ -2368,40 +2368,6 @@
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term><varname>TokenBufferFilterLatencySec=</varname></term>
|
|
||||||
<listitem>
|
|
||||||
<para>Specifies the latency parameter, which specifies the maximum amount of time a
|
|
||||||
packet can sit in the Token Buffer Filter (TBF). Defaults to unset.</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term><varname>TokenBufferFilterBurst=</varname></term>
|
|
||||||
<listitem>
|
|
||||||
<para>Specifies the size of the bucket. This is the maximum amount of bytes that tokens
|
|
||||||
can be available for instantaneous transfer. When the size is suffixed with K, M, or G, it is
|
|
||||||
parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1000. Defaults to
|
|
||||||
unset.</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term><varname>TokenBufferFilterRate=</varname></term>
|
|
||||||
<listitem>
|
|
||||||
<para>Specifies the device specific bandwidth. When suffixed with K, M, or G, the specified
|
|
||||||
bandwidth is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1000.
|
|
||||||
Defaults to unset.</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term><varname>StochasticFairnessQueueingPerturbPeriodSec=</varname></term>
|
|
||||||
<listitem>
|
|
||||||
<para>Specifies the interval in seconds for queue algorithm perturbation. Defaults to unset.</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
</variablelist>
|
</variablelist>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <linux/loop.h>
|
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/mount.h>
|
#include <sys/mount.h>
|
||||||
|
@ -1221,7 +1220,6 @@ int setup_namespace(
|
||||||
|
|
||||||
r = loop_device_make_by_path(root_image,
|
r = loop_device_make_by_path(root_image,
|
||||||
dissect_image_flags & DISSECT_IMAGE_READ_ONLY ? O_RDONLY : O_RDWR,
|
dissect_image_flags & DISSECT_IMAGE_READ_ONLY ? O_RDONLY : O_RDWR,
|
||||||
LO_FLAGS_PARTSCAN,
|
|
||||||
&loop_device);
|
&loop_device);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_debug_errno(r, "Failed to create loop device for root image: %m");
|
return log_debug_errno(r, "Failed to create loop device for root image: %m");
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <getopt.h>
|
|
||||||
#include <linux/loop.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
#include "architecture.h"
|
#include "architecture.h"
|
||||||
#include "dissect-image.h"
|
#include "dissect-image.h"
|
||||||
|
@ -172,7 +171,7 @@ static int run(int argc, char *argv[]) {
|
||||||
if (r <= 0)
|
if (r <= 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = loop_device_make_by_path(arg_image, (arg_flags & DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : O_RDWR, LO_FLAGS_PARTSCAN, &d);
|
r = loop_device_make_by_path(arg_image, (arg_flags & DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : O_RDWR, &d);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to set up loopback device: %m");
|
return log_error_errno(r, "Failed to set up loopback device: %m");
|
||||||
|
|
||||||
|
|
|
@ -532,6 +532,7 @@ int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t type) {
|
||||||
|
|
||||||
assert_return(m, -EINVAL);
|
assert_return(m, -EINVAL);
|
||||||
assert_return(!m->sealed, -EPERM);
|
assert_return(!m->sealed, -EPERM);
|
||||||
|
assert_return(m->n_containers > 0, -EINVAL);
|
||||||
|
|
||||||
r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0);
|
r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
|
|
@ -109,10 +109,6 @@ sources = files('''
|
||||||
tc/netem.h
|
tc/netem.h
|
||||||
tc/qdisc.c
|
tc/qdisc.c
|
||||||
tc/qdisc.h
|
tc/qdisc.h
|
||||||
tc/sfq.c
|
|
||||||
tc/sfq.h
|
|
||||||
tc/tbf.c
|
|
||||||
tc/tbf.h
|
|
||||||
tc/tc-util.c
|
tc/tc-util.c
|
||||||
tc/tc-util.h
|
tc/tc-util.h
|
||||||
'''.split())
|
'''.split())
|
||||||
|
|
|
@ -2585,7 +2585,7 @@ static int link_drop_config(Link *link) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int link_configure_qdiscs(Link *link) {
|
static int link_configure_qdiscs(Link *link) {
|
||||||
QDisc *qdisc;
|
QDiscs *qdisc;
|
||||||
Iterator i;
|
Iterator i;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
@ -2601,7 +2601,7 @@ static int link_configure_qdiscs(Link *link) {
|
||||||
if (link->qdisc_messages == 0)
|
if (link->qdisc_messages == 0)
|
||||||
link->qdiscs_configured = true;
|
link->qdiscs_configured = true;
|
||||||
else
|
else
|
||||||
log_link_debug(link, "Configuring queuing discipline (qdisc)");
|
log_link_debug(link, "Configuring QDiscs");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -250,10 +250,6 @@ TrafficControlQueueingDiscipline.NetworkEmulatorDelayJitterSec, con
|
||||||
TrafficControlQueueingDiscipline.NetworkEmulatorLossRate, config_parse_tc_network_emulator_rate, 0, 0
|
TrafficControlQueueingDiscipline.NetworkEmulatorLossRate, config_parse_tc_network_emulator_rate, 0, 0
|
||||||
TrafficControlQueueingDiscipline.NetworkEmulatorDuplicateRate, config_parse_tc_network_emulator_rate, 0, 0
|
TrafficControlQueueingDiscipline.NetworkEmulatorDuplicateRate, config_parse_tc_network_emulator_rate, 0, 0
|
||||||
TrafficControlQueueingDiscipline.NetworkEmulatorPacketLimit, config_parse_tc_network_emulator_packet_limit, 0, 0
|
TrafficControlQueueingDiscipline.NetworkEmulatorPacketLimit, config_parse_tc_network_emulator_packet_limit, 0, 0
|
||||||
TrafficControlQueueingDiscipline.TokenBufferFilterRate, config_parse_tc_token_buffer_filter_size, 0, 0
|
|
||||||
TrafficControlQueueingDiscipline.TokenBufferFilterBurst, config_parse_tc_token_buffer_filter_size, 0, 0
|
|
||||||
TrafficControlQueueingDiscipline.TokenBufferFilterLatencySec, config_parse_tc_token_buffer_filter_latency, 0, 0
|
|
||||||
TrafficControlQueueingDiscipline.StochasticFairnessQueueingPerturbPeriodSec, config_parse_tc_stochastic_fairness_queueing_perturb_period, 0, 0
|
|
||||||
/* backwards compatibility: do not add new entries to this section */
|
/* backwards compatibility: do not add new entries to this section */
|
||||||
Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local)
|
Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local)
|
||||||
DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
|
DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
|
||||||
|
|
|
@ -154,8 +154,6 @@ int network_verify(Network *network) {
|
||||||
Prefix *prefix, *prefix_next;
|
Prefix *prefix, *prefix_next;
|
||||||
Route *route, *route_next;
|
Route *route, *route_next;
|
||||||
FdbEntry *fdb, *fdb_next;
|
FdbEntry *fdb, *fdb_next;
|
||||||
QDisc *qdisc;
|
|
||||||
Iterator i;
|
|
||||||
|
|
||||||
assert(network);
|
assert(network);
|
||||||
assert(network->filename);
|
assert(network->filename);
|
||||||
|
@ -315,11 +313,6 @@ int network_verify(Network *network) {
|
||||||
if (routing_policy_rule_section_verify(rule) < 0)
|
if (routing_policy_rule_section_verify(rule) < 0)
|
||||||
routing_policy_rule_free(rule);
|
routing_policy_rule_free(rule);
|
||||||
|
|
||||||
bool has_root = false, has_clsact = false;
|
|
||||||
ORDERED_HASHMAP_FOREACH(qdisc, network->qdiscs_by_section, i)
|
|
||||||
if (qdisc_section_verify(qdisc, &has_root, &has_clsact) < 0)
|
|
||||||
qdisc_free(qdisc);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,12 @@
|
||||||
* Copyright © 2019 VMware, Inc. */
|
* Copyright © 2019 VMware, Inc. */
|
||||||
|
|
||||||
#include <linux/pkt_sched.h>
|
#include <linux/pkt_sched.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
#include "conf-parser.h"
|
#include "conf-parser.h"
|
||||||
|
#include "hashmap.h"
|
||||||
|
#include "in-addr-util.h"
|
||||||
#include "netem.h"
|
#include "netem.h"
|
||||||
#include "netlink-util.h"
|
#include "netlink-util.h"
|
||||||
#include "networkd-manager.h"
|
#include "networkd-manager.h"
|
||||||
|
@ -12,6 +15,7 @@
|
||||||
#include "qdisc.h"
|
#include "qdisc.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
#include "tc-util.h"
|
#include "tc-util.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
int network_emulator_new(NetworkEmulator **ret) {
|
int network_emulator_new(NetworkEmulator **ret) {
|
||||||
NetworkEmulator *ne = NULL;
|
NetworkEmulator *ne = NULL;
|
||||||
|
@ -30,33 +34,33 @@ int network_emulator_new(NetworkEmulator **ret) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int network_emulator_fill_message(Link *link, const NetworkEmulator *ne, sd_netlink_message *req) {
|
int network_emulator_fill_message(Link *link, QDiscs *qdisc, sd_netlink_message *req) {
|
||||||
struct tc_netem_qopt opt = {
|
struct tc_netem_qopt opt = {
|
||||||
.limit = 1000,
|
.limit = 1000,
|
||||||
};
|
};
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(link);
|
assert(link);
|
||||||
assert(ne);
|
assert(qdisc);
|
||||||
assert(req);
|
assert(req);
|
||||||
|
|
||||||
if (ne->limit > 0)
|
if (qdisc->ne.limit > 0)
|
||||||
opt.limit = ne->limit;
|
opt.limit = qdisc->ne.limit;
|
||||||
|
|
||||||
if (ne->loss > 0)
|
if (qdisc->ne.loss > 0)
|
||||||
opt.loss = ne->loss;
|
opt.loss = qdisc->ne.loss;
|
||||||
|
|
||||||
if (ne->duplicate > 0)
|
if (qdisc->ne.duplicate > 0)
|
||||||
opt.duplicate = ne->duplicate;
|
opt.duplicate = qdisc->ne.duplicate;
|
||||||
|
|
||||||
if (ne->delay != USEC_INFINITY) {
|
if (qdisc->ne.delay != USEC_INFINITY) {
|
||||||
r = tc_time_to_tick(ne->delay, &opt.latency);
|
r = tc_time_to_tick(qdisc->ne.delay, &opt.latency);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_link_error_errno(link, r, "Failed to calculate latency in TCA_OPTION: %m");
|
return log_link_error_errno(link, r, "Failed to calculate latency in TCA_OPTION: %m");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ne->jitter != USEC_INFINITY) {
|
if (qdisc->ne.jitter != USEC_INFINITY) {
|
||||||
r = tc_time_to_tick(ne->jitter, &opt.jitter);
|
r = tc_time_to_tick(qdisc->ne.jitter, &opt.jitter);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_link_error_errno(link, r, "Failed to calculate jitter in TCA_OPTION: %m");
|
return log_link_error_errno(link, r, "Failed to calculate jitter in TCA_OPTION: %m");
|
||||||
}
|
}
|
||||||
|
@ -80,7 +84,7 @@ int config_parse_tc_network_emulator_delay(
|
||||||
void *data,
|
void *data,
|
||||||
void *userdata) {
|
void *userdata) {
|
||||||
|
|
||||||
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
|
_cleanup_(qdisc_free_or_set_invalidp) QDiscs *qdisc = NULL;
|
||||||
Network *network = data;
|
Network *network = data;
|
||||||
usec_t u;
|
usec_t u;
|
||||||
int r;
|
int r;
|
||||||
|
@ -135,7 +139,7 @@ int config_parse_tc_network_emulator_rate(
|
||||||
void *data,
|
void *data,
|
||||||
void *userdata) {
|
void *userdata) {
|
||||||
|
|
||||||
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
|
_cleanup_(qdisc_free_or_set_invalidp) QDiscs *qdisc = NULL;
|
||||||
Network *network = data;
|
Network *network = data;
|
||||||
uint32_t rate;
|
uint32_t rate;
|
||||||
int r;
|
int r;
|
||||||
|
@ -185,7 +189,7 @@ int config_parse_tc_network_emulator_packet_limit(
|
||||||
void *data,
|
void *data,
|
||||||
void *userdata) {
|
void *userdata) {
|
||||||
|
|
||||||
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
|
_cleanup_(qdisc_free_or_set_invalidp) QDiscs *qdisc = NULL;
|
||||||
Network *network = data;
|
Network *network = data;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
#include "networkd-link.h"
|
#include "networkd-link.h"
|
||||||
#include "time-util.h"
|
#include "time-util.h"
|
||||||
|
|
||||||
|
typedef struct QDiscs QDiscs;
|
||||||
|
|
||||||
typedef struct NetworkEmulator {
|
typedef struct NetworkEmulator {
|
||||||
usec_t delay;
|
usec_t delay;
|
||||||
usec_t jitter;
|
usec_t jitter;
|
||||||
|
@ -18,7 +20,7 @@ typedef struct NetworkEmulator {
|
||||||
} NetworkEmulator;
|
} NetworkEmulator;
|
||||||
|
|
||||||
int network_emulator_new(NetworkEmulator **ret);
|
int network_emulator_new(NetworkEmulator **ret);
|
||||||
int network_emulator_fill_message(Link *link, const NetworkEmulator *ne, sd_netlink_message *req);
|
int network_emulator_fill_message(Link *link, QDiscs *qdisc, sd_netlink_message *req);
|
||||||
|
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_tc_network_emulator_delay);
|
CONFIG_PARSER_PROTOTYPE(config_parse_tc_network_emulator_delay);
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_tc_network_emulator_rate);
|
CONFIG_PARSER_PROTOTYPE(config_parse_tc_network_emulator_rate);
|
||||||
|
|
|
@ -12,15 +12,16 @@
|
||||||
#include "qdisc.h"
|
#include "qdisc.h"
|
||||||
#include "set.h"
|
#include "set.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
static int qdisc_new(QDisc **ret) {
|
static int qdisc_new(QDiscs **ret) {
|
||||||
QDisc *qdisc;
|
QDiscs *qdisc;
|
||||||
|
|
||||||
qdisc = new(QDisc, 1);
|
qdisc = new(QDiscs, 1);
|
||||||
if (!qdisc)
|
if (!qdisc)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
*qdisc = (QDisc) {
|
*qdisc = (QDiscs) {
|
||||||
.family = AF_UNSPEC,
|
.family = AF_UNSPEC,
|
||||||
.parent = TC_H_ROOT,
|
.parent = TC_H_ROOT,
|
||||||
};
|
};
|
||||||
|
@ -30,9 +31,9 @@ static int qdisc_new(QDisc **ret) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int qdisc_new_static(Network *network, const char *filename, unsigned section_line, QDisc **ret) {
|
int qdisc_new_static(Network *network, const char *filename, unsigned section_line, QDiscs **ret) {
|
||||||
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
|
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
|
||||||
_cleanup_(qdisc_freep) QDisc *qdisc = NULL;
|
_cleanup_(qdisc_freep) QDiscs *qdisc = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(network);
|
assert(network);
|
||||||
|
@ -75,7 +76,7 @@ int qdisc_new_static(Network *network, const char *filename, unsigned section_li
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void qdisc_free(QDisc *qdisc) {
|
void qdisc_free(QDiscs *qdisc) {
|
||||||
if (!qdisc)
|
if (!qdisc)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -105,7 +106,7 @@ static int qdisc_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (link->route_messages == 0) {
|
if (link->route_messages == 0) {
|
||||||
log_link_debug(link, "QDisc configured");
|
log_link_debug(link, "QDiscs configured");
|
||||||
link->qdiscs_configured = true;
|
link->qdiscs_configured = true;
|
||||||
link_check_ready(link);
|
link_check_ready(link);
|
||||||
}
|
}
|
||||||
|
@ -113,7 +114,7 @@ static int qdisc_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int qdisc_configure(Link *link, QDisc *qdisc) {
|
int qdisc_configure(Link *link, QDiscs *qdisc) {
|
||||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
|
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
|
||||||
_cleanup_free_ char *tca_kind = NULL;
|
_cleanup_free_ char *tca_kind = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
@ -146,27 +147,7 @@ int qdisc_configure(Link *link, QDisc *qdisc) {
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
r = network_emulator_fill_message(link, &qdisc->ne, req);
|
r = network_emulator_fill_message(link, qdisc, req);
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qdisc->has_token_buffer_filter) {
|
|
||||||
r = free_and_strdup(&tca_kind, "tbf");
|
|
||||||
if (r < 0)
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
r = token_buffer_filter_fill_message(link, &qdisc->tbf, req);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qdisc->has_stochastic_fairness_queueing) {
|
|
||||||
r = free_and_strdup(&tca_kind, "sfq");
|
|
||||||
if (r < 0)
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
r = stochastic_fairness_queueing_fill_message(link, &qdisc->sfq, req);
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
@ -187,42 +168,6 @@ int qdisc_configure(Link *link, QDisc *qdisc) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact) {
|
|
||||||
unsigned i;
|
|
||||||
|
|
||||||
assert(qdisc);
|
|
||||||
assert(has_root);
|
|
||||||
assert(has_clsact);
|
|
||||||
|
|
||||||
if (section_is_invalid(qdisc->section))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
i = qdisc->has_network_emulator + qdisc->has_token_buffer_filter + qdisc->has_stochastic_fairness_queueing;
|
|
||||||
if (i > 1)
|
|
||||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
||||||
"%s: TrafficControlQueueingDiscipline section has more than one type of discipline. "
|
|
||||||
"Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
|
|
||||||
qdisc->section->filename, qdisc->section->line);
|
|
||||||
|
|
||||||
if (qdisc->parent == TC_H_ROOT) {
|
|
||||||
if (*has_root)
|
|
||||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
||||||
"%s: More than one root TrafficControlQueueingDiscipline sections are defined. "
|
|
||||||
"Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
|
|
||||||
qdisc->section->filename, qdisc->section->line);
|
|
||||||
*has_root = true;
|
|
||||||
} else if (qdisc->parent == TC_H_CLSACT) {
|
|
||||||
if (*has_clsact)
|
|
||||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
||||||
"%s: More than one clsact TrafficControlQueueingDiscipline sections are defined. "
|
|
||||||
"Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
|
|
||||||
qdisc->section->filename, qdisc->section->line);
|
|
||||||
*has_clsact = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int config_parse_tc_qdiscs_parent(
|
int config_parse_tc_qdiscs_parent(
|
||||||
const char *unit,
|
const char *unit,
|
||||||
const char *filename,
|
const char *filename,
|
||||||
|
@ -235,7 +180,7 @@ int config_parse_tc_qdiscs_parent(
|
||||||
void *data,
|
void *data,
|
||||||
void *userdata) {
|
void *userdata) {
|
||||||
|
|
||||||
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
|
_cleanup_(qdisc_free_or_set_invalidp) QDiscs *qdisc = NULL;
|
||||||
Network *network = data;
|
Network *network = data;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,8 @@
|
||||||
#include "networkd-link.h"
|
#include "networkd-link.h"
|
||||||
#include "networkd-network.h"
|
#include "networkd-network.h"
|
||||||
#include "networkd-util.h"
|
#include "networkd-util.h"
|
||||||
#include "sfq.h"
|
|
||||||
#include "tbf.h"
|
|
||||||
|
|
||||||
typedef struct QDisc {
|
typedef struct QDiscs {
|
||||||
NetworkConfigSection *section;
|
NetworkConfigSection *section;
|
||||||
Network *network;
|
Network *network;
|
||||||
|
|
||||||
|
@ -22,21 +20,15 @@ typedef struct QDisc {
|
||||||
uint32_t parent;
|
uint32_t parent;
|
||||||
|
|
||||||
bool has_network_emulator:1;
|
bool has_network_emulator:1;
|
||||||
bool has_token_buffer_filter:1;
|
|
||||||
bool has_stochastic_fairness_queueing:1;
|
|
||||||
|
|
||||||
NetworkEmulator ne;
|
NetworkEmulator ne;
|
||||||
TokenBufferFilter tbf;
|
} QDiscs;
|
||||||
StochasticFairnessQueueing sfq;
|
|
||||||
} QDisc;
|
|
||||||
|
|
||||||
void qdisc_free(QDisc *qdisc);
|
void qdisc_free(QDiscs *qdisc);
|
||||||
int qdisc_new_static(Network *network, const char *filename, unsigned section_line, QDisc **ret);
|
int qdisc_new_static(Network *network, const char *filename, unsigned section_line, QDiscs **ret);
|
||||||
|
|
||||||
int qdisc_configure(Link *link, QDisc *qdisc);
|
int qdisc_configure(Link *link, QDiscs *qdisc);
|
||||||
|
|
||||||
int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact);
|
DEFINE_NETWORK_SECTION_FUNCTIONS(QDiscs, qdisc_free);
|
||||||
|
|
||||||
DEFINE_NETWORK_SECTION_FUNCTIONS(QDisc, qdisc_free);
|
|
||||||
|
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_tc_qdiscs_parent);
|
CONFIG_PARSER_PROTOTYPE(config_parse_tc_qdiscs_parent);
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
/* SPDX-License-Identifier: LGPL-2.1+
|
|
||||||
* Copyright © 2019 VMware, Inc. */
|
|
||||||
|
|
||||||
#include <linux/pkt_sched.h>
|
|
||||||
|
|
||||||
#include "alloc-util.h"
|
|
||||||
#include "conf-parser.h"
|
|
||||||
#include "netlink-util.h"
|
|
||||||
#include "parse-util.h"
|
|
||||||
#include "qdisc.h"
|
|
||||||
#include "sfq.h"
|
|
||||||
#include "string-util.h"
|
|
||||||
|
|
||||||
int stochastic_fairness_queueing_new(StochasticFairnessQueueing **ret) {
|
|
||||||
StochasticFairnessQueueing *sfq = NULL;
|
|
||||||
|
|
||||||
sfq = new0(StochasticFairnessQueueing, 1);
|
|
||||||
if (!sfq)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
*ret = TAKE_PTR(sfq);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int stochastic_fairness_queueing_fill_message(Link *link, const StochasticFairnessQueueing *sfq, sd_netlink_message *req) {
|
|
||||||
struct tc_sfq_qopt_v1 opt = {};
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(link);
|
|
||||||
assert(sfq);
|
|
||||||
assert(req);
|
|
||||||
|
|
||||||
opt.v0.perturb_period = sfq->perturb_period / USEC_PER_SEC;
|
|
||||||
|
|
||||||
r = sd_netlink_message_append_data(req, TCA_OPTIONS, &opt, sizeof(struct tc_sfq_qopt_v1));
|
|
||||||
if (r < 0)
|
|
||||||
return log_link_error_errno(link, r, "Could not append TCA_OPTIONS attribute: %m");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int config_parse_tc_stochastic_fairness_queueing_perturb_period(
|
|
||||||
const char *unit,
|
|
||||||
const char *filename,
|
|
||||||
unsigned line,
|
|
||||||
const char *section,
|
|
||||||
unsigned section_line,
|
|
||||||
const char *lvalue,
|
|
||||||
int ltype,
|
|
||||||
const char *rvalue,
|
|
||||||
void *data,
|
|
||||||
void *userdata) {
|
|
||||||
|
|
||||||
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
|
|
||||||
Network *network = data;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(filename);
|
|
||||||
assert(lvalue);
|
|
||||||
assert(rvalue);
|
|
||||||
assert(data);
|
|
||||||
|
|
||||||
r = qdisc_new_static(network, filename, section_line, &qdisc);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
if (isempty(rvalue)) {
|
|
||||||
qdisc->sfq.perturb_period = 0;
|
|
||||||
|
|
||||||
qdisc = NULL;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = parse_sec(rvalue, &qdisc->sfq.perturb_period);
|
|
||||||
if (r < 0) {
|
|
||||||
log_syntax(unit, LOG_ERR, filename, line, r,
|
|
||||||
"Failed to parse '%s=', ignoring assignment: %s",
|
|
||||||
lvalue, rvalue);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
qdisc->has_stochastic_fairness_queueing = true;
|
|
||||||
qdisc = NULL;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
/* SPDX-License-Identifier: LGPL-2.1+
|
|
||||||
* Copyright © 2019 VMware, Inc. */
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "sd-netlink.h"
|
|
||||||
|
|
||||||
#include "conf-parser.h"
|
|
||||||
#include "networkd-link.h"
|
|
||||||
|
|
||||||
typedef struct StochasticFairnessQueueing {
|
|
||||||
usec_t perturb_period;
|
|
||||||
} StochasticFairnessQueueing;
|
|
||||||
|
|
||||||
int stochastic_fairness_queueing_new(StochasticFairnessQueueing **ret);
|
|
||||||
int stochastic_fairness_queueing_fill_message(Link *link, const StochasticFairnessQueueing *sfq, sd_netlink_message *req);
|
|
||||||
|
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_tc_stochastic_fairness_queueing_perturb_period);
|
|
|
@ -1,167 +0,0 @@
|
||||||
/* SPDX-License-Identifier: LGPL-2.1+
|
|
||||||
* Copyright © 2019 VMware, Inc. */
|
|
||||||
|
|
||||||
#include <linux/pkt_sched.h>
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#include "alloc-util.h"
|
|
||||||
#include "conf-parser.h"
|
|
||||||
#include "netem.h"
|
|
||||||
#include "netlink-util.h"
|
|
||||||
#include "networkd-manager.h"
|
|
||||||
#include "parse-util.h"
|
|
||||||
#include "qdisc.h"
|
|
||||||
#include "string-util.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
int token_buffer_filter_new(TokenBufferFilter **ret) {
|
|
||||||
TokenBufferFilter *ne = NULL;
|
|
||||||
|
|
||||||
ne = new0(TokenBufferFilter, 1);
|
|
||||||
if (!ne)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
*ret = TAKE_PTR(ne);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int token_buffer_filter_fill_message(Link *link, const TokenBufferFilter *tbf, sd_netlink_message *req) {
|
|
||||||
struct tc_tbf_qopt opt = {};
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(link);
|
|
||||||
assert(tbf);
|
|
||||||
assert(req);
|
|
||||||
|
|
||||||
opt.rate.rate = tbf->rate >= (1ULL << 32) ? ~0U : tbf->rate;
|
|
||||||
opt.limit = tbf->rate * (double) tbf->latency / USEC_PER_SEC + tbf->burst;
|
|
||||||
|
|
||||||
r = sd_netlink_message_open_array(req, TCA_OPTIONS);
|
|
||||||
if (r < 0)
|
|
||||||
return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
|
|
||||||
|
|
||||||
r = sd_netlink_message_append_data(req, TCA_TBF_PARMS, &opt, sizeof(struct tc_tbf_qopt));
|
|
||||||
if (r < 0)
|
|
||||||
return log_link_error_errno(link, r, "Could not append TCA_TBF_PARMS attribute: %m");
|
|
||||||
|
|
||||||
r = sd_netlink_message_append_data(req, TCA_TBF_BURST, &tbf->burst, sizeof(tbf->burst));
|
|
||||||
if (r < 0)
|
|
||||||
return log_link_error_errno(link, r, "Could not append TCA_TBF_BURST attribute: %m");
|
|
||||||
|
|
||||||
if (tbf->rate >= (1ULL << 32)) {
|
|
||||||
r = sd_netlink_message_append_data(req, TCA_TBF_RATE64, &tbf->rate, sizeof(tbf->rate));
|
|
||||||
if (r < 0)
|
|
||||||
return log_link_error_errno(link, r, "Could not append TCA_TBF_RATE64 attribute: %m");
|
|
||||||
}
|
|
||||||
|
|
||||||
r = sd_netlink_message_close_container(req);
|
|
||||||
if (r < 0)
|
|
||||||
return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int config_parse_tc_token_buffer_filter_size(
|
|
||||||
const char *unit,
|
|
||||||
const char *filename,
|
|
||||||
unsigned line,
|
|
||||||
const char *section,
|
|
||||||
unsigned section_line,
|
|
||||||
const char *lvalue,
|
|
||||||
int ltype,
|
|
||||||
const char *rvalue,
|
|
||||||
void *data,
|
|
||||||
void *userdata) {
|
|
||||||
|
|
||||||
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
|
|
||||||
Network *network = data;
|
|
||||||
uint64_t k;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(filename);
|
|
||||||
assert(lvalue);
|
|
||||||
assert(rvalue);
|
|
||||||
assert(data);
|
|
||||||
|
|
||||||
r = qdisc_new_static(network, filename, section_line, &qdisc);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
if (isempty(rvalue)) {
|
|
||||||
if (streq(lvalue, "TokenBufferFilterRate"))
|
|
||||||
qdisc->tbf.rate = 0;
|
|
||||||
else if (streq(lvalue, "TokenBufferFilterBurst"))
|
|
||||||
qdisc->tbf.burst = 0;
|
|
||||||
|
|
||||||
qdisc = NULL;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = parse_size(rvalue, 1000, &k);
|
|
||||||
if (r < 0) {
|
|
||||||
log_syntax(unit, LOG_ERR, filename, line, r,
|
|
||||||
"Failed to parse '%s=', ignoring assignment: %s",
|
|
||||||
lvalue, rvalue);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (streq(lvalue, "TokenBufferFilterRate"))
|
|
||||||
qdisc->tbf.rate = k / 8;
|
|
||||||
else if (streq(lvalue, "TokenBufferFilterBurst"))
|
|
||||||
qdisc->tbf.burst = k;
|
|
||||||
|
|
||||||
qdisc->has_token_buffer_filter = true;
|
|
||||||
qdisc = NULL;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int config_parse_tc_token_buffer_filter_latency(
|
|
||||||
const char *unit,
|
|
||||||
const char *filename,
|
|
||||||
unsigned line,
|
|
||||||
const char *section,
|
|
||||||
unsigned section_line,
|
|
||||||
const char *lvalue,
|
|
||||||
int ltype,
|
|
||||||
const char *rvalue,
|
|
||||||
void *data,
|
|
||||||
void *userdata) {
|
|
||||||
|
|
||||||
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
|
|
||||||
Network *network = data;
|
|
||||||
usec_t u;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(filename);
|
|
||||||
assert(lvalue);
|
|
||||||
assert(rvalue);
|
|
||||||
assert(data);
|
|
||||||
|
|
||||||
r = qdisc_new_static(network, filename, section_line, &qdisc);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
if (isempty(rvalue)) {
|
|
||||||
qdisc->tbf.latency = 0;
|
|
||||||
|
|
||||||
qdisc = NULL;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = parse_sec(rvalue, &u);
|
|
||||||
if (r < 0) {
|
|
||||||
log_syntax(unit, LOG_ERR, filename, line, r,
|
|
||||||
"Failed to parse '%s=', ignoring assignment: %s",
|
|
||||||
lvalue, rvalue);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
qdisc->tbf.latency = u;
|
|
||||||
|
|
||||||
qdisc->has_token_buffer_filter = true;
|
|
||||||
qdisc = NULL;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
/* SPDX-License-Identifier: LGPL-2.1+
|
|
||||||
* Copyright © 2019 VMware, Inc. */
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "sd-netlink.h"
|
|
||||||
|
|
||||||
#include "conf-parser.h"
|
|
||||||
#include "networkd-link.h"
|
|
||||||
|
|
||||||
typedef struct TokenBufferFilter {
|
|
||||||
uint64_t rate;
|
|
||||||
|
|
||||||
uint32_t burst;
|
|
||||||
uint32_t latency;
|
|
||||||
} TokenBufferFilter;
|
|
||||||
|
|
||||||
int token_buffer_filter_new(TokenBufferFilter **ret);
|
|
||||||
int token_buffer_filter_fill_message(Link *link, const TokenBufferFilter *tbf, sd_netlink_message *req);
|
|
||||||
|
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_tc_token_buffer_filter_latency);
|
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_tc_token_buffer_filter_size);
|
|
|
@ -5036,7 +5036,7 @@ static int run(int argc, char *argv[]) {
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = loop_device_make_by_path(arg_image, arg_read_only ? O_RDONLY : O_RDWR, LO_FLAGS_PARTSCAN, &loop);
|
r = loop_device_make_by_path(arg_image, arg_read_only ? O_RDONLY : O_RDWR, &loop);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_error_errno(r, "Failed to set up loopback block device: %m");
|
log_error_errno(r, "Failed to set up loopback block device: %m");
|
||||||
goto finish;
|
goto finish;
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||||
|
|
||||||
#include <linux/loop.h>
|
|
||||||
|
|
||||||
#include "bus-common-errors.h"
|
#include "bus-common-errors.h"
|
||||||
#include "bus-error.h"
|
#include "bus-error.h"
|
||||||
#include "conf-files.h"
|
#include "conf-files.h"
|
||||||
|
@ -361,7 +359,7 @@ static int portable_extract_by_path(
|
||||||
|
|
||||||
assert(path);
|
assert(path);
|
||||||
|
|
||||||
r = loop_device_make_by_path(path, O_RDONLY, LO_FLAGS_PARTSCAN, &d);
|
r = loop_device_make_by_path(path, O_RDONLY, &d);
|
||||||
if (r == -EISDIR) {
|
if (r == -EISDIR) {
|
||||||
/* We can't turn this into a loop-back block device, and this returns EISDIR? Then this is a directory
|
/* We can't turn this into a loop-back block device, and this returns EISDIR? Then this is a directory
|
||||||
* tree and not a raw device. It's easy then. */
|
* tree and not a raw device. It's easy then. */
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||||
|
|
||||||
#if HAVE_VALGRIND_MEMCHECK_H
|
|
||||||
#include <valgrind/memcheck.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <linux/dm-ioctl.h>
|
#include <linux/dm-ioctl.h>
|
||||||
#include <linux/loop.h>
|
#include <linux/loop.h>
|
||||||
#include <sys/mount.h>
|
#include <sys/mount.h>
|
||||||
|
@ -219,15 +215,9 @@ static int wait_for_partitions_to_appear(
|
||||||
* an explicit recognizable error about this, so that callers can generate a
|
* an explicit recognizable error about this, so that callers can generate a
|
||||||
* proper message explaining the situation. */
|
* proper message explaining the situation. */
|
||||||
|
|
||||||
if (ioctl(fd, LOOP_GET_STATUS64, &info) >= 0) {
|
if (ioctl(fd, LOOP_GET_STATUS64, &info) >= 0 && (info.lo_flags & LO_FLAGS_PARTSCAN) == 0) {
|
||||||
#if HAVE_VALGRIND_MEMCHECK_H
|
log_debug("Device is a loop device and partition scanning is off!");
|
||||||
/* Valgrind currently doesn't know LOOP_GET_STATUS64. Remove this once it does */
|
return -EPROTONOSUPPORT;
|
||||||
VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if ((info.lo_flags & LO_FLAGS_PARTSCAN) == 0)
|
|
||||||
return log_debug_errno(EPROTONOSUPPORT,
|
|
||||||
"Device is a loop device and partition scanning is off!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (r != -EBUSY)
|
if (r != -EBUSY)
|
||||||
|
|
|
@ -1,40 +1,26 @@
|
||||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||||
|
|
||||||
#if HAVE_VALGRIND_MEMCHECK_H
|
|
||||||
#include <valgrind/memcheck.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <linux/blkpg.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/loop.h>
|
#include <linux/loop.h>
|
||||||
#include <sys/file.h>
|
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
#include "fileio.h"
|
|
||||||
#include "loop-util.h"
|
#include "loop-util.h"
|
||||||
#include "parse-util.h"
|
|
||||||
#include "stat-util.h"
|
#include "stat-util.h"
|
||||||
#include "stdio-util.h"
|
|
||||||
|
|
||||||
int loop_device_make_full(
|
int loop_device_make(int fd, int open_flags, LoopDevice **ret) {
|
||||||
int fd,
|
const struct loop_info64 info = {
|
||||||
int open_flags,
|
.lo_flags = LO_FLAGS_AUTOCLEAR|LO_FLAGS_PARTSCAN|(open_flags == O_RDONLY ? LO_FLAGS_READ_ONLY : 0),
|
||||||
uint64_t offset,
|
};
|
||||||
uint64_t size,
|
|
||||||
uint32_t loop_flags,
|
|
||||||
LoopDevice **ret) {
|
|
||||||
|
|
||||||
_cleanup_close_ int control = -1, loop = -1;
|
_cleanup_close_ int control = -1, loop = -1;
|
||||||
_cleanup_free_ char *loopdev = NULL;
|
_cleanup_free_ char *loopdev = NULL;
|
||||||
unsigned n_attempts = 0;
|
unsigned n_attempts = 0;
|
||||||
struct loop_info64 info;
|
|
||||||
LoopDevice *d = NULL;
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
int nr = -1, r;
|
LoopDevice *d;
|
||||||
|
int nr, r;
|
||||||
|
|
||||||
assert(fd >= 0);
|
assert(fd >= 0);
|
||||||
assert(ret);
|
assert(ret);
|
||||||
|
@ -44,20 +30,6 @@ int loop_device_make_full(
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
if (S_ISBLK(st.st_mode)) {
|
if (S_ISBLK(st.st_mode)) {
|
||||||
if (ioctl(loop, LOOP_GET_STATUS64, &info) >= 0) {
|
|
||||||
/* Oh! This is a loopback device? That's interesting! */
|
|
||||||
|
|
||||||
#if HAVE_VALGRIND_MEMCHECK_H
|
|
||||||
/* Valgrind currently doesn't know LOOP_GET_STATUS64. Remove this once it does */
|
|
||||||
VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info));
|
|
||||||
#endif
|
|
||||||
nr = info.lo_number;
|
|
||||||
|
|
||||||
if (asprintf(&loopdev, "/dev/loop%i", nr) < 0)
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (offset == 0 && IN_SET(size, 0, UINT64_MAX)) {
|
|
||||||
int copy;
|
int copy;
|
||||||
|
|
||||||
/* If this is already a block device, store a copy of the fd as it is */
|
/* If this is already a block device, store a copy of the fd as it is */
|
||||||
|
@ -66,25 +38,23 @@ int loop_device_make_full(
|
||||||
if (copy < 0)
|
if (copy < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
d = new(LoopDevice, 1);
|
d = new0(LoopDevice, 1);
|
||||||
if (!d)
|
if (!d)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
*d = (LoopDevice) {
|
*d = (LoopDevice) {
|
||||||
.fd = copy,
|
.fd = copy,
|
||||||
.nr = nr,
|
.nr = -1,
|
||||||
.node = TAKE_PTR(loopdev),
|
|
||||||
.relinquished = true, /* It's not allocated by us, don't destroy it when this object is freed */
|
.relinquished = true, /* It's not allocated by us, don't destroy it when this object is freed */
|
||||||
};
|
};
|
||||||
|
|
||||||
*ret = d;
|
*ret = d;
|
||||||
return d->fd;
|
return d->fd;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
r = stat_verify_regular(&st);
|
r = stat_verify_regular(&st);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
}
|
|
||||||
|
|
||||||
control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
|
control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
|
||||||
if (control < 0)
|
if (control < 0)
|
||||||
|
@ -116,23 +86,12 @@ int loop_device_make_full(
|
||||||
loop = safe_close(loop);
|
loop = safe_close(loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
info = (struct loop_info64) {
|
if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0)
|
||||||
/* Use the specified flags, but configure the read-only flag from the open flags, and force autoclear */
|
return -errno;
|
||||||
.lo_flags = (loop_flags & ~LO_FLAGS_READ_ONLY) | ((loop_flags & O_ACCMODE) == O_RDONLY ? LO_FLAGS_READ_ONLY : 0) | LO_FLAGS_AUTOCLEAR,
|
|
||||||
.lo_offset = offset,
|
|
||||||
.lo_sizelimit = size == UINT64_MAX ? 0 : size,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0) {
|
|
||||||
r = -errno;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
d = new(LoopDevice, 1);
|
d = new(LoopDevice, 1);
|
||||||
if (!d) {
|
if (!d)
|
||||||
r = -ENOMEM;
|
return -ENOMEM;
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
*d = (LoopDevice) {
|
*d = (LoopDevice) {
|
||||||
.fd = TAKE_FD(loop),
|
.fd = TAKE_FD(loop),
|
||||||
|
@ -142,17 +101,9 @@ int loop_device_make_full(
|
||||||
|
|
||||||
*ret = d;
|
*ret = d;
|
||||||
return d->fd;
|
return d->fd;
|
||||||
|
|
||||||
fail:
|
|
||||||
if (fd >= 0)
|
|
||||||
(void) ioctl(fd, LOOP_CLR_FD);
|
|
||||||
if (d && d->fd >= 0)
|
|
||||||
(void) ioctl(d->fd, LOOP_CLR_FD);
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int loop_device_make_by_path(const char *path, int open_flags, uint32_t loop_flags, LoopDevice **ret) {
|
int loop_device_make_by_path(const char *path, int open_flags, LoopDevice **ret) {
|
||||||
_cleanup_close_ int fd = -1;
|
_cleanup_close_ int fd = -1;
|
||||||
|
|
||||||
assert(path);
|
assert(path);
|
||||||
|
@ -163,7 +114,7 @@ int loop_device_make_by_path(const char *path, int open_flags, uint32_t loop_fla
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
return loop_device_make(fd, open_flags, loop_flags, ret);
|
return loop_device_make(fd, open_flags, ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
LoopDevice* loop_device_unref(LoopDevice *d) {
|
LoopDevice* loop_device_unref(LoopDevice *d) {
|
||||||
|
@ -205,190 +156,3 @@ void loop_device_relinquish(LoopDevice *d) {
|
||||||
|
|
||||||
d->relinquished = true;
|
d->relinquished = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int loop_device_open(const char *loop_path, int open_flags, LoopDevice **ret) {
|
|
||||||
_cleanup_close_ int loop_fd = -1;
|
|
||||||
_cleanup_free_ char *p = NULL;
|
|
||||||
struct loop_info64 info;
|
|
||||||
struct stat st;
|
|
||||||
LoopDevice *d;
|
|
||||||
int nr;
|
|
||||||
|
|
||||||
assert(loop_path);
|
|
||||||
assert(ret);
|
|
||||||
|
|
||||||
loop_fd = open(loop_path, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|open_flags);
|
|
||||||
if (loop_fd < 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
if (fstat(loop_fd, &st) < 0)
|
|
||||||
return -errno;
|
|
||||||
if (!S_ISBLK(st.st_mode))
|
|
||||||
return -ENOTBLK;
|
|
||||||
|
|
||||||
if (ioctl(loop_fd, LOOP_GET_STATUS64, &info) >= 0) {
|
|
||||||
#if HAVE_VALGRIND_MEMCHECK_H
|
|
||||||
/* Valgrind currently doesn't know LOOP_GET_STATUS64. Remove this once it does */
|
|
||||||
VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info));
|
|
||||||
#endif
|
|
||||||
nr = info.lo_number;
|
|
||||||
} else
|
|
||||||
nr = -1;
|
|
||||||
|
|
||||||
p = strdup(loop_path);
|
|
||||||
if (!p)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
d = new(LoopDevice, 1);
|
|
||||||
if (!d)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
*d = (LoopDevice) {
|
|
||||||
.fd = TAKE_FD(loop_fd),
|
|
||||||
.nr = nr,
|
|
||||||
.node = TAKE_PTR(p),
|
|
||||||
.relinquished = true, /* It's not ours, don't try to destroy it when this object is freed */
|
|
||||||
};
|
|
||||||
|
|
||||||
*ret = d;
|
|
||||||
return d->fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int resize_partition(int partition_fd, uint64_t offset, uint64_t size) {
|
|
||||||
char sysfs[STRLEN("/sys/dev/block/:/partition") + 2*DECIMAL_STR_MAX(dev_t) + 1];
|
|
||||||
_cleanup_free_ char *whole = NULL, *buffer = NULL;
|
|
||||||
uint64_t current_offset, current_size, partno;
|
|
||||||
_cleanup_close_ int whole_fd = -1;
|
|
||||||
struct stat st;
|
|
||||||
dev_t devno;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(partition_fd >= 0);
|
|
||||||
|
|
||||||
/* Resizes the partition the loopback device refer to (assuming it refers to one instead of an actual
|
|
||||||
* loopback device), and changes the offset, if needed. This is a fancy wrapper around
|
|
||||||
* BLKPG_RESIZE_PARTITION. */
|
|
||||||
|
|
||||||
if (fstat(partition_fd, &st) < 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
assert(S_ISBLK(st.st_mode));
|
|
||||||
|
|
||||||
xsprintf(sysfs, "/sys/dev/block/%u:%u/partition", major(st.st_rdev), minor(st.st_rdev));
|
|
||||||
r = read_one_line_file(sysfs, &buffer);
|
|
||||||
if (r == -ENOENT) /* not a partition, cannot resize */
|
|
||||||
return -ENOTTY;
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
r = safe_atou64(buffer, &partno);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
xsprintf(sysfs, "/sys/dev/block/%u:%u/start", major(st.st_rdev), minor(st.st_rdev));
|
|
||||||
|
|
||||||
buffer = mfree(buffer);
|
|
||||||
r = read_one_line_file(sysfs, &buffer);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
r = safe_atou64(buffer, ¤t_offset);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
if (current_offset > UINT64_MAX/512U)
|
|
||||||
return -EINVAL;
|
|
||||||
current_offset *= 512U;
|
|
||||||
|
|
||||||
if (ioctl(partition_fd, BLKGETSIZE64, ¤t_size) < 0)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (size == UINT64_MAX && offset == UINT64_MAX)
|
|
||||||
return 0;
|
|
||||||
if (current_size == size && current_offset == offset)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
xsprintf(sysfs, "/sys/dev/block/%u:%u/../dev", major(st.st_rdev), minor(st.st_rdev));
|
|
||||||
|
|
||||||
buffer = mfree(buffer);
|
|
||||||
r = read_one_line_file(sysfs, &buffer);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
r = parse_dev(buffer, &devno);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
r = device_path_make_major_minor(S_IFBLK, devno, &whole);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
whole_fd = open(whole, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
|
|
||||||
if (whole_fd < 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
struct blkpg_partition bp = {
|
|
||||||
.pno = partno,
|
|
||||||
.start = offset == UINT64_MAX ? current_offset : offset,
|
|
||||||
.length = size == UINT64_MAX ? current_size : size,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct blkpg_ioctl_arg ba = {
|
|
||||||
.op = BLKPG_RESIZE_PARTITION,
|
|
||||||
.data = &bp,
|
|
||||||
.datalen = sizeof(bp),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (ioctl(whole_fd, BLKPG, &ba) < 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int loop_device_refresh_size(LoopDevice *d, uint64_t offset, uint64_t size) {
|
|
||||||
struct loop_info64 info;
|
|
||||||
assert(d);
|
|
||||||
|
|
||||||
/* Changes the offset/start of the loop device relative to the beginning of the underlying file or
|
|
||||||
* block device. If this loop device actually refers to a partition and not a loopback device, we'll
|
|
||||||
* try to adjust the partition offsets instead.
|
|
||||||
*
|
|
||||||
* If either offset or size is UINT64_MAX we won't change that parameter. */
|
|
||||||
|
|
||||||
if (d->fd < 0)
|
|
||||||
return -EBADF;
|
|
||||||
|
|
||||||
if (d->nr < 0) /* not a loopback device */
|
|
||||||
return resize_partition(d->fd, offset, size);
|
|
||||||
|
|
||||||
if (ioctl(d->fd, LOOP_GET_STATUS64, &info) < 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
#if HAVE_VALGRIND_MEMCHECK_H
|
|
||||||
/* Valgrind currently doesn't know LOOP_GET_STATUS64. Remove this once it does */
|
|
||||||
VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (size == UINT64_MAX && offset == UINT64_MAX)
|
|
||||||
return 0;
|
|
||||||
if (info.lo_sizelimit == size && info.lo_offset == offset)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (size != UINT64_MAX)
|
|
||||||
info.lo_sizelimit = size;
|
|
||||||
if (offset != UINT64_MAX)
|
|
||||||
info.lo_offset = offset;
|
|
||||||
|
|
||||||
if (ioctl(d->fd, LOOP_SET_STATUS64, &info) < 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int loop_device_flock(LoopDevice *d, int operation) {
|
|
||||||
assert(d);
|
|
||||||
|
|
||||||
if (d->fd < 0)
|
|
||||||
return -EBADF;
|
|
||||||
|
|
||||||
if (flock(d->fd, operation) < 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
|
@ -14,19 +14,10 @@ struct LoopDevice {
|
||||||
bool relinquished;
|
bool relinquished;
|
||||||
};
|
};
|
||||||
|
|
||||||
int loop_device_make_full(int fd, int open_flags, uint64_t offset, uint64_t size, uint32_t loop_flags, LoopDevice **ret);
|
int loop_device_make(int fd, int open_flags, LoopDevice **ret);
|
||||||
static inline int loop_device_make(int fd, int open_flags, uint32_t loop_flags, LoopDevice **ret) {
|
int loop_device_make_by_path(const char *path, int open_flags, LoopDevice **ret);
|
||||||
return loop_device_make_full(fd, open_flags, 0, 0, loop_flags, ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
int loop_device_make_by_path(const char *path, int open_flags, uint32_t loop_flags, LoopDevice **ret);
|
|
||||||
int loop_device_open(const char *loop_path, int open_flags, LoopDevice **ret);
|
|
||||||
|
|
||||||
LoopDevice* loop_device_unref(LoopDevice *d);
|
LoopDevice* loop_device_unref(LoopDevice *d);
|
||||||
DEFINE_TRIVIAL_CLEANUP_FUNC(LoopDevice*, loop_device_unref);
|
DEFINE_TRIVIAL_CLEANUP_FUNC(LoopDevice*, loop_device_unref);
|
||||||
|
|
||||||
void loop_device_relinquish(LoopDevice *d);
|
void loop_device_relinquish(LoopDevice *d);
|
||||||
|
|
||||||
int loop_device_refresh_size(LoopDevice *d, uint64_t offset, uint64_t size);
|
|
||||||
|
|
||||||
int loop_device_flock(LoopDevice *d, int operation);
|
|
||||||
|
|
|
@ -2,14 +2,13 @@
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/loop.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/file.h>
|
#include <sys/file.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
#include "btrfs-util.h"
|
#include "btrfs-util.h"
|
||||||
|
@ -1167,7 +1166,7 @@ int image_read_metadata(Image *i) {
|
||||||
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
|
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
|
||||||
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
|
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
|
||||||
|
|
||||||
r = loop_device_make_by_path(i->path, O_RDONLY, LO_FLAGS_PARTSCAN, &d);
|
r = loop_device_make_by_path(i->path, O_RDONLY, &d);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <linux/loop.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "dissect-image.h"
|
#include "dissect-image.h"
|
||||||
|
@ -22,7 +21,7 @@ int main(int argc, char *argv[]) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = loop_device_make_by_path(argv[1], O_RDONLY, LO_FLAGS_PARTSCAN, &d);
|
r = loop_device_make_by_path(argv[1], O_RDONLY, &d);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_error_errno(r, "Failed to set up loopback device: %m");
|
log_error_errno(r, "Failed to set up loopback device: %m");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
|
|
|
@ -270,7 +270,3 @@ NetworkEmulatorDelayJitterSec=
|
||||||
NetworkEmulatorLossRate=
|
NetworkEmulatorLossRate=
|
||||||
NetworkEmulatorDuplicateRate=
|
NetworkEmulatorDuplicateRate=
|
||||||
NetworkEmulatorPacketLimit=
|
NetworkEmulatorPacketLimit=
|
||||||
TokenBufferFilterRate=
|
|
||||||
TokenBufferFilterBurst=
|
|
||||||
TokenBufferFilterLatencySec=
|
|
||||||
StochasticFairnessQueueingPerturbPeriodSec=
|
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
[Match]
|
|
||||||
Name=test1
|
|
||||||
|
|
||||||
[Network]
|
|
||||||
IPv6AcceptRA=no
|
|
||||||
Address=10.1.2.4/16
|
|
||||||
|
|
||||||
[TrafficControlQueueingDiscipline]
|
|
||||||
Parent=root
|
|
||||||
TokenBufferFilterRate=0.5M
|
|
||||||
TokenBufferFilterBurst=5K
|
|
||||||
TokenBufferFilterLatencySec=70msec
|
|
||||||
|
|
||||||
[TrafficControlQueueingDiscipline]
|
|
||||||
Parent=clsact
|
|
||||||
StochasticFairnessQueueingPerturbPeriodSec=5sec
|
|
|
@ -1498,8 +1498,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
|
||||||
'25-neighbor-ip-dummy.network',
|
'25-neighbor-ip-dummy.network',
|
||||||
'25-neighbor-ip.network',
|
'25-neighbor-ip.network',
|
||||||
'25-nexthop.network',
|
'25-nexthop.network',
|
||||||
'25-qdisc-netem.network',
|
'25-qdisc.network',
|
||||||
'25-qdisc-tbf-and-sfq.network',
|
|
||||||
'25-route-ipv6-src.network',
|
'25-route-ipv6-src.network',
|
||||||
'25-route-static.network',
|
'25-route-static.network',
|
||||||
'25-gateway-static.network',
|
'25-gateway-static.network',
|
||||||
|
@ -2058,23 +2057,15 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
|
||||||
self.assertRegex(output, '192.168.5.1')
|
self.assertRegex(output, '192.168.5.1')
|
||||||
|
|
||||||
def test_qdisc(self):
|
def test_qdisc(self):
|
||||||
copy_unit_to_networkd_unit_path('25-qdisc-netem.network', '12-dummy.netdev',
|
copy_unit_to_networkd_unit_path('25-qdisc.network', '12-dummy.netdev')
|
||||||
'25-qdisc-tbf-and-sfq.network', '11-dummy.netdev')
|
|
||||||
start_networkd()
|
start_networkd()
|
||||||
|
|
||||||
self.wait_online(['dummy98:routable', 'test1:routable'])
|
self.wait_online(['dummy98:routable'])
|
||||||
|
|
||||||
output = check_output('tc qdisc show dev dummy98')
|
output = check_output('tc qdisc show dev dummy98')
|
||||||
print(output)
|
print(output)
|
||||||
self.assertRegex(output, 'qdisc netem')
|
|
||||||
self.assertRegex(output, 'limit 100 delay 50.0ms 10.0ms loss 20%')
|
self.assertRegex(output, 'limit 100 delay 50.0ms 10.0ms loss 20%')
|
||||||
self.assertRegex(output, 'limit 200 delay 100.0ms 13.0ms loss 20.5%')
|
self.assertRegex(output, 'limit 200 delay 100.0ms 13.0ms loss 20.5%')
|
||||||
output = check_output('tc qdisc show dev test1')
|
|
||||||
print(output)
|
|
||||||
self.assertRegex(output, 'qdisc tbf')
|
|
||||||
self.assertRegex(output, 'rate 500Kbit burst 5000b lat 70.0ms')
|
|
||||||
self.assertRegex(output, 'qdisc sfq')
|
|
||||||
self.assertRegex(output, 'perturb 5sec')
|
|
||||||
|
|
||||||
class NetworkdStateFileTests(unittest.TestCase, Utilities):
|
class NetworkdStateFileTests(unittest.TestCase, Utilities):
|
||||||
links = [
|
links = [
|
||||||
|
|
Loading…
Reference in New Issue