buffer_sync.c 13.3 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3
/**
 * @file buffer_sync.c
 *
4
 * @remark Copyright 2002-2009 OProfile authors
L
Linus Torvalds 已提交
5 6 7
 * @remark Read the file COPYING
 *
 * @author John Levon <levon@movementarian.org>
8
 * @author Barry Kasindorf
9
 * @author Robert Richter <robert.richter@amd.com>
L
Linus Torvalds 已提交
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
 *
 * This is the core of the buffer management. Each
 * CPU buffer is processed and entered into the
 * global event buffer. Such processing is necessary
 * in several circumstances, mentioned below.
 *
 * The processing does the job of converting the
 * transitory EIP value into a persistent dentry/offset
 * value that the profiler can record at its leisure.
 *
 * See fs/dcookies.c for a description of the dentry/offset
 * objects.
 */

#include <linux/mm.h>
#include <linux/workqueue.h>
#include <linux/notifier.h>
#include <linux/dcookies.h>
#include <linux/profile.h>
#include <linux/module.h>
#include <linux/fs.h>
31
#include <linux/oprofile.h>
A
Alexey Dobriyan 已提交
32
#include <linux/sched.h>
33

L
Linus Torvalds 已提交
34 35 36 37
#include "oprofile_stats.h"
#include "event_buffer.h"
#include "cpu_buffer.h"
#include "buffer_sync.h"
38

L
Linus Torvalds 已提交
39 40 41 42 43 44 45 46 47 48
static LIST_HEAD(dying_tasks);
static LIST_HEAD(dead_tasks);
static cpumask_t marked_cpus = CPU_MASK_NONE;
static DEFINE_SPINLOCK(task_mortuary);
static void process_task_mortuary(void);

/* Take ownership of the task struct and place it on the
 * list for processing. Only after two full buffer syncs
 * does the task eventually get freed, because by then
 * we are sure we will not reference it again.
49 50
 * Can be invoked from softirq via RCU callback due to
 * call_rcu() of the task struct, hence the _irqsave.
L
Linus Torvalds 已提交
51
 */
52 53
static int
task_free_notify(struct notifier_block *self, unsigned long val, void *data)
L
Linus Torvalds 已提交
54
{
55
	unsigned long flags;
56
	struct task_struct *task = data;
57
	spin_lock_irqsave(&task_mortuary, flags);
L
Linus Torvalds 已提交
58
	list_add(&task->tasks, &dying_tasks);
59
	spin_unlock_irqrestore(&task_mortuary, flags);
L
Linus Torvalds 已提交
60 61 62 63 64 65 66
	return NOTIFY_OK;
}


/* The task is on its way out. A sync of the buffer means we can catch
 * any remaining samples for this task.
 */
67 68
static int
task_exit_notify(struct notifier_block *self, unsigned long val, void *data)
L
Linus Torvalds 已提交
69 70 71 72
{
	/* To avoid latency problems, we only process the current CPU,
	 * hoping that most samples for the task are on this CPU
	 */
I
Ingo Molnar 已提交
73
	sync_buffer(raw_smp_processor_id());
74
	return 0;
L
Linus Torvalds 已提交
75 76 77 78 79 80 81 82
}


/* The task is about to try a do_munmap(). We peek at what it's going to
 * do, and if it's an executable region, process the samples first, so
 * we don't lose any. This does not have to be exact, it's a QoI issue
 * only.
 */
83 84
static int
munmap_notify(struct notifier_block *self, unsigned long val, void *data)
L
Linus Torvalds 已提交
85 86
{
	unsigned long addr = (unsigned long)data;
87 88
	struct mm_struct *mm = current->mm;
	struct vm_area_struct *mpnt;
L
Linus Torvalds 已提交
89 90 91 92 93 94 95 96 97

	down_read(&mm->mmap_sem);

	mpnt = find_vma(mm, addr);
	if (mpnt && mpnt->vm_file && (mpnt->vm_flags & VM_EXEC)) {
		up_read(&mm->mmap_sem);
		/* To avoid latency problems, we only process the current CPU,
		 * hoping that most samples for the task are on this CPU
		 */
I
Ingo Molnar 已提交
98
		sync_buffer(raw_smp_processor_id());
L
Linus Torvalds 已提交
99 100 101 102 103 104 105
		return 0;
	}

	up_read(&mm->mmap_sem);
	return 0;
}

106

L
Linus Torvalds 已提交
107 108 109
/* We need to be told about new modules so we don't attribute to a previously
 * loaded module, or drop the samples on the floor.
 */
110 111
static int
module_load_notify(struct notifier_block *self, unsigned long val, void *data)
L
Linus Torvalds 已提交
112 113 114 115 116 117
{
#ifdef CONFIG_MODULES
	if (val != MODULE_STATE_COMING)
		return 0;

	/* FIXME: should we process all CPU buffers ? */
118
	mutex_lock(&buffer_mutex);
L
Linus Torvalds 已提交
119 120
	add_event_entry(ESCAPE_CODE);
	add_event_entry(MODULE_LOADED_CODE);
121
	mutex_unlock(&buffer_mutex);
L
Linus Torvalds 已提交
122 123 124 125
#endif
	return 0;
}

126

L
Linus Torvalds 已提交
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
static struct notifier_block task_free_nb = {
	.notifier_call	= task_free_notify,
};

static struct notifier_block task_exit_nb = {
	.notifier_call	= task_exit_notify,
};

static struct notifier_block munmap_nb = {
	.notifier_call	= munmap_notify,
};

static struct notifier_block module_load_nb = {
	.notifier_call = module_load_notify,
};

143

L
Linus Torvalds 已提交
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
static void end_sync(void)
{
	end_cpu_work();
	/* make sure we don't leak task structs */
	process_task_mortuary();
	process_task_mortuary();
}


int sync_start(void)
{
	int err;

	start_cpu_work();

	err = task_handoff_register(&task_free_nb);
	if (err)
		goto out1;
	err = profile_event_register(PROFILE_TASK_EXIT, &task_exit_nb);
	if (err)
		goto out2;
	err = profile_event_register(PROFILE_MUNMAP, &munmap_nb);
	if (err)
		goto out3;
	err = register_module_notifier(&module_load_nb);
	if (err)
		goto out4;

out:
	return err;
out4:
	profile_event_unregister(PROFILE_MUNMAP, &munmap_nb);
out3:
	profile_event_unregister(PROFILE_TASK_EXIT, &task_exit_nb);
out2:
	task_handoff_unregister(&task_free_nb);
out1:
	end_sync();
	goto out;
}


void sync_stop(void)
{
	unregister_module_notifier(&module_load_nb);
	profile_event_unregister(PROFILE_MUNMAP, &munmap_nb);
	profile_event_unregister(PROFILE_TASK_EXIT, &task_exit_nb);
	task_handoff_unregister(&task_free_nb);
	end_sync();
}

195

L
Linus Torvalds 已提交
196 197 198 199
/* Optimisation. We can manage without taking the dcookie sem
 * because we cannot reach this code without at least one
 * dcookie user still being registered (namely, the reader
 * of the event buffer). */
200
static inline unsigned long fast_get_dcookie(struct path *path)
L
Linus Torvalds 已提交
201 202
{
	unsigned long cookie;
203

N
Nick Piggin 已提交
204
	if (path->dentry->d_flags & DCACHE_COOKIE)
205 206
		return (unsigned long)path->dentry;
	get_dcookie(path, &cookie);
L
Linus Torvalds 已提交
207 208 209
	return cookie;
}

210

L
Linus Torvalds 已提交
211 212 213 214 215
/* Look up the dcookie for the task's first VM_EXECUTABLE mapping,
 * which corresponds loosely to "application name". This is
 * not strictly necessary but allows oprofile to associate
 * shared-library samples with particular applications
 */
216
static unsigned long get_exec_dcookie(struct mm_struct *mm)
L
Linus Torvalds 已提交
217
{
218
	unsigned long cookie = NO_COOKIE;
219 220
	struct vm_area_struct *vma;

L
Linus Torvalds 已提交
221 222
	if (!mm)
		goto out;
223

L
Linus Torvalds 已提交
224 225 226 227 228
	for (vma = mm->mmap; vma; vma = vma->vm_next) {
		if (!vma->vm_file)
			continue;
		if (!(vma->vm_flags & VM_EXECUTABLE))
			continue;
229
		cookie = fast_get_dcookie(&vma->vm_file->f_path);
L
Linus Torvalds 已提交
230 231 232 233 234 235 236 237 238 239 240 241 242
		break;
	}

out:
	return cookie;
}


/* Convert the EIP value of a sample into a persistent dentry/offset
 * pair that can then be added to the global event buffer. We make
 * sure to do this lookup before a mm->mmap modification happens so
 * we don't lose track.
 */
243 244
static unsigned long
lookup_dcookie(struct mm_struct *mm, unsigned long addr, off_t *offset)
L
Linus Torvalds 已提交
245
{
246
	unsigned long cookie = NO_COOKIE;
247
	struct vm_area_struct *vma;
L
Linus Torvalds 已提交
248 249

	for (vma = find_vma(mm, addr); vma; vma = vma->vm_next) {
250

L
Linus Torvalds 已提交
251 252 253
		if (addr < vma->vm_start || addr >= vma->vm_end)
			continue;

254
		if (vma->vm_file) {
255
			cookie = fast_get_dcookie(&vma->vm_file->f_path);
256 257 258 259 260 261 262
			*offset = (vma->vm_pgoff << PAGE_SHIFT) + addr -
				vma->vm_start;
		} else {
			/* must be an anonymous map */
			*offset = addr;
		}

L
Linus Torvalds 已提交
263 264 265
		break;
	}

266 267 268
	if (!vma)
		cookie = INVALID_COOKIE;

L
Linus Torvalds 已提交
269 270 271
	return cookie;
}

272
static unsigned long last_cookie = INVALID_COOKIE;
273

L
Linus Torvalds 已提交
274 275 276 277 278
static void add_cpu_switch(int i)
{
	add_event_entry(ESCAPE_CODE);
	add_event_entry(CPU_SWITCH_CODE);
	add_event_entry(i);
279
	last_cookie = INVALID_COOKIE;
L
Linus Torvalds 已提交
280 281 282 283 284 285
}

static void add_kernel_ctx_switch(unsigned int in_kernel)
{
	add_event_entry(ESCAPE_CODE);
	if (in_kernel)
286
		add_event_entry(KERNEL_ENTER_SWITCH_CODE);
L
Linus Torvalds 已提交
287
	else
288
		add_event_entry(KERNEL_EXIT_SWITCH_CODE);
L
Linus Torvalds 已提交
289
}
290

L
Linus Torvalds 已提交
291
static void
292
add_user_ctx_switch(struct task_struct const *task, unsigned long cookie)
L
Linus Torvalds 已提交
293 294
{
	add_event_entry(ESCAPE_CODE);
295
	add_event_entry(CTX_SWITCH_CODE);
L
Linus Torvalds 已提交
296 297 298 299 300 301 302 303
	add_event_entry(task->pid);
	add_event_entry(cookie);
	/* Another code for daemon back-compat */
	add_event_entry(ESCAPE_CODE);
	add_event_entry(CTX_TGID_CODE);
	add_event_entry(task->tgid);
}

304

L
Linus Torvalds 已提交
305 306 307 308 309 310 311
static void add_cookie_switch(unsigned long cookie)
{
	add_event_entry(ESCAPE_CODE);
	add_event_entry(COOKIE_SWITCH_CODE);
	add_event_entry(cookie);
}

312

L
Linus Torvalds 已提交
313 314 315 316 317 318
static void add_trace_begin(void)
{
	add_event_entry(ESCAPE_CODE);
	add_event_entry(TRACE_BEGIN_CODE);
}

319
static void add_data(struct op_entry *entry, struct mm_struct *mm)
320
{
321 322
	unsigned long code, pc, val;
	unsigned long cookie;
323 324
	off_t offset;

325 326 327 328 329
	if (!op_cpu_buffer_get_data(entry, &code))
		return;
	if (!op_cpu_buffer_get_data(entry, &pc))
		return;
	if (!op_cpu_buffer_get_size(entry))
330
		return;
331 332

	if (mm) {
333
		cookie = lookup_dcookie(mm, pc, &offset);
334

335 336 337
		if (cookie == NO_COOKIE)
			offset = pc;
		if (cookie == INVALID_COOKIE) {
338
			atomic_inc(&oprofile_stats.sample_lost_no_mapping);
339
			offset = pc;
340
		}
341 342 343
		if (cookie != last_cookie) {
			add_cookie_switch(cookie);
			last_cookie = cookie;
344 345
		}
	} else
346
		offset = pc;
347 348 349 350 351

	add_event_entry(ESCAPE_CODE);
	add_event_entry(code);
	add_event_entry(offset);	/* Offset from Dcookie */

352 353
	while (op_cpu_buffer_get_data(entry, &val))
		add_event_entry(val);
354
}
L
Linus Torvalds 已提交
355

356
static inline void add_sample_entry(unsigned long offset, unsigned long event)
L
Linus Torvalds 已提交
357 358 359 360 361 362
{
	add_event_entry(offset);
	add_event_entry(event);
}


R
Robert Richter 已提交
363 364 365 366 367 368 369
/*
 * Add a sample to the global event buffer. If possible the
 * sample is converted into a persistent dentry/offset pair
 * for later lookup from userspace. Return 0 on failure.
 */
static int
add_sample(struct mm_struct *mm, struct op_sample *s, int in_kernel)
L
Linus Torvalds 已提交
370 371 372
{
	unsigned long cookie;
	off_t offset;
373

R
Robert Richter 已提交
374 375 376 377 378 379 380 381 382 383 384 385
	if (in_kernel) {
		add_sample_entry(s->eip, s->event);
		return 1;
	}

	/* add userspace sample */

	if (!mm) {
		atomic_inc(&oprofile_stats.sample_lost_no_mm);
		return 0;
	}

386 387
	cookie = lookup_dcookie(mm, s->eip, &offset);

388
	if (cookie == INVALID_COOKIE) {
L
Linus Torvalds 已提交
389 390 391 392 393 394 395 396 397 398 399 400 401 402
		atomic_inc(&oprofile_stats.sample_lost_no_mapping);
		return 0;
	}

	if (cookie != last_cookie) {
		add_cookie_switch(cookie);
		last_cookie = cookie;
	}

	add_sample_entry(offset, s->event);

	return 1;
}

403 404

static void release_mm(struct mm_struct *mm)
L
Linus Torvalds 已提交
405 406 407 408 409 410 411 412
{
	if (!mm)
		return;
	up_read(&mm->mmap_sem);
	mmput(mm);
}


413
static struct mm_struct *take_tasks_mm(struct task_struct *task)
L
Linus Torvalds 已提交
414
{
415
	struct mm_struct *mm = get_task_mm(task);
L
Linus Torvalds 已提交
416 417 418 419 420 421 422 423 424 425
	if (mm)
		down_read(&mm->mmap_sem);
	return mm;
}


static inline int is_code(unsigned long val)
{
	return val == ESCAPE_CODE;
}
426

L
Linus Torvalds 已提交
427 428 429 430 431 432 433 434 435

/* Move tasks along towards death. Any tasks on dead_tasks
 * will definitely have no remaining references in any
 * CPU buffers at this point, because we use two lists,
 * and to have reached the list, it must have gone through
 * one full sync already.
 */
static void process_task_mortuary(void)
{
436 437
	unsigned long flags;
	LIST_HEAD(local_dead_tasks);
438 439
	struct task_struct *task;
	struct task_struct *ttask;
L
Linus Torvalds 已提交
440

441
	spin_lock_irqsave(&task_mortuary, flags);
L
Linus Torvalds 已提交
442

443 444
	list_splice_init(&dead_tasks, &local_dead_tasks);
	list_splice_init(&dying_tasks, &dead_tasks);
L
Linus Torvalds 已提交
445

446 447 448
	spin_unlock_irqrestore(&task_mortuary, flags);

	list_for_each_entry_safe(task, ttask, &local_dead_tasks, tasks) {
L
Linus Torvalds 已提交
449
		list_del(&task->tasks);
450
		free_task(task);
L
Linus Torvalds 已提交
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
	}
}


static void mark_done(int cpu)
{
	int i;

	cpu_set(cpu, marked_cpus);

	for_each_online_cpu(i) {
		if (!cpu_isset(i, marked_cpus))
			return;
	}

	/* All CPUs have been processed at least once,
	 * we can process the mortuary once
	 */
	process_task_mortuary();

	cpus_clear(marked_cpus);
}


/* FIXME: this is not sufficient if we implement syscall barrier backtrace
 * traversal, the code switch to sb_sample_start at first kernel enter/exit
 * switch so we need a fifth state and some special handling in sync_buffer()
 */
typedef enum {
	sb_bt_ignore = -2,
	sb_buffer_start,
	sb_bt_start,
	sb_sample_start,
} sync_buffer_state;

/* Sync one of the CPU's buffers into the global event buffer.
 * Here we need to go through each batch of samples punctuated
 * by context switch notes, taking the task's mmap_sem and doing
 * lookup in task->mm->mmap to convert EIP into dcookie/offset
 * value.
 */
void sync_buffer(int cpu)
{
	struct mm_struct *mm = NULL;
495
	struct mm_struct *oldmm;
496
	unsigned long val;
497
	struct task_struct *new;
L
Linus Torvalds 已提交
498 499 500
	unsigned long cookie = 0;
	int in_kernel = 1;
	sync_buffer_state state = sb_buffer_start;
501
	unsigned int i;
L
Linus Torvalds 已提交
502
	unsigned long available;
503
	unsigned long flags;
504 505
	struct op_entry entry;
	struct op_sample *sample;
L
Linus Torvalds 已提交
506

507
	mutex_lock(&buffer_mutex);
508

L
Linus Torvalds 已提交
509 510
	add_cpu_switch(cpu);

511 512
	op_cpu_buffer_reset(cpu);
	available = op_cpu_buffer_entries(cpu);
L
Linus Torvalds 已提交
513 514

	for (i = 0; i < available; ++i) {
515 516
		sample = op_cpu_buffer_read_entry(&entry, cpu);
		if (!sample)
517
			break;
518

519
		if (is_code(sample->eip)) {
520 521 522 523 524 525
			flags = sample->event;
			if (flags & TRACE_BEGIN) {
				state = sb_bt_start;
				add_trace_begin();
			}
			if (flags & KERNEL_CTX_SWITCH) {
L
Linus Torvalds 已提交
526
				/* kernel/userspace switch */
527
				in_kernel = flags & IS_KERNEL;
L
Linus Torvalds 已提交
528 529
				if (state == sb_buffer_start)
					state = sb_sample_start;
530 531
				add_kernel_ctx_switch(flags & IS_KERNEL);
			}
532 533
			if (flags & USER_CTX_SWITCH
			    && op_cpu_buffer_get_data(&entry, &val)) {
L
Linus Torvalds 已提交
534
				/* userspace context switch */
535
				new = (struct task_struct *)val;
536
				oldmm = mm;
L
Linus Torvalds 已提交
537 538 539 540 541 542
				release_mm(oldmm);
				mm = take_tasks_mm(new);
				if (mm != oldmm)
					cookie = get_exec_dcookie(mm);
				add_user_ctx_switch(new, cookie);
			}
543 544
			if (op_cpu_buffer_get_size(&entry))
				add_data(&entry, mm);
R
Robert Richter 已提交
545 546 547 548 549 550 551
			continue;
		}

		if (state < sb_bt_start)
			/* ignore sample */
			continue;

552
		if (add_sample(mm, sample, in_kernel))
R
Robert Richter 已提交
553 554 555 556 557 558
			continue;

		/* ignore backtraces if failed to add a sample */
		if (state == sb_bt_start) {
			state = sb_bt_ignore;
			atomic_inc(&oprofile_stats.bt_lost_no_mapping);
L
Linus Torvalds 已提交
559 560 561 562 563 564
		}
	}
	release_mm(mm);

	mark_done(cpu);

565
	mutex_unlock(&buffer_mutex);
L
Linus Torvalds 已提交
566
}
567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590

/* The function can be used to add a buffer worth of data directly to
 * the kernel buffer. The buffer is assumed to be a circular buffer.
 * Take the entries from index start and end at index end, wrapping
 * at max_entries.
 */
void oprofile_put_buff(unsigned long *buf, unsigned int start,
		       unsigned int stop, unsigned int max)
{
	int i;

	i = start;

	mutex_lock(&buffer_mutex);
	while (i != stop) {
		add_event_entry(buf[i++]);

		if (i >= max)
			i = 0;
	}

	mutex_unlock(&buffer_mutex);
}