diff options
Diffstat (limited to 'net/core')
-rw-r--r-- | net/core/ethtool.c | 72 |
1 files changed, 72 insertions, 0 deletions
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 | } |