Blame view

arch/sh/kernel/unwinder.c 4.19 KB
bf61ad1f8   Matt Fleming   sh: Allow multipl...
1
2
3
4
5
6
7
8
9
10
11
12
13
  /*
   * Copyright (C) 2009  Matt Fleming
   *
   * Based, in part, on kernel/time/clocksource.c.
   *
   * This file provides arbitration code for stack unwinders.
   *
   * Multiple stack unwinders can be available on a system, usually with
   * the most accurate unwinder being the currently active one.
   */
  #include <linux/errno.h>
  #include <linux/list.h>
  #include <linux/spinlock.h>
4ab8f241f   Paul Mundt   sh: Export unwind...
14
  #include <linux/module.h>
bf61ad1f8   Matt Fleming   sh: Allow multipl...
15
  #include <asm/unwinder.h>
60063497a   Arun Sharma   atomic: use <linu...
16
  #include <linux/atomic.h>
bf61ad1f8   Matt Fleming   sh: Allow multipl...
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
  
  /*
   * This is the most basic stack unwinder an architecture can
   * provide. For architectures without reliable frame pointers, e.g.
   * RISC CPUs, it can be implemented by looking through the stack for
   * addresses that lie within the kernel text section.
   *
   * Other CPUs, e.g. x86, can use their frame pointer register to
   * construct more accurate stack traces.
   */
  static struct list_head unwinder_list;
  static struct unwinder stack_reader = {
  	.name = "stack-reader",
  	.dump = stack_reader_dump,
  	.rating = 50,
  	.list = {
  		.next = &unwinder_list,
  		.prev = &unwinder_list,
  	},
  };
  
  /*
   * "curr_unwinder" points to the stack unwinder currently in use. This
   * is the unwinder with the highest rating.
   *
   * "unwinder_list" is a linked-list of all available unwinders, sorted
   * by rating.
   *
   * All modifications of "curr_unwinder" and "unwinder_list" must be
   * performed whilst holding "unwinder_lock".
   */
  static struct unwinder *curr_unwinder = &stack_reader;
  
  static struct list_head unwinder_list = {
  	.next = &stack_reader.list,
  	.prev = &stack_reader.list,
  };
  
  static DEFINE_SPINLOCK(unwinder_lock);
bf61ad1f8   Matt Fleming   sh: Allow multipl...
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
  /**
   * select_unwinder - Select the best registered stack unwinder.
   *
   * Private function. Must hold unwinder_lock when called.
   *
   * Select the stack unwinder with the best rating. This is useful for
   * setting up curr_unwinder.
   */
  static struct unwinder *select_unwinder(void)
  {
  	struct unwinder *best;
  
  	if (list_empty(&unwinder_list))
  		return NULL;
  
  	best = list_entry(unwinder_list.next, struct unwinder, list);
  	if (best == curr_unwinder)
  		return NULL;
  
  	return best;
  }
  
  /*
   * Enqueue the stack unwinder sorted by rating.
   */
  static int unwinder_enqueue(struct unwinder *ops)
  {
  	struct list_head *tmp, *entry = &unwinder_list;
  
  	list_for_each(tmp, &unwinder_list) {
  		struct unwinder *o;
  
  		o = list_entry(tmp, struct unwinder, list);
  		if (o == ops)
  			return -EBUSY;
  		/* Keep track of the place, where to insert */
  		if (o->rating >= ops->rating)
  			entry = tmp;
  	}
  	list_add(&ops->list, entry);
  
  	return 0;
  }
  
  /**
   * unwinder_register - Used to install new stack unwinder
   * @u: unwinder to be registered
   *
   * Install the new stack unwinder on the unwinder list, which is sorted
   * by rating.
   *
   * Returns -EBUSY if registration fails, zero otherwise.
   */
  int unwinder_register(struct unwinder *u)
  {
  	unsigned long flags;
  	int ret;
  
  	spin_lock_irqsave(&unwinder_lock, flags);
  	ret = unwinder_enqueue(u);
  	if (!ret)
  		curr_unwinder = select_unwinder();
  	spin_unlock_irqrestore(&unwinder_lock, flags);
  
  	return ret;
  }
b344e24a8   Matt Fleming   sh: unwinder: Int...
122
  int unwinder_faulted = 0;
bf61ad1f8   Matt Fleming   sh: Allow multipl...
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
  /*
   * Unwind the call stack and pass information to the stacktrace_ops
   * functions. Also handle the case where we need to switch to a new
   * stack dumper because the current one faulted unexpectedly.
   */
  void unwind_stack(struct task_struct *task, struct pt_regs *regs,
  		  unsigned long *sp, const struct stacktrace_ops *ops,
  		  void *data)
  {
  	unsigned long flags;
  
  	/*
  	 * The problem with unwinders with high ratings is that they are
  	 * inherently more complicated than the simple ones with lower
  	 * ratings. We are therefore more likely to fault in the
  	 * complicated ones, e.g. hitting BUG()s. If we fault in the
  	 * code for the current stack unwinder we try to downgrade to
  	 * one with a lower rating.
  	 *
  	 * Hopefully this will give us a semi-reliable stacktrace so we
  	 * can diagnose why curr_unwinder->dump() faulted.
  	 */
b344e24a8   Matt Fleming   sh: unwinder: Int...
145
  	if (unwinder_faulted) {
bf61ad1f8   Matt Fleming   sh: Allow multipl...
146
  		spin_lock_irqsave(&unwinder_lock, flags);
b344e24a8   Matt Fleming   sh: unwinder: Int...
147
148
  		/* Make sure no one beat us to changing the unwinder */
  		if (unwinder_faulted && !list_is_singular(&unwinder_list)) {
bf61ad1f8   Matt Fleming   sh: Allow multipl...
149
150
  			list_del(&curr_unwinder->list);
  			curr_unwinder = select_unwinder();
b344e24a8   Matt Fleming   sh: unwinder: Int...
151
152
  
  			unwinder_faulted = 0;
bf61ad1f8   Matt Fleming   sh: Allow multipl...
153
154
155
  		}
  
  		spin_unlock_irqrestore(&unwinder_lock, flags);
bf61ad1f8   Matt Fleming   sh: Allow multipl...
156
157
158
  	}
  
  	curr_unwinder->dump(task, regs, sp, ops, data);
b344e24a8   Matt Fleming   sh: unwinder: Int...
159
  }
4ab8f241f   Paul Mundt   sh: Export unwind...
160
  EXPORT_SYMBOL_GPL(unwind_stack);