[PATCH] tmpfs 8/9 loopable tmpfs

From: Hugh Dickins (hugh@veritas.com)
Date: Thu Oct 17 2002 - 19:01:56 EST


Added shmem_readpage and shmem_prepare_write so tmpfs files can be used
by the loop driver (together with simple_commit_write). shmem_getpage
extended to accept file page passed in, which may have to be copied
over from swap page.

Use bdget and sb_set_blocksize so loop can see our preferred blocksize
PAGE_CACHE_SIZE. Use copy_highpage, removed from highmem.h in 2.4.17:
restore it but with kmap_atomics. Restore (a simple) copy_page to
asm-sparc64/page.h, which alone of all arches discarded it.

--- tmpfs7/Documentation/filesystems/tmpfs.txt Wed Mar 20 13:04:25 2002
+++ tmpfs8/Documentation/filesystems/tmpfs.txt Thu Oct 17 22:01:59 2002
@@ -47,10 +47,9 @@
    shared memory)
 
 3) Some people (including me) find it very convenient to mount it
- e.g. on /tmp and /var/tmp and have a big swap partition. But be
- aware: loop mounts of tmpfs files do not work due to the internal
- design. So mkinitrd shipped by most distributions will fail with a
- tmpfs /tmp.
+ e.g. on /tmp and /var/tmp and have a big swap partition. And now
+ loop mounts of tmpfs files do work, so mkinitrd shipped by most
+ distributions should succeed with a tmpfs /tmp.
 
 4) And probably a lot more I do not know about :-)
 
@@ -90,13 +89,9 @@
    size=50% the tmpfs instance should be able to grow to 50 percent of
    RAM + swap. So the instance should adapt automatically if you add
    or remove swap space.
-2) loop mounts: This is difficult since loop.c relies on the readpage
- operation. This operation gets a page from the caller to be filled
- with the content of the file at that position. But tmpfs always has
- the page and thus cannot copy the content to the given page. So it
- cannot provide this operation. The VM had to be changed seriously
- to achieve this.
-3) Show the number of tmpfs RAM pages. (As shared?)
+2) Show the number of tmpfs RAM pages. (As shared?)
 
 Author:
    Christoph Rohland <cr@sap.com>, 1.12.01
+Updated:
+ Hugh Dickins <hugh@veritas.com>, 17 Oct 2002
--- tmpfs7/include/asm-sparc64/page.h Mon Oct 7 20:37:47 2002
+++ tmpfs8/include/asm-sparc64/page.h Thu Oct 17 22:01:59 2002
@@ -39,6 +39,7 @@
 #define clear_page(X) _clear_page((void *)(X))
 struct page;
 extern void clear_user_page(void *addr, unsigned long vaddr, struct page *page);
+#define copy_page(X,Y) __memcpy((void *)(X), (void *)(Y), PAGE_SIZE)
 extern void copy_user_page(void *to, void *from, unsigned long vaddr, struct page *topage);
 
 /* GROSS, defining this makes gcc pass these types as aggregates,
--- tmpfs7/include/linux/highmem.h Mon Sep 16 05:51:51 2002
+++ tmpfs8/include/linux/highmem.h Thu Oct 17 22:01:59 2002
@@ -73,4 +73,15 @@
         kunmap_atomic(vto, KM_USER1);
 }
 
+static inline void copy_highpage(struct page *to, struct page *from)
+{
+ char *vfrom, *vto;
+
+ vfrom = kmap_atomic(from, KM_USER0);
+ vto = kmap_atomic(to, KM_USER1);
+ copy_page(vto, vfrom);
+ kunmap_atomic(vfrom, KM_USER0);
+ kunmap_atomic(vto, KM_USER1);
+}
+
 #endif /* _LINUX_HIGHMEM_H */
--- tmpfs7/mm/shmem.c Thu Oct 17 22:01:49 2002
+++ tmpfs8/mm/shmem.c Thu Oct 17 22:01:59 2002
@@ -735,39 +735,48 @@
         struct address_space *mapping = inode->i_mapping;
         struct shmem_inode_info *info = SHMEM_I(inode);
         struct shmem_sb_info *sbinfo;
- struct page *page;
+ struct page *filepage = *pagep;
+ struct page *swappage;
         swp_entry_t *entry;
         swp_entry_t swap;
- int error = 0;
+ int error;
 
         if (idx >= SHMEM_MAX_INDEX)
                 return -EFBIG;
+ /*
+ * Normally, filepage is NULL on entry, and either found
+ * uptodate immediately, or allocated and zeroed, or read
+ * in under swappage, which is then assigned to filepage.
+ * But shmem_readpage and shmem_prepare_write pass in a locked
+ * filepage, which may be found not uptodate by other callers
+ * too, and may need to be copied from the swappage read in.
+ */
 repeat:
- page = find_lock_page(mapping, idx);
- if (page) {
- unlock_page(page);
- *pagep = page;
- return 0;
- }
+ if (!filepage)
+ filepage = find_lock_page(mapping, idx);
+ if (filepage && PageUptodate(filepage))
+ goto done;
+ error = 0;
 
         spin_lock(&info->lock);
         shmem_recalc_inode(inode);
         entry = shmem_swp_alloc(info, idx, sgp);
         if (IS_ERR(entry)) {
                 spin_unlock(&info->lock);
- return PTR_ERR(entry);
+ error = PTR_ERR(entry);
+ goto failed;
         }
         swap = *entry;
 
         if (swap.val) {
                 /* Look it up and read it in.. */
- page = lookup_swap_cache(swap);
- if (!page) {
+ swappage = lookup_swap_cache(swap);
+ if (!swappage) {
                         shmem_swp_unmap(entry);
                         spin_unlock(&info->lock);
                         swapin_readahead(swap);
- page = read_swap_cache_async(swap);
- if (!page) {
+ swappage = read_swap_cache_async(swap);
+ if (!swappage) {
                                 spin_lock(&info->lock);
                                 entry = shmem_swp_alloc(info, idx, sgp);
                                 if (IS_ERR(entry))
@@ -779,110 +788,152 @@
                                 }
                                 spin_unlock(&info->lock);
                                 if (error)
- return error;
+ goto failed;
                                 goto repeat;
                         }
- wait_on_page_locked(page);
- page_cache_release(page);
+ wait_on_page_locked(swappage);
+ page_cache_release(swappage);
                         goto repeat;
                 }
 
                 /* We have to do this with page locked to prevent races */
- if (TestSetPageLocked(page)) {
+ if (TestSetPageLocked(swappage)) {
                         shmem_swp_unmap(entry);
                         spin_unlock(&info->lock);
- wait_on_page_locked(page);
- page_cache_release(page);
+ wait_on_page_locked(swappage);
+ page_cache_release(swappage);
                         goto repeat;
                 }
- if (PageWriteback(page)) {
+ if (PageWriteback(swappage)) {
                         shmem_swp_unmap(entry);
                         spin_unlock(&info->lock);
- wait_on_page_writeback(page);
- unlock_page(page);
- page_cache_release(page);
+ wait_on_page_writeback(swappage);
+ unlock_page(swappage);
+ page_cache_release(swappage);
                         goto repeat;
                 }
-
- error = PageUptodate(page)?
- move_from_swap_cache(page, idx, mapping): -EIO;
- if (error) {
+ if (!PageUptodate(swappage)) {
                         shmem_swp_unmap(entry);
                         spin_unlock(&info->lock);
- unlock_page(page);
- page_cache_release(page);
- return error;
+ unlock_page(swappage);
+ page_cache_release(swappage);
+ error = -EIO;
+ goto failed;
                 }
 
- shmem_swp_set(info, entry, 0);
- shmem_swp_unmap(entry);
- spin_unlock(&info->lock);
- swap_free(swap);
- } else if (sgp == SGP_READ) {
+ if (filepage) {
+ shmem_swp_set(info, entry, 0);
+ shmem_swp_unmap(entry);
+ delete_from_swap_cache(swappage);
+ spin_unlock(&info->lock);
+ flush_page_to_ram(swappage);
+ copy_highpage(filepage, swappage);
+ unlock_page(swappage);
+ page_cache_release(swappage);
+ flush_dcache_page(filepage);
+ SetPageUptodate(filepage);
+ set_page_dirty(filepage);
+ swap_free(swap);
+ } else if (!(error = move_from_swap_cache(
+ swappage, idx, mapping))) {
+ shmem_swp_set(info, entry, 0);
+ shmem_swp_unmap(entry);
+ spin_unlock(&info->lock);
+ filepage = swappage;
+ swap_free(swap);
+ } else {
+ shmem_swp_unmap(entry);
+ spin_unlock(&info->lock);
+ unlock_page(swappage);
+ page_cache_release(swappage);
+ if (error != -EEXIST)
+ goto failed;
+ goto repeat;
+ }
+ } else if (sgp == SGP_READ && !filepage) {
                 shmem_swp_unmap(entry);
- page = find_get_page(mapping, idx);
- if (page && TestSetPageLocked(page)) {
+ filepage = find_get_page(mapping, idx);
+ if (filepage &&
+ (!PageUptodate(filepage) || TestSetPageLocked(filepage))) {
                         spin_unlock(&info->lock);
- wait_on_page_locked(page);
- page_cache_release(page);
+ wait_on_page_locked(filepage);
+ page_cache_release(filepage);
+ filepage = NULL;
                         goto repeat;
                 }
                 spin_unlock(&info->lock);
         } else {
                 shmem_swp_unmap(entry);
- spin_unlock(&info->lock);
                 sbinfo = SHMEM_SB(inode->i_sb);
                 spin_lock(&sbinfo->stat_lock);
                 if (sbinfo->free_blocks == 0) {
                         spin_unlock(&sbinfo->stat_lock);
- return -ENOSPC;
+ spin_unlock(&info->lock);
+ error = -ENOSPC;
+ goto failed;
                 }
                 sbinfo->free_blocks--;
                 inode->i_blocks += BLOCKS_PER_PAGE;
                 spin_unlock(&sbinfo->stat_lock);
 
- page = page_cache_alloc(mapping);
- if (!page) {
- shmem_free_block(inode);
- return -ENOMEM;
- }
-
- spin_lock(&info->lock);
- entry = shmem_swp_alloc(info, idx, sgp);
- if (IS_ERR(entry))
- error = PTR_ERR(entry);
- else {
- swap = *entry;
- shmem_swp_unmap(entry);
- }
- if (error || swap.val ||
- add_to_page_cache_lru(page, mapping, idx) < 0) {
+ if (!filepage) {
                         spin_unlock(&info->lock);
- page_cache_release(page);
- shmem_free_block(inode);
- if (error)
- return error;
- goto repeat;
+ filepage = page_cache_alloc(mapping);
+ if (!filepage) {
+ shmem_free_block(inode);
+ error = -ENOMEM;
+ goto failed;
+ }
+
+ spin_lock(&info->lock);
+ entry = shmem_swp_alloc(info, idx, sgp);
+ if (IS_ERR(entry))
+ error = PTR_ERR(entry);
+ else {
+ swap = *entry;
+ shmem_swp_unmap(entry);
+ }
+ if (error || swap.val ||
+ (error = add_to_page_cache_lru(
+ filepage, mapping, idx))) {
+ spin_unlock(&info->lock);
+ page_cache_release(filepage);
+ shmem_free_block(inode);
+ filepage = NULL;
+ if (error != -EEXIST)
+ goto failed;
+ goto repeat;
+ }
                 }
+
                 info->alloced++;
                 spin_unlock(&info->lock);
- clear_highpage(page);
- flush_dcache_page(page);
- SetPageUptodate(page);
+ clear_highpage(filepage);
+ flush_dcache_page(filepage);
+ SetPageUptodate(filepage);
+ }
+done:
+ if (!*pagep) {
+ if (filepage) {
+ unlock_page(filepage);
+ *pagep = filepage;
+ } else
+ *pagep = ZERO_PAGE(0);
         }
-
- if (page) {
- unlock_page(page);
- *pagep = page;
- } else
- *pagep = ZERO_PAGE(0);
         return 0;
+
+failed:
+ if (*pagep != filepage) {
+ unlock_page(filepage);
+ page_cache_release(filepage);
+ }
+ return error;
 }
 
 struct page *shmem_nopage(struct vm_area_struct *vma, unsigned long address, int unused)
 {
         struct inode *inode = vma->vm_file->f_dentry->d_inode;
- struct page *page;
+ struct page *page = NULL;
         unsigned long idx;
         int error;
 
@@ -1007,13 +1058,33 @@
 static struct inode_operations shmem_symlink_inode_operations;
 static struct inode_operations shmem_symlink_inline_operations;
 
+/*
+ * tmpfs itself makes no use of generic_file_read, generic_file_mmap
+ * or generic_file_write; but shmem_readpage, shmem_prepare_write and
+ * simple_commit_write let a tmpfs file be used below the loop driver.
+ */
+static int
+shmem_readpage(struct file *file, struct page *page)
+{
+ struct inode *inode = page->mapping->host;
+ int error = shmem_getpage(inode, page->index, &page, SGP_CACHE);
+ unlock_page(page);
+ return error;
+}
+
+static int
+shmem_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to)
+{
+ struct inode *inode = page->mapping->host;
+ return shmem_getpage(inode, page->index, &page, SGP_WRITE);
+}
+
 static ssize_t
 shmem_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
 {
         struct inode *inode = file->f_dentry->d_inode;
         unsigned long limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
         loff_t pos;
- struct page *page;
         unsigned long written;
         long status;
         int err;
@@ -1110,6 +1181,7 @@
         }
 
         while (count) {
+ struct page *page = NULL;
                 unsigned long bytes, index, offset;
                 char *kaddr;
                 int left;
@@ -1188,7 +1260,7 @@
         offset = *ppos & ~PAGE_CACHE_MASK;
 
         for (;;) {
- struct page *page;
+ struct page *page = NULL;
                 unsigned long end_index, nr, ret;
 
                 end_index = inode->i_size >> PAGE_CACHE_SHIFT;
@@ -1416,7 +1488,7 @@
         int error;
         int len;
         struct inode *inode;
- struct page *page;
+ struct page *page = NULL;
         char *kaddr;
         struct shmem_inode_info *info;
 
@@ -1474,7 +1546,7 @@
 
 static int shmem_readlink(struct dentry *dentry, char *buffer, int buflen)
 {
- struct page *page;
+ struct page *page = NULL;
         int res = shmem_getpage(dentry->d_inode, 0, &page, SGP_READ);
         if (res)
                 return res;
@@ -1486,7 +1558,7 @@
 
 static int shmem_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
- struct page *page;
+ struct page *page = NULL;
         int res = shmem_getpage(dentry->d_inode, 0, &page, SGP_READ);
         if (res)
                 return res;
@@ -1592,7 +1664,7 @@
         gid_t gid = current->fsgid;
         struct shmem_sb_info *sbinfo;
         struct sysinfo si;
- int err;
+ int err = -ENOMEM;
 
         sbinfo = kmalloc(sizeof(struct shmem_sb_info), GFP_KERNEL);
         if (!sbinfo)
@@ -1622,28 +1694,29 @@
         sbinfo->max_inodes = inodes;
         sbinfo->free_inodes = inodes;
         sb->s_maxbytes = SHMEM_MAX_BYTES;
- sb->s_blocksize = PAGE_CACHE_SIZE;
- sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+ sb->s_bdev = bdget(sb->s_dev);
+ if (!sb->s_bdev)
+ goto failed;
+ if (!sb_set_blocksize(sb, PAGE_CACHE_SIZE))
+ BUG();
         sb->s_magic = TMPFS_MAGIC;
         sb->s_op = &shmem_ops;
         inode = shmem_get_inode(sb, S_IFDIR | mode, 0);
- if (!inode) {
- err = -ENOMEM;
- goto failed;
- }
-
+ if (!inode)
+ goto failed_bdput;
         inode->i_uid = uid;
         inode->i_gid = gid;
         root = d_alloc_root(inode);
- if (!root) {
- err = -ENOMEM;
+ if (!root)
                 goto failed_iput;
- }
         sb->s_root = root;
         return 0;
 
 failed_iput:
         iput(inode);
+failed_bdput:
+ bdput(sb->s_bdev);
+ sb->s_bdev = NULL;
 failed:
         kfree(sbinfo);
         sb->s_fs_info = NULL;
@@ -1652,6 +1725,8 @@
 
 static void shmem_put_super(struct super_block *sb)
 {
+ bdput(sb->s_bdev);
+ sb->s_bdev = NULL;
         kfree(sb->s_fs_info);
         sb->s_fs_info = NULL;
 }
@@ -1704,6 +1779,11 @@
         .writepages = shmem_writepages,
         .vm_writeback = shmem_vm_writeback,
         .set_page_dirty = __set_page_dirty_nobuffers,
+#ifdef CONFIG_TMPFS
+ .readpage = shmem_readpage,
+ .prepare_write = shmem_prepare_write,
+ .commit_write = simple_commit_write,
+#endif
 };
 
 static struct file_operations shmem_file_operations = {

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Wed Oct 23 2002 - 22:00:38 EST