aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortherealneworld@gmail.com <therealneworld@gmail.com>2011-06-02 14:27:02 +0200
committerMiklos Szeredi <mszeredi@suse.cz>2011-06-02 14:27:02 +0200
commit94c2b63955271d039c62e7c966aa82c9565d28ec (patch)
treea47133690e8d1a5aca9351dd8803e2e5fc4c5dc3
parent4a9f6ab4805d805ac3b437c89d11ae365a0370f3 (diff)
downloadandroid_external_fuse-94c2b63955271d039c62e7c966aa82c9565d28ec.tar.gz
android_external_fuse-94c2b63955271d039c62e7c966aa82c9565d28ec.tar.bz2
android_external_fuse-94c2b63955271d039c62e7c966aa82c9565d28ec.zip
add "remember" option
This works similar to "noforget" except that eventually the node will be allowed to expire from the cache.
-rw-r--r--ChangeLog6
-rw-r--r--include/fuse.h28
-rw-r--r--lib/fuse.c185
-rw-r--r--lib/fuse_i.h2
-rw-r--r--lib/fuse_loop_mt.c51
-rw-r--r--lib/fuse_mt.c8
-rw-r--r--lib/fuse_versionscript3
7 files changed, 255 insertions, 28 deletions
diff --git a/ChangeLog b/ChangeLog
index 572425c..db029ff 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2011-06-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add "remember" option. This works similar to "noforget" except
+ that eventually the node will be allowed to expire from the cache.
+ Patch by therealneworld@gmail.com
+
2011-05-27 Miklos Szeredi <miklos@szeredi.hu>
* Check if splice/vmsplice are supported
diff --git a/include/fuse.h b/include/fuse.h
index b0e6f5b..7e52719 100644
--- a/include/fuse.h
+++ b/include/fuse.h
@@ -723,6 +723,34 @@ int fuse_is_lib_option(const char *opt);
int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
size_t op_size, void *user_data);
+/**
+ * Start the cleanup thread when using option "remember".
+ *
+ * This is done automatically by fuse_loop_mt()
+ * @param fuse struct fuse pointer for fuse instance
+ * @return 0 on success and -1 on error
+ */
+int fuse_start_cleanup_thread(struct fuse *fuse);
+
+/**
+ * Stop the cleanup thread when using option "remember".
+ *
+ * This is done automatically by fuse_loop_mt()
+ * @param fuse struct fuse pointer for fuse instance
+ */
+void fuse_stop_cleanup_thread(struct fuse *fuse);
+
+/**
+ * Iterate over cache removing stale entries
+ * use in conjunction with "-oremember"
+ *
+ * NOTE: This is already done for the standard sessions
+ *
+ * @param fuse struct fuse pointer for fuse instance
+ * @return the number of seconds until the next cleanup
+ */
+int fuse_clean_cache(struct fuse *fuse);
+
/*
* Stacking API
*/
diff --git a/lib/fuse.c b/lib/fuse.c
index b8cce23..50f3d0d 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -30,6 +30,7 @@
#include <signal.h>
#include <dlfcn.h>
#include <assert.h>
+#include <poll.h>
#include <sys/param.h>
#include <sys/uio.h>
#include <sys/time.h>
@@ -57,7 +58,7 @@ struct fuse_config {
double attr_timeout;
double ac_attr_timeout;
int ac_attr_timeout_set;
- int noforget;
+ int remember;
int nopath;
int debug;
int hard_remove;
@@ -128,6 +129,7 @@ struct fuse {
int pagesize;
struct list_head partial_slabs;
struct list_head full_slabs;
+ pthread_t prune_thread;
};
struct lock {
@@ -151,6 +153,7 @@ struct node {
int open_count;
struct timespec stat_updated;
struct timespec mtime;
+ struct timespec forget_time;
off_t size;
struct lock *locks;
unsigned int is_hidden : 1;
@@ -465,6 +468,10 @@ static struct node *get_node(struct fuse *f, fuse_ino_t nodeid)
return node;
}
+static void curr_time(struct timespec *now);
+static double diff_timespec(const struct timespec *t1,
+ const struct timespec *t2);
+
static void free_node(struct fuse *f, struct node *node)
{
if (node->name != node->inline_name)
@@ -774,7 +781,7 @@ static struct node *find_node(struct fuse *f, fuse_ino_t parent,
if (node == NULL)
goto out_err;
- if (f->conf.noforget)
+ if (f->conf.remember)
node->nlookup = 1;
node->refctr = 1;
node->nodeid = next_id(f);
@@ -1170,13 +1177,16 @@ static void forget_node(struct fuse *f, fuse_ino_t nodeid, uint64_t nlookup)
if (!node->nlookup) {
unhash_name(f, node);
unref_node(f, node);
+ } else if (node->nlookup == 1 && f->conf.remember &&
+ f->conf.remember != -1) {
+ curr_time(&node->forget_time);
}
pthread_mutex_unlock(&f->lock);
}
static void unlink_node(struct fuse *f, struct node *node)
{
- if (f->conf.noforget) {
+ if (f->conf.remember) {
assert(node->nlookup > 1);
node->nlookup--;
}
@@ -3832,6 +3842,68 @@ static void fuse_lib_poll(fuse_req_t req, fuse_ino_t ino,
reply_err(req, err);
}
+static int clean_delay(struct fuse *f)
+{
+ /*
+ * This is calculating the delay between clean runs. To
+ * reduce the number of cleans we are doing them 10 times
+ * within the remember window.
+ */
+ int min_sleep = 60;
+ int max_sleep = 3600;
+ int sleep_time = f->conf.remember / 10;
+
+ if (sleep_time > max_sleep)
+ return max_sleep;
+ if (sleep_time < min_sleep)
+ return min_sleep;
+ return sleep_time;
+}
+
+int fuse_clean_cache(struct fuse *f)
+{
+ int i;
+ struct node *node, *next;
+ struct timespec now;
+ static int next_clean;
+
+ pthread_mutex_lock(&f->lock);
+ next_clean = clean_delay(f);
+
+ curr_time(&now);
+ for (i = 0; i < f->name_table.size; ++i) {
+ for (node = f->name_table.array[i]; node; node = next) {
+ double age;
+
+ next = node->name_next;
+
+ if (node->nodeid == FUSE_ROOT_ID)
+ continue;
+
+ /* Don't forget active directories */
+ if (node->refctr > 1)
+ continue;
+
+ /*
+ * Only want to try the forget after the lookup count
+ * has been reduced to 1 and the time to keep the node
+ * around has expired
+ */
+ if (node->nlookup != 1)
+ continue;
+
+ age = diff_timespec(&now, &node->forget_time);
+ if (age > f->conf.remember) {
+ node->nlookup = 0;
+ unhash_name(f, node);
+ unref_node(f, node);
+ }
+ }
+ }
+ pthread_mutex_unlock(&f->lock);
+ return next_clean;
+}
+
static struct fuse_lowlevel_ops fuse_path_ops = {
.init = fuse_lib_init,
.destroy = fuse_lib_destroy,
@@ -3934,12 +4006,77 @@ struct fuse_cmd *fuse_read_cmd(struct fuse *f)
return cmd;
}
+static int fuse_session_loop_remember(struct fuse *f)
+{
+ struct fuse_session *se = f->se;
+ int res = 0;
+ struct timespec now;
+ time_t next_clean;
+ struct fuse_chan *ch = fuse_session_next_chan(se, NULL);
+ size_t bufsize = fuse_chan_bufsize(ch);
+ char *buf = (char *) malloc(bufsize);
+ struct pollfd fds = {
+ .fd = fuse_chan_fd(ch),
+ .events = POLLIN
+ };
+
+ if (!buf) {
+ fprintf(stderr, "fuse: failed to allocate read buffer\n");
+ return -1;
+ }
+
+ curr_time(&now);
+ next_clean = now.tv_sec;
+ while (!fuse_session_exited(se)) {
+ struct fuse_chan *tmpch = ch;
+ struct fuse_buf fbuf = {
+ .mem = buf,
+ .size = bufsize,
+ };
+ unsigned timeout;
+
+ curr_time(&now);
+ if (now.tv_sec < next_clean)
+ timeout = next_clean - now.tv_sec;
+ else
+ timeout = 0;
+
+ res = poll(&fds, 1, timeout * 1000);
+ if (res == -1) {
+ if (errno == -EINTR)
+ continue;
+ else
+ break;
+ } else if (res > 0) {
+ res = fuse_session_receive_buf(se, &fbuf, &tmpch);
+
+ if (res == -EINTR)
+ continue;
+ if (res <= 0)
+ break;
+
+ fuse_session_process_buf(se, &fbuf, tmpch);
+ } else {
+ timeout = fuse_clean_cache(f);
+ curr_time(&now);
+ next_clean = now.tv_sec + timeout;
+ }
+ }
+
+ free(buf);
+ fuse_session_reset(se);
+ return res < 0 ? -1 : 0;
+}
+
int fuse_loop(struct fuse *f)
{
- if (f)
- return fuse_session_loop(f->se);
- else
+ if (!f)
return -1;
+
+ if (f->conf.remember && f->conf.remember != -1)
+ return fuse_session_loop_remember(f);
+
+ return fuse_session_loop(f->se);
}
int fuse_invalidate(struct fuse *f, const char *path)
@@ -4019,7 +4156,8 @@ static const struct fuse_opt fuse_lib_opts[] = {
FUSE_LIB_OPT("ac_attr_timeout=%lf", ac_attr_timeout, 0),
FUSE_LIB_OPT("ac_attr_timeout=", ac_attr_timeout_set, 1),
FUSE_LIB_OPT("negative_timeout=%lf", negative_timeout, 0),
- FUSE_LIB_OPT("noforget", noforget, 1),
+ FUSE_LIB_OPT("noforget", remember, -1),
+ FUSE_LIB_OPT("remember=%u", remember, 0),
FUSE_LIB_OPT("nopath", nopath, 1),
FUSE_LIB_OPT("intr", intr, 1),
FUSE_LIB_OPT("intr_signal=%d", intr_signal, 0),
@@ -4043,7 +4181,8 @@ static void fuse_lib_help(void)
" -o negative_timeout=T cache timeout for deleted names (0.0s)\n"
" -o attr_timeout=T cache timeout for attributes (1.0s)\n"
" -o ac_attr_timeout=T auto cache timeout for attributes (attr_timeout)\n"
-" -o noforget remember inode numbers (increases memory use)\n"
+" -o noforget never forget cached inodes\n"
+" -o remember=T remember cached inodes for T seconds (0s)\n"
" -o intr allow requests to be interrupted\n"
" -o intr_signal=NUM signal to send on interrupt (%i)\n"
" -o modules=M1[:M2...] names of modules to push onto filesystem stack\n"
@@ -4183,6 +4322,36 @@ static int node_table_init(struct node_table *t)
return 0;
}
+static void *fuse_prune_nodes(void *fuse)
+{
+ struct fuse *f = fuse;
+ int sleep_time;
+
+ while(1) {
+ sleep_time = fuse_clean_cache(f);
+ sleep(sleep_time);
+ }
+ return NULL;
+}
+
+int fuse_start_cleanup_thread(struct fuse *f)
+{
+ if (f->conf.remember && f->conf.remember != -1)
+ return fuse_start_thread(&f->prune_thread, fuse_prune_nodes, f);
+
+ return 0;
+}
+
+void fuse_stop_cleanup_thread(struct fuse *f)
+{
+ if (f->conf.remember && f->conf.remember != -1) {
+ pthread_mutex_lock(&f->lock);
+ pthread_cancel(f->prune_thread);
+ pthread_mutex_unlock(&f->lock);
+ pthread_join(f->prune_thread, NULL);
+ }
+}
+
struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args,
const struct fuse_operations *op,
size_t op_size, void *user_data, int compat)
diff --git a/lib/fuse_i.h b/lib/fuse_i.h
index b715da7..dd98737 100644
--- a/lib/fuse_i.h
+++ b/lib/fuse_i.h
@@ -123,3 +123,5 @@ struct fuse *fuse_setup_common(int argc, char *argv[],
int compat);
void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeide, const void *inarg);
+
+int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg);
diff --git a/lib/fuse_loop_mt.c b/lib/fuse_loop_mt.c
index ab5fd11..b5ad1c7 100644
--- a/lib/fuse_loop_mt.c
+++ b/lib/fuse_loop_mt.c
@@ -9,6 +9,7 @@
#include "fuse_lowlevel.h"
#include "fuse_misc.h"
#include "fuse_kernel.h"
+#include "fuse_i.h"
#include <stdio.h>
#include <stdlib.h>
@@ -60,7 +61,7 @@ static void list_del_worker(struct fuse_worker *w)
next->prev = prev;
}
-static int fuse_start_thread(struct fuse_mt *mt);
+static int fuse_loop_start_thread(struct fuse_mt *mt);
static void *fuse_do_work(void *data)
{
@@ -110,7 +111,7 @@ static void *fuse_do_work(void *data)
if (!isforget)
mt->numavail--;
if (mt->numavail == 0)
- fuse_start_thread(mt);
+ fuse_loop_start_thread(mt);
pthread_mutex_unlock(&mt->lock);
fuse_session_process_buf(mt->se, &fbuf, ch);
@@ -141,27 +142,13 @@ static void *fuse_do_work(void *data)
return NULL;
}
-static int fuse_start_thread(struct fuse_mt *mt)
+int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg)
{
sigset_t oldset;
sigset_t newset;
int res;
pthread_attr_t attr;
char *stack_size;
- struct fuse_worker *w = malloc(sizeof(struct fuse_worker));
- if (!w) {
- fprintf(stderr, "fuse: failed to allocate worker structure\n");
- return -1;
- }
- memset(w, 0, sizeof(struct fuse_worker));
- w->bufsize = fuse_chan_bufsize(mt->prevch);
- w->buf = malloc(w->bufsize);
- w->mt = mt;
- if (!w->buf) {
- fprintf(stderr, "fuse: failed to allocate read buffer\n");
- free(w);
- return -1;
- }
/* Override default stack size */
pthread_attr_init(&attr);
@@ -176,12 +163,38 @@ static int fuse_start_thread(struct fuse_mt *mt)
sigaddset(&newset, SIGHUP);
sigaddset(&newset, SIGQUIT);
pthread_sigmask(SIG_BLOCK, &newset, &oldset);
- res = pthread_create(&w->thread_id, &attr, fuse_do_work, w);
+ res = pthread_create(thread_id, &attr, func, arg);
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
pthread_attr_destroy(&attr);
if (res != 0) {
fprintf(stderr, "fuse: error creating thread: %s\n",
strerror(res));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int fuse_loop_start_thread(struct fuse_mt *mt)
+{
+ int res;
+ struct fuse_worker *w = malloc(sizeof(struct fuse_worker));
+ if (!w) {
+ fprintf(stderr, "fuse: failed to allocate worker structure\n");
+ return -1;
+ }
+ memset(w, 0, sizeof(struct fuse_worker));
+ w->bufsize = fuse_chan_bufsize(mt->prevch);
+ w->buf = malloc(w->bufsize);
+ w->mt = mt;
+ if (!w->buf) {
+ fprintf(stderr, "fuse: failed to allocate read buffer\n");
+ free(w);
+ return -1;
+ }
+
+ res = fuse_start_thread(&w->thread_id, fuse_do_work, w);
+ if (res == -1) {
free(w->buf);
free(w);
return -1;
@@ -221,7 +234,7 @@ int fuse_session_loop_mt(struct fuse_session *se)
fuse_mutex_init(&mt.lock);
pthread_mutex_lock(&mt.lock);
- err = fuse_start_thread(&mt);
+ err = fuse_loop_start_thread(&mt);
pthread_mutex_unlock(&mt.lock);
if (!err) {
/* sem_wait() is interruptible */
diff --git a/lib/fuse_mt.c b/lib/fuse_mt.c
index 95c3a5c..f6dbe71 100644
--- a/lib/fuse_mt.c
+++ b/lib/fuse_mt.c
@@ -110,7 +110,13 @@ int fuse_loop_mt(struct fuse *f)
if (f == NULL)
return -1;
- return fuse_session_loop_mt(fuse_get_session(f));
+ int res = fuse_start_cleanup_thread(f);
+ if (res)
+ return -1;
+
+ res = fuse_session_loop_mt(fuse_get_session(f));
+ fuse_stop_cleanup_thread(f);
+ return res;
}
FUSE_SYMVER(".symver fuse_loop_mt_proc,__fuse_loop_mt@");
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
index 4694575..96403c5 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -191,6 +191,9 @@ FUSE_2.9 {
fuse_reply_data;
fuse_session_process_buf;
fuse_session_receive_buf;
+ fuse_start_cleanup_thread;
+ fuse_stop_cleanup_thread;
+ fuse_clean_cache;
local:
*;