/*
**++
**  FACILITY:  DISKPERF
**
**  MODULE DESCRIPTION:
**
**      Programme d'evaluation des performances sous-systeme disques
**
**  AUTHORS:
**
**      JFP. - PiƩronne JF (jf.pieronne@laposte.net)
**
**  CREATION DATE:  9-Nov-1995
**
**  DESIGN ISSUES:
**
**      x
**
**  VERSION:
**
**      1.0
**
**
**  MODIFICATION HISTORY:
**
**      Date      | Name  | Description
**----------------+-------+-----------------------------------------------------
**    9-Nov-1995  |  JFP  | Version initial
**----------------+-------+-----------------------------------------------------
**    9-Nov-1995  |  GC   | Ajout info dans fichier resultats
**----------------+-------+-----------------------------------------------------
**   20-Jul-2002  |  JFP  | Correction creation fichiers pour test multi-disques
**----------------+-------+-----------------------------------------------------
**   15-Jun-2006  |  JFP  | Message d'utilisation fichier pour tests en R/W
**                |       | changement FAB$M_CTG en FAB$M_CBT
**----------------+-------+-----------------------------------------------------
**
**--
*/
/*
  compilation :
    vax : cc/decc diskperf
          cc/vaxc diskperf
    axp : cc/prefix=all diskperf
-*/

/*
**  INCLUDE FILES
**/

#ifndef __NEW_STARLET
#define __NEW_STARLET
#endif

#include <climsgdef.h>
#include <dcdef.h>
#include <descrip.h>
#include <dvidef.h>
#include <fibdef.h>
#include <gen64def.h>
#include <iledef.h>
#include <iodef.h>
#include <iosbdef.h>
#include <lib$routines.h>
#include <libwaitdef.h>
#include <rms.h>
#include <rmsdef.h>
#include <ssdef.h>
#include <starlet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stsdef.h>
#include <unixio.h>
#include <unixlib.h>

#define K_DisksCard 10
#define K_MaxSimQIO 200

/* VMS pre 6.0 */
#ifndef IO$M_NOVCACHE
#define IO$M_NOVCACHE 0x20000
#endif

#ifdef IN_VSCODE
# define _align(n)
#endif

typedef struct {
    int _IOCount;
    int _ReadIOCount;
    int _WriteIOCount;
    int _elapsed[2];
    unsigned int _cpu;
} Stat_s;

static enum _tt { KReadOnly = 0, KWriteOnly = 1, KReadWrite = 2 } TypeTest;
static enum _ts { KDuration = 0, KIOCount = 1, KSize = 2 } TypeStop;
volatile static long ReadCount;
volatile static long WriteCount;
static long CacheHitPercent;
static long ReadPercent;
static int StartingBlock;
static int EndingBlock;
static int Random;
static int Pass;
static int Interval;
volatile static int TotIO;
volatile static ReadIOCount;
volatile static WriteIOCount;
volatile static EndPass;
volatile static ActiveIOCount = 0;
static int SimultaneousIO;
static int MinIOSize;
static int MaxIOSize;
static int ReadIOSize;
static int WriteIOSize;
static double ReadWriteIOSizeRatio;
static int PassDuration;
static int TotSize;
static int VioModifier;
static IOSB Iosb[K_MaxSimQIO];
static int DisksCount;
static char FullDevnam[K_DisksCard][65], Medianam[K_DisksCard][65];
static char Volnam[K_DisksCard][13];
static unsigned short Channel[K_DisksCard];
volatile static int Address[K_DisksCard];
Stat_s StatInfo[8][127]; /* 8 pass 1-127 blocks */

static char FileName[K_DisksCard][256];
static FIBDEF Fib[K_DisksCard];

static _align(PAGE) char IOBuffer[K_MaxSimQIO][512 * 127]; /* buffer size 127
                                                              blocks maximum */

static void IOCompletionAST(int);

/*
 * -- DCL callback interface
 */
extern int cli$get_value(struct dsc$descriptor_s *, struct dsc$descriptor_s *,
                         short *);
extern int cli$present(struct dsc$descriptor_s *);
/*
 * -- RTL string routines
 */
extern int str$free1_dx(struct dsc$descriptor_s *);

static int YesOrNo(const char *str) {
    int rep = 2;
    while ((rep != 0) && (rep != 1)) {
        printf("%s", str);
        scanf("%d", &rep);
    }
    return rep;
}

static void InteractiveQueries() {
    int i;

    printf("\nTest type (0 : Read only, 1 : Write only, 2 : Read-Write): ");
    scanf("%d", &i);
    TypeTest = (enum _tt)i;
    if (TypeTest == KReadWrite) {
        printf("\nRead percent: ");
        scanf("%d", &ReadPercent);
        printf("\n(Read Size / Write Size) Ratio: ");
        scanf("%lf", &ReadWriteIOSizeRatio);
    }
#ifdef FullInteractive
    printf("\n# disks (1 - 10): ");
    scanf("%d", &DisksCount);
    getchar();
    if (DisksCount > K_DisksCard)
        exit(SS$_ABORT);
    for (i = 0; i < DisksCount; ++i) {
        int classDisk, opcnt, max, free;
        int hoav;
        int status;
        unsigned short devlen;
        unsigned short volnamlen, medialen;
        int freebl, fp, ip, catchup, merge, member, fail, refcnt;
        int errcnt;
        char devnam[65];
        ILE3 gds[] = {{4, DVI$_DEVCLASS, &classDisk, 0},
                      {4, DVI$_SHDW_CATCHUP_COPYING, &catchup, 0},
                      {4, DVI$_SHDW_FAILED_MEMBER, &fail, 0},
                      {4, DVI$_SHDW_MEMBER, &member, 0},
                      {4, DVI$_SHDW_MERGE_COPYING, &merge, 0},
                      {4, DVI$_HOST_AVAIL, &hoav, 0},
                      {4, DVI$_REFCNT, &refcnt, 0},
                      {4, DVI$_FREEBLOCKS, &freebl, 0},
                      {4, DVI$_MAXBLOCK, &max, 0},
                      {64, DVI$_MEDIA_NAME, Medianam[i], &medialen},
                      {64, DVI$_FULLDEVNAM, FullDevnam[i], &devlen},
                      {4, DVI$_OPCNT, &opcnt, 0},
                      {4, DVI$_ERRCNT, &errcnt, 0},
                      {12, DVI$_VOLNAM, Volnam[i], &volnamlen},
                      {0, 0}};
        $DESCRIPTOR(devnam_d, devnam);

        printf("\nDevice name: ");
        gets(devnam);

        puts("device name    label    type  # blocks    free blocks        I/O "
             "     errors");
        puts("------------  --------  ----  ---------  --------------  "
             "-----------  ------");

        devnam_d.dsc$w_length = strlen(devnam);
        memset(Medianam[i], ' ', sizeof(Medianam[0]) - 1);
        Medianam[i][sizeof(Medianam[0]) - 1] = '\0';
        memset(Volnam[i], ' ', sizeof(Volnam[0]) - 1);
        Volnam[i][sizeof(Volnam[0]) - 1] = '\0';
        memset(FullDevnam[i], ' ', sizeof(FullDevnam[0]) - 1);
        FullDevnam[i][sizeof(FullDevnam[0]) - 1] = '\0';

        status = sys$getdviw(0, 0, &devnam_d, gds, 0, 0, 0, 0);
        if (!$VMS_STATUS_SUCCESS(status))
            exit(status);
        if ((classDisk != DC$_DISK) || !hoav || member || fail || catchup ||
            merge || (refcnt == 0))
            exit(SS$_ABORT);
        if (max == 0)
            exit(SS$_ABORT);
        //    FullDevnam[i][14] = '\0';
        //    Volnam[i][9] = '\0';
        //    Medianam[i][6] = '\0';
        fp = 100.0 * (double)freebl / (double)max;
        printf("%-13s %-9s %-6s%9d%9d (%3d%%)  %9d  %6d\n", &FullDevnam[i][1],
               Volnam[i], Medianam[i], max, freebl, fp, opcnt, errcnt);
    }
#endif
    printf("\nStarting block: ");
    scanf("%d", &StartingBlock);
    printf("\nEnding block: ");
    scanf("%d", &EndingBlock);
    Random = YesOrNo("\nRandom (0/1): ");
    if (!Random) {
        printf("\nInterval (blocks -1 : use IO size): ");
        scanf("%d", &Interval);
    }

    VioModifier = YesOrNo("\nVirtual I/O Cache (0/1): ") ? 0 : IO$M_NOVCACHE;

    printf("\nEstimate cache hit percent");
    if ((EndingBlock - StartingBlock) < 1000000)
        printf(" (may not be significant): ");
    else
        printf(": ");
    scanf("%d", &CacheHitPercent);

    printf("\nPass (8 max): ");
    scanf("%d", &Pass);

    printf("\nStop on (0 : Duration, 1 : IOs Count, 2 : Total Size): ");
    scanf("%d", &i);
    TypeStop = (enum _ts)i;
    switch (TypeStop) {
    case KDuration:
        printf("\nPass Duration: ");
        scanf("%d", &PassDuration);
        break;
    case KIOCount:
        printf("\nTotal IOs count: ");
        scanf("%d", &TotIO);
        break;
    case KSize:
        printf("\nTotal Size (Kbytes): ");
        scanf("%d", &TotSize);
        break;
    default:
        fprintf(stderr, "\nInvalid stop switch\n");
        exit(SS$_ABORT);
    }
    printf("\nSimultaneous IOs count (%d max): ", K_MaxSimQIO / DisksCount);
    scanf("%d", &SimultaneousIO);
    if (SimultaneousIO * DisksCount > K_MaxSimQIO)
        exit(SS$_ABORT);
    printf("\nMin IO size (1-127 blocks): ");
    scanf("%d", &MinIOSize);
    printf("\nMax IO size (%d-127 blocks): ", MinIOSize);
    scanf("%d", &MaxIOSize);
}

/*
 *
 * CLI interface
 *
 */
static void ProcessCommandLine() {
    $DESCRIPTOR(interactiveQualDesc, "INTERACTIVE");
    $DESCRIPTOR(sequentialQualDesc, "SEQUENTIAL");
    $DESCRIPTOR(seqIntervalQualDesc, "SEQUENTIAL.INTERVAL");
    $DESCRIPTOR(disksQualDesc, "DISKS");
    $DESCRIPTOR(typeReadQualDesc, "TYPE.READ");
    $DESCRIPTOR(typeWriteQualDesc, "TYPE.WRITE");
    $DESCRIPTOR(typeRatioQualDesc, "TYPE.RATIO");
    $DESCRIPTOR(rangeStartQualDesc, "RANGE.START");
    $DESCRIPTOR(rangeSizeQualDesc, "RANGE.SIZE");
    $DESCRIPTOR(cacheQualDesc, "CACHE");
    $DESCRIPTOR(VIOCacheQualDesc, "VIOCACHE");
    $DESCRIPTOR(cachePercentQualDesc, "CACHE.PERCENT");
    $DESCRIPTOR(passQualDesc, "PASS");
    $DESCRIPTOR(EndIOQualDesc, "END.IO_COUNT");
    $DESCRIPTOR(EndSecondsQualDesc, "END.SECONDS");
    $DESCRIPTOR(EndVolumeQualDesc, "END.VOLUME");
    $DESCRIPTOR(IOSizeMinQualDesc, "IO_SIZE.MINIMUM");
    $DESCRIPTOR(IOSizeMaxQualDesc, "IO_SIZE.MAXIMUM");
    $DESCRIPTOR(readWriteRatioSizeQualDesc, "IO_SIZE.RATIO");
    $DESCRIPTOR(simultaneousIOQualDesc, "SIMULTANEOUS_IO");
    int i;
    char str[100];
    struct dsc$descriptor_s retdesc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_D, NULL};
    short retlen;
    int maxBlock = 0;

    DisksCount = 0;
    for (i = 0;
         $VMS_STATUS_SUCCESS(cli$get_value(&disksQualDesc, &retdesc, &retlen));
         ++i) {
        int classDisk, opcnt, max, free;
        int hoav;
        int status;
        unsigned short devlen;
        unsigned short volnamlen, medialen;
        int freebl, fp, ip, catchup, merge, member, fail, refcnt;
        int errcnt;
        char devnam[65];
        ILE3 gds[] = {{4, DVI$_DEVCLASS, &classDisk, 0},
                      {4, DVI$_SHDW_CATCHUP_COPYING, &catchup, 0},
                      {4, DVI$_SHDW_FAILED_MEMBER, &fail, 0},
                      {4, DVI$_SHDW_MEMBER, &member, 0},
                      {4, DVI$_SHDW_MERGE_COPYING, &merge, 0},
                      {4, DVI$_HOST_AVAIL, &hoav, 0},
                      {4, DVI$_REFCNT, &refcnt, 0},
                      {4, DVI$_FREEBLOCKS, &freebl, 0},
                      {4, DVI$_MAXBLOCK, &max, 0},
                      {64, DVI$_MEDIA_NAME, Medianam[i], &medialen},
                      {64, DVI$_FULLDEVNAM, FullDevnam[i], &devlen},
                      {4, DVI$_OPCNT, &opcnt, 0},
                      {4, DVI$_ERRCNT, &errcnt, 0},
                      {12, DVI$_VOLNAM, Volnam[i], &volnamlen},
                      {0, 0}};
        $DESCRIPTOR(devnam_d, devnam);

        ++DisksCount;
        if (DisksCount > K_DisksCard)
            exit(SS$_ABORT);
        strncpy(str, retdesc.dsc$a_pointer, retlen);
        str[retlen] = '\0';
        strcpy(devnam, str);
        devnam_d.dsc$w_length = strlen(devnam);
        memset(Medianam[i], ' ', sizeof(Medianam[0]) - 1);
        Medianam[i][sizeof(Medianam[0]) - 1] = '\0';
        memset(Volnam[i], ' ', sizeof(Volnam[0]) - 1);
        Volnam[i][sizeof(Volnam[0]) - 1] = '\0';
        memset(FullDevnam[i], ' ', sizeof(FullDevnam[0]) - 1);
        FullDevnam[i][sizeof(FullDevnam[0]) - 1] = '\0';

        status = sys$getdviw(0, 0, &devnam_d, gds, 0, 0, 0, 0);
        if (!$VMS_STATUS_SUCCESS(status))
            exit(status);
        if ((classDisk != DC$_DISK) || !hoav || member || fail || catchup ||
            merge || (refcnt == 0))
            exit(SS$_ABORT);
        if (max == 0)
            exit(SS$_ABORT);
        if ((max < maxBlock) || (maxBlock == 0))
            maxBlock = max;
        //    FullDevnam[i][14] = '\0';
        //    Volnam[i][9] = '\0';
        //    Medianam[i][6] = '\0';
    }

    if ($VMS_STATUS_SUCCESS(cli$present(&interactiveQualDesc))) {
        InteractiveQueries();
        return;
    }
    if ($VMS_STATUS_SUCCESS(cli$present(&sequentialQualDesc))) {
        Random = 0;
        if ($VMS_STATUS_SUCCESS(
                cli$get_value(&seqIntervalQualDesc, &retdesc, &retlen))) {
            strncpy(str, retdesc.dsc$a_pointer, retlen);
            str[retlen] = '\0';
            sscanf(str, "%d", &Interval);
        } else
            Interval = -1;
    } else
        Random = 1;
    if ($VMS_STATUS_SUCCESS(cli$present(&typeReadQualDesc)) &&
        $VMS_STATUS_SUCCESS(cli$present(&typeWriteQualDesc))) {
        TypeTest = KReadWrite;
    } else if ($VMS_STATUS_SUCCESS(cli$present(&typeWriteQualDesc))) {
        TypeTest = KWriteOnly;
    } else { /* read only */
        TypeTest = KReadOnly;
    }

    if ($VMS_STATUS_SUCCESS(
            cli$get_value(&typeRatioQualDesc, &retdesc, &retlen))) {
        strncpy(str, retdesc.dsc$a_pointer, retlen);
        str[retlen] = '\0';
        sscanf(str, "%d", &ReadPercent);
    } else
        ReadPercent = 80;

    cli$get_value(&passQualDesc, &retdesc, &retlen);
    strncpy(str, retdesc.dsc$a_pointer, retlen);
    str[retlen] = '\0';
    sscanf(str, "%d", &Pass);

    if ($VMS_STATUS_SUCCESS(cli$present(&rangeStartQualDesc))) {
        cli$get_value(&rangeStartQualDesc, &retdesc, &retlen);
        strncpy(str, retdesc.dsc$a_pointer, retlen);
        str[retlen] = '\0';
        sscanf(str, "%d", &StartingBlock);
    } else
        StartingBlock = 1;
    if ($VMS_STATUS_SUCCESS(cli$present(&rangeSizeQualDesc))) {
        cli$get_value(&rangeSizeQualDesc, &retdesc, &retlen);
        strncpy(str, retdesc.dsc$a_pointer, retlen);
        str[retlen] = '\0';
        sscanf(str, "%d", &EndingBlock);
    } else
        EndingBlock = StartingBlock + maxBlock / 3;

    if ($VMS_STATUS_SUCCESS(
            cli$get_value(&cachePercentQualDesc, &retdesc, &retlen))) {
        strncpy(str, retdesc.dsc$a_pointer, retlen);
        str[retlen] = '\0';
        sscanf(str, "%d", &CacheHitPercent);
    } else if ($VMS_STATUS_SUCCESS(cli$present(&cacheQualDesc)))
        CacheHitPercent = 20;
    else
        CacheHitPercent = 0;

    if ($VMS_STATUS_SUCCESS(
            cli$get_value(&EndVolumeQualDesc, &retdesc, &retlen))) {
        strncpy(str, retdesc.dsc$a_pointer, retlen);
        str[retlen] = '\0';
        sscanf(str, "%d", &TotSize);
        TypeStop = KSize;
    } else if ($VMS_STATUS_SUCCESS(
                   cli$get_value(&EndIOQualDesc, &retdesc, &retlen))) {
        strncpy(str, retdesc.dsc$a_pointer, retlen);
        str[retlen] = '\0';
        sscanf(str, "%d", &TotIO);
        TypeStop = KIOCount;
    } else if ($VMS_STATUS_SUCCESS(
                   cli$get_value(&EndSecondsQualDesc, &retdesc, &retlen))) {
        strncpy(str, retdesc.dsc$a_pointer, retlen);
        str[retlen] = '\0';
        sscanf(str, "%d", &PassDuration);
        TypeStop = KDuration;
    } else {
        PassDuration = 15;
        TypeStop = KDuration;
    }

    if ($VMS_STATUS_SUCCESS(
            cli$get_value(&IOSizeMinQualDesc, &retdesc, &retlen))) {
        strncpy(str, retdesc.dsc$a_pointer, retlen);
        str[retlen] = '\0';
        sscanf(str, "%d", &MinIOSize);
    } else
        MinIOSize = 12;

    if ($VMS_STATUS_SUCCESS(
            cli$get_value(&IOSizeMaxQualDesc, &retdesc, &retlen))) {
        strncpy(str, retdesc.dsc$a_pointer, retlen);
        str[retlen] = '\0';
        sscanf(str, "%d", &MaxIOSize);
    } else
        MaxIOSize = MinIOSize;

    if ($VMS_STATUS_SUCCESS(
            cli$get_value(&readWriteRatioSizeQualDesc, &retdesc, &retlen))) {
        strncpy(str, retdesc.dsc$a_pointer, retlen);
        str[retlen] = '\0';
        sscanf(str, "%lf", &ReadWriteIOSizeRatio);
    } else
        ReadWriteIOSizeRatio = 3;

    cli$get_value(&simultaneousIOQualDesc, &retdesc, &retlen);
    strncpy(str, retdesc.dsc$a_pointer, retlen);
    str[retlen] = '\0';
    sscanf(str, "%d", &SimultaneousIO);

    if (SimultaneousIO * DisksCount > K_MaxSimQIO)
        exit(SS$_ABORT);

    if ($VMS_STATUS_SUCCESS(cli$present(&VIOCacheQualDesc)))
        VioModifier = 0;
    else
        VioModifier = IO$M_NOVCACHE;

    /*
     * Release the dynamic string we have been using.
     */
    if (retdesc.dsc$a_pointer != NULL)
        str$free1_dx(&retdesc);
}

void IoAccess(unsigned short channel, FIBDEF *fib) {
    IOSB ioFich;
    long status;
    struct {
        unsigned long l;
        unsigned long adr;
    } fibDes;

    fibDes.l = sizeof(*fib);
    fibDes.adr = (unsigned long)fib;
    status = sys$qiow(0, channel, IO$_ACCESS | IO$M_ACCESS, &ioFich, 0, 0,
                      &fibDes, 0, 0, 0, 0, 0);
    if (!$VMS_STATUS_SUCCESS(status)) {
        fprintf(stderr, "\nIoAccess Error\n");
        exit(status);
    }
}

void IoDeAccess(unsigned short channel, FIBDEF *fib) {
    IOSB ioFich;
    long status;
    struct {
        unsigned long l;
        unsigned long adr;
    } fibDes;

    fibDes.l = sizeof(*fib);
    fibDes.adr = (unsigned long)fib;
    status = sys$qiow(0, channel, IO$_DEACCESS, &ioFich, 0, 0, &fibDes, 0, 0, 0,
                      0, 0);
    if (!$VMS_STATUS_SUCCESS(status)) {
        fprintf(stderr, "\nIoDeAccess Error %X\n", status);
    }
}

static void DeleteFiles() {
    static done;
    int i;

    if (!done) {
        for (i = 0; i < DisksCount; ++i) {
            IoDeAccess(Channel[i], &(Fib[i]));
            delete (FileName[i]);
        }
        done = 1;
    } else {
        for (i = 0; i < DisksCount; ++i) {
            delete (FileName[i]);
        }
    }
}

static CreateFile(unsigned short *channel, FIBDEF *fib, char *fileName,
                  long maxVbn) {
    struct FAB fabFlog;
    struct RAB rabFlog;
    long status;
    struct FAB *fabP = 0;
    struct NAM *namP;
    long statRms;
    char temp[256];
    $DESCRIPTOR(tempDesc, temp);
    $DESCRIPTOR(fichierDesc, fileName);

    fichierDesc.dsc$w_length = strlen(fileName);

    fabFlog = cc$rms_fab;
    rabFlog = cc$rms_rab;
    fabFlog.fab$l_fna = fileName;
    fabFlog.fab$b_fns = strlen(fileName);
    fabFlog.fab$b_org = FAB$C_SEQ;
    fabFlog.fab$l_alq = maxVbn;
    fabFlog.fab$b_fac = FAB$M_GET | FAB$M_PUT;
    fabFlog.fab$b_shr = FAB$M_SHRPUT | FAB$M_SHRGET;
    fabFlog.fab$l_fop = FAB$M_CBT;
    fabFlog.fab$w_mrs = 512;

    status = sys$create(&fabFlog, 0, 0);
    if (!$VMS_STATUS_SUCCESS(status)) {
        fprintf(stderr, "\nCreate Error\n");
        exit(status);
    }
    status = sys$close(&fabFlog, 0, 0);
    if (!$VMS_STATUS_SUCCESS(status)) {
        fprintf(stderr, "\nClose Error\n");
        exit(status);
    }

    fabP = 0;

    status = lib$find_file(&fichierDesc, &tempDesc, (unsigned int *)&fabP, 0, 0,
                           &statRms, 0);
    if (!$VMS_STATUS_SUCCESS(status)) {
        fprintf(stderr, "\nFind File Error\n");
        exit(status);
    }

    status = sys$assign(&fichierDesc, channel, 0, 0, 0);
    if (!$VMS_STATUS_SUCCESS(status)) {
        fprintf(stderr, "\nAssign Error\n");
        exit(status);
    }

    namP = fabP->fab$l_nam;
#if defined(__VAXC) || defined(VAXC)
    fib->fib$r_fid_overlay.fib$w_fid[0] = namP->nam$w_fid[0];
    fib->fib$r_fid_overlay.fib$w_fid[1] = namP->nam$w_fid[1];
    fib->fib$r_fid_overlay.fib$w_fid[2] = namP->nam$w_fid[2];
    fib->fib$r_acctl_overlay.fib$l_acctl = FIB$M_NOWRITE | FIB$M_WRITE;
#else
    fib->fib$w_fid[0] = namP->nam$w_fid[0];
    fib->fib$w_fid[1] = namP->nam$w_fid[1];
    fib->fib$w_fid[2] = namP->nam$w_fid[2];
    fib->fib$l_acctl = FIB$M_NOWRITE | FIB$M_WRITE;
#endif
}

static void DoIO(int i) {
    int status;
    int ioSize;

    switch (TypeStop) {
    case KDuration:
        break;
    case KIOCount:
    case KSize:
        if (TotIO == 0)
            EndPass = 1;
        else
            --TotIO;
        break;
    default:
        fprintf(stderr, "\nInvalid stop switch\n");
        exit(SS$_ABORT);
    }
    if (!EndPass) {
        unsigned short channel = Channel[i];
        int writeIO;

        ++ActiveIOCount;

        if (TypeTest == KReadWrite) {
            writeIO = (100 * ((double)rand()) / 0x7fffffff) >= ReadPercent;
            ioSize = writeIO ? WriteIOSize : ReadIOSize;
            if (writeIO)
                ++WriteIOCount;
            else
                ++ReadIOCount;
        } else {
            ioSize = ReadIOSize;
            ++ReadIOCount;
        }
        if ((100 * ((double)rand()) / 0x7fffffff) >= CacheHitPercent)
            if (Random)
                Address[i] = StartingBlock + (EndingBlock - StartingBlock) *
                                                 ((double)rand()) / 0x7fffffff;
            else {
                Address[i] += (Interval == -1 ? ioSize : Interval);
                if (Address[i] + ioSize > EndingBlock)
                    Address[i] = StartingBlock;
            }
        switch (TypeTest) {
        case KReadOnly:
            status =
                sys$qio(0, channel, IO$_READLBLK, &Iosb[i], IOCompletionAST, i,
                        IOBuffer[i], 512 * ioSize, Address[i], 0, 0, 0);
            break;
        case KWriteOnly:
            status = sys$qio(0, channel, IO$_WRITEVBLK | VioModifier, &Iosb[i],
                             IOCompletionAST, i, IOBuffer[i], 512 * ioSize,
                             Address[i], 0, 0, 0);
            break;
        case KReadWrite:
            if (writeIO) {
                ++WriteCount;
                status = sys$qio(0, channel, IO$_WRITEVBLK | VioModifier,
                                 &Iosb[i], IOCompletionAST, i, IOBuffer[i],
                                 512 * ioSize, Address[i], 0, 0, 0);
            } else {
                ++ReadCount;
                status = sys$qio(0, channel, IO$_READVBLK | VioModifier,
                                 &Iosb[i], IOCompletionAST, i, IOBuffer[i],
                                 512 * ioSize, Address[i], 0, 0, 0);
            }
            break;
        default:
            fputs("Invalid test", stderr);
            exit(SS$_ABORT);
        }
        if (!$VMS_STATUS_SUCCESS(status))
            exit(status);
    } else if (ActiveIOCount == 0) {
        unsigned int pid = getpid();
        status = sys$wake(&pid, 0);
        if (!$VMS_STATUS_SUCCESS(status))
            exit(status);
    }
}

static void IOCompletionAST(int i) {
    --ActiveIOCount;
    DoIO(i);
}

#ifdef __cplusplus
static void TimerAST(...) {
#else
static void TimerAST() {
#endif
    EndPass = 1;
}

main() {
    int status;
    int i;
    FILE *fo;

    printf("%s %s, J.F. Pi?ronne, jf.pieronne@laposte.net\n", __DATE__,
           __TIME__);

    ProcessCommandLine();

    if (TypeTest != KReadOnly) {
        int i;
        atexit(DeleteFiles);
        for (i = 0; i < DisksCount; ++i) {
            strcpy(FileName[i], (char *)&FullDevnam[i]);
            strcat(FileName[i], "[000000]DISKPERF.TMP");
            printf("Temporary file used for write perfomance test :\n%s\n",
                   FileName[i]);
            CreateFile(&(Channel[i]), &(Fib[i]), FileName[i],
                       EndingBlock - StartingBlock + 1);
            IoAccess(Channel[i], &(Fib[i]));
        }
    } else {
        int i;
        for (i = 0; i < DisksCount; ++i) {
            char devnam[65];
            $DESCRIPTOR(devnamDesc, devnam);
            strcpy(devnam, FullDevnam[i]);
            devnamDesc.dsc$w_length = strlen(devnam);
            status = sys$assign(&devnamDesc, &(Channel[i]), 0, 0);
            if (!$VMS_STATUS_SUCCESS(status))
                exit(status);
        }
    }

    for (i = 0; i < Pass; ++i) {
        //    float waitTime = 15;
        //    int waitFlags = LIB$K_NOWAKE;

        for (ReadIOSize = MinIOSize; ReadIOSize <= MaxIOSize; ++ReadIOSize) {
            Stat_s *sp = &(StatInfo[i][ReadIOSize - 1]);
            int sio;
            int tio;
            int ts;
            int j;
            GENERIC_64 deltaTime;

            ReadIOCount = WriteIOCount = 0;

            if (TypeTest == KReadWrite)
                WriteIOSize = ReadIOSize / ReadWriteIOSizeRatio + 0.5;
            else
                WriteIOSize = 1;

            if (WriteIOSize == 0)
                WriteIOSize = 1;

            EndPass = 0;

            switch (TypeStop) {
            case KDuration:
                deltaTime.gen64$l_longword[0] = -10000000 * PassDuration;
                deltaTime.gen64$l_longword[1] = -1;
                status = sys$setimr(0, &deltaTime, TimerAST, 0, 0);
                if (!$VMS_STATUS_SUCCESS(status))
                    exit(status);
                break;
            case KIOCount:
                tio = TotIO;
                break;
            case KSize:
                TotIO = TotSize / ReadIOSize;
                break;
            default:
                fprintf(stderr, "\nInvalid stop switch\n");
                exit(SS$_ABORT);
            }

            for (j = 0; j < DisksCount; ++j)
                Address[j] = StartingBlock;

            lib$init_timer(NULL);

            status = sys$setast(0);
            if (!$VMS_STATUS_SUCCESS(status))
                exit(status);
            for (j = 0; j < SimultaneousIO; ++j) {
                int k;
                for (k = 0; k < DisksCount; ++k)
                    DoIO(k);
            }
            status = sys$setast(1);
            if (!$VMS_STATUS_SUCCESS(status))
                exit(status);

            while (!EndPass || ActiveIOCount) {
                status = sys$hiber();
                if (!$VMS_STATUS_SUCCESS(status))
                    exit(status);
            }

            j = 2;
            status = lib$stat_timer(&j, &(sp->_cpu));
            if (!$VMS_STATUS_SUCCESS(status))
                exit(status);

            j = 1;
            status = lib$stat_timer(&j, (unsigned int *)sp->_elapsed);
            if (!$VMS_STATUS_SUCCESS(status))
                exit(status);

            if (TypeStop == KIOCount)
                TotIO = tio;
            StatInfo[i][ReadIOSize - 1]._IOCount = ReadIOCount + WriteIOCount;
            StatInfo[i][ReadIOSize - 1]._ReadIOCount = ReadIOCount;
            StatInfo[i][ReadIOSize - 1]._WriteIOCount = WriteIOCount;
            //      lib$wait(&waitTime, &waitFlags);
            sleep(5);
        }
    }

    fo = fopen("diskperf.lis", "w");
    fprintf(fo, "%s and ",
            TypeTest == KWriteOnly  ? "Write"
            : TypeTest == KReadOnly ? "Read"
                                    : "Read-Write");
    fputs(Random ? "Random access test\n" : "Sequential access Test\n", fo);
    fprintf(fo, "Starting block %d, ending block %d, Simultaneous i/o %d\n",
            StartingBlock, EndingBlock, SimultaneousIO);
    fprintf(fo, "Giving cache hit percent : %d\n", CacheHitPercent);
    if (VioModifier || TypeTest == KReadOnly)
        fprintf(fo, "Virtual IO cache not active\n");
    else
        fprintf(fo, "Virtual IO cache active\n");
    if (TypeTest == KReadWrite)
        fprintf(
            fo,
            "Effective read percent : %4.1lf\nRead Size / Write Size : %lf\n",
            100 * ((double)ReadCount) / (ReadCount + WriteCount),
            ReadWriteIOSizeRatio);
    fputs("device name    label    type\n", fo);
    fputs("------------  --------  ----\n", fo);
    for (i = 0; i < DisksCount; ++i)
        fprintf(fo, "%-13s %-9s %-6s\n", &(FullDevnam[i][1]), Volnam[i],
                Medianam[i]);
    for (ReadIOSize = MinIOSize; ReadIOSize <= MaxIOSize; ++ReadIOSize) {
        if (TypeTest == KReadWrite)
            WriteIOSize = ReadIOSize / ReadWriteIOSizeRatio + 0.5;
        else
            WriteIOSize = 1;
        if (WriteIOSize == 0)
            WriteIOSize = 1;

        fprintf(fo, "\n!%3d !", ReadIOSize);
        for (i = 0; i < Pass; ++i) {
            Stat_s *sp = &(StatInfo[i][ReadIOSize - 1]);
            fprintf(
                fo, " %5d IOs %2d ms %4d IO/s %6d KB/s!", sp->_IOCount,
                -(int)((double)sp->_elapsed[0] / 10000.0 / sp->_IOCount *
                       SimultaneousIO * DisksCount),
                -(int)((double)(sp->_IOCount) * 10000000.0 / sp->_elapsed[0]),
                -(int)((double)(sp->_ReadIOCount) * ReadIOSize * 10000000.0 /
                       sp->_elapsed[0] / 2) -
                    (int)((double)(sp->_WriteIOCount) * WriteIOSize *
                          10000000.0 / sp->_elapsed[0] / 2));
        }
    }
    fclose(fo);
    fo = fopen("diskperf.dat", "w");
    fprintf(fo, "Size\tIOs\tService\tIO/s\tKB/s");
    for (ReadIOSize = MinIOSize; ReadIOSize <= MaxIOSize; ++ReadIOSize) {
        double ioc = 0, ser = 0, ios = 0, kbs = 0;
        for (i = 0; i < Pass; ++i) {
            Stat_s *sp = &(StatInfo[i][ReadIOSize - 1]);
            ioc += sp->_IOCount;
            ser += -(double)sp->_elapsed[0] / 10000.0 / sp->_IOCount *
                   SimultaneousIO * DisksCount;
            ios +=
                -(int)((double)(sp->_IOCount) * 10000000.0 / sp->_elapsed[0]);
            kbs += -(int)((double)(sp->_ReadIOCount) * ReadIOSize * 10000000.0 /
                          sp->_elapsed[0] / 2) -
                   (int)((double)(sp->_WriteIOCount) * WriteIOSize *
                         10000000.0 / sp->_elapsed[0] / 2);
        }
        ioc /= Pass;
        ser /= Pass;
        ios /= Pass;
        kbs /= Pass;
        fprintf(fo, "\n%3d\t%d\t%d\t%d\t%d", ReadIOSize, (int)ioc, (int)ser,
                (int)ios, (int)kbs);
        /*
            for(i = 0; i < Pass; ++i) {
              Stat_s* sp = &(StatInfo[i][ReadIOSize - 1]);
              fprintf(fo, "\t%5d\t%2d\t%4d\t%4d", sp->_IOCount,
                        - (int)((double)sp->_elapsed[0]
                                        / 10000.0
                                        / sp->_IOCount * SimultaneousIO *
           DisksCount),
                        - (int)((double)(sp->_IOCount) * 10000000.0 /
           sp->_elapsed[0]),
                        - (int)((double)(sp->_ReadIOCount) *
                            10000000.0 / sp->_elapsed[0] * ReadIOSize / 2)
                          - (int)((double)(sp->_WriteIOCount) *
                              10000000.0 / sp->_elapsed[0] * WriteIOSize / 2));
            }
        */
    }
    fclose(fo);
}