Commit f38d138a authored by Kan Liang's avatar Kan Liang Committed by David S. Miller

net/ethtool: support set coalesce per queue

This patch implements sub command ETHTOOL_SCOALESCE for ioctl
ETHTOOL_PERQUEUE. It introduces an interface set_per_queue_coalesce to
set coalesce of each masked queue to device driver. The wanted coalesce
information are stored in "data" for each masked queue, which can copy
from userspace.
If it fails to set coalesce to device driver, the value which already
set to specific queue will be tried to rollback.
Signed-off-by: default avatarKan Liang <kan.liang@intel.com>
Reviewed-by: default avatarBen Hutchings <ben@decadent.org.uk>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 421797b1
...@@ -206,6 +206,11 @@ static inline u32 ethtool_rxfh_indir_default(u32 index, u32 n_rx_rings) ...@@ -206,6 +206,11 @@ static inline u32 ethtool_rxfh_indir_default(u32 index, u32 n_rx_rings)
* a TX queue has this number, return -EINVAL. If only a RX queue or a TX * a TX queue has this number, return -EINVAL. If only a RX queue or a TX
* queue has this number, set the inapplicable fields to ~0 and return 0. * queue has this number, set the inapplicable fields to ~0 and return 0.
* Returns a negative error code or zero. * Returns a negative error code or zero.
* @set_per_queue_coalesce: Set interrupt coalescing parameters per queue.
* It must check that the given queue number is valid. If neither a RX nor
* a TX queue has this number, return -EINVAL. If only a RX queue or a TX
* queue has this number, ignore the inapplicable fields.
* Returns a negative error code or zero.
* *
* All operations are optional (i.e. the function pointer may be set * All operations are optional (i.e. the function pointer may be set
* to %NULL) and callers must take this into account. Callers must * to %NULL) and callers must take this into account. Callers must
...@@ -286,6 +291,8 @@ struct ethtool_ops { ...@@ -286,6 +291,8 @@ struct ethtool_ops {
const struct ethtool_tunable *, const void *); const struct ethtool_tunable *, const void *);
int (*get_per_queue_coalesce)(struct net_device *, u32, int (*get_per_queue_coalesce)(struct net_device *, u32,
struct ethtool_coalesce *); struct ethtool_coalesce *);
int (*set_per_queue_coalesce)(struct net_device *, u32,
struct ethtool_coalesce *);
}; };
#endif /* _LINUX_ETHTOOL_H */ #endif /* _LINUX_ETHTOOL_H */
...@@ -1920,6 +1920,65 @@ static int ethtool_get_per_queue_coalesce(struct net_device *dev, ...@@ -1920,6 +1920,65 @@ static int ethtool_get_per_queue_coalesce(struct net_device *dev,
return 0; return 0;
} }
static int ethtool_set_per_queue_coalesce(struct net_device *dev,
void __user *useraddr,
struct ethtool_per_queue_op *per_queue_opt)
{
u32 bit;
int i, ret = 0;
int n_queue;
struct ethtool_coalesce *backup = NULL, *tmp = NULL;
DECLARE_BITMAP(queue_mask, MAX_NUM_QUEUE);
if ((!dev->ethtool_ops->set_per_queue_coalesce) ||
(!dev->ethtool_ops->get_per_queue_coalesce))
return -EOPNOTSUPP;
useraddr += sizeof(*per_queue_opt);
bitmap_from_u32array(queue_mask,
MAX_NUM_QUEUE,
per_queue_opt->queue_mask,
DIV_ROUND_UP(MAX_NUM_QUEUE, 32));
n_queue = bitmap_weight(queue_mask, MAX_NUM_QUEUE);
tmp = backup = kmalloc_array(n_queue, sizeof(*backup), GFP_KERNEL);
if (!backup)
return -ENOMEM;
for_each_set_bit(bit, queue_mask, MAX_NUM_QUEUE) {
struct ethtool_coalesce coalesce;
ret = dev->ethtool_ops->get_per_queue_coalesce(dev, bit, tmp);
if (ret != 0)
goto roll_back;
tmp++;
if (copy_from_user(&coalesce, useraddr, sizeof(coalesce))) {
ret = -EFAULT;
goto roll_back;
}
ret = dev->ethtool_ops->set_per_queue_coalesce(dev, bit, &coalesce);
if (ret != 0)
goto roll_back;
useraddr += sizeof(coalesce);
}
roll_back:
if (ret != 0) {
tmp = backup;
for_each_set_bit(i, queue_mask, bit) {
dev->ethtool_ops->set_per_queue_coalesce(dev, i, tmp);
tmp++;
}
}
kfree(backup);
return ret;
}
static int ethtool_set_per_queue(struct net_device *dev, void __user *useraddr) static int ethtool_set_per_queue(struct net_device *dev, void __user *useraddr)
{ {
struct ethtool_per_queue_op per_queue_opt; struct ethtool_per_queue_op per_queue_opt;
...@@ -1930,6 +1989,8 @@ static int ethtool_set_per_queue(struct net_device *dev, void __user *useraddr) ...@@ -1930,6 +1989,8 @@ static int ethtool_set_per_queue(struct net_device *dev, void __user *useraddr)
switch (per_queue_opt.sub_command) { switch (per_queue_opt.sub_command) {
case ETHTOOL_GCOALESCE: case ETHTOOL_GCOALESCE:
return ethtool_get_per_queue_coalesce(dev, useraddr, &per_queue_opt); return ethtool_get_per_queue_coalesce(dev, useraddr, &per_queue_opt);
case ETHTOOL_SCOALESCE:
return ethtool_set_per_queue_coalesce(dev, useraddr, &per_queue_opt);
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment