From: Govindarajulu Varadarajan <_gov...@gmx.com>

Add support for setting/getting driver's tx/rx_copybreak value.

Copybreak is handled through a new ethtool tunable interface.

The kernel support was added in 3.18, commit f0db9b07341 "ethtool:
Add generic options for tunables"

Signed-off-by: Govindarajulu Varadarajan <_gov...@gmx.com>
Signed-off-by: Hadar Hen Zion <had...@mellanox.com>
---
 ethtool-copy.h |   1 +
 ethtool.c      | 227 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 228 insertions(+)

diff --git a/ethtool-copy.h b/ethtool-copy.h
index d23ffc4..f92743b 100644
--- a/ethtool-copy.h
+++ b/ethtool-copy.h
@@ -545,6 +545,7 @@ enum ethtool_stringset {
        ETH_SS_NTUPLE_FILTERS,
        ETH_SS_FEATURES,
        ETH_SS_RSS_HASH_FUNCS,
+       ETH_SS_TUNABLES,
 };
 
 /**
diff --git a/ethtool.c b/ethtool.c
index 01b13a6..16b5c41 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -145,6 +145,12 @@ struct cmdline_info {
        void *seen_val;
 };
 
+struct ethtool_stunable {
+       cmdline_type_t type;
+       __u32 u32_val;
+       int seen_val;
+};
+
 struct flag_info {
        const char *name;
        u32 value;
@@ -1800,6 +1806,223 @@ static int do_gring(struct cmd_context *ctx)
        return 0;
 }
 
+static int get_u32tunable(struct cmd_context *ctx, enum tunable_id id,
+                         __u32 *value)
+{
+       struct ethtool_tunable *etuna;
+       int ret;
+
+       etuna = calloc(sizeof(*etuna) + sizeof(__u32), 1);
+       if (!etuna)
+               return 1;
+       etuna->cmd = ETHTOOL_GTUNABLE;
+       etuna->id = id;
+       etuna->type_id = ETHTOOL_TUNABLE_U32;
+       etuna->len = sizeof(__u32);
+       ret = send_ioctl(ctx, etuna);
+       *value = *(__u32 *)((void *)etuna + sizeof(*etuna));
+       free(etuna);
+
+       return ret;
+}
+
+static int print_u32tunable(int err, struct ethtool_gstrings *tunables,
+                           enum tunable_id id, const __u32 value)
+{
+       char *tunable_name = (char *)tunables->data + id * ETH_GSTRING_LEN;
+
+       if (err) {
+               switch (errno) {
+               /* Driver does not support this particular tunable
+                * Usually displays 0
+                */
+               case EINVAL:
+                       goto print;
+               /* Driver does not support get tunables ops or no such device
+                * No point in proceeding further
+                */
+               case EOPNOTSUPP:
+               case ENODEV:
+                       perror("Cannot get device settings");
+                       exit(err);
+               default:
+                       perror(tunable_name);
+                       return err;
+               }
+       }
+print:
+       fprintf(stdout, "%s: %u\n", tunable_name, value);
+
+       return 0;
+}
+
+static int do_gtunables(struct cmd_context *ctx)
+{
+       int err, anyerror = 0;
+       __u32 u32value = 0;
+       struct ethtool_gstrings *tunables;
+       int idx;
+       __u32 n_tunables;
+
+       if (ctx->argc != 0)
+               exit_bad_args();
+
+       tunables = get_stringset(ctx, ETH_SS_TUNABLES, 0, 1);
+       if (!tunables) {
+               perror("Cannot get tunables names");
+               return 1;
+       }
+       if (tunables->len == 0) {
+               fprintf(stderr, "No tunables defined\n");
+               return 1;
+       }
+       n_tunables = tunables->len;
+
+       fprintf(stdout, "Tunables settings for device %s\n", ctx->devname);
+
+       for (idx = 0; idx < n_tunables; idx++) {
+               switch(idx) {
+               case ETHTOOL_ID_UNSPEC:
+                       break;
+               case ETHTOOL_RX_COPYBREAK:
+               case ETHTOOL_TX_COPYBREAK:
+                       err = get_u32tunable(ctx, idx, &u32value);
+                       err = print_u32tunable(err, tunables, idx, u32value);
+                       if (err)
+                               anyerror = err;
+                       break;
+               default:
+                       anyerror = EINVAL;
+               }
+       }
+       if (anyerror)
+               fprintf(stderr, "Failed to get all settings. displayed partial 
settings\n");
+
+       free(tunables);
+       return anyerror;
+}
+
+static int set_u32tunable(struct cmd_context *ctx, enum tunable_id id,
+                         const __u32 value)
+{
+       struct ethtool_tunable *etuna;
+       int ret;
+       __u32 *data;
+
+       etuna = malloc(sizeof(*etuna) + sizeof(__u32));
+       if (!etuna) {
+               perror("Set tunable:");
+               return 1;
+       }
+       data = (void *)etuna + sizeof(*etuna);
+       *data = value;
+       etuna->cmd = ETHTOOL_STUNABLE;
+       etuna->id = id;
+       etuna->type_id = ETHTOOL_TUNABLE_U32;
+       etuna->len = sizeof(__u32);
+       ret = send_ioctl(ctx, etuna);
+       free(etuna);
+
+       return ret;
+}
+
+static int check_set_u32tunable(int err, enum tunable_id id)
+{
+       if (err) {
+               switch (errno) {
+               /* Driver does not support get tunables ops or no such device
+                * No point in proceeding further
+                */
+               case EOPNOTSUPP:
+               case ENODEV:
+                       perror("Cannot set device settings");
+                       exit(err);
+               default:
+                       perror("Check set tunable");
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+static int do_stunables(struct cmd_context *ctx)
+{
+       int err, anyerr = 0;
+       int tunable_changed = 0;
+       struct ethtool_gstrings *tunables;
+       struct cmdline_info *cmd_tunables = NULL;
+       struct ethtool_stunable *set_tunables = NULL;
+       int idx;
+       __u32 n_tunables;
+
+       tunables = get_stringset(ctx, ETH_SS_TUNABLES, 0, 1);
+       if (!tunables) {
+               perror("Cannot get tunables names");
+               return 1;
+       }
+       if (tunables->len == 0) {
+               fprintf(stderr, "No tunables defined\n");
+               return 1;
+       }
+       n_tunables = tunables->len;
+
+       cmd_tunables = calloc(n_tunables, sizeof(*cmd_tunables));
+       set_tunables = calloc(n_tunables, sizeof(*set_tunables));
+       if (!cmd_tunables || !set_tunables) {
+               anyerr = 1;
+               goto err;
+       }
+
+       for (idx = 0; idx < n_tunables; idx++) {
+               cmd_tunables[idx].name =
+                       (char *)tunables->data + idx * ETH_GSTRING_LEN;
+               cmd_tunables[idx].seen_val = &set_tunables[idx].seen_val;
+
+               switch(idx) {
+               case ETHTOOL_ID_UNSPEC:
+                       break;
+               case ETHTOOL_RX_COPYBREAK:
+               case ETHTOOL_TX_COPYBREAK:
+                       cmd_tunables[idx].wanted_val = 
&set_tunables[idx].u32_val;
+                       cmd_tunables[idx].type = CMDL_U32;
+                       break;
+               default:
+                       anyerr = EINVAL;
+                       goto err;
+               }
+       }
+
+       parse_generic_cmdline(ctx, &tunable_changed, cmd_tunables, n_tunables);
+
+       for (idx = 0; idx < n_tunables; idx++) {
+               if (set_tunables[idx].seen_val) {
+                       switch(idx) {
+                       case ETHTOOL_ID_UNSPEC:
+                               break;
+                       case ETHTOOL_RX_COPYBREAK:
+                       case ETHTOOL_TX_COPYBREAK:
+                               err = set_u32tunable(ctx, idx, 
set_tunables[idx].u32_val);
+                               err = check_set_u32tunable(err, idx);
+                               if (err)
+                                       anyerr = err;
+                               break;
+                       default:
+                               anyerr = EINVAL;
+                               goto err;
+                       }
+               }
+       }
+
+       if (anyerr)
+               fprintf(stderr, "Failed to set requested parameters\n");
+err:
+       free(tunables);
+       free(cmd_tunables);
+       free(set_tunables);
+       return anyerr;
+}
+
 static int do_schannels(struct cmd_context *ctx)
 {
        struct ethtool_channels echannels;
@@ -4050,6 +4273,10 @@ static const struct option {
          "             [ rx-mini N ]\n"
          "             [ rx-jumbo N ]\n"
          "             [ tx N ]\n" },
+       { "-b|--show-tunable", 1, do_gtunables, "Show tunable values" },
+       { "-B|--set-tunable", 1, do_stunables, "Set tunable values",
+         "             [ rx-copybreak N]\n"
+         "             [ tx-copybreak N]\n" },
        { "-k|--show-features|--show-offload", 1, do_gfeatures,
          "Get state of protocol offload and other features" },
        { "-K|--features|--offload", 1, do_sfeatures,
-- 
1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to