aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fuse
diff options
context:
space:
mode:
authorMiklos Szeredi <miklos@szeredi.hu>2006-01-17 01:14:52 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-01-17 02:15:31 -0500
commit095da6cbb6a1c54c19b11190218eb0fbac666b6d (patch)
tree29ba46cea17454fe1888182f6722aee64e1a7ef5 /fs/fuse
parentbacac382fbf53f717ca7f83558e45cce44e67df9 (diff)
[PATCH] fuse: fix bitfield race
Fix race in setting bitfields of fuse_conn. Spotted by Andrew Morton. The two fields ->connected and ->mounted were always changed with the fuse_lock held. But other bitfields in the same structure were changed without the lock. In theory this could lead to losing the assignment of even the ones under lock. The chosen solution is to change these two fields to be a full unsigned type. The other bitfields aren't "important" enough to warrant the extra complexity of full locking or changing them to bitops. For all bitfields document why they are safe wrt. concurrent assignments. Also make the initialization of the 'num_waiting' atomic counter explicit. Signed-off-by: Miklos Szeredi <miklos@szeredi.hu> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs/fuse')
-rw-r--r--fs/fuse/fuse_i.h24
-rw-r--r--fs/fuse/inode.c2
2 files changed, 23 insertions, 3 deletions
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 7ed1d3c53b8a..46cf933aa3bf 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -94,6 +94,11 @@ struct fuse_out {
94 /** Header returned from userspace */ 94 /** Header returned from userspace */
95 struct fuse_out_header h; 95 struct fuse_out_header h;
96 96
97 /*
98 * The following bitfields are not changed during the request
99 * processing
100 */
101
97 /** Last argument is variable length (can be shorter than 102 /** Last argument is variable length (can be shorter than
98 arg->size) */ 103 arg->size) */
99 unsigned argvar:1; 104 unsigned argvar:1;
@@ -136,6 +141,12 @@ struct fuse_req {
136 /** refcount */ 141 /** refcount */
137 atomic_t count; 142 atomic_t count;
138 143
144 /*
145 * The following bitfields are either set once before the
146 * request is queued or setting/clearing them is protected by
147 * fuse_lock
148 */
149
139 /** True if the request has reply */ 150 /** True if the request has reply */
140 unsigned isreply:1; 151 unsigned isreply:1;
141 152
@@ -250,15 +261,22 @@ struct fuse_conn {
250 u64 reqctr; 261 u64 reqctr;
251 262
252 /** Mount is active */ 263 /** Mount is active */
253 unsigned mounted : 1; 264 unsigned mounted;
254 265
255 /** Connection established, cleared on umount, connection 266 /** Connection established, cleared on umount, connection
256 abort and device release */ 267 abort and device release */
257 unsigned connected : 1; 268 unsigned connected;
258 269
259 /** Connection failed (version mismatch) */ 270 /** Connection failed (version mismatch). Cannot race with
271 setting other bitfields since it is only set once in INIT
272 reply, before any other request, and never cleared */
260 unsigned conn_error : 1; 273 unsigned conn_error : 1;
261 274
275 /*
276 * The following bitfields are only for optimization purposes
277 * and hence races in setting them will not cause malfunction
278 */
279
262 /** Is fsync not implemented by fs? */ 280 /** Is fsync not implemented by fs? */
263 unsigned no_fsync : 1; 281 unsigned no_fsync : 1;
264 282
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 8683e7254d53..c755a0440a66 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -397,6 +397,7 @@ static struct fuse_conn *new_conn(void)
397 init_rwsem(&fc->sbput_sem); 397 init_rwsem(&fc->sbput_sem);
398 kobj_set_kset_s(fc, connections_subsys); 398 kobj_set_kset_s(fc, connections_subsys);
399 kobject_init(&fc->kobj); 399 kobject_init(&fc->kobj);
400 atomic_set(&fc->num_waiting, 0);
400 for (i = 0; i < FUSE_MAX_OUTSTANDING; i++) { 401 for (i = 0; i < FUSE_MAX_OUTSTANDING; i++) {
401 struct fuse_req *req = fuse_request_alloc(); 402 struct fuse_req *req = fuse_request_alloc();
402 if (!req) { 403 if (!req) {
@@ -492,6 +493,7 @@ static void fuse_send_init(struct fuse_conn *fc)
492 to be exactly one request available */ 493 to be exactly one request available */
493 struct fuse_req *req = fuse_get_request(fc); 494 struct fuse_req *req = fuse_get_request(fc);
494 struct fuse_init_in *arg = &req->misc.init_in; 495 struct fuse_init_in *arg = &req->misc.init_in;
496
495 arg->major = FUSE_KERNEL_VERSION; 497 arg->major = FUSE_KERNEL_VERSION;
496 arg->minor = FUSE_KERNEL_MINOR_VERSION; 498 arg->minor = FUSE_KERNEL_MINOR_VERSION;
497 req->in.h.opcode = FUSE_INIT; 499 req->in.h.opcode = FUSE_INIT;