diff options
Diffstat (limited to 'fs/tracefs/inode.c')
| -rw-r--r-- | fs/tracefs/inode.c | 650 |
1 files changed, 650 insertions, 0 deletions
diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c new file mode 100644 index 000000000000..d92bdf3b079a --- /dev/null +++ b/fs/tracefs/inode.c | |||
| @@ -0,0 +1,650 @@ | |||
| 1 | /* | ||
| 2 | * inode.c - part of tracefs, a pseudo file system for activating tracing | ||
| 3 | * | ||
| 4 | * Based on debugfs by: Greg Kroah-Hartman <greg@kroah.com> | ||
| 5 | * | ||
| 6 | * Copyright (C) 2014 Red Hat Inc, author: Steven Rostedt <srostedt@redhat.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License version | ||
| 10 | * 2 as published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * tracefs is the file system that is used by the tracing infrastructure. | ||
| 13 | * | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/module.h> | ||
| 17 | #include <linux/fs.h> | ||
| 18 | #include <linux/mount.h> | ||
| 19 | #include <linux/kobject.h> | ||
| 20 | #include <linux/namei.h> | ||
| 21 | #include <linux/tracefs.h> | ||
| 22 | #include <linux/fsnotify.h> | ||
| 23 | #include <linux/seq_file.h> | ||
| 24 | #include <linux/parser.h> | ||
| 25 | #include <linux/magic.h> | ||
| 26 | #include <linux/slab.h> | ||
| 27 | |||
| 28 | #define TRACEFS_DEFAULT_MODE 0700 | ||
| 29 | |||
| 30 | static struct vfsmount *tracefs_mount; | ||
| 31 | static int tracefs_mount_count; | ||
| 32 | static bool tracefs_registered; | ||
| 33 | |||
| 34 | static ssize_t default_read_file(struct file *file, char __user *buf, | ||
| 35 | size_t count, loff_t *ppos) | ||
| 36 | { | ||
| 37 | return 0; | ||
| 38 | } | ||
| 39 | |||
| 40 | static ssize_t default_write_file(struct file *file, const char __user *buf, | ||
| 41 | size_t count, loff_t *ppos) | ||
| 42 | { | ||
| 43 | return count; | ||
| 44 | } | ||
| 45 | |||
| 46 | static const struct file_operations tracefs_file_operations = { | ||
| 47 | .read = default_read_file, | ||
| 48 | .write = default_write_file, | ||
| 49 | .open = simple_open, | ||
| 50 | .llseek = noop_llseek, | ||
| 51 | }; | ||
| 52 | |||
| 53 | static struct tracefs_dir_ops { | ||
| 54 | int (*mkdir)(const char *name); | ||
| 55 | int (*rmdir)(const char *name); | ||
| 56 | } tracefs_ops; | ||
| 57 | |||
| 58 | static char *get_dname(struct dentry *dentry) | ||
| 59 | { | ||
| 60 | const char *dname; | ||
| 61 | char *name; | ||
| 62 | int len = dentry->d_name.len; | ||
| 63 | |||
| 64 | dname = dentry->d_name.name; | ||
| 65 | name = kmalloc(len + 1, GFP_KERNEL); | ||
| 66 | if (!name) | ||
| 67 | return NULL; | ||
| 68 | memcpy(name, dname, len); | ||
| 69 | name[len] = 0; | ||
| 70 | return name; | ||
| 71 | } | ||
| 72 | |||
| 73 | static int tracefs_syscall_mkdir(struct inode *inode, struct dentry *dentry, umode_t mode) | ||
| 74 | { | ||
| 75 | char *name; | ||
| 76 | int ret; | ||
| 77 | |||
| 78 | name = get_dname(dentry); | ||
| 79 | if (!name) | ||
| 80 | return -ENOMEM; | ||
| 81 | |||
| 82 | /* | ||
| 83 | * The mkdir call can call the generic functions that create | ||
| 84 | * the files within the tracefs system. It is up to the individual | ||
| 85 | * mkdir routine to handle races. | ||
| 86 | */ | ||
| 87 | mutex_unlock(&inode->i_mutex); | ||
| 88 | ret = tracefs_ops.mkdir(name); | ||
| 89 | mutex_lock(&inode->i_mutex); | ||
| 90 | |||
| 91 | kfree(name); | ||
| 92 | |||
| 93 | return ret; | ||
| 94 | } | ||
| 95 | |||
| 96 | static int tracefs_syscall_rmdir(struct inode *inode, struct dentry *dentry) | ||
| 97 | { | ||
| 98 | char *name; | ||
| 99 | int ret; | ||
| 100 | |||
| 101 | name = get_dname(dentry); | ||
| 102 | if (!name) | ||
| 103 | return -ENOMEM; | ||
| 104 | |||
| 105 | /* | ||
| 106 | * The rmdir call can call the generic functions that create | ||
| 107 | * the files within the tracefs system. It is up to the individual | ||
| 108 | * rmdir routine to handle races. | ||
| 109 | * This time we need to unlock not only the parent (inode) but | ||
| 110 | * also the directory that is being deleted. | ||
| 111 | */ | ||
| 112 | mutex_unlock(&inode->i_mutex); | ||
| 113 | mutex_unlock(&dentry->d_inode->i_mutex); | ||
| 114 | |||
| 115 | ret = tracefs_ops.rmdir(name); | ||
| 116 | |||
| 117 | mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT); | ||
| 118 | mutex_lock(&dentry->d_inode->i_mutex); | ||
| 119 | |||
| 120 | kfree(name); | ||
| 121 | |||
| 122 | return ret; | ||
| 123 | } | ||
| 124 | |||
| 125 | static const struct inode_operations tracefs_dir_inode_operations = { | ||
| 126 | .lookup = simple_lookup, | ||
| 127 | .mkdir = tracefs_syscall_mkdir, | ||
| 128 | .rmdir = tracefs_syscall_rmdir, | ||
| 129 | }; | ||
| 130 | |||
| 131 | static struct inode *tracefs_get_inode(struct super_block *sb) | ||
| 132 | { | ||
| 133 | struct inode *inode = new_inode(sb); | ||
| 134 | if (inode) { | ||
| 135 | inode->i_ino = get_next_ino(); | ||
| 136 | inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; | ||
| 137 | } | ||
| 138 | return inode; | ||
| 139 | } | ||
| 140 | |||
| 141 | struct tracefs_mount_opts { | ||
| 142 | kuid_t uid; | ||
| 143 | kgid_t gid; | ||
| 144 | umode_t mode; | ||
| 145 | }; | ||
| 146 | |||
| 147 | enum { | ||
| 148 | Opt_uid, | ||
| 149 | Opt_gid, | ||
| 150 | Opt_mode, | ||
| 151 | Opt_err | ||
| 152 | }; | ||
| 153 | |||
| 154 | static const match_table_t tokens = { | ||
| 155 | {Opt_uid, "uid=%u"}, | ||
| 156 | {Opt_gid, "gid=%u"}, | ||
| 157 | {Opt_mode, "mode=%o"}, | ||
| 158 | {Opt_err, NULL} | ||
| 159 | }; | ||
| 160 | |||
| 161 | struct tracefs_fs_info { | ||
| 162 | struct tracefs_mount_opts mount_opts; | ||
| 163 | }; | ||
| 164 | |||
| 165 | static int tracefs_parse_options(char *data, struct tracefs_mount_opts *opts) | ||
| 166 | { | ||
| 167 | substring_t args[MAX_OPT_ARGS]; | ||
| 168 | int option; | ||
| 169 | int token; | ||
| 170 | kuid_t uid; | ||
| 171 | kgid_t gid; | ||
| 172 | char *p; | ||
| 173 | |||
| 174 | opts->mode = TRACEFS_DEFAULT_MODE; | ||
| 175 | |||
| 176 | while ((p = strsep(&data, ",")) != NULL) { | ||
| 177 | if (!*p) | ||
| 178 | continue; | ||
| 179 | |||
| 180 | token = match_token(p, tokens, args); | ||
| 181 | switch (token) { | ||
| 182 | case Opt_uid: | ||
| 183 | if (match_int(&args[0], &option)) | ||
| 184 | return -EINVAL; | ||
| 185 | uid = make_kuid(current_user_ns(), option); | ||
| 186 | if (!uid_valid(uid)) | ||
| 187 | return -EINVAL; | ||
| 188 | opts->uid = uid; | ||
| 189 | break; | ||
| 190 | case Opt_gid: | ||
| 191 | if (match_int(&args[0], &option)) | ||
| 192 | return -EINVAL; | ||
| 193 | gid = make_kgid(current_user_ns(), option); | ||
| 194 | if (!gid_valid(gid)) | ||
| 195 | return -EINVAL; | ||
| 196 | opts->gid = gid; | ||
| 197 | break; | ||
| 198 | case Opt_mode: | ||
| 199 | if (match_octal(&args[0], &option)) | ||
| 200 | return -EINVAL; | ||
| 201 | opts->mode = option & S_IALLUGO; | ||
| 202 | break; | ||
| 203 | /* | ||
| 204 | * We might like to report bad mount options here; | ||
| 205 | * but traditionally tracefs has ignored all mount options | ||
| 206 | */ | ||
| 207 | } | ||
| 208 | } | ||
| 209 | |||
| 210 | return 0; | ||
| 211 | } | ||
| 212 | |||
| 213 | static int tracefs_apply_options(struct super_block *sb) | ||
| 214 | { | ||
| 215 | struct tracefs_fs_info *fsi = sb->s_fs_info; | ||
| 216 | struct inode *inode = sb->s_root->d_inode; | ||
| 217 | struct tracefs_mount_opts *opts = &fsi->mount_opts; | ||
| 218 | |||
| 219 | inode->i_mode &= ~S_IALLUGO; | ||
| 220 | inode->i_mode |= opts->mode; | ||
| 221 | |||
| 222 | inode->i_uid = opts->uid; | ||
| 223 | inode->i_gid = opts->gid; | ||
| 224 | |||
| 225 | return 0; | ||
| 226 | } | ||
| 227 | |||
| 228 | static int tracefs_remount(struct super_block *sb, int *flags, char *data) | ||
| 229 | { | ||
| 230 | int err; | ||
| 231 | struct tracefs_fs_info *fsi = sb->s_fs_info; | ||
| 232 | |||
| 233 | sync_filesystem(sb); | ||
| 234 | err = tracefs_parse_options(data, &fsi->mount_opts); | ||
| 235 | if (err) | ||
| 236 | goto fail; | ||
| 237 | |||
| 238 | tracefs_apply_options(sb); | ||
| 239 | |||
| 240 | fail: | ||
| 241 | return err; | ||
| 242 | } | ||
| 243 | |||
| 244 | static int tracefs_show_options(struct seq_file *m, struct dentry *root) | ||
| 245 | { | ||
| 246 | struct tracefs_fs_info *fsi = root->d_sb->s_fs_info; | ||
| 247 | struct tracefs_mount_opts *opts = &fsi->mount_opts; | ||
| 248 | |||
| 249 | if (!uid_eq(opts->uid, GLOBAL_ROOT_UID)) | ||
| 250 | seq_printf(m, ",uid=%u", | ||
| 251 | from_kuid_munged(&init_user_ns, opts->uid)); | ||
| 252 | if (!gid_eq(opts->gid, GLOBAL_ROOT_GID)) | ||
| 253 | seq_printf(m, ",gid=%u", | ||
| 254 | from_kgid_munged(&init_user_ns, opts->gid)); | ||
| 255 | if (opts->mode != TRACEFS_DEFAULT_MODE) | ||
| 256 | seq_printf(m, ",mode=%o", opts->mode); | ||
| 257 | |||
| 258 | return 0; | ||
| 259 | } | ||
| 260 | |||
| 261 | static const struct super_operations tracefs_super_operations = { | ||
| 262 | .statfs = simple_statfs, | ||
| 263 | .remount_fs = tracefs_remount, | ||
| 264 | .show_options = tracefs_show_options, | ||
| 265 | }; | ||
| 266 | |||
| 267 | static int trace_fill_super(struct super_block *sb, void *data, int silent) | ||
| 268 | { | ||
| 269 | static struct tree_descr trace_files[] = {{""}}; | ||
| 270 | struct tracefs_fs_info *fsi; | ||
| 271 | int err; | ||
| 272 | |||
| 273 | save_mount_options(sb, data); | ||
| 274 | |||
| 275 | fsi = kzalloc(sizeof(struct tracefs_fs_info), GFP_KERNEL); | ||
| 276 | sb->s_fs_info = fsi; | ||
| 277 | if (!fsi) { | ||
| 278 | err = -ENOMEM; | ||
| 279 | goto fail; | ||
| 280 | } | ||
| 281 | |||
| 282 | err = tracefs_parse_options(data, &fsi->mount_opts); | ||
| 283 | if (err) | ||
| 284 | goto fail; | ||
| 285 | |||
| 286 | err = simple_fill_super(sb, TRACEFS_MAGIC, trace_files); | ||
| 287 | if (err) | ||
| 288 | goto fail; | ||
| 289 | |||
| 290 | sb->s_op = &tracefs_super_operations; | ||
| 291 | |||
| 292 | tracefs_apply_options(sb); | ||
| 293 | |||
| 294 | return 0; | ||
| 295 | |||
| 296 | fail: | ||
| 297 | kfree(fsi); | ||
| 298 | sb->s_fs_info = NULL; | ||
| 299 | return err; | ||
| 300 | } | ||
| 301 | |||
| 302 | static struct dentry *trace_mount(struct file_system_type *fs_type, | ||
| 303 | int flags, const char *dev_name, | ||
| 304 | void *data) | ||
| 305 | { | ||
| 306 | return mount_single(fs_type, flags, data, trace_fill_super); | ||
| 307 | } | ||
| 308 | |||
| 309 | static struct file_system_type trace_fs_type = { | ||
| 310 | .owner = THIS_MODULE, | ||
| 311 | .name = "tracefs", | ||
| 312 | .mount = trace_mount, | ||
| 313 | .kill_sb = kill_litter_super, | ||
| 314 | }; | ||
| 315 | MODULE_ALIAS_FS("tracefs"); | ||
| 316 | |||
| 317 | static struct dentry *start_creating(const char *name, struct dentry *parent) | ||
| 318 | { | ||
| 319 | struct dentry *dentry; | ||
| 320 | int error; | ||
| 321 | |||
| 322 | pr_debug("tracefs: creating file '%s'\n",name); | ||
| 323 | |||
| 324 | error = simple_pin_fs(&trace_fs_type, &tracefs_mount, | ||
| 325 | &tracefs_mount_count); | ||
| 326 | if (error) | ||
| 327 | return ERR_PTR(error); | ||
| 328 | |||
| 329 | /* If the parent is not specified, we create it in the root. | ||
| 330 | * We need the root dentry to do this, which is in the super | ||
| 331 | * block. A pointer to that is in the struct vfsmount that we | ||
| 332 | * have around. | ||
| 333 | */ | ||
| 334 | if (!parent) | ||
| 335 | parent = tracefs_mount->mnt_root; | ||
| 336 | |||
| 337 | mutex_lock(&parent->d_inode->i_mutex); | ||
| 338 | dentry = lookup_one_len(name, parent, strlen(name)); | ||
| 339 | if (!IS_ERR(dentry) && dentry->d_inode) { | ||
| 340 | dput(dentry); | ||
| 341 | dentry = ERR_PTR(-EEXIST); | ||
| 342 | } | ||
| 343 | if (IS_ERR(dentry)) | ||
| 344 | mutex_unlock(&parent->d_inode->i_mutex); | ||
| 345 | return dentry; | ||
| 346 | } | ||
| 347 | |||
| 348 | static struct dentry *failed_creating(struct dentry *dentry) | ||
| 349 | { | ||
| 350 | mutex_unlock(&dentry->d_parent->d_inode->i_mutex); | ||
| 351 | dput(dentry); | ||
| 352 | simple_release_fs(&tracefs_mount, &tracefs_mount_count); | ||
| 353 | return NULL; | ||
| 354 | } | ||
| 355 | |||
| 356 | static struct dentry *end_creating(struct dentry *dentry) | ||
| 357 | { | ||
| 358 | mutex_unlock(&dentry->d_parent->d_inode->i_mutex); | ||
| 359 | return dentry; | ||
| 360 | } | ||
| 361 | |||
| 362 | /** | ||
| 363 | * tracefs_create_file - create a file in the tracefs filesystem | ||
| 364 | * @name: a pointer to a string containing the name of the file to create. | ||
| 365 | * @mode: the permission that the file should have. | ||
| 366 | * @parent: a pointer to the parent dentry for this file. This should be a | ||
| 367 | * directory dentry if set. If this parameter is NULL, then the | ||
| 368 | * file will be created in the root of the tracefs filesystem. | ||
| 369 | * @data: a pointer to something that the caller will want to get to later | ||
| 370 | * on. The inode.i_private pointer will point to this value on | ||
| 371 | * the open() call. | ||
| 372 | * @fops: a pointer to a struct file_operations that should be used for | ||
| 373 | * this file. | ||
| 374 | * | ||
| 375 | * This is the basic "create a file" function for tracefs. It allows for a | ||
| 376 | * wide range of flexibility in creating a file, or a directory (if you want | ||
| 377 | * to create a directory, the tracefs_create_dir() function is | ||
| 378 | * recommended to be used instead.) | ||
| 379 | * | ||
| 380 | * This function will return a pointer to a dentry if it succeeds. This | ||
| 381 | * pointer must be passed to the tracefs_remove() function when the file is | ||
| 382 | * to be removed (no automatic cleanup happens if your module is unloaded, | ||
| 383 | * you are responsible here.) If an error occurs, %NULL will be returned. | ||
| 384 | * | ||
| 385 | * If tracefs is not enabled in the kernel, the value -%ENODEV will be | ||
| 386 | * returned. | ||
| 387 | */ | ||
| 388 | struct dentry *tracefs_create_file(const char *name, umode_t mode, | ||
| 389 | struct dentry *parent, void *data, | ||
| 390 | const struct file_operations *fops) | ||
| 391 | { | ||
| 392 | struct dentry *dentry; | ||
| 393 | struct inode *inode; | ||
| 394 | |||
| 395 | if (!(mode & S_IFMT)) | ||
| 396 | mode |= S_IFREG; | ||
| 397 | BUG_ON(!S_ISREG(mode)); | ||
| 398 | dentry = start_creating(name, parent); | ||
| 399 | |||
| 400 | if (IS_ERR(dentry)) | ||
| 401 | return NULL; | ||
| 402 | |||
| 403 | inode = tracefs_get_inode(dentry->d_sb); | ||
| 404 | if (unlikely(!inode)) | ||
| 405 | return failed_creating(dentry); | ||
| 406 | |||
| 407 | inode->i_mode = mode; | ||
| 408 | inode->i_fop = fops ? fops : &tracefs_file_operations; | ||
| 409 | inode->i_private = data; | ||
| 410 | d_instantiate(dentry, inode); | ||
| 411 | fsnotify_create(dentry->d_parent->d_inode, dentry); | ||
| 412 | return end_creating(dentry); | ||
| 413 | } | ||
| 414 | |||
| 415 | static struct dentry *__create_dir(const char *name, struct dentry *parent, | ||
| 416 | const struct inode_operations *ops) | ||
| 417 | { | ||
| 418 | struct dentry *dentry = start_creating(name, parent); | ||
| 419 | struct inode *inode; | ||
| 420 | |||
| 421 | if (IS_ERR(dentry)) | ||
| 422 | return NULL; | ||
| 423 | |||
| 424 | inode = tracefs_get_inode(dentry->d_sb); | ||
| 425 | if (unlikely(!inode)) | ||
| 426 | return failed_creating(dentry); | ||
| 427 | |||
| 428 | inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; | ||
| 429 | inode->i_op = ops; | ||
| 430 | inode->i_fop = &simple_dir_operations; | ||
| 431 | |||
| 432 | /* directory inodes start off with i_nlink == 2 (for "." entry) */ | ||
| 433 | inc_nlink(inode); | ||
| 434 | d_instantiate(dentry, inode); | ||
| 435 | inc_nlink(dentry->d_parent->d_inode); | ||
| 436 | fsnotify_mkdir(dentry->d_parent->d_inode, dentry); | ||
| 437 | return end_creating(dentry); | ||
| 438 | } | ||
| 439 | |||
| 440 | /** | ||
| 441 | * tracefs_create_dir - create a directory in the tracefs filesystem | ||
| 442 | * @name: a pointer to a string containing the name of the directory to | ||
| 443 | * create. | ||
| 444 | * @parent: a pointer to the parent dentry for this file. This should be a | ||
| 445 | * directory dentry if set. If this parameter is NULL, then the | ||
| 446 | * directory will be created in the root of the tracefs filesystem. | ||
| 447 | * | ||
| 448 | * This function creates a directory in tracefs with the given name. | ||
| 449 | * | ||
| 450 | * This function will return a pointer to a dentry if it succeeds. This | ||
| 451 | * pointer must be passed to the tracefs_remove() function when the file is | ||
| 452 | * to be removed. If an error occurs, %NULL will be returned. | ||
| 453 | * | ||
| 454 | * If tracing is not enabled in the kernel, the value -%ENODEV will be | ||
| 455 | * returned. | ||
| 456 | */ | ||
| 457 | struct dentry *tracefs_create_dir(const char *name, struct dentry *parent) | ||
| 458 | { | ||
| 459 | return __create_dir(name, parent, &simple_dir_inode_operations); | ||
| 460 | } | ||
| 461 | |||
| 462 | /** | ||
| 463 | * tracefs_create_instance_dir - create the tracing instances directory | ||
| 464 | * @name: The name of the instances directory to create | ||
| 465 | * @parent: The parent directory that the instances directory will exist | ||
| 466 | * @mkdir: The function to call when a mkdir is performed. | ||
| 467 | * @rmdir: The function to call when a rmdir is performed. | ||
| 468 | * | ||
| 469 | * Only one instances directory is allowed. | ||
| 470 | * | ||
| 471 | * The instances directory is special as it allows for mkdir and rmdir to | ||
| 472 | * to be done by userspace. When a mkdir or rmdir is performed, the inode | ||
| 473 | * locks are released and the methhods passed in (@mkdir and @rmdir) are | ||
| 474 | * called without locks and with the name of the directory being created | ||
| 475 | * within the instances directory. | ||
| 476 | * | ||
| 477 | * Returns the dentry of the instances directory. | ||
| 478 | */ | ||
| 479 | struct dentry *tracefs_create_instance_dir(const char *name, struct dentry *parent, | ||
| 480 | int (*mkdir)(const char *name), | ||
| 481 | int (*rmdir)(const char *name)) | ||
| 482 | { | ||
| 483 | struct dentry *dentry; | ||
| 484 | |||
| 485 | /* Only allow one instance of the instances directory. */ | ||
| 486 | if (WARN_ON(tracefs_ops.mkdir || tracefs_ops.rmdir)) | ||
| 487 | return NULL; | ||
| 488 | |||
| 489 | dentry = __create_dir(name, parent, &tracefs_dir_inode_operations); | ||
| 490 | if (!dentry) | ||
| 491 | return NULL; | ||
| 492 | |||
| 493 | tracefs_ops.mkdir = mkdir; | ||
| 494 | tracefs_ops.rmdir = rmdir; | ||
| 495 | |||
| 496 | return dentry; | ||
| 497 | } | ||
| 498 | |||
| 499 | static inline int tracefs_positive(struct dentry *dentry) | ||
| 500 | { | ||
| 501 | return dentry->d_inode && !d_unhashed(dentry); | ||
| 502 | } | ||
| 503 | |||
| 504 | static int __tracefs_remove(struct dentry *dentry, struct dentry *parent) | ||
| 505 | { | ||
| 506 | int ret = 0; | ||
| 507 | |||
| 508 | if (tracefs_positive(dentry)) { | ||
| 509 | if (dentry->d_inode) { | ||
| 510 | dget(dentry); | ||
| 511 | switch (dentry->d_inode->i_mode & S_IFMT) { | ||
| 512 | case S_IFDIR: | ||
| 513 | ret = simple_rmdir(parent->d_inode, dentry); | ||
| 514 | break; | ||
| 515 | default: | ||
| 516 | simple_unlink(parent->d_inode, dentry); | ||
| 517 | break; | ||
| 518 | } | ||
| 519 | if (!ret) | ||
| 520 | d_delete(dentry); | ||
| 521 | dput(dentry); | ||
| 522 | } | ||
| 523 | } | ||
| 524 | return ret; | ||
| 525 | } | ||
| 526 | |||
| 527 | /** | ||
| 528 | * tracefs_remove - removes a file or directory from the tracefs filesystem | ||
| 529 | * @dentry: a pointer to a the dentry of the file or directory to be | ||
| 530 | * removed. | ||
| 531 | * | ||
| 532 | * This function removes a file or directory in tracefs that was previously | ||
| 533 | * created with a call to another tracefs function (like | ||
| 534 | * tracefs_create_file() or variants thereof.) | ||
| 535 | */ | ||
| 536 | void tracefs_remove(struct dentry *dentry) | ||
| 537 | { | ||
| 538 | struct dentry *parent; | ||
| 539 | int ret; | ||
| 540 | |||
| 541 | if (IS_ERR_OR_NULL(dentry)) | ||
| 542 | return; | ||
| 543 | |||
| 544 | parent = dentry->d_parent; | ||
| 545 | if (!parent || !parent->d_inode) | ||
| 546 | return; | ||
| 547 | |||
| 548 | mutex_lock(&parent->d_inode->i_mutex); | ||
| 549 | ret = __tracefs_remove(dentry, parent); | ||
| 550 | mutex_unlock(&parent->d_inode->i_mutex); | ||
| 551 | if (!ret) | ||
| 552 | simple_release_fs(&tracefs_mount, &tracefs_mount_count); | ||
| 553 | } | ||
| 554 | |||
| 555 | /** | ||
| 556 | * tracefs_remove_recursive - recursively removes a directory | ||
| 557 | * @dentry: a pointer to a the dentry of the directory to be removed. | ||
| 558 | * | ||
| 559 | * This function recursively removes a directory tree in tracefs that | ||
| 560 | * was previously created with a call to another tracefs function | ||
| 561 | * (like tracefs_create_file() or variants thereof.) | ||
| 562 | */ | ||
| 563 | void tracefs_remove_recursive(struct dentry *dentry) | ||
| 564 | { | ||
| 565 | struct dentry *child, *parent; | ||
| 566 | |||
| 567 | if (IS_ERR_OR_NULL(dentry)) | ||
| 568 | return; | ||
| 569 | |||
| 570 | parent = dentry->d_parent; | ||
| 571 | if (!parent || !parent->d_inode) | ||
| 572 | return; | ||
| 573 | |||
| 574 | parent = dentry; | ||
| 575 | down: | ||
| 576 | mutex_lock(&parent->d_inode->i_mutex); | ||
| 577 | loop: | ||
| 578 | /* | ||
| 579 | * The parent->d_subdirs is protected by the d_lock. Outside that | ||
| 580 | * lock, the child can be unlinked and set to be freed which can | ||
| 581 | * use the d_u.d_child as the rcu head and corrupt this list. | ||
| 582 | */ | ||
| 583 | spin_lock(&parent->d_lock); | ||
| 584 | list_for_each_entry(child, &parent->d_subdirs, d_child) { | ||
| 585 | if (!tracefs_positive(child)) | ||
| 586 | continue; | ||
| 587 | |||
| 588 | /* perhaps simple_empty(child) makes more sense */ | ||
| 589 | if (!list_empty(&child->d_subdirs)) { | ||
| 590 | spin_unlock(&parent->d_lock); | ||
| 591 | mutex_unlock(&parent->d_inode->i_mutex); | ||
| 592 | parent = child; | ||
| 593 | goto down; | ||
| 594 | } | ||
| 595 | |||
| 596 | spin_unlock(&parent->d_lock); | ||
| 597 | |||
| 598 | if (!__tracefs_remove(child, parent)) | ||
| 599 | simple_release_fs(&tracefs_mount, &tracefs_mount_count); | ||
| 600 | |||
| 601 | /* | ||
| 602 | * The parent->d_lock protects agaist child from unlinking | ||
| 603 | * from d_subdirs. When releasing the parent->d_lock we can | ||
| 604 | * no longer trust that the next pointer is valid. | ||
| 605 | * Restart the loop. We'll skip this one with the | ||
| 606 | * tracefs_positive() check. | ||
| 607 | */ | ||
| 608 | goto loop; | ||
| 609 | } | ||
| 610 | spin_unlock(&parent->d_lock); | ||
| 611 | |||
| 612 | mutex_unlock(&parent->d_inode->i_mutex); | ||
| 613 | child = parent; | ||
| 614 | parent = parent->d_parent; | ||
| 615 | mutex_lock(&parent->d_inode->i_mutex); | ||
| 616 | |||
| 617 | if (child != dentry) | ||
| 618 | /* go up */ | ||
| 619 | goto loop; | ||
| 620 | |||
| 621 | if (!__tracefs_remove(child, parent)) | ||
| 622 | simple_release_fs(&tracefs_mount, &tracefs_mount_count); | ||
| 623 | mutex_unlock(&parent->d_inode->i_mutex); | ||
| 624 | } | ||
| 625 | |||
| 626 | /** | ||
| 627 | * tracefs_initialized - Tells whether tracefs has been registered | ||
| 628 | */ | ||
| 629 | bool tracefs_initialized(void) | ||
| 630 | { | ||
| 631 | return tracefs_registered; | ||
| 632 | } | ||
| 633 | |||
| 634 | static struct kobject *trace_kobj; | ||
| 635 | |||
| 636 | static int __init tracefs_init(void) | ||
| 637 | { | ||
| 638 | int retval; | ||
| 639 | |||
| 640 | trace_kobj = kobject_create_and_add("tracing", kernel_kobj); | ||
| 641 | if (!trace_kobj) | ||
| 642 | return -EINVAL; | ||
| 643 | |||
| 644 | retval = register_filesystem(&trace_fs_type); | ||
| 645 | if (!retval) | ||
| 646 | tracefs_registered = true; | ||
| 647 | |||
| 648 | return retval; | ||
| 649 | } | ||
| 650 | core_initcall(tracefs_init); | ||
