| File: | build-scan/../src/udev/net/ethtool-util.c | 
| Warning: | line 558, column 19 Potential leak of memory pointed to by 'u'  | 
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ | |||
| 2 | ||||
| 3 | #include <net/if.h> | |||
| 4 | #include <sys/ioctl.h> | |||
| 5 | #include <linux1/ethtool.h> | |||
| 6 | #include <linux1/sockios.h> | |||
| 7 | ||||
| 8 | #include "conf-parser.h" | |||
| 9 | #include "ethtool-util.h" | |||
| 10 | #include "link-config.h" | |||
| 11 | #include "log.h" | |||
| 12 | #include "missing.h" | |||
| 13 | #include "socket-util.h" | |||
| 14 | #include "string-table.h" | |||
| 15 | #include "strxcpyx.h" | |||
| 16 | #include "util.h" | |||
| 17 | ||||
| 18 | static const char* const duplex_table[_DUP_MAX] = { | |||
| 19 | [DUP_FULL] = "full", | |||
| 20 | [DUP_HALF] = "half" | |||
| 21 | }; | |||
| 22 | ||||
| 23 | DEFINE_STRING_TABLE_LOOKUP(duplex, Duplex)const char *duplex_to_string(Duplex i) { if (i < 0 || i >= (Duplex) __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(duplex_table), typeof(&*(duplex_table))), sizeof( duplex_table)/sizeof((duplex_table)[0]), ((void)0)))) return ( (void*)0); return duplex_table[i]; } Duplex duplex_from_string (const char *s) { return (Duplex) string_table_lookup(duplex_table , __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(duplex_table), typeof(&*(duplex_table))), sizeof( duplex_table)/sizeof((duplex_table)[0]), ((void)0))), s); };  | |||
| 24 | DEFINE_CONFIG_PARSE_ENUM(config_parse_duplex, duplex, Duplex, "Failed to parse duplex setting")int config_parse_duplex(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) { Duplex *i = data, x; do { if ((__builtin_expect (!!(!(filename)),0))) log_assert_failed_realm(LOG_REALM_UDEV, ("filename"), "../src/udev/net/ethtool-util.c", 24, __PRETTY_FUNCTION__ ); } while (0); do { if ((__builtin_expect(!!(!(lvalue)),0))) log_assert_failed_realm(LOG_REALM_UDEV, ("lvalue"), "../src/udev/net/ethtool-util.c" , 24, __PRETTY_FUNCTION__); } while (0); do { if ((__builtin_expect (!!(!(rvalue)),0))) log_assert_failed_realm(LOG_REALM_UDEV, ( "rvalue"), "../src/udev/net/ethtool-util.c", 24, __PRETTY_FUNCTION__ ); } while (0); do { if ((__builtin_expect(!!(!(data)),0))) log_assert_failed_realm (LOG_REALM_UDEV, ("data"), "../src/udev/net/ethtool-util.c", 24 , __PRETTY_FUNCTION__); } while (0); x = duplex_from_string(rvalue ); if (x < 0) { ({ int _level = (3), _e = (0); (log_get_max_level_realm (LOG_REALM_UDEV) >= ((_level) & 0x07)) ? log_syntax_internal (unit, _level, filename, line, _e, "../src/udev/net/ethtool-util.c" , 24, __func__, "Failed to parse duplex setting" ", ignoring: %s" , rvalue) : -abs(_e); }); return 0; } *i = x; return 0; };  | |||
| 25 | ||||
| 26 | static const char* const wol_table[_WOL_MAX] = { | |||
| 27 | [WOL_PHY] = "phy", | |||
| 28 | [WOL_UCAST] = "unicast", | |||
| 29 | [WOL_MCAST] = "multicast", | |||
| 30 | [WOL_BCAST] = "broadcast", | |||
| 31 | [WOL_ARP] = "arp", | |||
| 32 | [WOL_MAGIC] = "magic", | |||
| 33 | [WOL_MAGICSECURE] = "secureon", | |||
| 34 | [WOL_OFF] = "off" | |||
| 35 | }; | |||
| 36 | ||||
| 37 | DEFINE_STRING_TABLE_LOOKUP(wol, WakeOnLan)const char *wol_to_string(WakeOnLan i) { if (i < 0 || i >= (WakeOnLan) __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(wol_table), typeof(&*(wol_table))), sizeof(wol_table )/sizeof((wol_table)[0]), ((void)0)))) return ((void*)0); return wol_table[i]; } WakeOnLan wol_from_string(const char *s) { return (WakeOnLan) string_table_lookup(wol_table, __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(wol_table), typeof(& *(wol_table))), sizeof(wol_table)/sizeof((wol_table)[0]), ((void )0))), s); };  | |||
| 38 | DEFINE_CONFIG_PARSE_ENUM(config_parse_wol, wol, WakeOnLan, "Failed to parse WakeOnLan setting")int config_parse_wol(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 ) { WakeOnLan *i = data, x; do { if ((__builtin_expect(!!(!(filename )),0))) log_assert_failed_realm(LOG_REALM_UDEV, ("filename"), "../src/udev/net/ethtool-util.c", 38, __PRETTY_FUNCTION__); } while (0); do { if ((__builtin_expect(!!(!(lvalue)),0))) log_assert_failed_realm (LOG_REALM_UDEV, ("lvalue"), "../src/udev/net/ethtool-util.c" , 38, __PRETTY_FUNCTION__); } while (0); do { if ((__builtin_expect (!!(!(rvalue)),0))) log_assert_failed_realm(LOG_REALM_UDEV, ( "rvalue"), "../src/udev/net/ethtool-util.c", 38, __PRETTY_FUNCTION__ ); } while (0); do { if ((__builtin_expect(!!(!(data)),0))) log_assert_failed_realm (LOG_REALM_UDEV, ("data"), "../src/udev/net/ethtool-util.c", 38 , __PRETTY_FUNCTION__); } while (0); x = wol_from_string(rvalue ); if (x < 0) { ({ int _level = (3), _e = (0); (log_get_max_level_realm (LOG_REALM_UDEV) >= ((_level) & 0x07)) ? log_syntax_internal (unit, _level, filename, line, _e, "../src/udev/net/ethtool-util.c" , 38, __func__, "Failed to parse WakeOnLan setting" ", ignoring: %s" , rvalue) : -abs(_e); }); return 0; } *i = x; return 0; };  | |||
| 39 | ||||
| 40 | static const char* const port_table[_NET_DEV_PORT_MAX] = { | |||
| 41 | [NET_DEV_PORT_TP] = "tp", | |||
| 42 | [NET_DEV_PORT_AUI] = "aui", | |||
| 43 | [NET_DEV_PORT_MII] = "mii", | |||
| 44 | [NET_DEV_PORT_FIBRE] = "fibre", | |||
| 45 | [NET_DEV_PORT_BNC] = "bnc" | |||
| 46 | }; | |||
| 47 | ||||
| 48 | DEFINE_STRING_TABLE_LOOKUP(port, NetDevPort)const char *port_to_string(NetDevPort i) { if (i < 0 || i >= (NetDevPort) __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(port_table), typeof(&*(port_table))), sizeof(port_table )/sizeof((port_table)[0]), ((void)0)))) return ((void*)0); return port_table[i]; } NetDevPort port_from_string(const char *s) { return (NetDevPort) string_table_lookup(port_table, __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p(typeof (port_table), typeof(&*(port_table))), sizeof(port_table) /sizeof((port_table)[0]), ((void)0))), s); };  | |||
| 49 | DEFINE_CONFIG_PARSE_ENUM(config_parse_port, port, NetDevPort, "Failed to parse Port setting")int config_parse_port(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) { NetDevPort *i = data, x; do { if ((__builtin_expect (!!(!(filename)),0))) log_assert_failed_realm(LOG_REALM_UDEV, ("filename"), "../src/udev/net/ethtool-util.c", 49, __PRETTY_FUNCTION__ ); } while (0); do { if ((__builtin_expect(!!(!(lvalue)),0))) log_assert_failed_realm(LOG_REALM_UDEV, ("lvalue"), "../src/udev/net/ethtool-util.c" , 49, __PRETTY_FUNCTION__); } while (0); do { if ((__builtin_expect (!!(!(rvalue)),0))) log_assert_failed_realm(LOG_REALM_UDEV, ( "rvalue"), "../src/udev/net/ethtool-util.c", 49, __PRETTY_FUNCTION__ ); } while (0); do { if ((__builtin_expect(!!(!(data)),0))) log_assert_failed_realm (LOG_REALM_UDEV, ("data"), "../src/udev/net/ethtool-util.c", 49 , __PRETTY_FUNCTION__); } while (0); x = port_from_string(rvalue ); if (x < 0) { ({ int _level = (3), _e = (0); (log_get_max_level_realm (LOG_REALM_UDEV) >= ((_level) & 0x07)) ? log_syntax_internal (unit, _level, filename, line, _e, "../src/udev/net/ethtool-util.c" , 49, __func__, "Failed to parse Port setting" ", ignoring: %s" , rvalue) : -abs(_e); }); return 0; } *i = x; return 0; };  | |||
| 50 | ||||
| 51 | static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = { | |||
| 52 | [NET_DEV_FEAT_GSO] = "tx-generic-segmentation", | |||
| 53 | [NET_DEV_FEAT_GRO] = "rx-gro", | |||
| 54 | [NET_DEV_FEAT_LRO] = "rx-lro", | |||
| 55 | [NET_DEV_FEAT_TSO] = "tx-tcp-segmentation", | |||
| 56 | [NET_DEV_FEAT_TSO6] = "tx-tcp6-segmentation", | |||
| 57 | }; | |||
| 58 | ||||
| 59 | int ethtool_connect(int *ret) { | |||
| 60 | int fd; | |||
| 61 | ||||
| 62 |         assert_return(ret, -EINVAL)do { if (!(((__builtin_expect(!!(ret),1))) ? (1) : (log_assert_failed_return_realm (LOG_REALM_UDEV, ("ret"), "../src/udev/net/ethtool-util.c", 62 , __PRETTY_FUNCTION__), 0))) return (-22); } while (0);  | |||
| 63 | ||||
| 64 | fd = socket_ioctl_fd(); | |||
| 65 | if (fd < 0) | |||
| 66 | return fd; | |||
| 67 | ||||
| 68 | *ret = fd; | |||
| 69 | ||||
| 70 | return 0; | |||
| 71 | } | |||
| 72 | ||||
| 73 | int ethtool_get_driver(int *fd, const char *ifname, char **ret) { | |||
| 74 | struct ethtool_drvinfo ecmd = { | |||
| 75 | .cmd = ETHTOOL_GDRVINFO0x00000003 | |||
| 76 | }; | |||
| 77 | struct ifreq ifr = { | |||
| 78 | .ifr_dataifr_ifru.ifru_data = (void*) &ecmd | |||
| 79 | }; | |||
| 80 | char *d; | |||
| 81 | int r; | |||
| 82 | ||||
| 83 | if (*fd < 0) { | |||
| 84 | r = ethtool_connect(fd); | |||
| 85 | if (r < 0) | |||
| 86 |                         return log_warning_errno(r, "link_config: could not connect to ethtool: %m")({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_UDEV); (log_get_max_level_realm(_realm) >= ((_level) & 0x07) ) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/udev/net/ethtool-util.c", 86, __func__, "link_config: could not connect to ethtool: %m" ) : -abs(_e); });  | |||
| 87 | } | |||
| 88 | ||||
| 89 | strscpy(ifr.ifr_nameifr_ifrn.ifrn_name, IFNAMSIZ16, ifname); | |||
| 90 | ||||
| 91 | r = ioctl(*fd, SIOCETHTOOL0x8946, &ifr); | |||
| 92 | if (r < 0) | |||
| 93 | return -errno(*__errno_location ()); | |||
| 94 | ||||
| 95 | d = strdup(ecmd.driver); | |||
| 96 | if (!d) | |||
| 97 | return -ENOMEM12; | |||
| 98 | ||||
| 99 | *ret = d; | |||
| 100 | return 0; | |||
| 101 | } | |||
| 102 | ||||
| 103 | int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex) { | |||
| 104 | struct ethtool_cmd ecmd = { | |||
| 105 | .cmd = ETHTOOL_GSET0x00000001 | |||
| 106 | }; | |||
| 107 | struct ifreq ifr = { | |||
| 108 | .ifr_dataifr_ifru.ifru_data = (void*) &ecmd | |||
| 109 | }; | |||
| 110 | bool_Bool need_update = false0; | |||
| 111 | int r; | |||
| 112 | ||||
| 113 | if (speed == 0 && duplex == _DUP_INVALID) | |||
| 114 | return 0; | |||
| 115 | ||||
| 116 | if (*fd < 0) { | |||
| 117 | r = ethtool_connect(fd); | |||
| 118 | if (r < 0) | |||
| 119 |                         return log_warning_errno(r, "link_config: could not connect to ethtool: %m")({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_UDEV); (log_get_max_level_realm(_realm) >= ((_level) & 0x07) ) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/udev/net/ethtool-util.c", 119, __func__, "link_config: could not connect to ethtool: %m" ) : -abs(_e); });  | |||
| 120 | } | |||
| 121 | ||||
| 122 | strscpy(ifr.ifr_nameifr_ifrn.ifrn_name, IFNAMSIZ16, ifname); | |||
| 123 | ||||
| 124 | r = ioctl(*fd, SIOCETHTOOL0x8946, &ifr); | |||
| 125 | if (r < 0) | |||
| 126 | return -errno(*__errno_location ()); | |||
| 127 | ||||
| 128 | if (ethtool_cmd_speed(&ecmd) != speed) { | |||
| 129 | ethtool_cmd_speed_set(&ecmd, speed); | |||
| 130 | need_update = true1; | |||
| 131 | } | |||
| 132 | ||||
| 133 | switch (duplex) { | |||
| 134 | case DUP_HALF: | |||
| 135 | if (ecmd.duplex != DUPLEX_HALF0x00) { | |||
| 136 | ecmd.duplex = DUPLEX_HALF0x00; | |||
| 137 | need_update = true1; | |||
| 138 | } | |||
| 139 | break; | |||
| 140 | case DUP_FULL: | |||
| 141 | if (ecmd.duplex != DUPLEX_FULL0x01) { | |||
| 142 | ecmd.duplex = DUPLEX_FULL0x01; | |||
| 143 | need_update = true1; | |||
| 144 | } | |||
| 145 | break; | |||
| 146 | default: | |||
| 147 | break; | |||
| 148 | } | |||
| 149 | ||||
| 150 | if (need_update) { | |||
| 151 | ecmd.cmd = ETHTOOL_SSET0x00000002; | |||
| 152 | ||||
| 153 | r = ioctl(*fd, SIOCETHTOOL0x8946, &ifr); | |||
| 154 | if (r < 0) | |||
| 155 | return -errno(*__errno_location ()); | |||
| 156 | } | |||
| 157 | ||||
| 158 | return 0; | |||
| 159 | } | |||
| 160 | ||||
| 161 | int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol) { | |||
| 162 | struct ethtool_wolinfo ecmd = { | |||
| 163 | .cmd = ETHTOOL_GWOL0x00000005 | |||
| 164 | }; | |||
| 165 | struct ifreq ifr = { | |||
| 166 | .ifr_dataifr_ifru.ifru_data = (void*) &ecmd | |||
| 167 | }; | |||
| 168 | bool_Bool need_update = false0; | |||
| 169 | int r; | |||
| 170 | ||||
| 171 | if (wol == _WOL_INVALID) | |||
| 172 | return 0; | |||
| 173 | ||||
| 174 | if (*fd < 0) { | |||
| 175 | r = ethtool_connect(fd); | |||
| 176 | if (r < 0) | |||
| 177 |                         return log_warning_errno(r, "link_config: could not connect to ethtool: %m")({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_UDEV); (log_get_max_level_realm(_realm) >= ((_level) & 0x07) ) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/udev/net/ethtool-util.c", 177, __func__, "link_config: could not connect to ethtool: %m" ) : -abs(_e); });  | |||
| 178 | } | |||
| 179 | ||||
| 180 | strscpy(ifr.ifr_nameifr_ifrn.ifrn_name, IFNAMSIZ16, ifname); | |||
| 181 | ||||
| 182 | r = ioctl(*fd, SIOCETHTOOL0x8946, &ifr); | |||
| 183 | if (r < 0) | |||
| 184 | return -errno(*__errno_location ()); | |||
| 185 | ||||
| 186 | switch (wol) { | |||
| 187 | case WOL_PHY: | |||
| 188 | if (ecmd.wolopts != WAKE_PHY(1 << 0)) { | |||
| 189 | ecmd.wolopts = WAKE_PHY(1 << 0); | |||
| 190 | need_update = true1; | |||
| 191 | } | |||
| 192 | break; | |||
| 193 | case WOL_UCAST: | |||
| 194 | if (ecmd.wolopts != WAKE_UCAST(1 << 1)) { | |||
| 195 | ecmd.wolopts = WAKE_UCAST(1 << 1); | |||
| 196 | need_update = true1; | |||
| 197 | } | |||
| 198 | break; | |||
| 199 | case WOL_MCAST: | |||
| 200 | if (ecmd.wolopts != WAKE_MCAST(1 << 2)) { | |||
| 201 | ecmd.wolopts = WAKE_MCAST(1 << 2); | |||
| 202 | need_update = true1; | |||
| 203 | } | |||
| 204 | break; | |||
| 205 | case WOL_BCAST: | |||
| 206 | if (ecmd.wolopts != WAKE_BCAST(1 << 3)) { | |||
| 207 | ecmd.wolopts = WAKE_BCAST(1 << 3); | |||
| 208 | need_update = true1; | |||
| 209 | } | |||
| 210 | break; | |||
| 211 | case WOL_ARP: | |||
| 212 | if (ecmd.wolopts != WAKE_ARP(1 << 4)) { | |||
| 213 | ecmd.wolopts = WAKE_ARP(1 << 4); | |||
| 214 | need_update = true1; | |||
| 215 | } | |||
| 216 | break; | |||
| 217 | case WOL_MAGIC: | |||
| 218 | if (ecmd.wolopts != WAKE_MAGIC(1 << 5)) { | |||
| 219 | ecmd.wolopts = WAKE_MAGIC(1 << 5); | |||
| 220 | need_update = true1; | |||
| 221 | } | |||
| 222 | break; | |||
| 223 | case WOL_MAGICSECURE: | |||
| 224 | if (ecmd.wolopts != WAKE_MAGICSECURE(1 << 6)) { | |||
| 225 | ecmd.wolopts = WAKE_MAGICSECURE(1 << 6); | |||
| 226 | need_update = true1; | |||
| 227 | } | |||
| 228 | break; | |||
| 229 | case WOL_OFF: | |||
| 230 | if (ecmd.wolopts != 0) { | |||
| 231 | ecmd.wolopts = 0; | |||
| 232 | need_update = true1; | |||
| 233 | } | |||
| 234 | break; | |||
| 235 | default: | |||
| 236 | break; | |||
| 237 | } | |||
| 238 | ||||
| 239 | if (need_update) { | |||
| 240 | ecmd.cmd = ETHTOOL_SWOL0x00000006; | |||
| 241 | ||||
| 242 | r = ioctl(*fd, SIOCETHTOOL0x8946, &ifr); | |||
| 243 | if (r < 0) | |||
| 244 | return -errno(*__errno_location ()); | |||
| 245 | } | |||
| 246 | ||||
| 247 | return 0; | |||
| 248 | } | |||
| 249 | ||||
| 250 | static int get_stringset(int fd, struct ifreq *ifr, int stringset_id, struct ethtool_gstrings **gstrings) { | |||
| 251 | _cleanup_free___attribute__((cleanup(freep))) struct ethtool_gstrings *strings = NULL((void*)0); | |||
| 252 | struct { | |||
| 253 | struct ethtool_sset_info info; | |||
| 254 | uint32_t space; | |||
| 255 | } buffer = { | |||
| 256 | .info = { | |||
| 257 | .cmd = ETHTOOL_GSSET_INFO0x00000037, | |||
| 258 | .sset_mask = UINT64_C(1)1UL << stringset_id, | |||
| 259 | }, | |||
| 260 | }; | |||
| 261 | unsigned len; | |||
| 262 | int r; | |||
| 263 | ||||
| 264 | ifr->ifr_dataifr_ifru.ifru_data = (void *) &buffer.info; | |||
| 265 | ||||
| 266 | r = ioctl(fd, SIOCETHTOOL0x8946, ifr); | |||
| 267 | if (r < 0) | |||
| 268 | return -errno(*__errno_location ()); | |||
| 269 | ||||
| 270 | if (!buffer.info.sset_mask) | |||
| 271 | return -EINVAL22; | |||
| 272 | ||||
| 273 | len = buffer.info.data[0]; | |||
| 274 | ||||
| 275 | strings = malloc0(sizeof(struct ethtool_gstrings) + len * ETH_GSTRING_LEN)(calloc(1, (sizeof(struct ethtool_gstrings) + len * 32))); | |||
| 276 | if (!strings) | |||
| 277 | return -ENOMEM12; | |||
| 278 | ||||
| 279 | strings->cmd = ETHTOOL_GSTRINGS0x0000001b; | |||
| 280 | strings->string_set = stringset_id; | |||
| 281 | strings->len = len; | |||
| 282 | ||||
| 283 | ifr->ifr_dataifr_ifru.ifru_data = (void *) strings; | |||
| 284 | ||||
| 285 | r = ioctl(fd, SIOCETHTOOL0x8946, ifr); | |||
| 286 | if (r < 0) | |||
| 287 | return -errno(*__errno_location ()); | |||
| 288 | ||||
| 289 |         *gstrings = TAKE_PTR(strings)({ typeof(strings) _ptr_ = (strings); (strings) = ((void*)0); _ptr_; });  | |||
| 290 | ||||
| 291 | return 0; | |||
| 292 | } | |||
| 293 | ||||
| 294 | static int find_feature_index(struct ethtool_gstrings *strings, const char *feature) { | |||
| 295 | unsigned i; | |||
| 296 | ||||
| 297 | for (i = 0; i < strings->len; i++) { | |||
| 298 |                 if (streq((char *) &strings->data[i * ETH_GSTRING_LEN], feature)(strcmp(((char *) &strings->data[i * 32]),(feature)) == 0))  | |||
| 299 | return i; | |||
| 300 | } | |||
| 301 | ||||
| 302 | return -1; | |||
| 303 | } | |||
| 304 | ||||
| 305 | int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features) { | |||
| 306 | _cleanup_free___attribute__((cleanup(freep))) struct ethtool_gstrings *strings = NULL((void*)0); | |||
| 307 | struct ethtool_sfeatures *sfeatures; | |||
| 308 | int block, bit, i, r; | |||
| 309 | struct ifreq ifr = {}; | |||
| 310 | ||||
| 311 | if (*fd < 0) { | |||
| 312 | r = ethtool_connect(fd); | |||
| 313 | if (r < 0) | |||
| 314 |                         return log_warning_errno(r, "link_config: could not connect to ethtool: %m")({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_UDEV); (log_get_max_level_realm(_realm) >= ((_level) & 0x07) ) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/udev/net/ethtool-util.c", 314, __func__, "link_config: could not connect to ethtool: %m" ) : -abs(_e); });  | |||
| 315 | } | |||
| 316 | ||||
| 317 | strscpy(ifr.ifr_nameifr_ifrn.ifrn_name, IFNAMSIZ16, ifname); | |||
| 318 | ||||
| 319 | r = get_stringset(*fd, &ifr, ETH_SS_FEATURES, &strings); | |||
| 320 | if (r < 0) | |||
| 321 |                 return log_warning_errno(r, "link_config: could not get ethtool features for %s", ifname)({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_UDEV); (log_get_max_level_realm(_realm) >= ((_level) & 0x07) ) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/udev/net/ethtool-util.c", 321, __func__, "link_config: could not get ethtool features for %s" , ifname) : -abs(_e); });  | |||
| 322 | ||||
| 323 |         sfeatures = alloca0(sizeof(struct ethtool_gstrings) + DIV_ROUND_UP(strings->len, 32U) * sizeof(sfeatures->features[0]))({ char *_new_; size_t _len_ = sizeof(struct ethtool_gstrings ) + ({ const typeof((strings->len)) __unique_prefix_X44 = ( (strings->len)); const typeof((32U)) __unique_prefix_Y45 = ((32U)); (__unique_prefix_X44 / __unique_prefix_Y45 + !!(__unique_prefix_X44 % __unique_prefix_Y45)); }) * sizeof(sfeatures->features[ 0]); _new_ = __builtin_alloca (_len_); (void *) memset(_new_, 0, _len_); });  | |||
| 324 | sfeatures->cmd = ETHTOOL_SFEATURES0x0000003b; | |||
| 325 |         sfeatures->size = DIV_ROUND_UP(strings->len, 32U)({ const typeof((strings->len)) __unique_prefix_X46 = ((strings ->len)); const typeof((32U)) __unique_prefix_Y47 = ((32U)) ; (__unique_prefix_X46 / __unique_prefix_Y47 + !!(__unique_prefix_X46 % __unique_prefix_Y47)); });  | |||
| 326 | ||||
| 327 | for (i = 0; i < _NET_DEV_FEAT_MAX; i++) { | |||
| 328 | ||||
| 329 | if (features[i] != -1) { | |||
| 330 | ||||
| 331 | r = find_feature_index(strings, netdev_feature_table[i]); | |||
| 332 | if (r < 0) { | |||
| 333 |                                 log_warning_errno(r, "link_config: could not find feature: %s", netdev_feature_table[i])({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_UDEV); (log_get_max_level_realm(_realm) >= ((_level) & 0x07) ) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/udev/net/ethtool-util.c", 333, __func__, "link_config: could not find feature: %s" , netdev_feature_table[i]) : -abs(_e); });  | |||
| 334 | continue; | |||
| 335 | } | |||
| 336 | ||||
| 337 | block = r / 32; | |||
| 338 | bit = r % 32; | |||
| 339 | ||||
| 340 | sfeatures->features[block].valid |= 1 << bit; | |||
| 341 | ||||
| 342 | if (features[i]) | |||
| 343 | sfeatures->features[block].requested |= 1 << bit; | |||
| 344 | else | |||
| 345 | sfeatures->features[block].requested &= ~(1 << bit); | |||
| 346 | } | |||
| 347 | } | |||
| 348 | ||||
| 349 | ifr.ifr_dataifr_ifru.ifru_data = (void *) sfeatures; | |||
| 350 | ||||
| 351 | r = ioctl(*fd, SIOCETHTOOL0x8946, &ifr); | |||
| 352 | if (r < 0) | |||
| 353 |                 return log_warning_errno(r, "link_config: could not set ethtool features for %s", ifname)({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_UDEV); (log_get_max_level_realm(_realm) >= ((_level) & 0x07) ) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/udev/net/ethtool-util.c", 353, __func__, "link_config: could not set ethtool features for %s" , ifname) : -abs(_e); });  | |||
| 354 | ||||
| 355 | return 0; | |||
| 356 | } | |||
| 357 | ||||
| 358 | static int get_glinksettings(int fd, struct ifreq *ifr, struct ethtool_link_usettings **g) { | |||
| 359 | struct ecmd { | |||
| 360 | struct ethtool_link_settings req; | |||
| 361 | __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32(127)]; | |||
| 362 | } ecmd = { | |||
| 363 | .req.cmd = ETHTOOL_GLINKSETTINGS0x0000004c, | |||
| 364 | }; | |||
| 365 | struct ethtool_link_usettings *u; | |||
| 366 | unsigned offset; | |||
| 367 | int r; | |||
| 368 | ||||
| 369 | /* The interaction user/kernel via the new API requires a small ETHTOOL_GLINKSETTINGS | |||
| 370 | handshake first to agree on the length of the link mode bitmaps. If kernel doesn't | |||
| 371 | agree with user, it returns the bitmap length it is expecting from user as a negative | |||
| 372 | length (and cmd field is 0). When kernel and user agree, kernel returns valid info in | |||
| 373 | all fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS). Based on | |||
| 374 | https://github.com/torvalds/linux/commit/3f1ac7a700d039c61d8d8b99f28d605d489a60cf | |||
| 375 | */ | |||
| 376 | ||||
| 377 | ifr->ifr_dataifr_ifru.ifru_data = (void *) &ecmd; | |||
| 378 | ||||
| 379 | r = ioctl(fd, SIOCETHTOOL0x8946, ifr); | |||
| 380 | if (r < 0) | |||
| 381 | return -errno(*__errno_location ()); | |||
| 382 | ||||
| 383 | if (ecmd.req.link_mode_masks_nwords >= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS0x0000004c) | |||
| 384 | return -EOPNOTSUPP95; | |||
| 385 | ||||
| 386 | ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords; | |||
| 387 | ||||
| 388 | ifr->ifr_dataifr_ifru.ifru_data = (void *) &ecmd; | |||
| 389 | ||||
| 390 | r = ioctl(fd, SIOCETHTOOL0x8946, ifr); | |||
| 391 | if (r < 0) | |||
| 392 | return -errno(*__errno_location ()); | |||
| 393 | ||||
| 394 | if (ecmd.req.link_mode_masks_nwords <= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS0x0000004c) | |||
| 395 | return -EOPNOTSUPP95; | |||
| 396 | ||||
| 397 |         u = new0(struct ethtool_link_usettings , 1)((struct ethtool_link_usettings*) calloc((1), sizeof(struct ethtool_link_usettings )));  | |||
| 398 | if (!u) | |||
| 399 | return -ENOMEM12; | |||
| 400 | ||||
| 401 | u->base = ecmd.req; | |||
| 402 | ||||
| 403 | offset = 0; | |||
| 404 | memcpy(u->link_modes.supported, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords); | |||
| 405 | ||||
| 406 | offset += ecmd.req.link_mode_masks_nwords; | |||
| 407 | memcpy(u->link_modes.advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords); | |||
| 408 | ||||
| 409 | offset += ecmd.req.link_mode_masks_nwords; | |||
| 410 | memcpy(u->link_modes.lp_advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords); | |||
| 411 | ||||
| 412 | *g = u; | |||
| 413 | ||||
| 414 | return 0; | |||
| 415 | } | |||
| 416 | ||||
| 417 | static int get_gset(int fd, struct ifreq *ifr, struct ethtool_link_usettings **u) { | |||
| 418 | struct ethtool_link_usettings *e; | |||
| 419 | struct ethtool_cmd ecmd = { | |||
| 420 | .cmd = ETHTOOL_GSET0x00000001, | |||
| 421 | }; | |||
| 422 | int r; | |||
| 423 | ||||
| 424 | ifr->ifr_dataifr_ifru.ifru_data = (void *) &ecmd; | |||
| 425 | ||||
| 426 | r = ioctl(fd, SIOCETHTOOL0x8946, ifr); | |||
| 427 | if (r < 0) | |||
| 428 | return -errno(*__errno_location ()); | |||
| 429 | ||||
| 430 |         e = new0(struct ethtool_link_usettings, 1)((struct ethtool_link_usettings*) calloc((1), sizeof(struct ethtool_link_usettings )));  | |||
| 431 | if (!e) | |||
| 432 | return -ENOMEM12; | |||
| 433 | ||||
| 434 | e->base.cmd = ETHTOOL_GSET0x00000001; | |||
| 435 | ||||
| 436 | e->base.link_mode_masks_nwords = 1; | |||
| 437 | e->base.speed = ethtool_cmd_speed(&ecmd); | |||
| 438 | e->base.duplex = ecmd.duplex; | |||
| 439 | e->base.port = ecmd.port; | |||
| 440 | e->base.phy_address = ecmd.phy_address; | |||
| 441 | e->base.autoneg = ecmd.autoneg; | |||
| 442 | e->base.mdio_support = ecmd.mdio_support; | |||
| 443 | ||||
| 444 | e->link_modes.supported[0] = ecmd.supported; | |||
| 445 | e->link_modes.advertising[0] = ecmd.advertising; | |||
| 446 | e->link_modes.lp_advertising[0] = ecmd.lp_advertising; | |||
| 447 | ||||
| 448 | *u = e; | |||
| 449 | ||||
| 450 | return 0; | |||
| 451 | } | |||
| 452 | ||||
| 453 | static int set_slinksettings(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) { | |||
| 454 | struct { | |||
| 455 | struct ethtool_link_settings req; | |||
| 456 | __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32(127)]; | |||
| 457 | } ecmd = {}; | |||
| 458 | unsigned int offset; | |||
| 459 | int r; | |||
| 460 | ||||
| 461 | if (u->base.cmd != ETHTOOL_GLINKSETTINGS0x0000004c || u->base.link_mode_masks_nwords <= 0) | |||
| 462 | return -EINVAL22; | |||
| 463 | ||||
| 464 | ecmd.req = u->base; | |||
| 465 | ecmd.req.cmd = ETHTOOL_SLINKSETTINGS0x0000004d; | |||
| 466 | offset = 0; | |||
| 467 | memcpy(&ecmd.link_mode_data[offset], u->link_modes.supported, 4 * ecmd.req.link_mode_masks_nwords); | |||
| 468 | ||||
| 469 | offset += ecmd.req.link_mode_masks_nwords; | |||
| 470 | memcpy(&ecmd.link_mode_data[offset], u->link_modes.advertising, 4 * ecmd.req.link_mode_masks_nwords); | |||
| 471 | ||||
| 472 | offset += ecmd.req.link_mode_masks_nwords; | |||
| 473 | memcpy(&ecmd.link_mode_data[offset], u->link_modes.lp_advertising, 4 * ecmd.req.link_mode_masks_nwords); | |||
| 474 | ||||
| 475 | ifr->ifr_dataifr_ifru.ifru_data = (void *) &ecmd; | |||
| 476 | ||||
| 477 | r = ioctl(fd, SIOCETHTOOL0x8946, ifr); | |||
| 478 | if (r < 0) | |||
| 479 | return -errno(*__errno_location ()); | |||
| 480 | ||||
| 481 | return 0; | |||
| 482 | } | |||
| 483 | ||||
| 484 | static int set_sset(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) { | |||
| 485 | struct ethtool_cmd ecmd = { | |||
| 486 | .cmd = ETHTOOL_SSET0x00000002, | |||
| 487 | }; | |||
| 488 | int r; | |||
| 489 | ||||
| 490 | if (u->base.cmd != ETHTOOL_GSET0x00000001 || u->base.link_mode_masks_nwords <= 0) | |||
| 491 | return -EINVAL22; | |||
| 492 | ||||
| 493 | ecmd.supported = u->link_modes.supported[0]; | |||
| 494 | ecmd.advertising = u->link_modes.advertising[0]; | |||
| 495 | ecmd.lp_advertising = u->link_modes.lp_advertising[0]; | |||
| 496 | ||||
| 497 | ethtool_cmd_speed_set(&ecmd, u->base.speed); | |||
| 498 | ||||
| 499 | ecmd.duplex = u->base.duplex; | |||
| 500 | ecmd.port = u->base.port; | |||
| 501 | ecmd.phy_address = u->base.phy_address; | |||
| 502 | ecmd.autoneg = u->base.autoneg; | |||
| 503 | ecmd.mdio_support = u->base.mdio_support; | |||
| 504 | ||||
| 505 | ifr->ifr_dataifr_ifru.ifru_data = (void *) &ecmd; | |||
| 506 | ||||
| 507 | r = ioctl(fd, SIOCETHTOOL0x8946, ifr); | |||
| 508 | if (r < 0) | |||
| 509 | return -errno(*__errno_location ()); | |||
| 510 | ||||
| 511 | return 0; | |||
| 512 | } | |||
| 513 | ||||
| 514 | /* If autonegotiation is disabled, the speed and duplex represent the fixed link | |||
| 515 | * mode and are writable if the driver supports multiple link modes. If it is | |||
| 516 | * enabled then they are read-only. If the link is up they represent the negotiated | |||
| 517 | * link mode; if the link is down, the speed is 0, %SPEED_UNKNOWN or the highest | |||
| 518 | * enabled speed and @duplex is %DUPLEX_UNKNOWN or the best enabled duplex mode. | |||
| 519 | */ | |||
| 520 | ||||
| 521 | int ethtool_set_glinksettings(int *fd, const char *ifname, struct link_config *link) { | |||
| 522 | _cleanup_free___attribute__((cleanup(freep))) struct ethtool_link_usettings *u = NULL((void*)0); | |||
| 523 | struct ifreq ifr = {}; | |||
| 524 | int r; | |||
| 525 | ||||
| 526 | if (link->autonegotiation != 0) { | |||
  | ||||
| 527 |                 log_info("link_config: autonegotiation is unset or enabled, the speed and duplex are not writable.")({ int _level = (((6))), _e = ((0)), _realm = (LOG_REALM_UDEV ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/udev/net/ethtool-util.c", 527, __func__, "link_config: autonegotiation is unset or enabled, the speed and duplex are not writable." ) : -abs(_e); });  | |||
| 528 | return 0; | |||
| 529 | } | |||
| 530 | ||||
| 531 | if (*fd < 0) { | |||
| 532 | r = ethtool_connect(fd); | |||
| 533 | if (r < 0) | |||
| 534 |                         return log_warning_errno(r, "link_config: could not connect to ethtool: %m")({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_UDEV); (log_get_max_level_realm(_realm) >= ((_level) & 0x07) ) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/udev/net/ethtool-util.c", 534, __func__, "link_config: could not connect to ethtool: %m" ) : -abs(_e); });  | |||
| 535 | } | |||
| 536 | ||||
| 537 | strscpy(ifr.ifr_nameifr_ifrn.ifrn_name, IFNAMSIZ16, ifname); | |||
| 538 | ||||
| 539 | r = get_glinksettings(*fd, &ifr, &u); | |||
| 540 |         if (r
  | |||
| 541 | r = get_gset(*fd, &ifr, &u); | |||
| 542 | if (r < 0) | |||
| 543 |                         return log_warning_errno(r, "link_config: Cannot get device settings for %s : %m", ifname)({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_UDEV); (log_get_max_level_realm(_realm) >= ((_level) & 0x07) ) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/udev/net/ethtool-util.c", 543, __func__, "link_config: Cannot get device settings for %s : %m" , ifname) : -abs(_e); });  | |||
| 544 | } | |||
| 545 | ||||
| 546 | if (link->speed) | |||
| 547 |                 u->base.speed = DIV_ROUND_UP(link->speed, 1000000)({ const typeof((link->speed)) __unique_prefix_X48 = ((link ->speed)); const typeof((1000000)) __unique_prefix_Y49 = ( (1000000)); (__unique_prefix_X48 / __unique_prefix_Y49 + !!(__unique_prefix_X48 % __unique_prefix_Y49)); });  | |||
| 548 | ||||
| 549 | if (link->duplex != _DUP_INVALID) | |||
| 550 | u->base.duplex = link->duplex; | |||
| 551 | ||||
| 552 | if (link->port != _NET_DEV_PORT_INVALID) | |||
| 553 | u->base.port = link->port; | |||
| 554 | ||||
| 555 | u->base.autoneg = link->autonegotiation; | |||
| 556 | ||||
| 557 |         if (u->base.cmd
  | |||
| 558 | r = set_slinksettings(*fd, &ifr, u); | |||
  | ||||
| 559 | else | |||
| 560 | r = set_sset(*fd, &ifr, u); | |||
| 561 | if (r < 0) | |||
| 562 |                 return log_warning_errno(r, "link_config: Cannot set device settings for %s : %m", ifname)({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_UDEV); (log_get_max_level_realm(_realm) >= ((_level) & 0x07) ) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/udev/net/ethtool-util.c", 562, __func__, "link_config: Cannot set device settings for %s : %m" , ifname) : -abs(_e); });  | |||
| 563 | ||||
| 564 | return r; | |||
| 565 | } | |||
| 566 | ||||
| 567 | int config_parse_channel(const char *unit, | |||
| 568 | const char *filename, | |||
| 569 | unsigned line, | |||
| 570 | const char *section, | |||
| 571 | unsigned section_line, | |||
| 572 | const char *lvalue, | |||
| 573 | int ltype, | |||
| 574 | const char *rvalue, | |||
| 575 | void *data, | |||
| 576 | void *userdata) { | |||
| 577 | link_config *config = data; | |||
| 578 | uint32_t k; | |||
| 579 | int r; | |||
| 580 | ||||
| 581 |         assert(filename)do { if ((__builtin_expect(!!(!(filename)),0))) log_assert_failed_realm (LOG_REALM_UDEV, ("filename"), "../src/udev/net/ethtool-util.c" , 581, __PRETTY_FUNCTION__); } while (0);  | |||
| 582 |         assert(section)do { if ((__builtin_expect(!!(!(section)),0))) log_assert_failed_realm (LOG_REALM_UDEV, ("section"), "../src/udev/net/ethtool-util.c" , 582, __PRETTY_FUNCTION__); } while (0);  | |||
| 583 |         assert(lvalue)do { if ((__builtin_expect(!!(!(lvalue)),0))) log_assert_failed_realm (LOG_REALM_UDEV, ("lvalue"), "../src/udev/net/ethtool-util.c" , 583, __PRETTY_FUNCTION__); } while (0);  | |||
| 584 |         assert(rvalue)do { if ((__builtin_expect(!!(!(rvalue)),0))) log_assert_failed_realm (LOG_REALM_UDEV, ("rvalue"), "../src/udev/net/ethtool-util.c" , 584, __PRETTY_FUNCTION__); } while (0);  | |||
| 585 |         assert(data)do { if ((__builtin_expect(!!(!(data)),0))) log_assert_failed_realm (LOG_REALM_UDEV, ("data"), "../src/udev/net/ethtool-util.c", 585 , __PRETTY_FUNCTION__); } while (0);  | |||
| 586 | ||||
| 587 | r = safe_atou32(rvalue, &k); | |||
| 588 | if (r < 0) { | |||
| 589 |                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse channel value, ignoring: %s", rvalue)({ int _level = (3), _e = (r); (log_get_max_level_realm(LOG_REALM_UDEV ) >= ((_level) & 0x07)) ? log_syntax_internal(unit, _level , filename, line, _e, "../src/udev/net/ethtool-util.c", 589, __func__ , "Failed to parse channel value, ignoring: %s", rvalue) : -abs (_e); });  | |||
| 590 | return 0; | |||
| 591 | } | |||
| 592 | ||||
| 593 | if (k < 1) { | |||
| 594 |                 log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Invalid %s value, ignoring: %s", lvalue, rvalue)({ int _level = (3), _e = (-22); (log_get_max_level_realm(LOG_REALM_UDEV ) >= ((_level) & 0x07)) ? log_syntax_internal(unit, _level , filename, line, _e, "../src/udev/net/ethtool-util.c", 594, __func__ , "Invalid %s value, ignoring: %s", lvalue, rvalue) : -abs(_e ); });  | |||
| 595 | return 0; | |||
| 596 | } | |||
| 597 | ||||
| 598 | if (streq(lvalue, "RxChannels")(strcmp((lvalue),("RxChannels")) == 0)) { | |||
| 599 | config->channels.rx_count = k; | |||
| 600 | config->channels.rx_count_set = true1; | |||
| 601 | } else if (streq(lvalue, "TxChannels")(strcmp((lvalue),("TxChannels")) == 0)) { | |||
| 602 | config->channels.tx_count = k; | |||
| 603 | config->channels.tx_count_set = true1; | |||
| 604 | } else if (streq(lvalue, "OtherChannels")(strcmp((lvalue),("OtherChannels")) == 0)) { | |||
| 605 | config->channels.other_count = k; | |||
| 606 | config->channels.other_count_set = true1; | |||
| 607 | } else if (streq(lvalue, "CombinedChannels")(strcmp((lvalue),("CombinedChannels")) == 0)) { | |||
| 608 | config->channels.combined_count = k; | |||
| 609 | config->channels.combined_count_set = true1; | |||
| 610 | } | |||
| 611 | ||||
| 612 | return 0; | |||
| 613 | } | |||
| 614 | ||||
| 615 | int ethtool_set_channels(int *fd, const char *ifname, netdev_channels *channels) { | |||
| 616 | struct ethtool_channels ecmd = { | |||
| 617 | .cmd = ETHTOOL_GCHANNELS0x0000003c | |||
| 618 | }; | |||
| 619 | struct ifreq ifr = { | |||
| 620 | .ifr_dataifr_ifru.ifru_data = (void*) &ecmd | |||
| 621 | }; | |||
| 622 | ||||
| 623 | bool_Bool need_update = false0; | |||
| 624 | int r; | |||
| 625 | ||||
| 626 | if (*fd < 0) { | |||
| 627 | r = ethtool_connect(fd); | |||
| 628 | if (r < 0) | |||
| 629 |                         return log_warning_errno(r, "link_config: could not connect to ethtool: %m")({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_UDEV); (log_get_max_level_realm(_realm) >= ((_level) & 0x07) ) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/udev/net/ethtool-util.c", 629, __func__, "link_config: could not connect to ethtool: %m" ) : -abs(_e); });  | |||
| 630 | } | |||
| 631 | ||||
| 632 | strscpy(ifr.ifr_nameifr_ifrn.ifrn_name, IFNAMSIZ16, ifname); | |||
| 633 | ||||
| 634 | r = ioctl(*fd, SIOCETHTOOL0x8946, &ifr); | |||
| 635 | if (r < 0) | |||
| 636 | return -errno(*__errno_location ()); | |||
| 637 | ||||
| 638 | if (channels->rx_count_set && ecmd.rx_count != channels->rx_count) { | |||
| 639 | ecmd.rx_count = channels->rx_count; | |||
| 640 | need_update = true1; | |||
| 641 | } | |||
| 642 | ||||
| 643 | if (channels->tx_count_set && ecmd.tx_count != channels->tx_count) { | |||
| 644 | ecmd.tx_count = channels->tx_count; | |||
| 645 | need_update = true1; | |||
| 646 | } | |||
| 647 | ||||
| 648 | if (channels->other_count_set && ecmd.other_count != channels->other_count) { | |||
| 649 | ecmd.other_count = channels->other_count; | |||
| 650 | need_update = true1; | |||
| 651 | } | |||
| 652 | ||||
| 653 | if (channels->combined_count_set && ecmd.combined_count != channels->combined_count) { | |||
| 654 | ecmd.combined_count = channels->combined_count; | |||
| 655 | need_update = true1; | |||
| 656 | } | |||
| 657 | ||||
| 658 | if (need_update) { | |||
| 659 | ecmd.cmd = ETHTOOL_SCHANNELS0x0000003d; | |||
| 660 | ||||
| 661 | r = ioctl(*fd, SIOCETHTOOL0x8946, &ifr); | |||
| 662 | if (r < 0) | |||
| 663 | return -errno(*__errno_location ()); | |||
| 664 | } | |||
| 665 | ||||
| 666 | return 0; | |||
| 667 | } |