blob: 0875f2f122b3182104155e5eca1956d5efd2d259 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/**
2 * @file oprofilefs.c
3 *
4 * @remark Copyright 2002 OProfile authors
5 * @remark Read the file COPYING
6 *
7 * @author John Levon
8 *
9 * A simple filesystem for configuration and
10 * access of oprofile.
11 */
12
13#include <linux/init.h>
14#include <linux/module.h>
15#include <linux/oprofile.h>
16#include <linux/fs.h>
David Howellsc6a2c722019-03-25 16:38:30 +000017#include <linux/fs_context.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <linux/pagemap.h>
Linus Torvalds7c0f6ba2016-12-24 11:46:01 -080019#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020
21#include "oprof.h"
22
23#define OPROFILEFS_MAGIC 0x6f70726f
24
Thomas Gleixner2d21a292009-07-25 16:18:34 +020025DEFINE_RAW_SPINLOCK(oprofilefs_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070026
Robert Richter25ad2912008-09-05 17:12:36 +020027static struct inode *oprofilefs_get_inode(struct super_block *sb, int mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -070028{
Robert Richter25ad2912008-09-05 17:12:36 +020029 struct inode *inode = new_inode(sb);
Linus Torvalds1da177e2005-04-16 15:20:36 -070030
31 if (inode) {
Christoph Hellwig85fe4022010-10-23 11:19:54 -040032 inode->i_ino = get_next_ino();
Linus Torvalds1da177e2005-04-16 15:20:36 -070033 inode->i_mode = mode;
Deepa Dinamani078cd822016-09-14 07:48:04 -070034 inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -070035 }
36 return inode;
37}
38
39
Alexey Dobriyanb87221d2009-09-21 17:01:09 -070040static const struct super_operations s_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070041 .statfs = simple_statfs,
42 .drop_inode = generic_delete_inode,
43};
44
45
Robert Richter25ad2912008-09-05 17:12:36 +020046ssize_t oprofilefs_str_to_user(char const *str, char __user *buf, size_t count, loff_t *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -070047{
48 return simple_read_from_buffer(buf, count, offset, str, strlen(str));
49}
50
51
52#define TMPBUFSIZE 50
53
Robert Richter25ad2912008-09-05 17:12:36 +020054ssize_t oprofilefs_ulong_to_user(unsigned long val, char __user *buf, size_t count, loff_t *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -070055{
56 char tmpbuf[TMPBUFSIZE];
57 size_t maxlen = snprintf(tmpbuf, TMPBUFSIZE, "%lu\n", val);
58 if (maxlen > TMPBUFSIZE)
59 maxlen = TMPBUFSIZE;
60 return simple_read_from_buffer(buf, count, offset, tmpbuf, maxlen);
61}
62
63
Robert Richter913050b2011-12-19 16:38:30 +010064/*
65 * Note: If oprofilefs_ulong_from_user() returns 0, then *val remains
66 * unchanged and might be uninitialized. This follows write syscall
67 * implementation when count is zero: "If count is zero ... [and if]
68 * no errors are detected, 0 will be returned without causing any
69 * other effect." (man 2 write)
70 */
Robert Richter25ad2912008-09-05 17:12:36 +020071int oprofilefs_ulong_from_user(unsigned long *val, char const __user *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -070072{
73 char tmpbuf[TMPBUFSIZE];
Jiri Kosina4dfc8962007-03-28 18:12:34 +020074 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -070075
76 if (!count)
77 return 0;
78
79 if (count > TMPBUFSIZE - 1)
80 return -EINVAL;
81
82 memset(tmpbuf, 0x0, TMPBUFSIZE);
83
84 if (copy_from_user(tmpbuf, buf, count))
85 return -EFAULT;
86
Thomas Gleixner2d21a292009-07-25 16:18:34 +020087 raw_spin_lock_irqsave(&oprofilefs_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 *val = simple_strtoul(tmpbuf, NULL, 0);
Thomas Gleixner2d21a292009-07-25 16:18:34 +020089 raw_spin_unlock_irqrestore(&oprofilefs_lock, flags);
Robert Richter913050b2011-12-19 16:38:30 +010090 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -070091}
92
93
Robert Richter25ad2912008-09-05 17:12:36 +020094static ssize_t ulong_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -070095{
Robert Richter25ad2912008-09-05 17:12:36 +020096 unsigned long *val = file->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 return oprofilefs_ulong_to_user(*val, buf, count, offset);
98}
99
100
Robert Richter25ad2912008-09-05 17:12:36 +0200101static ssize_t ulong_write_file(struct file *file, char const __user *buf, size_t count, loff_t *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102{
Robert Richter7df01d92010-10-04 21:09:36 +0200103 unsigned long value;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104 int retval;
105
106 if (*offset)
107 return -EINVAL;
108
Robert Richter7df01d92010-10-04 21:09:36 +0200109 retval = oprofilefs_ulong_from_user(&value, buf, count);
Robert Richter913050b2011-12-19 16:38:30 +0100110 if (retval <= 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111 return retval;
Robert Richter7df01d92010-10-04 21:09:36 +0200112
113 retval = oprofile_set_ulong(file->private_data, value);
114 if (retval)
115 return retval;
116
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 return count;
118}
119
120
Arjan van de Vend54b1fd2007-02-12 00:55:34 -0800121static const struct file_operations ulong_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 .read = ulong_read_file,
123 .write = ulong_write_file,
Stephen Boyd234e3402012-04-05 14:25:11 -0700124 .open = simple_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200125 .llseek = default_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126};
127
128
Arjan van de Vend54b1fd2007-02-12 00:55:34 -0800129static const struct file_operations ulong_ro_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 .read = ulong_read_file,
Stephen Boyd234e3402012-04-05 14:25:11 -0700131 .open = simple_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200132 .llseek = default_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133};
134
135
Al Viro6af4ea02013-07-19 16:10:36 +0400136static int __oprofilefs_create_file(struct dentry *root, char const *name,
137 const struct file_operations *fops, int perm, void *priv)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138{
Robert Richter25ad2912008-09-05 17:12:36 +0200139 struct dentry *dentry;
140 struct inode *inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141
Al Viroa7498962018-03-10 16:40:33 -0500142 if (!root)
143 return -ENOMEM;
144
Al Viro59551022016-01-22 15:40:57 -0500145 inode_lock(d_inode(root));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 dentry = d_alloc_name(root, name);
Al Viro3f3834c2013-01-28 14:42:42 -0500147 if (!dentry) {
Al Viro59551022016-01-22 15:40:57 -0500148 inode_unlock(d_inode(root));
Robert Richter4fdaa7b2010-08-19 10:50:26 +0200149 return -ENOMEM;
Al Viro3f3834c2013-01-28 14:42:42 -0500150 }
Al Viro6af4ea02013-07-19 16:10:36 +0400151 inode = oprofilefs_get_inode(root->d_sb, S_IFREG | perm);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152 if (!inode) {
153 dput(dentry);
Al Viro59551022016-01-22 15:40:57 -0500154 inode_unlock(d_inode(root));
Robert Richter4fdaa7b2010-08-19 10:50:26 +0200155 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 }
157 inode->i_fop = fops;
Al Viro3f3834c2013-01-28 14:42:42 -0500158 inode->i_private = priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 d_add(dentry, inode);
Al Viro59551022016-01-22 15:40:57 -0500160 inode_unlock(d_inode(root));
Robert Richter4fdaa7b2010-08-19 10:50:26 +0200161 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162}
163
164
Al Viro6af4ea02013-07-19 16:10:36 +0400165int oprofilefs_create_ulong(struct dentry *root,
Robert Richter25ad2912008-09-05 17:12:36 +0200166 char const *name, unsigned long *val)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167{
Al Viro6af4ea02013-07-19 16:10:36 +0400168 return __oprofilefs_create_file(root, name,
Robert Richter4fdaa7b2010-08-19 10:50:26 +0200169 &ulong_fops, 0644, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170}
171
172
Al Viro6af4ea02013-07-19 16:10:36 +0400173int oprofilefs_create_ro_ulong(struct dentry *root,
Robert Richter25ad2912008-09-05 17:12:36 +0200174 char const *name, unsigned long *val)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175{
Al Viro6af4ea02013-07-19 16:10:36 +0400176 return __oprofilefs_create_file(root, name,
Robert Richter4fdaa7b2010-08-19 10:50:26 +0200177 &ulong_ro_fops, 0444, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178}
179
180
Robert Richter25ad2912008-09-05 17:12:36 +0200181static ssize_t atomic_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182{
Robert Richter25ad2912008-09-05 17:12:36 +0200183 atomic_t *val = file->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 return oprofilefs_ulong_to_user(atomic_read(val), buf, count, offset);
185}
Robert Richter6a180372008-10-16 15:01:40 +0200186
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187
Arjan van de Vend54b1fd2007-02-12 00:55:34 -0800188static const struct file_operations atomic_ro_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 .read = atomic_read_file,
Stephen Boyd234e3402012-04-05 14:25:11 -0700190 .open = simple_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200191 .llseek = default_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192};
Robert Richter6a180372008-10-16 15:01:40 +0200193
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194
Al Viro6af4ea02013-07-19 16:10:36 +0400195int oprofilefs_create_ro_atomic(struct dentry *root,
Robert Richter25ad2912008-09-05 17:12:36 +0200196 char const *name, atomic_t *val)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197{
Al Viro6af4ea02013-07-19 16:10:36 +0400198 return __oprofilefs_create_file(root, name,
Robert Richter4fdaa7b2010-08-19 10:50:26 +0200199 &atomic_ro_fops, 0444, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200}
201
Robert Richter6a180372008-10-16 15:01:40 +0200202
Al Viro6af4ea02013-07-19 16:10:36 +0400203int oprofilefs_create_file(struct dentry *root,
Robert Richter25ad2912008-09-05 17:12:36 +0200204 char const *name, const struct file_operations *fops)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205{
Al Viro6af4ea02013-07-19 16:10:36 +0400206 return __oprofilefs_create_file(root, name, fops, 0644, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207}
208
209
Al Viro6af4ea02013-07-19 16:10:36 +0400210int oprofilefs_create_file_perm(struct dentry *root,
Robert Richter25ad2912008-09-05 17:12:36 +0200211 char const *name, const struct file_operations *fops, int perm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212{
Al Viro6af4ea02013-07-19 16:10:36 +0400213 return __oprofilefs_create_file(root, name, fops, perm, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214}
215
216
Al Viroecde2822013-07-19 15:58:27 +0400217struct dentry *oprofilefs_mkdir(struct dentry *parent, char const *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218{
Robert Richter25ad2912008-09-05 17:12:36 +0200219 struct dentry *dentry;
220 struct inode *inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221
Al Viro59551022016-01-22 15:40:57 -0500222 inode_lock(d_inode(parent));
Al Viroecde2822013-07-19 15:58:27 +0400223 dentry = d_alloc_name(parent, name);
Al Viro3f3834c2013-01-28 14:42:42 -0500224 if (!dentry) {
Al Viro59551022016-01-22 15:40:57 -0500225 inode_unlock(d_inode(parent));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 return NULL;
Al Viro3f3834c2013-01-28 14:42:42 -0500227 }
Al Viroecde2822013-07-19 15:58:27 +0400228 inode = oprofilefs_get_inode(parent->d_sb, S_IFDIR | 0755);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 if (!inode) {
230 dput(dentry);
Al Viro59551022016-01-22 15:40:57 -0500231 inode_unlock(d_inode(parent));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 return NULL;
233 }
234 inode->i_op = &simple_dir_inode_operations;
235 inode->i_fop = &simple_dir_operations;
236 d_add(dentry, inode);
Al Viro59551022016-01-22 15:40:57 -0500237 inode_unlock(d_inode(parent));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 return dentry;
239}
240
241
David Howellsc6a2c722019-03-25 16:38:30 +0000242static int oprofilefs_fill_super(struct super_block *sb, struct fs_context *fc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243{
Robert Richter25ad2912008-09-05 17:12:36 +0200244 struct inode *root_inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245
Kirill A. Shutemov09cbfea2016-04-01 15:29:47 +0300246 sb->s_blocksize = PAGE_SIZE;
247 sb->s_blocksize_bits = PAGE_SHIFT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 sb->s_magic = OPROFILEFS_MAGIC;
249 sb->s_op = &s_ops;
250 sb->s_time_gran = 1;
251
252 root_inode = oprofilefs_get_inode(sb, S_IFDIR | 0755);
253 if (!root_inode)
254 return -ENOMEM;
255 root_inode->i_op = &simple_dir_inode_operations;
256 root_inode->i_fop = &simple_dir_operations;
Al Viro318ceed2012-02-12 22:08:01 -0500257 sb->s_root = d_make_root(root_inode);
258 if (!sb->s_root)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260
Al Viroa9e599e2013-07-19 15:47:29 +0400261 oprofile_create_files(sb->s_root);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262
263 // FIXME: verify kill_litter_super removes our dentries
264 return 0;
265}
266
David Howellsc6a2c722019-03-25 16:38:30 +0000267static int oprofilefs_get_tree(struct fs_context *fc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268{
David Howellsc6a2c722019-03-25 16:38:30 +0000269 return get_tree_single(fc, oprofilefs_fill_super);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270}
271
David Howellsc6a2c722019-03-25 16:38:30 +0000272static const struct fs_context_operations oprofilefs_context_ops = {
273 .get_tree = oprofilefs_get_tree,
274};
275
276static int oprofilefs_init_fs_context(struct fs_context *fc)
277{
278 fc->ops = &oprofilefs_context_ops;
279 return 0;
280}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281
282static struct file_system_type oprofilefs_type = {
283 .owner = THIS_MODULE,
284 .name = "oprofilefs",
David Howellsc6a2c722019-03-25 16:38:30 +0000285 .init_fs_context = oprofilefs_init_fs_context,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 .kill_sb = kill_litter_super,
287};
Eric W. Biederman7f78e032013-03-02 19:39:14 -0800288MODULE_ALIAS_FS("oprofilefs");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289
290
291int __init oprofilefs_register(void)
292{
293 return register_filesystem(&oprofilefs_type);
294}
295
296
297void __exit oprofilefs_unregister(void)
298{
299 unregister_filesystem(&oprofilefs_type);
300}