Merge tag 'mac80211-for-john-2014-11-18' of git://git.kernel.org/pub/scm/linux/kernel...
[cascardo/linux.git] / tools / perf / util / evlist.c
index 5ff3c66..3c9e77d 100644 (file)
@@ -25,6 +25,9 @@
 #include <linux/bitops.h>
 #include <linux/hash.h>
 
+static void perf_evlist__mmap_put(struct perf_evlist *evlist, int idx);
+static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx);
+
 #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
 #define SID(e, x, y) xyarray__entry(e->sample_id, x, y)
 
@@ -37,6 +40,7 @@ void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus,
                INIT_HLIST_HEAD(&evlist->heads[i]);
        INIT_LIST_HEAD(&evlist->entries);
        perf_evlist__set_maps(evlist, cpus, threads);
+       fdarray__init(&evlist->pollfd, 64);
        evlist->workload.pid = -1;
 }
 
@@ -102,7 +106,7 @@ static void perf_evlist__purge(struct perf_evlist *evlist)
 void perf_evlist__exit(struct perf_evlist *evlist)
 {
        zfree(&evlist->mmap);
-       zfree(&evlist->pollfd);
+       fdarray__exit(&evlist->pollfd);
 }
 
 void perf_evlist__delete(struct perf_evlist *evlist)
@@ -402,20 +406,6 @@ int perf_evlist__enable_event_idx(struct perf_evlist *evlist,
                return perf_evlist__enable_event_thread(evlist, evsel, idx);
 }
 
-static int perf_evlist__grow_pollfd(struct perf_evlist *evlist, int hint)
-{
-       int nr_fds_alloc = evlist->nr_fds_alloc + hint;
-       size_t size = sizeof(struct pollfd) * nr_fds_alloc;
-       struct pollfd *pollfd = realloc(evlist->pollfd, size);
-
-       if (pollfd == NULL)
-               return -ENOMEM;
-
-       evlist->nr_fds_alloc = nr_fds_alloc;
-       evlist->pollfd       = pollfd;
-       return 0;
-}
-
 int perf_evlist__alloc_pollfd(struct perf_evlist *evlist)
 {
        int nr_cpus = cpu_map__nr(evlist->cpus);
@@ -430,54 +420,50 @@ int perf_evlist__alloc_pollfd(struct perf_evlist *evlist)
                        nfds += nr_cpus * nr_threads;
        }
 
-       if (evlist->nr_fds_alloc - evlist->nr_fds < nfds &&
-           perf_evlist__grow_pollfd(evlist, nfds) < 0)
+       if (fdarray__available_entries(&evlist->pollfd) < nfds &&
+           fdarray__grow(&evlist->pollfd, nfds) < 0)
                return -ENOMEM;
 
        return 0;
 }
 
-int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd)
+static int __perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd, int idx)
 {
+       int pos = fdarray__add(&evlist->pollfd, fd, POLLIN | POLLERR | POLLHUP);
        /*
-        * XXX: 64 is arbitrary, just not to call realloc at each fd.
-        *      Find a better autogrowing heuristic
+        * Save the idx so that when we filter out fds POLLHUP'ed we can
+        * close the associated evlist->mmap[] entry.
         */
-       if (evlist->nr_fds == evlist->nr_fds_alloc &&
-           perf_evlist__grow_pollfd(evlist, 64) < 0)
-               return -ENOMEM;
+       if (pos >= 0) {
+               evlist->pollfd.priv[pos].idx = idx;
 
-       fcntl(fd, F_SETFL, O_NONBLOCK);
-       evlist->pollfd[evlist->nr_fds].fd = fd;
-       evlist->pollfd[evlist->nr_fds].events = POLLIN | POLLERR | POLLHUP;
-       evlist->nr_fds++;
-       return 0;
+               fcntl(fd, F_SETFL, O_NONBLOCK);
+       }
+
+       return pos;
 }
 
-int perf_evlist__filter_pollfd(struct perf_evlist *evlist, short revents_and_mask)
+int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd)
 {
-       int fd, nr_fds = 0;
-
-       if (evlist->nr_fds == 0)
-               return 0;
-
-       for (fd = 0; fd < evlist->nr_fds; ++fd) {
-               if (evlist->pollfd[fd].revents & revents_and_mask)
-                       continue;
+       return __perf_evlist__add_pollfd(evlist, fd, -1);
+}
 
-               if (fd != nr_fds)
-                       evlist->pollfd[nr_fds] = evlist->pollfd[fd];
+static void perf_evlist__munmap_filtered(struct fdarray *fda, int fd)
+{
+       struct perf_evlist *evlist = container_of(fda, struct perf_evlist, pollfd);
 
-               ++nr_fds;
-       }
+       perf_evlist__mmap_put(evlist, fda->priv[fd].idx);
+}
 
-       evlist->nr_fds = nr_fds;
-       return nr_fds;
+int perf_evlist__filter_pollfd(struct perf_evlist *evlist, short revents_and_mask)
+{
+       return fdarray__filter(&evlist->pollfd, revents_and_mask,
+                              perf_evlist__munmap_filtered);
 }
 
 int perf_evlist__poll(struct perf_evlist *evlist, int timeout)
 {
-       return poll(evlist->pollfd, evlist->nr_fds, timeout);
+       return fdarray__poll(&evlist->pollfd, timeout);
 }
 
 static void perf_evlist__id_hash(struct perf_evlist *evlist,
@@ -690,14 +676,36 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)
        return event;
 }
 
+static bool perf_mmap__empty(struct perf_mmap *md)
+{
+       return perf_mmap__read_head(md) != md->prev;
+}
+
+static void perf_evlist__mmap_get(struct perf_evlist *evlist, int idx)
+{
+       ++evlist->mmap[idx].refcnt;
+}
+
+static void perf_evlist__mmap_put(struct perf_evlist *evlist, int idx)
+{
+       BUG_ON(evlist->mmap[idx].refcnt == 0);
+
+       if (--evlist->mmap[idx].refcnt == 0)
+               __perf_evlist__munmap(evlist, idx);
+}
+
 void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx)
 {
+       struct perf_mmap *md = &evlist->mmap[idx];
+
        if (!evlist->overwrite) {
-               struct perf_mmap *md = &evlist->mmap[idx];
                unsigned int old = md->prev;
 
                perf_mmap__write_tail(md, old);
        }
+
+       if (md->refcnt == 1 && perf_mmap__empty(md))
+               perf_evlist__mmap_put(evlist, idx);
 }
 
 static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx)
@@ -705,6 +713,7 @@ static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx)
        if (evlist->mmap[idx].base != NULL) {
                munmap(evlist->mmap[idx].base, evlist->mmap_len);
                evlist->mmap[idx].base = NULL;
+               evlist->mmap[idx].refcnt = 0;
        }
 }
 
@@ -738,6 +747,20 @@ struct mmap_params {
 static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx,
                               struct mmap_params *mp, int fd)
 {
+       /*
+        * The last one will be done at perf_evlist__mmap_consume(), so that we
+        * make sure we don't prevent tools from consuming every last event in
+        * the ring buffer.
+        *
+        * I.e. we can get the POLLHUP meaning that the fd doesn't exist
+        * anymore, but the last events for it are still in the ring buffer,
+        * waiting to be consumed.
+        *
+        * Tools can chose to ignore this at their own discretion, but the
+        * evlist layer can't just drop it when filtering events in
+        * perf_evlist__filter_pollfd().
+        */
+       evlist->mmap[idx].refcnt = 2;
        evlist->mmap[idx].prev = 0;
        evlist->mmap[idx].mask = mp->mask;
        evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, mp->prot,
@@ -773,10 +796,14 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx,
                } else {
                        if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0)
                                return -1;
+
+                       perf_evlist__mmap_get(evlist, idx);
                }
 
-               if (perf_evlist__add_pollfd(evlist, fd) < 0)
+               if (__perf_evlist__add_pollfd(evlist, fd, idx) < 0) {
+                       perf_evlist__mmap_put(evlist, idx);
                        return -1;
+               }
 
                if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
                    perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0)
@@ -935,7 +962,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
        if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0)
                return -ENOMEM;
 
-       if (evlist->pollfd == NULL && perf_evlist__alloc_pollfd(evlist) < 0)
+       if (evlist->pollfd.entries == NULL && perf_evlist__alloc_pollfd(evlist) < 0)
                return -ENOMEM;
 
        evlist->overwrite = overwrite;
@@ -976,6 +1003,7 @@ int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target)
 
 out_delete_threads:
        thread_map__delete(evlist->threads);
+       evlist->threads = NULL;
        return -1;
 }
 
@@ -1148,11 +1176,51 @@ void perf_evlist__close(struct perf_evlist *evlist)
        }
 }
 
+static int perf_evlist__create_syswide_maps(struct perf_evlist *evlist)
+{
+       int err = -ENOMEM;
+
+       /*
+        * Try reading /sys/devices/system/cpu/online to get
+        * an all cpus map.
+        *
+        * FIXME: -ENOMEM is the best we can do here, the cpu_map
+        * code needs an overhaul to properly forward the
+        * error, and we may not want to do that fallback to a
+        * default cpu identity map :-\
+        */
+       evlist->cpus = cpu_map__new(NULL);
+       if (evlist->cpus == NULL)
+               goto out;
+
+       evlist->threads = thread_map__new_dummy();
+       if (evlist->threads == NULL)
+               goto out_free_cpus;
+
+       err = 0;
+out:
+       return err;
+out_free_cpus:
+       cpu_map__delete(evlist->cpus);
+       evlist->cpus = NULL;
+       goto out;
+}
+
 int perf_evlist__open(struct perf_evlist *evlist)
 {
        struct perf_evsel *evsel;
        int err;
 
+       /*
+        * Default: one fd per CPU, all threads, aka systemwide
+        * as sys_perf_event_open(cpu = -1, thread = -1) is EINVAL
+        */
+       if (evlist->threads == NULL && evlist->cpus == NULL) {
+               err = perf_evlist__create_syswide_maps(evlist);
+               if (err < 0)
+                       goto out_err;
+       }
+
        perf_evlist__update_id_pos(evlist);
 
        evlist__for_each(evlist, evsel) {
@@ -1249,8 +1317,14 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *tar
                sigaction(SIGUSR1, &act, NULL);
        }
 
-       if (target__none(target))
+       if (target__none(target)) {
+               if (evlist->threads == NULL) {
+                       fprintf(stderr, "FATAL: evlist->threads need to be set at this point (%s:%d).\n",
+                               __func__, __LINE__);
+                       goto out_close_pipes;
+               }
                evlist->threads->map[0] = evlist->workload.pid;
+       }
 
        close(child_ready_pipe[1]);
        close(go_pipe[0]);