This is mostly a huge wave of LLM-driven fixes

A few exceptions:
 - a new vsock transport was added, since it's a variant of the existing
 fd/tcp transport there is little harm in taking it in
 - there's an incomplete fix for when file actual size doesn't match
 the size attribute (e.g. exports of sysfs), the fix addresses when
 st_size > actual size with cache=none but just about everything else
 is still broken (st_size=0 with actual data regardless of cache, or
 with data cache st_size > actual is still wrong and so are symlinks
 with 0 size)... Maybe at some point I'll find time to understand how
 netfs works and address these :|
 - some fixes for the usbg transport by its author, thank you!
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE/IPbcYBuWt0zoYhOq06b7GqY5nAFAmrpzl8ACgkQq06b7GqY
 5nA7SxAAmThz0j/BVRmej5Ii8ZWE18tibAG5o4EgEqTX15HfaePc3BxaogayrZkh
 xueb3ZBqN0jdKqScIL+6SeQcFwskoSohfZeFv5c75zafw+2x9MrmxVLeJw0oU2N0
 vybsMzLJOjMX7gRP3FUangqKlIoeQVOuP8XzYsbqLziMknQp4N2cAsZ/seuZOo+H
 +JBDWry3qok39TLEhIcFEACW+X3RNW2asRBtDNvjGrASBWRZsJSNnLdSIZ00XYPe
 BYddasTbBjkX3cdyXfEdtcYRb3BNmcNhdBEVpEuhVyUc/qL+Aft0lBTbIt1CIbdO
 LOmX/vbCvj4kTee7SjkE59aFp84wcZ+tYx0+4RmZBQPJE8FbqxMrtQ0i7Z6XNee4
 mgugn5OnnH37Fw8z3EhRMVDZrNXjaS7tl5gJxfEWuyn6RdKWWICWlm8K5P58WGy3
 HOU21/uOLxACLoAZLqudjA6CfjGoJXuk9T9+cnh4lqu9ghPgfY39lWVjXKSNpjg7
 4A1DNRn6fwfJiC7JtTlfbV9xtHmdsGV5syClKWNVGaCLUp+KDCs3J3aigHiUVt+y
 biBeB+yvU+0NkvAmzKUv97X7wBCzW0HgruZQQJ1rSEBAtrlaC4ev5CyjBOhn3//P
 wZ7wvAmW2fr2INo0nE4usv46jF6emLWH/kbWAKHzAW/BzFGaiUw=
 =oC41
 -----END PGP SIGNATURE-----

Merge tag '9p-for-7.4-rc2' of https://github.com/martinetd/linux

Pull late 9p updates from Dominique Martinet:
 "This is mostly a huge wave of LLM-driven fixes. At least the LLM fixes
  sort of slowed down recently? Or maybe it's just the time of the cycle
  and/or I got used to it, time will tell...

  A few exceptions:

   - a new vsock transport was added, since it's a variant of the
     existing fd/tcp transport there is little harm in taking it in

   - there's an incomplete fix for when file actual size doesn't match
     the size attribute (e.g. exports of sysfs).

     The fix addresses when st_size > actual size with cache=none but
     just about everything else is still broken (st_size=0 with actual
     data regardless of cache, or with data cache st_size > actual is
     still wrong and so are symlinks with 0 size)...

     Maybe at some point I'll find time to understand how netfs works
     and address these :|

   - some fixes for the usbg transport by its author, thank you!"

* tag '9p-for-7.4-rc2' of https://github.com/martinetd/linux: (39 commits)
  9p: optimize p9_fcall struct
  9p: reject RERROR/RLERROR with zero errcode
  9p: do not store the create fid on special files
  9p: fall back to the normal open path for non-regular files
  9p: don't decrement a server-owned zero nlink
  9p: fix kernel-doc warning for enum p9_session_flags
  9p: treat read return values of 0 as EOF
  9p/xen: validate the response size from the backend
  9p: fix typo in file header comments
  9p: remove duplicated assignment in v9fs_fill_super()
  9p: fix typos and wrong parameter docs in vfs_inode.c
  9p: fix header guard comment in acl.h
  net/9p/usbg: clear stale client pointer on close
  net/9p/usbg: remove bogus context initialization in alloc_requests
  net/9p/usbg: fix out_req buffer leak in disable_usb9pfs
  net/9p/usbg: call disable_usb9pfs() from usb9pfs_disable()
  net/9p/usbg: always reset completion when disconnecting
  net/9p/usbg: set client to Disconnected on usb9pfs_disable
  net/9p/usbg: also disable endpoints on p9_usbg_close
  net/9p/usbg: Fix use-after-free in disable_usb9pfs()
  ...
This commit is contained in:
Linus Torvalds 2026-11-03 08:32:35 -08:00
commit 7d1471e509
23 changed files with 372 additions and 144 deletions

View File

@ -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

View File

@ -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 */

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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,
@ -49,6 +45,20 @@ enum p9_session_flags {
V9FS_NDENTRY_TIMEOUT_SET = 0x400,
};
/**
* 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

View File

@ -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 @@ static void v9fs_issue_read(struct netfs_io_subrequest *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)

View File

@ -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>

View File

@ -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;

View File

@ -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>

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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:

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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);
p9_req_put(usb9pfs->client, p9_tx_req);
client = usb9pfs->client;
p9_tx_req = req->context;
req->context = NULL;
if (!client || !p9_tx_req)
goto unlock_complete;
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);
req = usb9pfs->in_req->context;
if (!req)
if (!req || !client)
return;
usb9pfs->in_req->context = NULL;
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)

View File

@ -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;

View File

@ -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;
}
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;
}
free_page((unsigned long)ring->intf);
ring->intf = NULL;
if (!ring->intf)
break;
if (ring->irq >= 0) {
unbind_from_irqhandler(ring->irq, ring);
ring->irq = -1;
}
kfree(priv->rings);
cancel_work_sync(&ring->work);
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;
}
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;