aboutsummaryrefslogtreecommitdiffstats
path: root/net/core
diff options
context:
space:
mode:
authorJeff Garzik <jgarzik@redhat.com>2010-03-03 17:51:50 -0500
committerDavid S. Miller <davem@davemloft.net>2010-03-05 17:00:17 -0500
commit723b2f57ad83ee7087acf9a95e8e289414b1f521 (patch)
treee8120bff2899245d2c84905b1368a128bf881268 /net/core
parent4b79a1aedcb9dd6e3f27b970dcb553aefcd97254 (diff)
ethtool: Add direct access to ops->get_sset_count
This patch is an alternative approach for accessing string counts, vs. the drvinfo indirect approach. This way the drvinfo space doesn't run out, and we don't break ABI later. Signed-off-by: Jeff Garzik <jgarzik@redhat.com> Signed-off-by: Peter P Waskiewicz Jr <peter.p.waskiewicz.jr@intel.com> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core')
-rw-r--r--net/core/ethtool.c72
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 */
247static 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
304out:
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 */
243static noinline int ethtool_set_rxnfc(struct net_device *dev, void __user *useraddr) 312static 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 }