CIFS: Separate pages initialization from writepages
[deliverable/linux.git] / fs / cifs / file.c
index 208f56eca4bf4de164d8af873b0050ac4884c5ea..0064d38fdb76b4cf039d6b5014f9bb00bc7ebc53 100644 (file)
@@ -1878,6 +1878,157 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
        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;
+
+       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);
+
+       for (i = 0; i < nr_pages; ++i)
+               unlock_page(wdata->pages[i]);
+
+       return rc;
+}
+
 static int cifs_writepages(struct address_space *mapping,
                           struct writeback_control *wbc)
 {
@@ -1885,8 +2036,6 @@ static int cifs_writepages(struct address_space *mapping,
        bool done = false, scanned = false, range_whole = false;
        pgoff_t end, index;
        struct cifs_writedata *wdata;
-       struct TCP_Server_Info *server;
-       struct page *page;
        int rc = 0;
 
        /*
@@ -1910,109 +2059,24 @@ retry:
        while (!done && index <= end) {
                unsigned int i, nr_pages, found_pages;
                pgoff_t next = 0, tofind;
-               struct page **pages;
 
                tofind = min((cifs_sb->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) {
                        rc = -ENOMEM;
                        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) {
                        kref_put(&wdata->refcount, cifs_writedata_release);
                        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? */
                if (nr_pages == 0) {
@@ -2020,35 +2084,7 @@ retry:
                        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);
-
-               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 */
                if (rc != 0) {
@@ -2385,14 +2421,12 @@ cifs_uncached_retry_writev(struct cifs_writedata *wdata)
 }
 
 static ssize_t
-cifs_iovec_write(struct file *file, const struct iovec *iov,
-                unsigned long nr_segs, loff_t *poffset)
+cifs_iovec_write(struct file *file, struct iov_iter *from, loff_t *poffset)
 {
        unsigned long nr_pages, i;
        size_t bytes, copied, len, cur_len;
        ssize_t total_written = 0;
        loff_t offset;
-       struct iov_iter it;
        struct cifsFileInfo *open_file;
        struct cifs_tcon *tcon;
        struct cifs_sb_info *cifs_sb;
@@ -2401,14 +2435,16 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
        int rc;
        pid_t pid;
 
-       len = iov_length(iov, nr_segs);
-       if (!len)
-               return 0;
-
+       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;
@@ -2424,7 +2460,6 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
        else
                pid = current->tgid;
 
-       iov_iter_init(&it, iov, nr_segs, len, 0);
        do {
                size_t save_len;
 
@@ -2444,11 +2479,10 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
 
                save_len = cur_len;
                for (i = 0; i < nr_pages; i++) {
-                       bytes = min_t(const size_t, cur_len, PAGE_SIZE);
-                       copied = iov_iter_copy_from_user(wdata->pages[i], &it,
-                                                        0, bytes);
+                       bytes = min_t(size_t, cur_len, PAGE_SIZE);
+                       copied = copy_page_from_iter(wdata->pages[i], 0, bytes,
+                                                    from);
                        cur_len -= copied;
-                       iov_iter_advance(&it, copied);
                        /*
                         * If we didn't copy as much as we expected, then that
                         * may mean we trod into an unmapped area. Stop copying
@@ -2546,11 +2580,11 @@ restart_loop:
        return total_written ? total_written : (ssize_t)rc;
 }
 
-ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov,
-                               unsigned long nr_segs, loff_t pos)
+ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from)
 {
        ssize_t written;
        struct inode *inode;
+       loff_t pos = iocb->ki_pos;
 
        inode = file_inode(iocb->ki_filp);
 
@@ -2560,7 +2594,7 @@ ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov,
         * write request.
         */
 
-       written = cifs_iovec_write(iocb->ki_filp, iov, nr_segs, &pos);
+       written = cifs_iovec_write(iocb->ki_filp, from, &pos);
        if (written > 0) {
                set_bit(CIFS_INO_INVALID_MAPPING, &CIFS_I(inode)->flags);
                iocb->ki_pos = pos;
@@ -2570,8 +2604,7 @@ ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov,
 }
 
 static ssize_t
-cifs_writev(struct kiocb *iocb, const struct iovec *iov,
-           unsigned long nr_segs, loff_t pos)
+cifs_writev(struct kiocb *iocb, struct iov_iter *from)
 {
        struct file *file = iocb->ki_filp;
        struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data;
@@ -2589,10 +2622,10 @@ cifs_writev(struct kiocb *iocb, const struct iovec *iov,
        mutex_lock(&inode->i_mutex);
        if (file->f_flags & O_APPEND)
                lock_pos = i_size_read(inode);
-       if (!cifs_find_lock_conflict(cfile, lock_pos, iov_length(iov, nr_segs),
+       if (!cifs_find_lock_conflict(cfile, lock_pos, iov_iter_count(from),
                                     server->vals->exclusive_lock_type, NULL,
                                     CIFS_WRITE_OP)) {
-               rc = __generic_file_aio_write(iocb, iov, nr_segs);
+               rc = __generic_file_write_iter(iocb, from);
                mutex_unlock(&inode->i_mutex);
 
                if (rc > 0) {
@@ -2610,8 +2643,7 @@ cifs_writev(struct kiocb *iocb, const struct iovec *iov,
 }
 
 ssize_t
-cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
-                  unsigned long nr_segs, loff_t pos)
+cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from)
 {
        struct inode *inode = file_inode(iocb->ki_filp);
        struct cifsInodeInfo *cinode = CIFS_I(inode);
@@ -2629,11 +2661,10 @@ cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
                if (cap_unix(tcon->ses) &&
                (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability))
                  && ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) {
-                       written = generic_file_aio_write(
-                                       iocb, iov, nr_segs, pos);
+                       written = generic_file_write_iter(iocb, from);
                        goto out;
                }
-               written = cifs_writev(iocb, iov, nr_segs, pos);
+               written = cifs_writev(iocb, from);
                goto out;
        }
        /*
@@ -2642,7 +2673,7 @@ cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
         * affected pages because it may cause a error with mandatory locks on
         * these pages but not on the region from pos to ppos+len-1.
         */
-       written = cifs_user_writev(iocb, iov, nr_segs, pos);
+       written = cifs_user_writev(iocb, from);
        if (written > 0 && CIFS_CACHE_READ(cinode)) {
                /*
                 * Windows 7 server can delay breaking level2 oplock if a write
@@ -2828,35 +2859,28 @@ cifs_uncached_read_into_pages(struct TCP_Server_Info *server,
                total_read += result;
        }
 
-       return total_read > 0 ? total_read : result;
+       return total_read > 0 && result != -EAGAIN ? total_read : result;
 }
 
-ssize_t cifs_user_readv(struct kiocb *iocb, const struct iovec *iov,
-                              unsigned long nr_segs, loff_t pos)
+ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to)
 {
        struct file *file = iocb->ki_filp;
        ssize_t rc;
        size_t len, cur_len;
        ssize_t total_read = 0;
-       loff_t offset = pos;
+       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 iov_iter to;
        pid_t pid;
 
-       if (!nr_segs)
-               return 0;
-
-       len = iov_length(iov, nr_segs);
+       len = iov_iter_count(to);
        if (!len)
                return 0;
 
-       iov_iter_init(&to, iov, nr_segs, len, 0);
-
        INIT_LIST_HEAD(&rdata_list);
        cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
        open_file = file->private_data;
@@ -2914,7 +2938,7 @@ error:
        if (!list_empty(&rdata_list))
                rc = 0;
 
-       len = iov_iter_count(&to);
+       len = iov_iter_count(to);
        /* the loop below should proceed in the order of increasing offsets */
        list_for_each_entry_safe(rdata, tmp, &rdata_list, list) {
        again:
@@ -2931,7 +2955,7 @@ error:
                                        goto again;
                                }
                        } else {
-                               rc = cifs_readdata_to_iov(rdata, &to);
+                               rc = cifs_readdata_to_iov(rdata, to);
                        }
 
                }
@@ -2939,7 +2963,7 @@ error:
                kref_put(&rdata->refcount, cifs_uncached_readdata_release);
        }
 
-       total_read = len - iov_iter_count(&to);
+       total_read = len - iov_iter_count(to);
 
        cifs_stats_bytes_read(tcon, total_read);
 
@@ -2948,15 +2972,14 @@ error:
                rc = 0;
 
        if (total_read) {
-               iocb->ki_pos = pos + total_read;
+               iocb->ki_pos += total_read;
                return total_read;
        }
        return rc;
 }
 
 ssize_t
-cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov,
-                 unsigned long nr_segs, loff_t pos)
+cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to)
 {
        struct inode *inode = file_inode(iocb->ki_filp);
        struct cifsInodeInfo *cinode = CIFS_I(inode);
@@ -2975,22 +2998,22 @@ cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov,
         * pos+len-1.
         */
        if (!CIFS_CACHE_READ(cinode))
-               return cifs_user_readv(iocb, iov, nr_segs, pos);
+               return cifs_user_readv(iocb, to);
 
        if (cap_unix(tcon->ses) &&
            (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&
            ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0))
-               return generic_file_aio_read(iocb, iov, nr_segs, pos);
+               return generic_file_read_iter(iocb, to);
 
        /*
         * We need to hold the sem to be sure nobody modifies lock list
         * with a brlock that prevents reading.
         */
        down_read(&cinode->lock_sem);
-       if (!cifs_find_lock_conflict(cfile, pos, iov_length(iov, nr_segs),
+       if (!cifs_find_lock_conflict(cfile, iocb->ki_pos, iov_iter_count(to),
                                     tcon->ses->server->vals->shared_lock_type,
                                     NULL, CIFS_READ_OP))
-               rc = generic_file_aio_read(iocb, iov, nr_segs, pos);
+               rc = generic_file_read_iter(iocb, to);
        up_read(&cinode->lock_sem);
        return rc;
 }
@@ -3244,7 +3267,7 @@ cifs_readpages_read_into_pages(struct TCP_Server_Info *server,
                total_read += result;
        }
 
-       return total_read > 0 ? total_read : result;
+       return total_read > 0 && result != -EAGAIN ? total_read : result;
 }
 
 static int cifs_readpages(struct file *file, struct address_space *mapping,
@@ -3703,8 +3726,8 @@ void cifs_oplock_break(struct work_struct *work)
  * Direct IO is not yet supported in the cached mode. 
  */
 static ssize_t
-cifs_direct_io(int rw, struct kiocb *iocb, const struct iovec *iov,
-               loff_t pos, unsigned long nr_segs)
+cifs_direct_io(int rw, struct kiocb *iocb, struct iov_iter *iter,
+               loff_t pos)
 {
         /*
          * FIXME
This page took 0.033477 seconds and 5 git commands to generate.