aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRavi Bangoria <ravi.bangoria@linux.ibm.com>2018-08-20 00:42:47 -0400
committerSteven Rostedt (VMware) <rostedt@goodmis.org>2018-09-24 04:44:53 -0400
commit1cc33161a83d20b5462b1e93f95d3ce6388079ee (patch)
treebb9b7abedc85e2a78eb5268cd1dae921e3e6bb3d
parentd6b183eda466415bb5defcf9afe4cb64734839e8 (diff)
uprobes: Support SDT markers having reference count (semaphore)
Userspace Statically Defined Tracepoints[1] are dtrace style markers inside userspace applications. Applications like PostgreSQL, MySQL, Pthread, Perl, Python, Java, Ruby, Node.js, libvirt, QEMU, glib etc have these markers embedded in them. These markers are added by developer at important places in the code. Each marker source expands to a single nop instruction in the compiled code but there may be additional overhead for computing the marker arguments which expands to couple of instructions. In case the overhead is more, execution of it can be omitted by runtime if() condition when no one is tracing on the marker: if (reference_counter > 0) { Execute marker instructions; } Default value of reference counter is 0. Tracer has to increment the reference counter before tracing on a marker and decrement it when done with the tracing. Implement the reference counter logic in core uprobe. User will be able to use it from trace_uprobe as well as from kernel module. New trace_uprobe definition with reference counter will now be: <path>:<offset>[(ref_ctr_offset)] where ref_ctr_offset is an optional field. For kernel module, new variant of uprobe_register() has been introduced: uprobe_register_refctr(inode, offset, ref_ctr_offset, consumer) No new variant for uprobe_unregister() because it's assumed to have only one reference counter for one uprobe. [1] https://sourceware.org/systemtap/wiki/UserSpaceProbeImplementation Note: 'reference counter' is called as 'semaphore' in original Dtrace (or Systemtap, bcc and even in ELF) documentation and code. But the term 'semaphore' is misleading in this context. This is just a counter used to hold number of tracers tracing on a marker. This is not really used for any synchronization. So we are calling it a 'reference counter' in kernel / perf code. Link: http://lkml.kernel.org/r/20180820044250.11659-2-ravi.bangoria@linux.ibm.com Reviewed-by: Masami Hiramatsu <mhiramat@kernel.org> [Only trace_uprobe.c] Reviewed-by: Oleg Nesterov <oleg@redhat.com> Reviewed-by: Song Liu <songliubraving@fb.com> Tested-by: Song Liu <songliubraving@fb.com> Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.ibm.com> Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
-rw-r--r--include/linux/uprobes.h5
-rw-r--r--kernel/events/uprobes.c259
-rw-r--r--kernel/trace/trace.c2
-rw-r--r--kernel/trace/trace_uprobe.c38
4 files changed, 293 insertions, 11 deletions
diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index bb9d2084af03..103a48a48872 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -123,6 +123,7 @@ extern unsigned long uprobe_get_swbp_addr(struct pt_regs *regs);
123extern unsigned long uprobe_get_trap_addr(struct pt_regs *regs); 123extern unsigned long uprobe_get_trap_addr(struct pt_regs *regs);
124extern int uprobe_write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_t); 124extern int uprobe_write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_t);
125extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); 125extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc);
126extern int uprobe_register_refctr(struct inode *inode, loff_t offset, loff_t ref_ctr_offset, struct uprobe_consumer *uc);
126extern int uprobe_apply(struct inode *inode, loff_t offset, struct uprobe_consumer *uc, bool); 127extern int uprobe_apply(struct inode *inode, loff_t offset, struct uprobe_consumer *uc, bool);
127extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); 128extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc);
128extern int uprobe_mmap(struct vm_area_struct *vma); 129extern int uprobe_mmap(struct vm_area_struct *vma);
@@ -160,6 +161,10 @@ uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc)
160{ 161{
161 return -ENOSYS; 162 return -ENOSYS;
162} 163}
164static inline int uprobe_register_refctr(struct inode *inode, loff_t offset, loff_t ref_ctr_offset, struct uprobe_consumer *uc)
165{
166 return -ENOSYS;
167}
163static inline int 168static inline int
164uprobe_apply(struct inode *inode, loff_t offset, struct uprobe_consumer *uc, bool add) 169uprobe_apply(struct inode *inode, loff_t offset, struct uprobe_consumer *uc, bool add)
165{ 170{
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 3207a4d26849..934feb39f6be 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -73,6 +73,7 @@ struct uprobe {
73 struct uprobe_consumer *consumers; 73 struct uprobe_consumer *consumers;
74 struct inode *inode; /* Also hold a ref to inode */ 74 struct inode *inode; /* Also hold a ref to inode */
75 loff_t offset; 75 loff_t offset;
76 loff_t ref_ctr_offset;
76 unsigned long flags; 77 unsigned long flags;
77 78
78 /* 79 /*
@@ -88,6 +89,15 @@ struct uprobe {
88 struct arch_uprobe arch; 89 struct arch_uprobe arch;
89}; 90};
90 91
92struct delayed_uprobe {
93 struct list_head list;
94 struct uprobe *uprobe;
95 struct mm_struct *mm;
96};
97
98static DEFINE_MUTEX(delayed_uprobe_lock);
99static LIST_HEAD(delayed_uprobe_list);
100
91/* 101/*
92 * Execute out of line area: anonymous executable mapping installed 102 * Execute out of line area: anonymous executable mapping installed
93 * by the probed task to execute the copy of the original instruction 103 * by the probed task to execute the copy of the original instruction
@@ -282,6 +292,166 @@ static int verify_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t
282 return 1; 292 return 1;
283} 293}
284 294
295static struct delayed_uprobe *
296delayed_uprobe_check(struct uprobe *uprobe, struct mm_struct *mm)
297{
298 struct delayed_uprobe *du;
299
300 list_for_each_entry(du, &delayed_uprobe_list, list)
301 if (du->uprobe == uprobe && du->mm == mm)
302 return du;
303 return NULL;
304}
305
306static int delayed_uprobe_add(struct uprobe *uprobe, struct mm_struct *mm)
307{
308 struct delayed_uprobe *du;
309
310 if (delayed_uprobe_check(uprobe, mm))
311 return 0;
312
313 du = kzalloc(sizeof(*du), GFP_KERNEL);
314 if (!du)
315 return -ENOMEM;
316
317 du->uprobe = uprobe;
318 du->mm = mm;
319 list_add(&du->list, &delayed_uprobe_list);
320 return 0;
321}
322
323static void delayed_uprobe_delete(struct delayed_uprobe *du)
324{
325 if (WARN_ON(!du))
326 return;
327 list_del(&du->list);
328 kfree(du);
329}
330
331static void delayed_uprobe_remove(struct uprobe *uprobe, struct mm_struct *mm)
332{
333 struct list_head *pos, *q;
334 struct delayed_uprobe *du;
335
336 if (!uprobe && !mm)
337 return;
338
339 list_for_each_safe(pos, q, &delayed_uprobe_list) {
340 du = list_entry(pos, struct delayed_uprobe, list);
341
342 if (uprobe && du->uprobe != uprobe)
343 continue;
344 if (mm && du->mm != mm)
345 continue;
346
347 delayed_uprobe_delete(du);
348 }
349}
350
351static bool valid_ref_ctr_vma(struct uprobe *uprobe,
352 struct vm_area_struct *vma)
353{
354 unsigned long vaddr = offset_to_vaddr(vma, uprobe->ref_ctr_offset);
355
356 return uprobe->ref_ctr_offset &&
357 vma->vm_file &&
358 file_inode(vma->vm_file) == uprobe->inode &&
359 (vma->vm_flags & (VM_WRITE|VM_SHARED)) == VM_WRITE &&
360 vma->vm_start <= vaddr &&
361 vma->vm_end > vaddr;
362}
363
364static struct vm_area_struct *
365find_ref_ctr_vma(struct uprobe *uprobe, struct mm_struct *mm)
366{
367 struct vm_area_struct *tmp;
368
369 for (tmp = mm->mmap; tmp; tmp = tmp->vm_next)
370 if (valid_ref_ctr_vma(uprobe, tmp))
371 return tmp;
372
373 return NULL;
374}
375
376static int
377__update_ref_ctr(struct mm_struct *mm, unsigned long vaddr, short d)
378{
379 void *kaddr;
380 struct page *page;
381 struct vm_area_struct *vma;
382 int ret;
383 short *ptr;
384
385 if (!vaddr || !d)
386 return -EINVAL;
387
388 ret = get_user_pages_remote(NULL, mm, vaddr, 1,
389 FOLL_WRITE, &page, &vma, NULL);
390 if (unlikely(ret <= 0)) {
391 /*
392 * We are asking for 1 page. If get_user_pages_remote() fails,
393 * it may return 0, in that case we have to return error.
394 */
395 return ret == 0 ? -EBUSY : ret;
396 }
397
398 kaddr = kmap_atomic(page);
399 ptr = kaddr + (vaddr & ~PAGE_MASK);
400
401 if (unlikely(*ptr + d < 0)) {
402 pr_warn("ref_ctr going negative. vaddr: 0x%lx, "
403 "curr val: %d, delta: %d\n", vaddr, *ptr, d);
404 ret = -EINVAL;
405 goto out;
406 }
407
408 *ptr += d;
409 ret = 0;
410out:
411 kunmap_atomic(kaddr);
412 put_page(page);
413 return ret;
414}
415
416static void update_ref_ctr_warn(struct uprobe *uprobe,
417 struct mm_struct *mm, short d)
418{
419 pr_warn("ref_ctr %s failed for inode: 0x%lx offset: "
420 "0x%llx ref_ctr_offset: 0x%llx of mm: 0x%pK\n",
421 d > 0 ? "increment" : "decrement", uprobe->inode->i_ino,
422 (unsigned long long) uprobe->offset,
423 (unsigned long long) uprobe->ref_ctr_offset, mm);
424}
425
426static int update_ref_ctr(struct uprobe *uprobe, struct mm_struct *mm,
427 short d)
428{
429 struct vm_area_struct *rc_vma;
430 unsigned long rc_vaddr;
431 int ret = 0;
432
433 rc_vma = find_ref_ctr_vma(uprobe, mm);
434
435 if (rc_vma) {
436 rc_vaddr = offset_to_vaddr(rc_vma, uprobe->ref_ctr_offset);
437 ret = __update_ref_ctr(mm, rc_vaddr, d);
438 if (ret)
439 update_ref_ctr_warn(uprobe, mm, d);
440
441 if (d > 0)
442 return ret;
443 }
444
445 mutex_lock(&delayed_uprobe_lock);
446 if (d > 0)
447 ret = delayed_uprobe_add(uprobe, mm);
448 else
449 delayed_uprobe_remove(uprobe, mm);
450 mutex_unlock(&delayed_uprobe_lock);
451
452 return ret;
453}
454
285/* 455/*
286 * NOTE: 456 * NOTE:
287 * Expect the breakpoint instruction to be the smallest size instruction for 457 * Expect the breakpoint instruction to be the smallest size instruction for
@@ -302,9 +472,13 @@ static int verify_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t
302int uprobe_write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm, 472int uprobe_write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,
303 unsigned long vaddr, uprobe_opcode_t opcode) 473 unsigned long vaddr, uprobe_opcode_t opcode)
304{ 474{
475 struct uprobe *uprobe;
305 struct page *old_page, *new_page; 476 struct page *old_page, *new_page;
306 struct vm_area_struct *vma; 477 struct vm_area_struct *vma;
307 int ret; 478 int ret, is_register, ref_ctr_updated = 0;
479
480 is_register = is_swbp_insn(&opcode);
481 uprobe = container_of(auprobe, struct uprobe, arch);
308 482
309retry: 483retry:
310 /* Read the page with vaddr into memory */ 484 /* Read the page with vaddr into memory */
@@ -317,6 +491,15 @@ retry:
317 if (ret <= 0) 491 if (ret <= 0)
318 goto put_old; 492 goto put_old;
319 493
494 /* We are going to replace instruction, update ref_ctr. */
495 if (!ref_ctr_updated && uprobe->ref_ctr_offset) {
496 ret = update_ref_ctr(uprobe, mm, is_register ? 1 : -1);
497 if (ret)
498 goto put_old;
499
500 ref_ctr_updated = 1;
501 }
502
320 ret = anon_vma_prepare(vma); 503 ret = anon_vma_prepare(vma);
321 if (ret) 504 if (ret)
322 goto put_old; 505 goto put_old;
@@ -337,6 +520,11 @@ put_old:
337 520
338 if (unlikely(ret == -EAGAIN)) 521 if (unlikely(ret == -EAGAIN))
339 goto retry; 522 goto retry;
523
524 /* Revert back reference counter if instruction update failed. */
525 if (ret && is_register && ref_ctr_updated)
526 update_ref_ctr(uprobe, mm, -1);
527
340 return ret; 528 return ret;
341} 529}
342 530
@@ -378,8 +566,15 @@ static struct uprobe *get_uprobe(struct uprobe *uprobe)
378 566
379static void put_uprobe(struct uprobe *uprobe) 567static void put_uprobe(struct uprobe *uprobe)
380{ 568{
381 if (atomic_dec_and_test(&uprobe->ref)) 569 if (atomic_dec_and_test(&uprobe->ref)) {
570 /*
571 * If application munmap(exec_vma) before uprobe_unregister()
572 * gets called, we don't get a chance to remove uprobe from
573 * delayed_uprobe_list from remove_breakpoint(). Do it here.
574 */
575 delayed_uprobe_remove(uprobe, NULL);
382 kfree(uprobe); 576 kfree(uprobe);
577 }
383} 578}
384 579
385static int match_uprobe(struct uprobe *l, struct uprobe *r) 580static int match_uprobe(struct uprobe *l, struct uprobe *r)
@@ -484,7 +679,8 @@ static struct uprobe *insert_uprobe(struct uprobe *uprobe)
484 return u; 679 return u;
485} 680}
486 681
487static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset) 682static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset,
683 loff_t ref_ctr_offset)
488{ 684{
489 struct uprobe *uprobe, *cur_uprobe; 685 struct uprobe *uprobe, *cur_uprobe;
490 686
@@ -494,6 +690,7 @@ static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset)
494 690
495 uprobe->inode = inode; 691 uprobe->inode = inode;
496 uprobe->offset = offset; 692 uprobe->offset = offset;
693 uprobe->ref_ctr_offset = ref_ctr_offset;
497 init_rwsem(&uprobe->register_rwsem); 694 init_rwsem(&uprobe->register_rwsem);
498 init_rwsem(&uprobe->consumer_rwsem); 695 init_rwsem(&uprobe->consumer_rwsem);
499 696
@@ -895,7 +1092,7 @@ EXPORT_SYMBOL_GPL(uprobe_unregister);
895 * else return 0 (success) 1092 * else return 0 (success)
896 */ 1093 */
897static int __uprobe_register(struct inode *inode, loff_t offset, 1094static int __uprobe_register(struct inode *inode, loff_t offset,
898 struct uprobe_consumer *uc) 1095 loff_t ref_ctr_offset, struct uprobe_consumer *uc)
899{ 1096{
900 struct uprobe *uprobe; 1097 struct uprobe *uprobe;
901 int ret; 1098 int ret;
@@ -912,7 +1109,7 @@ static int __uprobe_register(struct inode *inode, loff_t offset,
912 return -EINVAL; 1109 return -EINVAL;
913 1110
914 retry: 1111 retry:
915 uprobe = alloc_uprobe(inode, offset); 1112 uprobe = alloc_uprobe(inode, offset, ref_ctr_offset);
916 if (!uprobe) 1113 if (!uprobe)
917 return -ENOMEM; 1114 return -ENOMEM;
918 /* 1115 /*
@@ -938,10 +1135,17 @@ static int __uprobe_register(struct inode *inode, loff_t offset,
938int uprobe_register(struct inode *inode, loff_t offset, 1135int uprobe_register(struct inode *inode, loff_t offset,
939 struct uprobe_consumer *uc) 1136 struct uprobe_consumer *uc)
940{ 1137{
941 return __uprobe_register(inode, offset, uc); 1138 return __uprobe_register(inode, offset, 0, uc);
942} 1139}
943EXPORT_SYMBOL_GPL(uprobe_register); 1140EXPORT_SYMBOL_GPL(uprobe_register);
944 1141
1142int uprobe_register_refctr(struct inode *inode, loff_t offset,
1143 loff_t ref_ctr_offset, struct uprobe_consumer *uc)
1144{
1145 return __uprobe_register(inode, offset, ref_ctr_offset, uc);
1146}
1147EXPORT_SYMBOL_GPL(uprobe_register_refctr);
1148
945/* 1149/*
946 * uprobe_apply - unregister an already registered probe. 1150 * uprobe_apply - unregister an already registered probe.
947 * @inode: the file in which the probe has to be removed. 1151 * @inode: the file in which the probe has to be removed.
@@ -1060,6 +1264,35 @@ static void build_probe_list(struct inode *inode,
1060 spin_unlock(&uprobes_treelock); 1264 spin_unlock(&uprobes_treelock);
1061} 1265}
1062 1266
1267/* @vma contains reference counter, not the probed instruction. */
1268static int delayed_ref_ctr_inc(struct vm_area_struct *vma)
1269{
1270 struct list_head *pos, *q;
1271 struct delayed_uprobe *du;
1272 unsigned long vaddr;
1273 int ret = 0, err = 0;
1274
1275 mutex_lock(&delayed_uprobe_lock);
1276 list_for_each_safe(pos, q, &delayed_uprobe_list) {
1277 du = list_entry(pos, struct delayed_uprobe, list);
1278
1279 if (du->mm != vma->vm_mm ||
1280 !valid_ref_ctr_vma(du->uprobe, vma))
1281 continue;
1282
1283 vaddr = offset_to_vaddr(vma, du->uprobe->ref_ctr_offset);
1284 ret = __update_ref_ctr(vma->vm_mm, vaddr, 1);
1285 if (ret) {
1286 update_ref_ctr_warn(du->uprobe, vma->vm_mm, 1);
1287 if (!err)
1288 err = ret;
1289 }
1290 delayed_uprobe_delete(du);
1291 }
1292 mutex_unlock(&delayed_uprobe_lock);
1293 return err;
1294}
1295
1063/* 1296/*
1064 * Called from mmap_region/vma_adjust with mm->mmap_sem acquired. 1297 * Called from mmap_region/vma_adjust with mm->mmap_sem acquired.
1065 * 1298 *
@@ -1072,7 +1305,15 @@ int uprobe_mmap(struct vm_area_struct *vma)
1072 struct uprobe *uprobe, *u; 1305 struct uprobe *uprobe, *u;
1073 struct inode *inode; 1306 struct inode *inode;
1074 1307
1075 if (no_uprobe_events() || !valid_vma(vma, true)) 1308 if (no_uprobe_events())
1309 return 0;
1310
1311 if (vma->vm_file &&
1312 (vma->vm_flags & (VM_WRITE|VM_SHARED)) == VM_WRITE &&
1313 test_bit(MMF_HAS_UPROBES, &vma->vm_mm->flags))
1314 delayed_ref_ctr_inc(vma);
1315
1316 if (!valid_vma(vma, true))
1076 return 0; 1317 return 0;
1077 1318
1078 inode = file_inode(vma->vm_file); 1319 inode = file_inode(vma->vm_file);
@@ -1246,6 +1487,10 @@ void uprobe_clear_state(struct mm_struct *mm)
1246{ 1487{
1247 struct xol_area *area = mm->uprobes_state.xol_area; 1488 struct xol_area *area = mm->uprobes_state.xol_area;
1248 1489
1490 mutex_lock(&delayed_uprobe_lock);
1491 delayed_uprobe_remove(NULL, mm);
1492 mutex_unlock(&delayed_uprobe_lock);
1493
1249 if (!area) 1494 if (!area)
1250 return; 1495 return;
1251 1496
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index bf6f1d70484d..147be8523560 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -4621,7 +4621,7 @@ static const char readme_msg[] =
4621 "place (kretprobe): [<module>:]<symbol>[+<offset>]|<memaddr>\n" 4621 "place (kretprobe): [<module>:]<symbol>[+<offset>]|<memaddr>\n"
4622#endif 4622#endif
4623#ifdef CONFIG_UPROBE_EVENTS 4623#ifdef CONFIG_UPROBE_EVENTS
4624 "\t place: <path>:<offset>\n" 4624 " place (uprobe): <path>:<offset>[(ref_ctr_offset)]\n"
4625#endif 4625#endif
4626 "\t args: <name>=fetcharg[:type]\n" 4626 "\t args: <name>=fetcharg[:type]\n"
4627 "\t fetcharg: %<register>, @<address>, @<symbol>[+|-<offset>],\n" 4627 "\t fetcharg: %<register>, @<address>, @<symbol>[+|-<offset>],\n"
diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c
index e696667da29a..7b85172beab6 100644
--- a/kernel/trace/trace_uprobe.c
+++ b/kernel/trace/trace_uprobe.c
@@ -47,6 +47,7 @@ struct trace_uprobe {
47 struct inode *inode; 47 struct inode *inode;
48 char *filename; 48 char *filename;
49 unsigned long offset; 49 unsigned long offset;
50 unsigned long ref_ctr_offset;
50 unsigned long nhit; 51 unsigned long nhit;
51 struct trace_probe tp; 52 struct trace_probe tp;
52}; 53};
@@ -352,10 +353,10 @@ end:
352static int create_trace_uprobe(int argc, char **argv) 353static int create_trace_uprobe(int argc, char **argv)
353{ 354{
354 struct trace_uprobe *tu; 355 struct trace_uprobe *tu;
355 char *arg, *event, *group, *filename; 356 char *arg, *event, *group, *filename, *rctr, *rctr_end;
356 char buf[MAX_EVENT_NAME_LEN]; 357 char buf[MAX_EVENT_NAME_LEN];
357 struct path path; 358 struct path path;
358 unsigned long offset; 359 unsigned long offset, ref_ctr_offset;
359 bool is_delete, is_return; 360 bool is_delete, is_return;
360 int i, ret; 361 int i, ret;
361 362
@@ -364,6 +365,7 @@ static int create_trace_uprobe(int argc, char **argv)
364 is_return = false; 365 is_return = false;
365 event = NULL; 366 event = NULL;
366 group = NULL; 367 group = NULL;
368 ref_ctr_offset = 0;
367 369
368 /* argc must be >= 1 */ 370 /* argc must be >= 1 */
369 if (argv[0][0] == '-') 371 if (argv[0][0] == '-')
@@ -438,6 +440,26 @@ static int create_trace_uprobe(int argc, char **argv)
438 goto fail_address_parse; 440 goto fail_address_parse;
439 } 441 }
440 442
443 /* Parse reference counter offset if specified. */
444 rctr = strchr(arg, '(');
445 if (rctr) {
446 rctr_end = strchr(rctr, ')');
447 if (rctr > rctr_end || *(rctr_end + 1) != 0) {
448 ret = -EINVAL;
449 pr_info("Invalid reference counter offset.\n");
450 goto fail_address_parse;
451 }
452
453 *rctr++ = '\0';
454 *rctr_end = '\0';
455 ret = kstrtoul(rctr, 0, &ref_ctr_offset);
456 if (ret) {
457 pr_info("Invalid reference counter offset.\n");
458 goto fail_address_parse;
459 }
460 }
461
462 /* Parse uprobe offset. */
441 ret = kstrtoul(arg, 0, &offset); 463 ret = kstrtoul(arg, 0, &offset);
442 if (ret) 464 if (ret)
443 goto fail_address_parse; 465 goto fail_address_parse;
@@ -472,6 +494,7 @@ static int create_trace_uprobe(int argc, char **argv)
472 goto fail_address_parse; 494 goto fail_address_parse;
473 } 495 }
474 tu->offset = offset; 496 tu->offset = offset;
497 tu->ref_ctr_offset = ref_ctr_offset;
475 tu->path = path; 498 tu->path = path;
476 tu->filename = kstrdup(filename, GFP_KERNEL); 499 tu->filename = kstrdup(filename, GFP_KERNEL);
477 500
@@ -590,6 +613,9 @@ static int probes_seq_show(struct seq_file *m, void *v)
590 trace_event_name(&tu->tp.call), tu->filename, 613 trace_event_name(&tu->tp.call), tu->filename,
591 (int)(sizeof(void *) * 2), tu->offset); 614 (int)(sizeof(void *) * 2), tu->offset);
592 615
616 if (tu->ref_ctr_offset)
617 seq_printf(m, "(0x%lx)", tu->ref_ctr_offset);
618
593 for (i = 0; i < tu->tp.nr_args; i++) 619 for (i = 0; i < tu->tp.nr_args; i++)
594 seq_printf(m, " %s=%s", tu->tp.args[i].name, tu->tp.args[i].comm); 620 seq_printf(m, " %s=%s", tu->tp.args[i].name, tu->tp.args[i].comm);
595 621
@@ -905,7 +931,13 @@ probe_event_enable(struct trace_uprobe *tu, struct trace_event_file *file,
905 931
906 tu->consumer.filter = filter; 932 tu->consumer.filter = filter;
907 tu->inode = d_real_inode(tu->path.dentry); 933 tu->inode = d_real_inode(tu->path.dentry);
908 ret = uprobe_register(tu->inode, tu->offset, &tu->consumer); 934 if (tu->ref_ctr_offset) {
935 ret = uprobe_register_refctr(tu->inode, tu->offset,
936 tu->ref_ctr_offset, &tu->consumer);
937 } else {
938 ret = uprobe_register(tu->inode, tu->offset, &tu->consumer);
939 }
940
909 if (ret) 941 if (ret)
910 goto err_buffer; 942 goto err_buffer;
911 943