diff options
Diffstat (limited to 'src/VBox/Runtime/testcase/tstRTFileAio.cpp')
-rw-r--r-- | src/VBox/Runtime/testcase/tstRTFileAio.cpp | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/src/VBox/Runtime/testcase/tstRTFileAio.cpp b/src/VBox/Runtime/testcase/tstRTFileAio.cpp new file mode 100644 index 00000000..c32af367 --- /dev/null +++ b/src/VBox/Runtime/testcase/tstRTFileAio.cpp @@ -0,0 +1,248 @@ +/* $Id: tstRTFileAio.cpp $ */ +/** @file + * IPRT Testcase - File Async I/O. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/file.h> + +#include <iprt/err.h> +#include <iprt/mem.h> +#include <iprt/param.h> +#include <iprt/string.h> +#include <iprt/test.h> + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** @todo make configurable through cmd line. */ +#define TSTFILEAIO_MAX_REQS_IN_FLIGHT 64 +#define TSTFILEAIO_BUFFER_SIZE (64*_1K) + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +static RTTEST g_hTest = NIL_RTTEST; + + +void tstFileAioTestReadWriteBasic(RTFILE File, bool fWrite, void *pvTestBuf, + size_t cbTestBuf, size_t cbTestFile, uint32_t cMaxReqsInFlight) +{ + /* Allocate request array. */ + RTFILEAIOREQ *paReqs; + paReqs = (PRTFILEAIOREQ)RTTestGuardedAllocHead(g_hTest, cMaxReqsInFlight * sizeof(RTFILEAIOREQ)); + RTTESTI_CHECK_RETV(paReqs); + RT_BZERO(paReqs, cMaxReqsInFlight * sizeof(RTFILEAIOREQ)); + + /* Allocate array holding pointer to data buffers. */ + void **papvBuf = (void **)RTTestGuardedAllocHead(g_hTest, cMaxReqsInFlight * sizeof(void *)); + RTTESTI_CHECK_RETV(papvBuf); + + /* Allocate the buffers*/ + for (unsigned i = 0; i < cMaxReqsInFlight; i++) + { + RTTESTI_CHECK_RC_OK_RETV(RTTestGuardedAlloc(g_hTest, cbTestBuf, PAGE_SIZE, true /*fHead*/, &papvBuf[i])); + if (fWrite) + memcpy(papvBuf[i], pvTestBuf, cbTestBuf); + if (fWrite) + memcpy(papvBuf[i], pvTestBuf, cbTestBuf); + else + RT_BZERO(papvBuf[i], cbTestBuf); + } + + /* Allocate array holding completed requests. */ + RTFILEAIOREQ *paReqsCompleted; + paReqsCompleted = (PRTFILEAIOREQ)RTTestGuardedAllocHead(g_hTest, cMaxReqsInFlight * sizeof(RTFILEAIOREQ)); + RTTESTI_CHECK_RETV(paReqsCompleted); + RT_BZERO(paReqsCompleted, cMaxReqsInFlight * sizeof(RTFILEAIOREQ)); + + /* Create a context and associate the file handle with it. */ + RTFILEAIOCTX hAioContext; + RTTESTI_CHECK_RC_RETV(RTFileAioCtxCreate(&hAioContext, cMaxReqsInFlight, 0 /* fFlags */), VINF_SUCCESS); + RTTESTI_CHECK_RC_RETV(RTFileAioCtxAssociateWithFile(hAioContext, File), VINF_SUCCESS); + + /* Initialize requests. */ + for (unsigned i = 0; i < cMaxReqsInFlight; i++) + RTFileAioReqCreate(&paReqs[i]); + + RTFOFF off = 0; + int cRuns = 0; + uint64_t NanoTS = RTTimeNanoTS(); + size_t cbLeft = cbTestFile; + while (cbLeft) + { + int rc; + int cReqs = 0; + for (unsigned i = 0; i < cMaxReqsInFlight; i++) + { + size_t cbTransfer = cbLeft < cbTestBuf ? cbLeft : cbTestBuf; + if (!cbTransfer) + break; + + if (fWrite) + rc = RTFileAioReqPrepareWrite(paReqs[i], File, off, papvBuf[i], + cbTransfer, papvBuf[i]); + else + rc = RTFileAioReqPrepareRead(paReqs[i], File, off, papvBuf[i], + cbTransfer, papvBuf[i]); + RTTESTI_CHECK_RC(rc, VINF_SUCCESS); + + cbLeft -= cbTransfer; + off += cbTransfer; + cReqs++; + } + + rc = RTFileAioCtxSubmit(hAioContext, paReqs, cReqs); + RTTESTI_CHECK_MSG(rc == VINF_SUCCESS, ("Failed to submit tasks after %d runs. rc=%Rrc\n", cRuns, rc)); + if (rc != VINF_SUCCESS) + break; + + /* Wait */ + uint32_t cCompleted = 0; + RTTESTI_CHECK_RC(rc = RTFileAioCtxWait(hAioContext, cReqs, RT_INDEFINITE_WAIT, + paReqsCompleted, cMaxReqsInFlight, &cCompleted), + VINF_SUCCESS); + if (rc != VINF_SUCCESS) + break; + + if (!fWrite) + { + for (uint32_t i = 0; i < cCompleted; i++) + { + /* Compare that we read the right stuff. */ + void *pvBuf = RTFileAioReqGetUser(paReqsCompleted[i]); + RTTESTI_CHECK(pvBuf); + + size_t cbTransfered; + RTTESTI_CHECK_RC(rc = RTFileAioReqGetRC(paReqsCompleted[i], &cbTransfered), VINF_SUCCESS); + if (rc != VINF_SUCCESS) + break; + RTTESTI_CHECK_MSG(cbTransfered == cbTestBuf, ("cbTransfered=%zd\n", cbTransfered)); + RTTESTI_CHECK_RC_OK(rc = (memcmp(pvBuf, pvTestBuf, cbTestBuf) == 0 ? VINF_SUCCESS : VERR_BAD_EXE_FORMAT)); + if (rc != VINF_SUCCESS) + break; + memset(pvBuf, 0, cbTestBuf); + } + } + cRuns++; + if (RT_FAILURE(rc)) + break; + } + + NanoTS = RTTimeNanoTS() - NanoTS; + uint64_t SpeedKBs = (uint64_t)((double)cbTestFile / ((double)NanoTS / 1000000000.0) / 1024); + RTTestValue(g_hTest, "Throughput", SpeedKBs, RTTESTUNIT_KILOBYTES_PER_SEC); + + /* cleanup */ + for (unsigned i = 0; i < cMaxReqsInFlight; i++) + RTTestGuardedFree(g_hTest, papvBuf[i]); + RTTestGuardedFree(g_hTest, papvBuf); + for (unsigned i = 0; i < cMaxReqsInFlight; i++) + RTTESTI_CHECK_RC(RTFileAioReqDestroy(paReqs[i]), VINF_SUCCESS); + RTTESTI_CHECK_RC(RTFileAioCtxDestroy(hAioContext), VINF_SUCCESS); + RTTestGuardedFree(g_hTest, paReqs); +} + +int main() +{ + int rc = RTTestInitAndCreate("tstRTFileAio", &g_hTest); + if (rc) + return rc; + + /* Check if the API is available. */ + RTTestSub(g_hTest, "RTFileAioGetLimits"); + RTFILEAIOLIMITS AioLimits; + RT_ZERO(AioLimits); + RTTESTI_CHECK_RC(rc = RTFileAioGetLimits(&AioLimits), VINF_SUCCESS); + if (RT_SUCCESS(rc)) + { + RTTestSub(g_hTest, "Write"); + RTFILE hFile; + RTFSTYPE enmType; + bool fAsyncMayFail = false; + rc = RTFsQueryType("tstFileAio#1.tst", &enmType); + if ( RT_SUCCESS(rc) + && enmType == RTFSTYPE_TMPFS) + fAsyncMayFail = true; + rc = RTFileOpen(&hFile, "tstFileAio#1.tst", + RTFILE_O_READWRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_ASYNC_IO); + RTTESTI_CHECK( rc == VINF_SUCCESS + || ((rc == VERR_ACCESS_DENIED || rc == VERR_INVALID_PARAMETER) && fAsyncMayFail)); + if (RT_SUCCESS(rc)) + { + uint8_t *pbTestBuf = (uint8_t *)RTTestGuardedAllocTail(g_hTest, TSTFILEAIO_BUFFER_SIZE); + for (unsigned i = 0; i < TSTFILEAIO_BUFFER_SIZE; i++) + pbTestBuf[i] = i % 256; + + uint32_t cReqsMax = AioLimits.cReqsOutstandingMax < TSTFILEAIO_MAX_REQS_IN_FLIGHT + ? AioLimits.cReqsOutstandingMax + : TSTFILEAIO_MAX_REQS_IN_FLIGHT; + + /* Basic write test. */ + RTTestIPrintf(RTTESTLVL_ALWAYS, "Preparing test file, this can take some time and needs quite a bit of harddisk space...\n"); + tstFileAioTestReadWriteBasic(hFile, true /*fWrite*/, pbTestBuf, TSTFILEAIO_BUFFER_SIZE, 100*_1M, cReqsMax); + + /* Reopen the file before doing the next test. */ + RTTESTI_CHECK_RC(RTFileClose(hFile), VINF_SUCCESS); + if (RTTestErrorCount(g_hTest) == 0) + { + RTTestSub(g_hTest, "Read/Write"); + RTTESTI_CHECK_RC(rc = RTFileOpen(&hFile, "tstFileAio#1.tst", + RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_ASYNC_IO), + VINF_SUCCESS); + if (RT_SUCCESS(rc)) + { + tstFileAioTestReadWriteBasic(hFile, false /*fWrite*/, pbTestBuf, TSTFILEAIO_BUFFER_SIZE, 100*_1M, cReqsMax); + RTFileClose(hFile); + } + } + + /* Cleanup */ + RTFileDelete("tstFileAio#1.tst"); + } + else + RTTestSkipped(g_hTest, "rc=%Rrc", rc); + } + + /* + * Summary + */ + return RTTestSummaryAndDestroy(g_hTest); +} + |