1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2018, Matthew Macy 5 * Copyright (c) 2021, The FreeBSD Foundation 6 * 7 * Portions of this software were developed by Mitchell Horne 8 * under sponsorship from the FreeBSD Foundation. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 * $FreeBSD$ 32 * 33 */ 34 35 #include <sys/types.h> 36 #include <sys/errno.h> 37 #include <sys/pmc.h> 38 #include <sys/sysctl.h> 39 #include <stddef.h> 40 #include <stdlib.h> 41 #include <limits.h> 42 #include <regex.h> 43 #include <string.h> 44 #include <pmc.h> 45 #include <pmclog.h> 46 #include <assert.h> 47 #include <libpmcstat.h> 48 #include "pmu-events/pmu-events.h" 49 50 struct pmu_alias { 51 const char *pa_alias; 52 const char *pa_name; 53 }; 54 55 #if defined(__amd64__) || defined(__i386__) 56 typedef enum { 57 PMU_INVALID, 58 PMU_INTEL, 59 PMU_AMD, 60 } pmu_mfr_t; 61 62 static struct pmu_alias pmu_intel_alias_table[] = { 63 {"UNHALTED_CORE_CYCLES", "CPU_CLK_UNHALTED.THREAD_P_ANY"}, 64 {"UNHALTED-CORE-CYCLES", "CPU_CLK_UNHALTED.THREAD_P_ANY"}, 65 {"LLC_MISSES", "LONGEST_LAT_CACHE.MISS"}, 66 {"LLC-MISSES", "LONGEST_LAT_CACHE.MISS"}, 67 {"LLC_REFERENCE", "LONGEST_LAT_CACHE.REFERENCE"}, 68 {"LLC-REFERENCE", "LONGEST_LAT_CACHE.REFERENCE"}, 69 {"LLC_MISS_RHITM", "mem_load_l3_miss_retired.remote_hitm"}, 70 {"LLC-MISS-RHITM", "mem_load_l3_miss_retired.remote_hitm"}, 71 {"RESOURCE_STALL", "RESOURCE_STALLS.ANY"}, 72 {"RESOURCE_STALLS_ANY", "RESOURCE_STALLS.ANY"}, 73 {"BRANCH_INSTRUCTION_RETIRED", "BR_INST_RETIRED.ALL_BRANCHES"}, 74 {"BRANCH-INSTRUCTION-RETIRED", "BR_INST_RETIRED.ALL_BRANCHES"}, 75 {"BRANCH_MISSES_RETIRED", "BR_MISP_RETIRED.ALL_BRANCHES"}, 76 {"BRANCH-MISSES-RETIRED", "BR_MISP_RETIRED.ALL_BRANCHES"}, 77 {"cycles", "tsc-tsc"}, 78 {"unhalted-cycles", "CPU_CLK_UNHALTED.THREAD_P_ANY"}, 79 {"instructions", "inst_retired.any_p"}, 80 {"branch-mispredicts", "br_misp_retired.all_branches"}, 81 {"branches", "br_inst_retired.all_branches"}, 82 {"interrupts", "hw_interrupts.received"}, 83 {"ic-misses", "frontend_retired.l1i_miss"}, 84 {NULL, NULL}, 85 }; 86 87 static struct pmu_alias pmu_amd_alias_table[] = { 88 {"UNHALTED_CORE_CYCLES", "ls_not_halted_cyc"}, 89 {"UNHALTED-CORE-CYCLES", "ls_not_halted_cyc"}, 90 {NULL, NULL}, 91 }; 92 93 94 static pmu_mfr_t 95 pmu_events_mfr(void) 96 { 97 char buf[PMC_CPUID_LEN]; 98 size_t s = sizeof(buf); 99 pmu_mfr_t mfr; 100 101 if (sysctlbyname("kern.hwpmc.cpuid", buf, &s, 102 (void *)NULL, 0) == -1) 103 return (PMU_INVALID); 104 if (strcasestr(buf, "AuthenticAMD") != NULL || 105 strcasestr(buf, "HygonGenuine") != NULL) 106 mfr = PMU_AMD; 107 else if (strcasestr(buf, "GenuineIntel") != NULL) 108 mfr = PMU_INTEL; 109 else 110 mfr = PMU_INVALID; 111 return (mfr); 112 } 113 114 /* 115 * The Intel fixed mode counters are: 116 * "inst_retired.any", 117 * "cpu_clk_unhalted.thread", 118 * "cpu_clk_unhalted.thread_any", 119 * "cpu_clk_unhalted.ref_tsc", 120 * 121 */ 122 123 static const char * 124 pmu_alias_get(const char *name) 125 { 126 pmu_mfr_t mfr; 127 struct pmu_alias *pa; 128 struct pmu_alias *pmu_alias_table; 129 130 if ((mfr = pmu_events_mfr()) == PMU_INVALID) 131 return (name); 132 if (mfr == PMU_AMD) 133 pmu_alias_table = pmu_amd_alias_table; 134 else if (mfr == PMU_INTEL) 135 pmu_alias_table = pmu_intel_alias_table; 136 else 137 return (name); 138 139 for (pa = pmu_alias_table; pa->pa_alias != NULL; pa++) 140 if (strcasecmp(name, pa->pa_alias) == 0) 141 return (pa->pa_name); 142 143 return (name); 144 } 145 146 #elif defined(__aarch64__) 147 148 static struct pmu_alias pmu_armv8_alias_table[] = { 149 {NULL, NULL}, 150 }; 151 152 static const char * 153 pmu_alias_get(const char *name) 154 { 155 struct pmu_alias *pa; 156 157 for (pa = pmu_armv8_alias_table; pa->pa_alias != NULL; pa++) 158 if (strcasecmp(name, pa->pa_alias) == 0) 159 return (pa->pa_name); 160 161 return (name); 162 } 163 164 #else 165 166 static const char * 167 pmu_alias_get(const char *name) 168 { 169 170 return (name); 171 } 172 #endif 173 174 struct pmu_event_desc { 175 uint64_t ped_period; 176 uint64_t ped_offcore_rsp; 177 uint64_t ped_l3_thread; 178 uint64_t ped_l3_slice; 179 uint32_t ped_event; 180 uint32_t ped_frontend; 181 uint32_t ped_ldlat; 182 uint32_t ped_config1; 183 int16_t ped_umask; 184 uint8_t ped_cmask; 185 uint8_t ped_any; 186 uint8_t ped_inv; 187 uint8_t ped_edge; 188 uint8_t ped_fc_mask; 189 uint8_t ped_ch_mask; 190 }; 191 192 static const struct pmu_events_map * 193 pmu_events_map_get(const char *cpuid) 194 { 195 regex_t re; 196 regmatch_t pmatch[1]; 197 char buf[PMC_CPUID_LEN]; 198 size_t s = sizeof(buf); 199 int match; 200 const struct pmu_events_map *pme; 201 202 if (cpuid != NULL) { 203 strlcpy(buf, cpuid, s); 204 } else { 205 if (sysctlbyname("kern.hwpmc.cpuid", buf, &s, 206 (void *)NULL, 0) == -1) 207 return (NULL); 208 } 209 for (pme = pmu_events_map; pme->cpuid != NULL; pme++) { 210 if (regcomp(&re, pme->cpuid, REG_EXTENDED) != 0) { 211 printf("regex '%s' failed to compile, ignoring\n", 212 pme->cpuid); 213 continue; 214 } 215 match = regexec(&re, buf, 1, pmatch, 0); 216 regfree(&re); 217 if (match == 0) { 218 if (pmatch[0].rm_so == 0 && (buf[pmatch[0].rm_eo] == 0 219 || buf[pmatch[0].rm_eo] == '-')) 220 return (pme); 221 } 222 } 223 return (NULL); 224 } 225 226 static const struct pmu_event * 227 pmu_event_get(const char *cpuid, const char *event_name, int *idx) 228 { 229 const struct pmu_events_map *pme; 230 const struct pmu_event *pe; 231 int i; 232 233 if ((pme = pmu_events_map_get(cpuid)) == NULL) 234 return (NULL); 235 for (i = 0, pe = pme->table; pe->name || pe->desc || pe->event; pe++, i++) { 236 if (pe->name == NULL) 237 continue; 238 if (strcasecmp(pe->name, event_name) == 0) { 239 if (idx) 240 *idx = i; 241 return (pe); 242 } 243 } 244 return (NULL); 245 } 246 247 int 248 pmc_pmu_idx_get_by_event(const char *cpuid, const char *event) 249 { 250 int idx; 251 const char *realname; 252 253 realname = pmu_alias_get(event); 254 if (pmu_event_get(cpuid, realname, &idx) == NULL) 255 return (-1); 256 return (idx); 257 } 258 259 const char * 260 pmc_pmu_event_get_by_idx(const char *cpuid, int idx) 261 { 262 const struct pmu_events_map *pme; 263 264 if ((pme = pmu_events_map_get(cpuid)) == NULL) 265 return (NULL); 266 assert(pme->table[idx].name); 267 return (pme->table[idx].name); 268 } 269 270 static int 271 pmu_parse_event(struct pmu_event_desc *ped, const char *eventin) 272 { 273 char *event; 274 char *kvp, *key, *value, *r; 275 char *debug; 276 277 if ((event = strdup(eventin)) == NULL) 278 return (ENOMEM); 279 r = event; 280 bzero(ped, sizeof(*ped)); 281 ped->ped_period = DEFAULT_SAMPLE_COUNT; 282 ped->ped_umask = -1; 283 while ((kvp = strsep(&event, ",")) != NULL) { 284 key = strsep(&kvp, "="); 285 if (key == NULL) 286 abort(); 287 value = kvp; 288 if (strcmp(key, "umask") == 0) 289 ped->ped_umask = strtol(value, NULL, 16); 290 else if (strcmp(key, "event") == 0) 291 ped->ped_event = strtol(value, NULL, 16); 292 else if (strcmp(key, "period") == 0) 293 ped->ped_period = strtol(value, NULL, 10); 294 else if (strcmp(key, "offcore_rsp") == 0) 295 ped->ped_offcore_rsp = strtol(value, NULL, 16); 296 else if (strcmp(key, "any") == 0) 297 ped->ped_any = strtol(value, NULL, 10); 298 else if (strcmp(key, "cmask") == 0) 299 ped->ped_cmask = strtol(value, NULL, 10); 300 else if (strcmp(key, "inv") == 0) 301 ped->ped_inv = strtol(value, NULL, 10); 302 else if (strcmp(key, "edge") == 0) 303 ped->ped_edge = strtol(value, NULL, 10); 304 else if (strcmp(key, "frontend") == 0) 305 ped->ped_frontend = strtol(value, NULL, 16); 306 else if (strcmp(key, "ldlat") == 0) 307 ped->ped_ldlat = strtol(value, NULL, 16); 308 else if (strcmp(key, "fc_mask") == 0) 309 ped->ped_fc_mask = strtol(value, NULL, 16); 310 else if (strcmp(key, "ch_mask") == 0) 311 ped->ped_ch_mask = strtol(value, NULL, 16); 312 else if (strcmp(key, "config1") == 0) 313 ped->ped_config1 = strtol(value, NULL, 16); 314 else if (strcmp(key, "l3_thread_mask") == 0) 315 ped->ped_l3_thread = strtol(value, NULL, 16); 316 else if (strcmp(key, "l3_slice_mask") == 0) 317 ped->ped_l3_slice = strtol(value, NULL, 16); 318 else { 319 debug = getenv("PMUDEBUG"); 320 if (debug != NULL && strcmp(debug, "true") == 0 && value != NULL) 321 printf("unrecognized kvpair: %s:%s\n", key, value); 322 } 323 } 324 free(r); 325 return (0); 326 } 327 328 uint64_t 329 pmc_pmu_sample_rate_get(const char *event_name) 330 { 331 const struct pmu_event *pe; 332 struct pmu_event_desc ped; 333 334 event_name = pmu_alias_get(event_name); 335 if ((pe = pmu_event_get(NULL, event_name, NULL)) == NULL) 336 return (DEFAULT_SAMPLE_COUNT); 337 if (pe->event == NULL) 338 return (DEFAULT_SAMPLE_COUNT); 339 if (pmu_parse_event(&ped, pe->event)) 340 return (DEFAULT_SAMPLE_COUNT); 341 return (ped.ped_period); 342 } 343 344 int 345 pmc_pmu_enabled(void) 346 { 347 348 return (pmu_events_map_get(NULL) != NULL); 349 } 350 351 void 352 pmc_pmu_print_counters(const char *event_name) 353 { 354 const struct pmu_events_map *pme; 355 const struct pmu_event *pe; 356 struct pmu_event_desc ped; 357 char *debug; 358 int do_debug; 359 360 debug = getenv("PMUDEBUG"); 361 do_debug = 0; 362 363 if (debug != NULL && strcmp(debug, "true") == 0) 364 do_debug = 1; 365 if ((pme = pmu_events_map_get(NULL)) == NULL) 366 return; 367 for (pe = pme->table; pe->name || pe->desc || pe->event; pe++) { 368 if (pe->name == NULL) 369 continue; 370 if (event_name != NULL && strcasestr(pe->name, event_name) == NULL) 371 continue; 372 printf("\t%s\n", pe->name); 373 if (do_debug) 374 pmu_parse_event(&ped, pe->event); 375 } 376 } 377 378 void 379 pmc_pmu_print_counter_desc(const char *ev) 380 { 381 const struct pmu_events_map *pme; 382 const struct pmu_event *pe; 383 384 if ((pme = pmu_events_map_get(NULL)) == NULL) 385 return; 386 for (pe = pme->table; pe->name || pe->desc || pe->event; pe++) { 387 if (pe->name == NULL) 388 continue; 389 if (strcasestr(pe->name, ev) != NULL && 390 pe->desc != NULL) 391 printf("%s:\t%s\n", pe->name, pe->desc); 392 } 393 } 394 395 void 396 pmc_pmu_print_counter_desc_long(const char *ev) 397 { 398 const struct pmu_events_map *pme; 399 const struct pmu_event *pe; 400 401 if ((pme = pmu_events_map_get(NULL)) == NULL) 402 return; 403 for (pe = pme->table; pe->name || pe->desc || pe->event; pe++) { 404 if (pe->name == NULL) 405 continue; 406 if (strcasestr(pe->name, ev) != NULL) { 407 if (pe->long_desc != NULL) 408 printf("%s:\n%s\n", pe->name, pe->long_desc); 409 else if (pe->desc != NULL) 410 printf("%s:\t%s\n", pe->name, pe->desc); 411 } 412 } 413 } 414 415 void 416 pmc_pmu_print_counter_full(const char *ev) 417 { 418 const struct pmu_events_map *pme; 419 const struct pmu_event *pe; 420 421 if ((pme = pmu_events_map_get(NULL)) == NULL) 422 return; 423 for (pe = pme->table; pe->name || pe->desc || pe->event; pe++) { 424 if (pe->name == NULL) 425 continue; 426 if (strcasestr(pe->name, ev) == NULL) 427 continue; 428 printf("name: %s\n", pe->name); 429 if (pe->long_desc != NULL) 430 printf("desc: %s\n", pe->long_desc); 431 else if (pe->desc != NULL) 432 printf("desc: %s\n", pe->desc); 433 if (pe->event != NULL) 434 printf("event: %s\n", pe->event); 435 if (pe->topic != NULL) 436 printf("topic: %s\n", pe->topic); 437 if (pe->pmu != NULL) 438 printf("pmu: %s\n", pe->pmu); 439 if (pe->unit != NULL) 440 printf("unit: %s\n", pe->unit); 441 if (pe->perpkg != NULL) 442 printf("perpkg: %s\n", pe->perpkg); 443 if (pe->metric_expr != NULL) 444 printf("metric_expr: %s\n", pe->metric_expr); 445 if (pe->metric_name != NULL) 446 printf("metric_name: %s\n", pe->metric_name); 447 if (pe->metric_group != NULL) 448 printf("metric_group: %s\n", pe->metric_group); 449 } 450 } 451 452 #if defined(__amd64__) || defined(__i386__) 453 static int 454 pmc_pmu_amd_pmcallocate(const char *event_name, struct pmc_op_pmcallocate *pm, 455 struct pmu_event_desc *ped) 456 { 457 struct pmc_md_amd_op_pmcallocate *amd; 458 const struct pmu_event *pe; 459 int idx = -1; 460 461 amd = &pm->pm_md.pm_amd; 462 if (ped->ped_umask > 0) { 463 pm->pm_caps |= PMC_CAP_QUALIFIER; 464 amd->pm_amd_config |= AMD_PMC_TO_UNITMASK(ped->ped_umask); 465 } 466 pm->pm_class = PMC_CLASS_K8; 467 pe = pmu_event_get(NULL, event_name, &idx); 468 469 if (strcmp("l3cache", pe->topic) == 0){ 470 amd->pm_amd_config |= AMD_PMC_TO_EVENTMASK(ped->ped_event); 471 amd->pm_amd_sub_class = PMC_AMD_SUB_CLASS_L3_CACHE; 472 amd->pm_amd_config |= AMD_PMC_TO_L3SLICE(ped->ped_l3_slice); 473 amd->pm_amd_config |= AMD_PMC_TO_L3CORE(ped->ped_l3_thread); 474 } 475 else if (strcmp("data fabric", pe->topic) == 0){ 476 477 amd->pm_amd_config |= AMD_PMC_TO_EVENTMASK_DF(ped->ped_event); 478 amd->pm_amd_sub_class = PMC_AMD_SUB_CLASS_DATA_FABRIC; 479 } 480 else{ 481 amd->pm_amd_config |= AMD_PMC_TO_EVENTMASK(ped->ped_event); 482 amd->pm_amd_sub_class = PMC_AMD_SUB_CLASS_CORE; 483 if ((pm->pm_caps & (PMC_CAP_USER|PMC_CAP_SYSTEM)) == 0 || 484 (pm->pm_caps & (PMC_CAP_USER|PMC_CAP_SYSTEM)) == 485 (PMC_CAP_USER|PMC_CAP_SYSTEM)) 486 amd->pm_amd_config |= (AMD_PMC_USR | AMD_PMC_OS); 487 else if (pm->pm_caps & PMC_CAP_USER) 488 amd->pm_amd_config |= AMD_PMC_USR; 489 else if (pm->pm_caps & PMC_CAP_SYSTEM) 490 amd->pm_amd_config |= AMD_PMC_OS; 491 if (ped->ped_edge) 492 amd->pm_amd_config |= AMD_PMC_EDGE; 493 if (ped->ped_inv) 494 amd->pm_amd_config |= AMD_PMC_EDGE; 495 if (pm->pm_caps & PMC_CAP_INTERRUPT) 496 amd->pm_amd_config |= AMD_PMC_INT; 497 } 498 return (0); 499 } 500 501 static int 502 pmc_pmu_intel_pmcallocate(const char *event_name, struct pmc_op_pmcallocate *pm, 503 struct pmu_event_desc *ped) 504 { 505 struct pmc_md_iap_op_pmcallocate *iap; 506 507 iap = &pm->pm_md.pm_iap; 508 if (strcasestr(event_name, "UNC_") == event_name || 509 strcasestr(event_name, "uncore") != NULL) { 510 pm->pm_class = PMC_CLASS_UCP; 511 pm->pm_caps |= PMC_CAP_QUALIFIER; 512 } else if ((ped->ped_umask == -1) || 513 (ped->ped_event == 0x0 && ped->ped_umask == 0x3)) { 514 pm->pm_class = PMC_CLASS_IAF; 515 } else { 516 pm->pm_class = PMC_CLASS_IAP; 517 pm->pm_caps |= PMC_CAP_QUALIFIER; 518 } 519 iap->pm_iap_config |= IAP_EVSEL(ped->ped_event); 520 if (ped->ped_umask > 0) 521 iap->pm_iap_config |= IAP_UMASK(ped->ped_umask); 522 iap->pm_iap_config |= IAP_CMASK(ped->ped_cmask); 523 iap->pm_iap_rsp = ped->ped_offcore_rsp; 524 525 if ((pm->pm_caps & (PMC_CAP_USER|PMC_CAP_SYSTEM)) == 0 || 526 (pm->pm_caps & (PMC_CAP_USER|PMC_CAP_SYSTEM)) == 527 (PMC_CAP_USER|PMC_CAP_SYSTEM)) 528 iap->pm_iap_config |= (IAP_USR | IAP_OS); 529 else if (pm->pm_caps & PMC_CAP_USER) 530 iap->pm_iap_config |= IAP_USR; 531 else if (pm->pm_caps & PMC_CAP_SYSTEM) 532 iap->pm_iap_config |= IAP_OS; 533 if (ped->ped_edge) 534 iap->pm_iap_config |= IAP_EDGE; 535 if (ped->ped_any) 536 iap->pm_iap_config |= IAP_ANY; 537 if (ped->ped_inv) 538 iap->pm_iap_config |= IAP_EDGE; 539 if (pm->pm_caps & PMC_CAP_INTERRUPT) 540 iap->pm_iap_config |= IAP_INT; 541 return (0); 542 } 543 544 int 545 pmc_pmu_pmcallocate(const char *event_name, struct pmc_op_pmcallocate *pm) 546 { 547 const struct pmu_event *pe; 548 struct pmu_event_desc ped; 549 pmu_mfr_t mfr; 550 int idx = -1; 551 552 if ((mfr = pmu_events_mfr()) == PMU_INVALID) 553 return (ENOENT); 554 555 bzero(&pm->pm_md, sizeof(pm->pm_md)); 556 pm->pm_caps |= (PMC_CAP_READ | PMC_CAP_WRITE); 557 event_name = pmu_alias_get(event_name); 558 if ((pe = pmu_event_get(NULL, event_name, &idx)) == NULL) 559 return (ENOENT); 560 assert(idx >= 0); 561 pm->pm_ev = idx; 562 563 if (pe->event == NULL) 564 return (ENOENT); 565 if (pmu_parse_event(&ped, pe->event)) 566 return (ENOENT); 567 568 if (mfr == PMU_INTEL) 569 return (pmc_pmu_intel_pmcallocate(event_name, pm, &ped)); 570 else 571 return (pmc_pmu_amd_pmcallocate(event_name, pm, &ped)); 572 } 573 574 #elif defined(__aarch64__) 575 576 int 577 pmc_pmu_pmcallocate(const char *event_name, struct pmc_op_pmcallocate *pm) 578 { 579 const struct pmu_event *pe; 580 int idx = -1; 581 582 event_name = pmu_alias_get(event_name); 583 if ((pe = pmu_event_get(NULL, event_name, &idx)) == NULL) 584 return (ENOENT); 585 if (pe->event == NULL) 586 return (ENOENT); 587 588 assert(idx >= 0); 589 pm->pm_md.pm_md_flags |= PM_MD_RAW_EVENT; 590 pm->pm_class = PMC_CLASS_ARMV8; 591 pm->pm_caps |= (PMC_CAP_READ | PMC_CAP_WRITE); 592 593 return (0); 594 } 595 596 #else 597 598 int 599 pmc_pmu_pmcallocate(const char *e __unused, struct pmc_op_pmcallocate *p __unused) 600 { 601 return (EOPNOTSUPP); 602 } 603 #endif 604