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 <pshilov@microsoft.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
Pavel Shilovsky 2019-01-16 11:12:41 -08:00 committed by Steve French
parent 5b96485260
commit 335b7b62ff
8 changed files with 118 additions and 63 deletions

View File

@ -216,6 +216,7 @@ struct cifs_io_parms;
struct cifs_search_info; struct cifs_search_info;
struct cifsInodeInfo; struct cifsInodeInfo;
struct cifs_open_parms; struct cifs_open_parms;
struct cifs_credits;
struct smb_version_operations { struct smb_version_operations {
int (*send_cancel)(struct TCP_Server_Info *, struct smb_rqst *, int (*send_cancel)(struct TCP_Server_Info *, struct smb_rqst *,
@ -230,8 +231,9 @@ struct smb_version_operations {
/* check response: verify signature, map error */ /* check response: verify signature, map error */
int (*check_receive)(struct mid_q_entry *, struct TCP_Server_Info *, int (*check_receive)(struct mid_q_entry *, struct TCP_Server_Info *,
bool); bool);
void (*add_credits)(struct TCP_Server_Info *, const unsigned int, void (*add_credits)(struct TCP_Server_Info *server,
const int); const struct cifs_credits *credits,
const int optype);
void (*set_credits)(struct TCP_Server_Info *, const int); void (*set_credits)(struct TCP_Server_Info *, const int);
int * (*get_credits_field)(struct TCP_Server_Info *, const int); int * (*get_credits_field)(struct TCP_Server_Info *, const int);
unsigned int (*get_credits)(struct mid_q_entry *); unsigned int (*get_credits)(struct mid_q_entry *);
@ -454,7 +456,7 @@ struct smb_version_operations {
unsigned int (*wp_retry_size)(struct inode *); unsigned int (*wp_retry_size)(struct inode *);
/* get mtu credits */ /* get mtu credits */
int (*wait_mtu_credits)(struct TCP_Server_Info *, unsigned int, 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 */ /* check if we need to issue closedir */
bool (*dir_needs_close)(struct cifsFileInfo *); bool (*dir_needs_close)(struct cifsFileInfo *);
long (*fallocate)(struct file *, struct cifs_tcon *, int, loff_t, long (*fallocate)(struct file *, struct cifs_tcon *, int, loff_t,
@ -713,6 +715,11 @@ struct TCP_Server_Info {
int nr_targets; int nr_targets;
}; };
struct cifs_credits {
unsigned int value;
unsigned int instance;
};
static inline unsigned int static inline unsigned int
in_flight(struct TCP_Server_Info *server) 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, add_credits(struct TCP_Server_Info *server, const unsigned int add,
const int optype) 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 static inline void
add_credits_and_wake_if(struct TCP_Server_Info *server, const unsigned int add, add_credits_and_wake_if(struct TCP_Server_Info *server,
const int optype) const struct cifs_credits *credits, const int optype)
{ {
if (add) { if (credits->value) {
server->ops->add_credits(server, add, optype); server->ops->add_credits(server, credits, optype);
wake_up(&server->request_q); wake_up(&server->request_q);
} }
} }
@ -1253,7 +1262,7 @@ struct cifs_readdata {
unsigned int pagesz; unsigned int pagesz;
unsigned int page_offset; unsigned int page_offset;
unsigned int tailsz; unsigned int tailsz;
unsigned int credits; struct cifs_credits credits;
unsigned int nr_pages; unsigned int nr_pages;
struct page **pages; struct page **pages;
}; };
@ -1279,7 +1288,7 @@ struct cifs_writedata {
unsigned int pagesz; unsigned int pagesz;
unsigned int page_offset; unsigned int page_offset;
unsigned int tailsz; unsigned int tailsz;
unsigned int credits; struct cifs_credits credits;
unsigned int nr_pages; unsigned int nr_pages;
struct page **pages; struct page **pages;
}; };

View File

@ -115,7 +115,7 @@ extern int cifs_check_receive(struct mid_q_entry *mid,
struct TCP_Server_Info *server, bool log_error); struct TCP_Server_Info *server, bool log_error);
extern int cifs_wait_mtu_credits(struct TCP_Server_Info *server, extern int cifs_wait_mtu_credits(struct TCP_Server_Info *server,
unsigned int size, unsigned int *num, unsigned int size, unsigned int *num,
unsigned int *credits); struct cifs_credits *credits);
extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *, extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *,
struct kvec *, int /* nvec to send */, struct kvec *, int /* nvec to send */,
int * /* type of buf returned */, const int flags, int * /* type of buf returned */, const int flags,

View File

@ -593,6 +593,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
msleep(3000); msleep(3000);
} else { } else {
atomic_inc(&tcpSesReconnectCount); atomic_inc(&tcpSesReconnectCount);
set_credits(server, 1);
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
if (server->tcpStatus != CifsExiting) if (server->tcpStatus != CifsExiting)
server->tcpStatus = CifsNeedNegotiate; server->tcpStatus = CifsNeedNegotiate;
@ -4951,8 +4952,6 @@ cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses)
if (!server->ops->need_neg(server)) if (!server->ops->need_neg(server))
return 0; return 0;
set_credits(server, 1);
rc = server->ops->negotiate(xid, ses); rc = server->ops->negotiate(xid, ses);
if (rc == 0) { if (rc == 0) {
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);

View File

@ -2143,11 +2143,13 @@ static int cifs_writepages(struct address_space *mapping,
server = cifs_sb_master_tcon(cifs_sb)->ses->server; server = cifs_sb_master_tcon(cifs_sb)->ses->server;
retry: retry:
while (!done && index <= end) { 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; 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, rc = server->ops->wait_mtu_credits(server, cifs_sb->wsize,
&wsize, &credits); &wsize, credits);
if (rc != 0) { if (rc != 0) {
done = true; done = true;
break; break;
@ -2180,13 +2182,13 @@ static int cifs_writepages(struct address_space *mapping,
continue; continue;
} }
wdata->credits = credits; wdata->credits = credits_on_stack;
rc = wdata_send_pages(wdata, nr_pages, mapping, wbc); rc = wdata_send_pages(wdata, nr_pages, mapping, wbc);
/* send failure -- clean up the mess */ /* send failure -- clean up the mess */
if (rc != 0) { 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) { for (i = 0; i < nr_pages; ++i) {
if (is_retryable_error(rc)) if (is_retryable_error(rc))
redirty_page_for_writepage(wbc, redirty_page_for_writepage(wbc,
@ -2567,7 +2569,8 @@ static int
cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list, cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list,
struct cifs_aio_ctx *ctx) struct cifs_aio_ctx *ctx)
{ {
unsigned int wsize, credits; unsigned int wsize;
struct cifs_credits credits;
int rc; int rc;
struct TCP_Server_Info *server = struct TCP_Server_Info *server =
tlink_tcon(wdata->cfile->tlink)->ses->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 * Note: we are attempting to resend the whole wdata not in segments
*/ */
do { do {
rc = server->ops->wait_mtu_credits( rc = server->ops->wait_mtu_credits(server, wdata->bytes, &wsize,
server, wdata->bytes, &wsize, &credits); &credits);
if (rc) if (rc)
goto out; goto out;
if (wsize < wdata->bytes) { if (wsize < wdata->bytes) {
add_credits_and_wake_if(server, credits, 0); add_credits_and_wake_if(server, &credits, 0);
msleep(1000); msleep(1000);
} }
} while (wsize < wdata->bytes); } while (wsize < wdata->bytes);
wdata->credits = credits;
rc = -EAGAIN; rc = -EAGAIN;
while (rc == -EAGAIN) { while (rc == -EAGAIN) {
rc = 0; rc = 0;
@ -2604,7 +2608,7 @@ cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list,
return 0; return 0;
} }
add_credits_and_wake_if(server, wdata->credits, 0); add_credits_and_wake_if(server, &wdata->credits, 0);
out: out:
kref_put(&wdata->refcount, cifs_uncached_writedata_release); 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 TCP_Server_Info *server;
struct page **pagevec; struct page **pagevec;
size_t start; size_t start;
unsigned int xid;
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
pid = open_file->pid; 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; pid = current->tgid;
server = tlink_tcon(open_file->tlink)->ses->server; server = tlink_tcon(open_file->tlink)->ses->server;
xid = get_xid();
do { 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, rc = server->ops->wait_mtu_credits(server, cifs_sb->wsize,
&wsize, &credits); &wsize, credits);
if (rc) if (rc)
break; break;
@ -2731,7 +2739,7 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
wdata->pid = pid; wdata->pid = pid;
wdata->bytes = cur_len; wdata->bytes = cur_len;
wdata->pagesz = PAGE_SIZE; wdata->pagesz = PAGE_SIZE;
wdata->credits = credits; wdata->credits = credits_on_stack;
wdata->ctx = ctx; wdata->ctx = ctx;
kref_get(&ctx->refcount); 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, rc = server->ops->async_writev(wdata,
cifs_uncached_writedata_release); cifs_uncached_writedata_release);
if (rc) { if (rc) {
add_credits_and_wake_if(server, wdata->credits, 0); add_credits_and_wake_if(server, &wdata->credits, 0);
kref_put(&wdata->refcount, kref_put(&wdata->refcount,
cifs_uncached_writedata_release); cifs_uncached_writedata_release);
if (rc == -EAGAIN) { if (rc == -EAGAIN) {
@ -2756,6 +2764,7 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
len -= cur_len; len -= cur_len;
} while (len > 0); } while (len > 0);
free_xid(xid);
return rc; return rc;
} }
@ -3260,7 +3269,8 @@ static int cifs_resend_rdata(struct cifs_readdata *rdata,
struct list_head *rdata_list, struct list_head *rdata_list,
struct cifs_aio_ctx *ctx) struct cifs_aio_ctx *ctx)
{ {
unsigned int rsize, credits; unsigned int rsize;
struct cifs_credits credits;
int rc; int rc;
struct TCP_Server_Info *server = struct TCP_Server_Info *server =
tlink_tcon(rdata->cfile->tlink)->ses->server; tlink_tcon(rdata->cfile->tlink)->ses->server;
@ -3277,11 +3287,12 @@ static int cifs_resend_rdata(struct cifs_readdata *rdata,
goto out; goto out;
if (rsize < rdata->bytes) { if (rsize < rdata->bytes) {
add_credits_and_wake_if(server, credits, 0); add_credits_and_wake_if(server, &credits, 0);
msleep(1000); msleep(1000);
} }
} while (rsize < rdata->bytes); } while (rsize < rdata->bytes);
rdata->credits = credits;
rc = -EAGAIN; rc = -EAGAIN;
while (rc == -EAGAIN) { while (rc == -EAGAIN) {
rc = 0; rc = 0;
@ -3297,7 +3308,7 @@ static int cifs_resend_rdata(struct cifs_readdata *rdata,
return 0; return 0;
} }
add_credits_and_wake_if(server, rdata->credits, 0); add_credits_and_wake_if(server, &rdata->credits, 0);
out: out:
kref_put(&rdata->refcount, kref_put(&rdata->refcount,
cifs_uncached_readdata_release); 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_aio_ctx *ctx)
{ {
struct cifs_readdata *rdata; 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; size_t cur_len;
int rc; int rc;
pid_t pid; pid_t pid;
@ -3332,7 +3345,7 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
do { do {
rc = server->ops->wait_mtu_credits(server, cifs_sb->rsize, rc = server->ops->wait_mtu_credits(server, cifs_sb->rsize,
&rsize, &credits); &rsize, credits);
if (rc) if (rc)
break; break;
@ -3406,7 +3419,7 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
rdata->pagesz = PAGE_SIZE; rdata->pagesz = PAGE_SIZE;
rdata->read_into_pages = cifs_uncached_read_into_pages; rdata->read_into_pages = cifs_uncached_read_into_pages;
rdata->copy_into_pages = cifs_uncached_copy_into_pages; rdata->copy_into_pages = cifs_uncached_copy_into_pages;
rdata->credits = credits; rdata->credits = credits_on_stack;
rdata->ctx = ctx; rdata->ctx = ctx;
kref_get(&ctx->refcount); 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 = cifs_reopen_file(rdata->cfile, true)))
rc = server->ops->async_readv(rdata); rc = server->ops->async_readv(rdata);
if (rc) { if (rc) {
add_credits_and_wake_if(server, rdata->credits, 0); add_credits_and_wake_if(server, &rdata->credits, 0);
kref_put(&rdata->refcount, kref_put(&rdata->refcount,
cifs_uncached_readdata_release); cifs_uncached_readdata_release);
if (rc == -EAGAIN) { if (rc == -EAGAIN) {
@ -4093,10 +4106,11 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
loff_t offset; loff_t offset;
struct page *page, *tpage; struct page *page, *tpage;
struct cifs_readdata *rdata; 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, rc = server->ops->wait_mtu_credits(server, cifs_sb->rsize,
&rsize, &credits); &rsize, credits);
if (rc) if (rc)
break; break;
@ -4142,7 +4156,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
rdata->tailsz = PAGE_SIZE; rdata->tailsz = PAGE_SIZE;
rdata->read_into_pages = cifs_readpages_read_into_pages; rdata->read_into_pages = cifs_readpages_read_into_pages;
rdata->copy_into_pages = cifs_readpages_copy_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_for_each_entry_safe(page, tpage, &tmplist, lru) {
list_del(&page->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 = cifs_reopen_file(rdata->cfile, true)))
rc = server->ops->async_readv(rdata); rc = server->ops->async_readv(rdata);
if (rc) { 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++) { for (i = 0; i < rdata->nr_pages; i++) {
page = rdata->pages[i]; page = rdata->pages[i];
lru_cache_add_file(page); lru_cache_add_file(page);

View File

@ -117,11 +117,11 @@ cifs_find_mid(struct TCP_Server_Info *server, char *buffer)
} }
static void static void
cifs_add_credits(struct TCP_Server_Info *server, const unsigned int add, cifs_add_credits(struct TCP_Server_Info *server,
const int optype) const struct cifs_credits *credits, const int optype)
{ {
spin_lock(&server->req_lock); spin_lock(&server->req_lock);
server->credits += add; server->credits += credits->value;
server->in_flight--; server->in_flight--;
spin_unlock(&server->req_lock); spin_unlock(&server->req_lock);
wake_up(&server->request_q); wake_up(&server->request_q);

View File

@ -67,10 +67,13 @@ change_conf(struct TCP_Server_Info *server)
} }
static void static void
smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add, smb2_add_credits(struct TCP_Server_Info *server,
const int optype) const struct cifs_credits *credits, const int optype)
{ {
int *val, rc = -1; int *val, rc = -1;
unsigned int add = credits->value;
unsigned int instance = credits->instance;
bool reconnect_detected = false;
spin_lock(&server->req_lock); spin_lock(&server->req_lock);
val = server->ops->get_credits_field(server, optype); 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)) if (((optype & CIFS_OP_MASK) == CIFS_NEG_OP) && (*val != 0))
trace_smb3_reconnect_with_invalid_credits(server->CurrentMid, trace_smb3_reconnect_with_invalid_credits(server->CurrentMid,
server->hostname, *val); server->hostname, *val);
if ((instance == 0) || (instance == server->reconnect_instance))
*val += add;
else
reconnect_detected = true;
*val += add;
if (*val > 65000) { if (*val > 65000) {
*val = 65000; /* Don't get near 64K credits, avoid srv bugs */ *val = 65000; /* Don't get near 64K credits, avoid srv bugs */
printk_once(KERN_WARNING "server overflowed SMB3 credits\n"); 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); spin_unlock(&server->req_lock);
wake_up(&server->request_q); 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 if (server->tcpStatus == CifsNeedReconnect
|| server->tcpStatus == CifsExiting) || server->tcpStatus == CifsExiting)
return; return;
@ -164,7 +174,7 @@ smb2_get_credits(struct mid_q_entry *mid)
static int static int
smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, 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; int rc = 0;
unsigned int scredits; unsigned int scredits;
@ -190,7 +200,8 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
/* can deadlock with reopen */ /* can deadlock with reopen */
if (scredits <= 8) { if (scredits <= 8) {
*num = SMB2_MAX_BUFFER_SIZE; *num = SMB2_MAX_BUFFER_SIZE;
*credits = 0; credits->value = 0;
credits->instance = 0;
break; break;
} }
@ -199,8 +210,10 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
*num = min_t(unsigned int, size, *num = min_t(unsigned int, size,
scredits * SMB2_MAX_BUFFER_SIZE); scredits * SMB2_MAX_BUFFER_SIZE);
*credits = DIV_ROUND_UP(*num, SMB2_MAX_BUFFER_SIZE); credits->value =
server->credits -= *credits; DIV_ROUND_UP(*num, SMB2_MAX_BUFFER_SIZE);
credits->instance = server->reconnect_instance;
server->credits -= credits->value;
server->in_flight++; server->in_flight++;
break; break;
} }

View File

@ -3294,9 +3294,9 @@ smb2_async_readv(struct cifs_readdata *rdata)
rc = smb2_new_read_req( rc = smb2_new_read_req(
(void **) &buf, &total_len, &io_parms, rdata, 0, 0); (void **) &buf, &total_len, &io_parms, rdata, 0, 0);
if (rc) { if (rc) {
if (rc == -EAGAIN && rdata->credits) { if (rc == -EAGAIN && rdata->credits.value) {
/* credits was reset by reconnect */ /* credits was reset by reconnect */
rdata->credits = 0; rdata->credits.value = 0;
/* reduce in_flight value since we won't send the req */ /* reduce in_flight value since we won't send the req */
spin_lock(&server->req_lock); spin_lock(&server->req_lock);
server->in_flight--; server->in_flight--;
@ -3313,17 +3313,26 @@ smb2_async_readv(struct cifs_readdata *rdata)
shdr = (struct smb2_sync_hdr *)buf; shdr = (struct smb2_sync_hdr *)buf;
if (rdata->credits) { if (rdata->credits.value > 0) {
shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes, shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes,
SMB2_MAX_BUFFER_SIZE)); SMB2_MAX_BUFFER_SIZE));
shdr->CreditRequest = shdr->CreditRequest =
cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 1); cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 1);
spin_lock(&server->req_lock); 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); 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); spin_unlock(&server->req_lock);
wake_up(&server->request_q); wake_up(&server->request_q);
rdata->credits = le16_to_cpu(shdr->CreditCharge); rdata->credits.value = le16_to_cpu(shdr->CreditCharge);
flags |= CIFS_HAS_CREDITS; flags |= CIFS_HAS_CREDITS;
} }
@ -3340,6 +3349,7 @@ smb2_async_readv(struct cifs_readdata *rdata)
io_parms.offset, io_parms.length, rc); io_parms.offset, io_parms.length, rc);
} }
async_readv_out:
cifs_small_buf_release(buf); cifs_small_buf_release(buf);
return rc; 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); rc = smb2_plain_req_init(SMB2_WRITE, tcon, (void **) &req, &total_len);
if (rc) { if (rc) {
if (rc == -EAGAIN && wdata->credits) { if (rc == -EAGAIN && wdata->credits.value) {
/* credits was reset by reconnect */ /* credits was reset by reconnect */
wdata->credits = 0; wdata->credits.value = 0;
/* reduce in_flight value since we won't send the req */ /* reduce in_flight value since we won't send the req */
spin_lock(&server->req_lock); spin_lock(&server->req_lock);
server->in_flight--; server->in_flight--;
@ -3603,17 +3613,26 @@ smb2_async_writev(struct cifs_writedata *wdata,
req->Length = cpu_to_le32(wdata->bytes); req->Length = cpu_to_le32(wdata->bytes);
#endif #endif
if (wdata->credits) { if (wdata->credits.value > 0) {
shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes, shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes,
SMB2_MAX_BUFFER_SIZE)); SMB2_MAX_BUFFER_SIZE));
shdr->CreditRequest = shdr->CreditRequest =
cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 1); cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 1);
spin_lock(&server->req_lock); 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); 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); spin_unlock(&server->req_lock);
wake_up(&server->request_q); wake_up(&server->request_q);
wdata->credits = le16_to_cpu(shdr->CreditCharge); wdata->credits.value = le16_to_cpu(shdr->CreditCharge);
flags |= CIFS_HAS_CREDITS; flags |= CIFS_HAS_CREDITS;
} }

View File

@ -512,10 +512,11 @@ wait_for_free_request(struct TCP_Server_Info *server, const int timeout,
int int
cifs_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, 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; *num = size;
*credits = 0; credits->value = 0;
credits->instance = server->reconnect_instance;
return 0; return 0;
} }
@ -606,7 +607,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
{ {
int rc, timeout, optype; int rc, timeout, optype;
struct mid_q_entry *mid; struct mid_q_entry *mid;
unsigned int credits = 0; struct cifs_credits credits = { .value = 0, .instance = 0 };
timeout = flags & CIFS_TIMEOUT_MASK; timeout = flags & CIFS_TIMEOUT_MASK;
optype = flags & CIFS_OP_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); rc = wait_for_free_request(server, timeout, optype);
if (rc) if (rc)
return rc; return rc;
credits = 1; credits.value = 1;
} }
mutex_lock(&server->srv_mutex); mutex_lock(&server->srv_mutex);
mid = server->ops->setup_async_request(server, rqst); mid = server->ops->setup_async_request(server, rqst);
if (IS_ERR(mid)) { if (IS_ERR(mid)) {
mutex_unlock(&server->srv_mutex); mutex_unlock(&server->srv_mutex);
add_credits_and_wake_if(server, credits, optype); add_credits_and_wake_if(server, &credits, optype);
return PTR_ERR(mid); return PTR_ERR(mid);
} }
@ -657,7 +658,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
if (rc == 0) if (rc == 0)
return 0; return 0;
add_credits_and_wake_if(server, credits, optype); add_credits_and_wake_if(server, &credits, optype);
return rc; return rc;
} }