summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-10-30 10:54:17 +0000
committerMark Brown <broonie@kernel.org>2026-10-30 10:54:17 +0000
commite9dae0467bd37a0ecd683da960021bb0885ded7c (patch)
treebf8f4d25992091cfbcf7125ebd602dd58ebab038
parent2f3fc992562134fbfcb97c23d5caeb14358f019a (diff)
parent35ecd40c0d7e4f772d00dfec9f7077315ba5ca82 (diff)
downloadlinux-next-e9dae0467bd37a0ecd683da960021bb0885ded7c.tar.gz
linux-next-e9dae0467bd37a0ecd683da960021bb0885ded7c.zip
Merge branch '9p-next' of https://github.com/martinetd/linux
-rw-r--r--Documentation/filesystems/9p.rst12
-rw-r--r--fs/9p/acl.h2
-rw-r--r--fs/9p/cache.c7
-rw-r--r--fs/9p/fid.c2
-rw-r--r--fs/9p/v9fs.c18
-rw-r--r--fs/9p/v9fs.h26
-rw-r--r--fs/9p/vfs_addr.c10
-rw-r--r--fs/9p/vfs_dentry.c2
-rw-r--r--fs/9p/vfs_dir.c32
-rw-r--r--fs/9p/vfs_file.c2
-rw-r--r--fs/9p/vfs_inode.c26
-rw-r--r--fs/9p/vfs_inode_dotl.c36
-rw-r--r--fs/9p/vfs_super.c2
-rw-r--r--include/net/9p/9p.h4
-rw-r--r--include/net/9p/client.h14
-rw-r--r--net/9p/Kconfig10
-rw-r--r--net/9p/client.c3
-rw-r--r--net/9p/protocol.c12
-rw-r--r--net/9p/trans_fd.c109
-rw-r--r--net/9p/trans_rdma.c2
-rw-r--r--net/9p/trans_usbg.c99
-rw-r--r--net/9p/trans_virtio.c5
-rw-r--r--net/9p/trans_xen.c75
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;