CIFS: Fix an issue with re-sending wdata when transport returning -EAGAIN
When sending a wdata, transport may return -EAGAIN. In this case we should re-obtain credits because the session may have been reconnected. Change in v2: adjust_credits before re-sending Signed-off-by: Long Li <longli@microsoft.com> Signed-off-by: Steve French <stfrench@microsoft.com> Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com>
This commit is contained in:
@@ -2632,43 +2632,56 @@ cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list,
|
|||||||
struct TCP_Server_Info *server =
|
struct TCP_Server_Info *server =
|
||||||
tlink_tcon(wdata->cfile->tlink)->ses->server;
|
tlink_tcon(wdata->cfile->tlink)->ses->server;
|
||||||
|
|
||||||
/*
|
|
||||||
* Wait for credits to resend this wdata.
|
|
||||||
* Note: we are attempting to resend the whole wdata not in segments
|
|
||||||
*/
|
|
||||||
do {
|
do {
|
||||||
rc = server->ops->wait_mtu_credits(server, wdata->bytes, &wsize,
|
if (wdata->cfile->invalidHandle) {
|
||||||
&credits);
|
|
||||||
|
|
||||||
if (rc)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (wsize < wdata->bytes) {
|
|
||||||
add_credits_and_wake_if(server, &credits, 0);
|
|
||||||
msleep(1000);
|
|
||||||
}
|
|
||||||
} while (wsize < wdata->bytes);
|
|
||||||
|
|
||||||
wdata->credits = credits;
|
|
||||||
rc = -EAGAIN;
|
|
||||||
while (rc == -EAGAIN) {
|
|
||||||
rc = 0;
|
|
||||||
if (wdata->cfile->invalidHandle)
|
|
||||||
rc = cifs_reopen_file(wdata->cfile, false);
|
rc = cifs_reopen_file(wdata->cfile, false);
|
||||||
if (!rc)
|
if (rc == -EAGAIN)
|
||||||
rc = server->ops->async_writev(wdata,
|
continue;
|
||||||
|
else if (rc)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wait for credits to resend this wdata.
|
||||||
|
* Note: we are attempting to resend the whole wdata not in
|
||||||
|
* segments
|
||||||
|
*/
|
||||||
|
do {
|
||||||
|
rc = server->ops->wait_mtu_credits(server, wdata->bytes,
|
||||||
|
&wsize, &credits);
|
||||||
|
if (rc)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (wsize < wdata->bytes) {
|
||||||
|
add_credits_and_wake_if(server, &credits, 0);
|
||||||
|
msleep(1000);
|
||||||
|
}
|
||||||
|
} while (wsize < wdata->bytes);
|
||||||
|
wdata->credits = credits;
|
||||||
|
|
||||||
|
rc = adjust_credits(server, &wdata->credits, wdata->bytes);
|
||||||
|
|
||||||
|
if (!rc) {
|
||||||
|
if (wdata->cfile->invalidHandle)
|
||||||
|
rc = -EAGAIN;
|
||||||
|
else
|
||||||
|
rc = server->ops->async_writev(wdata,
|
||||||
cifs_uncached_writedata_release);
|
cifs_uncached_writedata_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rc) {
|
/* If the write was successfully sent, we are done */
|
||||||
list_add_tail(&wdata->list, wdata_list);
|
if (!rc) {
|
||||||
return 0;
|
list_add_tail(&wdata->list, wdata_list);
|
||||||
}
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
add_credits_and_wake_if(server, &wdata->credits, 0);
|
/* Roll back credits and retry if needed */
|
||||||
out:
|
add_credits_and_wake_if(server, &wdata->credits, 0);
|
||||||
|
} while (rc == -EAGAIN);
|
||||||
|
|
||||||
|
fail:
|
||||||
kref_put(&wdata->refcount, cifs_uncached_writedata_release);
|
kref_put(&wdata->refcount, cifs_uncached_writedata_release);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2896,12 +2909,12 @@ restart_loop:
|
|||||||
wdata->bytes, &tmp_from,
|
wdata->bytes, &tmp_from,
|
||||||
ctx->cfile, cifs_sb, &tmp_list,
|
ctx->cfile, cifs_sb, &tmp_list,
|
||||||
ctx);
|
ctx);
|
||||||
|
|
||||||
|
kref_put(&wdata->refcount,
|
||||||
|
cifs_uncached_writedata_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
list_splice(&tmp_list, &ctx->list);
|
list_splice(&tmp_list, &ctx->list);
|
||||||
|
|
||||||
kref_put(&wdata->refcount,
|
|
||||||
cifs_uncached_writedata_release);
|
|
||||||
goto restart_loop;
|
goto restart_loop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user