Merge branch 'for-3.20/bdi' of git://git.kernel.dk/linux-block
[pandora-kernel.git] / fs / nfs / write.c
index 298abcc..88a6d21 100644 (file)
@@ -473,13 +473,18 @@ try_again:
        do {
                /*
                 * Subrequests are always contiguous, non overlapping
-                * and in order. If not, it's a programming error.
+                * and in order - but may be repeated (mirrored writes).
                 */
-               WARN_ON_ONCE(subreq->wb_offset !=
-                    (head->wb_offset + total_bytes));
-
-               /* keep track of how many bytes this group covers */
-               total_bytes += subreq->wb_bytes;
+               if (subreq->wb_offset == (head->wb_offset + total_bytes)) {
+                       /* keep track of how many bytes this group covers */
+                       total_bytes += subreq->wb_bytes;
+               } else if (WARN_ON_ONCE(subreq->wb_offset < head->wb_offset ||
+                           ((subreq->wb_offset + subreq->wb_bytes) >
+                            (head->wb_offset + total_bytes)))) {
+                       nfs_page_group_unlock(head);
+                       spin_unlock(&inode->i_lock);
+                       return ERR_PTR(-EIO);
+               }
 
                if (!nfs_lock_request(subreq)) {
                        /* releases page group bit lock and
@@ -842,9 +847,9 @@ EXPORT_SYMBOL_GPL(nfs_init_cinfo);
  */
 void
 nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg,
-                       struct nfs_commit_info *cinfo)
+                       struct nfs_commit_info *cinfo, u32 ds_commit_idx)
 {
-       if (pnfs_mark_request_commit(req, lseg, cinfo))
+       if (pnfs_mark_request_commit(req, lseg, cinfo, ds_commit_idx))
                return;
        nfs_request_add_commit_list(req, &cinfo->mds->list, cinfo);
 }
@@ -900,7 +905,8 @@ static void nfs_write_completion(struct nfs_pgio_header *hdr)
                }
                if (nfs_write_need_commit(hdr)) {
                        memcpy(&req->wb_verf, &hdr->verf.verifier, sizeof(req->wb_verf));
-                       nfs_mark_request_commit(req, hdr->lseg, &cinfo);
+                       nfs_mark_request_commit(req, hdr->lseg, &cinfo,
+                               hdr->pgio_mirror_idx);
                        goto next;
                }
 remove_req:
@@ -1091,6 +1097,7 @@ int nfs_flush_incompatible(struct file *file, struct page *page)
 {
        struct nfs_open_context *ctx = nfs_file_open_context(file);
        struct nfs_lock_context *l_ctx;
+       struct file_lock_context *flctx = file_inode(file)->i_flctx;
        struct nfs_page *req;
        int do_flush, status;
        /*
@@ -1109,7 +1116,9 @@ int nfs_flush_incompatible(struct file *file, struct page *page)
                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) {
+               if (l_ctx && flctx &&
+                   !(list_empty_careful(&flctx->flc_posix) &&
+                     list_empty_careful(&flctx->flc_flock))) {
                        do_flush |= l_ctx->lockowner.l_owner != current->files
                                || l_ctx->lockowner.l_pid != current->tgid;
                }
@@ -1170,6 +1179,13 @@ out:
        return PageUptodate(page) != 0;
 }
 
+static bool
+is_whole_file_wrlock(struct file_lock *fl)
+{
+       return fl->fl_start == 0 && fl->fl_end == OFFSET_MAX &&
+                       fl->fl_type == F_WRLCK;
+}
+
 /* If we know the page is up to date, and we're not using byte range locks (or
  * if we have the whole file locked for writing), it may be more efficient to
  * extend the write to cover the entire page in order to avoid fragmentation
@@ -1180,17 +1196,36 @@ out:
  */
 static int nfs_can_extend_write(struct file *file, struct page *page, struct inode *inode)
 {
+       int ret;
+       struct file_lock_context *flctx = inode->i_flctx;
+       struct file_lock *fl;
+
        if (file->f_flags & O_DSYNC)
                return 0;
        if (!nfs_write_pageuptodate(page, inode))
                return 0;
        if (NFS_PROTO(inode)->have_delegation(inode, FMODE_WRITE))
                return 1;
-       if (inode->i_flock == NULL || (inode->i_flock->fl_start == 0 &&
-                       inode->i_flock->fl_end == OFFSET_MAX &&
-                       inode->i_flock->fl_type != F_RDLCK))
-               return 1;
-       return 0;
+       if (!flctx || (list_empty_careful(&flctx->flc_flock) &&
+                      list_empty_careful(&flctx->flc_posix)))
+               return 0;
+
+       /* Check to see if there are whole file write locks */
+       ret = 0;
+       spin_lock(&flctx->flc_lock);
+       if (!list_empty(&flctx->flc_posix)) {
+               fl = list_first_entry(&flctx->flc_posix, struct file_lock,
+                                       fl_list);
+               if (is_whole_file_wrlock(fl))
+                       ret = 1;
+       } else if (!list_empty(&flctx->flc_flock)) {
+               fl = list_first_entry(&flctx->flc_flock, struct file_lock,
+                                       fl_list);
+               if (fl->fl_type == F_WRLCK)
+                       ret = 1;
+       }
+       spin_unlock(&flctx->flc_lock);
+       return ret;
 }
 
 /*
@@ -1240,15 +1275,15 @@ static int flush_task_priority(int how)
 
 static void nfs_initiate_write(struct nfs_pgio_header *hdr,
                               struct rpc_message *msg,
+                              const struct nfs_rpc_ops *rpc_ops,
                               struct rpc_task_setup *task_setup_data, int how)
 {
-       struct inode *inode = hdr->inode;
        int priority = flush_task_priority(how);
 
        task_setup_data->priority = priority;
-       NFS_PROTO(inode)->write_setup(hdr, msg);
+       rpc_ops->write_setup(hdr, msg);
 
-       nfs4_state_protect_write(NFS_SERVER(inode)->nfs_client,
+       nfs4_state_protect_write(NFS_SERVER(hdr->inode)->nfs_client,
                                 &task_setup_data->rpc_client, msg, hdr);
 }
 
@@ -1298,8 +1333,14 @@ EXPORT_SYMBOL_GPL(nfs_pageio_init_write);
 
 void nfs_pageio_reset_write_mds(struct nfs_pageio_descriptor *pgio)
 {
+       struct nfs_pgio_mirror *mirror;
+
        pgio->pg_ops = &nfs_pgio_rw_ops;
-       pgio->pg_bsize = NFS_SERVER(pgio->pg_inode)->wsize;
+
+       nfs_pageio_stop_mirroring(pgio);
+
+       mirror = &pgio->pg_mirrors[0];
+       mirror->pg_bsize = NFS_SERVER(pgio->pg_inode)->wsize;
 }
 EXPORT_SYMBOL_GPL(nfs_pageio_reset_write_mds);
 
@@ -1465,6 +1506,7 @@ void nfs_commitdata_release(struct nfs_commit_data *data)
 EXPORT_SYMBOL_GPL(nfs_commitdata_release);
 
 int nfs_initiate_commit(struct rpc_clnt *clnt, struct nfs_commit_data *data,
+                       const struct nfs_rpc_ops *nfs_ops,
                        const struct rpc_call_ops *call_ops,
                        int how, int flags)
 {
@@ -1486,7 +1528,7 @@ int nfs_initiate_commit(struct rpc_clnt *clnt, struct nfs_commit_data *data,
                .priority = priority,
        };
        /* Set up the initial task struct.  */
-       NFS_PROTO(data->inode)->commit_setup(data, &msg);
+       nfs_ops->commit_setup(data, &msg);
 
        dprintk("NFS: %5u initiated commit call\n", data->task.tk_pid);
 
@@ -1554,14 +1596,15 @@ EXPORT_SYMBOL_GPL(nfs_init_commit);
 
 void nfs_retry_commit(struct list_head *page_list,
                      struct pnfs_layout_segment *lseg,
-                     struct nfs_commit_info *cinfo)
+                     struct nfs_commit_info *cinfo,
+                     u32 ds_commit_idx)
 {
        struct nfs_page *req;
 
        while (!list_empty(page_list)) {
                req = nfs_list_entry(page_list->next);
                nfs_list_remove_request(req);
-               nfs_mark_request_commit(req, lseg, cinfo);
+               nfs_mark_request_commit(req, lseg, cinfo, ds_commit_idx);
                if (!cinfo->dreq) {
                        dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
                        dec_bdi_stat(inode_to_bdi(page_file_mapping(req->wb_page)->host),
@@ -1589,10 +1632,10 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how,
        /* Set up the argument struct */
        nfs_init_commit(data, head, NULL, cinfo);
        atomic_inc(&cinfo->mds->rpcs_out);
-       return nfs_initiate_commit(NFS_CLIENT(inode), data, data->mds_ops,
-                                  how, 0);
+       return nfs_initiate_commit(NFS_CLIENT(inode), data, NFS_PROTO(inode),
+                                  data->mds_ops, how, 0);
  out_bad:
-       nfs_retry_commit(head, NULL, cinfo);
+       nfs_retry_commit(head, NULL, cinfo, 0);
        cinfo->completion_ops->error_cleanup(NFS_I(inode));
        return -ENOMEM;
 }