[RFC 3/3] uprobe: add vdso support

Dmitry Safonov
2016-08-25T15:59:48Z

For RFC, this patch is full of hacks:
- non-generic vdso_mnt -- it will not built on !x86_64
- used externs in trace_uprobe.c, rather than clean header-includes

This patch converts vdsofs from lookup-less, renames vdso file name
to just "64". It also adds support to uprobe_events for setting uprobe
to vdso image file.

One can set vdso uprobe now like this:
echo 'p:gettimeofday :vdso:/64:0xc50' >> uprobe_events

Signed-off-by: Dmitry Safonov <dsafonov@virtuozzo.com>
---
arch/x86/entry/vdso/vma.c | 28 ++++++++++++++-------------
kernel/trace/trace_uprobe.c | 46 +++++++++++++++++++++++++++++++++++++--------
2 files changed, 53 insertions(+), 21 deletions(-)

diff --git a/arch/x86/entry/vdso/vma.c b/arch/x86/entry/vdso/vma.c
index e83830e422ae..9e966b649028 100644
--- a/arch/x86/entry/vdso/vma.c
+++ b/arch/x86/entry/vdso/vma.c
@@ -39,7 +39,7 @@ void __init init_vdso_image(const struct vdso_image *image)
image->alt_len));
}

-static struct vfsmount *vdso_mnt;
+struct vfsmount *vdso_mnt;
struct file *vdso_file_64;

struct linux_binprm;
@@ -401,38 +401,40 @@ static __init struct file *init_vdso_file(const struct vdso_image *vdso_image,
const char *name)
{
struct super_block *sb;
- struct qstr name_str;
struct inode *inode;
struct path path;
struct file *res;
+ int ret;

if (IS_ERR(vdso_mnt))
return ERR_CAST(vdso_mnt);
sb = vdso_mnt->mnt_sb;

- name_str.hash = 0;
- name_str.len = strlen(name);
- name_str.name = name;
-
res = ERR_PTR(-ENOMEM);
path.mnt = mntget(vdso_mnt);
- path.dentry = d_alloc_pseudo(sb, &name_str);
- if (!path.dentry)
+ path.dentry = d_alloc_name(vdso_mnt->mnt_root, name);
+ if (!path.dentry) {
+ pr_err("Failed to allocate dentry for %s vdso file\n", name);
goto put_path;
+ }
d_set_d_op(path.dentry, &vdso_dops);

res = ERR_PTR(-ENOSPC);
inode = ramfs_get_inode(sb, NULL, S_IFREG | S_IRUGO | S_IXUGO, 0);
- if (!inode)
+ if (!inode) {
+ pr_err("Failed to get inode for %s vdso file\n", name);
goto put_path;
+ }

inode->i_flags |= S_PRIVATE;
- d_instantiate(path.dentry, inode);
+ d_add(path.dentry, inode);
inode->i_size = vdso_image->size;

- res = ERR_PTR(add_vdso_pages_to_page_cache(vdso_image, inode));
- if (IS_ERR(res))
+ ret = add_vdso_pages_to_page_cache(vdso_image, inode);
+ if (ret) {
+ pr_err("Failed to put %s pages in page cache: %d\n", name, ret);
goto put_path;
+ }

res = alloc_file(&path, FMODE_READ, &ramfs_file_operations);
if (!IS_ERR(res))
@@ -475,7 +477,7 @@ static int __init init_vdso(void)

BUG_ON(init_vdso_fs());

- vdso_file_64 = init_vdso_file(&vdso_image_64, "vdso_image_64");
+ vdso_file_64 = init_vdso_file(&vdso_image_64, "64");
BUG_ON(IS_ERR(vdso_file_64));

/* notifier priority > KVM */
diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c
index a74f2d9ff379..3d4dc6c6ddd9 100644
--- a/kernel/trace/trace_uprobe.c
+++ b/kernel/trace/trace_uprobe.c
@@ -23,6 +23,7 @@
#include <linux/uprobes.h>
#include <linux/namei.h>
#include <linux/string.h>
+#include <linux/mount.h>

#include "trace_probe.h"

@@ -352,6 +353,9 @@ end:
*
* - Remove uprobe: -:[GRP/]EVENT
*/
+extern struct vfsmount *vdso_mnt;
+extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
+ const char *, unsigned int, struct path *);
static int create_trace_uprobe(int argc, char **argv)
{
struct trace_uprobe *tu;
@@ -427,15 +431,41 @@ static int create_trace_uprobe(int argc, char **argv)
pr_info("Probe point is not specified.\n");
return -EINVAL;
}
- arg = strchr(argv[1], ':');
- if (!arg) {
- ret = -EINVAL;
- goto fail_address_parse;
- }
+ if (argv[1][0] == ':') {
+ arg = strchr(&argv[1][1], ':');
+ if (!arg) {
+ ret = -EINVAL;
+ goto fail_address_parse;
+ }
+ *arg++ = '\0';
+ if (strcmp(&argv[1][1], "vdso")) {
+ ret = -EINVAL;
+ goto fail_address_parse;
+ }
+ filename = arg;
+ arg = strchr(filename, ':');
+ if (!filename) {
+ ret = -EINVAL;
+ goto fail_address_parse;
+ }
+ *arg++ = '\0';
+ if (!vdso_mnt) {
+ ret = -ENODEV;
+ goto fail_address_parse;
+ }
+ ret = vfs_path_lookup(vdso_mnt->mnt_root, vdso_mnt,
+ filename, LOOKUP_FOLLOW, &path);
+ } else {
+ arg = strchr(argv[1], ':');
+ if (!arg) {
+ ret = -EINVAL;
+ goto fail_address_parse;
+ }

- *arg++ = '\0';
- filename = argv[1];
- ret = kern_path(filename, LOOKUP_FOLLOW, &path);
+ *arg++ = '\0';
+ filename = argv[1];
+ ret = kern_path(filename, LOOKUP_FOLLOW, &path);
+ }
if (ret)
goto fail_address_parse;

--
2.9.0