Merge to Fedora kernel-2.6.7-1.492
[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         int perm)
170 {
171         struct dentry * dentry;
172         struct inode * inode;
173         struct qstr qname;
174         qname.name = name;
175         qname.len = strlen(name);
176         qname.hash = full_name_hash(qname.name, qname.len);
177         dentry = d_alloc(root, &qname);
178         if (!dentry)
179                 return NULL;
180         inode = oprofilefs_get_inode(sb, S_IFREG | perm);
181         if (!inode) {
182                 dput(dentry);
183                 return NULL;
184         }
185         inode->i_fop = fops;
186         d_add(dentry, inode);
187         return dentry;
188 }
189
190
191 int oprofilefs_create_ulong(struct super_block * sb, struct dentry * root,
192         char const * name, unsigned long * val)
193 {
194         struct dentry * d = __oprofilefs_create_file(sb, root, name,
195                                                      &ulong_fops, 0644);
196         if (!d)
197                 return -EFAULT;
198
199         d->d_inode->u.generic_ip = val;
200         return 0;
201 }
202
203
204 int oprofilefs_create_ro_ulong(struct super_block * sb, struct dentry * root,
205         char const * name, unsigned long * val)
206 {
207         struct dentry * d = __oprofilefs_create_file(sb, root, name,
208                                                      &ulong_ro_fops, 0444);
209         if (!d)
210                 return -EFAULT;
211
212         d->d_inode->u.generic_ip = val;
213         return 0;
214 }
215
216
217 static ssize_t atomic_read_file(struct file * file, char __user * buf, size_t count, loff_t * offset)
218 {
219         atomic_t * val = file->private_data;
220         return oprofilefs_ulong_to_user(atomic_read(val), buf, count, offset);
221 }
222  
223
224 static struct file_operations atomic_ro_fops = {
225         .read           = atomic_read_file,
226         .open           = default_open,
227 };
228  
229
230 int oprofilefs_create_ro_atomic(struct super_block * sb, struct dentry * root,
231         char const * name, atomic_t * val)
232 {
233         struct dentry * d = __oprofilefs_create_file(sb, root, name,
234                                                      &atomic_ro_fops, 0444);
235         if (!d)
236                 return -EFAULT;
237
238         d->d_inode->u.generic_ip = val;
239         return 0;
240 }
241
242  
243 int oprofilefs_create_file(struct super_block * sb, struct dentry * root,
244         char const * name, struct file_operations * fops)
245 {
246         if (!__oprofilefs_create_file(sb, root, name, fops, 0644))
247                 return -EFAULT;
248         return 0;
249 }
250
251
252 int oprofilefs_create_file_perm(struct super_block * sb, struct dentry * root,
253         char const * name, struct file_operations * fops, int perm)
254 {
255         if (!__oprofilefs_create_file(sb, root, name, fops, perm))
256                 return -EFAULT;
257         return 0;
258 }
259
260
261 struct dentry * oprofilefs_mkdir(struct super_block * sb,
262         struct dentry * root, char const * name)
263 {
264         struct dentry * dentry;
265         struct inode * inode;
266         struct qstr qname;
267         qname.name = name;
268         qname.len = strlen(name);
269         qname.hash = full_name_hash(qname.name, qname.len);
270         dentry = d_alloc(root, &qname);
271         if (!dentry)
272                 return NULL;
273         inode = oprofilefs_get_inode(sb, S_IFDIR | 0755);
274         if (!inode) {
275                 dput(dentry);
276                 return NULL;
277         }
278         inode->i_op = &simple_dir_inode_operations;
279         inode->i_fop = &simple_dir_operations;
280         d_add(dentry, inode);
281         return dentry;
282 }
283
284
285 static int oprofilefs_fill_super(struct super_block * sb, void * data, int silent)
286 {
287         struct inode * root_inode;
288         struct dentry * root_dentry;
289
290         sb->s_blocksize = PAGE_CACHE_SIZE;
291         sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
292         sb->s_magic = OPROFILEFS_MAGIC;
293         sb->s_op = &s_ops;
294
295         root_inode = oprofilefs_get_inode(sb, S_IFDIR | 0755);
296         if (!root_inode)
297                 return -ENOMEM;
298         root_inode->i_op = &simple_dir_inode_operations;
299         root_inode->i_fop = &simple_dir_operations;
300         root_dentry = d_alloc_root(root_inode);
301         if (!root_dentry) {
302                 iput(root_inode);
303                 return -ENOMEM;
304         }
305
306         sb->s_root = root_dentry;
307
308         oprofile_create_files(sb, root_dentry);
309
310         // FIXME: verify kill_litter_super removes our dentries
311         return 0;
312 }
313
314
315 static struct super_block *oprofilefs_get_sb(struct file_system_type *fs_type,
316         int flags, const char *dev_name, void *data)
317 {
318         return get_sb_single(fs_type, flags, data, oprofilefs_fill_super);
319 }
320
321
322 static struct file_system_type oprofilefs_type = {
323         .owner          = THIS_MODULE,
324         .name           = "oprofilefs",
325         .get_sb         = oprofilefs_get_sb,
326         .kill_sb        = kill_litter_super,
327 };
328
329
330 int __init oprofilefs_register(void)
331 {
332         return register_filesystem(&oprofilefs_type);
333 }
334
335
336 void __exit oprofilefs_unregister(void)
337 {
338         unregister_filesystem(&oprofilefs_type);
339 }