Skip to content
Browse files

Merge branch 'master' of https://github.com/tomato42/lvmts

  • Loading branch information...
2 parents b576133 + 9e1f702 commit 3188e87de117f52147bdd9b2794c84f2d998c04b @tomato42 tomato42 committed
Showing with 282 additions and 75 deletions.
  1. +5 −0 Makefile
  2. +8 −9 README
  3. +35 −8 lvmls.c
  4. +234 −58 lvmtsd.c
View
5 Makefile
@@ -0,0 +1,5 @@
+lvmtsd: lvmtsd.c lvmls.o
+ gcc -std=gnu99 -Wall lvmtsd.c lvmls.o -llvm2cmd -o lvmtsd -Os -ggdb
+
+lvmls.o: lvmls.c
+ gcc -std=gnu99 -Wall -c lvmls.c -Os -ggdb
View
17 README
@@ -1,21 +1,20 @@
Application for monitoring LVM volumes and moving blocks based on usage to
higher or lower tier storage.
-ATM quite rudimentary, more of a helper script.
-
to compile:
-gcc -std=gnu99 -Wall lvmtsd.c -o lvmtsd
+make
lvmtsd is used like this:
-btrace -t -a complete /dev/vol-grp00/log-vol00 | awk ' {print $7, $8/(2*4*1024), ($8+$10)/(2*4*1024)} ' | ./lvmtsd
+./lvmtsd /dev/vol-grp00/log-vol00
It will dump collected data (including 200 most active, most written and most
read blocks in format ready to use with pvmove) when the pipe is broken (killall
-blktrace).
+blktrace) and every 5 minutes.
+there is one optional parameter, the number of extents written in status
+information:
+./lvmtsd <lv-dev-path> [<number-of-extents>]
+for example:
-To compile lvmls:
-gcc -llvm2cmd lvmls.c -o lvmls -std=gnu99 -Wall
+./lvmtsd /dev/VolGrp00/LogVol01 400
-lvmls is test code for parsing pvs output, extracting PV extent to LV extent
-allocations.
View
43 lvmls.c
@@ -20,6 +20,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include "lvmls.h"
// information about continuous extent allocations
struct pv_allocations {
@@ -181,11 +182,6 @@ void parse_pvs_segments(int level, const char *file, int line,
return;
}
-struct pv_info {
- char *pv_name;
- uint64_t start_seg;
-};
-
// convert logical extent from logical volume specified by lv_name,
// vg_name and logical extent number (le_num) to physical extent
// on specific device
@@ -230,8 +226,8 @@ struct vg_pe_sizes {
uint64_t pe_size;
};
-struct vg_pe_sizes *vg_pe_sizes = NULL;
-size_t vg_pe_sizes_len = 0;
+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,
@@ -334,9 +330,10 @@ void parse_vgs_pe_size(int level, const char *file, int line,
// return size of extents in provided volume group
uint64_t get_pe_size(char *vg_name)
{
- for(size_t i=0; i<vg_pe_sizes_len; i++)
+ 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;
}
@@ -371,6 +368,9 @@ void init_le_to_pe()
if(pv_segments)
le_to_pe_exit();
+
+ vg_pe_sizes = NULL;
+ vg_pe_sizes_len = 0;
lvm2_log_fn(parse_pvs_segments);
@@ -394,6 +394,32 @@ void init_le_to_pe()
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(char *vg_name, 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;
+}
+
+/*
int main(int argc, char **argv)
{
init_le_to_pe();
@@ -425,3 +451,4 @@ int main(int argc, char **argv)
return 0;
}
+*/
View
292 lvmtsd.c
@@ -16,23 +16,36 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
+#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
+#include "lvmls.h"
/// number of extents in 2TiB LV
-#define EXTENTS 524288
+//#define EXTENTS 524288
+#define EXTENTS extents
+
+uint64_t extents=1024;
+
+int ext_to_print = 200;
+
+// granularity of samples (5 minutes)
+#define GRANULARITY 300
#define READ 0x1
#define WRITE 0x2
#define HISTORY_LEN 20
+char *vg_name=NULL;
+char *lv_name=NULL;
+
struct extent_info_t {
- long long reads[HISTORY_LEN];
- long long writes[HISTORY_LEN];
+ uint64_t reads[HISTORY_LEN];
+ uint64_t writes[HISTORY_LEN];
};
struct extent_score_t{
@@ -41,28 +54,25 @@ struct extent_score_t{
long long write_score;
};
-int add_io(struct extent_info_t *ei, int type)
+int add_io(struct extent_info_t *ei, uint64_t time, int type)
{
long long result;
long long now;
- long long one_hour;
-
- one_hour=3600;
- now = time(NULL);
+ now = time;
if(type == READ) {
result = now - ei->reads[0];
- {//if(timercmp(&result, &one_hour, >)) {
- memmove(&ei->reads[1], &ei->reads[0], sizeof(struct timeval)*(HISTORY_LEN-1));
+ if(result > GRANULARITY) {
+ memmove(&ei->reads[1], &ei->reads[0], sizeof(uint64_t)*(HISTORY_LEN-1));
ei->reads[0] = now;
}
}
if(type == WRITE) {
result = now - ei->writes[0];
- {//if(timercmp(&result, &one_hour, >)) {
- memmove(&ei->writes[1], &ei->writes[0], sizeof(struct timeval)*(HISTORY_LEN-1));
+ if(result > GRANULARITY) {
+ memmove(&ei->writes[1], &ei->writes[0], sizeof(uint64_t)*(HISTORY_LEN-1));
ei->writes[0] = now;
}
}
@@ -99,7 +109,7 @@ unsigned int get_read_score(struct extent_info_t *ei) {
continue;
}
extent_time = ei->reads[i];
- score -= (now_time - extent_time) / 3600;
+ score -= (now_time - extent_time) / GRANULARITY;
}
return score;
@@ -122,7 +132,7 @@ unsigned int get_write_score(struct extent_info_t *ei) {
continue;
}
extent_time = ei->writes[i];
- score -= (now_time - extent_time) / 3600;
+ score -= (now_time - extent_time) / GRANULARITY;
}
return score;
@@ -199,9 +209,216 @@ struct extent_score_t* convert_extent_info_to_extent_score(struct extent_info_t
return es;
}
+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();
+}
+
+struct extent_info_t* read_stdin(uint64_t start_time, struct extent_info_t *extent_info)
+{
+ char in_buf[8192]={0};
+ char blk_dev_num[4096]={0};
+ int cpu_id=0;
+ uint64_t seq_num=0;
+ double time_stamp=0;
+ int proc_id=0;
+ char action_id[8]={0};
+ char rwbs[8]={0};
+ uint64_t offset=0;
+ char plus_sgn[8]={0};
+ uint64_t len=0;
+ char err_val[16];
+ // number of sectors in extent
+ init_le_to_pe();
+ uint64_t sec_in_ext = get_pe_size(vg_name);
+
+ if (sec_in_ext == 0) {
+ fprintf(stderr, "No volume group named %s\n", vg_name);
+ exit(1);
+ }
+
+ FILE *btrace;
+
+ // offset in extents
+ uint64_t extent_num=0;
+ int r;
+
+ uint64_t last_print = 0;
+
+ char cmd[8192]={0};
+
+ sprintf(cmd, "btrace -t -a complete /dev/%s/%s", vg_name, lv_name);
+
+ // XXX UGLY!!
+ btrace = popen(cmd, "r");
+ if(!btrace){
+ fprintf(stderr, "can't invoke btrace");
+ exit(1);
+ }
+
+ while (fgets(in_buf, 8191, btrace)){
+ r = sscanf(in_buf,
+ "%4095s %100i %" SCNu64 " %64lf %64i %7s %7s %" SCNu64 " %4s "
+ "%" SCNu64 " %15s",
+ blk_dev_num, &cpu_id, &seq_num, &time_stamp, &proc_id,
+ action_id, rwbs, &offset, plus_sgn, &len, err_val);
+
+ // ignore all non Complete events
+ if (strcmp(action_id,"C"))
+ continue;
+
+ // print current stats every 5 minutes
+ if (last_print+60*5<time_stamp) {
+ print_extents(extent_info);
+ last_print = time_stamp;
+ }
+
+ // round up
+ extent_num=(offset+(sec_in_ext-1))/sec_in_ext;
+
+ if (extents<=extent_num) {
+ extent_info = realloc(extent_info,
+ sizeof(struct extent_info_t)*(extent_num+100));
+ if (!extent_info){
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+
+ memset(&extent_info[extents], 0,
+ (extent_num+100-extents)*sizeof(struct extent_info_t));
+
+ extents=extent_num+100;
+ }
+
+ if (rwbs[0] == 'R')
+ add_io(&extent_info[(size_t)extent_num],
+ start_time + time_stamp, READ);
+
+ if (rwbs[0] == 'W')
+ add_io(&extent_info[(size_t)extent_num],
+ start_time + time_stamp, WRITE);
+ }
+
+ pclose(btrace);
+
+ return extent_info;
+}
+
+void parse_lv_name(char *path)
+{
+ // XXX UGLY! UNSAFE!
+ static char copy[8192];
+ char *last, *second_to_last;
+ strcpy(copy, path);
+ last = strrchr(copy, '/');
+ if (last == NULL) {
+ fprintf(stderr, "specify path as /dev/<vg-name>/<lv-name>\n");
+ exit(1);
+ }
+ *last=0;
+ last++;
+ lv_name = last;
+ second_to_last = strrchr(copy, '/');
+ if (second_to_last == NULL) {
+ fprintf(stderr, "specify path as /dev/<vg-name>/<lv-name>\n");
+ exit(1);
+ }
+ vg_name = second_to_last+1;
+}
+
int main(int argc, char **argv)
{
struct extent_info_t *extent_info;
+
+ if(argc==1) {
+ fprintf(stderr, "Usage: %s <dev name> [<number of extents to print>]\n", argv[0]);
+ exit(1);
+ }
+
+ parse_lv_name(argv[1]);
+
+ if(argc>2)
+ ext_to_print = atoi(argv[2]);
extent_info = calloc(sizeof(struct extent_info_t), EXTENTS);
if (!extent_info) {
@@ -209,28 +426,10 @@ int main(int argc, char **argv)
exit(1);
}
- printf("sizeof: %li\n", sizeof(struct extent_info_t));
-
- char type[10];
- float begin, end;
- int ret;
- while (1){
- ret = fscanf(stdin, "%s %f %f\n", type, &begin, &end);
- if (ret == EOF)
- goto clean_up;
-
- if (type[0] == 'R') {
-// printf("read: %lu\n", (size_t)begin);
- add_io(&extent_info[(size_t)begin], READ);
- }
+ uint64_t now = time(NULL);
- if (type[0] == 'W') {
-// printf("write: %lu\n", (size_t)begin);
- add_io(&extent_info[(size_t)begin], WRITE);
- }
- }
+ extent_info = read_stdin(now, extent_info);
-clean_up:
printf("individual extent score:\n");
for(size_t i=0; i<EXTENTS; i++)
if(extent_info[i].reads[0] || extent_info[i].writes[0]) {
@@ -238,31 +437,8 @@ int main(int argc, char **argv)
// print_io(&extent_info[i]);
}
- struct extent_score_t *extent_score;
-
- extent_score = convert_extent_info_to_extent_score(extent_info);
+ print_extents(extent_info);
- qsort(extent_score, EXTENTS, sizeof(struct extent_score_t), extent_cmp);
-
- printf("\n\n");
- printf("200 most active extents: (from least to most)\n");
- for(int i=EXTENTS-200; i<EXTENTS; i++)
- printf("%lu:", extent_score[i].offset);
- printf("\n\n");
-
- qsort(extent_score, EXTENTS, sizeof(struct extent_score_t), extent_read_cmp);
-
- printf("200 most read extents (from least to most):\n");
- for(int i=EXTENTS-200; i<EXTENTS; i++)
- printf("%lu:", extent_score[i].offset);
- printf("\n\n");
-
- qsort(extent_score, EXTENTS, sizeof(struct extent_score_t), extent_write_cmp);
- printf("200 most write extents (from least to most):\n");
- for(int i=EXTENTS-200; i<EXTENTS; i++)
- printf("%lu:", extent_score[i].offset);
-
- free(extent_score);
free(extent_info);
return 0;
}

0 comments on commit 3188e87

Please sign in to comment.
Something went wrong with that request. Please try again.