Revision d0313e629f2690edfd269896b398048275227db0 authored by Ian Rogers on 12 August 2022, 23:09:49 UTC, committed by Arnaldo Carvalho de Melo on 13 August 2022, 18:03:09 UTC
If a shorter string ends a longer string then the shorter string may
reuse the longer string at an offset. For example, on x86 the event
arith.cycles_div_busy and cycles_div_busy can be folded, even though
they have difference names the strings are identical after 6
characters. cycles_div_busy can reuse the arith.cycles_div_busy string
at an offset of 6.

In pmu-events.c this looks like the following where the 'also:' lists
folded strings:

/* offset=177541 */ "arith.cycles_div_busy\000\000pipeline\000Cycles the divider is busy\000\000\000event=0x14,period=2000000,umask=0x1\000\000\000\000\000\000\000\000\000" /* also: cycles_div_busy\000\000pipeline\000Cycles the divider is busy\000\000\000event=0x14,period=2000000,umask=0x1\000\000\000\000\000\000\000\000\000 */

As jevents.py combines multiple strings for an event into a larger
string, the amount of folding is minimal as all parts of the event must
align. Other organizations can benefit more from folding, but lose space
by say recording more offsets.

Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: John Garry <john.garry@huawei.com>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Leo Yan <leo.yan@linaro.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Mike Leach <mike.leach@linaro.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ravi Bangoria <ravi.bangoria@amd.com>
Cc: Stephane Eranian <eranian@google.com>
Cc: Will Deacon <will@kernel.org>
Cc: Xing Zhengjun <zhengjun.xing@linux.intel.com>
Cc: linux-arm-kernel@lists.infradead.org
Link: https://lore.kernel.org/r/20220812230949.683239-15-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
1 parent 9118259
Raw File
usermode_driver.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * umd - User mode driver support
 */
#include <linux/shmem_fs.h>
#include <linux/pipe_fs_i.h>
#include <linux/mount.h>
#include <linux/fs_struct.h>
#include <linux/task_work.h>
#include <linux/usermode_driver.h>

static struct vfsmount *blob_to_mnt(const void *data, size_t len, const char *name)
{
	struct file_system_type *type;
	struct vfsmount *mnt;
	struct file *file;
	ssize_t written;
	loff_t pos = 0;

	type = get_fs_type("tmpfs");
	if (!type)
		return ERR_PTR(-ENODEV);

	mnt = kern_mount(type);
	put_filesystem(type);
	if (IS_ERR(mnt))
		return mnt;

	file = file_open_root_mnt(mnt, name, O_CREAT | O_WRONLY, 0700);
	if (IS_ERR(file)) {
		kern_unmount(mnt);
		return ERR_CAST(file);
	}

	written = kernel_write(file, data, len, &pos);
	if (written != len) {
		int err = written;
		if (err >= 0)
			err = -ENOMEM;
		filp_close(file, NULL);
		kern_unmount(mnt);
		return ERR_PTR(err);
	}

	fput(file);

	/* Flush delayed fput so exec can open the file read-only */
	flush_delayed_fput();
	task_work_run();
	return mnt;
}

/**
 * umd_load_blob - Remember a blob of bytes for fork_usermode_driver
 * @info: information about usermode driver
 * @data: a blob of bytes that can be executed as a file
 * @len:  The lentgh of the blob
 *
 */
int umd_load_blob(struct umd_info *info, const void *data, size_t len)
{
	struct vfsmount *mnt;

	if (WARN_ON_ONCE(info->wd.dentry || info->wd.mnt))
		return -EBUSY;

	mnt = blob_to_mnt(data, len, info->driver_name);
	if (IS_ERR(mnt))
		return PTR_ERR(mnt);

	info->wd.mnt = mnt;
	info->wd.dentry = mnt->mnt_root;
	return 0;
}
EXPORT_SYMBOL_GPL(umd_load_blob);

/**
 * umd_unload_blob - Disassociate @info from a previously loaded blob
 * @info: information about usermode driver
 *
 */
int umd_unload_blob(struct umd_info *info)
{
	if (WARN_ON_ONCE(!info->wd.mnt ||
			 !info->wd.dentry ||
			 info->wd.mnt->mnt_root != info->wd.dentry))
		return -EINVAL;

	kern_unmount(info->wd.mnt);
	info->wd.mnt = NULL;
	info->wd.dentry = NULL;
	return 0;
}
EXPORT_SYMBOL_GPL(umd_unload_blob);

static int umd_setup(struct subprocess_info *info, struct cred *new)
{
	struct umd_info *umd_info = info->data;
	struct file *from_umh[2];
	struct file *to_umh[2];
	int err;

	/* create pipe to send data to umh */
	err = create_pipe_files(to_umh, 0);
	if (err)
		return err;
	err = replace_fd(0, to_umh[0], 0);
	fput(to_umh[0]);
	if (err < 0) {
		fput(to_umh[1]);
		return err;
	}

	/* create pipe to receive data from umh */
	err = create_pipe_files(from_umh, 0);
	if (err) {
		fput(to_umh[1]);
		replace_fd(0, NULL, 0);
		return err;
	}
	err = replace_fd(1, from_umh[1], 0);
	fput(from_umh[1]);
	if (err < 0) {
		fput(to_umh[1]);
		replace_fd(0, NULL, 0);
		fput(from_umh[0]);
		return err;
	}

	set_fs_pwd(current->fs, &umd_info->wd);
	umd_info->pipe_to_umh = to_umh[1];
	umd_info->pipe_from_umh = from_umh[0];
	umd_info->tgid = get_pid(task_tgid(current));
	return 0;
}

static void umd_cleanup(struct subprocess_info *info)
{
	struct umd_info *umd_info = info->data;

	/* cleanup if umh_setup() was successful but exec failed */
	if (info->retval)
		umd_cleanup_helper(umd_info);
}

/**
 * umd_cleanup_helper - release the resources which were allocated in umd_setup
 * @info: information about usermode driver
 */
void umd_cleanup_helper(struct umd_info *info)
{
	fput(info->pipe_to_umh);
	fput(info->pipe_from_umh);
	put_pid(info->tgid);
	info->tgid = NULL;
}
EXPORT_SYMBOL_GPL(umd_cleanup_helper);

/**
 * fork_usermode_driver - fork a usermode driver
 * @info: information about usermode driver (shouldn't be NULL)
 *
 * Returns either negative error or zero which indicates success in
 * executing a usermode driver. In such case 'struct umd_info *info'
 * is populated with two pipes and a tgid of the process. The caller is
 * responsible for health check of the user process, killing it via
 * tgid, and closing the pipes when user process is no longer needed.
 */
int fork_usermode_driver(struct umd_info *info)
{
	struct subprocess_info *sub_info;
	const char *argv[] = { info->driver_name, NULL };
	int err;

	if (WARN_ON_ONCE(info->tgid))
		return -EBUSY;

	err = -ENOMEM;
	sub_info = call_usermodehelper_setup(info->driver_name,
					     (char **)argv, NULL, GFP_KERNEL,
					     umd_setup, umd_cleanup, info);
	if (!sub_info)
		goto out;

	err = call_usermodehelper_exec(sub_info, UMH_WAIT_EXEC);
out:
	return err;
}
EXPORT_SYMBOL_GPL(fork_usermode_driver);


back to top