From: Miklos S. <mi...@sz...> - 2011-05-30 11:47:44
|
the...@gm... writes: > On Fri, May 27, 2011 at 3:49 PM, <the...@gm...> wrote: >> On Fri, May 27, 2011 at 12:44 PM, Miklos Szeredi <mi...@sz...> wrote: >>> the...@gm... writes: >>> >>>> 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. >>> >>> Ok, first I was confused by that /10, thinking it has something to do >>> with the second to minute conversion. >>> >>> Thanks for the patch. I looked it in detail and fixed quite a few small >>> issues. Here's an updated patch, can you please give it a test? >> > > I looked a little more at your changes and I noticed that you are only > updating the last_lookup when forget is firing. This causes the node > to stay around the remember time after the forget. I was trying to get > the node to stay around for the remember time after its last access. > Both will have somewhat similar effects, but with yours it will force > the node to stay in memory even if it hasn't been accessed for a long > time. If you prefer this implementation I would say the variable > last_lookup should probably be renamed to something like forget_time. Here again I was thinking about performance issues, and saving a syscall from each and every operation. I see now that (at least on recent linux) there's no syscall associated with the time functions, so it shoudln't really matter. But it doesn't hurt to do it on just the last forget. Updated patch below. Thanks, Miklos --- include/fuse.h | 28 +++++++ lib/fuse.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++--- lib/fuse_i.h | 2 lib/fuse_loop_mt.c | 51 ++++++++----- lib/fuse_mt.c | 8 +- lib/fuse_versionscript | 3 6 files changed, 249 insertions(+), 28 deletions(-) Index: fuse/include/fuse.h =================================================================== --- fuse.orig/include/fuse.h 2011-05-27 18:43:58.000000000 +0200 +++ fuse/include/fuse.h 2011-05-30 13:28:28.000000000 +0200 @@ -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 */ Index: fuse/lib/fuse.c =================================================================== --- fuse.orig/lib/fuse.c 2011-05-27 18:43:58.000000000 +0200 +++ fuse/lib/fuse.c 2011-05-30 13:45:20.000000000 +0200 @@ -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 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 fus 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, 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 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 fu 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_op 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_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) Index: fuse/lib/fuse_mt.c =================================================================== --- fuse.orig/lib/fuse_mt.c 2011-05-27 18:43:58.000000000 +0200 +++ fuse/lib/fuse_mt.c 2011-05-30 13:28:28.000000000 +0200 @@ -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@"); Index: fuse/lib/fuse_versionscript =================================================================== --- fuse.orig/lib/fuse_versionscript 2011-05-27 18:43:58.000000000 +0200 +++ fuse/lib/fuse_versionscript 2011-05-30 13:28:28.000000000 +0200 @@ -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: *; Index: fuse/lib/fuse_i.h =================================================================== --- fuse.orig/lib/fuse_i.h 2011-05-27 18:43:58.000000000 +0200 +++ fuse/lib/fuse_i.h 2011-05-30 13:28:28.000000000 +0200 @@ -123,3 +123,5 @@ struct fuse *fuse_setup_common(int argc, 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); Index: fuse/lib/fuse_loop_mt.c =================================================================== --- fuse.orig/lib/fuse_loop_mt.c 2011-05-27 18:43:58.000000000 +0200 +++ fuse/lib/fuse_loop_mt.c 2011-05-30 13:28:28.000000000 +0200 @@ -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_ 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 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_ses 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 */ |
From: <the...@gm...> - 2011-05-31 18:48:54
|
On Mon, May 30, 2011 at 7:47 AM, Miklos Szeredi <mi...@sz...> wrote: > Here again I was thinking about performance issues, and saving a syscall > from each and every operation. I see now that (at least on recent > linux) there's no syscall associated with the time functions, so it > shoudln't really matter. But it doesn't hurt to do it on just the last > forget. Just to try and clarify what I was saying before. The original patch didn't update the time stamp on a forget. It was updating the time stamp on a find of the nodeid. I'll admit it might have been updating the time stamp to often, but I was trying to do it on user based activity. > > Updated patch below. > The patch works fine. Thanks, -nate |
From: Miklos S. <mi...@sz...> - 2011-06-07 12:38:23
|
the...@gm... writes: > On Mon, May 30, 2011 at 7:47 AM, Miklos Szeredi <mi...@sz...> wrote: >> Here again I was thinking about performance issues, and saving a syscall >> from each and every operation. I see now that (at least on recent >> linux) there's no syscall associated with the time functions, so it >> shoudln't really matter. But it doesn't hurt to do it on just the last >> forget. > > Just to try and clarify what I was saying before. The original patch > didn't update the time stamp on a forget. It was updating the time > stamp on a find of the nodeid. I'll admit it might have been updating > the time stamp to often, but I was trying to do it on user based > activity. > >> >> Updated patch below. >> > > The patch works fine. Thanks. Committed to the fuse tree. Miklos |
From: <the...@gm...> - 2011-06-10 22:44:07
|
> > Thanks. Committed to the fuse tree. > Thanks for committing the patch. Here is a patch to add an lru for the remember option. To prevent the lru from having any overhead for those not using remember I created a separate struct for node_lru. The lru will only contain nodes with a nlookup of 1. I did add an extra helper function, remember_enabled(), to clean up all the places where there were checks to see if remember was enabled. The way I implemented this function does change the semantics a bit. Previously -1 meant remember was disabled but not anything less than -1. Let me know what is thought of this patch. Signed-off-by: Nate Clark<na...@ne...> diff --git a/lib/fuse.c b/lib/fuse.c index 50f3d0d..d9ab3a6 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -101,6 +101,11 @@ struct node_table { size_t split; }; +struct node_lru_table { + struct node_lru *head; + struct node_lru *tail; +}; + struct list_head { struct list_head *next; struct list_head *prev; @@ -116,6 +121,7 @@ struct fuse { struct fuse_session *se; struct node_table name_table; struct node_table id_table; + struct node_lru_table lru_table; fuse_ino_t ctr; unsigned int generation; unsigned int hidectr; @@ -163,6 +169,12 @@ struct node { char inline_name[32]; }; +struct node_lru { + struct node node; + struct node_lru *next; + struct node_lru *prev; +}; + struct fuse_dh { pthread_mutex_t lock; struct fuse *fuse; @@ -341,6 +353,10 @@ static struct node_slab *node_to_slab(struct fuse *f, struct node *node) return (struct node_slab *) (((uintptr_t) node) & ~((uintptr_t) f->pagesize - 1)); } +static inline int remember_enabled(struct fuse *f) { + return f->conf.remember > 0; +} + static int alloc_slab(struct fuse *f) { void *mem; @@ -348,6 +364,9 @@ static int alloc_slab(struct fuse *f) char *start; size_t num; size_t i; + size_t node_size; + node_size = remember_enabled(f) ? sizeof(struct node_lru) + : sizeof(struct node); mem = mmap(NULL, f->pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); @@ -358,13 +377,13 @@ static int alloc_slab(struct fuse *f) slab = mem; init_list_head(&slab->freelist); slab->used = 0; - num = (f->pagesize - sizeof(struct node_slab)) / sizeof(struct node); + num = (f->pagesize - sizeof(struct node_slab)) / node_size; - start = (char *) mem + f->pagesize - num * sizeof(struct node); + start = (char *) mem + f->pagesize - num * node_size; for (i = 0; i < num; i++) { struct list_head *n; - n = (struct list_head *) (start + i * sizeof(struct node)); + n = (struct list_head *) (start + i * node_size); list_add_tail(n, &slab->freelist); } list_add_tail(&slab->list, &f->partial_slabs); @@ -423,8 +442,11 @@ static void free_node_mem(struct fuse *f, struct node *node) #else static struct node *alloc_node(struct fuse *f) { - (void) f; - return (struct node *) calloc(1, sizeof(struct node)); + size_t node_size; + node_size = remember_enabled(f) ? sizeof(struct node_lru) + : sizeof(struct node); + + return (struct node *) calloc(1, node_size); } static void free_node_mem(struct fuse *f, struct node *node) @@ -472,6 +494,66 @@ static void curr_time(struct timespec *now); static double diff_timespec(const struct timespec *t1, const struct timespec *t2); +static void lru_remove_node(struct fuse *f, struct node_lru *node) +{ + if (f->lru_table.head == node) + f->lru_table.head = node->next; + + if (f->lru_table.tail == node) + f->lru_table.tail = node->prev; + + if (node->prev) + node->prev->next = node->next; + + if (node->next) + node->next->prev = node->prev; + + node->next = NULL; + node->prev = NULL; +} + +static void lru_append_node(struct fuse *f, struct node_lru *node) +{ + if (!f->lru_table.head) { + f->lru_table.head = node; + node->prev = NULL; + } else { + node->prev = f->lru_table.tail; + f->lru_table.tail->next = node; + } + f->lru_table.tail = node; + node->next = NULL; +} + +static void set_forget_time(struct fuse *f, struct node *node) +{ + if (remember_enabled(f)) { + struct node_lru *lnode = (struct node_lru *) node; + if (f->lru_table.tail != lnode) { + lru_remove_node(f, lnode); + lru_append_node(f, lnode); + } + curr_time(&node->forget_time); + } +} + +static void init_node_lru(struct fuse *f, struct node *node) +{ + if (remember_enabled(f)) { + struct node_lru *lnode = (struct node_lru *) node; + lnode->next = NULL; + lnode->prev = NULL; + } +} + +static void remove_node_lru(struct fuse *f, struct node *node) +{ + if (remember_enabled(f)) { + struct node_lru *lnode = (struct node_lru *) node; + lru_remove_node(f, lnode); + } +} + static void free_node(struct fuse *f, struct node *node) { if (node->name != node->inline_name) @@ -729,6 +811,7 @@ static void delete_node(struct fuse *f, struct node *node) assert(node->treelock == 0); assert(!node->name); + remove_node_lru(f, node); unhash_id(f, node); free_node(f, node); } @@ -796,7 +879,11 @@ static struct node *find_node(struct fuse *f, fuse_ino_t parent, goto out_err; } hash_id(f, node); + init_node_lru(f, node); + } else { + remove_node_lru(f, node); } + node->nlookup ++; out_err: pthread_mutex_unlock(&f->lock); @@ -1177,9 +1264,8 @@ 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); + } else if (node->nlookup == 1) { + set_forget_time(f, node); } pthread_mutex_unlock(&f->lock); } @@ -3862,46 +3948,37 @@ static int clean_delay(struct fuse *f) int fuse_clean_cache(struct fuse *f) { - int i; - struct node *node, *next; + struct node_lru *lnode, *next; + struct node *node; 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; + for (lnode = f->lru_table.head; lnode; lnode = next) { + double age; - if (node->nodeid == FUSE_ROOT_ID) - continue; + next = lnode->next; + node = &lnode->node; - /* Don't forget active directories */ - if (node->refctr > 1) - continue; + age = diff_timespec(&now, &node->forget_time); + if (age <= f->conf.remember) + break; - /* - * 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; + assert(node->nlookup == 1); - age = diff_timespec(&now, &node->forget_time); - if (age > f->conf.remember) { - node->nlookup = 0; - unhash_name(f, node); - unref_node(f, node); - } - } + /* Don't forget active directories */ + if (node->refctr > 1) + continue; + + node->nlookup = 0; + unhash_name(f, node); + unref_node(f, node); } pthread_mutex_unlock(&f->lock); - return next_clean; + + return clean_delay(f); } static struct fuse_lowlevel_ops fuse_path_ops = { @@ -4073,7 +4150,7 @@ int fuse_loop(struct fuse *f) if (!f) return -1; - if (f->conf.remember && f->conf.remember != -1) + if (remember_enabled(f)) return fuse_session_loop_remember(f); return fuse_session_loop(f->se); @@ -4336,7 +4413,7 @@ static void *fuse_prune_nodes(void *fuse) int fuse_start_cleanup_thread(struct fuse *f) { - if (f->conf.remember && f->conf.remember != -1) + if (remember_enabled(f)) return fuse_start_thread(&f->prune_thread, fuse_prune_nodes, f); return 0; @@ -4344,7 +4421,7 @@ int fuse_start_cleanup_thread(struct fuse *f) void fuse_stop_cleanup_thread(struct fuse *f) { - if (f->conf.remember && f->conf.remember != -1) { + if (remember_enabled(f)) { pthread_mutex_lock(&f->lock); pthread_cancel(f->prune_thread); pthread_mutex_unlock(&f->lock); |
From: <the...@gm...> - 2011-06-13 15:42:54
|
Sorry accidentally hit reply instead of reply-all. Didn't mean to send the patch to Miklos only. -nate On Mon, Jun 13, 2011 at 11:40 AM, <the...@gm...> wrote: > Here is a slightly updated patch which also moves forget_time to > struct node_lru since it isn't needed outside of that context. > > Signed-off-by: Nate Clark<na...@ne...> > diff --git a/lib/fuse.c b/lib/fuse.c index 50f3d0d..41e66fd 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -101,6 +101,11 @@ struct node_table { size_t split; }; +struct node_lru_table { + struct node_lru *head; + struct node_lru *tail; +}; + struct list_head { struct list_head *next; struct list_head *prev; @@ -116,6 +121,7 @@ struct fuse { struct fuse_session *se; struct node_table name_table; struct node_table id_table; + struct node_lru_table lru_table; fuse_ino_t ctr; unsigned int generation; unsigned int hidectr; @@ -153,7 +159,6 @@ 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; @@ -163,6 +168,13 @@ struct node { char inline_name[32]; }; +struct node_lru { + struct node node; + struct node_lru *next; + struct node_lru *prev; + struct timespec forget_time; +}; + struct fuse_dh { pthread_mutex_t lock; struct fuse *fuse; @@ -341,6 +353,10 @@ static struct node_slab *node_to_slab(struct fuse *f, struct node *node) return (struct node_slab *) (((uintptr_t) node) & ~((uintptr_t) f->pagesize - 1)); } +static inline int remember_enabled(struct fuse *f) { + return f->conf.remember > 0; +} + static int alloc_slab(struct fuse *f) { void *mem; @@ -348,6 +364,9 @@ static int alloc_slab(struct fuse *f) char *start; size_t num; size_t i; + size_t node_size; + node_size = remember_enabled(f) ? sizeof(struct node_lru) + : sizeof(struct node); mem = mmap(NULL, f->pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); @@ -358,13 +377,13 @@ static int alloc_slab(struct fuse *f) slab = mem; init_list_head(&slab->freelist); slab->used = 0; - num = (f->pagesize - sizeof(struct node_slab)) / sizeof(struct node); + num = (f->pagesize - sizeof(struct node_slab)) / node_size; - start = (char *) mem + f->pagesize - num * sizeof(struct node); + start = (char *) mem + f->pagesize - num * node_size; for (i = 0; i < num; i++) { struct list_head *n; - n = (struct list_head *) (start + i * sizeof(struct node)); + n = (struct list_head *) (start + i * node_size); list_add_tail(n, &slab->freelist); } list_add_tail(&slab->list, &f->partial_slabs); @@ -423,8 +442,11 @@ static void free_node_mem(struct fuse *f, struct node *node) #else static struct node *alloc_node(struct fuse *f) { - (void) f; - return (struct node *) calloc(1, sizeof(struct node)); + size_t node_size; + node_size = remember_enabled(f) ? sizeof(struct node_lru) + : sizeof(struct node); + + return (struct node *) calloc(1, node_size); } static void free_node_mem(struct fuse *f, struct node *node) @@ -472,6 +494,66 @@ static void curr_time(struct timespec *now); static double diff_timespec(const struct timespec *t1, const struct timespec *t2); +static void lru_remove_node(struct fuse *f, struct node_lru *node) +{ + if (f->lru_table.head == node) + f->lru_table.head = node->next; + + if (f->lru_table.tail == node) + f->lru_table.tail = node->prev; + + if (node->prev) + node->prev->next = node->next; + + if (node->next) + node->next->prev = node->prev; + + node->next = NULL; + node->prev = NULL; +} + +static void lru_append_node(struct fuse *f, struct node_lru *node) +{ + if (!f->lru_table.head) { + f->lru_table.head = node; + node->prev = NULL; + } else { + node->prev = f->lru_table.tail; + f->lru_table.tail->next = node; + } + f->lru_table.tail = node; + node->next = NULL; +} + +static void set_forget_time(struct fuse *f, struct node *node) +{ + if (remember_enabled(f)) { + struct node_lru *lnode = (struct node_lru *) node; + if (f->lru_table.tail != lnode) { + lru_remove_node(f, lnode); + lru_append_node(f, lnode); + } + curr_time(&lnode->forget_time); + } +} + +static void init_node_lru(struct fuse *f, struct node *node) +{ + if (remember_enabled(f)) { + struct node_lru *lnode = (struct node_lru *) node; + lnode->next = NULL; + lnode->prev = NULL; + } +} + +static void remove_node_lru(struct fuse *f, struct node *node) +{ + if (remember_enabled(f)) { + struct node_lru *lnode = (struct node_lru *) node; + lru_remove_node(f, lnode); + } +} + static void free_node(struct fuse *f, struct node *node) { if (node->name != node->inline_name) @@ -729,6 +811,7 @@ static void delete_node(struct fuse *f, struct node *node) assert(node->treelock == 0); assert(!node->name); + remove_node_lru(f, node); unhash_id(f, node); free_node(f, node); } @@ -796,7 +879,11 @@ static struct node *find_node(struct fuse *f, fuse_ino_t parent, goto out_err; } hash_id(f, node); + init_node_lru(f, node); + } else { + remove_node_lru(f, node); } + node->nlookup ++; out_err: pthread_mutex_unlock(&f->lock); @@ -1177,9 +1264,8 @@ 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); + } else if (node->nlookup == 1) { + set_forget_time(f, node); } pthread_mutex_unlock(&f->lock); } @@ -3862,46 +3948,37 @@ static int clean_delay(struct fuse *f) int fuse_clean_cache(struct fuse *f) { - int i; - struct node *node, *next; + struct node_lru *lnode, *next; + struct node *node; 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; + for (lnode = f->lru_table.head; lnode; lnode = next) { + double age; - if (node->nodeid == FUSE_ROOT_ID) - continue; + next = lnode->next; + node = &lnode->node; - /* Don't forget active directories */ - if (node->refctr > 1) - continue; + age = diff_timespec(&now, &lnode->forget_time); + if (age <= f->conf.remember) + break; - /* - * 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; + assert(node->nlookup == 1); - age = diff_timespec(&now, &node->forget_time); - if (age > f->conf.remember) { - node->nlookup = 0; - unhash_name(f, node); - unref_node(f, node); - } - } + /* Don't forget active directories */ + if (node->refctr > 1) + continue; + + node->nlookup = 0; + unhash_name(f, node); + unref_node(f, node); } pthread_mutex_unlock(&f->lock); - return next_clean; + + return clean_delay(f); } static struct fuse_lowlevel_ops fuse_path_ops = { @@ -4073,7 +4150,7 @@ int fuse_loop(struct fuse *f) if (!f) return -1; - if (f->conf.remember && f->conf.remember != -1) + if (remember_enabled(f)) return fuse_session_loop_remember(f); return fuse_session_loop(f->se); @@ -4336,7 +4413,7 @@ static void *fuse_prune_nodes(void *fuse) int fuse_start_cleanup_thread(struct fuse *f) { - if (f->conf.remember && f->conf.remember != -1) + if (remember_enabled(f)) return fuse_start_thread(&f->prune_thread, fuse_prune_nodes, f); return 0; @@ -4344,7 +4421,7 @@ int fuse_start_cleanup_thread(struct fuse *f) void fuse_stop_cleanup_thread(struct fuse *f) { - if (f->conf.remember && f->conf.remember != -1) { + if (remember_enabled(f)) { pthread_mutex_lock(&f->lock); pthread_cancel(f->prune_thread); pthread_mutex_unlock(&f->lock); |