linux/drivers/oprofile/oprofilefs.c
<<
>>
Prefs
   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/fs_context.h>
  18#include <linux/pagemap.h>
  19#include <linux/uaccess.h>
  20
  21#include "oprof.h"
  22
  23#define OPROFILEFS_MAGIC 0x6f70726f
  24
  25DEFINE_RAW_SPINLOCK(oprofilefs_lock);
  26
  27static struct inode *oprofilefs_get_inode(struct super_block *sb, int mode)
  28{
  29        struct inode *inode = new_inode(sb);
  30
  31        if (inode) {
  32                inode->i_ino = get_next_ino();
  33                inode->i_mode = mode;
  34                inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
  35        }
  36        return inode;
  37}
  38
  39
  40static const struct super_operations s_ops = {
  41        .statfs         = simple_statfs,
  42        .drop_inode     = generic_delete_inode,
  43};
  44
  45
  46ssize_t oprofilefs_str_to_user(char const *str, char __user *buf, size_t count, loff_t *offset)
  47{
  48        return simple_read_from_buffer(buf, count, offset, str, strlen(str));
  49}
  50
  51
  52#define TMPBUFSIZE 50
  53
  54ssize_t oprofilefs_ulong_to_user(unsigned long val, char __user *buf, size_t count, loff_t *offset)
  55{
  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
  64/*
  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 */
  71int oprofilefs_ulong_from_user(unsigned long *val, char const __user *buf, size_t count)
  72{
  73        char tmpbuf[TMPBUFSIZE];
  74        unsigned long flags;
  75
  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
  87        raw_spin_lock_irqsave(&oprofilefs_lock, flags);
  88        *val = simple_strtoul(tmpbuf, NULL, 0);
  89        raw_spin_unlock_irqrestore(&oprofilefs_lock, flags);
  90        return count;
  91}
  92
  93
  94static ssize_t ulong_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset)
  95{
  96        unsigned long *val = file->private_data;
  97        return oprofilefs_ulong_to_user(*val, buf, count, offset);
  98}
  99
 100
 101static ssize_t ulong_write_file(struct file *file, char const __user *buf, size_t count, loff_t *offset)
 102{
 103        unsigned long value;
 104        int retval;
 105
 106        if (*offset)
 107                return -EINVAL;
 108
 109        retval = oprofilefs_ulong_from_user(&value, buf, count);
 110        if (retval <= 0)
 111                return retval;
 112
 113        retval = oprofile_set_ulong(file->private_data, value);
 114        if (retval)
 115                return retval;
 116
 117        return count;
 118}
 119
 120
 121static const struct file_operations ulong_fops = {
 122        .read           = ulong_read_file,
 123        .write          = ulong_write_file,
 124        .open           = simple_open,
 125        .llseek         = default_llseek,
 126};
 127
 128
 129static const struct file_operations ulong_ro_fops = {
 130        .read           = ulong_read_file,
 131        .open           = simple_open,
 132        .llseek         = default_llseek,
 133};
 134
 135
 136static int __oprofilefs_create_file(struct dentry *root, char const *name,
 137        const struct file_operations *fops, int perm, void *priv)
 138{
 139        struct dentry *dentry;
 140        struct inode *inode;
 141
 142        if (!root)
 143                return -ENOMEM;
 144
 145        inode_lock(d_inode(root));
 146        dentry = d_alloc_name(root, name);
 147        if (!dentry) {
 148                inode_unlock(d_inode(root));
 149                return -ENOMEM;
 150        }
 151        inode = oprofilefs_get_inode(root->d_sb, S_IFREG | perm);
 152        if (!inode) {
 153                dput(dentry);
 154                inode_unlock(d_inode(root));
 155                return -ENOMEM;
 156        }
 157        inode->i_fop = fops;
 158        inode->i_private = priv;
 159        d_add(dentry, inode);
 160        inode_unlock(d_inode(root));
 161        return 0;
 162}
 163
 164
 165int oprofilefs_create_ulong(struct dentry *root,
 166        char const *name, unsigned long *val)
 167{
 168        return __oprofilefs_create_file(root, name,
 169                                        &ulong_fops, 0644, val);
 170}
 171
 172
 173int oprofilefs_create_ro_ulong(struct dentry *root,
 174        char const *name, unsigned long *val)
 175{
 176        return __oprofilefs_create_file(root, name,
 177                                        &ulong_ro_fops, 0444, val);
 178}
 179
 180
 181static ssize_t atomic_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset)
 182{
 183        atomic_t *val = file->private_data;
 184        return oprofilefs_ulong_to_user(atomic_read(val), buf, count, offset);
 185}
 186
 187
 188static const struct file_operations atomic_ro_fops = {
 189        .read           = atomic_read_file,
 190        .open           = simple_open,
 191        .llseek         = default_llseek,
 192};
 193
 194
 195int oprofilefs_create_ro_atomic(struct dentry *root,
 196        char const *name, atomic_t *val)
 197{
 198        return __oprofilefs_create_file(root, name,
 199                                        &atomic_ro_fops, 0444, val);
 200}
 201
 202
 203int oprofilefs_create_file(struct dentry *root,
 204        char const *name, const struct file_operations *fops)
 205{
 206        return __oprofilefs_create_file(root, name, fops, 0644, NULL);
 207}
 208
 209
 210int oprofilefs_create_file_perm(struct dentry *root,
 211        char const *name, const struct file_operations *fops, int perm)
 212{
 213        return __oprofilefs_create_file(root, name, fops, perm, NULL);
 214}
 215
 216
 217struct dentry *oprofilefs_mkdir(struct dentry *parent, char const *name)
 218{
 219        struct dentry *dentry;
 220        struct inode *inode;
 221
 222        inode_lock(d_inode(parent));
 223        dentry = d_alloc_name(parent, name);
 224        if (!dentry) {
 225                inode_unlock(d_inode(parent));
 226                return NULL;
 227        }
 228        inode = oprofilefs_get_inode(parent->d_sb, S_IFDIR | 0755);
 229        if (!inode) {
 230                dput(dentry);
 231                inode_unlock(d_inode(parent));
 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        inode_unlock(d_inode(parent));
 238        return dentry;
 239}
 240
 241
 242static int oprofilefs_fill_super(struct super_block *sb, struct fs_context *fc)
 243{
 244        struct inode *root_inode;
 245
 246        sb->s_blocksize = PAGE_SIZE;
 247        sb->s_blocksize_bits = PAGE_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        sb->s_root = d_make_root(root_inode);
 258        if (!sb->s_root)
 259                return -ENOMEM;
 260
 261        oprofile_create_files(sb->s_root);
 262
 263        // FIXME: verify kill_litter_super removes our dentries
 264        return 0;
 265}
 266
 267static int oprofilefs_get_tree(struct fs_context *fc)
 268{
 269        return get_tree_single(fc, oprofilefs_fill_super);
 270}
 271
 272static 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}
 281
 282static struct file_system_type oprofilefs_type = {
 283        .owner          = THIS_MODULE,
 284        .name           = "oprofilefs",
 285        .init_fs_context = oprofilefs_init_fs_context,
 286        .kill_sb        = kill_litter_super,
 287};
 288MODULE_ALIAS_FS("oprofilefs");
 289
 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}
 301