Signed-off-by: Pavel Shilovsky <pshilovsky@xxxxxxxxx> --- fs/cifs/cifsglob.h | 7 ++++++ fs/cifs/cifsproto.h | 3 ++ fs/cifs/cifssmb.c | 7 ++++- fs/cifs/file.c | 33 ++++++++++++++++++++++++++++--- fs/cifs/smb1ops.c | 8 +++++++ fs/cifs/smb2ops.c | 48 ++++++++++++++++++++++++++++++++++++++++++---- fs/cifs/smb2pdu.c | 14 +++++++++++- fs/cifs/smb2transport.c | 4 +++ fs/cifs/transport.c | 29 +++++++++++++++++++++++---- 9 files changed, 135 insertions(+), 18 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 3155080..3e5ab4e 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -367,6 +367,11 @@ struct smb_version_operations { void (*set_lease_key)(struct inode *, struct cifs_fid *fid); /* generate new lease key */ void (*new_lease_key)(struct cifs_fid *fid); + /* get mtu credits */ + int (*wait_mtu_credits)(struct TCP_Server_Info *, unsigned int, + unsigned int *, unsigned int *); + /* how many credits a packet charge */ + unsigned int (*get_credit_size)(char *); }; struct smb_version_values { @@ -983,6 +988,7 @@ struct cifs_writedata { int result; unsigned int pagesz; unsigned int tailsz; + unsigned int credits; unsigned int nr_pages; struct page *pages[1]; }; @@ -1303,6 +1309,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 15e38dc..5411857 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -86,6 +86,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/cifssmb.c b/fs/cifs/cifssmb.c index 88bbb3e..75e9c87 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -2025,7 +2025,7 @@ cifs_writev_callback(struct mid_q_entry *mid) int cifs_async_writev(struct cifs_writedata *wdata) { - int rc = -EACCES; + int rc = -EACCES, flags = 0; WRITE_REQ *smb = NULL; int wct; struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); @@ -2089,9 +2089,12 @@ cifs_async_writev(struct cifs_writedata *wdata) iov.iov_len += 4; /* pad bigger by four bytes */ } + if (wdata->credits) + flags = CIFS_HAS_CREDITS; + kref_get(&wdata->refcount); rc = cifs_call_async(tcon->ses->server, &rqst, NULL, - cifs_writev_callback, wdata, 0); + cifs_writev_callback, wdata, flags); if (rc == 0) cifs_stats_inc(&tcon->stats.cifs_stats.num_writes); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index af4a832..99c7532 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1781,20 +1781,29 @@ static int cifs_writepages(struct address_space *mapping, } 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; struct page **pages; - tofind = min((cifs_sb->wsize / PAGE_CACHE_SIZE) - 1, - end - index) + 1; + server = cifs_sb_master_tcon(cifs_sb)->ses->server; + 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 = cifs_writedata_alloc((unsigned int)tofind, cifs_writev_complete); if (!wdata) { rc = -ENOMEM; + add_credits(server, credits, 0); + wake_up(&server->request_q); break; } + wdata->credits = credits; + /* * find_get_pages_tag seems to return a max of 256 on each * iteration, so we must call it several times in order to @@ -1814,6 +1823,8 @@ retry: if (found_pages == 0) { kref_put(&wdata->refcount, cifs_writedata_release); + add_credits(server, credits, 0); + wake_up(&server->request_q); break; } @@ -1890,6 +1901,8 @@ retry: /* nothing to write? */ if (nr_pages == 0) { kref_put(&wdata->refcount, cifs_writedata_release); + add_credits(server, credits, 0); + wake_up(&server->request_q); continue; } @@ -2259,6 +2272,7 @@ cifs_iovec_write(struct file *file, const struct iovec *iov, struct iov_iter it; struct cifsFileInfo *open_file; struct cifs_tcon *tcon; + struct TCP_Server_Info *server; struct cifs_sb_info *cifs_sb; struct cifs_writedata *wdata, *tmp; struct list_head wdata_list; @@ -2291,18 +2305,29 @@ cifs_iovec_write(struct file *file, const struct iovec *iov, iov_iter_init(&it, iov, nr_segs, len, 0); do { size_t save_len; + unsigned int wsize, credits; + + server = tcon->ses->server; + rc = server->ops->wait_mtu_credits(server, cifs_sb->wsize, + &wsize, &credits); - nr_pages = get_numpages(cifs_sb->wsize, len, &cur_len); + nr_pages = get_numpages(wsize, len, &cur_len); wdata = cifs_writedata_alloc(nr_pages, cifs_uncached_writev_complete); if (!wdata) { rc = -ENOMEM; + add_credits(server, credits, 0); + wake_up(&server->request_q); break; } + wdata->credits = credits; + rc = cifs_write_allocate_pages(wdata->pages, nr_pages); if (rc) { kfree(wdata); + add_credits(server, credits, 0); + wake_up(&server->request_q); break; } diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index 5fb0fe5..7e34bd1 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -134,6 +134,12 @@ cifs_get_credits(struct mid_q_entry *mid) return 1; } +static unsigned int +cifs_get_credit_size(char *buf) +{ + return 1; +} + /* * Find a free multiplex id (SMB mid). Otherwise there could be * mid collisions which might cause problems, demultiplexing the @@ -918,6 +924,8 @@ 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_credit_size = cifs_get_credit_size, .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 6a11190..8b93f03 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -111,6 +111,47 @@ 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; + 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; + } + + *num = min_t(unsigned int, server->credits * (2 << 15), + size); + + *credits = DIV_ROUND_UP(*num, 2 << 15); + server->credits -= *credits; + server->in_flight++; + break; + } + } + spin_unlock(&server->req_lock); + return rc; +} + +static unsigned int +smb2_get_credit_size(char *buf) +{ + return le16_to_cpu(((struct smb2_hdr *)buf)->CreditCharge); +} + static __u64 smb2_get_next_mid(struct TCP_Server_Info *server) { @@ -181,11 +222,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); - /* - * limit write size to 2 ** 16, because we don't support multicredit - * requests now. - */ - wsize = min_t(unsigned int, wsize, 2 << 15); return wsize; } @@ -586,6 +622,8 @@ 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_credit_size = smb2_get_credit_size, .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 8466031..6fc41b8 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -103,6 +103,7 @@ smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ , hdr->StructureSize = cpu_to_le16(64); hdr->Command = smb2_cmd; hdr->CreditRequest = cpu_to_le16(2); /* BB make this dynamic */ + hdr->CreditCharge = cpu_to_le16(1); hdr->ProcessId = cpu_to_le32((__u16)current->tgid); if (!tcon) @@ -1595,7 +1596,7 @@ smb2_writev_callback(struct mid_q_entry *mid) int smb2_async_writev(struct cifs_writedata *wdata) { - int rc = -EACCES; + int rc = -EACCES, flags = 0; struct smb2_write_req *req = NULL; struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); struct kvec iov; @@ -1635,9 +1636,18 @@ 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, 2 << 15)); + add_credits(tcon->ses->server, + wdata->credits - le16_to_cpu(req->hdr.CreditCharge), 0); + wake_up(&tcon->ses->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); + smb2_writev_callback, wdata, flags); if (rc) kref_put(&wdata->refcount, cifs_writedata_release); diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index 2a5fdf2..6e6b51a 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c @@ -188,7 +188,11 @@ 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) { + int i, num = le16_to_cpu(hdr->CreditCharge); hdr->MessageId = get_next_mid(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 0844b03..ca7a427 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -413,6 +413,19 @@ wait_for_free_request(struct TCP_Server_Info *server, const int timeout, server->ops->get_credits_field(server, optype)); } +int +cifs_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, + unsigned int *num, unsigned int *credits) +{ + int rc; + rc = wait_for_free_request(server, 0, 0); + if (rc) + return rc; + *num = size; + *credits = 1; + return rc; +} + static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf, struct mid_q_entry **ppmidQ) { @@ -489,19 +502,25 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst, { int rc, timeout, optype; struct mid_q_entry *mid; + unsigned int credits; 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; + } else + credits = server->ops->get_credit_size( + rqst->rq_iov[0].iov_base); 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); + add_credits(server, credits, optype); wake_up(&server->request_q); return PTR_ERR(mid); } @@ -527,7 +546,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst, return 0; cifs_delete_mid(mid); - add_credits(server, 1, optype); + add_credits(server, credits, optype); wake_up(&server->request_q); return rc; } -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe linux-cifs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html