| /*
* MEMRES.C; demonstrate use of memory-resident sections.
* This program is purported to work on OpenVMS Alpha V7.1 and higher.
*
* To compile: $ CC/PREFIX=ALL MEMRES
* To link: $ LINK/SYSEXE MEMRES
* To define: $! MEMRES can be provided as a command by either:
* $ MEMRES :== $SYS$DISK:[]MEMRES.EXE ! (foreign command symbol)
* $! or (only one of these two commands is necessary):
* $ DEFINE/JOB DCL$PATH SYS$DISK:[] ! (tell DCL where to find)
* To run: $ MEMRES <section-name> <size-in-bytes>
*
* To delete a section, specify <section-name>, but omit <size-in-bytes>.
*
* *** Please Note:
*
* If the second argument specifies any number of bytes other than an
* exact multiple of the page size, it is interpreted as <size-in-pages>.
* Yes, that is a hazardous arrangement, however convenient it may be.
* Take care not to deplete the available free memory on your system.
* If I had known of a system data cell containing the current amount
* of free memory, I would have checked it against the amount requested.
*
* If a third argument of "mb" is specified, the second argument is
* interpreted as <size-in-Megabytes>.
*/
#include <builtins.h> /* Alpha built-in functions */
#include <c_asm.h> /* in-line assembler */
#include <descrip.h> /* string descriptors */
#include <hwrpbdef.h> /* H/W restart parameter block */
#include <ints.h> /* integer types */
#include <lib$routines.h> /* RTL LIB routines */
#include <secdef.h> /* section flags */
#include <ssdef.h> /* SS$_ message codes */
#include <starlet.h> /* system services */
#include <stdio.h> /* standard I/O */
#include <stdlib.h> /* standard library */
#include <string.h> /* string routines */
#include <stsdef.h> /* message code format */
#include <vadef.h> /* virtual address format/flags */
typedef struct
{
uint32 cnt;
uint32 off;
} pccs_t;
typedef union
{
uint64 all;
pccs_t pcc;
} pccu_t;
#pragma extern_model save
#pragma extern_model relaxed_refdef shr
extern const HWRPB *BOO$GA_HWRPB; /* pointer to restart parameter block */
extern const uint32 MMG$GL_MEMSIZE; /* actual memory size in pages */
extern const uint64 MMG$GQ_PAGE_SIZE; /* system page size in bytes */
extern const uint64 MMG$GQ_PTES_PER_PAGE; /* # of PTEs that fit on 1 PT page */
extern const uint32 EXE$GL_SYSTICK; /* # of 100-ns units per clock tick */
extern volatile int64 EXE$GQ_SYSTIME; /* current quadword system time */
#pragma extern_model restore
#define one_KB 1024uL
#define one_MB (one_KB * one_KB)
static const $DESCRIPTOR(default_gs_name,"tst-mem-res-sec");
static const uint32 create_gdzro_flags =
SEC$M_DZRO | SEC$M_GBL | SEC$M_PERM |
SEC$M_SYSGBL | SEC$M_MRES | SEC$M_WRT;
static const uint32 mgblsc_flags =
SEC$M_EXPREG | SEC$M_GBL | SEC$M_NO_OVERMAP | SEC$M_SYSGBL | SEC$M_WRT;
static const int64 systime_units_per_second = 10000000L;
static const char *access_mode_name[4] =
{ "kernel", "executive", "supervisor", "user" };
void show_pcc(pccu_t pcc_beg, pccu_t pcc_end)
{
pccu_t pcc_dif;
double cpu_cycle_time_seconds, cpu_seconds, cpu_systime_units;
int64 bintim;
struct dsc$descriptor_s ascdsc;
char asctim[24];
/* determine the CPU cycle time in seconds */
cpu_cycle_time_seconds = BOO$GA_HWRPB->hwrpb$iq_cycle_count_freq;
cpu_cycle_time_seconds = 1.0 / cpu_cycle_time_seconds;
/* calculate the elapsed and CPU times in cycles */
pcc_dif.pcc.cnt = pcc_end.pcc.cnt - pcc_beg.pcc.cnt;
pcc_dif.pcc.off = pcc_end.pcc.off - pcc_beg.pcc.off;
pcc_dif.pcc.off += pcc_dif.pcc.cnt;
/* calculate the CPU time in seconds */
cpu_seconds = cpu_cycle_time_seconds * (double)pcc_dif.pcc.off;
/* calculate the approximate CPU time in systime units */
cpu_systime_units = (cpu_seconds+0.005) * (double)systime_units_per_second;
bintim = -cpu_systime_units;
/* get text representation of CPU time interval */
ascdsc.dsc$w_length = sizeof(asctim) - 1;
ascdsc.dsc$b_dtype = DSC$K_DTYPE_T;
ascdsc.dsc$b_class = DSC$K_CLASS_S;
ascdsc.dsc$a_pointer = asctim;
sys$asctim(&ascdsc.dsc$w_length, &ascdsc, &bintim, 0);
asctim[ascdsc.dsc$w_length] = '\0';
/* display only the CPU time (elapsed seems error-prone on SMP systems) */
printf(" __RPCC() difference, CPU: %s; %E sec (%u cyc).\n",
asctim, cpu_seconds, pcc_dif.pcc.off);
}
void report_lkwset_failure(int failure, int64 failed_va, uint64 failed_length)
{
struct dsc$descriptor_s msgdsc;
char msg[256];
printf("Failed VA %016LX; %Lu page%s; ",
failed_va, failed_length / MMG$GQ_PAGE_SIZE,
(((failed_length / MMG$GQ_PAGE_SIZE) == 1) ? " " : "s"));
msgdsc.dsc$w_length = sizeof(msg) - 1;
msgdsc.dsc$b_dtype = DSC$K_DTYPE_T;
msgdsc.dsc$b_class = DSC$K_CLASS_S;
msgdsc.dsc$a_pointer = msg;
sys$getmsg(failure, &msgdsc.dsc$w_length, &msgdsc, 2, 0);
msg[msgdsc.dsc$w_length] = '\0';
printf("%s\n", msg);
}
int lock_all_regions()
{
int status, failure;
int64 region_start_va_64, lkwset_start_va_64, return_va_64, failed_va;
uint64 lkwset_length_64, return_length_64, failed_length, locked_length;
uint32 lregsum;
regsum regsum;
if (sizeof(regsum) != VA$C_REGSUM_LENGTH)
{
printf(" sizeof(regsum) = %u.\n", sizeof(regsum));
printf("VA$C_REGSUM_LENGTH = %u.\n", VA$C_REGSUM_LENGTH);
}
region_start_va_64 = -1L;
while ((status = sys$get_region_info(VA$_NEXT_REGSUM_BY_VA, 0,
region_start_va_64, 0, sizeof(regsum), ®sum, &lregsum)) & 1)
{
if (lregsum != sizeof(regsum))
{
printf(" lregsum = %u.\n", lregsum);
printf("sizeof(regsum) = %u.\n", sizeof(regsum));
}
printf("\nRegion ID: %Lu.\n", *((uint64*)regsum.va$q_region_id));
if (regsum.va$v_descend)
printf(" descending\n");
if (regsum.va$v_p0_space)
printf(" in P0 space\n");
if (regsum.va$v_p1_space)
printf(" in P1 space\n");
if (regsum.va$v_permanent)
printf(" permanent\n");
/*
if (regsum.va$v_no_clone)
printf(" no clone\n");
*/
if (regsum.va$v_shared_pts)
printf(" shared page tables\n");
printf("Region protection; owner mode: %s; create mode: %s\n",
access_mode_name[regsum.va$v_owner_mode],
access_mode_name[regsum.va$v_create_mode]);
printf(" Start VA %016LX\n", regsum.va$pq_start_va);
printf("1st Free VA %016LX\n", regsum.va$pq_first_free_va);
printf("Region Size %Lu bytes; %Lu page%s; %Lu MB\n",
*((uint64*)regsum.va$q_region_size),
*((uint64*)regsum.va$q_region_size) / MMG$GQ_PAGE_SIZE,
(((*((uint64*)regsum.va$q_region_size) / MMG$GQ_PAGE_SIZE) == 1) ? " " : "s"),
*((uint64*)regsum.va$q_region_size) / one_MB);
if (regsum.va$v_descend)
{
lkwset_start_va_64 = regsum.va$pq_first_free_va;
lkwset_start_va_64 += MMG$GQ_PAGE_SIZE;
lkwset_length_64 = regsum.va$pq_start_va;
lkwset_length_64 += *((uint64*)regsum.va$q_region_size);
lkwset_length_64 -= lkwset_start_va_64;
}
else
{
lkwset_start_va_64 = regsum.va$pq_start_va;
lkwset_length_64 = regsum.va$pq_first_free_va - regsum.va$pq_start_va;
}
printf("LKWSET VA %016LX; %Lu bytes; %Lu page%s; %Lu MB\n",
lkwset_start_va_64,
lkwset_length_64,
lkwset_length_64 / MMG$GQ_PAGE_SIZE,
(((lkwset_length_64 / MMG$GQ_PAGE_SIZE) == 1) ? " " : "s"),
lkwset_length_64 / one_MB);
failure = 0;
failed_va = 0L;
failed_length = 0uL;
locked_length = 0uL;
for (;;)
{
status = sys$lkwset_64(lkwset_start_va_64, lkwset_length_64, 0,
&return_va_64, &return_length_64);
if (return_va_64 == -1L)
{
return_length_64 = 0uL;
}
else
{
if (failed_length)
{
report_lkwset_failure(failure, failed_va, failed_length);
failure = 0;
failed_va = 0L;
failed_length = 0uL;
}
printf("Locked VA %016LX; %Lu page%s into working set.\n",
return_va_64, return_length_64 / MMG$GQ_PAGE_SIZE,
(((return_length_64 / MMG$GQ_PAGE_SIZE) == 1) ? " " : "s"));
lkwset_start_va_64 += return_length_64;
lkwset_length_64 -= return_length_64;
locked_length += return_length_64;
}
if (!(status & 1))
{
if (failure == 0) failure = status;
if (status != failure)
{
report_lkwset_failure(failure, failed_va, failed_length);
failure = status;
failed_va = lkwset_start_va_64;
failed_length = 0uL;
}
else if (return_length_64)
{
failed_va = lkwset_start_va_64;
}
lkwset_start_va_64 += MMG$GQ_PAGE_SIZE;
lkwset_length_64 -= MMG$GQ_PAGE_SIZE;
failed_length += MMG$GQ_PAGE_SIZE;
}
if (status == SS$_LKWSETFUL) break;
if (lkwset_length_64 == 0uL) break;
} /* keep trying until done or lock limit reached */
if (failed_length)
{
report_lkwset_failure(failure, failed_va, failed_length);
failure = 0;
failed_va = 0L;
failed_length = 0uL;
}
printf("Total of %Lu bytes; %Lu page%s; %Lu MB locked in region ID %Lu.\n",
locked_length,
locked_length / MMG$GQ_PAGE_SIZE,
(((locked_length / MMG$GQ_PAGE_SIZE) == 1) ? " " : "s"),
locked_length / one_MB,
*((uint64*)regsum.va$q_region_id));
region_start_va_64 = regsum.va$pq_start_va;
region_start_va_64 += *((uint64*)regsum.va$q_region_size);
}
return status;
}
int memres(int argc, char *argv[])
#pragma nostandard
main_program
#pragma standard
{
int status;
int msgvec[2] = {1,0};
int64 systime_units_per_clock_int;
int64 d64;
uint64 i64, n64, i64prv, i64dsp;
uint64 nbytes, nclkint, kclkint;
uint64 clear_length;
uint64 region_id;
uint64 region_length;
uint64 mapped_length;
uint64 unmapped_length;
uint64 deleted_length;
uint64 reserved_length;
struct dsc$descriptor_s gs_name;
register pccu_t pcc_beg, pcc_end;
#pragma required_pointer_size save
#pragma required_pointer_size long
void *region_va;
void *mapped_va;
void *unmapped_va;
void *deleted_va;
int64 *p64;
void *_memset64(void *__s, int __c, size_t __n);
#pragma required_pointer_size restore
/* determine the expected system time increment for each clock interrupt */
systime_units_per_clock_int = systime_units_per_second;
systime_units_per_clock_int <<= 12L; /* factor of 4096 */
systime_units_per_clock_int /= BOO$GA_HWRPB->hwrpb$iq_clock_int_freq;
/* this *should* be the same as EXE$GL_SYSTICK */
if (systime_units_per_clock_int != EXE$GL_SYSTICK)
{
printf("EXE$GL_SYSTICK = %u.\n", EXE$GL_SYSTICK);
printf("expected value = %Ld.\n", systime_units_per_clock_int);
}
/* determine the global section name */
gs_name.dsc$b_dtype = DSC$K_DTYPE_T;
gs_name.dsc$b_class = DSC$K_CLASS_S;
if (argc < 2)
{
gs_name.dsc$w_length = default_gs_name.dsc$w_length;
gs_name.dsc$a_pointer = default_gs_name.dsc$a_pointer;
}
else
{
gs_name.dsc$w_length = strlen(argv[1]);
gs_name.dsc$a_pointer = argv[1];
}
/* determine the section size in bytes */
nbytes = 0uL;
if (argc > 2)
{
nbytes = atoq(argv[2]);
if ((argc > 3) && !strcmp(argv[3],"mb"))
{
nbytes *= one_MB;
}
else if (nbytes % MMG$GQ_PAGE_SIZE)
{
printf("Section size (second argument)\n");
printf(" is not an exact multiple of the page size (%Lu bytes);\n",
MMG$GQ_PAGE_SIZE);
printf(" interpreting value as %Lu pages,\n", nbytes);
nbytes *= MMG$GQ_PAGE_SIZE;
printf(" or %Lu bytes.\n", nbytes);
}
if (nbytes > (MMG$GQ_PAGE_SIZE*(MMG$GL_MEMSIZE/2)))
{
printf("You asked for more than half the memory on the system.\n");
printf("This seems unreasonable, so you will get only one page.\n");
nbytes = MMG$GQ_PAGE_SIZE;
}
}
/*
printf("System memory size: %Lu bytes; %Lu pages; %Lu MB\n",
MMG$GL_MEMSIZE * MMG$GQ_PAGE_SIZE,
MMG$GL_MEMSIZE,
MMG$GL_MEMSIZE * MMG$GQ_PAGE_SIZE / one_MB);
printf("Page table entry size: %Lu bytes.\n",
MMG$GQ_PAGE_SIZE / MMG$GQ_PTES_PER_PAGE);
printf("Memory mapped by one PTE page: %Lu bytes; %Lu pages; %Lu MB\n",
MMG$GQ_PAGE_SIZE * MMG$GQ_PTES_PER_PAGE,
MMG$GQ_PTES_PER_PAGE,
MMG$GQ_PAGE_SIZE * MMG$GQ_PTES_PER_PAGE / one_MB);
*/
/*
* If section size in bytes is non-zero, try to create a new section.
* If a section with the same name already exists, use that one.
* Map the section into (64-bit) process virtual address space.
* Create a special region so that shared page tables can be used.
*/
if (nbytes)
{
/* try to create a new section */
try_create_gdzro:
reserved_length = 0uL;
printf("Trying to create a new section \"%s\"...\n", gs_name.dsc$a_pointer);
lib$init_timer(0);
pcc_beg.all = __RPCC();
status = sys$create_gdzro(
&gs_name, /* gs_name_64 */
0uL, /* ident_64 */
0, /* prot */
nbytes, /* length_64 */
0, /* acmode */
create_gdzro_flags, /* flags */
&reserved_length); /* reserved_length_64 */
pcc_end.all = __RPCC();
lib$show_timer(0);
show_pcc(pcc_beg, pcc_end);
msgvec[1] = status;
sys$putmsg(msgvec,0,0,0);
if (((status == SS$_MRES_PFNSMALL)
|| (status == SS$_INSFLPGS))
&& (reserved_length != 0uL)
&& (nbytes > reserved_length))
{
printf(
"Trying again; reducing request from %Lu to reserved %Lu bytes.\n",
nbytes, reserved_length);
nbytes = reserved_length;
goto try_create_gdzro;
}
else if (status == SS$_DUPLNAM)
{
printf("Section \"%s\" already exists;\n", gs_name.dsc$a_pointer);
}
else if (status & 1)
{
printf("New section \"%s\" created;\n", gs_name.dsc$a_pointer);
}
else
{
fprintf(stderr, "Failed to create section \"%s\".\n",
gs_name.dsc$a_pointer);
return status | STS$M_INHIB_MSG;
}
printf(" Reserved Length: %Lu bytes; %Lu pages; %Lu MB\n",
reserved_length,
reserved_length / MMG$GQ_PAGE_SIZE,
reserved_length / one_MB);
/* create a region with suitable attributes */
region_id = 0;
region_va = 0;
region_length = 0uL;
printf("Trying to create a region for mapping the section...\n");
lib$init_timer(0);
pcc_beg.all = __RPCC();
status = sys$create_region_64(
nbytes, /* length_64 */
VA$C_REGION_UCREATE_UOWN, /* region_prot */
VA$M_SHARED_PTS, /* flags */
®ion_id, /* return_region_id_64 */
®ion_va, /* return_va_64 */
®ion_length, /* return_length_64 */
0uL); /* start_va_64 */
pcc_end.all = __RPCC();
lib$show_timer(0);
show_pcc(pcc_beg, pcc_end);
msgvec[1] = status;
sys$putmsg(msgvec,0,0,0);
if (!(status & 1))
{
fprintf(stderr, "Failed to create region for section.\n");
return status | STS$M_INHIB_MSG;
}
printf("Created region ID %Lu for section;\n", region_id);
printf(" Region VA %016LX; %Lu bytes; %Lu pages; %Lu MB\n",
region_va,
region_length,
region_length / MMG$GQ_PAGE_SIZE,
region_length / one_MB);
/* use the region to map the section */
mapped_va = 0;
mapped_length = 0uL;
printf("Trying to map the section using the region...\n");
lib$init_timer(0);
pcc_beg.all = __RPCC();
status = sys$mgblsc_64(
&gs_name, /* gs_name_64 */
0uL, /* ident_64 */
®ion_id, /* region_id_64 */
0uL, /* section_offset_64 */
nbytes, /* length_64 */
0, /* acmode */
mgblsc_flags, /* flags */
&mapped_va, /* return_va_64 */
&mapped_length, /* return_length_64 */
0uL); /* start_va_64 */
pcc_end.all = __RPCC();
lib$show_timer(0);
show_pcc(pcc_beg, pcc_end);
msgvec[1] = status;
sys$putmsg(msgvec,0,0,0);
if (!(status & 1))
{
fprintf(stderr, "Failed to map section.\n");
return status | STS$M_INHIB_MSG;
}
printf("Mapped memory-resident section \"%s\";\n", gs_name.dsc$a_pointer);
printf(" Mapped VA %016LX; %Lu bytes; %Lu pages; %Lu MB\n",
mapped_va,
mapped_length,
mapped_length / MMG$GQ_PAGE_SIZE,
mapped_length / one_MB);
/* try locking pages from all regions into working set (just for fun) */
lock_all_regions();
/* describe the section as an array of signed quadword integers (time stamps) */
n64 = mapped_length / sizeof(int64);
p64 = mapped_va;
/* increment the mapping count in the lowest quadword */
p64[0]++;
i64 = p64[0];
printf("This program has now mapped this section %Ld times.\n", i64);
/* clear any remaining section memory */
clear_length = mapped_length - (sizeof(int64) * i64);
if (clear_length < 0x100000000uL)
{
printf("Clearing %Lu section bytes...\n", clear_length);
lib$init_timer(0);
pcc_beg.all = __RPCC();
_memset64(&p64[i64], 0, clear_length);
pcc_end.all = __RPCC();
lib$show_timer(0);
show_pcc(pcc_beg, pcc_end);
}
/* write some SYSTIME time stamps,
printf("Writing %Lu quadword SYSTIME stamps...\n", n64-i64);
lib$init_timer(0);
pcc_beg.all = __RPCC();
for (i64 = p64[0]; i64 < n64; i64++) p64[i64] = EXE$GQ_SYSTIME;
pcc_end.all = __RPCC();
lib$show_timer(0);
show_pcc(pcc_beg, pcc_end);
/* look for and list changes in the time stamp values */
nclkint = 0uL;
i64prv = i64dsp = 1;
for (i64 = 2; i64 < n64; i64++)
{
d64 = p64[i64] - p64[i64prv];
if (d64)
{
kclkint = d64 / systime_units_per_clock_int;
if (((kclkint < 0) ? -kclkint : kclkint) < 50)
{
nclkint += kclkint;
}
else
{
if (nclkint)
{
printf("(%Lu clock interrupts found in %Ld time stamps)\n",
nclkint, i64-i64dsp);
nclkint = 0uL;
}
printf(
"p64[%5Ld]: %+Ld.%07Lu seconds (%+Ld clock interrupts).\n",
i64, d64 / systime_units_per_second,
((d64 < 0L) ? -d64 : d64) % systime_units_per_second, kclkint);
i64dsp = i64;
}
i64prv = i64;
}
}
if (nclkint)
{
printf("(%Lu clock interrupts found in %Ld time stamps)\n",
nclkint, n64-i64dsp);
nclkint = 0;
}
/*
* Delete the region (and any address space within it), just to show
* that it can be done. Image rundown would have done this anyway.
*/
printf("Trying to delete region ID %Lu...\n", region_id);
lib$init_timer(0);
pcc_beg.all = __RPCC();
status = sys$delete_region_64(
®ion_id, /* region_id_64 */
0, /* acmode */
&deleted_va, /* return_va_64 */
&deleted_length); /* return_length_64 */
pcc_end.all = __RPCC();
lib$show_timer(0);
show_pcc(pcc_beg, pcc_end);
msgvec[1] = status;
sys$putmsg(msgvec,0,0,0);
if (!(status & 1))
{
fprintf(stderr, "Failed to delete region ID %Lu.\n", region_id);
return status | STS$M_INHIB_MSG;
}
printf("Deleted region ID %Lu.\n", region_id);
printf(" Deleted VA %016LX; %Lu bytes; %Lu pages; %Lu MB\n",
deleted_va,
deleted_length,
deleted_length / MMG$GQ_PAGE_SIZE,
deleted_length / one_MB);
}
/* section size is unspecified or zero; delete named system global section */
else
{
printf("Trying to delete section \"%s\"...\n", gs_name.dsc$a_pointer);
lib$init_timer(0);
pcc_beg.all = __RPCC();
status = sys$dgblsc(SEC$M_SYSGBL, &gs_name, 0);
pcc_end.all = __RPCC();
lib$show_timer(0);
show_pcc(pcc_beg, pcc_end);
msgvec[1] = status;
sys$putmsg(msgvec,0,0,0);
if (!(status & 1))
{
fprintf(stderr, "Failed to delete system global section \"%s\".\n",
gs_name.dsc$a_pointer);
return status | STS$M_INHIB_MSG;
}
printf("Deleted system global section \"%s\".\n", gs_name.dsc$a_pointer);
}
return status;
}
/*
* The sequence of calls to (1) sys$create_gdzro, (2) sys$create_region_64,
* (3) sys$mgblsc_64 (which was used above) could have been replaced by a
* sequence of calls to (1) sys$create_region_64, (2) sys$crmpsc_gdzro_64.
* The single sys$crmpsc_gdzro_64 service combines the actions of both the
* sys$create_gdzro and sys$mgblsc_64 services. The call would have looked
* something like the following:
*/
/*
mapped_va = 0;
mapped_length = 0uL;
reserved_length = 0uL;
status = sys$crmpsc_gdzro_64(
&gs_name,
0uL,
0,
nbytes,
®ion_id,
0uL,
0,
create_gdzro_flags | mgblsc_flags,
&mapped_va,
&mapped_length,
0uL,
nbytes,
&reserved_length);
*/
/*
* The sys$deltva_64 service could have been used to unmap all or part
* of the address space mapped to the section. The call would have
* looked something like the following:
*/
/*
status = sys$deltva_64(
®ion_id,
mapped_va,
mapped_length,
0,
&unmapped_va,
&unmapped_length);
*/
|