diff options
Diffstat (limited to 'fs/fuse/inode.c')
-rw-r--r-- | fs/fuse/inode.c | 135 |
1 files changed, 48 insertions, 87 deletions
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 879e6fba9480..fd34037b0588 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | FUSE: Filesystem in Userspace | 2 | FUSE: Filesystem in Userspace |
3 | Copyright (C) 2001-2005 Miklos Szeredi <miklos@szeredi.hu> | 3 | Copyright (C) 2001-2006 Miklos Szeredi <miklos@szeredi.hu> |
4 | 4 | ||
5 | This program can be distributed under the terms of the GNU GPL. | 5 | This program can be distributed under the terms of the GNU GPL. |
6 | See the file COPYING. | 6 | See the file COPYING. |
@@ -22,7 +22,6 @@ MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>"); | |||
22 | MODULE_DESCRIPTION("Filesystem in Userspace"); | 22 | MODULE_DESCRIPTION("Filesystem in Userspace"); |
23 | MODULE_LICENSE("GPL"); | 23 | MODULE_LICENSE("GPL"); |
24 | 24 | ||
25 | spinlock_t fuse_lock; | ||
26 | static kmem_cache_t *fuse_inode_cachep; | 25 | static kmem_cache_t *fuse_inode_cachep; |
27 | static struct subsystem connections_subsys; | 26 | static struct subsystem connections_subsys; |
28 | 27 | ||
@@ -207,15 +206,17 @@ static void fuse_put_super(struct super_block *sb) | |||
207 | 206 | ||
208 | down_write(&fc->sbput_sem); | 207 | down_write(&fc->sbput_sem); |
209 | while (!list_empty(&fc->background)) | 208 | while (!list_empty(&fc->background)) |
210 | fuse_release_background(list_entry(fc->background.next, | 209 | fuse_release_background(fc, |
210 | list_entry(fc->background.next, | ||
211 | struct fuse_req, bg_entry)); | 211 | struct fuse_req, bg_entry)); |
212 | 212 | ||
213 | spin_lock(&fuse_lock); | 213 | spin_lock(&fc->lock); |
214 | fc->mounted = 0; | 214 | fc->mounted = 0; |
215 | fc->connected = 0; | 215 | fc->connected = 0; |
216 | spin_unlock(&fuse_lock); | 216 | spin_unlock(&fc->lock); |
217 | up_write(&fc->sbput_sem); | 217 | up_write(&fc->sbput_sem); |
218 | /* Flush all readers on this fs */ | 218 | /* Flush all readers on this fs */ |
219 | kill_fasync(&fc->fasync, SIGIO, POLL_IN); | ||
219 | wake_up_all(&fc->waitq); | 220 | wake_up_all(&fc->waitq); |
220 | kobject_del(&fc->kobj); | 221 | kobject_del(&fc->kobj); |
221 | kobject_put(&fc->kobj); | 222 | kobject_put(&fc->kobj); |
@@ -242,9 +243,9 @@ static int fuse_statfs(struct super_block *sb, struct kstatfs *buf) | |||
242 | struct fuse_statfs_out outarg; | 243 | struct fuse_statfs_out outarg; |
243 | int err; | 244 | int err; |
244 | 245 | ||
245 | req = fuse_get_request(fc); | 246 | req = fuse_get_req(fc); |
246 | if (!req) | 247 | if (IS_ERR(req)) |
247 | return -EINTR; | 248 | return PTR_ERR(req); |
248 | 249 | ||
249 | memset(&outarg, 0, sizeof(outarg)); | 250 | memset(&outarg, 0, sizeof(outarg)); |
250 | req->in.numargs = 0; | 251 | req->in.numargs = 0; |
@@ -369,15 +370,7 @@ static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt) | |||
369 | 370 | ||
370 | static void fuse_conn_release(struct kobject *kobj) | 371 | static void fuse_conn_release(struct kobject *kobj) |
371 | { | 372 | { |
372 | struct fuse_conn *fc = get_fuse_conn_kobj(kobj); | 373 | kfree(get_fuse_conn_kobj(kobj)); |
373 | |||
374 | while (!list_empty(&fc->unused_list)) { | ||
375 | struct fuse_req *req; | ||
376 | req = list_entry(fc->unused_list.next, struct fuse_req, list); | ||
377 | list_del(&req->list); | ||
378 | fuse_request_free(req); | ||
379 | } | ||
380 | kfree(fc); | ||
381 | } | 374 | } |
382 | 375 | ||
383 | static struct fuse_conn *new_conn(void) | 376 | static struct fuse_conn *new_conn(void) |
@@ -386,64 +379,25 @@ static struct fuse_conn *new_conn(void) | |||
386 | 379 | ||
387 | fc = kzalloc(sizeof(*fc), GFP_KERNEL); | 380 | fc = kzalloc(sizeof(*fc), GFP_KERNEL); |
388 | if (fc) { | 381 | if (fc) { |
389 | int i; | 382 | spin_lock_init(&fc->lock); |
390 | init_waitqueue_head(&fc->waitq); | 383 | init_waitqueue_head(&fc->waitq); |
384 | init_waitqueue_head(&fc->blocked_waitq); | ||
391 | INIT_LIST_HEAD(&fc->pending); | 385 | INIT_LIST_HEAD(&fc->pending); |
392 | INIT_LIST_HEAD(&fc->processing); | 386 | INIT_LIST_HEAD(&fc->processing); |
393 | INIT_LIST_HEAD(&fc->io); | 387 | INIT_LIST_HEAD(&fc->io); |
394 | INIT_LIST_HEAD(&fc->unused_list); | ||
395 | INIT_LIST_HEAD(&fc->background); | 388 | INIT_LIST_HEAD(&fc->background); |
396 | sema_init(&fc->outstanding_sem, 1); /* One for INIT */ | ||
397 | init_rwsem(&fc->sbput_sem); | 389 | init_rwsem(&fc->sbput_sem); |
398 | kobj_set_kset_s(fc, connections_subsys); | 390 | kobj_set_kset_s(fc, connections_subsys); |
399 | kobject_init(&fc->kobj); | 391 | kobject_init(&fc->kobj); |
400 | atomic_set(&fc->num_waiting, 0); | 392 | atomic_set(&fc->num_waiting, 0); |
401 | for (i = 0; i < FUSE_MAX_OUTSTANDING; i++) { | ||
402 | struct fuse_req *req = fuse_request_alloc(); | ||
403 | if (!req) { | ||
404 | kobject_put(&fc->kobj); | ||
405 | return NULL; | ||
406 | } | ||
407 | list_add(&req->list, &fc->unused_list); | ||
408 | } | ||
409 | fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE; | 393 | fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE; |
410 | fc->bdi.unplug_io_fn = default_unplug_io_fn; | 394 | fc->bdi.unplug_io_fn = default_unplug_io_fn; |
411 | fc->reqctr = 0; | 395 | fc->reqctr = 0; |
396 | fc->blocked = 1; | ||
412 | } | 397 | } |
413 | return fc; | 398 | return fc; |
414 | } | 399 | } |
415 | 400 | ||
416 | static struct fuse_conn *get_conn(struct file *file, struct super_block *sb) | ||
417 | { | ||
418 | struct fuse_conn *fc; | ||
419 | int err; | ||
420 | |||
421 | err = -EINVAL; | ||
422 | if (file->f_op != &fuse_dev_operations) | ||
423 | goto out_err; | ||
424 | |||
425 | err = -ENOMEM; | ||
426 | fc = new_conn(); | ||
427 | if (!fc) | ||
428 | goto out_err; | ||
429 | |||
430 | spin_lock(&fuse_lock); | ||
431 | err = -EINVAL; | ||
432 | if (file->private_data) | ||
433 | goto out_unlock; | ||
434 | |||
435 | kobject_get(&fc->kobj); | ||
436 | file->private_data = fc; | ||
437 | spin_unlock(&fuse_lock); | ||
438 | return fc; | ||
439 | |||
440 | out_unlock: | ||
441 | spin_unlock(&fuse_lock); | ||
442 | kobject_put(&fc->kobj); | ||
443 | out_err: | ||
444 | return ERR_PTR(err); | ||
445 | } | ||
446 | |||
447 | static struct inode *get_root_inode(struct super_block *sb, unsigned mode) | 401 | static struct inode *get_root_inode(struct super_block *sb, unsigned mode) |
448 | { | 402 | { |
449 | struct fuse_attr attr; | 403 | struct fuse_attr attr; |
@@ -467,7 +421,6 @@ static struct super_operations fuse_super_operations = { | |||
467 | 421 | ||
468 | static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req) | 422 | static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req) |
469 | { | 423 | { |
470 | int i; | ||
471 | struct fuse_init_out *arg = &req->misc.init_out; | 424 | struct fuse_init_out *arg = &req->misc.init_out; |
472 | 425 | ||
473 | if (req->out.h.error || arg->major != FUSE_KERNEL_VERSION) | 426 | if (req->out.h.error || arg->major != FUSE_KERNEL_VERSION) |
@@ -486,22 +439,13 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req) | |||
486 | fc->minor = arg->minor; | 439 | fc->minor = arg->minor; |
487 | fc->max_write = arg->minor < 5 ? 4096 : arg->max_write; | 440 | fc->max_write = arg->minor < 5 ? 4096 : arg->max_write; |
488 | } | 441 | } |
489 | |||
490 | /* After INIT reply is received other requests can go | ||
491 | out. So do (FUSE_MAX_OUTSTANDING - 1) number of | ||
492 | up()s on outstanding_sem. The last up() is done in | ||
493 | fuse_putback_request() */ | ||
494 | for (i = 1; i < FUSE_MAX_OUTSTANDING; i++) | ||
495 | up(&fc->outstanding_sem); | ||
496 | |||
497 | fuse_put_request(fc, req); | 442 | fuse_put_request(fc, req); |
443 | fc->blocked = 0; | ||
444 | wake_up_all(&fc->blocked_waitq); | ||
498 | } | 445 | } |
499 | 446 | ||
500 | static void fuse_send_init(struct fuse_conn *fc) | 447 | static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req) |
501 | { | 448 | { |
502 | /* This is called from fuse_read_super() so there's guaranteed | ||
503 | to be exactly one request available */ | ||
504 | struct fuse_req *req = fuse_get_request(fc); | ||
505 | struct fuse_init_in *arg = &req->misc.init_in; | 449 | struct fuse_init_in *arg = &req->misc.init_in; |
506 | 450 | ||
507 | arg->major = FUSE_KERNEL_VERSION; | 451 | arg->major = FUSE_KERNEL_VERSION; |
@@ -525,12 +469,9 @@ static void fuse_send_init(struct fuse_conn *fc) | |||
525 | 469 | ||
526 | static unsigned long long conn_id(void) | 470 | static unsigned long long conn_id(void) |
527 | { | 471 | { |
472 | /* BKL is held for ->get_sb() */ | ||
528 | static unsigned long long ctr = 1; | 473 | static unsigned long long ctr = 1; |
529 | unsigned long long val; | 474 | return ctr++; |
530 | spin_lock(&fuse_lock); | ||
531 | val = ctr++; | ||
532 | spin_unlock(&fuse_lock); | ||
533 | return val; | ||
534 | } | 475 | } |
535 | 476 | ||
536 | static int fuse_fill_super(struct super_block *sb, void *data, int silent) | 477 | static int fuse_fill_super(struct super_block *sb, void *data, int silent) |
@@ -540,6 +481,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) | |||
540 | struct fuse_mount_data d; | 481 | struct fuse_mount_data d; |
541 | struct file *file; | 482 | struct file *file; |
542 | struct dentry *root_dentry; | 483 | struct dentry *root_dentry; |
484 | struct fuse_req *init_req; | ||
543 | int err; | 485 | int err; |
544 | 486 | ||
545 | if (!parse_fuse_opt((char *) data, &d)) | 487 | if (!parse_fuse_opt((char *) data, &d)) |
@@ -555,10 +497,17 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) | |||
555 | if (!file) | 497 | if (!file) |
556 | return -EINVAL; | 498 | return -EINVAL; |
557 | 499 | ||
558 | fc = get_conn(file, sb); | 500 | if (file->f_op != &fuse_dev_operations) |
559 | fput(file); | 501 | return -EINVAL; |
560 | if (IS_ERR(fc)) | 502 | |
561 | return PTR_ERR(fc); | 503 | /* Setting file->private_data can't race with other mount() |
504 | instances, since BKL is held for ->get_sb() */ | ||
505 | if (file->private_data) | ||
506 | return -EINVAL; | ||
507 | |||
508 | fc = new_conn(); | ||
509 | if (!fc) | ||
510 | return -ENOMEM; | ||
562 | 511 | ||
563 | fc->flags = d.flags; | 512 | fc->flags = d.flags; |
564 | fc->user_id = d.user_id; | 513 | fc->user_id = d.user_id; |
@@ -579,27 +528,40 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) | |||
579 | goto err; | 528 | goto err; |
580 | } | 529 | } |
581 | 530 | ||
531 | init_req = fuse_request_alloc(); | ||
532 | if (!init_req) | ||
533 | goto err_put_root; | ||
534 | |||
582 | err = kobject_set_name(&fc->kobj, "%llu", conn_id()); | 535 | err = kobject_set_name(&fc->kobj, "%llu", conn_id()); |
583 | if (err) | 536 | if (err) |
584 | goto err_put_root; | 537 | goto err_free_req; |
585 | 538 | ||
586 | err = kobject_add(&fc->kobj); | 539 | err = kobject_add(&fc->kobj); |
587 | if (err) | 540 | if (err) |
588 | goto err_put_root; | 541 | goto err_free_req; |
589 | 542 | ||
590 | sb->s_root = root_dentry; | 543 | sb->s_root = root_dentry; |
591 | spin_lock(&fuse_lock); | ||
592 | fc->mounted = 1; | 544 | fc->mounted = 1; |
593 | fc->connected = 1; | 545 | fc->connected = 1; |
594 | spin_unlock(&fuse_lock); | 546 | kobject_get(&fc->kobj); |
547 | file->private_data = fc; | ||
548 | /* | ||
549 | * atomic_dec_and_test() in fput() provides the necessary | ||
550 | * memory barrier for file->private_data to be visible on all | ||
551 | * CPUs after this | ||
552 | */ | ||
553 | fput(file); | ||
595 | 554 | ||
596 | fuse_send_init(fc); | 555 | fuse_send_init(fc, init_req); |
597 | 556 | ||
598 | return 0; | 557 | return 0; |
599 | 558 | ||
559 | err_free_req: | ||
560 | fuse_request_free(init_req); | ||
600 | err_put_root: | 561 | err_put_root: |
601 | dput(root_dentry); | 562 | dput(root_dentry); |
602 | err: | 563 | err: |
564 | fput(file); | ||
603 | kobject_put(&fc->kobj); | 565 | kobject_put(&fc->kobj); |
604 | return err; | 566 | return err; |
605 | } | 567 | } |
@@ -753,7 +715,6 @@ static int __init fuse_init(void) | |||
753 | printk("fuse init (API version %i.%i)\n", | 715 | printk("fuse init (API version %i.%i)\n", |
754 | FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION); | 716 | FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION); |
755 | 717 | ||
756 | spin_lock_init(&fuse_lock); | ||
757 | res = fuse_fs_init(); | 718 | res = fuse_fs_init(); |
758 | if (res) | 719 | if (res) |
759 | goto err; | 720 | goto err; |