mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-23 20:24:12 +08:00
CacheFiles: Provide read-and-reset release counters for cachefilesd
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>
This commit is contained in:
parent
92e963f50f
commit
a5b3a80b89
@ -162,6 +162,8 @@ static ssize_t cachefiles_daemon_read(struct file *file, char __user *_buffer,
|
|||||||
size_t buflen, loff_t *pos)
|
size_t buflen, loff_t *pos)
|
||||||
{
|
{
|
||||||
struct cachefiles_cache *cache = file->private_data;
|
struct cachefiles_cache *cache = file->private_data;
|
||||||
|
unsigned long long b_released;
|
||||||
|
unsigned f_released;
|
||||||
char buffer[256];
|
char buffer[256];
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
@ -174,6 +176,8 @@ static ssize_t cachefiles_daemon_read(struct file *file, char __user *_buffer,
|
|||||||
cachefiles_has_space(cache, 0, 0);
|
cachefiles_has_space(cache, 0, 0);
|
||||||
|
|
||||||
/* summarise */
|
/* 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);
|
clear_bit(CACHEFILES_STATE_CHANGED, &cache->flags);
|
||||||
|
|
||||||
n = snprintf(buffer, sizeof(buffer),
|
n = snprintf(buffer, sizeof(buffer),
|
||||||
@ -183,15 +187,18 @@ static ssize_t cachefiles_daemon_read(struct file *file, char __user *_buffer,
|
|||||||
" fstop=%llx"
|
" fstop=%llx"
|
||||||
" brun=%llx"
|
" brun=%llx"
|
||||||
" bcull=%llx"
|
" bcull=%llx"
|
||||||
" bstop=%llx",
|
" bstop=%llx"
|
||||||
|
" freleased=%x"
|
||||||
|
" breleased=%llx",
|
||||||
test_bit(CACHEFILES_CULLING, &cache->flags) ? '1' : '0',
|
test_bit(CACHEFILES_CULLING, &cache->flags) ? '1' : '0',
|
||||||
(unsigned long long) cache->frun,
|
(unsigned long long) cache->frun,
|
||||||
(unsigned long long) cache->fcull,
|
(unsigned long long) cache->fcull,
|
||||||
(unsigned long long) cache->fstop,
|
(unsigned long long) cache->fstop,
|
||||||
(unsigned long long) cache->brun,
|
(unsigned long long) cache->brun,
|
||||||
(unsigned long long) cache->bcull,
|
(unsigned long long) cache->bcull,
|
||||||
(unsigned long long) cache->bstop
|
(unsigned long long) cache->bstop,
|
||||||
);
|
f_released,
|
||||||
|
b_released);
|
||||||
|
|
||||||
if (n > buflen)
|
if (n > buflen)
|
||||||
return -EMSGSIZE;
|
return -EMSGSIZE;
|
||||||
|
@ -291,15 +291,8 @@ static void cachefiles_drop_object(struct fscache_object *_object)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* note that the object is now inactive */
|
/* note that the object is now inactive */
|
||||||
if (test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags)) {
|
if (test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags))
|
||||||
write_lock(&cache->active_lock);
|
cachefiles_mark_object_inactive(cache, object);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
dput(object->dentry);
|
dput(object->dentry);
|
||||||
object->dentry = NULL;
|
object->dentry = NULL;
|
||||||
|
@ -66,6 +66,8 @@ struct cachefiles_cache {
|
|||||||
struct rb_root active_nodes; /* active nodes (can't be culled) */
|
struct rb_root active_nodes; /* active nodes (can't be culled) */
|
||||||
rwlock_t active_lock; /* lock for active_nodes */
|
rwlock_t active_lock; /* lock for active_nodes */
|
||||||
atomic_t gravecounter; /* graveyard uniquifier */
|
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 frun_percent; /* when to stop culling (% files) */
|
||||||
unsigned fcull_percent; /* when to start culling (% files) */
|
unsigned fcull_percent; /* when to start culling (% files) */
|
||||||
unsigned fstop_percent; /* when to stop allocating (% 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
|
* 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,
|
extern int cachefiles_delete_object(struct cachefiles_cache *cache,
|
||||||
struct cachefiles_object *object);
|
struct cachefiles_object *object);
|
||||||
extern int cachefiles_walk_to_object(struct cachefiles_object *parent,
|
extern int cachefiles_walk_to_object(struct cachefiles_object *parent,
|
||||||
|
@ -257,6 +257,28 @@ requeue:
|
|||||||
return -ETIMEDOUT;
|
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
|
* delete an object representation from the cache
|
||||||
* - file backed objects are unlinked
|
* - file backed objects are unlinked
|
||||||
@ -684,11 +706,7 @@ mark_active_timed_out:
|
|||||||
|
|
||||||
check_error:
|
check_error:
|
||||||
_debug("check error %d", ret);
|
_debug("check error %d", ret);
|
||||||
write_lock(&cache->active_lock);
|
cachefiles_mark_object_inactive(cache, object);
|
||||||
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);
|
|
||||||
release_dentry:
|
release_dentry:
|
||||||
dput(object->dentry);
|
dput(object->dentry);
|
||||||
object->dentry = NULL;
|
object->dentry = NULL;
|
||||||
|
Loading…
Reference in New Issue
Block a user