Al,
Thanks. I'll print this one out and post it on the wall for tonight's
debugging session.
:-)
Jeff
Alexander Viro wrote:
>
> On Wed, 25 Oct 2000, Alexander Viro wrote:
>
> > I can do the proof-of-concept patch (below 10Kb, ext2 + generic code, with
> > the need to repeat the fs-specific parts for other filesystems) in an
> > hour. Clearance?
>
> OK, it didn't take an hour. Warning: completely untested, needs
> (trivial) changes to other filesystems.
>
> Rules: when you change i_size for inode with non-trivial ->a_ops -
> either call update_cached_size(inode) or do the equivalent by hands
> (e.g. generic_commit_write have to mess with i_size value to update the
> ->size_in_pages and ->last_page_size - we already have them calculated).
> Ditto for the places where you set ->a_ops.
>
> Invariants:
> inode->i_mapping->size_in_pages ==
> (inode->i_size + PAGE_CACHE_SIZE - 1)>>PAGE_CACHE_SHIFT;
> inode->i_size == ((....->size_in_pages-1)<<PAGE_CACHE_SHIFT) +
> ....->last_page_size;
>
> Linus, what do you think about that? I can do the remaining filesystems
> and give it initial testing today.
>
> diff -urN rc10-pre5/fs/buffer.c rc10-pre5-size_in_pages/fs/buffer.c
> --- rc10-pre5/fs/buffer.c Tue Oct 24 01:27:28 2000
> +++ rc10-pre5-size_in_pages/fs/buffer.c Wed Oct 25 17:46:25 2000
> @@ -1751,6 +1751,9 @@
> kunmap(page);
> if (pos > inode->i_size) {
> inode->i_size = pos;
> + /* to is always positive */
> + page->mapping->size_in_pages = page->index+1;
> + page->mapping->last_page_size = to;
> mark_inode_dirty(inode);
> }
> return 0;
> @@ -1835,19 +1838,22 @@
> int block_write_full_page(struct page *page, get_block_t *get_block)
> {
> struct inode *inode = (struct inode*)page->mapping->host;
> - unsigned long end_index = inode->i_size >> PAGE_CACHE_SHIFT;
> + unsigned long end_index = page->mapping->size_in_pages;
> unsigned offset;
> int err;
>
> /* easy case */
> - if (page->index < end_index)
> - return __block_write_full_page(inode, page, get_block);
> -
> - /* things got complicated... */
> - offset = inode->i_size & (PAGE_CACHE_SIZE-1);
> + if (page->index+1 < end_index)
> + goto easy;
> /* OK, are we completely out? */
> - if (page->index >= end_index+1 || !offset)
> + if (page->index+1 > end_index)
> return -EIO;
> +
> + offset = inode->i_mapping->last_page_size;
> + /* Another easy case - 'partial' page is not */
> + if (offset == PAGE_CACHE_SIZE)
> + goto easy;
> +
> /* Sigh... will have to work, then... */
> err = __block_prepare_write(inode, page, 0, offset, get_block);
> if (!err) {
> @@ -1860,6 +1866,8 @@
> }
> ClearPageUptodate(page);
> goto done;
> +easy:
> + return __block_write_full_page(inode, page, get_block);
> }
>
> int generic_block_bmap(struct address_space *mapping, long block, get_block_t *get_block)
> diff -urN rc10-pre5/fs/ext2/inode.c rc10-pre5-size_in_pages/fs/ext2/inode.c
> --- rc10-pre5/fs/ext2/inode.c Wed Oct 4 03:44:54 2000
> +++ rc10-pre5-size_in_pages/fs/ext2/inode.c Wed Oct 25 17:41:24 2000
> @@ -54,6 +54,8 @@
> mark_inode_dirty(inode);
> ext2_update_inode(inode, IS_SYNC(inode));
> inode->i_size = 0;
> + inode.i_data->size_in_pages = 0;
> + inode.i_data->last_page_size = PAGE_CACHE_SIZE;
> if (inode->i_blocks)
> ext2_truncate (inode);
> ext2_free_inode (inode);
> @@ -1063,6 +1065,7 @@
> inode->i_op = &ext2_file_inode_operations;
> inode->i_fop = &ext2_file_operations;
> inode->i_mapping->a_ops = &ext2_aops;
> + update_cached_size(inode);
> } else if (S_ISDIR(inode->i_mode)) {
> inode->i_op = &ext2_dir_inode_operations;
> inode->i_fop = &ext2_dir_operations;
> @@ -1072,6 +1075,7 @@
> else {
> inode->i_op = &page_symlink_inode_operations;
> inode->i_mapping->a_ops = &ext2_aops;
> + update_cached_size(inode);
> }
> } else
> init_special_inode(inode, inode->i_mode,
> diff -urN rc10-pre5/fs/inode.c rc10-pre5-size_in_pages/fs/inode.c
> --- rc10-pre5/fs/inode.c Tue Oct 24 01:27:29 2000
> +++ rc10-pre5-size_in_pages/fs/inode.c Wed Oct 25 17:39:28 2000
> @@ -529,6 +529,8 @@
> inode->i_bdev = NULL;
> inode->i_data.a_ops = &empty_aops;
> inode->i_data.host = (void*)inode;
> + inode->i_data.size_in_pages = 0;
> + inode->i_data.last_page_size = PAGE_CACHE_SIZE;
> inode->i_mapping = &inode->i_data;
> }
>
> diff -urN rc10-pre5/include/linux/fs.h rc10-pre5-size_in_pages/include/linux/fs.h
> --- rc10-pre5/include/linux/fs.h Tue Oct 24 01:27:36 2000
> +++ rc10-pre5-size_in_pages/include/linux/fs.h Wed Oct 25 17:31:40 2000
> @@ -366,6 +366,8 @@
> struct vm_area_struct *i_mmap; /* list of private mappings */
> struct vm_area_struct *i_mmap_shared; /* list of shared mappings */
> spinlock_t i_shared_lock; /* and spinlock protecting it */
> + unsigned long size_in_pages;
> + unsigned last_page_size;
> };
>
> struct block_device {
> @@ -456,6 +458,16 @@
> #define I_LOCK 2
> #define I_FREEING 4
> #define I_CLEAR 8
> +
> +static inline void update_cached_size(struct inode *inode)
> +{
> + loff_t size = inode->i_size;
> + inode->i_mapping.size_in_pages =
> + (size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
> + if ((size &= ~PAGE_CACHE_MASK) == 0)
> + size = PAGE_CACHE_SIZE;
> + inode->i_mapping.last_page_size = size;
> +}
>
> extern void __mark_inode_dirty(struct inode *);
> static inline void mark_inode_dirty(struct inode *inode)
> diff -urN rc10-pre5/mm/filemap.c rc10-pre5-size_in_pages/mm/filemap.c
> --- rc10-pre5/mm/filemap.c Tue Oct 24 01:27:38 2000
> +++ rc10-pre5-size_in_pages/mm/filemap.c Wed Oct 25 17:43:01 2000
> @@ -429,8 +429,7 @@
> */
> static inline int page_cache_read(struct file * file, unsigned long offset)
> {
> - struct inode *inode = file->f_dentry->d_inode;
> - struct address_space *mapping = inode->i_mapping;
> + struct address_space *mapping = file->f_dentry->d_inode->i_mapping;
> struct page **hash = page_hash(mapping, offset);
> struct page *page;
>
> @@ -633,8 +632,7 @@
> */
> static void drop_behind(struct file * file, unsigned long index)
> {
> - struct inode *inode = file->f_dentry->d_inode;
> - struct address_space *mapping = inode->i_mapping;
> + struct address_space *mapping = file->f_dentry->d_inode->i_mapping;
> struct page **hash;
> struct page *page;
> unsigned long start;
> @@ -794,7 +792,7 @@
> struct file * filp, struct inode * inode,
> struct page * page)
> {
> - unsigned long end_index = inode->i_size >> PAGE_CACHE_SHIFT;
> + unsigned long end_index = inode->i_mapping->size_in_pages;
> unsigned long index = page->index;
> unsigned long max_ahead, ahead;
> unsigned long raend;
> @@ -819,8 +817,8 @@
> filp->f_rawin = 0;
> filp->f_ralen = 1;
> if (!max_ahead) {
> - filp->f_raend = index + filp->f_ralen;
> - filp->f_rawin += filp->f_ralen;
> + filp->f_raend = index + 1;
> + filp->f_rawin = 1;
> }
> }
> }
> @@ -966,14 +964,14 @@
>
> for (;;) {
> struct page *page, **hash;
> - unsigned long end_index, nr;
> + unsigned nr;
>
> - end_index = inode->i_size >> PAGE_CACHE_SHIFT;
> - if (index > end_index)
> + if (index >= mapping->size_in_pages)
> break;
> +
> nr = PAGE_CACHE_SIZE;
> - if (index == end_index) {
> - nr = inode->i_size & ~PAGE_CACHE_MASK;
> + if (index == mapping->size_in_pages - 1) {
> + nr = mapping->last_page_size;
> if (nr <= offset)
> break;
> }
> @@ -1328,10 +1326,9 @@
> {
> int error;
> struct file *file = area->vm_file;
> - struct inode *inode = file->f_dentry->d_inode;
> - struct address_space *mapping = inode->i_mapping;
> + struct address_space *mapping = file->f_dentry->d_inode->i_mapping;
> struct page *page, **hash, *old_page;
> - unsigned long size = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
> + unsigned long size = mapping->size_in_pages;
>
> unsigned long pgoff = ((address - area->vm_start) >> PAGE_CACHE_SHIFT) + area->vm_pgoff;
>
> @@ -1910,8 +1907,7 @@
> if (!vma->vm_file)
> return error;
> file = vma->vm_file;
> - size = (file->f_dentry->d_inode->i_size + PAGE_CACHE_SIZE - 1) >>
> - PAGE_CACHE_SHIFT;
> + size = file->f_dentry->d_inode->i_mapping->size_in_pages;
>
> start = ((start - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
> if (end > vma->vm_end)
> diff -urN rc10-pre5/mm/memory.c rc10-pre5-size_in_pages/mm/memory.c
> --- rc10-pre5/mm/memory.c Tue Oct 24 01:27:38 2000
> +++ rc10-pre5-size_in_pages/mm/memory.c Wed Oct 25 17:44:14 2000
> @@ -979,6 +979,7 @@
> if (inode->i_size < offset)
> goto do_expand;
> inode->i_size = offset;
> + update_cached_size(inode);
> truncate_inode_pages(mapping, offset);
> spin_lock(&mapping->i_shared_lock);
> if (!mapping->i_mmap && !mapping->i_mmap_shared)
> @@ -1013,6 +1014,7 @@
> }
> }
> inode->i_size = offset;
> + update_cached_size(inode);
> if (inode->i_op && inode->i_op->truncate)
> inode->i_op->truncate(inode);
> out:
>
> -
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> Please read the FAQ at http://www.tux.org/lkml/
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
Please read the FAQ at http://www.tux.org/lkml/
This archive was generated by hypermail 2b29 : Tue Oct 31 2000 - 21:00:16 EST