Merge remote-tracking branch 'sm8350/lineage-20' into lineage-23.2

* sm8350/lineage-20: (148 commits)
  ANDROID: Add FUSE_BPF to gki_defconfig
  Reapply "UPSTREAM: seccomp: Remove bogus __user annotations"
  ANDROID: fuse: Open-code vma_set_file() logic in fuse_backing_mmap()
  BACKPORT: fuse: fix livelock in synchronous file put from fuseblk workers
  BACKPORT: compat_ioctl: move more drivers to compat_ptr_ioctl
  UPSTREAM: fuse: make sure reclaim doesn't write the inode
  UPSTREAM: ANDROID: fuse-bpf: Correct fuse bpf feature flag
  UPSTREAM: fuse: verify {g,u}id mount options correctly
  fixup! BACKPORT: fuse: name fs_context consistently
  BACKPORT: fuse: name fs_context consistently
  UPSTREAM: fuse: fix root lookup with nonzero generation
  UPSTREAM: ANDROID: fuse-bpf: Fix the issue of abnormal lseek system calls
  UPSTREAM: ANDROID: fuse-bpf: Follow mounts in lookups
  UPSTREAM: fuse: dax: set fc->dax to NULL in fuse_dax_conn_free()
  BACKPORT: ANDROID: fuse-bpf: Ignore readaheads unless they go to the daemon
  UPSTREAM: ANDROID: fuse-bpf: Add NULL pointer check in fuse_release_in
  UPSTREAM: ANDROID: fs/passthrough: Fix compatibility with R/O file system
  UPSTREAM: ANDROID: fuse-bpf: Add NULL pointer check in fuse_entry_revalidate
  UPSTREAM: ANDROID: fuse-bpf: Get correct inode in mkdir
  UPSTREAM: ANDROID: fuse-bpf: Use stored bpf for create_open
  ...

Change-Id: Ice6b8c09d19db311c827d0a9f48d46795be998ee
This commit is contained in:
Michael Bestas 2026-05-14 13:35:30 +02:00
commit e14e0696e2
No known key found for this signature in database
81 changed files with 7762 additions and 1069 deletions

View file

@ -0,0 +1,19 @@
What: /sys/fs/fuse/features/fuse_bpf
Date: December 2022
Contact: Paul Lawrence <paullawrence@google.com>
Description:
Read-only file that contains the word 'supported' if fuse-bpf is
supported, does not exist otherwise
What: /sys/fs/fuse/bpf_prog_type_fuse
Date: December 2022
Contact: Paul Lawrence <paullawrence@google.com>
Description:
bpf_prog_type_fuse defines the program type of bpf programs that
may be passed to fuse-bpf. For upstream bpf program types, this
is a constant defined in a contiguous array of constants.
bpf_prog_type_fuse is appended to the end of the list, so it may
change and therefore its value must be read from this file.
Contents is ASCII decimal representation of bpf_prog_type_fuse

View file

@ -46,10 +46,8 @@ non-privileged mounts. This opens up new possibilities for the use of
filesystems. A good example is sshfs: a secure network filesystem
using the sftp protocol.
The userspace library and utilities are available from the FUSE
homepage:
http://fuse.sourceforge.net/
The userspace library and utilities are available from the
`FUSE homepage: <https://github.com/libfuse/>`_
Filesystem type
~~~~~~~~~~~~~~~

View file

@ -6742,7 +6742,8 @@ F: drivers/platform/x86/fujitsu-tablet.c
FUSE: FILESYSTEM IN USERSPACE
M: Miklos Szeredi <miklos@szeredi.hu>
L: linux-fsdevel@vger.kernel.org
W: http://fuse.sourceforge.net/
S: Maintained
W: https://github.com/libfuse/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git
S: Maintained
F: fs/fuse/

View file

@ -513,6 +513,7 @@ CONFIG_FS_VERITY_BUILTIN_SIGNATURES=y
CONFIG_QUOTA=y
CONFIG_QFMT_V2=y
CONFIG_FUSE_FS=y
CONFIG_FUSE_BPF=y
CONFIG_OVERLAY_FS=y
CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y

View file

@ -442,6 +442,7 @@ CONFIG_FS_VERITY_BUILTIN_SIGNATURES=y
CONFIG_QUOTA=y
CONFIG_QFMT_V2=y
CONFIG_FUSE_FS=y
CONFIG_FUSE_BPF=y
CONFIG_OVERLAY_FS=y
CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y

View file

@ -504,8 +504,6 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)
if (!q->stats)
goto fail_stats;
q->backing_dev_info->ra_pages = VM_READAHEAD_PAGES;
q->backing_dev_info->io_pages = VM_READAHEAD_PAGES;
q->backing_dev_info->capabilities = BDI_CAP_CGROUP_WRITEBACK;
q->backing_dev_info->name = "block";
q->node = node_id;

View file

@ -6923,7 +6923,7 @@ const struct file_operations binder_fops = {
.owner = THIS_MODULE,
.poll = binder_poll,
.unlocked_ioctl = binder_ioctl,
.compat_ioctl = binder_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.mmap = binder_mmap,
.open = binder_open,
.flush = binder_flush,

View file

@ -68,7 +68,7 @@ static long adf_ctl_ioctl(struct file *fp, unsigned int cmd, unsigned long arg);
static const struct file_operations adf_ctl_ops = {
.owner = THIS_MODULE,
.unlocked_ioctl = adf_ctl_ioctl,
.compat_ioctl = adf_ctl_ioctl,
.compat_ioctl = compat_ptr_ioctl,
};
struct adf_ctl_drv_info {

View file

@ -348,6 +348,26 @@ size_t dax_copy_to_iter(struct dax_device *dax_dev, pgoff_t pgoff, void *addr,
}
EXPORT_SYMBOL_GPL(dax_copy_to_iter);
int dax_zero_page_range(struct dax_device *dax_dev, pgoff_t pgoff,
size_t nr_pages)
{
if (!dax_alive(dax_dev))
return -ENXIO;
if (!dax_dev->ops->zero_page_range)
return -EOPNOTSUPP;
/*
* There are no callers that want to zero more than one page as of now.
* Once users are there, this check can be removed after the
* device mapper code has been updated to split ranges across targets.
*/
if (nr_pages != 1)
return -EIO;
return dax_dev->ops->zero_page_range(dax_dev, pgoff, nr_pages);
}
EXPORT_SYMBOL_GPL(dax_zero_page_range);
#ifdef CONFIG_ARCH_HAS_PMEM_API
void arch_wb_cache_pmem(void *addr, size_t size);
void dax_flush(struct dax_device *dax_dev, void *addr, size_t size)

View file

@ -468,9 +468,7 @@ static const struct file_operations dma_buf_fops = {
.llseek = dma_buf_llseek,
.poll = dma_buf_poll,
.unlocked_ioctl = dma_buf_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = dma_buf_ioctl,
#endif
.compat_ioctl = compat_ptr_ioctl,
.show_fdinfo = dma_buf_show_fdinfo,
};

View file

@ -408,5 +408,5 @@ const struct file_operations sw_sync_debugfs_fops = {
.open = sw_sync_debugfs_open,
.release = sw_sync_debugfs_release,
.unlocked_ioctl = sw_sync_ioctl,
.compat_ioctl = sw_sync_ioctl,
.compat_ioctl = compat_ptr_ioctl,
};

View file

@ -481,5 +481,5 @@ static const struct file_operations sync_file_fops = {
.release = sync_file_release,
.poll = sync_file_poll,
.unlocked_ioctl = sync_file_ioctl,
.compat_ioctl = sync_file_ioctl,
.compat_ioctl = compat_ptr_ioctl,
};

View file

@ -49,7 +49,7 @@ static const char kfd_dev_name[] = "kfd";
static const struct file_operations kfd_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = kfd_ioctl,
.compat_ioctl = kfd_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.open = kfd_open,
.mmap = kfd_mmap,
};

View file

@ -472,9 +472,7 @@ static const struct file_operations hidraw_ops = {
.release = hidraw_release,
.unlocked_ioctl = hidraw_ioctl,
.fasync = hidraw_fasync,
#ifdef CONFIG_COMPAT
.compat_ioctl = hidraw_ioctl,
#endif
.compat_ioctl = compat_ptr_ioctl,
.llseek = noop_llseek,
};

View file

@ -1612,7 +1612,7 @@ static const struct file_operations iio_buffer_fileops = {
.owner = THIS_MODULE,
.llseek = noop_llseek,
.unlocked_ioctl = iio_ioctl,
.compat_ioctl = iio_ioctl,
.compat_ioctl = compat_ptr_ioctl,
};
static int iio_check_unique_scan_index(struct iio_dev *indio_dev)

View file

@ -1135,7 +1135,7 @@ static const struct file_operations uverbs_fops = {
.release = ib_uverbs_close,
.llseek = no_llseek,
.unlocked_ioctl = ib_uverbs_ioctl,
.compat_ioctl = ib_uverbs_ioctl,
.compat_ioctl = compat_ptr_ioctl,
};
static const struct file_operations uverbs_mmap_fops = {
@ -1146,7 +1146,7 @@ static const struct file_operations uverbs_mmap_fops = {
.release = ib_uverbs_close,
.llseek = no_llseek,
.unlocked_ioctl = ib_uverbs_ioctl,
.compat_ioctl = ib_uverbs_ioctl,
.compat_ioctl = compat_ptr_ioctl,
};
static int ib_uverbs_get_nl_info(struct ib_device *ibdev, void *client_data,

View file

@ -724,9 +724,7 @@ static const struct file_operations lirc_fops = {
.owner = THIS_MODULE,
.write = ir_lirc_transmit_ir,
.unlocked_ioctl = ir_lirc_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = ir_lirc_ioctl,
#endif
.compat_ioctl = compat_ptr_ioctl,
.read = ir_lirc_read,
.poll = ir_lirc_poll,
.open = ir_lirc_open,

View file

@ -968,7 +968,7 @@ static const struct file_operations vmuser_fops = {
.release = vmci_host_close,
.poll = vmci_host_poll,
.unlocked_ioctl = vmci_host_unlocked_ioctl,
.compat_ioctl = vmci_host_unlocked_ioctl,
.compat_ioctl = compat_ptr_ioctl,
};
static struct miscdevice vmci_host_miscdev = {

View file

@ -1948,6 +1948,8 @@ static struct backing_dev_info * __init mtd_bdi_init(char *name)
bdi = bdi_alloc(GFP_KERNEL);
if (!bdi)
return ERR_PTR(-ENOMEM);
bdi->ra_pages = 0;
bdi->io_pages = 0;
bdi->name = name;
/*

View file

@ -1229,7 +1229,7 @@ static const struct file_operations nvdimm_bus_fops = {
.owner = THIS_MODULE,
.open = nd_open,
.unlocked_ioctl = bus_ioctl,
.compat_ioctl = bus_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.llseek = noop_llseek,
};
@ -1237,7 +1237,7 @@ static const struct file_operations nvdimm_fops = {
.owner = THIS_MODULE,
.open = nd_open,
.unlocked_ioctl = dimm_ioctl,
.compat_ioctl = dimm_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.llseek = noop_llseek,
};

View file

@ -137,9 +137,25 @@ static blk_status_t read_pmem(struct page *page, unsigned int off,
return BLK_STS_OK;
}
static blk_status_t pmem_do_bvec(struct pmem_device *pmem, struct page *page,
unsigned int len, unsigned int off, unsigned int op,
sector_t sector)
static blk_status_t pmem_do_read(struct pmem_device *pmem,
struct page *page, unsigned int page_off,
sector_t sector, unsigned int len)
{
blk_status_t rc;
phys_addr_t pmem_off = sector * 512 + pmem->data_offset;
void *pmem_addr = pmem->virt_addr + pmem_off;
if (unlikely(is_bad_pmem(&pmem->bb, sector, len)))
return BLK_STS_IOERR;
rc = read_pmem(page, page_off, pmem_addr, len);
flush_dcache_page(page);
return rc;
}
static blk_status_t pmem_do_write(struct pmem_device *pmem,
struct page *page, unsigned int page_off,
sector_t sector, unsigned int len)
{
blk_status_t rc = BLK_STS_OK;
bool bad_pmem = false;
@ -149,34 +165,25 @@ static blk_status_t pmem_do_bvec(struct pmem_device *pmem, struct page *page,
if (unlikely(is_bad_pmem(&pmem->bb, sector, len)))
bad_pmem = true;
if (!op_is_write(op)) {
if (unlikely(bad_pmem))
rc = BLK_STS_IOERR;
else {
rc = read_pmem(page, off, pmem_addr, len);
flush_dcache_page(page);
}
} else {
/*
* Note that we write the data both before and after
* clearing poison. The write before clear poison
* handles situations where the latest written data is
* preserved and the clear poison operation simply marks
* the address range as valid without changing the data.
* In this case application software can assume that an
* interrupted write will either return the new good
* data or an error.
*
* However, if pmem_clear_poison() leaves the data in an
* indeterminate state we need to perform the write
* after clear poison.
*/
flush_dcache_page(page);
write_pmem(pmem_addr, page, off, len);
if (unlikely(bad_pmem)) {
rc = pmem_clear_poison(pmem, pmem_off, len);
write_pmem(pmem_addr, page, off, len);
}
/*
* Note that we write the data both before and after
* clearing poison. The write before clear poison
* handles situations where the latest written data is
* preserved and the clear poison operation simply marks
* the address range as valid without changing the data.
* In this case application software can assume that an
* interrupted write will either return the new good
* data or an error.
*
* However, if pmem_clear_poison() leaves the data in an
* indeterminate state we need to perform the write
* after clear poison.
*/
flush_dcache_page(page);
write_pmem(pmem_addr, page, page_off, len);
if (unlikely(bad_pmem)) {
rc = pmem_clear_poison(pmem, pmem_off, len);
write_pmem(pmem_addr, page, page_off, len);
}
return rc;
@ -198,8 +205,12 @@ static blk_qc_t pmem_make_request(struct request_queue *q, struct bio *bio)
do_acct = nd_iostat_start(bio, &start);
bio_for_each_segment(bvec, bio, iter) {
rc = pmem_do_bvec(pmem, bvec.bv_page, bvec.bv_len,
bvec.bv_offset, bio_op(bio), iter.bi_sector);
if (op_is_write(bio_op(bio)))
rc = pmem_do_write(pmem, bvec.bv_page, bvec.bv_offset,
iter.bi_sector, bvec.bv_len);
else
rc = pmem_do_read(pmem, bvec.bv_page, bvec.bv_offset,
iter.bi_sector, bvec.bv_len);
if (rc) {
bio->bi_status = rc;
break;
@ -224,9 +235,12 @@ static int pmem_rw_page(struct block_device *bdev, sector_t sector,
struct pmem_device *pmem = bdev->bd_queue->queuedata;
blk_status_t rc;
rc = pmem_do_bvec(pmem, page, hpage_nr_pages(page) * PAGE_SIZE,
0, op, sector);
if (op_is_write(op))
rc = pmem_do_write(pmem, page, 0, sector,
hpage_nr_pages(page) * PAGE_SIZE);
else
rc = pmem_do_read(pmem, page, 0, sector,
hpage_nr_pages(page) * PAGE_SIZE);
/*
* The ->rw_page interface is subtle and tricky. The core
* retries on any error, so we can only invoke page_endio() in
@ -269,6 +283,16 @@ static const struct block_device_operations pmem_fops = {
.revalidate_disk = nvdimm_revalidate_disk,
};
static int pmem_dax_zero_page_range(struct dax_device *dax_dev, pgoff_t pgoff,
size_t nr_pages)
{
struct pmem_device *pmem = dax_get_private(dax_dev);
return blk_status_to_errno(pmem_do_write(pmem, ZERO_PAGE(0), 0,
PFN_PHYS(pgoff) >> SECTOR_SHIFT,
PAGE_SIZE));
}
static long pmem_dax_direct_access(struct dax_device *dax_dev,
pgoff_t pgoff, long nr_pages, void **kaddr, pfn_t *pfn)
{
@ -300,6 +324,7 @@ static const struct dax_operations pmem_dax_ops = {
.dax_supported = generic_fsdax_supported,
.copy_from_iter = pmem_copy_from_iter,
.copy_to_iter = pmem_copy_to_iter,
.zero_page_range = pmem_dax_zero_page_range,
};
static const struct attribute_group *pmem_attribute_groups[] = {

View file

@ -3062,7 +3062,7 @@ static const struct file_operations nvme_dev_fops = {
.open = nvme_dev_open,
.release = nvme_dev_release,
.unlocked_ioctl = nvme_dev_ioctl,
.compat_ioctl = nvme_dev_ioctl,
.compat_ioctl = compat_ptr_ioctl,
};
static ssize_t nvme_sysfs_reset(struct device *dev,

View file

@ -1025,7 +1025,7 @@ static const struct file_operations switchtec_fops = {
.read = switchtec_dev_read,
.poll = switchtec_dev_poll,
.unlocked_ioctl = switchtec_dev_ioctl,
.compat_ioctl = switchtec_dev_ioctl,
.compat_ioctl = compat_ptr_ioctl,
};
static void link_event_work(struct work_struct *work)

View file

@ -912,7 +912,7 @@ static const struct file_operations wmi_fops = {
.read = wmi_char_read,
.open = wmi_char_open,
.unlocked_ioctl = wmi_ioctl,
.compat_ioctl = wmi_ioctl,
.compat_ioctl = compat_ptr_ioctl,
};
static int wmi_dev_probe(struct device *dev)

View file

@ -292,7 +292,7 @@ static const struct file_operations rpmsg_eptdev_fops = {
.write_iter = rpmsg_eptdev_write_iter,
.poll = rpmsg_eptdev_poll,
.unlocked_ioctl = rpmsg_eptdev_ioctl,
.compat_ioctl = rpmsg_eptdev_ioctl,
.compat_ioctl = compat_ptr_ioctl,
};
static ssize_t name_show(struct device *dev, struct device_attribute *attr,
@ -446,7 +446,7 @@ static const struct file_operations rpmsg_ctrldev_fops = {
.open = rpmsg_ctrldev_open,
.release = rpmsg_ctrldev_release,
.unlocked_ioctl = rpmsg_ctrldev_ioctl,
.compat_ioctl = rpmsg_ctrldev_ioctl,
.compat_ioctl = compat_ptr_ioctl,
};
static void rpmsg_ctrldev_release_device(struct device *dev)

View file

@ -156,7 +156,7 @@ static long d7s_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
static const struct file_operations d7s_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = d7s_ioctl,
.compat_ioctl = d7s_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.open = d7s_open,
.release = d7s_release,
.llseek = noop_llseek,

View file

@ -715,9 +715,7 @@ static const struct file_operations envctrl_fops = {
.owner = THIS_MODULE,
.read = envctrl_read,
.unlocked_ioctl = envctrl_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = envctrl_ioctl,
#endif
.compat_ioctl = compat_ptr_ioctl,
.open = envctrl_open,
.release = envctrl_release,
.llseek = noop_llseek,

View file

@ -1049,9 +1049,7 @@ static int tw_chrdev_open(struct inode *inode, struct file *file)
static const struct file_operations tw_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = tw_chrdev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = tw_chrdev_ioctl,
#endif
.compat_ioctl = compat_ptr_ioctl,
.open = tw_chrdev_open,
.release = NULL,
.llseek = noop_llseek,

View file

@ -3593,7 +3593,7 @@ static const struct file_operations cxlflash_chr_fops = {
.owner = THIS_MODULE,
.open = cxlflash_chr_open,
.unlocked_ioctl = cxlflash_chr_ioctl,
.compat_ioctl = cxlflash_chr_ioctl,
.compat_ioctl = compat_ptr_ioctl,
};
/**

View file

@ -613,7 +613,7 @@ static int __init esas2r_init(void)
/* Handle ioctl calls to "/proc/scsi/esas2r/ATTOnode" */
static const struct file_operations esas2r_proc_fops = {
.compat_ioctl = esas2r_proc_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.unlocked_ioctl = esas2r_proc_ioctl,
};

View file

@ -3973,9 +3973,7 @@ static const struct file_operations pmcraid_fops = {
.open = pmcraid_chr_open,
.fasync = pmcraid_chr_fasync,
.unlocked_ioctl = pmcraid_chr_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = pmcraid_chr_ioctl,
#endif
.compat_ioctl = compat_ptr_ioctl,
.llseek = noop_llseek,
};

View file

@ -221,9 +221,7 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
static const struct file_operations ion_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = ion_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = ion_ioctl,
#endif
.compat_ioctl = compat_ptr_ioctl,
};
static int debug_shrink_set(void *data, u64 val)

View file

@ -494,7 +494,7 @@ static const struct file_operations vme_user_fops = {
.write = vme_user_write,
.llseek = vme_user_llseek,
.unlocked_ioctl = vme_user_unlocked_ioctl,
.compat_ioctl = vme_user_unlocked_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.mmap = vme_user_mmap,
};

View file

@ -683,7 +683,7 @@ static const struct file_operations tee_fops = {
.open = tee_open,
.release = tee_release,
.unlocked_ioctl = tee_ioctl,
.compat_ioctl = tee_ioctl,
.compat_ioctl = compat_ptr_ioctl,
};
static void tee_release_device(struct device *dev)

View file

@ -790,7 +790,7 @@ static const struct file_operations wdm_fops = {
.release = wdm_release,
.poll = wdm_poll,
.unlocked_ioctl = wdm_ioctl,
.compat_ioctl = wdm_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.llseek = noop_llseek,
};

View file

@ -2241,9 +2241,7 @@ static const struct file_operations fops = {
.release = usbtmc_release,
.flush = usbtmc_flush,
.unlocked_ioctl = usbtmc_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = usbtmc_ioctl,
#endif
.compat_ioctl = compat_ptr_ioctl,
.fasync = usbtmc_fasync,
.poll = usbtmc_poll,
.llseek = default_llseek,

View file

@ -705,7 +705,7 @@ static const struct file_operations fsl_hv_fops = {
.poll = fsl_hv_poll,
.read = fsl_hv_read,
.unlocked_ioctl = fsl_hv_ioctl,
.compat_ioctl = fsl_hv_ioctl,
.compat_ioctl = compat_ptr_ioctl,
};
static struct miscdevice fsl_hv_misc_dev = {

View file

@ -455,7 +455,7 @@ static int v9fs_test_inode(struct inode *inode, void *data)
umode = p9mode2unixmode(v9ses, st, &rdev);
/* don't match inode of different type */
if ((inode->i_mode & S_IFMT) != (umode & S_IFMT))
if (inode_wrong_type(inode, umode))
return 0;
/* compare qid details */
@ -1412,7 +1412,7 @@ int v9fs_refresh_inode(struct p9_fid *fid, struct inode *inode)
* Don't update inode if the file type is different
*/
umode = p9mode2unixmode(v9ses, st, &rdev);
if ((inode->i_mode & S_IFMT) != (umode & S_IFMT))
if (inode_wrong_type(inode, umode))
goto out;
/*

View file

@ -59,7 +59,7 @@ static int v9fs_test_inode_dotl(struct inode *inode, void *data)
struct p9_stat_dotl *st = (struct p9_stat_dotl *)data;
/* don't match inode of different type */
if ((inode->i_mode & S_IFMT) != (st->st_mode & S_IFMT))
if (inode_wrong_type(inode, st->st_mode))
return 0;
if (inode->i_generation != st->st_gen)
@ -924,7 +924,7 @@ int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode)
/*
* Don't update inode if the file type is different
*/
if ((inode->i_mode & S_IFMT) != (st->st_mode & S_IFMT))
if (inode_wrong_type(inode, st->st_mode))
goto out;
/*

View file

@ -80,8 +80,10 @@ v9fs_fill_super(struct super_block *sb, struct v9fs_session_info *v9ses,
if (ret)
return ret;
if (v9ses->cache)
sb->s_bdi->ra_pages = VM_READAHEAD_PAGES;
if (!v9ses->cache) {
sb->s_bdi->ra_pages = 0;
sb->s_bdi->io_pages = 0;
}
sb->s_flags |= SB_ACTIVE | SB_DIRSYNC;
if (!v9ses->cache)

View file

@ -449,7 +449,6 @@ static int afs_fill_super(struct super_block *sb, struct afs_fs_context *ctx)
ret = super_setup_bdi(sb);
if (ret)
return ret;
sb->s_bdi->ra_pages = VM_READAHEAD_PAGES;
/* allocate the root inode and dentry */
if (as->dyn_root) {

View file

@ -3008,7 +3008,6 @@ int open_ctree(struct super_block *sb,
sb->s_bdi->congested_fn = btrfs_congested_fn;
sb->s_bdi->congested_data = fs_info;
sb->s_bdi->capabilities |= BDI_CAP_CGROUP_WRITEBACK;
sb->s_bdi->ra_pages = VM_READAHEAD_PAGES;
sb->s_bdi->ra_pages *= btrfs_super_num_devices(disk_super);
sb->s_bdi->ra_pages = max(sb->s_bdi->ra_pages, SZ_4M / PAGE_SIZE);

View file

@ -2322,7 +2322,7 @@ static const struct super_operations btrfs_super_ops = {
static const struct file_operations btrfs_ctl_fops = {
.open = btrfs_control_open,
.unlocked_ioctl = btrfs_control_ioctl,
.compat_ioctl = btrfs_control_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.owner = THIS_MODULE,
.llseek = noop_llseek,
};

View file

@ -420,8 +420,7 @@ int cifs_get_inode_info_unix(struct inode **pinode,
}
/* if filetype is different, return error */
if (unlikely(((*pinode)->i_mode & S_IFMT) !=
(fattr.cf_mode & S_IFMT))) {
if (unlikely(inode_wrong_type(*pinode, fattr.cf_mode))) {
CIFS_I(*pinode)->time = 0; /* force reval */
rc = -ESTALE;
goto cgiiu_exit;
@ -980,7 +979,7 @@ cifs_find_inode(struct inode *inode, void *opaque)
return 0;
/* don't match inode of different type */
if ((inode->i_mode & S_IFMT) != (fattr->cf_mode & S_IFMT))
if (inode_wrong_type(inode, fattr->cf_mode))
return 0;
/* if it's not a directory or has no dentries, then flag it */

View file

@ -8,7 +8,7 @@ config FUSE_FS
There's also a companion library: libfuse2. This library is available
from the FUSE homepage:
<http://fuse.sourceforge.net/>
<https://github.com/libfuse/>
although chances are your distribution already has that library
installed if you've installed the "fuse" package itself.
@ -34,7 +34,29 @@ config VIRTIO_FS
select VIRTIO
help
The Virtio Filesystem allows guests to mount file systems from the
host.
host.
If you want to share files between guests or with the host, answer Y
or M.
or M.
config FUSE_DAX
bool "Virtio Filesystem Direct Host Memory Access support"
default y
select INTERVAL_TREE
depends on VIRTIO_FS
depends on FS_DAX
depends on DAX_DRIVER
help
This allows bypassing guest page cache and allows mapping host page
cache directly in guest address space.
If you want to allow mounting a Virtio Filesystem with the "dax"
option, answer Y.
config FUSE_BPF
bool "Adds BPF to fuse"
depends on FUSE_FS
depends on BPF
help
Extends FUSE by adding BPF to prefilter calls and potentially pass to a
backing file system

View file

@ -7,6 +7,8 @@ obj-$(CONFIG_FUSE_FS) += fuse.o
obj-$(CONFIG_CUSE) += cuse.o
obj-$(CONFIG_VIRTIO_FS) += virtiofs.o
fuse-objs := dev.o dir.o file.o inode.o control.o xattr.o acl.o readdir.o
fuse-objs += passthrough.o
virtiofs-y += virtio_fs.o
fuse-y := dev.o dir.o file.o inode.o control.o xattr.o acl.o readdir.o passthrough.o
fuse-$(CONFIG_FUSE_DAX) += dax.o
fuse-$(CONFIG_FUSE_BPF) += backing.o
virtiofs-y := virtio_fs.o

2546
fs/fuse/backing.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -164,6 +164,7 @@ static ssize_t fuse_conn_congestion_threshold_write(struct file *file,
{
unsigned val;
struct fuse_conn *fc;
struct fuse_mount *fm;
ssize_t ret;
ret = fuse_conn_limit_write(file, buf, count, ppos, &val,
@ -174,18 +175,27 @@ static ssize_t fuse_conn_congestion_threshold_write(struct file *file,
if (!fc)
goto out;
down_read(&fc->killsb);
spin_lock(&fc->bg_lock);
fc->congestion_threshold = val;
if (fc->sb) {
/*
* Get any fuse_mount belonging to this fuse_conn; s_bdi is
* shared between all of them
*/
if (!list_empty(&fc->mounts)) {
fm = list_first_entry(&fc->mounts, struct fuse_mount, fc_entry);
if (fc->num_background < fc->congestion_threshold) {
clear_bdi_congested(fc->sb->s_bdi, BLK_RW_SYNC);
clear_bdi_congested(fc->sb->s_bdi, BLK_RW_ASYNC);
clear_bdi_congested(fm->sb->s_bdi, BLK_RW_SYNC);
clear_bdi_congested(fm->sb->s_bdi, BLK_RW_ASYNC);
} else {
set_bdi_congested(fc->sb->s_bdi, BLK_RW_SYNC);
set_bdi_congested(fc->sb->s_bdi, BLK_RW_ASYNC);
set_bdi_congested(fm->sb->s_bdi, BLK_RW_SYNC);
set_bdi_congested(fm->sb->s_bdi, BLK_RW_ASYNC);
}
}
spin_unlock(&fc->bg_lock);
up_read(&fc->killsb);
fuse_conn_put(fc);
out:
return ret;
@ -318,7 +328,7 @@ void fuse_ctl_remove_conn(struct fuse_conn *fc)
drop_nlink(d_inode(fuse_control_sb->s_root));
}
static int fuse_ctl_fill_super(struct super_block *sb, struct fs_context *fctx)
static int fuse_ctl_fill_super(struct super_block *sb, struct fs_context *fsc)
{
static const struct tree_descr empty_descr = {""};
struct fuse_conn *fc;
@ -344,18 +354,18 @@ static int fuse_ctl_fill_super(struct super_block *sb, struct fs_context *fctx)
return 0;
}
static int fuse_ctl_get_tree(struct fs_context *fc)
static int fuse_ctl_get_tree(struct fs_context *fsc)
{
return get_tree_single(fc, fuse_ctl_fill_super);
return get_tree_single(fsc, fuse_ctl_fill_super);
}
static const struct fs_context_operations fuse_ctl_context_ops = {
.get_tree = fuse_ctl_get_tree,
};
static int fuse_ctl_init_fs_context(struct fs_context *fc)
static int fuse_ctl_init_fs_context(struct fs_context *fsc)
{
fc->ops = &fuse_ctl_context_ops;
fsc->ops = &fuse_ctl_context_ops;
return 0;
}
@ -385,7 +395,7 @@ int __init fuse_ctl_init(void)
return register_filesystem(&fuse_ctl_fs_type);
}
void __exit fuse_ctl_cleanup(void)
void fuse_ctl_cleanup(void)
{
unregister_filesystem(&fuse_ctl_fs_type);
}

View file

@ -57,6 +57,7 @@
struct cuse_conn {
struct list_head list; /* linked on cuse_conntbl */
struct fuse_mount fm; /* Dummy mount referencing fc */
struct fuse_conn fc; /* fuse connection */
struct cdev *cdev; /* associated character device */
struct device *dev; /* device representing @cdev */
@ -134,7 +135,7 @@ static int cuse_open(struct inode *inode, struct file *file)
* Generic permission check is already done against the chrdev
* file, proceed to open.
*/
rc = fuse_do_open(&cc->fc, 0, file, 0);
rc = fuse_do_open(&cc->fm, 0, file, 0);
if (rc)
fuse_conn_put(&cc->fc);
return rc;
@ -143,10 +144,10 @@ static int cuse_open(struct inode *inode, struct file *file)
static int cuse_release(struct inode *inode, struct file *file)
{
struct fuse_file *ff = file->private_data;
struct fuse_conn *fc = ff->fc;
struct fuse_mount *fm = ff->fm;
fuse_sync_release(NULL, ff, file->f_flags);
fuse_conn_put(fc);
fuse_conn_put(fm->fc);
return 0;
}
@ -155,7 +156,7 @@ static long cuse_file_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct fuse_file *ff = file->private_data;
struct cuse_conn *cc = fc_to_cc(ff->fc);
struct cuse_conn *cc = fc_to_cc(ff->fm->fc);
unsigned int flags = 0;
if (cc->unrestricted_ioctl)
@ -168,7 +169,7 @@ static long cuse_file_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct fuse_file *ff = file->private_data;
struct cuse_conn *cc = fc_to_cc(ff->fc);
struct cuse_conn *cc = fc_to_cc(ff->fm->fc);
unsigned int flags = FUSE_IOCTL_COMPAT;
if (cc->unrestricted_ioctl)
@ -313,9 +314,10 @@ struct cuse_init_args {
* required data structures for it. Please read the comment at the
* top of this file for high level overview.
*/
static void cuse_process_init_reply(struct fuse_conn *fc,
static void cuse_process_init_reply(struct fuse_mount *fm,
struct fuse_args *args, int error)
{
struct fuse_conn *fc = fm->fc;
struct cuse_init_args *ia = container_of(args, typeof(*ia), ap.args);
struct fuse_args_pages *ap = &ia->ap;
struct cuse_conn *cc = fc_to_cc(fc), *pos;
@ -424,7 +426,7 @@ static int cuse_send_init(struct cuse_conn *cc)
{
int rc;
struct page *page;
struct fuse_conn *fc = &cc->fc;
struct fuse_mount *fm = &cc->fm;
struct cuse_init_args *ia;
struct fuse_args_pages *ap;
@ -451,8 +453,8 @@ static int cuse_send_init(struct cuse_conn *cc)
ap->args.out_args[0].size = sizeof(ia->out);
ap->args.out_args[0].value = &ia->out;
ap->args.out_args[1].size = CUSE_INIT_INFO_MAX;
ap->args.out_argvar = 1;
ap->args.out_pages = 1;
ap->args.out_argvar = true;
ap->args.out_pages = true;
ap->num_pages = 1;
ap->pages = &ia->page;
ap->descs = &ia->desc;
@ -460,7 +462,7 @@ static int cuse_send_init(struct cuse_conn *cc)
ia->desc.length = ap->args.out_args[1].size;
ap->args.end = cuse_process_init_reply;
rc = fuse_simple_background(fc, &ap->args, GFP_KERNEL);
rc = fuse_simple_background(fm, &ap->args, GFP_KERNEL);
if (rc) {
kfree(ia);
err_free_page:
@ -506,7 +508,8 @@ static int cuse_channel_open(struct inode *inode, struct file *file)
* Limit the cuse channel to requests that can
* be represented in file->f_cred->user_ns.
*/
fuse_conn_init(&cc->fc, file->f_cred->user_ns, &fuse_dev_fiq_ops, NULL);
fuse_conn_init(&cc->fc, &cc->fm, file->f_cred->user_ns,
&fuse_dev_fiq_ops, NULL);
fud = fuse_dev_alloc_install(&cc->fc);
if (!fud) {

1366
fs/fuse/dax.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -42,20 +42,21 @@ static struct fuse_dev *fuse_get_dev(struct file *file)
return READ_ONCE(file->private_data);
}
static void fuse_request_init(struct fuse_req *req)
static void fuse_request_init(struct fuse_mount *fm, struct fuse_req *req)
{
INIT_LIST_HEAD(&req->list);
INIT_LIST_HEAD(&req->intr_entry);
init_waitqueue_head(&req->waitq);
refcount_set(&req->count, 1);
__set_bit(FR_PENDING, &req->flags);
req->fm = fm;
}
static struct fuse_req *fuse_request_alloc(gfp_t flags)
static struct fuse_req *fuse_request_alloc(struct fuse_mount *fm, gfp_t flags)
{
struct fuse_req *req = kmem_cache_zalloc(fuse_req_cachep, flags);
if (req)
fuse_request_init(req);
fuse_request_init(fm, req);
return req;
}
@ -102,10 +103,11 @@ static void fuse_drop_waiting(struct fuse_conn *fc)
}
}
static void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req);
static void fuse_put_request(struct fuse_req *req);
static struct fuse_req *fuse_get_req(struct fuse_conn *fc, bool for_background)
static struct fuse_req *fuse_get_req(struct fuse_mount *fm, bool for_background)
{
struct fuse_conn *fc = fm->fc;
struct fuse_req *req;
int err;
atomic_inc(&fc->num_waiting);
@ -127,7 +129,7 @@ static struct fuse_req *fuse_get_req(struct fuse_conn *fc, bool for_background)
if (fc->conn_error)
goto out;
req = fuse_request_alloc(GFP_KERNEL);
req = fuse_request_alloc(fm, GFP_KERNEL);
err = -ENOMEM;
if (!req) {
if (for_background)
@ -145,7 +147,7 @@ static struct fuse_req *fuse_get_req(struct fuse_conn *fc, bool for_background)
if (unlikely(req->in.h.uid == ((uid_t)-1) ||
req->in.h.gid == ((gid_t)-1))) {
fuse_put_request(fc, req);
fuse_put_request(req);
return ERR_PTR(-EOVERFLOW);
}
return req;
@ -155,8 +157,10 @@ static struct fuse_req *fuse_get_req(struct fuse_conn *fc, bool for_background)
return ERR_PTR(err);
}
static void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
static void fuse_put_request(struct fuse_req *req)
{
struct fuse_conn *fc = req->fm->fc;
if (refcount_dec_and_test(&req->count)) {
if (test_bit(FR_BACKGROUND, &req->flags)) {
/*
@ -239,6 +243,11 @@ void fuse_queue_forget(struct fuse_conn *fc, struct fuse_forget_link *forget,
{
struct fuse_iqueue *fiq = &fc->iq;
if (nodeid == 0) {
kfree(forget);
return;
}
forget->forget_one.nodeid = nodeid;
forget->forget_one.nlookup = nlookup;
@ -278,8 +287,10 @@ static void flush_bg_queue(struct fuse_conn *fc)
* the 'end' callback is called if given, else the reference to the
* request is released
*/
void fuse_request_end(struct fuse_conn *fc, struct fuse_req *req)
void fuse_request_end(struct fuse_req *req)
{
struct fuse_mount *fm = req->fm;
struct fuse_conn *fc = fm->fc;
struct fuse_iqueue *fiq = &fc->iq;
if (test_and_set_bit(FR_FINISHED, &req->flags))
@ -314,9 +325,9 @@ void fuse_request_end(struct fuse_conn *fc, struct fuse_req *req)
wake_up(&fc->blocked_waitq);
}
if (fc->num_background == fc->congestion_threshold && fc->sb) {
clear_bdi_congested(fc->sb->s_bdi, BLK_RW_SYNC);
clear_bdi_congested(fc->sb->s_bdi, BLK_RW_ASYNC);
if (fc->num_background == fc->congestion_threshold && fm->sb) {
clear_bdi_congested(fm->sb->s_bdi, BLK_RW_SYNC);
clear_bdi_congested(fm->sb->s_bdi, BLK_RW_ASYNC);
}
fc->num_background--;
fc->active_background--;
@ -328,14 +339,16 @@ void fuse_request_end(struct fuse_conn *fc, struct fuse_req *req)
}
if (test_bit(FR_ASYNC, &req->flags))
req->args->end(fc, req->args, req->out.h.error);
req->args->end(fm, req->args, req->out.h.error);
put_request:
fuse_put_request(fc, req);
fuse_put_request(req);
}
EXPORT_SYMBOL_GPL(fuse_request_end);
static int queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req)
static int queue_interrupt(struct fuse_req *req)
{
struct fuse_iqueue *fiq = &req->fm->fc->iq;
spin_lock(&fiq->lock);
/* Check for we've sent request to interrupt this req */
if (unlikely(!test_bit(FR_INTERRUPTED, &req->flags))) {
@ -347,7 +360,7 @@ static int queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req)
list_add_tail(&req->intr_entry, &fiq->interrupts);
/*
* Pairs with smp_mb() implied by test_and_set_bit()
* from request_end().
* from fuse_request_end().
*/
smp_mb();
if (test_bit(FR_FINISHED, &req->flags)) {
@ -362,8 +375,9 @@ static int queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req)
return 0;
}
static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req)
static void request_wait_answer(struct fuse_req *req)
{
struct fuse_conn *fc = req->fm->fc;
struct fuse_iqueue *fiq = &fc->iq;
int err;
@ -378,7 +392,7 @@ static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req)
/* matches barrier in fuse_dev_do_read() */
smp_mb__after_atomic();
if (test_bit(FR_SENT, &req->flags))
queue_interrupt(fiq, req);
queue_interrupt(req);
}
if (!test_bit(FR_FORCE, &req->flags)) {
@ -409,9 +423,9 @@ static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req)
test_bit(FR_FINISHED, &req->flags));
}
static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
static void __fuse_request_send(struct fuse_req *req)
{
struct fuse_iqueue *fiq = &fc->iq;
struct fuse_iqueue *fiq = &req->fm->fc->iq;
BUG_ON(test_bit(FR_BACKGROUND, &req->flags));
spin_lock(&fiq->lock);
@ -425,7 +439,7 @@ static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
__fuse_get_request(req);
queue_request_and_unlock(fiq, req, 1);
request_wait_answer(fc, req);
request_wait_answer(req);
/* Pairs with smp_wmb() in fuse_request_end() */
smp_rmb();
}
@ -464,8 +478,10 @@ static void fuse_adjust_compat(struct fuse_conn *fc, struct fuse_args *args)
}
}
static void fuse_force_creds(struct fuse_conn *fc, struct fuse_req *req)
static void fuse_force_creds(struct fuse_req *req)
{
struct fuse_conn *fc = req->fm->fc;
req->in.h.uid = from_kuid_munged(fc->user_ns, current_fsuid());
req->in.h.gid = from_kgid_munged(fc->user_ns, current_fsgid());
req->in.h.pid = pid_nr_ns(task_pid(current), fc->pid_ns);
@ -475,28 +491,30 @@ static void fuse_args_to_req(struct fuse_req *req, struct fuse_args *args)
{
req->in.h.opcode = args->opcode;
req->in.h.nodeid = args->nodeid;
req->in.h.error_in = args->error_in;
req->args = args;
if (args->end)
__set_bit(FR_ASYNC, &req->flags);
}
ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args)
ssize_t fuse_simple_request(struct fuse_mount *fm, struct fuse_args *args)
{
struct fuse_conn *fc = fm->fc;
struct fuse_req *req;
ssize_t ret;
if (args->force) {
atomic_inc(&fc->num_waiting);
req = fuse_request_alloc(GFP_KERNEL | __GFP_NOFAIL);
req = fuse_request_alloc(fm, GFP_KERNEL | __GFP_NOFAIL);
if (!args->nocreds)
fuse_force_creds(fc, req);
fuse_force_creds(req);
__set_bit(FR_WAITING, &req->flags);
__set_bit(FR_FORCE, &req->flags);
} else {
WARN_ON(args->nocreds);
req = fuse_get_req(fc, false);
req = fuse_get_req(fm, false);
if (IS_ERR(req))
return PTR_ERR(req);
}
@ -507,20 +525,21 @@ ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args)
if (!args->noreply)
__set_bit(FR_ISREPLY, &req->flags);
__fuse_request_send(fc, req);
__fuse_request_send(req);
ret = req->out.h.error;
if (!ret && args->out_argvar) {
BUG_ON(args->out_numargs == 0);
ret = args->out_args[args->out_numargs - 1].size;
}
fuse_put_request(fc, req);
fuse_put_request(req);
return ret;
}
static bool fuse_request_queue_background(struct fuse_conn *fc,
struct fuse_req *req)
static bool fuse_request_queue_background(struct fuse_req *req)
{
struct fuse_mount *fm = req->fm;
struct fuse_conn *fc = fm->fc;
bool queued = false;
WARN_ON(!test_bit(FR_BACKGROUND, &req->flags));
@ -534,9 +553,9 @@ static bool fuse_request_queue_background(struct fuse_conn *fc,
fc->num_background++;
if (fc->num_background == fc->max_background)
fc->blocked = 1;
if (fc->num_background == fc->congestion_threshold && fc->sb) {
set_bdi_congested(fc->sb->s_bdi, BLK_RW_SYNC);
set_bdi_congested(fc->sb->s_bdi, BLK_RW_ASYNC);
if (fc->num_background == fc->congestion_threshold && fm->sb) {
set_bdi_congested(fm->sb->s_bdi, BLK_RW_SYNC);
set_bdi_congested(fm->sb->s_bdi, BLK_RW_ASYNC);
}
list_add_tail(&req->list, &fc->bg_queue);
flush_bg_queue(fc);
@ -547,28 +566,28 @@ static bool fuse_request_queue_background(struct fuse_conn *fc,
return queued;
}
int fuse_simple_background(struct fuse_conn *fc, struct fuse_args *args,
int fuse_simple_background(struct fuse_mount *fm, struct fuse_args *args,
gfp_t gfp_flags)
{
struct fuse_req *req;
if (args->force) {
WARN_ON(!args->nocreds);
req = fuse_request_alloc(gfp_flags);
req = fuse_request_alloc(fm, gfp_flags);
if (!req)
return -ENOMEM;
__set_bit(FR_BACKGROUND, &req->flags);
} else {
WARN_ON(args->nocreds);
req = fuse_get_req(fc, true);
req = fuse_get_req(fm, true);
if (IS_ERR(req))
return PTR_ERR(req);
}
fuse_args_to_req(req, args);
if (!fuse_request_queue_background(fc, req)) {
fuse_put_request(fc, req);
if (!fuse_request_queue_background(req)) {
fuse_put_request(req);
return -ENOTCONN;
}
@ -576,14 +595,14 @@ int fuse_simple_background(struct fuse_conn *fc, struct fuse_args *args,
}
EXPORT_SYMBOL_GPL(fuse_simple_background);
static int fuse_simple_notify_reply(struct fuse_conn *fc,
static int fuse_simple_notify_reply(struct fuse_mount *fm,
struct fuse_args *args, u64 unique)
{
struct fuse_req *req;
struct fuse_iqueue *fiq = &fc->iq;
struct fuse_iqueue *fiq = &fm->fc->iq;
int err = 0;
req = fuse_get_req(fc, false);
req = fuse_get_req(fm, false);
if (IS_ERR(req))
return PTR_ERR(req);
@ -598,7 +617,7 @@ static int fuse_simple_notify_reply(struct fuse_conn *fc,
} else {
err = -ENODEV;
spin_unlock(&fiq->lock);
fuse_put_request(fc, req);
fuse_put_request(req);
}
return err;
@ -677,6 +696,10 @@ static void fuse_copy_finish(struct fuse_copy_state *cs)
flush_dcache_page(cs->pg);
set_page_dirty_lock(cs->pg);
}
/*
* The page could be GUP page(see iov_iter_get_pages in
* fuse_copy_fill) so use put_page to release it.
*/
put_page(cs->pg);
}
cs->pg = NULL;
@ -780,8 +803,7 @@ static int fuse_check_page(struct page *page)
1 << PG_workingset |
1 << PG_reclaim |
1 << PG_waiters))) {
pr_warn("trying to steal weird page\n");
pr_warn(" page=%p index=%li flags=%08lx, count=%i, mapcount=%i, mapping=%p\n", page, page->index, page->flags, page_count(page), page_mapcount(page), page->mapping);
dump_page(page, "fuse: trying to steal weird page");
return 1;
}
return 0;
@ -1283,7 +1305,7 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file,
/* SETXATTR is special, since it may contain too large data */
if (args->opcode == FUSE_SETXATTR)
req->out.h.error = -E2BIG;
fuse_request_end(fc, req);
fuse_request_end(req);
goto restart;
}
spin_lock(&fpq->lock);
@ -1326,8 +1348,8 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file,
/* matches barrier in request_wait_answer() */
smp_mb__after_atomic();
if (test_bit(FR_INTERRUPTED, &req->flags))
queue_interrupt(fiq, req);
fuse_put_request(fc, req);
queue_interrupt(req);
fuse_put_request(req);
return reqsize;
@ -1335,7 +1357,7 @@ out_end:
if (!test_bit(FR_PRIVATE, &req->flags))
list_del_init(&req->list);
spin_unlock(&fpq->lock);
fuse_request_end(fc, req);
fuse_request_end(req);
return err;
err_unlock:
@ -1458,11 +1480,8 @@ static int fuse_notify_inval_inode(struct fuse_conn *fc, unsigned int size,
fuse_copy_finish(cs);
down_read(&fc->killsb);
err = -ENOENT;
if (fc->sb) {
err = fuse_reverse_inval_inode(fc->sb, outarg.ino,
outarg.off, outarg.len);
}
err = fuse_reverse_inval_inode(fc, outarg.ino,
outarg.off, outarg.len);
up_read(&fc->killsb);
return err;
@ -1508,9 +1527,7 @@ static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size,
buf[outarg.namelen] = 0;
down_read(&fc->killsb);
err = -ENOENT;
if (fc->sb)
err = fuse_reverse_inval_entry(fc->sb, outarg.parent, 0, &name);
err = fuse_reverse_inval_entry(fc, outarg.parent, 0, &name);
up_read(&fc->killsb);
kfree(buf);
return err;
@ -1558,10 +1575,7 @@ static int fuse_notify_delete(struct fuse_conn *fc, unsigned int size,
buf[outarg.namelen] = 0;
down_read(&fc->killsb);
err = -ENOENT;
if (fc->sb)
err = fuse_reverse_inval_entry(fc->sb, outarg.parent,
outarg.child, &name);
err = fuse_reverse_inval_entry(fc, outarg.parent, outarg.child, &name);
up_read(&fc->killsb);
kfree(buf);
return err;
@ -1603,10 +1617,7 @@ static int fuse_notify_store(struct fuse_conn *fc, unsigned int size,
down_read(&fc->killsb);
err = -ENOENT;
if (!fc->sb)
goto out_up_killsb;
inode = ilookup5(fc->sb, nodeid, fuse_inode_eq, &nodeid);
inode = fuse_ilookup(fc, nodeid, NULL);
if (!inode)
goto out_up_killsb;
@ -1665,7 +1676,7 @@ struct fuse_retrieve_args {
struct fuse_notify_retrieve_in inarg;
};
static void fuse_retrieve_end(struct fuse_conn *fc, struct fuse_args *args,
static void fuse_retrieve_end(struct fuse_mount *fm, struct fuse_args *args,
int error)
{
struct fuse_retrieve_args *ra =
@ -1675,7 +1686,7 @@ static void fuse_retrieve_end(struct fuse_conn *fc, struct fuse_args *args,
kfree(ra);
}
static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode,
static int fuse_retrieve(struct fuse_mount *fm, struct inode *inode,
struct fuse_notify_retrieve_out *outarg)
{
int err;
@ -1686,6 +1697,7 @@ static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode,
unsigned int offset;
size_t total_len = 0;
unsigned int num_pages;
struct fuse_conn *fc = fm->fc;
struct fuse_retrieve_args *ra;
size_t args_size = sizeof(*ra);
struct fuse_args_pages *ap;
@ -1747,9 +1759,9 @@ static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode,
args->in_args[0].value = &ra->inarg;
args->in_args[1].size = total_len;
err = fuse_simple_notify_reply(fc, args, outarg->notify_unique);
err = fuse_simple_notify_reply(fm, args, outarg->notify_unique);
if (err)
fuse_retrieve_end(fc, args, err);
fuse_retrieve_end(fm, args, err);
return err;
}
@ -1758,7 +1770,9 @@ static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size,
struct fuse_copy_state *cs)
{
struct fuse_notify_retrieve_out outarg;
struct fuse_mount *fm;
struct inode *inode;
u64 nodeid;
int err;
err = -EINVAL;
@ -1773,14 +1787,12 @@ static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size,
down_read(&fc->killsb);
err = -ENOENT;
if (fc->sb) {
u64 nodeid = outarg.nodeid;
nodeid = outarg.nodeid;
inode = ilookup5(fc->sb, nodeid, fuse_inode_eq, &nodeid);
if (inode) {
err = fuse_retrieve(fc, inode, &outarg);
iput(inode);
}
inode = fuse_ilookup(fc, nodeid, &fm);
if (inode) {
err = fuse_retrieve(fm, inode, &outarg);
iput(inode);
}
up_read(&fc->killsb);
@ -1919,9 +1931,9 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
else if (oh.error == -ENOSYS)
fc->no_interrupt = 1;
else if (oh.error == -EAGAIN)
err = queue_interrupt(&fc->iq, req);
err = queue_interrupt(req);
fuse_put_request(fc, req);
fuse_put_request(req);
goto copy_finish;
}
@ -1947,6 +1959,19 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
req->out.h.error = kern_path(path, 0, req->args->canonical_path);
}
if (!err && (req->in.h.opcode == FUSE_LOOKUP ||
req->in.h.opcode == (FUSE_LOOKUP | FUSE_POSTFILTER)) &&
req->args->out_args[1].size == sizeof(struct fuse_entry_bpf_out)) {
struct fuse_entry_bpf_out *febo = (struct fuse_entry_bpf_out *)
req->args->out_args[1].value;
struct fuse_entry_bpf *feb = container_of(febo, struct fuse_entry_bpf, out);
if (febo->backing_action == FUSE_ACTION_REPLACE)
feb->backing_file = fget(febo->backing_fd);
if (febo->bpf_action == FUSE_ACTION_REPLACE)
feb->bpf_file = fget(febo->bpf_fd);
}
spin_lock(&fpq->lock);
clear_bit(FR_LOCKED, &req->flags);
if (!fpq->connected)
@ -1957,7 +1982,7 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
list_del_init(&req->list);
spin_unlock(&fpq->lock);
fuse_request_end(fc, req);
fuse_request_end(req);
out:
return err ? err : nbytes;
@ -2093,7 +2118,7 @@ static __poll_t fuse_dev_poll(struct file *file, poll_table *wait)
}
/* Abort all requests on the given list (pending or processing) */
static void end_requests(struct fuse_conn *fc, struct list_head *head)
static void end_requests(struct list_head *head)
{
while (!list_empty(head)) {
struct fuse_req *req;
@ -2101,7 +2126,7 @@ static void end_requests(struct fuse_conn *fc, struct list_head *head)
req->out.h.error = -ECONNABORTED;
clear_bit(FR_SENT, &req->flags);
list_del_init(&req->list);
fuse_request_end(fc, req);
fuse_request_end(req);
}
}
@ -2196,7 +2221,7 @@ void fuse_abort_conn(struct fuse_conn *fc)
wake_up_all(&fc->blocked_waitq);
spin_unlock(&fc->lock);
end_requests(fc, &to_end);
end_requests(&to_end);
} else {
spin_unlock(&fc->lock);
}
@ -2226,7 +2251,7 @@ int fuse_dev_release(struct inode *inode, struct file *file)
list_splice_init(&fpq->processing[i], &to_end);
spin_unlock(&fpq->lock);
end_requests(fc, &to_end);
end_requests(&to_end);
/* Are we the last open device? */
if (atomic_dec_and_test(&fc->dev_count)) {
@ -2328,7 +2353,7 @@ const struct file_operations fuse_dev_operations = {
.release = fuse_dev_release,
.fasync = fuse_dev_fasync,
.unlocked_ioctl = fuse_dev_ioctl,
.compat_ioctl = fuse_dev_ioctl,
.compat_ioctl = compat_ptr_ioctl,
};
EXPORT_SYMBOL_GPL(fuse_dev_operations);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -14,18 +14,6 @@ struct fuse_aio_req {
struct kiocb *iocb_fuse;
};
static inline void kiocb_clone(struct kiocb *kiocb, struct kiocb *kiocb_src,
struct file *filp)
{
*kiocb = (struct kiocb){
.ki_filp = filp,
.ki_flags = kiocb_src->ki_flags,
.ki_hint = kiocb_src->ki_hint,
.ki_ioprio = kiocb_src->ki_ioprio,
.ki_pos = kiocb_src->ki_pos,
};
}
static void fuse_file_accessed(struct file *dst_file, struct file *src_file)
{
struct inode *dst_inode;
@ -46,7 +34,7 @@ static void fuse_file_accessed(struct file *dst_file, struct file *src_file)
touch_atime(&dst_file->f_path);
}
static void fuse_copyattr(struct file *dst_file, struct file *src_file)
void fuse_copyattr(struct file *dst_file, struct file *src_file)
{
struct inode *dst = file_inode(dst_file);
struct inode *src = file_inode(src_file);
@ -225,7 +213,8 @@ int fuse_passthrough_open(struct fuse_dev *fud, u32 lower_fd)
}
if (!passthrough_filp->f_op->read_iter ||
!passthrough_filp->f_op->write_iter) {
!((passthrough_filp->f_path.mnt->mnt_flags | MNT_READONLY) ||
passthrough_filp->f_op->write_iter)) {
pr_err("FUSE: passthrough file misses file operations.\n");
res = -EBADF;
goto err_free_file;

View file

@ -20,6 +20,8 @@ static bool fuse_use_readdirplus(struct inode *dir, struct dir_context *ctx)
if (!fc->do_readdirplus)
return false;
if (fi->nodeid == 0)
return false;
if (!fc->readdirplus_auto)
return true;
if (test_and_clear_bit(FUSE_I_ADVISE_RDPLUS, &fi->state))
@ -77,8 +79,10 @@ static void fuse_add_dirent_to_cache(struct file *file,
goto unlock;
addr = kmap_atomic(page);
if (!offset)
if (!offset) {
clear_page(addr);
SetPageUptodate(page);
}
memcpy(addr + offset, dirent, reclen);
kunmap_atomic(addr);
fi->rdc.size = (index << PAGE_SHIFT) + offset + reclen;
@ -200,9 +204,12 @@ retry:
if (!d_in_lookup(dentry)) {
struct fuse_inode *fi;
inode = d_inode(dentry);
if (inode && get_node_id(inode) != o->nodeid)
inode = NULL;
if (!inode ||
get_node_id(inode) != o->nodeid ||
((o->attr.mode ^ inode->i_mode) & S_IFMT)) {
fuse_stale_inode(inode, o->generation, &o->attr)) {
if (inode)
fuse_make_bad(inode);
d_invalidate(dentry);
dput(dentry);
goto retry;
@ -260,7 +267,7 @@ retry:
static void fuse_force_forget(struct file *file, u64 nodeid)
{
struct inode *inode = file_inode(file);
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_mount *fm = get_fuse_mount(inode);
struct fuse_forget_in inarg;
FUSE_ARGS(args);
@ -274,7 +281,7 @@ static void fuse_force_forget(struct file *file, u64 nodeid)
args.force = true;
args.noreply = true;
fuse_simple_request(fc, &args);
fuse_simple_request(fm, &args);
/* ignore errors */
}
@ -328,7 +335,7 @@ static int fuse_readdir_uncached(struct file *file, struct dir_context *ctx)
ssize_t res;
struct page *page;
struct inode *inode = file_inode(file);
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_mount *fm = get_fuse_mount(inode);
struct fuse_io_args ia = {};
struct fuse_args_pages *ap = &ia.ap;
struct fuse_page_desc desc = { .length = PAGE_SIZE };
@ -340,12 +347,12 @@ static int fuse_readdir_uncached(struct file *file, struct dir_context *ctx)
return -ENOMEM;
plus = fuse_use_readdirplus(inode, ctx);
ap->args.out_pages = 1;
ap->args.out_pages = true;
ap->num_pages = 1;
ap->pages = &page;
ap->descs = &desc;
if (plus) {
attr_version = fuse_get_attr_version(fc);
attr_version = fuse_get_attr_version(fm->fc);
fuse_read_args_fill(&ia, file, ctx->pos, PAGE_SIZE,
FUSE_READDIRPLUS);
} else {
@ -353,7 +360,7 @@ static int fuse_readdir_uncached(struct file *file, struct dir_context *ctx)
FUSE_READDIR);
}
locked = fuse_lock_inode(inode);
res = fuse_simple_request(fc, &ap->args);
res = fuse_simple_request(fm, &ap->args);
fuse_unlock_inode(inode, locked);
if (res >= 0) {
if (!res) {
@ -521,6 +528,12 @@ retry_locked:
page = find_get_page_flags(file->f_mapping, index,
FGP_ACCESSED | FGP_LOCK);
/* Page gone missing, then re-added to cache, but not initialized? */
if (page && !PageUptodate(page)) {
unlock_page(page);
put_page(page);
page = NULL;
}
spin_lock(&fi->rdc.lock);
if (!page) {
/*
@ -576,6 +589,26 @@ int fuse_readdir(struct file *file, struct dir_context *ctx)
struct inode *inode = file_inode(file);
int err;
#ifdef CONFIG_FUSE_BPF
struct fuse_err_ret fer;
bool allow_force;
bool force_again = false;
bool is_continued = false;
again:
fer = fuse_bpf_backing(inode, struct fuse_read_io,
fuse_readdir_initialize, fuse_readdir_backing,
fuse_readdir_finalize,
file, ctx, &force_again, &allow_force, is_continued);
if (force_again && !IS_ERR(fer.result)) {
is_continued = true;
goto again;
}
if (fer.ret)
return PTR_ERR(fer.result);
#endif
if (fuse_is_bad(inode))
return -EIO;

View file

@ -5,14 +5,25 @@
*/
#include <linux/fs.h>
#include <linux/dax.h>
#include <linux/pci.h>
#include <linux/pfn_t.h>
#include <linux/module.h>
#include <linux/virtio.h>
#include <linux/virtio_fs.h>
#include <linux/delay.h>
#include <linux/fs_context.h>
#include <linux/fs_parser.h>
#include <linux/highmem.h>
#include <linux/uio.h>
#include "fuse_i.h"
/* Used to help calculate the FUSE connection's max_pages limit for a request's
* size. Parts of the struct fuse_req are sliced into scattergather lists in
* addition to the pages used, so this can help account for that overhead.
*/
#define FUSE_HEADER_OVERHEAD 4
/* List of virtio-fs device instances and a lock for the list. Also provides
* mutual exclusion in device removal and mounting path
*/
@ -24,6 +35,8 @@ enum {
VQ_REQUEST
};
#define VQ_NAME_LEN 24
/* Per-virtqueue state */
struct virtio_fs_vq {
spinlock_t lock;
@ -35,7 +48,8 @@ struct virtio_fs_vq {
struct fuse_dev *fud;
bool connected;
long in_flight;
char name[24];
struct completion in_flight_zero; /* No inflight requests */
char name[VQ_NAME_LEN];
} ____cacheline_aligned_in_smp;
/* A virtio-fs device instance */
@ -46,13 +60,23 @@ struct virtio_fs {
struct virtio_fs_vq *vqs;
unsigned int nvqs; /* number of virtqueues */
unsigned int num_request_queues; /* number of request queues */
struct dax_device *dax_dev;
/* DAX memory window where file contents are mapped */
void *window_kaddr;
phys_addr_t window_phys_addr;
size_t window_len;
};
struct virtio_fs_forget_req {
struct fuse_in_header ih;
struct fuse_forget_in arg;
};
struct virtio_fs_forget {
struct fuse_in_header ih;
struct fuse_forget_in arg;
/* This request can be temporarily queued on virt queue */
struct list_head list;
struct virtio_fs_forget_req req;
};
struct virtio_fs_req_work {
@ -64,6 +88,48 @@ struct virtio_fs_req_work {
static int virtio_fs_enqueue_req(struct virtio_fs_vq *fsvq,
struct fuse_req *req, bool in_flight);
enum {
OPT_DAX,
};
static const struct fs_parameter_spec virtio_param_specs[] = {
fsparam_flag("dax", OPT_DAX),
{}
};
static const struct fs_parameter_description virtio_fs_parameters = {
.specs = virtio_param_specs,
};
static int virtio_fs_parse_param(struct fs_context *fsc,
struct fs_parameter *param)
{
struct fs_parse_result result;
struct fuse_fs_context *ctx = fsc->fs_private;
int opt;
opt = fs_parse(fsc, &virtio_fs_parameters, param, &result);
if (opt < 0)
return opt;
switch (opt) {
case OPT_DAX:
ctx->dax = 1;
break;
default:
return -EINVAL;
}
return 0;
}
static void virtio_fs_free_fsc(struct fs_context *fsc)
{
struct fuse_fs_context *ctx = fsc->fs_private;
kfree(ctx);
}
static inline struct virtio_fs_vq *vq_to_fsvq(struct virtqueue *vq)
{
struct virtio_fs *fs = vq->vdev->priv;
@ -87,6 +153,8 @@ static inline void dec_in_flight_req(struct virtio_fs_vq *fsvq)
{
WARN_ON(fsvq->in_flight <= 0);
fsvq->in_flight--;
if (!fsvq->in_flight)
complete(&fsvq->in_flight_zero);
}
static void release_virtio_fs_obj(struct kref *ref)
@ -117,22 +185,23 @@ static void virtio_fs_drain_queue(struct virtio_fs_vq *fsvq)
WARN_ON(fsvq->in_flight < 0);
/* Wait for in flight requests to finish.*/
while (1) {
spin_lock(&fsvq->lock);
if (!fsvq->in_flight) {
spin_unlock(&fsvq->lock);
break;
}
spin_lock(&fsvq->lock);
if (fsvq->in_flight) {
/* We are holding virtio_fs_mutex. There should not be any
* waiters waiting for completion.
*/
reinit_completion(&fsvq->in_flight_zero);
spin_unlock(&fsvq->lock);
wait_for_completion(&fsvq->in_flight_zero);
} else {
spin_unlock(&fsvq->lock);
/* TODO use completion instead of timeout */
usleep_range(1000, 2000);
}
flush_work(&fsvq->done_work);
flush_delayed_work(&fsvq->dispatch_work);
}
static void virtio_fs_drain_all_queues(struct virtio_fs *fs)
static void virtio_fs_drain_all_queues_locked(struct virtio_fs *fs)
{
struct virtio_fs_vq *fsvq;
int i;
@ -143,6 +212,19 @@ static void virtio_fs_drain_all_queues(struct virtio_fs *fs)
}
}
static void virtio_fs_drain_all_queues(struct virtio_fs *fs)
{
/* Provides mutual exclusion between ->remove and ->kill_sb
* paths. We don't want both of these draining queue at the
* same time. Current completion logic reinits completion
* and that means there should not be any other thread
* doing reinit or waiting for completion already.
*/
mutex_lock(&virtio_fs_mutex);
virtio_fs_drain_all_queues_locked(fs);
mutex_unlock(&virtio_fs_mutex);
}
static void virtio_fs_start_all_queues(struct virtio_fs *fs)
{
struct virtio_fs_vq *fsvq;
@ -278,7 +360,6 @@ static void virtio_fs_request_dispatch_work(struct work_struct *work)
struct fuse_req *req;
struct virtio_fs_vq *fsvq = container_of(work, struct virtio_fs_vq,
dispatch_work.work);
struct fuse_conn *fc = fsvq->fud->fc;
int ret;
pr_debug("virtio-fs: worker %s called.\n", __func__);
@ -293,7 +374,7 @@ static void virtio_fs_request_dispatch_work(struct work_struct *work)
list_del_init(&req->list);
spin_unlock(&fsvq->lock);
fuse_request_end(fc, req);
fuse_request_end(req);
}
/* Dispatch pending requests */
@ -324,22 +405,77 @@ static void virtio_fs_request_dispatch_work(struct work_struct *work)
spin_unlock(&fsvq->lock);
pr_err("virtio-fs: virtio_fs_enqueue_req() failed %d\n",
ret);
fuse_request_end(fc, req);
fuse_request_end(req);
}
}
}
/*
* Returns 1 if queue is full and sender should wait a bit before sending
* next request, 0 otherwise.
*/
static int send_forget_request(struct virtio_fs_vq *fsvq,
struct virtio_fs_forget *forget,
bool in_flight)
{
struct scatterlist sg;
struct virtqueue *vq;
int ret = 0;
bool notify;
struct virtio_fs_forget_req *req = &forget->req;
spin_lock(&fsvq->lock);
if (!fsvq->connected) {
if (in_flight)
dec_in_flight_req(fsvq);
kfree(forget);
goto out;
}
sg_init_one(&sg, req, sizeof(*req));
vq = fsvq->vq;
dev_dbg(&vq->vdev->dev, "%s\n", __func__);
ret = virtqueue_add_outbuf(vq, &sg, 1, forget, GFP_ATOMIC);
if (ret < 0) {
if (ret == -ENOMEM || ret == -ENOSPC) {
pr_debug("virtio-fs: Could not queue FORGET: err=%d. Will try later\n",
ret);
list_add_tail(&forget->list, &fsvq->queued_reqs);
schedule_delayed_work(&fsvq->dispatch_work,
msecs_to_jiffies(1));
if (!in_flight)
inc_in_flight_req(fsvq);
/* Queue is full */
ret = 1;
} else {
pr_debug("virtio-fs: Could not queue FORGET: err=%d. Dropping it.\n",
ret);
kfree(forget);
if (in_flight)
dec_in_flight_req(fsvq);
}
goto out;
}
if (!in_flight)
inc_in_flight_req(fsvq);
notify = virtqueue_kick_prepare(vq);
spin_unlock(&fsvq->lock);
if (notify)
virtqueue_notify(vq);
return ret;
out:
spin_unlock(&fsvq->lock);
return ret;
}
static void virtio_fs_hiprio_dispatch_work(struct work_struct *work)
{
struct virtio_fs_forget *forget;
struct virtio_fs_vq *fsvq = container_of(work, struct virtio_fs_vq,
dispatch_work.work);
struct virtqueue *vq = fsvq->vq;
struct scatterlist sg;
struct scatterlist *sgs[] = {&sg};
bool notify;
int ret;
pr_debug("virtio-fs: worker %s called.\n", __func__);
while (1) {
spin_lock(&fsvq->lock);
@ -351,43 +487,9 @@ static void virtio_fs_hiprio_dispatch_work(struct work_struct *work)
}
list_del(&forget->list);
if (!fsvq->connected) {
dec_in_flight_req(fsvq);
spin_unlock(&fsvq->lock);
kfree(forget);
continue;
}
sg_init_one(&sg, forget, sizeof(*forget));
/* Enqueue the request */
dev_dbg(&vq->vdev->dev, "%s\n", __func__);
ret = virtqueue_add_sgs(vq, sgs, 1, 0, forget, GFP_ATOMIC);
if (ret < 0) {
if (ret == -ENOMEM || ret == -ENOSPC) {
pr_debug("virtio-fs: Could not queue FORGET: err=%d. Will try later\n",
ret);
list_add_tail(&forget->list,
&fsvq->queued_reqs);
schedule_delayed_work(&fsvq->dispatch_work,
msecs_to_jiffies(1));
} else {
pr_debug("virtio-fs: Could not queue FORGET: err=%d. Dropping it.\n",
ret);
dec_in_flight_req(fsvq);
kfree(forget);
}
spin_unlock(&fsvq->lock);
return;
}
notify = virtqueue_kick_prepare(vq);
spin_unlock(&fsvq->lock);
if (notify)
virtqueue_notify(vq);
pr_debug("virtio-fs: worker %s dispatched one forget request.\n",
__func__);
if (send_forget_request(fsvq, forget, true))
return;
}
}
@ -463,7 +565,6 @@ static void virtio_fs_request_complete(struct fuse_req *req,
struct virtio_fs_vq *fsvq)
{
struct fuse_pqueue *fpq = &fsvq->fud->pq;
struct fuse_conn *fc = fsvq->fud->fc;
struct fuse_args *args;
struct fuse_args_pages *ap;
unsigned int len, i, thislen;
@ -496,7 +597,7 @@ static void virtio_fs_request_complete(struct fuse_req *req,
clear_bit(FR_SENT, &req->flags);
spin_unlock(&fpq->lock);
fuse_request_end(fc, req);
fuse_request_end(req);
spin_lock(&fsvq->lock);
dec_in_flight_req(fsvq);
spin_unlock(&fsvq->lock);
@ -564,6 +665,26 @@ static void virtio_fs_vq_done(struct virtqueue *vq)
schedule_work(&fsvq->done_work);
}
static void virtio_fs_init_vq(struct virtio_fs_vq *fsvq, char *name,
int vq_type)
{
strncpy(fsvq->name, name, VQ_NAME_LEN);
spin_lock_init(&fsvq->lock);
INIT_LIST_HEAD(&fsvq->queued_reqs);
INIT_LIST_HEAD(&fsvq->end_reqs);
init_completion(&fsvq->in_flight_zero);
if (vq_type == VQ_REQUEST) {
INIT_WORK(&fsvq->done_work, virtio_fs_requests_done_work);
INIT_DELAYED_WORK(&fsvq->dispatch_work,
virtio_fs_request_dispatch_work);
} else {
INIT_WORK(&fsvq->done_work, virtio_fs_hiprio_done_work);
INIT_DELAYED_WORK(&fsvq->dispatch_work,
virtio_fs_hiprio_dispatch_work);
}
}
/* Initialize virtqueues */
static int virtio_fs_setup_vqs(struct virtio_device *vdev,
struct virtio_fs *fs)
@ -574,12 +695,12 @@ static int virtio_fs_setup_vqs(struct virtio_device *vdev,
unsigned int i;
int ret = 0;
virtio_cread(vdev, struct virtio_fs_config, num_request_queues,
&fs->num_request_queues);
virtio_cread_le(vdev, struct virtio_fs_config, num_request_queues,
&fs->num_request_queues);
if (fs->num_request_queues == 0)
return -EINVAL;
fs->nvqs = 1 + fs->num_request_queues;
fs->nvqs = VQ_REQUEST + fs->num_request_queues;
fs->vqs = kcalloc(fs->nvqs, sizeof(fs->vqs[VQ_HIPRIO]), GFP_KERNEL);
if (!fs->vqs)
return -ENOMEM;
@ -593,27 +714,17 @@ static int virtio_fs_setup_vqs(struct virtio_device *vdev,
goto out;
}
/* Initialize the hiprio/forget request virtqueue */
callbacks[VQ_HIPRIO] = virtio_fs_vq_done;
snprintf(fs->vqs[VQ_HIPRIO].name, sizeof(fs->vqs[VQ_HIPRIO].name),
"hiprio");
virtio_fs_init_vq(&fs->vqs[VQ_HIPRIO], "hiprio", VQ_HIPRIO);
names[VQ_HIPRIO] = fs->vqs[VQ_HIPRIO].name;
INIT_WORK(&fs->vqs[VQ_HIPRIO].done_work, virtio_fs_hiprio_done_work);
INIT_LIST_HEAD(&fs->vqs[VQ_HIPRIO].queued_reqs);
INIT_LIST_HEAD(&fs->vqs[VQ_HIPRIO].end_reqs);
INIT_DELAYED_WORK(&fs->vqs[VQ_HIPRIO].dispatch_work,
virtio_fs_hiprio_dispatch_work);
spin_lock_init(&fs->vqs[VQ_HIPRIO].lock);
/* Initialize the requests virtqueues */
for (i = VQ_REQUEST; i < fs->nvqs; i++) {
spin_lock_init(&fs->vqs[i].lock);
INIT_WORK(&fs->vqs[i].done_work, virtio_fs_requests_done_work);
INIT_DELAYED_WORK(&fs->vqs[i].dispatch_work,
virtio_fs_request_dispatch_work);
INIT_LIST_HEAD(&fs->vqs[i].queued_reqs);
INIT_LIST_HEAD(&fs->vqs[i].end_reqs);
snprintf(fs->vqs[i].name, sizeof(fs->vqs[i].name),
"requests.%u", i - VQ_REQUEST);
char vq_name[VQ_NAME_LEN];
snprintf(vq_name, VQ_NAME_LEN, "requests.%u", i - VQ_REQUEST);
virtio_fs_init_vq(&fs->vqs[i], vq_name, VQ_REQUEST);
callbacks[i] = virtio_fs_vq_done;
names[i] = fs->vqs[i].name;
}
@ -642,6 +753,130 @@ static void virtio_fs_cleanup_vqs(struct virtio_device *vdev,
vdev->config->del_vqs(vdev);
}
/* Map a window offset to a page frame number. The window offset will have
* been produced by .iomap_begin(), which maps a file offset to a window
* offset.
*/
static long virtio_fs_direct_access(struct dax_device *dax_dev, pgoff_t pgoff,
long nr_pages, void **kaddr, pfn_t *pfn)
{
struct virtio_fs *fs = dax_get_private(dax_dev);
phys_addr_t offset = PFN_PHYS(pgoff);
size_t max_nr_pages = fs->window_len/PAGE_SIZE - pgoff;
if (kaddr)
*kaddr = fs->window_kaddr + offset;
if (pfn)
*pfn = phys_to_pfn_t(fs->window_phys_addr + offset,
PFN_DEV | PFN_MAP);
return nr_pages > max_nr_pages ? max_nr_pages : nr_pages;
}
static size_t virtio_fs_copy_from_iter(struct dax_device *dax_dev,
pgoff_t pgoff, void *addr,
size_t bytes, struct iov_iter *i)
{
return copy_from_iter(addr, bytes, i);
}
static size_t virtio_fs_copy_to_iter(struct dax_device *dax_dev,
pgoff_t pgoff, void *addr,
size_t bytes, struct iov_iter *i)
{
return copy_to_iter(addr, bytes, i);
}
static int virtio_fs_zero_page_range(struct dax_device *dax_dev,
pgoff_t pgoff, size_t nr_pages)
{
long rc;
void *kaddr;
rc = dax_direct_access(dax_dev, pgoff, nr_pages, &kaddr, NULL);
if (rc < 0)
return rc;
memset(kaddr, 0, nr_pages << PAGE_SHIFT);
dax_flush(dax_dev, kaddr, nr_pages << PAGE_SHIFT);
return 0;
}
static const struct dax_operations virtio_fs_dax_ops = {
.direct_access = virtio_fs_direct_access,
.copy_from_iter = virtio_fs_copy_from_iter,
.copy_to_iter = virtio_fs_copy_to_iter,
.zero_page_range = virtio_fs_zero_page_range,
};
static void virtio_fs_cleanup_dax(void *data)
{
struct dax_device *dax_dev = data;
kill_dax(dax_dev);
put_dax(dax_dev);
}
static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)
{
struct virtio_shm_region cache_reg;
struct dev_pagemap *pgmap;
bool have_cache;
if (!IS_ENABLED(CONFIG_FUSE_DAX))
return 0;
/* Get cache region */
have_cache = virtio_get_shm_region(vdev, &cache_reg,
(u8)VIRTIO_FS_SHMCAP_ID_CACHE);
if (!have_cache) {
dev_notice(&vdev->dev, "%s: No cache capability\n", __func__);
return 0;
}
if (!devm_request_mem_region(&vdev->dev, cache_reg.addr, cache_reg.len,
dev_name(&vdev->dev))) {
dev_warn(&vdev->dev, "could not reserve region addr=0x%llx len=0x%llx\n",
cache_reg.addr, cache_reg.len);
return -EBUSY;
}
dev_notice(&vdev->dev, "Cache len: 0x%llx @ 0x%llx\n", cache_reg.len,
cache_reg.addr);
pgmap = devm_kzalloc(&vdev->dev, sizeof(*pgmap), GFP_KERNEL);
if (!pgmap)
return -ENOMEM;
pgmap->type = MEMORY_DEVICE_FS_DAX;
/* Ideally we would directly use the PCI BAR resource but
* devm_memremap_pages() wants its own copy in pgmap. So
* initialize a struct resource from scratch (only the start
* and end fields will be used).
*/
pgmap->res = (struct resource){
.name = "virtio-fs dax window",
.start = (phys_addr_t) cache_reg.addr,
.end = (phys_addr_t) cache_reg.addr + cache_reg.len - 1,
};
fs->window_kaddr = devm_memremap_pages(&vdev->dev, pgmap);
if (IS_ERR(fs->window_kaddr))
return PTR_ERR(fs->window_kaddr);
fs->window_phys_addr = (phys_addr_t) cache_reg.addr;
fs->window_len = (phys_addr_t) cache_reg.len;
dev_dbg(&vdev->dev, "%s: window kaddr 0x%px phys_addr 0x%llx len 0x%llx\n",
__func__, fs->window_kaddr, cache_reg.addr, cache_reg.len);
fs->dax_dev = alloc_dax(fs, NULL, &virtio_fs_dax_ops, 0);
if (IS_ERR(fs->dax_dev))
return PTR_ERR(fs->dax_dev);
return devm_add_action_or_reset(&vdev->dev, virtio_fs_cleanup_dax,
fs->dax_dev);
}
static int virtio_fs_probe(struct virtio_device *vdev)
{
struct virtio_fs *fs;
@ -663,6 +898,10 @@ static int virtio_fs_probe(struct virtio_device *vdev)
/* TODO vq affinity */
ret = virtio_fs_setup_dax(vdev, fs);
if (ret < 0)
goto out_vqs;
/* Bring the device online in case the filesystem is mounted and
* requests need to be sent before we return.
*/
@ -706,7 +945,7 @@ static void virtio_fs_remove(struct virtio_device *vdev)
/* This device is going away. No one should get new reference */
list_del_init(&fs->list);
virtio_fs_stop_all_queues(fs);
virtio_fs_drain_all_queues(fs);
virtio_fs_drain_all_queues_locked(fs);
vdev->config->reset(vdev);
virtio_fs_cleanup_vqs(vdev, fs);
@ -731,12 +970,12 @@ static int virtio_fs_restore(struct virtio_device *vdev)
}
#endif /* CONFIG_PM_SLEEP */
const static struct virtio_device_id id_table[] = {
static const struct virtio_device_id id_table[] = {
{ VIRTIO_ID_FS, VIRTIO_DEV_ANY_ID },
{},
};
const static unsigned int feature_table[] = {};
static const unsigned int feature_table[] = {};
static struct virtio_driver virtio_fs_driver = {
.driver.name = KBUILD_MODNAME,
@ -757,14 +996,10 @@ __releases(fiq->lock)
{
struct fuse_forget_link *link;
struct virtio_fs_forget *forget;
struct scatterlist sg;
struct scatterlist *sgs[] = {&sg};
struct virtio_fs_forget_req *req;
struct virtio_fs *fs;
struct virtqueue *vq;
struct virtio_fs_vq *fsvq;
bool notify;
u64 unique;
int ret;
link = fuse_dequeue_forget(fiq, 1, NULL);
unique = fuse_get_unique(fiq);
@ -775,57 +1010,19 @@ __releases(fiq->lock)
/* Allocate a buffer for the request */
forget = kmalloc(sizeof(*forget), GFP_NOFS | __GFP_NOFAIL);
req = &forget->req;
forget->ih = (struct fuse_in_header){
req->ih = (struct fuse_in_header){
.opcode = FUSE_FORGET,
.nodeid = link->forget_one.nodeid,
.unique = unique,
.len = sizeof(*forget),
.len = sizeof(*req),
};
forget->arg = (struct fuse_forget_in){
req->arg = (struct fuse_forget_in){
.nlookup = link->forget_one.nlookup,
};
sg_init_one(&sg, forget, sizeof(*forget));
/* Enqueue the request */
spin_lock(&fsvq->lock);
if (!fsvq->connected) {
kfree(forget);
spin_unlock(&fsvq->lock);
goto out;
}
vq = fsvq->vq;
dev_dbg(&vq->vdev->dev, "%s\n", __func__);
ret = virtqueue_add_sgs(vq, sgs, 1, 0, forget, GFP_ATOMIC);
if (ret < 0) {
if (ret == -ENOMEM || ret == -ENOSPC) {
pr_debug("virtio-fs: Could not queue FORGET: err=%d. Will try later.\n",
ret);
list_add_tail(&forget->list, &fsvq->queued_reqs);
schedule_delayed_work(&fsvq->dispatch_work,
msecs_to_jiffies(1));
inc_in_flight_req(fsvq);
} else {
pr_debug("virtio-fs: Could not queue FORGET: err=%d. Dropping it.\n",
ret);
kfree(forget);
}
spin_unlock(&fsvq->lock);
goto out;
}
inc_in_flight_req(fsvq);
notify = virtqueue_kick_prepare(vq);
spin_unlock(&fsvq->lock);
if (notify)
virtqueue_notify(vq);
out:
send_forget_request(fsvq, forget, false);
kfree(link);
}
@ -842,18 +1039,37 @@ __releases(fiq->lock)
spin_unlock(&fiq->lock);
}
/* Count number of scatter-gather elements required */
static unsigned int sg_count_fuse_pages(struct fuse_page_desc *page_descs,
unsigned int num_pages,
unsigned int total_len)
{
unsigned int i;
unsigned int this_len;
for (i = 0; i < num_pages && total_len; i++) {
this_len = min(page_descs[i].length, total_len);
total_len -= this_len;
}
return i;
}
/* Return the number of scatter-gather list elements required */
static unsigned int sg_count_fuse_req(struct fuse_req *req)
{
struct fuse_args *args = req->args;
struct fuse_args_pages *ap = container_of(args, typeof(*ap), args);
unsigned int total_sgs = 1 /* fuse_in_header */;
unsigned int size, total_sgs = 1 /* fuse_in_header */;
if (args->in_numargs - args->in_pages)
total_sgs += 1;
if (args->in_pages)
total_sgs += ap->num_pages;
if (args->in_pages) {
size = args->in_args[args->in_numargs - 1].size;
total_sgs += sg_count_fuse_pages(ap->descs, ap->num_pages,
size);
}
if (!test_bit(FR_ISREPLY, &req->flags))
return total_sgs;
@ -863,8 +1079,11 @@ static unsigned int sg_count_fuse_req(struct fuse_req *req)
if (args->out_numargs - args->out_pages)
total_sgs += 1;
if (args->out_pages)
total_sgs += ap->num_pages;
if (args->out_pages) {
size = args->out_args[args->out_numargs - 1].size;
total_sgs += sg_count_fuse_pages(ap->descs, ap->num_pages,
size);
}
return total_sgs;
}
@ -1073,31 +1292,35 @@ __releases(fiq->lock)
}
}
const static struct fuse_iqueue_ops virtio_fs_fiq_ops = {
static const struct fuse_iqueue_ops virtio_fs_fiq_ops = {
.wake_forget_and_unlock = virtio_fs_wake_forget_and_unlock,
.wake_interrupt_and_unlock = virtio_fs_wake_interrupt_and_unlock,
.wake_pending_and_unlock = virtio_fs_wake_pending_and_unlock,
.release = virtio_fs_fiq_release,
};
static int virtio_fs_fill_super(struct super_block *sb)
static inline void virtio_fs_ctx_set_defaults(struct fuse_fs_context *ctx)
{
struct fuse_conn *fc = get_fuse_conn_super(sb);
ctx->rootmode = S_IFDIR;
ctx->default_permissions = 1;
ctx->allow_other = 1;
ctx->max_read = UINT_MAX;
ctx->blksize = 512;
ctx->destroy = true;
ctx->no_control = true;
ctx->no_force_umount = true;
}
static int virtio_fs_fill_super(struct super_block *sb, struct fs_context *fsc)
{
struct fuse_mount *fm = get_fuse_mount_super(sb);
struct fuse_conn *fc = fm->fc;
struct virtio_fs *fs = fc->iq.priv;
struct fuse_fs_context *ctx = fsc->fs_private;
unsigned int i;
int err;
struct fuse_fs_context ctx = {
.rootmode = S_IFDIR,
.default_permissions = 1,
.allow_other = 1,
.max_read = UINT_MAX,
.blksize = 512,
.destroy = true,
.no_control = true,
.no_force_umount = true,
.no_mount_options = true,
};
virtio_fs_ctx_set_defaults(ctx);
mutex_lock(&virtio_fs_mutex);
/* After holding mutex, make sure virtiofs device is still there.
@ -1112,7 +1335,7 @@ static int virtio_fs_fill_super(struct super_block *sb)
err = -ENOMEM;
/* Allocate fuse_dev for hiprio and notification queues */
for (i = 0; i < VQ_REQUEST; i++) {
for (i = 0; i < fs->nvqs; i++) {
struct virtio_fs_vq *fsvq = &fs->vqs[i];
fsvq->fud = fuse_dev_alloc();
@ -1120,24 +1343,30 @@ static int virtio_fs_fill_super(struct super_block *sb)
goto err_free_fuse_devs;
}
ctx.fudptr = (void **)&fs->vqs[VQ_REQUEST].fud;
err = fuse_fill_super_common(sb, &ctx);
/* virtiofs allocates and installs its own fuse devices */
ctx->fudptr = NULL;
if (ctx->dax) {
if (!fs->dax_dev) {
err = -EINVAL;
pr_err("virtio-fs: dax can't be enabled as filesystem"
" device does not support it.\n");
goto err_free_fuse_devs;
}
ctx->dax_dev = fs->dax_dev;
}
err = fuse_fill_super_common(sb, ctx);
if (err < 0)
goto err_free_fuse_devs;
fc = fs->vqs[VQ_REQUEST].fud->fc;
for (i = 0; i < fs->nvqs; i++) {
struct virtio_fs_vq *fsvq = &fs->vqs[i];
if (i == VQ_REQUEST)
continue; /* already initialized */
fuse_dev_install(fsvq->fud, fc);
}
/* Previous unmount will stop all queues. Start these again */
virtio_fs_start_all_queues(fs);
fuse_send_init(fc);
fuse_send_init(fm);
mutex_unlock(&virtio_fs_mutex);
return 0;
@ -1148,18 +1377,17 @@ err:
return err;
}
static void virtio_kill_sb(struct super_block *sb)
static void virtio_fs_conn_destroy(struct fuse_mount *fm)
{
struct fuse_conn *fc = get_fuse_conn_super(sb);
struct virtio_fs *vfs;
struct virtio_fs_vq *fsvq;
struct fuse_conn *fc = fm->fc;
struct virtio_fs *vfs = fc->iq.priv;
struct virtio_fs_vq *fsvq = &vfs->vqs[VQ_HIPRIO];
/* If mount failed, we can still be called without any fc */
if (!fc)
return fuse_kill_sb_anon(sb);
vfs = fc->iq.priv;
fsvq = &vfs->vqs[VQ_HIPRIO];
/* Stop dax worker. Soon evict_inodes() will be called which
* will free all memory ranges belonging to all inodes.
*/
if (IS_ENABLED(CONFIG_FUSE_DAX))
fuse_dax_cancel_work(fc);
/* Stop forget queue. Soon destroy will be sent */
spin_lock(&fsvq->lock);
@ -1167,9 +1395,9 @@ static void virtio_kill_sb(struct super_block *sb)
spin_unlock(&fsvq->lock);
virtio_fs_drain_all_queues(vfs);
fuse_kill_sb_anon(sb);
fuse_conn_destroy(fm);
/* fuse_kill_sb_anon() must have sent destroy. Stop all queues
/* fuse_conn_destroy() must have sent destroy. Stop all queues
* and drain one more time and free fuse devices. Freeing fuse
* devices will drop their reference on fuse_conn and that in
* turn will drop its reference on virtio_fs object.
@ -1179,12 +1407,27 @@ static void virtio_kill_sb(struct super_block *sb)
virtio_fs_free_devs(vfs);
}
static void virtio_kill_sb(struct super_block *sb)
{
struct fuse_mount *fm = get_fuse_mount_super(sb);
bool last;
/* If mount failed, we can still be called without any fc */
if (sb->s_root) {
last = fuse_mount_remove(fm);
if (last)
virtio_fs_conn_destroy(fm);
}
kill_anon_super(sb);
}
static int virtio_fs_test_super(struct super_block *sb,
struct fs_context *fsc)
{
struct fuse_conn *fc = fsc->s_fs_info;
struct fuse_mount *fsc_fm = fsc->s_fs_info;
struct fuse_mount *sb_fm = get_fuse_mount_super(sb);
return fc->iq.priv == get_fuse_conn_super(sb)->iq.priv;
return fsc_fm->fc->iq.priv == sb_fm->fc->iq.priv;
}
static int virtio_fs_set_super(struct super_block *sb,
@ -1194,7 +1437,7 @@ static int virtio_fs_set_super(struct super_block *sb,
err = get_anon_bdev(&sb->s_dev);
if (!err)
fuse_conn_get(fsc->s_fs_info);
fuse_mount_get(fsc->s_fs_info);
return err;
}
@ -1203,8 +1446,10 @@ static int virtio_fs_get_tree(struct fs_context *fsc)
{
struct virtio_fs *fs;
struct super_block *sb;
struct fuse_conn *fc;
int err;
struct fuse_conn *fc = NULL;
struct fuse_mount *fm;
unsigned int virtqueue_size;
int err = -EIO;
if (!fsc->source)
return invalf(fsc, "No source specified");
@ -1219,28 +1464,39 @@ static int virtio_fs_get_tree(struct fs_context *fsc)
return -EINVAL;
}
fc = kzalloc(sizeof(struct fuse_conn), GFP_KERNEL);
if (!fc) {
mutex_lock(&virtio_fs_mutex);
virtio_fs_put(fs);
mutex_unlock(&virtio_fs_mutex);
return -ENOMEM;
}
virtqueue_size = virtqueue_get_vring_size(fs->vqs[VQ_REQUEST].vq);
if (WARN_ON(virtqueue_size <= FUSE_HEADER_OVERHEAD))
goto out_err;
fuse_conn_init(fc, get_user_ns(current_user_ns()), &virtio_fs_fiq_ops,
fs);
err = -ENOMEM;
fc = kzalloc(sizeof(struct fuse_conn), GFP_KERNEL);
if (!fc)
goto out_err;
fm = kzalloc(sizeof(struct fuse_mount), GFP_KERNEL);
if (!fm)
goto out_err;
fuse_conn_init(fc, fm, fsc->user_ns, &virtio_fs_fiq_ops, fs);
fc->release = fuse_free_conn;
fc->delete_stale = true;
fc->auto_submounts = true;
fsc->s_fs_info = fc;
/* Tell FUSE to split requests that exceed the virtqueue's size */
fc->max_pages_limit = min_t(unsigned int, fc->max_pages_limit,
virtqueue_size - FUSE_HEADER_OVERHEAD);
fsc->s_fs_info = fm;
sb = sget_fc(fsc, virtio_fs_test_super, virtio_fs_set_super);
fuse_conn_put(fc);
fuse_mount_put(fm);
if (IS_ERR(sb))
return PTR_ERR(sb);
if (!sb->s_root) {
err = virtio_fs_fill_super(sb);
err = virtio_fs_fill_super(sb, fsc);
if (err) {
fuse_mount_put(fm);
sb->s_fs_info = NULL;
deactivate_locked_super(sb);
return err;
}
@ -1251,14 +1507,29 @@ static int virtio_fs_get_tree(struct fs_context *fsc)
WARN_ON(fsc->root);
fsc->root = dget(sb->s_root);
return 0;
out_err:
kfree(fc);
mutex_lock(&virtio_fs_mutex);
virtio_fs_put(fs);
mutex_unlock(&virtio_fs_mutex);
return err;
}
static const struct fs_context_operations virtio_fs_context_ops = {
.free = virtio_fs_free_fsc,
.parse_param = virtio_fs_parse_param,
.get_tree = virtio_fs_get_tree,
};
static int virtio_fs_init_fs_context(struct fs_context *fsc)
{
struct fuse_fs_context *ctx;
ctx = kzalloc(sizeof(struct fuse_fs_context), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
fsc->fs_private = ctx;
fsc->ops = &virtio_fs_context_ops;
return 0;
}

View file

@ -14,12 +14,12 @@
int fuse_setxattr(struct inode *inode, const char *name, const void *value,
size_t size, int flags)
{
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_mount *fm = get_fuse_mount(inode);
FUSE_ARGS(args);
struct fuse_setxattr_in inarg;
int err;
if (fc->no_setxattr)
if (fm->fc->no_setxattr)
return -EOPNOTSUPP;
memset(&inarg, 0, sizeof(inarg));
@ -34,9 +34,9 @@ int fuse_setxattr(struct inode *inode, const char *name, const void *value,
args.in_args[1].value = name;
args.in_args[2].size = size;
args.in_args[2].value = value;
err = fuse_simple_request(fc, &args);
err = fuse_simple_request(fm, &args);
if (err == -ENOSYS) {
fc->no_setxattr = 1;
fm->fc->no_setxattr = 1;
err = -EOPNOTSUPP;
}
if (!err) {
@ -49,13 +49,13 @@ int fuse_setxattr(struct inode *inode, const char *name, const void *value,
ssize_t fuse_getxattr(struct inode *inode, const char *name, void *value,
size_t size)
{
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_mount *fm = get_fuse_mount(inode);
FUSE_ARGS(args);
struct fuse_getxattr_in inarg;
struct fuse_getxattr_out outarg;
ssize_t ret;
if (fc->no_getxattr)
if (fm->fc->no_getxattr)
return -EOPNOTSUPP;
memset(&inarg, 0, sizeof(inarg));
@ -77,11 +77,11 @@ ssize_t fuse_getxattr(struct inode *inode, const char *name, void *value,
args.out_args[0].size = sizeof(outarg);
args.out_args[0].value = &outarg;
}
ret = fuse_simple_request(fc, &args);
ret = fuse_simple_request(fm, &args);
if (!ret && !size)
ret = min_t(size_t, outarg.size, XATTR_SIZE_MAX);
if (ret == -ENOSYS) {
fc->no_getxattr = 1;
fm->fc->no_getxattr = 1;
ret = -EOPNOTSUPP;
}
return ret;
@ -107,19 +107,30 @@ static int fuse_verify_xattr_list(char *list, size_t size)
ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
{
struct inode *inode = d_inode(entry);
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_mount *fm = get_fuse_mount(inode);
FUSE_ARGS(args);
struct fuse_getxattr_in inarg;
struct fuse_getxattr_out outarg;
ssize_t ret;
#ifdef CONFIG_FUSE_BPF
struct fuse_err_ret fer;
fer = fuse_bpf_backing(inode, struct fuse_getxattr_io,
fuse_listxattr_initialize,
fuse_listxattr_backing, fuse_listxattr_finalize,
entry, list, size);
if (fer.ret)
return PTR_ERR(fer.result);
#endif
if (fuse_is_bad(inode))
return -EIO;
if (!fuse_allow_current_process(fc))
if (!fuse_allow_current_process(fm->fc))
return -EACCES;
if (fc->no_listxattr)
if (fm->fc->no_listxattr)
return -EOPNOTSUPP;
memset(&inarg, 0, sizeof(inarg));
@ -139,13 +150,13 @@ ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
args.out_args[0].size = sizeof(outarg);
args.out_args[0].value = &outarg;
}
ret = fuse_simple_request(fc, &args);
ret = fuse_simple_request(fm, &args);
if (!ret && !size)
ret = min_t(size_t, outarg.size, XATTR_LIST_MAX);
if (ret > 0 && size)
ret = fuse_verify_xattr_list(list, ret);
if (ret == -ENOSYS) {
fc->no_listxattr = 1;
fm->fc->no_listxattr = 1;
ret = -EOPNOTSUPP;
}
return ret;
@ -153,11 +164,11 @@ ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
int fuse_removexattr(struct inode *inode, const char *name)
{
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_mount *fm = get_fuse_mount(inode);
FUSE_ARGS(args);
int err;
if (fc->no_removexattr)
if (fm->fc->no_removexattr)
return -EOPNOTSUPP;
args.opcode = FUSE_REMOVEXATTR;
@ -165,9 +176,9 @@ int fuse_removexattr(struct inode *inode, const char *name)
args.in_numargs = 1;
args.in_args[0].size = strlen(name) + 1;
args.in_args[0].value = name;
err = fuse_simple_request(fc, &args);
err = fuse_simple_request(fm, &args);
if (err == -ENOSYS) {
fc->no_removexattr = 1;
fm->fc->no_removexattr = 1;
err = -EOPNOTSUPP;
}
if (!err) {
@ -181,6 +192,17 @@ static int fuse_xattr_get(const struct xattr_handler *handler,
struct dentry *dentry, struct inode *inode,
const char *name, void *value, size_t size, int flags)
{
#ifdef CONFIG_FUSE_BPF
struct fuse_err_ret fer;
fer = fuse_bpf_backing(inode, struct fuse_getxattr_io,
fuse_getxattr_initialize, fuse_getxattr_backing,
fuse_getxattr_finalize,
dentry, name, value, size);
if (fer.ret)
return PTR_ERR(fer.result);
#endif
if (fuse_is_bad(inode))
return -EIO;
@ -192,6 +214,24 @@ static int fuse_xattr_set(const struct xattr_handler *handler,
const char *name, const void *value, size_t size,
int flags)
{
#ifdef CONFIG_FUSE_BPF
struct fuse_err_ret fer;
if (value)
fer = fuse_bpf_backing(inode, struct fuse_setxattr_in,
fuse_setxattr_initialize, fuse_setxattr_backing,
fuse_setxattr_finalize, dentry, name, value,
size, flags);
else
fer = fuse_bpf_backing(inode, struct fuse_dummy_io,
fuse_removexattr_initialize,
fuse_removexattr_backing,
fuse_removexattr_finalize,
dentry, name);
if (fer.ret)
return PTR_ERR(fer.result);
#endif
if (fuse_is_bad(inode))
return -EIO;

View file

@ -2618,6 +2618,7 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags,
if (IS_ERR(fc))
return PTR_ERR(fc);
fc->oldapi = true;
err = parse_monolithic_mount_data(fc, data);
if (!err) {
down_write(&sb->s_umount);

View file

@ -310,7 +310,7 @@ nfs_find_actor(struct inode *inode, void *opaque)
if (NFS_FILEID(inode) != fattr->fileid)
return 0;
if ((S_IFMT & inode->i_mode) != (S_IFMT & fattr->mode))
if (inode_wrong_type(inode, fattr->mode))
return 0;
if (nfs_compare_fh(NFS_FH(inode), fh))
return 0;
@ -1418,7 +1418,7 @@ static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fat
return 0;
return -ESTALE;
}
if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && inode_wrong_type(inode, fattr->mode))
return -ESTALE;
@ -1834,7 +1834,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
/*
* Make sure the inode's type hasn't changed.
*/
if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) {
if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && inode_wrong_type(inode, fattr->mode)) {
/*
* Big trouble! The inode has become a different object.
*/

View file

@ -376,7 +376,7 @@ nfsd_proc_create(struct svc_rqst *rqstp)
/* Make sure the type and device matches */
nfserr = nfserr_exist;
if (inode && type != (inode->i_mode & S_IFMT))
if (inode && inode_wrong_type(inode, type))
goto out_unlock;
}

View file

@ -523,7 +523,7 @@ static const struct file_operations fanotify_fops = {
.fasync = NULL,
.release = fanotify_release,
.unlocked_ioctl = fanotify_ioctl,
.compat_ioctl = fanotify_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.llseek = noop_llseek,
};

View file

@ -345,7 +345,7 @@ int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected,
return PTR_ERR(origin);
if (upperdentry && !ovl_is_whiteout(upperdentry) &&
((d_inode(origin)->i_mode ^ d_inode(upperdentry)->i_mode) & S_IFMT))
inode_wrong_type(d_inode(upperdentry), d_inode(origin)->i_mode))
goto invalid;
if (!*stackp)
@ -717,7 +717,7 @@ struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper,
index = ERR_PTR(-ESTALE);
goto out;
} else if (ovl_dentry_weird(index) || ovl_is_whiteout(index) ||
((inode->i_mode ^ d_inode(origin)->i_mode) & S_IFMT)) {
inode_wrong_type(inode, d_inode(origin)->i_mode)) {
/*
* Index should always be of the same file type as origin
* except for the case of a whiteout index. A whiteout

View file

@ -939,6 +939,34 @@ out:
return ret;
}
ssize_t vfs_iocb_iter_read(struct file *file, struct kiocb *iocb,
struct iov_iter *iter)
{
size_t tot_len;
ssize_t ret = 0;
if (!file->f_op->read_iter)
return -EINVAL;
if (!(file->f_mode & FMODE_READ))
return -EBADF;
if (!(file->f_mode & FMODE_CAN_READ))
return -EINVAL;
tot_len = iov_iter_count(iter);
if (!tot_len)
goto out;
ret = rw_verify_area(READ, file, &iocb->ki_pos, tot_len);
if (ret < 0)
return ret;
ret = call_read_iter(file, iocb, iter);
out:
if (ret >= 0)
fsnotify_access(file);
return ret;
}
EXPORT_SYMBOL(vfs_iocb_iter_read);
ssize_t vfs_iter_read(struct file *file, struct iov_iter *iter, loff_t *ppos,
rwf_t flags)
{
@ -975,6 +1003,34 @@ static ssize_t do_iter_write(struct file *file, struct iov_iter *iter,
return ret;
}
ssize_t vfs_iocb_iter_write(struct file *file, struct kiocb *iocb,
struct iov_iter *iter)
{
size_t tot_len;
ssize_t ret = 0;
if (!file->f_op->write_iter)
return -EINVAL;
if (!(file->f_mode & FMODE_WRITE))
return -EBADF;
if (!(file->f_mode & FMODE_CAN_WRITE))
return -EINVAL;
tot_len = iov_iter_count(iter);
if (!tot_len)
return 0;
ret = rw_verify_area(WRITE, file, &iocb->ki_pos, tot_len);
if (ret < 0)
return ret;
ret = call_write_iter(file, iocb, iter);
if (ret > 0)
fsnotify_modify(file);
return ret;
}
EXPORT_SYMBOL(vfs_iocb_iter_write);
ssize_t vfs_iter_write(struct file *file, struct iov_iter *iter, loff_t *ppos,
rwf_t flags)
{

View file

@ -2189,6 +2189,8 @@ static int ubifs_fill_super(struct super_block *sb, void *data, int silent)
c->vi.vol_id);
if (err)
goto out_close;
sb->s_bdi->ra_pages = 0;
sb->s_bdi->io_pages = 0;
sb->s_fs_info = c;
sb->s_magic = UBIFS_SUPER_MAGIC;

View file

@ -2017,7 +2017,7 @@ static const struct file_operations userfaultfd_fops = {
.poll = userfaultfd_poll,
.read = userfaultfd_read,
.unlocked_ioctl = userfaultfd_ioctl,
.compat_ioctl = userfaultfd_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.llseek = noop_llseek,
};

View file

@ -77,6 +77,9 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_LSM, lsm,
void *, void *)
#endif /* CONFIG_BPF_LSM */
#endif
#ifdef CONFIG_FUSE_BPF
BPF_PROG_TYPE(BPF_PROG_TYPE_FUSE, fuse, struct fuse_bpf_args, struct fuse_bpf_args)
#endif
BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops)
BPF_MAP_TYPE(BPF_MAP_TYPE_PERCPU_ARRAY, percpu_array_map_ops)

View file

@ -34,6 +34,8 @@ struct dax_operations {
/* copy_to_iter: required operation for fs-dax direct-i/o */
size_t (*copy_to_iter)(struct dax_device *, pgoff_t, void *, size_t,
struct iov_iter *);
/* zero_page_range: required operation. Zero page range */
int (*zero_page_range)(struct dax_device *, pgoff_t, size_t);
};
extern struct attribute_group dax_attribute_group;
@ -226,6 +228,8 @@ size_t dax_copy_from_iter(struct dax_device *dax_dev, pgoff_t pgoff, void *addr,
size_t bytes, struct iov_iter *i);
size_t dax_copy_to_iter(struct dax_device *dax_dev, pgoff_t pgoff, void *addr,
size_t bytes, struct iov_iter *i);
int dax_zero_page_range(struct dax_device *dax_dev, pgoff_t pgoff,
size_t nr_pages);
void dax_flush(struct dax_device *dax_dev, void *addr, size_t size);
ssize_t dax_iomap_rw(struct kiocb *iocb, struct iov_iter *iter,

View file

@ -2167,6 +2167,18 @@ static inline void init_sync_kiocb(struct kiocb *kiocb, struct file *filp)
};
}
static inline void kiocb_clone(struct kiocb *kiocb, struct kiocb *kiocb_src,
struct file *filp)
{
*kiocb = (struct kiocb) {
.ki_filp = filp,
.ki_flags = kiocb_src->ki_flags,
.ki_hint = kiocb_src->ki_hint,
.ki_ioprio = kiocb_src->ki_ioprio,
.ki_pos = kiocb_src->ki_pos,
};
}
/*
* Inode state bits. Protected by inode->i_lock
*
@ -2985,6 +2997,11 @@ static inline bool execute_ok(struct inode *inode)
return (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode);
}
static inline bool inode_wrong_type(const struct inode *inode, umode_t mode)
{
return (inode->i_mode ^ mode) & S_IFMT;
}
static inline void file_start_write(struct file *file)
{
if (!S_ISREG(file_inode(file)->i_mode))
@ -3215,6 +3232,10 @@ ssize_t vfs_iter_read(struct file *file, struct iov_iter *iter, loff_t *ppos,
rwf_t flags);
ssize_t vfs_iter_write(struct file *file, struct iov_iter *iter, loff_t *ppos,
rwf_t flags);
ssize_t vfs_iocb_iter_read(struct file *file, struct kiocb *iocb,
struct iov_iter *iter);
ssize_t vfs_iocb_iter_write(struct file *file, struct kiocb *iocb,
struct iov_iter *iter);
/* fs/block_dev.c */
extern ssize_t blkdev_read_iter(struct kiocb *iocb, struct iov_iter *to);

View file

@ -105,6 +105,7 @@ struct fs_context {
enum fs_context_phase phase:8; /* The phase the context is in */
bool need_free:1; /* Need to call ops->free() */
bool global:1; /* Goes into &init_user_ns */
bool oldapi:1; /* Coming from mount(2) */
};
struct fs_context_operations {

View file

@ -10,6 +10,11 @@
struct irq_affinity;
struct virtio_shm_region {
u64 addr;
u64 len;
};
/**
* virtio_config_ops - operations for configuring a virtio device
* Note: Do not assume that a transport implements all of the operations
@ -66,6 +71,7 @@ struct irq_affinity;
* the caller can then copy.
* @set_vq_affinity: set the affinity for a virtqueue (optional).
* @get_vq_affinity: get the affinity for a virtqueue (optional).
* @get_shm_region: get a shared memory region based on the index.
*/
typedef void vq_callback_t(struct virtqueue *);
struct virtio_config_ops {
@ -89,6 +95,8 @@ struct virtio_config_ops {
const struct cpumask *cpu_mask);
const struct cpumask *(*get_vq_affinity)(struct virtio_device *vdev,
int index);
bool (*get_shm_region)(struct virtio_device *vdev,
struct virtio_shm_region *region, u8 id);
};
/* If driver didn't advertise the feature, it will never appear. */
@ -251,6 +259,15 @@ int virtqueue_set_affinity(struct virtqueue *vq, const struct cpumask *cpu_mask)
return 0;
}
static inline
bool virtio_get_shm_region(struct virtio_device *vdev,
struct virtio_shm_region *region, u8 id)
{
if (!vdev->config->get_shm_region)
return false;
return vdev->config->get_shm_region(vdev, region, id);
}
static inline bool virtio_is_little_endian(struct virtio_device *vdev)
{
return virtio_has_feature(vdev, VIRTIO_F_VERSION_1) ||

View file

@ -199,6 +199,7 @@ enum bpf_prog_type {
BPF_PROG_TYPE_EXT,
BPF_PROG_TYPE_LSM,
BPF_PROG_TYPE_SK_LOOKUP,
BPF_PROG_TYPE_FUSE,
};
enum bpf_attach_type {

View file

@ -172,6 +172,9 @@
* - add FUSE_WRITE_KILL_PRIV flag
* - add FUSE_SETUPMAPPING and FUSE_REMOVEMAPPING
* - add map_alignment to fuse_init_out, add FUSE_MAP_ALIGNMENT flag
*
* 7.32
* - add flags to fuse_attr, add FUSE_ATTR_SUBMOUNT, add FUSE_SUBMOUNTS
*/
#ifndef _LINUX_FUSE_H
@ -207,7 +210,7 @@
#define FUSE_KERNEL_VERSION 7
/** Minor version number of this interface */
#define FUSE_KERNEL_MINOR_VERSION 31
#define FUSE_KERNEL_MINOR_VERSION 32
/** The node ID of the root inode */
#define FUSE_ROOT_ID 1
@ -231,7 +234,7 @@ struct fuse_attr {
uint32_t gid;
uint32_t rdev;
uint32_t blksize;
uint32_t padding;
uint32_t flags;
};
struct fuse_kstatfs {
@ -313,7 +316,10 @@ struct fuse_file_lock {
* FUSE_CACHE_SYMLINKS: cache READLINK responses
* FUSE_NO_OPENDIR_SUPPORT: kernel supports zero-message opendir
* FUSE_EXPLICIT_INVAL_DATA: only invalidate cached pages on explicit request
* FUSE_MAP_ALIGNMENT: map_alignment field is valid
* FUSE_MAP_ALIGNMENT: init_out.map_alignment contains log2(byte alignment) for
* foffset and moffset fields in struct
* fuse_setupmapping_out and fuse_removemapping_one.
* FUSE_SUBMOUNTS: kernel supports auto-mounting directory submounts
*/
#define FUSE_ASYNC_READ (1 << 0)
#define FUSE_POSIX_LOCKS (1 << 1)
@ -342,6 +348,7 @@ struct fuse_file_lock {
#define FUSE_NO_OPENDIR_SUPPORT (1 << 24)
#define FUSE_EXPLICIT_INVAL_DATA (1 << 25)
#define FUSE_MAP_ALIGNMENT (1 << 26)
#define FUSE_SUBMOUNTS (1 << 27)
#define FUSE_PASSTHROUGH (1 << 31)
/**
@ -418,6 +425,13 @@ struct fuse_file_lock {
*/
#define FUSE_FSYNC_FDATASYNC (1 << 0)
/**
* fuse_attr flags
*
* FUSE_ATTR_SUBMOUNT: Object is a submount root
*/
#define FUSE_ATTR_SUBMOUNT (1 << 0)
enum fuse_opcode {
FUSE_LOOKUP = 1,
FUSE_FORGET = 2, /* no reply */
@ -502,6 +516,23 @@ struct fuse_entry_out {
struct fuse_attr attr;
};
#define FUSE_ACTION_KEEP 0
#define FUSE_ACTION_REMOVE 1
#define FUSE_ACTION_REPLACE 2
struct fuse_entry_bpf_out {
uint64_t backing_action;
uint64_t backing_fd;
uint64_t bpf_action;
uint64_t bpf_fd;
};
struct fuse_entry_bpf {
struct fuse_entry_bpf_out out;
struct file *backing_file;
struct file *bpf_file;
};
struct fuse_forget_in {
uint64_t nlookup;
};
@ -620,6 +651,12 @@ struct fuse_read_in {
uint32_t padding;
};
struct fuse_read_out {
uint64_t offset;
uint32_t again;
uint32_t padding;
};
#define FUSE_COMPAT_WRITE_IN_SIZE 24
struct fuse_write_in {
@ -793,7 +830,7 @@ struct fuse_in_header {
uint32_t uid;
uint32_t gid;
uint32_t pid;
uint32_t padding;
uint32_t error_in;
};
struct fuse_out_header {
@ -898,4 +935,82 @@ struct fuse_copy_file_range_in {
uint64_t flags;
};
#define FUSE_SETUPMAPPING_FLAG_WRITE (1ull << 0)
#define FUSE_SETUPMAPPING_FLAG_READ (1ull << 1)
struct fuse_setupmapping_in {
/* An already open handle */
uint64_t fh;
/* Offset into the file to start the mapping */
uint64_t foffset;
/* Length of mapping required */
uint64_t len;
/* Flags, FUSE_SETUPMAPPING_FLAG_* */
uint64_t flags;
/* Offset in Memory Window */
uint64_t moffset;
};
struct fuse_removemapping_in {
/* number of fuse_removemapping_one follows */
uint32_t count;
};
struct fuse_removemapping_one {
/* Offset into the dax window start the unmapping */
uint64_t moffset;
/* Length of mapping required */
uint64_t len;
};
#define FUSE_REMOVEMAPPING_MAX_ENTRY \
(PAGE_SIZE / sizeof(struct fuse_removemapping_one))
/*
* Fuse BPF Args
*
* Used to communicate with bpf programs to allow checking or altering certain values.
* The end_offset allows the bpf verifier to check boundaries statically. This reflects
* the ends of the buffer. size shows the length that was actually used.
*
*/
/** One input argument of a request */
struct fuse_bpf_in_arg {
uint32_t size;
const void *value;
const void *end_offset;
};
/** One output argument of a request */
struct fuse_bpf_arg {
uint32_t size;
void *value;
void *end_offset;
};
#define FUSE_MAX_IN_ARGS 5
#define FUSE_MAX_OUT_ARGS 3
#define FUSE_BPF_FORCE (1 << 0)
#define FUSE_BPF_OUT_ARGVAR (1 << 6)
struct fuse_bpf_args {
uint64_t nodeid;
uint32_t opcode;
uint32_t error_in;
uint32_t in_numargs;
uint32_t out_numargs;
uint32_t flags;
struct fuse_bpf_in_arg in_args[FUSE_MAX_IN_ARGS];
struct fuse_bpf_arg out_args[FUSE_MAX_OUT_ARGS];
};
#define FUSE_BPF_USER_FILTER 1
#define FUSE_BPF_BACKING 2
#define FUSE_BPF_POST_FILTER 4
#define FUSE_OPCODE_FILTER 0x0ffff
#define FUSE_PREFILTER 0x10000
#define FUSE_POSTFILTER 0x20000
#endif /* _LINUX_FUSE_H */

View file

@ -16,4 +16,7 @@ struct virtio_fs_config {
__u32 num_request_queues;
} __attribute__((packed));
/* For the id field in virtio_pci_shm_cap */
#define VIRTIO_FS_SHMCAP_ID_CACHE 0
#endif /* _UAPI_LINUX_VIRTIO_FS_H */

View file

@ -36,3 +36,6 @@ obj-$(CONFIG_BPF_SYSCALL) += bpf_struct_ops.o
obj-${CONFIG_BPF_LSM} += bpf_lsm.o
endif
obj-$(CONFIG_BPF_PRELOAD) += preload/
ifeq ($(CONFIG_FUSE_BPF),y)
obj-$(CONFIG_BPF_SYSCALL) += bpf_fuse.o
endif

72
kernel/bpf/bpf_fuse.c Normal file
View file

@ -0,0 +1,72 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2021 Google LLC
#include <linux/filter.h>
#include <linux/fuse.h>
static const struct bpf_func_proto *
fuse_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
{
switch (func_id) {
case BPF_FUNC_trace_printk:
return bpf_get_trace_printk_proto();
case BPF_FUNC_get_current_uid_gid:
return &bpf_get_current_uid_gid_proto;
case BPF_FUNC_get_current_pid_tgid:
return &bpf_get_current_pid_tgid_proto;
case BPF_FUNC_map_lookup_elem:
return &bpf_map_lookup_elem_proto;
case BPF_FUNC_map_update_elem:
return &bpf_map_update_elem_proto;
default:
pr_debug("Invalid fuse bpf func %d\n", func_id);
return NULL;
}
}
static bool fuse_prog_is_valid_access(int off, int size,
enum bpf_access_type type,
const struct bpf_prog *prog,
struct bpf_insn_access_aux *info)
{
int i;
if (off < 0 || off > offsetofend(struct fuse_bpf_args, out_args))
return false;
/* TODO This is garbage. Do it properly */
for (i = 0; i < 5; i++) {
if (off == offsetof(struct fuse_bpf_args, in_args[i].value)) {
info->reg_type = PTR_TO_RDONLY_BUF;
info->ctx_field_size = 256;
if (type != BPF_READ)
return false;
return true;
}
}
for (i = 0; i < 3; i++) {
if (off == offsetof(struct fuse_bpf_args, out_args[i].value)) {
info->reg_type = PTR_TO_RDWR_BUF;
info->ctx_field_size = 256;
return true;
}
}
if (type != BPF_READ)
return false;
return true;
}
const struct bpf_verifier_ops fuse_verifier_ops = {
.get_func_proto = fuse_prog_func_proto,
.is_valid_access = fuse_prog_is_valid_access,
};
const struct bpf_prog_ops fuse_prog_ops = {
};

View file

@ -3,6 +3,7 @@
#include <uapi/linux/btf.h>
#include <uapi/linux/bpf.h>
#include <uapi/linux/fuse.h>
#include <uapi/linux/bpf_perf_event.h>
#include <uapi/linux/types.h>
#include <linux/seq_file.h>

View file

@ -2035,7 +2035,7 @@ static bool seccomp_actions_logged_from_names(u32 *actions_logged, char *names)
return true;
}
static int read_actions_logged(struct ctl_table *ro_table, void __user *buffer,
static int read_actions_logged(struct ctl_table *ro_table, void *buffer,
size_t *lenp, loff_t *ppos)
{
char names[sizeof(seccomp_actions_avail)];
@ -2053,7 +2053,7 @@ static int read_actions_logged(struct ctl_table *ro_table, void __user *buffer,
return proc_dostring(&table, 0, buffer, lenp, ppos);
}
static int write_actions_logged(struct ctl_table *ro_table, void __user *buffer,
static int write_actions_logged(struct ctl_table *ro_table, void *buffer,
size_t *lenp, loff_t *ppos, u32 *actions_logged)
{
char names[sizeof(seccomp_actions_avail)];

View file

@ -878,6 +878,8 @@ struct backing_dev_info *bdi_alloc_node(gfp_t gfp_mask, int node_id)
kfree(bdi);
return NULL;
}
bdi->ra_pages = VM_READAHEAD_PAGES;
bdi->io_pages = VM_READAHEAD_PAGES;
return bdi;
}
EXPORT_SYMBOL(bdi_alloc_node);

View file

@ -1317,7 +1317,7 @@ static const struct file_operations rfkill_fops = {
.release = rfkill_fop_release,
#ifdef CONFIG_RFKILL_INPUT
.unlocked_ioctl = rfkill_fop_ioctl,
.compat_ioctl = rfkill_fop_ioctl,
.compat_ioctl = compat_ptr_ioctl,
#endif
.llseek = no_llseek,
};