virtio-fs: shared file system for virtual machines
Subsystems (9)
Vivek Goyal
maintainercover virtio-fs: shared file system for virtual machines
Hi,
Here are RFC patches for virtio-fs. Looking for feedback on this approach.
These patches should apply on top of 4.20-rc5. We have also put code for various components here.
Problem Description
===================
We want to be able to take a directory tree on the host and share it with guest[s]. Our goal is to be able to do it in a fast, consistent and secure manner. Our primary use case is kata containers, but it should be usable in other scenarios as well.
Containers may rely on local file system semantics for shared volumes, read-write mounts that multiple containers access simultaneously. File system changes must be visible to other containers with the same consistency expected of a local file system, including mmap MAP_SHARED.
Existing Solutions
==================
We looked at existing solutions and virtio-9p already provides basic shared file system functionality although does not offer local file system semantics, causing some workloads and test suites to fail. In addition, virtio-9p performance has been an issue for Kata Containers and we believe this cannot be alleviated without major changes that do not fit into the 9P protocol.
Design Overview
===============
With the goal of designing something with better performance and local file system semantics, a bunch of ideas were proposed.
- Use fuse protocol (instead of 9p) for communication between guest and host. Guest kernel will be fuse client and a fuse server will run on host to serve the requests. Benchmark results (see below) are encouraging and show this approach performs well (2x to 8x improvement depending on test being run).
- For data access inside guest, mmap portion of file in QEMU address space and guest accesses this memory using dax. That way guest page cache is bypassed and there is only one copy of data (on host). This will also enable mmap(MAP_SHARED) between guests.
- For metadata coherency, there is a shared memory region which contains version number associated with metadata and any guest changing metadata updates version number and other guests refresh metadata on next access. This is still experimental and implementation is not complete.
How virtio-fs differs from existing approaches
==============================================
The unique idea behind virtio-fs is to take advantage of the co-location of the virtual machine and hypervisor to avoid communication (vmexits).
DAX allows file contents to be accessed without communication with the hypervisor. The shared memory region for metadata avoids communication in the common case where metadata is unchanged.
By replacing expensive communication with cheaper shared memory accesses, we expect to achieve better performance than approaches based on network file system protocols. In addition, this also makes it easier to achieve local file system semantics (coherency).
These techniques are not applicable to network file system protocols since the communications channel is bypassed by taking advantage of shared memory on a local machine. This is why we decided to build virtio-fs rather than focus on 9P or NFS.
HOWTO
======
We have put instructions on how to use it here.
Caching Modes
=============
Like virtio-9p, different caching modes are supported which determine the coherency level as well. The “cache=FOO” and “writeback” options control the level of coherence between the guest and host filesystems. The “shared” option only has an effect on coherence between virtio-fs filesystem instances running inside different guests.
- cache=none
metadata, data and pathname lookup are not cached in guest. They are always fetched from host and any changes are immediately pushed to host.
- cache=always
metadata, data and pathname lookup are cached in guest and never expire.
- cache=auto
metadata and pathname lookup cache expires after a configured amount of time (default is 1 second). Data is cached while the file is open (close to open consistency).
- writeback/no_writeback
These options control the writeback strategy. If writeback is disabled, then normal writes will immediately be synchronized with the host fs. If writeback is enabled, then writes may be cached in the guest until the file is closed or an fsync(2) performed. This option has no effect on mmap-ed writes or writes going through the DAX mechanism.
- shared/no_shared
These options control the use of the shared version table. If shared mode is enabled then metadata and pathname lookup is cached in guest, but is refreshed due to changes in another virtio-fs instance.
DAX
===
- dax can be turned on/off when mounting virtio-fs inside guest.
WHAT WORKS
==========
- As of now primarily cache options none, auto and always are working. shared option is still being worked on.
- Dax on/off seems to work. It does not seem to be as fast as we were expecting it to be. Still need to look into optimization opportunities.
TODO
====
- Complete "cache=shared" implementation.
- Look into improving performance for dax. It seems slow.
- Lot of bug fixing, cleanup and performance improvement.
RESULTS
=======
- pjdfstests are passing. Have tried cache=none/auto/always and dax on/off).
https://github.com/pjd/pjdfstest
(one symlink test fails and that seems to be due xfs on host. Yet to
look into it).
- We have run some basic tests and compared with virtio-9p and it seems to be faster. I ran "smallfile" utility and a simple fio job to test mmap performance.
Test Setup
-----------
- A fedora 28 host with 32G RAM, 2 sockets (6 cores per socket, 2 threads per core)
- Using a PCIE SSD at host as backing store.
- Created a VM with 16 VCPUS and 6GB memory. A 2GB cache window (for dax mmap).
fio mmap
--------
Wrote simple fio job to run mmap and READ. Ran test on 1 file and 4 files and different caching modes. File size is 4G. Dropped cache in guest before each run. Cache on host was untouched. So data on host must have been cached. These results are average of 3 runs.
cache mode 1-file(one thread) 4-files(4 threads)
virtio-9p mmap 28 MB/s 140 MB/s
virtio-fs none + dax 126 MB/s 501 MB/s
virtio-9p loose 31 MB/s 135 MB/s
virtio-fs always 235 MB/s 858 MB/s
virtio-fs always + dax 121 MB/s 487 MB/s
smallfile
---------
https://github.com/distributed-system-analysis/smallfile
I basically ran bunch of operations like create, ls-l, read, append, rename and delete-renamed and measured performance over 3 runs and took average. Dropped cache after before each operation started running. Used effectively following command for each operation.
# python smallfile_cli.py --operation create --threads 8 --file-size 1024 --files 2048 --top <test-dir>
cache mode operation (files/sec)
virtio-9p none create 194
virtio-fs none create 714
virtio-9p mmap create 201
virtio-fs none + dax create 759
virtio-9p loose create 16
virtio-fs always create 685
virtio-fs always + dax create 735
virtio-9p none ls-l 2038
virtio-fs none ls-l 4615
virtio-9p mmap ls-l 2087
virtio-fs none + dax ls-l 4616
virtio-9p loose ls-l 1619
virtio-fs always ls-l 13571
virtio-fs always + dax ls-l 12626
virtio-9p none read 199
virtio-fs none read 1405
virtio-9p mmap read 203
virtio-fs none + dax read 1345
virtio-9p loose read 207
virtio-fs always read 1436
virtio-fs always + dax read 1368
virtio-9p none append 197
virtio-fs none append 717
virtio-9p mmap append 200
virtio-fs none + dax append 645
virtio-9p loose append 16
virtio-fs always append 651
virtio-fs always + dax append 704
virtio-9p none rename 2442
virtio-fs none rename 5797
virtio-9p mmap rename 2518
virtio-fs none + dax rename 6386
virtio-9p loose rename 4178
virtio-fs always rename 15834
virtio-fs always + dax rename 15529
Thanks
Vivek
Dr. David Alan Gilbert (5):
virtio-fs: Add VIRTIO_PCI_CAP_SHARED_MEMORY_CFG and utility to find
them
virito-fs: Make dax optional
virtio: Free fuse devices on umount
virtio-fs: Retrieve shm capabilities for version table
virtio-fs: Map using the values from the capabilities
Miklos Szeredi (8):
fuse: simplify fuse_fill_super_common() calling
fuse: delete dentry if timeout is zero
fuse: multiplex cached/direct_io/dax file operations
virtio-fs: pass version table pointer to fuse
fuse: don't crash if version table is NULL
fuse: add shared version support (virtio-fs only)
fuse: shared version cleanups
fuse: fix fuse_permission() for the default_permissions case
Stefan Hajnoczi (17):
fuse: add skeleton virtio_fs.ko module
fuse: add probe/remove virtio driver
fuse: rely on mutex_unlock() barrier instead of fput()
fuse: extract fuse_fill_super_common()
virtio_fs: get mount working
fuse: export fuse_end_request()
fuse: export fuse_len_args()
fuse: add fuse_iqueue_ops callbacks
fuse: process requests queues
fuse: export fuse_get_unique()
fuse: implement FUSE_FORGET for virtio-fs
virtio_fs: Set up dax_device
dax: remove block device dependencies
fuse: add fuse_conn->dax_dev field
fuse: map virtio_fs DAX window BAR
fuse: Implement basic DAX read/write support commands
fuse: add DAX mmap support
Vivek Goyal (22):
virtio-fs: Retrieve shm capabilities for cache
virtio-fs: Map cache using the values from the capabilities Limit number of pages returned by direct_access()
fuse: Introduce fuse_dax_mapping
Create a list of free memory ranges
fuse: Introduce setupmapping/removemapping commands Introduce interval tree basic data structures
fuse: Maintain a list of busy elements
Do fallocate() to grow file before mapping for file growing writes
dax: Pass dax_dev to dax_writeback_mapping_range()
fuse: Define dax address space operations
fuse, dax: Take ->i_mmap_sem lock during dax page fault
fuse: Add logic to free up a memory range
fuse: Add logic to do direct reclaim of memory
fuse: Kick worker when free memory drops below 20% of total ranges Dispatch FORGET requests later instead of dropping them Release file in process context
fuse: Do not block on inode lock while freeing memory range
fuse: Reschedule dax free work if too many EAGAIN attempts
fuse: Wait for memory ranges to become free
fuse: Take inode lock for dax inode truncation
fuse: Clear setuid bit even in direct I/O path
drivers/dax/super.c | 3 +- fs/dax.c | 23 +- fs/ext4/inode.c | 2 +- fs/fuse/Kconfig | 11 + fs/fuse/Makefile | 1 + fs/fuse/cuse.c | 3 +- fs/fuse/dev.c | 80 ++- fs/fuse/dir.c | 282 +++++++-- fs/fuse/file.c | 1012 +++++++++++++++++++++++++++-- fs/fuse/fuse_i.h | 234 ++++++- fs/fuse/inode.c | 278 ++++++-- fs/fuse/readdir.c | 12 +- fs/fuse/virtio_fs.c | 1336 +++++++++++++++++++++++++++++++++++++++ fs/splice.c | 3 +- fs/xfs/xfs_aops.c | 2 +- include/linux/dax.h | 6 +- include/linux/fs.h | 2 + include/uapi/linux/fuse.h | 39 ++ include/uapi/linux/virtio_fs.h | 46 ++ include/uapi/linux/virtio_ids.h | 1 + include/uapi/linux/virtio_pci.h | 10 + 21 files changed, 3151 insertions(+), 235 deletions(-) create mode 100644 fs/fuse/virtio_fs.c create mode 100644 include/uapi/linux/virtio_fs.h
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 1/52 fuse: add skeleton virtio_fs.ko module
From: Stefan Hajnoczi <[email protected]>
Add a basic file system module for virtio-fs.
--- fs/fuse/Kconfig | 10 ++++++++++ fs/fuse/Makefile | 1 + fs/fuse/virtio_fs.c | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 fs/fuse/virtio_fs.c
fs/fuse/Kconfig +10 -0
@@ -26,3 +26,13 @@ config CUSE If you want to develop or use a userspace character device based on CUSE, answer Y or M. + +config VIRTIO_FS + tristate "Virtio Filesystem" + depends on FUSE_FS + help + The Virtio Filesystem allows guests to mount file systems from the + host. + + If you want to share files between guests or with the host, answer Y + or M.
fs/fuse/Makefile +1 -0
@@ -4,5 +4,6 @@ obj-$(CONFIG_FUSE_FS) += fuse.o obj-$(CONFIG_CUSE) += cuse.o +obj-$(CONFIG_VIRTIO_FS) += virtio_fs.o fuse-objs := dev.o dir.o file.o inode.o control.o xattr.o acl.o readdir.o
fs/fuse/virtio_fs.c new file +33 -0
@@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* virtio-fs: Virtio Filesystem +* Copyright (C) 2018 Red Hat, Inc. +*/ + +#include <linux/module.h> +#include <linux/fs.h> + +MODULE_AUTHOR ("Stefan Hajnoczi <[email protected]>" ); +MODULE_DESCRIPTION ("Virtio Filesystem" ); +MODULE_LICENSE ("GPL" ); +MODULE_ALIAS_FS (KBUILD_MODNAME ); + +static struct file_system_type virtio_fs_type = { + .owner =THIS_MODULE , + .name =KBUILD_MODNAME , + .mount =NULL , + .kill_sb =NULL , +}; + +static int __init virtio_fs_init (void ) +{ +return register_filesystem (&virtio_fs_type); +} + +static void __exit virtio_fs_exit(void) +{ +unregister_filesystem (&virtio_fs_type); +} + +module_init (virtio_fs_init); +module_exit (virtio_fs_exit);
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 2/52 fuse: add probe/remove virtio driver
In reply to Vivek GoyalFrom: Stefan Hajnoczi <[email protected]>
Add basic probe/remove functionality for the new virtio-fs device.
--- fs/fuse/Kconfig | 1 + fs/fuse/virtio_fs.c | 160 ++++++++++++++++++++++++++++++++++++++-- include/uapi/linux/virtio_fs.h | 41 ++++++++++ include/uapi/linux/virtio_ids.h | 1 + 4 files changed, 195 insertions(+), 8 deletions(-) create mode 100644 include/uapi/linux/virtio_fs.h
fs/fuse/Kconfig +1 -0
@@ -30,6 +30,7 @@ config CUSE config VIRTIO_FS tristate "Virtio Filesystem" depends on FUSE_FS + select VIRTIO help The Virtio Filesystem allows guests to mount file systems from the host.
fs/fuse/virtio_fs.c +152 -8
178 lines not on this page · raw message
include/uapi/linux/virtio_fs.h new file +41 -0
41 lines not on this page · raw message
include/uapi/linux/virtio_ids.h +1 -0
@@ -43,5 +43,6 @@#define VIRTIO_ID_INPUT 18/* virtio input */ #define VIRTIO_ID_VSOCK 19/* virtio vsock transport */ #define VIRTIO_ID_CRYPTO 20/* virtio crypto */ +#define VIRTIO_ID_FS 26/* virtio filesystem */ #endif /* _LINUX_VIRTIO_IDS_H */
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 3/52 fuse: rely on mutex_unlock() barrier instead of fput()
In reply to Vivek GoyalFrom: Stefan Hajnoczi <[email protected]>
fput() will be moved out of this function in a later patch, so we cannot rely on it as the memory barrier for ensuring file->private_data = fud is visible.
Luckily there is a mutex_unlock() right before fput() which provides the same effect.
--- fs/fuse/inode.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)
fs/fuse/inode.c +3 -4
@@ -1198,12 +1198,11 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)list_add_tail (&fc->entry, &fuse_conn_list); sb->s_root = root_dentry; file->private_data = fud; -mutex_unlock (&fuse_mutex);/* -* atomic_dec_and_test() in fput() provides the necessary -* memory barrier for file->private_data to be visible on all -* CPUs after this +* mutex_unlock() provides the necessary memory barrier for +* file->private_data to be visible on all CPUs after this */ +mutex_unlock (&fuse_mutex);fput (file);fuse_send_init (fc, init_req);
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 4/52 fuse: extract fuse_fill_super_common()
In reply to Vivek GoyalFrom: Stefan Hajnoczi <[email protected]>
fuse_fill_super() includes code to process the fd= option and link the struct fuse_dev to the fd's struct file. In virtio-fs there is no file descriptor because /dev/fuse is not used.
This patch extracts fuse_fill_super_common() so that both classic fuse and virtio-fs can share the code to initialize a mount.
parse_fuse_opt() is also extracted so that the fuse_fill_super_common() caller has access to the mount options. This allows classic fuse to handle the fd= option outside fuse_fill_super_common().
--- fs/fuse/fuse_i.h | 32 +++++++++++++++++ fs/fuse/inode.c | 102 +++++++++++++++++++++++++++---------------------------- 2 files changed, 83 insertions(+), 51 deletions(-)
fs/fuse/fuse_i.h +32 -0
@@ -56,6 +56,22 @@ extern struct mutex fuse_mutex;extern unsigned max_user_bgreq;extern unsigned max_user_congthresh; +/** Mount options */ +struct fuse_mount_data { +int fd; +unsigned rootmode; +kuid_t user_id; +kgid_t group_id; +unsigned fd_present:1 ; +unsigned rootmode_present:1 ; +unsigned user_id_present:1 ; +unsigned group_id_present:1 ; +unsigned default_permissions:1 ; +unsigned allow_other:1 ; +unsigned max_read; +unsigned blksize; +}; +/* One forget request */ struct fuse_forget_link {struct fuse_forget_one forget_one;
@@ -970,6 +986,22 @@ struct fuse_dev *fuse_dev_alloc(struct fuse_conn *fc);void fuse_dev_free (struct fuse_dev *fud);/** +* Parse a mount options string +*/ +int parse_fuse_opt (char *opt,struct fuse_mount_data *d,int is_bdev, +struct user_namespace *user_ns); + +/** +* Fill in superblock and initialize fuse connection +* @sb: partially-initialized superblock to fill in +* @mount_data: mount parameters +* @fudptr: fuse_dev pointer to fill in, should contain NULL on entry +*/ +int fuse_fill_super_common (struct super_block *sb, +struct fuse_mount_data *mount_data, +void **fudptr); + +/** * Add connection to control filesystem */ int fuse_ctl_add_conn (struct fuse_conn *fc);
fs/fuse/inode.c +51 -51
192 lines not on this page · raw message
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 5/52 virtio_fs: get mount working
In reply to Vivek GoyalFrom: Stefan Hajnoczi <[email protected]>
Provide definitions of ->mount and ->kill_sb. This is still WIP.
--- fs/fuse/fuse_i.h | 9 ++++ fs/fuse/inode.c | 12 ++++- fs/fuse/virtio_fs.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 146 insertions(+), 4 deletions(-)
fs/fuse/fuse_i.h +9 -0
@@ -59,10 +59,12 @@ extern unsigned max_user_congthresh;/** Mount options */ struct fuse_mount_data {int fd; +const char *tag;/* lifetime: .fill_super() data argument */ unsigned rootmode;kuid_t user_id;kgid_t group_id;unsigned fd_present:1 ; +unsigned tag_present:1 ;unsigned rootmode_present:1 ;unsigned user_id_present:1 ;unsigned group_id_present:1 ;
@@ -1002,6 +1004,13 @@ int fuse_fill_super_common(struct super_block *sb, void **fudptr);/** +* Disassociate fuse connection from superblock and kill the superblock +* +* Calls kill_anon_super(), use with do not use with bdev mounts. +*/ +void fuse_kill_sb_anon (struct super_block *sb); + +/** * Add connection to control filesystem */ int fuse_ctl_add_conn (struct fuse_conn *fc);
fs/fuse/inode.c +10 -2
@@ -431,6 +431,7 @@ static int fuse_statfs(struct dentry *dentry, struct kstatfs *buf)enum {OPT_FD , +OPT_TAG ,OPT_ROOTMODE ,OPT_USER_ID ,OPT_GROUP_ID ,
@@ -443,6 +444,7 @@ enum {static const match_table_t tokens = { {OPT_FD ,"fd=%u" }, + {OPT_TAG ,"tag=%s" }, {OPT_ROOTMODE ,"rootmode=%o" }, {OPT_USER_ID ,"user_id=%u" }, {OPT_GROUP_ID ,"group_id=%u" },
@@ -489,6 +491,11 @@ int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev, d->fd_present =1 ;break ; +case OPT_TAG : + d->tag = args[0 ].from; + d->tag_present =1 ; +break ; +case OPT_ROOTMODE :if (match_octal (&args[0 ], &value))return 0 ;
@@ -1204,7 +1211,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) err = -EINVAL ;if (!parse_fuse_opt (data, &d, is_bdev, sb->s_user_ns)) goto err; -if (!d.fd_present) +if (!d.fd_present || d.tag_present) goto err; file =fget (d.fd);
@@ -1249,11 +1256,12 @@ static void fuse_sb_destroy(struct super_block *sb) } } -static void fuse_kill_sb_anon (struct super_block *sb) +void fuse_kill_sb_anon (struct super_block *sb) {fuse_sb_destroy (sb);kill_anon_super (sb); } +EXPORT_SYMBOL_GPL (fuse_kill_sb_anon);static struct file_system_type fuse_fs_type = { .owner =THIS_MODULE ,
fs/fuse/virtio_fs.c +127 -2
168 lines not on this page · raw message
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 6/52 fuse: export fuse_end_request()
In reply to Vivek GoyalFrom: Stefan Hajnoczi <[email protected]>
virtio-fs will need to complete requests from outside fs/fuse/dev.c. Make the symbol visible.
--- fs/fuse/dev.c | 19 ++++++++++--------- fs/fuse/fuse_i.h | 5 +++++ 2 files changed, 15 insertions(+), 9 deletions(-)
fs/fuse/dev.c +10 -9
@@ -425,7 +425,7 @@ static void flush_bg_queue(struct fuse_conn *fc)* the 'end' callback is called if given, else the reference to the * request is released */ -static void request_end (struct fuse_conn *fc,struct fuse_req *req) +void fuse_request_end (struct fuse_conn *fc,struct fuse_req *req) {struct fuse_iqueue *fiq = &fc->iq;
@@ -469,6 +469,7 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req) put_request:fuse_put_request (fc, req); } +EXPORT_SYMBOL_GPL (fuse_request_end);static void queue_interrupt (struct fuse_iqueue *fiq,struct fuse_req *req) {
@@ -543,12 +544,12 @@ static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req) req->in.h.unique =fuse_get_unique (fiq);queue_request (fiq, req);/* acquire extra reference, since request is still needed -after request_end() */ +after fuse_request_end() */ __fuse_get_request (req);spin_unlock (&fiq->waitq.lock);request_wait_answer (fc, req); -/* Pairs with smp_wmb() in request_end() */ +/* Pairs with smp_wmb() in fuse_request_end() */ smp_rmb (); } }
@@ -1278,7 +1279,7 @@ __releases(fiq->waitq.lock)* the pending list and copies request data to userspace buffer. If * no reply is needed (FORGET) or request has been aborted or there * was an error during the copying then it's finished by calling -* request_end(). Otherwise add it to the processing list, and set +* fuse_request_end(). Otherwise add it to the processing list, and set * the 'sent' flag. */ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file,
@@ -1338,7 +1339,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 (in->h.opcode ==FUSE_SETXATTR ) req->out.h.error = -E2BIG ; -request_end (fc, req); +fuse_request_end (fc, req); goto restart; }spin_lock (&fpq->lock);
@@ -1381,7 +1382,7 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file,if (!test_bit (FR_PRIVATE , &req->flags))list_del_init (&req->list);spin_unlock (&fpq->lock); -request_end (fc, req); +fuse_request_end (fc, req);return err; err_unlock:
@@ -1889,7 +1890,7 @@ static int copy_out_args(struct fuse_copy_state *cs, struct fuse_out *out,* the write buffer. The request is then searched on the processing * list by the unique ID found in the header. If found, then remove * it from the list and copy the rest of the buffer to the request. -* The request is finished by calling request_end() +* The request is finished by calling fuse_request_end(). */ static ssize_t fuse_dev_do_write (struct fuse_dev *fud,struct fuse_copy_state *cs,size_t nbytes)
@@ -1976,7 +1977,7 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,list_del_init (&req->list);spin_unlock (&fpq->lock); -request_end (fc, req); +fuse_request_end (fc, req);return err ? err : nbytes;
@@ -2120,7 +2121,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); -request_end (fc, req); +fuse_request_end (fc, req); } }
fs/fuse/fuse_i.h +5 -0
@@ -953,6 +953,11 @@ ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args);void fuse_request_send_background (struct fuse_conn *fc,struct fuse_req *req);bool fuse_request_queue_background (struct fuse_conn *fc,struct fuse_req *req); +/** +* End a finished request +*/ +void fuse_request_end (struct fuse_conn *fc,struct fuse_req *req); +/* Abort all requests */ void fuse_abort_conn (struct fuse_conn *fc,bool is_abort);void fuse_wait_aborted (struct fuse_conn *fc);
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 7/52 fuse: export fuse_len_args()
In reply to Vivek GoyalFrom: Stefan Hajnoczi <[email protected]>
virtio-fs will need to query the length of fuse_arg lists. Make the symbol visible.
--- fs/fuse/dev.c | 7 ++++--- fs/fuse/fuse_i.h | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-)
fs/fuse/dev.c +4 -3
@@ -348,7 +348,7 @@ void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req) }EXPORT_SYMBOL_GPL (fuse_put_request); -static unsigned len_args (unsigned numargs,struct fuse_arg *args) +unsigned fuse_len_args (unsigned numargs,struct fuse_arg *args) {unsigned nbytes =0 ;unsigned i;
@@ -358,6 +358,7 @@ static unsigned len_args(unsigned numargs, struct fuse_arg *args)return nbytes; } +EXPORT_SYMBOL_GPL (fuse_len_args);static u64 fuse_get_unique (struct fuse_iqueue *fiq) {
@@ -373,7 +374,7 @@ static unsigned int fuse_req_hash(u64 unique)static void queue_request (struct fuse_iqueue *fiq,struct fuse_req *req) { req->in.h.len =sizeof (struct fuse_in_header ) + -len_args (req->in.numargs, (struct fuse_arg *) req->in.args); +fuse_len_args (req->in.numargs, (struct fuse_arg *) req->in.args);list_add_tail (&req->list, &fiq->pending);wake_up_locked (&fiq->waitq);kill_fasync (&fiq->fasync,SIGIO ,POLL_IN );
@@ -1870,7 +1871,7 @@ static int copy_out_args(struct fuse_copy_state *cs, struct fuse_out *out,if (out->h.error)return nbytes != reqsize ? -EINVAL :0 ; - reqsize +=len_args (out->numargs, out->args); + reqsize +=fuse_len_args (out->numargs, out->args);if (reqsize < nbytes || (reqsize > nbytes && !out->argvar))return -EINVAL ;
fs/fuse/fuse_i.h +5 -0
@@ -1120,4 +1120,9 @@ int fuse_set_acl(struct inode *inode, struct posix_acl *acl, int type);/* readdir.c */ int fuse_readdir (struct file *file,struct dir_context *ctx); +/** +* Return the number of bytes in an arguments list +*/ +unsigned fuse_len_args (unsigned numargs,struct fuse_arg *args); +#endif /* _FS_FUSE_I_H */
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 8/52 fuse: add fuse_iqueue_ops callbacks
In reply to Vivek GoyalFrom: Stefan Hajnoczi <[email protected]>
The /dev/fuse device uses fiq->waitq and fasync to signal that requests are available. These mechanisms do not apply to virtio-fs. This patch introduces callbacks so alternative behavior can be used.
Note that queue_interrupt() changes along these lines:
spin_lock(&fiq->waitq.lock);
wake_up_locked(&fiq->waitq);
+ kill_fasync(&fiq->fasync, SIGIO, POLL_IN);
spin_unlock(&fiq->waitq.lock);
- kill_fasync(&fiq->fasync, SIGIO, POLL_IN);
Since queue_request() and queue_forget() also call kill_fasync() inside the spinlock this should be safe.
--- fs/fuse/cuse.c | 2 +- fs/fuse/dev.c | 50 ++++++++++++++++++++++++++++++++++---------------- fs/fuse/fuse_i.h | 46 +++++++++++++++++++++++++++++++++++++++++++++- fs/fuse/inode.c | 18 +++++++++++++----- 4 files changed, 93 insertions(+), 23 deletions(-)
fs/fuse/cuse.c +1 -1
@@ -503,7 +503,7 @@ 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_conn_init (&cc->fc, file->f_cred->user_ns, &fuse_dev_fiq_ops,NULL ); fud =fuse_dev_alloc (&cc->fc);if (!fud) {
fs/fuse/dev.c +34 -16
98 lines not on this page · raw message
fs/fuse/fuse_i.h +45 -1
74 lines not on this page · raw message
fs/fuse/inode.c +13 -5
@@ -574,7 +574,9 @@ static int fuse_show_options(struct seq_file *m, struct dentry *root)return 0 ; } -static void fuse_iqueue_init (struct fuse_iqueue *fiq) +static void fuse_iqueue_init (struct fuse_iqueue *fiq, +const struct fuse_iqueue_ops *ops, +void *priv) {memset (fiq,0 ,sizeof (struct fuse_iqueue ));init_waitqueue_head (&fiq->waitq);
@@ -582,6 +584,8 @@ static void fuse_iqueue_init(struct fuse_iqueue *fiq)INIT_LIST_HEAD (&fiq->interrupts); fiq->forget_list_tail = &fiq->forget_list_head; fiq->connected =1 ; + fiq->ops = ops; + fiq->priv = priv; }static void fuse_pqueue_init (struct fuse_pqueue *fpq)
@@ -595,7 +599,8 @@ static void fuse_pqueue_init(struct fuse_pqueue *fpq) fpq->connected =1 ; } -void fuse_conn_init (struct fuse_conn *fc,struct user_namespace *user_ns) +void fuse_conn_init (struct fuse_conn *fc,struct user_namespace *user_ns, +const struct fuse_iqueue_ops *fiq_ops,void *fiq_priv) {memset (fc,0 ,sizeof (*fc));spin_lock_init (&fc->lock);
@@ -605,7 +610,7 @@ void fuse_conn_init(struct fuse_conn *fc, struct user_namespace *user_ns)atomic_set (&fc->dev_count,1 );init_waitqueue_head (&fc->blocked_waitq);init_waitqueue_head (&fc->reserved_req_waitq); -fuse_iqueue_init (&fc->iq); +fuse_iqueue_init (&fc->iq, fiq_ops, fiq_priv);INIT_LIST_HEAD (&fc->bg_queue);INIT_LIST_HEAD (&fc->entry);INIT_LIST_HEAD (&fc->devices);
@@ -1067,6 +1072,8 @@ EXPORT_SYMBOL_GPL(fuse_dev_free);int fuse_fill_super_common (struct super_block *sb,struct fuse_mount_data *mount_data, +const struct fuse_iqueue_ops *fiq_ops, +void *fiq_priv,void **fudptr) {struct fuse_dev *fud;
@@ -1115,7 +1122,7 @@ int fuse_fill_super_common(struct super_block *sb,if (!fc) goto err; -fuse_conn_init (fc, sb->s_user_ns); +fuse_conn_init (fc, sb->s_user_ns, fiq_ops, fiq_priv); fc->release = fuse_free_conn; fud =fuse_dev_alloc (fc);
@@ -1226,7 +1233,8 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) (file->f_cred->user_ns != sb->s_user_ns)) goto err_fput; - err =fuse_fill_super_common (sb, &d, &file->private_data); + err =fuse_fill_super_common (sb, &d, &fuse_dev_fiq_ops,NULL , + &file->private_data); err_fput:fput (file); err:
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 9/52 fuse: process requests queues
In reply to Vivek GoyalFrom: Stefan Hajnoczi <[email protected]>
Send normal requests to the device and handle completions.
This is enough to get mount and basic I/O working. The hiprio and notifications queues still need to be implemented for full FUSE functionality.
--- fs/fuse/fuse_i.h | 3 + fs/fuse/virtio_fs.c | 529 +++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 501 insertions(+), 31 deletions(-)
fs/fuse/fuse_i.h +3 -0
@@ -452,6 +452,9 @@ struct fuse_req {/** Request is stolen from fuse_file->reserved_req */ struct file *stolen_file; + +/** virtio-fs's physically contiguous buffer for in and out args */ +void *argbuf; };struct fuse_iqueue ;
fs/fuse/virtio_fs.c +498 -31
601 lines not on this page · raw message
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 10/52 fuse: export fuse_get_unique()
In reply to Vivek GoyalFrom: Stefan Hajnoczi <[email protected]>
virtio-fs will need unique IDs for FORGET requests from outside fs/fuse/dev.c. Make the symbol visible.
--- fs/fuse/dev.c | 3 ++- fs/fuse/fuse_i.h | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-)
fs/fuse/dev.c +2 -1
@@ -360,11 +360,12 @@ unsigned fuse_len_args(unsigned numargs, struct fuse_arg *args) }EXPORT_SYMBOL_GPL (fuse_len_args); -static u64 fuse_get_unique (struct fuse_iqueue *fiq) +u64 fuse_get_unique (struct fuse_iqueue *fiq) { fiq->reqctr +=FUSE_REQ_ID_STEP ;return fiq->reqctr; } +EXPORT_SYMBOL_GPL (fuse_get_unique);static unsigned int fuse_req_hash (u64 unique) {
fs/fuse/fuse_i.h +5 -0
@@ -1172,4 +1172,9 @@ int fuse_readdir(struct file *file, struct dir_context *ctx);*/ unsigned fuse_len_args (unsigned numargs,struct fuse_arg *args); +/** +* Get the next unique ID for a request +*/ +u64 fuse_get_unique (struct fuse_iqueue *fiq); +#endif /* _FS_FUSE_I_H */
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 11/52 fuse: implement FUSE_FORGET for virtio-fs
In reply to Vivek GoyalFrom: Stefan Hajnoczi <[email protected]>
Sent single FUSE_FORGET requests on the hiprio queue. In the future it may be possible to do FUSE_BATCH_FORGET but that is tricky since virtio-fs gets called synchronously when forgets are queued.
--- fs/fuse/virtio_fs.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 89 insertions(+), 4 deletions(-)
fs/fuse/virtio_fs.c +89 -4
@@ -140,10 +140,26 @@ static void virtio_fs_notifications_done_work(struct work_struct *work)return ; } -static void virtio_fs_hiprio_done (struct virtqueue *vq) +/* Work function for hiprio completion */ +static void virtio_fs_hiprio_done_work (struct work_struct *work) { -/* TODO */ -dev_dbg (&vq->vdev->dev,"%s\n" , __func__); + struct virtio_fs_vq *fsvq = container_of(work, struct virtio_fs_vq, + done_work); +struct fuse_pqueue *fpq = &fsvq->fud->pq; +struct virtqueue *vq = fsvq->vq; + +/* Free completed FUSE_FORGET requests */ +spin_lock (&fpq->lock); +do { +unsigned len; +void *req; + +virtqueue_disable_cb (vq); + +while ((req =virtqueue_get_buf (vq, &len)) !=NULL ) +kfree (req); + }while (!virtqueue_enable_cb (vq) &&likely (!virtqueue_is_broken (vq))); +spin_unlock (&fpq->lock); }/* Allocate and copy args into req->argbuf */
@@ -302,6 +318,7 @@ static int virtio_fs_setup_vqs(struct virtio_device *vdev, callbacks[1 ] = virtio_fs_vq_done;snprintf (fs->vqs[1 ].name,sizeof (fs->vqs[1 ].name),"hiprio" ); names[1 ] = fs->vqs[1 ].name; +INIT_WORK (&fs->vqs[1 ].done_work, virtio_fs_hiprio_done_work);/* Initialize the requests virtqueues */ for (i =2 ; i < fs->nvqs; i++) {
@@ -424,11 +441,79 @@ static struct virtio_driver virtio_fs_driver = {#endif }; +struct virtio_fs_forget { +struct fuse_in_header ih; +struct fuse_forget_in arg; +}; +static void virtio_fs_wake_forget_and_unlock (struct fuse_iqueue *fiq) __releases(fiq->waitq.lock) { -/* TODO */ +struct fuse_forget_link *link; +struct virtio_fs_forget *forget; +struct fuse_pqueue *fpq; +struct scatterlist sg; +struct scatterlist *sgs[] = {&sg}; +struct virtio_fs *fs; +struct virtqueue *vq; +bool notify; +u64 unique; +int ret; + +BUG_ON (!fiq->forget_list_head.next); + link = fiq->forget_list_head.next; +BUG_ON (link->next); + fiq->forget_list_head.next =NULL ; + fiq->forget_list_tail = &fiq->forget_list_head; + + unique =fuse_get_unique (fiq); + + fs = fiq->priv; +spin_unlock (&fiq->waitq.lock); + +/* Allocate a buffer for the request */ + forget =kmalloc (sizeof (*forget),GFP_ATOMIC ); +if (!forget) { +pr_err ("virtio-fs: dropped FORGET: kmalloc failed\n" ); + goto out;/* TODO avoid dropping it? */ + } + + forget->ih = (struct fuse_in_header ){ + .opcode =FUSE_FORGET , + .nodeid = link->forget_one.nodeid, + .unique = unique, + .len =sizeof (*forget), + }; + forget->arg = (struct fuse_forget_in ){ + .nlookup = link->forget_one.nlookup, + }; + +sg_init_one (&sg, forget,sizeof (*forget)); + +/* Enqueue the request */ + vq = fs->vqs[1 ].vq; +dev_dbg (&vq->vdev->dev,"%s\n" , __func__); + fpq =vq_to_fpq (vq); +spin_lock (&fpq->lock); + + ret =virtqueue_add_sgs (vq, sgs,1 ,0 , forget,GFP_ATOMIC ); +if (ret <0 ) { +pr_err ("virtio-fs: dropped FORGET: queue full\n" ); +/* TODO handle full virtqueue */ +spin_unlock (&fpq->lock); + goto out; + } + + notify =virtqueue_kick_prepare (vq); + +spin_unlock (&fpq->lock); + +if (notify) +virtqueue_notify (vq); + +out: +kfree (link); }static void virtio_fs_wake_interrupt_and_unlock (struct fuse_iqueue *fiq)
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 12/52 virtio_fs: Set up dax_device
In reply to Vivek GoyalFrom: Stefan Hajnoczi <[email protected]>
Setup a dax device.
--- fs/fuse/virtio_fs.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+)
fs/fuse/virtio_fs.c +68 -0
@@ -5,6 +5,8 @@*/ #include <linux/fs.h> +#include <linux/dax.h> +#include <linux/pfn_t.h> #include <linux/module.h> #include <linux/virtio.h> #include <linux/virtio_fs.h>
@@ -29,6 +31,11 @@ struct virtio_fs {struct virtio_fs_vq *vqs;unsigned nvqs;/* number of virtqueues */ unsigned num_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; };static inline struct virtio_fs_vq *vq_to_fsvq (struct virtqueue *vq)
@@ -350,6 +357,44 @@ 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); + +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; +} + +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 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, +}; +static int virtio_fs_probe (struct virtio_device *vdev) {struct virtio_fs *fs;
@@ -371,6 +416,17 @@ static int virtio_fs_probe(struct virtio_device *vdev)/* TODO vq affinity */ /* TODO populate notifications vq */ +if (IS_ENABLED (CONFIG_DAX_DRIVER )) { +/* TODO map window */ + fs->window_kaddr =NULL ; + fs->window_phys_addr =0 ; + + fs->dax_dev =alloc_dax (fs,NULL , &virtio_fs_dax_ops); +if (!fs->dax_dev) + goto out_vqs;/* TODO handle case where device doesn't expose +BAR */ + } +/* Bring the device online in case the filesystem is mounted and * requests need to be sent before we return. */
@@ -386,6 +442,12 @@ static int virtio_fs_probe(struct virtio_device *vdev) vdev->config->reset (vdev);virtio_fs_cleanup_vqs (vdev, fs); +if (fs->dax_dev) { +kill_dax (fs->dax_dev); +put_dax (fs->dax_dev); + fs->dax_dev =NULL ; + } + out: vdev->priv =NULL ;return ret;
@@ -404,6 +466,12 @@ static void virtio_fs_remove(struct virtio_device *vdev)list_del (&fs->list);mutex_unlock (&virtio_fs_mutex); +if (fs->dax_dev) { +kill_dax (fs->dax_dev); +put_dax (fs->dax_dev); + fs->dax_dev =NULL ; + } + vdev->priv =NULL ; }
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 13/52 dax: remove block device dependencies
In reply to Vivek GoyalFrom: Stefan Hajnoczi <[email protected]>
Although struct dax_device itself is not tied to a block device, some DAX code assumes there is a block device. Make block devices optional by allowing bdev to be NULL in commonly used DAX APIs.
When there is no block device:
* Skip the partition offset calculation in bdev_dax_pgoff()
* Skip the blkdev_issue_zeroout() optimization
Note that more block device assumptions remain but I haven't reach those code paths yet.
--- drivers/dax/super.c | 3 ++- fs/dax.c | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-)
drivers/dax/super.c +2 -1
@@ -52,7 +52,8 @@ EXPORT_SYMBOL_GPL(dax_read_unlock);int bdev_dax_pgoff (struct block_device *bdev,sector_t sector,size_t size,pgoff_t *pgoff) { -phys_addr_t phys_off = (get_start_sect (bdev) + sector) *512 ; +sector_t start_sect = bdev ?get_start_sect (bdev) :0 ; +phys_addr_t phys_off = (start_sect + sector) *512 ;if (pgoff) *pgoff =PHYS_PFN (phys_off);
fs/dax.c +6 -1
@@ -1021,7 +1021,12 @@ static vm_fault_t dax_load_hole(struct xa_state *xas,static bool dax_range_is_aligned (struct block_device *bdev,unsigned int offset,unsigned int length) { -unsigned short sector_size =bdev_logical_block_size (bdev); +unsigned short sector_size; + +if (!bdev) +return false; + + sector_size =bdev_logical_block_size (bdev);if (!IS_ALIGNED (offset, sector_size))return false;
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 14/52 fuse: add fuse_conn->dax_dev field
In reply to Vivek GoyalFrom: Stefan Hajnoczi <[email protected]>
A struct dax_device instance is a prerequisite for the DAX filesystem APIs. Let virtio_fs associate a dax_device with a fuse_conn. Classic FUSE and CUSE set the pointer to NULL, disabling DAX.
--- fs/fuse/cuse.c | 3 ++- fs/fuse/fuse_i.h | 8 +++++++- fs/fuse/inode.c | 9 ++++++--- fs/fuse/virtio_fs.c | 3 ++- 4 files changed, 17 insertions(+), 6 deletions(-)
fs/fuse/cuse.c +2 -1
@@ -503,7 +503,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, file->f_cred->user_ns,NULL , &fuse_dev_fiq_ops, +NULL ); fud =fuse_dev_alloc (&cc->fc);if (!fud) {
fs/fuse/fuse_i.h +7 -1
@@ -803,6 +803,9 @@ struct fuse_conn {/** List of device instances belonging to this connection */ struct list_head devices; + +/** DAX device, non-NULL if DAX is supported */ +struct dax_device *dax_dev; };static inline struct fuse_conn *get_fuse_conn_super (struct super_block *sb)
@@ -1025,7 +1028,8 @@ struct fuse_conn *fuse_conn_get(struct fuse_conn *fc);* Initialize fuse_conn */ void fuse_conn_init (struct fuse_conn *fc,struct user_namespace *user_ns, -const struct fuse_iqueue_ops *fiq_ops,void *fiq_priv); +struct dax_device *dax_dev, +const struct fuse_iqueue_ops *fiq_ops,void *fiq_priv);/** * Release reference to fuse_conn
@@ -1045,12 +1049,14 @@ int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev,* Fill in superblock and initialize fuse connection * @sb: partially-initialized superblock to fill in * @mount_data: mount parameters +* @dax_dev: DAX device, may be NULL * @fiq_ops: fuse input queue operations * @fiq_priv: device-specific state for fuse_iqueue * @fudptr: fuse_dev pointer to fill in, should contain NULL on entry */ int fuse_fill_super_common (struct super_block *sb,struct fuse_mount_data *mount_data, +struct dax_device *dax_dev,const struct fuse_iqueue_ops *fiq_ops,void *fiq_priv,void **fudptr);
fs/fuse/inode.c +6 -3
@@ -600,7 +600,8 @@ static void fuse_pqueue_init(struct fuse_pqueue *fpq) }void fuse_conn_init (struct fuse_conn *fc,struct user_namespace *user_ns, -const struct fuse_iqueue_ops *fiq_ops,void *fiq_priv) +struct dax_device *dax_dev, +const struct fuse_iqueue_ops *fiq_ops,void *fiq_priv) {memset (fc,0 ,sizeof (*fc));spin_lock_init (&fc->lock);
@@ -625,6 +626,7 @@ void fuse_conn_init(struct fuse_conn *fc, struct user_namespace *user_ns, fc->attr_version =1 ;get_random_bytes (&fc->scramble_key,sizeof (fc->scramble_key)); fc->pid_ns =get_pid_ns (task_active_pid_ns (current)); + fc->dax_dev = dax_dev; fc->user_ns =get_user_ns (user_ns); }EXPORT_SYMBOL_GPL (fuse_conn_init);
@@ -1072,6 +1074,7 @@ EXPORT_SYMBOL_GPL(fuse_dev_free);int fuse_fill_super_common (struct super_block *sb,struct fuse_mount_data *mount_data, +struct dax_device *dax_dev,const struct fuse_iqueue_ops *fiq_ops,void *fiq_priv,void **fudptr)
@@ -1122,7 +1125,7 @@ int fuse_fill_super_common(struct super_block *sb,if (!fc) goto err; -fuse_conn_init (fc, sb->s_user_ns, fiq_ops, fiq_priv); +fuse_conn_init (fc, sb->s_user_ns, dax_dev, fiq_ops, fiq_priv); fc->release = fuse_free_conn; fud =fuse_dev_alloc (fc);
@@ -1233,7 +1236,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) (file->f_cred->user_ns != sb->s_user_ns)) goto err_fput; - err =fuse_fill_super_common (sb, &d, &fuse_dev_fiq_ops,NULL , + err =fuse_fill_super_common (sb, &d,NULL , &fuse_dev_fiq_ops,NULL , &file->private_data); err_fput:fput (file);
fs/fuse/virtio_fs.c +2 -1
@@ -844,7 +844,8 @@ static int virtio_fs_fill_super(struct super_block *sb, void *data,/* TODO this sends FUSE_INIT and could cause hiprio or notifications * virtqueue races since they haven't been set up yet! */ - err =fuse_fill_super_common (sb, &d, &virtio_fs_fiq_ops, fs, + err =fuse_fill_super_common (sb, &d, fs->dax_dev, + &virtio_fs_fiq_ops, fs, (void **)&fs->vqs[2 ].fud);if (err <0 ) goto err_fud;
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 15/52 fuse: map virtio_fs DAX window BAR
In reply to Vivek GoyalFrom: Stefan Hajnoczi <[email protected]>
Experimental QEMU code introduces an MMIO BAR for mapping portions of files in the virtio-fs device. Map this BAR so that FUSE DAX can access file contents from the host page cache.
The DAX window is accessed by the fs/dax.c infrastructure and must have struct pages (at least on x86). Use devm_memremap_pages() to map the DAX window PCI BAR and allocate struct page.
--- fs/fuse/virtio_fs.c | 166 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 143 insertions(+), 23 deletions(-)
fs/fuse/virtio_fs.c +143 -23
@@ -6,12 +6,18 @@#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 "fuse_i.h" +enum { +/* PCI BAR number of the virtio-fs DAX window */ +VIRTIO_FS_WINDOW_BAR =2 , +}; +/* List of virtio-fs device instances and a lock for the list */ static DEFINE_MUTEX (virtio_fs_mutex);static LIST_HEAD (virtio_fs_instances);
@@ -24,6 +30,18 @@ struct virtio_fs_vq {char name[24 ]; }____cacheline_aligned_in_smp ; +/* State needed for devm_memremap_pages(). This API is called on the +* underlying pci_dev instead of struct virtio_fs (layering violation). Since +* the memremap release function only gets called when the pci_dev is released, +* keep the associated state separate from struct virtio_fs (it has a different +* lifecycle from pci_dev). +*/ +struct virtio_fs_memremap_info { +struct dev_pagemap pgmap; +struct percpu_ref ref; +struct completion completion; +}; +/* A virtio-fs device instance */ struct virtio_fs {struct list_head list;/* on virtio_fs_instances */
@@ -36,6 +54,7 @@ struct virtio_fs {/* DAX memory window where file contents are mapped */ void *window_kaddr;phys_addr_t window_phys_addr; +size_t window_len; };static inline struct virtio_fs_vq *vq_to_fsvq (struct virtqueue *vq)
@@ -395,6 +414,127 @@ static const struct dax_operations virtio_fs_dax_ops = { .copy_to_iter = virtio_fs_copy_to_iter, }; +static void virtio_fs_percpu_release (struct percpu_ref *ref) +{ +struct virtio_fs_memremap_info *mi = + container_of(ref, struct virtio_fs_memremap_info, ref); + +complete (&mi->completion); +} + +static void virtio_fs_percpu_exit (void *data) +{ +struct virtio_fs_memremap_info *mi = data; + +wait_for_completion (&mi->completion); +percpu_ref_exit (&mi->ref); +} + +static void virtio_fs_percpu_kill (void *data) +{ +percpu_ref_kill (data); +} + +static void virtio_fs_cleanup_dax (void *data) +{ +struct virtio_fs *fs = data; + +kill_dax (fs->dax_dev); +put_dax (fs->dax_dev); +} + +static int virtio_fs_setup_dax (struct virtio_device *vdev,struct virtio_fs *fs) +{ +struct virtio_fs_memremap_info *mi; +struct dev_pagemap *pgmap; +struct pci_dev *pci_dev; +phys_addr_t phys_addr; +size_t len; +int ret; + +if (!IS_ENABLED (CONFIG_DAX_DRIVER )) +return 0 ; + +/* HACK implement VIRTIO shared memory regions instead of +* directly accessing the PCI BAR from a virtio device driver. +*/ + pci_dev = container_of(vdev->dev.parent, struct pci_dev, dev); + +/* TODO Is this safe - the virtio_pci_* driver doesn't use managed +* device APIs? */ + ret =pcim_enable_device (pci_dev); +if (ret <0 ) +return ret; + +/* TODO handle case where device doesn't expose BAR? */ + ret =pci_request_region (pci_dev,VIRTIO_FS_WINDOW_BAR , +"virtio-fs-window" ); +if (ret <0 ) { +dev_err (&vdev->dev,"%s: failed to request window BAR\n" , + __func__); +return ret; + } + + phys_addr =pci_resource_start (pci_dev,VIRTIO_FS_WINDOW_BAR ); + len =pci_resource_len (pci_dev,VIRTIO_FS_WINDOW_BAR ); + + mi =devm_kzalloc (&pci_dev->dev,sizeof (*mi),GFP_KERNEL ); +if (!mi) +return -ENOMEM ; + +init_completion (&mi->completion); + ret =percpu_ref_init (&mi->ref, virtio_fs_percpu_release,0 , +GFP_KERNEL ); +if (ret <0 ) { +dev_err (&vdev->dev,"%s: percpu_ref_init failed (%d)\n" , + __func__, ret); +return ret; + } + + ret =devm_add_action (&pci_dev->dev, virtio_fs_percpu_exit, mi); +if (ret <0 ) { +percpu_ref_exit (&mi->ref); +return ret; + } + + pgmap = &mi->pgmap; + pgmap->altmap_valid = false; + pgmap->ref = &mi->ref; + 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, + .end = phys_addr + len, + }; + + fs->window_kaddr =devm_memremap_pages (&pci_dev->dev, pgmap); +if (IS_ERR (fs->window_kaddr)) +return PTR_ERR (fs->window_kaddr); + + ret =devm_add_action_or_reset (&pci_dev->dev, virtio_fs_percpu_kill, + &mi->ref); +if (ret <0 ) +return ret; + + fs->window_phys_addr = phys_addr; + fs->window_len = len; + +dev_dbg (&vdev->dev,"%s: window kaddr 0x%px phys_addr 0x%llx len %zu\n" , + __func__, fs->window_kaddr, phys_addr, len); + + fs->dax_dev =alloc_dax (fs,NULL , &virtio_fs_dax_ops); +if (!fs->dax_dev) +return -ENOMEM ; + +return devm_add_action_or_reset (&vdev->dev, virtio_fs_cleanup_dax, fs); +} +static int virtio_fs_probe (struct virtio_device *vdev) {struct virtio_fs *fs;
@@ -416,16 +556,9 @@ static int virtio_fs_probe(struct virtio_device *vdev)/* TODO vq affinity */ /* TODO populate notifications vq */ -if (IS_ENABLED (CONFIG_DAX_DRIVER )) { -/* TODO map window */ - fs->window_kaddr =NULL ; - fs->window_phys_addr =0 ; - - fs->dax_dev =alloc_dax (fs,NULL , &virtio_fs_dax_ops); -if (!fs->dax_dev) - goto out_vqs;/* TODO handle case where device doesn't expose -BAR */ - } + 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.
@@ -441,13 +574,6 @@ static int virtio_fs_probe(struct virtio_device *vdev) out_vqs: vdev->config->reset (vdev);virtio_fs_cleanup_vqs (vdev, fs); - -if (fs->dax_dev) { -kill_dax (fs->dax_dev); -put_dax (fs->dax_dev); - fs->dax_dev =NULL ; - } - out: vdev->priv =NULL ;return ret;
@@ -466,12 +592,6 @@ static void virtio_fs_remove(struct virtio_device *vdev)list_del (&fs->list);mutex_unlock (&virtio_fs_mutex); -if (fs->dax_dev) { -kill_dax (fs->dax_dev); -put_dax (fs->dax_dev); - fs->dax_dev =NULL ; - } - vdev->priv =NULL ; }
Signature · 1 line
2.13.6
Re: [PATCH 15/52] fuse: map virtio_fs DAX window BAR
Quoted 23 lines · Vivek Goyal
From: Stefan Hajnoczi <[email protected]>
fs/fuse/virtio_fs.c +21
-0+static int virtio_fs_setup_dax (struct virtio_device *vdev,struct virtio_fs *fs) +{ +struct virtio_fs_memremap_info *mi; +struct dev_pagemap *pgmap; +struct pci_dev *pci_dev; +phys_addr_t phys_addr; +size_t len; +int ret; + +if (!IS_ENABLED (CONFIG_DAX_DRIVER )) +return 0 ; + +/* HACK implement VIRTIO shared memory regions instead of +* directly accessing the PCI BAR from a virtio device driver. +*/ + pci_dev = container_of(vdev->dev.parent, struct pci_dev, dev); + +/* TODO Is this safe - the virtio_pci_* driver doesn't use managed +* device APIs? */ + ret =pcim_enable_device (pci_dev); +if (ret <0 )
fs/fuse/virtio_fs.c static const struct dax_operations virtio_fs_dax_ops = { · line 476
Quoted 10 lines
fs/fuse/virtio_fs.c +10
-0+return ret; + +/* TODO handle case where device doesn't expose BAR? */ + ret =pci_request_region (pci_dev,VIRTIO_FS_WINDOW_BAR , +"virtio-fs-window" ); +if (ret <0 ) { +dev_err (&vdev->dev,"%s: failed to request window BAR\n" , + __func__); +return ret; + }
Can we please have a generic virtio interface to map the address (the default can then fall back to PCI) instead of mapping a PCI bar? This would make it easier to implement virtio-ccw or virtio-mmio.
Stefan Hajnoczi
maintainerRe: [PATCH 15/52] fuse: map virtio_fs DAX window BAR
Quoted 32 lines · Christian Borntraeger
Quoted 29 lines · Vivek Goyal
From: Stefan Hajnoczi <[email protected]>
fs/fuse/virtio_fs.c +27
-0+static int virtio_fs_setup_dax (struct virtio_device *vdev,struct virtio_fs *fs) +{ +struct virtio_fs_memremap_info *mi; +struct dev_pagemap *pgmap; +struct pci_dev *pci_dev; +phys_addr_t phys_addr; +size_t len; +int ret; + +if (!IS_ENABLED (CONFIG_DAX_DRIVER )) +return 0 ; + +/* HACK implement VIRTIO shared memory regions instead of +* directly accessing the PCI BAR from a virtio device driver. +*/ + pci_dev = container_of(vdev->dev.parent, struct pci_dev, dev); + +/* TODO Is this safe - the virtio_pci_* driver doesn't use managed +* device APIs? */ + ret =pcim_enable_device (pci_dev); +if (ret <0 ) +return ret; + +/* TODO handle case where device doesn't expose BAR? */ + ret =pci_request_region (pci_dev,VIRTIO_FS_WINDOW_BAR , +"virtio-fs-window" ); +if (ret <0 ) {
Quoted 8 lines
fs/fuse/virtio_fs.c static const struct dax_operations virtio_fs_dax_ops = { · line 476
Quoted 4 lines
fs/fuse/virtio_fs.c +4
-0+dev_err (&vdev->dev,"%s: failed to request window BAR\n" , + __func__); +return ret; + }Can we please have a generic virtio interface to map the address (the default can then fall back to PCI) instead of mapping a PCI bar? This would make it easier to implement virtio-ccw or virtio-mmio.
Yes, we'll define shared memory as a device resource in the VIRTIO specification. It will become part of the device model, alongside virtqueues, configuration space, etc. That means devices can use shared memory without it being tied to PCI BARs explicitly.
But only the PCI transport will have a realization of shared memory resources in the beginning. We need to work together to add this feature to the ccw and mmio transports.
Stefan
kbuild test robot
build botRe: [PATCH 15/52] fuse: map virtio_fs DAX window BAR
In reply to Vivek GoyalI love your patch! Yet something to improve:
Hi Stefan,
I love your patch! Yet something to improve:
[auto build test ERROR on fuse/for-next]
[also build test ERROR on v4.20-rc6]
[cannot apply to next-20181213]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Vivek-Goyal/virtio-fs-shared-file-system-for-virtual-machines/20181211-103034 base: https://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git for-next config: sh-allmodconfig (attached as .config) compiler: sh4-linux-gnu-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
GCC_VERSION=7.2.0 make.cross ARCH=sh
All errors (new ones prefixed by >>):
fs/fuse/virtio_fs.c: In function 'virtio_fs_setup_dax':
>> fs/fuse/virtio_fs.c:465:8: error: implicit declaration of function 'pcim_enable_device'; did you mean 'pci_enable_device'? [-Werror=implicit-function-declaration]
ret = pcim_enable_device(pci_dev);
^~~~~~~~~~~~~~~~~~
pci_enable_device
>> fs/fuse/virtio_fs.c:470:8: error: implicit declaration of function 'pci_request_region'; did you mean 'pci_request_regions'? [-Werror=implicit-function-declaration]
ret = pci_request_region(pci_dev, VIRTIO_FS_WINDOW_BAR,
^~~~~~~~~~~~~~~~~~
pci_request_regions
In file included from include/linux/printk.h:336:0,
from include/linux/kernel.h:14,
from include/linux/list.h:9,
from include/linux/wait.h:7,
from include/linux/wait_bit.h:8,
from include/linux/fs.h:6,
from fs/fuse/virtio_fs.c:7:
fs/fuse/virtio_fs.c:528:22: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 6 has type 'phys_addr_t {aka unsigned int}' [-Wformat=]
dev_dbg(&vdev->dev, "%s: window kaddr 0x%px phys_addr 0x%llx len %zu\n",
^
include/linux/dynamic_debug.h:135:39: note: in definition of macro 'dynamic_dev_dbg'
__dynamic_dev_dbg(&descriptor, dev, fmt, \
^~~
include/linux/device.h:1463:23: note: in expansion of macro 'dev_fmt'
dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
^~~~~~~
fs/fuse/virtio_fs.c:528:2: note: in expansion of macro 'dev_dbg'
dev_dbg(&vdev->dev, "%s: window kaddr 0x%px phys_addr 0x%llx len %zu\n",
^~~~~~~ At top level:
fs/fuse/virtio_fs.c:604:12: warning: 'virtio_fs_restore' defined but not used [-Wunused-function]
static int virtio_fs_restore(struct virtio_device *vdev)
^~~~~~~~~~~~~~~~~
fs/fuse/virtio_fs.c:599:12: warning: 'virtio_fs_freeze' defined but not used [-Wunused-function]
static int virtio_fs_freeze(struct virtio_device *vdev)
^~~~~~~~~~~~~~~~
cc1: some warnings being treated as errors
vim +465 fs/fuse/virtio_fs.c
445
446 static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)
447 {
448 struct virtio_fs_memremap_info *mi;
449 struct dev_pagemap *pgmap;
450 struct pci_dev *pci_dev;
451 phys_addr_t phys_addr;
452 size_t len;
453 int ret;
454
455 if (!IS_ENABLED(CONFIG_DAX_DRIVER))
456 return 0;
457
458 /* HACK implement VIRTIO shared memory regions instead of
459 * directly accessing the PCI BAR from a virtio device driver.
460 */
461 pci_dev = container_of(vdev->dev.parent, struct pci_dev, dev);
462
463 /* TODO Is this safe - the virtio_pci_* driver doesn't use managed
464 * device APIs? */
> 465 ret = pcim_enable_device(pci_dev); 466 if (ret < 0) 467 return ret; 468 469 /* TODO handle case where device doesn't expose BAR? */ > 470 ret = pci_request_region(pci_dev, VIRTIO_FS_WINDOW_BAR,
471 "virtio-fs-window");
472 if (ret < 0) {
473 dev_err(&vdev->dev, "%s: failed to request window BAR\n",
474 __func__);
475 return ret;
476 }
477
478 phys_addr = pci_resource_start(pci_dev, VIRTIO_FS_WINDOW_BAR);
479 len = pci_resource_len(pci_dev, VIRTIO_FS_WINDOW_BAR);
480
481 mi = devm_kzalloc(&pci_dev->dev, sizeof(*mi), GFP_KERNEL);
482 if (!mi)
483 return -ENOMEM;
484
485 init_completion(&mi->completion);
486 ret = percpu_ref_init(&mi->ref, virtio_fs_percpu_release, 0,
487 GFP_KERNEL);
488 if (ret < 0) {
489 dev_err(&vdev->dev, "%s: percpu_ref_init failed (%d)\n",
490 __func__, ret);
491 return ret;
492 }
493
494 ret = devm_add_action(&pci_dev->dev, virtio_fs_percpu_exit, mi);
495 if (ret < 0) {
496 percpu_ref_exit(&mi->ref);
497 return ret;
498 }
499
500 pgmap = &mi->pgmap;
501 pgmap->altmap_valid = false;
502 pgmap->ref = &mi->ref;
503 pgmap->type = MEMORY_DEVICE_FS_DAX;
504
505 /* Ideally we would directly use the PCI BAR resource but
506 * devm_memremap_pages() wants its own copy in pgmap. So
507 * initialize a struct resource from scratch (only the start
508 * and end fields will be used).
509 */
510 pgmap->res = (struct resource){
511 .name = "virtio-fs dax window",
512 .start = phys_addr,
513 .end = phys_addr + len,
514 };
515
516 fs->window_kaddr = devm_memremap_pages(&pci_dev->dev, pgmap);
517 if (IS_ERR(fs->window_kaddr))
518 return PTR_ERR(fs->window_kaddr);
519
520 ret = devm_add_action_or_reset(&pci_dev->dev, virtio_fs_percpu_kill,
521 &mi->ref);
522 if (ret < 0)
523 return ret;
524
525 fs->window_phys_addr = phys_addr;
526 fs->window_len = len;
527
528 dev_dbg(&vdev->dev, "%s: window kaddr 0x%px phys_addr 0x%llx len %zu\n",
529 __func__, fs->window_kaddr, phys_addr, len);
530
531 fs->dax_dev = alloc_dax(fs, NULL, &virtio_fs_dax_ops);
532 if (!fs->dax_dev)
533 return -ENOMEM;
534
535 return devm_add_action_or_reset(&vdev->dev, virtio_fs_cleanup_dax, fs);
536 }
537
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Re: [PATCH 15/52] fuse: map virtio_fs DAX window BAR
In reply to Vivek GoyalQuoted 6 lines · Vivek Goyal
From: Stefan Hajnoczi <[email protected]>
Experimental QEMU code introduces an MMIO BAR for mapping portions of files in the virtio-fs device. Map this BAR so that FUSE DAX can access file contents from the host page cache.
FUSE DAX sounds terrifying, can you explain a bit more about what this is?
Quoted 3 lines
The DAX window is accessed by the
fs/dax.cinfrastructure and must have struct pages (at least on x86). Usedevm_memremap_pages()to map the DAX window PCI BAR and allocate struct page.
PCI BAR space is not cache coherent, what prevents these pages from being used in paths that would do:
object = page_address(pfn_to_page(virtio_fs_pfn));
...?
Re: [PATCH 15/52] fuse: map virtio_fs DAX window BAR
Quoted 9 lines · * Dan Williams ([email protected]) wrote:
Quoted 6 lines · Vivek Goyal
From: Stefan Hajnoczi <[email protected]>
Experimental QEMU code introduces an MMIO BAR for mapping portions of files in the virtio-fs device. Map this BAR so that FUSE DAX can access file contents from the host page cache.
FUSE DAX sounds terrifying, can you explain a bit more about what this is?
We've got a guest running in QEMU, it sees an emulated PCI device; that runs a FUSE protocol over virtio on that PCI device, but also has a trick where via commands sent over the virtio queue associated with that device, (fragments of) host files get mmap'd into the qemu virtual memory that corresponds to the kvm slot exposed to the guest for that bar.
The guest sees those chunks in that BAR, and thus you can read/write to the host file by directly writing into that BAR.
Quoted 5 lines
Quoted 3 lines
The DAX window is accessed by the
fs/dax.cinfrastructure and must have struct pages (at least on x86). Usedevm_memremap_pages()to map the DAX window PCI BAR and allocate struct page.PCI BAR space is not cache coherent,
Note that no real PCI infrastructure is involved - this is all emulated devices, backed by mmap'd files on the host qemu process.
Dave
Quoted 6 lines
what prevents these pages from
being used in paths that would do:object = page_address(pfn_to_page(virtio_fs_pfn));...?
--
Dr. David Alan Gilbert / [email protected] / Manchester, UK
Re: [PATCH 15/52] fuse: map virtio_fs DAX window BAR
Quoted 12 lines · Dr. David Alan Gilbert
Quoted 9 lines · * Dan Williams ([email protected]) wrote:
Quoted 6 lines · Vivek Goyal
From: Stefan Hajnoczi <[email protected]>
Experimental QEMU code introduces an MMIO BAR for mapping portions of files in the virtio-fs device. Map this BAR so that FUSE DAX can access file contents from the host page cache.
FUSE DAX sounds terrifying, can you explain a bit more about what this is?
Quoted 8 lines
We've got a guest running in QEMU, it sees an emulated PCI device; that runs a FUSE protocol over virtio on that PCI device, but also has a trick where via commands sent over the virtio queue associated with that device, (fragments of) host files get mmap'd into the qemu virtual memory that corresponds to the kvm slot exposed to the guest for that bar.
The guest sees those chunks in that BAR, and thus you can read/write to the host file by directly writing into that BAR.
Ok so it's all software emulated and there won't be hardware DMA initiated by the guest to that address? I.e. if the host file gets truncated / hole-punched the guest would just cause a refault and the filesystem could fill in the block, or the guest is expected to die if the fault to the truncated file range results in SIGBUS.
Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
The DAX window is accessed by the
fs/dax.cinfrastructure and must have struct pages (at least on x86). Usedevm_memremap_pages()to map the DAX window PCI BAR and allocate struct page.PCI BAR space is not cache coherent,
Note that no real PCI infrastructure is involved - this is all emulated devices, backed by mmap'd files on the host qemu process.
Ok, terror level decreased.
Vivek Goyal
maintainerRe: [PATCH 15/52] fuse: map virtio_fs DAX window BAR
Quoted 20 lines · Dan Williams
Quoted 18 lines · Dr. David Alan Gilbert
Quoted 9 lines · * Dan Williams ([email protected]) wrote:
Quoted 6 lines · Vivek Goyal
From: Stefan Hajnoczi <[email protected]>
Experimental QEMU code introduces an MMIO BAR for mapping portions of files in the virtio-fs device. Map this BAR so that FUSE DAX can access file contents from the host page cache.
FUSE DAX sounds terrifying, can you explain a bit more about what this is?
We've got a guest running in QEMU, it sees an emulated PCI device; that runs a FUSE protocol over virtio on that PCI device, but also has a trick where via commands sent over the virtio queue associated with that device, (fragments of) host files get mmap'd into the qemu virtual memory that corresponds to the kvm slot exposed to the guest for that bar.
Quoted 5 lines
Quoted 2 lines
The guest sees those chunks in that BAR, and thus you can read/write to the host file by directly writing into that BAR.
Ok so it's all software emulated and there won't be hardware DMA initiated by the guest to that address?
That's my understanding.
Quoted 3 lines
I.e. if the host file gets
truncated / hole-punched the guest would just cause a refault and the filesystem could fill in the block,
Right
Quoted 2 lines
or the guest is expected to die if
the fault to the truncated file range results in SIGBUS.
Are you referring to the case where a file page is mapped in qemu and another guest/process trucates that page and when qemu tries to access it it will get SIGBUS. Have not tried it, will give it a try. Not sure what happens when QEMU receives SIGBUS.
Having said that, this is not different from the case of one process mapping a file and another process truncating the file and first process getting SIGBUS, right?
Thanks
Vivek
Quoted 11 lines
Quoted 9 lines
Quoted 6 lines
Quoted 4 lines
The DAX window is accessed by the
fs/dax.cinfrastructure and must have struct pages (at least on x86). Usedevm_memremap_pages()to map the DAX window PCI BAR and allocate struct page.PCI BAR space is not cache coherent,
Note that no real PCI infrastructure is involved - this is all emulated devices, backed by mmap'd files on the host qemu process.
Ok, terror level decreased.
Vivek Goyal
maintainerRe: [PATCH 15/52] fuse: map virtio_fs DAX window BAR
Quoted 38 lines · Vivek Goyal
Quoted 20 lines · Dan Williams
Quoted 18 lines · Dr. David Alan Gilbert
Quoted 9 lines · * Dan Williams ([email protected]) wrote:
Quoted 6 lines · Vivek Goyal
From: Stefan Hajnoczi <[email protected]>
Experimental QEMU code introduces an MMIO BAR for mapping portions of files in the virtio-fs device. Map this BAR so that FUSE DAX can access file contents from the host page cache.
FUSE DAX sounds terrifying, can you explain a bit more about what this is?
We've got a guest running in QEMU, it sees an emulated PCI device; that runs a FUSE protocol over virtio on that PCI device, but also has a trick where via commands sent over the virtio queue associated with that device, (fragments of) host files get mmap'd into the qemu virtual memory that corresponds to the kvm slot exposed to the guest for that bar.
Quoted 5 lines
Quoted 2 lines
The guest sees those chunks in that BAR, and thus you can read/write to the host file by directly writing into that BAR.
Ok so it's all software emulated and there won't be hardware DMA initiated by the guest to that address?
That's my understanding.
Quoted 3 lines
I.e. if the host file gets
truncated / hole-punched the guest would just cause a refault and the filesystem could fill in the block,Right
Quoted 2 lines
or the guest is expected to die if
the fault to the truncated file range results in SIGBUS.
Quoted 8 lines
Are you referring to the case where a file page is mapped in qemu and another guest/process trucates that page and when qemu tries to access it it will get SIGBUS. Have not tried it, will give it a try. Not sure what happens when QEMU receives SIGBUS.
Having said that, this is not different from the case of one process mapping a file and another process truncating the file and first process getting SIGBUS, right?
Ok, tried this and guest process hangs.
Stefan, dgilbert, this reminds me that we have faced this issue during our testing and we decided that this will need some fixing in KVM. I even put this in as part of changelog of patch with subject "fuse: Take inode lock for dax inode truncation"
"Another problem is, if we setup a mapping in fuse_iomap_begin(), and
file gets truncated and dax read/write happens, KVM currently hangs. It tries to fault in a page which does not exist on host (file got truncated). It probably requries fixing in KVM."
Not sure what should happen though when qemu receives SIGBUS in this case.
Thanks
Vivek
Re: [PATCH 15/52] fuse: map virtio_fs DAX window BAR
Quoted 55 lines · * Vivek Goyal ([email protected]) wrote:
Quoted 38 lines · Vivek Goyal
Quoted 20 lines · Dan Williams
Quoted 18 lines · Dr. David Alan Gilbert
Quoted 9 lines · * Dan Williams ([email protected]) wrote:
Quoted 6 lines · Vivek Goyal
From: Stefan Hajnoczi <[email protected]>
Experimental QEMU code introduces an MMIO BAR for mapping portions of files in the virtio-fs device. Map this BAR so that FUSE DAX can access file contents from the host page cache.
FUSE DAX sounds terrifying, can you explain a bit more about what this is?
We've got a guest running in QEMU, it sees an emulated PCI device; that runs a FUSE protocol over virtio on that PCI device, but also has a trick where via commands sent over the virtio queue associated with that device, (fragments of) host files get mmap'd into the qemu virtual memory that corresponds to the kvm slot exposed to the guest for that bar.
Quoted 5 lines
Quoted 2 lines
The guest sees those chunks in that BAR, and thus you can read/write to the host file by directly writing into that BAR.
Ok so it's all software emulated and there won't be hardware DMA initiated by the guest to that address?
That's my understanding.
Quoted 3 lines
I.e. if the host file gets
truncated / hole-punched the guest would just cause a refault and the filesystem could fill in the block,Right
Quoted 2 lines
or the guest is expected to die if
the fault to the truncated file range results in SIGBUS.Quoted 8 lines
Are you referring to the case where a file page is mapped in qemu and another guest/process trucates that page and when qemu tries to access it it will get SIGBUS. Have not tried it, will give it a try. Not sure what happens when QEMU receives SIGBUS.
Having said that, this is not different from the case of one process mapping a file and another process truncating the file and first process getting SIGBUS, right?
Ok, tried this and guest process hangs.
Stefan, dgilbert, this reminds me that we have faced this issue during our testing and we decided that this will need some fixing in KVM. I even put this in as part of changelog of patch with subject "fuse: Take inode lock for dax inode truncation"
Quoted 7 lines
"Another problem is, if we setup a mapping in
fuse_iomap_begin(), and
file gets truncated and dax read/write happens, KVM currently hangs. It tries to fault in a page which does not exist on host (file got truncated). It probably requries fixing in KVM."Not sure what should happen though when qemu receives SIGBUS in this case.
Yes, and I noted it in the TODO in my qemu patch posting.
We need to figure out what we want the guest to see in this case and figure out how to make QEMU/kvm fix it up so that the guest doesn't see anything odd.
Dave
Quoted 2 lines
Thanks
Vivek
--
Dr. David Alan Gilbert / [email protected] / Manchester, UK
Vivek Goyal
maintainerPATCH 16/52 virtio-fs: Add VIRTIO_PCI_CAP_SHARED_MEMORY_CFG and utility to find them
In reply to Vivek GoyalFrom: "Dr. David Alan Gilbert" <[email protected]>
The shm cap defines a capability to allow 64bit size chunks at 64 bit size offsets into a bar. There can be multiple such chunks on any one device.
--- fs/fuse/virtio_fs.c | 69 +++++++++++++++++++++++++++++++++++++++++ include/uapi/linux/virtio_pci.h | 10 ++++++ 2 files changed, 79 insertions(+)
fs/fuse/virtio_fs.c +69 -0
@@ -11,6 +11,7 @@#include <linux/module.h> #include <linux/virtio.h> #include <linux/virtio_fs.h> +#include <uapi/linux/virtio_pci.h> #include "fuse_i.h" enum {
@@ -57,6 +58,74 @@ struct virtio_fs {size_t window_len; }; +/* TODO: This should be in a PCI file somewhere */ +static int virtio_pci_find_shm_cap (struct pci_dev *dev, +u8 required_id, +u8 *bar,u64 *offset,u64 *len) +{ +int pos; + +for (pos =pci_find_capability (dev,PCI_CAP_ID_VNDR ); + pos >0 ; + pos =pci_find_next_capability (dev, pos,PCI_CAP_ID_VNDR )) { +u8 type, cap_len, id; +u32 tmp32; +u64 res_offset, res_length; + +pci_read_config_byte (dev, pos + offsetof(struct virtio_pci_cap , + cfg_type), + &type); +if (type !=VIRTIO_PCI_CAP_SHARED_MEMORY_CFG ) +continue ; + +pci_read_config_byte (dev, pos + offsetof(struct virtio_pci_cap , + cap_len), + &cap_len); +if (cap_len !=sizeof (struct virtio_pci_shm_cap )) { +printk (KERN_ERR "%s: shm cap with bad size offset: %d size: %d\n" , + __func__, pos, cap_len); +continue ; + }; + +pci_read_config_byte (dev, pos + offsetof(struct virtio_pci_shm_cap , + id), + &id); +if (id != required_id) +continue ; + +/* Type, and ID match, looks good */ +pci_read_config_byte (dev, pos + offsetof(struct virtio_pci_cap , + bar), + bar); + +/* Read the lower 32bit of length and offset */ +pci_read_config_dword (dev, pos + offsetof(struct virtio_pci_cap , offset), + &tmp32); + res_offset = tmp32; +pci_read_config_dword (dev, pos + offsetof(struct virtio_pci_cap , length), + &tmp32); + res_length = tmp32; + +/* and now the top half */ +pci_read_config_dword (dev, + pos + offsetof(struct virtio_pci_shm_cap , + offset_hi), + &tmp32); + res_offset |= ((u64 )tmp32) <<32 ; +pci_read_config_dword (dev, + pos + offsetof(struct virtio_pci_shm_cap , + length_hi), + &tmp32); + res_length |= ((u64 )tmp32) <<32 ; + + *offset = res_offset; + *len = res_length; + +return pos; + } +return 0 ; +} +static inline struct virtio_fs_vq *vq_to_fsvq (struct virtqueue *vq) {struct virtio_fs *fs = vq->vdev->priv;
include/uapi/linux/virtio_pci.h +10 -0
@@ -113,6 +113,8 @@#define VIRTIO_PCI_CAP_DEVICE_CFG 4/* PCI configuration access */ #define VIRTIO_PCI_CAP_PCI_CFG 5 +/* Additional shared memory capability */ +#define VIRTIO_PCI_CAP_SHARED_MEMORY_CFG 8/* This is the PCI capability header: */ struct virtio_pci_cap {
@@ -163,6 +165,14 @@ struct virtio_pci_cfg_cap {__u8 pci_cfg_data[4 ];/* Data for BAR access. */ }; +/* Fields in VIRTIO_PCI_CAP_SHARED_MEMORY_CFG */ +struct virtio_pci_shm_cap { +struct virtio_pci_cap cap; +__le32 offset_hi;/* Most sig 32 bits of offset */ +__le32 length_hi;/* Most sig 32 bits of length */ +__u8 id;/* To distinguish shm chunks */ +}; +/* Macro versions of offsets for the Old Timers! */ #define VIRTIO_PCI_CAP_VNDR 0#define VIRTIO_PCI_CAP_NEXT 1
Signature · 1 line
2.13.6
kbuild test robot
build bot[PATCH] virtio-fs: fix semicolon.cocci warnings
fs/fuse/virtio_fs.c:88:17-18: Unneeded semicolon
From: kbuild test robot <[email protected]>
fs/fuse/virtio_fs.c:88:17-18: Unneeded semicolon
Remove unneeded semicolon.
Generated by: scripts/coccinelle/misc/semicolon.cocci
---
url: https://github.com/0day-ci/linux/commits/Vivek-Goyal/virtio-fs-shared-file-system-for-virtual-machines/20181211-103034 base: https://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git for-next
virtio_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/fuse/virtio_fs.c +1 -1
@@ -85,7 +85,7 @@ static int virtio_pci_find_shm_cap(strucprintk (KERN_ERR "%s: shm cap with bad size offset: %d size: %d\n" , __func__, pos, cap_len);continue ; - }; + } pci_read_config_byte(dev, pos + offsetof(struct virtio_pci_shm_cap, id),
kbuild test robot
build botRe: [PATCH 16/52] virtio-fs: Add VIRTIO_PCI_CAP_SHARED_MEMORY_CFG and utility to find them
In reply to Vivek GoyalThank you for the patch! Perhaps something to improve:
Hi David,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on fuse/for-next]
[also build test WARNING on v4.20-rc6]
[cannot apply to next-20181212]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Vivek-Goyal/virtio-fs-shared-file-system-for-virtual-machines/20181211-103034 base: https://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git for-next
coccinelle warnings: (new ones prefixed by >>)
>> fs/fuse/virtio_fs.c:88:17-18: Unneeded semicolon
Please review and possibly fold the followup patch.
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Vivek Goyal
maintainerPATCH 17/52 virtio-fs: Retrieve shm capabilities for cache
In reply to Vivek GoyalRetrieve the capabilities needed to find the cache.
--- fs/fuse/virtio_fs.c | 15 +++++++++++++++ include/uapi/linux/virtio_fs.h | 3 +++ 2 files changed, 18 insertions(+)
fs/fuse/virtio_fs.c +15 -0
@@ -520,6 +520,8 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)phys_addr_t phys_addr;size_t len;int ret; +u8 have_cache, cache_bar; +u64 cache_offset, cache_len;if (!IS_ENABLED (CONFIG_DAX_DRIVER ))return 0 ;
@@ -535,6 +537,19 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)if (ret <0 )return ret; + have_cache =virtio_pci_find_shm_cap (pci_dev, +VIRTIO_FS_PCI_SHMCAP_ID_CACHE , &cache_bar, + &cache_offset, &cache_len); + +if (!have_cache) { +dev_err (&vdev->dev,"%s: No cache capability\n" , + __func__); +return -ENXIO ; + }else { +dev_notice (&vdev->dev,"Cache bar: %d len: 0x%llx @ 0x%llx\n" , + cache_bar, cache_len, cache_offset); + } +/* TODO handle case where device doesn't expose BAR? */ ret =pci_request_region (pci_dev,VIRTIO_FS_WINDOW_BAR ,"virtio-fs-window" );
include/uapi/linux/virtio_fs.h +3 -0
@@ -38,4 +38,7 @@ struct virtio_fs_config {__u32 num_queues; } __attribute__((packed)); +/* For the id field in virtio_pci_shm_cap */ +#define VIRTIO_FS_PCI_SHMCAP_ID_CACHE 0 +#endif /* _UAPI_LINUX_VIRTIO_FS_H */
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 18/52 virtio-fs: Map cache using the values from the capabilities
In reply to Vivek GoyalInstead of assuming we had the fixed bar for the cache, use the value from the capabilities.
--- fs/fuse/virtio_fs.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-)
fs/fuse/virtio_fs.c +17 -15
@@ -14,11 +14,6 @@#include <uapi/linux/virtio_pci.h> #include "fuse_i.h" -enum { -/* PCI BAR number of the virtio-fs DAX window */ -VIRTIO_FS_WINDOW_BAR =2 , -}; -/* List of virtio-fs device instances and a lock for the list */ static DEFINE_MUTEX (virtio_fs_mutex);static LIST_HEAD (virtio_fs_instances);
@@ -518,7 +513,7 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)struct dev_pagemap *pgmap;struct pci_dev *pci_dev;phys_addr_t phys_addr; -size_t len; +size_t bar_len;int ret;u8 have_cache, cache_bar;u64 cache_offset, cache_len;
@@ -551,17 +546,13 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) }/* TODO handle case where device doesn't expose BAR? */ - ret =pci_request_region (pci_dev,VIRTIO_FS_WINDOW_BAR , -"virtio-fs-window" ); + ret =pci_request_region (pci_dev, cache_bar,"virtio-fs-window" );if (ret <0 ) {dev_err (&vdev->dev,"%s: failed to request window BAR\n" , __func__);return ret; } - phys_addr =pci_resource_start (pci_dev,VIRTIO_FS_WINDOW_BAR ); - len =pci_resource_len (pci_dev,VIRTIO_FS_WINDOW_BAR ); - mi =devm_kzalloc (&pci_dev->dev,sizeof (*mi),GFP_KERNEL );if (!mi)return -ENOMEM ;
@@ -586,6 +577,17 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) pgmap->ref = &mi->ref; pgmap->type =MEMORY_DEVICE_FS_DAX ; + phys_addr =pci_resource_start (pci_dev, cache_bar); + bar_len =pci_resource_len (pci_dev, cache_bar); + +if (cache_offset + cache_len > bar_len) { +dev_err (&vdev->dev, +"%s: cache bar shorter than cap offset+len\n" , + __func__); +return -EINVAL ; + } + phys_addr += cache_offset; +/* 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
@@ -594,7 +596,7 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) pgmap->res = (struct resource ){ .name ="virtio-fs dax window" , .start = phys_addr, - .end = phys_addr + len, + .end = phys_addr + cache_len, }; fs->window_kaddr =devm_memremap_pages (&pci_dev->dev, pgmap);
@@ -607,10 +609,10 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)return ret; fs->window_phys_addr = phys_addr; - fs->window_len = len; + fs->window_len = cache_len; -dev_dbg (&vdev->dev,"%s: window kaddr 0x%px phys_addr 0x%llx len %zu\n" , - __func__, fs->window_kaddr, phys_addr, len); +dev_dbg (&vdev->dev,"%s: cache kaddr 0x%px phys_addr 0x%llx len %llx\n" , + __func__, fs->window_kaddr, phys_addr, cache_len); fs->dax_dev =alloc_dax (fs,NULL , &virtio_fs_dax_ops);if (!fs->dax_dev)
Signature · 1 line
2.13.6
Re: [PATCH 18/52] virtio-fs: Map cache using the values from the capabilities
Quoted 34 lines · Vivek Goyal
Instead of assuming we had the fixed bar for the cache, use the value from the capabilities.
Signed-off-by Dr. David Alan Gilbert <[email protected]>--- fs/fuse/virtio_fs.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-)fs/fuse/virtio_fs.c +1
-6@@ -14,11 +14,6 @@#include <uapi/linux/virtio_pci.h> #include "fuse_i.h" -enum { -/* PCI BAR number of the virtio-fs DAX window */ -VIRTIO_FS_WINDOW_BAR =2 , -}; -/* List of virtio-fs device instances and a lock for the list */ static DEFINE_MUTEX (virtio_fs_mutex);static LIST_HEAD (virtio_fs_instances);@@ -518,7 +513,7 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)struct dev_pagemap *pgmap;struct pci_dev *pci_dev;phys_addr_t phys_addr; -size_t len; +size_t bar_len;int ret;u8 have_cache, cache_bar;u64 cache_offset, cache_len;
fs/fuse/virtio_fs.c static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) · line 548
Quoted 3 lines
fs/fuse/virtio_fs.c +0
-0@@ -551,17 +546,13 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) }/* TODO handle case where device doesn't expose BAR? */
For virtio-pmem we decided to not go via BARs as this would effectively make it only usable for virtio-pci implementers. Instead, we are going to export the applicable physical device region directly (e.g. phys_start, phys_size in virtio config), so it is decoupled from PCI details. Doing the same for virtio-fs would allow e.g. also virtio-ccw to make eventually use of this.
Quoted 57 lines
fs/fuse/virtio_fs.c +16
-9- ret =pci_request_region (pci_dev,VIRTIO_FS_WINDOW_BAR , -"virtio-fs-window" ); + ret =pci_request_region (pci_dev, cache_bar,"virtio-fs-window" );if (ret <0 ) {dev_err (&vdev->dev,"%s: failed to request window BAR\n" , __func__);return ret; } - phys_addr =pci_resource_start (pci_dev,VIRTIO_FS_WINDOW_BAR ); - len =pci_resource_len (pci_dev,VIRTIO_FS_WINDOW_BAR ); - mi =devm_kzalloc (&pci_dev->dev,sizeof (*mi),GFP_KERNEL );if (!mi)return -ENOMEM ;@@ -586,6 +577,17 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) pgmap->ref = &mi->ref; pgmap->type =MEMORY_DEVICE_FS_DAX ; + phys_addr =pci_resource_start (pci_dev, cache_bar); + bar_len =pci_resource_len (pci_dev, cache_bar); + +if (cache_offset + cache_len > bar_len) { +dev_err (&vdev->dev, +"%s: cache bar shorter than cap offset+len\n" , + __func__); +return -EINVAL ; + } + phys_addr += cache_offset; +/* 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 @@ -594,7 +596,7 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) pgmap->res = (struct resource ){ .name ="virtio-fs dax window" , .start = phys_addr, - .end = phys_addr + len, + .end = phys_addr + cache_len, }; fs->window_kaddr =devm_memremap_pages (&pci_dev->dev, pgmap);@@ -607,10 +609,10 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)return ret; fs->window_phys_addr = phys_addr; - fs->window_len = len; + fs->window_len = cache_len; -dev_dbg (&vdev->dev,"%s: window kaddr 0x%px phys_addr 0x%llx len %zu\n" , - __func__, fs->window_kaddr, phys_addr, len); +dev_dbg (&vdev->dev,"%s: cache kaddr 0x%px phys_addr 0x%llx len %llx\n" , + __func__, fs->window_kaddr, phys_addr, cache_len); fs->dax_dev =alloc_dax (fs,NULL , &virtio_fs_dax_ops);if (!fs->dax_dev)
Signature · 3 lines
Thanks, David / dhildenb
Re: [PATCH 18/52] virtio-fs: Map cache using the values from the capabilities
Quoted 37 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 36 lines · Vivek Goyal
Instead of assuming we had the fixed bar for the cache, use the value from the capabilities.
Signed-off-by Dr. David Alan Gilbert <[email protected]>--- fs/fuse/virtio_fs.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-)fs/fuse/virtio_fs.c +1
-6@@ -14,11 +14,6 @@#include <uapi/linux/virtio_pci.h> #include "fuse_i.h" -enum { -/* PCI BAR number of the virtio-fs DAX window */ -VIRTIO_FS_WINDOW_BAR =2 , -}; -/* List of virtio-fs device instances and a lock for the list */ static DEFINE_MUTEX (virtio_fs_mutex);static LIST_HEAD (virtio_fs_instances);@@ -518,7 +513,7 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)struct dev_pagemap *pgmap;struct pci_dev *pci_dev;phys_addr_t phys_addr; -size_t len; +size_t bar_len;int ret;u8 have_cache, cache_bar;u64 cache_offset, cache_len;@@ -551,17 +546,13 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) }
Quoted 8 lines
fs/fuse/virtio_fs.c static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) · line 548
Quoted 1 line
fs/fuse/virtio_fs.c +0
-0/* TODO handle case where device doesn't expose BAR? */ For virtio-pmem we decided to not go via BARs as this would effectively make it only usable for virtio-pci implementers. Instead, we are going to export the applicable physical device region directly (e.g.
phys_start,phys_sizein virtio config), so it is decoupled from PCI details. Doing the same for virtio-fs would allow e.g. also virtio-ccw to make eventually use of this.
That makes it a very odd looking PCI device; I can see that with virtio-pmem it makes some sense, given that it's job is to expose arbitrary chunks of memory.
Dave
Quoted 49 lines
Quoted 49 lines
fs/fuse/virtio_fs.c +14
-7- ret =pci_request_region (pci_dev,VIRTIO_FS_WINDOW_BAR , -"virtio-fs-window" ); + ret =pci_request_region (pci_dev, cache_bar,"virtio-fs-window" );if (ret <0 ) {dev_err (&vdev->dev,"%s: failed to request window BAR\n" , __func__);return ret; } - phys_addr =pci_resource_start (pci_dev,VIRTIO_FS_WINDOW_BAR ); - len =pci_resource_len (pci_dev,VIRTIO_FS_WINDOW_BAR ); - mi =devm_kzalloc (&pci_dev->dev,sizeof (*mi),GFP_KERNEL );if (!mi)return -ENOMEM ;@@ -586,6 +577,17 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) pgmap->ref = &mi->ref; pgmap->type =MEMORY_DEVICE_FS_DAX ; + phys_addr =pci_resource_start (pci_dev, cache_bar); + bar_len =pci_resource_len (pci_dev, cache_bar); + +if (cache_offset + cache_len > bar_len) { +dev_err (&vdev->dev, +"%s: cache bar shorter than cap offset+len\n" , + __func__); +return -EINVAL ; + } + phys_addr += cache_offset; +/* 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 @@ -594,7 +596,7 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) pgmap->res = (struct resource ){ .name ="virtio-fs dax window" , .start = phys_addr, - .end = phys_addr + len, + .end = phys_addr + cache_len, }; fs->window_kaddr =devm_memremap_pages (&pci_dev->dev, pgmap);@@ -607,10 +609,10 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)return ret; fs->window_phys_addr = phys_addr; - fs->window_len = len; + fs->window_len = cache_len;
Quoted 15 lines
Quoted 8 lines
fs/fuse/virtio_fs.c +2
-2-dev_dbg (&vdev->dev,"%s: window kaddr 0x%px phys_addr 0x%llx len %zu\n" , - __func__, fs->window_kaddr, phys_addr, len); +dev_dbg (&vdev->dev,"%s: cache kaddr 0x%px phys_addr 0x%llx len %llx\n" , + __func__, fs->window_kaddr, phys_addr, cache_len); fs->dax_dev =alloc_dax (fs,NULL , &virtio_fs_dax_ops);if (!fs->dax_dev)Signature · 3 lines
Thanks, David / dhildenb
--
Dr. David Alan Gilbert / [email protected] / Manchester, UK
Re: [PATCH 18/52] virtio-fs: Map cache using the values from the capabilities
Quoted 47 lines · Dr. David Alan Gilbert
Quoted 37 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 36 lines · Vivek Goyal
Instead of assuming we had the fixed bar for the cache, use the value from the capabilities.
Signed-off-by Dr. David Alan Gilbert <[email protected]>--- fs/fuse/virtio_fs.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-)fs/fuse/virtio_fs.c +1
-6@@ -14,11 +14,6 @@#include <uapi/linux/virtio_pci.h> #include "fuse_i.h" -enum { -/* PCI BAR number of the virtio-fs DAX window */ -VIRTIO_FS_WINDOW_BAR =2 , -}; -/* List of virtio-fs device instances and a lock for the list */ static DEFINE_MUTEX (virtio_fs_mutex);static LIST_HEAD (virtio_fs_instances);@@ -518,7 +513,7 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)struct dev_pagemap *pgmap;struct pci_dev *pci_dev;phys_addr_t phys_addr; -size_t len; +size_t bar_len;int ret;u8 have_cache, cache_bar;u64 cache_offset, cache_len;@@ -551,17 +546,13 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) }Quoted 8 lines
fs/fuse/virtio_fs.c static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) · line 548
Quoted 1 line
fs/fuse/virtio_fs.c +0
-0/* TODO handle case where device doesn't expose BAR? */ For virtio-pmem we decided to not go via BARs as this would effectively make it only usable for virtio-pci implementers. Instead, we are going to export the applicable physical device region directly (e.g.
phys_start,phys_sizein virtio config), so it is decoupled from PCI details. Doing the same for virtio-fs would allow e.g. also virtio-ccw to make eventually use of this.
Quoted 5 lines
That makes it a very odd looking PCI device; I can see that with virtio-pmem it makes some sense, given that it's job is to expose arbitrary chunks of memory.
Dave
Well, the fact that your are
- including <uapi/linux/virtio_pci.h>
- adding pci related code
in/to fs/fuse/virtio_fs.c
tells me that these properties might be better communicated on the virtio layer, not on the PCI layer.
Or do you really want to glue virtio-fs to virtio-pci for all eternity?
Signature · 3 lines
Thanks, David / dhildenb
Re: [PATCH 18/52] virtio-fs: Map cache using the values from the capabilities
Quoted 59 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 47 lines · Dr. David Alan Gilbert
Quoted 37 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 36 lines · Vivek Goyal
Instead of assuming we had the fixed bar for the cache, use the value from the capabilities.
Signed-off-by Dr. David Alan Gilbert <[email protected]>--- fs/fuse/virtio_fs.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-)fs/fuse/virtio_fs.c +1
-6@@ -14,11 +14,6 @@#include <uapi/linux/virtio_pci.h> #include "fuse_i.h" -enum { -/* PCI BAR number of the virtio-fs DAX window */ -VIRTIO_FS_WINDOW_BAR =2 , -}; -/* List of virtio-fs device instances and a lock for the list */ static DEFINE_MUTEX (virtio_fs_mutex);static LIST_HEAD (virtio_fs_instances);@@ -518,7 +513,7 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)struct dev_pagemap *pgmap;struct pci_dev *pci_dev;phys_addr_t phys_addr; -size_t len; +size_t bar_len;int ret;u8 have_cache, cache_bar;u64 cache_offset, cache_len;@@ -551,17 +546,13 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) }Quoted 8 lines
fs/fuse/virtio_fs.c static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) · line 548
Quoted 1 line
fs/fuse/virtio_fs.c +0
-0/* TODO handle case where device doesn't expose BAR? */ For virtio-pmem we decided to not go via BARs as this would effectively make it only usable for virtio-pci implementers. Instead, we are going to export the applicable physical device region directly (e.g.
phys_start,phys_sizein virtio config), so it is decoupled from PCI details. Doing the same for virtio-fs would allow e.g. also virtio-ccw to make eventually use of this.Quoted 5 lines
That makes it a very odd looking PCI device; I can see that with virtio-pmem it makes some sense, given that it's job is to expose arbitrary chunks of memory.
Dave
Well, the fact that your are
- including <
uapi/linux/virtio_pci.h>
- adding pci related code
Quoted 6 lines
in/to
fs/fuse/virtio_fs.ctells me that these properties might be better communicated on the virtio layer, not on the PCI layer.
Or do you really want to glue virtio-fs to virtio-pci for all eternity?
No, these need cleaning up; and the split within the bar
is probably going to change to be communicated via virtio layer rather than pci capabilities. However, I don't want to make our PCI device look odd, just to make portability to non-PCI devices - so it's right to make the split appropriately, but still to use PCI bars for what they were designed for.
Dave
Quoted 6 lines
Signature · 5 lines
-- Thanks, David / dhildenb
--
Dr. David Alan Gilbert / [email protected] / Manchester, UK
Re: [PATCH 18/52] virtio-fs: Map cache using the values from the capabilities
Quoted 67 lines · Dr. David Alan Gilbert
Quoted 59 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 47 lines · Dr. David Alan Gilbert
Quoted 37 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 36 lines · Vivek Goyal
Instead of assuming we had the fixed bar for the cache, use the value from the capabilities.
Signed-off-by Dr. David Alan Gilbert <[email protected]>--- fs/fuse/virtio_fs.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-)fs/fuse/virtio_fs.c +1
-6@@ -14,11 +14,6 @@#include <uapi/linux/virtio_pci.h> #include "fuse_i.h" -enum { -/* PCI BAR number of the virtio-fs DAX window */ -VIRTIO_FS_WINDOW_BAR =2 , -}; -/* List of virtio-fs device instances and a lock for the list */ static DEFINE_MUTEX (virtio_fs_mutex);static LIST_HEAD (virtio_fs_instances);@@ -518,7 +513,7 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)struct dev_pagemap *pgmap;struct pci_dev *pci_dev;phys_addr_t phys_addr; -size_t len; +size_t bar_len;int ret;u8 have_cache, cache_bar;u64 cache_offset, cache_len;@@ -551,17 +546,13 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) }Quoted 8 lines
fs/fuse/virtio_fs.c static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) · line 548
Quoted 1 line
fs/fuse/virtio_fs.c +0
-0/* TODO handle case where device doesn't expose BAR? */ For virtio-pmem we decided to not go via BARs as this would effectively make it only usable for virtio-pci implementers. Instead, we are going to export the applicable physical device region directly (e.g.
phys_start,phys_sizein virtio config), so it is decoupled from PCI details. Doing the same for virtio-fs would allow e.g. also virtio-ccw to make eventually use of this.Quoted 5 lines
That makes it a very odd looking PCI device; I can see that with virtio-pmem it makes some sense, given that it's job is to expose arbitrary chunks of memory.
Dave
Well, the fact that your are
- including <
uapi/linux/virtio_pci.h>
- adding pci related codeQuoted 6 lines
in/to
fs/fuse/virtio_fs.ctells me that these properties might be better communicated on the virtio layer, not on the PCI layer.
Or do you really want to glue virtio-fs to virtio-pci for all eternity?
Quoted 8 lines
No, these need cleaning up; and the split within the bar
is probably going to change to be communicated via virtio layer rather than pci capabilities. However, I don't want to make our PCI device look odd, just to make portability to non-PCI devices - so it's right to make the split appropriately, but still to use PCI bars for what they were designed for.Dave
Let's discuss after the cleanup. In general I am not convinced this is the right thing to do. Using virtio-pci for anything else than pure transport smells like bad design to me (well, I am no virtio expert after all ;) ). No matter what PCI bars were designed for. If we can't get the same running with e.g. virtio-ccw or virtio-whatever, it is broken by design (or an addon that is tightly glued to virtio-pci, if that is the general idea).
Signature · 3 lines
Thanks, David / dhildenb
Re: [PATCH 18/52] virtio-fs: Map cache using the values from the capabilities
Quoted 77 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 67 lines · Dr. David Alan Gilbert
Quoted 59 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 47 lines · Dr. David Alan Gilbert
Quoted 37 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 36 lines · Vivek Goyal
Instead of assuming we had the fixed bar for the cache, use the value from the capabilities.
Signed-off-by Dr. David Alan Gilbert <[email protected]>--- fs/fuse/virtio_fs.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-)fs/fuse/virtio_fs.c +1
-6@@ -14,11 +14,6 @@#include <uapi/linux/virtio_pci.h> #include "fuse_i.h" -enum { -/* PCI BAR number of the virtio-fs DAX window */ -VIRTIO_FS_WINDOW_BAR =2 , -}; -/* List of virtio-fs device instances and a lock for the list */ static DEFINE_MUTEX (virtio_fs_mutex);static LIST_HEAD (virtio_fs_instances);@@ -518,7 +513,7 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)struct dev_pagemap *pgmap;struct pci_dev *pci_dev;phys_addr_t phys_addr; -size_t len; +size_t bar_len;int ret;u8 have_cache, cache_bar;u64 cache_offset, cache_len;@@ -551,17 +546,13 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) }Quoted 8 lines
fs/fuse/virtio_fs.c static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) · line 548
Quoted 1 line
fs/fuse/virtio_fs.c +0
-0/* TODO handle case where device doesn't expose BAR? */ For virtio-pmem we decided to not go via BARs as this would effectively make it only usable for virtio-pci implementers. Instead, we are going to export the applicable physical device region directly (e.g.
phys_start,phys_sizein virtio config), so it is decoupled from PCI details. Doing the same for virtio-fs would allow e.g. also virtio-ccw to make eventually use of this.Quoted 5 lines
That makes it a very odd looking PCI device; I can see that with virtio-pmem it makes some sense, given that it's job is to expose arbitrary chunks of memory.
Dave
Well, the fact that your are
- including <
uapi/linux/virtio_pci.h>
- adding pci related codeQuoted 6 lines
in/to
fs/fuse/virtio_fs.ctells me that these properties might be better communicated on the virtio layer, not on the PCI layer.
Or do you really want to glue virtio-fs to virtio-pci for all eternity?
Quoted 8 lines
No, these need cleaning up; and the split within the bar
is probably going to change to be communicated via virtio layer rather than pci capabilities. However, I don't want to make our PCI device look odd, just to make portability to non-PCI devices - so it's right to make the split appropriately, but still to use PCI bars for what they were designed for.Dave
Quoted 7 lines
Let's discuss after the cleanup. In general I am not convinced this is the right thing to do. Using virtio-pci for anything else than pure transport smells like bad design to me (well, I am no virtio expert after all ;) ). No matter what PCI bars were designed for. If we can't get the same running with e.g. virtio-ccw or virtio-whatever, it is broken by design (or an addon that is tightly glued to virtio-pci, if that is the general idea).
I'm sure we can find alternatives for virtio-*, so I wouldn't expect it to be glued to virtio-pci.
Dave
Quoted 5 lines
Signature · 3 lines
Thanks, David / dhildenb
--
Dr. David Alan Gilbert / [email protected] / Manchester, UK
Re: [PATCH 18/52] virtio-fs: Map cache using the values from the capabilities
Quoted 86 lines · Dr. David Alan Gilbert
Quoted 77 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 67 lines · Dr. David Alan Gilbert
Quoted 59 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 47 lines · Dr. David Alan Gilbert
Quoted 37 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 36 lines · Vivek Goyal
Instead of assuming we had the fixed bar for the cache, use the value from the capabilities.
Signed-off-by Dr. David Alan Gilbert <[email protected]>--- fs/fuse/virtio_fs.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-)fs/fuse/virtio_fs.c +1
-6@@ -14,11 +14,6 @@#include <uapi/linux/virtio_pci.h> #include "fuse_i.h" -enum { -/* PCI BAR number of the virtio-fs DAX window */ -VIRTIO_FS_WINDOW_BAR =2 , -}; -/* List of virtio-fs device instances and a lock for the list */ static DEFINE_MUTEX (virtio_fs_mutex);static LIST_HEAD (virtio_fs_instances);@@ -518,7 +513,7 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)struct dev_pagemap *pgmap;struct pci_dev *pci_dev;phys_addr_t phys_addr; -size_t len; +size_t bar_len;int ret;u8 have_cache, cache_bar;u64 cache_offset, cache_len;@@ -551,17 +546,13 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) }Quoted 8 lines
fs/fuse/virtio_fs.c static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) · line 548
Quoted 1 line
fs/fuse/virtio_fs.c +0
-0/* TODO handle case where device doesn't expose BAR? */ For virtio-pmem we decided to not go via BARs as this would effectively make it only usable for virtio-pci implementers. Instead, we are going to export the applicable physical device region directly (e.g.
phys_start,phys_sizein virtio config), so it is decoupled from PCI details. Doing the same for virtio-fs would allow e.g. also virtio-ccw to make eventually use of this.Quoted 5 lines
That makes it a very odd looking PCI device; I can see that with virtio-pmem it makes some sense, given that it's job is to expose arbitrary chunks of memory.
Dave
Well, the fact that your are
- including <
uapi/linux/virtio_pci.h>
- adding pci related codeQuoted 6 lines
in/to
fs/fuse/virtio_fs.ctells me that these properties might be better communicated on the virtio layer, not on the PCI layer.
Or do you really want to glue virtio-fs to virtio-pci for all eternity?
Quoted 8 lines
No, these need cleaning up; and the split within the bar
is probably going to change to be communicated via virtio layer rather than pci capabilities. However, I don't want to make our PCI device look odd, just to make portability to non-PCI devices - so it's right to make the split appropriately, but still to use PCI bars for what they were designed for.Dave
Quoted 7 lines
Let's discuss after the cleanup. In general I am not convinced this is the right thing to do. Using virtio-pci for anything else than pure transport smells like bad design to me (well, I am no virtio expert after all ;) ). No matter what PCI bars were designed for. If we can't get the same running with e.g. virtio-ccw or virtio-whatever, it is broken by design (or an addon that is tightly glued to virtio-pci, if that is the general idea).
Quoted 4 lines
I'm sure we can find alternatives for virtio-*, so I wouldn't expect it to be glued to virtio-pci.
Dave
As s390x does not have the concept of memory mapped io (RAM is RAM, nothing else), this is not architectured. vitio-ccw can therefore not define anything similar like that. However, in virtual environments we can do whatever we want on top of the pure transport (e.g. on the virtio layer).
Conny can correct me if I am wrong.
Signature · 3 lines
Thanks, David / dhildenb
Re: [PATCH 18/52] virtio-fs: Map cache using the values from the capabilities
Quoted 92 lines · On Thu, 13 Dec 2018 13:24:31 +0100 David Hildenbrand <[email protected]> wrote:
Quoted 86 lines · Dr. David Alan Gilbert
Quoted 77 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 67 lines · Dr. David Alan Gilbert
Quoted 59 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 47 lines · Dr. David Alan Gilbert
Quoted 37 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 36 lines · Vivek Goyal
Instead of assuming we had the fixed bar for the cache, use the value from the capabilities.
Signed-off-by Dr. David Alan Gilbert <[email protected]>--- fs/fuse/virtio_fs.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-)fs/fuse/virtio_fs.c +1
-6@@ -14,11 +14,6 @@#include <uapi/linux/virtio_pci.h> #include "fuse_i.h" -enum { -/* PCI BAR number of the virtio-fs DAX window */ -VIRTIO_FS_WINDOW_BAR =2 , -}; -/* List of virtio-fs device instances and a lock for the list */ static DEFINE_MUTEX (virtio_fs_mutex);static LIST_HEAD (virtio_fs_instances);@@ -518,7 +513,7 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)struct dev_pagemap *pgmap;struct pci_dev *pci_dev;phys_addr_t phys_addr; -size_t len; +size_t bar_len;int ret;u8 have_cache, cache_bar;u64 cache_offset, cache_len;@@ -551,17 +546,13 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) }Quoted 8 lines
fs/fuse/virtio_fs.c static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) · line 548
Quoted 1 line
fs/fuse/virtio_fs.c +0
-0/* TODO handle case where device doesn't expose BAR? */ For virtio-pmem we decided to not go via BARs as this would effectively make it only usable for virtio-pci implementers. Instead, we are going to export the applicable physical device region directly (e.g.
phys_start,phys_sizein virtio config), so it is decoupled from PCI details. Doing the same for virtio-fs would allow e.g. also virtio-ccw to make eventually use of this.Quoted 5 lines
That makes it a very odd looking PCI device; I can see that with virtio-pmem it makes some sense, given that it's job is to expose arbitrary chunks of memory.
Dave
Well, the fact that your are
- including <
uapi/linux/virtio_pci.h>
- adding pci related codeQuoted 6 lines
in/to
fs/fuse/virtio_fs.ctells me that these properties might be better communicated on the virtio layer, not on the PCI layer.
Or do you really want to glue virtio-fs to virtio-pci for all eternity?
Quoted 8 lines
No, these need cleaning up; and the split within the bar
is probably going to change to be communicated via virtio layer rather than pci capabilities. However, I don't want to make our PCI device look odd, just to make portability to non-PCI devices - so it's right to make the split appropriately, but still to use PCI bars for what they were designed for.Dave
Quoted 7 lines
Let's discuss after the cleanup. In general I am not convinced this is the right thing to do. Using virtio-pci for anything else than pure transport smells like bad design to me (well, I am no virtio expert after all ;) ). No matter what PCI bars were designed for. If we can't get the same running with e.g. virtio-ccw or virtio-whatever, it is broken by design (or an addon that is tightly glued to virtio-pci, if that is the general idea).
Quoted 4 lines
I'm sure we can find alternatives for virtio-*, so I wouldn't expect it to be glued to virtio-pci.
Dave
Quoted 7 lines
As s390x does not have the concept of memory mapped io (RAM is RAM, nothing else), this is not architectured. vitio-ccw can therefore not define anything similar like that. However, in virtual environments we can do whatever we want on top of the pure transport (e.g. on the virtio layer).
Conny can correct me if I am wrong.
I don't think you're wrong, but I haven't read the code yet and I'm therefore not aware of the purpose of this BAR.
Generally, if there is a memory location shared between host and guest, we need a way to communicate its location, which will likely differ between transports. For ccw, I could imagine a new channel command dedicated to exchanging configuration information (similar to what exists today to communicate the locations of virtqueues), but I'd rather not go down this path.
Without reading the code/design further, can we use one of the following instead of a BAR:
- a virtqueue;
- something in config space?
That would be implementable by any virtio transport.
Stefan Hajnoczi
maintainerRe: [PATCH 18/52] virtio-fs: Map cache using the values from the capabilities
Quoted 113 lines · Cornelia Huck
Quoted 92 lines · On Thu, 13 Dec 2018 13:24:31 +0100 David Hildenbrand <[email protected]> wrote:
Quoted 86 lines · Dr. David Alan Gilbert
Quoted 77 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 67 lines · Dr. David Alan Gilbert
Quoted 59 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 47 lines · Dr. David Alan Gilbert
Quoted 37 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 36 lines · Vivek Goyal
Instead of assuming we had the fixed bar for the cache, use the value from the capabilities.
Signed-off-by Dr. David Alan Gilbert <[email protected]>--- fs/fuse/virtio_fs.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-)fs/fuse/virtio_fs.c +1
-6@@ -14,11 +14,6 @@#include <uapi/linux/virtio_pci.h> #include "fuse_i.h" -enum { -/* PCI BAR number of the virtio-fs DAX window */ -VIRTIO_FS_WINDOW_BAR =2 , -}; -/* List of virtio-fs device instances and a lock for the list */ static DEFINE_MUTEX (virtio_fs_mutex);static LIST_HEAD (virtio_fs_instances);@@ -518,7 +513,7 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)struct dev_pagemap *pgmap;struct pci_dev *pci_dev;phys_addr_t phys_addr; -size_t len; +size_t bar_len;int ret;u8 have_cache, cache_bar;u64 cache_offset, cache_len;@@ -551,17 +546,13 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) }Quoted 8 lines
fs/fuse/virtio_fs.c static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) · line 548
Quoted 1 line
fs/fuse/virtio_fs.c +0
-0/* TODO handle case where device doesn't expose BAR? */ For virtio-pmem we decided to not go via BARs as this would effectively make it only usable for virtio-pci implementers. Instead, we are going to export the applicable physical device region directly (e.g.
phys_start,phys_sizein virtio config), so it is decoupled from PCI details. Doing the same for virtio-fs would allow e.g. also virtio-ccw to make eventually use of this.Quoted 5 lines
That makes it a very odd looking PCI device; I can see that with virtio-pmem it makes some sense, given that it's job is to expose arbitrary chunks of memory.
Dave
Well, the fact that your are
- including <
uapi/linux/virtio_pci.h>
- adding pci related codeQuoted 6 lines
in/to
fs/fuse/virtio_fs.ctells me that these properties might be better communicated on the virtio layer, not on the PCI layer.
Or do you really want to glue virtio-fs to virtio-pci for all eternity?
Quoted 8 lines
No, these need cleaning up; and the split within the bar
is probably going to change to be communicated via virtio layer rather than pci capabilities. However, I don't want to make our PCI device look odd, just to make portability to non-PCI devices - so it's right to make the split appropriately, but still to use PCI bars for what they were designed for.Dave
Quoted 7 lines
Let's discuss after the cleanup. In general I am not convinced this is the right thing to do. Using virtio-pci for anything else than pure transport smells like bad design to me (well, I am no virtio expert after all ;) ). No matter what PCI bars were designed for. If we can't get the same running with e.g. virtio-ccw or virtio-whatever, it is broken by design (or an addon that is tightly glued to virtio-pci, if that is the general idea).
Quoted 4 lines
I'm sure we can find alternatives for virtio-*, so I wouldn't expect it to be glued to virtio-pci.
Dave
Quoted 7 lines
As s390x does not have the concept of memory mapped io (RAM is RAM, nothing else), this is not architectured. vitio-ccw can therefore not define anything similar like that. However, in virtual environments we can do whatever we want on top of the pure transport (e.g. on the virtio layer).
Conny can correct me if I am wrong.
I don't think you're wrong, but I haven't read the code yet and I'm therefore not aware of the purpose of this BAR.
Generally, if there is a memory location shared between host and guest, we need a way to communicate its location, which will likely differ between transports. For ccw, I could imagine a new channel command dedicated to exchanging configuration information (similar to what exists today to communicate the locations of virtqueues), but I'd rather not go down this path.
Quoted 5 lines
Without reading the code/design further, can we use one of the following instead of a BAR:
- a virtqueue;
- something in config space?
That would be implementable by any virtio transport.
The way I think about this is that we wish to extend the VIRTIO device model with the concept of shared memory. virtio-fs, virtio-gpu, and virtio-vhost-user all have requirements for shared memory.
This seems like a transport-level issue to me. PCI supports memory-mapped I/O and that's the right place to do it. If you try to put it into config space or the virtqueue, you'll end up with something that cannot be realized as a PCI device because it bypasses PCI bus address translation.
If CCW needs a side-channel, that's fine. But that side-channel is a CCW-specific mechanism and probably doesn't apply to all other transports.
Stefan
Re: [PATCH 18/52] virtio-fs: Map cache using the values from the capabilities
Quoted 130 lines · On Fri, 14 Dec 2018 13:44:34 +0000 Stefan Hajnoczi <[email protected]> wrote:
Quoted 113 lines · Cornelia Huck
Quoted 92 lines · On Thu, 13 Dec 2018 13:24:31 +0100 David Hildenbrand <[email protected]> wrote:
Quoted 86 lines · Dr. David Alan Gilbert
Quoted 77 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 67 lines · Dr. David Alan Gilbert
Quoted 59 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 47 lines · Dr. David Alan Gilbert
Quoted 37 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 36 lines · Vivek Goyal
Instead of assuming we had the fixed bar for the cache, use the value from the capabilities.
Signed-off-by Dr. David Alan Gilbert <[email protected]>--- fs/fuse/virtio_fs.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-)fs/fuse/virtio_fs.c +1
-6@@ -14,11 +14,6 @@#include <uapi/linux/virtio_pci.h> #include "fuse_i.h" -enum { -/* PCI BAR number of the virtio-fs DAX window */ -VIRTIO_FS_WINDOW_BAR =2 , -}; -/* List of virtio-fs device instances and a lock for the list */ static DEFINE_MUTEX (virtio_fs_mutex);static LIST_HEAD (virtio_fs_instances);@@ -518,7 +513,7 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)struct dev_pagemap *pgmap;struct pci_dev *pci_dev;phys_addr_t phys_addr; -size_t len; +size_t bar_len;int ret;u8 have_cache, cache_bar;u64 cache_offset, cache_len;@@ -551,17 +546,13 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) }Quoted 8 lines
fs/fuse/virtio_fs.c static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) · line 548
Quoted 1 line
fs/fuse/virtio_fs.c +0
-0/* TODO handle case where device doesn't expose BAR? */ For virtio-pmem we decided to not go via BARs as this would effectively make it only usable for virtio-pci implementers. Instead, we are going to export the applicable physical device region directly (e.g.
phys_start,phys_sizein virtio config), so it is decoupled from PCI details. Doing the same for virtio-fs would allow e.g. also virtio-ccw to make eventually use of this.Quoted 5 lines
That makes it a very odd looking PCI device; I can see that with virtio-pmem it makes some sense, given that it's job is to expose arbitrary chunks of memory.
Dave
Well, the fact that your are
- including <
uapi/linux/virtio_pci.h>
- adding pci related codeQuoted 6 lines
in/to
fs/fuse/virtio_fs.ctells me that these properties might be better communicated on the virtio layer, not on the PCI layer.
Or do you really want to glue virtio-fs to virtio-pci for all eternity?
Quoted 8 lines
No, these need cleaning up; and the split within the bar
is probably going to change to be communicated via virtio layer rather than pci capabilities. However, I don't want to make our PCI device look odd, just to make portability to non-PCI devices - so it's right to make the split appropriately, but still to use PCI bars for what they were designed for.Dave
Quoted 7 lines
Let's discuss after the cleanup. In general I am not convinced this is the right thing to do. Using virtio-pci for anything else than pure transport smells like bad design to me (well, I am no virtio expert after all ;) ). No matter what PCI bars were designed for. If we can't get the same running with e.g. virtio-ccw or virtio-whatever, it is broken by design (or an addon that is tightly glued to virtio-pci, if that is the general idea).
Quoted 4 lines
I'm sure we can find alternatives for virtio-*, so I wouldn't expect it to be glued to virtio-pci.
Dave
Quoted 7 lines
As s390x does not have the concept of memory mapped io (RAM is RAM, nothing else), this is not architectured. vitio-ccw can therefore not define anything similar like that. However, in virtual environments we can do whatever we want on top of the pure transport (e.g. on the virtio layer).
Conny can correct me if I am wrong.
I don't think you're wrong, but I haven't read the code yet and I'm therefore not aware of the purpose of this BAR.
Generally, if there is a memory location shared between host and guest, we need a way to communicate its location, which will likely differ between transports. For ccw, I could imagine a new channel command dedicated to exchanging configuration information (similar to what exists today to communicate the locations of virtqueues), but I'd rather not go down this path.
Quoted 5 lines
Without reading the code/design further, can we use one of the following instead of a BAR:
- a virtqueue;
- something in config space?
That would be implementable by any virtio transport.The way I think about this is that we wish to extend the VIRTIO device model with the concept of shared memory. virtio-fs, virtio-gpu, and virtio-vhost-user all have requirements for shared memory.
This seems like a transport-level issue to me. PCI supports memory-mapped I/O and that's the right place to do it. If you try to put it into config space or the virtqueue, you'll end up with something that cannot be realized as a PCI device because it bypasses PCI bus address translation.
Quoted 3 lines
If CCW needs a side-channel, that's fine. But that side-channel is a CCW-specific mechanism and probably doesn't apply to all other transports.
But virtio-gpu works with ccw right now (I haven't checked what it uses); can virtio-fs use an equivalent method?
If there's a more generic case to be made for extending virtio devices with a way to handle shared memory, a ccw for that would be fine. I just want to avoid adding new ccws for everything as the namespace is not infinite.
Re: [PATCH 18/52] virtio-fs: Map cache using the values from the capabilities
Quoted 137 lines · * Cornelia Huck ([email protected]) wrote:
Quoted 130 lines · On Fri, 14 Dec 2018 13:44:34 +0000 Stefan Hajnoczi <[email protected]> wrote:
Quoted 113 lines · Cornelia Huck
Quoted 92 lines · On Thu, 13 Dec 2018 13:24:31 +0100 David Hildenbrand <[email protected]> wrote:
Quoted 86 lines · Dr. David Alan Gilbert
Quoted 77 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 67 lines · Dr. David Alan Gilbert
Quoted 59 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 47 lines · Dr. David Alan Gilbert
Quoted 37 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 36 lines · Vivek Goyal
Instead of assuming we had the fixed bar for the cache, use the value from the capabilities.
Signed-off-by Dr. David Alan Gilbert <[email protected]>--- fs/fuse/virtio_fs.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-)fs/fuse/virtio_fs.c +1
-6@@ -14,11 +14,6 @@#include <uapi/linux/virtio_pci.h> #include "fuse_i.h" -enum { -/* PCI BAR number of the virtio-fs DAX window */ -VIRTIO_FS_WINDOW_BAR =2 , -}; -/* List of virtio-fs device instances and a lock for the list */ static DEFINE_MUTEX (virtio_fs_mutex);static LIST_HEAD (virtio_fs_instances);@@ -518,7 +513,7 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)struct dev_pagemap *pgmap;struct pci_dev *pci_dev;phys_addr_t phys_addr; -size_t len; +size_t bar_len;int ret;u8 have_cache, cache_bar;u64 cache_offset, cache_len;@@ -551,17 +546,13 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) }Quoted 8 lines
fs/fuse/virtio_fs.c static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) · line 548
Quoted 1 line
fs/fuse/virtio_fs.c +0
-0/* TODO handle case where device doesn't expose BAR? */ For virtio-pmem we decided to not go via BARs as this would effectively make it only usable for virtio-pci implementers. Instead, we are going to export the applicable physical device region directly (e.g.
phys_start,phys_sizein virtio config), so it is decoupled from PCI details. Doing the same for virtio-fs would allow e.g. also virtio-ccw to make eventually use of this.Quoted 5 lines
That makes it a very odd looking PCI device; I can see that with virtio-pmem it makes some sense, given that it's job is to expose arbitrary chunks of memory.
Dave
Well, the fact that your are
- including <
uapi/linux/virtio_pci.h>
- adding pci related codeQuoted 6 lines
in/to
fs/fuse/virtio_fs.ctells me that these properties might be better communicated on the virtio layer, not on the PCI layer.
Or do you really want to glue virtio-fs to virtio-pci for all eternity?
Quoted 8 lines
No, these need cleaning up; and the split within the bar
is probably going to change to be communicated via virtio layer rather than pci capabilities. However, I don't want to make our PCI device look odd, just to make portability to non-PCI devices - so it's right to make the split appropriately, but still to use PCI bars for what they were designed for.Dave
Quoted 7 lines
Let's discuss after the cleanup. In general I am not convinced this is the right thing to do. Using virtio-pci for anything else than pure transport smells like bad design to me (well, I am no virtio expert after all ;) ). No matter what PCI bars were designed for. If we can't get the same running with e.g. virtio-ccw or virtio-whatever, it is broken by design (or an addon that is tightly glued to virtio-pci, if that is the general idea).
Quoted 4 lines
I'm sure we can find alternatives for virtio-*, so I wouldn't expect it to be glued to virtio-pci.
Dave
Quoted 7 lines
As s390x does not have the concept of memory mapped io (RAM is RAM, nothing else), this is not architectured. vitio-ccw can therefore not define anything similar like that. However, in virtual environments we can do whatever we want on top of the pure transport (e.g. on the virtio layer).
Conny can correct me if I am wrong.
I don't think you're wrong, but I haven't read the code yet and I'm therefore not aware of the purpose of this BAR.
Generally, if there is a memory location shared between host and guest, we need a way to communicate its location, which will likely differ between transports. For ccw, I could imagine a new channel command dedicated to exchanging configuration information (similar to what exists today to communicate the locations of virtqueues), but I'd rather not go down this path.
Quoted 5 lines
Without reading the code/design further, can we use one of the following instead of a BAR:
- a virtqueue;
- something in config space?
That would be implementable by any virtio transport.The way I think about this is that we wish to extend the VIRTIO device model with the concept of shared memory. virtio-fs, virtio-gpu, and virtio-vhost-user all have requirements for shared memory.
This seems like a transport-level issue to me. PCI supports memory-mapped I/O and that's the right place to do it. If you try to put it into config space or the virtqueue, you'll end up with something that cannot be realized as a PCI device because it bypasses PCI bus address translation.
Quoted 3 lines
If CCW needs a side-channel, that's fine. But that side-channel is a CCW-specific mechanism and probably doesn't apply to all other transports.
Quoted 7 lines
But virtio-gpu works with ccw right now (I haven't checked what it uses); can virtio-fs use an equivalent method?
If there's a more generic case to be made for extending virtio devices with a way to handle shared memory, a ccw for that would be fine. I just want to avoid adding new ccws for everything as the namespace is not infinite.
In our case we've got somewhere between 0..3 ranges of memory, and I was specifying them as PCI capabilities; however Gerd's suggestion was that it would be better to just use 1 bar and then have something as part of virtio or the like to split them up.
If we do that, then we could have something of the form
(index, base, length)
for each of the regions, where in the PCI case 'index' means BAR and in CCW it means something else. (For mmio it's probably irrelevant and the base is probably a physical address).
Dave
--
Dr. David Alan Gilbert / [email protected] / Manchester, UK
Stefan Hajnoczi
maintainerRe: [PATCH 18/52] virtio-fs: Map cache using the values from the capabilities
In reply to Cornelia HuckQuoted 133 lines · Cornelia Huck
Quoted 130 lines · On Fri, 14 Dec 2018 13:44:34 +0000 Stefan Hajnoczi <[email protected]> wrote:
Quoted 113 lines · Cornelia Huck
Quoted 92 lines · On Thu, 13 Dec 2018 13:24:31 +0100 David Hildenbrand <[email protected]> wrote:
Quoted 86 lines · Dr. David Alan Gilbert
Quoted 77 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 67 lines · Dr. David Alan Gilbert
Quoted 59 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 47 lines · Dr. David Alan Gilbert
Quoted 37 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 36 lines · Vivek Goyal
Instead of assuming we had the fixed bar for the cache, use the value from the capabilities.
Signed-off-by Dr. David Alan Gilbert <[email protected]>--- fs/fuse/virtio_fs.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-)fs/fuse/virtio_fs.c +1
-6@@ -14,11 +14,6 @@#include <uapi/linux/virtio_pci.h> #include "fuse_i.h" -enum { -/* PCI BAR number of the virtio-fs DAX window */ -VIRTIO_FS_WINDOW_BAR =2 , -}; -/* List of virtio-fs device instances and a lock for the list */ static DEFINE_MUTEX (virtio_fs_mutex);static LIST_HEAD (virtio_fs_instances);@@ -518,7 +513,7 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)struct dev_pagemap *pgmap;struct pci_dev *pci_dev;phys_addr_t phys_addr; -size_t len; +size_t bar_len;int ret;u8 have_cache, cache_bar;u64 cache_offset, cache_len;@@ -551,17 +546,13 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) }Quoted 8 lines
fs/fuse/virtio_fs.c static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) · line 548
Quoted 1 line
fs/fuse/virtio_fs.c +0
-0/* TODO handle case where device doesn't expose BAR? */ For virtio-pmem we decided to not go via BARs as this would effectively make it only usable for virtio-pci implementers. Instead, we are going to export the applicable physical device region directly (e.g.
phys_start,phys_sizein virtio config), so it is decoupled from PCI details. Doing the same for virtio-fs would allow e.g. also virtio-ccw to make eventually use of this.Quoted 5 lines
That makes it a very odd looking PCI device; I can see that with virtio-pmem it makes some sense, given that it's job is to expose arbitrary chunks of memory.
Dave
Well, the fact that your are
- including <
uapi/linux/virtio_pci.h>
- adding pci related codeQuoted 6 lines
in/to
fs/fuse/virtio_fs.ctells me that these properties might be better communicated on the virtio layer, not on the PCI layer.
Or do you really want to glue virtio-fs to virtio-pci for all eternity?
Quoted 8 lines
No, these need cleaning up; and the split within the bar
is probably going to change to be communicated via virtio layer rather than pci capabilities. However, I don't want to make our PCI device look odd, just to make portability to non-PCI devices - so it's right to make the split appropriately, but still to use PCI bars for what they were designed for.Dave
Quoted 7 lines
Let's discuss after the cleanup. In general I am not convinced this is the right thing to do. Using virtio-pci for anything else than pure transport smells like bad design to me (well, I am no virtio expert after all ;) ). No matter what PCI bars were designed for. If we can't get the same running with e.g. virtio-ccw or virtio-whatever, it is broken by design (or an addon that is tightly glued to virtio-pci, if that is the general idea).
Quoted 4 lines
I'm sure we can find alternatives for virtio-*, so I wouldn't expect it to be glued to virtio-pci.
Dave
Quoted 7 lines
As s390x does not have the concept of memory mapped io (RAM is RAM, nothing else), this is not architectured. vitio-ccw can therefore not define anything similar like that. However, in virtual environments we can do whatever we want on top of the pure transport (e.g. on the virtio layer).
Conny can correct me if I am wrong.
I don't think you're wrong, but I haven't read the code yet and I'm therefore not aware of the purpose of this BAR.
Generally, if there is a memory location shared between host and guest, we need a way to communicate its location, which will likely differ between transports. For ccw, I could imagine a new channel command dedicated to exchanging configuration information (similar to what exists today to communicate the locations of virtqueues), but I'd rather not go down this path.
Quoted 5 lines
Without reading the code/design further, can we use one of the following instead of a BAR:
- a virtqueue;
- something in config space?
That would be implementable by any virtio transport.The way I think about this is that we wish to extend the VIRTIO device model with the concept of shared memory. virtio-fs, virtio-gpu, and virtio-vhost-user all have requirements for shared memory.
This seems like a transport-level issue to me. PCI supports memory-mapped I/O and that's the right place to do it. If you try to put it into config space or the virtqueue, you'll end up with something that cannot be realized as a PCI device because it bypasses PCI bus address translation.
Quoted 6 lines
Quoted 3 lines
If CCW needs a side-channel, that's fine. But that side-channel is a CCW-specific mechanism and probably doesn't apply to all other transports.
But virtio-gpu works with ccw right now (I haven't checked what it uses); can virtio-fs use an equivalent method?
virtio-gpu does not use shared memory yet but it needs to in the future.
Quoted 4 lines
If there's a more generic case to be made for extending virtio devices with a way to handle shared memory, a ccw for that would be fine. I just want to avoid adding new ccws for everything as the namespace is not infinite.
Yes, virtio-vhost-user needs it too. I think it makes sense for shared memory resources to be part of the VIRTIO device model.
Stefan
Re: [PATCH 18/52] virtio-fs: Map cache using the values from the capabilities
In reply to Stefan HajnocziQuoted 130 lines · Stefan Hajnoczi
Quoted 113 lines · Cornelia Huck
Quoted 92 lines · On Thu, 13 Dec 2018 13:24:31 +0100 David Hildenbrand <[email protected]> wrote:
Quoted 86 lines · Dr. David Alan Gilbert
Quoted 77 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 67 lines · Dr. David Alan Gilbert
Quoted 59 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 47 lines · Dr. David Alan Gilbert
Quoted 37 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 36 lines · Vivek Goyal
Instead of assuming we had the fixed bar for the cache, use the value from the capabilities.
Signed-off-by Dr. David Alan Gilbert <[email protected]>--- fs/fuse/virtio_fs.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-)fs/fuse/virtio_fs.c +1
-6@@ -14,11 +14,6 @@#include <uapi/linux/virtio_pci.h> #include "fuse_i.h" -enum { -/* PCI BAR number of the virtio-fs DAX window */ -VIRTIO_FS_WINDOW_BAR =2 , -}; -/* List of virtio-fs device instances and a lock for the list */ static DEFINE_MUTEX (virtio_fs_mutex);static LIST_HEAD (virtio_fs_instances);@@ -518,7 +513,7 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)struct dev_pagemap *pgmap;struct pci_dev *pci_dev;phys_addr_t phys_addr; -size_t len; +size_t bar_len;int ret;u8 have_cache, cache_bar;u64 cache_offset, cache_len;@@ -551,17 +546,13 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) }Quoted 8 lines
fs/fuse/virtio_fs.c static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) · line 548
Quoted 1 line
fs/fuse/virtio_fs.c +0
-0/* TODO handle case where device doesn't expose BAR? */ For virtio-pmem we decided to not go via BARs as this would effectively make it only usable for virtio-pci implementers. Instead, we are going to export the applicable physical device region directly (e.g.
phys_start,phys_sizein virtio config), so it is decoupled from PCI details. Doing the same for virtio-fs would allow e.g. also virtio-ccw to make eventually use of this.Quoted 5 lines
That makes it a very odd looking PCI device; I can see that with virtio-pmem it makes some sense, given that it's job is to expose arbitrary chunks of memory.
Dave
Well, the fact that your are
- including <
uapi/linux/virtio_pci.h>
- adding pci related codeQuoted 6 lines
in/to
fs/fuse/virtio_fs.ctells me that these properties might be better communicated on the virtio layer, not on the PCI layer.
Or do you really want to glue virtio-fs to virtio-pci for all eternity?
Quoted 8 lines
No, these need cleaning up; and the split within the bar
is probably going to change to be communicated via virtio layer rather than pci capabilities. However, I don't want to make our PCI device look odd, just to make portability to non-PCI devices - so it's right to make the split appropriately, but still to use PCI bars for what they were designed for.Dave
Quoted 7 lines
Let's discuss after the cleanup. In general I am not convinced this is the right thing to do. Using virtio-pci for anything else than pure transport smells like bad design to me (well, I am no virtio expert after all ;) ). No matter what PCI bars were designed for. If we can't get the same running with e.g. virtio-ccw or virtio-whatever, it is broken by design (or an addon that is tightly glued to virtio-pci, if that is the general idea).
Quoted 4 lines
I'm sure we can find alternatives for virtio-*, so I wouldn't expect it to be glued to virtio-pci.
Dave
Quoted 7 lines
As s390x does not have the concept of memory mapped io (RAM is RAM, nothing else), this is not architectured. vitio-ccw can therefore not define anything similar like that. However, in virtual environments we can do whatever we want on top of the pure transport (e.g. on the virtio layer).
Conny can correct me if I am wrong.
I don't think you're wrong, but I haven't read the code yet and I'm therefore not aware of the purpose of this BAR.
Generally, if there is a memory location shared between host and guest, we need a way to communicate its location, which will likely differ between transports. For ccw, I could imagine a new channel command dedicated to exchanging configuration information (similar to what exists today to communicate the locations of virtqueues), but I'd rather not go down this path.
Quoted 5 lines
Without reading the code/design further, can we use one of the following instead of a BAR:
- a virtqueue;
- something in config space?
That would be implementable by any virtio transport.The way I think about this is that we wish to extend the VIRTIO device model with the concept of shared memory. virtio-fs, virtio-gpu, and virtio-vhost-user all have requirements for shared memory.
This seems like a transport-level issue to me. PCI supports memory-mapped I/O and that's the right place to do it. If you try to put it into config space or the virtqueue, you'll end up with something that cannot be realized as a PCI device because it bypasses PCI bus address translation.
Quoted 6 lines
If CCW needs a side-channel, that's fine. But that side-channel is a CCW-specific mechanism and probably doesn't apply to all other transports.
Stefan
I think the problem is more fundamental. There is no iommu. Whatever shared region you want to indicate, you want it to be assigned a memory region in guest physical memory. Like a DIMM/NVDIMM. And this should be different to the concept of a BAR. Or am I missing something?
I am ok with using whatever other channel to transport such information. But I believe this is different to a typical BAR. (I wish I knew more about PCI internals ;) ).
I would also like to know how shared memory works as of now for e.g. virtio-gpu.
Signature · 3 lines
Thanks, David / dhildenb
Stefan Hajnoczi
maintainerRe: [PATCH 18/52] virtio-fs: Map cache using the values from the capabilities
Quoted 135 lines · David Hildenbrand
Quoted 134 lines · Stefan Hajnoczi
Quoted 113 lines · Cornelia Huck
Quoted 92 lines · On Thu, 13 Dec 2018 13:24:31 +0100 David Hildenbrand <[email protected]> wrote:
Quoted 86 lines · Dr. David Alan Gilbert
Quoted 77 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 67 lines · Dr. David Alan Gilbert
Quoted 59 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 47 lines · Dr. David Alan Gilbert
Quoted 37 lines · * David Hildenbrand ([email protected]) wrote:
Quoted 36 lines · Vivek Goyal
Instead of assuming we had the fixed bar for the cache, use the value from the capabilities.
Signed-off-by Dr. David Alan Gilbert <[email protected]>--- fs/fuse/virtio_fs.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-)fs/fuse/virtio_fs.c +1
-6@@ -14,11 +14,6 @@#include <uapi/linux/virtio_pci.h> #include "fuse_i.h" -enum { -/* PCI BAR number of the virtio-fs DAX window */ -VIRTIO_FS_WINDOW_BAR =2 , -}; -/* List of virtio-fs device instances and a lock for the list */ static DEFINE_MUTEX (virtio_fs_mutex);static LIST_HEAD (virtio_fs_instances);@@ -518,7 +513,7 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)struct dev_pagemap *pgmap;struct pci_dev *pci_dev;phys_addr_t phys_addr; -size_t len; +size_t bar_len;int ret;u8 have_cache, cache_bar;u64 cache_offset, cache_len;@@ -551,17 +546,13 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) }Quoted 8 lines
fs/fuse/virtio_fs.c static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) · line 548
Quoted 1 line
fs/fuse/virtio_fs.c +0
-0/* TODO handle case where device doesn't expose BAR? */ For virtio-pmem we decided to not go via BARs as this would effectively make it only usable for virtio-pci implementers. Instead, we are going to export the applicable physical device region directly (e.g.
phys_start,phys_sizein virtio config), so it is decoupled from PCI details. Doing the same for virtio-fs would allow e.g. also virtio-ccw to make eventually use of this.Quoted 5 lines
That makes it a very odd looking PCI device; I can see that with virtio-pmem it makes some sense, given that it's job is to expose arbitrary chunks of memory.
Dave
Well, the fact that your are
- including <
uapi/linux/virtio_pci.h>
- adding pci related codeQuoted 6 lines
in/to
fs/fuse/virtio_fs.ctells me that these properties might be better communicated on the virtio layer, not on the PCI layer.
Or do you really want to glue virtio-fs to virtio-pci for all eternity?
Quoted 8 lines
No, these need cleaning up; and the split within the bar
is probably going to change to be communicated via virtio layer rather than pci capabilities. However, I don't want to make our PCI device look odd, just to make portability to non-PCI devices - so it's right to make the split appropriately, but still to use PCI bars for what they were designed for.Dave
Quoted 7 lines
Let's discuss after the cleanup. In general I am not convinced this is the right thing to do. Using virtio-pci for anything else than pure transport smells like bad design to me (well, I am no virtio expert after all ;) ). No matter what PCI bars were designed for. If we can't get the same running with e.g. virtio-ccw or virtio-whatever, it is broken by design (or an addon that is tightly glued to virtio-pci, if that is the general idea).
Quoted 4 lines
I'm sure we can find alternatives for virtio-*, so I wouldn't expect it to be glued to virtio-pci.
Dave
Quoted 7 lines
As s390x does not have the concept of memory mapped io (RAM is RAM, nothing else), this is not architectured. vitio-ccw can therefore not define anything similar like that. However, in virtual environments we can do whatever we want on top of the pure transport (e.g. on the virtio layer).
Conny can correct me if I am wrong.
I don't think you're wrong, but I haven't read the code yet and I'm therefore not aware of the purpose of this BAR.
Generally, if there is a memory location shared between host and guest, we need a way to communicate its location, which will likely differ between transports. For ccw, I could imagine a new channel command dedicated to exchanging configuration information (similar to what exists today to communicate the locations of virtqueues), but I'd rather not go down this path.
Quoted 5 lines
Without reading the code/design further, can we use one of the following instead of a BAR:
- a virtqueue;
- something in config space?
That would be implementable by any virtio transport.The way I think about this is that we wish to extend the VIRTIO device model with the concept of shared memory. virtio-fs, virtio-gpu, and virtio-vhost-user all have requirements for shared memory.
This seems like a transport-level issue to me. PCI supports memory-mapped I/O and that's the right place to do it. If you try to put it into config space or the virtqueue, you'll end up with something that cannot be realized as a PCI device because it bypasses PCI bus address translation.
If CCW needs a side-channel, that's fine. But that side-channel is a CCW-specific mechanism and probably doesn't apply to all other transports.
Quoted 7 lines
Quoted 2 lines
Stefan
I think the problem is more fundamental. There is no iommu. Whatever shared region you want to indicate, you want it to be assigned a memory region in guest physical memory. Like a DIMM/NVDIMM. And this should be different to the concept of a BAR. Or am I missing something?
If you implement a physical virtio PCI adapter then there is bus addressing and an IOMMU and VIRTIO has support for that. I'm not sure I understand what you mean by "there is no iommu"?
Quoted 6 lines
I am ok with using whatever other channel to transport such information. But I believe this is different to a typical BAR. (I wish I knew more about PCI internals ;) ).
I would also like to know how shared memory works as of now for e.g. virtio-gpu.
virtio-gpu currently does not use shared memory, it needs it for future features.
Stefan
Re: [PATCH 18/52] virtio-fs: Map cache using the values from the capabilities
Quoted 47 lines · On Mon, 17 Dec 2018 14:56:38 +0000 Stefan Hajnoczi <[email protected]> wrote:
Quoted 46 lines · David Hildenbrand
Quoted 44 lines · Stefan Hajnoczi
Quoted 21 lines · Cornelia Huck
Quoted 7 lines · On Thu, 13 Dec 2018 13:24:31 +0100 David Hildenbrand <[email protected]> wrote:
As s390x does not have the concept of memory mapped io (RAM is RAM, nothing else), this is not architectured. vitio-ccw can therefore not define anything similar like that. However, in virtual environments we can do whatever we want on top of the pure transport (e.g. on the virtio layer).
Conny can correct me if I am wrong.
I don't think you're wrong, but I haven't read the code yet and I'm therefore not aware of the purpose of this BAR.
Generally, if there is a memory location shared between host and guest, we need a way to communicate its location, which will likely differ between transports. For ccw, I could imagine a new channel command dedicated to exchanging configuration information (similar to what exists today to communicate the locations of virtqueues), but I'd rather not go down this path.
Quoted 5 lines
Without reading the code/design further, can we use one of the following instead of a BAR:
- a virtqueue;
- something in config space?
That would be implementable by any virtio transport.The way I think about this is that we wish to extend the VIRTIO device model with the concept of shared memory. virtio-fs, virtio-gpu, and virtio-vhost-user all have requirements for shared memory.
This seems like a transport-level issue to me. PCI supports memory-mapped I/O and that's the right place to do it. If you try to put it into config space or the virtqueue, you'll end up with something that cannot be realized as a PCI device because it bypasses PCI bus address translation.
If CCW needs a side-channel, that's fine. But that side-channel is a CCW-specific mechanism and probably doesn't apply to all other transports.
Stefan
Quoted 8 lines
Quoted 4 lines
I think the problem is more fundamental. There is no iommu. Whatever shared region you want to indicate, you want it to be assigned a memory region in guest physical memory. Like a DIMM/NVDIMM. And this should be different to the concept of a BAR. Or am I missing something?
If you implement a physical virtio PCI adapter then there is bus addressing and an IOMMU and VIRTIO has support for that. I'm not sure I understand what you mean by "there is no iommu"?
For ccw, there is no iommu; channel-program translation is doing similar things. (I hope that is what David meant :)
Quoted 10 lines
Quoted 7 lines
I am ok with using whatever other channel to transport such information. But I believe this is different to a typical BAR. (I wish I knew more about PCI internals ;) ).
I would also like to know how shared memory works as of now for e.g. virtio-gpu.
virtio-gpu currently does not use shared memory, it needs it for future features.
OK, that all sounds like we need to define a generic, per transport, device agnostic way to specify shared memory.
Where is that memory situated? Is it something in guest memory (like virtqueues)? If it is something provided by the device, things will get tricky for ccw (remember that there's no mmio on s390; pci on s390 uses special instructions for that.)
Re: [PATCH 18/52] virtio-fs: Map cache using the values from the capabilities
Quoted 73 lines · Cornelia Huck
Quoted 47 lines · On Mon, 17 Dec 2018 14:56:38 +0000 Stefan Hajnoczi <[email protected]> wrote:
Quoted 46 lines · David Hildenbrand
Quoted 44 lines · Stefan Hajnoczi
Quoted 21 lines · Cornelia Huck
Quoted 7 lines · On Thu, 13 Dec 2018 13:24:31 +0100 David Hildenbrand <[email protected]> wrote:
As s390x does not have the concept of memory mapped io (RAM is RAM, nothing else), this is not architectured. vitio-ccw can therefore not define anything similar like that. However, in virtual environments we can do whatever we want on top of the pure transport (e.g. on the virtio layer).
Conny can correct me if I am wrong.
I don't think you're wrong, but I haven't read the code yet and I'm therefore not aware of the purpose of this BAR.
Generally, if there is a memory location shared between host and guest, we need a way to communicate its location, which will likely differ between transports. For ccw, I could imagine a new channel command dedicated to exchanging configuration information (similar to what exists today to communicate the locations of virtqueues), but I'd rather not go down this path.
Quoted 5 lines
Without reading the code/design further, can we use one of the following instead of a BAR:
- a virtqueue;
- something in config space?
That would be implementable by any virtio transport.The way I think about this is that we wish to extend the VIRTIO device model with the concept of shared memory. virtio-fs, virtio-gpu, and virtio-vhost-user all have requirements for shared memory.
This seems like a transport-level issue to me. PCI supports memory-mapped I/O and that's the right place to do it. If you try to put it into config space or the virtqueue, you'll end up with something that cannot be realized as a PCI device because it bypasses PCI bus address translation.
If CCW needs a side-channel, that's fine. But that side-channel is a CCW-specific mechanism and probably doesn't apply to all other transports.
Stefan
Quoted 8 lines
Quoted 4 lines
I think the problem is more fundamental. There is no iommu. Whatever shared region you want to indicate, you want it to be assigned a memory region in guest physical memory. Like a DIMM/NVDIMM. And this should be different to the concept of a BAR. Or am I missing something?
If you implement a physical virtio PCI adapter then there is bus addressing and an IOMMU and VIRTIO has support for that. I'm not sure I understand what you mean by "there is no iommu"?
For ccw, there is no iommu; channel-program translation is doing similar things. (I hope that is what David meant :)
Quoted 10 lines
Quoted 7 lines
I am ok with using whatever other channel to transport such information. But I believe this is different to a typical BAR. (I wish I knew more about PCI internals ;) ).
I would also like to know how shared memory works as of now for e.g. virtio-gpu.
virtio-gpu currently does not use shared memory, it needs it for future features.
Quoted 8 lines
OK, that all sounds like we need to define a generic, per transport, device agnostic way to specify shared memory.
Where is that memory situated? Is it something in guest memory (like virtqueues)? If it is something provided by the device, things will get tricky for ccw (remember that there's no mmio on s390; pci on s390 uses special instructions for that.)
I am just very very confused right now. What I am struggling with right now (Stefan, hope you can clarify it for me):
We need some place where this shared memory is located in the guest physical memory. On x86 - if I am not wrong - this BAR is placed into the reserved memory area between 3 and 4 GB. There is no such thing on s390x. Because we don't have IO via memory (yet). All we have is one or two KVM memory slots filled with all memory.
So what we will need on s390x is on the QEMU side such a reserved memory region where devices like virtio-fs can reserve a region for shared memory.
So it is something like a dimm/nvdimm except that it is smaller and not visible to the user directly (via memory backends).
Signature · 3 lines
Thanks, David / dhildenb
Stefan Hajnoczi
maintainerRe: [PATCH 18/52] virtio-fs: Map cache using the values from the capabilities
Quoted 83 lines · David Hildenbrand
Quoted 73 lines · Cornelia Huck
Quoted 47 lines · On Mon, 17 Dec 2018 14:56:38 +0000 Stefan Hajnoczi <[email protected]> wrote:
Quoted 46 lines · David Hildenbrand
Quoted 44 lines · Stefan Hajnoczi
Quoted 21 lines · Cornelia Huck
Quoted 7 lines · On Thu, 13 Dec 2018 13:24:31 +0100 David Hildenbrand <[email protected]> wrote:
As s390x does not have the concept of memory mapped io (RAM is RAM, nothing else), this is not architectured. vitio-ccw can therefore not define anything similar like that. However, in virtual environments we can do whatever we want on top of the pure transport (e.g. on the virtio layer).
Conny can correct me if I am wrong.
I don't think you're wrong, but I haven't read the code yet and I'm therefore not aware of the purpose of this BAR.
Generally, if there is a memory location shared between host and guest, we need a way to communicate its location, which will likely differ between transports. For ccw, I could imagine a new channel command dedicated to exchanging configuration information (similar to what exists today to communicate the locations of virtqueues), but I'd rather not go down this path.
Quoted 5 lines
Without reading the code/design further, can we use one of the following instead of a BAR:
- a virtqueue;
- something in config space?
That would be implementable by any virtio transport.The way I think about this is that we wish to extend the VIRTIO device model with the concept of shared memory. virtio-fs, virtio-gpu, and virtio-vhost-user all have requirements for shared memory.
This seems like a transport-level issue to me. PCI supports memory-mapped I/O and that's the right place to do it. If you try to put it into config space or the virtqueue, you'll end up with something that cannot be realized as a PCI device because it bypasses PCI bus address translation.
If CCW needs a side-channel, that's fine. But that side-channel is a CCW-specific mechanism and probably doesn't apply to all other transports.
Stefan
Quoted 8 lines
Quoted 4 lines
I think the problem is more fundamental. There is no iommu. Whatever shared region you want to indicate, you want it to be assigned a memory region in guest physical memory. Like a DIMM/NVDIMM. And this should be different to the concept of a BAR. Or am I missing something?
If you implement a physical virtio PCI adapter then there is bus addressing and an IOMMU and VIRTIO has support for that. I'm not sure I understand what you mean by "there is no iommu"?
For ccw, there is no iommu; channel-program translation is doing similar things. (I hope that is what David meant :)
Quoted 10 lines
Quoted 7 lines
I am ok with using whatever other channel to transport such information. But I believe this is different to a typical BAR. (I wish I knew more about PCI internals ;) ).
I would also like to know how shared memory works as of now for e.g. virtio-gpu.
virtio-gpu currently does not use shared memory, it needs it for future features.
Quoted 8 lines
OK, that all sounds like we need to define a generic, per transport, device agnostic way to specify shared memory.
Where is that memory situated? Is it something in guest memory (like virtqueues)? If it is something provided by the device, things will get tricky for ccw (remember that there's no mmio on s390; pci on s390 uses special instructions for that.)
Quoted 6 lines
I am just very very confused right now. What I am struggling with right now (Stefan, hope you can clarify it for me):
We need some place where this shared memory is located in the guest physical memory. On x86 - if I am not wrong - this BAR is placed into the reserved memory area between 3 and 4 GB.
Right, the shared memory is provided by the device and does not live in guest RAM.
Quoted 9 lines
There is no such thing on
s390x. Because we don't have IO via memory (yet). All we have is one or two KVM memory slots filled with all memory.So what we will need on s390x is on the QEMU side such a reserved memory region where devices like virtio-fs can reserve a region for shared memory.
So it is something like a dimm/nvdimm except that it is smaller and not visible to the user directly (via memory backends).
I see. That makes sense.
Stefan
Re: [PATCH 18/52] virtio-fs: Map cache using the values from the capabilities
In reply to Vivek GoyalQuoted 72 lines · Vivek Goyal
Instead of assuming we had the fixed bar for the cache, use the value from the capabilities.
Signed-off-by Dr. David Alan Gilbert <[email protected]>--- fs/fuse/virtio_fs.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-)fs/fuse/virtio_fs.c +13
-11@@ -14,11 +14,6 @@#include <uapi/linux/virtio_pci.h> #include "fuse_i.h" -enum { -/* PCI BAR number of the virtio-fs DAX window */ -VIRTIO_FS_WINDOW_BAR =2 , -}; -/* List of virtio-fs device instances and a lock for the list */ static DEFINE_MUTEX (virtio_fs_mutex);static LIST_HEAD (virtio_fs_instances);@@ -518,7 +513,7 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)struct dev_pagemap *pgmap;struct pci_dev *pci_dev;phys_addr_t phys_addr; -size_t len; +size_t bar_len;int ret;u8 have_cache, cache_bar;u64 cache_offset, cache_len;@@ -551,17 +546,13 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) }/* TODO handle case where device doesn't expose BAR? */ - ret =pci_request_region (pci_dev,VIRTIO_FS_WINDOW_BAR , -"virtio-fs-window" ); + ret =pci_request_region (pci_dev, cache_bar,"virtio-fs-window" );if (ret <0 ) {dev_err (&vdev->dev,"%s: failed to request window BAR\n" , __func__);return ret; } - phys_addr =pci_resource_start (pci_dev,VIRTIO_FS_WINDOW_BAR ); - len =pci_resource_len (pci_dev,VIRTIO_FS_WINDOW_BAR ); - mi =devm_kzalloc (&pci_dev->dev,sizeof (*mi),GFP_KERNEL );if (!mi)return -ENOMEM ;@@ -586,6 +577,17 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) pgmap->ref = &mi->ref; pgmap->type =MEMORY_DEVICE_FS_DAX ; + phys_addr =pci_resource_start (pci_dev, cache_bar); + bar_len =pci_resource_len (pci_dev, cache_bar); + +if (cache_offset + cache_len > bar_len) { +dev_err (&vdev->dev, +"%s: cache bar shorter than cap offset+len\n" , + __func__); +return -EINVAL ; + } + phys_addr += cache_offset; +/* 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
fs/fuse/virtio_fs.c static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) · line 599
Quoted 5 lines
fs/fuse/virtio_fs.c +1
-1@@ -594,7 +596,7 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) pgmap->res = (struct resource ){ .name ="virtio-fs dax window" , .start = phys_addr, - .end = phys_addr + len, + .end = phys_addr + cache_len,
Just in case you haven't noticed/fixed this problem, it should be
+ .end = phys_addr + cache_len - 1,
because resource_size() counts %size as "end - start + 1". The end result of the above is a "conflicting page map" warning when specifying a second virtio-fs pci device.
I'll send a patch for this, and feel free to take it along with the patchset if needed.
thanks,
liubo
Quoted 20 lines
fs/fuse/virtio_fs.c +3
-4}; fs->window_kaddr =devm_memremap_pages (&pci_dev->dev, pgmap);@@ -607,10 +609,10 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)return ret; fs->window_phys_addr = phys_addr; - fs->window_len = len; + fs->window_len = cache_len; -dev_dbg (&vdev->dev,"%s: window kaddr 0x%px phys_addr 0x%llx len %zu\n" , - __func__, fs->window_kaddr, phys_addr, len); +dev_dbg (&vdev->dev,"%s: cache kaddr 0x%px phys_addr 0x%llx len %llx\n" , + __func__, fs->window_kaddr, phys_addr, cache_len); fs->dax_dev =alloc_dax (fs,NULL , &virtio_fs_dax_ops);if (!fs->dax_dev) --2.13.6
Re: [PATCH 18/52] virtio-fs: Map cache using the values from the capabilities
Quoted 81 lines · * Liu Bo ([email protected]) wrote:
Quoted 72 lines · Vivek Goyal
Instead of assuming we had the fixed bar for the cache, use the value from the capabilities.
Signed-off-by Dr. David Alan Gilbert <[email protected]>--- fs/fuse/virtio_fs.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-)fs/fuse/virtio_fs.c +13
-11@@ -14,11 +14,6 @@#include <uapi/linux/virtio_pci.h> #include "fuse_i.h" -enum { -/* PCI BAR number of the virtio-fs DAX window */ -VIRTIO_FS_WINDOW_BAR =2 , -}; -/* List of virtio-fs device instances and a lock for the list */ static DEFINE_MUTEX (virtio_fs_mutex);static LIST_HEAD (virtio_fs_instances);@@ -518,7 +513,7 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)struct dev_pagemap *pgmap;struct pci_dev *pci_dev;phys_addr_t phys_addr; -size_t len; +size_t bar_len;int ret;u8 have_cache, cache_bar;u64 cache_offset, cache_len;@@ -551,17 +546,13 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) }/* TODO handle case where device doesn't expose BAR? */ - ret =pci_request_region (pci_dev,VIRTIO_FS_WINDOW_BAR , -"virtio-fs-window" ); + ret =pci_request_region (pci_dev, cache_bar,"virtio-fs-window" );if (ret <0 ) {dev_err (&vdev->dev,"%s: failed to request window BAR\n" , __func__);return ret; } - phys_addr =pci_resource_start (pci_dev,VIRTIO_FS_WINDOW_BAR ); - len =pci_resource_len (pci_dev,VIRTIO_FS_WINDOW_BAR ); - mi =devm_kzalloc (&pci_dev->dev,sizeof (*mi),GFP_KERNEL );if (!mi)return -ENOMEM ;@@ -586,6 +577,17 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) pgmap->ref = &mi->ref; pgmap->type =MEMORY_DEVICE_FS_DAX ; + phys_addr =pci_resource_start (pci_dev, cache_bar); + bar_len =pci_resource_len (pci_dev, cache_bar); + +if (cache_offset + cache_len > bar_len) { +dev_err (&vdev->dev, +"%s: cache bar shorter than cap offset+len\n" , + __func__); +return -EINVAL ; + } + phys_addr += cache_offset; +/* 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 fs/fuse/virtio_fs.c static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) · line 599
Quoted 5 lines
fs/fuse/virtio_fs.c +1
-1@@ -594,7 +596,7 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) pgmap->res = (struct resource ){ .name ="virtio-fs dax window" , .start = phys_addr, - .end = phys_addr + len, + .end = phys_addr + cache_len,Just in case you haven't noticed/fixed this problem, it should be
Quoted 5 lines
+ .end =
phys_addr+cache_len- 1,because
resource_size()counts %size as "end - start + 1". The end result of the above is a "conflicting page map" warning when specifying a second virtio-fs pci device.
Thanks for spotting this! I think we'd seen that message once but not noticed where from.
Quoted 3 lines
I'll send a patch for this, and feel free to take it along with the patchset if needed.
Dave
Quoted 18 lines
thanks,
liuboQuoted 15 lines
fs/fuse/virtio_fs.c +3
-3}; fs->window_kaddr =devm_memremap_pages (&pci_dev->dev, pgmap);@@ -607,10 +609,10 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)return ret; fs->window_phys_addr = phys_addr; - fs->window_len = len; + fs->window_len = cache_len; -dev_dbg (&vdev->dev,"%s: window kaddr 0x%px phys_addr 0x%llx len %zu\n" , - __func__, fs->window_kaddr, phys_addr, len); +dev_dbg (&vdev->dev,"%s: cache kaddr 0x%px phys_addr 0x%llx len %llx\n" , + __func__, fs->window_kaddr, phys_addr, cache_len);
Quoted 5 lines
Quoted 5 lines
fs/fuse/virtio_fs.c +0
-1fs->dax_dev =alloc_dax (fs,NULL , &virtio_fs_dax_ops);if (!fs->dax_dev) --2.13.6
--
Dr. David Alan Gilbert / [email protected] / Manchester, UK
PATCH 1/1 virtio-fs: fix multiple tag support
In reply to Vivek GoyalWhile doing memremap from pci_dev's system bus address to kernel virtual address, we assign a wrong value to the %end of pgmap.res, which ends up with a wrong resource size in the process of memremap, and that further prevent the second virtiofs pci device from being probed successfully.
--- fs/fuse/virtio_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/fuse/virtio_fs.c +1 -1
@@ -713,7 +713,7 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) pgmap->res = (struct resource ){ .name ="virtio-fs dax window" , .start = phys_addr, - .end = phys_addr + cache_len, + .end = phys_addr + cache_len -1 , }; fs->window_kaddr =devm_memremap_pages (&pci_dev->dev, pgmap);
Signature · 1 line
2.20.1.2.gb21ebb6
Vivek Goyal
maintainerRe: [PATCH] virtio-fs: fix multiple tag support
Quoted 6 lines · Liu Bo
While doing memremap from pci_dev's system bus address to kernel virtual address, we assign a wrong value to the %end of pgmap.res, which ends up with a wrong resource size in the process of memremap, and that further prevent the second virtiofs pci device from being probed successfully.
Signed-off-by Liu Bo <[email protected]>
Hi Liu Bo,
Thanks for the fix. This seems right. I will fix it in my internal branch. These patches are not upstream yet. Will fold this into existing patch for the next posting.
Thanks
Vivek
Quoted 20 lines
--- fs/fuse/virtio_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)fs/fuse/virtio_fs.c +1
-1@@ -713,7 +713,7 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) pgmap->res = (struct resource ){ .name ="virtio-fs dax window" , .start = phys_addr, - .end = phys_addr + cache_len, + .end = phys_addr + cache_len -1 , }; fs->window_kaddr =devm_memremap_pages (&pci_dev->dev, pgmap);Signature · 1 line
2.20.1.2.gb21ebb6
Re: [PATCH] virtio-fs: fix multiple tag support
Quoted 6 lines · Vivek Goyal
Quoted 5 lines · Liu Bo
While doing memremap from pci_dev's system bus address to kernel virtual address, we assign a wrong value to the %end of pgmap.res, which ends up with a wrong resource size in the process of memremap, and that further prevent the second virtiofs pci device from being probed successfully.
Quoted 8 lines
Quoted 1 line
Signed-off-by Liu Bo <[email protected]>Hi Liu Bo,
Thanks for the fix. This seems right. I will fix it in my internal branch. These patches are not upstream yet. Will fold this into existing patch for the next posting.
Feel free to fold it, and I'm looking forward to the 1.0 release.
thanks,
-liubo
Quoted 23 lines
Thanks
VivekQuoted 20 lines
--- fs/fuse/virtio_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)fs/fuse/virtio_fs.c +1
-1@@ -713,7 +713,7 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) pgmap->res = (struct resource ){ .name ="virtio-fs dax window" , .start = phys_addr, - .end = phys_addr + cache_len, + .end = phys_addr + cache_len -1 , }; fs->window_kaddr =devm_memremap_pages (&pci_dev->dev, pgmap);Signature · 1 line
2.20.1.2.gb21ebb6
Vivek Goyal
maintainerPATCH 19/52 virito-fs: Make dax optional
In reply to Vivek GoyalFrom: "Dr. David Alan Gilbert" <[email protected]>
Add a 'dax' option and only enable dax when it's on.
Also show "dax" in mount options if filesystem was mounted with dax enabled.
--- fs/fuse/fuse_i.h | 1 + fs/fuse/inode.c | 8 ++++++++ fs/fuse/virtio_fs.c | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-)
fs/fuse/fuse_i.h +1 -0
@@ -70,6 +70,7 @@ struct fuse_mount_data { unsigned group_id_present:1; unsigned default_permissions:1; unsigned allow_other:1; + unsigned dax:1;unsigned max_read;unsigned blksize; };
fs/fuse/inode.c +8 -0
@@ -439,6 +439,7 @@ enum {OPT_ALLOW_OTHER ,OPT_MAX_READ ,OPT_BLKSIZE , +OPT_DAX , OPT_ERR };
@@ -452,6 +453,7 @@ static const match_table_t tokens = { {OPT_ALLOW_OTHER, "allow_other"}, {OPT_MAX_READ, "max_read=%u"}, {OPT_BLKSIZE, "blksize=%u"}, + {OPT_DAX, "dax"}, {OPT_ERR, NULL} };
@@ -543,6 +545,10 @@ int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev, d->blksize = value;break ; +case OPT_DAX : + d->dax =1 ; +break ; +default :return 0 ; }
@@ -571,6 +577,8 @@ static int fuse_show_options(struct seq_file *m, struct dentry *root)seq_printf (m,",max_read=%u" , fc->max_read);if (sb->s_bdev && sb->s_blocksize !=FUSE_DEFAULT_BLKSIZE )seq_printf (m,",blksize=%lu" , sb->s_blocksize); +if (fc->dax_dev) +seq_printf (m,",dax" );return 0 ; }
fs/fuse/virtio_fs.c +1 -1
@@ -1050,7 +1050,7 @@ static int virtio_fs_fill_super(struct super_block *sb, void *data,/* TODO this sends FUSE_INIT and could cause hiprio or notifications * virtqueue races since they haven't been set up yet! */ - err =fuse_fill_super_common (sb, &d, fs->dax_dev, + err =fuse_fill_super_common (sb, &d, d.dax ? fs->dax_dev :NULL , &virtio_fs_fiq_ops, fs, (void **)&fs->vqs[2 ].fud);if (err <0 )
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 20/52 Limit number of pages returned by direct_access()
In reply to Vivek GoyalTruncate number of pages mapped by direct_access() to remain with-in window size. User might request mapping pages beyond window size.
--- fs/fuse/virtio_fs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
fs/fuse/virtio_fs.c +2 -1
@@ -449,13 +449,14 @@ static long virtio_fs_direct_access(struct dax_device *dax_dev, pgoff_t pgoff, {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; +return nr_pages > max_nr_pages ? max_nr_pages : nr_pages; } static size_t virtio_fs_copy_from_iter(struct dax_device *dax_dev,
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 21/52 fuse: Introduce fuse_dax_mapping
In reply to Vivek GoyalIntroduce fuse_dax_mapping. This type will be used to keep track of per inode dax mappings.
--- fs/fuse/fuse_i.h | 9 +++++++++ 1 file changed, 9 insertions(+)
fs/fuse/fuse_i.h +9 -0
@@ -81,6 +81,15 @@ struct fuse_forget_link {struct fuse_forget_link *next; }; +/** Translation information for file offsets to DAX window offsets */ +struct fuse_dax_mapping { +/** Position in DAX window */ +u64 window_offset; + +/** Length of mapping, in bytes */ +loff_t length; +}; +/** FUSE inode */ struct fuse_inode {/** Inode data */
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 22/52 Create a list of free memory ranges
In reply to Vivek GoyalDivide the dax memory range into fixed size ranges (2MB for now) and put them in a list. This will track free ranges. Once an inode requires a free range, we will take one from here and put it in interval-tree of ranges assigned to inode.
--- fs/fuse/fuse_i.h | 14 +++++++++ fs/fuse/inode.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++- fs/fuse/virtio_fs.c | 2 ++ 3 files changed, 96 insertions(+), 1 deletion(-)
fs/fuse/fuse_i.h +14 -0
@@ -46,6 +46,10 @@/** Number of page pointers embedded in fuse_req */ #define FUSE_REQ_INLINE_PAGES 1 +/* Default memory range size, 2MB */ +#define FUSE_DAX_MEM_RANGE_SZ (2*1024*1024) +#define FUSE_DAX_MEM_RANGE_PAGES (FUSE_DAX_MEM_RANGE_SZ/PAGE_SIZE) +/** List of active connections */ extern struct list_head fuse_conn_list;
@@ -83,6 +87,9 @@ struct fuse_forget_link {/** Translation information for file offsets to DAX window offsets */ struct fuse_dax_mapping { +/* Will connect in fc->free_ranges to keep track of free memory */ +struct list_head list; +/** Position in DAX window */ u64 window_offset;
@@ -816,6 +823,13 @@ struct fuse_conn {/** DAX device, non-NULL if DAX is supported */ struct dax_device *dax_dev; + +/* +* DAX Window Free Ranges. TODO: This might not be best place to store +* this free list +*/ +unsigned long nr_free_ranges; +struct list_head free_ranges; };static inline struct fuse_conn *get_fuse_conn_super (struct super_block *sb)
fs/fuse/inode.c +80 -1
119 lines not on this page · raw message
fs/fuse/virtio_fs.c +2 -0
@@ -451,6 +451,8 @@ static long virtio_fs_direct_access(struct dax_device *dax_dev, pgoff_t pgoff,phys_addr_t offset =PFN_PHYS (pgoff);size_t max_nr_pages = fs->window_len/PAGE_SIZE - pgoff; +pr_debug ("virtio_fs_direct_access(): called. nr_pages=%ld max_nr_pages=%ld\n" , nr_pages, max_nr_pages); +if (kaddr) *kaddr = fs->window_kaddr + offset;if (pfn)
Signature · 1 line
2.13.6
kbuild test robot
build botRe: [PATCH 22/52] Create a list of free memory ranges
I love your patch! Perhaps something to improve:
Hi Vivek,
I love your patch! Perhaps something to improve:
[auto build test WARNING on fuse/for-next]
[also build test WARNING on v4.20-rc6]
[cannot apply to next-20181210]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Vivek-Goyal/virtio-fs-shared-file-system-for-virtual-machines/20181211-103034 base: https://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git for-next config: arm-allmodconfig (attached as .config) compiler: arm-linux-gnueabi-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
GCC_VERSION=7.2.0 make.cross ARCH=arm
All warnings (new ones prefixed by >>):
In file included from include/linux/kernel.h:14:0,
from include/linux/list.h:9,
from include/linux/wait.h:7,
from include/linux/wait_bit.h:8,
from include/linux/fs.h:6,
from fs/fuse/virtio_fs.c:7:
fs/fuse/virtio_fs.c: In function 'virtio_fs_direct_access':
>> fs/fuse/virtio_fs.c:454:11: warning: format '%ld' expects argument of type 'long int', but argument 4 has type 'size_t {aka unsigned int}' [-Wformat=]
pr_debug("virtio_fs_direct_access(): called. nr_pages=%ld max_nr_pages=%ld\n", nr_pages, max_nr_pages);
^
include/linux/printk.h:292:21: note: in definition of macro 'pr_fmt'
#define pr_fmt(fmt) fmt
^~~
include/linux/printk.h:340:2: note: in expansion of macro 'dynamic_pr_debug'
dynamic_pr_debug(fmt, ##__VA_ARGS__)
^~~~~~~~~~~~~~~~
>> fs/fuse/virtio_fs.c:454:2: note: in expansion of macro 'pr_debug'
pr_debug("virtio_fs_direct_access(): called. nr_pages=%ld max_nr_pages=%ld\n", nr_pages, max_nr_pages);
^~~~~~~~
In file included from include/linux/printk.h:336:0,
from include/linux/kernel.h:14,
from include/linux/list.h:9,
from include/linux/wait.h:7,
from include/linux/wait_bit.h:8,
from include/linux/fs.h:6,
from fs/fuse/virtio_fs.c:7:
fs/fuse/virtio_fs.c: In function 'virtio_fs_setup_dax':
fs/fuse/virtio_fs.c:617:22: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 6 has type 'phys_addr_t {aka unsigned int}' [-Wformat=]
dev_dbg(&vdev->dev, "%s: cache kaddr 0x%px phys_addr 0x%llx len %llx\n",
^
include/linux/dynamic_debug.h:135:39: note: in definition of macro 'dynamic_dev_dbg'
__dynamic_dev_dbg(&descriptor, dev, fmt, \
^~~
include/linux/device.h:1463:23: note: in expansion of macro 'dev_fmt'
dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
^~~~~~~
fs/fuse/virtio_fs.c:617:2: note: in expansion of macro 'dev_dbg'
dev_dbg(&vdev->dev, "%s: cache kaddr 0x%px phys_addr 0x%llx len %llx\n",
^~~~~~~vim +454 fs/fuse/virtio_fs.c
442
443 /* Map a window offset to a page frame number. The window offset will have
444 * been produced by .iomap_begin(), which maps a file offset to a window
445 * offset.
446 */
447 static long virtio_fs_direct_access(struct dax_device *dax_dev, pgoff_t pgoff,
448 long nr_pages, void **kaddr, pfn_t *pfn)
449 {
450 struct virtio_fs *fs = dax_get_private(dax_dev);
451 phys_addr_t offset = PFN_PHYS(pgoff);
452 size_t max_nr_pages = fs->window_len/PAGE_SIZE - pgoff;
453
> 454 pr_debug("virtio_fs_direct_access(): called. nr_pages=%ld max_nr_pages=%ld\n", nr_pages, max_nr_pages); 455
456 if (kaddr)
457 *kaddr = fs->window_kaddr + offset;
458 if (pfn)
459 *pfn = phys_to_pfn_t(fs->window_phys_addr + offset,
460 PFN_DEV | PFN_MAP);
461 return nr_pages > max_nr_pages ? max_nr_pages : nr_pages;
462 }
463
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
kbuild test robot
build botRe: [PATCH 22/52] Create a list of free memory ranges
In reply to Vivek GoyalI love your patch! Yet something to improve:
Hi Vivek,
I love your patch! Yet something to improve:
[auto build test ERROR on fuse/for-next]
[also build test ERROR on v4.20-rc6]
[cannot apply to next-20181214]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Vivek-Goyal/virtio-fs-shared-file-system-for-virtual-machines/20181211-103034 base: https://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git for-next config: nds32-defconfig (attached as .config) compiler: nds32le-linux-gcc (GCC) 6.4.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
GCC_VERSION=6.4.0 make.cross ARCH=nds32
All errors (new ones prefixed by >>):
fs/fuse/inode.o: In function `fuse_fill_super_common':
>> inode.c:(.text+0x1c00): undefined reference to `dax_read_lock' inode.c:(.text+0x1c04): undefined reference to `dax_read_lock' >> inode.c:(.text+0x1c22): undefined reference to `dax_direct_access' inode.c:(.text+0x1c26): undefined reference to `dax_direct_access' >> inode.c:(.text+0x1c32): undefined reference to `dax_read_unlock'
inode.c:(.text+0x1c36): undefined reference to `dax_read_unlock'
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Vivek Goyal
maintainerPATCH 23/52 fuse: simplify fuse_fill_super_common() calling
In reply to Vivek GoyalFrom: Miklos Szeredi <[email protected]>
Add more fields to "struct fuse_mount_data" so that less parameters have to be passed to function fuse_fill_super_common().
--- fs/fuse/fuse_i.h | 22 +++++++++++++--------- fs/fuse/inode.c | 27 ++++++++++++++------------- fs/fuse/virtio_fs.c | 10 +++++++--- 3 files changed, 34 insertions(+), 25 deletions(-)
fs/fuse/fuse_i.h +13 -9
@@ -77,6 +77,18 @@ struct fuse_mount_data { unsigned dax:1;unsigned max_read;unsigned blksize; + +/* DAX device, may be NULL */ +struct dax_device *dax_dev; + +/* fuse input queue operations */ +const struct fuse_iqueue_ops *fiq_ops; + +/* device-specific state for fuse_iqueue */ +void *fiq_priv; + +/* fuse_dev pointer to fill in, should contain NULL on entry */ +void **fudptr; };/* One forget request */
@@ -1073,17 +1085,9 @@ int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev,* Fill in superblock and initialize fuse connection * @sb: partially-initialized superblock to fill in * @mount_data: mount parameters -* @dax_dev: DAX device, may be NULL -* @fiq_ops: fuse input queue operations -* @fiq_priv: device-specific state for fuse_iqueue -* @fudptr: fuse_dev pointer to fill in, should contain NULL on entry */ int fuse_fill_super_common (struct super_block *sb, -struct fuse_mount_data *mount_data, -struct dax_device *dax_dev, -const struct fuse_iqueue_ops *fiq_ops, -void *fiq_priv, -void **fudptr); +struct fuse_mount_data *mount_data);/** * Disassociate fuse connection from superblock and kill the superblock
fs/fuse/inode.c +14 -13
@@ -1149,11 +1149,7 @@ void fuse_dev_free(struct fuse_dev *fud)EXPORT_SYMBOL_GPL (fuse_dev_free);int fuse_fill_super_common (struct super_block *sb, -struct fuse_mount_data *mount_data, -struct dax_device *dax_dev, -const struct fuse_iqueue_ops *fiq_ops, -void *fiq_priv, -void **fudptr) +struct fuse_mount_data *mount_data) {struct fuse_dev *fud;struct fuse_conn *fc;
@@ -1201,11 +1197,12 @@ int fuse_fill_super_common(struct super_block *sb,if (!fc) goto err; -fuse_conn_init (fc, sb->s_user_ns, dax_dev, fiq_ops, fiq_priv); +fuse_conn_init (fc, sb->s_user_ns, mount_data->dax_dev, + mount_data->fiq_ops, mount_data->fiq_priv); fc->release = fuse_free_conn; -if (dax_dev) { - err =fuse_dax_mem_range_init (fc, dax_dev); +if (mount_data->dax_dev) { + err =fuse_dax_mem_range_init (fc, mount_data->dax_dev);if (err) {pr_debug ("fuse_dax_mem_range_init() returned %d\n" , err); goto err_put_conn;
@@ -1259,7 +1256,7 @@ int fuse_fill_super_common(struct super_block *sb,mutex_lock (&fuse_mutex); err = -EINVAL ; -if (*fudptr) +if (*mount_data->fudptr) goto err_unlock; err =fuse_ctl_add_conn (fc);
@@ -1268,7 +1265,7 @@ int fuse_fill_super_common(struct super_block *sb,list_add_tail (&fc->entry, &fuse_conn_list); sb->s_root = root_dentry; - *fudptr = fud; + *mount_data->fudptr = fud;/* * mutex_unlock() provides the necessary memory barrier for * *fudptr to be visible on all CPUs after this
@@ -1288,7 +1285,7 @@ int fuse_fill_super_common(struct super_block *sb, err_dev_free:fuse_dev_free (fud); err_free_ranges: -if (dax_dev) +if (mount_data->dax_dev)fuse_free_dax_mem_ranges (&fc->free_ranges); err_put_conn:fuse_conn_put (fc);
@@ -1323,8 +1320,12 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) (file->f_cred->user_ns != sb->s_user_ns)) goto err_fput; - err =fuse_fill_super_common (sb, &d,NULL , &fuse_dev_fiq_ops,NULL , - &file->private_data); + d.dax_dev =NULL ; + d.fiq_ops = &fuse_dev_fiq_ops; + d.fiq_priv =NULL ; + d.fudptr = &file->private_data; + err =fuse_fill_super_common (sb, &d); + err_fput:fput (file); err:
fs/fuse/virtio_fs.c +7 -3
@@ -1053,9 +1053,13 @@ static int virtio_fs_fill_super(struct super_block *sb, void *data,/* TODO this sends FUSE_INIT and could cause hiprio or notifications * virtqueue races since they haven't been set up yet! */ - err =fuse_fill_super_common (sb, &d, d.dax ? fs->dax_dev :NULL , - &virtio_fs_fiq_ops, fs, - (void **)&fs->vqs[2 ].fud); + + d.dax_dev = d.dax ? fs->dax_dev :NULL ; + d.fiq_ops = &virtio_fs_fiq_ops; + d.fiq_priv = fs; + d.fudptr = (void **)&fs->vqs[2 ].fud; + err =fuse_fill_super_common (sb, &d); +if (err <0 ) goto err_fud;
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 24/52 fuse: Introduce setupmapping/removemapping commands
In reply to Vivek GoyalIntroduce two new fuse commands to setup/remove memory mappings.
--- include/uapi/linux/fuse.h | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+)
include/uapi/linux/fuse.h +33 -0
@@ -394,6 +394,8 @@ enum fuse_opcode {FUSE_RENAME2 =45 ,FUSE_LSEEK =46 ,FUSE_COPY_FILE_RANGE =47 , +FUSE_SETUPMAPPING =48 , +FUSE_REMOVEMAPPING =49 ,/* CUSE specific operations */ CUSE_INIT = 4096,
@@ -817,4 +819,35 @@ struct fuse_copy_file_range_in { uint64_t flags; }; +#define FUSE_SETUPMAPPING_ENTRIES 8 +#define FUSE_SETUPMAPPING_FLAG_WRITE (1ull << 0) +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_setupmapping_out { +/* Offsets into the cache of mappings */ +uint64_t coffset[FUSE_SETUPMAPPING_ENTRIES ]; +/* Lengths of each mapping */ +uint64_t len[FUSE_SETUPMAPPING_ENTRIES ]; +}; + +struct fuse_removemapping_in { +/* An already open handle */ +uint64_t fh; +/* Offset into the dax window start the unmapping */ +uint64_t moffset; +/* Length of mapping required */ +uint64_t len; +}; +#endif /* _LINUX_FUSE_H */
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 25/52 Introduce interval tree basic data structures
In reply to Vivek GoyalWe want to use interval tree to keep track of per inode dax mappings. Introduce basic data structures.
--- fs/fuse/fuse_i.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
fs/fuse/fuse_i.h +15 -0
@@ -97,11 +97,22 @@ struct fuse_forget_link {struct fuse_forget_link *next; }; +#define START (node) ((node)->start) +#define LAST (node) ((node)->end) +/** Translation information for file offsets to DAX window offsets */ struct fuse_dax_mapping {/* Will connect in fc->free_ranges to keep track of free memory */ struct list_head list; +/* For interval tree in file/inode */ +struct rb_node rb; +/** Start Position in file */ +__u64 start; +/** End Position in file */ +__u64 end; +__u64 __subtree_last; +/** Position in DAX window */ u64 window_offset;
@@ -191,6 +202,10 @@ struct fuse_inode {/** Lock for serializing lookup and readdir for back compatibility*/ struct mutex mutex; + +/** Sorted rb tree of struct fuse_dax_mapping elements */ +struct rb_root_cached dmap_tree; +unsigned long nr_dmaps; };/** FUSE inode state bits */
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 26/52 fuse: Implement basic DAX read/write support commands
In reply to Vivek GoyalFrom: Stefan Hajnoczi <[email protected]>
This patch implements basic DAX support. mmap() is not implemented yet and will come in later patches. This patch looks into implemeting read/write.
--- fs/fuse/file.c | 400 ++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/fuse_i.h | 6 + fs/fuse/inode.c | 6 + include/uapi/linux/fuse.h | 1 + 4 files changed, 413 insertions(+)
fs/fuse/file.c +400 -0
@@ -18,9 +18,16 @@#include <linux/swap.h> #include <linux/falloc.h> #include <linux/uio.h> +#include <linux/dax.h> +#include <linux/iomap.h> +#include <linux/interval_tree_generic.h> static const struct file_operations fuse_direct_io_file_operations; +INTERVAL_TREE_DEFINE(struct fuse_dax_mapping, + rb, __u64, __subtree_last, + START, LAST, static inline, fuse_dax_interval_tree); +static int fuse_send_open (struct fuse_conn *fc,u64 nodeid,struct file *file,int opcode,struct fuse_open_out *outargp) {
@@ -172,6 +179,171 @@ static void fuse_link_write_file(struct file *file)spin_unlock (&fc->lock); } +static struct fuse_dax_mapping *alloc_dax_mapping (struct fuse_conn *fc) +{ +struct fuse_dax_mapping *dmap =NULL ; + +spin_lock (&fc->lock); + +/* TODO: Add logic to try to free up memory if wait is allowed */ +if (fc->nr_free_ranges <=0 ) { +spin_unlock (&fc->lock); +return NULL ; + } + +WARN_ON (list_empty (&fc->free_ranges)); + +/* Take a free range */ + dmap = list_first_entry(&fc->free_ranges, struct fuse_dax_mapping, + list); +list_del_init (&dmap->list); + fc->nr_free_ranges--; +spin_unlock (&fc->lock); +return dmap; +} + +/* This assumes fc->lock is held */ +static void __free_dax_mapping (struct fuse_conn *fc, +struct fuse_dax_mapping *dmap) +{ +list_add_tail (&dmap->list, &fc->free_ranges); + fc->nr_free_ranges++; +} + +static void free_dax_mapping (struct fuse_conn *fc, +struct fuse_dax_mapping *dmap) +{ +/* Return fuse_dax_mapping to free list */ +spin_lock (&fc->lock); +__free_dax_mapping (fc, dmap); +spin_unlock (&fc->lock); +} + +/* offset passed in should be aligned to FUSE_DAX_MEM_RANGE_SZ */ +static int fuse_setup_one_mapping (struct inode *inode, +struct file *file,loff_t offset, +struct fuse_dax_mapping *dmap) +{ +struct fuse_conn *fc =get_fuse_conn (inode); +struct fuse_inode *fi =get_fuse_inode (inode); +struct fuse_file *ff =NULL ; +struct fuse_setupmapping_in inarg; +FUSE_ARGS (args); +ssize_t err; + +if (file) + ff = file->private_data; + +WARN_ON (offset %FUSE_DAX_MEM_RANGE_SZ ); +WARN_ON (fc->nr_free_ranges <0 ); + +/* Ask fuse daemon to setup mapping */ +memset (&inarg,0 ,sizeof (inarg)); + inarg.foffset = offset; +if (ff) + inarg.fh = ff->fh; +else + inarg.fh =-1 ; + inarg.moffset = dmap->window_offset; + inarg.len =FUSE_DAX_MEM_RANGE_SZ ; +if (file) { + inarg.flags |= (file->f_mode &FMODE_WRITE ) ? +FUSE_SETUPMAPPING_FLAG_WRITE :0 ; + inarg.flags |= (file->f_mode &FMODE_READ ) ? +FUSE_SETUPMAPPING_FLAG_READ :0 ; + }else { + inarg.flags |=FUSE_SETUPMAPPING_FLAG_READ ; + inarg.flags |=FUSE_SETUPMAPPING_FLAG_WRITE ; + } + args.in.h.opcode =FUSE_SETUPMAPPING ; + args.in.h.nodeid = fi->nodeid; + args.in.numargs =1 ; + args.in.args[0 ].size =sizeof (inarg); + args.in.args[0 ].value = &inarg; + err =fuse_simple_request (fc, &args); +if (err <0 ) { +printk (KERN_ERR "%s request failed at mem_offset=0x%llx %zd\n" , + __func__, dmap->window_offset, err); +return err; + } + +pr_debug ("fuse_setup_one_mapping() succeeded. offset=0x%llx err=%zd\n" , offset, err); + +/* TODO: What locking is required here. For now, using fc->lock */ + dmap->start = offset; + dmap->end = offset +FUSE_DAX_MEM_RANGE_SZ -1 ; +/* Protected by fi->i_dmap_sem */ +fuse_dax_interval_tree_insert (dmap, &fi->dmap_tree); + fi->nr_dmaps++; +return 0 ; +} + +static int fuse_removemapping_one (struct inode *inode, +struct fuse_dax_mapping *dmap) +{ +struct fuse_inode *fi =get_fuse_inode (inode); +struct fuse_conn *fc =get_fuse_conn (inode); +struct fuse_removemapping_in inarg; +FUSE_ARGS (args); +ssize_t err =0 ; + +memset (&inarg,0 ,sizeof (inarg)); + inarg.moffset = dmap->window_offset; + inarg.len = dmap->length; + args.in.h.opcode =FUSE_REMOVEMAPPING ; + args.in.h.nodeid = fi->nodeid; + args.in.numargs =1 ; + args.in.args[0 ].size =sizeof (inarg); + args.in.args[0 ].value = &inarg; + err =fuse_simple_request (fc, &args); +if (err <0 ) { +printk (KERN_ERR "%s request failed %zd\n" , __func__, err); +return err; + } +pr_debug ("%s request succeeded\n" , __func__); +return 0 ; +} + +void fuse_removemapping (struct inode *inode) +{ +struct fuse_conn *fc =get_fuse_conn (inode); +struct fuse_inode *fi =get_fuse_inode (inode); +ssize_t err; +struct fuse_dax_mapping *dmap; + +down_write (&fi->i_dmap_sem); + +/* Clear the mappings list */ +while (true) { +WARN_ON (fi->nr_dmaps <0 ); + + dmap =fuse_dax_interval_tree_iter_first (&fi->dmap_tree,0 , +-1 ); +if (dmap) { +fuse_dax_interval_tree_remove (dmap, &fi->dmap_tree); + fi->nr_dmaps--; + } + +if (!dmap) +break ; + + err =fuse_removemapping_one (inode, dmap); +if (err) { +/* TODO: Add it back to tree. */ +printk ("Failed to removemapping. offset=0x%llx" +" len=0x%llx\n" , dmap->window_offset, + dmap->length); +continue ; + } + +/* Add it back to free ranges list */ +free_dax_mapping (fc, dmap); + } + +up_write (&fi->i_dmap_sem); +pr_debug ("%s request succeeded\n" , __func__); +} +void fuse_finish_open (struct inode *inode,struct file *file) {struct fuse_file *ff = file->private_data;
@@ -1452,6 +1624,204 @@ static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from)return res; } +static void fuse_fill_iomap_hole (struct iomap *iomap,loff_t length) +{ + iomap->addr =IOMAP_NULL_ADDR ; + iomap->length = length; + iomap->type =IOMAP_HOLE ; +} + +static void fuse_fill_iomap (struct inode *inode,loff_t pos,loff_t length, +struct iomap *iomap,struct fuse_dax_mapping *dmap, +unsigned flags) +{ +loff_t offset, len; +loff_t i_size =i_size_read (inode); + + offset = pos - dmap->start; + len =min (length, dmap->length - offset); + +/* If length is beyond end of file, truncate further */ +if (pos + len > i_size) + len = i_size - pos; + +if (len >0 ) { + iomap->addr = dmap->window_offset + offset; + iomap->length = len; +if (flags &IOMAP_FAULT ) + iomap->length =ALIGN (len,PAGE_SIZE ); + iomap->type =IOMAP_MAPPED ; +pr_debug ("%s: returns iomap: addr 0x%llx offset 0x%llx" +" length 0x%llx\n" , __func__, iomap->addr, + iomap->offset, iomap->length); + }else { +/* Mapping beyond end of file is hole */ +fuse_fill_iomap_hole (iomap, length); +pr_debug ("%s: returns iomap: addr 0x%llx offset 0x%llx" +"length 0x%llx\n" , __func__, iomap->addr, + iomap->offset, iomap->length); + } +} + +/* This is just for DAX and the mapping is ephemeral, do not use it for other +* purposes since there is no block device with a permanent mapping. +*/ +static int fuse_iomap_begin (struct inode *inode,loff_t pos,loff_t length, +unsigned flags,struct iomap *iomap) +{ +struct fuse_inode *fi =get_fuse_inode (inode); +struct fuse_conn *fc =get_fuse_conn (inode); +struct fuse_dax_mapping *dmap, *alloc_dmap =NULL ; +int ret; + +/* We don't support FIEMAP */ +BUG_ON (flags &IOMAP_REPORT ); + +pr_debug ("fuse_iomap_begin() called. pos=0x%llx length=0x%llx\n" , + pos, length); + + iomap->offset = pos; + iomap->flags =0 ; + iomap->bdev =NULL ; + iomap->dax_dev = fc->dax_dev; + +/* +* Both read/write and mmap path can race here. So we need something +* to make sure if we are setting up mapping, then other path waits +* +* For now, use a semaphore for this. It probably needs to be +* optimized later. +*/ +down_read (&fi->i_dmap_sem); + dmap =fuse_dax_interval_tree_iter_first (&fi->dmap_tree, pos, pos); + +if (dmap) { +fuse_fill_iomap (inode, pos, length, iomap, dmap, flags); +up_read (&fi->i_dmap_sem); +return 0 ; + }else { +up_read (&fi->i_dmap_sem); +pr_debug ("%s: no mapping at offset 0x%llx length 0x%llx\n" , + __func__, pos, length); +if (pos >=i_size_read (inode)) + goto iomap_hole; + + alloc_dmap =alloc_dax_mapping (fc); +if (!alloc_dmap) +return -EBUSY ; + +/* +* Drop read lock and take write lock so that only one +* caller can try to setup mapping and other waits +*/ +down_write (&fi->i_dmap_sem); +/* +* We dropped lock. Check again if somebody else setup +* mapping already. +*/ + dmap =fuse_dax_interval_tree_iter_first (&fi->dmap_tree, pos, + pos); +if (dmap) { +fuse_fill_iomap (inode, pos, length, iomap, dmap, flags); +free_dax_mapping (fc, alloc_dmap); +up_write (&fi->i_dmap_sem); +return 0 ; + } + +/* Setup one mapping */ + ret =fuse_setup_one_mapping (inode,NULL , +ALIGN_DOWN (pos,FUSE_DAX_MEM_RANGE_SZ ), + alloc_dmap); +if (ret <0 ) { +printk ("fuse_setup_one_mapping() failed. err=%d" +" pos=0x%llx\n" , ret, pos); +free_dax_mapping (fc, alloc_dmap); +up_write (&fi->i_dmap_sem); +return ret; + } +fuse_fill_iomap (inode, pos, length, iomap, alloc_dmap, flags); +up_write (&fi->i_dmap_sem); +return 0 ; + } + +/* +* If read beyond end of file happnes, fs code seems to return +* it as hole +*/ +iomap_hole: +fuse_fill_iomap_hole (iomap, length); +pr_debug ("fuse_iomap_begin() returning hole mapping. pos=0x%llx length_asked=0x%llx length_returned=0x%llx\n" , pos, length, iomap->length); +return 0 ; +} + +static int fuse_iomap_end (struct inode *inode,loff_t pos,loff_t length, +ssize_t written,unsigned flags, +struct iomap *iomap) +{ +/* DAX writes beyond end-of-file aren't handled using iomap, so the +* file size is unchanged and there is nothing to do here. +*/ +return 0 ; +} + +static const struct iomap_ops fuse_iomap_ops = { + .iomap_begin = fuse_iomap_begin, + .iomap_end = fuse_iomap_end, +}; + +static ssize_t fuse_dax_read_iter (struct kiocb *iocb,struct iov_iter *to) +{ +struct inode *inode =file_inode (iocb->ki_filp); +ssize_t ret; + +if (iocb->ki_flags &IOCB_NOWAIT ) { +if (!inode_trylock_shared (inode)) +return -EAGAIN ; + }else { +inode_lock_shared (inode); + } + + ret =dax_iomap_rw (iocb, to, &fuse_iomap_ops); +inode_unlock_shared (inode); + +/* TODO file_accessed(iocb->f_filp) */ + +return ret; +} + +static ssize_t fuse_dax_write_iter (struct kiocb *iocb,struct iov_iter *from) +{ +struct inode *inode =file_inode (iocb->ki_filp); +ssize_t ret; + +if (iocb->ki_flags &IOCB_NOWAIT ) { +if (!inode_trylock (inode)) +return -EAGAIN ; + }else { +inode_lock (inode); + } + + ret =generic_write_checks (iocb, from); +if (ret <=0 ) + goto out; + + ret =file_remove_privs (iocb->ki_filp); +if (ret) + goto out; +/* TODO file_update_time() but we don't want metadata I/O */ + +/* TODO handle growing the file */ + + ret =dax_iomap_rw (iocb, from, &fuse_iomap_ops); + +out: +inode_unlock (inode); + +if (ret >0 ) + ret =generic_write_sync (iocb, ret); +return ret; +} +static void fuse_writepage_free (struct fuse_conn *fc,struct fuse_req *req) {int i;
@@ -2104,6 +2474,11 @@ static int fuse_direct_mmap(struct file *file, struct vm_area_struct *vma)return generic_file_mmap (file, vma); } +static int fuse_dax_mmap (struct file *file,struct vm_area_struct *vma) +{ +return -EINVAL ;/* TODO */ +} +static int convert_fuse_file_lock (struct fuse_conn *fc,const struct fuse_file_lock *ffl,struct file_lock *fl)
@@ -3137,6 +3512,24 @@ static const struct file_operations fuse_direct_io_file_operations = {/* no splice_read */ }; +static const struct file_operations fuse_dax_file_operations = { + .llseek = fuse_file_llseek, + .read_iter = fuse_dax_read_iter, + .write_iter = fuse_dax_write_iter, + .mmap = fuse_dax_mmap, + .open = fuse_open, + .flush = fuse_flush, + .release = fuse_release, + .fsync = fuse_fsync, + .lock = fuse_file_lock, + .flock = fuse_file_flock, + .unlocked_ioctl = fuse_file_ioctl, + .compat_ioctl = fuse_file_compat_ioctl, + .poll = fuse_file_poll, + .fallocate = fuse_file_fallocate, +/* no splice_read */ +}; +static const struct address_space_operations fuse_file_aops = { .readpage = fuse_readpage, .writepage = fuse_writepage,
@@ -3153,6 +3546,7 @@ static const struct address_space_operations fuse_file_aops = {void fuse_init_file_inode (struct inode *inode) {struct fuse_inode *fi =get_fuse_inode (inode); +struct fuse_conn *fc =get_fuse_conn (inode); inode->i_fop = &fuse_file_operations; inode->i_data.a_ops = &fuse_file_aops;
@@ -3162,4 +3556,10 @@ void fuse_init_file_inode(struct inode *inode) fi->writectr =0 ;init_waitqueue_head (&fi->page_waitq);INIT_LIST_HEAD (&fi->writepages); + fi->dmap_tree =RB_ROOT_CACHED ; + +if (fc->dax_dev) { + inode->i_flags |=S_DAX ; + inode->i_fop = &fuse_dax_file_operations; + } }
fs/fuse/fuse_i.h +6 -0
@@ -203,6 +203,11 @@ struct fuse_inode {/** Lock for serializing lookup and readdir for back compatibility*/ struct mutex mutex; +/* +* Semaphore to protect modifications to dmap_tree +*/ +struct rw_semaphore i_dmap_sem; +/** Sorted rb tree of struct fuse_dax_mapping elements */ struct rb_root_cached dmap_tree;unsigned long nr_dmaps;
@@ -1225,5 +1230,6 @@ unsigned fuse_len_args(unsigned numargs, struct fuse_arg *args);* Get the next unique ID for a request */ u64 fuse_get_unique (struct fuse_iqueue *fiq); +void fuse_removemapping (struct inode *inode);#endif /* _FS_FUSE_I_H */
fs/fuse/inode.c +6 -0
@@ -83,7 +83,9 @@ static struct inode *fuse_alloc_inode(struct super_block *sb) fi->attr_version =0 ; fi->orig_ino =0 ; fi->state =0 ; + fi->nr_dmaps =0 ;mutex_init (&fi->mutex); +init_rwsem (&fi->i_dmap_sem); fi->forget =fuse_alloc_forget ();if (!fi->forget) {kmem_cache_free (fuse_inode_cachep, inode);
@@ -118,6 +120,10 @@ static void fuse_evict_inode(struct inode *inode)if (inode->i_sb->s_flags &SB_ACTIVE ) {struct fuse_conn *fc =get_fuse_conn (inode);struct fuse_inode *fi =get_fuse_inode (inode); +if (IS_DAX (inode)) { +fuse_removemapping (inode); +WARN_ON (fi->nr_dmaps); + }fuse_queue_forget (fc, fi->forget, fi->nodeid, fi->nlookup); fi->forget =NULL ; }
include/uapi/linux/fuse.h +1 -0
@@ -821,6 +821,7 @@ struct fuse_copy_file_range_in {#define FUSE_SETUPMAPPING_ENTRIES 8#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;
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 27/52 fuse: Maintain a list of busy elements
In reply to Vivek GoyalThis list will be used selecting fuse_dax_mapping to free when number of free mappings drops below a threshold.
--- fs/fuse/file.c | 8 ++++++++ fs/fuse/fuse_i.h | 7 +++++++ fs/fuse/inode.c | 4 ++++ 3 files changed, 19 insertions(+)
fs/fuse/file.c +8 -0
@@ -275,6 +275,10 @@ static int fuse_setup_one_mapping(struct inode *inode,/* Protected by fi->i_dmap_sem */ fuse_dax_interval_tree_insert (dmap, &fi->dmap_tree); fi->nr_dmaps++; +spin_lock (&fc->lock); +list_add_tail (&dmap->busy_list, &fc->busy_ranges); + fc->nr_busy_ranges++; +spin_unlock (&fc->lock);return 0 ; }
@@ -322,6 +326,10 @@ void fuse_removemapping(struct inode *inode)if (dmap) {fuse_dax_interval_tree_remove (dmap, &fi->dmap_tree); fi->nr_dmaps--; +spin_lock (&fc->lock); +list_del_init (&dmap->busy_list); + fc->nr_busy_ranges--; +spin_unlock (&fc->lock); }if (!dmap)
fs/fuse/fuse_i.h +7 -0
@@ -113,6 +113,9 @@ struct fuse_dax_mapping {__u64 end;__u64 __subtree_last; +/* Will connect in fc->busy_ranges to keep track busy memory */ +struct list_head busy_list; +/** Position in DAX window */ u64 window_offset;
@@ -856,6 +859,10 @@ struct fuse_conn {/** DAX device, non-NULL if DAX is supported */ struct dax_device *dax_dev; +/* List of memory ranges which are busy */ +unsigned long nr_busy_ranges; +struct list_head busy_ranges; +/* * DAX Window Free Ranges. TODO: This might not be best place to store * this free list
fs/fuse/inode.c +4 -0
@@ -622,6 +622,8 @@ static void fuse_free_dax_mem_ranges(struct list_head *mem_list)/* Free All allocated elements */ list_for_each_entry_safe (range, temp, mem_list, list) {list_del (&range->list); +if (!list_empty (&range->busy_list)) +list_del (&range->busy_list);kfree (range); } }
@@ -666,6 +668,7 @@ static int fuse_dax_mem_range_init(struct fuse_conn *fc, range->window_offset = i *FUSE_DAX_MEM_RANGE_SZ ; range->length =FUSE_DAX_MEM_RANGE_SZ ;list_add_tail (&range->list, &mem_ranges); +INIT_LIST_HEAD (&range->busy_list); allocated_ranges++; }
@@ -708,6 +711,7 @@ void fuse_conn_init(struct fuse_conn *fc, struct user_namespace *user_ns, fc->dax_dev = dax_dev; fc->user_ns =get_user_ns (user_ns);INIT_LIST_HEAD (&fc->free_ranges); +INIT_LIST_HEAD (&fc->busy_ranges); }EXPORT_SYMBOL_GPL (fuse_conn_init);
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 28/52 Do fallocate() to grow file before mapping for file growing writes
In reply to Vivek GoyalHow to handle file growing writes. For now, this patch does fallocate() to grow file and then map it using dax. We need to figure out what's the best way to handle it.
This patch does fallocate() and setup mapping operations in fuse_dax_write_iter(), instead of iomap_begin(). I don't have access to file pointer needed to send a message to fuse daemon in iomap_begin().
Dave Chinner has expressed concers with this approach as this is not atomic. If guest crashes after falloc() but before data was written, user will think that filesystem lost its data. So this is still an outstanding issue.
--- fs/fuse/file.c | 71 +++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 55 insertions(+), 16 deletions(-)
fs/fuse/file.c +55 -16
@@ -28,6 +28,9 @@ INTERVAL_TREE_DEFINE(struct fuse_dax_mapping, rb, __u64, __subtree_last, START, LAST, static inline, fuse_dax_interval_tree); +static long __fuse_file_fallocate (struct file *file,int mode, +loff_t offset,loff_t length); +static int fuse_send_open (struct fuse_conn *fc,u64 nodeid,struct file *file,int opcode,struct fuse_open_out *outargp) {
@@ -1819,6 +1822,22 @@ static ssize_t fuse_dax_write_iter(struct kiocb *iocb, struct iov_iter *from)/* TODO file_update_time() but we don't want metadata I/O */ /* TODO handle growing the file */ +/* Grow file here if need be. iomap_begin() does not have access +* to file pointer +*/ +if (iov_iter_rw (from) ==WRITE && + ((iocb->ki_pos +iov_iter_count (from)) >i_size_read (inode))) { + ret =__fuse_file_fallocate (iocb->ki_filp,0 , iocb->ki_pos, +iov_iter_count (from)); +if (ret <0 ) { +printk ("fallocate(offset=0x%llx length=0x%lx)" +" failed. err=%ld\n" , iocb->ki_pos, +iov_iter_count (from), ret); + goto out; + } +pr_debug ("fallocate(offset=0x%llx length=0x%lx)" +" succeed. ret=%ld\n" , iocb->ki_pos,iov_iter_count (from), ret); + } ret =dax_iomap_rw (iocb, from, &fuse_iomap_ops);
@@ -3331,8 +3350,12 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter)return ret; } -static long fuse_file_fallocate (struct file *file,int mode,loff_t offset, -loff_t length) +/* +* This variant does not take any inode lock and if locking is required, +* caller is supposed to hold lock +*/ +static long __fuse_file_fallocate (struct file *file,int mode, +loff_t offset,loff_t length) {struct fuse_file *ff = file->private_data;struct inode *inode =file_inode (file);
@@ -3346,8 +3369,6 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, .mode = mode };int err; -bool lock_inode = !(mode &FALLOC_FL_KEEP_SIZE ) || - (mode &FALLOC_FL_PUNCH_HOLE );if (mode & ~(FALLOC_FL_KEEP_SIZE |FALLOC_FL_PUNCH_HOLE ))return -EOPNOTSUPP ;
@@ -3355,17 +3376,13 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,if (fc->no_fallocate)return -EOPNOTSUPP ; -if (lock_inode) { -inode_lock (inode); -if (mode &FALLOC_FL_PUNCH_HOLE ) { -loff_t endbyte = offset + length -1 ; - err =filemap_write_and_wait_range (inode->i_mapping, - offset, endbyte); -if (err) - goto out; - -fuse_sync_writes (inode); - } +if (mode &FALLOC_FL_PUNCH_HOLE ) { +loff_t endbyte = offset + length -1 ; + err =filemap_write_and_wait_range (inode->i_mapping, offset, + endbyte); +if (err) + goto out; +fuse_sync_writes (inode); }if (!(mode &FALLOC_FL_KEEP_SIZE ))
@@ -3401,9 +3418,31 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,if (!(mode &FALLOC_FL_KEEP_SIZE ))clear_bit (FUSE_I_SIZE_UNSTABLE , &fi->state); +return err; +} + +static long fuse_file_fallocate (struct file *file,int mode,loff_t offset, +loff_t length) +{ +struct fuse_file *ff = file->private_data; +struct inode *inode =file_inode (file); +struct fuse_conn *fc = ff->fc; +int err; +bool lock_inode = !(mode &FALLOC_FL_KEEP_SIZE ) || + (mode &FALLOC_FL_PUNCH_HOLE ); + +if (mode & ~(FALLOC_FL_KEEP_SIZE |FALLOC_FL_PUNCH_HOLE )) +return -EOPNOTSUPP ; + +if (fc->no_fallocate) +return -EOPNOTSUPP ; +if (lock_inode) -inode_unlock (inode); +inode_lock (inode); + err =__fuse_file_fallocate (file, mode, offset, length); +if (lock_inode) +inode_unlock (inode);return err; }
Signature · 1 line
2.13.6
kbuild test robot
build botRe: [PATCH 28/52] Do fallocate() to grow file before mapping for file growing writes
I love your patch! Perhaps something to improve:
Hi Vivek,
I love your patch! Perhaps something to improve:
[auto build test WARNING on fuse/for-next]
[also build test WARNING on v4.20-rc6]
[cannot apply to next-20181210]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Vivek-Goyal/virtio-fs-shared-file-system-for-virtual-machines/20181211-103034 base: https://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git for-next config: i386-randconfig-x005-201849 (attached as .config) compiler: gcc-7 (Debian 7.3.0-1) 7.3.0 reproduce: # save the attached .config to linux build tree make ARCH=i386
All warnings (new ones prefixed by >>):
fs/fuse/file.c: In function 'fuse_dax_write_iter':
>> fs/fuse/file.c:1834:47: warning: format '%lx' expects argument of type 'long unsigned int', but argument 3 has type 'size_t {aka unsigned int}' [-Wformat=]
printk("fallocate(offset=0x%llx length=0x%lx)"
~~^
%x
fs/fuse/file.c:1836:4:
iov_iter_count(from), ret);
~~~~~~~~~~~~~~~~~~~~
>> fs/fuse/file.c:1834:11: warning: format '%ld' expects argument of type 'long int', but argument 4 has type 'ssize_t {aka int}' [-Wformat=]
printk("fallocate(offset=0x%llx length=0x%lx)"
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
fs/fuse/file.c:1835:20: note: format string is defined here
" failed. err=%ld\n", iocb->ki_pos,
~~^
%d
In file included from include/linux/kernel.h:14:0,
from include/linux/list.h:9,
from include/linux/wait.h:7,
from include/linux/wait_bit.h:8,
from include/linux/fs.h:6,
from fs/fuse/fuse_i.h:13,
from fs/fuse/file.c:9:
fs/fuse/file.c:1839:12: warning: format '%lx' expects argument of type 'long unsigned int', but argument 4 has type 'size_t {aka unsigned int}' [-Wformat=]
pr_debug("fallocate(offset=0x%llx length=0x%lx)"
^
include/linux/printk.h:292:21: note: in definition of macro 'pr_fmt'
#define pr_fmt(fmt) fmt
^~~
include/linux/printk.h:340:2: note: in expansion of macro 'dynamic_pr_debug'
dynamic_pr_debug(fmt, ##__VA_ARGS__)
^~~~~~~~~~~~~~~~
>> fs/fuse/file.c:1839:3: note: in expansion of macro 'pr_debug'
pr_debug("fallocate(offset=0x%llx length=0x%lx)"
^~~~~~~~
fs/fuse/file.c:1839:48: note: format string is defined here
pr_debug("fallocate(offset=0x%llx length=0x%lx)"
~~^
%x
In file included from include/linux/kernel.h:14:0,
from include/linux/list.h:9,
from include/linux/wait.h:7,
from include/linux/wait_bit.h:8,
from include/linux/fs.h:6,
from fs/fuse/fuse_i.h:13,
from fs/fuse/file.c:9:
fs/fuse/file.c:1839:12: warning: format '%ld' expects argument of type 'long int', but argument 5 has type 'ssize_t {aka int}' [-Wformat=]
pr_debug("fallocate(offset=0x%llx length=0x%lx)"
^
include/linux/printk.h:292:21: note: in definition of macro 'pr_fmt'
#define pr_fmt(fmt) fmt
^~~
include/linux/printk.h:340:2: note: in expansion of macro 'dynamic_pr_debug'
dynamic_pr_debug(fmt, ##__VA_ARGS__)
^~~~~~~~~~~~~~~~
>> fs/fuse/file.c:1839:3: note: in expansion of macro 'pr_debug'
pr_debug("fallocate(offset=0x%llx length=0x%lx)"
^~~~~~~~
fs/fuse/file.c:1840:20: note: format string is defined here
" succeed. ret=%ld\n", iocb->ki_pos, iov_iter_count(from), ret);
~~^
%d Cyclomatic Complexity 5 include/linux/compiler.h:__read_once_size Cyclomatic Complexity 5 include/linux/compiler.h:__write_once_size Cyclomatic Complexity 1 include/linux/kasan-checks.h:kasan_check_read Cyclomatic Complexity 1 include/linux/kasan-checks.h:kasan_check_write Cyclomatic Complexity 2 arch/x86/include/asm/bitops.h:set_bit Cyclomatic Complexity 1 arch/x86/include/asm/bitops.h:__set_bit Cyclomatic Complexity 2 arch/x86/include/asm/bitops.h:clear_bit Cyclomatic Complexity 1 arch/x86/include/asm/bitops.h:__clear_bit Cyclomatic Complexity 1 arch/x86/include/asm/bitops.h:test_and_set_bit Cyclomatic Complexity 1 arch/x86/include/asm/bitops.h:test_and_set_bit_lock Cyclomatic Complexity 1 arch/x86/include/asm/bitops.h:constant_test_bit Cyclomatic Complexity 1 arch/x86/include/asm/bitops.h:fls
Cyclomatic Complexity 1 include/linux/log2.h:__ilog2_u32
Cyclomatic Complexity 2 arch/x86/include/asm/jump_label.h:arch_static_branch Cyclomatic Complexity 1 include/linux/list.h:INIT_LIST_HEAD
Cyclomatic Complexity 1 include/linux/list.h:__list_add_valid Cyclomatic Complexity 1 include/linux/list.h:__list_del_entry_valid Cyclomatic Complexity 2 include/linux/list.h:__list_add
Cyclomatic Complexity 1 include/linux/list.h:list_add
Cyclomatic Complexity 1 include/linux/list.h:list_add_tail
Cyclomatic Complexity 1 include/linux/list.h:__list_del
Cyclomatic Complexity 2 include/linux/list.h:__list_del_entry Cyclomatic Complexity 1 include/linux/list.h:list_del
Cyclomatic Complexity 1 include/linux/list.h:list_del_init
Cyclomatic Complexity 1 include/linux/list.h:list_empty
Cyclomatic Complexity 1 arch/x86/include/asm/current.h:get_current Cyclomatic Complexity 1 arch/x86/include/asm/page_32.h:copy_page Cyclomatic Complexity 1 include/asm-generic/getorder.h:__get_order Cyclomatic Complexity 1 include/linux/err.h:PTR_ERR
Cyclomatic Complexity 1 include/linux/err.h:IS_ERR
Cyclomatic Complexity 1 arch/x86/include/asm/irqflags.h:native_save_fl Cyclomatic Complexity 1 arch/x86/include/asm/irqflags.h:native_restore_fl Cyclomatic Complexity 1 arch/x86/include/asm/irqflags.h:native_irq_disable Cyclomatic Complexity 1 arch/x86/include/asm/irqflags.h:arch_local_save_flags Cyclomatic Complexity 1 arch/x86/include/asm/irqflags.h:arch_local_irq_restore Cyclomatic Complexity 1 arch/x86/include/asm/irqflags.h:arch_local_irq_disable Cyclomatic Complexity 1 arch/x86/include/asm/irqflags.h:arch_local_irq_save Cyclomatic Complexity 1 arch/x86/include/asm/processor.h:rep_nop Cyclomatic Complexity 1 arch/x86/include/asm/processor.h:cpu_relax Cyclomatic Complexity 1 arch/x86/include/asm/atomic.h:arch_atomic_read Cyclomatic Complexity 1 arch/x86/include/asm/atomic.h:arch_atomic_set Cyclomatic Complexity 1 arch/x86/include/asm/atomic.h:arch_atomic_inc Cyclomatic Complexity 1 arch/x86/include/asm/atomic.h:arch_atomic_dec_and_test Cyclomatic Complexity 1 include/asm-generic/atomic-instrumented.h:atomic_read Cyclomatic Complexity 1 include/asm-generic/atomic-instrumented.h:atomic_set Cyclomatic Complexity 1 include/asm-generic/atomic-instrumented.h:atomic_inc Cyclomatic Complexity 1 include/asm-generic/atomic-instrumented.h:atomic_dec_and_test Cyclomatic Complexity 5 arch/x86/include/asm/preempt.h:__preempt_count_add Cyclomatic Complexity 5 arch/x86/include/asm/preempt.h:__preempt_count_sub Cyclomatic Complexity 1 include/linux/spinlock.h:spinlock_check Cyclomatic Complexity 1 include/linux/spinlock.h:spin_lock
Cyclomatic Complexity 1 include/linux/spinlock.h:spin_unlock
Cyclomatic Complexity 1 include/linux/wait.h:waitqueue_active Cyclomatic Complexity 1 include/linux/rcupdate.h:__rcu_read_lock Cyclomatic Complexity 1 include/linux/rcupdate.h:__rcu_read_unlock Cyclomatic Complexity 1 include/linux/rcupdate.h:rcu_lock_acquire Cyclomatic Complexity 1 include/linux/rcupdate.h:rcu_lock_release Cyclomatic Complexity 1 include/linux/rcupdate.h:rcu_read_lock Cyclomatic Complexity 1 include/linux/rcupdate.h:rcu_read_unlock Cyclomatic Complexity 1 include/linux/seqlock.h:seqcount_lockdep_reader_access Cyclomatic Complexity 2 include/linux/seqlock.h:__read_seqcount_begin Cyclomatic Complexity 1 include/linux/seqlock.h:raw_read_seqcount_begin Cyclomatic Complexity 1 include/linux/seqlock.h:read_seqcount_begin Cyclomatic Complexity 1 include/linux/seqlock.h:__read_seqcount_retry Cyclomatic Complexity 1 include/linux/seqlock.h:read_seqcount_retry Cyclomatic Complexity 1 include/linux/seqlock.h:raw_write_seqcount_begin Cyclomatic Complexity 1 include/linux/seqlock.h:raw_write_seqcount_end Cyclomatic Complexity 1 include/linux/seqlock.h:write_seqcount_begin_nested Cyclomatic Complexity 1 include/linux/seqlock.h:write_seqcount_begin Cyclomatic Complexity 1 include/linux/seqlock.h:write_seqcount_end Cyclomatic Complexity 2 include/linux/dcache.h:d_real
Cyclomatic Complexity 1 include/linux/completion.h:__init_completion Cyclomatic Complexity 1 arch/x86/include/asm/topology.h:numa_node_id Cyclomatic Complexity 1 include/linux/rbtree.h:rb_link_node
Cyclomatic Complexity 1 include/linux/topology.h:numa_mem_id
Cyclomatic Complexity 1 include/linux/gfp.h:__alloc_pages
Cyclomatic Complexity 1 include/linux/gfp.h:__alloc_pages_node Cyclomatic Complexity 2 include/linux/gfp.h:alloc_pages_node
Cyclomatic Complexity 1 include/linux/refcount.h:refcount_set Cyclomatic Complexity 1 include/linux/refcount.h:refcount_read Cyclomatic Complexity 1 arch/x86/include/asm/refcount.h:refcount_inc Cyclomatic Complexity 1 arch/x86/include/asm/refcount.h:refcount_dec_and_test Cyclomatic Complexity 1 include/linux/fs.h:is_sync_kiocb
Cyclomatic Complexity 1 include/linux/fs.h:mapping_writably_mapped Cyclomatic Complexity 1 include/linux/fs.h:inode_lock
Cyclomatic Complexity 1 include/linux/fs.h:inode_unlock
Cyclomatic Complexity 1 include/linux/fs.h:inode_lock_shared
Cyclomatic Complexity 1 include/linux/fs.h:inode_unlock_shared Cyclomatic Complexity 1 include/linux/fs.h:inode_trylock
Cyclomatic Complexity 1 include/linux/fs.h:inode_trylock_shared Cyclomatic Complexity 2 include/linux/fs.h:i_size_read
Cyclomatic Complexity 1 include/linux/fs.h:i_size_write
Cyclomatic Complexity 1 include/linux/fs.h:file_inode
Cyclomatic Complexity 1 include/linux/fs.h:file_dentry
vim +1834 fs/fuse/file.c
1803
1804 static ssize_t fuse_dax_write_iter(struct kiocb *iocb, struct iov_iter *from)
1805 {
1806 struct inode *inode = file_inode(iocb->ki_filp);
1807 ssize_t ret;
1808
1809 if (iocb->ki_flags & IOCB_NOWAIT) {
1810 if (!inode_trylock(inode))
1811 return -EAGAIN;
1812 } else {
1813 inode_lock(inode);
1814 }
1815
1816 ret = generic_write_checks(iocb, from);
1817 if (ret <= 0)
1818 goto out;
1819
1820 ret = file_remove_privs(iocb->ki_filp);
1821 if (ret)
1822 goto out;
1823 /* TODO file_update_time() but we don't want metadata I/O */
1824
1825 /* TODO handle growing the file */
1826 /* Grow file here if need be. iomap_begin() does not have access
1827 * to file pointer
1828 */
1829 if (iov_iter_rw(from) == WRITE &&
1830 ((iocb->ki_pos + iov_iter_count(from)) > i_size_read(inode))) {
1831 ret = __fuse_file_fallocate(iocb->ki_filp, 0, iocb->ki_pos,
1832 iov_iter_count(from));
1833 if (ret < 0) {
> 1834 printk("fallocate(offset=0x%llx length=0x%lx)"
1835 " failed. err=%ld\n", iocb->ki_pos,
1836 iov_iter_count(from), ret);
1837 goto out;
1838 }
> 1839 pr_debug("fallocate(offset=0x%llx length=0x%lx)" 1840 " succeed. ret=%ld\n", iocb->ki_pos, iov_iter_count(from), ret);
1841 }
1842
1843 ret = dax_iomap_rw(iocb, from, &fuse_iomap_ops);
1844
1845 out:
1846 inode_unlock(inode);
1847
1848 if (ret > 0)
1849 ret = generic_write_sync(iocb, ret);
1850 return ret;
1851 }
1852
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
kbuild test robot
build botRe: [PATCH 28/52] Do fallocate() to grow file before mapping for file growing writes
In reply to Vivek GoyalI love your patch! Perhaps something to improve:
Hi Vivek,
I love your patch! Perhaps something to improve:
[auto build test WARNING on fuse/for-next]
[also build test WARNING on v4.20-rc6]
[cannot apply to next-20181210]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Vivek-Goyal/virtio-fs-shared-file-system-for-virtual-machines/20181211-103034 base: https://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git for-next config: i386-randconfig-x000-201849 (attached as .config) compiler: gcc-7 (Debian 7.3.0-1) 7.3.0 reproduce: # save the attached .config to linux build tree make ARCH=i386
All warnings (new ones prefixed by >>):
fs/fuse/file.c: In function 'fuse_dax_write_iter':
fs/fuse/file.c:1834:47: warning: format '%lx' expects argument of type 'long unsigned int', but argument 3 has type 'size_t {aka unsigned int}' [-Wformat=]
printk("fallocate(offset=0x%llx length=0x%lx)"
~~^
%x
fs/fuse/file.c:1836:4:
iov_iter_count(from), ret);
~~~~~~~~~~~~~~~~~~~~
fs/fuse/file.c:1834:11: warning: format '%ld' expects argument of type 'long int', but argument 4 has type 'ssize_t {aka int}' [-Wformat=]
printk("fallocate(offset=0x%llx length=0x%lx)"
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
fs/fuse/file.c:1835:20: note: format string is defined here
" failed. err=%ld\n", iocb->ki_pos,
~~^
%d
In file included from include/linux/kernel.h:14:0,
from include/linux/list.h:9,
from include/linux/wait.h:7,
from include/linux/wait_bit.h:8,
from include/linux/fs.h:6,
from fs/fuse/fuse_i.h:13,
from fs/fuse/file.c:9:
include/linux/kern_levels.h:5:18: warning: format '%lx' expects argument of type 'long unsigned int', but argument 3 has type 'size_t {aka unsigned int}' [-Wformat=]
#define KERN_SOH "\001" /* ASCII Start Of Header */
^
include/linux/printk.h:136:10: note: in definition of macro 'no_printk'
printk(fmt, ##__VA_ARGS__); \
^~~
include/linux/kern_levels.h:15:20: note: in expansion of macro 'KERN_SOH'
#define KERN_DEBUG KERN_SOH "7" /* debug-level messages */
^~~~~~~~
include/linux/printk.h:346:12: note: in expansion of macro 'KERN_DEBUG'
no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
^~~~~~~~~~
fs/fuse/file.c:1839:3: note: in expansion of macro 'pr_debug'
pr_debug("fallocate(offset=0x%llx length=0x%lx)"
^~~~~~~~
fs/fuse/file.c:1839:48: note: format string is defined here
pr_debug("fallocate(offset=0x%llx length=0x%lx)"
~~^
%x
In file included from include/linux/kernel.h:14:0,
from include/linux/list.h:9,
from include/linux/wait.h:7,
from include/linux/wait_bit.h:8,
from include/linux/fs.h:6,
from fs/fuse/fuse_i.h:13,
from fs/fuse/file.c:9:
>> include/linux/kern_levels.h:5:18: warning: format '%ld' expects argument of type 'long int', but argument 4 has type 'ssize_t {aka int}' [-Wformat=]
#define KERN_SOH "\001" /* ASCII Start Of Header */
^
include/linux/printk.h:136:10: note: in definition of macro 'no_printk'
printk(fmt, ##__VA_ARGS__); \
^~~
include/linux/kern_levels.h:15:20: note: in expansion of macro 'KERN_SOH'
#define KERN_DEBUG KERN_SOH "7" /* debug-level messages */
^~~~~~~~
include/linux/printk.h:346:12: note: in expansion of macro 'KERN_DEBUG'
no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
^~~~~~~~~~
fs/fuse/file.c:1839:3: note: in expansion of macro 'pr_debug'
pr_debug("fallocate(offset=0x%llx length=0x%lx)"
^~~~~~~~
fs/fuse/file.c:1840:20: note: format string is defined here
" succeed. ret=%ld\n", iocb->ki_pos, iov_iter_count(from), ret);
~~^
%d
--
fs//fuse/file.c: In function 'fuse_dax_write_iter':
fs//fuse/file.c:1834:47: warning: format '%lx' expects argument of type 'long unsigned int', but argument 3 has type 'size_t {aka unsigned int}' [-Wformat=]
printk("fallocate(offset=0x%llx length=0x%lx)"
~~^
%x
fs//fuse/file.c:1836:4:
iov_iter_count(from), ret);
~~~~~~~~~~~~~~~~~~~~
fs//fuse/file.c:1834:11: warning: format '%ld' expects argument of type 'long int', but argument 4 has type 'ssize_t {aka int}' [-Wformat=]
printk("fallocate(offset=0x%llx length=0x%lx)"
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
fs//fuse/file.c:1835:20: note: format string is defined here
" failed. err=%ld\n", iocb->ki_pos,
~~^
%d
In file included from include/linux/kernel.h:14:0,
from include/linux/list.h:9,
from include/linux/wait.h:7,
from include/linux/wait_bit.h:8,
from include/linux/fs.h:6,
from fs//fuse/fuse_i.h:13,
from fs//fuse/file.c:9:
include/linux/kern_levels.h:5:18: warning: format '%lx' expects argument of type 'long unsigned int', but argument 3 has type 'size_t {aka unsigned int}' [-Wformat=]
#define KERN_SOH "\001" /* ASCII Start Of Header */
^
include/linux/printk.h:136:10: note: in definition of macro 'no_printk'
printk(fmt, ##__VA_ARGS__); \
^~~
include/linux/kern_levels.h:15:20: note: in expansion of macro 'KERN_SOH'
#define KERN_DEBUG KERN_SOH "7" /* debug-level messages */
^~~~~~~~
include/linux/printk.h:346:12: note: in expansion of macro 'KERN_DEBUG'
no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
^~~~~~~~~~
fs//fuse/file.c:1839:3: note: in expansion of macro 'pr_debug'
pr_debug("fallocate(offset=0x%llx length=0x%lx)"
^~~~~~~~
fs//fuse/file.c:1839:48: note: format string is defined here
pr_debug("fallocate(offset=0x%llx length=0x%lx)"
~~^
%x
In file included from include/linux/kernel.h:14:0,
from include/linux/list.h:9,
from include/linux/wait.h:7,
from include/linux/wait_bit.h:8,
from include/linux/fs.h:6,
from fs//fuse/fuse_i.h:13,
from fs//fuse/file.c:9:
>> include/linux/kern_levels.h:5:18: warning: format '%ld' expects argument of type 'long int', but argument 4 has type 'ssize_t {aka int}' [-Wformat=]
#define KERN_SOH "\001" /* ASCII Start Of Header */
^
include/linux/printk.h:136:10: note: in definition of macro 'no_printk'
printk(fmt, ##__VA_ARGS__); \
^~~
include/linux/kern_levels.h:15:20: note: in expansion of macro 'KERN_SOH'
#define KERN_DEBUG KERN_SOH "7" /* debug-level messages */
^~~~~~~~
include/linux/printk.h:346:12: note: in expansion of macro 'KERN_DEBUG'
no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
^~~~~~~~~~
fs//fuse/file.c:1839:3: note: in expansion of macro 'pr_debug'
pr_debug("fallocate(offset=0x%llx length=0x%lx)"
^~~~~~~~
fs//fuse/file.c:1840:20: note: format string is defined here
" succeed. ret=%ld\n", iocb->ki_pos, iov_iter_count(from), ret);
~~^
%dvim +5 include/linux/kern_levels.h
314ba352 Joe Perches 2012-07-30 404d2c8c8 Joe Perches 2012-07-30 @5 #define KERN_SOH "\001" /* ASCII Start Of Header */04d2c8c8 Joe Perches 2012-07-30 6 #define KERN_SOH_ASCII '\001'04d2c8c8 Joe Perches 2012-07-30 7
:::::: The code at line 5 was first introduced by commit
:::::: 04d2c8c83d0e3ac5f78aeede51babb3236200112 printk: convert the format for KERN_<LEVEL> to a 2 byte pattern
:::::: TO: Joe Perches <[email protected]>
:::::: CC: Linus Torvalds <[email protected]>
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Vivek Goyal
maintainerPATCH 29/52 fuse: add DAX mmap support
In reply to Vivek GoyalFrom: Stefan Hajnoczi <[email protected]>
Add DAX mmap() support.
--- fs/fuse/file.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-)
fs/fuse/file.c +57 -1
@@ -2501,9 +2501,65 @@ static int fuse_direct_mmap(struct file *file, struct vm_area_struct *vma)return generic_file_mmap (file, vma); } +static int __fuse_dax_fault (struct vm_fault *vmf,enum page_entry_size pe_size, +bool write) +{ +int ret; +struct inode *inode =file_inode (vmf->vma->vm_file); +struct super_block *sb = inode->i_sb; +pfn_t pfn; + +if (write) +sb_start_pagefault (sb); + +/* TODO inode semaphore to protect faults vs truncate */ + + ret =dax_iomap_fault (vmf, pe_size, &pfn,NULL , &fuse_iomap_ops); + +if (ret &VM_FAULT_NEEDDSYNC ) + ret =dax_finish_sync_fault (vmf, pe_size, pfn); + +if (write) +sb_end_pagefault (sb); + +return ret; +} + +static int fuse_dax_fault (struct vm_fault *vmf) +{ +return __fuse_dax_fault (vmf,PE_SIZE_PTE , + vmf->flags &FAULT_FLAG_WRITE ); +} + +static int fuse_dax_huge_fault (struct vm_fault *vmf, +enum page_entry_size pe_size) +{ +return __fuse_dax_fault (vmf, pe_size, vmf->flags &FAULT_FLAG_WRITE ); +} + +static int fuse_dax_page_mkwrite (struct vm_fault *vmf) +{ +return __fuse_dax_fault (vmf,PE_SIZE_PTE , true); +} + +static int fuse_dax_pfn_mkwrite (struct vm_fault *vmf) +{ +return __fuse_dax_fault (vmf,PE_SIZE_PTE , true); +} + +static const struct vm_operations_struct fuse_dax_vm_ops = { + .fault = fuse_dax_fault, + .huge_fault = fuse_dax_huge_fault, + .page_mkwrite = fuse_dax_page_mkwrite, + .pfn_mkwrite = fuse_dax_pfn_mkwrite, +}; +static int fuse_dax_mmap (struct file *file,struct vm_area_struct *vma) { -return -EINVAL ;/* TODO */ +file_accessed (file); + vma->vm_ops = &fuse_dax_vm_ops; + vma->vm_flags |=VM_MIXEDMAP |VM_HUGEPAGE ; +return 0 ; } static int convert_fuse_file_lock(struct fuse_conn *fc,
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 30/52 fuse: delete dentry if timeout is zero
In reply to Vivek GoyalFrom: Miklos Szeredi <[email protected]>
Don't hold onto dentry in lru list if need to re-lookup it anyway at next access.
More advanced version of this patch would periodically flush out dentries from the lru which have gone stale.
--- fs/fuse/dir.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-)
fs/fuse/dir.c +23 -3
@@ -29,12 +29,26 @@ union fuse_dentry {struct rcu_head rcu; }; -static inline void fuse_dentry_settime (struct dentry *entry,u64 time) +static void fuse_dentry_settime (struct dentry *dentry,u64 time) { - ((union fuse_dentry *) entry->d_fsdata)->time = time; +/* +* Mess with DCACHE_OP_DELETE because dput() will be faster without it. +* Don't care about races, either way it's just an optimization +*/ +if ((time && (dentry->d_flags &DCACHE_OP_DELETE )) || + (!time && !(dentry->d_flags &DCACHE_OP_DELETE ))) { +spin_lock (&dentry->d_lock); +if (time) + dentry->d_flags &= ~DCACHE_OP_DELETE ; +else + dentry->d_flags |=DCACHE_OP_DELETE ; +spin_unlock (&dentry->d_lock); + } + + ((union fuse_dentry *) dentry->d_fsdata)->time = time; } -static inline u64 fuse_dentry_time (struct dentry *entry) +static inline u64 fuse_dentry_time (const struct dentry *entry) {return ((union fuse_dentry *) entry->d_fsdata)->time; }
@@ -270,8 +284,14 @@ static void fuse_dentry_release(struct dentry *dentry)kfree_rcu (fd, rcu); } +static int fuse_dentry_delete (const struct dentry *dentry) +{ +return time_before64 (fuse_dentry_time (dentry),get_jiffies_64 ()); +} +const struct dentry_operations fuse_dentry_operations = { .d_revalidate = fuse_dentry_revalidate, + .d_delete = fuse_dentry_delete, .d_init = fuse_dentry_init, .d_release = fuse_dentry_release, };
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 31/52 dax: Pass dax_dev to dax_writeback_mapping_range()
In reply to Vivek GoyalRight now dax_writeback_mapping_range() is passed a bdev and dax_dev is searched from that bdev name.
virtio-fs does not have a bdev. So pass in dax_dev also to dax_writeback_mapping_range(). If dax_dev is passed in, bdev is not used otherwise dax_dev is searched using bdev.
--- fs/dax.c | 16 ++++++++++------ fs/ext4/inode.c | 2 +- fs/xfs/xfs_aops.c | 2 +- include/linux/dax.h | 6 ++++-- 4 files changed, 16 insertions(+), 10 deletions(-)
fs/dax.c +10 -6
@@ -911,12 +911,12 @@ static int dax_writeback_one(struct xa_state *xas, struct dax_device *dax_dev,* on persistent storage prior to completion of the operation. */ int dax_writeback_mapping_range (struct address_space *mapping, -struct block_device *bdev,struct writeback_control *wbc) +struct block_device *bdev,struct dax_device *dax_dev, +struct writeback_control *wbc) {XA_STATE (xas, &mapping->i_pages, wbc->range_start >>PAGE_SHIFT );struct inode *inode = mapping->host;pgoff_t end_index = wbc->range_end >>PAGE_SHIFT ; -struct dax_device *dax_dev;void *entry;int ret =0 ;unsigned int scanned =0 ;
@@ -927,9 +927,12 @@ int dax_writeback_mapping_range(struct address_space *mapping,if (!mapping->nrexceptional || wbc->sync_mode !=WB_SYNC_ALL )return 0 ; - dax_dev =dax_get_by_host (bdev->bd_disk->disk_name); -if (!dax_dev) -return -EIO ; +if (bdev) { +WARN_ON (dax_dev); + dax_dev =dax_get_by_host (bdev->bd_disk->disk_name); +if (!dax_dev) +return -EIO ; + }trace_dax_writeback_range (inode, xas.xa_index, end_index);
@@ -951,7 +954,8 @@ int dax_writeback_mapping_range(struct address_space *mapping,xas_lock_irq (&xas); }xas_unlock_irq (&xas); -put_dax (dax_dev); +if (bdev) +put_dax (dax_dev);trace_dax_writeback_range_done (inode, xas.xa_index, end_index);return ret; }
fs/ext4/inode.c +1 -1
@@ -2978,7 +2978,7 @@ static int ext4_dax_writepages(struct address_space *mapping,percpu_down_read (&sbi->s_journal_flag_rwsem);trace_ext4_writepages (inode, wbc); - ret =dax_writeback_mapping_range (mapping, inode->i_sb->s_bdev, wbc); + ret =dax_writeback_mapping_range (mapping, inode->i_sb->s_bdev,NULL , wbc);trace_ext4_writepages_result (inode, wbc, ret, nr_to_write - wbc->nr_to_write);percpu_up_read (&sbi->s_journal_flag_rwsem);
fs/xfs/xfs_aops.c +1 -1
@@ -951,7 +951,7 @@ xfs_dax_writepages( {xfs_iflags_clear (XFS_I (mapping->host),XFS_ITRUNCATED );return dax_writeback_mapping_range (mapping, -xfs_find_bdev_for_inode (mapping->host), wbc); +xfs_find_bdev_for_inode (mapping->host),NULL , wbc); }STATIC int
include/linux/dax.h +4 -2
@@ -85,7 +85,8 @@ static inline void fs_put_dax(struct dax_device *dax_dev)struct dax_device *fs_dax_get_by_bdev (struct block_device *bdev);int dax_writeback_mapping_range (struct address_space *mapping, -struct block_device *bdev,struct writeback_control *wbc); +struct block_device *bdev,struct dax_device *dax_dev, +struct writeback_control *wbc);struct page *dax_layout_busy_page (struct address_space *mapping);bool dax_lock_mapping_entry (struct page *page);
@@ -117,7 +118,8 @@ static inline struct page *dax_layout_busy_page(struct address_space *mapping) }static inline int dax_writeback_mapping_range (struct address_space *mapping, -struct block_device *bdev,struct writeback_control *wbc) +struct block_device *bdev,struct dax_device *dax_dev, +struct writeback_control *wbc) {return -EOPNOTSUPP ; }
Signature · 1 line
2.13.6
kbuild test robot
build botRe: [PATCH 31/52] dax: Pass dax_dev to dax_writeback_mapping_range()
I love your patch! Yet something to improve:
Hi Vivek,
I love your patch! Yet something to improve:
[auto build test ERROR on fuse/for-next]
[also build test ERROR on v4.20-rc6]
[cannot apply to next-20181210]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Vivek-Goyal/virtio-fs-shared-file-system-for-virtual-machines/20181211-103034 base: https://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git for-next config: i386-randconfig-x006-201849 (attached as .config) compiler: gcc-7 (Debian 7.3.0-1) 7.3.0 reproduce: # save the attached .config to linux build tree make ARCH=i386
All errors (new ones prefixed by >>):
fs//ext2/inode.c: In function 'ext2_dax_writepages':
>> fs//ext2/inode.c:959:33: error: passing argument 3 of 'dax_writeback_mapping_range' from incompatible pointer type [-Werror=incompatible-pointer-types]
mapping->host->i_sb->s_bdev, wbc);
^~~
In file included from fs//ext2/inode.c:29:0:
include/linux/dax.h:120:19: note: expected 'struct dax_device *' but argument is of type 'struct writeback_control *'
static inline int dax_writeback_mapping_range(struct address_space *mapping,
^~~~~~~~~~~~~~~~~~~~~~~~~~~
>> fs//ext2/inode.c:958:9: error: too few arguments to function 'dax_writeback_mapping_range'
return dax_writeback_mapping_range(mapping,
^~~~~~~~~~~~~~~~~~~~~~~~~~~ In file included from fs//ext2/inode.c:29:0:
include/linux/dax.h:120:19: note: declared here
static inline int dax_writeback_mapping_range(struct address_space *mapping,
^~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: some warnings being treated as errors
vim +/dax_writeback_mapping_range +959 fs//ext2/inode.c
7f6d5b52 Ross Zwisler 2016-02-26 954
fb094c90 Dan Williams 2017-12-21 955 static int
fb094c90 Dan Williams 2017-12-21 956 ext2_dax_writepages(struct address_space *mapping, struct writeback_control *wbc)
fb094c90 Dan Williams 2017-12-21 957 {
fb094c90 Dan Williams 2017-12-21 @958 return dax_writeback_mapping_range(mapping,
fb094c90 Dan Williams 2017-12-21 @959 mapping->host->i_sb->s_bdev, wbc);^1da177e Linus Torvalds 2005-04-16 960 }
^1da177e Linus Torvalds 2005-04-16 961
:::::: The code at line 959 was first introduced by commit
:::::: fb094c90748fbeba1063927eeb751add147b35b9 ext2, dax: introduce ext2_dax_aops
:::::: TO: Dan Williams <[email protected]> :::::: CC: Dan Williams <[email protected]>
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
kbuild test robot
build botRe: [PATCH 31/52] dax: Pass dax_dev to dax_writeback_mapping_range()
In reply to Vivek GoyalI love your patch! Perhaps something to improve:
Hi Vivek,
I love your patch! Perhaps something to improve:
[auto build test WARNING on fuse/for-next]
[also build test WARNING on v4.20-rc6]
[cannot apply to next-20181211]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Vivek-Goyal/virtio-fs-shared-file-system-for-virtual-machines/20181211-103034 base: https://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git for-next config: arm64-allmodconfig (attached as .config) compiler: aarch64-linux-gnu-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
GCC_VERSION=7.2.0 make.cross ARCH=arm64
All warnings (new ones prefixed by >>):
fs/ext2/inode.c: In function 'ext2_dax_writepages':
fs/ext2/inode.c:959:33: error: passing argument 3 of 'dax_writeback_mapping_range' from incompatible pointer type [-Werror=incompatible-pointer-types]
mapping->host->i_sb->s_bdev, wbc);
^~~
In file included from fs/ext2/inode.c:29:0:
include/linux/dax.h:87:5: note: expected 'struct dax_device *' but argument is of type 'struct writeback_control *'
int dax_writeback_mapping_range(struct address_space *mapping,
^~~~~~~~~~~~~~~~~~~~~~~~~~~
fs/ext2/inode.c:958:9: error: too few arguments to function 'dax_writeback_mapping_range'
return dax_writeback_mapping_range(mapping,
^~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from fs/ext2/inode.c:29:0:
include/linux/dax.h:87:5: note: declared here
int dax_writeback_mapping_range(struct address_space *mapping,
^~~~~~~~~~~~~~~~~~~~~~~~~~~
>> fs/ext2/inode.c:960:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^cc1: some warnings being treated as errors
vim +960 fs/ext2/inode.c
7f6d5b52 Ross Zwisler 2016-02-26 954
fb094c90 Dan Williams 2017-12-21 955 static int
fb094c90 Dan Williams 2017-12-21 956 ext2_dax_writepages(struct address_space *mapping, struct writeback_control *wbc)
fb094c90 Dan Williams 2017-12-21 957 {
fb094c90 Dan Williams 2017-12-21 @958 return dax_writeback_mapping_range(mapping,
fb094c90 Dan Williams 2017-12-21 959 mapping->host->i_sb->s_bdev, wbc);^1da177e Linus Torvalds 2005-04-16 @960 } ^1da177e Linus Torvalds 2005-04-16 961
:::::: The code at line 960 was first introduced by commit :::::: 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 Linux-2.6.12-rc2
:::::: TO: Linus Torvalds <[email protected]> :::::: CC: Linus Torvalds <[email protected]>
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Vivek Goyal
maintainerPATCH 32/52 fuse: Define dax address space operations
In reply to Vivek GoyalThis is done along the lines of ext4 and xfs. I primarily wanted ->writepages hook at this time so that I could call into dax_writeback_mapping_range(). This in turn will decide which pfns need to be written back and call dax_flush() on those.
--- fs/fuse/file.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
fs/fuse/file.c +19 -0
@@ -2306,6 +2306,17 @@ static int fuse_writepages_fill(struct page *page,return err; } +static int fuse_dax_writepages (struct address_space *mapping, +struct writeback_control *wbc) +{ + +struct inode *inode = mapping->host; +struct fuse_conn *fc =get_fuse_conn (inode); + +return dax_writeback_mapping_range (mapping, +NULL , fc->dax_dev, wbc); +} +static int fuse_writepages (struct address_space *mapping,struct writeback_control *wbc) {
@@ -3646,6 +3657,13 @@ static const struct address_space_operations fuse_file_aops = { .write_end = fuse_write_end, }; +static const struct address_space_operations fuse_dax_file_aops = { + .writepages = fuse_dax_writepages, + .direct_IO = noop_direct_IO, + .set_page_dirty = noop_set_page_dirty, + .invalidatepage = noop_invalidatepage, +}; +void fuse_init_file_inode (struct inode *inode) {struct fuse_inode *fi =get_fuse_inode (inode);
@@ -3664,5 +3682,6 @@ void fuse_init_file_inode(struct inode *inode)if (fc->dax_dev) { inode->i_flags |=S_DAX ; inode->i_fop = &fuse_dax_file_operations; + inode->i_data.a_ops = &fuse_dax_file_aops; } }
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 33/52 fuse, dax: Take ->i_mmap_sem lock during dax page fault
In reply to Vivek GoyalWe need some kind of locking mechanism here. Normal file systems like ext4 and xfs seems to take their own semaphore to protect agains truncate while fault is going on.
We have additional requirement to protect against fuse dax memory range reclaim. When a range has been selected for reclaim, we need to make sure no other read/write/fault can try to access that memory range while reclaim is in progress. Once reclaim is complete, lock will be released and read/write/fault will trigger allocation of fresh dax range.
Taking inode_lock() is not an option in fault path as lockdep complains about circular dependencies. So define a new fuse_inode->i_mmap_sem.
--- fs/fuse/dir.c | 2 ++ fs/fuse/file.c | 17 +++++++++++++---- fs/fuse/fuse_i.h | 7 +++++++ fs/fuse/inode.c | 1 + 4 files changed, 23 insertions(+), 4 deletions(-)
fs/fuse/dir.c +2 -0
@@ -1553,8 +1553,10 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,*/ if ((is_truncate || !is_wb) &&S_ISREG (inode->i_mode) && oldsize != outarg.attr.size) { +down_write (&fi->i_mmap_sem);truncate_pagecache (inode, outarg.attr.size);invalidate_inode_pages2 (inode->i_mapping); +up_write (&fi->i_mmap_sem); }clear_bit (FUSE_I_SIZE_UNSTABLE , &fi->state);
fs/fuse/file.c +13 -4
@@ -2523,13 +2523,20 @@ static int __fuse_dax_fault(struct vm_fault *vmf, enum page_entry_size pe_size,if (write)sb_start_pagefault (sb); -/* TODO inode semaphore to protect faults vs truncate */ - +/* +* We need to serialize against not only truncate but also against +* fuse dax memory range reclaim. While a range is being reclaimed, +* we do not want any read/write/mmap to make progress and try +* to populate page cache or access memory we are trying to free. +*/ +down_read (&get_fuse_inode (inode)->i_mmap_sem); ret =dax_iomap_fault (vmf, pe_size, &pfn,NULL , &fuse_iomap_ops);if (ret &VM_FAULT_NEEDDSYNC ) ret =dax_finish_sync_fault (vmf, pe_size, pfn); +up_read (&get_fuse_inode (inode)->i_mmap_sem); +if (write)sb_end_pagefault (sb);
@@ -3476,9 +3483,11 @@ static long __fuse_file_fallocate(struct file *file, int mode,file_update_time (file); } -if (mode &FALLOC_FL_PUNCH_HOLE ) +if (mode &FALLOC_FL_PUNCH_HOLE ) { +down_write (&fi->i_mmap_sem);truncate_pagecache_range (inode, offset, offset + length -1 ); - +down_write (&fi->i_mmap_sem); + }fuse_invalidate_attr (inode); out:
fs/fuse/fuse_i.h +7 -0
@@ -211,6 +211,13 @@ struct fuse_inode {*/ struct rw_semaphore i_dmap_sem; +/** +* Can't take inode lock in fault path (leads to circular dependency). +* So take this in fuse dax fault path to make sure truncate and +* punch hole etc. can't make progress in parallel. +*/ +struct rw_semaphore i_mmap_sem; +/** Sorted rb tree of struct fuse_dax_mapping elements */ struct rb_root_cached dmap_tree;unsigned long nr_dmaps;
fs/fuse/inode.c +1 -0
@@ -85,6 +85,7 @@ static struct inode *fuse_alloc_inode(struct super_block *sb) fi->state =0 ; fi->nr_dmaps =0 ;mutex_init (&fi->mutex); +init_rwsem (&fi->i_mmap_sem);init_rwsem (&fi->i_dmap_sem); fi->forget =fuse_alloc_forget ();if (!fi->forget) {
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 34/52 fuse: Add logic to free up a memory range
In reply to Vivek GoyalAdd logic to free up a busy memory range. Freed memory range will be returned to free pool. Add a worker which can be started to select and free some busy memory ranges.
--- fs/fuse/file.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- fs/fuse/fuse_i.h | 10 ++++ fs/fuse/inode.c | 2 + 3 files changed, 159 insertions(+), 1 deletion(-)
fs/fuse/file.c +147 -1
@@ -272,7 +272,15 @@ static int fuse_setup_one_mapping(struct inode *inode,pr_debug ("fuse_setup_one_mapping() succeeded. offset=0x%llx err=%zd\n" , offset, err); -/* TODO: What locking is required here. For now, using fc->lock */ +/* +* We don't take a refernce on inode. inode is valid right now and +* when inode is going away, cleanup logic should first cleanup +* dmap entries. +* +* TODO: Do we need to ensure that we are holding inode lock +* as well. +*/ + dmap->inode = inode; dmap->start = offset; dmap->end = offset +FUSE_DAX_MEM_RANGE_SZ -1 ;/* Protected by fi->i_dmap_sem */
@@ -347,6 +355,8 @@ void fuse_removemapping(struct inode *inode)continue ; } + dmap->inode =NULL ; +/* Add it back to free ranges list */ free_dax_mapping (fc, dmap); }
@@ -3694,3 +3704,139 @@ void fuse_init_file_inode(struct inode *inode) inode->i_data.a_ops = &fuse_dax_file_aops; } } + +int fuse_dax_free_one_mapping_locked (struct fuse_conn *fc,struct inode *inode, +u64 dmap_start) +{ +int ret; +struct fuse_inode *fi =get_fuse_inode (inode); +struct fuse_dax_mapping *dmap; + +WARN_ON (!inode_is_locked (inode)); + +/* Find fuse dax mapping at file offset inode. */ + dmap =fuse_dax_interval_tree_iter_first (&fi->dmap_tree, dmap_start, + dmap_start); + +/* Range already got cleaned up by somebody else */ +if (!dmap) +return 0 ; + + ret =filemap_fdatawrite_range (inode->i_mapping, dmap->start, dmap->end); +if (ret) { +printk ("filemap_fdatawrite_range() failed. err=%d start=0x%llx," +" end=0x%llx\n" , ret, dmap->start, dmap->end); +return ret; + } + + ret =invalidate_inode_pages2_range (inode->i_mapping, + dmap->start >>PAGE_SHIFT , + dmap->end >>PAGE_SHIFT ); +/* TODO: What to do if above fails? For now, +* leave the range in place. +*/ +if (ret) { +printk ("invalidate_inode_pages2_range() failed err=%d\n" , ret); +return ret; + } + +/* Remove dax mapping from inode interval tree now */ +fuse_dax_interval_tree_remove (dmap, &fi->dmap_tree); + fi->nr_dmaps--; + +/* Cleanup dmap entry and add back to free list */ +spin_lock (&fc->lock); +list_del_init (&dmap->busy_list); +WARN_ON (fc->nr_busy_ranges ==0 ); + fc->nr_busy_ranges--; + dmap->inode =NULL ; + dmap->start = dmap->end =0 ; +__free_dax_mapping (fc, dmap); +spin_unlock (&fc->lock); + +pr_debug ("fuse: freed memory range window_offset=0x%llx," +" length=0x%llx\n" , dmap->window_offset, + dmap->length); + +return ret; +} + +/* +* Free a range of memory. +* Locking. +* 1. Take inode->i_rwsem to prever further read/write. +* 2. Take fuse_inode->i_mmap_sem to block dax faults. +* 3. Take fuse_inode->i_dmap_sem to protect interval tree. It might not +* be strictly necessary as lock 1 and 2 seem sufficient. +*/ +int fuse_dax_free_one_mapping (struct fuse_conn *fc,struct inode *inode, +u64 dmap_start) +{ +int ret; +struct fuse_inode *fi =get_fuse_inode (inode); + +inode_lock (inode); +down_write (&fi->i_mmap_sem); +down_write (&fi->i_dmap_sem); + ret =fuse_dax_free_one_mapping_locked (fc, inode, dmap_start); +up_write (&fi->i_dmap_sem); +up_write (&fi->i_mmap_sem); +inode_unlock (inode); +return ret; +} + +int fuse_dax_free_memory (struct fuse_conn *fc,unsigned long nr_to_free) +{ +struct fuse_dax_mapping *dmap, *pos; +int ret, i; +u64 dmap_start =0 , window_offset =0 ; +struct inode *inode =NULL ; + +/* Pick first busy range and free it for now*/ +for (i =0 ; i < nr_to_free; i++) { + dmap =NULL ; +spin_lock (&fc->lock); + +list_for_each_entry (pos, &fc->busy_ranges, busy_list) { + dmap = pos; + inode =igrab (dmap->inode); +/* +* This inode is going away. That will free +* up all the ranges anyway, continue to +* next range. +*/ +if (!inode) +continue ; + dmap_start = dmap->start; + window_offset = dmap->window_offset; +break ; + } +spin_unlock (&fc->lock); +if (!dmap) +return 0 ; + + ret =fuse_dax_free_one_mapping (fc, inode, dmap_start); +iput (inode); +if (ret) { +printk ("%s(window_offset=0x%llx) failed. err=%d\n" , + __func__, window_offset, ret); +return ret; + } + } +return 0 ; +} + +/* TODO: This probably should go in inode.c */ +void fuse_dax_free_mem_worker (struct work_struct *work) +{ +int ret; + struct fuse_conn *fc = container_of(work, struct fuse_conn, + dax_free_work.work); +pr_debug ("fuse: Worker to free memory called.\n" ); +pr_debug ("fuse: Worker to free memory called. nr_free_ranges=%lu" +" nr_busy_ranges=%lu\n" , fc->nr_free_ranges, + fc->nr_busy_ranges); + ret =fuse_dax_free_memory (fc,FUSE_DAX_RECLAIM_CHUNK ); +if (ret) +pr_debug ("fuse: fuse_dax_free_memory() failed with err=%d\n" , ret); +}
fs/fuse/fuse_i.h +10 -0
@@ -50,6 +50,9 @@#define FUSE_DAX_MEM_RANGE_SZ (2*1024*1024)#define FUSE_DAX_MEM_RANGE_PAGES (FUSE_DAX_MEM_RANGE_SZ/PAGE_SIZE) +/* Number of ranges reclaimer will try to free in one invocation */ +#define FUSE_DAX_RECLAIM_CHUNK (10) +/** List of active connections */ extern struct list_head fuse_conn_list;
@@ -102,6 +105,9 @@ struct fuse_forget_link {/** Translation information for file offsets to DAX window offsets */ struct fuse_dax_mapping { +/* Pointer to inode where this memory range is mapped */ +struct inode *inode; +/* Will connect in fc->free_ranges to keep track of free memory */ struct list_head list;
@@ -870,6 +876,9 @@ struct fuse_conn {unsigned long nr_busy_ranges;struct list_head busy_ranges; +/* Worker to free up memory ranges */ +struct delayed_work dax_free_work; +/* * DAX Window Free Ranges. TODO: This might not be best place to store * this free list
@@ -1244,6 +1253,7 @@ unsigned fuse_len_args(unsigned numargs, struct fuse_arg *args);* Get the next unique ID for a request */ u64 fuse_get_unique (struct fuse_iqueue *fiq); +void fuse_dax_free_mem_worker (struct work_struct *work);void fuse_removemapping (struct inode *inode);#endif /* _FS_FUSE_I_H */
fs/fuse/inode.c +2 -0
@@ -713,6 +713,7 @@ void fuse_conn_init(struct fuse_conn *fc, struct user_namespace *user_ns, fc->user_ns =get_user_ns (user_ns);INIT_LIST_HEAD (&fc->free_ranges);INIT_LIST_HEAD (&fc->busy_ranges); +INIT_DELAYED_WORK (&fc->dax_free_work, fuse_dax_free_mem_worker); }EXPORT_SYMBOL_GPL (fuse_conn_init);
@@ -721,6 +722,7 @@ void fuse_conn_put(struct fuse_conn *fc)if (refcount_dec_and_test (&fc->count)) {if (fc->destroy_req)fuse_request_free (fc->destroy_req); +flush_delayed_work (&fc->dax_free_work);if (fc->dax_dev)fuse_free_dax_mem_ranges (&fc->free_ranges);put_pid_ns (fc->pid_ns);
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 35/52 fuse: Add logic to do direct reclaim of memory
In reply to Vivek GoyalThis can be done only from same inode. Also it can be done only for read/write case and not for fault case. Reason, as of now reclaim requires holding inode_lock, fuse_inode->i_mmap_sem and fuse_inode->dmap_tree locks in that order and only read/write path will allow that (and not fault path).
--- fs/fuse/file.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 105 insertions(+), 16 deletions(-)
fs/fuse/file.c +105 -16
@@ -30,6 +30,8 @@ INTERVAL_TREE_DEFINE(struct fuse_dax_mapping,static long __fuse_file_fallocate (struct file *file,int mode,loff_t offset,loff_t length); +static struct fuse_dax_mapping *alloc_dax_mapping_reclaim (struct fuse_conn *fc, +struct inode *inode);static int fuse_send_open (struct fuse_conn *fc,u64 nodeid,struct file *file,int opcode,struct fuse_open_out *outargp)
@@ -1727,7 +1729,12 @@ static int fuse_iomap_begin(struct inode *inode, loff_t pos, loff_t length,if (pos >=i_size_read (inode)) goto iomap_hole; - alloc_dmap =alloc_dax_mapping (fc); +/* Can't do reclaim in fault path yet due to lock ordering */ +if (flags &IOMAP_FAULT ) + alloc_dmap =alloc_dax_mapping (fc); +else + alloc_dmap =alloc_dax_mapping_reclaim (fc, inode); +if (!alloc_dmap)return -EBUSY ;
@@ -3705,24 +3712,14 @@ void fuse_init_file_inode(struct inode *inode) } } -int fuse_dax_free_one_mapping_locked (struct fuse_conn *fc,struct inode *inode, -u64 dmap_start) +int fuse_dax_reclaim_dmap_locked (struct fuse_conn *fc,struct inode *inode, +struct fuse_dax_mapping *dmap) {int ret;struct fuse_inode *fi =get_fuse_inode (inode); -struct fuse_dax_mapping *dmap; - -WARN_ON (!inode_is_locked (inode)); - -/* Find fuse dax mapping at file offset inode. */ - dmap =fuse_dax_interval_tree_iter_first (&fi->dmap_tree, dmap_start, - dmap_start); - -/* Range already got cleaned up by somebody else */ -if (!dmap) -return 0 ; - ret =filemap_fdatawrite_range (inode->i_mapping, dmap->start, dmap->end); + ret =filemap_fdatawrite_range (inode->i_mapping, dmap->start, + dmap->end);if (ret) {printk ("filemap_fdatawrite_range() failed. err=%d start=0x%llx," " end=0x%llx\n" , ret, dmap->start, dmap->end);
@@ -3743,6 +3740,99 @@ int fuse_dax_free_one_mapping_locked(struct fuse_conn *fc, struct inode *inode,/* Remove dax mapping from inode interval tree now */ fuse_dax_interval_tree_remove (dmap, &fi->dmap_tree); fi->nr_dmaps--; +return 0 ; +} + +/* First first mapping in the tree and free it. */ +struct fuse_dax_mapping *fuse_dax_reclaim_first_mapping_locked ( +struct fuse_conn *fc,struct inode *inode) +{ +struct fuse_inode *fi =get_fuse_inode (inode); +struct fuse_dax_mapping *dmap; +int ret; + +/* Find fuse dax mapping at file offset inode. */ + dmap =fuse_dax_interval_tree_iter_first (&fi->dmap_tree,0 ,-1 ); +if (!dmap) +return NULL ; + + ret =fuse_dax_reclaim_dmap_locked (fc, inode, dmap); +if (ret <0 ) +return ERR_PTR (ret); + +/* Clean up dmap. Do not add back to free list */ +spin_lock (&fc->lock); +list_del_init (&dmap->busy_list); +WARN_ON (fc->nr_busy_ranges ==0 ); + fc->nr_busy_ranges--; + dmap->inode =NULL ; + dmap->start = dmap->end =0 ; +spin_unlock (&fc->lock); + +pr_debug ("fuse: reclaimed memory range window_offset=0x%llx," +" length=0x%llx\n" , dmap->window_offset, + dmap->length); +return dmap; +} + +/* +* First first mapping in the tree and free it and return it. Do not add +* it back to free pool. +* +* This is called with inode lock held. +*/ +struct fuse_dax_mapping *fuse_dax_reclaim_first_mapping (struct fuse_conn *fc, +struct inode *inode) +{ +struct fuse_inode *fi =get_fuse_inode (inode); +struct fuse_dax_mapping *dmap; + +down_write (&fi->i_mmap_sem); +down_write (&fi->i_dmap_sem); + dmap =fuse_dax_reclaim_first_mapping_locked (fc, inode); +up_write (&fi->i_dmap_sem); +up_write (&fi->i_mmap_sem); +return dmap; +} + +static struct fuse_dax_mapping *alloc_dax_mapping_reclaim (struct fuse_conn *fc, +struct inode *inode) +{ +struct fuse_dax_mapping *dmap; +struct fuse_inode *fi =get_fuse_inode (inode); + + dmap =alloc_dax_mapping (fc); +if (dmap) +return dmap; + +/* There are no mappings which can be reclaimed */ +if (!fi->nr_dmaps) +return NULL ; + +/* Try reclaim a fuse dax memory range */ +return fuse_dax_reclaim_first_mapping (fc, inode); +} + +int fuse_dax_free_one_mapping_locked (struct fuse_conn *fc,struct inode *inode, +u64 dmap_start) +{ +int ret; +struct fuse_inode *fi =get_fuse_inode (inode); +struct fuse_dax_mapping *dmap; + +WARN_ON (!inode_is_locked (inode)); + +/* Find fuse dax mapping at file offset inode. */ + dmap =fuse_dax_interval_tree_iter_first (&fi->dmap_tree, dmap_start, + dmap_start); + +/* Range already got cleaned up by somebody else */ +if (!dmap) +return 0 ; + + ret =fuse_dax_reclaim_dmap_locked (fc, inode, dmap); +if (ret <0 ) +return ret;/* Cleanup dmap entry and add back to free list */ spin_lock (&fc->lock);
@@ -3757,7 +3847,6 @@ int fuse_dax_free_one_mapping_locked(struct fuse_conn *fc, struct inode *inode,pr_debug ("fuse: freed memory range window_offset=0x%llx," " length=0x%llx\n" , dmap->window_offset, dmap->length); -return ret; }
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 36/52 fuse: Kick worker when free memory drops below 20% of total ranges
In reply to Vivek GoyalKick worker to free up some memory when number of free ranges drops below 20% of total free ranges at the time of initialization.
--- fs/fuse/file.c | 11 ++++++++++- fs/fuse/fuse_i.h | 9 +++++++++ fs/fuse/inode.c | 1 + 3 files changed, 20 insertions(+), 1 deletion(-)
fs/fuse/file.c +10 -1
@@ -186,6 +186,7 @@ static void fuse_link_write_file(struct file *file)static struct fuse_dax_mapping *alloc_dax_mapping (struct fuse_conn *fc) { +unsigned long free_threshold;struct fuse_dax_mapping *dmap =NULL ;spin_lock (&fc->lock);
@@ -193,7 +194,7 @@ static struct fuse_dax_mapping *alloc_dax_mapping(struct fuse_conn *fc)/* TODO: Add logic to try to free up memory if wait is allowed */ if (fc->nr_free_ranges <=0 ) {spin_unlock (&fc->lock); -return NULL ; + goto out_kick; }WARN_ON (list_empty (&fc->free_ranges));
@@ -204,6 +205,14 @@ static struct fuse_dax_mapping *alloc_dax_mapping(struct fuse_conn *fc)list_del_init (&dmap->list); fc->nr_free_ranges--;spin_unlock (&fc->lock); + +out_kick: +/* If number of free ranges are below threshold, start reclaim */ + free_threshold = (fc->nr_ranges *FUSE_DAX_RECLAIM_THRESHOLD )/100 ; +if (free_threshold >0 && fc->nr_free_ranges < free_threshold) { +pr_debug ("fuse: Kicking dax memory reclaim worker. nr_free_ranges=0x%ld nr_total_ranges=%ld\n" , fc->nr_free_ranges, fc->nr_ranges); +queue_delayed_work (system_long_wq, &fc->dax_free_work,0 ); + }return dmap; }
fs/fuse/fuse_i.h +9 -0
@@ -53,6 +53,13 @@/* Number of ranges reclaimer will try to free in one invocation */ #define FUSE_DAX_RECLAIM_CHUNK (10) +/* +* Dax memory reclaim threshold in percetage of total ranges. When free +* number of free ranges drops below this threshold, reclaim can trigger +* Default is 20% +* */ +#define FUSE_DAX_RECLAIM_THRESHOLD (20) +/** List of active connections */ extern struct list_head fuse_conn_list;
@@ -885,6 +892,8 @@ struct fuse_conn {*/ unsigned long nr_free_ranges;struct list_head free_ranges; + +unsigned long nr_ranges; };static inline struct fuse_conn *get_fuse_conn_super (struct super_block *sb)
fs/fuse/inode.c +1 -0
@@ -675,6 +675,7 @@ static int fuse_dax_mem_range_init(struct fuse_conn *fc,list_replace_init (&mem_ranges, &fc->free_ranges); fc->nr_free_ranges = allocated_ranges; + fc->nr_ranges = allocated_ranges;return 0 ; out_err:/* Free All allocated elements */
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 37/52 fuse: multiplex cached/direct_io/dax file operations
In reply to Vivek GoyalFrom: Miklos Szeredi <[email protected]>
--- fs/fuse/file.c | 91 ++++++++++++++++++++++++++++-------------------------- fs/splice.c | 3 +- include/linux/fs.h | 2 ++ 3 files changed, 52 insertions(+), 44 deletions(-)
fs/fuse/file.c +48 -43
@@ -22,8 +22,6 @@#include <linux/iomap.h> #include <linux/interval_tree_generic.h> -static const struct file_operations fuse_direct_io_file_operations; - INTERVAL_TREE_DEFINE(struct fuse_dax_mapping, rb, __u64, __subtree_last, START, LAST, static inline, fuse_dax_interval_tree);
@@ -381,8 +379,6 @@ void fuse_finish_open(struct inode *inode, struct file *file)struct fuse_file *ff = file->private_data;struct fuse_conn *fc =get_fuse_conn (inode); -if (ff->open_flags &FOPEN_DIRECT_IO ) - file->f_op = &fuse_direct_io_file_operations;if (!(ff->open_flags &FOPEN_KEEP_CACHE ))invalidate_inode_pages2 (inode->i_mapping);if (ff->open_flags &FOPEN_NONSEEKABLE )
@@ -1121,11 +1117,23 @@ static int fuse_readpages(struct file *file, struct address_space *mapping,return err; } + +static ssize_t fuse_direct_read_iter (struct kiocb *iocb,struct iov_iter *to); +static ssize_t fuse_dax_read_iter (struct kiocb *iocb,struct iov_iter *to); +static ssize_t fuse_file_read_iter (struct kiocb *iocb,struct iov_iter *to) { -struct inode *inode = iocb->ki_filp->f_mapping->host; +struct file *file = iocb->ki_filp; +struct fuse_file *ff = file->private_data; +struct inode *inode = file->f_mapping->host;struct fuse_conn *fc =get_fuse_conn (inode); +if (ff->open_flags &FOPEN_DIRECT_IO ) +return fuse_direct_read_iter (iocb, to); + +if (IS_DAX (inode)) +return fuse_dax_read_iter (iocb, to); +/* * In auto invalidate mode, always update attributes on read. * Otherwise, only update if we attempt to read past EOF (to ensure
@@ -1375,9 +1383,14 @@ static ssize_t fuse_perform_write(struct kiocb *iocb,return res >0 ? res : err; } +static ssize_t fuse_direct_write_iter (struct kiocb *iocb, +struct iov_iter *from); +static ssize_t fuse_dax_write_iter (struct kiocb *iocb,struct iov_iter *from); +static ssize_t fuse_file_write_iter (struct kiocb *iocb,struct iov_iter *from) {struct file *file = iocb->ki_filp; +struct fuse_file *ff = file->private_data;struct address_space *mapping = file->f_mapping;ssize_t written =0 ;ssize_t written_buffered =0 ;
@@ -1385,6 +1398,11 @@ static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from)ssize_t err;loff_t endbyte =0 ; +if (ff->open_flags &FOPEN_DIRECT_IO ) +return fuse_direct_write_iter (iocb, from); +if (IS_DAX (inode)) +return fuse_dax_write_iter (iocb, from); +if (get_fuse_conn (inode)->writeback_cache) {/* Update size (EOF optimization) and mode (SUID clearing) */ err =fuse_update_attributes (mapping->host, file);
@@ -2517,8 +2535,20 @@ static const struct vm_operations_struct fuse_file_vm_ops = { .page_mkwrite = fuse_page_mkwrite, }; +static int fuse_direct_mmap (struct file *file,struct vm_area_struct *vma); +static int fuse_dax_mmap (struct file *file,struct vm_area_struct *vma); +static int fuse_file_mmap (struct file *file,struct vm_area_struct *vma) { +struct fuse_file *ff = file->private_data; + +/* DAX mmap is superior to direct_io mmap */ +if (IS_DAX (file_inode (file))) +return fuse_dax_mmap (file, vma); + +if (ff->open_flags &FOPEN_DIRECT_IO ) +return fuse_direct_mmap (file, vma); +if ((vma->vm_flags &VM_SHARED ) && (vma->vm_flags &VM_MAYWRITE ))fuse_link_write_file (file);
@@ -2538,6 +2568,18 @@ static int fuse_direct_mmap(struct file *file, struct vm_area_struct *vma)return generic_file_mmap (file, vma); } +static ssize_t fuse_file_splice_read (struct file *in,loff_t *ppos, +struct pipe_inode_info *pipe,size_t len, +unsigned int flags) +{ +struct fuse_file *ff = in->private_data; + +if (ff->open_flags &FOPEN_DIRECT_IO ) +return default_file_splice_read (in, ppos, pipe, len, flags); +else +return generic_file_splice_read (in, ppos, pipe, len, flags); + +}static int __fuse_dax_fault (struct vm_fault *vmf,enum page_entry_size pe_size,bool write) {
@@ -3629,13 +3671,13 @@ static const struct file_operations fuse_file_operations = { .read_iter = fuse_file_read_iter, .write_iter = fuse_file_write_iter, .mmap = fuse_file_mmap, + .splice_read = fuse_file_splice_read, .open = fuse_open, .flush = fuse_flush, .release = fuse_release, .fsync = fuse_fsync, .lock = fuse_file_lock, .flock = fuse_file_flock, - .splice_read = generic_file_splice_read, .unlocked_ioctl = fuse_file_ioctl, .compat_ioctl = fuse_file_compat_ioctl, .poll = fuse_file_poll,
@@ -3643,42 +3685,6 @@ static const struct file_operations fuse_file_operations = { .copy_file_range = fuse_copy_file_range, }; -static const struct file_operations fuse_direct_io_file_operations = { - .llseek = fuse_file_llseek, - .read_iter = fuse_direct_read_iter, - .write_iter = fuse_direct_write_iter, - .mmap = fuse_direct_mmap, - .open = fuse_open, - .flush = fuse_flush, - .release = fuse_release, - .fsync = fuse_fsync, - .lock = fuse_file_lock, - .flock = fuse_file_flock, - .unlocked_ioctl = fuse_file_ioctl, - .compat_ioctl = fuse_file_compat_ioctl, - .poll = fuse_file_poll, - .fallocate = fuse_file_fallocate, -/* no splice_read */ -}; - -static const struct file_operations fuse_dax_file_operations = { - .llseek = fuse_file_llseek, - .read_iter = fuse_dax_read_iter, - .write_iter = fuse_dax_write_iter, - .mmap = fuse_dax_mmap, - .open = fuse_open, - .flush = fuse_flush, - .release = fuse_release, - .fsync = fuse_fsync, - .lock = fuse_file_lock, - .flock = fuse_file_flock, - .unlocked_ioctl = fuse_file_ioctl, - .compat_ioctl = fuse_file_compat_ioctl, - .poll = fuse_file_poll, - .fallocate = fuse_file_fallocate, -/* no splice_read */ -}; -static const struct address_space_operations fuse_file_aops = { .readpage = fuse_readpage, .writepage = fuse_writepage,
@@ -3716,7 +3722,6 @@ void fuse_init_file_inode(struct inode *inode)if (fc->dax_dev) { inode->i_flags |=S_DAX ; - inode->i_fop = &fuse_dax_file_operations; inode->i_data.a_ops = &fuse_dax_file_aops; } }
fs/splice.c +2 -1
@@ -365,7 +365,7 @@ static ssize_t kernel_readv(struct file *file, const struct kvec *vec,return res; } -static ssize_t default_file_splice_read (struct file *in,loff_t *ppos, +ssize_t default_file_splice_read (struct file *in,loff_t *ppos,struct pipe_inode_info *pipe,size_t len,unsigned int flags) {
@@ -429,6 +429,7 @@ static ssize_t default_file_splice_read(struct file *in, loff_t *ppos,iov_iter_advance (&to, copied);/* truncates and discards */ return res; } +EXPORT_SYMBOL (default_file_splice_read);/* * Send 'sd->len' bytes to socket from 'sd->file' at position 'sd->pos'
include/linux/fs.h +2 -0
@@ -3040,6 +3040,8 @@ extern void block_sync_page(struct page *page);/* fs/splice.c */ extern ssize_t generic_file_splice_read (struct file *,loff_t *,struct pipe_inode_info *,size_t ,unsigned int ); +extern ssize_t default_file_splice_read (struct file *,loff_t *, +struct pipe_inode_info *,size_t ,unsigned int );extern ssize_t iter_file_splice_write (struct pipe_inode_info *,struct file *,loff_t *,size_t ,unsigned int ); extern ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe,
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 38/52 Dispatch FORGET requests later instead of dropping them
In reply to Vivek GoyalIf virtio queue is full, then don't drop FORGET requests. Instead, wait a bit and try to dispatch these little later using a worker thread.
--- fs/fuse/virtio_fs.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 78 insertions(+), 8 deletions(-)
fs/fuse/virtio_fs.c +78 -8
@@ -22,6 +22,8 @@ static LIST_HEAD(virtio_fs_instances);struct virtio_fs_vq {struct virtqueue *vq;/* protected by fpq->lock */ struct work_struct done_work; +struct list_head queued_reqs; +struct delayed_work dispatch_work;struct fuse_dev *fud;char name[24 ]; }____cacheline_aligned_in_smp ;
@@ -53,6 +55,13 @@ struct virtio_fs {size_t window_len; }; +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; +}; +/* TODO: This should be in a PCI file somewhere */ static int virtio_pci_find_shm_cap(struct pci_dev *dev, u8 required_id,
@@ -189,6 +198,7 @@ static void virtio_fs_free_devs(struct virtio_fs *fs)continue ;flush_work (&fsvq->done_work); +flush_delayed_work (&fsvq->dispatch_work);fuse_dev_free (fsvq->fud);/* TODO need to quiesce/end_requests/decrement dev_count */ fsvq->fud =NULL ;
@@ -252,6 +262,58 @@ static void virtio_fs_hiprio_done_work(struct work_struct *work)spin_unlock (&fpq->lock); } +static void virtio_fs_dummy_dispatch_work (struct work_struct *work) +{ +return ; +} + +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 fuse_pqueue *fpq = &fsvq->fud->pq; +struct virtqueue *vq = fsvq->vq; +struct scatterlist sg; +struct scatterlist *sgs[] = {&sg}; +bool notify; +int ret; + +pr_debug ("worker virtio_fs_hiprio_dispatch_work() called.\n" ); +while (1 ) { +spin_lock (&fpq->lock); + forget =list_first_entry_or_null (&fsvq->queued_reqs, + struct virtio_fs_forget, list); +if (!forget) { +spin_unlock (&fpq->lock); +return ; + } + +list_del (&forget->list); +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 ) { +pr_debug ("virtio-fs: Could not queue FORGET: queue full. Will try later\n" ); +list_add_tail (&forget->list, &fsvq->queued_reqs); +schedule_delayed_work (&fsvq->dispatch_work, +msecs_to_jiffies (1 )); +/* TODO handle full virtqueue */ +spin_unlock (&fpq->lock); +return ; + } + + notify =virtqueue_kick_prepare (vq); +spin_unlock (&fpq->lock); + +if (notify) +virtqueue_notify (vq); +pr_debug ("worker virtio_fs_hiprio_dispatch_work() dispatched one forget request.\n" ); + } +} +/* Allocate and copy args into req->argbuf */ static int copy_args_to_argbuf (struct fuse_req *req) {
@@ -404,15 +466,24 @@ static int virtio_fs_setup_vqs(struct virtio_device *vdev,snprintf (fs->vqs[0 ].name,sizeof (fs->vqs[0 ].name),"notifications" );INIT_WORK (&fs->vqs[0 ].done_work, virtio_fs_notifications_done_work); names[0 ] = fs->vqs[0 ].name; +INIT_LIST_HEAD (&fs->vqs[0 ].queued_reqs); +INIT_DELAYED_WORK (&fs->vqs[0 ].dispatch_work, + virtio_fs_dummy_dispatch_work); callbacks[1 ] = virtio_fs_vq_done;snprintf (fs->vqs[1 ].name,sizeof (fs->vqs[1 ].name),"hiprio" ); names[1 ] = fs->vqs[1 ].name;INIT_WORK (&fs->vqs[1 ].done_work, virtio_fs_hiprio_done_work); +INIT_LIST_HEAD (&fs->vqs[1 ].queued_reqs); +INIT_DELAYED_WORK (&fs->vqs[1 ].dispatch_work, + virtio_fs_hiprio_dispatch_work);/* Initialize the requests virtqueues */ for (i =2 ; i < fs->nvqs; i++) {INIT_WORK (&fs->vqs[i].done_work, virtio_fs_requests_done_work); +INIT_DELAYED_WORK (&fs->vqs[i].dispatch_work, + virtio_fs_dummy_dispatch_work); +INIT_LIST_HEAD (&fs->vqs[i].queued_reqs);snprintf (fs->vqs[i].name,sizeof (fs->vqs[i].name),"requests.%u" , i -2 ); callbacks[i] = virtio_fs_vq_done;
@@ -718,11 +789,6 @@ static struct virtio_driver virtio_fs_driver = {#endif }; -struct virtio_fs_forget { -struct fuse_in_header ih; -struct fuse_forget_in arg; -}; -static void virtio_fs_wake_forget_and_unlock (struct fuse_iqueue *fiq) __releases(fiq->waitq.lock) {
@@ -733,6 +799,7 @@ __releases(fiq->waitq.lock)struct scatterlist *sgs[] = {&sg};struct virtio_fs *fs;struct virtqueue *vq; +struct virtio_fs_vq *fsvq;bool notify;u64 unique;int ret;
@@ -746,7 +813,7 @@ __releases(fiq->waitq.lock) unique =fuse_get_unique (fiq); fs = fiq->priv; - + fsvq = &fs->vqs[1 ];spin_unlock (&fiq->waitq.lock);/* Allocate a buffer for the request */
@@ -769,14 +836,17 @@ __releases(fiq->waitq.lock)sg_init_one (&sg, forget,sizeof (*forget));/* Enqueue the request */ - vq = fs->vqs[1 ].vq; + vq = fsvq->vq;dev_dbg (&vq->vdev->dev,"%s\n" , __func__); fpq =vq_to_fpq (vq);spin_lock (&fpq->lock); ret =virtqueue_add_sgs (vq, sgs,1 ,0 , forget,GFP_ATOMIC );if (ret <0 ) { -pr_err ("virtio-fs: dropped FORGET: queue full\n" ); +pr_debug ("virtio-fs: Could not queue FORGET: queue full. Will try later\n" ); +list_add_tail (&forget->list, &fsvq->queued_reqs); +schedule_delayed_work (&fsvq->dispatch_work, +msecs_to_jiffies (1 ));/* TODO handle full virtqueue */ spin_unlock (&fpq->lock); goto out;
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 39/52 Release file in process context
In reply to Vivek Goyalfuse_file_put(sync) can be called with sync=true/false. If sync=true, it waits for release request response and then calls iput() in the caller's context. If sync=false, it does not wait for release request response, frees the fuse_file struct immediately and req->end function does the iput().
iput() can be a problem with DAX if called in req->end context. If this is last reference to inode (VFS has let go its reference already), then iput() will clean DAX mappings as well and send REMOVEMAPPING requests and wait for completion. (All the the worker thread context which is processing fuse replies from daemon on the host).
That means it blocks worker thread and it stops processing further replies and system deadlocks.
So for now, force sync release of file in case of DAX inodes.
--- fs/fuse/file.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-)
fs/fuse/file.c +14 -1
@@ -451,6 +451,7 @@ void fuse_release_common(struct file *file, int opcode) {struct fuse_file *ff = file->private_data;struct fuse_req *req = ff->reserved_req; +bool sync = false;fuse_prepare_release (ff, file->f_flags, opcode);
@@ -471,8 +472,20 @@ void fuse_release_common(struct file *file, int opcode)* Make the release synchronous if this is a fuseblk mount, * synchronous RELEASE is allowed (and desirable) in this case * because the server can be trusted not to screw up. +* +* For DAX, fuse server is trusted. So it should be fine to +* do a sync file put. Doing async file put is creating +* problems right now because when request finish, iput() +* can lead to freeing of inode. That means it tears down +* mappings backing DAX memory and sends REMOVEMAPPING message +* to server and blocks for completion. Currently, waiting +* in req->end context deadlocks the system as same worker thread +* can't process REMOVEMAPPING reply it is waiting for. */ -fuse_file_put (ff, ff->fc->destroy_req !=NULL ); +if (IS_DAX (req->misc.release.inode) || ff->fc->destroy_req !=NULL ) + sync = true; + +fuse_file_put (ff, sync); }static int fuse_open (struct inode *inode,struct file *file)
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 40/52 fuse: Do not block on inode lock while freeing memory range
In reply to Vivek GoyalOnce we select a memory range to free, we currently block on inode lock. Do not block and use trylock instead. And move on to next memory range if trylock fails.
Reason being that in next few patches I want to enabling waiting for memmory ranges to become free in fuse_iomap_begin(). So insted of returning -EBUSY, a process will wait for a memory range to become free.
We don't want to end up in a situation where process is sleeping in iomap_begin() with inode lock held and worker is trying to free memory from same inode, resulting in deadlock.
To avoid deadlock, use trylock instead.
--- fs/fuse/file.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-)
fs/fuse/file.c +28 -8
@@ -3891,7 +3891,12 @@ int fuse_dax_free_one_mapping(struct fuse_conn *fc, struct inode *inode,int ret;struct fuse_inode *fi =get_fuse_inode (inode); -inode_lock (inode); +/* +* If process is blocked waiting for memory while holding inode +* lock, we will deadlock. So continue to free next range. +*/ +if (!inode_trylock (inode)) +return -EAGAIN ;down_write (&fi->i_mmap_sem);down_write (&fi->i_dmap_sem); ret =fuse_dax_free_one_mapping_locked (fc, inode, dmap_start);
@@ -3903,19 +3908,22 @@ int fuse_dax_free_one_mapping(struct fuse_conn *fc, struct inode *inode,int fuse_dax_free_memory (struct fuse_conn *fc,unsigned long nr_to_free) { -struct fuse_dax_mapping *dmap, *pos; -int ret, i; +struct fuse_dax_mapping *dmap, *pos, *temp; +int ret, nr_freed =0 ;u64 dmap_start =0 , window_offset =0 ;struct inode *inode =NULL ;/* Pick first busy range and free it for now*/ -for (i =0 ; i < nr_to_free; i++) { +while (1 ) { +if (nr_freed >= nr_to_free) +break ; + dmap =NULL ;spin_lock (&fc->lock); -list_for_each_entry (pos, &fc->busy_ranges, busy_list) { - dmap = pos; - inode =igrab (dmap->inode); +list_for_each_entry_safe (pos, temp, &fc->busy_ranges, + busy_list) { + inode =igrab (pos->inode);/* * This inode is going away. That will free * up all the ranges anyway, continue to
@@ -3923,6 +3931,13 @@ int fuse_dax_free_memory(struct fuse_conn *fc, unsigned long nr_to_free)*/ if (!inode)continue ; +/* +* Take this element off list and add it tail. If +* inode lock can't be obtained, this will help with +* selecting new element +*/ + dmap = pos; +list_move_tail (&dmap->busy_list, &fc->busy_ranges); dmap_start = dmap->start; window_offset = dmap->window_offset;break ;
@@ -3933,11 +3948,16 @@ int fuse_dax_free_memory(struct fuse_conn *fc, unsigned long nr_to_free) ret =fuse_dax_free_one_mapping (fc, inode, dmap_start);iput (inode); -if (ret) { +if (ret && ret != -EAGAIN ) {printk ("%s(window_offset=0x%llx) failed. err=%d\n" , __func__, window_offset, ret);return ret; } + +/* Could not get inode lock. Try next element */ +if (ret == -EAGAIN ) +continue ; + nr_freed++; }return 0 ; }
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 41/52 fuse: Reschedule dax free work if too many EAGAIN attempts
In reply to Vivek Goyalfuse_dax_free_memory() can be very cpu intensive in corner cases. For example, if one inode has consumed all the memory and a setupmapping request is pending, that means inode lock is held by request and worker thread will not get lock for a while. And given there is only one inode consuming all the dax ranges, all the attempts to acquire lock will fail.
So if there are too many inode lock failures (-EAGAIN), reschedule the worker with a 10ms delay.
--- fs/fuse/file.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)
fs/fuse/file.c +10 -2
@@ -3909,7 +3909,7 @@ int fuse_dax_free_one_mapping(struct fuse_conn *fc, struct inode *inode,int fuse_dax_free_memory (struct fuse_conn *fc,unsigned long nr_to_free) {struct fuse_dax_mapping *dmap, *pos, *temp; -int ret, nr_freed =0 ; +int ret, nr_freed =0 , nr_eagain =0 ;u64 dmap_start =0 , window_offset =0 ;struct inode *inode =NULL ;
@@ -3918,6 +3918,12 @@ int fuse_dax_free_memory(struct fuse_conn *fc, unsigned long nr_to_free)if (nr_freed >= nr_to_free)break ; +if (nr_eagain >20 ) { +queue_delayed_work (system_long_wq, &fc->dax_free_work, +msecs_to_jiffies (10 )); +return 0 ; + } + dmap =NULL ;spin_lock (&fc->lock);
@@ -3955,8 +3961,10 @@ int fuse_dax_free_memory(struct fuse_conn *fc, unsigned long nr_to_free) }/* Could not get inode lock. Try next element */ -if (ret == -EAGAIN ) +if (ret == -EAGAIN ) { + nr_eagain++;continue ; + } nr_freed++; }return 0 ;
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 42/52 fuse: Wait for memory ranges to become free
In reply to Vivek GoyalSometimes we run out of memory ranges. So in that case, wait for memory ranges to become free, instead of returning -EBUSY.
dax fault path is holding fuse_inode->i_mmap_sem and once that is being held, memory reclaim can't be done. Its not safe to wait while holding fuse_inode->i_mmap_sem for two reasons.
- Worker thread to free memory might block on fuse_inode->i_mmap_sem as well.
- This inode is holding all the memory and more memory can't be freed.
In both the cases, deadlock will ensue. So return -ENOSPC from iomap_begin() in fault path if memory can't be allocated. Drop fuse_inode->i_mmap_sem, and wait for a free range to become available and retry.
read/write path is a different story. We hold inode lock and lock ordering allows to grab fuse_inode->immap_sem, if needed. That means we can do direct reclaim in that path. But if there is no memory allocated to this inode, then direct reclaim will not work and we need to wait for a memory range to become free. So try following order.
A. Try to get a free range.
B. If not, try direct reclaim.
C. If not, wait for a memory range to become free
Here sleeping with locks held should be fine because in step B, we made sure this inode is not holding any ranges. That means other inodes are holding ranges and somebody should be able to free memory. Also, worker thread does a trylock() on inode lock. That means worker tread will not wait on this inode and move onto next memory range. Hence above sequence should be deadlock free.
--- fs/fuse/file.c | 60 +++++++++++++++++++++++++++++++++++++++++++------------- fs/fuse/fuse_i.h | 3 +++ fs/fuse/inode.c | 1 + 3 files changed, 50 insertions(+), 14 deletions(-)
fs/fuse/file.c +46 -14
@@ -220,6 +220,8 @@ static void __free_dax_mapping(struct fuse_conn *fc, {list_add_tail (&dmap->list, &fc->free_ranges); fc->nr_free_ranges++; +/* TODO: Wake up only when needed */ +wake_up (&fc->dax_range_waitq); } static void free_dax_mapping(struct fuse_conn *fc,
@@ -1770,12 +1772,18 @@ static int fuse_iomap_begin(struct inode *inode, loff_t pos, loff_t length, goto iomap_hole;/* Can't do reclaim in fault path yet due to lock ordering */ -if (flags &IOMAP_FAULT ) +if (flags &IOMAP_FAULT ) { alloc_dmap =alloc_dax_mapping (fc); -else +if (!alloc_dmap) +return -ENOSPC ; + }else { alloc_dmap =alloc_dax_mapping_reclaim (fc, inode); +if (IS_ERR (alloc_dmap)) +return PTR_ERR (alloc_dmap); + } -if (!alloc_dmap) +/* If we are here, we should have memory allocated */ +if (WARN_ON (!alloc_dmap))return -EBUSY ;/*
@@ -2596,14 +2604,24 @@ static ssize_t fuse_file_splice_read(struct file *in, loff_t *ppos,static int __fuse_dax_fault (struct vm_fault *vmf,enum page_entry_size pe_size,bool write) { -int ret; +int ret, error =0 ;struct inode *inode =file_inode (vmf->vma->vm_file);struct super_block *sb = inode->i_sb;pfn_t pfn; +struct fuse_conn *fc =get_fuse_conn (inode); +bool retry = false;if (write)sb_start_pagefault (sb); +retry: +if (retry && !(fc->nr_free_ranges >0 )) { + ret = -EINTR ; +if (wait_event_killable_exclusive (fc->dax_range_waitq, + (fc->nr_free_ranges >0 ))) + goto out; + } +/* * We need to serialize against not only truncate but also against * fuse dax memory range reclaim. While a range is being reclaimed,
@@ -2611,13 +2629,20 @@ static int __fuse_dax_fault(struct vm_fault *vmf, enum page_entry_size pe_size,* to populate page cache or access memory we are trying to free. */ down_read (&get_fuse_inode (inode)->i_mmap_sem); - ret =dax_iomap_fault (vmf, pe_size, &pfn,NULL , &fuse_iomap_ops); + ret =dax_iomap_fault (vmf, pe_size, &pfn, &error, &fuse_iomap_ops); +if ((ret &VM_FAULT_ERROR ) && error == -ENOSPC ) { + error =0 ; + retry = true; +up_read (&get_fuse_inode (inode)->i_mmap_sem); + goto retry; + }if (ret &VM_FAULT_NEEDDSYNC ) ret =dax_finish_sync_fault (vmf, pe_size, pfn);up_read (&get_fuse_inode (inode)->i_mmap_sem); +out:if (write)sb_end_pagefault (sb);
@@ -3828,16 +3853,23 @@ static struct fuse_dax_mapping *alloc_dax_mapping_reclaim(struct fuse_conn *fc,struct fuse_dax_mapping *dmap;struct fuse_inode *fi =get_fuse_inode (inode); - dmap =alloc_dax_mapping (fc); -if (dmap) -return dmap; - -/* There are no mappings which can be reclaimed */ -if (!fi->nr_dmaps) -return NULL ; +while (1 ) { + dmap =alloc_dax_mapping (fc); +if (dmap) +return dmap; -/* Try reclaim a fuse dax memory range */ -return fuse_dax_reclaim_first_mapping (fc, inode); +if (fi->nr_dmaps) +return fuse_dax_reclaim_first_mapping (fc, inode); +/* +* There are no mappings which can be reclaimed. +* Wait for one. +*/ +if (!(fc->nr_free_ranges >0 )) { +if (wait_event_killable_exclusive (fc->dax_range_waitq, + (fc->nr_free_ranges >0 ))) +return ERR_PTR (-EINTR ); + } + } } int fuse_dax_free_one_mapping_locked(struct fuse_conn *fc, struct inode *inode,
fs/fuse/fuse_i.h +3 -0
@@ -886,6 +886,9 @@ struct fuse_conn {/* Worker to free up memory ranges */ struct delayed_work dax_free_work; +/* Wait queue for a dax range to become free */ +wait_queue_head_t dax_range_waitq; +/* * DAX Window Free Ranges. TODO: This might not be best place to store * this free list
fs/fuse/inode.c +1 -0
@@ -695,6 +695,7 @@ void fuse_conn_init(struct fuse_conn *fc, struct user_namespace *user_ns,atomic_set (&fc->dev_count,1 );init_waitqueue_head (&fc->blocked_waitq);init_waitqueue_head (&fc->reserved_req_waitq); +init_waitqueue_head (&fc->dax_range_waitq);fuse_iqueue_init (&fc->iq, fiq_ops, fiq_priv);INIT_LIST_HEAD (&fc->bg_queue);INIT_LIST_HEAD (&fc->entry);
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 43/52 fuse: Take inode lock for dax inode truncation
In reply to Vivek GoyalWhen a file is opened with O_TRUNC, we need to make sure that any other DAX operation is not in progress. DAX expects i_size to be stable.
In fuse_iomap_begin() we check for i_size at multiple places and we expect i_size to not change.
Another problem is, if we setup a mapping in fuse_iomap_begin(), and file gets truncated and dax read/write happens, KVM currently hangs. It tries to fault in a page which does not exist on host (file got truncated). It probably requries fixing in KVM.
So for now, take inode lock. Once KVM is fixed, we might have to have a look at it again.
--- fs/fuse/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/fuse/file.c +1 -1
@@ -406,7 +406,7 @@ int fuse_open_common(struct inode *inode, struct file *file, bool isdir)int err;bool lock_inode = (file->f_flags &O_TRUNC ) && fc->atomic_o_trunc && - fc->writeback_cache; + (fc->writeback_cache ||IS_DAX (inode)); err =generic_file_open (inode, file);if (err)
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 44/52 fuse: Clear setuid bit even in direct I/O path
In reply to Vivek GoyalWith cache=never, we fall back to direct IO. pjdfstest chmod test 12.t was failing because if a file has setuid bit, it should be cleared if an unpriviledged user opens it for write and writes to it.
Call fuse_remove_privs() even for direct I/O path.
--- fs/fuse/file.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-)
fs/fuse/file.c +16 -4
@@ -1679,13 +1679,25 @@ static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from)/* Don't allow parallel writes to the same file */ inode_lock (inode); res =generic_write_checks (iocb, from); -if (res >0 ) - res =fuse_direct_io (&io, from, &iocb->ki_pos,FUSE_DIO_WRITE ); +if (res <0 ) + goto out_invalidate; + + res =file_remove_privs (iocb->ki_filp); +if (res) + goto out_invalidate; + + res =fuse_direct_io (&io, from, &iocb->ki_pos,FUSE_DIO_WRITE ); +if (res <0 ) + goto out_invalidate; +fuse_invalidate_attr (inode); -if (res >0 ) -fuse_write_update_size (inode, iocb->ki_pos); +fuse_write_update_size (inode, iocb->ki_pos);inode_unlock (inode); +return res; +out_invalidate: +fuse_invalidate_attr (inode); +inode_unlock (inode);return res; }
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 45/52 virtio: Free fuse devices on umount
In reply to Vivek GoyalFrom: "Dr. David Alan Gilbert" <[email protected]>
When unmounting the fs close all the fuse devices. This includes making sure the daemon gets a FUSE_DESTROY to tell it.
--- fs/fuse/fuse_i.h | 1 + fs/fuse/inode.c | 3 ++- fs/fuse/virtio_fs.c | 13 ++++++++++++- 3 files changed, 15 insertions(+), 2 deletions(-)
fs/fuse/fuse_i.h +1 -0
@@ -85,6 +85,7 @@ struct fuse_mount_data { unsigned default_permissions:1; unsigned allow_other:1; unsigned dax:1; + unsigned destroy:1;unsigned max_read;unsigned blksize;
fs/fuse/inode.c +2 -1
@@ -1263,7 +1263,7 @@ int fuse_fill_super_common(struct super_block *sb, goto err_put_root;__set_bit (FR_BACKGROUND , &init_req->flags); -if (is_bdev) { +if (mount_data->destroy) { fc->destroy_req =fuse_request_alloc (0 );if (!fc->destroy_req) goto err_free_init_req;
@@ -1339,6 +1339,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) d.fiq_ops = &fuse_dev_fiq_ops; d.fiq_priv =NULL ; d.fudptr = &file->private_data; + d.destroy = is_bdev; err =fuse_fill_super_common (sb, &d); err_fput:
fs/fuse/virtio_fs.c +12 -1
@@ -1128,6 +1128,7 @@ static int virtio_fs_fill_super(struct super_block *sb, void *data, d.fiq_ops = &virtio_fs_fiq_ops; d.fiq_priv = fs; d.fudptr = (void **)&fs->vqs[2 ].fud; + d.destroy = true;/* Send destroy request on unmount */ err =fuse_fill_super_common (sb, &d);if (err <0 )
@@ -1160,6 +1161,16 @@ static int virtio_fs_fill_super(struct super_block *sb, void *data,return err; } +static void virtio_kill_sb (struct super_block *sb) +{ +struct fuse_conn *fc =get_fuse_conn_super (sb); +fuse_kill_sb_anon (sb); +if (fc) { +struct virtio_fs *vfs = fc->iq.priv; +virtio_fs_free_devs (vfs); + } +} +static struct dentry *virtio_fs_mount (struct file_system_type *fs_type,int flags,const char *dev_name,void *raw_data)
@@ -1171,7 +1182,7 @@ static struct file_system_type virtio_fs_type = { .owner = THIS_MODULE, .name = KBUILD_MODNAME, .mount = virtio_fs_mount, - .kill_sb = fuse_kill_sb_anon, + .kill_sb = virtio_kill_sb, };static int __init virtio_fs_init (void )
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 46/52 virtio-fs: Retrieve shm capabilities for version table
In reply to Vivek GoyalFrom: "Dr. David Alan Gilbert" <[email protected]>
Retrieve the capabilities needed to find the journal and version table.
--- fs/fuse/virtio_fs.c | 26 ++++++++++++++++++++++++-- include/uapi/linux/virtio_fs.h | 2 ++ 2 files changed, 26 insertions(+), 2 deletions(-)
fs/fuse/virtio_fs.c +24 -2
@@ -589,8 +589,11 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)phys_addr_t phys_addr;size_t bar_len;int ret; -u8 have_cache, cache_bar; -u64 cache_offset, cache_len; +u8 have_cache, have_journal, have_vertab; +u8 cache_bar, journal_bar, vertab_bar; +u64 cache_offset, cache_len; +u64 journal_offset, journal_len; +u64 vertab_offset, vertab_len;if (!IS_ENABLED (CONFIG_DAX_DRIVER ))return 0 ;
@@ -619,6 +622,25 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) cache_bar, cache_len, cache_offset); } + have_journal =virtio_pci_find_shm_cap (pci_dev, +VIRTIO_FS_PCI_SHMCAP_ID_JOURNAL , + &journal_bar, &journal_offset, + &journal_len); +if (have_journal) { +dev_notice (&vdev->dev,"Journal bar: %d len: 0x%llx @ 0x%llx\n" , + journal_bar, journal_len, journal_offset); + } + + have_vertab =virtio_pci_find_shm_cap (pci_dev, +VIRTIO_FS_PCI_SHMCAP_ID_VERTAB , + &vertab_bar, &vertab_offset, + &vertab_len); +if (have_vertab) { +dev_notice (&vdev->dev,"Version table bar: %d len: 0x%llx @ 0x%llx\n" , + vertab_bar, vertab_len, vertab_offset); + } + +/* TODO handle case where device doesn't expose BAR? */ ret =pci_request_region (pci_dev, cache_bar,"virtio-fs-window" );if (ret <0 ) {
include/uapi/linux/virtio_fs.h +2 -0
@@ -40,5 +40,7 @@ struct virtio_fs_config {/* For the id field in virtio_pci_shm_cap */ #define VIRTIO_FS_PCI_SHMCAP_ID_CACHE 0 +#define VIRTIO_FS_PCI_SHMCAP_ID_VERTAB 1 +#define VIRTIO_FS_PCI_SHMCAP_ID_JOURNAL 2#endif /* _UAPI_LINUX_VIRTIO_FS_H */
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 47/52 virtio-fs: Map using the values from the capabilities
In reply to Vivek GoyalFrom: "Dr. David Alan Gilbert" <[email protected]>
Instead of assuming we had the fixed bar for the cache, use the value from the capabilities.
Use the other capabilities to map their memory.
--- fs/fuse/virtio_fs.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+)
fs/fuse/virtio_fs.c +95 -0
@@ -53,6 +53,16 @@ struct virtio_fs {void *window_kaddr;phys_addr_t window_phys_addr;size_t window_len; + +/* Version table where version numbers can be read */ +void *vertab_kaddr; +phys_addr_t vertab_phys_addr; +size_t vertab_len; + +/* Journal */ +void *journal_kaddr; +phys_addr_t journal_phys_addr; +size_t journal_len; };struct virtio_fs_forget {
@@ -684,6 +694,17 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs) } phys_addr += cache_offset; + phys_addr =pci_resource_start (pci_dev, cache_bar); + bar_len =pci_resource_len (pci_dev, cache_bar); + +if (cache_offset + cache_len > bar_len) { +dev_err (&vdev->dev, +"%s: cache bar shorter than cap offset+len\n" , + __func__); +return -EINVAL ; + } + phys_addr += cache_offset; +/* 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
@@ -710,6 +731,80 @@ static int virtio_fs_setup_dax(struct virtio_device *vdev, struct virtio_fs *fs)dev_dbg (&vdev->dev,"%s: cache kaddr 0x%px phys_addr 0x%llx len %llx\n" , __func__, fs->window_kaddr, phys_addr, cache_len); +/* +* The journal and version table should be easier since DAX doesn't +* need them +*/ +if (have_journal) { +if (journal_bar != cache_bar) { + ret =pci_request_region (pci_dev, journal_bar, +"virtio-fs-journal" ); +if (ret <0 ) { +dev_err (&vdev->dev, +"%s: failed to request journal BAR\n" , + __func__); +return ret; + } + } + + phys_addr =pci_resource_start (pci_dev, journal_bar); + bar_len =pci_resource_len (pci_dev, journal_bar); + +if (journal_offset + journal_len > bar_len) { +dev_err (&vdev->dev, +"%s: journal bar shorter than cap offset+len\n" , + __func__); +return -EINVAL ; + } + fs->journal_phys_addr = phys_addr + journal_offset; + fs->journal_len = journal_len; + + fs->journal_kaddr =devm_memremap (&pci_dev->dev, + fs->journal_phys_addr, + journal_len,MEMREMAP_WB ); +if (!fs->journal_kaddr) { +dev_err (&vdev->dev,"%s: failed to remap journal\n" , + __func__); +return -ENOMEM ; + } +dev_notice (&vdev->dev,"%s: journal at %px\n" , __func__, + fs->journal_kaddr); + } + +if (have_vertab) { +if (vertab_bar != cache_bar && + vertab_bar != journal_bar) { + ret =pci_request_region (pci_dev, vertab_bar, +"virtio-fs-vertab" ); +if (ret <0 ) { +dev_err (&vdev->dev,"%s: failed to request" +" vertab BAR\n" , __func__); +return ret; + } + } + + phys_addr =pci_resource_start (pci_dev, vertab_bar); + bar_len =pci_resource_len (pci_dev, vertab_bar); + +if (vertab_offset + vertab_len > bar_len) { +dev_err (&vdev->dev,"%s: version tab bar shorter than" +" cap offset+len\n" , __func__); +return -EINVAL ; + } + fs->vertab_phys_addr = phys_addr + vertab_offset; + fs->vertab_len = vertab_len; + fs->vertab_kaddr =devm_memremap (&pci_dev->dev, + fs->vertab_phys_addr, + vertab_len,MEMREMAP_WB ); +if (!fs->vertab_kaddr) { +dev_err (&vdev->dev,"%s: failed to remap version" +" table\n" , __func__); +return -ENOMEM ; + } +dev_notice (&vdev->dev,"%s: version table at %px\n" , + __func__, fs->vertab_kaddr); + } + fs->dax_dev =alloc_dax (fs,NULL , &virtio_fs_dax_ops);if (!fs->dax_dev)return -ENOMEM ;
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 48/52 virtio-fs: pass version table pointer to fuse
In reply to Vivek GoyalFrom: Miklos Szeredi <[email protected]>
--- fs/fuse/fuse_i.h | 12 ++++++++++++ fs/fuse/inode.c | 10 ++++++++++ fs/fuse/virtio_fs.c | 2 ++ 3 files changed, 24 insertions(+)
fs/fuse/fuse_i.h +12 -0
@@ -100,6 +100,12 @@ struct fuse_mount_data {/* fuse_dev pointer to fill in, should contain NULL on entry */ void **fudptr; + +/* version table length in bytes */ +size_t vertab_len; + +/* version table kernel address */ +void *vertab_kaddr; };/* One forget request */
@@ -898,6 +904,12 @@ struct fuse_conn {struct list_head free_ranges;unsigned long nr_ranges; + +/** Size of version table */ +uint64_t version_table_size; + +/** Shared version entry for each active inode */ +s64 *version_table; };static inline struct fuse_conn *get_fuse_conn_super (struct super_block *sb)
fs/fuse/inode.c +10 -0
@@ -52,6 +52,7 @@ MODULE_PARM_DESC(max_user_congthresh, "unprivileged user can set");#define FUSE_SUPER_MAGIC 0x65735546 +#define VERSION_TABLE_MAGIC 0x7265566465726853#define FUSE_DEFAULT_BLKSIZE 512
@@ -1215,6 +1216,15 @@ int fuse_fill_super_common(struct super_block *sb,fuse_conn_init (fc, sb->s_user_ns, mount_data->dax_dev, mount_data->fiq_ops, mount_data->fiq_priv); fc->release = fuse_free_conn; + fc->version_table_size = mount_data->vertab_len /sizeof (s64); + fc->version_table = mount_data->vertab_kaddr; + +if (fc->version_table[0 ] !=VERSION_TABLE_MAGIC ) { +pr_warn ("bad version table magic: 0x%16llx\n" , + fc->version_table[0 ]); + fc->version_table_size =0 ; + fc->version_table =NULL ; + }if (mount_data->dax_dev) { err =fuse_dax_mem_range_init (fc, mount_data->dax_dev);
fs/fuse/virtio_fs.c +2 -0
@@ -1246,6 +1246,8 @@ static int virtio_fs_fill_super(struct super_block *sb, void *data, d.fiq_priv = fs; d.fudptr = (void **)&fs->vqs[2 ].fud; d.destroy = true;/* Send destroy request on unmount */ + d.vertab_len = fs->vertab_len; + d.vertab_kaddr = fs->vertab_kaddr; err =fuse_fill_super_common (sb, &d);if (err <0 )
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 49/52 fuse: don't crash if version table is NULL
In reply to Vivek GoyalFrom: Miklos Szeredi <[email protected]>
Version table can be NULL. Do not crash.
--- fs/fuse/inode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
fs/fuse/inode.c +2 -1
@@ -1219,7 +1219,8 @@ int fuse_fill_super_common(struct super_block *sb, fc->version_table_size = mount_data->vertab_len /sizeof (s64); fc->version_table = mount_data->vertab_kaddr; -if (fc->version_table[0 ] !=VERSION_TABLE_MAGIC ) { +if (fc->version_table && fc->version_table_size >0 && + fc->version_table[0 ] !=VERSION_TABLE_MAGIC ) {pr_warn ("bad version table magic: 0x%16llx\n" , fc->version_table[0 ]); fc->version_table_size =0 ;
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 50/52 fuse: add shared version support (virtio-fs only)
In reply to Vivek GoyalFrom: Miklos Szeredi <[email protected]>
Metadata and dcache versioning support.
READDIRPLUS doesn't supply version information yet, so don't use.
--- fs/fuse/dev.c | 3 +- fs/fuse/dir.c | 244 +++++++++++++++++++++++++++++++++++++++------- fs/fuse/file.c | 53 ++++++---- fs/fuse/fuse_i.h | 25 +++-- fs/fuse/inode.c | 23 +++-- fs/fuse/readdir.c | 12 ++- include/uapi/linux/fuse.h | 5 + 7 files changed, 284 insertions(+), 81 deletions(-)
fs/fuse/dev.c +1 -2
@@ -640,8 +640,7 @@ ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args) args->out.numargs * sizeof(struct fuse_arg));fuse_request_send (fc, req); ret = req->out.h.error; -if (!ret && args->out.argvar) { -BUG_ON (args->out.numargs !=1 ); +if (!ret && args->out.argvar && args->out.numargs ==1 ) { ret = req->out.args[0 ].size; }fuse_put_request (fc, req);
fs/fuse/dir.c +207 -37
476 lines not on this page · raw message
fs/fuse/file.c +33 -20
119 lines not on this page · raw message
fs/fuse/fuse_i.h +15 -10
@@ -172,7 +172,7 @@ struct fuse_inode {u64 orig_ino;/** Version of last attribute change */ -u64 attr_version; +s64 attr_version;union {/* Write related fields (regular file only) */
@@ -223,7 +223,7 @@ struct fuse_inode {/** Miscellaneous bits describing inode state */ unsigned long state; -/** Lock for serializing lookup and readdir for back compatibility*/ +/** Lock for serializing lookup and readdir for back compatibility */ struct mutex mutex;/*
@@ -241,6 +241,9 @@ struct fuse_inode {/** Sorted rb tree of struct fuse_dax_mapping elements */ struct rb_root_cached dmap_tree;unsigned long nr_dmaps; + +/** Pointer to shared version */ +s64 *version_ptr; };/** FUSE inode state bits */
@@ -364,7 +367,7 @@ struct fuse_out {unsigned numargs;/** Array of arguments */ -struct fuse_arg args[2 ]; +struct fuse_arg args[3 ]; };/** FUSE page descriptor */
@@ -386,7 +389,7 @@ struct fuse_args {struct {unsigned argvar:1 ;unsigned numargs; -struct fuse_arg args[2 ]; +struct fuse_arg args[3 ]; } out; };
@@ -486,7 +489,7 @@ struct fuse_req {struct cuse_init_in cuse_init_in;struct {struct fuse_read_in in; -u64 attr_ver; +s64 attr_ver; } read;struct {struct fuse_write_in in;
@@ -869,7 +872,7 @@ struct fuse_conn {struct fuse_req *destroy_req;/** Version counter for attribute changes */ -u64 attr_version; +s64 attr_ctr;/** Called on final put */ void (*release)(struct fuse_conn *);
@@ -953,7 +956,7 @@ int fuse_inode_eq(struct inode *inode, void *_nodeidp);*/ struct inode *fuse_iget (struct super_block *sb,u64 nodeid,int generation,struct fuse_attr *attr, -u64 attr_valid,u64 attr_version); +u64 attr_valid,s64 attr_version);int fuse_lookup_name (struct super_block *sb,u64 nodeid,const struct qstr *name,struct fuse_entry_out *outarg,struct inode **inode);
@@ -1027,10 +1030,10 @@ void fuse_init_symlink(struct inode *inode);* Change attributes of an inode */ void fuse_change_attributes (struct inode *inode,struct fuse_attr *attr, -u64 attr_valid,u64 attr_version); +u64 attr_valid,s64 attr_version);void fuse_change_attributes_common (struct inode *inode,struct fuse_attr *attr, -u64 attr_valid); +u64 attr_valid,s64 attr_version);/** * Initialize the client device
@@ -1195,7 +1198,7 @@ void fuse_flush_writepages(struct inode *inode);void fuse_set_nowrite (struct inode *inode);void fuse_release_nowrite (struct inode *inode); -u64 fuse_get_attr_version (struct fuse_conn *fc); +s64 fuse_get_attr_version (struct inode *inode);/** * File-system tells the kernel to invalidate cache for the given node id.
@@ -1281,4 +1284,6 @@ u64 fuse_get_unique(struct fuse_iqueue *fiq);void fuse_dax_free_mem_worker (struct work_struct *work);void fuse_removemapping (struct inode *inode); +s64 fuse_update_attr_version_locked (struct inode *inode); +#endif /* _FS_FUSE_I_H */
fs/fuse/inode.c +15 -8
@@ -82,6 +82,8 @@ static struct inode *fuse_alloc_inode(struct super_block *sb) fi->nodeid =0 ; fi->nlookup =0 ; fi->attr_version =0 ; + fi->state =0 ; + fi->version_ptr =NULL ; fi->orig_ino =0 ; fi->state =0 ; fi->nr_dmaps =0 ;
@@ -153,12 +155,11 @@ static ino_t fuse_squash_ino(u64 ino64) }void fuse_change_attributes_common (struct inode *inode,struct fuse_attr *attr, -u64 attr_valid) +u64 attr_valid,s64 attr_version) {struct fuse_conn *fc =get_fuse_conn (inode);struct fuse_inode *fi =get_fuse_inode (inode); - fi->attr_version = ++fc->attr_version; fi->i_time = attr_valid;WRITE_ONCE (fi->inval_mask,0 );
@@ -193,10 +194,13 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, inode->i_mode &= ~S_ISVTX ; fi->orig_ino = attr->ino; +smp_wmb (); +WRITE_ONCE (fi->attr_version, attr_version); + }void fuse_change_attributes (struct inode *inode,struct fuse_attr *attr, -u64 attr_valid,u64 attr_version) +u64 attr_valid,s64 attr_version) {struct fuse_conn *fc =get_fuse_conn (inode);struct fuse_inode *fi =get_fuse_inode (inode);
@@ -205,14 +209,17 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,struct timespec64 old_mtime;spin_lock (&fc->lock); -if ((attr_version !=0 && fi->attr_version > attr_version) || -test_bit (FUSE_I_SIZE_UNSTABLE , &fi->state)) { +if (test_bit (FUSE_I_SIZE_UNSTABLE , &fi->state)) { +spin_unlock (&fc->lock); +return ; + } +if (attr_version !=0 && fi->attr_version > attr_version) {spin_unlock (&fc->lock);return ; } old_mtime = inode->i_mtime; -fuse_change_attributes_common (inode, attr, attr_valid); +fuse_change_attributes_common (inode, attr, attr_valid, attr_version); oldsize = inode->i_size;/*
@@ -291,7 +298,7 @@ static int fuse_inode_set(struct inode *inode, void *_nodeidp)struct inode *fuse_iget (struct super_block *sb,u64 nodeid,int generation,struct fuse_attr *attr, -u64 attr_valid,u64 attr_version) +u64 attr_valid,s64 attr_version) {struct inode *inode;struct fuse_inode *fi;
@@ -709,7 +716,7 @@ void fuse_conn_init(struct fuse_conn *fc, struct user_namespace *user_ns, fc->blocked =0 ; fc->initialized =0 ; fc->connected =1 ; - fc->attr_version =1 ; + fc->attr_ctr =1 ;get_random_bytes (&fc->scramble_key,sizeof (fc->scramble_key)); fc->pid_ns =get_pid_ns (task_active_pid_ns (current)); fc->dax_dev = dax_dev;
fs/fuse/readdir.c +8 -4
@@ -147,7 +147,7 @@ static int parse_dirfile(char *buf, size_t nbytes, struct file *file,static int fuse_direntplus_link (struct file *file,struct fuse_direntplus *direntplus, -u64 attr_version) +s64 attr_version) {struct fuse_entry_out *o = &direntplus->entry_out;struct fuse_dirent *dirent = &direntplus->dirent;
@@ -212,6 +212,9 @@ static int fuse_direntplus_link(struct file *file,return -EIO ; } +/* FIXME: translate version_ptr on reading from device... */ +/* fuse_set_version_ptr(inode, o); */ + fi =get_fuse_inode (inode);spin_lock (&fc->lock); fi->nlookup++;
@@ -231,6 +234,7 @@ static int fuse_direntplus_link(struct file *file, attr_version);if (!inode) inode =ERR_PTR (-ENOMEM ); +/* else fuse_set_version_ptr(inode, o); */ alias =d_splice_alias (inode, dentry);d_lookup_done (dentry);
@@ -250,7 +254,7 @@ static int fuse_direntplus_link(struct file *file, }static int parse_dirplusfile (char *buf,size_t nbytes,struct file *file, -struct dir_context *ctx,u64 attr_version) +struct dir_context *ctx,s64 attr_version) {struct fuse_direntplus *direntplus;struct fuse_dirent *dirent;
@@ -301,7 +305,7 @@ static int fuse_readdir_uncached(struct file *file, struct dir_context *ctx)struct inode *inode =file_inode (file);struct fuse_conn *fc =get_fuse_conn (inode);struct fuse_req *req; -u64 attr_version =0 ; +s64 attr_version =0 ;bool locked; req =fuse_get_req (fc,1 );
@@ -320,7 +324,7 @@ static int fuse_readdir_uncached(struct file *file, struct dir_context *ctx) req->pages[0 ] = page; req->page_descs[0 ].length =PAGE_SIZE ;if (plus) { - attr_version =fuse_get_attr_version (fc); + attr_version =fuse_get_attr_version (inode);fuse_read_fill (req, file, ctx->pos,PAGE_SIZE ,FUSE_READDIRPLUS ); }else {
include/uapi/linux/fuse.h +5 -0
@@ -427,6 +427,11 @@ struct fuse_entry_out {struct fuse_attr attr; }; +struct fuse_entryver_out { +uint64_t version_index; +int64_t initial_version; +}; +struct fuse_forget_in {uint64_t nlookup; };
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 51/52 fuse: shared version cleanups
In reply to Vivek GoyalFrom: Miklos Szeredi <[email protected]>
--- fs/fuse/dir.c | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-)
fs/fuse/dir.c +25 -15
@@ -253,29 +253,36 @@ static bool fuse_dentry_version_mismatch(struct dentry *dentry)return fuse_version_mismatch (inode,READ_ONCE (fude->version)); } -static void fuse_set_version_ptr (struct inode *inode, -struct fuse_entryver_out *outver) +static s64 *fuse_version_ptr (struct inode *inode, +struct fuse_entryver_out *outver) {struct fuse_conn *fc =get_fuse_conn (inode); -struct fuse_inode *fi =get_fuse_inode (inode); -if (!fc->version_table || !outver->version_index) { - fi->version_ptr =NULL ; -return ; - } +if (!fc->version_table || !outver->version_index) +return NULL ; +if (outver->version_index >= fc->version_table_size) {pr_warn_ratelimited ("version index too large (%llu >= %llu)\n" , outver->version_index, fc->version_table_size); - fi->version_ptr =NULL ; -return ; +return NULL ; } - fi->version_ptr = fc->version_table + outver->version_index; +return fc->version_table + outver->version_index; +} -pr_info ("fuse: version_ptr = %p\n" , fi->version_ptr); -pr_info ("fuse: version = %lli\n" , fi->attr_version); -pr_info ("fuse: current_version: %lli\n" , *fi->version_ptr); +static void fuse_set_version_ptr (struct inode *inode, +struct fuse_entryver_out *outver) +{ +struct fuse_inode *fi =get_fuse_inode (inode); + + fi->version_ptr =fuse_version_ptr (inode, outver); + +if (fi->version_ptr) { +pr_info ("fuse: version_ptr = %p\n" , fi->version_ptr); +pr_info ("fuse: version = %lli\n" , fi->attr_version); +pr_info ("fuse: current_version: %lli\n" , *fi->version_ptr); + } }/*
@@ -335,13 +342,16 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)if (!ret && !outarg.nodeid) ret = -ENOENT ;if (!ret) { +s64 *new_version_ptr =fuse_version_ptr (inode, &outver); + fi =get_fuse_inode (inode);if (outarg.nodeid !=get_node_id (inode)) {fuse_queue_forget (fc, forget, outarg.nodeid,1 ); goto invalid; } -if (fi->version_ptr != fc->version_table + outver.version_index) -pr_warn ("fuse_dentry_revalidate: version_ptr changed (%p -> %p)\n" , fi->version_ptr, fc->version_table + outver.version_index); +if (fi->version_ptr != new_version_ptr) { +pr_warn ("fuse_dentry_revalidate: version_ptr changed (%p -> %p)\n" , fi->version_ptr, new_version_ptr); + }spin_lock (&fc->lock); fi->nlookup++;
Signature · 1 line
2.13.6
Vivek Goyal
maintainerPATCH 52/52 fuse: fix fuse_permission() for the default_permissions case
In reply to Vivek GoyalFrom: Miklos Szeredi <[email protected]>
--- fs/fuse/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/fuse/dir.c +1 -1
@@ -1324,7 +1324,7 @@ static int fuse_permission(struct inode *inode, int mask)if (fc->default_permissions) { err = -EACCES ; -if (!refreshed && !fuse_shared_version_mismatch (inode)) +if (refreshed || !fuse_shared_version_mismatch (inode)) err =generic_permission (inode, mask);/* If permission is denied, try to refresh file
Signature · 1 line
2.13.6
kbuild test robot
build botRe: [PATCH 52/52] fuse: fix fuse_permission() for the default_permissions case
I love your patch! Yet something to improve:
Hi Miklos,
I love your patch! Yet something to improve:
[auto build test ERROR on fuse/for-next]
[cannot apply to v4.20-rc7 next-20181219]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Vivek-Goyal/virtio-fs-shared-file-system-for-virtual-machines/20181211-103034 base: https://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git for-next config: x86_64-allmodconfig (attached as .config) compiler: gcc-7 (Debian 7.3.0-1) 7.3.0 reproduce: # save the attached .config to linux build tree make ARCH=x86_64
All error/warnings (new ones prefixed by >>):
>> fs/ext2/inode.c:959:54: warning: incorrect type in argument 3 (different base types)
fs/ext2/inode.c:959:54: expected struct dax_device *dax_dev
fs/ext2/inode.c:959:54: got struct writeback_control *wbc
>> fs/ext2/inode.c:958:43: error: not enough arguments for function dax_writeback_mapping_range
fs/ext2/inode.c: In function 'ext2_dax_writepages':
fs/ext2/inode.c:959:33: error: passing argument 3 of 'dax_writeback_mapping_range' from incompatible pointer type [-Werror=incompatible-pointer-types]
mapping->host->i_sb->s_bdev, wbc);
^~~
In file included from fs/ext2/inode.c:29:0:
include/linux/dax.h:87:5: note: expected 'struct dax_device *' but argument is of type 'struct writeback_control *'
int dax_writeback_mapping_range(struct address_space *mapping,
^~~~~~~~~~~~~~~~~~~~~~~~~~~
fs/ext2/inode.c:958:9: error: too few arguments to function 'dax_writeback_mapping_range'
return dax_writeback_mapping_range(mapping,
^~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from fs/ext2/inode.c:29:0:
include/linux/dax.h:87:5: note: declared here
int dax_writeback_mapping_range(struct address_space *mapping,
^~~~~~~~~~~~~~~~~~~~~~~~~~~
fs/ext2/inode.c:960:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^ cc1: some warnings being treated as errors
--
include/linux/slab.h:332:43: warning: dubious: x & !y include/linux/slab.h:332:43: warning: dubious: x & !y include/linux/slab.h:332:43: warning: dubious: x & !y include/linux/slab.h:332:43: warning: dubious: x & !y
>> fs/fuse/file.c:3793:5: warning: symbol 'fuse_dax_reclaim_dmap_locked' was not declared. Should it be static? >> fs/fuse/file.c:3825:25: warning: symbol 'fuse_dax_reclaim_first_mapping_locked' was not declared. Should it be static? >> fs/fuse/file.c:3862:25: warning: symbol 'fuse_dax_reclaim_first_mapping' was not declared. Should it be static? >> fs/fuse/file.c:3901:5: warning: symbol 'fuse_dax_free_one_mapping_locked' was not declared. Should it be static? >> fs/fuse/file.c:3946:5: warning: symbol 'fuse_dax_free_one_mapping' was not declared. Should it be static? >> fs/fuse/file.c:3967:5: warning: symbol 'fuse_dax_free_memory' was not declared. Should it be static?
vim +958 fs/ext2/inode.c
7f6d5b52 Ross Zwisler 2016-02-26 954
fb094c90 Dan Williams 2017-12-21 955 static int
fb094c90 Dan Williams 2017-12-21 956 ext2_dax_writepages(struct address_space *mapping, struct writeback_control *wbc)
fb094c90 Dan Williams 2017-12-21 957 {
fb094c90 Dan Williams 2017-12-21 @958 return dax_writeback_mapping_range(mapping,
fb094c90 Dan Williams 2017-12-21 @959 mapping->host->i_sb->s_bdev, wbc);^1da177e Linus Torvalds 2005-04-16 960 } ^1da177e Linus Torvalds 2005-04-16 961
:::::: The code at line 958 was first introduced by commit
:::::: fb094c90748fbeba1063927eeb751add147b35b9 ext2, dax: introduce ext2_dax_aops
:::::: TO: Dan Williams <[email protected]> :::::: CC: Dan Williams <[email protected]>
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Stefan Hajnoczi
maintainerQuoted 8 lines · Vivek Goyal
Hi,
Here are RFC patches for virtio-fs. Looking for feedback on this approach.
These patches should apply on top of 4.20-rc5. We have also put code for various components here.
A draft specification for the virtio-fs device is available here:
https://stefanha.github.io/virtio/virtio-fs.html#x1-38800010 (HTML)
https://github.com/stefanha/virtio/commit/e1cac3777ef03bc9c5c8ee91bcc6ba478272e6b6
Stefan
Quoted 45 lines · Vivek Goyal
Hi,
Here are RFC patches for virtio-fs. Looking for feedback on this approach.
These patches should apply on top of 4.20-rc5. We have also put code for various components here.
Problem Description
===================
We want to be able to take a directory tree on the host and share it with guest[s]. Our goal is to be able to do it in a fast, consistent and secure manner. Our primary use case is kata containers, but it should be usable in other scenarios as well.Containers may rely on local file system semantics for shared volumes, read-write mounts that multiple containers access simultaneously. File system changes must be visible to other containers with the same consistency expected of a local file system, including mmap
MAP_SHARED.Existing Solutions
==================
We looked at existing solutions and virtio-9p already provides basic shared file system functionality although does not offer local file system semantics, causing some workloads and test suites to fail. In addition, virtio-9p performance has been an issue for Kata Containers and we believe this cannot be alleviated without major changes that do not fit into the 9P protocol.Design Overview
===============
With the goal of designing something with better performance and local file system semantics, a bunch of ideas were proposed.- Use fuse protocol (instead of 9p) for communication between guest and host. Guest kernel will be fuse client and a fuse server will run on host to serve the requests. Benchmark results (see below) are encouraging and show this approach performs well (2x to 8x improvement depending on test being run).
- For data access inside guest, mmap portion of file in QEMU address space and guest accesses this memory using dax. That way guest page cache is bypassed and there is only one copy of data (on host). This will also enable
mmap(MAP_SHARED)between guests.
Quoted 4 lines
- For metadata coherency, there is a shared memory region which contains version number associated with metadata and any guest changing metadata updates version number and other guests refresh metadata on next access. This is still experimental and implementation is not complete.
What about Windows guests or BSD ones? Is there a plan to make that work with them as well?
What about the Virtio spec? Plans to make changes there as well?
Vivek Goyal
maintainerQuoted 46 lines · Konrad Rzeszutek Wilk
Quoted 45 lines · Vivek Goyal
Hi,
Here are RFC patches for virtio-fs. Looking for feedback on this approach.
These patches should apply on top of 4.20-rc5. We have also put code for various components here.
Problem Description
===================
We want to be able to take a directory tree on the host and share it with guest[s]. Our goal is to be able to do it in a fast, consistent and secure manner. Our primary use case is kata containers, but it should be usable in other scenarios as well.Containers may rely on local file system semantics for shared volumes, read-write mounts that multiple containers access simultaneously. File system changes must be visible to other containers with the same consistency expected of a local file system, including mmap
MAP_SHARED.Existing Solutions
==================
We looked at existing solutions and virtio-9p already provides basic shared file system functionality although does not offer local file system semantics, causing some workloads and test suites to fail. In addition, virtio-9p performance has been an issue for Kata Containers and we believe this cannot be alleviated without major changes that do not fit into the 9P protocol.Design Overview
===============
With the goal of designing something with better performance and local file system semantics, a bunch of ideas were proposed.- Use fuse protocol (instead of 9p) for communication between guest and host. Guest kernel will be fuse client and a fuse server will run on host to serve the requests. Benchmark results (see below) are encouraging and show this approach performs well (2x to 8x improvement depending on test being run).
- For data access inside guest, mmap portion of file in QEMU address space and guest accesses this memory using dax. That way guest page cache is bypassed and there is only one copy of data (on host). This will also enable
mmap(MAP_SHARED)between guests.
Quoted 6 lines
Quoted 4 lines
- For metadata coherency, there is a shared memory region which contains version number associated with metadata and any guest changing metadata updates version number and other guests refresh metadata on next access. This is still experimental and implementation is not complete.
What about Windows guests or BSD ones? Is there a plan to make that work with them as well?
Hi Konrad,
I have not thought much about making it work on Windows or BSD yet. Does Fuse work with windows. I am assuming it does with BSD. As long as FUSE works, I am assuming that atleast basic mode can be made to work.
Quoted 2 lines
What about the Virtio spec? Plans to make changes there as well?
There are plans to change that. Stefan posted a proposal here.
https://lists.oasis-open.org/archives/virtio-dev/201812/msg00073.html
Thanks
Vivek
Quoted 21 lines · Vivek Goyal
Hi,
Here are RFC patches for virtio-fs. Looking for feedback on this approach.
These patches should apply on top of 4.20-rc5. We have also put code for various components here.
Problem Description
===================
We want to be able to take a directory tree on the host and share it with guest[s]. Our goal is to be able to do it in a fast, consistent and secure manner. Our primary use case is kata containers, but it should be usable in other scenarios as well.Containers may rely on local file system semantics for shared volumes, read-write mounts that multiple containers access simultaneously. File system changes must be visible to other containers with the same consistency expected of a local file system, including mmap
MAP_SHARED.
Quoted 5 lines
Existing Solutions
==================
We looked at existing solutions and virtio-9p already provides basic shared file system functionality although does not offer local file system semantics, causing some workloads and test suites to fail.
Can you elaborate on this? Is this with 9p2000.L ? We did quiet a lot of work to make sure posix test suite pass on 9p file system. Also was the mount option with cache=loose?
-aneesh
Vivek Goyal
maintainerQuoted 29 lines · Aneesh Kumar K.V
Quoted 21 lines · Vivek Goyal
Hi,
Here are RFC patches for virtio-fs. Looking for feedback on this approach.
These patches should apply on top of 4.20-rc5. We have also put code for various components here.
Problem Description
===================
We want to be able to take a directory tree on the host and share it with guest[s]. Our goal is to be able to do it in a fast, consistent and secure manner. Our primary use case is kata containers, but it should be usable in other scenarios as well.Containers may rely on local file system semantics for shared volumes, read-write mounts that multiple containers access simultaneously. File system changes must be visible to other containers with the same consistency expected of a local file system, including mmap
MAP_SHARED.Quoted 5 lines
Existing Solutions
==================
We looked at existing solutions and virtio-9p already provides basic shared file system functionality although does not offer local file system semantics, causing some workloads and test suites to fail.
Quoted 3 lines
Can you elaborate on this? Is this with 9p2000.L ? We did quiet a lot of work to make sure posix test suite pass on 9p file system. Also was the mount option with cache=loose?
Hi Aneesh,
Yes this is with 9p2000.L and cache=loose. I used following mount option.
mount -t 9p -o trans=virtio hostShared /mnt/virtio-9p/ -oversion=9p2000.L,posixacl,cache=loose
We noticed primarily two issues.
- Ran pjdfstests and a lot of them are failing. I think even kata container folks also experienced pjdfstests failures. I have never looked into details of why it is failing.
- We thought mmap(MAP_SHARED) will not work with virtio-9p when two clients are running in two different VMs and mapped same file with MAP_SHARED.
Having said that, biggest concern with virtio-9p seems to be performance. We are looking for ways to improve performance with virtio-fs. Hoping DAX can provide faster data access and fuse protocol itself seems to be faster (in primilinary testing results).
Thanks
Vivek