Merge branch 'tip/x86/ftrace' of git://git.kernel.org/pub/scm/linux/kernel/git/rosted...
authorIngo Molnar <mingo@elte.hu>
Sun, 22 Feb 2009 17:12:01 +0000 (18:12 +0100)
committerIngo Molnar <mingo@elte.hu>
Sun, 22 Feb 2009 17:12:01 +0000 (18:12 +0100)
Conflicts:
include/linux/ftrace.h
kernel/trace/ftrace.c

1  2 
arch/x86/kernel/ftrace.c
include/linux/ftrace.h
kernel/trace/ftrace.c

diff --combined arch/x86/kernel/ftrace.c
index c2e057d9f88c3cc11826fe240aca782f8c9a6152,c56d73894322bc7445ff745e8430fc7ea3ce8e38..3925ec0184b1dcd2fb1aa5b8af51567677a820be
@@@ -18,6 -18,7 +18,7 @@@
  #include <linux/init.h>
  #include <linux/list.h>
  
+ #include <asm/cacheflush.h>
  #include <asm/ftrace.h>
  #include <linux/ftrace.h>
  #include <asm/nops.h>
  
  #ifdef CONFIG_DYNAMIC_FTRACE
  
+ int ftrace_arch_code_modify_prepare(void)
+ {
+       set_kernel_text_rw();
+       return 0;
+ }
+ int ftrace_arch_code_modify_post_process(void)
+ {
+       set_kernel_text_ro();
+       return 0;
+ }
  union ftrace_code_union {
        char code[MCOUNT_INSN_SIZE];
        struct {
@@@ -82,7 -95,7 +95,7 @@@ static unsigned char *ftrace_call_repla
   * are the same as what exists.
   */
  
 -static atomic_t in_nmi = ATOMIC_INIT(0);
 +static atomic_t nmi_running = ATOMIC_INIT(0);
  static int mod_code_status;           /* holds return value of text write */
  static int mod_code_write;            /* set when NMI should do the write */
  static void *mod_code_ip;             /* holds the IP to write to */
@@@ -111,12 -124,16 +124,16 @@@ static void ftrace_mod_code(void
         */
        mod_code_status = probe_kernel_write(mod_code_ip, mod_code_newcode,
                                             MCOUNT_INSN_SIZE);
+       /* if we fail, then kill any new writers */
+       if (mod_code_status)
+               mod_code_write = 0;
  }
  
  void ftrace_nmi_enter(void)
  {
 -      atomic_inc(&in_nmi);
 -      /* Must have in_nmi seen before reading write flag */
 +      atomic_inc(&nmi_running);
 +      /* Must have nmi_running seen before reading write flag */
        smp_mb();
        if (mod_code_write) {
                ftrace_mod_code();
  
  void ftrace_nmi_exit(void)
  {
 -      /* Finish all executions before clearing in_nmi */
 +      /* Finish all executions before clearing nmi_running */
        smp_wmb();
 -      atomic_dec(&in_nmi);
 +      atomic_dec(&nmi_running);
  }
  
  static void wait_for_nmi(void)
  {
 -      int waited = 0;
 +      if (!atomic_read(&nmi_running))
 +              return;
  
 -      while (atomic_read(&in_nmi)) {
 -              waited = 1;
 +      do {
                cpu_relax();
 -      }
 +      } while (atomic_read(&nmi_running));
  
 -      if (waited)
 -              nmi_wait_count++;
 +      nmi_wait_count++;
  }
  
  static int
@@@ -367,8 -385,100 +384,8 @@@ int ftrace_disable_ftrace_graph_caller(
        return ftrace_mod_jmp(ip, old_offset, new_offset);
  }
  
 -#else /* CONFIG_DYNAMIC_FTRACE */
 -
 -/*
 - * These functions are picked from those used on
 - * this page for dynamic ftrace. They have been
 - * simplified to ignore all traces in NMI context.
 - */
 -static atomic_t in_nmi;
 -
 -void ftrace_nmi_enter(void)
 -{
 -      atomic_inc(&in_nmi);
 -}
 -
 -void ftrace_nmi_exit(void)
 -{
 -      atomic_dec(&in_nmi);
 -}
 -
  #endif /* !CONFIG_DYNAMIC_FTRACE */
  
 -/* Add a function return address to the trace stack on thread info.*/
 -static int push_return_trace(unsigned long ret, unsigned long long time,
 -                              unsigned long func, int *depth)
 -{
 -      int index;
 -
 -      if (!current->ret_stack)
 -              return -EBUSY;
 -
 -      /* The return trace stack is full */
 -      if (current->curr_ret_stack == FTRACE_RETFUNC_DEPTH - 1) {
 -              atomic_inc(&current->trace_overrun);
 -              return -EBUSY;
 -      }
 -
 -      index = ++current->curr_ret_stack;
 -      barrier();
 -      current->ret_stack[index].ret = ret;
 -      current->ret_stack[index].func = func;
 -      current->ret_stack[index].calltime = time;
 -      *depth = index;
 -
 -      return 0;
 -}
 -
 -/* Retrieve a function return address to the trace stack on thread info.*/
 -static void pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret)
 -{
 -      int index;
 -
 -      index = current->curr_ret_stack;
 -
 -      if (unlikely(index < 0)) {
 -              ftrace_graph_stop();
 -              WARN_ON(1);
 -              /* Might as well panic, otherwise we have no where to go */
 -              *ret = (unsigned long)panic;
 -              return;
 -      }
 -
 -      *ret = current->ret_stack[index].ret;
 -      trace->func = current->ret_stack[index].func;
 -      trace->calltime = current->ret_stack[index].calltime;
 -      trace->overrun = atomic_read(&current->trace_overrun);
 -      trace->depth = index;
 -      barrier();
 -      current->curr_ret_stack--;
 -
 -}
 -
 -/*
 - * Send the trace to the ring-buffer.
 - * @return the original return address.
 - */
 -unsigned long ftrace_return_to_handler(void)
 -{
 -      struct ftrace_graph_ret trace;
 -      unsigned long ret;
 -
 -      pop_return_trace(&trace, &ret);
 -      trace.rettime = cpu_clock(raw_smp_processor_id());
 -      ftrace_graph_return(&trace);
 -
 -      if (unlikely(!ret)) {
 -              ftrace_graph_stop();
 -              WARN_ON(1);
 -              /* Might as well panic. What else to do? */
 -              ret = (unsigned long)panic;
 -      }
 -
 -      return ret;
 -}
 -
  /*
   * Hook the return address and push it in the stack of return addrs
   * in current thread info.
@@@ -383,7 -493,7 +400,7 @@@ void prepare_ftrace_return(unsigned lon
                                &return_to_handler;
  
        /* Nmi's are currently unsupported */
 -      if (unlikely(atomic_read(&in_nmi)))
 +      if (unlikely(in_nmi()))
                return;
  
        if (unlikely(atomic_read(&current->tracing_graph_pause)))
                return;
        }
  
 -      if (unlikely(!__kernel_text_address(old))) {
 -              ftrace_graph_stop();
 -              *parent = old;
 -              WARN_ON(1);
 -              return;
 -      }
 -
        calltime = cpu_clock(raw_smp_processor_id());
  
 -      if (push_return_trace(old, calltime,
 +      if (ftrace_push_return_trace(old, calltime,
                                self_addr, &trace.depth) == -EBUSY) {
                *parent = old;
                return;
diff --combined include/linux/ftrace.h
index 915f4723fc8b3e5fa1b68ac1cbc0ee6ada593a76,fdb2a89ae54347331cae40473ae842c4837d9317..847bb3c48dd09edd49ddc0c42f8a835a5a084639
@@@ -95,41 -95,13 +95,44 @@@ stack_trace_sysctl(struct ctl_table *ta
                   loff_t *ppos);
  #endif
  
 +struct ftrace_func_command {
 +      struct list_head        list;
 +      char                    *name;
 +      int                     (*func)(char *func, char *cmd,
 +                                      char *params, int enable);
 +};
 +
  #ifdef CONFIG_DYNAMIC_FTRACE
  /* asm/ftrace.h must be defined for archs supporting dynamic ftrace */
  #include <asm/ftrace.h>
  
+ int ftrace_arch_code_modify_prepare(void);
+ int ftrace_arch_code_modify_post_process(void);
 +struct seq_file;
 +
 +struct ftrace_probe_ops {
 +      void                    (*func)(unsigned long ip,
 +                                      unsigned long parent_ip,
 +                                      void **data);
 +      int                     (*callback)(unsigned long ip, void **data);
 +      void                    (*free)(void **data);
 +      int                     (*print)(struct seq_file *m,
 +                                       unsigned long ip,
 +                                       struct ftrace_probe_ops *ops,
 +                                       void *data);
 +};
 +
 +extern int
 +register_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops,
 +                            void *data);
 +extern void
 +unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops,
 +                              void *data);
 +extern void
 +unregister_ftrace_function_probe_func(char *glob, struct ftrace_probe_ops *ops);
 +extern void unregister_ftrace_function_probe_all(char *glob);
 +
  enum {
        FTRACE_FL_FREE          = (1 << 0),
        FTRACE_FL_FAILED        = (1 << 1),
@@@ -150,9 -122,6 +153,9 @@@ struct dyn_ftrace 
  int ftrace_force_update(void);
  void ftrace_set_filter(unsigned char *buf, int len, int reset);
  
 +int register_ftrace_command(struct ftrace_func_command *cmd);
 +int unregister_ftrace_command(struct ftrace_func_command *cmd);
 +
  /* defined in arch */
  extern int ftrace_ip_converted(unsigned long ip);
  extern int ftrace_dyn_arch_init(void *data);
@@@ -160,10 -129,6 +163,10 @@@ extern int ftrace_update_ftrace_func(ft
  extern void ftrace_caller(void);
  extern void ftrace_call(void);
  extern void mcount_call(void);
 +
 +#ifndef FTRACE_ADDR
 +#define FTRACE_ADDR ((unsigned long)ftrace_caller)
 +#endif
  #ifdef CONFIG_FUNCTION_GRAPH_TRACER
  extern void ftrace_graph_caller(void);
  extern int ftrace_enable_ftrace_graph_caller(void);
@@@ -174,7 -139,7 +177,7 @@@ static inline int ftrace_disable_ftrace
  #endif
  
  /**
 - * ftrace_make_nop - convert code into top
 + * ftrace_make_nop - convert code into nop
   * @mod: module structure if called by module load initialization
   * @rec: the mcount call site record
   * @addr: the address that the call site should be calling
@@@ -236,14 -201,6 +239,14 @@@ extern void ftrace_enable_daemon(void)
  # define ftrace_disable_daemon()              do { } while (0)
  # define ftrace_enable_daemon()                       do { } while (0)
  static inline void ftrace_release(void *start, unsigned long size) { }
 +static inline int register_ftrace_command(struct ftrace_func_command *cmd)
 +{
 +      return -EINVAL;
 +}
 +static inline int unregister_ftrace_command(char *cmd_name)
 +{
 +      return -EINVAL;
 +}
  #endif /* CONFIG_DYNAMIC_FTRACE */
  
  /* totally disable ftrace - can not re-enable after this */
@@@ -344,9 -301,6 +347,9 @@@ ftrace_special(unsigned long arg1, unsi
  extern int
  __ftrace_printk(unsigned long ip, const char *fmt, ...)
        __attribute__ ((format (printf, 2, 3)));
 +# define ftrace_vprintk(fmt, ap) __ftrace_printk(_THIS_IP_, fmt, ap)
 +extern int
 +__ftrace_vprintk(unsigned long ip, const char *fmt, va_list ap);
  extern void ftrace_dump(void);
  #else
  static inline void
@@@ -362,11 -316,6 +365,11 @@@ ftrace_printk(const char *fmt, ...
  {
        return 0;
  }
 +static inline int
 +ftrace_vprintk(const char *fmt, va_list ap)
 +{
 +      return 0;
 +}
  static inline void ftrace_dump(void) { }
  #endif
  
@@@ -381,6 -330,36 +384,6 @@@ ftrace_init_module(struct module *mod
                   unsigned long *start, unsigned long *end) { }
  #endif
  
 -enum {
 -      POWER_NONE = 0,
 -      POWER_CSTATE = 1,
 -      POWER_PSTATE = 2,
 -};
 -
 -struct power_trace {
 -#ifdef CONFIG_POWER_TRACER
 -      ktime_t                 stamp;
 -      ktime_t                 end;
 -      int                     type;
 -      int                     state;
 -#endif
 -};
 -
 -#ifdef CONFIG_POWER_TRACER
 -extern void trace_power_start(struct power_trace *it, unsigned int type,
 -                                      unsigned int state);
 -extern void trace_power_mark(struct power_trace *it, unsigned int type,
 -                                      unsigned int state);
 -extern void trace_power_end(struct power_trace *it);
 -#else
 -static inline void trace_power_start(struct power_trace *it, unsigned int type,
 -                                      unsigned int state) { }
 -static inline void trace_power_mark(struct power_trace *it, unsigned int type,
 -                                      unsigned int state) { }
 -static inline void trace_power_end(struct power_trace *it) { }
 -#endif
 -
 -
  /*
   * Structure that defines an entry function trace.
   */
@@@ -403,30 -382,6 +406,30 @@@ struct ftrace_graph_ret 
  
  #ifdef CONFIG_FUNCTION_GRAPH_TRACER
  
 +/*
 + * Stack of return addresses for functions
 + * of a thread.
 + * Used in struct thread_info
 + */
 +struct ftrace_ret_stack {
 +      unsigned long ret;
 +      unsigned long func;
 +      unsigned long long calltime;
 +};
 +
 +/*
 + * Primary handler of a function return.
 + * It relays on ftrace_return_to_handler.
 + * Defined in entry_32/64.S
 + */
 +extern void return_to_handler(void);
 +
 +extern int
 +ftrace_push_return_trace(unsigned long ret, unsigned long long time,
 +                       unsigned long func, int *depth);
 +extern void
 +ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret);
 +
  /*
   * Sometimes we don't want to trace a function with the function
   * graph tracer but we want them to keep traced by the usual function
@@@ -540,17 -495,4 +543,17 @@@ static inline int test_tsk_trace_graph(
  
  #endif /* CONFIG_TRACING */
  
 +
 +#ifdef CONFIG_HW_BRANCH_TRACER
 +
 +void trace_hw_branch(u64 from, u64 to);
 +void trace_hw_branch_oops(void);
 +
 +#else /* CONFIG_HW_BRANCH_TRACER */
 +
 +static inline void trace_hw_branch(u64 from, u64 to) {}
 +static inline void trace_hw_branch_oops(void) {}
 +
 +#endif /* CONFIG_HW_BRANCH_TRACER */
 +
  #endif /* _LINUX_FTRACE_H */
diff --combined kernel/trace/ftrace.c
index cf59f4c547452cd46b230abc756140257e0ce26a,11ad796ca0495ee9ab0bb47a99f40e86bcfc1d43..5a3a06b21eeee97fd8c527deaf1bd57887402b7b
@@@ -27,7 -27,6 +27,7 @@@
  #include <linux/sysctl.h>
  #include <linux/ctype.h>
  #include <linux/list.h>
 +#include <linux/hash.h>
  
  #include <asm/ftrace.h>
  
                        ftrace_kill();          \
        } while (0)
  
 +/* hash bits for specific function selection */
 +#define FTRACE_HASH_BITS 7
 +#define FTRACE_FUNC_HASHSIZE (1 << FTRACE_HASH_BITS)
 +
  /* ftrace_enabled is a method to turn ftrace on or off */
  int ftrace_enabled __read_mostly;
  static int last_ftrace_enabled;
  
 -/* set when tracing only a pid */
 -struct pid *ftrace_pid_trace;
 -static struct pid * const ftrace_swapper_pid = &init_struct_pid;
 -
  /* Quick disabling of function tracer. */
  int function_trace_stop;
  
@@@ -62,7 -61,9 +62,7 @@@
   */
  static int ftrace_disabled __read_mostly;
  
 -static DEFINE_SPINLOCK(ftrace_lock);
 -static DEFINE_MUTEX(ftrace_sysctl_lock);
 -static DEFINE_MUTEX(ftrace_start_lock);
 +static DEFINE_MUTEX(ftrace_lock);
  
  static struct ftrace_ops ftrace_list_end __read_mostly =
  {
@@@ -133,6 -134,9 +133,6 @@@ static void ftrace_test_stop_func(unsig
  
  static int __register_ftrace_function(struct ftrace_ops *ops)
  {
 -      /* should not be called from interrupt context */
 -      spin_lock(&ftrace_lock);
 -
        ops->next = ftrace_list;
        /*
         * We are entering ops into the ftrace_list but another
  #endif
        }
  
 -      spin_unlock(&ftrace_lock);
 -
        return 0;
  }
  
  static int __unregister_ftrace_function(struct ftrace_ops *ops)
  {
        struct ftrace_ops **p;
 -      int ret = 0;
 -
 -      /* should not be called from interrupt context */
 -      spin_lock(&ftrace_lock);
  
        /*
         * If we are removing the last function, then simply point
        if (ftrace_list == ops && ops->next == &ftrace_list_end) {
                ftrace_trace_function = ftrace_stub;
                ftrace_list = &ftrace_list_end;
 -              goto out;
 +              return 0;
        }
  
        for (p = &ftrace_list; *p != &ftrace_list_end; p = &(*p)->next)
                if (*p == ops)
                        break;
  
 -      if (*p != ops) {
 -              ret = -1;
 -              goto out;
 -      }
 +      if (*p != ops)
 +              return -1;
  
        *p = (*p)->next;
  
                }
        }
  
 - out:
 -      spin_unlock(&ftrace_lock);
 -
 -      return ret;
 +      return 0;
  }
  
  static void ftrace_update_pid_func(void)
  {
        ftrace_func_t func;
  
 -      /* should not be called from interrupt context */
 -      spin_lock(&ftrace_lock);
 +      mutex_lock(&ftrace_lock);
  
        if (ftrace_trace_function == ftrace_stub)
                goto out;
  #endif
  
   out:
 -      spin_unlock(&ftrace_lock);
 +      mutex_unlock(&ftrace_lock);
  }
  
 +/* set when tracing only a pid */
 +struct pid *ftrace_pid_trace;
 +static struct pid * const ftrace_swapper_pid = &init_struct_pid;
 +
  #ifdef CONFIG_DYNAMIC_FTRACE
 +
  #ifndef CONFIG_FTRACE_MCOUNT_RECORD
  # error Dynamic ftrace depends on MCOUNT_RECORD
  #endif
  
 -/*
 - * Since MCOUNT_ADDR may point to mcount itself, we do not want
 - * to get it confused by reading a reference in the code as we
 - * are parsing on objcopy output of text. Use a variable for
 - * it instead.
 - */
 -static unsigned long mcount_addr = MCOUNT_ADDR;
 +static struct hlist_head ftrace_func_hash[FTRACE_FUNC_HASHSIZE] __read_mostly;
 +
 +struct ftrace_func_probe {
 +      struct hlist_node       node;
 +      struct ftrace_probe_ops *ops;
 +      unsigned long           flags;
 +      unsigned long           ip;
 +      void                    *data;
 +      struct rcu_head         rcu;
 +};
 +
  
  enum {
        FTRACE_ENABLE_CALLS             = (1 << 0),
@@@ -283,7 -290,7 +283,7 @@@ static DEFINE_MUTEX(ftrace_regex_lock)
  
  struct ftrace_page {
        struct ftrace_page      *next;
 -      unsigned long           index;
 +      int                     index;
        struct dyn_ftrace       records[];
  };
  
@@@ -298,19 -305,6 +298,19 @@@ static struct ftrace_page        *ftrace_pages
  
  static struct dyn_ftrace *ftrace_free_records;
  
 +/*
 + * This is a double for. Do not use 'break' to break out of the loop,
 + * you must use a goto.
 + */
 +#define do_for_each_ftrace_rec(pg, rec)                                       \
 +      for (pg = ftrace_pages_start; pg; pg = pg->next) {              \
 +              int _____i;                                             \
 +              for (_____i = 0; _____i < pg->index; _____i++) {        \
 +                      rec = &pg->records[_____i];
 +
 +#define while_for_each_ftrace_rec()           \
 +              }                               \
 +      }
  
  #ifdef CONFIG_KPROBES
  
@@@ -355,16 -349,23 +355,16 @@@ void ftrace_release(void *start, unsign
        struct ftrace_page *pg;
        unsigned long s = (unsigned long)start;
        unsigned long e = s + size;
 -      int i;
  
        if (ftrace_disabled || !start)
                return;
  
 -      /* should not be called from interrupt context */
 -      spin_lock(&ftrace_lock);
 -
 -      for (pg = ftrace_pages_start; pg; pg = pg->next) {
 -              for (i = 0; i < pg->index; i++) {
 -                      rec = &pg->records[i];
 -
 -                      if ((rec->ip >= s) && (rec->ip < e))
 -                              ftrace_free_rec(rec);
 -              }
 -      }
 -      spin_unlock(&ftrace_lock);
 +      mutex_lock(&ftrace_lock);
 +      do_for_each_ftrace_rec(pg, rec) {
 +              if ((rec->ip >= s) && (rec->ip < e))
 +                      ftrace_free_rec(rec);
 +      } while_for_each_ftrace_rec();
 +      mutex_unlock(&ftrace_lock);
  }
  
  static struct dyn_ftrace *ftrace_alloc_dyn_node(unsigned long ip)
@@@ -460,10 -461,10 +460,10 @@@ static void ftrace_bug(int failed, unsi
  static int
  __ftrace_replace_code(struct dyn_ftrace *rec, int enable)
  {
 -      unsigned long ip, fl;
        unsigned long ftrace_addr;
 +      unsigned long ip, fl;
  
 -      ftrace_addr = (unsigned long)ftrace_caller;
 +      ftrace_addr = (unsigned long)FTRACE_ADDR;
  
        ip = rec->ip;
  
         * it is not enabled then do nothing.
         *
         * If this record is not to be traced and
 -       * it is enabled then disabled it.
 +       * it is enabled then disable it.
         *
         */
        if (rec->flags & FTRACE_FL_NOTRACE) {
                if (fl == (FTRACE_FL_FILTER | FTRACE_FL_ENABLED))
                        return 0;
  
 -              /* Record is not filtered and is not enabled do nothing */
 +              /* Record is not filtered or enabled, do nothing */
                if (!fl)
                        return 0;
  
  
                } else {
  
 -                      /* if record is not enabled do nothing */
 +                      /* if record is not enabled, do nothing */
                        if (!(rec->flags & FTRACE_FL_ENABLED))
                                return 0;
  
  
  static void ftrace_replace_code(int enable)
  {
 -      int i, failed;
        struct dyn_ftrace *rec;
        struct ftrace_page *pg;
 +      int failed;
  
 -      for (pg = ftrace_pages_start; pg; pg = pg->next) {
 -              for (i = 0; i < pg->index; i++) {
 -                      rec = &pg->records[i];
 -
 -                      /*
 -                       * Skip over free records and records that have
 -                       * failed.
 -                       */
 -                      if (rec->flags & FTRACE_FL_FREE ||
 -                          rec->flags & FTRACE_FL_FAILED)
 -                              continue;
 +      do_for_each_ftrace_rec(pg, rec) {
 +              /*
 +               * Skip over free records and records that have
 +               * failed.
 +               */
 +              if (rec->flags & FTRACE_FL_FREE ||
 +                  rec->flags & FTRACE_FL_FAILED)
 +                      continue;
  
 -                      /* ignore updates to this record's mcount site */
 -                      if (get_kprobe((void *)rec->ip)) {
 -                              freeze_record(rec);
 -                              continue;
 -                      } else {
 -                              unfreeze_record(rec);
 -                      }
 +              /* ignore updates to this record's mcount site */
 +              if (get_kprobe((void *)rec->ip)) {
 +                      freeze_record(rec);
 +                      continue;
 +              } else {
 +                      unfreeze_record(rec);
 +              }
  
 -                      failed = __ftrace_replace_code(rec, enable);
 -                      if (failed && (rec->flags & FTRACE_FL_CONVERTED)) {
 -                              rec->flags |= FTRACE_FL_FAILED;
 -                              if ((system_state == SYSTEM_BOOTING) ||
 -                                  !core_kernel_text(rec->ip)) {
 -                                      ftrace_free_rec(rec);
 +              failed = __ftrace_replace_code(rec, enable);
 +              if (failed && (rec->flags & FTRACE_FL_CONVERTED)) {
 +                      rec->flags |= FTRACE_FL_FAILED;
 +                      if ((system_state == SYSTEM_BOOTING) ||
 +                          !core_kernel_text(rec->ip)) {
 +                              ftrace_free_rec(rec);
-                       } else
+                               } else {
 -                                      ftrace_bug(failed, rec->ip);
 +                              ftrace_bug(failed, rec->ip);
+                                       /* Stop processing */
+                                       return;
+                               }
 -                      }
                }
 -      }
 +      } while_for_each_ftrace_rec();
  }
  
  static int
@@@ -571,7 -579,7 +574,7 @@@ ftrace_code_disable(struct module *mod
  
        ip = rec->ip;
  
 -      ret = ftrace_make_nop(mod, rec, mcount_addr);
 +      ret = ftrace_make_nop(mod, rec, MCOUNT_ADDR);
        if (ret) {
                ftrace_bug(ret, ip);
                rec->flags |= FTRACE_FL_FAILED;
        return 1;
  }
  
+ /*
+  * archs can override this function if they must do something
+  * before the modifying code is performed.
+  */
+ int __weak ftrace_arch_code_modify_prepare(void)
+ {
+       return 0;
+ }
+ /*
+  * archs can override this function if they must do something
+  * after the modifying code is performed.
+  */
+ int __weak ftrace_arch_code_modify_post_process(void)
+ {
+       return 0;
+ }
  static int __ftrace_modify_code(void *data)
  {
        int *command = data;
  
  static void ftrace_run_update_code(int command)
  {
+       int ret;
+       ret = ftrace_arch_code_modify_prepare();
+       FTRACE_WARN_ON(ret);
+       if (ret)
+               return;
        stop_machine(__ftrace_modify_code, &command, NULL);
+       ret = ftrace_arch_code_modify_post_process();
+       FTRACE_WARN_ON(ret);
  }
  
  static ftrace_func_t saved_ftrace_func;
@@@ -626,10 -662,13 +657,10 @@@ static void ftrace_startup(int command
        if (unlikely(ftrace_disabled))
                return;
  
 -      mutex_lock(&ftrace_start_lock);
        ftrace_start_up++;
        command |= FTRACE_ENABLE_CALLS;
  
        ftrace_startup_enable(command);
 -
 -      mutex_unlock(&ftrace_start_lock);
  }
  
  static void ftrace_shutdown(int command)
        if (unlikely(ftrace_disabled))
                return;
  
 -      mutex_lock(&ftrace_start_lock);
        ftrace_start_up--;
        if (!ftrace_start_up)
                command |= FTRACE_DISABLE_CALLS;
        }
  
        if (!command || !ftrace_enabled)
 -              goto out;
 +              return;
  
        ftrace_run_update_code(command);
 - out:
 -      mutex_unlock(&ftrace_start_lock);
  }
  
  static void ftrace_startup_sysctl(void)
        if (unlikely(ftrace_disabled))
                return;
  
 -      mutex_lock(&ftrace_start_lock);
        /* Force update next time */
        saved_ftrace_func = NULL;
        /* ftrace_start_up is true if we want ftrace running */
                command |= FTRACE_ENABLE_CALLS;
  
        ftrace_run_update_code(command);
 -      mutex_unlock(&ftrace_start_lock);
  }
  
  static void ftrace_shutdown_sysctl(void)
        if (unlikely(ftrace_disabled))
                return;
  
 -      mutex_lock(&ftrace_start_lock);
        /* ftrace_start_up is true if ftrace is running */
        if (ftrace_start_up)
                command |= FTRACE_DISABLE_CALLS;
  
        ftrace_run_update_code(command);
 -      mutex_unlock(&ftrace_start_lock);
  }
  
  static cycle_t                ftrace_update_time;
@@@ -766,106 -812,29 +797,106 @@@ enum 
        FTRACE_ITER_CONT        = (1 << 1),
        FTRACE_ITER_NOTRACE     = (1 << 2),
        FTRACE_ITER_FAILURES    = (1 << 3),
 +      FTRACE_ITER_PRINTALL    = (1 << 4),
 +      FTRACE_ITER_HASH        = (1 << 5),
  };
  
  #define FTRACE_BUFF_MAX (KSYM_SYMBOL_LEN+4) /* room for wildcards */
  
  struct ftrace_iterator {
        struct ftrace_page      *pg;
 -      unsigned                idx;
 +      int                     hidx;
 +      int                     idx;
        unsigned                flags;
        unsigned char           buffer[FTRACE_BUFF_MAX+1];
        unsigned                buffer_idx;
        unsigned                filtered;
  };
  
 +static void *
 +t_hash_next(struct seq_file *m, void *v, loff_t *pos)
 +{
 +      struct ftrace_iterator *iter = m->private;
 +      struct hlist_node *hnd = v;
 +      struct hlist_head *hhd;
 +
 +      WARN_ON(!(iter->flags & FTRACE_ITER_HASH));
 +
 +      (*pos)++;
 +
 + retry:
 +      if (iter->hidx >= FTRACE_FUNC_HASHSIZE)
 +              return NULL;
 +
 +      hhd = &ftrace_func_hash[iter->hidx];
 +
 +      if (hlist_empty(hhd)) {
 +              iter->hidx++;
 +              hnd = NULL;
 +              goto retry;
 +      }
 +
 +      if (!hnd)
 +              hnd = hhd->first;
 +      else {
 +              hnd = hnd->next;
 +              if (!hnd) {
 +                      iter->hidx++;
 +                      goto retry;
 +              }
 +      }
 +
 +      return hnd;
 +}
 +
 +static void *t_hash_start(struct seq_file *m, loff_t *pos)
 +{
 +      struct ftrace_iterator *iter = m->private;
 +      void *p = NULL;
 +
 +      iter->flags |= FTRACE_ITER_HASH;
 +
 +      return t_hash_next(m, p, pos);
 +}
 +
 +static int t_hash_show(struct seq_file *m, void *v)
 +{
 +      struct ftrace_func_probe *rec;
 +      struct hlist_node *hnd = v;
 +      char str[KSYM_SYMBOL_LEN];
 +
 +      rec = hlist_entry(hnd, struct ftrace_func_probe, node);
 +
 +      if (rec->ops->print)
 +              return rec->ops->print(m, rec->ip, rec->ops, rec->data);
 +
 +      kallsyms_lookup(rec->ip, NULL, NULL, NULL, str);
 +      seq_printf(m, "%s:", str);
 +
 +      kallsyms_lookup((unsigned long)rec->ops->func, NULL, NULL, NULL, str);
 +      seq_printf(m, "%s", str);
 +
 +      if (rec->data)
 +              seq_printf(m, ":%p", rec->data);
 +      seq_putc(m, '\n');
 +
 +      return 0;
 +}
 +
  static void *
  t_next(struct seq_file *m, void *v, loff_t *pos)
  {
        struct ftrace_iterator *iter = m->private;
        struct dyn_ftrace *rec = NULL;
  
 +      if (iter->flags & FTRACE_ITER_HASH)
 +              return t_hash_next(m, v, pos);
 +
        (*pos)++;
  
 -      /* should not be called from interrupt context */
 -      spin_lock(&ftrace_lock);
 +      if (iter->flags & FTRACE_ITER_PRINTALL)
 +              return NULL;
 +
   retry:
        if (iter->idx >= iter->pg->index) {
                if (iter->pg->next) {
                        goto retry;
                }
        }
 -      spin_unlock(&ftrace_lock);
  
        return rec;
  }
@@@ -903,23 -873,6 +934,23 @@@ static void *t_start(struct seq_file *m
        struct ftrace_iterator *iter = m->private;
        void *p = NULL;
  
 +      mutex_lock(&ftrace_lock);
 +      /*
 +       * For set_ftrace_filter reading, if we have the filter
 +       * off, we can short cut and just print out that all
 +       * functions are enabled.
 +       */
 +      if (iter->flags & FTRACE_ITER_FILTER && !ftrace_filtered) {
 +              if (*pos > 0)
 +                      return t_hash_start(m, pos);
 +              iter->flags |= FTRACE_ITER_PRINTALL;
 +              (*pos)++;
 +              return iter;
 +      }
 +
 +      if (iter->flags & FTRACE_ITER_HASH)
 +              return t_hash_start(m, pos);
 +
        if (*pos > 0) {
                if (iter->idx < 0)
                        return p;
  
        p = t_next(m, p, pos);
  
 +      if (!p)
 +              return t_hash_start(m, pos);
 +
        return p;
  }
  
  static void t_stop(struct seq_file *m, void *p)
  {
 +      mutex_unlock(&ftrace_lock);
  }
  
  static int t_show(struct seq_file *m, void *v)
  {
 +      struct ftrace_iterator *iter = m->private;
        struct dyn_ftrace *rec = v;
        char str[KSYM_SYMBOL_LEN];
  
 +      if (iter->flags & FTRACE_ITER_HASH)
 +              return t_hash_show(m, v);
 +
 +      if (iter->flags & FTRACE_ITER_PRINTALL) {
 +              seq_printf(m, "#### all functions enabled ####\n");
 +              return 0;
 +      }
 +
        if (!rec)
                return 0;
  
@@@ -1032,16 -972,23 +1063,16 @@@ static void ftrace_filter_reset(int ena
        struct ftrace_page *pg;
        struct dyn_ftrace *rec;
        unsigned long type = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE;
 -      unsigned i;
  
 -      /* should not be called from interrupt context */
 -      spin_lock(&ftrace_lock);
 +      mutex_lock(&ftrace_lock);
        if (enable)
                ftrace_filtered = 0;
 -      pg = ftrace_pages_start;
 -      while (pg) {
 -              for (i = 0; i < pg->index; i++) {
 -                      rec = &pg->records[i];
 -                      if (rec->flags & FTRACE_FL_FAILED)
 -                              continue;
 -                      rec->flags &= ~type;
 -              }
 -              pg = pg->next;
 -      }
 -      spin_unlock(&ftrace_lock);
 +      do_for_each_ftrace_rec(pg, rec) {
 +              if (rec->flags & FTRACE_FL_FAILED)
 +                      continue;
 +              rec->flags &= ~type;
 +      } while_for_each_ftrace_rec();
 +      mutex_unlock(&ftrace_lock);
  }
  
  static int
@@@ -1122,536 -1069,86 +1153,536 @@@ enum 
        MATCH_END_ONLY,
  };
  
 -static void
 -ftrace_match(unsigned char *buff, int len, int enable)
 +/*
 + * (static function - no need for kernel doc)
 + *
 + * Pass in a buffer containing a glob and this function will
 + * set search to point to the search part of the buffer and
 + * return the type of search it is (see enum above).
 + * This does modify buff.
 + *
 + * Returns enum type.
 + *  search returns the pointer to use for comparison.
 + *  not returns 1 if buff started with a '!'
 + *     0 otherwise.
 + */
 +static int
 +ftrace_setup_glob(char *buff, int len, char **search, int *not)
  {
 -      char str[KSYM_SYMBOL_LEN];
 -      char *search = NULL;
 -      struct ftrace_page *pg;
 -      struct dyn_ftrace *rec;
        int type = MATCH_FULL;
 -      unsigned long flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE;
 -      unsigned i, match = 0, search_len = 0;
 -      int not = 0;
 +      int i;
  
        if (buff[0] == '!') {
 -              not = 1;
 +              *not = 1;
                buff++;
                len--;
 -      }
 +      } else
 +              *not = 0;
 +
 +      *search = buff;
  
        for (i = 0; i < len; i++) {
                if (buff[i] == '*') {
                        if (!i) {
 -                              search = buff + i + 1;
 +                              *search = buff + 1;
                                type = MATCH_END_ONLY;
 -                              search_len = len - (i + 1);
                        } else {
 -                              if (type == MATCH_END_ONLY) {
 +                              if (type == MATCH_END_ONLY)
                                        type = MATCH_MIDDLE_ONLY;
 -                              } else {
 -                                      match = i;
 +                              else
                                        type = MATCH_FRONT_ONLY;
 -                              }
                                buff[i] = 0;
                                break;
                        }
                }
        }
  
 -      /* should not be called from interrupt context */
 -      spin_lock(&ftrace_lock);
 -      if (enable)
 -              ftrace_filtered = 1;
 -      pg = ftrace_pages_start;
 -      while (pg) {
 -              for (i = 0; i < pg->index; i++) {
 -                      int matched = 0;
 -                      char *ptr;
 -
 -                      rec = &pg->records[i];
 -                      if (rec->flags & FTRACE_FL_FAILED)
 +      return type;
 +}
 +
 +static int ftrace_match(char *str, char *regex, int len, int type)
 +{
 +      int matched = 0;
 +      char *ptr;
 +
 +      switch (type) {
 +      case MATCH_FULL:
 +              if (strcmp(str, regex) == 0)
 +                      matched = 1;
 +              break;
 +      case MATCH_FRONT_ONLY:
 +              if (strncmp(str, regex, len) == 0)
 +                      matched = 1;
 +              break;
 +      case MATCH_MIDDLE_ONLY:
 +              if (strstr(str, regex))
 +                      matched = 1;
 +              break;
 +      case MATCH_END_ONLY:
 +              ptr = strstr(str, regex);
 +              if (ptr && (ptr[len] == 0))
 +                      matched = 1;
 +              break;
 +      }
 +
 +      return matched;
 +}
 +
 +static int
 +ftrace_match_record(struct dyn_ftrace *rec, char *regex, int len, int type)
 +{
 +      char str[KSYM_SYMBOL_LEN];
 +
 +      kallsyms_lookup(rec->ip, NULL, NULL, NULL, str);
 +      return ftrace_match(str, regex, len, type);
 +}
 +
 +static void ftrace_match_records(char *buff, int len, int enable)
 +{
 +      unsigned int search_len;
 +      struct ftrace_page *pg;
 +      struct dyn_ftrace *rec;
 +      unsigned long flag;
 +      char *search;
 +      int type;
 +      int not;
 +
 +      flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE;
 +      type = ftrace_setup_glob(buff, len, &search, &not);
 +
 +      search_len = strlen(search);
 +
 +      mutex_lock(&ftrace_lock);
 +      do_for_each_ftrace_rec(pg, rec) {
 +
 +              if (rec->flags & FTRACE_FL_FAILED)
 +                      continue;
 +
 +              if (ftrace_match_record(rec, search, search_len, type)) {
 +                      if (not)
 +                              rec->flags &= ~flag;
 +                      else
 +                              rec->flags |= flag;
 +              }
 +              /*
 +               * Only enable filtering if we have a function that
 +               * is filtered on.
 +               */
 +              if (enable && (rec->flags & FTRACE_FL_FILTER))
 +                      ftrace_filtered = 1;
 +      } while_for_each_ftrace_rec();
 +      mutex_unlock(&ftrace_lock);
 +}
 +
 +static int
 +ftrace_match_module_record(struct dyn_ftrace *rec, char *mod,
 +                         char *regex, int len, int type)
 +{
 +      char str[KSYM_SYMBOL_LEN];
 +      char *modname;
 +
 +      kallsyms_lookup(rec->ip, NULL, NULL, &modname, str);
 +
 +      if (!modname || strcmp(modname, mod))
 +              return 0;
 +
 +      /* blank search means to match all funcs in the mod */
 +      if (len)
 +              return ftrace_match(str, regex, len, type);
 +      else
 +              return 1;
 +}
 +
 +static void ftrace_match_module_records(char *buff, char *mod, int enable)
 +{
 +      unsigned search_len = 0;
 +      struct ftrace_page *pg;
 +      struct dyn_ftrace *rec;
 +      int type = MATCH_FULL;
 +      char *search = buff;
 +      unsigned long flag;
 +      int not = 0;
 +
 +      flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE;
 +
 +      /* blank or '*' mean the same */
 +      if (strcmp(buff, "*") == 0)
 +              buff[0] = 0;
 +
 +      /* handle the case of 'dont filter this module' */
 +      if (strcmp(buff, "!") == 0 || strcmp(buff, "!*") == 0) {
 +              buff[0] = 0;
 +              not = 1;
 +      }
 +
 +      if (strlen(buff)) {
 +              type = ftrace_setup_glob(buff, strlen(buff), &search, &not);
 +              search_len = strlen(search);
 +      }
 +
 +      mutex_lock(&ftrace_lock);
 +      do_for_each_ftrace_rec(pg, rec) {
 +
 +              if (rec->flags & FTRACE_FL_FAILED)
 +                      continue;
 +
 +              if (ftrace_match_module_record(rec, mod,
 +                                             search, search_len, type)) {
 +                      if (not)
 +                              rec->flags &= ~flag;
 +                      else
 +                              rec->flags |= flag;
 +              }
 +              if (enable && (rec->flags & FTRACE_FL_FILTER))
 +                      ftrace_filtered = 1;
 +
 +      } while_for_each_ftrace_rec();
 +      mutex_unlock(&ftrace_lock);
 +}
 +
 +/*
 + * We register the module command as a template to show others how
 + * to register the a command as well.
 + */
 +
 +static int
 +ftrace_mod_callback(char *func, char *cmd, char *param, int enable)
 +{
 +      char *mod;
 +
 +      /*
 +       * cmd == 'mod' because we only registered this func
 +       * for the 'mod' ftrace_func_command.
 +       * But if you register one func with multiple commands,
 +       * you can tell which command was used by the cmd
 +       * parameter.
 +       */
 +
 +      /* we must have a module name */
 +      if (!param)
 +              return -EINVAL;
 +
 +      mod = strsep(&param, ":");
 +      if (!strlen(mod))
 +              return -EINVAL;
 +
 +      ftrace_match_module_records(func, mod, enable);
 +      return 0;
 +}
 +
 +static struct ftrace_func_command ftrace_mod_cmd = {
 +      .name                   = "mod",
 +      .func                   = ftrace_mod_callback,
 +};
 +
 +static int __init ftrace_mod_cmd_init(void)
 +{
 +      return register_ftrace_command(&ftrace_mod_cmd);
 +}
 +device_initcall(ftrace_mod_cmd_init);
 +
 +static void
 +function_trace_probe_call(unsigned long ip, unsigned long parent_ip)
 +{
 +      struct ftrace_func_probe *entry;
 +      struct hlist_head *hhd;
 +      struct hlist_node *n;
 +      unsigned long key;
 +      int resched;
 +
 +      key = hash_long(ip, FTRACE_HASH_BITS);
 +
 +      hhd = &ftrace_func_hash[key];
 +
 +      if (hlist_empty(hhd))
 +              return;
 +
 +      /*
 +       * Disable preemption for these calls to prevent a RCU grace
 +       * period. This syncs the hash iteration and freeing of items
 +       * on the hash. rcu_read_lock is too dangerous here.
 +       */
 +      resched = ftrace_preempt_disable();
 +      hlist_for_each_entry_rcu(entry, n, hhd, node) {
 +              if (entry->ip == ip)
 +                      entry->ops->func(ip, parent_ip, &entry->data);
 +      }
 +      ftrace_preempt_enable(resched);
 +}
 +
 +static struct ftrace_ops trace_probe_ops __read_mostly =
 +{
 +      .func = function_trace_probe_call,
 +};
 +
 +static int ftrace_probe_registered;
 +
 +static void __enable_ftrace_function_probe(void)
 +{
 +      int i;
 +
 +      if (ftrace_probe_registered)
 +              return;
 +
 +      for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) {
 +              struct hlist_head *hhd = &ftrace_func_hash[i];
 +              if (hhd->first)
 +                      break;
 +      }
 +      /* Nothing registered? */
 +      if (i == FTRACE_FUNC_HASHSIZE)
 +              return;
 +
 +      __register_ftrace_function(&trace_probe_ops);
 +      ftrace_startup(0);
 +      ftrace_probe_registered = 1;
 +}
 +
 +static void __disable_ftrace_function_probe(void)
 +{
 +      int i;
 +
 +      if (!ftrace_probe_registered)
 +              return;
 +
 +      for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) {
 +              struct hlist_head *hhd = &ftrace_func_hash[i];
 +              if (hhd->first)
 +                      return;
 +      }
 +
 +      /* no more funcs left */
 +      __unregister_ftrace_function(&trace_probe_ops);
 +      ftrace_shutdown(0);
 +      ftrace_probe_registered = 0;
 +}
 +
 +
 +static void ftrace_free_entry_rcu(struct rcu_head *rhp)
 +{
 +      struct ftrace_func_probe *entry =
 +              container_of(rhp, struct ftrace_func_probe, rcu);
 +
 +      if (entry->ops->free)
 +              entry->ops->free(&entry->data);
 +      kfree(entry);
 +}
 +
 +
 +int
 +register_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops,
 +                            void *data)
 +{
 +      struct ftrace_func_probe *entry;
 +      struct ftrace_page *pg;
 +      struct dyn_ftrace *rec;
 +      int type, len, not;
 +      unsigned long key;
 +      int count = 0;
 +      char *search;
 +
 +      type = ftrace_setup_glob(glob, strlen(glob), &search, &not);
 +      len = strlen(search);
 +
 +      /* we do not support '!' for function probes */
 +      if (WARN_ON(not))
 +              return -EINVAL;
 +
 +      mutex_lock(&ftrace_lock);
 +      do_for_each_ftrace_rec(pg, rec) {
 +
 +              if (rec->flags & FTRACE_FL_FAILED)
 +                      continue;
 +
 +              if (!ftrace_match_record(rec, search, len, type))
 +                      continue;
 +
 +              entry = kmalloc(sizeof(*entry), GFP_KERNEL);
 +              if (!entry) {
 +                      /* If we did not process any, then return error */
 +                      if (!count)
 +                              count = -ENOMEM;
 +                      goto out_unlock;
 +              }
 +
 +              count++;
 +
 +              entry->data = data;
 +
 +              /*
 +               * The caller might want to do something special
 +               * for each function we find. We call the callback
 +               * to give the caller an opportunity to do so.
 +               */
 +              if (ops->callback) {
 +                      if (ops->callback(rec->ip, &entry->data) < 0) {
 +                              /* caller does not like this func */
 +                              kfree(entry);
                                continue;
 -                      kallsyms_lookup(rec->ip, NULL, NULL, NULL, str);
 -                      switch (type) {
 -                      case MATCH_FULL:
 -                              if (strcmp(str, buff) == 0)
 -                                      matched = 1;
 -                              break;
 -                      case MATCH_FRONT_ONLY:
 -                              if (memcmp(str, buff, match) == 0)
 -                                      matched = 1;
 -                              break;
 -                      case MATCH_MIDDLE_ONLY:
 -                              if (strstr(str, search))
 -                                      matched = 1;
 -                              break;
 -                      case MATCH_END_ONLY:
 -                              ptr = strstr(str, search);
 -                              if (ptr && (ptr[search_len] == 0))
 -                                      matched = 1;
 -                              break;
                        }
 -                      if (matched) {
 -                              if (not)
 -                                      rec->flags &= ~flag;
 -                              else
 -                                      rec->flags |= flag;
 +              }
 +
 +              entry->ops = ops;
 +              entry->ip = rec->ip;
 +
 +              key = hash_long(entry->ip, FTRACE_HASH_BITS);
 +              hlist_add_head_rcu(&entry->node, &ftrace_func_hash[key]);
 +
 +      } while_for_each_ftrace_rec();
 +      __enable_ftrace_function_probe();
 +
 + out_unlock:
 +      mutex_unlock(&ftrace_lock);
 +
 +      return count;
 +}
 +
 +enum {
 +      PROBE_TEST_FUNC         = 1,
 +      PROBE_TEST_DATA         = 2
 +};
 +
 +static void
 +__unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops,
 +                                void *data, int flags)
 +{
 +      struct ftrace_func_probe *entry;
 +      struct hlist_node *n, *tmp;
 +      char str[KSYM_SYMBOL_LEN];
 +      int type = MATCH_FULL;
 +      int i, len = 0;
 +      char *search;
 +
 +      if (glob && (strcmp(glob, "*") || !strlen(glob)))
 +              glob = NULL;
 +      else {
 +              int not;
 +
 +              type = ftrace_setup_glob(glob, strlen(glob), &search, &not);
 +              len = strlen(search);
 +
 +              /* we do not support '!' for function probes */
 +              if (WARN_ON(not))
 +                      return;
 +      }
 +
 +      mutex_lock(&ftrace_lock);
 +      for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) {
 +              struct hlist_head *hhd = &ftrace_func_hash[i];
 +
 +              hlist_for_each_entry_safe(entry, n, tmp, hhd, node) {
 +
 +                      /* break up if statements for readability */
 +                      if ((flags & PROBE_TEST_FUNC) && entry->ops != ops)
 +                              continue;
 +
 +                      if ((flags & PROBE_TEST_DATA) && entry->data != data)
 +                              continue;
 +
 +                      /* do this last, since it is the most expensive */
 +                      if (glob) {
 +                              kallsyms_lookup(entry->ip, NULL, NULL,
 +                                              NULL, str);
 +                              if (!ftrace_match(str, glob, len, type))
 +                                      continue;
                        }
 +
 +                      hlist_del(&entry->node);
 +                      call_rcu(&entry->rcu, ftrace_free_entry_rcu);
 +              }
 +      }
 +      __disable_ftrace_function_probe();
 +      mutex_unlock(&ftrace_lock);
 +}
 +
 +void
 +unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops,
 +                              void *data)
 +{
 +      __unregister_ftrace_function_probe(glob, ops, data,
 +                                        PROBE_TEST_FUNC | PROBE_TEST_DATA);
 +}
 +
 +void
 +unregister_ftrace_function_probe_func(char *glob, struct ftrace_probe_ops *ops)
 +{
 +      __unregister_ftrace_function_probe(glob, ops, NULL, PROBE_TEST_FUNC);
 +}
 +
 +void unregister_ftrace_function_probe_all(char *glob)
 +{
 +      __unregister_ftrace_function_probe(glob, NULL, NULL, 0);
 +}
 +
 +static LIST_HEAD(ftrace_commands);
 +static DEFINE_MUTEX(ftrace_cmd_mutex);
 +
 +int register_ftrace_command(struct ftrace_func_command *cmd)
 +{
 +      struct ftrace_func_command *p;
 +      int ret = 0;
 +
 +      mutex_lock(&ftrace_cmd_mutex);
 +      list_for_each_entry(p, &ftrace_commands, list) {
 +              if (strcmp(cmd->name, p->name) == 0) {
 +                      ret = -EBUSY;
 +                      goto out_unlock;
 +              }
 +      }
 +      list_add(&cmd->list, &ftrace_commands);
 + out_unlock:
 +      mutex_unlock(&ftrace_cmd_mutex);
 +
 +      return ret;
 +}
 +
 +int unregister_ftrace_command(struct ftrace_func_command *cmd)
 +{
 +      struct ftrace_func_command *p, *n;
 +      int ret = -ENODEV;
 +
 +      mutex_lock(&ftrace_cmd_mutex);
 +      list_for_each_entry_safe(p, n, &ftrace_commands, list) {
 +              if (strcmp(cmd->name, p->name) == 0) {
 +                      ret = 0;
 +                      list_del_init(&p->list);
 +                      goto out_unlock;
                }
 -              pg = pg->next;
        }
 -      spin_unlock(&ftrace_lock);
 + out_unlock:
 +      mutex_unlock(&ftrace_cmd_mutex);
 +
 +      return ret;
 +}
 +
 +static int ftrace_process_regex(char *buff, int len, int enable)
 +{
 +      char *func, *command, *next = buff;
 +      struct ftrace_func_command *p;
 +      int ret = -EINVAL;
 +
 +      func = strsep(&next, ":");
 +
 +      if (!next) {
 +              ftrace_match_records(func, len, enable);
 +              return 0;
 +      }
 +
 +      /* command found */
 +
 +      command = strsep(&next, ":");
 +
 +      mutex_lock(&ftrace_cmd_mutex);
 +      list_for_each_entry(p, &ftrace_commands, list) {
 +              if (strcmp(p->name, command) == 0) {
 +                      ret = p->func(func, command, next, enable);
 +                      goto out_unlock;
 +              }
 +      }
 + out_unlock:
 +      mutex_unlock(&ftrace_cmd_mutex);
 +
 +      return ret;
  }
  
  static ssize_t
@@@ -1721,10 -1218,7 +1752,10 @@@ ftrace_regex_write(struct file *file, c
        if (isspace(ch)) {
                iter->filtered++;
                iter->buffer[iter->buffer_idx] = 0;
 -              ftrace_match(iter->buffer, iter->buffer_idx, enable);
 +              ret = ftrace_process_regex(iter->buffer,
 +                                         iter->buffer_idx, enable);
 +              if (ret)
 +                      goto out;
                iter->buffer_idx = 0;
        } else
                iter->flags |= FTRACE_ITER_CONT;
@@@ -1763,7 -1257,7 +1794,7 @@@ ftrace_set_regex(unsigned char *buf, in
        if (reset)
                ftrace_filter_reset(enable);
        if (buf)
 -              ftrace_match(buf, len, enable);
 +              ftrace_match_records(buf, len, enable);
        mutex_unlock(&ftrace_regex_lock);
  }
  
@@@ -1813,13 -1307,15 +1844,13 @@@ ftrace_regex_release(struct inode *inod
        if (iter->buffer_idx) {
                iter->filtered++;
                iter->buffer[iter->buffer_idx] = 0;
 -              ftrace_match(iter->buffer, iter->buffer_idx, enable);
 +              ftrace_match_records(iter->buffer, iter->buffer_idx, enable);
        }
  
 -      mutex_lock(&ftrace_sysctl_lock);
 -      mutex_lock(&ftrace_start_lock);
 +      mutex_lock(&ftrace_lock);
        if (ftrace_start_up && ftrace_enabled)
                ftrace_run_update_code(FTRACE_ENABLE_CALLS);
 -      mutex_unlock(&ftrace_start_lock);
 -      mutex_unlock(&ftrace_sysctl_lock);
 +      mutex_unlock(&ftrace_lock);
  
        kfree(iter);
        mutex_unlock(&ftrace_regex_lock);
@@@ -1895,10 -1391,6 +1926,10 @@@ static void *g_start(struct seq_file *m
  
        mutex_lock(&graph_lock);
  
 +      /* Nothing, tell g_show to print all functions are enabled */
 +      if (!ftrace_graph_count && !*pos)
 +              return (void *)1;
 +
        p = g_next(m, p, pos);
  
        return p;
@@@ -1917,11 -1409,6 +1948,11 @@@ static int g_show(struct seq_file *m, v
        if (!ptr)
                return 0;
  
 +      if (ptr == (unsigned long *)1) {
 +              seq_printf(m, "#### all functions enabled ####\n");
 +              return 0;
 +      }
 +
        kallsyms_lookup(*ptr, NULL, NULL, NULL, str);
  
        seq_printf(m, "%s\n", str);
@@@ -1975,52 -1462,42 +2006,52 @@@ ftrace_graph_read(struct file *file, ch
  }
  
  static int
 -ftrace_set_func(unsigned long *array, int idx, char *buffer)
 +ftrace_set_func(unsigned long *array, int *idx, char *buffer)
  {
 -      char str[KSYM_SYMBOL_LEN];
        struct dyn_ftrace *rec;
        struct ftrace_page *pg;
 +      int search_len;
        int found = 0;
 -      int i, j;
 +      int type, not;
 +      char *search;
 +      bool exists;
 +      int i;
  
        if (ftrace_disabled)
                return -ENODEV;
  
 -      /* should not be called from interrupt context */
 -      spin_lock(&ftrace_lock);
 +      /* decode regex */
 +      type = ftrace_setup_glob(buffer, strlen(buffer), &search, &not);
 +      if (not)
 +              return -EINVAL;
  
 -      for (pg = ftrace_pages_start; pg; pg = pg->next) {
 -              for (i = 0; i < pg->index; i++) {
 -                      rec = &pg->records[i];
 +      search_len = strlen(search);
  
 -                      if (rec->flags & (FTRACE_FL_FAILED | FTRACE_FL_FREE))
 -                              continue;
 +      mutex_lock(&ftrace_lock);
 +      do_for_each_ftrace_rec(pg, rec) {
 +
 +              if (*idx >= FTRACE_GRAPH_MAX_FUNCS)
 +                      break;
  
 -                      kallsyms_lookup(rec->ip, NULL, NULL, NULL, str);
 -                      if (strcmp(str, buffer) == 0) {
 +              if (rec->flags & (FTRACE_FL_FAILED | FTRACE_FL_FREE))
 +                      continue;
 +
 +              if (ftrace_match_record(rec, search, search_len, type)) {
 +                      /* ensure it is not already in the array */
 +                      exists = false;
 +                      for (i = 0; i < *idx; i++)
 +                              if (array[i] == rec->ip) {
 +                                      exists = true;
 +                                      break;
 +                              }
 +                      if (!exists) {
 +                              array[(*idx)++] = rec->ip;
                                found = 1;
 -                              for (j = 0; j < idx; j++)
 -                                      if (array[j] == rec->ip) {
 -                                              found = 0;
 -                                              break;
 -                                      }
 -                              if (found)
 -                                      array[idx] = rec->ip;
 -                              break;
                        }
                }
 -      }
 -      spin_unlock(&ftrace_lock);
 +      } while_for_each_ftrace_rec();
 +
 +      mutex_unlock(&ftrace_lock);
  
        return found ? 0 : -EINVAL;
  }
@@@ -2088,11 -1565,13 +2119,11 @@@ ftrace_graph_write(struct file *file, c
        }
        buffer[index] = 0;
  
 -      /* we allow only one at a time */
 -      ret = ftrace_set_func(array, ftrace_graph_count, buffer);
 +      /* we allow only one expression at a time */
 +      ret = ftrace_set_func(array, &ftrace_graph_count, buffer);
        if (ret)
                goto out;
  
 -      ftrace_graph_count++;
 -
        file->f_pos += read;
  
        ret = read;
@@@ -2156,7 -1635,7 +2187,7 @@@ static int ftrace_convert_nops(struct m
        unsigned long addr;
        unsigned long flags;
  
 -      mutex_lock(&ftrace_start_lock);
 +      mutex_lock(&ftrace_lock);
        p = start;
        while (p < end) {
                addr = ftrace_call_adjust(*p++);
        local_irq_save(flags);
        ftrace_update_code(mod);
        local_irq_restore(flags);
 -      mutex_unlock(&ftrace_start_lock);
 +      mutex_unlock(&ftrace_lock);
  
        return 0;
  }
@@@ -2348,7 -1827,7 +2379,7 @@@ ftrace_pid_write(struct file *filp, con
        if (ret < 0)
                return ret;
  
 -      mutex_lock(&ftrace_start_lock);
 +      mutex_lock(&ftrace_lock);
        if (val < 0) {
                /* disable pid tracing */
                if (!ftrace_pid_trace)
        ftrace_startup_enable(0);
  
   out:
 -      mutex_unlock(&ftrace_start_lock);
 +      mutex_unlock(&ftrace_lock);
  
        return cnt;
  }
@@@ -2415,6 -1894,7 +2446,6 @@@ static __init int ftrace_init_debugfs(v
                           "'set_ftrace_pid' entry\n");
        return 0;
  }
 -
  fs_initcall(ftrace_init_debugfs);
  
  /**
@@@ -2449,17 -1929,17 +2480,17 @@@ int register_ftrace_function(struct ftr
        if (unlikely(ftrace_disabled))
                return -1;
  
 -      mutex_lock(&ftrace_sysctl_lock);
 +      mutex_lock(&ftrace_lock);
  
        ret = __register_ftrace_function(ops);
        ftrace_startup(0);
  
 -      mutex_unlock(&ftrace_sysctl_lock);
 +      mutex_unlock(&ftrace_lock);
        return ret;
  }
  
  /**
 - * unregister_ftrace_function - unresgister a function for profiling.
 + * unregister_ftrace_function - unregister a function for profiling.
   * @ops - ops structure that holds the function to unregister
   *
   * Unregister a function that was added to be called by ftrace profiling.
@@@ -2468,10 -1948,10 +2499,10 @@@ int unregister_ftrace_function(struct f
  {
        int ret;
  
 -      mutex_lock(&ftrace_sysctl_lock);
 +      mutex_lock(&ftrace_lock);
        ret = __unregister_ftrace_function(ops);
        ftrace_shutdown(0);
 -      mutex_unlock(&ftrace_sysctl_lock);
 +      mutex_unlock(&ftrace_lock);
  
        return ret;
  }
@@@ -2486,7 -1966,7 +2517,7 @@@ ftrace_enable_sysctl(struct ctl_table *
        if (unlikely(ftrace_disabled))
                return -ENODEV;
  
 -      mutex_lock(&ftrace_sysctl_lock);
 +      mutex_lock(&ftrace_lock);
  
        ret  = proc_dointvec(table, write, file, buffer, lenp, ppos);
  
        }
  
   out:
 -      mutex_unlock(&ftrace_sysctl_lock);
 +      mutex_unlock(&ftrace_lock);
        return ret;
  }
  
@@@ -2631,7 -2111,7 +2662,7 @@@ int register_ftrace_graph(trace_func_gr
  {
        int ret = 0;
  
 -      mutex_lock(&ftrace_sysctl_lock);
 +      mutex_lock(&ftrace_lock);
  
        ftrace_suspend_notifier.notifier_call = ftrace_suspend_notifier_call;
        register_pm_notifier(&ftrace_suspend_notifier);
        ftrace_startup(FTRACE_START_FUNC_RET);
  
  out:
 -      mutex_unlock(&ftrace_sysctl_lock);
 +      mutex_unlock(&ftrace_lock);
        return ret;
  }
  
  void unregister_ftrace_graph(void)
  {
 -      mutex_lock(&ftrace_sysctl_lock);
 +      mutex_lock(&ftrace_lock);
  
        atomic_dec(&ftrace_graph_active);
        ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub;
        ftrace_shutdown(FTRACE_STOP_FUNC_RET);
        unregister_pm_notifier(&ftrace_suspend_notifier);
  
 -      mutex_unlock(&ftrace_sysctl_lock);
 +      mutex_unlock(&ftrace_lock);
  }
  
  /* Allocate a return stack for newly created task */