From: therealneworld@gmail.com Date: Thu, 2 Jun 2011 12:27:02 +0000 (+0200) Subject: add "remember" option X-Git-Tag: fuse_2_9_0~32 X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=94c2b63955271d039c62e7c966aa82c9565d28ec;p=qemu-gpiodev%2Flibfuse.git add "remember" option This works similar to "noforget" except that eventually the node will be allowed to expire from the cache. --- diff --git a/ChangeLog b/ChangeLog index 572425c..db029ff 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2011-06-02 Miklos Szeredi + + * 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 * 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 #include #include +#include #include #include #include @@ -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 #include @@ -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: *;