Whamcloud - gitweb
LU-13827 utils: ofd_access_batch to print top hot files 29/39529/12
authorAlex Zhuravlev <bzzz@whamcloud.com>
Mon, 15 Jun 2020 08:47:44 +0000 (11:47 +0300)
committerOleg Drokin <green@whamcloud.com>
Thu, 10 Sep 2020 15:00:36 +0000 (15:00 +0000)
with -F option one can specify fraction (in %%) of hot
files to be printed.

Signed-off-by: Alex Zhuravlev <bzzz@whamcloud.com>
Change-Id: I131c47bb2252da88c9afce757c5264b8d2011d1a
Reviewed-on: https://review.whamcloud.com/39529
Reviewed-by: John L. Hammond <jhammond@whamcloud.com>
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: Gu Zheng <gzheng@ddn.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
lustre/utils/Makefile.am
lustre/utils/ofd_access_batch.c
lustre/utils/ofd_access_batch.h
lustre/utils/ofd_access_log_reader.c

index 1f769b8..22cc532 100644 (file)
@@ -140,6 +140,7 @@ ofd_access_log_reader_SOURCES = \
        ofd_access_batch.c \
        ofd_access_batch.h \
        ofd_access_log_reader.c
+ofd_access_log_reader_LDADD := -lpthread
 
 if UTILS
 
index aebc940..f02932b 100644 (file)
  *
  * Access log entry batching for ofd_access_log_reader.
  */
+#include <stdlib.h>
 #include <stdbool.h>
 #include <stddef.h>
 #include <assert.h>
 #include <malloc.h>
+#include <unistd.h>
+#include <pthread.h>
 #include <linux/lustre/lustre_access_log.h>
 #include <linux/lustre/lustre_fid.h>
 #include <linux/lustre/lustre_idl.h>
 #include "ofd_access_batch.h"
 
 /* XXX Weird param order to be consistent with list_replace_init(). */
-static inline void hlist_replace_init(struct hlist_node *old_node,
-                               struct hlist_node *new_node)
+static inline void list_replace_init(struct list_head *old_node,
+                               struct list_head *new_node)
 {
-       hlist_add_before(new_node, old_node);
-       hlist_del_init(old_node);
+       list_add(new_node, old_node);
+       list_del_init(old_node);
 }
 
 struct fid_hash_node {
-       struct hlist_node fhn_node;
+       struct list_head fhn_node;
        struct lu_fid fhn_fid;
 };
 
@@ -118,45 +121,44 @@ static unsigned long fid_hash(const struct lu_fid *f, unsigned int shift)
 
 static void fhn_init(struct fid_hash_node *fhn, const struct lu_fid *fid)
 {
-       INIT_HLIST_NODE(&fhn->fhn_node);
+       INIT_LIST_HEAD(&fhn->fhn_node);
        fhn->fhn_fid = *fid;
 }
 
-static bool fhn_is_hashed(const struct fid_hash_node *fhn)
+static bool fhn_is_hashed(struct fid_hash_node *fhn)
 {
-       return !hlist_unhashed(&fhn->fhn_node);
+       return !list_empty(&fhn->fhn_node);
 }
 
 static void fhn_del_init(struct fid_hash_node *fhn)
 {
        if (fhn_is_hashed(fhn))
-               hlist_del_init(&fhn->fhn_node);
+               list_del_init(&fhn->fhn_node);
 }
 
 static inline void fhn_replace_init(struct fid_hash_node *old_fhn,
                                struct fid_hash_node *new_fhn)
 {
-       hlist_add_before(&new_fhn->fhn_node, &old_fhn->fhn_node);
-       hlist_del_init(&old_fhn->fhn_node);
+       list_add(&new_fhn->fhn_node, &old_fhn->fhn_node);
+       list_del_init(&old_fhn->fhn_node);
 }
 
-void fid_hash_add(struct hlist_head *head, unsigned int shift,
+void fid_hash_add(struct list_head *head, unsigned int shift,
                struct fid_hash_node *fhn)
 {
        assert(!fhn_is_hashed(fhn));
 
-       hlist_add_head(&fhn->fhn_node, &head[fid_hash(&fhn->fhn_fid, shift)]);
+       list_add(&fhn->fhn_node, &head[fid_hash(&fhn->fhn_fid, shift)]);
 }
 
 struct fid_hash_node *
-fid_hash_find(struct hlist_head *head, unsigned int shift, const struct lu_fid *fid)
+fid_hash_find(struct list_head *head, unsigned int shift, const struct lu_fid *fid)
 {
-       struct hlist_head *hash_list;
-       struct hlist_node *node, *next;
-       struct fid_hash_node *fhn;
+       struct list_head *hash_list;
+       struct fid_hash_node *fhn, *next;
 
        hash_list = &head[fid_hash(fid, shift)];
-       hlist_for_each_entry_safe(fhn, node, next, hash_list, fhn_node) {
+       list_for_each_entry_safe(fhn, next, hash_list, fhn_node) {
                assert(fhn_is_hashed(fhn));
 
                if (fid_eq(fid, &fhn->fhn_fid))
@@ -167,28 +169,27 @@ fid_hash_find(struct hlist_head *head, unsigned int shift, const struct lu_fid *
 }
 
 struct fid_hash_node *
-fid_hash_insert(struct hlist_head *head, unsigned int shift, struct fid_hash_node *new_fhn)
+fid_hash_insert(struct list_head *head, unsigned int shift, struct fid_hash_node *new_fhn)
 {
-       struct hlist_head *list;
-       struct hlist_node *node, *next;
-       struct fid_hash_node *old_fhn;
+       struct list_head *list;
+       struct fid_hash_node *old_fhn, *next;
 
        list = &head[fid_hash(&new_fhn->fhn_fid, shift)];
-       hlist_for_each_entry_safe(old_fhn, node, next, list, fhn_node) {
+       list_for_each_entry_safe(old_fhn, next, list, fhn_node) {
                assert(fhn_is_hashed(old_fhn));
 
                if (fid_eq(&old_fhn->fhn_fid, &new_fhn->fhn_fid))
                        return old_fhn;
        }
 
-       hlist_add_head(&new_fhn->fhn_node, list);
+       list_add(&new_fhn->fhn_node, list);
 
        return new_fhn;
 }
 
-int fid_hash_init(struct hlist_head **phead, unsigned int *pshift, unsigned int shift)
+int fid_hash_init(struct list_head **phead, unsigned int *pshift, unsigned int shift)
 {
-       struct hlist_head *new_head;
+       struct list_head *new_head;
        unsigned int i;
 
        new_head = malloc(sizeof(*new_head) << shift);
@@ -196,7 +197,7 @@ int fid_hash_init(struct hlist_head **phead, unsigned int *pshift, unsigned int
                return -1;
 
        for (i = 0; i < (1 << shift); i++)
-               INIT_HLIST_HEAD(&new_head[i]);
+               INIT_LIST_HEAD(&new_head[i]);
 
        *phead = new_head;
        *pshift = shift;
@@ -204,9 +205,9 @@ int fid_hash_init(struct hlist_head **phead, unsigned int *pshift, unsigned int
        return 0;
 }
 
-int fid_hash_resize(struct hlist_head **phead, unsigned int *pshift, unsigned int new_shift)
+int fid_hash_resize(struct list_head **phead, unsigned int *pshift, unsigned int new_shift)
 {
-       struct hlist_head *new_head;
+       struct list_head *new_head;
        unsigned int i;
        int rc;
 
@@ -218,11 +219,10 @@ int fid_hash_resize(struct hlist_head **phead, unsigned int *pshift, unsigned in
                return rc;
 
        for (i = 0; i < (1 << *pshift); i++) {
-               struct hlist_head *list = &(*phead)[i];
-               struct hlist_node *node, *next;
-               struct fid_hash_node *fhn;
+               struct list_head *list = &(*phead)[i];
+               struct fid_hash_node *fhn, *next;
 
-               hlist_for_each_entry_safe(fhn, node, next, list, fhn_node) {
+               list_for_each_entry_safe(fhn, next, list, fhn_node) {
                        fhn_del_init(fhn);
                        fid_hash_add(new_head, new_shift, fhn);
                }
@@ -258,7 +258,7 @@ enum {
 };
 
 struct alr_batch {
-       struct hlist_head *alrb_hash;
+       struct list_head *alrb_hash;
        unsigned int alrb_hash_shift;
        unsigned int alrb_count;
 };
@@ -323,52 +323,132 @@ out:
        return rc;
 }
 
-/* Print, clear, and resize the batch. */
-int alr_batch_print(struct alr_batch *alrb, FILE *file)
+int sort_compare(const void *a1, const void *a2)
 {
-       unsigned int i;
-       unsigned int new_hash_shift;
-       int rc = 0;
-
-       if (alrb == NULL)
-               return 0;
+       int l = *(const int*)a1;
+       int r = *(const int *)a2;
+       if (l > r) return -1;
+       if (l < r) return  1;
+       return 0;
+}
 
-       for (i = 0; i < (1 << alrb->alrb_hash_shift); i++) {
-               struct hlist_head *list = &alrb->alrb_hash[i];
-               struct hlist_node *node, *next;
-               struct alr_entry *alre;
+static void alre_printf(FILE *f, struct alr_entry *alre, int d)
+{
+       fprintf(f, "%s "DFID" %lld %llu %llu %llu %llu %llu %c\n",
+               alre->alre_obd_name,
+               PFID(&alre->alre_fid_hash_node.fhn_fid),
+               (long long)alre->alre_time[d],
+               (unsigned long long)alre->alre_begin[d],
+               (unsigned long long)alre->alre_end[d],
+               (unsigned long long)alre->alre_size[d],
+               (unsigned long long)alre->alre_segment_count[d],
+               (unsigned long long)alre->alre_count[d],
+               (d == ALR_READ) ? 'r' : 'w');
+}
 
-               hlist_for_each_entry_safe(alre, node, next, list,
-                                       alre_fid_hash_node.fhn_node) {
-                       unsigned int d;
+struct alr_thread_arg {
+       struct list_head list;
+       int fraction;
+       FILE *file;
+};
 
+void *alr_sort_and_print_thread(void *arg)
+{
+       struct alr_entry *alre, *next;
+       struct alr_thread_arg *aa = arg;
+       struct list_head *tmp = &aa->list;
+       int *sa, d, i, nr = 0;
+       unsigned long cut;
+
+       list_for_each_entry(alre, tmp, alre_fid_hash_node.fhn_node) {
+               if (alre->alre_count[0] > 0)
+                       nr++;
+               if (alre->alre_count[1] > 0)
+                       nr++;
+       }
+       if (nr) {
+               sa = calloc(sizeof(int), nr);
+               if (!sa) {
+                       fprintf(stderr, "cannot allocate memory for sorting\n");
+                       exit(1);
+               }
+               i = 0;
+               list_for_each_entry(alre, tmp, alre_fid_hash_node.fhn_node) {
+                       if (alre->alre_count[0] > 0)
+                               sa[i++] = alre->alre_count[0];
+                       if (alre->alre_count[1] > 0)
+                               sa[i++] = alre->alre_count[1];
+               }
+               qsort(sa, nr, sizeof(int), sort_compare);
+               i = nr * aa->fraction / 100;
+               if (i > 0)
+                       i--;
+               cut = sa[i];
+               if (cut < 1)
+                       cut = 1;
+               free(sa);
+
+               /* there might be lots of items at @cut, but we want to limit total
+                * output. so the first loop dumps all items > @cut and the second
+                * loop dumps items=@cut so that total number (@i) is not exceeeded.
+                * XXX: possible optimization - move items=@cut to another list, so
+                * that 2nd pass takes < O(n) */
+               list_for_each_entry_safe(alre, next, tmp, alre_fid_hash_node.fhn_node) {
                        for (d = 0; d < 2; d++) {
-                               int rc2;
-
-                               if (alre->alre_count[d] == 0)
+                               if (alre->alre_count[d] <= cut)
                                        continue;
-
-                               /* stdio stream error state is sticky. */
-                               rc2 = fprintf(file,
-                                       "%s "DFID" %lld %llu %llu %llu %llu %llu %c\n",
-                                       alre->alre_obd_name,
-                                       PFID(&alre->alre_fid_hash_node.fhn_fid),
-                                       (long long)alre->alre_time[d],
-                                       (unsigned long long)alre->alre_begin[d],
-                                       (unsigned long long)alre->alre_end[d],
-                                       (unsigned long long)alre->alre_size[d],
-                                       (unsigned long long)alre->alre_segment_count[d],
-                                       (unsigned long long)alre->alre_count[d],
-                                       (d == ALR_READ) ? 'r' : 'w');
-                               if (rc2 < 0)
-                                       rc = rc2;
+                               alre_printf(aa->file, alre, d);
+                               i--;
+                       }
+               }
+               list_for_each_entry_safe(alre, next, tmp, alre_fid_hash_node.fhn_node) {
+                       for (d = 0; d < 2 && i > 0; d++) {
+                               if (alre->alre_count[d] != cut)
+                                       continue;
+                               alre_printf(aa->file, alre, d);
+                               i--;
                        }
-
                        alre_del_init(alre);
                        free(alre);
                }
        }
 
+       free(aa);
+       pthread_exit((void *)0);
+
+       return 0;
+}
+
+/* Print, clear, and resize the batch. */
+int alr_batch_print(struct alr_batch *alrb, FILE *file, int fraction)
+{
+       unsigned int new_hash_shift;
+       struct alr_thread_arg *aa;
+       pthread_t pid;
+       int i, rc;
+
+       if (alrb == NULL)
+               return 0;
+
+       aa = malloc(sizeof(*aa));
+       if (!aa)
+               return -ENOMEM;
+
+       /* move all collected items to the temp list */
+       INIT_LIST_HEAD(&aa->list);
+       for (i = 0; i < (1 << alrb->alrb_hash_shift); i++) {
+               if (list_empty(&alrb->alrb_hash[i]))
+                       continue;
+               list_splice(&alrb->alrb_hash[i], &aa->list);
+               INIT_LIST_HEAD(&alrb->alrb_hash[i]);
+       }
+       aa->file = file;
+       aa->fraction = fraction;
+
+       /* as sorting may take time and we don't want to lose access
+        * records we better do sorting and printing in a different thread */
+       rc = pthread_create(&pid, NULL, alr_sort_and_print_thread, aa);
+
        /* Resize hash based on previous count. */
        new_hash_shift = alrb->alrb_hash_shift;
 
@@ -413,11 +493,10 @@ void alr_batch_destroy(struct alr_batch *alrb)
                return;
 
        for (i = 0; i < (1 << alrb->alrb_hash_shift); i++) {
-               struct hlist_head *list = &alrb->alrb_hash[i];
-               struct hlist_node *node, *next;
-               struct alr_entry *alre;
+               struct list_head *list = &alrb->alrb_hash[i];
+               struct alr_entry *alre, *next;
 
-               hlist_for_each_entry_safe(alre, node, next, list, alre_fid_hash_node.fhn_node) {
+               list_for_each_entry_safe(alre, next, list, alre_fid_hash_node.fhn_node) {
                        alre_del_init(alre);
                        free(alre);
                }
index e06278f..7b874ee 100644 (file)
@@ -11,6 +11,6 @@ void alr_batch_destroy(struct alr_batch *alrb);
 int alr_batch_add(struct alr_batch *alrb, const char *obd_name,
                const struct lu_fid *pfid, time_t time, __u64 begin, __u64 end,
                __u32 size, __u32 segment_count, __u32 flags);
-int alr_batch_print(struct alr_batch *alrb, FILE *file);
+int alr_batch_print(struct alr_batch *alrb, FILE *file, int fraction);
 
 #endif /* _OFD_ACCESS_BATCH_H_ */
index 35d4a1c..75d5a59 100644 (file)
@@ -128,6 +128,7 @@ static unsigned int oal_log_minor_max;
 static struct alr_batch *alr_batch;
 static FILE *alr_batch_file;
 static const char *alr_batch_file_path;
+static int alr_print_fraction = 100;
 
 #define D_ALR_DEV "%s %d"
 #define P_ALR_DEV(ad) \
@@ -498,7 +499,7 @@ static int alr_batch_timer_io(int epoll_fd, struct alr_dev *td, unsigned int mas
 
        DEBUG_U(expire_count);
 
-       rc = alr_batch_print(alr_batch, alr_batch_file);
+       rc = alr_batch_print(alr_batch, alr_batch_file, alr_print_fraction);
        if (rc < 0) {
                ERROR("cannot write to '%s': %s\n",
                        alr_batch_file_path, strerror(errno));
@@ -629,12 +630,13 @@ int main(int argc, char *argv[])
                { .name = "batch-offset", .has_arg = required_argument, .val = 'o', },
                { .name = "debug", .has_arg = optional_argument, .val = 'd', },
                { .name = "help", .has_arg = no_argument, .val = 'h', },
+               { .name = "fraction", .has_arg = required_argument, .val = 'F', },
                { .name = "list", .has_arg = no_argument, .val = 'l', },
                { .name = "trace", .has_arg = optional_argument, .val = 't', },
                { .name = NULL, },
        };
 
-       while ((c = getopt_long(argc, argv, "d::f:hi:lt::", options, NULL)) != -1) {
+       while ((c = getopt_long(argc, argv, "d::f:F:hi:lt::", options, NULL)) != -1) {
                switch (c) {
                case 'f':
                        alr_batch_file_path = optarg;
@@ -669,6 +671,11 @@ int main(int argc, char *argv[])
                case 'h':
                        usage();
                        exit(EXIT_SUCCESS);
+               case 'F':
+                       alr_print_fraction = strtoll(optarg, NULL, 0);
+                       if (alr_print_fraction < 1 || alr_print_fraction > 100)
+                               FATAL("invalid batch offset '%s'\n", optarg);
+                       break;
                case 'l':
                        list_info = 1;
                        break;