aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fuse/dir.c
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
commit07e77dca8a1f17a724a9b7449f0ca02e70e9d057 (patch)
tree69186be641145fd997ce15e17a22598d9a264119 /fs/fuse/dir.c
parent8ac835056ca39b242d98332f46e4d65428a8b7db (diff)
fuse: separate queue for FORGET requests
Terje Malmedal reports that a fuse filesystem with 32 million inodes on a machine with lots of memory can go unresponsive for up to 30 minutes when all those inodes are evicted from the icache. The reason is that FORGET messages, sent when the inode is evicted, are queued up together with regular filesystem requests, and while the huge queue of FORGET messages are processed no other filesystem operation can proceed. Since a full fuse request structure is allocated for each inode, these take up quite a bit of memory as well. To solve these issues, create a slim 'fuse_forget_link' structure containing just the minimum of information required to send the FORGET request and chain these on a separate queue. When userspace is asking for a request make sure that FORGET and non-FORGET requests are selected fairly: for each 8 non-FORGET allow 16 FORGET requests. This will make sure FORGETs do not pile up, yet other requests are also allowed to proceed while the queued FORGETs are processed. Reported-by: Terje Malmedal <terje.malmedal@usit.uio.no> Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Diffstat (limited to 'fs/fuse/dir.c')
-rw-r--r--fs/fuse/dir.c53
1 files changed, 26 insertions, 27 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index c9627c95482d..6ea42e98cb17 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -10,9 +10,9 @@
10 10
11#include <linux/pagemap.h> 11#include <linux/pagemap.h>
12#include <linux/file.h> 12#include <linux/file.h>
13#include <linux/gfp.h>
14#include <linux/sched.h> 13#include <linux/sched.h>
15#include <linux/namei.h> 14#include <linux/namei.h>
15#include <linux/slab.h>
16 16
17#if BITS_PER_LONG >= 64 17#if BITS_PER_LONG >= 64
18static inline void fuse_dentry_settime(struct dentry *entry, u64 time) 18static inline void fuse_dentry_settime(struct dentry *entry, u64 time)
@@ -165,7 +165,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
165 struct fuse_entry_out outarg; 165 struct fuse_entry_out outarg;
166 struct fuse_conn *fc; 166 struct fuse_conn *fc;
167 struct fuse_req *req; 167 struct fuse_req *req;
168 struct fuse_req *forget_req; 168 struct fuse_forget_link *forget;
169 struct dentry *parent; 169 struct dentry *parent;
170 u64 attr_version; 170 u64 attr_version;
171 171
@@ -178,8 +178,8 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
178 if (IS_ERR(req)) 178 if (IS_ERR(req))
179 return 0; 179 return 0;
180 180
181 forget_req = fuse_get_req(fc); 181 forget = fuse_alloc_forget();
182 if (IS_ERR(forget_req)) { 182 if (!forget) {
183 fuse_put_request(fc, req); 183 fuse_put_request(fc, req);
184 return 0; 184 return 0;
185 } 185 }
@@ -199,15 +199,14 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
199 if (!err) { 199 if (!err) {
200 struct fuse_inode *fi = get_fuse_inode(inode); 200 struct fuse_inode *fi = get_fuse_inode(inode);
201 if (outarg.nodeid != get_node_id(inode)) { 201 if (outarg.nodeid != get_node_id(inode)) {
202 fuse_send_forget(fc, forget_req, 202 fuse_queue_forget(fc, forget, outarg.nodeid, 1);
203 outarg.nodeid, 1);
204 return 0; 203 return 0;
205 } 204 }
206 spin_lock(&fc->lock); 205 spin_lock(&fc->lock);
207 fi->nlookup++; 206 fi->nlookup++;
208 spin_unlock(&fc->lock); 207 spin_unlock(&fc->lock);
209 } 208 }
210 fuse_put_request(fc, forget_req); 209 kfree(forget);
211 if (err || (outarg.attr.mode ^ inode->i_mode) & S_IFMT) 210 if (err || (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
212 return 0; 211 return 0;
213 212
@@ -259,7 +258,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, struct qstr *name,
259{ 258{
260 struct fuse_conn *fc = get_fuse_conn_super(sb); 259 struct fuse_conn *fc = get_fuse_conn_super(sb);
261 struct fuse_req *req; 260 struct fuse_req *req;
262 struct fuse_req *forget_req; 261 struct fuse_forget_link *forget;
263 u64 attr_version; 262 u64 attr_version;
264 int err; 263 int err;
265 264
@@ -273,9 +272,9 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, struct qstr *name,
273 if (IS_ERR(req)) 272 if (IS_ERR(req))
274 goto out; 273 goto out;
275 274
276 forget_req = fuse_get_req(fc); 275 forget = fuse_alloc_forget();
277 err = PTR_ERR(forget_req); 276 err = -ENOMEM;
278 if (IS_ERR(forget_req)) { 277 if (!forget) {
279 fuse_put_request(fc, req); 278 fuse_put_request(fc, req);
280 goto out; 279 goto out;
281 } 280 }
@@ -301,13 +300,13 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, struct qstr *name,
301 attr_version); 300 attr_version);
302 err = -ENOMEM; 301 err = -ENOMEM;
303 if (!*inode) { 302 if (!*inode) {
304 fuse_send_forget(fc, forget_req, outarg->nodeid, 1); 303 fuse_queue_forget(fc, forget, outarg->nodeid, 1);
305 goto out; 304 goto out;
306 } 305 }
307 err = 0; 306 err = 0;
308 307
309 out_put_forget: 308 out_put_forget:
310 fuse_put_request(fc, forget_req); 309 kfree(forget);
311 out: 310 out:
312 return err; 311 return err;
313} 312}
@@ -374,7 +373,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
374 struct inode *inode; 373 struct inode *inode;
375 struct fuse_conn *fc = get_fuse_conn(dir); 374 struct fuse_conn *fc = get_fuse_conn(dir);
376 struct fuse_req *req; 375 struct fuse_req *req;
377 struct fuse_req *forget_req; 376 struct fuse_forget_link *forget;
378 struct fuse_create_in inarg; 377 struct fuse_create_in inarg;
379 struct fuse_open_out outopen; 378 struct fuse_open_out outopen;
380 struct fuse_entry_out outentry; 379 struct fuse_entry_out outentry;
@@ -388,9 +387,9 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
388 if (flags & O_DIRECT) 387 if (flags & O_DIRECT)
389 return -EINVAL; 388 return -EINVAL;
390 389
391 forget_req = fuse_get_req(fc); 390 forget = fuse_alloc_forget();
392 if (IS_ERR(forget_req)) 391 if (!forget)
393 return PTR_ERR(forget_req); 392 return -ENOMEM;
394 393
395 req = fuse_get_req(fc); 394 req = fuse_get_req(fc);
396 err = PTR_ERR(req); 395 err = PTR_ERR(req);
@@ -448,10 +447,10 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
448 if (!inode) { 447 if (!inode) {
449 flags &= ~(O_CREAT | O_EXCL | O_TRUNC); 448 flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
450 fuse_sync_release(ff, flags); 449 fuse_sync_release(ff, flags);
451 fuse_send_forget(fc, forget_req, outentry.nodeid, 1); 450 fuse_queue_forget(fc, forget, outentry.nodeid, 1);
452 return -ENOMEM; 451 return -ENOMEM;
453 } 452 }
454 fuse_put_request(fc, forget_req); 453 kfree(forget);
455 d_instantiate(entry, inode); 454 d_instantiate(entry, inode);
456 fuse_change_entry_timeout(entry, &outentry); 455 fuse_change_entry_timeout(entry, &outentry);
457 fuse_invalidate_attr(dir); 456 fuse_invalidate_attr(dir);
@@ -469,7 +468,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
469 out_put_request: 468 out_put_request:
470 fuse_put_request(fc, req); 469 fuse_put_request(fc, req);
471 out_put_forget_req: 470 out_put_forget_req:
472 fuse_put_request(fc, forget_req); 471 kfree(forget);
473 return err; 472 return err;
474} 473}
475 474
@@ -483,12 +482,12 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req,
483 struct fuse_entry_out outarg; 482 struct fuse_entry_out outarg;
484 struct inode *inode; 483 struct inode *inode;
485 int err; 484 int err;
486 struct fuse_req *forget_req; 485 struct fuse_forget_link *forget;
487 486
488 forget_req = fuse_get_req(fc); 487 forget = fuse_alloc_forget();
489 if (IS_ERR(forget_req)) { 488 if (!forget) {
490 fuse_put_request(fc, req); 489 fuse_put_request(fc, req);
491 return PTR_ERR(forget_req); 490 return -ENOMEM;
492 } 491 }
493 492
494 memset(&outarg, 0, sizeof(outarg)); 493 memset(&outarg, 0, sizeof(outarg));
@@ -515,10 +514,10 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req,
515 inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, 514 inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
516 &outarg.attr, entry_attr_timeout(&outarg), 0); 515 &outarg.attr, entry_attr_timeout(&outarg), 0);
517 if (!inode) { 516 if (!inode) {
518 fuse_send_forget(fc, forget_req, outarg.nodeid, 1); 517 fuse_queue_forget(fc, forget, outarg.nodeid, 1);
519 return -ENOMEM; 518 return -ENOMEM;
520 } 519 }
521 fuse_put_request(fc, forget_req); 520 kfree(forget);
522 521
523 if (S_ISDIR(inode->i_mode)) { 522 if (S_ISDIR(inode->i_mode)) {
524 struct dentry *alias; 523 struct dentry *alias;
@@ -541,7 +540,7 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req,
541 return 0; 540 return 0;
542 541
543 out_put_forget_req: 542 out_put_forget_req:
544 fuse_put_request(fc, forget_req); 543 kfree(forget);
545 return err; 544 return err;
546} 545}
547 546