These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / tools / perf / util / dso.c
index fc0ddd5..425df5c 100644 (file)
@@ -4,6 +4,7 @@
 #include "symbol.h"
 #include "dso.h"
 #include "machine.h"
+#include "auxtrace.h"
 #include "util.h"
 #include "debug.h"
 
@@ -165,12 +166,28 @@ bool is_supported_compression(const char *ext)
        return false;
 }
 
-bool is_kernel_module(const char *pathname)
+bool is_kernel_module(const char *pathname, int cpumode)
 {
        struct kmod_path m;
-
-       if (kmod_path__parse(&m, pathname))
-               return NULL;
+       int mode = cpumode & PERF_RECORD_MISC_CPUMODE_MASK;
+
+       WARN_ONCE(mode != cpumode,
+                 "Internal error: passing unmasked cpumode (%x) to is_kernel_module",
+                 cpumode);
+
+       switch (mode) {
+       case PERF_RECORD_MISC_USER:
+       case PERF_RECORD_MISC_HYPERVISOR:
+       case PERF_RECORD_MISC_GUEST_USER:
+               return false;
+       /* Treat PERF_RECORD_MISC_CPUMODE_UNKNOWN as kernel */
+       default:
+               if (kmod_path__parse(&m, pathname)) {
+                       pr_err("Failed to check whether %s is a kernel module or not. Assume it is.",
+                                       pathname);
+                       return true;
+               }
+       }
 
        return m.kmod;
 }
@@ -214,12 +231,33 @@ int __kmod_path__parse(struct kmod_path *m, const char *path,
 {
        const char *name = strrchr(path, '/');
        const char *ext  = strrchr(path, '.');
+       bool is_simple_name = false;
 
        memset(m, 0x0, sizeof(*m));
        name = name ? name + 1 : path;
 
+       /*
+        * '.' is also a valid character for module name. For example:
+        * [aaa.bbb] is a valid module name. '[' should have higher
+        * priority than '.ko' suffix.
+        *
+        * The kernel names are from machine__mmap_name. Such
+        * name should belong to kernel itself, not kernel module.
+        */
+       if (name[0] == '[') {
+               is_simple_name = true;
+               if ((strncmp(name, "[kernel.kallsyms]", 17) == 0) ||
+                   (strncmp(name, "[guest.kernel.kallsyms", 22) == 0) ||
+                   (strncmp(name, "[vdso]", 6) == 0) ||
+                   (strncmp(name, "[vsyscall]", 10) == 0)) {
+                       m->kmod = false;
+
+               } else
+                       m->kmod = true;
+       }
+
        /* No extension, just return name. */
-       if (ext == NULL) {
+       if ((ext == NULL) || is_simple_name) {
                if (alloc_name) {
                        m->name = strdup(name);
                        return m->name ? 0 : -ENOMEM;
@@ -264,6 +302,7 @@ int __kmod_path__parse(struct kmod_path *m, const char *path,
  */
 static LIST_HEAD(dso__data_open);
 static long dso__data_open_cnt;
+static pthread_mutex_t dso__data_open_lock = PTHREAD_MUTEX_INITIALIZER;
 
 static void dso__list_add(struct dso *dso)
 {
@@ -433,18 +472,12 @@ static void check_data_close(void)
  */
 void dso__data_close(struct dso *dso)
 {
+       pthread_mutex_lock(&dso__data_open_lock);
        close_dso(dso);
+       pthread_mutex_unlock(&dso__data_open_lock);
 }
 
-/**
- * dso__data_fd - Get dso's data file descriptor
- * @dso: dso object
- * @machine: machine object
- *
- * External interface to find dso's file, open it and
- * returns file descriptor.
- */
-int dso__data_fd(struct dso *dso, struct machine *machine)
+static void try_to_open_dso(struct dso *dso, struct machine *machine)
 {
        enum dso_binary_type binary_type_data[] = {
                DSO_BINARY_TYPE__BUILD_ID_CACHE,
@@ -453,11 +486,8 @@ int dso__data_fd(struct dso *dso, struct machine *machine)
        };
        int i = 0;
 
-       if (dso->data.status == DSO_DATA_STATUS_ERROR)
-               return -1;
-
        if (dso->data.fd >= 0)
-               goto out;
+               return;
 
        if (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND) {
                dso->data.fd = open_dso(dso, machine);
@@ -477,10 +507,38 @@ out:
                dso->data.status = DSO_DATA_STATUS_OK;
        else
                dso->data.status = DSO_DATA_STATUS_ERROR;
+}
+
+/**
+ * dso__data_get_fd - Get dso's data file descriptor
+ * @dso: dso object
+ * @machine: machine object
+ *
+ * External interface to find dso's file, open it and
+ * returns file descriptor.  It should be paired with
+ * dso__data_put_fd() if it returns non-negative value.
+ */
+int dso__data_get_fd(struct dso *dso, struct machine *machine)
+{
+       if (dso->data.status == DSO_DATA_STATUS_ERROR)
+               return -1;
+
+       if (pthread_mutex_lock(&dso__data_open_lock) < 0)
+               return -1;
+
+       try_to_open_dso(dso, machine);
+
+       if (dso->data.fd < 0)
+               pthread_mutex_unlock(&dso__data_open_lock);
 
        return dso->data.fd;
 }
 
+void dso__data_put_fd(struct dso *dso __maybe_unused)
+{
+       pthread_mutex_unlock(&dso__data_open_lock);
+}
+
 bool dso__data_status_seen(struct dso *dso, enum dso_data_status_seen by)
 {
        u32 flag = 1 << by;
@@ -494,10 +552,12 @@ bool dso__data_status_seen(struct dso *dso, enum dso_data_status_seen by)
 }
 
 static void
-dso_cache__free(struct rb_root *root)
+dso_cache__free(struct dso *dso)
 {
+       struct rb_root *root = &dso->data.cache;
        struct rb_node *next = rb_first(root);
 
+       pthread_mutex_lock(&dso->lock);
        while (next) {
                struct dso_cache *cache;
 
@@ -506,10 +566,12 @@ dso_cache__free(struct rb_root *root)
                rb_erase(&cache->rb_node, root);
                free(cache);
        }
+       pthread_mutex_unlock(&dso->lock);
 }
 
-static struct dso_cache *dso_cache__find(const struct rb_root *root, u64 offset)
+static struct dso_cache *dso_cache__find(struct dso *dso, u64 offset)
 {
+       const struct rb_root *root = &dso->data.cache;
        struct rb_node * const *p = &root->rb_node;
        const struct rb_node *parent = NULL;
        struct dso_cache *cache;
@@ -528,17 +590,20 @@ static struct dso_cache *dso_cache__find(const struct rb_root *root, u64 offset)
                else
                        return cache;
        }
+
        return NULL;
 }
 
-static void
-dso_cache__insert(struct rb_root *root, struct dso_cache *new)
+static struct dso_cache *
+dso_cache__insert(struct dso *dso, struct dso_cache *new)
 {
+       struct rb_root *root = &dso->data.cache;
        struct rb_node **p = &root->rb_node;
        struct rb_node *parent = NULL;
        struct dso_cache *cache;
        u64 offset = new->offset;
 
+       pthread_mutex_lock(&dso->lock);
        while (*p != NULL) {
                u64 end;
 
@@ -550,10 +615,17 @@ dso_cache__insert(struct rb_root *root, struct dso_cache *new)
                        p = &(*p)->rb_left;
                else if (offset >= end)
                        p = &(*p)->rb_right;
+               else
+                       goto out;
        }
 
        rb_link_node(&new->rb_node, parent, p);
        rb_insert_color(&new->rb_node, root);
+
+       cache = NULL;
+out:
+       pthread_mutex_unlock(&dso->lock);
+       return cache;
 }
 
 static ssize_t
@@ -568,19 +640,33 @@ dso_cache__memcpy(struct dso_cache *cache, u64 offset,
 }
 
 static ssize_t
-dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size)
+dso_cache__read(struct dso *dso, struct machine *machine,
+               u64 offset, u8 *data, ssize_t size)
 {
        struct dso_cache *cache;
+       struct dso_cache *old;
        ssize_t ret;
 
        do {
                u64 cache_offset;
 
-               ret = -ENOMEM;
-
                cache = zalloc(sizeof(*cache) + DSO__DATA_CACHE_SIZE);
                if (!cache)
+                       return -ENOMEM;
+
+               pthread_mutex_lock(&dso__data_open_lock);
+
+               /*
+                * dso->data.fd might be closed if other thread opened another
+                * file (dso) due to open file limit (RLIMIT_NOFILE).
+                */
+               try_to_open_dso(dso, machine);
+
+               if (dso->data.fd < 0) {
+                       ret = -errno;
+                       dso->data.status = DSO_DATA_STATUS_ERROR;
                        break;
+               }
 
                cache_offset = offset & DSO__DATA_CACHE_MASK;
 
@@ -590,11 +676,20 @@ dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size)
 
                cache->offset = cache_offset;
                cache->size   = ret;
-               dso_cache__insert(&dso->data.cache, cache);
+       } while (0);
 
-               ret = dso_cache__memcpy(cache, offset, data, size);
+       pthread_mutex_unlock(&dso__data_open_lock);
 
-       } while (0);
+       if (ret > 0) {
+               old = dso_cache__insert(dso, cache);
+               if (old) {
+                       /* we lose the race */
+                       free(cache);
+                       cache = old;
+               }
+
+               ret = dso_cache__memcpy(cache, offset, data, size);
+       }
 
        if (ret <= 0)
                free(cache);
@@ -602,16 +697,16 @@ dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size)
        return ret;
 }
 
-static ssize_t dso_cache_read(struct dso *dso, u64 offset,
-                             u8 *data, ssize_t size)
+static ssize_t dso_cache_read(struct dso *dso, struct machine *machine,
+                             u64 offset, u8 *data, ssize_t size)
 {
        struct dso_cache *cache;
 
-       cache = dso_cache__find(&dso->data.cache, offset);
+       cache = dso_cache__find(dso, offset);
        if (cache)
                return dso_cache__memcpy(cache, offset, data, size);
        else
-               return dso_cache__read(dso, offset, data, size);
+               return dso_cache__read(dso, machine, offset, data, size);
 }
 
 /*
@@ -619,7 +714,8 @@ static ssize_t dso_cache_read(struct dso *dso, u64 offset,
  * in the rb_tree. Any read to already cached data is served
  * by cached data.
  */
-static ssize_t cached_read(struct dso *dso, u64 offset, u8 *data, ssize_t size)
+static ssize_t cached_read(struct dso *dso, struct machine *machine,
+                          u64 offset, u8 *data, ssize_t size)
 {
        ssize_t r = 0;
        u8 *p = data;
@@ -627,7 +723,7 @@ static ssize_t cached_read(struct dso *dso, u64 offset, u8 *data, ssize_t size)
        do {
                ssize_t ret;
 
-               ret = dso_cache_read(dso, offset, p, size);
+               ret = dso_cache_read(dso, machine, offset, p, size);
                if (ret < 0)
                        return ret;
 
@@ -647,21 +743,44 @@ static ssize_t cached_read(struct dso *dso, u64 offset, u8 *data, ssize_t size)
        return r;
 }
 
-static int data_file_size(struct dso *dso)
+static int data_file_size(struct dso *dso, struct machine *machine)
 {
+       int ret = 0;
        struct stat st;
        char sbuf[STRERR_BUFSIZE];
 
-       if (!dso->data.file_size) {
-               if (fstat(dso->data.fd, &st)) {
-                       pr_err("dso mmap failed, fstat: %s\n",
-                               strerror_r(errno, sbuf, sizeof(sbuf)));
-                       return -1;
-               }
-               dso->data.file_size = st.st_size;
+       if (dso->data.file_size)
+               return 0;
+
+       if (dso->data.status == DSO_DATA_STATUS_ERROR)
+               return -1;
+
+       pthread_mutex_lock(&dso__data_open_lock);
+
+       /*
+        * dso->data.fd might be closed if other thread opened another
+        * file (dso) due to open file limit (RLIMIT_NOFILE).
+        */
+       try_to_open_dso(dso, machine);
+
+       if (dso->data.fd < 0) {
+               ret = -errno;
+               dso->data.status = DSO_DATA_STATUS_ERROR;
+               goto out;
        }
 
-       return 0;
+       if (fstat(dso->data.fd, &st) < 0) {
+               ret = -errno;
+               pr_err("dso cache fstat failed: %s\n",
+                      strerror_r(errno, sbuf, sizeof(sbuf)));
+               dso->data.status = DSO_DATA_STATUS_ERROR;
+               goto out;
+       }
+       dso->data.file_size = st.st_size;
+
+out:
+       pthread_mutex_unlock(&dso__data_open_lock);
+       return ret;
 }
 
 /**
@@ -673,23 +792,17 @@ static int data_file_size(struct dso *dso)
  */
 off_t dso__data_size(struct dso *dso, struct machine *machine)
 {
-       int fd;
-
-       fd = dso__data_fd(dso, machine);
-       if (fd < 0)
-               return fd;
-
-       if (data_file_size(dso))
+       if (data_file_size(dso, machine))
                return -1;
 
        /* For now just estimate dso data size is close to file size */
        return dso->data.file_size;
 }
 
-static ssize_t data_read_offset(struct dso *dso, u64 offset,
-                               u8 *data, ssize_t size)
+static ssize_t data_read_offset(struct dso *dso, struct machine *machine,
+                               u64 offset, u8 *data, ssize_t size)
 {
-       if (data_file_size(dso))
+       if (data_file_size(dso, machine))
                return -1;
 
        /* Check the offset sanity. */
@@ -699,7 +812,7 @@ static ssize_t data_read_offset(struct dso *dso, u64 offset,
        if (offset + size < offset)
                return -1;
 
-       return cached_read(dso, offset, data, size);
+       return cached_read(dso, machine, offset, data, size);
 }
 
 /**
@@ -716,10 +829,10 @@ static ssize_t data_read_offset(struct dso *dso, u64 offset,
 ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,
                              u64 offset, u8 *data, ssize_t size)
 {
-       if (dso__data_fd(dso, machine) < 0)
+       if (dso->data.status == DSO_DATA_STATUS_ERROR)
                return -1;
 
-       return data_read_offset(dso, offset, data, size);
+       return data_read_offset(dso, machine, offset, data, size);
 }
 
 /**
@@ -751,13 +864,13 @@ struct map *dso__new_map(const char *name)
        return map;
 }
 
-struct dso *dso__kernel_findnew(struct machine *machine, const char *name,
-                   const char *short_name, int dso_type)
+struct dso *machine__findnew_kernel(struct machine *machine, const char *name,
+                                   const char *short_name, int dso_type)
 {
        /*
         * The kernel dso could be created by build_id processing.
         */
-       struct dso *dso = __dsos__findnew(&machine->kernel_dsos, name);
+       struct dso *dso = machine__findnew_dso(machine, name);
 
        /*
         * We need to run this in all cases, since during the build_id
@@ -776,8 +889,8 @@ struct dso *dso__kernel_findnew(struct machine *machine, const char *name,
  * Either one of the dso or name parameter must be non-NULL or the
  * function will not work.
  */
-static struct dso *dso__findlink_by_longname(struct rb_root *root,
-                                            struct dso *dso, const char *name)
+static struct dso *__dso__findlink_by_longname(struct rb_root *root,
+                                              struct dso *dso, const char *name)
 {
        struct rb_node **p = &root->rb_node;
        struct rb_node  *parent = NULL;
@@ -820,27 +933,43 @@ static struct dso *dso__findlink_by_longname(struct rb_root *root,
                /* Add new node and rebalance tree */
                rb_link_node(&dso->rb_node, parent, p);
                rb_insert_color(&dso->rb_node, root);
+               dso->root = root;
        }
        return NULL;
 }
 
-static inline struct dso *
-dso__find_by_longname(const struct rb_root *root, const char *name)
+static inline struct dso *__dso__find_by_longname(struct rb_root *root,
+                                                 const char *name)
 {
-       return dso__findlink_by_longname((struct rb_root *)root, NULL, name);
+       return __dso__findlink_by_longname(root, NULL, name);
 }
 
 void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated)
 {
+       struct rb_root *root = dso->root;
+
        if (name == NULL)
                return;
 
        if (dso->long_name_allocated)
                free((char *)dso->long_name);
 
+       if (root) {
+               rb_erase(&dso->rb_node, root);
+               /*
+                * __dso__findlink_by_longname() isn't guaranteed to add it
+                * back, so a clean removal is required here.
+                */
+               RB_CLEAR_NODE(&dso->rb_node);
+               dso->root = NULL;
+       }
+
        dso->long_name           = name;
        dso->long_name_len       = strlen(name);
        dso->long_name_allocated = name_allocated;
+
+       if (root)
+               __dso__findlink_by_longname(root, dso, NULL);
 }
 
 void dso__set_short_name(struct dso *dso, const char *name, bool name_allocated)
@@ -933,8 +1062,11 @@ struct dso *dso__new(const char *name)
                dso->kernel = DSO_TYPE_USER;
                dso->needs_swap = DSO_SWAP__UNSET;
                RB_CLEAR_NODE(&dso->rb_node);
+               dso->root = NULL;
                INIT_LIST_HEAD(&dso->node);
                INIT_LIST_HEAD(&dso->data.open_entry);
+               pthread_mutex_init(&dso->lock, NULL);
+               atomic_set(&dso->refcnt, 1);
        }
 
        return dso;
@@ -961,12 +1093,27 @@ void dso__delete(struct dso *dso)
        }
 
        dso__data_close(dso);
-       dso_cache__free(&dso->data.cache);
+       auxtrace_cache__free(dso->auxtrace_cache);
+       dso_cache__free(dso);
        dso__free_a2l(dso);
        zfree(&dso->symsrc_filename);
+       pthread_mutex_destroy(&dso->lock);
        free(dso);
 }
 
+struct dso *dso__get(struct dso *dso)
+{
+       if (dso)
+               atomic_inc(&dso->refcnt);
+       return dso;
+}
+
+void dso__put(struct dso *dso)
+{
+       if (dso && atomic_dec_and_test(&dso->refcnt))
+               dso__delete(dso);
+}
+
 void dso__set_build_id(struct dso *dso, void *build_id)
 {
        memcpy(dso->build_id, build_id, sizeof(dso->build_id));
@@ -1033,14 +1180,41 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
        return have_build_id;
 }
 
-void dsos__add(struct dsos *dsos, struct dso *dso)
+void __dsos__add(struct dsos *dsos, struct dso *dso)
 {
        list_add_tail(&dso->node, &dsos->head);
-       dso__findlink_by_longname(&dsos->root, dso, NULL);
+       __dso__findlink_by_longname(&dsos->root, dso, NULL);
+       /*
+        * It is now in the linked list, grab a reference, then garbage collect
+        * this when needing memory, by looking at LRU dso instances in the
+        * list with atomic_read(&dso->refcnt) == 1, i.e. no references
+        * anywhere besides the one for the list, do, under a lock for the
+        * list: remove it from the list, then a dso__put(), that probably will
+        * be the last and will then call dso__delete(), end of life.
+        *
+        * That, or at the end of the 'struct machine' lifetime, when all
+        * 'struct dso' instances will be removed from the list, in
+        * dsos__exit(), if they have no other reference from some other data
+        * structure.
+        *
+        * E.g.: after processing a 'perf.data' file and storing references
+        * to objects instantiated while processing events, we will have
+        * references to the 'thread', 'map', 'dso' structs all from 'struct
+        * hist_entry' instances, but we may not need anything not referenced,
+        * so we might as well call machines__exit()/machines__delete() and
+        * garbage collect it.
+        */
+       dso__get(dso);
 }
 
-struct dso *dsos__find(const struct dsos *dsos, const char *name,
-                      bool cmp_short)
+void dsos__add(struct dsos *dsos, struct dso *dso)
+{
+       pthread_rwlock_wrlock(&dsos->lock);
+       __dsos__add(dsos, dso);
+       pthread_rwlock_unlock(&dsos->lock);
+}
+
+struct dso *__dsos__find(struct dsos *dsos, const char *name, bool cmp_short)
 {
        struct dso *pos;
 
@@ -1050,15 +1224,24 @@ struct dso *dsos__find(const struct dsos *dsos, const char *name,
                                return pos;
                return NULL;
        }
-       return dso__find_by_longname(&dsos->root, name);
+       return __dso__find_by_longname(&dsos->root, name);
+}
+
+struct dso *dsos__find(struct dsos *dsos, const char *name, bool cmp_short)
+{
+       struct dso *dso;
+       pthread_rwlock_rdlock(&dsos->lock);
+       dso = __dsos__find(dsos, name, cmp_short);
+       pthread_rwlock_unlock(&dsos->lock);
+       return dso;
 }
 
-struct dso *dsos__addnew(struct dsos *dsos, const char *name)
+struct dso *__dsos__addnew(struct dsos *dsos, const char *name)
 {
        struct dso *dso = dso__new(name);
 
        if (dso != NULL) {
-               dsos__add(dsos, dso);
+               __dsos__add(dsos, dso);
                dso__set_basename(dso);
        }
        return dso;
@@ -1066,9 +1249,18 @@ struct dso *dsos__addnew(struct dsos *dsos, const char *name)
 
 struct dso *__dsos__findnew(struct dsos *dsos, const char *name)
 {
-       struct dso *dso = dsos__find(dsos, name, false);
+       struct dso *dso = __dsos__find(dsos, name, false);
 
-       return dso ? dso : dsos__addnew(dsos, name);
+       return dso ? dso : __dsos__addnew(dsos, name);
+}
+
+struct dso *dsos__findnew(struct dsos *dsos, const char *name)
+{
+       struct dso *dso;
+       pthread_rwlock_wrlock(&dsos->lock);
+       dso = dso__get(__dsos__findnew(dsos, name));
+       pthread_rwlock_unlock(&dsos->lock);
+       return dso;
 }
 
 size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
@@ -1130,12 +1322,15 @@ size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp)
 enum dso_type dso__type(struct dso *dso, struct machine *machine)
 {
        int fd;
+       enum dso_type type = DSO__TYPE_UNKNOWN;
 
-       fd = dso__data_fd(dso, machine);
-       if (fd < 0)
-               return DSO__TYPE_UNKNOWN;
+       fd = dso__data_get_fd(dso, machine);
+       if (fd >= 0) {
+               type = dso__type_fd(fd);
+               dso__data_put_fd(dso);
+       }
 
-       return dso__type_fd(fd);
+       return type;
 }
 
 int dso__strerror_load(struct dso *dso, char *buf, size_t buflen)