From 9ad1506b42c828dff0b9d8f3914e1f837734e91c Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Fri, 8 Apr 2011 05:29:10 +0400 Subject: CIFS: Add launder_page operation (try #3) Add this let us drop filemap_write_and_wait from cifs_invalidate_mapping and simplify the code to properly process invalidate logic. Reviewed-by: Jeff Layton Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/file.c | 48 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 6 deletions(-) (limited to 'fs/cifs/file.c') diff --git a/fs/cifs/file.c b/fs/cifs/file.c index faf59529e84..00b466e667a 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1420,9 +1420,10 @@ retry_write: return rc; } -static int cifs_writepage(struct page *page, struct writeback_control *wbc) +static int +cifs_writepage_locked(struct page *page, struct writeback_control *wbc) { - int rc = -EFAULT; + int rc; int xid; xid = GetXid(); @@ -1442,15 +1443,29 @@ static int cifs_writepage(struct page *page, struct writeback_control *wbc) * to fail to update with the state of the page correctly. */ set_page_writeback(page); +retry_write: rc = cifs_partialpagewrite(page, 0, PAGE_CACHE_SIZE); - SetPageUptodate(page); /* BB add check for error and Clearuptodate? */ - unlock_page(page); + if (rc == -EAGAIN && wbc->sync_mode == WB_SYNC_ALL) + goto retry_write; + else if (rc == -EAGAIN) + redirty_page_for_writepage(wbc, page); + else if (rc != 0) + SetPageError(page); + else + SetPageUptodate(page); end_page_writeback(page); page_cache_release(page); FreeXid(xid); return rc; } +static int cifs_writepage(struct page *page, struct writeback_control *wbc) +{ + int rc = cifs_writepage_locked(page, wbc); + unlock_page(page); + return rc; +} + static int cifs_write_end(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned copied, struct page *page, void *fsdata) @@ -2415,6 +2430,27 @@ static void cifs_invalidate_page(struct page *page, unsigned long offset) cifs_fscache_invalidate_page(page, &cifsi->vfs_inode); } +static int cifs_launder_page(struct page *page) +{ + int rc = 0; + loff_t range_start = page_offset(page); + loff_t range_end = range_start + (loff_t)(PAGE_CACHE_SIZE - 1); + struct writeback_control wbc = { + .sync_mode = WB_SYNC_ALL, + .nr_to_write = 0, + .range_start = range_start, + .range_end = range_end, + }; + + cFYI(1, "Launder page: %p", page); + + if (clear_page_dirty_for_io(page)) + rc = cifs_writepage_locked(page, &wbc); + + cifs_fscache_invalidate_page(page, page->mapping->host); + return rc; +} + void cifs_oplock_break(struct work_struct *work) { struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo, @@ -2486,7 +2522,7 @@ const struct address_space_operations cifs_addr_ops = { .set_page_dirty = __set_page_dirty_nobuffers, .releasepage = cifs_release_page, .invalidatepage = cifs_invalidate_page, - /* .direct_IO = */ + .launder_page = cifs_launder_page, }; /* @@ -2503,5 +2539,5 @@ const struct address_space_operations cifs_addr_ops_smallbuf = { .set_page_dirty = __set_page_dirty_nobuffers, .releasepage = cifs_release_page, .invalidatepage = cifs_invalidate_page, - /* .direct_IO = */ + .launder_page = cifs_launder_page, }; -- cgit v1.2.3-70-g09d2 From 0b81c1c405c063f3ecea66c2f5e9c3aefc5359c8 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Thu, 10 Mar 2011 10:11:05 +0300 Subject: CIFS: directio read/write cleanups Recently introduced strictcache mode brought a new code that can be efficiently used by directio part. That's let us add vectored operations and break unnecessary cifs_user_read and cifs_user_write. Signed-off-by: Pavel Shilovsky Reviewed-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsfs.c | 18 +++++----- fs/cifs/cifsfs.h | 8 ++--- fs/cifs/file.c | 103 ++----------------------------------------------------- 3 files changed, 16 insertions(+), 113 deletions(-) (limited to 'fs/cifs/file.c') diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 6c1d738418f..f736d8a2e77 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -760,10 +760,11 @@ const struct file_operations cifs_file_strict_ops = { }; const struct file_operations cifs_file_direct_ops = { - /* no aio, no readv - - BB reevaluate whether they can be done with directio, no cache */ - .read = cifs_user_read, - .write = cifs_user_write, + /* BB reevaluate whether they can be done with directio, no cache */ + .read = do_sync_read, + .write = do_sync_write, + .aio_read = cifs_user_readv, + .aio_write = cifs_user_writev, .open = cifs_open, .release = cifs_close, .lock = cifs_lock, @@ -815,10 +816,11 @@ const struct file_operations cifs_file_strict_nobrl_ops = { }; const struct file_operations cifs_file_direct_nobrl_ops = { - /* no mmap, no aio, no readv - - BB reevaluate whether they can be done with directio, no cache */ - .read = cifs_user_read, - .write = cifs_user_write, + /* BB reevaluate whether they can be done with directio, no cache */ + .read = do_sync_read, + .write = do_sync_write, + .aio_read = cifs_user_readv, + .aio_write = cifs_user_writev, .open = cifs_open, .release = cifs_close, .fsync = cifs_fsync, diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 371d021815b..bb64313fd75 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -80,12 +80,12 @@ extern const struct file_operations cifs_file_strict_nobrl_ops; extern int cifs_open(struct inode *inode, struct file *file); extern int cifs_close(struct inode *inode, struct file *file); extern int cifs_closedir(struct inode *inode, struct file *file); -extern ssize_t cifs_user_read(struct file *file, char __user *read_data, - size_t read_size, loff_t *poffset); +extern ssize_t cifs_user_readv(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t pos); extern ssize_t cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos); -extern ssize_t cifs_user_write(struct file *file, const char __user *write_data, - size_t write_size, loff_t *poffset); +extern ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t pos); extern ssize_t cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos); extern int cifs_lock(struct file *, int, struct file_lock *); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 00b466e667a..0aeaaf7bf15 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -857,95 +857,6 @@ cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset, cifsi->server_eof = end_of_write; } -ssize_t cifs_user_write(struct file *file, const char __user *write_data, - size_t write_size, loff_t *poffset) -{ - struct inode *inode = file->f_path.dentry->d_inode; - int rc = 0; - unsigned int bytes_written = 0; - unsigned int total_written; - struct cifs_sb_info *cifs_sb; - struct cifsTconInfo *pTcon; - int xid; - struct cifsFileInfo *open_file; - struct cifsInodeInfo *cifsi = CIFS_I(inode); - - cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); - - /* cFYI(1, " write %d bytes to offset %lld of %s", write_size, - *poffset, file->f_path.dentry->d_name.name); */ - - if (file->private_data == NULL) - return -EBADF; - - open_file = file->private_data; - pTcon = tlink_tcon(open_file->tlink); - - rc = generic_write_checks(file, poffset, &write_size, 0); - if (rc) - return rc; - - xid = GetXid(); - - for (total_written = 0; write_size > total_written; - total_written += bytes_written) { - rc = -EAGAIN; - while (rc == -EAGAIN) { - if (file->private_data == NULL) { - /* file has been closed on us */ - FreeXid(xid); - /* if we have gotten here we have written some data - and blocked, and the file has been freed on us while - we blocked so return what we managed to write */ - return total_written; - } - if (open_file->invalidHandle) { - /* we could deadlock if we called - filemap_fdatawait from here so tell - reopen_file not to flush data to server - now */ - rc = cifs_reopen_file(open_file, false); - if (rc != 0) - break; - } - - rc = CIFSSMBWrite(xid, pTcon, - open_file->netfid, - min_t(const int, cifs_sb->wsize, - write_size - total_written), - *poffset, &bytes_written, - NULL, write_data + total_written, 0); - } - if (rc || (bytes_written == 0)) { - if (total_written) - break; - else { - FreeXid(xid); - return rc; - } - } else { - cifs_update_eof(cifsi, *poffset, bytes_written); - *poffset += bytes_written; - } - } - - cifs_stats_bytes_written(pTcon, total_written); - -/* Do not update local mtime - server will set its actual value on write - * inode->i_ctime = inode->i_mtime = - * current_fs_time(inode->i_sb);*/ - if (total_written > 0) { - spin_lock(&inode->i_lock); - if (*poffset > inode->i_size) - i_size_write(inode, *poffset); - spin_unlock(&inode->i_lock); - } - mark_inode_dirty_sync(inode); - - FreeXid(xid); - return total_written; -} - static ssize_t cifs_write(struct cifsFileInfo *open_file, const char *write_data, size_t write_size, loff_t *poffset) @@ -1741,7 +1652,7 @@ cifs_iovec_write(struct file *file, const struct iovec *iov, return total_written; } -static ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov, +ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos) { ssize_t written; @@ -1864,17 +1775,7 @@ cifs_iovec_read(struct file *file, const struct iovec *iov, return total_read; } -ssize_t cifs_user_read(struct file *file, char __user *read_data, - size_t read_size, loff_t *poffset) -{ - struct iovec iov; - iov.iov_base = read_data; - iov.iov_len = read_size; - - return cifs_iovec_read(file, &iov, 1, poffset); -} - -static ssize_t cifs_user_readv(struct kiocb *iocb, const struct iovec *iov, +ssize_t cifs_user_readv(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos) { ssize_t read; -- cgit v1.2.3-70-g09d2 From 6feb9891da4f8b04ffca69c00eb56bb7c1b64dc4 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Thu, 7 Apr 2011 18:18:11 +0400 Subject: CIFS: Simplify invalidate part (try #5) Simplify many places when we call cifs_revalidate/invalidate to make it do what it exactly needs. Reviewed-by: Jeff Layton Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifsfs.c | 33 +++++++++++----- fs/cifs/cifsfs.h | 4 +- fs/cifs/file.c | 16 ++++++-- fs/cifs/inode.c | 117 +++++++++++++++++++++++++++++++++++-------------------- 4 files changed, 113 insertions(+), 57 deletions(-) (limited to 'fs/cifs/file.c') diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index f736d8a2e77..0f6a54f14ef 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -618,16 +618,29 @@ static loff_t cifs_llseek(struct file *file, loff_t offset, int origin) { /* origin == SEEK_END => we must revalidate the cached file length */ if (origin == SEEK_END) { - int retval; - - /* some applications poll for the file length in this strange - way so we must seek to end on non-oplocked files by - setting the revalidate time to zero */ - CIFS_I(file->f_path.dentry->d_inode)->time = 0; - - retval = cifs_revalidate_file(file); - if (retval < 0) - return (loff_t)retval; + int rc; + struct inode *inode = file->f_path.dentry->d_inode; + + /* + * We need to be sure that all dirty pages are written and the + * server has the newest file length. + */ + if (!CIFS_I(inode)->clientCanCacheRead && inode->i_mapping && + inode->i_mapping->nrpages != 0) { + rc = filemap_fdatawait(inode->i_mapping); + mapping_set_error(inode->i_mapping, rc); + return rc; + } + /* + * Some applications poll for the file length in this strange + * way so we must seek to end on non-oplocked files by + * setting the revalidate time to zero. + */ + CIFS_I(inode)->time = 0; + + rc = cifs_revalidate_file_attr(file); + if (rc < 0) + return (loff_t)rc; } return generic_file_llseek_unlocked(file, offset, origin); } diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index bb64313fd75..d304584408d 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -59,9 +59,11 @@ extern int cifs_mkdir(struct inode *, struct dentry *, int); extern int cifs_rmdir(struct inode *, struct dentry *); extern int cifs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); +extern int cifs_revalidate_file_attr(struct file *filp); +extern int cifs_revalidate_dentry_attr(struct dentry *); extern int cifs_revalidate_file(struct file *filp); extern int cifs_revalidate_dentry(struct dentry *); -extern void cifs_invalidate_mapping(struct inode *inode); +extern int cifs_invalidate_mapping(struct inode *inode); extern int cifs_getattr(struct vfsmount *, struct dentry *, struct kstat *); extern int cifs_setattr(struct dentry *, struct iattr *); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 0aeaaf7bf15..c672afef0c0 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1445,8 +1445,13 @@ int cifs_strict_fsync(struct file *file, int datasync) cFYI(1, "Sync file - name: %s datasync: 0x%x", file->f_path.dentry->d_name.name, datasync); - if (!CIFS_I(inode)->clientCanCacheRead) - cifs_invalidate_mapping(inode); + if (!CIFS_I(inode)->clientCanCacheRead) { + rc = cifs_invalidate_mapping(inode); + if (rc) { + cFYI(1, "rc: %d during invalidate phase", rc); + rc = 0; /* don't care about it in fsync */ + } + } tcon = tlink_tcon(smbfile->tlink); if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) @@ -1903,8 +1908,11 @@ int cifs_file_strict_mmap(struct file *file, struct vm_area_struct *vma) xid = GetXid(); - if (!CIFS_I(inode)->clientCanCacheRead) - cifs_invalidate_mapping(inode); + if (!CIFS_I(inode)->clientCanCacheRead) { + rc = cifs_invalidate_mapping(inode); + if (rc) + return rc; + } rc = generic_file_mmap(file, vma); if (rc == 0) diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index fbe7d585856..0cc7eddb077 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1683,18 +1683,15 @@ cifs_inode_needs_reval(struct inode *inode) /* * Zap the cache. Called when invalid_mapping flag is set. */ -void +int cifs_invalidate_mapping(struct inode *inode) { - int rc; + int rc = 0; struct cifsInodeInfo *cifs_i = CIFS_I(inode); cifs_i->invalid_mapping = false; if (inode->i_mapping && inode->i_mapping->nrpages != 0) { - /* write back any cached data */ - rc = filemap_write_and_wait(inode->i_mapping); - mapping_set_error(inode->i_mapping, rc); rc = invalidate_inode_pages2(inode->i_mapping); if (rc) { cERROR(1, "%s: could not invalidate inode %p", __func__, @@ -1704,56 +1701,52 @@ cifs_invalidate_mapping(struct inode *inode) } cifs_fscache_reset_inode_cookie(inode); + return rc; } -int cifs_revalidate_file(struct file *filp) +int cifs_revalidate_file_attr(struct file *filp) { int rc = 0; struct inode *inode = filp->f_path.dentry->d_inode; struct cifsFileInfo *cfile = (struct cifsFileInfo *) filp->private_data; if (!cifs_inode_needs_reval(inode)) - goto check_inval; + return rc; if (tlink_tcon(cfile->tlink)->unix_ext) rc = cifs_get_file_info_unix(filp); else rc = cifs_get_file_info(filp); -check_inval: - if (CIFS_I(inode)->invalid_mapping) - cifs_invalidate_mapping(inode); - return rc; } -/* revalidate a dentry's inode attributes */ -int cifs_revalidate_dentry(struct dentry *dentry) +int cifs_revalidate_dentry_attr(struct dentry *dentry) { int xid; int rc = 0; - char *full_path = NULL; struct inode *inode = dentry->d_inode; struct super_block *sb = dentry->d_sb; + char *full_path = NULL; if (inode == NULL) return -ENOENT; - xid = GetXid(); - if (!cifs_inode_needs_reval(inode)) - goto check_inval; + return rc; + + xid = GetXid(); /* can not safely grab the rename sem here if rename calls revalidate since that would deadlock */ full_path = build_path_from_dentry(dentry); if (full_path == NULL) { rc = -ENOMEM; - goto check_inval; + goto out; } - cFYI(1, "Revalidate: %s inode 0x%p count %d dentry: 0x%p d_time %ld " - "jiffies %ld", full_path, inode, inode->i_count.counter, + cFYI(1, "Update attributes: %s inode 0x%p count %d dentry: 0x%p d_time " + "%ld jiffies %ld", full_path, inode, inode->i_count.counter, dentry, dentry->d_time, jiffies); if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext) @@ -1762,41 +1755,81 @@ int cifs_revalidate_dentry(struct dentry *dentry) rc = cifs_get_inode_info(&inode, full_path, NULL, sb, xid, NULL); -check_inval: - if (CIFS_I(inode)->invalid_mapping) - cifs_invalidate_mapping(inode); - +out: kfree(full_path); FreeXid(xid); return rc; } +int cifs_revalidate_file(struct file *filp) +{ + int rc; + struct inode *inode = filp->f_path.dentry->d_inode; + + rc = cifs_revalidate_file_attr(filp); + if (rc) + return rc; + + if (CIFS_I(inode)->invalid_mapping) + rc = cifs_invalidate_mapping(inode); + return rc; +} + +/* revalidate a dentry's inode attributes */ +int cifs_revalidate_dentry(struct dentry *dentry) +{ + int rc; + struct inode *inode = dentry->d_inode; + + rc = cifs_revalidate_dentry_attr(dentry); + if (rc) + return rc; + + if (CIFS_I(inode)->invalid_mapping) + rc = cifs_invalidate_mapping(inode); + return rc; +} + int cifs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { struct cifs_sb_info *cifs_sb = CIFS_SB(dentry->d_sb); struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); - int err = cifs_revalidate_dentry(dentry); + struct inode *inode = dentry->d_inode; + int rc; - if (!err) { - generic_fillattr(dentry->d_inode, stat); - stat->blksize = CIFS_MAX_MSGSIZE; - stat->ino = CIFS_I(dentry->d_inode)->uniqueid; + /* + * We need to be sure that all dirty pages are written and the server + * has actual ctime, mtime and file length. + */ + if (!CIFS_I(inode)->clientCanCacheRead && inode->i_mapping && + inode->i_mapping->nrpages != 0) { + rc = filemap_fdatawait(inode->i_mapping); + mapping_set_error(inode->i_mapping, rc); + return rc; + } - /* - * If on a multiuser mount without unix extensions, and the - * admin hasn't overridden them, set the ownership to the - * fsuid/fsgid of the current process. - */ - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) && - !tcon->unix_ext) { - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)) - stat->uid = current_fsuid(); - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) - stat->gid = current_fsgid(); - } + rc = cifs_revalidate_dentry_attr(dentry); + if (rc) + return rc; + + generic_fillattr(inode, stat); + stat->blksize = CIFS_MAX_MSGSIZE; + stat->ino = CIFS_I(inode)->uniqueid; + + /* + * If on a multiuser mount without unix extensions, and the admin hasn't + * overridden them, set the ownership to the fsuid/fsgid of the current + * process. + */ + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) && + !tcon->unix_ext) { + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)) + stat->uid = current_fsuid(); + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) + stat->gid = current_fsgid(); } - return err; + return rc; } static int cifs_truncate_page(struct address_space *mapping, loff_t from) -- cgit v1.2.3-70-g09d2