LTTng modularization, import of lttng 0.226
[lttng-modules.git] / probes / ext4-trace.c
1 /*
2 * ltt/probes/ext4-trace.c
3 *
4 * ext4 tracepoint probes.
5 *
6 * (C) Copyright 2009 - Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
7 * Dual LGPL v2.1/GPL v2 license.
8 */
9
10 #include <linux/module.h>
11 #include <linux/writeback.h>
12 #include <linux/debugfs.h>
13 #include <linux/mutex.h>
14 #include <linux/rcupdate.h>
15 #include <trace/events/ext4.h>
16
17 #include "../ltt-tracer.h"
18 #include "../../fs/ext4/mballoc.h"
19
20 static struct dentry *ext4_filter_dentry, *ext4_filter_dev_dentry,
21 *ext4_filter_inode_dentry;
22 static DEFINE_MUTEX(ext4_filter_mutex);
23 /* Make sure we don't race between module exit and file write */
24 static int module_exits;
25
26 struct rcu_dev_filter {
27 struct rcu_head rcu;
28 char devname[NAME_MAX];
29 };
30
31 static struct rcu_dev_filter *dev_filter;
32 /* ~0UL inode_filter enables all inodes */
33 static unsigned long inode_filter = ~0UL;
34
35 /*
36 * Probes are executed in rcu_sched read-side critical section.
37 */
38
39 static int do_dev_filter(const char *dev)
40 {
41 struct rcu_dev_filter *ldev_filter = rcu_dereference(dev_filter);
42
43 if (unlikely(ldev_filter))
44 if (unlikely(strcmp(ldev_filter->devname, dev)))
45 return 0;
46 return 1;
47 }
48
49 static int do_inode_filter(unsigned long ino)
50 {
51 if (unlikely(inode_filter != ~0UL))
52 if (unlikely(inode_filter != ino))
53 return 0;
54 return 1;
55 }
56
57 /*
58 * Logical AND between dev and inode filter.
59 */
60 static int do_filter(const char *dev, unsigned long ino)
61 {
62 if (unlikely(!do_dev_filter(dev)))
63 return 0;
64 if (unlikely(!do_inode_filter(ino)))
65 return 0;
66 return 1;
67 }
68
69
70 void probe_ext4_free_inode(void *data, struct inode *inode)
71 {
72 if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)))
73 return;
74 trace_mark_tp(ext4, free_inode, ext4_free_inode,
75 probe_ext4_free_inode,
76 "dev %s ino %lu mode %d uid %lu gid %lu blocks %llu",
77 inode->i_sb->s_id, inode->i_ino, inode->i_mode,
78 (unsigned long) inode->i_uid, (unsigned long) inode->i_gid,
79 (unsigned long long) inode->i_blocks);
80 }
81
82 void probe_ext4_request_inode(void *data, struct inode *dir, int mode)
83 {
84 if (unlikely(!do_filter(dir->i_sb->s_id, dir->i_ino)))
85 return;
86 trace_mark_tp(ext4, request_inode, ext4_request_inode,
87 probe_ext4_request_inode,
88 "dev %s dir %lu mode %d",
89 dir->i_sb->s_id, dir->i_ino, mode);
90 }
91
92 void probe_ext4_allocate_inode(void *data, struct inode *inode, struct inode *dir, int mode)
93 {
94 if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)
95 && !do_filter(dir->i_sb->s_id, dir->i_ino)))
96 return;
97 trace_mark_tp(ext4, allocate_inode, ext4_allocate_inode,
98 probe_ext4_allocate_inode,
99 "dev %s ino %lu dir %lu mode %d",
100 dir->i_sb->s_id, inode->i_ino, dir->i_ino, mode);
101 }
102
103 void probe_ext4_write_begin(void *data, struct inode *inode, loff_t pos, unsigned int len,
104 unsigned int flags)
105 {
106 if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)))
107 return;
108 trace_mark_tp(ext4, write_begin, ext4_write_begin,
109 probe_ext4_write_begin,
110 "dev %s ino %lu pos %llu len %u flags %u",
111 inode->i_sb->s_id, inode->i_ino,
112 (unsigned long long) pos, len, flags);
113 }
114
115 void probe_ext4_ordered_write_end(void *data, struct inode *inode, loff_t pos,
116 unsigned int len, unsigned int copied)
117 {
118 if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)))
119 return;
120 trace_mark_tp(ext4, ordered_write_end, ext4_ordered_write_end,
121 probe_ext4_ordered_write_end,
122 "dev %s ino %lu pos %llu len %u copied %u",
123 inode->i_sb->s_id, inode->i_ino,
124 (unsigned long long) pos, len, copied);
125 }
126
127 void probe_ext4_writeback_write_end(void *data, struct inode *inode, loff_t pos,
128 unsigned int len, unsigned int copied)
129 {
130 if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)))
131 return;
132 trace_mark_tp(ext4, writeback_write_end, ext4_writeback_write_end,
133 probe_ext4_writeback_write_end,
134 "dev %s ino %lu pos %llu len %u copied %u",
135 inode->i_sb->s_id, inode->i_ino,
136 (unsigned long long) pos, len, copied);
137 }
138
139 void probe_ext4_journalled_write_end(void *data, struct inode *inode, loff_t pos,
140 unsigned int len, unsigned int copied)
141 {
142 if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)))
143 return;
144 trace_mark_tp(ext4, journalled_write_end, ext4_journalled_write_end,
145 probe_ext4_journalled_write_end,
146 "dev %s ino %lu pos %llu len %u copied %u",
147 inode->i_sb->s_id, inode->i_ino,
148 (unsigned long long) pos, len, copied);
149 }
150
151 /*
152 * note : wbc_flags will have to be decoded by userspace.
153 * #1x uses a single byte in the trace. Limits to 8 bits.
154 */
155 void probe_ext4_da_writepages(void *data, struct inode *inode,
156 struct writeback_control *wbc)
157 {
158 if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)))
159 return;
160 trace_mark_tp(ext4, da_writepages, ext4_da_writepages,
161 probe_ext4_da_writepages,
162 "dev %s ino %lu nr_to_write %ld "
163 "pages_skipped %ld range_start %llu range_end %llu "
164 "wbc_flags(nonblocking,for_kupdate,"
165 "for_reclaim,range_cyclic) #1x%u",
166 inode->i_sb->s_id, inode->i_ino, wbc->nr_to_write,
167 wbc->pages_skipped,
168 (unsigned long long) wbc->range_start,
169 (unsigned long long) wbc->range_end,
170 (wbc->nonblocking << 3)
171 | (wbc->for_kupdate << 2)
172 | (wbc->for_reclaim << 1)
173 | wbc->range_cyclic);
174 }
175
176 /*
177 * note : wbc_flags will have to be decoded by userspace.
178 * #1x uses a single byte in the trace. Limits to 8 bits.
179 */
180 void probe_ext4_da_writepages_result(void *data, struct inode *inode,
181 struct writeback_control *wbc,
182 int ret, int pages_written)
183 {
184 if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)))
185 return;
186 trace_mark_tp(ext4, da_writepages_result, ext4_da_writepages_result,
187 probe_ext4_da_writepages_result,
188 "dev %s ino %lu ret %d pages_written %d "
189 "pages_skipped %ld "
190 "wbc_flags(encountered_congestion,"
191 "more_io,no_nrwrite_index_update) #1x%u",
192 inode->i_sb->s_id, inode->i_ino, ret, pages_written,
193 wbc->pages_skipped,
194 (wbc->encountered_congestion << 2)
195 | (wbc->more_io << 1)
196 | wbc->no_nrwrite_index_update);
197 }
198
199 void probe_ext4_da_write_begin(void *data, struct inode *inode, loff_t pos,
200 unsigned int len, unsigned int flags)
201 {
202 if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)))
203 return;
204 trace_mark_tp(ext4, da_write_begin, ext4_da_write_begin,
205 probe_ext4_da_write_begin,
206 "dev %s ino %lu pos %llu len %u flags %u",
207 inode->i_sb->s_id, inode->i_ino,
208 (unsigned long long) pos, len, flags);
209 }
210
211 void probe_ext4_da_write_end(void *data, struct inode *inode, loff_t pos,
212 unsigned int len, unsigned int copied)
213 {
214 if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)))
215 return;
216 trace_mark_tp(ext4, da_write_end, ext4_da_write_end,
217 probe_ext4_da_write_end,
218 "dev %s ino %lu pos %llu len %u copied %u",
219 inode->i_sb->s_id, inode->i_ino,
220 (unsigned long long) pos, len, copied);
221 }
222
223 void probe_ext4_discard_blocks(void *data, struct super_block *sb, unsigned long long blk,
224 unsigned long long count)
225 {
226 if (unlikely(!do_dev_filter(sb->s_id)))
227 return;
228 trace_mark_tp(ext4, discard_blocks, ext4_discard_blocks,
229 probe_ext4_discard_blocks,
230 "dev %s blk %llu count %llu",
231 sb->s_id, blk, count);
232 }
233
234 void probe_ext4_mb_new_inode_pa(void *data, struct ext4_allocation_context *ac,
235 struct ext4_prealloc_space *pa)
236 {
237 if (unlikely(!do_filter(ac->ac_sb->s_id, ac->ac_inode->i_ino)))
238 return;
239 trace_mark_tp(ext4, mb_new_inode_pa, ext4_mb_new_inode_pa,
240 probe_ext4_mb_new_inode_pa,
241 "dev %s ino %lu pstart %llu len %u lstart %u",
242 ac->ac_sb->s_id, ac->ac_inode->i_ino, pa->pa_pstart,
243 pa->pa_len, pa->pa_lstart);
244 }
245
246 void probe_ext4_mb_new_group_pa(void *data, struct ext4_allocation_context *ac,
247 struct ext4_prealloc_space *pa)
248 {
249 if (unlikely(!do_dev_filter(ac->ac_sb->s_id)))
250 return;
251 trace_mark_tp(ext4, mb_new_group_pa, ext4_mb_new_group_pa,
252 probe_ext4_mb_new_group_pa,
253 "dev %s pstart %llu len %u lstart %u",
254 ac->ac_sb->s_id, pa->pa_pstart,
255 pa->pa_len, pa->pa_lstart);
256 }
257
258 void probe_ext4_mb_release_inode_pa(void *data, struct ext4_allocation_context *ac,
259 struct ext4_prealloc_space *pa,
260 unsigned long long block,
261 unsigned int count)
262 {
263 if (unlikely(!do_filter(ac->ac_sb->s_id, ac->ac_inode->i_ino)))
264 return;
265 trace_mark_tp(ext4, mb_release_inode_pa, ext4_mb_release_inode_pa,
266 probe_ext4_mb_release_inode_pa,
267 "dev %s ino %lu block %llu count %u",
268 ac->ac_sb->s_id, pa->pa_inode->i_ino, block, count);
269 }
270
271 void probe_ext4_mb_release_group_pa(void *data, struct ext4_allocation_context *ac,
272 struct ext4_prealloc_space *pa)
273 {
274 if (unlikely(!do_dev_filter(ac->ac_sb->s_id)))
275 return;
276 trace_mark_tp(ext4, mb_release_group_pa, ext4_mb_release_group_pa,
277 probe_ext4_mb_release_group_pa,
278 "dev %s pstart %llu len %d",
279 ac->ac_sb->s_id, pa->pa_pstart, pa->pa_len);
280 }
281
282 void probe_ext4_discard_preallocations(void *data, struct inode *inode)
283 {
284 if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)))
285 return;
286 trace_mark_tp(ext4, discard_preallocations,
287 ext4_discard_preallocations,
288 probe_ext4_discard_preallocations,
289 "dev %s ino %lu",
290 inode->i_sb->s_id, inode->i_ino);
291 }
292
293 void probe_ext4_mb_discard_preallocations(void *data, struct super_block *sb, int needed)
294 {
295 if (unlikely(!do_dev_filter(sb->s_id)))
296 return;
297 trace_mark_tp(ext4, mb_discard_preallocations,
298 ext4_mb_discard_preallocations,
299 probe_ext4_mb_discard_preallocations,
300 "dev %s needed %d",
301 sb->s_id, needed);
302 }
303
304 void probe_ext4_request_blocks(void *data, struct ext4_allocation_request *ar)
305 {
306 if (ar->inode) {
307 if (unlikely(!do_filter(ar->inode->i_sb->s_id,
308 ar->inode->i_ino)))
309 return;
310 } else {
311 if (unlikely(!do_dev_filter(ar->inode->i_sb->s_id)))
312 return;
313 }
314 trace_mark_tp(ext4, request_blocks, ext4_request_blocks,
315 probe_ext4_request_blocks,
316 "dev %s flags %u len %u ino %lu "
317 "lblk %llu goal %llu lleft %llu lright %llu "
318 "pleft %llu pright %llu",
319 ar->inode->i_sb->s_id, ar->flags, ar->len,
320 ar->inode ? ar->inode->i_ino : 0,
321 (unsigned long long) ar->logical,
322 (unsigned long long) ar->goal,
323 (unsigned long long) ar->lleft,
324 (unsigned long long) ar->lright,
325 (unsigned long long) ar->pleft,
326 (unsigned long long) ar->pright);
327 }
328
329 void probe_ext4_allocate_blocks(void *data, struct ext4_allocation_request *ar,
330 unsigned long long block)
331 {
332 if (ar->inode) {
333 if (unlikely(!do_filter(ar->inode->i_sb->s_id,
334 ar->inode->i_ino)))
335 return;
336 } else {
337 if (unlikely(!do_dev_filter(ar->inode->i_sb->s_id)))
338 return;
339 }
340 trace_mark_tp(ext4, allocate_blocks, ext4_allocate_blocks,
341 probe_ext4_allocate_blocks,
342 "dev %s block %llu flags %u len %u ino %lu "
343 "logical %llu goal %llu lleft %llu lright %llu "
344 "pleft %llu pright %llu",
345 ar->inode->i_sb->s_id, (unsigned long long) block,
346 ar->flags, ar->len, ar->inode ? ar->inode->i_ino : 0,
347 (unsigned long long) ar->logical,
348 (unsigned long long) ar->goal,
349 (unsigned long long) ar->lleft,
350 (unsigned long long) ar->lright,
351 (unsigned long long) ar->pleft,
352 (unsigned long long) ar->pright);
353 }
354
355 void probe_ext4_free_blocks(void *data, struct inode *inode, __u64 block,
356 unsigned long count, int metadata)
357 {
358 if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)))
359 return;
360 trace_mark_tp(ext4, free_blocks, ext4_free_blocks,
361 probe_ext4_free_blocks,
362 "dev %s block %llu count %lu metadata %d ino %lu",
363 inode->i_sb->s_id, (unsigned long long)block,
364 count, metadata, inode->i_ino);
365 }
366
367 void probe_ext4_sync_file(void *data, struct file *file, struct dentry *dentry,
368 int datasync)
369 {
370 if (unlikely(!do_dev_filter(dentry->d_inode->i_sb->s_id)))
371 return;
372 if (unlikely(!do_inode_filter(dentry->d_inode->i_ino)
373 && !do_inode_filter(dentry->d_parent->d_inode->i_ino)))
374 return;
375 trace_mark_tp(ext4, sync_file, ext4_sync_file,
376 probe_ext4_sync_file,
377 "dev %s datasync %d ino %ld parent %ld",
378 dentry->d_inode->i_sb->s_id, datasync, dentry->d_inode->i_ino,
379 dentry->d_parent->d_inode->i_ino);
380 }
381
382 void probe_ext4_sync_fs(void *data, struct super_block *sb, int wait)
383 {
384 if (unlikely(!do_dev_filter(sb->s_id)))
385 return;
386 trace_mark_tp(ext4, sync_fs, ext4_sync_fs,
387 probe_ext4_sync_fs,
388 "dev %s wait %d",
389 sb->s_id, wait);
390 }
391
392 static void free_dev_filter(struct rcu_head *head)
393 {
394 kfree(container_of(head, struct rcu_dev_filter, rcu));
395 }
396
397 static ssize_t dev_filter_op_write(struct file *file,
398 const char __user *user_buf, size_t count, loff_t *ppos)
399 {
400 int err = 0;
401 char buf[NAME_MAX];
402 int buf_size;
403 char name[NAME_MAX];
404 struct rcu_dev_filter *new, *old;
405
406 mutex_lock(&ext4_filter_mutex);
407 if (module_exits) {
408 err = -EPERM;
409 goto error;
410 }
411 buf_size = min(count, sizeof(buf) - 1);
412 err = copy_from_user(buf, user_buf, buf_size);
413 if (err)
414 goto error;
415 buf[buf_size] = 0;
416
417 if (sscanf(buf, "%s", name) != 1) {
418 err = -EPERM;
419 goto error;
420 }
421
422 old = dev_filter;
423
424 /* Empty string or * means all active */
425 if (name[0] == '\0' || (name[0] == '*' && name[1] == '\0')) {
426 new = NULL;
427 } else {
428 new = kmalloc(sizeof(*new), GFP_KERNEL);
429 strcpy(new->devname, name);
430 }
431
432 rcu_assign_pointer(dev_filter, new);
433 if (old)
434 call_rcu_sched(&old->rcu, free_dev_filter);
435
436 mutex_unlock(&ext4_filter_mutex);
437 return count;
438
439 error:
440 mutex_unlock(&ext4_filter_mutex);
441 return err;
442 }
443
444 static ssize_t dev_filter_op_read(struct file *filp, char __user *buffer,
445 size_t count, loff_t *ppos)
446 {
447 ssize_t bcount;
448 const char *devname;
449
450 mutex_lock(&ext4_filter_mutex);
451 if (!dev_filter)
452 devname = "*";
453 else
454 devname = dev_filter->devname;
455 bcount = simple_read_from_buffer(buffer, count, ppos,
456 devname, strlen(devname));
457 mutex_unlock(&ext4_filter_mutex);
458 return bcount;
459 }
460
461 static struct file_operations ext4_dev_file_operations = {
462 .write = dev_filter_op_write,
463 .read = dev_filter_op_read,
464 };
465
466 static ssize_t inode_filter_op_write(struct file *file,
467 const char __user *user_buf, size_t count, loff_t *ppos)
468 {
469 int err = 0;
470 char buf[NAME_MAX];
471 int buf_size;
472 char name[NAME_MAX];
473 unsigned long inode_num;
474
475 mutex_lock(&ext4_filter_mutex);
476 if (module_exits) {
477 err = -EPERM;
478 goto error;
479 }
480 buf_size = min(count, sizeof(buf) - 1);
481 err = copy_from_user(buf, user_buf, buf_size);
482 if (err)
483 goto error;
484 buf[buf_size] = 0;
485
486 if (sscanf(buf, "%s", name) != 1) {
487 err = -EPERM;
488 goto error;
489 }
490
491 /* Empty string or * means all active */
492 if (name[0] == '\0' || (name[0] == '*' && name[1] == '\0')) {
493 inode_filter = ~0UL;
494 } else {
495 if (sscanf(buf, "%lu", &inode_num) != 1) {
496 err = -EPERM;
497 goto error;
498 }
499 inode_filter = inode_num;
500 }
501
502 mutex_unlock(&ext4_filter_mutex);
503 return count;
504
505 error:
506 mutex_unlock(&ext4_filter_mutex);
507 return err;
508 }
509
510 static ssize_t inode_filter_op_read(struct file *filp, char __user *buffer,
511 size_t count, loff_t *ppos)
512 {
513 ssize_t bcount;
514 char inode_str[NAME_MAX];
515
516 mutex_lock(&ext4_filter_mutex);
517 if (inode_filter == ~0UL)
518 strcpy(inode_str, "*");
519 else {
520 bcount = snprintf(inode_str, sizeof(inode_str), "%lu",
521 inode_filter);
522 if (bcount == sizeof(inode_str))
523 bcount = -ENOSPC;
524 if (bcount < 0)
525 goto end;
526 }
527 bcount = simple_read_from_buffer(buffer, count, ppos,
528 inode_str, strlen(inode_str));
529 end:
530 mutex_unlock(&ext4_filter_mutex);
531 return bcount;
532 }
533
534 static struct file_operations ext4_inode_file_operations = {
535 .write = inode_filter_op_write,
536 .read = inode_filter_op_read,
537 };
538
539 static void release_filter_dev(void)
540 {
541 struct rcu_dev_filter *old;
542
543 mutex_lock(&ext4_filter_mutex);
544 module_exits = 1;
545 old = dev_filter;
546 rcu_assign_pointer(dev_filter, NULL);
547 if (old)
548 call_rcu_sched(&old->rcu, free_dev_filter);
549 mutex_unlock(&ext4_filter_mutex);
550 }
551
552 static int __init filter_init(void)
553 {
554 struct dentry *filter_root_dentry;
555 int err = 0;
556
557 filter_root_dentry = get_filter_root();
558 if (!filter_root_dentry) {
559 err = -ENOENT;
560 goto end;
561 }
562
563 ext4_filter_dentry = debugfs_create_dir("ext4", filter_root_dentry);
564
565 if (IS_ERR(ext4_filter_dentry) || !ext4_filter_dentry) {
566 printk(KERN_ERR "Failed to create ext4 filter file\n");
567 err = -ENOMEM;
568 goto end;
569 }
570
571 ext4_filter_dev_dentry = debugfs_create_file("dev", S_IWUSR,
572 ext4_filter_dentry, NULL, &ext4_dev_file_operations);
573 if (IS_ERR(ext4_filter_dev_dentry) || !ext4_filter_dev_dentry) {
574 printk(KERN_ERR "Failed to create ext4 dev filter file\n");
575 err = -ENOMEM;
576 goto release_filter_dentry;
577 }
578
579 ext4_filter_inode_dentry = debugfs_create_file("inode", S_IWUSR,
580 ext4_filter_dentry, NULL, &ext4_inode_file_operations);
581 if (IS_ERR(ext4_filter_inode_dentry) || !ext4_filter_inode_dentry) {
582 printk(KERN_ERR "Failed to create ext4 inode filter file\n");
583 err = -ENOMEM;
584 goto release_filter_dev_dentry;
585 }
586
587 goto end;
588
589 release_filter_dev_dentry:
590 debugfs_remove(ext4_filter_dev_dentry);
591 release_filter_dentry:
592 debugfs_remove(ext4_filter_dentry);
593 release_filter_dev();
594 end:
595 return err;
596 }
597
598 static void __exit filter_exit(void)
599 {
600 debugfs_remove(ext4_filter_dev_dentry);
601 debugfs_remove(ext4_filter_inode_dentry);
602 debugfs_remove(ext4_filter_dentry);
603 release_filter_dev();
604 }
605
606 module_init(filter_init);
607 module_exit(filter_exit);
608
609 MODULE_LICENSE("GPL and additional rights");
610 MODULE_AUTHOR("Mathieu Desnoyers");
611 MODULE_DESCRIPTION("ext4 Tracepoint Probes");
This page took 0.040762 seconds and 4 git commands to generate.