Skip to content

Commit e9bf385

Browse files
author
Miklos Szeredi
committed
fuse: add refcount to fuse_dev
This will make it possible to grab the fuse_dev and subsequently release the file that it came from. In the above case, fud->fc will be set to FUSE_DEV_FC_DISCONNECTED to indicate that this is no longer a functional device. When trying to assign an fc to such a disconnected fuse_dev, the fc is set to the disconnected state. Use atomic operations xchg() and cmpxchg() to prevent races. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
1 parent a8dd5f1 commit e9bf385

6 files changed

Lines changed: 50 additions & 18 deletions

File tree

fs/fuse/cuse.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ static int cuse_channel_open(struct inode *inode, struct file *file)
527527
cc->fc.initialized = 1;
528528
rc = cuse_send_init(cc);
529529
if (rc) {
530-
fuse_dev_free(fud);
530+
fuse_dev_put(fud);
531531
return rc;
532532
}
533533
file->private_data = fud;

fs/fuse/dev.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2540,7 +2540,8 @@ void fuse_wait_aborted(struct fuse_conn *fc)
25402540
int fuse_dev_release(struct inode *inode, struct file *file)
25412541
{
25422542
struct fuse_dev *fud = fuse_file_to_fud(file);
2543-
struct fuse_conn *fc = fuse_dev_fc_get(fud);
2543+
/* Pairs with cmpxchg() in fuse_dev_install() */
2544+
struct fuse_conn *fc = xchg(&fud->fc, FUSE_DEV_FC_DISCONNECTED);
25442545

25452546
if (fc) {
25462547
struct fuse_pqueue *fpq = &fud->pq;
@@ -2560,8 +2561,12 @@ int fuse_dev_release(struct inode *inode, struct file *file)
25602561
WARN_ON(fc->iq.fasync != NULL);
25612562
fuse_abort_conn(fc);
25622563
}
2564+
spin_lock(&fc->lock);
2565+
list_del(&fud->entry);
2566+
spin_unlock(&fc->lock);
2567+
fuse_conn_put(fc);
25632568
}
2564-
fuse_dev_free(fud);
2569+
fuse_dev_put(fud);
25652570
return 0;
25662571
}
25672572
EXPORT_SYMBOL_GPL(fuse_dev_release);

fs/fuse/fuse_dev_i.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,22 +39,23 @@ struct fuse_copy_state {
3939
} ring;
4040
};
4141

42+
/* fud->fc gets assigned to this value when /dev/fuse is closed */
43+
#define FUSE_DEV_FC_DISCONNECTED ((struct fuse_conn *) 1)
44+
4245
/*
4346
* Lockless access is OK, because fud->fc is set once during mount and is valid
4447
* until the file is released.
48+
*
49+
* fud->fc is set to FUSE_DEV_FC_DISCONNECTED only after the containing file is
50+
* released, so result is safe to dereference in most cases. Exceptions are:
51+
* fuse_dev_put() and fuse_fill_super_common().
4552
*/
4653
static inline struct fuse_conn *fuse_dev_fc_get(struct fuse_dev *fud)
4754
{
48-
/* Pairs with smp_store_release() in fuse_dev_fc_set() */
55+
/* Pairs with xchg() in fuse_dev_install() */
4956
return smp_load_acquire(&fud->fc);
5057
}
5158

52-
static inline void fuse_dev_fc_set(struct fuse_dev *fud, struct fuse_conn *fc)
53-
{
54-
/* Pairs with smp_load_acquire() in fuse_dev_fc_get() */
55-
smp_store_release(&fud->fc, fc);
56-
}
57-
5859
static inline struct fuse_dev *fuse_file_to_fud(struct file *file)
5960
{
6061
return file->private_data;

fs/fuse/fuse_i.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,9 @@ struct fuse_pqueue {
577577
* Fuse device instance
578578
*/
579579
struct fuse_dev {
580+
/** Reference count of this object */
581+
refcount_t ref;
582+
580583
/** Issue FUSE_INIT synchronously */
581584
bool sync_init;
582585

@@ -1344,7 +1347,7 @@ void fuse_conn_put(struct fuse_conn *fc);
13441347
struct fuse_dev *fuse_dev_alloc_install(struct fuse_conn *fc);
13451348
struct fuse_dev *fuse_dev_alloc(void);
13461349
void fuse_dev_install(struct fuse_dev *fud, struct fuse_conn *fc);
1347-
void fuse_dev_free(struct fuse_dev *fud);
1350+
void fuse_dev_put(struct fuse_dev *fud);
13481351
int fuse_send_init(struct fuse_mount *fm);
13491352

13501353
/**

fs/fuse/inode.c

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1626,6 +1626,7 @@ struct fuse_dev *fuse_dev_alloc(void)
16261626
if (!fud)
16271627
return NULL;
16281628

1629+
refcount_set(&fud->ref, 1);
16291630
pq = kzalloc_objs(struct list_head, FUSE_PQ_HASH_SIZE);
16301631
if (!pq) {
16311632
kfree(fud);
@@ -1641,9 +1642,26 @@ EXPORT_SYMBOL_GPL(fuse_dev_alloc);
16411642

16421643
void fuse_dev_install(struct fuse_dev *fud, struct fuse_conn *fc)
16431644
{
1644-
fuse_dev_fc_set(fud, fuse_conn_get(fc));
1645+
struct fuse_conn *old_fc;
1646+
16451647
spin_lock(&fc->lock);
1646-
list_add_tail(&fud->entry, &fc->devices);
1648+
/*
1649+
* Pairs with:
1650+
* - xchg() in fuse_dev_release()
1651+
* - smp_load_acquire() in fuse_dev_fc_get()
1652+
*/
1653+
old_fc = cmpxchg(&fud->fc, NULL, fc);
1654+
if (old_fc) {
1655+
/*
1656+
* failed to set fud->fc because
1657+
* - it was already set to a different fc
1658+
* - it was set to disconneted
1659+
*/
1660+
fc->connected = 0;
1661+
} else {
1662+
list_add_tail(&fud->entry, &fc->devices);
1663+
fuse_conn_get(fc);
1664+
}
16471665
spin_unlock(&fc->lock);
16481666
}
16491667
EXPORT_SYMBOL_GPL(fuse_dev_install);
@@ -1661,11 +1679,16 @@ struct fuse_dev *fuse_dev_alloc_install(struct fuse_conn *fc)
16611679
}
16621680
EXPORT_SYMBOL_GPL(fuse_dev_alloc_install);
16631681

1664-
void fuse_dev_free(struct fuse_dev *fud)
1682+
void fuse_dev_put(struct fuse_dev *fud)
16651683
{
1666-
struct fuse_conn *fc = fuse_dev_fc_get(fud);
1684+
struct fuse_conn *fc;
1685+
1686+
if (!refcount_dec_and_test(&fud->ref))
1687+
return;
16671688

1668-
if (fc) {
1689+
fc = fuse_dev_fc_get(fud);
1690+
if (fc && fc != FUSE_DEV_FC_DISCONNECTED) {
1691+
/* This is the virtiofs case (fuse_dev_release() not called) */
16691692
spin_lock(&fc->lock);
16701693
list_del(&fud->entry);
16711694
spin_unlock(&fc->lock);
@@ -1675,7 +1698,7 @@ void fuse_dev_free(struct fuse_dev *fud)
16751698
kfree(fud->pq.processing);
16761699
kfree(fud);
16771700
}
1678-
EXPORT_SYMBOL_GPL(fuse_dev_free);
1701+
EXPORT_SYMBOL_GPL(fuse_dev_put);
16791702

16801703
static void fuse_fill_attr_from_inode(struct fuse_attr *attr,
16811704
const struct fuse_inode *fi)

fs/fuse/virtio_fs.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,7 @@ static void virtio_fs_free_devs(struct virtio_fs *fs)
486486
if (!fsvq->fud)
487487
continue;
488488

489-
fuse_dev_free(fsvq->fud);
489+
fuse_dev_put(fsvq->fud);
490490
fsvq->fud = NULL;
491491
}
492492
}

0 commit comments

Comments
 (0)