patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / drivers / oprofile / oprofilefs.c
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 spinlock_t oprofilefs_lock = SPIN_LOCK_UNLOCKED;
25
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_uid = 0;
33                 inode->i_gid = 0;
34                 inode->i_blksize = PAGE_CACHE_SIZE;
35                 inode->i_blocks = 0;
36                 inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
37         }
38         return inode;
39 }
40
41
42 static struct super_operations s_ops = {
43         .statfs         = simple_statfs,
44         .drop_inode     = generic_delete_inode,
45 };
46
47
48 ssize_t oprofilefs_str_to_user(char const * str, char __user * buf, size_t count, loff_t * offset)
49 {
50         size_t len = strlen(str);
51
52         if (!count)
53                 return 0;
54
55         if (*offset > len)
56                 return 0;
57
58         if (count > len - *offset)
59                 count = len - *offset;
60
61         if (copy_to_user(buf, str + *offset, count))
62                 return -EFAULT;
63
64         *offset += count;
65
66         return count;
67 }
68
69
70 #define TMPBUFSIZE 50
71
72 ssize_t oprofilefs_ulong_to_user(unsigned long val, char __user * buf, size_t count, loff_t * offset)
73 {
74         char tmpbuf[TMPBUFSIZE];
75         size_t maxlen;
76
77         if (!count)
78                 return 0;
79
80         spin_lock(&oprofilefs_lock);
81         maxlen = snprintf(tmpbuf, TMPBUFSIZE, "%lu\n", val);
82         spin_unlock(&oprofilefs_lock);
83         if (maxlen > TMPBUFSIZE)
84                 maxlen = TMPBUFSIZE;
85
86         if (*offset > maxlen)
87                 return 0;
88
89         if (count > maxlen - *offset)
90                 count = maxlen - *offset;
91
92         if (copy_to_user(buf, tmpbuf + *offset, count))
93                 return -EFAULT;
94
95         *offset += count;
96
97         return count;
98 }
99
100
101 int oprofilefs_ulong_from_user(unsigned long * val, char const __user * buf, size_t count)
102 {
103         char tmpbuf[TMPBUFSIZE];
104
105         if (!count)
106                 return 0;
107
108         if (count > TMPBUFSIZE - 1)
109                 return -EINVAL;
110
111         memset(tmpbuf, 0x0, TMPBUFSIZE);
112
113         if (copy_from_user(tmpbuf, buf, count))
114                 return -EFAULT;
115
116         spin_lock(&oprofilefs_lock);
117         *val = simple_strtoul(tmpbuf, NULL, 0);
118         spin_unlock(&oprofilefs_lock);
119         return 0;
120 }
121
122
123 static ssize_t ulong_read_file(struct file * file, char __user * buf, size_t count, loff_t * offset)
124 {
125         unsigned long * val = file->private_data;
126         return oprofilefs_ulong_to_user(*val, buf, count, offset);
127 }
128
129
130 static ssize_t ulong_write_file(struct file * file, char const __user * buf, size_t count, loff_t * offset)
131 {
132         unsigned long * value = file->private_data;
133         int retval;
134
135         if (*offset)
136                 return -EINVAL;
137
138         retval = oprofilefs_ulong_from_user(value, buf, count);
139
140         if (retval)
141                 return retval;
142         return count;
143 }
144
145
146 static int default_open(struct inode * inode, struct file * filp)
147 {
148         if (inode->u.generic_ip)
149                 filp->private_data = inode->u.generic_ip;
150         return 0;
151 }
152
153
154 static struct file_operations ulong_fops = {
155         .read           = ulong_read_file,
156         .write          = ulong_write_file,
157         .open           = default_open,
158 };
159
160
161 static struct file_operations ulong_ro_fops = {
162         .read           = ulong_read_file,
163         .open           = default_open,
164 };
165
166
167 static struct dentry * __oprofilefs_create_file(struct super_block * sb,
168         struct dentry * root, char const * name, struct file_operations * fops)
169 {
170         struct dentry * dentry;
171         struct inode * inode;
172         struct qstr qname;
173         qname.name = name;
174         qname.len = strlen(name);
175         qname.hash = full_name_hash(qname.name, qname.len);
176         dentry = d_alloc(root, &qname);
177         if (!dentry)
178                 return 0;
179         inode = oprofilefs_get_inode(sb, S_IFREG | 0644);
180         if (!inode) {
181                 dput(dentry);
182                 return 0;
183         }
184         inode->i_fop = fops;
185         d_add(dentry, inode);
186         return dentry;
187 }
188
189
190 int oprofilefs_create_ulong(struct super_block * sb, struct dentry * root,
191         char const * name, unsigned long * val)
192 {
193         struct dentry * d = __oprofilefs_create_file(sb, root, name, &ulong_fops);
194         if (!d)
195                 return -EFAULT;
196
197         d->d_inode->u.generic_ip = val;
198         return 0;
199 }
200
201
202 int oprofilefs_create_ro_ulong(struct super_block * sb, struct dentry * root,
203         char const * name, unsigned long * val)
204 {
205         struct dentry * d = __oprofilefs_create_file(sb, root, name, &ulong_ro_fops);
206         if (!d)
207                 return -EFAULT;
208
209         d->d_inode->u.generic_ip = val;
210         return 0;
211 }
212
213
214 static ssize_t atomic_read_file(struct file * file, char __user * buf, size_t count, loff_t * offset)
215 {
216         atomic_t * val = file->private_data;
217         return oprofilefs_ulong_to_user(atomic_read(val), buf, count, offset);
218 }
219  
220
221 static struct file_operations atomic_ro_fops = {
222         .read           = atomic_read_file,
223         .open           = default_open,
224 };
225  
226
227 int oprofilefs_create_ro_atomic(struct super_block * sb, struct dentry * root,
228         char const * name, atomic_t * val)
229 {
230         struct dentry * d = __oprofilefs_create_file(sb, root, name, &atomic_ro_fops);
231         if (!d)
232                 return -EFAULT;
233
234         d->d_inode->u.generic_ip = val;
235         return 0;
236 }
237
238  
239 int oprofilefs_create_file(struct super_block * sb, struct dentry * root,
240         char const * name, struct file_operations * fops)
241 {
242         if (!__oprofilefs_create_file(sb, root, name, fops))
243                 return -EFAULT;
244         return 0;
245 }
246
247
248 struct dentry * oprofilefs_mkdir(struct super_block * sb,
249         struct dentry * root, char const * name)
250 {
251         struct dentry * dentry;
252         struct inode * inode;
253         struct qstr qname;
254         qname.name = name;
255         qname.len = strlen(name);
256         qname.hash = full_name_hash(qname.name, qname.len);
257         dentry = d_alloc(root, &qname);
258         if (!dentry)
259                 return 0;
260         inode = oprofilefs_get_inode(sb, S_IFDIR | 0755);
261         if (!inode) {
262                 dput(dentry);
263                 return 0;
264         }
265         inode->i_op = &simple_dir_inode_operations;
266         inode->i_fop = &simple_dir_operations;
267         d_add(dentry, inode);
268         return dentry;
269 }
270
271
272 static int oprofilefs_fill_super(struct super_block * sb, void * data, int silent)
273 {
274         struct inode * root_inode;
275         struct dentry * root_dentry;
276
277         sb->s_blocksize = PAGE_CACHE_SIZE;
278         sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
279         sb->s_magic = OPROFILEFS_MAGIC;
280         sb->s_op = &s_ops;
281
282         root_inode = oprofilefs_get_inode(sb, S_IFDIR | 0755);
283         if (!root_inode)
284                 return -ENOMEM;
285         root_inode->i_op = &simple_dir_inode_operations;
286         root_inode->i_fop = &simple_dir_operations;
287         root_dentry = d_alloc_root(root_inode);
288         if (!root_dentry) {
289                 iput(root_inode);
290                 return -ENOMEM;
291         }
292
293         sb->s_root = root_dentry;
294
295         oprofile_create_files(sb, root_dentry);
296
297         // FIXME: verify kill_litter_super removes our dentries
298         return 0;
299 }
300
301
302 static struct super_block *oprofilefs_get_sb(struct file_system_type *fs_type,
303         int flags, const char *dev_name, void *data)
304 {
305         return get_sb_single(fs_type, flags, data, oprofilefs_fill_super);
306 }
307
308
309 static struct file_system_type oprofilefs_type = {
310         .owner          = THIS_MODULE,
311         .name           = "oprofilefs",
312         .get_sb         = oprofilefs_get_sb,
313         .kill_sb        = kill_litter_super,
314 };
315
316
317 int __init oprofilefs_register(void)
318 {
319         return register_filesystem(&oprofilefs_type);
320 }
321
322
323 void __exit oprofilefs_unregister(void)
324 {
325         unregister_filesystem(&oprofilefs_type);
326 }