VirtualBox

source: vbox/trunk/src/VBox/Storage/testcase/tstVDIo.cpp@ 35689

Last change on this file since 35689 was 35671, checked in by vboxsync, 14 years ago

Storeage/tstVDIo: Fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 61.2 KB
Line 
1/* $Id: tstVDIo.cpp 35671 2011-01-21 20:20:08Z vboxsync $ */
2/** @file
3 *
4 * VBox HDD container test utility - I/O replay.
5 */
6
7/*
8 * Copyright (C) 2011 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18#define LOGGROUP LOGGROUP_DEFAULT
19#include <VBox/vd.h>
20#include <VBox/err.h>
21#include <VBox/log.h>
22#include <iprt/asm.h>
23#include <iprt/string.h>
24#include <iprt/stream.h>
25#include <iprt/mem.h>
26#include <iprt/initterm.h>
27#include <iprt/getopt.h>
28#include <iprt/list.h>
29#include <iprt/ctype.h>
30#include <iprt/semaphore.h>
31#include <iprt/thread.h>
32
33#include "VDMemDisk.h"
34#include "VDIoBackendMem.h"
35#include "VDIoRnd.h"
36
37/**
38 * A virtual file backed by memory.
39 */
40typedef struct VDFILE
41{
42 /** Pointer to the next file. */
43 RTLISTNODE Node;
44 /** Name of the file. */
45 char *pszName;
46 /** Memory file baking the file. */
47 PVDMEMDISK pMemDisk;
48 /** Completion callback of the VD layer. */
49 PFNVDCOMPLETED pfnComplete;
50} VDFILE, *PVDFILE;
51
52/**
53 * Global VD test state.
54 */
55typedef struct VDTESTGLOB
56{
57 /** HDD handle to operate on. */
58 PVBOXHDD pVD;
59 /** Head of the active file list. */
60 RTLISTNODE ListFiles;
61 /** Memory I/O backend. */
62 PVDIOBACKENDMEM pIoBackend;
63 /** Error interface. */
64 VDINTERFACE VDIError;
65 /** Error interface callbacks. */
66 VDINTERFACEERROR VDIErrorCallbacks;
67 /** Pointer to the per disk interface list. */
68 PVDINTERFACE pInterfacesDisk;
69 /** I/O interface. */
70 VDINTERFACE VDIIo;
71 /** I/O interface callbacks. */
72 VDINTERFACEIO VDIIoCallbacks;
73 /** Pointer to the per image interface list. */
74 PVDINTERFACE pInterfacesImages;
75 /** Physical CHS Geometry. */
76 VDGEOMETRY PhysGeom;
77 /** Logical CHS geometry. */
78 VDGEOMETRY LogicalGeom;
79 /** I/O RNG handle. */
80 PVDIORND pIoRnd;
81} VDTESTGLOB, *PVDTESTGLOB;
82
83/**
84 * Transfer direction.
85 */
86typedef enum VDIOREQTXDIR
87{
88 VDIOREQTXDIR_READ = 0,
89 VDIOREQTXDIR_WRITE,
90 VDIOREQTXDIR_FLUSH
91} VDIOREQTXDIR;
92
93/**
94 * I/O request.
95 */
96typedef struct VDIOREQ
97{
98 /** Transfer type. */
99 VDIOREQTXDIR enmTxDir;
100 /** slot index. */
101 unsigned idx;
102 /** Start offset. */
103 uint64_t off;
104 /** Size to transfer. */
105 size_t cbReq;
106 /** S/G Buffer */
107 RTSGBUF SgBuf;
108 /** Data segment */
109 RTSGSEG DataSeg;
110 /** Flag whether the request is outstanding or not. */
111 volatile bool fOutstanding;
112 /** Buffer to use for reads. */
113 void *pvBufRead;
114} VDIOREQ, *PVDIOREQ;
115
116/**
117 * I/O test data.
118 */
119typedef struct VDIOTEST
120{
121 /** Start offset. */
122 uint64_t offStart;
123 /** End offset. */
124 uint64_t offEnd;
125 /** Flag whether random or sequential access is wanted */
126 bool fRandomAccess;
127 /** Block size. */
128 size_t cbBlkIo;
129 /** Number of bytes to transfer. */
130 uint64_t cbIo;
131 /** Chance in percent to get a write. */
132 unsigned uWriteChance;
133 /** Pointer to the I/O data generator. */
134 PVDIORND pIoRnd;
135 /** Data dependent on the I/O mode (sequential or random). */
136 union
137 {
138 /** Next offset for seuential access. */
139 uint64_t offNext;
140 /** Data for random acess. */
141 struct
142 {
143 /** Number of valid entries in the bitmap. */
144 uint32_t cBlocks;
145 /** Pointer to the bitmap marking accessed blocks. */
146 uint8_t *pbMapAccessed;
147 /** Number of unaccessed blocks. */
148 uint32_t cBlocksLeft;
149 } Rnd;
150 } u;
151} VDIOTEST, *PVDIOTEST;
152
153/**
154 * Argument types.
155 */
156typedef enum VDSCRIPTARGTYPE
157{
158 /** Argument is a string. */
159 VDSCRIPTARGTYPE_STRING = 0,
160 /** Argument is a 64bit unsigned number. */
161 VDSCRIPTARGTYPE_UNSIGNED_NUMBER,
162 /** Argument is a 64bit signed number. */
163 VDSCRIPTARGTYPE_SIGNED_NUMBER,
164 /** Arugment is a unsigned 64bit range */
165 VDSCRIPTARGTYPE_UNSIGNED_RANGE,
166 /** Arugment is a boolean. */
167 VDSCRIPTARGTYPE_BOOL
168} VDSCRIPTARGTYPE;
169
170/**
171 * Script argument.
172 */
173typedef struct VDSCRIPTARG
174{
175 /** Argument identifier. */
176 char chId;
177 /** Type of the argument. */
178 VDSCRIPTARGTYPE enmType;
179 /** Type depndent data. */
180 union
181 {
182 /** String. */
183 const char *pcszString;
184 /** Bool. */
185 bool fFlag;
186 /** unsigned number. */
187 uint64_t u64;
188 /** Signed number. */
189 int64_t i64;
190 /** Unsigned range. */
191 struct
192 {
193 uint64_t Start;
194 uint64_t End;
195 } Range;
196 } u;
197} VDSCRIPTARG, *PVDSCRIPTARG;
198
199/** Script action handler. */
200typedef DECLCALLBACK(int) FNVDSCRIPTACTION(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
201/** Pointer to a script action handler. */
202typedef FNVDSCRIPTACTION *PFNVDSCRIPTACTION;
203
204/**
205 * Script argument descriptor.
206 */
207typedef struct VDSCRIPTARGDESC
208{
209 /** Name of the arugment. */
210 const char *pcszName;
211 /** Identifier for the argument. */
212 char chId;
213 /** Type of the argument. */
214 VDSCRIPTARGTYPE enmType;
215 /** Flags */
216 uint32_t fFlags;
217} VDSCRIPTARGDESC, *PVDSCRIPTARGDESC;
218/** Pointer to a const script argument descriptor. */
219typedef const VDSCRIPTARGDESC *PCVDSCRIPTARGDESC;
220
221/** Flag whether the argument is mandatory. */
222#define VDSCRIPTARGDESC_FLAG_MANDATORY RT_BIT(0)
223/** Flag whether the number can have a size suffix (K|M|G) */
224#define VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX RT_BIT(1)
225
226/**
227 * Script action.
228 */
229typedef struct VDSCRIPTACTION
230{
231 /** Action name. */
232 const char *pcszAction;
233 /** Pointer to the arguments. */
234 const PCVDSCRIPTARGDESC paArgDesc;
235 /** Number of arugments in the array. */
236 unsigned cArgDescs;
237 /** Pointer to the action handler. */
238 PFNVDSCRIPTACTION pfnHandler;
239} VDSCRIPTACTION, *PVDSCRIPTACTION;
240
241typedef const VDSCRIPTACTION *PCVDSCRIPTACTION;
242
243static DECLCALLBACK(int) vdScriptHandlerCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
244static DECLCALLBACK(int) vdScriptHandlerOpen(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
245static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
246static DECLCALLBACK(int) vdScriptHandlerFlush(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
247static DECLCALLBACK(int) vdScriptHandlerMerge(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
248static DECLCALLBACK(int) vdScriptHandlerClose(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
249static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
250static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
251static DECLCALLBACK(int) vdScriptHandlerSleep(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
252static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
253
254/* create action */
255const VDSCRIPTARGDESC g_aArgCreate[] =
256{
257 /* pcszName chId enmType fFlags */
258 {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
259 {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
260 {"backend", 'b', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
261 {"size", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
262};
263
264/* open action */
265const VDSCRIPTARGDESC g_aArgOpen[] =
266{
267 /* pcszName chId enmType fFlags */
268 {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
269 {"backend", 'b', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}
270};
271
272/* write action */
273const VDSCRIPTARGDESC g_aArgIo[] =
274{
275 /* pcszName chId enmType fFlags */
276 {"async", 'a', VDSCRIPTARGTYPE_BOOL, 0},
277 {"max-reqs", 'l', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, 0},
278 {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
279 {"size", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
280 {"blocksize", 'b', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
281 {"off", 'o', VDSCRIPTARGTYPE_UNSIGNED_RANGE, VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
282 {"reads", 'r', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY},
283 {"writes", 'w', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY}
284};
285
286/* flush action */
287const VDSCRIPTARGDESC g_aArgFlush[] =
288{
289 /* pcszName chId enmType fFlags */
290 {"async", 'a', VDSCRIPTARGTYPE_BOOL, 0}
291};
292
293/* merge action */
294const VDSCRIPTARGDESC g_aArgMerge[] =
295{
296 /* pcszName chId enmType fFlags */
297 {"forward", 'f', VDSCRIPTARGTYPE_BOOL, VDSCRIPTARGDESC_FLAG_MANDATORY}
298};
299
300/* close action */
301const VDSCRIPTARGDESC g_aArgClose[] =
302{
303 /* pcszName chId enmType fFlags */
304 {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
305 {"delete", 'd', VDSCRIPTARGTYPE_BOOL, VDSCRIPTARGDESC_FLAG_MANDATORY}
306};
307
308/* I/O RNG create action */
309const VDSCRIPTARGDESC g_aArgIoRngCreate[] =
310{
311 /* pcszName chId enmType fFlags */
312 {"size", 'd', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
313 {"seed", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY}
314};
315
316/* Sleep */
317const VDSCRIPTARGDESC g_aArgSleep[] =
318{
319 /* pcszName chId enmType fFlags */
320 {"time", 't', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY},
321};
322
323/* Dump memory file */
324const VDSCRIPTARGDESC g_aArgDumpFile[] =
325{
326 /* pcszName chId enmType fFlags */
327 {"file", 'f', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
328 {"path", 'p', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
329};
330
331const VDSCRIPTACTION g_aScriptActions[] =
332{
333 /* pcszAction paArgDesc cArgDescs pfnHandler */
334 {"create", g_aArgCreate, RT_ELEMENTS(g_aArgCreate), vdScriptHandlerCreate},
335 {"open", g_aArgOpen, RT_ELEMENTS(g_aArgOpen), vdScriptHandlerOpen},
336 {"io", g_aArgIo, RT_ELEMENTS(g_aArgIo), vdScriptHandlerIo},
337 {"flush", g_aArgFlush, RT_ELEMENTS(g_aArgFlush), vdScriptHandlerFlush},
338 {"close", g_aArgClose, RT_ELEMENTS(g_aArgClose), vdScriptHandlerClose},
339 {"merge", g_aArgMerge, RT_ELEMENTS(g_aArgMerge), vdScriptHandlerMerge},
340 {"iorngcreate", g_aArgIoRngCreate, RT_ELEMENTS(g_aArgIoRngCreate), vdScriptHandlerIoRngCreate},
341 {"iorngdestroy", NULL, 0, vdScriptHandlerIoRngDestroy},
342 {"sleep", g_aArgSleep, RT_ELEMENTS(g_aArgSleep), vdScriptHandlerSleep},
343 {"dumpfile", g_aArgDumpFile, RT_ELEMENTS(g_aArgDumpFile), vdScriptHandlerDumpFile}
344};
345
346const unsigned g_cScriptActions = RT_ELEMENTS(g_aScriptActions);
347
348static void tstVDError(void *pvUser, int rc, RT_SRC_POS_DECL,
349 const char *pszFormat, va_list va)
350{
351 RTPrintf("tstVD: Error %Rrc at %s:%u (%s): ", rc, RT_SRC_POS_ARGS);
352 RTPrintfV(pszFormat, va);
353 RTPrintf("\n");
354}
355
356static int tstVDMessage(void *pvUser, const char *pszFormat, va_list va)
357{
358 RTPrintf("tstVD: ");
359 RTPrintfV(pszFormat, va);
360 return VINF_SUCCESS;
361}
362
363static int tstVDIoTestInit(PVDIOTEST pIoTest, PVDTESTGLOB pGlob, bool fRandomAcc, uint64_t cbIo,
364 size_t cbBlkSize, uint64_t offStart, uint64_t offEnd,
365 unsigned uWriteChance, unsigned uReadChance);
366static bool tstVDIoTestRunning(PVDIOTEST pIoTest);
367static void tstVDIoTestDestroy(PVDIOTEST pIoTest);
368static bool tstVDIoTestReqOutstanding(PVDIOREQ pIoReq);
369static int tstVDIoTestReqInit(PVDIOTEST pIoTest, PVDIOREQ pIoReq);
370static void tstVDIoTestReqComplete(void *pvUser1, void *pvUser2, int rcReq);
371
372static DECLCALLBACK(int) vdScriptHandlerCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
373{
374 int rc = VINF_SUCCESS;
375 uint64_t cbSize = 0;
376 const char *pcszBackend = NULL;
377 const char *pcszImage = NULL;
378 bool fBase = false;
379
380 for (unsigned i = 0; i < cScriptArgs; i++)
381 {
382 switch (paScriptArgs[i].chId)
383 {
384 case 'm':
385 {
386 if (!RTStrICmp(paScriptArgs[i].u.pcszString, "base"))
387 fBase = true;
388 else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "diff"))
389 fBase = false;
390 else
391 {
392 RTPrintf("Invalid image mode '%s' given\n", paScriptArgs[i].u.pcszString);
393 rc = VERR_INVALID_PARAMETER;
394 }
395 break;
396 }
397 case 'n':
398 {
399 pcszImage = paScriptArgs[i].u.pcszString;
400 break;
401 }
402 case 'b':
403 {
404 pcszBackend = paScriptArgs[i].u.pcszString;
405 break;
406 }
407 case 's':
408 {
409 cbSize = paScriptArgs[i].u.u64;
410 break;
411 }
412 default:
413 AssertMsgFailed(("Invalid argument given!\n"));
414 }
415
416 if (RT_FAILURE(rc))
417 break;
418 }
419
420 if (RT_SUCCESS(rc))
421 {
422 if (fBase)
423 rc = VDCreateBase(pGlob->pVD, pcszBackend, pcszImage, cbSize, 0, NULL,
424 &pGlob->PhysGeom, &pGlob->LogicalGeom,
425 NULL, VD_OPEN_FLAGS_ASYNC_IO, pGlob->pInterfacesImages, NULL);
426 else
427 rc = VDCreateDiff(pGlob->pVD, pcszBackend, pcszImage, 0, NULL, NULL, NULL, VD_OPEN_FLAGS_ASYNC_IO,
428 pGlob->pInterfacesImages, NULL);
429 }
430
431 return rc;
432}
433
434static DECLCALLBACK(int) vdScriptHandlerOpen(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
435{
436 int rc = VINF_SUCCESS;
437 const char *pcszBackend = NULL;
438 const char *pcszImage = NULL;
439
440 for (unsigned i = 0; i < cScriptArgs; i++)
441 {
442 switch (paScriptArgs[i].chId)
443 {
444 case 'n':
445 {
446 pcszImage = paScriptArgs[i].u.pcszString;
447 break;
448 }
449 case 'b':
450 {
451 pcszBackend = paScriptArgs[i].u.pcszString;
452 break;
453 }
454 default:
455 AssertMsgFailed(("Invalid argument given!\n"));
456 }
457
458 if (RT_FAILURE(rc))
459 break;
460 }
461
462 if (RT_SUCCESS(rc))
463 {
464 rc = VDOpen(pGlob->pVD, pcszBackend, pcszImage, VD_OPEN_FLAGS_ASYNC_IO, pGlob->pInterfacesImages);
465 }
466
467 return rc;
468}
469
470static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
471{
472 int rc = VINF_SUCCESS;
473 bool fAsync = false;
474 bool fRandomAcc = false;
475 uint64_t cbIo = 0;
476 uint64_t cbBlkSize = 0;
477 bool fDataProviderRnd = false;
478 bool fPrintStats = false;
479 uint64_t offStart = 0;
480 uint64_t offEnd = 0;
481 unsigned cMaxReqs = 0;
482 uint8_t uWriteChance = 0;
483 uint8_t uReadChance = 0;
484
485 offEnd = VDGetSize(pGlob->pVD, VD_LAST_IMAGE);
486 if (offEnd == 0)
487 return VERR_INVALID_STATE;
488 cbIo = offEnd;
489
490 for (unsigned i = 0; i < cScriptArgs; i++)
491 {
492 switch (paScriptArgs[i].chId)
493 {
494 case 'a':
495 {
496 fAsync = paScriptArgs[i].u.fFlag;
497 break;
498 }
499 case 'l':
500 {
501 cMaxReqs = paScriptArgs[i].u.u64;
502 break;
503 }
504 case 'm':
505 {
506 if (!RTStrICmp(paScriptArgs[i].u.pcszString, "seq"))
507 fRandomAcc = false;
508 else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "rnd"))
509 fRandomAcc = true;
510 else
511 {
512 RTPrintf("Invalid access mode '%s'\n", paScriptArgs[i].u.pcszString);
513 rc = VERR_INVALID_PARAMETER;
514 }
515 break;
516 }
517 case 's':
518 {
519 cbIo = paScriptArgs[i].u.u64;
520 break;
521 }
522 case 'b':
523 {
524 cbBlkSize = paScriptArgs[i].u.u64;
525 break;
526 }
527 case 'o':
528 {
529 offStart = paScriptArgs[i].u.Range.Start;
530 offEnd = paScriptArgs[i].u.Range.End;
531 break;
532 }
533 case 'r':
534 {
535 uReadChance = (uint8_t)paScriptArgs[i].u.u64;
536 break;
537 }
538 case 'w':
539 {
540 uWriteChance = (uint8_t)paScriptArgs[i].u.u64;
541 break;
542 }
543 default:
544 AssertMsgFailed(("Invalid argument given!\n"));
545 }
546
547 if (RT_FAILURE(rc))
548 break;
549 }
550
551 if (RT_SUCCESS(rc))
552 {
553 VDIOTEST IoTest;
554
555 rc = tstVDIoTestInit(&IoTest, pGlob, fRandomAcc, cbIo, cbBlkSize, offStart, offEnd, uWriteChance, uReadChance);
556 if (RT_SUCCESS(rc))
557 {
558 PVDIOREQ paIoReq = NULL;
559 unsigned cMaxTasksOutstanding = fAsync ? cMaxReqs : 1;
560 RTSEMEVENT EventSem;
561
562 rc = RTSemEventCreate(&EventSem);
563 paIoReq = (PVDIOREQ)RTMemAllocZ(cMaxTasksOutstanding * sizeof(VDIOREQ));
564 if (paIoReq && RT_SUCCESS(rc))
565 {
566 uint64_t NanoTS = RTTimeNanoTS();
567
568 /* Init requests. */
569 for (unsigned i = 0; i < cMaxTasksOutstanding; i++)
570 {
571 paIoReq[i].idx = i;
572 paIoReq[i].pvBufRead = RTMemAlloc(cbBlkSize);
573 if (!paIoReq[i].pvBufRead)
574 {
575 rc = VERR_NO_MEMORY;
576 break;
577 }
578 }
579
580 while ( tstVDIoTestRunning(&IoTest)
581 && RT_SUCCESS(rc))
582 {
583 bool fTasksOutstanding = false;
584 unsigned idx = 0;
585
586 /* Submit all idling requests. */
587 while ( idx < cMaxTasksOutstanding
588 && tstVDIoTestRunning(&IoTest))
589 {
590 if (!tstVDIoTestReqOutstanding(&paIoReq[idx]))
591 {
592 rc = tstVDIoTestReqInit(&IoTest, &paIoReq[idx]);
593 AssertRC(rc);
594
595 if (RT_SUCCESS(rc))
596 {
597 if (!fAsync)
598 {
599 switch (paIoReq[idx].enmTxDir)
600 {
601 case VDIOREQTXDIR_READ:
602 {
603 rc = VDRead(pGlob->pVD, paIoReq[idx].off, paIoReq[idx].DataSeg.pvSeg, paIoReq[idx].cbReq);
604 break;
605 }
606 case VDIOREQTXDIR_WRITE:
607 {
608 rc = VDWrite(pGlob->pVD, paIoReq[idx].off, paIoReq[idx].DataSeg.pvSeg, paIoReq[idx].cbReq);
609 break;
610 }
611 case VDIOREQTXDIR_FLUSH:
612 {
613 rc = VDFlush(pGlob->pVD);
614 break;
615 }
616 }
617 if (RT_SUCCESS(rc))
618 idx++;
619 }
620 else
621 {
622 switch (paIoReq[idx].enmTxDir)
623 {
624 case VDIOREQTXDIR_READ:
625 {
626 rc = VDAsyncRead(pGlob->pVD, paIoReq[idx].off, paIoReq[idx].cbReq, &paIoReq[idx].SgBuf,
627 tstVDIoTestReqComplete, &paIoReq[idx], EventSem);
628 break;
629 }
630 case VDIOREQTXDIR_WRITE:
631 {
632 rc = VDAsyncWrite(pGlob->pVD, paIoReq[idx].off, paIoReq[idx].cbReq, &paIoReq[idx].SgBuf,
633 tstVDIoTestReqComplete, &paIoReq[idx], EventSem);
634 break;
635 }
636 case VDIOREQTXDIR_FLUSH:
637 {
638 rc = VDAsyncFlush(pGlob->pVD, tstVDIoTestReqComplete, &paIoReq[idx], EventSem);
639 break;
640 }
641 }
642
643 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
644 {
645 idx++;
646 fTasksOutstanding = true;
647 rc = VINF_SUCCESS;
648 }
649 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
650 {
651 ASMAtomicXchgBool(&paIoReq[idx].fOutstanding, false);
652 rc = VINF_SUCCESS;
653 }
654 }
655
656 if (RT_FAILURE(rc))
657 RTPrintf("Error submitting task %u rc=%Rrc\n", paIoReq[idx].idx, rc);
658 }
659 }
660 }
661
662 /* Wait for a request to complete. */
663 if ( fAsync
664 && fTasksOutstanding)
665 {
666 rc = RTSemEventWait(EventSem, RT_INDEFINITE_WAIT);
667 AssertRC(rc);
668 }
669 }
670
671 /* Cleanup, wait for all tasks to complete. */
672 while (fAsync)
673 {
674 unsigned idx = 0;
675 bool fAllIdle = true;
676
677 while (idx < cMaxTasksOutstanding)
678 {
679 if (tstVDIoTestReqOutstanding(&paIoReq[idx]))
680 {
681 fAllIdle = false;
682 break;
683 }
684 idx++;
685 }
686
687 if (!fAllIdle)
688 {
689 rc = RTSemEventWait(EventSem, 100);
690 Assert(RT_SUCCESS(rc) || rc == VERR_TIMEOUT);
691 }
692 else
693 break;
694 }
695
696 NanoTS = RTTimeNanoTS() - NanoTS;
697 uint64_t SpeedKBs = (uint64_t)(cbIo / (NanoTS / 1000000000.0) / 1024);
698 RTPrintf("I/O Test: Throughput %lld kb/s\n", SpeedKBs);
699
700 RTSemEventDestroy(EventSem);
701 RTMemFree(paIoReq);
702 }
703 else
704 rc = VERR_NO_MEMORY;
705
706 tstVDIoTestDestroy(&IoTest);
707 }
708 }
709
710 return rc;
711}
712
713static DECLCALLBACK(int) vdScriptHandlerFlush(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
714{
715 int rc = VINF_SUCCESS;
716 bool fAsync = false;
717
718 if (cScriptArgs == 1 && paScriptArgs[0].chId == 'a')
719 fAsync = paScriptArgs[0].u.fFlag;
720
721 if (fAsync)
722 {
723 /** @todo */
724 rc = VERR_NOT_IMPLEMENTED;
725 }
726 else
727 rc = VDFlush(pGlob->pVD);
728
729 return rc;
730}
731
732static DECLCALLBACK(int) vdScriptHandlerMerge(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
733{
734 return VERR_NOT_IMPLEMENTED;
735}
736
737static DECLCALLBACK(int) vdScriptHandlerClose(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
738{
739 int rc = VINF_SUCCESS;
740 bool fAll = false;
741 bool fDelete = false;
742
743 for (unsigned i = 0; i < cScriptArgs; i++)
744 {
745 switch (paScriptArgs[i].chId)
746 {
747 case 'm':
748 {
749 if (!RTStrICmp(paScriptArgs[i].u.pcszString, "all"))
750 fAll = true;
751 else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "single"))
752 fAll = false;
753 else
754 {
755 RTPrintf("Invalid mode '%s' given\n", paScriptArgs[i].u.pcszString);
756 rc = VERR_INVALID_PARAMETER;
757 }
758 break;
759 }
760 case 'd':
761 {
762 fDelete = paScriptArgs[i].u.fFlag;
763 break;
764 }
765 default:
766 AssertMsgFailed(("Invalid argument given!\n"));
767 }
768
769 if (RT_FAILURE(rc))
770 break;
771 }
772
773 if ( RT_SUCCESS(rc)
774 && fAll
775 && fDelete)
776 {
777 RTPrintf("mode=all doesn't work with delete=yes\n");
778 rc = VERR_INVALID_PARAMETER;
779 }
780
781 if (RT_SUCCESS(rc))
782 {
783 if (fAll)
784 rc = VDCloseAll(pGlob->pVD);
785 else
786 rc = VDClose(pGlob->pVD, fDelete);
787 }
788 return rc;
789}
790
791
792static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
793{
794 int rc = VINF_SUCCESS;
795 size_t cbPattern = 0;
796 uint64_t uSeed = 0;
797
798 for (unsigned i = 0; i < cScriptArgs; i++)
799 {
800 switch (paScriptArgs[i].chId)
801 {
802 case 'd':
803 {
804 cbPattern = paScriptArgs[i].u.u64;
805 break;
806 }
807 case 's':
808 {
809 uSeed = paScriptArgs[i].u.u64;
810 break;
811 }
812 default:
813 AssertMsgFailed(("Invalid argument given!\n"));
814 }
815 }
816
817 if (pGlob->pIoRnd)
818 {
819 RTPrintf("I/O RNG already exists\n");
820 rc = VERR_INVALID_STATE;
821 }
822 else
823 rc = VDIoRndCreate(&pGlob->pIoRnd, cbPattern, uSeed);
824
825 return rc;
826}
827
828static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
829{
830 if (pGlob->pIoRnd)
831 {
832 VDIoRndDestroy(pGlob->pIoRnd);
833 pGlob->pIoRnd = NULL;
834 }
835 else
836 RTPrintf("WARNING: No I/O RNG active, faulty script. Continuing\n");
837
838 return VINF_SUCCESS;
839}
840
841static DECLCALLBACK(int) vdScriptHandlerSleep(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
842{
843 int rc = VINF_SUCCESS;
844 uint64_t cMillies = 0;
845
846 for (unsigned i = 0; i < cScriptArgs; i++)
847 {
848 switch (paScriptArgs[i].chId)
849 {
850 case 't':
851 {
852 cMillies = paScriptArgs[i].u.u64;
853 break;
854 }
855 default:
856 AssertMsgFailed(("Invalid argument given!\n"));
857 }
858 }
859
860 rc = RTThreadSleep(cMillies);
861 return rc;
862}
863
864static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
865{
866 int rc = VINF_SUCCESS;
867 const char *pcszFile = NULL;
868 const char *pcszPathToDump = NULL;
869
870 for (unsigned i = 0; i < cScriptArgs; i++)
871 {
872 switch (paScriptArgs[i].chId)
873 {
874 case 'f':
875 {
876 pcszFile = paScriptArgs[i].u.pcszString;
877 break;
878 }
879 case 'p':
880 {
881 pcszPathToDump = paScriptArgs[i].u.pcszString;
882 break;
883 }
884 default:
885 AssertMsgFailed(("Invalid argument given!\n"));
886 }
887 }
888
889 /* Check for the file. */
890 PVDFILE pIt = NULL;
891 bool fFound = false;
892 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
893 {
894 if (!RTStrCmp(pIt->pszName, pcszFile))
895 {
896 fFound = true;
897 break;
898 }
899 }
900
901 if (fFound)
902 {
903 RTPrintf("Dumping memory file %s to %s, this might take some time\n", pcszFile, pcszPathToDump);
904 rc = VDMemDiskWriteToFile(pIt->pMemDisk, pcszPathToDump);
905 }
906 else
907 rc = VERR_FILE_NOT_FOUND;
908
909 return rc;
910}
911
912static DECLCALLBACK(int) tstVDIoFileOpen(void *pvUser, const char *pszLocation,
913 uint32_t fOpen,
914 PFNVDCOMPLETED pfnCompleted,
915 void **ppStorage)
916{
917 int rc = VINF_SUCCESS;
918 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
919 bool fFound = false;
920
921 /* Check if the file exists. */
922 PVDFILE pIt = NULL;
923 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
924 {
925 if (!RTStrCmp(pIt->pszName, pszLocation))
926 {
927 fFound = true;
928 break;
929 }
930 }
931
932 if (fFound && pIt->pfnComplete)
933 rc = VERR_FILE_LOCK_FAILED;
934 else if ((fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE)
935 {
936 /* If the file exists delete the memory disk. */
937 if (fFound)
938 rc = VDMemDiskSetSize(pIt->pMemDisk, 0);
939 else
940 {
941 /* Create completey new. */
942 pIt = (PVDFILE)RTMemAllocZ(sizeof(VDFILE));
943 if (pIt)
944 {
945 pIt->pfnComplete = pfnCompleted;
946 pIt->pszName = RTStrDup(pszLocation);
947
948 if (pIt->pszName)
949 {
950 rc = VDMemDiskCreate(&pIt->pMemDisk, 0);
951 }
952 else
953 rc = VERR_NO_MEMORY;
954
955 if (RT_FAILURE(rc))
956 {
957 if (pIt->pszName)
958 RTStrFree(pIt->pszName);
959 RTMemFree(pIt);
960 }
961 }
962 else
963 rc = VERR_NO_MEMORY;
964
965 RTListAppend(&pGlob->ListFiles, &pIt->Node);
966 }
967 }
968 else if ((fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN)
969 {
970 if (!fFound)
971 rc = VERR_FILE_NOT_FOUND;
972 else
973 pIt->pfnComplete = pfnCompleted;
974 }
975 else
976 rc = VERR_INVALID_PARAMETER;
977
978 if (RT_SUCCESS(rc))
979 {
980 AssertPtr(pIt);
981 *ppStorage = pIt;
982 }
983
984 return rc;
985}
986
987static DECLCALLBACK(int) tstVDIoFileClose(void *pvUser, void *pStorage)
988{
989 PVDFILE pFile = (PVDFILE)pStorage;
990
991 /* Mark as not busy. */
992 pFile->pfnComplete = NULL;
993 return VINF_SUCCESS;
994}
995
996static DECLCALLBACK(int) tstVDIoFileDelete(void *pvUser, const char *pcszFilename)
997{
998 int rc = VINF_SUCCESS;
999 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1000 bool fFound = false;
1001
1002 /* Check if the file exists. */
1003 PVDFILE pIt = NULL;
1004 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
1005 {
1006 if (!RTStrCmp(pIt->pszName, pcszFilename))
1007 {
1008 fFound = true;
1009 break;
1010 }
1011 }
1012
1013 if (fFound)
1014 {
1015 RTListNodeRemove(&pIt->Node);
1016 VDMemDiskDestroy(pIt->pMemDisk);
1017 RTStrFree(pIt->pszName);
1018 RTMemFree(pIt);
1019 }
1020 else
1021 rc = VERR_FILE_NOT_FOUND;
1022
1023 return rc;
1024}
1025
1026static DECLCALLBACK(int) tstVDIoFileMove(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
1027{
1028 int rc = VINF_SUCCESS;
1029 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1030 bool fFound = false;
1031
1032 /* Check if the file exists. */
1033 PVDFILE pIt = NULL;
1034 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
1035 {
1036 if (!RTStrCmp(pIt->pszName, pcszSrc))
1037 {
1038 fFound = true;
1039 break;
1040 }
1041 }
1042
1043 if (fFound)
1044 {
1045 char *pszNew = RTStrDup(pcszDst);
1046 if (pszNew)
1047 {
1048 RTStrFree(pIt->pszName);
1049 pIt->pszName = pszNew;
1050 }
1051 else
1052 rc = VERR_NO_MEMORY;
1053 }
1054 else
1055 rc = VERR_FILE_NOT_FOUND;
1056
1057 return rc;
1058}
1059
1060static DECLCALLBACK(int) tstVDIoFileGetFreeSpace(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
1061{
1062 AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
1063
1064 *pcbFreeSpace = ~0ULL; /** @todo: Implement */
1065 return VINF_SUCCESS;
1066}
1067
1068static DECLCALLBACK(int) tstVDIoFileGetModificationTime(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
1069{
1070 AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
1071
1072 /** @todo: Implement */
1073 return VINF_SUCCESS;
1074}
1075
1076static DECLCALLBACK(int) tstVDIoFileGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
1077{
1078 PVDFILE pFile = (PVDFILE)pStorage;
1079
1080 return VDMemDiskGetSize(pFile->pMemDisk, pcbSize);
1081}
1082
1083static DECLCALLBACK(int) tstVDIoFileSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
1084{
1085 PVDFILE pFile = (PVDFILE)pStorage;
1086
1087 return VDMemDiskSetSize(pFile->pMemDisk, cbSize);
1088}
1089
1090static DECLCALLBACK(int) tstVDIoFileWriteSync(void *pvUser, void *pStorage, uint64_t uOffset,
1091 const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten)
1092{
1093 int rc = VINF_SUCCESS;
1094 PVDFILE pFile = (PVDFILE)pStorage;
1095
1096 RTSGBUF SgBuf;
1097 RTSGSEG Seg;
1098
1099 Seg.pvSeg = (void *)pvBuffer;
1100 Seg.cbSeg = cbBuffer;
1101 RTSgBufInit(&SgBuf, &Seg, 1);
1102 rc = VDMemDiskWrite(pFile->pMemDisk, uOffset, cbBuffer, &SgBuf);
1103 if (RT_SUCCESS(rc) && pcbWritten)
1104 *pcbWritten = cbBuffer;
1105
1106 return rc;
1107}
1108
1109static DECLCALLBACK(int) tstVDIoFileReadSync(void *pvUser, void *pStorage, uint64_t uOffset,
1110 void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
1111{
1112 int rc = VINF_SUCCESS;
1113 PVDFILE pFile = (PVDFILE)pStorage;
1114
1115 RTSGBUF SgBuf;
1116 RTSGSEG Seg;
1117
1118 Seg.pvSeg = pvBuffer;
1119 Seg.cbSeg = cbBuffer;
1120 RTSgBufInit(&SgBuf, &Seg, 1);
1121 rc = VDMemDiskRead(pFile->pMemDisk, uOffset, cbBuffer, &SgBuf);
1122 if (RT_SUCCESS(rc) && pcbRead)
1123 *pcbRead = cbBuffer;
1124
1125 return rc;
1126}
1127
1128static DECLCALLBACK(int) tstVDIoFileFlushSync(void *pvUser, void *pStorage)
1129{
1130 /* nothing to do. */
1131 return VINF_SUCCESS;
1132}
1133
1134static DECLCALLBACK(int) tstVDIoFileReadAsync(void *pvUser, void *pStorage, uint64_t uOffset,
1135 PCRTSGSEG paSegments, size_t cSegments,
1136 size_t cbRead, void *pvCompletion,
1137 void **ppTask)
1138{
1139 int rc = VINF_SUCCESS;
1140 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1141 PVDFILE pFile = (PVDFILE)pStorage;
1142
1143 rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pFile->pMemDisk, VDIOTXDIR_READ, uOffset,
1144 cbRead, paSegments, cSegments, pFile->pfnComplete, pvCompletion);
1145 if (RT_SUCCESS(rc))
1146 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1147
1148 return rc;
1149}
1150
1151static DECLCALLBACK(int) tstVDIoFileWriteAsync(void *pvUser, void *pStorage, uint64_t uOffset,
1152 PCRTSGSEG paSegments, size_t cSegments,
1153 size_t cbWrite, void *pvCompletion,
1154 void **ppTask)
1155{
1156 int rc = VINF_SUCCESS;
1157 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1158 PVDFILE pFile = (PVDFILE)pStorage;
1159
1160 rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pFile->pMemDisk, VDIOTXDIR_WRITE, uOffset,
1161 cbWrite, paSegments, cSegments, pFile->pfnComplete, pvCompletion);
1162 if (RT_SUCCESS(rc))
1163 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1164
1165 return rc;
1166}
1167
1168static DECLCALLBACK(int) tstVDIoFileFlushAsync(void *pvUser, void *pStorage, void *pvCompletion,
1169 void **ppTask)
1170{
1171 int rc = VINF_SUCCESS;
1172 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1173 PVDFILE pFile = (PVDFILE)pStorage;
1174
1175 rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pFile->pMemDisk, VDIOTXDIR_FLUSH, 0,
1176 0, NULL, 0, pFile->pfnComplete, pvCompletion);
1177 if (RT_SUCCESS(rc))
1178 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1179
1180 return rc;
1181}
1182
1183static int tstVDIoTestInit(PVDIOTEST pIoTest, PVDTESTGLOB pGlob, bool fRandomAcc, uint64_t cbIo,
1184 size_t cbBlkSize, uint64_t offStart, uint64_t offEnd,
1185 unsigned uWriteChance, unsigned uReadChance)
1186{
1187 int rc = VINF_SUCCESS;
1188
1189 pIoTest->fRandomAccess = fRandomAcc;
1190 pIoTest->cbIo = cbIo;
1191 pIoTest->cbBlkIo = cbBlkSize;
1192 pIoTest->offStart = offStart;
1193 pIoTest->offEnd = offEnd;
1194 pIoTest->uWriteChance = uWriteChance;
1195 pIoTest->pIoRnd = pGlob->pIoRnd;
1196
1197 if (fRandomAcc)
1198 {
1199 uint64_t cbRange = pIoTest->offEnd < pIoTest->offStart
1200 ? pIoTest->offStart - pIoTest->offEnd
1201 : pIoTest->offEnd - pIoTest->offStart;
1202
1203 pIoTest->u.Rnd.cBlocks = cbRange / cbBlkSize + ((cbRange % cbBlkSize) ? 1 : 0);
1204 pIoTest->u.Rnd.cBlocksLeft = pIoTest->u.Rnd.cBlocks;
1205 pIoTest->u.Rnd.pbMapAccessed = (uint8_t *)RTMemAllocZ(pIoTest->u.Rnd.cBlocks / 8
1206 + ((pIoTest->u.Rnd.cBlocks % 8)
1207 ? 1
1208 : 0));
1209 if (!pIoTest->u.Rnd.pbMapAccessed)
1210 rc = VERR_NO_MEMORY;
1211 }
1212 else
1213 pIoTest->u.offNext = pIoTest->offEnd < pIoTest->offStart ? pIoTest->offStart - cbBlkSize : 0;
1214
1215 return rc;
1216}
1217
1218static void tstVDIoTestDestroy(PVDIOTEST pIoTest)
1219{
1220 if (pIoTest->fRandomAccess)
1221 RTMemFree(pIoTest->u.Rnd.pbMapAccessed);
1222}
1223
1224static bool tstVDIoTestRunning(PVDIOTEST pIoTest)
1225{
1226 return pIoTest->cbIo > 0;
1227}
1228
1229static bool tstVDIoTestReqOutstanding(PVDIOREQ pIoReq)
1230{
1231 return pIoReq->fOutstanding;
1232}
1233
1234/**
1235 * Returns true with the given chance in percent.
1236 *
1237 * @returns true or false
1238 * @param iPercentage The percentage of the chance to return true.
1239 */
1240static bool tstVDIoTestIsTrue(PVDIOTEST pIoTest, int iPercentage)
1241{
1242 int uRnd = VDIoRndGetU32Ex(pIoTest->pIoRnd, 0, 100);
1243
1244 return (uRnd <= iPercentage); /* This should be enough for our purpose */
1245}
1246
1247static int tstVDIoTestReqInit(PVDIOTEST pIoTest, PVDIOREQ pIoReq)
1248{
1249 int rc = VINF_SUCCESS;
1250
1251 if (pIoTest->cbIo)
1252 {
1253 /* Read or Write? */
1254 pIoReq->enmTxDir = tstVDIoTestIsTrue(pIoTest, pIoTest->uWriteChance) ? VDIOREQTXDIR_WRITE : VDIOREQTXDIR_READ;
1255 pIoReq->cbReq = RT_MIN(pIoTest->cbBlkIo, pIoTest->cbIo);
1256 pIoTest->cbIo -= pIoReq->cbReq;
1257 pIoReq->DataSeg.cbSeg = pIoReq->cbReq;
1258
1259 if (pIoReq->enmTxDir == VDIOREQTXDIR_WRITE)
1260 {
1261 rc = VDIoRndGetBuffer(pIoTest->pIoRnd, &pIoReq->DataSeg.pvSeg, pIoReq->cbReq);
1262 AssertRC(rc);
1263 }
1264 else
1265 {
1266 /* Read */
1267 pIoReq->DataSeg.pvSeg = pIoReq->pvBufRead;
1268 }
1269
1270 if (RT_SUCCESS(rc))
1271 {
1272 RTSgBufInit(&pIoReq->SgBuf, &pIoReq->DataSeg, 1);
1273
1274 if (pIoTest->fRandomAccess)
1275 {
1276 int idx = -1;
1277
1278 idx = ASMBitFirstClear(pIoTest->u.Rnd.pbMapAccessed, pIoTest->u.Rnd.cBlocks);
1279
1280 /* In case this is the last request we don't need to search further. */
1281 if (pIoTest->u.Rnd.cBlocksLeft > 1)
1282 {
1283 int idxIo;
1284 idxIo = VDIoRndGetU32Ex(pIoTest->pIoRnd, idx, pIoTest->u.Rnd.cBlocks - 1);
1285
1286 /*
1287 * If the bit is marked free use it, otherwise search for the next free bit
1288 * and if that doesn't work use the first free bit.
1289 */
1290 if (ASMBitTest(pIoTest->u.Rnd.pbMapAccessed, idxIo))
1291 {
1292 idxIo = ASMBitNextClear(pIoTest->u.Rnd.pbMapAccessed, pIoTest->u.Rnd.cBlocks, idxIo);
1293 if (idxIo != -1)
1294 idx = idxIo;
1295 }
1296 else
1297 idx = idxIo;
1298 }
1299
1300 Assert(idx != -1);
1301 pIoReq->off = idx * pIoTest->cbBlkIo;
1302 pIoTest->u.Rnd.cBlocksLeft--;
1303 if (!pIoTest->u.Rnd.cBlocksLeft)
1304 {
1305 /* New round, clear everthing. */
1306 ASMBitClearRange(pIoTest->u.Rnd.pbMapAccessed, 0, pIoTest->u.Rnd.cBlocks);
1307 pIoTest->u.Rnd.cBlocksLeft = pIoTest->u.Rnd.cBlocks;
1308 }
1309 else
1310 ASMBitSet(pIoTest->u.Rnd.pbMapAccessed, idx);
1311 }
1312 else
1313 {
1314 pIoReq->off = pIoTest->u.offNext;
1315 if (pIoTest->offEnd < pIoTest->offStart)
1316 {
1317 pIoTest->u.offNext = pIoTest->u.offNext == 0
1318 ? pIoTest->offEnd - pIoTest->cbBlkIo
1319 : RT_MAX(pIoTest->offEnd, pIoTest->u.offNext - pIoTest->cbBlkIo);
1320 }
1321 else
1322 {
1323 pIoTest->u.offNext = pIoTest->u.offNext + pIoTest->cbBlkIo >= pIoTest->offEnd
1324 ? 0
1325 : RT_MIN(pIoTest->offEnd, pIoTest->u.offNext + pIoTest->cbBlkIo);
1326 }
1327 }
1328 pIoReq->fOutstanding = true;
1329 }
1330 }
1331 else
1332 rc = VERR_ACCESS_DENIED;
1333
1334 return rc;
1335}
1336
1337static void tstVDIoTestReqComplete(void *pvUser1, void *pvUser2, int rcReq)
1338{
1339 PVDIOREQ pIoReq = (PVDIOREQ)pvUser1;
1340 RTSEMEVENT hEventSem = (RTSEMEVENT)pvUser2;
1341
1342 ASMAtomicXchgBool(&pIoReq->fOutstanding, false);
1343 RTSemEventSignal(hEventSem);
1344 return;
1345}
1346
1347/**
1348 * Skips the characters until the given character is reached.
1349 *
1350 * @returns Start of the string with the given character
1351 * or NULL if the string ended before.
1352 *
1353 * @param psz The string to skip.
1354 * @param ch The character.
1355 */
1356static char *tstVDIoScriptSkipUntil(char *psz, char ch)
1357{
1358 while ( *psz != '\0'
1359 && *psz != ch)
1360 psz++;
1361
1362 return psz;
1363}
1364
1365/**
1366 * Skips the spaces of the current string.
1367 *
1368 * @returns Start of the string with a non space character
1369 * or NULL if the string ended before.
1370 *
1371 * @param psz The string to skip.
1372 */
1373static char *tstVDIoScriptSkipSpace(char *psz)
1374{
1375 while ( *psz != '\0'
1376 && RT_C_IS_SPACE(*psz))
1377 psz++;
1378
1379 return psz;
1380}
1381
1382/**
1383 * Skips all characters until a space is reached of the current
1384 * string.
1385 *
1386 * @returns Start of the string with a space character
1387 * or NULL if the string ended before.
1388 *
1389 * @param psz The string to skip.
1390 */
1391static char *tstVDIoScriptSkipNonSpace(char *psz)
1392{
1393 while ( *psz != '\0'
1394 && !RT_C_IS_SPACE(*psz))
1395 psz++;
1396
1397 return psz;
1398}
1399
1400/**
1401 * Parses one argument name, value pair.
1402 *
1403 * @returns IPRT status code.
1404 *
1405 * @param pVDScriptAction Script action.
1406 * @param pcszName Argument name.
1407 * @param pcszValue Argument value.
1408 * @param pScriptArg Where to fill in the parsed
1409 * argument.
1410 * @param pfMandatory Where to store whether the argument
1411 * is mandatory.
1412 */
1413static int tstVDIoScriptArgumentParse(PCVDSCRIPTACTION pVDScriptAction, const char *pcszName,
1414 const char *pcszValue, PVDSCRIPTARG pScriptArg, bool *pfMandatory)
1415{
1416 int rc = VERR_NOT_FOUND;
1417
1418 for (unsigned i = 0; i < pVDScriptAction->cArgDescs; i++)
1419 {
1420 if (!RTStrCmp(pVDScriptAction->paArgDesc[i].pcszName, pcszName))
1421 {
1422 rc = VINF_SUCCESS;
1423
1424 switch (pVDScriptAction->paArgDesc[i].enmType)
1425 {
1426 case VDSCRIPTARGTYPE_BOOL:
1427 {
1428 pScriptArg->enmType = VDSCRIPTARGTYPE_BOOL;
1429 if (!RTStrICmp(pcszValue, "yes") || !RTStrICmp(pcszValue, "on"))
1430 pScriptArg->u.fFlag = true;
1431 else if (!RTStrICmp(pcszValue, "no") || !RTStrICmp(pcszValue, "off"))
1432 pScriptArg->u.fFlag = false;
1433 else
1434 {
1435 RTPrintf("Boolean argument malformed '%s'\n", pcszValue);
1436 rc = VERR_INVALID_PARAMETER;
1437 }
1438 break;
1439 }
1440 case VDSCRIPTARGTYPE_SIGNED_NUMBER:
1441 {
1442 pScriptArg->enmType = VDSCRIPTARGTYPE_SIGNED_NUMBER;
1443 AssertMsgFailed(("todo\n"));
1444 break;
1445 }
1446 case VDSCRIPTARGTYPE_STRING:
1447 {
1448 pScriptArg->enmType = VDSCRIPTARGTYPE_STRING;
1449 pScriptArg->u.pcszString = pcszValue;
1450 break;
1451 }
1452 case VDSCRIPTARGTYPE_UNSIGNED_NUMBER:
1453 {
1454 char *pszSuffix = NULL;
1455
1456 pScriptArg->enmType = VDSCRIPTARGTYPE_UNSIGNED_NUMBER;
1457 rc = RTStrToUInt64Ex(pcszValue, &pszSuffix, 10, &pScriptArg->u.u64);
1458 if (rc == VWRN_TRAILING_CHARS)
1459 {
1460 switch (*pszSuffix)
1461 {
1462 case 'k':
1463 case 'K':
1464 {
1465 pScriptArg->u.u64 *= _1K;
1466 break;
1467 }
1468 case 'm':
1469 case 'M':
1470 {
1471 pScriptArg->u.u64 *= _1M;
1472 break;
1473 }
1474 case 'g':
1475 case 'G':
1476 {
1477 pScriptArg->u.u64 *= _1G;
1478 break;
1479 }
1480 default:
1481 {
1482 RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
1483 rc = VERR_INVALID_PARAMETER;
1484 }
1485 }
1486 if (rc != VERR_INVALID_PARAMETER)
1487 rc = VINF_SUCCESS;
1488 }
1489
1490 break;
1491 }
1492 case VDSCRIPTARGTYPE_UNSIGNED_RANGE:
1493 {
1494 char *pszSuffix = NULL;
1495
1496 pScriptArg->enmType = VDSCRIPTARGTYPE_UNSIGNED_RANGE;
1497 rc = RTStrToUInt64Ex(pcszValue, &pszSuffix, 10, &pScriptArg->u.Range.Start);
1498 if (rc == VWRN_TRAILING_CHARS)
1499 {
1500 if (*pszSuffix != '-')
1501 {
1502 switch (*pszSuffix)
1503 {
1504 case 'k':
1505 case 'K':
1506 {
1507 pScriptArg->u.u64 *= _1K;
1508 break;
1509 }
1510 case 'm':
1511 case 'M':
1512 {
1513 pScriptArg->u.u64 *= _1M;
1514 break;
1515 }
1516 case 'g':
1517 case 'G':
1518 {
1519 pScriptArg->u.u64 *= _1G;
1520 break;
1521 }
1522 default:
1523 {
1524 RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
1525 rc = VERR_INVALID_PARAMETER;
1526 }
1527 }
1528 if (RT_SUCCESS(rc))
1529 pszSuffix++;
1530 }
1531
1532 if (*pszSuffix == '-')
1533 {
1534 pszSuffix++;
1535 rc = RTStrToUInt64Ex(pszSuffix, &pszSuffix, 10, &pScriptArg->u.Range.End);
1536 if (rc == VWRN_TRAILING_CHARS)
1537 {
1538 switch (*pszSuffix)
1539 {
1540 case 'k':
1541 case 'K':
1542 {
1543 pScriptArg->u.Range.End *= _1K;
1544 break;
1545 }
1546 case 'm':
1547 case 'M':
1548 {
1549 pScriptArg->u.Range.End *= _1M;
1550 break;
1551 }
1552 case 'g':
1553 case 'G':
1554 {
1555 pScriptArg->u.Range.End *= _1G;
1556 break;
1557 }
1558 default:
1559 {
1560 RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
1561 rc = VERR_INVALID_PARAMETER;
1562 }
1563 }
1564 }
1565 }
1566 else
1567 rc = VERR_INVALID_PARAMETER;
1568 }
1569 else
1570 rc = VERR_INVALID_PARAMETER;
1571
1572 if (rc == VERR_INVALID_PARAMETER)
1573 RTPrintf("Invalid range format\n");
1574 break;
1575 }
1576 default:
1577 AssertMsgFailed(("Invalid script argument type\n"));
1578 }
1579
1580 if (RT_SUCCESS(rc))
1581 {
1582 pScriptArg->chId = pVDScriptAction->paArgDesc[i].chId;
1583 *pfMandatory = !!(pVDScriptAction->paArgDesc[i].fFlags & VDSCRIPTARGDESC_FLAG_MANDATORY);
1584 }
1585 break;
1586 }
1587 }
1588
1589 if (rc == VERR_NOT_FOUND)
1590 RTPrintf("Argument '%s' not found\n", pcszName);
1591
1592 return rc;
1593}
1594
1595/**
1596 * Parses the arguments of a action in the script.
1597 *
1598 * @returns IPRT status code.
1599 *
1600 * @param psz Argument string.
1601 * @param pVDScriptAction The script action to parses
1602 * arguments for.
1603 * @param paScriptArgs Where to store the arguments.
1604 * @param pcScriptArgs Where to store the actual number of
1605 * arguments parsed.
1606 */
1607static int tstVDIoScriptArgumentListParse(char *psz, PCVDSCRIPTACTION pVDScriptAction, PVDSCRIPTARG paScriptArgs, unsigned *pcScriptArgs)
1608{
1609 int rc = VINF_SUCCESS;
1610 unsigned cMandatoryArgsReq = 0;
1611 unsigned cScriptArgs = 0;
1612
1613 /* Count the number of mandatory arguments first. */
1614 for (unsigned i = 0; i < pVDScriptAction->cArgDescs; i++)
1615 if (pVDScriptAction->paArgDesc[i].fFlags & VDSCRIPTARGDESC_FLAG_MANDATORY)
1616 cMandatoryArgsReq++;
1617
1618 /* One argument is given in the form name=value. */
1619 *pcScriptArgs = 0;
1620
1621 while ( psz
1622 && *psz != '\0')
1623 {
1624 const char *pcszName = psz;
1625
1626 psz = tstVDIoScriptSkipUntil(psz, '=');
1627 if (psz != '\0')
1628 {
1629 *psz = '\0'; /* Overwrite */
1630 psz++;
1631 const char *pcszValue = psz;
1632
1633 psz = tstVDIoScriptSkipNonSpace(psz);
1634 if (psz != '\0')
1635 {
1636 *psz = '\0'; /* Overwrite */
1637 psz++;
1638 psz = tstVDIoScriptSkipSpace(psz);
1639
1640 /* We have the name and value pair now. */
1641 bool fMandatory = false; /* Shut up gcc */
1642 rc = tstVDIoScriptArgumentParse(pVDScriptAction, pcszName, pcszValue, &paScriptArgs[cScriptArgs], &fMandatory);
1643 if (RT_SUCCESS(rc))
1644 {
1645 if (fMandatory)
1646 cMandatoryArgsReq--;
1647 cScriptArgs++;
1648 }
1649 }
1650 else
1651 {
1652 RTPrintf("Value missing for argument '%s'\n", pcszName);
1653 rc = VERR_INVALID_STATE;
1654 break;
1655 }
1656 }
1657 else
1658 {
1659 RTPrintf("Argument in invalid form\n");
1660 rc = VERR_INVALID_STATE;
1661 break;
1662 }
1663 }
1664
1665 if ( RT_SUCCESS(rc)
1666 && cMandatoryArgsReq)
1667 {
1668 /* No arguments anymore but there are still mandatory arguments left. */
1669 RTPrintf("There are %u arguments missing for script action '%s\n", pVDScriptAction->pcszAction);
1670 rc = VERR_INVALID_STATE;
1671 }
1672
1673 if (RT_SUCCESS(rc))
1674 *pcScriptArgs = cScriptArgs;
1675
1676 return rc;
1677}
1678
1679/**
1680 * Executes the script pointed to by the given stream.
1681 *
1682 * @returns IPRT status code.
1683 *
1684 * @param pStrm The stream handle of the script.
1685 * @param pGlob Global test data.
1686 */
1687static int tstVDIoScriptExecute(PRTSTREAM pStrm, PVDTESTGLOB pGlob)
1688{
1689 int rc = VINF_SUCCESS;
1690 char abBuffer[0x1000]; /* Current assumption that a line is never longer than 4096 bytes. */
1691 PVDSCRIPTARG paScriptArgs = NULL;
1692 unsigned cScriptArgsMax = 0;
1693
1694 do
1695 {
1696 memset(abBuffer, 0, sizeof(abBuffer));
1697 rc = RTStrmGetLine(pStrm, abBuffer, sizeof(abBuffer));
1698 if (RT_SUCCESS(rc))
1699 {
1700 const char *pcszAction = NULL;
1701 char *psz = abBuffer;
1702
1703 /* Skip space */
1704 psz = tstVDIoScriptSkipSpace(psz);
1705 if (psz != '\0')
1706 {
1707 PCVDSCRIPTACTION pVDScriptAction = NULL;
1708
1709 /* Get the action name. */
1710 pcszAction = psz;
1711
1712 psz = tstVDIoScriptSkipNonSpace(psz);
1713 if (psz != '\0')
1714 {
1715 Assert(RT_C_IS_SPACE(*psz));
1716 *psz++ = '\0';
1717 }
1718
1719 /* Find the action. */
1720 for (unsigned i = 0; i < g_cScriptActions; i++)
1721 {
1722 if (!RTStrCmp(pcszAction, g_aScriptActions[i].pcszAction))
1723 {
1724 pVDScriptAction = &g_aScriptActions[i];
1725 break;
1726 }
1727 }
1728
1729 if (pVDScriptAction)
1730 {
1731 /* Parse arguments. */
1732 if (cScriptArgsMax < pVDScriptAction->cArgDescs)
1733 {
1734 /* Increase arguments array. */
1735 if (paScriptArgs)
1736 RTMemFree(paScriptArgs);
1737
1738 cScriptArgsMax = pVDScriptAction->cArgDescs;
1739 paScriptArgs = (PVDSCRIPTARG)RTMemAllocZ(cScriptArgsMax * sizeof(VDSCRIPTARG));
1740 }
1741
1742 if (paScriptArgs)
1743 {
1744 unsigned cScriptArgs;
1745
1746 rc = tstVDIoScriptArgumentListParse(psz, pVDScriptAction, paScriptArgs, &cScriptArgs);
1747 if (RT_SUCCESS(rc))
1748 {
1749 /* Execute the handler. */
1750 rc = pVDScriptAction->pfnHandler(pGlob, paScriptArgs, cScriptArgs);
1751 }
1752 }
1753 else
1754 {
1755 RTPrintf("Out of memory while allocating argument array for script action %s\n", pcszAction);
1756 rc = VERR_NO_MEMORY;
1757 }
1758 }
1759 else
1760 {
1761 RTPrintf("Script action %s is not known\n", pcszAction);
1762 rc = VERR_NOT_FOUND;
1763 }
1764 }
1765 else
1766 {
1767 RTPrintf("Missing action name\n");
1768 rc = VERR_INVALID_STATE;
1769 }
1770 }
1771 } while(RT_SUCCESS(rc));
1772
1773 if (rc == VERR_EOF)
1774 {
1775 RTPrintf("Successfully executed I/O script\n");
1776 rc = VINF_SUCCESS;
1777 }
1778 return rc;
1779}
1780
1781/**
1782 * Executes the given I/O script.
1783 *
1784 * @returns nothing.
1785 *
1786 * @param pcszFilename The script to execute.
1787 */
1788static void tstVDIoScriptRun(const char *pcszFilename)
1789{
1790 int rc = VINF_SUCCESS;
1791 PRTSTREAM pScriptStrm; /**< Stream of the script file. */
1792 VDTESTGLOB GlobTest; /**< Global test data. */
1793
1794 memset(&GlobTest, 0, sizeof(VDTESTGLOB));
1795 RTListInit(&GlobTest.ListFiles);
1796
1797 rc = RTStrmOpen(pcszFilename, "r", &pScriptStrm);
1798 if (RT_SUCCESS(rc))
1799 {
1800 /* Init global test data. */
1801 GlobTest.VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
1802 GlobTest.VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
1803 GlobTest.VDIErrorCallbacks.pfnError = tstVDError;
1804 GlobTest.VDIErrorCallbacks.pfnMessage = tstVDMessage;
1805
1806 rc = VDInterfaceAdd(&GlobTest.VDIError, "tstVDIo_VDIError", VDINTERFACETYPE_ERROR,
1807 &GlobTest.VDIErrorCallbacks, NULL, &GlobTest.pInterfacesDisk);
1808 AssertRC(rc);
1809
1810 GlobTest.VDIIoCallbacks.cbSize = sizeof(VDINTERFACEIO);
1811 GlobTest.VDIIoCallbacks.enmInterface = VDINTERFACETYPE_IO;
1812 GlobTest.VDIIoCallbacks.pfnOpen = tstVDIoFileOpen;
1813 GlobTest.VDIIoCallbacks.pfnClose = tstVDIoFileClose;
1814 GlobTest.VDIIoCallbacks.pfnDelete = tstVDIoFileDelete;
1815 GlobTest.VDIIoCallbacks.pfnMove = tstVDIoFileMove;
1816 GlobTest.VDIIoCallbacks.pfnGetFreeSpace = tstVDIoFileGetFreeSpace;
1817 GlobTest.VDIIoCallbacks.pfnGetModificationTime = tstVDIoFileGetModificationTime;
1818 GlobTest.VDIIoCallbacks.pfnGetSize = tstVDIoFileGetSize;
1819 GlobTest.VDIIoCallbacks.pfnSetSize = tstVDIoFileSetSize;
1820 GlobTest.VDIIoCallbacks.pfnWriteSync = tstVDIoFileWriteSync;
1821 GlobTest.VDIIoCallbacks.pfnReadSync = tstVDIoFileReadSync;
1822 GlobTest.VDIIoCallbacks.pfnFlushSync = tstVDIoFileFlushSync;
1823 GlobTest.VDIIoCallbacks.pfnReadAsync = tstVDIoFileReadAsync;
1824 GlobTest.VDIIoCallbacks.pfnWriteAsync = tstVDIoFileWriteAsync;
1825 GlobTest.VDIIoCallbacks.pfnFlushAsync = tstVDIoFileFlushAsync;
1826
1827 rc = VDInterfaceAdd(&GlobTest.VDIIo, "tstVDIo_VDIIo", VDINTERFACETYPE_IO,
1828 &GlobTest.VDIIoCallbacks, &GlobTest, &GlobTest.pInterfacesImages);
1829 AssertRC(rc);
1830
1831 /* Init I/O backend. */
1832 rc = VDIoBackendMemCreate(&GlobTest.pIoBackend);
1833 if (RT_SUCCESS(rc))
1834 {
1835 rc = VDCreate(GlobTest.pInterfacesDisk, VDTYPE_HDD, &GlobTest.pVD);
1836 if (RT_SUCCESS(rc))
1837 {
1838 /* Execute the script. */
1839 rc = tstVDIoScriptExecute(pScriptStrm, &GlobTest);
1840 if (RT_FAILURE(rc))
1841 {
1842 RTPrintf("Executing the script stream failed rc=%Rrc\n", rc);
1843 }
1844 }
1845 else
1846 RTPrintf("Failed to create disk container rc=%Rrc\n", rc);
1847 VDIoBackendMemDestroy(GlobTest.pIoBackend);
1848 }
1849 else
1850 RTPrintf("Creating the I/O backend failed rc=%Rrc\n");
1851
1852 RTStrmClose(pScriptStrm);
1853 }
1854 else
1855 RTPrintf("Opening script failed rc=%Rrc\n", rc);
1856}
1857
1858/**
1859 * Shows help message.
1860 */
1861static void printUsage(void)
1862{
1863 RTPrintf("Usage:\n"
1864 "--script <filename> Script to execute\n"
1865 "--replay <filename> Log to replay (not implemented yet)\n");
1866}
1867
1868static const RTGETOPTDEF g_aOptions[] =
1869{
1870 { "--script", 's', RTGETOPT_REQ_STRING },
1871 { "--replay", 'r', RTGETOPT_REQ_STRING },
1872};
1873
1874int main(int argc, char *argv[])
1875{
1876 RTR3Init();
1877 int rc;
1878 RTGETOPTUNION ValueUnion;
1879 RTGETOPTSTATE GetState;
1880 char c;
1881
1882 if (argc != 3)
1883 {
1884 printUsage();
1885 return RTEXITCODE_FAILURE;
1886 }
1887
1888 rc = VDInit();
1889 if (RT_FAILURE(rc))
1890 return RTEXITCODE_FAILURE;
1891
1892 RTGetOptInit(&GetState, argc, argv, g_aOptions,
1893 RT_ELEMENTS(g_aOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1894
1895 while ( RT_SUCCESS(rc)
1896 && (c = RTGetOpt(&GetState, &ValueUnion)))
1897 {
1898 switch (c)
1899 {
1900 case 's':
1901 tstVDIoScriptRun(ValueUnion.psz);
1902 break;
1903 case 'r':
1904 RTPrintf("Replaying I/O logs is not implemented yet\n");
1905 break;
1906 default:
1907 printUsage();
1908 }
1909 }
1910
1911 rc = VDShutdown();
1912 if (RT_FAILURE(rc))
1913 RTPrintf("tstVDIo: unloading backends failed! rc=%Rrc\n", rc);
1914
1915 return RTEXITCODE_SUCCESS;
1916}
1917
Note: See TracBrowser for help on using the repository browser.

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