Ejemplo n.º 1
0
// 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;
}
Ejemplo n.º 2
0
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;
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
0
/*
 *	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;
}
Ejemplo n.º 5
0
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)
	}
Ejemplo n.º 6
0
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);
	}
Ejemplo n.º 7
0
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;
	}
Ejemplo n.º 8
0
// 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;
}