Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

+50 -18
+1 -1
fs/fuse/cuse.c
··· 527 527 cc->fc.initialized = 1; 528 528 rc = cuse_send_init(cc); 529 529 if (rc) { 530 - fuse_dev_free(fud); 530 + fuse_dev_put(fud); 531 531 return rc; 532 532 } 533 533 file->private_data = fud;
+7 -2
fs/fuse/dev.c
··· 2540 2540 int fuse_dev_release(struct inode *inode, struct file *file) 2541 2541 { 2542 2542 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); 2544 2545 2545 2546 if (fc) { 2546 2547 struct fuse_pqueue *fpq = &fud->pq; ··· 2561 2560 WARN_ON(fc->iq.fasync != NULL); 2562 2561 fuse_abort_conn(fc); 2563 2562 } 2563 + spin_lock(&fc->lock); 2564 + list_del(&fud->entry); 2565 + spin_unlock(&fc->lock); 2566 + fuse_conn_put(fc); 2564 2567 } 2565 - fuse_dev_free(fud); 2568 + fuse_dev_put(fud); 2566 2569 return 0; 2567 2570 } 2568 2571 EXPORT_SYMBOL_GPL(fuse_dev_release);
+8 -7
fs/fuse/fuse_dev_i.h
··· 39 39 } ring; 40 40 }; 41 41 42 + /* fud->fc gets assigned to this value when /dev/fuse is closed */ 43 + #define FUSE_DEV_FC_DISCONNECTED ((struct fuse_conn *) 1) 44 + 42 45 /* 43 46 * Lockless access is OK, because fud->fc is set once during mount and is valid 44 47 * 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(). 45 52 */ 46 53 static inline struct fuse_conn *fuse_dev_fc_get(struct fuse_dev *fud) 47 54 { 48 - /* Pairs with smp_store_release() in fuse_dev_fc_set() */ 55 + /* Pairs with xchg() in fuse_dev_install() */ 49 56 return smp_load_acquire(&fud->fc); 50 - } 51 - 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 } 57 58 58 59 static inline struct fuse_dev *fuse_file_to_fud(struct file *file)
+4 -1
fs/fuse/fuse_i.h
··· 577 577 * Fuse device instance 578 578 */ 579 579 struct fuse_dev { 580 + /** Reference count of this object */ 581 + refcount_t ref; 582 + 580 583 /** Issue FUSE_INIT synchronously */ 581 584 bool sync_init; 582 585 ··· 1347 1344 struct fuse_dev *fuse_dev_alloc_install(struct fuse_conn *fc); 1348 1345 struct fuse_dev *fuse_dev_alloc(void); 1349 1346 void fuse_dev_install(struct fuse_dev *fud, struct fuse_conn *fc); 1350 - void fuse_dev_free(struct fuse_dev *fud); 1347 + void fuse_dev_put(struct fuse_dev *fud); 1351 1348 int fuse_send_init(struct fuse_mount *fm); 1352 1349 1353 1350 /**
+29 -6
fs/fuse/inode.c
··· 1626 1626 if (!fud) 1627 1627 return NULL; 1628 1628 1629 + refcount_set(&fud->ref, 1); 1629 1630 pq = kzalloc_objs(struct list_head, FUSE_PQ_HASH_SIZE); 1630 1631 if (!pq) { 1631 1632 kfree(fud); ··· 1642 1641 1643 1642 void fuse_dev_install(struct fuse_dev *fud, struct fuse_conn *fc) 1644 1643 { 1645 - fuse_dev_fc_set(fud, fuse_conn_get(fc)); 1644 + struct fuse_conn *old_fc; 1645 + 1646 1646 spin_lock(&fc->lock); 1647 - list_add_tail(&fud->entry, &fc->devices); 1647 + /* 1648 + * Pairs with: 1649 + * - xchg() in fuse_dev_release() 1650 + * - smp_load_acquire() in fuse_dev_fc_get() 1651 + */ 1652 + old_fc = cmpxchg(&fud->fc, NULL, fc); 1653 + if (old_fc) { 1654 + /* 1655 + * failed to set fud->fc because 1656 + * - it was already set to a different fc 1657 + * - it was set to disconneted 1658 + */ 1659 + fc->connected = 0; 1660 + } else { 1661 + list_add_tail(&fud->entry, &fc->devices); 1662 + fuse_conn_get(fc); 1663 + } 1648 1664 spin_unlock(&fc->lock); 1649 1665 } 1650 1666 EXPORT_SYMBOL_GPL(fuse_dev_install); ··· 1679 1661 } 1680 1662 EXPORT_SYMBOL_GPL(fuse_dev_alloc_install); 1681 1663 1682 - void fuse_dev_free(struct fuse_dev *fud) 1664 + void fuse_dev_put(struct fuse_dev *fud) 1683 1665 { 1684 - struct fuse_conn *fc = fuse_dev_fc_get(fud); 1666 + struct fuse_conn *fc; 1685 1667 1686 - if (fc) { 1668 + if (!refcount_dec_and_test(&fud->ref)) 1669 + return; 1670 + 1671 + fc = fuse_dev_fc_get(fud); 1672 + if (fc && fc != FUSE_DEV_FC_DISCONNECTED) { 1673 + /* This is the virtiofs case (fuse_dev_release() not called) */ 1687 1674 spin_lock(&fc->lock); 1688 1675 list_del(&fud->entry); 1689 1676 spin_unlock(&fc->lock); ··· 1698 1675 kfree(fud->pq.processing); 1699 1676 kfree(fud); 1700 1677 } 1701 - EXPORT_SYMBOL_GPL(fuse_dev_free); 1678 + EXPORT_SYMBOL_GPL(fuse_dev_put); 1702 1679 1703 1680 static void fuse_fill_attr_from_inode(struct fuse_attr *attr, 1704 1681 const struct fuse_inode *fi)
+1 -1
fs/fuse/virtio_fs.c
··· 486 486 if (!fsvq->fud) 487 487 continue; 488 488 489 - fuse_dev_free(fsvq->fud); 489 + fuse_dev_put(fsvq->fud); 490 490 fsvq->fud = NULL; 491 491 } 492 492 }