@@ -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
@@ -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);
@@ -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);
@@ -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;
}
@@ -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,
@@ -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,
@@ -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);
@@ -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 *
@@ -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;
}
Signed-off-by: Pavel Shilovsky <pshilovsky@samba.org> --- 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(-)