VirtualBox

Ignore:
Timestamp:
Aug 12, 2010 3:48:14 PM (15 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
64735
Message:

iSCSI: Proper handling of lost connections. Reset the state and resend all active tasks after the connection was successfully established again. Handle commands which succeed but have sense data properly

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Storage/ISCSIHDDCore.cpp

    r31588 r31608  
    342342    /** Length of Target2Initiator data buffer. */
    343343    size_t          cbT2IData;
    344     /** Length of sense buffer. */
     344    /** Length of sense buffer
     345     * This contains the number of sense bytes received upon completion. */
    345346    size_t          cbSense;
    346347    /** Completion status of the command. */
     
    376377    /** The sense buffer. */
    377378    uint8_t         abSense[96];
     379    /** Status code to return if we got sense data. */
     380    int             rcSense;
     381    /** Number of retries if the command completes with sense
     382     * data before we return with an error.
     383     */
     384    unsigned        cSenseRetries;
    378385    /** The number of entries in the I2T S/G list. */
    379386    unsigned        cI2TSegs;
     
    661668static uint32_t iscsiNewITT(PISCSIIMAGE pImage);
    662669static int iscsiSendPDU(PISCSIIMAGE pImage, PISCSIREQ paReq, uint32_t cnReq, uint32_t uFlags);
    663 static int iscsiRecvPDU(PISCSIIMAGE pImage, uint32_t itt, PISCSIRES paRes, uint32_t cnRes, bool fSelect);
     670static int iscsiRecvPDU(PISCSIIMAGE pImage, uint32_t itt, PISCSIRES paRes, uint32_t cnRes);
    664671static int iscsiRecvPDUAsync(PISCSIIMAGE pImage);
    665672static int iscsiSendPDUAsync(PISCSIIMAGE pImage);
     
    777784
    778785    return pIScsiCmd;
     786}
     787
     788/**
     789 * Removes all commands from the table and returns the
     790 * list head
     791 *
     792 * @returns Pointer to the head of teh command list.
     793 * @param   pImage    iSCSI connection to use.
     794 */
     795static PISCSICMD iscsiCmdRemoveAll(PISCSIIMAGE pImage)
     796{
     797    PISCSICMD pIScsiCmdHead = NULL;
     798
     799    for (unsigned idx = 0; idx < RT_ELEMENTS(pImage->aCmdsWaiting); idx++)
     800    {
     801        PISCSICMD pHead;
     802        PISCSICMD pTail;
     803
     804        pHead = pImage->aCmdsWaiting[idx];
     805        pImage->aCmdsWaiting[idx] = NULL;
     806
     807        /* Get the tail. */
     808        pTail = pHead;
     809        while (pTail->pNext)
     810            pTail = pTail->pNext;
     811
     812        /* Concatenate. */
     813        pTail->pNext = pIScsiCmdHead;
     814        pIScsiCmdHead = pHead;
     815    }
     816
     817    return pIScsiCmdHead;
    779818}
    780819
     
    829868
    830869
    831 static int iscsiTransportRead(PISCSIIMAGE pImage, PISCSIRES paResponse, unsigned int cnResponse, bool fSelect)
     870static int iscsiTransportRead(PISCSIIMAGE pImage, PISCSIRES paResponse, unsigned int cnResponse)
    832871{
    833872    int rc = VINF_SUCCESS;
     
    860899            }
    861900            Assert(cMilliesRemaining < 1000000);
    862             if (fSelect)
    863             {
    864                 rc = pImage->pInterfaceNetCallbacks->pfnSelectOne(pImage->Socket,
    865                                                                   cMilliesRemaining);
    866                 if (RT_FAILURE(rc))
    867                     break;
    868             }
     901            rc = pImage->pInterfaceNetCallbacks->pfnSelectOne(pImage->Socket,
     902                                                              cMilliesRemaining);
     903            if (RT_FAILURE(rc))
     904                break;
    869905            rc = pImage->pInterfaceNetCallbacks->pfnRead(pImage->Socket,
    870906                                                         pDst, residual,
     
    13511387            cnISCSIRes++;
    13521388
    1353             rc = iscsiRecvPDU(pImage, itt, aISCSIRes, cnISCSIRes, true);
     1389            rc = iscsiRecvPDU(pImage, itt, aISCSIRes, cnISCSIRes);
    13541390            if (RT_FAILURE(rc))
    13551391                break;
     
    16421678            aISCSIRes.pvSeg = aResBHS;
    16431679            aISCSIRes.cbSeg = sizeof(aResBHS);
    1644             rc = iscsiRecvPDU(pImage, itt, &aISCSIRes, 1, true);
     1680            rc = iscsiRecvPDU(pImage, itt, &aISCSIRes, 1);
    16451681            if (RT_SUCCESS(rc))
    16461682            {
     
    18021838        cnISCSIRes++;
    18031839
    1804         rc = iscsiRecvPDU(pImage, itt, aISCSIRes, cnISCSIRes, true);
     1840        rc = iscsiRecvPDU(pImage, itt, aISCSIRes, cnISCSIRes);
    18051841        if (RT_FAILURE(rc))
    18061842            break;
     
    19812017 * @param   cnRes       Number of valid iSCSI response sections in the array.
    19822018 */
    1983 static int iscsiRecvPDU(PISCSIIMAGE pImage, uint32_t itt, PISCSIRES paRes, uint32_t cnRes, bool fSelect)
     2019static int iscsiRecvPDU(PISCSIIMAGE pImage, uint32_t itt, PISCSIRES paRes, uint32_t cnRes)
    19842020{
    19852021    int rc = VINF_SUCCESS;
     
    19902026        aResBuf.pvSeg = pImage->pvRecvPDUBuf;
    19912027        aResBuf.cbSeg = pImage->cbRecvPDUBuf;
    1992         rc = iscsiTransportRead(pImage, &aResBuf, 1, fSelect);
     2028        rc = iscsiTransportRead(pImage, &aResBuf, 1);
    19932029        if (RT_FAILURE(rc))
    19942030        {
     
    21452181
    21462182/**
     2183 * Reset the PDU buffer
     2184 *
     2185 * @param   pImage      The iSCSI connection state to be used.
     2186 */
     2187static void iscsiRecvPDUReset(PISCSIIMAGE pImage)
     2188{
     2189    pImage->cbRecvPDUResidual = ISCSI_BHS_SIZE;
     2190    pImage->fRecvPDUBHS       = true;
     2191    pImage->pbRecvPDUBufCur   = (uint8_t *)pImage->pvRecvPDUBuf;
     2192}
     2193
     2194static void iscsiPDUTxAdd(PISCSIIMAGE pImage, PISCSIPDUTX pIScsiPDUTx, bool fFront)
     2195{
     2196    if (!fFront)
     2197    {
     2198        /* Link the PDU at the tail of the list. */
     2199        if (!pImage->pIScsiPDUTxHead)
     2200            pImage->pIScsiPDUTxHead = pIScsiPDUTx;
     2201        else
     2202            pImage->pIScsiPDUTxTail->pNext = pIScsiPDUTx;
     2203        pImage->pIScsiPDUTxTail = pIScsiPDUTx;
     2204    }
     2205    else
     2206    {
     2207        /* Link PDU to at the front of the list. */
     2208        pIScsiPDUTx->pNext = pImage->pIScsiPDUTxHead;
     2209        pImage->pIScsiPDUTxHead = pIScsiPDUTx;
     2210        if (!pImage->pIScsiPDUTxTail)
     2211            pImage->pIScsiPDUTxTail = pIScsiPDUTx;
     2212    }
     2213}
     2214
     2215/**
    21472216 * Receives a PDU in a non blocking way.
    21482217 *
     
    21622231        /*
    21632232         * We are receiving a new PDU, don't read more than the BHS initially
    2164          * until we now the real size of the PDU.
     2233         * until we know the real size of the PDU.
    21652234         */
    2166         pImage->cbRecvPDUResidual = ISCSI_BHS_SIZE;
    2167         pImage->fRecvPDUBHS       = true;
    2168         pImage->pbRecvPDUBufCur   = (uint8_t *)pImage->pvRecvPDUBuf;
     2235        iscsiRecvPDUReset(pImage);
    21692236        LogFlow(("Receiving new PDU\n"));
    21702237    }
     
    23782445
    23792446                    /* Link the PDU to the list. */
    2380                     if (!pImage->pIScsiPDUTxHead)
    2381                         pImage->pIScsiPDUTxHead = pIScsiPDUTx;
    2382                     else
    2383                         pImage->pIScsiPDUTxTail->pNext = pIScsiPDUTx;
    2384                     pImage->pIScsiPDUTxTail = pIScsiPDUTx;
     2447                    iscsiPDUTxAdd(pImage, pIScsiPDUTx, false /* fFront */);
    23852448
    23862449                    /* Start transfer of a PDU if there is no one active at the moment. */
     
    24682531            break;
    24692532        case ISCSIOP_ASYN_MSG:
    2470             /* Asynchronous Messages must not have the final bit unser and may not contain
     2533            /* Asynchronous Messages must not have the final bit unset and may not contain
    24712534             * an initiator task tag. */
    24722535            if (    ((hw0 & ISCSI_FINAL_BIT) == 0)
     
    24902553}
    24912554
     2555
    24922556/**
    24932557 * Prepares a PDU to transfer for the given command and adds it to the list.
     
    24982562    uint32_t *paReqBHS;
    24992563    size_t cbData = 0;
     2564    size_t cbSegs = 0;
    25002565    PSCSIREQ pScsiReq;
    25012566    PISCSIPDUTX pIScsiPDU = NULL;
     
    25472612    pIScsiPDU->aISCSIReq[cnISCSIReq].pvSeg = pIScsiPDU->aBHS;
    25482613    cnISCSIReq++;
     2614    cbSegs = sizeof(pIScsiPDU->aBHS);
    25492615    /* Padding is not necessary for the BHS. */
    25502616
     
    25562622            pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg = pScsiReq->paI2TSegs[cSeg].cbSeg;
    25572623            pIScsiPDU->aISCSIReq[cnISCSIReq].pvSeg = pScsiReq->paI2TSegs[cSeg].pvSeg;
     2624            cbSegs += pScsiReq->paI2TSegs[cSeg].cbSeg;
    25582625            cnISCSIReq++;
    25592626
     
    25642631                pIScsiPDU->aISCSIReq[cnISCSIReq].pvSeg = &pImage->aPadding[0];
    25652632                pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg = 4 - (pScsiReq->paI2TSegs[cSeg].cbSeg & 3);
     2633                cbSegs += pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg;
    25662634                cnISCSIReq++;
    25672635            }
     
    25702638
    25712639    pIScsiPDU->cISCSIReq = cnISCSIReq;
    2572     pIScsiPDU->cbSgLeft  = pScsiReq->cbI2TData + sizeof(pIScsiPDU->aBHS);
     2640    pIScsiPDU->cbSgLeft  = cbSegs;
    25732641    RTSgBufInit(&pIScsiPDU->SgBuf, pIScsiPDU->aISCSIReq, cnISCSIReq);
    25742642
    25752643    /* Link the PDU to the list. */
    2576     if (!pImage->pIScsiPDUTxHead)
    2577         pImage->pIScsiPDUTxHead = pIScsiPDU;
    2578     else
    2579         pImage->pIScsiPDUTxTail->pNext = pIScsiPDU;
    2580     pImage->pIScsiPDUTxTail = pIScsiPDU;
     2644    iscsiPDUTxAdd(pImage, pIScsiPDU, false /* fFront */);
    25812645
    25822646    /* Start transfer of a PDU if there is no one active at the moment. */
     
    26732737            if (final && cbData > pScsiReq->cbT2IData)
    26742738            {
    2675                 /* The received PDU is partially stored in the buffer for status.
     2739                /* The received PDU is bigger than what we requested.
    26762740                 * Must not happen under normal circumstances and is a target error. */
    26772741                rc = VERR_BUFFER_OVERFLOW;
     
    26952759    }
    26962760
     2761    /* Log any errors here but ignore the PDU. */
     2762    if (RT_FAILURE(rc))
     2763    {
     2764        LogRel(("iSCSI: Received malformed PDU from target %s (rc=%Rrc), ignoring\n", pImage->pszTargetName, rc));
     2765        rc = VINF_SUCCESS;
     2766    }
     2767
    26972768    return rc;
    26982769}
    2699 
    27002770
    27012771/**
     
    30693139    PISCSIIMAGE pImage = (PISCSIIMAGE)pvUser;
    30703140
    3071     /* Initialise the initial event mask. */
     3141    /* Initialize the initial event mask. */
    30723142    pImage->fPollEvents = VD_INTERFACETCPNET_EVT_READ | VD_INTERFACETCPNET_EVT_ERROR;
    30733143
     
    30933163                    {
    30943164                        rc = iscsiPDUTxPrepare(pImage, pIScsiCmd);
     3165                        AssertRC(rc);
    30953166                        break;
    30963167                    }
     
    31103181        else if (RT_SUCCESS(rc))
    31113182        {
     3183            Assert(pImage->state == ISCSISTATE_NORMAL);
    31123184            LogFlow(("Got socket events %#x\n", fEvents));
    31133185
     
    31293201            else if (fEvents & VD_INTERFACETCPNET_EVT_ERROR)
    31303202            {
    3131                 /** @todo: Determine type of error, reset states, reconnect
    3132                  * and resend all active PDUs.
     3203                PISCSICMD pIScsiCmdHead = NULL;
     3204                PISCSICMD pIScsiCmd = NULL;
     3205                PISCSICMD pIScsiCmdCur = NULL;
     3206                PISCSIPDUTX pIScsiPDUTx = NULL;
     3207                LogFlow(("An error ocurred\n"));
     3208
     3209                /* Close connection. */
     3210                iscsiTransportClose(pImage);
     3211                pImage->state = ISCSISTATE_FREE;
     3212
     3213                /* Reset PDU we are receiving. */
     3214                iscsiRecvPDUReset(pImage);
     3215
     3216                /*
     3217                 * Abort all PDUs we are about to transmit,
     3218                 * the command need a new Itt if the relogin is successful.
    31333219                 */
    3134                 LogFlow(("An error ocurred\n"));
     3220                while (pImage->pIScsiPDUTxHead)
     3221                {
     3222                    pIScsiPDUTx = pImage->pIScsiPDUTxHead;
     3223                    pImage->pIScsiPDUTxHead = pIScsiPDUTx->pNext;
     3224
     3225                    pIScsiCmd = pIScsiPDUTx->pIScsiCmd;
     3226
     3227                    if (pIScsiCmd)
     3228                    {
     3229                        /* Place on command list. */
     3230                        pIScsiCmd->pNext = pIScsiCmdHead;
     3231                        pIScsiCmdHead = pIScsiCmd;
     3232                    }
     3233                    RTMemFree(pIScsiPDUTx);
     3234                }
     3235
     3236                /* Clear the current PDU too. */
     3237                if (pImage->pIScsiPDUTxCur)
     3238                {
     3239                    pIScsiPDUTx = pImage->pIScsiPDUTxCur;
     3240
     3241                    pImage->pIScsiPDUTxCur = NULL;
     3242                    pIScsiCmd = pIScsiPDUTx->pIScsiCmd;
     3243
     3244                    if (pIScsiCmd)
     3245                    {
     3246                        pIScsiCmd->pNext = pIScsiCmdHead;
     3247                        pIScsiCmdHead = pIScsiCmd;
     3248                    }
     3249                    RTMemFree(pIScsiPDUTx);
     3250                }
     3251
     3252                /*
     3253                 * Get all commands which are waiting for a response
     3254                 * They need to be resend too after a successful reconnect.
     3255                 */
     3256                pIScsiCmd = iscsiCmdRemoveAll(pImage);
     3257
     3258                pIScsiCmdCur = pIScsiCmd;
     3259                while (pIScsiCmdCur->pNext)
     3260                    pIScsiCmdCur = pIScsiCmdCur->pNext;
     3261
     3262                /*
     3263                 * Place them in front of the list because they are the oldest requests
     3264                 * and need to be processed first to minimize the risk to time out.
     3265                 */
     3266                pIScsiCmdCur->pNext = pIScsiCmdHead;
     3267                pIScsiCmdHead = pIScsiCmd;
     3268
     3269                /* Try to attach. */
     3270                rc = iscsiAttach(pImage);
     3271                if (RT_SUCCESS(rc))
     3272                {
     3273                    /* Phew, we have a connection again.
     3274                     * Prepare new PDUs for the aborted commands.
     3275                     */
     3276                    while (pIScsiCmdHead)
     3277                    {
     3278                        pIScsiCmd = pIScsiCmdHead;
     3279                        pIScsiCmdHead = pIScsiCmdHead->pNext;
     3280
     3281                        rc = iscsiPDUTxPrepare(pImage, pIScsiCmd);
     3282                        AssertRC(rc);
     3283                    }
     3284                }
     3285                else
     3286                {
     3287                    /*
     3288                     * Still no luck, complete commands with error so the caller
     3289                     * has a chance to inform the user and maybe resend the command.
     3290                     */
     3291                    while (pIScsiCmdHead)
     3292                    {
     3293                        pIScsiCmd = pIScsiCmdHead;
     3294                        pIScsiCmdHead = pIScsiCmdHead->pNext;
     3295
     3296                        iscsiCmdComplete(pImage, pIScsiCmd, VERR_BROKEN_PIPE);
     3297                    }
     3298                }
    31353299            }
    31363300            else
     
    31443308
    31453309    return VINF_SUCCESS;
    3146 }
    3147 
    3148 static void iscsiCommandAsyncComplete(PISCSIIMAGE pImage, int rcReq, void *pvUser)
    3149 {
    3150     size_t cbTransfered = 0;
    3151     PSCSIREQASYNC pReqAsync = (PSCSIREQASYNC)pvUser;
    3152     PSCSIREQ pScsiReq = pReqAsync->pScsiReq;
    3153 
    3154     /** @todo Retry and sense buffer handling. */
    3155 
    3156     if (pScsiReq->enmXfer == SCSIXFER_FROM_TARGET)
    3157         cbTransfered = pScsiReq->cbT2IData;
    3158     else if (pScsiReq->enmXfer == SCSIXFER_TO_TARGET)
    3159         cbTransfered = pScsiReq->cbI2TData;
    3160     else
    3161         AssertMsg(pScsiReq->enmXfer == SCSIXFER_NONE, ("To/From transfers are not supported yet\n"));
    3162 
    3163     /* Continue I/O context. */
    3164     pImage->pInterfaceIoCallbacks->pfnIoCtxCompleted(pImage->pInterfaceIo->pvUser,
    3165                                                      pReqAsync->pIoCtx, rcReq,
    3166                                                      cbTransfered);
    3167 
    3168     RTMemFree(pScsiReq);
    3169     RTMemFree(pReqAsync);
    3170 }
    3171 
    3172 static void iscsiCommandCompleteSync(PISCSIIMAGE pImage, int rcReq, void *pvUser)
    3173 {
    3174     PISCSICMDSYNC pIScsiCmdSync = (PISCSICMDSYNC)pvUser;
    3175 
    3176     pIScsiCmdSync->rcCmd = rcReq;
    3177     int rc = RTSemEventSignal(pIScsiCmdSync->EventSem);
    3178     AssertRC(rc);
    31793310}
    31803311
     
    32083339
    32093340    return rc;
     3341}
     3342
     3343static void iscsiCommandCompleteSync(PISCSIIMAGE pImage, int rcReq, void *pvUser)
     3344{
     3345    PISCSICMDSYNC pIScsiCmdSync = (PISCSICMDSYNC)pvUser;
     3346
     3347    pIScsiCmdSync->rcCmd = rcReq;
     3348    int rc = RTSemEventSignal(pIScsiCmdSync->EventSem);
     3349    AssertRC(rc);
    32103350}
    32113351
     
    33373477    return rc;
    33383478}
     3479
     3480
     3481static void iscsiCommandAsyncComplete(PISCSIIMAGE pImage, int rcReq, void *pvUser)
     3482{
     3483    bool fComplete = true;
     3484    size_t cbTransfered = 0;
     3485    PSCSIREQASYNC pReqAsync = (PSCSIREQASYNC)pvUser;
     3486    PSCSIREQ pScsiReq = pReqAsync->pScsiReq;
     3487
     3488    if (   RT_SUCCESS(rcReq)
     3489        && pScsiReq->cbSense > 0)
     3490    {
     3491        /* Try again if possible. */
     3492        if (pReqAsync->cSenseRetries > 0)
     3493        {
     3494            pReqAsync->cSenseRetries--;
     3495            pScsiReq->cbSense = sizeof(pReqAsync->abSense);
     3496            int rc = iscsiCommandAsync(pImage, pScsiReq, iscsiCommandAsyncComplete, pReqAsync);
     3497            if (RT_SUCCESS(rc))
     3498                fComplete = false;
     3499            else
     3500                rcReq = pReqAsync->rcSense;
     3501        }
     3502        else
     3503            rcReq = pReqAsync->rcSense;
     3504    }
     3505
     3506    if (fComplete)
     3507    {
     3508        if (pScsiReq->enmXfer == SCSIXFER_FROM_TARGET)
     3509            cbTransfered = pScsiReq->cbT2IData;
     3510        else if (pScsiReq->enmXfer == SCSIXFER_TO_TARGET)
     3511            cbTransfered = pScsiReq->cbI2TData;
     3512        else
     3513            AssertMsg(pScsiReq->enmXfer == SCSIXFER_NONE, ("To/From transfers are not supported yet\n"));
     3514
     3515        /* Continue I/O context. */
     3516        pImage->pInterfaceIoCallbacks->pfnIoCtxCompleted(pImage->pInterfaceIo->pvUser,
     3517                                                         pReqAsync->pIoCtx, rcReq,
     3518                                                         cbTransfered);
     3519
     3520        RTMemFree(pScsiReq);
     3521        RTMemFree(pReqAsync);
     3522    }
     3523}
     3524
    33393525
    33403526/**
     
    50185204            pReqAsync->pIoCtx = pIoCtx;
    50195205            pReqAsync->pScsiReq = pReq;
     5206            pReqAsync->cSenseRetries = 10;
     5207            pReqAsync->rcSense       = VERR_READ_ERROR;
    50205208
    50215209            pbCDB[0] = SCSI_READ_10;
     
    51165304            pReqAsync->pIoCtx = pIoCtx;
    51175305            pReqAsync->pScsiReq = pReq;
     5306            pReqAsync->cSenseRetries = 10;
     5307            pReqAsync->rcSense       = VERR_WRITE_ERROR;
    51185308
    51195309            pbCDB[0] = SCSI_WRITE_10;
     
    51785368            uint8_t *pbCDB = &pReqAsync->abCDB[0];
    51795369
    5180             pReqAsync->pIoCtx   = pIoCtx;
    5181             pReqAsync->pScsiReq = pReq;
     5370            pReqAsync->pIoCtx        = pIoCtx;
     5371            pReqAsync->pScsiReq      = pReq;
     5372            pReqAsync->cSenseRetries = 0;
     5373            pReqAsync->rcSense       = VINF_SUCCESS;
    51825374
    51835375            pbCDB[0] = SCSI_SYNCHRONIZE_CACHE;
Note: See TracChangeset for help on using the changeset viewer.

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