#include <linux/inetdevice.h>
#include <linux/sched.h>
#include <linux/mm.h>
-#include <linux/fdtable.h>
#include <linux/swap.h>
#include <linux/wait.h>
#include <linux/mutex.h>
#include <wrapper/tracepoint.h>
#include <wrapper/genhd.h>
#include <wrapper/file.h>
-#include <wrapper/time.h>
+#include <wrapper/fdtable.h>
#ifdef CONFIG_LTTNG_HAS_LIST_IRQ
#include <linux/irq.h>
struct lttng_fd_ctx {
char *page;
struct lttng_session *session;
- struct task_struct *p;
struct files_struct *files;
};
disk_part_iter_init(&piter, disk, DISK_PITER_INCL_PART0);
while ((part = disk_part_iter_next(&piter))) {
+ struct block_device bdev;
char name_buf[BDEVNAME_SIZE];
- char *p;
+ const char *p;
+
+ /*
+ * Create a partial 'struct blockdevice' to use
+ * 'bdevname()' which is a simple wrapper over
+ * 'disk_name()' but has the honor to be EXPORT_SYMBOL.
+ */
+ bdev.bd_disk = disk;
+ bdev.bd_part = part;
- p = wrapper_disk_name(disk, part->partno, name_buf);
+ p = bdevname(&bdev, name_buf);
if (!p) {
disk_part_iter_exit(&piter);
class_dev_iter_exit(&iter);
/* Make sure we give at least some info */
spin_lock(&dentry->d_lock);
- trace_lttng_statedump_file_descriptor(ctx->session, ctx->p, fd,
- dentry->d_name.name, flags, file->f_mode);
+ trace_lttng_statedump_file_descriptor(ctx->session,
+ ctx->files, fd, dentry->d_name.name, flags,
+ file->f_mode);
spin_unlock(&dentry->d_lock);
goto end;
}
- trace_lttng_statedump_file_descriptor(ctx->session, ctx->p, fd, s,
- flags, file->f_mode);
+ trace_lttng_statedump_file_descriptor(ctx->session,
+ ctx->files, fd, s, flags, file->f_mode);
end:
return 0;
}
+/* Called with task lock held. */
static
-void lttng_enumerate_task_fd(struct lttng_session *session,
- struct task_struct *p, char *tmp)
+void lttng_enumerate_files(struct lttng_session *session,
+ struct files_struct *files,
+ char *tmp)
{
- struct lttng_fd_ctx ctx = { .page = tmp, .session = session, .p = p };
- struct files_struct *files;
+ struct lttng_fd_ctx ctx = { .page = tmp, .session = session, .files = files, };
- task_lock(p);
- files = p->files;
- if (!files)
- goto end;
- ctx.files = files;
lttng_iterate_fd(files, 0, lttng_dump_one_fd, &ctx);
-end:
- task_unlock(p);
-}
-
-static
-int lttng_enumerate_file_descriptors(struct lttng_session *session)
-{
- struct task_struct *p;
- char *tmp;
-
- tmp = (char *) __get_free_page(GFP_KERNEL);
- if (!tmp)
- return -ENOMEM;
-
- /* Enumerate active file descriptors */
- rcu_read_lock();
- for_each_process(p)
- lttng_enumerate_task_fd(session, p, tmp);
- rcu_read_unlock();
- free_page((unsigned long) tmp);
- return 0;
}
#ifdef LTTNG_HAVE_STATEDUMP_CPU_TOPOLOGY
pid_ns = task_active_pid_ns(p);
do {
trace_lttng_statedump_process_pid_ns(session, p, pid_ns);
- pid_ns = pid_ns->parent;
+ pid_ns = pid_ns ? pid_ns->parent : NULL;
} while (pid_ns);
user_ns = task_cred_xxx(p, user_ns);
do {
trace_lttng_statedump_process_user_ns(session, p, user_ns);
- user_ns = user_ns->lttng_user_ns_parent;
+ /*
+ * trace_lttng_statedump_process_user_ns() internally
+ * checks whether user_ns is NULL. While this does not
+ * appear to be a possible return value for
+ * task_cred_xxx(), err on the safe side and check
+ * for NULL here as well to be consistent with the
+ * paranoid behavior of
+ * trace_lttng_statedump_process_user_ns().
+ */
+ user_ns = user_ns ? user_ns->lttng_user_ns_parent : NULL;
} while (user_ns);
/*
int lttng_enumerate_process_states(struct lttng_session *session)
{
struct task_struct *g, *p;
+ char *tmp;
+
+ tmp = (char *) __get_free_page(GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
rcu_read_lock();
for_each_process(g) {
+ struct files_struct *prev_files = NULL;
+
p = g;
do {
enum lttng_execution_mode mode =
LTTNG_UNKNOWN;
enum lttng_process_status status;
enum lttng_thread_type type;
+ struct files_struct *files;
task_lock(p);
if (p->exit_state == EXIT_ZOMBIE)
type = LTTNG_USER_THREAD;
else
type = LTTNG_KERNEL_THREAD;
+ files = p->files;
trace_lttng_statedump_process_state(session,
- p, type, mode, submode, status);
+ p, type, mode, submode, status, files);
lttng_statedump_process_ns(session,
p, type, mode, submode, status);
+ /*
+ * As an optimisation for the common case, do not
+ * repeat information for the same files_struct in
+ * two consecutive threads. This is the common case
+ * for threads sharing the same fd table. RCU guarantees
+ * that the same files_struct pointer is not re-used
+ * throughout processes/threads iteration.
+ */
+ if (files && files != prev_files) {
+ lttng_enumerate_files(session, files, tmp);
+ prev_files = files;
+ }
task_unlock(p);
} while_each_thread(g, p);
}
rcu_read_unlock();
+ free_page((unsigned long) tmp);
+
return 0;
}
trace_lttng_statedump_start(session);
ret = lttng_enumerate_process_states(session);
- if (ret)
- return ret;
- ret = lttng_enumerate_file_descriptors(session);
if (ret)
return ret;
/*