]> rtime.felk.cvut.cz Git - linux-imx.git/blobdiff - fs/nfs/pnfs.c
NFSv4.1: LAYOUTGET EDELAY loops timeout to the MDS
[linux-imx.git] / fs / nfs / pnfs.c
index d00260b0810332c622e82faa44d4d94b75c111db..48ac5aad62589cd7140f55a52d5ee63f31e743b5 100644 (file)
@@ -505,37 +505,147 @@ pnfs_destroy_layout(struct nfs_inode *nfsi)
 }
 EXPORT_SYMBOL_GPL(pnfs_destroy_layout);
 
-/*
- * Called by the state manger to remove all layouts established under an
- * expired lease.
- */
-void
-pnfs_destroy_all_layouts(struct nfs_client *clp)
+static bool
+pnfs_layout_add_bulk_destroy_list(struct inode *inode,
+               struct list_head *layout_list)
 {
-       struct nfs_server *server;
        struct pnfs_layout_hdr *lo;
-       LIST_HEAD(tmp_list);
+       bool ret = false;
 
-       nfs4_deviceid_mark_client_invalid(clp);
-       nfs4_deviceid_purge_client(clp);
+       spin_lock(&inode->i_lock);
+       lo = NFS_I(inode)->layout;
+       if (lo != NULL && list_empty(&lo->plh_bulk_destroy)) {
+               pnfs_get_layout_hdr(lo);
+               list_add(&lo->plh_bulk_destroy, layout_list);
+               ret = true;
+       }
+       spin_unlock(&inode->i_lock);
+       return ret;
+}
+
+/* Caller must hold rcu_read_lock and clp->cl_lock */
+static int
+pnfs_layout_bulk_destroy_byserver_locked(struct nfs_client *clp,
+               struct nfs_server *server,
+               struct list_head *layout_list)
+{
+       struct pnfs_layout_hdr *lo, *next;
+       struct inode *inode;
+
+       list_for_each_entry_safe(lo, next, &server->layouts, plh_layouts) {
+               inode = igrab(lo->plh_inode);
+               if (inode == NULL)
+                       continue;
+               list_del_init(&lo->plh_layouts);
+               if (pnfs_layout_add_bulk_destroy_list(inode, layout_list))
+                       continue;
+               rcu_read_unlock();
+               spin_unlock(&clp->cl_lock);
+               iput(inode);
+               spin_lock(&clp->cl_lock);
+               rcu_read_lock();
+               return -EAGAIN;
+       }
+       return 0;
+}
+
+static int
+pnfs_layout_free_bulk_destroy_list(struct list_head *layout_list,
+               bool is_bulk_recall)
+{
+       struct pnfs_layout_hdr *lo;
+       struct inode *inode;
+       struct pnfs_layout_range range = {
+               .iomode = IOMODE_ANY,
+               .offset = 0,
+               .length = NFS4_MAX_UINT64,
+       };
+       LIST_HEAD(lseg_list);
+       int ret = 0;
+
+       while (!list_empty(layout_list)) {
+               lo = list_entry(layout_list->next, struct pnfs_layout_hdr,
+                               plh_bulk_destroy);
+               dprintk("%s freeing layout for inode %lu\n", __func__,
+                       lo->plh_inode->i_ino);
+               inode = lo->plh_inode;
+               spin_lock(&inode->i_lock);
+               list_del_init(&lo->plh_bulk_destroy);
+               lo->plh_block_lgets++; /* permanently block new LAYOUTGETs */
+               if (is_bulk_recall)
+                       set_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags);
+               if (pnfs_mark_matching_lsegs_invalid(lo, &lseg_list, &range))
+                       ret = -EAGAIN;
+               spin_unlock(&inode->i_lock);
+               pnfs_free_lseg_list(&lseg_list);
+               pnfs_put_layout_hdr(lo);
+               iput(inode);
+       }
+       return ret;
+}
+
+int
+pnfs_destroy_layouts_byfsid(struct nfs_client *clp,
+               struct nfs_fsid *fsid,
+               bool is_recall)
+{
+       struct nfs_server *server;
+       LIST_HEAD(layout_list);
 
        spin_lock(&clp->cl_lock);
        rcu_read_lock();
+restart:
        list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
-               if (!list_empty(&server->layouts))
-                       list_splice_init(&server->layouts, &tmp_list);
+               if (memcmp(&server->fsid, fsid, sizeof(*fsid)) != 0)
+                       continue;
+               if (pnfs_layout_bulk_destroy_byserver_locked(clp,
+                               server,
+                               &layout_list) != 0)
+                       goto restart;
        }
        rcu_read_unlock();
        spin_unlock(&clp->cl_lock);
 
-       while (!list_empty(&tmp_list)) {
-               lo = list_entry(tmp_list.next, struct pnfs_layout_hdr,
-                               plh_layouts);
-               dprintk("%s freeing layout for inode %lu\n", __func__,
-                       lo->plh_inode->i_ino);
-               list_del_init(&lo->plh_layouts);
-               pnfs_destroy_layout(NFS_I(lo->plh_inode));
+       if (list_empty(&layout_list))
+               return 0;
+       return pnfs_layout_free_bulk_destroy_list(&layout_list, is_recall);
+}
+
+int
+pnfs_destroy_layouts_byclid(struct nfs_client *clp,
+               bool is_recall)
+{
+       struct nfs_server *server;
+       LIST_HEAD(layout_list);
+
+       spin_lock(&clp->cl_lock);
+       rcu_read_lock();
+restart:
+       list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
+               if (pnfs_layout_bulk_destroy_byserver_locked(clp,
+                                       server,
+                                       &layout_list) != 0)
+                       goto restart;
        }
+       rcu_read_unlock();
+       spin_unlock(&clp->cl_lock);
+
+       if (list_empty(&layout_list))
+               return 0;
+       return pnfs_layout_free_bulk_destroy_list(&layout_list, is_recall);
+}
+
+/*
+ * Called by the state manger to remove all layouts established under an
+ * expired lease.
+ */
+void
+pnfs_destroy_all_layouts(struct nfs_client *clp)
+{
+       nfs4_deviceid_mark_client_invalid(clp);
+       nfs4_deviceid_purge_client(clp);
+
+       pnfs_destroy_layouts_byclid(clp, false);
 }
 
 /*
@@ -888,7 +998,7 @@ alloc_init_layout_hdr(struct inode *ino,
        atomic_set(&lo->plh_refcount, 1);
        INIT_LIST_HEAD(&lo->plh_layouts);
        INIT_LIST_HEAD(&lo->plh_segs);
-       INIT_LIST_HEAD(&lo->plh_bulk_recall);
+       INIT_LIST_HEAD(&lo->plh_bulk_destroy);
        lo->plh_inode = ino;
        lo->plh_lc_cred = get_rpccred(ctx->state->owner->so_cred);
        return lo;
@@ -1071,7 +1181,7 @@ pnfs_update_layout(struct inode *ino,
        struct nfs_client *clp = server->nfs_client;
        struct pnfs_layout_hdr *lo;
        struct pnfs_layout_segment *lseg = NULL;
-       bool first = false;
+       bool first;
 
        if (!pnfs_enabled_sb(NFS_SERVER(ino)))
                goto out;
@@ -1105,10 +1215,9 @@ pnfs_update_layout(struct inode *ino,
                goto out_unlock;
        atomic_inc(&lo->plh_outstanding);
 
-       if (list_empty(&lo->plh_segs))
-               first = true;
-
+       first = list_empty(&lo->plh_layouts) ? true : false;
        spin_unlock(&ino->i_lock);
+
        if (first) {
                /* The lo must be on the clp list if there is any
                 * chance of a CB_LAYOUTRECALL(FILE) coming in.
@@ -1312,13 +1421,15 @@ EXPORT_SYMBOL_GPL(pnfs_generic_pg_test);
 
 int pnfs_write_done_resend_to_mds(struct inode *inode,
                                struct list_head *head,
-                               const struct nfs_pgio_completion_ops *compl_ops)
+                               const struct nfs_pgio_completion_ops *compl_ops,
+                               struct nfs_direct_req *dreq)
 {
        struct nfs_pageio_descriptor pgio;
        LIST_HEAD(failed);
 
        /* Resend all requests through the MDS */
        nfs_pageio_init_write(&pgio, inode, FLUSH_STABLE, compl_ops);
+       pgio.pg_dreq = dreq;
        while (!list_empty(head)) {
                struct nfs_page *req = nfs_list_entry(head->next);
 
@@ -1353,7 +1464,8 @@ static void pnfs_ld_handle_write_error(struct nfs_write_data *data)
        if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags))
                data->task.tk_status = pnfs_write_done_resend_to_mds(hdr->inode,
                                                        &hdr->pages,
-                                                       hdr->completion_ops);
+                                                       hdr->completion_ops,
+                                                       hdr->dreq);
 }
 
 /*
@@ -1468,13 +1580,15 @@ EXPORT_SYMBOL_GPL(pnfs_generic_pg_writepages);
 
 int pnfs_read_done_resend_to_mds(struct inode *inode,
                                struct list_head *head,
-                               const struct nfs_pgio_completion_ops *compl_ops)
+                               const struct nfs_pgio_completion_ops *compl_ops,
+                               struct nfs_direct_req *dreq)
 {
        struct nfs_pageio_descriptor pgio;
        LIST_HEAD(failed);
 
        /* Resend all requests through the MDS */
        nfs_pageio_init_read(&pgio, inode, compl_ops);
+       pgio.pg_dreq = dreq;
        while (!list_empty(head)) {
                struct nfs_page *req = nfs_list_entry(head->next);
 
@@ -1505,7 +1619,8 @@ static void pnfs_ld_handle_read_error(struct nfs_read_data *data)
        if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags))
                data->task.tk_status = pnfs_read_done_resend_to_mds(hdr->inode,
                                                        &hdr->pages,
-                                                       hdr->completion_ops);
+                                                       hdr->completion_ops,
+                                                       hdr->dreq);
 }
 
 /*