VirtualBox

Changeset 83521 in vbox for trunk/src/VBox/Devices/Serial


Ignore:
Timestamp:
Apr 3, 2020 8:36:26 AM (5 years ago)
Author:
vboxsync
Message:

Devices/Serial/DrvHostSerial: Show a warning when the serial port encounters a fatal error and stop any I/O activity. Upon a suspend/resume cycle the serial port is reopened if a fatal error was encountered previously to try restarting I/O.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Serial/DrvHostSerial.cpp

    r82968 r83521  
    6262    /** the device path */
    6363    char                        *pszDevicePath;
     64    /** The active config of the serial port. */
     65    RTSERIALPORTCFG             Cfg;
    6466
    6567    /** Flag whether data is available from the device/driver above as notified by the driver. */
     
    8183    volatile size_t             cbReadBuf;
    8284
     85    /* Flag whether the host device ran into a fatal error condition and I/O is suspended
     86     * until the nuext VM suspend/resume cycle where we will try again. */
     87    volatile bool               fIoFatalErr;
     88    /** Event semaphore the I/O thread is waiting on */
     89    RTSEMEVENT                  hSemEvtIoFatalErr;
     90
    8391    /** Read/write statistics */
    8492    STAMCOUNTER                 StatBytesRead;
     
    186194
    187195
     196/**
     197 * Wakes up the serial port I/O thread.
     198 *
     199 * @returns VBox status code.
     200 * @param   pThis               The host serial driver instance.
     201 */
     202static int drvHostSerialWakeupIoThread(PDRVHOSTSERIAL pThis)
     203{
     204
     205    if (RT_UNLIKELY(pThis->fIoFatalErr))
     206        return RTSemEventSignal(pThis->hSemEvtIoFatalErr);
     207
     208    return RTSerialPortEvtPollInterrupt(pThis->hSerialPort);
     209}
     210
     211
    188212/* -=-=-=-=- IBase -=-=-=-=- */
    189213
     
    212236    bool fAvailOld = ASMAtomicXchgBool(&pThis->fAvailWrExt, true);
    213237    if (!fAvailOld)
    214         rc = RTSerialPortEvtPollInterrupt(pThis->hSerialPort);
     238        rc = drvHostSerialWakeupIoThread(pThis);
    215239
    216240    return rc;
     
    248272    /* Kick the I/O thread if there is nothing to read to recalculate the poll flags. */
    249273    if (!drvHostSerialReadBufGetRead(pThis, NULL))
    250         rc = RTSerialPortEvtPollInterrupt(pThis->hSerialPort);
     274        rc = drvHostSerialWakeupIoThread(pThis);
    251275
    252276    STAM_COUNTER_ADD(&pThis->StatBytesRead, cbReadAll);
     
    263287{
    264288    PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector);
    265     RTSERIALPORTCFG Cfg;
    266 
    267     Cfg.uBaudRate = uBps;
     289
     290    pThis->Cfg.uBaudRate = uBps;
    268291
    269292    switch (enmParity)
    270293    {
    271294        case PDMSERIALPARITY_EVEN:
    272             Cfg.enmParity = RTSERIALPORTPARITY_EVEN;
     295            pThis->Cfg.enmParity = RTSERIALPORTPARITY_EVEN;
    273296            break;
    274297        case PDMSERIALPARITY_ODD:
    275             Cfg.enmParity = RTSERIALPORTPARITY_ODD;
     298            pThis->Cfg.enmParity = RTSERIALPORTPARITY_ODD;
    276299            break;
    277300        case PDMSERIALPARITY_NONE:
    278             Cfg.enmParity = RTSERIALPORTPARITY_NONE;
     301            pThis->Cfg.enmParity = RTSERIALPORTPARITY_NONE;
    279302            break;
    280303        case PDMSERIALPARITY_MARK:
    281             Cfg.enmParity = RTSERIALPORTPARITY_MARK;
     304            pThis->Cfg.enmParity = RTSERIALPORTPARITY_MARK;
    282305            break;
    283306        case PDMSERIALPARITY_SPACE:
    284             Cfg.enmParity = RTSERIALPORTPARITY_SPACE;
     307            pThis->Cfg.enmParity = RTSERIALPORTPARITY_SPACE;
    285308            break;
    286309        default:
    287310            AssertMsgFailed(("Unsupported parity setting %d\n", enmParity)); /* Should not happen. */
    288             Cfg.enmParity = RTSERIALPORTPARITY_NONE;
     311            pThis->Cfg.enmParity = RTSERIALPORTPARITY_NONE;
    289312    }
    290313
     
    292315    {
    293316        case 5:
    294             Cfg.enmDataBitCount = RTSERIALPORTDATABITS_5BITS;
     317            pThis->Cfg.enmDataBitCount = RTSERIALPORTDATABITS_5BITS;
    295318            break;
    296319        case 6:
    297             Cfg.enmDataBitCount = RTSERIALPORTDATABITS_6BITS;
     320            pThis->Cfg.enmDataBitCount = RTSERIALPORTDATABITS_6BITS;
    298321            break;
    299322        case 7:
    300             Cfg.enmDataBitCount = RTSERIALPORTDATABITS_7BITS;
     323            pThis->Cfg.enmDataBitCount = RTSERIALPORTDATABITS_7BITS;
    301324            break;
    302325        case 8:
    303             Cfg.enmDataBitCount = RTSERIALPORTDATABITS_8BITS;
     326            pThis->Cfg.enmDataBitCount = RTSERIALPORTDATABITS_8BITS;
    304327            break;
    305328        default:
    306329            AssertMsgFailed(("Unsupported data bit count %u\n", cDataBits)); /* Should not happen. */
    307             Cfg.enmDataBitCount = RTSERIALPORTDATABITS_8BITS;
     330            pThis->Cfg.enmDataBitCount = RTSERIALPORTDATABITS_8BITS;
    308331    }
    309332
     
    311334    {
    312335        case PDMSERIALSTOPBITS_ONE:
    313             Cfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONE;
     336            pThis->Cfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONE;
    314337            break;
    315338        case PDMSERIALSTOPBITS_ONEPOINTFIVE:
    316             Cfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONEPOINTFIVE;
     339            pThis->Cfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONEPOINTFIVE;
    317340            break;
    318341        case PDMSERIALSTOPBITS_TWO:
    319             Cfg.enmStopBitCount = RTSERIALPORTSTOPBITS_TWO;
     342            pThis->Cfg.enmStopBitCount = RTSERIALPORTSTOPBITS_TWO;
    320343            break;
    321344        default:
    322345            AssertMsgFailed(("Unsupported stop bit count %d\n", enmStopBits)); /* Should not happen. */
    323             Cfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONE;
    324     }
    325 
    326     return RTSerialPortCfgSet(pThis->hSerialPort, &Cfg, NULL);
     346            pThis->Cfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONE;
     347    }
     348
     349    return RTSerialPortCfgSet(pThis->hSerialPort, &pThis->Cfg, NULL);
    327350}
    328351
     
    388411        size_t cbOld = drvHostSerialReadBufReset(pThis);
    389412        if (cbOld) /* Kick the I/O thread to fetch new data. */
    390             rc = RTSerialPortEvtPollInterrupt(pThis->hSerialPort);
     413            rc = drvHostSerialWakeupIoThread(pThis);
    391414    }
    392415
     
    399422
    400423/**
    401  * I/O thread loop.
    402  *
    403  * @returns VINF_SUCCESS.
    404  * @param   pDrvIns     PDM driver instance data.
    405  * @param   pThread     The PDM thread data.
    406  */
    407 static DECLCALLBACK(int) drvHostSerialIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
    408 {
    409     PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
    410 
    411     if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
    412         return VINF_SUCCESS;
    413 
    414     while (pThread->enmState == PDMTHREADSTATE_RUNNING)
     424 * The normal I/O loop.
     425 *
     426 * @returns VBox status code.
     427 * @param   pDrvIns             Pointer to the driver instance data.
     428 * @param   pThis               Host serial driver instance data.
     429 * @param   pThread             Thread instance data.
     430 */
     431static int drvHostSerialIoLoopNormal(PPDMDRVINS pDrvIns, PDRVHOSTSERIAL pThis, PPDMTHREAD pThread)
     432{
     433    int rc = VINF_SUCCESS;
     434    while (   pThread->enmState == PDMTHREADSTATE_RUNNING
     435           && RT_SUCCESS(rc))
    415436    {
    416437        uint32_t fEvtFlags = RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED | RTSERIALPORT_EVT_F_BREAK_DETECTED;
     
    429450
    430451        uint32_t fEvtsRecv = 0;
    431         int rc = RTSerialPortEvtPoll(pThis->hSerialPort, fEvtFlags, &fEvtsRecv, RT_INDEFINITE_WAIT);
     452        rc = RTSerialPortEvtPoll(pThis->hSerialPort, fEvtFlags, &fEvtsRecv, RT_INDEFINITE_WAIT);
    432453        if (RT_SUCCESS(rc))
    433454        {
     
    464485                        {
    465486                            /* Move the data in the TX buffer to the front to fill the end again. */
    466                             memmove(&pThis->abTxBuf[0], &pThis->abTxBuf[cbProcessed], pThis->cbTxUsed);                        }
     487                            memmove(&pThis->abTxBuf[0], &pThis->abTxBuf[cbProcessed], pThis->cbTxUsed);
     488                        }
    467489                        else
    468490                            pThis->pDrvSerialPort->pfnDataSentNotify(pThis->pDrvSerialPort);
     
    531553                        LogRelMax(10, ("HostSerial#%d: Notifying device about changed status lines failed with error %Rrc; continuing.\n",
    532554                                       pDrvIns->iInstance, rc));
     555                        rc = VINF_SUCCESS;
    533556                    }
    534557                }
    535558                else
     559                {
    536560                    LogRelMax(10, ("HostSerial#%d: Getting status lines state failed with error %Rrc; continuing.\n", pDrvIns->iInstance, rc));
     561                    rc = VINF_SUCCESS;
     562                }
    537563            }
    538564
     
    547573    }
    548574
     575    LogRel(("HostSerial#%d: The underlying host device run into a fatal error condition %Rrc, any data transfer is disabled\n",
     576            pDrvIns->iInstance, rc));
     577
     578    return rc;
     579}
     580
     581
     582/**
     583 * The error I/O loop.
     584 *
     585 * @returns VBox status code.
     586 * @param   pDrvIns             Pointer to the driver instance data.
     587 * @param   pThis               Host serial driver instance data.
     588 * @param   pThread             Thread instance data.
     589 */
     590static void drvHostSerialIoLoopError(PDRVHOSTSERIAL pThis, PPDMTHREAD pThread)
     591{
     592    ASMAtomicXchgBool(&pThis->fIoFatalErr, true);
     593
     594    PDMDrvHlpVMSetRuntimeError(pThis->pDrvIns, 0 /*fFlags*/, "SerialPortIoError",
     595                               N_("The host serial port \"%s\" encountered a fatal error and stopped functioning. "
     596                                  "This can be caused by bad cabling or USB to serial converters being unplugged by accident. "
     597                                  "To restart I/O transfers suspend and resume the VM after fixing the underlying issue."),
     598                               pThis->pszDevicePath);
     599
     600    while (pThread->enmState == PDMTHREADSTATE_RUNNING)
     601    {
     602        /*
     603         * We have to discard any data which is going to be send (the error
     604         * mode resembles the "someone just pulled the plug on the serial port" situation)
     605         */
     606        RTSemEventWait(pThis->hSemEvtIoFatalErr, RT_INDEFINITE_WAIT);
     607
     608        if (ASMAtomicXchgBool(&pThis->fAvailWrExt, false))
     609        {
     610            size_t cbFetched = 0;
     611
     612            do
     613            {
     614                /* Stuff as much data into the TX buffer as we can. */
     615                uint8_t abDiscard[64];
     616                int rc = pThis->pDrvSerialPort->pfnReadWr(pThis->pDrvSerialPort, &abDiscard, sizeof(abDiscard),
     617                                                          &cbFetched);
     618                AssertRC(rc);
     619            } while (cbFetched > 0);
     620
     621            /* Acknowledge the sent data. */
     622            pThis->pDrvSerialPort->pfnDataSentNotify(pThis->pDrvSerialPort);
     623
     624            /*
     625             * Sleep a bit to avoid excessive I/O loop CPU usage, timing is not important in
     626             * this mode.
     627             */
     628            PDMR3ThreadSleep(pThread, 100);
     629        }
     630    }
     631}
     632
     633
     634/**
     635 * I/O thread loop.
     636 *
     637 * @returns VINF_SUCCESS.
     638 * @param   pDrvIns     PDM driver instance data.
     639 * @param   pThread     The PDM thread data.
     640 */
     641static DECLCALLBACK(int) drvHostSerialIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
     642{
     643    PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
     644
     645    if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
     646        return VINF_SUCCESS;
     647
     648    int rc = VINF_SUCCESS;
     649    if (!pThis->fIoFatalErr)
     650        rc = drvHostSerialIoLoopNormal(pDrvIns, pThis, pThread);
     651
     652    if (   RT_FAILURE(rc)
     653        || pThis->fIoFatalErr)
     654        drvHostSerialIoLoopError(pThis, pThread);
     655
    549656    return VINF_SUCCESS;
    550657}
     
    563670    PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
    564671
    565     return RTSerialPortEvtPollInterrupt(pThis->hSerialPort);
     672    return drvHostSerialWakeupIoThread(pThis);
    566673}
    567674
    568675
    569676/* -=-=-=-=- driver interface -=-=-=-=- */
     677
     678/**
     679 * @callback_method_impl{FNPDMDRVRESUME}
     680 */
     681static DECLCALLBACK(void) drvHostSerialResume(PPDMDRVINS pDrvIns)
     682{
     683    PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
     684
     685    if (RT_UNLIKELY(pThis->fIoFatalErr))
     686    {
     687        /* Try to reopen the device and set the old config. */
     688        uint32_t fOpenFlags =   RTSERIALPORT_OPEN_F_READ
     689                              | RTSERIALPORT_OPEN_F_WRITE
     690                              | RTSERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING
     691                              | RTSERIALPORT_OPEN_F_DETECT_BREAK_CONDITION;
     692        int rc = RTSerialPortOpen(&pThis->hSerialPort, pThis->pszDevicePath, fOpenFlags);
     693        if (rc == VERR_NOT_SUPPORTED)
     694        {
     695            /*
     696             * For certain devices (or pseudo terminals) status line monitoring does not work
     697             * so try again without it.
     698             */
     699            fOpenFlags &= ~RTSERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING;
     700            rc = RTSerialPortOpen(&pThis->hSerialPort, pThis->pszDevicePath, fOpenFlags);
     701        }
     702
     703        if (RT_SUCCESS(rc))
     704        {
     705            /* Set the config which is currently active. */
     706            rc = RTSerialPortCfgSet(pThis->hSerialPort, &pThis->Cfg, NULL);
     707            if (RT_FAILURE(rc))
     708                LogRelMax(10, ("HostSerial#%d: Setting the active serial port config failed with error %Rrc during VM resume; continuing.\n", pDrvIns->iInstance, rc));
     709            /* Reset the I/O error flag on success to resume the normal I/O thread loop. */
     710            ASMAtomicXchgBool(&pThis->fIoFatalErr, false);
     711        }
     712    }
     713}
     714
     715
     716/**
     717 * @callback_method_impl{FNPDMDRVSUSPEND}
     718 */
     719static DECLCALLBACK(void) drvHostSerialSuspend(PPDMDRVINS pDrvIns)
     720{
     721    PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
     722
     723    if (RT_UNLIKELY(pThis->fIoFatalErr))
     724    {
     725        /* Close the device and try reopening it on resume. */
     726        if (pThis->hSerialPort != NIL_RTSERIALPORT)
     727        {
     728            RTSerialPortClose(pThis->hSerialPort);
     729            pThis->hSerialPort = NIL_RTSERIALPORT;
     730        }
     731    }
     732}
     733
    570734
    571735/**
     
    587751        RTSerialPortClose(pThis->hSerialPort);
    588752        pThis->hSerialPort = NIL_RTSERIALPORT;
     753    }
     754
     755    if (pThis->hSemEvtIoFatalErr != NIL_RTSEMEVENT)
     756    {
     757        RTSemEventDestroy(pThis->hSemEvtIoFatalErr);
     758        pThis->hSemEvtIoFatalErr = NIL_RTSEMEVENT;
    589759    }
    590760
     
    620790    pThis->offRead                               = 0;
    621791    pThis->cbReadBuf                             = 0;
     792    pThis->fIoFatalErr                           = false;
     793    pThis->hSemEvtIoFatalErr                     = NIL_RTSEMEVENT;
    622794    /* IBase. */
    623795    pDrvIns->IBase.pfnQueryInterface             = drvHostSerialQueryInterface;
     
    683855        }
    684856    }
     857
     858    rc = RTSemEventCreate(&pThis->hSemEvtIoFatalErr);
     859    if (RT_FAILURE(rc))
     860        return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d failed to create event semaphore"), pDrvIns->iInstance);
    685861
    686862    /*
     
    745921    NULL,
    746922    /* pfnSuspend */
    747     NULL,
     923    drvHostSerialSuspend,
    748924    /* pfnResume */
    749     NULL,
     925    drvHostSerialResume,
    750926    /* pfnAttach */
    751927    NULL,
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette