// initialize or reload cache variables void init_le_to_pe() { void *handle; int r; if(pv_segments) le_to_pe_exit(); vg_pe_sizes = NULL; vg_pe_sizes_len = 0; lvm2_log_fn(parse_pvs_segments); handle = lvm2_init(); lvm2_log_level(handle, 1); r = lvm2_run(handle, "pvs --noheadings --segments -o+lv_name," "seg_start_pe,segtype"); // if (r) // fprintf(stderr, "command failed\n"); sort_segments(pv_segments, pv_segments_num); lvm2_log_fn(parse_vgs_pe_size); r = lvm2_run(handle, "vgs -o vg_name,vg_extent_size --noheadings"); lvm2_exit(handle); return; }
unsigned long add_buffer(struct kexec_info *info, const void *buf, unsigned long bufsz, unsigned long memsz, unsigned long buf_align, unsigned long buf_min, unsigned long buf_max, int buf_end) { unsigned long base; int result; int pagesize; result = sort_segments(info); if (result < 0) { die("sort_segments failed\n"); } /* Round memsz up to a multiple of pagesize */ pagesize = getpagesize(); memsz = (memsz + (pagesize - 1)) & ~(pagesize - 1); base = locate_hole(info, memsz, buf_align, buf_min, buf_max, buf_end); if (base == ULONG_MAX) { die("locate_hole failed\n"); } add_segment(info, buf, bufsz, base, memsz); return base; }
unsigned long add_buffer_phys_virt(struct kexec_info *info, const void *buf, unsigned long bufsz, unsigned long memsz, unsigned long buf_align, unsigned long buf_min, unsigned long buf_max, int buf_end, int phys) { unsigned long base, start, end; int result; int pagesize; result = sort_segments(info); if (result < 0) { die("sort_segments failed\n"); } /* Round memsz up to a multiple of pagesize */ pagesize = getpagesize(); memsz = (memsz + (pagesize - 1)) & ~(pagesize - 1); // base = locate_hole(info, memsz, buf_align, buf_min, buf_max, buf_end); /* local_hole will find memory at end of RAM where capture kernel * is generally allocated, but in case HIGHMEM is supported then * capture kernel need not be at the end of RAM */ result = get_crashkernel_mem_range(&start, &end); if (result < 0) { die("get_crashkernel_mem_range failed"); } base = end - memsz + 1; if ((base <= start) || (base >= end)) { die("locate_hole failed\n"); } add_segment_phys_virt(info, buf, bufsz, base, memsz, phys); return base; }
/* * Load the new kernel */ static int my_load(const char *type, int fileind, int argc, char **argv, unsigned long kexec_flags) { char *kernel; char *kernel_buf; off_t kernel_size; int i = 0; int result; struct kexec_info info; int guess_only = 0; memset(&info, 0, sizeof(info)); info.segment = NULL; info.nr_segments = 0; info.entry = NULL; info.backup_start = 0; info.kexec_flags = kexec_flags; result = 0; if (argc - fileind <= 0) { fprintf(stderr, "No kernel specified\n"); usage(); return -1; } kernel = argv[fileind]; /* slurp in the input kernel */ kernel_buf = slurp_decompress_file(kernel, &kernel_size); #if 0 fprintf(stderr, "kernel: %p kernel_size: %lx\n", kernel_buf, kernel_size); #endif if (get_memory_ranges(&info.memory_range, &info.memory_ranges, info.kexec_flags) < 0) { fprintf(stderr, "Could not get memory layout\n"); return -1; } /* if a kernel type was specified, try to honor it */ if (type) { for (i = 0; i < file_types; i++) { if (strcmp(type, file_type[i].name) == 0) break; } if (i == file_types) { fprintf(stderr, "Unsupported kernel type %s\n", type); return -1; } else { /* make sure our file is really of that type */ if (file_type[i].probe(kernel_buf, kernel_size) < 0) guess_only = 1; } } if (!type || guess_only) { for (i = 0; i < file_types; i++) { if (file_type[i].probe(kernel_buf, kernel_size) >= 0) break; } if (i == file_types) { fprintf(stderr, "Cannot determine the file type " "of %s\n", kernel); return -1; } else { if (guess_only) { fprintf(stderr, "Wrong file type %s, " "file matches type %s\n", type, file_type[i].name); return -1; } } } if (file_type[i].load(argc, argv, kernel_buf, kernel_size, &info) < 0) { fprintf(stderr, "Cannot load %s\n", kernel); return -1; } /* If we are not in native mode setup an appropriate trampoline */ if (arch_compat_trampoline(&info) < 0) { return -1; } /* Verify all of the segments load to a valid location in memory */ for (i = 0; i < info.nr_segments; i++) { if (!valid_memory_segment(&info, info.segment +i)) { fprintf(stderr, "Invalid memory segment %p - %p\n", info.segment[i].mem, ((char *)info.segment[i].mem) + info.segment[i].memsz); return -1; } } /* Sort the segments and verify we don't have overlaps */ if (sort_segments(&info) < 0) { return -1; } /* if purgatory is loaded update it */ update_purgatory(&info); #if 0 fprintf(stderr, "kexec_load: entry = %p flags = %lx\n", info.entry, info.kexec_flags); print_segments(stderr, &info); #endif result = kexec_load( info.entry, info.nr_segments, info.segment, info.kexec_flags); if (result != 0) { /* The load failed, print some debugging information */ fprintf(stderr, "kexec_load failed: %s\n", strerror(errno)); fprintf(stderr, "entry = %p flags = %lx\n", info.entry, info.kexec_flags); print_segments(stderr, &info); } return result; }
segtable* add_segment (segtable* st, unspos pos1, unspos pos2, unspos length, score s, int id) { u32 newSize; size_t bytesNeeded; segment* seg, *parent; segment tempSeg; int ix, pIx; int tied, stopped; // fprintf (stderr, "add " unsposSlashSFmt " " unsposFmt " " scoreFmtSimple "; id %d\n", // pos1+1, "+", // pos2+1, ((id & rcf_rev) != 0)? "-" : "+", // length, s, id); ////////// // add the segment to the table, enlarging the table if needed, but // discarding the segment if it is low-scoring and the table has met its // coverage limit ////////// // if the table is already full and this segment scores less than the // lowest score in the table, discard it if ((st->len > 0) && (st->coverageLimit != 0) && (st->coverage >= st->coverageLimit) && (s < st->lowScore)) return st; // if there's no room for the new segment, re-allocate if (st->len >= st->size) { newSize = st->size + 100 + (st->size / 3); bytesNeeded = segtable_bytes (newSize); if (bytesNeeded > mallocLimit) goto overflow; st = (segtable*) realloc_or_die ("add_segment", st, bytesNeeded); st->size = newSize; } // add the segment, by appending it at the end seg = &st->seg[st->len++]; seg->pos1 = pos1; seg->pos2 = pos2; seg->length = length; seg->s = s; seg->id = id; seg->filter = false; seg->scoreCov = (possum) length; st->coverage += length; if ((st->len == 1) || (s < st->lowScore)) st->lowScore = s; ////////// // handle the transition between the two table states // below-the-coverage-limit: table is kept as a simple list // met-the-coverage-limit: table is kept as a proper min-heap ////////// // if this segment leaves us below the limit, we're done if ((st->coverageLimit == 0) || (st->coverage < st->coverageLimit)) return st; // if this is the first time we've reached the limit, sort the segments to // create a proper min-heap, and add the tied-score information // nota bene: if we reach here, st->coverageLimit > 0 and // st->coverage >= st->coverageLimit if (st->coverage - length < st->coverageLimit) { sort_segments (st, qSegmentsByIncreasingScore); record_tie_scores (st); #ifdef debugBinaryHeap fprintf (stderr, "\nafter sort:\n"); dump_segments (stderr, st, NULL, NULL); validate_heap (st, "after sort"); #endif // debugBinaryHeap goto prune; } ////////// // maintain the min-heap property ////////// #ifdef debugBinaryHeap //fprintf (stderr, "\nbefore percolation:\n"); //dump_segments (stderr, st, NULL, NULL); #endif // debugBinaryHeap // the rest of the list is a proper min-heap, so percolate the new segment // up the tree, while maintaining the tied-score information // nota bene: if we reach here, length >= 2 tied = false; for (ix=st->len-1 ; ix>0 ; ) { pIx = (ix-1) / 2; seg = &st->seg[ix]; parent = &st->seg[pIx]; if (seg->s >= parent->s) { tied = (seg->s == parent->s); break; } // swap this segment with its parent, and adjust old parent's tied-score // subheap tempSeg = *seg; *seg = *parent; *parent = tempSeg; record_tie_score (st, ix); ix = pIx; } record_tie_score (st, ix); // if the new segment tied an existing score, we must continue to percolate // the tied-score info up the tree if (tied) { stopped = false; for (ix=(ix-1)/2 ; ix>0 ; ix=(ix-1)/2) { if (!record_tie_score (st, ix)) { stopped = true; break; } } if (!stopped) record_tie_score (st, 0); } #ifdef debugBinaryHeap fprintf (stderr, "\nafter percolation:\n"); dump_segments (stderr, st, NULL, NULL); validate_heap (st, "after percolation"); #endif // debugBinaryHeap ////////// // remove low-scoring segments ////////// prune: // if removing the minimum scoring subheap would bring us below the // limit, no pruning is necessary if (st->coverage - st->seg[0].scoreCov < st->coverageLimit) return st; // otherwise, we must remove subheaps as long as doing so leaves us at or // above the limit while (st->coverage - st->seg[0].scoreCov >= st->coverageLimit) { s = st->seg[0].s; while (st->seg[0].s == s) { remove_root (st); #ifdef debugBinaryHeap fprintf (stderr, "\nafter a pruning:\n"); dump_segments (stderr, st, NULL, NULL); validate_heap (st, "after pruning"); #endif // debugBinaryHeap } } st->lowScore = st->seg[0].s; #ifdef debugBinaryHeap fprintf (stderr, "\nafter pruning:\n"); dump_segments (stderr, st, NULL, NULL); validate_heap (st, "after pruning"); #endif // debugBinaryHeap return st; // failure exits #define suggestions " consider using lastz_m40," \ " or setting max_malloc_index for a special build," \ " or raising scoring threshold (--hspthresh or --exact)," \ " or break your target sequence into smaller pieces" overflow: suicidef ("in add_segment()\n" "table size (%s for %s segments) exceeds allocation limit of %s;\n" suggestions, commatize(bytesNeeded), commatize(newSize), commatize(mallocLimit)); return NULL; // (doesn't get here) }
void limit_segment_table (segtable* st, unspos coverageLimit) { int ix, newLen; possum cov, newCov; score prevScore, newLow; segment* seg, *tail; segment tempSeg; st->coverageLimit = coverageLimit; // if this leaves us below the limit, we're done if ((st->coverageLimit == 0) || (st->coverage < st->coverageLimit) || (st->len < 2)) return; // otherwise, may need to reduce; begin by sorting segments by decreasing // score sort_segments (st, qSegmentsByDecreasingScore); // find the score that reduces coverage to no more than the limit newLen = st->len; newCov = st->coverage; newLow = st->lowScore; ix = st->len; seg = &st->seg[--ix]; cov = st->coverage - seg->length; prevScore = seg->s; while (ix > 0) { seg = &st->seg[--ix]; // if this is the same as the segment after it, just keep looking (we // treat segments with the same score as one unbreakable entity) if (seg->s == prevScore) { cov -= seg->length; continue; } // if removing the segments above this one would be enough to get us // within the limit, we are finished; note that what we remove will be // the segments above the (score-tied) segments above this one if (cov < st->coverageLimit) break; // potentially, we will remove the segments above this one, so record // that information and keep going newLen = ix+1; newCov = cov; newLow = seg->s; cov -= seg->length; prevScore = seg->s; } st->len = newLen; st->coverage = newCov; st->lowScore = newLow; // now make the list a proper min-heap by simply reversing its order (which // orders it by increasing score), and add the tied-score information seg = &st->seg[0]; tail = &st->seg[st->len-1]; while (seg < tail) { tempSeg = *seg; *(seg++) = *tail; *(tail--) = tempSeg; } record_tie_scores (st); }
void merge_segments (segtable* st) { u32 srcIx, dstIx; segment* srcSeg, *dstSeg; unspos pos2, end2, srcPos2, srcEnd2; sgnpos diag, srcDiag; score s, srcS; // if we have fewer than two segments, there's nothing to merge if (st->len < 2) return; // sort segments by increasing pos2 along each diagonal sort_segments (st, qSegmentsByDiag); // start first segment srcSeg = st->seg; pos2 = srcSeg->pos2; diag = diagNumber (srcSeg->pos1, pos2); end2 = pos2 + srcSeg->length; s = srcSeg->s; srcSeg++; // scan segments, merging as needed; note that any segment written to the // list is written to an earlier (or the same) index as the latest read dstIx = 0; for (srcIx=1 ; srcIx<st->len ; srcIx++,srcSeg++) { srcPos2 = srcSeg->pos2; srcDiag = diagNumber (srcSeg->pos1, srcPos2); srcEnd2 = srcPos2 + srcSeg->length; srcS = srcSeg->s; if ((srcDiag == diag) && (srcPos2 < end2)) { // merge if (srcEnd2 > end2) end2 = srcEnd2; if (srcS > s) s = srcS; continue; } // deposit the previous segment dstSeg = &st->seg[dstIx++]; dstSeg->pos1 = (unspos) (diag + pos2); dstSeg->pos2 = pos2; dstSeg->length = end2 - pos2; dstSeg->s = s; // start a new segment pos2 = srcPos2; diag = srcDiag; end2 = srcEnd2; s = srcS; } // deposit the final segment dstSeg = &st->seg[dstIx++]; dstSeg->pos1 = (unspos) (diag + pos2); dstSeg->pos2 = pos2; dstSeg->length = end2 - pos2; dstSeg->s = s; // adjust the length of the list st->len = dstIx; }
// convert logical extent from logical volume specified by lv_name, // vg_name and logical extent number (le_num) to physical extent // on specific device struct pv_info *LE_to_PE(const char *vg_name, const char *lv_name, uint64_t le_num) { struct pv_allocations pv_alloc = { .lv_name = (char *)lv_name, .vg_name = (char *)vg_name, .lv_start = le_num }; struct pv_allocations *needle; needle = bsearch(&pv_alloc, pv_segments, pv_segments_num, sizeof(struct pv_allocations), _find_segment); if (!needle) return NULL; struct pv_info *pv_info; pv_info = malloc(sizeof(struct pv_info)); if (!pv_info) { fprintf(stderr, "Out of memory\n"); exit(1); } pv_info->pv_name = strdup(needle->pv_name); if (!pv_info->pv_name) { fprintf(stderr, "Out of memory\n"); exit(1); } pv_info->start_seg = needle->pv_start + (le_num - needle->lv_start); return pv_info; } struct vg_pe_sizes { char *vg_name; uint64_t pe_size; }; struct vg_pe_sizes *vg_pe_sizes; size_t vg_pe_sizes_len; // parse output from lvm2cmd about extent sizes void parse_vgs_pe_size(int level, const char *file, int line, int dm_errno, const char *format) { // disregard debug output if (level != 4) return; char vg_name[4096], pe_size[4096]; uint64_t pe_size_bytes=0; int r; r = sscanf(format, " %4095s %4095s ", vg_name, pe_size); if (r == EOF || r != 2) { fprintf(stderr, "%s:%i Error parsing line %i: %s\n", __FILE__, __LINE__, line, format); return; } double temp; char *tail; temp = strtod(pe_size, &tail); if (temp == 0.0) { fprintf(stderr, "%s:%i Error parsing line %i: %s\n", __FILE__, __LINE__, line, format); return; } switch(tail[0]){ case 'b': case 'B': pe_size_bytes = temp; break; case 'S': pe_size_bytes = temp * 512; break; case 'k': pe_size_bytes = temp * 1024; break; case 'K': pe_size_bytes = temp * 1000; break; case 'm': pe_size_bytes = temp * 1024 * 1024; break; case 'M': pe_size_bytes = temp * 1000 * 1000; break; case 'g': pe_size_bytes = temp * 1024 * 1024 * 1024; break; case 'G': pe_size_bytes = temp * 1000 * 1000 * 1000; break; case 't': pe_size_bytes = temp * 1024 * 1024 * 1024 * 1024; break; case 'T': pe_size_bytes = temp * 1000 * 1000 * 1000 * 1000; break; case 'p': pe_size_bytes = temp * 1024 * 1024 * 1024 * 1024 * 1024; break; case 'P': pe_size_bytes = temp * 1000 * 1000 * 1000 * 1000 * 1000; break; case 'e': pe_size_bytes = temp * 1024 * 1024 * 1024 * 1024 * 1024 * 1024; break; case 'E': pe_size_bytes = temp * 1000 * 1000 * 1000 * 1000 * 1000 * 1000; break; default: pe_size_bytes = temp; /* break; */ } // save info about first volume group if (vg_pe_sizes_len == 0) { vg_pe_sizes = malloc(sizeof(struct vg_pe_sizes)); if (!vg_pe_sizes) goto vgs_failure; vg_pe_sizes[0].vg_name = strdup(vg_name); if (!vg_pe_sizes[0].vg_name) goto vgs_failure; vg_pe_sizes[0].pe_size = pe_size_bytes; vg_pe_sizes_len=1; return; } // save info about subsequent groups vg_pe_sizes = realloc(vg_pe_sizes, sizeof(struct vg_pe_sizes)* (vg_pe_sizes_len+1)); if (!vg_pe_sizes) goto vgs_failure; vg_pe_sizes[vg_pe_sizes_len].vg_name = malloc(strlen(vg_name)+1); if(!vg_pe_sizes[vg_pe_sizes_len].vg_name) goto vgs_failure; strcpy(vg_pe_sizes[vg_pe_sizes_len].vg_name, vg_name); vg_pe_sizes[vg_pe_sizes_len].pe_size = pe_size_bytes; vg_pe_sizes_len+=1; return; vgs_failure: fprintf(stderr, "Out of memory\n"); exit(1); } // return size of extents in provided volume group uint64_t get_pe_size(const char *vg_name) { for(size_t i=0; i<vg_pe_sizes_len; i++) if (!strcmp(vg_pe_sizes[i].vg_name, vg_name)) return vg_pe_sizes[i].pe_size; return 0; } // free allocated memory and objects void le_to_pe_exit(struct program_params *pp) { for(size_t i=0; i<pv_segments_num; i++){ free(pv_segments[i].pv_name); free(pv_segments[i].vg_name); free(pv_segments[i].vg_format); free(pv_segments[i].vg_attr); free(pv_segments[i].lv_name); free(pv_segments[i].pv_type); } free(pv_segments); pv_segments = NULL; pv_segments_num = 0; for(size_t i=0; i<vg_pe_sizes_len; i++) free(vg_pe_sizes[i].vg_name); free(vg_pe_sizes); vg_pe_sizes = NULL; vg_pe_sizes_len = 0; } // initialize or reload cache variables void init_le_to_pe(struct program_params *pp) { // int r; if(pv_segments) le_to_pe_exit(pp); vg_pe_sizes = NULL; vg_pe_sizes_len = 0; lvm2_log_fn(parse_pvs_segments); if (!pp->lvm2_handle) pp->lvm2_handle = lvm2_init(); lvm2_log_level(pp->lvm2_handle, 1); // r = lvm2_run(pp->lvm2_handle, "pvs --noheadings --segments -o+lv_name," "seg_start_pe,segtype --units=b"); // if (r) // fprintf(stderr, "command failed\n"); sort_segments(pv_segments, pv_segments_num); lvm2_log_fn(parse_vgs_pe_size); // r = lvm2_run(pp->lvm2_handle, "vgs -o vg_name,vg_extent_size --noheadings --units=b"); return; } // return number of free extents in PV in specified volume group // or in whole volume group if pv_name is NULL uint64_t get_free_extent_number(const char *vg_name, const char *pv_name) { if (!vg_name) return 0; uint64_t sum=0; if(pv_name) for(size_t i=0; i < pv_segments_num; i++) { if (!strcmp(pv_segments[i].vg_name, vg_name) && !strcmp(pv_segments[i].pv_name, pv_name) && !strcmp(pv_segments[i].pv_type, "free")) sum+=pv_segments[i].pv_length; } else for(size_t i=0; i < pv_segments_num; i++) if (!strcmp(pv_segments[i].vg_name, vg_name) && !strcmp(pv_segments[i].pv_type, "free")) sum+=pv_segments[i].pv_length; return sum; } struct le_info get_first_LE_info(const char *vg_name, const char *lv_name, const char *pv_name) { struct le_info ret = { .dev = NULL }; for (size_t i=0; i < pv_segments_num; i++) { if (!strcmp(pv_segments[i].vg_name, vg_name) && !strcmp(pv_segments[i].pv_name, pv_name) && !strcmp(pv_segments[i].lv_name, lv_name)) { if (ret.dev == NULL) { // save first segment info ret.le = pv_segments[i].lv_start; ret.pe = pv_segments[i].pv_start; ret.dev = pv_segments[i].pv_name; } else { if (ret.le > pv_segments[i].lv_start) { ret.le = pv_segments[i].lv_start; ret.pe = pv_segments[i].pv_start; ret.dev = pv_segments[i].pv_name; } } } } return ret; } struct le_info get_PE_allocation(const char *vg_name, const char *pv_name, uint64_t pe_num) { const char *free_str = "free"; struct le_info ret = { .dev = NULL, .lv_name = NULL }; for (size_t i=0; i < pv_segments_num; i++) { if (!strcmp(pv_segments[i].vg_name, vg_name) && !strcmp(pv_segments[i].pv_name, pv_name) && pv_segments[i].pv_start <= pe_num && pv_segments[i].pv_start + pv_segments[i].pv_length > pe_num) { ret.dev = pv_segments[i].pv_name; if (!strcmp(pv_segments[i].pv_type, free_str)) ret.lv_name = free_str; else ret.lv_name = pv_segments[i].lv_name; uint64_t diff = pe_num - pv_segments[i].pv_start; ret.le = pv_segments[i].lv_start + diff; ret.pe = pv_segments[i].pv_start + diff; return ret; } } return ret; } // return used number of extents by LV on provided PV uint64_t get_used_space_on_pv(const char *vg_name, const char *lv_name, const char *pv_name) { assert(vg_name); assert(lv_name); assert(pv_name); uint64_t sum = 0; for(size_t i=0; i < pv_segments_num; i++) { if(!strcmp(pv_segments[i].lv_name, lv_name) && !strcmp(pv_segments[i].vg_name, vg_name) && !strcmp(pv_segments[i].pv_name, pv_name)) { sum += pv_segments[i].pv_length; } } return sum; }