/** * @brief * We maintain the mom_vnodeinfo data for use in constructing CPU sets and * must ensure that the CPU information is correctly reflected in the * vnodes' "resources_available.ncpus" attribute values before those are * passed back to the server. mom_vnodeinfo data are authoritative since * they must remain unchanged across MoM reconfiguration operations (e.g. * SIGHUP). This function updates those attribute values in the vnode * attribute lists hanging off the list of vnodes (see "placementsets.h") * that is used in constructing the IS_UPDATE2 response to server IS_HELLO * messages. * * @return Void * */ void cpu_raresync(void) { static char ra_ncpus[] = "resources_available.ncpus"; AVL_IX_DESC *pix; int ret; if ((pix = cpuctx) != NULL) { avl_first_key(pix); if (pe == NULL) { pe = malloc(sizeof(AVL_IX_REC) + PBS_MAXNODENAME + 1); if (pe == NULL) { log_err(errno, __func__, "malloc pe failed"); return; } } while ((ret = avl_next_key(pe, pix)) == AVL_IX_OK) { unsigned int i; mominfo_t *mip; mom_vninfo_t *mvp; mip = (mominfo_t *) pe->recptr; assert(mip != NULL); assert(mip->mi_data != NULL); mvp = (mom_vninfo_t *) mip->mi_data; for (i = 0; i < mvp->mvi_ncpus; i++) resadj(vnlp, mvp->mvi_id, ra_ncpus, RES_SET, mvp->mvi_acpus); } assert(ret == AVL_EOIX); } }
/** * @brief Update the AVL tree in global variable * called "env_avltree" for environment variables * * @return void * * @retval None */ void update_env_avltree() { int i = 0; int found = 0; int ret = 0; char varname_tmp[_MAX_ENV] = {'\0'}; char *name = NULL; char *value = NULL; AVL_IX_REC *pe = NULL; extern char **environ; if (env_avltree != NULL) { avl_first_key(env_avltree); if ((pe = malloc(sizeof(AVL_IX_REC) + WINLOG_BUF_SIZE + 1)) != NULL) { while ((ret = avl_next_key(pe, env_avltree)) == AVL_IX_OK) { found = 0; for (i=0; environ[i] != NULL; i++) { if (!strncmp(environ[i], pe->key, strlen(pe->key))) { found = 1; break; } } if (!found) { free(pe->recptr); pe->recptr = NULL; avl_delete_key(pe, env_avltree); } } free(pe); pe = NULL; } for (i=0; environ[i] != NULL; i++) { (void)strncpy_s(varname_tmp, sizeof(varname_tmp), environ[i], _TRUNCATE); if ((name = strtok_s(varname_tmp, "=", &value)) != NULL) { if ((pe = malloc(sizeof(AVL_IX_REC) + WINLOG_BUF_SIZE + 1)) != NULL) { strncpy(pe->key, name, WINLOG_BUF_SIZE); if (avl_find_key(pe, env_avltree) != AVL_IX_OK) { if ((pe->recptr = (void *)strdup(value)) != NULL) avl_add_key(pe, env_avltree); } free(pe); pe = NULL; } } } } }
/** * @brief * cpunum_free() is a context-free function to mark a CPU as available. * It must previously have been marked as inuse via cpuindex_inuse(). * This function is used to recover from failure of make_cpuset(). * * @param[in] cpunum - number of cpu * * @return Void * */ void cpunum_free(unsigned int cpunum) { AVL_IX_DESC *pix; int ret; char buf[BUFSIZ]; sprintf(buf, "mark CPU %u free", cpunum); log_event(PBSEVENT_DEBUG3, 0, LOG_DEBUG, __func__, buf); if ((pix = cpuctx) != NULL) { avl_first_key(pix); if (pe == NULL) { pe = malloc(sizeof(AVL_IX_REC) + PBS_MAXNODENAME + 1); if (pe == NULL) { log_err(errno, __func__, "malloc pe failed"); return; } } while ((ret = avl_next_key(pe, pix)) == AVL_IX_OK) { unsigned int i; mominfo_t *mip; mom_vninfo_t *mvp; mip = (mominfo_t *) pe->recptr; assert(mip != NULL); assert(mip->mi_data != NULL); mvp = (mom_vninfo_t *) mip->mi_data; for (i = 0; i < mvp->mvi_ncpus; i++) if (mvp->mvi_cpulist[i].mvic_cpunum == cpunum) { cpuindex_free(mvp, i); return; } } assert(ret == AVL_EOIX); sprintf(log_buffer, "CPU %d not found in cpuctx", cpunum); log_err(PBSE_SYSTEM, __func__, log_buffer); } }
int entlim_free_ctx(void *ctx, void free_leaf(void *)) { pbs_entlim_key_t *leaf; int rc; leaf = entlim_create_key(NULL); /* alloc space for max sized key */ if (leaf == NULL) return -1; avl_first_key((AVL_IX_DESC *)ctx); while ((rc = avl_next_key((AVL_IX_REC *)leaf, (AVL_IX_DESC *)ctx)) == AVL_IX_OK) { free_leaf(leaf->recptr); } free(leaf); avl_destroy_index((AVL_IX_DESC *)ctx); free(ctx); return 0; }
/** * @brief * entlim_get_next - walk the objects returning the next entry. * If called with a NULL key, it allocates a key and returns * the first entry; otherwise it returns the next entry. * * @param[in] keystr - key string whose key is to be built * @param[in] ctx - pointer to avl descending order tree info * * @return structure handle * @retval key info success * @retval NULL error * Returns NULL following the last entry or when no entry found * The key needs to be freed by the caller when all is said and done. */ pbs_entlim_key_t * entlim_get_next(pbs_entlim_key_t *pkey, void *ctx) { if (ctx == NULL) return NULL; if (pkey == NULL) { pkey = entlim_create_key(NULL); if (pkey == NULL) return NULL; avl_first_key((AVL_IX_DESC *)ctx); } if (avl_next_key(pkey, (AVL_IX_DESC *)ctx) == AVL_IX_OK) { return pkey; } else { free(pkey); return NULL; } }
/** * @brief Destroy the global AVL tree of environment variables created by * create_env_avltree in global variable called "env_avltree" * * @return void * * @retval None */ void destroy_env_avltree() { AVL_IX_REC *pe = NULL; int ret = 0; if (env_avltree != NULL) { avl_first_key(env_avltree); if ((pe = malloc(sizeof(AVL_IX_REC) + WINLOG_BUF_SIZE + 1)) != NULL) { while ((ret = avl_next_key(pe, env_avltree)) == AVL_IX_OK) { free(pe->recptr); pe->recptr = NULL; avl_delete_key(pe, env_avltree); } free(pe); pe = NULL; } avl_destroy_index(env_avltree); free(env_avltree); env_avltree = NULL; } }
/** * @brief * Common code for cpunum_inuse() and cpunum_outofservice(): to find * the given CPU in our list of CPUs per vnode, we walk the list of * mom_vninfo_t structures and for each of those, the attached CPU lists * looking for a match. If taking a CPU out of service, cpu_inuse() must * also adjust the "resources_available.ncpus" for the vnode that contains * the CPU being taken out of service. * * @param[in] cpunum - number of cpu * @param[in] pjob - pointer to job structure * @param[in] outofserviceflag - flag value to indicate whether cpu out of service * * @return - Void * */ static void cpu_inuse(unsigned int cpunum, job *pjob, int outofserviceflag) { static char ra_ncpus[] = "resources_available.ncpus"; AVL_IX_DESC *pix; int ret; if ((pix = cpuctx) != NULL) { avl_first_key(pix); if (pe == NULL) { pe = malloc(sizeof(AVL_IX_REC) + PBS_MAXNODENAME + 1); if (pe == NULL) { log_err(errno, __func__, "malloc pe failed"); return; } } while ((ret = avl_next_key(pe, pix)) == AVL_IX_OK) { unsigned int i; mominfo_t *mip; mom_vninfo_t *mvp; mip = (mominfo_t *) pe->recptr; assert(mip != NULL); assert(mip->mi_data != NULL); mvp = (mom_vninfo_t *) mip->mi_data; for (i = 0; i < mvp->mvi_ncpus; i++) if (mvp->mvi_cpulist[i].mvic_cpunum == cpunum) { if (MVIC_CPUISFREE(mvp, i)) { cpuindex_inuse(mvp, i, pjob); if (outofserviceflag != 0) { assert(vnlp != NULL); assert(mvp->mvi_id != NULL); resadj(vnlp, mvp->mvi_id, ra_ncpus, RES_DECR, 1); mvp->mvi_acpus--; } } return; } } assert(ret == AVL_EOIX); /* * If we get here, we didn't find the CPU in question. * Requests to mark a CPU for which we have no record * out of service may be benign; we may never have * known about it because we were never told about it * in a vnode definitions file, and the caller may * simply not have checked first. So, we silently * ignore those requests. However, if we're asked * to mark a CPU in use but haven't heard of it, that's * an error. */ if (outofserviceflag == 0) { sprintf(log_buffer, "CPU %d not found in cpuctx", cpunum); log_err(PBSE_SYSTEM, __func__, log_buffer); } } }
/** * @brief * Log debugging information pertaining to each CPU that we are managing. * Each CPU may be in one of three states: free for use, in use by a job, * or in use but not assigned to a job (the last of these is used for CPUs * declared unusable by cpunum_outofservice()). * * @return Void * */ void mom_CPUs_report(void) { AVL_IX_DESC *pix; int ret; char reportbuf[LOG_BUF_SIZE]; int bufspace; /* space remaining in reportbuf[] */ if ((pix = cpuctx) != NULL) { char *p; avl_first_key(pix); if (pe == NULL) { pe = malloc(sizeof(AVL_IX_REC) + PBS_MAXNODENAME + 1); if (pe == NULL) { log_err(errno, __func__, "malloc pe failed"); return; } } while ((ret = avl_next_key(pe, pix)) == AVL_IX_OK) { unsigned int i; int first; mominfo_t *mip; mom_vninfo_t *mvp; mip = (mominfo_t *) pe->recptr; assert(mip != NULL); assert(mip->mi_data != NULL); mvp = (mom_vninfo_t *) mip->mi_data; p = reportbuf; bufspace = sizeof(reportbuf); ret = snprintf(p, bufspace, "%s: cpus = ", mvp->mvi_id); if (ret >= bufspace) { truncate_and_log(__func__, reportbuf, sizeof(reportbuf)); continue; } p += ret, bufspace -= ret; for (i = 0, first = 1; i < mvp->mvi_ncpus; i++) { if (first) { first = 0; } else { if (bufspace < 1) { truncate_and_log(__func__, reportbuf, sizeof(reportbuf)); goto line_done; } sprintf(p, ","); p++, bufspace--; } ret = snprintf(p, bufspace, "%d", mvp->mvi_cpulist[i].mvic_cpunum); if (ret >= bufspace) { truncate_and_log(__func__, reportbuf, sizeof(reportbuf)); goto line_done; } p += ret, bufspace -= ret; if (MVIC_CPUISFREE(mvp, i)) ret = snprintf(p, bufspace, " (free)"); else { if (mvp->mvi_cpulist[i].mvic_job == NULL) ret = snprintf(p, bufspace, " (inuse, no job)"); else ret = snprintf(p, bufspace, " (inuse, job %s)", mvp->mvi_cpulist[i].mvic_job->ji_qs.ji_jobid); } if (ret >= bufspace) { truncate_and_log(__func__, reportbuf, sizeof(reportbuf)); goto line_done; } p += ret, bufspace -= ret; } log_event(PBSEVENT_DEBUG3, 0, LOG_DEBUG, __func__, reportbuf); line_done: ; } assert(ret == AVL_EOIX); } }