diff options
-rw-r--r-- | include/linux/ethtool.h | 17 | ||||
-rw-r--r-- | net/core/ethtool.c | 72 |
2 files changed, 86 insertions, 3 deletions
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index cca1c3de140d..f6f961fefbe5 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h | |||
@@ -253,6 +253,17 @@ struct ethtool_gstrings { | |||
253 | __u8 data[0]; | 253 | __u8 data[0]; |
254 | }; | 254 | }; |
255 | 255 | ||
256 | struct ethtool_sset_info { | ||
257 | __u32 cmd; /* ETHTOOL_GSSET_INFO */ | ||
258 | __u32 reserved; | ||
259 | __u64 sset_mask; /* input: each bit selects an sset to query */ | ||
260 | /* output: each bit a returned sset */ | ||
261 | __u32 data[0]; /* ETH_SS_xxx count, in order, based on bits | ||
262 | in sset_mask. One bit implies one | ||
263 | __u32, two bits implies two | ||
264 | __u32's, etc. */ | ||
265 | }; | ||
266 | |||
256 | enum ethtool_test_flags { | 267 | enum ethtool_test_flags { |
257 | ETH_TEST_FL_OFFLINE = (1 << 0), /* online / offline */ | 268 | ETH_TEST_FL_OFFLINE = (1 << 0), /* online / offline */ |
258 | ETH_TEST_FL_FAILED = (1 << 1), /* test passed / failed */ | 269 | ETH_TEST_FL_FAILED = (1 << 1), /* test passed / failed */ |
@@ -606,9 +617,9 @@ struct ethtool_ops { | |||
606 | #define ETHTOOL_SRXCLSRLINS 0x00000032 /* Insert RX classification rule */ | 617 | #define ETHTOOL_SRXCLSRLINS 0x00000032 /* Insert RX classification rule */ |
607 | #define ETHTOOL_FLASHDEV 0x00000033 /* Flash firmware to device */ | 618 | #define ETHTOOL_FLASHDEV 0x00000033 /* Flash firmware to device */ |
608 | #define ETHTOOL_RESET 0x00000034 /* Reset hardware */ | 619 | #define ETHTOOL_RESET 0x00000034 /* Reset hardware */ |
609 | 620 | #define ETHTOOL_SRXNTUPLE 0x00000035 /* Add an n-tuple filter to device */ | |
610 | #define ETHTOOL_SRXNTUPLE 0x00000035 /* Add an n-tuple filter to device */ | 621 | #define ETHTOOL_GRXNTUPLE 0x00000036 /* Get n-tuple filters from device */ |
611 | #define ETHTOOL_GRXNTUPLE 0x00000036 /* Get n-tuple filters from device */ | 622 | #define ETHTOOL_GSSET_INFO 0x00000037 /* Get string set info */ |
612 | 623 | ||
613 | /* compatibility with older code */ | 624 | /* compatibility with older code */ |
614 | #define SPARC_ETH_GSET ETHTOOL_GSET | 625 | #define SPARC_ETH_GSET ETHTOOL_GSET |
diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 0f2f82185ec4..70075c47ada8 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c | |||
@@ -214,6 +214,10 @@ static noinline int ethtool_get_drvinfo(struct net_device *dev, void __user *use | |||
214 | info.cmd = ETHTOOL_GDRVINFO; | 214 | info.cmd = ETHTOOL_GDRVINFO; |
215 | ops->get_drvinfo(dev, &info); | 215 | ops->get_drvinfo(dev, &info); |
216 | 216 | ||
217 | /* | ||
218 | * this method of obtaining string set info is deprecated; | ||
219 | * consider using ETHTOOL_GSSET_INFO instead | ||
220 | */ | ||
217 | if (ops->get_sset_count) { | 221 | if (ops->get_sset_count) { |
218 | int rc; | 222 | int rc; |
219 | 223 | ||
@@ -240,6 +244,71 @@ static noinline int ethtool_get_drvinfo(struct net_device *dev, void __user *use | |||
240 | /* | 244 | /* |
241 | * noinline attribute so that gcc doesnt use too much stack in dev_ethtool() | 245 | * noinline attribute so that gcc doesnt use too much stack in dev_ethtool() |
242 | */ | 246 | */ |
247 | static noinline int ethtool_get_sset_info(struct net_device *dev, | ||
248 | void __user *useraddr) | ||
249 | { | ||
250 | struct ethtool_sset_info info; | ||
251 | const struct ethtool_ops *ops = dev->ethtool_ops; | ||
252 | u64 sset_mask; | ||
253 | int i, idx = 0, n_bits = 0, ret, rc; | ||
254 | u32 *info_buf = NULL; | ||
255 | |||
256 | if (!ops->get_sset_count) | ||
257 | return -EOPNOTSUPP; | ||
258 | |||
259 | if (copy_from_user(&info, useraddr, sizeof(info))) | ||
260 | return -EFAULT; | ||
261 | |||
262 | /* store copy of mask, because we zero struct later on */ | ||
263 | sset_mask = info.sset_mask; | ||
264 | if (!sset_mask) | ||
265 | return 0; | ||
266 | |||
267 | /* calculate size of return buffer */ | ||
268 | for (i = 0; i < 64; i++) | ||
269 | if (sset_mask & (1ULL << i)) | ||
270 | n_bits++; | ||
271 | |||
272 | memset(&info, 0, sizeof(info)); | ||
273 | info.cmd = ETHTOOL_GSSET_INFO; | ||
274 | |||
275 | info_buf = kzalloc(n_bits * sizeof(u32), GFP_USER); | ||
276 | if (!info_buf) | ||
277 | return -ENOMEM; | ||
278 | |||
279 | /* | ||
280 | * fill return buffer based on input bitmask and successful | ||
281 | * get_sset_count return | ||
282 | */ | ||
283 | for (i = 0; i < 64; i++) { | ||
284 | if (!(sset_mask & (1ULL << i))) | ||
285 | continue; | ||
286 | |||
287 | rc = ops->get_sset_count(dev, i); | ||
288 | if (rc >= 0) { | ||
289 | info.sset_mask |= (1ULL << i); | ||
290 | info_buf[idx++] = rc; | ||
291 | } | ||
292 | } | ||
293 | |||
294 | ret = -EFAULT; | ||
295 | if (copy_to_user(useraddr, &info, sizeof(info))) | ||
296 | goto out; | ||
297 | |||
298 | useraddr += offsetof(struct ethtool_sset_info, data); | ||
299 | if (copy_to_user(useraddr, info_buf, idx * sizeof(u32))) | ||
300 | goto out; | ||
301 | |||
302 | ret = 0; | ||
303 | |||
304 | out: | ||
305 | kfree(info_buf); | ||
306 | return ret; | ||
307 | } | ||
308 | |||
309 | /* | ||
310 | * noinline attribute so that gcc doesnt use too much stack in dev_ethtool() | ||
311 | */ | ||
243 | static noinline int ethtool_set_rxnfc(struct net_device *dev, void __user *useraddr) | 312 | static noinline int ethtool_set_rxnfc(struct net_device *dev, void __user *useraddr) |
244 | { | 313 | { |
245 | struct ethtool_rxnfc cmd; | 314 | struct ethtool_rxnfc cmd; |
@@ -1471,6 +1540,9 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) | |||
1471 | case ETHTOOL_GRXNTUPLE: | 1540 | case ETHTOOL_GRXNTUPLE: |
1472 | rc = ethtool_get_rx_ntuple(dev, useraddr); | 1541 | rc = ethtool_get_rx_ntuple(dev, useraddr); |
1473 | break; | 1542 | break; |
1543 | case ETHTOOL_GSSET_INFO: | ||
1544 | rc = ethtool_get_sset_info(dev, useraddr); | ||
1545 | break; | ||
1474 | default: | 1546 | default: |
1475 | rc = -EOPNOTSUPP; | 1547 | rc = -EOPNOTSUPP; |
1476 | } | 1548 | } |