2011/9/6 Jeff Layton <jlayton@xxxxxxxxxx>: > Currently we cap the rsize at a value that fits in CIFSMaxBufSize. That's > not needed any longer for readpages. Allow the use of larger values for > readpages. cifs_iovec_read and cifs_read however are still limited to the > CIFSMaxBufSize. Make sure they don't exceed that. > > The patch also changes the rsize defaults. The default when unix > extensions are enabled is set to 1M for parity with the wsize, and there > is a hard cap of ~16M. > > When unix extensions are not enabled, the default is set to 60k. According > to MS-CIFS, Windows servers can only send a max of 60k at a time, so > this is more efficient than requesting a larger size. If the user wishes > however, the max can be extended up to 128k - the length of the READ_RSP > header. > > Really old servers however require a special hack to ensure that we don't > request too large a read. > > Signed-off-by: Jeff Layton <jlayton@xxxxxxxxxx> > --- > fs/cifs/connect.c | 118 ++++++++++++++++++++++++++++++++--------------------- > fs/cifs/file.c | 12 +++++- > 2 files changed, 82 insertions(+), 48 deletions(-) > > diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c > index 6f663ea..02bc0e2 100644 > --- a/fs/cifs/connect.c > +++ b/fs/cifs/connect.c > @@ -2292,16 +2292,16 @@ compare_mount_options(struct super_block *sb, struct cifs_mnt_data *mnt_data) > (new->mnt_cifs_flags & CIFS_MOUNT_MASK)) > return 0; > > - if (old->rsize != new->rsize) > - return 0; > - > /* > - * We want to share sb only if we don't specify wsize or specified wsize > - * is greater or equal than existing one. > + * We want to share sb only if we don't specify an r/wsize or > + * specified r/wsize is greater than or equal to existing one. > */ > if (new->wsize && new->wsize < old->wsize) > return 0; > > + if (new->rsize && new->rsize < old->rsize) > + return 0; > + > if (old->mnt_uid != new->mnt_uid || old->mnt_gid != new->mnt_gid) > return 0; > > @@ -2739,14 +2739,6 @@ void reset_cifs_unix_caps(int xid, struct cifs_tcon *tcon, > CIFS_MOUNT_POSIX_PATHS; > } > > - if (cifs_sb && (cifs_sb->rsize > 127 * 1024)) { > - if ((cap & CIFS_UNIX_LARGE_READ_CAP) == 0) { > - cifs_sb->rsize = 127 * 1024; > - cFYI(DBG2, "larger reads not supported by srv"); > - } > - } > - > - > cFYI(1, "Negotiate caps 0x%x", (int)cap); > #ifdef CONFIG_CIFS_DEBUG2 > if (cap & CIFS_UNIX_FCNTL_CAP) > @@ -2791,27 +2783,11 @@ void cifs_setup_cifs_sb(struct smb_vol *pvolume_info, > spin_lock_init(&cifs_sb->tlink_tree_lock); > cifs_sb->tlink_tree = RB_ROOT; > > - if (pvolume_info->rsize > CIFSMaxBufSize) { > - cERROR(1, "rsize %d too large, using MaxBufSize", > - pvolume_info->rsize); > - cifs_sb->rsize = CIFSMaxBufSize; > - } else if ((pvolume_info->rsize) && > - (pvolume_info->rsize <= CIFSMaxBufSize)) > - cifs_sb->rsize = pvolume_info->rsize; > - else /* default */ > - cifs_sb->rsize = CIFSMaxBufSize; > - > - if (cifs_sb->rsize < 2048) { > - cifs_sb->rsize = 2048; > - /* Windows ME may prefer this */ > - cFYI(1, "readsize set to minimum: 2048"); > - } > - > /* > - * Temporarily set wsize for matching superblock. If we end up using > - * new sb then cifs_negotiate_wsize will later negotiate it downward > - * if needed. > + * Temporarily set r/wsize for matching superblock. If we end up using > + * new sb then client will later negotiate it downward if needed. > */ > + cifs_sb->rsize = pvolume_info->rsize; > cifs_sb->wsize = pvolume_info->wsize; > > cifs_sb->mnt_uid = pvolume_info->linux_uid; > @@ -2878,29 +2854,41 @@ void cifs_setup_cifs_sb(struct smb_vol *pvolume_info, > } > > /* > - * When the server supports very large writes via POSIX extensions, we can > - * allow up to 2^24-1, minus the size of a WRITE_AND_X header, not including > - * the RFC1001 length. > + * When the server supports very large reads and writes via POSIX extensions, > + * we can allow up to 2^24-1, minus the size of a READ/WRITE_AND_X header, not > + * including the RFC1001 length. > * > * Note that this might make for "interesting" allocation problems during > * writeback however as we have to allocate an array of pointers for the > * pages. A 16M write means ~32kb page array with PAGE_CACHE_SIZE == 4096. > + * > + * For reads, there is a similar problem as we need to allocate an array > + * of kvecs to handle the receive, though that should only need to be done > + * once. > */ > #define CIFS_MAX_WSIZE ((1<<24) - 1 - sizeof(WRITE_REQ) + 4) > +#define CIFS_MAX_RSIZE ((1<<24) - sizeof(READ_RSP) + 4) May be (1<<24) - 1 for rsize too as it is for wsize? > > /* > - * When the server doesn't allow large posix writes, only allow a wsize of > - * 128k minus the size of the WRITE_AND_X header. That allows for a write up > + * When the server doesn't allow large posix writes, only allow a rsize/wsize of > + * 128k minus the size of the call header. That allows for a read or write up > * to the maximum size described by RFC1002. > */ > #define CIFS_MAX_RFC1002_WSIZE (128 * 1024 - sizeof(WRITE_REQ) + 4) > +#define CIFS_MAX_RFC1002_RSIZE (128 * 1024 - sizeof(READ_RSP) + 4) The same issue I reported about for the write. But as it's disable for iovec_read it should be hit - anyway, better to fix this. > > /* > * The default wsize is 1M. find_get_pages seems to return a maximum of 256 > * pages in a single call. With PAGE_CACHE_SIZE == 4k, this means we can fill > * a single wsize request with a single call. > */ > -#define CIFS_DEFAULT_WSIZE (1024 * 1024) > +#define CIFS_DEFAULT_IOSIZE (1024 * 1024) > + > +/* > + * Windows only supports a max of 60k reads. Default to that when posix > + * extensions aren't in force. > + */ > +#define CIFS_DEFAULT_NON_POSIX_RSIZE (60 *1024) > > static unsigned int > cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *pvolume_info) > @@ -2908,7 +2896,7 @@ cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *pvolume_info) > __u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); > struct TCP_Server_Info *server = tcon->ses->server; > unsigned int wsize = pvolume_info->wsize ? pvolume_info->wsize : > - CIFS_DEFAULT_WSIZE; > + CIFS_DEFAULT_IOSIZE; > > /* can server support 24-bit write sizes? (via UNIX extensions) */ > if (!tcon->unix_ext || !(unix_cap & CIFS_UNIX_LARGE_WRITE_CAP)) > @@ -2931,6 +2919,50 @@ cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *pvolume_info) > return wsize; > } > > +static unsigned int > +cifs_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *pvolume_info) > +{ > + __u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); > + struct TCP_Server_Info *server = tcon->ses->server; > + unsigned int rsize, defsize; > + > + /* > + * Set default value... > + * > + * HACK alert! Ancient servers have very small buffers. Even though > + * MS-CIFS indicates that servers are only limited by the client's > + * bufsize for reads, testing against win98se shows that it throws > + * INVALID_PARAMETER errors if you try to request too large a read. > + * > + * If the server advertises a MaxBufferSize of less than one page, > + * assume that it also can't satisfy reads larger than that either. > + * > + * FIXME: Is there a better heuristic for this? > + */ > + if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_READ_CAP)) > + defsize = CIFS_DEFAULT_IOSIZE; > + else if (server->capabilities & CAP_LARGE_READ_X) > + defsize = CIFS_DEFAULT_NON_POSIX_RSIZE; > + else if (server->maxBuf >= PAGE_CACHE_SIZE) > + defsize = CIFSMaxBufSize; > + else > + defsize = server->maxBuf - sizeof(READ_RSP); > + > + rsize = pvolume_info->rsize ? pvolume_info->rsize : defsize; > + > + /* > + * no CAP_LARGE_READ_X? Then MS-CIFS states that we must limit this to > + * the client's MaxBufferSize. > + */ > + if (!(server->capabilities & CAP_LARGE_READ_X)) > + rsize = min_t(unsigned int, CIFSMaxBufSize, rsize); > + > + /* hard limit of CIFS_MAX_RSIZE */ > + rsize = min_t(unsigned int, rsize, CIFS_MAX_RSIZE); > + > + return rsize; > +} > + > static int > is_path_accessible(int xid, struct cifs_tcon *tcon, > struct cifs_sb_info *cifs_sb, const char *full_path) > @@ -3208,14 +3240,8 @@ try_mount_again: > CIFSSMBQFSAttributeInfo(xid, tcon); > } > > - if ((tcon->unix_ext == 0) && (cifs_sb->rsize > (1024 * 127))) { > - cifs_sb->rsize = 1024 * 127; > - cFYI(DBG2, "no very large read support, rsize now 127K"); > - } > - if (!(tcon->ses->capabilities & CAP_LARGE_READ_X)) > - cifs_sb->rsize = min(cifs_sb->rsize, CIFSMaxBufSize); > - > cifs_sb->wsize = cifs_negotiate_wsize(tcon, volume_info); > + cifs_sb->rsize = cifs_negotiate_rsize(tcon, volume_info); > > remote_path_check: > #ifdef CONFIG_CIFS_DFS_UPCALL > diff --git a/fs/cifs/file.c b/fs/cifs/file.c > index ba3ef1b..a855ff1 100644 > --- a/fs/cifs/file.c > +++ b/fs/cifs/file.c > @@ -1715,6 +1715,7 @@ cifs_iovec_read(struct file *file, const struct iovec *iov, > struct smb_com_read_rsp *pSMBr; > struct cifs_io_parms io_parms; > char *read_data; > + unsigned int rsize; > __u32 pid; > > if (!nr_segs) > @@ -1727,6 +1728,9 @@ cifs_iovec_read(struct file *file, const struct iovec *iov, > xid = GetXid(); > cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); > > + /* FIXME: set up handlers for larger reads and/or convert to async */ > + rsize = min_t(unsigned int, cifs_sb->rsize, CIFSMaxBufSize); > + > open_file = file->private_data; > pTcon = tlink_tcon(open_file->tlink); > > @@ -1739,7 +1743,7 @@ cifs_iovec_read(struct file *file, const struct iovec *iov, > cFYI(1, "attempting read on write only file instance"); > > for (total_read = 0; total_read < len; total_read += bytes_read) { > - cur_len = min_t(const size_t, len - total_read, cifs_sb->rsize); > + cur_len = min_t(const size_t, len - total_read, rsize); > rc = -EAGAIN; > read_data = NULL; > > @@ -1831,6 +1835,7 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, > unsigned int bytes_read = 0; > unsigned int total_read; > unsigned int current_read_size; > + unsigned int rsize; > struct cifs_sb_info *cifs_sb; > struct cifs_tcon *pTcon; > int xid; > @@ -1843,6 +1848,9 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, > xid = GetXid(); > cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); > > + /* FIXME: set up handlers for larger reads and/or convert to async */ > + rsize = min_t(unsigned int, cifs_sb->rsize, CIFSMaxBufSize); > + > if (file->private_data == NULL) { > rc = -EBADF; > FreeXid(xid); > @@ -1863,7 +1871,7 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, > read_size > total_read; > total_read += bytes_read, current_offset += bytes_read) { > current_read_size = min_t(const int, read_size - total_read, > - cifs_sb->rsize); > + rsize); > /* For windows me and 9x we do not want to request more > than it negotiated since it will refuse the read then */ > if ((pTcon->ses) && > -- > 1.7.6 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-cifs" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html > -- Best regards, Pavel Shilovsky. -- To unsubscribe from this list: send the line "unsubscribe linux-cifs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html