Use an inode flag, I_PINNING_FSCACHE_WB, to indicate that a cookie is
pinned in use by that inode for the purposes of writeback.

Pinning is necessary because the in-use pin from the open file is released
before the writeback takes place, but if the resources aren't pinned, the
dirty data can't be written to the cache.

Signed-off-by: David Howells <[email protected]>
---

 fs/fs-writeback.c         |    8 ++++++++
 fs/fscache/io.c           |   38 ++++++++++++++++++++++++++++++++++++++
 include/linux/fs.h        |    3 +++
 include/linux/fscache.h   |   22 ++++++++++++++++++++++
 include/linux/writeback.h |    1 +
 5 files changed, 72 insertions(+)

diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index e6005c78bfa9..f7fb7fb37337 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -1506,6 +1506,13 @@ __writeback_single_inode(struct inode *inode, struct 
writeback_control *wbc)
 
        if (mapping_tagged(mapping, PAGECACHE_TAG_DIRTY))
                inode->i_state |= I_DIRTY_PAGES;
+       else if (unlikely(inode->i_state & I_PINNING_FSCACHE_WB)) {
+               if (!(inode->i_state & I_DIRTY_PAGES)) {
+                       inode->i_state &= ~I_PINNING_FSCACHE_WB;
+                       wbc->unpinned_fscache_wb = true;
+                       dirty |= I_PINNING_FSCACHE_WB; /* Cause write_inode */
+               }
+       }
 
        spin_unlock(&inode->i_lock);
 
@@ -1517,6 +1524,7 @@ __writeback_single_inode(struct inode *inode, struct 
writeback_control *wbc)
                if (ret == 0)
                        ret = err;
        }
+       wbc->unpinned_fscache_wb = false;
        trace_writeback_single_inode(inode, wbc, nr_to_write);
        return ret;
 }
diff --git a/fs/fscache/io.c b/fs/fscache/io.c
index 7ad13900b281..87ffe84c9f27 100644
--- a/fs/fscache/io.c
+++ b/fs/fscache/io.c
@@ -142,3 +142,41 @@ int __fscache_begin_operation(struct fscache_cookie 
*cookie,
        return -ENOBUFS;
 }
 EXPORT_SYMBOL(__fscache_begin_operation);
+
+/**
+ * fscache_set_page_dirty - Mark page dirty and pin a cache object for 
writeback
+ * @page: The page being dirtied
+ * @cookie: The cookie referring to the cache object
+ *
+ * Set the dirty flag on a page and pin an in-use cache object in memory when
+ * dirtying a page so that writeback can later write to it.  This is intended
+ * to be called from the filesystem's ->set_page_dirty() method.
+ *
+ *  Returns 1 if PG_dirty was set on the page, 0 otherwise.
+ */
+int fscache_set_page_dirty(struct page *page, struct fscache_cookie *cookie)
+{
+       struct inode *inode = page->mapping->host;
+       bool need_use = false;
+
+       _enter("");
+
+       if (!__set_page_dirty_nobuffers(page))
+               return 0;
+       if (!fscache_cookie_valid(cookie))
+               return 1;
+
+       if (!(inode->i_state & I_PINNING_FSCACHE_WB)) {
+               spin_lock(&inode->i_lock);
+               if (!(inode->i_state & I_PINNING_FSCACHE_WB)) {
+                       inode->i_state |= I_PINNING_FSCACHE_WB;
+                       need_use = true;
+               }
+               spin_unlock(&inode->i_lock);
+
+               if (need_use)
+                       fscache_use_cookie(cookie, true);
+       }
+       return 1;
+}
+EXPORT_SYMBOL(fscache_set_page_dirty);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index f6dad577ea07..fdbac1252381 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2151,6 +2151,8 @@ static inline void kiocb_clone(struct kiocb *kiocb, 
struct kiocb *kiocb_src,
  *                     Used to detect that mark_inode_dirty() should not move
  *                     inode between dirty lists.
  *
+ * I_PINNING_FSCACHE_WB        Inode is pinning an fscache object for 
writeback.
+ *
  * Q: What is the difference between I_WILL_FREE and I_FREEING?
  */
 #define I_DIRTY_SYNC           (1 << 0)
@@ -2173,6 +2175,7 @@ static inline void kiocb_clone(struct kiocb *kiocb, 
struct kiocb *kiocb_src,
 #define I_CREATING             (1 << 15)
 #define I_DONTCACHE            (1 << 16)
 #define I_SYNC_QUEUED          (1 << 17)
+#define I_PINNING_FSCACHE_WB   (1 << 18)
 
 #define I_DIRTY_INODE (I_DIRTY_SYNC | I_DIRTY_DATASYNC)
 #define I_DIRTY (I_DIRTY_INODE | I_DIRTY_PAGES)
diff --git a/include/linux/fscache.h b/include/linux/fscache.h
index 8d47c7a08456..d2fc98a5755a 100644
--- a/include/linux/fscache.h
+++ b/include/linux/fscache.h
@@ -19,6 +19,7 @@
 #include <linux/pagemap.h>
 #include <linux/pagevec.h>
 #include <linux/list_bl.h>
+#include <linux/writeback.h>
 
 #if defined(CONFIG_FSCACHE) || defined(CONFIG_FSCACHE_MODULE)
 #define __fscache_available (1)
@@ -580,4 +581,25 @@ int fscache_write(struct fscache_op_resources *opr,
        return ops->write(opr, start_pos, iter, term_func, term_func_priv);
 }
 
+#if __fscache_available
+extern int fscache_set_page_dirty(struct page *page, struct fscache_cookie 
*cookie);
+#else
+#define fscache_set_page_dirty(PAGE, COOKIE) 
(__set_page_dirty_nobuffers((PAGE)))
+#endif
+
+/**
+ * fscache_unpin_writeback - Unpin writeback resources
+ * @wbc: The writeback control
+ * @cookie: The cookie referring to the cache object
+ *
+ * Unpin the writeback resources pinned by fscache_set_page_dirty().  This is
+ * intended to be called by the netfs's ->write_inode() method.
+ */
+static inline void fscache_unpin_writeback(struct writeback_control *wbc,
+                                          struct fscache_cookie *cookie)
+{
+       if (wbc->unpinned_fscache_wb)
+               fscache_unuse_cookie(cookie, NULL, NULL);
+}
+
 #endif /* _LINUX_FSCACHE_H */
diff --git a/include/linux/writeback.h b/include/linux/writeback.h
index 8e5c5bb16e2d..5e7d1d38f261 100644
--- a/include/linux/writeback.h
+++ b/include/linux/writeback.h
@@ -69,6 +69,7 @@ struct writeback_control {
        unsigned for_reclaim:1;         /* Invoked from the page allocator */
        unsigned range_cyclic:1;        /* range_start is cyclic */
        unsigned for_sync:1;            /* sync(2) WB_SYNC_ALL writeback */
+       unsigned unpinned_fscache_wb:1; /* Cleared I_PINNING_FSCACHE_WB */
 
        /*
         * When writeback IOs are bounced through async layers, only the


Reply via email to