pNFS: Add infrastructure for cleaning up per-layout commit structures
[linux-2.6-microblaze.git] / fs / nfs / pnfs_nfs.c
index f895a28..edad251 100644 (file)
@@ -118,6 +118,67 @@ pnfs_free_commit_array(struct pnfs_commit_array *p)
 }
 EXPORT_SYMBOL_GPL(pnfs_free_commit_array);
 
+static void
+pnfs_release_commit_array_locked(struct pnfs_commit_array *array)
+{
+       list_del_rcu(&array->cinfo_list);
+       list_del(&array->lseg_list);
+       pnfs_free_commit_array(array);
+}
+
+static void
+pnfs_put_commit_array_locked(struct pnfs_commit_array *array)
+{
+       if (refcount_dec_and_test(&array->refcount))
+               pnfs_release_commit_array_locked(array);
+}
+
+static void
+pnfs_put_commit_array(struct pnfs_commit_array *array, struct inode *inode)
+{
+       if (refcount_dec_and_lock(&array->refcount, &inode->i_lock)) {
+               pnfs_release_commit_array_locked(array);
+               spin_unlock(&inode->i_lock);
+       }
+}
+
+static struct pnfs_commit_array *
+pnfs_get_commit_array(struct pnfs_commit_array *array)
+{
+       if (refcount_inc_not_zero(&array->refcount))
+               return array;
+       return NULL;
+}
+
+static void
+pnfs_remove_and_free_commit_array(struct pnfs_commit_array *array)
+{
+       array->lseg = NULL;
+       list_del_init(&array->lseg_list);
+       pnfs_put_commit_array_locked(array);
+}
+
+void
+pnfs_generic_ds_cinfo_release_lseg(struct pnfs_ds_commit_info *fl_cinfo,
+               struct pnfs_layout_segment *lseg)
+{
+       struct pnfs_commit_array *array, *tmp;
+
+       list_for_each_entry_safe(array, tmp, &lseg->pls_commits, lseg_list)
+               pnfs_remove_and_free_commit_array(array);
+}
+EXPORT_SYMBOL_GPL(pnfs_generic_ds_cinfo_release_lseg);
+
+void
+pnfs_generic_ds_cinfo_destroy(struct pnfs_ds_commit_info *fl_cinfo)
+{
+       struct pnfs_commit_array *array, *tmp;
+
+       list_for_each_entry_safe(array, tmp, &fl_cinfo->commits, cinfo_list)
+               pnfs_remove_and_free_commit_array(array);
+}
+EXPORT_SYMBOL_GPL(pnfs_generic_ds_cinfo_destroy);
+
 /*
  * Locks the nfs_page requests for commit and moves them to
  * @bucket->committing.
@@ -177,14 +238,21 @@ int pnfs_generic_scan_commit_lists(struct nfs_commit_info *cinfo, int max)
        max -= cnt;
        if (!max)
                return rv;
-       list_for_each_entry(array, &fl_cinfo->commits, cinfo_list) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(array, &fl_cinfo->commits, cinfo_list) {
+               if (!array->lseg || !pnfs_get_commit_array(array))
+                       continue;
+               rcu_read_unlock();
                cnt = pnfs_bucket_scan_array(cinfo, array->buckets,
                                array->nbuckets, max);
+               rcu_read_lock();
+               pnfs_put_commit_array(array, cinfo->inode);
                rv += cnt;
                max -= cnt;
                if (!max)
                        break;
        }
+       rcu_read_unlock();
        return rv;
 }
 EXPORT_SYMBOL_GPL(pnfs_generic_scan_commit_lists);
@@ -230,13 +298,20 @@ void pnfs_generic_recover_commit_reqs(struct list_head *dst,
                                                   fl_cinfo->nbuckets,
                                                   cinfo);
        fl_cinfo->nwritten -= nwritten;
-       list_for_each_entry(array, &fl_cinfo->commits, cinfo_list) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(array, &fl_cinfo->commits, cinfo_list) {
+               if (!array->lseg || !pnfs_get_commit_array(array))
+                       continue;
+               rcu_read_unlock();
                nwritten = pnfs_bucket_recover_commit_reqs(dst,
                                                           array->buckets,
                                                           array->nbuckets,
                                                           cinfo);
+               rcu_read_lock();
+               pnfs_put_commit_array(array, cinfo->inode);
                fl_cinfo->nwritten -= nwritten;
        }
+       rcu_read_unlock();
 }
 EXPORT_SYMBOL_GPL(pnfs_generic_recover_commit_reqs);
 
@@ -330,9 +405,16 @@ pnfs_alloc_ds_commits_list(struct list_head *list,
        struct pnfs_commit_array *array;
        unsigned int ret = 0;
 
-       list_for_each_entry(array, &fl_cinfo->commits, cinfo_list)
+       rcu_read_lock();
+       list_for_each_entry_rcu(array, &fl_cinfo->commits, cinfo_list) {
+               if (!array->lseg || !pnfs_get_commit_array(array))
+                       continue;
+               rcu_read_unlock();
                ret += pnfs_bucket_alloc_ds_commits(list, array->buckets,
                                array->nbuckets, cinfo);
+               rcu_read_lock();
+               pnfs_put_commit_array(array, cinfo->inode);
+       }
        return ret;
 }