diff options
Diffstat (limited to 'fs/fuse/dev.c')
-rw-r--r-- | fs/fuse/dev.c | 46 |
1 files changed, 35 insertions, 11 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 4526da8907c6..0c9a2ee54c91 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c | |||
@@ -66,6 +66,12 @@ static void restore_sigs(sigset_t *oldset) | |||
66 | sigprocmask(SIG_SETMASK, oldset, NULL); | 66 | sigprocmask(SIG_SETMASK, oldset, NULL); |
67 | } | 67 | } |
68 | 68 | ||
69 | /* | ||
70 | * Reset request, so that it can be reused | ||
71 | * | ||
72 | * The caller must be _very_ careful to make sure, that it is holding | ||
73 | * the only reference to req | ||
74 | */ | ||
69 | void fuse_reset_request(struct fuse_req *req) | 75 | void fuse_reset_request(struct fuse_req *req) |
70 | { | 76 | { |
71 | int preallocated = req->preallocated; | 77 | int preallocated = req->preallocated; |
@@ -120,9 +126,9 @@ struct fuse_req *fuse_get_request(struct fuse_conn *fc) | |||
120 | return do_get_request(fc); | 126 | return do_get_request(fc); |
121 | } | 127 | } |
122 | 128 | ||
129 | /* Must be called with fuse_lock held */ | ||
123 | static void fuse_putback_request(struct fuse_conn *fc, struct fuse_req *req) | 130 | static void fuse_putback_request(struct fuse_conn *fc, struct fuse_req *req) |
124 | { | 131 | { |
125 | spin_lock(&fuse_lock); | ||
126 | if (req->preallocated) { | 132 | if (req->preallocated) { |
127 | atomic_dec(&fc->num_waiting); | 133 | atomic_dec(&fc->num_waiting); |
128 | list_add(&req->list, &fc->unused_list); | 134 | list_add(&req->list, &fc->unused_list); |
@@ -134,11 +140,19 @@ static void fuse_putback_request(struct fuse_conn *fc, struct fuse_req *req) | |||
134 | fc->outstanding_debt--; | 140 | fc->outstanding_debt--; |
135 | else | 141 | else |
136 | up(&fc->outstanding_sem); | 142 | up(&fc->outstanding_sem); |
137 | spin_unlock(&fuse_lock); | ||
138 | } | 143 | } |
139 | 144 | ||
140 | void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req) | 145 | void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req) |
141 | { | 146 | { |
147 | if (atomic_dec_and_test(&req->count)) { | ||
148 | spin_lock(&fuse_lock); | ||
149 | fuse_putback_request(fc, req); | ||
150 | spin_unlock(&fuse_lock); | ||
151 | } | ||
152 | } | ||
153 | |||
154 | static void fuse_put_request_locked(struct fuse_conn *fc, struct fuse_req *req) | ||
155 | { | ||
142 | if (atomic_dec_and_test(&req->count)) | 156 | if (atomic_dec_and_test(&req->count)) |
143 | fuse_putback_request(fc, req); | 157 | fuse_putback_request(fc, req); |
144 | } | 158 | } |
@@ -163,26 +177,36 @@ void fuse_release_background(struct fuse_req *req) | |||
163 | * still waiting), the 'end' callback is called if given, else the | 177 | * still waiting), the 'end' callback is called if given, else the |
164 | * reference to the request is released | 178 | * reference to the request is released |
165 | * | 179 | * |
180 | * Releasing extra reference for foreground requests must be done | ||
181 | * within the same locked region as setting state to finished. This | ||
182 | * is because fuse_reset_request() may be called after request is | ||
183 | * finished and it must be the sole possessor. If request is | ||
184 | * interrupted and put in the background, it will return with an error | ||
185 | * and hence never be reset and reused. | ||
186 | * | ||
166 | * Called with fuse_lock, unlocks it | 187 | * Called with fuse_lock, unlocks it |
167 | */ | 188 | */ |
168 | static void request_end(struct fuse_conn *fc, struct fuse_req *req) | 189 | static void request_end(struct fuse_conn *fc, struct fuse_req *req) |
169 | { | 190 | { |
170 | void (*end) (struct fuse_conn *, struct fuse_req *) = req->end; | ||
171 | req->end = NULL; | ||
172 | list_del(&req->list); | 191 | list_del(&req->list); |
173 | req->state = FUSE_REQ_FINISHED; | 192 | req->state = FUSE_REQ_FINISHED; |
174 | spin_unlock(&fuse_lock); | 193 | if (!req->background) { |
175 | if (req->background) { | 194 | wake_up(&req->waitq); |
195 | fuse_put_request_locked(fc, req); | ||
196 | spin_unlock(&fuse_lock); | ||
197 | } else { | ||
198 | void (*end) (struct fuse_conn *, struct fuse_req *) = req->end; | ||
199 | req->end = NULL; | ||
200 | spin_unlock(&fuse_lock); | ||
176 | down_read(&fc->sbput_sem); | 201 | down_read(&fc->sbput_sem); |
177 | if (fc->mounted) | 202 | if (fc->mounted) |
178 | fuse_release_background(req); | 203 | fuse_release_background(req); |
179 | up_read(&fc->sbput_sem); | 204 | up_read(&fc->sbput_sem); |
205 | if (end) | ||
206 | end(fc, req); | ||
207 | else | ||
208 | fuse_put_request(fc, req); | ||
180 | } | 209 | } |
181 | wake_up(&req->waitq); | ||
182 | if (end) | ||
183 | end(fc, req); | ||
184 | else | ||
185 | fuse_put_request(fc, req); | ||
186 | } | 210 | } |
187 | 211 | ||
188 | /* | 212 | /* |