Message ID | 1405957558-18476-11-git-send-email-pshilovsky@samba.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Looks correct. Reviewed-by: Shirish Pargaonkar <spargaonkar@suse.com> Only comment would be, wish there was a mnemonic/define for a regular op when calling add_credits_and_wake_if() for optype such as CIFS_ECHO_OP or CIFS_OPBREAK_OP instead of 0. Oh and super nitpick... s/reseted/reset/. On Mon, Jul 21, 2014 at 10:45 AM, Pavel Shilovsky <pshilovsky@samba.org> wrote: > If we negotiate SMB 2.1 and higher version of the protocol and > a server supports large write buffer size, we need to consume 1 > credit per 65536 bytes. So, we need to know how many credits > we have and obtain the required number of them before constructing > a writedata structure in writepages and iovec write. > > Signed-off-by: Pavel Shilovsky <pshilovsky@samba.org> > --- > fs/cifs/cifsglob.h | 15 ++++++++++++++ > fs/cifs/cifsproto.h | 3 +++ > fs/cifs/file.c | 36 ++++++++++++++++++++++++++++------ > fs/cifs/smb1ops.c | 1 + > fs/cifs/smb2ops.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++-- > fs/cifs/smb2pdu.c | 29 +++++++++++++++++++++++---- > fs/cifs/smb2transport.c | 5 +++++ > fs/cifs/transport.c | 25 +++++++++++++++++------- > 8 files changed, 147 insertions(+), 19 deletions(-) > > diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h > index 8c53f20..54ca2b9 100644 > --- a/fs/cifs/cifsglob.h > +++ b/fs/cifs/cifsglob.h > @@ -406,6 +406,9 @@ struct smb_version_operations { > int); > /* writepages retry size */ > unsigned int (*wp_retry_size)(struct inode *); > + /* get mtu credits */ > + int (*wait_mtu_credits)(struct TCP_Server_Info *, unsigned int, > + unsigned int *, unsigned int *); > }; > > struct smb_version_values { > @@ -642,6 +645,16 @@ add_credits(struct TCP_Server_Info *server, const unsigned int add, > } > > static inline void > +add_credits_and_wake_if(struct TCP_Server_Info *server, const unsigned int add, > + const int optype) > +{ > + if (add) { > + server->ops->add_credits(server, add, optype); > + wake_up(&server->request_q); > + } > +} > + > +static inline void > set_credits(struct TCP_Server_Info *server, const int val) > { > server->ops->set_credits(server, val); > @@ -1075,6 +1088,7 @@ struct cifs_writedata { > int result; > unsigned int pagesz; > unsigned int tailsz; > + unsigned int credits; > unsigned int nr_pages; > struct page *pages[]; > }; > @@ -1400,6 +1414,7 @@ static inline void free_dfs_info_array(struct dfs_info3_param *param, > #define CIFS_OBREAK_OP 0x0100 /* oplock break request */ > #define CIFS_NEG_OP 0x0200 /* negotiate request */ > #define CIFS_OP_MASK 0x0380 /* mask request type */ > +#define CIFS_HAS_CREDITS 0x0400 /* already has credits */ > > /* Security Flags: indicate type of session setup needed */ > #define CIFSSEC_MAY_SIGN 0x00001 > diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h > index ca7980a..009e5cb 100644 > --- a/fs/cifs/cifsproto.h > +++ b/fs/cifs/cifsproto.h > @@ -89,6 +89,9 @@ extern struct mid_q_entry *cifs_setup_async_request(struct TCP_Server_Info *, > struct smb_rqst *); > 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); > 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/file.c b/fs/cifs/file.c > index c9c4f5a..c79bdf3 100644 > --- a/fs/cifs/file.c > +++ b/fs/cifs/file.c > @@ -1670,8 +1670,8 @@ cifs_write(struct cifsFileInfo *open_file, __u32 pid, const char *write_data, > break; > } > > - len = min((size_t)cifs_sb->wsize, > - write_size - total_written); > + len = min(server->ops->wp_retry_size(dentry->d_inode), > + (unsigned int)write_size - total_written); > /* iov[0] is reserved for smb header */ > iov[1].iov_base = (char *)write_data + total_written; > iov[1].iov_len = len; > @@ -2031,6 +2031,7 @@ static int cifs_writepages(struct address_space *mapping, > struct writeback_control *wbc) > { > struct cifs_sb_info *cifs_sb = CIFS_SB(mapping->host->i_sb); > + struct TCP_Server_Info *server; > bool done = false, scanned = false, range_whole = false; > pgoff_t end, index; > struct cifs_writedata *wdata; > @@ -2053,23 +2054,30 @@ static int cifs_writepages(struct address_space *mapping, > range_whole = true; > scanned = true; > } > + server = cifs_sb_master_tcon(cifs_sb)->ses->server; > retry: > while (!done && index <= end) { > - unsigned int i, nr_pages, found_pages; > + unsigned int i, nr_pages, found_pages, wsize, credits; > pgoff_t next = 0, tofind, saved_index = index; > > - tofind = min((cifs_sb->wsize / PAGE_CACHE_SIZE) - 1, > - end - index) + 1; > + rc = server->ops->wait_mtu_credits(server, cifs_sb->wsize, > + &wsize, &credits); > + if (rc) > + break; > + > + tofind = min((wsize / PAGE_CACHE_SIZE) - 1, end - index) + 1; > > wdata = wdata_alloc_and_fillpages(tofind, mapping, end, &index, > &found_pages); > if (!wdata) { > rc = -ENOMEM; > + add_credits_and_wake_if(server, credits, 0); > break; > } > > if (found_pages == 0) { > kref_put(&wdata->refcount, cifs_writedata_release); > + add_credits_and_wake_if(server, credits, 0); > break; > } > > @@ -2079,13 +2087,17 @@ retry: > /* nothing to write? */ > if (nr_pages == 0) { > kref_put(&wdata->refcount, cifs_writedata_release); > + add_credits_and_wake_if(server, credits, 0); > continue; > } > > + wdata->credits = credits; > + > 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); > for (i = 0; i < nr_pages; ++i) { > if (rc == -EAGAIN) > redirty_page_for_writepage(wbc, > @@ -2466,17 +2478,26 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from, > memcpy(&saved_from, from, sizeof(struct iov_iter)); > > do { > - nr_pages = get_numpages(cifs_sb->wsize, len, &cur_len); > + unsigned int wsize, credits; > + > + rc = server->ops->wait_mtu_credits(server, cifs_sb->wsize, > + &wsize, &credits); > + if (rc) > + break; > + > + nr_pages = get_numpages(wsize, len, &cur_len); > wdata = cifs_writedata_alloc(nr_pages, > cifs_uncached_writev_complete); > if (!wdata) { > rc = -ENOMEM; > + add_credits_and_wake_if(server, credits, 0); > break; > } > > rc = cifs_write_allocate_pages(wdata->pages, nr_pages); > if (rc) { > kfree(wdata); > + add_credits_and_wake_if(server, credits, 0); > break; > } > > @@ -2486,6 +2507,7 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from, > for (i = 0; i < nr_pages; i++) > put_page(wdata->pages[i]); > kfree(wdata); > + add_credits_and_wake_if(server, credits, 0); > break; > } > > @@ -2504,12 +2526,14 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from, > wdata->bytes = cur_len; > wdata->pagesz = PAGE_SIZE; > wdata->tailsz = cur_len - ((nr_pages - 1) * PAGE_SIZE); > + wdata->credits = credits; > > if (!wdata->cfile->invalidHandle || > !cifs_reopen_file(wdata->cfile, false)) > rc = server->ops->async_writev(wdata, > cifs_uncached_writedata_release); > if (rc) { > + add_credits_and_wake_if(server, wdata->credits, 0); > kref_put(&wdata->refcount, > cifs_uncached_writedata_release); > if (rc == -EAGAIN) { > diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c > index 8a96342..5e8c22d 100644 > --- a/fs/cifs/smb1ops.c > +++ b/fs/cifs/smb1ops.c > @@ -1025,6 +1025,7 @@ struct smb_version_operations smb1_operations = { > .set_credits = cifs_set_credits, > .get_credits_field = cifs_get_credits_field, > .get_credits = cifs_get_credits, > + .wait_mtu_credits = cifs_wait_mtu_credits, > .get_next_mid = cifs_get_next_mid, > .read_data_offset = cifs_read_data_offset, > .read_data_length = cifs_read_data_length, > diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c > index e35ce5b..fecc2de 100644 > --- a/fs/cifs/smb2ops.c > +++ b/fs/cifs/smb2ops.c > @@ -112,6 +112,53 @@ smb2_get_credits(struct mid_q_entry *mid) > return le16_to_cpu(((struct smb2_hdr *)mid->resp_buf)->CreditRequest); > } > > +static int > +smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, > + unsigned int *num, unsigned int *credits) > +{ > + int rc = 0; > + unsigned int scredits; > + > + spin_lock(&server->req_lock); > + while (1) { > + if (server->credits <= 0) { > + spin_unlock(&server->req_lock); > + cifs_num_waiters_inc(server); > + rc = wait_event_killable(server->request_q, > + has_credits(server, &server->credits)); > + cifs_num_waiters_dec(server); > + if (rc) > + return rc; > + spin_lock(&server->req_lock); > + } else { > + if (server->tcpStatus == CifsExiting) { > + spin_unlock(&server->req_lock); > + return -ENOENT; > + } > + > + scredits = server->credits; > + /* can deadlock with reopen */ > + if (scredits == 1) { > + *num = SMB2_MAX_BUFFER_SIZE; > + *credits = 0; > + break; > + } > + > + /* leave one credit for a possible reopen */ > + scredits--; > + *num = min_t(unsigned int, size, > + scredits * SMB2_MAX_BUFFER_SIZE); > + > + *credits = DIV_ROUND_UP(*num, SMB2_MAX_BUFFER_SIZE); > + server->credits -= *credits; > + server->in_flight++; > + break; > + } > + } > + spin_unlock(&server->req_lock); > + return rc; > +} > + > static __u64 > smb2_get_next_mid(struct TCP_Server_Info *server) > { > @@ -182,8 +229,6 @@ smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) > /* start with specified wsize, or default */ > wsize = volume_info->wsize ? volume_info->wsize : CIFS_DEFAULT_IOSIZE; > wsize = min_t(unsigned int, wsize, server->max_write); > - /* set it to the maximum buffer size value we can send with 1 credit */ > - wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE); > > return wsize; > } > @@ -1120,6 +1165,7 @@ struct smb_version_operations smb20_operations = { > .set_credits = smb2_set_credits, > .get_credits_field = smb2_get_credits_field, > .get_credits = smb2_get_credits, > + .wait_mtu_credits = cifs_wait_mtu_credits, > .get_next_mid = smb2_get_next_mid, > .read_data_offset = smb2_read_data_offset, > .read_data_length = smb2_read_data_length, > @@ -1196,6 +1242,7 @@ struct smb_version_operations smb21_operations = { > .set_credits = smb2_set_credits, > .get_credits_field = smb2_get_credits_field, > .get_credits = smb2_get_credits, > + .wait_mtu_credits = smb2_wait_mtu_credits, > .get_next_mid = smb2_get_next_mid, > .read_data_offset = smb2_read_data_offset, > .read_data_length = smb2_read_data_length, > @@ -1272,6 +1319,7 @@ struct smb_version_operations smb30_operations = { > .set_credits = smb2_set_credits, > .get_credits_field = smb2_get_credits_field, > .get_credits = smb2_get_credits, > + .wait_mtu_credits = smb2_wait_mtu_credits, > .get_next_mid = smb2_get_next_mid, > .read_data_offset = smb2_read_data_offset, > .read_data_length = smb2_read_data_length, > diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c > index 8f5754a..a1d89b7 100644 > --- a/fs/cifs/smb2pdu.c > +++ b/fs/cifs/smb2pdu.c > @@ -1902,15 +1902,25 @@ int > smb2_async_writev(struct cifs_writedata *wdata, > void (*release)(struct kref *kref)) > { > - int rc = -EACCES; > + int rc = -EACCES, flags = 0; > struct smb2_write_req *req = NULL; > struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); > + struct TCP_Server_Info *server = tcon->ses->server; > struct kvec iov; > struct smb_rqst rqst; > > rc = small_smb2_init(SMB2_WRITE, tcon, (void **) &req); > - if (rc) > + if (rc) { > + if (rc == -EAGAIN && wdata->credits) { > + /* credits was reseted by reconnect */ > + wdata->credits = 0; > + /* reduce in_flight value since we won't send the req */ > + spin_lock(&server->req_lock); > + server->in_flight--; > + spin_unlock(&server->req_lock); > + } > goto async_writev_out; > + } > > req->hdr.ProcessId = cpu_to_le32(wdata->cfile->pid); > > @@ -1943,9 +1953,20 @@ smb2_async_writev(struct cifs_writedata *wdata, > > inc_rfc1001_len(&req->hdr, wdata->bytes - 1 /* Buffer */); > > + if (wdata->credits) { > + req->hdr.CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes, > + SMB2_MAX_BUFFER_SIZE)); > + spin_lock(&server->req_lock); > + server->credits += wdata->credits - > + le16_to_cpu(req->hdr.CreditCharge); > + spin_unlock(&server->req_lock); > + wake_up(&server->request_q); > + flags = CIFS_HAS_CREDITS; > + } > + > kref_get(&wdata->refcount); > - rc = cifs_call_async(tcon->ses->server, &rqst, NULL, > - smb2_writev_callback, wdata, 0); > + rc = cifs_call_async(server, &rqst, NULL, smb2_writev_callback, wdata, > + flags); > > if (rc) { > kref_put(&wdata->refcount, release); > diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c > index 59c748c..5111e72 100644 > --- a/fs/cifs/smb2transport.c > +++ b/fs/cifs/smb2transport.c > @@ -466,7 +466,12 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) > static inline void > smb2_seq_num_into_buf(struct TCP_Server_Info *server, struct smb2_hdr *hdr) > { > + unsigned int i, num = le16_to_cpu(hdr->CreditCharge); > + > hdr->MessageId = get_next_mid64(server); > + /* skip message numbers according to CreditCharge field */ > + for (i = 1; i < num; i++) > + get_next_mid(server); > } > > static struct mid_q_entry * > diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c > index 18cd565..9d087f4 100644 > --- a/fs/cifs/transport.c > +++ b/fs/cifs/transport.c > @@ -448,6 +448,15 @@ wait_for_free_request(struct TCP_Server_Info *server, const int timeout, > return wait_for_free_credits(server, timeout, val); > } > > +int > +cifs_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, > + unsigned int *num, unsigned int *credits) > +{ > + *num = size; > + *credits = 0; > + return 0; > +} > + > static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf, > struct mid_q_entry **ppmidQ) > { > @@ -531,20 +540,23 @@ 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; > > timeout = flags & CIFS_TIMEOUT_MASK; > optype = flags & CIFS_OP_MASK; > > - rc = wait_for_free_request(server, timeout, optype); > - if (rc) > - return rc; > + if ((flags & CIFS_HAS_CREDITS) == 0) { > + rc = wait_for_free_request(server, timeout, optype); > + if (rc) > + return rc; > + credits = 1; > + } > > mutex_lock(&server->srv_mutex); > mid = server->ops->setup_async_request(server, rqst); > if (IS_ERR(mid)) { > mutex_unlock(&server->srv_mutex); > - add_credits(server, 1, optype); > - wake_up(&server->request_q); > + add_credits_and_wake_if(server, credits, optype); > return PTR_ERR(mid); > } > > @@ -572,8 +584,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst, > return 0; > > cifs_delete_mid(mid); > - add_credits(server, 1, optype); > - wake_up(&server->request_q); > + add_credits_and_wake_if(server, credits, optype); > return rc; > } > > -- > 1.8.1.2 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-cifs" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-cifs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
2014-07-25 5:06 GMT+04:00 Shirish Pargaonkar <shirishpargaonkar@gmail.com>: > Looks correct. > > Reviewed-by: Shirish Pargaonkar <spargaonkar@suse.com> Thank you for reviewing the series! > Only comment would be, wish there was a mnemonic/define for > a regular op when calling add_credits_and_wake_if() for optype > such as CIFS_ECHO_OP or CIFS_OPBREAK_OP instead of 0. Yes, it can make sense but should be in another patch/series. > Oh and super nitpick... s/reseted/reset/. Ok, will fix it in my smb2-dev branch on git.altlinux.org.
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 8c53f20..54ca2b9 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -406,6 +406,9 @@ struct smb_version_operations { int); /* writepages retry size */ unsigned int (*wp_retry_size)(struct inode *); + /* get mtu credits */ + int (*wait_mtu_credits)(struct TCP_Server_Info *, unsigned int, + unsigned int *, unsigned int *); }; struct smb_version_values { @@ -642,6 +645,16 @@ add_credits(struct TCP_Server_Info *server, const unsigned int add, } static inline void +add_credits_and_wake_if(struct TCP_Server_Info *server, const unsigned int add, + const int optype) +{ + if (add) { + server->ops->add_credits(server, add, optype); + wake_up(&server->request_q); + } +} + +static inline void set_credits(struct TCP_Server_Info *server, const int val) { server->ops->set_credits(server, val); @@ -1075,6 +1088,7 @@ struct cifs_writedata { int result; unsigned int pagesz; unsigned int tailsz; + unsigned int credits; unsigned int nr_pages; struct page *pages[]; }; @@ -1400,6 +1414,7 @@ static inline void free_dfs_info_array(struct dfs_info3_param *param, #define CIFS_OBREAK_OP 0x0100 /* oplock break request */ #define CIFS_NEG_OP 0x0200 /* negotiate request */ #define CIFS_OP_MASK 0x0380 /* mask request type */ +#define CIFS_HAS_CREDITS 0x0400 /* already has credits */ /* Security Flags: indicate type of session setup needed */ #define CIFSSEC_MAY_SIGN 0x00001 diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index ca7980a..009e5cb 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -89,6 +89,9 @@ extern struct mid_q_entry *cifs_setup_async_request(struct TCP_Server_Info *, struct smb_rqst *); 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); 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/file.c b/fs/cifs/file.c index c9c4f5a..c79bdf3 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1670,8 +1670,8 @@ cifs_write(struct cifsFileInfo *open_file, __u32 pid, const char *write_data, break; } - len = min((size_t)cifs_sb->wsize, - write_size - total_written); + len = min(server->ops->wp_retry_size(dentry->d_inode), + (unsigned int)write_size - total_written); /* iov[0] is reserved for smb header */ iov[1].iov_base = (char *)write_data + total_written; iov[1].iov_len = len; @@ -2031,6 +2031,7 @@ static int cifs_writepages(struct address_space *mapping, struct writeback_control *wbc) { struct cifs_sb_info *cifs_sb = CIFS_SB(mapping->host->i_sb); + struct TCP_Server_Info *server; bool done = false, scanned = false, range_whole = false; pgoff_t end, index; struct cifs_writedata *wdata; @@ -2053,23 +2054,30 @@ static int cifs_writepages(struct address_space *mapping, range_whole = true; scanned = true; } + server = cifs_sb_master_tcon(cifs_sb)->ses->server; retry: while (!done && index <= end) { - unsigned int i, nr_pages, found_pages; + unsigned int i, nr_pages, found_pages, wsize, credits; pgoff_t next = 0, tofind, saved_index = index; - tofind = min((cifs_sb->wsize / PAGE_CACHE_SIZE) - 1, - end - index) + 1; + rc = server->ops->wait_mtu_credits(server, cifs_sb->wsize, + &wsize, &credits); + if (rc) + break; + + tofind = min((wsize / PAGE_CACHE_SIZE) - 1, end - index) + 1; wdata = wdata_alloc_and_fillpages(tofind, mapping, end, &index, &found_pages); if (!wdata) { rc = -ENOMEM; + add_credits_and_wake_if(server, credits, 0); break; } if (found_pages == 0) { kref_put(&wdata->refcount, cifs_writedata_release); + add_credits_and_wake_if(server, credits, 0); break; } @@ -2079,13 +2087,17 @@ retry: /* nothing to write? */ if (nr_pages == 0) { kref_put(&wdata->refcount, cifs_writedata_release); + add_credits_and_wake_if(server, credits, 0); continue; } + wdata->credits = credits; + 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); for (i = 0; i < nr_pages; ++i) { if (rc == -EAGAIN) redirty_page_for_writepage(wbc, @@ -2466,17 +2478,26 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from, memcpy(&saved_from, from, sizeof(struct iov_iter)); do { - nr_pages = get_numpages(cifs_sb->wsize, len, &cur_len); + unsigned int wsize, credits; + + rc = server->ops->wait_mtu_credits(server, cifs_sb->wsize, + &wsize, &credits); + if (rc) + break; + + nr_pages = get_numpages(wsize, len, &cur_len); wdata = cifs_writedata_alloc(nr_pages, cifs_uncached_writev_complete); if (!wdata) { rc = -ENOMEM; + add_credits_and_wake_if(server, credits, 0); break; } rc = cifs_write_allocate_pages(wdata->pages, nr_pages); if (rc) { kfree(wdata); + add_credits_and_wake_if(server, credits, 0); break; } @@ -2486,6 +2507,7 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from, for (i = 0; i < nr_pages; i++) put_page(wdata->pages[i]); kfree(wdata); + add_credits_and_wake_if(server, credits, 0); break; } @@ -2504,12 +2526,14 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from, wdata->bytes = cur_len; wdata->pagesz = PAGE_SIZE; wdata->tailsz = cur_len - ((nr_pages - 1) * PAGE_SIZE); + wdata->credits = credits; if (!wdata->cfile->invalidHandle || !cifs_reopen_file(wdata->cfile, false)) rc = server->ops->async_writev(wdata, cifs_uncached_writedata_release); if (rc) { + add_credits_and_wake_if(server, wdata->credits, 0); kref_put(&wdata->refcount, cifs_uncached_writedata_release); if (rc == -EAGAIN) { diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index 8a96342..5e8c22d 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -1025,6 +1025,7 @@ struct smb_version_operations smb1_operations = { .set_credits = cifs_set_credits, .get_credits_field = cifs_get_credits_field, .get_credits = cifs_get_credits, + .wait_mtu_credits = cifs_wait_mtu_credits, .get_next_mid = cifs_get_next_mid, .read_data_offset = cifs_read_data_offset, .read_data_length = cifs_read_data_length, diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index e35ce5b..fecc2de 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -112,6 +112,53 @@ smb2_get_credits(struct mid_q_entry *mid) return le16_to_cpu(((struct smb2_hdr *)mid->resp_buf)->CreditRequest); } +static int +smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, + unsigned int *num, unsigned int *credits) +{ + int rc = 0; + unsigned int scredits; + + spin_lock(&server->req_lock); + while (1) { + if (server->credits <= 0) { + spin_unlock(&server->req_lock); + cifs_num_waiters_inc(server); + rc = wait_event_killable(server->request_q, + has_credits(server, &server->credits)); + cifs_num_waiters_dec(server); + if (rc) + return rc; + spin_lock(&server->req_lock); + } else { + if (server->tcpStatus == CifsExiting) { + spin_unlock(&server->req_lock); + return -ENOENT; + } + + scredits = server->credits; + /* can deadlock with reopen */ + if (scredits == 1) { + *num = SMB2_MAX_BUFFER_SIZE; + *credits = 0; + break; + } + + /* leave one credit for a possible reopen */ + scredits--; + *num = min_t(unsigned int, size, + scredits * SMB2_MAX_BUFFER_SIZE); + + *credits = DIV_ROUND_UP(*num, SMB2_MAX_BUFFER_SIZE); + server->credits -= *credits; + server->in_flight++; + break; + } + } + spin_unlock(&server->req_lock); + return rc; +} + static __u64 smb2_get_next_mid(struct TCP_Server_Info *server) { @@ -182,8 +229,6 @@ smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) /* start with specified wsize, or default */ wsize = volume_info->wsize ? volume_info->wsize : CIFS_DEFAULT_IOSIZE; wsize = min_t(unsigned int, wsize, server->max_write); - /* set it to the maximum buffer size value we can send with 1 credit */ - wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE); return wsize; } @@ -1120,6 +1165,7 @@ struct smb_version_operations smb20_operations = { .set_credits = smb2_set_credits, .get_credits_field = smb2_get_credits_field, .get_credits = smb2_get_credits, + .wait_mtu_credits = cifs_wait_mtu_credits, .get_next_mid = smb2_get_next_mid, .read_data_offset = smb2_read_data_offset, .read_data_length = smb2_read_data_length, @@ -1196,6 +1242,7 @@ struct smb_version_operations smb21_operations = { .set_credits = smb2_set_credits, .get_credits_field = smb2_get_credits_field, .get_credits = smb2_get_credits, + .wait_mtu_credits = smb2_wait_mtu_credits, .get_next_mid = smb2_get_next_mid, .read_data_offset = smb2_read_data_offset, .read_data_length = smb2_read_data_length, @@ -1272,6 +1319,7 @@ struct smb_version_operations smb30_operations = { .set_credits = smb2_set_credits, .get_credits_field = smb2_get_credits_field, .get_credits = smb2_get_credits, + .wait_mtu_credits = smb2_wait_mtu_credits, .get_next_mid = smb2_get_next_mid, .read_data_offset = smb2_read_data_offset, .read_data_length = smb2_read_data_length, diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 8f5754a..a1d89b7 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -1902,15 +1902,25 @@ int smb2_async_writev(struct cifs_writedata *wdata, void (*release)(struct kref *kref)) { - int rc = -EACCES; + int rc = -EACCES, flags = 0; struct smb2_write_req *req = NULL; struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); + struct TCP_Server_Info *server = tcon->ses->server; struct kvec iov; struct smb_rqst rqst; rc = small_smb2_init(SMB2_WRITE, tcon, (void **) &req); - if (rc) + if (rc) { + if (rc == -EAGAIN && wdata->credits) { + /* credits was reseted by reconnect */ + wdata->credits = 0; + /* reduce in_flight value since we won't send the req */ + spin_lock(&server->req_lock); + server->in_flight--; + spin_unlock(&server->req_lock); + } goto async_writev_out; + } req->hdr.ProcessId = cpu_to_le32(wdata->cfile->pid); @@ -1943,9 +1953,20 @@ smb2_async_writev(struct cifs_writedata *wdata, inc_rfc1001_len(&req->hdr, wdata->bytes - 1 /* Buffer */); + if (wdata->credits) { + req->hdr.CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes, + SMB2_MAX_BUFFER_SIZE)); + spin_lock(&server->req_lock); + server->credits += wdata->credits - + le16_to_cpu(req->hdr.CreditCharge); + spin_unlock(&server->req_lock); + wake_up(&server->request_q); + flags = CIFS_HAS_CREDITS; + } + kref_get(&wdata->refcount); - rc = cifs_call_async(tcon->ses->server, &rqst, NULL, - smb2_writev_callback, wdata, 0); + rc = cifs_call_async(server, &rqst, NULL, smb2_writev_callback, wdata, + flags); if (rc) { kref_put(&wdata->refcount, release); diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index 59c748c..5111e72 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c @@ -466,7 +466,12 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) static inline void smb2_seq_num_into_buf(struct TCP_Server_Info *server, struct smb2_hdr *hdr) { + unsigned int i, num = le16_to_cpu(hdr->CreditCharge); + hdr->MessageId = get_next_mid64(server); + /* skip message numbers according to CreditCharge field */ + for (i = 1; i < num; i++) + get_next_mid(server); } static struct mid_q_entry * diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 18cd565..9d087f4 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -448,6 +448,15 @@ wait_for_free_request(struct TCP_Server_Info *server, const int timeout, return wait_for_free_credits(server, timeout, val); } +int +cifs_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, + unsigned int *num, unsigned int *credits) +{ + *num = size; + *credits = 0; + return 0; +} + static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf, struct mid_q_entry **ppmidQ) { @@ -531,20 +540,23 @@ 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; timeout = flags & CIFS_TIMEOUT_MASK; optype = flags & CIFS_OP_MASK; - rc = wait_for_free_request(server, timeout, optype); - if (rc) - return rc; + if ((flags & CIFS_HAS_CREDITS) == 0) { + rc = wait_for_free_request(server, timeout, optype); + if (rc) + return rc; + credits = 1; + } mutex_lock(&server->srv_mutex); mid = server->ops->setup_async_request(server, rqst); if (IS_ERR(mid)) { mutex_unlock(&server->srv_mutex); - add_credits(server, 1, optype); - wake_up(&server->request_q); + add_credits_and_wake_if(server, credits, optype); return PTR_ERR(mid); } @@ -572,8 +584,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst, return 0; cifs_delete_mid(mid); - add_credits(server, 1, optype); - wake_up(&server->request_q); + add_credits_and_wake_if(server, credits, optype); return rc; }
If we negotiate SMB 2.1 and higher version of the protocol and a server supports large write buffer size, we need to consume 1 credit per 65536 bytes. So, we need to know how many credits we have and obtain the required number of them before constructing a writedata structure in writepages and iovec write. Signed-off-by: Pavel Shilovsky <pshilovsky@samba.org> --- fs/cifs/cifsglob.h | 15 ++++++++++++++ fs/cifs/cifsproto.h | 3 +++ fs/cifs/file.c | 36 ++++++++++++++++++++++++++++------ fs/cifs/smb1ops.c | 1 + fs/cifs/smb2ops.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++-- fs/cifs/smb2pdu.c | 29 +++++++++++++++++++++++---- fs/cifs/smb2transport.c | 5 +++++ fs/cifs/transport.c | 25 +++++++++++++++++------- 8 files changed, 147 insertions(+), 19 deletions(-)