diff options
| author | Mark Brown <broonie@kernel.org> | 2026-10-30 10:54:17 +0000 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-10-30 10:54:17 +0000 |
| commit | e9dae0467bd37a0ecd683da960021bb0885ded7c (patch) | |
| tree | bf8f4d25992091cfbcf7125ebd602dd58ebab038 | |
| parent | 2f3fc992562134fbfcb97c23d5caeb14358f019a (diff) | |
| parent | 35ecd40c0d7e4f772d00dfec9f7077315ba5ca82 (diff) | |
| download | linux-next-e9dae0467bd37a0ecd683da960021bb0885ded7c.tar.gz linux-next-e9dae0467bd37a0ecd683da960021bb0885ded7c.zip | |
Merge branch '9p-next' of https://github.com/martinetd/linux
| -rw-r--r-- | Documentation/filesystems/9p.rst | 12 | ||||
| -rw-r--r-- | fs/9p/acl.h | 2 | ||||
| -rw-r--r-- | fs/9p/cache.c | 7 | ||||
| -rw-r--r-- | fs/9p/fid.c | 2 | ||||
| -rw-r--r-- | fs/9p/v9fs.c | 18 | ||||
| -rw-r--r-- | fs/9p/v9fs.h | 26 | ||||
| -rw-r--r-- | fs/9p/vfs_addr.c | 10 | ||||
| -rw-r--r-- | fs/9p/vfs_dentry.c | 2 | ||||
| -rw-r--r-- | fs/9p/vfs_dir.c | 32 | ||||
| -rw-r--r-- | fs/9p/vfs_file.c | 2 | ||||
| -rw-r--r-- | fs/9p/vfs_inode.c | 26 | ||||
| -rw-r--r-- | fs/9p/vfs_inode_dotl.c | 36 | ||||
| -rw-r--r-- | fs/9p/vfs_super.c | 2 | ||||
| -rw-r--r-- | include/net/9p/9p.h | 4 | ||||
| -rw-r--r-- | include/net/9p/client.h | 14 | ||||
| -rw-r--r-- | net/9p/Kconfig | 10 | ||||
| -rw-r--r-- | net/9p/client.c | 3 | ||||
| -rw-r--r-- | net/9p/protocol.c | 12 | ||||
| -rw-r--r-- | net/9p/trans_fd.c | 109 | ||||
| -rw-r--r-- | net/9p/trans_rdma.c | 2 | ||||
| -rw-r--r-- | net/9p/trans_usbg.c | 99 | ||||
| -rw-r--r-- | net/9p/trans_virtio.c | 5 | ||||
| -rw-r--r-- | net/9p/trans_xen.c | 75 |
23 files changed, 369 insertions, 141 deletions
diff --git a/Documentation/filesystems/9p.rst b/Documentation/filesystems/9p.rst index 3f65db648db0..a791684627b6 100644 --- a/Documentation/filesystems/9p.rst +++ b/Documentation/filesystems/9p.rst @@ -50,6 +50,14 @@ mount points. Each 9P export is seen by the client as a virtio device with an associated "mount_tag" property. Available mount tags can be seen by reading /sys/bus/virtio/drivers/9pnet_virtio/virtio<n>/mount_tag files. +For server accessible over vsock (VM sockets):: + + mount -t 9p -o trans=vsock[,port=<port>] <CID> /mnt/9 + +where CID is the vsock Context ID of the remote endpoint (2 for host from +a guest VM, 3+ for guest VMs from the host). The port to connect to can be +specified with the port option (default: 564). + USBG Usage ========== @@ -117,6 +125,8 @@ Options tcp specifying a normal TCP/IP connection fd used passed file descriptors for connection (see rfdno and wfdno) + vsock connect over VM sockets (vsock) using + CID as the mount source virtio connect to the next virtio channel available (from QEMU with trans_virtio module) rdma connect to a specified RDMA channel @@ -228,7 +238,7 @@ Options any v9fs does single attach and performs all operations as one user - clien + client ACL based access check on the 9p client side for access validation diff --git a/fs/9p/acl.h b/fs/9p/acl.h index 2d1b24abcd3f..d123ab613317 100644 --- a/fs/9p/acl.h +++ b/fs/9p/acl.h @@ -51,4 +51,4 @@ static inline int v9fs_acl_mode(struct inode *dir, umode_t *modep, } #endif -#endif /* FS_9P_XATTR_H */ +#endif /* FS_9P_ACL_H */ diff --git a/fs/9p/cache.c b/fs/9p/cache.c index 12c0ae29f185..194a912158ac 100644 --- a/fs/9p/cache.c +++ b/fs/9p/cache.c @@ -20,17 +20,14 @@ int v9fs_cache_session_get_cookie(struct v9fs_session_info *v9ses, const char *dev_name) { struct fscache_volume *vcookie; - char *name, *p; + char *name; name = kasprintf(GFP_KERNEL, "9p,%s,%s", dev_name, v9ses->cachetag ?: v9ses->aname); if (!name) return -ENOMEM; - for (p = name; *p; p++) - if (*p == '/') - *p = ';'; - + strreplace(name, '/', ';'); vcookie = fscache_acquire_volume(name, NULL, NULL, 0); p9_debug(P9_DEBUG_FSC, "session %p get volume %p (%s)\n", v9ses, vcookie, name); diff --git a/fs/9p/fid.c b/fs/9p/fid.c index 76242d450aa7..65dc7972b396 100644 --- a/fs/9p/fid.c +++ b/fs/9p/fid.c @@ -154,7 +154,7 @@ static int build_path_from_dentry(struct v9fs_session_info *v9ses, for (ds = dentry; !IS_ROOT(ds); ds = ds->d_parent) n++; - wnames = kmalloc_array(n, sizeof(char *), GFP_KERNEL); + wnames = kmalloc_array(n, sizeof(*wnames), GFP_KERNEL); if (!wnames) goto err_out; diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 274c5157135d..b30ca5145fae 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -17,6 +17,7 @@ #include <linux/fs_context.h> #include <linux/slab.h> #include <linux/seq_file.h> +#include <linux/sysfs.h> #include <net/9p/9p.h> #include <net/9p/client.h> #include <net/9p/transport.h> @@ -406,7 +407,7 @@ int v9fs_parse_param(struct fs_context *fc, struct fs_parameter *param) break; case Opt_privport: fd_opts->privport = true; - rdma_opts->port = true; + rdma_opts->privport = true; break; } @@ -617,21 +618,14 @@ static ssize_t caches_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - ssize_t n = 0, count = 0, limit = PAGE_SIZE; + ssize_t count = 0; struct v9fs_session_info *v9ses; spin_lock(&v9fs_sessionlist_lock); list_for_each_entry(v9ses, &v9fs_sessionlist, slist) { - if (v9ses->cachetag) { - n = snprintf(buf + count, limit, "%s\n", v9ses->cachetag); - if (n < 0) { - count = n; - break; - } - - count += n; - limit -= n; - } + if (v9ses->cachetag) + count += sysfs_emit_at(buf, count, "%s\n", + v9ses->cachetag); } spin_unlock(&v9fs_sessionlist_lock); diff --git a/fs/9p/v9fs.h b/fs/9p/v9fs.h index e26e9811875b..7603d0960689 100644 --- a/fs/9p/v9fs.h +++ b/fs/9p/v9fs.h @@ -21,20 +21,16 @@ * @V9FS_ACCESS_SINGLE: only the mounting user can access the hierarchy * @V9FS_ACCESS_USER: a new attach will be issued for every user (default) * @V9FS_ACCESS_CLIENT: Just like user, but access check is performed on client. - * @V9FS_ACCESS_ANY: use a single attach for all users - * @V9FS_ACCESS_MASK: bit mask of different ACCESS options * @V9FS_POSIX_ACL: POSIX ACLs are enforced + * @V9FS_NO_XATTR: do not offer xattr functions on this mount + * @V9FS_IGNORE_QV: ignore qid.version==0 as a marker to disable data cache + * @V9FS_DIRECT_IO: disable data read/write cache + * @V9FS_SYNC: disable data write cache * @V9FS_NDENTRY_TIMEOUT_SET: Has negative dentry timeout retention time been * overridden by negtimeout mount option * * Session flags reflect options selected by users at mount time */ -#define V9FS_ACCESS_ANY (V9FS_ACCESS_SINGLE | \ - V9FS_ACCESS_USER | \ - V9FS_ACCESS_CLIENT) -#define V9FS_ACCESS_MASK V9FS_ACCESS_ANY -#define V9FS_ACL_MASK V9FS_POSIX_ACL - enum p9_session_flags { V9FS_PROTO_2000U = 0x01, V9FS_PROTO_2000L = 0x02, @@ -50,6 +46,20 @@ enum p9_session_flags { }; /** + * V9FS_ACCESS_ANY - use a single attach for all users + */ +#define V9FS_ACCESS_ANY (V9FS_ACCESS_SINGLE | \ + V9FS_ACCESS_USER | \ + V9FS_ACCESS_CLIENT) + +/** + * V9FS_ACCESS_MASK - bit mask of different ACCESS options + */ +#define V9FS_ACCESS_MASK V9FS_ACCESS_ANY + +#define V9FS_ACL_MASK V9FS_POSIX_ACL + +/** * enum p9_cache_shortcuts - human readable cache preferences * @CACHE_SC_NONE: disable all caches * @CACHE_SC_READAHEAD: only provide caching for readahead diff --git a/fs/9p/vfs_addr.c b/fs/9p/vfs_addr.c index 170a2b91c5f0..90756b2c3fdd 100644 --- a/fs/9p/vfs_addr.c +++ b/fs/9p/vfs_addr.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * This file contians vfs address (mmap) ops for 9P2000. + * This file contains vfs address (mmap) ops for 9P2000. * * Copyright (C) 2005 by Eric Van Hensbergen <ericvh@gmail.com> * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> @@ -99,6 +99,7 @@ static void v9fs_issue_read(struct netfs_io_subrequest *subreq) char *target; unsigned long long pos = subreq->start + subreq->transferred; int total = 0, err, len, n; + size_t to_read = iov_iter_count(&subreq->io_iter); if (S_ISLNK(rreq->inode->i_mode)) { /* p9_client_readlink() must not be called for legacy protocols @@ -131,6 +132,8 @@ fill_subreq: __set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags); if (pos + total >= i_size_read(rreq->inode)) __set_bit(NETFS_SREQ_HIT_EOF, &subreq->flags); + if (!err && !total && to_read) + __set_bit(NETFS_SREQ_HIT_EOF, &subreq->flags); if (!err && total) { subreq->transferred += total; __set_bit(NETFS_SREQ_MADE_PROGRESS, &subreq->flags); @@ -165,13 +168,16 @@ static int v9fs_init_request(struct netfs_io_request *rreq, struct file *file) goto no_fid; p9_fid_get(fid); } else if (S_ISLNK(rreq->inode->i_mode)) { + /* racing unlink/rename can make this fail with -ENOENT; + * that's expected, not a kernel bug, so don't WARN + */ dentry = d_find_any_alias(rreq->inode); if (!dentry) goto no_fid; fid = v9fs_fid_lookup(dentry); dput(dentry); if (IS_ERR(fid)) - goto no_fid; + return PTR_ERR(fid); } else { fid = v9fs_fid_find_inode(rreq->inode, writing, INVALID_UID, true); if (!fid) diff --git a/fs/9p/vfs_dentry.c b/fs/9p/vfs_dentry.c index fa6b7143db98..5b05fa7bbc06 100644 --- a/fs/9p/vfs_dentry.c +++ b/fs/9p/vfs_dentry.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * This file contians vfs dentry ops for the 9P2000 protocol. + * This file contains vfs dentry ops for the 9P2000 protocol. * * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> diff --git a/fs/9p/vfs_dir.c b/fs/9p/vfs_dir.c index 323f85352f6a..b561f4ce020e 100644 --- a/fs/9p/vfs_dir.c +++ b/fs/9p/vfs_dir.c @@ -85,7 +85,6 @@ static struct p9_rdir *v9fs_alloc_rdir_buf(struct file *filp, int buflen) static int v9fs_dir_readdir(struct file *file, struct dir_context *ctx) { - bool over; struct p9_wstat st; int err = 0; struct p9_fid *fid; @@ -125,6 +124,9 @@ static int v9fs_dir_readdir(struct file *file, struct dir_context *ctx) rdir->offset = ctx->pos; } while (rdir->head < rdir->tail) { + bool over = false; + size_t namelen; + err = p9stat_read(fid->clnt, rdir->buf + rdir->head, rdir->tail - rdir->head, &st); if (err <= 0) { @@ -132,8 +134,15 @@ static int v9fs_dir_readdir(struct file *file, struct dir_context *ctx) return -EIO; } - over = !dir_emit(ctx, st.name, strlen(st.name), - QID2INO(&st.qid), dt_type(&st)); + namelen = strlen(st.name); + if (namelen > NAME_MAX) { + p9_debug(P9_DEBUG_ERROR, + "skip dentry: name length %zu > NAME_MAX\n", + namelen); + } else { + over = !dir_emit(ctx, st.name, namelen, + QID2INO(&st.qid), dt_type(&st)); + } p9stat_free(&st); if (over) return 0; @@ -184,6 +193,7 @@ static int v9fs_dir_readdir_dotl(struct file *file, struct dir_context *ctx) } while (rdir->head < rdir->tail) { + size_t namelen; err = p9dirent_read(fid->clnt, rdir->buf + rdir->head, rdir->tail - rdir->head, @@ -193,11 +203,19 @@ static int v9fs_dir_readdir_dotl(struct file *file, struct dir_context *ctx) return -EIO; } - if (!dir_emit(ctx, curdirent.d_name, - strlen(curdirent.d_name), - QID2INO(&curdirent.qid), - curdirent.d_type)) + namelen = strlen(curdirent.d_name); + if (namelen > NAME_MAX) { + p9_debug(P9_DEBUG_ERROR, + "skip dentry: name length %zu > NAME_MAX\n", + namelen); + } else if (!dir_emit(ctx, curdirent.d_name, namelen, + QID2INO(&curdirent.qid), + curdirent.d_type)) { + kfree(curdirent.d_name); return 0; + } + + kfree(curdirent.d_name); ctx->pos = curdirent.d_off; rdir->head += err; diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c index cddfb4b7ce4e..a983ef866713 100644 --- a/fs/9p/vfs_file.c +++ b/fs/9p/vfs_file.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * This file contians vfs file ops for 9P2000. + * This file contains vfs file ops for 9P2000. * * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 537cb22ac48b..73776416902c 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -481,7 +481,7 @@ static int v9fs_at_to_dotl_flags(int flags) } /** - * v9fs_dec_count - helper functon to drop i_nlink. + * v9fs_dec_count - helper function to drop i_nlink. * * If a directory had nlink <= 2 (including . and ..), then we should not drop * the link count, which indicates the underlying exported fs doesn't maintain @@ -495,6 +495,12 @@ static int v9fs_at_to_dotl_flags(int flags) * nothing and races with concurrent metadata fetches that may already * have observed the post-unlink value (nlink == 0). * + * The same applies to a regular file whose cached nlink has already been + * driven to zero by the server: in the cached modes the attributes come + * from GETATTR and are applied verbatim, so decrementing a link count the + * server does not maintain only trips the drop_nlink() WARN_ON and + * underflows i_nlink. + * * @inode: inode whose nlink is being dropped */ static void v9fs_dec_count(struct inode *inode) @@ -503,6 +509,8 @@ static void v9fs_dec_count(struct inode *inode) if (!(v9ses->cache & (CACHE_META | CACHE_LOOSE))) return; + if (inode->i_nlink == 0) + return; if (!S_ISDIR(inode->i_mode) || inode->i_nlink > 2) drop_nlink(inode); } @@ -673,8 +681,8 @@ v9fs_vfs_create(const struct mnt_idmap *idmap, struct inode *dir, /** * v9fs_vfs_mkdir - VFS mkdir hook to create a directory * @idmap: idmap of the mount - * @dir: inode that is being unlinked - * @dentry: dentry that is being unlinked + * @dir: inode of the parent directory + * @dentry: dentry representing the directory being created * @mode: mode for new directory * */ @@ -802,6 +810,16 @@ v9fs_vfs_atomic_open(struct inode *dir, struct dentry *dentry, v9fs_invalidate_inode_attr(dir); inode = d_inode(dentry); v9inode = V9FS_I(inode); + if (!S_ISREG(inode->i_mode)) { + /* + * Not a regular file. v9fs_create() already stored the + * unopened fid on the dentry. Clunk the opened fid from + * the create and let the VFS call ->open. + */ + p9_fid_put(fid); + file->f_mode |= FMODE_CREATED; + return finish_no_open(file, NULL); + } err = finish_open(file, dentry, generic_file_open); if (unlikely(err)) { p9_fid_put(fid); @@ -1134,7 +1152,7 @@ v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode, if (v9fs_proto_dotu(v9ses)) { unsigned int i_nlink; /* - * Hadlink support got added later to the .u extension. + * Hardlink support got added later to the .u extension. * So there can be a server out there that doesn't * support this even with .u extension. That would * just leave us with stat->extension being an empty diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index c6afc9c1cf58..b224a0893177 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -312,20 +312,40 @@ v9fs_vfs_atomic_open_dotl(struct inode *dir, struct dentry *dentry, v9fs_fid_add(dentry, &fid); d_instantiate(dentry, inode); + if (!S_ISREG(inode->i_mode)) { + /* + * Not a regular file. The unopened fid is already on the + * dentry. Clunk the opened create fid and let the VFS call + * ->open. + */ + p9_fid_put(ofid); + ofid = NULL; + file->f_mode |= FMODE_CREATED; + err = finish_no_open(file, NULL); + goto out; + } /* Since we are opening a file, assign the open fid to the file */ err = finish_open(file, dentry, generic_file_open); if (err) goto out; - file->private_data = ofid; + /* + * Special files have type-specific fops that own + * file->private_data. The create fid is unused + * there: leave it for p9_fid_put() on the out + * path so it is clunked before we return. + */ + if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)) { + file->private_data = ofid; #ifdef CONFIG_9P_FSCACHE - if (v9ses->cache & CACHE_FSCACHE) { - struct v9fs_inode *v9inode = V9FS_I(inode); - fscache_use_cookie(v9fs_inode_cookie(v9inode), - file->f_mode & FMODE_WRITE); - } + if (v9ses->cache & CACHE_FSCACHE) { + struct v9fs_inode *v9inode = V9FS_I(inode); + fscache_use_cookie(v9fs_inode_cookie(v9inode), + file->f_mode & FMODE_WRITE); + } #endif - v9fs_fid_add_modes(ofid, v9ses->flags, v9ses->cache, flags); - v9fs_open_fid_add(inode, &ofid); + v9fs_fid_add_modes(ofid, v9ses->flags, v9ses->cache, flags); + v9fs_open_fid_add(inode, &ofid); + } file->f_mode |= FMODE_CREATED; out: p9_fid_put(dfid); diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c index 79526044685c..7b67193d9210 100644 --- a/fs/9p/vfs_super.c +++ b/fs/9p/vfs_super.c @@ -34,7 +34,7 @@ static const struct super_operations v9fs_super_ops, v9fs_super_ops_dotl; static int v9fs_fill_super(struct super_block *sb) { int ret; - struct v9fs_session_info *v9ses = v9ses = sb->s_fs_info; + struct v9fs_session_info *v9ses = sb->s_fs_info; sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_blocksize_bits = fls(v9ses->maxdata - 1); diff --git a/include/net/9p/9p.h b/include/net/9p/9p.h index fd7a034b8278..067e8a3b38d0 100644 --- a/include/net/9p/9p.h +++ b/include/net/9p/9p.h @@ -120,7 +120,7 @@ void _p9_debug(enum p9_debug_flags level, const char *func, * @P9_RCREATE: response with file access information * @P9_TREAD: request to transfer data from a file or directory * @P9_RREAD: response with data requested - * @P9_TWRITE: reuqest to transfer data to a file + * @P9_TWRITE: request to transfer data to a file * @P9_RWRITE: response with out much data was transferred to file * @P9_TCLUNK: forget about a handle to an entity within the file system * @P9_RCLUNK: response when server has forgotten about the handle @@ -581,6 +581,7 @@ struct p9_rstatfs { struct p9_fcall { u32 size; u8 id; + bool zc; u16 tag; size_t offset; @@ -588,7 +589,6 @@ struct p9_fcall { struct kmem_cache *cache; u8 *sdata; - bool zc; }; int p9_errstr2errno(char *errstr, int len); diff --git a/include/net/9p/client.h b/include/net/9p/client.h index 55c6cb54bd25..e4e5cb102fb1 100644 --- a/include/net/9p/client.h +++ b/include/net/9p/client.h @@ -97,7 +97,7 @@ struct p9_req_t { * @proto_version: 9P protocol version to use * @trans_mod: module API instantiated with this client * @status: connection state - * @trans: tranport instance state and API + * @trans: transport instance state and API * @fids: All active FID handles * @reqs: All active requests. * @name: node name used as client id @@ -122,8 +122,12 @@ struct p9_client { struct { u16 port; bool privport; - } tcp; +#if IS_ENABLED(CONFIG_NET_9P_VSOCK) + struct { + u32 port; + } vsock; +#endif } trans_opts; struct idr fids; @@ -151,13 +155,13 @@ struct p9_client_opts { * struct p9_fd_opts - per-transport options for fd transport * @rfd: file descriptor for reading (trans=fd) * @wfd: file descriptor for writing (trans=fd) - * @port: port to connect to (trans=tcp) + * @port: port to connect to (trans=tcp, trans=vsock) * @privport: port is privileged */ struct p9_fd_opts { int rfd; int wfd; - u16 port; + u32 port; bool privport; }; @@ -270,7 +274,7 @@ struct p9_dirent { struct p9_qid qid; u64 d_off; unsigned char d_type; - char d_name[256]; + char *d_name; }; struct iov_iter; diff --git a/net/9p/Kconfig b/net/9p/Kconfig index 22f8c167845d..b0a6342e3475 100644 --- a/net/9p/Kconfig +++ b/net/9p/Kconfig @@ -32,6 +32,16 @@ config NET_9P_VIRTIO This builds support for a transports between guest partitions and a host partition. +config NET_9P_VSOCK + depends on NET_9P_FD + depends on VSOCKETS + bool "9P VSOCK Transport" + help + This builds support for a transport for 9pfs over + virtual sockets (vsock). This is useful for communication + between virtual machines and their host without requiring + TCP/IP networking configuration or additional userspace software. + config NET_9P_XEN depends on XEN select XEN_XENBUS_FRONTEND diff --git a/net/9p/client.c b/net/9p/client.c index ef64546c6d52..e63e8c2a853d 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -448,6 +448,9 @@ static int p9_check_errors(struct p9_client *c, struct p9_req_t *req) p9_debug(P9_DEBUG_9P, "<<< RLERROR (%d)\n", -ecode); } + if (!err) + err = -EPROTO; + return err; out_err: diff --git a/net/9p/protocol.c b/net/9p/protocol.c index 67b0586d807f..d4335884e0c0 100644 --- a/net/9p/protocol.c +++ b/net/9p/protocol.c @@ -770,7 +770,7 @@ int p9dirent_read(struct p9_client *clnt, char *buf, int len, { struct p9_fcall fake_pdu; int ret; - char *nameptr; + char *nameptr = NULL; fake_pdu.size = len; fake_pdu.capacity = len; @@ -785,15 +785,7 @@ int p9dirent_read(struct p9_client *clnt, char *buf, int len, return ret; } - ret = strscpy(dirent->d_name, nameptr, sizeof(dirent->d_name)); - if (ret < 0) { - p9_debug(P9_DEBUG_ERROR, - "On the wire dirent name too long: %s\n", - nameptr); - kfree(nameptr); - return ret; - } - kfree(nameptr); + dirent->d_name = nameptr; return fake_pdu.offset; } diff --git a/net/9p/trans_fd.c b/net/9p/trans_fd.c index eb685b52aeb2..159d57e41e56 100644 --- a/net/9p/trans_fd.c +++ b/net/9p/trans_fd.c @@ -28,6 +28,9 @@ #include <net/9p/9p.h> #include <net/9p/client.h> #include <net/9p/transport.h> +#if IS_ENABLED(CONFIG_NET_9P_VSOCK) +#include <uapi/linux/vm_sockets.h> +#endif #include <linux/syscalls.h> /* killme */ @@ -36,6 +39,9 @@ static struct p9_trans_module p9_tcp_trans; static struct p9_trans_module p9_fd_trans; +#if IS_ENABLED(CONFIG_NET_9P_VSOCK) +static struct p9_trans_module p9_vsock_trans; +#endif enum { Rworksched = 1, /* read work scheduled or running */ @@ -714,6 +720,12 @@ static int p9_fd_show_options(struct seq_file *m, struct p9_client *clnt) if (clnt->trans_opts.fd.wfd != ~0) seq_printf(m, ",wfd=%u", clnt->trans_opts.fd.wfd); } +#if IS_ENABLED(CONFIG_NET_9P_VSOCK) + else if (clnt->trans_mod == &p9_vsock_trans) { + if (clnt->trans_opts.vsock.port != P9_FD_PORT) + seq_printf(m, ",port=%u", clnt->trans_opts.vsock.port); + } +#endif return 0; } @@ -875,7 +887,7 @@ p9_fd_create_tcp(struct p9_client *client, struct fs_context *fc) const char *addr = fc->source; struct v9fs_context *ctx = fc->fs_private; int err; - char port_str[6]; + char port_str[11]; struct socket *csocket; struct sockaddr_storage stor = { 0 }; struct p9_fd_opts opts; @@ -887,8 +899,8 @@ p9_fd_create_tcp(struct p9_client *client, struct fs_context *fc) return -EINVAL; sprintf(port_str, "%u", opts.port); - err = inet_pton_with_scope(current->nsproxy->net_ns, AF_UNSPEC, addr, - port_str, &stor); + err = inet_pton_with_scope(fc->net_ns, AF_UNSPEC, addr, port_str, + &stor); if (err < 0) return err; @@ -896,8 +908,8 @@ p9_fd_create_tcp(struct p9_client *client, struct fs_context *fc) client->trans_opts.tcp.port = opts.port; client->trans_opts.tcp.privport = opts.privport; - err = __sock_create(current->nsproxy->net_ns, stor.ss_family, - SOCK_STREAM, IPPROTO_TCP, &csocket, 1); + err = __sock_create(fc->net_ns, stor.ss_family, SOCK_STREAM, + IPPROTO_TCP, &csocket, 1); if (err) { pr_err("%s (%d): problem creating socket\n", __func__, task_pid_nr(current)); @@ -946,8 +958,7 @@ p9_fd_create_unix(struct p9_client *client, struct fs_context *fc) __func__, task_pid_nr(current), addr); return -ENAMETOOLONG; } - err = __sock_create(current->nsproxy->net_ns, PF_UNIX, - SOCK_STREAM, 0, &csocket, 1); + err = __sock_create(fc->net_ns, PF_UNIX, SOCK_STREAM, 0, &csocket, 1); if (err < 0) { pr_err("%s (%d): problem creating socket\n", __func__, task_pid_nr(current)); @@ -966,6 +977,67 @@ p9_fd_create_unix(struct p9_client *client, struct fs_context *fc) return p9_socket_open(client, csocket); } +#if IS_ENABLED(CONFIG_NET_9P_VSOCK) +static int +p9_fd_create_vsock(struct p9_client *client, struct fs_context *fc) +{ + const char *addr = fc->source; + struct v9fs_context *ctx = fc->fs_private; + int err; + struct socket *csocket; + struct sockaddr_vm addr_vm = { 0 }; + struct p9_fd_opts opts; + unsigned int cid; + + /* opts are already parsed in context */ + opts = ctx->fd_opts; + + if (opts.privport) { + // TODO: implement vsock privileged port binding + pr_err("%s (%d): privport currently not implemented for vsock\n", + __func__, task_pid_nr(current)); + return -EOPNOTSUPP; + } + + if (!addr) + return -EINVAL; + + err = kstrtouint(addr, 10, &cid); + if (err < 0) { + pr_err("%s (%d): invalid CID: %s\n", + __func__, task_pid_nr(current), addr); + return err; + } + + csocket = NULL; + + client->trans_opts.vsock.port = opts.port; + err = __sock_create(fc->net_ns, AF_VSOCK, + SOCK_STREAM, 0, &csocket, 1); + if (err) { + pr_err("%s (%d): problem creating socket\n", + __func__, task_pid_nr(current)); + return err; + } + + addr_vm.svm_family = AF_VSOCK; + addr_vm.svm_port = opts.port; + addr_vm.svm_cid = cid; + + err = READ_ONCE(csocket->ops)->connect(csocket, + (struct sockaddr_unsized *)&addr_vm, + sizeof(addr_vm), 0); + if (err < 0) { + pr_err("%s (%d): problem connecting socket to %s:%u\n", + __func__, task_pid_nr(current), addr, opts.port); + sock_release(csocket); + return err; + } + + return p9_socket_open(client, csocket); +} +#endif /* CONFIG_NET_9P_VSOCK */ + static int p9_fd_create(struct p9_client *client, struct fs_context *fc) { @@ -1036,6 +1108,23 @@ static struct p9_trans_module p9_fd_trans = { }; MODULE_ALIAS_9P("fd"); +#if IS_ENABLED(CONFIG_NET_9P_VSOCK) +static struct p9_trans_module p9_vsock_trans = { + .name = "vsock", + .maxsize = MAX_SOCK_BUF, + .def = false, + .supports_vmalloc = true, + .create = p9_fd_create_vsock, + .close = p9_fd_close, + .request = p9_fd_request, + .cancel = p9_fd_cancel, + .cancelled = p9_fd_cancelled, + .show_options = p9_fd_show_options, + .owner = THIS_MODULE, +}; +MODULE_ALIAS_9P("vsock"); +#endif + /** * p9_poll_workfn - poll worker thread * @work: work queue @@ -1073,6 +1162,9 @@ static int __init p9_trans_fd_init(void) v9fs_register_trans(&p9_tcp_trans); v9fs_register_trans(&p9_unix_trans); v9fs_register_trans(&p9_fd_trans); +#if IS_ENABLED(CONFIG_NET_9P_VSOCK) + v9fs_register_trans(&p9_vsock_trans); +#endif return 0; } @@ -1083,6 +1175,9 @@ static void __exit p9_trans_fd_exit(void) v9fs_unregister_trans(&p9_tcp_trans); v9fs_unregister_trans(&p9_unix_trans); v9fs_unregister_trans(&p9_fd_trans); +#if IS_ENABLED(CONFIG_NET_9P_VSOCK) + v9fs_unregister_trans(&p9_vsock_trans); +#endif } module_init(p9_trans_fd_init); diff --git a/net/9p/trans_rdma.c b/net/9p/trans_rdma.c index b4274f10fa44..f4450e779a79 100644 --- a/net/9p/trans_rdma.c +++ b/net/9p/trans_rdma.c @@ -556,7 +556,7 @@ rdma_create_trans(struct p9_client *client, struct fs_context *fc) return -ENOMEM; /* Create the RDMA CM ID */ - rdma->cm_id = rdma_create_id(&init_net, p9_cm_event_handler, client, + rdma->cm_id = rdma_create_id(fc->net_ns, p9_cm_event_handler, client, RDMA_PS_TCP, IB_QPT_RC); if (IS_ERR(rdma->cm_id)) goto error; diff --git a/net/9p/trans_usbg.c b/net/9p/trans_usbg.c index 419cda13a7b5..45b0ea594211 100644 --- a/net/9p/trans_usbg.c +++ b/net/9p/trans_usbg.c @@ -149,7 +149,8 @@ static void usb9pfs_tx_complete(struct usb_ep *ep, struct usb_request *req) { struct f_usb9pfs *usb9pfs = ep->driver_data; struct usb_composite_dev *cdev = usb9pfs->function.config->cdev; - struct p9_req_t *p9_tx_req = req->context; + struct p9_client *client; + struct p9_req_t *p9_tx_req; unsigned long flags; /* reset zero packages */ @@ -165,18 +166,25 @@ static void usb9pfs_tx_complete(struct usb_ep *ep, struct usb_request *req) ep->name, req->status, req->actual, req->length); spin_lock_irqsave(&usb9pfs->lock, flags); - WRITE_ONCE(p9_tx_req->status, REQ_STATUS_SENT); + client = usb9pfs->client; + p9_tx_req = req->context; + req->context = NULL; - p9_req_put(usb9pfs->client, p9_tx_req); + if (!client || !p9_tx_req) + goto unlock_complete; - req->context = NULL; + WRITE_ONCE(p9_tx_req->status, REQ_STATUS_SENT); + p9_req_put(client, p9_tx_req); + +unlock_complete: spin_unlock_irqrestore(&usb9pfs->lock, flags); complete(&usb9pfs->send); } -static struct p9_req_t *usb9pfs_rx_header(struct f_usb9pfs *usb9pfs, void *buf) +static struct p9_req_t *usb9pfs_rx_header(struct f_usb9pfs *usb9pfs, + struct p9_client *client, void *buf) { struct p9_req_t *p9_rx_req; struct p9_fcall rc; @@ -202,7 +210,7 @@ static struct p9_req_t *usb9pfs_rx_header(struct f_usb9pfs *usb9pfs, void *buf) "mux %p pkt: size: %d bytes tag: %d\n", usb9pfs, rc.size, rc.tag); - p9_rx_req = p9_tag_lookup(usb9pfs->client, rc.tag); + p9_rx_req = p9_tag_lookup(client, rc.tag); if (!p9_rx_req || p9_rx_req->status != REQ_STATUS_SENT) { p9_debug(P9_DEBUG_ERROR, "Unexpected packet tag %d\n", rc.tag); return NULL; @@ -212,7 +220,7 @@ static struct p9_req_t *usb9pfs_rx_header(struct f_usb9pfs *usb9pfs, void *buf) p9_debug(P9_DEBUG_ERROR, "requested packet size too big: %d for tag %d with capacity %zd\n", rc.size, rc.tag, p9_rx_req->rc.capacity); - p9_req_put(usb9pfs->client, p9_rx_req); + p9_req_put(client, p9_rx_req); return NULL; } @@ -220,7 +228,7 @@ static struct p9_req_t *usb9pfs_rx_header(struct f_usb9pfs *usb9pfs, void *buf) p9_debug(P9_DEBUG_ERROR, "No recv fcall for tag %d (req %p), disconnecting!\n", rc.tag, p9_rx_req); - p9_req_put(usb9pfs->client, p9_rx_req); + p9_req_put(client, p9_rx_req); return NULL; } @@ -231,8 +239,10 @@ static void usb9pfs_rx_complete(struct usb_ep *ep, struct usb_request *req) { struct f_usb9pfs *usb9pfs = ep->driver_data; struct usb_composite_dev *cdev = usb9pfs->function.config->cdev; + struct p9_client *client; struct p9_req_t *p9_rx_req; unsigned int req_size = req->actual; + unsigned long flags; int status = REQ_STATUS_RCVD; if (req->status) { @@ -241,9 +251,16 @@ static void usb9pfs_rx_complete(struct usb_ep *ep, struct usb_request *req) return; } - p9_rx_req = usb9pfs_rx_header(usb9pfs, req->buf); - if (!p9_rx_req) + spin_lock_irqsave(&usb9pfs->lock, flags); + client = usb9pfs->client; + if (!client) { + spin_unlock_irqrestore(&usb9pfs->lock, flags); return; + } + + p9_rx_req = usb9pfs_rx_header(usb9pfs, client, req->buf); + if (!p9_rx_req) + goto out_unlock; if (req_size > p9_rx_req->rc.capacity) { dev_err(&cdev->gadget->dev, @@ -257,8 +274,11 @@ static void usb9pfs_rx_complete(struct usb_ep *ep, struct usb_request *req) p9_rx_req->rc.size = req_size; - p9_client_cb(usb9pfs->client, p9_rx_req, status); - p9_req_put(usb9pfs->client, p9_rx_req); + p9_client_cb(client, p9_rx_req, status); + p9_req_put(client, p9_rx_req); + +out_unlock: + spin_unlock_irqrestore(&usb9pfs->lock, flags); complete(&usb9pfs->received); } @@ -278,18 +298,18 @@ static void disable_usb9pfs(struct f_usb9pfs *usb9pfs) struct usb_composite_dev *cdev = usb9pfs->function.config->cdev; + disable_ep(cdev, usb9pfs->in_ep); + disable_ep(cdev, usb9pfs->out_ep); + if (usb9pfs->in_req) { usb_ep_free_request(usb9pfs->in_ep, usb9pfs->in_req); usb9pfs->in_req = NULL; } if (usb9pfs->out_req) { - usb_ep_free_request(usb9pfs->out_ep, usb9pfs->out_req); + free_ep_req(usb9pfs->out_ep, usb9pfs->out_req); usb9pfs->out_req = NULL; } - - disable_ep(cdev, usb9pfs->in_ep); - disable_ep(cdev, usb9pfs->out_ep); dev_dbg(&cdev->gadget->dev, "%s disabled\n", usb9pfs->function.name); } @@ -314,10 +334,6 @@ static int alloc_requests(struct usb_composite_dev *cdev, usb9pfs->in_req->complete = usb9pfs_tx_complete; usb9pfs->out_req->complete = usb9pfs_rx_complete; - /* length will be set in complete routine */ - usb9pfs->in_req->context = usb9pfs; - usb9pfs->out_req->context = usb9pfs; - return 0; fail_in: @@ -391,7 +407,7 @@ static int p9_usbg_create(struct p9_client *client, struct fs_context *fc) guard(mutex)(&usb9pfs_lock); list_for_each_entry(dev, &usbg_instance_list, usb9pfs_instance) { - if (!strncmp(devname, dev->tag, strlen(devname))) { + if (!strcmp(devname, dev->tag)) { if (!dev->inuse) { dev->inuse = true; found = 1; @@ -416,7 +432,9 @@ static int p9_usbg_create(struct p9_client *client, struct fs_context *fc) client->status = Disconnected; else client->status = Connected; + spin_lock_irq(&usb9pfs->lock); usb9pfs->client = client; + spin_unlock_irq(&usb9pfs->lock); client->trans_mod->maxsize = usb9pfs->buflen; @@ -427,18 +445,31 @@ static int p9_usbg_create(struct p9_client *client, struct fs_context *fc) static void usb9pfs_clear_tx(struct f_usb9pfs *usb9pfs) { + struct p9_client *client; struct p9_req_t *req; + unsigned long flags; - guard(spinlock_irqsave)(&usb9pfs->lock); + spin_lock_irqsave(&usb9pfs->lock, flags); + client = usb9pfs->client; + usb9pfs->client = NULL; + req = usb9pfs->in_req ? usb9pfs->in_req->context : NULL; + if (usb9pfs->in_req) + usb9pfs->in_req->context = NULL; + spin_unlock_irqrestore(&usb9pfs->lock, flags); + + if (!req || !client) + return; + + usb9pfs->in_req->context = NULL; - req = usb9pfs->in_req->context; - if (!req) + if (!usb9pfs->client) return; if (!req->t_err) req->t_err = -ECONNRESET; - p9_client_cb(usb9pfs->client, req, REQ_STATUS_ERROR); + p9_client_cb(client, req, REQ_STATUS_ERROR); + usb9pfs->in_req->context = NULL; } static void p9_usbg_close(struct p9_client *client) @@ -457,6 +488,7 @@ static void p9_usbg_close(struct p9_client *client) client->status = Disconnected; usb9pfs_clear_tx(usb9pfs); + usb9pfs->client = NULL; opts = container_of(usb9pfs->function.fi, struct f_usb9pfs_opts, func_inst); @@ -466,6 +498,9 @@ static void p9_usbg_close(struct p9_client *client) mutex_lock(&usb9pfs_lock); dev->inuse = false; mutex_unlock(&usb9pfs_lock); + + disable_usb9pfs(usb9pfs); + reinit_completion(&usb9pfs->send); } static int p9_usbg_request(struct p9_client *client, struct p9_req_t *p9_req) @@ -725,8 +760,6 @@ static void usb9pfs_free_func(struct usb_function *f) struct f_usb9pfs *usb9pfs = func_to_usb9pfs(f); struct f_usb9pfs_opts *opts; - kfree(usb9pfs); - opts = container_of(f->fi, struct f_usb9pfs_opts, func_inst); mutex_lock(&opts->lock); @@ -734,6 +767,8 @@ static void usb9pfs_free_func(struct usb_function *f) mutex_unlock(&opts->lock); usb_free_all_descriptors(f); + + kfree(usb9pfs); } static int usb9pfs_set_alt(struct usb_function *f, @@ -748,8 +783,18 @@ static int usb9pfs_set_alt(struct usb_function *f, static void usb9pfs_disable(struct usb_function *f) { struct f_usb9pfs *usb9pfs = func_to_usb9pfs(f); + unsigned long flags; + spin_lock_irqsave(&usb9pfs->lock, flags); + if (usb9pfs->client) + usb9pfs->client->status = Disconnected; + spin_unlock_irqrestore(&usb9pfs->lock, flags); usb9pfs_clear_tx(usb9pfs); + + if (usb9pfs->in_ep->enabled) + disable_usb9pfs(usb9pfs); + + reinit_completion(&usb9pfs->send); } static struct usb_function *usb9pfs_alloc(struct usb_function_instance *fi) diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c index b0d0094ec8e2..9fa63e3ebf67 100644 --- a/net/9p/trans_virtio.c +++ b/net/9p/trans_virtio.c @@ -633,6 +633,11 @@ static int p9_virtio_probe(struct virtio_device *vdev) err = -EINVAL; goto out_free_vq; } + if (tag_len > NAME_MAX) { + dev_err(&vdev->dev, "mount tag too long (%u bytes)\n", tag_len); + err = -EINVAL; + goto out_free_vq; + } tag = kzalloc(tag_len + 1, GFP_KERNEL); if (!tag) { err = -ENOMEM; diff --git a/net/9p/trans_xen.c b/net/9p/trans_xen.c index f9fb2db7a066..690d941236b6 100644 --- a/net/9p/trans_xen.c +++ b/net/9p/trans_xen.c @@ -55,7 +55,7 @@ struct xen_9pfs_front_priv { char *tag; struct p9_client *client; - struct xen_9pfs_dataring *rings; + struct xen_9pfs_dataring rings[XEN_9PFS_NUM_RINGS]; }; static LIST_HEAD(xen_9pfs_devs); @@ -200,9 +200,19 @@ static void p9_xen_response(struct work_struct *work) masked_prod, &masked_cons, XEN_9PFS_RING_SIZE(ring)); + if (h.size < sizeof(h) || + h.size > xen_9pfs_queued(prod, cons, + XEN_9PFS_RING_SIZE(ring))) { + dev_warn_ratelimited(&priv->dev->dev, + "bad response size %u from backend\n", h.size); + break; + } + req = p9_tag_lookup(priv->client, h.tag); if (!req || req->status != REQ_STATUS_SENT) { dev_warn(&priv->dev->dev, "Wrong req tag=%x\n", h.tag); + if (req) + p9_req_put(priv->client, req); cons += h.size; virt_mb(); ring->intf->in_cons = cons; @@ -277,41 +287,37 @@ static void xen_9pfs_front_free(struct xen_9pfs_front_priv *priv) { int i, j; - if (priv->rings) { - for (i = 0; i < XEN_9PFS_NUM_RINGS; i++) { - struct xen_9pfs_dataring *ring = &priv->rings[i]; + for (i = 0; i < XEN_9PFS_NUM_RINGS; i++) { + struct xen_9pfs_dataring *ring = &priv->rings[i]; - cancel_work_sync(&ring->work); + if (!ring->intf) + break; + if (ring->irq >= 0) { + unbind_from_irqhandler(ring->irq, ring); + ring->irq = -1; + } + cancel_work_sync(&ring->work); - if (!ring->intf) - break; - if (ring->irq >= 0) { - unbind_from_irqhandler(ring->irq, ring); - ring->irq = -1; - } - if (ring->data.in) { - for (j = 0; j < (1 << ring->intf->ring_order); - j++) { - grant_ref_t ref; - - ref = ring->intf->ref[j]; - gnttab_end_foreign_access(ref, NULL); - ring->intf->ref[j] = INVALID_GRANT_REF; - } - free_pages_exact(ring->data.in, - 1UL << (ring->intf->ring_order + - XEN_PAGE_SHIFT)); - ring->data.in = NULL; - ring->data.out = NULL; - } - if (ring->ref != INVALID_GRANT_REF) { - gnttab_end_foreign_access(ring->ref, NULL); - ring->ref = INVALID_GRANT_REF; + if (ring->data.in) { + for (j = 0; j < (1 << ring->intf->ring_order); j++) { + grant_ref_t ref; + + ref = ring->intf->ref[j]; + gnttab_end_foreign_access(ref, NULL); + ring->intf->ref[j] = INVALID_GRANT_REF; } - free_page((unsigned long)ring->intf); - ring->intf = NULL; + free_pages_exact(ring->data.in, + 1UL << (ring->intf->ring_order + + XEN_PAGE_SHIFT)); + ring->data.in = NULL; + ring->data.out = NULL; } - kfree(priv->rings); + if (ring->ref != INVALID_GRANT_REF) { + gnttab_end_foreign_access(ring->ref, NULL); + ring->ref = INVALID_GRANT_REF; + } + free_page((unsigned long)ring->intf); + ring->intf = NULL; } kfree(priv->tag); kfree(priv); @@ -450,11 +456,6 @@ static int xen_9pfs_front_init(struct xenbus_device *dev) if (!priv) return -ENOMEM; priv->dev = dev; - priv->rings = kzalloc_objs(*priv->rings, XEN_9PFS_NUM_RINGS); - if (!priv->rings) { - kfree(priv); - return -ENOMEM; - } for (i = 0; i < XEN_9PFS_NUM_RINGS; i++) { priv->rings[i].priv = priv; |
