]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - fs/nfs/write.c
Merge tag 'nfs-for-3.16-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
[karo-tx-linux.git] / fs / nfs / write.c
index e773df207c054034c371fbdfacfd7c0cf1b87b69..3ee5af4e738efb0f014d5b9dbd9963a179de5dc5 100644 (file)
@@ -154,18 +154,78 @@ static void nfs_set_pageerror(struct page *page)
        nfs_zap_mapping(page_file_mapping(page)->host, page_file_mapping(page));
 }
 
+/*
+ * nfs_page_group_search_locked
+ * @head - head request of page group
+ * @page_offset - offset into page
+ *
+ * Search page group with head @head to find a request that contains the
+ * page offset @page_offset.
+ *
+ * Returns a pointer to the first matching nfs request, or NULL if no
+ * match is found.
+ *
+ * Must be called with the page group lock held
+ */
+static struct nfs_page *
+nfs_page_group_search_locked(struct nfs_page *head, unsigned int page_offset)
+{
+       struct nfs_page *req;
+
+       WARN_ON_ONCE(head != head->wb_head);
+       WARN_ON_ONCE(!test_bit(PG_HEADLOCK, &head->wb_head->wb_flags));
+
+       req = head;
+       do {
+               if (page_offset >= req->wb_pgbase &&
+                   page_offset < (req->wb_pgbase + req->wb_bytes))
+                       return req;
+
+               req = req->wb_this_page;
+       } while (req != head);
+
+       return NULL;
+}
+
+/*
+ * nfs_page_group_covers_page
+ * @head - head request of page group
+ *
+ * Return true if the page group with head @head covers the whole page,
+ * returns false otherwise
+ */
+static bool nfs_page_group_covers_page(struct nfs_page *req)
+{
+       struct nfs_page *tmp;
+       unsigned int pos = 0;
+       unsigned int len = nfs_page_length(req->wb_page);
+
+       nfs_page_group_lock(req);
+
+       do {
+               tmp = nfs_page_group_search_locked(req->wb_head, pos);
+               if (tmp) {
+                       /* no way this should happen */
+                       WARN_ON_ONCE(tmp->wb_pgbase != pos);
+                       pos += tmp->wb_bytes - (pos - tmp->wb_pgbase);
+               }
+       } while (tmp && pos < len);
+
+       nfs_page_group_unlock(req);
+       WARN_ON_ONCE(pos > len);
+       return pos == len;
+}
+
 /* We can set the PG_uptodate flag if we see that a write request
  * covers the full page.
  */
-static void nfs_mark_uptodate(struct page *page, unsigned int base, unsigned int count)
+static void nfs_mark_uptodate(struct nfs_page *req)
 {
-       if (PageUptodate(page))
-               return;
-       if (base != 0)
+       if (PageUptodate(req->wb_page))
                return;
-       if (count != nfs_page_length(page))
+       if (!nfs_page_group_covers_page(req))
                return;
-       SetPageUptodate(page);
+       SetPageUptodate(req->wb_page);
 }
 
 static int wb_priority(struct writeback_control *wbc)
@@ -201,12 +261,15 @@ static void nfs_set_page_writeback(struct page *page)
        }
 }
 
-static void nfs_end_page_writeback(struct page *page)
+static void nfs_end_page_writeback(struct nfs_page *req)
 {
-       struct inode *inode = page_file_mapping(page)->host;
+       struct inode *inode = page_file_mapping(req->wb_page)->host;
        struct nfs_server *nfss = NFS_SERVER(inode);
 
-       end_page_writeback(page);
+       if (!nfs_page_group_sync_on_bit(req, PG_WB_END))
+               return;
+
+       end_page_writeback(req->wb_page);
        if (atomic_long_dec_return(&nfss->writeback) < NFS_CONGESTION_OFF_THRESH)
                clear_bdi_congested(&nfss->backing_dev_info, BLK_RW_ASYNC);
 }
@@ -347,7 +410,7 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
        nfs_pageio_complete(&pgio);
 
        clear_bit_unlock(NFS_INO_FLUSHING, bitlock);
-       smp_mb__after_clear_bit();
+       smp_mb__after_atomic();
        wake_up_bit(bitlock, NFS_INO_FLUSHING);
 
        if (err < 0)
@@ -367,6 +430,8 @@ static void nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
 
+       WARN_ON_ONCE(req->wb_this_page != req);
+
        /* Lock the request! */
        nfs_lock_request(req);
 
@@ -383,6 +448,7 @@ static void nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
                set_page_private(req->wb_page, (unsigned long)req);
        }
        nfsi->npages++;
+       set_bit(PG_INODE_REF, &req->wb_flags);
        kref_get(&req->wb_kref);
        spin_unlock(&inode->i_lock);
 }
@@ -394,15 +460,20 @@ static void nfs_inode_remove_request(struct nfs_page *req)
 {
        struct inode *inode = req->wb_context->dentry->d_inode;
        struct nfs_inode *nfsi = NFS_I(inode);
+       struct nfs_page *head;
 
-       spin_lock(&inode->i_lock);
-       if (likely(!PageSwapCache(req->wb_page))) {
-               set_page_private(req->wb_page, 0);
-               ClearPagePrivate(req->wb_page);
-               clear_bit(PG_MAPPED, &req->wb_flags);
+       if (nfs_page_group_sync_on_bit(req, PG_REMOVE)) {
+               head = req->wb_head;
+
+               spin_lock(&inode->i_lock);
+               if (likely(!PageSwapCache(head->wb_page))) {
+                       set_page_private(head->wb_page, 0);
+                       ClearPagePrivate(head->wb_page);
+                       clear_bit(PG_MAPPED, &head->wb_flags);
+               }
+               nfsi->npages--;
+               spin_unlock(&inode->i_lock);
        }
-       nfsi->npages--;
-       spin_unlock(&inode->i_lock);
        nfs_release_request(req);
 }
 
@@ -567,6 +638,7 @@ static void nfs_write_completion(struct nfs_pgio_header *hdr)
 {
        struct nfs_commit_info cinfo;
        unsigned long bytes = 0;
+       bool do_destroy;
 
        if (test_bit(NFS_IOHDR_REDO, &hdr->flags))
                goto out;
@@ -595,7 +667,8 @@ remove_req:
                nfs_inode_remove_request(req);
 next:
                nfs_unlock_request(req);
-               nfs_end_page_writeback(req->wb_page);
+               nfs_end_page_writeback(req);
+               do_destroy = !test_bit(NFS_IOHDR_NEED_COMMIT, &hdr->flags);
                nfs_release_request(req);
        }
 out:
@@ -700,6 +773,10 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode,
                if (req == NULL)
                        goto out_unlock;
 
+               /* should be handled by nfs_flush_incompatible */
+               WARN_ON_ONCE(req->wb_head != req);
+               WARN_ON_ONCE(req->wb_this_page != req);
+
                rqend = req->wb_offset + req->wb_bytes;
                /*
                 * Tell the caller to flush out the request if
@@ -761,7 +838,7 @@ static struct nfs_page * nfs_setup_write_request(struct nfs_open_context* ctx,
        req = nfs_try_to_update_request(inode, page, offset, bytes);
        if (req != NULL)
                goto out;
-       req = nfs_create_request(ctx, page, offset, bytes);
+       req = nfs_create_request(ctx, page, NULL, offset, bytes);
        if (IS_ERR(req))
                goto out;
        nfs_inode_add_request(inode, req);
@@ -779,7 +856,7 @@ static int nfs_writepage_setup(struct nfs_open_context *ctx, struct page *page,
                return PTR_ERR(req);
        /* Update file length */
        nfs_grow_file(page, offset, count);
-       nfs_mark_uptodate(page, req->wb_pgbase, req->wb_bytes);
+       nfs_mark_uptodate(req);
        nfs_mark_request_dirty(req);
        nfs_unlock_and_release_request(req);
        return 0;
@@ -805,6 +882,8 @@ int nfs_flush_incompatible(struct file *file, struct page *page)
                        return 0;
                l_ctx = req->wb_lock_context;
                do_flush = req->wb_page != page || req->wb_context != ctx;
+               /* for now, flush if more than 1 request in page_group */
+               do_flush |= req->wb_this_page != req;
                if (l_ctx && ctx->dentry->d_inode->i_flock != NULL) {
                        do_flush |= l_ctx->lockowner.l_owner != current->files
                                || l_ctx->lockowner.l_pid != current->tgid;
@@ -953,7 +1032,7 @@ static void nfs_redirty_request(struct nfs_page *req)
 {
        nfs_mark_request_dirty(req);
        nfs_unlock_request(req);
-       nfs_end_page_writeback(req->wb_page);
+       nfs_end_page_writeback(req);
        nfs_release_request(req);
 }
 
@@ -1158,7 +1237,7 @@ static int nfs_commit_set_lock(struct nfs_inode *nfsi, int may_wait)
 static void nfs_commit_clear_lock(struct nfs_inode *nfsi)
 {
        clear_bit(NFS_INO_COMMIT, &nfsi->flags);
-       smp_mb__after_clear_bit();
+       smp_mb__after_atomic();
        wake_up_bit(&nfsi->flags, NFS_INO_COMMIT);
 }