/***************************************************************************** /* VM.c Virtual memory support functions for HTTPd using the LIB$*_VM_* routines. The reason for not using generic memory routines such as calloc() and malloc() for frequently requested and then disposed of dynamic memory is of course efficiency. The theory goes like this ... tailoring VMS memory zones against the expected request profile for it's use will result in faster and more efficient memory management. An added, and not inconsequential bonus, is the additional integrity checking the library routines can provide. A general zone is created for non-specific allocations. Zones for fixed sized memory blocks are created for the HTTP/2 and request structures. An individual zone is created for each HTTP/2 connection's and request's heap. The overhead of zone creation will be offset by the lower overhead required for managing a smaller and short-lived collection of chunks, and the documentation specifically states that reseting or deleting a memory zone is a more efficient method of disposing of virtual memory than individually freeing each chunk. A separate zone is created for each of volatile and permanent cache memory. All memory allocations contain a small prologue structure immediately forward of the location returned to the caller. This provides some memory management data (chunk size and guard magic) and 9 bytes of space immediately adjacent to the first returned byte. This space is intended to contain an HTTP/2 frame header immediately preceding data to be written to the network. It is always present in allocated memory whether used for HTTP/2 framing or not and its presence detected through some specific magic stored immediately preceding (and so "close") to the returned pointer. See HTTP2.c and HTTP2NET.C for further background, example usage, etc. MEMORY MANAGEMENT IS CONSIDERED CRUCIAL TO HTTPD FUNCTIONING. IF AN ALLOCATION OR FREEING GENERATES AN ERROR (E.G. INSUFFICIENT VIRTUAL, BAD BLOCK, ETC.) THEN THIS IS CONSIDERED SERIOUS (ESPECIALLY THE LATTER, INDICATING STRUCTURE CORRUPTION) AND THE SERVER EXITS REPORTING STATUS INFORMATION. Hence calls to memory allocation and freeing do not need to check for success as only successful operations will return to them! VERSION HISTORY --------------- 04-AUG-2015 MGD support HTTP/2 specific memory management add magic allowing various allocations to be differentiated HTTP2_STRUCT and REQUEST_STRUCT sizes via external storage 14-MAR-2011 MGD sizeof(VM_STRUCT) now 8 (natural alignment) instead of 4 04-JUL-2006 MGD use PercentOf() for more accurate percentages 16-APR-2005 MGD modify statistics to a maximum of 1024 pages and granularity of 8 (GZIP significantly increased memory requirements) 26-MAR-2005 MGD VmRequestTune() set 'VmRequestSizePages' dynamically 22-MAY-2004 MGD bugfix; VmGetRequest() error exit if zone create fails 27-JAN-2004 MGD VmCacheInit() and VmPermCacheInit() extend size reduced 09-DEC-2003 MGD VmGeneralInit() upped from 1024/1024 to 4096/4096 18-JUN-2003 MGD refine VmRequestTune() 14-JUN-2003 MGD request heap statistics and VmRequestTune() 24-MAY-2003 MGD VmPermCache..() 29-SEP-2001 MGD instance support 04-AUG-2001 MGD support module WATCHing 26-FEB-2001 MGD observation indicates VmGeneralInit() extend up to 1024 04-MAR-2000 MGD use FaolToNet(), et.al. 05-FEB-2000 MGD add module name and line number to VmFree/Realloc...() 16-JUL-1998 MGD observation indicates VmGeneralInit() initial up to 1024 07-DEC-1997 MGD using LIB$*_VM_* routines for virtual memory manipulation (and unbundled from support.c for v5.0) */ /*****************************************************************************/ #ifdef WASD_VMS_V7 #undef _VMS__V6__SOURCE #define _VMS__V6__SOURCE #undef __VMS_VER #define __VMS_VER 70000000 #undef __CRTL_VER #define __CRTL_VER 70000000 #endif /* standard C header files */ #include #include #include /* VMS related header files */ #include #include #include #include #include /* application related header files */ #include "wasd.h" #define WASD_MODULE "VM" /******************/ /* global storage */ /******************/ /* number of pages in initial and extend request virtual memory zone */ #define VM_REQUEST_SIZE_PAGES 48 #define VM_REQUEST_SIZE_PAGES_MIN 24 #define VM_REQUEST_SIZE_PAGES_MAX 1024 #define VM_REQUEST_SIZE_PAGES_STAT_MAX 1024 #define VM_REQUEST_SIZE_PAGES_STAT_GRANULE 8 /* Where appropriate allocate an extra few bytes for carriage-control in buffers, null string terminators, programming errors ;^) etc. */ #define VM_ELBOW_ROOM 8 const int VmStructSize = sizeof(VM_STRUCT); ulong VmCacheVmZoneId, VmGeneralVmZoneId, VmHttp2SizePages, VmHttp2VmZoneId, VmPermCacheVmZoneId, VmRequestExtendPages, VmRequestSizePages, VmRequestVmZoneId; /* statistics are accumulated in a combination of ints and the structure */ #define VM_STATS_MAXMIN 0xffffffff ulong VmHeapStatsCount, VmHeapStatsGetCountMax, VmHeapStatsGetCountMin, VmHeapStatsFreeCountMax, VmHeapStatsFreeCountMin, VmHeapStatsReallocCountMax, VmHeapStatsReallocCountMin, VmHeapStatsResetCount, VmHeap2StatsCount, VmHeap2StatsGetCountMax, VmHeap2StatsGetCountMin, VmHeap2StatsFreeCountMax, VmHeap2StatsFreeCountMin, VmHeap2StatsReallocCountMax, VmHeap2StatsReallocCountMin, VmHeap2StatsResetCount; ulong VmHeapStatsPageCount [(VM_REQUEST_SIZE_PAGES_STAT_MAX/ VM_REQUEST_SIZE_PAGES_STAT_GRANULE)+1]; ulong VmHeap2StatsPageCount [(VM_REQUEST_SIZE_PAGES_STAT_MAX/ VM_REQUEST_SIZE_PAGES_STAT_GRANULE)+1]; struct HeapStatsStruct VmHeapStats, VmHeap2Stats; /********************/ /* external storage */ /********************/ extern int CacheEntryKBytesMax, CacheTotalKBytesMax, Http2StructSize, NetConcurrentMax, RequestStructSize; extern char ErrorSanityCheck[], HttpProtocol[], SoftwareID[]; extern CONFIG_STRUCT Config; extern HTTPD_GBLSEC *HttpdGblSecPtr; extern WATCH_STRUCT Watch; /*****************************************************************************/ /* Initialize the virtual memory zone general memory will be allocated from. */ VmGeneralInit () { static $DESCRIPTOR (ZoneNameDsc, "HTTPd General"); static ulong Algorithm = LIB$K_VM_QUICK_FIT, AlgorithmArg = 64, Flags = LIB$M_VM_BOUNDARY_TAGS | LIB$M_VM_GET_FILL0 | LIB$M_VM_TAIL_LARGE, ExtendSize = 4096, InitialSize = 4096, BlockSize = 64; int status; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (WATCHALL, WATCH_MOD_VM, "VmGeneralInit()"); /* create the cache virtual memory zone */ status = lib$create_vm_zone (&VmGeneralVmZoneId, &Algorithm, &AlgorithmArg, &Flags, &ExtendSize, &InitialSize, &BlockSize, 0, 0, 0, &ZoneNameDsc, 0, 0); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$create_vm_zone()", FI_LI); } /*****************************************************************************/ /* Allocate memory for general use. */ uchar* VmGet (ulong ChunkSize) { int status; ulong BaseAddress; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (WATCHALL, WATCH_MOD_VM, "VmGet() !UL", ChunkSize); ChunkSize += VmStructSize + VM_ELBOW_ROOM; status = lib$get_vm (&ChunkSize, &BaseAddress, &VmGeneralVmZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "VmGet() lib$get_vm()", FI_LI); ChunkSize -= VmStructSize + VM_ELBOW_ROOM; ((VM_STRUCT*)BaseAddress)->size = ChunkSize; ((VM_STRUCT*)BaseAddress)->magic = VM_MAGIC1; ((VM_STRUCT*)BaseAddress)->h2magic = VM_H2MAGIC; return ((uchar*)BaseAddress+VmStructSize); } /*****************************************************************************/ /* Expand (or even contract) an individual a general-use chunk. See VmGet(). */ uchar* VmRealloc ( uchar *ChunkPtr, ulong ChunkSize, char *SourceModuleName, int SourceLineNumber ) { int status, OriginalChunkSize; ulong BaseAddress, OriginalBaseAddress; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) { if (ChunkPtr) OriginalChunkSize = ((VM_STRUCT*)(ChunkPtr-VmStructSize))->size; else OriginalChunkSize = 0; WatchThis (WATCHALL, WATCH_MOD_VM, "VmRealloc() !UL !UL", OriginalChunkSize, ChunkSize); } if (!ChunkPtr) return (VmGet (ChunkSize)); OriginalBaseAddress = ChunkPtr - VmStructSize; if (((VM_STRUCT*)OriginalBaseAddress)->magic != VM_MAGIC1) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, SourceModuleName, SourceLineNumber); /* if this chunk satisfies the reallocation then just return it */ OriginalChunkSize = ((VM_STRUCT*)OriginalBaseAddress)->size; if (OriginalChunkSize >= ChunkSize) return (ChunkPtr); /* allocate a new, larger chunk */ ChunkSize += VmStructSize + VM_ELBOW_ROOM; status = lib$get_vm (&ChunkSize, &BaseAddress, &VmGeneralVmZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "VmRealloc() lib$get_vm()", SourceModuleName, SourceLineNumber); ChunkSize -= VmStructSize + VM_ELBOW_ROOM; /* copy the existing chunk into the new chunk */ memcpy ((uchar*)BaseAddress, (uchar*)OriginalBaseAddress, OriginalChunkSize + VmStructSize + VM_ELBOW_ROOM); /* update the chunk size */ ((VM_STRUCT*)BaseAddress)->size = ChunkSize; /* free the previous chunk */ status = lib$free_vm (0, &OriginalBaseAddress, &VmGeneralVmZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "VmRealloc() lib$free_vm()", SourceModuleName, SourceLineNumber); return ((uchar*)BaseAddress+VmStructSize); } /*****************************************************************************/ /* Release memory allocated for general use. */ VmFree ( uchar *ChunkPtr, char *SourceModuleName, int SourceLineNumber ) { int status, ChunkSize; ulong BaseAddress; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) { ChunkSize = ((VM_STRUCT*)(ChunkPtr-VmStructSize))->size; WatchThis (WATCHALL, WATCH_MOD_VM, "VmFree() !UL", ChunkSize); } BaseAddress = ChunkPtr - VmStructSize; if (((VM_STRUCT*)BaseAddress)->magic != VM_MAGIC1) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, SourceModuleName, SourceLineNumber); status = lib$free_vm (0, &BaseAddress, &VmGeneralVmZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "VmFree() lib$free_vm()", SourceModuleName, SourceLineNumber); } /*****************************************************************************/ /* Initialize the virtual memory zone for the request structures. */ VmRequestInit () { static $DESCRIPTOR (ZoneNameDsc, "HTTPd Request"); static ulong Algorithm = LIB$K_VM_QUICK_FIT, AlgorithmArg = 32, Flags = LIB$M_VM_BOUNDARY_TAGS | LIB$M_VM_GET_FILL0 | LIB$M_VM_TAIL_LARGE, InitialSize, BlockSize = 64; int status; char *cptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (WATCHALL, WATCH_MOD_VM, "VmRequestInit() !UL", NetConcurrentMax); cptr = getenv("WASD_VM_REQUEST_SIZE_PAGES"); if (cptr) VmRequestSizePages = atol(cptr); else { InstanceMutexLock (INSTANCE_MUTEX_HTTPD); VmRequestSizePages = HttpdGblSecPtr->VmRequestSizePages; InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD); } if (VmRequestSizePages < 24 || VmRequestSizePages > 128) VmRequestSizePages = VM_REQUEST_SIZE_PAGES; InitialSize = (NetConcurrentMax * RequestStructSize) / 512 + 1; /* create the cache virtual memory zone */ status = lib$create_vm_zone (&VmRequestVmZoneId, &Algorithm, &AlgorithmArg, &Flags, 0, &InitialSize, &BlockSize, 0, 0, 0, &ZoneNameDsc, 0, 0); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$create_vm_zone()", FI_LI); VmHeapStatsGetCountMin = VmHeapStatsFreeCountMin = VmHeapStatsReallocCountMin = VmHeapStats.FreeByteMin = VmHeapStats.GetByteMin = VmHeapStats.ReallocByteMin = VM_STATS_MAXMIN; } /*****************************************************************************/ /* Called periodically by HttpTick() and also on an ad hoc basis when VmReport() is used. Check most commonly required request heap pages and store it away for the next startup. I know the way the pages are calculated before being stored into the array (VmFreeRequest()) is not 100% percent accurate with what may have actually been used by LIB$VM but as this statistic is not available from anywhere I know of and it's not worth fooling around EXEC code to get it this will be close enough for jazz. */ VmRequestTune () { int idx; ulong PageCount, RequestCount, RequestCountTotal; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (WATCHALL, WATCH_MOD_VM, "VmRequestTune()"); PageCount = RequestCount = RequestCountTotal = 0; for (idx = 0; idx < VM_REQUEST_SIZE_PAGES_MAX / VM_REQUEST_SIZE_PAGES_STAT_GRANULE; idx++) RequestCountTotal += VmHeapStatsPageCount[idx]; /* only take any notice of this if there's been at least 1,000 requests */ if (RequestCountTotal < 1000) return; for (idx = 0; idx < VM_REQUEST_SIZE_PAGES_MAX / VM_REQUEST_SIZE_PAGES_STAT_GRANULE; idx++) { if (!VmHeapStatsPageCount[idx]) continue; RequestCount += VmHeapStatsPageCount[idx]; /* if the percentage calculation would overflow forget it! */ if (RequestCount * 100 < RequestCount) return; /* if this still accounts for less than eighty percent of requests */ if (RequestCount * 100 / RequestCountTotal < 80) continue; break; } PageCount = (idx + 1) * VM_REQUEST_SIZE_PAGES_STAT_GRANULE; if (WATCH_MODULE(WATCH_MOD_VM)) WatchDataFormatted ("!UL !UL !UL% !UL\n", RequestCountTotal, RequestCount, PercentOf(RequestCount,RequestCountTotal), PageCount); if (PageCount) { /* change the value dynamically */ VmRequestSizePages = PageCount; /* place the value in the global section for next startup */ InstanceMutexLock (INSTANCE_MUTEX_HTTPD); HttpdGblSecPtr->VmRequestSizePages = PageCount; InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD); } } /*****************************************************************************/ /* Allocate a request structure with associated virtual memory zone ready for heap allocation. */ REQUEST_STRUCT* VmGetRequest () { static $DESCRIPTOR (ZoneNameDsc, "HTTPd Heap"); static ulong Algorithm = LIB$K_VM_QUICK_FIT, AlgorithmArg = 16, Flags = LIB$M_VM_BOUNDARY_TAGS | LIB$M_VM_GET_FILL0 | LIB$M_VM_TAIL_LARGE, BlockSize = 64; int status; ulong BaseAddress; REQUEST_STRUCT *rqptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (WATCHALL, WATCH_MOD_VM, "VmGetRequest()"); status = lib$get_vm (&RequestStructSize, &BaseAddress, &VmRequestVmZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$get_vm()", FI_LI); rqptr = (REQUEST_STRUCT*)BaseAddress; /* now create a virtual memory zone for the request's heap */ rqptr->VmHeapZoneId = 0; status = lib$create_vm_zone (&rqptr->VmHeapZoneId, &Algorithm, &AlgorithmArg, &Flags, &VmRequestSizePages, &VmRequestSizePages, &BlockSize, 0, 0, 0, &ZoneNameDsc, 0, 0); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$create_vm_zone()", FI_LI); rqptr->rqHeapStats.FreeByteMin = rqptr->rqHeapStats.GetByteMin = rqptr->rqHeapStats.ReallocByteMin = VM_STATS_MAXMIN; return (rqptr); } /*****************************************************************************/ /* Delete any virtual memory zone created for the request's heap, then return the request structure to the request virtual memory pool. */ VmFreeRequest ( REQUEST_STRUCT *rqptr, char *SourceModuleName, int SourceLineNumber ) { static ulong PrevByteCount; int status; ulong PageCount; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_VM)) { PageCount = rqptr->rqHeapStats.ByteCount >> 9; if (rqptr->rqHeapStats.ByteCount & 0x1ff) PageCount++; WatchThis (WATCHITM(rqptr), WATCH_MOD_VM, "VmFreeRequest()"); WatchDataFormatted ( "ByteCount: !SL PageCount: !SL VmRequestSizePages: !SL\n\ GetCount: !SL GetByteCount: !SL GetByteMax: !SL GetByteMin: !SL\n\ FreeCount: !SL FreeByteCount: !SL FreeByteMax: !SL FreeByteMin: !SL\n\ ReallocCount: !SL ReallocByteCount: !SL \ ReallocByteMax: !SL ReallocByteMin: !SL\n", rqptr->rqHeapStats.ByteCount, PageCount, VmRequestSizePages, rqptr->rqHeapStats.GetCount, rqptr->rqHeapStats.GetByteCount, rqptr->rqHeapStats.GetByteMax, rqptr->rqHeapStats.GetByteMin, rqptr->rqHeapStats.FreeCount, rqptr->rqHeapStats.FreeByteCount, rqptr->rqHeapStats.FreeByteMax, rqptr->rqHeapStats.FreeByteMin, rqptr->rqHeapStats.ReallocCount, rqptr->rqHeapStats.ReallocByteCount, rqptr->rqHeapStats.ReallocByteMax, rqptr->rqHeapStats.ReallocByteMin); } VmHeapStats.ByteCount += rqptr->rqHeapStats.ByteCount; if (VmHeapStats.ByteCount < PrevByteCount) { /* no nicker knotting here, just detect and reset */ memset (&VmHeapStats, 0, sizeof(VmHeapStats)); memset (&VmHeapStatsPageCount, 0, sizeof(VmHeapStatsPageCount)); VmHeapStatsGetCountMax = VmHeapStatsFreeCountMax = VmHeapStatsReallocCountMax = VmHeapStatsCount = 0; VmHeapStatsGetCountMin = VmHeapStatsFreeCountMin = VmHeapStatsReallocCountMin = VmHeapStats.FreeByteMin = VmHeapStats.GetByteMin = VmHeapStats.ReallocByteMin = VM_STATS_MAXMIN; VmHeapStatsResetCount++; VmHeapStats.ByteCount = rqptr->rqHeapStats.ByteCount; } PrevByteCount = VmHeapStats.ByteCount; VmHeapStatsCount++; PageCount = rqptr->rqHeapStats.ByteCount >> 9; if (rqptr->rqHeapStats.ByteCount & 0x1ff) PageCount++; if (PageCount >= VM_REQUEST_SIZE_PAGES_STAT_MAX) VmHeapStatsPageCount[VM_REQUEST_SIZE_PAGES_STAT_MAX/ VM_REQUEST_SIZE_PAGES_STAT_GRANULE]++; else VmHeapStatsPageCount[PageCount/VM_REQUEST_SIZE_PAGES_STAT_GRANULE]++; if (rqptr->rqHeapStats.GetCount) { VmHeapStats.GetCount += rqptr->rqHeapStats.GetCount; if (rqptr->rqHeapStats.GetCount > VmHeapStatsGetCountMax) VmHeapStatsGetCountMax = rqptr->rqHeapStats.GetCount; if (rqptr->rqHeapStats.GetCount < VmHeapStatsGetCountMin) VmHeapStatsGetCountMin = rqptr->rqHeapStats.GetCount; if (rqptr->rqHeapStats.GetByteMax > VmHeapStats.GetByteMax) VmHeapStats.GetByteMax = rqptr->rqHeapStats.GetByteMax; if (rqptr->rqHeapStats.GetByteMin != VM_STATS_MAXMIN && rqptr->rqHeapStats.GetByteMin < VmHeapStats.GetByteMin) VmHeapStats.GetByteMin = rqptr->rqHeapStats.GetByteMin; } if (rqptr->rqHeapStats.FreeCount) { VmHeapStats.FreeCount += rqptr->rqHeapStats.FreeCount; if (rqptr->rqHeapStats.FreeCount > VmHeapStatsFreeCountMax) VmHeapStatsFreeCountMax = rqptr->rqHeapStats.FreeCount; if (rqptr->rqHeapStats.FreeCount < VmHeapStatsFreeCountMin) VmHeapStatsFreeCountMin = rqptr->rqHeapStats.FreeCount; if (rqptr->rqHeapStats.FreeByteMax > VmHeapStats.FreeByteMax) VmHeapStats.FreeByteMax = rqptr->rqHeapStats.FreeByteMax; if (rqptr->rqHeapStats.FreeByteMin != VM_STATS_MAXMIN && rqptr->rqHeapStats.FreeByteMin < VmHeapStats.FreeByteMin) VmHeapStats.FreeByteMin = rqptr->rqHeapStats.FreeByteMin; } if (rqptr->rqHeapStats.ReallocCount) { VmHeapStats.ReallocCount += rqptr->rqHeapStats.ReallocCount; if (rqptr->rqHeapStats.ReallocCount > VmHeapStatsReallocCountMax) VmHeapStatsReallocCountMax = rqptr->rqHeapStats.ReallocCount; if (rqptr->rqHeapStats.ReallocCount < VmHeapStatsReallocCountMin) VmHeapStatsReallocCountMin = rqptr->rqHeapStats.ReallocCount; if (rqptr->rqHeapStats.ReallocByteMax > VmHeapStats.ReallocByteMax) VmHeapStats.ReallocByteMax = rqptr->rqHeapStats.ReallocByteMax; if (rqptr->rqHeapStats.ReallocByteMin != VM_STATS_MAXMIN && rqptr->rqHeapStats.ReallocByteMin < VmHeapStats.ReallocByteMin) VmHeapStats.ReallocByteMin = rqptr->rqHeapStats.ReallocByteMin; } /* free the request structure's virtual memory */ if (rqptr->VmHeapZoneId) { /* delete the request's heap's virtual memory zone */ status = lib$delete_vm_zone (&rqptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$delete_vm_zone()", FI_LI); } status = lib$free_vm (0, &rqptr, &VmRequestVmZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "VmFreeRequest() lib$free_vm()", SourceModuleName, SourceLineNumber); } /*****************************************************************************/ /* Allocate dynamic memory to an individual thread's heap. Return a pointer to the start of new *usable* memory area if successful, explode if not. */ uchar* VmGetHeap ( REQUEST_STRUCT *rqptr, ulong ChunkSize ) { int status; ulong BaseAddress; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_VM)) WatchThis (WATCHITM(rqptr), WATCH_MOD_VM, "VmGetHeap() !UL", ChunkSize); ChunkSize += VmStructSize + VM_ELBOW_ROOM; status = lib$get_vm (&ChunkSize, &BaseAddress, &rqptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$get_vm()", FI_LI); ChunkSize -= VmStructSize + VM_ELBOW_ROOM; ((VM_STRUCT*)BaseAddress)->size = ChunkSize; ((VM_STRUCT*)BaseAddress)->magic = VM_MAGIC2; ((VM_STRUCT*)BaseAddress)->h2magic = VM_H2MAGIC; rqptr->rqHeapStats.GetCount++; rqptr->rqHeapStats.GetByteCount += ChunkSize; rqptr->rqHeapStats.ByteCount += ChunkSize; if (ChunkSize > rqptr->rqHeapStats.GetByteMax) rqptr->rqHeapStats.GetByteMax = ChunkSize; if (ChunkSize < rqptr->rqHeapStats.GetByteMin) rqptr->rqHeapStats.GetByteMin = ChunkSize; return ((uchar*)BaseAddress + VmStructSize); } /*****************************************************************************/ /* Expand (or even contract) an individual chunk. See VmGetHeap(). */ uchar* VmReallocHeap ( REQUEST_STRUCT *rqptr, uchar *ChunkPtr, ulong ChunkSize, char *SourceModuleName, int SourceLineNumber ) { int status, OriginalChunkSize; ulong BaseAddress, OriginalBaseAddress; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_VM)) { if (ChunkPtr) OriginalChunkSize = ((VM_STRUCT*)(ChunkPtr-VmStructSize))->size; else OriginalChunkSize = 0; WatchThis (WATCHITM(rqptr), WATCH_MOD_VM, "VmReallocHeap() !UL !UL", OriginalChunkSize, ChunkSize); } if (!ChunkPtr) return (VmGetHeap (rqptr, ChunkSize)); OriginalBaseAddress = ChunkPtr - VmStructSize; if (((VM_STRUCT*)OriginalBaseAddress)->magic != VM_MAGIC2) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, SourceModuleName, SourceLineNumber); /* if this chunk satisfies the reallocation then just return it */ OriginalChunkSize = ((VM_STRUCT*)OriginalBaseAddress)->size; if (OriginalChunkSize >= ChunkSize) return (ChunkPtr); /* allocate a new, larger chunk */ ChunkSize += VmStructSize + VM_ELBOW_ROOM; status = lib$get_vm (&ChunkSize, &BaseAddress, &rqptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "VmReallocHeap() lib$get_vm()", SourceModuleName, SourceLineNumber); ChunkSize -= VmStructSize + VM_ELBOW_ROOM; /* copy the existing chunk into the new chunk */ memcpy ((uchar*)BaseAddress, (uchar*)OriginalBaseAddress, OriginalChunkSize + VmStructSize + VM_ELBOW_ROOM); /* update the chunk size */ ((VM_STRUCT*)BaseAddress)->size = ChunkSize; /* free the previous chunk */ status = lib$free_vm (0, &OriginalBaseAddress, &rqptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "VmReallocHeap() lib$free_vm()", SourceModuleName, SourceLineNumber); /* update statistics */ rqptr->rqHeapStats.ReallocCount++; rqptr->rqHeapStats.ReallocByteCount += ChunkSize; if (ChunkSize > rqptr->rqHeapStats.ReallocByteMax) rqptr->rqHeapStats.ReallocByteMax = ChunkSize; if (ChunkSize < rqptr->rqHeapStats.ReallocByteMin) rqptr->rqHeapStats.ReallocByteMin = ChunkSize; rqptr->rqHeapStats.ByteCount -= OriginalChunkSize; rqptr->rqHeapStats.ByteCount += ChunkSize; return ((uchar*)BaseAddress+VmStructSize); } /*****************************************************************************/ /* Release back into the virtual memory zone one chunk of request heap memory. */ VmFreeFromHeap ( REQUEST_STRUCT *rqptr, uchar *ChunkPtr, char *SourceModuleName, int SourceLineNumber ) { int status, ChunkSize; ulong BaseAddress; /*********/ /* begin */ /*********/ BaseAddress = ChunkPtr - VmStructSize; ChunkSize = ((VM_STRUCT*)(BaseAddress))->size; if (WATCHMOD (rqptr, WATCH_MOD_VM)) WatchThis (WATCHITM(rqptr), WATCH_MOD_VM, "VmFreeFromHeap() !UL", ChunkSize); if (((VM_STRUCT*)BaseAddress)->magic != VM_MAGIC2) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, SourceModuleName, SourceLineNumber); status = lib$free_vm (0, &BaseAddress, &rqptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "VmFreeFromHeap() lib$free_vm()", SourceModuleName, SourceLineNumber); /* update statistics */ rqptr->rqHeapStats.FreeCount++; rqptr->rqHeapStats.FreeByteCount += ChunkSize; if (ChunkSize > rqptr->rqHeapStats.FreeByteMax) rqptr->rqHeapStats.FreeByteMax = ChunkSize; if (ChunkSize < rqptr->rqHeapStats.FreeByteMin) rqptr->rqHeapStats.FreeByteMin = ChunkSize; rqptr->rqHeapStats.ByteCount -= ChunkSize; } /*****************************************************************************/ /* Release back into the virtual memory zone the individually allocated chunks of memory (the zone is deleted on request structure release). */ VmFreeHeap ( REQUEST_STRUCT *rqptr, char *SourceModuleName, int SourceLineNumber ) { int status, ChunkSize; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_VM)) WatchThis (WATCHITM(rqptr), WATCH_MOD_VM, "VmFreeHeap()"); status = lib$reset_vm_zone (&rqptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "VmFreeHeap() lib$reset_vm_zone()", SourceModuleName, SourceLineNumber); } /*****************************************************************************/ /* Initialize the virtual memory zone for the HTTP/2 structures. */ VmHttp2Init () { static $DESCRIPTOR (ZoneNameDsc, "HTTPd HTTP/2"); static ulong Algorithm = LIB$K_VM_QUICK_FIT, AlgorithmArg = 32, Flags = LIB$M_VM_BOUNDARY_TAGS | LIB$M_VM_GET_FILL0 | LIB$M_VM_TAIL_LARGE, InitialSize, BlockSize = 64; int status; char *cptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (WATCHALL, WATCH_MOD_VM, "VmHttp2Init() !UL", NetConcurrentMax); cptr = getenv("WASD_VM_HTTP2_SIZE_PAGES"); if (cptr) VmHttp2SizePages = atol(cptr); else { InstanceMutexLock (INSTANCE_MUTEX_HTTPD); VmHttp2SizePages = HttpdGblSecPtr->VmHttp2SizePages; InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD); } if (VmHttp2SizePages < 24 || VmHttp2SizePages > 128) VmHttp2SizePages = VM_REQUEST_SIZE_PAGES; InitialSize = (NetConcurrentMax * RequestStructSize) / 512 + 1; /* create the cache virtual memory zone */ status = lib$create_vm_zone (&VmHttp2VmZoneId, &Algorithm, &AlgorithmArg, &Flags, 0, &InitialSize, &BlockSize, 0, 0, 0, &ZoneNameDsc, 0, 0); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$create_vm_zone()", FI_LI); VmHeapStatsGetCountMin = VmHeapStatsFreeCountMin = VmHeapStatsReallocCountMin = VmHeapStats.FreeByteMin = VmHeapStats.GetByteMin = VmHeapStats.ReallocByteMin = VM_STATS_MAXMIN; } /*****************************************************************************/ /* Called periodically by HttpTick() and also on an ad hoc basis when VmReport() is used. Check most commonly required request heap pages and store it away for the next startup. I know the way the pages are calculated before being stored into the array (VmHttp2Free()) is not 100% percent accurate with what may have actually been used by LIB$VM but as this statistic is not available from anywhere I know of and it's not worth fooling around EXEC code to get it this will be close enough for jazz. */ VmHttp2Tune () { int idx; ulong PageCount, Http2Count, Http2CountTotal; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (WATCHALL, WATCH_MOD_VM, "VmHttp2Tune()"); PageCount = Http2Count = Http2CountTotal = 0; for (idx = 0; idx < VM_REQUEST_SIZE_PAGES_MAX / VM_REQUEST_SIZE_PAGES_STAT_GRANULE; idx++) Http2CountTotal += VmHeapStatsPageCount[idx]; /* only take any notice of this if there's been at least 1,000 requests */ if (Http2CountTotal < 1000) return; for (idx = 0; idx < VM_REQUEST_SIZE_PAGES_MAX / VM_REQUEST_SIZE_PAGES_STAT_GRANULE; idx++) { if (!VmHeapStatsPageCount[idx]) continue; Http2Count += VmHeapStatsPageCount[idx]; /* if the percentage calculation would overflow forget it! */ if (Http2Count * 100 < Http2Count) return; /* if this still accounts for less than eighty percent of requests */ if (Http2Count * 100 / Http2CountTotal < 80) continue; break; } PageCount = (idx + 1) * VM_REQUEST_SIZE_PAGES_STAT_GRANULE; if (WATCH_MODULE(WATCH_MOD_VM)) WatchDataFormatted ("!UL !UL !UL% !UL\n", Http2CountTotal, Http2Count, PercentOf(Http2Count,Http2CountTotal), PageCount); if (PageCount) { /* change the value dynamically */ VmHttp2SizePages = PageCount; /* place the value in the global section for next startup */ InstanceMutexLock (INSTANCE_MUTEX_HTTPD); HttpdGblSecPtr->VmHttp2SizePages = PageCount; InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD); } } /*****************************************************************************/ /* Allocate an HTTP/2 structure with associated virtual memory zone ready for HTTP/2 heap allocation. */ HTTP2_STRUCT* VmHttp2Get () { static $DESCRIPTOR (ZoneNameDsc, "HTTP2 Heap"); static ulong Algorithm = LIB$K_VM_QUICK_FIT, AlgorithmArg = 16, Flags = LIB$M_VM_BOUNDARY_TAGS | LIB$M_VM_GET_FILL0 | LIB$M_VM_TAIL_LARGE, BlockSize = 64; int status; ulong BaseAddress; HTTP2_STRUCT *h2ptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (WATCHALL, WATCH_MOD_VM, "VmHttp2Get()"); status = lib$get_vm (&Http2StructSize, &BaseAddress, &VmHttp2VmZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$get_vm()", FI_LI); h2ptr = (HTTP2_STRUCT*)BaseAddress; /* now create a virtual memory zone for the request's heap */ h2ptr->VmHeapZoneId = 0; status = lib$create_vm_zone (&h2ptr->VmHeapZoneId, &Algorithm, &AlgorithmArg, &Flags, &VmHttp2SizePages, &VmHttp2SizePages, &BlockSize, 0, 0, 0, &ZoneNameDsc, 0, 0); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$create_vm_zone()", FI_LI); h2ptr->Heap2Stats.FreeByteMin = h2ptr->Heap2Stats.GetByteMin = h2ptr->Heap2Stats.ReallocByteMin = VM_STATS_MAXMIN; return (h2ptr); } /*****************************************************************************/ /* Delete any virtual memory zone created for the HTTP/2 heap, then return the HTTP/2 structure to the request virtual memory pool. */ VmHttp2Free ( HTTP2_STRUCT *h2ptr, char *SourceModuleName, int SourceLineNumber ) { static ulong PrevByteCount; int status; ulong PageCount; /*********/ /* begin */ /*********/ if (WATCHMOD (h2ptr, WATCH_MOD_VM)) { PageCount = h2ptr->Heap2Stats.ByteCount >> 9; if (h2ptr->Heap2Stats.ByteCount & 0x1ff) PageCount++; WatchThis (WATCHITM(h2ptr), WATCH_MOD_VM, "VmHttp2Free()"); WatchDataFormatted ( "ByteCount: !SL PageCount: !SL VmHttp2SizePages: !SL\n\ GetCount: !SL GetByteCount: !SL GetByteMax: !SL GetByteMin: !SL\n\ FreeCount: !SL FreeByteCount: !SL FreeByteMax: !SL FreeByteMin: !SL\n\ ReallocCount: !SL ReallocByteCount: !SL \ ReallocByteMax: !SL ReallocByteMin: !SL\n", h2ptr->Heap2Stats.ByteCount, PageCount, VmHttp2SizePages, h2ptr->Heap2Stats.GetCount, h2ptr->Heap2Stats.GetByteCount, h2ptr->Heap2Stats.GetByteMax, h2ptr->Heap2Stats.GetByteMin, h2ptr->Heap2Stats.FreeCount, h2ptr->Heap2Stats.FreeByteCount, h2ptr->Heap2Stats.FreeByteMax, h2ptr->Heap2Stats.FreeByteMin, h2ptr->Heap2Stats.ReallocCount, h2ptr->Heap2Stats.ReallocByteCount, h2ptr->Heap2Stats.ReallocByteMax, h2ptr->Heap2Stats.ReallocByteMin); } VmHeap2Stats.ByteCount += h2ptr->Heap2Stats.ByteCount; if (VmHeap2Stats.ByteCount < PrevByteCount) { /* no nicker knotting here, just detect and reset */ memset (&VmHeap2Stats, 0, sizeof(VmHeap2Stats)); memset (&VmHeap2StatsPageCount, 0, sizeof(VmHeap2StatsPageCount)); VmHeap2StatsGetCountMax = VmHeap2StatsFreeCountMax = VmHeap2StatsReallocCountMax = VmHeap2StatsCount = 0; VmHeap2StatsGetCountMin = VmHeap2StatsFreeCountMin = VmHeap2StatsReallocCountMin = VmHeap2Stats.FreeByteMin = VmHeap2Stats.GetByteMin = VmHeap2Stats.ReallocByteMin = VM_STATS_MAXMIN; VmHeap2StatsResetCount++; VmHeap2Stats.ByteCount = h2ptr->Heap2Stats.ByteCount; } PrevByteCount = VmHeap2Stats.ByteCount; VmHeap2StatsCount++; PageCount = h2ptr->Heap2Stats.ByteCount >> 9; if (h2ptr->Heap2Stats.ByteCount & 0x1ff) PageCount++; if (PageCount >= VM_REQUEST_SIZE_PAGES_STAT_MAX) VmHeap2StatsPageCount[VM_REQUEST_SIZE_PAGES_STAT_MAX/ VM_REQUEST_SIZE_PAGES_STAT_GRANULE]++; else VmHeap2StatsPageCount[PageCount/VM_REQUEST_SIZE_PAGES_STAT_GRANULE]++; if (h2ptr->Heap2Stats.GetCount) { VmHeap2Stats.GetCount += h2ptr->Heap2Stats.GetCount; if (h2ptr->Heap2Stats.GetCount > VmHeap2StatsGetCountMax) VmHeap2StatsGetCountMax = h2ptr->Heap2Stats.GetCount; if (h2ptr->Heap2Stats.GetCount < VmHeap2StatsGetCountMin) VmHeap2StatsGetCountMin = h2ptr->Heap2Stats.GetCount; if (h2ptr->Heap2Stats.GetByteMax > VmHeap2Stats.GetByteMax) VmHeap2Stats.GetByteMax = h2ptr->Heap2Stats.GetByteMax; if (h2ptr->Heap2Stats.GetByteMin != VM_STATS_MAXMIN && h2ptr->Heap2Stats.GetByteMin < VmHeap2Stats.GetByteMin) VmHeap2Stats.GetByteMin = h2ptr->Heap2Stats.GetByteMin; } if (h2ptr->Heap2Stats.FreeCount) { VmHeap2Stats.FreeCount += h2ptr->Heap2Stats.FreeCount; if (h2ptr->Heap2Stats.FreeCount > VmHeap2StatsFreeCountMax) VmHeap2StatsFreeCountMax = h2ptr->Heap2Stats.FreeCount; if (h2ptr->Heap2Stats.FreeCount < VmHeap2StatsFreeCountMin) VmHeap2StatsFreeCountMin = h2ptr->Heap2Stats.FreeCount; if (h2ptr->Heap2Stats.FreeByteMax > VmHeap2Stats.FreeByteMax) VmHeap2Stats.FreeByteMax = h2ptr->Heap2Stats.FreeByteMax; if (h2ptr->Heap2Stats.FreeByteMin != VM_STATS_MAXMIN && h2ptr->Heap2Stats.FreeByteMin < VmHeap2Stats.FreeByteMin) VmHeap2Stats.FreeByteMin = h2ptr->Heap2Stats.FreeByteMin; } if (h2ptr->Heap2Stats.ReallocCount) { VmHeap2Stats.ReallocCount += h2ptr->Heap2Stats.ReallocCount; if (h2ptr->Heap2Stats.ReallocCount > VmHeap2StatsReallocCountMax) VmHeap2StatsReallocCountMax = h2ptr->Heap2Stats.ReallocCount; if (h2ptr->Heap2Stats.ReallocCount < VmHeap2StatsReallocCountMin) VmHeap2StatsReallocCountMin = h2ptr->Heap2Stats.ReallocCount; if (h2ptr->Heap2Stats.ReallocByteMax > VmHeap2Stats.ReallocByteMax) VmHeap2Stats.ReallocByteMax = h2ptr->Heap2Stats.ReallocByteMax; if (h2ptr->Heap2Stats.ReallocByteMin != VM_STATS_MAXMIN && h2ptr->Heap2Stats.ReallocByteMin < VmHeap2Stats.ReallocByteMin) VmHeap2Stats.ReallocByteMin = h2ptr->Heap2Stats.ReallocByteMin; } /* free the request structure's virtual memory */ if (h2ptr->VmHeapZoneId) { /* delete the request's heap's virtual memory zone */ status = lib$delete_vm_zone (&h2ptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$delete_vm_zone()", FI_LI); } status = lib$free_vm (0, &h2ptr, &VmHttp2VmZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "VmHttp2Free() lib$free_vm()", SourceModuleName, SourceLineNumber); } /*****************************************************************************/ /* Allocate dynamic memory to an individual HTTP/2 heap. Return a pointer to the start of new *usable* memory area if successful, explode if not. */ uchar* VmGet2Heap ( HTTP2_STRUCT *h2ptr, ulong ChunkSize ) { int status; ulong BaseAddress; /*********/ /* begin */ /*********/ if (WATCHMOD (h2ptr, WATCH_MOD_VM)) WatchThis (WATCHITM(h2ptr), WATCH_MOD_VM, "VmGet2Heap() !UL", ChunkSize); ChunkSize += VmStructSize + VM_ELBOW_ROOM; status = lib$get_vm (&ChunkSize, &BaseAddress, &h2ptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$get_vm()", FI_LI); ChunkSize -= VmStructSize + VM_ELBOW_ROOM; ((VM_STRUCT*)BaseAddress)->size = ChunkSize; ((VM_STRUCT*)BaseAddress)->magic = VM_MAGIC3; ((VM_STRUCT*)BaseAddress)->h2magic = VM_H2MAGIC; /* update statistics */ h2ptr->Heap2Stats.GetCount++; h2ptr->Heap2Stats.GetByteCount += ChunkSize; h2ptr->Heap2Stats.ByteCount += ChunkSize; if (ChunkSize > h2ptr->Heap2Stats.GetByteMax) h2ptr->Heap2Stats.GetByteMax = ChunkSize; if (ChunkSize < h2ptr->Heap2Stats.GetByteMin) h2ptr->Heap2Stats.GetByteMin = ChunkSize; return ((uchar*)BaseAddress+VmStructSize); } /*****************************************************************************/ /* Expand (or even contract) an individual HTTP/2 chunk. See VmGet2Heap(). */ uchar* VmRealloc2Heap ( HTTP2_STRUCT *h2ptr, uchar *ChunkPtr, ulong ChunkSize, char *SourceModuleName, int SourceLineNumber ) { int status, OriginalChunkSize; ulong BaseAddress, OriginalBaseAddress; /*********/ /* begin */ /*********/ if (WATCHMOD (h2ptr, WATCH_MOD_VM)) { if (ChunkPtr) OriginalChunkSize = ((VM_STRUCT*)(ChunkPtr-VmStructSize))->size; else OriginalChunkSize = 0; WatchThis (WATCHITM(h2ptr), WATCH_MOD_VM, "VmRealloc2Heap() !UL !UL", OriginalChunkSize, ChunkSize); } if (!ChunkPtr) return (VmGet2Heap (h2ptr, ChunkSize)); OriginalBaseAddress = ChunkPtr - VmStructSize; if (((VM_STRUCT*)OriginalBaseAddress)->magic != VM_MAGIC3) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, SourceModuleName, SourceLineNumber); /* if this chunk satisfies the reallocation then just return it */ OriginalChunkSize = ((VM_STRUCT*)OriginalBaseAddress)->size; if (OriginalChunkSize >= ChunkSize) return (ChunkPtr); /* allocate a new, larger chunk */ ChunkSize += VmStructSize + VM_ELBOW_ROOM; status = lib$get_vm (&ChunkSize, &BaseAddress, &h2ptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "VmRealloc2Heap() lib$get_vm()", SourceModuleName, SourceLineNumber); ChunkSize -= VmStructSize + VM_ELBOW_ROOM; /* copy the existing chunk into the new chunk */ memcpy ((uchar*)BaseAddress, (uchar*)OriginalBaseAddress, OriginalChunkSize + VmStructSize + VM_ELBOW_ROOM); /* update the chunk size */ ((VM_STRUCT*)BaseAddress)->size = ChunkSize; /* free the previous chunk */ status = lib$free_vm (0, &OriginalBaseAddress, &h2ptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "VmRealloc2Heap() lib$free_vm()", SourceModuleName, SourceLineNumber); /* update statistics */ h2ptr->Heap2Stats.ReallocCount++; h2ptr->Heap2Stats.ReallocByteCount += ChunkSize; if (ChunkSize > h2ptr->Heap2Stats.ReallocByteMax) h2ptr->Heap2Stats.ReallocByteMax = ChunkSize; if (ChunkSize < h2ptr->Heap2Stats.ReallocByteMin) h2ptr->Heap2Stats.ReallocByteMin = ChunkSize; h2ptr->Heap2Stats.ByteCount -= OriginalChunkSize; h2ptr->Heap2Stats.ByteCount += ChunkSize; return ((uchar*)BaseAddress+VmStructSize); } /*****************************************************************************/ /* Release back into the virtual memory zone one chunk of HTTP/2 heap memory. */ VmFreeFrom2Heap ( HTTP2_STRUCT *h2ptr, uchar *ChunkPtr, char *SourceModuleName, int SourceLineNumber ) { int status, ChunkSize; ulong BaseAddress; /*********/ /* begin */ /*********/ BaseAddress = ChunkPtr - VmStructSize; ChunkSize = ((VM_STRUCT*)(BaseAddress))->size; if (WATCHMOD (h2ptr, WATCH_MOD_VM)) WatchThis (WATCHITM(h2ptr), WATCH_MOD_VM, "VmFreeFrom2Heap() !UL", ChunkSize); if (((VM_STRUCT*)BaseAddress)->magic != VM_MAGIC3) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, SourceModuleName, SourceLineNumber); status = lib$free_vm (0, &BaseAddress, &h2ptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "VmFreeFrom2Heap() lib$free_vm()", SourceModuleName, SourceLineNumber); /* update statistics */ h2ptr->Heap2Stats.FreeCount++; h2ptr->Heap2Stats.FreeByteCount += ChunkSize; if (ChunkSize > h2ptr->Heap2Stats.FreeByteMax) h2ptr->Heap2Stats.FreeByteMax = ChunkSize; if (ChunkSize < h2ptr->Heap2Stats.FreeByteMin) h2ptr->Heap2Stats.FreeByteMin = ChunkSize; h2ptr->Heap2Stats.ByteCount -= ChunkSize; } /*****************************************************************************/ /* Release back into the HTTP/2 virtual memory zone the individually allocated chunks of memory (the zone is deleted on request structure release). */ VmFree2Heap ( HTTP2_STRUCT *h2ptr, char *SourceModuleName, int SourceLineNumber ) { int status; /*********/ /* begin */ /*********/ if (WATCHMOD (h2ptr, WATCH_MOD_VM)) WatchThis (WATCHITM(h2ptr), WATCH_MOD_VM, "VmFree2Heap()"); status = lib$reset_vm_zone (&h2ptr->VmHeapZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "VmFree2Heap() lib$reset_vm_zone()", SourceModuleName, SourceLineNumber); } /*****************************************************************************/ /* Initialize the virtual memory zone cache memory will be allocated from. */ VmCacheInit (int TotalKBytesMax) { static $DESCRIPTOR (ZoneNameDsc, "HTTPd Cache"); static ulong Algorithm = LIB$K_VM_QUICK_FIT, AlgorithmArg = 128, Flags = LIB$M_VM_BOUNDARY_TAGS | LIB$M_VM_GET_FILL0 | LIB$M_VM_TAIL_LARGE, ExtendSize, InitialSize, BlockSize = CACHE_CHUNK_SIZE; int status; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (WATCHALL, WATCH_MOD_VM, "VmCacheInit() !UL", TotalKBytesMax); if ((InitialSize = TotalKBytesMax * 2) <= 0) InitialSize = 32; ExtendSize = InitialSize / 2; /* create the cache virtual memory zone */ status = lib$create_vm_zone (&VmCacheVmZoneId, &Algorithm, &AlgorithmArg, &Flags, &ExtendSize, &InitialSize, &BlockSize, 0, 0, 0, &ZoneNameDsc, 0, 0); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$create_vm_zone()", FI_LI); } /*****************************************************************************/ /* */ uchar* VmGetCache (ulong ChunkSize) { int status; ulong BaseAddress; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (WATCHALL, WATCH_MOD_VM, "VmGetCache() !UL", ChunkSize); status = lib$get_vm (&ChunkSize, &BaseAddress, &VmCacheVmZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$get_vm()", FI_LI); return ((uchar*)BaseAddress); } /*****************************************************************************/ /* Release back into the virtual memory zone the individually allocated chunks of cache memory. */ VmFreeCache ( uchar *ChunkPtr, char *SourceModuleName, int SourceLineNumber ) { int status, ChunkSize; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) { if (ChunkPtr) ChunkSize = ((VM_STRUCT*)(ChunkPtr-VmStructSize))->size; else ChunkSize = 0; WatchThis (WATCHALL, WATCH_MOD_VM, "VmFreeCache() !UL", ChunkSize); } status = lib$free_vm (0, &ChunkPtr, &VmCacheVmZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "VmFreeCache() lib$free_vm()", SourceModuleName, SourceLineNumber); } /*****************************************************************************/ /* Initialize the virtual memory zone permanent cache memory will be allocated from. */ VmPermCacheInit (int TotalKBytesMax) { static $DESCRIPTOR (ZoneNameDsc, "HTTPd Perm-Cache"); static ulong Algorithm = LIB$K_VM_QUICK_FIT, AlgorithmArg = 128, Flags = LIB$M_VM_BOUNDARY_TAGS | LIB$M_VM_GET_FILL0 | LIB$M_VM_TAIL_LARGE, ExtendSize, InitialSize, BlockSize = CACHE_PERM_CHUNK_SIZE; int status; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (WATCHALL, WATCH_MOD_VM, "VmPermCacheInit() !UL", TotalKBytesMax); if ((InitialSize = TotalKBytesMax * 2) <= 0) InitialSize = 32; ExtendSize = InitialSize / 2; /* create the cache virtual memory zone */ status = lib$create_vm_zone (&VmPermCacheVmZoneId, &Algorithm, &AlgorithmArg, &Flags, &ExtendSize, &InitialSize, &BlockSize, 0, 0, 0, &ZoneNameDsc, 0, 0); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$create_vm_zone()", FI_LI); } /*****************************************************************************/ /* */ uchar* VmGetPermCache (ulong ChunkSize) { int status; ulong BaseAddress; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (WATCHALL, WATCH_MOD_VM, "VmGetPermCache() !UL", ChunkSize); if (!VmPermCacheVmZoneId) VmPermCacheInit (CacheTotalKBytesMax); status = lib$get_vm (&ChunkSize, &BaseAddress, &VmPermCacheVmZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$get_vm()", FI_LI); return ((uchar*)BaseAddress); } /*****************************************************************************/ /* Release back into the virtual memory zone the individually allocated chunks of permanent cache memory. */ VmFreePermCache ( uchar *ChunkPtr, char *SourceModuleName, int SourceLineNumber ) { int status, ChunkSize; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) { if (ChunkPtr) ChunkSize = ((VM_STRUCT*)(ChunkPtr-VmStructSize))->size; else ChunkSize = 0; WatchThis (WATCHALL, WATCH_MOD_VM, "VmFreePermCache() !UL", ChunkSize); } status = lib$free_vm (0, &ChunkPtr, &VmPermCacheVmZoneId); if (VMSnok (status)) ErrorExitVmsStatus (status, "VmFreePermCache() lib$free_vm()", SourceModuleName, SourceLineNumber); } /*****************************************************************************/ /* Return a report on processes' virtual memory usage. This function blocks while executing. */ VmReport ( REQUEST_STRUCT *rqptr, REQUEST_AST NextTaskFunction ) { static ulong ShowVmCode123 = 0, ShowVmCode567 = 4, ShowVmZoneDetail = 3; static char BeginPageFao [] = "

\n\ \n\
\n\ \n\ \n\
\n\
REQUEST HEAP STATISTICS for !UL requests (reset !UL time!%s)\n\n";

   static char  MoreStatsFao [] =
" !UL pages initial allocation (!UL default)\n\
 !UL request!%s extended from initial allocation (!UL%)\n\
 !UL pages set via dynamic tuning\n\
\n\
 !UL bytes average (!UL pages)\n\
 !UL call!%s to GET heap memory\n\
 !UL per request ave, !UL max, !UL min, !UL bytes max, !UL bytes min\n\
 !UL call!%s to FREE heap memory\n\
 !UL per request ave, !UL max, !UL min, !UL bytes max, !UL bytes min\n\
 !UL call!%s to REALLOC heap memory\n\
 !UL per request ave, !UL max, !UL min, !UL bytes max, !UL bytes min\n\
\n!90*-\n\n";

   static char  EndPageFao [] =
"
\n\
\n\ \n\ \n"; int cnt, idx, status, ByteCountAve, Count, Percent; ushort Length; ulong Context, ExtendCount, PageCountAve, PageCountTuned, ZoneId; ulong *vecptr; ulong FaoVector [32]; char Buffer [4096]; $DESCRIPTOR (BufferDsc, Buffer); /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (WATCHALL, WATCH_MOD_VM, "VmReport()"); VmRequestTune (); InstanceMutexLock (INSTANCE_MUTEX_HTTPD); PageCountTuned = HttpdGblSecPtr->VmRequestSizePages; InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD); AdminPageTitle (rqptr, "Virtual Memory Report"); ByteCountAve = VmHeapStats.ByteCount / (VmHeapStatsCount ? VmHeapStatsCount : 1); PageCountAve = ByteCountAve >> 9; if (ByteCountAve & 0x1ff) PageCountAve++; status = FaoToNet (rqptr, BeginPageFao, VmHeapStatsCount, VmHeapStatsResetCount); ExtendCount = 0; for (idx = 0; idx < VM_REQUEST_SIZE_PAGES_MAX / VM_REQUEST_SIZE_PAGES_STAT_GRANULE; idx++) { if (!VmHeapStatsPageCount[idx]) continue; Percent = PercentOf (VmHeapStatsPageCount[idx], VmHeapStatsCount); if (idx*VM_REQUEST_SIZE_PAGES_STAT_GRANULE >= VmRequestSizePages) ExtendCount += VmHeapStatsPageCount[idx]; if (idx*VM_REQUEST_SIZE_PAGES_STAT_GRANULE < VM_REQUEST_SIZE_PAGES_STAT_MAX) status = FaoToNet (rqptr, " !UL-!UL pages !UL request!%s (!UL%)\n", idx*VM_REQUEST_SIZE_PAGES_STAT_GRANULE, ((idx+1)*VM_REQUEST_SIZE_PAGES_STAT_GRANULE)-1, VmHeapStatsPageCount[idx], Percent); else status = FaoToNet (rqptr, " >=!UL pages !UL request!%s (!UL%)\n", idx*VM_REQUEST_SIZE_PAGES_STAT_GRANULE, VmHeapStatsPageCount[idx], Percent); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } Percent = PercentOf (ExtendCount, VmHeapStatsCount); status = FaoToNet (rqptr, MoreStatsFao, VmRequestSizePages, VM_REQUEST_SIZE_PAGES, ExtendCount, Percent, PageCountTuned, ByteCountAve, PageCountAve, VmHeapStats.GetCount, VmHeapStats.GetCount / (VmHeapStatsCount ? VmHeapStatsCount : 1), VmHeapStatsGetCountMax, VmHeapStatsGetCountMin != VM_STATS_MAXMIN ? VmHeapStatsGetCountMin : 0, VmHeapStats.GetByteMax, VmHeapStats.GetByteMin != VM_STATS_MAXMIN ? VmHeapStats.GetByteMin : 0, VmHeapStats.FreeCount, VmHeapStats.FreeCount / (VmHeapStatsCount ? VmHeapStatsCount : 1), VmHeapStatsFreeCountMax, VmHeapStatsFreeCountMin != VM_STATS_MAXMIN ? VmHeapStatsFreeCountMin : 0, VmHeapStats.FreeByteMax, VmHeapStats.FreeByteMin != VM_STATS_MAXMIN ? VmHeapStats.FreeByteMin : 0, VmHeapStats.ReallocCount, VmHeapStats.ReallocCount / (VmHeapStatsCount ? VmHeapStatsCount : 1), VmHeapStatsReallocCountMax, VmHeapStatsReallocCountMin != VM_STATS_MAXMIN ? VmHeapStatsReallocCountMin : 0, VmHeapStats.ReallocByteMax, VmHeapStats.ReallocByteMin != VM_STATS_MAXMIN ? VmHeapStats.ReallocByteMin : 0); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); if (VMSnok (status = lib$show_vm (&ShowVmCode123, &VmWrite, rqptr))) { rqptr->rqResponse.ErrorTextPtr = "lib$show_vm()"; ErrorVmsStatus (rqptr, status, FI_LI); SysDclAst (NextTaskFunction, rqptr); return; } if (VMSnok (status = lib$show_vm (&ShowVmCode567, &VmWrite, rqptr))) { rqptr->rqResponse.ErrorTextPtr = "lib$show_vm()"; ErrorVmsStatus (rqptr, status, FI_LI); SysDclAst (NextTaskFunction, rqptr); return; } status = FaoToNet (rqptr, "\n"); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); Context = 0; while ((status = lib$find_vm_zone (&Context, &ZoneId)) == SS$_NORMAL) { status = FaoToNet (rqptr, "!90*-\n\n"); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); status = lib$show_vm_zone (&ZoneId, &ShowVmZoneDetail, &VmWrite, rqptr); if (VMSnok (status)) { rqptr->rqResponse.ErrorTextPtr = "lib$show_vm_zone()"; ErrorVmsStatus (rqptr, status, FI_LI); SysDclAst (NextTaskFunction, rqptr); return; } } if (status != LIB$_NOTFOU) { rqptr->rqResponse.ErrorTextPtr = "lib$find_vm_zone()"; ErrorVmsStatus (rqptr, status, FI_LI); SysDclAst (NextTaskFunction, rqptr); return; } status = FaolToNet (rqptr, EndPageFao, NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); rqptr->rqResponse.PreExpired = PRE_EXPIRE_ADMIN; ResponseHeader200 (rqptr, "text/html", &rqptr->NetWriteBufferDsc); SysDclAst (NextTaskFunction, rqptr); } /*****************************************************************************/ /* Action routine for lib$show_vm*() routines. Simply write the contents of the paremeter descriptor to the client plus a newline character. */ int VmWrite ( struct dsc$descriptor *DscPtr, REQUEST_STRUCT *rqptr ) { int status; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (WATCHALL, WATCH_MOD_VM, "VmWrite()"); status = FaoToNet (rqptr, "!#&;AZ\n", DscPtr->dsc$w_length, DscPtr->dsc$a_pointer); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); return (SS$_NORMAL); } /*****************************************************************************/ /* */ VmDebug (ulong ZoneId) { #ifdef DBUG static ulong ShowVmZoneDetail = 3; int status; ulong Context; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_VM)) WatchThis (WATCHALL, WATCH_MOD_VM, "VmDebug() !UL !60*-", ZoneId); if (ZoneId) { status = lib$show_vm_zone (&ZoneId, &ShowVmZoneDetail, 0, 0); if (VMSnok (status)) exit (status); } else { Context = 0; while ((status = lib$find_vm_zone (&Context, &ZoneId)) == SS$_NORMAL) { status = lib$show_vm_zone (&ZoneId, &ShowVmZoneDetail, 0, 0); if (VMSnok (status)) exit (status); } } if (WATCH_MODULE(WATCH_MOD_VM)) WatchDataFormatted ("----------\n"); #endif } /*****************************************************************************/