From patchwork Tue Jul 5 11:57:38 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jeff Layton X-Patchwork-Id: 944652 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter2.kernel.org (8.14.4/8.14.4) with ESMTP id p65Bv4oO032168 for ; Tue, 5 Jul 2011 11:57:46 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756108Ab1GEL5p (ORCPT ); Tue, 5 Jul 2011 07:57:45 -0400 Received: from mail-yw0-f46.google.com ([209.85.213.46]:36467 "EHLO mail-yw0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756109Ab1GEL5o (ORCPT ); Tue, 5 Jul 2011 07:57:44 -0400 Received: by ywe9 with SMTP id 9so2225747ywe.19 for ; Tue, 05 Jul 2011 04:57:44 -0700 (PDT) Received: by 10.91.159.14 with SMTP id l14mr2153859ago.19.1309867063965; Tue, 05 Jul 2011 04:57:43 -0700 (PDT) Received: from salusa.poochiereds.net (cpe-076-182-054-018.nc.res.rr.com [76.182.54.18]) by mx.google.com with ESMTPS id w13sm6566555ano.49.2011.07.05.04.57.42 (version=SSLv3 cipher=OTHER); Tue, 05 Jul 2011 04:57:42 -0700 (PDT) From: Jeff Layton To: stable@kernel.org Cc: linux-cifs@vger.kernel.org, helge.hafting@hist.no, linux-kernel@vger.kernel.org Subject: [PATCH] cifs: fix wsize negotiation for 2.6.39 stable Date: Tue, 5 Jul 2011 07:57:38 -0400 Message-Id: <1309867058-22983-1-git-send-email-jlayton@redhat.com> X-Mailer: git-send-email 1.7.5.4 Sender: linux-cifs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-cifs@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter2.kernel.org [140.211.167.43]); Tue, 05 Jul 2011 11:57:46 +0000 (UTC) Prior to 2.6.39, when signing was enabled on a socket the client only sent single-page writes. This changed with commit ca83ce3d5b, which made signed and unsigned connections use the same codepaths for write calls. This caused a regression when working with windows servers. Windows machines will reject writes larger than the MaxBufferSize when signing is active, but does not clear the CAP_LARGE_WRITE_X flag in the negotiation. This patch backports 2 patches that fix this problem in 3.0 kernels, and changes a couple of the constants to values appropriate for the writeback code in 2.6.39. Cc: stable@kernel.org Reported-by: Helge Hafting Signed-off-by: Jeff Layton --- fs/cifs/connect.c | 64 ++++++++++++++++++++++++++++++++++++---------------- 1 files changed, 44 insertions(+), 20 deletions(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 5236582..8e78710 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2571,23 +2571,6 @@ static void setup_cifs_sb(struct smb_vol *pvolume_info, else /* default */ cifs_sb->rsize = CIFSMaxBufSize; - if (pvolume_info->wsize > PAGEVEC_SIZE * PAGE_CACHE_SIZE) { - cERROR(1, "wsize %d too large, using 4096 instead", - pvolume_info->wsize); - cifs_sb->wsize = 4096; - } else if (pvolume_info->wsize) - cifs_sb->wsize = pvolume_info->wsize; - else - cifs_sb->wsize = min_t(const int, - PAGEVEC_SIZE * PAGE_CACHE_SIZE, - 127*1024); - /* old default of CIFSMaxBufSize was too small now - that SMB Write2 can send multiple pages in kvec. - RFC1001 does not describe what happens when frame - bigger than 128K is sent so use that as max in - conjunction with 52K kvec constraint on arch with 4K - page size */ - if (cifs_sb->rsize < 2048) { cifs_sb->rsize = 2048; /* Windows ME may prefer this */ @@ -2665,6 +2648,48 @@ static void setup_cifs_sb(struct smb_vol *pvolume_info, "mount option supported"); } +/* Prior to 3.0, cifs couldn't handle writes larger than this */ +#define CIFS_MAX_WSIZE (PAGEVEC_SIZE * PAGE_CACHE_SIZE) + +/* + * 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 + * to the maximum size described by RFC1002. + */ +#define CIFS_MAX_RFC1002_WSIZE (128 * 1024 - sizeof(WRITE_REQ) + 4) + +/* Make the default the same as the max */ +#define CIFS_DEFAULT_WSIZE CIFS_MAX_WSIZE + +static unsigned int +cifs_negotiate_wsize(struct cifsTconInfo *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; + + /* can server support 24-bit write sizes? (via UNIX extensions) */ + if (!tcon->unix_ext || !(unix_cap & CIFS_UNIX_LARGE_WRITE_CAP)) + wsize = min_t(unsigned int, wsize, CIFS_MAX_RFC1002_WSIZE); + + /* + * no CAP_LARGE_WRITE_X or is signing enabled without CAP_UNIX set? + * Limit it to max buffer offered by the server, minus the size of the + * WRITEX header, not including the 4 byte RFC1001 length. + */ + if (!(server->capabilities & CAP_LARGE_WRITE_X) || + (!(server->capabilities & CAP_UNIX) && + (server->sec_mode & (SECMODE_SIGN_ENABLED|SECMODE_SIGN_REQUIRED)))) + wsize = min_t(unsigned int, wsize, + server->maxBuf - sizeof(WRITE_REQ) + 4); + + /* hard limit of CIFS_MAX_WSIZE */ + wsize = min_t(unsigned int, wsize, CIFS_MAX_WSIZE); + + return wsize; +} + static int is_path_accessible(int xid, struct cifsTconInfo *tcon, struct cifs_sb_info *cifs_sb, const char *full_path) @@ -2866,13 +2891,12 @@ try_mount_again: cifs_sb->rsize = 1024 * 127; cFYI(DBG2, "no very large read support, rsize now 127K"); } - if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X)) - cifs_sb->wsize = min(cifs_sb->wsize, - (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)); if (!(tcon->ses->capabilities & CAP_LARGE_READ_X)) cifs_sb->rsize = min(cifs_sb->rsize, (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)); + cifs_sb->wsize = cifs_negotiate_wsize(tcon, volume_info); + remote_path_check: /* check if a whole path (including prepath) is not remote */ if (!rc && tcon) {