Implement the "will_modify" parameter passed to fscache_use_cookie().

Setting this to true will henceforth cause the affected object to be marked
as dirty on disk, subject to conflict resolution in the event that power
failure or a crash occurs or the filesystem operates in disconnected mode.

The dirty flag is removed when the fscache_object is discarded from memory.

A cache hook is provided to prepare for writing - and this can be used to
mark the object on disk.

Signed-off-by: David Howells <dhowe...@redhat.com>
---

 fs/cachefiles/interface.c     |   65 +++++++++++++++++++++++++++++++++++++++++
 fs/cachefiles/internal.h      |    2 +
 fs/cachefiles/xattr.c         |   21 +++++++++++++
 fs/fscache/cookie.c           |   17 +++++++++--
 fs/fscache/internal.h         |    1 +
 fs/fscache/obj.c              |   29 +++++++++++++++---
 include/linux/fscache-cache.h |    4 +++
 7 files changed, 130 insertions(+), 9 deletions(-)

diff --git a/fs/cachefiles/interface.c b/fs/cachefiles/interface.c
index b312a04f672b..457adfca5931 100644
--- a/fs/cachefiles/interface.c
+++ b/fs/cachefiles/interface.c
@@ -210,14 +210,78 @@ static void cachefiles_update_object(struct 
fscache_object *_object)
        _leave("");
 }
 
+/*
+ * Shorten the backing object to discard any dirty data and free up
+ * any unused granules.
+ */
+static bool cachefiles_shorten_object(struct cachefiles_object *object, loff_t 
new_size)
+{
+       struct cachefiles_cache *cache;
+       struct inode *inode;
+       struct path path;
+       loff_t i_size;
+
+       cache = container_of(object->fscache.cache,
+                            struct cachefiles_cache, cache);
+       path.mnt = cache->mnt;
+       path.dentry = object->dentry;
+
+       inode = d_inode(object->dentry);
+       trace_cachefiles_trunc(object, inode, i_size_read(inode), new_size);
+       if (vfs_truncate(&path, new_size) < 0) {
+               cachefiles_io_error_obj(object, "Trunc-to-size failed");
+               cachefiles_remove_object_xattr(cache, object->dentry);
+               return false;
+       }
+
+       new_size = round_up(new_size, CACHEFILES_DIO_BLOCK_SIZE);
+       i_size = i_size_read(inode);
+       if (i_size < new_size) {
+               trace_cachefiles_trunc(object, inode, i_size, new_size);
+               if (vfs_truncate(&path, new_size) < 0) {
+                       cachefiles_io_error_obj(object, "Trunc-to-dio-size 
failed");
+                       cachefiles_remove_object_xattr(cache, object->dentry);
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+/*
+ * Trim excess stored data off of an object.
+ */
+static bool cachefiles_trim_object(struct cachefiles_object *object)
+{
+       loff_t object_size;
+
+       _enter("{OBJ%x}", object->fscache.debug_id);
+
+       object_size = object->fscache.cookie->object_size;
+       if (i_size_read(d_inode(object->dentry)) <= object_size)
+               return true;
+
+       return cachefiles_shorten_object(object, object_size);
+}
+
 /*
  * Commit changes to the object as we drop it.
  */
 static bool cachefiles_commit_object(struct cachefiles_object *object,
                                     struct cachefiles_cache *cache)
 {
+       bool update = false;
+
        if (object->content_map_changed)
                cachefiles_save_content_map(object);
+       if (test_and_clear_bit(FSCACHE_OBJECT_LOCAL_WRITE, 
&object->fscache.flags))
+               update = true;
+       if (test_and_clear_bit(FSCACHE_OBJECT_NEEDS_UPDATE, 
&object->fscache.flags))
+               update = true;
+       if (update) {
+               if (cachefiles_trim_object(object))
+                       cachefiles_set_object_xattr(object);
+       }
 
        if (test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags))
                return cachefiles_commit_tmpfile(cache, object);
@@ -577,5 +641,6 @@ const struct fscache_cache_ops cachefiles_cache_ops = {
        .shape_extent           = cachefiles_shape_extent,
        .read                   = cachefiles_read,
        .write                  = cachefiles_write,
+       .prepare_to_write       = cachefiles_prepare_to_write,
        .display_object         = cachefiles_display_object,
 };
diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h
index ccc8cd2b8250..487a434ddb75 100644
--- a/fs/cachefiles/internal.h
+++ b/fs/cachefiles/internal.h
@@ -248,7 +248,7 @@ extern int cachefiles_set_object_xattr(struct 
cachefiles_object *object);
 extern int cachefiles_check_auxdata(struct cachefiles_object *object);
 extern int cachefiles_remove_object_xattr(struct cachefiles_cache *cache,
                                          struct dentry *dentry);
-
+extern int cachefiles_prepare_to_write(struct fscache_object *object);
 
 /*
  * error handling
diff --git a/fs/cachefiles/xattr.c b/fs/cachefiles/xattr.c
index 22c56ca2fd0b..456301b7abb0 100644
--- a/fs/cachefiles/xattr.c
+++ b/fs/cachefiles/xattr.c
@@ -124,6 +124,8 @@ int cachefiles_set_object_xattr(struct cachefiles_object 
*object)
        buf->zero_point         = 
cpu_to_be64(object->fscache.cookie->zero_point);
        buf->type               = object->fscache.cookie->type;
        buf->content            = object->content_info;
+       if (test_bit(FSCACHE_OBJECT_LOCAL_WRITE, &object->fscache.flags))
+               buf->content    = CACHEFILES_CONTENT_DIRTY;
        if (len > 0)
                memcpy(buf->data, fscache_get_aux(object->fscache.cookie), len);
 
@@ -184,10 +186,16 @@ int cachefiles_check_auxdata(struct cachefiles_object 
*object)
                why = cachefiles_coherency_check_aux;
        } else if (be64_to_cpu(buf->object_size) != 
object->fscache.cookie->object_size) {
                why = cachefiles_coherency_check_objsize;
+       } else if (buf->content == CACHEFILES_CONTENT_DIRTY) {
+               // TODO: Begin conflict resolution
+               pr_warn("Dirty object in cache\n");
+               why = cachefiles_coherency_check_dirty;
        } else {
                object->fscache.cookie->zero_point = 
be64_to_cpu(buf->zero_point);
                object->content_info = buf->content;
                why = cachefiles_coherency_check_ok;
+               object->fscache.cookie->zero_point = 
be64_to_cpu(buf->zero_point);
+               object->content_info = buf->content;
                ret = 0;
        }
 
@@ -219,3 +227,16 @@ int cachefiles_remove_object_xattr(struct cachefiles_cache 
*cache,
        _leave(" = %d", ret);
        return ret;
 }
+
+/*
+ * Stick a marker on the cache object to indicate that it's dirty.
+ */
+int cachefiles_prepare_to_write(struct fscache_object *_object)
+{
+       struct cachefiles_object *object =
+               container_of(_object, struct cachefiles_object, fscache);
+
+       _enter("c=%08x", object->fscache.cookie->debug_id);
+
+       return cachefiles_set_object_xattr(object);
+}
diff --git a/fs/fscache/cookie.c b/fs/fscache/cookie.c
index c0d93dc7f69f..c96cd23bcdb2 100644
--- a/fs/fscache/cookie.c
+++ b/fs/fscache/cookie.c
@@ -342,6 +342,8 @@ EXPORT_SYMBOL(__fscache_acquire_cookie);
 void __fscache_use_cookie(struct fscache_cookie *cookie, bool will_modify)
 {
        enum fscache_cookie_stage stage;
+       struct fscache_object *object;
+       bool write_set;
 
        _enter("c=%08x", cookie->debug_id);
 
@@ -360,7 +362,7 @@ void __fscache_use_cookie(struct fscache_cookie *cookie, 
bool will_modify)
 
                /* The lookup job holds its own active increment */
                atomic_inc(&cookie->n_active);
-               fscache_dispatch(cookie, NULL, 0, fscache_lookup_object);
+               fscache_dispatch(cookie, NULL, will_modify, 
fscache_lookup_object);
                break;
 
        case FSCACHE_COOKIE_STAGE_INITIALISING:
@@ -373,8 +375,17 @@ void __fscache_use_cookie(struct fscache_cookie *cookie, 
bool will_modify)
        case FSCACHE_COOKIE_STAGE_NO_DATA_YET:
        case FSCACHE_COOKIE_STAGE_ACTIVE:
        case FSCACHE_COOKIE_STAGE_INVALIDATING:
-               // TODO: Handle will_modify
-               spin_unlock(&cookie->lock);
+               if (will_modify) {
+                       object = hlist_entry(cookie->backing_objects.first,
+                                            struct fscache_object, 
cookie_link);
+                       write_set = test_and_set_bit(FSCACHE_OBJECT_LOCAL_WRITE,
+                                                    &object->flags);
+                       spin_unlock(&cookie->lock);
+                       if (!write_set)
+                               fscache_dispatch(cookie, object, 0, 
fscache_prepare_to_write);
+               } else {
+                       spin_unlock(&cookie->lock);
+               }
                break;
 
        case FSCACHE_COOKIE_STAGE_DEAD:
diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h
index b9cad60e3c4e..0b370d059bdf 100644
--- a/fs/fscache/internal.h
+++ b/fs/fscache/internal.h
@@ -134,6 +134,7 @@ extern void fscache_lookup_object(struct fscache_cookie *, 
struct fscache_object
 extern void fscache_invalidate_object(struct fscache_cookie *, struct 
fscache_object *, int);
 extern void fscache_drop_object(struct fscache_cookie *, struct fscache_object 
*, bool);
 extern void fscache_discard_objects(struct fscache_cookie *, struct 
fscache_object *, int);
+extern void fscache_prepare_to_write(struct fscache_cookie *, struct 
fscache_object *, int);
 
 /*
  * object-list.c
diff --git a/fs/fscache/obj.c b/fs/fscache/obj.c
index aea4239641a9..332ba132413d 100644
--- a/fs/fscache/obj.c
+++ b/fs/fscache/obj.c
@@ -117,7 +117,8 @@ static bool fscache_wrangle_object(struct fscache_cookie 
*cookie,
  * Create an object chain, making sure that the index chain is fully created.
  */
 static struct fscache_object *fscache_lookup_object_chain(struct 
fscache_cookie *cookie,
-                                                         struct fscache_cache 
*cache)
+                                                         struct fscache_cache 
*cache,
+                                                         bool will_modify)
 {
        struct fscache_object *object = NULL, *parent, *xobject;
 
@@ -131,7 +132,7 @@ static struct fscache_object 
*fscache_lookup_object_chain(struct fscache_cookie
        spin_unlock(&cookie->lock);
 
        /* Recurse to look up/create the parent index. */
-       parent = fscache_lookup_object_chain(cookie->parent, cache);
+       parent = fscache_lookup_object_chain(cookie->parent, cache, false);
        if (IS_ERR(parent))
                goto error;
 
@@ -146,9 +147,13 @@ static struct fscache_object 
*fscache_lookup_object_chain(struct fscache_cookie
        if (!object)
                goto error;
 
+       if (will_modify)
+               __set_bit(FSCACHE_OBJECT_LOCAL_WRITE, &object->flags);
+
        xobject = fscache_attach_object(cookie, object);
        if (xobject != object) {
                fscache_do_put_object(object, fscache_obj_put_alloc_dup);
+               object = xobject;
                goto object_exists;
        }
 
@@ -199,7 +204,8 @@ static struct fscache_object 
*fscache_lookup_object_chain(struct fscache_cookie
  * - this must make sure the index chain is instantiated and instantiate the
  *   object representation too
  */
-static void fscache_lookup_object_locked(struct fscache_cookie *cookie)
+static void fscache_lookup_object_locked(struct fscache_cookie *cookie,
+                                        bool will_modify)
 {
        struct fscache_object *object;
        struct fscache_cache *cache;
@@ -217,12 +223,16 @@ static void fscache_lookup_object_locked(struct 
fscache_cookie *cookie)
 
        _debug("cache %s", cache->tag->name);
 
-       object = fscache_lookup_object_chain(cookie, cache);
+       object = fscache_lookup_object_chain(cookie, cache, will_modify);
        if (!object) {
                _leave(" [fail]");
                return;
        }
 
+       if (will_modify &&
+           test_and_set_bit(FSCACHE_OBJECT_LOCAL_WRITE, &object->flags))
+               fscache_prepare_to_write(cookie, object, 0);
+
        fscache_do_put_object(object, fscache_obj_put);
        _leave(" [done]");
 }
@@ -231,7 +241,7 @@ void fscache_lookup_object(struct fscache_cookie *cookie,
                           struct fscache_object *object, int param)
 {
        down_read(&fscache_addremove_sem);
-       fscache_lookup_object_locked(cookie);
+       fscache_lookup_object_locked(cookie, param);
        up_read(&fscache_addremove_sem);
        __fscache_unuse_cookie(cookie, NULL, NULL);
 }
@@ -336,3 +346,12 @@ void fscache_discard_objects(struct fscache_cookie *cookie,
        up_read(&fscache_addremove_sem);
        _leave("");
 }
+
+/*
+ * Prepare a cache object to be written to.
+ */
+void fscache_prepare_to_write(struct fscache_cookie *cookie,
+                             struct fscache_object *object, int param)
+{
+       object->cache->ops->prepare_to_write(object);
+}
diff --git a/include/linux/fscache-cache.h b/include/linux/fscache-cache.h
index 4fb63d8a60cd..848ced13c4ae 100644
--- a/include/linux/fscache-cache.h
+++ b/include/linux/fscache-cache.h
@@ -155,6 +155,9 @@ struct fscache_cache_ops {
                     struct fscache_io_request *req,
                     struct iov_iter *iter);
 
+       /* Prepare to write to a live cache object */
+       int (*prepare_to_write)(struct fscache_object *object);
+
        /* Display object info in /proc/fs/fscache/objects */
        int (*display_object)(struct seq_file *m, struct fscache_object 
*object);
 };
@@ -183,6 +186,7 @@ struct fscache_object {
        spinlock_t              lock;           /* state and operations lock */
 
        unsigned long           flags;
+#define FSCACHE_OBJECT_LOCAL_WRITE     1       /* T if the object is being 
modified locally */
 #define FSCACHE_OBJECT_NEEDS_INVAL     8       /* T if object needs 
invalidation */
 #define FSCACHE_OBJECT_NEEDS_UPDATE    9       /* T if object attrs need 
writing to disk */
 


Reply via email to