1f2f23566SVivien Didelot /* 2f2f23566SVivien Didelot * Handling of a master device, switching frames via its switch fabric CPU port 3f2f23566SVivien Didelot * 4f2f23566SVivien Didelot * Copyright (c) 2017 Savoir-faire Linux Inc. 5f2f23566SVivien Didelot * Vivien Didelot <vivien.didelot@savoirfairelinux.com> 6f2f23566SVivien Didelot * 7f2f23566SVivien Didelot * This program is free software; you can redistribute it and/or modify 8f2f23566SVivien Didelot * it under the terms of the GNU General Public License as published by 9f2f23566SVivien Didelot * the Free Software Foundation; either version 2 of the License, or 10f2f23566SVivien Didelot * (at your option) any later version. 11f2f23566SVivien Didelot */ 12f2f23566SVivien Didelot 13f2f23566SVivien Didelot #include "dsa_priv.h" 14f2f23566SVivien Didelot 15f2f23566SVivien Didelot static void dsa_master_get_ethtool_stats(struct net_device *dev, 16f2f23566SVivien Didelot struct ethtool_stats *stats, 17f2f23566SVivien Didelot uint64_t *data) 18f2f23566SVivien Didelot { 192f657a60SVivien Didelot struct dsa_port *cpu_dp = dev->dsa_ptr; 207ec764eeSVivien Didelot const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 217ec764eeSVivien Didelot struct dsa_switch *ds = cpu_dp->ds; 227ec764eeSVivien Didelot int port = cpu_dp->index; 23f2f23566SVivien Didelot int count = 0; 24f2f23566SVivien Didelot 25f2f23566SVivien Didelot if (ops && ops->get_sset_count && ops->get_ethtool_stats) { 26f2f23566SVivien Didelot count = ops->get_sset_count(dev, ETH_SS_STATS); 27f2f23566SVivien Didelot ops->get_ethtool_stats(dev, stats, data); 28f2f23566SVivien Didelot } 29f2f23566SVivien Didelot 30f2f23566SVivien Didelot if (ds->ops->get_ethtool_stats) 317ec764eeSVivien Didelot ds->ops->get_ethtool_stats(ds, port, data + count); 32f2f23566SVivien Didelot } 33f2f23566SVivien Didelot 34f2f23566SVivien Didelot static int dsa_master_get_sset_count(struct net_device *dev, int sset) 35f2f23566SVivien Didelot { 362f657a60SVivien Didelot struct dsa_port *cpu_dp = dev->dsa_ptr; 377ec764eeSVivien Didelot const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 387ec764eeSVivien Didelot struct dsa_switch *ds = cpu_dp->ds; 39f2f23566SVivien Didelot int count = 0; 40f2f23566SVivien Didelot 41f2f23566SVivien Didelot if (ops && ops->get_sset_count) 42f2f23566SVivien Didelot count += ops->get_sset_count(dev, sset); 43f2f23566SVivien Didelot 44f2f23566SVivien Didelot if (sset == ETH_SS_STATS && ds->ops->get_sset_count) 45f2f23566SVivien Didelot count += ds->ops->get_sset_count(ds); 46f2f23566SVivien Didelot 47f2f23566SVivien Didelot return count; 48f2f23566SVivien Didelot } 49f2f23566SVivien Didelot 50f2f23566SVivien Didelot static void dsa_master_get_strings(struct net_device *dev, uint32_t stringset, 51f2f23566SVivien Didelot uint8_t *data) 52f2f23566SVivien Didelot { 532f657a60SVivien Didelot struct dsa_port *cpu_dp = dev->dsa_ptr; 547ec764eeSVivien Didelot const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 557ec764eeSVivien Didelot struct dsa_switch *ds = cpu_dp->ds; 567ec764eeSVivien Didelot int port = cpu_dp->index; 57f2f23566SVivien Didelot int len = ETH_GSTRING_LEN; 58f2f23566SVivien Didelot int mcount = 0, count; 59f2f23566SVivien Didelot unsigned int i; 60f2f23566SVivien Didelot uint8_t pfx[4]; 61f2f23566SVivien Didelot uint8_t *ndata; 62f2f23566SVivien Didelot 637ec764eeSVivien Didelot snprintf(pfx, sizeof(pfx), "p%.2d", port); 64f2f23566SVivien Didelot /* We do not want to be NULL-terminated, since this is a prefix */ 65f2f23566SVivien Didelot pfx[sizeof(pfx) - 1] = '_'; 66f2f23566SVivien Didelot 67f2f23566SVivien Didelot if (ops && ops->get_sset_count && ops->get_strings) { 68f2f23566SVivien Didelot mcount = ops->get_sset_count(dev, ETH_SS_STATS); 69f2f23566SVivien Didelot ops->get_strings(dev, stringset, data); 70f2f23566SVivien Didelot } 71f2f23566SVivien Didelot 72f2f23566SVivien Didelot if (stringset == ETH_SS_STATS && ds->ops->get_strings) { 73f2f23566SVivien Didelot ndata = data + mcount * len; 74f2f23566SVivien Didelot /* This function copies ETH_GSTRINGS_LEN bytes, we will mangle 75f2f23566SVivien Didelot * the output after to prepend our CPU port prefix we 76f2f23566SVivien Didelot * constructed earlier 77f2f23566SVivien Didelot */ 787ec764eeSVivien Didelot ds->ops->get_strings(ds, port, ndata); 79f2f23566SVivien Didelot count = ds->ops->get_sset_count(ds); 80f2f23566SVivien Didelot for (i = 0; i < count; i++) { 81f2f23566SVivien Didelot memmove(ndata + (i * len + sizeof(pfx)), 82f2f23566SVivien Didelot ndata + i * len, len - sizeof(pfx)); 83f2f23566SVivien Didelot memcpy(ndata + i * len, pfx, sizeof(pfx)); 84f2f23566SVivien Didelot } 85f2f23566SVivien Didelot } 86f2f23566SVivien Didelot } 87f2f23566SVivien Didelot 88*17a22fcfSVivien Didelot static int dsa_master_ethtool_setup(struct net_device *dev) 89f2f23566SVivien Didelot { 902f657a60SVivien Didelot struct dsa_port *cpu_dp = dev->dsa_ptr; 917ec764eeSVivien Didelot struct dsa_switch *ds = cpu_dp->ds; 92f2f23566SVivien Didelot struct ethtool_ops *ops; 93f2f23566SVivien Didelot 94f2f23566SVivien Didelot ops = devm_kzalloc(ds->dev, sizeof(*ops), GFP_KERNEL); 95f2f23566SVivien Didelot if (!ops) 96f2f23566SVivien Didelot return -ENOMEM; 97f2f23566SVivien Didelot 987ec764eeSVivien Didelot cpu_dp->orig_ethtool_ops = dev->ethtool_ops; 997ec764eeSVivien Didelot if (cpu_dp->orig_ethtool_ops) 1007ec764eeSVivien Didelot memcpy(ops, cpu_dp->orig_ethtool_ops, sizeof(*ops)); 101f2f23566SVivien Didelot 102f2f23566SVivien Didelot ops->get_sset_count = dsa_master_get_sset_count; 103f2f23566SVivien Didelot ops->get_ethtool_stats = dsa_master_get_ethtool_stats; 104f2f23566SVivien Didelot ops->get_strings = dsa_master_get_strings; 105f2f23566SVivien Didelot 106f2f23566SVivien Didelot dev->ethtool_ops = ops; 107f2f23566SVivien Didelot 108f2f23566SVivien Didelot return 0; 109f2f23566SVivien Didelot } 110f2f23566SVivien Didelot 111*17a22fcfSVivien Didelot static void dsa_master_ethtool_teardown(struct net_device *dev) 112f2f23566SVivien Didelot { 1132f657a60SVivien Didelot struct dsa_port *cpu_dp = dev->dsa_ptr; 114f2f23566SVivien Didelot 1157ec764eeSVivien Didelot dev->ethtool_ops = cpu_dp->orig_ethtool_ops; 1167ec764eeSVivien Didelot cpu_dp->orig_ethtool_ops = NULL; 117f2f23566SVivien Didelot } 118*17a22fcfSVivien Didelot 119*17a22fcfSVivien Didelot int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp) 120*17a22fcfSVivien Didelot { 121*17a22fcfSVivien Didelot /* If we use a tagging format that doesn't have an ethertype 122*17a22fcfSVivien Didelot * field, make sure that all packets from this point on get 123*17a22fcfSVivien Didelot * sent to the tag format's receive function. 124*17a22fcfSVivien Didelot */ 125*17a22fcfSVivien Didelot wmb(); 126*17a22fcfSVivien Didelot 127*17a22fcfSVivien Didelot dev->dsa_ptr = cpu_dp; 128*17a22fcfSVivien Didelot 129*17a22fcfSVivien Didelot return dsa_master_ethtool_setup(dev); 130*17a22fcfSVivien Didelot } 131*17a22fcfSVivien Didelot 132*17a22fcfSVivien Didelot void dsa_master_teardown(struct net_device *dev) 133*17a22fcfSVivien Didelot { 134*17a22fcfSVivien Didelot dsa_master_ethtool_teardown(dev); 135*17a22fcfSVivien Didelot 136*17a22fcfSVivien Didelot dev->dsa_ptr = NULL; 137*17a22fcfSVivien Didelot 138*17a22fcfSVivien Didelot /* If we used a tagging format that doesn't have an ethertype 139*17a22fcfSVivien Didelot * field, make sure that all packets from this point get sent 140*17a22fcfSVivien Didelot * without the tag and go through the regular receive path. 141*17a22fcfSVivien Didelot */ 142*17a22fcfSVivien Didelot wmb(); 143*17a22fcfSVivien Didelot } 144