Merge branch 'task_killable' of git://git.kernel.org/pub/scm/linux/kernel/git/willy...
[pandora-kernel.git] / fs / nfs / write.c
index 0cf9d1c..522efff 100644 (file)
@@ -174,8 +174,6 @@ static void nfs_mark_uptodate(struct page *page, unsigned int base, unsigned int
                return;
        if (count != nfs_page_length(page))
                return;
-       if (count != PAGE_CACHE_SIZE)
-               zero_user_page(page, count, PAGE_CACHE_SIZE - count, KM_USER0);
        SetPageUptodate(page);
 }
 
@@ -198,7 +196,7 @@ static int nfs_writepage_setup(struct nfs_open_context *ctx, struct page *page,
        }
        /* Update file length */
        nfs_grow_file(page, offset, count);
-       nfs_unlock_request(req);
+       nfs_clear_page_tag_locked(req);
        return 0;
 }
 
@@ -254,7 +252,6 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
                                struct page *page)
 {
        struct inode *inode = page->mapping->host;
-       struct nfs_inode *nfsi = NFS_I(inode);
        struct nfs_page *req;
        int ret;
 
@@ -265,10 +262,10 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
                        spin_unlock(&inode->i_lock);
                        return 0;
                }
-               if (nfs_lock_request_dontget(req))
+               if (nfs_set_page_tag_locked(req))
                        break;
                /* Note: If we hold the page lock, as is the case in nfs_writepage,
-                *       then the call to nfs_lock_request_dontget() will always
+                *       then the call to nfs_set_page_tag_locked() will always
                 *       succeed provided that someone hasn't already marked the
                 *       request as dirty (in which case we don't care).
                 */
@@ -282,7 +279,7 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
        if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) {
                /* This request is marked for commit */
                spin_unlock(&inode->i_lock);
-               nfs_unlock_request(req);
+               nfs_clear_page_tag_locked(req);
                nfs_pageio_complete(pgio);
                return 0;
        }
@@ -290,8 +287,6 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
                spin_unlock(&inode->i_lock);
                BUG();
        }
-       radix_tree_tag_set(&nfsi->nfs_page_tree, req->wb_index,
-                       NFS_PAGE_TAG_LOCKED);
        spin_unlock(&inode->i_lock);
        nfs_pageio_add_request(pgio, req);
        return 0;
@@ -383,6 +378,7 @@ static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
        set_page_private(req->wb_page, (unsigned long)req);
        nfsi->npages++;
        kref_get(&req->wb_kref);
+       radix_tree_tag_set(&nfsi->nfs_page_tree, req->wb_index, NFS_PAGE_TAG_LOCKED);
        return 0;
 }
 
@@ -492,7 +488,7 @@ int nfs_reschedule_unstable_write(struct nfs_page *req)
 /*
  * Wait for a request to complete.
  *
- * Interruptible by signals only if mounted with intr flag.
+ * Interruptible by fatal signals only.
  */
 static int nfs_wait_on_requests_locked(struct inode *inode, pgoff_t idx_start, unsigned int npages)
 {
@@ -598,7 +594,7 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx,
                spin_lock(&inode->i_lock);
                req = nfs_page_find_request_locked(page);
                if (req) {
-                       if (!nfs_lock_request_dontget(req)) {
+                       if (!nfs_set_page_tag_locked(req)) {
                                int error;
 
                                spin_unlock(&inode->i_lock);
@@ -627,7 +623,8 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx,
                                return ERR_PTR(error);
                        }
                        spin_unlock(&inode->i_lock);
-                       return new;
+                       req = new;
+                       goto zero_page;
                }
                spin_unlock(&inode->i_lock);
 
@@ -647,7 +644,7 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx,
            || req->wb_page != page
            || !nfs_dirty_request(req)
            || offset > rqend || end < req->wb_offset) {
-               nfs_unlock_request(req);
+               nfs_clear_page_tag_locked(req);
                return ERR_PTR(-EBUSY);
        }
 
@@ -655,13 +652,23 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx,
        if (offset < req->wb_offset) {
                req->wb_offset = offset;
                req->wb_pgbase = offset;
-               req->wb_bytes = rqend - req->wb_offset;
+               req->wb_bytes = max(end, rqend) - req->wb_offset;
+               goto zero_page;
        }
 
        if (end > rqend)
                req->wb_bytes = end - req->wb_offset;
 
        return req;
+zero_page:
+       /* If this page might potentially be marked as up to date,
+        * then we need to zero any uninitalised data. */
+       if (req->wb_pgbase == 0 && req->wb_bytes != PAGE_CACHE_SIZE
+                       && !PageUptodate(req->wb_page))
+               zero_user_page(req->wb_page, req->wb_bytes,
+                               PAGE_CACHE_SIZE - req->wb_bytes,
+                               KM_USER0);
+       return req;
 }
 
 int nfs_flush_incompatible(struct file *file, struct page *page)
@@ -746,7 +753,7 @@ static void nfs_writepage_release(struct nfs_page *req)
        nfs_clear_page_tag_locked(req);
 }
 
-static inline int flush_task_priority(int how)
+static int flush_task_priority(int how)
 {
        switch (how & (FLUSH_HIGHPRI|FLUSH_LOWPRI)) {
                case FLUSH_HIGHPRI:
@@ -766,15 +773,31 @@ static void nfs_write_rpcsetup(struct nfs_page *req,
                unsigned int count, unsigned int offset,
                int how)
 {
-       struct inode            *inode;
-       int flags;
+       struct inode *inode = req->wb_context->path.dentry->d_inode;
+       int flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC;
+       int priority = flush_task_priority(how);
+       struct rpc_task *task;
+       struct rpc_message msg = {
+               .rpc_argp = &data->args,
+               .rpc_resp = &data->res,
+               .rpc_cred = req->wb_context->cred,
+       };
+       struct rpc_task_setup task_setup_data = {
+               .rpc_client = NFS_CLIENT(inode),
+               .task = &data->task,
+               .rpc_message = &msg,
+               .callback_ops = call_ops,
+               .callback_data = data,
+               .flags = flags,
+               .priority = priority,
+       };
 
        /* Set up the RPC argument and reply structs
         * NB: take care not to mess about with data->commit et al. */
 
        data->req = req;
        data->inode = inode = req->wb_context->path.dentry->d_inode;
-       data->cred = req->wb_context->cred;
+       data->cred = msg.rpc_cred;
 
        data->args.fh     = NFS_FH(inode);
        data->args.offset = req_offset(req) + offset;
@@ -782,6 +805,12 @@ static void nfs_write_rpcsetup(struct nfs_page *req,
        data->args.pages  = data->pagevec;
        data->args.count  = count;
        data->args.context = req->wb_context;
+       data->args.stable  = NFS_UNSTABLE;
+       if (how & FLUSH_STABLE) {
+               data->args.stable = NFS_DATA_SYNC;
+               if (!NFS_I(inode)->ncommit)
+                       data->args.stable = NFS_FILE_SYNC;
+       }
 
        data->res.fattr   = &data->fattr;
        data->res.count   = count;
@@ -789,12 +818,7 @@ static void nfs_write_rpcsetup(struct nfs_page *req,
        nfs_fattr_init(&data->fattr);
 
        /* Set up the initial task struct.  */
-       flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC;
-       rpc_init_task(&data->task, NFS_CLIENT(inode), flags, call_ops, data);
-       NFS_PROTO(inode)->write_setup(data, how);
-
-       data->task.tk_priority = flush_task_priority(how);
-       data->task.tk_cookie = (unsigned long)inode;
+       NFS_PROTO(inode)->write_setup(data, &msg);
 
        dprintk("NFS: %5u initiated write call "
                "(req %s/%Ld, %u bytes @ offset %Lu)\n",
@@ -803,16 +827,10 @@ static void nfs_write_rpcsetup(struct nfs_page *req,
                (long long)NFS_FILEID(inode),
                count,
                (unsigned long long)data->args.offset);
-}
 
-static void nfs_execute_write(struct nfs_write_data *data)
-{
-       struct rpc_clnt *clnt = NFS_CLIENT(data->inode);
-       sigset_t oldset;
-
-       rpc_clnt_sigmask(clnt, &oldset);
-       rpc_execute(&data->task);
-       rpc_clnt_sigunmask(clnt, &oldset);
+       task = rpc_run_task(&task_setup_data);
+       if (!IS_ERR(task))
+               rpc_put_task(task);
 }
 
 /*
@@ -859,7 +877,6 @@ static int nfs_flush_multi(struct inode *inode, struct list_head *head, unsigned
                                   wsize, offset, how);
                offset += wsize;
                nbytes -= wsize;
-               nfs_execute_write(data);
        } while (nbytes != 0);
 
        return 0;
@@ -907,7 +924,6 @@ static int nfs_flush_one(struct inode *inode, struct list_head *head, unsigned i
        /* Set up the argument struct */
        nfs_write_rpcsetup(req, data, &nfs_write_full_ops, count, 0, how);
 
-       nfs_execute_write(data);
        return 0;
  out_bad:
        while (!list_empty(head)) {
@@ -923,7 +939,7 @@ static int nfs_flush_one(struct inode *inode, struct list_head *head, unsigned i
 static void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
                                  struct inode *inode, int ioflags)
 {
-       int wsize = NFS_SERVER(inode)->wsize;
+       size_t wsize = NFS_SERVER(inode)->wsize;
 
        if (wsize < PAGE_CACHE_SIZE)
                nfs_pageio_init(pgio, inode, nfs_flush_multi, wsize, ioflags);
@@ -1137,19 +1153,33 @@ static void nfs_commit_rpcsetup(struct list_head *head,
                struct nfs_write_data *data,
                int how)
 {
-       struct nfs_page         *first;
-       struct inode            *inode;
-       int flags;
+       struct nfs_page *first = nfs_list_entry(head->next);
+       struct inode *inode = first->wb_context->path.dentry->d_inode;
+       int flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC;
+       int priority = flush_task_priority(how);
+       struct rpc_task *task;
+       struct rpc_message msg = {
+               .rpc_argp = &data->args,
+               .rpc_resp = &data->res,
+               .rpc_cred = first->wb_context->cred,
+       };
+       struct rpc_task_setup task_setup_data = {
+               .task = &data->task,
+               .rpc_client = NFS_CLIENT(inode),
+               .rpc_message = &msg,
+               .callback_ops = &nfs_commit_ops,
+               .callback_data = data,
+               .flags = flags,
+               .priority = priority,
+       };
 
        /* Set up the RPC argument and reply structs
         * NB: take care not to mess about with data->commit et al. */
 
        list_splice_init(head, &data->pages);
-       first = nfs_list_entry(data->pages.next);
-       inode = first->wb_context->path.dentry->d_inode;
 
        data->inode       = inode;
-       data->cred        = first->wb_context->cred;
+       data->cred        = msg.rpc_cred;
 
        data->args.fh     = NFS_FH(data->inode);
        /* Note: we always request a commit of the entire inode */
@@ -1161,14 +1191,13 @@ static void nfs_commit_rpcsetup(struct list_head *head,
        nfs_fattr_init(&data->fattr);
 
        /* Set up the initial task struct.  */
-       flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC;
-       rpc_init_task(&data->task, NFS_CLIENT(inode), flags, &nfs_commit_ops, data);
-       NFS_PROTO(inode)->commit_setup(data, how);
+       NFS_PROTO(inode)->commit_setup(data, &msg);
 
-       data->task.tk_priority = flush_task_priority(how);
-       data->task.tk_cookie = (unsigned long)inode;
-       
        dprintk("NFS: %5u initiated commit call\n", data->task.tk_pid);
+
+       task = rpc_run_task(&task_setup_data);
+       if (!IS_ERR(task))
+               rpc_put_task(task);
 }
 
 /*
@@ -1188,7 +1217,6 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how)
        /* Set up the argument struct */
        nfs_commit_rpcsetup(head, data, how);
 
-       nfs_execute_write(data);
        return 0;
  out_bad:
        while (!list_empty(head)) {
@@ -1427,7 +1455,8 @@ out:
        return ret;
 }
 
-int nfs_wb_page_priority(struct inode *inode, struct page *page, int how)
+static int nfs_wb_page_priority(struct inode *inode, struct page *page,
+                               int how)
 {
        loff_t range_start = page_offset(page);
        loff_t range_end = range_start + (loff_t)(PAGE_CACHE_SIZE - 1);