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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/systm.h> 29 #include <sys/types.h> 30 #include <sys/param.h> 31 #include <sys/thread.h> 32 #include <sys/cpuvar.h> 33 #include <sys/kmem.h> 34 #include <sys/cmn_err.h> 35 #include <sys/group.h> 36 #include <sys/pg.h> 37 #include <sys/pghw.h> 38 39 /* 40 * Processor Groups: Hardware sharing relationship layer 41 * 42 * This file implements an extension to Processor Groups to capture 43 * hardware sharing relationships existing between logical CPUs. Examples of 44 * hardware sharing relationships include shared caches on some CMT 45 * procesoor architectures, or shared local memory controllers on NUMA 46 * based system architectures. 47 * 48 * The pghw_t structure represents the extended PG. The first member 49 * of the structure is the generic pg_t with the pghw specific members 50 * following. The generic pg_t *must* remain the first member of the 51 * structure as the code uses casting of structure references to access 52 * the generic pg_t structure elements. 53 * 54 * In addition to the generic CPU grouping, physical PGs have a hardware 55 * sharing relationship enumerated "type", and an instance id. The enumerated 56 * type is defined by the pghw_type_t enumeration, while the instance id 57 * uniquely identifies the sharing instance from among others of the same 58 * hardware sharing type. 59 * 60 * The physical PGs are organized into an overall hierarchy, and are tracked 61 * in a number of different per CPU, and per pghw_type_t type groups. 62 * As an example: 63 * 64 * ------------- 65 * | pg_hw | 66 * | (group_t) | 67 * ------------- 68 * || ============================ 69 * ||\\-----------------------// \\ \\ 70 * || | hwset (PGC_HW_CHIP) | ------------- ------------- 71 * || | (group_t) | | pghw_t | | pghw_t | 72 * || ----------------------- | chip 0 | | chip 1 | 73 * || ------------- ------------- 74 * || \\ \\ \\ \\ \\ \\ \\ \\ 75 * || cpu cpu cpu cpu cpu cpu cpu cpu 76 * || 77 * || ============================ 78 * ||\\-----------------------// \\ \\ 79 * || | hwset (PGC_HW_IPIPE)| ------------- ------------- 80 * || | (group_t) | | pghw_t | | pghw_t | 81 * || ----------------------- | ipipe 0 | | ipipe 1 | 82 * || ------------- ------------- 83 * || \\ \\ \\ \\ 84 * || cpu cpu cpu cpu 85 * ... 86 * 87 * 88 * The top level pg_hw is a group of "hwset" groups. Each hwset holds of group 89 * of physical PGs of the same hardware sharing type. Within each hwset, the 90 * PG's instance id uniquely identifies the grouping relationshsip among other 91 * groupings of the same sharing type. The instance id for a grouping is 92 * platform defined, and in some cases may be used by platform code as a handle 93 * to search for a particular relationship instance. 94 * 95 * Each physical PG (by virtue of the embedded pg_t) contains a group of CPUs 96 * that participate in the sharing relationship. Each CPU also has associated 97 * with it a grouping tracking the PGs in which the CPU belongs. This can be 98 * used to iterate over the various relationships in which the CPU participates 99 * (the CPU's chip, cache, lgroup, etc.). 100 * 101 * The hwsets are created dynamically as new hardware sharing relationship types 102 * are instantiated. They are never destroyed, as once a given relathionship 103 * type appears in the system, it is quite likely that at least one instance of 104 * that relationship will always persist as long as the system is running. 105 */ 106 107 static group_t *pg_hw; /* top level pg hw group */ 108 109 /* 110 * Lookup table mapping hardware sharing relationships with hierarchy levels 111 */ 112 static int pghw_level_table[PGHW_NUM_COMPONENTS]; 113 114 /* 115 * Physical PG kstats 116 */ 117 struct pghw_kstat { 118 kstat_named_t pg_id; 119 kstat_named_t pg_class; 120 kstat_named_t pg_ncpus; 121 kstat_named_t pg_instance_id; 122 kstat_named_t pg_hw; 123 } pghw_kstat = { 124 { "id", KSTAT_DATA_UINT64 }, 125 { "pg_class", KSTAT_DATA_STRING }, 126 { "ncpus", KSTAT_DATA_UINT64 }, 127 { "instance_id", KSTAT_DATA_UINT64 }, 128 { "hardware", KSTAT_DATA_STRING }, 129 }; 130 131 kmutex_t pghw_kstat_lock; 132 133 /* 134 * hwset operations 135 */ 136 static group_t *pghw_set_create(pghw_type_t); 137 static void pghw_set_add(group_t *, pghw_t *); 138 static void pghw_set_remove(group_t *, pghw_t *); 139 140 /* 141 * Initialize the physical portion of a physical PG 142 */ 143 void 144 pghw_init(pghw_t *pg, cpu_t *cp, pghw_type_t hw) 145 { 146 group_t *hwset; 147 148 if ((hwset = pghw_set_lookup(hw)) == NULL) { 149 /* 150 * Haven't seen this hardware type yet 151 */ 152 hwset = pghw_set_create(hw); 153 } 154 155 pghw_set_add(hwset, pg); 156 pg->pghw_hw = hw; 157 pg->pghw_instance = 158 pg_plat_hw_instance_id(cp, hw); 159 pghw_kstat_create(pg); 160 } 161 162 /* 163 * Teardown the physical portion of a physical PG 164 */ 165 void 166 pghw_fini(pghw_t *pg) 167 { 168 group_t *hwset; 169 170 hwset = pghw_set_lookup(pg->pghw_hw); 171 ASSERT(hwset != NULL); 172 173 pghw_set_remove(hwset, pg); 174 pg->pghw_instance = (id_t)PGHW_INSTANCE_ANON; 175 pg->pghw_hw = (pghw_type_t)-1; 176 177 if (pg->pghw_kstat) 178 kstat_delete(pg->pghw_kstat); 179 } 180 181 /* 182 * Find an existing physical PG in which to place 183 * the given CPU for the specified hardware sharing 184 * relationship 185 */ 186 pghw_t * 187 pghw_place_cpu(cpu_t *cp, pghw_type_t hw) 188 { 189 group_t *hwset; 190 191 if ((hwset = pghw_set_lookup(hw)) == NULL) { 192 return (NULL); 193 } 194 195 return ((pghw_t *)pg_cpu_find_pg(cp, hwset)); 196 } 197 198 /* 199 * Find the pg representing the hw sharing relationship in which 200 * cp belongs 201 */ 202 pghw_t * 203 pghw_find_pg(cpu_t *cp, pghw_type_t hw) 204 { 205 group_iter_t i; 206 pghw_t *pg; 207 208 group_iter_init(&i); 209 while ((pg = group_iterate(&cp->cpu_pg->pgs, &i)) != NULL) { 210 if (pg->pghw_hw == hw) 211 return (pg); 212 } 213 return (NULL); 214 } 215 216 /* 217 * Find the PG of the given hardware sharing relationship 218 * type with the given instance id 219 */ 220 pghw_t * 221 pghw_find_by_instance(id_t id, pghw_type_t hw) 222 { 223 group_iter_t i; 224 group_t *set; 225 pghw_t *pg; 226 227 set = pghw_set_lookup(hw); 228 if (!set) 229 return (NULL); 230 231 group_iter_init(&i); 232 while ((pg = group_iterate(set, &i)) != NULL) { 233 if (pg->pghw_instance == id) 234 return (pg); 235 } 236 return (NULL); 237 } 238 239 /* 240 * CPUs physical ID cache creation / destruction 241 * The cache's elements are initialized to the CPU's id 242 */ 243 void 244 pghw_physid_create(cpu_t *cp) 245 { 246 int i; 247 248 cp->cpu_physid = kmem_alloc(sizeof (cpu_physid_t), KM_SLEEP); 249 250 for (i = 0; i < (sizeof (cpu_physid_t) / sizeof (id_t)); i++) { 251 ((id_t *)cp->cpu_physid)[i] = cp->cpu_id; 252 } 253 } 254 255 void 256 pghw_physid_destroy(cpu_t *cp) 257 { 258 if (cp->cpu_physid) { 259 kmem_free(cp->cpu_physid, sizeof (cpu_physid_t)); 260 cp->cpu_physid = NULL; 261 } 262 } 263 264 /* 265 * Return a sequential level identifier for the specified 266 * hardware sharing relationship 267 */ 268 int 269 pghw_level(pghw_type_t hw) 270 { 271 return (pg_plat_hw_level(hw)); 272 } 273 274 /* 275 * Create a new, empty hwset. 276 * This routine may block, and must not be called from any 277 * paused CPU context. 278 */ 279 static group_t * 280 pghw_set_create(pghw_type_t hw) 281 { 282 group_t *g; 283 int ret; 284 285 /* 286 * Create the top level PG hw group if it doesn't already exist 287 * This is a "set" of hardware sets, that is ordered (and indexed) 288 * by the pghw_type_t enum. 289 */ 290 if (pg_hw == NULL) { 291 pg_hw = kmem_alloc(sizeof (group_t), KM_SLEEP); 292 group_create(pg_hw); 293 group_expand(pg_hw, (uint_t)PGHW_NUM_COMPONENTS); 294 } 295 296 /* 297 * Create the new hwset 298 * Add it to the top level pg_hw group. 299 */ 300 g = kmem_alloc(sizeof (group_t), KM_SLEEP); 301 group_create(g); 302 303 ret = group_add_at(pg_hw, g, (uint_t)hw); 304 ASSERT(ret == 0); 305 306 /* 307 * Update the table that maps hardware sharing relationships 308 * to hierarchy levels 309 */ 310 ASSERT(pghw_level_table[hw] == NULL); 311 pghw_level_table[hw] = pg_plat_hw_level(hw); 312 313 return (g); 314 } 315 316 /* 317 * Find the hwset associated with the given hardware sharing type 318 */ 319 group_t * 320 pghw_set_lookup(pghw_type_t hw) 321 { 322 group_t *hwset; 323 324 if (pg_hw == NULL) 325 return (NULL); 326 327 hwset = GROUP_ACCESS(pg_hw, (uint_t)hw); 328 return (hwset); 329 } 330 331 /* 332 * Add a PG to a hwset 333 */ 334 static void 335 pghw_set_add(group_t *hwset, pghw_t *pg) 336 { 337 (void) group_add(hwset, pg, GRP_RESIZE); 338 } 339 340 /* 341 * Remove a PG from a hwset 342 */ 343 static void 344 pghw_set_remove(group_t *hwset, pghw_t *pg) 345 { 346 int result; 347 348 result = group_remove(hwset, pg, GRP_RESIZE); 349 ASSERT(result == 0); 350 } 351 352 353 /* 354 * Return a string name given a pg_hw sharing type 355 */ 356 #define PGHW_TYPE_NAME_MAX 8 357 358 static char * 359 pghw_type_string(pghw_type_t hw) 360 { 361 switch (hw) { 362 case PGHW_IPIPE: 363 return ("ipipe"); 364 case PGHW_CACHE: 365 return ("cache"); 366 case PGHW_FPU: 367 return ("fpu"); 368 case PGHW_MPIPE: 369 return ("mpipe"); 370 case PGHW_CHIP: 371 return ("chip"); 372 case PGHW_MEMORY: 373 return ("memory"); 374 default: 375 return ("unknown"); 376 } 377 } 378 379 /* 380 * Create / Update routines for PG hw kstats 381 * 382 * It is the intention of these kstats to provide some level 383 * of informational / debugging observability into the types 384 * and nature of the system's detected hardware sharing relationships 385 */ 386 void 387 pghw_kstat_create(pghw_t *pg) 388 { 389 /* 390 * Create a physical pg kstat 391 */ 392 if ((pg->pghw_kstat = kstat_create("pg", ((pg_t *)pg)->pg_id, 393 "pg", "pg", KSTAT_TYPE_NAMED, 394 sizeof (pghw_kstat) / sizeof (kstat_named_t), 395 KSTAT_FLAG_VIRTUAL)) != NULL) { 396 pg->pghw_kstat->ks_data_size += PG_CLASS_NAME_MAX; 397 pg->pghw_kstat->ks_data_size += PGHW_TYPE_NAME_MAX; 398 pg->pghw_kstat->ks_lock = &pghw_kstat_lock; 399 pg->pghw_kstat->ks_data = &pghw_kstat; 400 pg->pghw_kstat->ks_update = pghw_kstat_update; 401 pg->pghw_kstat->ks_private = pg; 402 kstat_install(pg->pghw_kstat); 403 } 404 } 405 406 int 407 pghw_kstat_update(kstat_t *ksp, int rw) 408 { 409 struct pghw_kstat *pgsp = &pghw_kstat; 410 pghw_t *pg = ksp->ks_private; 411 412 if (rw == KSTAT_WRITE) 413 return (EACCES); 414 415 pgsp->pg_id.value.ui64 = ((pg_t *)pg)->pg_id; 416 pgsp->pg_ncpus.value.ui64 = GROUP_SIZE(&((pg_t *)pg)->pg_cpus); 417 pgsp->pg_instance_id.value.ui64 = (uint64_t)pg->pghw_instance; 418 kstat_named_setstr(&pgsp->pg_class, ((pg_t *)pg)->pg_class->pgc_name); 419 kstat_named_setstr(&pgsp->pg_hw, pghw_type_string(pg->pghw_hw)); 420 421 return (0); 422 } 423