From: Vladimir Saveliev This patch fixes an error in readpage operation for files whose data are stored in metadata blocks: page did not get padded with 0s properly. Signed-off-by: Vladimir Saveliev Signed-off-by: Hans Reiser Signed-off-by: Andrew Morton --- fs/reiser4/plugin/file/file.c | 160 ++------------------- fs/reiser4/plugin/item/extent_file_ops.c | 60 +++++-- 2 files changed, 57 insertions(+), 163 deletions(-) diff -puN fs/reiser4/plugin/file/file.c~reiser4-do-not-use-get_user_pages-and-do-not-check fs/reiser4/plugin/file/file.c --- devel/fs/reiser4/plugin/file/file.c~reiser4-do-not-use-get_user_pages-and-do-not-check 2006-01-25 11:27:13.000000000 -0800 +++ devel-akpm/fs/reiser4/plugin/file/file.c 2006-01-25 11:27:13.000000000 -0800 @@ -1910,72 +1910,6 @@ static reiser4_block_nr unix_file_estima return estimate_update_common(inode); } -#define NR_PAGES_TO_PIN 8 - -static int -get_nr_pages_nr_bytes(unsigned long addr, size_t count, int *nr_pages) -{ - int nr_bytes; - - /* number of pages through which count bytes starting of address addr - are spread */ - *nr_pages = ((addr + count + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT) - - (addr >> PAGE_CACHE_SHIFT); - if (*nr_pages > NR_PAGES_TO_PIN) { - *nr_pages = NR_PAGES_TO_PIN; - nr_bytes = - (*nr_pages * PAGE_CACHE_SIZE) - - (addr & (PAGE_CACHE_SIZE - 1)); - } else - nr_bytes = count; - - return nr_bytes; -} - -/** - * adjust_nr_bytes - recalcualte number of bytes more accurately - * @addr: address of user space buffer - * @count: number of bytes to be written - * @nr_pages: number of pages faulted into pagetables - * - * Sometimes get_user_pages "gets" less pages than it is asked for. When this - * happens we have to recalculate number of bytes which will be written/read in - * one iteration of read/write. - */ -static size_t adjust_nr_bytes(unsigned long addr, size_t count, int nr_pages) -{ - size_t bytes; - - bytes = 0; - if (addr % PAGE_CACHE_SIZE) { - nr_pages --; - bytes = PAGE_CACHE_SIZE - (addr % PAGE_CACHE_SIZE); - } - bytes += nr_pages * PAGE_CACHE_SIZE; - if (count > bytes) - return bytes; - return count; -} - -static int -reiser4_get_user_pages(struct page **pages, unsigned long addr, int nr_pages, - int rw) -{ - down_read(¤t->mm->mmap_sem); - nr_pages = get_user_pages(current, current->mm, addr, - nr_pages, (rw == READ), 0, pages, NULL); - up_read(¤t->mm->mmap_sem); - return nr_pages; -} - -static void reiser4_put_user_pages(struct page **pages, int nr_pages) -{ - int i; - - for (i = 0; i < nr_pages; i++) - page_cache_release(pages[i]); -} - /* this is called with nonexclusive access obtained, file's container can not change */ static size_t read_file(hint_t * hint, struct file *file, /* file to read from to */ char __user *buf, /* address of user-space buffer */ @@ -2039,11 +1973,6 @@ static size_t read_file(hint_t * hint, s return (count - flow.length) ? (count - flow.length) : result; } -static int is_user_space(const char __user *buf) -{ - return (unsigned long)buf < PAGE_OFFSET; -} - /** * read_unix_file - read of struct file_operations * @file: file to read from @@ -2063,12 +1992,9 @@ read_unix_file(struct file *file, char _ struct inode *inode; hint_t *hint; unix_file_info_t *uf_info; - struct page *pages[NR_PAGES_TO_PIN]; - int nr_pages; size_t count, read, left; reiser4_block_nr needed; loff_t size; - int user_space; if (unlikely(read_amount == 0)) return 0; @@ -2099,61 +2025,31 @@ read_unix_file(struct file *file, char _ left = read_amount; count = 0; - user_space = is_user_space(buf); - nr_pages = 0; uf_info = unix_file_inode_data(inode); while (left > 0) { - unsigned long addr; - size_t to_read; - - addr = (unsigned long)buf; txn_restart_current(); + get_nonexclusive_access(uf_info, 0); + size = i_size_read(inode); - if (*off >= size) + if (*off >= size) { /* position to read from is past the end of file */ + drop_nonexclusive_access(uf_info); break; + } if (*off + left > size) left = size - *off; - if (user_space) { - memset(pages, 0, sizeof(pages)); - to_read = get_nr_pages_nr_bytes(addr, left, &nr_pages); - nr_pages = - reiser4_get_user_pages(pages, addr, nr_pages, READ); - if (nr_pages < 0) { - result = nr_pages; - break; - } - to_read = adjust_nr_bytes(addr, to_read, nr_pages); - /* get_user_pages might create a transaction */ - txn_restart_current(); - } else - to_read = left; - - get_nonexclusive_access(uf_info, 0); - - /* define more precisely read size now when filesize can not change */ - if (*off >= inode->i_size) { - if (user_space) - reiser4_put_user_pages(pages, nr_pages); - - /* position to read from is past the end of file */ + /* faultin user page */ + result = fault_in_pages_writeable(buf, left > PAGE_CACHE_SIZE ? PAGE_CACHE_SIZE : left); + if (result) { drop_nonexclusive_access(uf_info); - break; + return RETERR(-EFAULT); } - if (*off + left > inode->i_size) - left = inode->i_size - *off; - if (*off + to_read > inode->i_size) - to_read = inode->i_size - *off; - - assert("vs-1706", to_read <= left); - read = read_file(hint, file, buf, to_read, off); - if (user_space) - reiser4_put_user_pages(pages, nr_pages); + read = read_file(hint, file, buf, left, off); - drop_nonexclusive_access(uf_info); + drop_nonexclusive_access(uf_info); if (read < 0) { result = read; @@ -2531,10 +2427,7 @@ ssize_t write_unix_file(struct file *fil struct inode *inode; hint_t *hint; unix_file_info_t *uf_info; - struct page *pages[NR_PAGES_TO_PIN]; - int nr_pages; size_t count, written, left; - int user_space; int try_free_space; if (unlikely(write_amount == 0)) @@ -2640,35 +2533,18 @@ ssize_t write_unix_file(struct file *fil left = write_amount; count = 0; - user_space = is_user_space(buf); - nr_pages = 0; try_free_space = 1; while (left > 0) { - unsigned long addr; - size_t to_write; int excl = 0; - addr = (unsigned long)buf; - /* getting exclusive or not exclusive access requires no transaction open */ txn_restart_current(); - if (user_space) { - to_write = get_nr_pages_nr_bytes(addr, left, &nr_pages); - nr_pages = - reiser4_get_user_pages(pages, addr, nr_pages, - WRITE); - if (nr_pages < 0) { - result = nr_pages; - break; - } - to_write = adjust_nr_bytes(addr, to_write, nr_pages); - /* get_user_pages might create a transaction */ - txn_restart_current(); - } else - to_write = left; + /* faultin user page */ + fault_in_pages_readable(buf, + left > PAGE_CACHE_SIZE ? PAGE_CACHE_SIZE : left); if (inode->i_size == 0) { get_exclusive_access(uf_info); @@ -2679,10 +2555,7 @@ ssize_t write_unix_file(struct file *fil } all_grabbed2free(); - written = write_file(hint, file, buf, to_write, off, excl); - if (user_space) - reiser4_put_user_pages(pages, nr_pages); - + written = write_file(hint, file, buf, left, off, excl); if (excl) drop_exclusive_access(uf_info); else @@ -2706,7 +2579,7 @@ ssize_t write_unix_file(struct file *fil count += written; } - if ((file->f_flags & O_SYNC) || IS_SYNC(inode)) { + if (result == 0 && ((file->f_flags & O_SYNC) || IS_SYNC(inode))) { txn_restart_current(); grab_space_enable(); result = @@ -2725,6 +2598,7 @@ ssize_t write_unix_file(struct file *fil context_set_commit_async(ctx); reiser4_exit_context(ctx); + /* return number of written bytes or error code if nothing is written */ return count ? count : result; } diff -puN fs/reiser4/plugin/item/extent_file_ops.c~reiser4-do-not-use-get_user_pages-and-do-not-check fs/reiser4/plugin/item/extent_file_ops.c --- devel/fs/reiser4/plugin/item/extent_file_ops.c~reiser4-do-not-use-get_user_pages-and-do-not-check 2006-01-25 11:27:13.000000000 -0800 +++ devel-akpm/fs/reiser4/plugin/item/extent_file_ops.c 2006-01-25 11:27:13.000000000 -0800 @@ -754,17 +754,17 @@ static void assign_jnode_blocknr(jnode * } static int -extent_balance_dirty_pages(struct inode *inode, const flow_t * f, hint_t * hint) +extent_balance_dirty_pages(struct inode *inode, const flow_t *f,hint_t *hint) { int result; int excl; unix_file_info_t *uf_info; + /* seal twig node if possible and unlock it */ if (hint->ext_coord.valid) set_hint(hint, &f->key, ZNODE_WRITE_LOCK); else unset_hint(hint); - /* &hint->lh is done-ed */ /* file was appended, update its size */ if (get_key_offset(&f->key) > inode->i_size) { @@ -772,28 +772,38 @@ extent_balance_dirty_pages(struct inode INODE_SET_FIELD(inode, i_size, get_key_offset(&f->key)); } if (f->user != 0) { - /* this was writing data from user space. Update timestamps, - therefore. Othrewise, this is tail conversion where we - should not update timestamps */ + /* + * this was writing data from user space. Update timestamps, + * therefore. Othrewise, this is tail conversion where we + * should not update timestamps + */ inode->i_ctime = inode->i_mtime = CURRENT_TIME; result = reiser4_update_sd(inode); if (result) return result; } - if (!reiser4_is_set(inode->i_sb, REISER4_ATOMIC_WRITE)) { + if (likely(!reiser4_is_set(inode->i_sb, REISER4_ATOMIC_WRITE))) { uf_info = unix_file_inode_data(inode); excl = unix_file_inode_data(inode)->exclusive_use; if (excl) { - /* we are about to drop exclusive access. Set file - container to UF_CONTAINER_EXTENTS if file is not - under tail conversion */ + /* + * we are about to drop exclusive access. Set file + * container to UF_CONTAINER_EXTENTS if file is not + * under tail conversion + */ if (!inode_get_flag(inode, REISER4_PART_CONV)) uf_info->container = UF_CONTAINER_EXTENTS; drop_exclusive_access(uf_info); } else drop_nonexclusive_access(uf_info); reiser4_throttle_write(inode); + + /* faultin next user page */ + fault_in_pages_readable(f->data, + f->length > PAGE_CACHE_SIZE ? + PAGE_CACHE_SIZE : f->length); + if (excl) get_exclusive_access(uf_info); else @@ -1400,7 +1410,7 @@ static int filler(void *vp, struct page } /* Implements plugin->u.item.s.file.read operation for extent items. */ -int read_extent(struct file *file, flow_t * flow, hint_t * hint) +int read_extent(struct file *file, flow_t *flow, hint_t *hint) { int result; struct page *page; @@ -1413,6 +1423,7 @@ int read_extent(struct file *file, flow_ extent_coord_extension_t *ext_coord; unsigned long nr_pages, prev_page; struct file_ra_state ra; + char *kaddr; assert("vs-1353", current_blocksize == PAGE_CACHE_SIZE); assert("vs-572", flow->user == 1); @@ -1507,23 +1518,32 @@ int read_extent(struct file *file, flow_ /* number of bytes which are to be read from the page */ if (count > flow->length) count = flow->length; - /* user area is already get_user_pages-ed in read_unix_file, - which makes major page faults impossible */ - result = - __copy_to_user((char __user *)flow->data, - (char *)kmap(page) + page_off, - count); - kunmap(page); - page_cache_release(page); - if (unlikely(result)) + result = fault_in_pages_writeable(flow->data, count); + if (result) { + page_cache_release(page); return RETERR(-EFAULT); + } + + kaddr = kmap_atomic(page, KM_USER0); + result = __copy_to_user_inatomic(flow->data, + kaddr + page_off, count); + kunmap_atomic(kaddr, KM_USER0); + if (result != 0) { + kaddr = kmap(page); + result = __copy_to_user(flow->data, kaddr + page_off, count); + kunmap(page); + if (unlikely(result)) + return RETERR(-EFAULT); + } + + page_cache_release(page); /* increase key (flow->key), update user area pointer (flow->data) */ move_flow_forward(flow, count); page_off = 0; - cur_page++; + cur_page ++; count = PAGE_CACHE_SIZE; nr_pages--; } while (flow->length); _