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 [EMAIL PROTECTED]
> 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 [EMAIL PROTECTED]
Please read the FAQ at http://www.tux.org/lkml/

Reply via email to