18c2ecf20Sopenharmony_ci/**
28c2ecf20Sopenharmony_ci * @file oprofilefs.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * @remark Copyright 2002 OProfile authors
58c2ecf20Sopenharmony_ci * @remark Read the file COPYING
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * @author John Levon
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * A simple filesystem for configuration and
108c2ecf20Sopenharmony_ci * access of oprofile.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/oprofile.h>
168c2ecf20Sopenharmony_ci#include <linux/fs.h>
178c2ecf20Sopenharmony_ci#include <linux/fs_context.h>
188c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
198c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include "oprof.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define OPROFILEFS_MAGIC 0x6f70726f
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ciDEFINE_RAW_SPINLOCK(oprofilefs_lock);
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic struct inode *oprofilefs_get_inode(struct super_block *sb, int mode)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	struct inode *inode = new_inode(sb);
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	if (inode) {
328c2ecf20Sopenharmony_ci		inode->i_ino = get_next_ino();
338c2ecf20Sopenharmony_ci		inode->i_mode = mode;
348c2ecf20Sopenharmony_ci		inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
358c2ecf20Sopenharmony_ci	}
368c2ecf20Sopenharmony_ci	return inode;
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic const struct super_operations s_ops = {
418c2ecf20Sopenharmony_ci	.statfs		= simple_statfs,
428c2ecf20Sopenharmony_ci	.drop_inode 	= generic_delete_inode,
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cissize_t oprofilefs_str_to_user(char const *str, char __user *buf, size_t count, loff_t *offset)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	return simple_read_from_buffer(buf, count, offset, str, strlen(str));
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#define TMPBUFSIZE 50
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cissize_t oprofilefs_ulong_to_user(unsigned long val, char __user *buf, size_t count, loff_t *offset)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	char tmpbuf[TMPBUFSIZE];
578c2ecf20Sopenharmony_ci	size_t maxlen = snprintf(tmpbuf, TMPBUFSIZE, "%lu\n", val);
588c2ecf20Sopenharmony_ci	if (maxlen > TMPBUFSIZE)
598c2ecf20Sopenharmony_ci		maxlen = TMPBUFSIZE;
608c2ecf20Sopenharmony_ci	return simple_read_from_buffer(buf, count, offset, tmpbuf, maxlen);
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/*
658c2ecf20Sopenharmony_ci * Note: If oprofilefs_ulong_from_user() returns 0, then *val remains
668c2ecf20Sopenharmony_ci * unchanged and might be uninitialized. This follows write syscall
678c2ecf20Sopenharmony_ci * implementation when count is zero: "If count is zero ... [and if]
688c2ecf20Sopenharmony_ci * no errors are detected, 0 will be returned without causing any
698c2ecf20Sopenharmony_ci * other effect." (man 2 write)
708c2ecf20Sopenharmony_ci */
718c2ecf20Sopenharmony_ciint oprofilefs_ulong_from_user(unsigned long *val, char const __user *buf, size_t count)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	char tmpbuf[TMPBUFSIZE];
748c2ecf20Sopenharmony_ci	unsigned long flags;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	if (!count)
778c2ecf20Sopenharmony_ci		return 0;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	if (count > TMPBUFSIZE - 1)
808c2ecf20Sopenharmony_ci		return -EINVAL;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	memset(tmpbuf, 0x0, TMPBUFSIZE);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	if (copy_from_user(tmpbuf, buf, count))
858c2ecf20Sopenharmony_ci		return -EFAULT;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&oprofilefs_lock, flags);
888c2ecf20Sopenharmony_ci	*val = simple_strtoul(tmpbuf, NULL, 0);
898c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&oprofilefs_lock, flags);
908c2ecf20Sopenharmony_ci	return count;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic ssize_t ulong_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	unsigned long *val = file->private_data;
978c2ecf20Sopenharmony_ci	return oprofilefs_ulong_to_user(*val, buf, count, offset);
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic ssize_t ulong_write_file(struct file *file, char const __user *buf, size_t count, loff_t *offset)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	unsigned long value;
1048c2ecf20Sopenharmony_ci	int retval;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (*offset)
1078c2ecf20Sopenharmony_ci		return -EINVAL;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	retval = oprofilefs_ulong_from_user(&value, buf, count);
1108c2ecf20Sopenharmony_ci	if (retval <= 0)
1118c2ecf20Sopenharmony_ci		return retval;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	retval = oprofile_set_ulong(file->private_data, value);
1148c2ecf20Sopenharmony_ci	if (retval)
1158c2ecf20Sopenharmony_ci		return retval;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	return count;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic const struct file_operations ulong_fops = {
1228c2ecf20Sopenharmony_ci	.read		= ulong_read_file,
1238c2ecf20Sopenharmony_ci	.write		= ulong_write_file,
1248c2ecf20Sopenharmony_ci	.open		= simple_open,
1258c2ecf20Sopenharmony_ci	.llseek		= default_llseek,
1268c2ecf20Sopenharmony_ci};
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic const struct file_operations ulong_ro_fops = {
1308c2ecf20Sopenharmony_ci	.read		= ulong_read_file,
1318c2ecf20Sopenharmony_ci	.open		= simple_open,
1328c2ecf20Sopenharmony_ci	.llseek		= default_llseek,
1338c2ecf20Sopenharmony_ci};
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic int __oprofilefs_create_file(struct dentry *root, char const *name,
1378c2ecf20Sopenharmony_ci	const struct file_operations *fops, int perm, void *priv)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	struct dentry *dentry;
1408c2ecf20Sopenharmony_ci	struct inode *inode;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (!root)
1438c2ecf20Sopenharmony_ci		return -ENOMEM;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	inode_lock(d_inode(root));
1468c2ecf20Sopenharmony_ci	dentry = d_alloc_name(root, name);
1478c2ecf20Sopenharmony_ci	if (!dentry) {
1488c2ecf20Sopenharmony_ci		inode_unlock(d_inode(root));
1498c2ecf20Sopenharmony_ci		return -ENOMEM;
1508c2ecf20Sopenharmony_ci	}
1518c2ecf20Sopenharmony_ci	inode = oprofilefs_get_inode(root->d_sb, S_IFREG | perm);
1528c2ecf20Sopenharmony_ci	if (!inode) {
1538c2ecf20Sopenharmony_ci		dput(dentry);
1548c2ecf20Sopenharmony_ci		inode_unlock(d_inode(root));
1558c2ecf20Sopenharmony_ci		return -ENOMEM;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci	inode->i_fop = fops;
1588c2ecf20Sopenharmony_ci	inode->i_private = priv;
1598c2ecf20Sopenharmony_ci	d_add(dentry, inode);
1608c2ecf20Sopenharmony_ci	inode_unlock(d_inode(root));
1618c2ecf20Sopenharmony_ci	return 0;
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ciint oprofilefs_create_ulong(struct dentry *root,
1668c2ecf20Sopenharmony_ci	char const *name, unsigned long *val)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	return __oprofilefs_create_file(root, name,
1698c2ecf20Sopenharmony_ci					&ulong_fops, 0644, val);
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ciint oprofilefs_create_ro_ulong(struct dentry *root,
1748c2ecf20Sopenharmony_ci	char const *name, unsigned long *val)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	return __oprofilefs_create_file(root, name,
1778c2ecf20Sopenharmony_ci					&ulong_ro_fops, 0444, val);
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic ssize_t atomic_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	atomic_t *val = file->private_data;
1848c2ecf20Sopenharmony_ci	return oprofilefs_ulong_to_user(atomic_read(val), buf, count, offset);
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic const struct file_operations atomic_ro_fops = {
1898c2ecf20Sopenharmony_ci	.read		= atomic_read_file,
1908c2ecf20Sopenharmony_ci	.open		= simple_open,
1918c2ecf20Sopenharmony_ci	.llseek		= default_llseek,
1928c2ecf20Sopenharmony_ci};
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ciint oprofilefs_create_ro_atomic(struct dentry *root,
1968c2ecf20Sopenharmony_ci	char const *name, atomic_t *val)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	return __oprofilefs_create_file(root, name,
1998c2ecf20Sopenharmony_ci					&atomic_ro_fops, 0444, val);
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ciint oprofilefs_create_file(struct dentry *root,
2048c2ecf20Sopenharmony_ci	char const *name, const struct file_operations *fops)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	return __oprofilefs_create_file(root, name, fops, 0644, NULL);
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ciint oprofilefs_create_file_perm(struct dentry *root,
2118c2ecf20Sopenharmony_ci	char const *name, const struct file_operations *fops, int perm)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	return __oprofilefs_create_file(root, name, fops, perm, NULL);
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistruct dentry *oprofilefs_mkdir(struct dentry *parent, char const *name)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	struct dentry *dentry;
2208c2ecf20Sopenharmony_ci	struct inode *inode;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	inode_lock(d_inode(parent));
2238c2ecf20Sopenharmony_ci	dentry = d_alloc_name(parent, name);
2248c2ecf20Sopenharmony_ci	if (!dentry) {
2258c2ecf20Sopenharmony_ci		inode_unlock(d_inode(parent));
2268c2ecf20Sopenharmony_ci		return NULL;
2278c2ecf20Sopenharmony_ci	}
2288c2ecf20Sopenharmony_ci	inode = oprofilefs_get_inode(parent->d_sb, S_IFDIR | 0755);
2298c2ecf20Sopenharmony_ci	if (!inode) {
2308c2ecf20Sopenharmony_ci		dput(dentry);
2318c2ecf20Sopenharmony_ci		inode_unlock(d_inode(parent));
2328c2ecf20Sopenharmony_ci		return NULL;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci	inode->i_op = &simple_dir_inode_operations;
2358c2ecf20Sopenharmony_ci	inode->i_fop = &simple_dir_operations;
2368c2ecf20Sopenharmony_ci	d_add(dentry, inode);
2378c2ecf20Sopenharmony_ci	inode_unlock(d_inode(parent));
2388c2ecf20Sopenharmony_ci	return dentry;
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic int oprofilefs_fill_super(struct super_block *sb, struct fs_context *fc)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	struct inode *root_inode;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	sb->s_blocksize = PAGE_SIZE;
2478c2ecf20Sopenharmony_ci	sb->s_blocksize_bits = PAGE_SHIFT;
2488c2ecf20Sopenharmony_ci	sb->s_magic = OPROFILEFS_MAGIC;
2498c2ecf20Sopenharmony_ci	sb->s_op = &s_ops;
2508c2ecf20Sopenharmony_ci	sb->s_time_gran = 1;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	root_inode = oprofilefs_get_inode(sb, S_IFDIR | 0755);
2538c2ecf20Sopenharmony_ci	if (!root_inode)
2548c2ecf20Sopenharmony_ci		return -ENOMEM;
2558c2ecf20Sopenharmony_ci	root_inode->i_op = &simple_dir_inode_operations;
2568c2ecf20Sopenharmony_ci	root_inode->i_fop = &simple_dir_operations;
2578c2ecf20Sopenharmony_ci	sb->s_root = d_make_root(root_inode);
2588c2ecf20Sopenharmony_ci	if (!sb->s_root)
2598c2ecf20Sopenharmony_ci		return -ENOMEM;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	oprofile_create_files(sb->s_root);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	// FIXME: verify kill_litter_super removes our dentries
2648c2ecf20Sopenharmony_ci	return 0;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic int oprofilefs_get_tree(struct fs_context *fc)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	return get_tree_single(fc, oprofilefs_fill_super);
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic const struct fs_context_operations oprofilefs_context_ops = {
2738c2ecf20Sopenharmony_ci	.get_tree	= oprofilefs_get_tree,
2748c2ecf20Sopenharmony_ci};
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic int oprofilefs_init_fs_context(struct fs_context *fc)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	fc->ops = &oprofilefs_context_ops;
2798c2ecf20Sopenharmony_ci	return 0;
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic struct file_system_type oprofilefs_type = {
2838c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
2848c2ecf20Sopenharmony_ci	.name		= "oprofilefs",
2858c2ecf20Sopenharmony_ci	.init_fs_context = oprofilefs_init_fs_context,
2868c2ecf20Sopenharmony_ci	.kill_sb	= kill_litter_super,
2878c2ecf20Sopenharmony_ci};
2888c2ecf20Sopenharmony_ciMODULE_ALIAS_FS("oprofilefs");
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ciint __init oprofilefs_register(void)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	return register_filesystem(&oprofilefs_type);
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_civoid __exit oprofilefs_unregister(void)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	unregister_filesystem(&oprofilefs_type);
3008c2ecf20Sopenharmony_ci}
301