diff options
author | Miklos Szeredi <mszeredi@suse.cz> | 2010-12-07 14:16:56 -0500 |
---|---|---|
committer | Miklos Szeredi <mszeredi@suse.cz> | 2010-12-07 14:16:56 -0500 |
commit | 07e77dca8a1f17a724a9b7449f0ca02e70e9d057 (patch) | |
tree | 69186be641145fd997ce15e17a22598d9a264119 /fs/fuse/fuse_i.h | |
parent | 8ac835056ca39b242d98332f46e4d65428a8b7db (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/fuse_i.h')
-rw-r--r-- | fs/fuse/fuse_i.h | 28 |
1 files changed, 19 insertions, 9 deletions
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 57d4a3a0f102..33369c63a522 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h | |||
@@ -53,6 +53,13 @@ extern struct mutex fuse_mutex; | |||
53 | extern unsigned max_user_bgreq; | 53 | extern unsigned max_user_bgreq; |
54 | extern unsigned max_user_congthresh; | 54 | extern unsigned max_user_congthresh; |
55 | 55 | ||
56 | /* One forget request */ | ||
57 | struct fuse_forget_link { | ||
58 | u64 nodeid; | ||
59 | u64 nlookup; | ||
60 | struct fuse_forget_link *next; | ||
61 | }; | ||
62 | |||
56 | /** FUSE inode */ | 63 | /** FUSE inode */ |
57 | struct fuse_inode { | 64 | struct fuse_inode { |
58 | /** Inode data */ | 65 | /** Inode data */ |
@@ -66,7 +73,7 @@ struct fuse_inode { | |||
66 | u64 nlookup; | 73 | u64 nlookup; |
67 | 74 | ||
68 | /** The request used for sending the FORGET message */ | 75 | /** The request used for sending the FORGET message */ |
69 | struct fuse_req *forget_req; | 76 | struct fuse_forget_link *forget; |
70 | 77 | ||
71 | /** Time in jiffies until the file attributes are valid */ | 78 | /** Time in jiffies until the file attributes are valid */ |
72 | u64 i_time; | 79 | u64 i_time; |
@@ -255,7 +262,6 @@ struct fuse_req { | |||
255 | 262 | ||
256 | /** Data for asynchronous requests */ | 263 | /** Data for asynchronous requests */ |
257 | union { | 264 | union { |
258 | struct fuse_forget_in forget_in; | ||
259 | struct { | 265 | struct { |
260 | struct fuse_release_in in; | 266 | struct fuse_release_in in; |
261 | struct path path; | 267 | struct path path; |
@@ -369,6 +375,13 @@ struct fuse_conn { | |||
369 | /** Pending interrupts */ | 375 | /** Pending interrupts */ |
370 | struct list_head interrupts; | 376 | struct list_head interrupts; |
371 | 377 | ||
378 | /** Queue of pending forgets */ | ||
379 | struct fuse_forget_link forget_list_head; | ||
380 | struct fuse_forget_link *forget_list_tail; | ||
381 | |||
382 | /** Batching of FORGET requests (positive indicates FORGET batch) */ | ||
383 | int forget_batch; | ||
384 | |||
372 | /** Flag indicating if connection is blocked. This will be | 385 | /** Flag indicating if connection is blocked. This will be |
373 | the case before the INIT reply is received, and if there | 386 | the case before the INIT reply is received, and if there |
374 | are too many outstading backgrounds requests */ | 387 | are too many outstading backgrounds requests */ |
@@ -543,8 +556,10 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, struct qstr *name, | |||
543 | /** | 556 | /** |
544 | * Send FORGET command | 557 | * Send FORGET command |
545 | */ | 558 | */ |
546 | void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req, | 559 | void fuse_queue_forget(struct fuse_conn *fc, struct fuse_forget_link *forget, |
547 | u64 nodeid, u64 nlookup); | 560 | u64 nodeid, u64 nlookup); |
561 | |||
562 | struct fuse_forget_link *fuse_alloc_forget(void); | ||
548 | 563 | ||
549 | /** | 564 | /** |
550 | * Initialize READ or READDIR request | 565 | * Initialize READ or READDIR request |
@@ -656,11 +671,6 @@ void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req); | |||
656 | void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req); | 671 | void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req); |
657 | 672 | ||
658 | /** | 673 | /** |
659 | * Send a request with no reply | ||
660 | */ | ||
661 | void fuse_request_send_noreply(struct fuse_conn *fc, struct fuse_req *req); | ||
662 | |||
663 | /** | ||
664 | * Send a request in the background | 674 | * Send a request in the background |
665 | */ | 675 | */ |
666 | void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req); | 676 | void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req); |