|
@@ -1670,8 +1670,8 @@ cifs_write(struct cifsFileInfo *open_file, __u32 pid, const char *write_data,
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
- len = min((size_t)cifs_sb->wsize,
|
|
|
|
- write_size - total_written);
|
|
|
|
|
|
+ len = min(server->ops->wp_retry_size(dentry->d_inode),
|
|
|
|
+ (unsigned int)write_size - total_written);
|
|
/* iov[0] is reserved for smb header */
|
|
/* iov[0] is reserved for smb header */
|
|
iov[1].iov_base = (char *)write_data + total_written;
|
|
iov[1].iov_base = (char *)write_data + total_written;
|
|
iov[1].iov_len = len;
|
|
iov[1].iov_len = len;
|
|
@@ -1878,15 +1878,163 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
|
|
return rc;
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static struct cifs_writedata *
|
|
|
|
+wdata_alloc_and_fillpages(pgoff_t tofind, struct address_space *mapping,
|
|
|
|
+ pgoff_t end, pgoff_t *index,
|
|
|
|
+ unsigned int *found_pages)
|
|
|
|
+{
|
|
|
|
+ unsigned int nr_pages;
|
|
|
|
+ struct page **pages;
|
|
|
|
+ struct cifs_writedata *wdata;
|
|
|
|
+
|
|
|
|
+ wdata = cifs_writedata_alloc((unsigned int)tofind,
|
|
|
|
+ cifs_writev_complete);
|
|
|
|
+ if (!wdata)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * find_get_pages_tag seems to return a max of 256 on each
|
|
|
|
+ * iteration, so we must call it several times in order to
|
|
|
|
+ * fill the array or the wsize is effectively limited to
|
|
|
|
+ * 256 * PAGE_CACHE_SIZE.
|
|
|
|
+ */
|
|
|
|
+ *found_pages = 0;
|
|
|
|
+ pages = wdata->pages;
|
|
|
|
+ do {
|
|
|
|
+ nr_pages = find_get_pages_tag(mapping, index,
|
|
|
|
+ PAGECACHE_TAG_DIRTY, tofind,
|
|
|
|
+ pages);
|
|
|
|
+ *found_pages += nr_pages;
|
|
|
|
+ tofind -= nr_pages;
|
|
|
|
+ pages += nr_pages;
|
|
|
|
+ } while (nr_pages && tofind && *index <= end);
|
|
|
|
+
|
|
|
|
+ return wdata;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static unsigned int
|
|
|
|
+wdata_prepare_pages(struct cifs_writedata *wdata, unsigned int found_pages,
|
|
|
|
+ struct address_space *mapping,
|
|
|
|
+ struct writeback_control *wbc,
|
|
|
|
+ pgoff_t end, pgoff_t *index, pgoff_t *next, bool *done)
|
|
|
|
+{
|
|
|
|
+ unsigned int nr_pages = 0, i;
|
|
|
|
+ struct page *page;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < found_pages; i++) {
|
|
|
|
+ page = wdata->pages[i];
|
|
|
|
+ /*
|
|
|
|
+ * At this point we hold neither mapping->tree_lock nor
|
|
|
|
+ * lock on the page itself: the page may be truncated or
|
|
|
|
+ * invalidated (changing page->mapping to NULL), or even
|
|
|
|
+ * swizzled back from swapper_space to tmpfs file
|
|
|
|
+ * mapping
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+ if (nr_pages == 0)
|
|
|
|
+ lock_page(page);
|
|
|
|
+ else if (!trylock_page(page))
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ if (unlikely(page->mapping != mapping)) {
|
|
|
|
+ unlock_page(page);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!wbc->range_cyclic && page->index > end) {
|
|
|
|
+ *done = true;
|
|
|
|
+ unlock_page(page);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (*next && (page->index != *next)) {
|
|
|
|
+ /* Not next consecutive page */
|
|
|
|
+ unlock_page(page);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (wbc->sync_mode != WB_SYNC_NONE)
|
|
|
|
+ wait_on_page_writeback(page);
|
|
|
|
+
|
|
|
|
+ if (PageWriteback(page) ||
|
|
|
|
+ !clear_page_dirty_for_io(page)) {
|
|
|
|
+ unlock_page(page);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * This actually clears the dirty bit in the radix tree.
|
|
|
|
+ * See cifs_writepage() for more commentary.
|
|
|
|
+ */
|
|
|
|
+ set_page_writeback(page);
|
|
|
|
+ if (page_offset(page) >= i_size_read(mapping->host)) {
|
|
|
|
+ *done = true;
|
|
|
|
+ unlock_page(page);
|
|
|
|
+ end_page_writeback(page);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ wdata->pages[i] = page;
|
|
|
|
+ *next = page->index + 1;
|
|
|
|
+ ++nr_pages;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* reset index to refind any pages skipped */
|
|
|
|
+ if (nr_pages == 0)
|
|
|
|
+ *index = wdata->pages[0]->index + 1;
|
|
|
|
+
|
|
|
|
+ /* put any pages we aren't going to use */
|
|
|
|
+ for (i = nr_pages; i < found_pages; i++) {
|
|
|
|
+ page_cache_release(wdata->pages[i]);
|
|
|
|
+ wdata->pages[i] = NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return nr_pages;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int
|
|
|
|
+wdata_send_pages(struct cifs_writedata *wdata, unsigned int nr_pages,
|
|
|
|
+ struct address_space *mapping, struct writeback_control *wbc)
|
|
|
|
+{
|
|
|
|
+ int rc = 0;
|
|
|
|
+ struct TCP_Server_Info *server;
|
|
|
|
+ unsigned int i;
|
|
|
|
+
|
|
|
|
+ wdata->sync_mode = wbc->sync_mode;
|
|
|
|
+ wdata->nr_pages = nr_pages;
|
|
|
|
+ wdata->offset = page_offset(wdata->pages[0]);
|
|
|
|
+ wdata->pagesz = PAGE_CACHE_SIZE;
|
|
|
|
+ wdata->tailsz = min(i_size_read(mapping->host) -
|
|
|
|
+ page_offset(wdata->pages[nr_pages - 1]),
|
|
|
|
+ (loff_t)PAGE_CACHE_SIZE);
|
|
|
|
+ wdata->bytes = ((nr_pages - 1) * PAGE_CACHE_SIZE) + wdata->tailsz;
|
|
|
|
+
|
|
|
|
+ if (wdata->cfile != NULL)
|
|
|
|
+ cifsFileInfo_put(wdata->cfile);
|
|
|
|
+ wdata->cfile = find_writable_file(CIFS_I(mapping->host), false);
|
|
|
|
+ if (!wdata->cfile) {
|
|
|
|
+ cifs_dbg(VFS, "No writable handles for inode\n");
|
|
|
|
+ rc = -EBADF;
|
|
|
|
+ } else {
|
|
|
|
+ wdata->pid = wdata->cfile->pid;
|
|
|
|
+ server = tlink_tcon(wdata->cfile->tlink)->ses->server;
|
|
|
|
+ rc = server->ops->async_writev(wdata, cifs_writedata_release);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < nr_pages; ++i)
|
|
|
|
+ unlock_page(wdata->pages[i]);
|
|
|
|
+
|
|
|
|
+ return rc;
|
|
|
|
+}
|
|
|
|
+
|
|
static int cifs_writepages(struct address_space *mapping,
|
|
static int cifs_writepages(struct address_space *mapping,
|
|
struct writeback_control *wbc)
|
|
struct writeback_control *wbc)
|
|
{
|
|
{
|
|
struct cifs_sb_info *cifs_sb = CIFS_SB(mapping->host->i_sb);
|
|
struct cifs_sb_info *cifs_sb = CIFS_SB(mapping->host->i_sb);
|
|
|
|
+ struct TCP_Server_Info *server;
|
|
bool done = false, scanned = false, range_whole = false;
|
|
bool done = false, scanned = false, range_whole = false;
|
|
pgoff_t end, index;
|
|
pgoff_t end, index;
|
|
struct cifs_writedata *wdata;
|
|
struct cifs_writedata *wdata;
|
|
- struct TCP_Server_Info *server;
|
|
|
|
- struct page *page;
|
|
|
|
int rc = 0;
|
|
int rc = 0;
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1906,152 +2054,50 @@ static int cifs_writepages(struct address_space *mapping,
|
|
range_whole = true;
|
|
range_whole = true;
|
|
scanned = true;
|
|
scanned = true;
|
|
}
|
|
}
|
|
|
|
+ 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;
|
|
|
|
- pgoff_t next = 0, tofind;
|
|
|
|
- struct page **pages;
|
|
|
|
|
|
+ unsigned int i, nr_pages, found_pages, wsize, credits;
|
|
|
|
+ pgoff_t next = 0, tofind, saved_index = index;
|
|
|
|
+
|
|
|
|
+ rc = server->ops->wait_mtu_credits(server, cifs_sb->wsize,
|
|
|
|
+ &wsize, &credits);
|
|
|
|
+ if (rc)
|
|
|
|
+ break;
|
|
|
|
|
|
- tofind = min((cifs_sb->wsize / PAGE_CACHE_SIZE) - 1,
|
|
|
|
- end - index) + 1;
|
|
|
|
|
|
+ tofind = min((wsize / PAGE_CACHE_SIZE) - 1, end - index) + 1;
|
|
|
|
|
|
- wdata = cifs_writedata_alloc((unsigned int)tofind,
|
|
|
|
- cifs_writev_complete);
|
|
|
|
|
|
+ wdata = wdata_alloc_and_fillpages(tofind, mapping, end, &index,
|
|
|
|
+ &found_pages);
|
|
if (!wdata) {
|
|
if (!wdata) {
|
|
rc = -ENOMEM;
|
|
rc = -ENOMEM;
|
|
|
|
+ add_credits_and_wake_if(server, credits, 0);
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
- /*
|
|
|
|
- * find_get_pages_tag seems to return a max of 256 on each
|
|
|
|
- * iteration, so we must call it several times in order to
|
|
|
|
- * fill the array or the wsize is effectively limited to
|
|
|
|
- * 256 * PAGE_CACHE_SIZE.
|
|
|
|
- */
|
|
|
|
- found_pages = 0;
|
|
|
|
- pages = wdata->pages;
|
|
|
|
- do {
|
|
|
|
- nr_pages = find_get_pages_tag(mapping, &index,
|
|
|
|
- PAGECACHE_TAG_DIRTY,
|
|
|
|
- tofind, pages);
|
|
|
|
- found_pages += nr_pages;
|
|
|
|
- tofind -= nr_pages;
|
|
|
|
- pages += nr_pages;
|
|
|
|
- } while (nr_pages && tofind && index <= end);
|
|
|
|
-
|
|
|
|
if (found_pages == 0) {
|
|
if (found_pages == 0) {
|
|
kref_put(&wdata->refcount, cifs_writedata_release);
|
|
kref_put(&wdata->refcount, cifs_writedata_release);
|
|
|
|
+ add_credits_and_wake_if(server, credits, 0);
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
- nr_pages = 0;
|
|
|
|
- for (i = 0; i < found_pages; i++) {
|
|
|
|
- page = wdata->pages[i];
|
|
|
|
- /*
|
|
|
|
- * At this point we hold neither mapping->tree_lock nor
|
|
|
|
- * lock on the page itself: the page may be truncated or
|
|
|
|
- * invalidated (changing page->mapping to NULL), or even
|
|
|
|
- * swizzled back from swapper_space to tmpfs file
|
|
|
|
- * mapping
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- if (nr_pages == 0)
|
|
|
|
- lock_page(page);
|
|
|
|
- else if (!trylock_page(page))
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- if (unlikely(page->mapping != mapping)) {
|
|
|
|
- unlock_page(page);
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (!wbc->range_cyclic && page->index > end) {
|
|
|
|
- done = true;
|
|
|
|
- unlock_page(page);
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (next && (page->index != next)) {
|
|
|
|
- /* Not next consecutive page */
|
|
|
|
- unlock_page(page);
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (wbc->sync_mode != WB_SYNC_NONE)
|
|
|
|
- wait_on_page_writeback(page);
|
|
|
|
-
|
|
|
|
- if (PageWriteback(page) ||
|
|
|
|
- !clear_page_dirty_for_io(page)) {
|
|
|
|
- unlock_page(page);
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /*
|
|
|
|
- * This actually clears the dirty bit in the radix tree.
|
|
|
|
- * See cifs_writepage() for more commentary.
|
|
|
|
- */
|
|
|
|
- set_page_writeback(page);
|
|
|
|
-
|
|
|
|
- if (page_offset(page) >= i_size_read(mapping->host)) {
|
|
|
|
- done = true;
|
|
|
|
- unlock_page(page);
|
|
|
|
- end_page_writeback(page);
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- wdata->pages[i] = page;
|
|
|
|
- next = page->index + 1;
|
|
|
|
- ++nr_pages;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* reset index to refind any pages skipped */
|
|
|
|
- if (nr_pages == 0)
|
|
|
|
- index = wdata->pages[0]->index + 1;
|
|
|
|
-
|
|
|
|
- /* put any pages we aren't going to use */
|
|
|
|
- for (i = nr_pages; i < found_pages; i++) {
|
|
|
|
- page_cache_release(wdata->pages[i]);
|
|
|
|
- wdata->pages[i] = NULL;
|
|
|
|
- }
|
|
|
|
|
|
+ nr_pages = wdata_prepare_pages(wdata, found_pages, mapping, wbc,
|
|
|
|
+ end, &index, &next, &done);
|
|
|
|
|
|
/* nothing to write? */
|
|
/* nothing to write? */
|
|
if (nr_pages == 0) {
|
|
if (nr_pages == 0) {
|
|
kref_put(&wdata->refcount, cifs_writedata_release);
|
|
kref_put(&wdata->refcount, cifs_writedata_release);
|
|
|
|
+ add_credits_and_wake_if(server, credits, 0);
|
|
continue;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
|
|
- wdata->sync_mode = wbc->sync_mode;
|
|
|
|
- wdata->nr_pages = nr_pages;
|
|
|
|
- wdata->offset = page_offset(wdata->pages[0]);
|
|
|
|
- wdata->pagesz = PAGE_CACHE_SIZE;
|
|
|
|
- wdata->tailsz =
|
|
|
|
- min(i_size_read(mapping->host) -
|
|
|
|
- page_offset(wdata->pages[nr_pages - 1]),
|
|
|
|
- (loff_t)PAGE_CACHE_SIZE);
|
|
|
|
- wdata->bytes = ((nr_pages - 1) * PAGE_CACHE_SIZE) +
|
|
|
|
- wdata->tailsz;
|
|
|
|
-
|
|
|
|
- do {
|
|
|
|
- if (wdata->cfile != NULL)
|
|
|
|
- cifsFileInfo_put(wdata->cfile);
|
|
|
|
- wdata->cfile = find_writable_file(CIFS_I(mapping->host),
|
|
|
|
- false);
|
|
|
|
- if (!wdata->cfile) {
|
|
|
|
- cifs_dbg(VFS, "No writable handles for inode\n");
|
|
|
|
- rc = -EBADF;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- wdata->pid = wdata->cfile->pid;
|
|
|
|
- server = tlink_tcon(wdata->cfile->tlink)->ses->server;
|
|
|
|
- rc = server->ops->async_writev(wdata,
|
|
|
|
- cifs_writedata_release);
|
|
|
|
- } while (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN);
|
|
|
|
|
|
+ wdata->credits = credits;
|
|
|
|
|
|
- for (i = 0; i < nr_pages; ++i)
|
|
|
|
- unlock_page(wdata->pages[i]);
|
|
|
|
|
|
+ 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);
|
|
for (i = 0; i < nr_pages; ++i) {
|
|
for (i = 0; i < nr_pages; ++i) {
|
|
if (rc == -EAGAIN)
|
|
if (rc == -EAGAIN)
|
|
redirty_page_for_writepage(wbc,
|
|
redirty_page_for_writepage(wbc,
|
|
@@ -2066,6 +2112,11 @@ retry:
|
|
}
|
|
}
|
|
kref_put(&wdata->refcount, cifs_writedata_release);
|
|
kref_put(&wdata->refcount, cifs_writedata_release);
|
|
|
|
|
|
|
|
+ if (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN) {
|
|
|
|
+ index = saved_index;
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
wbc->nr_to_write -= nr_pages;
|
|
wbc->nr_to_write -= nr_pages;
|
|
if (wbc->nr_to_write <= 0)
|
|
if (wbc->nr_to_write <= 0)
|
|
done = true;
|
|
done = true;
|
|
@@ -2362,123 +2413,109 @@ 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
|
|
static int
|
|
-cifs_uncached_retry_writev(struct cifs_writedata *wdata)
|
|
|
|
|
|
+wdata_fill_from_iovec(struct cifs_writedata *wdata, struct iov_iter *from,
|
|
|
|
+ size_t *len, unsigned long *num_pages)
|
|
{
|
|
{
|
|
- int rc;
|
|
|
|
- struct TCP_Server_Info *server;
|
|
|
|
|
|
+ size_t save_len, copied, bytes, cur_len = *len;
|
|
|
|
+ unsigned long i, nr_pages = *num_pages;
|
|
|
|
|
|
- server = tlink_tcon(wdata->cfile->tlink)->ses->server;
|
|
|
|
|
|
+ save_len = cur_len;
|
|
|
|
+ for (i = 0; i < nr_pages; i++) {
|
|
|
|
+ bytes = min_t(const size_t, cur_len, PAGE_SIZE);
|
|
|
|
+ copied = copy_page_from_iter(wdata->pages[i], 0, bytes, from);
|
|
|
|
+ cur_len -= copied;
|
|
|
|
+ /*
|
|
|
|
+ * If we didn't copy as much as we expected, then that
|
|
|
|
+ * may mean we trod into an unmapped area. Stop copying
|
|
|
|
+ * at that point. On the next pass through the big
|
|
|
|
+ * loop, we'll likely end up getting a zero-length
|
|
|
|
+ * write and bailing out of it.
|
|
|
|
+ */
|
|
|
|
+ if (copied < bytes)
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ cur_len = save_len - cur_len;
|
|
|
|
+ *len = cur_len;
|
|
|
|
|
|
- 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);
|
|
|
|
|
|
+ /*
|
|
|
|
+ * If we have no data to send, then that probably means that
|
|
|
|
+ * the copy above failed altogether. That's most likely because
|
|
|
|
+ * the address in the iovec was bogus. Return -EFAULT and let
|
|
|
|
+ * the caller free anything we allocated and bail out.
|
|
|
|
+ */
|
|
|
|
+ if (!cur_len)
|
|
|
|
+ return -EFAULT;
|
|
|
|
|
|
- return rc;
|
|
|
|
|
|
+ /*
|
|
|
|
+ * i + 1 now represents the number of pages we actually used in
|
|
|
|
+ * the copy phase above.
|
|
|
|
+ */
|
|
|
|
+ *num_pages = i + 1;
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static ssize_t
|
|
|
|
-cifs_iovec_write(struct file *file, struct iov_iter *from, loff_t *poffset)
|
|
|
|
|
|
+static int
|
|
|
|
+cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
|
|
|
|
+ struct cifsFileInfo *open_file,
|
|
|
|
+ struct cifs_sb_info *cifs_sb, struct list_head *wdata_list)
|
|
{
|
|
{
|
|
- unsigned long nr_pages, i;
|
|
|
|
- size_t bytes, copied, len, cur_len;
|
|
|
|
- ssize_t total_written = 0;
|
|
|
|
- loff_t offset;
|
|
|
|
- struct cifsFileInfo *open_file;
|
|
|
|
- struct cifs_tcon *tcon;
|
|
|
|
- struct cifs_sb_info *cifs_sb;
|
|
|
|
- struct cifs_writedata *wdata, *tmp;
|
|
|
|
- struct list_head wdata_list;
|
|
|
|
- int rc;
|
|
|
|
|
|
+ int rc = 0;
|
|
|
|
+ size_t cur_len;
|
|
|
|
+ unsigned long nr_pages, num_pages, i;
|
|
|
|
+ struct cifs_writedata *wdata;
|
|
|
|
+ struct iov_iter saved_from;
|
|
|
|
+ loff_t saved_offset = offset;
|
|
pid_t pid;
|
|
pid_t pid;
|
|
-
|
|
|
|
- len = iov_iter_count(from);
|
|
|
|
- rc = generic_write_checks(file, poffset, &len, 0);
|
|
|
|
- if (rc)
|
|
|
|
- return rc;
|
|
|
|
-
|
|
|
|
- if (!len)
|
|
|
|
- return 0;
|
|
|
|
-
|
|
|
|
- iov_iter_truncate(from, len);
|
|
|
|
-
|
|
|
|
- INIT_LIST_HEAD(&wdata_list);
|
|
|
|
- cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
|
|
|
|
- open_file = file->private_data;
|
|
|
|
- tcon = tlink_tcon(open_file->tlink);
|
|
|
|
-
|
|
|
|
- if (!tcon->ses->server->ops->async_writev)
|
|
|
|
- return -ENOSYS;
|
|
|
|
-
|
|
|
|
- offset = *poffset;
|
|
|
|
|
|
+ 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 {
|
|
- size_t save_len;
|
|
|
|
|
|
+ unsigned int wsize, credits;
|
|
|
|
+
|
|
|
|
+ rc = server->ops->wait_mtu_credits(server, cifs_sb->wsize,
|
|
|
|
+ &wsize, &credits);
|
|
|
|
+ if (rc)
|
|
|
|
+ break;
|
|
|
|
|
|
- nr_pages = get_numpages(cifs_sb->wsize, len, &cur_len);
|
|
|
|
|
|
+ nr_pages = get_numpages(wsize, len, &cur_len);
|
|
wdata = cifs_writedata_alloc(nr_pages,
|
|
wdata = cifs_writedata_alloc(nr_pages,
|
|
cifs_uncached_writev_complete);
|
|
cifs_uncached_writev_complete);
|
|
if (!wdata) {
|
|
if (!wdata) {
|
|
rc = -ENOMEM;
|
|
rc = -ENOMEM;
|
|
|
|
+ add_credits_and_wake_if(server, credits, 0);
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
rc = cifs_write_allocate_pages(wdata->pages, nr_pages);
|
|
rc = cifs_write_allocate_pages(wdata->pages, nr_pages);
|
|
if (rc) {
|
|
if (rc) {
|
|
kfree(wdata);
|
|
kfree(wdata);
|
|
|
|
+ add_credits_and_wake_if(server, credits, 0);
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
- save_len = cur_len;
|
|
|
|
- for (i = 0; i < nr_pages; i++) {
|
|
|
|
- bytes = min_t(size_t, cur_len, PAGE_SIZE);
|
|
|
|
- copied = copy_page_from_iter(wdata->pages[i], 0, bytes,
|
|
|
|
- from);
|
|
|
|
- cur_len -= copied;
|
|
|
|
- /*
|
|
|
|
- * If we didn't copy as much as we expected, then that
|
|
|
|
- * may mean we trod into an unmapped area. Stop copying
|
|
|
|
- * at that point. On the next pass through the big
|
|
|
|
- * loop, we'll likely end up getting a zero-length
|
|
|
|
- * write and bailing out of it.
|
|
|
|
- */
|
|
|
|
- if (copied < bytes)
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- cur_len = save_len - cur_len;
|
|
|
|
-
|
|
|
|
- /*
|
|
|
|
- * If we have no data to send, then that probably means that
|
|
|
|
- * the copy above failed altogether. That's most likely because
|
|
|
|
- * the address in the iovec was bogus. Set the rc to -EFAULT,
|
|
|
|
- * free anything we allocated and bail out.
|
|
|
|
- */
|
|
|
|
- if (!cur_len) {
|
|
|
|
|
|
+ num_pages = nr_pages;
|
|
|
|
+ rc = wdata_fill_from_iovec(wdata, from, &cur_len, &num_pages);
|
|
|
|
+ if (rc) {
|
|
for (i = 0; i < nr_pages; i++)
|
|
for (i = 0; i < nr_pages; i++)
|
|
put_page(wdata->pages[i]);
|
|
put_page(wdata->pages[i]);
|
|
kfree(wdata);
|
|
kfree(wdata);
|
|
- rc = -EFAULT;
|
|
|
|
|
|
+ add_credits_and_wake_if(server, credits, 0);
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
- * i + 1 now represents the number of pages we actually used in
|
|
|
|
- * the copy phase above. Bring nr_pages down to that, and free
|
|
|
|
- * any pages that we didn't use.
|
|
|
|
|
|
+ * Bring nr_pages down to the number of pages we actually used,
|
|
|
|
+ * and free any pages that we didn't use.
|
|
*/
|
|
*/
|
|
- for ( ; nr_pages > i + 1; nr_pages--)
|
|
|
|
|
|
+ for ( ; nr_pages > num_pages; nr_pages--)
|
|
put_page(wdata->pages[nr_pages - 1]);
|
|
put_page(wdata->pages[nr_pages - 1]);
|
|
|
|
|
|
wdata->sync_mode = WB_SYNC_ALL;
|
|
wdata->sync_mode = WB_SYNC_ALL;
|
|
@@ -2489,18 +2526,69 @@ cifs_iovec_write(struct file *file, struct iov_iter *from, loff_t *poffset)
|
|
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);
|
|
|
|
|
|
+ wdata->credits = credits;
|
|
|
|
+
|
|
|
|
+ if (!wdata->cfile->invalidHandle ||
|
|
|
|
+ !cifs_reopen_file(wdata->cfile, false))
|
|
|
|
+ rc = server->ops->async_writev(wdata,
|
|
|
|
+ cifs_uncached_writedata_release);
|
|
if (rc) {
|
|
if (rc) {
|
|
|
|
+ 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) {
|
|
|
|
+ memcpy(from, &saved_from,
|
|
|
|
+ sizeof(struct iov_iter));
|
|
|
|
+ iov_iter_advance(from, offset - saved_offset);
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
- list_add_tail(&wdata->list, &wdata_list);
|
|
|
|
|
|
+ list_add_tail(&wdata->list, wdata_list);
|
|
offset += cur_len;
|
|
offset += cur_len;
|
|
len -= cur_len;
|
|
len -= cur_len;
|
|
} while (len > 0);
|
|
} while (len > 0);
|
|
|
|
|
|
|
|
+ return rc;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static ssize_t
|
|
|
|
+cifs_iovec_write(struct file *file, struct iov_iter *from, loff_t *poffset)
|
|
|
|
+{
|
|
|
|
+ size_t len;
|
|
|
|
+ ssize_t total_written = 0;
|
|
|
|
+ struct cifsFileInfo *open_file;
|
|
|
|
+ struct cifs_tcon *tcon;
|
|
|
|
+ struct cifs_sb_info *cifs_sb;
|
|
|
|
+ struct cifs_writedata *wdata, *tmp;
|
|
|
|
+ struct list_head wdata_list;
|
|
|
|
+ struct iov_iter saved_from;
|
|
|
|
+ int rc;
|
|
|
|
+
|
|
|
|
+ len = iov_iter_count(from);
|
|
|
|
+ rc = generic_write_checks(file, poffset, &len, 0);
|
|
|
|
+ if (rc)
|
|
|
|
+ return rc;
|
|
|
|
+
|
|
|
|
+ if (!len)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ iov_iter_truncate(from, len);
|
|
|
|
+
|
|
|
|
+ INIT_LIST_HEAD(&wdata_list);
|
|
|
|
+ cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
|
|
|
|
+ open_file = file->private_data;
|
|
|
|
+ tcon = tlink_tcon(open_file->tlink);
|
|
|
|
+
|
|
|
|
+ if (!tcon->ses->server->ops->async_writev)
|
|
|
|
+ return -ENOSYS;
|
|
|
|
+
|
|
|
|
+ memcpy(&saved_from, from, sizeof(struct iov_iter));
|
|
|
|
+
|
|
|
|
+ rc = cifs_write_from_iter(*poffset, len, from, open_file, cifs_sb,
|
|
|
|
+ &wdata_list);
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* If at least one write was successfully sent, then discard any rc
|
|
* If at least one write was successfully sent, then discard any rc
|
|
* value from the later writes. If the other write succeeds, then
|
|
* value from the later writes. If the other write succeeds, then
|
|
@@ -2529,7 +2617,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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -2722,26 +2828,6 @@ cifs_uncached_readdata_release(struct kref *refcount)
|
|
cifs_readdata_release(refcount);
|
|
cifs_readdata_release(refcount);
|
|
}
|
|
}
|
|
|
|
|
|
-static int
|
|
|
|
-cifs_retry_async_readv(struct cifs_readdata *rdata)
|
|
|
|
-{
|
|
|
|
- int rc;
|
|
|
|
- struct TCP_Server_Info *server;
|
|
|
|
-
|
|
|
|
- server = tlink_tcon(rdata->cfile->tlink)->ses->server;
|
|
|
|
-
|
|
|
|
- do {
|
|
|
|
- if (rdata->cfile->invalidHandle) {
|
|
|
|
- rc = cifs_reopen_file(rdata->cfile, true);
|
|
|
|
- if (rc != 0)
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
- rc = server->ops->async_readv(rdata);
|
|
|
|
- } while (rc == -EAGAIN);
|
|
|
|
-
|
|
|
|
- return rc;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* cifs_readdata_to_iov - copy data from pages in response to an iovec
|
|
* cifs_readdata_to_iov - copy data from pages in response to an iovec
|
|
* @rdata: the readdata response with list of pages holding data
|
|
* @rdata: the readdata response with list of pages holding data
|
|
@@ -2754,7 +2840,7 @@ cifs_retry_async_readv(struct cifs_readdata *rdata)
|
|
static int
|
|
static int
|
|
cifs_readdata_to_iov(struct cifs_readdata *rdata, struct iov_iter *iter)
|
|
cifs_readdata_to_iov(struct cifs_readdata *rdata, struct iov_iter *iter)
|
|
{
|
|
{
|
|
- size_t remaining = rdata->bytes;
|
|
|
|
|
|
+ size_t remaining = rdata->got_bytes;
|
|
unsigned int i;
|
|
unsigned int i;
|
|
|
|
|
|
for (i = 0; i < rdata->nr_pages; i++) {
|
|
for (i = 0; i < rdata->nr_pages; i++) {
|
|
@@ -2782,11 +2868,12 @@ static int
|
|
cifs_uncached_read_into_pages(struct TCP_Server_Info *server,
|
|
cifs_uncached_read_into_pages(struct TCP_Server_Info *server,
|
|
struct cifs_readdata *rdata, unsigned int len)
|
|
struct cifs_readdata *rdata, unsigned int len)
|
|
{
|
|
{
|
|
- int total_read = 0, result = 0;
|
|
|
|
|
|
+ int result = 0;
|
|
unsigned int i;
|
|
unsigned int i;
|
|
unsigned int nr_pages = rdata->nr_pages;
|
|
unsigned int nr_pages = rdata->nr_pages;
|
|
struct kvec iov;
|
|
struct kvec iov;
|
|
|
|
|
|
|
|
+ rdata->got_bytes = 0;
|
|
rdata->tailsz = PAGE_SIZE;
|
|
rdata->tailsz = PAGE_SIZE;
|
|
for (i = 0; i < nr_pages; i++) {
|
|
for (i = 0; i < nr_pages; i++) {
|
|
struct page *page = rdata->pages[i];
|
|
struct page *page = rdata->pages[i];
|
|
@@ -2820,55 +2907,45 @@ cifs_uncached_read_into_pages(struct TCP_Server_Info *server,
|
|
if (result < 0)
|
|
if (result < 0)
|
|
break;
|
|
break;
|
|
|
|
|
|
- total_read += result;
|
|
|
|
|
|
+ rdata->got_bytes += result;
|
|
}
|
|
}
|
|
|
|
|
|
- return total_read > 0 ? total_read : result;
|
|
|
|
|
|
+ return rdata->got_bytes > 0 && result != -ECONNABORTED ?
|
|
|
|
+ rdata->got_bytes : result;
|
|
}
|
|
}
|
|
|
|
|
|
-ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to)
|
|
|
|
|
|
+static int
|
|
|
|
+cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
|
|
|
|
+ struct cifs_sb_info *cifs_sb, struct list_head *rdata_list)
|
|
{
|
|
{
|
|
- struct file *file = iocb->ki_filp;
|
|
|
|
- ssize_t rc;
|
|
|
|
- size_t len, cur_len;
|
|
|
|
- ssize_t total_read = 0;
|
|
|
|
- loff_t offset = iocb->ki_pos;
|
|
|
|
- unsigned int npages;
|
|
|
|
- struct cifs_sb_info *cifs_sb;
|
|
|
|
- struct cifs_tcon *tcon;
|
|
|
|
- struct cifsFileInfo *open_file;
|
|
|
|
- struct cifs_readdata *rdata, *tmp;
|
|
|
|
- struct list_head rdata_list;
|
|
|
|
|
|
+ struct cifs_readdata *rdata;
|
|
|
|
+ unsigned int npages, rsize, credits;
|
|
|
|
+ size_t cur_len;
|
|
|
|
+ int rc;
|
|
pid_t pid;
|
|
pid_t pid;
|
|
|
|
+ struct TCP_Server_Info *server;
|
|
|
|
|
|
- len = iov_iter_count(to);
|
|
|
|
- if (!len)
|
|
|
|
- return 0;
|
|
|
|
-
|
|
|
|
- INIT_LIST_HEAD(&rdata_list);
|
|
|
|
- cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
|
|
|
|
- open_file = file->private_data;
|
|
|
|
- tcon = tlink_tcon(open_file->tlink);
|
|
|
|
-
|
|
|
|
- if (!tcon->ses->server->ops->async_readv)
|
|
|
|
- return -ENOSYS;
|
|
|
|
|
|
+ server = tlink_tcon(open_file->tlink)->ses->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;
|
|
|
|
|
|
- if ((file->f_flags & O_ACCMODE) == O_WRONLY)
|
|
|
|
- cifs_dbg(FYI, "attempting read on write only file instance\n");
|
|
|
|
-
|
|
|
|
do {
|
|
do {
|
|
- cur_len = min_t(const size_t, len - total_read, cifs_sb->rsize);
|
|
|
|
|
|
+ rc = server->ops->wait_mtu_credits(server, cifs_sb->rsize,
|
|
|
|
+ &rsize, &credits);
|
|
|
|
+ if (rc)
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ cur_len = min_t(const size_t, len, rsize);
|
|
npages = DIV_ROUND_UP(cur_len, PAGE_SIZE);
|
|
npages = DIV_ROUND_UP(cur_len, PAGE_SIZE);
|
|
|
|
|
|
/* allocate a readdata struct */
|
|
/* allocate a readdata struct */
|
|
rdata = cifs_readdata_alloc(npages,
|
|
rdata = cifs_readdata_alloc(npages,
|
|
cifs_uncached_readv_complete);
|
|
cifs_uncached_readv_complete);
|
|
if (!rdata) {
|
|
if (!rdata) {
|
|
|
|
+ add_credits_and_wake_if(server, credits, 0);
|
|
rc = -ENOMEM;
|
|
rc = -ENOMEM;
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
@@ -2884,44 +2961,113 @@ ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to)
|
|
rdata->pid = pid;
|
|
rdata->pid = pid;
|
|
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->credits = credits;
|
|
|
|
|
|
- rc = cifs_retry_async_readv(rdata);
|
|
|
|
|
|
+ if (!rdata->cfile->invalidHandle ||
|
|
|
|
+ !cifs_reopen_file(rdata->cfile, true))
|
|
|
|
+ rc = server->ops->async_readv(rdata);
|
|
error:
|
|
error:
|
|
if (rc) {
|
|
if (rc) {
|
|
|
|
+ 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)
|
|
|
|
+ continue;
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
- list_add_tail(&rdata->list, &rdata_list);
|
|
|
|
|
|
+ list_add_tail(&rdata->list, rdata_list);
|
|
offset += cur_len;
|
|
offset += cur_len;
|
|
len -= cur_len;
|
|
len -= cur_len;
|
|
} while (len > 0);
|
|
} while (len > 0);
|
|
|
|
|
|
|
|
+ return rc;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to)
|
|
|
|
+{
|
|
|
|
+ struct file *file = iocb->ki_filp;
|
|
|
|
+ ssize_t rc;
|
|
|
|
+ size_t len;
|
|
|
|
+ ssize_t total_read = 0;
|
|
|
|
+ loff_t offset = iocb->ki_pos;
|
|
|
|
+ struct cifs_sb_info *cifs_sb;
|
|
|
|
+ struct cifs_tcon *tcon;
|
|
|
|
+ struct cifsFileInfo *open_file;
|
|
|
|
+ struct cifs_readdata *rdata, *tmp;
|
|
|
|
+ struct list_head rdata_list;
|
|
|
|
+
|
|
|
|
+ len = iov_iter_count(to);
|
|
|
|
+ if (!len)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ INIT_LIST_HEAD(&rdata_list);
|
|
|
|
+ cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
|
|
|
|
+ open_file = file->private_data;
|
|
|
|
+ tcon = tlink_tcon(open_file->tlink);
|
|
|
|
+
|
|
|
|
+ if (!tcon->ses->server->ops->async_readv)
|
|
|
|
+ return -ENOSYS;
|
|
|
|
+
|
|
|
|
+ if ((file->f_flags & O_ACCMODE) == O_WRONLY)
|
|
|
|
+ cifs_dbg(FYI, "attempting read on write only file instance\n");
|
|
|
|
+
|
|
|
|
+ rc = cifs_send_async_read(offset, len, open_file, cifs_sb, &rdata_list);
|
|
|
|
+
|
|
/* if at least one read request send succeeded, then reset rc */
|
|
/* if at least one read request send succeeded, then reset rc */
|
|
if (!list_empty(&rdata_list))
|
|
if (!list_empty(&rdata_list))
|
|
rc = 0;
|
|
rc = 0;
|
|
|
|
|
|
len = iov_iter_count(to);
|
|
len = iov_iter_count(to);
|
|
/* the loop below should proceed in the order of increasing offsets */
|
|
/* the loop below should proceed in the order of increasing offsets */
|
|
|
|
+again:
|
|
list_for_each_entry_safe(rdata, tmp, &rdata_list, list) {
|
|
list_for_each_entry_safe(rdata, tmp, &rdata_list, list) {
|
|
- again:
|
|
|
|
if (!rc) {
|
|
if (!rc) {
|
|
/* FIXME: freezable sleep too? */
|
|
/* FIXME: freezable sleep too? */
|
|
rc = wait_for_completion_killable(&rdata->done);
|
|
rc = wait_for_completion_killable(&rdata->done);
|
|
if (rc)
|
|
if (rc)
|
|
rc = -EINTR;
|
|
rc = -EINTR;
|
|
- else if (rdata->result) {
|
|
|
|
- rc = rdata->result;
|
|
|
|
|
|
+ else if (rdata->result == -EAGAIN) {
|
|
/* resend call if it's a retryable error */
|
|
/* resend call if it's a retryable error */
|
|
- if (rc == -EAGAIN) {
|
|
|
|
- rc = cifs_retry_async_readv(rdata);
|
|
|
|
- goto again;
|
|
|
|
|
|
+ struct list_head tmp_list;
|
|
|
|
+ unsigned int got_bytes = rdata->got_bytes;
|
|
|
|
+
|
|
|
|
+ list_del_init(&rdata->list);
|
|
|
|
+ INIT_LIST_HEAD(&tmp_list);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Got a part of data and then reconnect has
|
|
|
|
+ * happened -- fill the buffer and continue
|
|
|
|
+ * reading.
|
|
|
|
+ */
|
|
|
|
+ if (got_bytes && got_bytes < rdata->bytes) {
|
|
|
|
+ rc = cifs_readdata_to_iov(rdata, to);
|
|
|
|
+ if (rc) {
|
|
|
|
+ kref_put(&rdata->refcount,
|
|
|
|
+ cifs_uncached_readdata_release);
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- } else {
|
|
|
|
|
|
+
|
|
|
|
+ rc = cifs_send_async_read(
|
|
|
|
+ rdata->offset + got_bytes,
|
|
|
|
+ rdata->bytes - got_bytes,
|
|
|
|
+ rdata->cfile, cifs_sb,
|
|
|
|
+ &tmp_list);
|
|
|
|
+
|
|
|
|
+ list_splice(&tmp_list, &rdata_list);
|
|
|
|
+
|
|
|
|
+ kref_put(&rdata->refcount,
|
|
|
|
+ cifs_uncached_readdata_release);
|
|
|
|
+ goto again;
|
|
|
|
+ } else if (rdata->result)
|
|
|
|
+ rc = rdata->result;
|
|
|
|
+ else
|
|
rc = cifs_readdata_to_iov(rdata, to);
|
|
rc = cifs_readdata_to_iov(rdata, to);
|
|
- }
|
|
|
|
|
|
|
|
|
|
+ /* if there was a short read -- discard anything left */
|
|
|
|
+ if (rdata->got_bytes && rdata->got_bytes < rdata->bytes)
|
|
|
|
+ rc = -ENODATA;
|
|
}
|
|
}
|
|
list_del_init(&rdata->list);
|
|
list_del_init(&rdata->list);
|
|
kref_put(&rdata->refcount, cifs_uncached_readdata_release);
|
|
kref_put(&rdata->refcount, cifs_uncached_readdata_release);
|
|
@@ -3030,18 +3176,19 @@ cifs_read(struct file *file, char *read_data, size_t read_size, loff_t *offset)
|
|
|
|
|
|
for (total_read = 0, cur_offset = read_data; read_size > total_read;
|
|
for (total_read = 0, cur_offset = read_data; read_size > total_read;
|
|
total_read += bytes_read, cur_offset += bytes_read) {
|
|
total_read += bytes_read, cur_offset += bytes_read) {
|
|
- current_read_size = min_t(uint, read_size - total_read, rsize);
|
|
|
|
- /*
|
|
|
|
- * For windows me and 9x we do not want to request more than it
|
|
|
|
- * negotiated since it will refuse the read then.
|
|
|
|
- */
|
|
|
|
- if ((tcon->ses) && !(tcon->ses->capabilities &
|
|
|
|
|
|
+ do {
|
|
|
|
+ current_read_size = min_t(uint, read_size - total_read,
|
|
|
|
+ rsize);
|
|
|
|
+ /*
|
|
|
|
+ * For windows me and 9x we do not want to request more
|
|
|
|
+ * than it negotiated since it will refuse the read
|
|
|
|
+ * then.
|
|
|
|
+ */
|
|
|
|
+ if ((tcon->ses) && !(tcon->ses->capabilities &
|
|
tcon->ses->server->vals->cap_large_files)) {
|
|
tcon->ses->server->vals->cap_large_files)) {
|
|
- current_read_size = min_t(uint, current_read_size,
|
|
|
|
- CIFSMaxBufSize);
|
|
|
|
- }
|
|
|
|
- rc = -EAGAIN;
|
|
|
|
- while (rc == -EAGAIN) {
|
|
|
|
|
|
+ current_read_size = min_t(uint,
|
|
|
|
+ current_read_size, CIFSMaxBufSize);
|
|
|
|
+ }
|
|
if (open_file->invalidHandle) {
|
|
if (open_file->invalidHandle) {
|
|
rc = cifs_reopen_file(open_file, true);
|
|
rc = cifs_reopen_file(open_file, true);
|
|
if (rc != 0)
|
|
if (rc != 0)
|
|
@@ -3054,7 +3201,8 @@ cifs_read(struct file *file, char *read_data, size_t read_size, loff_t *offset)
|
|
rc = server->ops->sync_read(xid, open_file, &io_parms,
|
|
rc = server->ops->sync_read(xid, open_file, &io_parms,
|
|
&bytes_read, &cur_offset,
|
|
&bytes_read, &cur_offset,
|
|
&buf_type);
|
|
&buf_type);
|
|
- }
|
|
|
|
|
|
+ } while (rc == -EAGAIN);
|
|
|
|
+
|
|
if (rc || (bytes_read == 0)) {
|
|
if (rc || (bytes_read == 0)) {
|
|
if (total_read) {
|
|
if (total_read) {
|
|
break;
|
|
break;
|
|
@@ -3133,25 +3281,30 @@ int cifs_file_mmap(struct file *file, struct vm_area_struct *vma)
|
|
static void
|
|
static void
|
|
cifs_readv_complete(struct work_struct *work)
|
|
cifs_readv_complete(struct work_struct *work)
|
|
{
|
|
{
|
|
- unsigned int i;
|
|
|
|
|
|
+ unsigned int i, got_bytes;
|
|
struct cifs_readdata *rdata = container_of(work,
|
|
struct cifs_readdata *rdata = container_of(work,
|
|
struct cifs_readdata, work);
|
|
struct cifs_readdata, work);
|
|
|
|
|
|
|
|
+ got_bytes = rdata->got_bytes;
|
|
for (i = 0; i < rdata->nr_pages; i++) {
|
|
for (i = 0; i < rdata->nr_pages; i++) {
|
|
struct page *page = rdata->pages[i];
|
|
struct page *page = rdata->pages[i];
|
|
|
|
|
|
lru_cache_add_file(page);
|
|
lru_cache_add_file(page);
|
|
|
|
|
|
- if (rdata->result == 0) {
|
|
|
|
|
|
+ if (rdata->result == 0 ||
|
|
|
|
+ (rdata->result == -EAGAIN && got_bytes)) {
|
|
flush_dcache_page(page);
|
|
flush_dcache_page(page);
|
|
SetPageUptodate(page);
|
|
SetPageUptodate(page);
|
|
}
|
|
}
|
|
|
|
|
|
unlock_page(page);
|
|
unlock_page(page);
|
|
|
|
|
|
- if (rdata->result == 0)
|
|
|
|
|
|
+ if (rdata->result == 0 ||
|
|
|
|
+ (rdata->result == -EAGAIN && got_bytes))
|
|
cifs_readpage_to_fscache(rdata->mapping->host, page);
|
|
cifs_readpage_to_fscache(rdata->mapping->host, page);
|
|
|
|
|
|
|
|
+ got_bytes -= min_t(unsigned int, PAGE_CACHE_SIZE, got_bytes);
|
|
|
|
+
|
|
page_cache_release(page);
|
|
page_cache_release(page);
|
|
rdata->pages[i] = NULL;
|
|
rdata->pages[i] = NULL;
|
|
}
|
|
}
|
|
@@ -3162,7 +3315,7 @@ static int
|
|
cifs_readpages_read_into_pages(struct TCP_Server_Info *server,
|
|
cifs_readpages_read_into_pages(struct TCP_Server_Info *server,
|
|
struct cifs_readdata *rdata, unsigned int len)
|
|
struct cifs_readdata *rdata, unsigned int len)
|
|
{
|
|
{
|
|
- int total_read = 0, result = 0;
|
|
|
|
|
|
+ int result = 0;
|
|
unsigned int i;
|
|
unsigned int i;
|
|
u64 eof;
|
|
u64 eof;
|
|
pgoff_t eof_index;
|
|
pgoff_t eof_index;
|
|
@@ -3174,6 +3327,7 @@ cifs_readpages_read_into_pages(struct TCP_Server_Info *server,
|
|
eof_index = eof ? (eof - 1) >> PAGE_CACHE_SHIFT : 0;
|
|
eof_index = eof ? (eof - 1) >> PAGE_CACHE_SHIFT : 0;
|
|
cifs_dbg(FYI, "eof=%llu eof_index=%lu\n", eof, eof_index);
|
|
cifs_dbg(FYI, "eof=%llu eof_index=%lu\n", eof, eof_index);
|
|
|
|
|
|
|
|
+ rdata->got_bytes = 0;
|
|
rdata->tailsz = PAGE_CACHE_SIZE;
|
|
rdata->tailsz = PAGE_CACHE_SIZE;
|
|
for (i = 0; i < nr_pages; i++) {
|
|
for (i = 0; i < nr_pages; i++) {
|
|
struct page *page = rdata->pages[i];
|
|
struct page *page = rdata->pages[i];
|
|
@@ -3228,10 +3382,70 @@ cifs_readpages_read_into_pages(struct TCP_Server_Info *server,
|
|
if (result < 0)
|
|
if (result < 0)
|
|
break;
|
|
break;
|
|
|
|
|
|
- total_read += result;
|
|
|
|
|
|
+ rdata->got_bytes += result;
|
|
}
|
|
}
|
|
|
|
|
|
- return total_read > 0 ? total_read : result;
|
|
|
|
|
|
+ return rdata->got_bytes > 0 && result != -ECONNABORTED ?
|
|
|
|
+ rdata->got_bytes : result;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int
|
|
|
|
+readpages_get_pages(struct address_space *mapping, struct list_head *page_list,
|
|
|
|
+ unsigned int rsize, struct list_head *tmplist,
|
|
|
|
+ unsigned int *nr_pages, loff_t *offset, unsigned int *bytes)
|
|
|
|
+{
|
|
|
|
+ struct page *page, *tpage;
|
|
|
|
+ unsigned int expected_index;
|
|
|
|
+ int rc;
|
|
|
|
+
|
|
|
|
+ INIT_LIST_HEAD(tmplist);
|
|
|
|
+
|
|
|
|
+ page = list_entry(page_list->prev, struct page, lru);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Lock the page and put it in the cache. Since no one else
|
|
|
|
+ * should have access to this page, we're safe to simply set
|
|
|
|
+ * PG_locked without checking it first.
|
|
|
|
+ */
|
|
|
|
+ __set_page_locked(page);
|
|
|
|
+ rc = add_to_page_cache_locked(page, mapping,
|
|
|
|
+ page->index, GFP_KERNEL);
|
|
|
|
+
|
|
|
|
+ /* give up if we can't stick it in the cache */
|
|
|
|
+ if (rc) {
|
|
|
|
+ __clear_page_locked(page);
|
|
|
|
+ return rc;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* move first page to the tmplist */
|
|
|
|
+ *offset = (loff_t)page->index << PAGE_CACHE_SHIFT;
|
|
|
|
+ *bytes = PAGE_CACHE_SIZE;
|
|
|
|
+ *nr_pages = 1;
|
|
|
|
+ list_move_tail(&page->lru, tmplist);
|
|
|
|
+
|
|
|
|
+ /* now try and add more pages onto the request */
|
|
|
|
+ expected_index = page->index + 1;
|
|
|
|
+ list_for_each_entry_safe_reverse(page, tpage, page_list, lru) {
|
|
|
|
+ /* discontinuity ? */
|
|
|
|
+ if (page->index != expected_index)
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ /* would this page push the read over the rsize? */
|
|
|
|
+ if (*bytes + PAGE_CACHE_SIZE > rsize)
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ __set_page_locked(page);
|
|
|
|
+ if (add_to_page_cache_locked(page, mapping, page->index,
|
|
|
|
+ GFP_KERNEL)) {
|
|
|
|
+ __clear_page_locked(page);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ list_move_tail(&page->lru, tmplist);
|
|
|
|
+ (*bytes) += PAGE_CACHE_SIZE;
|
|
|
|
+ expected_index++;
|
|
|
|
+ (*nr_pages)++;
|
|
|
|
+ }
|
|
|
|
+ return rc;
|
|
}
|
|
}
|
|
|
|
|
|
static int cifs_readpages(struct file *file, struct address_space *mapping,
|
|
static int cifs_readpages(struct file *file, struct address_space *mapping,
|
|
@@ -3241,18 +3455,9 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
|
|
struct list_head tmplist;
|
|
struct list_head tmplist;
|
|
struct cifsFileInfo *open_file = file->private_data;
|
|
struct cifsFileInfo *open_file = file->private_data;
|
|
struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
|
|
struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
|
|
- unsigned int rsize = cifs_sb->rsize;
|
|
|
|
|
|
+ struct TCP_Server_Info *server;
|
|
pid_t pid;
|
|
pid_t pid;
|
|
|
|
|
|
- /*
|
|
|
|
- * Give up immediately if rsize is too small to read an entire page.
|
|
|
|
- * The VFS will fall back to readpage. We should never reach this
|
|
|
|
- * point however since we set ra_pages to 0 when the rsize is smaller
|
|
|
|
- * than a cache page.
|
|
|
|
- */
|
|
|
|
- if (unlikely(rsize < PAGE_CACHE_SIZE))
|
|
|
|
- return 0;
|
|
|
|
-
|
|
|
|
/*
|
|
/*
|
|
* Reads as many pages as possible from fscache. Returns -ENOBUFS
|
|
* Reads as many pages as possible from fscache. Returns -ENOBUFS
|
|
* immediately if the cookie is negative
|
|
* immediately if the cookie is negative
|
|
@@ -3271,7 +3476,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
|
|
pid = current->tgid;
|
|
pid = current->tgid;
|
|
|
|
|
|
rc = 0;
|
|
rc = 0;
|
|
- INIT_LIST_HEAD(&tmplist);
|
|
|
|
|
|
+ server = tlink_tcon(open_file->tlink)->ses->server;
|
|
|
|
|
|
cifs_dbg(FYI, "%s: file=%p mapping=%p num_pages=%u\n",
|
|
cifs_dbg(FYI, "%s: file=%p mapping=%p num_pages=%u\n",
|
|
__func__, file, mapping, num_pages);
|
|
__func__, file, mapping, num_pages);
|
|
@@ -3288,58 +3493,35 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
|
|
* the rdata->pages, then we want them in increasing order.
|
|
* the rdata->pages, then we want them in increasing order.
|
|
*/
|
|
*/
|
|
while (!list_empty(page_list)) {
|
|
while (!list_empty(page_list)) {
|
|
- unsigned int i;
|
|
|
|
- unsigned int bytes = PAGE_CACHE_SIZE;
|
|
|
|
- unsigned int expected_index;
|
|
|
|
- unsigned int nr_pages = 1;
|
|
|
|
|
|
+ unsigned int i, nr_pages, bytes, rsize;
|
|
loff_t offset;
|
|
loff_t offset;
|
|
struct page *page, *tpage;
|
|
struct page *page, *tpage;
|
|
struct cifs_readdata *rdata;
|
|
struct cifs_readdata *rdata;
|
|
|
|
+ unsigned credits;
|
|
|
|
|
|
- page = list_entry(page_list->prev, struct page, lru);
|
|
|
|
|
|
+ rc = server->ops->wait_mtu_credits(server, cifs_sb->rsize,
|
|
|
|
+ &rsize, &credits);
|
|
|
|
+ if (rc)
|
|
|
|
+ break;
|
|
|
|
|
|
/*
|
|
/*
|
|
- * Lock the page and put it in the cache. Since no one else
|
|
|
|
- * should have access to this page, we're safe to simply set
|
|
|
|
- * PG_locked without checking it first.
|
|
|
|
|
|
+ * Give up immediately if rsize is too small to read an entire
|
|
|
|
+ * page. The VFS will fall back to readpage. We should never
|
|
|
|
+ * reach this point however since we set ra_pages to 0 when the
|
|
|
|
+ * rsize is smaller than a cache page.
|
|
*/
|
|
*/
|
|
- __set_page_locked(page);
|
|
|
|
- rc = add_to_page_cache_locked(page, mapping,
|
|
|
|
- page->index, GFP_KERNEL);
|
|
|
|
|
|
+ if (unlikely(rsize < PAGE_CACHE_SIZE)) {
|
|
|
|
+ add_credits_and_wake_if(server, credits, 0);
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
|
|
- /* give up if we can't stick it in the cache */
|
|
|
|
|
|
+ rc = readpages_get_pages(mapping, page_list, rsize, &tmplist,
|
|
|
|
+ &nr_pages, &offset, &bytes);
|
|
if (rc) {
|
|
if (rc) {
|
|
- __clear_page_locked(page);
|
|
|
|
|
|
+ add_credits_and_wake_if(server, credits, 0);
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
- /* move first page to the tmplist */
|
|
|
|
- offset = (loff_t)page->index << PAGE_CACHE_SHIFT;
|
|
|
|
- list_move_tail(&page->lru, &tmplist);
|
|
|
|
-
|
|
|
|
- /* now try and add more pages onto the request */
|
|
|
|
- expected_index = page->index + 1;
|
|
|
|
- list_for_each_entry_safe_reverse(page, tpage, page_list, lru) {
|
|
|
|
- /* discontinuity ? */
|
|
|
|
- if (page->index != expected_index)
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- /* would this page push the read over the rsize? */
|
|
|
|
- if (bytes + PAGE_CACHE_SIZE > rsize)
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- __set_page_locked(page);
|
|
|
|
- if (add_to_page_cache_locked(page, mapping,
|
|
|
|
- page->index, GFP_KERNEL)) {
|
|
|
|
- __clear_page_locked(page);
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- list_move_tail(&page->lru, &tmplist);
|
|
|
|
- bytes += PAGE_CACHE_SIZE;
|
|
|
|
- expected_index++;
|
|
|
|
- nr_pages++;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
rdata = cifs_readdata_alloc(nr_pages, cifs_readv_complete);
|
|
rdata = cifs_readdata_alloc(nr_pages, cifs_readv_complete);
|
|
if (!rdata) {
|
|
if (!rdata) {
|
|
/* best to give up if we're out of mem */
|
|
/* best to give up if we're out of mem */
|
|
@@ -3350,6 +3532,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
|
|
page_cache_release(page);
|
|
page_cache_release(page);
|
|
}
|
|
}
|
|
rc = -ENOMEM;
|
|
rc = -ENOMEM;
|
|
|
|
+ add_credits_and_wake_if(server, credits, 0);
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -3360,21 +3543,32 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
|
|
rdata->pid = pid;
|
|
rdata->pid = pid;
|
|
rdata->pagesz = PAGE_CACHE_SIZE;
|
|
rdata->pagesz = PAGE_CACHE_SIZE;
|
|
rdata->read_into_pages = cifs_readpages_read_into_pages;
|
|
rdata->read_into_pages = cifs_readpages_read_into_pages;
|
|
|
|
+ rdata->credits = credits;
|
|
|
|
|
|
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);
|
|
rdata->pages[rdata->nr_pages++] = page;
|
|
rdata->pages[rdata->nr_pages++] = page;
|
|
}
|
|
}
|
|
|
|
|
|
- rc = cifs_retry_async_readv(rdata);
|
|
|
|
- if (rc != 0) {
|
|
|
|
|
|
+ if (!rdata->cfile->invalidHandle ||
|
|
|
|
+ !cifs_reopen_file(rdata->cfile, true))
|
|
|
|
+ rc = server->ops->async_readv(rdata);
|
|
|
|
+ if (rc) {
|
|
|
|
+ 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);
|
|
unlock_page(page);
|
|
unlock_page(page);
|
|
page_cache_release(page);
|
|
page_cache_release(page);
|
|
|
|
+ if (rc == -EAGAIN)
|
|
|
|
+ list_add_tail(&page->lru, &tmplist);
|
|
}
|
|
}
|
|
kref_put(&rdata->refcount, cifs_readdata_release);
|
|
kref_put(&rdata->refcount, cifs_readdata_release);
|
|
|
|
+ if (rc == -EAGAIN) {
|
|
|
|
+ /* Re-add pages to the page_list and retry */
|
|
|
|
+ list_splice(&tmplist, page_list);
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|