aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/ethtool.h15
-rw-r--r--net/core/ethtool.c80
2 files changed, 95 insertions, 0 deletions
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index 084ddb3c8032..c1be61f3938b 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -384,6 +384,15 @@ struct ethtool_rxnfc {
384 __u32 rule_locs[0]; 384 __u32 rule_locs[0];
385}; 385};
386 386
387struct ethtool_rxfh_indir {
388 __u32 cmd;
389 /* On entry, this is the array size of the user buffer. On
390 * return from ETHTOOL_GRXFHINDIR, this is the array size of
391 * the hardware indirection table. */
392 __u32 size;
393 __u32 ring_index[0]; /* ring/queue index for each hash value */
394};
395
387struct ethtool_rx_ntuple_flow_spec { 396struct ethtool_rx_ntuple_flow_spec {
388 __u32 flow_type; 397 __u32 flow_type;
389 union { 398 union {
@@ -576,6 +585,10 @@ struct ethtool_ops {
576 int (*set_rx_ntuple)(struct net_device *, 585 int (*set_rx_ntuple)(struct net_device *,
577 struct ethtool_rx_ntuple *); 586 struct ethtool_rx_ntuple *);
578 int (*get_rx_ntuple)(struct net_device *, u32 stringset, void *); 587 int (*get_rx_ntuple)(struct net_device *, u32 stringset, void *);
588 int (*get_rxfh_indir)(struct net_device *,
589 struct ethtool_rxfh_indir *);
590 int (*set_rxfh_indir)(struct net_device *,
591 const struct ethtool_rxfh_indir *);
579}; 592};
580#endif /* __KERNEL__ */ 593#endif /* __KERNEL__ */
581 594
@@ -637,6 +650,8 @@ struct ethtool_ops {
637#define ETHTOOL_SRXNTUPLE 0x00000035 /* Add an n-tuple filter to device */ 650#define ETHTOOL_SRXNTUPLE 0x00000035 /* Add an n-tuple filter to device */
638#define ETHTOOL_GRXNTUPLE 0x00000036 /* Get n-tuple filters from device */ 651#define ETHTOOL_GRXNTUPLE 0x00000036 /* Get n-tuple filters from device */
639#define ETHTOOL_GSSET_INFO 0x00000037 /* Get string set info */ 652#define ETHTOOL_GSSET_INFO 0x00000037 /* Get string set info */
653#define ETHTOOL_GRXFHINDIR 0x00000038 /* Get RX flow hash indir'n table */
654#define ETHTOOL_SRXFHINDIR 0x00000039 /* Set RX flow hash indir'n table */
640 655
641/* compatibility with older code */ 656/* compatibility with older code */
642#define SPARC_ETH_GSET ETHTOOL_GSET 657#define SPARC_ETH_GSET ETHTOOL_GSET
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 5d42fae520d9..072d1d3796cb 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -358,6 +358,80 @@ err_out:
358 return ret; 358 return ret;
359} 359}
360 360
361static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
362 void __user *useraddr)
363{
364 struct ethtool_rxfh_indir *indir;
365 u32 table_size;
366 size_t full_size;
367 int ret;
368
369 if (!dev->ethtool_ops->get_rxfh_indir)
370 return -EOPNOTSUPP;
371
372 if (copy_from_user(&table_size,
373 useraddr + offsetof(struct ethtool_rxfh_indir, size),
374 sizeof(table_size)))
375 return -EFAULT;
376
377 if (table_size >
378 (KMALLOC_MAX_SIZE - sizeof(*indir)) / sizeof(*indir->ring_index))
379 return -ENOMEM;
380 full_size = sizeof(*indir) + sizeof(*indir->ring_index) * table_size;
381 indir = kmalloc(full_size, GFP_USER);
382 if (!indir)
383 return -ENOMEM;
384
385 indir->cmd = ETHTOOL_GRXFHINDIR;
386 indir->size = table_size;
387 ret = dev->ethtool_ops->get_rxfh_indir(dev, indir);
388 if (ret)
389 goto out;
390
391 if (copy_to_user(useraddr, indir, full_size))
392 ret = -EFAULT;
393
394out:
395 kfree(indir);
396 return ret;
397}
398
399static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
400 void __user *useraddr)
401{
402 struct ethtool_rxfh_indir *indir;
403 u32 table_size;
404 size_t full_size;
405 int ret;
406
407 if (!dev->ethtool_ops->set_rxfh_indir)
408 return -EOPNOTSUPP;
409
410 if (copy_from_user(&table_size,
411 useraddr + offsetof(struct ethtool_rxfh_indir, size),
412 sizeof(table_size)))
413 return -EFAULT;
414
415 if (table_size >
416 (KMALLOC_MAX_SIZE - sizeof(*indir)) / sizeof(*indir->ring_index))
417 return -ENOMEM;
418 full_size = sizeof(*indir) + sizeof(*indir->ring_index) * table_size;
419 indir = kmalloc(full_size, GFP_USER);
420 if (!indir)
421 return -ENOMEM;
422
423 if (copy_from_user(indir, useraddr, full_size)) {
424 ret = -EFAULT;
425 goto out;
426 }
427
428 ret = dev->ethtool_ops->set_rxfh_indir(dev, indir);
429
430out:
431 kfree(indir);
432 return ret;
433}
434
361static void __rx_ntuple_filter_add(struct ethtool_rx_ntuple_list *list, 435static void __rx_ntuple_filter_add(struct ethtool_rx_ntuple_list *list,
362 struct ethtool_rx_ntuple_flow_spec *spec, 436 struct ethtool_rx_ntuple_flow_spec *spec,
363 struct ethtool_rx_ntuple_flow_spec_container *fsc) 437 struct ethtool_rx_ntuple_flow_spec_container *fsc)
@@ -1526,6 +1600,12 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
1526 case ETHTOOL_GSSET_INFO: 1600 case ETHTOOL_GSSET_INFO:
1527 rc = ethtool_get_sset_info(dev, useraddr); 1601 rc = ethtool_get_sset_info(dev, useraddr);
1528 break; 1602 break;
1603 case ETHTOOL_GRXFHINDIR:
1604 rc = ethtool_get_rxfh_indir(dev, useraddr);
1605 break;
1606 case ETHTOOL_SRXFHINDIR:
1607 rc = ethtool_set_rxfh_indir(dev, useraddr);
1608 break;
1529 default: 1609 default:
1530 rc = -EOPNOTSUPP; 1610 rc = -EOPNOTSUPP;
1531 } 1611 }