From patchwork Thu Apr 2 20:50:46 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jeff Layton X-Patchwork-Id: 15994 Received: from lists.samba.org (mail.samba.org [66.70.73.150]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n32KpALo029593 for ; Thu, 2 Apr 2009 20:51:10 GMT Received: from dp.samba.org (localhost [127.0.0.1]) by lists.samba.org (Postfix) with ESMTP id 99FA0163C88 for ; Thu, 2 Apr 2009 20:50:52 +0000 (GMT) X-Spam-Checker-Version: SpamAssassin 3.1.7 (2006-10-05) on dp.samba.org X-Spam-Level: X-Spam-Status: No, score=-3.8 required=3.8 tests=AWL, BAYES_00, SPF_HELO_PASS, SPF_PASS autolearn=ham version=3.1.7 X-Original-To: linux-cifs-client@lists.samba.org Delivered-To: linux-cifs-client@lists.samba.org Received: from mx2.redhat.com (mx2.redhat.com [66.187.237.31]) by lists.samba.org (Postfix) with ESMTP id 37AF2163BC8 for ; Thu, 2 Apr 2009 20:50:32 +0000 (GMT) Received: from int-mx2.corp.redhat.com (int-mx2.corp.redhat.com [172.16.27.26]) by mx2.redhat.com (8.13.8/8.13.8) with ESMTP id n32Komld026375; Thu, 2 Apr 2009 16:50:48 -0400 Received: from ns3.rdu.redhat.com (ns3.rdu.redhat.com [10.11.255.199]) by int-mx2.corp.redhat.com (8.13.1/8.13.1) with ESMTP id n32KomTL008198; Thu, 2 Apr 2009 16:50:48 -0400 Received: from dantu.rdu.redhat.com (dantu.rdu.redhat.com [10.11.228.66]) by ns3.rdu.redhat.com (8.13.8/8.13.8) with ESMTP id n32Kol47031692; Thu, 2 Apr 2009 16:50:47 -0400 Received: from dantu.rdu.redhat.com ([127.0.0.1]) by dantu.rdu.redhat.com (8.13.8/8.13.8) with ESMTP id n32KokG0026025; Thu, 2 Apr 2009 16:50:46 -0400 Received: (from jlayton@localhost) by dantu.rdu.redhat.com (8.13.8/8.13.8/Submit) id n32KokAh026024; Thu, 2 Apr 2009 16:50:46 -0400 From: Jeff Layton To: smfrench@gmail.com Date: Thu, 2 Apr 2009 16:50:46 -0400 Message-Id: <1238705446-26004-1-git-send-email-jlayton@redhat.com> X-Scanned-By: MIMEDefang 2.58 on 172.16.27.26 Cc: linux-cifs-client@lists.samba.org Subject: [linux-cifs-client] [PATCH] cifs: vary timeout on writes past EOF based on offset (try #2) X-BeenThere: linux-cifs-client@lists.samba.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: The Linux CIFS VFS client List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linux-cifs-client-bounces+patchwork-cifs-client=patchwork.kernel.org@lists.samba.org Errors-To: linux-cifs-client-bounces+patchwork-cifs-client=patchwork.kernel.org@lists.samba.org This is the second version of this patch, the first didn't set the file size when it was explicitly set via truncate(). Track the size of the file on the server separate from the i_size, and then use this info to semi-intelligently set the timeout for writes past the EOF. This prevents timeouts when trying to write large sparse files on windows servers. Signed-off-by: Jeff Layton --- fs/cifs/cifsfs.c | 1 + fs/cifs/cifsglob.h | 1 + fs/cifs/cifssmb.c | 4 ++-- fs/cifs/file.c | 50 ++++++++++++++++++++++++++++++++++++-------------- fs/cifs/inode.c | 8 +++++--- 5 files changed, 45 insertions(+), 19 deletions(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 38491fd..30f2efc 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -316,6 +316,7 @@ cifs_alloc_inode(struct super_block *sb) cifs_inode->clientCanCacheAll = false; cifs_inode->delete_pending = false; cifs_inode->vfs_inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */ + cifs_inode->end_of_file = 0; /* Can not set i_flags here - they get immediately overwritten to zero by the VFS */ diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 9fbf4df..954205a 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -370,6 +370,7 @@ struct cifsInodeInfo { bool clientCanCacheAll:1; /* read and writebehind oplock */ bool oplockPending:1; bool delete_pending:1; /* DELETE_ON_CLOSE is set */ + u64 end_of_file; struct inode vfs_inode; }; diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 3f36b1e..a0845dc 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -1626,6 +1626,8 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, int smb_hdr_len; int resp_buf_type = 0; + *nbytes = 0; + cFYI(1, ("write2 at %lld %d bytes", (long long)offset, count)); if (tcon->ses->capabilities & CAP_LARGE_FILES) { @@ -1682,11 +1684,9 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, cifs_stats_inc(&tcon->num_writes); if (rc) { cFYI(1, ("Send error Write2 = %d", rc)); - *nbytes = 0; } else if (resp_buf_type == 0) { /* presumably this can not happen, but best to be safe */ rc = -EIO; - *nbytes = 0; } else { WRITE_RSP *pSMBr = (WRITE_RSP *)iov[0].iov_base; *nbytes = le16_to_cpu(pSMBr->CountHigh); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 81747ac..d9cda9f 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -971,6 +971,27 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) return rc; } +/* + * Set the timeout on write requests past EOF. For some servers (Windows) + * these calls can be very long. To my knowledge, NTFS doesn't have sparse + * files so it has to zero-fill the holes. Pfft. + * + * If we're writing 10M past the EOF we give a 180s timeout. Anything less + * than that gets a 45s timeout. Writes not past EOF get 15s timeouts. + * The 10M cutoff is totally arbitrary. A better scheme for this would be + * welcome. + */ +static int +cifs_write_timeout(struct cifsInodeInfo *cifsi, loff_t offset) +{ + if (offset <= cifsi->end_of_file) + return CIFS_STD_OP; + else if (offset > (cifsi->end_of_file + (10 * 1024 * 1024))) + return CIFS_VLONG_OP; + else + return CIFS_LONG_OP; +} + ssize_t cifs_user_write(struct file *file, const char __user *write_data, size_t write_size, loff_t *poffset) { @@ -981,6 +1002,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, struct cifsTconInfo *pTcon; int xid, long_op; struct cifsFileInfo *open_file; + struct cifsInodeInfo *cifsi = CIFS_I(file->f_path.dentry->d_inode); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); @@ -1000,11 +1022,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, xid = GetXid(); - if (*poffset > file->f_path.dentry->d_inode->i_size) - long_op = CIFS_VLONG_OP; /* writes past EOF take long time */ - else - long_op = CIFS_LONG_OP; - + long_op = cifs_write_timeout(cifsi, *poffset); for (total_written = 0; write_size > total_written; total_written += bytes_written) { rc = -EAGAIN; @@ -1048,8 +1066,10 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, FreeXid(xid); return rc; } - } else + } else { *poffset += bytes_written; + cifsi->end_of_file += bytes_written; + } long_op = CIFS_STD_OP; /* subsequent writes fast - 15 seconds is plenty */ } @@ -1085,6 +1105,7 @@ static ssize_t cifs_write(struct file *file, const char *write_data, struct cifsTconInfo *pTcon; int xid, long_op; struct cifsFileInfo *open_file; + struct cifsInodeInfo *cifsi = CIFS_I(file->f_path.dentry->d_inode); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); @@ -1099,11 +1120,7 @@ static ssize_t cifs_write(struct file *file, const char *write_data, xid = GetXid(); - if (*poffset > file->f_path.dentry->d_inode->i_size) - long_op = CIFS_VLONG_OP; /* writes past EOF can be slow */ - else - long_op = CIFS_LONG_OP; - + long_op = cifs_write_timeout(cifsi, *poffset); for (total_written = 0; write_size > total_written; total_written += bytes_written) { rc = -EAGAIN; @@ -1166,8 +1183,10 @@ static ssize_t cifs_write(struct file *file, const char *write_data, FreeXid(xid); return rc; } - } else + } else { *poffset += bytes_written; + cifsi->end_of_file += bytes_written; + } long_op = CIFS_STD_OP; /* subsequent writes fast - 15 seconds is plenty */ } @@ -1380,11 +1399,12 @@ static int cifs_writepages(struct address_space *mapping, int nr_pages; __u64 offset = 0; struct cifsFileInfo *open_file; + struct cifsInodeInfo *cifsi = CIFS_I(mapping->host); struct page *page; struct pagevec pvec; int rc = 0; int scanned = 0; - int xid; + int xid, long_op; cifs_sb = CIFS_SB(mapping->host->i_sb); @@ -1528,12 +1548,14 @@ retry: cERROR(1, ("No writable handles for inode")); rc = -EBADF; } else { + long_op = cifs_write_timeout(cifsi, offset); rc = CIFSSMBWrite2(xid, cifs_sb->tcon, open_file->netfid, bytes_to_write, offset, &bytes_written, iov, n_iov, - CIFS_LONG_OP); + long_op); atomic_dec(&open_file->wrtPending); + cifsi->end_of_file += bytes_written; if (rc || bytes_written < bytes_to_write) { cERROR(1, ("Write2 ret %d, wrote %d", rc, bytes_written)); diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 8c794be..e082766 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -143,6 +143,7 @@ static void cifs_unix_info_to_inode(struct inode *inode, inode->i_nlink = le64_to_cpu(info->Nlinks); + cifsInfo->end_of_file = end_of_file; spin_lock(&inode->i_lock); if (is_size_safe_to_change(cifsInfo, end_of_file)) { /* @@ -606,12 +607,12 @@ int cifs_get_inode_info(struct inode **pinode, inode->i_mode |= S_IFREG; } + cifsInfo->end_of_file = le64_to_cpu(pfindData->EndOfFile); spin_lock(&inode->i_lock); - if (is_size_safe_to_change(cifsInfo, - le64_to_cpu(pfindData->EndOfFile))) { + if (is_size_safe_to_change(cifsInfo, cifsInfo->end_of_file)) { /* can not safely shrink the file size here if the client is writing to it due to potential races */ - i_size_write(inode, le64_to_cpu(pfindData->EndOfFile)); + i_size_write(inode, cifsInfo->end_of_file); /* 512 bytes (2**9) is the fake blocksize that must be used for this calculation */ @@ -1755,6 +1756,7 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, } if (rc == 0) { + cifsInode->end_of_file = attrs->ia_size; rc = cifs_vmtruncate(inode, attrs->ia_size); cifs_truncate_page(inode->i_mapping, inode->i_size); }