diff options
| -rw-r--r-- | hwdb/60-keyboard.hwdb | 1 | ||||
| -rw-r--r-- | man/systemd.link.xml | 40 | ||||
| -rw-r--r-- | man/systemd.mount.xml | 11 | ||||
| -rw-r--r-- | man/systemd.resource-control.xml | 18 | ||||
| -rw-r--r-- | src/boot/bootctl.c | 5 | ||||
| -rw-r--r-- | src/core/cgroup.c | 12 | ||||
| -rw-r--r-- | src/core/cgroup.h | 1 | ||||
| -rw-r--r-- | src/core/dbus-cgroup.c | 5 | ||||
| -rw-r--r-- | src/core/dbus-mount.c | 1 | ||||
| -rw-r--r-- | src/core/load-fragment-gperf.gperf.m4 | 2 | ||||
| -rw-r--r-- | src/core/load-fragment.c | 27 | ||||
| -rw-r--r-- | src/core/mount.c | 10 | ||||
| -rw-r--r-- | src/core/mount.h | 1 | ||||
| -rw-r--r-- | src/network/networkd-link.c | 8 | ||||
| -rw-r--r-- | src/systemctl/systemctl.c | 11 | ||||
| -rw-r--r-- | src/udev/net/ethtool-util.c | 113 | ||||
| -rw-r--r-- | src/udev/net/ethtool-util.h | 11 | ||||
| -rw-r--r-- | src/udev/net/link-config-gperf.gperf | 43 | ||||
| -rw-r--r-- | src/udev/net/link-config.c | 6 | ||||
| -rw-r--r-- | src/udev/net/link-config.h | 1 | 
20 files changed, 289 insertions, 38 deletions
| diff --git a/hwdb/60-keyboard.hwdb b/hwdb/60-keyboard.hwdb index 25caa60626..f7d5ac58d4 100644 --- a/hwdb/60-keyboard.hwdb +++ b/hwdb/60-keyboard.hwdb @@ -896,7 +896,6 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnOLPC:pnXO:*   KEYBOARD_KEY_c2=f8   KEYBOARD_KEY_c3=f9   KEYBOARD_KEY_c4=f10 - # KEYBOARD_KEY_c7=f11 # FIXME!   KEYBOARD_KEY_d8=f12   KEYBOARD_KEY_f7=f13   KEYBOARD_KEY_f6=f14 diff --git a/man/systemd.link.xml b/man/systemd.link.xml index d5b4d1038d..10fddeced0 100644 --- a/man/systemd.link.xml +++ b/man/systemd.link.xml @@ -387,6 +387,46 @@            </variablelist>          </listitem>        </varlistentry> +      <varlistentry> +        <term><varname>TCPSegmentationOffload=</varname></term> +        <listitem> +          <para>The TCP Segmentation Offload (TSO) when true enables +          TCP segmentation offload. Takes a boolean value. +          Defaults to "unset".</para> +        </listitem> +      </varlistentry> +      <varlistentry> +        <term><varname>GenericSegmentationOffload=</varname></term> +        <listitem> +          <para>The Generic Segmentation Offload (GSO) when true enables +          generic segmentation offload. Takes a boolean value. +          Defaults to "unset".</para> +        </listitem> +      </varlistentry> +      <varlistentry> +        <term><varname>UDPSegmentationOffload=</varname></term> +        <listitem> +          <para>The UDP Segmentation Offload (USO) when true enables +          UDP segmentation offload. Takes a boolean value. +          Defaults to "unset".</para> +        </listitem> +      </varlistentry> +    <varlistentry> +        <term><varname>GenericReceiveOffload=</varname></term> +        <listitem> +          <para>The Generic Receive Offload (GRO) when true enables +          generic receive offload. Takes a boolean value. +          Defaults to "unset".</para> +        </listitem> +      </varlistentry> +      <varlistentry> +        <term><varname>LargeReceiveOffload=</varname></term> +        <listitem> +          <para>The Large Receive Offload (LRO) when true enables +          large receive offload. Takes a boolean value. +          Defaults to "unset".</para> +        </listitem> +      </varlistentry>      </variablelist>    </refsect1> diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml index bfdb3ce315..b0f156f6df 100644 --- a/man/systemd.mount.xml +++ b/man/systemd.mount.xml @@ -365,6 +365,17 @@        </varlistentry>        <varlistentry> +        <term><varname>ForceUnmount=</varname></term> + +        <listitem><para>Takes a boolean argument. If true, force an +        unmount (in case of an unreachable NFS system). +        This corresponds with +        <citerefentry project='man-pages'><refentrytitle>umount</refentrytitle><manvolnum>8</manvolnum></citerefentry>'s +        <parameter>-f</parameter> switch. Defaults to +        off.</para></listitem> +      </varlistentry> + +      <varlistentry>          <term><varname>DirectoryMode=</varname></term>          <listitem><para>Directories of mount points (and any parent          directories) are automatically created if needed. This option diff --git a/man/systemd.resource-control.xml b/man/systemd.resource-control.xml index 84dbfa2ff3..c11f420fe5 100644 --- a/man/systemd.resource-control.xml +++ b/man/systemd.resource-control.xml @@ -326,6 +326,24 @@        </varlistentry>        <varlistentry> +        <term><varname>MemorySwapMax=<replaceable>bytes</replaceable></varname></term> + +        <listitem> +          <para>Specify the absolute limit on swap usage of the executed processes in this unit.</para> + +          <para>Takes a swap size in bytes. If the value is suffixed with K, M, G or T, the specified swap size is +          parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. If assigned the +          special value <literal>infinity</literal>, no swap limit is applied. This controls the +          <literal>memory.swap.max</literal> control group attribute. For details about this control group attribute, +          see <ulink url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para> + +          <para>Implies <literal>MemoryAccounting=true</literal>.</para> + +          <para>This setting is supported only if the unified control group hierarchy is used.</para> +        </listitem> +      </varlistentry> + +      <varlistentry>          <term><varname>MemoryLimit=<replaceable>bytes</replaceable></varname></term>          <listitem> diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index a7cdf92ed2..ee6d7eb864 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -439,9 +439,12 @@ static int status_variables(void) {                  for (j = 0; j < n_order; j++)                          if (options[i] == order[j]) -                                continue; +                                goto next_option;                  print_efi_option(options[i], false); + +        next_option: +                continue;          }          return 0; diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 20f9f9fc7e..7873f88785 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -66,6 +66,7 @@ void cgroup_context_init(CGroupContext *c) {          c->memory_high = CGROUP_LIMIT_MAX;          c->memory_max = CGROUP_LIMIT_MAX; +        c->memory_swap_max = CGROUP_LIMIT_MAX;          c->memory_limit = CGROUP_LIMIT_MAX; @@ -173,6 +174,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {                  "%sMemoryLow=%" PRIu64 "\n"                  "%sMemoryHigh=%" PRIu64 "\n"                  "%sMemoryMax=%" PRIu64 "\n" +                "%sMemorySwapMax=%" PRIu64 "\n"                  "%sMemoryLimit=%" PRIu64 "\n"                  "%sTasksMax=%" PRIu64 "\n"                  "%sDevicePolicy=%s\n" @@ -194,6 +196,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {                  prefix, c->memory_low,                  prefix, c->memory_high,                  prefix, c->memory_max, +                prefix, c->memory_swap_max,                  prefix, c->memory_limit,                  prefix, c->tasks_max,                  prefix, cgroup_device_policy_to_string(c->device_policy), @@ -617,7 +620,7 @@ static unsigned cgroup_apply_blkio_device_limit(Unit *u, const char *dev_path, u  }  static bool cgroup_context_has_unified_memory_config(CGroupContext *c) { -        return c->memory_low > 0 || c->memory_high != CGROUP_LIMIT_MAX || c->memory_max != CGROUP_LIMIT_MAX; +        return c->memory_low > 0 || c->memory_high != CGROUP_LIMIT_MAX || c->memory_max != CGROUP_LIMIT_MAX || c->memory_swap_max != CGROUP_LIMIT_MAX;  }  static void cgroup_apply_unified_memory_limit(Unit *u, const char *file, uint64_t v) { @@ -848,10 +851,12 @@ static void cgroup_context_apply(Unit *u, CGroupMask mask, ManagerState state) {          if ((mask & CGROUP_MASK_MEMORY) && !is_root) {                  if (cg_all_unified() > 0) {                          uint64_t max = c->memory_max; +                        uint64_t swap_max = c->memory_swap_max; -                        if (cgroup_context_has_unified_memory_config(c)) +                        if (cgroup_context_has_unified_memory_config(c)) {                                  max = c->memory_max; -                        else { +                                swap_max = c->memory_swap_max; +                        } else {                                  max = c->memory_limit;                                  if (max != CGROUP_LIMIT_MAX) @@ -861,6 +866,7 @@ static void cgroup_context_apply(Unit *u, CGroupMask mask, ManagerState state) {                          cgroup_apply_unified_memory_limit(u, "memory.low", c->memory_low);                          cgroup_apply_unified_memory_limit(u, "memory.high", c->memory_high);                          cgroup_apply_unified_memory_limit(u, "memory.max", max); +                        cgroup_apply_unified_memory_limit(u, "memory.swap.max", swap_max);                  } else {                          char buf[DECIMAL_STR_MAX(uint64_t) + 1];                          uint64_t val = c->memory_limit; diff --git a/src/core/cgroup.h b/src/core/cgroup.h index 2fe9cc4039..4cd168f63e 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -101,6 +101,7 @@ struct CGroupContext {          uint64_t memory_low;          uint64_t memory_high;          uint64_t memory_max; +        uint64_t memory_swap_max;          /* For legacy hierarchies */          uint64_t cpu_shares; diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c index 2ca80d2996..c4067a95bf 100644 --- a/src/core/dbus-cgroup.c +++ b/src/core/dbus-cgroup.c @@ -233,6 +233,7 @@ const sd_bus_vtable bus_cgroup_vtable[] = {          SD_BUS_PROPERTY("MemoryLow", "t", NULL, offsetof(CGroupContext, memory_low), 0),          SD_BUS_PROPERTY("MemoryHigh", "t", NULL, offsetof(CGroupContext, memory_high), 0),          SD_BUS_PROPERTY("MemoryMax", "t", NULL, offsetof(CGroupContext, memory_max), 0), +        SD_BUS_PROPERTY("MemorySwapMax", "t", NULL, offsetof(CGroupContext, memory_swap_max), 0),          SD_BUS_PROPERTY("MemoryLimit", "t", NULL, offsetof(CGroupContext, memory_limit), 0),          SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy, offsetof(CGroupContext, device_policy), 0),          SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow, 0, 0), @@ -875,7 +876,7 @@ int bus_cgroup_set_property(                  return 1; -        } else if (STR_IN_SET(name, "MemoryLow", "MemoryHigh", "MemoryMax")) { +        } else if (STR_IN_SET(name, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax")) {                  uint64_t v;                  r = sd_bus_message_read(message, "t", &v); @@ -889,6 +890,8 @@ int bus_cgroup_set_property(                                  c->memory_low = v;                          else if (streq(name, "MemoryHigh"))                                  c->memory_high = v; +                        else if (streq(name, "MemorySwapMax")) +                                c->memory_swap_max = v;                          else                                  c->memory_max = v; diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c index 4421f26bc1..76a7a7ce97 100644 --- a/src/core/dbus-mount.c +++ b/src/core/dbus-mount.c @@ -117,6 +117,7 @@ const sd_bus_vtable bus_mount_vtable[] = {          SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Mount, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST),          SD_BUS_PROPERTY("SloppyOptions", "b", bus_property_get_bool, offsetof(Mount, sloppy_options), SD_BUS_VTABLE_PROPERTY_CONST),          SD_BUS_PROPERTY("LazyUnmount", "b", bus_property_get_bool, offsetof(Mount, lazy_unmount), SD_BUS_VTABLE_PROPERTY_CONST), +        SD_BUS_PROPERTY("ForceUnmount", "b", bus_property_get_bool, offsetof(Mount, force_unmount), SD_BUS_VTABLE_PROPERTY_CONST),          SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Mount, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),          SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),          SD_BUS_PROPERTY("GID", "u", NULL, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 420d32dbd7..2e6c965aec 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -131,6 +131,7 @@ $1.MemoryAccounting,             config_parse_bool,                  0,  $1.MemoryLow,                    config_parse_memory_limit,          0,                             offsetof($1, cgroup_context)  $1.MemoryHigh,                   config_parse_memory_limit,          0,                             offsetof($1, cgroup_context)  $1.MemoryMax,                    config_parse_memory_limit,          0,                             offsetof($1, cgroup_context) +$1.MemorySwapMax,                config_parse_memory_limit,          0,                             offsetof($1, cgroup_context)  $1.MemoryLimit,                  config_parse_memory_limit,          0,                             offsetof($1, cgroup_context)  $1.DeviceAllow,                  config_parse_device_allow,          0,                             offsetof($1, cgroup_context)  $1.DevicePolicy,                 config_parse_device_policy,         0,                             offsetof($1, cgroup_context.device_policy) @@ -356,6 +357,7 @@ Mount.TimeoutSec,                config_parse_sec,                   0,  Mount.DirectoryMode,             config_parse_mode,                  0,                             offsetof(Mount, directory_mode)  Mount.SloppyOptions,             config_parse_bool,                  0,                             offsetof(Mount, sloppy_options)  Mount.LazyUnmount,               config_parse_bool,                  0,                             offsetof(Mount, lazy_unmount) +Mount.ForceUnmount,              config_parse_bool,                  0,                             offsetof(Mount, force_unmount)  EXEC_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl  CGROUP_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl  KILL_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index d5185cf6a0..8f067b5586 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -1338,10 +1338,13 @@ int config_parse_timer(const char *unit,                         void *userdata) {          Timer *t = data; -        usec_t u = 0; +        usec_t usec = 0;          TimerValue *v;          TimerBase b;          CalendarSpec *c = NULL; +        Unit *u = userdata; +        _cleanup_free_ char *k = NULL; +        int r;          assert(filename);          assert(lvalue); @@ -1360,14 +1363,20 @@ int config_parse_timer(const char *unit,                  return 0;          } +        r = unit_full_printf(u, rvalue, &k); +        if (r < 0) { +                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue); +                return 0; +        } +          if (b == TIMER_CALENDAR) { -                if (calendar_spec_from_string(rvalue, &c) < 0) { -                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse calendar specification, ignoring: %s", rvalue); +                if (calendar_spec_from_string(k, &c) < 0) { +                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse calendar specification, ignoring: %s", k);                          return 0;                  }          } else { -                if (parse_sec(rvalue, &u) < 0) { -                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer value, ignoring: %s", rvalue); +                if (parse_sec(k, &usec) < 0) { +                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer value, ignoring: %s", k);                          return 0;                  }          } @@ -1379,7 +1388,7 @@ int config_parse_timer(const char *unit,          }          v->base = b; -        v->value = u; +        v->value = usec;          v->calendar_spec = c;          LIST_PREPEND(value, t->values, v); @@ -2981,8 +2990,12 @@ int config_parse_memory_limit(                  c->memory_high = bytes;          else if (streq(lvalue, "MemoryMax"))                  c->memory_max = bytes; -        else +        else if (streq(lvalue, "MemorySwapMax")) +                c->memory_swap_max = bytes; +        else if (streq(lvalue, "MemoryLimit"))                  c->memory_limit = bytes; +        else +                return -EINVAL;          return 0;  } diff --git a/src/core/mount.c b/src/core/mount.c index 2c2a54edbb..04025b83b9 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -678,7 +678,9 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) {                  "%sFrom /proc/self/mountinfo: %s\n"                  "%sFrom fragment: %s\n"                  "%sDirectoryMode: %04o\n" -                "%sLazyUnmount: %s\n", +                "%sSloppyOptions: %s\n" +                "%sLazyUnmount: %s\n" +                "%sForceUnmount: %s\n",                  prefix, mount_state_to_string(m->state),                  prefix, mount_result_to_string(m->result),                  prefix, m->where, @@ -688,7 +690,9 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) {                  prefix, yes_no(m->from_proc_self_mountinfo),                  prefix, yes_no(m->from_fragment),                  prefix, m->directory_mode, -                prefix, yes_no(m->lazy_unmount)); +                prefix, yes_no(m->sloppy_options), +                prefix, yes_no(m->lazy_unmount), +                prefix, yes_no(m->force_unmount));          if (m->control_pid > 0)                  fprintf(f, @@ -850,6 +854,8 @@ static void mount_enter_unmounting(Mount *m) {          r = exec_command_set(m->control_command, UMOUNT_PATH, m->where, NULL);          if (r >= 0 && m->lazy_unmount)                  r = exec_command_append(m->control_command, "-l", NULL); +        if (r >= 0 && m->force_unmount) +                r = exec_command_append(m->control_command, "-f", NULL);          if (r < 0)                  goto fail; diff --git a/src/core/mount.h b/src/core/mount.h index c4a2bff100..9f7326ba6a 100644 --- a/src/core/mount.h +++ b/src/core/mount.h @@ -72,6 +72,7 @@ struct Mount {          bool sloppy_options;          bool lazy_unmount; +        bool force_unmount;          MountResult result;          MountResult reload_result; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 71484e3288..aab40a0eb1 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -2957,9 +2957,11 @@ static int link_carrier_lost(Link *link) {          if (r < 0)                  return r; -        r = link_drop_foreign_config(link); -        if (r < 0) -                return r; +        if (link->state != LINK_STATE_UNMANAGED) { +                r = link_drop_foreign_config(link); +                if (r < 0) +                        return r; +        }          r = link_handle_bound_by_list(link);          if (r < 0) diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 4b87bdceb2..682805045d 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -3571,6 +3571,7 @@ typedef struct UnitStatusInfo {          uint64_t memory_low;          uint64_t memory_high;          uint64_t memory_max; +        uint64_t memory_swap_max;          uint64_t memory_limit;          uint64_t cpu_usage_nsec;          uint64_t tasks_current; @@ -3883,7 +3884,8 @@ static void print_status_info(                  printf("   Memory: %s", format_bytes(buf, sizeof(buf), i->memory_current)); -                if (i->memory_low > 0 || i->memory_high != CGROUP_LIMIT_MAX || i->memory_max != CGROUP_LIMIT_MAX || +                if (i->memory_low > 0 || i->memory_high != CGROUP_LIMIT_MAX || +                    i->memory_max != CGROUP_LIMIT_MAX || i->memory_swap_max != CGROUP_LIMIT_MAX ||                      i->memory_limit != CGROUP_LIMIT_MAX) {                          const char *prefix = ""; @@ -3900,6 +3902,10 @@ static void print_status_info(                                  printf("%smax: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_max));                                  prefix = " ";                          } +                        if (i->memory_swap_max != CGROUP_LIMIT_MAX) { +                                printf("%sswap max: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_swap_max)); +                                prefix = " "; +                        }                          if (i->memory_limit != CGROUP_LIMIT_MAX) {                                  printf("%slimit: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_limit));                                  prefix = " "; @@ -4140,6 +4146,8 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *                          i->memory_high = u;                  else if (streq(name, "MemoryMax"))                          i->memory_max = u; +                else if (streq(name, "MemorySwapMax")) +                        i->memory_swap_max = u;                  else if (streq(name, "MemoryLimit"))                          i->memory_limit = u;                  else if (streq(name, "TasksCurrent")) @@ -4655,6 +4663,7 @@ static int show_one(                  .memory_current = (uint64_t) -1,                  .memory_high = CGROUP_LIMIT_MAX,                  .memory_max = CGROUP_LIMIT_MAX, +                .memory_swap_max = CGROUP_LIMIT_MAX,                  .memory_limit = (uint64_t) -1,                  .cpu_usage_nsec = (uint64_t) -1,                  .tasks_current = (uint64_t) -1, diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c index c00ff79123..19c69a98b1 100644 --- a/src/udev/net/ethtool-util.c +++ b/src/udev/net/ethtool-util.c @@ -46,6 +46,14 @@ static const char* const wol_table[_WOL_MAX] = {  DEFINE_STRING_TABLE_LOOKUP(wol, WakeOnLan);  DEFINE_CONFIG_PARSE_ENUM(config_parse_wol, wol, WakeOnLan, "Failed to parse WakeOnLan setting"); +static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = { +        [NET_DEV_FEAT_GSO] = "tx-generic-segmentation", +        [NET_DEV_FEAT_GRO] = "rx-gro", +        [NET_DEV_FEAT_LRO] = "rx-lro", +        [NET_DEV_FEAT_TSO] = "tx-tcp-segmentation", +        [NET_DEV_FEAT_UFO] = "tx-udp-fragmentation", +}; +  int ethtool_connect(int *ret) {          int fd; @@ -206,3 +214,108 @@ int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol) {          return 0;  } + +static int ethtool_get_stringset(int *fd, struct ifreq *ifr, int stringset_id, struct ethtool_gstrings **gstrings) { +        _cleanup_free_ struct ethtool_gstrings *strings = NULL; +        struct ethtool_sset_info info =  { +                .cmd = ETHTOOL_GSSET_INFO, +                .reserved = 0, +                .sset_mask = 1ULL << stringset_id, +        }; +        unsigned len; +        int r; + +        ifr->ifr_data = (void *) &info; + +        r = ioctl(*fd, SIOCETHTOOL, ifr); +        if (r < 0) +                return -errno; + +        if (!info.sset_mask) +                return -EINVAL; + +        len = info.data[0]; + +        strings = malloc0(sizeof(struct ethtool_gstrings) + len * ETH_GSTRING_LEN); +        if (!strings) +                return -ENOMEM; + +        strings->cmd = ETHTOOL_GSTRINGS; +        strings->string_set = stringset_id; +        strings->len = len; + +        ifr->ifr_data = (void *) strings; + +        r = ioctl(*fd, SIOCETHTOOL, ifr); +        if (r < 0) +                return -errno; + +        *gstrings = strings; +        strings = NULL; + +        return 0; +} + +static int find_feature_index(struct ethtool_gstrings *strings, const char *feature) { +        unsigned i; + +        for (i = 0; i < strings->len; i++) { +                if (streq((char *) &strings->data[i * ETH_GSTRING_LEN], feature)) +                        return i; +        } + +        return -1; +} + +int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features) { +        _cleanup_free_ struct ethtool_gstrings *strings = NULL; +        struct ethtool_sfeatures *sfeatures; +        int block, bit, i, r; +        struct ifreq ifr; + +        if (*fd < 0) { +                r = ethtool_connect(fd); +                if (r < 0) +                        return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); +        } + +        strscpy(ifr.ifr_name, IFNAMSIZ, ifname); + +        r = ethtool_get_stringset(fd, &ifr, ETH_SS_FEATURES, &strings); +        if (r < 0) +                return log_warning_errno(r, "link_config: could not get ethtool features for %s", ifname); + +        sfeatures = alloca0(sizeof(struct ethtool_gstrings) + DIV_ROUND_UP(strings->len, 32U) * sizeof(sfeatures->features[0])); +        sfeatures->cmd = ETHTOOL_SFEATURES; +        sfeatures->size = DIV_ROUND_UP(strings->len, 32U); + +        for (i = 0; i < _NET_DEV_FEAT_MAX; i++) { + +                if (features[i] != -1) { + +                        r = find_feature_index(strings, netdev_feature_table[i]); +                        if (r < 0) { +                                log_warning_errno(r, "link_config: could not find feature: %s", netdev_feature_table[i]); +                                continue; +                        } + +                        block = r / 32; +                        bit = r % 32; + +                        sfeatures->features[block].valid |= 1 << bit; + +                        if (features[i]) +                                sfeatures->features[block].requested |= 1 << bit; +                        else +                                sfeatures->features[block].requested &= ~(1 << bit); +                } +        } + +        ifr.ifr_data = (void *) sfeatures; + +        r = ioctl(*fd, SIOCETHTOOL, &ifr); +        if (r < 0) +                return log_warning_errno(r, "link_config: could not set ethtool features for %s", ifname); + +        return 0; +} diff --git a/src/udev/net/ethtool-util.h b/src/udev/net/ethtool-util.h index 7716516e76..0744164653 100644 --- a/src/udev/net/ethtool-util.h +++ b/src/udev/net/ethtool-util.h @@ -38,11 +38,22 @@ typedef enum WakeOnLan {          _WOL_INVALID = -1  } WakeOnLan; +typedef enum NetDevFeature { +        NET_DEV_FEAT_GSO, +        NET_DEV_FEAT_GRO, +        NET_DEV_FEAT_LRO, +        NET_DEV_FEAT_TSO, +        NET_DEV_FEAT_UFO, +        _NET_DEV_FEAT_MAX, +        _NET_DEV_FEAT_INVALID = -1 +} NetDevFeature; +  int ethtool_connect(int *ret);  int ethtool_get_driver(int *fd, const char *ifname, char **ret);  int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex);  int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol); +int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features);  const char *duplex_to_string(Duplex d) _const_;  Duplex duplex_from_string(const char *d) _pure_; diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf index b25e4b3344..f8b85cbd13 100644 --- a/src/udev/net/link-config-gperf.gperf +++ b/src/udev/net/link-config-gperf.gperf @@ -16,22 +16,27 @@ struct ConfigPerfItem;  %struct-type  %includes  %% -Match.MACAddress,          config_parse_hwaddr,        0,                             offsetof(link_config, match_mac) -Match.OriginalName,        config_parse_ifnames,       0,                             offsetof(link_config, match_name) -Match.Path,                config_parse_strv,          0,                             offsetof(link_config, match_path) -Match.Driver,              config_parse_strv,          0,                             offsetof(link_config, match_driver) -Match.Type,                config_parse_strv,          0,                             offsetof(link_config, match_type) -Match.Host,                config_parse_net_condition, CONDITION_HOST,                offsetof(link_config, match_host) -Match.Virtualization,      config_parse_net_condition, CONDITION_VIRTUALIZATION,      offsetof(link_config, match_virt) -Match.KernelCommandLine,   config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(link_config, match_kernel) -Match.Architecture,        config_parse_net_condition, CONDITION_ARCHITECTURE,        offsetof(link_config, match_arch) -Link.Description,          config_parse_string,        0,                             offsetof(link_config, description) -Link.MACAddressPolicy,     config_parse_mac_policy,    0,                             offsetof(link_config, mac_policy) -Link.MACAddress,           config_parse_hwaddr,        0,                             offsetof(link_config, mac) -Link.NamePolicy,           config_parse_name_policy,   0,                             offsetof(link_config, name_policy) -Link.Name,                 config_parse_ifname,        0,                             offsetof(link_config, name) -Link.Alias,                config_parse_ifalias,       0,                             offsetof(link_config, alias) -Link.MTUBytes,             config_parse_iec_size,      0,                             offsetof(link_config, mtu) -Link.BitsPerSecond,        config_parse_si_size,       0,                             offsetof(link_config, speed) -Link.Duplex,               config_parse_duplex,        0,                             offsetof(link_config, duplex) -Link.WakeOnLan,            config_parse_wol,           0,                             offsetof(link_config, wol) +Match.MACAddress,                config_parse_hwaddr,        0,                             offsetof(link_config, match_mac) +Match.OriginalName,              config_parse_ifnames,       0,                             offsetof(link_config, match_name) +Match.Path,                      config_parse_strv,          0,                             offsetof(link_config, match_path) +Match.Driver,                    config_parse_strv,          0,                             offsetof(link_config, match_driver) +Match.Type,                      config_parse_strv,          0,                             offsetof(link_config, match_type) +Match.Host,                      config_parse_net_condition, CONDITION_HOST,                offsetof(link_config, match_host) +Match.Virtualization,            config_parse_net_condition, CONDITION_VIRTUALIZATION,      offsetof(link_config, match_virt) +Match.KernelCommandLine,         config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(link_config, match_kernel) +Match.Architecture,              config_parse_net_condition, CONDITION_ARCHITECTURE,        offsetof(link_config, match_arch) +Link.Description,                config_parse_string,        0,                             offsetof(link_config, description) +Link.MACAddressPolicy,           config_parse_mac_policy,    0,                             offsetof(link_config, mac_policy) +Link.MACAddress,                 config_parse_hwaddr,        0,                             offsetof(link_config, mac) +Link.NamePolicy,                 config_parse_name_policy,   0,                             offsetof(link_config, name_policy) +Link.Name,                       config_parse_ifname,        0,                             offsetof(link_config, name) +Link.Alias,                      config_parse_ifalias,       0,                             offsetof(link_config, alias) +Link.MTUBytes,                   config_parse_iec_size,      0,                             offsetof(link_config, mtu) +Link.BitsPerSecond,              config_parse_si_size,       0,                             offsetof(link_config, speed) +Link.Duplex,                     config_parse_duplex,        0,                             offsetof(link_config, duplex) +Link.WakeOnLan,                  config_parse_wol,           0,                             offsetof(link_config, wol) +Link.GenericSegmentationOffload, config_parse_tristate,      0,                             offsetof(link_config, features[NET_DEV_FEAT_GSO]) +Link.TCPSegmentationOffload,     config_parse_tristate,      0,                             offsetof(link_config, features[NET_DEV_FEAT_TSO]) +Link.UDPSegmentationOffload,     config_parse_tristate,      0,                             offsetof(link_config, features[NET_DEV_FEAT_UFO]) +Link.GenericReceiveOffload,      config_parse_tristate,      0,                             offsetof(link_config, features[NET_DEV_FEAT_GRO]) +Link.LargeReceiveOffload,        config_parse_tristate,      0,                             offsetof(link_config, features[NET_DEV_FEAT_LRO]) diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index c66504102f..eedd94e777 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -168,6 +168,8 @@ static int load_link(link_config_ctx *ctx, const char *filename) {          link->wol = _WOL_INVALID;          link->duplex = _DUP_INVALID; +        memset(&link->features, -1, _NET_DEV_FEAT_MAX); +          r = config_parse(NULL, filename, file,                           "Match\0Link\0Ethernet\0",                           config_item_perf_lookup, link_config_gperf_lookup, @@ -397,6 +399,10 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,                  log_warning_errno(r, "Could not set WakeOnLan of %s to %s: %m",                                    old_name, wol_to_string(config->wol)); +        r = ethtool_set_features(&ctx->ethtool_fd, old_name, config->features); +        if (r < 0) +                log_warning_errno(r, "Could not set offload features of %s: %m", old_name); +          ifindex = udev_device_get_ifindex(device);          if (ifindex <= 0) {                  log_warning("Could not find ifindex"); diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index 9df5529d05..91cc0357c4 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -70,6 +70,7 @@ struct link_config {          size_t speed;          Duplex duplex;          WakeOnLan wol; +        NetDevFeature features[_NET_DEV_FEAT_MAX];          LIST_FIELDS(link_config, links);  }; | 
