Blame view
kernel/trace/blktrace.c
42.3 KB
2056a782f
|
1 |
/* |
0fe234795
|
2 |
* Copyright (C) 2006 Jens Axboe <axboe@kernel.dk> |
2056a782f
|
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
|
18 19 20 21 22 23 |
#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> |
5a0e3ad6a
|
24 |
#include <linux/slab.h> |
2056a782f
|
25 |
#include <linux/debugfs.h> |
6e5fdeedc
|
26 |
#include <linux/export.h> |
be1c63411
|
27 |
#include <linux/time.h> |
939b36697
|
28 |
#include <linux/uaccess.h> |
a404d5576
|
29 |
#include <linux/list.h> |
55782138e
|
30 31 |
#include <trace/events/block.h> |
2db270a80
|
32 |
#include "trace_output.h" |
2056a782f
|
33 |
|
55782138e
|
34 |
#ifdef CONFIG_BLK_DEV_IO_TRACE |
2056a782f
|
35 |
static unsigned int blktrace_seq __read_mostly = 1; |
c71a89615
|
36 |
static struct trace_array *blk_tr; |
5006ea73f
|
37 |
static bool blk_tracer_enabled __read_mostly; |
c71a89615
|
38 |
|
a404d5576
|
39 40 |
static LIST_HEAD(running_trace_list); static __cacheline_aligned_in_smp DEFINE_SPINLOCK(running_trace_lock); |
c71a89615
|
41 |
/* Select an alternative, minimalistic output than the original one */ |
ef18012b2
|
42 |
#define TRACE_BLK_OPT_CLASSIC 0x1 |
c71a89615
|
43 44 45 |
static struct tracer_opt blk_tracer_opts[] = { /* Default disable the minimalistic output */ |
157f9c00e
|
46 |
{ TRACER_OPT(blk_classic, TRACE_BLK_OPT_CLASSIC) }, |
c71a89615
|
47 48 49 50 51 52 53 |
{ } }; static struct tracer_flags blk_tracer_flags = { .val = 0, .opts = blk_tracer_opts, }; |
5f3ea37c7
|
54 |
/* Global reference count of probes */ |
5f3ea37c7
|
55 |
static atomic_t blk_probes_ref = ATOMIC_INIT(0); |
3c289ba7c
|
56 |
static void blk_register_tracepoints(void); |
5f3ea37c7
|
57 |
static void blk_unregister_tracepoints(void); |
2056a782f
|
58 |
/* |
be1c63411
|
59 60 |
* Send out a notify message. */ |
a863055b1
|
61 62 |
static void trace_note(struct blk_trace *bt, pid_t pid, int action, const void *data, size_t len) |
be1c63411
|
63 64 |
{ struct blk_io_trace *t; |
18cea4591
|
65 |
struct ring_buffer_event *event = NULL; |
e77405ad8
|
66 |
struct ring_buffer *buffer = NULL; |
18cea4591
|
67 68 69 70 71 |
int pc = 0; int cpu = smp_processor_id(); bool blk_tracer = blk_tracer_enabled; if (blk_tracer) { |
12883efb6
|
72 |
buffer = blk_tr->trace_buffer.buffer; |
18cea4591
|
73 |
pc = preempt_count(); |
e77405ad8
|
74 |
event = trace_buffer_lock_reserve(buffer, TRACE_BLK, |
18cea4591
|
75 76 77 78 79 80 81 |
sizeof(*t) + len, 0, pc); if (!event) return; t = ring_buffer_event_data(event); goto record_it; } |
be1c63411
|
82 |
|
c71a89615
|
83 84 |
if (!bt->rchan) return; |
be1c63411
|
85 |
t = relay_reserve(bt->rchan, sizeof(*t) + len); |
d3d9d2a5e
|
86 |
if (t) { |
d3d9d2a5e
|
87 |
t->magic = BLK_IO_TRACE_MAGIC | BLK_IO_TRACE_VERSION; |
2997c8c4a
|
88 |
t->time = ktime_to_ns(ktime_get()); |
18cea4591
|
89 |
record_it: |
d3d9d2a5e
|
90 91 92 93 94 95 |
t->device = bt->dev; t->action = action; t->pid = pid; t->cpu = cpu; t->pdu_len = len; memcpy((void *) t + sizeof(*t), data, len); |
18cea4591
|
96 97 |
if (blk_tracer) |
e77405ad8
|
98 |
trace_buffer_unlock_commit(buffer, event, 0, pc); |
d3d9d2a5e
|
99 |
} |
be1c63411
|
100 101 102 |
} /* |
2056a782f
|
103 104 105 |
* Send out a notify for this process, if we haven't done so since a trace * started */ |
a404d5576
|
106 |
static void trace_note_tsk(struct task_struct *tsk) |
2056a782f
|
107 |
{ |
a404d5576
|
108 109 |
unsigned long flags; struct blk_trace *bt; |
a863055b1
|
110 |
tsk->btrace_seq = blktrace_seq; |
a404d5576
|
111 112 113 114 115 116 |
spin_lock_irqsave(&running_trace_lock, flags); list_for_each_entry(bt, &running_trace_list, running_list) { trace_note(bt, tsk->pid, BLK_TN_PROCESS, tsk->comm, sizeof(tsk->comm)); } spin_unlock_irqrestore(&running_trace_lock, flags); |
be1c63411
|
117 |
} |
2056a782f
|
118 |
|
be1c63411
|
119 120 121 122 123 124 125 126 127 128 129 130 131 |
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
|
132 |
} |
9d5f09a42
|
133 134 135 136 |
void __trace_note_message(struct blk_trace *bt, const char *fmt, ...) { int n; va_list args; |
14a73f547
|
137 |
unsigned long flags; |
64565911c
|
138 |
char *buf; |
9d5f09a42
|
139 |
|
18cea4591
|
140 141 |
if (unlikely(bt->trace_state != Blktrace_running && !blk_tracer_enabled)) |
c71a89615
|
142 |
return; |
490da40d8
|
143 144 145 146 147 148 |
/* * If the BLK_TC_NOTIFY action mask isn't set, don't send any note * message to the trace. */ if (!(bt->act_mask & BLK_TC_NOTIFY)) return; |
14a73f547
|
149 |
local_irq_save(flags); |
d8a0349c0
|
150 |
buf = this_cpu_ptr(bt->msg_data); |
9d5f09a42
|
151 |
va_start(args, fmt); |
64565911c
|
152 |
n = vscnprintf(buf, BLK_TN_MAX_MSG, fmt, args); |
9d5f09a42
|
153 |
va_end(args); |
64565911c
|
154 |
trace_note(bt, 0, BLK_TN_MESSAGE, buf, n); |
14a73f547
|
155 |
local_irq_restore(flags); |
9d5f09a42
|
156 157 |
} EXPORT_SYMBOL_GPL(__trace_note_message); |
2056a782f
|
158 159 160 161 162 |
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; |
d0deef5b1
|
163 |
if (sector && (sector < bt->start_lba || sector > bt->end_lba)) |
2056a782f
|
164 165 166 167 168 169 170 171 172 173 |
return 1; if (bt->pid && pid != bt->pid) return 1; return 0; } /* * Data direction bit lookup */ |
e4955c998
|
174 175 |
static const u32 ddir_act[2] = { BLK_TC_ACT(BLK_TC_READ), BLK_TC_ACT(BLK_TC_WRITE) }; |
2056a782f
|
176 |
|
7b6d91dae
|
177 |
#define BLK_TC_RAHEAD BLK_TC_AHEAD |
35ba8f708
|
178 |
/* The ilog2() calls fall out because they're constant */ |
7b6d91dae
|
179 180 |
#define MASK_TC_BIT(rw, __name) ((rw & REQ_ ## __name) << \ (ilog2(BLK_TC_ ## __name) + BLK_TC_SHIFT - __REQ_ ## __name)) |
2056a782f
|
181 182 183 184 185 |
/* * 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
|
186 |
static void __blk_add_trace(struct blk_trace *bt, sector_t sector, int bytes, |
2056a782f
|
187 188 189 |
int rw, u32 what, int error, int pdu_len, void *pdu_data) { struct task_struct *tsk = current; |
c71a89615
|
190 |
struct ring_buffer_event *event = NULL; |
e77405ad8
|
191 |
struct ring_buffer *buffer = NULL; |
2056a782f
|
192 |
struct blk_io_trace *t; |
0a9877514
|
193 |
unsigned long flags = 0; |
2056a782f
|
194 195 |
unsigned long *sequence; pid_t pid; |
c71a89615
|
196 |
int cpu, pc = 0; |
18cea4591
|
197 |
bool blk_tracer = blk_tracer_enabled; |
2056a782f
|
198 |
|
18cea4591
|
199 |
if (unlikely(bt->trace_state != Blktrace_running && !blk_tracer)) |
2056a782f
|
200 201 202 |
return; what |= ddir_act[rw & WRITE]; |
7b6d91dae
|
203 204 |
what |= MASK_TC_BIT(rw, SYNC); what |= MASK_TC_BIT(rw, RAHEAD); |
35ba8f708
|
205 206 |
what |= MASK_TC_BIT(rw, META); what |= MASK_TC_BIT(rw, DISCARD); |
c09c47cae
|
207 208 |
what |= MASK_TC_BIT(rw, FLUSH); what |= MASK_TC_BIT(rw, FUA); |
2056a782f
|
209 210 |
pid = tsk->pid; |
d0deef5b1
|
211 |
if (act_log_check(bt, what, sector, pid)) |
2056a782f
|
212 |
return; |
c71a89615
|
213 |
cpu = raw_smp_processor_id(); |
18cea4591
|
214 |
if (blk_tracer) { |
c71a89615
|
215 |
tracing_record_cmdline(current); |
12883efb6
|
216 |
buffer = blk_tr->trace_buffer.buffer; |
51a763dd8
|
217 |
pc = preempt_count(); |
e77405ad8
|
218 |
event = trace_buffer_lock_reserve(buffer, TRACE_BLK, |
51a763dd8
|
219 220 |
sizeof(*t) + pdu_len, 0, pc); |
c71a89615
|
221 222 |
if (!event) return; |
51a763dd8
|
223 |
t = ring_buffer_event_data(event); |
c71a89615
|
224 225 |
goto record_it; } |
2056a782f
|
226 |
|
a404d5576
|
227 228 |
if (unlikely(tsk->btrace_seq != blktrace_seq)) trace_note_tsk(tsk); |
2056a782f
|
229 230 231 |
/* * A word about the locking here - we disable interrupts to reserve * some space in the relay per-cpu buffer, to prevent an irq |
14a73f547
|
232 |
* from coming in and stepping on our toes. |
2056a782f
|
233 234 |
*/ local_irq_save(flags); |
2056a782f
|
235 236 |
t = relay_reserve(bt->rchan, sizeof(*t) + pdu_len); if (t) { |
2056a782f
|
237 238 239 240 |
sequence = per_cpu_ptr(bt->sequence, cpu); t->magic = BLK_IO_TRACE_MAGIC | BLK_IO_TRACE_VERSION; t->sequence = ++(*sequence); |
2997c8c4a
|
241 |
t->time = ktime_to_ns(ktime_get()); |
c71a89615
|
242 |
record_it: |
08a06b83f
|
243 |
/* |
939b36697
|
244 245 246 247 248 249 250 |
* These two are not needed in ftrace as they are in the * generic trace_entry, filled by tracing_generic_entry_update, * but for the trace_event->bin() synthesizer benefit we do it * here too. */ t->cpu = cpu; t->pid = pid; |
08a06b83f
|
251 |
|
2056a782f
|
252 253 254 |
t->sector = sector; t->bytes = bytes; t->action = what; |
2056a782f
|
255 |
t->device = bt->dev; |
2056a782f
|
256 257 258 259 260 |
t->error = error; t->pdu_len = pdu_len; if (pdu_len) memcpy((void *) t + sizeof(*t), pdu_data, pdu_len); |
c71a89615
|
261 |
|
18cea4591
|
262 |
if (blk_tracer) { |
e77405ad8
|
263 |
trace_buffer_unlock_commit(buffer, event, 0, pc); |
c71a89615
|
264 265 |
return; } |
2056a782f
|
266 267 268 269 |
} local_irq_restore(flags); } |
2056a782f
|
270 |
static struct dentry *blk_tree_root; |
11a57153e
|
271 |
static DEFINE_MUTEX(blk_tree_mutex); |
2056a782f
|
272 |
|
ad5dd5493
|
273 |
static void blk_trace_free(struct blk_trace *bt) |
2056a782f
|
274 |
{ |
02c62304e
|
275 |
debugfs_remove(bt->msg_file); |
2056a782f
|
276 |
debugfs_remove(bt->dropped_file); |
f48fc4d32
|
277 |
relay_close(bt->rchan); |
39cbb602b
|
278 |
debugfs_remove(bt->dir); |
2056a782f
|
279 |
free_percpu(bt->sequence); |
64565911c
|
280 |
free_percpu(bt->msg_data); |
2056a782f
|
281 |
kfree(bt); |
ad5dd5493
|
282 283 284 285 286 |
} static void blk_trace_cleanup(struct blk_trace *bt) { blk_trace_free(bt); |
5f3ea37c7
|
287 288 |
if (atomic_dec_and_test(&blk_probes_ref)) blk_unregister_tracepoints(); |
2056a782f
|
289 |
} |
6da127ad0
|
290 |
int blk_trace_remove(struct request_queue *q) |
2056a782f
|
291 292 293 294 295 296 |
{ struct blk_trace *bt; bt = xchg(&q->blk_trace, NULL); if (!bt) return -EINVAL; |
555472048
|
297 |
if (bt->trace_state != Blktrace_running) |
2056a782f
|
298 299 300 301 |
blk_trace_cleanup(bt); return 0; } |
6da127ad0
|
302 |
EXPORT_SYMBOL_GPL(blk_trace_remove); |
2056a782f
|
303 |
|
2056a782f
|
304 305 306 307 308 309 310 311 312 313 314 |
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
|
315 |
static const struct file_operations blk_dropped_fops = { |
2056a782f
|
316 |
.owner = THIS_MODULE, |
234e34058
|
317 |
.open = simple_open, |
2056a782f
|
318 |
.read = blk_dropped_read, |
6038f373a
|
319 |
.llseek = default_llseek, |
2056a782f
|
320 |
}; |
02c62304e
|
321 322 323 324 325 |
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; |
7635b03ad
|
326 |
if (count >= BLK_TN_MAX_MSG) |
02c62304e
|
327 |
return -EINVAL; |
a4b3ada83
|
328 |
msg = kmalloc(count + 1, GFP_KERNEL); |
02c62304e
|
329 330 331 332 333 334 335 |
if (msg == NULL) return -ENOMEM; if (copy_from_user(msg, buffer, count)) { kfree(msg); return -EFAULT; } |
a4b3ada83
|
336 |
msg[count] = '\0'; |
02c62304e
|
337 338 339 340 341 342 343 344 345 |
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, |
234e34058
|
346 |
.open = simple_open, |
02c62304e
|
347 |
.write = blk_msg_write, |
6038f373a
|
348 |
.llseek = noop_llseek, |
02c62304e
|
349 |
}; |
2056a782f
|
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 |
/* * 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); |
f48fc4d32
|
370 |
|
2056a782f
|
371 372 373 374 375 |
return 0; } static struct dentry *blk_create_buf_file_callback(const char *filename, struct dentry *parent, |
f4ae40a6a
|
376 |
umode_t mode, |
2056a782f
|
377 378 379 380 381 382 383 384 385 386 387 388 |
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, }; |
9908c3099
|
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 |
static void blk_trace_setup_lba(struct blk_trace *bt, struct block_device *bdev) { struct hd_struct *part = NULL; if (bdev) part = bdev->bd_part; if (part) { bt->start_lba = part->start_sect; bt->end_lba = part->start_sect + part->nr_sects; } else { bt->start_lba = 0; bt->end_lba = -1ULL; } } |
2056a782f
|
405 406 407 |
/* * Setup everything required to start tracing */ |
6da127ad0
|
408 |
int do_blk_trace_setup(struct request_queue *q, char *name, dev_t dev, |
d0deef5b1
|
409 410 |
struct block_device *bdev, struct blk_user_trace_setup *buts) |
2056a782f
|
411 |
{ |
2056a782f
|
412 413 |
struct blk_trace *old_bt, *bt = NULL; struct dentry *dir = NULL; |
2056a782f
|
414 |
int ret, i; |
171044d44
|
415 |
if (!buts->buf_size || !buts->buf_nr) |
2056a782f
|
416 |
return -EINVAL; |
0497b345e
|
417 418 |
strncpy(buts->name, name, BLKTRACE_BDEV_SIZE); buts->name[BLKTRACE_BDEV_SIZE - 1] = '\0'; |
2056a782f
|
419 420 421 422 423 |
/* * some device names have larger paths - convert the slashes * to underscores for this to work as expected */ |
171044d44
|
424 425 426 |
for (i = 0; i < strlen(buts->name); i++) if (buts->name[i] == '/') buts->name[i] = '_'; |
2056a782f
|
427 |
|
2056a782f
|
428 429 |
bt = kzalloc(sizeof(*bt), GFP_KERNEL); if (!bt) |
ad5dd5493
|
430 |
return -ENOMEM; |
2056a782f
|
431 |
|
ad5dd5493
|
432 |
ret = -ENOMEM; |
2056a782f
|
433 434 435 |
bt->sequence = alloc_percpu(unsigned long); if (!bt->sequence) goto err; |
313e458f8
|
436 |
bt->msg_data = __alloc_percpu(BLK_TN_MAX_MSG, __alignof__(char)); |
64565911c
|
437 438 |
if (!bt->msg_data) goto err; |
2056a782f
|
439 |
ret = -ENOENT; |
f48fc4d32
|
440 |
|
b5230b56e
|
441 |
mutex_lock(&blk_tree_mutex); |
f48fc4d32
|
442 443 |
if (!blk_tree_root) { blk_tree_root = debugfs_create_dir("block", NULL); |
b5230b56e
|
444 445 |
if (!blk_tree_root) { mutex_unlock(&blk_tree_mutex); |
1a17662ea
|
446 |
goto err; |
b5230b56e
|
447 |
} |
f48fc4d32
|
448 |
} |
b5230b56e
|
449 |
mutex_unlock(&blk_tree_mutex); |
f48fc4d32
|
450 451 |
dir = debugfs_create_dir(buts->name, blk_tree_root); |
2056a782f
|
452 453 454 455 |
if (!dir) goto err; bt->dir = dir; |
6da127ad0
|
456 |
bt->dev = dev; |
2056a782f
|
457 |
atomic_set(&bt->dropped, 0); |
a404d5576
|
458 |
INIT_LIST_HEAD(&bt->running_list); |
2056a782f
|
459 460 |
ret = -EIO; |
939b36697
|
461 462 |
bt->dropped_file = debugfs_create_file("dropped", 0444, dir, bt, &blk_dropped_fops); |
2056a782f
|
463 464 |
if (!bt->dropped_file) goto err; |
02c62304e
|
465 466 467 |
bt->msg_file = debugfs_create_file("msg", 0222, dir, bt, &blk_msg_fops); if (!bt->msg_file) goto err; |
171044d44
|
468 469 |
bt->rchan = relay_open("trace", dir, buts->buf_size, buts->buf_nr, &blk_relay_callbacks, bt); |
2056a782f
|
470 471 |
if (!bt->rchan) goto err; |
2056a782f
|
472 |
|
171044d44
|
473 |
bt->act_mask = buts->act_mask; |
2056a782f
|
474 475 |
if (!bt->act_mask) bt->act_mask = (u16) -1; |
9908c3099
|
476 |
blk_trace_setup_lba(bt, bdev); |
2056a782f
|
477 |
|
d0deef5b1
|
478 479 480 481 482 |
/* overwrite with user settings */ if (buts->start_lba) bt->start_lba = buts->start_lba; if (buts->end_lba) bt->end_lba = buts->end_lba; |
171044d44
|
483 |
bt->pid = buts->pid; |
2056a782f
|
484 485 486 487 488 489 490 491 |
bt->trace_state = Blktrace_setup; ret = -EBUSY; old_bt = xchg(&q->blk_trace, bt); if (old_bt) { (void) xchg(&q->blk_trace, old_bt); goto err; } |
17ba97e34
|
492 |
if (atomic_inc_return(&blk_probes_ref) == 1) |
cbe28296e
|
493 |
blk_register_tracepoints(); |
2056a782f
|
494 495 |
return 0; err: |
ad5dd5493
|
496 |
blk_trace_free(bt); |
2056a782f
|
497 498 |
return ret; } |
171044d44
|
499 |
|
6da127ad0
|
500 |
int blk_trace_setup(struct request_queue *q, char *name, dev_t dev, |
d0deef5b1
|
501 |
struct block_device *bdev, |
6da127ad0
|
502 |
char __user *arg) |
171044d44
|
503 504 505 506 507 508 509 |
{ struct blk_user_trace_setup buts; int ret; ret = copy_from_user(&buts, arg, sizeof(buts)); if (ret) return -EFAULT; |
d0deef5b1
|
510 |
ret = do_blk_trace_setup(q, name, dev, bdev, &buts); |
171044d44
|
511 512 |
if (ret) return ret; |
9a8c28c83
|
513 514 |
if (copy_to_user(arg, &buts, sizeof(buts))) { blk_trace_remove(q); |
171044d44
|
515 |
return -EFAULT; |
9a8c28c83
|
516 |
} |
171044d44
|
517 518 |
return 0; } |
6da127ad0
|
519 |
EXPORT_SYMBOL_GPL(blk_trace_setup); |
2056a782f
|
520 |
|
62c2a7d96
|
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 |
#if defined(CONFIG_COMPAT) && defined(CONFIG_X86_64) static int compat_blk_trace_setup(struct request_queue *q, char *name, dev_t dev, struct block_device *bdev, char __user *arg) { struct blk_user_trace_setup buts; struct compat_blk_user_trace_setup cbuts; int ret; if (copy_from_user(&cbuts, arg, sizeof(cbuts))) return -EFAULT; buts = (struct blk_user_trace_setup) { .act_mask = cbuts.act_mask, .buf_size = cbuts.buf_size, .buf_nr = cbuts.buf_nr, .start_lba = cbuts.start_lba, .end_lba = cbuts.end_lba, .pid = cbuts.pid, }; |
62c2a7d96
|
541 542 543 544 |
ret = do_blk_trace_setup(q, name, dev, bdev, &buts); if (ret) return ret; |
f8c5e9448
|
545 |
if (copy_to_user(arg, &buts.name, ARRAY_SIZE(buts.name))) { |
62c2a7d96
|
546 547 548 549 550 551 552 |
blk_trace_remove(q); return -EFAULT; } return 0; } #endif |
6da127ad0
|
553 |
int blk_trace_startstop(struct request_queue *q, int start) |
2056a782f
|
554 |
{ |
2056a782f
|
555 |
int ret; |
939b36697
|
556 |
struct blk_trace *bt = q->blk_trace; |
2056a782f
|
557 |
|
939b36697
|
558 |
if (bt == NULL) |
2056a782f
|
559 560 561 562 563 564 565 566 567 568 569 570 571 |
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; |
a404d5576
|
572 573 574 |
spin_lock_irq(&running_trace_lock); list_add(&bt->running_list, &running_trace_list); spin_unlock_irq(&running_trace_lock); |
be1c63411
|
575 576 |
trace_note_time(bt); |
2056a782f
|
577 578 579 580 581 |
ret = 0; } } else { if (bt->trace_state == Blktrace_running) { bt->trace_state = Blktrace_stopped; |
a404d5576
|
582 583 584 |
spin_lock_irq(&running_trace_lock); list_del_init(&bt->running_list); spin_unlock_irq(&running_trace_lock); |
2056a782f
|
585 586 587 588 589 590 591 |
relay_flush(bt->rchan); ret = 0; } } return ret; } |
6da127ad0
|
592 |
EXPORT_SYMBOL_GPL(blk_trace_startstop); |
2056a782f
|
593 594 595 596 |
/** * blk_trace_ioctl: - handle the ioctls associated with tracing * @bdev: the block device |
ef18012b2
|
597 |
* @cmd: the ioctl cmd |
2056a782f
|
598 599 600 601 602 |
* @arg: the argument data, if any * **/ int blk_trace_ioctl(struct block_device *bdev, unsigned cmd, char __user *arg) { |
165125e1e
|
603 |
struct request_queue *q; |
2056a782f
|
604 |
int ret, start = 0; |
6da127ad0
|
605 |
char b[BDEVNAME_SIZE]; |
2056a782f
|
606 607 608 609 610 611 612 613 614 |
q = bdev_get_queue(bdev); if (!q) return -ENXIO; mutex_lock(&bdev->bd_mutex); switch (cmd) { case BLKTRACESETUP: |
f36f21ecc
|
615 |
bdevname(bdev, b); |
d0deef5b1
|
616 |
ret = blk_trace_setup(q, b, bdev->bd_dev, bdev, arg); |
2056a782f
|
617 |
break; |
62c2a7d96
|
618 619 620 621 622 623 |
#if defined(CONFIG_COMPAT) && defined(CONFIG_X86_64) case BLKTRACESETUP32: bdevname(bdev, b); ret = compat_blk_trace_setup(q, b, bdev->bd_dev, bdev, arg); break; #endif |
2056a782f
|
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 |
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
|
646 |
void blk_trace_shutdown(struct request_queue *q) |
2056a782f
|
647 |
{ |
6c5c93415
|
648 649 650 651 |
if (q->blk_trace) { blk_trace_startstop(q, 0); blk_trace_remove(q); } |
2056a782f
|
652 |
} |
5f3ea37c7
|
653 654 655 656 657 658 659 660 661 |
/* * blktrace probes */ /** * blk_add_trace_rq - Add a trace for a request oriented action * @q: queue the io is for * @rq: the source request |
af5040da0
|
662 |
* @nr_bytes: number of completed bytes |
5f3ea37c7
|
663 664 665 666 667 668 669 |
* @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, |
af5040da0
|
670 |
unsigned int nr_bytes, u32 what) |
5f3ea37c7
|
671 672 |
{ struct blk_trace *bt = q->blk_trace; |
5f3ea37c7
|
673 674 675 |
if (likely(!bt)) return; |
33659ebba
|
676 |
if (rq->cmd_type == REQ_TYPE_BLOCK_PC) { |
5f3ea37c7
|
677 |
what |= BLK_TC_ACT(BLK_TC_PC); |
af5040da0
|
678 |
__blk_add_trace(bt, 0, nr_bytes, rq->cmd_flags, |
2e46e8b27
|
679 |
what, rq->errors, rq->cmd_len, rq->cmd); |
5f3ea37c7
|
680 681 |
} else { what |= BLK_TC_ACT(BLK_TC_FS); |
af5040da0
|
682 |
__blk_add_trace(bt, blk_rq_pos(rq), nr_bytes, |
805f6b5e1
|
683 |
rq->cmd_flags, what, rq->errors, 0, NULL); |
5f3ea37c7
|
684 685 |
} } |
38516ab59
|
686 687 |
static void blk_add_trace_rq_abort(void *ignore, struct request_queue *q, struct request *rq) |
5f3ea37c7
|
688 |
{ |
af5040da0
|
689 |
blk_add_trace_rq(q, rq, blk_rq_bytes(rq), BLK_TA_ABORT); |
5f3ea37c7
|
690 |
} |
38516ab59
|
691 692 |
static void blk_add_trace_rq_insert(void *ignore, struct request_queue *q, struct request *rq) |
5f3ea37c7
|
693 |
{ |
af5040da0
|
694 |
blk_add_trace_rq(q, rq, blk_rq_bytes(rq), BLK_TA_INSERT); |
5f3ea37c7
|
695 |
} |
38516ab59
|
696 697 |
static void blk_add_trace_rq_issue(void *ignore, struct request_queue *q, struct request *rq) |
5f3ea37c7
|
698 |
{ |
af5040da0
|
699 |
blk_add_trace_rq(q, rq, blk_rq_bytes(rq), BLK_TA_ISSUE); |
5f3ea37c7
|
700 |
} |
38516ab59
|
701 702 |
static void blk_add_trace_rq_requeue(void *ignore, struct request_queue *q, |
939b36697
|
703 |
struct request *rq) |
5f3ea37c7
|
704 |
{ |
af5040da0
|
705 |
blk_add_trace_rq(q, rq, blk_rq_bytes(rq), BLK_TA_REQUEUE); |
5f3ea37c7
|
706 |
} |
38516ab59
|
707 708 |
static void blk_add_trace_rq_complete(void *ignore, struct request_queue *q, |
af5040da0
|
709 710 |
struct request *rq, unsigned int nr_bytes) |
5f3ea37c7
|
711 |
{ |
af5040da0
|
712 |
blk_add_trace_rq(q, rq, nr_bytes, BLK_TA_COMPLETE); |
5f3ea37c7
|
713 714 715 716 717 718 719 |
} /** * 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 |
797a455d2
|
720 |
* @error: error, if any |
5f3ea37c7
|
721 722 723 724 725 726 |
* * 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, |
797a455d2
|
727 |
u32 what, int error) |
5f3ea37c7
|
728 729 730 731 732 |
{ struct blk_trace *bt = q->blk_trace; if (likely(!bt)) return; |
797a455d2
|
733 734 |
if (!error && !bio_flagged(bio, BIO_UPTODATE)) error = EIO; |
4f024f379
|
735 736 |
__blk_add_trace(bt, bio->bi_iter.bi_sector, bio->bi_iter.bi_size, bio->bi_rw, what, error, 0, NULL); |
5f3ea37c7
|
737 |
} |
38516ab59
|
738 739 |
static void blk_add_trace_bio_bounce(void *ignore, struct request_queue *q, struct bio *bio) |
5f3ea37c7
|
740 |
{ |
797a455d2
|
741 |
blk_add_trace_bio(q, bio, BLK_TA_BOUNCE, 0); |
5f3ea37c7
|
742 |
} |
0a82a8d13
|
743 744 745 |
static void blk_add_trace_bio_complete(void *ignore, struct request_queue *q, struct bio *bio, int error) |
5f3ea37c7
|
746 |
{ |
797a455d2
|
747 |
blk_add_trace_bio(q, bio, BLK_TA_COMPLETE, error); |
5f3ea37c7
|
748 |
} |
38516ab59
|
749 750 |
static void blk_add_trace_bio_backmerge(void *ignore, struct request_queue *q, |
8c1cf6bb0
|
751 |
struct request *rq, |
939b36697
|
752 |
struct bio *bio) |
5f3ea37c7
|
753 |
{ |
797a455d2
|
754 |
blk_add_trace_bio(q, bio, BLK_TA_BACKMERGE, 0); |
5f3ea37c7
|
755 |
} |
38516ab59
|
756 757 |
static void blk_add_trace_bio_frontmerge(void *ignore, struct request_queue *q, |
8c1cf6bb0
|
758 |
struct request *rq, |
939b36697
|
759 |
struct bio *bio) |
5f3ea37c7
|
760 |
{ |
797a455d2
|
761 |
blk_add_trace_bio(q, bio, BLK_TA_FRONTMERGE, 0); |
5f3ea37c7
|
762 |
} |
38516ab59
|
763 764 |
static void blk_add_trace_bio_queue(void *ignore, struct request_queue *q, struct bio *bio) |
5f3ea37c7
|
765 |
{ |
797a455d2
|
766 |
blk_add_trace_bio(q, bio, BLK_TA_QUEUE, 0); |
5f3ea37c7
|
767 |
} |
38516ab59
|
768 769 |
static void blk_add_trace_getrq(void *ignore, struct request_queue *q, |
939b36697
|
770 |
struct bio *bio, int rw) |
5f3ea37c7
|
771 772 |
{ if (bio) |
797a455d2
|
773 |
blk_add_trace_bio(q, bio, BLK_TA_GETRQ, 0); |
5f3ea37c7
|
774 775 776 777 778 779 780 |
else { struct blk_trace *bt = q->blk_trace; if (bt) __blk_add_trace(bt, 0, 0, rw, BLK_TA_GETRQ, 0, 0, NULL); } } |
38516ab59
|
781 782 |
static void blk_add_trace_sleeprq(void *ignore, struct request_queue *q, |
939b36697
|
783 |
struct bio *bio, int rw) |
5f3ea37c7
|
784 785 |
{ if (bio) |
797a455d2
|
786 |
blk_add_trace_bio(q, bio, BLK_TA_SLEEPRQ, 0); |
5f3ea37c7
|
787 788 789 790 |
else { struct blk_trace *bt = q->blk_trace; if (bt) |
939b36697
|
791 792 |
__blk_add_trace(bt, 0, 0, rw, BLK_TA_SLEEPRQ, 0, 0, NULL); |
5f3ea37c7
|
793 794 |
} } |
38516ab59
|
795 |
static void blk_add_trace_plug(void *ignore, struct request_queue *q) |
5f3ea37c7
|
796 797 798 799 800 801 |
{ struct blk_trace *bt = q->blk_trace; if (bt) __blk_add_trace(bt, 0, 0, 0, BLK_TA_PLUG, 0, 0, NULL); } |
49cac01e1
|
802 803 |
static void blk_add_trace_unplug(void *ignore, struct request_queue *q, unsigned int depth, bool explicit) |
5f3ea37c7
|
804 805 806 807 |
{ struct blk_trace *bt = q->blk_trace; if (bt) { |
94b5eb28b
|
808 |
__be64 rpdu = cpu_to_be64(depth); |
49cac01e1
|
809 |
u32 what; |
5f3ea37c7
|
810 |
|
49cac01e1
|
811 812 813 814 815 816 |
if (explicit) what = BLK_TA_UNPLUG_IO; else what = BLK_TA_UNPLUG_TIMER; __blk_add_trace(bt, 0, 0, 0, what, 0, sizeof(rpdu), &rpdu); |
5f3ea37c7
|
817 818 |
} } |
38516ab59
|
819 820 |
static void blk_add_trace_split(void *ignore, struct request_queue *q, struct bio *bio, |
5f3ea37c7
|
821 822 823 824 825 826 |
unsigned int pdu) { struct blk_trace *bt = q->blk_trace; if (bt) { __be64 rpdu = cpu_to_be64(pdu); |
4f024f379
|
827 828 829 |
__blk_add_trace(bt, bio->bi_iter.bi_sector, bio->bi_iter.bi_size, bio->bi_rw, BLK_TA_SPLIT, !bio_flagged(bio, BIO_UPTODATE), |
5f3ea37c7
|
830 831 832 833 834 |
sizeof(rpdu), &rpdu); } } /** |
d07335e51
|
835 |
* blk_add_trace_bio_remap - Add a trace for a bio-remap operation |
546cf44a1
|
836 |
* @ignore: trace callback data parameter (not used) |
5f3ea37c7
|
837 838 839 |
* @q: queue the io is for * @bio: the source bio * @dev: target device |
a42aaa3bb
|
840 |
* @from: source sector |
5f3ea37c7
|
841 842 843 844 845 846 |
* * 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. * **/ |
d07335e51
|
847 848 849 |
static void blk_add_trace_bio_remap(void *ignore, struct request_queue *q, struct bio *bio, dev_t dev, sector_t from) |
5f3ea37c7
|
850 851 852 853 854 855 |
{ struct blk_trace *bt = q->blk_trace; struct blk_io_trace_remap r; if (likely(!bt)) return; |
a42aaa3bb
|
856 857 858 |
r.device_from = cpu_to_be32(dev); r.device_to = cpu_to_be32(bio->bi_bdev->bd_dev); r.sector_from = cpu_to_be64(from); |
5f3ea37c7
|
859 |
|
4f024f379
|
860 861 862 |
__blk_add_trace(bt, bio->bi_iter.bi_sector, bio->bi_iter.bi_size, bio->bi_rw, BLK_TA_REMAP, !bio_flagged(bio, BIO_UPTODATE), sizeof(r), &r); |
5f3ea37c7
|
863 864 865 |
} /** |
b0da3f0da
|
866 |
* blk_add_trace_rq_remap - Add a trace for a request-remap operation |
546cf44a1
|
867 |
* @ignore: trace callback data parameter (not used) |
b0da3f0da
|
868 869 870 871 872 873 874 875 876 877 |
* @q: queue the io is for * @rq: the source request * @dev: target device * @from: source sector * * Description: * Device mapper remaps request to other devices. * Add a trace for that action. * **/ |
38516ab59
|
878 879 |
static void blk_add_trace_rq_remap(void *ignore, struct request_queue *q, |
b0da3f0da
|
880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 |
struct request *rq, dev_t dev, sector_t from) { struct blk_trace *bt = q->blk_trace; struct blk_io_trace_remap r; if (likely(!bt)) return; r.device_from = cpu_to_be32(dev); r.device_to = cpu_to_be32(disk_devt(rq->rq_disk)); r.sector_from = cpu_to_be64(from); __blk_add_trace(bt, blk_rq_pos(rq), blk_rq_bytes(rq), rq_data_dir(rq), BLK_TA_REMAP, !!rq->errors, sizeof(r), &r); } /** |
5f3ea37c7
|
899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 |
* 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; |
33659ebba
|
917 |
if (rq->cmd_type == REQ_TYPE_BLOCK_PC) |
2e46e8b27
|
918 919 |
__blk_add_trace(bt, 0, blk_rq_bytes(rq), 0, BLK_TA_DRV_DATA, rq->errors, len, data); |
5f3ea37c7
|
920 |
else |
2e46e8b27
|
921 922 |
__blk_add_trace(bt, blk_rq_pos(rq), blk_rq_bytes(rq), 0, BLK_TA_DRV_DATA, rq->errors, len, data); |
5f3ea37c7
|
923 924 |
} EXPORT_SYMBOL_GPL(blk_add_driver_data); |
3c289ba7c
|
925 |
static void blk_register_tracepoints(void) |
5f3ea37c7
|
926 927 |
{ int ret; |
38516ab59
|
928 |
ret = register_trace_block_rq_abort(blk_add_trace_rq_abort, NULL); |
5f3ea37c7
|
929 |
WARN_ON(ret); |
38516ab59
|
930 |
ret = register_trace_block_rq_insert(blk_add_trace_rq_insert, NULL); |
5f3ea37c7
|
931 |
WARN_ON(ret); |
38516ab59
|
932 |
ret = register_trace_block_rq_issue(blk_add_trace_rq_issue, NULL); |
5f3ea37c7
|
933 |
WARN_ON(ret); |
38516ab59
|
934 |
ret = register_trace_block_rq_requeue(blk_add_trace_rq_requeue, NULL); |
5f3ea37c7
|
935 |
WARN_ON(ret); |
38516ab59
|
936 |
ret = register_trace_block_rq_complete(blk_add_trace_rq_complete, NULL); |
5f3ea37c7
|
937 |
WARN_ON(ret); |
38516ab59
|
938 |
ret = register_trace_block_bio_bounce(blk_add_trace_bio_bounce, NULL); |
5f3ea37c7
|
939 |
WARN_ON(ret); |
38516ab59
|
940 |
ret = register_trace_block_bio_complete(blk_add_trace_bio_complete, NULL); |
5f3ea37c7
|
941 |
WARN_ON(ret); |
38516ab59
|
942 |
ret = register_trace_block_bio_backmerge(blk_add_trace_bio_backmerge, NULL); |
5f3ea37c7
|
943 |
WARN_ON(ret); |
38516ab59
|
944 |
ret = register_trace_block_bio_frontmerge(blk_add_trace_bio_frontmerge, NULL); |
5f3ea37c7
|
945 |
WARN_ON(ret); |
38516ab59
|
946 |
ret = register_trace_block_bio_queue(blk_add_trace_bio_queue, NULL); |
5f3ea37c7
|
947 |
WARN_ON(ret); |
38516ab59
|
948 |
ret = register_trace_block_getrq(blk_add_trace_getrq, NULL); |
5f3ea37c7
|
949 |
WARN_ON(ret); |
38516ab59
|
950 |
ret = register_trace_block_sleeprq(blk_add_trace_sleeprq, NULL); |
5f3ea37c7
|
951 |
WARN_ON(ret); |
38516ab59
|
952 |
ret = register_trace_block_plug(blk_add_trace_plug, NULL); |
5f3ea37c7
|
953 |
WARN_ON(ret); |
49cac01e1
|
954 |
ret = register_trace_block_unplug(blk_add_trace_unplug, NULL); |
5f3ea37c7
|
955 |
WARN_ON(ret); |
38516ab59
|
956 |
ret = register_trace_block_split(blk_add_trace_split, NULL); |
5f3ea37c7
|
957 |
WARN_ON(ret); |
d07335e51
|
958 |
ret = register_trace_block_bio_remap(blk_add_trace_bio_remap, NULL); |
5f3ea37c7
|
959 |
WARN_ON(ret); |
38516ab59
|
960 |
ret = register_trace_block_rq_remap(blk_add_trace_rq_remap, NULL); |
b0da3f0da
|
961 |
WARN_ON(ret); |
5f3ea37c7
|
962 963 964 965 |
} static void blk_unregister_tracepoints(void) { |
38516ab59
|
966 |
unregister_trace_block_rq_remap(blk_add_trace_rq_remap, NULL); |
d07335e51
|
967 |
unregister_trace_block_bio_remap(blk_add_trace_bio_remap, NULL); |
38516ab59
|
968 |
unregister_trace_block_split(blk_add_trace_split, NULL); |
49cac01e1
|
969 |
unregister_trace_block_unplug(blk_add_trace_unplug, NULL); |
38516ab59
|
970 971 972 973 974 975 976 977 978 979 980 981 982 |
unregister_trace_block_plug(blk_add_trace_plug, NULL); unregister_trace_block_sleeprq(blk_add_trace_sleeprq, NULL); unregister_trace_block_getrq(blk_add_trace_getrq, NULL); unregister_trace_block_bio_queue(blk_add_trace_bio_queue, NULL); unregister_trace_block_bio_frontmerge(blk_add_trace_bio_frontmerge, NULL); unregister_trace_block_bio_backmerge(blk_add_trace_bio_backmerge, NULL); unregister_trace_block_bio_complete(blk_add_trace_bio_complete, NULL); unregister_trace_block_bio_bounce(blk_add_trace_bio_bounce, NULL); unregister_trace_block_rq_complete(blk_add_trace_rq_complete, NULL); unregister_trace_block_rq_requeue(blk_add_trace_rq_requeue, NULL); unregister_trace_block_rq_issue(blk_add_trace_rq_issue, NULL); unregister_trace_block_rq_insert(blk_add_trace_rq_insert, NULL); unregister_trace_block_rq_abort(blk_add_trace_rq_abort, NULL); |
5f3ea37c7
|
983 984 985 |
tracepoint_synchronize_unregister(); } |
c71a89615
|
986 987 988 989 990 991 992 |
/* * struct blk_io_tracer formatting routines */ static void fill_rwbs(char *rwbs, const struct blk_io_trace *t) { |
157f9c00e
|
993 |
int i = 0; |
65796348e
|
994 |
int tc = t->action >> BLK_TC_SHIFT; |
157f9c00e
|
995 |
|
18cea4591
|
996 997 998 999 |
if (t->action == BLK_TN_MESSAGE) { rwbs[i++] = 'N'; goto out; } |
c09c47cae
|
1000 1001 |
if (tc & BLK_TC_FLUSH) rwbs[i++] = 'F'; |
65796348e
|
1002 |
if (tc & BLK_TC_DISCARD) |
157f9c00e
|
1003 |
rwbs[i++] = 'D'; |
65796348e
|
1004 |
else if (tc & BLK_TC_WRITE) |
157f9c00e
|
1005 1006 1007 1008 1009 |
rwbs[i++] = 'W'; else if (t->bytes) rwbs[i++] = 'R'; else rwbs[i++] = 'N'; |
c09c47cae
|
1010 1011 |
if (tc & BLK_TC_FUA) rwbs[i++] = 'F'; |
65796348e
|
1012 |
if (tc & BLK_TC_AHEAD) |
157f9c00e
|
1013 |
rwbs[i++] = 'A'; |
65796348e
|
1014 |
if (tc & BLK_TC_SYNC) |
157f9c00e
|
1015 |
rwbs[i++] = 'S'; |
65796348e
|
1016 |
if (tc & BLK_TC_META) |
157f9c00e
|
1017 |
rwbs[i++] = 'M'; |
18cea4591
|
1018 |
out: |
157f9c00e
|
1019 |
rwbs[i] = '\0'; |
c71a89615
|
1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 |
} static inline const struct blk_io_trace *te_blk_io_trace(const struct trace_entry *ent) { return (const struct blk_io_trace *)ent; } static inline const void *pdu_start(const struct trace_entry *ent) { return te_blk_io_trace(ent) + 1; } |
66de7792c
|
1032 1033 1034 1035 1036 1037 1038 1039 1040 |
static inline u32 t_action(const struct trace_entry *ent) { return te_blk_io_trace(ent)->action; } static inline u32 t_bytes(const struct trace_entry *ent) { return te_blk_io_trace(ent)->bytes; } |
c71a89615
|
1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 |
static inline u32 t_sec(const struct trace_entry *ent) { return te_blk_io_trace(ent)->bytes >> 9; } static inline unsigned long long t_sector(const struct trace_entry *ent) { return te_blk_io_trace(ent)->sector; } static inline __u16 t_error(const struct trace_entry *ent) { |
e0dc81bec
|
1053 |
return te_blk_io_trace(ent)->error; |
c71a89615
|
1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 |
} static __u64 get_pdu_int(const struct trace_entry *ent) { const __u64 *val = pdu_start(ent); return be64_to_cpu(*val); } static void get_pdu_remap(const struct trace_entry *ent, struct blk_io_trace_remap *r) { const struct blk_io_trace_remap *__r = pdu_start(ent); |
a42aaa3bb
|
1066 |
__u64 sector_from = __r->sector_from; |
c71a89615
|
1067 |
|
c71a89615
|
1068 |
r->device_from = be32_to_cpu(__r->device_from); |
a42aaa3bb
|
1069 1070 |
r->device_to = be32_to_cpu(__r->device_to); r->sector_from = be64_to_cpu(sector_from); |
c71a89615
|
1071 |
} |
f4a1d08ce
|
1072 |
typedef void (blk_log_action_t) (struct trace_iterator *iter, const char *act); |
b6a4b0c3a
|
1073 |
|
f4a1d08ce
|
1074 |
static void blk_log_action_classic(struct trace_iterator *iter, const char *act) |
c71a89615
|
1075 |
{ |
c09c47cae
|
1076 |
char rwbs[RWBS_LEN]; |
35ac51bfe
|
1077 1078 |
unsigned long long ts = iter->ts; unsigned long nsec_rem = do_div(ts, NSEC_PER_SEC); |
c71a89615
|
1079 |
unsigned secs = (unsigned long)ts; |
b6a4b0c3a
|
1080 |
const struct blk_io_trace *t = te_blk_io_trace(iter->ent); |
c71a89615
|
1081 1082 |
fill_rwbs(rwbs, t); |
f4a1d08ce
|
1083 1084 1085 1086 |
trace_seq_printf(&iter->seq, "%3d,%-3d %2d %5d.%09lu %5u %2s %3s ", MAJOR(t->device), MINOR(t->device), iter->cpu, secs, nsec_rem, iter->ent->pid, act, rwbs); |
c71a89615
|
1087 |
} |
f4a1d08ce
|
1088 |
static void blk_log_action(struct trace_iterator *iter, const char *act) |
c71a89615
|
1089 |
{ |
c09c47cae
|
1090 |
char rwbs[RWBS_LEN]; |
b6a4b0c3a
|
1091 |
const struct blk_io_trace *t = te_blk_io_trace(iter->ent); |
c71a89615
|
1092 |
fill_rwbs(rwbs, t); |
f4a1d08ce
|
1093 1094 |
trace_seq_printf(&iter->seq, "%3d,%-3d %2s %3s ", MAJOR(t->device), MINOR(t->device), act, rwbs); |
c71a89615
|
1095 |
} |
f4a1d08ce
|
1096 |
static void blk_log_dump_pdu(struct trace_seq *s, const struct trace_entry *ent) |
66de7792c
|
1097 |
{ |
049862579
|
1098 |
const unsigned char *pdu_buf; |
66de7792c
|
1099 |
int pdu_len; |
f4a1d08ce
|
1100 |
int i, end; |
66de7792c
|
1101 1102 1103 1104 1105 |
pdu_buf = pdu_start(ent); pdu_len = te_blk_io_trace(ent)->pdu_len; if (!pdu_len) |
f4a1d08ce
|
1106 |
return; |
66de7792c
|
1107 1108 1109 1110 1111 1112 |
/* find the last zero that needs to be printed */ for (end = pdu_len - 1; end >= 0; end--) if (pdu_buf[end]) break; end++; |
f4a1d08ce
|
1113 |
trace_seq_putc(s, '('); |
66de7792c
|
1114 1115 |
for (i = 0; i < pdu_len; i++) { |
f4a1d08ce
|
1116 1117 |
trace_seq_printf(s, "%s%02x", i == 0 ? "" : " ", pdu_buf[i]); |
66de7792c
|
1118 1119 1120 1121 1122 |
/* * stop when the rest is just zeroes and indicate so * with a ".." appended */ |
f4a1d08ce
|
1123 1124 1125 1126 |
if (i == end && end != pdu_len - 1) { trace_seq_puts(s, " ..) "); return; } |
66de7792c
|
1127 |
} |
f4a1d08ce
|
1128 |
trace_seq_puts(s, ") "); |
66de7792c
|
1129 |
} |
f4a1d08ce
|
1130 |
static void blk_log_generic(struct trace_seq *s, const struct trace_entry *ent) |
c71a89615
|
1131 |
{ |
4ca530852
|
1132 1133 1134 |
char cmd[TASK_COMM_LEN]; trace_find_cmdline(ent->pid, cmd); |
c71a89615
|
1135 |
|
66de7792c
|
1136 |
if (t_action(ent) & BLK_TC_ACT(BLK_TC_PC)) { |
f4a1d08ce
|
1137 1138 1139 1140 |
trace_seq_printf(s, "%u ", t_bytes(ent)); blk_log_dump_pdu(s, ent); trace_seq_printf(s, "[%s] ", cmd); |
66de7792c
|
1141 1142 |
} else { if (t_sec(ent)) |
f4a1d08ce
|
1143 1144 |
trace_seq_printf(s, "%llu + %u [%s] ", |
66de7792c
|
1145 |
t_sector(ent), t_sec(ent), cmd); |
f4a1d08ce
|
1146 1147 1148 |
else trace_seq_printf(s, "[%s] ", cmd); |
66de7792c
|
1149 |
} |
c71a89615
|
1150 |
} |
f4a1d08ce
|
1151 |
static void blk_log_with_error(struct trace_seq *s, |
157f9c00e
|
1152 |
const struct trace_entry *ent) |
c71a89615
|
1153 |
{ |
66de7792c
|
1154 |
if (t_action(ent) & BLK_TC_ACT(BLK_TC_PC)) { |
f4a1d08ce
|
1155 1156 1157 |
blk_log_dump_pdu(s, ent); trace_seq_printf(s, "[%d] ", t_error(ent)); |
66de7792c
|
1158 1159 |
} else { if (t_sec(ent)) |
f4a1d08ce
|
1160 1161 1162 1163 1164 1165 1166 1167 |
trace_seq_printf(s, "%llu + %u [%d] ", t_sector(ent), t_sec(ent), t_error(ent)); else trace_seq_printf(s, "%llu [%d] ", t_sector(ent), t_error(ent)); |
66de7792c
|
1168 |
} |
c71a89615
|
1169 |
} |
f4a1d08ce
|
1170 |
static void blk_log_remap(struct trace_seq *s, const struct trace_entry *ent) |
c71a89615
|
1171 |
{ |
a42aaa3bb
|
1172 |
struct blk_io_trace_remap r = { .device_from = 0, }; |
c71a89615
|
1173 1174 |
get_pdu_remap(ent, &r); |
f4a1d08ce
|
1175 1176 1177 1178 1179 |
trace_seq_printf(s, "%llu + %u <- (%d,%d) %llu ", t_sector(ent), t_sec(ent), MAJOR(r.device_from), MINOR(r.device_from), (unsigned long long)r.sector_from); |
c71a89615
|
1180 |
} |
f4a1d08ce
|
1181 |
static void blk_log_plug(struct trace_seq *s, const struct trace_entry *ent) |
c71a89615
|
1182 |
{ |
4ca530852
|
1183 1184 1185 |
char cmd[TASK_COMM_LEN]; trace_find_cmdline(ent->pid, cmd); |
f4a1d08ce
|
1186 1187 |
trace_seq_printf(s, "[%s] ", cmd); |
c71a89615
|
1188 |
} |
f4a1d08ce
|
1189 |
static void blk_log_unplug(struct trace_seq *s, const struct trace_entry *ent) |
c71a89615
|
1190 |
{ |
4ca530852
|
1191 1192 1193 |
char cmd[TASK_COMM_LEN]; trace_find_cmdline(ent->pid, cmd); |
f4a1d08ce
|
1194 1195 |
trace_seq_printf(s, "[%s] %llu ", cmd, get_pdu_int(ent)); |
c71a89615
|
1196 |
} |
f4a1d08ce
|
1197 |
static void blk_log_split(struct trace_seq *s, const struct trace_entry *ent) |
c71a89615
|
1198 |
{ |
4ca530852
|
1199 1200 1201 |
char cmd[TASK_COMM_LEN]; trace_find_cmdline(ent->pid, cmd); |
f4a1d08ce
|
1202 1203 1204 |
trace_seq_printf(s, "%llu / %llu [%s] ", t_sector(ent), get_pdu_int(ent), cmd); |
c71a89615
|
1205 |
} |
f4a1d08ce
|
1206 |
static void blk_log_msg(struct trace_seq *s, const struct trace_entry *ent) |
18cea4591
|
1207 |
{ |
18cea4591
|
1208 |
const struct blk_io_trace *t = te_blk_io_trace(ent); |
f4a1d08ce
|
1209 1210 1211 |
trace_seq_putmem(s, t + 1, t->pdu_len); trace_seq_putc(s, ' '); |
18cea4591
|
1212 |
} |
c71a89615
|
1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 |
/* * struct tracer operations */ static void blk_tracer_print_header(struct seq_file *m) { if (!(blk_tracer_flags.val & TRACE_BLK_OPT_CLASSIC)) return; seq_puts(m, "# DEV CPU TIMESTAMP PID ACT FLG " "# | | | | | | "); } static void blk_tracer_start(struct trace_array *tr) { |
ad5dd5493
|
1229 |
blk_tracer_enabled = true; |
c71a89615
|
1230 1231 1232 1233 1234 1235 |
} static int blk_tracer_init(struct trace_array *tr) { blk_tr = tr; blk_tracer_start(tr); |
c71a89615
|
1236 1237 1238 1239 1240 |
return 0; } static void blk_tracer_stop(struct trace_array *tr) { |
ad5dd5493
|
1241 |
blk_tracer_enabled = false; |
c71a89615
|
1242 1243 1244 1245 |
} static void blk_tracer_reset(struct trace_array *tr) { |
c71a89615
|
1246 1247 |
blk_tracer_stop(tr); } |
e4955c998
|
1248 |
static const struct { |
c71a89615
|
1249 |
const char *act[2]; |
f4a1d08ce
|
1250 |
void (*print)(struct trace_seq *s, const struct trace_entry *ent); |
e4955c998
|
1251 |
} what2act[] = { |
ef18012b2
|
1252 |
[__BLK_TA_QUEUE] = {{ "Q", "queue" }, blk_log_generic }, |
c71a89615
|
1253 1254 1255 1256 1257 1258 1259 1260 1261 |
[__BLK_TA_BACKMERGE] = {{ "M", "backmerge" }, blk_log_generic }, [__BLK_TA_FRONTMERGE] = {{ "F", "frontmerge" }, blk_log_generic }, [__BLK_TA_GETRQ] = {{ "G", "getrq" }, blk_log_generic }, [__BLK_TA_SLEEPRQ] = {{ "S", "sleeprq" }, blk_log_generic }, [__BLK_TA_REQUEUE] = {{ "R", "requeue" }, blk_log_with_error }, [__BLK_TA_ISSUE] = {{ "D", "issue" }, blk_log_generic }, [__BLK_TA_COMPLETE] = {{ "C", "complete" }, blk_log_with_error }, [__BLK_TA_PLUG] = {{ "P", "plug" }, blk_log_plug }, [__BLK_TA_UNPLUG_IO] = {{ "U", "unplug_io" }, blk_log_unplug }, |
49cac01e1
|
1262 |
[__BLK_TA_UNPLUG_TIMER] = {{ "UT", "unplug_timer" }, blk_log_unplug }, |
c71a89615
|
1263 1264 1265 1266 1267 |
[__BLK_TA_INSERT] = {{ "I", "insert" }, blk_log_generic }, [__BLK_TA_SPLIT] = {{ "X", "split" }, blk_log_split }, [__BLK_TA_BOUNCE] = {{ "B", "bounce" }, blk_log_generic }, [__BLK_TA_REMAP] = {{ "A", "remap" }, blk_log_remap }, }; |
b6a4b0c3a
|
1268 1269 |
static enum print_line_t print_one_line(struct trace_iterator *iter, bool classic) |
c71a89615
|
1270 |
{ |
2c9b238eb
|
1271 |
struct trace_seq *s = &iter->seq; |
b6a4b0c3a
|
1272 1273 |
const struct blk_io_trace *t; u16 what; |
b6a4b0c3a
|
1274 1275 |
bool long_act; blk_log_action_t *log_action; |
c71a89615
|
1276 |
|
b6a4b0c3a
|
1277 1278 1279 1280 |
t = te_blk_io_trace(iter->ent); what = t->action & ((1 << BLK_TC_SHIFT) - 1); long_act = !!(trace_flags & TRACE_ITER_VERBOSE); log_action = classic ? &blk_log_action_classic : &blk_log_action; |
08a06b83f
|
1281 |
|
18cea4591
|
1282 |
if (t->action == BLK_TN_MESSAGE) { |
f4a1d08ce
|
1283 1284 |
log_action(iter, long_act ? "message" : "m"); blk_log_msg(s, iter->ent); |
18cea4591
|
1285 |
} |
eb08f8eb0
|
1286 |
if (unlikely(what == 0 || what >= ARRAY_SIZE(what2act))) |
f4a1d08ce
|
1287 1288 |
trace_seq_printf(s, "Unknown action %x ", what); |
c71a89615
|
1289 |
else { |
f4a1d08ce
|
1290 1291 |
log_action(iter, what2act[what].act[long_act]); what2act[what].print(s, iter->ent); |
c71a89615
|
1292 |
} |
f4a1d08ce
|
1293 1294 |
return trace_handle_return(s); |
c71a89615
|
1295 |
} |
b6a4b0c3a
|
1296 |
static enum print_line_t blk_trace_event_print(struct trace_iterator *iter, |
a9a577638
|
1297 |
int flags, struct trace_event *event) |
b6a4b0c3a
|
1298 |
{ |
b6a4b0c3a
|
1299 1300 |
return print_one_line(iter, false); } |
f4a1d08ce
|
1301 |
static void blk_trace_synthesize_old_trace(struct trace_iterator *iter) |
08a06b83f
|
1302 1303 1304 1305 1306 1307 |
{ struct trace_seq *s = &iter->seq; struct blk_io_trace *t = (struct blk_io_trace *)iter->ent; const int offset = offsetof(struct blk_io_trace, sector); struct blk_io_trace old = { .magic = BLK_IO_TRACE_MAGIC | BLK_IO_TRACE_VERSION, |
6c051ce03
|
1308 |
.time = iter->ts, |
08a06b83f
|
1309 |
}; |
f4a1d08ce
|
1310 1311 1312 |
trace_seq_putmem(s, &old, offset); trace_seq_putmem(s, &t->sector, sizeof(old) - offset + t->pdu_len); |
08a06b83f
|
1313 |
} |
ae7462b4f
|
1314 |
static enum print_line_t |
a9a577638
|
1315 1316 |
blk_trace_event_print_binary(struct trace_iterator *iter, int flags, struct trace_event *event) |
08a06b83f
|
1317 |
{ |
f4a1d08ce
|
1318 1319 1320 |
blk_trace_synthesize_old_trace(iter); return trace_handle_return(&iter->seq); |
08a06b83f
|
1321 |
} |
c71a89615
|
1322 1323 |
static enum print_line_t blk_tracer_print_line(struct trace_iterator *iter) { |
c71a89615
|
1324 1325 |
if (!(blk_tracer_flags.val & TRACE_BLK_OPT_CLASSIC)) return TRACE_TYPE_UNHANDLED; |
b6a4b0c3a
|
1326 |
return print_one_line(iter, true); |
c71a89615
|
1327 |
} |
8c1a49aed
|
1328 1329 |
static int blk_tracer_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set) |
f3948f885
|
1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 |
{ /* don't output context-info for blk_classic output */ if (bit == TRACE_BLK_OPT_CLASSIC) { if (set) trace_flags &= ~TRACE_ITER_CONTEXT_INFO; else trace_flags |= TRACE_ITER_CONTEXT_INFO; } return 0; } |
c71a89615
|
1340 1341 1342 1343 1344 1345 1346 1347 1348 |
static struct tracer blk_tracer __read_mostly = { .name = "blk", .init = blk_tracer_init, .reset = blk_tracer_reset, .start = blk_tracer_start, .stop = blk_tracer_stop, .print_header = blk_tracer_print_header, .print_line = blk_tracer_print_line, .flags = &blk_tracer_flags, |
f3948f885
|
1349 |
.set_flag = blk_tracer_set_flag, |
c71a89615
|
1350 |
}; |
a9a577638
|
1351 |
static struct trace_event_functions trace_blk_event_funcs = { |
c71a89615
|
1352 |
.trace = blk_trace_event_print, |
08a06b83f
|
1353 |
.binary = blk_trace_event_print_binary, |
c71a89615
|
1354 |
}; |
a9a577638
|
1355 1356 1357 1358 |
static struct trace_event trace_blk_event = { .type = TRACE_BLK, .funcs = &trace_blk_event_funcs, }; |
c71a89615
|
1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 |
static int __init init_blk_tracer(void) { if (!register_ftrace_event(&trace_blk_event)) { pr_warning("Warning: could not register block events "); return 1; } if (register_tracer(&blk_tracer) != 0) { pr_warning("Warning: could not register the block tracer "); unregister_ftrace_event(&trace_blk_event); return 1; } return 0; } device_initcall(init_blk_tracer); static int blk_trace_remove_queue(struct request_queue *q) { struct blk_trace *bt; bt = xchg(&q->blk_trace, NULL); if (bt == NULL) return -EINVAL; |
17ba97e34
|
1386 1387 |
if (atomic_dec_and_test(&blk_probes_ref)) blk_unregister_tracepoints(); |
ad5dd5493
|
1388 |
blk_trace_free(bt); |
c71a89615
|
1389 1390 1391 1392 1393 1394 |
return 0; } /* * Setup everything required to start tracing */ |
9908c3099
|
1395 1396 |
static int blk_trace_setup_queue(struct request_queue *q, struct block_device *bdev) |
c71a89615
|
1397 1398 |
{ struct blk_trace *old_bt, *bt = NULL; |
18cea4591
|
1399 |
int ret = -ENOMEM; |
c71a89615
|
1400 |
|
c71a89615
|
1401 1402 |
bt = kzalloc(sizeof(*bt), GFP_KERNEL); if (!bt) |
15152e448
|
1403 |
return -ENOMEM; |
c71a89615
|
1404 |
|
18cea4591
|
1405 1406 1407 |
bt->msg_data = __alloc_percpu(BLK_TN_MAX_MSG, __alignof__(char)); if (!bt->msg_data) goto free_bt; |
9908c3099
|
1408 |
bt->dev = bdev->bd_dev; |
c71a89615
|
1409 |
bt->act_mask = (u16)-1; |
9908c3099
|
1410 1411 |
blk_trace_setup_lba(bt, bdev); |
c71a89615
|
1412 1413 1414 1415 |
old_bt = xchg(&q->blk_trace, bt); if (old_bt != NULL) { (void)xchg(&q->blk_trace, old_bt); |
18cea4591
|
1416 1417 |
ret = -EBUSY; goto free_bt; |
c71a89615
|
1418 |
} |
15152e448
|
1419 |
|
17ba97e34
|
1420 1421 |
if (atomic_inc_return(&blk_probes_ref) == 1) blk_register_tracepoints(); |
c71a89615
|
1422 |
return 0; |
18cea4591
|
1423 1424 1425 1426 |
free_bt: blk_trace_free(bt); return ret; |
c71a89615
|
1427 1428 1429 1430 1431 |
} /* * sysfs interface to enable and configure tracing */ |
c71a89615
|
1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 |
static ssize_t sysfs_blk_trace_attr_show(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t sysfs_blk_trace_attr_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); #define BLK_TRACE_DEVICE_ATTR(_name) \ DEVICE_ATTR(_name, S_IRUGO | S_IWUSR, \ sysfs_blk_trace_attr_show, \ sysfs_blk_trace_attr_store) |
cd649b8bb
|
1442 |
static BLK_TRACE_DEVICE_ATTR(enable); |
c71a89615
|
1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 |
static BLK_TRACE_DEVICE_ATTR(act_mask); static BLK_TRACE_DEVICE_ATTR(pid); static BLK_TRACE_DEVICE_ATTR(start_lba); static BLK_TRACE_DEVICE_ATTR(end_lba); static struct attribute *blk_trace_attrs[] = { &dev_attr_enable.attr, &dev_attr_act_mask.attr, &dev_attr_pid.attr, &dev_attr_start_lba.attr, &dev_attr_end_lba.attr, NULL }; struct attribute_group blk_trace_attr_group = { .name = "trace", .attrs = blk_trace_attrs, }; |
093419971
|
1461 1462 1463 1464 1465 1466 |
static const struct { int mask; const char *str; } mask_maps[] = { { BLK_TC_READ, "read" }, { BLK_TC_WRITE, "write" }, |
c09c47cae
|
1467 |
{ BLK_TC_FLUSH, "flush" }, |
093419971
|
1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 |
{ BLK_TC_SYNC, "sync" }, { BLK_TC_QUEUE, "queue" }, { BLK_TC_REQUEUE, "requeue" }, { BLK_TC_ISSUE, "issue" }, { BLK_TC_COMPLETE, "complete" }, { BLK_TC_FS, "fs" }, { BLK_TC_PC, "pc" }, { BLK_TC_AHEAD, "ahead" }, { BLK_TC_META, "meta" }, { BLK_TC_DISCARD, "discard" }, { BLK_TC_DRV_DATA, "drv_data" }, |
c09c47cae
|
1479 |
{ BLK_TC_FUA, "fua" }, |
093419971
|
1480 1481 1482 |
}; static int blk_trace_str2mask(const char *str) |
c71a89615
|
1483 |
{ |
093419971
|
1484 |
int i; |
c71a89615
|
1485 |
int mask = 0; |
9eb85125c
|
1486 |
char *buf, *s, *token; |
c71a89615
|
1487 |
|
9eb85125c
|
1488 1489 |
buf = kstrdup(str, GFP_KERNEL); if (buf == NULL) |
c71a89615
|
1490 |
return -ENOMEM; |
9eb85125c
|
1491 |
s = strstrip(buf); |
c71a89615
|
1492 1493 |
while (1) { |
093419971
|
1494 1495 |
token = strsep(&s, ","); if (token == NULL) |
c71a89615
|
1496 |
break; |
093419971
|
1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 |
if (*token == '\0') continue; for (i = 0; i < ARRAY_SIZE(mask_maps); i++) { if (strcasecmp(token, mask_maps[i].str) == 0) { mask |= mask_maps[i].mask; break; } } if (i == ARRAY_SIZE(mask_maps)) { mask = -EINVAL; break; } |
c71a89615
|
1510 |
} |
9eb85125c
|
1511 |
kfree(buf); |
c71a89615
|
1512 1513 1514 |
return mask; } |
093419971
|
1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 |
static ssize_t blk_trace_mask2str(char *buf, int mask) { int i; char *p = buf; for (i = 0; i < ARRAY_SIZE(mask_maps); i++) { if (mask & mask_maps[i].mask) { p += sprintf(p, "%s%s", (p == buf) ? "" : ",", mask_maps[i].str); } } *p++ = ' '; return p - buf; } |
b125130b2
|
1531 1532 1533 1534 1535 1536 1537 |
static struct request_queue *blk_trace_get_queue(struct block_device *bdev) { if (bdev->bd_disk == NULL) return NULL; return bdev_get_queue(bdev); } |
c71a89615
|
1538 1539 1540 1541 1542 1543 1544 1545 |
static ssize_t sysfs_blk_trace_attr_show(struct device *dev, struct device_attribute *attr, char *buf) { struct hd_struct *p = dev_to_part(dev); struct request_queue *q; struct block_device *bdev; ssize_t ret = -ENXIO; |
c71a89615
|
1546 1547 |
bdev = bdget(part_devt(p)); if (bdev == NULL) |
01b284f9b
|
1548 |
goto out; |
c71a89615
|
1549 |
|
b125130b2
|
1550 |
q = blk_trace_get_queue(bdev); |
c71a89615
|
1551 1552 |
if (q == NULL) goto out_bdput; |
b125130b2
|
1553 |
|
c71a89615
|
1554 |
mutex_lock(&bdev->bd_mutex); |
cd649b8bb
|
1555 1556 1557 1558 1559 1560 |
if (attr == &dev_attr_enable) { ret = sprintf(buf, "%u ", !!q->blk_trace); goto out_unlock_bdev; } |
c71a89615
|
1561 1562 1563 1564 |
if (q->blk_trace == NULL) ret = sprintf(buf, "disabled "); else if (attr == &dev_attr_act_mask) |
093419971
|
1565 |
ret = blk_trace_mask2str(buf, q->blk_trace->act_mask); |
c71a89615
|
1566 1567 1568 1569 1570 1571 1572 1573 1574 |
else if (attr == &dev_attr_pid) ret = sprintf(buf, "%u ", q->blk_trace->pid); else if (attr == &dev_attr_start_lba) ret = sprintf(buf, "%llu ", q->blk_trace->start_lba); else if (attr == &dev_attr_end_lba) ret = sprintf(buf, "%llu ", q->blk_trace->end_lba); |
cd649b8bb
|
1575 1576 |
out_unlock_bdev: |
c71a89615
|
1577 1578 1579 |
mutex_unlock(&bdev->bd_mutex); out_bdput: bdput(bdev); |
01b284f9b
|
1580 |
out: |
c71a89615
|
1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 |
return ret; } static ssize_t sysfs_blk_trace_attr_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct block_device *bdev; struct request_queue *q; struct hd_struct *p; u64 value; |
093419971
|
1592 |
ssize_t ret = -EINVAL; |
c71a89615
|
1593 1594 1595 1596 1597 1598 1599 |
if (count == 0) goto out; if (attr == &dev_attr_act_mask) { if (sscanf(buf, "%llx", &value) != 1) { /* Assume it is a list of trace category names */ |
093419971
|
1600 1601 |
ret = blk_trace_str2mask(buf); if (ret < 0) |
c71a89615
|
1602 |
goto out; |
093419971
|
1603 |
value = ret; |
c71a89615
|
1604 1605 1606 |
} } else if (sscanf(buf, "%llu", &value) != 1) goto out; |
093419971
|
1607 |
ret = -ENXIO; |
c71a89615
|
1608 1609 1610 |
p = dev_to_part(dev); bdev = bdget(part_devt(p)); if (bdev == NULL) |
01b284f9b
|
1611 |
goto out; |
c71a89615
|
1612 |
|
b125130b2
|
1613 |
q = blk_trace_get_queue(bdev); |
c71a89615
|
1614 1615 1616 1617 |
if (q == NULL) goto out_bdput; mutex_lock(&bdev->bd_mutex); |
cd649b8bb
|
1618 1619 1620 |
if (attr == &dev_attr_enable) { if (value) |
9908c3099
|
1621 |
ret = blk_trace_setup_queue(q, bdev); |
cd649b8bb
|
1622 1623 1624 1625 |
else ret = blk_trace_remove_queue(q); goto out_unlock_bdev; } |
c71a89615
|
1626 1627 |
ret = 0; if (q->blk_trace == NULL) |
9908c3099
|
1628 |
ret = blk_trace_setup_queue(q, bdev); |
c71a89615
|
1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 |
if (ret == 0) { if (attr == &dev_attr_act_mask) q->blk_trace->act_mask = value; else if (attr == &dev_attr_pid) q->blk_trace->pid = value; else if (attr == &dev_attr_start_lba) q->blk_trace->start_lba = value; else if (attr == &dev_attr_end_lba) q->blk_trace->end_lba = value; |
c71a89615
|
1639 |
} |
cd649b8bb
|
1640 1641 |
out_unlock_bdev: |
c71a89615
|
1642 1643 1644 |
mutex_unlock(&bdev->bd_mutex); out_bdput: bdput(bdev); |
c71a89615
|
1645 |
out: |
cd649b8bb
|
1646 |
return ret ? ret : count; |
c71a89615
|
1647 |
} |
cd649b8bb
|
1648 |
|
1d54ad6da
|
1649 1650 1651 1652 |
int blk_trace_init_sysfs(struct device *dev) { return sysfs_create_group(&dev->kobj, &blk_trace_attr_group); } |
48c0d4d4c
|
1653 1654 1655 1656 |
void blk_trace_remove_sysfs(struct device *dev) { sysfs_remove_group(&dev->kobj, &blk_trace_attr_group); } |
55782138e
|
1657 1658 1659 1660 1661 1662 1663 1664 1665 |
#endif /* CONFIG_BLK_DEV_IO_TRACE */ #ifdef CONFIG_EVENT_TRACING void blk_dump_cmd(char *buf, struct request *rq) { int i, end; int len = rq->cmd_len; unsigned char *cmd = rq->cmd; |
33659ebba
|
1666 |
if (rq->cmd_type != REQ_TYPE_BLOCK_PC) { |
55782138e
|
1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 |
buf[0] = '\0'; return; } for (end = len - 1; end >= 0; end--) if (cmd[end]) break; end++; for (i = 0; i < len; i++) { buf += sprintf(buf, "%s%02x", i == 0 ? "" : " ", cmd[i]); if (i == end && end != len - 1) { sprintf(buf, " .."); break; } } } void blk_fill_rwbs(char *rwbs, u32 rw, int bytes) { int i = 0; |
c09c47cae
|
1688 1689 |
if (rw & REQ_FLUSH) rwbs[i++] = 'F'; |
55782138e
|
1690 1691 |
if (rw & WRITE) rwbs[i++] = 'W'; |
7b6d91dae
|
1692 |
else if (rw & REQ_DISCARD) |
55782138e
|
1693 1694 1695 1696 1697 |
rwbs[i++] = 'D'; else if (bytes) rwbs[i++] = 'R'; else rwbs[i++] = 'N'; |
c09c47cae
|
1698 1699 |
if (rw & REQ_FUA) rwbs[i++] = 'F'; |
7b6d91dae
|
1700 |
if (rw & REQ_RAHEAD) |
55782138e
|
1701 |
rwbs[i++] = 'A'; |
7b6d91dae
|
1702 |
if (rw & REQ_SYNC) |
55782138e
|
1703 |
rwbs[i++] = 'S'; |
7b6d91dae
|
1704 |
if (rw & REQ_META) |
55782138e
|
1705 |
rwbs[i++] = 'M'; |
8d57a98cc
|
1706 1707 |
if (rw & REQ_SECURE) rwbs[i++] = 'E'; |
55782138e
|
1708 1709 1710 |
rwbs[i] = '\0'; } |
9ca8f8e51
|
1711 |
EXPORT_SYMBOL_GPL(blk_fill_rwbs); |
55782138e
|
1712 |
|
55782138e
|
1713 |
#endif /* CONFIG_EVENT_TRACING */ |