Index: acpi_cpu.c =================================================================== RCS file: /home/FreeBSD/ncvs/src/sys/dev/acpica/acpi_cpu.c,v retrieving revision 1.63 diff -u -r1.63 acpi_cpu.c --- acpi_cpu.c 23 Jan 2007 07:20:44 -0000 1.63 +++ acpi_cpu.c 13 Feb 2007 03:28:32 -0000 @@ -80,6 +80,7 @@ /* Runtime state. */ int cpu_non_c3; /* Index of lowest non-C3 state. */ int cpu_short_slp; /* Count of < 1us sleeps. */ + int cpu_long_slp; /* Count of > hz sleeps. */ u_int cpu_cx_stats[MAX_CX_STATES];/* Cx usage history. */ /* Values for sysctl. */ struct sysctl_ctx_list cpu_sysctl_ctx; @@ -123,6 +124,9 @@ static int cpu_disable_idle; /* Disable entry to idle function */ static int cpu_cx_count; /* Number of valid Cx states */ +/* Longest allowable sleep */ +static int cpu_max_sleep = 0; + /* Values for sysctl. */ static struct sysctl_ctx_list cpu_sysctl_ctx; static struct sysctl_oid *cpu_sysctl_tree; @@ -278,6 +282,9 @@ cpu_smi_cmd = AcpiGbl_FADT->SmiCmd; cpu_cst_cnt = AcpiGbl_FADT->CstCnt; + sc->cpu_short_slp = 0; + sc->cpu_long_slp = 0; + buf.Pointer = NULL; buf.Length = ACPI_ALLOCATE_BUFFER; status = AcpiEvaluateObject(sc->cpu_handle, NULL, NULL, &buf); @@ -311,6 +318,10 @@ /* Queue post cpu-probing task handler */ AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cpu_startup, NULL); + + /* Setup the longest allowable sleep */ + cpu_max_sleep = 1000000 / hz; + cpu_max_sleep *= 10; } /* @@ -832,10 +843,17 @@ * that both cores will get out of C3 state as soon as one of them * requires it. This breaks the sleep detection logic as the sleep * counter is local to each cpu. Disable the sleep logic for now as a - * workaround if there's more than one CPU. The right fix would probably - * be to add quirks for system that don't really support C3 state. + * workaround if we have more than one cpus and some of them are. + * logical (more than one core per package). The right fix would + * probably be to add quirks for system that don't really support + * C3 state. */ - if (mp_ncpus < 2 && sc->cpu_prev_sleep <= 1) { +#ifdef SMP + if (((mp_ncpus >= 2 && logical_cpus_mask == 0) || mp_ncpus < 2) + && sc->cpu_prev_sleep <= 1) { +#else + if (sc->cpu_prev_sleep <= 1) { +#endif sc->cpu_short_slp++; if (sc->cpu_short_slp == 1000 && sc->cpu_cx_lowest != 0) { if (sc->cpu_non_c3 == sc->cpu_cx_lowest && sc->cpu_non_c3 != 0) @@ -849,6 +867,26 @@ } else sc->cpu_short_slp = 0; + /* + * If we keep sleeping for more than 1 hz it may indicate a broken + * system where the clock will not fire anymore in low Cx states. + * Keep backing off in this case until we start getting back clock + * interrupts. + */ + if (sc->cpu_prev_sleep > cpu_max_sleep) { + sc->cpu_long_slp++; + if (sc->cpu_long_slp == 10 && sc->cpu_cx_lowest != 0) { + if (sc->cpu_non_c3 == sc->cpu_cx_lowest && sc->cpu_non_c3 != 0) + sc->cpu_non_c3--; + sc->cpu_cx_lowest--; + sc->cpu_long_slp = 0; + device_printf(sc->cpu_dev, + "too many long sleeps, backing off to C%d\n", + sc->cpu_cx_lowest + 1); + } + } else + sc->cpu_long_slp = 0; + for (i = sc->cpu_cx_lowest; i >= 0; i--) if (sc->cpu_cx_states[i].trans_lat <= sc->cpu_prev_sleep) { cx_next_idx = i;