From owner-svn-src-head@FreeBSD.ORG Wed Jul 10 17:42:21 2013 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) by hub.freebsd.org (Postfix) with ESMTP id 0F9052B1; Wed, 10 Jul 2013 17:42:21 +0000 (UTC) (envelope-from marcel@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) by mx1.freebsd.org (Postfix) with ESMTP id E59F91054; Wed, 10 Jul 2013 17:42:20 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.7/8.14.7) with ESMTP id r6AHgKmm024114; Wed, 10 Jul 2013 17:42:20 GMT (envelope-from marcel@svn.freebsd.org) Received: (from marcel@localhost) by svn.freebsd.org (8.14.7/8.14.5/Submit) id r6AHgKOm024113; Wed, 10 Jul 2013 17:42:20 GMT (envelope-from marcel@svn.freebsd.org) Message-Id: <201307101742.r6AHgKOm024113@svn.freebsd.org> From: Marcel Moolenaar Date: Wed, 10 Jul 2013 17:42:20 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r253161 - head/sys/dev/uart X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 10 Jul 2013 17:42:21 -0000 Author: marcel Date: Wed Jul 10 17:42:20 2013 New Revision: 253161 URL: http://svnweb.freebsd.org/changeset/base/253161 Log: Protect against broken hardware. In this particular case, protect against H/W not de-asserting the interrupt at all. On x86, and because of the following conditions, this results in a hard hang with interrupts disabled: 1. The uart(4) driver uses a spin lock to protect against concurrent access to the H/W. Spin locks disable and restore interrupts. 2. Restoring the interrupt on x86 always writes the flags register. Even if we're restoring the interrupt from disabled to disabled. 3. The x86 CPU has a short window in which interrupts are enabled when the flags register is written. 4. The uart(4) driver registers a fast interrupt by default. To catch this case, we first try to clear any pending H/W interrupts and in particular, before setting up the interrupt. This makes sure the interrupt is masked on the PIC. The interrupt handler now has a limit set on the number of iterations it'll go through to clear interrupt conditions. If the limit is hit, the handler will return FILTER_SCHEDULE_THREAD. The attach function will check for this return code and avoid setting up the interrupt and foce polling in that case. Obtained from: Juniper Networks, Inc. Modified: head/sys/dev/uart/uart_core.c Modified: head/sys/dev/uart/uart_core.c ============================================================================== --- head/sys/dev/uart/uart_core.c Wed Jul 10 17:16:10 2013 (r253160) +++ head/sys/dev/uart/uart_core.c Wed Jul 10 17:42:20 2013 (r253161) @@ -248,10 +248,14 @@ static int uart_intr(void *arg) { struct uart_softc *sc = arg; - int flag = 0, ipend; + int cnt, ipend; - while (!sc->sc_leaving && (ipend = UART_IPEND(sc)) != 0) { - flag = 1; + if (sc->sc_leaving) + return (FILTER_STRAY); + + cnt = 0; + while (cnt < 20 && (ipend = UART_IPEND(sc)) != 0) { + cnt++; if (ipend & SER_INT_OVERRUN) uart_intr_overrun(sc); if (ipend & SER_INT_BREAK) @@ -269,7 +273,8 @@ uart_intr(void *arg) (timeout_t *)uart_intr, sc); } - return((flag)?FILTER_HANDLED:FILTER_STRAY); + return ((cnt == 0) ? FILTER_STRAY : + ((cnt == 20) ? FILTER_SCHEDULE_THREAD : FILTER_HANDLED)); } serdev_intr_t * @@ -390,7 +395,7 @@ uart_bus_attach(device_t dev) { struct uart_softc *sc, *sc0; const char *sep; - int error; + int error, filt; /* * The sc_class field defines the type of UART we're going to work @@ -430,33 +435,6 @@ uart_bus_attach(device_t dev) sc->sc_bas.bsh = rman_get_bushandle(sc->sc_rres); sc->sc_bas.bst = rman_get_bustag(sc->sc_rres); - sc->sc_irid = 0; - sc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irid, - RF_ACTIVE | RF_SHAREABLE); - if (sc->sc_ires != NULL) { - error = bus_setup_intr(dev, - sc->sc_ires, INTR_TYPE_TTY, - uart_intr, NULL, sc, &sc->sc_icookie); - if (error) - error = bus_setup_intr(dev, - sc->sc_ires, INTR_TYPE_TTY | INTR_MPSAFE, - NULL, (driver_intr_t *)uart_intr, sc, &sc->sc_icookie); - else - sc->sc_fastintr = 1; - - if (error) { - device_printf(dev, "could not activate interrupt\n"); - bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, - sc->sc_ires); - sc->sc_ires = NULL; - } - } - if (sc->sc_ires == NULL) { - /* No interrupt resource. Force polled mode. */ - sc->sc_polled = 1; - callout_init(&sc->sc_timer, 1); - } - /* * Ensure there is room for at least three full FIFOs of data in the * receive buffer (handles the case of low-level drivers with huge @@ -487,20 +465,6 @@ uart_bus_attach(device_t dev) printf("\n"); } - if (bootverbose && (sc->sc_fastintr || sc->sc_polled)) { - sep = ""; - device_print_prettyname(dev); - if (sc->sc_fastintr) { - printf("%sfast interrupt", sep); - sep = ", "; - } - if (sc->sc_polled) { - printf("%spolled mode", sep); - sep = ", "; - } - printf("\n"); - } - if (sc->sc_sysdev != NULL) { if (sc->sc_sysdev->baudrate == 0) { if (UART_IOCTL(sc, UART_IOCTL_BAUD, @@ -529,6 +493,56 @@ uart_bus_attach(device_t dev) sc->sc_pps.ppscap = PPS_CAPTUREBOTH; pps_init(&sc->sc_pps); + sc->sc_leaving = 0; + filt = uart_intr(sc); + + /* + * Don't use interrupts if we couldn't clear any pending interrupt + * conditions. We may have broken H/W and polling is probably the + * safest thing to do. + */ + if (filt != FILTER_SCHEDULE_THREAD) { + sc->sc_irid = 0; + sc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &sc->sc_irid, RF_ACTIVE | RF_SHAREABLE); + } + if (sc->sc_ires != NULL) { + error = bus_setup_intr(dev, sc->sc_ires, INTR_TYPE_TTY, + uart_intr, NULL, sc, &sc->sc_icookie); + sc->sc_fastintr = (error == 0) ? 1 : 0; + + if (!sc->sc_fastintr) + error = bus_setup_intr(dev, sc->sc_ires, + INTR_TYPE_TTY | INTR_MPSAFE, NULL, + (driver_intr_t *)uart_intr, sc, &sc->sc_icookie); + + if (error) { + device_printf(dev, "could not activate interrupt\n"); + bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, + sc->sc_ires); + sc->sc_ires = NULL; + } + } + if (sc->sc_ires == NULL) { + /* No interrupt resource. Force polled mode. */ + sc->sc_polled = 1; + callout_init(&sc->sc_timer, 1); + } + + if (bootverbose && (sc->sc_fastintr || sc->sc_polled)) { + sep = ""; + device_print_prettyname(dev); + if (sc->sc_fastintr) { + printf("%sfast interrupt", sep); + sep = ", "; + } + if (sc->sc_polled) { + printf("%spolled mode", sep); + sep = ", "; + } + printf("\n"); + } + error = (sc->sc_sysdev != NULL && sc->sc_sysdev->attach != NULL) ? (*sc->sc_sysdev->attach)(sc) : uart_tty_attach(sc); if (error) @@ -537,8 +551,6 @@ uart_bus_attach(device_t dev) if (sc->sc_sysdev != NULL) sc->sc_sysdev->hwmtx = sc->sc_hwmtx; - sc->sc_leaving = 0; - uart_intr(sc); return (0); fail: