1 // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2 /* Statistics for Ocelot switch family 3 * 4 * Copyright (c) 2017 Microsemi Corporation 5 */ 6 #include <linux/spinlock.h> 7 #include <linux/mutex.h> 8 #include <linux/workqueue.h> 9 #include "ocelot.h" 10 11 /* Read the counters from hardware and keep them in region->buf. 12 * Caller must hold &ocelot->stat_view_lock. 13 */ 14 static int ocelot_port_update_stats(struct ocelot *ocelot, int port) 15 { 16 struct ocelot_stats_region *region; 17 int err; 18 19 /* Configure the port to read the stats from */ 20 ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port), SYS_STAT_CFG); 21 22 list_for_each_entry(region, &ocelot->stats_regions, node) { 23 err = ocelot_bulk_read(ocelot, region->base, region->buf, 24 region->count); 25 if (err) 26 return err; 27 } 28 29 return 0; 30 } 31 32 /* Transfer the counters from region->buf to ocelot->stats. 33 * Caller must hold &ocelot->stat_view_lock and &ocelot->stats_lock. 34 */ 35 static void ocelot_port_transfer_stats(struct ocelot *ocelot, int port) 36 { 37 unsigned int idx = port * OCELOT_NUM_STATS; 38 struct ocelot_stats_region *region; 39 int j; 40 41 list_for_each_entry(region, &ocelot->stats_regions, node) { 42 for (j = 0; j < region->count; j++) { 43 u64 *stat = &ocelot->stats[idx + j]; 44 u64 val = region->buf[j]; 45 46 if (val < (*stat & U32_MAX)) 47 *stat += (u64)1 << 32; 48 49 *stat = (*stat & ~(u64)U32_MAX) + val; 50 } 51 52 idx += region->count; 53 } 54 } 55 56 static void ocelot_check_stats_work(struct work_struct *work) 57 { 58 struct delayed_work *del_work = to_delayed_work(work); 59 struct ocelot *ocelot = container_of(del_work, struct ocelot, 60 stats_work); 61 int port, err; 62 63 mutex_lock(&ocelot->stat_view_lock); 64 65 for (port = 0; port < ocelot->num_phys_ports; port++) { 66 err = ocelot_port_update_stats(ocelot, port); 67 if (err) 68 break; 69 70 spin_lock(&ocelot->stats_lock); 71 ocelot_port_transfer_stats(ocelot, port); 72 spin_unlock(&ocelot->stats_lock); 73 } 74 75 if (!err && ocelot->ops->update_stats) 76 ocelot->ops->update_stats(ocelot); 77 78 mutex_unlock(&ocelot->stat_view_lock); 79 80 if (err) 81 dev_err(ocelot->dev, "Error %d updating ethtool stats\n", err); 82 83 queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work, 84 OCELOT_STATS_CHECK_DELAY); 85 } 86 87 void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data) 88 { 89 int i; 90 91 if (sset != ETH_SS_STATS) 92 return; 93 94 for (i = 0; i < OCELOT_NUM_STATS; i++) { 95 if (ocelot->stats_layout[i].name[0] == '\0') 96 continue; 97 98 memcpy(data + i * ETH_GSTRING_LEN, ocelot->stats_layout[i].name, 99 ETH_GSTRING_LEN); 100 } 101 } 102 EXPORT_SYMBOL(ocelot_get_strings); 103 104 void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data) 105 { 106 int i, err; 107 108 mutex_lock(&ocelot->stat_view_lock); 109 110 /* check and update now */ 111 err = ocelot_port_update_stats(ocelot, port); 112 113 spin_lock(&ocelot->stats_lock); 114 115 ocelot_port_transfer_stats(ocelot, port); 116 117 /* Copy all supported counters */ 118 for (i = 0; i < OCELOT_NUM_STATS; i++) { 119 int index = port * OCELOT_NUM_STATS + i; 120 121 if (ocelot->stats_layout[i].name[0] == '\0') 122 continue; 123 124 *data++ = ocelot->stats[index]; 125 } 126 127 spin_unlock(&ocelot->stats_lock); 128 129 mutex_unlock(&ocelot->stat_view_lock); 130 131 if (err) 132 dev_err(ocelot->dev, "Error %d updating ethtool stats\n", err); 133 } 134 EXPORT_SYMBOL(ocelot_get_ethtool_stats); 135 136 int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset) 137 { 138 int i, num_stats = 0; 139 140 if (sset != ETH_SS_STATS) 141 return -EOPNOTSUPP; 142 143 for (i = 0; i < OCELOT_NUM_STATS; i++) 144 if (ocelot->stats_layout[i].name[0] != '\0') 145 num_stats++; 146 147 return num_stats; 148 } 149 EXPORT_SYMBOL(ocelot_get_sset_count); 150 151 void ocelot_port_get_stats64(struct ocelot *ocelot, int port, 152 struct rtnl_link_stats64 *stats) 153 { 154 u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; 155 156 spin_lock(&ocelot->stats_lock); 157 158 /* Get Rx stats */ 159 stats->rx_bytes = s[OCELOT_STAT_RX_OCTETS]; 160 stats->rx_packets = s[OCELOT_STAT_RX_SHORTS] + 161 s[OCELOT_STAT_RX_FRAGMENTS] + 162 s[OCELOT_STAT_RX_JABBERS] + 163 s[OCELOT_STAT_RX_LONGS] + 164 s[OCELOT_STAT_RX_64] + 165 s[OCELOT_STAT_RX_65_127] + 166 s[OCELOT_STAT_RX_128_255] + 167 s[OCELOT_STAT_RX_256_511] + 168 s[OCELOT_STAT_RX_512_1023] + 169 s[OCELOT_STAT_RX_1024_1526] + 170 s[OCELOT_STAT_RX_1527_MAX]; 171 stats->multicast = s[OCELOT_STAT_RX_MULTICAST]; 172 stats->rx_missed_errors = s[OCELOT_STAT_DROP_TAIL]; 173 stats->rx_dropped = s[OCELOT_STAT_RX_RED_PRIO_0] + 174 s[OCELOT_STAT_RX_RED_PRIO_1] + 175 s[OCELOT_STAT_RX_RED_PRIO_2] + 176 s[OCELOT_STAT_RX_RED_PRIO_3] + 177 s[OCELOT_STAT_RX_RED_PRIO_4] + 178 s[OCELOT_STAT_RX_RED_PRIO_5] + 179 s[OCELOT_STAT_RX_RED_PRIO_6] + 180 s[OCELOT_STAT_RX_RED_PRIO_7] + 181 s[OCELOT_STAT_DROP_LOCAL] + 182 s[OCELOT_STAT_DROP_YELLOW_PRIO_0] + 183 s[OCELOT_STAT_DROP_YELLOW_PRIO_1] + 184 s[OCELOT_STAT_DROP_YELLOW_PRIO_2] + 185 s[OCELOT_STAT_DROP_YELLOW_PRIO_3] + 186 s[OCELOT_STAT_DROP_YELLOW_PRIO_4] + 187 s[OCELOT_STAT_DROP_YELLOW_PRIO_5] + 188 s[OCELOT_STAT_DROP_YELLOW_PRIO_6] + 189 s[OCELOT_STAT_DROP_YELLOW_PRIO_7] + 190 s[OCELOT_STAT_DROP_GREEN_PRIO_0] + 191 s[OCELOT_STAT_DROP_GREEN_PRIO_1] + 192 s[OCELOT_STAT_DROP_GREEN_PRIO_2] + 193 s[OCELOT_STAT_DROP_GREEN_PRIO_3] + 194 s[OCELOT_STAT_DROP_GREEN_PRIO_4] + 195 s[OCELOT_STAT_DROP_GREEN_PRIO_5] + 196 s[OCELOT_STAT_DROP_GREEN_PRIO_6] + 197 s[OCELOT_STAT_DROP_GREEN_PRIO_7]; 198 199 /* Get Tx stats */ 200 stats->tx_bytes = s[OCELOT_STAT_TX_OCTETS]; 201 stats->tx_packets = s[OCELOT_STAT_TX_64] + 202 s[OCELOT_STAT_TX_65_127] + 203 s[OCELOT_STAT_TX_128_255] + 204 s[OCELOT_STAT_TX_256_511] + 205 s[OCELOT_STAT_TX_512_1023] + 206 s[OCELOT_STAT_TX_1024_1526] + 207 s[OCELOT_STAT_TX_1527_MAX]; 208 stats->tx_dropped = s[OCELOT_STAT_TX_DROPS] + 209 s[OCELOT_STAT_TX_AGED]; 210 stats->collisions = s[OCELOT_STAT_TX_COLLISION]; 211 212 spin_unlock(&ocelot->stats_lock); 213 } 214 EXPORT_SYMBOL(ocelot_port_get_stats64); 215 216 static int ocelot_prepare_stats_regions(struct ocelot *ocelot) 217 { 218 struct ocelot_stats_region *region = NULL; 219 unsigned int last; 220 int i; 221 222 INIT_LIST_HEAD(&ocelot->stats_regions); 223 224 for (i = 0; i < OCELOT_NUM_STATS; i++) { 225 if (!ocelot->stats_layout[i].reg) 226 continue; 227 228 if (region && ocelot->stats_layout[i].reg == last + 4) { 229 region->count++; 230 } else { 231 region = devm_kzalloc(ocelot->dev, sizeof(*region), 232 GFP_KERNEL); 233 if (!region) 234 return -ENOMEM; 235 236 region->base = ocelot->stats_layout[i].reg; 237 region->count = 1; 238 list_add_tail(®ion->node, &ocelot->stats_regions); 239 } 240 241 last = ocelot->stats_layout[i].reg; 242 } 243 244 list_for_each_entry(region, &ocelot->stats_regions, node) { 245 region->buf = devm_kcalloc(ocelot->dev, region->count, 246 sizeof(*region->buf), GFP_KERNEL); 247 if (!region->buf) 248 return -ENOMEM; 249 } 250 251 return 0; 252 } 253 254 int ocelot_stats_init(struct ocelot *ocelot) 255 { 256 char queue_name[32]; 257 int ret; 258 259 ocelot->stats = devm_kcalloc(ocelot->dev, 260 ocelot->num_phys_ports * OCELOT_NUM_STATS, 261 sizeof(u64), GFP_KERNEL); 262 if (!ocelot->stats) 263 return -ENOMEM; 264 265 snprintf(queue_name, sizeof(queue_name), "%s-stats", 266 dev_name(ocelot->dev)); 267 ocelot->stats_queue = create_singlethread_workqueue(queue_name); 268 if (!ocelot->stats_queue) 269 return -ENOMEM; 270 271 spin_lock_init(&ocelot->stats_lock); 272 mutex_init(&ocelot->stat_view_lock); 273 274 ret = ocelot_prepare_stats_regions(ocelot); 275 if (ret) { 276 destroy_workqueue(ocelot->stats_queue); 277 return ret; 278 } 279 280 INIT_DELAYED_WORK(&ocelot->stats_work, ocelot_check_stats_work); 281 queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work, 282 OCELOT_STATS_CHECK_DELAY); 283 284 return 0; 285 } 286 287 void ocelot_stats_deinit(struct ocelot *ocelot) 288 { 289 cancel_delayed_work(&ocelot->stats_work); 290 destroy_workqueue(ocelot->stats_queue); 291 } 292