aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fuse
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@suse.cz>2010-12-07 14:16:56 -0500
committerMiklos Szeredi <mszeredi@suse.cz>2010-12-07 14:16:56 -0500
commit02c048b919455aaa38628563cdcc2e691c8a9f53 (patch)
tree1f98cd9fab564b6df8869a60f36e82d5d20f14f3 /fs/fuse
parent07e77dca8a1f17a724a9b7449f0ca02e70e9d057 (diff)
fuse: allow batching of FORGET requests
Terje Malmedal reports that a fuse filesystem with 32 million inodes on a machine with lots of memory can take up to 30 minutes to process FORGET requests when all those inodes are evicted from the icache. To solve this, create a BATCH_FORGET request that allows up to about 8000 FORGET requests to be sent in a single message. This request is only sent if userspace supports interface version 7.16 or later, otherwise fall back to sending individual FORGET messages. Reported-by: Terje Malmedal <terje.malmedal@usit.uio.no> Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Diffstat (limited to 'fs/fuse')
-rw-r--r--fs/fuse/dev.c92
-rw-r--r--fs/fuse/fuse_i.h3
2 files changed, 82 insertions, 13 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index fed65303eeeb..cf8d28d1fbad 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -254,8 +254,8 @@ static void queue_request(struct fuse_conn *fc, struct fuse_req *req)
254void fuse_queue_forget(struct fuse_conn *fc, struct fuse_forget_link *forget, 254void fuse_queue_forget(struct fuse_conn *fc, struct fuse_forget_link *forget,
255 u64 nodeid, u64 nlookup) 255 u64 nodeid, u64 nlookup)
256{ 256{
257 forget->nodeid = nodeid; 257 forget->forget_one.nodeid = nodeid;
258 forget->nlookup = nlookup; 258 forget->forget_one.nlookup = nlookup;
259 259
260 spin_lock(&fc->lock); 260 spin_lock(&fc->lock);
261 fc->forget_list_tail->next = forget; 261 fc->forget_list_tail->next = forget;
@@ -974,15 +974,26 @@ __releases(fc->lock)
974 return err ? err : reqsize; 974 return err ? err : reqsize;
975} 975}
976 976
977static struct fuse_forget_link *dequeue_forget(struct fuse_conn *fc) 977static struct fuse_forget_link *dequeue_forget(struct fuse_conn *fc,
978 unsigned max,
979 unsigned *countp)
978{ 980{
979 struct fuse_forget_link *forget = fc->forget_list_head.next; 981 struct fuse_forget_link *head = fc->forget_list_head.next;
982 struct fuse_forget_link **newhead = &head;
983 unsigned count;
980 984
981 fc->forget_list_head.next = forget->next; 985 for (count = 0; *newhead != NULL && count < max; count++)
986 newhead = &(*newhead)->next;
987
988 fc->forget_list_head.next = *newhead;
989 *newhead = NULL;
982 if (fc->forget_list_head.next == NULL) 990 if (fc->forget_list_head.next == NULL)
983 fc->forget_list_tail = &fc->forget_list_head; 991 fc->forget_list_tail = &fc->forget_list_head;
984 992
985 return forget; 993 if (countp != NULL)
994 *countp = count;
995
996 return head;
986} 997}
987 998
988static int fuse_read_single_forget(struct fuse_conn *fc, 999static int fuse_read_single_forget(struct fuse_conn *fc,
@@ -991,13 +1002,13 @@ static int fuse_read_single_forget(struct fuse_conn *fc,
991__releases(fc->lock) 1002__releases(fc->lock)
992{ 1003{
993 int err; 1004 int err;
994 struct fuse_forget_link *forget = dequeue_forget(fc); 1005 struct fuse_forget_link *forget = dequeue_forget(fc, 1, NULL);
995 struct fuse_forget_in arg = { 1006 struct fuse_forget_in arg = {
996 .nlookup = forget->nlookup, 1007 .nlookup = forget->forget_one.nlookup,
997 }; 1008 };
998 struct fuse_in_header ih = { 1009 struct fuse_in_header ih = {
999 .opcode = FUSE_FORGET, 1010 .opcode = FUSE_FORGET,
1000 .nodeid = forget->nodeid, 1011 .nodeid = forget->forget_one.nodeid,
1001 .unique = fuse_get_unique(fc), 1012 .unique = fuse_get_unique(fc),
1002 .len = sizeof(ih) + sizeof(arg), 1013 .len = sizeof(ih) + sizeof(arg),
1003 }; 1014 };
@@ -1018,6 +1029,65 @@ __releases(fc->lock)
1018 return ih.len; 1029 return ih.len;
1019} 1030}
1020 1031
1032static int fuse_read_batch_forget(struct fuse_conn *fc,
1033 struct fuse_copy_state *cs, size_t nbytes)
1034__releases(fc->lock)
1035{
1036 int err;
1037 unsigned max_forgets;
1038 unsigned count;
1039 struct fuse_forget_link *head;
1040 struct fuse_batch_forget_in arg = { .count = 0 };
1041 struct fuse_in_header ih = {
1042 .opcode = FUSE_BATCH_FORGET,
1043 .unique = fuse_get_unique(fc),
1044 .len = sizeof(ih) + sizeof(arg),
1045 };
1046
1047 if (nbytes < ih.len) {
1048 spin_unlock(&fc->lock);
1049 return -EINVAL;
1050 }
1051
1052 max_forgets = (nbytes - ih.len) / sizeof(struct fuse_forget_one);
1053 head = dequeue_forget(fc, max_forgets, &count);
1054 spin_unlock(&fc->lock);
1055
1056 arg.count = count;
1057 ih.len += count * sizeof(struct fuse_forget_one);
1058 err = fuse_copy_one(cs, &ih, sizeof(ih));
1059 if (!err)
1060 err = fuse_copy_one(cs, &arg, sizeof(arg));
1061
1062 while (head) {
1063 struct fuse_forget_link *forget = head;
1064
1065 if (!err) {
1066 err = fuse_copy_one(cs, &forget->forget_one,
1067 sizeof(forget->forget_one));
1068 }
1069 head = forget->next;
1070 kfree(forget);
1071 }
1072
1073 fuse_copy_finish(cs);
1074
1075 if (err)
1076 return err;
1077
1078 return ih.len;
1079}
1080
1081static int fuse_read_forget(struct fuse_conn *fc, struct fuse_copy_state *cs,
1082 size_t nbytes)
1083__releases(fc->lock)
1084{
1085 if (fc->minor < 16 || fc->forget_list_head.next->next == NULL)
1086 return fuse_read_single_forget(fc, cs, nbytes);
1087 else
1088 return fuse_read_batch_forget(fc, cs, nbytes);
1089}
1090
1021/* 1091/*
1022 * Read a single request into the userspace filesystem's buffer. This 1092 * Read a single request into the userspace filesystem's buffer. This
1023 * function waits until a request is available, then removes it from 1093 * function waits until a request is available, then removes it from
@@ -1058,7 +1128,7 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file,
1058 1128
1059 if (forget_pending(fc)) { 1129 if (forget_pending(fc)) {
1060 if (list_empty(&fc->pending) || fc->forget_batch-- > 0) 1130 if (list_empty(&fc->pending) || fc->forget_batch-- > 0)
1061 return fuse_read_single_forget(fc, cs, nbytes); 1131 return fuse_read_forget(fc, cs, nbytes);
1062 1132
1063 if (fc->forget_batch <= -8) 1133 if (fc->forget_batch <= -8)
1064 fc->forget_batch = 16; 1134 fc->forget_batch = 16;
@@ -1837,7 +1907,7 @@ __acquires(fc->lock)
1837 end_requests(fc, &fc->pending); 1907 end_requests(fc, &fc->pending);
1838 end_requests(fc, &fc->processing); 1908 end_requests(fc, &fc->processing);
1839 while (forget_pending(fc)) 1909 while (forget_pending(fc))
1840 kfree(dequeue_forget(fc)); 1910 kfree(dequeue_forget(fc, 1, NULL));
1841} 1911}
1842 1912
1843/* 1913/*
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 33369c63a522..ae5744a2f9e9 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -55,8 +55,7 @@ extern unsigned max_user_congthresh;
55 55
56/* One forget request */ 56/* One forget request */
57struct fuse_forget_link { 57struct fuse_forget_link {
58 u64 nodeid; 58 struct fuse_forget_one forget_one;
59 u64 nlookup;
60 struct fuse_forget_link *next; 59 struct fuse_forget_link *next;
61}; 60};
62 61