diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index 6e7c4427369d878f008320e32e34f29af9e5cf8e..d14002d67b6ae4a9ce21eb36634100c68836d4b6 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -58,12 +58,15 @@ struct cifs_sb_info { struct rb_root tlink_tree; + struct list_head tcon_sb_link; spinlock_t tlink_tree_lock; struct tcon_link *master_tlink; struct nls_table *local_nls; unsigned int bsize; unsigned int rsize; unsigned int wsize; + unsigned int vol_rsize; + unsigned int vol_wsize; unsigned long actimeo; /* attribute cache timeout (jiffies) */ atomic_t active; kuid_t mnt_uid; diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index a6697954fd68a2ff78ccf28886677032d8cd0bd1..7d1f68008435224d40a34388599a8352112bb216 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -271,9 +271,11 @@ struct smb_version_operations { /* negotiate to the server */ int (*negotiate)(const unsigned int, struct cifs_ses *); /* set negotiated write size */ - unsigned int (*negotiate_wsize)(struct cifs_tcon *, struct smb_vol *); + unsigned int (*negotiate_wsize)(struct cifs_tcon *tcon, + unsigned int vol_wsize); /* set negotiated read size */ - unsigned int (*negotiate_rsize)(struct cifs_tcon *, struct smb_vol *); + unsigned int (*negotiate_rsize)(struct cifs_tcon *tcon, + unsigned int vol_rsize); /* setup smb sessionn */ int (*sess_setup)(const unsigned int, struct cifs_ses *, const struct nls_table *); @@ -916,6 +918,9 @@ compare_mid(__u16 mid, const struct smb_hdr *smb) #define CIFS_DEFAULT_IOSIZE (1024 * 1024) #define SMB3_DEFAULT_IOSIZE (4 * 1024 * 1024) +/* According to MS-SMB2 specification The minimum recommended value is 65536.*/ +#define CIFS_MIN_DEFAULT_IOSIZE (65536) + /* * Windows only supports a max of 60kb reads and 65535 byte writes. Default to * those values when posix extensions aren't in force. In actuality here, we @@ -1174,7 +1179,8 @@ struct cifs_tcon { #endif struct list_head pending_opens; /* list of incomplete opens */ struct cached_fid crfid; /* Cached root fid */ - /* BB add field for back pointer to sb struct(s)? */ + struct list_head cifs_sb_list; + spinlock_t sb_list_lock; #ifdef CONFIG_CIFS_DFS_UPCALL char *dfs_path; int remap:2; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 07b56705bcb05a8587886a8338097855c828a66e..56afed6d9ef8f665e3f51b28e83e2f2ba0085144 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -3967,6 +3967,7 @@ int cifs_setup_cifs_sb(struct smb_vol *pvolume_info, struct cifs_sb_info *cifs_sb) { INIT_DELAYED_WORK(&cifs_sb->prune_tlinks, cifs_prune_tlinks); + INIT_LIST_HEAD(&cifs_sb->tcon_sb_link); spin_lock_init(&cifs_sb->tlink_tree_lock); cifs_sb->tlink_tree = RB_ROOT; @@ -3979,6 +3980,9 @@ int cifs_setup_cifs_sb(struct smb_vol *pvolume_info, cifs_sb->rsize = pvolume_info->rsize; cifs_sb->wsize = pvolume_info->wsize; + cifs_sb->vol_rsize = pvolume_info->rsize; + cifs_sb->vol_wsize = pvolume_info->wsize; + cifs_sb->mnt_uid = pvolume_info->linux_uid; cifs_sb->mnt_gid = pvolume_info->linux_gid; cifs_sb->mnt_file_mode = pvolume_info->file_mode; @@ -4205,8 +4209,8 @@ static int mount_get_conns(struct smb_vol *vol, struct cifs_sb_info *cifs_sb, } } - cifs_sb->wsize = server->ops->negotiate_wsize(tcon, vol); - cifs_sb->rsize = server->ops->negotiate_rsize(tcon, vol); + cifs_sb->wsize = server->ops->negotiate_wsize(tcon, cifs_sb->vol_wsize); + cifs_sb->rsize = server->ops->negotiate_rsize(tcon, cifs_sb->vol_rsize); return 0; } @@ -4232,6 +4236,10 @@ static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, tlink_rb_insert(&cifs_sb->tlink_tree, tlink); spin_unlock(&cifs_sb->tlink_tree_lock); + spin_lock(&tcon->sb_list_lock); + list_add(&cifs_sb->tcon_sb_link, &tcon->cifs_sb_list); + spin_unlock(&tcon->sb_list_lock); + queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks, TLINK_IDLE_EXPIRE); return 0; @@ -5049,9 +5057,19 @@ cifs_umount(struct cifs_sb_info *cifs_sb) struct rb_root *root = &cifs_sb->tlink_tree; struct rb_node *node; struct tcon_link *tlink; + struct cifs_tcon *tcon = NULL; cancel_delayed_work_sync(&cifs_sb->prune_tlinks); + if (cifs_sb->master_tlink) { + tcon = cifs_sb->master_tlink->tl_tcon; + if (tcon) { + spin_lock(&tcon->sb_list_lock); + list_del_init(&cifs_sb->tcon_sb_link); + spin_unlock(&tcon->sb_list_lock); + } + } + spin_lock(&cifs_sb->tlink_tree_lock); while ((node = rb_first(root))) { tlink = rb_entry(node, struct tcon_link, tl_rbnode); diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 2d46018b02839dea463dfb724847a6e8a329a581..12131a5d507303a5a0125d2c585cd59304476890 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -130,7 +130,9 @@ tconInfoAlloc(void) ++ret_buf->tc_count; INIT_LIST_HEAD(&ret_buf->openFileList); INIT_LIST_HEAD(&ret_buf->tcon_list); + INIT_LIST_HEAD(&ret_buf->cifs_sb_list); spin_lock_init(&ret_buf->open_file_lock); + spin_lock_init(&ret_buf->sb_list_lock); mutex_init(&ret_buf->crfid.fid_mutex); spin_lock_init(&ret_buf->stat_lock); atomic_set(&ret_buf->num_local_opens, 0); diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index 80287c26cfac0cce1e0d514bdfa94bd5305a4486..e540908bae439e8e36aed727fa0c941efd3844e0 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -428,15 +428,15 @@ cifs_negotiate(const unsigned int xid, struct cifs_ses *ses) } static unsigned int -cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) +cifs_negotiate_wsize(struct cifs_tcon *tcon, unsigned int vol_wsize) { __u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); struct TCP_Server_Info *server = tcon->ses->server; unsigned int wsize; /* start with specified wsize, or default */ - if (volume_info->wsize) - wsize = volume_info->wsize; + if (vol_wsize) + wsize = vol_wsize; else if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_WRITE_CAP)) wsize = CIFS_DEFAULT_IOSIZE; else @@ -463,7 +463,7 @@ cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) } static unsigned int -cifs_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) +cifs_negotiate_rsize(struct cifs_tcon *tcon, unsigned int vol_rsize) { __u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); struct TCP_Server_Info *server = tcon->ses->server; @@ -488,7 +488,7 @@ cifs_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) else defsize = server->maxBuf - sizeof(READ_RSP); - rsize = volume_info->rsize ? volume_info->rsize : defsize; + rsize = vol_rsize ? vol_rsize : defsize; /* * no CAP_LARGE_READ_X? Then MS-CIFS states that we must limit this to diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index ba9456bc91e80bc6aa433edd7e5c26d135a242ce..a597490ae1f781a03b7121159d77ad6745526870 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -341,29 +341,40 @@ smb2_negotiate(const unsigned int xid, struct cifs_ses *ses) return rc; } +static inline unsigned int +prevent_zero_iosize(unsigned int size, const char *type) +{ + if (size == 0) { + cifs_dbg(VFS, "SMB: Zero %ssize calculated, using minimum value %u\n", + type, CIFS_MIN_DEFAULT_IOSIZE); + return CIFS_MIN_DEFAULT_IOSIZE; + } + return size; +} + static unsigned int -smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) +smb2_negotiate_wsize(struct cifs_tcon *tcon, unsigned int vol_wsize) { struct TCP_Server_Info *server = tcon->ses->server; unsigned int wsize; /* start with specified wsize, or default */ - wsize = volume_info->wsize ? volume_info->wsize : CIFS_DEFAULT_IOSIZE; + wsize = vol_wsize ? vol_wsize : CIFS_DEFAULT_IOSIZE; wsize = min_t(unsigned int, wsize, server->max_write); if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE); - return wsize; + return prevent_zero_iosize(wsize, "w"); } static unsigned int -smb3_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) +smb3_negotiate_wsize(struct cifs_tcon *tcon, unsigned int vol_wsize) { struct TCP_Server_Info *server = tcon->ses->server; unsigned int wsize; /* start with specified wsize, or default */ - wsize = volume_info->wsize ? volume_info->wsize : SMB3_DEFAULT_IOSIZE; + wsize = vol_wsize ? vol_wsize : SMB3_DEFAULT_IOSIZE; wsize = min_t(unsigned int, wsize, server->max_write); #ifdef CONFIG_CIFS_SMB_DIRECT if (server->rdma) { @@ -385,33 +396,33 @@ smb3_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE); - return wsize; + return prevent_zero_iosize(wsize, "w"); } static unsigned int -smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) +smb2_negotiate_rsize(struct cifs_tcon *tcon, unsigned int vol_rsize) { struct TCP_Server_Info *server = tcon->ses->server; unsigned int rsize; /* start with specified rsize, or default */ - rsize = volume_info->rsize ? volume_info->rsize : CIFS_DEFAULT_IOSIZE; + rsize = vol_rsize ? vol_rsize : CIFS_DEFAULT_IOSIZE; rsize = min_t(unsigned int, rsize, server->max_read); if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE); - return rsize; + return prevent_zero_iosize(rsize, "r"); } static unsigned int -smb3_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) +smb3_negotiate_rsize(struct cifs_tcon *tcon, unsigned int vol_rsize) { struct TCP_Server_Info *server = tcon->ses->server; unsigned int rsize; /* start with specified rsize, or default */ - rsize = volume_info->rsize ? volume_info->rsize : SMB3_DEFAULT_IOSIZE; + rsize = vol_rsize ? vol_rsize : SMB3_DEFAULT_IOSIZE; rsize = min_t(unsigned int, rsize, server->max_read); #ifdef CONFIG_CIFS_SMB_DIRECT if (server->rdma) { @@ -434,7 +445,7 @@ smb3_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE); - return rsize; + return prevent_zero_iosize(rsize, "r"); } static int diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 4197096e7fdbcc1a317d7b3b508bad39255d5336..696c84632617a5c65e9fbe4328de2aa36a9a5a7d 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -3676,6 +3676,24 @@ smb2_echo_callback(struct mid_q_entry *mid) add_credits(server, &credits, CIFS_ECHO_OP); } +static void cifs_renegotiate_iosize(struct TCP_Server_Info *server, + struct cifs_tcon *tcon) +{ + struct cifs_sb_info *cifs_sb; + + if (server == NULL || tcon == NULL) + return; + + spin_lock(&tcon->sb_list_lock); + list_for_each_entry(cifs_sb, &tcon->cifs_sb_list, tcon_sb_link) { + cifs_sb->rsize = + server->ops->negotiate_rsize(tcon, cifs_sb->rsize); + cifs_sb->wsize = + server->ops->negotiate_wsize(tcon, cifs_sb->wsize); + } + spin_unlock(&tcon->sb_list_lock); +} + void smb2_reconnect_server(struct work_struct *work) { struct TCP_Server_Info *server = container_of(work, @@ -3724,10 +3742,12 @@ void smb2_reconnect_server(struct work_struct *work) list_for_each_entry_safe(tcon, tcon2, &tmp_list, rlist) { rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server); - if (!rc) + if (!rc) { + cifs_renegotiate_iosize(server, tcon); cifs_reopen_persistent_handles(tcon); - else + } else { resched = true; + } list_del_init(&tcon->rlist); if (tcon->ipc) cifs_put_smb_ses(tcon->ses);