Merge branch 'for-next' of git://git.samba.org/sfrench/cifs-2.6
[cascardo/linux.git] / fs / nfs / nfs4filelayout.c
index fe2cb55..e134029 100644 (file)
@@ -82,29 +82,76 @@ filelayout_get_dserver_offset(struct pnfs_layout_segment *lseg, loff_t offset)
        BUG();
 }
 
+static void filelayout_reset_write(struct nfs_write_data *data)
+{
+       struct nfs_pgio_header *hdr = data->header;
+       struct rpc_task *task = &data->task;
+
+       if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) {
+               dprintk("%s Reset task %5u for i/o through MDS "
+                       "(req %s/%lld, %u bytes @ offset %llu)\n", __func__,
+                       data->task.tk_pid,
+                       hdr->inode->i_sb->s_id,
+                       (long long)NFS_FILEID(hdr->inode),
+                       data->args.count,
+                       (unsigned long long)data->args.offset);
+
+               task->tk_status = pnfs_write_done_resend_to_mds(hdr->inode,
+                                                       &hdr->pages,
+                                                       hdr->completion_ops);
+       }
+}
+
+static void filelayout_reset_read(struct nfs_read_data *data)
+{
+       struct nfs_pgio_header *hdr = data->header;
+       struct rpc_task *task = &data->task;
+
+       if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) {
+               dprintk("%s Reset task %5u for i/o through MDS "
+                       "(req %s/%lld, %u bytes @ offset %llu)\n", __func__,
+                       data->task.tk_pid,
+                       hdr->inode->i_sb->s_id,
+                       (long long)NFS_FILEID(hdr->inode),
+                       data->args.count,
+                       (unsigned long long)data->args.offset);
+
+               task->tk_status = pnfs_read_done_resend_to_mds(hdr->inode,
+                                                       &hdr->pages,
+                                                       hdr->completion_ops);
+       }
+}
+
 static int filelayout_async_handle_error(struct rpc_task *task,
                                         struct nfs4_state *state,
                                         struct nfs_client *clp,
-                                        int *reset)
+                                        struct pnfs_layout_segment *lseg)
 {
-       struct nfs_server *mds_server = NFS_SERVER(state->inode);
+       struct inode *inode = lseg->pls_layout->plh_inode;
+       struct nfs_server *mds_server = NFS_SERVER(inode);
+       struct nfs4_deviceid_node *devid = FILELAYOUT_DEVID_NODE(lseg);
        struct nfs_client *mds_client = mds_server->nfs_client;
+       struct nfs4_slot_table *tbl = &clp->cl_session->fc_slot_table;
 
        if (task->tk_status >= 0)
                return 0;
-       *reset = 0;
 
        switch (task->tk_status) {
        /* MDS state errors */
        case -NFS4ERR_DELEG_REVOKED:
        case -NFS4ERR_ADMIN_REVOKED:
        case -NFS4ERR_BAD_STATEID:
+               if (state == NULL)
+                       break;
                nfs_remove_bad_delegation(state->inode);
        case -NFS4ERR_OPENMODE:
+               if (state == NULL)
+                       break;
                nfs4_schedule_stateid_recovery(mds_server, state);
                goto wait_on_recovery;
        case -NFS4ERR_EXPIRED:
-               nfs4_schedule_stateid_recovery(mds_server, state);
+               if (state != NULL)
+                       nfs4_schedule_stateid_recovery(mds_server, state);
                nfs4_schedule_lease_recovery(mds_client);
                goto wait_on_recovery;
        /* DS session errors */
@@ -118,7 +165,7 @@ static int filelayout_async_handle_error(struct rpc_task *task,
                dprintk("%s ERROR %d, Reset session. Exchangeid "
                        "flags 0x%x\n", __func__, task->tk_status,
                        clp->cl_exchange_flags);
-               nfs4_schedule_session_recovery(clp->cl_session);
+               nfs4_schedule_session_recovery(clp->cl_session, task->tk_status);
                break;
        case -NFS4ERR_DELAY:
        case -NFS4ERR_GRACE:
@@ -127,11 +174,48 @@ static int filelayout_async_handle_error(struct rpc_task *task,
                break;
        case -NFS4ERR_RETRY_UNCACHED_REP:
                break;
+       /* Invalidate Layout errors */
+       case -NFS4ERR_PNFS_NO_LAYOUT:
+       case -ESTALE:           /* mapped NFS4ERR_STALE */
+       case -EBADHANDLE:       /* mapped NFS4ERR_BADHANDLE */
+       case -EISDIR:           /* mapped NFS4ERR_ISDIR */
+       case -NFS4ERR_FHEXPIRED:
+       case -NFS4ERR_WRONG_TYPE:
+               dprintk("%s Invalid layout error %d\n", __func__,
+                       task->tk_status);
+               /*
+                * Destroy layout so new i/o will get a new layout.
+                * Layout will not be destroyed until all current lseg
+                * references are put. Mark layout as invalid to resend failed
+                * i/o and all i/o waiting on the slot table to the MDS until
+                * layout is destroyed and a new valid layout is obtained.
+                */
+               set_bit(NFS_LAYOUT_INVALID,
+                               &NFS_I(inode)->layout->plh_flags);
+               pnfs_destroy_layout(NFS_I(inode));
+               rpc_wake_up(&tbl->slot_tbl_waitq);
+               goto reset;
+       /* RPC connection errors */
+       case -ECONNREFUSED:
+       case -EHOSTDOWN:
+       case -EHOSTUNREACH:
+       case -ENETUNREACH:
+       case -EIO:
+       case -ETIMEDOUT:
+       case -EPIPE:
+               dprintk("%s DS connection error %d\n", __func__,
+                       task->tk_status);
+               if (!filelayout_test_devid_invalid(devid))
+                       _pnfs_return_layout(inode);
+               filelayout_mark_devid_invalid(devid);
+               rpc_wake_up(&tbl->slot_tbl_waitq);
+               nfs4_ds_disconnect(clp);
+               /* fall through */
        default:
-               dprintk("%s DS error. Retry through MDS %d\n", __func__,
+reset:
+               dprintk("%s Retry through MDS. Error %d\n", __func__,
                        task->tk_status);
-               *reset = 1;
-               break;
+               return -NFS4ERR_RESET_TO_MDS;
        }
 out:
        task->tk_status = 0;
@@ -149,18 +233,16 @@ static int filelayout_read_done_cb(struct rpc_task *task,
                                struct nfs_read_data *data)
 {
        struct nfs_pgio_header *hdr = data->header;
-       int reset = 0;
+       int err;
 
-       dprintk("%s DS read\n", __func__);
+       err = filelayout_async_handle_error(task, data->args.context->state,
+                                           data->ds_clp, hdr->lseg);
 
-       if (filelayout_async_handle_error(task, data->args.context->state,
-                                         data->ds_clp, &reset) == -EAGAIN) {
-               dprintk("%s calling restart ds_clp %p ds_clp->cl_session %p\n",
-                       __func__, data->ds_clp, data->ds_clp->cl_session);
-               if (reset) {
-                       pnfs_set_lo_fail(hdr->lseg);
-                       nfs4_reset_read(task, data);
-               }
+       switch (err) {
+       case -NFS4ERR_RESET_TO_MDS:
+               filelayout_reset_read(data);
+               return task->tk_status;
+       case -EAGAIN:
                rpc_restart_call_prepare(task);
                return -EAGAIN;
        }
@@ -196,6 +278,12 @@ static void filelayout_read_prepare(struct rpc_task *task, void *data)
 {
        struct nfs_read_data *rdata = data;
 
+       if (filelayout_reset_to_mds(rdata->header->lseg)) {
+               dprintk("%s task %u reset io to MDS\n", __func__, task->tk_pid);
+               filelayout_reset_read(rdata);
+               rpc_exit(task, 0);
+               return;
+       }
        rdata->read_done_cb = filelayout_read_done_cb;
 
        if (nfs41_setup_sequence(rdata->ds_clp->cl_session,
@@ -212,6 +300,10 @@ static void filelayout_read_call_done(struct rpc_task *task, void *data)
 
        dprintk("--> %s task->tk_status %d\n", __func__, task->tk_status);
 
+       if (test_bit(NFS_IOHDR_REDO, &rdata->header->flags) &&
+           task->tk_status == 0)
+               return;
+
        /* Note this may cause RPC to be resent */
        rdata->header->mds_ops->rpc_call_done(task, data);
 }
@@ -227,6 +319,7 @@ static void filelayout_read_release(void *data)
 {
        struct nfs_read_data *rdata = data;
 
+       nfs_put_client(rdata->ds_clp);
        rdata->header->mds_ops->rpc_release(data);
 }
 
@@ -234,16 +327,16 @@ static int filelayout_write_done_cb(struct rpc_task *task,
                                struct nfs_write_data *data)
 {
        struct nfs_pgio_header *hdr = data->header;
-       int reset = 0;
-
-       if (filelayout_async_handle_error(task, data->args.context->state,
-                                         data->ds_clp, &reset) == -EAGAIN) {
-               dprintk("%s calling restart ds_clp %p ds_clp->cl_session %p\n",
-                       __func__, data->ds_clp, data->ds_clp->cl_session);
-               if (reset) {
-                       pnfs_set_lo_fail(hdr->lseg);
-                       nfs4_reset_write(task, data);
-               }
+       int err;
+
+       err = filelayout_async_handle_error(task, data->args.context->state,
+                                           data->ds_clp, hdr->lseg);
+
+       switch (err) {
+       case -NFS4ERR_RESET_TO_MDS:
+               filelayout_reset_write(data);
+               return task->tk_status;
+       case -EAGAIN:
                rpc_restart_call_prepare(task);
                return -EAGAIN;
        }
@@ -266,17 +359,17 @@ static void prepare_to_resend_writes(struct nfs_commit_data *data)
 static int filelayout_commit_done_cb(struct rpc_task *task,
                                     struct nfs_commit_data *data)
 {
-       int reset = 0;
-
-       if (filelayout_async_handle_error(task, data->context->state,
-                                         data->ds_clp, &reset) == -EAGAIN) {
-               dprintk("%s calling restart ds_clp %p ds_clp->cl_session %p\n",
-                       __func__, data->ds_clp, data->ds_clp->cl_session);
-               if (reset) {
-                       prepare_to_resend_writes(data);
-                       pnfs_set_lo_fail(data->lseg);
-               } else
-                       rpc_restart_call_prepare(task);
+       int err;
+
+       err = filelayout_async_handle_error(task, NULL, data->ds_clp,
+                                           data->lseg);
+
+       switch (err) {
+       case -NFS4ERR_RESET_TO_MDS:
+               prepare_to_resend_writes(data);
+               return -EAGAIN;
+       case -EAGAIN:
+               rpc_restart_call_prepare(task);
                return -EAGAIN;
        }
 
@@ -287,6 +380,12 @@ static void filelayout_write_prepare(struct rpc_task *task, void *data)
 {
        struct nfs_write_data *wdata = data;
 
+       if (filelayout_reset_to_mds(wdata->header->lseg)) {
+               dprintk("%s task %u reset io to MDS\n", __func__, task->tk_pid);
+               filelayout_reset_write(wdata);
+               rpc_exit(task, 0);
+               return;
+       }
        if (nfs41_setup_sequence(wdata->ds_clp->cl_session,
                                &wdata->args.seq_args, &wdata->res.seq_res,
                                task))
@@ -299,6 +398,10 @@ static void filelayout_write_call_done(struct rpc_task *task, void *data)
 {
        struct nfs_write_data *wdata = data;
 
+       if (test_bit(NFS_IOHDR_REDO, &wdata->header->flags) &&
+           task->tk_status == 0)
+               return;
+
        /* Note this may cause RPC to be resent */
        wdata->header->mds_ops->rpc_call_done(task, data);
 }
@@ -314,6 +417,7 @@ static void filelayout_write_release(void *data)
 {
        struct nfs_write_data *wdata = data;
 
+       nfs_put_client(wdata->ds_clp);
        wdata->header->mds_ops->rpc_release(data);
 }
 
@@ -347,13 +451,10 @@ static void filelayout_commit_count_stats(struct rpc_task *task, void *data)
 static void filelayout_commit_release(void *calldata)
 {
        struct nfs_commit_data *data = calldata;
-       struct nfs_commit_info cinfo;
 
-       nfs_commit_release_pages(data);
-       nfs_init_cinfo(&cinfo, data->inode, data->dreq);
-       if (atomic_dec_and_test(&cinfo.mds->rpcs_out))
-               nfs_commit_clear_lock(NFS_I(data->inode));
+       data->completion_ops->completion(data);
        put_lseg(data->lseg);
+       nfs_put_client(data->ds_clp);
        nfs_commitdata_release(data);
 }
 
@@ -393,22 +494,17 @@ filelayout_read_pagelist(struct nfs_read_data *data)
                __func__, hdr->inode->i_ino,
                data->args.pgbase, (size_t)data->args.count, offset);
 
-       if (test_bit(NFS_DEVICEID_INVALID, &FILELAYOUT_DEVID_NODE(lseg)->flags))
-               return PNFS_NOT_ATTEMPTED;
-
        /* Retrieve the correct rpc_client for the byte range */
        j = nfs4_fl_calc_j_index(lseg, offset);
        idx = nfs4_fl_calc_ds_index(lseg, j);
        ds = nfs4_fl_prepare_ds(lseg, idx);
-       if (!ds) {
-               /* Either layout fh index faulty, or ds connect failed */
-               set_bit(lo_fail_bit(IOMODE_RW), &lseg->pls_layout->plh_flags);
-               set_bit(lo_fail_bit(IOMODE_READ), &lseg->pls_layout->plh_flags);
+       if (!ds)
                return PNFS_NOT_ATTEMPTED;
-       }
-       dprintk("%s USE DS: %s\n", __func__, ds->ds_remotestr);
+       dprintk("%s USE DS: %s cl_count %d\n", __func__,
+               ds->ds_remotestr, atomic_read(&ds->ds_clp->cl_count));
 
        /* No multipath support. Use first DS */
+       atomic_inc(&ds->ds_clp->cl_count);
        data->ds_clp = ds->ds_clp;
        fh = nfs4_fl_select_ds_fh(lseg, j);
        if (fh)
@@ -419,7 +515,7 @@ filelayout_read_pagelist(struct nfs_read_data *data)
 
        /* Perform an asynchronous read to ds */
        status = nfs_initiate_read(ds->ds_clp->cl_rpcclient, data,
-                                  &filelayout_read_call_ops);
+                                 &filelayout_read_call_ops, RPC_TASK_SOFTCONN);
        BUG_ON(status != 0);
        return PNFS_ATTEMPTED;
 }
@@ -436,25 +532,18 @@ filelayout_write_pagelist(struct nfs_write_data *data, int sync)
        struct nfs_fh *fh;
        int status;
 
-       if (test_bit(NFS_DEVICEID_INVALID, &FILELAYOUT_DEVID_NODE(lseg)->flags))
-               return PNFS_NOT_ATTEMPTED;
-
        /* Retrieve the correct rpc_client for the byte range */
        j = nfs4_fl_calc_j_index(lseg, offset);
        idx = nfs4_fl_calc_ds_index(lseg, j);
        ds = nfs4_fl_prepare_ds(lseg, idx);
-       if (!ds) {
-               printk(KERN_ERR "NFS: %s: prepare_ds failed, use MDS\n",
-                       __func__);
-               set_bit(lo_fail_bit(IOMODE_RW), &lseg->pls_layout->plh_flags);
-               set_bit(lo_fail_bit(IOMODE_READ), &lseg->pls_layout->plh_flags);
+       if (!ds)
                return PNFS_NOT_ATTEMPTED;
-       }
-       dprintk("%s ino %lu sync %d req %Zu@%llu DS: %s\n", __func__,
-               hdr->inode->i_ino, sync, (size_t) data->args.count, offset,
-               ds->ds_remotestr);
+       dprintk("%s ino %lu sync %d req %Zu@%llu DS: %s cl_count %d\n",
+               __func__, hdr->inode->i_ino, sync, (size_t) data->args.count,
+               offset, ds->ds_remotestr, atomic_read(&ds->ds_clp->cl_count));
 
        data->write_done_cb = filelayout_write_done_cb;
+       atomic_inc(&ds->ds_clp->cl_count);
        data->ds_clp = ds->ds_clp;
        fh = nfs4_fl_select_ds_fh(lseg, j);
        if (fh)
@@ -467,7 +556,8 @@ filelayout_write_pagelist(struct nfs_write_data *data, int sync)
 
        /* Perform an asynchronous write */
        status = nfs_initiate_write(ds->ds_clp->cl_rpcclient, data,
-                                   &filelayout_write_call_ops, sync);
+                                   &filelayout_write_call_ops, sync,
+                                   RPC_TASK_SOFTCONN);
        BUG_ON(status != 0);
        return PNFS_ATTEMPTED;
 }
@@ -981,46 +1071,56 @@ static int filelayout_initiate_commit(struct nfs_commit_data *data, int how)
        idx = calc_ds_index_from_commit(lseg, data->ds_commit_index);
        ds = nfs4_fl_prepare_ds(lseg, idx);
        if (!ds) {
-               printk(KERN_ERR "NFS: %s: prepare_ds failed, use MDS\n",
-                       __func__);
-               set_bit(lo_fail_bit(IOMODE_RW), &lseg->pls_layout->plh_flags);
-               set_bit(lo_fail_bit(IOMODE_READ), &lseg->pls_layout->plh_flags);
                prepare_to_resend_writes(data);
                filelayout_commit_release(data);
                return -EAGAIN;
        }
-       dprintk("%s ino %lu, how %d\n", __func__, data->inode->i_ino, how);
+       dprintk("%s ino %lu, how %d cl_count %d\n", __func__,
+               data->inode->i_ino, how, atomic_read(&ds->ds_clp->cl_count));
        data->commit_done_cb = filelayout_commit_done_cb;
+       atomic_inc(&ds->ds_clp->cl_count);
        data->ds_clp = ds->ds_clp;
        fh = select_ds_fh_from_commit(lseg, data->ds_commit_index);
        if (fh)
                data->args.fh = fh;
        return nfs_initiate_commit(ds->ds_clp->cl_rpcclient, data,
-                                  &filelayout_commit_call_ops, how);
+                                  &filelayout_commit_call_ops, how,
+                                  RPC_TASK_SOFTCONN);
 }
 
 static int
-filelayout_scan_ds_commit_list(struct pnfs_commit_bucket *bucket,
-                              struct nfs_commit_info *cinfo,
-                              int max)
+transfer_commit_list(struct list_head *src, struct list_head *dst,
+                    struct nfs_commit_info *cinfo, int max)
 {
-       struct list_head *src = &bucket->written;
-       struct list_head *dst = &bucket->committing;
        struct nfs_page *req, *tmp;
        int ret = 0;
 
        list_for_each_entry_safe(req, tmp, src, wb_list) {
                if (!nfs_lock_request(req))
                        continue;
+               kref_get(&req->wb_kref);
                if (cond_resched_lock(cinfo->lock))
                        list_safe_reset_next(req, tmp, wb_list);
                nfs_request_remove_commit_list(req, cinfo);
                clear_bit(PG_COMMIT_TO_DS, &req->wb_flags);
                nfs_list_add_request(req, dst);
                ret++;
-               if (ret == max)
+               if ((ret == max) && !cinfo->dreq)
                        break;
        }
+       return ret;
+}
+
+static int
+filelayout_scan_ds_commit_list(struct pnfs_commit_bucket *bucket,
+                              struct nfs_commit_info *cinfo,
+                              int max)
+{
+       struct list_head *src = &bucket->written;
+       struct list_head *dst = &bucket->committing;
+       int ret;
+
+       ret = transfer_commit_list(src, dst, cinfo, max);
        if (ret) {
                cinfo->ds->nwritten -= ret;
                cinfo->ds->ncommitting += ret;
@@ -1050,6 +1150,27 @@ static int filelayout_scan_commit_lists(struct nfs_commit_info *cinfo,
        return rv;
 }
 
+/* Pull everything off the committing lists and dump into @dst */
+static void filelayout_recover_commit_reqs(struct list_head *dst,
+                                          struct nfs_commit_info *cinfo)
+{
+       struct pnfs_commit_bucket *b;
+       int i;
+
+       /* NOTE cinfo->lock is NOT held, relying on fact that this is
+        * only called on single thread per dreq.
+        * Can't take the lock because need to do put_lseg
+        */
+       for (i = 0, b = cinfo->ds->buckets; i < cinfo->ds->nbuckets; i++, b++) {
+               if (transfer_commit_list(&b->written, dst, cinfo, 0)) {
+                       BUG_ON(!list_empty(&b->written));
+                       put_lseg(b->wlseg);
+                       b->wlseg = NULL;
+               }
+       }
+       cinfo->ds->nwritten = 0;
+}
+
 static unsigned int
 alloc_ds_commits(struct nfs_commit_info *cinfo, struct list_head *list)
 {
@@ -1108,7 +1229,7 @@ filelayout_commit_pagelist(struct inode *inode, struct list_head *mds_pages,
        nreq += alloc_ds_commits(cinfo, &list);
 
        if (nreq == 0) {
-               nfs_commit_clear_lock(NFS_I(inode));
+               cinfo->completion_ops->error_cleanup(NFS_I(inode));
                goto out;
        }
 
@@ -1117,14 +1238,14 @@ filelayout_commit_pagelist(struct inode *inode, struct list_head *mds_pages,
        list_for_each_entry_safe(data, tmp, &list, pages) {
                list_del_init(&data->pages);
                if (!data->lseg) {
-                       nfs_init_commit(data, mds_pages, NULL);
+                       nfs_init_commit(data, mds_pages, NULL, cinfo);
                        nfs_initiate_commit(NFS_CLIENT(inode), data,
-                                           data->mds_ops, how);
+                                           data->mds_ops, how, 0);
                } else {
                        struct pnfs_commit_bucket *buckets;
 
                        buckets = cinfo->ds->buckets;
-                       nfs_init_commit(data, &buckets[data->ds_commit_index].committing, data->lseg);
+                       nfs_init_commit(data, &buckets[data->ds_commit_index].committing, data->lseg, cinfo);
                        filelayout_initiate_commit(data, how);
                }
        }
@@ -1157,7 +1278,12 @@ filelayout_free_layout_hdr(struct pnfs_layout_hdr *lo)
 static struct pnfs_ds_commit_info *
 filelayout_get_ds_info(struct inode *inode)
 {
-       return &FILELAYOUT_FROM_HDR(NFS_I(inode)->layout)->commit_info;
+       struct pnfs_layout_hdr *layout = NFS_I(inode)->layout;
+
+       if (layout == NULL)
+               return NULL;
+       else
+               return &FILELAYOUT_FROM_HDR(layout)->commit_info;
 }
 
 static struct pnfs_layoutdriver_type filelayout_type = {
@@ -1174,6 +1300,7 @@ static struct pnfs_layoutdriver_type filelayout_type = {
        .mark_request_commit    = filelayout_mark_request_commit,
        .clear_request_commit   = filelayout_clear_request_commit,
        .scan_commit_lists      = filelayout_scan_commit_lists,
+       .recover_commit_reqs    = filelayout_recover_commit_reqs,
        .commit_pagelist        = filelayout_commit_pagelist,
        .read_pagelist          = filelayout_read_pagelist,
        .write_pagelist         = filelayout_write_pagelist,