// 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; }
/** destroy program_params */ void free_program_params(struct program_params *pp) { if(!pp) return; if(pp->conf_file_path) free(pp->conf_file_path); cfg_free(pp->cfg); if (pp->lvm2_handle) lvm2_exit(pp->lvm2_handle); le_to_pe_exit(pp); free(pp); }
int main(int argc, char **argv) { int ret = 0; int n; double mean_lifetime = 3 * 24 * 60 * 60; if (parse_arguments(argc, argv)) return 1; struct activity_stats *as = NULL; if (file == NULL) { fprintf(stderr, "No file name provided\n"); usage(); return 1; } if (!print_le && (!lv_name || !vg_name)) { fprintf(stderr, "You must ask for logical extents or provide volume" " group and logical volume name.\n"); usage(); return 1; } struct program_params pp = { .lvm2_handle = NULL }; if (!print_le) { init_le_to_pe(&pp); } n = read_activity_stats(&as, file); assert(!n); struct block_scores *bs = NULL; if(get_max) get_best_blocks_with_max_score(as, &bs, blocks, read_mult, write_mult, mean_lifetime, max_score); else get_best_blocks(as, &bs, blocks, read_mult, write_mult, mean_lifetime); if (pvmove_output) { if (print_le) { printf("%llu", bs[0].offset); for (int i=1; i< blocks; i++) { printf(":%llu", bs[i].offset); } printf("\n"); } else { struct pv_info *pvi; struct pv_info *pvn; pvi = LE_to_PE(vg_name, lv_name, bs[0].offset); printf("%s:%llu", pvi->pv_name, pvi->start_seg); for (int i=1; i<blocks; i++) { pvn = LE_to_PE(vg_name, lv_name, bs[i].offset); if (!pvn) continue; if (!strcmp(pvi->pv_name, pvn->pv_name)) { printf(":%llu", pvn->start_seg); pv_info_free(pvn); continue; } else { printf("\n%s:%llu", pvn->pv_name, pvn->start_seg); pv_info_free(pvi); pvi = pvn; continue; } } printf("\n"); pv_info_free(pvi); } } else { if (!print_le) { fprintf(stderr, "Unsupported combination of parameters" " (add --pvmove or --LE)\n"); } else print_block_scores(bs, blocks); } free(bs); bs = NULL; //dump_activity_stats(as); destroy_activity_stats(as); as = NULL; if (!print_le) { le_to_pe_exit(&pp); } return ret; }
void print_extents(struct extent_info_t *extent_info) { struct extent_score_t *extent_score; extent_score = convert_extent_info_to_extent_score(extent_info); qsort(extent_score, EXTENTS, sizeof(struct extent_score_t), extent_cmp); init_le_to_pe(); time_t res; res = time(NULL); printf("\n\n"); printf("%s", asctime(localtime(&res))); printf("%i most active physical extents: (from most to least)\n", ext_to_print); for(int i=EXTENTS-1, num=0; num<ext_to_print; i--) { struct pv_info *ret; if(!extent_score[i].read_score && !extent_score[i].write_score) break; ret = LE_to_PE(vg_name, lv_name, extent_score[i].offset); if(num%10==9) printf("%lu\n", ret->start_seg); else printf("%lu:", ret->start_seg); num++; free(ret->pv_name); free(ret); } printf("\n\n"); qsort(extent_score, EXTENTS, sizeof(struct extent_score_t), extent_read_cmp); printf("%i most read extents (from most to least):\n", ext_to_print); for(int i=EXTENTS-1, num=0; num<ext_to_print; i--) { struct pv_info *ret; if(!extent_score[i].read_score) break; ret = LE_to_PE(vg_name, lv_name, extent_score[i].offset); if(num%10==9) printf("%lu\n", ret->start_seg); else printf("%lu:", ret->start_seg); num++; free(ret->pv_name); free(ret); } printf("\n\n"); qsort(extent_score, EXTENTS, sizeof(struct extent_score_t), extent_write_cmp); printf("%i most write extents (from most to least):\n", ext_to_print); for(int i=EXTENTS-1, num=0; num<ext_to_print; i--) { struct pv_info *ret; if(!extent_score[i].write_score) break; ret = LE_to_PE(vg_name, lv_name, extent_score[i].offset); if(num%10==9) printf("%lu\n", ret->start_seg); else printf("%lu:", ret->start_seg); num++; free(ret->pv_name); free(ret); } free(extent_score); le_to_pe_exit(); }
int main(int argc, char **argv) { if (argc != 4) { fprintf(stderr, "Tool to defragment LogicalVolume on selected PhysicalVolume\n"); fprintf(stderr, "Usage: lvmdefrag VolumeGroup LogicalVolume PhysicalVolume\n"); return EXIT_FAILURE; } char *vg_name = argv[1]; char *lv_name = argv[2]; char *pv_name = argv[3]; struct program_params pp = { .lvm2_handle = NULL }; init_le_to_pe(&pp); struct le_info first_le; first_le = get_first_LE_info(vg_name, lv_name, pv_name); 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)) { long int optimal_pos; optimal_pos = first_le.pe + pv_segments[i].lv_start - first_le.le; if (pv_segments[i].pv_start == optimal_pos) continue; struct le_info optimal; long int move_extent = 0; for (long int j=optimal_pos; j < optimal_pos + pv_segments[i].pv_length; j++) { optimal = get_PE_allocation(vg_name, pv_name, j); if (optimal.dev == NULL) { printf("# Optimal position for LE %li-%li on %s is after end of " "the device\n", pv_segments[i].lv_start, pv_segments[i].lv_start + pv_segments[i].pv_length, pv_name); break; } else if (strcmp(optimal.lv_name, "free")) { printf("# Optimal position for LE %li-%li is used by %s LE %li\n", pv_segments[i].lv_start, pv_segments[i].lv_start + pv_segments[i].pv_length, optimal.lv_name, optimal.le); break; } move_extent++; } if (move_extent) { printf("pvmove -i1 --alloc anywhere %s:%li-%li %s:%li-%li # LE %li (size: %li)\n", pv_name, pv_segments[i].pv_start, pv_segments[i].pv_start + move_extent - 1, pv_name, optimal_pos, optimal_pos + move_extent - 1, pv_segments[i].lv_start, move_extent); } } le_to_pe_exit(&pp); lvm2_exit(pp.lvm2_handle); return EXIT_SUCCESS; }
int main(int argc, char **argv) { struct program_params pp = { .lvm2_handle = NULL }; init_le_to_pe(&pp); if (argc != 4) { printf("Usage: %s VolumeGroupName LogicalVolumeName" " LogicalVolumeExtent\n", argv[0]); le_to_pe_exit(&pp); return 1; } for(int i=0; i < pv_segments_num; i++) if(!strcmp(pv_segments[i].lv_name, argv[1])) printf("%s %li-%li (%li-%li)\n", pv_segments[i].pv_name, pv_segments[i].pv_start, pv_segments[i].pv_start+pv_segments[i].pv_length, pv_segments[i].lv_start, pv_segments[i].lv_start+pv_segments[i].pv_length); if (argc <= 2) return 0; struct pv_info *pv_info; pv_info = LE_to_PE(argv[1], argv[2], atoi(argv[3])); if (pv_info) printf("LE no %i of %s-%s is at: %s:%li\n", atoi(argv[3]), argv[1], argv[2], pv_info->pv_name, pv_info->start_seg); else printf("no LE found\n"); printf("vg: %s, extent size: %lu bytes\n", argv[1], get_pe_size(argv[1])); long int free_extents = get_free_extent_number(argv[1], pv_info->pv_name); printf("vg: %s, pv: %s, free space: %lue (%luB)\n", argv[1], pv_info->pv_name, free_extents, free_extents * get_pe_size(argv[1])); long int used_extents = get_used_space_on_pv(argv[1], argv[2], pv_info->pv_name); printf("Space used by lv %s on pv %s: %lue (%luB)\n", argv[2], pv_info->pv_name, used_extents, used_extents * get_pe_size(argv[1])); struct le_info le_inf; le_inf = get_first_LE_info(argv[1], argv[2], pv_info->pv_name); printf("First LE on %s is %li at PE %li\n", le_inf.dev, le_inf.le, le_inf.pe); long int optimal_pe = le_inf.pe + atoi(argv[3]) - le_inf.le; printf("Optimal position for LE %i is at %s:%li\n", atoi(argv[3]), le_inf.dev, optimal_pe); printf("%s:%li is ", le_inf.dev, optimal_pe); if (optimal_pe == pv_info->start_seg) { printf("allocated correctly\n"); } else { struct le_info optimal; optimal = get_PE_allocation(argv[1], pv_info->pv_name, optimal_pe); if (optimal.dev == NULL) printf("after the end of the device\n"); else if (!strcmp(optimal.lv_name, "free")) printf("free\n"); else printf("allocated to %s, LE: %li\n", optimal.lv_name, optimal.le); } pv_info_free(pv_info); le_to_pe_exit(&pp); lvm2_exit(pp.lvm2_handle); return 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; }