• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
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>
17 #include <linux/pagemap.h>
18 #include <asm/uaccess.h>
19 
20 #include "oprof.h"
21 
22 #define OPROFILEFS_MAGIC 0x6f70726f
23 
24 DEFINE_SPINLOCK(oprofilefs_lock);
25 
oprofilefs_get_inode(struct super_block * sb,int mode)26 static struct inode *oprofilefs_get_inode(struct super_block *sb, int mode)
27 {
28 	struct inode *inode = new_inode(sb);
29 
30 	if (inode) {
31 		inode->i_mode = mode;
32 		inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
33 	}
34 	return inode;
35 }
36 
37 
38 static struct super_operations s_ops = {
39 	.statfs		= simple_statfs,
40 	.drop_inode 	= generic_delete_inode,
41 };
42 
43 
oprofilefs_str_to_user(char const * str,char __user * buf,size_t count,loff_t * offset)44 ssize_t oprofilefs_str_to_user(char const *str, char __user *buf, size_t count, loff_t *offset)
45 {
46 	return simple_read_from_buffer(buf, count, offset, str, strlen(str));
47 }
48 
49 
50 #define TMPBUFSIZE 50
51 
oprofilefs_ulong_to_user(unsigned long val,char __user * buf,size_t count,loff_t * offset)52 ssize_t oprofilefs_ulong_to_user(unsigned long val, char __user *buf, size_t count, loff_t *offset)
53 {
54 	char tmpbuf[TMPBUFSIZE];
55 	size_t maxlen = snprintf(tmpbuf, TMPBUFSIZE, "%lu\n", val);
56 	if (maxlen > TMPBUFSIZE)
57 		maxlen = TMPBUFSIZE;
58 	return simple_read_from_buffer(buf, count, offset, tmpbuf, maxlen);
59 }
60 
61 
oprofilefs_ulong_from_user(unsigned long * val,char const __user * buf,size_t count)62 int oprofilefs_ulong_from_user(unsigned long *val, char const __user *buf, size_t count)
63 {
64 	char tmpbuf[TMPBUFSIZE];
65 	unsigned long flags;
66 
67 	if (!count)
68 		return 0;
69 
70 	if (count > TMPBUFSIZE - 1)
71 		return -EINVAL;
72 
73 	memset(tmpbuf, 0x0, TMPBUFSIZE);
74 
75 	if (copy_from_user(tmpbuf, buf, count))
76 		return -EFAULT;
77 
78 	spin_lock_irqsave(&oprofilefs_lock, flags);
79 	*val = simple_strtoul(tmpbuf, NULL, 0);
80 	spin_unlock_irqrestore(&oprofilefs_lock, flags);
81 	return 0;
82 }
83 
84 
ulong_read_file(struct file * file,char __user * buf,size_t count,loff_t * offset)85 static ssize_t ulong_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset)
86 {
87 	unsigned long *val = file->private_data;
88 	return oprofilefs_ulong_to_user(*val, buf, count, offset);
89 }
90 
91 
ulong_write_file(struct file * file,char const __user * buf,size_t count,loff_t * offset)92 static ssize_t ulong_write_file(struct file *file, char const __user *buf, size_t count, loff_t *offset)
93 {
94 	unsigned long *value = file->private_data;
95 	int retval;
96 
97 	if (*offset)
98 		return -EINVAL;
99 
100 	retval = oprofilefs_ulong_from_user(value, buf, count);
101 
102 	if (retval)
103 		return retval;
104 	return count;
105 }
106 
107 
default_open(struct inode * inode,struct file * filp)108 static int default_open(struct inode *inode, struct file *filp)
109 {
110 	if (inode->i_private)
111 		filp->private_data = inode->i_private;
112 	return 0;
113 }
114 
115 
116 static const struct file_operations ulong_fops = {
117 	.read		= ulong_read_file,
118 	.write		= ulong_write_file,
119 	.open		= default_open,
120 };
121 
122 
123 static const struct file_operations ulong_ro_fops = {
124 	.read		= ulong_read_file,
125 	.open		= default_open,
126 };
127 
128 
__oprofilefs_create_file(struct super_block * sb,struct dentry * root,char const * name,const struct file_operations * fops,int perm)129 static struct dentry *__oprofilefs_create_file(struct super_block *sb,
130 	struct dentry *root, char const *name, const struct file_operations *fops,
131 	int perm)
132 {
133 	struct dentry *dentry;
134 	struct inode *inode;
135 
136 	dentry = d_alloc_name(root, name);
137 	if (!dentry)
138 		return NULL;
139 	inode = oprofilefs_get_inode(sb, S_IFREG | perm);
140 	if (!inode) {
141 		dput(dentry);
142 		return NULL;
143 	}
144 	inode->i_fop = fops;
145 	d_add(dentry, inode);
146 	return dentry;
147 }
148 
149 
oprofilefs_create_ulong(struct super_block * sb,struct dentry * root,char const * name,unsigned long * val)150 int oprofilefs_create_ulong(struct super_block *sb, struct dentry *root,
151 	char const *name, unsigned long *val)
152 {
153 	struct dentry *d = __oprofilefs_create_file(sb, root, name,
154 						     &ulong_fops, 0644);
155 	if (!d)
156 		return -EFAULT;
157 
158 	d->d_inode->i_private = val;
159 	return 0;
160 }
161 
162 
oprofilefs_create_ro_ulong(struct super_block * sb,struct dentry * root,char const * name,unsigned long * val)163 int oprofilefs_create_ro_ulong(struct super_block *sb, struct dentry *root,
164 	char const *name, unsigned long *val)
165 {
166 	struct dentry *d = __oprofilefs_create_file(sb, root, name,
167 						     &ulong_ro_fops, 0444);
168 	if (!d)
169 		return -EFAULT;
170 
171 	d->d_inode->i_private = val;
172 	return 0;
173 }
174 
175 
atomic_read_file(struct file * file,char __user * buf,size_t count,loff_t * offset)176 static ssize_t atomic_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset)
177 {
178 	atomic_t *val = file->private_data;
179 	return oprofilefs_ulong_to_user(atomic_read(val), buf, count, offset);
180 }
181 
182 
183 static const struct file_operations atomic_ro_fops = {
184 	.read		= atomic_read_file,
185 	.open		= default_open,
186 };
187 
188 
oprofilefs_create_ro_atomic(struct super_block * sb,struct dentry * root,char const * name,atomic_t * val)189 int oprofilefs_create_ro_atomic(struct super_block *sb, struct dentry *root,
190 	char const *name, atomic_t *val)
191 {
192 	struct dentry *d = __oprofilefs_create_file(sb, root, name,
193 						     &atomic_ro_fops, 0444);
194 	if (!d)
195 		return -EFAULT;
196 
197 	d->d_inode->i_private = val;
198 	return 0;
199 }
200 
201 
oprofilefs_create_file(struct super_block * sb,struct dentry * root,char const * name,const struct file_operations * fops)202 int oprofilefs_create_file(struct super_block *sb, struct dentry *root,
203 	char const *name, const struct file_operations *fops)
204 {
205 	if (!__oprofilefs_create_file(sb, root, name, fops, 0644))
206 		return -EFAULT;
207 	return 0;
208 }
209 
210 
oprofilefs_create_file_perm(struct super_block * sb,struct dentry * root,char const * name,const struct file_operations * fops,int perm)211 int oprofilefs_create_file_perm(struct super_block *sb, struct dentry *root,
212 	char const *name, const struct file_operations *fops, int perm)
213 {
214 	if (!__oprofilefs_create_file(sb, root, name, fops, perm))
215 		return -EFAULT;
216 	return 0;
217 }
218 
219 
oprofilefs_mkdir(struct super_block * sb,struct dentry * root,char const * name)220 struct dentry *oprofilefs_mkdir(struct super_block *sb,
221 	struct dentry *root, char const *name)
222 {
223 	struct dentry *dentry;
224 	struct inode *inode;
225 
226 	dentry = d_alloc_name(root, name);
227 	if (!dentry)
228 		return NULL;
229 	inode = oprofilefs_get_inode(sb, S_IFDIR | 0755);
230 	if (!inode) {
231 		dput(dentry);
232 		return NULL;
233 	}
234 	inode->i_op = &simple_dir_inode_operations;
235 	inode->i_fop = &simple_dir_operations;
236 	d_add(dentry, inode);
237 	return dentry;
238 }
239 
240 
oprofilefs_fill_super(struct super_block * sb,void * data,int silent)241 static int oprofilefs_fill_super(struct super_block *sb, void *data, int silent)
242 {
243 	struct inode *root_inode;
244 	struct dentry *root_dentry;
245 
246 	sb->s_blocksize = PAGE_CACHE_SIZE;
247 	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
248 	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;
257 	root_dentry = d_alloc_root(root_inode);
258 	if (!root_dentry) {
259 		iput(root_inode);
260 		return -ENOMEM;
261 	}
262 
263 	sb->s_root = root_dentry;
264 
265 	oprofile_create_files(sb, root_dentry);
266 
267 	// FIXME: verify kill_litter_super removes our dentries
268 	return 0;
269 }
270 
271 
oprofilefs_get_sb(struct file_system_type * fs_type,int flags,const char * dev_name,void * data,struct vfsmount * mnt)272 static int oprofilefs_get_sb(struct file_system_type *fs_type,
273 	int flags, const char *dev_name, void *data, struct vfsmount *mnt)
274 {
275 	return get_sb_single(fs_type, flags, data, oprofilefs_fill_super, mnt);
276 }
277 
278 
279 static struct file_system_type oprofilefs_type = {
280 	.owner		= THIS_MODULE,
281 	.name		= "oprofilefs",
282 	.get_sb		= oprofilefs_get_sb,
283 	.kill_sb	= kill_litter_super,
284 };
285 
286 
oprofilefs_register(void)287 int __init oprofilefs_register(void)
288 {
289 	return register_filesystem(&oprofilefs_type);
290 }
291 
292 
oprofilefs_unregister(void)293 void __exit oprofilefs_unregister(void)
294 {
295 	unregister_filesystem(&oprofilefs_type);
296 }
297