aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <miklos@szeredi.hu>2006-06-25 08:48:52 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-25 13:01:19 -0400
commit33649c91a3df57c1090a657637d44b896de367e7 (patch)
tree1ee4871a65f2b37a931c796463eb39737a32cbaa
parent7142125937e1482ad3ae4366594c6586153dfc86 (diff)
[PATCH] fuse: ensure FLUSH reaches userspace
All POSIX locks owned by the current task are removed on close(). If the FLUSH request resulting initiated by close() fails to reach userspace, there might be locks remaining, which cannot be removed. The only reason it could fail, is if allocating the request fails. In this case use the request reserved for RELEASE, or if that is currently used by another FLUSH, wait for it to become available. Signed-off-by: Miklos Szeredi <miklos@szeredi.hu> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--fs/fuse/dev.c88
-rw-r--r--fs/fuse/file.c13
-rw-r--r--fs/fuse/fuse_i.h12
3 files changed, 99 insertions, 14 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index fe3adf589174..ae26f37e53a1 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -76,6 +76,13 @@ static void __fuse_put_request(struct fuse_req *req)
76 atomic_dec(&req->count); 76 atomic_dec(&req->count);
77} 77}
78 78
79static void fuse_req_init_context(struct fuse_req *req)
80{
81 req->in.h.uid = current->fsuid;
82 req->in.h.gid = current->fsgid;
83 req->in.h.pid = current->pid;
84}
85
79struct fuse_req *fuse_get_req(struct fuse_conn *fc) 86struct fuse_req *fuse_get_req(struct fuse_conn *fc)
80{ 87{
81 struct fuse_req *req; 88 struct fuse_req *req;
@@ -100,9 +107,7 @@ struct fuse_req *fuse_get_req(struct fuse_conn *fc)
100 if (!req) 107 if (!req)
101 goto out; 108 goto out;
102 109
103 req->in.h.uid = current->fsuid; 110 fuse_req_init_context(req);
104 req->in.h.gid = current->fsgid;
105 req->in.h.pid = current->pid;
106 req->waiting = 1; 111 req->waiting = 1;
107 return req; 112 return req;
108 113
@@ -111,12 +116,87 @@ struct fuse_req *fuse_get_req(struct fuse_conn *fc)
111 return ERR_PTR(err); 116 return ERR_PTR(err);
112} 117}
113 118
119/*
120 * Return request in fuse_file->reserved_req. However that may
121 * currently be in use. If that is the case, wait for it to become
122 * available.
123 */
124static struct fuse_req *get_reserved_req(struct fuse_conn *fc,
125 struct file *file)
126{
127 struct fuse_req *req = NULL;
128 struct fuse_file *ff = file->private_data;
129
130 do {
131 wait_event(fc->blocked_waitq, ff->reserved_req);
132 spin_lock(&fc->lock);
133 if (ff->reserved_req) {
134 req = ff->reserved_req;
135 ff->reserved_req = NULL;
136 get_file(file);
137 req->stolen_file = file;
138 }
139 spin_unlock(&fc->lock);
140 } while (!req);
141
142 return req;
143}
144
145/*
146 * Put stolen request back into fuse_file->reserved_req
147 */
148static void put_reserved_req(struct fuse_conn *fc, struct fuse_req *req)
149{
150 struct file *file = req->stolen_file;
151 struct fuse_file *ff = file->private_data;
152
153 spin_lock(&fc->lock);
154 fuse_request_init(req);
155 BUG_ON(ff->reserved_req);
156 ff->reserved_req = req;
157 wake_up(&fc->blocked_waitq);
158 spin_unlock(&fc->lock);
159 fput(file);
160}
161
162/*
163 * Gets a requests for a file operation, always succeeds
164 *
165 * This is used for sending the FLUSH request, which must get to
166 * userspace, due to POSIX locks which may need to be unlocked.
167 *
168 * If allocation fails due to OOM, use the reserved request in
169 * fuse_file.
170 *
171 * This is very unlikely to deadlock accidentally, since the
172 * filesystem should not have it's own file open. If deadlock is
173 * intentional, it can still be broken by "aborting" the filesystem.
174 */
175struct fuse_req *fuse_get_req_nofail(struct fuse_conn *fc, struct file *file)
176{
177 struct fuse_req *req;
178
179 atomic_inc(&fc->num_waiting);
180 wait_event(fc->blocked_waitq, !fc->blocked);
181 req = fuse_request_alloc();
182 if (!req)
183 req = get_reserved_req(fc, file);
184
185 fuse_req_init_context(req);
186 req->waiting = 1;
187 return req;
188}
189
114void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req) 190void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
115{ 191{
116 if (atomic_dec_and_test(&req->count)) { 192 if (atomic_dec_and_test(&req->count)) {
117 if (req->waiting) 193 if (req->waiting)
118 atomic_dec(&fc->num_waiting); 194 atomic_dec(&fc->num_waiting);
119 fuse_request_free(req); 195
196 if (req->stolen_file)
197 put_reserved_req(fc, req);
198 else
199 fuse_request_free(req);
120 } 200 }
121} 201}
122 202
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index d9a8289297c0..ce759414cff9 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -48,8 +48,8 @@ struct fuse_file *fuse_file_alloc(void)
48 struct fuse_file *ff; 48 struct fuse_file *ff;
49 ff = kmalloc(sizeof(struct fuse_file), GFP_KERNEL); 49 ff = kmalloc(sizeof(struct fuse_file), GFP_KERNEL);
50 if (ff) { 50 if (ff) {
51 ff->release_req = fuse_request_alloc(); 51 ff->reserved_req = fuse_request_alloc();
52 if (!ff->release_req) { 52 if (!ff->reserved_req) {
53 kfree(ff); 53 kfree(ff);
54 ff = NULL; 54 ff = NULL;
55 } 55 }
@@ -59,7 +59,7 @@ struct fuse_file *fuse_file_alloc(void)
59 59
60void fuse_file_free(struct fuse_file *ff) 60void fuse_file_free(struct fuse_file *ff)
61{ 61{
62 fuse_request_free(ff->release_req); 62 fuse_request_free(ff->reserved_req);
63 kfree(ff); 63 kfree(ff);
64} 64}
65 65
@@ -115,7 +115,7 @@ int fuse_open_common(struct inode *inode, struct file *file, int isdir)
115struct fuse_req *fuse_release_fill(struct fuse_file *ff, u64 nodeid, int flags, 115struct fuse_req *fuse_release_fill(struct fuse_file *ff, u64 nodeid, int flags,
116 int opcode) 116 int opcode)
117{ 117{
118 struct fuse_req *req = ff->release_req; 118 struct fuse_req *req = ff->reserved_req;
119 struct fuse_release_in *inarg = &req->misc.release_in; 119 struct fuse_release_in *inarg = &req->misc.release_in;
120 120
121 inarg->fh = ff->fh; 121 inarg->fh = ff->fh;
@@ -187,10 +187,7 @@ static int fuse_flush(struct file *file, fl_owner_t id)
187 if (fc->no_flush) 187 if (fc->no_flush)
188 return 0; 188 return 0;
189 189
190 req = fuse_get_req(fc); 190 req = fuse_get_req_nofail(fc, file);
191 if (IS_ERR(req))
192 return PTR_ERR(req);
193
194 memset(&inarg, 0, sizeof(inarg)); 191 memset(&inarg, 0, sizeof(inarg));
195 inarg.fh = ff->fh; 192 inarg.fh = ff->fh;
196 inarg.lock_owner = fuse_lock_owner_id(id); 193 inarg.lock_owner = fuse_lock_owner_id(id);
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index eb3166625ca9..f7c74516f3a6 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -65,7 +65,7 @@ struct fuse_inode {
65/** FUSE specific file data */ 65/** FUSE specific file data */
66struct fuse_file { 66struct fuse_file {
67 /** Request reserved for flush and release */ 67 /** Request reserved for flush and release */
68 struct fuse_req *release_req; 68 struct fuse_req *reserved_req;
69 69
70 /** File handle used by userspace */ 70 /** File handle used by userspace */
71 u64 fh; 71 u64 fh;
@@ -213,6 +213,9 @@ struct fuse_req {
213 213
214 /** Request completion callback */ 214 /** Request completion callback */
215 void (*end)(struct fuse_conn *, struct fuse_req *); 215 void (*end)(struct fuse_conn *, struct fuse_req *);
216
217 /** Request is stolen from fuse_file->reserved_req */
218 struct file *stolen_file;
216}; 219};
217 220
218/** 221/**
@@ -456,11 +459,16 @@ struct fuse_req *fuse_request_alloc(void);
456void fuse_request_free(struct fuse_req *req); 459void fuse_request_free(struct fuse_req *req);
457 460
458/** 461/**
459 * Reserve a preallocated request 462 * Get a request, may fail with -ENOMEM
460 */ 463 */
461struct fuse_req *fuse_get_req(struct fuse_conn *fc); 464struct fuse_req *fuse_get_req(struct fuse_conn *fc);
462 465
463/** 466/**
467 * Gets a requests for a file operation, always succeeds
468 */
469struct fuse_req *fuse_get_req_nofail(struct fuse_conn *fc, struct file *file);
470
471/**
464 * Decrement reference count of a request. If count goes to zero free 472 * Decrement reference count of a request. If count goes to zero free
465 * the request. 473 * the request.
466 */ 474 */