CacheFiles: Provide read-and-reset release counters for cachefilesd
authorDavid Howells <dhowells@redhat.com>
Mon, 1 Feb 2016 16:43:04 +0000 (16:43 +0000)
committerAl Viro <viro@zeniv.linux.org.uk>
Mon, 1 Feb 2016 17:30:10 +0000 (12:30 -0500)
Provide read-and-reset objects- and blocks-released counters for cachefilesd
to use to work out whether there's anything new that can be culled.

One of the problems cachefilesd has is that if all the objects in the cache
are pinned by inodes lying dormant in the kernel inode cache, there isn't
anything for it to cull.  In such a case, it just spins around walking the
filesystem tree and scanning for something to cull.  This eats up a lot of
CPU time.

By telling cachefilesd if there have been any releases, the daemon can
sleep until there is the possibility of something to do.

cachefilesd finds this information by the following means:

 (1) When the control fd is read, the kernel presents a list of values of
     interest.  "freleased=N" and "breleased=N" are added to this list to
     indicate the number of files released and number of blocks released
     since the last read call.  At this point the counters are reset.

 (2) POLLIN is signalled if the number of files released becomes greater
     than 0.

Note that by 'released' it just means that the kernel has released its
interest in those files for the moment, not necessarily that the files
should be deleted from the cache.

Signed-off-by: David Howells <dhowells@redhat.com>
Reviewed-by: Steve Dickson <steved@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/cachefiles/daemon.c
fs/cachefiles/interface.c
fs/cachefiles/internal.h
fs/cachefiles/namei.c

index 452e98dd756053363a092a456ede5de373d9361f..1ee54ffd3a248d1cd279c6bc7fe69be40ac01353 100644 (file)
@@ -162,6 +162,8 @@ static ssize_t cachefiles_daemon_read(struct file *file, char __user *_buffer,
                                      size_t buflen, loff_t *pos)
 {
        struct cachefiles_cache *cache = file->private_data;
+       unsigned long long b_released;
+       unsigned f_released;
        char buffer[256];
        int n;
 
@@ -174,6 +176,8 @@ static ssize_t cachefiles_daemon_read(struct file *file, char __user *_buffer,
        cachefiles_has_space(cache, 0, 0);
 
        /* summarise */
+       f_released = atomic_xchg(&cache->f_released, 0);
+       b_released = atomic_long_xchg(&cache->b_released, 0);
        clear_bit(CACHEFILES_STATE_CHANGED, &cache->flags);
 
        n = snprintf(buffer, sizeof(buffer),
@@ -183,15 +187,18 @@ static ssize_t cachefiles_daemon_read(struct file *file, char __user *_buffer,
                     " fstop=%llx"
                     " brun=%llx"
                     " bcull=%llx"
-                    " bstop=%llx",
+                    " bstop=%llx"
+                    " freleased=%x"
+                    " breleased=%llx",
                     test_bit(CACHEFILES_CULLING, &cache->flags) ? '1' : '0',
                     (unsigned long long) cache->frun,
                     (unsigned long long) cache->fcull,
                     (unsigned long long) cache->fstop,
                     (unsigned long long) cache->brun,
                     (unsigned long long) cache->bcull,
-                    (unsigned long long) cache->bstop
-                    );
+                    (unsigned long long) cache->bstop,
+                    f_released,
+                    b_released);
 
        if (n > buflen)
                return -EMSGSIZE;
index 675a3332d72fad1eed4cba643959dbb9dabde051..861d611b8c05870063984b9736c4bdb177dc3f3f 100644 (file)
@@ -291,15 +291,8 @@ static void cachefiles_drop_object(struct fscache_object *_object)
        }
 
        /* note that the object is now inactive */
-       if (test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags)) {
-               write_lock(&cache->active_lock);
-               if (!test_and_clear_bit(CACHEFILES_OBJECT_ACTIVE,
-                                       &object->flags))
-                       BUG();
-               rb_erase(&object->active_node, &cache->active_nodes);
-               wake_up_bit(&object->flags, CACHEFILES_OBJECT_ACTIVE);
-               write_unlock(&cache->active_lock);
-       }
+       if (test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags))
+               cachefiles_mark_object_inactive(cache, object);
 
        dput(object->dentry);
        object->dentry = NULL;
index 9c4b737a54df64bb15df446c5c359e594ce5b3a3..2fcde1a34b7ce8357c82c2202ccb2f1b8fcfde21 100644 (file)
@@ -66,6 +66,8 @@ struct cachefiles_cache {
        struct rb_root                  active_nodes;   /* active nodes (can't be culled) */
        rwlock_t                        active_lock;    /* lock for active_nodes */
        atomic_t                        gravecounter;   /* graveyard uniquifier */
+       atomic_t                        f_released;     /* number of objects released lately */
+       atomic_long_t                   b_released;     /* number of blocks released lately */
        unsigned                        frun_percent;   /* when to stop culling (% files) */
        unsigned                        fcull_percent;  /* when to start culling (% files) */
        unsigned                        fstop_percent;  /* when to stop allocating (% files) */
@@ -157,6 +159,8 @@ extern char *cachefiles_cook_key(const u8 *raw, int keylen, uint8_t type);
 /*
  * namei.c
  */
+extern void cachefiles_mark_object_inactive(struct cachefiles_cache *cache,
+                                           struct cachefiles_object *object);
 extern int cachefiles_delete_object(struct cachefiles_cache *cache,
                                    struct cachefiles_object *object);
 extern int cachefiles_walk_to_object(struct cachefiles_object *parent,
index 1c2334c163ddea17f99298b89ec58e09c51d3bd0..4ae75006e73bd20e04562240e1e85e63b63f74a4 100644 (file)
@@ -257,6 +257,28 @@ requeue:
        return -ETIMEDOUT;
 }
 
+/*
+ * Mark an object as being inactive.
+ */
+void cachefiles_mark_object_inactive(struct cachefiles_cache *cache,
+                                    struct cachefiles_object *object)
+{
+       write_lock(&cache->active_lock);
+       rb_erase(&object->active_node, &cache->active_nodes);
+       clear_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags);
+       write_unlock(&cache->active_lock);
+
+       wake_up_bit(&object->flags, CACHEFILES_OBJECT_ACTIVE);
+
+       /* This object can now be culled, so we need to let the daemon know
+        * that there is something it can remove if it needs to.
+        */
+       atomic_long_add(d_backing_inode(object->dentry)->i_blocks,
+                       &cache->b_released);
+       if (atomic_inc_return(&cache->f_released))
+               cachefiles_state_changed(cache);
+}
+
 /*
  * delete an object representation from the cache
  * - file backed objects are unlinked
@@ -684,11 +706,7 @@ mark_active_timed_out:
 
 check_error:
        _debug("check error %d", ret);
-       write_lock(&cache->active_lock);
-       rb_erase(&object->active_node, &cache->active_nodes);
-       clear_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags);
-       wake_up_bit(&object->flags, CACHEFILES_OBJECT_ACTIVE);
-       write_unlock(&cache->active_lock);
+       cachefiles_mark_object_inactive(cache, object);
 release_dentry:
        dput(object->dentry);
        object->dentry = NULL;