diff options
Diffstat (limited to 'fs/fuse')
-rw-r--r-- | fs/fuse/dev.c | 88 | ||||
-rw-r--r-- | fs/fuse/file.c | 13 | ||||
-rw-r--r-- | fs/fuse/fuse_i.h | 12 |
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 | ||
79 | static 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 | |||
79 | struct fuse_req *fuse_get_req(struct fuse_conn *fc) | 86 | struct 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 | */ | ||
124 | static 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 | */ | ||
148 | static 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 | */ | ||
175 | struct 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 | |||
114 | void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req) | 190 | void 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 | ||
60 | void fuse_file_free(struct fuse_file *ff) | 60 | void 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) | |||
115 | struct fuse_req *fuse_release_fill(struct fuse_file *ff, u64 nodeid, int flags, | 115 | struct 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 */ |
66 | struct fuse_file { | 66 | struct 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); | |||
456 | void fuse_request_free(struct fuse_req *req); | 459 | void 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 | */ |
461 | struct fuse_req *fuse_get_req(struct fuse_conn *fc); | 464 | struct fuse_req *fuse_get_req(struct fuse_conn *fc); |
462 | 465 | ||
463 | /** | 466 | /** |
467 | * Gets a requests for a file operation, always succeeds | ||
468 | */ | ||
469 | struct 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 | */ |