Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/kernel/trace/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 112 kB image not shown  

Quelle  trace_events.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * event tracer
 *
 * Copyright (C) 2008 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
 *
 *  - Added format output of fields of the trace point.
 *    This was based off of work by Tom Zanussi <tzanussi@gmail.com>.
 *
 */


#define pr_fmt(fmt) fmt

#include <linux/workqueue.h>
#include <linux/security.h>
#include <linux/spinlock.h>
#include <linux/kthread.h>
#include <linux/tracefs.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/ctype.h>
#include <linux/sort.h>
#include <linux/slab.h>
#include <linux/delay.h>

#include <trace/events/sched.h>
#include <trace/syscall.h>

#include <asm/setup.h>

#include "trace_output.h"

#undef TRACE_SYSTEM
#define TRACE_SYSTEM "TRACE_SYSTEM"

DEFINE_MUTEX(event_mutex);

LIST_HEAD(ftrace_events);
static LIST_HEAD(ftrace_generic_fields);
static LIST_HEAD(ftrace_common_fields);
static bool eventdir_initialized;

static LIST_HEAD(module_strings);

struct module_string {
 struct list_head next;
 struct module  *module;
 char   *str;
};

#define GFP_TRACE (GFP_KERNEL | __GFP_ZERO)

static struct kmem_cache *field_cachep;
static struct kmem_cache *file_cachep;

static inline int system_refcount(struct event_subsystem *system)
{
 return system->ref_count;
}

static int system_refcount_inc(struct event_subsystem *system)
{
 return system->ref_count++;
}

static int system_refcount_dec(struct event_subsystem *system)
{
 return --system->ref_count;
}

/* Double loops, do not use break, only goto's work */
#define do_for_each_event_file(tr, file)   \
 list_for_each_entry(tr, &ftrace_trace_arrays, list) { \
  list_for_each_entry(file, &tr->events, list)

#define do_for_each_event_file_safe(tr, file)   \
 list_for_each_entry(tr, &ftrace_trace_arrays, list) { \
  struct trace_event_file *___n;    \
  list_for_each_entry_safe(file, ___n, &tr->events, list)

#define while_for_each_event_file()  \
 }

static struct ftrace_event_field *
__find_event_field(struct list_head *head, const char *name)
{
 struct ftrace_event_field *field;

 list_for_each_entry(field, head, link) {
  if (!strcmp(field->name, name))
   return field;
 }

 return NULL;
}

struct ftrace_event_field *
trace_find_event_field(struct trace_event_call *call, char *name)
{
 struct ftrace_event_field *field;
 struct list_head *head;

 head = trace_get_fields(call);
 field = __find_event_field(head, name);
 if (field)
  return field;

 field = __find_event_field(&ftrace_generic_fields, name);
 if (field)
  return field;

 return __find_event_field(&ftrace_common_fields, name);
}

static int __trace_define_field(struct list_head *head, const char *type,
    const char *name, int offset, int size,
    int is_signed, int filter_type, int len,
    int need_test)
{
 struct ftrace_event_field *field;

 field = kmem_cache_alloc(field_cachep, GFP_TRACE);
 if (!field)
  return -ENOMEM;

 field->name = name;
 field->type = type;

 if (filter_type == FILTER_OTHER)
  field->filter_type = filter_assign_type(type);
 else
  field->filter_type = filter_type;

 field->offset = offset;
 field->size = size;
 field->is_signed = is_signed;
 field->needs_test = need_test;
 field->len = len;

 list_add(&field->link, head);

 return 0;
}

int trace_define_field(struct trace_event_call *call, const char *type,
         const char *name, int offset, int size, int is_signed,
         int filter_type)
{
 struct list_head *head;

 if (WARN_ON(!call->class))
  return 0;

 head = trace_get_fields(call);
 return __trace_define_field(head, type, name, offset, size,
        is_signed, filter_type, 0, 0);
}
EXPORT_SYMBOL_GPL(trace_define_field);

static int trace_define_field_ext(struct trace_event_call *call, const char *type,
         const char *name, int offset, int size, int is_signed,
         int filter_type, int len, int need_test)
{
 struct list_head *head;

 if (WARN_ON(!call->class))
  return 0;

 head = trace_get_fields(call);
 return __trace_define_field(head, type, name, offset, size,
        is_signed, filter_type, len, need_test);
}

#define __generic_field(type, item, filter_type)   \
 ret = __trace_define_field(&ftrace_generic_fields, #type, \
       #item, 0, 0, is_signed_type(type), \
       filter_type, 0, 0);   \
 if (ret)       \
  return ret;

#define __common_field(type, item)     \
 ret = __trace_define_field(&ftrace_common_fields, #type, \
       "common_" #item,   \
       offsetof(typeof(ent), item),  \
       sizeof(ent.item),   \
       is_signed_type(type), FILTER_OTHER, \
       0, 0);    \
 if (ret)       \
  return ret;

static int trace_define_generic_fields(void)
{
 int ret;

 __generic_field(int, CPU, FILTER_CPU);
 __generic_field(int, cpu, FILTER_CPU);
 __generic_field(int, common_cpu, FILTER_CPU);
 __generic_field(char *, COMM, FILTER_COMM);
 __generic_field(char *, comm, FILTER_COMM);
 __generic_field(char *, stacktrace, FILTER_STACKTRACE);
 __generic_field(char *, STACKTRACE, FILTER_STACKTRACE);

 return ret;
}

static int trace_define_common_fields(void)
{
 int ret;
 struct trace_entry ent;

 __common_field(unsigned short, type);
 __common_field(unsigned char, flags);
 /* Holds both preempt_count and migrate_disable */
 __common_field(unsigned char, preempt_count);
 __common_field(int, pid);

 return ret;
}

static void trace_destroy_fields(struct trace_event_call *call)
{
 struct ftrace_event_field *field, *next;
 struct list_head *head;

 head = trace_get_fields(call);
 list_for_each_entry_safe(field, next, head, link) {
  list_del(&field->link);
  kmem_cache_free(field_cachep, field);
 }
}

/*
 * run-time version of trace_event_get_offsets_<call>() that returns the last
 * accessible offset of trace fields excluding __dynamic_array bytes
 */

int trace_event_get_offsets(struct trace_event_call *call)
{
 struct ftrace_event_field *tail;
 struct list_head *head;

 head = trace_get_fields(call);
 /*
 * head->next points to the last field with the largest offset,
 * since it was added last by trace_define_field()
 */

 tail = list_first_entry(head, struct ftrace_event_field, link);
 return tail->offset + tail->size;
}


static struct trace_event_fields *find_event_field(const char *fmt,
         struct trace_event_call *call)
{
 struct trace_event_fields *field = call->class->fields_array;
 const char *p = fmt;
 int len;

 if (!(len = str_has_prefix(fmt, "REC->")))
  return NULL;
 fmt += len;
 for (p = fmt; *p; p++) {
  if (!isalnum(*p) && *p != '_')
   break;
 }
 len = p - fmt;

 for (; field->type; field++) {
  if (strncmp(field->name, fmt, len) || field->name[len])
   continue;

  return field;
 }
 return NULL;
}

/*
 * Check if the referenced field is an array and return true,
 * as arrays are OK to dereference.
 */

static bool test_field(const char *fmt, struct trace_event_call *call)
{
 struct trace_event_fields *field;

 field = find_event_field(fmt, call);
 if (!field)
  return false;

 /* This is an array and is OK to dereference. */
 return strchr(field->type, '[') != NULL;
}

/* Look for a string within an argument */
static bool find_print_string(const char *arg, const char *str, const char *end)
{
 const char *r;

 r = strstr(arg, str);
 return r && r < end;
}

/* Return true if the argument pointer is safe */
static bool process_pointer(const char *fmt, int len, struct trace_event_call *call)
{
 const char *r, *e, *a;

 e = fmt + len;

 /* Find the REC-> in the argument */
 r = strstr(fmt, "REC->");
 if (r && r < e) {
  /*
 * Addresses of events on the buffer, or an array on the buffer is
 * OK to dereference. There's ways to fool this, but
 * this is to catch common mistakes, not malicious code.
 */

  a = strchr(fmt, '&');
  if ((a && (a < r)) || test_field(r, call))
   return true;
 } else if (find_print_string(fmt, "__get_dynamic_array(", e)) {
  return true;
 } else if (find_print_string(fmt, "__get_rel_dynamic_array(", e)) {
  return true;
 } else if (find_print_string(fmt, "__get_dynamic_array_len(", e)) {
  return true;
 } else if (find_print_string(fmt, "__get_rel_dynamic_array_len(", e)) {
  return true;
 } else if (find_print_string(fmt, "__get_sockaddr(", e)) {
  return true;
 } else if (find_print_string(fmt, "__get_rel_sockaddr(", e)) {
  return true;
 }
 return false;
}

/* Return true if the string is safe */
static bool process_string(const char *fmt, int len, struct trace_event_call *call)
{
 struct trace_event_fields *field;
 const char *r, *e, *s;

 e = fmt + len;

 /*
 * There are several helper functions that return strings.
 * If the argument contains a function, then assume its field is valid.
 * It is considered that the argument has a function if it has:
 *   alphanumeric or '_' before a parenthesis.
 */

 s = fmt;
 do {
  r = strstr(s, "(");
  if (!r || r >= e)
   break;
  for (int i = 1; r - i >= s; i++) {
   char ch = *(r - i);
   if (isspace(ch))
    continue;
   if (isalnum(ch) || ch == '_')
    return true;
   /* Anything else, this isn't a function */
   break;
  }
  /* A function could be wrapped in parethesis, try the next one */
  s = r + 1;
 } while (s < e);

 /*
 * Check for arrays. If the argument has: foo[REC->val]
 * then it is very likely that foo is an array of strings
 * that are safe to use.
 */

 r = strstr(s, "[");
 if (r && r < e) {
  r = strstr(r, "REC->");
  if (r && r < e)
   return true;
 }

 /*
 * If there's any strings in the argument consider this arg OK as it
 * could be: REC->field ? "foo" : "bar" and we don't want to get into
 * verifying that logic here.
 */

 if (find_print_string(fmt, "\"", e))
  return true;

 /* Dereferenced strings are also valid like any other pointer */
 if (process_pointer(fmt, len, call))
  return true;

 /* Make sure the field is found */
 field = find_event_field(fmt, call);
 if (!field)
  return false;

 /* Test this field's string before printing the event */
 call->flags |= TRACE_EVENT_FL_TEST_STR;
 field->needs_test = 1;

 return true;
}

static void handle_dereference_arg(const char *arg_str, u64 string_flags, int len,
       u64 *dereference_flags, int arg,
       struct trace_event_call *call)
{
 if (string_flags & (1ULL << arg)) {
  if (process_string(arg_str, len, call))
   *dereference_flags &= ~(1ULL << arg);
 } else if (process_pointer(arg_str, len, call))
  *dereference_flags &= ~(1ULL << arg);
 else
  pr_warn("TRACE EVENT ERROR: Bad dereference argument: '%.*s'\n",
   len, arg_str);
}

/*
 * Examine the print fmt of the event looking for unsafe dereference
 * pointers using %p* that could be recorded in the trace event and
 * much later referenced after the pointer was freed. Dereferencing
 * pointers are OK, if it is dereferenced into the event itself.
 */

static void test_event_printk(struct trace_event_call *call)
{
 u64 dereference_flags = 0;
 u64 string_flags = 0;
 bool first = true;
 const char *fmt;
 int parens = 0;
 char in_quote = 0;
 int start_arg = 0;
 int arg = 0;
 int i, e;

 fmt = call->print_fmt;

 if (!fmt)
  return;

 for (i = 0; fmt[i]; i++) {
  switch (fmt[i]) {
  case '\\':
   i++;
   if (!fmt[i])
    return;
   continue;
  case '"':
  case '\'':
   /*
 * The print fmt starts with a string that
 * is processed first to find %p* usage,
 * then after the first string, the print fmt
 * contains arguments that are used to check
 * if the dereferenced %p* usage is safe.
 */

   if (first) {
    if (fmt[i] == '\'')
     continue;
    if (in_quote) {
     arg = 0;
     first = false;
     /*
 * If there was no %p* uses
 * the fmt is OK.
 */

     if (!dereference_flags)
      return;
    }
   }
   if (in_quote) {
    if (in_quote == fmt[i])
     in_quote = 0;
   } else {
    in_quote = fmt[i];
   }
   continue;
  case '%':
   if (!first || !in_quote)
    continue;
   i++;
   if (!fmt[i])
    return;
   switch (fmt[i]) {
   case '%':
    continue;
   case 'p':
 do_pointer:
    /* Find dereferencing fields */
    switch (fmt[i + 1]) {
    case 'B'case 'R'case 'r':
    case 'b'case 'M'case 'm':
    case 'I'case 'i'case 'E':
    case 'U'case 'V'case 'N':
    case 'a'case 'd'case 'D':
    case 'g'case 't'case 'C':
    case 'O'case 'f':
     if (WARN_ONCE(arg == 63,
            "Too many args for event: %s",
            trace_event_name(call)))
      return;
     dereference_flags |= 1ULL << arg;
    }
    break;
   default:
   {
    bool star = false;
    int j;

    /* Increment arg if %*s exists. */
    for (j = 0; fmt[i + j]; j++) {
     if (isdigit(fmt[i + j]) ||
         fmt[i + j] == '.')
      continue;
     if (fmt[i + j] == '*') {
      star = true;
      /* Handle %*pbl case */
      if (!j && fmt[i + 1] == 'p') {
       arg++;
       i++;
       goto do_pointer;
      }
      continue;
     }
     if ((fmt[i + j] == 's')) {
      if (star)
       arg++;
      if (WARN_ONCE(arg == 63,
             "Too many args for event: %s",
             trace_event_name(call)))
       return;
      dereference_flags |= 1ULL << arg;
      string_flags |= 1ULL << arg;
     }
     break;
    }
    break;
   } /* default */

   } /* switch */
   arg++;
   continue;
  case '(':
   if (in_quote)
    continue;
   parens++;
   continue;
  case ')':
   if (in_quote)
    continue;
   parens--;
   if (WARN_ONCE(parens < 0,
          "Paren mismatch for event: %s\narg='%s'\n%*s",
          trace_event_name(call),
          fmt + start_arg,
          (i - start_arg) + 5, "^"))
    return;
   continue;
  case ',':
   if (in_quote || parens)
    continue;
   e = i;
   i++;
   while (isspace(fmt[i]))
    i++;

   /*
 * If start_arg is zero, then this is the start of the
 * first argument. The processing of the argument happens
 * when the end of the argument is found, as it needs to
 * handle paranthesis and such.
 */

   if (!start_arg) {
    start_arg = i;
    /* Balance out the i++ in the for loop */
    i--;
    continue;
   }

   if (dereference_flags & (1ULL << arg)) {
    handle_dereference_arg(fmt + start_arg, string_flags,
             e - start_arg,
             &dereference_flags, arg, call);
   }

   start_arg = i;
   arg++;
   /* Balance out the i++ in the for loop */
   i--;
  }
 }

 if (dereference_flags & (1ULL << arg)) {
  handle_dereference_arg(fmt + start_arg, string_flags,
           i - start_arg,
           &dereference_flags, arg, call);
 }

 /*
 * If you triggered the below warning, the trace event reported
 * uses an unsafe dereference pointer %p*. As the data stored
 * at the trace event time may no longer exist when the trace
 * event is printed, dereferencing to the original source is
 * unsafe. The source of the dereference must be copied into the
 * event itself, and the dereference must access the copy instead.
 */

 if (WARN_ON_ONCE(dereference_flags)) {
  arg = 1;
  while (!(dereference_flags & 1)) {
   dereference_flags >>= 1;
   arg++;
  }
  pr_warn("event %s has unsafe dereference of argument %d\n",
   trace_event_name(call), arg);
  pr_warn("print_fmt: %s\n", fmt);
 }
}

int trace_event_raw_init(struct trace_event_call *call)
{
 int id;

 id = register_trace_event(&call->event);
 if (!id)
  return -ENODEV;

 test_event_printk(call);

 return 0;
}
EXPORT_SYMBOL_GPL(trace_event_raw_init);

bool trace_event_ignore_this_pid(struct trace_event_file *trace_file)
{
 struct trace_array *tr = trace_file->tr;
 struct trace_pid_list *no_pid_list;
 struct trace_pid_list *pid_list;

 pid_list = rcu_dereference_raw(tr->filtered_pids);
 no_pid_list = rcu_dereference_raw(tr->filtered_no_pids);

 if (!pid_list && !no_pid_list)
  return false;

 /*
 * This is recorded at every sched_switch for this task.
 * Thus, even if the task migrates the ignore value will be the same.
 */

 return this_cpu_read(tr->array_buffer.data->ignore_pid) != 0;
}
EXPORT_SYMBOL_GPL(trace_event_ignore_this_pid);

void *trace_event_buffer_reserve(struct trace_event_buffer *fbuffer,
     struct trace_event_file *trace_file,
     unsigned long len)
{
 struct trace_event_call *event_call = trace_file->event_call;

 if ((trace_file->flags & EVENT_FILE_FL_PID_FILTER) &&
     trace_event_ignore_this_pid(trace_file))
  return NULL;

 /*
 * If CONFIG_PREEMPTION is enabled, then the tracepoint itself disables
 * preemption (adding one to the preempt_count). Since we are
 * interested in the preempt_count at the time the tracepoint was
 * hit, we need to subtract one to offset the increment.
 */

 fbuffer->trace_ctx = tracing_gen_ctx_dec();
 fbuffer->trace_file = trace_file;

 fbuffer->event =
  trace_event_buffer_lock_reserve(&fbuffer->buffer, trace_file,
      event_call->event.type, len,
      fbuffer->trace_ctx);
 if (!fbuffer->event)
  return NULL;

 fbuffer->regs = NULL;
 fbuffer->entry = ring_buffer_event_data(fbuffer->event);
 return fbuffer->entry;
}
EXPORT_SYMBOL_GPL(trace_event_buffer_reserve);

int trace_event_reg(struct trace_event_call *call,
      enum trace_reg type, void *data)
{
 struct trace_event_file *file = data;

 WARN_ON(!(call->flags & TRACE_EVENT_FL_TRACEPOINT));
 switch (type) {
 case TRACE_REG_REGISTER:
  return tracepoint_probe_register(call->tp,
       call->class->probe,
       file);
 case TRACE_REG_UNREGISTER:
  tracepoint_probe_unregister(call->tp,
         call->class->probe,
         file);
  return 0;

#ifdef CONFIG_PERF_EVENTS
 case TRACE_REG_PERF_REGISTER:
  return tracepoint_probe_register(call->tp,
       call->class->perf_probe,
       call);
 case TRACE_REG_PERF_UNREGISTER:
  tracepoint_probe_unregister(call->tp,
         call->class->perf_probe,
         call);
  return 0;
 case TRACE_REG_PERF_OPEN:
 case TRACE_REG_PERF_CLOSE:
 case TRACE_REG_PERF_ADD:
 case TRACE_REG_PERF_DEL:
  return 0;
#endif
 }
 return 0;
}
EXPORT_SYMBOL_GPL(trace_event_reg);

void trace_event_enable_cmd_record(bool enable)
{
 struct trace_event_file *file;
 struct trace_array *tr;

 lockdep_assert_held(&event_mutex);

 do_for_each_event_file(tr, file) {

  if (!(file->flags & EVENT_FILE_FL_ENABLED))
   continue;

  if (enable) {
   tracing_start_cmdline_record();
   set_bit(EVENT_FILE_FL_RECORDED_CMD_BIT, &file->flags);
  } else {
   tracing_stop_cmdline_record();
   clear_bit(EVENT_FILE_FL_RECORDED_CMD_BIT, &file->flags);
  }
 } while_for_each_event_file();
}

void trace_event_enable_tgid_record(bool enable)
{
 struct trace_event_file *file;
 struct trace_array *tr;

 lockdep_assert_held(&event_mutex);

 do_for_each_event_file(tr, file) {
  if (!(file->flags & EVENT_FILE_FL_ENABLED))
   continue;

  if (enable) {
   tracing_start_tgid_record();
   set_bit(EVENT_FILE_FL_RECORDED_TGID_BIT, &file->flags);
  } else {
   tracing_stop_tgid_record();
   clear_bit(EVENT_FILE_FL_RECORDED_TGID_BIT,
      &file->flags);
  }
 } while_for_each_event_file();
}

static int __ftrace_event_enable_disable(struct trace_event_file *file,
      int enable, int soft_disable)
{
 struct trace_event_call *call = file->event_call;
 struct trace_array *tr = file->tr;
 bool soft_mode = atomic_read(&file->sm_ref) != 0;
 int ret = 0;
 int disable;

 switch (enable) {
 case 0:
  /*
 * When soft_disable is set and enable is cleared, the sm_ref
 * reference counter is decremented. If it reaches 0, we want
 * to clear the SOFT_DISABLED flag but leave the event in the
 * state that it was. That is, if the event was enabled and
 * SOFT_DISABLED isn't set, then do nothing. But if SOFT_DISABLED
 * is set we do not want the event to be enabled before we
 * clear the bit.
 *
 * When soft_disable is not set but the soft_mode is,
 * we do nothing. Do not disable the tracepoint, otherwise
 * "soft enable"s (clearing the SOFT_DISABLED bit) wont work.
 */

  if (soft_disable) {
   if (atomic_dec_return(&file->sm_ref) > 0)
    break;
   disable = file->flags & EVENT_FILE_FL_SOFT_DISABLED;
   soft_mode = false;
   /* Disable use of trace_buffered_event */
   trace_buffered_event_disable();
  } else
   disable = !soft_mode;

  if (disable && (file->flags & EVENT_FILE_FL_ENABLED)) {
   clear_bit(EVENT_FILE_FL_ENABLED_BIT, &file->flags);
   if (file->flags & EVENT_FILE_FL_RECORDED_CMD) {
    tracing_stop_cmdline_record();
    clear_bit(EVENT_FILE_FL_RECORDED_CMD_BIT, &file->flags);
   }

   if (file->flags & EVENT_FILE_FL_RECORDED_TGID) {
    tracing_stop_tgid_record();
    clear_bit(EVENT_FILE_FL_RECORDED_TGID_BIT, &file->flags);
   }

   ret = call->class->reg(call, TRACE_REG_UNREGISTER, file);

   WARN_ON_ONCE(ret);
  }
  /* If in soft mode, just set the SOFT_DISABLE_BIT, else clear it */
  if (soft_mode)
   set_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &file->flags);
  else
   clear_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &file->flags);
  break;
 case 1:
  /*
 * When soft_disable is set and enable is set, we want to
 * register the tracepoint for the event, but leave the event
 * as is. That means, if the event was already enabled, we do
 * nothing (but set soft_mode). If the event is disabled, we
 * set SOFT_DISABLED before enabling the event tracepoint, so
 * it still seems to be disabled.
 */

  if (!soft_disable)
   clear_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &file->flags);
  else {
   if (atomic_inc_return(&file->sm_ref) > 1)
    break;
   soft_mode = true;
   /* Enable use of trace_buffered_event */
   trace_buffered_event_enable();
  }

  if (!(file->flags & EVENT_FILE_FL_ENABLED)) {
   bool cmd = false, tgid = false;

   /* Keep the event disabled, when going to soft mode. */
   if (soft_disable)
    set_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &file->flags);

   if (tr->trace_flags & TRACE_ITER_RECORD_CMD) {
    cmd = true;
    tracing_start_cmdline_record();
    set_bit(EVENT_FILE_FL_RECORDED_CMD_BIT, &file->flags);
   }

   if (tr->trace_flags & TRACE_ITER_RECORD_TGID) {
    tgid = true;
    tracing_start_tgid_record();
    set_bit(EVENT_FILE_FL_RECORDED_TGID_BIT, &file->flags);
   }

   ret = call->class->reg(call, TRACE_REG_REGISTER, file);
   if (ret) {
    if (cmd)
     tracing_stop_cmdline_record();
    if (tgid)
     tracing_stop_tgid_record();
    pr_info("event trace: Could not enable event "
     "%s\n", trace_event_name(call));
    break;
   }
   set_bit(EVENT_FILE_FL_ENABLED_BIT, &file->flags);

   /* WAS_ENABLED gets set but never cleared. */
   set_bit(EVENT_FILE_FL_WAS_ENABLED_BIT, &file->flags);
  }
  break;
 }

 return ret;
}

int trace_event_enable_disable(struct trace_event_file *file,
          int enable, int soft_disable)
{
 return __ftrace_event_enable_disable(file, enable, soft_disable);
}

static int ftrace_event_enable_disable(struct trace_event_file *file,
           int enable)
{
 return __ftrace_event_enable_disable(file, enable, 0);
}

#ifdef CONFIG_MODULES
struct event_mod_load {
 struct list_head list;
 char   *module;
 char   *match;
 char   *system;
 char   *event;
};

static void free_event_mod(struct event_mod_load *event_mod)
{
 list_del(&event_mod->list);
 kfree(event_mod->module);
 kfree(event_mod->match);
 kfree(event_mod->system);
 kfree(event_mod->event);
 kfree(event_mod);
}

static void clear_mod_events(struct trace_array *tr)
{
 struct event_mod_load *event_mod, *n;

 list_for_each_entry_safe(event_mod, n, &tr->mod_events, list) {
  free_event_mod(event_mod);
 }
}

static int remove_cache_mod(struct trace_array *tr, const char *mod,
       const char *match, const char *system, const char *event)
{
 struct event_mod_load *event_mod, *n;
 int ret = -EINVAL;

 list_for_each_entry_safe(event_mod, n, &tr->mod_events, list) {
  if (strcmp(event_mod->module, mod) != 0)
   continue;

  if (match && strcmp(event_mod->match, match) != 0)
   continue;

  if (system &&
      (!event_mod->system || strcmp(event_mod->system, system) != 0))
   continue;

  if (event &&
      (!event_mod->event || strcmp(event_mod->event, event) != 0))
   continue;

  free_event_mod(event_mod);
  ret = 0;
 }

 return ret;
}

static int cache_mod(struct trace_array *tr, const char *mod, int set,
       const char *match, const char *system, const char *event)
{
 struct event_mod_load *event_mod;

 /* If the module exists, then this just failed to find an event */
 if (module_exists(mod))
  return -EINVAL;

 /* See if this is to remove a cached filter */
 if (!set)
  return remove_cache_mod(tr, mod, match, system, event);

 event_mod = kzalloc(sizeof(*event_mod), GFP_KERNEL);
 if (!event_mod)
  return -ENOMEM;

 INIT_LIST_HEAD(&event_mod->list);
 event_mod->module = kstrdup(mod, GFP_KERNEL);
 if (!event_mod->module)
  goto out_free;

 if (match) {
  event_mod->match = kstrdup(match, GFP_KERNEL);
  if (!event_mod->match)
   goto out_free;
 }

 if (system) {
  event_mod->system = kstrdup(system, GFP_KERNEL);
  if (!event_mod->system)
   goto out_free;
 }

 if (event) {
  event_mod->event = kstrdup(event, GFP_KERNEL);
  if (!event_mod->event)
   goto out_free;
 }

 list_add(&event_mod->list, &tr->mod_events);

 return 0;

 out_free:
 free_event_mod(event_mod);

 return -ENOMEM;
}
#else /* CONFIG_MODULES */
static inline void clear_mod_events(struct trace_array *tr) { }
static int cache_mod(struct trace_array *tr, const char *mod, int set,
       const char *match, const char *system, const char *event)
{
 return -EINVAL;
}
#endif

static void ftrace_clear_events(struct trace_array *tr)
{
 struct trace_event_file *file;

 mutex_lock(&event_mutex);
 list_for_each_entry(file, &tr->events, list) {
  ftrace_event_enable_disable(file, 0);
 }
 clear_mod_events(tr);
 mutex_unlock(&event_mutex);
}

static void
event_filter_pid_sched_process_exit(void *data, struct task_struct *task)
{
 struct trace_pid_list *pid_list;
 struct trace_array *tr = data;

 pid_list = rcu_dereference_raw(tr->filtered_pids);
 trace_filter_add_remove_task(pid_list, NULL, task);

 pid_list = rcu_dereference_raw(tr->filtered_no_pids);
 trace_filter_add_remove_task(pid_list, NULL, task);
}

static void
event_filter_pid_sched_process_fork(void *data,
        struct task_struct *self,
        struct task_struct *task)
{
 struct trace_pid_list *pid_list;
 struct trace_array *tr = data;

 pid_list = rcu_dereference_sched(tr->filtered_pids);
 trace_filter_add_remove_task(pid_list, self, task);

 pid_list = rcu_dereference_sched(tr->filtered_no_pids);
 trace_filter_add_remove_task(pid_list, self, task);
}

void trace_event_follow_fork(struct trace_array *tr, bool enable)
{
 if (enable) {
  register_trace_prio_sched_process_fork(event_filter_pid_sched_process_fork,
             tr, INT_MIN);
  register_trace_prio_sched_process_free(event_filter_pid_sched_process_exit,
             tr, INT_MAX);
 } else {
  unregister_trace_sched_process_fork(event_filter_pid_sched_process_fork,
          tr);
  unregister_trace_sched_process_free(event_filter_pid_sched_process_exit,
          tr);
 }
}

static void
event_filter_pid_sched_switch_probe_pre(void *data, bool preempt,
     struct task_struct *prev,
     struct task_struct *next,
     unsigned int prev_state)
{
 struct trace_array *tr = data;
 struct trace_pid_list *no_pid_list;
 struct trace_pid_list *pid_list;
 bool ret;

 pid_list = rcu_dereference_sched(tr->filtered_pids);
 no_pid_list = rcu_dereference_sched(tr->filtered_no_pids);

 /*
 * Sched switch is funny, as we only want to ignore it
 * in the notrace case if both prev and next should be ignored.
 */

 ret = trace_ignore_this_task(NULL, no_pid_list, prev) &&
  trace_ignore_this_task(NULL, no_pid_list, next);

 this_cpu_write(tr->array_buffer.data->ignore_pid, ret ||
         (trace_ignore_this_task(pid_list, NULL, prev) &&
   trace_ignore_this_task(pid_list, NULL, next)));
}

static void
event_filter_pid_sched_switch_probe_post(void *data, bool preempt,
      struct task_struct *prev,
      struct task_struct *next,
      unsigned int prev_state)
{
 struct trace_array *tr = data;
 struct trace_pid_list *no_pid_list;
 struct trace_pid_list *pid_list;

 pid_list = rcu_dereference_sched(tr->filtered_pids);
 no_pid_list = rcu_dereference_sched(tr->filtered_no_pids);

 this_cpu_write(tr->array_buffer.data->ignore_pid,
         trace_ignore_this_task(pid_list, no_pid_list, next));
}

static void
event_filter_pid_sched_wakeup_probe_pre(void *data, struct task_struct *task)
{
 struct trace_array *tr = data;
 struct trace_pid_list *no_pid_list;
 struct trace_pid_list *pid_list;

 /* Nothing to do if we are already tracing */
 if (!this_cpu_read(tr->array_buffer.data->ignore_pid))
  return;

 pid_list = rcu_dereference_sched(tr->filtered_pids);
 no_pid_list = rcu_dereference_sched(tr->filtered_no_pids);

 this_cpu_write(tr->array_buffer.data->ignore_pid,
         trace_ignore_this_task(pid_list, no_pid_list, task));
}

static void
event_filter_pid_sched_wakeup_probe_post(void *data, struct task_struct *task)
{
 struct trace_array *tr = data;
 struct trace_pid_list *no_pid_list;
 struct trace_pid_list *pid_list;

 /* Nothing to do if we are not tracing */
 if (this_cpu_read(tr->array_buffer.data->ignore_pid))
  return;

 pid_list = rcu_dereference_sched(tr->filtered_pids);
 no_pid_list = rcu_dereference_sched(tr->filtered_no_pids);

 /* Set tracing if current is enabled */
 this_cpu_write(tr->array_buffer.data->ignore_pid,
         trace_ignore_this_task(pid_list, no_pid_list, current));
}

static void unregister_pid_events(struct trace_array *tr)
{
 unregister_trace_sched_switch(event_filter_pid_sched_switch_probe_pre, tr);
 unregister_trace_sched_switch(event_filter_pid_sched_switch_probe_post, tr);

 unregister_trace_sched_wakeup(event_filter_pid_sched_wakeup_probe_pre, tr);
 unregister_trace_sched_wakeup(event_filter_pid_sched_wakeup_probe_post, tr);

 unregister_trace_sched_wakeup_new(event_filter_pid_sched_wakeup_probe_pre, tr);
 unregister_trace_sched_wakeup_new(event_filter_pid_sched_wakeup_probe_post, tr);

 unregister_trace_sched_waking(event_filter_pid_sched_wakeup_probe_pre, tr);
 unregister_trace_sched_waking(event_filter_pid_sched_wakeup_probe_post, tr);
}

static void __ftrace_clear_event_pids(struct trace_array *tr, int type)
{
 struct trace_pid_list *pid_list;
 struct trace_pid_list *no_pid_list;
 struct trace_event_file *file;
 int cpu;

 pid_list = rcu_dereference_protected(tr->filtered_pids,
          lockdep_is_held(&event_mutex));
 no_pid_list = rcu_dereference_protected(tr->filtered_no_pids,
          lockdep_is_held(&event_mutex));

 /* Make sure there's something to do */
 if (!pid_type_enabled(type, pid_list, no_pid_list))
  return;

 if (!still_need_pid_events(type, pid_list, no_pid_list)) {
  unregister_pid_events(tr);

  list_for_each_entry(file, &tr->events, list) {
   clear_bit(EVENT_FILE_FL_PID_FILTER_BIT, &file->flags);
  }

  for_each_possible_cpu(cpu)
   per_cpu_ptr(tr->array_buffer.data, cpu)->ignore_pid = false;
 }

 if (type & TRACE_PIDS)
  rcu_assign_pointer(tr->filtered_pids, NULL);

 if (type & TRACE_NO_PIDS)
  rcu_assign_pointer(tr->filtered_no_pids, NULL);

 /* Wait till all users are no longer using pid filtering */
 tracepoint_synchronize_unregister();

 if ((type & TRACE_PIDS) && pid_list)
  trace_pid_list_free(pid_list);

 if ((type & TRACE_NO_PIDS) && no_pid_list)
  trace_pid_list_free(no_pid_list);
}

static void ftrace_clear_event_pids(struct trace_array *tr, int type)
{
 mutex_lock(&event_mutex);
 __ftrace_clear_event_pids(tr, type);
 mutex_unlock(&event_mutex);
}

static void __put_system(struct event_subsystem *system)
{
 struct event_filter *filter = system->filter;

 WARN_ON_ONCE(system_refcount(system) == 0);
 if (system_refcount_dec(system))
  return;

 list_del(&system->list);

 if (filter) {
  kfree(filter->filter_string);
  kfree(filter);
 }
 kfree_const(system->name);
 kfree(system);
}

static void __get_system(struct event_subsystem *system)
{
 WARN_ON_ONCE(system_refcount(system) == 0);
 system_refcount_inc(system);
}

static void __get_system_dir(struct trace_subsystem_dir *dir)
{
 WARN_ON_ONCE(dir->ref_count == 0);
 dir->ref_count++;
 __get_system(dir->subsystem);
}

static void __put_system_dir(struct trace_subsystem_dir *dir)
{
 WARN_ON_ONCE(dir->ref_count == 0);
 /* If the subsystem is about to be freed, the dir must be too */
 WARN_ON_ONCE(system_refcount(dir->subsystem) == 1 && dir->ref_count != 1);

 __put_system(dir->subsystem);
 if (!--dir->ref_count)
  kfree(dir);
}

static void put_system(struct trace_subsystem_dir *dir)
{
 mutex_lock(&event_mutex);
 __put_system_dir(dir);
 mutex_unlock(&event_mutex);
}

static void remove_subsystem(struct trace_subsystem_dir *dir)
{
 if (!dir)
  return;

 if (!--dir->nr_events) {
  eventfs_remove_dir(dir->ei);
  list_del(&dir->list);
  __put_system_dir(dir);
 }
}

void event_file_get(struct trace_event_file *file)
{
 refcount_inc(&file->ref);
}

void event_file_put(struct trace_event_file *file)
{
 if (WARN_ON_ONCE(!refcount_read(&file->ref))) {
  if (file->flags & EVENT_FILE_FL_FREED)
   kmem_cache_free(file_cachep, file);
  return;
 }

 if (refcount_dec_and_test(&file->ref)) {
  /* Count should only go to zero when it is freed */
  if (WARN_ON_ONCE(!(file->flags & EVENT_FILE_FL_FREED)))
   return;
  kmem_cache_free(file_cachep, file);
 }
}

static void remove_event_file_dir(struct trace_event_file *file)
{
 eventfs_remove_dir(file->ei);
 list_del(&file->list);
 remove_subsystem(file->system);
 free_event_filter(file->filter);
 file->flags |= EVENT_FILE_FL_FREED;
 event_file_put(file);
}

/*
 * __ftrace_set_clr_event(NULL, NULL, NULL, set) will set/unset all events.
 */

static int
__ftrace_set_clr_event_nolock(struct trace_array *tr, const char *match,
         const char *sub, const char *event, int set,
         const char *mod)
{
 struct trace_event_file *file;
 struct trace_event_call *call;
 char *module __free(kfree) = NULL;
 const char *name;
 int ret = -EINVAL;
 int eret = 0;

 if (mod) {
  char *p;

  module = kstrdup(mod, GFP_KERNEL);
  if (!module)
   return -ENOMEM;

  /* Replace all '-' with '_' as that's what modules do */
  for (p = strchr(module, '-'); p; p = strchr(p + 1, '-'))
   *p = '_';
 }

 list_for_each_entry(file, &tr->events, list) {

  call = file->event_call;

  /* If a module is specified, skip events that are not that module */
  if (module && (!call->module || strcmp(module_name(call->module), module)))
   continue;

  name = trace_event_name(call);

  if (!name || !call->class || !call->class->reg)
   continue;

  if (call->flags & TRACE_EVENT_FL_IGNORE_ENABLE)
   continue;

  if (match &&
      strcmp(match, name) != 0 &&
      strcmp(match, call->class->system) != 0)
   continue;

  if (sub && strcmp(sub, call->class->system) != 0)
   continue;

  if (event && strcmp(event, name) != 0)
   continue;

  ret = ftrace_event_enable_disable(file, set);

  /*
 * Save the first error and return that. Some events
 * may still have been enabled, but let the user
 * know that something went wrong.
 */

  if (ret && !eret)
   eret = ret;

  ret = eret;
 }

 /*
 * If this is a module setting and nothing was found,
 * check if the module was loaded. If it wasn't cache it.
 */

 if (module && ret == -EINVAL && !eret)
  ret = cache_mod(tr, module, set, match, sub, event);

 return ret;
}

static int __ftrace_set_clr_event(struct trace_array *tr, const char *match,
      const char *sub, const char *event, int set,
      const char *mod)
{
 int ret;

 mutex_lock(&event_mutex);
 ret = __ftrace_set_clr_event_nolock(tr, match, sub, event, set, mod);
 mutex_unlock(&event_mutex);

 return ret;
}

int ftrace_set_clr_event(struct trace_array *tr, char *buf, int set)
{
 char *event = NULL, *sub = NULL, *match, *mod;
 int ret;

 if (!tr)
  return -ENOENT;

 /* Modules events can be appened with :mod:<module> */
 mod = strstr(buf, ":mod:");
 if (mod) {
  *mod = '\0';
  /* move to the module name */
  mod += 5;
 }

 /*
 * The buf format can be <subsystem>:<event-name>
 *  *:<event-name> means any event by that name.
 *  :<event-name> is the same.
 *
 *  <subsystem>:* means all events in that subsystem
 *  <subsystem>: means the same.
 *
 *  <name> (no ':') means all events in a subsystem with
 *  the name <name> or any event that matches <name>
 */


 match = strsep(&buf, ":");
 if (buf) {
  sub = match;
  event = buf;
  match = NULL;

  if (!strlen(sub) || strcmp(sub, "*") == 0)
   sub = NULL;
  if (!strlen(event) || strcmp(event, "*") == 0)
   event = NULL;
 } else if (mod) {
  /* Allow wildcard for no length or star */
  if (!strlen(match) || strcmp(match, "*") == 0)
   match = NULL;
 }

 ret = __ftrace_set_clr_event(tr, match, sub, event, set, mod);

 /* Put back the colon to allow this to be called again */
 if (buf)
  *(buf - 1) = ':';

 return ret;
}

/**
 * trace_set_clr_event - enable or disable an event
 * @system: system name to match (NULL for any system)
 * @event: event name to match (NULL for all events, within system)
 * @set: 1 to enable, 0 to disable
 *
 * This is a way for other parts of the kernel to enable or disable
 * event recording.
 *
 * Returns 0 on success, -EINVAL if the parameters do not match any
 * registered events.
 */

int trace_set_clr_event(const char *system, const char *event, int set)
{
 struct trace_array *tr = top_trace_array();

 if (!tr)
  return -ENODEV;

 return __ftrace_set_clr_event(tr, NULL, system, event, set, NULL);
}
EXPORT_SYMBOL_GPL(trace_set_clr_event);

/**
 * trace_array_set_clr_event - enable or disable an event for a trace array.
 * @tr: concerned trace array.
 * @system: system name to match (NULL for any system)
 * @event: event name to match (NULL for all events, within system)
 * @enable: true to enable, false to disable
 *
 * This is a way for other parts of the kernel to enable or disable
 * event recording.
 *
 * Returns 0 on success, -EINVAL if the parameters do not match any
 * registered events.
 */

int trace_array_set_clr_event(struct trace_array *tr, const char *system,
  const char *event, bool enable)
{
 int set;

 if (!tr)
  return -ENOENT;

 set = (enable == true) ? 1 : 0;
 return __ftrace_set_clr_event(tr, NULL, system, event, set, NULL);
}
EXPORT_SYMBOL_GPL(trace_array_set_clr_event);

/* 128 should be much more than enough */
#define EVENT_BUF_SIZE  127

static ssize_t
ftrace_event_write(struct file *file, const char __user *ubuf,
     size_t cnt, loff_t *ppos)
{
 struct trace_parser parser;
 struct seq_file *m = file->private_data;
 struct trace_array *tr = m->private;
 ssize_t read, ret;

 if (!cnt)
  return 0;

 ret = tracing_update_buffers(tr);
 if (ret < 0)
  return ret;

 if (trace_parser_get_init(&parser, EVENT_BUF_SIZE + 1))
  return -ENOMEM;

 read = trace_get_user(&parser, ubuf, cnt, ppos);

 if (read >= 0 && trace_parser_loaded((&parser))) {
  int set = 1;

  if (*parser.buffer == '!')
   set = 0;

  ret = ftrace_set_clr_event(tr, parser.buffer + !set, set);
  if (ret)
   goto out_put;
 }

 ret = read;

 out_put:
 trace_parser_put(&parser);

 return ret;
}

static void *
t_next(struct seq_file *m, void *v, loff_t *pos)
{
 struct trace_event_file *file = v;
 struct trace_event_call *call;
 struct trace_array *tr = m->private;

 (*pos)++;

 list_for_each_entry_continue(file, &tr->events, list) {
  call = file->event_call;
  /*
 * The ftrace subsystem is for showing formats only.
 * They can not be enabled or disabled via the event files.
 */

  if (call->class && call->class->reg &&
      !(call->flags & TRACE_EVENT_FL_IGNORE_ENABLE))
   return file;
 }

 return NULL;
}

static void *t_start(struct seq_file *m, loff_t *pos)
{
 struct trace_event_file *file;
 struct trace_array *tr = m->private;
 loff_t l;

 mutex_lock(&event_mutex);

 file = list_entry(&tr->events, struct trace_event_file, list);
 for (l = 0; l <= *pos; ) {
  file = t_next(m, file, &l);
  if (!file)
   break;
 }
 return file;
}

enum set_event_iter_type {
 SET_EVENT_FILE,
 SET_EVENT_MOD,
};

struct set_event_iter {
 enum set_event_iter_type type;
 union {
  struct trace_event_file *file;
  struct event_mod_load *event_mod;
 };
};

static void *
s_next(struct seq_file *m, void *v, loff_t *pos)
{
 struct set_event_iter *iter = v;
 struct trace_event_file *file;
 struct trace_array *tr = m->private;

 (*pos)++;

 if (iter->type == SET_EVENT_FILE) {
  file = iter->file;
  list_for_each_entry_continue(file, &tr->events, list) {
   if (file->flags & EVENT_FILE_FL_ENABLED) {
    iter->file = file;
    return iter;
   }
  }
#ifdef CONFIG_MODULES
  iter->type = SET_EVENT_MOD;
  iter->event_mod = list_entry(&tr->mod_events, struct event_mod_load, list);
#endif
 }

#ifdef CONFIG_MODULES
 list_for_each_entry_continue(iter->event_mod, &tr->mod_events, list)
  return iter;
#endif

 /*
 * The iter is allocated in s_start() and passed via the 'v'
 * parameter. To stop the iterator, NULL must be returned. But
 * the return value is what the 'v' parameter in s_stop() receives
 * and frees. Free iter here as it will no longer be used.
 */

 kfree(iter);
 return NULL;
}

static void *s_start(struct seq_file *m, loff_t *pos)
{
 struct trace_array *tr = m->private;
 struct set_event_iter *iter;
 loff_t l;

 iter = kzalloc(sizeof(*iter), GFP_KERNEL);
 mutex_lock(&event_mutex);
 if (!iter)
  return NULL;

 iter->type = SET_EVENT_FILE;
 iter->file = list_entry(&tr->events, struct trace_event_file, list);

 for (l = 0; l <= *pos; ) {
  iter = s_next(m, iter, &l);
  if (!iter)
   break;
 }
 return iter;
}

static int t_show(struct seq_file *m, void *v)
{
 struct trace_event_file *file = v;
 struct trace_event_call *call = file->event_call;

 if (strcmp(call->class->system, TRACE_SYSTEM) != 0)
  seq_printf(m, "%s:", call->class->system);
 seq_printf(m, "%s\n", trace_event_name(call));

 return 0;
}

static void t_stop(struct seq_file *m, void *p)
{
 mutex_unlock(&event_mutex);
}

#ifdef CONFIG_MODULES
static int s_show(struct seq_file *m, void *v)
{
 struct set_event_iter *iter = v;
 const char *system;
 const char *event;

 if (iter->type == SET_EVENT_FILE)
  return t_show(m, iter->file);

 /* When match is set, system and event are not */
 if (iter->event_mod->match) {
  seq_printf(m, "%s:mod:%s\n", iter->event_mod->match,
      iter->event_mod->module);
  return 0;
 }

 system = iter->event_mod->system ? : "*";
 event = iter->event_mod->event ? : "*";

 seq_printf(m, "%s:%s:mod:%s\n", system, event, iter->event_mod->module);

 return 0;
}
#else /* CONFIG_MODULES */
static int s_show(struct seq_file *m, void *v)
{
 struct set_event_iter *iter = v;

 return t_show(m, iter->file);
}
#endif

static void s_stop(struct seq_file *m, void *v)
{
 kfree(v);
 t_stop(m, NULL);
}

static void *
__next(struct seq_file *m, void *v, loff_t *pos, int type)
{
 struct trace_array *tr = m->private;
 struct trace_pid_list *pid_list;

 if (type == TRACE_PIDS)
  pid_list = rcu_dereference_sched(tr->filtered_pids);
 else
  pid_list = rcu_dereference_sched(tr->filtered_no_pids);

 return trace_pid_next(pid_list, v, pos);
}

static void *
p_next(struct seq_file *m, void *v, loff_t *pos)
{
 return __next(m, v, pos, TRACE_PIDS);
}

static void *
np_next(struct seq_file *m, void *v, loff_t *pos)
{
 return __next(m, v, pos, TRACE_NO_PIDS);
}

static void *__start(struct seq_file *m, loff_t *pos, int type)
 __acquires(RCU)
{
 struct trace_pid_list *pid_list;
 struct trace_array *tr = m->private;

 /*
 * Grab the mutex, to keep calls to p_next() having the same
 * tr->filtered_pids as p_start() has.
 * If we just passed the tr->filtered_pids around, then RCU would
 * have been enough, but doing that makes things more complex.
 */

 mutex_lock(&event_mutex);
 rcu_read_lock_sched();

 if (type == TRACE_PIDS)
  pid_list = rcu_dereference_sched(tr->filtered_pids);
 else
  pid_list = rcu_dereference_sched(tr->filtered_no_pids);

 if (!pid_list)
  return NULL;

 return trace_pid_start(pid_list, pos);
}

static void *p_start(struct seq_file *m, loff_t *pos)
 __acquires(RCU)
{
 return __start(m, pos, TRACE_PIDS);
}

static void *np_start(struct seq_file *m, loff_t *pos)
 __acquires(RCU)
{
 return __start(m, pos, TRACE_NO_PIDS);
}

static void p_stop(struct seq_file *m, void *p)
 __releases(RCU)
{
 rcu_read_unlock_sched();
 mutex_unlock(&event_mutex);
}

static ssize_t
event_enable_read(struct file *filp, char __user *ubuf, size_t cnt,
    loff_t *ppos)
{
 struct trace_event_file *file;
 unsigned long flags;
 char buf[4] = "0";

 mutex_lock(&event_mutex);
 file = event_file_file(filp);
 if (likely(file))
  flags = file->flags;
 mutex_unlock(&event_mutex);

 if (!file)
  return -ENODEV;

 if (flags & EVENT_FILE_FL_ENABLED &&
     !(flags & EVENT_FILE_FL_SOFT_DISABLED))
  strcpy(buf, "1");

 if (atomic_read(&file->sm_ref) != 0)
  strcat(buf, "*");

 strcat(buf, "\n");

 return simple_read_from_buffer(ubuf, cnt, ppos, buf, strlen(buf));
}

static ssize_t
event_enable_write(struct file *filp, const char __user *ubuf, size_t cnt,
     loff_t *ppos)
{
 struct trace_event_file *file;
 unsigned long val;
 int ret;

 ret = kstrtoul_from_user(ubuf, cnt, 10, &val);
 if (ret)
  return ret;

 guard(mutex)(&event_mutex);

 switch (val) {
 case 0:
 case 1:
  file = event_file_file(filp);
  if (!file)
   return -ENODEV;
  ret = tracing_update_buffers(file->tr);
  if (ret < 0)
   return ret;
  ret = ftrace_event_enable_disable(file, val);
  if (ret < 0)
   return ret;
  break;

 default:
  return -EINVAL;
 }

 *ppos += cnt;

 return cnt;
}

/*
 * Returns:
 *   0 : no events exist?
 *   1 : all events are disabled
 *   2 : all events are enabled
 *   3 : some events are enabled and some are enabled
 */

int trace_events_enabled(struct trace_array *tr, const char *system)
{
 struct trace_event_call *call;
 struct trace_event_file *file;
 int set = 0;

 guard(mutex)(&event_mutex);

 list_for_each_entry(file, &tr->events, list) {
  call = file->event_call;
  if ((call->flags & TRACE_EVENT_FL_IGNORE_ENABLE) ||
      !trace_event_name(call) || !call->class || !call->class->reg)
   continue;

  if (system && strcmp(call->class->system, system) != 0)
   continue;

  /*
 * We need to find out if all the events are set
 * or if all events or cleared, or if we have
 * a mixture.
 */

  set |= (1 << !!(file->flags & EVENT_FILE_FL_ENABLED));

  /*
 * If we have a mixture, no need to look further.
 */

  if (set == 3)
   break;
 }

 return set;
}

static ssize_t
system_enable_read(struct file *filp, char __user *ubuf, size_t cnt,
     loff_t *ppos)
{
 const char set_to_char[4] = { '?''0''1''X' };
 struct trace_subsystem_dir *dir = filp->private_data;
 struct event_subsystem *system = dir->subsystem;
 struct trace_array *tr = dir->tr;
 char buf[2];
 int set;
 int ret;

 set = trace_events_enabled(tr, system ? system->name : NULL);

 buf[0] = set_to_char[set];
 buf[1] = '\n';

 ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, 2);

 return ret;
}

static ssize_t
system_enable_write(struct file *filp, const char __user *ubuf, size_t cnt,
      loff_t *ppos)
{
 struct trace_subsystem_dir *dir = filp->private_data;
 struct event_subsystem *system = dir->subsystem;
 const char *name = NULL;
 unsigned long val;
 ssize_t ret;

 ret = kstrtoul_from_user(ubuf, cnt, 10, &val);
 if (ret)
  return ret;

 ret = tracing_update_buffers(dir->tr);
 if (ret < 0)
  return ret;

 if (val != 0 && val != 1)
  return -EINVAL;

 /*
 * Opening of "enable" adds a ref count to system,
 * so the name is safe to use.
 */

 if (system)
  name = system->name;

 ret = __ftrace_set_clr_event(dir->tr, NULL, name, NULL, val, NULL);
 if (ret)
  goto out;

 ret = cnt;

out:
 *ppos += cnt;

 return ret;
}

enum {
 FORMAT_HEADER  = 1,
 FORMAT_FIELD_SEPERATOR = 2,
 FORMAT_PRINTFMT  = 3,
};

static void *f_next(struct seq_file *m, void *v, loff_t *pos)
{
 struct trace_event_file *file = event_file_data(m->private);
 struct trace_event_call *call = file->event_call;
 struct list_head *common_head = &ftrace_common_fields;
 struct list_head *head = trace_get_fields(call);
 struct list_head *node = v;

 (*pos)++;

 switch ((unsigned long)v) {
 case FORMAT_HEADER:
  node = common_head;
  break;

 case FORMAT_FIELD_SEPERATOR:
  node = head;
  break;

 case FORMAT_PRINTFMT:
  /* all done */
  return NULL;
 }

 node = node->prev;
 if (node == common_head)
  return (void *)FORMAT_FIELD_SEPERATOR;
 else if (node == head)
  return (void *)FORMAT_PRINTFMT;
 else
  return node;
}

static int f_show(struct seq_file *m, void *v)
{
 struct trace_event_file *file = event_file_data(m->private);
 struct trace_event_call *call = file->event_call;
 struct ftrace_event_field *field;
 const char *array_descriptor;

 switch ((unsigned long)v) {
 case FORMAT_HEADER:
  seq_printf(m, "name: %s\n", trace_event_name(call));
  seq_printf(m, "ID: %d\n", call->event.type);
  seq_puts(m, "format:\n");
  return 0;

 case FORMAT_FIELD_SEPERATOR:
  seq_putc(m, '\n');
  return 0;

 case FORMAT_PRINTFMT:
  seq_printf(m, "\nprint fmt: %s\n",
      call->print_fmt);
  return 0;
 }

 field = list_entry(v, struct ftrace_event_field, link);
 /*
 * Smartly shows the array type(except dynamic array).
 * Normal:
 * field:TYPE VAR
 * If TYPE := TYPE[LEN], it is shown:
 * field:TYPE VAR[LEN]
 */

 array_descriptor = strchr(field->type, '[');

 if (str_has_prefix(field->type, "__data_loc"))
  array_descriptor = NULL;

 if (!array_descriptor)
  seq_printf(m, "\tfield:%s %s;\toffset:%u;\tsize:%u;\tsigned:%d;\n",
      field->type, field->name, field->offset,
      field->size, !!field->is_signed);
 else if (field->len)
  seq_printf(m, "\tfield:%.*s %s[%d];\toffset:%u;\tsize:%u;\tsigned:%d;\n",
      (int)(array_descriptor - field->type),
      field->type, field->name,
      field->len, field->offset,
      field->size, !!field->is_signed);
 else
  seq_printf(m, "\tfield:%.*s %s[];\toffset:%u;\tsize:%u;\tsigned:%d;\n",
    (int)(array_descriptor - field->type),
    field->type, field->name,
    field->offset, field->size, !!field->is_signed);

 return 0;
}

static void *f_start(struct seq_file *m, loff_t *pos)
{
 struct trace_event_file *file;
 void *p = (void *)FORMAT_HEADER;
 loff_t l = 0;

 /* ->stop() is called even if ->start() fails */
 mutex_lock(&event_mutex);
 file = event_file_file(m->private);
 if (!file)
  return ERR_PTR(-ENODEV);

 while (l < *pos && p)
  p = f_next(m, p, &l);

 return p;
}

static void f_stop(struct seq_file *m, void *p)
{
 mutex_unlock(&event_mutex);
}

static const struct seq_operations trace_format_seq_ops = {
 .start  = f_start,
 .next  = f_next,
 .stop  = f_stop,
 .show  = f_show,
};

static int trace_format_open(struct inode *inode, struct file *file)
{
 struct seq_file *m;
 int ret;

 /* Do we want to hide event format files on tracefs lockdown? */

 ret = seq_open(file, &trace_format_seq_ops);
 if (ret < 0)
  return ret;

 m = file->private_data;
 m->private = file;

 return 0;
}

#ifdef CONFIG_PERF_EVENTS
static ssize_t
event_id_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos)
{
 int id = (long)event_file_data(filp);
 char buf[32];
 int len;

 if (unlikely(!id))
  return -ENODEV;

 len = sprintf(buf, "%d\n", id);

 return simple_read_from_buffer(ubuf, cnt, ppos, buf, len);
}
#endif

static ssize_t
event_filter_read(struct file *filp, char __user *ubuf, size_t cnt,
    loff_t *ppos)
{
 struct trace_event_file *file;
 struct trace_seq *s;
 int r = -ENODEV;

 if (*ppos)
  return 0;

 s = kmalloc(sizeof(*s), GFP_KERNEL);

 if (!s)
  return -ENOMEM;

 trace_seq_init(s);

 mutex_lock(&event_mutex);
 file = event_file_file(filp);
 if (file)
  print_event_filter(file, s);
 mutex_unlock(&event_mutex);

 if (file)
  r = simple_read_from_buffer(ubuf, cnt, ppos,
         s->buffer, trace_seq_used(s));

 kfree(s);

 return r;
}

static ssize_t
event_filter_write(struct file *filp, const char __user *ubuf, size_t cnt,
     loff_t *ppos)
{
 struct trace_event_file *file;
 char *buf;
 int err = -ENODEV;

 if (cnt >= PAGE_SIZE)
  return -EINVAL;

 buf = memdup_user_nul(ubuf, cnt);
 if (IS_ERR(buf))
  return PTR_ERR(buf);

 mutex_lock(&event_mutex);
 file = event_file_file(filp);
 if (file) {
  if (file->flags & EVENT_FILE_FL_FREED)
   err = -ENODEV;
  else
   err = apply_event_filter(file, buf);
 }
 mutex_unlock(&event_mutex);

 kfree(buf);
 if (err < 0)
  return err;

 *ppos += cnt;

 return cnt;
}

static LIST_HEAD(event_subsystems);

static int subsystem_open(struct inode *inode, struct file *filp)
{
 struct trace_subsystem_dir *dir = NULL, *iter_dir;
 struct trace_array *tr = NULL, *iter_tr;
 struct event_subsystem *system = NULL;
 int ret;

 if (tracing_is_disabled())
  return -ENODEV;

 /* Make sure the system still exists */
 mutex_lock(&event_mutex);
 mutex_lock(&trace_types_lock);
 list_for_each_entry(iter_tr, &ftrace_trace_arrays, list) {
  list_for_each_entry(iter_dir, &iter_tr->systems, list) {
   if (iter_dir == inode->i_private) {
    /* Don't open systems with no events */
    tr = iter_tr;
    dir = iter_dir;
    if (dir->nr_events) {
     __get_system_dir(dir);
     system = dir->subsystem;
    }
    goto exit_loop;
   }
  }
 }
 exit_loop:
 mutex_unlock(&trace_types_lock);
 mutex_unlock(&event_mutex);

 if (!system)
  return -ENODEV;

 /* Still need to increment the ref count of the system */
 if (trace_array_get(tr) < 0) {
  put_system(dir);
  return -ENODEV;
 }

 ret = tracing_open_generic(inode, filp);
 if (ret < 0) {
  trace_array_put(tr);
  put_system(dir);
 }

 return ret;
}

static int system_tr_open(struct inode *inode, struct file *filp)
{
 struct trace_subsystem_dir *dir;
 struct trace_array *tr = inode->i_private;
 int ret;

 /* Make a temporary dir that has no system but points to tr */
 dir = kzalloc(sizeof(*dir), GFP_KERNEL);
 if (!dir)
  return -ENOMEM;

 ret = tracing_open_generic_tr(inode, filp);
 if (ret < 0) {
  kfree(dir);
  return ret;
 }
 dir->tr = tr;
 filp->private_data = dir;

 return 0;
}

static int subsystem_release(struct inode *inode, struct file *file)
{
 struct trace_subsystem_dir *dir = file->private_data;

 trace_array_put(dir->tr);

 /*
 * If dir->subsystem is NULL, then this is a temporary
 * descriptor that was made for a trace_array to enable
 * all subsystems.
 */

 if (dir->subsystem)
  put_system(dir);
 else
  kfree(dir);

 return 0;
}

static ssize_t
subsystem_filter_read(struct file *filp, char __user *ubuf, size_t cnt,
        loff_t *ppos)
{
 struct trace_subsystem_dir *dir = filp->private_data;
 struct event_subsystem *system = dir->subsystem;
 struct trace_seq *s;
 int r;

 if (*ppos)
  return 0;

 s = kmalloc(sizeof(*s), GFP_KERNEL);
 if (!s)
  return -ENOMEM;

 trace_seq_init(s);

 print_subsystem_event_filter(system, s);
 r = simple_read_from_buffer(ubuf, cnt, ppos,
        s->buffer, trace_seq_used(s));

 kfree(s);

 return r;
}

static ssize_t
subsystem_filter_write(struct file *filp, const char __user *ubuf, size_t cnt,
         loff_t *ppos)
{
 struct trace_subsystem_dir *dir = filp->private_data;
 char *buf;
 int err;

 if (cnt >= PAGE_SIZE)
  return -EINVAL;

 buf = memdup_user_nul(ubuf, cnt);
 if (IS_ERR(buf))
  return PTR_ERR(buf);

 err = apply_subsystem_event_filter(dir, buf);
 kfree(buf);
 if (err < 0)
  return err;

 *ppos += cnt;

 return cnt;
}

static ssize_t
show_header_page_file(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos)
{
 struct trace_array *tr = filp->private_data;
 struct trace_seq *s;
 int r;

 if (*ppos)
  return 0;

 s = kmalloc(sizeof(*s), GFP_KERNEL);
 if (!s)
  return -ENOMEM;

 trace_seq_init(s);

 ring_buffer_print_page_header(tr->array_buffer.buffer, s);
 r = simple_read_from_buffer(ubuf, cnt, ppos,
        s->buffer, trace_seq_used(s));

 kfree(s);

 return r;
}

static ssize_t
show_header_event_file(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos)
{
 struct trace_seq *s;
 int r;

 if (*ppos)
  return 0;

 s = kmalloc(sizeof(*s), GFP_KERNEL);
 if (!s)
  return -ENOMEM;

 trace_seq_init(s);

 ring_buffer_print_entry_header(s);
 r = simple_read_from_buffer(ubuf, cnt, ppos,
        s->buffer, trace_seq_used(s));

 kfree(s);

 return r;
}

static void ignore_task_cpu(void *data)
{
 struct trace_array *tr = data;
 struct trace_pid_list *pid_list;
 struct trace_pid_list *no_pid_list;

 /*
 * This function is called by on_each_cpu() while the
 * event_mutex is held.
 */

 pid_list = rcu_dereference_protected(tr->filtered_pids,
          mutex_is_locked(&event_mutex));
 no_pid_list = rcu_dereference_protected(tr->filtered_no_pids,
          mutex_is_locked(&event_mutex));

 this_cpu_write(tr->array_buffer.data->ignore_pid,
         trace_ignore_this_task(pid_list, no_pid_list, current));
}

static void register_pid_events(struct trace_array *tr)
{
 /*
 * Register a probe that is called before all other probes
 * to set ignore_pid if next or prev do not match.
 * Register a probe this is called after all other probes
 * to only keep ignore_pid set if next pid matches.
 */

 register_trace_prio_sched_switch(event_filter_pid_sched_switch_probe_pre,
      tr, INT_MAX);
 register_trace_prio_sched_switch(event_filter_pid_sched_switch_probe_post,
      tr, 0);

 register_trace_prio_sched_wakeup(event_filter_pid_sched_wakeup_probe_pre,
      tr, INT_MAX);
 register_trace_prio_sched_wakeup(event_filter_pid_sched_wakeup_probe_post,
      tr, 0);

 register_trace_prio_sched_wakeup_new(event_filter_pid_sched_wakeup_probe_pre,
          tr, INT_MAX);
 register_trace_prio_sched_wakeup_new(event_filter_pid_sched_wakeup_probe_post,
          tr, 0);

 register_trace_prio_sched_waking(event_filter_pid_sched_wakeup_probe_pre,
      tr, INT_MAX);
 register_trace_prio_sched_waking(event_filter_pid_sched_wakeup_probe_post,
      tr, 0);
}

static ssize_t
event_pid_write(struct file *filp, const char __user *ubuf,
  size_t cnt, loff_t *ppos, int type)
{
 struct seq_file *m = filp->private_data;
 struct trace_array *tr = m->private;
 struct trace_pid_list *filtered_pids = NULL;
 struct trace_pid_list *other_pids = NULL;
 struct trace_pid_list *pid_list;
 struct trace_event_file *file;
 ssize_t ret;

 if (!cnt)
  return 0;

 ret = tracing_update_buffers(tr);
 if (ret < 0)
  return ret;

 guard(mutex)(&event_mutex);

 if (type == TRACE_PIDS) {
  filtered_pids = rcu_dereference_protected(tr->filtered_pids,
         lockdep_is_held(&event_mutex));
  other_pids = rcu_dereference_protected(tr->filtered_no_pids,
         lockdep_is_held(&event_mutex));
 } else {
  filtered_pids = rcu_dereference_protected(tr->filtered_no_pids,
         lockdep_is_held(&event_mutex));
  other_pids = rcu_dereference_protected(tr->filtered_pids,
         lockdep_is_held(&event_mutex));
 }

 ret = trace_pid_write(filtered_pids, &pid_list, ubuf, cnt);
 if (ret < 0)
  return ret;

 if (type == TRACE_PIDS)
  rcu_assign_pointer(tr->filtered_pids, pid_list);
 else
  rcu_assign_pointer(tr->filtered_no_pids, pid_list);

 list_for_each_entry(file, &tr->events, list) {
  set_bit(EVENT_FILE_FL_PID_FILTER_BIT, &file->flags);
 }

 if (filtered_pids) {
  tracepoint_synchronize_unregister();
  trace_pid_list_free(filtered_pids);
 } else if (pid_list && !other_pids) {
  register_pid_events(tr);
 }

 /*
 * Ignoring of pids is done at task switch. But we have to
 * check for those tasks that are currently running.
 * Always do this in case a pid was appended or removed.
 */

 on_each_cpu(ignore_task_cpu, tr, 1);

 *ppos += ret;

 return ret;
}

static ssize_t
ftrace_event_pid_write(struct file *filp, const char __user *ubuf,
         size_t cnt, loff_t *ppos)
{
 return event_pid_write(filp, ubuf, cnt, ppos, TRACE_PIDS);
}

static ssize_t
ftrace_event_npid_write(struct file *filp, const char __user *ubuf,
   size_t cnt, loff_t *ppos)
{
 return event_pid_write(filp, ubuf, cnt, ppos, TRACE_NO_PIDS);
}

static int ftrace_event_avail_open(struct inode *inode, struct file *file);
static int ftrace_event_set_open(struct inode *inode, struct file *file);
static int ftrace_event_set_pid_open(struct inode *inode, struct file *file);
static int ftrace_event_set_npid_open(struct inode *inode, struct file *file);
static int ftrace_event_release(struct inode *inode, struct file *file);

static const struct seq_operations show_event_seq_ops = {
 .start = t_start,
 .next = t_next,
 .show = t_show,
 .stop = t_stop,
};

static const struct seq_operations show_set_event_seq_ops = {
 .start = s_start,
 .next = s_next,
 .show = s_show,
 .stop = s_stop,
};

static const struct seq_operations show_set_pid_seq_ops = {
 .start = p_start,
 .next = p_next,
 .show = trace_pid_show,
 .stop = p_stop,
};

static const struct seq_operations show_set_no_pid_seq_ops = {
 .start = np_start,
 .next = np_next,
 .show = trace_pid_show,
 .stop = p_stop,
};

static const struct file_operations ftrace_avail_fops = {
 .open = ftrace_event_avail_open,
 .read = seq_read,
 .llseek = seq_lseek,
 .release = seq_release,
};

static const struct file_operations ftrace_set_event_fops = {
 .open = ftrace_event_set_open,
 .read = seq_read,
 .write = ftrace_event_write,
 .llseek = seq_lseek,
 .release = ftrace_event_release,
};

static const struct file_operations ftrace_set_event_pid_fops = {
 .open = ftrace_event_set_pid_open,
 .read = seq_read,
 .write = ftrace_event_pid_write,
 .llseek = seq_lseek,
 .release = ftrace_event_release,
};

static const struct file_operations ftrace_set_event_notrace_pid_fops = {
 .open = ftrace_event_set_npid_open,
 .read = seq_read,
 .write = ftrace_event_npid_write,
 .llseek = seq_lseek,
 .release = ftrace_event_release,
};

static const struct file_operations ftrace_enable_fops = {
 .open = tracing_open_file_tr,
 .read = event_enable_read,
 .write = event_enable_write,
 .release = tracing_release_file_tr,
 .llseek = default_llseek,
};

static const struct file_operations ftrace_event_format_fops = {
 .open = trace_format_open,
 .read = seq_read,
 .llseek = seq_lseek,
 .release = seq_release,
};

#ifdef CONFIG_PERF_EVENTS
static const struct file_operations ftrace_event_id_fops = {
 .read = event_id_read,
 .llseek = default_llseek,
};
#endif

static const struct file_operations ftrace_event_filter_fops = {
 .open = tracing_open_file_tr,
 .read = event_filter_read,
 .write = event_filter_write,
 .release = tracing_release_file_tr,
 .llseek = default_llseek,
};

static const struct file_operations ftrace_subsystem_filter_fops = {
 .open = subsystem_open,
 .read = subsystem_filter_read,
 .write = subsystem_filter_write,
 .llseek = default_llseek,
 .release = subsystem_release,
};

static const struct file_operations ftrace_system_enable_fops = {
 .open = subsystem_open,
 .read = system_enable_read,
 .write = system_enable_write,
 .llseek = default_llseek,
 .release = subsystem_release,
};

static const struct file_operations ftrace_tr_enable_fops = {
 .open = system_tr_open,
 .read = system_enable_read,
 .write = system_enable_write,
 .llseek = default_llseek,
 .release = subsystem_release,
};

static const struct file_operations ftrace_show_header_page_fops = {
 .open = tracing_open_generic_tr,
 .read = show_header_page_file,
 .llseek = default_llseek,
 .release = tracing_release_generic_tr,
};

static const struct file_operations ftrace_show_header_event_fops = {
 .open = tracing_open_generic_tr,
 .read = show_header_event_file,
 .llseek = default_llseek,
 .release = tracing_release_generic_tr,
};

static int
ftrace_event_open(struct inode *inode, struct file *file,
    const struct seq_operations *seq_ops)
{
 struct seq_file *m;
 int ret;

 ret = security_locked_down(LOCKDOWN_TRACEFS);
 if (ret)
  return ret;

 ret = seq_open(file, seq_ops);
 if (ret < 0)
  return ret;
 m = file->private_data;
 /* copy tr over to seq ops */
 m->private = inode->i_private;

 return ret;
}

static int ftrace_event_release(struct inode *inode, struct file *file)
{
 struct trace_array *tr = inode->i_private;

 trace_array_put(tr);

 return seq_release(inode, file);
}

static int
ftrace_event_avail_open(struct inode *inode, struct file *file)
{
 const struct seq_operations *seq_ops = &show_event_seq_ops;

 /* Checks for tracefs lockdown */
 return ftrace_event_open(inode, file, seq_ops);
}

static int
ftrace_event_set_open(struct inode *inode, struct file *file)
{
 const struct seq_operations *seq_ops = &show_set_event_seq_ops;
 struct trace_array *tr = inode->i_private;
 int ret;

 ret = tracing_check_open_get_tr(tr);
 if (ret)
  return ret;

 if ((file->f_mode & FMODE_WRITE) &&
     (file->f_flags & O_TRUNC))
  ftrace_clear_events(tr);

 ret = ftrace_event_open(inode, file, seq_ops);
 if (ret < 0)
  trace_array_put(tr);
 return ret;
}

static int
ftrace_event_set_pid_open(struct inode *inode, struct file *file)
{
 const struct seq_operations *seq_ops = &show_set_pid_seq_ops;
 struct trace_array *tr = inode->i_private;
 int ret;

 ret = tracing_check_open_get_tr(tr);
 if (ret)
  return ret;

 if ((file->f_mode & FMODE_WRITE) &&
     (file->f_flags & O_TRUNC))
  ftrace_clear_event_pids(tr, TRACE_PIDS);

 ret = ftrace_event_open(inode, file, seq_ops);
 if (ret < 0)
  trace_array_put(tr);
 return ret;
}

static int
ftrace_event_set_npid_open(struct inode *inode, struct file *file)
{
 const struct seq_operations *seq_ops = &show_set_no_pid_seq_ops;
 struct trace_array *tr = inode->i_private;
 int ret;

 ret = tracing_check_open_get_tr(tr);
 if (ret)
  return ret;

 if ((file->f_mode & FMODE_WRITE) &&
     (file->f_flags & O_TRUNC))
  ftrace_clear_event_pids(tr, TRACE_NO_PIDS);

 ret = ftrace_event_open(inode, file, seq_ops);
 if (ret < 0)
  trace_array_put(tr);
 return ret;
}

static struct event_subsystem *
create_new_subsystem(const char *name)
{
 struct event_subsystem *system;

 /* need to create new entry */
 system = kmalloc(sizeof(*system), GFP_KERNEL);
 if (!system)
  return NULL;

 system->ref_count = 1;

 /* Only allocate if dynamic (kprobes and modules) */
 system->name = kstrdup_const(name, GFP_KERNEL);
 if (!system->name)
  goto out_free;

 system->filter = kzalloc(sizeof(struct event_filter), GFP_KERNEL);
 if (!system->filter)
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=97 G=96

¤ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.