static int ssd_pick_block_to_clean(int plane_num, int elem_num, ssd_element_metadata *metadata, ssd_t *s) { int block; int log_block; int lbn; int min_valid = s->params.pages_per_block * 2; int i,j; double avg_lifetime = 1; unsigned int reserved_blocks_per_plane = (s->params.reserve_blocks * s->params.blocks_per_plane) / 100; unsigned int usable_blocks_per_plane = s->params.blocks_per_plane - reserved_blocks_per_plane; unsigned int reserved_blocks = reserved_blocks_per_plane * s->params.planes_per_pkg; unsigned int usable_blocks = usable_blocks_per_plane * s->params.planes_per_pkg; int num_valid; avg_lifetime = ssd_compute_avg_lifetime(plane_num, elem_num, s); for(i = 0; i < usable_blocks ; i++) { block = metadata->lba_table[i]; if(block == -1){ continue; } if( metadata->block_usage[block].log_index == -1){ continue; } log_block = metadata->log_data[metadata->block_usage[block].log_index].bsn; num_valid = metadata->block_usage[block].num_valid + metadata->block_usage[log_block].num_valid; if(num_valid < min_valid) { /*min_valid = metadata->block_usage[block].num_valid; if(_ssd_pick_block_to_clean(block, plane_num, elem_num, metadata, s)){ int mig_blk; block_metadata bm; bm = metadata->block_usage[block]; mig_blk = ssd_pick_wear_aware_with_migration(bm.block_num, bm.rem_lifetime, avg_lifetime, mcost, bm.plane_num, elem_num, s); if (ssd_pick_wear_aware(bm.block_num, bm.rem_lifetime, avg_lifetime, s)) { lbn = i; break; } }*/ lbn = i; } } return lbn; }
/* * first we create a hash table of blocks according to their * usage. then we select blocks with the least usage and clean * them. */ static double ssd_clean_blocks_greedy(int plane_num, int elem_num, ssd_t *s) { double cost = 0; double avg_lifetime; int i; usage_table *table; ssd_element_metadata *metadata = &(s->elements[elem_num].metadata); ///////////////////////////////////////////////////////////////////////////// // build the histogram table = ssd_build_usage_table(elem_num, s); ////////////////////////////////////////////////////////////////////////////// // find the average life time of all the blocks in this element avg_lifetime = ssd_compute_avg_lifetime(plane_num, elem_num, s); ///////////////////////////////////////////////////////////////////////////// // we now have a hash table of blocks, where the key of each // bucket is the usage count and each bucket has all the blocks with // the same usage count (i.e., the same num of valid pages). for (i = 0; i <= s->params.pages_per_block; i ++) { int j; usage_table *entry; // get the bucket of blocks with 'i' valid pages entry = &(table[i]); // free all the blocks with 'i' valid pages for (j = 0; j < entry->len; j ++) { int blk = entry->block[j]; int block_life = metadata->block_usage[blk].rem_lifetime; // if this is plane specific cleaning, then skip all the // blocks that don't belong to this plane. if ((plane_num != -1) && (metadata->block_usage[blk].plane_num != plane_num)) { continue; } // if the block is already dead, skip it if (block_life == 0) { continue; } // clean only those blocks that are sealed. if (ssd_can_clean_block(s, metadata, blk)) { // if we care about wear-leveling, then we must rate limit overly cleaned blocks if (s->params.cleaning_policy == DISKSIM_SSD_CLEANING_POLICY_GREEDY_WEAR_AWARE) { // see if this block's remaining lifetime is within // a certain threshold of the average remaining lifetime // of all blocks in this element if (block_life < (SSD_LIFETIME_THRESHOLD_X * avg_lifetime)) { // we have to rate limit this block as it has exceeded // its cleaning limits printf("Rate limiting block %d (block life %d avg life %f\n", blk, block_life, avg_lifetime); if (ssd_rate_limit(block_life, avg_lifetime)) { // skip this block and go to the next one continue; } } } // okies, finally here we're with the block to be cleaned. // invoke cleaning until we reach the high watermark. cost += _ssd_clean_block_fully(blk, metadata->block_usage[blk].plane_num, elem_num, metadata, s); if (ssd_stop_cleaning(plane_num, elem_num, s)) { // no more cleaning is required -- so quit. break; } } } if (ssd_stop_cleaning(plane_num, elem_num, s)) { // no more cleaning is required -- so quit. break; } } // release the table ssd_release_usage_table(table, s); // see if we were able to generate enough free blocks if (!ssd_stop_cleaning(plane_num, elem_num, s)) { printf("Yuck! we couldn't generate enough free pages in plane %d elem %d ssd %d\n", plane_num, elem_num, s->devno); } return cost; }
/* * a greedy solution, where we find the block in a plane with the least * num of valid pages and return it. */ static int ssd_pick_block_to_clean2(int plane_num, int elem_num, double *mcost, ssd_element_metadata *metadata, ssd_t *s) { double avg_lifetime = 1; int i; int size; int block = -1; int min_valid = s->params.pages_per_block - 1; // one page goes for the summary info listnode *greedy_list; *mcost = 0; // find the average life time of all the blocks in this element avg_lifetime = ssd_compute_avg_lifetime(plane_num, elem_num, s); // we create a list of greedily selected blocks ll_create(&greedy_list); for (i = 0; i < s->params.blocks_per_element; i ++) { if (_ssd_pick_block_to_clean(i, plane_num, elem_num, metadata, s)) { // greedily select the block if (metadata->block_usage[i].num_valid <= min_valid) { ASSERT(i == metadata->block_usage[i].block_num); ll_insert_at_head(greedy_list, (void*)&metadata->block_usage[i]); min_valid = metadata->block_usage[i].num_valid; block = i; } } } ASSERT(block != -1); block = -1; // from the greedily picked blocks, select one after rate // limiting the overly used blocks size = ll_get_size(greedy_list); //printf("plane %d elem %d size %d avg lifetime %f\n", // plane_num, elem_num, size, avg_lifetime); for (i = 0; i < size; i ++) { block_metadata *bm; int mig_blk; listnode *n = ll_get_nth_node(greedy_list, i); bm = ((block_metadata *)n->data); if (i == 0) { ASSERT(min_valid == bm->num_valid); } // this is the last of the greedily picked blocks. if (i == size -1) { // select it! block = bm->block_num; break; } if (s->params.cleaning_policy == DISKSIM_SSD_CLEANING_POLICY_GREEDY_WEAR_AGNOSTIC) { block = bm->block_num; break; } else { #if MIGRATE // migration mig_blk = ssd_pick_wear_aware_with_migration(bm->block_num, bm->rem_lifetime, avg_lifetime, mcost, bm->plane_num, elem_num, s); if (mig_blk != bm->block_num) { // data has been migrated and we have a new // block to use block = mig_blk; break; } #endif // pick this block giving consideration to its life time if (ssd_pick_wear_aware(bm->block_num, bm->rem_lifetime, avg_lifetime, s)) { block = bm->block_num; break; } } } ll_release(greedy_list); ASSERT(block != -1); return block; }
//prints the cleaning algo statistics void ssd_printcleanstats(int *set, int setsize, char *sourcestr) { int i; int tot_ssd = 0; int elts_count = 0; double iops = 0; fprintf(outputfile, "\n\nSSD CLEANING STATISTICS\n"); fprintf(outputfile, "---------------------------------------------\n\n"); for (i = 0; i < setsize; i ++) { int j; int tot_elts = 0; ssd_t *s = getssd(set[i]); if (s->params.write_policy == DISKSIM_SSD_WRITE_POLICY_OSR) { elts_count += s->params.nelements; for (j = 0; j < s->params.nelements; j ++) { int plane_num; double avg_lifetime; double elem_iops = 0; double elem_clean_iops = 0; ssd_element_stat *stat = &(s->elements[j].stat); avg_lifetime = ssd_compute_avg_lifetime(-1, j, s); fprintf(outputfile, "%s #%d elem #%d Total reqs issued:\t%d\n", sourcestr, set[i], j, s->elements[j].stat.tot_reqs_issued); fprintf(outputfile, "%s #%d elem #%d Total time taken:\t%f\n", sourcestr, set[i], j, s->elements[j].stat.tot_time_taken); if (s->elements[j].stat.tot_time_taken > 0) { elem_iops = ((s->elements[j].stat.tot_reqs_issued*1000.0)/s->elements[j].stat.tot_time_taken); fprintf(outputfile, "%s #%d elem #%d IOPS:\t%f\n", sourcestr, set[i], j, elem_iops); } fprintf(outputfile, "%s #%d elem #%d Total cleaning reqs issued:\t%d\n", sourcestr, set[i], j, s->elements[j].stat.num_clean); fprintf(outputfile, "%s #%d elem #%d Total cleaning time taken:\t%f\n", sourcestr, set[i], j, s->elements[j].stat.tot_clean_time); fprintf(outputfile, "%s #%d elem #%d Total migrations:\t%d\n", sourcestr, set[i], j, s->elements[j].metadata.tot_migrations); fprintf(outputfile, "%s #%d elem #%d Total pages migrated:\t%d\n", sourcestr, set[i], j, s->elements[j].metadata.tot_pgs_migrated); fprintf(outputfile, "%s #%d elem #%d Total migrations cost:\t%f\n", sourcestr, set[i], j, s->elements[j].metadata.mig_cost); if (s->elements[j].stat.tot_clean_time > 0) { elem_clean_iops = ((s->elements[j].stat.num_clean*1000.0)/s->elements[j].stat.tot_clean_time); fprintf(outputfile, "%s #%d elem #%d clean IOPS:\t%f\n", sourcestr, set[i], j, elem_clean_iops); } fprintf(outputfile, "%s #%d elem #%d Overall IOPS:\t%f\n", sourcestr, set[i], j, ((s->elements[j].stat.num_clean+s->elements[j].stat.tot_reqs_issued)*1000.0)/(s->elements[j].stat.tot_clean_time+s->elements[j].stat.tot_time_taken)); iops += elem_iops; fprintf(outputfile, "%s #%d elem #%d Number of free blocks:\t%d\n", sourcestr, set[i], j, s->elements[j].metadata.tot_free_blocks); fprintf(outputfile, "%s #%d elem #%d Number of cleans:\t%d\n", sourcestr, set[i], j, stat->num_clean); fprintf(outputfile, "%s #%d elem #%d Pages moved:\t%d\n", sourcestr, set[i], j, stat->pages_moved); fprintf(outputfile, "%s #%d elem #%d Total xfer time:\t%f\n", sourcestr, set[i], j, stat->tot_xfer_cost); if (stat->tot_xfer_cost > 0) { fprintf(outputfile, "%s #%d elem #%d Xfer time per page:\t%f\n", sourcestr, set[i], j, stat->tot_xfer_cost/(1.0*stat->pages_moved)); } else { fprintf(outputfile, "%s #%d elem #%d Xfer time per page:\t0\n", sourcestr, set[i], j); } fprintf(outputfile, "%s #%d elem #%d Average lifetime:\t%f\n", sourcestr, set[i], j, avg_lifetime); fprintf(outputfile, "%s #%d elem #%d Plane Level Statistics\n", sourcestr, set[i], j); fprintf(outputfile, "%s #%d elem #%d ", sourcestr, set[i], j); for (plane_num = 0; plane_num < s->params.planes_per_pkg; plane_num ++) { fprintf(outputfile, "%d:(%d) ", plane_num, s->elements[j].metadata.plane_meta[plane_num].num_cleans); } fprintf(outputfile, "\n"); ssd_print_block_lifetime_distribution(j, s, set[i], avg_lifetime, sourcestr); fprintf(outputfile, "\n"); tot_elts += stat->pages_moved; } //fprintf(outputfile, "%s SSD %d average # of pages moved per element %d\n", // sourcestr, set[i], tot_elts / s->params.nelements); tot_ssd += tot_elts; fprintf(outputfile, "\n"); } } if (elts_count > 0) { fprintf(outputfile, "%s Total SSD IOPS:\t%f\n", sourcestr, iops); fprintf(outputfile, "%s Average SSD element IOPS:\t%f\n", sourcestr, iops/elts_count); } //fprintf(outputfile, "%s SSD average # of pages moved per ssd %d\n\n", // sourcestr, tot_ssd / setsize); }