CIFS: Fix wsize usage in iovec write
If a server change maximum buffer size for write (wsize) requests on reconnect we can fail on repeating with a big size buffer on -EAGAIN error in iovec write. Fix this by checking wsize all the time before repeating request in iovec write. Reviewed-by: Shirish Pargaonkar <spargaonkar@suse.com> Signed-off-by: Pavel Shilovsky <pshilovsky@samba.org> Signed-off-by: Steve French <smfrench@gmail.com>
This commit is contained in:
committed by
Steve French
parent
43de94eadf
commit
6ec0b01b26
@@ -196,10 +196,6 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
|
|||||||
if (rc)
|
if (rc)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/*
|
|
||||||
* FIXME: check if wsize needs updated due to negotiated smb buffer
|
|
||||||
* size shrinking
|
|
||||||
*/
|
|
||||||
atomic_inc(&tconInfoReconnectCount);
|
atomic_inc(&tconInfoReconnectCount);
|
||||||
|
|
||||||
/* tell server Unix caps we support */
|
/* tell server Unix caps we support */
|
||||||
|
|||||||
@@ -2401,28 +2401,6 @@ cifs_uncached_writev_complete(struct work_struct *work)
|
|||||||
kref_put(&wdata->refcount, cifs_uncached_writedata_release);
|
kref_put(&wdata->refcount, cifs_uncached_writedata_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* attempt to send write to server, retry on any -EAGAIN errors */
|
|
||||||
static int
|
|
||||||
cifs_uncached_retry_writev(struct cifs_writedata *wdata)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
struct TCP_Server_Info *server;
|
|
||||||
|
|
||||||
server = tlink_tcon(wdata->cfile->tlink)->ses->server;
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (wdata->cfile->invalidHandle) {
|
|
||||||
rc = cifs_reopen_file(wdata->cfile, false);
|
|
||||||
if (rc != 0)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
rc = server->ops->async_writev(wdata,
|
|
||||||
cifs_uncached_writedata_release);
|
|
||||||
} while (rc == -EAGAIN);
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
wdata_fill_from_iovec(struct cifs_writedata *wdata, struct iov_iter *from,
|
wdata_fill_from_iovec(struct cifs_writedata *wdata, struct iov_iter *from,
|
||||||
size_t *len, unsigned long *num_pages)
|
size_t *len, unsigned long *num_pages)
|
||||||
@@ -2474,13 +2452,19 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
|
|||||||
size_t cur_len;
|
size_t cur_len;
|
||||||
unsigned long nr_pages, num_pages, i;
|
unsigned long nr_pages, num_pages, i;
|
||||||
struct cifs_writedata *wdata;
|
struct cifs_writedata *wdata;
|
||||||
|
struct iov_iter saved_from;
|
||||||
|
loff_t saved_offset = offset;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
struct TCP_Server_Info *server;
|
||||||
|
|
||||||
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;
|
||||||
else
|
else
|
||||||
pid = current->tgid;
|
pid = current->tgid;
|
||||||
|
|
||||||
|
server = tlink_tcon(open_file->tlink)->ses->server;
|
||||||
|
memcpy(&saved_from, from, sizeof(struct iov_iter));
|
||||||
|
|
||||||
do {
|
do {
|
||||||
nr_pages = get_numpages(cifs_sb->wsize, len, &cur_len);
|
nr_pages = get_numpages(cifs_sb->wsize, len, &cur_len);
|
||||||
wdata = cifs_writedata_alloc(nr_pages,
|
wdata = cifs_writedata_alloc(nr_pages,
|
||||||
@@ -2520,10 +2504,20 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
|
|||||||
wdata->bytes = cur_len;
|
wdata->bytes = cur_len;
|
||||||
wdata->pagesz = PAGE_SIZE;
|
wdata->pagesz = PAGE_SIZE;
|
||||||
wdata->tailsz = cur_len - ((nr_pages - 1) * PAGE_SIZE);
|
wdata->tailsz = cur_len - ((nr_pages - 1) * PAGE_SIZE);
|
||||||
rc = cifs_uncached_retry_writev(wdata);
|
|
||||||
|
if (!wdata->cfile->invalidHandle ||
|
||||||
|
!cifs_reopen_file(wdata->cfile, false))
|
||||||
|
rc = server->ops->async_writev(wdata,
|
||||||
|
cifs_uncached_writedata_release);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
kref_put(&wdata->refcount,
|
kref_put(&wdata->refcount,
|
||||||
cifs_uncached_writedata_release);
|
cifs_uncached_writedata_release);
|
||||||
|
if (rc == -EAGAIN) {
|
||||||
|
memcpy(from, &saved_from,
|
||||||
|
sizeof(struct iov_iter));
|
||||||
|
iov_iter_advance(from, offset - saved_offset);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2545,6 +2539,7 @@ cifs_iovec_write(struct file *file, struct iov_iter *from, loff_t *poffset)
|
|||||||
struct cifs_sb_info *cifs_sb;
|
struct cifs_sb_info *cifs_sb;
|
||||||
struct cifs_writedata *wdata, *tmp;
|
struct cifs_writedata *wdata, *tmp;
|
||||||
struct list_head wdata_list;
|
struct list_head wdata_list;
|
||||||
|
struct iov_iter saved_from;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
len = iov_iter_count(from);
|
len = iov_iter_count(from);
|
||||||
@@ -2565,6 +2560,8 @@ cifs_iovec_write(struct file *file, struct iov_iter *from, loff_t *poffset)
|
|||||||
if (!tcon->ses->server->ops->async_writev)
|
if (!tcon->ses->server->ops->async_writev)
|
||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
|
|
||||||
|
memcpy(&saved_from, from, sizeof(struct iov_iter));
|
||||||
|
|
||||||
rc = cifs_write_from_iter(*poffset, len, from, open_file, cifs_sb,
|
rc = cifs_write_from_iter(*poffset, len, from, open_file, cifs_sb,
|
||||||
&wdata_list);
|
&wdata_list);
|
||||||
|
|
||||||
@@ -2596,7 +2593,25 @@ restart_loop:
|
|||||||
|
|
||||||
/* resend call if it's a retryable error */
|
/* resend call if it's a retryable error */
|
||||||
if (rc == -EAGAIN) {
|
if (rc == -EAGAIN) {
|
||||||
rc = cifs_uncached_retry_writev(wdata);
|
struct list_head tmp_list;
|
||||||
|
struct iov_iter tmp_from;
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&tmp_list);
|
||||||
|
list_del_init(&wdata->list);
|
||||||
|
|
||||||
|
memcpy(&tmp_from, &saved_from,
|
||||||
|
sizeof(struct iov_iter));
|
||||||
|
iov_iter_advance(&tmp_from,
|
||||||
|
wdata->offset - *poffset);
|
||||||
|
|
||||||
|
rc = cifs_write_from_iter(wdata->offset,
|
||||||
|
wdata->bytes, &tmp_from,
|
||||||
|
open_file, cifs_sb, &tmp_list);
|
||||||
|
|
||||||
|
list_splice(&tmp_list, &wdata_list);
|
||||||
|
|
||||||
|
kref_put(&wdata->refcount,
|
||||||
|
cifs_uncached_writedata_release);
|
||||||
goto restart_loop;
|
goto restart_loop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -245,10 +245,6 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
|
|||||||
if (rc)
|
if (rc)
|
||||||
goto out;
|
goto out;
|
||||||
atomic_inc(&tconInfoReconnectCount);
|
atomic_inc(&tconInfoReconnectCount);
|
||||||
/*
|
|
||||||
* BB FIXME add code to check if wsize needs update due to negotiated
|
|
||||||
* smb buffer size shrinking.
|
|
||||||
*/
|
|
||||||
out:
|
out:
|
||||||
/*
|
/*
|
||||||
* Check if handle based operation so we know whether we can continue
|
* Check if handle based operation so we know whether we can continue
|
||||||
|
|||||||
Reference in New Issue
Block a user