int ea_delete(EA *ea) { int rc = 0; if ( ea->st_type == EA_STORE_TYPE_MEMORY ) { KV_TRC(pAT, "free ea %p ea->st_memory %p", ea, ea->st_memory); // Simple free the block of store if (ea->st_memory) { free(ea->st_memory); ea->st_memory=NULL; } } else { // Call to close out the chunk and free the space // for the device name rc = cblk_close(ea->st_flash, 0); am_free(ea->st_device); } if ( rc == 0 ) { KV_TRC(pAT, "free ea %p", ea); am_free(ea); } return rc; }
/** ******************************************************************************* * \brief ******************************************************************************/ uint32_t kv_async_init_perf_io(uint32_t num_ctxt, uint32_t jobs, uint32_t npool, uint32_t klen, uint32_t vlen, uint32_t LEN, uint32_t secs) { uint32_t job = 0; uint32_t ctxt = 0; kv_t *db[jobs]; printf("ASYNC %dx%dx%d", klen, vlen, LEN); fflush(stdout); for (job=0; job<jobs; job++) { db[job] = (kv_t*)kv_db_create_fixed(LEN, klen+job, vlen); } printf("."); fflush(stdout); for (ctxt=0; ctxt<num_ctxt; ctxt++) { if (kv_async_init_ctxt_perf(ctxt, npool, secs)) return ENOMEM; for (job=0; job<jobs; job++) { kv_async_set_job(KV_ASYNC_CB_SGD | KV_ASYNC_CB_MULTI_CTXT_IO | KV_ASYNC_CB_GTEST, ctxt, job, db[job], vlen, LEN); KV_TRC(pFT, "CREATE_JOB FIXED %dx%dx%d", klen+job, vlen, LEN); } } printf("> "); fflush(stdout); return 0; }
/** ******************************************************************************* * \brief ******************************************************************************/ void kv_async_init_ark_io(uint32_t num_ctxt, uint32_t jobs, uint32_t vlen, uint32_t secs) { uint32_t klen = 16; uint32_t LEN = 50; uint32_t job = 0; uint32_t ctxt = 0; kv_t *db[jobs]; printf("ctxt:%d jobs:%d ASYNC %dx%dx%d", num_ctxt, jobs, klen, vlen, LEN); fflush(stdout); for (job=0; job<jobs; job++) { db[job] = (kv_t*)kv_db_create_fixed(LEN, klen+job, vlen); } printf("."); fflush(stdout); for (ctxt=0; ctxt<num_ctxt; ctxt++) { kv_async_init_ctxt(ctxt, secs); for (job=0; job<jobs; job++) { kv_async_set_job(KV_ASYNC_CB_SGD | KV_ASYNC_CB_MULTI_CTXT_IO, ctxt, job, db[job], vlen, LEN); KV_TRC(pFT, "CREATE_JOB FIXED %dx%dx%d", klen+job, vlen, LEN); } } printf("> "); fflush(stdout); }
/** ******************************************************************************* * \brief ******************************************************************************/ void kv_async_init_ctxt(uint32_t ctxt, uint32_t secs) { char *env_FVT = getenv("FVT_DEV"); async_context_t *pCT = pCTs+ctxt; if (ctxt < 0 || ctxt > KV_ASYNC_MAX_CONTEXTS) { printf("FFDC: kv_async_init_ctxt %d %X\n", ctxt, ctxt); return; } memset(pCT, 0, sizeof(async_context_t)); memset(pCT->pCBs, 0, sizeof(async_CB_t)*KV_ASYNC_JOB_Q); ASSERT_EQ(0, ark_create_verbose(env_FVT, &pCT->ark, 1048576, 4096, 1048576, 20, 256, 8*1024, ARK_KV_VIRTUAL_LUN)); ASSERT_TRUE(NULL != pCT->ark); pCT->flags |= KV_ASYNC_CT_RUNNING; pCT->secs = secs; KV_TRC(pFT, "init_ctxt ctxt:%d ark:%p secs:%d", ctxt, pCT->ark, pCT->secs); }
/** ******************************************************************************* * \brief ******************************************************************************/ void kv_async_set_job(uint32_t flags, uint32_t ctxt, uint32_t job, kv_t *db, uint32_t vlen, uint32_t len) { async_context_t *pCT = pCTs+ctxt; async_CB_t *pCB = NULL; char type[4] = {0}; ASSERT_TRUE(NULL != pCT); ASSERT_TRUE(ctxt >= 0); ASSERT_TRUE(ctxt < KV_ASYNC_MAX_CONTEXTS); ASSERT_TRUE(0 != len); if (flags & KV_ASYNC_CB_SGD) sprintf(type, "SGD"); else sprintf(type, "REP"); pCB = pCT->pCBs+job; memset(pCB, 0, sizeof(async_CB_t)); pCB->ark = pCT->ark; pCB->flags = flags | KV_ASYNC_CB_SET | KV_ASYNC_CB_QUEUED; pCB->db = db; pCB->regen = kv_db_fixed_regen_values; pCB->len = len; pCB->cb = kv_async_cb; pCB->regen_len = vlen; pCB->gvalue = (char*)malloc(pCB->regen_len); pCB->b_mark = B_MARK; pCB->e_mark = E_MARK; KV_TRC(pFT, "CREATE_JOB: ctxt:%d ark:%p %s: pCB:%p flags:%X", ctxt, pCT->ark, type, pCB, pCB->flags); }
/** ******************************************************************************* * \brief ******************************************************************************/ IV *iv_new(uint64_t n, uint64_t m) { uint64_t bits = n * m; uint64_t words = divup(bits, 64); uint64_t bytes = sizeof(IV) + words * sizeof(uint64_t); IV *iv = am_malloc(bytes); if (iv == NULL) { errno = ENOMEM; KV_TRC_FFDC(pAT, "FFDC: n %"PRIu64" m %"PRIu64", errno = %d", n, m, errno); } else { memset(iv,0x00, bytes); iv->n = n; iv->m = m; iv->bits = bits; iv->words = words; iv->mask = 1; iv->mask <<= m; iv->mask -= 1; iv->bar = 64 - m; } KV_TRC(pAT, "iv:%p n:%ld m:%ld", iv, n, m); return iv; }
/** ******************************************************************************* * \brief ******************************************************************************/ uint32_t kv_async_init_ctxt_perf(uint32_t ctxt, uint32_t npool, uint32_t secs) { char *env_FVT = getenv("FVT_DEV"); async_context_t *pCT = pCTs+ctxt; if (ctxt < 0 || ctxt > KV_ASYNC_MAX_CONTEXTS) { printf("FFDC: kv_async_init_ctxt %d %X\n", ctxt, ctxt); return EINVAL; } memset(pCT, 0, sizeof(async_context_t)); memset(pCT->pCBs, 0, sizeof(async_CB_t)*KV_ASYNC_JOB_Q); if (ark_create_verbose(env_FVT, &pCT->ark, 1048576, 4096, 1048576, npool, 256, 8*1024, ARK_KV_VIRTUAL_LUN)) { printf("ark_create failed for ctxt:%d\n", ctxt); return ENOMEM; } if (NULL == pCT->ark) return ENOMEM; pCT->flags |= KV_ASYNC_CT_RUNNING; pCT->secs = secs; KV_TRC(pFT, "init_ctxt ctxt:%d ark:%p", ctxt, pCT->ark); return 0; }
void ark_persistence_calc(_ARK *_arkp) { uint64_t bytes = 0; // We need to determine the total size of the data // that needs to be persisted. Items that we persist // are: // // - Configuration // - Hash Table (hash_t) // - Block List (BL) // - IV->data // Configuration _arkp->pers_cs_bytes = sizeof(p_cntr_t) + sizeof(P_ARK_t); // Hash Table _arkp->pers_cs_bytes += sizeof(hash_t) + (_arkp->ht->n * sizeof(uint64_t)); // Block List _arkp->pers_cs_bytes += sizeof(BL); // calculate for a full ark bytes = _arkp->pers_cs_bytes; bytes += _arkp->bl->list->words * sizeof(uint64_t); _arkp->pers_max_blocks = divup(bytes, _arkp->bsize); KV_TRC(pAT, "PERSIST_CALC pers_cs_bytes:%ld pers_max_blocks:%ld", _arkp->pers_cs_bytes, _arkp->pers_max_blocks); return; }
/** ******************************************************************************* * \brief ******************************************************************************/ void kv_async_init_job(uint32_t flags, uint32_t ctxt, uint32_t job, uint32_t klen, uint32_t vlen, uint32_t len) { kv_t *db = (kv_t*)kv_db_create_fixed(len, klen, vlen); ASSERT_TRUE(NULL != db); KV_TRC(pFT, "CREATE_JOB FIXED %dx%dx%d", klen, vlen, len); kv_async_set_job(flags|KV_ASYNC_CB_GTEST, ctxt, job, db, vlen, len); }
/** ******************************************************************************* * \brief ******************************************************************************/ static void kv_async_perf_done(async_CB_t *pCB) { ++pCB->perf_loops; if (pCB->flags & KV_ASYNC_CB_SHUTDOWN) { KV_TRC(pFT, "shutdown %p %d", pCB, pCB->perf_loops); pCB->flags &= ~(KV_ASYNC_CB_SET | KV_ASYNC_CB_GET | KV_ASYNC_CB_WRITE_PERF | KV_ASYNC_CB_READ_PERF | KV_ASYNC_CB_RUNNING); return; } kv_async_dispatch(pCB); }
/** ******************************************************************************* * \brief ******************************************************************************/ void kv_async_run_jobs(void) { async_CB_t *pCB = NULL; uint32_t ctxt_running = 0; uint32_t jobs_running = 0; uint32_t i = 0; uint32_t next = 0; uint32_t elapse = 0; uint32_t inject = 0; uint32_t secs = 0; uint32_t log_interval = 600; uint64_t ops = 0; uint64_t ios = 0; uint32_t tops = 0; uint32_t tios = 0; uint32_t perf = 0; KV_TRC(pFT, "ASYNC START: 0 minutes"); if (!(pCTs->pCBs->flags & KV_ASYNC_CB_RUNNING)) start = time(0); next = log_interval; do { ctxt_running = FALSE; if (elapse > next) { KV_TRC(pFT, "ASYNC RUNNING: %d elapsed minutes", elapse/60); next += log_interval; } for (i=0; i<KV_ASYNC_MAX_CONTEXTS; i++) { if (! (pCTs[i].flags & KV_ASYNC_CT_RUNNING)) continue; jobs_running = kv_async_dispatch_jobs(i); if (!jobs_running) { pCTs[i].flags &= ~KV_ASYNC_CT_RUNNING; pCTs[i].flags |= KV_ASYNC_CT_DONE; KV_TRC(pFT, "ASYNC DONE ctxt %d %x", i, pCTs[i].flags); continue; } else { ctxt_running = TRUE; } elapse = time(0) - start; if (elapse >= inject && pCTs[i].flags & KV_ASYNC_CT_ERROR_INJECT) { KV_TRC_FFDC(pFT, "FFDC: INJECT ERRORS"); FVT_KV_INJECT_READ_ERROR; FVT_KV_INJECT_WRITE_ERROR; FVT_KV_INJECT_ALLOC_ERROR; ++inject; } if (elapse >= pCTs[i].secs) { for (pCB=pCTs[i].pCBs;pCB<pCTs[i].pCBs+KV_ASYNC_JOB_Q;pCB++) { if ((pCB->flags & KV_ASYNC_CB_RUNNING || pCB->flags & KV_ASYNC_CB_QUEUED) && (!(pCB->flags & KV_ASYNC_CB_SHUTDOWN)) ) { pCB->flags |= KV_ASYNC_CB_SHUTDOWN; KV_TRC_IO(pFT, "SHUTDOWN pCB %p (%d >= %d)", pCB, elapse, pCTs[i].secs); } } } usleep(100); } } while (ctxt_running); stop = time(0); secs = stop - start; KV_TRC(pFT, "ASYNC RUNNING DONE: %d minutes", elapse/60); /* log cleanup, since the first ark_delete closes the log file */ for (i=0; i<KV_ASYNC_MAX_CONTEXTS; i++) { if (pCTs[i].flags & KV_ASYNC_CT_DONE) KV_TRC(pFT, "ASYNC CLEANUP: ctxt:%d ark:%p", i, pCTs[i].ark); } /* check for MULTI_CTXT_IO, destroy common kv dbs */ for (pCB=pCTs->pCBs;pCB<pCTs->pCBs+KV_ASYNC_JOB_Q;pCB++) { if (pCB->flags & KV_ASYNC_CB_MULTI_CTXT_IO) { kv_db_destroy(pCB->db, pCB->len); } } for (i=0; i<KV_ASYNC_MAX_CONTEXTS; i++) { /* if this context didn't run any I/O */ if (! (pCTs[i].flags & KV_ASYNC_CT_DONE)) continue; pCTs[i].flags &= ~KV_ASYNC_CT_DONE; /* if perf then don't delete the ark here */ if (pCTs[i].flags & KV_ASYNC_CT_PERF) { perf = TRUE; continue; } (void)ark_stats(pCTs[i].ark, &ops, &ios); tops += (uint32_t)ops; tios += (uint32_t)ios; KV_TRC(pFT, "PERF ark%p ops:%"PRIu64" ios:%"PRIu64"", pCTs[i].ark, ops, ios); EXPECT_EQ(0, ark_delete(pCTs[i].ark)); } if (!perf) { tops = tops / secs; tios = tios / secs; printf("op/s:%d io/s:%d secs:%d\n", tops, tios, secs); KV_TRC(pFT, "PERF op/s:%d io/s:%d secs:%d", tops, tios, secs); } }
/** ******************************************************************************* * \brief ******************************************************************************/ void kv_async_job_perf(uint32_t jobs, uint32_t klen, uint32_t vlen,uint32_t len) { long int wr_us = 0; long int rd_us = 0; long int mil = 1000000; float wr_s = 0; float rd_s = 0; float wr_mb = 0; float rd_mb = 0; uint64_t mb64_1 = (uint64_t)KV_1M; uint64_t wr_bytes = 0; uint64_t rd_bytes = 0; uint64_t ops = 0; uint64_t post_ops = 0; uint64_t ios = 0; uint64_t post_ios = 0; float wr_ops = 0; float wr_ios = 0; float rd_ops = 0; float rd_ios = 0; uint32_t secs = 5; uint32_t job = 0; struct timeval stop, start; kv_async_init_ctxt(0, secs); for (job=0; job<jobs; job++) { kv_async_init_job(KV_ASYNC_CB_SGD|KV_ASYNC_CB_WRITE_PERF, ASYNC_SINGLE_CONTEXT, job, klen+job, vlen, len); } pCTs->flags |= KV_ASYNC_CT_PERF; /* do writes */ (void)ark_stats(kv_async_get_ark(ASYNC_SINGLE_CONTEXT), &ops, &ios); KV_TRC(pFT, "PERF wr: ops:%"PRIu64" ios:%"PRIu64"", ops, ios); gettimeofday(&start, NULL); kv_async_run_jobs(); /* run write jobs */ KV_TRC(pFT, "writes done"); gettimeofday(&stop, NULL); wr_us += (stop.tv_sec*mil + stop.tv_usec) - (start.tv_sec*mil + start.tv_usec); (void)ark_stats(kv_async_get_ark(ASYNC_SINGLE_CONTEXT),&post_ops,&post_ios); KV_TRC(pFT, "PERF wr: ops:%"PRIu64" ios:%"PRIu64"", post_ops, post_ios); wr_ops += post_ops - ops; wr_ios += post_ios - ios; /* calc bytes written */ for (job=0; job<jobs; job++) { wr_bytes += (klen+vlen+job)*len*(pCTs->pCBs+job)->perf_loops; } /* do reads */ for (job=0; job<jobs; job++) { if ((pCTs->pCBs+job)->perf_loops) { (pCTs->pCBs+job)->perf_loops = 0; (pCTs->pCBs+job)->flags = KV_ASYNC_CB_GET | KV_ASYNC_CB_QUEUED | KV_ASYNC_CB_READ_PERF; } } pCTs->flags |= KV_ASYNC_CT_RUNNING; (void)ark_stats(kv_async_get_ark(0), &ops, &ios); KV_TRC(pFT, "PERF rd: ops:%"PRIu64" ios:%"PRIu64"", ops, ios); gettimeofday(&start, NULL); kv_async_run_jobs(); /* run read jobs */ gettimeofday(&stop, NULL); KV_TRC(pFT, "reads done"); rd_us += (stop.tv_sec*mil + stop.tv_usec) - (start.tv_sec*mil + start.tv_usec); (void)ark_stats(kv_async_get_ark(0), &post_ops, &post_ios); KV_TRC(pFT, "PERF rd: ops:%"PRIu64" ios:%"PRIu64"", post_ops, post_ios); rd_ops += post_ops - ops; rd_ios += post_ios - ios; ASSERT_EQ(0, ark_delete(pCTs->ark)); /* calc bytes read */ for (job=0; job<jobs; job++) { rd_bytes += vlen*len*(pCTs->pCBs+job)->perf_loops; kv_db_destroy((pCTs->pCBs+job)->db, (pCTs->pCBs+job)->len); if ((pCTs->pCBs+job)->gvalue) free((pCTs->pCBs+job)->gvalue); } /* calc and print results */ wr_s = (float)((float)wr_us/(float)mil); wr_mb = (float)((double)wr_bytes / (double)mb64_1); rd_s = (float)((float)rd_us/(float)mil); rd_mb = (float)((double)rd_bytes / (double)mb64_1); printf("ASYNC %dx%dx%d writes: %.3d jobs %2.3f mb in %.1f secs at ", klen, vlen, len, jobs, wr_mb, wr_s); printf("%2.3f mbps, %6.0f op/s, %.0f io/s\n", wr_mb/wr_s, wr_ops/wr_s, wr_ios/wr_s); printf("ASYNC %dx%dx%d reads: %.3d jobs %2.3f mb in %.1f secs at ", klen, vlen, len, jobs, rd_mb, rd_s); printf("%2.3f mbps, %6.0f op/s, %.0f io/s\n", rd_mb/rd_s, rd_ops/rd_s, rd_ios/rd_s); }
EA *ea_new(const char *path, uint64_t bsize, int basyncs, uint64_t *size, uint64_t *bcount, uint64_t vlun) { int rc = 0; size_t plen = 0; uint8_t *store = NULL; EA *ea = NULL; chunk_id_t chkid = NULL_CHUNK_ID; chunk_ext_arg_t ext = 0; if (!(fetch_and_or(&cflsh_blk_lib_init,1))) { // We need to call cblk_init once before // we use any other cblk_ interfaces rc = cblk_init(NULL,0); if (rc) { KV_TRC_FFDC(pAT, "cblk_init failed path %s bsize %"PRIu64" " "size %"PRIu64" bcount %"PRIu64", errno = %d", path, bsize, *size, *bcount, errno); goto error_exit; } } ea = am_malloc(sizeof(EA)); if (NULL == ea) { KV_TRC_FFDC(pAT, "Out of memory path %s bsize %"PRIu64" size %"PRIu64" " "bcount %"PRIu64", errno = %d", path, bsize, *size, *bcount, errno); goto error_exit; } // We need to check the path parameter to see if // we are going to use memory or a file/capi // device (to be determined by the block layer) if ( (NULL == path) || (strlen(path) == 0) ) { KV_TRC(pAT, "EA_STORE_TYPE_MEMORY"); // Using memory for store ea->st_type = EA_STORE_TYPE_MEMORY; store = malloc(*size); if (NULL == store) { errno = ENOMEM; KV_TRC_FFDC(pAT, "Out of memory for store path %s bsize %"PRIu64" " "size %"PRIu64" bcount %"PRIu64", errno = %d", path, bsize, *size, *bcount, errno); goto error_exit; } *bcount = ((*size) / bsize); ea->st_memory = store; } else { KV_TRC(pAT, "EA_STORE_TYPE_FILE(%s)", path); // Using a file. We don't care if it's an actual // file or a CAPI device, we let block layer // decide and we just use the chunk ID that is // passed back from the cblk_open call. ea->st_type = EA_STORE_TYPE_FILE; // Check to see if we need to create the store on a // physical or virtual LUN. Previously, in GA1, // we keyed off the size and if it was 0, then we // asked for the LUN to be physical. Now, the user // can specify with a flag. if ( vlun == 0 ) { KV_TRC(pAT, "cblk_open PHYSICAL LUN: %s", path); chkid = cblk_open(path, basyncs, O_RDWR, ext, CBLK_OPN_NO_INTRP_THREADS); if (NULL_CHUNK_ID == chkid) { printf("cblk_open physical lun failed\n"); KV_TRC_FFDC(pAT, "cblk_open phys lun failed path:%s bsize:%ld " "size:%ld bcount:%ld, errno:%d", path, bsize, *size, *bcount, errno); goto error_exit; } rc = cblk_get_size(chkid, (size_t *)bcount, 0); if ( (rc != 0) || (*bcount == 0) ) { // An error was encountered, close the chunk cblk_close(chkid, 0); chkid = NULL_CHUNK_ID; KV_TRC_FFDC(pAT, "cblk_get_size failed path %s bsize %"PRIu64" " "size %"PRIu64" bcount %"PRIu64", errno = %d", path, bsize, *size, *bcount, errno); goto error_exit; } // Set the size to be returned *size = *bcount * bsize; } else { KV_TRC(pAT, "cblk_open VIRTUAL LUN: %s", path); chkid = cblk_open(path, basyncs, O_RDWR, ext, CBLK_OPN_VIRT_LUN|CBLK_OPN_NO_INTRP_THREADS); if (NULL_CHUNK_ID == chkid) { printf("cblk_open virtual lun failed\n"); KV_TRC_FFDC(pAT, "cblk_open virt lun failed path:%s bsize:%ld " "size:%ld bcount:%ld, errno:%d", path, bsize, *size, *bcount, errno); goto error_exit; } // A specific size was passed in so we try to set the // size of the chunk. *bcount = *size / bsize; rc = cblk_set_size(chkid, (size_t)*bcount, 0); if ( rc != 0 ) { printf("cblk_set_size failed for %ld\n", *bcount); // An error was encountered, close the chunk cblk_close(chkid, 0); chkid = NULL_CHUNK_ID; KV_TRC_FFDC(pAT, "cblk_set_size failed path %s bsize %"PRIu64" " "size %"PRIu64" bcount %"PRIu64", errno = %d", path, bsize, *size, *bcount, errno); goto error_exit; } } // Save off the chunk ID and the device name ea->st_flash = chkid; plen = strlen(path) + 1; ea->st_device = (char *)am_malloc(plen); if (!ea->st_device) { cblk_close(chkid, 0); KV_TRC_FFDC(pAT, "MALLOC st_device failed (%s) plen=%ld errno:%d", path, plen, errno); goto error_exit; } memset(ea->st_device, 0, plen); strncpy(ea->st_device, path, plen); } // Fill in the EA struct pthread_rwlock_init(&(ea->ea_rwlock), NULL); ea->bsize = bsize; ea->bcount = *bcount; ea->size = *size; KV_TRC(pAT, "path %s bsize %"PRIu64" size %"PRIu64" bcount %"PRIu64"", path, bsize, *size, *bcount); goto done; error_exit: am_free(ea); ea = NULL; if (!errno) {KV_TRC_FFDC(pAT, "UNSET_ERRNO"); errno=ENOSPC;} done: return ea; }
int ark_delete(ARK *ark) { int rc = 0; int i = 0; _ARK *_arkp = (_ARK *)ark; scb_t *scbp = NULL; if (NULL == ark) { rc = EINVAL; KV_TRC_FFDC(pAT, "Invalid ARK control block parameter: %d", rc); goto ark_delete_ark_err; } // Wait for all active threads to exit for (i = 0; i < _arkp->nthrds; i++) { scbp = &(_arkp->poolthreads[i]); scbp->poolstate = PT_EXIT; queue_lock(scbp->rqueue); queue_wakeup(scbp->rqueue); queue_unlock(scbp->rqueue); pthread_join(scbp->pooltid, NULL); queue_free(scbp->rqueue); queue_free(scbp->tqueue); queue_free(scbp->ioqueue); pthread_mutex_destroy(&(scbp->poolmutex)); pthread_cond_destroy(&(scbp->poolcond)); KV_TRC(pAT, "thread %d joined", i); } if (_arkp->poolthreads) am_free(_arkp->poolthreads); if (_arkp->pts) am_free(_arkp->pts); for ( i = 0; i < _arkp->nasyncs ; i++ ) { pthread_cond_destroy(&(_arkp->rcbs[i].acond)); pthread_mutex_destroy(&(_arkp->rcbs[i].alock)); } for ( i = 0; i < _arkp->ntasks; i++ ) { bt_delete(_arkp->tcbs[i].inb); bt_delete(_arkp->tcbs[i].oub); am_free(_arkp->tcbs[i].vb_orig); } if (_arkp->iocbs) { am_free(_arkp->iocbs); } if (_arkp->tcbs) { am_free(_arkp->tcbs); } if (_arkp->rcbs) { am_free(_arkp->rcbs); } if (_arkp->ttags) { tag_free(_arkp->ttags); } if (_arkp->rtags) { tag_free(_arkp->rtags); } if (!(_arkp->flags & ARK_KV_VIRTUAL_LUN)) { rc = ark_persist(_arkp); if ( rc != 0 ) { KV_TRC_FFDC(pAT, "FFDC: ark_persist failed: %d", rc); } } pthread_mutex_destroy(&_arkp->mainmutex); (void)ea_delete(_arkp->ea); hash_free(_arkp->ht); bl_delete(_arkp->bl); KV_TRC(pAT, "ark_delete done %p", _arkp); am_free(_arkp); ark_delete_ark_err: KV_TRC_CLOSE(pAT); return rc; }
int ark_create_verbose(char *path, ARK **arkret, uint64_t size, uint64_t bsize, uint64_t hcount, int nthrds, int nqueue, int basyncs, uint64_t flags) { int rc = 0; int p_rc = 0; uint64_t bcount = 0; uint64_t x = 0; int i = 0; int tnum = 0; int rnum = 0; scb_t *scbp = NULL; KV_TRC_OPEN(pAT, "arkdb"); if (NULL == arkret) { KV_TRC_FFDC(pAT, "Incorrect value for ARK control block: rc=EINVAL"); rc = EINVAL; goto ark_create_ark_err; } if ( (flags & (ARK_KV_PERSIST_LOAD|ARK_KV_PERSIST_STORE)) && (flags & ARK_KV_VIRTUAL_LUN) ) { KV_TRC_FFDC(pAT, "Invalid persistence combination with ARK flags: %016lx", flags); rc = EINVAL; goto ark_create_ark_err; } if (nthrds <= 0) { KV_TRC_FFDC(pAT, "invalid nthrds:%d", nthrds); rc = EINVAL; goto ark_create_ark_err; } _ARK *ark = am_malloc(sizeof(_ARK)); if (ark == NULL) { rc = ENOMEM; KV_TRC_FFDC(pAT, "Out of memory allocating ARK control structure for %ld", sizeof(_ARK)); goto ark_create_ark_err; } KV_TRC(pAT, "%p path(%s) size %ld bsize %ld hcount %ld " "nthrds %d nqueue %d basyncs %d flags:%08lx", ark, path, size, bsize, hcount, nthrds, nqueue, basyncs, flags); ark->bsize = bsize; ark->rthread = 0; ark->persload = 0; ark->nasyncs = ((nqueue <= 0) ? ARK_MAX_ASYNC_OPS : nqueue); ark->basyncs = basyncs; ark->ntasks = ARK_MAX_TASK_OPS; ark->nthrds = ARK_VERBOSE_NTHRDS_DEF; // hardcode, perf requirement // Create the KV storage, whether that will be memory based // or flash ark->ea = ea_new(path, ark->bsize, basyncs, &size, &bcount, (flags & ARK_KV_VIRTUAL_LUN)); if (ark->ea == NULL) { if (!errno) {KV_TRC_FFDC(pAT, "UNSET_ERRNO"); errno=ENOMEM;} rc = errno; KV_TRC_FFDC(pAT, "KV storage initialization failed: rc/errno:%d", rc); goto ark_create_ea_err; } // Now that the "connection" to the store has been established // we need to check to see if data was persisted from a previous // instantiation of the KV store. p_rc = ark_check_persistence(ark, flags); if (p_rc > 0) { // We ran into an error while trying to read from // the store. rc = p_rc; KV_TRC_FFDC(pAT, "Persistence check failed: %d", rc); goto ark_create_persist_err; } else if (p_rc == -1) { KV_TRC(pAT, "NO PERSIST LOAD FLAG"); // There was no persistence data, so we just build off // of what was passed into the API. ark->size = size; ark->bcount = bcount; ark->hcount = hcount; ark->vlimit = ARK_VERBOSE_VLIMIT_DEF; ark->blkbits = ARK_VERBOSE_BLKBITS_DEF; ark->grow = ARK_VERBOSE_GROW_DEF; ark->rthread = 0; ark->flags = flags; ark->astart = 0; ark->blkused = 1; ark->ark_exit = 0; ark->nactive = 0; ark->pers_stats.kv_cnt = 0; ark->pers_stats.blk_cnt = 0; ark->pers_stats.byte_cnt = 0; ark->pcmd = PT_IDLE; // Create the requests and tag control blocks and queues. x = ark->hcount / ark->nthrds; ark->npart = x + (ark->hcount % ark->nthrds ? 1 : 0); // Create the hash table ark->ht = hash_new(ark->hcount); if (ark->ht == NULL) { if (!errno) {KV_TRC_FFDC(pAT, "UNSET_ERRNO"); errno=ENOMEM;} rc = errno; KV_TRC_FFDC(pAT, "Hash initialization failed: %d", rc); goto ark_create_ht_err; } // Create the block list ark->bl = bl_new(ark->bcount, ark->blkbits); if (ark->bl == NULL) { if (!errno) {KV_TRC_FFDC(pAT, "UNSET_ERRNO"); errno=ENOMEM;} rc = errno; KV_TRC_FFDC(pAT, "Block list initialization failed: %d", rc); goto ark_create_bl_err; } if (flags & ARK_KV_PERSIST_STORE) { ark_persistence_calc(ark); if (bl_reserve(ark->bl, ark->pers_max_blocks)) {goto ark_create_bl_err;} } } else { KV_TRC(pAT, "PERSIST: %p path(%s) size %ld bsize %ld hcount %ld " "nthrds %d nqueue %ld basyncs %d bcount %ld blkbits %ld", ark, path, ark->size, ark->bsize, ark->hcount, ark->nthrds, ark->nasyncs, ark->basyncs, ark->bcount, ark->blkbits); } rc = pthread_mutex_init(&ark->mainmutex,NULL); if (rc != 0) { KV_TRC_FFDC(pAT, "pthread_mutex_init for main mutex failed: %d", rc); goto ark_create_pth_mutex_err; } ark->rtags = tag_new(ark->nasyncs); if ( NULL == ark->rtags ) { rc = ENOMEM; KV_TRC_FFDC(pAT, "Tag initialization for requests failed: %d", rc); goto ark_create_rtag_err; } ark->ttags = tag_new(ark->ntasks); if ( NULL == ark->ttags ) { rc = ENOMEM; KV_TRC_FFDC(pAT, "Tag initialization for tasks failed: %d", rc); goto ark_create_ttag_err; } ark->rcbs = am_malloc(ark->nasyncs * sizeof(rcb_t)); if ( NULL == ark->rcbs ) { rc = ENOMEM; KV_TRC_FFDC(pAT, "Out of memory allocation of %"PRIu64" bytes for request control blocks", (ark->nasyncs * sizeof(rcb_t))); goto ark_create_rcbs_err; } ark->tcbs = am_malloc(ark->ntasks * sizeof(tcb_t)); if ( NULL == ark->tcbs ) { rc = ENOMEM; KV_TRC_FFDC(pAT, "Out of memory allocation of %"PRIu64" bytes for task control blocks", (ark->ntasks * sizeof(rcb_t))); goto ark_create_tcbs_err; } ark->iocbs = am_malloc(ark->ntasks * sizeof(iocb_t)); if ( NULL == ark->iocbs ) { rc = ENOMEM; KV_TRC_FFDC(pAT, "Out of memory allocation of %"PRIu64" bytes for io control blocks", (ark->ntasks * sizeof(iocb_t))); goto ark_create_iocbs_err; } ark->poolthreads = am_malloc(ark->nthrds * sizeof(scb_t)); if ( NULL == ark->poolthreads ) { rc = ENOMEM; KV_TRC_FFDC(pAT, "Out of memory allocation of %"PRIu64" bytes for server thread control blocks", (ark->nthrds * sizeof(scb_t))); goto ark_create_poolthreads_err; } for ( rnum = 0; rnum < ark->nasyncs ; rnum++ ) { ark->rcbs[rnum].stat = A_NULL; pthread_cond_init(&(ark->rcbs[rnum].acond), NULL); pthread_mutex_init(&(ark->rcbs[rnum].alock), NULL); } for ( tnum = 0; tnum < ark->ntasks; tnum++ ) { ark->tcbs[tnum].inb = bt_new(0, ark->vlimit, sizeof(uint64_t), &(ark->tcbs[tnum].inblen), &(ark->tcbs[tnum].inb_orig)); if (ark->tcbs[tnum].inb == NULL) { if (!errno) {KV_TRC_FFDC(pAT, "UNSET_ERRNO"); errno=ENOMEM;} rc = errno; KV_TRC_FFDC(pAT, "Bucket allocation for inbuffer failed: %d", rc); goto ark_create_taskloop_err; } ark->tcbs[tnum].oub = bt_new(0, ark->vlimit, sizeof(uint64_t), &(ark->tcbs[tnum].oublen), &(ark->tcbs[tnum].oub_orig)); if (ark->tcbs[tnum].oub == NULL) { if (!errno) {KV_TRC_FFDC(pAT, "UNSET_ERRNO"); errno=ENOMEM;} rc = errno; KV_TRC_FFDC(pAT, "Bucket allocation for outbuffer failed: %d", rc); goto ark_create_taskloop_err; } //ark->tcbs[tnum].vbsize = bsize * 1024; ark->tcbs[tnum].vbsize = bsize * 256; ark->tcbs[tnum].vb_orig = am_malloc(ark->tcbs[tnum].vbsize); if (ark->tcbs[tnum].vb_orig == NULL) { rc = ENOMEM; KV_TRC_FFDC(pAT, "Out of memory allocation for %"PRIu64" bytes for variable size buffer", (bsize * 1024)); goto ark_create_taskloop_err; } ark->tcbs[tnum].vb = ptr_align(ark->tcbs[tnum].vb_orig); } *arkret = (void *)ark; ark->pts = (PT *)am_malloc(sizeof(PT) * ark->nthrds); if ( ark->pts == NULL ) { rc = ENOMEM; KV_TRC_FFDC(pAT, "Out of memory allocation for %"PRIu64" bytes for server thread data", (sizeof(PT) * ark->nthrds)); goto ark_create_taskloop_err; } for (i = 0; i < ark->nthrds; i++) { PT *pt = &(ark->pts[i]); scbp = &(ark->poolthreads[i]); memset(scbp, 0, sizeof(scb_t)); // Start off the random start point for this thread // at -1, to show that it has not been part of a // ark_random call. scbp->rlast = -1; scbp->holds = 0; scbp->poolstate = PT_RUN; scbp->poolstats.io_cnt = 0; scbp->poolstats.ops_cnt = 0; scbp->poolstats.kv_cnt = 0; scbp->poolstats.blk_cnt = 0; scbp->poolstats.byte_cnt = 0; pthread_mutex_init(&(scbp->poolmutex), NULL); pthread_cond_init(&(scbp->poolcond), NULL); scbp->rqueue = queue_new(ark->nasyncs); scbp->tqueue = queue_new(ark->ntasks); scbp->ioqueue = queue_new(ark->ntasks); pt->id = i; pt->ark = ark; rc = pthread_create(&(scbp->pooltid), NULL, pool_function, pt); if (rc != 0) { KV_TRC_FFDC(pAT, "pthread_create of server thread failed: %d", rc); goto ark_create_poolloop_err; } } #if 0 while (ark->nactive < ark->nthrds) { usleep(1); //printf("Create waiting %d/%d\n", ark->nactive, ark->nthrds); } #endif ark->pcmd = PT_RUN; goto ark_create_return; ark_create_poolloop_err: for (; i >= 0; i--) { scbp = &(ark->poolthreads[i]); if (scbp->pooltid != 0) { queue_lock(scbp->rqueue); queue_wakeup(scbp->rqueue); queue_unlock(scbp->rqueue); pthread_join(scbp->pooltid, NULL); pthread_mutex_destroy(&(scbp->poolmutex)); pthread_cond_destroy(&(scbp->poolcond)); if ( scbp->rqueue != NULL ) { queue_free(scbp->rqueue); } if ( scbp->tqueue != NULL ) { queue_free(scbp->tqueue); } if ( scbp->ioqueue != NULL ) { queue_free(scbp->ioqueue); } } } if ( ark->pts != NULL ) { am_free(ark->pts); } ark_create_taskloop_err: for ( tnum = 0; tnum < ark->ntasks; tnum++ ) { if (ark->tcbs[tnum].inb) { bt_delete(ark->tcbs[tnum].inb); } if (ark->tcbs[tnum].oub) { bt_delete(ark->tcbs[tnum].oub); } if (ark->tcbs[tnum].vb_orig) { am_free(ark->tcbs[tnum].vb_orig); } } for (rnum = 0; rnum < ark->nasyncs; rnum++) { pthread_cond_destroy(&(ark->rcbs[rnum].acond)); pthread_mutex_destroy(&(ark->rcbs[rnum].alock)); } if ( ark->poolthreads != NULL ) { am_free(ark->poolthreads); } ark_create_poolthreads_err: if (ark->iocbs) { am_free(ark->iocbs); } ark_create_iocbs_err: if (ark->tcbs) { am_free(ark->tcbs); } ark_create_tcbs_err: if (ark->rcbs) { am_free(ark->rcbs); } ark_create_rcbs_err: if (ark->ttags) { tag_free(ark->ttags); } ark_create_ttag_err: if (ark->rtags) { tag_free(ark->rtags); } ark_create_rtag_err: pthread_mutex_destroy(&ark->mainmutex); ark_create_pth_mutex_err: bl_delete(ark->bl); ark_create_bl_err: hash_free(ark->ht); ark_create_ht_err: ark_create_persist_err: ea_delete(ark->ea); ark_create_ea_err: am_free(ark); *arkret = NULL; ark_create_ark_err: KV_TRC_CLOSE(pAT); ark_create_return: return rc; }
int ark_check_persistence(_ARK *_arkp, uint64_t flags) { int32_t rc = -1; char *p_data_orig = NULL; char *p_data = NULL; ark_io_list_t *bl_array = NULL; p_cntr_t *pptr = NULL; P_ARK_t *pcfg = NULL; hash_t *htp = NULL; BL *blp = NULL; uint64_t rdblks = 0; if (flags & ARK_KV_PERSIST_LOAD) {KV_TRC(pAT, "PERSIST_LOAD");} // Ignore the persistence data and load from scratch if ( (!(flags & ARK_KV_PERSIST_LOAD)) || (flags & ARK_KV_VIRTUAL_LUN) ) { return -1; } p_data_orig = am_malloc(_arkp->bsize); if (p_data_orig == NULL) { KV_TRC_FFDC(pAT, "Out of memory allocating %"PRIu64" bytes for the first " "persistence block", _arkp->bsize); rc = ENOMEM; } else { p_data = ptr_align(p_data_orig); bl_array = bl_chain_no_bl(0, 1); rc = ea_async_io(_arkp->ea, ARK_EA_READ, (void *)p_data, bl_array, 1, 1); am_free(bl_array); } if (rc == 0) { // We've read the first block. We check to see if // persistence data is present and if so, then // read the rest of the data from the flash. pptr = (p_cntr_t *)p_data; _arkp->persdata = p_data_orig; if ( memcmp(pptr->p_cntr_magic, ARK_P_MAGIC, sizeof(pptr->p_cntr_magic) != 0)) { KV_TRC_FFDC(pAT, "No magic number found in persistence data: %d", EINVAL); // The magic number does not match so data is either // not present or is corrupted. rc = -1; } else { // Now we check version and the first persistence data // needs to be the ARK_PERSIST_CONFIG block if (pptr->p_cntr_version != ARK_P_VERSION_1 && pptr->p_cntr_version != ARK_P_VERSION_2) { KV_TRC_FFDC(pAT, "Invalid / unsupported version: %"PRIu64"", pptr->p_cntr_version); rc = EINVAL; } else { // Read in the rest of the persistence data pcfg = (P_ARK_t *)(pptr->p_cntr_data + pptr->p_cntr_cfg_offset); rdblks = pcfg->pblocks; if (rdblks > 1) { p_data_orig = am_realloc(p_data_orig, (rdblks * _arkp->bsize)); if (p_data_orig == NULL) { KV_TRC_FFDC(pAT, "Out of memory allocating %"PRIu64" bytes for " "full persistence block", (rdblks * _arkp->bsize)); rc = ENOMEM; } else { p_data = ptr_align(p_data_orig); bl_array = bl_chain_no_bl(0, rdblks); if (bl_array == NULL) { KV_TRC_FFDC(pAT, "Out of memory allocating %"PRIu64" blocks for " "full persistence data", rdblks); rc = ENOMEM; } } // We are still good to read the rest of the data // from the flash if (rc == 0) { KV_TRC(pAT, "PERSIST_RD rdblks:%ld", rdblks); rc = ea_async_io(_arkp->ea, ARK_EA_READ, (void *)p_data, bl_array, rdblks, 1); am_free(bl_array); pptr = (p_cntr_t *)p_data; pcfg = (P_ARK_t *)(pptr->p_cntr_data + pptr->p_cntr_cfg_offset); _arkp->persdata = p_data_orig; } } } } } // If rc == 0, that means we have persistence data if (rc == 0) { KV_TRC(pAT, "PERSIST_META size %ld bsize %ld hcount %ld bcount %ld " "nthrds %d nasyncs %d basyncs %d blkbits %ld version:%ld", pcfg->size, pcfg->bsize, pcfg->hcount, pcfg->bcount, pcfg->nthrds, pcfg->nasyncs, pcfg->basyncs, pcfg->blkbits, pptr->p_cntr_version); _arkp->persload = 1; _arkp->size = pcfg->size; _arkp->flags = flags; _arkp->bsize = pcfg->bsize; _arkp->bcount = pcfg->bcount; _arkp->blkbits = pcfg->blkbits; _arkp->grow = pcfg->grow; _arkp->hcount = pcfg->hcount; _arkp->vlimit = pcfg->vlimit; _arkp->blkused = pcfg->blkused; _arkp->pers_stats.kv_cnt = pcfg->pstats.kv_cnt; _arkp->pers_stats.blk_cnt = pcfg->pstats.blk_cnt; _arkp->pers_stats.byte_cnt = pcfg->pstats.byte_cnt; KV_TRC(pAT, "ARK_META size %ld bsize %ld hcount %ld bcount %ld " "nthrds %d nasyncs %ld basyncs %d blkbits %ld", _arkp->size, _arkp->bsize, _arkp->hcount, _arkp->bcount, _arkp->nthrds, _arkp->nasyncs, _arkp->basyncs, _arkp->blkbits); htp = (hash_t *)(pptr->p_cntr_data + pptr->p_cntr_ht_offset); _arkp->ht = hash_new(htp->n); if (_arkp->ht == NULL) { if (!errno) {KV_TRC_FFDC(pAT, "UNSET_ERRNO"); errno=ENOMEM;} rc = errno; KV_TRC_FFDC(pAT, "ht_new failed: n:%ld rc:%d", htp->n, rc); goto error_exit; } memcpy(_arkp->ht, htp, pptr->p_cntr_ht_size); blp = (BL *)(pptr->p_cntr_data + pptr->p_cntr_bl_offset); _arkp->bl = bl_new(blp->n, blp->w); if (_arkp->bl == NULL) { if (!errno) {KV_TRC_FFDC(pAT, "UNSET_ERRNO"); errno=ENOMEM;} rc = errno; KV_TRC_FFDC(pAT, "bl_new failed: n:%ld w:%ld rc:%d", blp->n, blp->w, rc); goto error_exit; } _arkp->bl->count = blp->count; _arkp->bl->head = blp->head; _arkp->bl->hold = blp->hold; _arkp->bl->top = blp->top; if (pptr->p_cntr_version == ARK_P_VERSION_1) { IV *piv = (IV *)(pptr->p_cntr_data + pptr->p_cntr_bliv_offset); KV_TRC(pAT, "PERSIST_VERSION_1 LOADED"); _arkp->bl->top = _arkp->bl->n; // copy IV->data from piv->data memcpy(_arkp->bl->list->data, piv->data, pptr->p_cntr_bliv_size); } else if (pptr->p_cntr_version == ARK_P_VERSION_2) { KV_TRC(pAT, "PERSIST_VERSION_2 LOADED"); // copy IV->data from bliv_offset memcpy(_arkp->bl->list->data, pptr->p_cntr_data + pptr->p_cntr_bliv_offset, pptr->p_cntr_bliv_size); } else { rc = EINVAL; KV_TRC_FFDC(pAT, "bad persistent version number: ver:%ld", pptr->p_cntr_version); goto error_exit; } KV_TRC(pAT, "BL_META: n:%ld count:%ld head:%ld hold:%ld top:%ld", _arkp->bl->n, _arkp->bl->count, _arkp->bl->head, _arkp->bl->hold, _arkp->bl->top); } error_exit: am_free(p_data_orig); return rc; }
int ark_persist(_ARK *_arkp) { int32_t rc = 0; uint64_t tot_bytes = 0; uint64_t wrblks = 0; char *p_data_orig = NULL; char *p_data = NULL; p_cntr_t *pptr = NULL; char *dptr = NULL; P_ARK_t *pcfg = NULL; ark_io_list_t *bl_array = NULL; if ( (_arkp->ea->st_type == EA_STORE_TYPE_MEMORY) || !(_arkp->flags & ARK_KV_PERSIST_STORE) ) { return 0; } ark_persistence_calc(_arkp); // allocate write buffer tot_bytes = _arkp->pers_max_blocks * _arkp->bsize; p_data_orig = am_malloc(tot_bytes); if (p_data_orig == NULL) { KV_TRC_FFDC(pAT, "Out of memory allocating %"PRIu64" bytes for " "persistence data", tot_bytes); return ENOMEM; } memset(p_data_orig, 0, tot_bytes); p_data = ptr_align(p_data_orig); // Record cntr data pptr = (p_cntr_t *)p_data; memcpy(pptr->p_cntr_magic, ARK_P_MAGIC, sizeof(pptr->p_cntr_magic)); pptr->p_cntr_version = ARK_P_VERSION_2; pptr->p_cntr_size = sizeof(p_cntr_t); // Record configuration info pcfg = (P_ARK_t*)pptr->p_cntr_data; pcfg->flags = _arkp->flags; pcfg->size = _arkp->ea->size; pcfg->bsize = _arkp->bsize; pcfg->bcount = _arkp->bcount; pcfg->blkbits = _arkp->blkbits; pcfg->grow = _arkp->blkbits; pcfg->hcount = _arkp->hcount; pcfg->vlimit = _arkp->vlimit; pcfg->blkused = _arkp->blkused; pcfg->nasyncs = _arkp->nasyncs; pcfg->basyncs = _arkp->basyncs; pcfg->ntasks = _arkp->ntasks; pcfg->nthrds = _arkp->nthrds; ark_persist_stats(_arkp, &(pcfg->pstats)); pptr->p_cntr_cfg_offset = 0; pptr->p_cntr_cfg_size = sizeof(P_ARK_t); dptr = pptr->p_cntr_data; // Record hash info dptr += pptr->p_cntr_cfg_size; pptr->p_cntr_ht_offset = dptr - pptr->p_cntr_data; pptr->p_cntr_ht_size = sizeof(hash_t) + (_arkp->ht->n * sizeof(uint64_t)); memcpy(dptr, _arkp->ht, pptr->p_cntr_ht_size); // Record block list info dptr += pptr->p_cntr_ht_size; pptr->p_cntr_bl_offset = dptr - pptr->p_cntr_data; pptr->p_cntr_bl_size = sizeof(BL); memcpy(dptr, _arkp->bl, pptr->p_cntr_bl_size); // Record IV list info dptr += pptr->p_cntr_bl_size; pptr->p_cntr_bliv_offset = dptr - pptr->p_cntr_data; // bliv_size = bytes in bl->list->data[cs_blocks + kvdata_blocks] // add 2 to top because of how IV->data chaining works pptr->p_cntr_bliv_size = divup((_arkp->bl->top+2) * _arkp->bl->w, 8); memcpy(dptr, _arkp->bl->list->data, pptr->p_cntr_bliv_size); // Calculate wrblks: number of persist metadata blocks to write tot_bytes = _arkp->pers_cs_bytes + pptr->p_cntr_bliv_size; wrblks = pcfg->pblocks = divup(tot_bytes, _arkp->bsize); KV_TRC(pAT, "PERSIST_WR dev:%s top:%ld wrblks:%ld vs pers_max_blocks:%ld", _arkp->ea->st_device, _arkp->bl->top, pcfg->pblocks, _arkp->pers_max_blocks); bl_array = bl_chain_blocks(_arkp->bl, 0, wrblks); if ( NULL == bl_array ) { KV_TRC_FFDC(pAT, "Out of memory allocating %"PRIu64" blocks for block list", wrblks); rc = ENOMEM; } else { rc = ea_async_io(_arkp->ea, ARK_EA_WRITE, (void *)p_data, bl_array, wrblks, _arkp->nthrds); am_free(bl_array); } KV_TRC(pAT, "PERSIST_DATA_STORED rc:%d", rc); am_free(p_data_orig); return rc; }