lkml.org 
[lkml]   [2002]   [Oct]   [17]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[PATCH] tmpfs 8/9 loopable tmpfs
    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/

    \
     
     \ /
      Last update: 2005-03-22 13:30    [W:0.047 / U:31.524 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site