/* * 1. find a plane to clean in each of the parallel unit * 2. invoke copyback cleaning on all such planes simultaneously */ double ssd_clean_element_copyback(int elem_num, ssd_t *s) { // this means that some (or all) planes require cleaning int clean_req = 0; int i; double max_cleaning_cost = 0; int plane_to_clean[SSD_MAX_PARUNITS_PER_ELEM]; ssd_element_metadata *metadata = &s->elements[elem_num].metadata; int tot_cleans = 0; for (i = 0; i < SSD_PARUNITS_PER_ELEM(s); i ++) { if ((plane_to_clean[i] = ssd_start_cleaning_parunit(i, elem_num, s)) != -1) { clean_req = 1; } } if (clean_req) { for (i = 0; i < SSD_PARUNITS_PER_ELEM(s); i ++) { double cleaning_cost = 0; int plane_num = plane_to_clean[i]; if (plane_num == -1) { // don't force cleaning continue; } if (metadata->plane_meta[plane_num].clean_in_progress) { metadata->plane_meta[plane_num].clean_in_progress = 0; metadata->plane_meta[plane_num].clean_in_block = -1; } metadata->active_page = metadata->plane_meta[plane_num].active_page; cleaning_cost = ssd_clean_plane_copyback(plane_num, elem_num, s); tot_cleans ++; if (max_cleaning_cost < cleaning_cost) { max_cleaning_cost = cleaning_cost; } } } return max_cleaning_cost; }
static double ssd_issue_overlapped_ios(ssd_req **reqs, int total, int elem_num, ssd_t *s) { double max_cost = 0; double parunit_op_cost[SSD_MAX_PARUNITS_PER_ELEM]; double parunit_tot_cost[SSD_MAX_PARUNITS_PER_ELEM]; ssd_element_metadata *metadata; ssd_power_element_stat *power_stat; int lbn; int offset; int i; int read_cycle = 0; listnode **parunits; // all the requests must be of the same type for (i = 1; i < total; i ++) { ASSERT(reqs[i]->is_read == reqs[0]->is_read); } // is this a set of read requests? if (reqs[0]->is_read) { read_cycle = 1; } memset(parunit_tot_cost, 0, sizeof(double)*SSD_MAX_PARUNITS_PER_ELEM); // find the planes to which the reqs are to be issued metadata = &(s->elements[elem_num].metadata); power_stat = &(s->elements[elem_num].power_stat); parunits = ssd_pick_parunits(reqs, total, elem_num, metadata, s); // repeat until we've served all the requests while (1) { double max_op_cost = 0; double read_xfer_cost = 0.0; double write_xfer_cost = 0.0; int active_parunits = 0; int op_count = 0; // do we still have any request to service? for (i = 0; i < SSD_PARUNITS_PER_ELEM(s); i ++) { if (ll_get_size(parunits[i]) > 0) { active_parunits ++; } } // no more requests -- get out if (active_parunits == 0) { break; } // clear this arrays for storing costs memset(parunit_op_cost, 0, sizeof(double)*SSD_MAX_PARUNITS_PER_ELEM); // begin a round of serving. we serve one request per // parallel unit. if an unit has more than one request // in the list, they have to be serialized. max_cost = 0; for (i = 0; i < SSD_PARUNITS_PER_ELEM(s); i ++) { int size; size = ll_get_size(parunits[i]); if (size > 0) { int apn; // this parallel unit has a request to serve ssd_req *r; listnode *n = ll_get_nth_node(parunits[i], 0); op_count ++; ASSERT(op_count <= active_parunits); // get the request r = (ssd_req *)n->data; lbn = ssd_logical_blockno(r->blk, s); apn = r->blk/s->params.page_size; offset = (apn/s->params.nelements)%(s->params.pages_per_block-1); parunit_op_cost[i] = 0; if (r->is_read) { int block = metadata->lba_table[lbn]; if(block == -1){ parunit_op_cost[i] = s->params.page_read_latency; //Micky ssd_power_flash_calculate(SSD_POWER_FLASH_READ, s->params.page_read_latency, power_stat, s); }else if(metadata->block_usage[block].log_index == -1){ parunit_op_cost[i] = s->params.page_read_latency; //Micky ssd_power_flash_calculate(SSD_POWER_FLASH_READ, s->params.page_read_latency, power_stat, s); }else{ parunit_op_cost[i] = s->params.page_read_latency; //Micky ssd_power_flash_calculate(SSD_POWER_FLASH_READ, s->params.page_read_latency, power_stat, s); parunit_op_cost[i] += s->params.page_read_latency; ssd_power_flash_calculate(SSD_POWER_FLASH_READ, s->params.page_read_latency, power_stat, s); s->spare_read++; } //tiel xfer cost read_xfer_cost += ssd_data_transfer_cost(s,r->count); } else { //for write int plane_num = r->plane_num; // issue the write to the current active page. // we need to transfer the data across the serial pins for write. metadata->active_block = metadata->plane_meta[plane_num].active_block; // check lbn table if(metadata->lba_table[lbn] == -1 ) { metadata->lba_table[lbn] = metadata->active_block; parunit_op_cost[i] = _ssd_write_page_osr(s, metadata, lbn, offset, power_stat); _ssd_alloc_active_block(plane_num, elem_num, s); } else { //if already mapped, check log block int tmp_block = metadata->lba_table[lbn]; if(metadata->block_usage[tmp_block].page[offset] == -1) { parunit_op_cost[i] = _ssd_write_page_osr(s, metadata, lbn, offset, power_stat); } else { if (metadata->block_usage[tmp_block].log_index == -1) { metadata->block_usage[tmp_block].log_index = _ssd_alloc_log_block(plane_num, elem_num, s, tmp_block); parunit_op_cost[i] = _ssd_write_log_block_osr(s, metadata, lbn, offset, power_stat); } else { if(_last_page_in_log_block(metadata, s, tmp_block)){ int new_block; parunit_op_cost[i] += ssd_invoke_logblock_cleaning(elem_num, s, lbn); new_block = metadata->lba_table[lbn]; if(metadata->block_usage[new_block].log_index == -1){ metadata->block_usage[new_block].log_index = _ssd_alloc_log_block(plane_num, elem_num, s, tmp_block); } }else{ parunit_op_cost[i] += _ssd_write_log_block_osr(s, metadata, lbn, offset, power_stat); } } } } write_xfer_cost += ssd_data_transfer_cost(s,r->count); } ASSERT(r->count <= s->params.page_size); // calc the cost: the access time should be something like this // for read if (read_cycle) { if (SSD_PARUNITS_PER_ELEM(s) > 4) { printf("modify acc time here ...\n"); ASSERT(0); } if (op_count == 1) { r->acctime = parunit_op_cost[i] + read_xfer_cost; r->schtime = parunit_tot_cost[i] + r->acctime; } else { r->acctime = ssd_data_transfer_cost(s,r->count); r->schtime = parunit_tot_cost[i] + read_xfer_cost + parunit_op_cost[i]; } } else { // for write r->acctime = parunit_op_cost[i]; r->schtime = parunit_tot_cost[i] + write_xfer_cost + r->acctime; } // find the maximum cost for this round of operations if (max_cost < r->schtime) { max_cost = r->schtime; } // release the node from the linked list ll_release_node(parunits[i], n); } } ssd_power_flash_calculate(SSD_POWER_FLASH_BUS_DATA_TRANSFER, read_xfer_cost, power_stat, s); ssd_power_flash_calculate(SSD_POWER_FLASH_BUS_DATA_TRANSFER, write_xfer_cost, power_stat, s); // we can start the next round of operations only after all // the operations in the first round are over because we're // limited by the one set of pins to all the parunits for (i = 0; i < SSD_PARUNITS_PER_ELEM(s); i ++) { parunit_tot_cost[i] = max_cost; } } for (i = 0; i < SSD_PARUNITS_PER_ELEM(s); i ++) { ll_release(parunits[i]); } free(parunits); power_stat->acc_time += max_cost; return max_cost; }
/* * vp * description: this routine allocates and initializes the ssd element metadata * structures. FIXME: if the systems is powered up, this init routine has to * populate the structures by scanning the summary pages (to implement this, * we can read from a disk checkpoint file). but, this is future work. */ void ssd_element_metadata_init(int elem_number, ssd_element_metadata *metadata, ssd_t *currdisk) { gang_metadata *g; unsigned int ppage; unsigned int i; unsigned int bytes_to_alloc; unsigned int tot_blocks = currdisk->params.blocks_per_element; unsigned int tot_pages = tot_blocks * currdisk->params.pages_per_block; unsigned int reserved_blocks, usable_blocks, export_size; unsigned int reserved_blocks_per_plane, usable_blocks_per_plane; unsigned int bitpos; unsigned int active_block; unsigned int elem_index; unsigned int bsn = 1; int plane_block_mapping = currdisk->params.plane_block_mapping; ////////////////////////////////////////////////////////////////////////////// // active page starts at the 1st page on the reserved section reserved_blocks_per_plane = (currdisk->params.reserve_blocks * currdisk->params.blocks_per_plane) / 100; usable_blocks_per_plane = currdisk->params.blocks_per_plane - reserved_blocks_per_plane; reserved_blocks = reserved_blocks_per_plane * currdisk->params.planes_per_pkg; usable_blocks = usable_blocks_per_plane * currdisk->params.planes_per_pkg; ////////////////////////////////////////////////////////////////////////////// // initialize the free blocks and free pages metadata->tot_free_blocks = reserved_blocks; ////////////////////////////////////////////////////////////////////////////// // assign the gang and init the element's free pages metadata->gang_num = elem_number / currdisk->params.elements_per_gang; currdisk->gang_meta[metadata->gang_num].elem_free_pages[elem_number] = \ metadata->tot_free_blocks * SSD_DATA_PAGES_PER_BLOCK(currdisk); g = &currdisk->gang_meta[metadata->gang_num]; elem_index = elem_number % currdisk->params.elements_per_gang; ////////////////////////////////////////////////////////////////////////////// // let's begin cleaning with the first plane metadata->plane_to_clean = 0; metadata->plane_to_write = 0; metadata->block_alloc_pos = 0; metadata->reqs_waiting = 0; metadata->tot_migrations = 0; metadata->tot_pgs_migrated = 0; metadata->mig_cost = 0; ////////////////////////////////////////////////////////////////////////////// // init the plane metadata for (i = 0; i < (unsigned int)currdisk->params.planes_per_pkg; i ++) { int blocks_to_skip; switch(plane_block_mapping) { case PLANE_BLOCKS_CONCAT: blocks_to_skip = (i*currdisk->params.blocks_per_plane + usable_blocks_per_plane); break; case PLANE_BLOCKS_PAIRWISE_STRIPE: blocks_to_skip = (i/2)*(2*currdisk->params.blocks_per_plane) + (2*usable_blocks_per_plane) + i%2; break; case PLANE_BLOCKS_FULL_STRIPE: blocks_to_skip = (currdisk->params.planes_per_pkg * usable_blocks_per_plane) + i; break; default: fprintf(stderr, "Error: unknown plane_block_mapping %d\n", plane_block_mapping); exit(1); } metadata->plane_meta[i].active_page = blocks_to_skip*currdisk->params.pages_per_block; metadata->plane_meta[i].free_blocks = reserved_blocks_per_plane; metadata->plane_meta[i].valid_pages = 0; metadata->plane_meta[i].clean_in_progress = 0; metadata->plane_meta[i].clean_in_block = -1; metadata->plane_meta[i].block_alloc_pos = i*currdisk->params.blocks_per_plane; metadata->plane_meta[i].parunit_num = i / SSD_PLANES_PER_PARUNIT(currdisk); metadata->plane_meta[i].num_cleans = 0; } ////////////////////////////////////////////////////////////////////////////// // init the next plane to clean in a parunit for (i = 0; i < (unsigned int) SSD_PARUNITS_PER_ELEM(currdisk); i ++) { metadata->parunits[i].plane_to_clean = SSD_PLANES_PER_PARUNIT(currdisk)*i; } ////////////////////////////////////////////////////////////////////////////// // init the element's active page switch(plane_block_mapping) { case PLANE_BLOCKS_CONCAT: metadata->active_page = usable_blocks_per_plane * currdisk->params.pages_per_block; break; case PLANE_BLOCKS_PAIRWISE_STRIPE: metadata->active_page = (2 * usable_blocks_per_plane) * currdisk->params.pages_per_block; break; case PLANE_BLOCKS_FULL_STRIPE: metadata->active_page = (currdisk->params.planes_per_pkg * usable_blocks_per_plane) * currdisk->params.pages_per_block; break; default: fprintf(stderr, "Error: unknown plane_block_mapping %d\n", plane_block_mapping); exit(1); } ASSERT(metadata->active_page == metadata->plane_meta[0].active_page); active_block = metadata->active_page / currdisk->params.pages_per_block; // since we reserve one page out of every block to store the summary info, // the size exported by the flash disk is little less. export_size = usable_blocks * SSD_DATA_PAGES_PER_BLOCK(currdisk); currdisk->data_pages_per_elem = export_size; //printf("res blks = %d, use blks = %d act page = %d exp size = %d\n", // reserved_blocks, usable_blocks, metadata->active_page, export_size); ////////////////////////////////////////////////////////////////////////////// // allocate the lba table if ((metadata->lba_table = (int *)malloc(export_size * sizeof(int))) == NULL) { fprintf(stderr, "Error: malloc to lba table in ssd_element_metadata_init failed\n"); fprintf(stderr, "Allocation size = %d\n", export_size * sizeof(int)); exit(1); } ////////////////////////////////////////////////////////////////////////////// // allocate the free blocks bit map // what if the no of blocks is not divisible by 8? if ((tot_blocks % (sizeof(unsigned char) * 8)) != 0) { fprintf(stderr, "This case is not yet handled\n"); exit(1); } bytes_to_alloc = tot_blocks / (sizeof(unsigned char) * 8); if (!(metadata->free_blocks = (unsigned char *)malloc(bytes_to_alloc))) { fprintf(stderr, "Error: malloc to free_blocks in ssd_element_metadata_init failed\n"); fprintf(stderr, "Allocation size = %d\n", bytes_to_alloc); exit(1); } bzero(metadata->free_blocks, bytes_to_alloc); ////////////////////////////////////////////////////////////////////////////// // allocate the block usage array and initialize it if (!(metadata->block_usage = (block_metadata *)malloc(tot_blocks * sizeof(block_metadata)))) { fprintf(stderr, "Error: malloc to block_usage in ssd_element_metadata_init failed\n"); fprintf(stderr, "Allocation size = %d\n", tot_blocks * sizeof(block_metadata)); exit(1); } bzero(metadata->block_usage, tot_blocks * sizeof(block_metadata)); for (i = 0; i < tot_blocks; i ++) { int j; metadata->block_usage[i].block_num = i; metadata->block_usage[i].page = (int*)malloc(sizeof(int) * currdisk->params.pages_per_block); for (j = 0; j < currdisk->params.pages_per_block; j ++) { metadata->block_usage[i].page[j] = -1; } // assign the plane number to each block switch(plane_block_mapping) { case PLANE_BLOCKS_CONCAT: metadata->block_usage[i].plane_num = i / currdisk->params.blocks_per_plane; break; case PLANE_BLOCKS_PAIRWISE_STRIPE: metadata->block_usage[i].plane_num = (i/(2*currdisk->params.blocks_per_plane))*2 + i%2; break; case PLANE_BLOCKS_FULL_STRIPE: metadata->block_usage[i].plane_num = i % currdisk->params.planes_per_pkg; break; default: fprintf(stderr, "Error: unknown plane_block_mapping %d\n", plane_block_mapping); exit(1); } // set the remaining life time and time of last erasure metadata->block_usage[i].rem_lifetime = SSD_MAX_ERASURES; metadata->block_usage[i].time_of_last_erasure = simtime; // set the block state metadata->block_usage[i].state = SSD_BLOCK_CLEAN; // init the bsn to be zero metadata->block_usage[i].bsn = 0; } ////////////////////////////////////////////////////////////////////////////// // initially, we assume that every logical page is mapped // onto a physical page. we start from the first phy page // and continue to map, leaving the last page of every block // to store the summary information. ppage = 0; i = 0; while (i < export_size) { int pgnum_in_gang; int pp_index; int plane_num; unsigned int block = SSD_PAGE_TO_BLOCK(ppage, currdisk); ASSERT(block < (unsigned int)currdisk->params.blocks_per_element); // if this is the last page in the block if (ssd_last_page_in_block(ppage, currdisk)) { // leave this physical page for summary page and // seal the block metadata->block_usage[block].state = SSD_BLOCK_SEALED; // go to next block ppage ++; block = SSD_PAGE_TO_BLOCK(ppage, currdisk); } // if this block is in the reserved section, skip it // and go to the next block. switch(plane_block_mapping) { case PLANE_BLOCKS_CONCAT: { unsigned int block_index = block % currdisk->params.blocks_per_plane; if ((block_index >= usable_blocks_per_plane) && (block_index < currdisk->params.blocks_per_plane)) { // go to next block ppage = ssd_first_page_in_next_block(ppage, currdisk); continue; } } break; case PLANE_BLOCKS_PAIRWISE_STRIPE: { unsigned int block_index = block % (2*currdisk->params.blocks_per_plane); if ((block_index >= 2*usable_blocks_per_plane) && (block_index < 2*currdisk->params.blocks_per_plane)) { ppage = ssd_first_page_in_next_block(ppage, currdisk); continue; } } break; case PLANE_BLOCKS_FULL_STRIPE: // ideally the control should not come here ... if ((block >= usable_blocks) && (block < (unsigned int)currdisk->params.blocks_per_element)) { printf("Error: the control should not come here ...\n"); ppage = ssd_first_page_in_next_block(ppage, currdisk); continue; } break; default: fprintf(stderr, "Error: unknown plane_block_mapping %d\n", plane_block_mapping); exit(1); } // when the control comes here, 'ppage' contains the next page // that can be assigned to a logical page. // find the index of the phy page within the block pp_index = ppage % currdisk->params.pages_per_block; // populate the lba table metadata->lba_table[i] = ppage; pgnum_in_gang = elem_index * export_size + i; g->pg2elem[pgnum_in_gang].e = elem_number; // mark this block as not free and its state as 'in use'. // note that a block could be not free and its state be 'sealed'. // it is enough if we set it once while working on the first phy page. // also increment the block sequence number. if (pp_index == 0) { bitpos = ssd_block_to_bitpos(currdisk, block); ssd_set_bit(metadata->free_blocks, bitpos); metadata->block_usage[block].state = SSD_BLOCK_INUSE; metadata->block_usage[block].bsn = bsn ++; } // increase the usage count per block plane_num = metadata->block_usage[block].plane_num; metadata->block_usage[block].page[pp_index] = i; metadata->block_usage[block].num_valid ++; metadata->plane_meta[plane_num].valid_pages ++; // go to the next physical page ppage ++; // go to the next logical page i ++; } ////////////////////////////////////////////////////////////////////////////// // mark the block that corresponds to the active page // as not free and 'in_use'. switch(currdisk->params.copy_back) { case SSD_COPY_BACK_DISABLE: bitpos = ssd_block_to_bitpos(currdisk, active_block); ssd_set_bit(metadata->free_blocks, bitpos); metadata->block_usage[active_block].state = SSD_BLOCK_INUSE; metadata->block_usage[active_block].bsn = bsn ++; break; case SSD_COPY_BACK_ENABLE: for (i = 0; i < (unsigned int)currdisk->params.planes_per_pkg; i ++) { int plane_active_block = SSD_PAGE_TO_BLOCK(metadata->plane_meta[i].active_page, currdisk); bitpos = ssd_block_to_bitpos(currdisk, plane_active_block); ssd_set_bit(metadata->free_blocks, bitpos); metadata->block_usage[plane_active_block].state = SSD_BLOCK_INUSE; metadata->block_usage[plane_active_block].bsn = bsn ++; metadata->tot_free_blocks --; metadata->plane_meta[i].free_blocks --; } break; default: fprintf(stderr, "Error: invalid copy back policy %d\n", currdisk->params.copy_back); exit(1); } ////////////////////////////////////////////////////////////////////////////// // set the bsn for the ssd element metadata->bsn = bsn; //printf("set the bsn to %d\n", bsn); }
/* * vp * description: this routine allocates and initializes the ssd element metadata * structures. FIXME: if the systems is powered up, this init routine has to * populate the structures by scanning the summary pages (to implement this, * we can read from a disk checkpoint file). but, this is future work. */ void ssd_element_metadata_init(int elem_number, ssd_element_metadata *metadata, ssd_t *currdisk) { gang_metadata *g; unsigned int ppage; unsigned int i; unsigned int bytes_to_alloc; unsigned int tot_blocks = currdisk->params.blocks_per_element; unsigned int tot_pages = tot_blocks * currdisk->params.pages_per_block; unsigned int reserved_blocks, usable_blocks, export_size; unsigned int reserved_blocks_per_plane, usable_blocks_per_plane; unsigned int bitpos; unsigned int active_block; unsigned int elem_index; unsigned int bsn = 1; int plane_block_mapping = currdisk->params.plane_block_mapping; ////////////////////////////////////////////////////////////////////////////// // active page starts at the 1st page on the reserved section reserved_blocks_per_plane = (currdisk->params.reserve_blocks * currdisk->params.blocks_per_plane) / 100; usable_blocks_per_plane = currdisk->params.blocks_per_plane - reserved_blocks_per_plane; reserved_blocks = reserved_blocks_per_plane * currdisk->params.planes_per_pkg; usable_blocks = usable_blocks_per_plane * currdisk->params.planes_per_pkg; ////////////////////////////////////////////////////////////////////////////// // initialize the free blocks and free pages metadata->tot_free_blocks = reserved_blocks; ////////////////////////////////////////////////////////////////////////////// // assign the gang and init the element's free pages metadata->gang_num = elem_number / currdisk->params.elements_per_gang; currdisk->gang_meta[metadata->gang_num].elem_free_pages[elem_number] = \ metadata->tot_free_blocks * SSD_DATA_PAGES_PER_BLOCK(currdisk); g = &currdisk->gang_meta[metadata->gang_num]; elem_index = elem_number % currdisk->params.elements_per_gang; ////////////////////////////////////////////////////////////////////////////// // let's begin cleaning with the first plane metadata->plane_to_clean = 0; metadata->plane_to_write = 0; metadata->block_alloc_pos = 0; metadata->reqs_waiting = 0; metadata->tot_migrations = 0; metadata->tot_pgs_migrated = 0; metadata->mig_cost = 0; ////////////////////////////////////////////////////////////////////////////// // init the plane metadata for (i = 0; i < (unsigned int)currdisk->params.planes_per_pkg; i ++) { int blocks_to_skip; switch(plane_block_mapping) { case PLANE_BLOCKS_CONCAT: assert(!"Refresh Operations not supported with PLANE_BLOCKS_CONCAT mapping"); blocks_to_skip = (i*currdisk->params.blocks_per_plane + usable_blocks_per_plane); break; case PLANE_BLOCKS_PAIRWISE_STRIPE: assert(!"Refresh Operations not supported with PLANE_BLOCKS_PAIRWISE_STRIPE mapping"); blocks_to_skip = (i/2)*(2*currdisk->params.blocks_per_plane) + (2*usable_blocks_per_plane) + i%2; break; case PLANE_BLOCKS_FULL_STRIPE: blocks_to_skip = (currdisk->params.planes_per_pkg * usable_blocks_per_plane) + i; break; default: fprintf(stderr, "Error: unknown plane_block_mapping %d\n", plane_block_mapping); exit(1); } //metadata->plane_meta[i].active_page = blocks_to_skip*currdisk->params.pages_per_block; metadata->plane_meta[i].free_blocks = reserved_blocks_per_plane; metadata->plane_meta[i].valid_pages = 0; metadata->plane_meta[i].clean_in_progress = 0; metadata->plane_meta[i].clean_in_block = -1; metadata->plane_meta[i].block_alloc_pos = i*currdisk->params.blocks_per_plane; metadata->plane_meta[i].parunit_num = i / SSD_PLANES_PER_PARUNIT(currdisk); metadata->plane_meta[i].num_cleans = 0; metadata->plane_meta[i].dead_blocks = 0; } ////////////////////////////////////////////////////////////////////////////// // init the next plane to clean in a parunit for (i = 0; i < (unsigned int) SSD_PARUNITS_PER_ELEM(currdisk); i ++) { metadata->parunits[i].plane_to_clean = SSD_PLANES_PER_PARUNIT(currdisk)*i; } // since we reserve one page out of every block to store the summary info, // the size exported by the flash disk is little less. export_size = usable_blocks * SSD_DATA_PAGES_PER_BLOCK(currdisk); currdisk->data_pages_per_elem = export_size; //printf("res blks = %d, use blks = %d act page = %d exp size = %d\n", // reserved_blocks, usable_blocks, metadata->active_page, export_size); ////////////////////////////////////////////////////////////////////////////// // allocate the lba table if ((metadata->lba_table = (int *)malloc(export_size * sizeof(int))) == NULL) { fprintf(stderr, "Error: malloc to lba table in ssd_element_metadata_init failed\n"); #ifdef __x86_64__ fprintf(stderr, "Allocation size = %lu\n", export_size * sizeof(int)); #else fprintf(stderr, "Allocation size = %u\n", export_size * sizeof(int)); #endif exit(1); } ////////////////////////////////////////////////////////////////////////////// // allocate the free blocks bit map // what if the no of blocks is not divisible by 8? if ((tot_blocks % (sizeof(unsigned char) * 8)) != 0) { fprintf(stderr, "This case is not yet handled\n"); exit(1); } bytes_to_alloc = tot_blocks / (sizeof(unsigned char) * 8); if (!(metadata->free_blocks = (unsigned char *)malloc(bytes_to_alloc))) { fprintf(stderr, "Error: malloc to free_blocks in ssd_element_metadata_init failed\n"); fprintf(stderr, "Allocation size = %d\n", bytes_to_alloc); exit(1); } bzero(metadata->free_blocks, bytes_to_alloc); ////////////////////////////////////////////////////////////////////////////// // allocate the block usage array and initialize it if (!(metadata->block_usage = (block_metadata *)malloc(tot_blocks * sizeof(block_metadata)))) { fprintf(stderr, "Error: malloc to block_usage in ssd_element_metadata_init failed\n"); #ifdef __x86_64__ fprintf(stderr, "Allocation size = %lu\n", tot_blocks * sizeof(block_metadata)); #else fprintf(stderr, "Allocation size = %u\n", tot_blocks * sizeof(block_metadata)); #endif exit(1); } bzero(metadata->block_usage, tot_blocks * sizeof(block_metadata)); for (i = 0; i < tot_blocks; i ++) { int j; metadata->block_usage[i].block_num = i; metadata->block_usage[i].elem_num = elem_number; metadata->block_usage[i].page = (ssd_page_metadata*)malloc(sizeof(ssd_page_metadata) * currdisk->params.pages_per_block); // KJ: initialize all the page metadata bzero(metadata->block_usage[i].page, currdisk->params.pages_per_block * sizeof(ssd_page_metadata)); metadata->block_usage[i].entry_in_refresh_queue= 0; metadata->block_usage[i].total_pages_written = INITIAL_STRESSES*currdisk->params.pages_per_block; metadata->block_usage[i].least_retention_page = &metadata->block_usage[i].page[0]; metadata->block_usage[i].logical_stresses = INITIAL_STRESSES*currdisk->params.pages_per_block; metadata->block_usage[i].physical_stresses = INITIAL_STRESSES*currdisk->params.pages_per_block; assert(metadata->block_usage[i].page); for (j = 0; j < currdisk->params.pages_per_block; j ++) { metadata->block_usage[i].page[j].lpn = -1; metadata->block_usage[i].page[j].time_of_last_stress = 0; metadata->block_usage[i].page[j].retention_period = 1e10; metadata->block_usage[i].page[j].logical_stresses= INITIAL_STRESSES; metadata->block_usage[i].page[j].physical_stresses = INITIAL_STRESSES; metadata->block_usage[i].page[j].eqn_cycle = INITIAL_STRESSES; metadata->block_usage[i].page[j].stress_increment = 1; metadata->block_usage[i].page[j].recovery_period_total = 0; } // assign the plane number to each block switch(plane_block_mapping) { case PLANE_BLOCKS_CONCAT: metadata->block_usage[i].plane_num = i / currdisk->params.blocks_per_plane; break; case PLANE_BLOCKS_PAIRWISE_STRIPE: metadata->block_usage[i].plane_num = (i/(2*currdisk->params.blocks_per_plane))*2 + i%2; break; case PLANE_BLOCKS_FULL_STRIPE: metadata->block_usage[i].plane_num = i % currdisk->params.planes_per_pkg; break; default: fprintf(stderr, "Error: unknown plane_block_mapping %d\n", plane_block_mapping); exit(1); } // set the remaining life time and time of last erasure metadata->block_usage[i].rem_lifetime = SSD_MAX_ERASURES; metadata->block_usage[i].time_of_last_erasure = simtime; // set the block state metadata->block_usage[i].state = SSD_BLOCK_CLEAN; // init the bsn to be zero metadata->block_usage[i].bsn = 0; } //load the ssd state from the snapshot provided by trace_analyzer. if(ta_snapshotfile) ssd_init_stress_info_ta(currdisk,metadata,elem_number); else if(ds_snapshotfile) ssd_init_stress_info_ds(currdisk,metadata,elem_number); //check for dead block status. int dead_block_count = 0; for (i=0;i<currdisk->params.blocks_per_element;i++) { if (i < usable_blocks) //temporarily mark blocks as in use. metadata->block_usage[i].state = SSD_BLOCK_INUSE; if (ssd_block_dead(&metadata->block_usage[i],currdisk)) dead_block_count++; else metadata->block_usage[i].state = SSD_BLOCK_CLEAN; } fprintf(stderr,"Dead blocks in element #%d : %d\n",elem_number,dead_block_count); if (dead_block_count >= reserved_blocks) { //if dead blocks more than overprovisioned blocks, then simulation cannot proceed further. fprintf(stderr,"Total reserved blocks : %d\n",reserved_blocks); fprintf(stderr,"Unable to proceed further as dead blocks more than reserved blocks\n"); fprintf(outputfile,"Total reserved blocks : %d\n",reserved_blocks); fprintf(outputfile,"Unable to proceed further as dead blocks more than reserved blocks\n"); fclose(outputfile); exit(1); } ssd_assert_free_blocks(currdisk,metadata); ////////////////////////////////////////////////////////////////////////////// // initially, we assume that every logical page is mapped // onto a physical page. we start from the first phy page // and continue to map, leaving the last page of every block // to store the summary information. ppage = 0; i = 0; while (i < export_size) { int pgnum_in_gang; int pp_index; int plane_num; unsigned int block = SSD_PAGE_TO_BLOCK(ppage, currdisk); ASSERT(block < (unsigned int)currdisk->params.blocks_per_element); if (metadata->block_usage[block].state == SSD_BLOCK_DEAD) { //block dead. dont use this block for future. bitpos = ssd_block_to_bitpos(currdisk, block); ssd_set_bit(metadata->free_blocks, bitpos); ppage+=currdisk->params.pages_per_block; continue; } // if this is the last page in the block if (ssd_last_page_in_block(ppage, currdisk)) { // leave this physical page for summary page and // seal the block metadata->block_usage[block].state = SSD_BLOCK_SEALED; //metadata->block_usage[block].page[currdisk->params.pages_per_block-1].logical_stresses++; //metadata->block_usage[block].total_pages_written++; // go to next block ppage ++; block = SSD_PAGE_TO_BLOCK(ppage, currdisk); } // if this block is in the reserved section, skip it // and go to the next block. switch(plane_block_mapping) { case PLANE_BLOCKS_CONCAT: { assert(!"Unsupported plane block mapping PLANE_BLOCKS_CONCAT"); unsigned int block_index = block % currdisk->params.blocks_per_plane; if ((block_index >= usable_blocks_per_plane) && (block_index < currdisk->params.blocks_per_plane)) { // go to next block ppage = ssd_first_page_in_next_block(ppage, currdisk); continue; } } break; case PLANE_BLOCKS_PAIRWISE_STRIPE: { assert(!"Unsupported plane block mapping PLANE_BLOCKS_PAIRWISE_STRIPE"); unsigned int block_index = block % (2*currdisk->params.blocks_per_plane); if ((block_index >= 2*usable_blocks_per_plane) && (block_index < 2*currdisk->params.blocks_per_plane)) { ppage = ssd_first_page_in_next_block(ppage, currdisk); continue; } } break; case PLANE_BLOCKS_FULL_STRIPE: if ((block >= usable_blocks+dead_block_count) && (block < (unsigned int)currdisk->params.blocks_per_element)) { printf("Error: the control should not come here ...\n"); ppage = ssd_first_page_in_next_block(ppage, currdisk); continue; } break; default: fprintf(stderr, "Error: unknown plane_block_mapping %d\n", plane_block_mapping); exit(1); } // when the control comes here, 'ppage' contains the next page // that can be assigned to a logical page. // find the index of the phy page within the block pp_index = ppage % currdisk->params.pages_per_block; // populate the lba table metadata->lba_table[i] = ppage; pgnum_in_gang = elem_index * export_size + i; g->pg2elem[pgnum_in_gang].e = elem_number; // mark this block as not free and its state as 'in use'. // note that a block could be not free and its state be 'sealed'. // it is enough if we set it once while working on the first phy page. // also increment the block sequence number. if (pp_index == 0) { bitpos = ssd_block_to_bitpos(currdisk, block); ssd_set_bit(metadata->free_blocks, bitpos); metadata->block_usage[block].state = SSD_BLOCK_INUSE; metadata->block_usage[block].bsn = bsn ++; } // increase the usage count per block plane_num = metadata->block_usage[block].plane_num; metadata->block_usage[block].page[pp_index].lpn = i; metadata->block_usage[block].page[pp_index].time_of_last_stress = simtime; metadata->block_usage[block].num_valid ++; metadata->plane_meta[plane_num].valid_pages ++; // go to the next physical page ppage ++; // go to the next logical page i ++; } ssd_assert_free_blocks(currdisk,metadata); ////////////////////////////////////////////////////////////////////////////// // init the element's active page as well as the plane's active page. int total_blocks = currdisk->params.blocks_per_plane * currdisk->params.planes_per_pkg; for (i = 0; i < (unsigned int)currdisk->params.planes_per_pkg; i ++) { int blocks_to_skip; switch(plane_block_mapping) { case PLANE_BLOCKS_CONCAT: assert(!"Refresh Operations not supported with PLANE_BLOCKS_CONCAT mapping"); blocks_to_skip = (i*currdisk->params.blocks_per_plane + usable_blocks_per_plane); break; case PLANE_BLOCKS_PAIRWISE_STRIPE: assert(!"Refresh Operations not supported with PLANE_BLOCKS_PAIRWISE_STRIPE mapping"); blocks_to_skip = (i/2)*(2*currdisk->params.blocks_per_plane) + (2*usable_blocks_per_plane) + i%2; break; case PLANE_BLOCKS_FULL_STRIPE: /*blocks_to_skip = (currdisk->params.planes_per_pkg * usable_blocks_per_plane) + i + metadata->plane_meta[i].dead_blocks;*/ blocks_to_skip = i; for (;blocks_to_skip<total_blocks;blocks_to_skip+=currdisk->params.planes_per_pkg) if(metadata->block_usage[blocks_to_skip].state==SSD_BLOCK_CLEAN) break; break; default: fprintf(stderr, "Error: unknown plane_block_mapping %d\n", plane_block_mapping); exit(1); } metadata->plane_meta[i].active_page = blocks_to_skip*currdisk->params.pages_per_block; //metadata->plane_meta[i].free_blocks = reserved_blocks_per_plane-metadata->plane_meta[i].dead_blocks; } switch(plane_block_mapping) { case PLANE_BLOCKS_CONCAT: assert(!"Refresh Operations not supported with PLANE_BLOCKS_CONCAT mapping"); metadata->active_page = usable_blocks_per_plane * currdisk->params.pages_per_block; break; case PLANE_BLOCKS_PAIRWISE_STRIPE: assert(!"Refresh Operations not supported with PLANE_BLOCKS_PAIRWISE_STRIPE mapping"); metadata->active_page = (2 * usable_blocks_per_plane) * currdisk->params.pages_per_block; break; case PLANE_BLOCKS_FULL_STRIPE: //metadata->active_page = currdisk->params.planes_per_pkg * (usable_blocks_per_plane+metadata->plane_meta[0].dead_blocks) * currdisk->params.pages_per_block; metadata->active_page = metadata->plane_meta[0].active_page; break; default: fprintf(stderr, "Error: unknown plane_block_mapping %d\n", plane_block_mapping); exit(1); } //ASSERT(metadata->active_page == metadata->plane_meta[0].active_page); active_block = metadata->active_page / currdisk->params.pages_per_block; ////////////////////////////////////////////////////////////////////////////// // mark the block that corresponds to the active page // as not free and 'in_use'. ssd_assert_free_blocks(currdisk,metadata); switch(currdisk->params.copy_back) { case SSD_COPY_BACK_DISABLE: assert(!"Refresh Operation not supported for copy back disabled parameter"); bitpos = ssd_block_to_bitpos(currdisk, active_block); ssd_set_bit(metadata->free_blocks, bitpos); metadata->block_usage[active_block].state = SSD_BLOCK_INUSE; metadata->block_usage[active_block].bsn = bsn ++; break; case SSD_COPY_BACK_ENABLE: for (i = 0; i < (unsigned int)currdisk->params.planes_per_pkg; i ++) { int plane_active_block = SSD_PAGE_TO_BLOCK(metadata->plane_meta[i].active_page, currdisk); bitpos = ssd_block_to_bitpos(currdisk, plane_active_block); ssd_set_bit(metadata->free_blocks, bitpos); metadata->block_usage[plane_active_block].state = SSD_BLOCK_INUSE; metadata->block_usage[plane_active_block].bsn = bsn ++; metadata->tot_free_blocks --; metadata->plane_meta[i].free_blocks --; } ssd_assert_free_blocks(currdisk,metadata); break; default: fprintf(stderr, "Error: invalid copy back policy %d\n", currdisk->params.copy_back); exit(1); } ////////////////////////////////////////////////////////////////////////////// // set the bsn for the ssd element metadata->bsn = bsn; //printf("set the bsn to %d\n", bsn); }
static double ssd_issue_overlapped_ios(ssd_req **reqs, int total, int elem_num, ssd_t *s) { double max_cost = 0; double parunit_op_cost[SSD_MAX_PARUNITS_PER_ELEM]; double parunit_tot_cost[SSD_MAX_PARUNITS_PER_ELEM]; ssd_element_metadata *metadata; int lpn; int i; int read_cycle = 0; listnode **parunits; // all the requests must be of the same type for (i = 1; i < total; i ++) { ASSERT(reqs[i]->is_read == reqs[0]->is_read); } // is this a set of read requests? if (reqs[0]->is_read) { read_cycle = 1; } memset(parunit_tot_cost, 0, sizeof(double)*SSD_MAX_PARUNITS_PER_ELEM); // find the planes to which the reqs are to be issued metadata = &(s->elements[elem_num].metadata); parunits = ssd_pick_parunits(reqs, total, elem_num, metadata, s); // repeat until we've served all the requests while (1) { //double tot_xfer_cost = 0; double max_op_cost = 0; int active_parunits = 0; int op_count = 0; // do we still have any request to service? for (i = 0; i < SSD_PARUNITS_PER_ELEM(s); i ++) { if (ll_get_size(parunits[i]) > 0) { active_parunits ++; } } // no more requests -- get out if (active_parunits == 0) { break; } // clear this arrays for storing costs memset(parunit_op_cost, 0, sizeof(double)*SSD_MAX_PARUNITS_PER_ELEM); // begin a round of serving. we serve one request per // parallel unit. if an unit has more than one request // in the list, they have to be serialized. max_cost = 0; for (i = 0; i < SSD_PARUNITS_PER_ELEM(s); i ++) { int size; size = ll_get_size(parunits[i]); if (size > 0) { // this parallel unit has a request to serve ssd_req *r; listnode *n = ll_get_nth_node(parunits[i], 0); op_count ++; ASSERT(op_count <= active_parunits); // get the request r = (ssd_req *)n->data; lpn = ssd_logical_pageno(r->blk, s); if (r->is_read) { parunit_op_cost[i] = s->params.page_read_latency; } else { int plane_num = r->plane_num; // if this is the last page on the block, allocate a new block if (ssd_last_page_in_block(metadata->plane_meta[plane_num].active_page, s)) { _ssd_alloc_active_block(plane_num, elem_num, s); } // issue the write to the current active page. // we need to transfer the data across the serial pins for write. metadata->active_page = metadata->plane_meta[plane_num].active_page; //printf("elem %d plane %d ", elem_num, plane_num); parunit_op_cost[i] = _ssd_write_page_osr(s, metadata, lpn); } ASSERT(r->count <= s->params.page_size); // calc the cost: the access time should be something like this // for read if (read_cycle) { if (SSD_PARUNITS_PER_ELEM(s) > 4) { printf("modify acc time here ...\n"); ASSERT(0); } if (op_count == 1) { r->acctime = parunit_op_cost[i] + ssd_data_transfer_cost(s,s->params.page_size); r->schtime = parunit_tot_cost[i] + (op_count-1)*ssd_data_transfer_cost(s,s->params.page_size) + r->acctime; } else { r->acctime = ssd_data_transfer_cost(s,s->params.page_size); r->schtime = parunit_tot_cost[i] + op_count*ssd_data_transfer_cost(s,s->params.page_size) + parunit_op_cost[i]; } } else { // for write r->acctime = parunit_op_cost[i] + ssd_data_transfer_cost(s,s->params.page_size); r->schtime = parunit_tot_cost[i] + (op_count-1)*ssd_data_transfer_cost(s,s->params.page_size) + r->acctime; } // find the maximum cost for this round of operations if (max_cost < r->schtime) { max_cost = r->schtime; } // release the node from the linked list ll_release_node(parunits[i], n); } } // we can start the next round of operations only after all // the operations in the first round are over because we're // limited by the one set of pins to all the parunits for (i = 0; i < SSD_PARUNITS_PER_ELEM(s); i ++) { parunit_tot_cost[i] = max_cost; } } for (i = 0; i < SSD_PARUNITS_PER_ELEM(s); i ++) { ll_release(parunits[i]); } free(parunits); return max_cost; }