diff mbox series

[v2,4/6] ethtool: support per-queue sub command --show-coalesce

Message ID 20190206000106.24364-4-jeffrey.t.kirsher@intel.com
State Superseded
Delegated to: John Linville
Headers show
Series [v2,1/6] ethtool: move option parsing related code into function | expand

Commit Message

Kirsher, Jeffrey T Feb. 6, 2019, 12:01 a.m. UTC
From: Nicholas Nunley <nicholas.d.nunley@intel.com>

Get all masked queues' coalesce settings from kernel and dump them one by
one.

Example:

 $ sudo ./ethtool --set-perqueue-command eth5 queue_mask 0x11
   --show-coalesce
 Queue: 0
 Adaptive RX: off  TX: off
 stats-block-usecs: 0
 sample-interval: 0
 pkt-rate-low: 0
 pkt-rate-high: 0

 rx-usecs: 222
 rx-frames: 0
 rx-usecs-irq: 0
 rx-frames-irq: 256

 tx-usecs: 222
 tx-frames: 0
 tx-usecs-irq: 0
 tx-frames-irq: 256

 rx-usecs-low: 0
 rx-frame-low: 0
 tx-usecs-low: 0
 tx-frame-low: 0

 rx-usecs-high: 0
 rx-frame-high: 0
 tx-usecs-high: 0
 tx-frame-high: 0

 Queue: 4
 Adaptive RX: off  TX: off
 stats-block-usecs: 0
 sample-interval: 0
 pkt-rate-low: 0
 pkt-rate-high: 0

 rx-usecs: 222
 rx-frames: 0
 rx-usecs-irq: 0
 rx-frames-irq: 256

 tx-usecs: 222
 tx-frames: 0
 tx-usecs-irq: 0
 tx-frames-irq: 256

 rx-usecs-low: 0
 rx-frame-low: 0
 tx-usecs-low: 0
 tx-frame-low: 0

 rx-usecs-high: 0
 rx-frame-high: 0
 tx-usecs-high: 0
 tx-frame-high: 0

Based on patch by Kan Liang <kan.liang@intel.com>

Signed-off-by: Nicholas Nunley <nicholas.d.nunley@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
 ethtool.8.in |  2 +-
 ethtool.c    | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 63 insertions(+), 3 deletions(-)

Comments

Michal Kubecek Feb. 6, 2019, 1:22 p.m. UTC | #1
On Tue, Feb 05, 2019 at 04:01:04PM -0800, Jeff Kirsher wrote:
> diff --git a/ethtool.c b/ethtool.c
> index 4dc725c..9a1b83b 100644
> --- a/ethtool.c
> +++ b/ethtool.c
> @@ -1409,6 +1409,29 @@ static int dump_coalesce(const struct ethtool_coalesce *ecoal)
>  	return 0;
>  }
>  
> +void dump_per_queue_coalesce(struct ethtool_per_queue_op *per_queue_opt,
> +			     __u32 *queue_mask)
> +{
> +	char *addr;
> +	int i;
> +
> +	addr = (char *)per_queue_opt + sizeof(*per_queue_opt);
> +	for (i = 0; i < __KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32); i++) {
> +		int queue = i * 32;
> +		__u32 mask = queue_mask[i];
> +
> +		while (mask > 0) {
> +			if (mask & 0x1) {
> +				fprintf(stdout, "Queue: %d\n", queue);
> +				dump_coalesce((struct ethtool_coalesce *)addr);
> +				addr += sizeof(struct ethtool_coalesce);
> +			}
> +			mask = mask >> 1;
> +			queue++;
> +		}
> +	}
> +}
> +
>  struct feature_state {
>  	u32 off_flags;
>  	struct ethtool_gfeatures features;

The casts and pointer arithmetic are a bit complicated. How about this?

	struct ethtool_coalesce *addr;
...
	addr = (struct ethtool_coalesce *)(per_queue_opt + 1);
...
	dump_coalesce(addr);
	addr++;

Also passing n_queue down from do_perqueue() would prevent having to
parse the whole bitmap even if we already know the NIC has only few
queues.

> @@ -5245,7 +5268,8 @@ static const struct option {
>  	{ "--show-fec", 1, do_gfec, "Show FEC settings"},
>  	{ "--set-fec", 1, do_sfec, "Set FEC settings",
>  	  "		[ encoding auto|off|rs|baser [...]]\n"},
> -	{ "--set-perqueue-command", 1, do_perqueue, "Set per queue command",
> +	{ "--set-perqueue-command", 1, do_perqueue, "Set per queue command. "
> +	  "The supported sub commands include --show-coalesce",
>  	  "             [queue_mask %x] SUB_COMMAND\n"},
>  	{ "-h|--help", 0, show_usage, "Show this help" },
>  	{ "--version", 0, do_version, "Show version number" },
> @@ -5350,8 +5374,32 @@ static int find_max_num_queues(struct cmd_context *ctx)
>  		   echannels.combined_count);
>  }
>  
> +static struct ethtool_per_queue_op *
> +get_per_queue_coalesce(struct cmd_context *ctx, __u32 *queue_mask, int n_queues)
> +{
> +	struct ethtool_per_queue_op *per_queue_opt;
> +
> +	per_queue_opt = malloc(sizeof(*per_queue_opt) + n_queues *
> +			sizeof(struct ethtool_coalesce));
> +	if (!per_queue_opt)
> +		return NULL;
> +
> +	memcpy(per_queue_opt->queue_mask, queue_mask,
> +	       __KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32) * sizeof(__u32));
> +	per_queue_opt->cmd = ETHTOOL_PERQUEUE;
> +	per_queue_opt->sub_command = ETHTOOL_GCOALESCE;
> +	if (send_ioctl(ctx, per_queue_opt)) {
> +		free(per_queue_opt);
> +		perror("Cannot get device per queue parameters");
> +		return NULL;
> +	}
> +
> +	return per_queue_opt;
> +}
> +
>  static int do_perqueue(struct cmd_context *ctx)
>  {
> +	struct ethtool_per_queue_op *per_queue_opt;
>  	__u32 queue_mask[__KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32)] = {0};
>  	int i, n_queues = 0;
>  
> @@ -5390,7 +5438,19 @@ static int do_perqueue(struct cmd_context *ctx)
>  	if (i < 0)
>  		exit_bad_args();
>  
> -	/* no sub_command support yet */
> +	if (strstr(args[i].opts, "--show-coalesce") != NULL) {

Comparing args[i].func to do_gcoalesce might be easier.

> +		per_queue_opt = get_per_queue_coalesce(ctx, queue_mask,
> +						       n_queues);
> +		if (per_queue_opt == NULL) {
> +			perror("Cannot get device per queue parameters");
> +			return -EFAULT;
> +		}
> +		dump_per_queue_coalesce(per_queue_opt, queue_mask);
> +		free(per_queue_opt);
> +	} else {
> +		perror("The subcommand is not supported yet");
> +		return -EOPNOTSUPP;
> +	}
>  
>  	return 0;
>  }

Michal Kubecek
Nunley, Nicholas D Feb. 7, 2019, 11:53 p.m. UTC | #2
> -----Original Message-----
> From: Michal Kubecek [mailto:mkubecek@suse.cz]
> Sent: Wednesday, February 6, 2019 5:22 AM
> To: netdev@vger.kernel.org
> Cc: Kirsher, Jeffrey T <jeffrey.t.kirsher@intel.com>; linville@tuxdriver.com;
> Nunley, Nicholas D <nicholas.d.nunley@intel.com>; nhorman@redhat.com;
> sassmann@redhat.com
> Subject: Re: [PATCH v2 4/6] ethtool: support per-queue sub command --
> show-coalesce
> 
> On Tue, Feb 05, 2019 at 04:01:04PM -0800, Jeff Kirsher wrote:
> > diff --git a/ethtool.c b/ethtool.c
> > index 4dc725c..9a1b83b 100644
> > --- a/ethtool.c
> > +++ b/ethtool.c
> > @@ -1409,6 +1409,29 @@ static int dump_coalesce(const struct
> ethtool_coalesce *ecoal)
> >  	return 0;
> >  }
> >
> > +void dump_per_queue_coalesce(struct ethtool_per_queue_op
> *per_queue_opt,
> > +			     __u32 *queue_mask)
> > +{
> > +	char *addr;
> > +	int i;
> > +
> > +	addr = (char *)per_queue_opt + sizeof(*per_queue_opt);
> > +	for (i = 0; i < __KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32);
> i++) {
> > +		int queue = i * 32;
> > +		__u32 mask = queue_mask[i];
> > +
> > +		while (mask > 0) {
> > +			if (mask & 0x1) {
> > +				fprintf(stdout, "Queue: %d\n", queue);
> > +				dump_coalesce((struct ethtool_coalesce
> *)addr);
> > +				addr += sizeof(struct ethtool_coalesce);
> > +			}
> > +			mask = mask >> 1;
> > +			queue++;
> > +		}
> > +	}
> > +}
> > +
> >  struct feature_state {
> >  	u32 off_flags;
> >  	struct ethtool_gfeatures features;
> 
> The casts and pointer arithmetic are a bit complicated. How about this?
> 
> 	struct ethtool_coalesce *addr;
> ...
> 	addr = (struct ethtool_coalesce *)(per_queue_opt + 1); ...
> 	dump_coalesce(addr);
> 	addr++;
> 
> Also passing n_queue down from do_perqueue() would prevent having to
> parse the whole bitmap even if we already know the NIC has only few
> queues.

Okay, that does make the pointer arithmetic a little more straightforward, so I'll change this. I'll also add the n_queue parameter to set_per_queue_coalesce() and dump_per_queue_coalesce() so we can exit early once we've found all the queues.

> > @@ -5245,7 +5268,8 @@ static const struct option {
> >  	{ "--show-fec", 1, do_gfec, "Show FEC settings"},
> >  	{ "--set-fec", 1, do_sfec, "Set FEC settings",
> >  	  "		[ encoding auto|off|rs|baser [...]]\n"},
> > -	{ "--set-perqueue-command", 1, do_perqueue, "Set per queue
> command",
> > +	{ "--set-perqueue-command", 1, do_perqueue, "Set per queue
> command. "
> > +	  "The supported sub commands include --show-coalesce",
> >  	  "             [queue_mask %x] SUB_COMMAND\n"},
> >  	{ "-h|--help", 0, show_usage, "Show this help" },
> >  	{ "--version", 0, do_version, "Show version number" }, @@ -5350,8
> > +5374,32 @@ static int find_max_num_queues(struct cmd_context *ctx)
> >  		   echannels.combined_count);
> >  }
> >
> > +static struct ethtool_per_queue_op *
> > +get_per_queue_coalesce(struct cmd_context *ctx, __u32 *queue_mask,
> > +int n_queues) {
> > +	struct ethtool_per_queue_op *per_queue_opt;
> > +
> > +	per_queue_opt = malloc(sizeof(*per_queue_opt) + n_queues *
> > +			sizeof(struct ethtool_coalesce));
> > +	if (!per_queue_opt)
> > +		return NULL;
> > +
> > +	memcpy(per_queue_opt->queue_mask, queue_mask,
> > +	       __KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32) *
> sizeof(__u32));
> > +	per_queue_opt->cmd = ETHTOOL_PERQUEUE;
> > +	per_queue_opt->sub_command = ETHTOOL_GCOALESCE;
> > +	if (send_ioctl(ctx, per_queue_opt)) {
> > +		free(per_queue_opt);
> > +		perror("Cannot get device per queue parameters");
> > +		return NULL;
> > +	}
> > +
> > +	return per_queue_opt;
> > +}
> > +
> >  static int do_perqueue(struct cmd_context *ctx)  {
> > +	struct ethtool_per_queue_op *per_queue_opt;
> >  	__u32
> queue_mask[__KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32)] = {0};
> >  	int i, n_queues = 0;
> >
> > @@ -5390,7 +5438,19 @@ static int do_perqueue(struct cmd_context *ctx)
> >  	if (i < 0)
> >  		exit_bad_args();
> >
> > -	/* no sub_command support yet */
> > +	if (strstr(args[i].opts, "--show-coalesce") != NULL) {
> 
> Comparing args[i].func to do_gcoalesce might be easier.

This is the one comment where I think it's better to leave the code as it is. To me is seems more confusing to match on a function pointer that we're never going to call. Unless there are more objections I'd rather keep it the way it is.

Otherwise, thanks for the review and all the comments. I'll work on making these changes and get an updated version submitted.

Nick

> 
> > +		per_queue_opt = get_per_queue_coalesce(ctx,
> queue_mask,
> > +						       n_queues);
> > +		if (per_queue_opt == NULL) {
> > +			perror("Cannot get device per queue parameters");
> > +			return -EFAULT;
> > +		}
> > +		dump_per_queue_coalesce(per_queue_opt, queue_mask);
> > +		free(per_queue_opt);
> > +	} else {
> > +		perror("The subcommand is not supported yet");
> > +		return -EOPNOTSUPP;
> > +	}
> >
> >  	return 0;
> >  }
> 
> Michal Kubecek
Michal Kubecek Feb. 8, 2019, 7:10 a.m. UTC | #3
On Thu, Feb 07, 2019 at 11:53:40PM +0000, Nunley, Nicholas D wrote:
> > > @@ -5390,7 +5438,19 @@ static int do_perqueue(struct cmd_context *ctx)
> > >  	if (i < 0)
> > >  		exit_bad_args();
> > >
> > > -	/* no sub_command support yet */
> > > +	if (strstr(args[i].opts, "--show-coalesce") != NULL) {
> > 
> > Comparing args[i].func to do_gcoalesce might be easier.
> 
> This is the one comment where I think it's better to leave the code as it is.
> To me is seems more confusing to match on a function pointer that we're never
> going to call. Unless there are more objections I'd rather keep it the way it
> is.

No problem. This is not a code where performance is crucial. In theory,
you could get into trouble if someone introduces another command
(allowing per queue settings) with name like "--show-coalesce-foo" but
that's not very likely, IMHO.

Michal Kubecek
diff mbox series

Patch

diff --git a/ethtool.8.in b/ethtool.8.in
index 0aaca2c..71bb962 100644
--- a/ethtool.8.in
+++ b/ethtool.8.in
@@ -1153,7 +1153,7 @@  Sets the specific queues which the sub command is applied to.
 If queue_mask is not set, the sub command will be applied to all queues.
 .TP
 .B sub_command
-Sets the sub command.
+Sets the sub command. The supported sub commands include --show-coalesce.
 .RE
 .SH BUGS
 Not supported (in part or whole) on all network drivers.
diff --git a/ethtool.c b/ethtool.c
index 4dc725c..9a1b83b 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -1409,6 +1409,29 @@  static int dump_coalesce(const struct ethtool_coalesce *ecoal)
 	return 0;
 }
 
+void dump_per_queue_coalesce(struct ethtool_per_queue_op *per_queue_opt,
+			     __u32 *queue_mask)
+{
+	char *addr;
+	int i;
+
+	addr = (char *)per_queue_opt + sizeof(*per_queue_opt);
+	for (i = 0; i < __KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32); i++) {
+		int queue = i * 32;
+		__u32 mask = queue_mask[i];
+
+		while (mask > 0) {
+			if (mask & 0x1) {
+				fprintf(stdout, "Queue: %d\n", queue);
+				dump_coalesce((struct ethtool_coalesce *)addr);
+				addr += sizeof(struct ethtool_coalesce);
+			}
+			mask = mask >> 1;
+			queue++;
+		}
+	}
+}
+
 struct feature_state {
 	u32 off_flags;
 	struct ethtool_gfeatures features;
@@ -5245,7 +5268,8 @@  static const struct option {
 	{ "--show-fec", 1, do_gfec, "Show FEC settings"},
 	{ "--set-fec", 1, do_sfec, "Set FEC settings",
 	  "		[ encoding auto|off|rs|baser [...]]\n"},
-	{ "--set-perqueue-command", 1, do_perqueue, "Set per queue command",
+	{ "--set-perqueue-command", 1, do_perqueue, "Set per queue command. "
+	  "The supported sub commands include --show-coalesce",
 	  "             [queue_mask %x] SUB_COMMAND\n"},
 	{ "-h|--help", 0, show_usage, "Show this help" },
 	{ "--version", 0, do_version, "Show version number" },
@@ -5350,8 +5374,32 @@  static int find_max_num_queues(struct cmd_context *ctx)
 		   echannels.combined_count);
 }
 
+static struct ethtool_per_queue_op *
+get_per_queue_coalesce(struct cmd_context *ctx, __u32 *queue_mask, int n_queues)
+{
+	struct ethtool_per_queue_op *per_queue_opt;
+
+	per_queue_opt = malloc(sizeof(*per_queue_opt) + n_queues *
+			sizeof(struct ethtool_coalesce));
+	if (!per_queue_opt)
+		return NULL;
+
+	memcpy(per_queue_opt->queue_mask, queue_mask,
+	       __KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32) * sizeof(__u32));
+	per_queue_opt->cmd = ETHTOOL_PERQUEUE;
+	per_queue_opt->sub_command = ETHTOOL_GCOALESCE;
+	if (send_ioctl(ctx, per_queue_opt)) {
+		free(per_queue_opt);
+		perror("Cannot get device per queue parameters");
+		return NULL;
+	}
+
+	return per_queue_opt;
+}
+
 static int do_perqueue(struct cmd_context *ctx)
 {
+	struct ethtool_per_queue_op *per_queue_opt;
 	__u32 queue_mask[__KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32)] = {0};
 	int i, n_queues = 0;
 
@@ -5390,7 +5438,19 @@  static int do_perqueue(struct cmd_context *ctx)
 	if (i < 0)
 		exit_bad_args();
 
-	/* no sub_command support yet */
+	if (strstr(args[i].opts, "--show-coalesce") != NULL) {
+		per_queue_opt = get_per_queue_coalesce(ctx, queue_mask,
+						       n_queues);
+		if (per_queue_opt == NULL) {
+			perror("Cannot get device per queue parameters");
+			return -EFAULT;
+		}
+		dump_per_queue_coalesce(per_queue_opt, queue_mask);
+		free(per_queue_opt);
+	} else {
+		perror("The subcommand is not supported yet");
+		return -EOPNOTSUPP;
+	}
 
 	return 0;
 }