From 5fc7874238a2aebe6a476d628868cc03e8edb48e Mon Sep 17 00:00:00 2001 From: Irek Fakhrutdinov Date: Tue, 7 May 2024 13:44:51 +0200 Subject: [PATCH] Module registry initial implementation To address potential LPA shortages when multiple ZIS instances are running on the same LPAR, a new feature--the module registry--has been introduced. The module registry is a set of data structures in common storage in which eligible modules are traced. An eligible module is a module marked with a special macro; the mark and the module names are then used to uniquely identify the module. Once ZIS and its plug-in modules are marked, they can be added to the registry and shared: if another ZIS instance needs to load an identical module to the LPA, it will reuse a previously registered (and loaded to the LPA) module instead. Sharing identical modules should dramatically decrease the LPA footprint. This feature must not be used when an application wants to remove its modules from the LPA (e.g., ZIS does that in dev mode) because it will affect any other applications using the same modules. Fixes: #405 Signed-off-by: Irek Fakhrutdinov --- CHANGELOG.md | 1 + c/crossmemory.c | 56 +++- c/modreg.c | 584 ++++++++++++++++++++++++++++++++++++++++++ h/crossmemory.h | 17 +- h/logging.h | 1 + h/modreg.h | 101 ++++++++ h/zvt.h | 3 +- tests/build_modreg.sh | 54 ++++ tests/modregtest.c | 72 ++++++ 9 files changed, 878 insertions(+), 11 deletions(-) create mode 100644 c/modreg.c create mode 100644 h/modreg.h create mode 100755 tests/build_modreg.sh create mode 100644 tests/modregtest.c diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f102529e..2e0e8d17c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## `3.1.0` - Bugfix: removed "ByteOutputStream" debug message, which was part of the `zwe` command output (#491) - Bugfix: HEAPPOOLS and HEAPPOOLS64 no longer need to be set to OFF for configmgr (#497) +- Enhancement: module registry (#405) ## `3.0.0` - Feature: added javascript `zos.getStatvfs(path)` function to obtain file system information (#482). diff --git a/c/crossmemory.c b/c/crossmemory.c index 5ddc6199e..6c529658a 100644 --- a/c/crossmemory.c +++ b/c/crossmemory.c @@ -42,6 +42,7 @@ #include "stcbase.h" #include "utils.h" #include "zvt.h" +#include "modreg.h" #define CMS_STATIC_ASSERT($expr) typedef char p[($expr) ? 1 : -1] @@ -367,6 +368,8 @@ void cmsInitializeLogging() { logConfigureComponent(NULL, LOG_COMP_STCBASE, "STCBASE", LOG_DEST_PRINTF_STDOUT, ZOWE_LOG_INFO); logConfigureComponent(NULL, LOG_COMP_ID_CMS, "CIDB", LOG_DEST_PRINTF_STDOUT, ZOWE_LOG_INFO); logConfigureComponent(NULL, LOG_COMP_ID_CMSPC, "CIDB CM", LOG_DEST_PRINTF_STDOUT, ZOWE_LOG_INFO); + logConfigureComponent(NULL, LOG_COMP_LPA, "LPA", LOG_DEST_PRINTF_STDOUT, ZOWE_LOG_INFO); + logConfigureComponent(NULL, LOG_COMP_MODREG, "MODREG", LOG_DEST_PRINTF_STDOUT, ZOWE_LOG_INFO); printf("DATESTAMP JOBNAME ASCB (ASID) TCB MSGID MSGTEXT\n"); } @@ -2442,6 +2445,8 @@ CrossMemoryServer *makeCrossMemoryServer2( logSetLevel(NULL, LOG_COMP_ID_CMS, ZOWE_LOG_DEBUG); logSetLevel(NULL, LOG_COMP_ID_CMSPC, ZOWE_LOG_DEBUG); logSetLevel(NULL, LOG_COMP_STCBASE, ZOWE_LOG_DEBUG); + logSetLevel(NULL, LOG_COMP_LPA, ZOWE_LOG_DEBUG); + logSetLevel(NULL, LOG_COMP_MODREG, ZOWE_LOG_DEBUG); } EightCharString cmsModule = {0}; @@ -2483,6 +2488,10 @@ CrossMemoryServer *makeCrossMemoryServer2( server->flags |= CROSS_MEMORY_SERVER_FLAG_RESET_LOOKUP; } + if (flags & CMS_SERVER_FLAG_USE_MODREG) { + server->flags |= CROSS_MEMORY_SERVER_FLAG_USE_MODREG; + } + int allocResourcesRC = allocServerResources(server); if (allocResourcesRC != RC_CMS_OK) { safeFree31((char *)server, sizeof(CrossMemoryServer)); @@ -3388,17 +3397,41 @@ static int allocateGlobalResources(CrossMemoryServer *server) { globalArea->serverFlags |= server->flags; globalArea->serverASID = getMyPASID(); - /* Load the module to LPA if needed, otherwise re-use the existing module. */ + const char *statusText = "n/a"; + /* Register the module or load it to LPA if needed, otherwise re-use the + * existing module. */ if (moduleAddressLPA == NULL) { - - int lpaAddRSN = 0; - int lpaAddRC = lpaAdd(&server->lpaCodeInfo, &server->ddname, &server->dsname, - &lpaAddRSN); - if (lpaAddRC != 0) { - zowelog(NULL, LOG_COMP_ID_CMS, ZOWE_LOG_SEVERE, CMS_LOG_LPA_LOAD_FAILURE_MSG, lpaAddRC, lpaAddRSN); - return RC_CMS_LPA_ADD_FAILED; + /* Load to the LPA manually only if the "clean LPA" (dev mode) is enabled + * or the "use module registry" option is disabled. */ + if (server->flags & CROSS_MEMORY_SERVER_FLAG_CLEAN_LPA || + !(server->flags & CROSS_MEMORY_SERVER_FLAG_USE_MODREG)) { + int lpaAddRSN = 0; + int lpaAddRC = lpaAdd(&server->lpaCodeInfo, &server->ddname, + &server->dsname, &lpaAddRSN); + if (lpaAddRC == 0) { + statusText = "own instance loaded to LPA"; + } else { + zowelog(NULL, LOG_COMP_ID_CMS, ZOWE_LOG_SEVERE, + CMS_LOG_LPA_LOAD_FAILURE_MSG, lpaAddRC, lpaAddRSN); + return RC_CMS_LPA_ADD_FAILED; + } + } else { + uint64_t modregRSN; + int modregRC = modregRegister(server->ddname, server->dsname, + &server->lpaCodeInfo, &modregRSN); + if (modregRC == RC_MODREG_OK) { + statusText = "new instance added to registry"; + } else if (modregRC == RC_MODREG_ALREADY_REGISTERED) { + statusText = "instance reused from registry"; + } else { + zowelog(NULL, LOG_COMP_ID_CMS, ZOWE_LOG_SEVERE, + CMS_LOG_MODREG_ADD_FAILURE_MSG, modregRC, modregRSN); + return RC_CMS_MODREG_FAILED; + } } globalArea->lpaModuleInfo = server->lpaCodeInfo; + moduleAddressLPA = + server->lpaCodeInfo.outputInfo.stuff.successInfo.loadPointAddr; zowelog(NULL, LOG_COMP_ID_CMS, ZOWE_LOG_DEBUG, CMS_LOG_DEBUG_MSG_ID " module successfully loaded into LPA @ 0x%p, LPMEA:\n", @@ -3408,6 +3441,7 @@ static int allocateGlobalResources(CrossMemoryServer *server) { } else { + statusText = "previously added/loaded instance reused"; server->lpaCodeInfo = globalArea->lpaModuleInfo; zowelog(NULL, LOG_COMP_ID_CMS, ZOWE_LOG_DEBUG, CMS_LOG_DEBUG_MSG_ID @@ -3417,9 +3451,11 @@ static int allocateGlobalResources(CrossMemoryServer *server) { } + zowelog(NULL, LOG_COMP_ID_CMS, ZOWE_LOG_INFO, CMS_LOG_MODULE_STATUS_MSG, + statusText, moduleAddressLPA); + /* The required module is in LPA, update the corresponding fields. */ globalArea->lpaModuleTimestamp = getServerBuildTimestamp(); - moduleAddressLPA = server->lpaCodeInfo.outputInfo.stuff.successInfo.loadPointAddr; server->moduleAddressLPA = moduleAddressLPA; /* Prepare the service table */ @@ -4925,6 +4961,8 @@ static int testEnvironment(const CrossMemoryServer *srv) { int cmsStartMainLoop(CrossMemoryServer *srv) { + MODREG_MARK_MODULE(); + int envStatus = testEnvironment(srv); if (envStatus != 0) { return envStatus; diff --git a/c/modreg.c b/c/modreg.c new file mode 100644 index 000000000..85153a627 --- /dev/null +++ b/c/modreg.c @@ -0,0 +1,584 @@ + +/* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ + +#include +#include +#include +#include +#include + +#include "zowetypes.h" +#include "lpa.h" +#include "isgenq.h" +#include "qsam.h" +#include "zos.h" +#include "zvt.h" + +#include + +#include "modreg.h" + +#include "logging.h" + +#ifndef _LP64 +#error AMODE 31 is not supported +#endif + +#ifndef METTLE +/* TODO LE support after c/lpa.c gets it */ +#error LE is not supported +#endif + +#define PACK_RC_RSN(rc32, rsn32) ((uint64_t) (rc32) << 32 | (rsn32)) + +#define LOG_DEBUG($fmt, ...) \ + zowelog(NULL, LOG_COMP_MODREG, ZOWE_LOG_DEBUG, \ + MODREG_LOG_DEBUG_MSG_ID" %s:%d: "$fmt, __FUNCTION__, __LINE__, \ + ##__VA_ARGS__) + +#define DUMP_DEBUG($data, $size) \ + zowedump(NULL, LOG_COMP_MODREG, ZOWE_LOG_DEBUG, (char *) $data, $size) + + +#ifndef MODREG_LOG_DEBUG_MSG_ID +#define MODREG_LOG_DEBUG_MSG_ID "" +#endif + +ZOWE_PRAGMA_PACK + +/* + * WARNING: THESE TWO VALUES MUST NEVER CHANGE!!! + */ +static const QName MODR_QNAME = {"ZWE "}; +static const RName MODR_RNAME = {8, "ISMODR "}; + +typedef struct ModuleMark_tag { + /* IMPORTANT: this must be kept in sync with the macro MODREG_MARK_MODULE. */ + char eyecatcher[8]; +#define MODREG_MODULE_MARK_EYECATCHER "ZWECMOD:" + uint16_t version; +#define MODREG_MODULE_MARK_VERSION 1 + uint16_t size; +#define MODREG_MODULE_MARK_SIZE 48 + char reserved0[4]; + char buildTime[26]; + char reserved1[6]; +} ModuleMark; + +typedef struct ModuleRegistryEntry_tag { + + char eyecatcher[8]; +#define MODREG_ENTRY_EYECATCHER "ZWEMODRE" + uint8_t version; +#define MODREG_ENTRY_VERSION 1 + uint8_t key; +#define MODREG_ENTRY_KEY 0 + char reserved1[2]; + uint16_t size; +#define MODREG_ENTRY_SIZE 144 + int8_t flag; + char reserved2[1]; + uint64_t creationTime; + char jobName[8]; + uint16_t asid; + char reserved3[6]; + + struct ModuleRegistryEntry_tag *next; + + char moduleName[8]; + ModuleMark mark; + + LPMEA lpaInfo; + +} ModuleRegistryEntry; + +ZOWE_PRAGMA_PACK_RESET + +// TODO move all these similar macros to utils.h +#define MODREG_STATIC_ASSERT($expr) typedef char p[($expr) ? 1 : -1] + +MODREG_STATIC_ASSERT(sizeof(ModuleMark) == MODREG_MODULE_MARK_SIZE); +MODREG_STATIC_ASSERT(sizeof(ModuleRegistryEntry) == MODREG_ENTRY_SIZE); + +static void *allocateCommonStorage(uint32_t size, uint8_t key, + int *rc, int *rsn) { + + void *result = NULL; + key <<= 4; + + __asm( + ASM_PREFIX + " IARST64 REQUEST=GET" + ",SIZE=%[size]" + ",COMMON=YES" + ",OWNER=SYSTEM" + ",FPROT=NO" + ",SENSITIVE=NO" + ",TYPE=PAGEABLE" + ",CALLERKEY=NO,KEY00TOF0=%[key]" + ",FAILMODE=RC" + ",REGS=SAVE" + ",RETCODE=%[rc],RSNCODE=%[rsn] \n" + : "=NR:r1"(result), [rc]"=m"(*rc), [rsn]"=m"(*rsn) + : [size]"m"(size), [key]"m"(key) + : "r0", "r14", "r15" + ); + + if (*rc != 0) { + result = NULL; + } + + return result; +} + +static void freeCommonStorage(void *data) { + + __asm( + ASM_PREFIX + " IARST64 REQUEST=FREE,AREAADDR=%[data],REGS=SAVE \n" + : + : [data]"m"(data) + : "r0", "r1", "r14", "r15" + ); + +} + +static uint64_t getAdjustedSTCK(void) { + // get store-clock value + uint64_t stckValue; + __asm(" STCK 0(%0)" : : "r"(&stckValue)); + // get leap seconds + CVT * __ptr32 cvt = *(void * __ptr32 * __ptr32) 0x10; + void * __ptr32 cvtext2 = cvt->cvtext2; + int64_t *cvtlso = (int64_t * __ptr32) (cvtext2 + 0x50); + // return adjusted value + return stckValue - *cvtlso; +} + +static ModuleRegistryEntry *allocEntry(uint64_t *rsn) { + + ModuleRegistryEntry *entry = NULL; + + int wasProblemState = supervisorMode(TRUE); + + int originalKey = setKey(0); + { + int allocRC, allocRSN; + entry = allocateCommonStorage(sizeof(ModuleRegistryEntry), MODREG_ENTRY_KEY, + &allocRC, &allocRSN); + if (entry != NULL) { + memset(entry, 0, sizeof(ModuleRegistryEntry)); + memcpy(entry->eyecatcher, MODREG_ENTRY_EYECATCHER, + sizeof(entry->eyecatcher)); + entry->version = MODREG_ENTRY_VERSION; + entry->key = MODREG_ENTRY_KEY; + entry->size = sizeof(ModuleRegistryEntry); + entry->creationTime = getAdjustedSTCK(); + memset(entry->moduleName, ' ', sizeof(entry->moduleName)); + + ASCB *ascb = getASCB(); + char *jobName = getASCBJobname(ascb); + if (jobName) { + memcpy(entry->jobName, jobName, sizeof(entry->jobName)); + } else { + memset(entry->jobName, ' ', sizeof(entry->jobName)); + } + entry->asid = ascb->ascbasid; + } else { + *rsn = PACK_RC_RSN(allocRC, allocRSN); + } + + } + setKey(originalKey); + + if (wasProblemState) { + supervisorMode(FALSE); + } + + return entry; +} + +static void freeEntry(ModuleRegistryEntry *entry) { + + int wasProblemState = supervisorMode(TRUE); + + int originalKey = setKey(0); + { + freeCommonStorage(entry); + } + setKey(originalKey); + + if (wasProblemState) { + supervisorMode(FALSE); + } + +} + +static bool isModuleEntryEligible(const ModuleRegistryEntry *entry) { + LOG_DEBUG("validating module entry @ %p:", entry); + DUMP_DEBUG(entry, sizeof(*entry)); + if (entry->version > MODREG_ENTRY_VERSION) { + LOG_DEBUG("module entry has unsupported version %u", entry->version); + return false; + } + if (entry->size > sizeof(*entry)) { + LOG_DEBUG("module entry has unexpected size %u", entry->size); + return false; + } + return true; +} + +static const ModuleRegistryEntry *findModuleEntry(EightCharString module, + const ModuleMark *mark) { + ZVT *zvt = zvtGet(); + LOG_DEBUG("ZVT address = %p", zvt); + if (zvt == NULL) { + return NULL; + } + + LOG_DEBUG("looking for module \'%.8s\'; mark:", module.text); + DUMP_DEBUG(mark, sizeof(*mark)); + + ModuleRegistryEntry *entry = zvt->moduleRegistry; + int entryCount; + for (entryCount = 0; entryCount <= MODREG_MAX_REGISTRY_SIZE; entryCount++) { + if (entry == NULL) { + break; + } + if (isModuleEntryEligible(entry)) { + if (!memcmp(module.text, entry->moduleName, sizeof(entry->moduleName)) && + !memcmp(mark, &entry->mark, sizeof(entry->mark))) { + LOG_DEBUG("entry matched"); + return entry; + } + } else { + LOG_DEBUG("module entry not eligible, skipping..."); + } + entry = entry->next; + } + if (entryCount > MODREG_MAX_REGISTRY_SIZE) { + LOG_DEBUG("too many entries encountered"); + __asm(" ABEND 777,REASON=108 ":::); + } + + return NULL; +} + +static int lockChain(ENQToken *lockToken, uint64_t *rsn) { + + *rsn = 0; + + int lockRSN = 0; + int lockRC = isgenqGetExclusiveLock(&MODR_QNAME, &MODR_RNAME, + ISGENQ_SCOPE_SYSTEM, lockToken, &lockRSN); + if (lockRC > 4) { + *rsn = PACK_RC_RSN(lockRC, lockRSN); + return RC_MODREG_CHAIN_NOT_LOCKED; + } + + return RC_MODREG_OK; +} + +static int unlockChain(ENQToken *lockToken, uint64_t *rsn) { + + *rsn = 0; + + int unlockRSN = 0; + int unlockRC = isgenqReleaseLock(lockToken, &unlockRSN); + if (unlockRC > 4) { + *rsn = PACK_RC_RSN(unlockRC, unlockRSN); + return RC_MODREG_CHAIN_NOT_UNLOCKED; + } + + return RC_MODREG_OK; +} + +static void *loadModule(const void *dcb, const char name[8], + struct exti * __ptr32 moduleInfo, + int *rc, int *rsn) { + + void * __ptr32 entryPoint = NULL; + char parmList[32] = {0}; + + __asm( + ASM_PREFIX + " LOAD EPLOC=(%[name])" + ",DCB=(%[dcb])" + ",ERRET=LOADMERR" + ",EXTINFO=(%[exti])" + ",SF=(E,%[parm]) \n" + " J LOADMEND \n" + "LOADMERR DS 0H \n" + " XGR 0,0 \n" + "LOADMEND DS 0H \n" + : "=NR:r0"(entryPoint), "=NR:r1"(*rc), "=NR:r15"(*rsn) + : [name]"r"(name), [dcb]"r"(dcb), [exti]"r"(moduleInfo), + [parm]"m"(parmList) + : "r14" + ); + + return (void *) ((int) entryPoint & 0x7ffffffe); +} + +static int deleteModule(const char name[8]) { + + int rc = 0; + + __asm( + ASM_PREFIX + " DELETE EPLOC=(%[name]) \n" + : "=NR:r15"(rc) + : [name]"r"(name) + : "r0", "r1", "r14" + ); + + return rc; +} + +static const void *findSequence(const void *buffer, size_t bufferSize, + const void *sequence, size_t sequenceSize) { + if ((bufferSize < sequenceSize) || bufferSize == 0 || sequenceSize == 0) { + return NULL; + } + const char *currPos = buffer; + const char *endPos = (const char *) buffer + bufferSize - sequenceSize; + while (currPos <= endPos) { + currPos = memchr(currPos, *(const char *) sequence, endPos - currPos + 1); + if (currPos == NULL) { + return NULL; + } + if (!memcmp(currPos, sequence, sequenceSize)) { + return currPos; + } + currPos++; + } + return NULL; +} + +static bool isMarkEligible(const ModuleMark *mark, size_t markSpace) { + size_t markStructSize = sizeof(*mark); + LOG_DEBUG("validating mark @ %p:", mark); + DUMP_DEBUG(mark, min(markSpace, markStructSize)); + if (markSpace < markStructSize) { + LOG_DEBUG("mark space too small - %zu vs %zu", markSpace, markStructSize); + return false; + } + if (mark->buildTime[0] != '2' || + mark->buildTime[1] != '0') { + LOG_DEBUG("mark build time has unexpected values"); + return false; + } + if (mark->version > MODREG_MODULE_MARK_VERSION) { + LOG_DEBUG("mark has unsupported version %u", mark->version); + return false; + } + if (mark->size > MODREG_MODULE_MARK_SIZE) { + LOG_DEBUG("mark has unexpected size %u", mark->size); + return false; + } + return true; +} + +static const ModuleMark *findMark(const void *buffer, size_t bufferSize) { + ssize_t bufferOffset = 0; + while (true) { + LOG_DEBUG("looking for a mark in buffer %p (%zu) + %zd", + buffer, bufferSize, bufferOffset); + const char *markInModule = + findSequence((char *) buffer + bufferOffset, bufferSize - bufferOffset, + MODREG_MODULE_MARK_EYECATCHER, + strlen(MODREG_MODULE_MARK_EYECATCHER)); + LOG_DEBUG("mark found @ %p", markInModule); + if (markInModule) { + size_t markSpace = + (char *) buffer + bufferSize - (char *) markInModule; + LOG_DEBUG("mark space = %zu", markSpace); + const ModuleMark *candidate = (ModuleMark *) markInModule; + if (isMarkEligible(candidate, markSpace)) { + return candidate; + } else { + LOG_DEBUG("mark %p is not eligible", markInModule); + bufferOffset += markInModule - (char *) buffer + + strlen(MODREG_MODULE_MARK_EYECATCHER); + } + } else { + return NULL; + } + } +} + +static int readModuleMark(EightCharString ddname, EightCharString module, + ModuleMark *mark, uint64_t *rsn) { + + void *dcb = openSAM(ddname.text, OPEN_CLOSE_INPUT, FALSE, 0, 0, 0); + LOG_DEBUG("dcb = %p", dcb); + if (!dcb) { + return RC_MODREG_OPEN_SAM_ERROR; + } + + int rc = RC_MODREG_OK; + + struct exti modInfo; + int loadRC, loadRSN; + const void *ep = loadModule(dcb, module.text, &modInfo, &loadRC, &loadRSN); + LOG_DEBUG("load - name = \'%.8s\', ep = %p, rc = %d, rsn = 0x%08X", + module.text, ep, loadRC, loadRSN); + if (!ep) { + *rsn = PACK_RC_RSN(loadRC, loadRSN); + rc = RC_MODREG_MODULE_LOAD_ERROR; + goto out_close_sam; + } + LOG_DEBUG("ext num = %d; mod info:", modInfo.exti_numextents); + DUMP_DEBUG(&modInfo, sizeof(modInfo)); + + rc = RC_MODREG_MARK_MISSING; + for (unsigned i = 0; i < modInfo.exti_numextents; i++) { + + struct extixe *moduleExtent = + (struct extixe *) &modInfo.exti_extent_area + i; + void *extentAddress = *(void **) moduleExtent->extixe_addr; + size_t extentSize = *(size_t *) moduleExtent->extixe_length; + LOG_DEBUG("ext addr = %d, size = %zu", extentAddress, extentSize); + /* Can a mark be in-between extents? It's a valid question. Although, + * no proof has been found in any of the doc, it's reasonable to assume, + * that that cannot happen based on how a mark is defined (several DC + * declarations) because otherwise other programs using the same approach + * to declare constants would break.*/ + const ModuleMark *candidate = findMark(extentAddress, extentSize); + if (candidate) { + *mark = *candidate; + rc = RC_MODREG_OK; + break; + } + + } + + int delRC = deleteModule(module.text); + LOG_DEBUG("delete - rc = %d", delRC); + rc = rc ? rc : (delRC ? RC_MODREG_MODULE_DELETE_ERROR : RC_MODREG_OK); + +out_close_sam: + closeSAM(dcb, OPEN_CLOSE_INPUT); + + return rc; +} + +static void initAndAddEntry(ZVT *zvt, ModuleRegistryEntry *entry, + EightCharString module, + const LPMEA *lpaInfo, + const ModuleMark *mark) { + int wasProblemState = supervisorMode(TRUE); + int originalKey = setKey(0); + { + *(LPMEA *) &entry->lpaInfo = *lpaInfo; + memcpy(entry->moduleName, module.text, sizeof(entry->moduleName)); + *(ModuleMark *) &entry->mark = *mark; + entry->next = zvt->moduleRegistry; + zvt->moduleRegistry = entry; + } + setKey(originalKey); + if (wasProblemState) { + supervisorMode(FALSE); + } +} + +int modregRegister(EightCharString ddname, EightCharString module, + LPMEA *lpaInfo, uint64_t *rsn) { + + uint64_t localRSN; + rsn = rsn ? rsn : &localRSN; + + ZVT *zvt = zvtGet(); + LOG_DEBUG("ZVT address = %p", zvt); + if (zvt == NULL) { + return RC_MODREG_ZVT_NULL; + } + + ModuleMark mark; + int readRC = readModuleMark(ddname, module, &mark, rsn); + if (readRC) { + return readRC; + } + + int lockRC; + uint64_t lockRSN; + ENQToken lockToken; + lockRC = lockChain(&lockToken, &lockRSN); + LOG_DEBUG("lock rc = %d, rsn = 0x%08X", lockRC, lockRSN); + if (lockRC) { + return lockRC; + } + + int rc = RC_MODREG_OK; + + const ModuleRegistryEntry *existingEntry = findModuleEntry(module, &mark); + LOG_DEBUG("existing entry found @ %p", existingEntry); + if (existingEntry) { + *lpaInfo = existingEntry->lpaInfo; + rc = RC_MODREG_ALREADY_REGISTERED; + goto out_unlock; + } + + ModuleRegistryEntry *newEntry = allocEntry(rsn); + LOG_DEBUG("new entry allocated @ %p", newEntry); + if (!newEntry) { + rc = RC_MODREG_ALLOC_FAILED; + goto out_unlock; + } + + int lpaRC, lpaRSN; + lpaRC = lpaAdd(lpaInfo, &ddname, &module, &lpaRSN); + LOG_DEBUG("lpa add rc = %d, rsn = 0x%08X", lpaRC, lpaRSN); + if (lpaRC) { + *rsn = PACK_RC_RSN(lpaRC, lpaRSN); + rc = RC_MODREG_LPA_ADD_FAILED; + goto out_free_entry; + } + LOG_DEBUG("lpa info @ %p:", lpaInfo); + DUMP_DEBUG(lpaInfo, sizeof(*lpaInfo)); + + void *lpaModuleAddress = lpaInfo->outputInfo.stuff.successInfo.loadPointAddr; + int lpaModuleSize = *(int *) lpaInfo->outputInfo.stuff.successInfo.modLen; + LOG_DEBUG("lpaModuleAddress = %p, size = %d", + lpaModuleAddress, lpaModuleSize); + const ModuleMark *lpaMark = findMark(lpaModuleAddress, lpaModuleSize); + if (!lpaMark) { + LOG_DEBUG("mark not found in LPA module"); + rc = RC_MODREG_MARK_MISSING_LPA; + goto out_free_entry; + } + + initAndAddEntry(zvt, newEntry, module, lpaInfo, lpaMark); + +out_free_entry: + if (rc != RC_MODREG_OK) { + freeEntry(newEntry); + } + +out_unlock: + lockRC = unlockChain(&lockToken, &lockRSN); + LOG_DEBUG("unlock rc = %d, rsn = 0x%08X", lockRC, lockRSN); + rc = rc ? rc : lockRC; + + return rc; +} + + +/* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ + diff --git a/h/crossmemory.h b/h/crossmemory.h index aa4c14637..19ca97a02 100644 --- a/h/crossmemory.h +++ b/h/crossmemory.h @@ -144,7 +144,8 @@ #define RC_CMS_LANC_NOT_RELEASED 93 #define RC_CMS_STDSVC_PARM_BAD_VERSION 94 #define RC_CMS_CONFIG_VALUE_BUF_TOO_SMALL 95 -#define RC_CMS_MAX_RC 95 +#define RC_CMS_MODREG_FAILED 96 +#define RC_CMS_MAX_RC 96 extern const char *CMS_RC_DESCRIPTION[]; @@ -337,6 +338,7 @@ typedef struct CrossMemoryServer_tag { #define CROSS_MEMORY_SERVER_FLAG_CHECKAUTH 0x00000010 #define CROSS_MEMORY_SERVER_FLAG_CLEAN_LPA 0x00000020 #define CROSS_MEMORY_SERVER_FLAG_RESET_LOOKUP 0x00000040 +#define CROSS_MEMORY_SERVER_FLAG_USE_MODREG 0x00000080 STCBase * __ptr32 base; CMSStarCallback * __ptr32 startCallback; CMSStopCallback * __ptr32 stopCallback; @@ -460,6 +462,7 @@ ZOWE_PRAGMA_PACK_RESET #define CMS_SERVER_FLAG_DEV_MODE 0x00000008 #define CMS_SERVER_FLAG_DEV_MODE_LPA 0x00000010 #define CMS_SERVER_FLAG_RESET_LOOKUP 0x00000020 +#define CMS_SERVER_FLAG_USE_MODREG 0x00000040 #define CMS_SERVICE_FLAG_NONE 0x00000000 #define CMS_SERVICE_FLAG_SPACE_SWITCH 0x00000001 @@ -1162,6 +1165,18 @@ typedef struct CMSDynlinkEnv_tag { #define CMS_LOG_LOOKUP_ANC_RESET_WARN_MSG_TEXT "Look-up routine anchor discard RC = %d" #define CMS_LOG_LOOKUP_ANC_RESET_WARN_MSG CMS_LOG_LOOKUP_ANC_RESET_WARN_MSG_ID" "CMS_LOG_LOOKUP_ANC_RESET_WARN_MSG_TEXT +#ifndef CMS_LOG_MODULE_STATUS_MSG_ID +#define CMS_LOG_MODULE_STATUS_MSG_ID CMS_MSG_PRFX"0258I" +#endif +#define CMS_LOG_MODULE_STATUS_MSG_TEXT "Module status: %s (%p)" +#define CMS_LOG_MODULE_STATUS_MSG CMS_LOG_MODULE_STATUS_MSG_ID" "CMS_LOG_MODULE_STATUS_MSG_TEXT + +#ifndef CMS_LOG_MODREG_ADD_FAILURE_MSG_ID +#define CMS_LOG_MODREG_ADD_FAILURE_MSG_ID CMS_MSG_PRFX"0259E" +#endif +#define CMS_LOG_MODREG_ADD_FAILURE_MSG_TEXT "Module not registered, RC = %d, RSN = 0x%016llX" +#define CMS_LOG_MODREG_ADD_FAILURE_MSG CMS_LOG_MODREG_ADD_FAILURE_MSG_ID" "CMS_LOG_MODREG_ADD_FAILURE_MSG_TEXT + #endif /* H_CROSSMEMORY_H_ */ diff --git a/h/logging.h b/h/logging.h index 1d7654f71..bc70ee950 100644 --- a/h/logging.h +++ b/h/logging.h @@ -92,6 +92,7 @@ ZOWE_PRAGMA_PACK_RESET #define LOG_COMP_LPA 0x008F0001000D0000LLU #define LOG_COMP_RESTDATASET 0x008F0001000E0000LLU #define LOG_COMP_RESTFILE 0x008F0001000F0000LLU +#define LOG_COMP_MODREG 0x008F000100100000LLU #define LOG_COMP_ZOS 0x008F000100130000LLU #define LOG_COMP_HTTPCLIENT 0x008F000100140000LLU #define LOG_COMP_JWT 0x008F000100150000LLU diff --git a/h/modreg.h b/h/modreg.h new file mode 100644 index 000000000..670bbc283 --- /dev/null +++ b/h/modreg.h @@ -0,0 +1,101 @@ +/* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ + +#ifndef H_MODREG_H_ +#define H_MODREG_H_ 1 + +#ifdef METTLE +#include +#include +#else +#include +#endif + +#include "lpa.h" +#include "zowetypes.h" + +/** + * This macro marks a module as eligible for the module registry. The macro + * should be used once in the module code, for example, in the main function. + */ +#define MODREG_MARK_MODULE() \ + __asm( \ + " MACRO \n" \ + "&LABEL GENMMARK \n" \ + " LCLA &MARKVER \n" \ + " LCLA &MARKLEN \n" \ + "&MARKVER SETA 1 \n" \ + "&MARKLEN SETA 48 \n" \ + "L$GMST DC 0D \n" \ + " DC CL8'ZWECMOD:' \n" \ + " DC H'&MARKVER' \n" \ + " DC H'&MARKLEN' \n" \ + " DC XL4'00' \n" \ + " DC CL26'&SYSCLOCK' \n" \ + " DC XL6'00' \n" \ + " MEND , \n" \ + "L$GMMBGN DS 0H \n" \ + " J L$GMMEXT \n" \ + " GENMMARK \n" \ + "L$GMMEXT DS 0H \n" \ + ) + +#define MODREG_MAX_REGISTRY_SIZE 32768 + +#define RC_MODREG_OK 0 +#define RC_MODREG_ALREADY_REGISTERED 1 +#define RC_MODREG_MARK_MISSING 2 +#define RC_MODREG_ZVT_NULL 3 +#define RC_MODREG_OPEN_SAM_ERROR 8 +#define RC_MODREG_MODULE_LOAD_ERROR 9 +#define RC_MODREG_MODULE_DELETE_ERROR 10 +#define RC_MODREG_MARK_MISSING_LPA 11 +#define RC_MODREG_CHAIN_NOT_LOCKED 12 +#define RC_MODREG_CHAIN_NOT_UNLOCKED 13 +#define RC_MODREG_ALLOC_FAILED 14 +#define RC_MODREG_LPA_ADD_FAILED 15 + +#ifndef __LONGNAME__ +#define modregRegister MODRRGST +#endif + +/** + * The function attempts to add a module to the module registry and the LPA; if + * the module is already in the registry, only the existing LPA info is + * returned and @c RC_MODREG_ALREADY_REGISTERED will be returned; if the module + * has no "mark" defined by the @c MODREG_MARK_MODULE macro, no action will be + * taken and @c RC_MODREG_MARK_MISSING will be returned. + * + * When checking the registry, two modules are considered the same, if and + * only if, the modules names and their marks match. + * + * @param[in] ddname the DDNAME of the module to be registered. + * @param[in] module the module to be registered. + * @param[out] lpaInfo the LPA info of this module. + * @param[out] rsn an optional reason code variable providing additional + * information about the failure. + * @return @c RC_MODREG_OK if the modules has been successfully added, + * @c RC_MODREG_ALREADY_REGISTERED if the module is already in the registry, + * and any other RC_MODREG_xxx in case of failure. + */ +int modregRegister(EightCharString ddname, EightCharString module, + LPMEA *lpaInfo, uint64_t *rsn); + +#endif /* H_MODREG_H_ */ + +/* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. +*/ diff --git a/h/zvt.h b/h/zvt.h index c0464c470..83c490744 100644 --- a/h/zvt.h +++ b/h/zvt.h @@ -85,7 +85,8 @@ typedef struct ZVT_tag { uint16_t asid; char reserved22[6]; PAD_LONG(9, void *cmsLookupRoutine); /* points at another page in 31-common */ - char reserved3[104]; + PAD_LONG(10, void *moduleRegistry); /* points at the module registry */ + char reserved3[96]; struct { PAD_LONG(10, ZVTEntry *zis); diff --git a/tests/build_modreg.sh b/tests/build_modreg.sh new file mode 100755 index 000000000..38a19bd32 --- /dev/null +++ b/tests/build_modreg.sh @@ -0,0 +1,54 @@ +#!/bin/sh + +set -e + +export _LD_SYSLIB="//'SYS1.CSSLIB'://'CEE.SCEELKEX'://'CEE.SCEELKED'://'CEE.SCEERUN'://'CEE.SCEERUN2'://'CSF.SCSFMOD0'" + +xlc -S -M -qmetal -q64 -DSUBPOOL=132 -DMETTLE=1 -DMSGPREFIX=\"ZWE\" \ + -qreserved_reg=r12 \ + -DHAVE_METALIO=1 \ + -Wc,langlvl\(extc99\),arch\(8\),agg,exp,list\(\),so\(\),off,xref,roconst,longname,lp64 \ + -I ../h -I /usr/include/zos \ + ../c/modreg.c \ + ../c/alloc.c \ + ../c/isgenq.c \ + ../c/lpa.c \ + ../c/qsam.c \ + ../c/metalio.c \ + ../c/utils.c \ + ../c/timeutls.c \ + ../c/zvt.c \ + ../c/zos.c \ + ./modregtest.c + +for file in \ + modreg \ + alloc \ + isgenq \ + lpa \ + qsam \ + metalio \ + utils \ + timeutls \ + zvt \ + zos \ + modregtest +do + as -mgoff -mobject -mflag=nocont --TERM --RENT -aegimrsx=${file}.asm ${file}.s +done + +ld -V -b ac=1 -b rent -b case=mixed -b map -b xref -b reus -e main \ +-o "//'${USER}.LOADLIB(MODREG)'" \ +modreg.o \ +alloc.o \ +isgenq.o \ +lpa.o \ +qsam.o \ +metalio.o \ +utils.o \ +timeutls.o \ +zvt.o \ +zos.o \ +modregtest.o > MODREG.lnk + + diff --git a/tests/modregtest.c b/tests/modregtest.c new file mode 100644 index 000000000..b26406f4f --- /dev/null +++ b/tests/modregtest.c @@ -0,0 +1,72 @@ + +#include +#include +#include + +#include "zowetypes.h" +#include "lpa.h" +#include "utils.h" +#include "modreg.h" + +/* + +### Build + +Run build_modreg.sh; the module will be placed in `.LOADLIB`. + +### Run + +**IMPORTANT: running this test will consume common storage which is a limited +resource.** + +* Make sure `.LOADLIB` is APF-authorized; +* Use the following JCL to run the program: + +``` +//MODREG JOB USER=&SYSUID,NOTIFY=&SYSUID +//TEST EXEC PGM=MODREG +//STEPLIB DD DSNAME=&SYSUID..LOADLIB,DISP=SHR +//SYSPRINT DD SYSOUT=* +``` + +*/ + +void zowelog(void *context, uint64 compID, int level, char *formatString, ...) { + va_list argPointer; + va_start(argPointer, formatString); + vfprintf(NULL, formatString, argPointer); + va_end(argPointer); + printf("\n"); +} + +void zowedump(void *context, uint64 compID, int level, void *data, + int dataSize) { + dumpbuffer(data, dataSize); +} + +int main() { + + MODREG_MARK_MODULE(); + + LPMEA lpaInfo; + int rc; + uint64_t rsn; + + rc = modregRegister((EightCharString) {"STEPLIB "}, + (EightCharString) {"MODREG "}, + &lpaInfo, &rsn); + printf("modregRegister() -> %d\n", rc); + if (rc != RC_MODREG_OK) { + return 8; + } + + rc = modregRegister((EightCharString) {"STEPLIB "}, + (EightCharString) {"MODREG "}, + &lpaInfo, &rsn); + printf("modregRegister() -> %d\n", rc); + if (rc != RC_MODREG_ALREADY_REGISTERED) { + return 8; + } + + return 0; +} \ No newline at end of file