📄 rrc_common.c
字号:
for (disk = 0; disk != old_md_cfg->array.param.nr_disks; disk++) if (old_rrc_cfg[disk].disk_id == diskid) break; if (disk == old_md_cfg->array.param.nr_disks) { /* disk is not in source, so block is free per definition */ fprintf (stderr, "\ndisk_id %i not in source\n", diskid); return; } if (block >= old_rrc_cfg[disk].reconf_blocks) { /* Block must be free, since it's definitely not in the source disk scope. */ /* But this must also be an error, as we cannot resize a single device */ fprintf (stderr, "\nblock %lu is out of range on diskid %i\n", block, diskid); return; } /* Then look in map */ blk = block / 8; ofs = block % 8; if (blk >= source_disk_free_size[disk]) { /* blocks out of range are also free */ fprintf (stderr, "\nblock %lu out of range for free map on diskid %i\n", block, diskid); abort (); } if (!(source_disk_free_map[disk][blk] & (1 << ofs))) { source_disk_free_map[disk][blk] |= (1 << ofs); nr_free_disk_blocks[disk]++; if (nr_free_disk_blocks[disk] > old_rrc_cfg[disk].reconf_blocks) { abort (); } if (nr_free_blocks () > source_blocks) { abort (); } } else if (check) { fprintf (stderr, "\nDouble free of block %lu on diskid %i\n", block, disk); }}voidmark_disk_block_free (int diskid, unsigned long block){ common_mark_disk_block_free (diskid, block, 1);}voidunchecked_mark_disk_block_free (int diskid, unsigned long block){ common_mark_disk_block_free (diskid, block, 0);}voidmark_disk_block_unfree (int diskid, unsigned long block){ int disk; unsigned long blk, ofs; for (disk = 0; disk != old_md_cfg->array.param.nr_disks; disk++) if (old_rrc_cfg[disk].disk_id == diskid) break; if (disk == old_md_cfg->array.param.nr_disks) { /* disk is not in source, so block cannot be unfreed * This is an acceptable error, (in order to keep the unfree_all_blocks() routines simple */ return; } if (block >= old_rrc_cfg[disk].reconf_blocks) { fprintf (stderr, "\nblock %lu is out of range on diskid %i and cannot be unfreed \n", block, diskid); abort (); } /* Then look in map */ blk = block / 8; ofs = block % 8; if (blk >= source_disk_free_size[disk]) { /* blocks out of range are also free */ fprintf (stderr, "\nblock %lu out of range for free map on diskid %i\n", block, diskid); abort (); } if (source_disk_free_map[disk][blk] & (1 << ofs)) { assert (nr_free_disk_blocks[disk]); source_disk_free_map[disk][blk] = source_disk_free_map[disk][blk] & ~(1 << ofs); nr_free_disk_blocks[disk]--; } else { /* We don't care about unfreeing unfree blocks */ }}voiddebug_print_nonfree_blocks (void){ int disk; for (disk = 0; disk != old_md_cfg->array.param.nr_disks; disk++) { unsigned long blk; fprintf (stderr, "\nUnfree blocks on disk %i\n", disk); for (blk = 0; blk != old_rrc_cfg[disk].reconf_blocks; blk++) { if (!is_disk_block_free (old_rrc_cfg[disk].disk_id, blk)) fprintf (stderr, "%lu ", blk); } }}voiddebug_print_wish_list (void){ wishlist_diskid_t *wll = wish_lists; while (wll) { wish_t *wl; fprintf (stderr, "Wish list for source disk id %i:\n", wll->source_disk_id); for (wl = wll->wish_list; wl; wl = wl->next) fprintf (stderr, " source disk id %i block %lu , sink disk id %i block %lu\n", wl->source_disk_id, wl->source_rblock, wl->sink_disk_id, wl->sink_rblock); wll = wll->next; }}const char *generic_write_blocks (int partial){ /* * Find nice long consecutive sequences for each disk... * * To-do: Optimization: Start with disks that didn't have read requests, * or had read requests near the write-point. Only if we have to, we * should use the disks we're also reading from. */ int dsk; const char *ret = 0; /* Traverse sink disks */ for (dsk = 0; dsk != new_md_cfg->array.param.nr_disks; dsk++) { fulfilled_t *gift = gift_list_sink_diskid (new_rrc_cfg[dsk].disk_id); if (!gift) continue; /* Now write gifts to disks, sorted */ while (gift) { fulfilled_t *cgift, *best_gift = gift; unsigned long long lrc; for (cgift = gift; cgift; cgift = cgift->next) if (cgift->sink_rblock < best_gift->sink_rblock) best_gift = cgift; /* Seek and write */ lrc = raidseek (new_rrc_cfg[dsk].fd, best_gift->sink_rblock * reconf_block_size); if (lrc == -1) { fprintf (stderr, "\nWriter seek error on disk %i block %lu\n", dsk, best_gift->sink_rblock * reconf_block_size); abort (); } if (!algorithm_check) { int rc = write (new_rrc_cfg[dsk].fd, best_gift->data, reconf_block_size * 1024); if (rc == -1) { fprintf (stderr, "\nWrite error on disk %i\n", dsk); ret = "Writer failed to flush data to disk - bad blocks on disk ?"; } } /* Unhook the gift */ unhook_gift (best_gift); /* Update gift pointer, as we may just have unhooked the head */ gift = gift_list_sink_diskid (new_rrc_cfg[dsk]. disk_id); } /* Yes, we could sync() here, but it's better to schedule write to as many drives * as possible before doing so */ } /* sync() disks for two reasons: * It's nice to know that the data reached the platter. However * this isn't much good usually, since if the reconfiguration fails * the layout is hosed anyway. * If we will be reading from the same disks, it's important for us * to know that the write has completed, if we will be doing more * sophisticated requesting in the future */ for (dsk = 0; dsk != new_md_cfg->array.param.nr_disks; dsk++) fsync (new_rrc_cfg[dsk].fd); return ret;}intmust_fulfill_more (int partial){ /* We tell the source driver whether we require that it * fulfills more wishes. * It will only ask us this if there are no good sequences * left, so we should only tell it "yes" if we really need * those extra gifts */ if (!wish_list_length) return 0; if (!partial) return !!nr_wishes_left (); /* We consider the number of wishes, the number of max. wishes * and the number of gifts. * if the (wishes fulfilled)/(wishes left) fraction is too small, we tell the * driver to keep working. * * This stuff can be tuned... We could also take into account the current * free_friends_depth, as that is the number of _extra_ un-wished-for * gifts that ``sometimes'' occur when fulfilling one single wish. */ return (gift_list_length * 10 / wish_list_length) < 7;}unsignednr_wishes_left (void){ return wish_list_length;}unsignednr_gifts_left (void){ return gift_list_length;}/* * This routine should be generalized to simply fulfill wishes on all disk id's * in a reasonably sequential manner. This will save us from re-implementing * the read_blocks routine for each raid level, as these routines are actually * identical anyways... */voidfulfill_wishes (int partial){ /* * Read requests from the wish_list, fulfill wishes until * either: * A) There are no more wishes * B) There aren't any good wishes left and we've fulfilled enough * * This routine has O(np^2) behaviour for p total wishes in n sequences - which is bad. * Improvements: * sort wishes at wish-time */ while (nr_wishes_left ()) { wish_t *wp; unsigned long length, best_length = 0; int disk, best_disk = -1; unsigned long begin, best_begin = 0; unsigned long end, best_end = 0; int progress; /* * Look for a good fat contigous read */ for (disk = 0; disk != nr_unique_disks; disk++) { unsigned long low_block = 0; int diskid = global_disk_rrc[disk]->disk_id; end = 0; begin = global_disk_rrc[disk]->reconf_blocks; /* Find request with lowest block >= low_block */ for (wp = wish_list_source_diskid (diskid); wp; wp = wp->next) { assert (wp->source_disk_id == diskid); if (wp->source_rblock >= low_block && wp->source_rblock < begin) begin = wp->source_rblock; } /* If there's no wishes for blocks on this disk, keep moving */ if (begin == global_disk_rrc[disk]->reconf_blocks) continue; length = 1; end = begin + 1; /* Now look for requests that follow begin. */ progress = 1; while (progress) { progress = 0; for (wp = wish_list_source_diskid (diskid); wp; wp = wp->next) { assert (wp->source_disk_id == diskid); if (wp->source_rblock == end) { end++; length++; progress = 1; } } } /* Is this one better ? */ if (length > best_length) { best_length = length; best_disk = disk; best_begin = begin; best_end = end; } } /* So we have a best request - maybe */ if (best_length > GOOD_READ_LENGTH || (must_fulfill_more (partial) && best_length > 0)) { fulfill_wish (global_disk_rrc[best_disk]->disk_id, best_begin, best_end); } else if (must_fulfill_more (partial)) { /* there was no best request */ fprintf (stderr, "\nNo best request in reader... best is %lu [%lu-%lu]. This is an internal error" " - I will dump core..\n", best_length, best_begin, best_end); fprintf (stderr, "%u wishes left\n", nr_wishes_left ()); fprintf (stderr, "%lu free blocks\n", nr_free_blocks ()); debug_print_wish_list (); abort (); } else { /* We don't want to read anymore, and we don't have to */ assert (partial);/* fprintf(stderr, "\nReader skipping early, wishes are crappy.\n"); */ return; } } /* Good, we're done. */}static voidfree_block_and_friends (int diskid, unsigned long block){ unsigned long cdepth = 0; while (!is_disk_block_free (diskid, block)) { unsigned long source_block; int dsk; fulfilled_t *gift; unsigned long gblock; const char *ret; cdepth++; /* Calculate block and disk numbers */ source_block = block * reconf_block_size; for (dsk = 0; dsk != old_md_cfg->array.param.nr_disks; dsk++) if (old_rrc_cfg[dsk].disk_id == diskid) break; if (dsk == old_md_cfg->array.param.nr_disks) { fprintf (stderr, "Diskid %i is not in source, when freeing blocks and their friends...\n", diskid); abort (); } /* Seek to and read the block */ if (raidseek (old_rrc_cfg[dsk].fd, source_block) == -1) { fprintf (stderr, "\nSeek error in fulfill_wish() (secondary) for disk %i block %lu. Will dump core.\n", dsk, source_block); abort (); } gift = (fulfilled_t *) malloc (sizeof (fulfilled_t)); if (!gift) { fprintf (stderr, "\nOh cr*p, we failed malloc() when setting up secondary gift. Dump core for now.\n"); abort (); } /* Calculate sink disk for block we're reading */ gblock = source_driver->map_local_to_global (source_driver-> priv, diskid, block); /* We must catch the case where we're shrinking, and the global block ** does exist in the sink, but not in the source, and for RAID[45], we ** should ignore parity blocks (flagged by gblock being ULONG_MAX) */ if (gblock >= sink_blocks) { free (gift); return; } /* Now map to sink diskid and block */ if ( (ret = sink_driver->map_global_to_local (sink_driver->priv, gblock, &gift->sink_disk_id, &gift-> sink_rblock))) { fprintf (stderr, "Sink driver returned fatal error: \"%s\"\n", ret); abort (); } /* Allocate data area and fill it */ if (! (gift->data = (char *) malloc (reconf_block_size * 1024))) { fprintf (stderr, "\nWe couldn't allocate memory for the secondary gift buffer." " Dying horrible death for now.\n"); abort (); } if (!algorithm_check) { int rc = read (old_rrc_cfg[dsk].fd, gift->data, reconf_block_size * 1024); if (rc == -1) { fprintf (stderr, "\nSecondary request: Read error on disk %i in souce (disk_id=%i)." " Bad blocks on disk ?.\n", dsk, diskid); abort (); } } else { /* Make sure that we also actually commit any over-comitted pages */ memset (gift->data, 0, reconf_block_size * 1024); } /* Mark the block as free */ mark_disk_block_free (diskid, block); /* Hook the un-wished-for gift as well */ hook_gift (gift); /* Repeat procedure - the sink disk and block is our new prospect... */ diskid = gift->sink_disk_id; block = gift->sink_rblock; } if (cdepth > free_friends_depth) free_friends_depth = cdepth;}voidfulfill_wish (int source_diskid, unsigned long begin, unsigned long end){ /* * This routine will read a sequence of blocks as requested */ unsigned long curr_block = begin; while (curr_block != end) { wish_t *wishbase = wish_list_source_diskid (source_diskid); if (!wishbase) { fprintf (stderr, "\nNo wishes on disk_id %i given to fulfill_wish() routine!\n", source_diskid); return; } /* Now find the wish for the current block */ for (; wishbase; wishbase = wishbase->next) if (wishbase->source_rblock == curr_block) break; if (!wishbase) { fprintf (stderr, "\nFound hole in the claimed contiguous wish sequence... Will dump core.\n"); abort (); } free_block_and_friends (wishbase->source_disk_id, wishbase->source_rblock); /* Unhook the wish */ unhook_wish (wishbase); /* Next block */ curr_block++; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -