diff options
Diffstat (limited to 'net/core/ethtool.c')
-rw-r--r-- | net/core/ethtool.c | 556 |
1 files changed, 101 insertions, 455 deletions
diff --git a/net/core/ethtool.c b/net/core/ethtool.c index f44481707124..31b0b7f5383e 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c | |||
@@ -36,235 +36,44 @@ u32 ethtool_op_get_link(struct net_device *dev) | |||
36 | } | 36 | } |
37 | EXPORT_SYMBOL(ethtool_op_get_link); | 37 | EXPORT_SYMBOL(ethtool_op_get_link); |
38 | 38 | ||
39 | u32 ethtool_op_get_tx_csum(struct net_device *dev) | ||
40 | { | ||
41 | return (dev->features & NETIF_F_ALL_CSUM) != 0; | ||
42 | } | ||
43 | EXPORT_SYMBOL(ethtool_op_get_tx_csum); | ||
44 | |||
45 | int ethtool_op_set_tx_csum(struct net_device *dev, u32 data) | ||
46 | { | ||
47 | if (data) | ||
48 | dev->features |= NETIF_F_IP_CSUM; | ||
49 | else | ||
50 | dev->features &= ~NETIF_F_IP_CSUM; | ||
51 | |||
52 | return 0; | ||
53 | } | ||
54 | EXPORT_SYMBOL(ethtool_op_set_tx_csum); | ||
55 | |||
56 | int ethtool_op_set_tx_hw_csum(struct net_device *dev, u32 data) | ||
57 | { | ||
58 | if (data) | ||
59 | dev->features |= NETIF_F_HW_CSUM; | ||
60 | else | ||
61 | dev->features &= ~NETIF_F_HW_CSUM; | ||
62 | |||
63 | return 0; | ||
64 | } | ||
65 | EXPORT_SYMBOL(ethtool_op_set_tx_hw_csum); | ||
66 | |||
67 | int ethtool_op_set_tx_ipv6_csum(struct net_device *dev, u32 data) | ||
68 | { | ||
69 | if (data) | ||
70 | dev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; | ||
71 | else | ||
72 | dev->features &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM); | ||
73 | |||
74 | return 0; | ||
75 | } | ||
76 | EXPORT_SYMBOL(ethtool_op_set_tx_ipv6_csum); | ||
77 | |||
78 | u32 ethtool_op_get_sg(struct net_device *dev) | ||
79 | { | ||
80 | return (dev->features & NETIF_F_SG) != 0; | ||
81 | } | ||
82 | EXPORT_SYMBOL(ethtool_op_get_sg); | ||
83 | |||
84 | int ethtool_op_set_sg(struct net_device *dev, u32 data) | ||
85 | { | ||
86 | if (data) | ||
87 | dev->features |= NETIF_F_SG; | ||
88 | else | ||
89 | dev->features &= ~NETIF_F_SG; | ||
90 | |||
91 | return 0; | ||
92 | } | ||
93 | EXPORT_SYMBOL(ethtool_op_set_sg); | ||
94 | |||
95 | u32 ethtool_op_get_tso(struct net_device *dev) | ||
96 | { | ||
97 | return (dev->features & NETIF_F_TSO) != 0; | ||
98 | } | ||
99 | EXPORT_SYMBOL(ethtool_op_get_tso); | ||
100 | |||
101 | int ethtool_op_set_tso(struct net_device *dev, u32 data) | ||
102 | { | ||
103 | if (data) | ||
104 | dev->features |= NETIF_F_TSO; | ||
105 | else | ||
106 | dev->features &= ~NETIF_F_TSO; | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | EXPORT_SYMBOL(ethtool_op_set_tso); | ||
111 | |||
112 | u32 ethtool_op_get_ufo(struct net_device *dev) | ||
113 | { | ||
114 | return (dev->features & NETIF_F_UFO) != 0; | ||
115 | } | ||
116 | EXPORT_SYMBOL(ethtool_op_get_ufo); | ||
117 | |||
118 | int ethtool_op_set_ufo(struct net_device *dev, u32 data) | ||
119 | { | ||
120 | if (data) | ||
121 | dev->features |= NETIF_F_UFO; | ||
122 | else | ||
123 | dev->features &= ~NETIF_F_UFO; | ||
124 | return 0; | ||
125 | } | ||
126 | EXPORT_SYMBOL(ethtool_op_set_ufo); | ||
127 | |||
128 | /* the following list of flags are the same as their associated | ||
129 | * NETIF_F_xxx values in include/linux/netdevice.h | ||
130 | */ | ||
131 | static const u32 flags_dup_features = | ||
132 | (ETH_FLAG_LRO | ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN | ETH_FLAG_NTUPLE | | ||
133 | ETH_FLAG_RXHASH); | ||
134 | |||
135 | u32 ethtool_op_get_flags(struct net_device *dev) | ||
136 | { | ||
137 | /* in the future, this function will probably contain additional | ||
138 | * handling for flags which are not so easily handled | ||
139 | * by a simple masking operation | ||
140 | */ | ||
141 | |||
142 | return dev->features & flags_dup_features; | ||
143 | } | ||
144 | EXPORT_SYMBOL(ethtool_op_get_flags); | ||
145 | |||
146 | /* Check if device can enable (or disable) particular feature coded in "data" | ||
147 | * argument. Flags "supported" describe features that can be toggled by device. | ||
148 | * If feature can not be toggled, it state (enabled or disabled) must match | ||
149 | * hardcoded device features state, otherwise flags are marked as invalid. | ||
150 | */ | ||
151 | bool ethtool_invalid_flags(struct net_device *dev, u32 data, u32 supported) | ||
152 | { | ||
153 | u32 features = dev->features & flags_dup_features; | ||
154 | /* "data" can contain only flags_dup_features bits, | ||
155 | * see __ethtool_set_flags */ | ||
156 | |||
157 | return (features & ~supported) != (data & ~supported); | ||
158 | } | ||
159 | EXPORT_SYMBOL(ethtool_invalid_flags); | ||
160 | |||
161 | int ethtool_op_set_flags(struct net_device *dev, u32 data, u32 supported) | ||
162 | { | ||
163 | if (ethtool_invalid_flags(dev, data, supported)) | ||
164 | return -EINVAL; | ||
165 | |||
166 | dev->features = ((dev->features & ~flags_dup_features) | | ||
167 | (data & flags_dup_features)); | ||
168 | return 0; | ||
169 | } | ||
170 | EXPORT_SYMBOL(ethtool_op_set_flags); | ||
171 | |||
172 | /* Handlers for each ethtool command */ | 39 | /* Handlers for each ethtool command */ |
173 | 40 | ||
174 | #define ETHTOOL_DEV_FEATURE_WORDS 1 | 41 | #define ETHTOOL_DEV_FEATURE_WORDS ((NETDEV_FEATURE_COUNT + 31) / 32) |
175 | 42 | ||
176 | static void ethtool_get_features_compat(struct net_device *dev, | 43 | static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] = { |
177 | struct ethtool_get_features_block *features) | 44 | [NETIF_F_SG_BIT] = "tx-scatter-gather", |
178 | { | 45 | [NETIF_F_IP_CSUM_BIT] = "tx-checksum-ipv4", |
179 | if (!dev->ethtool_ops) | 46 | [NETIF_F_HW_CSUM_BIT] = "tx-checksum-ip-generic", |
180 | return; | 47 | [NETIF_F_IPV6_CSUM_BIT] = "tx-checksum-ipv6", |
181 | 48 | [NETIF_F_HIGHDMA_BIT] = "highdma", | |
182 | /* getting RX checksum */ | 49 | [NETIF_F_FRAGLIST_BIT] = "tx-scatter-gather-fraglist", |
183 | if (dev->ethtool_ops->get_rx_csum) | 50 | [NETIF_F_HW_VLAN_TX_BIT] = "tx-vlan-hw-insert", |
184 | if (dev->ethtool_ops->get_rx_csum(dev)) | 51 | |
185 | features[0].active |= NETIF_F_RXCSUM; | 52 | [NETIF_F_HW_VLAN_RX_BIT] = "rx-vlan-hw-parse", |
186 | 53 | [NETIF_F_HW_VLAN_FILTER_BIT] = "rx-vlan-filter", | |
187 | /* mark legacy-changeable features */ | 54 | [NETIF_F_VLAN_CHALLENGED_BIT] = "vlan-challenged", |
188 | if (dev->ethtool_ops->set_sg) | 55 | [NETIF_F_GSO_BIT] = "tx-generic-segmentation", |
189 | features[0].available |= NETIF_F_SG; | 56 | [NETIF_F_LLTX_BIT] = "tx-lockless", |
190 | if (dev->ethtool_ops->set_tx_csum) | 57 | [NETIF_F_NETNS_LOCAL_BIT] = "netns-local", |
191 | features[0].available |= NETIF_F_ALL_CSUM; | 58 | [NETIF_F_GRO_BIT] = "rx-gro", |
192 | if (dev->ethtool_ops->set_tso) | 59 | [NETIF_F_LRO_BIT] = "rx-lro", |
193 | features[0].available |= NETIF_F_ALL_TSO; | 60 | |
194 | if (dev->ethtool_ops->set_rx_csum) | 61 | [NETIF_F_TSO_BIT] = "tx-tcp-segmentation", |
195 | features[0].available |= NETIF_F_RXCSUM; | 62 | [NETIF_F_UFO_BIT] = "tx-udp-fragmentation", |
196 | if (dev->ethtool_ops->set_flags) | 63 | [NETIF_F_GSO_ROBUST_BIT] = "tx-gso-robust", |
197 | features[0].available |= flags_dup_features; | 64 | [NETIF_F_TSO_ECN_BIT] = "tx-tcp-ecn-segmentation", |
198 | } | 65 | [NETIF_F_TSO6_BIT] = "tx-tcp6-segmentation", |
199 | 66 | [NETIF_F_FSO_BIT] = "tx-fcoe-segmentation", | |
200 | static int ethtool_set_feature_compat(struct net_device *dev, | 67 | |
201 | int (*legacy_set)(struct net_device *, u32), | 68 | [NETIF_F_FCOE_CRC_BIT] = "tx-checksum-fcoe-crc", |
202 | struct ethtool_set_features_block *features, u32 mask) | 69 | [NETIF_F_SCTP_CSUM_BIT] = "tx-checksum-sctp", |
203 | { | 70 | [NETIF_F_FCOE_MTU_BIT] = "fcoe-mtu", |
204 | u32 do_set; | 71 | [NETIF_F_NTUPLE_BIT] = "rx-ntuple-filter", |
205 | 72 | [NETIF_F_RXHASH_BIT] = "rx-hashing", | |
206 | if (!legacy_set) | 73 | [NETIF_F_RXCSUM_BIT] = "rx-checksum", |
207 | return 0; | 74 | [NETIF_F_NOCACHE_COPY_BIT] = "tx-nocache-copy", |
208 | 75 | [NETIF_F_LOOPBACK_BIT] = "loopback", | |
209 | if (!(features[0].valid & mask)) | 76 | }; |
210 | return 0; | ||
211 | |||
212 | features[0].valid &= ~mask; | ||
213 | |||
214 | do_set = !!(features[0].requested & mask); | ||
215 | |||
216 | if (legacy_set(dev, do_set) < 0) | ||
217 | netdev_info(dev, | ||
218 | "Legacy feature change (%s) failed for 0x%08x\n", | ||
219 | do_set ? "set" : "clear", mask); | ||
220 | |||
221 | return 1; | ||
222 | } | ||
223 | |||
224 | static int ethtool_set_flags_compat(struct net_device *dev, | ||
225 | int (*legacy_set)(struct net_device *, u32), | ||
226 | struct ethtool_set_features_block *features, u32 mask) | ||
227 | { | ||
228 | u32 value; | ||
229 | |||
230 | if (!legacy_set) | ||
231 | return 0; | ||
232 | |||
233 | if (!(features[0].valid & mask)) | ||
234 | return 0; | ||
235 | |||
236 | value = dev->features & ~features[0].valid; | ||
237 | value |= features[0].requested; | ||
238 | |||
239 | features[0].valid &= ~mask; | ||
240 | |||
241 | if (legacy_set(dev, value & mask) < 0) | ||
242 | netdev_info(dev, "Legacy flags change failed\n"); | ||
243 | |||
244 | return 1; | ||
245 | } | ||
246 | |||
247 | static int ethtool_set_features_compat(struct net_device *dev, | ||
248 | struct ethtool_set_features_block *features) | ||
249 | { | ||
250 | int compat; | ||
251 | |||
252 | if (!dev->ethtool_ops) | ||
253 | return 0; | ||
254 | |||
255 | compat = ethtool_set_feature_compat(dev, dev->ethtool_ops->set_sg, | ||
256 | features, NETIF_F_SG); | ||
257 | compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_tx_csum, | ||
258 | features, NETIF_F_ALL_CSUM); | ||
259 | compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_tso, | ||
260 | features, NETIF_F_ALL_TSO); | ||
261 | compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_rx_csum, | ||
262 | features, NETIF_F_RXCSUM); | ||
263 | compat |= ethtool_set_flags_compat(dev, dev->ethtool_ops->set_flags, | ||
264 | features, flags_dup_features); | ||
265 | |||
266 | return compat; | ||
267 | } | ||
268 | 77 | ||
269 | static int ethtool_get_features(struct net_device *dev, void __user *useraddr) | 78 | static int ethtool_get_features(struct net_device *dev, void __user *useraddr) |
270 | { | 79 | { |
@@ -272,18 +81,21 @@ static int ethtool_get_features(struct net_device *dev, void __user *useraddr) | |||
272 | .cmd = ETHTOOL_GFEATURES, | 81 | .cmd = ETHTOOL_GFEATURES, |
273 | .size = ETHTOOL_DEV_FEATURE_WORDS, | 82 | .size = ETHTOOL_DEV_FEATURE_WORDS, |
274 | }; | 83 | }; |
275 | struct ethtool_get_features_block features[ETHTOOL_DEV_FEATURE_WORDS] = { | 84 | struct ethtool_get_features_block features[ETHTOOL_DEV_FEATURE_WORDS]; |
276 | { | ||
277 | .available = dev->hw_features, | ||
278 | .requested = dev->wanted_features, | ||
279 | .active = dev->features, | ||
280 | .never_changed = NETIF_F_NEVER_CHANGE, | ||
281 | }, | ||
282 | }; | ||
283 | u32 __user *sizeaddr; | 85 | u32 __user *sizeaddr; |
284 | u32 copy_size; | 86 | u32 copy_size; |
87 | int i; | ||
285 | 88 | ||
286 | ethtool_get_features_compat(dev, features); | 89 | /* in case feature bits run out again */ |
90 | BUILD_BUG_ON(ETHTOOL_DEV_FEATURE_WORDS * sizeof(u32) > sizeof(netdev_features_t)); | ||
91 | |||
92 | for (i = 0; i < ETHTOOL_DEV_FEATURE_WORDS; ++i) { | ||
93 | features[i].available = (u32)(dev->hw_features >> (32 * i)); | ||
94 | features[i].requested = (u32)(dev->wanted_features >> (32 * i)); | ||
95 | features[i].active = (u32)(dev->features >> (32 * i)); | ||
96 | features[i].never_changed = | ||
97 | (u32)(NETIF_F_NEVER_CHANGE >> (32 * i)); | ||
98 | } | ||
287 | 99 | ||
288 | sizeaddr = useraddr + offsetof(struct ethtool_gfeatures, size); | 100 | sizeaddr = useraddr + offsetof(struct ethtool_gfeatures, size); |
289 | if (get_user(copy_size, sizeaddr)) | 101 | if (get_user(copy_size, sizeaddr)) |
@@ -305,7 +117,8 @@ static int ethtool_set_features(struct net_device *dev, void __user *useraddr) | |||
305 | { | 117 | { |
306 | struct ethtool_sfeatures cmd; | 118 | struct ethtool_sfeatures cmd; |
307 | struct ethtool_set_features_block features[ETHTOOL_DEV_FEATURE_WORDS]; | 119 | struct ethtool_set_features_block features[ETHTOOL_DEV_FEATURE_WORDS]; |
308 | int ret = 0; | 120 | netdev_features_t wanted = 0, valid = 0; |
121 | int i, ret = 0; | ||
309 | 122 | ||
310 | if (copy_from_user(&cmd, useraddr, sizeof(cmd))) | 123 | if (copy_from_user(&cmd, useraddr, sizeof(cmd))) |
311 | return -EFAULT; | 124 | return -EFAULT; |
@@ -317,65 +130,29 @@ static int ethtool_set_features(struct net_device *dev, void __user *useraddr) | |||
317 | if (copy_from_user(features, useraddr, sizeof(features))) | 130 | if (copy_from_user(features, useraddr, sizeof(features))) |
318 | return -EFAULT; | 131 | return -EFAULT; |
319 | 132 | ||
320 | if (features[0].valid & ~NETIF_F_ETHTOOL_BITS) | 133 | for (i = 0; i < ETHTOOL_DEV_FEATURE_WORDS; ++i) { |
321 | return -EINVAL; | 134 | valid |= (netdev_features_t)features[i].valid << (32 * i); |
135 | wanted |= (netdev_features_t)features[i].requested << (32 * i); | ||
136 | } | ||
322 | 137 | ||
323 | if (ethtool_set_features_compat(dev, features)) | 138 | if (valid & ~NETIF_F_ETHTOOL_BITS) |
324 | ret |= ETHTOOL_F_COMPAT; | 139 | return -EINVAL; |
325 | 140 | ||
326 | if (features[0].valid & ~dev->hw_features) { | 141 | if (valid & ~dev->hw_features) { |
327 | features[0].valid &= dev->hw_features; | 142 | valid &= dev->hw_features; |
328 | ret |= ETHTOOL_F_UNSUPPORTED; | 143 | ret |= ETHTOOL_F_UNSUPPORTED; |
329 | } | 144 | } |
330 | 145 | ||
331 | dev->wanted_features &= ~features[0].valid; | 146 | dev->wanted_features &= ~valid; |
332 | dev->wanted_features |= features[0].valid & features[0].requested; | 147 | dev->wanted_features |= wanted & valid; |
333 | __netdev_update_features(dev); | 148 | __netdev_update_features(dev); |
334 | 149 | ||
335 | if ((dev->wanted_features ^ dev->features) & features[0].valid) | 150 | if ((dev->wanted_features ^ dev->features) & valid) |
336 | ret |= ETHTOOL_F_WISH; | 151 | ret |= ETHTOOL_F_WISH; |
337 | 152 | ||
338 | return ret; | 153 | return ret; |
339 | } | 154 | } |
340 | 155 | ||
341 | static const char netdev_features_strings[ETHTOOL_DEV_FEATURE_WORDS * 32][ETH_GSTRING_LEN] = { | ||
342 | /* NETIF_F_SG */ "tx-scatter-gather", | ||
343 | /* NETIF_F_IP_CSUM */ "tx-checksum-ipv4", | ||
344 | /* NETIF_F_NO_CSUM */ "tx-checksum-unneeded", | ||
345 | /* NETIF_F_HW_CSUM */ "tx-checksum-ip-generic", | ||
346 | /* NETIF_F_IPV6_CSUM */ "tx-checksum-ipv6", | ||
347 | /* NETIF_F_HIGHDMA */ "highdma", | ||
348 | /* NETIF_F_FRAGLIST */ "tx-scatter-gather-fraglist", | ||
349 | /* NETIF_F_HW_VLAN_TX */ "tx-vlan-hw-insert", | ||
350 | |||
351 | /* NETIF_F_HW_VLAN_RX */ "rx-vlan-hw-parse", | ||
352 | /* NETIF_F_HW_VLAN_FILTER */ "rx-vlan-filter", | ||
353 | /* NETIF_F_VLAN_CHALLENGED */ "vlan-challenged", | ||
354 | /* NETIF_F_GSO */ "tx-generic-segmentation", | ||
355 | /* NETIF_F_LLTX */ "tx-lockless", | ||
356 | /* NETIF_F_NETNS_LOCAL */ "netns-local", | ||
357 | /* NETIF_F_GRO */ "rx-gro", | ||
358 | /* NETIF_F_LRO */ "rx-lro", | ||
359 | |||
360 | /* NETIF_F_TSO */ "tx-tcp-segmentation", | ||
361 | /* NETIF_F_UFO */ "tx-udp-fragmentation", | ||
362 | /* NETIF_F_GSO_ROBUST */ "tx-gso-robust", | ||
363 | /* NETIF_F_TSO_ECN */ "tx-tcp-ecn-segmentation", | ||
364 | /* NETIF_F_TSO6 */ "tx-tcp6-segmentation", | ||
365 | /* NETIF_F_FSO */ "tx-fcoe-segmentation", | ||
366 | "", | ||
367 | "", | ||
368 | |||
369 | /* NETIF_F_FCOE_CRC */ "tx-checksum-fcoe-crc", | ||
370 | /* NETIF_F_SCTP_CSUM */ "tx-checksum-sctp", | ||
371 | /* NETIF_F_FCOE_MTU */ "fcoe-mtu", | ||
372 | /* NETIF_F_NTUPLE */ "rx-ntuple-filter", | ||
373 | /* NETIF_F_RXHASH */ "rx-hashing", | ||
374 | /* NETIF_F_RXCSUM */ "rx-checksum", | ||
375 | /* NETIF_F_NOCACHE_COPY */ "tx-nocache-copy", | ||
376 | /* NETIF_F_LOOPBACK */ "loopback", | ||
377 | }; | ||
378 | |||
379 | static int __ethtool_get_sset_count(struct net_device *dev, int sset) | 156 | static int __ethtool_get_sset_count(struct net_device *dev, int sset) |
380 | { | 157 | { |
381 | const struct ethtool_ops *ops = dev->ethtool_ops; | 158 | const struct ethtool_ops *ops = dev->ethtool_ops; |
@@ -402,7 +179,7 @@ static void __ethtool_get_strings(struct net_device *dev, | |||
402 | ops->get_strings(dev, stringset, data); | 179 | ops->get_strings(dev, stringset, data); |
403 | } | 180 | } |
404 | 181 | ||
405 | static u32 ethtool_get_feature_mask(u32 eth_cmd) | 182 | static netdev_features_t ethtool_get_feature_mask(u32 eth_cmd) |
406 | { | 183 | { |
407 | /* feature masks of legacy discrete ethtool ops */ | 184 | /* feature masks of legacy discrete ethtool ops */ |
408 | 185 | ||
@@ -433,136 +210,82 @@ static u32 ethtool_get_feature_mask(u32 eth_cmd) | |||
433 | } | 210 | } |
434 | } | 211 | } |
435 | 212 | ||
436 | static void *__ethtool_get_one_feature_actor(struct net_device *dev, u32 ethcmd) | ||
437 | { | ||
438 | const struct ethtool_ops *ops = dev->ethtool_ops; | ||
439 | |||
440 | if (!ops) | ||
441 | return NULL; | ||
442 | |||
443 | switch (ethcmd) { | ||
444 | case ETHTOOL_GTXCSUM: | ||
445 | return ops->get_tx_csum; | ||
446 | case ETHTOOL_GRXCSUM: | ||
447 | return ops->get_rx_csum; | ||
448 | case ETHTOOL_SSG: | ||
449 | return ops->get_sg; | ||
450 | case ETHTOOL_STSO: | ||
451 | return ops->get_tso; | ||
452 | case ETHTOOL_SUFO: | ||
453 | return ops->get_ufo; | ||
454 | default: | ||
455 | return NULL; | ||
456 | } | ||
457 | } | ||
458 | |||
459 | static u32 __ethtool_get_rx_csum_oldbug(struct net_device *dev) | ||
460 | { | ||
461 | return !!(dev->features & NETIF_F_ALL_CSUM); | ||
462 | } | ||
463 | |||
464 | static int ethtool_get_one_feature(struct net_device *dev, | 213 | static int ethtool_get_one_feature(struct net_device *dev, |
465 | char __user *useraddr, u32 ethcmd) | 214 | char __user *useraddr, u32 ethcmd) |
466 | { | 215 | { |
467 | u32 mask = ethtool_get_feature_mask(ethcmd); | 216 | netdev_features_t mask = ethtool_get_feature_mask(ethcmd); |
468 | struct ethtool_value edata = { | 217 | struct ethtool_value edata = { |
469 | .cmd = ethcmd, | 218 | .cmd = ethcmd, |
470 | .data = !!(dev->features & mask), | 219 | .data = !!(dev->features & mask), |
471 | }; | 220 | }; |
472 | 221 | ||
473 | /* compatibility with discrete get_ ops */ | ||
474 | if (!(dev->hw_features & mask)) { | ||
475 | u32 (*actor)(struct net_device *); | ||
476 | |||
477 | actor = __ethtool_get_one_feature_actor(dev, ethcmd); | ||
478 | |||
479 | /* bug compatibility with old get_rx_csum */ | ||
480 | if (ethcmd == ETHTOOL_GRXCSUM && !actor) | ||
481 | actor = __ethtool_get_rx_csum_oldbug; | ||
482 | |||
483 | if (actor) | ||
484 | edata.data = actor(dev); | ||
485 | } | ||
486 | |||
487 | if (copy_to_user(useraddr, &edata, sizeof(edata))) | 222 | if (copy_to_user(useraddr, &edata, sizeof(edata))) |
488 | return -EFAULT; | 223 | return -EFAULT; |
489 | return 0; | 224 | return 0; |
490 | } | 225 | } |
491 | 226 | ||
492 | static int __ethtool_set_tx_csum(struct net_device *dev, u32 data); | ||
493 | static int __ethtool_set_rx_csum(struct net_device *dev, u32 data); | ||
494 | static int __ethtool_set_sg(struct net_device *dev, u32 data); | ||
495 | static int __ethtool_set_tso(struct net_device *dev, u32 data); | ||
496 | static int __ethtool_set_ufo(struct net_device *dev, u32 data); | ||
497 | |||
498 | static int ethtool_set_one_feature(struct net_device *dev, | 227 | static int ethtool_set_one_feature(struct net_device *dev, |
499 | void __user *useraddr, u32 ethcmd) | 228 | void __user *useraddr, u32 ethcmd) |
500 | { | 229 | { |
501 | struct ethtool_value edata; | 230 | struct ethtool_value edata; |
502 | u32 mask; | 231 | netdev_features_t mask; |
503 | 232 | ||
504 | if (copy_from_user(&edata, useraddr, sizeof(edata))) | 233 | if (copy_from_user(&edata, useraddr, sizeof(edata))) |
505 | return -EFAULT; | 234 | return -EFAULT; |
506 | 235 | ||
507 | mask = ethtool_get_feature_mask(ethcmd); | 236 | mask = ethtool_get_feature_mask(ethcmd); |
508 | mask &= dev->hw_features; | 237 | mask &= dev->hw_features; |
509 | if (mask) { | 238 | if (!mask) |
510 | if (edata.data) | 239 | return -EOPNOTSUPP; |
511 | dev->wanted_features |= mask; | ||
512 | else | ||
513 | dev->wanted_features &= ~mask; | ||
514 | 240 | ||
515 | __netdev_update_features(dev); | 241 | if (edata.data) |
516 | return 0; | 242 | dev->wanted_features |= mask; |
517 | } | 243 | else |
244 | dev->wanted_features &= ~mask; | ||
518 | 245 | ||
519 | /* Driver is not converted to ndo_fix_features or does not | 246 | __netdev_update_features(dev); |
520 | * support changing this offload. In the latter case it won't | ||
521 | * have corresponding ethtool_ops field set. | ||
522 | * | ||
523 | * Following part is to be removed after all drivers advertise | ||
524 | * their changeable features in netdev->hw_features and stop | ||
525 | * using discrete offload setting ops. | ||
526 | */ | ||
527 | 247 | ||
528 | switch (ethcmd) { | 248 | return 0; |
529 | case ETHTOOL_STXCSUM: | 249 | } |
530 | return __ethtool_set_tx_csum(dev, edata.data); | 250 | |
531 | case ETHTOOL_SRXCSUM: | 251 | #define ETH_ALL_FLAGS (ETH_FLAG_LRO | ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN | \ |
532 | return __ethtool_set_rx_csum(dev, edata.data); | 252 | ETH_FLAG_NTUPLE | ETH_FLAG_RXHASH) |
533 | case ETHTOOL_SSG: | 253 | #define ETH_ALL_FEATURES (NETIF_F_LRO | NETIF_F_HW_VLAN_RX | \ |
534 | return __ethtool_set_sg(dev, edata.data); | 254 | NETIF_F_HW_VLAN_TX | NETIF_F_NTUPLE | NETIF_F_RXHASH) |
535 | case ETHTOOL_STSO: | 255 | |
536 | return __ethtool_set_tso(dev, edata.data); | 256 | static u32 __ethtool_get_flags(struct net_device *dev) |
537 | case ETHTOOL_SUFO: | 257 | { |
538 | return __ethtool_set_ufo(dev, edata.data); | 258 | u32 flags = 0; |
539 | default: | 259 | |
540 | return -EOPNOTSUPP; | 260 | if (dev->features & NETIF_F_LRO) flags |= ETH_FLAG_LRO; |
541 | } | 261 | if (dev->features & NETIF_F_HW_VLAN_RX) flags |= ETH_FLAG_RXVLAN; |
262 | if (dev->features & NETIF_F_HW_VLAN_TX) flags |= ETH_FLAG_TXVLAN; | ||
263 | if (dev->features & NETIF_F_NTUPLE) flags |= ETH_FLAG_NTUPLE; | ||
264 | if (dev->features & NETIF_F_RXHASH) flags |= ETH_FLAG_RXHASH; | ||
265 | |||
266 | return flags; | ||
542 | } | 267 | } |
543 | 268 | ||
544 | int __ethtool_set_flags(struct net_device *dev, u32 data) | 269 | static int __ethtool_set_flags(struct net_device *dev, u32 data) |
545 | { | 270 | { |
546 | u32 changed; | 271 | netdev_features_t features = 0, changed; |
547 | 272 | ||
548 | if (data & ~flags_dup_features) | 273 | if (data & ~ETH_ALL_FLAGS) |
549 | return -EINVAL; | 274 | return -EINVAL; |
550 | 275 | ||
551 | /* legacy set_flags() op */ | 276 | if (data & ETH_FLAG_LRO) features |= NETIF_F_LRO; |
552 | if (dev->ethtool_ops->set_flags) { | 277 | if (data & ETH_FLAG_RXVLAN) features |= NETIF_F_HW_VLAN_RX; |
553 | if (unlikely(dev->hw_features & flags_dup_features)) | 278 | if (data & ETH_FLAG_TXVLAN) features |= NETIF_F_HW_VLAN_TX; |
554 | netdev_warn(dev, | 279 | if (data & ETH_FLAG_NTUPLE) features |= NETIF_F_NTUPLE; |
555 | "driver BUG: mixed hw_features and set_flags()\n"); | 280 | if (data & ETH_FLAG_RXHASH) features |= NETIF_F_RXHASH; |
556 | return dev->ethtool_ops->set_flags(dev, data); | ||
557 | } | ||
558 | 281 | ||
559 | /* allow changing only bits set in hw_features */ | 282 | /* allow changing only bits set in hw_features */ |
560 | changed = (data ^ dev->features) & flags_dup_features; | 283 | changed = (features ^ dev->features) & ETH_ALL_FEATURES; |
561 | if (changed & ~dev->hw_features) | 284 | if (changed & ~dev->hw_features) |
562 | return (changed & dev->hw_features) ? -EINVAL : -EOPNOTSUPP; | 285 | return (changed & dev->hw_features) ? -EINVAL : -EOPNOTSUPP; |
563 | 286 | ||
564 | dev->wanted_features = | 287 | dev->wanted_features = |
565 | (dev->wanted_features & ~changed) | (data & dev->hw_features); | 288 | (dev->wanted_features & ~changed) | (features & changed); |
566 | 289 | ||
567 | __netdev_update_features(dev); | 290 | __netdev_update_features(dev); |
568 | 291 | ||
@@ -1231,81 +954,6 @@ static int ethtool_set_pauseparam(struct net_device *dev, void __user *useraddr) | |||
1231 | return dev->ethtool_ops->set_pauseparam(dev, &pauseparam); | 954 | return dev->ethtool_ops->set_pauseparam(dev, &pauseparam); |
1232 | } | 955 | } |
1233 | 956 | ||
1234 | static int __ethtool_set_sg(struct net_device *dev, u32 data) | ||
1235 | { | ||
1236 | int err; | ||
1237 | |||
1238 | if (!dev->ethtool_ops->set_sg) | ||
1239 | return -EOPNOTSUPP; | ||
1240 | |||
1241 | if (data && !(dev->features & NETIF_F_ALL_CSUM)) | ||
1242 | return -EINVAL; | ||
1243 | |||
1244 | if (!data && dev->ethtool_ops->set_tso) { | ||
1245 | err = dev->ethtool_ops->set_tso(dev, 0); | ||
1246 | if (err) | ||
1247 | return err; | ||
1248 | } | ||
1249 | |||
1250 | if (!data && dev->ethtool_ops->set_ufo) { | ||
1251 | err = dev->ethtool_ops->set_ufo(dev, 0); | ||
1252 | if (err) | ||
1253 | return err; | ||
1254 | } | ||
1255 | return dev->ethtool_ops->set_sg(dev, data); | ||
1256 | } | ||
1257 | |||
1258 | static int __ethtool_set_tx_csum(struct net_device *dev, u32 data) | ||
1259 | { | ||
1260 | int err; | ||
1261 | |||
1262 | if (!dev->ethtool_ops->set_tx_csum) | ||
1263 | return -EOPNOTSUPP; | ||
1264 | |||
1265 | if (!data && dev->ethtool_ops->set_sg) { | ||
1266 | err = __ethtool_set_sg(dev, 0); | ||
1267 | if (err) | ||
1268 | return err; | ||
1269 | } | ||
1270 | |||
1271 | return dev->ethtool_ops->set_tx_csum(dev, data); | ||
1272 | } | ||
1273 | |||
1274 | static int __ethtool_set_rx_csum(struct net_device *dev, u32 data) | ||
1275 | { | ||
1276 | if (!dev->ethtool_ops->set_rx_csum) | ||
1277 | return -EOPNOTSUPP; | ||
1278 | |||
1279 | if (!data) | ||
1280 | dev->features &= ~NETIF_F_GRO; | ||
1281 | |||
1282 | return dev->ethtool_ops->set_rx_csum(dev, data); | ||
1283 | } | ||
1284 | |||
1285 | static int __ethtool_set_tso(struct net_device *dev, u32 data) | ||
1286 | { | ||
1287 | if (!dev->ethtool_ops->set_tso) | ||
1288 | return -EOPNOTSUPP; | ||
1289 | |||
1290 | if (data && !(dev->features & NETIF_F_SG)) | ||
1291 | return -EINVAL; | ||
1292 | |||
1293 | return dev->ethtool_ops->set_tso(dev, data); | ||
1294 | } | ||
1295 | |||
1296 | static int __ethtool_set_ufo(struct net_device *dev, u32 data) | ||
1297 | { | ||
1298 | if (!dev->ethtool_ops->set_ufo) | ||
1299 | return -EOPNOTSUPP; | ||
1300 | if (data && !(dev->features & NETIF_F_SG)) | ||
1301 | return -EINVAL; | ||
1302 | if (data && !((dev->features & NETIF_F_GEN_CSUM) || | ||
1303 | (dev->features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM)) | ||
1304 | == (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) | ||
1305 | return -EINVAL; | ||
1306 | return dev->ethtool_ops->set_ufo(dev, data); | ||
1307 | } | ||
1308 | |||
1309 | static int ethtool_self_test(struct net_device *dev, char __user *useraddr) | 957 | static int ethtool_self_test(struct net_device *dev, char __user *useraddr) |
1310 | { | 958 | { |
1311 | struct ethtool_test test; | 959 | struct ethtool_test test; |
@@ -1771,9 +1419,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) | |||
1771 | break; | 1419 | break; |
1772 | case ETHTOOL_GFLAGS: | 1420 | case ETHTOOL_GFLAGS: |
1773 | rc = ethtool_get_value(dev, useraddr, ethcmd, | 1421 | rc = ethtool_get_value(dev, useraddr, ethcmd, |
1774 | (dev->ethtool_ops->get_flags ? | 1422 | __ethtool_get_flags); |
1775 | dev->ethtool_ops->get_flags : | ||
1776 | ethtool_op_get_flags)); | ||
1777 | break; | 1423 | break; |
1778 | case ETHTOOL_SFLAGS: | 1424 | case ETHTOOL_SFLAGS: |
1779 | rc = ethtool_set_value(dev, useraddr, __ethtool_set_flags); | 1425 | rc = ethtool_set_value(dev, useraddr, __ethtool_set_flags); |