From: <the...@gm...> - 2011-05-26 16:11:53
|
Here is the updated diff with Miklos' suggestions taken into account. I left the /10 for calculating the delay between cleans, but as I said it was a bit arbitrary so it can be changed as desired. diff --git a/include/fuse.h b/include/fuse.h index b0e6f5b..90581cc 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 an error number 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..e443686 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> @@ -58,6 +59,7 @@ struct fuse_config { double ac_attr_timeout; int ac_attr_timeout_set; int noforget; + int remember; int nopath; int debug; int hard_remove; @@ -128,6 +130,7 @@ struct fuse { int pagesize; struct list_head partial_slabs; struct list_head full_slabs; + pthread_t prune_thread; }; struct lock { @@ -151,6 +154,7 @@ struct node { int open_count; struct timespec stat_updated; struct timespec mtime; + struct timespec last_lookup; off_t size; struct lock *locks; unsigned int is_hidden : 1; @@ -454,7 +458,7 @@ static struct node *get_node_nocheck(struct fuse *f, fuse_ino_t nodeid) return NULL; } -static struct node *get_node(struct fuse *f, fuse_ino_t nodeid) +static struct node *get_node_noremember(struct fuse *f, fuse_ino_t nodeid) { struct node *node = get_node_nocheck(f, nodeid); if (!node) { @@ -465,6 +469,18 @@ 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 struct node *get_node(struct fuse *f, fuse_ino_t nodeid) +{ + struct node *node = get_node_noremember(f, nodeid); + if(f->conf.remember) + curr_time(&node->last_lookup); + return node; +} + static void free_node(struct fuse *f, struct node *node) { if (node->name != node->inline_name) @@ -774,7 +790,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.noforget || f->conf.remember) node->nlookup = 1; node->refctr = 1; node->nodeid = next_id(f); @@ -791,6 +807,8 @@ static struct node *find_node(struct fuse *f, fuse_ino_t parent, hash_id(f, node); } node->nlookup ++; + if(f->conf.remember) + curr_time(&node->last_lookup); out_err: pthread_mutex_unlock(&f->lock); return node; @@ -1141,13 +1159,8 @@ static void free_path2(struct fuse *f, fuse_ino_t nodeid1, fuse_ino_t nodeid2, free(path2); } -static void forget_node(struct fuse *f, fuse_ino_t nodeid, uint64_t nlookup) +static void do_forget_node(struct fuse *f, struct node *node, uint64_t nlookup) { - struct node *node; - if (nodeid == FUSE_ROOT_ID) - return; - pthread_mutex_lock(&f->lock); - node = get_node(f, nodeid); /* * Node may still be locked due to interrupt idiocy in open, @@ -1171,6 +1184,17 @@ static void forget_node(struct fuse *f, fuse_ino_t nodeid, uint64_t nlookup) unhash_name(f, node); unref_node(f, node); } +} + +static void forget_node(struct fuse *f, fuse_ino_t nodeid, uint64_t nlookup) +{ + struct node *node; + + if (nodeid == FUSE_ROOT_ID) + return; + pthread_mutex_lock(&f->lock); + node = get_node_noremember(f, nodeid); + do_forget_node(f, node, nlookup); pthread_mutex_unlock(&f->lock); } @@ -3832,6 +3856,51 @@ 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 a max of 10 times + * within the remember window. + */ + int min_sleep = 60, sleep_time = f->conf.remember/10; + return sleep_time > min_sleep ? sleep_time : min_sleep; +} + +int fuse_clean_cache(struct fuse *fuse) +{ + int i; + struct node *node, *next; + struct timespec now; + static int next_clean = 0; + + pthread_mutex_lock(&fuse->lock); + if (!next_clean) + next_clean = clean_delay(fuse); + + curr_time(&now); + for (i=0; i < fuse->id_table.size; ++i) { + for (node=fuse->id_table.array[i]; node != NULL; node=next) { + next = node->id_next; + if(FUSE_ROOT_ID == node->nodeid) + 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 + * + * Expect 1 because after initial lookup it is 2 and + * the kernel will reduce it to 1 when it is done + * with the node. + */ + if (node->nlookup == 1 && node->refctr == 1 + && diff_timespec(&now, &node->last_lookup) > fuse->conf.remember) + do_forget_node(fuse, node, 1); + } + } + pthread_mutex_unlock(&fuse->lock); + return next_clean; +} + static struct fuse_lowlevel_ops fuse_path_ops = { .init = fuse_lib_init, .destroy = fuse_lib_destroy, @@ -3934,11 +4003,69 @@ struct fuse_cmd *fuse_read_cmd(struct fuse *f) return cmd; } +static int curr_time_ms(void) +{ + struct timespec now; + curr_time(&now); + return (now.tv_sec * 1000) + (now.tv_nsec / 1000000); +} + +static int fuse_session_loop_remember(struct fuse *f) +{ + struct fuse_session *se = f->se; + int res = 0, timeout_ms = -1, 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; + } + + while (!fuse_session_exited(se)) { + struct fuse_chan *tmpch = ch; + struct fuse_buf fbuf = { + .mem = buf, + .size = bufsize, + }; + + if (timeout_ms >= 0) { + res = poll(&fds, 1, timeout_ms); + if (res) { + res = fuse_session_receive_buf(se, &fbuf, &tmpch); + + if (res == -EINTR) + continue; + if (res <= 0) + break; + + fuse_session_process_buf(se, &fbuf, tmpch); + timeout_ms = next_clean - curr_time_ms(); + } else + timeout_ms = -1; + } else { + timeout_ms = fuse_clean_cache(f) * 1000; + next_clean = curr_time_ms() + timeout_ms; + } + } + + free(buf); + fuse_session_reset(se); + return res < 0 ? -1 : 0; +} + int fuse_loop(struct fuse *f) { - if (f) + if (f) { + if (f->conf.remember) + return fuse_session_loop_remember(f); return fuse_session_loop(f->se); - else + } else return -1; } @@ -4020,6 +4147,7 @@ static const struct fuse_opt fuse_lib_opts[] = { 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("remember=%d", remember, 0), FUSE_LIB_OPT("nopath", nopath, 1), FUSE_LIB_OPT("intr", intr, 1), FUSE_LIB_OPT("intr_signal=%d", intr_signal, 0), @@ -4035,6 +4163,7 @@ static void fuse_lib_help(void) " -o readdir_ino try to fill in d_ino in readdir\n" " -o direct_io use direct I/O\n" " -o kernel_cache cache files in kernel\n" +" -o remember=T force file information to stay cached for T seconds (0s)\n" " -o [no]auto_cache enable caching based on modification times (off)\n" " -o umask=M set file permissions (octal)\n" " -o uid=N set file owner\n" @@ -4183,6 +4312,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) { + return pthread_create(&f->prune_thread, NULL, fuse_prune_nodes, f); + } + return 0; +} + +void fuse_stop_cleanup_thread(struct fuse *f) +{ + if (f->conf.remember) { + pthread_mutex_lock(&f->lock); + pthread_cancel(f->prune_thread); + pthread_join(f->prune_thread, NULL); + pthread_mutex_unlock(&f->lock); + } +} + 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_mt.c b/lib/fuse_mt.c index 95c3a5c..324d2a2 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: *; |