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 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;
};

View File

@ -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,

View File

@ -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);

View File

@ -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 @@ static int cifs_writepages(struct address_space *mapping,
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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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, rqst);
if (IS_ERR(mid)) {
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);
}
@ -657,7 +658,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
if (rc == 0)
return 0;
add_credits_and_wake_if(server, credits, optype);
add_credits_and_wake_if(server, &credits, optype);
return rc;
}