diff options
author | Theodore Ts'o <tytso@mit.edu> | 2009-03-31 09:10:09 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2009-03-31 09:10:09 -0400 |
commit | 3197ebdb130473a92760100cbfe0d7e671838f48 (patch) | |
tree | 7101dc642e91026a65264adb7845ce3561d691fe /fs/ext4/super.c | |
parent | afc32f7ee9febc020c73da61402351d4c90437f3 (diff) |
ext4: Add sysfs support
Add basic sysfs support so that information about the mounted
filesystem and various tuning parameters can be accessed via
/sys/fs/ext4/<dev>/*.
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs/ext4/super.c')
-rw-r--r-- | fs/ext4/super.c | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 30fc27cdf8fc..2883d4318c22 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <linux/quotaops.h> | 35 | #include <linux/quotaops.h> |
36 | #include <linux/seq_file.h> | 36 | #include <linux/seq_file.h> |
37 | #include <linux/proc_fs.h> | 37 | #include <linux/proc_fs.h> |
38 | #include <linux/ctype.h> | ||
38 | #include <linux/marker.h> | 39 | #include <linux/marker.h> |
39 | #include <linux/log2.h> | 40 | #include <linux/log2.h> |
40 | #include <linux/crc16.h> | 41 | #include <linux/crc16.h> |
@@ -48,6 +49,7 @@ | |||
48 | #include "group.h" | 49 | #include "group.h" |
49 | 50 | ||
50 | struct proc_dir_entry *ext4_proc_root; | 51 | struct proc_dir_entry *ext4_proc_root; |
52 | static struct kset *ext4_kset; | ||
51 | 53 | ||
52 | static int ext4_load_journal(struct super_block *, struct ext4_super_block *, | 54 | static int ext4_load_journal(struct super_block *, struct ext4_super_block *, |
53 | unsigned long journal_devnum); | 55 | unsigned long journal_devnum); |
@@ -580,6 +582,7 @@ static void ext4_put_super(struct super_block *sb) | |||
580 | remove_proc_entry("inode_readahead_blks", sbi->s_proc); | 582 | remove_proc_entry("inode_readahead_blks", sbi->s_proc); |
581 | remove_proc_entry(sb->s_id, ext4_proc_root); | 583 | remove_proc_entry(sb->s_id, ext4_proc_root); |
582 | } | 584 | } |
585 | kobject_del(&sbi->s_kobj); | ||
583 | 586 | ||
584 | for (i = 0; i < sbi->s_gdb_count; i++) | 587 | for (i = 0; i < sbi->s_gdb_count; i++) |
585 | brelse(sbi->s_group_desc[i]); | 588 | brelse(sbi->s_group_desc[i]); |
@@ -615,6 +618,16 @@ static void ext4_put_super(struct super_block *sb) | |||
615 | ext4_blkdev_remove(sbi); | 618 | ext4_blkdev_remove(sbi); |
616 | } | 619 | } |
617 | sb->s_fs_info = NULL; | 620 | sb->s_fs_info = NULL; |
621 | /* | ||
622 | * Now that we are completely done shutting down the | ||
623 | * superblock, we need to actually destroy the kobject. | ||
624 | */ | ||
625 | unlock_kernel(); | ||
626 | unlock_super(sb); | ||
627 | kobject_put(&sbi->s_kobj); | ||
628 | wait_for_completion(&sbi->s_kobj_unregister); | ||
629 | lock_super(sb); | ||
630 | lock_kernel(); | ||
618 | kfree(sbi->s_blockgroup_lock); | 631 | kfree(sbi->s_blockgroup_lock); |
619 | kfree(sbi); | 632 | kfree(sbi); |
620 | return; | 633 | return; |
@@ -1464,6 +1477,11 @@ set_qf_format: | |||
1464 | return 0; | 1477 | return 0; |
1465 | if (option < 0 || option > (1 << 30)) | 1478 | if (option < 0 || option > (1 << 30)) |
1466 | return 0; | 1479 | return 0; |
1480 | if (option & (option - 1)) { | ||
1481 | printk(KERN_ERR "EXT4-fs: inode_readahead_blks" | ||
1482 | " must be a power of 2\n"); | ||
1483 | return 0; | ||
1484 | } | ||
1467 | sbi->s_inode_readahead_blks = option; | 1485 | sbi->s_inode_readahead_blks = option; |
1468 | break; | 1486 | break; |
1469 | case Opt_journal_ioprio: | 1487 | case Opt_journal_ioprio: |
@@ -1992,6 +2010,181 @@ static unsigned long ext4_get_stripe_size(struct ext4_sb_info *sbi) | |||
1992 | return 0; | 2010 | return 0; |
1993 | } | 2011 | } |
1994 | 2012 | ||
2013 | /* sysfs supprt */ | ||
2014 | |||
2015 | struct ext4_attr { | ||
2016 | struct attribute attr; | ||
2017 | ssize_t (*show)(struct ext4_attr *, struct ext4_sb_info *, char *); | ||
2018 | ssize_t (*store)(struct ext4_attr *, struct ext4_sb_info *, | ||
2019 | const char *, size_t); | ||
2020 | int offset; | ||
2021 | }; | ||
2022 | |||
2023 | static int parse_strtoul(const char *buf, | ||
2024 | unsigned long max, unsigned long *value) | ||
2025 | { | ||
2026 | char *endp; | ||
2027 | |||
2028 | while (*buf && isspace(*buf)) | ||
2029 | buf++; | ||
2030 | *value = simple_strtoul(buf, &endp, 0); | ||
2031 | while (*endp && isspace(*endp)) | ||
2032 | endp++; | ||
2033 | if (*endp || *value > max) | ||
2034 | return -EINVAL; | ||
2035 | |||
2036 | return 0; | ||
2037 | } | ||
2038 | |||
2039 | static ssize_t delayed_allocation_blocks_show(struct ext4_attr *a, | ||
2040 | struct ext4_sb_info *sbi, | ||
2041 | char *buf) | ||
2042 | { | ||
2043 | return snprintf(buf, PAGE_SIZE, "%llu\n", | ||
2044 | (s64) percpu_counter_sum(&sbi->s_dirtyblocks_counter)); | ||
2045 | } | ||
2046 | |||
2047 | static ssize_t session_write_kbytes_show(struct ext4_attr *a, | ||
2048 | struct ext4_sb_info *sbi, char *buf) | ||
2049 | { | ||
2050 | struct super_block *sb = sbi->s_buddy_cache->i_sb; | ||
2051 | |||
2052 | return snprintf(buf, PAGE_SIZE, "%lu\n", | ||
2053 | (part_stat_read(sb->s_bdev->bd_part, sectors[1]) - | ||
2054 | sbi->s_sectors_written_start) >> 1); | ||
2055 | } | ||
2056 | |||
2057 | static ssize_t lifetime_write_kbytes_show(struct ext4_attr *a, | ||
2058 | struct ext4_sb_info *sbi, char *buf) | ||
2059 | { | ||
2060 | struct super_block *sb = sbi->s_buddy_cache->i_sb; | ||
2061 | |||
2062 | return snprintf(buf, PAGE_SIZE, "%llu\n", | ||
2063 | sbi->s_kbytes_written + | ||
2064 | ((part_stat_read(sb->s_bdev->bd_part, sectors[1]) - | ||
2065 | EXT4_SB(sb)->s_sectors_written_start) >> 1)); | ||
2066 | } | ||
2067 | |||
2068 | static ssize_t inode_readahead_blks_store(struct ext4_attr *a, | ||
2069 | struct ext4_sb_info *sbi, | ||
2070 | const char *buf, size_t count) | ||
2071 | { | ||
2072 | unsigned long t; | ||
2073 | |||
2074 | if (parse_strtoul(buf, 0x40000000, &t)) | ||
2075 | return -EINVAL; | ||
2076 | |||
2077 | /* inode_readahead_blks must be a power of 2 */ | ||
2078 | if (t & (t-1)) | ||
2079 | return -EINVAL; | ||
2080 | |||
2081 | sbi->s_inode_readahead_blks = t; | ||
2082 | return count; | ||
2083 | } | ||
2084 | |||
2085 | static ssize_t sbi_ui_show(struct ext4_attr *a, | ||
2086 | struct ext4_sb_info *sbi, char *buf) | ||
2087 | { | ||
2088 | unsigned int *ui = (unsigned int *) (((char *) sbi) + a->offset); | ||
2089 | |||
2090 | return snprintf(buf, PAGE_SIZE, "%u\n", *ui); | ||
2091 | } | ||
2092 | |||
2093 | static ssize_t sbi_ui_store(struct ext4_attr *a, | ||
2094 | struct ext4_sb_info *sbi, | ||
2095 | const char *buf, size_t count) | ||
2096 | { | ||
2097 | unsigned int *ui = (unsigned int *) (((char *) sbi) + a->offset); | ||
2098 | unsigned long t; | ||
2099 | |||
2100 | if (parse_strtoul(buf, 0xffffffff, &t)) | ||
2101 | return -EINVAL; | ||
2102 | *ui = t; | ||
2103 | return count; | ||
2104 | } | ||
2105 | |||
2106 | #define EXT4_ATTR_OFFSET(_name,_mode,_show,_store,_elname) \ | ||
2107 | static struct ext4_attr ext4_attr_##_name = { \ | ||
2108 | .attr = {.name = __stringify(_name), .mode = _mode }, \ | ||
2109 | .show = _show, \ | ||
2110 | .store = _store, \ | ||
2111 | .offset = offsetof(struct ext4_sb_info, _elname), \ | ||
2112 | } | ||
2113 | #define EXT4_ATTR(name, mode, show, store) \ | ||
2114 | static struct ext4_attr ext4_attr_##name = __ATTR(name, mode, show, store) | ||
2115 | |||
2116 | #define EXT4_RO_ATTR(name) EXT4_ATTR(name, 0444, name##_show, NULL) | ||
2117 | #define EXT4_RW_ATTR(name) EXT4_ATTR(name, 0644, name##_show, name##_store) | ||
2118 | #define EXT4_RW_ATTR_SBI_UI(name, elname) \ | ||
2119 | EXT4_ATTR_OFFSET(name, 0644, sbi_ui_show, sbi_ui_store, elname) | ||
2120 | #define ATTR_LIST(name) &ext4_attr_##name.attr | ||
2121 | |||
2122 | EXT4_RO_ATTR(delayed_allocation_blocks); | ||
2123 | EXT4_RO_ATTR(session_write_kbytes); | ||
2124 | EXT4_RO_ATTR(lifetime_write_kbytes); | ||
2125 | EXT4_ATTR_OFFSET(inode_readahead_blks, 0644, sbi_ui_show, | ||
2126 | inode_readahead_blks_store, s_inode_readahead_blks); | ||
2127 | EXT4_RW_ATTR_SBI_UI(mb_stats, s_mb_stats); | ||
2128 | EXT4_RW_ATTR_SBI_UI(mb_max_to_scan, s_mb_max_to_scan); | ||
2129 | EXT4_RW_ATTR_SBI_UI(mb_min_to_scan, s_mb_min_to_scan); | ||
2130 | EXT4_RW_ATTR_SBI_UI(mb_order2_req, s_mb_order2_reqs); | ||
2131 | EXT4_RW_ATTR_SBI_UI(mb_stream_req, s_mb_stream_request); | ||
2132 | EXT4_RW_ATTR_SBI_UI(mb_group_prealloc, s_mb_group_prealloc); | ||
2133 | |||
2134 | static struct attribute *ext4_attrs[] = { | ||
2135 | ATTR_LIST(delayed_allocation_blocks), | ||
2136 | ATTR_LIST(session_write_kbytes), | ||
2137 | ATTR_LIST(lifetime_write_kbytes), | ||
2138 | ATTR_LIST(inode_readahead_blks), | ||
2139 | ATTR_LIST(mb_stats), | ||
2140 | ATTR_LIST(mb_max_to_scan), | ||
2141 | ATTR_LIST(mb_min_to_scan), | ||
2142 | ATTR_LIST(mb_order2_req), | ||
2143 | ATTR_LIST(mb_stream_req), | ||
2144 | ATTR_LIST(mb_group_prealloc), | ||
2145 | NULL, | ||
2146 | }; | ||
2147 | |||
2148 | static ssize_t ext4_attr_show(struct kobject *kobj, | ||
2149 | struct attribute *attr, char *buf) | ||
2150 | { | ||
2151 | struct ext4_sb_info *sbi = container_of(kobj, struct ext4_sb_info, | ||
2152 | s_kobj); | ||
2153 | struct ext4_attr *a = container_of(attr, struct ext4_attr, attr); | ||
2154 | |||
2155 | return a->show ? a->show(a, sbi, buf) : 0; | ||
2156 | } | ||
2157 | |||
2158 | static ssize_t ext4_attr_store(struct kobject *kobj, | ||
2159 | struct attribute *attr, | ||
2160 | const char *buf, size_t len) | ||
2161 | { | ||
2162 | struct ext4_sb_info *sbi = container_of(kobj, struct ext4_sb_info, | ||
2163 | s_kobj); | ||
2164 | struct ext4_attr *a = container_of(attr, struct ext4_attr, attr); | ||
2165 | |||
2166 | return a->store ? a->store(a, sbi, buf, len) : 0; | ||
2167 | } | ||
2168 | |||
2169 | static void ext4_sb_release(struct kobject *kobj) | ||
2170 | { | ||
2171 | struct ext4_sb_info *sbi = container_of(kobj, struct ext4_sb_info, | ||
2172 | s_kobj); | ||
2173 | complete(&sbi->s_kobj_unregister); | ||
2174 | } | ||
2175 | |||
2176 | |||
2177 | static struct sysfs_ops ext4_attr_ops = { | ||
2178 | .show = ext4_attr_show, | ||
2179 | .store = ext4_attr_store, | ||
2180 | }; | ||
2181 | |||
2182 | static struct kobj_type ext4_ktype = { | ||
2183 | .default_attrs = ext4_attrs, | ||
2184 | .sysfs_ops = &ext4_attr_ops, | ||
2185 | .release = ext4_sb_release, | ||
2186 | }; | ||
2187 | |||
1995 | static int ext4_fill_super(struct super_block *sb, void *data, int silent) | 2188 | static int ext4_fill_super(struct super_block *sb, void *data, int silent) |
1996 | __releases(kernel_lock) | 2189 | __releases(kernel_lock) |
1997 | __acquires(kernel_lock) | 2190 | __acquires(kernel_lock) |
@@ -2575,6 +2768,16 @@ no_journal: | |||
2575 | goto failed_mount4; | 2768 | goto failed_mount4; |
2576 | } | 2769 | } |
2577 | 2770 | ||
2771 | sbi->s_kobj.kset = ext4_kset; | ||
2772 | init_completion(&sbi->s_kobj_unregister); | ||
2773 | err = kobject_init_and_add(&sbi->s_kobj, &ext4_ktype, NULL, | ||
2774 | "%s", sb->s_id); | ||
2775 | if (err) { | ||
2776 | ext4_mb_release(sb); | ||
2777 | ext4_ext_release(sb); | ||
2778 | goto failed_mount4; | ||
2779 | }; | ||
2780 | |||
2578 | /* | 2781 | /* |
2579 | * akpm: core read_super() calls in here with the superblock locked. | 2782 | * akpm: core read_super() calls in here with the superblock locked. |
2580 | * That deadlocks, because orphan cleanup needs to lock the superblock | 2783 | * That deadlocks, because orphan cleanup needs to lock the superblock |
@@ -3734,6 +3937,9 @@ static int __init init_ext4_fs(void) | |||
3734 | { | 3937 | { |
3735 | int err; | 3938 | int err; |
3736 | 3939 | ||
3940 | ext4_kset = kset_create_and_add("ext4", NULL, fs_kobj); | ||
3941 | if (!ext4_kset) | ||
3942 | return -ENOMEM; | ||
3737 | ext4_proc_root = proc_mkdir("fs/ext4", NULL); | 3943 | ext4_proc_root = proc_mkdir("fs/ext4", NULL); |
3738 | err = init_ext4_mballoc(); | 3944 | err = init_ext4_mballoc(); |
3739 | if (err) | 3945 | if (err) |
@@ -3775,6 +3981,7 @@ static void __exit exit_ext4_fs(void) | |||
3775 | exit_ext4_xattr(); | 3981 | exit_ext4_xattr(); |
3776 | exit_ext4_mballoc(); | 3982 | exit_ext4_mballoc(); |
3777 | remove_proc_entry("fs/ext4", NULL); | 3983 | remove_proc_entry("fs/ext4", NULL); |
3984 | kset_unregister(ext4_kset); | ||
3778 | } | 3985 | } |
3779 | 3986 | ||
3780 | MODULE_AUTHOR("Remy Card, Stephen Tweedie, Andrew Morton, Andreas Dilger, Theodore Ts'o and others"); | 3987 | MODULE_AUTHOR("Remy Card, Stephen Tweedie, Andrew Morton, Andreas Dilger, Theodore Ts'o and others"); |