Move the xcall(9) that does the P- and T-state transformations from the MD

layer to the main code. Makes the caches coherent and provides consistent
vmstat(1) output. This is still not quite right, given that most of the
cross-calls are typically unnecessary with the dependency coordination.
This commit is contained in:
jruoho 2011-03-01 04:35:48 +00:00
parent 6024239e67
commit c9111546fe
4 changed files with 124 additions and 158 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: acpi_cpu_md.c,v 1.48 2011/02/27 18:32:54 jruoho Exp $ */
/* $NetBSD: acpi_cpu_md.c,v 1.49 2011/03/01 04:35:48 jruoho Exp $ */
/*-
* Copyright (c) 2010, 2011 Jukka Ruohonen <jruohonen@iki.fi>
@ -27,7 +27,7 @@
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: acpi_cpu_md.c,v 1.48 2011/02/27 18:32:54 jruoho Exp $");
__KERNEL_RCSID(0, "$NetBSD: acpi_cpu_md.c,v 1.49 2011/03/01 04:35:48 jruoho Exp $");
#include <sys/param.h>
#include <sys/bus.h>
@ -102,21 +102,18 @@ void (*native_idle)(void) = NULL;
static int acpicpu_md_quirk_piix4(struct pci_attach_args *);
static void acpicpu_md_pstate_percent_reset(struct acpicpu_softc *);
static void acpicpu_md_pstate_percent_status(void *, void *);
static void acpicpu_md_pstate_status(void *, void *);
static int acpicpu_md_pstate_fidvid_get(struct acpicpu_softc *,
uint32_t *);
static int acpicpu_md_pstate_fidvid_set(struct acpicpu_pstate *);
static int acpicpu_md_pstate_fidvid_read(uint32_t *, uint32_t *);
static void acpicpu_md_pstate_fidvid_write(uint32_t, uint32_t,
uint32_t, uint32_t);
static void acpicpu_md_tstate_status(void *, void *);
static int acpicpu_md_pstate_sysctl_init(void);
static int acpicpu_md_pstate_sysctl_get(SYSCTLFN_PROTO);
static int acpicpu_md_pstate_sysctl_set(SYSCTLFN_PROTO);
static int acpicpu_md_pstate_sysctl_all(SYSCTLFN_PROTO);
extern struct acpicpu_softc **acpicpu_sc;
static bool acpicpu_pstate_status = false;
static struct sysctllog *acpicpu_log = NULL;
struct cpu_info *
@ -725,6 +722,9 @@ acpicpu_md_pstate_get(struct acpicpu_softc *sc, uint32_t *freq)
if ((sc->sc_flags & ACPICPU_FLAG_P_FIDVID) != 0)
return acpicpu_md_pstate_fidvid_get(sc, freq);
/*
* Pick any P-state for the status address.
*/
for (i = 0; i < sc->sc_pstate_count; i++) {
ps = &sc->sc_pstate[i];
@ -744,6 +744,9 @@ acpicpu_md_pstate_get(struct acpicpu_softc *sc, uint32_t *freq)
if (__predict_true(ps->ps_status_mask != 0))
val = val & ps->ps_status_mask;
/*
* Search for the value from known P-states.
*/
for (i = 0; i < sc->sc_pstate_count; i++) {
ps = &sc->sc_pstate[i];
@ -763,9 +766,7 @@ acpicpu_md_pstate_get(struct acpicpu_softc *sc, uint32_t *freq)
int
acpicpu_md_pstate_set(struct acpicpu_pstate *ps)
{
struct msr_rw_info msr;
uint64_t xc;
int rv = 0;
uint64_t val;
if (__predict_false(ps->ps_control_addr == 0))
return EINVAL;
@ -773,54 +774,15 @@ acpicpu_md_pstate_set(struct acpicpu_pstate *ps)
if ((ps->ps_flags & ACPICPU_FLAG_P_FIDVID) != 0)
return acpicpu_md_pstate_fidvid_set(ps);
msr.msr_read = false;
msr.msr_type = ps->ps_control_addr;
msr.msr_value = ps->ps_control;
val = ps->ps_control;
if (__predict_true(ps->ps_control_mask != 0)) {
msr.msr_mask = ps->ps_control_mask;
msr.msr_read = true;
}
if (__predict_true(ps->ps_control_mask != 0))
val = val & ps->ps_control_mask;
xc = xc_broadcast(0, (xcfunc_t)x86_msr_xcall, &msr, NULL);
xc_wait(xc);
wrmsr(ps->ps_control_addr, val);
DELAY(ps->ps_latency);
/*
* Due several problems, we bypass the
* relatively expensive status check.
*/
if (acpicpu_pstate_status != true) {
DELAY(ps->ps_latency);
return 0;
}
xc = xc_broadcast(0, (xcfunc_t)acpicpu_md_pstate_status, ps, &rv);
xc_wait(xc);
return rv;
}
static void
acpicpu_md_pstate_status(void *arg1, void *arg2)
{
struct acpicpu_pstate *ps = arg1;
uint64_t val;
int i;
for (i = val = 0; i < ACPICPU_P_STATE_RETRY; i++) {
val = rdmsr(ps->ps_status_addr);
if (__predict_true(ps->ps_status_mask != 0))
val = val & ps->ps_status_mask;
if (val == ps->ps_status)
return;
DELAY(ps->ps_latency);
}
*(uintptr_t *)arg2 = EAGAIN;
return 0;
}
static int
@ -988,21 +950,14 @@ static void
acpicpu_md_pstate_fidvid_write(uint32_t fid,
uint32_t vid, uint32_t cnt, uint32_t tmo)
{
struct msr_rw_info msr;
uint64_t xc;
uint64_t val = 0;
msr.msr_read = false;
msr.msr_type = MSR_0FH_CONTROL;
msr.msr_value = 0;
msr.msr_value |= __SHIFTIN(fid, MSR_0FH_CONTROL_FID);
msr.msr_value |= __SHIFTIN(vid, MSR_0FH_CONTROL_VID);
msr.msr_value |= __SHIFTIN(cnt, MSR_0FH_CONTROL_CNT);
msr.msr_value |= __SHIFTIN(0x1, MSR_0FH_CONTROL_CHG);
xc = xc_broadcast(0, (xcfunc_t)x86_msr_xcall, &msr, NULL);
xc_wait(xc);
val |= __SHIFTIN(fid, MSR_0FH_CONTROL_FID);
val |= __SHIFTIN(vid, MSR_0FH_CONTROL_VID);
val |= __SHIFTIN(cnt, MSR_0FH_CONTROL_CNT);
val |= __SHIFTIN(0x1, MSR_0FH_CONTROL_CHG);
wrmsr(MSR_0FH_CONTROL, val);
DELAY(tmo);
}
@ -1034,47 +989,30 @@ acpicpu_md_tstate_get(struct acpicpu_softc *sc, uint32_t *percent)
int
acpicpu_md_tstate_set(struct acpicpu_tstate *ts)
{
struct msr_rw_info msr;
uint64_t xc;
int rv = 0;
uint64_t val;
uint8_t i;
msr.msr_read = true;
msr.msr_type = MSR_THERM_CONTROL;
msr.msr_value = ts->ts_control;
msr.msr_mask = __BITS(1, 4);
val = ts->ts_control;
val = val & __BITS(1, 4);
xc = xc_broadcast(0, (xcfunc_t)x86_msr_xcall, &msr, NULL);
xc_wait(xc);
wrmsr(MSR_THERM_CONTROL, val);
if (ts->ts_status == 0) {
DELAY(ts->ts_latency);
return 0;
}
xc = xc_broadcast(0, (xcfunc_t)acpicpu_md_tstate_status, ts, &rv);
xc_wait(xc);
return rv;
}
static void
acpicpu_md_tstate_status(void *arg1, void *arg2)
{
struct acpicpu_tstate *ts = arg1;
uint64_t val;
int i;
for (i = val = 0; i < ACPICPU_T_STATE_RETRY; i++) {
val = rdmsr(MSR_THERM_CONTROL);
if (val == ts->ts_status)
return;
return 0;
DELAY(ts->ts_latency);
}
*(uintptr_t *)arg2 = EAGAIN;
return EAGAIN;
}
/*
@ -1160,17 +1098,11 @@ static int
acpicpu_md_pstate_sysctl_get(SYSCTLFN_ARGS)
{
struct cpu_info *ci = curcpu();
struct acpicpu_softc *sc;
struct sysctlnode node;
uint32_t freq;
int err;
sc = acpicpu_sc[ci->ci_acpiid];
if (sc == NULL)
return ENXIO;
err = acpicpu_pstate_get(sc, &freq);
err = acpicpu_pstate_get(ci, &freq);
if (err != 0)
return err;
@ -1190,17 +1122,11 @@ static int
acpicpu_md_pstate_sysctl_set(SYSCTLFN_ARGS)
{
struct cpu_info *ci = curcpu();
struct acpicpu_softc *sc;
struct sysctlnode node;
uint32_t freq;
int err;
sc = acpicpu_sc[ci->ci_acpiid];
if (sc == NULL)
return ENXIO;
err = acpicpu_pstate_get(sc, &freq);
err = acpicpu_pstate_get(ci, &freq);
if (err != 0)
return err;
@ -1213,10 +1139,7 @@ acpicpu_md_pstate_sysctl_set(SYSCTLFN_ARGS)
if (err != 0 || newp == NULL)
return err;
err = acpicpu_pstate_set(sc, freq);
if (err != 0)
return err;
acpicpu_pstate_set(ci, freq);
return 0;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: acpi_cpu.h,v 1.34 2011/02/27 18:32:53 jruoho Exp $ */
/* $NetBSD: acpi_cpu.h,v 1.35 2011/03/01 04:35:48 jruoho Exp $ */
/*-
* Copyright (c) 2010, 2011 Jukka Ruohonen <jruohonen@iki.fi>
@ -244,8 +244,8 @@ void acpicpu_pstate_start(device_t);
bool acpicpu_pstate_suspend(device_t);
bool acpicpu_pstate_resume(device_t);
void acpicpu_pstate_callback(void *);
int acpicpu_pstate_get(struct acpicpu_softc *, uint32_t *);
int acpicpu_pstate_set(struct acpicpu_softc *, uint32_t);
int acpicpu_pstate_get(struct cpu_info *, uint32_t *);
void acpicpu_pstate_set(struct cpu_info *, uint32_t);
void acpicpu_tstate_attach(device_t);
int acpicpu_tstate_detach(device_t);
@ -253,8 +253,8 @@ void acpicpu_tstate_start(device_t);
bool acpicpu_tstate_suspend(device_t);
bool acpicpu_tstate_resume(device_t);
void acpicpu_tstate_callback(void *);
int acpicpu_tstate_get(struct acpicpu_softc *, uint32_t *);
int acpicpu_tstate_set(struct acpicpu_softc *, uint32_t);
int acpicpu_tstate_get(struct cpu_info *, uint32_t *);
void acpicpu_tstate_set(struct cpu_info *, uint32_t);
struct cpu_info *acpicpu_md_match(device_t, cfdata_t, void *);
struct cpu_info *acpicpu_md_attach(device_t, device_t, void *);

View File

@ -1,4 +1,4 @@
/* $NetBSD: acpi_cpu_pstate.c,v 1.41 2011/02/27 17:10:33 jruoho Exp $ */
/* $NetBSD: acpi_cpu_pstate.c,v 1.42 2011/03/01 04:35:48 jruoho Exp $ */
/*-
* Copyright (c) 2010, 2011 Jukka Ruohonen <jruohonen@iki.fi>
@ -27,12 +27,13 @@
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: acpi_cpu_pstate.c,v 1.41 2011/02/27 17:10:33 jruoho Exp $");
__KERNEL_RCSID(0, "$NetBSD: acpi_cpu_pstate.c,v 1.42 2011/03/01 04:35:48 jruoho Exp $");
#include <sys/param.h>
#include <sys/evcnt.h>
#include <sys/kmem.h>
#include <sys/once.h>
#include <sys/xcall.h>
#include <dev/acpi/acpireg.h>
#include <dev/acpi/acpivar.h>
@ -57,8 +58,10 @@ static int acpicpu_pstate_min(struct acpicpu_softc *);
static void acpicpu_pstate_change(struct acpicpu_softc *);
static void acpicpu_pstate_reset(struct acpicpu_softc *);
static void acpicpu_pstate_bios(void);
static void acpicpu_pstate_set_xcall(void *, void *);
static uint32_t acpicpu_pstate_saved = 0;
static uint32_t acpicpu_pstate_saved = 0;
extern struct acpicpu_softc **acpicpu_sc;
void
acpicpu_pstate_attach(device_t self)
@ -264,31 +267,21 @@ acpicpu_pstate_start(device_t self)
goto fail;
/*
* Initialize the state to P0.
* Initialize the states to P0.
*/
for (i = 0, rv = ENXIO; i < sc->sc_pstate_count; i++) {
ps = &sc->sc_pstate[i];
if (ps->ps_freq != 0) {
rv = acpicpu_pstate_set(sc, ps->ps_freq);
break;
acpicpu_pstate_set(sc->sc_ci, ps->ps_freq);
return;
}
}
if (rv != 0)
goto fail;
return;
fail:
sc->sc_flags &= ~ACPICPU_FLAG_P;
if (rv == EEXIST) {
aprint_error_dev(self, "driver conflicts with existing one\n");
return;
}
aprint_error_dev(self, "failed to start P-states (err %d)\n", rv);
}
@ -332,7 +325,7 @@ acpicpu_pstate_suspend(device_t self)
if (acpicpu_pstate_saved == ps->ps_freq)
return true;
(void)acpicpu_pstate_set(sc, ps->ps_freq);
acpicpu_pstate_set(sc->sc_ci, ps->ps_freq);
return true;
}
@ -343,7 +336,7 @@ acpicpu_pstate_resume(device_t self)
struct acpicpu_softc *sc = device_private(self);
if (acpicpu_pstate_saved != 0) {
(void)acpicpu_pstate_set(sc, acpicpu_pstate_saved);
acpicpu_pstate_set(sc->sc_ci, acpicpu_pstate_saved);
acpicpu_pstate_saved = 0;
}
@ -377,7 +370,7 @@ acpicpu_pstate_callback(void *aux)
old, sc->sc_pstate[old].ps_freq, new,
sc->sc_pstate[sc->sc_pstate_max].ps_freq));
(void)acpicpu_pstate_set(sc, sc->sc_pstate[new].ps_freq);
acpicpu_pstate_set(sc->sc_ci, sc->sc_pstate[new].ps_freq);
}
ACPI_STATUS
@ -947,15 +940,22 @@ acpicpu_pstate_bios(void)
}
int
acpicpu_pstate_get(struct acpicpu_softc *sc, uint32_t *freq)
acpicpu_pstate_get(struct cpu_info *ci, uint32_t *freq)
{
const uint8_t method = sc->sc_pstate_control.reg_spaceid;
struct acpicpu_pstate *ps = NULL;
struct acpicpu_softc *sc;
uint32_t i, val = 0;
uint64_t addr;
uint8_t width;
int rv;
sc = acpicpu_sc[ci->ci_acpiid];
if (__predict_false(sc == NULL)) {
rv = ENXIO;
goto fail;
}
if (__predict_false(sc->sc_cold != false)) {
rv = EBUSY;
goto fail;
@ -979,7 +979,7 @@ acpicpu_pstate_get(struct acpicpu_softc *sc, uint32_t *freq)
mutex_exit(&sc->sc_mtx);
switch (method) {
switch (sc->sc_pstate_status.reg_spaceid) {
case ACPI_ADR_SPACE_FIXED_HARDWARE:
@ -1043,16 +1043,34 @@ fail:
return rv;
}
int
acpicpu_pstate_set(struct acpicpu_softc *sc, uint32_t freq)
void
acpicpu_pstate_set(struct cpu_info *ci, uint32_t freq)
{
uint64_t xc;
xc = xc_broadcast(0, acpicpu_pstate_set_xcall, &freq, NULL);
xc_wait(xc);
}
static void
acpicpu_pstate_set_xcall(void *arg1, void *arg2)
{
const uint8_t method = sc->sc_pstate_control.reg_spaceid;
struct acpicpu_pstate *ps = NULL;
uint32_t i, val;
struct cpu_info *ci = curcpu();
struct acpicpu_softc *sc;
uint32_t freq, i, val;
uint64_t addr;
uint8_t width;
int rv;
freq = *(uint32_t *)arg1;
sc = acpicpu_sc[ci->ci_acpiid];
if (__predict_false(sc == NULL)) {
rv = ENXIO;
goto fail;
}
if (__predict_false(sc->sc_cold != false)) {
rv = EBUSY;
goto fail;
@ -1067,7 +1085,7 @@ acpicpu_pstate_set(struct acpicpu_softc *sc, uint32_t freq)
if (sc->sc_pstate_current == freq) {
mutex_exit(&sc->sc_mtx);
return 0;
return;
}
/*
@ -1094,7 +1112,7 @@ acpicpu_pstate_set(struct acpicpu_softc *sc, uint32_t freq)
goto fail;
}
switch (method) {
switch (sc->sc_pstate_control.reg_spaceid) {
case ACPI_ADR_SPACE_FIXED_HARDWARE:
@ -1146,7 +1164,7 @@ acpicpu_pstate_set(struct acpicpu_softc *sc, uint32_t freq)
sc->sc_pstate_current = freq;
mutex_exit(&sc->sc_mtx);
return 0;
return;
fail:
aprint_error_dev(sc->sc_dev, "failed to set "
@ -1155,6 +1173,4 @@ fail:
mutex_enter(&sc->sc_mtx);
sc->sc_pstate_current = ACPICPU_P_STATE_UNKNOWN;
mutex_exit(&sc->sc_mtx);
return rv;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: acpi_cpu_tstate.c,v 1.23 2011/02/25 19:55:07 jruoho Exp $ */
/* $NetBSD: acpi_cpu_tstate.c,v 1.24 2011/03/01 04:35:48 jruoho Exp $ */
/*-
* Copyright (c) 2010 Jukka Ruohonen <jruohonen@iki.fi>
@ -27,11 +27,12 @@
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: acpi_cpu_tstate.c,v 1.23 2011/02/25 19:55:07 jruoho Exp $");
__KERNEL_RCSID(0, "$NetBSD: acpi_cpu_tstate.c,v 1.24 2011/03/01 04:35:48 jruoho Exp $");
#include <sys/param.h>
#include <sys/evcnt.h>
#include <sys/kmem.h>
#include <sys/xcall.h>
#include <dev/acpi/acpireg.h>
#include <dev/acpi/acpivar.h>
@ -51,6 +52,9 @@ static ACPI_STATUS acpicpu_tstate_dep(struct acpicpu_softc *);
static ACPI_STATUS acpicpu_tstate_fadt(struct acpicpu_softc *);
static ACPI_STATUS acpicpu_tstate_change(struct acpicpu_softc *);
static void acpicpu_tstate_reset(struct acpicpu_softc *);
static void acpicpu_tstate_set_xcall(void *, void *);
extern struct acpicpu_softc **acpicpu_sc;
void
acpicpu_tstate_attach(device_t self)
@ -721,15 +725,22 @@ acpicpu_tstate_reset(struct acpicpu_softc *sc)
}
int
acpicpu_tstate_get(struct acpicpu_softc *sc, uint32_t *percent)
acpicpu_tstate_get(struct cpu_info *ci, uint32_t *percent)
{
const uint8_t method = sc->sc_tstate_control.reg_spaceid;
struct acpicpu_tstate *ts = NULL;
struct acpicpu_softc *sc;
uint32_t i, val = 0;
uint8_t offset;
uint64_t addr;
int rv;
sc = acpicpu_sc[ci->ci_acpiid];
if (__predict_false(sc == NULL)) {
rv = ENXIO;
goto fail;
}
if (__predict_false(sc->sc_cold != false)) {
rv = EBUSY;
goto fail;
@ -750,7 +761,7 @@ acpicpu_tstate_get(struct acpicpu_softc *sc, uint32_t *percent)
mutex_exit(&sc->sc_mtx);
switch (method) {
switch (sc->sc_tstate_status.reg_spaceid) {
case ACPI_ADR_SPACE_FIXED_HARDWARE:
@ -811,16 +822,34 @@ fail:
return rv;
}
int
acpicpu_tstate_set(struct acpicpu_softc *sc, uint32_t percent)
void
acpicpu_tstate_set(struct cpu_info *ci, uint32_t percent)
{
uint64_t xc;
xc = xc_broadcast(0, acpicpu_tstate_set_xcall, &percent, NULL);
xc_wait(xc);
}
static void
acpicpu_tstate_set_xcall(void *arg1, void *arg2)
{
const uint8_t method = sc->sc_tstate_control.reg_spaceid;
struct acpicpu_tstate *ts = NULL;
uint32_t i, val;
struct cpu_info *ci = curcpu();
struct acpicpu_softc *sc;
uint32_t i, percent, val;
uint8_t offset;
uint64_t addr;
int rv;
percent = *(uint32_t *)arg1;
sc = acpicpu_sc[ci->ci_acpiid];
if (__predict_false(sc == NULL)) {
rv = ENXIO;
goto fail;
}
if (__predict_false(sc->sc_cold != false)) {
rv = EBUSY;
goto fail;
@ -835,7 +864,7 @@ acpicpu_tstate_set(struct acpicpu_softc *sc, uint32_t percent)
if (sc->sc_tstate_current == percent) {
mutex_exit(&sc->sc_mtx);
return 0;
return;
}
for (i = sc->sc_tstate_max; i <= sc->sc_tstate_min; i++) {
@ -856,7 +885,7 @@ acpicpu_tstate_set(struct acpicpu_softc *sc, uint32_t percent)
goto fail;
}
switch (method) {
switch (sc->sc_tstate_control.reg_spaceid) {
case ACPI_ADR_SPACE_FIXED_HARDWARE:
@ -921,7 +950,7 @@ acpicpu_tstate_set(struct acpicpu_softc *sc, uint32_t percent)
sc->sc_tstate_current = percent;
mutex_exit(&sc->sc_mtx);
return 0;
return;
fail:
aprint_error_dev(sc->sc_dev, "failed to "
@ -930,6 +959,4 @@ fail:
mutex_enter(&sc->sc_mtx);
sc->sc_tstate_current = ACPICPU_T_STATE_UNKNOWN;
mutex_exit(&sc->sc_mtx);
return rv;
}