aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2006-01-06 03:11:41 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-01-06 11:33:32 -0500
commit642fb4d1f1dd2417aa69189fe5ceb81e4fb72900 (patch)
tree77427e3141a6160bfa09ed3af4428c6e874f3e0c
parent9f6d4b0c21a6894dad7665d3dda4174c7c120784 (diff)
[PATCH] NOMMU: Provide shared-writable mmap support on ramfs
The attached patch makes ramfs support shared-writable mmaps by: (1) Attempting to perform a contiguous block allocation to the requested size when truncate attempts to increase the file from zero size, such as happens when: fd = shm_open("/file/on/ramfs", ...): ftruncate(fd, size_requested); addr = mmap(NULL, subsize, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED, fd, offset); (2) Permitting any shared-writable mapping over any contiguous set of extant pages. get_unmapped_area() will return the address into the actual ramfs pages. The mapping may start anywhere and be of any size, but may not go over the end of file. Multiple mappings may overlap in any way. (3) Not permitting a file to be shrunk if it would truncate any shared mappings (private mappings are copied). Thus this patch provides support for POSIX shared memory on NOMMU kernels, with certain limitations such as there being a large enough block of pages available to support the allocation and it only working on directly mappable filesystems. Signed-off-by: David Howells <dhowells@redhat.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--fs/ramfs/Makefile4
-rw-r--r--fs/ramfs/file-mmu.c57
-rw-r--r--fs/ramfs/file-nommu.c292
-rw-r--r--fs/ramfs/inode.c22
-rw-r--r--fs/ramfs/internal.h15
-rw-r--r--include/linux/ramfs.h10
6 files changed, 378 insertions, 22 deletions
diff --git a/fs/ramfs/Makefile b/fs/ramfs/Makefile
index f096f3007091..5a0236e02ee1 100644
--- a/fs/ramfs/Makefile
+++ b/fs/ramfs/Makefile
@@ -4,4 +4,6 @@
4 4
5obj-$(CONFIG_RAMFS) += ramfs.o 5obj-$(CONFIG_RAMFS) += ramfs.o
6 6
7ramfs-objs := inode.o 7file-mmu-y := file-nommu.o
8file-mmu-$(CONFIG_MMU) := file-mmu.o
9ramfs-objs += inode.o $(file-mmu-y)
diff --git a/fs/ramfs/file-mmu.c b/fs/ramfs/file-mmu.c
new file mode 100644
index 000000000000..2115383dcc8d
--- /dev/null
+++ b/fs/ramfs/file-mmu.c
@@ -0,0 +1,57 @@
1/* file-mmu.c: ramfs MMU-based file operations
2 *
3 * Resizable simple ram filesystem for Linux.
4 *
5 * Copyright (C) 2000 Linus Torvalds.
6 * 2000 Transmeta Corp.
7 *
8 * Usage limits added by David Gibson, Linuxcare Australia.
9 * This file is released under the GPL.
10 */
11
12/*
13 * NOTE! This filesystem is probably most useful
14 * not as a real filesystem, but as an example of
15 * how virtual filesystems can be written.
16 *
17 * It doesn't get much simpler than this. Consider
18 * that this file implements the full semantics of
19 * a POSIX-compliant read-write filesystem.
20 *
21 * Note in particular how the filesystem does not
22 * need to implement any data structures of its own
23 * to keep track of the virtual data: using the VFS
24 * caches is sufficient.
25 */
26
27#include <linux/module.h>
28#include <linux/fs.h>
29#include <linux/pagemap.h>
30#include <linux/highmem.h>
31#include <linux/init.h>
32#include <linux/string.h>
33#include <linux/smp_lock.h>
34#include <linux/backing-dev.h>
35#include <linux/ramfs.h>
36
37#include <asm/uaccess.h>
38#include "internal.h"
39
40struct address_space_operations ramfs_aops = {
41 .readpage = simple_readpage,
42 .prepare_write = simple_prepare_write,
43 .commit_write = simple_commit_write
44};
45
46struct file_operations ramfs_file_operations = {
47 .read = generic_file_read,
48 .write = generic_file_write,
49 .mmap = generic_file_mmap,
50 .fsync = simple_sync_file,
51 .sendfile = generic_file_sendfile,
52 .llseek = generic_file_llseek,
53};
54
55struct inode_operations ramfs_file_inode_operations = {
56 .getattr = simple_getattr,
57};
diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c
new file mode 100644
index 000000000000..3f810acd0bfa
--- /dev/null
+++ b/fs/ramfs/file-nommu.c
@@ -0,0 +1,292 @@
1/* file-nommu.c: no-MMU version of ramfs
2 *
3 * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12#include <linux/module.h>
13#include <linux/fs.h>
14#include <linux/pagemap.h>
15#include <linux/highmem.h>
16#include <linux/init.h>
17#include <linux/string.h>
18#include <linux/smp_lock.h>
19#include <linux/backing-dev.h>
20#include <linux/ramfs.h>
21#include <linux/quotaops.h>
22#include <linux/pagevec.h>
23#include <linux/mman.h>
24
25#include <asm/uaccess.h>
26#include "internal.h"
27
28static int ramfs_nommu_setattr(struct dentry *, struct iattr *);
29
30struct address_space_operations ramfs_aops = {
31 .readpage = simple_readpage,
32 .prepare_write = simple_prepare_write,
33 .commit_write = simple_commit_write
34};
35
36struct file_operations ramfs_file_operations = {
37 .mmap = ramfs_nommu_mmap,
38 .get_unmapped_area = ramfs_nommu_get_unmapped_area,
39 .read = generic_file_read,
40 .write = generic_file_write,
41 .fsync = simple_sync_file,
42 .sendfile = generic_file_sendfile,
43 .llseek = generic_file_llseek,
44};
45
46struct inode_operations ramfs_file_inode_operations = {
47 .setattr = ramfs_nommu_setattr,
48 .getattr = simple_getattr,
49};
50
51/*****************************************************************************/
52/*
53 * add a contiguous set of pages into a ramfs inode when it's truncated from
54 * size 0 on the assumption that it's going to be used for an mmap of shared
55 * memory
56 */
57static int ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize)
58{
59 struct pagevec lru_pvec;
60 unsigned long npages, xpages, loop, limit;
61 struct page *pages;
62 unsigned order;
63 void *data;
64 int ret;
65
66 /* make various checks */
67 order = get_order(newsize);
68 if (unlikely(order >= MAX_ORDER))
69 goto too_big;
70
71 limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
72 if (limit != RLIM_INFINITY && newsize > limit)
73 goto fsize_exceeded;
74
75 if (newsize > inode->i_sb->s_maxbytes)
76 goto too_big;
77
78 i_size_write(inode, newsize);
79
80 /* allocate enough contiguous pages to be able to satisfy the
81 * request */
82 pages = alloc_pages(mapping_gfp_mask(inode->i_mapping), order);
83 if (!pages)
84 return -ENOMEM;
85
86 /* split the high-order page into an array of single pages */
87 xpages = 1UL << order;
88 npages = (newsize + PAGE_SIZE - 1) >> PAGE_SHIFT;
89
90 for (loop = 0; loop < npages; loop++)
91 set_page_count(pages + loop, 1);
92
93 /* trim off any pages we don't actually require */
94 for (loop = npages; loop < xpages; loop++)
95 __free_page(pages + loop);
96
97 /* clear the memory we allocated */
98 newsize = PAGE_SIZE * npages;
99 data = page_address(pages);
100 memset(data, 0, newsize);
101
102 /* attach all the pages to the inode's address space */
103 pagevec_init(&lru_pvec, 0);
104 for (loop = 0; loop < npages; loop++) {
105 struct page *page = pages + loop;
106
107 ret = add_to_page_cache(page, inode->i_mapping, loop, GFP_KERNEL);
108 if (ret < 0)
109 goto add_error;
110
111 if (!pagevec_add(&lru_pvec, page))
112 __pagevec_lru_add(&lru_pvec);
113
114 unlock_page(page);
115 }
116
117 pagevec_lru_add(&lru_pvec);
118 return 0;
119
120 fsize_exceeded:
121 send_sig(SIGXFSZ, current, 0);
122 too_big:
123 return -EFBIG;
124
125 add_error:
126 page_cache_release(pages + loop);
127 for (loop++; loop < npages; loop++)
128 __free_page(pages + loop);
129 return ret;
130}
131
132/*****************************************************************************/
133/*
134 * check that file shrinkage doesn't leave any VMAs dangling in midair
135 */
136static int ramfs_nommu_check_mappings(struct inode *inode,
137 size_t newsize, size_t size)
138{
139 struct vm_area_struct *vma;
140 struct prio_tree_iter iter;
141
142 /* search for VMAs that fall within the dead zone */
143 vma_prio_tree_foreach(vma, &iter, &inode->i_mapping->i_mmap,
144 newsize >> PAGE_SHIFT,
145 (size + PAGE_SIZE - 1) >> PAGE_SHIFT
146 ) {
147 /* found one - only interested if it's shared out of the page
148 * cache */
149 if (vma->vm_flags & VM_SHARED)
150 return -ETXTBSY; /* not quite true, but near enough */
151 }
152
153 return 0;
154}
155
156/*****************************************************************************/
157/*
158 *
159 */
160static int ramfs_nommu_resize(struct inode *inode, loff_t newsize, loff_t size)
161{
162 int ret;
163
164 /* assume a truncate from zero size is going to be for the purposes of
165 * shared mmap */
166 if (size == 0) {
167 if (unlikely(newsize >> 32))
168 return -EFBIG;
169
170 return ramfs_nommu_expand_for_mapping(inode, newsize);
171 }
172
173 /* check that a decrease in size doesn't cut off any shared mappings */
174 if (newsize < size) {
175 ret = ramfs_nommu_check_mappings(inode, newsize, size);
176 if (ret < 0)
177 return ret;
178 }
179
180 ret = vmtruncate(inode, size);
181
182 return ret;
183}
184
185/*****************************************************************************/
186/*
187 * handle a change of attributes
188 * - we're specifically interested in a change of size
189 */
190static int ramfs_nommu_setattr(struct dentry *dentry, struct iattr *ia)
191{
192 struct inode *inode = dentry->d_inode;
193 unsigned int old_ia_valid = ia->ia_valid;
194 int ret = 0;
195
196 /* by providing our own setattr() method, we skip this quotaism */
197 if ((old_ia_valid & ATTR_UID && ia->ia_uid != inode->i_uid) ||
198 (old_ia_valid & ATTR_GID && ia->ia_gid != inode->i_gid))
199 ret = DQUOT_TRANSFER(inode, ia) ? -EDQUOT : 0;
200
201 /* pick out size-changing events */
202 if (ia->ia_valid & ATTR_SIZE) {
203 loff_t size = i_size_read(inode);
204 if (ia->ia_size != size) {
205 ret = ramfs_nommu_resize(inode, ia->ia_size, size);
206 if (ret < 0 || ia->ia_valid == ATTR_SIZE)
207 goto out;
208 } else {
209 /* we skipped the truncate but must still update
210 * timestamps
211 */
212 ia->ia_valid |= ATTR_MTIME|ATTR_CTIME;
213 }
214 }
215
216 ret = inode_setattr(inode, ia);
217 out:
218 ia->ia_valid = old_ia_valid;
219 return ret;
220}
221
222/*****************************************************************************/
223/*
224 * try to determine where a shared mapping can be made
225 * - we require that:
226 * - the pages to be mapped must exist
227 * - the pages be physically contiguous in sequence
228 */
229unsigned long ramfs_nommu_get_unmapped_area(struct file *file,
230 unsigned long addr, unsigned long len,
231 unsigned long pgoff, unsigned long flags)
232{
233 unsigned long maxpages, lpages, nr, loop, ret;
234 struct inode *inode = file->f_dentry->d_inode;
235 struct page **pages = NULL, **ptr, *page;
236 loff_t isize;
237
238 if (!(flags & MAP_SHARED))
239 return addr;
240
241 /* the mapping mustn't extend beyond the EOF */
242 lpages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
243 isize = i_size_read(inode);
244
245 ret = -EINVAL;
246 maxpages = (isize + PAGE_SIZE - 1) >> PAGE_SHIFT;
247 if (pgoff >= maxpages)
248 goto out;
249
250 if (maxpages - pgoff < lpages)
251 goto out;
252
253 /* gang-find the pages */
254 ret = -ENOMEM;
255 pages = kzalloc(lpages * sizeof(struct page *), GFP_KERNEL);
256 if (!pages)
257 goto out;
258
259 nr = find_get_pages(inode->i_mapping, pgoff, lpages, pages);
260 if (nr != lpages)
261 goto out; /* leave if some pages were missing */
262
263 /* check the pages for physical adjacency */
264 ptr = pages;
265 page = *ptr++;
266 page++;
267 for (loop = lpages; loop > 1; loop--)
268 if (*ptr++ != page++)
269 goto out;
270
271 /* okay - all conditions fulfilled */
272 ret = (unsigned long) page_address(pages[0]);
273
274 out:
275 if (pages) {
276 ptr = pages;
277 for (loop = lpages; loop > 0; loop--)
278 put_page(*ptr++);
279 kfree(pages);
280 }
281
282 return ret;
283}
284
285/*****************************************************************************/
286/*
287 * set up a mapping
288 */
289int ramfs_nommu_mmap(struct file *file, struct vm_area_struct *vma)
290{
291 return 0;
292}
diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c
index 0a88917605ae..c66bd5e4c05c 100644
--- a/fs/ramfs/inode.c
+++ b/fs/ramfs/inode.c
@@ -34,13 +34,12 @@
34#include <linux/ramfs.h> 34#include <linux/ramfs.h>
35 35
36#include <asm/uaccess.h> 36#include <asm/uaccess.h>
37#include "internal.h"
37 38
38/* some random number */ 39/* some random number */
39#define RAMFS_MAGIC 0x858458f6 40#define RAMFS_MAGIC 0x858458f6
40 41
41static struct super_operations ramfs_ops; 42static struct super_operations ramfs_ops;
42static struct address_space_operations ramfs_aops;
43static struct inode_operations ramfs_file_inode_operations;
44static struct inode_operations ramfs_dir_inode_operations; 43static struct inode_operations ramfs_dir_inode_operations;
45 44
46static struct backing_dev_info ramfs_backing_dev_info = { 45static struct backing_dev_info ramfs_backing_dev_info = {
@@ -142,25 +141,6 @@ static int ramfs_symlink(struct inode * dir, struct dentry *dentry, const char *
142 return error; 141 return error;
143} 142}
144 143
145static struct address_space_operations ramfs_aops = {
146 .readpage = simple_readpage,
147 .prepare_write = simple_prepare_write,
148 .commit_write = simple_commit_write
149};
150
151struct file_operations ramfs_file_operations = {
152 .read = generic_file_read,
153 .write = generic_file_write,
154 .mmap = generic_file_mmap,
155 .fsync = simple_sync_file,
156 .sendfile = generic_file_sendfile,
157 .llseek = generic_file_llseek,
158};
159
160static struct inode_operations ramfs_file_inode_operations = {
161 .getattr = simple_getattr,
162};
163
164static struct inode_operations ramfs_dir_inode_operations = { 144static struct inode_operations ramfs_dir_inode_operations = {
165 .create = ramfs_create, 145 .create = ramfs_create,
166 .lookup = simple_lookup, 146 .lookup = simple_lookup,
diff --git a/fs/ramfs/internal.h b/fs/ramfs/internal.h
new file mode 100644
index 000000000000..272c8a7120b0
--- /dev/null
+++ b/fs/ramfs/internal.h
@@ -0,0 +1,15 @@
1/* internal.h: ramfs internal definitions
2 *
3 * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12
13extern struct address_space_operations ramfs_aops;
14extern struct file_operations ramfs_file_operations;
15extern struct inode_operations ramfs_file_inode_operations;
diff --git a/include/linux/ramfs.h b/include/linux/ramfs.h
index e0a4faa9610c..953b6df5d037 100644
--- a/include/linux/ramfs.h
+++ b/include/linux/ramfs.h
@@ -5,6 +5,16 @@ struct inode *ramfs_get_inode(struct super_block *sb, int mode, dev_t dev);
5struct super_block *ramfs_get_sb(struct file_system_type *fs_type, 5struct super_block *ramfs_get_sb(struct file_system_type *fs_type,
6 int flags, const char *dev_name, void *data); 6 int flags, const char *dev_name, void *data);
7 7
8#ifndef CONFIG_MMU
9extern unsigned long ramfs_nommu_get_unmapped_area(struct file *file,
10 unsigned long addr,
11 unsigned long len,
12 unsigned long pgoff,
13 unsigned long flags);
14
15extern int ramfs_nommu_mmap(struct file *file, struct vm_area_struct *vma);
16#endif
17
8extern struct file_operations ramfs_file_operations; 18extern struct file_operations ramfs_file_operations;
9extern struct vm_operations_struct generic_file_vm_ops; 19extern struct vm_operations_struct generic_file_vm_ops;
10 20