1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * tavor_stats.c 29 * Tavor IB Performance Statistics routines 30 * 31 * Implements all the routines necessary for setting up, querying, and 32 * (later) tearing down all the kstats necessary for implementing to 33 * the interfaces necessary to provide busstat(1M) access. 34 */ 35 36 #include <sys/types.h> 37 #include <sys/conf.h> 38 #include <sys/ddi.h> 39 #include <sys/sunddi.h> 40 #include <sys/modctl.h> 41 42 #include <sys/ib/adapters/tavor/tavor.h> 43 44 static kstat_t *tavor_kstat_picN_create(tavor_state_t *state, int num_pic, 45 int num_evt, tavor_ks_mask_t *ev_array); 46 static kstat_t *tavor_kstat_cntr_create(tavor_state_t *state, int num_pic, 47 int (*update)(kstat_t *, int)); 48 static int tavor_kstat_cntr_update(kstat_t *ksp, int rw); 49 50 /* 51 * Tavor IB Performance Events structure 52 * This structure is read-only and is used to setup the individual kstats 53 * and to initialize the tki_ib_perfcnt[] array for each Tavor instance. 54 */ 55 tavor_ks_mask_t tavor_ib_perfcnt_list[TAVOR_CNTR_NUMENTRIES] = { 56 {"port_xmit_data", TAVOR_HW_PMEG_PORTXMITDATA_OFFSET, 57 0, 0xFFFFFFFF, 0, 0}, 58 {"port_recv_data", TAVOR_HW_PMEG_PORTRECVDATA_OFFSET, 59 0, 0xFFFFFFFF, 0, 0}, 60 {"port_xmit_pkts", TAVOR_HW_PMEG_PORTXMITPKTS_OFFSET, 61 0, 0xFFFFFFFF, 0, 0}, 62 {"port_recv_pkts", TAVOR_HW_PMEG_PORTRECVPKTS_OFFSET, 63 0, 0xFFFFFFFF, 0, 0}, 64 {"port_recv_err", TAVOR_HW_PMEG_PORTRECVERR_OFFSET, 65 0, 0xFFFF, 0, 0}, 66 {"port_xmit_discards", TAVOR_HW_PMEG_PORTXMITDISCARD_OFFSET, 67 0, 0xFFFF, 0, 0}, 68 {"vl15_dropped", TAVOR_HW_PMEG_VL15DROPPED_OFFSET, 69 0, 0xFFFF, 0, 0}, 70 {"port_xmit_wait", TAVOR_HW_PMEG_PORTXMITWAIT_OFFSET, 71 0, 0xFFFFFFFF, 0, 0}, 72 {"port_recv_remote_phys_err", TAVOR_HW_PMEG_PORTRECVREMPHYSERR_OFFSET, 73 0, 0xFFFF, 0, 0}, 74 {"port_xmit_constraint_err", TAVOR_HW_PMEG_PORTXMITCONSTERR_OFFSET, 75 0, 0xFF, 0, 0}, 76 {"port_recv_constraint_err", TAVOR_HW_PMEG_PORTRECVCONSTERR_OFFSET, 77 0, 0xFF, 0, 0}, 78 {"symbol_err_counter", TAVOR_HW_PMEG_SYMBOLERRCNT_OFFSET, 79 0, 0xFFFF, 0, 0}, 80 {"link_err_recovery_cnt", TAVOR_HW_PMEG_LINKERRRECOVERCNT_OFFSET, 81 0, 0xFFFF, 0, 0}, 82 {"link_downed_cnt", TAVOR_HW_PMEG_LINKDOWNEDCNT_OFFSET, 83 16, 0xFFFF, 0, 0}, 84 {"excessive_buffer_overruns", TAVOR_HW_PMEG_EXCESSBUFOVERRUN_OFFSET, 85 0, 0xF, 0, 0}, 86 {"local_link_integrity_err", TAVOR_HW_PMEG_LOCALLINKINTERR_OFFSET, 87 8, 0xF, 0, 0}, 88 {"clear_pic", 0, 0, 0, 0} 89 }; 90 91 92 /* 93 * tavor_kstat_init() 94 * Context: Only called from attach() path context 95 */ 96 int 97 tavor_kstat_init(tavor_state_t *state) 98 { 99 tavor_ks_info_t *ksi; 100 uint_t numports; 101 int i; 102 103 TAVOR_TNF_ENTER(tavor_kstat_init); 104 105 /* Allocate a kstat info structure */ 106 ksi = (tavor_ks_info_t *)kmem_zalloc(sizeof (tavor_ks_info_t), 107 KM_SLEEP); 108 if (ksi == NULL) { 109 TNF_PROBE_0(tavor_kstat_init_kma_fail, TAVOR_TNF_ERROR, ""); 110 TAVOR_TNF_EXIT(tavor_kstat_init); 111 return (DDI_FAILURE); 112 } 113 state->ts_ks_info = ksi; 114 115 /* 116 * Create as many "pic" kstats as we have IB ports. Enable all 117 * of the events specified in the "tavor_ib_perfcnt_list" structure. 118 */ 119 numports = state->ts_cfg_profile->cp_num_ports; 120 for (i = 0; i < numports; i++) { 121 ksi->tki_picN_ksp[i] = tavor_kstat_picN_create(state, i, 122 TAVOR_CNTR_NUMENTRIES, tavor_ib_perfcnt_list); 123 if (ksi->tki_picN_ksp[i] == NULL) { 124 TNF_PROBE_0(tavor_kstat_init_picN_fail, 125 TAVOR_TNF_ERROR, ""); 126 goto kstat_init_fail; 127 } 128 } 129 130 /* Create the "counters" kstat too */ 131 ksi->tki_cntr_ksp = tavor_kstat_cntr_create(state, numports, 132 tavor_kstat_cntr_update); 133 if (ksi->tki_cntr_ksp == NULL) { 134 TNF_PROBE_0(tavor_kstat_init_cntr_fail, TAVOR_TNF_ERROR, ""); 135 goto kstat_init_fail; 136 } 137 138 /* Initialize the control register and initial counter values */ 139 ksi->tki_pcr = 0; 140 ksi->tki_pic0 = 0; 141 ksi->tki_pic1 = 0; 142 143 /* 144 * Initialize the Tavor tki_ib_perfcnt[] array values using the 145 * default values in tavor_ib_perfcnt_list[] 146 */ 147 for (i = 0; i < TAVOR_CNTR_NUMENTRIES; i++) { 148 ksi->tki_ib_perfcnt[i] = tavor_ib_perfcnt_list[i]; 149 } 150 151 TAVOR_TNF_EXIT(tavor_kstat_init); 152 return (DDI_SUCCESS); 153 154 155 kstat_init_fail: 156 157 /* Delete all the previously created kstats */ 158 if (ksi->tki_cntr_ksp != NULL) { 159 kstat_delete(ksi->tki_cntr_ksp); 160 } 161 for (i = 0; i < numports; i++) { 162 if (ksi->tki_picN_ksp[i] != NULL) { 163 kstat_delete(ksi->tki_picN_ksp[i]); 164 } 165 } 166 167 /* Free the kstat info structure */ 168 kmem_free(ksi, sizeof (tavor_ks_info_t)); 169 170 TAVOR_TNF_EXIT(tavor_kstat_init); 171 return (DDI_FAILURE); 172 } 173 174 175 /* 176 * tavor_kstat_init() 177 * Context: Only called from attach() and/or detach() path contexts 178 */ 179 void 180 tavor_kstat_fini(tavor_state_t *state) 181 { 182 tavor_ks_info_t *ksi; 183 uint_t numports; 184 int i; 185 186 TAVOR_TNF_ENTER(tavor_kstat_fini); 187 188 /* Get pointer to kstat info */ 189 ksi = state->ts_ks_info; 190 191 /* Delete all the "pic" kstats (one per port) */ 192 numports = state->ts_cfg_profile->cp_num_ports; 193 for (i = 0; i < numports; i++) { 194 if (ksi->tki_picN_ksp[i] != NULL) { 195 kstat_delete(ksi->tki_picN_ksp[i]); 196 } 197 } 198 199 /* Delete the "counter" kstats (one per port) */ 200 kstat_delete(ksi->tki_cntr_ksp); 201 202 /* Free the kstat info structure */ 203 kmem_free(ksi, sizeof (tavor_ks_info_t)); 204 205 TAVOR_TNF_EXIT(tavor_kstat_fini); 206 } 207 208 209 /* 210 * tavor_kstat_picN_create() 211 * Context: Only called from attach() path context 212 */ 213 static kstat_t * 214 tavor_kstat_picN_create(tavor_state_t *state, int num_pic, int num_evt, 215 tavor_ks_mask_t *ev_array) 216 { 217 kstat_t *picN_ksp; 218 struct kstat_named *pic_named_data; 219 int drv_instance, i; 220 char *drv_name; 221 char pic_name[16]; 222 223 TAVOR_TNF_ENTER(tavor_kstat_picN_create); 224 225 /* 226 * Create the "picN" kstat. In the steps, below we will attach 227 * all of our named event types to it. 228 */ 229 drv_name = (char *)ddi_driver_name(state->ts_dip); 230 drv_instance = ddi_get_instance(state->ts_dip); 231 (void) sprintf(pic_name, "pic%d", num_pic); 232 picN_ksp = kstat_create(drv_name, drv_instance, pic_name, "bus", 233 KSTAT_TYPE_NAMED, num_evt, NULL); 234 if (picN_ksp == NULL) { 235 TNF_PROBE_0(tavor_kstat_picN_create_kstat_fail, 236 TAVOR_TNF_ERROR, ""); 237 TAVOR_TNF_EXIT(tavor_kstat_picN_create); 238 return (NULL); 239 } 240 pic_named_data = (struct kstat_named *)(picN_ksp->ks_data); 241 242 /* 243 * Write event names and their associated pcr masks. The last entry 244 * in the array (clear_pic) is added separately below (as its pic 245 * value must be inverted). 246 */ 247 for (i = 0; i < num_evt - 1; i++) { 248 pic_named_data[i].value.ui64 = 249 ((uint64_t)i << (num_pic * TAVOR_CNTR_SIZE)); 250 kstat_named_init(&pic_named_data[i], ev_array[i].ks_evt_name, 251 KSTAT_DATA_UINT64); 252 } 253 254 /* Add the "clear_pic" entry */ 255 pic_named_data[i].value.ui64 = 256 ~((uint64_t)TAVOR_CNTR_MASK << (num_pic * TAVOR_CNTR_SIZE)); 257 kstat_named_init(&pic_named_data[i], ev_array[i].ks_evt_name, 258 KSTAT_DATA_UINT64); 259 260 /* Install the kstat */ 261 kstat_install(picN_ksp); 262 263 TAVOR_TNF_EXIT(tavor_kstat_picN_create); 264 return (picN_ksp); 265 } 266 267 268 /* 269 * tavor_kstat_cntr_create() 270 * Context: Only called from attach() path context 271 */ 272 static kstat_t * 273 tavor_kstat_cntr_create(tavor_state_t *state, int num_pic, 274 int (*update)(kstat_t *, int)) 275 { 276 struct kstat *cntr_ksp; 277 struct kstat_named *cntr_named_data; 278 int drv_instance, i; 279 char *drv_name; 280 char pic_str[16]; 281 282 TAVOR_TNF_ENTER(tavor_kstat_cntr_create); 283 284 /* 285 * Create the "counters" kstat. In the steps, below we will attach 286 * all of our "pic" to it. Note: The size of this kstat is 287 * num_pic + 1 because it also contains the "%pcr". 288 */ 289 drv_name = (char *)ddi_driver_name(state->ts_dip); 290 drv_instance = ddi_get_instance(state->ts_dip); 291 cntr_ksp = kstat_create(drv_name, drv_instance, "counters", "bus", 292 KSTAT_TYPE_NAMED, num_pic + 1, KSTAT_FLAG_WRITABLE); 293 if (cntr_ksp == NULL) { 294 TNF_PROBE_0(tavor_kstat_picN_create_kstat_fail, 295 TAVOR_TNF_ERROR, ""); 296 TAVOR_TNF_EXIT(tavor_kstat_cntr_create); 297 return (NULL); 298 } 299 cntr_named_data = (struct kstat_named *)(cntr_ksp->ks_data); 300 301 /* 302 * Initialize the named kstats (for the "pcr" and for the 303 * individual "pic" kstats) 304 */ 305 kstat_named_init(&cntr_named_data[0], "pcr", KSTAT_DATA_UINT64); 306 for (i = 0; i < num_pic; i++) { 307 (void) sprintf(pic_str, "pic%d", i); 308 kstat_named_init(&cntr_named_data[i+1], pic_str, 309 KSTAT_DATA_UINT64); 310 } 311 312 /* 313 * Store the Tavor softstate pointer in the kstat's private field so 314 * that it is available to the update function. 315 */ 316 cntr_ksp->ks_private = (void *)state; 317 cntr_ksp->ks_update = update; 318 319 /* Install the kstat */ 320 kstat_install(cntr_ksp); 321 322 TAVOR_TNF_ENTER(tavor_kstat_cntr_create); 323 return (cntr_ksp); 324 } 325 326 327 /* 328 * tavor_kstat_cntr_update() 329 * Context: Called from the kstat context 330 */ 331 static int 332 tavor_kstat_cntr_update(kstat_t *ksp, int rw) 333 { 334 tavor_state_t *state; 335 tavor_ks_mask_t *ib_perf; 336 tavor_ks_info_t *ksi; 337 struct kstat_named *data; 338 uint64_t offset, pcr; 339 uint32_t pic0, pic1, tmp; 340 uint32_t shift, mask, oldval; 341 uint_t numports, indx; 342 343 TAVOR_TNF_ENTER(tavor_kstat_cntr_update); 344 345 /* 346 * Extract the Tavor softstate pointer, kstat data, pointer to the 347 * kstat info structure, and pointer to the tki_ib_perfcnt[] array 348 * from the input parameters. Note: For warlock purposes, these 349 * parameters are all accessed only in this routine and are, 350 * therefore, protected by the lock used by the kstat framework. 351 */ 352 state = ksp->ks_private; 353 data = (struct kstat_named *)(ksp->ks_data); 354 ksi = state->ts_ks_info; 355 ib_perf = &ksi->tki_ib_perfcnt[0]; 356 _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*ksi)) 357 _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*data)) 358 _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*ib_perf)) 359 360 /* 361 * Depending on whether we are reading the "pic" counters or 362 * writing the "pcr" control register, we need to handle and 363 * fill in the kstat data appropriately. 364 * 365 * If this is a write to the "pcr", then extract the value from 366 * the kstat data and store it in the kstat info structure. 367 * 368 * Otherwise, if this is a read of the "pic" counter(s), then 369 * extract the register offset, size, and mask values from the 370 * ib_perf[] array. Then read the corresponding register and store 371 * it into the kstat data. Note: We only read/fill in pic1 if more 372 * than one port is configured. 373 */ 374 numports = state->ts_cfg_profile->cp_num_ports; 375 if (rw == KSTAT_WRITE) { 376 /* Update the stored "pcr" value */ 377 ksi->tki_pcr = data[0].value.ui64; 378 TAVOR_TNF_EXIT(tavor_kstat_cntr_update); 379 return (0); 380 } else { 381 /* 382 * Get the current "pcr" value and extract the lower 383 * portion (corresponding to the counters for "pic0") 384 */ 385 pcr = ksi->tki_pcr; 386 indx = pcr & TAVOR_CNTR_MASK; 387 data[0].value.ui64 = pcr; 388 389 /* 390 * Fill in the "pic0" counter, corresponding to port 1. 391 * This involves reading in the current value in the register 392 * and calculating how many events have happened since this 393 * register was last polled. Then we save away the current 394 * value for the counter and increment the "pic0" total by 395 * the number of new events. 396 */ 397 offset = ib_perf[indx].ks_reg_offset; 398 shift = ib_perf[indx].ks_reg_shift; 399 mask = ib_perf[indx].ks_reg_mask; 400 oldval = ib_perf[indx].ks_old_pic0; 401 402 pic0 = ddi_get32(state->ts_reg_cmdhdl, (uint32_t *) 403 (uintptr_t)((uintptr_t)state->ts_reg_cmd_baseaddr + 404 offset)); 405 tmp = ((pic0 >> shift) & mask); 406 407 ib_perf[indx].ks_old_pic0 = tmp; 408 409 tmp = tmp - oldval; 410 ksi->tki_pic0 += tmp; 411 data[1].value.ui64 = ksi->tki_pic0; 412 413 /* 414 * If necessary, fill in the "pic1" counter for port 2. 415 * This works the same as above except that we extract the 416 * upper bits (corresponding to the counters for "pic1") 417 */ 418 if (numports == TAVOR_NUM_PORTS) { 419 indx = pcr >> TAVOR_CNTR_SIZE; 420 offset = ib_perf[indx].ks_reg_offset; 421 shift = ib_perf[indx].ks_reg_shift; 422 mask = ib_perf[indx].ks_reg_mask; 423 oldval = ib_perf[indx].ks_old_pic1; 424 425 pic1 = ddi_get32(state->ts_reg_cmdhdl, (uint32_t *) 426 (uintptr_t)((uintptr_t)state->ts_reg_cmd_baseaddr + 427 offset + TAVOR_HW_PORT_SIZE)); 428 tmp = ((pic1 >> shift) & mask); 429 430 ib_perf[indx].ks_old_pic1 = tmp; 431 432 tmp = tmp - oldval; 433 ksi->tki_pic1 += tmp; 434 data[2].value.ui64 = ksi->tki_pic1; 435 } 436 437 TAVOR_TNF_EXIT(tavor_kstat_cntr_update); 438 return (0); 439 } 440 } 441