From 165df9a080b6863ae286fa01780c13d87cd81076 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Tue, 29 Jan 2019 16:40:28 -0800 Subject: CIFS: Fix leaking locked VFS cache pages in writeback retry If we don't find a writable file handle when retrying writepages we break of the loop and do not unlock and put pages neither from wdata2 nor from the original wdata. Fix this by walking through all the remaining pages and cleanup them properly. Cc: Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifssmb.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index bb54ccf8481c..551924beb86f 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -2125,12 +2125,13 @@ cifs_writev_requeue(struct cifs_writedata *wdata) wdata2->cfile = find_writable_file(CIFS_I(inode), false); if (!wdata2->cfile) { - cifs_dbg(VFS, "No writable handles for inode\n"); + cifs_dbg(VFS, "No writable handle to retry writepages\n"); rc = -EBADF; - break; + } else { + wdata2->pid = wdata2->cfile->pid; + rc = server->ops->async_writev(wdata2, + cifs_writedata_release); } - wdata2->pid = wdata2->cfile->pid; - rc = server->ops->async_writev(wdata2, cifs_writedata_release); for (j = 0; j < nr_pages; j++) { unlock_page(wdata2->pages[j]); @@ -2145,6 +2146,7 @@ cifs_writev_requeue(struct cifs_writedata *wdata) kref_put(&wdata2->refcount, cifs_writedata_release); if (is_retryable_error(rc)) continue; + i += nr_pages; break; } @@ -2152,6 +2154,13 @@ cifs_writev_requeue(struct cifs_writedata *wdata) i += nr_pages; } while (i < wdata->nr_pages); + /* cleanup remaining pages from the original wdata */ + for (; i < wdata->nr_pages; i++) { + SetPageError(wdata->pages[i]); + end_page_writeback(wdata->pages[i]); + put_page(wdata->pages[i]); + } + if (rc != 0 && !is_retryable_error(rc)) mapping_set_error(inode->i_mapping, rc); kref_put(&wdata->refcount, cifs_writedata_release); -- cgit v1.2.3 From 68e2672f8fbd1e04982b8d2798dd318bf2515dd2 Mon Sep 17 00:00:00 2001 From: Yao Liu Date: Mon, 28 Jan 2019 19:47:28 +0800 Subject: cifs: Fix NULL pointer dereference of devname There is a NULL pointer dereference of devname in strspn() The oops looks something like: CIFS: Attempting to mount (null) BUG: unable to handle kernel NULL pointer dereference at 0000000000000000 ... RIP: 0010:strspn+0x0/0x50 ... Call Trace: ? cifs_parse_mount_options+0x222/0x1710 [cifs] ? cifs_get_volume_info+0x2f/0x80 [cifs] cifs_setup_volume_info+0x20/0x190 [cifs] cifs_get_volume_info+0x50/0x80 [cifs] cifs_smb3_do_mount+0x59/0x630 [cifs] ? ida_alloc_range+0x34b/0x3d0 cifs_do_mount+0x11/0x20 [cifs] mount_fs+0x52/0x170 vfs_kern_mount+0x6b/0x170 do_mount+0x216/0xdc0 ksys_mount+0x83/0xd0 __x64_sys_mount+0x25/0x30 do_syscall_64+0x65/0x220 entry_SYSCALL_64_after_hwframe+0x49/0xbe Fix this by adding a NULL check on devname in cifs_parse_devname() Signed-off-by: Yao Liu Signed-off-by: Steve French --- fs/cifs/connect.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 8463c940e0e5..2ed773b395a4 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1486,6 +1486,11 @@ cifs_parse_devname(const char *devname, struct smb_vol *vol) const char *delims = "/\\"; size_t len; + if (unlikely(!devname || !*devname)) { + cifs_dbg(VFS, "Device name not specified.\n"); + return -EINVAL; + } + /* make sure we have a valid UNC double delimiter prefix */ len = strspn(devname, delims); if (len != 2) -- cgit v1.2.3 From 74ea5f983f9e86ebb4b7ed611937776dab18c67e Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Sat, 9 Feb 2019 09:51:11 +1000 Subject: cifs: replace snprintf with scnprintf a trivial patch that replaces all use of snprintf with scnprintf. scnprintf() is generally seen as a safer function to use than snprintf for many use cases. In our case, there is no actual difference between the two since we never look at the return value. Thus we did not have any of the bugs that scnprintf protects against and the patch does nothing. However, for people reading our code it will be a receipt that we have done our due dilligence and checked our code for this type of bugs. See the presentation "Making C Less Dangerous In The Linux Kernel" at this years LCA Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/cifssmb.c | 6 +++--- fs/cifs/connect.c | 6 +++--- fs/cifs/link.c | 14 +++++++------- fs/cifs/smb2pdu.c | 6 +++--- fs/cifs/smbdirect.c | 6 +++--- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 551924beb86f..6bd3605b9b65 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -139,8 +139,8 @@ static int __cifs_reconnect_tcon(const struct nls_table *nlsc, return -ENOMEM; if (tcon->ipc) { - snprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", - tcon->ses->server->hostname); + scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", + tcon->ses->server->hostname); rc = CIFSTCon(0, tcon->ses, tree, tcon, nlsc); goto out; } @@ -172,7 +172,7 @@ static int __cifs_reconnect_tcon(const struct nls_table *nlsc, continue; } - snprintf(tree, MAX_TREE_SIZE, "\\%s", tgt); + scnprintf(tree, MAX_TREE_SIZE, "\\%s", tgt); rc = CIFSTCon(0, tcon->ses, tree, tcon, nlsc); if (!rc) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 2ed773b395a4..7c9108ce3de0 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -348,7 +348,7 @@ static int reconn_set_ipaddr(struct TCP_Server_Info *server) cifs_dbg(FYI, "%s: failed to create UNC path\n", __func__); return -ENOMEM; } - snprintf(unc, len, "\\\\%s", server->hostname); + scnprintf(unc, len, "\\\\%s", server->hostname); rc = dns_resolve_server_name_to_ip(unc, &ipaddr); kfree(unc); @@ -2775,7 +2775,7 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb_vol *volume_info) if (tcon == NULL) return -ENOMEM; - snprintf(unc, sizeof(unc), "\\\\%s\\IPC$", ses->server->hostname); + scnprintf(unc, sizeof(unc), "\\\\%s\\IPC$", ses->server->hostname); /* cannot fail */ nls_codepage = load_nls_default(); @@ -4203,7 +4203,7 @@ static int update_vol_info(const struct dfs_cache_tgt_iterator *tgt_it, new_unc = kmalloc(len, GFP_KERNEL); if (!new_unc) return -ENOMEM; - snprintf(new_unc, len, "\\%s", tgt); + scnprintf(new_unc, len, "\\%s", tgt); kfree(vol->UNC); vol->UNC = new_unc; diff --git a/fs/cifs/link.c b/fs/cifs/link.c index 2148b0f60e5e..62216dc8f9f5 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -103,9 +103,9 @@ parse_mf_symlink(const u8 *buf, unsigned int buf_len, unsigned int *_link_len, return rc; } - snprintf(md5_str2, sizeof(md5_str2), - CIFS_MF_SYMLINK_MD5_FORMAT, - CIFS_MF_SYMLINK_MD5_ARGS(md5_hash)); + scnprintf(md5_str2, sizeof(md5_str2), + CIFS_MF_SYMLINK_MD5_FORMAT, + CIFS_MF_SYMLINK_MD5_ARGS(md5_hash)); if (strncmp(md5_str1, md5_str2, 17) != 0) return -EINVAL; @@ -142,10 +142,10 @@ format_mf_symlink(u8 *buf, unsigned int buf_len, const char *link_str) return rc; } - snprintf(buf, buf_len, - CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT, - link_len, - CIFS_MF_SYMLINK_MD5_ARGS(md5_hash)); + scnprintf(buf, buf_len, + CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT, + link_len, + CIFS_MF_SYMLINK_MD5_ARGS(md5_hash)); ofs = CIFS_MF_SYMLINK_LINK_OFFSET; memcpy(buf + ofs, link_str, link_len); diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 77b3aaa39b35..7d9a1cb9ecae 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -173,8 +173,8 @@ static int __smb2_reconnect(const struct nls_table *nlsc, return -ENOMEM; if (tcon->ipc) { - snprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", - tcon->ses->server->hostname); + scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", + tcon->ses->server->hostname); rc = SMB2_tcon(0, tcon->ses, tree, tcon, nlsc); goto out; } @@ -206,7 +206,7 @@ static int __smb2_reconnect(const struct nls_table *nlsc, continue; } - snprintf(tree, MAX_TREE_SIZE, "\\%s", tgt); + scnprintf(tree, MAX_TREE_SIZE, "\\%s", tgt); rc = SMB2_tcon(0, tcon->ses, tree, tcon, nlsc); if (!rc) diff --git a/fs/cifs/smbdirect.c b/fs/cifs/smbdirect.c index a568dac7b3a1..b943b74cd246 100644 --- a/fs/cifs/smbdirect.c +++ b/fs/cifs/smbdirect.c @@ -1550,7 +1550,7 @@ static int allocate_caches_and_workqueue(struct smbd_connection *info) char name[MAX_NAME_LEN]; int rc; - snprintf(name, MAX_NAME_LEN, "smbd_request_%p", info); + scnprintf(name, MAX_NAME_LEN, "smbd_request_%p", info); info->request_cache = kmem_cache_create( name, @@ -1566,7 +1566,7 @@ static int allocate_caches_and_workqueue(struct smbd_connection *info) if (!info->request_mempool) goto out1; - snprintf(name, MAX_NAME_LEN, "smbd_response_%p", info); + scnprintf(name, MAX_NAME_LEN, "smbd_response_%p", info); info->response_cache = kmem_cache_create( name, @@ -1582,7 +1582,7 @@ static int allocate_caches_and_workqueue(struct smbd_connection *info) if (!info->response_mempool) goto out3; - snprintf(name, MAX_NAME_LEN, "smbd_%p", info); + scnprintf(name, MAX_NAME_LEN, "smbd_%p", info); info->workqueue = create_workqueue(name); if (!info->workqueue) goto out4; -- cgit v1.2.3 From eca004523811f816bcfca3046ab54e1278e0973b Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Tue, 5 Feb 2019 12:56:44 +1000 Subject: cifs: add credits from unmatched responses/messages We should add any credits granted to us from unmatched server responses. Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French Reviewed-by: Pavel Shilovsky --- fs/cifs/connect.c | 22 ++++++++++++++++++++++ fs/cifs/smb2misc.c | 7 ------- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 7c9108ce3de0..a825ac219bbd 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1063,6 +1063,26 @@ cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid) return 0; } +static void +smb2_add_credits_from_hdr(char *buffer, struct TCP_Server_Info *server) +{ + struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buffer; + + /* + * SMB1 does not use credits. + */ + if (server->vals->header_preamble_size) + return; + + if (shdr->CreditRequest) { + spin_lock(&server->req_lock); + server->credits += le16_to_cpu(shdr->CreditRequest); + spin_unlock(&server->req_lock); + wake_up(&server->request_q); + } +} + + static int cifs_demultiplex_thread(void *p) { @@ -1192,6 +1212,7 @@ next_pdu: } else if (server->ops->is_oplock_break && server->ops->is_oplock_break(bufs[i], server)) { + smb2_add_credits_from_hdr(bufs[i], server); cifs_dbg(FYI, "Received oplock break\n"); } else { cifs_dbg(VFS, "No task to wake, unknown frame " @@ -1203,6 +1224,7 @@ next_pdu: if (server->ops->dump_detail) server->ops->dump_detail(bufs[i], server); + smb2_add_credits_from_hdr(bufs[i], server); cifs_dump_mids(server); #endif /* CIFS_DEBUG2 */ } diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c index 7b8b58fb4d3f..6a9c47541c53 100644 --- a/fs/cifs/smb2misc.c +++ b/fs/cifs/smb2misc.c @@ -648,13 +648,6 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) if (rsp->sync_hdr.Command != SMB2_OPLOCK_BREAK) return false; - if (rsp->sync_hdr.CreditRequest) { - spin_lock(&server->req_lock); - server->credits += le16_to_cpu(rsp->sync_hdr.CreditRequest); - spin_unlock(&server->req_lock); - wake_up(&server->request_q); - } - if (rsp->StructureSize != smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) { if (le16_to_cpu(rsp->StructureSize) == 44) -- cgit v1.2.3 From 2109464184919f81efd593b4008291448c522815 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Thu, 7 Feb 2019 15:48:44 +1000 Subject: cifs: return -ENODATA when deleting an xattr that does not exist BUGZILLA: https://bugzilla.kernel.org/show_bug.cgi?id=202007 When deleting an xattr/EA: SMB2/3 servers will return SUCCESS when clients delete non-existing EAs. This means that we need to first QUERY the server and check if the EA exists or not so that we can return -ENODATA correctly when this happens. Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/smb2ops.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 6f96e2292856..991c2d40c0ef 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -940,6 +940,16 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; memset(rsp_iov, 0, sizeof(rsp_iov)); + if (ses->server->ops->query_all_EAs) { + if (!ea_value) { + rc = ses->server->ops->query_all_EAs(xid, tcon, path, + ea_name, NULL, 0, + cifs_sb); + if (rc == -ENODATA) + goto sea_exit; + } + } + /* Open */ memset(&open_iov, 0, sizeof(open_iov)); rqst[0].rq_iov = open_iov; -- cgit v1.2.3 From d26e2903fc10cfa2d3b8587b9eb8cf7c2a3ff2f5 Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 11 Feb 2019 15:38:12 -0600 Subject: smb3: fix bytes_read statistics /proc/fs/cifs/Stats bytes_read was double counting reads when uncached (ie mounted with cache=none) Signed-off-by: Steve French Reviewed-by: Ronnie Sahlberg --- fs/cifs/file.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 659ce1b92c44..da1f05f4ac0c 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -3533,8 +3533,6 @@ again: ctx->total_len = ctx->len - iov_iter_count(to); } - cifs_stats_bytes_read(tcon, ctx->total_len); - /* mask nodata case */ if (rc == -ENODATA) rc = 0; -- cgit v1.2.3 From 7b9b9edb49ad377b1e06abf14354c227e9ac4b06 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Wed, 13 Feb 2019 15:43:08 -0800 Subject: CIFS: Do not reset lease state to NONE on lease break Currently on lease break the client sets a caching level twice: when oplock is detected and when oplock is processed. While the 1st attempt sets the level to the value provided by the server, the 2nd one resets the level to None unconditionally. This happens because the oplock/lease processing code was changed to avoid races between page cache flushes and oplock breaks. The commit c11f1df5003d534 ("cifs: Wait for writebacks to complete before attempting write.") fixed the races for oplocks but didn't apply the same changes for leases resulting in overwriting the server granted value to None. Fix this by properly processing lease breaks. Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French CC: Stable --- fs/cifs/smb2misc.c | 17 ++++++++++++++--- fs/cifs/smb2ops.c | 15 ++++++++++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c index 6a9c47541c53..0e3570e40ff8 100644 --- a/fs/cifs/smb2misc.c +++ b/fs/cifs/smb2misc.c @@ -517,7 +517,6 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp, __u8 lease_state; struct list_head *tmp; struct cifsFileInfo *cfile; - struct TCP_Server_Info *server = tcon->ses->server; struct cifs_pending_open *open; struct cifsInodeInfo *cinode; int ack_req = le32_to_cpu(rsp->Flags & @@ -537,13 +536,25 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp, cifs_dbg(FYI, "lease key match, lease break 0x%x\n", le32_to_cpu(rsp->NewLeaseState)); - server->ops->set_oplock_level(cinode, lease_state, 0, NULL); - if (ack_req) cfile->oplock_break_cancelled = false; else cfile->oplock_break_cancelled = true; + set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags); + + /* + * Set or clear flags depending on the lease state being READ. + * HANDLE caching flag should be added when the client starts + * to defer closing remote file handles with HANDLE leases. + */ + if (lease_state & SMB2_LEASE_READ_CACHING_HE) + set_bit(CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, + &cinode->flags); + else + clear_bit(CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, + &cinode->flags); + queue_work(cifsoplockd_wq, &cfile->oplock_break); kfree(lw); return true; diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 991c2d40c0ef..16704941b969 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -2604,6 +2604,15 @@ smb2_downgrade_oplock(struct TCP_Server_Info *server, server->ops->set_oplock_level(cinode, 0, 0, NULL); } +static void +smb21_downgrade_oplock(struct TCP_Server_Info *server, + struct cifsInodeInfo *cinode, bool set_level2) +{ + server->ops->set_oplock_level(cinode, + set_level2 ? SMB2_LEASE_READ_CACHING_HE : + 0, 0, NULL); +} + static void smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, unsigned int epoch, bool *purge_cache) @@ -3656,7 +3665,7 @@ struct smb_version_operations smb21_operations = { .print_stats = smb2_print_stats, .is_oplock_break = smb2_is_valid_oplock_break, .handle_cancelled_mid = smb2_handle_cancelled_mid, - .downgrade_oplock = smb2_downgrade_oplock, + .downgrade_oplock = smb21_downgrade_oplock, .need_neg = smb2_need_neg, .negotiate = smb2_negotiate, .negotiate_wsize = smb2_negotiate_wsize, @@ -3753,7 +3762,7 @@ struct smb_version_operations smb30_operations = { .dump_share_caps = smb2_dump_share_caps, .is_oplock_break = smb2_is_valid_oplock_break, .handle_cancelled_mid = smb2_handle_cancelled_mid, - .downgrade_oplock = smb2_downgrade_oplock, + .downgrade_oplock = smb21_downgrade_oplock, .need_neg = smb2_need_neg, .negotiate = smb2_negotiate, .negotiate_wsize = smb3_negotiate_wsize, @@ -3858,7 +3867,7 @@ struct smb_version_operations smb311_operations = { .dump_share_caps = smb2_dump_share_caps, .is_oplock_break = smb2_is_valid_oplock_break, .handle_cancelled_mid = smb2_handle_cancelled_mid, - .downgrade_oplock = smb2_downgrade_oplock, + .downgrade_oplock = smb21_downgrade_oplock, .need_neg = smb2_need_neg, .negotiate = smb2_negotiate, .negotiate_wsize = smb3_negotiate_wsize, -- cgit v1.2.3 From e8506d25f740fd058791cc12a6dfa9386ada6b96 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 28 Feb 2019 21:32:15 -0600 Subject: smb3: make default i/o size for smb3 mounts larger We negotiate rsize mounts (and it can be overridden by user) to typically 4MB, so using larger default I/O sizes from userspace (changing to 1MB default i/o size returned by stat) the performance is much better (and not just for long latency network connections) in most use cases for SMB3 than the default I/O size (which ends up being 128K for cp and can be even smaller for cp). This can be 4x slower or worse depending on network latency. By changing inode->blocksize from 32K (which was perhaps ok for very old SMB1/CIFS) to a larger value, 1MB (but still less than max size negotiated with the server which is 4MB, in order to minimize risk) it significantly increases performance for the noncached case, and slightly increases it for the cached case. This can be changed by the user on mount (specifying bsize= values from 16K to 16MB) to tune better for performance for applications that depend on blocksize. Signed-off-by: Steve French Reviewed-by: Ronnie Sahlberg CC: Stable --- fs/cifs/cifs_fs_sb.h | 1 + fs/cifs/cifsfs.c | 1 + fs/cifs/cifsglob.h | 1 + fs/cifs/connect.c | 26 ++++++++++++++++++++++++-- fs/cifs/inode.c | 2 +- 5 files changed, 28 insertions(+), 3 deletions(-) diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index 42f0d67f1054..ed49222abecb 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -58,6 +58,7 @@ struct cifs_sb_info { 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 long actimeo; /* attribute cache timeout (jiffies) */ diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 62d48d486d8f..f2c0d863fb52 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -554,6 +554,7 @@ cifs_show_options(struct seq_file *s, struct dentry *root) seq_printf(s, ",rsize=%u", cifs_sb->rsize); seq_printf(s, ",wsize=%u", cifs_sb->wsize); + seq_printf(s, ",bsize=%u", cifs_sb->bsize); seq_printf(s, ",echo_interval=%lu", tcon->ses->server->echo_interval / HZ); if (tcon->snapshot_time) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 94dbdbe5be34..33264f9a9665 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -557,6 +557,7 @@ struct smb_vol { bool resilient:1; /* noresilient not required since not fored for CA */ bool domainauto:1; bool rdma:1; + unsigned int bsize; unsigned int rsize; unsigned int wsize; bool sockopt_tcp_nodelay:1; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index a825ac219bbd..106b6508f138 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -102,7 +102,7 @@ enum { Opt_backupuid, Opt_backupgid, Opt_uid, Opt_cruid, Opt_gid, Opt_file_mode, Opt_dirmode, Opt_port, - Opt_rsize, Opt_wsize, Opt_actimeo, + Opt_blocksize, Opt_rsize, Opt_wsize, Opt_actimeo, Opt_echo_interval, Opt_max_credits, Opt_snapshot, @@ -204,6 +204,7 @@ static const match_table_t cifs_mount_option_tokens = { { Opt_dirmode, "dirmode=%s" }, { Opt_dirmode, "dir_mode=%s" }, { Opt_port, "port=%s" }, + { Opt_blocksize, "bsize=%s" }, { Opt_rsize, "rsize=%s" }, { Opt_wsize, "wsize=%s" }, { Opt_actimeo, "actimeo=%s" }, @@ -1598,7 +1599,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, vol->cred_uid = current_uid(); vol->linux_uid = current_uid(); vol->linux_gid = current_gid(); - + vol->bsize = 1024 * 1024; /* can improve cp performance significantly */ /* * default to SFM style remapping of seven reserved characters * unless user overrides it or we negotiate CIFS POSIX where @@ -1971,6 +1972,26 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, } port = (unsigned short)option; break; + case Opt_blocksize: + if (get_option_ul(args, &option)) { + cifs_dbg(VFS, "%s: Invalid blocksize value\n", + __func__); + goto cifs_parse_mount_err; + } + /* + * inode blocksize realistically should never need to be + * less than 16K or greater than 16M and default is 1MB. + * Note that small inode block sizes (e.g. 64K) can lead + * to very poor performance of common tools like cp and scp + */ + if ((option < CIFS_MAX_MSGSIZE) || + (option > (4 * SMB3_DEFAULT_IOSIZE))) { + cifs_dbg(VFS, "%s: Invalid blocksize\n", + __func__); + goto cifs_parse_mount_err; + } + vol->bsize = option; + break; case Opt_rsize: if (get_option_ul(args, &option)) { cifs_dbg(VFS, "%s: Invalid rsize value\n", @@ -3866,6 +3887,7 @@ int cifs_setup_cifs_sb(struct smb_vol *pvolume_info, spin_lock_init(&cifs_sb->tlink_tree_lock); cifs_sb->tlink_tree = RB_ROOT; + cifs_sb->bsize = pvolume_info->bsize; /* * Temporarily set r/wsize for matching superblock. If we end up using * new sb then client will later negotiate it downward if needed. diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 478003644916..53fdb5df0d2e 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -2080,7 +2080,7 @@ int cifs_getattr(const struct path *path, struct kstat *stat, return rc; generic_fillattr(inode, stat); - stat->blksize = CIFS_MAX_MSGSIZE; + stat->blksize = cifs_sb->bsize; stat->ino = CIFS_I(inode)->uniqueid; /* old CIFS Unix Extensions doesn't return create time */ -- cgit v1.2.3 From 4fe75c4e4bc2caeb4159573e26cf3075e2c0fd9b Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 14 Feb 2019 01:19:02 -0600 Subject: smb3: request more credits on tree connect If we try large I/O (read or write) immediately after mount we won't typically have enough credits because we only request large amounts of credits on the first session setup. So if large I/O is attempted soon after mount we will typically only have about 43 credits rather than 105 credits (with this patch) available for the large i/o (which needs 64 credits minimum). This patch requests more credits during tree connect, which helps ensure that we have enough credits when mount completes (between these requests and the first session setup) in order to start large I/O immediately after mount if needed. Signed-off-by: Steve French Reviewed-by: Pavel Shilovsky --- fs/cifs/smb2pdu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 7d9a1cb9ecae..3e9bee35a03c 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -1614,6 +1614,9 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, rqst.rq_iov = iov; rqst.rq_nvec = 2; + /* Need 64 for max size write so ask for more in case not there yet */ + req->sync_hdr.CreditRequest = cpu_to_le16(64); + rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); cifs_small_buf_release(req); rsp = (struct smb2_tree_connect_rsp *)rsp_iov.iov_base; -- cgit v1.2.3 From c781af7e0c1fed9f1d0e0ec31b86f5b21a8dca17 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Mon, 4 Mar 2019 14:02:50 -0800 Subject: CIFS: Do not skip SMB2 message IDs on send failures When we hit failures during constructing MIDs or sending PDUs through the network, we end up not using message IDs assigned to the packet. The next SMB packet will skip those message IDs and continue with the next one. This behavior may lead to a server not granting us credits until we use the skipped IDs. Fix this by reverting the current ID to the original value if any errors occur before we push the packet through the network stack. This patch fixes the generic/310 test from the xfs-tests. Cc: # 4.19.x Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 19 +++++++++++++++++++ fs/cifs/smb2ops.c | 13 +++++++++++++ fs/cifs/smb2transport.c | 14 ++++++++++++-- fs/cifs/transport.c | 6 +++++- 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 33264f9a9665..1b25e6e95d45 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -236,6 +236,8 @@ struct smb_version_operations { int * (*get_credits_field)(struct TCP_Server_Info *, const int); unsigned int (*get_credits)(struct mid_q_entry *); __u64 (*get_next_mid)(struct TCP_Server_Info *); + void (*revert_current_mid)(struct TCP_Server_Info *server, + const unsigned int val); /* data offset from read response message */ unsigned int (*read_data_offset)(char *); /* @@ -771,6 +773,22 @@ get_next_mid(struct TCP_Server_Info *server) return cpu_to_le16(mid); } +static inline void +revert_current_mid(struct TCP_Server_Info *server, const unsigned int val) +{ + if (server->ops->revert_current_mid) + server->ops->revert_current_mid(server, val); +} + +static inline void +revert_current_mid_from_hdr(struct TCP_Server_Info *server, + const struct smb2_sync_hdr *shdr) +{ + unsigned int num = le16_to_cpu(shdr->CreditCharge); + + return revert_current_mid(server, num > 0 ? num : 1); +} + static inline __u16 get_mid(const struct smb_hdr *smb) { @@ -1423,6 +1441,7 @@ struct mid_q_entry { struct kref refcount; struct TCP_Server_Info *server; /* server corresponding to this mid */ __u64 mid; /* multiplex id */ + __u16 credits; /* number of credits consumed by this mid */ __u32 pid; /* process id */ __u32 sequence_number; /* for CIFS signing */ unsigned long when_alloc; /* when mid was created */ diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 16704941b969..ea56b1cdbdde 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -219,6 +219,15 @@ smb2_get_next_mid(struct TCP_Server_Info *server) return mid; } +static void +smb2_revert_current_mid(struct TCP_Server_Info *server, const unsigned int val) +{ + spin_lock(&GlobalMid_Lock); + if (server->CurrentMid >= val) + server->CurrentMid -= val; + spin_unlock(&GlobalMid_Lock); +} + static struct mid_q_entry * smb2_find_mid(struct TCP_Server_Info *server, char *buf) { @@ -3560,6 +3569,7 @@ struct smb_version_operations smb20_operations = { .get_credits = smb2_get_credits, .wait_mtu_credits = cifs_wait_mtu_credits, .get_next_mid = smb2_get_next_mid, + .revert_current_mid = smb2_revert_current_mid, .read_data_offset = smb2_read_data_offset, .read_data_length = smb2_read_data_length, .map_error = map_smb2_to_linux_error, @@ -3655,6 +3665,7 @@ struct smb_version_operations smb21_operations = { .get_credits = smb2_get_credits, .wait_mtu_credits = smb2_wait_mtu_credits, .get_next_mid = smb2_get_next_mid, + .revert_current_mid = smb2_revert_current_mid, .read_data_offset = smb2_read_data_offset, .read_data_length = smb2_read_data_length, .map_error = map_smb2_to_linux_error, @@ -3751,6 +3762,7 @@ struct smb_version_operations smb30_operations = { .get_credits = smb2_get_credits, .wait_mtu_credits = smb2_wait_mtu_credits, .get_next_mid = smb2_get_next_mid, + .revert_current_mid = smb2_revert_current_mid, .read_data_offset = smb2_read_data_offset, .read_data_length = smb2_read_data_length, .map_error = map_smb2_to_linux_error, @@ -3856,6 +3868,7 @@ struct smb_version_operations smb311_operations = { .get_credits = smb2_get_credits, .wait_mtu_credits = smb2_wait_mtu_credits, .get_next_mid = smb2_get_next_mid, + .revert_current_mid = smb2_revert_current_mid, .read_data_offset = smb2_read_data_offset, .read_data_length = smb2_read_data_length, .map_error = map_smb2_to_linux_error, diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index 7b351c65ee46..63264db78b89 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c @@ -576,6 +576,7 @@ smb2_mid_entry_alloc(const struct smb2_sync_hdr *shdr, struct TCP_Server_Info *server) { struct mid_q_entry *temp; + unsigned int credits = le16_to_cpu(shdr->CreditCharge); if (server == NULL) { cifs_dbg(VFS, "Null TCP session in smb2_mid_entry_alloc\n"); @@ -586,6 +587,7 @@ smb2_mid_entry_alloc(const struct smb2_sync_hdr *shdr, memset(temp, 0, sizeof(struct mid_q_entry)); kref_init(&temp->refcount); temp->mid = le64_to_cpu(shdr->MessageId); + temp->credits = credits > 0 ? credits : 1; temp->pid = current->pid; temp->command = shdr->Command; /* Always LE */ temp->when_alloc = jiffies; @@ -674,13 +676,18 @@ smb2_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst) smb2_seq_num_into_buf(ses->server, shdr); rc = smb2_get_mid_entry(ses, shdr, &mid); - if (rc) + if (rc) { + revert_current_mid_from_hdr(ses->server, shdr); return ERR_PTR(rc); + } + rc = smb2_sign_rqst(rqst, ses->server); if (rc) { + revert_current_mid_from_hdr(ses->server, shdr); cifs_delete_mid(mid); return ERR_PTR(rc); } + return mid; } @@ -695,11 +702,14 @@ smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) smb2_seq_num_into_buf(server, shdr); mid = smb2_mid_entry_alloc(shdr, server); - if (mid == NULL) + if (mid == NULL) { + revert_current_mid_from_hdr(server, shdr); return ERR_PTR(-ENOMEM); + } rc = smb2_sign_rqst(rqst, server); if (rc) { + revert_current_mid_from_hdr(server, shdr); DeleteMidQEntry(mid); return ERR_PTR(rc); } diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 53532bd3f50d..9544eb99b5a2 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -647,6 +647,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst, cifs_in_send_dec(server); if (rc < 0) { + revert_current_mid(server, mid->credits); server->sequence_number -= 2; cifs_delete_mid(mid); } @@ -868,6 +869,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, for (i = 0; i < num_rqst; i++) { midQ[i] = ses->server->ops->setup_request(ses, &rqst[i]); if (IS_ERR(midQ[i])) { + revert_current_mid(ses->server, i); for (j = 0; j < i; j++) cifs_delete_mid(midQ[j]); mutex_unlock(&ses->server->srv_mutex); @@ -897,8 +899,10 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, for (i = 0; i < num_rqst; i++) cifs_save_when_sent(midQ[i]); - if (rc < 0) + if (rc < 0) { + revert_current_mid(ses->server, num_rqst); ses->server->sequence_number -= 2; + } mutex_unlock(&ses->server->srv_mutex); -- cgit v1.2.3 From 969ae8e8d4ee54c99134d3895f2adf96047f5bee Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 22 Jan 2019 09:46:45 +0900 Subject: cifs: Accept validate negotiate if server return NT_STATUS_NOT_SUPPORTED Old windows version or Netapp SMB server will return NT_STATUS_NOT_SUPPORTED since they do not allow or implement FSCTL_VALIDATE_NEGOTIATE_INFO. The client should accept the response provided it's properly signed. See https://blogs.msdn.microsoft.com/openspecification/2012/06/28/smb3-secure-dialect-negotiation/ and MS-SMB2 validate negotiate response processing: https://msdn.microsoft.com/en-us/library/hh880630.aspx Samba client had already handled it. https://bugzilla.samba.org/attachment.cgi?id=13285&action=edit Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifs/smb2pdu.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 3e9bee35a03c..f2980f78149c 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -986,8 +986,14 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, FSCTL_VALIDATE_NEGOTIATE_INFO, true /* is_fsctl */, (char *)pneg_inbuf, inbuflen, (char **)&pneg_rsp, &rsplen); - - if (rc != 0) { + if (rc == -EOPNOTSUPP) { + /* + * Old Windows versions or Netapp SMB server can return + * not supported error. Client should accept it. + */ + cifs_dbg(VFS, "Server does not support validate negotiate\n"); + return 0; + } else if (rc != 0) { cifs_dbg(VFS, "validate protocol negotiate failed: %d\n", rc); rc = -EIO; goto out_free_inbuf; -- cgit v1.2.3 From 6b15eb18c6a9ddfbb387456c0f1ed86d987cb741 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Fri, 18 Jan 2019 15:46:14 -0800 Subject: CIFS: Always reset read error to -EIO if no response Currently we skip setting a read error to -EIO if a stored result is -ENODATA and a response hasn't been received. With the recent changes in read error processing there shouldn't be cases when -ENODATA is set without a response from the server, so reset the error to -EIO unconditionally. Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/smb2pdu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index f2980f78149c..77f2c723befa 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -3236,8 +3236,7 @@ smb2_readv_callback(struct mid_q_entry *mid) credits_received = le16_to_cpu(shdr->CreditRequest); /* fall through */ default: - if (rdata->result != -ENODATA) - rdata->result = -EIO; + rdata->result = -EIO; } #ifdef CONFIG_CIFS_SMB_DIRECT /* -- cgit v1.2.3 From 82e0457af5f92126a0a6389d827b1e4daad8e0fd Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Fri, 25 Jan 2019 10:56:41 -0800 Subject: CIFS: Do not log credits when unmounting a share Currently we only skip credits logging on reconnects. When unmounting a share the number of credits on the client doesn't matter, so skip logging in such cases too. Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/smb2ops.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index ea56b1cdbdde..90b1a8e218c9 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -102,7 +102,8 @@ smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add, spin_unlock(&server->req_lock); wake_up(&server->request_q); - if (server->tcpStatus == CifsNeedReconnect) + if (server->tcpStatus == CifsNeedReconnect + || server->tcpStatus == CifsExiting) return; switch (rc) { -- cgit v1.2.3 From 66265f134acfb202465fecfbeb61fefb66595c40 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Wed, 23 Jan 2019 17:11:16 -0800 Subject: CIFS: Count SMB3 credits for malformed pending responses Even if a response is malformed, we should count credits granted by the server to avoid miscalculations and unnecessary reconnects due to client or server bugs. If the response has been received partially, the session will be reconnected anyway on the next iteration of the demultiplex thread, so counting credits for such cases shouldn't break things. Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 4 ++-- fs/cifs/cifssmb.c | 2 +- fs/cifs/connect.c | 2 +- fs/cifs/smb2ops.c | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 1b25e6e95d45..0c796a27d1eb 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -385,8 +385,8 @@ struct smb_version_operations { struct cifs_fid *); /* calculate a size of SMB message */ unsigned int (*calc_smb_size)(void *buf, struct TCP_Server_Info *ptcpi); - /* check for STATUS_PENDING and process it in a positive case */ - bool (*is_status_pending)(char *, struct TCP_Server_Info *, int); + /* check for STATUS_PENDING and process the response if yes */ + bool (*is_status_pending)(char *buf, struct TCP_Server_Info *server); /* check for STATUS_NETWORK_SESSION_EXPIRED */ bool (*is_session_expired)(char *); /* send oplock break response */ diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 6bd3605b9b65..683c7c9d5a02 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -1605,7 +1605,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) } if (server->ops->is_status_pending && - server->ops->is_status_pending(buf, server, 0)) { + server->ops->is_status_pending(buf, server)) { cifs_discard_remaining_data(server); return -1; } diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 106b6508f138..306a2a6f4e47 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1054,7 +1054,7 @@ cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid) } if (server->ops->is_status_pending && - server->ops->is_status_pending(buf, server, length)) + server->ops->is_status_pending(buf, server)) return -1; if (!mid) diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 90b1a8e218c9..5adfe68f4754 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -1773,14 +1773,14 @@ smb2_close_dir(const unsigned int xid, struct cifs_tcon *tcon, * the number of credits and return true. Otherwise - return false. */ static bool -smb2_is_status_pending(char *buf, struct TCP_Server_Info *server, int length) +smb2_is_status_pending(char *buf, struct TCP_Server_Info *server) { struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf; if (shdr->Status != STATUS_PENDING) return false; - if (!length) { + if (shdr->CreditRequest) { spin_lock(&server->req_lock); server->credits += le16_to_cpu(shdr->CreditRequest); spin_unlock(&server->req_lock); @@ -3239,7 +3239,7 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid, } if (server->ops->is_status_pending && - server->ops->is_status_pending(buf, server, 0)) + server->ops->is_status_pending(buf, server)) return -1; /* set up first two iov to get credits */ -- cgit v1.2.3 From bb1bccb60c2ebd9a6f895507d1d48d5ed773814e Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Thu, 17 Jan 2019 16:18:38 -0800 Subject: CIFS: Respect SMB2 hdr preamble size in read responses There are a couple places where we still account for 4 bytes in the beginning of SMB2 packet which is not true in the current code. Fix this to use a header preamble size where possible. Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifssmb.c | 7 ++++--- fs/cifs/smb2ops.c | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 683c7c9d5a02..3f4e40abaad4 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -1612,9 +1612,10 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) /* set up first two iov for signature check and to get credits */ rdata->iov[0].iov_base = buf; - rdata->iov[0].iov_len = 4; - rdata->iov[1].iov_base = buf + 4; - rdata->iov[1].iov_len = server->total_read - 4; + rdata->iov[0].iov_len = server->vals->header_preamble_size; + rdata->iov[1].iov_base = buf + server->vals->header_preamble_size; + rdata->iov[1].iov_len = + server->total_read - server->vals->header_preamble_size; cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n", rdata->iov[0].iov_base, rdata->iov[0].iov_len); cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n", diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 5adfe68f4754..7291d8289511 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -3244,10 +3244,10 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid, /* set up first two iov to get credits */ rdata->iov[0].iov_base = buf; - rdata->iov[0].iov_len = 4; - rdata->iov[1].iov_base = buf + 4; + rdata->iov[0].iov_len = 0; + rdata->iov[1].iov_base = buf; rdata->iov[1].iov_len = - min_t(unsigned int, buf_len, server->vals->read_rsp_size) - 4; + min_t(unsigned int, buf_len, server->vals->read_rsp_size); cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n", rdata->iov[0].iov_base, rdata->iov[0].iov_len); cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n", -- cgit v1.2.3 From 5b964852609b2826126a526851f316fc06f5e37e Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Fri, 18 Jan 2019 11:30:26 -0800 Subject: CIFS: Set reconnect instance to one initially Currently we set reconnect instance to zero on the first connection but this is not convenient because we need to reserve some special value for credit handling on reconnects which is coming in subsequent patches. Fix this by starting with one when initiating a new TCP connection. Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/connect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 306a2a6f4e47..46bee3b4eeed 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2657,7 +2657,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) volume_info->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); tcp_ses->session_estab = false; tcp_ses->sequence_number = 0; - tcp_ses->reconnect_instance = 0; + tcp_ses->reconnect_instance = 1; tcp_ses->lstrp = jiffies; spin_lock_init(&tcp_ses->req_lock); INIT_LIST_HEAD(&tcp_ses->tcp_ses_list); -- cgit v1.2.3 From 335b7b62ffb69d18055f2bb6f3a029263a07c735 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Wed, 16 Jan 2019 11:12:41 -0800 Subject: CIFS: Respect reconnect in MTU credits calculations Every time after a session reconnect we don't need to account for credits obtained in previous sessions. Introduce new struct cifs_credits which contains both credits value and reconnect instance of the time those credits were taken. Modify a routine that add credits back to handle the reconnect instance by assuming zero credits if the reconnect happened after the credits were obtained and before we decided to add them back due to some errors during sending. This patch fixes the MTU credits cases. The subsequent patch will handle non-MTU ones. Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 29 ++++++++++++++++--------- fs/cifs/cifsproto.h | 2 +- fs/cifs/connect.c | 3 +-- fs/cifs/file.c | 62 ++++++++++++++++++++++++++++++++--------------------- fs/cifs/smb1ops.c | 6 +++--- fs/cifs/smb2ops.c | 27 +++++++++++++++++------ fs/cifs/smb2pdu.c | 39 ++++++++++++++++++++++++--------- fs/cifs/transport.c | 13 +++++------ 8 files changed, 118 insertions(+), 63 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 0c796a27d1eb..4293b1f13f00 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -216,6 +216,7 @@ struct cifs_io_parms; struct cifs_search_info; struct cifsInodeInfo; struct cifs_open_parms; +struct cifs_credits; struct smb_version_operations { int (*send_cancel)(struct TCP_Server_Info *, struct smb_rqst *, @@ -230,8 +231,9 @@ struct smb_version_operations { /* check response: verify signature, map error */ int (*check_receive)(struct mid_q_entry *, struct TCP_Server_Info *, bool); - void (*add_credits)(struct TCP_Server_Info *, const unsigned int, - const int); + void (*add_credits)(struct TCP_Server_Info *server, + const struct cifs_credits *credits, + const int optype); void (*set_credits)(struct TCP_Server_Info *, const int); int * (*get_credits_field)(struct TCP_Server_Info *, const int); unsigned int (*get_credits)(struct mid_q_entry *); @@ -454,7 +456,7 @@ struct smb_version_operations { unsigned int (*wp_retry_size)(struct inode *); /* get mtu credits */ int (*wait_mtu_credits)(struct TCP_Server_Info *, unsigned int, - unsigned int *, unsigned int *); + unsigned int *, struct cifs_credits *); /* check if we need to issue closedir */ bool (*dir_needs_close)(struct cifsFileInfo *); long (*fallocate)(struct file *, struct cifs_tcon *, int, loff_t, @@ -713,6 +715,11 @@ struct TCP_Server_Info { int nr_targets; }; +struct cifs_credits { + unsigned int value; + unsigned int instance; +}; + static inline unsigned int in_flight(struct TCP_Server_Info *server) { @@ -737,15 +744,17 @@ static inline void add_credits(struct TCP_Server_Info *server, const unsigned int add, const int optype) { - server->ops->add_credits(server, add, optype); + struct cifs_credits credits = { .value = add, .instance = 0 }; + + server->ops->add_credits(server, &credits, optype); } static inline void -add_credits_and_wake_if(struct TCP_Server_Info *server, const unsigned int add, - const int optype) +add_credits_and_wake_if(struct TCP_Server_Info *server, + const struct cifs_credits *credits, const int optype) { - if (add) { - server->ops->add_credits(server, add, optype); + if (credits->value) { + server->ops->add_credits(server, credits, optype); wake_up(&server->request_q); } } @@ -1253,7 +1262,7 @@ struct cifs_readdata { unsigned int pagesz; unsigned int page_offset; unsigned int tailsz; - unsigned int credits; + struct cifs_credits credits; unsigned int nr_pages; struct page **pages; }; @@ -1279,7 +1288,7 @@ struct cifs_writedata { unsigned int pagesz; unsigned int page_offset; unsigned int tailsz; - unsigned int credits; + struct cifs_credits credits; unsigned int nr_pages; struct page **pages; }; diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 336c116995d7..7a9a9fd12ea9 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -115,7 +115,7 @@ extern int cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, bool log_error); extern int cifs_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, unsigned int *num, - unsigned int *credits); + struct cifs_credits *credits); extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *, struct kvec *, int /* nvec to send */, int * /* type of buf returned */, const int flags, diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 46bee3b4eeed..b95db2b593cb 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -593,6 +593,7 @@ cifs_reconnect(struct TCP_Server_Info *server) msleep(3000); } else { atomic_inc(&tcpSesReconnectCount); + set_credits(server, 1); spin_lock(&GlobalMid_Lock); if (server->tcpStatus != CifsExiting) server->tcpStatus = CifsNeedNegotiate; @@ -4951,8 +4952,6 @@ cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses) if (!server->ops->need_neg(server)) return 0; - set_credits(server, 1); - rc = server->ops->negotiate(xid, ses); if (rc == 0) { spin_lock(&GlobalMid_Lock); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index da1f05f4ac0c..61b4dc7cfb91 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2143,11 +2143,13 @@ static int cifs_writepages(struct address_space *mapping, server = cifs_sb_master_tcon(cifs_sb)->ses->server; retry: while (!done && index <= end) { - unsigned int i, nr_pages, found_pages, wsize, credits; + unsigned int i, nr_pages, found_pages, wsize; pgoff_t next = 0, tofind, saved_index = index; + struct cifs_credits credits_on_stack; + struct cifs_credits *credits = &credits_on_stack; rc = server->ops->wait_mtu_credits(server, cifs_sb->wsize, - &wsize, &credits); + &wsize, credits); if (rc != 0) { done = true; break; @@ -2180,13 +2182,13 @@ retry: continue; } - wdata->credits = credits; + wdata->credits = credits_on_stack; rc = wdata_send_pages(wdata, nr_pages, mapping, wbc); /* send failure -- clean up the mess */ if (rc != 0) { - add_credits_and_wake_if(server, wdata->credits, 0); + add_credits_and_wake_if(server, &wdata->credits, 0); for (i = 0; i < nr_pages; ++i) { if (is_retryable_error(rc)) redirty_page_for_writepage(wbc, @@ -2567,7 +2569,8 @@ static int cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list, struct cifs_aio_ctx *ctx) { - unsigned int wsize, credits; + unsigned int wsize; + struct cifs_credits credits; int rc; struct TCP_Server_Info *server = tlink_tcon(wdata->cfile->tlink)->ses->server; @@ -2577,18 +2580,19 @@ cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list, * Note: we are attempting to resend the whole wdata not in segments */ do { - rc = server->ops->wait_mtu_credits( - server, wdata->bytes, &wsize, &credits); + rc = server->ops->wait_mtu_credits(server, wdata->bytes, &wsize, + &credits); if (rc) goto out; if (wsize < wdata->bytes) { - add_credits_and_wake_if(server, credits, 0); + add_credits_and_wake_if(server, &credits, 0); msleep(1000); } } while (wsize < wdata->bytes); + wdata->credits = credits; rc = -EAGAIN; while (rc == -EAGAIN) { rc = 0; @@ -2604,7 +2608,7 @@ cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list, return 0; } - add_credits_and_wake_if(server, wdata->credits, 0); + add_credits_and_wake_if(server, &wdata->credits, 0); out: kref_put(&wdata->refcount, cifs_uncached_writedata_release); @@ -2627,6 +2631,7 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from, struct TCP_Server_Info *server; struct page **pagevec; size_t start; + unsigned int xid; if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) pid = open_file->pid; @@ -2634,12 +2639,15 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from, pid = current->tgid; server = tlink_tcon(open_file->tlink)->ses->server; + xid = get_xid(); do { - unsigned int wsize, credits; + unsigned int wsize; + struct cifs_credits credits_on_stack; + struct cifs_credits *credits = &credits_on_stack; rc = server->ops->wait_mtu_credits(server, cifs_sb->wsize, - &wsize, &credits); + &wsize, credits); if (rc) break; @@ -2731,7 +2739,7 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from, wdata->pid = pid; wdata->bytes = cur_len; wdata->pagesz = PAGE_SIZE; - wdata->credits = credits; + wdata->credits = credits_on_stack; wdata->ctx = ctx; kref_get(&ctx->refcount); @@ -2740,7 +2748,7 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from, rc = server->ops->async_writev(wdata, cifs_uncached_writedata_release); if (rc) { - add_credits_and_wake_if(server, wdata->credits, 0); + add_credits_and_wake_if(server, &wdata->credits, 0); kref_put(&wdata->refcount, cifs_uncached_writedata_release); if (rc == -EAGAIN) { @@ -2756,6 +2764,7 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from, len -= cur_len; } while (len > 0); + free_xid(xid); return rc; } @@ -3260,7 +3269,8 @@ static int cifs_resend_rdata(struct cifs_readdata *rdata, struct list_head *rdata_list, struct cifs_aio_ctx *ctx) { - unsigned int rsize, credits; + unsigned int rsize; + struct cifs_credits credits; int rc; struct TCP_Server_Info *server = tlink_tcon(rdata->cfile->tlink)->ses->server; @@ -3277,11 +3287,12 @@ static int cifs_resend_rdata(struct cifs_readdata *rdata, goto out; if (rsize < rdata->bytes) { - add_credits_and_wake_if(server, credits, 0); + add_credits_and_wake_if(server, &credits, 0); msleep(1000); } } while (rsize < rdata->bytes); + rdata->credits = credits; rc = -EAGAIN; while (rc == -EAGAIN) { rc = 0; @@ -3297,7 +3308,7 @@ static int cifs_resend_rdata(struct cifs_readdata *rdata, return 0; } - add_credits_and_wake_if(server, rdata->credits, 0); + add_credits_and_wake_if(server, &rdata->credits, 0); out: kref_put(&rdata->refcount, cifs_uncached_readdata_release); @@ -3311,7 +3322,9 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file, struct cifs_aio_ctx *ctx) { struct cifs_readdata *rdata; - unsigned int npages, rsize, credits; + unsigned int npages, rsize; + struct cifs_credits credits_on_stack; + struct cifs_credits *credits = &credits_on_stack; size_t cur_len; int rc; pid_t pid; @@ -3332,7 +3345,7 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file, do { rc = server->ops->wait_mtu_credits(server, cifs_sb->rsize, - &rsize, &credits); + &rsize, credits); if (rc) break; @@ -3406,7 +3419,7 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file, rdata->pagesz = PAGE_SIZE; rdata->read_into_pages = cifs_uncached_read_into_pages; rdata->copy_into_pages = cifs_uncached_copy_into_pages; - rdata->credits = credits; + rdata->credits = credits_on_stack; rdata->ctx = ctx; kref_get(&ctx->refcount); @@ -3414,7 +3427,7 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file, !(rc = cifs_reopen_file(rdata->cfile, true))) rc = server->ops->async_readv(rdata); if (rc) { - add_credits_and_wake_if(server, rdata->credits, 0); + add_credits_and_wake_if(server, &rdata->credits, 0); kref_put(&rdata->refcount, cifs_uncached_readdata_release); if (rc == -EAGAIN) { @@ -4093,10 +4106,11 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, loff_t offset; struct page *page, *tpage; struct cifs_readdata *rdata; - unsigned credits; + struct cifs_credits credits_on_stack; + struct cifs_credits *credits = &credits_on_stack; rc = server->ops->wait_mtu_credits(server, cifs_sb->rsize, - &rsize, &credits); + &rsize, credits); if (rc) break; @@ -4142,7 +4156,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, rdata->tailsz = PAGE_SIZE; rdata->read_into_pages = cifs_readpages_read_into_pages; rdata->copy_into_pages = cifs_readpages_copy_into_pages; - rdata->credits = credits; + rdata->credits = credits_on_stack; list_for_each_entry_safe(page, tpage, &tmplist, lru) { list_del(&page->lru); @@ -4153,7 +4167,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, !(rc = cifs_reopen_file(rdata->cfile, true))) rc = server->ops->async_readv(rdata); if (rc) { - add_credits_and_wake_if(server, rdata->credits, 0); + add_credits_and_wake_if(server, &rdata->credits, 0); for (i = 0; i < rdata->nr_pages; i++) { page = rdata->pages[i]; lru_cache_add_file(page); diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index 32a6c020478f..6f92d649f6fb 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -117,11 +117,11 @@ cifs_find_mid(struct TCP_Server_Info *server, char *buffer) } static void -cifs_add_credits(struct TCP_Server_Info *server, const unsigned int add, - const int optype) +cifs_add_credits(struct TCP_Server_Info *server, + const struct cifs_credits *credits, const int optype) { spin_lock(&server->req_lock); - server->credits += add; + server->credits += credits->value; server->in_flight--; spin_unlock(&server->req_lock); wake_up(&server->request_q); diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 7291d8289511..6f1a5f504c02 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -67,10 +67,13 @@ change_conf(struct TCP_Server_Info *server) } static void -smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add, - const int optype) +smb2_add_credits(struct TCP_Server_Info *server, + const struct cifs_credits *credits, const int optype) { int *val, rc = -1; + unsigned int add = credits->value; + unsigned int instance = credits->instance; + bool reconnect_detected = false; spin_lock(&server->req_lock); val = server->ops->get_credits_field(server, optype); @@ -79,8 +82,11 @@ smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add, if (((optype & CIFS_OP_MASK) == CIFS_NEG_OP) && (*val != 0)) trace_smb3_reconnect_with_invalid_credits(server->CurrentMid, server->hostname, *val); + if ((instance == 0) || (instance == server->reconnect_instance)) + *val += add; + else + reconnect_detected = true; - *val += add; if (*val > 65000) { *val = 65000; /* Don't get near 64K credits, avoid srv bugs */ printk_once(KERN_WARNING "server overflowed SMB3 credits\n"); @@ -102,6 +108,10 @@ smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add, spin_unlock(&server->req_lock); wake_up(&server->request_q); + if (reconnect_detected) + cifs_dbg(FYI, "trying to put %d credits from the old server instance %d\n", + add, instance); + if (server->tcpStatus == CifsNeedReconnect || server->tcpStatus == CifsExiting) return; @@ -164,7 +174,7 @@ smb2_get_credits(struct mid_q_entry *mid) static int smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, - unsigned int *num, unsigned int *credits) + unsigned int *num, struct cifs_credits *credits) { int rc = 0; unsigned int scredits; @@ -190,7 +200,8 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, /* can deadlock with reopen */ if (scredits <= 8) { *num = SMB2_MAX_BUFFER_SIZE; - *credits = 0; + credits->value = 0; + credits->instance = 0; break; } @@ -199,8 +210,10 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, *num = min_t(unsigned int, size, scredits * SMB2_MAX_BUFFER_SIZE); - *credits = DIV_ROUND_UP(*num, SMB2_MAX_BUFFER_SIZE); - server->credits -= *credits; + credits->value = + DIV_ROUND_UP(*num, SMB2_MAX_BUFFER_SIZE); + credits->instance = server->reconnect_instance; + server->credits -= credits->value; server->in_flight++; break; } diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 77f2c723befa..04581f06f29f 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -3294,9 +3294,9 @@ smb2_async_readv(struct cifs_readdata *rdata) rc = smb2_new_read_req( (void **) &buf, &total_len, &io_parms, rdata, 0, 0); if (rc) { - if (rc == -EAGAIN && rdata->credits) { + if (rc == -EAGAIN && rdata->credits.value) { /* credits was reset by reconnect */ - rdata->credits = 0; + rdata->credits.value = 0; /* reduce in_flight value since we won't send the req */ spin_lock(&server->req_lock); server->in_flight--; @@ -3313,17 +3313,26 @@ smb2_async_readv(struct cifs_readdata *rdata) shdr = (struct smb2_sync_hdr *)buf; - if (rdata->credits) { + if (rdata->credits.value > 0) { shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes, SMB2_MAX_BUFFER_SIZE)); shdr->CreditRequest = cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 1); spin_lock(&server->req_lock); - server->credits += rdata->credits - + if (server->reconnect_instance == rdata->credits.instance) + server->credits += rdata->credits.value - le16_to_cpu(shdr->CreditCharge); + else { + spin_unlock(&server->req_lock); + cifs_dbg(VFS, "trying to return %u credits to old session\n", + rdata->credits.value + - le16_to_cpu(shdr->CreditCharge)); + rc = -EAGAIN; + goto async_readv_out; + } spin_unlock(&server->req_lock); wake_up(&server->request_q); - rdata->credits = le16_to_cpu(shdr->CreditCharge); + rdata->credits.value = le16_to_cpu(shdr->CreditCharge); flags |= CIFS_HAS_CREDITS; } @@ -3340,6 +3349,7 @@ smb2_async_readv(struct cifs_readdata *rdata) io_parms.offset, io_parms.length, rc); } +async_readv_out: cifs_small_buf_release(buf); return rc; } @@ -3508,9 +3518,9 @@ smb2_async_writev(struct cifs_writedata *wdata, rc = smb2_plain_req_init(SMB2_WRITE, tcon, (void **) &req, &total_len); if (rc) { - if (rc == -EAGAIN && wdata->credits) { + if (rc == -EAGAIN && wdata->credits.value) { /* credits was reset by reconnect */ - wdata->credits = 0; + wdata->credits.value = 0; /* reduce in_flight value since we won't send the req */ spin_lock(&server->req_lock); server->in_flight--; @@ -3603,17 +3613,26 @@ smb2_async_writev(struct cifs_writedata *wdata, req->Length = cpu_to_le32(wdata->bytes); #endif - if (wdata->credits) { + if (wdata->credits.value > 0) { shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes, SMB2_MAX_BUFFER_SIZE)); shdr->CreditRequest = cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 1); spin_lock(&server->req_lock); - server->credits += wdata->credits - + if (server->reconnect_instance == wdata->credits.instance) + server->credits += wdata->credits.value - le16_to_cpu(shdr->CreditCharge); + else { + spin_unlock(&server->req_lock); + cifs_dbg(VFS, "trying to return %d credits to old session\n", + wdata->credits.value + - le16_to_cpu(shdr->CreditCharge)); + rc = -EAGAIN; + goto async_writev_out; + } spin_unlock(&server->req_lock); wake_up(&server->request_q); - wdata->credits = le16_to_cpu(shdr->CreditCharge); + wdata->credits.value = le16_to_cpu(shdr->CreditCharge); flags |= CIFS_HAS_CREDITS; } diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 9544eb99b5a2..099a1c914720 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -512,10 +512,11 @@ wait_for_free_request(struct TCP_Server_Info *server, const int timeout, int cifs_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, - unsigned int *num, unsigned int *credits) + unsigned int *num, struct cifs_credits *credits) { *num = size; - *credits = 0; + credits->value = 0; + credits->instance = server->reconnect_instance; return 0; } @@ -606,7 +607,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst, { int rc, timeout, optype; struct mid_q_entry *mid; - unsigned int credits = 0; + struct cifs_credits credits = { .value = 0, .instance = 0 }; timeout = flags & CIFS_TIMEOUT_MASK; optype = flags & CIFS_OP_MASK; @@ -615,14 +616,14 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst, rc = wait_for_free_request(server, timeout, optype); if (rc) return rc; - credits = 1; + credits.value = 1; } mutex_lock(&server->srv_mutex); mid = server->ops->setup_async_request(server,