master.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. /*
  2. * Handling of a master device, switching frames via its switch fabric CPU port
  3. *
  4. * Copyright (c) 2017 Savoir-faire Linux Inc.
  5. * Vivien Didelot <vivien.didelot@savoirfairelinux.com>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. */
  12. #include "dsa_priv.h"
  13. static void dsa_master_get_ethtool_stats(struct net_device *dev,
  14. struct ethtool_stats *stats,
  15. uint64_t *data)
  16. {
  17. struct dsa_switch_tree *dst = dev->dsa_ptr;
  18. struct dsa_port *cpu_dp = dst->cpu_dp;
  19. const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops;
  20. struct dsa_switch *ds = cpu_dp->ds;
  21. int port = cpu_dp->index;
  22. int count = 0;
  23. if (ops && ops->get_sset_count && ops->get_ethtool_stats) {
  24. count = ops->get_sset_count(dev, ETH_SS_STATS);
  25. ops->get_ethtool_stats(dev, stats, data);
  26. }
  27. if (ds->ops->get_ethtool_stats)
  28. ds->ops->get_ethtool_stats(ds, port, data + count);
  29. }
  30. static int dsa_master_get_sset_count(struct net_device *dev, int sset)
  31. {
  32. struct dsa_switch_tree *dst = dev->dsa_ptr;
  33. struct dsa_port *cpu_dp = dst->cpu_dp;
  34. const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops;
  35. struct dsa_switch *ds = cpu_dp->ds;
  36. int count = 0;
  37. if (ops && ops->get_sset_count)
  38. count += ops->get_sset_count(dev, sset);
  39. if (sset == ETH_SS_STATS && ds->ops->get_sset_count)
  40. count += ds->ops->get_sset_count(ds);
  41. return count;
  42. }
  43. static void dsa_master_get_strings(struct net_device *dev, uint32_t stringset,
  44. uint8_t *data)
  45. {
  46. struct dsa_switch_tree *dst = dev->dsa_ptr;
  47. struct dsa_port *cpu_dp = dst->cpu_dp;
  48. const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops;
  49. struct dsa_switch *ds = cpu_dp->ds;
  50. int port = cpu_dp->index;
  51. int len = ETH_GSTRING_LEN;
  52. int mcount = 0, count;
  53. unsigned int i;
  54. uint8_t pfx[4];
  55. uint8_t *ndata;
  56. snprintf(pfx, sizeof(pfx), "p%.2d", port);
  57. /* We do not want to be NULL-terminated, since this is a prefix */
  58. pfx[sizeof(pfx) - 1] = '_';
  59. if (ops && ops->get_sset_count && ops->get_strings) {
  60. mcount = ops->get_sset_count(dev, ETH_SS_STATS);
  61. ops->get_strings(dev, stringset, data);
  62. }
  63. if (stringset == ETH_SS_STATS && ds->ops->get_strings) {
  64. ndata = data + mcount * len;
  65. /* This function copies ETH_GSTRINGS_LEN bytes, we will mangle
  66. * the output after to prepend our CPU port prefix we
  67. * constructed earlier
  68. */
  69. ds->ops->get_strings(ds, port, ndata);
  70. count = ds->ops->get_sset_count(ds);
  71. for (i = 0; i < count; i++) {
  72. memmove(ndata + (i * len + sizeof(pfx)),
  73. ndata + i * len, len - sizeof(pfx));
  74. memcpy(ndata + i * len, pfx, sizeof(pfx));
  75. }
  76. }
  77. }
  78. int dsa_master_ethtool_setup(struct net_device *dev)
  79. {
  80. struct dsa_switch_tree *dst = dev->dsa_ptr;
  81. struct dsa_port *cpu_dp = dst->cpu_dp;
  82. struct dsa_switch *ds = cpu_dp->ds;
  83. struct ethtool_ops *ops;
  84. ops = devm_kzalloc(ds->dev, sizeof(*ops), GFP_KERNEL);
  85. if (!ops)
  86. return -ENOMEM;
  87. cpu_dp->orig_ethtool_ops = dev->ethtool_ops;
  88. if (cpu_dp->orig_ethtool_ops)
  89. memcpy(ops, cpu_dp->orig_ethtool_ops, sizeof(*ops));
  90. ops->get_sset_count = dsa_master_get_sset_count;
  91. ops->get_ethtool_stats = dsa_master_get_ethtool_stats;
  92. ops->get_strings = dsa_master_get_strings;
  93. dev->ethtool_ops = ops;
  94. return 0;
  95. }
  96. void dsa_master_ethtool_restore(struct net_device *dev)
  97. {
  98. struct dsa_switch_tree *dst = dev->dsa_ptr;
  99. struct dsa_port *cpu_dp = dst->cpu_dp;
  100. dev->ethtool_ops = cpu_dp->orig_ethtool_ops;
  101. cpu_dp->orig_ethtool_ops = NULL;
  102. }