diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2016-08-16 20:19:05 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2016-08-16 20:19:05 +0000 |
commit | 27067774dce3388702a4cf744d7096c6fb71b688 (patch) | |
tree | 56300949abd050b3cd2e23d29210d2a567bc8aec /sys/x86 | |
parent | 44be0a8ea517cbe7a9140bca20e1e93228ac0a04 (diff) | |
parent | 915a263ea24ab051bb57674a4d6f5ffe4ef0d5b5 (diff) |
Merge ^/head r303250 through r304235.
Notes
Notes:
svn path=/projects/clang390-import/; revision=304236
Diffstat (limited to 'sys/x86')
-rw-r--r-- | sys/x86/cpufreq/powernow.c | 6 | ||||
-rw-r--r-- | sys/x86/iommu/intel_drv.c | 2 | ||||
-rw-r--r-- | sys/x86/iommu/intel_reg.h | 4 | ||||
-rw-r--r-- | sys/x86/x86/intr_machdep.c | 15 | ||||
-rw-r--r-- | sys/x86/x86/mp_watchdog.c | 210 | ||||
-rw-r--r-- | sys/x86/xen/xen_intr.c | 19 |
6 files changed, 228 insertions, 28 deletions
diff --git a/sys/x86/cpufreq/powernow.c b/sys/x86/cpufreq/powernow.c index cc62e87623f0..13ec9d846c6f 100644 --- a/sys/x86/cpufreq/powernow.c +++ b/sys/x86/cpufreq/powernow.c @@ -700,9 +700,9 @@ pn_decode_pst(device_t dev) if (sc->pn_type != PN8_TYPE) return (EINVAL); sc->vst = psb->settlingtime; - sc->rvo = PN8_PSB_TO_RVO(psb->res1), - sc->irt = PN8_PSB_TO_IRT(psb->res1), - sc->mvs = PN8_PSB_TO_MVS(psb->res1), + sc->rvo = PN8_PSB_TO_RVO(psb->res1); + sc->irt = PN8_PSB_TO_IRT(psb->res1); + sc->mvs = PN8_PSB_TO_MVS(psb->res1); sc->low = PN8_PSB_TO_BATT(psb->res1); if (bootverbose) { device_printf(dev, "PSB: VST: %d\n", diff --git a/sys/x86/iommu/intel_drv.c b/sys/x86/iommu/intel_drv.c index e5d7783658cb..5f7043bcec86 100644 --- a/sys/x86/iommu/intel_drv.c +++ b/sys/x86/iommu/intel_drv.c @@ -306,7 +306,7 @@ dmar_alloc_irq(device_t dev, struct dmar_unit *unit, int idx) dmd->name, error); goto err4; } - bus_describe_intr(dev, dmd->irq_res, dmd->intr_handle, dmd->name); + bus_describe_intr(dev, dmd->irq_res, dmd->intr_handle, "%s", dmd->name); error = PCIB_MAP_MSI(pcib, dev, dmd->irq, &msi_addr, &msi_data); if (error != 0) { device_printf(dev, "cannot map %s interrupt, %d\n", diff --git a/sys/x86/iommu/intel_reg.h b/sys/x86/iommu/intel_reg.h index 33e37ac23cc3..b9a70b6953bb 100644 --- a/sys/x86/iommu/intel_reg.h +++ b/sys/x86/iommu/intel_reg.h @@ -67,7 +67,9 @@ typedef struct dmar_ctx_entry { #define DMAR_CTX2_AW_4LVL 2 /* 4-level page tables */ #define DMAR_CTX2_AW_5LVL 3 /* 5-level page tables */ #define DMAR_CTX2_AW_6LVL 4 /* 6-level page tables */ +#define DMAR_CTX2_DID_MASK 0xffff0 #define DMAR_CTX2_DID(x) ((x) << 8) /* Domain Identifier */ +#define DMAR_CTX2_GET_DID(ctx2) (((ctx2) & DMAR_CTX2_DID_MASK) >> 8) typedef struct dmar_pte { uint64_t pte; @@ -214,6 +216,8 @@ typedef struct dmar_irte { /* Root-Entry Table Address register */ #define DMAR_RTADDR_REG 0x20 +#define DMAR_RTADDR_RTT (1 << 11) /* Root Table Type */ +#define DMAR_RTADDR_RTA_MASK 0xfffffffffffff000 /* Context Command register */ #define DMAR_CCMD_REG 0x28 diff --git a/sys/x86/x86/intr_machdep.c b/sys/x86/x86/intr_machdep.c index 1bc3038b77f3..dc84073ad053 100644 --- a/sys/x86/x86/intr_machdep.c +++ b/sys/x86/x86/intr_machdep.c @@ -197,28 +197,19 @@ int intr_remove_handler(void *cookie) { struct intsrc *isrc; - int error, mtx_owned; + int error; isrc = intr_handler_source(cookie); error = intr_event_remove_handler(cookie); if (error == 0) { - /* - * Recursion is needed here so PICs can remove interrupts - * while resuming. It was previously not possible due to - * intr_resume holding the intr_table_lock and - * intr_remove_handler recursing on it. - */ - mtx_owned = mtx_owned(&intr_table_lock); - if (mtx_owned == 0) - mtx_lock(&intr_table_lock); + mtx_lock(&intr_table_lock); isrc->is_handlers--; if (isrc->is_handlers == 0) { isrc->is_pic->pic_disable_source(isrc, PIC_NO_EOI); isrc->is_pic->pic_disable_intr(isrc); } intrcnt_updatename(isrc); - if (mtx_owned == 0) - mtx_unlock(&intr_table_lock); + mtx_unlock(&intr_table_lock); } return (error); } diff --git a/sys/x86/x86/mp_watchdog.c b/sys/x86/x86/mp_watchdog.c new file mode 100644 index 000000000000..0902f1c1062e --- /dev/null +++ b/sys/x86/x86/mp_watchdog.c @@ -0,0 +1,210 @@ +/*- + * Copyright (c) 2004 Robert N. M. Watson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "opt_mp_watchdog.h" +#include "opt_sched.h" + +#ifdef SCHED_ULE +#error MP_WATCHDOG cannot currently be used with SCHED_ULE +#endif + +#include <sys/param.h> +#include <sys/kdb.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/pcpu.h> +#include <sys/proc.h> +#include <sys/sysctl.h> +#include <sys/systm.h> + +#include <machine/smp.h> +#include <x86/apicreg.h> +#include <x86/apicvar.h> +#include <machine/mp_watchdog.h> + +/* + * mp_watchdog hijacks the idle thread on a specified CPU, prevents new work + * from being scheduled there, and uses it as a "watchdog" to detect kernel + * failure on other CPUs. This is made reasonable by inclusion of logical + * processors in Xeon hardware. The watchdog is configured by setting the + * debug.watchdog sysctl/tunable to the CPU of interest. A callout will then + * begin executing reseting a timer that is gradually lowered by the watching + * thread. If the timer reaches 0, the watchdog fires by ether dropping + * directly to the debugger, or by sending an NMI IPI to the boot processor. + * This is a somewhat less efficient substitute for dedicated watchdog + * hardware, but can be quite an effective tool for debugging hangs. + * + * XXXRW: This should really use the watchdog(9)/watchdog(4) framework, but + * doesn't yet. + */ +static int watchdog_cpu = -1; +static int watchdog_dontfire = 1; +static int watchdog_timer = -1; +static int watchdog_nmi = 1; + +SYSCTL_INT(_debug, OID_AUTO, watchdog_nmi, CTLFLAG_RWTUN, &watchdog_nmi, 0, + "IPI the boot processor with an NMI to enter the debugger"); + +static struct callout watchdog_callout; + +static void watchdog_change(int wdcpu); + +/* + * Number of seconds before the watchdog will fire if the callout fails to + * reset the timer. + */ +#define WATCHDOG_THRESHOLD 10 + +static void +watchdog_init(void *arg) +{ + + callout_init(&watchdog_callout, 1); + if (watchdog_cpu != -1) + watchdog_change(watchdog_cpu); +} + +/* + * This callout resets a timer until the watchdog kicks in. It acquires some + * critical locks to make sure things haven't gotten wedged with those locks + * held. + */ +static void +watchdog_function(void *arg) +{ + + /* + * Since the timer ran, we must not be wedged. Acquire some critical + * locks to make sure. Then reset the timer. + */ + mtx_lock(&Giant); + watchdog_timer = WATCHDOG_THRESHOLD; + mtx_unlock(&Giant); + callout_reset(&watchdog_callout, 1 * hz, watchdog_function, NULL); +} +SYSINIT(watchdog_init, SI_SUB_DRIVERS, SI_ORDER_ANY, watchdog_init, NULL); + +static void +watchdog_change(int wdcpu) +{ + + if (wdcpu == -1 || wdcpu == 0xffffffff) { + /* + * Disable the watchdog. + */ + watchdog_cpu = -1; + watchdog_dontfire = 1; + callout_stop(&watchdog_callout); + printf("watchdog stopped\n"); + } else { + watchdog_timer = WATCHDOG_THRESHOLD; + watchdog_dontfire = 0; + watchdog_cpu = wdcpu; + callout_reset(&watchdog_callout, 1 * hz, watchdog_function, + NULL); + } +} + +/* + * This sysctl sets which CPU is the watchdog CPU. Set to -1 or 0xffffffff + * to disable the watchdog. + */ +static int +sysctl_watchdog(SYSCTL_HANDLER_ARGS) +{ + int error, temp; + + temp = watchdog_cpu; + error = sysctl_handle_int(oidp, &temp, 0, req); + if (error) + return (error); + + if (req->newptr != NULL) + watchdog_change(temp); + return (0); +} +SYSCTL_PROC(_debug, OID_AUTO, watchdog, CTLTYPE_INT|CTLFLAG_RW, 0, 0, + sysctl_watchdog, "I", ""); + +/* + * Drop into the debugger by sending an IPI NMI to the boot processor. + */ +static void +watchdog_ipi_nmi(void) +{ + + /* + * Deliver NMI to the boot processor. Why not? + */ + lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_EDGE | + APIC_LEVEL_ASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_NMI, + boot_cpu_id); + lapic_ipi_wait(-1); +} + +/* + * ap_watchdog() is called by the SMP idle loop code. It works on the same + * premise that the disabling of logical processors does: that if the cpu is + * idle, then it can ignore the world from then on, as nothing will be + * scheduled on it. Leaving aside multi-runqueue schedulers (SCHED_ULE) and + * explicit process migration (sched_bind()), this is not an unreasonable + * assumption. + */ +void +ap_watchdog(u_int cpuid) +{ + char old_pcomm[MAXCOMLEN + 1]; + struct proc *p; + + if (watchdog_cpu != cpuid) + return; + + printf("watchdog started on cpu %d\n", cpuid); + p = curproc; + bcopy(p->p_comm, old_pcomm, MAXCOMLEN + 1); + snprintf(p->p_comm, MAXCOMLEN + 1, "mp_watchdog cpu %d", cpuid); + while (1) { + DELAY(1000000); /* One second. */ + if (watchdog_cpu != cpuid) + break; + atomic_subtract_int(&watchdog_timer, 1); + if (watchdog_timer < 4) + printf("Watchdog timer: %d\n", watchdog_timer); + if (watchdog_timer == 0 && watchdog_dontfire == 0) { + printf("Watchdog firing!\n"); + watchdog_dontfire = 1; + if (watchdog_nmi) + watchdog_ipi_nmi(); + else + kdb_enter(KDB_WHY_WATCHDOG, "mp_watchdog"); + } + } + bcopy(old_pcomm, p->p_comm, MAXCOMLEN + 1); + printf("watchdog stopped on cpu %d\n", cpuid); +} diff --git a/sys/x86/xen/xen_intr.c b/sys/x86/xen/xen_intr.c index a7b89fc7970f..13ada84a2b34 100644 --- a/sys/x86/xen/xen_intr.c +++ b/sys/x86/xen/xen_intr.c @@ -130,8 +130,6 @@ struct xenisrc { u_int xi_masked:1; }; -#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) - static void xen_intr_suspend(struct pic *); static void xen_intr_resume(struct pic *, bool suspend_cancelled); static void xen_intr_enable_source(struct intsrc *isrc); @@ -422,7 +420,7 @@ xen_intr_bind_isrc(struct xenisrc **isrcp, evtchn_port_t local_port, mtx_unlock(&xen_intr_isrc_lock); /* Assign the opaque handler (the event channel port) */ - *port_handlep = &isrc->xi_port; + *port_handlep = &isrc->xi_vector; #ifdef SMP if (type == EVTCHN_TYPE_PORT) { @@ -468,16 +466,17 @@ xen_intr_bind_isrc(struct xenisrc **isrcp, evtchn_port_t local_port, static struct xenisrc * xen_intr_isrc(xen_intr_handle_t handle) { - evtchn_port_t port; + int vector; if (handle == NULL) return (NULL); - port = *(evtchn_port_t *)handle; - if (!is_valid_evtchn(port) || port >= NR_EVENT_CHANNELS) - return (NULL); + vector = *(int *)handle; + KASSERT(vector >= FIRST_EVTCHN_INT && + vector < (FIRST_EVTCHN_INT + xen_intr_auto_vector_count), + ("Xen interrupt vector is out of range")); - return (xen_intr_port_to_isrc[port]); + return ((struct xenisrc *)intr_lookup_source(vector)); } /** @@ -780,10 +779,6 @@ xen_intr_resume(struct pic *unused, bool suspend_cancelled) xen_rebind_virq(isrc); break; default: - intr_remove_handler(isrc->xi_cookie); - isrc->xi_cpu = 0; - isrc->xi_type = EVTCHN_TYPE_UNBOUND; - isrc->xi_cookie = NULL; break; } } |