Blame view

block/blktrace.c 21 KB
2056a782f   Jens Axboe   [PATCH] Block que...
1
  /*
0fe234795   Jens Axboe   [PATCH] Update ax...
2
   * Copyright (C) 2006 Jens Axboe <axboe@kernel.dk>
2056a782f   Jens Axboe   [PATCH] Block que...
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
   *
   */
2056a782f   Jens Axboe   [PATCH] Block que...
18
19
20
21
22
23
24
  #include <linux/kernel.h>
  #include <linux/blkdev.h>
  #include <linux/blktrace_api.h>
  #include <linux/percpu.h>
  #include <linux/init.h>
  #include <linux/mutex.h>
  #include <linux/debugfs.h>
be1c63411   Olaf Kirch   [PATCH] blktrace:...
25
  #include <linux/time.h>
5f3ea37c7   Arnaldo Carvalho de Melo   blktrace: port to...
26
  #include <trace/block.h>
2056a782f   Jens Axboe   [PATCH] Block que...
27
  #include <asm/uaccess.h>
2056a782f   Jens Axboe   [PATCH] Block que...
28
  static unsigned int blktrace_seq __read_mostly = 1;
5f3ea37c7   Arnaldo Carvalho de Melo   blktrace: port to...
29
30
31
32
33
34
  /* Global reference count of probes */
  static DEFINE_MUTEX(blk_probe_mutex);
  static atomic_t blk_probes_ref = ATOMIC_INIT(0);
  
  static int blk_register_tracepoints(void);
  static void blk_unregister_tracepoints(void);
2056a782f   Jens Axboe   [PATCH] Block que...
35
  /*
be1c63411   Olaf Kirch   [PATCH] blktrace:...
36
37
   * Send out a notify message.
   */
a863055b1   Jens Axboe   [PATCH] blktrace:...
38
39
  static void trace_note(struct blk_trace *bt, pid_t pid, int action,
  		       const void *data, size_t len)
be1c63411   Olaf Kirch   [PATCH] blktrace:...
40
41
  {
  	struct blk_io_trace *t;
be1c63411   Olaf Kirch   [PATCH] blktrace:...
42
43
  
  	t = relay_reserve(bt->rchan, sizeof(*t) + len);
d3d9d2a5e   Jens Axboe   [PATCH] blktrace:...
44
45
46
47
  	if (t) {
  		const int cpu = smp_processor_id();
  
  		t->magic = BLK_IO_TRACE_MAGIC | BLK_IO_TRACE_VERSION;
2997c8c4a   Ingo Molnar   block: fix blktra...
48
  		t->time = ktime_to_ns(ktime_get());
d3d9d2a5e   Jens Axboe   [PATCH] blktrace:...
49
50
51
52
53
54
55
  		t->device = bt->dev;
  		t->action = action;
  		t->pid = pid;
  		t->cpu = cpu;
  		t->pdu_len = len;
  		memcpy((void *) t + sizeof(*t), data, len);
  	}
be1c63411   Olaf Kirch   [PATCH] blktrace:...
56
57
58
  }
  
  /*
2056a782f   Jens Axboe   [PATCH] Block que...
59
60
61
62
63
   * Send out a notify for this process, if we haven't done so since a trace
   * started
   */
  static void trace_note_tsk(struct blk_trace *bt, struct task_struct *tsk)
  {
a863055b1   Jens Axboe   [PATCH] blktrace:...
64
65
  	tsk->btrace_seq = blktrace_seq;
  	trace_note(bt, tsk->pid, BLK_TN_PROCESS, tsk->comm, sizeof(tsk->comm));
be1c63411   Olaf Kirch   [PATCH] blktrace:...
66
  }
2056a782f   Jens Axboe   [PATCH] Block que...
67

be1c63411   Olaf Kirch   [PATCH] blktrace:...
68
69
70
71
72
73
74
75
76
77
78
79
80
  static void trace_note_time(struct blk_trace *bt)
  {
  	struct timespec now;
  	unsigned long flags;
  	u32 words[2];
  
  	getnstimeofday(&now);
  	words[0] = now.tv_sec;
  	words[1] = now.tv_nsec;
  
  	local_irq_save(flags);
  	trace_note(bt, 0, BLK_TN_TIMESTAMP, words, sizeof(words));
  	local_irq_restore(flags);
2056a782f   Jens Axboe   [PATCH] Block que...
81
  }
9d5f09a42   Alan D. Brunelle   Added in MESSAGE ...
82
83
84
85
  void __trace_note_message(struct blk_trace *bt, const char *fmt, ...)
  {
  	int n;
  	va_list args;
14a73f547   Carl Henrik Lunde   block: disable IR...
86
  	unsigned long flags;
64565911c   Jens Axboe   block: make blktr...
87
  	char *buf;
9d5f09a42   Alan D. Brunelle   Added in MESSAGE ...
88

14a73f547   Carl Henrik Lunde   block: disable IR...
89
  	local_irq_save(flags);
64565911c   Jens Axboe   block: make blktr...
90
  	buf = per_cpu_ptr(bt->msg_data, smp_processor_id());
9d5f09a42   Alan D. Brunelle   Added in MESSAGE ...
91
  	va_start(args, fmt);
64565911c   Jens Axboe   block: make blktr...
92
  	n = vscnprintf(buf, BLK_TN_MAX_MSG, fmt, args);
9d5f09a42   Alan D. Brunelle   Added in MESSAGE ...
93
  	va_end(args);
64565911c   Jens Axboe   block: make blktr...
94
  	trace_note(bt, 0, BLK_TN_MESSAGE, buf, n);
14a73f547   Carl Henrik Lunde   block: disable IR...
95
  	local_irq_restore(flags);
9d5f09a42   Alan D. Brunelle   Added in MESSAGE ...
96
97
  }
  EXPORT_SYMBOL_GPL(__trace_note_message);
2056a782f   Jens Axboe   [PATCH] Block que...
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
  static int act_log_check(struct blk_trace *bt, u32 what, sector_t sector,
  			 pid_t pid)
  {
  	if (((bt->act_mask << BLK_TC_SHIFT) & what) == 0)
  		return 1;
  	if (sector < bt->start_lba || sector > bt->end_lba)
  		return 1;
  	if (bt->pid && pid != bt->pid)
  		return 1;
  
  	return 0;
  }
  
  /*
   * Data direction bit lookup
   */
  static u32 ddir_act[2] __read_mostly = { BLK_TC_ACT(BLK_TC_READ), BLK_TC_ACT(BLK_TC_WRITE) };
35ba8f708   David Woodhouse   blktrace: simplif...
115
116
117
  /* The ilog2() calls fall out because they're constant */
  #define MASK_TC_BIT(rw, __name) ( (rw & (1 << BIO_RW_ ## __name)) << \
  	  (ilog2(BLK_TC_ ## __name) + BLK_TC_SHIFT - BIO_RW_ ## __name) )
2056a782f   Jens Axboe   [PATCH] Block que...
118
119
120
121
122
  
  /*
   * The worker for the various blk_add_trace*() types. Fills out a
   * blk_io_trace structure and places it in a per-cpu subbuffer.
   */
5f3ea37c7   Arnaldo Carvalho de Melo   blktrace: port to...
123
  static void __blk_add_trace(struct blk_trace *bt, sector_t sector, int bytes,
2056a782f   Jens Axboe   [PATCH] Block que...
124
125
126
127
128
129
130
131
132
133
134
135
136
  		     int rw, u32 what, int error, int pdu_len, void *pdu_data)
  {
  	struct task_struct *tsk = current;
  	struct blk_io_trace *t;
  	unsigned long flags;
  	unsigned long *sequence;
  	pid_t pid;
  	int cpu;
  
  	if (unlikely(bt->trace_state != Blktrace_running))
  		return;
  
  	what |= ddir_act[rw & WRITE];
35ba8f708   David Woodhouse   blktrace: simplif...
137
138
139
140
141
  	what |= MASK_TC_BIT(rw, BARRIER);
  	what |= MASK_TC_BIT(rw, SYNC);
  	what |= MASK_TC_BIT(rw, AHEAD);
  	what |= MASK_TC_BIT(rw, META);
  	what |= MASK_TC_BIT(rw, DISCARD);
2056a782f   Jens Axboe   [PATCH] Block que...
142
143
144
145
146
147
148
149
  
  	pid = tsk->pid;
  	if (unlikely(act_log_check(bt, what, sector, pid)))
  		return;
  
  	/*
  	 * A word about the locking here - we disable interrupts to reserve
  	 * some space in the relay per-cpu buffer, to prevent an irq
14a73f547   Carl Henrik Lunde   block: disable IR...
150
  	 * from coming in and stepping on our toes.
2056a782f   Jens Axboe   [PATCH] Block que...
151
152
153
154
155
156
157
158
159
160
161
162
163
  	 */
  	local_irq_save(flags);
  
  	if (unlikely(tsk->btrace_seq != blktrace_seq))
  		trace_note_tsk(bt, tsk);
  
  	t = relay_reserve(bt->rchan, sizeof(*t) + pdu_len);
  	if (t) {
  		cpu = smp_processor_id();
  		sequence = per_cpu_ptr(bt->sequence, cpu);
  
  		t->magic = BLK_IO_TRACE_MAGIC | BLK_IO_TRACE_VERSION;
  		t->sequence = ++(*sequence);
2997c8c4a   Ingo Molnar   block: fix blktra...
164
  		t->time = ktime_to_ns(ktime_get());
2056a782f   Jens Axboe   [PATCH] Block que...
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
  		t->sector = sector;
  		t->bytes = bytes;
  		t->action = what;
  		t->pid = pid;
  		t->device = bt->dev;
  		t->cpu = cpu;
  		t->error = error;
  		t->pdu_len = pdu_len;
  
  		if (pdu_len)
  			memcpy((void *) t + sizeof(*t), pdu_data, pdu_len);
  	}
  
  	local_irq_restore(flags);
  }
2056a782f   Jens Axboe   [PATCH] Block que...
180
  static struct dentry *blk_tree_root;
11a57153e   Jens Axboe   blktrace: kill th...
181
  static DEFINE_MUTEX(blk_tree_mutex);
2056a782f   Jens Axboe   [PATCH] Block que...
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
  static unsigned int root_users;
  
  static inline void blk_remove_root(void)
  {
  	if (blk_tree_root) {
  		debugfs_remove(blk_tree_root);
  		blk_tree_root = NULL;
  	}
  }
  
  static void blk_remove_tree(struct dentry *dir)
  {
  	mutex_lock(&blk_tree_mutex);
  	debugfs_remove(dir);
  	if (--root_users == 0)
  		blk_remove_root();
  	mutex_unlock(&blk_tree_mutex);
  }
  
  static struct dentry *blk_create_tree(const char *blk_name)
  {
  	struct dentry *dir = NULL;
35fc51e7a   Aneesh Kumar K.V   blktrace: Make su...
204
  	int created = 0;
2056a782f   Jens Axboe   [PATCH] Block que...
205
206
207
208
209
210
211
  
  	mutex_lock(&blk_tree_mutex);
  
  	if (!blk_tree_root) {
  		blk_tree_root = debugfs_create_dir("block", NULL);
  		if (!blk_tree_root)
  			goto err;
35fc51e7a   Aneesh Kumar K.V   blktrace: Make su...
212
  		created = 1;
2056a782f   Jens Axboe   [PATCH] Block que...
213
214
215
216
217
  	}
  
  	dir = debugfs_create_dir(blk_name, blk_tree_root);
  	if (dir)
  		root_users++;
35fc51e7a   Aneesh Kumar K.V   blktrace: Make su...
218
219
220
221
222
  	else {
  		/* Delete root only if we created it */
  		if (created)
  			blk_remove_root();
  	}
2056a782f   Jens Axboe   [PATCH] Block que...
223
224
225
226
227
228
229
230
231
  
  err:
  	mutex_unlock(&blk_tree_mutex);
  	return dir;
  }
  
  static void blk_trace_cleanup(struct blk_trace *bt)
  {
  	relay_close(bt->rchan);
02c62304e   Alan D. Brunelle   Added in user-inj...
232
  	debugfs_remove(bt->msg_file);
2056a782f   Jens Axboe   [PATCH] Block que...
233
234
235
  	debugfs_remove(bt->dropped_file);
  	blk_remove_tree(bt->dir);
  	free_percpu(bt->sequence);
64565911c   Jens Axboe   block: make blktr...
236
  	free_percpu(bt->msg_data);
2056a782f   Jens Axboe   [PATCH] Block que...
237
  	kfree(bt);
5f3ea37c7   Arnaldo Carvalho de Melo   blktrace: port to...
238
239
240
241
  	mutex_lock(&blk_probe_mutex);
  	if (atomic_dec_and_test(&blk_probes_ref))
  		blk_unregister_tracepoints();
  	mutex_unlock(&blk_probe_mutex);
2056a782f   Jens Axboe   [PATCH] Block que...
242
  }
6da127ad0   Christof Schmitt   blktrace: Add blk...
243
  int blk_trace_remove(struct request_queue *q)
2056a782f   Jens Axboe   [PATCH] Block que...
244
245
246
247
248
249
250
251
252
253
254
255
256
  {
  	struct blk_trace *bt;
  
  	bt = xchg(&q->blk_trace, NULL);
  	if (!bt)
  		return -EINVAL;
  
  	if (bt->trace_state == Blktrace_setup ||
  	    bt->trace_state == Blktrace_stopped)
  		blk_trace_cleanup(bt);
  
  	return 0;
  }
6da127ad0   Christof Schmitt   blktrace: Add blk...
257
  EXPORT_SYMBOL_GPL(blk_trace_remove);
2056a782f   Jens Axboe   [PATCH] Block que...
258
259
260
  
  static int blk_dropped_open(struct inode *inode, struct file *filp)
  {
8e18e2941   Theodore Ts'o   [PATCH] inode_die...
261
  	filp->private_data = inode->i_private;
2056a782f   Jens Axboe   [PATCH] Block que...
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
  
  	return 0;
  }
  
  static ssize_t blk_dropped_read(struct file *filp, char __user *buffer,
  				size_t count, loff_t *ppos)
  {
  	struct blk_trace *bt = filp->private_data;
  	char buf[16];
  
  	snprintf(buf, sizeof(buf), "%u
  ", atomic_read(&bt->dropped));
  
  	return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
  }
2b8693c06   Arjan van de Ven   [PATCH] mark stru...
277
  static const struct file_operations blk_dropped_fops = {
2056a782f   Jens Axboe   [PATCH] Block que...
278
279
280
281
  	.owner =	THIS_MODULE,
  	.open =		blk_dropped_open,
  	.read =		blk_dropped_read,
  };
02c62304e   Alan D. Brunelle   Added in user-inj...
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
  static int blk_msg_open(struct inode *inode, struct file *filp)
  {
  	filp->private_data = inode->i_private;
  
  	return 0;
  }
  
  static ssize_t blk_msg_write(struct file *filp, const char __user *buffer,
  				size_t count, loff_t *ppos)
  {
  	char *msg;
  	struct blk_trace *bt;
  
  	if (count > BLK_TN_MAX_MSG)
  		return -EINVAL;
  
  	msg = kmalloc(count, GFP_KERNEL);
  	if (msg == NULL)
  		return -ENOMEM;
  
  	if (copy_from_user(msg, buffer, count)) {
  		kfree(msg);
  		return -EFAULT;
  	}
  
  	bt = filp->private_data;
  	__trace_note_message(bt, "%s", msg);
  	kfree(msg);
  
  	return count;
  }
  
  static const struct file_operations blk_msg_fops = {
  	.owner =	THIS_MODULE,
  	.open =		blk_msg_open,
  	.write =	blk_msg_write,
  };
2056a782f   Jens Axboe   [PATCH] Block que...
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
  /*
   * Keep track of how many times we encountered a full subbuffer, to aid
   * the user space app in telling how many lost events there were.
   */
  static int blk_subbuf_start_callback(struct rchan_buf *buf, void *subbuf,
  				     void *prev_subbuf, size_t prev_padding)
  {
  	struct blk_trace *bt;
  
  	if (!relay_buf_full(buf))
  		return 1;
  
  	bt = buf->chan->private_data;
  	atomic_inc(&bt->dropped);
  	return 0;
  }
  
  static int blk_remove_buf_file_callback(struct dentry *dentry)
  {
  	debugfs_remove(dentry);
  	return 0;
  }
  
  static struct dentry *blk_create_buf_file_callback(const char *filename,
  						   struct dentry *parent,
  						   int mode,
  						   struct rchan_buf *buf,
  						   int *is_global)
  {
  	return debugfs_create_file(filename, mode, parent, buf,
  					&relay_file_operations);
  }
  
  static struct rchan_callbacks blk_relay_callbacks = {
  	.subbuf_start		= blk_subbuf_start_callback,
  	.create_buf_file	= blk_create_buf_file_callback,
  	.remove_buf_file	= blk_remove_buf_file_callback,
  };
  
  /*
   * Setup everything required to start tracing
   */
6da127ad0   Christof Schmitt   blktrace: Add blk...
361
  int do_blk_trace_setup(struct request_queue *q, char *name, dev_t dev,
171044d44   Arnd Bergmann   compat_ioctl: han...
362
  			struct blk_user_trace_setup *buts)
2056a782f   Jens Axboe   [PATCH] Block que...
363
  {
2056a782f   Jens Axboe   [PATCH] Block que...
364
365
  	struct blk_trace *old_bt, *bt = NULL;
  	struct dentry *dir = NULL;
2056a782f   Jens Axboe   [PATCH] Block que...
366
  	int ret, i;
171044d44   Arnd Bergmann   compat_ioctl: han...
367
  	if (!buts->buf_size || !buts->buf_nr)
2056a782f   Jens Axboe   [PATCH] Block que...
368
  		return -EINVAL;
0497b345e   Jens Axboe   blktrace: use BLK...
369
370
  	strncpy(buts->name, name, BLKTRACE_BDEV_SIZE);
  	buts->name[BLKTRACE_BDEV_SIZE - 1] = '\0';
2056a782f   Jens Axboe   [PATCH] Block que...
371
372
373
374
375
  
  	/*
  	 * some device names have larger paths - convert the slashes
  	 * to underscores for this to work as expected
  	 */
171044d44   Arnd Bergmann   compat_ioctl: han...
376
377
378
  	for (i = 0; i < strlen(buts->name); i++)
  		if (buts->name[i] == '/')
  			buts->name[i] = '_';
2056a782f   Jens Axboe   [PATCH] Block que...
379
380
381
382
383
384
385
386
387
  
  	ret = -ENOMEM;
  	bt = kzalloc(sizeof(*bt), GFP_KERNEL);
  	if (!bt)
  		goto err;
  
  	bt->sequence = alloc_percpu(unsigned long);
  	if (!bt->sequence)
  		goto err;
64565911c   Jens Axboe   block: make blktr...
388
389
390
  	bt->msg_data = __alloc_percpu(BLK_TN_MAX_MSG);
  	if (!bt->msg_data)
  		goto err;
2056a782f   Jens Axboe   [PATCH] Block que...
391
  	ret = -ENOENT;
171044d44   Arnd Bergmann   compat_ioctl: han...
392
  	dir = blk_create_tree(buts->name);
2056a782f   Jens Axboe   [PATCH] Block que...
393
394
395
396
  	if (!dir)
  		goto err;
  
  	bt->dir = dir;
6da127ad0   Christof Schmitt   blktrace: Add blk...
397
  	bt->dev = dev;
2056a782f   Jens Axboe   [PATCH] Block que...
398
399
400
401
402
403
  	atomic_set(&bt->dropped, 0);
  
  	ret = -EIO;
  	bt->dropped_file = debugfs_create_file("dropped", 0444, dir, bt, &blk_dropped_fops);
  	if (!bt->dropped_file)
  		goto err;
02c62304e   Alan D. Brunelle   Added in user-inj...
404
405
406
  	bt->msg_file = debugfs_create_file("msg", 0222, dir, bt, &blk_msg_fops);
  	if (!bt->msg_file)
  		goto err;
171044d44   Arnd Bergmann   compat_ioctl: han...
407
408
  	bt->rchan = relay_open("trace", dir, buts->buf_size,
  				buts->buf_nr, &blk_relay_callbacks, bt);
2056a782f   Jens Axboe   [PATCH] Block que...
409
410
  	if (!bt->rchan)
  		goto err;
2056a782f   Jens Axboe   [PATCH] Block que...
411

171044d44   Arnd Bergmann   compat_ioctl: han...
412
  	bt->act_mask = buts->act_mask;
2056a782f   Jens Axboe   [PATCH] Block que...
413
414
  	if (!bt->act_mask)
  		bt->act_mask = (u16) -1;
171044d44   Arnd Bergmann   compat_ioctl: han...
415
416
  	bt->start_lba = buts->start_lba;
  	bt->end_lba = buts->end_lba;
2056a782f   Jens Axboe   [PATCH] Block que...
417
418
  	if (!bt->end_lba)
  		bt->end_lba = -1ULL;
171044d44   Arnd Bergmann   compat_ioctl: han...
419
  	bt->pid = buts->pid;
2056a782f   Jens Axboe   [PATCH] Block que...
420
  	bt->trace_state = Blktrace_setup;
5f3ea37c7   Arnaldo Carvalho de Melo   blktrace: port to...
421
422
423
424
425
426
427
  	mutex_lock(&blk_probe_mutex);
  	if (atomic_add_return(1, &blk_probes_ref) == 1) {
  		ret = blk_register_tracepoints();
  		if (ret)
  			goto probe_err;
  	}
  	mutex_unlock(&blk_probe_mutex);
2056a782f   Jens Axboe   [PATCH] Block que...
428
429
430
431
432
433
434
435
  	ret = -EBUSY;
  	old_bt = xchg(&q->blk_trace, bt);
  	if (old_bt) {
  		(void) xchg(&q->blk_trace, old_bt);
  		goto err;
  	}
  
  	return 0;
5f3ea37c7   Arnaldo Carvalho de Melo   blktrace: port to...
436
437
438
  probe_err:
  	atomic_dec(&blk_probes_ref);
  	mutex_unlock(&blk_probe_mutex);
2056a782f   Jens Axboe   [PATCH] Block que...
439
440
441
442
  err:
  	if (dir)
  		blk_remove_tree(dir);
  	if (bt) {
02c62304e   Alan D. Brunelle   Added in user-inj...
443
444
  		if (bt->msg_file)
  			debugfs_remove(bt->msg_file);
2056a782f   Jens Axboe   [PATCH] Block que...
445
446
  		if (bt->dropped_file)
  			debugfs_remove(bt->dropped_file);
a12058687   Alan Stern   [PATCH] Allow NUL...
447
  		free_percpu(bt->sequence);
64565911c   Jens Axboe   block: make blktr...
448
  		free_percpu(bt->msg_data);
2056a782f   Jens Axboe   [PATCH] Block que...
449
450
451
452
453
454
  		if (bt->rchan)
  			relay_close(bt->rchan);
  		kfree(bt);
  	}
  	return ret;
  }
171044d44   Arnd Bergmann   compat_ioctl: han...
455

6da127ad0   Christof Schmitt   blktrace: Add blk...
456
457
  int blk_trace_setup(struct request_queue *q, char *name, dev_t dev,
  		    char __user *arg)
171044d44   Arnd Bergmann   compat_ioctl: han...
458
459
460
461
462
463
464
  {
  	struct blk_user_trace_setup buts;
  	int ret;
  
  	ret = copy_from_user(&buts, arg, sizeof(buts));
  	if (ret)
  		return -EFAULT;
6da127ad0   Christof Schmitt   blktrace: Add blk...
465
  	ret = do_blk_trace_setup(q, name, dev, &buts);
171044d44   Arnd Bergmann   compat_ioctl: han...
466
467
468
469
470
471
472
473
  	if (ret)
  		return ret;
  
  	if (copy_to_user(arg, &buts, sizeof(buts)))
  		return -EFAULT;
  
  	return 0;
  }
6da127ad0   Christof Schmitt   blktrace: Add blk...
474
  EXPORT_SYMBOL_GPL(blk_trace_setup);
2056a782f   Jens Axboe   [PATCH] Block que...
475

6da127ad0   Christof Schmitt   blktrace: Add blk...
476
  int blk_trace_startstop(struct request_queue *q, int start)
2056a782f   Jens Axboe   [PATCH] Block que...
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
  {
  	struct blk_trace *bt;
  	int ret;
  
  	if ((bt = q->blk_trace) == NULL)
  		return -EINVAL;
  
  	/*
  	 * For starting a trace, we can transition from a setup or stopped
  	 * trace. For stopping a trace, the state must be running
  	 */
  	ret = -EINVAL;
  	if (start) {
  		if (bt->trace_state == Blktrace_setup ||
  		    bt->trace_state == Blktrace_stopped) {
  			blktrace_seq++;
  			smp_mb();
  			bt->trace_state = Blktrace_running;
be1c63411   Olaf Kirch   [PATCH] blktrace:...
495
496
  
  			trace_note_time(bt);
2056a782f   Jens Axboe   [PATCH] Block que...
497
498
499
500
501
502
503
504
505
506
507
508
  			ret = 0;
  		}
  	} else {
  		if (bt->trace_state == Blktrace_running) {
  			bt->trace_state = Blktrace_stopped;
  			relay_flush(bt->rchan);
  			ret = 0;
  		}
  	}
  
  	return ret;
  }
6da127ad0   Christof Schmitt   blktrace: Add blk...
509
  EXPORT_SYMBOL_GPL(blk_trace_startstop);
2056a782f   Jens Axboe   [PATCH] Block que...
510
511
512
513
514
515
516
517
518
519
  
  /**
   * blk_trace_ioctl: - handle the ioctls associated with tracing
   * @bdev:	the block device
   * @cmd: 	the ioctl cmd
   * @arg:	the argument data, if any
   *
   **/
  int blk_trace_ioctl(struct block_device *bdev, unsigned cmd, char __user *arg)
  {
165125e1e   Jens Axboe   [BLOCK] Get rid o...
520
  	struct request_queue *q;
2056a782f   Jens Axboe   [PATCH] Block que...
521
  	int ret, start = 0;
6da127ad0   Christof Schmitt   blktrace: Add blk...
522
  	char b[BDEVNAME_SIZE];
2056a782f   Jens Axboe   [PATCH] Block que...
523
524
525
526
527
528
529
530
531
  
  	q = bdev_get_queue(bdev);
  	if (!q)
  		return -ENXIO;
  
  	mutex_lock(&bdev->bd_mutex);
  
  	switch (cmd) {
  	case BLKTRACESETUP:
f36f21ecc   Jean Delvare   Fix misuses of bd...
532
  		bdevname(bdev, b);
6da127ad0   Christof Schmitt   blktrace: Add blk...
533
  		ret = blk_trace_setup(q, b, bdev->bd_dev, arg);
2056a782f   Jens Axboe   [PATCH] Block que...
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
  		break;
  	case BLKTRACESTART:
  		start = 1;
  	case BLKTRACESTOP:
  		ret = blk_trace_startstop(q, start);
  		break;
  	case BLKTRACETEARDOWN:
  		ret = blk_trace_remove(q);
  		break;
  	default:
  		ret = -ENOTTY;
  		break;
  	}
  
  	mutex_unlock(&bdev->bd_mutex);
  	return ret;
  }
  
  /**
   * blk_trace_shutdown: - stop and cleanup trace structures
   * @q:    the request queue associated with the device
   *
   **/
165125e1e   Jens Axboe   [BLOCK] Get rid o...
557
  void blk_trace_shutdown(struct request_queue *q)
2056a782f   Jens Axboe   [PATCH] Block que...
558
  {
6c5c93415   Alexey Dobriyan   [PATCH] ifdef blk...
559
560
561
562
  	if (q->blk_trace) {
  		blk_trace_startstop(q, 0);
  		blk_trace_remove(q);
  	}
2056a782f   Jens Axboe   [PATCH] Block que...
563
  }
5f3ea37c7   Arnaldo Carvalho de Melo   blktrace: port to...
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
  
  /*
   * blktrace probes
   */
  
  /**
   * blk_add_trace_rq - Add a trace for a request oriented action
   * @q:		queue the io is for
   * @rq:		the source request
   * @what:	the action
   *
   * Description:
   *     Records an action against a request. Will log the bio offset + size.
   *
   **/
  static void blk_add_trace_rq(struct request_queue *q, struct request *rq,
  				    u32 what)
  {
  	struct blk_trace *bt = q->blk_trace;
  	int rw = rq->cmd_flags & 0x03;
  
  	if (likely(!bt))
  		return;
  
  	if (blk_discard_rq(rq))
  		rw |= (1 << BIO_RW_DISCARD);
  
  	if (blk_pc_request(rq)) {
  		what |= BLK_TC_ACT(BLK_TC_PC);
  		__blk_add_trace(bt, 0, rq->data_len, rw, what, rq->errors,
  				sizeof(rq->cmd), rq->cmd);
  	} else  {
  		what |= BLK_TC_ACT(BLK_TC_FS);
  		__blk_add_trace(bt, rq->hard_sector, rq->hard_nr_sectors << 9,
  				rw, what, rq->errors, 0, NULL);
  	}
  }
  
  static void blk_add_trace_rq_abort(struct request_queue *q, struct request *rq)
  {
  	blk_add_trace_rq(q, rq, BLK_TA_ABORT);
  }
  
  static void blk_add_trace_rq_insert(struct request_queue *q, struct request *rq)
  {
  	blk_add_trace_rq(q, rq, BLK_TA_INSERT);
  }
  
  static void blk_add_trace_rq_issue(struct request_queue *q, struct request *rq)
  {
  	blk_add_trace_rq(q, rq, BLK_TA_ISSUE);
  }
  
  static void blk_add_trace_rq_requeue(struct request_queue *q, struct request *rq)
  {
  	blk_add_trace_rq(q, rq, BLK_TA_REQUEUE);
  }
  
  static void blk_add_trace_rq_complete(struct request_queue *q, struct request *rq)
  {
  	blk_add_trace_rq(q, rq, BLK_TA_COMPLETE);
  }
  
  /**
   * blk_add_trace_bio - Add a trace for a bio oriented action
   * @q:		queue the io is for
   * @bio:	the source bio
   * @what:	the action
   *
   * Description:
   *     Records an action against a bio. Will log the bio offset + size.
   *
   **/
  static void blk_add_trace_bio(struct request_queue *q, struct bio *bio,
  				     u32 what)
  {
  	struct blk_trace *bt = q->blk_trace;
  
  	if (likely(!bt))
  		return;
  
  	__blk_add_trace(bt, bio->bi_sector, bio->bi_size, bio->bi_rw, what,
  			!bio_flagged(bio, BIO_UPTODATE), 0, NULL);
  }
  
  static void blk_add_trace_bio_bounce(struct request_queue *q, struct bio *bio)
  {
  	blk_add_trace_bio(q, bio, BLK_TA_BOUNCE);
  }
  
  static void blk_add_trace_bio_complete(struct request_queue *q, struct bio *bio)
  {
  	blk_add_trace_bio(q, bio, BLK_TA_COMPLETE);
  }
  
  static void blk_add_trace_bio_backmerge(struct request_queue *q, struct bio *bio)
  {
  	blk_add_trace_bio(q, bio, BLK_TA_BACKMERGE);
  }
  
  static void blk_add_trace_bio_frontmerge(struct request_queue *q, struct bio *bio)
  {
  	blk_add_trace_bio(q, bio, BLK_TA_FRONTMERGE);
  }
  
  static void blk_add_trace_bio_queue(struct request_queue *q, struct bio *bio)
  {
  	blk_add_trace_bio(q, bio, BLK_TA_QUEUE);
  }
  
  static void blk_add_trace_getrq(struct request_queue *q, struct bio *bio, int rw)
  {
  	if (bio)
  		blk_add_trace_bio(q, bio, BLK_TA_GETRQ);
  	else {
  		struct blk_trace *bt = q->blk_trace;
  
  		if (bt)
  			__blk_add_trace(bt, 0, 0, rw, BLK_TA_GETRQ, 0, 0, NULL);
  	}
  }
  
  
  static void blk_add_trace_sleeprq(struct request_queue *q, struct bio *bio, int rw)
  {
  	if (bio)
  		blk_add_trace_bio(q, bio, BLK_TA_SLEEPRQ);
  	else {
  		struct blk_trace *bt = q->blk_trace;
  
  		if (bt)
  			__blk_add_trace(bt, 0, 0, rw, BLK_TA_SLEEPRQ, 0, 0, NULL);
  	}
  }
  
  static void blk_add_trace_plug(struct request_queue *q)
  {
  	struct blk_trace *bt = q->blk_trace;
  
  	if (bt)
  		__blk_add_trace(bt, 0, 0, 0, BLK_TA_PLUG, 0, 0, NULL);
  }
  
  static void blk_add_trace_unplug_io(struct request_queue *q)
  {
  	struct blk_trace *bt = q->blk_trace;
  
  	if (bt) {
  		unsigned int pdu = q->rq.count[READ] + q->rq.count[WRITE];
  		__be64 rpdu = cpu_to_be64(pdu);
  
  		__blk_add_trace(bt, 0, 0, 0, BLK_TA_UNPLUG_IO, 0,
  				sizeof(rpdu), &rpdu);
  	}
  }
  
  static void blk_add_trace_unplug_timer(struct request_queue *q)
  {
  	struct blk_trace *bt = q->blk_trace;
  
  	if (bt) {
  		unsigned int pdu = q->rq.count[READ] + q->rq.count[WRITE];
  		__be64 rpdu = cpu_to_be64(pdu);
  
  		__blk_add_trace(bt, 0, 0, 0, BLK_TA_UNPLUG_TIMER, 0,
  				sizeof(rpdu), &rpdu);
  	}
  }
  
  static void blk_add_trace_split(struct request_queue *q, struct bio *bio,
  				unsigned int pdu)
  {
  	struct blk_trace *bt = q->blk_trace;
  
  	if (bt) {
  		__be64 rpdu = cpu_to_be64(pdu);
  
  		__blk_add_trace(bt, bio->bi_sector, bio->bi_size, bio->bi_rw,
  				BLK_TA_SPLIT, !bio_flagged(bio, BIO_UPTODATE),
  				sizeof(rpdu), &rpdu);
  	}
  }
  
  /**
   * blk_add_trace_remap - Add a trace for a remap operation
   * @q:		queue the io is for
   * @bio:	the source bio
   * @dev:	target device
   * @from:	source sector
   * @to:		target sector
   *
   * Description:
   *     Device mapper or raid target sometimes need to split a bio because
   *     it spans a stripe (or similar). Add a trace for that action.
   *
   **/
  static void blk_add_trace_remap(struct request_queue *q, struct bio *bio,
  				       dev_t dev, sector_t from, sector_t to)
  {
  	struct blk_trace *bt = q->blk_trace;
  	struct blk_io_trace_remap r;
  
  	if (likely(!bt))
  		return;
  
  	r.device = cpu_to_be32(dev);
  	r.device_from = cpu_to_be32(bio->bi_bdev->bd_dev);
  	r.sector = cpu_to_be64(to);
  
  	__blk_add_trace(bt, from, bio->bi_size, bio->bi_rw, BLK_TA_REMAP,
  			!bio_flagged(bio, BIO_UPTODATE), sizeof(r), &r);
  }
  
  /**
   * blk_add_driver_data - Add binary message with driver-specific data
   * @q:		queue the io is for
   * @rq:		io request
   * @data:	driver-specific data
   * @len:	length of driver-specific data
   *
   * Description:
   *     Some drivers might want to write driver-specific data per request.
   *
   **/
  void blk_add_driver_data(struct request_queue *q,
  			 struct request *rq,
  			 void *data, size_t len)
  {
  	struct blk_trace *bt = q->blk_trace;
  
  	if (likely(!bt))
  		return;
  
  	if (blk_pc_request(rq))
  		__blk_add_trace(bt, 0, rq->data_len, 0, BLK_TA_DRV_DATA,
  				rq->errors, len, data);
  	else
  		__blk_add_trace(bt, rq->hard_sector, rq->hard_nr_sectors << 9,
  				0, BLK_TA_DRV_DATA, rq->errors, len, data);
  }
  EXPORT_SYMBOL_GPL(blk_add_driver_data);
  
  static int blk_register_tracepoints(void)
  {
  	int ret;
  
  	ret = register_trace_block_rq_abort(blk_add_trace_rq_abort);
  	WARN_ON(ret);
  	ret = register_trace_block_rq_insert(blk_add_trace_rq_insert);
  	WARN_ON(ret);
  	ret = register_trace_block_rq_issue(blk_add_trace_rq_issue);
  	WARN_ON(ret);
  	ret = register_trace_block_rq_requeue(blk_add_trace_rq_requeue);
  	WARN_ON(ret);
  	ret = register_trace_block_rq_complete(blk_add_trace_rq_complete);
  	WARN_ON(ret);
  	ret = register_trace_block_bio_bounce(blk_add_trace_bio_bounce);
  	WARN_ON(ret);
  	ret = register_trace_block_bio_complete(blk_add_trace_bio_complete);
  	WARN_ON(ret);
  	ret = register_trace_block_bio_backmerge(blk_add_trace_bio_backmerge);
  	WARN_ON(ret);
  	ret = register_trace_block_bio_frontmerge(blk_add_trace_bio_frontmerge);
  	WARN_ON(ret);
  	ret = register_trace_block_bio_queue(blk_add_trace_bio_queue);
  	WARN_ON(ret);
  	ret = register_trace_block_getrq(blk_add_trace_getrq);
  	WARN_ON(ret);
  	ret = register_trace_block_sleeprq(blk_add_trace_sleeprq);
  	WARN_ON(ret);
  	ret = register_trace_block_plug(blk_add_trace_plug);
  	WARN_ON(ret);
  	ret = register_trace_block_unplug_timer(blk_add_trace_unplug_timer);
  	WARN_ON(ret);
  	ret = register_trace_block_unplug_io(blk_add_trace_unplug_io);
  	WARN_ON(ret);
  	ret = register_trace_block_split(blk_add_trace_split);
  	WARN_ON(ret);
  	ret = register_trace_block_remap(blk_add_trace_remap);
  	WARN_ON(ret);
  	return 0;
  }
  
  static void blk_unregister_tracepoints(void)
  {
  	unregister_trace_block_remap(blk_add_trace_remap);
  	unregister_trace_block_split(blk_add_trace_split);
  	unregister_trace_block_unplug_io(blk_add_trace_unplug_io);
  	unregister_trace_block_unplug_timer(blk_add_trace_unplug_timer);
  	unregister_trace_block_plug(blk_add_trace_plug);
  	unregister_trace_block_sleeprq(blk_add_trace_sleeprq);
  	unregister_trace_block_getrq(blk_add_trace_getrq);
  	unregister_trace_block_bio_queue(blk_add_trace_bio_queue);
  	unregister_trace_block_bio_frontmerge(blk_add_trace_bio_frontmerge);
  	unregister_trace_block_bio_backmerge(blk_add_trace_bio_backmerge);
  	unregister_trace_block_bio_complete(blk_add_trace_bio_complete);
  	unregister_trace_block_bio_bounce(blk_add_trace_bio_bounce);
  	unregister_trace_block_rq_complete(blk_add_trace_rq_complete);
  	unregister_trace_block_rq_requeue(blk_add_trace_rq_requeue);
  	unregister_trace_block_rq_issue(blk_add_trace_rq_issue);
  	unregister_trace_block_rq_insert(blk_add_trace_rq_insert);
  	unregister_trace_block_rq_abort(blk_add_trace_rq_abort);
  
  	tracepoint_synchronize_unregister();
  }