Introduce a new ioctl for setting per-queue parameters.
Users can apply commands to specific queues by setting SUB_COMMAND and
queue_mask with the following ethtool command:

 ethtool --set-perqueue-command DEVNAME [queue_mask %x] SUB_COMMAND

If queue_mask is not set, the SUB_COMMAND will be applied to all queues.

SUB_COMMANDs for per-queue settings will be implemented in following
patches.

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

Signed-off-by: Nicholas Nunley <nicholas.d.nun...@intel.com>
---
 ethtool.8.in |  20 ++++++++++
 ethtool.c    | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 121 insertions(+)

diff --git a/ethtool.8.in b/ethtool.8.in
index 5a26cff..0aaca2c 100644
--- a/ethtool.8.in
+++ b/ethtool.8.in
@@ -391,6 +391,14 @@ ethtool \- query or control network driver and hardware 
settings
 .I devname
 .B encoding
 .BR auto | off | rs | baser \ [...]
+.HP
+.B ethtool \-\-set\-perqueue\-command
+.I devname
+.RB [ queue_mask
+.IR %x ]
+.I sub_command
+.RB ...
+ .
 .
 .\" Adjust lines (i.e. full justification) and hyphenate.
 .ad
@@ -1135,6 +1143,18 @@ RS       Force RS-FEC encoding
 BaseR  Force BaseR encoding
 .TE
 .RE
+.TP
+.B \-\-set\-perqueue\-command
+Sets sub command to specific queues.
+.RS 4
+.TP
+.B queue_mask %x
+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.
+.RE
 .SH BUGS
 Not supported (in part or whole) on all network drivers.
 .SH AUTHOR
diff --git a/ethtool.c b/ethtool.c
index 1f91303..e089981 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -5048,6 +5048,8 @@ static int do_sfec(struct cmd_context *ctx)
        return 0;
 }
 
+static int do_perqueue(struct cmd_context *ctx);
+
 #ifndef TEST_ETHTOOL
 int send_ioctl(struct cmd_context *ctx, void *cmd)
 {
@@ -5243,6 +5245,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",
+         "             [queue_mask %x] SUB_COMMAND\n"},
        { "-h|--help", 0, show_usage, "Show this help" },
        { "--version", 0, do_version, "Show version number" },
        {}
@@ -5294,6 +5298,103 @@ static int find_option(int argc, char **argp)
        return -1;
 }
 
+static int set_queue_mask(u32 *queue_mask, char *str)
+{
+       int len = strlen(str);
+       int index = __KERNEL_DIV_ROUND_UP(len * 4, 32);
+       char tmp[9];
+       char *end = str + len;
+       int i, num;
+       __u32 mask;
+       int n_queues = 0;
+
+       if (len > MAX_NUM_QUEUE)
+               return -EINVAL;
+
+       for (i = 0; i < index; i++) {
+               num = end - str;
+
+               if (num >= 8) {
+                       end -= 8;
+                       num = 8;
+               } else {
+                       end = str;
+               }
+               strncpy(tmp, end, num);
+               tmp[num] = '\0';
+
+               queue_mask[i] = strtoul(tmp, NULL, 16);
+
+               mask = queue_mask[i];
+               while (mask > 0) {
+                       if (mask & 0x1)
+                               n_queues++;
+                       mask = mask >> 1;
+               }
+       }
+
+       return n_queues;
+}
+
+#define MAX(x, y) (x > y ? x : y)
+
+static int find_max_num_queues(struct cmd_context *ctx)
+{
+       struct ethtool_channels echannels;
+
+       echannels.cmd = ETHTOOL_GCHANNELS;
+       if (send_ioctl(ctx, &echannels))
+               return -1;
+
+       return MAX(MAX(echannels.rx_count, echannels.tx_count),
+                  echannels.combined_count);
+}
+
+static int do_perqueue(struct cmd_context *ctx)
+{
+       __u32 queue_mask[__KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32)] = {0};
+       int i, n_queues = 0;
+
+       if (ctx->argc == 0)
+               exit_bad_args();
+
+       /*
+        * The sub commands will be applied to
+        * all queues if no queue_mask set
+        */
+       if (strncmp(*ctx->argp, "queue_mask", 10)) {
+               n_queues = find_max_num_queues(ctx);
+               if (n_queues < 0) {
+                       perror("Cannot get number of queues");
+                       return -EFAULT;
+               }
+               for (i = 0; i < n_queues / 32; i++)
+                       queue_mask[i] = ~0;
+               queue_mask[i] = (1 << (n_queues - i * 32)) - 1;
+               fprintf(stdout,
+                       "The sub commands will be applied to all %d queues\n",
+                       n_queues);
+       } else {
+               ctx->argc--;
+               ctx->argp++;
+               n_queues = set_queue_mask(queue_mask, *ctx->argp);
+               if (n_queues < 0) {
+                       perror("Invalid queue mask");
+                       return n_queues;
+               }
+               ctx->argc--;
+               ctx->argp++;
+       }
+
+       i = find_option(ctx->argc, ctx->argp);
+       if (i < 0)
+               exit_bad_args();
+
+       /* no sub_command support yet */
+
+       return 0;
+}
+
 int main(int argc, char **argp)
 {
        int (*func)(struct cmd_context *);
-- 
2.20.1

Reply via email to