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/

Reply via email to