1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2018 Intel Corporation 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted providing that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 24 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/types.h> 32 #include <sys/sbuf.h> 33 #include <sys/module.h> 34 #include <sys/systm.h> 35 #include <sys/errno.h> 36 #include <sys/param.h> 37 #include <sys/kernel.h> 38 #include <sys/bus.h> 39 #include <sys/cpu.h> 40 #include <sys/smp.h> 41 #include <sys/proc.h> 42 #include <sys/sched.h> 43 44 #include <machine/cpu.h> 45 #include <machine/md_var.h> 46 #include <machine/cputypes.h> 47 #include <machine/specialreg.h> 48 49 #include <contrib/dev/acpica/include/acpi.h> 50 51 #include <dev/acpica/acpivar.h> 52 53 #include <x86/cpufreq/hwpstate_intel_internal.h> 54 55 #include "acpi_if.h" 56 #include "cpufreq_if.h" 57 58 extern uint64_t tsc_freq; 59 60 static int intel_hwpstate_probe(device_t dev); 61 static int intel_hwpstate_attach(device_t dev); 62 static int intel_hwpstate_detach(device_t dev); 63 static int intel_hwpstate_suspend(device_t dev); 64 static int intel_hwpstate_resume(device_t dev); 65 66 static int intel_hwpstate_get(device_t dev, struct cf_setting *cf); 67 static int intel_hwpstate_type(device_t dev, int *type); 68 69 static device_method_t intel_hwpstate_methods[] = { 70 /* Device interface */ 71 DEVMETHOD(device_identify, intel_hwpstate_identify), 72 DEVMETHOD(device_probe, intel_hwpstate_probe), 73 DEVMETHOD(device_attach, intel_hwpstate_attach), 74 DEVMETHOD(device_detach, intel_hwpstate_detach), 75 DEVMETHOD(device_suspend, intel_hwpstate_suspend), 76 DEVMETHOD(device_resume, intel_hwpstate_resume), 77 78 /* cpufreq interface */ 79 DEVMETHOD(cpufreq_drv_get, intel_hwpstate_get), 80 DEVMETHOD(cpufreq_drv_type, intel_hwpstate_type), 81 82 DEVMETHOD_END 83 }; 84 85 struct hwp_softc { 86 device_t dev; 87 bool hwp_notifications; 88 bool hwp_activity_window; 89 bool hwp_pref_ctrl; 90 bool hwp_pkg_ctrl; 91 bool hwp_pkg_ctrl_en; 92 bool hwp_perf_bias; 93 bool hwp_perf_bias_cached; 94 95 uint64_t req; /* Cached copy of HWP_REQUEST */ 96 uint64_t hwp_energy_perf_bias; /* Cache PERF_BIAS */ 97 98 uint8_t high; 99 uint8_t guaranteed; 100 uint8_t efficient; 101 uint8_t low; 102 }; 103 104 static devclass_t hwpstate_intel_devclass; 105 static driver_t hwpstate_intel_driver = { 106 "hwpstate_intel", 107 intel_hwpstate_methods, 108 sizeof(struct hwp_softc), 109 }; 110 111 DRIVER_MODULE(hwpstate_intel, cpu, hwpstate_intel_driver, 112 hwpstate_intel_devclass, NULL, NULL); 113 MODULE_VERSION(hwpstate_intel, 1); 114 115 static bool hwpstate_pkg_ctrl_enable = true; 116 SYSCTL_BOOL(_machdep, OID_AUTO, hwpstate_pkg_ctrl, CTLFLAG_RDTUN, 117 &hwpstate_pkg_ctrl_enable, 0, 118 "Set 1 (default) to enable package-level control, 0 to disable"); 119 120 static int 121 intel_hwp_dump_sysctl_handler(SYSCTL_HANDLER_ARGS) 122 { 123 device_t dev; 124 struct pcpu *pc; 125 struct sbuf *sb; 126 struct hwp_softc *sc; 127 uint64_t data, data2; 128 int ret; 129 130 sc = (struct hwp_softc *)arg1; 131 dev = sc->dev; 132 133 pc = cpu_get_pcpu(dev); 134 if (pc == NULL) 135 return (ENXIO); 136 137 sb = sbuf_new(NULL, NULL, 1024, SBUF_FIXEDLEN | SBUF_INCLUDENUL); 138 sbuf_putc(sb, '\n'); 139 thread_lock(curthread); 140 sched_bind(curthread, pc->pc_cpuid); 141 thread_unlock(curthread); 142 143 rdmsr_safe(MSR_IA32_PM_ENABLE, &data); 144 sbuf_printf(sb, "CPU%d: HWP %sabled\n", pc->pc_cpuid, 145 ((data & 1) ? "En" : "Dis")); 146 147 if (data == 0) { 148 ret = 0; 149 goto out; 150 } 151 152 rdmsr_safe(MSR_IA32_HWP_CAPABILITIES, &data); 153 sbuf_printf(sb, "\tHighest Performance: %03ju\n", data & 0xff); 154 sbuf_printf(sb, "\tGuaranteed Performance: %03ju\n", (data >> 8) & 0xff); 155 sbuf_printf(sb, "\tEfficient Performance: %03ju\n", (data >> 16) & 0xff); 156 sbuf_printf(sb, "\tLowest Performance: %03ju\n", (data >> 24) & 0xff); 157 158 rdmsr_safe(MSR_IA32_HWP_REQUEST, &data); 159 data2 = 0; 160 if (sc->hwp_pkg_ctrl && (data & IA32_HWP_REQUEST_PACKAGE_CONTROL)) 161 rdmsr_safe(MSR_IA32_HWP_REQUEST_PKG, &data2); 162 163 sbuf_putc(sb, '\n'); 164 165 #define pkg_print(x, name, offset) do { \ 166 if (!sc->hwp_pkg_ctrl || (data & x) != 0) \ 167 sbuf_printf(sb, "\t%s: %03u\n", name, \ 168 (unsigned)(data >> offset) & 0xff); \ 169 else \ 170 sbuf_printf(sb, "\t%s: %03u\n", name, \ 171 (unsigned)(data2 >> offset) & 0xff); \ 172 } while (0) 173 174 pkg_print(IA32_HWP_REQUEST_EPP_VALID, 175 "Requested Efficiency Performance Preference", 24); 176 pkg_print(IA32_HWP_REQUEST_DESIRED_VALID, 177 "Requested Desired Performance", 16); 178 pkg_print(IA32_HWP_REQUEST_MAXIMUM_VALID, 179 "Requested Maximum Performance", 8); 180 pkg_print(IA32_HWP_REQUEST_MINIMUM_VALID, 181 "Requested Minimum Performance", 0); 182 #undef pkg_print 183 184 sbuf_putc(sb, '\n'); 185 186 out: 187 thread_lock(curthread); 188 sched_unbind(curthread); 189 thread_unlock(curthread); 190 191 ret = sbuf_finish(sb); 192 if (ret == 0) 193 ret = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb)); 194 sbuf_delete(sb); 195 196 return (ret); 197 } 198 199 static inline int 200 percent_to_raw(int x) 201 { 202 203 MPASS(x <= 100 && x >= 0); 204 return (0xff * x / 100); 205 } 206 207 /* 208 * Given x * 10 in [0, 1000], round to the integer nearest x. 209 * 210 * This allows round-tripping nice human readable numbers through this 211 * interface. Otherwise, user-provided percentages such as 25, 50, 75 get 212 * rounded down to 24, 49, and 74, which is a bit ugly. 213 */ 214 static inline int 215 round10(int xtimes10) 216 { 217 return ((xtimes10 + 5) / 10); 218 } 219 220 static inline int 221 raw_to_percent(int x) 222 { 223 MPASS(x <= 0xff && x >= 0); 224 return (round10(x * 1000 / 0xff)); 225 } 226 227 /* Range of MSR_IA32_ENERGY_PERF_BIAS is more limited: 0-0xf. */ 228 static inline int 229 percent_to_raw_perf_bias(int x) 230 { 231 /* 232 * Round up so that raw values present as nice round human numbers and 233 * also round-trip to the same raw value. 234 */ 235 MPASS(x <= 100 && x >= 0); 236 return (((0xf * x) + 50) / 100); 237 } 238 239 static inline int 240 raw_to_percent_perf_bias(int x) 241 { 242 /* Rounding to nice human numbers despite a step interval of 6.67%. */ 243 MPASS(x <= 0xf && x >= 0); 244 return (((x * 20) / 0xf) * 5); 245 } 246 247 static int 248 sysctl_epp_select(SYSCTL_HANDLER_ARGS) 249 { 250 struct hwp_softc *sc; 251 device_t dev; 252 struct pcpu *pc; 253 uint64_t epb; 254 uint32_t val; 255 int ret; 256 257 dev = oidp->oid_arg1; 258 sc = device_get_softc(dev); 259 if (!sc->hwp_pref_ctrl && !sc->hwp_perf_bias) 260 return (ENODEV); 261 262 pc = cpu_get_pcpu(dev); 263 if (pc == NULL) 264 return (ENXIO); 265 266 thread_lock(curthread); 267 sched_bind(curthread, pc->pc_cpuid); 268 thread_unlock(curthread); 269 270 if (sc->hwp_pref_ctrl) { 271 val = (sc->req & IA32_HWP_REQUEST_ENERGY_PERFORMANCE_PREFERENCE) >> 24; 272 val = raw_to_percent(val); 273 } else { 274 /* 275 * If cpuid indicates EPP is not supported, the HWP controller 276 * uses MSR_IA32_ENERGY_PERF_BIAS instead (Intel SDM §14.4.4). 277 * This register is per-core (but not HT). 278 */ 279 if (!sc->hwp_perf_bias_cached) { 280 ret = rdmsr_safe(MSR_IA32_ENERGY_PERF_BIAS, &epb); 281 if (ret) 282 goto out; 283 sc->hwp_energy_perf_bias = epb; 284 sc->hwp_perf_bias_cached = true; 285 } 286 val = sc->hwp_energy_perf_bias & 287 IA32_ENERGY_PERF_BIAS_POLICY_HINT_MASK; 288 val = raw_to_percent_perf_bias(val); 289 } 290 291 MPASS(val >= 0 && val <= 100); 292 293 ret = sysctl_handle_int(oidp, &val, 0, req); 294 if (ret || req->newptr == NULL) 295 goto out; 296 297 if (val > 100) { 298 ret = EINVAL; 299 goto out; 300 } 301 302 if (sc->hwp_pref_ctrl) { 303 val = percent_to_raw(val); 304 305 sc->req = 306 ((sc->req & ~IA32_HWP_REQUEST_ENERGY_PERFORMANCE_PREFERENCE) 307 | (val << 24u)); 308 309 if (sc->hwp_pkg_ctrl_en) 310 ret = wrmsr_safe(MSR_IA32_HWP_REQUEST_PKG, sc->req); 311 else 312 ret = wrmsr_safe(MSR_IA32_HWP_REQUEST, sc->req); 313 } else { 314 val = percent_to_raw_perf_bias(val); 315 MPASS((val & ~IA32_ENERGY_PERF_BIAS_POLICY_HINT_MASK) == 0); 316 317 sc->hwp_energy_perf_bias = 318 ((sc->hwp_energy_perf_bias & 319 ~IA32_ENERGY_PERF_BIAS_POLICY_HINT_MASK) | val); 320 ret = wrmsr_safe(MSR_IA32_ENERGY_PERF_BIAS, 321 sc->hwp_energy_perf_bias); 322 } 323 324 out: 325 thread_lock(curthread); 326 sched_unbind(curthread); 327 thread_unlock(curthread); 328 329 return (ret); 330 } 331 332 void 333 intel_hwpstate_identify(driver_t *driver, device_t parent) 334 { 335 if (device_find_child(parent, "hwpstate_intel", -1) != NULL) 336 return; 337 338 if (cpu_vendor_id != CPU_VENDOR_INTEL) 339 return; 340 341 if (resource_disabled("hwpstate_intel", 0)) 342 return; 343 344 /* 345 * Intel SDM 14.4.1 (HWP Programming Interfaces): 346 * Availability of HWP baseline resource and capability, 347 * CPUID.06H:EAX[bit 7]: If this bit is set, HWP provides several new 348 * architectural MSRs: IA32_PM_ENABLE, IA32_HWP_CAPABILITIES, 349 * IA32_HWP_REQUEST, IA32_HWP_STATUS. 350 */ 351 if ((cpu_power_eax & CPUTPM1_HWP) == 0) 352 return; 353 354 if (BUS_ADD_CHILD(parent, 10, "hwpstate_intel", -1) == NULL) 355 return; 356 357 if (bootverbose) 358 device_printf(parent, "hwpstate registered\n"); 359 } 360 361 static int 362 intel_hwpstate_probe(device_t dev) 363 { 364 365 device_set_desc(dev, "Intel Speed Shift"); 366 return (BUS_PROBE_NOWILDCARD); 367 } 368 369 static int 370 set_autonomous_hwp(struct hwp_softc *sc) 371 { 372 struct pcpu *pc; 373 device_t dev; 374 uint64_t caps; 375 int ret; 376 377 dev = sc->dev; 378 379 pc = cpu_get_pcpu(dev); 380 if (pc == NULL) 381 return (ENXIO); 382 383 thread_lock(curthread); 384 sched_bind(curthread, pc->pc_cpuid); 385 thread_unlock(curthread); 386 387 /* XXX: Many MSRs aren't readable until feature is enabled */ 388 ret = wrmsr_safe(MSR_IA32_PM_ENABLE, 1); 389 if (ret) { 390 /* 391 * This is actually a package-level MSR, and only the first 392 * write is not ignored. So it is harmless to enable it across 393 * all devices, and this allows us not to care especially in 394 * which order cores (and packages) are probed. This error 395 * condition should not happen given we gate on the HWP CPUID 396 * feature flag, if the Intel SDM is correct. 397 */ 398 device_printf(dev, "Failed to enable HWP for cpu%d (%d)\n", 399 pc->pc_cpuid, ret); 400 goto out; 401 } 402 403 ret = rdmsr_safe(MSR_IA32_HWP_REQUEST, &sc->req); 404 if (ret) { 405 device_printf(dev, 406 "Failed to read HWP request MSR for cpu%d (%d)\n", 407 pc->pc_cpuid, ret); 408 goto out; 409 } 410 411 ret = rdmsr_safe(MSR_IA32_HWP_CAPABILITIES, &caps); 412 if (ret) { 413 device_printf(dev, 414 "Failed to read HWP capabilities MSR for cpu%d (%d)\n", 415 pc->pc_cpuid, ret); 416 goto out; 417 } 418 419 /* 420 * High and low are static; "guaranteed" is dynamic; and efficient is 421 * also dynamic. 422 */ 423 sc->high = IA32_HWP_CAPABILITIES_HIGHEST_PERFORMANCE(caps); 424 sc->guaranteed = IA32_HWP_CAPABILITIES_GUARANTEED_PERFORMANCE(caps); 425 sc->efficient = IA32_HWP_CAPABILITIES_EFFICIENT_PERFORMANCE(caps); 426 sc->low = IA32_HWP_CAPABILITIES_LOWEST_PERFORMANCE(caps); 427 428 /* hardware autonomous selection determines the performance target */ 429 sc->req &= ~IA32_HWP_DESIRED_PERFORMANCE; 430 431 /* enable HW dynamic selection of window size */ 432 sc->req &= ~IA32_HWP_ACTIVITY_WINDOW; 433 434 /* IA32_HWP_REQUEST.Minimum_Performance = IA32_HWP_CAPABILITIES.Lowest_Performance */ 435 sc->req &= ~IA32_HWP_MINIMUM_PERFORMANCE; 436 sc->req |= sc->low; 437 438 /* IA32_HWP_REQUEST.Maximum_Performance = IA32_HWP_CAPABILITIES.Highest_Performance. */ 439 sc->req &= ~IA32_HWP_REQUEST_MAXIMUM_PERFORMANCE; 440 sc->req |= sc->high << 8; 441 442 /* If supported, request package-level control for this CPU. */ 443 if (sc->hwp_pkg_ctrl_en) 444 ret = wrmsr_safe(MSR_IA32_HWP_REQUEST, sc->req | 445 IA32_HWP_REQUEST_PACKAGE_CONTROL); 446 else 447 ret = wrmsr_safe(MSR_IA32_HWP_REQUEST, sc->req); 448 if (ret) { 449 device_printf(dev, 450 "Failed to setup%s autonomous HWP for cpu%d\n", 451 sc->hwp_pkg_ctrl_en ? " PKG" : "", pc->pc_cpuid); 452 goto out; 453 } 454 455 /* If supported, write the PKG-wide control MSR. */ 456 if (sc->hwp_pkg_ctrl_en) { 457 /* 458 * "The structure of the IA32_HWP_REQUEST_PKG MSR 459 * (package-level) is identical to the IA32_HWP_REQUEST MSR 460 * with the exception of the Package Control field, which does 461 * not exist." (Intel SDM §14.4.4) 462 */ 463 ret = wrmsr_safe(MSR_IA32_HWP_REQUEST_PKG, sc->req); 464 if (ret) { 465 device_printf(dev, 466 "Failed to set autonomous HWP for package\n"); 467 } 468 } 469 470 out: 471 thread_lock(curthread); 472 sched_unbind(curthread); 473 thread_unlock(curthread); 474 475 return (ret); 476 } 477 478 static int 479 intel_hwpstate_attach(device_t dev) 480 { 481 struct hwp_softc *sc; 482 int ret; 483 484 sc = device_get_softc(dev); 485 sc->dev = dev; 486 487 /* eax */ 488 if (cpu_power_eax & CPUTPM1_HWP_NOTIFICATION) 489 sc->hwp_notifications = true; 490 if (cpu_power_eax & CPUTPM1_HWP_ACTIVITY_WINDOW) 491 sc->hwp_activity_window = true; 492 if (cpu_power_eax & CPUTPM1_HWP_PERF_PREF) 493 sc->hwp_pref_ctrl = true; 494 if (cpu_power_eax & CPUTPM1_HWP_PKG) 495 sc->hwp_pkg_ctrl = true; 496 497 /* Allow administrators to disable pkg-level control. */ 498 sc->hwp_pkg_ctrl_en = (sc->hwp_pkg_ctrl && hwpstate_pkg_ctrl_enable); 499 500 /* ecx */ 501 if (cpu_power_ecx & CPUID_PERF_BIAS) 502 sc->hwp_perf_bias = true; 503 504 ret = set_autonomous_hwp(sc); 505 if (ret) 506 return (ret); 507 508 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 509 SYSCTL_STATIC_CHILDREN(_debug), OID_AUTO, device_get_nameunit(dev), 510 CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_SKIP | CTLFLAG_NEEDGIANT, 511 sc, 0, intel_hwp_dump_sysctl_handler, "A", ""); 512 513 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 514 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 515 "epp", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, dev, 0, 516 sysctl_epp_select, "I", 517 "Efficiency/Performance Preference " 518 "(range from 0, most performant, through 100, most efficient)"); 519 520 return (cpufreq_register(dev)); 521 } 522 523 static int 524 intel_hwpstate_detach(device_t dev) 525 { 526 527 return (cpufreq_unregister(dev)); 528 } 529 530 static int 531 intel_hwpstate_get(device_t dev, struct cf_setting *set) 532 { 533 struct pcpu *pc; 534 uint64_t rate; 535 int ret; 536 537 if (set == NULL) 538 return (EINVAL); 539 540 pc = cpu_get_pcpu(dev); 541 if (pc == NULL) 542 return (ENXIO); 543 544 memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set)); 545 set->dev = dev; 546 547 ret = cpu_est_clockrate(pc->pc_cpuid, &rate); 548 if (ret == 0) 549 set->freq = rate / 1000000; 550 551 set->volts = CPUFREQ_VAL_UNKNOWN; 552 set->power = CPUFREQ_VAL_UNKNOWN; 553 set->lat = CPUFREQ_VAL_UNKNOWN; 554 555 return (0); 556 } 557 558 static int 559 intel_hwpstate_type(device_t dev, int *type) 560 { 561 if (type == NULL) 562 return (EINVAL); 563 *type = CPUFREQ_TYPE_ABSOLUTE | CPUFREQ_FLAG_INFO_ONLY | CPUFREQ_FLAG_UNCACHED; 564 565 return (0); 566 } 567 568 static int 569 intel_hwpstate_suspend(device_t dev) 570 { 571 return (0); 572 } 573 574 /* 575 * Redo a subset of set_autonomous_hwp on resume; untested. Without this, 576 * testers observed that on resume MSR_IA32_HWP_REQUEST was bogus. 577 */ 578 static int 579 intel_hwpstate_resume(device_t dev) 580 { 581 struct hwp_softc *sc; 582 struct pcpu *pc; 583 int ret; 584 585 sc = device_get_softc(dev); 586 587 pc = cpu_get_pcpu(dev); 588 if (pc == NULL) 589 return (ENXIO); 590 591 thread_lock(curthread); 592 sched_bind(curthread, pc->pc_cpuid); 593 thread_unlock(curthread); 594 595 ret = wrmsr_safe(MSR_IA32_PM_ENABLE, 1); 596 if (ret) { 597 device_printf(dev, 598 "Failed to enable HWP for cpu%d after suspend (%d)\n", 599 pc->pc_cpuid, ret); 600 goto out; 601 } 602 603 if (sc->hwp_pkg_ctrl_en) 604 ret = wrmsr_safe(MSR_IA32_HWP_REQUEST, sc->req | 605 IA32_HWP_REQUEST_PACKAGE_CONTROL); 606 else 607 ret = wrmsr_safe(MSR_IA32_HWP_REQUEST, sc->req); 608 if (ret) { 609 device_printf(dev, 610 "Failed to set%s autonomous HWP for cpu%d after suspend\n", 611 sc->hwp_pkg_ctrl_en ? " PKG" : "", pc->pc_cpuid); 612 goto out; 613 } 614 if (sc->hwp_pkg_ctrl_en) { 615 ret = wrmsr_safe(MSR_IA32_HWP_REQUEST_PKG, sc->req); 616 if (ret) { 617 device_printf(dev, 618 "Failed to set autonomous HWP for package after " 619 "suspend\n"); 620 goto out; 621 } 622 } 623 if (!sc->hwp_pref_ctrl && sc->hwp_perf_bias_cached) { 624 ret = wrmsr_safe(MSR_IA32_ENERGY_PERF_BIAS, 625 sc->hwp_energy_perf_bias); 626 if (ret) { 627 device_printf(dev, 628 "Failed to set energy perf bias for cpu%d after " 629 "suspend\n", pc->pc_cpuid); 630 } 631 } 632 633 out: 634 thread_lock(curthread); 635 sched_unbind(curthread); 636 thread_unlock(curthread); 637 638 return (ret); 639 } 640