linux/kernel/trace/trace_sched_wakeup.c
<<
>>
Prefs
   1/*
   2 * trace task wakeup timings
   3 *
   4 * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
   5 * Copyright (C) 2008 Ingo Molnar <mingo@redhat.com>
   6 *
   7 * Based on code from the latency_tracer, that is:
   8 *
   9 *  Copyright (C) 2004-2006 Ingo Molnar
  10 *  Copyright (C) 2004 William Lee Irwin III
  11 */
  12#include <linux/module.h>
  13#include <linux/fs.h>
  14#include <linux/debugfs.h>
  15#include <linux/kallsyms.h>
  16#include <linux/uaccess.h>
  17#include <linux/ftrace.h>
  18#include <trace/events/sched.h>
  19
  20#include "trace.h"
  21
  22static struct trace_array       *wakeup_trace;
  23static int __read_mostly        tracer_enabled;
  24
  25static struct task_struct       *wakeup_task;
  26static int                      wakeup_cpu;
  27static int                      wakeup_current_cpu;
  28static unsigned                 wakeup_prio = -1;
  29static int                      wakeup_rt;
  30
  31static raw_spinlock_t wakeup_lock =
  32        (raw_spinlock_t)__RAW_SPIN_LOCK_UNLOCKED;
  33
  34static void __wakeup_reset(struct trace_array *tr);
  35
  36static int save_lat_flag;
  37
  38#ifdef CONFIG_FUNCTION_TRACER
  39/*
  40 * irqsoff uses its own tracer function to keep the overhead down:
  41 */
  42static void
  43wakeup_tracer_call(unsigned long ip, unsigned long parent_ip)
  44{
  45        struct trace_array *tr = wakeup_trace;
  46        struct trace_array_cpu *data;
  47        unsigned long flags;
  48        long disabled;
  49        int resched;
  50        int cpu;
  51        int pc;
  52
  53        if (likely(!wakeup_task))
  54                return;
  55
  56        pc = preempt_count();
  57        resched = ftrace_preempt_disable();
  58
  59        cpu = raw_smp_processor_id();
  60        if (cpu != wakeup_current_cpu)
  61                goto out_enable;
  62
  63        data = tr->data[cpu];
  64        disabled = atomic_inc_return(&data->disabled);
  65        if (unlikely(disabled != 1))
  66                goto out;
  67
  68        local_irq_save(flags);
  69
  70        trace_function(tr, ip, parent_ip, flags, pc);
  71
  72        local_irq_restore(flags);
  73
  74 out:
  75        atomic_dec(&data->disabled);
  76 out_enable:
  77        ftrace_preempt_enable(resched);
  78}
  79
  80static struct ftrace_ops trace_ops __read_mostly =
  81{
  82        .func = wakeup_tracer_call,
  83};
  84#endif /* CONFIG_FUNCTION_TRACER */
  85
  86/*
  87 * Should this new latency be reported/recorded?
  88 */
  89static int report_latency(cycle_t delta)
  90{
  91        if (tracing_thresh) {
  92                if (delta < tracing_thresh)
  93                        return 0;
  94        } else {
  95                if (delta <= tracing_max_latency)
  96                        return 0;
  97        }
  98        return 1;
  99}
 100
 101static void probe_wakeup_migrate_task(struct task_struct *task, int cpu)
 102{
 103        if (task != wakeup_task)
 104                return;
 105
 106        wakeup_current_cpu = cpu;
 107}
 108
 109static void notrace
 110probe_wakeup_sched_switch(struct rq *rq, struct task_struct *prev,
 111        struct task_struct *next)
 112{
 113        struct trace_array_cpu *data;
 114        cycle_t T0, T1, delta;
 115        unsigned long flags;
 116        long disabled;
 117        int cpu;
 118        int pc;
 119
 120        tracing_record_cmdline(prev);
 121
 122        if (unlikely(!tracer_enabled))
 123                return;
 124
 125        /*
 126         * When we start a new trace, we set wakeup_task to NULL
 127         * and then set tracer_enabled = 1. We want to make sure
 128         * that another CPU does not see the tracer_enabled = 1
 129         * and the wakeup_task with an older task, that might
 130         * actually be the same as next.
 131         */
 132        smp_rmb();
 133
 134        if (next != wakeup_task)
 135                return;
 136
 137        pc = preempt_count();
 138
 139        /* disable local data, not wakeup_cpu data */
 140        cpu = raw_smp_processor_id();
 141        disabled = atomic_inc_return(&wakeup_trace->data[cpu]->disabled);
 142        if (likely(disabled != 1))
 143                goto out;
 144
 145        local_irq_save(flags);
 146        __raw_spin_lock(&wakeup_lock);
 147
 148        /* We could race with grabbing wakeup_lock */
 149        if (unlikely(!tracer_enabled || next != wakeup_task))
 150                goto out_unlock;
 151
 152        /* The task we are waiting for is waking up */
 153        data = wakeup_trace->data[wakeup_cpu];
 154
 155        trace_function(wakeup_trace, CALLER_ADDR0, CALLER_ADDR1, flags, pc);
 156        tracing_sched_switch_trace(wakeup_trace, prev, next, flags, pc);
 157
 158        T0 = data->preempt_timestamp;
 159        T1 = ftrace_now(cpu);
 160        delta = T1-T0;
 161
 162        if (!report_latency(delta))
 163                goto out_unlock;
 164
 165        if (likely(!is_tracing_stopped())) {
 166                tracing_max_latency = delta;
 167                update_max_tr(wakeup_trace, wakeup_task, wakeup_cpu);
 168        }
 169
 170out_unlock:
 171        __wakeup_reset(wakeup_trace);
 172        __raw_spin_unlock(&wakeup_lock);
 173        local_irq_restore(flags);
 174out:
 175        atomic_dec(&wakeup_trace->data[cpu]->disabled);
 176}
 177
 178static void __wakeup_reset(struct trace_array *tr)
 179{
 180        wakeup_cpu = -1;
 181        wakeup_prio = -1;
 182
 183        if (wakeup_task)
 184                put_task_struct(wakeup_task);
 185
 186        wakeup_task = NULL;
 187}
 188
 189static void wakeup_reset(struct trace_array *tr)
 190{
 191        unsigned long flags;
 192
 193        tracing_reset_online_cpus(tr);
 194
 195        local_irq_save(flags);
 196        __raw_spin_lock(&wakeup_lock);
 197        __wakeup_reset(tr);
 198        __raw_spin_unlock(&wakeup_lock);
 199        local_irq_restore(flags);
 200}
 201
 202static void
 203probe_wakeup(struct rq *rq, struct task_struct *p, int success)
 204{
 205        struct trace_array_cpu *data;
 206        int cpu = smp_processor_id();
 207        unsigned long flags;
 208        long disabled;
 209        int pc;
 210
 211        if (likely(!tracer_enabled))
 212                return;
 213
 214        tracing_record_cmdline(p);
 215        tracing_record_cmdline(current);
 216
 217        if ((wakeup_rt && !rt_task(p)) ||
 218                        p->prio >= wakeup_prio ||
 219                        p->prio >= current->prio)
 220                return;
 221
 222        pc = preempt_count();
 223        disabled = atomic_inc_return(&wakeup_trace->data[cpu]->disabled);
 224        if (unlikely(disabled != 1))
 225                goto out;
 226
 227        /* interrupts should be off from try_to_wake_up */
 228        __raw_spin_lock(&wakeup_lock);
 229
 230        /* check for races. */
 231        if (!tracer_enabled || p->prio >= wakeup_prio)
 232                goto out_locked;
 233
 234        /* reset the trace */
 235        __wakeup_reset(wakeup_trace);
 236
 237        wakeup_cpu = task_cpu(p);
 238        wakeup_current_cpu = wakeup_cpu;
 239        wakeup_prio = p->prio;
 240
 241        wakeup_task = p;
 242        get_task_struct(wakeup_task);
 243
 244        local_save_flags(flags);
 245
 246        data = wakeup_trace->data[wakeup_cpu];
 247        data->preempt_timestamp = ftrace_now(cpu);
 248        tracing_sched_wakeup_trace(wakeup_trace, p, current, flags, pc);
 249
 250        /*
 251         * We must be careful in using CALLER_ADDR2. But since wake_up
 252         * is not called by an assembly function  (where as schedule is)
 253         * it should be safe to use it here.
 254         */
 255        trace_function(wakeup_trace, CALLER_ADDR1, CALLER_ADDR2, flags, pc);
 256
 257out_locked:
 258        __raw_spin_unlock(&wakeup_lock);
 259out:
 260        atomic_dec(&wakeup_trace->data[cpu]->disabled);
 261}
 262
 263static void start_wakeup_tracer(struct trace_array *tr)
 264{
 265        int ret;
 266
 267        ret = register_trace_sched_wakeup(probe_wakeup);
 268        if (ret) {
 269                pr_info("wakeup trace: Couldn't activate tracepoint"
 270                        " probe to kernel_sched_wakeup\n");
 271                return;
 272        }
 273
 274        ret = register_trace_sched_wakeup_new(probe_wakeup);
 275        if (ret) {
 276                pr_info("wakeup trace: Couldn't activate tracepoint"
 277                        " probe to kernel_sched_wakeup_new\n");
 278                goto fail_deprobe;
 279        }
 280
 281        ret = register_trace_sched_switch(probe_wakeup_sched_switch);
 282        if (ret) {
 283                pr_info("sched trace: Couldn't activate tracepoint"
 284                        " probe to kernel_sched_switch\n");
 285                goto fail_deprobe_wake_new;
 286        }
 287
 288        ret = register_trace_sched_migrate_task(probe_wakeup_migrate_task);
 289        if (ret) {
 290                pr_info("wakeup trace: Couldn't activate tracepoint"
 291                        " probe to kernel_sched_migrate_task\n");
 292                return;
 293        }
 294
 295        wakeup_reset(tr);
 296
 297        /*
 298         * Don't let the tracer_enabled = 1 show up before
 299         * the wakeup_task is reset. This may be overkill since
 300         * wakeup_reset does a spin_unlock after setting the
 301         * wakeup_task to NULL, but I want to be safe.
 302         * This is a slow path anyway.
 303         */
 304        smp_wmb();
 305
 306        register_ftrace_function(&trace_ops);
 307
 308        if (tracing_is_enabled())
 309                tracer_enabled = 1;
 310        else
 311                tracer_enabled = 0;
 312
 313        return;
 314fail_deprobe_wake_new:
 315        unregister_trace_sched_wakeup_new(probe_wakeup);
 316fail_deprobe:
 317        unregister_trace_sched_wakeup(probe_wakeup);
 318}
 319
 320static void stop_wakeup_tracer(struct trace_array *tr)
 321{
 322        tracer_enabled = 0;
 323        unregister_ftrace_function(&trace_ops);
 324        unregister_trace_sched_switch(probe_wakeup_sched_switch);
 325        unregister_trace_sched_wakeup_new(probe_wakeup);
 326        unregister_trace_sched_wakeup(probe_wakeup);
 327        unregister_trace_sched_migrate_task(probe_wakeup_migrate_task);
 328}
 329
 330static int __wakeup_tracer_init(struct trace_array *tr)
 331{
 332        save_lat_flag = trace_flags & TRACE_ITER_LATENCY_FMT;
 333        trace_flags |= TRACE_ITER_LATENCY_FMT;
 334
 335        tracing_max_latency = 0;
 336        wakeup_trace = tr;
 337        start_wakeup_tracer(tr);
 338        return 0;
 339}
 340
 341static int wakeup_tracer_init(struct trace_array *tr)
 342{
 343        wakeup_rt = 0;
 344        return __wakeup_tracer_init(tr);
 345}
 346
 347static int wakeup_rt_tracer_init(struct trace_array *tr)
 348{
 349        wakeup_rt = 1;
 350        return __wakeup_tracer_init(tr);
 351}
 352
 353static void wakeup_tracer_reset(struct trace_array *tr)
 354{
 355        stop_wakeup_tracer(tr);
 356        /* make sure we put back any tasks we are tracing */
 357        wakeup_reset(tr);
 358
 359        if (!save_lat_flag)
 360                trace_flags &= ~TRACE_ITER_LATENCY_FMT;
 361}
 362
 363static void wakeup_tracer_start(struct trace_array *tr)
 364{
 365        wakeup_reset(tr);
 366        tracer_enabled = 1;
 367}
 368
 369static void wakeup_tracer_stop(struct trace_array *tr)
 370{
 371        tracer_enabled = 0;
 372}
 373
 374static struct tracer wakeup_tracer __read_mostly =
 375{
 376        .name           = "wakeup",
 377        .init           = wakeup_tracer_init,
 378        .reset          = wakeup_tracer_reset,
 379        .start          = wakeup_tracer_start,
 380        .stop           = wakeup_tracer_stop,
 381        .print_max      = 1,
 382#ifdef CONFIG_FTRACE_SELFTEST
 383        .selftest    = trace_selftest_startup_wakeup,
 384#endif
 385};
 386
 387static struct tracer wakeup_rt_tracer __read_mostly =
 388{
 389        .name           = "wakeup_rt",
 390        .init           = wakeup_rt_tracer_init,
 391        .reset          = wakeup_tracer_reset,
 392        .start          = wakeup_tracer_start,
 393        .stop           = wakeup_tracer_stop,
 394        .wait_pipe      = poll_wait_pipe,
 395        .print_max      = 1,
 396#ifdef CONFIG_FTRACE_SELFTEST
 397        .selftest    = trace_selftest_startup_wakeup,
 398#endif
 399};
 400
 401__init static int init_wakeup_tracer(void)
 402{
 403        int ret;
 404
 405        ret = register_tracer(&wakeup_tracer);
 406        if (ret)
 407                return ret;
 408
 409        ret = register_tracer(&wakeup_rt_tracer);
 410        if (ret)
 411                return ret;
 412
 413        return 0;
 414}
 415device_initcall(init_wakeup_tracer);
 416
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.