diff options
author | akpm@linux-foundation.org <akpm@linux-foundation.org> | 2008-02-08 07:21:48 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-08 12:22:41 -0500 |
commit | 680d794babebc74484c141448baa9b95b211cf5e (patch) | |
tree | fa8b9c085fceaed12b54ea20a8c04696c750053e /mm/shmem.c | |
parent | 90d09e141bb23bf0df5e31c40fb3175c17e8bda2 (diff) |
mount options: fix tmpfs
Add .show_options super operation to tmpfs.
Signed-off-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/shmem.c')
-rw-r--r-- | mm/shmem.c | 196 |
1 files changed, 132 insertions, 64 deletions
diff --git a/mm/shmem.c b/mm/shmem.c index 85bed948fafc..2f961a6dbf57 100644 --- a/mm/shmem.c +++ b/mm/shmem.c | |||
@@ -49,6 +49,7 @@ | |||
49 | #include <linux/ctype.h> | 49 | #include <linux/ctype.h> |
50 | #include <linux/migrate.h> | 50 | #include <linux/migrate.h> |
51 | #include <linux/highmem.h> | 51 | #include <linux/highmem.h> |
52 | #include <linux/seq_file.h> | ||
52 | 53 | ||
53 | #include <asm/uaccess.h> | 54 | #include <asm/uaccess.h> |
54 | #include <asm/div64.h> | 55 | #include <asm/div64.h> |
@@ -84,6 +85,16 @@ enum sgp_type { | |||
84 | SGP_WRITE, /* may exceed i_size, may allocate page */ | 85 | SGP_WRITE, /* may exceed i_size, may allocate page */ |
85 | }; | 86 | }; |
86 | 87 | ||
88 | static unsigned long shmem_default_max_blocks(void) | ||
89 | { | ||
90 | return totalram_pages / 2; | ||
91 | } | ||
92 | |||
93 | static unsigned long shmem_default_max_inodes(void) | ||
94 | { | ||
95 | return min(totalram_pages - totalhigh_pages, totalram_pages / 2); | ||
96 | } | ||
97 | |||
87 | static int shmem_getpage(struct inode *inode, unsigned long idx, | 98 | static int shmem_getpage(struct inode *inode, unsigned long idx, |
88 | struct page **pagep, enum sgp_type sgp, int *type); | 99 | struct page **pagep, enum sgp_type sgp, int *type); |
89 | 100 | ||
@@ -1068,7 +1079,8 @@ redirty: | |||
1068 | } | 1079 | } |
1069 | 1080 | ||
1070 | #ifdef CONFIG_NUMA | 1081 | #ifdef CONFIG_NUMA |
1071 | static inline int shmem_parse_mpol(char *value, int *policy, nodemask_t *policy_nodes) | 1082 | #ifdef CONFIG_TMPFS |
1083 | static int shmem_parse_mpol(char *value, int *policy, nodemask_t *policy_nodes) | ||
1072 | { | 1084 | { |
1073 | char *nodelist = strchr(value, ':'); | 1085 | char *nodelist = strchr(value, ':'); |
1074 | int err = 1; | 1086 | int err = 1; |
@@ -1117,6 +1129,42 @@ out: | |||
1117 | return err; | 1129 | return err; |
1118 | } | 1130 | } |
1119 | 1131 | ||
1132 | static void shmem_show_mpol(struct seq_file *seq, int policy, | ||
1133 | const nodemask_t policy_nodes) | ||
1134 | { | ||
1135 | char *policy_string; | ||
1136 | |||
1137 | switch (policy) { | ||
1138 | case MPOL_PREFERRED: | ||
1139 | policy_string = "prefer"; | ||
1140 | break; | ||
1141 | case MPOL_BIND: | ||
1142 | policy_string = "bind"; | ||
1143 | break; | ||
1144 | case MPOL_INTERLEAVE: | ||
1145 | policy_string = "interleave"; | ||
1146 | break; | ||
1147 | default: | ||
1148 | /* MPOL_DEFAULT */ | ||
1149 | return; | ||
1150 | } | ||
1151 | |||
1152 | seq_printf(seq, ",mpol=%s", policy_string); | ||
1153 | |||
1154 | if (policy != MPOL_INTERLEAVE || | ||
1155 | !nodes_equal(policy_nodes, node_states[N_HIGH_MEMORY])) { | ||
1156 | char buffer[64]; | ||
1157 | int len; | ||
1158 | |||
1159 | len = nodelist_scnprintf(buffer, sizeof(buffer), policy_nodes); | ||
1160 | if (len < sizeof(buffer)) | ||
1161 | seq_printf(seq, ":%s", buffer); | ||
1162 | else | ||
1163 | seq_printf(seq, ":?"); | ||
1164 | } | ||
1165 | } | ||
1166 | #endif /* CONFIG_TMPFS */ | ||
1167 | |||
1120 | static struct page *shmem_swapin(swp_entry_t entry, gfp_t gfp, | 1168 | static struct page *shmem_swapin(swp_entry_t entry, gfp_t gfp, |
1121 | struct shmem_inode_info *info, unsigned long idx) | 1169 | struct shmem_inode_info *info, unsigned long idx) |
1122 | { | 1170 | { |
@@ -1148,13 +1196,20 @@ static struct page *shmem_alloc_page(gfp_t gfp, | |||
1148 | mpol_free(pvma.vm_policy); | 1196 | mpol_free(pvma.vm_policy); |
1149 | return page; | 1197 | return page; |
1150 | } | 1198 | } |
1151 | #else | 1199 | #else /* !CONFIG_NUMA */ |
1200 | #ifdef CONFIG_TMPFS | ||
1152 | static inline int shmem_parse_mpol(char *value, int *policy, | 1201 | static inline int shmem_parse_mpol(char *value, int *policy, |
1153 | nodemask_t *policy_nodes) | 1202 | nodemask_t *policy_nodes) |
1154 | { | 1203 | { |
1155 | return 1; | 1204 | return 1; |
1156 | } | 1205 | } |
1157 | 1206 | ||
1207 | static inline void shmem_show_mpol(struct seq_file *seq, int policy, | ||
1208 | const nodemask_t policy_nodes) | ||
1209 | { | ||
1210 | } | ||
1211 | #endif /* CONFIG_TMPFS */ | ||
1212 | |||
1158 | static inline struct page *shmem_swapin(swp_entry_t entry, gfp_t gfp, | 1213 | static inline struct page *shmem_swapin(swp_entry_t entry, gfp_t gfp, |
1159 | struct shmem_inode_info *info, unsigned long idx) | 1214 | struct shmem_inode_info *info, unsigned long idx) |
1160 | { | 1215 | { |
@@ -1166,7 +1221,7 @@ static inline struct page *shmem_alloc_page(gfp_t gfp, | |||
1166 | { | 1221 | { |
1167 | return alloc_page(gfp); | 1222 | return alloc_page(gfp); |
1168 | } | 1223 | } |
1169 | #endif | 1224 | #endif /* CONFIG_NUMA */ |
1170 | 1225 | ||
1171 | /* | 1226 | /* |
1172 | * shmem_getpage - either get the page from swap or allocate a new one | 1227 | * shmem_getpage - either get the page from swap or allocate a new one |
@@ -2077,9 +2132,8 @@ static const struct export_operations shmem_export_ops = { | |||
2077 | .fh_to_dentry = shmem_fh_to_dentry, | 2132 | .fh_to_dentry = shmem_fh_to_dentry, |
2078 | }; | 2133 | }; |
2079 | 2134 | ||
2080 | static int shmem_parse_options(char *options, int *mode, uid_t *uid, | 2135 | static int shmem_parse_options(char *options, struct shmem_sb_info *sbinfo, |
2081 | gid_t *gid, unsigned long *blocks, unsigned long *inodes, | 2136 | bool remount) |
2082 | int *policy, nodemask_t *policy_nodes) | ||
2083 | { | 2137 | { |
2084 | char *this_char, *value, *rest; | 2138 | char *this_char, *value, *rest; |
2085 | 2139 | ||
@@ -2122,35 +2176,37 @@ static int shmem_parse_options(char *options, int *mode, uid_t *uid, | |||
2122 | } | 2176 | } |
2123 | if (*rest) | 2177 | if (*rest) |
2124 | goto bad_val; | 2178 | goto bad_val; |
2125 | *blocks = DIV_ROUND_UP(size, PAGE_CACHE_SIZE); | 2179 | sbinfo->max_blocks = |
2180 | DIV_ROUND_UP(size, PAGE_CACHE_SIZE); | ||
2126 | } else if (!strcmp(this_char,"nr_blocks")) { | 2181 | } else if (!strcmp(this_char,"nr_blocks")) { |
2127 | *blocks = memparse(value,&rest); | 2182 | sbinfo->max_blocks = memparse(value, &rest); |
2128 | if (*rest) | 2183 | if (*rest) |
2129 | goto bad_val; | 2184 | goto bad_val; |
2130 | } else if (!strcmp(this_char,"nr_inodes")) { | 2185 | } else if (!strcmp(this_char,"nr_inodes")) { |
2131 | *inodes = memparse(value,&rest); | 2186 | sbinfo->max_inodes = memparse(value, &rest); |
2132 | if (*rest) | 2187 | if (*rest) |
2133 | goto bad_val; | 2188 | goto bad_val; |
2134 | } else if (!strcmp(this_char,"mode")) { | 2189 | } else if (!strcmp(this_char,"mode")) { |
2135 | if (!mode) | 2190 | if (remount) |
2136 | continue; | 2191 | continue; |
2137 | *mode = simple_strtoul(value,&rest,8); | 2192 | sbinfo->mode = simple_strtoul(value, &rest, 8) & 07777; |
2138 | if (*rest) | 2193 | if (*rest) |
2139 | goto bad_val; | 2194 | goto bad_val; |
2140 | } else if (!strcmp(this_char,"uid")) { | 2195 | } else if (!strcmp(this_char,"uid")) { |
2141 | if (!uid) | 2196 | if (remount) |
2142 | continue; | 2197 | continue; |
2143 | *uid = simple_strtoul(value,&rest,0); | 2198 | sbinfo->uid = simple_strtoul(value, &rest, 0); |
2144 | if (*rest) | 2199 | if (*rest) |
2145 | goto bad_val; | 2200 | goto bad_val; |
2146 | } else if (!strcmp(this_char,"gid")) { | 2201 | } else if (!strcmp(this_char,"gid")) { |
2147 | if (!gid) | 2202 | if (remount) |
2148 | continue; | 2203 | continue; |
2149 | *gid = simple_strtoul(value,&rest,0); | 2204 | sbinfo->gid = simple_strtoul(value, &rest, 0); |
2150 | if (*rest) | 2205 | if (*rest) |
2151 | goto bad_val; | 2206 | goto bad_val; |
2152 | } else if (!strcmp(this_char,"mpol")) { | 2207 | } else if (!strcmp(this_char,"mpol")) { |
2153 | if (shmem_parse_mpol(value,policy,policy_nodes)) | 2208 | if (shmem_parse_mpol(value, &sbinfo->policy, |
2209 | &sbinfo->policy_nodes)) | ||
2154 | goto bad_val; | 2210 | goto bad_val; |
2155 | } else { | 2211 | } else { |
2156 | printk(KERN_ERR "tmpfs: Bad mount option %s\n", | 2212 | printk(KERN_ERR "tmpfs: Bad mount option %s\n", |
@@ -2170,24 +2226,20 @@ bad_val: | |||
2170 | static int shmem_remount_fs(struct super_block *sb, int *flags, char *data) | 2226 | static int shmem_remount_fs(struct super_block *sb, int *flags, char *data) |
2171 | { | 2227 | { |
2172 | struct shmem_sb_info *sbinfo = SHMEM_SB(sb); | 2228 | struct shmem_sb_info *sbinfo = SHMEM_SB(sb); |
2173 | unsigned long max_blocks = sbinfo->max_blocks; | 2229 | struct shmem_sb_info config = *sbinfo; |
2174 | unsigned long max_inodes = sbinfo->max_inodes; | ||
2175 | int policy = sbinfo->policy; | ||
2176 | nodemask_t policy_nodes = sbinfo->policy_nodes; | ||
2177 | unsigned long blocks; | 2230 | unsigned long blocks; |
2178 | unsigned long inodes; | 2231 | unsigned long inodes; |
2179 | int error = -EINVAL; | 2232 | int error = -EINVAL; |
2180 | 2233 | ||
2181 | if (shmem_parse_options(data, NULL, NULL, NULL, &max_blocks, | 2234 | if (shmem_parse_options(data, &config, true)) |
2182 | &max_inodes, &policy, &policy_nodes)) | ||
2183 | return error; | 2235 | return error; |
2184 | 2236 | ||
2185 | spin_lock(&sbinfo->stat_lock); | 2237 | spin_lock(&sbinfo->stat_lock); |
2186 | blocks = sbinfo->max_blocks - sbinfo->free_blocks; | 2238 | blocks = sbinfo->max_blocks - sbinfo->free_blocks; |
2187 | inodes = sbinfo->max_inodes - sbinfo->free_inodes; | 2239 | inodes = sbinfo->max_inodes - sbinfo->free_inodes; |
2188 | if (max_blocks < blocks) | 2240 | if (config.max_blocks < blocks) |
2189 | goto out; | 2241 | goto out; |
2190 | if (max_inodes < inodes) | 2242 | if (config.max_inodes < inodes) |
2191 | goto out; | 2243 | goto out; |
2192 | /* | 2244 | /* |
2193 | * Those tests also disallow limited->unlimited while any are in | 2245 | * Those tests also disallow limited->unlimited while any are in |
@@ -2195,23 +2247,42 @@ static int shmem_remount_fs(struct super_block *sb, int *flags, char *data) | |||
2195 | * but we must separately disallow unlimited->limited, because | 2247 | * but we must separately disallow unlimited->limited, because |
2196 | * in that case we have no record of how much is already in use. | 2248 | * in that case we have no record of how much is already in use. |
2197 | */ | 2249 | */ |
2198 | if (max_blocks && !sbinfo->max_blocks) | 2250 | if (config.max_blocks && !sbinfo->max_blocks) |
2199 | goto out; | 2251 | goto out; |
2200 | if (max_inodes && !sbinfo->max_inodes) | 2252 | if (config.max_inodes && !sbinfo->max_inodes) |
2201 | goto out; | 2253 | goto out; |
2202 | 2254 | ||
2203 | error = 0; | 2255 | error = 0; |
2204 | sbinfo->max_blocks = max_blocks; | 2256 | sbinfo->max_blocks = config.max_blocks; |
2205 | sbinfo->free_blocks = max_blocks - blocks; | 2257 | sbinfo->free_blocks = config.max_blocks - blocks; |
2206 | sbinfo->max_inodes = max_inodes; | 2258 | sbinfo->max_inodes = config.max_inodes; |
2207 | sbinfo->free_inodes = max_inodes - inodes; | 2259 | sbinfo->free_inodes = config.max_inodes - inodes; |
2208 | sbinfo->policy = policy; | 2260 | sbinfo->policy = config.policy; |
2209 | sbinfo->policy_nodes = policy_nodes; | 2261 | sbinfo->policy_nodes = config.policy_nodes; |
2210 | out: | 2262 | out: |
2211 | spin_unlock(&sbinfo->stat_lock); | 2263 | spin_unlock(&sbinfo->stat_lock); |
2212 | return error; | 2264 | return error; |
2213 | } | 2265 | } |
2214 | #endif | 2266 | |
2267 | static int shmem_show_options(struct seq_file *seq, struct vfsmount *vfs) | ||
2268 | { | ||
2269 | struct shmem_sb_info *sbinfo = SHMEM_SB(vfs->mnt_sb); | ||
2270 | |||
2271 | if (sbinfo->max_blocks != shmem_default_max_blocks()) | ||
2272 | seq_printf(seq, ",size=%luk", | ||
2273 | sbinfo->max_blocks << (PAGE_CACHE_SHIFT - 10)); | ||
2274 | if (sbinfo->max_inodes != shmem_default_max_inodes()) | ||
2275 | seq_printf(seq, ",nr_inodes=%lu", sbinfo->max_inodes); | ||
2276 | if (sbinfo->mode != (S_IRWXUGO | S_ISVTX)) | ||
2277 | seq_printf(seq, ",mode=%03o", sbinfo->mode); | ||
2278 | if (sbinfo->uid != 0) | ||
2279 | seq_printf(seq, ",uid=%u", sbinfo->uid); | ||
2280 | if (sbinfo->gid != 0) | ||
2281 | seq_printf(seq, ",gid=%u", sbinfo->gid); | ||
2282 | shmem_show_mpol(seq, sbinfo->policy, sbinfo->policy_nodes); | ||
2283 | return 0; | ||
2284 | } | ||
2285 | #endif /* CONFIG_TMPFS */ | ||
2215 | 2286 | ||
2216 | static void shmem_put_super(struct super_block *sb) | 2287 | static void shmem_put_super(struct super_block *sb) |
2217 | { | 2288 | { |
@@ -2224,15 +2295,23 @@ static int shmem_fill_super(struct super_block *sb, | |||
2224 | { | 2295 | { |
2225 | struct inode *inode; | 2296 | struct inode *inode; |
2226 | struct dentry *root; | 2297 | struct dentry *root; |
2227 | int mode = S_IRWXUGO | S_ISVTX; | ||
2228 | uid_t uid = current->fsuid; | ||
2229 | gid_t gid = current->fsgid; | ||
2230 | int err = -ENOMEM; | ||
2231 | struct shmem_sb_info *sbinfo; | 2298 | struct shmem_sb_info *sbinfo; |
2232 | unsigned long blocks = 0; | 2299 | int err = -ENOMEM; |
2233 | unsigned long inodes = 0; | 2300 | |
2234 | int policy = MPOL_DEFAULT; | 2301 | /* Round up to L1_CACHE_BYTES to resist false sharing */ |
2235 | nodemask_t policy_nodes = node_states[N_HIGH_MEMORY]; | 2302 | sbinfo = kmalloc(max((int)sizeof(struct shmem_sb_info), |
2303 | L1_CACHE_BYTES), GFP_KERNEL); | ||
2304 | if (!sbinfo) | ||
2305 | return -ENOMEM; | ||
2306 | |||
2307 | sbinfo->max_blocks = 0; | ||
2308 | sbinfo->max_inodes = 0; | ||
2309 | sbinfo->mode = S_IRWXUGO | S_ISVTX; | ||
2310 | sbinfo->uid = current->fsuid; | ||
2311 | sbinfo->gid = current->fsgid; | ||
2312 | sbinfo->policy = MPOL_DEFAULT; | ||
2313 | sbinfo->policy_nodes = node_states[N_HIGH_MEMORY]; | ||
2314 | sb->s_fs_info = sbinfo; | ||
2236 | 2315 | ||
2237 | #ifdef CONFIG_TMPFS | 2316 | #ifdef CONFIG_TMPFS |
2238 | /* | 2317 | /* |
@@ -2241,34 +2320,22 @@ static int shmem_fill_super(struct super_block *sb, | |||
2241 | * but the internal instance is left unlimited. | 2320 | * but the internal instance is left unlimited. |
2242 | */ | 2321 | */ |
2243 | if (!(sb->s_flags & MS_NOUSER)) { | 2322 | if (!(sb->s_flags & MS_NOUSER)) { |
2244 | blocks = totalram_pages / 2; | 2323 | sbinfo->max_blocks = shmem_default_max_blocks(); |
2245 | inodes = totalram_pages - totalhigh_pages; | 2324 | sbinfo->max_inodes = shmem_default_max_inodes(); |
2246 | if (inodes > blocks) | 2325 | if (shmem_parse_options(data, sbinfo, false)) { |
2247 | inodes = blocks; | 2326 | err = -EINVAL; |
2248 | if (shmem_parse_options(data, &mode, &uid, &gid, &blocks, | 2327 | goto failed; |
2249 | &inodes, &policy, &policy_nodes)) | 2328 | } |
2250 | return -EINVAL; | ||
2251 | } | 2329 | } |
2252 | sb->s_export_op = &shmem_export_ops; | 2330 | sb->s_export_op = &shmem_export_ops; |
2253 | #else | 2331 | #else |
2254 | sb->s_flags |= MS_NOUSER; | 2332 | sb->s_flags |= MS_NOUSER; |
2255 | #endif | 2333 | #endif |
2256 | 2334 | ||
2257 | /* Round up to L1_CACHE_BYTES to resist false sharing */ | ||
2258 | sbinfo = kmalloc(max((int)sizeof(struct shmem_sb_info), | ||
2259 | L1_CACHE_BYTES), GFP_KERNEL); | ||
2260 | if (!sbinfo) | ||
2261 | return -ENOMEM; | ||
2262 | |||
2263 | spin_lock_init(&sbinfo->stat_lock); | 2335 | spin_lock_init(&sbinfo->stat_lock); |
2264 | sbinfo->max_blocks = blocks; | 2336 | sbinfo->free_blocks = sbinfo->max_blocks; |
2265 | sbinfo->free_blocks = blocks; | 2337 | sbinfo->free_inodes = sbinfo->max_inodes; |
2266 | sbinfo->max_inodes = inodes; | ||
2267 | sbinfo->free_inodes = inodes; | ||
2268 | sbinfo->policy = policy; | ||
2269 | sbinfo->policy_nodes = policy_nodes; | ||
2270 | 2338 | ||
2271 | sb->s_fs_info = sbinfo; | ||
2272 | sb->s_maxbytes = SHMEM_MAX_BYTES; | 2339 | sb->s_maxbytes = SHMEM_MAX_BYTES; |
2273 | sb->s_blocksize = PAGE_CACHE_SIZE; | 2340 | sb->s_blocksize = PAGE_CACHE_SIZE; |
2274 | sb->s_blocksize_bits = PAGE_CACHE_SHIFT; | 2341 | sb->s_blocksize_bits = PAGE_CACHE_SHIFT; |
@@ -2280,11 +2347,11 @@ static int shmem_fill_super(struct super_block *sb, | |||
2280 | sb->s_flags |= MS_POSIXACL; | 2347 | sb->s_flags |= MS_POSIXACL; |
2281 | #endif | 2348 | #endif |
2282 | 2349 | ||
2283 | inode = shmem_get_inode(sb, S_IFDIR | mode, 0); | 2350 | inode = shmem_get_inode(sb, S_IFDIR | sbinfo->mode, 0); |
2284 | if (!inode) | 2351 | if (!inode) |
2285 | goto failed; | 2352 | goto failed; |
2286 | inode->i_uid = uid; | 2353 | inode->i_uid = sbinfo->uid; |
2287 | inode->i_gid = gid; | 2354 | inode->i_gid = sbinfo->gid; |
2288 | root = d_alloc_root(inode); | 2355 | root = d_alloc_root(inode); |
2289 | if (!root) | 2356 | if (!root) |
2290 | goto failed_iput; | 2357 | goto failed_iput; |
@@ -2420,6 +2487,7 @@ static const struct super_operations shmem_ops = { | |||
2420 | #ifdef CONFIG_TMPFS | 2487 | #ifdef CONFIG_TMPFS |
2421 | .statfs = shmem_statfs, | 2488 | .statfs = shmem_statfs, |
2422 | .remount_fs = shmem_remount_fs, | 2489 | .remount_fs = shmem_remount_fs, |
2490 | .show_options = shmem_show_options, | ||
2423 | #endif | 2491 | #endif |
2424 | .delete_inode = shmem_delete_inode, | 2492 | .delete_inode = shmem_delete_inode, |
2425 | .drop_inode = generic_delete_inode, | 2493 | .drop_inode = generic_delete_inode, |