mirror of
https://github.com/fbelavenuto/arpl.git
synced 2026-01-04 15:42:07 +08:00
first commit
This commit is contained in:
6
addons/9p/install.sh
Normal file
6
addons/9p/install.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
if [ "${1}" = "rd" ]; then
|
||||
echo "Installing module for Plan 9 Resource Sharing Support (9P2000)"
|
||||
${INSMOD} "/modules/9pnet.ko"
|
||||
${INSMOD} "/modules/9pnet_virtio.ko"
|
||||
${INSMOD} "/modules/9p.ko" ${PARAMS}
|
||||
fi
|
||||
28
addons/9p/manifest.yml
Normal file
28
addons/9p/manifest.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
version: 1
|
||||
name: 9p
|
||||
description: "Driver for Plan 9 Resource Sharing Support (9P2000)"
|
||||
available-for:
|
||||
bromolow-3.10.108:
|
||||
install-script: &script "install.sh"
|
||||
modules: true
|
||||
apollolake-4.4.180:
|
||||
install-script: *script
|
||||
modules: true
|
||||
broadwell-4.4.180:
|
||||
install-script: *script
|
||||
modules: true
|
||||
broadwellnk-4.4.180:
|
||||
install-script: *script
|
||||
modules: true
|
||||
denverton-4.4.180:
|
||||
install-script: *script
|
||||
modules: true
|
||||
geminilake-4.4.180:
|
||||
install-script: *script
|
||||
modules: true
|
||||
v1000-4.4.180:
|
||||
install-script: *script
|
||||
modules: true
|
||||
purley-4.4.180:
|
||||
install-script: *script
|
||||
modules: true
|
||||
36
addons/9p/src/3.10.108/Makefile
Normal file
36
addons/9p/src/3.10.108/Makefile
Normal file
@@ -0,0 +1,36 @@
|
||||
obj-m := 9p.o
|
||||
|
||||
9p-objs := \
|
||||
vfs_super.o \
|
||||
vfs_inode.o \
|
||||
vfs_inode_dotl.o \
|
||||
vfs_addr.o \
|
||||
vfs_file.o \
|
||||
vfs_dir.o \
|
||||
vfs_dentry.o \
|
||||
v9fs.o \
|
||||
fid.o \
|
||||
xattr.o \
|
||||
xattr_user.o
|
||||
|
||||
9p-y += cache.o
|
||||
9p-n += acl.o
|
||||
|
||||
obj-m := 9pnet.o
|
||||
obj-m += 9pnet_virtio.o
|
||||
obj-n += 9pnet_rdma.o
|
||||
|
||||
9pnet-objs := \
|
||||
mod.o \
|
||||
client.o \
|
||||
error.o \
|
||||
util.o \
|
||||
protocol.o \
|
||||
trans_fd.o \
|
||||
trans_common.o \
|
||||
|
||||
9pnet_virtio-objs := \
|
||||
trans_virtio.o
|
||||
|
||||
9pnet_rdma-objs := \
|
||||
trans_rdma.o
|
||||
375
addons/9p/src/3.10.108/acl.c
Normal file
375
addons/9p/src/3.10.108/acl.c
Normal file
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
* Copyright IBM Corporation, 2010
|
||||
* Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/posix_acl_xattr.h>
|
||||
#include "xattr.h"
|
||||
#include "acl.h"
|
||||
#include "v9fs.h"
|
||||
#include "v9fs_vfs.h"
|
||||
#include "fid.h"
|
||||
|
||||
static struct posix_acl *__v9fs_get_acl(struct p9_fid *fid, char *name)
|
||||
{
|
||||
ssize_t size;
|
||||
void *value = NULL;
|
||||
struct posix_acl *acl = NULL;
|
||||
|
||||
size = v9fs_fid_xattr_get(fid, name, NULL, 0);
|
||||
if (size > 0) {
|
||||
value = kzalloc(size, GFP_NOFS);
|
||||
if (!value)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
size = v9fs_fid_xattr_get(fid, name, value, size);
|
||||
if (size > 0) {
|
||||
acl = posix_acl_from_xattr(&init_user_ns, value, size);
|
||||
if (IS_ERR(acl))
|
||||
goto err_out;
|
||||
}
|
||||
} else if (size == -ENODATA || size == 0 ||
|
||||
size == -ENOSYS || size == -EOPNOTSUPP) {
|
||||
acl = NULL;
|
||||
} else
|
||||
acl = ERR_PTR(-EIO);
|
||||
|
||||
err_out:
|
||||
kfree(value);
|
||||
return acl;
|
||||
}
|
||||
|
||||
int v9fs_get_acl(struct inode *inode, struct p9_fid *fid)
|
||||
{
|
||||
int retval = 0;
|
||||
struct posix_acl *pacl, *dacl;
|
||||
struct v9fs_session_info *v9ses;
|
||||
|
||||
v9ses = v9fs_inode2v9ses(inode);
|
||||
if (((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) ||
|
||||
((v9ses->flags & V9FS_ACL_MASK) != V9FS_POSIX_ACL)) {
|
||||
set_cached_acl(inode, ACL_TYPE_DEFAULT, NULL);
|
||||
set_cached_acl(inode, ACL_TYPE_ACCESS, NULL);
|
||||
return 0;
|
||||
}
|
||||
/* get the default/access acl values and cache them */
|
||||
dacl = __v9fs_get_acl(fid, POSIX_ACL_XATTR_DEFAULT);
|
||||
pacl = __v9fs_get_acl(fid, POSIX_ACL_XATTR_ACCESS);
|
||||
|
||||
if (!IS_ERR(dacl) && !IS_ERR(pacl)) {
|
||||
set_cached_acl(inode, ACL_TYPE_DEFAULT, dacl);
|
||||
set_cached_acl(inode, ACL_TYPE_ACCESS, pacl);
|
||||
} else
|
||||
retval = -EIO;
|
||||
|
||||
if (!IS_ERR(dacl))
|
||||
posix_acl_release(dacl);
|
||||
|
||||
if (!IS_ERR(pacl))
|
||||
posix_acl_release(pacl);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static struct posix_acl *v9fs_get_cached_acl(struct inode *inode, int type)
|
||||
{
|
||||
struct posix_acl *acl;
|
||||
/*
|
||||
* 9p Always cache the acl value when
|
||||
* instantiating the inode (v9fs_inode_from_fid)
|
||||
*/
|
||||
acl = get_cached_acl(inode, type);
|
||||
BUG_ON(acl == ACL_NOT_CACHED);
|
||||
return acl;
|
||||
}
|
||||
|
||||
struct posix_acl *v9fs_iop_get_acl(struct inode *inode, int type)
|
||||
{
|
||||
struct v9fs_session_info *v9ses;
|
||||
|
||||
v9ses = v9fs_inode2v9ses(inode);
|
||||
if (((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) ||
|
||||
((v9ses->flags & V9FS_ACL_MASK) != V9FS_POSIX_ACL)) {
|
||||
/*
|
||||
* On access = client and acl = on mode get the acl
|
||||
* values from the server
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
return v9fs_get_cached_acl(inode, type);
|
||||
|
||||
}
|
||||
|
||||
static int v9fs_set_acl(struct p9_fid *fid, int type, struct posix_acl *acl)
|
||||
{
|
||||
int retval;
|
||||
char *name;
|
||||
size_t size;
|
||||
void *buffer;
|
||||
if (!acl)
|
||||
return 0;
|
||||
|
||||
/* Set a setxattr request to server */
|
||||
size = posix_acl_xattr_size(acl->a_count);
|
||||
buffer = kmalloc(size, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
retval = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
|
||||
if (retval < 0)
|
||||
goto err_free_out;
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
name = POSIX_ACL_XATTR_ACCESS;
|
||||
break;
|
||||
case ACL_TYPE_DEFAULT:
|
||||
name = POSIX_ACL_XATTR_DEFAULT;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
retval = v9fs_fid_xattr_set(fid, name, buffer, size, 0);
|
||||
err_free_out:
|
||||
kfree(buffer);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int v9fs_acl_chmod(struct inode *inode, struct p9_fid *fid)
|
||||
{
|
||||
int retval = 0;
|
||||
struct posix_acl *acl;
|
||||
|
||||
if (S_ISLNK(inode->i_mode))
|
||||
return -EOPNOTSUPP;
|
||||
acl = v9fs_get_cached_acl(inode, ACL_TYPE_ACCESS);
|
||||
if (acl) {
|
||||
retval = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
|
||||
if (retval)
|
||||
return retval;
|
||||
set_cached_acl(inode, ACL_TYPE_ACCESS, acl);
|
||||
retval = v9fs_set_acl(fid, ACL_TYPE_ACCESS, acl);
|
||||
posix_acl_release(acl);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
int v9fs_set_create_acl(struct inode *inode, struct p9_fid *fid,
|
||||
struct posix_acl *dacl, struct posix_acl *acl)
|
||||
{
|
||||
set_cached_acl(inode, ACL_TYPE_DEFAULT, dacl);
|
||||
set_cached_acl(inode, ACL_TYPE_ACCESS, acl);
|
||||
v9fs_set_acl(fid, ACL_TYPE_DEFAULT, dacl);
|
||||
v9fs_set_acl(fid, ACL_TYPE_ACCESS, acl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void v9fs_put_acl(struct posix_acl *dacl,
|
||||
struct posix_acl *acl)
|
||||
{
|
||||
posix_acl_release(dacl);
|
||||
posix_acl_release(acl);
|
||||
}
|
||||
|
||||
int v9fs_acl_mode(struct inode *dir, umode_t *modep,
|
||||
struct posix_acl **dpacl, struct posix_acl **pacl)
|
||||
{
|
||||
int retval = 0;
|
||||
umode_t mode = *modep;
|
||||
struct posix_acl *acl = NULL;
|
||||
|
||||
if (!S_ISLNK(mode)) {
|
||||
acl = v9fs_get_cached_acl(dir, ACL_TYPE_DEFAULT);
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
if (!acl)
|
||||
mode &= ~current_umask();
|
||||
}
|
||||
if (acl) {
|
||||
if (S_ISDIR(mode))
|
||||
*dpacl = posix_acl_dup(acl);
|
||||
retval = posix_acl_create(&acl, GFP_NOFS, &mode);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
if (retval > 0)
|
||||
*pacl = acl;
|
||||
else
|
||||
posix_acl_release(acl);
|
||||
}
|
||||
*modep = mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int v9fs_remote_get_acl(struct dentry *dentry, const char *name,
|
||||
void *buffer, size_t size, int type)
|
||||
{
|
||||
char *full_name;
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
full_name = POSIX_ACL_XATTR_ACCESS;
|
||||
break;
|
||||
case ACL_TYPE_DEFAULT:
|
||||
full_name = POSIX_ACL_XATTR_DEFAULT;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
return v9fs_xattr_get(dentry, full_name, buffer, size);
|
||||
}
|
||||
|
||||
static int v9fs_xattr_get_acl(struct dentry *dentry, const char *name,
|
||||
void *buffer, size_t size, int type)
|
||||
{
|
||||
struct v9fs_session_info *v9ses;
|
||||
struct posix_acl *acl;
|
||||
int error;
|
||||
|
||||
if (strcmp(name, "") != 0)
|
||||
return -EINVAL;
|
||||
|
||||
v9ses = v9fs_dentry2v9ses(dentry);
|
||||
/*
|
||||
* We allow set/get/list of acl when access=client is not specified
|
||||
*/
|
||||
if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT)
|
||||
return v9fs_remote_get_acl(dentry, name, buffer, size, type);
|
||||
|
||||
acl = v9fs_get_cached_acl(dentry->d_inode, type);
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
if (acl == NULL)
|
||||
return -ENODATA;
|
||||
error = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
|
||||
posix_acl_release(acl);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int v9fs_remote_set_acl(struct dentry *dentry, const char *name,
|
||||
const void *value, size_t size,
|
||||
int flags, int type)
|
||||
{
|
||||
char *full_name;
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
full_name = POSIX_ACL_XATTR_ACCESS;
|
||||
break;
|
||||
case ACL_TYPE_DEFAULT:
|
||||
full_name = POSIX_ACL_XATTR_DEFAULT;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
return v9fs_xattr_set(dentry, full_name, value, size, flags);
|
||||
}
|
||||
|
||||
|
||||
static int v9fs_xattr_set_acl(struct dentry *dentry, const char *name,
|
||||
const void *value, size_t size,
|
||||
int flags, int type)
|
||||
{
|
||||
int retval;
|
||||
struct posix_acl *acl;
|
||||
struct v9fs_session_info *v9ses;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
|
||||
if (strcmp(name, "") != 0)
|
||||
return -EINVAL;
|
||||
|
||||
v9ses = v9fs_dentry2v9ses(dentry);
|
||||
/*
|
||||
* set the attribute on the remote. Without even looking at the
|
||||
* xattr value. We leave it to the server to validate
|
||||
*/
|
||||
if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT)
|
||||
return v9fs_remote_set_acl(dentry, name,
|
||||
value, size, flags, type);
|
||||
|
||||
if (S_ISLNK(inode->i_mode))
|
||||
return -EOPNOTSUPP;
|
||||
if (!inode_owner_or_capable(inode))
|
||||
return -EPERM;
|
||||
if (value) {
|
||||
/* update the cached acl value */
|
||||
acl = posix_acl_from_xattr(&init_user_ns, value, size);
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
else if (acl) {
|
||||
retval = posix_acl_valid(acl);
|
||||
if (retval)
|
||||
goto err_out;
|
||||
}
|
||||
} else
|
||||
acl = NULL;
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
name = POSIX_ACL_XATTR_ACCESS;
|
||||
if (acl) {
|
||||
struct iattr iattr;
|
||||
|
||||
retval = posix_acl_update_mode(inode, &iattr.ia_mode, &acl);
|
||||
if (retval)
|
||||
goto err_out;
|
||||
if (!acl) {
|
||||
/*
|
||||
* ACL can be represented
|
||||
* by the mode bits. So don't
|
||||
* update ACL.
|
||||
*/
|
||||
value = NULL;
|
||||
size = 0;
|
||||
}
|
||||
iattr.ia_valid = ATTR_MODE;
|
||||
/* FIXME should we update ctime ?
|
||||
* What is the following setxattr update the
|
||||
* mode ?
|
||||
*/
|
||||
v9fs_vfs_setattr_dotl(dentry, &iattr);
|
||||
}
|
||||
break;
|
||||
case ACL_TYPE_DEFAULT:
|
||||
name = POSIX_ACL_XATTR_DEFAULT;
|
||||
if (!S_ISDIR(inode->i_mode)) {
|
||||
retval = acl ? -EINVAL : 0;
|
||||
goto err_out;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
retval = v9fs_xattr_set(dentry, name, value, size, flags);
|
||||
if (!retval)
|
||||
set_cached_acl(inode, type, acl);
|
||||
err_out:
|
||||
posix_acl_release(acl);
|
||||
return retval;
|
||||
}
|
||||
|
||||
const struct xattr_handler v9fs_xattr_acl_access_handler = {
|
||||
.prefix = POSIX_ACL_XATTR_ACCESS,
|
||||
.flags = ACL_TYPE_ACCESS,
|
||||
.get = v9fs_xattr_get_acl,
|
||||
.set = v9fs_xattr_set_acl,
|
||||
};
|
||||
|
||||
const struct xattr_handler v9fs_xattr_acl_default_handler = {
|
||||
.prefix = POSIX_ACL_XATTR_DEFAULT,
|
||||
.flags = ACL_TYPE_DEFAULT,
|
||||
.get = v9fs_xattr_get_acl,
|
||||
.set = v9fs_xattr_set_acl,
|
||||
};
|
||||
55
addons/9p/src/3.10.108/acl.h
Normal file
55
addons/9p/src/3.10.108/acl.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright IBM Corporation, 2010
|
||||
* Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
#ifndef FS_9P_ACL_H
|
||||
#define FS_9P_ACL_H
|
||||
|
||||
#ifdef CONFIG_9P_FS_POSIX_ACL
|
||||
extern int v9fs_get_acl(struct inode *, struct p9_fid *);
|
||||
extern struct posix_acl *v9fs_iop_get_acl(struct inode *inode, int type);
|
||||
extern int v9fs_acl_chmod(struct inode *, struct p9_fid *);
|
||||
extern int v9fs_set_create_acl(struct inode *, struct p9_fid *,
|
||||
struct posix_acl *, struct posix_acl *);
|
||||
extern int v9fs_acl_mode(struct inode *dir, umode_t *modep,
|
||||
struct posix_acl **dpacl, struct posix_acl **pacl);
|
||||
extern void v9fs_put_acl(struct posix_acl *dacl, struct posix_acl *acl);
|
||||
#else
|
||||
#define v9fs_iop_get_acl NULL
|
||||
static inline int v9fs_get_acl(struct inode *inode, struct p9_fid *fid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int v9fs_acl_chmod(struct inode *inode, struct p9_fid *fid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int v9fs_set_create_acl(struct inode *inode,
|
||||
struct p9_fid *fid,
|
||||
struct posix_acl *dacl,
|
||||
struct posix_acl *acl)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void v9fs_put_acl(struct posix_acl *dacl,
|
||||
struct posix_acl *acl)
|
||||
{
|
||||
}
|
||||
static inline int v9fs_acl_mode(struct inode *dir, umode_t *modep,
|
||||
struct posix_acl **dpacl,
|
||||
struct posix_acl **pacl)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* FS_9P_XATTR_H */
|
||||
415
addons/9p/src/3.10.108/cache.c
Normal file
415
addons/9p/src/3.10.108/cache.c
Normal file
@@ -0,0 +1,415 @@
|
||||
/*
|
||||
* V9FS cache definitions.
|
||||
*
|
||||
* Copyright (C) 2009 by Abhishek Kulkarni <adkulkar@umail.iu.edu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/fs.h>
|
||||
#include <net/9p/9p.h>
|
||||
|
||||
#include "v9fs.h"
|
||||
#include "cache.h"
|
||||
|
||||
#define CACHETAG_LEN 11
|
||||
|
||||
struct fscache_netfs v9fs_cache_netfs = {
|
||||
.name = "9p",
|
||||
.version = 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* v9fs_random_cachetag - Generate a random tag to be associated
|
||||
* with a new cache session.
|
||||
*
|
||||
* The value of jiffies is used for a fairly randomly cache tag.
|
||||
*/
|
||||
|
||||
static
|
||||
int v9fs_random_cachetag(struct v9fs_session_info *v9ses)
|
||||
{
|
||||
v9ses->cachetag = kmalloc(CACHETAG_LEN, GFP_KERNEL);
|
||||
if (!v9ses->cachetag)
|
||||
return -ENOMEM;
|
||||
|
||||
return scnprintf(v9ses->cachetag, CACHETAG_LEN, "%lu", jiffies);
|
||||
}
|
||||
|
||||
static uint16_t v9fs_cache_session_get_key(const void *cookie_netfs_data,
|
||||
void *buffer, uint16_t bufmax)
|
||||
{
|
||||
struct v9fs_session_info *v9ses;
|
||||
uint16_t klen = 0;
|
||||
|
||||
v9ses = (struct v9fs_session_info *)cookie_netfs_data;
|
||||
p9_debug(P9_DEBUG_FSC, "session %p buf %p size %u\n",
|
||||
v9ses, buffer, bufmax);
|
||||
|
||||
if (v9ses->cachetag)
|
||||
klen = strlen(v9ses->cachetag);
|
||||
|
||||
if (klen > bufmax)
|
||||
return 0;
|
||||
|
||||
memcpy(buffer, v9ses->cachetag, klen);
|
||||
p9_debug(P9_DEBUG_FSC, "cache session tag %s\n", v9ses->cachetag);
|
||||
return klen;
|
||||
}
|
||||
|
||||
const struct fscache_cookie_def v9fs_cache_session_index_def = {
|
||||
.name = "9P.session",
|
||||
.type = FSCACHE_COOKIE_TYPE_INDEX,
|
||||
.get_key = v9fs_cache_session_get_key,
|
||||
};
|
||||
|
||||
void v9fs_cache_session_get_cookie(struct v9fs_session_info *v9ses)
|
||||
{
|
||||
/* If no cache session tag was specified, we generate a random one. */
|
||||
if (!v9ses->cachetag)
|
||||
v9fs_random_cachetag(v9ses);
|
||||
|
||||
v9ses->fscache = fscache_acquire_cookie(v9fs_cache_netfs.primary_index,
|
||||
&v9fs_cache_session_index_def,
|
||||
v9ses);
|
||||
p9_debug(P9_DEBUG_FSC, "session %p get cookie %p\n",
|
||||
v9ses, v9ses->fscache);
|
||||
}
|
||||
|
||||
void v9fs_cache_session_put_cookie(struct v9fs_session_info *v9ses)
|
||||
{
|
||||
p9_debug(P9_DEBUG_FSC, "session %p put cookie %p\n",
|
||||
v9ses, v9ses->fscache);
|
||||
fscache_relinquish_cookie(v9ses->fscache, 0);
|
||||
v9ses->fscache = NULL;
|
||||
}
|
||||
|
||||
|
||||
static uint16_t v9fs_cache_inode_get_key(const void *cookie_netfs_data,
|
||||
void *buffer, uint16_t bufmax)
|
||||
{
|
||||
const struct v9fs_inode *v9inode = cookie_netfs_data;
|
||||
memcpy(buffer, &v9inode->qid.path, sizeof(v9inode->qid.path));
|
||||
p9_debug(P9_DEBUG_FSC, "inode %p get key %llu\n",
|
||||
&v9inode->vfs_inode, v9inode->qid.path);
|
||||
return sizeof(v9inode->qid.path);
|
||||
}
|
||||
|
||||
static void v9fs_cache_inode_get_attr(const void *cookie_netfs_data,
|
||||
uint64_t *size)
|
||||
{
|
||||
const struct v9fs_inode *v9inode = cookie_netfs_data;
|
||||
*size = i_size_read(&v9inode->vfs_inode);
|
||||
|
||||
p9_debug(P9_DEBUG_FSC, "inode %p get attr %llu\n",
|
||||
&v9inode->vfs_inode, *size);
|
||||
}
|
||||
|
||||
static uint16_t v9fs_cache_inode_get_aux(const void *cookie_netfs_data,
|
||||
void *buffer, uint16_t buflen)
|
||||
{
|
||||
const struct v9fs_inode *v9inode = cookie_netfs_data;
|
||||
memcpy(buffer, &v9inode->qid.version, sizeof(v9inode->qid.version));
|
||||
p9_debug(P9_DEBUG_FSC, "inode %p get aux %u\n",
|
||||
&v9inode->vfs_inode, v9inode->qid.version);
|
||||
return sizeof(v9inode->qid.version);
|
||||
}
|
||||
|
||||
static enum
|
||||
fscache_checkaux v9fs_cache_inode_check_aux(void *cookie_netfs_data,
|
||||
const void *buffer,
|
||||
uint16_t buflen)
|
||||
{
|
||||
const struct v9fs_inode *v9inode = cookie_netfs_data;
|
||||
|
||||
if (buflen != sizeof(v9inode->qid.version))
|
||||
return FSCACHE_CHECKAUX_OBSOLETE;
|
||||
|
||||
if (memcmp(buffer, &v9inode->qid.version,
|
||||
sizeof(v9inode->qid.version)))
|
||||
return FSCACHE_CHECKAUX_OBSOLETE;
|
||||
|
||||
return FSCACHE_CHECKAUX_OKAY;
|
||||
}
|
||||
|
||||
static void v9fs_cache_inode_now_uncached(void *cookie_netfs_data)
|
||||
{
|
||||
struct v9fs_inode *v9inode = cookie_netfs_data;
|
||||
struct pagevec pvec;
|
||||
pgoff_t first;
|
||||
int loop, nr_pages;
|
||||
|
||||
pagevec_init(&pvec, 0);
|
||||
first = 0;
|
||||
|
||||
for (;;) {
|
||||
nr_pages = pagevec_lookup(&pvec, v9inode->vfs_inode.i_mapping,
|
||||
first,
|
||||
PAGEVEC_SIZE - pagevec_count(&pvec));
|
||||
if (!nr_pages)
|
||||
break;
|
||||
|
||||
for (loop = 0; loop < nr_pages; loop++)
|
||||
ClearPageFsCache(pvec.pages[loop]);
|
||||
|
||||
first = pvec.pages[nr_pages - 1]->index + 1;
|
||||
|
||||
pvec.nr = nr_pages;
|
||||
pagevec_release(&pvec);
|
||||
cond_resched();
|
||||
}
|
||||
}
|
||||
|
||||
const struct fscache_cookie_def v9fs_cache_inode_index_def = {
|
||||
.name = "9p.inode",
|
||||
.type = FSCACHE_COOKIE_TYPE_DATAFILE,
|
||||
.get_key = v9fs_cache_inode_get_key,
|
||||
.get_attr = v9fs_cache_inode_get_attr,
|
||||
.get_aux = v9fs_cache_inode_get_aux,
|
||||
.check_aux = v9fs_cache_inode_check_aux,
|
||||
.now_uncached = v9fs_cache_inode_now_uncached,
|
||||
};
|
||||
|
||||
void v9fs_cache_inode_get_cookie(struct inode *inode)
|
||||
{
|
||||
struct v9fs_inode *v9inode;
|
||||
struct v9fs_session_info *v9ses;
|
||||
|
||||
if (!S_ISREG(inode->i_mode))
|
||||
return;
|
||||
|
||||
v9inode = V9FS_I(inode);
|
||||
if (v9inode->fscache)
|
||||
return;
|
||||
|
||||
v9ses = v9fs_inode2v9ses(inode);
|
||||
v9inode->fscache = fscache_acquire_cookie(v9ses->fscache,
|
||||
&v9fs_cache_inode_index_def,
|
||||
v9inode);
|
||||
|
||||
p9_debug(P9_DEBUG_FSC, "inode %p get cookie %p\n",
|
||||
inode, v9inode->fscache);
|
||||
}
|
||||
|
||||
void v9fs_cache_inode_put_cookie(struct inode *inode)
|
||||
{
|
||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
|
||||
if (!v9inode->fscache)
|
||||
return;
|
||||
p9_debug(P9_DEBUG_FSC, "inode %p put cookie %p\n",
|
||||
inode, v9inode->fscache);
|
||||
|
||||
fscache_relinquish_cookie(v9inode->fscache, 0);
|
||||
v9inode->fscache = NULL;
|
||||
}
|
||||
|
||||
void v9fs_cache_inode_flush_cookie(struct inode *inode)
|
||||
{
|
||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
|
||||
if (!v9inode->fscache)
|
||||
return;
|
||||
p9_debug(P9_DEBUG_FSC, "inode %p flush cookie %p\n",
|
||||
inode, v9inode->fscache);
|
||||
|
||||
fscache_relinquish_cookie(v9inode->fscache, 1);
|
||||
v9inode->fscache = NULL;
|
||||
}
|
||||
|
||||
void v9fs_cache_inode_set_cookie(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
struct p9_fid *fid;
|
||||
|
||||
if (!v9inode->fscache)
|
||||
return;
|
||||
|
||||
spin_lock(&v9inode->fscache_lock);
|
||||
fid = filp->private_data;
|
||||
if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
|
||||
v9fs_cache_inode_flush_cookie(inode);
|
||||
else
|
||||
v9fs_cache_inode_get_cookie(inode);
|
||||
|
||||
spin_unlock(&v9inode->fscache_lock);
|
||||
}
|
||||
|
||||
void v9fs_cache_inode_reset_cookie(struct inode *inode)
|
||||
{
|
||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
struct v9fs_session_info *v9ses;
|
||||
struct fscache_cookie *old;
|
||||
|
||||
if (!v9inode->fscache)
|
||||
return;
|
||||
|
||||
old = v9inode->fscache;
|
||||
|
||||
spin_lock(&v9inode->fscache_lock);
|
||||
fscache_relinquish_cookie(v9inode->fscache, 1);
|
||||
|
||||
v9ses = v9fs_inode2v9ses(inode);
|
||||
v9inode->fscache = fscache_acquire_cookie(v9ses->fscache,
|
||||
&v9fs_cache_inode_index_def,
|
||||
v9inode);
|
||||
p9_debug(P9_DEBUG_FSC, "inode %p revalidating cookie old %p new %p\n",
|
||||
inode, old, v9inode->fscache);
|
||||
|
||||
spin_unlock(&v9inode->fscache_lock);
|
||||
}
|
||||
|
||||
int __v9fs_fscache_release_page(struct page *page, gfp_t gfp)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
|
||||
BUG_ON(!v9inode->fscache);
|
||||
|
||||
return fscache_maybe_release_page(v9inode->fscache, page, gfp);
|
||||
}
|
||||
|
||||
void __v9fs_fscache_invalidate_page(struct page *page)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
|
||||
BUG_ON(!v9inode->fscache);
|
||||
|
||||
if (PageFsCache(page)) {
|
||||
fscache_wait_on_page_write(v9inode->fscache, page);
|
||||
BUG_ON(!PageLocked(page));
|
||||
fscache_uncache_page(v9inode->fscache, page);
|
||||
}
|
||||
}
|
||||
|
||||
static void v9fs_vfs_readpage_complete(struct page *page, void *data,
|
||||
int error)
|
||||
{
|
||||
if (!error)
|
||||
SetPageUptodate(page);
|
||||
|
||||
unlock_page(page);
|
||||
}
|
||||
|
||||
/**
|
||||
* __v9fs_readpage_from_fscache - read a page from cache
|
||||
*
|
||||
* Returns 0 if the pages are in cache and a BIO is submitted,
|
||||
* 1 if the pages are not in cache and -error otherwise.
|
||||
*/
|
||||
|
||||
int __v9fs_readpage_from_fscache(struct inode *inode, struct page *page)
|
||||
{
|
||||
int ret;
|
||||
const struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
|
||||
p9_debug(P9_DEBUG_FSC, "inode %p page %p\n", inode, page);
|
||||
if (!v9inode->fscache)
|
||||
return -ENOBUFS;
|
||||
|
||||
ret = fscache_read_or_alloc_page(v9inode->fscache,
|
||||
page,
|
||||
v9fs_vfs_readpage_complete,
|
||||
NULL,
|
||||
GFP_KERNEL);
|
||||
switch (ret) {
|
||||
case -ENOBUFS:
|
||||
case -ENODATA:
|
||||
p9_debug(P9_DEBUG_FSC, "page/inode not in cache %d\n", ret);
|
||||
return 1;
|
||||
case 0:
|
||||
p9_debug(P9_DEBUG_FSC, "BIO submitted\n");
|
||||
return ret;
|
||||
default:
|
||||
p9_debug(P9_DEBUG_FSC, "ret %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* __v9fs_readpages_from_fscache - read multiple pages from cache
|
||||
*
|
||||
* Returns 0 if the pages are in cache and a BIO is submitted,
|
||||
* 1 if the pages are not in cache and -error otherwise.
|
||||
*/
|
||||
|
||||
int __v9fs_readpages_from_fscache(struct inode *inode,
|
||||
struct address_space *mapping,
|
||||
struct list_head *pages,
|
||||
unsigned *nr_pages)
|
||||
{
|
||||
int ret;
|
||||
const struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
|
||||
p9_debug(P9_DEBUG_FSC, "inode %p pages %u\n", inode, *nr_pages);
|
||||
if (!v9inode->fscache)
|
||||
return -ENOBUFS;
|
||||
|
||||
ret = fscache_read_or_alloc_pages(v9inode->fscache,
|
||||
mapping, pages, nr_pages,
|
||||
v9fs_vfs_readpage_complete,
|
||||
NULL,
|
||||
mapping_gfp_mask(mapping));
|
||||
switch (ret) {
|
||||
case -ENOBUFS:
|
||||
case -ENODATA:
|
||||
p9_debug(P9_DEBUG_FSC, "pages/inodes not in cache %d\n", ret);
|
||||
return 1;
|
||||
case 0:
|
||||
BUG_ON(!list_empty(pages));
|
||||
BUG_ON(*nr_pages != 0);
|
||||
p9_debug(P9_DEBUG_FSC, "BIO submitted\n");
|
||||
return ret;
|
||||
default:
|
||||
p9_debug(P9_DEBUG_FSC, "ret %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* __v9fs_readpage_to_fscache - write a page to the cache
|
||||
*
|
||||
*/
|
||||
|
||||
void __v9fs_readpage_to_fscache(struct inode *inode, struct page *page)
|
||||
{
|
||||
int ret;
|
||||
const struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
|
||||
p9_debug(P9_DEBUG_FSC, "inode %p page %p\n", inode, page);
|
||||
ret = fscache_write_page(v9inode->fscache, page, GFP_KERNEL);
|
||||
p9_debug(P9_DEBUG_FSC, "ret = %d\n", ret);
|
||||
if (ret != 0)
|
||||
v9fs_uncache_page(inode, page);
|
||||
}
|
||||
|
||||
/*
|
||||
* wait for a page to complete writing to the cache
|
||||
*/
|
||||
void __v9fs_fscache_wait_on_page_write(struct inode *inode, struct page *page)
|
||||
{
|
||||
const struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
p9_debug(P9_DEBUG_FSC, "inode %p page %p\n", inode, page);
|
||||
if (PageFsCache(page))
|
||||
fscache_wait_on_page_write(v9inode->fscache, page);
|
||||
}
|
||||
139
addons/9p/src/3.10.108/cache.h
Normal file
139
addons/9p/src/3.10.108/cache.h
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* V9FS cache definitions.
|
||||
*
|
||||
* Copyright (C) 2009 by Abhishek Kulkarni <adkulkar@umail.iu.edu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _9P_CACHE_H
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
#include <linux/fscache.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
extern struct fscache_netfs v9fs_cache_netfs;
|
||||
extern const struct fscache_cookie_def v9fs_cache_session_index_def;
|
||||
extern const struct fscache_cookie_def v9fs_cache_inode_index_def;
|
||||
|
||||
extern void v9fs_cache_session_get_cookie(struct v9fs_session_info *v9ses);
|
||||
extern void v9fs_cache_session_put_cookie(struct v9fs_session_info *v9ses);
|
||||
|
||||
extern void v9fs_cache_inode_get_cookie(struct inode *inode);
|
||||
extern void v9fs_cache_inode_put_cookie(struct inode *inode);
|
||||
extern void v9fs_cache_inode_flush_cookie(struct inode *inode);
|
||||
extern void v9fs_cache_inode_set_cookie(struct inode *inode, struct file *filp);
|
||||
extern void v9fs_cache_inode_reset_cookie(struct inode *inode);
|
||||
|
||||
extern int __v9fs_cache_register(void);
|
||||
extern void __v9fs_cache_unregister(void);
|
||||
|
||||
extern int __v9fs_fscache_release_page(struct page *page, gfp_t gfp);
|
||||
extern void __v9fs_fscache_invalidate_page(struct page *page);
|
||||
extern int __v9fs_readpage_from_fscache(struct inode *inode,
|
||||
struct page *page);
|
||||
extern int __v9fs_readpages_from_fscache(struct inode *inode,
|
||||
struct address_space *mapping,
|
||||
struct list_head *pages,
|
||||
unsigned *nr_pages);
|
||||
extern void __v9fs_readpage_to_fscache(struct inode *inode, struct page *page);
|
||||
extern void __v9fs_fscache_wait_on_page_write(struct inode *inode,
|
||||
struct page *page);
|
||||
|
||||
static inline int v9fs_fscache_release_page(struct page *page,
|
||||
gfp_t gfp)
|
||||
{
|
||||
return __v9fs_fscache_release_page(page, gfp);
|
||||
}
|
||||
|
||||
static inline void v9fs_fscache_invalidate_page(struct page *page)
|
||||
{
|
||||
__v9fs_fscache_invalidate_page(page);
|
||||
}
|
||||
|
||||
static inline int v9fs_readpage_from_fscache(struct inode *inode,
|
||||
struct page *page)
|
||||
{
|
||||
return __v9fs_readpage_from_fscache(inode, page);
|
||||
}
|
||||
|
||||
static inline int v9fs_readpages_from_fscache(struct inode *inode,
|
||||
struct address_space *mapping,
|
||||
struct list_head *pages,
|
||||
unsigned *nr_pages)
|
||||
{
|
||||
return __v9fs_readpages_from_fscache(inode, mapping, pages,
|
||||
nr_pages);
|
||||
}
|
||||
|
||||
static inline void v9fs_readpage_to_fscache(struct inode *inode,
|
||||
struct page *page)
|
||||
{
|
||||
if (PageFsCache(page))
|
||||
__v9fs_readpage_to_fscache(inode, page);
|
||||
}
|
||||
|
||||
static inline void v9fs_uncache_page(struct inode *inode, struct page *page)
|
||||
{
|
||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
fscache_uncache_page(v9inode->fscache, page);
|
||||
BUG_ON(PageFsCache(page));
|
||||
}
|
||||
|
||||
static inline void v9fs_fscache_wait_on_page_write(struct inode *inode,
|
||||
struct page *page)
|
||||
{
|
||||
return __v9fs_fscache_wait_on_page_write(inode, page);
|
||||
}
|
||||
|
||||
#else /* CONFIG_9P_FSCACHE */
|
||||
|
||||
static inline int v9fs_fscache_release_page(struct page *page,
|
||||
gfp_t gfp) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline void v9fs_fscache_invalidate_page(struct page *page) {}
|
||||
|
||||
static inline int v9fs_readpage_from_fscache(struct inode *inode,
|
||||
struct page *page)
|
||||
{
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
static inline int v9fs_readpages_from_fscache(struct inode *inode,
|
||||
struct address_space *mapping,
|
||||
struct list_head *pages,
|
||||
unsigned *nr_pages)
|
||||
{
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
static inline void v9fs_readpage_to_fscache(struct inode *inode,
|
||||
struct page *page)
|
||||
{}
|
||||
|
||||
static inline void v9fs_uncache_page(struct inode *inode, struct page *page)
|
||||
{}
|
||||
|
||||
static inline void v9fs_fscache_wait_on_page_write(struct inode *inode,
|
||||
struct page *page)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_9P_FSCACHE */
|
||||
#endif /* _9P_CACHE_H */
|
||||
2256
addons/9p/src/3.10.108/client.c
Normal file
2256
addons/9p/src/3.10.108/client.c
Normal file
File diff suppressed because it is too large
Load Diff
247
addons/9p/src/3.10.108/error.c
Normal file
247
addons/9p/src/3.10.108/error.c
Normal file
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* linux/fs/9p/error.c
|
||||
*
|
||||
* Error string handling
|
||||
*
|
||||
* Plan 9 uses error strings, Unix uses error numbers. These functions
|
||||
* try to help manage that and provide for dynamically adding error
|
||||
* mappings.
|
||||
*
|
||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/jhash.h>
|
||||
#include <linux/errno.h>
|
||||
#include <net/9p/9p.h>
|
||||
|
||||
/**
|
||||
* struct errormap - map string errors from Plan 9 to Linux numeric ids
|
||||
* @name: string sent over 9P
|
||||
* @val: numeric id most closely representing @name
|
||||
* @namelen: length of string
|
||||
* @list: hash-table list for string lookup
|
||||
*/
|
||||
struct errormap {
|
||||
char *name;
|
||||
int val;
|
||||
|
||||
int namelen;
|
||||
struct hlist_node list;
|
||||
};
|
||||
|
||||
#define ERRHASHSZ 32
|
||||
static struct hlist_head hash_errmap[ERRHASHSZ];
|
||||
|
||||
/* FixMe - reduce to a reasonable size */
|
||||
static struct errormap errmap[] = {
|
||||
{"Operation not permitted", EPERM},
|
||||
{"wstat prohibited", EPERM},
|
||||
{"No such file or directory", ENOENT},
|
||||
{"directory entry not found", ENOENT},
|
||||
{"file not found", ENOENT},
|
||||
{"Interrupted system call", EINTR},
|
||||
{"Input/output error", EIO},
|
||||
{"No such device or address", ENXIO},
|
||||
{"Argument list too long", E2BIG},
|
||||
{"Bad file descriptor", EBADF},
|
||||
{"Resource temporarily unavailable", EAGAIN},
|
||||
{"Cannot allocate memory", ENOMEM},
|
||||
{"Permission denied", EACCES},
|
||||
{"Bad address", EFAULT},
|
||||
{"Block device required", ENOTBLK},
|
||||
{"Device or resource busy", EBUSY},
|
||||
{"File exists", EEXIST},
|
||||
{"Invalid cross-device link", EXDEV},
|
||||
{"No such device", ENODEV},
|
||||
{"Not a directory", ENOTDIR},
|
||||
{"Is a directory", EISDIR},
|
||||
{"Invalid argument", EINVAL},
|
||||
{"Too many open files in system", ENFILE},
|
||||
{"Too many open files", EMFILE},
|
||||
{"Text file busy", ETXTBSY},
|
||||
{"File too large", EFBIG},
|
||||
{"No space left on device", ENOSPC},
|
||||
{"Illegal seek", ESPIPE},
|
||||
{"Read-only file system", EROFS},
|
||||
{"Too many links", EMLINK},
|
||||
{"Broken pipe", EPIPE},
|
||||
{"Numerical argument out of domain", EDOM},
|
||||
{"Numerical result out of range", ERANGE},
|
||||
{"Resource deadlock avoided", EDEADLK},
|
||||
{"File name too long", ENAMETOOLONG},
|
||||
{"No locks available", ENOLCK},
|
||||
{"Function not implemented", ENOSYS},
|
||||
{"Directory not empty", ENOTEMPTY},
|
||||
{"Too many levels of symbolic links", ELOOP},
|
||||
{"No message of desired type", ENOMSG},
|
||||
{"Identifier removed", EIDRM},
|
||||
{"No data available", ENODATA},
|
||||
{"Machine is not on the network", ENONET},
|
||||
{"Package not installed", ENOPKG},
|
||||
{"Object is remote", EREMOTE},
|
||||
{"Link has been severed", ENOLINK},
|
||||
{"Communication error on send", ECOMM},
|
||||
{"Protocol error", EPROTO},
|
||||
{"Bad message", EBADMSG},
|
||||
{"File descriptor in bad state", EBADFD},
|
||||
{"Streams pipe error", ESTRPIPE},
|
||||
{"Too many users", EUSERS},
|
||||
{"Socket operation on non-socket", ENOTSOCK},
|
||||
{"Message too long", EMSGSIZE},
|
||||
{"Protocol not available", ENOPROTOOPT},
|
||||
{"Protocol not supported", EPROTONOSUPPORT},
|
||||
{"Socket type not supported", ESOCKTNOSUPPORT},
|
||||
{"Operation not supported", EOPNOTSUPP},
|
||||
{"Protocol family not supported", EPFNOSUPPORT},
|
||||
{"Network is down", ENETDOWN},
|
||||
{"Network is unreachable", ENETUNREACH},
|
||||
{"Network dropped connection on reset", ENETRESET},
|
||||
{"Software caused connection abort", ECONNABORTED},
|
||||
{"Connection reset by peer", ECONNRESET},
|
||||
{"No buffer space available", ENOBUFS},
|
||||
{"Transport endpoint is already connected", EISCONN},
|
||||
{"Transport endpoint is not connected", ENOTCONN},
|
||||
{"Cannot send after transport endpoint shutdown", ESHUTDOWN},
|
||||
{"Connection timed out", ETIMEDOUT},
|
||||
{"Connection refused", ECONNREFUSED},
|
||||
{"Host is down", EHOSTDOWN},
|
||||
{"No route to host", EHOSTUNREACH},
|
||||
{"Operation already in progress", EALREADY},
|
||||
{"Operation now in progress", EINPROGRESS},
|
||||
{"Is a named type file", EISNAM},
|
||||
{"Remote I/O error", EREMOTEIO},
|
||||
{"Disk quota exceeded", EDQUOT},
|
||||
/* errors from fossil, vacfs, and u9fs */
|
||||
{"fid unknown or out of range", EBADF},
|
||||
{"permission denied", EACCES},
|
||||
{"file does not exist", ENOENT},
|
||||
{"authentication failed", ECONNREFUSED},
|
||||
{"bad offset in directory read", ESPIPE},
|
||||
{"bad use of fid", EBADF},
|
||||
{"wstat can't convert between files and directories", EPERM},
|
||||
{"directory is not empty", ENOTEMPTY},
|
||||
{"file exists", EEXIST},
|
||||
{"file already exists", EEXIST},
|
||||
{"file or directory already exists", EEXIST},
|
||||
{"fid already in use", EBADF},
|
||||
{"file in use", ETXTBSY},
|
||||
{"i/o error", EIO},
|
||||
{"file already open for I/O", ETXTBSY},
|
||||
{"illegal mode", EINVAL},
|
||||
{"illegal name", ENAMETOOLONG},
|
||||
{"not a directory", ENOTDIR},
|
||||
{"not a member of proposed group", EPERM},
|
||||
{"not owner", EACCES},
|
||||
{"only owner can change group in wstat", EACCES},
|
||||
{"read only file system", EROFS},
|
||||
{"no access to special file", EPERM},
|
||||
{"i/o count too large", EIO},
|
||||
{"unknown group", EINVAL},
|
||||
{"unknown user", EINVAL},
|
||||
{"bogus wstat buffer", EPROTO},
|
||||
{"exclusive use file already open", EAGAIN},
|
||||
{"corrupted directory entry", EIO},
|
||||
{"corrupted file entry", EIO},
|
||||
{"corrupted block label", EIO},
|
||||
{"corrupted meta data", EIO},
|
||||
{"illegal offset", EINVAL},
|
||||
{"illegal path element", ENOENT},
|
||||
{"root of file system is corrupted", EIO},
|
||||
{"corrupted super block", EIO},
|
||||
{"protocol botch", EPROTO},
|
||||
{"file system is full", ENOSPC},
|
||||
{"file is in use", EAGAIN},
|
||||
{"directory entry is not allocated", ENOENT},
|
||||
{"file is read only", EROFS},
|
||||
{"file has been removed", EIDRM},
|
||||
{"only support truncation to zero length", EPERM},
|
||||
{"cannot remove root", EPERM},
|
||||
{"file too big", EFBIG},
|
||||
{"venti i/o error", EIO},
|
||||
/* these are not errors */
|
||||
{"u9fs rhostsauth: no authentication required", 0},
|
||||
{"u9fs authnone: no authentication required", 0},
|
||||
{NULL, -1}
|
||||
};
|
||||
|
||||
/**
|
||||
* p9_error_init - preload mappings into hash list
|
||||
*
|
||||
*/
|
||||
|
||||
int p9_error_init(void)
|
||||
{
|
||||
struct errormap *c;
|
||||
int bucket;
|
||||
|
||||
/* initialize hash table */
|
||||
for (bucket = 0; bucket < ERRHASHSZ; bucket++)
|
||||
INIT_HLIST_HEAD(&hash_errmap[bucket]);
|
||||
|
||||
/* load initial error map into hash table */
|
||||
for (c = errmap; c->name != NULL; c++) {
|
||||
c->namelen = strlen(c->name);
|
||||
bucket = jhash(c->name, c->namelen, 0) % ERRHASHSZ;
|
||||
INIT_HLIST_NODE(&c->list);
|
||||
hlist_add_head(&c->list, &hash_errmap[bucket]);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL(p9_error_init);
|
||||
|
||||
/**
|
||||
* errstr2errno - convert error string to error number
|
||||
* @errstr: error string
|
||||
* @len: length of error string
|
||||
*
|
||||
*/
|
||||
|
||||
int p9_errstr2errno(char *errstr, int len)
|
||||
{
|
||||
int errno;
|
||||
struct errormap *c;
|
||||
int bucket;
|
||||
|
||||
errno = 0;
|
||||
c = NULL;
|
||||
bucket = jhash(errstr, len, 0) % ERRHASHSZ;
|
||||
hlist_for_each_entry(c, &hash_errmap[bucket], list) {
|
||||
if (c->namelen == len && !memcmp(c->name, errstr, len)) {
|
||||
errno = c->val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (errno == 0) {
|
||||
/* TODO: if error isn't found, add it dynamically */
|
||||
errstr[len] = 0;
|
||||
pr_err("%s: server reported unknown error %s\n",
|
||||
__func__, errstr);
|
||||
errno = ESERVERFAULT;
|
||||
}
|
||||
|
||||
return -errno;
|
||||
}
|
||||
EXPORT_SYMBOL(p9_errstr2errno);
|
||||
306
addons/9p/src/3.10.108/fid.c
Normal file
306
addons/9p/src/3.10.108/fid.c
Normal file
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* V9FS FID Management
|
||||
*
|
||||
* Copyright (C) 2007 by Latchesar Ionkov <lucho@ionkov.net>
|
||||
* Copyright (C) 2005, 2006 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/idr.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
|
||||
#include "v9fs.h"
|
||||
#include "v9fs_vfs.h"
|
||||
#include "fid.h"
|
||||
|
||||
/**
|
||||
* v9fs_fid_add - add a fid to a dentry
|
||||
* @dentry: dentry that the fid is being added to
|
||||
* @fid: fid to add
|
||||
*
|
||||
*/
|
||||
|
||||
static inline void __add_fid(struct dentry *dentry, struct p9_fid *fid)
|
||||
{
|
||||
hlist_add_head(&fid->dlist, (struct hlist_head *)&dentry->d_fsdata);
|
||||
}
|
||||
|
||||
void v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid)
|
||||
{
|
||||
spin_lock(&dentry->d_lock);
|
||||
__add_fid(dentry, fid);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_fid_find - retrieve a fid that belongs to the specified uid
|
||||
* @dentry: dentry to look for fid in
|
||||
* @uid: return fid that belongs to the specified user
|
||||
* @any: if non-zero, return any fid associated with the dentry
|
||||
*
|
||||
*/
|
||||
|
||||
static struct p9_fid *v9fs_fid_find(struct dentry *dentry, kuid_t uid, int any)
|
||||
{
|
||||
struct p9_fid *fid, *ret;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, " dentry: %s (%p) uid %d any %d\n",
|
||||
dentry->d_name.name, dentry, from_kuid(&init_user_ns, uid),
|
||||
any);
|
||||
ret = NULL;
|
||||
/* we'll recheck under lock if there's anything to look in */
|
||||
if (dentry->d_fsdata) {
|
||||
struct hlist_head *h = (struct hlist_head *)&dentry->d_fsdata;
|
||||
spin_lock(&dentry->d_lock);
|
||||
hlist_for_each_entry(fid, h, dlist) {
|
||||
if (any || uid_eq(fid->uid, uid)) {
|
||||
ret = fid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to hold v9ses->rename_sem as long as we hold references
|
||||
* to returned path array. Array element contain pointers to
|
||||
* dentry names.
|
||||
*/
|
||||
static int build_path_from_dentry(struct v9fs_session_info *v9ses,
|
||||
struct dentry *dentry, char ***names)
|
||||
{
|
||||
int n = 0, i;
|
||||
char **wnames;
|
||||
struct dentry *ds;
|
||||
|
||||
for (ds = dentry; !IS_ROOT(ds); ds = ds->d_parent)
|
||||
n++;
|
||||
|
||||
wnames = kmalloc(sizeof(char *) * n, GFP_KERNEL);
|
||||
if (!wnames)
|
||||
goto err_out;
|
||||
|
||||
for (ds = dentry, i = (n-1); i >= 0; i--, ds = ds->d_parent)
|
||||
wnames[i] = (char *)ds->d_name.name;
|
||||
|
||||
*names = wnames;
|
||||
return n;
|
||||
err_out:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry,
|
||||
kuid_t uid, int any)
|
||||
{
|
||||
struct dentry *ds;
|
||||
char **wnames, *uname;
|
||||
int i, n, l, clone, access;
|
||||
struct v9fs_session_info *v9ses;
|
||||
struct p9_fid *fid, *old_fid = NULL;
|
||||
|
||||
v9ses = v9fs_dentry2v9ses(dentry);
|
||||
access = v9ses->flags & V9FS_ACCESS_MASK;
|
||||
fid = v9fs_fid_find(dentry, uid, any);
|
||||
if (fid)
|
||||
return fid;
|
||||
/*
|
||||
* we don't have a matching fid. To do a TWALK we need
|
||||
* parent fid. We need to prevent rename when we want to
|
||||
* look at the parent.
|
||||
*/
|
||||
down_read(&v9ses->rename_sem);
|
||||
ds = dentry->d_parent;
|
||||
fid = v9fs_fid_find(ds, uid, any);
|
||||
if (fid) {
|
||||
/* Found the parent fid do a lookup with that */
|
||||
fid = p9_client_walk(fid, 1, (char **)&dentry->d_name.name, 1);
|
||||
goto fid_out;
|
||||
}
|
||||
up_read(&v9ses->rename_sem);
|
||||
|
||||
/* start from the root and try to do a lookup */
|
||||
fid = v9fs_fid_find(dentry->d_sb->s_root, uid, any);
|
||||
if (!fid) {
|
||||
/* the user is not attached to the fs yet */
|
||||
if (access == V9FS_ACCESS_SINGLE)
|
||||
return ERR_PTR(-EPERM);
|
||||
|
||||
if (v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses))
|
||||
uname = NULL;
|
||||
else
|
||||
uname = v9ses->uname;
|
||||
|
||||
fid = p9_client_attach(v9ses->clnt, NULL, uname, uid,
|
||||
v9ses->aname);
|
||||
if (IS_ERR(fid))
|
||||
return fid;
|
||||
|
||||
v9fs_fid_add(dentry->d_sb->s_root, fid);
|
||||
}
|
||||
/* If we are root ourself just return that */
|
||||
if (dentry->d_sb->s_root == dentry)
|
||||
return fid;
|
||||
/*
|
||||
* Do a multipath walk with attached root.
|
||||
* When walking parent we need to make sure we
|
||||
* don't have a parallel rename happening
|
||||
*/
|
||||
down_read(&v9ses->rename_sem);
|
||||
n = build_path_from_dentry(v9ses, dentry, &wnames);
|
||||
if (n < 0) {
|
||||
fid = ERR_PTR(n);
|
||||
goto err_out;
|
||||
}
|
||||
clone = 1;
|
||||
i = 0;
|
||||
while (i < n) {
|
||||
l = min(n - i, P9_MAXWELEM);
|
||||
/*
|
||||
* We need to hold rename lock when doing a multipath
|
||||
* walk to ensure none of the patch component change
|
||||
*/
|
||||
fid = p9_client_walk(fid, l, &wnames[i], clone);
|
||||
if (IS_ERR(fid)) {
|
||||
if (old_fid) {
|
||||
/*
|
||||
* If we fail, clunk fid which are mapping
|
||||
* to path component and not the last component
|
||||
* of the path.
|
||||
*/
|
||||
p9_client_clunk(old_fid);
|
||||
}
|
||||
kfree(wnames);
|
||||
goto err_out;
|
||||
}
|
||||
old_fid = fid;
|
||||
i += l;
|
||||
clone = 0;
|
||||
}
|
||||
kfree(wnames);
|
||||
fid_out:
|
||||
if (!IS_ERR(fid)) {
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (d_unhashed(dentry)) {
|
||||
spin_unlock(&dentry->d_lock);
|
||||
p9_client_clunk(fid);
|
||||
fid = ERR_PTR(-ENOENT);
|
||||
} else {
|
||||
__add_fid(dentry, fid);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
}
|
||||
err_out:
|
||||
up_read(&v9ses->rename_sem);
|
||||
return fid;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_fid_lookup - lookup for a fid, try to walk if not found
|
||||
* @dentry: dentry to look for fid in
|
||||
*
|
||||
* Look for a fid in the specified dentry for the current user.
|
||||
* If no fid is found, try to create one walking from a fid from the parent
|
||||
* dentry (if it has one), or the root dentry. If the user haven't accessed
|
||||
* the fs yet, attach now and walk from the root.
|
||||
*/
|
||||
|
||||
struct p9_fid *v9fs_fid_lookup(struct dentry *dentry)
|
||||
{
|
||||
kuid_t uid;
|
||||
int any, access;
|
||||
struct v9fs_session_info *v9ses;
|
||||
|
||||
v9ses = v9fs_dentry2v9ses(dentry);
|
||||
access = v9ses->flags & V9FS_ACCESS_MASK;
|
||||
switch (access) {
|
||||
case V9FS_ACCESS_SINGLE:
|
||||
case V9FS_ACCESS_USER:
|
||||
case V9FS_ACCESS_CLIENT:
|
||||
uid = current_fsuid();
|
||||
any = 0;
|
||||
break;
|
||||
|
||||
case V9FS_ACCESS_ANY:
|
||||
uid = v9ses->uid;
|
||||
any = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
uid = INVALID_UID;
|
||||
any = 0;
|
||||
break;
|
||||
}
|
||||
return v9fs_fid_lookup_with_uid(dentry, uid, any);
|
||||
}
|
||||
|
||||
struct p9_fid *v9fs_fid_clone(struct dentry *dentry)
|
||||
{
|
||||
struct p9_fid *fid, *ret;
|
||||
|
||||
fid = v9fs_fid_lookup(dentry);
|
||||
if (IS_ERR(fid))
|
||||
return fid;
|
||||
|
||||
ret = p9_client_walk(fid, 0, NULL, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct p9_fid *v9fs_fid_clone_with_uid(struct dentry *dentry, kuid_t uid)
|
||||
{
|
||||
struct p9_fid *fid, *ret;
|
||||
|
||||
fid = v9fs_fid_lookup_with_uid(dentry, uid, 0);
|
||||
if (IS_ERR(fid))
|
||||
return fid;
|
||||
|
||||
ret = p9_client_walk(fid, 0, NULL, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct p9_fid *v9fs_writeback_fid(struct dentry *dentry)
|
||||
{
|
||||
int err;
|
||||
struct p9_fid *fid;
|
||||
|
||||
fid = v9fs_fid_clone_with_uid(dentry, GLOBAL_ROOT_UID);
|
||||
if (IS_ERR(fid))
|
||||
goto error_out;
|
||||
/*
|
||||
* writeback fid will only be used to write back the
|
||||
* dirty pages. We always request for the open fid in read-write
|
||||
* mode so that a partial page write which result in page
|
||||
* read can work.
|
||||
*/
|
||||
err = p9_client_open(fid, O_RDWR);
|
||||
if (err < 0) {
|
||||
p9_client_clunk(fid);
|
||||
fid = ERR_PTR(err);
|
||||
goto error_out;
|
||||
}
|
||||
error_out:
|
||||
return fid;
|
||||
}
|
||||
30
addons/9p/src/3.10.108/fid.h
Normal file
30
addons/9p/src/3.10.108/fid.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* V9FS FID Management
|
||||
*
|
||||
* Copyright (C) 2005 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
#ifndef FS_9P_FID_H
|
||||
#define FS_9P_FID_H
|
||||
#include <linux/list.h>
|
||||
|
||||
struct p9_fid *v9fs_fid_lookup(struct dentry *dentry);
|
||||
struct p9_fid *v9fs_fid_clone(struct dentry *dentry);
|
||||
void v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid);
|
||||
struct p9_fid *v9fs_writeback_fid(struct dentry *dentry);
|
||||
#endif
|
||||
201
addons/9p/src/3.10.108/mod.c
Normal file
201
addons/9p/src/3.10.108/mod.c
Normal file
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* net/9p/9p.c
|
||||
*
|
||||
* 9P entry point
|
||||
*
|
||||
* Copyright (C) 2007 by Latchesar Ionkov <lucho@ionkov.net>
|
||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/parser.h>
|
||||
#include <net/9p/client.h>
|
||||
#include <net/9p/transport.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#ifdef CONFIG_NET_9P_DEBUG
|
||||
unsigned int p9_debug_level = 0; /* feature-rific global debug level */
|
||||
EXPORT_SYMBOL(p9_debug_level);
|
||||
module_param_named(debug, p9_debug_level, uint, 0);
|
||||
MODULE_PARM_DESC(debug, "9P debugging level");
|
||||
|
||||
void _p9_debug(enum p9_debug_flags level, const char *func,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
struct va_format vaf;
|
||||
va_list args;
|
||||
|
||||
if ((p9_debug_level & level) != level)
|
||||
return;
|
||||
|
||||
va_start(args, fmt);
|
||||
|
||||
vaf.fmt = fmt;
|
||||
vaf.va = &args;
|
||||
|
||||
if (level == P9_DEBUG_9P)
|
||||
pr_notice("(%8.8d) %pV", task_pid_nr(current), &vaf);
|
||||
else
|
||||
pr_notice("-- %s (%d): %pV", func, task_pid_nr(current), &vaf);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
EXPORT_SYMBOL(_p9_debug);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Dynamic Transport Registration Routines
|
||||
*
|
||||
*/
|
||||
|
||||
static DEFINE_SPINLOCK(v9fs_trans_lock);
|
||||
static LIST_HEAD(v9fs_trans_list);
|
||||
|
||||
/**
|
||||
* v9fs_register_trans - register a new transport with 9p
|
||||
* @m: structure describing the transport module and entry points
|
||||
*
|
||||
*/
|
||||
void v9fs_register_trans(struct p9_trans_module *m)
|
||||
{
|
||||
spin_lock(&v9fs_trans_lock);
|
||||
list_add_tail(&m->list, &v9fs_trans_list);
|
||||
spin_unlock(&v9fs_trans_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(v9fs_register_trans);
|
||||
|
||||
/**
|
||||
* v9fs_unregister_trans - unregister a 9p transport
|
||||
* @m: the transport to remove
|
||||
*
|
||||
*/
|
||||
void v9fs_unregister_trans(struct p9_trans_module *m)
|
||||
{
|
||||
spin_lock(&v9fs_trans_lock);
|
||||
list_del_init(&m->list);
|
||||
spin_unlock(&v9fs_trans_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(v9fs_unregister_trans);
|
||||
|
||||
/**
|
||||
* v9fs_get_trans_by_name - get transport with the matching name
|
||||
* @name: string identifying transport
|
||||
*
|
||||
*/
|
||||
struct p9_trans_module *v9fs_get_trans_by_name(char *s)
|
||||
{
|
||||
struct p9_trans_module *t, *found = NULL;
|
||||
|
||||
spin_lock(&v9fs_trans_lock);
|
||||
|
||||
list_for_each_entry(t, &v9fs_trans_list, list)
|
||||
if (strcmp(t->name, s) == 0 &&
|
||||
try_module_get(t->owner)) {
|
||||
found = t;
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock(&v9fs_trans_lock);
|
||||
return found;
|
||||
}
|
||||
EXPORT_SYMBOL(v9fs_get_trans_by_name);
|
||||
|
||||
/**
|
||||
* v9fs_get_default_trans - get the default transport
|
||||
*
|
||||
*/
|
||||
|
||||
struct p9_trans_module *v9fs_get_default_trans(void)
|
||||
{
|
||||
struct p9_trans_module *t, *found = NULL;
|
||||
|
||||
spin_lock(&v9fs_trans_lock);
|
||||
|
||||
list_for_each_entry(t, &v9fs_trans_list, list)
|
||||
if (t->def && try_module_get(t->owner)) {
|
||||
found = t;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
list_for_each_entry(t, &v9fs_trans_list, list)
|
||||
if (try_module_get(t->owner)) {
|
||||
found = t;
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock(&v9fs_trans_lock);
|
||||
return found;
|
||||
}
|
||||
EXPORT_SYMBOL(v9fs_get_default_trans);
|
||||
|
||||
/**
|
||||
* v9fs_put_trans - put trans
|
||||
* @m: transport to put
|
||||
*
|
||||
*/
|
||||
void v9fs_put_trans(struct p9_trans_module *m)
|
||||
{
|
||||
if (m)
|
||||
module_put(m->owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* init_p9 - Initialize module
|
||||
*
|
||||
*/
|
||||
static int __init init_p9(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
p9_error_init();
|
||||
pr_info("Installing 9P2000 support\n");
|
||||
p9_trans_fd_init();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* exit_p9 - shutdown module
|
||||
*
|
||||
*/
|
||||
|
||||
static void __exit exit_p9(void)
|
||||
{
|
||||
pr_info("Unloading 9P2000 support\n");
|
||||
|
||||
p9_trans_fd_exit();
|
||||
}
|
||||
|
||||
module_init(init_p9)
|
||||
module_exit(exit_p9)
|
||||
|
||||
MODULE_AUTHOR("Latchesar Ionkov <lucho@ionkov.net>");
|
||||
MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
|
||||
MODULE_AUTHOR("Ron Minnich <rminnich@lanl.gov>");
|
||||
MODULE_LICENSE("GPL");
|
||||
636
addons/9p/src/3.10.108/protocol.c
Normal file
636
addons/9p/src/3.10.108/protocol.c
Normal file
@@ -0,0 +1,636 @@
|
||||
/*
|
||||
* net/9p/protocol.c
|
||||
*
|
||||
* 9P Protocol Support Code
|
||||
*
|
||||
* Copyright (C) 2008 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
*
|
||||
* Base on code from Anthony Liguori <aliguori@us.ibm.com>
|
||||
* Copyright (C) 2008 by IBM, Corp.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/types.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
#include "protocol.h"
|
||||
|
||||
#include <trace/events/9p.h>
|
||||
|
||||
static int
|
||||
p9pdu_writef(struct p9_fcall *pdu, int proto_version, const char *fmt, ...);
|
||||
|
||||
void p9stat_free(struct p9_wstat *stbuf)
|
||||
{
|
||||
kfree(stbuf->name);
|
||||
kfree(stbuf->uid);
|
||||
kfree(stbuf->gid);
|
||||
kfree(stbuf->muid);
|
||||
kfree(stbuf->extension);
|
||||
}
|
||||
EXPORT_SYMBOL(p9stat_free);
|
||||
|
||||
size_t pdu_read(struct p9_fcall *pdu, void *data, size_t size)
|
||||
{
|
||||
size_t len = min(pdu->size - pdu->offset, size);
|
||||
memcpy(data, &pdu->sdata[pdu->offset], len);
|
||||
pdu->offset += len;
|
||||
return size - len;
|
||||
}
|
||||
|
||||
static size_t pdu_write(struct p9_fcall *pdu, const void *data, size_t size)
|
||||
{
|
||||
size_t len = min(pdu->capacity - pdu->size, size);
|
||||
memcpy(&pdu->sdata[pdu->size], data, len);
|
||||
pdu->size += len;
|
||||
return size - len;
|
||||
}
|
||||
|
||||
static size_t
|
||||
pdu_write_u(struct p9_fcall *pdu, const char __user *udata, size_t size)
|
||||
{
|
||||
size_t len = min(pdu->capacity - pdu->size, size);
|
||||
if (copy_from_user(&pdu->sdata[pdu->size], udata, len))
|
||||
len = 0;
|
||||
|
||||
pdu->size += len;
|
||||
return size - len;
|
||||
}
|
||||
|
||||
/*
|
||||
b - int8_t
|
||||
w - int16_t
|
||||
d - int32_t
|
||||
q - int64_t
|
||||
s - string
|
||||
u - numeric uid
|
||||
g - numeric gid
|
||||
S - stat
|
||||
Q - qid
|
||||
D - data blob (int32_t size followed by void *, results are not freed)
|
||||
T - array of strings (int16_t count, followed by strings)
|
||||
R - array of qids (int16_t count, followed by qids)
|
||||
A - stat for 9p2000.L (p9_stat_dotl)
|
||||
? - if optional = 1, continue parsing
|
||||
*/
|
||||
|
||||
static int
|
||||
p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt,
|
||||
va_list ap)
|
||||
{
|
||||
const char *ptr;
|
||||
int errcode = 0;
|
||||
|
||||
for (ptr = fmt; *ptr; ptr++) {
|
||||
switch (*ptr) {
|
||||
case 'b':{
|
||||
int8_t *val = va_arg(ap, int8_t *);
|
||||
if (pdu_read(pdu, val, sizeof(*val))) {
|
||||
errcode = -EFAULT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'w':{
|
||||
int16_t *val = va_arg(ap, int16_t *);
|
||||
__le16 le_val;
|
||||
if (pdu_read(pdu, &le_val, sizeof(le_val))) {
|
||||
errcode = -EFAULT;
|
||||
break;
|
||||
}
|
||||
*val = le16_to_cpu(le_val);
|
||||
}
|
||||
break;
|
||||
case 'd':{
|
||||
int32_t *val = va_arg(ap, int32_t *);
|
||||
__le32 le_val;
|
||||
if (pdu_read(pdu, &le_val, sizeof(le_val))) {
|
||||
errcode = -EFAULT;
|
||||
break;
|
||||
}
|
||||
*val = le32_to_cpu(le_val);
|
||||
}
|
||||
break;
|
||||
case 'q':{
|
||||
int64_t *val = va_arg(ap, int64_t *);
|
||||
__le64 le_val;
|
||||
if (pdu_read(pdu, &le_val, sizeof(le_val))) {
|
||||
errcode = -EFAULT;
|
||||
break;
|
||||
}
|
||||
*val = le64_to_cpu(le_val);
|
||||
}
|
||||
break;
|
||||
case 's':{
|
||||
char **sptr = va_arg(ap, char **);
|
||||
uint16_t len;
|
||||
|
||||
errcode = p9pdu_readf(pdu, proto_version,
|
||||
"w", &len);
|
||||
if (errcode)
|
||||
break;
|
||||
|
||||
*sptr = kmalloc(len + 1, GFP_NOFS);
|
||||
if (*sptr == NULL) {
|
||||
errcode = -EFAULT;
|
||||
break;
|
||||
}
|
||||
if (pdu_read(pdu, *sptr, len)) {
|
||||
errcode = -EFAULT;
|
||||
kfree(*sptr);
|
||||
*sptr = NULL;
|
||||
} else
|
||||
(*sptr)[len] = 0;
|
||||
}
|
||||
break;
|
||||
case 'u': {
|
||||
kuid_t *uid = va_arg(ap, kuid_t *);
|
||||
__le32 le_val;
|
||||
if (pdu_read(pdu, &le_val, sizeof(le_val))) {
|
||||
errcode = -EFAULT;
|
||||
break;
|
||||
}
|
||||
*uid = make_kuid(&init_user_ns,
|
||||
le32_to_cpu(le_val));
|
||||
} break;
|
||||
case 'g': {
|
||||
kgid_t *gid = va_arg(ap, kgid_t *);
|
||||
__le32 le_val;
|
||||
if (pdu_read(pdu, &le_val, sizeof(le_val))) {
|
||||
errcode = -EFAULT;
|
||||
break;
|
||||
}
|
||||
*gid = make_kgid(&init_user_ns,
|
||||
le32_to_cpu(le_val));
|
||||
} break;
|
||||
case 'Q':{
|
||||
struct p9_qid *qid =
|
||||
va_arg(ap, struct p9_qid *);
|
||||
|
||||
errcode = p9pdu_readf(pdu, proto_version, "bdq",
|
||||
&qid->type, &qid->version,
|
||||
&qid->path);
|
||||
}
|
||||
break;
|
||||
case 'S':{
|
||||
struct p9_wstat *stbuf =
|
||||
va_arg(ap, struct p9_wstat *);
|
||||
|
||||
memset(stbuf, 0, sizeof(struct p9_wstat));
|
||||
stbuf->n_uid = stbuf->n_muid = INVALID_UID;
|
||||
stbuf->n_gid = INVALID_GID;
|
||||
|
||||
errcode =
|
||||
p9pdu_readf(pdu, proto_version,
|
||||
"wwdQdddqssss?sugu",
|
||||
&stbuf->size, &stbuf->type,
|
||||
&stbuf->dev, &stbuf->qid,
|
||||
&stbuf->mode, &stbuf->atime,
|
||||
&stbuf->mtime, &stbuf->length,
|
||||
&stbuf->name, &stbuf->uid,
|
||||
&stbuf->gid, &stbuf->muid,
|
||||
&stbuf->extension,
|
||||
&stbuf->n_uid, &stbuf->n_gid,
|
||||
&stbuf->n_muid);
|
||||
if (errcode)
|
||||
p9stat_free(stbuf);
|
||||
}
|
||||
break;
|
||||
case 'D':{
|
||||
uint32_t *count = va_arg(ap, uint32_t *);
|
||||
void **data = va_arg(ap, void **);
|
||||
|
||||
errcode =
|
||||
p9pdu_readf(pdu, proto_version, "d", count);
|
||||
if (!errcode) {
|
||||
*count =
|
||||
min_t(uint32_t, *count,
|
||||
pdu->size - pdu->offset);
|
||||
*data = &pdu->sdata[pdu->offset];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'T':{
|
||||
uint16_t *nwname = va_arg(ap, uint16_t *);
|
||||
char ***wnames = va_arg(ap, char ***);
|
||||
|
||||
errcode = p9pdu_readf(pdu, proto_version,
|
||||
"w", nwname);
|
||||
if (!errcode) {
|
||||
*wnames =
|
||||
kmalloc(sizeof(char *) * *nwname,
|
||||
GFP_NOFS);
|
||||
if (!*wnames)
|
||||
errcode = -ENOMEM;
|
||||
}
|
||||
|
||||
if (!errcode) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < *nwname; i++) {
|
||||
errcode =
|
||||
p9pdu_readf(pdu,
|
||||
proto_version,
|
||||
"s",
|
||||
&(*wnames)[i]);
|
||||
if (errcode)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (errcode) {
|
||||
if (*wnames) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < *nwname; i++)
|
||||
kfree((*wnames)[i]);
|
||||
}
|
||||
kfree(*wnames);
|
||||
*wnames = NULL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'R':{
|
||||
int16_t *nwqid = va_arg(ap, int16_t *);
|
||||
struct p9_qid **wqids =
|
||||
va_arg(ap, struct p9_qid **);
|
||||
|
||||
*wqids = NULL;
|
||||
|
||||
errcode =
|
||||
p9pdu_readf(pdu, proto_version, "w", nwqid);
|
||||
if (!errcode) {
|
||||
*wqids =
|
||||
kmalloc(*nwqid *
|
||||
sizeof(struct p9_qid),
|
||||
GFP_NOFS);
|
||||
if (*wqids == NULL)
|
||||
errcode = -ENOMEM;
|
||||
}
|
||||
|
||||
if (!errcode) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < *nwqid; i++) {
|
||||
errcode =
|
||||
p9pdu_readf(pdu,
|
||||
proto_version,
|
||||
"Q",
|
||||
&(*wqids)[i]);
|
||||
if (errcode)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (errcode) {
|
||||
kfree(*wqids);
|
||||
*wqids = NULL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'A': {
|
||||
struct p9_stat_dotl *stbuf =
|
||||
va_arg(ap, struct p9_stat_dotl *);
|
||||
|
||||
memset(stbuf, 0, sizeof(struct p9_stat_dotl));
|
||||
errcode =
|
||||
p9pdu_readf(pdu, proto_version,
|
||||
"qQdugqqqqqqqqqqqqqqq",
|
||||
&stbuf->st_result_mask,
|
||||
&stbuf->qid,
|
||||
&stbuf->st_mode,
|
||||
&stbuf->st_uid, &stbuf->st_gid,
|
||||
&stbuf->st_nlink,
|
||||
&stbuf->st_rdev, &stbuf->st_size,
|
||||
&stbuf->st_blksize, &stbuf->st_blocks,
|
||||
&stbuf->st_atime_sec,
|
||||
&stbuf->st_atime_nsec,
|
||||
&stbuf->st_mtime_sec,
|
||||
&stbuf->st_mtime_nsec,
|
||||
&stbuf->st_ctime_sec,
|
||||
&stbuf->st_ctime_nsec,
|
||||
&stbuf->st_btime_sec,
|
||||
&stbuf->st_btime_nsec,
|
||||
&stbuf->st_gen,
|
||||
&stbuf->st_data_version);
|
||||
}
|
||||
break;
|
||||
case '?':
|
||||
if ((proto_version != p9_proto_2000u) &&
|
||||
(proto_version != p9_proto_2000L))
|
||||
return 0;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
break;
|
||||
}
|
||||
|
||||
if (errcode)
|
||||
break;
|
||||
}
|
||||
|
||||
return errcode;
|
||||
}
|
||||
|
||||
int
|
||||
p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt,
|
||||
va_list ap)
|
||||
{
|
||||
const char *ptr;
|
||||
int errcode = 0;
|
||||
|
||||
for (ptr = fmt; *ptr; ptr++) {
|
||||
switch (*ptr) {
|
||||
case 'b':{
|
||||
int8_t val = va_arg(ap, int);
|
||||
if (pdu_write(pdu, &val, sizeof(val)))
|
||||
errcode = -EFAULT;
|
||||
}
|
||||
break;
|
||||
case 'w':{
|
||||
__le16 val = cpu_to_le16(va_arg(ap, int));
|
||||
if (pdu_write(pdu, &val, sizeof(val)))
|
||||
errcode = -EFAULT;
|
||||
}
|
||||
break;
|
||||
case 'd':{
|
||||
__le32 val = cpu_to_le32(va_arg(ap, int32_t));
|
||||
if (pdu_write(pdu, &val, sizeof(val)))
|
||||
errcode = -EFAULT;
|
||||
}
|
||||
break;
|
||||
case 'q':{
|
||||
__le64 val = cpu_to_le64(va_arg(ap, int64_t));
|
||||
if (pdu_write(pdu, &val, sizeof(val)))
|
||||
errcode = -EFAULT;
|
||||
}
|
||||
break;
|
||||
case 's':{
|
||||
const char *sptr = va_arg(ap, const char *);
|
||||
uint16_t len = 0;
|
||||
if (sptr)
|
||||
len = min_t(size_t, strlen(sptr),
|
||||
USHRT_MAX);
|
||||
|
||||
errcode = p9pdu_writef(pdu, proto_version,
|
||||
"w", len);
|
||||
if (!errcode && pdu_write(pdu, sptr, len))
|
||||
errcode = -EFAULT;
|
||||
}
|
||||
break;
|
||||
case 'u': {
|
||||
kuid_t uid = va_arg(ap, kuid_t);
|
||||
__le32 val = cpu_to_le32(
|
||||
from_kuid(&init_user_ns, uid));
|
||||
if (pdu_write(pdu, &val, sizeof(val)))
|
||||
errcode = -EFAULT;
|
||||
} break;
|
||||
case 'g': {
|
||||
kgid_t gid = va_arg(ap, kgid_t);
|
||||
__le32 val = cpu_to_le32(
|
||||
from_kgid(&init_user_ns, gid));
|
||||
if (pdu_write(pdu, &val, sizeof(val)))
|
||||
errcode = -EFAULT;
|
||||
} break;
|
||||
case 'Q':{
|
||||
const struct p9_qid *qid =
|
||||
va_arg(ap, const struct p9_qid *);
|
||||
errcode =
|
||||
p9pdu_writef(pdu, proto_version, "bdq",
|
||||
qid->type, qid->version,
|
||||
qid->path);
|
||||
} break;
|
||||
case 'S':{
|
||||
const struct p9_wstat *stbuf =
|
||||
va_arg(ap, const struct p9_wstat *);
|
||||
errcode =
|
||||
p9pdu_writef(pdu, proto_version,
|
||||
"wwdQdddqssss?sugu",
|
||||
stbuf->size, stbuf->type,
|
||||
stbuf->dev, &stbuf->qid,
|
||||
stbuf->mode, stbuf->atime,
|
||||
stbuf->mtime, stbuf->length,
|
||||
stbuf->name, stbuf->uid,
|
||||
stbuf->gid, stbuf->muid,
|
||||
stbuf->extension, stbuf->n_uid,
|
||||
stbuf->n_gid, stbuf->n_muid);
|
||||
} break;
|
||||
case 'D':{
|
||||
uint32_t count = va_arg(ap, uint32_t);
|
||||
const void *data = va_arg(ap, const void *);
|
||||
|
||||
errcode = p9pdu_writef(pdu, proto_version, "d",
|
||||
count);
|
||||
if (!errcode && pdu_write(pdu, data, count))
|
||||
errcode = -EFAULT;
|
||||
}
|
||||
break;
|
||||
case 'U':{
|
||||
int32_t count = va_arg(ap, int32_t);
|
||||
const char __user *udata =
|
||||
va_arg(ap, const void __user *);
|
||||
errcode = p9pdu_writef(pdu, proto_version, "d",
|
||||
count);
|
||||
if (!errcode && pdu_write_u(pdu, udata, count))
|
||||
errcode = -EFAULT;
|
||||
}
|
||||
break;
|
||||
case 'T':{
|
||||
uint16_t nwname = va_arg(ap, int);
|
||||
const char **wnames = va_arg(ap, const char **);
|
||||
|
||||
errcode = p9pdu_writef(pdu, proto_version, "w",
|
||||
nwname);
|
||||
if (!errcode) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nwname; i++) {
|
||||
errcode =
|
||||
p9pdu_writef(pdu,
|
||||
proto_version,
|
||||
"s",
|
||||
wnames[i]);
|
||||
if (errcode)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'R':{
|
||||
int16_t nwqid = va_arg(ap, int);
|
||||
struct p9_qid *wqids =
|
||||
va_arg(ap, struct p9_qid *);
|
||||
|
||||
errcode = p9pdu_writef(pdu, proto_version, "w",
|
||||
nwqid);
|
||||
if (!errcode) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nwqid; i++) {
|
||||
errcode =
|
||||
p9pdu_writef(pdu,
|
||||
proto_version,
|
||||
"Q",
|
||||
&wqids[i]);
|
||||
if (errcode)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'I':{
|
||||
struct p9_iattr_dotl *p9attr = va_arg(ap,
|
||||
struct p9_iattr_dotl *);
|
||||
|
||||
errcode = p9pdu_writef(pdu, proto_version,
|
||||
"ddugqqqqq",
|
||||
p9attr->valid,
|
||||
p9attr->mode,
|
||||
p9attr->uid,
|
||||
p9attr->gid,
|
||||
p9attr->size,
|
||||
p9attr->atime_sec,
|
||||
p9attr->atime_nsec,
|
||||
p9attr->mtime_sec,
|
||||
p9attr->mtime_nsec);
|
||||
}
|
||||
break;
|
||||
case '?':
|
||||
if ((proto_version != p9_proto_2000u) &&
|
||||
(proto_version != p9_proto_2000L))
|
||||
return 0;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
break;
|
||||
}
|
||||
|
||||
if (errcode)
|
||||
break;
|
||||
}
|
||||
|
||||
return errcode;
|
||||
}
|
||||
|
||||
int p9pdu_readf(struct p9_fcall *pdu, int proto_version, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int ret;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = p9pdu_vreadf(pdu, proto_version, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
p9pdu_writef(struct p9_fcall *pdu, int proto_version, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int ret;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = p9pdu_vwritef(pdu, proto_version, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int p9stat_read(struct p9_client *clnt, char *buf, int len, struct p9_wstat *st)
|
||||
{
|
||||
struct p9_fcall fake_pdu;
|
||||
int ret;
|
||||
|
||||
fake_pdu.size = len;
|
||||
fake_pdu.capacity = len;
|
||||
fake_pdu.sdata = buf;
|
||||
fake_pdu.offset = 0;
|
||||
|
||||
ret = p9pdu_readf(&fake_pdu, clnt->proto_version, "S", st);
|
||||
if (ret) {
|
||||
p9_debug(P9_DEBUG_9P, "<<< p9stat_read failed: %d\n", ret);
|
||||
trace_9p_protocol_dump(clnt, &fake_pdu);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(p9stat_read);
|
||||
|
||||
int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type)
|
||||
{
|
||||
pdu->id = type;
|
||||
return p9pdu_writef(pdu, 0, "dbw", 0, type, tag);
|
||||
}
|
||||
|
||||
int p9pdu_finalize(struct p9_client *clnt, struct p9_fcall *pdu)
|
||||
{
|
||||
int size = pdu->size;
|
||||
int err;
|
||||
|
||||
pdu->size = 0;
|
||||
err = p9pdu_writef(pdu, 0, "d", size);
|
||||
pdu->size = size;
|
||||
|
||||
trace_9p_protocol_dump(clnt, pdu);
|
||||
p9_debug(P9_DEBUG_9P, ">>> size=%d type: %d tag: %d\n",
|
||||
pdu->size, pdu->id, pdu->tag);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void p9pdu_reset(struct p9_fcall *pdu)
|
||||
{
|
||||
pdu->offset = 0;
|
||||
pdu->size = 0;
|
||||
}
|
||||
|
||||
int p9dirent_read(struct p9_client *clnt, char *buf, int len,
|
||||
struct p9_dirent *dirent)
|
||||
{
|
||||
struct p9_fcall fake_pdu;
|
||||
int ret;
|
||||
char *nameptr;
|
||||
|
||||
fake_pdu.size = len;
|
||||
fake_pdu.capacity = len;
|
||||
fake_pdu.sdata = buf;
|
||||
fake_pdu.offset = 0;
|
||||
|
||||
ret = p9pdu_readf(&fake_pdu, clnt->proto_version, "Qqbs", &dirent->qid,
|
||||
&dirent->d_off, &dirent->d_type, &nameptr);
|
||||
if (ret) {
|
||||
p9_debug(P9_DEBUG_9P, "<<< p9dirent_read failed: %d\n", ret);
|
||||
trace_9p_protocol_dump(clnt, &fake_pdu);
|
||||
goto out;
|
||||
}
|
||||
|
||||
strcpy(dirent->d_name, nameptr);
|
||||
kfree(nameptr);
|
||||
|
||||
out:
|
||||
return fake_pdu.offset;
|
||||
}
|
||||
EXPORT_SYMBOL(p9dirent_read);
|
||||
34
addons/9p/src/3.10.108/protocol.h
Normal file
34
addons/9p/src/3.10.108/protocol.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* net/9p/protocol.h
|
||||
*
|
||||
* 9P Protocol Support Code
|
||||
*
|
||||
* Copyright (C) 2008 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
*
|
||||
* Base on code from Anthony Liguori <aliguori@us.ibm.com>
|
||||
* Copyright (C) 2008 by IBM, Corp.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
int p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt,
|
||||
va_list ap);
|
||||
int p9pdu_readf(struct p9_fcall *pdu, int proto_version, const char *fmt, ...);
|
||||
int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type);
|
||||
int p9pdu_finalize(struct p9_client *clnt, struct p9_fcall *pdu);
|
||||
void p9pdu_reset(struct p9_fcall *pdu);
|
||||
size_t pdu_read(struct p9_fcall *pdu, void *data, size_t size);
|
||||
69
addons/9p/src/3.10.108/trans_common.c
Normal file
69
addons/9p/src/3.10.108/trans_common.c
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright IBM Corporation, 2010
|
||||
* Author Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include "trans_common.h"
|
||||
|
||||
/**
|
||||
* p9_release_req_pages - Release pages after the transaction.
|
||||
*/
|
||||
void p9_release_pages(struct page **pages, int nr_pages)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr_pages; i++)
|
||||
if (pages[i])
|
||||
put_page(pages[i]);
|
||||
}
|
||||
EXPORT_SYMBOL(p9_release_pages);
|
||||
|
||||
/**
|
||||
* p9_nr_pages - Return number of pages needed to accommodate the payload.
|
||||
*/
|
||||
int p9_nr_pages(char *data, int len)
|
||||
{
|
||||
unsigned long start_page, end_page;
|
||||
start_page = (unsigned long)data >> PAGE_SHIFT;
|
||||
end_page = ((unsigned long)data + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||
return end_page - start_page;
|
||||
}
|
||||
EXPORT_SYMBOL(p9_nr_pages);
|
||||
|
||||
/**
|
||||
* payload_gup - Translates user buffer into kernel pages and
|
||||
* pins them either for read/write through get_user_pages_fast().
|
||||
* @req: Request to be sent to server.
|
||||
* @pdata_off: data offset into the first page after translation (gup).
|
||||
* @pdata_len: Total length of the IO. gup may not return requested # of pages.
|
||||
* @nr_pages: number of pages to accommodate the payload
|
||||
* @rw: Indicates if the pages are for read or write.
|
||||
*/
|
||||
|
||||
int p9_payload_gup(char *data, int *nr_pages, struct page **pages, int write)
|
||||
{
|
||||
int nr_mapped_pages;
|
||||
|
||||
nr_mapped_pages = get_user_pages_fast((unsigned long)data,
|
||||
*nr_pages, write, pages);
|
||||
if (nr_mapped_pages <= 0)
|
||||
return nr_mapped_pages;
|
||||
|
||||
*nr_pages = nr_mapped_pages;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(p9_payload_gup);
|
||||
17
addons/9p/src/3.10.108/trans_common.h
Normal file
17
addons/9p/src/3.10.108/trans_common.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright IBM Corporation, 2010
|
||||
* Author Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
void p9_release_pages(struct page **, int);
|
||||
int p9_payload_gup(char *, int *, struct page **, int);
|
||||
int p9_nr_pages(char *, int);
|
||||
1090
addons/9p/src/3.10.108/trans_fd.c
Normal file
1090
addons/9p/src/3.10.108/trans_fd.c
Normal file
File diff suppressed because it is too large
Load Diff
719
addons/9p/src/3.10.108/trans_rdma.c
Normal file
719
addons/9p/src/3.10.108/trans_rdma.c
Normal file
@@ -0,0 +1,719 @@
|
||||
/*
|
||||
* linux/fs/9p/trans_rdma.c
|
||||
*
|
||||
* RDMA transport layer based on the trans_fd.c implementation.
|
||||
*
|
||||
* Copyright (C) 2008 by Tom Tucker <tom@opengridcomputing.com>
|
||||
* Copyright (C) 2006 by Russ Cox <rsc@swtch.com>
|
||||
* Copyright (C) 2004-2005 by Latchesar Ionkov <lucho@ionkov.net>
|
||||
* Copyright (C) 2004-2008 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
* Copyright (C) 1997-2002 by Ron Minnich <rminnich@sarnoff.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/in.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/un.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/parser.h>
|
||||
#include <linux/semaphore.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
#include <net/9p/transport.h>
|
||||
#include <rdma/ib_verbs.h>
|
||||
#include <rdma/rdma_cm.h>
|
||||
|
||||
#define P9_PORT 5640
|
||||
#define P9_RDMA_SQ_DEPTH 32
|
||||
#define P9_RDMA_RQ_DEPTH 32
|
||||
#define P9_RDMA_SEND_SGE 4
|
||||
#define P9_RDMA_RECV_SGE 4
|
||||
#define P9_RDMA_IRD 0
|
||||
#define P9_RDMA_ORD 0
|
||||
#define P9_RDMA_TIMEOUT 30000 /* 30 seconds */
|
||||
#define P9_RDMA_MAXSIZE (4*4096) /* Min SGE is 4, so we can
|
||||
* safely advertise a maxsize
|
||||
* of 64k */
|
||||
|
||||
/**
|
||||
* struct p9_trans_rdma - RDMA transport instance
|
||||
*
|
||||
* @state: tracks the transport state machine for connection setup and tear down
|
||||
* @cm_id: The RDMA CM ID
|
||||
* @pd: Protection Domain pointer
|
||||
* @qp: Queue Pair pointer
|
||||
* @cq: Completion Queue pointer
|
||||
* @dm_mr: DMA Memory Region pointer
|
||||
* @lkey: The local access only memory region key
|
||||
* @timeout: Number of uSecs to wait for connection management events
|
||||
* @sq_depth: The depth of the Send Queue
|
||||
* @sq_sem: Semaphore for the SQ
|
||||
* @rq_depth: The depth of the Receive Queue.
|
||||
* @rq_count: Count of requests in the Receive Queue.
|
||||
* @addr: The remote peer's address
|
||||
* @req_lock: Protects the active request list
|
||||
* @cm_done: Completion event for connection management tracking
|
||||
*/
|
||||
struct p9_trans_rdma {
|
||||
enum {
|
||||
P9_RDMA_INIT,
|
||||
P9_RDMA_ADDR_RESOLVED,
|
||||
P9_RDMA_ROUTE_RESOLVED,
|
||||
P9_RDMA_CONNECTED,
|
||||
P9_RDMA_FLUSHING,
|
||||
P9_RDMA_CLOSING,
|
||||
P9_RDMA_CLOSED,
|
||||
} state;
|
||||
struct rdma_cm_id *cm_id;
|
||||
struct ib_pd *pd;
|
||||
struct ib_qp *qp;
|
||||
struct ib_cq *cq;
|
||||
struct ib_mr *dma_mr;
|
||||
u32 lkey;
|
||||
long timeout;
|
||||
int sq_depth;
|
||||
struct semaphore sq_sem;
|
||||
int rq_depth;
|
||||
atomic_t rq_count;
|
||||
struct sockaddr_in addr;
|
||||
spinlock_t req_lock;
|
||||
|
||||
struct completion cm_done;
|
||||
};
|
||||
|
||||
/**
|
||||
* p9_rdma_context - Keeps track of in-process WR
|
||||
*
|
||||
* @wc_op: The original WR op for when the CQE completes in error.
|
||||
* @busa: Bus address to unmap when the WR completes
|
||||
* @req: Keeps track of requests (send)
|
||||
* @rc: Keepts track of replies (receive)
|
||||
*/
|
||||
struct p9_rdma_req;
|
||||
struct p9_rdma_context {
|
||||
enum ib_wc_opcode wc_op;
|
||||
dma_addr_t busa;
|
||||
union {
|
||||
struct p9_req_t *req;
|
||||
struct p9_fcall *rc;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* p9_rdma_opts - Collection of mount options
|
||||
* @port: port of connection
|
||||
* @sq_depth: The requested depth of the SQ. This really doesn't need
|
||||
* to be any deeper than the number of threads used in the client
|
||||
* @rq_depth: The depth of the RQ. Should be greater than or equal to SQ depth
|
||||
* @timeout: Time to wait in msecs for CM events
|
||||
*/
|
||||
struct p9_rdma_opts {
|
||||
short port;
|
||||
int sq_depth;
|
||||
int rq_depth;
|
||||
long timeout;
|
||||
};
|
||||
|
||||
/*
|
||||
* Option Parsing (code inspired by NFS code)
|
||||
*/
|
||||
enum {
|
||||
/* Options that take integer arguments */
|
||||
Opt_port, Opt_rq_depth, Opt_sq_depth, Opt_timeout, Opt_err,
|
||||
};
|
||||
|
||||
static match_table_t tokens = {
|
||||
{Opt_port, "port=%u"},
|
||||
{Opt_sq_depth, "sq=%u"},
|
||||
{Opt_rq_depth, "rq=%u"},
|
||||
{Opt_timeout, "timeout=%u"},
|
||||
{Opt_err, NULL},
|
||||
};
|
||||
|
||||
/**
|
||||
* parse_opts - parse mount options into rdma options structure
|
||||
* @params: options string passed from mount
|
||||
* @opts: rdma transport-specific structure to parse options into
|
||||
*
|
||||
* Returns 0 upon success, -ERRNO upon failure
|
||||
*/
|
||||
static int parse_opts(char *params, struct p9_rdma_opts *opts)
|
||||
{
|
||||
char *p;
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
int option;
|
||||
char *options, *tmp_options;
|
||||
|
||||
opts->port = P9_PORT;
|
||||
opts->sq_depth = P9_RDMA_SQ_DEPTH;
|
||||
opts->rq_depth = P9_RDMA_RQ_DEPTH;
|
||||
opts->timeout = P9_RDMA_TIMEOUT;
|
||||
|
||||
if (!params)
|
||||
return 0;
|
||||
|
||||
tmp_options = kstrdup(params, GFP_KERNEL);
|
||||
if (!tmp_options) {
|
||||
p9_debug(P9_DEBUG_ERROR,
|
||||
"failed to allocate copy of option string\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
options = tmp_options;
|
||||
|
||||
while ((p = strsep(&options, ",")) != NULL) {
|
||||
int token;
|
||||
int r;
|
||||
if (!*p)
|
||||
continue;
|
||||
token = match_token(p, tokens, args);
|
||||
r = match_int(&args[0], &option);
|
||||
if (r < 0) {
|
||||
p9_debug(P9_DEBUG_ERROR,
|
||||
"integer field, but no integer?\n");
|
||||
continue;
|
||||
}
|
||||
switch (token) {
|
||||
case Opt_port:
|
||||
opts->port = option;
|
||||
break;
|
||||
case Opt_sq_depth:
|
||||
opts->sq_depth = option;
|
||||
break;
|
||||
case Opt_rq_depth:
|
||||
opts->rq_depth = option;
|
||||
break;
|
||||
case Opt_timeout:
|
||||
opts->timeout = option;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/* RQ must be at least as large as the SQ */
|
||||
opts->rq_depth = max(opts->rq_depth, opts->sq_depth);
|
||||
kfree(tmp_options);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
p9_cm_event_handler(struct rdma_cm_id *id, struct rdma_cm_event *event)
|
||||
{
|
||||
struct p9_client *c = id->context;
|
||||
struct p9_trans_rdma *rdma = c->trans;
|
||||
switch (event->event) {
|
||||
case RDMA_CM_EVENT_ADDR_RESOLVED:
|
||||
BUG_ON(rdma->state != P9_RDMA_INIT);
|
||||
rdma->state = P9_RDMA_ADDR_RESOLVED;
|
||||
break;
|
||||
|
||||
case RDMA_CM_EVENT_ROUTE_RESOLVED:
|
||||
BUG_ON(rdma->state != P9_RDMA_ADDR_RESOLVED);
|
||||
rdma->state = P9_RDMA_ROUTE_RESOLVED;
|
||||
break;
|
||||
|
||||
case RDMA_CM_EVENT_ESTABLISHED:
|
||||
BUG_ON(rdma->state != P9_RDMA_ROUTE_RESOLVED);
|
||||
rdma->state = P9_RDMA_CONNECTED;
|
||||
break;
|
||||
|
||||
case RDMA_CM_EVENT_DISCONNECTED:
|
||||
if (rdma)
|
||||
rdma->state = P9_RDMA_CLOSED;
|
||||
if (c)
|
||||
c->status = Disconnected;
|
||||
break;
|
||||
|
||||
case RDMA_CM_EVENT_TIMEWAIT_EXIT:
|
||||
break;
|
||||
|
||||
case RDMA_CM_EVENT_ADDR_CHANGE:
|
||||
case RDMA_CM_EVENT_ROUTE_ERROR:
|
||||
case RDMA_CM_EVENT_DEVICE_REMOVAL:
|
||||
case RDMA_CM_EVENT_MULTICAST_JOIN:
|
||||
case RDMA_CM_EVENT_MULTICAST_ERROR:
|
||||
case RDMA_CM_EVENT_REJECTED:
|
||||
case RDMA_CM_EVENT_CONNECT_REQUEST:
|
||||
case RDMA_CM_EVENT_CONNECT_RESPONSE:
|
||||
case RDMA_CM_EVENT_CONNECT_ERROR:
|
||||
case RDMA_CM_EVENT_ADDR_ERROR:
|
||||
case RDMA_CM_EVENT_UNREACHABLE:
|
||||
c->status = Disconnected;
|
||||
rdma_disconnect(rdma->cm_id);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
complete(&rdma->cm_done);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_recv(struct p9_client *client, struct p9_trans_rdma *rdma,
|
||||
struct p9_rdma_context *c, enum ib_wc_status status, u32 byte_len)
|
||||
{
|
||||
struct p9_req_t *req;
|
||||
int err = 0;
|
||||
int16_t tag;
|
||||
|
||||
req = NULL;
|
||||
ib_dma_unmap_single(rdma->cm_id->device, c->busa, client->msize,
|
||||
DMA_FROM_DEVICE);
|
||||
|
||||
if (status != IB_WC_SUCCESS)
|
||||
goto err_out;
|
||||
|
||||
err = p9_parse_header(c->rc, NULL, NULL, &tag, 1);
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
req = p9_tag_lookup(client, tag);
|
||||
if (!req)
|
||||
goto err_out;
|
||||
|
||||
req->rc = c->rc;
|
||||
req->status = REQ_STATUS_RCVD;
|
||||
p9_client_cb(client, req);
|
||||
|
||||
return;
|
||||
|
||||
err_out:
|
||||
p9_debug(P9_DEBUG_ERROR, "req %p err %d status %d\n", req, err, status);
|
||||
rdma->state = P9_RDMA_FLUSHING;
|
||||
client->status = Disconnected;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_send(struct p9_client *client, struct p9_trans_rdma *rdma,
|
||||
struct p9_rdma_context *c, enum ib_wc_status status, u32 byte_len)
|
||||
{
|
||||
ib_dma_unmap_single(rdma->cm_id->device,
|
||||
c->busa, c->req->tc->size,
|
||||
DMA_TO_DEVICE);
|
||||
}
|
||||
|
||||
static void qp_event_handler(struct ib_event *event, void *context)
|
||||
{
|
||||
p9_debug(P9_DEBUG_ERROR, "QP event %d context %p\n",
|
||||
event->event, context);
|
||||
}
|
||||
|
||||
static void cq_comp_handler(struct ib_cq *cq, void *cq_context)
|
||||
{
|
||||
struct p9_client *client = cq_context;
|
||||
struct p9_trans_rdma *rdma = client->trans;
|
||||
int ret;
|
||||
struct ib_wc wc;
|
||||
|
||||
ib_req_notify_cq(rdma->cq, IB_CQ_NEXT_COMP);
|
||||
while ((ret = ib_poll_cq(cq, 1, &wc)) > 0) {
|
||||
struct p9_rdma_context *c = (void *) (unsigned long) wc.wr_id;
|
||||
|
||||
switch (c->wc_op) {
|
||||
case IB_WC_RECV:
|
||||
atomic_dec(&rdma->rq_count);
|
||||
handle_recv(client, rdma, c, wc.status, wc.byte_len);
|
||||
break;
|
||||
|
||||
case IB_WC_SEND:
|
||||
handle_send(client, rdma, c, wc.status, wc.byte_len);
|
||||
up(&rdma->sq_sem);
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_err("unexpected completion type, c->wc_op=%d, wc.opcode=%d, status=%d\n",
|
||||
c->wc_op, wc.opcode, wc.status);
|
||||
break;
|
||||
}
|
||||
kfree(c);
|
||||
}
|
||||
}
|
||||
|
||||
static void cq_event_handler(struct ib_event *e, void *v)
|
||||
{
|
||||
p9_debug(P9_DEBUG_ERROR, "CQ event %d context %p\n", e->event, v);
|
||||
}
|
||||
|
||||
static void rdma_destroy_trans(struct p9_trans_rdma *rdma)
|
||||
{
|
||||
if (!rdma)
|
||||
return;
|
||||
|
||||
if (rdma->dma_mr && !IS_ERR(rdma->dma_mr))
|
||||
ib_dereg_mr(rdma->dma_mr);
|
||||
|
||||
if (rdma->qp && !IS_ERR(rdma->qp))
|
||||
ib_destroy_qp(rdma->qp);
|
||||
|
||||
if (rdma->pd && !IS_ERR(rdma->pd))
|
||||
ib_dealloc_pd(rdma->pd);
|
||||
|
||||
if (rdma->cq && !IS_ERR(rdma->cq))
|
||||
ib_destroy_cq(rdma->cq);
|
||||
|
||||
if (rdma->cm_id && !IS_ERR(rdma->cm_id))
|
||||
rdma_destroy_id(rdma->cm_id);
|
||||
|
||||
kfree(rdma);
|
||||
}
|
||||
|
||||
static int
|
||||
post_recv(struct p9_client *client, struct p9_rdma_context *c)
|
||||
{
|
||||
struct p9_trans_rdma *rdma = client->trans;
|
||||
struct ib_recv_wr wr, *bad_wr;
|
||||
struct ib_sge sge;
|
||||
|
||||
c->busa = ib_dma_map_single(rdma->cm_id->device,
|
||||
c->rc->sdata, client->msize,
|
||||
DMA_FROM_DEVICE);
|
||||
if (ib_dma_mapping_error(rdma->cm_id->device, c->busa))
|
||||
goto error;
|
||||
|
||||
sge.addr = c->busa;
|
||||
sge.length = client->msize;
|
||||
sge.lkey = rdma->lkey;
|
||||
|
||||
wr.next = NULL;
|
||||
c->wc_op = IB_WC_RECV;
|
||||
wr.wr_id = (unsigned long) c;
|
||||
wr.sg_list = &sge;
|
||||
wr.num_sge = 1;
|
||||
return ib_post_recv(rdma->qp, &wr, &bad_wr);
|
||||
|
||||
error:
|
||||
p9_debug(P9_DEBUG_ERROR, "EIO\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int rdma_request(struct p9_client *client, struct p9_req_t *req)
|
||||
{
|
||||
struct p9_trans_rdma *rdma = client->trans;
|
||||
struct ib_send_wr wr, *bad_wr;
|
||||
struct ib_sge sge;
|
||||
int err = 0;
|
||||
unsigned long flags;
|
||||
struct p9_rdma_context *c = NULL;
|
||||
struct p9_rdma_context *rpl_context = NULL;
|
||||
|
||||
/* Allocate an fcall for the reply */
|
||||
rpl_context = kmalloc(sizeof *rpl_context, GFP_NOFS);
|
||||
if (!rpl_context) {
|
||||
err = -ENOMEM;
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the request has a buffer, steal it, otherwise
|
||||
* allocate a new one. Typically, requests should already
|
||||
* have receive buffers allocated and just swap them around
|
||||
*/
|
||||
if (!req->rc) {
|
||||
req->rc = kmalloc(sizeof(struct p9_fcall)+client->msize,
|
||||
GFP_NOFS);
|
||||
if (req->rc) {
|
||||
req->rc->sdata = (char *) req->rc +
|
||||
sizeof(struct p9_fcall);
|
||||
req->rc->capacity = client->msize;
|
||||
}
|
||||
}
|
||||
rpl_context->rc = req->rc;
|
||||
if (!rpl_context->rc) {
|
||||
err = -ENOMEM;
|
||||
goto err_free2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Post a receive buffer for this request. We need to ensure
|
||||
* there is a reply buffer available for every outstanding
|
||||
* request. A flushed request can result in no reply for an
|
||||
* outstanding request, so we must keep a count to avoid
|
||||
* overflowing the RQ.
|
||||
*/
|
||||
if (atomic_inc_return(&rdma->rq_count) <= rdma->rq_depth) {
|
||||
err = post_recv(client, rpl_context);
|
||||
if (err)
|
||||
goto err_free1;
|
||||
} else
|
||||
atomic_dec(&rdma->rq_count);
|
||||
|
||||
/* remove posted receive buffer from request structure */
|
||||
req->rc = NULL;
|
||||
|
||||
/* Post the request */
|
||||
c = kmalloc(sizeof *c, GFP_NOFS);
|
||||
if (!c) {
|
||||
err = -ENOMEM;
|
||||
goto err_free1;
|
||||
}
|
||||
c->req = req;
|
||||
|
||||
c->busa = ib_dma_map_single(rdma->cm_id->device,
|
||||
c->req->tc->sdata, c->req->tc->size,
|
||||
DMA_TO_DEVICE);
|
||||
if (ib_dma_mapping_error(rdma->cm_id->device, c->busa))
|
||||
goto error;
|
||||
|
||||
sge.addr = c->busa;
|
||||
sge.length = c->req->tc->size;
|
||||
sge.lkey = rdma->lkey;
|
||||
|
||||
wr.next = NULL;
|
||||
c->wc_op = IB_WC_SEND;
|
||||
wr.wr_id = (unsigned long) c;
|
||||
wr.opcode = IB_WR_SEND;
|
||||
wr.send_flags = IB_SEND_SIGNALED;
|
||||
wr.sg_list = &sge;
|
||||
wr.num_sge = 1;
|
||||
|
||||
if (down_interruptible(&rdma->sq_sem))
|
||||
goto error;
|
||||
|
||||
return ib_post_send(rdma->qp, &wr, &bad_wr);
|
||||
|
||||
error:
|
||||
kfree(c);
|
||||
kfree(rpl_context->rc);
|
||||
kfree(rpl_context);
|
||||
p9_debug(P9_DEBUG_ERROR, "EIO\n");
|
||||
return -EIO;
|
||||
err_free1:
|
||||
kfree(rpl_context->rc);
|
||||
err_free2:
|
||||
kfree(rpl_context);
|
||||
err_close:
|
||||
spin_lock_irqsave(&rdma->req_lock, flags);
|
||||
if (rdma->state < P9_RDMA_CLOSING) {
|
||||
rdma->state = P9_RDMA_CLOSING;
|
||||
spin_unlock_irqrestore(&rdma->req_lock, flags);
|
||||
rdma_disconnect(rdma->cm_id);
|
||||
} else
|
||||
spin_unlock_irqrestore(&rdma->req_lock, flags);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void rdma_close(struct p9_client *client)
|
||||
{
|
||||
struct p9_trans_rdma *rdma;
|
||||
|
||||
if (!client)
|
||||
return;
|
||||
|
||||
rdma = client->trans;
|
||||
if (!rdma)
|
||||
return;
|
||||
|
||||
client->status = Disconnected;
|
||||
rdma_disconnect(rdma->cm_id);
|
||||
rdma_destroy_trans(rdma);
|
||||
}
|
||||
|
||||
/**
|
||||
* alloc_rdma - Allocate and initialize the rdma transport structure
|
||||
* @opts: Mount options structure
|
||||
*/
|
||||
static struct p9_trans_rdma *alloc_rdma(struct p9_rdma_opts *opts)
|
||||
{
|
||||
struct p9_trans_rdma *rdma;
|
||||
|
||||
rdma = kzalloc(sizeof(struct p9_trans_rdma), GFP_KERNEL);
|
||||
if (!rdma)
|
||||
return NULL;
|
||||
|
||||
rdma->sq_depth = opts->sq_depth;
|
||||
rdma->rq_depth = opts->rq_depth;
|
||||
rdma->timeout = opts->timeout;
|
||||
spin_lock_init(&rdma->req_lock);
|
||||
init_completion(&rdma->cm_done);
|
||||
sema_init(&rdma->sq_sem, rdma->sq_depth);
|
||||
atomic_set(&rdma->rq_count, 0);
|
||||
|
||||
return rdma;
|
||||
}
|
||||
|
||||
/* its not clear to me we can do anything after send has been posted */
|
||||
static int rdma_cancel(struct p9_client *client, struct p9_req_t *req)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* trans_create_rdma - Transport method for creating atransport instance
|
||||
* @client: client instance
|
||||
* @addr: IP address string
|
||||
* @args: Mount options string
|
||||
*/
|
||||
static int
|
||||
rdma_create_trans(struct p9_client *client, const char *addr, char *args)
|
||||
{
|
||||
int err;
|
||||
struct p9_rdma_opts opts;
|
||||
struct p9_trans_rdma *rdma;
|
||||
struct rdma_conn_param conn_param;
|
||||
struct ib_qp_init_attr qp_attr;
|
||||
struct ib_device_attr devattr;
|
||||
|
||||
/* Parse the transport specific mount options */
|
||||
err = parse_opts(args, &opts);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Create and initialize the RDMA transport structure */
|
||||
rdma = alloc_rdma(&opts);
|
||||
if (!rdma)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Create the RDMA CM ID */
|
||||
rdma->cm_id = rdma_create_id(p9_cm_event_handler, client, RDMA_PS_TCP,
|
||||
IB_QPT_RC);
|
||||
if (IS_ERR(rdma->cm_id))
|
||||
goto error;
|
||||
|
||||
/* Associate the client with the transport */
|
||||
client->trans = rdma;
|
||||
|
||||
/* Resolve the server's address */
|
||||
rdma->addr.sin_family = AF_INET;
|
||||
rdma->addr.sin_addr.s_addr = in_aton(addr);
|
||||
rdma->addr.sin_port = htons(opts.port);
|
||||
err = rdma_resolve_addr(rdma->cm_id, NULL,
|
||||
(struct sockaddr *)&rdma->addr,
|
||||
rdma->timeout);
|
||||
if (err)
|
||||
goto error;
|
||||
err = wait_for_completion_interruptible(&rdma->cm_done);
|
||||
if (err || (rdma->state != P9_RDMA_ADDR_RESOLVED))
|
||||
goto error;
|
||||
|
||||
/* Resolve the route to the server */
|
||||
err = rdma_resolve_route(rdma->cm_id, rdma->timeout);
|
||||
if (err)
|
||||
goto error;
|
||||
err = wait_for_completion_interruptible(&rdma->cm_done);
|
||||
if (err || (rdma->state != P9_RDMA_ROUTE_RESOLVED))
|
||||
goto error;
|
||||
|
||||
/* Query the device attributes */
|
||||
err = ib_query_device(rdma->cm_id->device, &devattr);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* Create the Completion Queue */
|
||||
rdma->cq = ib_create_cq(rdma->cm_id->device, cq_comp_handler,
|
||||
cq_event_handler, client,
|
||||
opts.sq_depth + opts.rq_depth + 1, 0);
|
||||
if (IS_ERR(rdma->cq))
|
||||
goto error;
|
||||
ib_req_notify_cq(rdma->cq, IB_CQ_NEXT_COMP);
|
||||
|
||||
/* Create the Protection Domain */
|
||||
rdma->pd = ib_alloc_pd(rdma->cm_id->device);
|
||||
if (IS_ERR(rdma->pd))
|
||||
goto error;
|
||||
|
||||
/* Cache the DMA lkey in the transport */
|
||||
rdma->dma_mr = NULL;
|
||||
if (devattr.device_cap_flags & IB_DEVICE_LOCAL_DMA_LKEY)
|
||||
rdma->lkey = rdma->cm_id->device->local_dma_lkey;
|
||||
else {
|
||||
rdma->dma_mr = ib_get_dma_mr(rdma->pd, IB_ACCESS_LOCAL_WRITE);
|
||||
if (IS_ERR(rdma->dma_mr))
|
||||
goto error;
|
||||
rdma->lkey = rdma->dma_mr->lkey;
|
||||
}
|
||||
|
||||
/* Create the Queue Pair */
|
||||
memset(&qp_attr, 0, sizeof qp_attr);
|
||||
qp_attr.event_handler = qp_event_handler;
|
||||
qp_attr.qp_context = client;
|
||||
qp_attr.cap.max_send_wr = opts.sq_depth;
|
||||
qp_attr.cap.max_recv_wr = opts.rq_depth;
|
||||
qp_attr.cap.max_send_sge = P9_RDMA_SEND_SGE;
|
||||
qp_attr.cap.max_recv_sge = P9_RDMA_RECV_SGE;
|
||||
qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR;
|
||||
qp_attr.qp_type = IB_QPT_RC;
|
||||
qp_attr.send_cq = rdma->cq;
|
||||
qp_attr.recv_cq = rdma->cq;
|
||||
err = rdma_create_qp(rdma->cm_id, rdma->pd, &qp_attr);
|
||||
if (err)
|
||||
goto error;
|
||||
rdma->qp = rdma->cm_id->qp;
|
||||
|
||||
/* Request a connection */
|
||||
memset(&conn_param, 0, sizeof(conn_param));
|
||||
conn_param.private_data = NULL;
|
||||
conn_param.private_data_len = 0;
|
||||
conn_param.responder_resources = P9_RDMA_IRD;
|
||||
conn_param.initiator_depth = P9_RDMA_ORD;
|
||||
err = rdma_connect(rdma->cm_id, &conn_param);
|
||||
if (err)
|
||||
goto error;
|
||||
err = wait_for_completion_interruptible(&rdma->cm_done);
|
||||
if (err || (rdma->state != P9_RDMA_CONNECTED))
|
||||
goto error;
|
||||
|
||||
client->status = Connected;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
rdma_destroy_trans(rdma);
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
static struct p9_trans_module p9_rdma_trans = {
|
||||
.name = "rdma",
|
||||
.maxsize = P9_RDMA_MAXSIZE,
|
||||
.def = 0,
|
||||
.owner = THIS_MODULE,
|
||||
.create = rdma_create_trans,
|
||||
.close = rdma_close,
|
||||
.request = rdma_request,
|
||||
.cancel = rdma_cancel,
|
||||
};
|
||||
|
||||
/**
|
||||
* p9_trans_rdma_init - Register the 9P RDMA transport driver
|
||||
*/
|
||||
static int __init p9_trans_rdma_init(void)
|
||||
{
|
||||
v9fs_register_trans(&p9_rdma_trans);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit p9_trans_rdma_exit(void)
|
||||
{
|
||||
v9fs_unregister_trans(&p9_rdma_trans);
|
||||
}
|
||||
|
||||
module_init(p9_trans_rdma_init);
|
||||
module_exit(p9_trans_rdma_exit);
|
||||
|
||||
MODULE_AUTHOR("Tom Tucker <tom@opengridcomputing.com>");
|
||||
MODULE_DESCRIPTION("RDMA Transport for 9P");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
730
addons/9p/src/3.10.108/trans_virtio.c
Normal file
730
addons/9p/src/3.10.108/trans_virtio.c
Normal file
@@ -0,0 +1,730 @@
|
||||
/*
|
||||
* The Virtio 9p transport driver
|
||||
*
|
||||
* This is a block based transport driver based on the lguest block driver
|
||||
* code.
|
||||
*
|
||||
* Copyright (C) 2007, 2008 Eric Van Hensbergen, IBM Corporation
|
||||
*
|
||||
* Based on virtio console driver
|
||||
* Copyright (C) 2006, 2007 Rusty Russell, IBM Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/in.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/un.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <linux/parser.h>
|
||||
#include <net/9p/client.h>
|
||||
#include <net/9p/transport.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/swap.h>
|
||||
#include <linux/virtio.h>
|
||||
#include <linux/virtio_9p.h>
|
||||
#include "trans_common.h"
|
||||
|
||||
#define VIRTQUEUE_NUM 128
|
||||
|
||||
/* a single mutex to manage channel initialization and attachment */
|
||||
static DEFINE_MUTEX(virtio_9p_lock);
|
||||
static DECLARE_WAIT_QUEUE_HEAD(vp_wq);
|
||||
static atomic_t vp_pinned = ATOMIC_INIT(0);
|
||||
|
||||
/**
|
||||
* struct virtio_chan - per-instance transport information
|
||||
* @initialized: whether the channel is initialized
|
||||
* @inuse: whether the channel is in use
|
||||
* @lock: protects multiple elements within this structure
|
||||
* @client: client instance
|
||||
* @vdev: virtio dev associated with this channel
|
||||
* @vq: virtio queue associated with this channel
|
||||
* @sg: scatter gather list which is used to pack a request (protected?)
|
||||
*
|
||||
* We keep all per-channel information in a structure.
|
||||
* This structure is allocated within the devices dev->mem space.
|
||||
* A pointer to the structure will get put in the transport private.
|
||||
*
|
||||
*/
|
||||
|
||||
struct virtio_chan {
|
||||
bool inuse;
|
||||
|
||||
spinlock_t lock;
|
||||
|
||||
struct p9_client *client;
|
||||
struct virtio_device *vdev;
|
||||
struct virtqueue *vq;
|
||||
int ring_bufs_avail;
|
||||
wait_queue_head_t *vc_wq;
|
||||
/* This is global limit. Since we don't have a global structure,
|
||||
* will be placing it in each channel.
|
||||
*/
|
||||
unsigned long p9_max_pages;
|
||||
/* Scatterlist: can be too big for stack. */
|
||||
struct scatterlist sg[VIRTQUEUE_NUM];
|
||||
|
||||
int tag_len;
|
||||
/*
|
||||
* tag name to identify a mount Non-null terminated
|
||||
*/
|
||||
char *tag;
|
||||
|
||||
struct list_head chan_list;
|
||||
};
|
||||
|
||||
static struct list_head virtio_chan_list;
|
||||
|
||||
/* How many bytes left in this page. */
|
||||
static unsigned int rest_of_page(void *data)
|
||||
{
|
||||
return PAGE_SIZE - ((unsigned long)data % PAGE_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* p9_virtio_close - reclaim resources of a channel
|
||||
* @client: client instance
|
||||
*
|
||||
* This reclaims a channel by freeing its resources and
|
||||
* reseting its inuse flag.
|
||||
*
|
||||
*/
|
||||
|
||||
static void p9_virtio_close(struct p9_client *client)
|
||||
{
|
||||
struct virtio_chan *chan = client->trans;
|
||||
|
||||
mutex_lock(&virtio_9p_lock);
|
||||
if (chan)
|
||||
chan->inuse = false;
|
||||
mutex_unlock(&virtio_9p_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* req_done - callback which signals activity from the server
|
||||
* @vq: virtio queue activity was received on
|
||||
*
|
||||
* This notifies us that the server has triggered some activity
|
||||
* on the virtio channel - most likely a response to request we
|
||||
* sent. Figure out which requests now have responses and wake up
|
||||
* those threads.
|
||||
*
|
||||
* Bugs: could do with some additional sanity checking, but appears to work.
|
||||
*
|
||||
*/
|
||||
|
||||
static void req_done(struct virtqueue *vq)
|
||||
{
|
||||
struct virtio_chan *chan = vq->vdev->priv;
|
||||
struct p9_fcall *rc;
|
||||
unsigned int len;
|
||||
struct p9_req_t *req;
|
||||
unsigned long flags;
|
||||
|
||||
p9_debug(P9_DEBUG_TRANS, ": request done\n");
|
||||
|
||||
while (1) {
|
||||
spin_lock_irqsave(&chan->lock, flags);
|
||||
rc = virtqueue_get_buf(chan->vq, &len);
|
||||
if (rc == NULL) {
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
break;
|
||||
}
|
||||
chan->ring_bufs_avail = 1;
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
/* Wakeup if anyone waiting for VirtIO ring space. */
|
||||
wake_up(chan->vc_wq);
|
||||
p9_debug(P9_DEBUG_TRANS, ": rc %p\n", rc);
|
||||
p9_debug(P9_DEBUG_TRANS, ": lookup tag %d\n", rc->tag);
|
||||
req = p9_tag_lookup(chan->client, rc->tag);
|
||||
req->status = REQ_STATUS_RCVD;
|
||||
p9_client_cb(chan->client, req);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pack_sg_list - pack a scatter gather list from a linear buffer
|
||||
* @sg: scatter/gather list to pack into
|
||||
* @start: which segment of the sg_list to start at
|
||||
* @limit: maximum segment to pack data to
|
||||
* @data: data to pack into scatter/gather list
|
||||
* @count: amount of data to pack into the scatter/gather list
|
||||
*
|
||||
* sg_lists have multiple segments of various sizes. This will pack
|
||||
* arbitrary data into an existing scatter gather list, segmenting the
|
||||
* data as necessary within constraints.
|
||||
*
|
||||
*/
|
||||
|
||||
static int pack_sg_list(struct scatterlist *sg, int start,
|
||||
int limit, char *data, int count)
|
||||
{
|
||||
int s;
|
||||
int index = start;
|
||||
|
||||
while (count) {
|
||||
s = rest_of_page(data);
|
||||
if (s > count)
|
||||
s = count;
|
||||
BUG_ON(index > limit);
|
||||
/* Make sure we don't terminate early. */
|
||||
sg_unmark_end(&sg[index]);
|
||||
sg_set_buf(&sg[index++], data, s);
|
||||
count -= s;
|
||||
data += s;
|
||||
}
|
||||
if (index-start)
|
||||
sg_mark_end(&sg[index - 1]);
|
||||
return index-start;
|
||||
}
|
||||
|
||||
/* We don't currently allow canceling of virtio requests */
|
||||
static int p9_virtio_cancel(struct p9_client *client, struct p9_req_t *req)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* pack_sg_list_p - Just like pack_sg_list. Instead of taking a buffer,
|
||||
* this takes a list of pages.
|
||||
* @sg: scatter/gather list to pack into
|
||||
* @start: which segment of the sg_list to start at
|
||||
* @pdata: a list of pages to add into sg.
|
||||
* @nr_pages: number of pages to pack into the scatter/gather list
|
||||
* @data: data to pack into scatter/gather list
|
||||
* @count: amount of data to pack into the scatter/gather list
|
||||
*/
|
||||
static int
|
||||
pack_sg_list_p(struct scatterlist *sg, int start, int limit,
|
||||
struct page **pdata, int nr_pages, char *data, int count)
|
||||
{
|
||||
int i = 0, s;
|
||||
int data_off;
|
||||
int index = start;
|
||||
|
||||
BUG_ON(nr_pages > (limit - start));
|
||||
/*
|
||||
* if the first page doesn't start at
|
||||
* page boundary find the offset
|
||||
*/
|
||||
data_off = offset_in_page(data);
|
||||
while (nr_pages) {
|
||||
s = rest_of_page(data);
|
||||
if (s > count)
|
||||
s = count;
|
||||
/* Make sure we don't terminate early. */
|
||||
sg_unmark_end(&sg[index]);
|
||||
sg_set_page(&sg[index++], pdata[i++], s, data_off);
|
||||
data_off = 0;
|
||||
data += s;
|
||||
count -= s;
|
||||
nr_pages--;
|
||||
}
|
||||
|
||||
if (index-start)
|
||||
sg_mark_end(&sg[index - 1]);
|
||||
return index - start;
|
||||
}
|
||||
|
||||
/**
|
||||
* p9_virtio_request - issue a request
|
||||
* @client: client instance issuing the request
|
||||
* @req: request to be issued
|
||||
*
|
||||
*/
|
||||
|
||||
static int
|
||||
p9_virtio_request(struct p9_client *client, struct p9_req_t *req)
|
||||
{
|
||||
int err;
|
||||
int in, out, out_sgs, in_sgs;
|
||||
unsigned long flags;
|
||||
struct virtio_chan *chan = client->trans;
|
||||
struct scatterlist *sgs[2];
|
||||
|
||||
p9_debug(P9_DEBUG_TRANS, "9p debug: virtio request\n");
|
||||
|
||||
req->status = REQ_STATUS_SENT;
|
||||
req_retry:
|
||||
spin_lock_irqsave(&chan->lock, flags);
|
||||
|
||||
out_sgs = in_sgs = 0;
|
||||
/* Handle out VirtIO ring buffers */
|
||||
out = pack_sg_list(chan->sg, 0,
|
||||
VIRTQUEUE_NUM, req->tc->sdata, req->tc->size);
|
||||
if (out)
|
||||
sgs[out_sgs++] = chan->sg;
|
||||
|
||||
in = pack_sg_list(chan->sg, out,
|
||||
VIRTQUEUE_NUM, req->rc->sdata, req->rc->capacity);
|
||||
if (in)
|
||||
sgs[out_sgs + in_sgs++] = chan->sg + out;
|
||||
|
||||
err = virtqueue_add_sgs(chan->vq, sgs, out_sgs, in_sgs, req->tc,
|
||||
GFP_ATOMIC);
|
||||
if (err < 0) {
|
||||
if (err == -ENOSPC) {
|
||||
chan->ring_bufs_avail = 0;
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
err = wait_event_interruptible(*chan->vc_wq,
|
||||
chan->ring_bufs_avail);
|
||||
if (err == -ERESTARTSYS)
|
||||
return err;
|
||||
|
||||
p9_debug(P9_DEBUG_TRANS, "Retry virtio request\n");
|
||||
goto req_retry;
|
||||
} else {
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
p9_debug(P9_DEBUG_TRANS,
|
||||
"virtio rpc add_sgs returned failure\n");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
virtqueue_kick(chan->vq);
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
|
||||
p9_debug(P9_DEBUG_TRANS, "virtio request kicked\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int p9_get_mapped_pages(struct virtio_chan *chan,
|
||||
struct page **pages, char *data,
|
||||
int nr_pages, int write, int kern_buf)
|
||||
{
|
||||
int err;
|
||||
if (!kern_buf) {
|
||||
/*
|
||||
* We allow only p9_max_pages pinned. We wait for the
|
||||
* Other zc request to finish here
|
||||
*/
|
||||
if (atomic_read(&vp_pinned) >= chan->p9_max_pages) {
|
||||
err = wait_event_interruptible(vp_wq,
|
||||
(atomic_read(&vp_pinned) < chan->p9_max_pages));
|
||||
if (err == -ERESTARTSYS)
|
||||
return err;
|
||||
}
|
||||
err = p9_payload_gup(data, &nr_pages, pages, write);
|
||||
if (err < 0)
|
||||
return err;
|
||||
atomic_add(nr_pages, &vp_pinned);
|
||||
} else {
|
||||
/* kernel buffer, no need to pin pages */
|
||||
int s, index = 0;
|
||||
int count = nr_pages;
|
||||
while (nr_pages) {
|
||||
s = rest_of_page(data);
|
||||
if (is_vmalloc_addr(data))
|
||||
pages[index++] = vmalloc_to_page(data);
|
||||
else
|
||||
pages[index++] = kmap_to_page(data);
|
||||
data += s;
|
||||
nr_pages--;
|
||||
}
|
||||
nr_pages = count;
|
||||
}
|
||||
return nr_pages;
|
||||
}
|
||||
|
||||
/**
|
||||
* p9_virtio_zc_request - issue a zero copy request
|
||||
* @client: client instance issuing the request
|
||||
* @req: request to be issued
|
||||
* @uidata: user bffer that should be ued for zero copy read
|
||||
* @uodata: user buffer that shoud be user for zero copy write
|
||||
* @inlen: read buffer size
|
||||
* @olen: write buffer size
|
||||
* @hdrlen: reader header size, This is the size of response protocol data
|
||||
*
|
||||
*/
|
||||
static int
|
||||
p9_virtio_zc_request(struct p9_client *client, struct p9_req_t *req,
|
||||
char *uidata, char *uodata, int inlen,
|
||||
int outlen, int in_hdr_len, int kern_buf)
|
||||
{
|
||||
int in, out, err, out_sgs, in_sgs;
|
||||
unsigned long flags;
|
||||
int in_nr_pages = 0, out_nr_pages = 0;
|
||||
struct page **in_pages = NULL, **out_pages = NULL;
|
||||
struct virtio_chan *chan = client->trans;
|
||||
struct scatterlist *sgs[4];
|
||||
|
||||
p9_debug(P9_DEBUG_TRANS, "virtio request\n");
|
||||
|
||||
if (uodata) {
|
||||
out_nr_pages = p9_nr_pages(uodata, outlen);
|
||||
out_pages = kmalloc(sizeof(struct page *) * out_nr_pages,
|
||||
GFP_NOFS);
|
||||
if (!out_pages) {
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
out_nr_pages = p9_get_mapped_pages(chan, out_pages, uodata,
|
||||
out_nr_pages, 0, kern_buf);
|
||||
if (out_nr_pages < 0) {
|
||||
err = out_nr_pages;
|
||||
kfree(out_pages);
|
||||
out_pages = NULL;
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
if (uidata) {
|
||||
in_nr_pages = p9_nr_pages(uidata, inlen);
|
||||
in_pages = kmalloc(sizeof(struct page *) * in_nr_pages,
|
||||
GFP_NOFS);
|
||||
if (!in_pages) {
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
in_nr_pages = p9_get_mapped_pages(chan, in_pages, uidata,
|
||||
in_nr_pages, 1, kern_buf);
|
||||
if (in_nr_pages < 0) {
|
||||
err = in_nr_pages;
|
||||
kfree(in_pages);
|
||||
in_pages = NULL;
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
req->status = REQ_STATUS_SENT;
|
||||
req_retry_pinned:
|
||||
spin_lock_irqsave(&chan->lock, flags);
|
||||
|
||||
out_sgs = in_sgs = 0;
|
||||
|
||||
/* out data */
|
||||
out = pack_sg_list(chan->sg, 0,
|
||||
VIRTQUEUE_NUM, req->tc->sdata, req->tc->size);
|
||||
|
||||
if (out)
|
||||
sgs[out_sgs++] = chan->sg;
|
||||
|
||||
if (out_pages) {
|
||||
sgs[out_sgs++] = chan->sg + out;
|
||||
out += pack_sg_list_p(chan->sg, out, VIRTQUEUE_NUM,
|
||||
out_pages, out_nr_pages, uodata, outlen);
|
||||
}
|
||||
|
||||
/*
|
||||
* Take care of in data
|
||||
* For example TREAD have 11.
|
||||
* 11 is the read/write header = PDU Header(7) + IO Size (4).
|
||||
* Arrange in such a way that server places header in the
|
||||
* alloced memory and payload onto the user buffer.
|
||||
*/
|
||||
in = pack_sg_list(chan->sg, out,
|
||||
VIRTQUEUE_NUM, req->rc->sdata, in_hdr_len);
|
||||
if (in)
|
||||
sgs[out_sgs + in_sgs++] = chan->sg + out;
|
||||
|
||||
if (in_pages) {
|
||||
sgs[out_sgs + in_sgs++] = chan->sg + out + in;
|
||||
in += pack_sg_list_p(chan->sg, out + in, VIRTQUEUE_NUM,
|
||||
in_pages, in_nr_pages, uidata, inlen);
|
||||
}
|
||||
|
||||
BUG_ON(out_sgs + in_sgs > ARRAY_SIZE(sgs));
|
||||
err = virtqueue_add_sgs(chan->vq, sgs, out_sgs, in_sgs, req->tc,
|
||||
GFP_ATOMIC);
|
||||
if (err < 0) {
|
||||
if (err == -ENOSPC) {
|
||||
chan->ring_bufs_avail = 0;
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
err = wait_event_interruptible(*chan->vc_wq,
|
||||
chan->ring_bufs_avail);
|
||||
if (err == -ERESTARTSYS)
|
||||
goto err_out;
|
||||
|
||||
p9_debug(P9_DEBUG_TRANS, "Retry virtio request\n");
|
||||
goto req_retry_pinned;
|
||||
} else {
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
p9_debug(P9_DEBUG_TRANS,
|
||||
"virtio rpc add_sgs returned failure\n");
|
||||
err = -EIO;
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
virtqueue_kick(chan->vq);
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
p9_debug(P9_DEBUG_TRANS, "virtio request kicked\n");
|
||||
err = wait_event_interruptible(*req->wq,
|
||||
req->status >= REQ_STATUS_RCVD);
|
||||
/*
|
||||
* Non kernel buffers are pinned, unpin them
|
||||
*/
|
||||
err_out:
|
||||
if (!kern_buf) {
|
||||
if (in_pages) {
|
||||
p9_release_pages(in_pages, in_nr_pages);
|
||||
atomic_sub(in_nr_pages, &vp_pinned);
|
||||
}
|
||||
if (out_pages) {
|
||||
p9_release_pages(out_pages, out_nr_pages);
|
||||
atomic_sub(out_nr_pages, &vp_pinned);
|
||||
}
|
||||
/* wakeup anybody waiting for slots to pin pages */
|
||||
wake_up(&vp_wq);
|
||||
}
|
||||
kfree(in_pages);
|
||||
kfree(out_pages);
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t p9_mount_tag_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct virtio_chan *chan;
|
||||
struct virtio_device *vdev;
|
||||
|
||||
vdev = dev_to_virtio(dev);
|
||||
chan = vdev->priv;
|
||||
|
||||
return snprintf(buf, chan->tag_len + 1, "%s", chan->tag);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(mount_tag, 0444, p9_mount_tag_show, NULL);
|
||||
|
||||
/**
|
||||
* p9_virtio_probe - probe for existence of 9P virtio channels
|
||||
* @vdev: virtio device to probe
|
||||
*
|
||||
* This probes for existing virtio channels.
|
||||
*
|
||||
*/
|
||||
|
||||
static int p9_virtio_probe(struct virtio_device *vdev)
|
||||
{
|
||||
__u16 tag_len;
|
||||
char *tag;
|
||||
int err;
|
||||
struct virtio_chan *chan;
|
||||
|
||||
chan = kmalloc(sizeof(struct virtio_chan), GFP_KERNEL);
|
||||
if (!chan) {
|
||||
pr_err("Failed to allocate virtio 9P channel\n");
|
||||
err = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
chan->vdev = vdev;
|
||||
|
||||
/* We expect one virtqueue, for requests. */
|
||||
chan->vq = virtio_find_single_vq(vdev, req_done, "requests");
|
||||
if (IS_ERR(chan->vq)) {
|
||||
err = PTR_ERR(chan->vq);
|
||||
goto out_free_vq;
|
||||
}
|
||||
chan->vq->vdev->priv = chan;
|
||||
spin_lock_init(&chan->lock);
|
||||
|
||||
sg_init_table(chan->sg, VIRTQUEUE_NUM);
|
||||
|
||||
chan->inuse = false;
|
||||
if (virtio_has_feature(vdev, VIRTIO_9P_MOUNT_TAG)) {
|
||||
vdev->config->get(vdev,
|
||||
offsetof(struct virtio_9p_config, tag_len),
|
||||
&tag_len, sizeof(tag_len));
|
||||
} else {
|
||||
err = -EINVAL;
|
||||
goto out_free_vq;
|
||||
}
|
||||
tag = kmalloc(tag_len, GFP_KERNEL);
|
||||
if (!tag) {
|
||||
err = -ENOMEM;
|
||||
goto out_free_vq;
|
||||
}
|
||||
vdev->config->get(vdev, offsetof(struct virtio_9p_config, tag),
|
||||
tag, tag_len);
|
||||
chan->tag = tag;
|
||||
chan->tag_len = tag_len;
|
||||
err = sysfs_create_file(&(vdev->dev.kobj), &dev_attr_mount_tag.attr);
|
||||
if (err) {
|
||||
goto out_free_tag;
|
||||
}
|
||||
chan->vc_wq = kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL);
|
||||
if (!chan->vc_wq) {
|
||||
err = -ENOMEM;
|
||||
goto out_free_tag;
|
||||
}
|
||||
init_waitqueue_head(chan->vc_wq);
|
||||
chan->ring_bufs_avail = 1;
|
||||
/* Ceiling limit to avoid denial of service attacks */
|
||||
chan->p9_max_pages = nr_free_buffer_pages()/4;
|
||||
|
||||
mutex_lock(&virtio_9p_lock);
|
||||
list_add_tail(&chan->chan_list, &virtio_chan_list);
|
||||
mutex_unlock(&virtio_9p_lock);
|
||||
|
||||
/* Let udev rules use the new mount_tag attribute. */
|
||||
kobject_uevent(&(vdev->dev.kobj), KOBJ_CHANGE);
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_tag:
|
||||
kfree(tag);
|
||||
out_free_vq:
|
||||
vdev->config->del_vqs(vdev);
|
||||
kfree(chan);
|
||||
fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* p9_virtio_create - allocate a new virtio channel
|
||||
* @client: client instance invoking this transport
|
||||
* @devname: string identifying the channel to connect to (unused)
|
||||
* @args: args passed from sys_mount() for per-transport options (unused)
|
||||
*
|
||||
* This sets up a transport channel for 9p communication. Right now
|
||||
* we only match the first available channel, but eventually we couldlook up
|
||||
* alternate channels by matching devname versus a virtio_config entry.
|
||||
* We use a simple reference count mechanism to ensure that only a single
|
||||
* mount has a channel open at a time.
|
||||
*
|
||||
*/
|
||||
|
||||
static int
|
||||
p9_virtio_create(struct p9_client *client, const char *devname, char *args)
|
||||
{
|
||||
struct virtio_chan *chan;
|
||||
int ret = -ENOENT;
|
||||
int found = 0;
|
||||
|
||||
mutex_lock(&virtio_9p_lock);
|
||||
list_for_each_entry(chan, &virtio_chan_list, chan_list) {
|
||||
if (!strncmp(devname, chan->tag, chan->tag_len) &&
|
||||
strlen(devname) == chan->tag_len) {
|
||||
if (!chan->inuse) {
|
||||
chan->inuse = true;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
ret = -EBUSY;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&virtio_9p_lock);
|
||||
|
||||
if (!found) {
|
||||
pr_err("no channels available\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
client->trans = (void *)chan;
|
||||
client->status = Connected;
|
||||
chan->client = client;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* p9_virtio_remove - clean up resources associated with a virtio device
|
||||
* @vdev: virtio device to remove
|
||||
*
|
||||
*/
|
||||
|
||||
static void p9_virtio_remove(struct virtio_device *vdev)
|
||||
{
|
||||
struct virtio_chan *chan = vdev->priv;
|
||||
|
||||
if (chan->inuse)
|
||||
p9_virtio_close(chan->client);
|
||||
vdev->config->del_vqs(vdev);
|
||||
|
||||
mutex_lock(&virtio_9p_lock);
|
||||
list_del(&chan->chan_list);
|
||||
mutex_unlock(&virtio_9p_lock);
|
||||
sysfs_remove_file(&(vdev->dev.kobj), &dev_attr_mount_tag.attr);
|
||||
kobject_uevent(&(vdev->dev.kobj), KOBJ_CHANGE);
|
||||
kfree(chan->tag);
|
||||
kfree(chan->vc_wq);
|
||||
kfree(chan);
|
||||
|
||||
}
|
||||
|
||||
static struct virtio_device_id id_table[] = {
|
||||
{ VIRTIO_ID_9P, VIRTIO_DEV_ANY_ID },
|
||||
{ 0 },
|
||||
};
|
||||
|
||||
static unsigned int features[] = {
|
||||
VIRTIO_9P_MOUNT_TAG,
|
||||
};
|
||||
|
||||
/* The standard "struct lguest_driver": */
|
||||
static struct virtio_driver p9_virtio_drv = {
|
||||
.feature_table = features,
|
||||
.feature_table_size = ARRAY_SIZE(features),
|
||||
.driver.name = KBUILD_MODNAME,
|
||||
.driver.owner = THIS_MODULE,
|
||||
.id_table = id_table,
|
||||
.probe = p9_virtio_probe,
|
||||
.remove = p9_virtio_remove,
|
||||
};
|
||||
|
||||
static struct p9_trans_module p9_virtio_trans = {
|
||||
.name = "virtio",
|
||||
.create = p9_virtio_create,
|
||||
.close = p9_virtio_close,
|
||||
.request = p9_virtio_request,
|
||||
.zc_request = p9_virtio_zc_request,
|
||||
.cancel = p9_virtio_cancel,
|
||||
/*
|
||||
* We leave one entry for input and one entry for response
|
||||
* headers. We also skip one more entry to accomodate, address
|
||||
* that are not at page boundary, that can result in an extra
|
||||
* page in zero copy.
|
||||
*/
|
||||
.maxsize = PAGE_SIZE * (VIRTQUEUE_NUM - 3),
|
||||
.def = 0,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
/* The standard init function */
|
||||
static int __init p9_virtio_init(void)
|
||||
{
|
||||
INIT_LIST_HEAD(&virtio_chan_list);
|
||||
|
||||
v9fs_register_trans(&p9_virtio_trans);
|
||||
return register_virtio_driver(&p9_virtio_drv);
|
||||
}
|
||||
|
||||
static void __exit p9_virtio_cleanup(void)
|
||||
{
|
||||
unregister_virtio_driver(&p9_virtio_drv);
|
||||
v9fs_unregister_trans(&p9_virtio_trans);
|
||||
}
|
||||
|
||||
module_init(p9_virtio_init);
|
||||
module_exit(p9_virtio_cleanup);
|
||||
|
||||
MODULE_DEVICE_TABLE(virtio, id_table);
|
||||
MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
|
||||
MODULE_DESCRIPTION("Virtio 9p Transport");
|
||||
MODULE_LICENSE("GPL");
|
||||
141
addons/9p/src/3.10.108/util.c
Normal file
141
addons/9p/src/3.10.108/util.c
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* net/9p/util.c
|
||||
*
|
||||
* This file contains some helper functions
|
||||
*
|
||||
* Copyright (C) 2007 by Latchesar Ionkov <lucho@ionkov.net>
|
||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/parser.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/9p/9p.h>
|
||||
|
||||
/**
|
||||
* struct p9_idpool - per-connection accounting for tag idpool
|
||||
* @lock: protects the pool
|
||||
* @pool: idr to allocate tag id from
|
||||
*
|
||||
*/
|
||||
|
||||
struct p9_idpool {
|
||||
spinlock_t lock;
|
||||
struct idr pool;
|
||||
};
|
||||
|
||||
/**
|
||||
* p9_idpool_create - create a new per-connection id pool
|
||||
*
|
||||
*/
|
||||
|
||||
struct p9_idpool *p9_idpool_create(void)
|
||||
{
|
||||
struct p9_idpool *p;
|
||||
|
||||
p = kmalloc(sizeof(struct p9_idpool), GFP_KERNEL);
|
||||
if (!p)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
spin_lock_init(&p->lock);
|
||||
idr_init(&p->pool);
|
||||
|
||||
return p;
|
||||
}
|
||||
EXPORT_SYMBOL(p9_idpool_create);
|
||||
|
||||
/**
|
||||
* p9_idpool_destroy - create a new per-connection id pool
|
||||
* @p: idpool to destroy
|
||||
*/
|
||||
|
||||
void p9_idpool_destroy(struct p9_idpool *p)
|
||||
{
|
||||
idr_destroy(&p->pool);
|
||||
kfree(p);
|
||||
}
|
||||
EXPORT_SYMBOL(p9_idpool_destroy);
|
||||
|
||||
/**
|
||||
* p9_idpool_get - allocate numeric id from pool
|
||||
* @p: pool to allocate from
|
||||
*
|
||||
* Bugs: This seems to be an awful generic function, should it be in idr.c with
|
||||
* the lock included in struct idr?
|
||||
*/
|
||||
|
||||
int p9_idpool_get(struct p9_idpool *p)
|
||||
{
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
idr_preload(GFP_NOFS);
|
||||
spin_lock_irqsave(&p->lock, flags);
|
||||
|
||||
/* no need to store exactly p, we just need something non-null */
|
||||
i = idr_alloc(&p->pool, p, 0, 0, GFP_NOWAIT);
|
||||
|
||||
spin_unlock_irqrestore(&p->lock, flags);
|
||||
idr_preload_end();
|
||||
if (i < 0)
|
||||
return -1;
|
||||
|
||||
p9_debug(P9_DEBUG_MUX, " id %d pool %p\n", i, p);
|
||||
return i;
|
||||
}
|
||||
EXPORT_SYMBOL(p9_idpool_get);
|
||||
|
||||
/**
|
||||
* p9_idpool_put - release numeric id from pool
|
||||
* @id: numeric id which is being released
|
||||
* @p: pool to release id into
|
||||
*
|
||||
* Bugs: This seems to be an awful generic function, should it be in idr.c with
|
||||
* the lock included in struct idr?
|
||||
*/
|
||||
|
||||
void p9_idpool_put(int id, struct p9_idpool *p)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
p9_debug(P9_DEBUG_MUX, " id %d pool %p\n", id, p);
|
||||
|
||||
spin_lock_irqsave(&p->lock, flags);
|
||||
idr_remove(&p->pool, id);
|
||||
spin_unlock_irqrestore(&p->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(p9_idpool_put);
|
||||
|
||||
/**
|
||||
* p9_idpool_check - check if the specified id is available
|
||||
* @id: id to check
|
||||
* @p: pool to check
|
||||
*/
|
||||
|
||||
int p9_idpool_check(int id, struct p9_idpool *p)
|
||||
{
|
||||
return idr_find(&p->pool, id) != NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(p9_idpool_check);
|
||||
|
||||
677
addons/9p/src/3.10.108/v9fs.c
Normal file
677
addons/9p/src/3.10.108/v9fs.c
Normal file
@@ -0,0 +1,677 @@
|
||||
/*
|
||||
* linux/fs/9p/v9fs.c
|
||||
*
|
||||
* This file contains functions assisting in mapping VFS to 9P2000
|
||||
*
|
||||
* Copyright (C) 2004-2008 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/parser.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
#include <net/9p/transport.h>
|
||||
#include "v9fs.h"
|
||||
#include "v9fs_vfs.h"
|
||||
#include "cache.h"
|
||||
|
||||
static DEFINE_SPINLOCK(v9fs_sessionlist_lock);
|
||||
static LIST_HEAD(v9fs_sessionlist);
|
||||
struct kmem_cache *v9fs_inode_cache;
|
||||
|
||||
/*
|
||||
* Option Parsing (code inspired by NFS code)
|
||||
* NOTE: each transport will parse its own options
|
||||
*/
|
||||
|
||||
enum {
|
||||
/* Options that take integer arguments */
|
||||
Opt_debug, Opt_dfltuid, Opt_dfltgid, Opt_afid,
|
||||
/* String options */
|
||||
Opt_uname, Opt_remotename, Opt_trans, Opt_cache, Opt_cachetag,
|
||||
/* Options that take no arguments */
|
||||
Opt_nodevmap,
|
||||
/* Cache options */
|
||||
Opt_cache_loose, Opt_fscache,
|
||||
/* Access options */
|
||||
Opt_access, Opt_posixacl,
|
||||
/* Error token */
|
||||
Opt_err
|
||||
};
|
||||
|
||||
static const match_table_t tokens = {
|
||||
{Opt_debug, "debug=%x"},
|
||||
{Opt_dfltuid, "dfltuid=%u"},
|
||||
{Opt_dfltgid, "dfltgid=%u"},
|
||||
{Opt_afid, "afid=%u"},
|
||||
{Opt_uname, "uname=%s"},
|
||||
{Opt_remotename, "aname=%s"},
|
||||
{Opt_nodevmap, "nodevmap"},
|
||||
{Opt_cache, "cache=%s"},
|
||||
{Opt_cache_loose, "loose"},
|
||||
{Opt_fscache, "fscache"},
|
||||
{Opt_cachetag, "cachetag=%s"},
|
||||
{Opt_access, "access=%s"},
|
||||
{Opt_posixacl, "posixacl"},
|
||||
{Opt_err, NULL}
|
||||
};
|
||||
|
||||
/* Interpret mount options for cache mode */
|
||||
static int get_cache_mode(char *s)
|
||||
{
|
||||
int version = -EINVAL;
|
||||
|
||||
if (!strcmp(s, "loose")) {
|
||||
version = CACHE_LOOSE;
|
||||
p9_debug(P9_DEBUG_9P, "Cache mode: loose\n");
|
||||
} else if (!strcmp(s, "fscache")) {
|
||||
version = CACHE_FSCACHE;
|
||||
p9_debug(P9_DEBUG_9P, "Cache mode: fscache\n");
|
||||
} else if (!strcmp(s, "none")) {
|
||||
version = CACHE_NONE;
|
||||
p9_debug(P9_DEBUG_9P, "Cache mode: none\n");
|
||||
} else
|
||||
pr_info("Unknown Cache mode %s\n", s);
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_parse_options - parse mount options into session structure
|
||||
* @v9ses: existing v9fs session information
|
||||
*
|
||||
* Return 0 upon success, -ERRNO upon failure.
|
||||
*/
|
||||
|
||||
static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
|
||||
{
|
||||
char *options, *tmp_options;
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
char *p;
|
||||
int option = 0;
|
||||
char *s, *e;
|
||||
int ret = 0;
|
||||
|
||||
/* setup defaults */
|
||||
v9ses->afid = ~0;
|
||||
v9ses->debug = 0;
|
||||
v9ses->cache = CACHE_NONE;
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
v9ses->cachetag = NULL;
|
||||
#endif
|
||||
|
||||
if (!opts)
|
||||
return 0;
|
||||
|
||||
tmp_options = kstrdup(opts, GFP_KERNEL);
|
||||
if (!tmp_options) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_option_alloc;
|
||||
}
|
||||
options = tmp_options;
|
||||
|
||||
while ((p = strsep(&options, ",")) != NULL) {
|
||||
int token, r;
|
||||
if (!*p)
|
||||
continue;
|
||||
token = match_token(p, tokens, args);
|
||||
switch (token) {
|
||||
case Opt_debug:
|
||||
r = match_int(&args[0], &option);
|
||||
if (r < 0) {
|
||||
p9_debug(P9_DEBUG_ERROR,
|
||||
"integer field, but no integer?\n");
|
||||
ret = r;
|
||||
continue;
|
||||
}
|
||||
v9ses->debug = option;
|
||||
#ifdef CONFIG_NET_9P_DEBUG
|
||||
p9_debug_level = option;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case Opt_dfltuid:
|
||||
r = match_int(&args[0], &option);
|
||||
if (r < 0) {
|
||||
p9_debug(P9_DEBUG_ERROR,
|
||||
"integer field, but no integer?\n");
|
||||
ret = r;
|
||||
continue;
|
||||
}
|
||||
v9ses->dfltuid = make_kuid(current_user_ns(), option);
|
||||
if (!uid_valid(v9ses->dfltuid)) {
|
||||
p9_debug(P9_DEBUG_ERROR,
|
||||
"uid field, but not a uid?\n");
|
||||
ret = -EINVAL;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case Opt_dfltgid:
|
||||
r = match_int(&args[0], &option);
|
||||
if (r < 0) {
|
||||
p9_debug(P9_DEBUG_ERROR,
|
||||
"integer field, but no integer?\n");
|
||||
ret = r;
|
||||
continue;
|
||||
}
|
||||
v9ses->dfltgid = make_kgid(current_user_ns(), option);
|
||||
if (!gid_valid(v9ses->dfltgid)) {
|
||||
p9_debug(P9_DEBUG_ERROR,
|
||||
"gid field, but not a gid?\n");
|
||||
ret = -EINVAL;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case Opt_afid:
|
||||
r = match_int(&args[0], &option);
|
||||
if (r < 0) {
|
||||
p9_debug(P9_DEBUG_ERROR,
|
||||
"integer field, but no integer?\n");
|
||||
ret = r;
|
||||
continue;
|
||||
}
|
||||
v9ses->afid = option;
|
||||
break;
|
||||
case Opt_uname:
|
||||
kfree(v9ses->uname);
|
||||
v9ses->uname = match_strdup(&args[0]);
|
||||
if (!v9ses->uname) {
|
||||
ret = -ENOMEM;
|
||||
goto free_and_return;
|
||||
}
|
||||
break;
|
||||
case Opt_remotename:
|
||||
kfree(v9ses->aname);
|
||||
v9ses->aname = match_strdup(&args[0]);
|
||||
if (!v9ses->aname) {
|
||||
ret = -ENOMEM;
|
||||
goto free_and_return;
|
||||
}
|
||||
break;
|
||||
case Opt_nodevmap:
|
||||
v9ses->nodev = 1;
|
||||
break;
|
||||
case Opt_cache_loose:
|
||||
v9ses->cache = CACHE_LOOSE;
|
||||
break;
|
||||
case Opt_fscache:
|
||||
v9ses->cache = CACHE_FSCACHE;
|
||||
break;
|
||||
case Opt_cachetag:
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
v9ses->cachetag = match_strdup(&args[0]);
|
||||
#endif
|
||||
break;
|
||||
case Opt_cache:
|
||||
s = match_strdup(&args[0]);
|
||||
if (!s) {
|
||||
ret = -ENOMEM;
|
||||
p9_debug(P9_DEBUG_ERROR,
|
||||
"problem allocating copy of cache arg\n");
|
||||
goto free_and_return;
|
||||
}
|
||||
ret = get_cache_mode(s);
|
||||
if (ret == -EINVAL) {
|
||||
kfree(s);
|
||||
goto free_and_return;
|
||||
}
|
||||
|
||||
v9ses->cache = ret;
|
||||
kfree(s);
|
||||
break;
|
||||
|
||||
case Opt_access:
|
||||
s = match_strdup(&args[0]);
|
||||
if (!s) {
|
||||
ret = -ENOMEM;
|
||||
p9_debug(P9_DEBUG_ERROR,
|
||||
"problem allocating copy of access arg\n");
|
||||
goto free_and_return;
|
||||
}
|
||||
|
||||
v9ses->flags &= ~V9FS_ACCESS_MASK;
|
||||
if (strcmp(s, "user") == 0)
|
||||
v9ses->flags |= V9FS_ACCESS_USER;
|
||||
else if (strcmp(s, "any") == 0)
|
||||
v9ses->flags |= V9FS_ACCESS_ANY;
|
||||
else if (strcmp(s, "client") == 0) {
|
||||
v9ses->flags |= V9FS_ACCESS_CLIENT;
|
||||
} else {
|
||||
uid_t uid;
|
||||
v9ses->flags |= V9FS_ACCESS_SINGLE;
|
||||
uid = simple_strtoul(s, &e, 10);
|
||||
if (*e != '\0') {
|
||||
ret = -EINVAL;
|
||||
pr_info("Unknown access argument %s\n",
|
||||
s);
|
||||
kfree(s);
|
||||
goto free_and_return;
|
||||
}
|
||||
v9ses->uid = make_kuid(current_user_ns(), uid);
|
||||
if (!uid_valid(v9ses->uid)) {
|
||||
ret = -EINVAL;
|
||||
pr_info("Uknown uid %s\n", s);
|
||||
kfree(s);
|
||||
goto free_and_return;
|
||||
}
|
||||
}
|
||||
|
||||
kfree(s);
|
||||
break;
|
||||
|
||||
case Opt_posixacl:
|
||||
#ifdef CONFIG_9P_FS_POSIX_ACL
|
||||
v9ses->flags |= V9FS_POSIX_ACL;
|
||||
#else
|
||||
p9_debug(P9_DEBUG_ERROR,
|
||||
"Not defined CONFIG_9P_FS_POSIX_ACL. Ignoring posixacl option\n");
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
free_and_return:
|
||||
kfree(tmp_options);
|
||||
fail_option_alloc:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_session_init - initialize session
|
||||
* @v9ses: session information structure
|
||||
* @dev_name: device being mounted
|
||||
* @data: options
|
||||
*
|
||||
*/
|
||||
|
||||
struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
|
||||
const char *dev_name, char *data)
|
||||
{
|
||||
int retval = -EINVAL;
|
||||
struct p9_fid *fid;
|
||||
int rc;
|
||||
|
||||
v9ses->uname = kstrdup(V9FS_DEFUSER, GFP_KERNEL);
|
||||
if (!v9ses->uname)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
v9ses->aname = kstrdup(V9FS_DEFANAME, GFP_KERNEL);
|
||||
if (!v9ses->aname) {
|
||||
kfree(v9ses->uname);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
init_rwsem(&v9ses->rename_sem);
|
||||
|
||||
rc = bdi_setup_and_register(&v9ses->bdi, "9p", BDI_CAP_MAP_COPY);
|
||||
if (rc) {
|
||||
kfree(v9ses->aname);
|
||||
kfree(v9ses->uname);
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
|
||||
spin_lock(&v9fs_sessionlist_lock);
|
||||
list_add(&v9ses->slist, &v9fs_sessionlist);
|
||||
spin_unlock(&v9fs_sessionlist_lock);
|
||||
|
||||
v9ses->uid = INVALID_UID;
|
||||
v9ses->dfltuid = V9FS_DEFUID;
|
||||
v9ses->dfltgid = V9FS_DEFGID;
|
||||
|
||||
v9ses->clnt = p9_client_create(dev_name, data);
|
||||
if (IS_ERR(v9ses->clnt)) {
|
||||
retval = PTR_ERR(v9ses->clnt);
|
||||
v9ses->clnt = NULL;
|
||||
p9_debug(P9_DEBUG_ERROR, "problem initializing 9p client\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
v9ses->flags = V9FS_ACCESS_USER;
|
||||
|
||||
if (p9_is_proto_dotl(v9ses->clnt)) {
|
||||
v9ses->flags = V9FS_ACCESS_CLIENT;
|
||||
v9ses->flags |= V9FS_PROTO_2000L;
|
||||
} else if (p9_is_proto_dotu(v9ses->clnt)) {
|
||||
v9ses->flags |= V9FS_PROTO_2000U;
|
||||
}
|
||||
|
||||
rc = v9fs_parse_options(v9ses, data);
|
||||
if (rc < 0) {
|
||||
retval = rc;
|
||||
goto error;
|
||||
}
|
||||
|
||||
v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ;
|
||||
|
||||
if (!v9fs_proto_dotl(v9ses) &&
|
||||
((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) {
|
||||
/*
|
||||
* We support ACCESS_CLIENT only for dotl.
|
||||
* Fall back to ACCESS_USER
|
||||
*/
|
||||
v9ses->flags &= ~V9FS_ACCESS_MASK;
|
||||
v9ses->flags |= V9FS_ACCESS_USER;
|
||||
}
|
||||
/*FIXME !! */
|
||||
/* for legacy mode, fall back to V9FS_ACCESS_ANY */
|
||||
if (!(v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses)) &&
|
||||
((v9ses->flags&V9FS_ACCESS_MASK) == V9FS_ACCESS_USER)) {
|
||||
|
||||
v9ses->flags &= ~V9FS_ACCESS_MASK;
|
||||
v9ses->flags |= V9FS_ACCESS_ANY;
|
||||
v9ses->uid = INVALID_UID;
|
||||
}
|
||||
if (!v9fs_proto_dotl(v9ses) ||
|
||||
!((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) {
|
||||
/*
|
||||
* We support ACL checks on clinet only if the protocol is
|
||||
* 9P2000.L and access is V9FS_ACCESS_CLIENT.
|
||||
*/
|
||||
v9ses->flags &= ~V9FS_ACL_MASK;
|
||||
}
|
||||
|
||||
fid = p9_client_attach(v9ses->clnt, NULL, v9ses->uname, INVALID_UID,
|
||||
v9ses->aname);
|
||||
if (IS_ERR(fid)) {
|
||||
retval = PTR_ERR(fid);
|
||||
fid = NULL;
|
||||
p9_debug(P9_DEBUG_ERROR, "cannot attach\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_SINGLE)
|
||||
fid->uid = v9ses->uid;
|
||||
else
|
||||
fid->uid = INVALID_UID;
|
||||
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
/* register the session for caching */
|
||||
v9fs_cache_session_get_cookie(v9ses);
|
||||
#endif
|
||||
|
||||
return fid;
|
||||
|
||||
error:
|
||||
bdi_destroy(&v9ses->bdi);
|
||||
return ERR_PTR(retval);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_session_close - shutdown a session
|
||||
* @v9ses: session information structure
|
||||
*
|
||||
*/
|
||||
|
||||
void v9fs_session_close(struct v9fs_session_info *v9ses)
|
||||
{
|
||||
if (v9ses->clnt) {
|
||||
p9_client_destroy(v9ses->clnt);
|
||||
v9ses->clnt = NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
if (v9ses->fscache) {
|
||||
v9fs_cache_session_put_cookie(v9ses);
|
||||
kfree(v9ses->cachetag);
|
||||
}
|
||||
#endif
|
||||
kfree(v9ses->uname);
|
||||
kfree(v9ses->aname);
|
||||
|
||||
bdi_destroy(&v9ses->bdi);
|
||||
|
||||
spin_lock(&v9fs_sessionlist_lock);
|
||||
list_del(&v9ses->slist);
|
||||
spin_unlock(&v9fs_sessionlist_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_session_cancel - terminate a session
|
||||
* @v9ses: session to terminate
|
||||
*
|
||||
* mark transport as disconnected and cancel all pending requests.
|
||||
*/
|
||||
|
||||
void v9fs_session_cancel(struct v9fs_session_info *v9ses) {
|
||||
p9_debug(P9_DEBUG_ERROR, "cancel session %p\n", v9ses);
|
||||
p9_client_disconnect(v9ses->clnt);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_session_begin_cancel - Begin terminate of a session
|
||||
* @v9ses: session to terminate
|
||||
*
|
||||
* After this call we don't allow any request other than clunk.
|
||||
*/
|
||||
|
||||
void v9fs_session_begin_cancel(struct v9fs_session_info *v9ses)
|
||||
{
|
||||
p9_debug(P9_DEBUG_ERROR, "begin cancel session %p\n", v9ses);
|
||||
p9_client_begin_disconnect(v9ses->clnt);
|
||||
}
|
||||
|
||||
extern int v9fs_error_init(void);
|
||||
|
||||
static struct kobject *v9fs_kobj;
|
||||
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
/**
|
||||
* caches_show - list caches associated with a session
|
||||
*
|
||||
* Returns the size of buffer written.
|
||||
*/
|
||||
|
||||
static ssize_t caches_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
ssize_t n = 0, count = 0, limit = PAGE_SIZE;
|
||||
struct v9fs_session_info *v9ses;
|
||||
|
||||
spin_lock(&v9fs_sessionlist_lock);
|
||||
list_for_each_entry(v9ses, &v9fs_sessionlist, slist) {
|
||||
if (v9ses->cachetag) {
|
||||
n = snprintf(buf, limit, "%s\n", v9ses->cachetag);
|
||||
if (n < 0) {
|
||||
count = n;
|
||||
break;
|
||||
}
|
||||
|
||||
count += n;
|
||||
limit -= n;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock(&v9fs_sessionlist_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct kobj_attribute v9fs_attr_cache = __ATTR_RO(caches);
|
||||
#endif /* CONFIG_9P_FSCACHE */
|
||||
|
||||
static struct attribute *v9fs_attrs[] = {
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
&v9fs_attr_cache.attr,
|
||||
#endif
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group v9fs_attr_group = {
|
||||
.attrs = v9fs_attrs,
|
||||
};
|
||||
|
||||
/**
|
||||
* v9fs_sysfs_init - Initialize the v9fs sysfs interface
|
||||
*
|
||||
*/
|
||||
|
||||
static int v9fs_sysfs_init(void)
|
||||
{
|
||||
v9fs_kobj = kobject_create_and_add("9p", fs_kobj);
|
||||
if (!v9fs_kobj)
|
||||
return -ENOMEM;
|
||||
|
||||
if (sysfs_create_group(v9fs_kobj, &v9fs_attr_group)) {
|
||||
kobject_put(v9fs_kobj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_sysfs_cleanup - Unregister the v9fs sysfs interface
|
||||
*
|
||||
*/
|
||||
|
||||
static void v9fs_sysfs_cleanup(void)
|
||||
{
|
||||
sysfs_remove_group(v9fs_kobj, &v9fs_attr_group);
|
||||
kobject_put(v9fs_kobj);
|
||||
}
|
||||
|
||||
static void v9fs_inode_init_once(void *foo)
|
||||
{
|
||||
struct v9fs_inode *v9inode = (struct v9fs_inode *)foo;
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
v9inode->fscache = NULL;
|
||||
#endif
|
||||
memset(&v9inode->qid, 0, sizeof(v9inode->qid));
|
||||
inode_init_once(&v9inode->vfs_inode);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_init_inode_cache - initialize a cache for 9P
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
static int v9fs_init_inode_cache(void)
|
||||
{
|
||||
v9fs_inode_cache = kmem_cache_create("v9fs_inode_cache",
|
||||
sizeof(struct v9fs_inode),
|
||||
0, (SLAB_RECLAIM_ACCOUNT|
|
||||
SLAB_MEM_SPREAD),
|
||||
v9fs_inode_init_once);
|
||||
if (!v9fs_inode_cache)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_destroy_inode_cache - destroy the cache of 9P inode
|
||||
*
|
||||
*/
|
||||
static void v9fs_destroy_inode_cache(void)
|
||||
{
|
||||
/*
|
||||
* Make sure all delayed rcu free inodes are flushed before we
|
||||
* destroy cache.
|
||||
*/
|
||||
rcu_barrier();
|
||||
kmem_cache_destroy(v9fs_inode_cache);
|
||||
}
|
||||
|
||||
static int v9fs_cache_register(void)
|
||||
{
|
||||
int ret;
|
||||
ret = v9fs_init_inode_cache();
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
return fscache_register_netfs(&v9fs_cache_netfs);
|
||||
#else
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void v9fs_cache_unregister(void)
|
||||
{
|
||||
v9fs_destroy_inode_cache();
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
fscache_unregister_netfs(&v9fs_cache_netfs);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* init_v9fs - Initialize module
|
||||
*
|
||||
*/
|
||||
|
||||
static int __init init_v9fs(void)
|
||||
{
|
||||
int err;
|
||||
pr_info("Installing v9fs 9p2000 file system support\n");
|
||||
/* TODO: Setup list of registered trasnport modules */
|
||||
|
||||
err = v9fs_cache_register();
|
||||
if (err < 0) {
|
||||
pr_err("Failed to register v9fs for caching\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = v9fs_sysfs_init();
|
||||
if (err < 0) {
|
||||
pr_err("Failed to register with sysfs\n");
|
||||
goto out_cache;
|
||||
}
|
||||
err = register_filesystem(&v9fs_fs_type);
|
||||
if (err < 0) {
|
||||
pr_err("Failed to register filesystem\n");
|
||||
goto out_sysfs_cleanup;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_sysfs_cleanup:
|
||||
v9fs_sysfs_cleanup();
|
||||
|
||||
out_cache:
|
||||
v9fs_cache_unregister();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* exit_v9fs - shutdown module
|
||||
*
|
||||
*/
|
||||
|
||||
static void __exit exit_v9fs(void)
|
||||
{
|
||||
v9fs_sysfs_cleanup();
|
||||
v9fs_cache_unregister();
|
||||
unregister_filesystem(&v9fs_fs_type);
|
||||
}
|
||||
|
||||
module_init(init_v9fs)
|
||||
module_exit(exit_v9fs)
|
||||
|
||||
MODULE_AUTHOR("Latchesar Ionkov <lucho@ionkov.net>");
|
||||
MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
|
||||
MODULE_AUTHOR("Ron Minnich <rminnich@lanl.gov>");
|
||||
MODULE_LICENSE("GPL");
|
||||
227
addons/9p/src/3.10.108/v9fs.h
Normal file
227
addons/9p/src/3.10.108/v9fs.h
Normal file
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* V9FS definitions.
|
||||
*
|
||||
* Copyright (C) 2004-2008 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
#ifndef FS_9P_V9FS_H
|
||||
#define FS_9P_V9FS_H
|
||||
|
||||
#include <linux/backing-dev.h>
|
||||
|
||||
/**
|
||||
* enum p9_session_flags - option flags for each 9P session
|
||||
* @V9FS_PROTO_2000U: whether or not to use 9P2000.u extensions
|
||||
* @V9FS_PROTO_2000L: whether or not to use 9P2000.l extensions
|
||||
* @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
|
||||
*
|
||||
* 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,
|
||||
V9FS_ACCESS_SINGLE = 0x04,
|
||||
V9FS_ACCESS_USER = 0x08,
|
||||
V9FS_ACCESS_CLIENT = 0x10,
|
||||
V9FS_POSIX_ACL = 0x20
|
||||
};
|
||||
|
||||
/* possible values of ->cache */
|
||||
/**
|
||||
* enum p9_cache_modes - user specified cache preferences
|
||||
* @CACHE_NONE: do not cache data, dentries, or directory contents (default)
|
||||
* @CACHE_LOOSE: cache data, dentries, and directory contents w/no consistency
|
||||
*
|
||||
* eventually support loose, tight, time, session, default always none
|
||||
*/
|
||||
|
||||
enum p9_cache_modes {
|
||||
CACHE_NONE,
|
||||
CACHE_LOOSE,
|
||||
CACHE_FSCACHE,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct v9fs_session_info - per-instance session information
|
||||
* @flags: session options of type &p9_session_flags
|
||||
* @nodev: set to 1 to disable device mapping
|
||||
* @debug: debug level
|
||||
* @afid: authentication handle
|
||||
* @cache: cache mode of type &p9_cache_modes
|
||||
* @cachetag: the tag of the cache associated with this session
|
||||
* @fscache: session cookie associated with FS-Cache
|
||||
* @options: copy of options string given by user
|
||||
* @uname: string user name to mount hierarchy as
|
||||
* @aname: mount specifier for remote hierarchy
|
||||
* @maxdata: maximum data to be sent/recvd per protocol message
|
||||
* @dfltuid: default numeric userid to mount hierarchy as
|
||||
* @dfltgid: default numeric groupid to mount hierarchy as
|
||||
* @uid: if %V9FS_ACCESS_SINGLE, the numeric uid which mounted the hierarchy
|
||||
* @clnt: reference to 9P network client instantiated for this session
|
||||
* @slist: reference to list of registered 9p sessions
|
||||
*
|
||||
* This structure holds state for each session instance established during
|
||||
* a sys_mount() .
|
||||
*
|
||||
* Bugs: there seems to be a lot of state which could be condensed and/or
|
||||
* removed.
|
||||
*/
|
||||
|
||||
struct v9fs_session_info {
|
||||
/* options */
|
||||
unsigned char flags;
|
||||
unsigned char nodev;
|
||||
unsigned short debug;
|
||||
unsigned int afid;
|
||||
unsigned int cache;
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
char *cachetag;
|
||||
struct fscache_cookie *fscache;
|
||||
#endif
|
||||
|
||||
char *uname; /* user name to mount as */
|
||||
char *aname; /* name of remote hierarchy being mounted */
|
||||
unsigned int maxdata; /* max data for client interface */
|
||||
kuid_t dfltuid; /* default uid/muid for legacy support */
|
||||
kgid_t dfltgid; /* default gid for legacy support */
|
||||
kuid_t uid; /* if ACCESS_SINGLE, the uid that has access */
|
||||
struct p9_client *clnt; /* 9p client */
|
||||
struct list_head slist; /* list of sessions registered with v9fs */
|
||||
struct backing_dev_info bdi;
|
||||
struct rw_semaphore rename_sem;
|
||||
};
|
||||
|
||||
/* cache_validity flags */
|
||||
#define V9FS_INO_INVALID_ATTR 0x01
|
||||
|
||||
struct v9fs_inode {
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
spinlock_t fscache_lock;
|
||||
struct fscache_cookie *fscache;
|
||||
#endif
|
||||
struct p9_qid qid;
|
||||
unsigned int cache_validity;
|
||||
struct p9_fid *writeback_fid;
|
||||
struct mutex v_mutex;
|
||||
struct inode vfs_inode;
|
||||
};
|
||||
|
||||
static inline struct v9fs_inode *V9FS_I(const struct inode *inode)
|
||||
{
|
||||
return container_of(inode, struct v9fs_inode, vfs_inode);
|
||||
}
|
||||
|
||||
struct p9_fid *v9fs_session_init(struct v9fs_session_info *, const char *,
|
||||
char *);
|
||||
extern void v9fs_session_close(struct v9fs_session_info *v9ses);
|
||||
extern void v9fs_session_cancel(struct v9fs_session_info *v9ses);
|
||||
extern void v9fs_session_begin_cancel(struct v9fs_session_info *v9ses);
|
||||
extern struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
unsigned int flags);
|
||||
extern int v9fs_vfs_unlink(struct inode *i, struct dentry *d);
|
||||
extern int v9fs_vfs_rmdir(struct inode *i, struct dentry *d);
|
||||
extern int v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct inode *new_dir, struct dentry *new_dentry);
|
||||
extern void v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd,
|
||||
void *p);
|
||||
extern struct inode *v9fs_inode_from_fid(struct v9fs_session_info *v9ses,
|
||||
struct p9_fid *fid,
|
||||
struct super_block *sb, int new);
|
||||
extern const struct inode_operations v9fs_dir_inode_operations_dotl;
|
||||
extern const struct inode_operations v9fs_file_inode_operations_dotl;
|
||||
extern const struct inode_operations v9fs_symlink_inode_operations_dotl;
|
||||
extern struct inode *v9fs_inode_from_fid_dotl(struct v9fs_session_info *v9ses,
|
||||
struct p9_fid *fid,
|
||||
struct super_block *sb, int new);
|
||||
|
||||
/* other default globals */
|
||||
#define V9FS_PORT 564
|
||||
#define V9FS_DEFUSER "nobody"
|
||||
#define V9FS_DEFANAME ""
|
||||
#define V9FS_DEFUID KUIDT_INIT(-2)
|
||||
#define V9FS_DEFGID KGIDT_INIT(-2)
|
||||
|
||||
static inline struct v9fs_session_info *v9fs_inode2v9ses(struct inode *inode)
|
||||
{
|
||||
return (inode->i_sb->s_fs_info);
|
||||
}
|
||||
|
||||
static inline struct v9fs_session_info *v9fs_dentry2v9ses(struct dentry *dentry)
|
||||
{
|
||||
return dentry->d_sb->s_fs_info;
|
||||
}
|
||||
|
||||
static inline int v9fs_proto_dotu(struct v9fs_session_info *v9ses)
|
||||
{
|
||||
return v9ses->flags & V9FS_PROTO_2000U;
|
||||
}
|
||||
|
||||
static inline int v9fs_proto_dotl(struct v9fs_session_info *v9ses)
|
||||
{
|
||||
return v9ses->flags & V9FS_PROTO_2000L;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_get_inode_from_fid - Helper routine to populate an inode by
|
||||
* issuing a attribute request
|
||||
* @v9ses: session information
|
||||
* @fid: fid to issue attribute request for
|
||||
* @sb: superblock on which to create inode
|
||||
*
|
||||
*/
|
||||
static inline struct inode *
|
||||
v9fs_get_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
|
||||
struct super_block *sb)
|
||||
{
|
||||
if (v9fs_proto_dotl(v9ses))
|
||||
return v9fs_inode_from_fid_dotl(v9ses, fid, sb, 0);
|
||||
else
|
||||
return v9fs_inode_from_fid(v9ses, fid, sb, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_get_new_inode_from_fid - Helper routine to populate an inode by
|
||||
* issuing a attribute request
|
||||
* @v9ses: session information
|
||||
* @fid: fid to issue attribute request for
|
||||
* @sb: superblock on which to create inode
|
||||
*
|
||||
*/
|
||||
static inline struct inode *
|
||||
v9fs_get_new_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
|
||||
struct super_block *sb)
|
||||
{
|
||||
if (v9fs_proto_dotl(v9ses))
|
||||
return v9fs_inode_from_fid_dotl(v9ses, fid, sb, 1);
|
||||
else
|
||||
return v9fs_inode_from_fid(v9ses, fid, sb, 1);
|
||||
}
|
||||
|
||||
#endif
|
||||
88
addons/9p/src/3.10.108/v9fs_vfs.h
Normal file
88
addons/9p/src/3.10.108/v9fs_vfs.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* V9FS VFS extensions.
|
||||
*
|
||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
#ifndef FS_9P_V9FS_VFS_H
|
||||
#define FS_9P_V9FS_VFS_H
|
||||
|
||||
/* plan9 semantics are that created files are implicitly opened.
|
||||
* But linux semantics are that you call create, then open.
|
||||
* the plan9 approach is superior as it provides an atomic
|
||||
* open.
|
||||
* we track the create fid here. When the file is opened, if fidopen is
|
||||
* non-zero, we use the fid and can skip some steps.
|
||||
* there may be a better way to do this, but I don't know it.
|
||||
* one BAD way is to clunk the fid on create, then open it again:
|
||||
* you lose the atomicity of file open
|
||||
*/
|
||||
|
||||
/* special case:
|
||||
* unlink calls remove, which is an implicit clunk. So we have to track
|
||||
* that kind of thing so that we don't try to clunk a dead fid.
|
||||
*/
|
||||
#define P9_LOCK_TIMEOUT (30*HZ)
|
||||
|
||||
extern struct file_system_type v9fs_fs_type;
|
||||
extern const struct address_space_operations v9fs_addr_operations;
|
||||
extern const struct file_operations v9fs_file_operations;
|
||||
extern const struct file_operations v9fs_file_operations_dotl;
|
||||
extern const struct file_operations v9fs_dir_operations;
|
||||
extern const struct file_operations v9fs_dir_operations_dotl;
|
||||
extern const struct dentry_operations v9fs_dentry_operations;
|
||||
extern const struct dentry_operations v9fs_cached_dentry_operations;
|
||||
extern const struct file_operations v9fs_cached_file_operations;
|
||||
extern const struct file_operations v9fs_cached_file_operations_dotl;
|
||||
extern struct kmem_cache *v9fs_inode_cache;
|
||||
|
||||
struct inode *v9fs_alloc_inode(struct super_block *sb);
|
||||
void v9fs_destroy_inode(struct inode *inode);
|
||||
struct inode *v9fs_get_inode(struct super_block *sb, umode_t mode, dev_t);
|
||||
int v9fs_init_inode(struct v9fs_session_info *v9ses,
|
||||
struct inode *inode, umode_t mode, dev_t);
|
||||
void v9fs_evict_inode(struct inode *inode);
|
||||
ino_t v9fs_qid2ino(struct p9_qid *qid);
|
||||
void v9fs_stat2inode(struct p9_wstat *, struct inode *, struct super_block *);
|
||||
void v9fs_stat2inode_dotl(struct p9_stat_dotl *, struct inode *);
|
||||
int v9fs_dir_release(struct inode *inode, struct file *filp);
|
||||
int v9fs_file_open(struct inode *inode, struct file *file);
|
||||
void v9fs_inode2stat(struct inode *inode, struct p9_wstat *stat);
|
||||
int v9fs_uflags2omode(int uflags, int extended);
|
||||
|
||||
ssize_t v9fs_file_readn(struct file *, char *, char __user *, u32, u64);
|
||||
ssize_t v9fs_fid_readn(struct p9_fid *, char *, char __user *, u32, u64);
|
||||
void v9fs_blank_wstat(struct p9_wstat *wstat);
|
||||
int v9fs_vfs_setattr_dotl(struct dentry *, struct iattr *);
|
||||
int v9fs_file_fsync_dotl(struct file *filp, loff_t start, loff_t end,
|
||||
int datasync);
|
||||
ssize_t v9fs_file_write_internal(struct inode *, struct p9_fid *,
|
||||
const char __user *, size_t, loff_t *, int);
|
||||
int v9fs_refresh_inode(struct p9_fid *fid, struct inode *inode);
|
||||
int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode);
|
||||
static inline void v9fs_invalidate_inode_attr(struct inode *inode)
|
||||
{
|
||||
struct v9fs_inode *v9inode;
|
||||
v9inode = V9FS_I(inode);
|
||||
v9inode->cache_validity |= V9FS_INO_INVALID_ATTR;
|
||||
return;
|
||||
}
|
||||
|
||||
int v9fs_open_to_dotl_flags(int flags);
|
||||
#endif
|
||||
353
addons/9p/src/3.10.108/vfs_addr.c
Normal file
353
addons/9p/src/3.10.108/vfs_addr.c
Normal file
@@ -0,0 +1,353 @@
|
||||
/*
|
||||
* linux/fs/9p/vfs_addr.c
|
||||
*
|
||||
* This file contians 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>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/aio.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
|
||||
#include "v9fs.h"
|
||||
#include "v9fs_vfs.h"
|
||||
#include "cache.h"
|
||||
#include "fid.h"
|
||||
|
||||
/**
|
||||
* v9fs_fid_readpage - read an entire page in from 9P
|
||||
*
|
||||
* @fid: fid being read
|
||||
* @page: structure to page
|
||||
*
|
||||
*/
|
||||
static int v9fs_fid_readpage(struct p9_fid *fid, struct page *page)
|
||||
{
|
||||
int retval;
|
||||
loff_t offset;
|
||||
char *buffer;
|
||||
struct inode *inode;
|
||||
|
||||
inode = page->mapping->host;
|
||||
p9_debug(P9_DEBUG_VFS, "\n");
|
||||
|
||||
BUG_ON(!PageLocked(page));
|
||||
|
||||
retval = v9fs_readpage_from_fscache(inode, page);
|
||||
if (retval == 0)
|
||||
return retval;
|
||||
|
||||
buffer = kmap(page);
|
||||
offset = page_offset(page);
|
||||
|
||||
retval = v9fs_fid_readn(fid, buffer, NULL, PAGE_CACHE_SIZE, offset);
|
||||
if (retval < 0) {
|
||||
v9fs_uncache_page(inode, page);
|
||||
goto done;
|
||||
}
|
||||
|
||||
memset(buffer + retval, 0, PAGE_CACHE_SIZE - retval);
|
||||
flush_dcache_page(page);
|
||||
SetPageUptodate(page);
|
||||
|
||||
v9fs_readpage_to_fscache(inode, page);
|
||||
retval = 0;
|
||||
|
||||
done:
|
||||
kunmap(page);
|
||||
unlock_page(page);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_vfs_readpage - read an entire page in from 9P
|
||||
*
|
||||
* @filp: file being read
|
||||
* @page: structure to page
|
||||
*
|
||||
*/
|
||||
|
||||
static int v9fs_vfs_readpage(struct file *filp, struct page *page)
|
||||
{
|
||||
return v9fs_fid_readpage(filp->private_data, page);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_vfs_readpages - read a set of pages from 9P
|
||||
*
|
||||
* @filp: file being read
|
||||
* @mapping: the address space
|
||||
* @pages: list of pages to read
|
||||
* @nr_pages: count of pages to read
|
||||
*
|
||||
*/
|
||||
|
||||
static int v9fs_vfs_readpages(struct file *filp, struct address_space *mapping,
|
||||
struct list_head *pages, unsigned nr_pages)
|
||||
{
|
||||
int ret = 0;
|
||||
struct inode *inode;
|
||||
|
||||
inode = mapping->host;
|
||||
p9_debug(P9_DEBUG_VFS, "inode: %p file: %p\n", inode, filp);
|
||||
|
||||
ret = v9fs_readpages_from_fscache(inode, mapping, pages, &nr_pages);
|
||||
if (ret == 0)
|
||||
return ret;
|
||||
|
||||
ret = read_cache_pages(mapping, pages, (void *)v9fs_vfs_readpage, filp);
|
||||
p9_debug(P9_DEBUG_VFS, " = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_release_page - release the private state associated with a page
|
||||
*
|
||||
* Returns 1 if the page can be released, false otherwise.
|
||||
*/
|
||||
|
||||
static int v9fs_release_page(struct page *page, gfp_t gfp)
|
||||
{
|
||||
if (PagePrivate(page))
|
||||
return 0;
|
||||
return v9fs_fscache_release_page(page, gfp);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_invalidate_page - Invalidate a page completely or partially
|
||||
*
|
||||
* @page: structure to page
|
||||
* @offset: offset in the page
|
||||
*/
|
||||
|
||||
static void v9fs_invalidate_page(struct page *page, unsigned long offset)
|
||||
{
|
||||
/*
|
||||
* If called with zero offset, we should release
|
||||
* the private state assocated with the page
|
||||
*/
|
||||
if (offset == 0)
|
||||
v9fs_fscache_invalidate_page(page);
|
||||
}
|
||||
|
||||
static int v9fs_vfs_writepage_locked(struct page *page)
|
||||
{
|
||||
char *buffer;
|
||||
int retval, len;
|
||||
loff_t offset, size;
|
||||
mm_segment_t old_fs;
|
||||
struct v9fs_inode *v9inode;
|
||||
struct inode *inode = page->mapping->host;
|
||||
|
||||
v9inode = V9FS_I(inode);
|
||||
size = i_size_read(inode);
|
||||
if (page->index == size >> PAGE_CACHE_SHIFT)
|
||||
len = size & ~PAGE_CACHE_MASK;
|
||||
else
|
||||
len = PAGE_CACHE_SIZE;
|
||||
|
||||
set_page_writeback(page);
|
||||
|
||||
buffer = kmap(page);
|
||||
offset = page_offset(page);
|
||||
|
||||
old_fs = get_fs();
|
||||
set_fs(get_ds());
|
||||
/* We should have writeback_fid always set */
|
||||
BUG_ON(!v9inode->writeback_fid);
|
||||
|
||||
retval = v9fs_file_write_internal(inode,
|
||||
v9inode->writeback_fid,
|
||||
(__force const char __user *)buffer,
|
||||
len, &offset, 0);
|
||||
if (retval > 0)
|
||||
retval = 0;
|
||||
|
||||
set_fs(old_fs);
|
||||
kunmap(page);
|
||||
end_page_writeback(page);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int v9fs_vfs_writepage(struct page *page, struct writeback_control *wbc)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = v9fs_vfs_writepage_locked(page);
|
||||
if (retval < 0) {
|
||||
if (retval == -EAGAIN) {
|
||||
redirty_page_for_writepage(wbc, page);
|
||||
retval = 0;
|
||||
} else {
|
||||
SetPageError(page);
|
||||
mapping_set_error(page->mapping, retval);
|
||||
}
|
||||
} else
|
||||
retval = 0;
|
||||
|
||||
unlock_page(page);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_launder_page - Writeback a dirty page
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
|
||||
static int v9fs_launder_page(struct page *page)
|
||||
{
|
||||
int retval;
|
||||
struct inode *inode = page->mapping->host;
|
||||
|
||||
v9fs_fscache_wait_on_page_write(inode, page);
|
||||
if (clear_page_dirty_for_io(page)) {
|
||||
retval = v9fs_vfs_writepage_locked(page);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_direct_IO - 9P address space operation for direct I/O
|
||||
* @rw: direction (read or write)
|
||||
* @iocb: target I/O control block
|
||||
* @iov: array of vectors that define I/O buffer
|
||||
* @pos: offset in file to begin the operation
|
||||
* @nr_segs: size of iovec array
|
||||
*
|
||||
* The presence of v9fs_direct_IO() in the address space ops vector
|
||||
* allowes open() O_DIRECT flags which would have failed otherwise.
|
||||
*
|
||||
* In the non-cached mode, we shunt off direct read and write requests before
|
||||
* the VFS gets them, so this method should never be called.
|
||||
*
|
||||
* Direct IO is not 'yet' supported in the cached mode. Hence when
|
||||
* this routine is called through generic_file_aio_read(), the read/write fails
|
||||
* with an error.
|
||||
*
|
||||
*/
|
||||
static ssize_t
|
||||
v9fs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
|
||||
loff_t pos, unsigned long nr_segs)
|
||||
{
|
||||
/*
|
||||
* FIXME
|
||||
* Now that we do caching with cache mode enabled, We need
|
||||
* to support direct IO
|
||||
*/
|
||||
p9_debug(P9_DEBUG_VFS, "v9fs_direct_IO: v9fs_direct_IO (%s) off/no(%lld/%lu) EINVAL\n",
|
||||
iocb->ki_filp->f_path.dentry->d_name.name,
|
||||
(long long)pos, nr_segs);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int v9fs_write_begin(struct file *filp, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned flags,
|
||||
struct page **pagep, void **fsdata)
|
||||
{
|
||||
int retval = 0;
|
||||
struct page *page;
|
||||
struct v9fs_inode *v9inode;
|
||||
pgoff_t index = pos >> PAGE_CACHE_SHIFT;
|
||||
struct inode *inode = mapping->host;
|
||||
|
||||
v9inode = V9FS_I(inode);
|
||||
start:
|
||||
page = grab_cache_page_write_begin(mapping, index, flags);
|
||||
if (!page) {
|
||||
retval = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
BUG_ON(!v9inode->writeback_fid);
|
||||
if (PageUptodate(page))
|
||||
goto out;
|
||||
|
||||
if (len == PAGE_CACHE_SIZE)
|
||||
goto out;
|
||||
|
||||
retval = v9fs_fid_readpage(v9inode->writeback_fid, page);
|
||||
page_cache_release(page);
|
||||
if (!retval)
|
||||
goto start;
|
||||
out:
|
||||
*pagep = page;
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int v9fs_write_end(struct file *filp, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied,
|
||||
struct page *page, void *fsdata)
|
||||
{
|
||||
loff_t last_pos = pos + copied;
|
||||
struct inode *inode = page->mapping->host;
|
||||
|
||||
if (unlikely(copied < len)) {
|
||||
/*
|
||||
* zero out the rest of the area
|
||||
*/
|
||||
unsigned from = pos & (PAGE_CACHE_SIZE - 1);
|
||||
|
||||
zero_user(page, from + copied, len - copied);
|
||||
flush_dcache_page(page);
|
||||
}
|
||||
|
||||
if (!PageUptodate(page))
|
||||
SetPageUptodate(page);
|
||||
/*
|
||||
* No need to use i_size_read() here, the i_size
|
||||
* cannot change under us because we hold the i_mutex.
|
||||
*/
|
||||
if (last_pos > inode->i_size) {
|
||||
inode_add_bytes(inode, last_pos - inode->i_size);
|
||||
i_size_write(inode, last_pos);
|
||||
}
|
||||
set_page_dirty(page);
|
||||
unlock_page(page);
|
||||
page_cache_release(page);
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
|
||||
const struct address_space_operations v9fs_addr_operations = {
|
||||
.readpage = v9fs_vfs_readpage,
|
||||
.readpages = v9fs_vfs_readpages,
|
||||
.set_page_dirty = __set_page_dirty_nobuffers,
|
||||
.writepage = v9fs_vfs_writepage,
|
||||
.write_begin = v9fs_write_begin,
|
||||
.write_end = v9fs_write_end,
|
||||
.releasepage = v9fs_release_page,
|
||||
.invalidatepage = v9fs_invalidate_page,
|
||||
.launder_page = v9fs_launder_page,
|
||||
.direct_IO = v9fs_direct_IO,
|
||||
};
|
||||
139
addons/9p/src/3.10.108/vfs_dentry.c
Normal file
139
addons/9p/src/3.10.108/vfs_dentry.c
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* linux/fs/9p/vfs_dentry.c
|
||||
*
|
||||
* This file contians 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>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
|
||||
#include "v9fs.h"
|
||||
#include "v9fs_vfs.h"
|
||||
#include "fid.h"
|
||||
|
||||
/**
|
||||
* v9fs_dentry_delete - called when dentry refcount equals 0
|
||||
* @dentry: dentry in question
|
||||
*
|
||||
* By returning 1 here we should remove cacheing of unused
|
||||
* dentry components.
|
||||
*
|
||||
*/
|
||||
|
||||
static int v9fs_dentry_delete(const struct dentry *dentry)
|
||||
{
|
||||
p9_debug(P9_DEBUG_VFS, " dentry: %s (%p)\n",
|
||||
dentry->d_name.name, dentry);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_cached_dentry_delete - called when dentry refcount equals 0
|
||||
* @dentry: dentry in question
|
||||
*
|
||||
*/
|
||||
static int v9fs_cached_dentry_delete(const struct dentry *dentry)
|
||||
{
|
||||
p9_debug(P9_DEBUG_VFS, " dentry: %s (%p)\n",
|
||||
dentry->d_name.name, dentry);
|
||||
|
||||
/* Don't cache negative dentries */
|
||||
if (!dentry->d_inode)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_dentry_release - called when dentry is going to be freed
|
||||
* @dentry: dentry that is being release
|
||||
*
|
||||
*/
|
||||
|
||||
static void v9fs_dentry_release(struct dentry *dentry)
|
||||
{
|
||||
struct hlist_node *p, *n;
|
||||
p9_debug(P9_DEBUG_VFS, " dentry: %s (%p)\n",
|
||||
dentry->d_name.name, dentry);
|
||||
hlist_for_each_safe(p, n, (struct hlist_head *)&dentry->d_fsdata)
|
||||
p9_client_clunk(hlist_entry(p, struct p9_fid, dlist));
|
||||
dentry->d_fsdata = NULL;
|
||||
}
|
||||
|
||||
static int v9fs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
|
||||
{
|
||||
struct p9_fid *fid;
|
||||
struct inode *inode;
|
||||
struct v9fs_inode *v9inode;
|
||||
|
||||
if (flags & LOOKUP_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
inode = dentry->d_inode;
|
||||
if (!inode)
|
||||
goto out_valid;
|
||||
|
||||
v9inode = V9FS_I(inode);
|
||||
if (v9inode->cache_validity & V9FS_INO_INVALID_ATTR) {
|
||||
int retval;
|
||||
struct v9fs_session_info *v9ses;
|
||||
fid = v9fs_fid_lookup(dentry);
|
||||
if (IS_ERR(fid))
|
||||
return PTR_ERR(fid);
|
||||
|
||||
v9ses = v9fs_inode2v9ses(inode);
|
||||
if (v9fs_proto_dotl(v9ses))
|
||||
retval = v9fs_refresh_inode_dotl(fid, inode);
|
||||
else
|
||||
retval = v9fs_refresh_inode(fid, inode);
|
||||
if (retval == -ENOENT)
|
||||
return 0;
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
}
|
||||
out_valid:
|
||||
return 1;
|
||||
}
|
||||
|
||||
const struct dentry_operations v9fs_cached_dentry_operations = {
|
||||
.d_revalidate = v9fs_lookup_revalidate,
|
||||
.d_weak_revalidate = v9fs_lookup_revalidate,
|
||||
.d_delete = v9fs_cached_dentry_delete,
|
||||
.d_release = v9fs_dentry_release,
|
||||
};
|
||||
|
||||
const struct dentry_operations v9fs_dentry_operations = {
|
||||
.d_delete = v9fs_dentry_delete,
|
||||
.d_release = v9fs_dentry_release,
|
||||
};
|
||||
269
addons/9p/src/3.10.108/vfs_dir.c
Normal file
269
addons/9p/src/3.10.108/vfs_dir.c
Normal file
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* linux/fs/9p/vfs_dir.c
|
||||
*
|
||||
* This file contains vfs directory ops for the 9P2000 protocol.
|
||||
*
|
||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
|
||||
#include "v9fs.h"
|
||||
#include "v9fs_vfs.h"
|
||||
#include "fid.h"
|
||||
|
||||
/**
|
||||
* struct p9_rdir - readdir accounting
|
||||
* @mutex: mutex protecting readdir
|
||||
* @head: start offset of current dirread buffer
|
||||
* @tail: end offset of current dirread buffer
|
||||
* @buf: dirread buffer
|
||||
*
|
||||
* private structure for keeping track of readdir
|
||||
* allocated on demand
|
||||
*/
|
||||
|
||||
struct p9_rdir {
|
||||
int head;
|
||||
int tail;
|
||||
uint8_t buf[];
|
||||
};
|
||||
|
||||
/**
|
||||
* dt_type - return file type
|
||||
* @mistat: mistat structure
|
||||
*
|
||||
*/
|
||||
|
||||
static inline int dt_type(struct p9_wstat *mistat)
|
||||
{
|
||||
unsigned long perm = mistat->mode;
|
||||
int rettype = DT_REG;
|
||||
|
||||
if (perm & P9_DMDIR)
|
||||
rettype = DT_DIR;
|
||||
if (perm & P9_DMSYMLINK)
|
||||
rettype = DT_LNK;
|
||||
|
||||
return rettype;
|
||||
}
|
||||
|
||||
static void p9stat_init(struct p9_wstat *stbuf)
|
||||
{
|
||||
stbuf->name = NULL;
|
||||
stbuf->uid = NULL;
|
||||
stbuf->gid = NULL;
|
||||
stbuf->muid = NULL;
|
||||
stbuf->extension = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_alloc_rdir_buf - Allocate buffer used for read and readdir
|
||||
* @filp: opened file structure
|
||||
* @buflen: Length in bytes of buffer to allocate
|
||||
*
|
||||
*/
|
||||
|
||||
static struct p9_rdir *v9fs_alloc_rdir_buf(struct file *filp, int buflen)
|
||||
{
|
||||
struct p9_fid *fid = filp->private_data;
|
||||
if (!fid->rdir)
|
||||
fid->rdir = kzalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL);
|
||||
return fid->rdir;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_dir_readdir - read a directory
|
||||
* @filp: opened file structure
|
||||
* @dirent: directory structure ???
|
||||
* @filldir: function to populate directory structure ???
|
||||
*
|
||||
*/
|
||||
|
||||
static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir)
|
||||
{
|
||||
int over;
|
||||
struct p9_wstat st;
|
||||
int err = 0;
|
||||
struct p9_fid *fid;
|
||||
int buflen;
|
||||
int reclen = 0;
|
||||
struct p9_rdir *rdir;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name);
|
||||
fid = filp->private_data;
|
||||
|
||||
buflen = fid->clnt->msize - P9_IOHDRSZ;
|
||||
|
||||
rdir = v9fs_alloc_rdir_buf(filp, buflen);
|
||||
if (!rdir)
|
||||
return -ENOMEM;
|
||||
|
||||
while (1) {
|
||||
if (rdir->tail == rdir->head) {
|
||||
err = v9fs_file_readn(filp, rdir->buf, NULL,
|
||||
buflen, filp->f_pos);
|
||||
if (err <= 0)
|
||||
return err;
|
||||
|
||||
rdir->head = 0;
|
||||
rdir->tail = err;
|
||||
}
|
||||
while (rdir->head < rdir->tail) {
|
||||
p9stat_init(&st);
|
||||
err = p9stat_read(fid->clnt, rdir->buf + rdir->head,
|
||||
rdir->tail - rdir->head, &st);
|
||||
if (err) {
|
||||
p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
|
||||
p9stat_free(&st);
|
||||
return -EIO;
|
||||
}
|
||||
reclen = st.size+2;
|
||||
|
||||
over = filldir(dirent, st.name, strlen(st.name),
|
||||
filp->f_pos, v9fs_qid2ino(&st.qid), dt_type(&st));
|
||||
|
||||
p9stat_free(&st);
|
||||
|
||||
if (over)
|
||||
return 0;
|
||||
|
||||
rdir->head += reclen;
|
||||
filp->f_pos += reclen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_dir_readdir_dotl - read a directory
|
||||
* @filp: opened file structure
|
||||
* @dirent: buffer to fill dirent structures
|
||||
* @filldir: function to populate dirent structures
|
||||
*
|
||||
*/
|
||||
static int v9fs_dir_readdir_dotl(struct file *filp, void *dirent,
|
||||
filldir_t filldir)
|
||||
{
|
||||
int over;
|
||||
int err = 0;
|
||||
struct p9_fid *fid;
|
||||
int buflen;
|
||||
struct p9_rdir *rdir;
|
||||
struct p9_dirent curdirent;
|
||||
u64 oldoffset = 0;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name);
|
||||
fid = filp->private_data;
|
||||
|
||||
buflen = fid->clnt->msize - P9_READDIRHDRSZ;
|
||||
|
||||
rdir = v9fs_alloc_rdir_buf(filp, buflen);
|
||||
if (!rdir)
|
||||
return -ENOMEM;
|
||||
|
||||
while (1) {
|
||||
if (rdir->tail == rdir->head) {
|
||||
err = p9_client_readdir(fid, rdir->buf, buflen,
|
||||
filp->f_pos);
|
||||
if (err <= 0)
|
||||
return err;
|
||||
|
||||
rdir->head = 0;
|
||||
rdir->tail = err;
|
||||
}
|
||||
|
||||
while (rdir->head < rdir->tail) {
|
||||
|
||||
err = p9dirent_read(fid->clnt, rdir->buf + rdir->head,
|
||||
rdir->tail - rdir->head,
|
||||
&curdirent);
|
||||
if (err < 0) {
|
||||
p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* d_off in dirent structure tracks the offset into
|
||||
* the next dirent in the dir. However, filldir()
|
||||
* expects offset into the current dirent. Hence
|
||||
* while calling filldir send the offset from the
|
||||
* previous dirent structure.
|
||||
*/
|
||||
over = filldir(dirent, curdirent.d_name,
|
||||
strlen(curdirent.d_name),
|
||||
oldoffset, v9fs_qid2ino(&curdirent.qid),
|
||||
curdirent.d_type);
|
||||
oldoffset = curdirent.d_off;
|
||||
|
||||
if (over)
|
||||
return 0;
|
||||
|
||||
filp->f_pos = curdirent.d_off;
|
||||
rdir->head += err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* v9fs_dir_release - close a directory
|
||||
* @inode: inode of the directory
|
||||
* @filp: file pointer to a directory
|
||||
*
|
||||
*/
|
||||
|
||||
int v9fs_dir_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct p9_fid *fid;
|
||||
|
||||
fid = filp->private_data;
|
||||
p9_debug(P9_DEBUG_VFS, "inode: %p filp: %p fid: %d\n",
|
||||
inode, filp, fid ? fid->fid : -1);
|
||||
if (fid)
|
||||
p9_client_clunk(fid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct file_operations v9fs_dir_operations = {
|
||||
.read = generic_read_dir,
|
||||
.llseek = generic_file_llseek,
|
||||
.readdir = v9fs_dir_readdir,
|
||||
.open = v9fs_file_open,
|
||||
.release = v9fs_dir_release,
|
||||
};
|
||||
|
||||
const struct file_operations v9fs_dir_operations_dotl = {
|
||||
.read = generic_read_dir,
|
||||
.llseek = generic_file_llseek,
|
||||
.readdir = v9fs_dir_readdir_dotl,
|
||||
.open = v9fs_file_open,
|
||||
.release = v9fs_dir_release,
|
||||
.fsync = v9fs_file_fsync_dotl,
|
||||
};
|
||||
790
addons/9p/src/3.10.108/vfs_file.c
Normal file
790
addons/9p/src/3.10.108/vfs_file.c
Normal file
@@ -0,0 +1,790 @@
|
||||
/*
|
||||
* linux/fs/9p/vfs_file.c
|
||||
*
|
||||
* This file contians vfs file ops for 9P2000.
|
||||
*
|
||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/idr.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
|
||||
#include "v9fs.h"
|
||||
#include "v9fs_vfs.h"
|
||||
#include "fid.h"
|
||||
#include "cache.h"
|
||||
|
||||
static const struct vm_operations_struct v9fs_file_vm_ops;
|
||||
|
||||
/**
|
||||
* v9fs_file_open - open a file (or directory)
|
||||
* @inode: inode to be opened
|
||||
* @file: file being opened
|
||||
*
|
||||
*/
|
||||
|
||||
int v9fs_file_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int err;
|
||||
struct v9fs_inode *v9inode;
|
||||
struct v9fs_session_info *v9ses;
|
||||
struct p9_fid *fid;
|
||||
int omode;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "inode: %p file: %p\n", inode, file);
|
||||
v9inode = V9FS_I(inode);
|
||||
v9ses = v9fs_inode2v9ses(inode);
|
||||
if (v9fs_proto_dotl(v9ses))
|
||||
omode = v9fs_open_to_dotl_flags(file->f_flags);
|
||||
else
|
||||
omode = v9fs_uflags2omode(file->f_flags,
|
||||
v9fs_proto_dotu(v9ses));
|
||||
fid = file->private_data;
|
||||
if (!fid) {
|
||||
fid = v9fs_fid_clone(file->f_path.dentry);
|
||||
if (IS_ERR(fid))
|
||||
return PTR_ERR(fid);
|
||||
|
||||
err = p9_client_open(fid, omode);
|
||||
if (err < 0) {
|
||||
p9_client_clunk(fid);
|
||||
return err;
|
||||
}
|
||||
if ((file->f_flags & O_APPEND) &&
|
||||
(!v9fs_proto_dotu(v9ses) && !v9fs_proto_dotl(v9ses)))
|
||||
generic_file_llseek(file, 0, SEEK_END);
|
||||
}
|
||||
|
||||
file->private_data = fid;
|
||||
mutex_lock(&v9inode->v_mutex);
|
||||
if (v9ses->cache && !v9inode->writeback_fid &&
|
||||
((file->f_flags & O_ACCMODE) != O_RDONLY)) {
|
||||
/*
|
||||
* clone a fid and add it to writeback_fid
|
||||
* we do it during open time instead of
|
||||
* page dirty time via write_begin/page_mkwrite
|
||||
* because we want write after unlink usecase
|
||||
* to work.
|
||||
*/
|
||||
fid = v9fs_writeback_fid(file->f_path.dentry);
|
||||
if (IS_ERR(fid)) {
|
||||
err = PTR_ERR(fid);
|
||||
mutex_unlock(&v9inode->v_mutex);
|
||||
goto out_error;
|
||||
}
|
||||
v9inode->writeback_fid = (void *) fid;
|
||||
}
|
||||
mutex_unlock(&v9inode->v_mutex);
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
if (v9ses->cache)
|
||||
v9fs_cache_inode_set_cookie(inode, file);
|
||||
#endif
|
||||
return 0;
|
||||
out_error:
|
||||
p9_client_clunk(file->private_data);
|
||||
file->private_data = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_file_lock - lock a file (or directory)
|
||||
* @filp: file to be locked
|
||||
* @cmd: lock command
|
||||
* @fl: file lock structure
|
||||
*
|
||||
* Bugs: this looks like a local only lock, we should extend into 9P
|
||||
* by using open exclusive
|
||||
*/
|
||||
|
||||
static int v9fs_file_lock(struct file *filp, int cmd, struct file_lock *fl)
|
||||
{
|
||||
int res = 0;
|
||||
struct inode *inode = file_inode(filp);
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "filp: %p lock: %p\n", filp, fl);
|
||||
|
||||
/* No mandatory locks */
|
||||
if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK)
|
||||
return -ENOLCK;
|
||||
|
||||
if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
|
||||
filemap_write_and_wait(inode->i_mapping);
|
||||
invalidate_mapping_pages(&inode->i_data, 0, -1);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int v9fs_file_do_lock(struct file *filp, int cmd, struct file_lock *fl)
|
||||
{
|
||||
struct p9_flock flock;
|
||||
struct p9_fid *fid;
|
||||
uint8_t status;
|
||||
int res = 0;
|
||||
unsigned char fl_type;
|
||||
|
||||
fid = filp->private_data;
|
||||
BUG_ON(fid == NULL);
|
||||
|
||||
if ((fl->fl_flags & FL_POSIX) != FL_POSIX)
|
||||
BUG();
|
||||
|
||||
res = posix_lock_file_wait(filp, fl);
|
||||
if (res < 0)
|
||||
goto out;
|
||||
|
||||
/* convert posix lock to p9 tlock args */
|
||||
memset(&flock, 0, sizeof(flock));
|
||||
/* map the lock type */
|
||||
switch (fl->fl_type) {
|
||||
case F_RDLCK:
|
||||
flock.type = P9_LOCK_TYPE_RDLCK;
|
||||
break;
|
||||
case F_WRLCK:
|
||||
flock.type = P9_LOCK_TYPE_WRLCK;
|
||||
break;
|
||||
case F_UNLCK:
|
||||
flock.type = P9_LOCK_TYPE_UNLCK;
|
||||
break;
|
||||
}
|
||||
flock.start = fl->fl_start;
|
||||
if (fl->fl_end == OFFSET_MAX)
|
||||
flock.length = 0;
|
||||
else
|
||||
flock.length = fl->fl_end - fl->fl_start + 1;
|
||||
flock.proc_id = fl->fl_pid;
|
||||
flock.client_id = utsname()->nodename;
|
||||
if (IS_SETLKW(cmd))
|
||||
flock.flags = P9_LOCK_FLAGS_BLOCK;
|
||||
|
||||
/*
|
||||
* if its a blocked request and we get P9_LOCK_BLOCKED as the status
|
||||
* for lock request, keep on trying
|
||||
*/
|
||||
for (;;) {
|
||||
res = p9_client_lock_dotl(fid, &flock, &status);
|
||||
if (res < 0)
|
||||
break;
|
||||
|
||||
if (status != P9_LOCK_BLOCKED)
|
||||
break;
|
||||
if (status == P9_LOCK_BLOCKED && !IS_SETLKW(cmd))
|
||||
break;
|
||||
if (schedule_timeout_interruptible(P9_LOCK_TIMEOUT) != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* map 9p status to VFS status */
|
||||
switch (status) {
|
||||
case P9_LOCK_SUCCESS:
|
||||
res = 0;
|
||||
break;
|
||||
case P9_LOCK_BLOCKED:
|
||||
res = -EAGAIN;
|
||||
break;
|
||||
case P9_LOCK_ERROR:
|
||||
case P9_LOCK_GRACE:
|
||||
res = -ENOLCK;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
/*
|
||||
* incase server returned error for lock request, revert
|
||||
* it locally
|
||||
*/
|
||||
if (res < 0 && fl->fl_type != F_UNLCK) {
|
||||
fl_type = fl->fl_type;
|
||||
fl->fl_type = F_UNLCK;
|
||||
res = posix_lock_file_wait(filp, fl);
|
||||
fl->fl_type = fl_type;
|
||||
}
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
|
||||
static int v9fs_file_getlock(struct file *filp, struct file_lock *fl)
|
||||
{
|
||||
struct p9_getlock glock;
|
||||
struct p9_fid *fid;
|
||||
int res = 0;
|
||||
|
||||
fid = filp->private_data;
|
||||
BUG_ON(fid == NULL);
|
||||
|
||||
posix_test_lock(filp, fl);
|
||||
/*
|
||||
* if we have a conflicting lock locally, no need to validate
|
||||
* with server
|
||||
*/
|
||||
if (fl->fl_type != F_UNLCK)
|
||||
return res;
|
||||
|
||||
/* convert posix lock to p9 tgetlock args */
|
||||
memset(&glock, 0, sizeof(glock));
|
||||
glock.type = P9_LOCK_TYPE_UNLCK;
|
||||
glock.start = fl->fl_start;
|
||||
if (fl->fl_end == OFFSET_MAX)
|
||||
glock.length = 0;
|
||||
else
|
||||
glock.length = fl->fl_end - fl->fl_start + 1;
|
||||
glock.proc_id = fl->fl_pid;
|
||||
glock.client_id = utsname()->nodename;
|
||||
|
||||
res = p9_client_getlock_dotl(fid, &glock);
|
||||
if (res < 0)
|
||||
return res;
|
||||
/* map 9p lock type to os lock type */
|
||||
switch (glock.type) {
|
||||
case P9_LOCK_TYPE_RDLCK:
|
||||
fl->fl_type = F_RDLCK;
|
||||
break;
|
||||
case P9_LOCK_TYPE_WRLCK:
|
||||
fl->fl_type = F_WRLCK;
|
||||
break;
|
||||
case P9_LOCK_TYPE_UNLCK:
|
||||
fl->fl_type = F_UNLCK;
|
||||
break;
|
||||
}
|
||||
if (glock.type != P9_LOCK_TYPE_UNLCK) {
|
||||
fl->fl_start = glock.start;
|
||||
if (glock.length == 0)
|
||||
fl->fl_end = OFFSET_MAX;
|
||||
else
|
||||
fl->fl_end = glock.start + glock.length - 1;
|
||||
fl->fl_pid = glock.proc_id;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_file_lock_dotl - lock a file (or directory)
|
||||
* @filp: file to be locked
|
||||
* @cmd: lock command
|
||||
* @fl: file lock structure
|
||||
*
|
||||
*/
|
||||
|
||||
static int v9fs_file_lock_dotl(struct file *filp, int cmd, struct file_lock *fl)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
int ret = -ENOLCK;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "filp: %p cmd:%d lock: %p name: %s\n",
|
||||
filp, cmd, fl, filp->f_path.dentry->d_name.name);
|
||||
|
||||
/* No mandatory locks */
|
||||
if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK)
|
||||
goto out_err;
|
||||
|
||||
if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
|
||||
filemap_write_and_wait(inode->i_mapping);
|
||||
invalidate_mapping_pages(&inode->i_data, 0, -1);
|
||||
}
|
||||
|
||||
if (IS_SETLK(cmd) || IS_SETLKW(cmd))
|
||||
ret = v9fs_file_do_lock(filp, cmd, fl);
|
||||
else if (IS_GETLK(cmd))
|
||||
ret = v9fs_file_getlock(filp, fl);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
out_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_file_flock_dotl - lock a file
|
||||
* @filp: file to be locked
|
||||
* @cmd: lock command
|
||||
* @fl: file lock structure
|
||||
*
|
||||
*/
|
||||
|
||||
static int v9fs_file_flock_dotl(struct file *filp, int cmd,
|
||||
struct file_lock *fl)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
int ret = -ENOLCK;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "filp: %p cmd:%d lock: %p name: %s\n",
|
||||
filp, cmd, fl, filp->f_path.dentry->d_name.name);
|
||||
|
||||
/* No mandatory locks */
|
||||
if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK)
|
||||
goto out_err;
|
||||
|
||||
if (!(fl->fl_flags & FL_FLOCK))
|
||||
goto out_err;
|
||||
|
||||
if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
|
||||
filemap_write_and_wait(inode->i_mapping);
|
||||
invalidate_mapping_pages(&inode->i_data, 0, -1);
|
||||
}
|
||||
/* Convert flock to posix lock */
|
||||
fl->fl_owner = (fl_owner_t)filp;
|
||||
fl->fl_start = 0;
|
||||
fl->fl_end = OFFSET_MAX;
|
||||
fl->fl_flags |= FL_POSIX;
|
||||
fl->fl_flags ^= FL_FLOCK;
|
||||
|
||||
if (IS_SETLK(cmd) | IS_SETLKW(cmd))
|
||||
ret = v9fs_file_do_lock(filp, cmd, fl);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
out_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_fid_readn - read from a fid
|
||||
* @fid: fid to read
|
||||
* @data: data buffer to read data into
|
||||
* @udata: user data buffer to read data into
|
||||
* @count: size of buffer
|
||||
* @offset: offset at which to read data
|
||||
*
|
||||
*/
|
||||
ssize_t
|
||||
v9fs_fid_readn(struct p9_fid *fid, char *data, char __user *udata, u32 count,
|
||||
u64 offset)
|
||||
{
|
||||
int n, total, size;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "fid %d offset %llu count %d\n",
|
||||
fid->fid, (long long unsigned)offset, count);
|
||||
n = 0;
|
||||
total = 0;
|
||||
size = fid->iounit ? fid->iounit : fid->clnt->msize - P9_IOHDRSZ;
|
||||
do {
|
||||
n = p9_client_read(fid, data, udata, offset, count);
|
||||
if (n <= 0)
|
||||
break;
|
||||
|
||||
if (data)
|
||||
data += n;
|
||||
if (udata)
|
||||
udata += n;
|
||||
|
||||
offset += n;
|
||||
count -= n;
|
||||
total += n;
|
||||
} while (count > 0 && n == size);
|
||||
|
||||
if (n < 0)
|
||||
total = n;
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_file_readn - read from a file
|
||||
* @filp: file pointer to read
|
||||
* @data: data buffer to read data into
|
||||
* @udata: user data buffer to read data into
|
||||
* @count: size of buffer
|
||||
* @offset: offset at which to read data
|
||||
*
|
||||
*/
|
||||
ssize_t
|
||||
v9fs_file_readn(struct file *filp, char *data, char __user *udata, u32 count,
|
||||
u64 offset)
|
||||
{
|
||||
return v9fs_fid_readn(filp->private_data, data, udata, count, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_file_read - read from a file
|
||||
* @filp: file pointer to read
|
||||
* @udata: user data buffer to read data into
|
||||
* @count: size of buffer
|
||||
* @offset: offset at which to read data
|
||||
*
|
||||
*/
|
||||
|
||||
static ssize_t
|
||||
v9fs_file_read(struct file *filp, char __user *udata, size_t count,
|
||||
loff_t * offset)
|
||||
{
|
||||
int ret;
|
||||
struct p9_fid *fid;
|
||||
size_t size;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "count %zu offset %lld\n", count, *offset);
|
||||
fid = filp->private_data;
|
||||
|
||||
size = fid->iounit ? fid->iounit : fid->clnt->msize - P9_IOHDRSZ;
|
||||
if (count > size)
|
||||
ret = v9fs_file_readn(filp, NULL, udata, count, *offset);
|
||||
else
|
||||
ret = p9_client_read(fid, NULL, udata, *offset, count);
|
||||
|
||||
if (ret > 0)
|
||||
*offset += ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
v9fs_file_write_internal(struct inode *inode, struct p9_fid *fid,
|
||||
const char __user *data, size_t count,
|
||||
loff_t *offset, int invalidate)
|
||||
{
|
||||
int n;
|
||||
loff_t i_size;
|
||||
size_t total = 0;
|
||||
struct p9_client *clnt;
|
||||
loff_t origin = *offset;
|
||||
unsigned long pg_start, pg_end;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "data %p count %d offset %x\n",
|
||||
data, (int)count, (int)*offset);
|
||||
|
||||
clnt = fid->clnt;
|
||||
do {
|
||||
n = p9_client_write(fid, NULL, data+total, origin+total, count);
|
||||
if (n <= 0)
|
||||
break;
|
||||
count -= n;
|
||||
total += n;
|
||||
} while (count > 0);
|
||||
|
||||
if (invalidate && (total > 0)) {
|
||||
pg_start = origin >> PAGE_CACHE_SHIFT;
|
||||
pg_end = (origin + total - 1) >> PAGE_CACHE_SHIFT;
|
||||
if (inode->i_mapping && inode->i_mapping->nrpages)
|
||||
invalidate_inode_pages2_range(inode->i_mapping,
|
||||
pg_start, pg_end);
|
||||
*offset += total;
|
||||
i_size = i_size_read(inode);
|
||||
if (*offset > i_size) {
|
||||
inode_add_bytes(inode, *offset - i_size);
|
||||
i_size_write(inode, *offset);
|
||||
}
|
||||
}
|
||||
if (n < 0)
|
||||
return n;
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_file_write - write to a file
|
||||
* @filp: file pointer to write
|
||||
* @data: data buffer to write data from
|
||||
* @count: size of buffer
|
||||
* @offset: offset at which to write data
|
||||
*
|
||||
*/
|
||||
static ssize_t
|
||||
v9fs_file_write(struct file *filp, const char __user * data,
|
||||
size_t count, loff_t *offset)
|
||||
{
|
||||
ssize_t retval = 0;
|
||||
loff_t origin = *offset;
|
||||
|
||||
|
||||
retval = generic_write_checks(filp, &origin, &count, 0);
|
||||
if (retval)
|
||||
goto out;
|
||||
|
||||
retval = -EINVAL;
|
||||
if ((ssize_t) count < 0)
|
||||
goto out;
|
||||
retval = 0;
|
||||
if (!count)
|
||||
goto out;
|
||||
|
||||
retval = v9fs_file_write_internal(file_inode(filp),
|
||||
filp->private_data,
|
||||
data, count, &origin, 1);
|
||||
/* update offset on successful write */
|
||||
if (retval > 0)
|
||||
*offset = origin;
|
||||
out:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
static int v9fs_file_fsync(struct file *filp, loff_t start, loff_t end,
|
||||
int datasync)
|
||||
{
|
||||
struct p9_fid *fid;
|
||||
struct inode *inode = filp->f_mapping->host;
|
||||
struct p9_wstat wstat;
|
||||
int retval;
|
||||
|
||||
retval = filemap_write_and_wait_range(inode->i_mapping, start, end);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
p9_debug(P9_DEBUG_VFS, "filp %p datasync %x\n", filp, datasync);
|
||||
|
||||
fid = filp->private_data;
|
||||
v9fs_blank_wstat(&wstat);
|
||||
|
||||
retval = p9_client_wstat(fid, &wstat);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int v9fs_file_fsync_dotl(struct file *filp, loff_t start, loff_t end,
|
||||
int datasync)
|
||||
{
|
||||
struct p9_fid *fid;
|
||||
struct inode *inode = filp->f_mapping->host;
|
||||
int retval;
|
||||
|
||||
retval = filemap_write_and_wait_range(inode->i_mapping, start, end);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
p9_debug(P9_DEBUG_VFS, "filp %p datasync %x\n", filp, datasync);
|
||||
|
||||
fid = filp->private_data;
|
||||
|
||||
retval = p9_client_fsync(fid, datasync);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int
|
||||
v9fs_file_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = generic_file_mmap(file, vma);
|
||||
if (!retval)
|
||||
vma->vm_ops = &v9fs_file_vm_ops;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int
|
||||
v9fs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
|
||||
{
|
||||
struct v9fs_inode *v9inode;
|
||||
struct page *page = vmf->page;
|
||||
struct file *filp = vma->vm_file;
|
||||
struct inode *inode = file_inode(filp);
|
||||
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "page %p fid %lx\n",
|
||||
page, (unsigned long)filp->private_data);
|
||||
|
||||
/* Update file times before taking page lock */
|
||||
file_update_time(filp);
|
||||
|
||||
v9inode = V9FS_I(inode);
|
||||
/* make sure the cache has finished storing the page */
|
||||
v9fs_fscache_wait_on_page_write(inode, page);
|
||||
BUG_ON(!v9inode->writeback_fid);
|
||||
lock_page(page);
|
||||
if (page->mapping != inode->i_mapping)
|
||||
goto out_unlock;
|
||||
wait_for_stable_page(page);
|
||||
|
||||
return VM_FAULT_LOCKED;
|
||||
out_unlock:
|
||||
unlock_page(page);
|
||||
return VM_FAULT_NOPAGE;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
v9fs_direct_read(struct file *filp, char __user *udata, size_t count,
|
||||
loff_t *offsetp)
|
||||
{
|
||||
loff_t size, offset;
|
||||
struct inode *inode;
|
||||
struct address_space *mapping;
|
||||
|
||||
offset = *offsetp;
|
||||
mapping = filp->f_mapping;
|
||||
inode = mapping->host;
|
||||
if (!count)
|
||||
return 0;
|
||||
size = i_size_read(inode);
|
||||
if (offset < size)
|
||||
filemap_write_and_wait_range(mapping, offset,
|
||||
offset + count - 1);
|
||||
|
||||
return v9fs_file_read(filp, udata, count, offsetp);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_cached_file_read - read from a file
|
||||
* @filp: file pointer to read
|
||||
* @udata: user data buffer to read data into
|
||||
* @count: size of buffer
|
||||
* @offset: offset at which to read data
|
||||
*
|
||||
*/
|
||||
static ssize_t
|
||||
v9fs_cached_file_read(struct file *filp, char __user *data, size_t count,
|
||||
loff_t *offset)
|
||||
{
|
||||
if (filp->f_flags & O_DIRECT)
|
||||
return v9fs_direct_read(filp, data, count, offset);
|
||||
return do_sync_read(filp, data, count, offset);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
v9fs_direct_write(struct file *filp, const char __user * data,
|
||||
size_t count, loff_t *offsetp)
|
||||
{
|
||||
loff_t offset;
|
||||
ssize_t retval;
|
||||
struct inode *inode;
|
||||
struct address_space *mapping;
|
||||
|
||||
offset = *offsetp;
|
||||
mapping = filp->f_mapping;
|
||||
inode = mapping->host;
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
retval = filemap_write_and_wait_range(mapping, offset,
|
||||
offset + count - 1);
|
||||
if (retval)
|
||||
goto err_out;
|
||||
/*
|
||||
* After a write we want buffered reads to be sure to go to disk to get
|
||||
* the new data. We invalidate clean cached page from the region we're
|
||||
* about to write. We do this *before* the write so that if we fail
|
||||
* here we fall back to buffered write
|
||||
*/
|
||||
if (mapping->nrpages) {
|
||||
pgoff_t pg_start = offset >> PAGE_CACHE_SHIFT;
|
||||
pgoff_t pg_end = (offset + count - 1) >> PAGE_CACHE_SHIFT;
|
||||
|
||||
retval = invalidate_inode_pages2_range(mapping,
|
||||
pg_start, pg_end);
|
||||
/*
|
||||
* If a page can not be invalidated, fall back
|
||||
* to buffered write.
|
||||
*/
|
||||
if (retval) {
|
||||
if (retval == -EBUSY)
|
||||
goto buff_write;
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
retval = v9fs_file_write(filp, data, count, offsetp);
|
||||
err_out:
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
return retval;
|
||||
|
||||
buff_write:
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
return do_sync_write(filp, data, count, offsetp);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_cached_file_write - write to a file
|
||||
* @filp: file pointer to write
|
||||
* @data: data buffer to write data from
|
||||
* @count: size of buffer
|
||||
* @offset: offset at which to write data
|
||||
*
|
||||
*/
|
||||
static ssize_t
|
||||
v9fs_cached_file_write(struct file *filp, const char __user * data,
|
||||
size_t count, loff_t *offset)
|
||||
{
|
||||
|
||||
if (filp->f_flags & O_DIRECT)
|
||||
return v9fs_direct_write(filp, data, count, offset);
|
||||
return do_sync_write(filp, data, count, offset);
|
||||
}
|
||||
|
||||
static const struct vm_operations_struct v9fs_file_vm_ops = {
|
||||
.fault = filemap_fault,
|
||||
.page_mkwrite = v9fs_vm_page_mkwrite,
|
||||
.remap_pages = generic_file_remap_pages,
|
||||
};
|
||||
|
||||
|
||||
const struct file_operations v9fs_cached_file_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read = v9fs_cached_file_read,
|
||||
.write = v9fs_cached_file_write,
|
||||
.aio_read = generic_file_aio_read,
|
||||
.aio_write = generic_file_aio_write,
|
||||
.open = v9fs_file_open,
|
||||
.release = v9fs_dir_release,
|
||||
.lock = v9fs_file_lock,
|
||||
.mmap = v9fs_file_mmap,
|
||||
.fsync = v9fs_file_fsync,
|
||||
};
|
||||
|
||||
const struct file_operations v9fs_cached_file_operations_dotl = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read = v9fs_cached_file_read,
|
||||
.write = v9fs_cached_file_write,
|
||||
.aio_read = generic_file_aio_read,
|
||||
.aio_write = generic_file_aio_write,
|
||||
.open = v9fs_file_open,
|
||||
.release = v9fs_dir_release,
|
||||
.lock = v9fs_file_lock_dotl,
|
||||
.flock = v9fs_file_flock_dotl,
|
||||
.mmap = v9fs_file_mmap,
|
||||
.fsync = v9fs_file_fsync_dotl,
|
||||
};
|
||||
|
||||
const struct file_operations v9fs_file_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read = v9fs_file_read,
|
||||
.write = v9fs_file_write,
|
||||
.open = v9fs_file_open,
|
||||
.release = v9fs_dir_release,
|
||||
.lock = v9fs_file_lock,
|
||||
.mmap = generic_file_readonly_mmap,
|
||||
.fsync = v9fs_file_fsync,
|
||||
};
|
||||
|
||||
const struct file_operations v9fs_file_operations_dotl = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read = v9fs_file_read,
|
||||
.write = v9fs_file_write,
|
||||
.open = v9fs_file_open,
|
||||
.release = v9fs_dir_release,
|
||||
.lock = v9fs_file_lock_dotl,
|
||||
.flock = v9fs_file_flock_dotl,
|
||||
.mmap = generic_file_readonly_mmap,
|
||||
.fsync = v9fs_file_fsync_dotl,
|
||||
};
|
||||
1538
addons/9p/src/3.10.108/vfs_inode.c
Normal file
1538
addons/9p/src/3.10.108/vfs_inode.c
Normal file
File diff suppressed because it is too large
Load Diff
1033
addons/9p/src/3.10.108/vfs_inode_dotl.c
Normal file
1033
addons/9p/src/3.10.108/vfs_inode_dotl.c
Normal file
File diff suppressed because it is too large
Load Diff
368
addons/9p/src/3.10.108/vfs_super.c
Normal file
368
addons/9p/src/3.10.108/vfs_super.c
Normal file
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
* linux/fs/9p/vfs_super.c
|
||||
*
|
||||
* This file contians superblock ops for 9P2000. It is intended that
|
||||
* you mount this file system on directories.
|
||||
*
|
||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/statfs.h>
|
||||
#include <linux/magic.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
|
||||
#include "v9fs.h"
|
||||
#include "v9fs_vfs.h"
|
||||
#include "fid.h"
|
||||
#include "xattr.h"
|
||||
#include "acl.h"
|
||||
|
||||
static const struct super_operations v9fs_super_ops, v9fs_super_ops_dotl;
|
||||
|
||||
/**
|
||||
* v9fs_set_super - set the superblock
|
||||
* @s: super block
|
||||
* @data: file system specific data
|
||||
*
|
||||
*/
|
||||
|
||||
static int v9fs_set_super(struct super_block *s, void *data)
|
||||
{
|
||||
s->s_fs_info = data;
|
||||
return set_anon_super(s, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_fill_super - populate superblock with info
|
||||
* @sb: superblock
|
||||
* @v9ses: session information
|
||||
* @flags: flags propagated from v9fs_mount()
|
||||
*
|
||||
*/
|
||||
|
||||
static void
|
||||
v9fs_fill_super(struct super_block *sb, struct v9fs_session_info *v9ses,
|
||||
int flags, void *data)
|
||||
{
|
||||
sb->s_maxbytes = MAX_LFS_FILESIZE;
|
||||
sb->s_blocksize_bits = fls(v9ses->maxdata - 1);
|
||||
sb->s_blocksize = 1 << sb->s_blocksize_bits;
|
||||
sb->s_magic = V9FS_MAGIC;
|
||||
if (v9fs_proto_dotl(v9ses)) {
|
||||
sb->s_op = &v9fs_super_ops_dotl;
|
||||
sb->s_xattr = v9fs_xattr_handlers;
|
||||
} else
|
||||
sb->s_op = &v9fs_super_ops;
|
||||
sb->s_bdi = &v9ses->bdi;
|
||||
if (v9ses->cache)
|
||||
sb->s_bdi->ra_pages = (VM_MAX_READAHEAD * 1024)/PAGE_CACHE_SIZE;
|
||||
|
||||
sb->s_flags |= MS_ACTIVE | MS_DIRSYNC | MS_NOATIME;
|
||||
if (!v9ses->cache)
|
||||
sb->s_flags |= MS_SYNCHRONOUS;
|
||||
|
||||
#ifdef CONFIG_9P_FS_POSIX_ACL
|
||||
if ((v9ses->flags & V9FS_ACL_MASK) == V9FS_POSIX_ACL)
|
||||
sb->s_flags |= MS_POSIXACL;
|
||||
#endif
|
||||
|
||||
save_mount_options(sb, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_mount - mount a superblock
|
||||
* @fs_type: file system type
|
||||
* @flags: mount flags
|
||||
* @dev_name: device name that was mounted
|
||||
* @data: mount options
|
||||
*
|
||||
*/
|
||||
|
||||
static struct dentry *v9fs_mount(struct file_system_type *fs_type, int flags,
|
||||
const char *dev_name, void *data)
|
||||
{
|
||||
struct super_block *sb = NULL;
|
||||
struct inode *inode = NULL;
|
||||
struct dentry *root = NULL;
|
||||
struct v9fs_session_info *v9ses = NULL;
|
||||
umode_t mode = S_IRWXUGO | S_ISVTX;
|
||||
struct p9_fid *fid;
|
||||
int retval = 0;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "\n");
|
||||
|
||||
v9ses = kzalloc(sizeof(struct v9fs_session_info), GFP_KERNEL);
|
||||
if (!v9ses)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
fid = v9fs_session_init(v9ses, dev_name, data);
|
||||
if (IS_ERR(fid)) {
|
||||
retval = PTR_ERR(fid);
|
||||
/*
|
||||
* we need to call session_close to tear down some
|
||||
* of the data structure setup by session_init
|
||||
*/
|
||||
goto close_session;
|
||||
}
|
||||
|
||||
sb = sget(fs_type, NULL, v9fs_set_super, flags, v9ses);
|
||||
if (IS_ERR(sb)) {
|
||||
retval = PTR_ERR(sb);
|
||||
goto clunk_fid;
|
||||
}
|
||||
v9fs_fill_super(sb, v9ses, flags, data);
|
||||
|
||||
if (v9ses->cache)
|
||||
sb->s_d_op = &v9fs_cached_dentry_operations;
|
||||
else
|
||||
sb->s_d_op = &v9fs_dentry_operations;
|
||||
|
||||
inode = v9fs_get_inode(sb, S_IFDIR | mode, 0);
|
||||
if (IS_ERR(inode)) {
|
||||
retval = PTR_ERR(inode);
|
||||
goto release_sb;
|
||||
}
|
||||
|
||||
root = d_make_root(inode);
|
||||
if (!root) {
|
||||
retval = -ENOMEM;
|
||||
goto release_sb;
|
||||
}
|
||||
sb->s_root = root;
|
||||
if (v9fs_proto_dotl(v9ses)) {
|
||||
struct p9_stat_dotl *st = NULL;
|
||||
st = p9_client_getattr_dotl(fid, P9_STATS_BASIC);
|
||||
if (IS_ERR(st)) {
|
||||
retval = PTR_ERR(st);
|
||||
goto release_sb;
|
||||
}
|
||||
root->d_inode->i_ino = v9fs_qid2ino(&st->qid);
|
||||
v9fs_stat2inode_dotl(st, root->d_inode);
|
||||
kfree(st);
|
||||
} else {
|
||||
struct p9_wstat *st = NULL;
|
||||
st = p9_client_stat(fid);
|
||||
if (IS_ERR(st)) {
|
||||
retval = PTR_ERR(st);
|
||||
goto release_sb;
|
||||
}
|
||||
|
||||
root->d_inode->i_ino = v9fs_qid2ino(&st->qid);
|
||||
v9fs_stat2inode(st, root->d_inode, sb);
|
||||
|
||||
p9stat_free(st);
|
||||
kfree(st);
|
||||
}
|
||||
retval = v9fs_get_acl(inode, fid);
|
||||
if (retval)
|
||||
goto release_sb;
|
||||
v9fs_fid_add(root, fid);
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, " simple set mount, return 0\n");
|
||||
return dget(sb->s_root);
|
||||
|
||||
clunk_fid:
|
||||
p9_client_clunk(fid);
|
||||
close_session:
|
||||
v9fs_session_close(v9ses);
|
||||
kfree(v9ses);
|
||||
return ERR_PTR(retval);
|
||||
|
||||
release_sb:
|
||||
/*
|
||||
* we will do the session_close and root dentry release
|
||||
* in the below call. But we need to clunk fid, because we haven't
|
||||
* attached the fid to dentry so it won't get clunked
|
||||
* automatically.
|
||||
*/
|
||||
p9_client_clunk(fid);
|
||||
deactivate_locked_super(sb);
|
||||
return ERR_PTR(retval);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_kill_super - Kill Superblock
|
||||
* @s: superblock
|
||||
*
|
||||
*/
|
||||
|
||||
static void v9fs_kill_super(struct super_block *s)
|
||||
{
|
||||
struct v9fs_session_info *v9ses = s->s_fs_info;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, " %p\n", s);
|
||||
|
||||
kill_anon_super(s);
|
||||
|
||||
v9fs_session_cancel(v9ses);
|
||||
v9fs_session_close(v9ses);
|
||||
kfree(v9ses);
|
||||
s->s_fs_info = NULL;
|
||||
p9_debug(P9_DEBUG_VFS, "exiting kill_super\n");
|
||||
}
|
||||
|
||||
static void
|
||||
v9fs_umount_begin(struct super_block *sb)
|
||||
{
|
||||
struct v9fs_session_info *v9ses;
|
||||
|
||||
v9ses = sb->s_fs_info;
|
||||
v9fs_session_begin_cancel(v9ses);
|
||||
}
|
||||
|
||||
static int v9fs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||
{
|
||||
struct v9fs_session_info *v9ses;
|
||||
struct p9_fid *fid;
|
||||
struct p9_rstatfs rs;
|
||||
int res;
|
||||
|
||||
fid = v9fs_fid_lookup(dentry);
|
||||
if (IS_ERR(fid)) {
|
||||
res = PTR_ERR(fid);
|
||||
goto done;
|
||||
}
|
||||
|
||||
v9ses = v9fs_dentry2v9ses(dentry);
|
||||
if (v9fs_proto_dotl(v9ses)) {
|
||||
res = p9_client_statfs(fid, &rs);
|
||||
if (res == 0) {
|
||||
buf->f_type = rs.type;
|
||||
buf->f_bsize = rs.bsize;
|
||||
buf->f_blocks = rs.blocks;
|
||||
buf->f_bfree = rs.bfree;
|
||||
buf->f_bavail = rs.bavail;
|
||||
buf->f_files = rs.files;
|
||||
buf->f_ffree = rs.ffree;
|
||||
buf->f_fsid.val[0] = rs.fsid & 0xFFFFFFFFUL;
|
||||
buf->f_fsid.val[1] = (rs.fsid >> 32) & 0xFFFFFFFFUL;
|
||||
buf->f_namelen = rs.namelen;
|
||||
}
|
||||
if (res != -ENOSYS)
|
||||
goto done;
|
||||
}
|
||||
res = simple_statfs(dentry, buf);
|
||||
done:
|
||||
return res;
|
||||
}
|
||||
|
||||
static int v9fs_drop_inode(struct inode *inode)
|
||||
{
|
||||
struct v9fs_session_info *v9ses;
|
||||
v9ses = v9fs_inode2v9ses(inode);
|
||||
if (v9ses->cache)
|
||||
return generic_drop_inode(inode);
|
||||
/*
|
||||
* in case of non cached mode always drop the
|
||||
* the inode because we want the inode attribute
|
||||
* to always match that on the server.
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int v9fs_write_inode(struct inode *inode,
|
||||
struct writeback_control *wbc)
|
||||
{
|
||||
int ret;
|
||||
struct p9_wstat wstat;
|
||||
struct v9fs_inode *v9inode;
|
||||
/*
|
||||
* send an fsync request to server irrespective of
|
||||
* wbc->sync_mode.
|
||||
*/
|
||||
p9_debug(P9_DEBUG_VFS, "%s: inode %p\n", __func__, inode);
|
||||
v9inode = V9FS_I(inode);
|
||||
if (!v9inode->writeback_fid)
|
||||
return 0;
|
||||
v9fs_blank_wstat(&wstat);
|
||||
|
||||
ret = p9_client_wstat(v9inode->writeback_fid, &wstat);
|
||||
if (ret < 0) {
|
||||
__mark_inode_dirty(inode, I_DIRTY_DATASYNC);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int v9fs_write_inode_dotl(struct inode *inode,
|
||||
struct writeback_control *wbc)
|
||||
{
|
||||
int ret;
|
||||
struct v9fs_inode *v9inode;
|
||||
/*
|
||||
* send an fsync request to server irrespective of
|
||||
* wbc->sync_mode.
|
||||
*/
|
||||
p9_debug(P9_DEBUG_VFS, "%s: inode %p\n", __func__, inode);
|
||||
v9inode = V9FS_I(inode);
|
||||
if (!v9inode->writeback_fid)
|
||||
return 0;
|
||||
ret = p9_client_fsync(v9inode->writeback_fid, 0);
|
||||
if (ret < 0) {
|
||||
__mark_inode_dirty(inode, I_DIRTY_DATASYNC);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct super_operations v9fs_super_ops = {
|
||||
.alloc_inode = v9fs_alloc_inode,
|
||||
.destroy_inode = v9fs_destroy_inode,
|
||||
.statfs = simple_statfs,
|
||||
.evict_inode = v9fs_evict_inode,
|
||||
.show_options = generic_show_options,
|
||||
.umount_begin = v9fs_umount_begin,
|
||||
.write_inode = v9fs_write_inode,
|
||||
};
|
||||
|
||||
static const struct super_operations v9fs_super_ops_dotl = {
|
||||
.alloc_inode = v9fs_alloc_inode,
|
||||
.destroy_inode = v9fs_destroy_inode,
|
||||
.statfs = v9fs_statfs,
|
||||
.drop_inode = v9fs_drop_inode,
|
||||
.evict_inode = v9fs_evict_inode,
|
||||
.show_options = generic_show_options,
|
||||
.umount_begin = v9fs_umount_begin,
|
||||
.write_inode = v9fs_write_inode_dotl,
|
||||
};
|
||||
|
||||
struct file_system_type v9fs_fs_type = {
|
||||
.name = "9p",
|
||||
.mount = v9fs_mount,
|
||||
.kill_sb = v9fs_kill_super,
|
||||
.owner = THIS_MODULE,
|
||||
.fs_flags = FS_RENAME_DOES_D_MOVE,
|
||||
};
|
||||
MODULE_ALIAS_FS("9p");
|
||||
175
addons/9p/src/3.10.108/xattr.c
Normal file
175
addons/9p/src/3.10.108/xattr.c
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright IBM Corporation, 2010
|
||||
* Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/sched.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
|
||||
#include "fid.h"
|
||||
#include "xattr.h"
|
||||
|
||||
ssize_t v9fs_fid_xattr_get(struct p9_fid *fid, const char *name,
|
||||
void *buffer, size_t buffer_size)
|
||||
{
|
||||
ssize_t retval;
|
||||
int msize, read_count;
|
||||
u64 offset = 0, attr_size;
|
||||
struct p9_fid *attr_fid;
|
||||
|
||||
attr_fid = p9_client_xattrwalk(fid, name, &attr_size);
|
||||
if (IS_ERR(attr_fid)) {
|
||||
retval = PTR_ERR(attr_fid);
|
||||
p9_debug(P9_DEBUG_VFS, "p9_client_attrwalk failed %zd\n",
|
||||
retval);
|
||||
attr_fid = NULL;
|
||||
goto error;
|
||||
}
|
||||
if (!buffer_size) {
|
||||
/* request to get the attr_size */
|
||||
retval = attr_size;
|
||||
goto error;
|
||||
}
|
||||
if (attr_size > buffer_size) {
|
||||
retval = -ERANGE;
|
||||
goto error;
|
||||
}
|
||||
msize = attr_fid->clnt->msize;
|
||||
while (attr_size) {
|
||||
if (attr_size > (msize - P9_IOHDRSZ))
|
||||
read_count = msize - P9_IOHDRSZ;
|
||||
else
|
||||
read_count = attr_size;
|
||||
read_count = p9_client_read(attr_fid, ((char *)buffer)+offset,
|
||||
NULL, offset, read_count);
|
||||
if (read_count < 0) {
|
||||
/* error in xattr read */
|
||||
retval = read_count;
|
||||
goto error;
|
||||
}
|
||||
offset += read_count;
|
||||
attr_size -= read_count;
|
||||
}
|
||||
/* Total read xattr bytes */
|
||||
retval = offset;
|
||||
error:
|
||||
if (attr_fid)
|
||||
p9_client_clunk(attr_fid);
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* v9fs_xattr_get()
|
||||
*
|
||||
* Copy an extended attribute into the buffer
|
||||
* provided, or compute the buffer size required.
|
||||
* Buffer is NULL to compute the size of the buffer required.
|
||||
*
|
||||
* Returns a negative error number on failure, or the number of bytes
|
||||
* used / required on success.
|
||||
*/
|
||||
ssize_t v9fs_xattr_get(struct dentry *dentry, const char *name,
|
||||
void *buffer, size_t buffer_size)
|
||||
{
|
||||
struct p9_fid *fid;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "name = %s value_len = %zu\n",
|
||||
name, buffer_size);
|
||||
fid = v9fs_fid_lookup(dentry);
|
||||
if (IS_ERR(fid))
|
||||
return PTR_ERR(fid);
|
||||
|
||||
return v9fs_fid_xattr_get(fid, name, buffer, buffer_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* v9fs_xattr_set()
|
||||
*
|
||||
* Create, replace or remove an extended attribute for this inode. Buffer
|
||||
* is NULL to remove an existing extended attribute, and non-NULL to
|
||||
* either replace an existing extended attribute, or create a new extended
|
||||
* attribute. The flags XATTR_REPLACE and XATTR_CREATE
|
||||
* specify that an extended attribute must exist and must not exist
|
||||
* previous to the call, respectively.
|
||||
*
|
||||
* Returns 0, or a negative error number on failure.
|
||||
*/
|
||||
int v9fs_xattr_set(struct dentry *dentry, const char *name,
|
||||
const void *value, size_t value_len, int flags)
|
||||
{
|
||||
struct p9_fid *fid = v9fs_fid_lookup(dentry);
|
||||
if (IS_ERR(fid))
|
||||
return PTR_ERR(fid);
|
||||
return v9fs_fid_xattr_set(fid, name, value, value_len, flags);
|
||||
}
|
||||
|
||||
int v9fs_fid_xattr_set(struct p9_fid *fid, const char *name,
|
||||
const void *value, size_t value_len, int flags)
|
||||
{
|
||||
u64 offset = 0;
|
||||
int retval, msize, write_count;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "name = %s value_len = %zu flags = %d\n",
|
||||
name, value_len, flags);
|
||||
|
||||
/* Clone it */
|
||||
fid = p9_client_walk(fid, 0, NULL, 1);
|
||||
if (IS_ERR(fid))
|
||||
return PTR_ERR(fid);
|
||||
|
||||
/*
|
||||
* On success fid points to xattr
|
||||
*/
|
||||
retval = p9_client_xattrcreate(fid, name, value_len, flags);
|
||||
if (retval < 0) {
|
||||
p9_debug(P9_DEBUG_VFS, "p9_client_xattrcreate failed %d\n",
|
||||
retval);
|
||||
p9_client_clunk(fid);
|
||||
return retval;
|
||||
}
|
||||
msize = fid->clnt->msize;
|
||||
while (value_len) {
|
||||
if (value_len > (msize - P9_IOHDRSZ))
|
||||
write_count = msize - P9_IOHDRSZ;
|
||||
else
|
||||
write_count = value_len;
|
||||
write_count = p9_client_write(fid, ((char *)value)+offset,
|
||||
NULL, offset, write_count);
|
||||
if (write_count < 0) {
|
||||
/* error in xattr write */
|
||||
retval = write_count;
|
||||
break;
|
||||
}
|
||||
offset += write_count;
|
||||
value_len -= write_count;
|
||||
}
|
||||
return p9_client_clunk(fid);
|
||||
}
|
||||
|
||||
ssize_t v9fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
|
||||
{
|
||||
return v9fs_xattr_get(dentry, NULL, buffer, buffer_size);
|
||||
}
|
||||
|
||||
const struct xattr_handler *v9fs_xattr_handlers[] = {
|
||||
&v9fs_xattr_user_handler,
|
||||
#ifdef CONFIG_9P_FS_POSIX_ACL
|
||||
&v9fs_xattr_acl_access_handler,
|
||||
&v9fs_xattr_acl_default_handler,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
35
addons/9p/src/3.10.108/xattr.h
Normal file
35
addons/9p/src/3.10.108/xattr.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright IBM Corporation, 2010
|
||||
* Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
#ifndef FS_9P_XATTR_H
|
||||
#define FS_9P_XATTR_H
|
||||
|
||||
#include <linux/xattr.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
|
||||
extern const struct xattr_handler *v9fs_xattr_handlers[];
|
||||
extern struct xattr_handler v9fs_xattr_user_handler;
|
||||
extern const struct xattr_handler v9fs_xattr_acl_access_handler;
|
||||
extern const struct xattr_handler v9fs_xattr_acl_default_handler;
|
||||
|
||||
extern ssize_t v9fs_fid_xattr_get(struct p9_fid *, const char *,
|
||||
void *, size_t);
|
||||
extern ssize_t v9fs_xattr_get(struct dentry *, const char *,
|
||||
void *, size_t);
|
||||
extern int v9fs_fid_xattr_set(struct p9_fid *, const char *,
|
||||
const void *, size_t, int);
|
||||
extern int v9fs_xattr_set(struct dentry *, const char *,
|
||||
const void *, size_t, int);
|
||||
extern ssize_t v9fs_listxattr(struct dentry *, char *, size_t);
|
||||
#endif /* FS_9P_XATTR_H */
|
||||
80
addons/9p/src/3.10.108/xattr_user.c
Normal file
80
addons/9p/src/3.10.108/xattr_user.c
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright IBM Corporation, 2010
|
||||
* Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include "xattr.h"
|
||||
|
||||
static int v9fs_xattr_user_get(struct dentry *dentry, const char *name,
|
||||
void *buffer, size_t size, int type)
|
||||
{
|
||||
int retval;
|
||||
char *full_name;
|
||||
size_t name_len;
|
||||
size_t prefix_len = XATTR_USER_PREFIX_LEN;
|
||||
|
||||
if (name == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (strcmp(name, "") == 0)
|
||||
return -EINVAL;
|
||||
|
||||
name_len = strlen(name);
|
||||
full_name = kmalloc(prefix_len + name_len + 1 , GFP_KERNEL);
|
||||
if (!full_name)
|
||||
return -ENOMEM;
|
||||
memcpy(full_name, XATTR_USER_PREFIX, prefix_len);
|
||||
memcpy(full_name+prefix_len, name, name_len);
|
||||
full_name[prefix_len + name_len] = '\0';
|
||||
|
||||
retval = v9fs_xattr_get(dentry, full_name, buffer, size);
|
||||
kfree(full_name);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int v9fs_xattr_user_set(struct dentry *dentry, const char *name,
|
||||
const void *value, size_t size, int flags, int type)
|
||||
{
|
||||
int retval;
|
||||
char *full_name;
|
||||
size_t name_len;
|
||||
size_t prefix_len = XATTR_USER_PREFIX_LEN;
|
||||
|
||||
if (name == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (strcmp(name, "") == 0)
|
||||
return -EINVAL;
|
||||
|
||||
name_len = strlen(name);
|
||||
full_name = kmalloc(prefix_len + name_len + 1 , GFP_KERNEL);
|
||||
if (!full_name)
|
||||
return -ENOMEM;
|
||||
memcpy(full_name, XATTR_USER_PREFIX, prefix_len);
|
||||
memcpy(full_name + prefix_len, name, name_len);
|
||||
full_name[prefix_len + name_len] = '\0';
|
||||
|
||||
retval = v9fs_xattr_set(dentry, full_name, value, size, flags);
|
||||
kfree(full_name);
|
||||
return retval;
|
||||
}
|
||||
|
||||
struct xattr_handler v9fs_xattr_user_handler = {
|
||||
.prefix = XATTR_USER_PREFIX,
|
||||
.get = v9fs_xattr_user_get,
|
||||
.set = v9fs_xattr_user_set,
|
||||
};
|
||||
35
addons/9p/src/4.4.180/Makefile
Normal file
35
addons/9p/src/4.4.180/Makefile
Normal file
@@ -0,0 +1,35 @@
|
||||
obj-m := 9p.o
|
||||
|
||||
9p-objs := \
|
||||
vfs_super.o \
|
||||
vfs_inode.o \
|
||||
vfs_inode_dotl.o \
|
||||
vfs_addr.o \
|
||||
vfs_file.o \
|
||||
vfs_dir.o \
|
||||
vfs_dentry.o \
|
||||
v9fs.o \
|
||||
fid.o \
|
||||
xattr.o
|
||||
|
||||
9p-y += cache.o
|
||||
9p-n += acl.o
|
||||
|
||||
obj-m := 9pnet.o
|
||||
obj-m += 9pnet_virtio.o
|
||||
obj-n += 9pnet_rdma.o
|
||||
|
||||
9pnet-objs := \
|
||||
mod.o \
|
||||
client.o \
|
||||
error.o \
|
||||
util.o \
|
||||
protocol.o \
|
||||
trans_fd.o \
|
||||
trans_common.o \
|
||||
|
||||
9pnet_virtio-objs := \
|
||||
trans_virtio.o
|
||||
|
||||
9pnet_rdma-objs := \
|
||||
trans_rdma.o
|
||||
338
addons/9p/src/4.4.180/acl.c
Normal file
338
addons/9p/src/4.4.180/acl.c
Normal file
@@ -0,0 +1,338 @@
|
||||
/*
|
||||
* Copyright IBM Corporation, 2010
|
||||
* Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/posix_acl_xattr.h>
|
||||
#include "xattr.h"
|
||||
#include "acl.h"
|
||||
#include "v9fs.h"
|
||||
#include "v9fs_vfs.h"
|
||||
#include "fid.h"
|
||||
|
||||
static struct posix_acl *__v9fs_get_acl(struct p9_fid *fid, char *name)
|
||||
{
|
||||
ssize_t size;
|
||||
void *value = NULL;
|
||||
struct posix_acl *acl = NULL;
|
||||
|
||||
size = v9fs_fid_xattr_get(fid, name, NULL, 0);
|
||||
if (size > 0) {
|
||||
value = kzalloc(size, GFP_NOFS);
|
||||
if (!value)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
size = v9fs_fid_xattr_get(fid, name, value, size);
|
||||
if (size > 0) {
|
||||
acl = posix_acl_from_xattr(&init_user_ns, value, size);
|
||||
if (IS_ERR(acl))
|
||||
goto err_out;
|
||||
}
|
||||
} else if (size == -ENODATA || size == 0 ||
|
||||
size == -ENOSYS || size == -EOPNOTSUPP) {
|
||||
acl = NULL;
|
||||
} else
|
||||
acl = ERR_PTR(-EIO);
|
||||
|
||||
err_out:
|
||||
kfree(value);
|
||||
return acl;
|
||||
}
|
||||
|
||||
int v9fs_get_acl(struct inode *inode, struct p9_fid *fid)
|
||||
{
|
||||
int retval = 0;
|
||||
struct posix_acl *pacl, *dacl;
|
||||
struct v9fs_session_info *v9ses;
|
||||
|
||||
v9ses = v9fs_inode2v9ses(inode);
|
||||
if (((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) ||
|
||||
((v9ses->flags & V9FS_ACL_MASK) != V9FS_POSIX_ACL)) {
|
||||
set_cached_acl(inode, ACL_TYPE_DEFAULT, NULL);
|
||||
set_cached_acl(inode, ACL_TYPE_ACCESS, NULL);
|
||||
return 0;
|
||||
}
|
||||
/* get the default/access acl values and cache them */
|
||||
dacl = __v9fs_get_acl(fid, POSIX_ACL_XATTR_DEFAULT);
|
||||
pacl = __v9fs_get_acl(fid, POSIX_ACL_XATTR_ACCESS);
|
||||
|
||||
if (!IS_ERR(dacl) && !IS_ERR(pacl)) {
|
||||
set_cached_acl(inode, ACL_TYPE_DEFAULT, dacl);
|
||||
set_cached_acl(inode, ACL_TYPE_ACCESS, pacl);
|
||||
} else
|
||||
retval = -EIO;
|
||||
|
||||
if (!IS_ERR(dacl))
|
||||
posix_acl_release(dacl);
|
||||
|
||||
if (!IS_ERR(pacl))
|
||||
posix_acl_release(pacl);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static struct posix_acl *v9fs_get_cached_acl(struct inode *inode, int type)
|
||||
{
|
||||
struct posix_acl *acl;
|
||||
/*
|
||||
* 9p Always cache the acl value when
|
||||
* instantiating the inode (v9fs_inode_from_fid)
|
||||
*/
|
||||
acl = get_cached_acl(inode, type);
|
||||
BUG_ON(acl == ACL_NOT_CACHED);
|
||||
return acl;
|
||||
}
|
||||
|
||||
struct posix_acl *v9fs_iop_get_acl(struct inode *inode, int type)
|
||||
{
|
||||
struct v9fs_session_info *v9ses;
|
||||
|
||||
v9ses = v9fs_inode2v9ses(inode);
|
||||
if (((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) ||
|
||||
((v9ses->flags & V9FS_ACL_MASK) != V9FS_POSIX_ACL)) {
|
||||
/*
|
||||
* On access = client and acl = on mode get the acl
|
||||
* values from the server
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
return v9fs_get_cached_acl(inode, type);
|
||||
|
||||
}
|
||||
|
||||
static int v9fs_set_acl(struct p9_fid *fid, int type, struct posix_acl *acl)
|
||||
{
|
||||
int retval;
|
||||
char *name;
|
||||
size_t size;
|
||||
void *buffer;
|
||||
if (!acl)
|
||||
return 0;
|
||||
|
||||
/* Set a setxattr request to server */
|
||||
size = posix_acl_xattr_size(acl->a_count);
|
||||
buffer = kmalloc(size, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
retval = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
|
||||
if (retval < 0)
|
||||
goto err_free_out;
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
name = POSIX_ACL_XATTR_ACCESS;
|
||||
break;
|
||||
case ACL_TYPE_DEFAULT:
|
||||
name = POSIX_ACL_XATTR_DEFAULT;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
retval = v9fs_fid_xattr_set(fid, name, buffer, size, 0);
|
||||
err_free_out:
|
||||
kfree(buffer);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int v9fs_acl_chmod(struct inode *inode, struct p9_fid *fid)
|
||||
{
|
||||
int retval = 0;
|
||||
struct posix_acl *acl;
|
||||
|
||||
if (S_ISLNK(inode->i_mode))
|
||||
return -EOPNOTSUPP;
|
||||
acl = v9fs_get_cached_acl(inode, ACL_TYPE_ACCESS);
|
||||
if (acl) {
|
||||
retval = __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
|
||||
if (retval)
|
||||
return retval;
|
||||
set_cached_acl(inode, ACL_TYPE_ACCESS, acl);
|
||||
retval = v9fs_set_acl(fid, ACL_TYPE_ACCESS, acl);
|
||||
posix_acl_release(acl);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
int v9fs_set_create_acl(struct inode *inode, struct p9_fid *fid,
|
||||
struct posix_acl *dacl, struct posix_acl *acl)
|
||||
{
|
||||
set_cached_acl(inode, ACL_TYPE_DEFAULT, dacl);
|
||||
set_cached_acl(inode, ACL_TYPE_ACCESS, acl);
|
||||
v9fs_set_acl(fid, ACL_TYPE_DEFAULT, dacl);
|
||||
v9fs_set_acl(fid, ACL_TYPE_ACCESS, acl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void v9fs_put_acl(struct posix_acl *dacl,
|
||||
struct posix_acl *acl)
|
||||
{
|
||||
posix_acl_release(dacl);
|
||||
posix_acl_release(acl);
|
||||
}
|
||||
|
||||
int v9fs_acl_mode(struct inode *dir, umode_t *modep,
|
||||
struct posix_acl **dpacl, struct posix_acl **pacl)
|
||||
{
|
||||
int retval = 0;
|
||||
umode_t mode = *modep;
|
||||
struct posix_acl *acl = NULL;
|
||||
|
||||
if (!S_ISLNK(mode)) {
|
||||
acl = v9fs_get_cached_acl(dir, ACL_TYPE_DEFAULT);
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
if (!acl)
|
||||
mode &= ~current_umask();
|
||||
}
|
||||
if (acl) {
|
||||
if (S_ISDIR(mode))
|
||||
*dpacl = posix_acl_dup(acl);
|
||||
retval = __posix_acl_create(&acl, GFP_NOFS, &mode);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
if (retval > 0)
|
||||
*pacl = acl;
|
||||
else
|
||||
posix_acl_release(acl);
|
||||
}
|
||||
*modep = mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int v9fs_xattr_get_acl(const struct xattr_handler *handler,
|
||||
struct dentry *dentry, const char *name,
|
||||
void *buffer, size_t size)
|
||||
{
|
||||
struct v9fs_session_info *v9ses;
|
||||
struct posix_acl *acl;
|
||||
int error;
|
||||
|
||||
if (strcmp(name, "") != 0)
|
||||
return -EINVAL;
|
||||
|
||||
v9ses = v9fs_dentry2v9ses(dentry);
|
||||
/*
|
||||
* We allow set/get/list of acl when access=client is not specified
|
||||
*/
|
||||
if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT)
|
||||
return v9fs_xattr_get(dentry, handler->prefix, buffer, size);
|
||||
|
||||
acl = v9fs_get_cached_acl(d_inode(dentry), handler->flags);
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
if (acl == NULL)
|
||||
return -ENODATA;
|
||||
error = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
|
||||
posix_acl_release(acl);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int v9fs_xattr_set_acl(const struct xattr_handler *handler,
|
||||
struct dentry *dentry, const char *name,
|
||||
const void *value, size_t size, int flags)
|
||||
{
|
||||
int retval;
|
||||
struct posix_acl *acl;
|
||||
struct v9fs_session_info *v9ses;
|
||||
struct inode *inode = d_inode(dentry);
|
||||
|
||||
if (strcmp(name, "") != 0)
|
||||
return -EINVAL;
|
||||
|
||||
v9ses = v9fs_dentry2v9ses(dentry);
|
||||
/*
|
||||
* set the attribute on the remote. Without even looking at the
|
||||
* xattr value. We leave it to the server to validate
|
||||
*/
|
||||
if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT)
|
||||
return v9fs_xattr_set(dentry, handler->prefix, value, size,
|
||||
flags);
|
||||
|
||||
if (S_ISLNK(inode->i_mode))
|
||||
return -EOPNOTSUPP;
|
||||
if (!inode_owner_or_capable(inode))
|
||||
return -EPERM;
|
||||
if (value) {
|
||||
/* update the cached acl value */
|
||||
acl = posix_acl_from_xattr(&init_user_ns, value, size);
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
else if (acl) {
|
||||
retval = posix_acl_valid(acl);
|
||||
if (retval)
|
||||
goto err_out;
|
||||
}
|
||||
} else
|
||||
acl = NULL;
|
||||
|
||||
switch (handler->flags) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
if (acl) {
|
||||
struct iattr iattr;
|
||||
struct posix_acl *old_acl = acl;
|
||||
|
||||
retval = posix_acl_update_mode(inode, &iattr.ia_mode, &acl);
|
||||
if (retval)
|
||||
goto err_out;
|
||||
if (!acl) {
|
||||
/*
|
||||
* ACL can be represented
|
||||
* by the mode bits. So don't
|
||||
* update ACL.
|
||||
*/
|
||||
posix_acl_release(old_acl);
|
||||
value = NULL;
|
||||
size = 0;
|
||||
}
|
||||
iattr.ia_valid = ATTR_MODE;
|
||||
/* FIXME should we update ctime ?
|
||||
* What is the following setxattr update the
|
||||
* mode ?
|
||||
*/
|
||||
v9fs_vfs_setattr_dotl(dentry, &iattr);
|
||||
}
|
||||
break;
|
||||
case ACL_TYPE_DEFAULT:
|
||||
if (!S_ISDIR(inode->i_mode)) {
|
||||
retval = acl ? -EINVAL : 0;
|
||||
goto err_out;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
retval = v9fs_xattr_set(dentry, handler->prefix, value, size, flags);
|
||||
if (!retval)
|
||||
set_cached_acl(inode, handler->flags, acl);
|
||||
err_out:
|
||||
posix_acl_release(acl);
|
||||
return retval;
|
||||
}
|
||||
|
||||
const struct xattr_handler v9fs_xattr_acl_access_handler = {
|
||||
.prefix = POSIX_ACL_XATTR_ACCESS,
|
||||
.flags = ACL_TYPE_ACCESS,
|
||||
.get = v9fs_xattr_get_acl,
|
||||
.set = v9fs_xattr_set_acl,
|
||||
};
|
||||
|
||||
const struct xattr_handler v9fs_xattr_acl_default_handler = {
|
||||
.prefix = POSIX_ACL_XATTR_DEFAULT,
|
||||
.flags = ACL_TYPE_DEFAULT,
|
||||
.get = v9fs_xattr_get_acl,
|
||||
.set = v9fs_xattr_set_acl,
|
||||
};
|
||||
55
addons/9p/src/4.4.180/acl.h
Normal file
55
addons/9p/src/4.4.180/acl.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright IBM Corporation, 2010
|
||||
* Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
#ifndef FS_9P_ACL_H
|
||||
#define FS_9P_ACL_H
|
||||
|
||||
#ifdef CONFIG_9P_FS_POSIX_ACL
|
||||
extern int v9fs_get_acl(struct inode *, struct p9_fid *);
|
||||
extern struct posix_acl *v9fs_iop_get_acl(struct inode *inode, int type);
|
||||
extern int v9fs_acl_chmod(struct inode *, struct p9_fid *);
|
||||
extern int v9fs_set_create_acl(struct inode *, struct p9_fid *,
|
||||
struct posix_acl *, struct posix_acl *);
|
||||
extern int v9fs_acl_mode(struct inode *dir, umode_t *modep,
|
||||
struct posix_acl **dpacl, struct posix_acl **pacl);
|
||||
extern void v9fs_put_acl(struct posix_acl *dacl, struct posix_acl *acl);
|
||||
#else
|
||||
#define v9fs_iop_get_acl NULL
|
||||
static inline int v9fs_get_acl(struct inode *inode, struct p9_fid *fid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int v9fs_acl_chmod(struct inode *inode, struct p9_fid *fid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int v9fs_set_create_acl(struct inode *inode,
|
||||
struct p9_fid *fid,
|
||||
struct posix_acl *dacl,
|
||||
struct posix_acl *acl)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void v9fs_put_acl(struct posix_acl *dacl,
|
||||
struct posix_acl *acl)
|
||||
{
|
||||
}
|
||||
static inline int v9fs_acl_mode(struct inode *dir, umode_t *modep,
|
||||
struct posix_acl **dpacl,
|
||||
struct posix_acl **pacl)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* FS_9P_XATTR_H */
|
||||
414
addons/9p/src/4.4.180/cache.c
Normal file
414
addons/9p/src/4.4.180/cache.c
Normal file
@@ -0,0 +1,414 @@
|
||||
/*
|
||||
* V9FS cache definitions.
|
||||
*
|
||||
* Copyright (C) 2009 by Abhishek Kulkarni <adkulkar@umail.iu.edu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/fs.h>
|
||||
#include <net/9p/9p.h>
|
||||
|
||||
#include "v9fs.h"
|
||||
#include "cache.h"
|
||||
|
||||
#define CACHETAG_LEN 11
|
||||
|
||||
struct fscache_netfs v9fs_cache_netfs = {
|
||||
.name = "9p",
|
||||
.version = 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* v9fs_random_cachetag - Generate a random tag to be associated
|
||||
* with a new cache session.
|
||||
*
|
||||
* The value of jiffies is used for a fairly randomly cache tag.
|
||||
*/
|
||||
|
||||
static
|
||||
int v9fs_random_cachetag(struct v9fs_session_info *v9ses)
|
||||
{
|
||||
v9ses->cachetag = kmalloc(CACHETAG_LEN, GFP_KERNEL);
|
||||
if (!v9ses->cachetag)
|
||||
return -ENOMEM;
|
||||
|
||||
return scnprintf(v9ses->cachetag, CACHETAG_LEN, "%lu", jiffies);
|
||||
}
|
||||
|
||||
static uint16_t v9fs_cache_session_get_key(const void *cookie_netfs_data,
|
||||
void *buffer, uint16_t bufmax)
|
||||
{
|
||||
struct v9fs_session_info *v9ses;
|
||||
uint16_t klen = 0;
|
||||
|
||||
v9ses = (struct v9fs_session_info *)cookie_netfs_data;
|
||||
p9_debug(P9_DEBUG_FSC, "session %p buf %p size %u\n",
|
||||
v9ses, buffer, bufmax);
|
||||
|
||||
if (v9ses->cachetag)
|
||||
klen = strlen(v9ses->cachetag);
|
||||
|
||||
if (klen > bufmax)
|
||||
return 0;
|
||||
|
||||
memcpy(buffer, v9ses->cachetag, klen);
|
||||
p9_debug(P9_DEBUG_FSC, "cache session tag %s\n", v9ses->cachetag);
|
||||
return klen;
|
||||
}
|
||||
|
||||
const struct fscache_cookie_def v9fs_cache_session_index_def = {
|
||||
.name = "9P.session",
|
||||
.type = FSCACHE_COOKIE_TYPE_INDEX,
|
||||
.get_key = v9fs_cache_session_get_key,
|
||||
};
|
||||
|
||||
void v9fs_cache_session_get_cookie(struct v9fs_session_info *v9ses)
|
||||
{
|
||||
/* If no cache session tag was specified, we generate a random one. */
|
||||
if (!v9ses->cachetag)
|
||||
v9fs_random_cachetag(v9ses);
|
||||
|
||||
v9ses->fscache = fscache_acquire_cookie(v9fs_cache_netfs.primary_index,
|
||||
&v9fs_cache_session_index_def,
|
||||
v9ses, true);
|
||||
p9_debug(P9_DEBUG_FSC, "session %p get cookie %p\n",
|
||||
v9ses, v9ses->fscache);
|
||||
}
|
||||
|
||||
void v9fs_cache_session_put_cookie(struct v9fs_session_info *v9ses)
|
||||
{
|
||||
p9_debug(P9_DEBUG_FSC, "session %p put cookie %p\n",
|
||||
v9ses, v9ses->fscache);
|
||||
fscache_relinquish_cookie(v9ses->fscache, 0);
|
||||
v9ses->fscache = NULL;
|
||||
}
|
||||
|
||||
|
||||
static uint16_t v9fs_cache_inode_get_key(const void *cookie_netfs_data,
|
||||
void *buffer, uint16_t bufmax)
|
||||
{
|
||||
const struct v9fs_inode *v9inode = cookie_netfs_data;
|
||||
memcpy(buffer, &v9inode->qid.path, sizeof(v9inode->qid.path));
|
||||
p9_debug(P9_DEBUG_FSC, "inode %p get key %llu\n",
|
||||
&v9inode->vfs_inode, v9inode->qid.path);
|
||||
return sizeof(v9inode->qid.path);
|
||||
}
|
||||
|
||||
static void v9fs_cache_inode_get_attr(const void *cookie_netfs_data,
|
||||
uint64_t *size)
|
||||
{
|
||||
const struct v9fs_inode *v9inode = cookie_netfs_data;
|
||||
*size = i_size_read(&v9inode->vfs_inode);
|
||||
|
||||
p9_debug(P9_DEBUG_FSC, "inode %p get attr %llu\n",
|
||||
&v9inode->vfs_inode, *size);
|
||||
}
|
||||
|
||||
static uint16_t v9fs_cache_inode_get_aux(const void *cookie_netfs_data,
|
||||
void *buffer, uint16_t buflen)
|
||||
{
|
||||
const struct v9fs_inode *v9inode = cookie_netfs_data;
|
||||
memcpy(buffer, &v9inode->qid.version, sizeof(v9inode->qid.version));
|
||||
p9_debug(P9_DEBUG_FSC, "inode %p get aux %u\n",
|
||||
&v9inode->vfs_inode, v9inode->qid.version);
|
||||
return sizeof(v9inode->qid.version);
|
||||
}
|
||||
|
||||
static enum
|
||||
fscache_checkaux v9fs_cache_inode_check_aux(void *cookie_netfs_data,
|
||||
const void *buffer,
|
||||
uint16_t buflen)
|
||||
{
|
||||
const struct v9fs_inode *v9inode = cookie_netfs_data;
|
||||
|
||||
if (buflen != sizeof(v9inode->qid.version))
|
||||
return FSCACHE_CHECKAUX_OBSOLETE;
|
||||
|
||||
if (memcmp(buffer, &v9inode->qid.version,
|
||||
sizeof(v9inode->qid.version)))
|
||||
return FSCACHE_CHECKAUX_OBSOLETE;
|
||||
|
||||
return FSCACHE_CHECKAUX_OKAY;
|
||||
}
|
||||
|
||||
static void v9fs_cache_inode_now_uncached(void *cookie_netfs_data)
|
||||
{
|
||||
struct v9fs_inode *v9inode = cookie_netfs_data;
|
||||
struct pagevec pvec;
|
||||
pgoff_t first;
|
||||
int loop, nr_pages;
|
||||
|
||||
pagevec_init(&pvec, 0);
|
||||
first = 0;
|
||||
|
||||
for (;;) {
|
||||
nr_pages = pagevec_lookup(&pvec, v9inode->vfs_inode.i_mapping,
|
||||
first,
|
||||
PAGEVEC_SIZE - pagevec_count(&pvec));
|
||||
if (!nr_pages)
|
||||
break;
|
||||
|
||||
for (loop = 0; loop < nr_pages; loop++)
|
||||
ClearPageFsCache(pvec.pages[loop]);
|
||||
|
||||
first = pvec.pages[nr_pages - 1]->index + 1;
|
||||
|
||||
pvec.nr = nr_pages;
|
||||
pagevec_release(&pvec);
|
||||
cond_resched();
|
||||
}
|
||||
}
|
||||
|
||||
const struct fscache_cookie_def v9fs_cache_inode_index_def = {
|
||||
.name = "9p.inode",
|
||||
.type = FSCACHE_COOKIE_TYPE_DATAFILE,
|
||||
.get_key = v9fs_cache_inode_get_key,
|
||||
.get_attr = v9fs_cache_inode_get_attr,
|
||||
.get_aux = v9fs_cache_inode_get_aux,
|
||||
.check_aux = v9fs_cache_inode_check_aux,
|
||||
.now_uncached = v9fs_cache_inode_now_uncached,
|
||||
};
|
||||
|
||||
void v9fs_cache_inode_get_cookie(struct inode *inode)
|
||||
{
|
||||
struct v9fs_inode *v9inode;
|
||||
struct v9fs_session_info *v9ses;
|
||||
|
||||
if (!S_ISREG(inode->i_mode))
|
||||
return;
|
||||
|
||||
v9inode = V9FS_I(inode);
|
||||
if (v9inode->fscache)
|
||||
return;
|
||||
|
||||
v9ses = v9fs_inode2v9ses(inode);
|
||||
v9inode->fscache = fscache_acquire_cookie(v9ses->fscache,
|
||||
&v9fs_cache_inode_index_def,
|
||||
v9inode, true);
|
||||
|
||||
p9_debug(P9_DEBUG_FSC, "inode %p get cookie %p\n",
|
||||
inode, v9inode->fscache);
|
||||
}
|
||||
|
||||
void v9fs_cache_inode_put_cookie(struct inode *inode)
|
||||
{
|
||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
|
||||
if (!v9inode->fscache)
|
||||
return;
|
||||
p9_debug(P9_DEBUG_FSC, "inode %p put cookie %p\n",
|
||||
inode, v9inode->fscache);
|
||||
|
||||
fscache_relinquish_cookie(v9inode->fscache, 0);
|
||||
v9inode->fscache = NULL;
|
||||
}
|
||||
|
||||
void v9fs_cache_inode_flush_cookie(struct inode *inode)
|
||||
{
|
||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
|
||||
if (!v9inode->fscache)
|
||||
return;
|
||||
p9_debug(P9_DEBUG_FSC, "inode %p flush cookie %p\n",
|
||||
inode, v9inode->fscache);
|
||||
|
||||
fscache_relinquish_cookie(v9inode->fscache, 1);
|
||||
v9inode->fscache = NULL;
|
||||
}
|
||||
|
||||
void v9fs_cache_inode_set_cookie(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
|
||||
if (!v9inode->fscache)
|
||||
return;
|
||||
|
||||
mutex_lock(&v9inode->fscache_lock);
|
||||
|
||||
if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
|
||||
v9fs_cache_inode_flush_cookie(inode);
|
||||
else
|
||||
v9fs_cache_inode_get_cookie(inode);
|
||||
|
||||
mutex_unlock(&v9inode->fscache_lock);
|
||||
}
|
||||
|
||||
void v9fs_cache_inode_reset_cookie(struct inode *inode)
|
||||
{
|
||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
struct v9fs_session_info *v9ses;
|
||||
struct fscache_cookie *old;
|
||||
|
||||
if (!v9inode->fscache)
|
||||
return;
|
||||
|
||||
old = v9inode->fscache;
|
||||
|
||||
mutex_lock(&v9inode->fscache_lock);
|
||||
fscache_relinquish_cookie(v9inode->fscache, 1);
|
||||
|
||||
v9ses = v9fs_inode2v9ses(inode);
|
||||
v9inode->fscache = fscache_acquire_cookie(v9ses->fscache,
|
||||
&v9fs_cache_inode_index_def,
|
||||
v9inode, true);
|
||||
p9_debug(P9_DEBUG_FSC, "inode %p revalidating cookie old %p new %p\n",
|
||||
inode, old, v9inode->fscache);
|
||||
|
||||
mutex_unlock(&v9inode->fscache_lock);
|
||||
}
|
||||
|
||||
int __v9fs_fscache_release_page(struct page *page, gfp_t gfp)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
|
||||
BUG_ON(!v9inode->fscache);
|
||||
|
||||
return fscache_maybe_release_page(v9inode->fscache, page, gfp);
|
||||
}
|
||||
|
||||
void __v9fs_fscache_invalidate_page(struct page *page)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
|
||||
BUG_ON(!v9inode->fscache);
|
||||
|
||||
if (PageFsCache(page)) {
|
||||
fscache_wait_on_page_write(v9inode->fscache, page);
|
||||
BUG_ON(!PageLocked(page));
|
||||
fscache_uncache_page(v9inode->fscache, page);
|
||||
}
|
||||
}
|
||||
|
||||
static void v9fs_vfs_readpage_complete(struct page *page, void *data,
|
||||
int error)
|
||||
{
|
||||
if (!error)
|
||||
SetPageUptodate(page);
|
||||
|
||||
unlock_page(page);
|
||||
}
|
||||
|
||||
/**
|
||||
* __v9fs_readpage_from_fscache - read a page from cache
|
||||
*
|
||||
* Returns 0 if the pages are in cache and a BIO is submitted,
|
||||
* 1 if the pages are not in cache and -error otherwise.
|
||||
*/
|
||||
|
||||
int __v9fs_readpage_from_fscache(struct inode *inode, struct page *page)
|
||||
{
|
||||
int ret;
|
||||
const struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
|
||||
p9_debug(P9_DEBUG_FSC, "inode %p page %p\n", inode, page);
|
||||
if (!v9inode->fscache)
|
||||
return -ENOBUFS;
|
||||
|
||||
ret = fscache_read_or_alloc_page(v9inode->fscache,
|
||||
page,
|
||||
v9fs_vfs_readpage_complete,
|
||||
NULL,
|
||||
GFP_KERNEL);
|
||||
switch (ret) {
|
||||
case -ENOBUFS:
|
||||
case -ENODATA:
|
||||
p9_debug(P9_DEBUG_FSC, "page/inode not in cache %d\n", ret);
|
||||
return 1;
|
||||
case 0:
|
||||
p9_debug(P9_DEBUG_FSC, "BIO submitted\n");
|
||||
return ret;
|
||||
default:
|
||||
p9_debug(P9_DEBUG_FSC, "ret %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* __v9fs_readpages_from_fscache - read multiple pages from cache
|
||||
*
|
||||
* Returns 0 if the pages are in cache and a BIO is submitted,
|
||||
* 1 if the pages are not in cache and -error otherwise.
|
||||
*/
|
||||
|
||||
int __v9fs_readpages_from_fscache(struct inode *inode,
|
||||
struct address_space *mapping,
|
||||
struct list_head *pages,
|
||||
unsigned *nr_pages)
|
||||
{
|
||||
int ret;
|
||||
const struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
|
||||
p9_debug(P9_DEBUG_FSC, "inode %p pages %u\n", inode, *nr_pages);
|
||||
if (!v9inode->fscache)
|
||||
return -ENOBUFS;
|
||||
|
||||
ret = fscache_read_or_alloc_pages(v9inode->fscache,
|
||||
mapping, pages, nr_pages,
|
||||
v9fs_vfs_readpage_complete,
|
||||
NULL,
|
||||
mapping_gfp_mask(mapping));
|
||||
switch (ret) {
|
||||
case -ENOBUFS:
|
||||
case -ENODATA:
|
||||
p9_debug(P9_DEBUG_FSC, "pages/inodes not in cache %d\n", ret);
|
||||
return 1;
|
||||
case 0:
|
||||
BUG_ON(!list_empty(pages));
|
||||
BUG_ON(*nr_pages != 0);
|
||||
p9_debug(P9_DEBUG_FSC, "BIO submitted\n");
|
||||
return ret;
|
||||
default:
|
||||
p9_debug(P9_DEBUG_FSC, "ret %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* __v9fs_readpage_to_fscache - write a page to the cache
|
||||
*
|
||||
*/
|
||||
|
||||
void __v9fs_readpage_to_fscache(struct inode *inode, struct page *page)
|
||||
{
|
||||
int ret;
|
||||
const struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
|
||||
p9_debug(P9_DEBUG_FSC, "inode %p page %p\n", inode, page);
|
||||
ret = fscache_write_page(v9inode->fscache, page, GFP_KERNEL);
|
||||
p9_debug(P9_DEBUG_FSC, "ret = %d\n", ret);
|
||||
if (ret != 0)
|
||||
v9fs_uncache_page(inode, page);
|
||||
}
|
||||
|
||||
/*
|
||||
* wait for a page to complete writing to the cache
|
||||
*/
|
||||
void __v9fs_fscache_wait_on_page_write(struct inode *inode, struct page *page)
|
||||
{
|
||||
const struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
p9_debug(P9_DEBUG_FSC, "inode %p page %p\n", inode, page);
|
||||
if (PageFsCache(page))
|
||||
fscache_wait_on_page_write(v9inode->fscache, page);
|
||||
}
|
||||
152
addons/9p/src/4.4.180/cache.h
Normal file
152
addons/9p/src/4.4.180/cache.h
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* V9FS cache definitions.
|
||||
*
|
||||
* Copyright (C) 2009 by Abhishek Kulkarni <adkulkar@umail.iu.edu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _9P_CACHE_H
|
||||
#define _9P_CACHE_H
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
#include <linux/fscache.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
extern struct fscache_netfs v9fs_cache_netfs;
|
||||
extern const struct fscache_cookie_def v9fs_cache_session_index_def;
|
||||
extern const struct fscache_cookie_def v9fs_cache_inode_index_def;
|
||||
|
||||
extern void v9fs_cache_session_get_cookie(struct v9fs_session_info *v9ses);
|
||||
extern void v9fs_cache_session_put_cookie(struct v9fs_session_info *v9ses);
|
||||
|
||||
extern void v9fs_cache_inode_get_cookie(struct inode *inode);
|
||||
extern void v9fs_cache_inode_put_cookie(struct inode *inode);
|
||||
extern void v9fs_cache_inode_flush_cookie(struct inode *inode);
|
||||
extern void v9fs_cache_inode_set_cookie(struct inode *inode, struct file *filp);
|
||||
extern void v9fs_cache_inode_reset_cookie(struct inode *inode);
|
||||
|
||||
extern int __v9fs_cache_register(void);
|
||||
extern void __v9fs_cache_unregister(void);
|
||||
|
||||
extern int __v9fs_fscache_release_page(struct page *page, gfp_t gfp);
|
||||
extern void __v9fs_fscache_invalidate_page(struct page *page);
|
||||
extern int __v9fs_readpage_from_fscache(struct inode *inode,
|
||||
struct page *page);
|
||||
extern int __v9fs_readpages_from_fscache(struct inode *inode,
|
||||
struct address_space *mapping,
|
||||
struct list_head *pages,
|
||||
unsigned *nr_pages);
|
||||
extern void __v9fs_readpage_to_fscache(struct inode *inode, struct page *page);
|
||||
extern void __v9fs_fscache_wait_on_page_write(struct inode *inode,
|
||||
struct page *page);
|
||||
|
||||
static inline int v9fs_fscache_release_page(struct page *page,
|
||||
gfp_t gfp)
|
||||
{
|
||||
return __v9fs_fscache_release_page(page, gfp);
|
||||
}
|
||||
|
||||
static inline void v9fs_fscache_invalidate_page(struct page *page)
|
||||
{
|
||||
__v9fs_fscache_invalidate_page(page);
|
||||
}
|
||||
|
||||
static inline int v9fs_readpage_from_fscache(struct inode *inode,
|
||||
struct page *page)
|
||||
{
|
||||
return __v9fs_readpage_from_fscache(inode, page);
|
||||
}
|
||||
|
||||
static inline int v9fs_readpages_from_fscache(struct inode *inode,
|
||||
struct address_space *mapping,
|
||||
struct list_head *pages,
|
||||
unsigned *nr_pages)
|
||||
{
|
||||
return __v9fs_readpages_from_fscache(inode, mapping, pages,
|
||||
nr_pages);
|
||||
}
|
||||
|
||||
static inline void v9fs_readpage_to_fscache(struct inode *inode,
|
||||
struct page *page)
|
||||
{
|
||||
if (PageFsCache(page))
|
||||
__v9fs_readpage_to_fscache(inode, page);
|
||||
}
|
||||
|
||||
static inline void v9fs_uncache_page(struct inode *inode, struct page *page)
|
||||
{
|
||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
fscache_uncache_page(v9inode->fscache, page);
|
||||
BUG_ON(PageFsCache(page));
|
||||
}
|
||||
|
||||
static inline void v9fs_fscache_wait_on_page_write(struct inode *inode,
|
||||
struct page *page)
|
||||
{
|
||||
return __v9fs_fscache_wait_on_page_write(inode, page);
|
||||
}
|
||||
|
||||
#else /* CONFIG_9P_FSCACHE */
|
||||
|
||||
static inline void v9fs_cache_inode_get_cookie(struct inode *inode)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void v9fs_cache_inode_put_cookie(struct inode *inode)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void v9fs_cache_inode_set_cookie(struct inode *inode, struct file *file)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int v9fs_fscache_release_page(struct page *page,
|
||||
gfp_t gfp) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline void v9fs_fscache_invalidate_page(struct page *page) {}
|
||||
|
||||
static inline int v9fs_readpage_from_fscache(struct inode *inode,
|
||||
struct page *page)
|
||||
{
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
static inline int v9fs_readpages_from_fscache(struct inode *inode,
|
||||
struct address_space *mapping,
|
||||
struct list_head *pages,
|
||||
unsigned *nr_pages)
|
||||
{
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
static inline void v9fs_readpage_to_fscache(struct inode *inode,
|
||||
struct page *page)
|
||||
{}
|
||||
|
||||
static inline void v9fs_uncache_page(struct inode *inode, struct page *page)
|
||||
{}
|
||||
|
||||
static inline void v9fs_fscache_wait_on_page_write(struct inode *inode,
|
||||
struct page *page)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_9P_FSCACHE */
|
||||
#endif /* _9P_CACHE_H */
|
||||
2297
addons/9p/src/4.4.180/client.c
Normal file
2297
addons/9p/src/4.4.180/client.c
Normal file
File diff suppressed because it is too large
Load Diff
247
addons/9p/src/4.4.180/error.c
Normal file
247
addons/9p/src/4.4.180/error.c
Normal file
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* linux/fs/9p/error.c
|
||||
*
|
||||
* Error string handling
|
||||
*
|
||||
* Plan 9 uses error strings, Unix uses error numbers. These functions
|
||||
* try to help manage that and provide for dynamically adding error
|
||||
* mappings.
|
||||
*
|
||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/jhash.h>
|
||||
#include <linux/errno.h>
|
||||
#include <net/9p/9p.h>
|
||||
|
||||
/**
|
||||
* struct errormap - map string errors from Plan 9 to Linux numeric ids
|
||||
* @name: string sent over 9P
|
||||
* @val: numeric id most closely representing @name
|
||||
* @namelen: length of string
|
||||
* @list: hash-table list for string lookup
|
||||
*/
|
||||
struct errormap {
|
||||
char *name;
|
||||
int val;
|
||||
|
||||
int namelen;
|
||||
struct hlist_node list;
|
||||
};
|
||||
|
||||
#define ERRHASHSZ 32
|
||||
static struct hlist_head hash_errmap[ERRHASHSZ];
|
||||
|
||||
/* FixMe - reduce to a reasonable size */
|
||||
static struct errormap errmap[] = {
|
||||
{"Operation not permitted", EPERM},
|
||||
{"wstat prohibited", EPERM},
|
||||
{"No such file or directory", ENOENT},
|
||||
{"directory entry not found", ENOENT},
|
||||
{"file not found", ENOENT},
|
||||
{"Interrupted system call", EINTR},
|
||||
{"Input/output error", EIO},
|
||||
{"No such device or address", ENXIO},
|
||||
{"Argument list too long", E2BIG},
|
||||
{"Bad file descriptor", EBADF},
|
||||
{"Resource temporarily unavailable", EAGAIN},
|
||||
{"Cannot allocate memory", ENOMEM},
|
||||
{"Permission denied", EACCES},
|
||||
{"Bad address", EFAULT},
|
||||
{"Block device required", ENOTBLK},
|
||||
{"Device or resource busy", EBUSY},
|
||||
{"File exists", EEXIST},
|
||||
{"Invalid cross-device link", EXDEV},
|
||||
{"No such device", ENODEV},
|
||||
{"Not a directory", ENOTDIR},
|
||||
{"Is a directory", EISDIR},
|
||||
{"Invalid argument", EINVAL},
|
||||
{"Too many open files in system", ENFILE},
|
||||
{"Too many open files", EMFILE},
|
||||
{"Text file busy", ETXTBSY},
|
||||
{"File too large", EFBIG},
|
||||
{"No space left on device", ENOSPC},
|
||||
{"Illegal seek", ESPIPE},
|
||||
{"Read-only file system", EROFS},
|
||||
{"Too many links", EMLINK},
|
||||
{"Broken pipe", EPIPE},
|
||||
{"Numerical argument out of domain", EDOM},
|
||||
{"Numerical result out of range", ERANGE},
|
||||
{"Resource deadlock avoided", EDEADLK},
|
||||
{"File name too long", ENAMETOOLONG},
|
||||
{"No locks available", ENOLCK},
|
||||
{"Function not implemented", ENOSYS},
|
||||
{"Directory not empty", ENOTEMPTY},
|
||||
{"Too many levels of symbolic links", ELOOP},
|
||||
{"No message of desired type", ENOMSG},
|
||||
{"Identifier removed", EIDRM},
|
||||
{"No data available", ENODATA},
|
||||
{"Machine is not on the network", ENONET},
|
||||
{"Package not installed", ENOPKG},
|
||||
{"Object is remote", EREMOTE},
|
||||
{"Link has been severed", ENOLINK},
|
||||
{"Communication error on send", ECOMM},
|
||||
{"Protocol error", EPROTO},
|
||||
{"Bad message", EBADMSG},
|
||||
{"File descriptor in bad state", EBADFD},
|
||||
{"Streams pipe error", ESTRPIPE},
|
||||
{"Too many users", EUSERS},
|
||||
{"Socket operation on non-socket", ENOTSOCK},
|
||||
{"Message too long", EMSGSIZE},
|
||||
{"Protocol not available", ENOPROTOOPT},
|
||||
{"Protocol not supported", EPROTONOSUPPORT},
|
||||
{"Socket type not supported", ESOCKTNOSUPPORT},
|
||||
{"Operation not supported", EOPNOTSUPP},
|
||||
{"Protocol family not supported", EPFNOSUPPORT},
|
||||
{"Network is down", ENETDOWN},
|
||||
{"Network is unreachable", ENETUNREACH},
|
||||
{"Network dropped connection on reset", ENETRESET},
|
||||
{"Software caused connection abort", ECONNABORTED},
|
||||
{"Connection reset by peer", ECONNRESET},
|
||||
{"No buffer space available", ENOBUFS},
|
||||
{"Transport endpoint is already connected", EISCONN},
|
||||
{"Transport endpoint is not connected", ENOTCONN},
|
||||
{"Cannot send after transport endpoint shutdown", ESHUTDOWN},
|
||||
{"Connection timed out", ETIMEDOUT},
|
||||
{"Connection refused", ECONNREFUSED},
|
||||
{"Host is down", EHOSTDOWN},
|
||||
{"No route to host", EHOSTUNREACH},
|
||||
{"Operation already in progress", EALREADY},
|
||||
{"Operation now in progress", EINPROGRESS},
|
||||
{"Is a named type file", EISNAM},
|
||||
{"Remote I/O error", EREMOTEIO},
|
||||
{"Disk quota exceeded", EDQUOT},
|
||||
/* errors from fossil, vacfs, and u9fs */
|
||||
{"fid unknown or out of range", EBADF},
|
||||
{"permission denied", EACCES},
|
||||
{"file does not exist", ENOENT},
|
||||
{"authentication failed", ECONNREFUSED},
|
||||
{"bad offset in directory read", ESPIPE},
|
||||
{"bad use of fid", EBADF},
|
||||
{"wstat can't convert between files and directories", EPERM},
|
||||
{"directory is not empty", ENOTEMPTY},
|
||||
{"file exists", EEXIST},
|
||||
{"file already exists", EEXIST},
|
||||
{"file or directory already exists", EEXIST},
|
||||
{"fid already in use", EBADF},
|
||||
{"file in use", ETXTBSY},
|
||||
{"i/o error", EIO},
|
||||
{"file already open for I/O", ETXTBSY},
|
||||
{"illegal mode", EINVAL},
|
||||
{"illegal name", ENAMETOOLONG},
|
||||
{"not a directory", ENOTDIR},
|
||||
{"not a member of proposed group", EPERM},
|
||||
{"not owner", EACCES},
|
||||
{"only owner can change group in wstat", EACCES},
|
||||
{"read only file system", EROFS},
|
||||
{"no access to special file", EPERM},
|
||||
{"i/o count too large", EIO},
|
||||
{"unknown group", EINVAL},
|
||||
{"unknown user", EINVAL},
|
||||
{"bogus wstat buffer", EPROTO},
|
||||
{"exclusive use file already open", EAGAIN},
|
||||
{"corrupted directory entry", EIO},
|
||||
{"corrupted file entry", EIO},
|
||||
{"corrupted block label", EIO},
|
||||
{"corrupted meta data", EIO},
|
||||
{"illegal offset", EINVAL},
|
||||
{"illegal path element", ENOENT},
|
||||
{"root of file system is corrupted", EIO},
|
||||
{"corrupted super block", EIO},
|
||||
{"protocol botch", EPROTO},
|
||||
{"file system is full", ENOSPC},
|
||||
{"file is in use", EAGAIN},
|
||||
{"directory entry is not allocated", ENOENT},
|
||||
{"file is read only", EROFS},
|
||||
{"file has been removed", EIDRM},
|
||||
{"only support truncation to zero length", EPERM},
|
||||
{"cannot remove root", EPERM},
|
||||
{"file too big", EFBIG},
|
||||
{"venti i/o error", EIO},
|
||||
/* these are not errors */
|
||||
{"u9fs rhostsauth: no authentication required", 0},
|
||||
{"u9fs authnone: no authentication required", 0},
|
||||
{NULL, -1}
|
||||
};
|
||||
|
||||
/**
|
||||
* p9_error_init - preload mappings into hash list
|
||||
*
|
||||
*/
|
||||
|
||||
int p9_error_init(void)
|
||||
{
|
||||
struct errormap *c;
|
||||
int bucket;
|
||||
|
||||
/* initialize hash table */
|
||||
for (bucket = 0; bucket < ERRHASHSZ; bucket++)
|
||||
INIT_HLIST_HEAD(&hash_errmap[bucket]);
|
||||
|
||||
/* load initial error map into hash table */
|
||||
for (c = errmap; c->name != NULL; c++) {
|
||||
c->namelen = strlen(c->name);
|
||||
bucket = jhash(c->name, c->namelen, 0) % ERRHASHSZ;
|
||||
INIT_HLIST_NODE(&c->list);
|
||||
hlist_add_head(&c->list, &hash_errmap[bucket]);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL(p9_error_init);
|
||||
|
||||
/**
|
||||
* errstr2errno - convert error string to error number
|
||||
* @errstr: error string
|
||||
* @len: length of error string
|
||||
*
|
||||
*/
|
||||
|
||||
int p9_errstr2errno(char *errstr, int len)
|
||||
{
|
||||
int errno;
|
||||
struct errormap *c;
|
||||
int bucket;
|
||||
|
||||
errno = 0;
|
||||
c = NULL;
|
||||
bucket = jhash(errstr, len, 0) % ERRHASHSZ;
|
||||
hlist_for_each_entry(c, &hash_errmap[bucket], list) {
|
||||
if (c->namelen == len && !memcmp(c->name, errstr, len)) {
|
||||
errno = c->val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (errno == 0) {
|
||||
/* TODO: if error isn't found, add it dynamically */
|
||||
errstr[len] = 0;
|
||||
pr_err("%s: server reported unknown error %s\n",
|
||||
__func__, errstr);
|
||||
errno = ESERVERFAULT;
|
||||
}
|
||||
|
||||
return -errno;
|
||||
}
|
||||
EXPORT_SYMBOL(p9_errstr2errno);
|
||||
306
addons/9p/src/4.4.180/fid.c
Normal file
306
addons/9p/src/4.4.180/fid.c
Normal file
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* V9FS FID Management
|
||||
*
|
||||
* Copyright (C) 2007 by Latchesar Ionkov <lucho@ionkov.net>
|
||||
* Copyright (C) 2005, 2006 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/idr.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
|
||||
#include "v9fs.h"
|
||||
#include "v9fs_vfs.h"
|
||||
#include "fid.h"
|
||||
|
||||
/**
|
||||
* v9fs_fid_add - add a fid to a dentry
|
||||
* @dentry: dentry that the fid is being added to
|
||||
* @fid: fid to add
|
||||
*
|
||||
*/
|
||||
|
||||
static inline void __add_fid(struct dentry *dentry, struct p9_fid *fid)
|
||||
{
|
||||
hlist_add_head(&fid->dlist, (struct hlist_head *)&dentry->d_fsdata);
|
||||
}
|
||||
|
||||
void v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid)
|
||||
{
|
||||
spin_lock(&dentry->d_lock);
|
||||
__add_fid(dentry, fid);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_fid_find - retrieve a fid that belongs to the specified uid
|
||||
* @dentry: dentry to look for fid in
|
||||
* @uid: return fid that belongs to the specified user
|
||||
* @any: if non-zero, return any fid associated with the dentry
|
||||
*
|
||||
*/
|
||||
|
||||
static struct p9_fid *v9fs_fid_find(struct dentry *dentry, kuid_t uid, int any)
|
||||
{
|
||||
struct p9_fid *fid, *ret;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, " dentry: %pd (%p) uid %d any %d\n",
|
||||
dentry, dentry, from_kuid(&init_user_ns, uid),
|
||||
any);
|
||||
ret = NULL;
|
||||
/* we'll recheck under lock if there's anything to look in */
|
||||
if (dentry->d_fsdata) {
|
||||
struct hlist_head *h = (struct hlist_head *)&dentry->d_fsdata;
|
||||
spin_lock(&dentry->d_lock);
|
||||
hlist_for_each_entry(fid, h, dlist) {
|
||||
if (any || uid_eq(fid->uid, uid)) {
|
||||
ret = fid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to hold v9ses->rename_sem as long as we hold references
|
||||
* to returned path array. Array element contain pointers to
|
||||
* dentry names.
|
||||
*/
|
||||
static int build_path_from_dentry(struct v9fs_session_info *v9ses,
|
||||
struct dentry *dentry, char ***names)
|
||||
{
|
||||
int n = 0, i;
|
||||
char **wnames;
|
||||
struct dentry *ds;
|
||||
|
||||
for (ds = dentry; !IS_ROOT(ds); ds = ds->d_parent)
|
||||
n++;
|
||||
|
||||
wnames = kmalloc(sizeof(char *) * n, GFP_KERNEL);
|
||||
if (!wnames)
|
||||
goto err_out;
|
||||
|
||||
for (ds = dentry, i = (n-1); i >= 0; i--, ds = ds->d_parent)
|
||||
wnames[i] = (char *)ds->d_name.name;
|
||||
|
||||
*names = wnames;
|
||||
return n;
|
||||
err_out:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry,
|
||||
kuid_t uid, int any)
|
||||
{
|
||||
struct dentry *ds;
|
||||
char **wnames, *uname;
|
||||
int i, n, l, clone, access;
|
||||
struct v9fs_session_info *v9ses;
|
||||
struct p9_fid *fid, *old_fid = NULL;
|
||||
|
||||
v9ses = v9fs_dentry2v9ses(dentry);
|
||||
access = v9ses->flags & V9FS_ACCESS_MASK;
|
||||
fid = v9fs_fid_find(dentry, uid, any);
|
||||
if (fid)
|
||||
return fid;
|
||||
/*
|
||||
* we don't have a matching fid. To do a TWALK we need
|
||||
* parent fid. We need to prevent rename when we want to
|
||||
* look at the parent.
|
||||
*/
|
||||
down_read(&v9ses->rename_sem);
|
||||
ds = dentry->d_parent;
|
||||
fid = v9fs_fid_find(ds, uid, any);
|
||||
if (fid) {
|
||||
/* Found the parent fid do a lookup with that */
|
||||
fid = p9_client_walk(fid, 1, (char **)&dentry->d_name.name, 1);
|
||||
goto fid_out;
|
||||
}
|
||||
up_read(&v9ses->rename_sem);
|
||||
|
||||
/* start from the root and try to do a lookup */
|
||||
fid = v9fs_fid_find(dentry->d_sb->s_root, uid, any);
|
||||
if (!fid) {
|
||||
/* the user is not attached to the fs yet */
|
||||
if (access == V9FS_ACCESS_SINGLE)
|
||||
return ERR_PTR(-EPERM);
|
||||
|
||||
if (v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses))
|
||||
uname = NULL;
|
||||
else
|
||||
uname = v9ses->uname;
|
||||
|
||||
fid = p9_client_attach(v9ses->clnt, NULL, uname, uid,
|
||||
v9ses->aname);
|
||||
if (IS_ERR(fid))
|
||||
return fid;
|
||||
|
||||
v9fs_fid_add(dentry->d_sb->s_root, fid);
|
||||
}
|
||||
/* If we are root ourself just return that */
|
||||
if (dentry->d_sb->s_root == dentry)
|
||||
return fid;
|
||||
/*
|
||||
* Do a multipath walk with attached root.
|
||||
* When walking parent we need to make sure we
|
||||
* don't have a parallel rename happening
|
||||
*/
|
||||
down_read(&v9ses->rename_sem);
|
||||
n = build_path_from_dentry(v9ses, dentry, &wnames);
|
||||
if (n < 0) {
|
||||
fid = ERR_PTR(n);
|
||||
goto err_out;
|
||||
}
|
||||
clone = 1;
|
||||
i = 0;
|
||||
while (i < n) {
|
||||
l = min(n - i, P9_MAXWELEM);
|
||||
/*
|
||||
* We need to hold rename lock when doing a multipath
|
||||
* walk to ensure none of the patch component change
|
||||
*/
|
||||
fid = p9_client_walk(fid, l, &wnames[i], clone);
|
||||
if (IS_ERR(fid)) {
|
||||
if (old_fid) {
|
||||
/*
|
||||
* If we fail, clunk fid which are mapping
|
||||
* to path component and not the last component
|
||||
* of the path.
|
||||
*/
|
||||
p9_client_clunk(old_fid);
|
||||
}
|
||||
kfree(wnames);
|
||||
goto err_out;
|
||||
}
|
||||
old_fid = fid;
|
||||
i += l;
|
||||
clone = 0;
|
||||
}
|
||||
kfree(wnames);
|
||||
fid_out:
|
||||
if (!IS_ERR(fid)) {
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (d_unhashed(dentry)) {
|
||||
spin_unlock(&dentry->d_lock);
|
||||
p9_client_clunk(fid);
|
||||
fid = ERR_PTR(-ENOENT);
|
||||
} else {
|
||||
__add_fid(dentry, fid);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
}
|
||||
err_out:
|
||||
up_read(&v9ses->rename_sem);
|
||||
return fid;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_fid_lookup - lookup for a fid, try to walk if not found
|
||||
* @dentry: dentry to look for fid in
|
||||
*
|
||||
* Look for a fid in the specified dentry for the current user.
|
||||
* If no fid is found, try to create one walking from a fid from the parent
|
||||
* dentry (if it has one), or the root dentry. If the user haven't accessed
|
||||
* the fs yet, attach now and walk from the root.
|
||||
*/
|
||||
|
||||
struct p9_fid *v9fs_fid_lookup(struct dentry *dentry)
|
||||
{
|
||||
kuid_t uid;
|
||||
int any, access;
|
||||
struct v9fs_session_info *v9ses;
|
||||
|
||||
v9ses = v9fs_dentry2v9ses(dentry);
|
||||
access = v9ses->flags & V9FS_ACCESS_MASK;
|
||||
switch (access) {
|
||||
case V9FS_ACCESS_SINGLE:
|
||||
case V9FS_ACCESS_USER:
|
||||
case V9FS_ACCESS_CLIENT:
|
||||
uid = current_fsuid();
|
||||
any = 0;
|
||||
break;
|
||||
|
||||
case V9FS_ACCESS_ANY:
|
||||
uid = v9ses->uid;
|
||||
any = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
uid = INVALID_UID;
|
||||
any = 0;
|
||||
break;
|
||||
}
|
||||
return v9fs_fid_lookup_with_uid(dentry, uid, any);
|
||||
}
|
||||
|
||||
struct p9_fid *v9fs_fid_clone(struct dentry *dentry)
|
||||
{
|
||||
struct p9_fid *fid, *ret;
|
||||
|
||||
fid = v9fs_fid_lookup(dentry);
|
||||
if (IS_ERR(fid))
|
||||
return fid;
|
||||
|
||||
ret = p9_client_walk(fid, 0, NULL, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct p9_fid *v9fs_fid_clone_with_uid(struct dentry *dentry, kuid_t uid)
|
||||
{
|
||||
struct p9_fid *fid, *ret;
|
||||
|
||||
fid = v9fs_fid_lookup_with_uid(dentry, uid, 0);
|
||||
if (IS_ERR(fid))
|
||||
return fid;
|
||||
|
||||
ret = p9_client_walk(fid, 0, NULL, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct p9_fid *v9fs_writeback_fid(struct dentry *dentry)
|
||||
{
|
||||
int err;
|
||||
struct p9_fid *fid;
|
||||
|
||||
fid = v9fs_fid_clone_with_uid(dentry, GLOBAL_ROOT_UID);
|
||||
if (IS_ERR(fid))
|
||||
goto error_out;
|
||||
/*
|
||||
* writeback fid will only be used to write back the
|
||||
* dirty pages. We always request for the open fid in read-write
|
||||
* mode so that a partial page write which result in page
|
||||
* read can work.
|
||||
*/
|
||||
err = p9_client_open(fid, O_RDWR);
|
||||
if (err < 0) {
|
||||
p9_client_clunk(fid);
|
||||
fid = ERR_PTR(err);
|
||||
goto error_out;
|
||||
}
|
||||
error_out:
|
||||
return fid;
|
||||
}
|
||||
30
addons/9p/src/4.4.180/fid.h
Normal file
30
addons/9p/src/4.4.180/fid.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* V9FS FID Management
|
||||
*
|
||||
* Copyright (C) 2005 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
#ifndef FS_9P_FID_H
|
||||
#define FS_9P_FID_H
|
||||
#include <linux/list.h>
|
||||
|
||||
struct p9_fid *v9fs_fid_lookup(struct dentry *dentry);
|
||||
struct p9_fid *v9fs_fid_clone(struct dentry *dentry);
|
||||
void v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid);
|
||||
struct p9_fid *v9fs_writeback_fid(struct dentry *dentry);
|
||||
#endif
|
||||
201
addons/9p/src/4.4.180/mod.c
Normal file
201
addons/9p/src/4.4.180/mod.c
Normal file
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* net/9p/9p.c
|
||||
*
|
||||
* 9P entry point
|
||||
*
|
||||
* Copyright (C) 2007 by Latchesar Ionkov <lucho@ionkov.net>
|
||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/parser.h>
|
||||
#include <net/9p/client.h>
|
||||
#include <net/9p/transport.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#ifdef CONFIG_NET_9P_DEBUG
|
||||
unsigned int p9_debug_level = 0; /* feature-rific global debug level */
|
||||
EXPORT_SYMBOL(p9_debug_level);
|
||||
module_param_named(debug, p9_debug_level, uint, 0);
|
||||
MODULE_PARM_DESC(debug, "9P debugging level");
|
||||
|
||||
void _p9_debug(enum p9_debug_flags level, const char *func,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
struct va_format vaf;
|
||||
va_list args;
|
||||
|
||||
if ((p9_debug_level & level) != level)
|
||||
return;
|
||||
|
||||
va_start(args, fmt);
|
||||
|
||||
vaf.fmt = fmt;
|
||||
vaf.va = &args;
|
||||
|
||||
if (level == P9_DEBUG_9P)
|
||||
pr_notice("(%8.8d) %pV", task_pid_nr(current), &vaf);
|
||||
else
|
||||
pr_notice("-- %s (%d): %pV", func, task_pid_nr(current), &vaf);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
EXPORT_SYMBOL(_p9_debug);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Dynamic Transport Registration Routines
|
||||
*
|
||||
*/
|
||||
|
||||
static DEFINE_SPINLOCK(v9fs_trans_lock);
|
||||
static LIST_HEAD(v9fs_trans_list);
|
||||
|
||||
/**
|
||||
* v9fs_register_trans - register a new transport with 9p
|
||||
* @m: structure describing the transport module and entry points
|
||||
*
|
||||
*/
|
||||
void v9fs_register_trans(struct p9_trans_module *m)
|
||||
{
|
||||
spin_lock(&v9fs_trans_lock);
|
||||
list_add_tail(&m->list, &v9fs_trans_list);
|
||||
spin_unlock(&v9fs_trans_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(v9fs_register_trans);
|
||||
|
||||
/**
|
||||
* v9fs_unregister_trans - unregister a 9p transport
|
||||
* @m: the transport to remove
|
||||
*
|
||||
*/
|
||||
void v9fs_unregister_trans(struct p9_trans_module *m)
|
||||
{
|
||||
spin_lock(&v9fs_trans_lock);
|
||||
list_del_init(&m->list);
|
||||
spin_unlock(&v9fs_trans_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(v9fs_unregister_trans);
|
||||
|
||||
/**
|
||||
* v9fs_get_trans_by_name - get transport with the matching name
|
||||
* @name: string identifying transport
|
||||
*
|
||||
*/
|
||||
struct p9_trans_module *v9fs_get_trans_by_name(char *s)
|
||||
{
|
||||
struct p9_trans_module *t, *found = NULL;
|
||||
|
||||
spin_lock(&v9fs_trans_lock);
|
||||
|
||||
list_for_each_entry(t, &v9fs_trans_list, list)
|
||||
if (strcmp(t->name, s) == 0 &&
|
||||
try_module_get(t->owner)) {
|
||||
found = t;
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock(&v9fs_trans_lock);
|
||||
return found;
|
||||
}
|
||||
EXPORT_SYMBOL(v9fs_get_trans_by_name);
|
||||
|
||||
/**
|
||||
* v9fs_get_default_trans - get the default transport
|
||||
*
|
||||
*/
|
||||
|
||||
struct p9_trans_module *v9fs_get_default_trans(void)
|
||||
{
|
||||
struct p9_trans_module *t, *found = NULL;
|
||||
|
||||
spin_lock(&v9fs_trans_lock);
|
||||
|
||||
list_for_each_entry(t, &v9fs_trans_list, list)
|
||||
if (t->def && try_module_get(t->owner)) {
|
||||
found = t;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
list_for_each_entry(t, &v9fs_trans_list, list)
|
||||
if (try_module_get(t->owner)) {
|
||||
found = t;
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock(&v9fs_trans_lock);
|
||||
return found;
|
||||
}
|
||||
EXPORT_SYMBOL(v9fs_get_default_trans);
|
||||
|
||||
/**
|
||||
* v9fs_put_trans - put trans
|
||||
* @m: transport to put
|
||||
*
|
||||
*/
|
||||
void v9fs_put_trans(struct p9_trans_module *m)
|
||||
{
|
||||
if (m)
|
||||
module_put(m->owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* init_p9 - Initialize module
|
||||
*
|
||||
*/
|
||||
static int __init init_p9(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
p9_error_init();
|
||||
pr_info("Installing 9P2000 support\n");
|
||||
p9_trans_fd_init();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* exit_p9 - shutdown module
|
||||
*
|
||||
*/
|
||||
|
||||
static void __exit exit_p9(void)
|
||||
{
|
||||
pr_info("Unloading 9P2000 support\n");
|
||||
|
||||
p9_trans_fd_exit();
|
||||
}
|
||||
|
||||
module_init(init_p9)
|
||||
module_exit(exit_p9)
|
||||
|
||||
MODULE_AUTHOR("Latchesar Ionkov <lucho@ionkov.net>");
|
||||
MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
|
||||
MODULE_AUTHOR("Ron Minnich <rminnich@lanl.gov>");
|
||||
MODULE_LICENSE("GPL");
|
||||
634
addons/9p/src/4.4.180/protocol.c
Normal file
634
addons/9p/src/4.4.180/protocol.c
Normal file
@@ -0,0 +1,634 @@
|
||||
/*
|
||||
* net/9p/protocol.c
|
||||
*
|
||||
* 9P Protocol Support Code
|
||||
*
|
||||
* Copyright (C) 2008 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
*
|
||||
* Base on code from Anthony Liguori <aliguori@us.ibm.com>
|
||||
* Copyright (C) 2008 by IBM, Corp.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uio.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
#include "protocol.h"
|
||||
|
||||
#include <trace/events/9p.h>
|
||||
|
||||
static int
|
||||
p9pdu_writef(struct p9_fcall *pdu, int proto_version, const char *fmt, ...);
|
||||
|
||||
void p9stat_free(struct p9_wstat *stbuf)
|
||||
{
|
||||
kfree(stbuf->name);
|
||||
stbuf->name = NULL;
|
||||
kfree(stbuf->uid);
|
||||
stbuf->uid = NULL;
|
||||
kfree(stbuf->gid);
|
||||
stbuf->gid = NULL;
|
||||
kfree(stbuf->muid);
|
||||
stbuf->muid = NULL;
|
||||
kfree(stbuf->extension);
|
||||
stbuf->extension = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(p9stat_free);
|
||||
|
||||
size_t pdu_read(struct p9_fcall *pdu, void *data, size_t size)
|
||||
{
|
||||
size_t len = min(pdu->size - pdu->offset, size);
|
||||
memcpy(data, &pdu->sdata[pdu->offset], len);
|
||||
pdu->offset += len;
|
||||
return size - len;
|
||||
}
|
||||
|
||||
static size_t pdu_write(struct p9_fcall *pdu, const void *data, size_t size)
|
||||
{
|
||||
size_t len = min(pdu->capacity - pdu->size, size);
|
||||
memcpy(&pdu->sdata[pdu->size], data, len);
|
||||
pdu->size += len;
|
||||
return size - len;
|
||||
}
|
||||
|
||||
static size_t
|
||||
pdu_write_u(struct p9_fcall *pdu, struct iov_iter *from, size_t size)
|
||||
{
|
||||
size_t len = min(pdu->capacity - pdu->size, size);
|
||||
struct iov_iter i = *from;
|
||||
if (copy_from_iter(&pdu->sdata[pdu->size], len, &i) != len)
|
||||
len = 0;
|
||||
|
||||
pdu->size += len;
|
||||
return size - len;
|
||||
}
|
||||
|
||||
/*
|
||||
b - int8_t
|
||||
w - int16_t
|
||||
d - int32_t
|
||||
q - int64_t
|
||||
s - string
|
||||
u - numeric uid
|
||||
g - numeric gid
|
||||
S - stat
|
||||
Q - qid
|
||||
D - data blob (int32_t size followed by void *, results are not freed)
|
||||
T - array of strings (int16_t count, followed by strings)
|
||||
R - array of qids (int16_t count, followed by qids)
|
||||
A - stat for 9p2000.L (p9_stat_dotl)
|
||||
? - if optional = 1, continue parsing
|
||||
*/
|
||||
|
||||
static int
|
||||
p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt,
|
||||
va_list ap)
|
||||
{
|
||||
const char *ptr;
|
||||
int errcode = 0;
|
||||
|
||||
for (ptr = fmt; *ptr; ptr++) {
|
||||
switch (*ptr) {
|
||||
case 'b':{
|
||||
int8_t *val = va_arg(ap, int8_t *);
|
||||
if (pdu_read(pdu, val, sizeof(*val))) {
|
||||
errcode = -EFAULT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'w':{
|
||||
int16_t *val = va_arg(ap, int16_t *);
|
||||
__le16 le_val;
|
||||
if (pdu_read(pdu, &le_val, sizeof(le_val))) {
|
||||
errcode = -EFAULT;
|
||||
break;
|
||||
}
|
||||
*val = le16_to_cpu(le_val);
|
||||
}
|
||||
break;
|
||||
case 'd':{
|
||||
int32_t *val = va_arg(ap, int32_t *);
|
||||
__le32 le_val;
|
||||
if (pdu_read(pdu, &le_val, sizeof(le_val))) {
|
||||
errcode = -EFAULT;
|
||||
break;
|
||||
}
|
||||
*val = le32_to_cpu(le_val);
|
||||
}
|
||||
break;
|
||||
case 'q':{
|
||||
int64_t *val = va_arg(ap, int64_t *);
|
||||
__le64 le_val;
|
||||
if (pdu_read(pdu, &le_val, sizeof(le_val))) {
|
||||
errcode = -EFAULT;
|
||||
break;
|
||||
}
|
||||
*val = le64_to_cpu(le_val);
|
||||
}
|
||||
break;
|
||||
case 's':{
|
||||
char **sptr = va_arg(ap, char **);
|
||||
uint16_t len;
|
||||
|
||||
errcode = p9pdu_readf(pdu, proto_version,
|
||||
"w", &len);
|
||||
if (errcode)
|
||||
break;
|
||||
|
||||
*sptr = kmalloc(len + 1, GFP_NOFS);
|
||||
if (*sptr == NULL) {
|
||||
errcode = -EFAULT;
|
||||
break;
|
||||
}
|
||||
if (pdu_read(pdu, *sptr, len)) {
|
||||
errcode = -EFAULT;
|
||||
kfree(*sptr);
|
||||
*sptr = NULL;
|
||||
} else
|
||||
(*sptr)[len] = 0;
|
||||
}
|
||||
break;
|
||||
case 'u': {
|
||||
kuid_t *uid = va_arg(ap, kuid_t *);
|
||||
__le32 le_val;
|
||||
if (pdu_read(pdu, &le_val, sizeof(le_val))) {
|
||||
errcode = -EFAULT;
|
||||
break;
|
||||
}
|
||||
*uid = make_kuid(&init_user_ns,
|
||||
le32_to_cpu(le_val));
|
||||
} break;
|
||||
case 'g': {
|
||||
kgid_t *gid = va_arg(ap, kgid_t *);
|
||||
__le32 le_val;
|
||||
if (pdu_read(pdu, &le_val, sizeof(le_val))) {
|
||||
errcode = -EFAULT;
|
||||
break;
|
||||
}
|
||||
*gid = make_kgid(&init_user_ns,
|
||||
le32_to_cpu(le_val));
|
||||
} break;
|
||||
case 'Q':{
|
||||
struct p9_qid *qid =
|
||||
va_arg(ap, struct p9_qid *);
|
||||
|
||||
errcode = p9pdu_readf(pdu, proto_version, "bdq",
|
||||
&qid->type, &qid->version,
|
||||
&qid->path);
|
||||
}
|
||||
break;
|
||||
case 'S':{
|
||||
struct p9_wstat *stbuf =
|
||||
va_arg(ap, struct p9_wstat *);
|
||||
|
||||
memset(stbuf, 0, sizeof(struct p9_wstat));
|
||||
stbuf->n_uid = stbuf->n_muid = INVALID_UID;
|
||||
stbuf->n_gid = INVALID_GID;
|
||||
|
||||
errcode =
|
||||
p9pdu_readf(pdu, proto_version,
|
||||
"wwdQdddqssss?sugu",
|
||||
&stbuf->size, &stbuf->type,
|
||||
&stbuf->dev, &stbuf->qid,
|
||||
&stbuf->mode, &stbuf->atime,
|
||||
&stbuf->mtime, &stbuf->length,
|
||||
&stbuf->name, &stbuf->uid,
|
||||
&stbuf->gid, &stbuf->muid,
|
||||
&stbuf->extension,
|
||||
&stbuf->n_uid, &stbuf->n_gid,
|
||||
&stbuf->n_muid);
|
||||
if (errcode)
|
||||
p9stat_free(stbuf);
|
||||
}
|
||||
break;
|
||||
case 'D':{
|
||||
uint32_t *count = va_arg(ap, uint32_t *);
|
||||
void **data = va_arg(ap, void **);
|
||||
|
||||
errcode =
|
||||
p9pdu_readf(pdu, proto_version, "d", count);
|
||||
if (!errcode) {
|
||||
*count =
|
||||
min_t(uint32_t, *count,
|
||||
pdu->size - pdu->offset);
|
||||
*data = &pdu->sdata[pdu->offset];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'T':{
|
||||
uint16_t *nwname = va_arg(ap, uint16_t *);
|
||||
char ***wnames = va_arg(ap, char ***);
|
||||
|
||||
errcode = p9pdu_readf(pdu, proto_version,
|
||||
"w", nwname);
|
||||
if (!errcode) {
|
||||
*wnames =
|
||||
kmalloc(sizeof(char *) * *nwname,
|
||||
GFP_NOFS);
|
||||
if (!*wnames)
|
||||
errcode = -ENOMEM;
|
||||
}
|
||||
|
||||
if (!errcode) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < *nwname; i++) {
|
||||
errcode =
|
||||
p9pdu_readf(pdu,
|
||||
proto_version,
|
||||
"s",
|
||||
&(*wnames)[i]);
|
||||
if (errcode)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (errcode) {
|
||||
if (*wnames) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < *nwname; i++)
|
||||
kfree((*wnames)[i]);
|
||||
}
|
||||
kfree(*wnames);
|
||||
*wnames = NULL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'R':{
|
||||
uint16_t *nwqid = va_arg(ap, uint16_t *);
|
||||
struct p9_qid **wqids =
|
||||
va_arg(ap, struct p9_qid **);
|
||||
|
||||
*wqids = NULL;
|
||||
|
||||
errcode =
|
||||
p9pdu_readf(pdu, proto_version, "w", nwqid);
|
||||
if (!errcode) {
|
||||
*wqids =
|
||||
kmalloc(*nwqid *
|
||||
sizeof(struct p9_qid),
|
||||
GFP_NOFS);
|
||||
if (*wqids == NULL)
|
||||
errcode = -ENOMEM;
|
||||
}
|
||||
|
||||
if (!errcode) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < *nwqid; i++) {
|
||||
errcode =
|
||||
p9pdu_readf(pdu,
|
||||
proto_version,
|
||||
"Q",
|
||||
&(*wqids)[i]);
|
||||
if (errcode)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (errcode) {
|
||||
kfree(*wqids);
|
||||
*wqids = NULL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'A': {
|
||||
struct p9_stat_dotl *stbuf =
|
||||
va_arg(ap, struct p9_stat_dotl *);
|
||||
|
||||
memset(stbuf, 0, sizeof(struct p9_stat_dotl));
|
||||
errcode =
|
||||
p9pdu_readf(pdu, proto_version,
|
||||
"qQdugqqqqqqqqqqqqqqq",
|
||||
&stbuf->st_result_mask,
|
||||
&stbuf->qid,
|
||||
&stbuf->st_mode,
|
||||
&stbuf->st_uid, &stbuf->st_gid,
|
||||
&stbuf->st_nlink,
|
||||
&stbuf->st_rdev, &stbuf->st_size,
|
||||
&stbuf->st_blksize, &stbuf->st_blocks,
|
||||
&stbuf->st_atime_sec,
|
||||
&stbuf->st_atime_nsec,
|
||||
&stbuf->st_mtime_sec,
|
||||
&stbuf->st_mtime_nsec,
|
||||
&stbuf->st_ctime_sec,
|
||||
&stbuf->st_ctime_nsec,
|
||||
&stbuf->st_btime_sec,
|
||||
&stbuf->st_btime_nsec,
|
||||
&stbuf->st_gen,
|
||||
&stbuf->st_data_version);
|
||||
}
|
||||
break;
|
||||
case '?':
|
||||
if ((proto_version != p9_proto_2000u) &&
|
||||
(proto_version != p9_proto_2000L))
|
||||
return 0;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
break;
|
||||
}
|
||||
|
||||
if (errcode)
|
||||
break;
|
||||
}
|
||||
|
||||
return errcode;
|
||||
}
|
||||
|
||||
int
|
||||
p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt,
|
||||
va_list ap)
|
||||
{
|
||||
const char *ptr;
|
||||
int errcode = 0;
|
||||
|
||||
for (ptr = fmt; *ptr; ptr++) {
|
||||
switch (*ptr) {
|
||||
case 'b':{
|
||||
int8_t val = va_arg(ap, int);
|
||||
if (pdu_write(pdu, &val, sizeof(val)))
|
||||
errcode = -EFAULT;
|
||||
}
|
||||
break;
|
||||
case 'w':{
|
||||
__le16 val = cpu_to_le16(va_arg(ap, int));
|
||||
if (pdu_write(pdu, &val, sizeof(val)))
|
||||
errcode = -EFAULT;
|
||||
}
|
||||
break;
|
||||
case 'd':{
|
||||
__le32 val = cpu_to_le32(va_arg(ap, int32_t));
|
||||
if (pdu_write(pdu, &val, sizeof(val)))
|
||||
errcode = -EFAULT;
|
||||
}
|
||||
break;
|
||||
case 'q':{
|
||||
__le64 val = cpu_to_le64(va_arg(ap, int64_t));
|
||||
if (pdu_write(pdu, &val, sizeof(val)))
|
||||
errcode = -EFAULT;
|
||||
}
|
||||
break;
|
||||
case 's':{
|
||||
const char *sptr = va_arg(ap, const char *);
|
||||
uint16_t len = 0;
|
||||
if (sptr)
|
||||
len = min_t(size_t, strlen(sptr),
|
||||
USHRT_MAX);
|
||||
|
||||
errcode = p9pdu_writef(pdu, proto_version,
|
||||
"w", len);
|
||||
if (!errcode && pdu_write(pdu, sptr, len))
|
||||
errcode = -EFAULT;
|
||||
}
|
||||
break;
|
||||
case 'u': {
|
||||
kuid_t uid = va_arg(ap, kuid_t);
|
||||
__le32 val = cpu_to_le32(
|
||||
from_kuid(&init_user_ns, uid));
|
||||
if (pdu_write(pdu, &val, sizeof(val)))
|
||||
errcode = -EFAULT;
|
||||
} break;
|
||||
case 'g': {
|
||||
kgid_t gid = va_arg(ap, kgid_t);
|
||||
__le32 val = cpu_to_le32(
|
||||
from_kgid(&init_user_ns, gid));
|
||||
if (pdu_write(pdu, &val, sizeof(val)))
|
||||
errcode = -EFAULT;
|
||||
} break;
|
||||
case 'Q':{
|
||||
const struct p9_qid *qid =
|
||||
va_arg(ap, const struct p9_qid *);
|
||||
errcode =
|
||||
p9pdu_writef(pdu, proto_version, "bdq",
|
||||
qid->type, qid->version,
|
||||
qid->path);
|
||||
} break;
|
||||
case 'S':{
|
||||
const struct p9_wstat *stbuf =
|
||||
va_arg(ap, const struct p9_wstat *);
|
||||
errcode =
|
||||
p9pdu_writef(pdu, proto_version,
|
||||
"wwdQdddqssss?sugu",
|
||||
stbuf->size, stbuf->type,
|
||||
stbuf->dev, &stbuf->qid,
|
||||
stbuf->mode, stbuf->atime,
|
||||
stbuf->mtime, stbuf->length,
|
||||
stbuf->name, stbuf->uid,
|
||||
stbuf->gid, stbuf->muid,
|
||||
stbuf->extension, stbuf->n_uid,
|
||||
stbuf->n_gid, stbuf->n_muid);
|
||||
} break;
|
||||
case 'V':{
|
||||
uint32_t count = va_arg(ap, uint32_t);
|
||||
struct iov_iter *from =
|
||||
va_arg(ap, struct iov_iter *);
|
||||
errcode = p9pdu_writef(pdu, proto_version, "d",
|
||||
count);
|
||||
if (!errcode && pdu_write_u(pdu, from, count))
|
||||
errcode = -EFAULT;
|
||||
}
|
||||
break;
|
||||
case 'T':{
|
||||
uint16_t nwname = va_arg(ap, int);
|
||||
const char **wnames = va_arg(ap, const char **);
|
||||
|
||||
errcode = p9pdu_writef(pdu, proto_version, "w",
|
||||
nwname);
|
||||
if (!errcode) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nwname; i++) {
|
||||
errcode =
|
||||
p9pdu_writef(pdu,
|
||||
proto_version,
|
||||
"s",
|
||||
wnames[i]);
|
||||
if (errcode)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'R':{
|
||||
uint16_t nwqid = va_arg(ap, int);
|
||||
struct p9_qid *wqids =
|
||||
va_arg(ap, struct p9_qid *);
|
||||
|
||||
errcode = p9pdu_writef(pdu, proto_version, "w",
|
||||
nwqid);
|
||||
if (!errcode) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nwqid; i++) {
|
||||
errcode =
|
||||
p9pdu_writef(pdu,
|
||||
proto_version,
|
||||
"Q",
|
||||
&wqids[i]);
|
||||
if (errcode)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'I':{
|
||||
struct p9_iattr_dotl *p9attr = va_arg(ap,
|
||||
struct p9_iattr_dotl *);
|
||||
|
||||
errcode = p9pdu_writef(pdu, proto_version,
|
||||
"ddugqqqqq",
|
||||
p9attr->valid,
|
||||
p9attr->mode,
|
||||
p9attr->uid,
|
||||
p9attr->gid,
|
||||
p9attr->size,
|
||||
p9attr->atime_sec,
|
||||
p9attr->atime_nsec,
|
||||
p9attr->mtime_sec,
|
||||
p9attr->mtime_nsec);
|
||||
}
|
||||
break;
|
||||
case '?':
|
||||
if ((proto_version != p9_proto_2000u) &&
|
||||
(proto_version != p9_proto_2000L))
|
||||
return 0;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
break;
|
||||
}
|
||||
|
||||
if (errcode)
|
||||
break;
|
||||
}
|
||||
|
||||
return errcode;
|
||||
}
|
||||
|
||||
int p9pdu_readf(struct p9_fcall *pdu, int proto_version, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int ret;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = p9pdu_vreadf(pdu, proto_version, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
p9pdu_writef(struct p9_fcall *pdu, int proto_version, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int ret;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = p9pdu_vwritef(pdu, proto_version, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int p9stat_read(struct p9_client *clnt, char *buf, int len, struct p9_wstat *st)
|
||||
{
|
||||
struct p9_fcall fake_pdu;
|
||||
int ret;
|
||||
|
||||
fake_pdu.size = len;
|
||||
fake_pdu.capacity = len;
|
||||
fake_pdu.sdata = buf;
|
||||
fake_pdu.offset = 0;
|
||||
|
||||
ret = p9pdu_readf(&fake_pdu, clnt->proto_version, "S", st);
|
||||
if (ret) {
|
||||
p9_debug(P9_DEBUG_9P, "<<< p9stat_read failed: %d\n", ret);
|
||||
trace_9p_protocol_dump(clnt, &fake_pdu);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return fake_pdu.offset;
|
||||
}
|
||||
EXPORT_SYMBOL(p9stat_read);
|
||||
|
||||
int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type)
|
||||
{
|
||||
pdu->id = type;
|
||||
return p9pdu_writef(pdu, 0, "dbw", 0, type, tag);
|
||||
}
|
||||
|
||||
int p9pdu_finalize(struct p9_client *clnt, struct p9_fcall *pdu)
|
||||
{
|
||||
int size = pdu->size;
|
||||
int err;
|
||||
|
||||
pdu->size = 0;
|
||||
err = p9pdu_writef(pdu, 0, "d", size);
|
||||
pdu->size = size;
|
||||
|
||||
trace_9p_protocol_dump(clnt, pdu);
|
||||
p9_debug(P9_DEBUG_9P, ">>> size=%d type: %d tag: %d\n",
|
||||
pdu->size, pdu->id, pdu->tag);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void p9pdu_reset(struct p9_fcall *pdu)
|
||||
{
|
||||
pdu->offset = 0;
|
||||
pdu->size = 0;
|
||||
}
|
||||
|
||||
int p9dirent_read(struct p9_client *clnt, char *buf, int len,
|
||||
struct p9_dirent *dirent)
|
||||
{
|
||||
struct p9_fcall fake_pdu;
|
||||
int ret;
|
||||
char *nameptr;
|
||||
|
||||
fake_pdu.size = len;
|
||||
fake_pdu.capacity = len;
|
||||
fake_pdu.sdata = buf;
|
||||
fake_pdu.offset = 0;
|
||||
|
||||
ret = p9pdu_readf(&fake_pdu, clnt->proto_version, "Qqbs", &dirent->qid,
|
||||
&dirent->d_off, &dirent->d_type, &nameptr);
|
||||
if (ret) {
|
||||
p9_debug(P9_DEBUG_9P, "<<< p9dirent_read failed: %d\n", ret);
|
||||
trace_9p_protocol_dump(clnt, &fake_pdu);
|
||||
goto out;
|
||||
}
|
||||
|
||||
strcpy(dirent->d_name, nameptr);
|
||||
kfree(nameptr);
|
||||
|
||||
out:
|
||||
return fake_pdu.offset;
|
||||
}
|
||||
EXPORT_SYMBOL(p9dirent_read);
|
||||
34
addons/9p/src/4.4.180/protocol.h
Normal file
34
addons/9p/src/4.4.180/protocol.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* net/9p/protocol.h
|
||||
*
|
||||
* 9P Protocol Support Code
|
||||
*
|
||||
* Copyright (C) 2008 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
*
|
||||
* Base on code from Anthony Liguori <aliguori@us.ibm.com>
|
||||
* Copyright (C) 2008 by IBM, Corp.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
int p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt,
|
||||
va_list ap);
|
||||
int p9pdu_readf(struct p9_fcall *pdu, int proto_version, const char *fmt, ...);
|
||||
int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type);
|
||||
int p9pdu_finalize(struct p9_client *clnt, struct p9_fcall *pdu);
|
||||
void p9pdu_reset(struct p9_fcall *pdu);
|
||||
size_t pdu_read(struct p9_fcall *pdu, void *data, size_t size);
|
||||
29
addons/9p/src/4.4.180/trans_common.c
Normal file
29
addons/9p/src/4.4.180/trans_common.c
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright IBM Corporation, 2010
|
||||
* Author Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
/**
|
||||
* p9_release_req_pages - Release pages after the transaction.
|
||||
*/
|
||||
void p9_release_pages(struct page **pages, int nr_pages)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr_pages; i++)
|
||||
if (pages[i])
|
||||
put_page(pages[i]);
|
||||
}
|
||||
EXPORT_SYMBOL(p9_release_pages);
|
||||
15
addons/9p/src/4.4.180/trans_common.h
Normal file
15
addons/9p/src/4.4.180/trans_common.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright IBM Corporation, 2010
|
||||
* Author Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
void p9_release_pages(struct page **, int);
|
||||
1122
addons/9p/src/4.4.180/trans_fd.c
Normal file
1122
addons/9p/src/4.4.180/trans_fd.c
Normal file
File diff suppressed because it is too large
Load Diff
784
addons/9p/src/4.4.180/trans_rdma.c
Normal file
784
addons/9p/src/4.4.180/trans_rdma.c
Normal file
@@ -0,0 +1,784 @@
|
||||
/*
|
||||
* linux/fs/9p/trans_rdma.c
|
||||
*
|
||||
* RDMA transport layer based on the trans_fd.c implementation.
|
||||
*
|
||||
* Copyright (C) 2008 by Tom Tucker <tom@opengridcomputing.com>
|
||||
* Copyright (C) 2006 by Russ Cox <rsc@swtch.com>
|
||||
* Copyright (C) 2004-2005 by Latchesar Ionkov <lucho@ionkov.net>
|
||||
* Copyright (C) 2004-2008 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
* Copyright (C) 1997-2002 by Ron Minnich <rminnich@sarnoff.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/in.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/un.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/parser.h>
|
||||
#include <linux/semaphore.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
#include <net/9p/transport.h>
|
||||
#include <rdma/ib_verbs.h>
|
||||
#include <rdma/rdma_cm.h>
|
||||
|
||||
#define P9_PORT 5640
|
||||
#define P9_RDMA_SQ_DEPTH 32
|
||||
#define P9_RDMA_RQ_DEPTH 32
|
||||
#define P9_RDMA_SEND_SGE 4
|
||||
#define P9_RDMA_RECV_SGE 4
|
||||
#define P9_RDMA_IRD 0
|
||||
#define P9_RDMA_ORD 0
|
||||
#define P9_RDMA_TIMEOUT 30000 /* 30 seconds */
|
||||
#define P9_RDMA_MAXSIZE (1024*1024) /* 1MB */
|
||||
|
||||
/**
|
||||
* struct p9_trans_rdma - RDMA transport instance
|
||||
*
|
||||
* @state: tracks the transport state machine for connection setup and tear down
|
||||
* @cm_id: The RDMA CM ID
|
||||
* @pd: Protection Domain pointer
|
||||
* @qp: Queue Pair pointer
|
||||
* @cq: Completion Queue pointer
|
||||
* @dm_mr: DMA Memory Region pointer
|
||||
* @lkey: The local access only memory region key
|
||||
* @timeout: Number of uSecs to wait for connection management events
|
||||
* @sq_depth: The depth of the Send Queue
|
||||
* @sq_sem: Semaphore for the SQ
|
||||
* @rq_depth: The depth of the Receive Queue.
|
||||
* @rq_sem: Semaphore for the RQ
|
||||
* @excess_rc : Amount of posted Receive Contexts without a pending request.
|
||||
* See rdma_request()
|
||||
* @addr: The remote peer's address
|
||||
* @req_lock: Protects the active request list
|
||||
* @cm_done: Completion event for connection management tracking
|
||||
*/
|
||||
struct p9_trans_rdma {
|
||||
enum {
|
||||
P9_RDMA_INIT,
|
||||
P9_RDMA_ADDR_RESOLVED,
|
||||
P9_RDMA_ROUTE_RESOLVED,
|
||||
P9_RDMA_CONNECTED,
|
||||
P9_RDMA_FLUSHING,
|
||||
P9_RDMA_CLOSING,
|
||||
P9_RDMA_CLOSED,
|
||||
} state;
|
||||
struct rdma_cm_id *cm_id;
|
||||
struct ib_pd *pd;
|
||||
struct ib_qp *qp;
|
||||
struct ib_cq *cq;
|
||||
long timeout;
|
||||
int sq_depth;
|
||||
struct semaphore sq_sem;
|
||||
int rq_depth;
|
||||
struct semaphore rq_sem;
|
||||
atomic_t excess_rc;
|
||||
struct sockaddr_in addr;
|
||||
spinlock_t req_lock;
|
||||
|
||||
struct completion cm_done;
|
||||
};
|
||||
|
||||
/**
|
||||
* p9_rdma_context - Keeps track of in-process WR
|
||||
*
|
||||
* @wc_op: The original WR op for when the CQE completes in error.
|
||||
* @busa: Bus address to unmap when the WR completes
|
||||
* @req: Keeps track of requests (send)
|
||||
* @rc: Keepts track of replies (receive)
|
||||
*/
|
||||
struct p9_rdma_req;
|
||||
struct p9_rdma_context {
|
||||
enum ib_wc_opcode wc_op;
|
||||
dma_addr_t busa;
|
||||
union {
|
||||
struct p9_req_t *req;
|
||||
struct p9_fcall *rc;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* p9_rdma_opts - Collection of mount options
|
||||
* @port: port of connection
|
||||
* @sq_depth: The requested depth of the SQ. This really doesn't need
|
||||
* to be any deeper than the number of threads used in the client
|
||||
* @rq_depth: The depth of the RQ. Should be greater than or equal to SQ depth
|
||||
* @timeout: Time to wait in msecs for CM events
|
||||
*/
|
||||
struct p9_rdma_opts {
|
||||
short port;
|
||||
int sq_depth;
|
||||
int rq_depth;
|
||||
long timeout;
|
||||
int privport;
|
||||
};
|
||||
|
||||
/*
|
||||
* Option Parsing (code inspired by NFS code)
|
||||
*/
|
||||
enum {
|
||||
/* Options that take integer arguments */
|
||||
Opt_port, Opt_rq_depth, Opt_sq_depth, Opt_timeout,
|
||||
/* Options that take no argument */
|
||||
Opt_privport,
|
||||
Opt_err,
|
||||
};
|
||||
|
||||
static match_table_t tokens = {
|
||||
{Opt_port, "port=%u"},
|
||||
{Opt_sq_depth, "sq=%u"},
|
||||
{Opt_rq_depth, "rq=%u"},
|
||||
{Opt_timeout, "timeout=%u"},
|
||||
{Opt_privport, "privport"},
|
||||
{Opt_err, NULL},
|
||||
};
|
||||
|
||||
/**
|
||||
* parse_opts - parse mount options into rdma options structure
|
||||
* @params: options string passed from mount
|
||||
* @opts: rdma transport-specific structure to parse options into
|
||||
*
|
||||
* Returns 0 upon success, -ERRNO upon failure
|
||||
*/
|
||||
static int parse_opts(char *params, struct p9_rdma_opts *opts)
|
||||
{
|
||||
char *p;
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
int option;
|
||||
char *options, *tmp_options;
|
||||
|
||||
opts->port = P9_PORT;
|
||||
opts->sq_depth = P9_RDMA_SQ_DEPTH;
|
||||
opts->rq_depth = P9_RDMA_RQ_DEPTH;
|
||||
opts->timeout = P9_RDMA_TIMEOUT;
|
||||
opts->privport = 0;
|
||||
|
||||
if (!params)
|
||||
return 0;
|
||||
|
||||
tmp_options = kstrdup(params, GFP_KERNEL);
|
||||
if (!tmp_options) {
|
||||
p9_debug(P9_DEBUG_ERROR,
|
||||
"failed to allocate copy of option string\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
options = tmp_options;
|
||||
|
||||
while ((p = strsep(&options, ",")) != NULL) {
|
||||
int token;
|
||||
int r;
|
||||
if (!*p)
|
||||
continue;
|
||||
token = match_token(p, tokens, args);
|
||||
if ((token != Opt_err) && (token != Opt_privport)) {
|
||||
r = match_int(&args[0], &option);
|
||||
if (r < 0) {
|
||||
p9_debug(P9_DEBUG_ERROR,
|
||||
"integer field, but no integer?\n");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
switch (token) {
|
||||
case Opt_port:
|
||||
opts->port = option;
|
||||
break;
|
||||
case Opt_sq_depth:
|
||||
opts->sq_depth = option;
|
||||
break;
|
||||
case Opt_rq_depth:
|
||||
opts->rq_depth = option;
|
||||
break;
|
||||
case Opt_timeout:
|
||||
opts->timeout = option;
|
||||
break;
|
||||
case Opt_privport:
|
||||
opts->privport = 1;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/* RQ must be at least as large as the SQ */
|
||||
opts->rq_depth = max(opts->rq_depth, opts->sq_depth);
|
||||
kfree(tmp_options);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
p9_cm_event_handler(struct rdma_cm_id *id, struct rdma_cm_event *event)
|
||||
{
|
||||
struct p9_client *c = id->context;
|
||||
struct p9_trans_rdma *rdma = c->trans;
|
||||
switch (event->event) {
|
||||
case RDMA_CM_EVENT_ADDR_RESOLVED:
|
||||
BUG_ON(rdma->state != P9_RDMA_INIT);
|
||||
rdma->state = P9_RDMA_ADDR_RESOLVED;
|
||||
break;
|
||||
|
||||
case RDMA_CM_EVENT_ROUTE_RESOLVED:
|
||||
BUG_ON(rdma->state != P9_RDMA_ADDR_RESOLVED);
|
||||
rdma->state = P9_RDMA_ROUTE_RESOLVED;
|
||||
break;
|
||||
|
||||
case RDMA_CM_EVENT_ESTABLISHED:
|
||||
BUG_ON(rdma->state != P9_RDMA_ROUTE_RESOLVED);
|
||||
rdma->state = P9_RDMA_CONNECTED;
|
||||
break;
|
||||
|
||||
case RDMA_CM_EVENT_DISCONNECTED:
|
||||
if (rdma)
|
||||
rdma->state = P9_RDMA_CLOSED;
|
||||
if (c)
|
||||
c->status = Disconnected;
|
||||
break;
|
||||
|
||||
case RDMA_CM_EVENT_TIMEWAIT_EXIT:
|
||||
break;
|
||||
|
||||
case RDMA_CM_EVENT_ADDR_CHANGE:
|
||||
case RDMA_CM_EVENT_ROUTE_ERROR:
|
||||
case RDMA_CM_EVENT_DEVICE_REMOVAL:
|
||||
case RDMA_CM_EVENT_MULTICAST_JOIN:
|
||||
case RDMA_CM_EVENT_MULTICAST_ERROR:
|
||||
case RDMA_CM_EVENT_REJECTED:
|
||||
case RDMA_CM_EVENT_CONNECT_REQUEST:
|
||||
case RDMA_CM_EVENT_CONNECT_RESPONSE:
|
||||
case RDMA_CM_EVENT_CONNECT_ERROR:
|
||||
case RDMA_CM_EVENT_ADDR_ERROR:
|
||||
case RDMA_CM_EVENT_UNREACHABLE:
|
||||
c->status = Disconnected;
|
||||
rdma_disconnect(rdma->cm_id);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
complete(&rdma->cm_done);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_recv(struct p9_client *client, struct p9_trans_rdma *rdma,
|
||||
struct p9_rdma_context *c, enum ib_wc_status status, u32 byte_len)
|
||||
{
|
||||
struct p9_req_t *req;
|
||||
int err = 0;
|
||||
int16_t tag;
|
||||
|
||||
req = NULL;
|
||||
ib_dma_unmap_single(rdma->cm_id->device, c->busa, client->msize,
|
||||
DMA_FROM_DEVICE);
|
||||
|
||||
if (status != IB_WC_SUCCESS)
|
||||
goto err_out;
|
||||
|
||||
err = p9_parse_header(c->rc, NULL, NULL, &tag, 1);
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
req = p9_tag_lookup(client, tag);
|
||||
if (!req)
|
||||
goto err_out;
|
||||
|
||||
/* Check that we have not yet received a reply for this request.
|
||||
*/
|
||||
if (unlikely(req->rc)) {
|
||||
pr_err("Duplicate reply for request %d", tag);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
req->rc = c->rc;
|
||||
p9_client_cb(client, req, REQ_STATUS_RCVD);
|
||||
|
||||
return;
|
||||
|
||||
err_out:
|
||||
p9_debug(P9_DEBUG_ERROR, "req %p err %d status %d\n", req, err, status);
|
||||
rdma->state = P9_RDMA_FLUSHING;
|
||||
client->status = Disconnected;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_send(struct p9_client *client, struct p9_trans_rdma *rdma,
|
||||
struct p9_rdma_context *c, enum ib_wc_status status, u32 byte_len)
|
||||
{
|
||||
ib_dma_unmap_single(rdma->cm_id->device,
|
||||
c->busa, c->req->tc->size,
|
||||
DMA_TO_DEVICE);
|
||||
}
|
||||
|
||||
static void qp_event_handler(struct ib_event *event, void *context)
|
||||
{
|
||||
p9_debug(P9_DEBUG_ERROR, "QP event %d context %p\n",
|
||||
event->event, context);
|
||||
}
|
||||
|
||||
static void cq_comp_handler(struct ib_cq *cq, void *cq_context)
|
||||
{
|
||||
struct p9_client *client = cq_context;
|
||||
struct p9_trans_rdma *rdma = client->trans;
|
||||
int ret;
|
||||
struct ib_wc wc;
|
||||
|
||||
ib_req_notify_cq(rdma->cq, IB_CQ_NEXT_COMP);
|
||||
while ((ret = ib_poll_cq(cq, 1, &wc)) > 0) {
|
||||
struct p9_rdma_context *c = (void *) (unsigned long) wc.wr_id;
|
||||
|
||||
switch (c->wc_op) {
|
||||
case IB_WC_RECV:
|
||||
handle_recv(client, rdma, c, wc.status, wc.byte_len);
|
||||
up(&rdma->rq_sem);
|
||||
break;
|
||||
|
||||
case IB_WC_SEND:
|
||||
handle_send(client, rdma, c, wc.status, wc.byte_len);
|
||||
up(&rdma->sq_sem);
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_err("unexpected completion type, c->wc_op=%d, wc.opcode=%d, status=%d\n",
|
||||
c->wc_op, wc.opcode, wc.status);
|
||||
break;
|
||||
}
|
||||
kfree(c);
|
||||
}
|
||||
}
|
||||
|
||||
static void cq_event_handler(struct ib_event *e, void *v)
|
||||
{
|
||||
p9_debug(P9_DEBUG_ERROR, "CQ event %d context %p\n", e->event, v);
|
||||
}
|
||||
|
||||
static void rdma_destroy_trans(struct p9_trans_rdma *rdma)
|
||||
{
|
||||
if (!rdma)
|
||||
return;
|
||||
|
||||
if (rdma->qp && !IS_ERR(rdma->qp))
|
||||
ib_destroy_qp(rdma->qp);
|
||||
|
||||
if (rdma->pd && !IS_ERR(rdma->pd))
|
||||
ib_dealloc_pd(rdma->pd);
|
||||
|
||||
if (rdma->cq && !IS_ERR(rdma->cq))
|
||||
ib_destroy_cq(rdma->cq);
|
||||
|
||||
if (rdma->cm_id && !IS_ERR(rdma->cm_id))
|
||||
rdma_destroy_id(rdma->cm_id);
|
||||
|
||||
kfree(rdma);
|
||||
}
|
||||
|
||||
static int
|
||||
post_recv(struct p9_client *client, struct p9_rdma_context *c)
|
||||
{
|
||||
struct p9_trans_rdma *rdma = client->trans;
|
||||
struct ib_recv_wr wr, *bad_wr;
|
||||
struct ib_sge sge;
|
||||
|
||||
c->busa = ib_dma_map_single(rdma->cm_id->device,
|
||||
c->rc->sdata, client->msize,
|
||||
DMA_FROM_DEVICE);
|
||||
if (ib_dma_mapping_error(rdma->cm_id->device, c->busa))
|
||||
goto error;
|
||||
|
||||
sge.addr = c->busa;
|
||||
sge.length = client->msize;
|
||||
sge.lkey = rdma->pd->local_dma_lkey;
|
||||
|
||||
wr.next = NULL;
|
||||
c->wc_op = IB_WC_RECV;
|
||||
wr.wr_id = (unsigned long) c;
|
||||
wr.sg_list = &sge;
|
||||
wr.num_sge = 1;
|
||||
return ib_post_recv(rdma->qp, &wr, &bad_wr);
|
||||
|
||||
error:
|
||||
p9_debug(P9_DEBUG_ERROR, "EIO\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int rdma_request(struct p9_client *client, struct p9_req_t *req)
|
||||
{
|
||||
struct p9_trans_rdma *rdma = client->trans;
|
||||
struct ib_send_wr wr, *bad_wr;
|
||||
struct ib_sge sge;
|
||||
int err = 0;
|
||||
unsigned long flags;
|
||||
struct p9_rdma_context *c = NULL;
|
||||
struct p9_rdma_context *rpl_context = NULL;
|
||||
|
||||
/* When an error occurs between posting the recv and the send,
|
||||
* there will be a receive context posted without a pending request.
|
||||
* Since there is no way to "un-post" it, we remember it and skip
|
||||
* post_recv() for the next request.
|
||||
* So here,
|
||||
* see if we are this `next request' and need to absorb an excess rc.
|
||||
* If yes, then drop and free our own, and do not recv_post().
|
||||
**/
|
||||
if (unlikely(atomic_read(&rdma->excess_rc) > 0)) {
|
||||
if ((atomic_sub_return(1, &rdma->excess_rc) >= 0)) {
|
||||
/* Got one ! */
|
||||
kfree(req->rc);
|
||||
req->rc = NULL;
|
||||
goto dont_need_post_recv;
|
||||
} else {
|
||||
/* We raced and lost. */
|
||||
atomic_inc(&rdma->excess_rc);
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate an fcall for the reply */
|
||||
rpl_context = kmalloc(sizeof *rpl_context, GFP_NOFS);
|
||||
if (!rpl_context) {
|
||||
err = -ENOMEM;
|
||||
goto recv_error;
|
||||
}
|
||||
rpl_context->rc = req->rc;
|
||||
|
||||
/*
|
||||
* Post a receive buffer for this request. We need to ensure
|
||||
* there is a reply buffer available for every outstanding
|
||||
* request. A flushed request can result in no reply for an
|
||||
* outstanding request, so we must keep a count to avoid
|
||||
* overflowing the RQ.
|
||||
*/
|
||||
if (down_interruptible(&rdma->rq_sem)) {
|
||||
err = -EINTR;
|
||||
goto recv_error;
|
||||
}
|
||||
|
||||
err = post_recv(client, rpl_context);
|
||||
if (err) {
|
||||
p9_debug(P9_DEBUG_FCALL, "POST RECV failed\n");
|
||||
goto recv_error;
|
||||
}
|
||||
/* remove posted receive buffer from request structure */
|
||||
req->rc = NULL;
|
||||
|
||||
dont_need_post_recv:
|
||||
/* Post the request */
|
||||
c = kmalloc(sizeof *c, GFP_NOFS);
|
||||
if (!c) {
|
||||
err = -ENOMEM;
|
||||
goto send_error;
|
||||
}
|
||||
c->req = req;
|
||||
|
||||
c->busa = ib_dma_map_single(rdma->cm_id->device,
|
||||
c->req->tc->sdata, c->req->tc->size,
|
||||
DMA_TO_DEVICE);
|
||||
if (ib_dma_mapping_error(rdma->cm_id->device, c->busa)) {
|
||||
err = -EIO;
|
||||
goto send_error;
|
||||
}
|
||||
|
||||
sge.addr = c->busa;
|
||||
sge.length = c->req->tc->size;
|
||||
sge.lkey = rdma->pd->local_dma_lkey;
|
||||
|
||||
wr.next = NULL;
|
||||
c->wc_op = IB_WC_SEND;
|
||||
wr.wr_id = (unsigned long) c;
|
||||
wr.opcode = IB_WR_SEND;
|
||||
wr.send_flags = IB_SEND_SIGNALED;
|
||||
wr.sg_list = &sge;
|
||||
wr.num_sge = 1;
|
||||
|
||||
if (down_interruptible(&rdma->sq_sem)) {
|
||||
err = -EINTR;
|
||||
goto send_error;
|
||||
}
|
||||
|
||||
/* Mark request as `sent' *before* we actually send it,
|
||||
* because doing if after could erase the REQ_STATUS_RCVD
|
||||
* status in case of a very fast reply.
|
||||
*/
|
||||
req->status = REQ_STATUS_SENT;
|
||||
err = ib_post_send(rdma->qp, &wr, &bad_wr);
|
||||
if (err)
|
||||
goto send_error;
|
||||
|
||||
/* Success */
|
||||
return 0;
|
||||
|
||||
/* Handle errors that happened during or while preparing the send: */
|
||||
send_error:
|
||||
req->status = REQ_STATUS_ERROR;
|
||||
kfree(c);
|
||||
p9_debug(P9_DEBUG_ERROR, "Error %d in rdma_request()\n", err);
|
||||
|
||||
/* Ach.
|
||||
* We did recv_post(), but not send. We have one recv_post in excess.
|
||||
*/
|
||||
atomic_inc(&rdma->excess_rc);
|
||||
return err;
|
||||
|
||||
/* Handle errors that happened during or while preparing post_recv(): */
|
||||
recv_error:
|
||||
kfree(rpl_context);
|
||||
spin_lock_irqsave(&rdma->req_lock, flags);
|
||||
if (rdma->state < P9_RDMA_CLOSING) {
|
||||
rdma->state = P9_RDMA_CLOSING;
|
||||
spin_unlock_irqrestore(&rdma->req_lock, flags);
|
||||
rdma_disconnect(rdma->cm_id);
|
||||
} else
|
||||
spin_unlock_irqrestore(&rdma->req_lock, flags);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void rdma_close(struct p9_client *client)
|
||||
{
|
||||
struct p9_trans_rdma *rdma;
|
||||
|
||||
if (!client)
|
||||
return;
|
||||
|
||||
rdma = client->trans;
|
||||
if (!rdma)
|
||||
return;
|
||||
|
||||
client->status = Disconnected;
|
||||
rdma_disconnect(rdma->cm_id);
|
||||
rdma_destroy_trans(rdma);
|
||||
}
|
||||
|
||||
/**
|
||||
* alloc_rdma - Allocate and initialize the rdma transport structure
|
||||
* @opts: Mount options structure
|
||||
*/
|
||||
static struct p9_trans_rdma *alloc_rdma(struct p9_rdma_opts *opts)
|
||||
{
|
||||
struct p9_trans_rdma *rdma;
|
||||
|
||||
rdma = kzalloc(sizeof(struct p9_trans_rdma), GFP_KERNEL);
|
||||
if (!rdma)
|
||||
return NULL;
|
||||
|
||||
rdma->sq_depth = opts->sq_depth;
|
||||
rdma->rq_depth = opts->rq_depth;
|
||||
rdma->timeout = opts->timeout;
|
||||
spin_lock_init(&rdma->req_lock);
|
||||
init_completion(&rdma->cm_done);
|
||||
sema_init(&rdma->sq_sem, rdma->sq_depth);
|
||||
sema_init(&rdma->rq_sem, rdma->rq_depth);
|
||||
atomic_set(&rdma->excess_rc, 0);
|
||||
|
||||
return rdma;
|
||||
}
|
||||
|
||||
static int rdma_cancel(struct p9_client *client, struct p9_req_t *req)
|
||||
{
|
||||
/* Nothing to do here.
|
||||
* We will take care of it (if we have to) in rdma_cancelled()
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* A request has been fully flushed without a reply.
|
||||
* That means we have posted one buffer in excess.
|
||||
*/
|
||||
static int rdma_cancelled(struct p9_client *client, struct p9_req_t *req)
|
||||
{
|
||||
struct p9_trans_rdma *rdma = client->trans;
|
||||
atomic_inc(&rdma->excess_rc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int p9_rdma_bind_privport(struct p9_trans_rdma *rdma)
|
||||
{
|
||||
struct sockaddr_in cl = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_addr.s_addr = htonl(INADDR_ANY),
|
||||
};
|
||||
int port, err = -EINVAL;
|
||||
|
||||
for (port = P9_DEF_MAX_RESVPORT; port >= P9_DEF_MIN_RESVPORT; port--) {
|
||||
cl.sin_port = htons((ushort)port);
|
||||
err = rdma_bind_addr(rdma->cm_id, (struct sockaddr *)&cl);
|
||||
if (err != -EADDRINUSE)
|
||||
break;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* trans_create_rdma - Transport method for creating atransport instance
|
||||
* @client: client instance
|
||||
* @addr: IP address string
|
||||
* @args: Mount options string
|
||||
*/
|
||||
static int
|
||||
rdma_create_trans(struct p9_client *client, const char *addr, char *args)
|
||||
{
|
||||
int err;
|
||||
struct p9_rdma_opts opts;
|
||||
struct p9_trans_rdma *rdma;
|
||||
struct rdma_conn_param conn_param;
|
||||
struct ib_qp_init_attr qp_attr;
|
||||
struct ib_cq_init_attr cq_attr = {};
|
||||
|
||||
if (addr == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
/* Parse the transport specific mount options */
|
||||
err = parse_opts(args, &opts);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Create and initialize the RDMA transport structure */
|
||||
rdma = alloc_rdma(&opts);
|
||||
if (!rdma)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Create the RDMA CM ID */
|
||||
rdma->cm_id = rdma_create_id(&init_net, p9_cm_event_handler, client,
|
||||
RDMA_PS_TCP, IB_QPT_RC);
|
||||
if (IS_ERR(rdma->cm_id))
|
||||
goto error;
|
||||
|
||||
/* Associate the client with the transport */
|
||||
client->trans = rdma;
|
||||
|
||||
/* Bind to a privileged port if we need to */
|
||||
if (opts.privport) {
|
||||
err = p9_rdma_bind_privport(rdma);
|
||||
if (err < 0) {
|
||||
pr_err("%s (%d): problem binding to privport: %d\n",
|
||||
__func__, task_pid_nr(current), -err);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/* Resolve the server's address */
|
||||
rdma->addr.sin_family = AF_INET;
|
||||
rdma->addr.sin_addr.s_addr = in_aton(addr);
|
||||
rdma->addr.sin_port = htons(opts.port);
|
||||
err = rdma_resolve_addr(rdma->cm_id, NULL,
|
||||
(struct sockaddr *)&rdma->addr,
|
||||
rdma->timeout);
|
||||
if (err)
|
||||
goto error;
|
||||
err = wait_for_completion_interruptible(&rdma->cm_done);
|
||||
if (err || (rdma->state != P9_RDMA_ADDR_RESOLVED))
|
||||
goto error;
|
||||
|
||||
/* Resolve the route to the server */
|
||||
err = rdma_resolve_route(rdma->cm_id, rdma->timeout);
|
||||
if (err)
|
||||
goto error;
|
||||
err = wait_for_completion_interruptible(&rdma->cm_done);
|
||||
if (err || (rdma->state != P9_RDMA_ROUTE_RESOLVED))
|
||||
goto error;
|
||||
|
||||
/* Create the Completion Queue */
|
||||
cq_attr.cqe = opts.sq_depth + opts.rq_depth + 1;
|
||||
rdma->cq = ib_create_cq(rdma->cm_id->device, cq_comp_handler,
|
||||
cq_event_handler, client,
|
||||
&cq_attr);
|
||||
if (IS_ERR(rdma->cq))
|
||||
goto error;
|
||||
ib_req_notify_cq(rdma->cq, IB_CQ_NEXT_COMP);
|
||||
|
||||
/* Create the Protection Domain */
|
||||
rdma->pd = ib_alloc_pd(rdma->cm_id->device);
|
||||
if (IS_ERR(rdma->pd))
|
||||
goto error;
|
||||
|
||||
/* Create the Queue Pair */
|
||||
memset(&qp_attr, 0, sizeof qp_attr);
|
||||
qp_attr.event_handler = qp_event_handler;
|
||||
qp_attr.qp_context = client;
|
||||
qp_attr.cap.max_send_wr = opts.sq_depth;
|
||||
qp_attr.cap.max_recv_wr = opts.rq_depth;
|
||||
qp_attr.cap.max_send_sge = P9_RDMA_SEND_SGE;
|
||||
qp_attr.cap.max_recv_sge = P9_RDMA_RECV_SGE;
|
||||
qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR;
|
||||
qp_attr.qp_type = IB_QPT_RC;
|
||||
qp_attr.send_cq = rdma->cq;
|
||||
qp_attr.recv_cq = rdma->cq;
|
||||
err = rdma_create_qp(rdma->cm_id, rdma->pd, &qp_attr);
|
||||
if (err)
|
||||
goto error;
|
||||
rdma->qp = rdma->cm_id->qp;
|
||||
|
||||
/* Request a connection */
|
||||
memset(&conn_param, 0, sizeof(conn_param));
|
||||
conn_param.private_data = NULL;
|
||||
conn_param.private_data_len = 0;
|
||||
conn_param.responder_resources = P9_RDMA_IRD;
|
||||
conn_param.initiator_depth = P9_RDMA_ORD;
|
||||
err = rdma_connect(rdma->cm_id, &conn_param);
|
||||
if (err)
|
||||
goto error;
|
||||
err = wait_for_completion_interruptible(&rdma->cm_done);
|
||||
if (err || (rdma->state != P9_RDMA_CONNECTED))
|
||||
goto error;
|
||||
|
||||
client->status = Connected;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
rdma_destroy_trans(rdma);
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
static struct p9_trans_module p9_rdma_trans = {
|
||||
.name = "rdma",
|
||||
.maxsize = P9_RDMA_MAXSIZE,
|
||||
.def = 0,
|
||||
.owner = THIS_MODULE,
|
||||
.create = rdma_create_trans,
|
||||
.close = rdma_close,
|
||||
.request = rdma_request,
|
||||
.cancel = rdma_cancel,
|
||||
.cancelled = rdma_cancelled,
|
||||
};
|
||||
|
||||
/**
|
||||
* p9_trans_rdma_init - Register the 9P RDMA transport driver
|
||||
*/
|
||||
static int __init p9_trans_rdma_init(void)
|
||||
{
|
||||
v9fs_register_trans(&p9_rdma_trans);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit p9_trans_rdma_exit(void)
|
||||
{
|
||||
v9fs_unregister_trans(&p9_rdma_trans);
|
||||
}
|
||||
|
||||
module_init(p9_trans_rdma_init);
|
||||
module_exit(p9_trans_rdma_exit);
|
||||
|
||||
MODULE_AUTHOR("Tom Tucker <tom@opengridcomputing.com>");
|
||||
MODULE_DESCRIPTION("RDMA Transport for 9P");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
788
addons/9p/src/4.4.180/trans_virtio.c
Normal file
788
addons/9p/src/4.4.180/trans_virtio.c
Normal file
@@ -0,0 +1,788 @@
|
||||
/*
|
||||
* The Virtio 9p transport driver
|
||||
*
|
||||
* This is a block based transport driver based on the lguest block driver
|
||||
* code.
|
||||
*
|
||||
* Copyright (C) 2007, 2008 Eric Van Hensbergen, IBM Corporation
|
||||
*
|
||||
* Based on virtio console driver
|
||||
* Copyright (C) 2006, 2007 Rusty Russell, IBM Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/in.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/un.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <linux/parser.h>
|
||||
#include <net/9p/client.h>
|
||||
#include <net/9p/transport.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/swap.h>
|
||||
#include <linux/virtio.h>
|
||||
#include <linux/virtio_9p.h>
|
||||
#include "trans_common.h"
|
||||
|
||||
#define VIRTQUEUE_NUM 128
|
||||
|
||||
/* a single mutex to manage channel initialization and attachment */
|
||||
static DEFINE_MUTEX(virtio_9p_lock);
|
||||
static DECLARE_WAIT_QUEUE_HEAD(vp_wq);
|
||||
static atomic_t vp_pinned = ATOMIC_INIT(0);
|
||||
|
||||
/**
|
||||
* struct virtio_chan - per-instance transport information
|
||||
* @initialized: whether the channel is initialized
|
||||
* @inuse: whether the channel is in use
|
||||
* @lock: protects multiple elements within this structure
|
||||
* @client: client instance
|
||||
* @vdev: virtio dev associated with this channel
|
||||
* @vq: virtio queue associated with this channel
|
||||
* @sg: scatter gather list which is used to pack a request (protected?)
|
||||
*
|
||||
* We keep all per-channel information in a structure.
|
||||
* This structure is allocated within the devices dev->mem space.
|
||||
* A pointer to the structure will get put in the transport private.
|
||||
*
|
||||
*/
|
||||
|
||||
struct virtio_chan {
|
||||
bool inuse;
|
||||
|
||||
spinlock_t lock;
|
||||
|
||||
struct p9_client *client;
|
||||
struct virtio_device *vdev;
|
||||
struct virtqueue *vq;
|
||||
int ring_bufs_avail;
|
||||
wait_queue_head_t *vc_wq;
|
||||
/* This is global limit. Since we don't have a global structure,
|
||||
* will be placing it in each channel.
|
||||
*/
|
||||
unsigned long p9_max_pages;
|
||||
/* Scatterlist: can be too big for stack. */
|
||||
struct scatterlist sg[VIRTQUEUE_NUM];
|
||||
|
||||
int tag_len;
|
||||
/*
|
||||
* tag name to identify a mount Non-null terminated
|
||||
*/
|
||||
char *tag;
|
||||
|
||||
struct list_head chan_list;
|
||||
};
|
||||
|
||||
static struct list_head virtio_chan_list;
|
||||
|
||||
/* How many bytes left in this page. */
|
||||
static unsigned int rest_of_page(void *data)
|
||||
{
|
||||
return PAGE_SIZE - ((unsigned long)data % PAGE_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* p9_virtio_close - reclaim resources of a channel
|
||||
* @client: client instance
|
||||
*
|
||||
* This reclaims a channel by freeing its resources and
|
||||
* reseting its inuse flag.
|
||||
*
|
||||
*/
|
||||
|
||||
static void p9_virtio_close(struct p9_client *client)
|
||||
{
|
||||
struct virtio_chan *chan = client->trans;
|
||||
|
||||
mutex_lock(&virtio_9p_lock);
|
||||
if (chan)
|
||||
chan->inuse = false;
|
||||
mutex_unlock(&virtio_9p_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* req_done - callback which signals activity from the server
|
||||
* @vq: virtio queue activity was received on
|
||||
*
|
||||
* This notifies us that the server has triggered some activity
|
||||
* on the virtio channel - most likely a response to request we
|
||||
* sent. Figure out which requests now have responses and wake up
|
||||
* those threads.
|
||||
*
|
||||
* Bugs: could do with some additional sanity checking, but appears to work.
|
||||
*
|
||||
*/
|
||||
|
||||
static void req_done(struct virtqueue *vq)
|
||||
{
|
||||
struct virtio_chan *chan = vq->vdev->priv;
|
||||
struct p9_fcall *rc;
|
||||
unsigned int len;
|
||||
struct p9_req_t *req;
|
||||
unsigned long flags;
|
||||
|
||||
p9_debug(P9_DEBUG_TRANS, ": request done\n");
|
||||
|
||||
while (1) {
|
||||
spin_lock_irqsave(&chan->lock, flags);
|
||||
rc = virtqueue_get_buf(chan->vq, &len);
|
||||
if (rc == NULL) {
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
break;
|
||||
}
|
||||
chan->ring_bufs_avail = 1;
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
/* Wakeup if anyone waiting for VirtIO ring space. */
|
||||
wake_up(chan->vc_wq);
|
||||
p9_debug(P9_DEBUG_TRANS, ": rc %p\n", rc);
|
||||
p9_debug(P9_DEBUG_TRANS, ": lookup tag %d\n", rc->tag);
|
||||
req = p9_tag_lookup(chan->client, rc->tag);
|
||||
p9_client_cb(chan->client, req, REQ_STATUS_RCVD);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pack_sg_list - pack a scatter gather list from a linear buffer
|
||||
* @sg: scatter/gather list to pack into
|
||||
* @start: which segment of the sg_list to start at
|
||||
* @limit: maximum segment to pack data to
|
||||
* @data: data to pack into scatter/gather list
|
||||
* @count: amount of data to pack into the scatter/gather list
|
||||
*
|
||||
* sg_lists have multiple segments of various sizes. This will pack
|
||||
* arbitrary data into an existing scatter gather list, segmenting the
|
||||
* data as necessary within constraints.
|
||||
*
|
||||
*/
|
||||
|
||||
static int pack_sg_list(struct scatterlist *sg, int start,
|
||||
int limit, char *data, int count)
|
||||
{
|
||||
int s;
|
||||
int index = start;
|
||||
|
||||
while (count) {
|
||||
s = rest_of_page(data);
|
||||
if (s > count)
|
||||
s = count;
|
||||
BUG_ON(index >= limit);
|
||||
/* Make sure we don't terminate early. */
|
||||
sg_unmark_end(&sg[index]);
|
||||
sg_set_buf(&sg[index++], data, s);
|
||||
count -= s;
|
||||
data += s;
|
||||
}
|
||||
if (index-start)
|
||||
sg_mark_end(&sg[index - 1]);
|
||||
return index-start;
|
||||
}
|
||||
|
||||
/* We don't currently allow canceling of virtio requests */
|
||||
static int p9_virtio_cancel(struct p9_client *client, struct p9_req_t *req)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* pack_sg_list_p - Just like pack_sg_list. Instead of taking a buffer,
|
||||
* this takes a list of pages.
|
||||
* @sg: scatter/gather list to pack into
|
||||
* @start: which segment of the sg_list to start at
|
||||
* @pdata: a list of pages to add into sg.
|
||||
* @nr_pages: number of pages to pack into the scatter/gather list
|
||||
* @offs: amount of data in the beginning of first page _not_ to pack
|
||||
* @count: amount of data to pack into the scatter/gather list
|
||||
*/
|
||||
static int
|
||||
pack_sg_list_p(struct scatterlist *sg, int start, int limit,
|
||||
struct page **pdata, int nr_pages, size_t offs, int count)
|
||||
{
|
||||
int i = 0, s;
|
||||
int data_off = offs;
|
||||
int index = start;
|
||||
|
||||
BUG_ON(nr_pages > (limit - start));
|
||||
/*
|
||||
* if the first page doesn't start at
|
||||
* page boundary find the offset
|
||||
*/
|
||||
while (nr_pages) {
|
||||
s = PAGE_SIZE - data_off;
|
||||
if (s > count)
|
||||
s = count;
|
||||
BUG_ON(index >= limit);
|
||||
/* Make sure we don't terminate early. */
|
||||
sg_unmark_end(&sg[index]);
|
||||
sg_set_page(&sg[index++], pdata[i++], s, data_off);
|
||||
data_off = 0;
|
||||
count -= s;
|
||||
nr_pages--;
|
||||
}
|
||||
|
||||
if (index-start)
|
||||
sg_mark_end(&sg[index - 1]);
|
||||
return index - start;
|
||||
}
|
||||
|
||||
/**
|
||||
* p9_virtio_request - issue a request
|
||||
* @client: client instance issuing the request
|
||||
* @req: request to be issued
|
||||
*
|
||||
*/
|
||||
|
||||
static int
|
||||
p9_virtio_request(struct p9_client *client, struct p9_req_t *req)
|
||||
{
|
||||
int err;
|
||||
int in, out, out_sgs, in_sgs;
|
||||
unsigned long flags;
|
||||
struct virtio_chan *chan = client->trans;
|
||||
struct scatterlist *sgs[2];
|
||||
|
||||
p9_debug(P9_DEBUG_TRANS, "9p debug: virtio request\n");
|
||||
|
||||
req->status = REQ_STATUS_SENT;
|
||||
req_retry:
|
||||
spin_lock_irqsave(&chan->lock, flags);
|
||||
|
||||
out_sgs = in_sgs = 0;
|
||||
/* Handle out VirtIO ring buffers */
|
||||
out = pack_sg_list(chan->sg, 0,
|
||||
VIRTQUEUE_NUM, req->tc->sdata, req->tc->size);
|
||||
if (out)
|
||||
sgs[out_sgs++] = chan->sg;
|
||||
|
||||
in = pack_sg_list(chan->sg, out,
|
||||
VIRTQUEUE_NUM, req->rc->sdata, req->rc->capacity);
|
||||
if (in)
|
||||
sgs[out_sgs + in_sgs++] = chan->sg + out;
|
||||
|
||||
err = virtqueue_add_sgs(chan->vq, sgs, out_sgs, in_sgs, req->tc,
|
||||
GFP_ATOMIC);
|
||||
if (err < 0) {
|
||||
if (err == -ENOSPC) {
|
||||
chan->ring_bufs_avail = 0;
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
err = wait_event_killable(*chan->vc_wq,
|
||||
chan->ring_bufs_avail);
|
||||
if (err == -ERESTARTSYS)
|
||||
return err;
|
||||
|
||||
p9_debug(P9_DEBUG_TRANS, "Retry virtio request\n");
|
||||
goto req_retry;
|
||||
} else {
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
p9_debug(P9_DEBUG_TRANS,
|
||||
"virtio rpc add_sgs returned failure\n");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
virtqueue_kick(chan->vq);
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
|
||||
p9_debug(P9_DEBUG_TRANS, "virtio request kicked\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int p9_get_mapped_pages(struct virtio_chan *chan,
|
||||
struct page ***pages,
|
||||
struct iov_iter *data,
|
||||
int count,
|
||||
size_t *offs,
|
||||
int *need_drop)
|
||||
{
|
||||
int nr_pages;
|
||||
int err;
|
||||
|
||||
if (!iov_iter_count(data))
|
||||
return 0;
|
||||
|
||||
if (!(data->type & ITER_KVEC)) {
|
||||
int n;
|
||||
/*
|
||||
* We allow only p9_max_pages pinned. We wait for the
|
||||
* Other zc request to finish here
|
||||
*/
|
||||
if (atomic_read(&vp_pinned) >= chan->p9_max_pages) {
|
||||
err = wait_event_killable(vp_wq,
|
||||
(atomic_read(&vp_pinned) < chan->p9_max_pages));
|
||||
if (err == -ERESTARTSYS)
|
||||
return err;
|
||||
}
|
||||
n = iov_iter_get_pages_alloc(data, pages, count, offs);
|
||||
if (n < 0)
|
||||
return n;
|
||||
*need_drop = 1;
|
||||
nr_pages = DIV_ROUND_UP(n + *offs, PAGE_SIZE);
|
||||
atomic_add(nr_pages, &vp_pinned);
|
||||
return n;
|
||||
} else {
|
||||
/* kernel buffer, no need to pin pages */
|
||||
int index;
|
||||
size_t len;
|
||||
void *p;
|
||||
|
||||
/* we'd already checked that it's non-empty */
|
||||
while (1) {
|
||||
len = iov_iter_single_seg_count(data);
|
||||
if (likely(len)) {
|
||||
p = data->kvec->iov_base + data->iov_offset;
|
||||
break;
|
||||
}
|
||||
iov_iter_advance(data, 0);
|
||||
}
|
||||
if (len > count)
|
||||
len = count;
|
||||
|
||||
nr_pages = DIV_ROUND_UP((unsigned long)p + len, PAGE_SIZE) -
|
||||
(unsigned long)p / PAGE_SIZE;
|
||||
|
||||
*pages = kmalloc(sizeof(struct page *) * nr_pages, GFP_NOFS);
|
||||
if (!*pages)
|
||||
return -ENOMEM;
|
||||
|
||||
*need_drop = 0;
|
||||
p -= (*offs = (unsigned long)p % PAGE_SIZE);
|
||||
for (index = 0; index < nr_pages; index++) {
|
||||
if (is_vmalloc_addr(p))
|
||||
(*pages)[index] = vmalloc_to_page(p);
|
||||
else
|
||||
(*pages)[index] = kmap_to_page(p);
|
||||
p += PAGE_SIZE;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* p9_virtio_zc_request - issue a zero copy request
|
||||
* @client: client instance issuing the request
|
||||
* @req: request to be issued
|
||||
* @uidata: user bffer that should be ued for zero copy read
|
||||
* @uodata: user buffer that shoud be user for zero copy write
|
||||
* @inlen: read buffer size
|
||||
* @olen: write buffer size
|
||||
* @hdrlen: reader header size, This is the size of response protocol data
|
||||
*
|
||||
*/
|
||||
static int
|
||||
p9_virtio_zc_request(struct p9_client *client, struct p9_req_t *req,
|
||||
struct iov_iter *uidata, struct iov_iter *uodata,
|
||||
int inlen, int outlen, int in_hdr_len)
|
||||
{
|
||||
int in, out, err, out_sgs, in_sgs;
|
||||
unsigned long flags;
|
||||
int in_nr_pages = 0, out_nr_pages = 0;
|
||||
struct page **in_pages = NULL, **out_pages = NULL;
|
||||
struct virtio_chan *chan = client->trans;
|
||||
struct scatterlist *sgs[4];
|
||||
size_t offs;
|
||||
int need_drop = 0;
|
||||
|
||||
p9_debug(P9_DEBUG_TRANS, "virtio request\n");
|
||||
|
||||
if (uodata) {
|
||||
__le32 sz;
|
||||
int n = p9_get_mapped_pages(chan, &out_pages, uodata,
|
||||
outlen, &offs, &need_drop);
|
||||
if (n < 0)
|
||||
return n;
|
||||
out_nr_pages = DIV_ROUND_UP(n + offs, PAGE_SIZE);
|
||||
if (n != outlen) {
|
||||
__le32 v = cpu_to_le32(n);
|
||||
memcpy(&req->tc->sdata[req->tc->size - 4], &v, 4);
|
||||
outlen = n;
|
||||
}
|
||||
/* The size field of the message must include the length of the
|
||||
* header and the length of the data. We didn't actually know
|
||||
* the length of the data until this point so add it in now.
|
||||
*/
|
||||
sz = cpu_to_le32(req->tc->size + outlen);
|
||||
memcpy(&req->tc->sdata[0], &sz, sizeof(sz));
|
||||
} else if (uidata) {
|
||||
int n = p9_get_mapped_pages(chan, &in_pages, uidata,
|
||||
inlen, &offs, &need_drop);
|
||||
if (n < 0)
|
||||
return n;
|
||||
in_nr_pages = DIV_ROUND_UP(n + offs, PAGE_SIZE);
|
||||
if (n != inlen) {
|
||||
__le32 v = cpu_to_le32(n);
|
||||
memcpy(&req->tc->sdata[req->tc->size - 4], &v, 4);
|
||||
inlen = n;
|
||||
}
|
||||
}
|
||||
req->status = REQ_STATUS_SENT;
|
||||
req_retry_pinned:
|
||||
spin_lock_irqsave(&chan->lock, flags);
|
||||
|
||||
out_sgs = in_sgs = 0;
|
||||
|
||||
/* out data */
|
||||
out = pack_sg_list(chan->sg, 0,
|
||||
VIRTQUEUE_NUM, req->tc->sdata, req->tc->size);
|
||||
|
||||
if (out)
|
||||
sgs[out_sgs++] = chan->sg;
|
||||
|
||||
if (out_pages) {
|
||||
sgs[out_sgs++] = chan->sg + out;
|
||||
out += pack_sg_list_p(chan->sg, out, VIRTQUEUE_NUM,
|
||||
out_pages, out_nr_pages, offs, outlen);
|
||||
}
|
||||
|
||||
/*
|
||||
* Take care of in data
|
||||
* For example TREAD have 11.
|
||||
* 11 is the read/write header = PDU Header(7) + IO Size (4).
|
||||
* Arrange in such a way that server places header in the
|
||||
* alloced memory and payload onto the user buffer.
|
||||
*/
|
||||
in = pack_sg_list(chan->sg, out,
|
||||
VIRTQUEUE_NUM, req->rc->sdata, in_hdr_len);
|
||||
if (in)
|
||||
sgs[out_sgs + in_sgs++] = chan->sg + out;
|
||||
|
||||
if (in_pages) {
|
||||
sgs[out_sgs + in_sgs++] = chan->sg + out + in;
|
||||
in += pack_sg_list_p(chan->sg, out + in, VIRTQUEUE_NUM,
|
||||
in_pages, in_nr_pages, offs, inlen);
|
||||
}
|
||||
|
||||
BUG_ON(out_sgs + in_sgs > ARRAY_SIZE(sgs));
|
||||
err = virtqueue_add_sgs(chan->vq, sgs, out_sgs, in_sgs, req->tc,
|
||||
GFP_ATOMIC);
|
||||
if (err < 0) {
|
||||
if (err == -ENOSPC) {
|
||||
chan->ring_bufs_avail = 0;
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
err = wait_event_killable(*chan->vc_wq,
|
||||
chan->ring_bufs_avail);
|
||||
if (err == -ERESTARTSYS)
|
||||
goto err_out;
|
||||
|
||||
p9_debug(P9_DEBUG_TRANS, "Retry virtio request\n");
|
||||
goto req_retry_pinned;
|
||||
} else {
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
p9_debug(P9_DEBUG_TRANS,
|
||||
"virtio rpc add_sgs returned failure\n");
|
||||
err = -EIO;
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
virtqueue_kick(chan->vq);
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
p9_debug(P9_DEBUG_TRANS, "virtio request kicked\n");
|
||||
err = wait_event_killable(*req->wq, req->status >= REQ_STATUS_RCVD);
|
||||
/*
|
||||
* Non kernel buffers are pinned, unpin them
|
||||
*/
|
||||
err_out:
|
||||
if (need_drop) {
|
||||
if (in_pages) {
|
||||
p9_release_pages(in_pages, in_nr_pages);
|
||||
atomic_sub(in_nr_pages, &vp_pinned);
|
||||
}
|
||||
if (out_pages) {
|
||||
p9_release_pages(out_pages, out_nr_pages);
|
||||
atomic_sub(out_nr_pages, &vp_pinned);
|
||||
}
|
||||
/* wakeup anybody waiting for slots to pin pages */
|
||||
wake_up(&vp_wq);
|
||||
}
|
||||
kfree(in_pages);
|
||||
kfree(out_pages);
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t p9_mount_tag_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct virtio_chan *chan;
|
||||
struct virtio_device *vdev;
|
||||
|
||||
vdev = dev_to_virtio(dev);
|
||||
chan = vdev->priv;
|
||||
|
||||
memcpy(buf, chan->tag, chan->tag_len);
|
||||
buf[chan->tag_len] = 0;
|
||||
|
||||
return chan->tag_len + 1;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(mount_tag, 0444, p9_mount_tag_show, NULL);
|
||||
|
||||
/**
|
||||
* p9_virtio_probe - probe for existence of 9P virtio channels
|
||||
* @vdev: virtio device to probe
|
||||
*
|
||||
* This probes for existing virtio channels.
|
||||
*
|
||||
*/
|
||||
|
||||
static int p9_virtio_probe(struct virtio_device *vdev)
|
||||
{
|
||||
__u16 tag_len;
|
||||
char *tag;
|
||||
int err;
|
||||
struct virtio_chan *chan;
|
||||
|
||||
if (!vdev->config->get) {
|
||||
dev_err(&vdev->dev, "%s failure: config access disabled\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chan = kmalloc(sizeof(struct virtio_chan), GFP_KERNEL);
|
||||
if (!chan) {
|
||||
pr_err("Failed to allocate virtio 9P channel\n");
|
||||
err = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
chan->vdev = vdev;
|
||||
|
||||
/* We expect one virtqueue, for requests. */
|
||||
chan->vq = virtio_find_single_vq(vdev, req_done, "requests");
|
||||
if (IS_ERR(chan->vq)) {
|
||||
err = PTR_ERR(chan->vq);
|
||||
goto out_free_chan;
|
||||
}
|
||||
chan->vq->vdev->priv = chan;
|
||||
spin_lock_init(&chan->lock);
|
||||
|
||||
sg_init_table(chan->sg, VIRTQUEUE_NUM);
|
||||
|
||||
chan->inuse = false;
|
||||
if (virtio_has_feature(vdev, VIRTIO_9P_MOUNT_TAG)) {
|
||||
virtio_cread(vdev, struct virtio_9p_config, tag_len, &tag_len);
|
||||
} else {
|
||||
err = -EINVAL;
|
||||
goto out_free_vq;
|
||||
}
|
||||
tag = kmalloc(tag_len, GFP_KERNEL);
|
||||
if (!tag) {
|
||||
err = -ENOMEM;
|
||||
goto out_free_vq;
|
||||
}
|
||||
|
||||
virtio_cread_bytes(vdev, offsetof(struct virtio_9p_config, tag),
|
||||
tag, tag_len);
|
||||
chan->tag = tag;
|
||||
chan->tag_len = tag_len;
|
||||
err = sysfs_create_file(&(vdev->dev.kobj), &dev_attr_mount_tag.attr);
|
||||
if (err) {
|
||||
goto out_free_tag;
|
||||
}
|
||||
chan->vc_wq = kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL);
|
||||
if (!chan->vc_wq) {
|
||||
err = -ENOMEM;
|
||||
goto out_free_tag;
|
||||
}
|
||||
init_waitqueue_head(chan->vc_wq);
|
||||
chan->ring_bufs_avail = 1;
|
||||
/* Ceiling limit to avoid denial of service attacks */
|
||||
chan->p9_max_pages = nr_free_buffer_pages()/4;
|
||||
|
||||
virtio_device_ready(vdev);
|
||||
|
||||
mutex_lock(&virtio_9p_lock);
|
||||
list_add_tail(&chan->chan_list, &virtio_chan_list);
|
||||
mutex_unlock(&virtio_9p_lock);
|
||||
|
||||
/* Let udev rules use the new mount_tag attribute. */
|
||||
kobject_uevent(&(vdev->dev.kobj), KOBJ_CHANGE);
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_tag:
|
||||
kfree(tag);
|
||||
out_free_vq:
|
||||
vdev->config->del_vqs(vdev);
|
||||
out_free_chan:
|
||||
kfree(chan);
|
||||
fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* p9_virtio_create - allocate a new virtio channel
|
||||
* @client: client instance invoking this transport
|
||||
* @devname: string identifying the channel to connect to (unused)
|
||||
* @args: args passed from sys_mount() for per-transport options (unused)
|
||||
*
|
||||
* This sets up a transport channel for 9p communication. Right now
|
||||
* we only match the first available channel, but eventually we couldlook up
|
||||
* alternate channels by matching devname versus a virtio_config entry.
|
||||
* We use a simple reference count mechanism to ensure that only a single
|
||||
* mount has a channel open at a time.
|
||||
*
|
||||
*/
|
||||
|
||||
static int
|
||||
p9_virtio_create(struct p9_client *client, const char *devname, char *args)
|
||||
{
|
||||
struct virtio_chan *chan;
|
||||
int ret = -ENOENT;
|
||||
int found = 0;
|
||||
|
||||
if (devname == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&virtio_9p_lock);
|
||||
list_for_each_entry(chan, &virtio_chan_list, chan_list) {
|
||||
if (!strncmp(devname, chan->tag, chan->tag_len) &&
|
||||
strlen(devname) == chan->tag_len) {
|
||||
if (!chan->inuse) {
|
||||
chan->inuse = true;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
ret = -EBUSY;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&virtio_9p_lock);
|
||||
|
||||
if (!found) {
|
||||
pr_err("no channels available\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
client->trans = (void *)chan;
|
||||
client->status = Connected;
|
||||
chan->client = client;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* p9_virtio_remove - clean up resources associated with a virtio device
|
||||
* @vdev: virtio device to remove
|
||||
*
|
||||
*/
|
||||
|
||||
static void p9_virtio_remove(struct virtio_device *vdev)
|
||||
{
|
||||
struct virtio_chan *chan = vdev->priv;
|
||||
unsigned long warning_time;
|
||||
|
||||
mutex_lock(&virtio_9p_lock);
|
||||
|
||||
/* Remove self from list so we don't get new users. */
|
||||
list_del(&chan->chan_list);
|
||||
warning_time = jiffies;
|
||||
|
||||
/* Wait for existing users to close. */
|
||||
while (chan->inuse) {
|
||||
mutex_unlock(&virtio_9p_lock);
|
||||
msleep(250);
|
||||
if (time_after(jiffies, warning_time + 10 * HZ)) {
|
||||
dev_emerg(&vdev->dev,
|
||||
"p9_virtio_remove: waiting for device in use.\n");
|
||||
warning_time = jiffies;
|
||||
}
|
||||
mutex_lock(&virtio_9p_lock);
|
||||
}
|
||||
|
||||
mutex_unlock(&virtio_9p_lock);
|
||||
|
||||
vdev->config->reset(vdev);
|
||||
vdev->config->del_vqs(vdev);
|
||||
|
||||
sysfs_remove_file(&(vdev->dev.kobj), &dev_attr_mount_tag.attr);
|
||||
kobject_uevent(&(vdev->dev.kobj), KOBJ_CHANGE);
|
||||
kfree(chan->tag);
|
||||
kfree(chan->vc_wq);
|
||||
kfree(chan);
|
||||
|
||||
}
|
||||
|
||||
static struct virtio_device_id id_table[] = {
|
||||
{ VIRTIO_ID_9P, VIRTIO_DEV_ANY_ID },
|
||||
{ 0 },
|
||||
};
|
||||
|
||||
static unsigned int features[] = {
|
||||
VIRTIO_9P_MOUNT_TAG,
|
||||
};
|
||||
|
||||
/* The standard "struct lguest_driver": */
|
||||
static struct virtio_driver p9_virtio_drv = {
|
||||
.feature_table = features,
|
||||
.feature_table_size = ARRAY_SIZE(features),
|
||||
.driver.name = KBUILD_MODNAME,
|
||||
.driver.owner = THIS_MODULE,
|
||||
.id_table = id_table,
|
||||
.probe = p9_virtio_probe,
|
||||
.remove = p9_virtio_remove,
|
||||
};
|
||||
|
||||
static struct p9_trans_module p9_virtio_trans = {
|
||||
.name = "virtio",
|
||||
.create = p9_virtio_create,
|
||||
.close = p9_virtio_close,
|
||||
.request = p9_virtio_request,
|
||||
.zc_request = p9_virtio_zc_request,
|
||||
.cancel = p9_virtio_cancel,
|
||||
/*
|
||||
* We leave one entry for input and one entry for response
|
||||
* headers. We also skip one more entry to accomodate, address
|
||||
* that are not at page boundary, that can result in an extra
|
||||
* page in zero copy.
|
||||
*/
|
||||
.maxsize = PAGE_SIZE * (VIRTQUEUE_NUM - 3),
|
||||
.def = 1,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
/* The standard init function */
|
||||
static int __init p9_virtio_init(void)
|
||||
{
|
||||
INIT_LIST_HEAD(&virtio_chan_list);
|
||||
|
||||
v9fs_register_trans(&p9_virtio_trans);
|
||||
return register_virtio_driver(&p9_virtio_drv);
|
||||
}
|
||||
|
||||
static void __exit p9_virtio_cleanup(void)
|
||||
{
|
||||
unregister_virtio_driver(&p9_virtio_drv);
|
||||
v9fs_unregister_trans(&p9_virtio_trans);
|
||||
}
|
||||
|
||||
module_init(p9_virtio_init);
|
||||
module_exit(p9_virtio_cleanup);
|
||||
|
||||
MODULE_DEVICE_TABLE(virtio, id_table);
|
||||
MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
|
||||
MODULE_DESCRIPTION("Virtio 9p Transport");
|
||||
MODULE_LICENSE("GPL");
|
||||
141
addons/9p/src/4.4.180/util.c
Normal file
141
addons/9p/src/4.4.180/util.c
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* net/9p/util.c
|
||||
*
|
||||
* This file contains some helper functions
|
||||
*
|
||||
* Copyright (C) 2007 by Latchesar Ionkov <lucho@ionkov.net>
|
||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/parser.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/9p/9p.h>
|
||||
|
||||
/**
|
||||
* struct p9_idpool - per-connection accounting for tag idpool
|
||||
* @lock: protects the pool
|
||||
* @pool: idr to allocate tag id from
|
||||
*
|
||||
*/
|
||||
|
||||
struct p9_idpool {
|
||||
spinlock_t lock;
|
||||
struct idr pool;
|
||||
};
|
||||
|
||||
/**
|
||||
* p9_idpool_create - create a new per-connection id pool
|
||||
*
|
||||
*/
|
||||
|
||||
struct p9_idpool *p9_idpool_create(void)
|
||||
{
|
||||
struct p9_idpool *p;
|
||||
|
||||
p = kmalloc(sizeof(struct p9_idpool), GFP_KERNEL);
|
||||
if (!p)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
spin_lock_init(&p->lock);
|
||||
idr_init(&p->pool);
|
||||
|
||||
return p;
|
||||
}
|
||||
EXPORT_SYMBOL(p9_idpool_create);
|
||||
|
||||
/**
|
||||
* p9_idpool_destroy - create a new per-connection id pool
|
||||
* @p: idpool to destroy
|
||||
*/
|
||||
|
||||
void p9_idpool_destroy(struct p9_idpool *p)
|
||||
{
|
||||
idr_destroy(&p->pool);
|
||||
kfree(p);
|
||||
}
|
||||
EXPORT_SYMBOL(p9_idpool_destroy);
|
||||
|
||||
/**
|
||||
* p9_idpool_get - allocate numeric id from pool
|
||||
* @p: pool to allocate from
|
||||
*
|
||||
* Bugs: This seems to be an awful generic function, should it be in idr.c with
|
||||
* the lock included in struct idr?
|
||||
*/
|
||||
|
||||
int p9_idpool_get(struct p9_idpool *p)
|
||||
{
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
idr_preload(GFP_NOFS);
|
||||
spin_lock_irqsave(&p->lock, flags);
|
||||
|
||||
/* no need to store exactly p, we just need something non-null */
|
||||
i = idr_alloc(&p->pool, p, 0, 0, GFP_NOWAIT);
|
||||
|
||||
spin_unlock_irqrestore(&p->lock, flags);
|
||||
idr_preload_end();
|
||||
if (i < 0)
|
||||
return -1;
|
||||
|
||||
p9_debug(P9_DEBUG_MUX, " id %d pool %p\n", i, p);
|
||||
return i;
|
||||
}
|
||||
EXPORT_SYMBOL(p9_idpool_get);
|
||||
|
||||
/**
|
||||
* p9_idpool_put - release numeric id from pool
|
||||
* @id: numeric id which is being released
|
||||
* @p: pool to release id into
|
||||
*
|
||||
* Bugs: This seems to be an awful generic function, should it be in idr.c with
|
||||
* the lock included in struct idr?
|
||||
*/
|
||||
|
||||
void p9_idpool_put(int id, struct p9_idpool *p)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
p9_debug(P9_DEBUG_MUX, " id %d pool %p\n", id, p);
|
||||
|
||||
spin_lock_irqsave(&p->lock, flags);
|
||||
idr_remove(&p->pool, id);
|
||||
spin_unlock_irqrestore(&p->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(p9_idpool_put);
|
||||
|
||||
/**
|
||||
* p9_idpool_check - check if the specified id is available
|
||||
* @id: id to check
|
||||
* @p: pool to check
|
||||
*/
|
||||
|
||||
int p9_idpool_check(int id, struct p9_idpool *p)
|
||||
{
|
||||
return idr_find(&p->pool, id) != NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(p9_idpool_check);
|
||||
|
||||
700
addons/9p/src/4.4.180/v9fs.c
Normal file
700
addons/9p/src/4.4.180/v9fs.c
Normal file
@@ -0,0 +1,700 @@
|
||||
/*
|
||||
* linux/fs/9p/v9fs.c
|
||||
*
|
||||
* This file contains functions assisting in mapping VFS to 9P2000
|
||||
*
|
||||
* Copyright (C) 2004-2008 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/parser.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
#include <net/9p/transport.h>
|
||||
#include "v9fs.h"
|
||||
#include "v9fs_vfs.h"
|
||||
#include "cache.h"
|
||||
|
||||
static DEFINE_SPINLOCK(v9fs_sessionlist_lock);
|
||||
static LIST_HEAD(v9fs_sessionlist);
|
||||
struct kmem_cache *v9fs_inode_cache;
|
||||
|
||||
/*
|
||||
* Option Parsing (code inspired by NFS code)
|
||||
* NOTE: each transport will parse its own options
|
||||
*/
|
||||
|
||||
enum {
|
||||
/* Options that take integer arguments */
|
||||
Opt_debug, Opt_dfltuid, Opt_dfltgid, Opt_afid,
|
||||
/* String options */
|
||||
Opt_uname, Opt_remotename, Opt_cache, Opt_cachetag,
|
||||
/* Options that take no arguments */
|
||||
Opt_nodevmap,
|
||||
/* Cache options */
|
||||
Opt_cache_loose, Opt_fscache, Opt_mmap,
|
||||
/* Access options */
|
||||
Opt_access, Opt_posixacl,
|
||||
/* Lock timeout option */
|
||||
Opt_locktimeout,
|
||||
/* Error token */
|
||||
Opt_err
|
||||
};
|
||||
|
||||
static const match_table_t tokens = {
|
||||
{Opt_debug, "debug=%x"},
|
||||
{Opt_dfltuid, "dfltuid=%u"},
|
||||
{Opt_dfltgid, "dfltgid=%u"},
|
||||
{Opt_afid, "afid=%u"},
|
||||
{Opt_uname, "uname=%s"},
|
||||
{Opt_remotename, "aname=%s"},
|
||||
{Opt_nodevmap, "nodevmap"},
|
||||
{Opt_cache, "cache=%s"},
|
||||
{Opt_cache_loose, "loose"},
|
||||
{Opt_fscache, "fscache"},
|
||||
{Opt_mmap, "mmap"},
|
||||
{Opt_cachetag, "cachetag=%s"},
|
||||
{Opt_access, "access=%s"},
|
||||
{Opt_posixacl, "posixacl"},
|
||||
{Opt_locktimeout, "locktimeout=%u"},
|
||||
{Opt_err, NULL}
|
||||
};
|
||||
|
||||
/* Interpret mount options for cache mode */
|
||||
static int get_cache_mode(char *s)
|
||||
{
|
||||
int version = -EINVAL;
|
||||
|
||||
if (!strcmp(s, "loose")) {
|
||||
version = CACHE_LOOSE;
|
||||
p9_debug(P9_DEBUG_9P, "Cache mode: loose\n");
|
||||
} else if (!strcmp(s, "fscache")) {
|
||||
version = CACHE_FSCACHE;
|
||||
p9_debug(P9_DEBUG_9P, "Cache mode: fscache\n");
|
||||
} else if (!strcmp(s, "mmap")) {
|
||||
version = CACHE_MMAP;
|
||||
p9_debug(P9_DEBUG_9P, "Cache mode: mmap\n");
|
||||
} else if (!strcmp(s, "none")) {
|
||||
version = CACHE_NONE;
|
||||
p9_debug(P9_DEBUG_9P, "Cache mode: none\n");
|
||||
} else
|
||||
pr_info("Unknown Cache mode %s\n", s);
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_parse_options - parse mount options into session structure
|
||||
* @v9ses: existing v9fs session information
|
||||
*
|
||||
* Return 0 upon success, -ERRNO upon failure.
|
||||
*/
|
||||
|
||||
static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
|
||||
{
|
||||
char *options, *tmp_options;
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
char *p;
|
||||
int option = 0;
|
||||
char *s, *e;
|
||||
int ret = 0;
|
||||
|
||||
/* setup defaults */
|
||||
v9ses->afid = ~0;
|
||||
v9ses->debug = 0;
|
||||
v9ses->cache = CACHE_NONE;
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
v9ses->cachetag = NULL;
|
||||
#endif
|
||||
v9ses->session_lock_timeout = P9_LOCK_TIMEOUT;
|
||||
|
||||
if (!opts)
|
||||
return 0;
|
||||
|
||||
tmp_options = kstrdup(opts, GFP_KERNEL);
|
||||
if (!tmp_options) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_option_alloc;
|
||||
}
|
||||
options = tmp_options;
|
||||
|
||||
while ((p = strsep(&options, ",")) != NULL) {
|
||||
int token, r;
|
||||
if (!*p)
|
||||
continue;
|
||||
token = match_token(p, tokens, args);
|
||||
switch (token) {
|
||||
case Opt_debug:
|
||||
r = match_int(&args[0], &option);
|
||||
if (r < 0) {
|
||||
p9_debug(P9_DEBUG_ERROR,
|
||||
"integer field, but no integer?\n");
|
||||
ret = r;
|
||||
continue;
|
||||
}
|
||||
v9ses->debug = option;
|
||||
#ifdef CONFIG_NET_9P_DEBUG
|
||||
p9_debug_level = option;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case Opt_dfltuid:
|
||||
r = match_int(&args[0], &option);
|
||||
if (r < 0) {
|
||||
p9_debug(P9_DEBUG_ERROR,
|
||||
"integer field, but no integer?\n");
|
||||
ret = r;
|
||||
continue;
|
||||
}
|
||||
v9ses->dfltuid = make_kuid(current_user_ns(), option);
|
||||
if (!uid_valid(v9ses->dfltuid)) {
|
||||
p9_debug(P9_DEBUG_ERROR,
|
||||
"uid field, but not a uid?\n");
|
||||
ret = -EINVAL;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case Opt_dfltgid:
|
||||
r = match_int(&args[0], &option);
|
||||
if (r < 0) {
|
||||
p9_debug(P9_DEBUG_ERROR,
|
||||
"integer field, but no integer?\n");
|
||||
ret = r;
|
||||
continue;
|
||||
}
|
||||
v9ses->dfltgid = make_kgid(current_user_ns(), option);
|
||||
if (!gid_valid(v9ses->dfltgid)) {
|
||||
p9_debug(P9_DEBUG_ERROR,
|
||||
"gid field, but not a gid?\n");
|
||||
ret = -EINVAL;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case Opt_afid:
|
||||
r = match_int(&args[0], &option);
|
||||
if (r < 0) {
|
||||
p9_debug(P9_DEBUG_ERROR,
|
||||
"integer field, but no integer?\n");
|
||||
ret = r;
|
||||
continue;
|
||||
}
|
||||
v9ses->afid = option;
|
||||
break;
|
||||
case Opt_uname:
|
||||
kfree(v9ses->uname);
|
||||
v9ses->uname = match_strdup(&args[0]);
|
||||
if (!v9ses->uname) {
|
||||
ret = -ENOMEM;
|
||||
goto free_and_return;
|
||||
}
|
||||
break;
|
||||
case Opt_remotename:
|
||||
kfree(v9ses->aname);
|
||||
v9ses->aname = match_strdup(&args[0]);
|
||||
if (!v9ses->aname) {
|
||||
ret = -ENOMEM;
|
||||
goto free_and_return;
|
||||
}
|
||||
break;
|
||||
case Opt_nodevmap:
|
||||
v9ses->nodev = 1;
|
||||
break;
|
||||
case Opt_cache_loose:
|
||||
v9ses->cache = CACHE_LOOSE;
|
||||
break;
|
||||
case Opt_fscache:
|
||||
v9ses->cache = CACHE_FSCACHE;
|
||||
break;
|
||||
case Opt_mmap:
|
||||
v9ses->cache = CACHE_MMAP;
|
||||
break;
|
||||
case Opt_cachetag:
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
v9ses->cachetag = match_strdup(&args[0]);
|
||||
#endif
|
||||
break;
|
||||
case Opt_cache:
|
||||
s = match_strdup(&args[0]);
|
||||
if (!s) {
|
||||
ret = -ENOMEM;
|
||||
p9_debug(P9_DEBUG_ERROR,
|
||||
"problem allocating copy of cache arg\n");
|
||||
goto free_and_return;
|
||||
}
|
||||
ret = get_cache_mode(s);
|
||||
if (ret == -EINVAL) {
|
||||
kfree(s);
|
||||
goto free_and_return;
|
||||
}
|
||||
|
||||
v9ses->cache = ret;
|
||||
kfree(s);
|
||||
break;
|
||||
|
||||
case Opt_access:
|
||||
s = match_strdup(&args[0]);
|
||||
if (!s) {
|
||||
ret = -ENOMEM;
|
||||
p9_debug(P9_DEBUG_ERROR,
|
||||
"problem allocating copy of access arg\n");
|
||||
goto free_and_return;
|
||||
}
|
||||
|
||||
v9ses->flags &= ~V9FS_ACCESS_MASK;
|
||||
if (strcmp(s, "user") == 0)
|
||||
v9ses->flags |= V9FS_ACCESS_USER;
|
||||
else if (strcmp(s, "any") == 0)
|
||||
v9ses->flags |= V9FS_ACCESS_ANY;
|
||||
else if (strcmp(s, "client") == 0) {
|
||||
v9ses->flags |= V9FS_ACCESS_CLIENT;
|
||||
} else {
|
||||
uid_t uid;
|
||||
v9ses->flags |= V9FS_ACCESS_SINGLE;
|
||||
uid = simple_strtoul(s, &e, 10);
|
||||
if (*e != '\0') {
|
||||
ret = -EINVAL;
|
||||
pr_info("Unknown access argument %s\n",
|
||||
s);
|
||||
kfree(s);
|
||||
goto free_and_return;
|
||||
}
|
||||
v9ses->uid = make_kuid(current_user_ns(), uid);
|
||||
if (!uid_valid(v9ses->uid)) {
|
||||
ret = -EINVAL;
|
||||
pr_info("Uknown uid %s\n", s);
|
||||
kfree(s);
|
||||
goto free_and_return;
|
||||
}
|
||||
}
|
||||
|
||||
kfree(s);
|
||||
break;
|
||||
|
||||
case Opt_posixacl:
|
||||
#ifdef CONFIG_9P_FS_POSIX_ACL
|
||||
v9ses->flags |= V9FS_POSIX_ACL;
|
||||
#else
|
||||
p9_debug(P9_DEBUG_ERROR,
|
||||
"Not defined CONFIG_9P_FS_POSIX_ACL. Ignoring posixacl option\n");
|
||||
#endif
|
||||
break;
|
||||
|
||||
case Opt_locktimeout:
|
||||
r = match_int(&args[0], &option);
|
||||
if (r < 0) {
|
||||
p9_debug(P9_DEBUG_ERROR,
|
||||
"integer field, but no integer?\n");
|
||||
ret = r;
|
||||
continue;
|
||||
}
|
||||
if (option < 1) {
|
||||
p9_debug(P9_DEBUG_ERROR,
|
||||
"locktimeout must be a greater than zero integer.\n");
|
||||
ret = -EINVAL;
|
||||
continue;
|
||||
}
|
||||
v9ses->session_lock_timeout = (long)option * HZ;
|
||||
break;
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
free_and_return:
|
||||
kfree(tmp_options);
|
||||
fail_option_alloc:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_session_init - initialize session
|
||||
* @v9ses: session information structure
|
||||
* @dev_name: device being mounted
|
||||
* @data: options
|
||||
*
|
||||
*/
|
||||
|
||||
struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
|
||||
const char *dev_name, char *data)
|
||||
{
|
||||
struct p9_fid *fid;
|
||||
int rc = -ENOMEM;
|
||||
|
||||
v9ses->uname = kstrdup(V9FS_DEFUSER, GFP_KERNEL);
|
||||
if (!v9ses->uname)
|
||||
goto err_names;
|
||||
|
||||
v9ses->aname = kstrdup(V9FS_DEFANAME, GFP_KERNEL);
|
||||
if (!v9ses->aname)
|
||||
goto err_names;
|
||||
init_rwsem(&v9ses->rename_sem);
|
||||
|
||||
rc = bdi_setup_and_register(&v9ses->bdi, "9p");
|
||||
if (rc)
|
||||
goto err_names;
|
||||
|
||||
v9ses->uid = INVALID_UID;
|
||||
v9ses->dfltuid = V9FS_DEFUID;
|
||||
v9ses->dfltgid = V9FS_DEFGID;
|
||||
|
||||
v9ses->clnt = p9_client_create(dev_name, data);
|
||||
if (IS_ERR(v9ses->clnt)) {
|
||||
rc = PTR_ERR(v9ses->clnt);
|
||||
p9_debug(P9_DEBUG_ERROR, "problem initializing 9p client\n");
|
||||
goto err_bdi;
|
||||
}
|
||||
|
||||
v9ses->flags = V9FS_ACCESS_USER;
|
||||
|
||||
if (p9_is_proto_dotl(v9ses->clnt)) {
|
||||
v9ses->flags = V9FS_ACCESS_CLIENT;
|
||||
v9ses->flags |= V9FS_PROTO_2000L;
|
||||
} else if (p9_is_proto_dotu(v9ses->clnt)) {
|
||||
v9ses->flags |= V9FS_PROTO_2000U;
|
||||
}
|
||||
|
||||
rc = v9fs_parse_options(v9ses, data);
|
||||
if (rc < 0)
|
||||
goto err_clnt;
|
||||
|
||||
v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ;
|
||||
|
||||
if (!v9fs_proto_dotl(v9ses) &&
|
||||
((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) {
|
||||
/*
|
||||
* We support ACCESS_CLIENT only for dotl.
|
||||
* Fall back to ACCESS_USER
|
||||
*/
|
||||
v9ses->flags &= ~V9FS_ACCESS_MASK;
|
||||
v9ses->flags |= V9FS_ACCESS_USER;
|
||||
}
|
||||
/*FIXME !! */
|
||||
/* for legacy mode, fall back to V9FS_ACCESS_ANY */
|
||||
if (!(v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses)) &&
|
||||
((v9ses->flags&V9FS_ACCESS_MASK) == V9FS_ACCESS_USER)) {
|
||||
|
||||
v9ses->flags &= ~V9FS_ACCESS_MASK;
|
||||
v9ses->flags |= V9FS_ACCESS_ANY;
|
||||
v9ses->uid = INVALID_UID;
|
||||
}
|
||||
if (!v9fs_proto_dotl(v9ses) ||
|
||||
!((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) {
|
||||
/*
|
||||
* We support ACL checks on clinet only if the protocol is
|
||||
* 9P2000.L and access is V9FS_ACCESS_CLIENT.
|
||||
*/
|
||||
v9ses->flags &= ~V9FS_ACL_MASK;
|
||||
}
|
||||
|
||||
fid = p9_client_attach(v9ses->clnt, NULL, v9ses->uname, INVALID_UID,
|
||||
v9ses->aname);
|
||||
if (IS_ERR(fid)) {
|
||||
rc = PTR_ERR(fid);
|
||||
p9_debug(P9_DEBUG_ERROR, "cannot attach\n");
|
||||
goto err_clnt;
|
||||
}
|
||||
|
||||
if ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_SINGLE)
|
||||
fid->uid = v9ses->uid;
|
||||
else
|
||||
fid->uid = INVALID_UID;
|
||||
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
/* register the session for caching */
|
||||
v9fs_cache_session_get_cookie(v9ses);
|
||||
#endif
|
||||
spin_lock(&v9fs_sessionlist_lock);
|
||||
list_add(&v9ses->slist, &v9fs_sessionlist);
|
||||
spin_unlock(&v9fs_sessionlist_lock);
|
||||
|
||||
return fid;
|
||||
|
||||
err_clnt:
|
||||
p9_client_destroy(v9ses->clnt);
|
||||
err_bdi:
|
||||
bdi_destroy(&v9ses->bdi);
|
||||
err_names:
|
||||
kfree(v9ses->uname);
|
||||
kfree(v9ses->aname);
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_session_close - shutdown a session
|
||||
* @v9ses: session information structure
|
||||
*
|
||||
*/
|
||||
|
||||
void v9fs_session_close(struct v9fs_session_info *v9ses)
|
||||
{
|
||||
if (v9ses->clnt) {
|
||||
p9_client_destroy(v9ses->clnt);
|
||||
v9ses->clnt = NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
if (v9ses->fscache) {
|
||||
v9fs_cache_session_put_cookie(v9ses);
|
||||
kfree(v9ses->cachetag);
|
||||
}
|
||||
#endif
|
||||
kfree(v9ses->uname);
|
||||
kfree(v9ses->aname);
|
||||
|
||||
bdi_destroy(&v9ses->bdi);
|
||||
|
||||
spin_lock(&v9fs_sessionlist_lock);
|
||||
list_del(&v9ses->slist);
|
||||
spin_unlock(&v9fs_sessionlist_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_session_cancel - terminate a session
|
||||
* @v9ses: session to terminate
|
||||
*
|
||||
* mark transport as disconnected and cancel all pending requests.
|
||||
*/
|
||||
|
||||
void v9fs_session_cancel(struct v9fs_session_info *v9ses) {
|
||||
p9_debug(P9_DEBUG_ERROR, "cancel session %p\n", v9ses);
|
||||
p9_client_disconnect(v9ses->clnt);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_session_begin_cancel - Begin terminate of a session
|
||||
* @v9ses: session to terminate
|
||||
*
|
||||
* After this call we don't allow any request other than clunk.
|
||||
*/
|
||||
|
||||
void v9fs_session_begin_cancel(struct v9fs_session_info *v9ses)
|
||||
{
|
||||
p9_debug(P9_DEBUG_ERROR, "begin cancel session %p\n", v9ses);
|
||||
p9_client_begin_disconnect(v9ses->clnt);
|
||||
}
|
||||
|
||||
extern int v9fs_error_init(void);
|
||||
|
||||
static struct kobject *v9fs_kobj;
|
||||
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
/**
|
||||
* caches_show - list caches associated with a session
|
||||
*
|
||||
* Returns the size of buffer written.
|
||||
*/
|
||||
|
||||
static ssize_t caches_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
ssize_t n = 0, count = 0, limit = PAGE_SIZE;
|
||||
struct v9fs_session_info *v9ses;
|
||||
|
||||
spin_lock(&v9fs_sessionlist_lock);
|
||||
list_for_each_entry(v9ses, &v9fs_sessionlist, slist) {
|
||||
if (v9ses->cachetag) {
|
||||
n = snprintf(buf, limit, "%s\n", v9ses->cachetag);
|
||||
if (n < 0) {
|
||||
count = n;
|
||||
break;
|
||||
}
|
||||
|
||||
count += n;
|
||||
limit -= n;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock(&v9fs_sessionlist_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct kobj_attribute v9fs_attr_cache = __ATTR_RO(caches);
|
||||
#endif /* CONFIG_9P_FSCACHE */
|
||||
|
||||
static struct attribute *v9fs_attrs[] = {
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
&v9fs_attr_cache.attr,
|
||||
#endif
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group v9fs_attr_group = {
|
||||
.attrs = v9fs_attrs,
|
||||
};
|
||||
|
||||
/**
|
||||
* v9fs_sysfs_init - Initialize the v9fs sysfs interface
|
||||
*
|
||||
*/
|
||||
|
||||
static int __init v9fs_sysfs_init(void)
|
||||
{
|
||||
v9fs_kobj = kobject_create_and_add("9p", fs_kobj);
|
||||
if (!v9fs_kobj)
|
||||
return -ENOMEM;
|
||||
|
||||
if (sysfs_create_group(v9fs_kobj, &v9fs_attr_group)) {
|
||||
kobject_put(v9fs_kobj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_sysfs_cleanup - Unregister the v9fs sysfs interface
|
||||
*
|
||||
*/
|
||||
|
||||
static void v9fs_sysfs_cleanup(void)
|
||||
{
|
||||
sysfs_remove_group(v9fs_kobj, &v9fs_attr_group);
|
||||
kobject_put(v9fs_kobj);
|
||||
}
|
||||
|
||||
static void v9fs_inode_init_once(void *foo)
|
||||
{
|
||||
struct v9fs_inode *v9inode = (struct v9fs_inode *)foo;
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
v9inode->fscache = NULL;
|
||||
#endif
|
||||
memset(&v9inode->qid, 0, sizeof(v9inode->qid));
|
||||
inode_init_once(&v9inode->vfs_inode);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_init_inode_cache - initialize a cache for 9P
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
static int v9fs_init_inode_cache(void)
|
||||
{
|
||||
v9fs_inode_cache = kmem_cache_create("v9fs_inode_cache",
|
||||
sizeof(struct v9fs_inode),
|
||||
0, (SLAB_RECLAIM_ACCOUNT|
|
||||
SLAB_MEM_SPREAD),
|
||||
v9fs_inode_init_once);
|
||||
if (!v9fs_inode_cache)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_destroy_inode_cache - destroy the cache of 9P inode
|
||||
*
|
||||
*/
|
||||
static void v9fs_destroy_inode_cache(void)
|
||||
{
|
||||
/*
|
||||
* Make sure all delayed rcu free inodes are flushed before we
|
||||
* destroy cache.
|
||||
*/
|
||||
rcu_barrier();
|
||||
kmem_cache_destroy(v9fs_inode_cache);
|
||||
}
|
||||
|
||||
static int v9fs_cache_register(void)
|
||||
{
|
||||
int ret;
|
||||
ret = v9fs_init_inode_cache();
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
ret = fscache_register_netfs(&v9fs_cache_netfs);
|
||||
if (ret < 0)
|
||||
v9fs_destroy_inode_cache();
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void v9fs_cache_unregister(void)
|
||||
{
|
||||
v9fs_destroy_inode_cache();
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
fscache_unregister_netfs(&v9fs_cache_netfs);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* init_v9fs - Initialize module
|
||||
*
|
||||
*/
|
||||
|
||||
static int __init init_v9fs(void)
|
||||
{
|
||||
int err;
|
||||
pr_info("Installing v9fs 9p2000 file system support\n");
|
||||
/* TODO: Setup list of registered trasnport modules */
|
||||
|
||||
err = v9fs_cache_register();
|
||||
if (err < 0) {
|
||||
pr_err("Failed to register v9fs for caching\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = v9fs_sysfs_init();
|
||||
if (err < 0) {
|
||||
pr_err("Failed to register with sysfs\n");
|
||||
goto out_cache;
|
||||
}
|
||||
err = register_filesystem(&v9fs_fs_type);
|
||||
if (err < 0) {
|
||||
pr_err("Failed to register filesystem\n");
|
||||
goto out_sysfs_cleanup;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_sysfs_cleanup:
|
||||
v9fs_sysfs_cleanup();
|
||||
|
||||
out_cache:
|
||||
v9fs_cache_unregister();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* exit_v9fs - shutdown module
|
||||
*
|
||||
*/
|
||||
|
||||
static void __exit exit_v9fs(void)
|
||||
{
|
||||
v9fs_sysfs_cleanup();
|
||||
v9fs_cache_unregister();
|
||||
unregister_filesystem(&v9fs_fs_type);
|
||||
}
|
||||
|
||||
module_init(init_v9fs)
|
||||
module_exit(exit_v9fs)
|
||||
|
||||
MODULE_AUTHOR("Latchesar Ionkov <lucho@ionkov.net>");
|
||||
MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
|
||||
MODULE_AUTHOR("Ron Minnich <rminnich@lanl.gov>");
|
||||
MODULE_LICENSE("GPL");
|
||||
226
addons/9p/src/4.4.180/v9fs.h
Normal file
226
addons/9p/src/4.4.180/v9fs.h
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* V9FS definitions.
|
||||
*
|
||||
* Copyright (C) 2004-2008 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
#ifndef FS_9P_V9FS_H
|
||||
#define FS_9P_V9FS_H
|
||||
|
||||
#include <linux/backing-dev.h>
|
||||
|
||||
/**
|
||||
* enum p9_session_flags - option flags for each 9P session
|
||||
* @V9FS_PROTO_2000U: whether or not to use 9P2000.u extensions
|
||||
* @V9FS_PROTO_2000L: whether or not to use 9P2000.l extensions
|
||||
* @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
|
||||
*
|
||||
* 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,
|
||||
V9FS_ACCESS_SINGLE = 0x04,
|
||||
V9FS_ACCESS_USER = 0x08,
|
||||
V9FS_ACCESS_CLIENT = 0x10,
|
||||
V9FS_POSIX_ACL = 0x20
|
||||
};
|
||||
|
||||
/* possible values of ->cache */
|
||||
/**
|
||||
* enum p9_cache_modes - user specified cache preferences
|
||||
* @CACHE_NONE: do not cache data, dentries, or directory contents (default)
|
||||
* @CACHE_LOOSE: cache data, dentries, and directory contents w/no consistency
|
||||
*
|
||||
* eventually support loose, tight, time, session, default always none
|
||||
*/
|
||||
|
||||
enum p9_cache_modes {
|
||||
CACHE_NONE,
|
||||
CACHE_MMAP,
|
||||
CACHE_LOOSE,
|
||||
CACHE_FSCACHE,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct v9fs_session_info - per-instance session information
|
||||
* @flags: session options of type &p9_session_flags
|
||||
* @nodev: set to 1 to disable device mapping
|
||||
* @debug: debug level
|
||||
* @afid: authentication handle
|
||||
* @cache: cache mode of type &p9_cache_modes
|
||||
* @cachetag: the tag of the cache associated with this session
|
||||
* @fscache: session cookie associated with FS-Cache
|
||||
* @uname: string user name to mount hierarchy as
|
||||
* @aname: mount specifier for remote hierarchy
|
||||
* @maxdata: maximum data to be sent/recvd per protocol message
|
||||
* @dfltuid: default numeric userid to mount hierarchy as
|
||||
* @dfltgid: default numeric groupid to mount hierarchy as
|
||||
* @uid: if %V9FS_ACCESS_SINGLE, the numeric uid which mounted the hierarchy
|
||||
* @clnt: reference to 9P network client instantiated for this session
|
||||
* @slist: reference to list of registered 9p sessions
|
||||
*
|
||||
* This structure holds state for each session instance established during
|
||||
* a sys_mount() .
|
||||
*
|
||||
* Bugs: there seems to be a lot of state which could be condensed and/or
|
||||
* removed.
|
||||
*/
|
||||
|
||||
struct v9fs_session_info {
|
||||
/* options */
|
||||
unsigned char flags;
|
||||
unsigned char nodev;
|
||||
unsigned short debug;
|
||||
unsigned int afid;
|
||||
unsigned int cache;
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
char *cachetag;
|
||||
struct fscache_cookie *fscache;
|
||||
#endif
|
||||
|
||||
char *uname; /* user name to mount as */
|
||||
char *aname; /* name of remote hierarchy being mounted */
|
||||
unsigned int maxdata; /* max data for client interface */
|
||||
kuid_t dfltuid; /* default uid/muid for legacy support */
|
||||
kgid_t dfltgid; /* default gid for legacy support */
|
||||
kuid_t uid; /* if ACCESS_SINGLE, the uid that has access */
|
||||
struct p9_client *clnt; /* 9p client */
|
||||
struct list_head slist; /* list of sessions registered with v9fs */
|
||||
struct backing_dev_info bdi;
|
||||
struct rw_semaphore rename_sem;
|
||||
long session_lock_timeout; /* retry interval for blocking locks */
|
||||
};
|
||||
|
||||
/* cache_validity flags */
|
||||
#define V9FS_INO_INVALID_ATTR 0x01
|
||||
|
||||
struct v9fs_inode {
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
struct mutex fscache_lock;
|
||||
struct fscache_cookie *fscache;
|
||||
#endif
|
||||
struct p9_qid qid;
|
||||
unsigned int cache_validity;
|
||||
struct p9_fid *writeback_fid;
|
||||
struct mutex v_mutex;
|
||||
struct inode vfs_inode;
|
||||
};
|
||||
|
||||
static inline struct v9fs_inode *V9FS_I(const struct inode *inode)
|
||||
{
|
||||
return container_of(inode, struct v9fs_inode, vfs_inode);
|
||||
}
|
||||
|
||||
struct p9_fid *v9fs_session_init(struct v9fs_session_info *, const char *,
|
||||
char *);
|
||||
extern void v9fs_session_close(struct v9fs_session_info *v9ses);
|
||||
extern void v9fs_session_cancel(struct v9fs_session_info *v9ses);
|
||||
extern void v9fs_session_begin_cancel(struct v9fs_session_info *v9ses);
|
||||
extern struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
unsigned int flags);
|
||||
extern int v9fs_vfs_unlink(struct inode *i, struct dentry *d);
|
||||
extern int v9fs_vfs_rmdir(struct inode *i, struct dentry *d);
|
||||
extern int v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct inode *new_dir, struct dentry *new_dentry);
|
||||
extern struct inode *v9fs_inode_from_fid(struct v9fs_session_info *v9ses,
|
||||
struct p9_fid *fid,
|
||||
struct super_block *sb, int new);
|
||||
extern const struct inode_operations v9fs_dir_inode_operations_dotl;
|
||||
extern const struct inode_operations v9fs_file_inode_operations_dotl;
|
||||
extern const struct inode_operations v9fs_symlink_inode_operations_dotl;
|
||||
extern struct inode *v9fs_inode_from_fid_dotl(struct v9fs_session_info *v9ses,
|
||||
struct p9_fid *fid,
|
||||
struct super_block *sb, int new);
|
||||
|
||||
/* other default globals */
|
||||
#define V9FS_PORT 564
|
||||
#define V9FS_DEFUSER "nobody"
|
||||
#define V9FS_DEFANAME ""
|
||||
#define V9FS_DEFUID KUIDT_INIT(-2)
|
||||
#define V9FS_DEFGID KGIDT_INIT(-2)
|
||||
|
||||
static inline struct v9fs_session_info *v9fs_inode2v9ses(struct inode *inode)
|
||||
{
|
||||
return (inode->i_sb->s_fs_info);
|
||||
}
|
||||
|
||||
static inline struct v9fs_session_info *v9fs_dentry2v9ses(struct dentry *dentry)
|
||||
{
|
||||
return dentry->d_sb->s_fs_info;
|
||||
}
|
||||
|
||||
static inline int v9fs_proto_dotu(struct v9fs_session_info *v9ses)
|
||||
{
|
||||
return v9ses->flags & V9FS_PROTO_2000U;
|
||||
}
|
||||
|
||||
static inline int v9fs_proto_dotl(struct v9fs_session_info *v9ses)
|
||||
{
|
||||
return v9ses->flags & V9FS_PROTO_2000L;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_get_inode_from_fid - Helper routine to populate an inode by
|
||||
* issuing a attribute request
|
||||
* @v9ses: session information
|
||||
* @fid: fid to issue attribute request for
|
||||
* @sb: superblock on which to create inode
|
||||
*
|
||||
*/
|
||||
static inline struct inode *
|
||||
v9fs_get_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
|
||||
struct super_block *sb)
|
||||
{
|
||||
if (v9fs_proto_dotl(v9ses))
|
||||
return v9fs_inode_from_fid_dotl(v9ses, fid, sb, 0);
|
||||
else
|
||||
return v9fs_inode_from_fid(v9ses, fid, sb, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_get_new_inode_from_fid - Helper routine to populate an inode by
|
||||
* issuing a attribute request
|
||||
* @v9ses: session information
|
||||
* @fid: fid to issue attribute request for
|
||||
* @sb: superblock on which to create inode
|
||||
*
|
||||
*/
|
||||
static inline struct inode *
|
||||
v9fs_get_new_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
|
||||
struct super_block *sb)
|
||||
{
|
||||
if (v9fs_proto_dotl(v9ses))
|
||||
return v9fs_inode_from_fid_dotl(v9ses, fid, sb, 1);
|
||||
else
|
||||
return v9fs_inode_from_fid(v9ses, fid, sb, 1);
|
||||
}
|
||||
|
||||
#endif
|
||||
105
addons/9p/src/4.4.180/v9fs_vfs.h
Normal file
105
addons/9p/src/4.4.180/v9fs_vfs.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* V9FS VFS extensions.
|
||||
*
|
||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
#ifndef FS_9P_V9FS_VFS_H
|
||||
#define FS_9P_V9FS_VFS_H
|
||||
|
||||
/* plan9 semantics are that created files are implicitly opened.
|
||||
* But linux semantics are that you call create, then open.
|
||||
* the plan9 approach is superior as it provides an atomic
|
||||
* open.
|
||||
* we track the create fid here. When the file is opened, if fidopen is
|
||||
* non-zero, we use the fid and can skip some steps.
|
||||
* there may be a better way to do this, but I don't know it.
|
||||
* one BAD way is to clunk the fid on create, then open it again:
|
||||
* you lose the atomicity of file open
|
||||
*/
|
||||
|
||||
/* special case:
|
||||
* unlink calls remove, which is an implicit clunk. So we have to track
|
||||
* that kind of thing so that we don't try to clunk a dead fid.
|
||||
*/
|
||||
#define P9_LOCK_TIMEOUT (30*HZ)
|
||||
|
||||
/* flags for v9fs_stat2inode() & v9fs_stat2inode_dotl() */
|
||||
#define V9FS_STAT2INODE_KEEP_ISIZE 1
|
||||
|
||||
extern struct file_system_type v9fs_fs_type;
|
||||
extern const struct address_space_operations v9fs_addr_operations;
|
||||
extern const struct file_operations v9fs_file_operations;
|
||||
extern const struct file_operations v9fs_file_operations_dotl;
|
||||
extern const struct file_operations v9fs_dir_operations;
|
||||
extern const struct file_operations v9fs_dir_operations_dotl;
|
||||
extern const struct dentry_operations v9fs_dentry_operations;
|
||||
extern const struct dentry_operations v9fs_cached_dentry_operations;
|
||||
extern const struct file_operations v9fs_cached_file_operations;
|
||||
extern const struct file_operations v9fs_cached_file_operations_dotl;
|
||||
extern const struct file_operations v9fs_mmap_file_operations;
|
||||
extern const struct file_operations v9fs_mmap_file_operations_dotl;
|
||||
extern struct kmem_cache *v9fs_inode_cache;
|
||||
|
||||
struct inode *v9fs_alloc_inode(struct super_block *sb);
|
||||
void v9fs_destroy_inode(struct inode *inode);
|
||||
struct inode *v9fs_get_inode(struct super_block *sb, umode_t mode, dev_t);
|
||||
int v9fs_init_inode(struct v9fs_session_info *v9ses,
|
||||
struct inode *inode, umode_t mode, dev_t);
|
||||
void v9fs_evict_inode(struct inode *inode);
|
||||
ino_t v9fs_qid2ino(struct p9_qid *qid);
|
||||
void v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,
|
||||
struct super_block *sb, unsigned int flags);
|
||||
void v9fs_stat2inode_dotl(struct p9_stat_dotl *stat, struct inode *inode,
|
||||
unsigned int flags);
|
||||
int v9fs_dir_release(struct inode *inode, struct file *filp);
|
||||
int v9fs_file_open(struct inode *inode, struct file *file);
|
||||
void v9fs_inode2stat(struct inode *inode, struct p9_wstat *stat);
|
||||
int v9fs_uflags2omode(int uflags, int extended);
|
||||
|
||||
void v9fs_blank_wstat(struct p9_wstat *wstat);
|
||||
int v9fs_vfs_setattr_dotl(struct dentry *, struct iattr *);
|
||||
int v9fs_file_fsync_dotl(struct file *filp, loff_t start, loff_t end,
|
||||
int datasync);
|
||||
int v9fs_refresh_inode(struct p9_fid *fid, struct inode *inode);
|
||||
int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode);
|
||||
static inline void v9fs_invalidate_inode_attr(struct inode *inode)
|
||||
{
|
||||
struct v9fs_inode *v9inode;
|
||||
v9inode = V9FS_I(inode);
|
||||
v9inode->cache_validity |= V9FS_INO_INVALID_ATTR;
|
||||
return;
|
||||
}
|
||||
|
||||
int v9fs_open_to_dotl_flags(int flags);
|
||||
|
||||
static inline void v9fs_i_size_write(struct inode *inode, loff_t i_size)
|
||||
{
|
||||
/*
|
||||
* 32-bit need the lock, concurrent updates could break the
|
||||
* sequences and make i_size_read() loop forever.
|
||||
* 64-bit updates are atomic and can skip the locking.
|
||||
*/
|
||||
if (sizeof(i_size) > sizeof(long))
|
||||
spin_lock(&inode->i_lock);
|
||||
i_size_write(inode, i_size);
|
||||
if (sizeof(i_size) > sizeof(long))
|
||||
spin_unlock(&inode->i_lock);
|
||||
}
|
||||
#endif
|
||||
351
addons/9p/src/4.4.180/vfs_addr.c
Normal file
351
addons/9p/src/4.4.180/vfs_addr.c
Normal file
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
* linux/fs/9p/vfs_addr.c
|
||||
*
|
||||
* This file contians 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>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/uio.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
|
||||
#include "v9fs.h"
|
||||
#include "v9fs_vfs.h"
|
||||
#include "cache.h"
|
||||
#include "fid.h"
|
||||
|
||||
/**
|
||||
* v9fs_fid_readpage - read an entire page in from 9P
|
||||
*
|
||||
* @fid: fid being read
|
||||
* @page: structure to page
|
||||
*
|
||||
*/
|
||||
static int v9fs_fid_readpage(struct p9_fid *fid, struct page *page)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
struct bio_vec bvec = {.bv_page = page, .bv_len = PAGE_SIZE};
|
||||
struct iov_iter to;
|
||||
int retval, err;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "\n");
|
||||
|
||||
BUG_ON(!PageLocked(page));
|
||||
|
||||
retval = v9fs_readpage_from_fscache(inode, page);
|
||||
if (retval == 0)
|
||||
return retval;
|
||||
|
||||
iov_iter_bvec(&to, ITER_BVEC | READ, &bvec, 1, PAGE_SIZE);
|
||||
|
||||
retval = p9_client_read(fid, page_offset(page), &to, &err);
|
||||
if (err) {
|
||||
v9fs_uncache_page(inode, page);
|
||||
retval = err;
|
||||
goto done;
|
||||
}
|
||||
|
||||
zero_user(page, retval, PAGE_SIZE - retval);
|
||||
flush_dcache_page(page);
|
||||
SetPageUptodate(page);
|
||||
|
||||
v9fs_readpage_to_fscache(inode, page);
|
||||
retval = 0;
|
||||
|
||||
done:
|
||||
unlock_page(page);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_vfs_readpage - read an entire page in from 9P
|
||||
*
|
||||
* @filp: file being read
|
||||
* @page: structure to page
|
||||
*
|
||||
*/
|
||||
|
||||
static int v9fs_vfs_readpage(struct file *filp, struct page *page)
|
||||
{
|
||||
return v9fs_fid_readpage(filp->private_data, page);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_vfs_readpages - read a set of pages from 9P
|
||||
*
|
||||
* @filp: file being read
|
||||
* @mapping: the address space
|
||||
* @pages: list of pages to read
|
||||
* @nr_pages: count of pages to read
|
||||
*
|
||||
*/
|
||||
|
||||
static int v9fs_vfs_readpages(struct file *filp, struct address_space *mapping,
|
||||
struct list_head *pages, unsigned nr_pages)
|
||||
{
|
||||
int ret = 0;
|
||||
struct inode *inode;
|
||||
|
||||
inode = mapping->host;
|
||||
p9_debug(P9_DEBUG_VFS, "inode: %p file: %p\n", inode, filp);
|
||||
|
||||
ret = v9fs_readpages_from_fscache(inode, mapping, pages, &nr_pages);
|
||||
if (ret == 0)
|
||||
return ret;
|
||||
|
||||
ret = read_cache_pages(mapping, pages, (void *)v9fs_vfs_readpage, filp);
|
||||
p9_debug(P9_DEBUG_VFS, " = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_release_page - release the private state associated with a page
|
||||
*
|
||||
* Returns 1 if the page can be released, false otherwise.
|
||||
*/
|
||||
|
||||
static int v9fs_release_page(struct page *page, gfp_t gfp)
|
||||
{
|
||||
if (PagePrivate(page))
|
||||
return 0;
|
||||
return v9fs_fscache_release_page(page, gfp);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_invalidate_page - Invalidate a page completely or partially
|
||||
*
|
||||
* @page: structure to page
|
||||
* @offset: offset in the page
|
||||
*/
|
||||
|
||||
static void v9fs_invalidate_page(struct page *page, unsigned int offset,
|
||||
unsigned int length)
|
||||
{
|
||||
/*
|
||||
* If called with zero offset, we should release
|
||||
* the private state assocated with the page
|
||||
*/
|
||||
if (offset == 0 && length == PAGE_CACHE_SIZE)
|
||||
v9fs_fscache_invalidate_page(page);
|
||||
}
|
||||
|
||||
static int v9fs_vfs_writepage_locked(struct page *page)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
loff_t size = i_size_read(inode);
|
||||
struct iov_iter from;
|
||||
struct bio_vec bvec;
|
||||
int err, len;
|
||||
|
||||
if (page->index == size >> PAGE_CACHE_SHIFT)
|
||||
len = size & ~PAGE_CACHE_MASK;
|
||||
else
|
||||
len = PAGE_CACHE_SIZE;
|
||||
|
||||
bvec.bv_page = page;
|
||||
bvec.bv_offset = 0;
|
||||
bvec.bv_len = len;
|
||||
iov_iter_bvec(&from, ITER_BVEC | WRITE, &bvec, 1, len);
|
||||
|
||||
/* We should have writeback_fid always set */
|
||||
BUG_ON(!v9inode->writeback_fid);
|
||||
|
||||
set_page_writeback(page);
|
||||
|
||||
p9_client_write(v9inode->writeback_fid, page_offset(page), &from, &err);
|
||||
|
||||
end_page_writeback(page);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int v9fs_vfs_writepage(struct page *page, struct writeback_control *wbc)
|
||||
{
|
||||
int retval;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "page %p\n", page);
|
||||
|
||||
retval = v9fs_vfs_writepage_locked(page);
|
||||
if (retval < 0) {
|
||||
if (retval == -EAGAIN) {
|
||||
redirty_page_for_writepage(wbc, page);
|
||||
retval = 0;
|
||||
} else {
|
||||
SetPageError(page);
|
||||
mapping_set_error(page->mapping, retval);
|
||||
}
|
||||
} else
|
||||
retval = 0;
|
||||
|
||||
unlock_page(page);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_launder_page - Writeback a dirty page
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
|
||||
static int v9fs_launder_page(struct page *page)
|
||||
{
|
||||
int retval;
|
||||
struct inode *inode = page->mapping->host;
|
||||
|
||||
v9fs_fscache_wait_on_page_write(inode, page);
|
||||
if (clear_page_dirty_for_io(page)) {
|
||||
retval = v9fs_vfs_writepage_locked(page);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_direct_IO - 9P address space operation for direct I/O
|
||||
* @iocb: target I/O control block
|
||||
* @pos: offset in file to begin the operation
|
||||
*
|
||||
* The presence of v9fs_direct_IO() in the address space ops vector
|
||||
* allowes open() O_DIRECT flags which would have failed otherwise.
|
||||
*
|
||||
* In the non-cached mode, we shunt off direct read and write requests before
|
||||
* the VFS gets them, so this method should never be called.
|
||||
*
|
||||
* Direct IO is not 'yet' supported in the cached mode. Hence when
|
||||
* this routine is called through generic_file_aio_read(), the read/write fails
|
||||
* with an error.
|
||||
*
|
||||
*/
|
||||
static ssize_t
|
||||
v9fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter, loff_t pos)
|
||||
{
|
||||
struct file *file = iocb->ki_filp;
|
||||
ssize_t n;
|
||||
int err = 0;
|
||||
if (iov_iter_rw(iter) == WRITE) {
|
||||
n = p9_client_write(file->private_data, pos, iter, &err);
|
||||
if (n) {
|
||||
struct inode *inode = file_inode(file);
|
||||
loff_t i_size = i_size_read(inode);
|
||||
if (pos + n > i_size)
|
||||
inode_add_bytes(inode, pos + n - i_size);
|
||||
}
|
||||
} else {
|
||||
n = p9_client_read(file->private_data, pos, iter, &err);
|
||||
}
|
||||
return n ? n : err;
|
||||
}
|
||||
|
||||
static int v9fs_write_begin(struct file *filp, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned flags,
|
||||
struct page **pagep, void **fsdata)
|
||||
{
|
||||
int retval = 0;
|
||||
struct page *page;
|
||||
struct v9fs_inode *v9inode;
|
||||
pgoff_t index = pos >> PAGE_CACHE_SHIFT;
|
||||
struct inode *inode = mapping->host;
|
||||
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "filp %p, mapping %p\n", filp, mapping);
|
||||
|
||||
v9inode = V9FS_I(inode);
|
||||
start:
|
||||
page = grab_cache_page_write_begin(mapping, index, flags);
|
||||
if (!page) {
|
||||
retval = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
BUG_ON(!v9inode->writeback_fid);
|
||||
if (PageUptodate(page))
|
||||
goto out;
|
||||
|
||||
if (len == PAGE_CACHE_SIZE)
|
||||
goto out;
|
||||
|
||||
retval = v9fs_fid_readpage(v9inode->writeback_fid, page);
|
||||
page_cache_release(page);
|
||||
if (!retval)
|
||||
goto start;
|
||||
out:
|
||||
*pagep = page;
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int v9fs_write_end(struct file *filp, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied,
|
||||
struct page *page, void *fsdata)
|
||||
{
|
||||
loff_t last_pos = pos + copied;
|
||||
struct inode *inode = page->mapping->host;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "filp %p, mapping %p\n", filp, mapping);
|
||||
|
||||
if (unlikely(copied < len)) {
|
||||
/*
|
||||
* zero out the rest of the area
|
||||
*/
|
||||
unsigned from = pos & (PAGE_CACHE_SIZE - 1);
|
||||
|
||||
zero_user(page, from + copied, len - copied);
|
||||
flush_dcache_page(page);
|
||||
}
|
||||
|
||||
if (!PageUptodate(page))
|
||||
SetPageUptodate(page);
|
||||
/*
|
||||
* No need to use i_size_read() here, the i_size
|
||||
* cannot change under us because we hold the i_mutex.
|
||||
*/
|
||||
if (last_pos > inode->i_size) {
|
||||
inode_add_bytes(inode, last_pos - inode->i_size);
|
||||
i_size_write(inode, last_pos);
|
||||
}
|
||||
set_page_dirty(page);
|
||||
unlock_page(page);
|
||||
page_cache_release(page);
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
|
||||
const struct address_space_operations v9fs_addr_operations = {
|
||||
.readpage = v9fs_vfs_readpage,
|
||||
.readpages = v9fs_vfs_readpages,
|
||||
.set_page_dirty = __set_page_dirty_nobuffers,
|
||||
.writepage = v9fs_vfs_writepage,
|
||||
.write_begin = v9fs_write_begin,
|
||||
.write_end = v9fs_write_end,
|
||||
.releasepage = v9fs_release_page,
|
||||
.invalidatepage = v9fs_invalidate_page,
|
||||
.launder_page = v9fs_launder_page,
|
||||
.direct_IO = v9fs_direct_IO,
|
||||
};
|
||||
122
addons/9p/src/4.4.180/vfs_dentry.c
Normal file
122
addons/9p/src/4.4.180/vfs_dentry.c
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* linux/fs/9p/vfs_dentry.c
|
||||
*
|
||||
* This file contians 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>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
|
||||
#include "v9fs.h"
|
||||
#include "v9fs_vfs.h"
|
||||
#include "fid.h"
|
||||
|
||||
/**
|
||||
* v9fs_cached_dentry_delete - called when dentry refcount equals 0
|
||||
* @dentry: dentry in question
|
||||
*
|
||||
*/
|
||||
static int v9fs_cached_dentry_delete(const struct dentry *dentry)
|
||||
{
|
||||
p9_debug(P9_DEBUG_VFS, " dentry: %pd (%p)\n",
|
||||
dentry, dentry);
|
||||
|
||||
/* Don't cache negative dentries */
|
||||
if (d_really_is_negative(dentry))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_dentry_release - called when dentry is going to be freed
|
||||
* @dentry: dentry that is being release
|
||||
*
|
||||
*/
|
||||
|
||||
static void v9fs_dentry_release(struct dentry *dentry)
|
||||
{
|
||||
struct hlist_node *p, *n;
|
||||
p9_debug(P9_DEBUG_VFS, " dentry: %pd (%p)\n",
|
||||
dentry, dentry);
|
||||
hlist_for_each_safe(p, n, (struct hlist_head *)&dentry->d_fsdata)
|
||||
p9_client_clunk(hlist_entry(p, struct p9_fid, dlist));
|
||||
dentry->d_fsdata = NULL;
|
||||
}
|
||||
|
||||
static int v9fs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
|
||||
{
|
||||
struct p9_fid *fid;
|
||||
struct inode *inode;
|
||||
struct v9fs_inode *v9inode;
|
||||
|
||||
if (flags & LOOKUP_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
inode = d_inode(dentry);
|
||||
if (!inode)
|
||||
goto out_valid;
|
||||
|
||||
v9inode = V9FS_I(inode);
|
||||
if (v9inode->cache_validity & V9FS_INO_INVALID_ATTR) {
|
||||
int retval;
|
||||
struct v9fs_session_info *v9ses;
|
||||
fid = v9fs_fid_lookup(dentry);
|
||||
if (IS_ERR(fid))
|
||||
return PTR_ERR(fid);
|
||||
|
||||
v9ses = v9fs_inode2v9ses(inode);
|
||||
if (v9fs_proto_dotl(v9ses))
|
||||
retval = v9fs_refresh_inode_dotl(fid, inode);
|
||||
else
|
||||
retval = v9fs_refresh_inode(fid, inode);
|
||||
if (retval == -ENOENT)
|
||||
return 0;
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
}
|
||||
out_valid:
|
||||
return 1;
|
||||
}
|
||||
|
||||
const struct dentry_operations v9fs_cached_dentry_operations = {
|
||||
.d_revalidate = v9fs_lookup_revalidate,
|
||||
.d_weak_revalidate = v9fs_lookup_revalidate,
|
||||
.d_delete = v9fs_cached_dentry_delete,
|
||||
.d_release = v9fs_dentry_release,
|
||||
};
|
||||
|
||||
const struct dentry_operations v9fs_dentry_operations = {
|
||||
.d_delete = always_delete_dentry,
|
||||
.d_release = v9fs_dentry_release,
|
||||
};
|
||||
248
addons/9p/src/4.4.180/vfs_dir.c
Normal file
248
addons/9p/src/4.4.180/vfs_dir.c
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* linux/fs/9p/vfs_dir.c
|
||||
*
|
||||
* This file contains vfs directory ops for the 9P2000 protocol.
|
||||
*
|
||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uio.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
|
||||
#include "v9fs.h"
|
||||
#include "v9fs_vfs.h"
|
||||
#include "fid.h"
|
||||
|
||||
/**
|
||||
* struct p9_rdir - readdir accounting
|
||||
* @head: start offset of current dirread buffer
|
||||
* @tail: end offset of current dirread buffer
|
||||
* @buf: dirread buffer
|
||||
*
|
||||
* private structure for keeping track of readdir
|
||||
* allocated on demand
|
||||
*/
|
||||
|
||||
struct p9_rdir {
|
||||
int head;
|
||||
int tail;
|
||||
uint8_t buf[];
|
||||
};
|
||||
|
||||
/**
|
||||
* dt_type - return file type
|
||||
* @mistat: mistat structure
|
||||
*
|
||||
*/
|
||||
|
||||
static inline int dt_type(struct p9_wstat *mistat)
|
||||
{
|
||||
unsigned long perm = mistat->mode;
|
||||
int rettype = DT_REG;
|
||||
|
||||
if (perm & P9_DMDIR)
|
||||
rettype = DT_DIR;
|
||||
if (perm & P9_DMSYMLINK)
|
||||
rettype = DT_LNK;
|
||||
|
||||
return rettype;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_alloc_rdir_buf - Allocate buffer used for read and readdir
|
||||
* @filp: opened file structure
|
||||
* @buflen: Length in bytes of buffer to allocate
|
||||
*
|
||||
*/
|
||||
|
||||
static struct p9_rdir *v9fs_alloc_rdir_buf(struct file *filp, int buflen)
|
||||
{
|
||||
struct p9_fid *fid = filp->private_data;
|
||||
if (!fid->rdir)
|
||||
fid->rdir = kzalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL);
|
||||
return fid->rdir;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_dir_readdir - iterate through a directory
|
||||
* @file: opened file structure
|
||||
* @ctx: actor we feed the entries to
|
||||
*
|
||||
*/
|
||||
|
||||
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;
|
||||
int buflen;
|
||||
struct p9_rdir *rdir;
|
||||
struct kvec kvec;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "name %pD\n", file);
|
||||
fid = file->private_data;
|
||||
|
||||
buflen = fid->clnt->msize - P9_IOHDRSZ;
|
||||
|
||||
rdir = v9fs_alloc_rdir_buf(file, buflen);
|
||||
if (!rdir)
|
||||
return -ENOMEM;
|
||||
kvec.iov_base = rdir->buf;
|
||||
kvec.iov_len = buflen;
|
||||
|
||||
while (1) {
|
||||
if (rdir->tail == rdir->head) {
|
||||
struct iov_iter to;
|
||||
int n;
|
||||
iov_iter_kvec(&to, READ | ITER_KVEC, &kvec, 1, buflen);
|
||||
n = p9_client_read(file->private_data, ctx->pos, &to,
|
||||
&err);
|
||||
if (err)
|
||||
return err;
|
||||
if (n == 0)
|
||||
return 0;
|
||||
|
||||
rdir->head = 0;
|
||||
rdir->tail = n;
|
||||
}
|
||||
while (rdir->head < rdir->tail) {
|
||||
err = p9stat_read(fid->clnt, rdir->buf + rdir->head,
|
||||
rdir->tail - rdir->head, &st);
|
||||
if (err <= 0) {
|
||||
p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
over = !dir_emit(ctx, st.name, strlen(st.name),
|
||||
v9fs_qid2ino(&st.qid), dt_type(&st));
|
||||
p9stat_free(&st);
|
||||
if (over)
|
||||
return 0;
|
||||
|
||||
rdir->head += err;
|
||||
ctx->pos += err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_dir_readdir_dotl - iterate through a directory
|
||||
* @file: opened file structure
|
||||
* @ctx: actor we feed the entries to
|
||||
*
|
||||
*/
|
||||
static int v9fs_dir_readdir_dotl(struct file *file, struct dir_context *ctx)
|
||||
{
|
||||
int err = 0;
|
||||
struct p9_fid *fid;
|
||||
int buflen;
|
||||
struct p9_rdir *rdir;
|
||||
struct p9_dirent curdirent;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "name %pD\n", file);
|
||||
fid = file->private_data;
|
||||
|
||||
buflen = fid->clnt->msize - P9_READDIRHDRSZ;
|
||||
|
||||
rdir = v9fs_alloc_rdir_buf(file, buflen);
|
||||
if (!rdir)
|
||||
return -ENOMEM;
|
||||
|
||||
while (1) {
|
||||
if (rdir->tail == rdir->head) {
|
||||
err = p9_client_readdir(fid, rdir->buf, buflen,
|
||||
ctx->pos);
|
||||
if (err <= 0)
|
||||
return err;
|
||||
|
||||
rdir->head = 0;
|
||||
rdir->tail = err;
|
||||
}
|
||||
|
||||
while (rdir->head < rdir->tail) {
|
||||
|
||||
err = p9dirent_read(fid->clnt, rdir->buf + rdir->head,
|
||||
rdir->tail - rdir->head,
|
||||
&curdirent);
|
||||
if (err < 0) {
|
||||
p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!dir_emit(ctx, curdirent.d_name,
|
||||
strlen(curdirent.d_name),
|
||||
v9fs_qid2ino(&curdirent.qid),
|
||||
curdirent.d_type))
|
||||
return 0;
|
||||
|
||||
ctx->pos = curdirent.d_off;
|
||||
rdir->head += err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* v9fs_dir_release - close a directory
|
||||
* @inode: inode of the directory
|
||||
* @filp: file pointer to a directory
|
||||
*
|
||||
*/
|
||||
|
||||
int v9fs_dir_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct p9_fid *fid;
|
||||
|
||||
fid = filp->private_data;
|
||||
p9_debug(P9_DEBUG_VFS, "inode: %p filp: %p fid: %d\n",
|
||||
inode, filp, fid ? fid->fid : -1);
|
||||
if (fid)
|
||||
p9_client_clunk(fid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct file_operations v9fs_dir_operations = {
|
||||
.read = generic_read_dir,
|
||||
.llseek = generic_file_llseek,
|
||||
.iterate = v9fs_dir_readdir,
|
||||
.open = v9fs_file_open,
|
||||
.release = v9fs_dir_release,
|
||||
};
|
||||
|
||||
const struct file_operations v9fs_dir_operations_dotl = {
|
||||
.read = generic_read_dir,
|
||||
.llseek = generic_file_llseek,
|
||||
.iterate = v9fs_dir_readdir_dotl,
|
||||
.open = v9fs_file_open,
|
||||
.release = v9fs_dir_release,
|
||||
.fsync = v9fs_file_fsync_dotl,
|
||||
};
|
||||
726
addons/9p/src/4.4.180/vfs_file.c
Normal file
726
addons/9p/src/4.4.180/vfs_file.c
Normal file
@@ -0,0 +1,726 @@
|
||||
/*
|
||||
* linux/fs/9p/vfs_file.c
|
||||
*
|
||||
* This file contians vfs file ops for 9P2000.
|
||||
*
|
||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/uio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
|
||||
#include "v9fs.h"
|
||||
#include "v9fs_vfs.h"
|
||||
#include "fid.h"
|
||||
#include "cache.h"
|
||||
|
||||
static const struct vm_operations_struct v9fs_file_vm_ops;
|
||||
static const struct vm_operations_struct v9fs_mmap_file_vm_ops;
|
||||
|
||||
/**
|
||||
* v9fs_file_open - open a file (or directory)
|
||||
* @inode: inode to be opened
|
||||
* @file: file being opened
|
||||
*
|
||||
*/
|
||||
|
||||
int v9fs_file_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int err;
|
||||
struct v9fs_inode *v9inode;
|
||||
struct v9fs_session_info *v9ses;
|
||||
struct p9_fid *fid;
|
||||
int omode;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "inode: %p file: %p\n", inode, file);
|
||||
v9inode = V9FS_I(inode);
|
||||
v9ses = v9fs_inode2v9ses(inode);
|
||||
if (v9fs_proto_dotl(v9ses))
|
||||
omode = v9fs_open_to_dotl_flags(file->f_flags);
|
||||
else
|
||||
omode = v9fs_uflags2omode(file->f_flags,
|
||||
v9fs_proto_dotu(v9ses));
|
||||
fid = file->private_data;
|
||||
if (!fid) {
|
||||
fid = v9fs_fid_clone(file_dentry(file));
|
||||
if (IS_ERR(fid))
|
||||
return PTR_ERR(fid);
|
||||
|
||||
err = p9_client_open(fid, omode);
|
||||
if (err < 0) {
|
||||
p9_client_clunk(fid);
|
||||
return err;
|
||||
}
|
||||
if ((file->f_flags & O_APPEND) &&
|
||||
(!v9fs_proto_dotu(v9ses) && !v9fs_proto_dotl(v9ses)))
|
||||
generic_file_llseek(file, 0, SEEK_END);
|
||||
}
|
||||
|
||||
file->private_data = fid;
|
||||
mutex_lock(&v9inode->v_mutex);
|
||||
if ((v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) &&
|
||||
!v9inode->writeback_fid &&
|
||||
((file->f_flags & O_ACCMODE) != O_RDONLY)) {
|
||||
/*
|
||||
* clone a fid and add it to writeback_fid
|
||||
* we do it during open time instead of
|
||||
* page dirty time via write_begin/page_mkwrite
|
||||
* because we want write after unlink usecase
|
||||
* to work.
|
||||
*/
|
||||
fid = v9fs_writeback_fid(file_dentry(file));
|
||||
if (IS_ERR(fid)) {
|
||||
err = PTR_ERR(fid);
|
||||
mutex_unlock(&v9inode->v_mutex);
|
||||
goto out_error;
|
||||
}
|
||||
v9inode->writeback_fid = (void *) fid;
|
||||
}
|
||||
mutex_unlock(&v9inode->v_mutex);
|
||||
if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE)
|
||||
v9fs_cache_inode_set_cookie(inode, file);
|
||||
return 0;
|
||||
out_error:
|
||||
p9_client_clunk(file->private_data);
|
||||
file->private_data = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_file_lock - lock a file (or directory)
|
||||
* @filp: file to be locked
|
||||
* @cmd: lock command
|
||||
* @fl: file lock structure
|
||||
*
|
||||
* Bugs: this looks like a local only lock, we should extend into 9P
|
||||
* by using open exclusive
|
||||
*/
|
||||
|
||||
static int v9fs_file_lock(struct file *filp, int cmd, struct file_lock *fl)
|
||||
{
|
||||
int res = 0;
|
||||
struct inode *inode = file_inode(filp);
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "filp: %p lock: %p\n", filp, fl);
|
||||
|
||||
/* No mandatory locks */
|
||||
if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK)
|
||||
return -ENOLCK;
|
||||
|
||||
if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
|
||||
filemap_write_and_wait(inode->i_mapping);
|
||||
invalidate_mapping_pages(&inode->i_data, 0, -1);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int v9fs_file_do_lock(struct file *filp, int cmd, struct file_lock *fl)
|
||||
{
|
||||
struct p9_flock flock;
|
||||
struct p9_fid *fid;
|
||||
uint8_t status = P9_LOCK_ERROR;
|
||||
int res = 0;
|
||||
unsigned char fl_type;
|
||||
struct v9fs_session_info *v9ses;
|
||||
|
||||
fid = filp->private_data;
|
||||
BUG_ON(fid == NULL);
|
||||
|
||||
if ((fl->fl_flags & FL_POSIX) != FL_POSIX)
|
||||
BUG();
|
||||
|
||||
res = locks_lock_file_wait(filp, fl);
|
||||
if (res < 0)
|
||||
goto out;
|
||||
|
||||
/* convert posix lock to p9 tlock args */
|
||||
memset(&flock, 0, sizeof(flock));
|
||||
/* map the lock type */
|
||||
switch (fl->fl_type) {
|
||||
case F_RDLCK:
|
||||
flock.type = P9_LOCK_TYPE_RDLCK;
|
||||
break;
|
||||
case F_WRLCK:
|
||||
flock.type = P9_LOCK_TYPE_WRLCK;
|
||||
break;
|
||||
case F_UNLCK:
|
||||
flock.type = P9_LOCK_TYPE_UNLCK;
|
||||
break;
|
||||
}
|
||||
flock.start = fl->fl_start;
|
||||
if (fl->fl_end == OFFSET_MAX)
|
||||
flock.length = 0;
|
||||
else
|
||||
flock.length = fl->fl_end - fl->fl_start + 1;
|
||||
flock.proc_id = fl->fl_pid;
|
||||
flock.client_id = fid->clnt->name;
|
||||
if (IS_SETLKW(cmd))
|
||||
flock.flags = P9_LOCK_FLAGS_BLOCK;
|
||||
|
||||
v9ses = v9fs_inode2v9ses(file_inode(filp));
|
||||
|
||||
/*
|
||||
* if its a blocked request and we get P9_LOCK_BLOCKED as the status
|
||||
* for lock request, keep on trying
|
||||
*/
|
||||
for (;;) {
|
||||
res = p9_client_lock_dotl(fid, &flock, &status);
|
||||
if (res < 0)
|
||||
goto out_unlock;
|
||||
|
||||
if (status != P9_LOCK_BLOCKED)
|
||||
break;
|
||||
if (status == P9_LOCK_BLOCKED && !IS_SETLKW(cmd))
|
||||
break;
|
||||
if (schedule_timeout_interruptible(v9ses->session_lock_timeout)
|
||||
!= 0)
|
||||
break;
|
||||
/*
|
||||
* p9_client_lock_dotl overwrites flock.client_id with the
|
||||
* server message, free and reuse the client name
|
||||
*/
|
||||
if (flock.client_id != fid->clnt->name) {
|
||||
kfree(flock.client_id);
|
||||
flock.client_id = fid->clnt->name;
|
||||
}
|
||||
}
|
||||
|
||||
/* map 9p status to VFS status */
|
||||
switch (status) {
|
||||
case P9_LOCK_SUCCESS:
|
||||
res = 0;
|
||||
break;
|
||||
case P9_LOCK_BLOCKED:
|
||||
res = -EAGAIN;
|
||||
break;
|
||||
default:
|
||||
WARN_ONCE(1, "unknown lock status code: %d\n", status);
|
||||
/* fallthough */
|
||||
case P9_LOCK_ERROR:
|
||||
case P9_LOCK_GRACE:
|
||||
res = -ENOLCK;
|
||||
break;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
/*
|
||||
* incase server returned error for lock request, revert
|
||||
* it locally
|
||||
*/
|
||||
if (res < 0 && fl->fl_type != F_UNLCK) {
|
||||
fl_type = fl->fl_type;
|
||||
fl->fl_type = F_UNLCK;
|
||||
/* Even if this fails we want to return the remote error */
|
||||
locks_lock_file_wait(filp, fl);
|
||||
fl->fl_type = fl_type;
|
||||
}
|
||||
if (flock.client_id != fid->clnt->name)
|
||||
kfree(flock.client_id);
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
|
||||
static int v9fs_file_getlock(struct file *filp, struct file_lock *fl)
|
||||
{
|
||||
struct p9_getlock glock;
|
||||
struct p9_fid *fid;
|
||||
int res = 0;
|
||||
|
||||
fid = filp->private_data;
|
||||
BUG_ON(fid == NULL);
|
||||
|
||||
posix_test_lock(filp, fl);
|
||||
/*
|
||||
* if we have a conflicting lock locally, no need to validate
|
||||
* with server
|
||||
*/
|
||||
if (fl->fl_type != F_UNLCK)
|
||||
return res;
|
||||
|
||||
/* convert posix lock to p9 tgetlock args */
|
||||
memset(&glock, 0, sizeof(glock));
|
||||
glock.type = P9_LOCK_TYPE_UNLCK;
|
||||
glock.start = fl->fl_start;
|
||||
if (fl->fl_end == OFFSET_MAX)
|
||||
glock.length = 0;
|
||||
else
|
||||
glock.length = fl->fl_end - fl->fl_start + 1;
|
||||
glock.proc_id = fl->fl_pid;
|
||||
glock.client_id = fid->clnt->name;
|
||||
|
||||
res = p9_client_getlock_dotl(fid, &glock);
|
||||
if (res < 0)
|
||||
goto out;
|
||||
/* map 9p lock type to os lock type */
|
||||
switch (glock.type) {
|
||||
case P9_LOCK_TYPE_RDLCK:
|
||||
fl->fl_type = F_RDLCK;
|
||||
break;
|
||||
case P9_LOCK_TYPE_WRLCK:
|
||||
fl->fl_type = F_WRLCK;
|
||||
break;
|
||||
case P9_LOCK_TYPE_UNLCK:
|
||||
fl->fl_type = F_UNLCK;
|
||||
break;
|
||||
}
|
||||
if (glock.type != P9_LOCK_TYPE_UNLCK) {
|
||||
fl->fl_start = glock.start;
|
||||
if (glock.length == 0)
|
||||
fl->fl_end = OFFSET_MAX;
|
||||
else
|
||||
fl->fl_end = glock.start + glock.length - 1;
|
||||
fl->fl_pid = glock.proc_id;
|
||||
}
|
||||
out:
|
||||
if (glock.client_id != fid->clnt->name)
|
||||
kfree(glock.client_id);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_file_lock_dotl - lock a file (or directory)
|
||||
* @filp: file to be locked
|
||||
* @cmd: lock command
|
||||
* @fl: file lock structure
|
||||
*
|
||||
*/
|
||||
|
||||
static int v9fs_file_lock_dotl(struct file *filp, int cmd, struct file_lock *fl)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
int ret = -ENOLCK;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "filp: %p cmd:%d lock: %p name: %pD\n",
|
||||
filp, cmd, fl, filp);
|
||||
|
||||
/* No mandatory locks */
|
||||
if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK)
|
||||
goto out_err;
|
||||
|
||||
if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
|
||||
filemap_write_and_wait(inode->i_mapping);
|
||||
invalidate_mapping_pages(&inode->i_data, 0, -1);
|
||||
}
|
||||
|
||||
if (IS_SETLK(cmd) || IS_SETLKW(cmd))
|
||||
ret = v9fs_file_do_lock(filp, cmd, fl);
|
||||
else if (IS_GETLK(cmd))
|
||||
ret = v9fs_file_getlock(filp, fl);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
out_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_file_flock_dotl - lock a file
|
||||
* @filp: file to be locked
|
||||
* @cmd: lock command
|
||||
* @fl: file lock structure
|
||||
*
|
||||
*/
|
||||
|
||||
static int v9fs_file_flock_dotl(struct file *filp, int cmd,
|
||||
struct file_lock *fl)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
int ret = -ENOLCK;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "filp: %p cmd:%d lock: %p name: %pD\n",
|
||||
filp, cmd, fl, filp);
|
||||
|
||||
/* No mandatory locks */
|
||||
if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK)
|
||||
goto out_err;
|
||||
|
||||
if (!(fl->fl_flags & FL_FLOCK))
|
||||
goto out_err;
|
||||
|
||||
if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
|
||||
filemap_write_and_wait(inode->i_mapping);
|
||||
invalidate_mapping_pages(&inode->i_data, 0, -1);
|
||||
}
|
||||
/* Convert flock to posix lock */
|
||||
fl->fl_flags |= FL_POSIX;
|
||||
fl->fl_flags ^= FL_FLOCK;
|
||||
|
||||
if (IS_SETLK(cmd) | IS_SETLKW(cmd))
|
||||
ret = v9fs_file_do_lock(filp, cmd, fl);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
out_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_file_read - read from a file
|
||||
* @filp: file pointer to read
|
||||
* @udata: user data buffer to read data into
|
||||
* @count: size of buffer
|
||||
* @offset: offset at which to read data
|
||||
*
|
||||
*/
|
||||
|
||||
static ssize_t
|
||||
v9fs_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
|
||||
{
|
||||
struct p9_fid *fid = iocb->ki_filp->private_data;
|
||||
int ret, err = 0;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "count %zu offset %lld\n",
|
||||
iov_iter_count(to), iocb->ki_pos);
|
||||
|
||||
ret = p9_client_read(fid, iocb->ki_pos, to, &err);
|
||||
if (!ret)
|
||||
return err;
|
||||
|
||||
iocb->ki_pos += ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_file_write - write to a file
|
||||
* @filp: file pointer to write
|
||||
* @data: data buffer to write data from
|
||||
* @count: size of buffer
|
||||
* @offset: offset at which to write data
|
||||
*
|
||||
*/
|
||||
static ssize_t
|
||||
v9fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
||||
{
|
||||
struct file *file = iocb->ki_filp;
|
||||
ssize_t retval;
|
||||
loff_t origin;
|
||||
int err = 0;
|
||||
|
||||
retval = generic_write_checks(iocb, from);
|
||||
if (retval <= 0)
|
||||
return retval;
|
||||
|
||||
origin = iocb->ki_pos;
|
||||
retval = p9_client_write(file->private_data, iocb->ki_pos, from, &err);
|
||||
if (retval > 0) {
|
||||
struct inode *inode = file_inode(file);
|
||||
loff_t i_size;
|
||||
unsigned long pg_start, pg_end;
|
||||
pg_start = origin >> PAGE_CACHE_SHIFT;
|
||||
pg_end = (origin + retval - 1) >> PAGE_CACHE_SHIFT;
|
||||
if (inode->i_mapping && inode->i_mapping->nrpages)
|
||||
invalidate_inode_pages2_range(inode->i_mapping,
|
||||
pg_start, pg_end);
|
||||
iocb->ki_pos += retval;
|
||||
i_size = i_size_read(inode);
|
||||
if (iocb->ki_pos > i_size) {
|
||||
inode_add_bytes(inode, iocb->ki_pos - i_size);
|
||||
/*
|
||||
* Need to serialize against i_size_write() in
|
||||
* v9fs_stat2inode()
|
||||
*/
|
||||
v9fs_i_size_write(inode, iocb->ki_pos);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int v9fs_file_fsync(struct file *filp, loff_t start, loff_t end,
|
||||
int datasync)
|
||||
{
|
||||
struct p9_fid *fid;
|
||||
struct inode *inode = filp->f_mapping->host;
|
||||
struct p9_wstat wstat;
|
||||
int retval;
|
||||
|
||||
retval = filemap_write_and_wait_range(inode->i_mapping, start, end);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
p9_debug(P9_DEBUG_VFS, "filp %p datasync %x\n", filp, datasync);
|
||||
|
||||
fid = filp->private_data;
|
||||
v9fs_blank_wstat(&wstat);
|
||||
|
||||
retval = p9_client_wstat(fid, &wstat);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int v9fs_file_fsync_dotl(struct file *filp, loff_t start, loff_t end,
|
||||
int datasync)
|
||||
{
|
||||
struct p9_fid *fid;
|
||||
struct inode *inode = filp->f_mapping->host;
|
||||
int retval;
|
||||
|
||||
retval = filemap_write_and_wait_range(inode->i_mapping, start, end);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
p9_debug(P9_DEBUG_VFS, "filp %p datasync %x\n", filp, datasync);
|
||||
|
||||
fid = filp->private_data;
|
||||
|
||||
retval = p9_client_fsync(fid, datasync);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int
|
||||
v9fs_file_mmap(struct file *filp, struct vm_area_struct *vma)
|
||||
{
|
||||
int retval;
|
||||
|
||||
|
||||
retval = generic_file_mmap(filp, vma);
|
||||
if (!retval)
|
||||
vma->vm_ops = &v9fs_file_vm_ops;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int
|
||||
v9fs_mmap_file_mmap(struct file *filp, struct vm_area_struct *vma)
|
||||
{
|
||||
int retval;
|
||||
struct inode *inode;
|
||||
struct v9fs_inode *v9inode;
|
||||
struct p9_fid *fid;
|
||||
|
||||
inode = file_inode(filp);
|
||||
v9inode = V9FS_I(inode);
|
||||
mutex_lock(&v9inode->v_mutex);
|
||||
if (!v9inode->writeback_fid &&
|
||||
(vma->vm_flags & VM_WRITE)) {
|
||||
/*
|
||||
* clone a fid and add it to writeback_fid
|
||||
* we do it during mmap instead of
|
||||
* page dirty time via write_begin/page_mkwrite
|
||||
* because we want write after unlink usecase
|
||||
* to work.
|
||||
*/
|
||||
fid = v9fs_writeback_fid(file_dentry(filp));
|
||||
if (IS_ERR(fid)) {
|
||||
retval = PTR_ERR(fid);
|
||||
mutex_unlock(&v9inode->v_mutex);
|
||||
return retval;
|
||||
}
|
||||
v9inode->writeback_fid = (void *) fid;
|
||||
}
|
||||
mutex_unlock(&v9inode->v_mutex);
|
||||
|
||||
retval = generic_file_mmap(filp, vma);
|
||||
if (!retval)
|
||||
vma->vm_ops = &v9fs_mmap_file_vm_ops;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int
|
||||
v9fs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
|
||||
{
|
||||
struct v9fs_inode *v9inode;
|
||||
struct page *page = vmf->page;
|
||||
struct file *filp = vma->vm_file;
|
||||
struct inode *inode = file_inode(filp);
|
||||
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "page %p fid %lx\n",
|
||||
page, (unsigned long)filp->private_data);
|
||||
|
||||
/* Update file times before taking page lock */
|
||||
file_update_time(filp);
|
||||
|
||||
v9inode = V9FS_I(inode);
|
||||
/* make sure the cache has finished storing the page */
|
||||
v9fs_fscache_wait_on_page_write(inode, page);
|
||||
BUG_ON(!v9inode->writeback_fid);
|
||||
lock_page(page);
|
||||
if (page->mapping != inode->i_mapping)
|
||||
goto out_unlock;
|
||||
wait_for_stable_page(page);
|
||||
|
||||
return VM_FAULT_LOCKED;
|
||||
out_unlock:
|
||||
unlock_page(page);
|
||||
return VM_FAULT_NOPAGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_mmap_file_read - read from a file
|
||||
* @filp: file pointer to read
|
||||
* @data: user data buffer to read data into
|
||||
* @count: size of buffer
|
||||
* @offset: offset at which to read data
|
||||
*
|
||||
*/
|
||||
static ssize_t
|
||||
v9fs_mmap_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
|
||||
{
|
||||
/* TODO: Check if there are dirty pages */
|
||||
return v9fs_file_read_iter(iocb, to);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_mmap_file_write - write to a file
|
||||
* @filp: file pointer to write
|
||||
* @data: data buffer to write data from
|
||||
* @count: size of buffer
|
||||
* @offset: offset at which to write data
|
||||
*
|
||||
*/
|
||||
static ssize_t
|
||||
v9fs_mmap_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
||||
{
|
||||
/*
|
||||
* TODO: invalidate mmaps on filp's inode between
|
||||
* offset and offset+count
|
||||
*/
|
||||
return v9fs_file_write_iter(iocb, from);
|
||||
}
|
||||
|
||||
static void v9fs_mmap_vm_close(struct vm_area_struct *vma)
|
||||
{
|
||||
struct inode *inode;
|
||||
|
||||
struct writeback_control wbc = {
|
||||
.nr_to_write = LONG_MAX,
|
||||
.sync_mode = WB_SYNC_ALL,
|
||||
.range_start = vma->vm_pgoff * PAGE_SIZE,
|
||||
/* absolute end, byte at end included */
|
||||
.range_end = vma->vm_pgoff * PAGE_SIZE +
|
||||
(vma->vm_end - vma->vm_start - 1),
|
||||
};
|
||||
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "9p VMA close, %p, flushing", vma);
|
||||
|
||||
inode = file_inode(vma->vm_file);
|
||||
|
||||
if (!mapping_cap_writeback_dirty(inode->i_mapping))
|
||||
wbc.nr_to_write = 0;
|
||||
|
||||
might_sleep();
|
||||
sync_inode(inode, &wbc);
|
||||
}
|
||||
|
||||
|
||||
static const struct vm_operations_struct v9fs_file_vm_ops = {
|
||||
.fault = filemap_fault,
|
||||
.map_pages = filemap_map_pages,
|
||||
.page_mkwrite = v9fs_vm_page_mkwrite,
|
||||
};
|
||||
|
||||
static const struct vm_operations_struct v9fs_mmap_file_vm_ops = {
|
||||
.close = v9fs_mmap_vm_close,
|
||||
.fault = filemap_fault,
|
||||
.map_pages = filemap_map_pages,
|
||||
.page_mkwrite = v9fs_vm_page_mkwrite,
|
||||
};
|
||||
|
||||
|
||||
const struct file_operations v9fs_cached_file_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read_iter = generic_file_read_iter,
|
||||
.write_iter = generic_file_write_iter,
|
||||
.open = v9fs_file_open,
|
||||
.release = v9fs_dir_release,
|
||||
.lock = v9fs_file_lock,
|
||||
.mmap = v9fs_file_mmap,
|
||||
.fsync = v9fs_file_fsync,
|
||||
};
|
||||
|
||||
const struct file_operations v9fs_cached_file_operations_dotl = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read_iter = generic_file_read_iter,
|
||||
.write_iter = generic_file_write_iter,
|
||||
.open = v9fs_file_open,
|
||||
.release = v9fs_dir_release,
|
||||
.lock = v9fs_file_lock_dotl,
|
||||
.flock = v9fs_file_flock_dotl,
|
||||
.mmap = v9fs_file_mmap,
|
||||
.fsync = v9fs_file_fsync_dotl,
|
||||
};
|
||||
|
||||
const struct file_operations v9fs_file_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read_iter = v9fs_file_read_iter,
|
||||
.write_iter = v9fs_file_write_iter,
|
||||
.open = v9fs_file_open,
|
||||
.release = v9fs_dir_release,
|
||||
.lock = v9fs_file_lock,
|
||||
.mmap = generic_file_readonly_mmap,
|
||||
.fsync = v9fs_file_fsync,
|
||||
};
|
||||
|
||||
const struct file_operations v9fs_file_operations_dotl = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read_iter = v9fs_file_read_iter,
|
||||
.write_iter = v9fs_file_write_iter,
|
||||
.open = v9fs_file_open,
|
||||
.release = v9fs_dir_release,
|
||||
.lock = v9fs_file_lock_dotl,
|
||||
.flock = v9fs_file_flock_dotl,
|
||||
.mmap = generic_file_readonly_mmap,
|
||||
.fsync = v9fs_file_fsync_dotl,
|
||||
};
|
||||
|
||||
const struct file_operations v9fs_mmap_file_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read_iter = v9fs_mmap_file_read_iter,
|
||||
.write_iter = v9fs_mmap_file_write_iter,
|
||||
.open = v9fs_file_open,
|
||||
.release = v9fs_dir_release,
|
||||
.lock = v9fs_file_lock,
|
||||
.mmap = v9fs_mmap_file_mmap,
|
||||
.fsync = v9fs_file_fsync,
|
||||
};
|
||||
|
||||
const struct file_operations v9fs_mmap_file_operations_dotl = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read_iter = v9fs_mmap_file_read_iter,
|
||||
.write_iter = v9fs_mmap_file_write_iter,
|
||||
.open = v9fs_file_open,
|
||||
.release = v9fs_dir_release,
|
||||
.lock = v9fs_file_lock_dotl,
|
||||
.flock = v9fs_file_flock_dotl,
|
||||
.mmap = v9fs_mmap_file_mmap,
|
||||
.fsync = v9fs_file_fsync_dotl,
|
||||
};
|
||||
1462
addons/9p/src/4.4.180/vfs_inode.c
Normal file
1462
addons/9p/src/4.4.180/vfs_inode.c
Normal file
File diff suppressed because it is too large
Load Diff
999
addons/9p/src/4.4.180/vfs_inode_dotl.c
Normal file
999
addons/9p/src/4.4.180/vfs_inode_dotl.c
Normal file
@@ -0,0 +1,999 @@
|
||||
/*
|
||||
* linux/fs/9p/vfs_inode_dotl.c
|
||||
*
|
||||
* This file contains vfs inode ops for the 9P2000.L protocol.
|
||||
*
|
||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/posix_acl.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
|
||||
#include "v9fs.h"
|
||||
#include "v9fs_vfs.h"
|
||||
#include "fid.h"
|
||||
#include "cache.h"
|
||||
#include "xattr.h"
|
||||
#include "acl.h"
|
||||
|
||||
static int
|
||||
v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, umode_t omode,
|
||||
dev_t rdev);
|
||||
|
||||
/**
|
||||
* v9fs_get_fsgid_for_create - Helper function to get the gid for creating a
|
||||
* new file system object. This checks the S_ISGID to determine the owning
|
||||
* group of the new file system object.
|
||||
*/
|
||||
|
||||
static kgid_t v9fs_get_fsgid_for_create(struct inode *dir_inode)
|
||||
{
|
||||
BUG_ON(dir_inode == NULL);
|
||||
|
||||
if (dir_inode->i_mode & S_ISGID) {
|
||||
/* set_gid bit is set.*/
|
||||
return dir_inode->i_gid;
|
||||
}
|
||||
return current_fsgid();
|
||||
}
|
||||
|
||||
static int v9fs_test_inode_dotl(struct inode *inode, void *data)
|
||||
{
|
||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
struct p9_stat_dotl *st = (struct p9_stat_dotl *)data;
|
||||
|
||||
/* don't match inode of different type */
|
||||
if ((inode->i_mode & S_IFMT) != (st->st_mode & S_IFMT))
|
||||
return 0;
|
||||
|
||||
if (inode->i_generation != st->st_gen)
|
||||
return 0;
|
||||
|
||||
/* compare qid details */
|
||||
if (memcmp(&v9inode->qid.version,
|
||||
&st->qid.version, sizeof(v9inode->qid.version)))
|
||||
return 0;
|
||||
|
||||
if (v9inode->qid.type != st->qid.type)
|
||||
return 0;
|
||||
|
||||
if (v9inode->qid.path != st->qid.path)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Always get a new inode */
|
||||
static int v9fs_test_new_inode_dotl(struct inode *inode, void *data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int v9fs_set_inode_dotl(struct inode *inode, void *data)
|
||||
{
|
||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
struct p9_stat_dotl *st = (struct p9_stat_dotl *)data;
|
||||
|
||||
memcpy(&v9inode->qid, &st->qid, sizeof(st->qid));
|
||||
inode->i_generation = st->st_gen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct inode *v9fs_qid_iget_dotl(struct super_block *sb,
|
||||
struct p9_qid *qid,
|
||||
struct p9_fid *fid,
|
||||
struct p9_stat_dotl *st,
|
||||
int new)
|
||||
{
|
||||
int retval;
|
||||
unsigned long i_ino;
|
||||
struct inode *inode;
|
||||
struct v9fs_session_info *v9ses = sb->s_fs_info;
|
||||
int (*test)(struct inode *, void *);
|
||||
|
||||
if (new)
|
||||
test = v9fs_test_new_inode_dotl;
|
||||
else
|
||||
test = v9fs_test_inode_dotl;
|
||||
|
||||
i_ino = v9fs_qid2ino(qid);
|
||||
inode = iget5_locked(sb, i_ino, test, v9fs_set_inode_dotl, st);
|
||||
if (!inode)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
if (!(inode->i_state & I_NEW))
|
||||
return inode;
|
||||
/*
|
||||
* initialize the inode with the stat info
|
||||
* FIXME!! we may need support for stale inodes
|
||||
* later.
|
||||
*/
|
||||
inode->i_ino = i_ino;
|
||||
retval = v9fs_init_inode(v9ses, inode,
|
||||
st->st_mode, new_decode_dev(st->st_rdev));
|
||||
if (retval)
|
||||
goto error;
|
||||
|
||||
v9fs_stat2inode_dotl(st, inode, 0);
|
||||
v9fs_cache_inode_get_cookie(inode);
|
||||
retval = v9fs_get_acl(inode, fid);
|
||||
if (retval)
|
||||
goto error;
|
||||
|
||||
unlock_new_inode(inode);
|
||||
return inode;
|
||||
error:
|
||||
iget_failed(inode);
|
||||
return ERR_PTR(retval);
|
||||
|
||||
}
|
||||
|
||||
struct inode *
|
||||
v9fs_inode_from_fid_dotl(struct v9fs_session_info *v9ses, struct p9_fid *fid,
|
||||
struct super_block *sb, int new)
|
||||
{
|
||||
struct p9_stat_dotl *st;
|
||||
struct inode *inode = NULL;
|
||||
|
||||
st = p9_client_getattr_dotl(fid, P9_STATS_BASIC | P9_STATS_GEN);
|
||||
if (IS_ERR(st))
|
||||
return ERR_CAST(st);
|
||||
|
||||
inode = v9fs_qid_iget_dotl(sb, &st->qid, fid, st, new);
|
||||
kfree(st);
|
||||
return inode;
|
||||
}
|
||||
|
||||
struct dotl_openflag_map {
|
||||
int open_flag;
|
||||
int dotl_flag;
|
||||
};
|
||||
|
||||
static int v9fs_mapped_dotl_flags(int flags)
|
||||
{
|
||||
int i;
|
||||
int rflags = 0;
|
||||
struct dotl_openflag_map dotl_oflag_map[] = {
|
||||
{ O_CREAT, P9_DOTL_CREATE },
|
||||
{ O_EXCL, P9_DOTL_EXCL },
|
||||
{ O_NOCTTY, P9_DOTL_NOCTTY },
|
||||
{ O_APPEND, P9_DOTL_APPEND },
|
||||
{ O_NONBLOCK, P9_DOTL_NONBLOCK },
|
||||
{ O_DSYNC, P9_DOTL_DSYNC },
|
||||
{ FASYNC, P9_DOTL_FASYNC },
|
||||
{ O_DIRECT, P9_DOTL_DIRECT },
|
||||
{ O_LARGEFILE, P9_DOTL_LARGEFILE },
|
||||
{ O_DIRECTORY, P9_DOTL_DIRECTORY },
|
||||
{ O_NOFOLLOW, P9_DOTL_NOFOLLOW },
|
||||
{ O_NOATIME, P9_DOTL_NOATIME },
|
||||
{ O_CLOEXEC, P9_DOTL_CLOEXEC },
|
||||
{ O_SYNC, P9_DOTL_SYNC},
|
||||
};
|
||||
for (i = 0; i < ARRAY_SIZE(dotl_oflag_map); i++) {
|
||||
if (flags & dotl_oflag_map[i].open_flag)
|
||||
rflags |= dotl_oflag_map[i].dotl_flag;
|
||||
}
|
||||
return rflags;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_open_to_dotl_flags- convert Linux specific open flags to
|
||||
* plan 9 open flag.
|
||||
* @flags: flags to convert
|
||||
*/
|
||||
int v9fs_open_to_dotl_flags(int flags)
|
||||
{
|
||||
int rflags = 0;
|
||||
|
||||
/*
|
||||
* We have same bits for P9_DOTL_READONLY, P9_DOTL_WRONLY
|
||||
* and P9_DOTL_NOACCESS
|
||||
*/
|
||||
rflags |= flags & O_ACCMODE;
|
||||
rflags |= v9fs_mapped_dotl_flags(flags);
|
||||
|
||||
return rflags;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_vfs_create_dotl - VFS hook to create files for 9P2000.L protocol.
|
||||
* @dir: directory inode that is being created
|
||||
* @dentry: dentry that is being deleted
|
||||
* @omode: create permissions
|
||||
*
|
||||
*/
|
||||
|
||||
static int
|
||||
v9fs_vfs_create_dotl(struct inode *dir, struct dentry *dentry, umode_t omode,
|
||||
bool excl)
|
||||
{
|
||||
return v9fs_vfs_mknod_dotl(dir, dentry, omode, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
v9fs_vfs_atomic_open_dotl(struct inode *dir, struct dentry *dentry,
|
||||
struct file *file, unsigned flags, umode_t omode,
|
||||
int *opened)
|
||||
{
|
||||
int err = 0;
|
||||
kgid_t gid;
|
||||
umode_t mode;
|
||||
char *name = NULL;
|
||||
struct p9_qid qid;
|
||||
struct inode *inode;
|
||||
struct p9_fid *fid = NULL;
|
||||
struct v9fs_inode *v9inode;
|
||||
struct p9_fid *dfid, *ofid, *inode_fid;
|
||||
struct v9fs_session_info *v9ses;
|
||||
struct posix_acl *pacl = NULL, *dacl = NULL;
|
||||
struct dentry *res = NULL;
|
||||
|
||||
if (d_unhashed(dentry)) {
|
||||
res = v9fs_vfs_lookup(dir, dentry, 0);
|
||||
if (IS_ERR(res))
|
||||
return PTR_ERR(res);
|
||||
|
||||
if (res)
|
||||
dentry = res;
|
||||
}
|
||||
|
||||
/* Only creates */
|
||||
if (!(flags & O_CREAT) || d_really_is_positive(dentry))
|
||||
return finish_no_open(file, res);
|
||||
|
||||
v9ses = v9fs_inode2v9ses(dir);
|
||||
|
||||
name = (char *) dentry->d_name.name;
|
||||
p9_debug(P9_DEBUG_VFS, "name:%s flags:0x%x mode:0x%hx\n",
|
||||
name, flags, omode);
|
||||
|
||||
dfid = v9fs_fid_lookup(dentry->d_parent);
|
||||
if (IS_ERR(dfid)) {
|
||||
err = PTR_ERR(dfid);
|
||||
p9_debug(P9_DEBUG_VFS, "fid lookup failed %d\n", err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* clone a fid to use for creation */
|
||||
ofid = p9_client_walk(dfid, 0, NULL, 1);
|
||||
if (IS_ERR(ofid)) {
|
||||
err = PTR_ERR(ofid);
|
||||
p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
gid = v9fs_get_fsgid_for_create(dir);
|
||||
|
||||
mode = omode;
|
||||
/* Update mode based on ACL value */
|
||||
err = v9fs_acl_mode(dir, &mode, &dacl, &pacl);
|
||||
if (err) {
|
||||
p9_debug(P9_DEBUG_VFS, "Failed to get acl values in creat %d\n",
|
||||
err);
|
||||
goto error;
|
||||
}
|
||||
err = p9_client_create_dotl(ofid, name, v9fs_open_to_dotl_flags(flags),
|
||||
mode, gid, &qid);
|
||||
if (err < 0) {
|
||||
p9_debug(P9_DEBUG_VFS, "p9_client_open_dotl failed in creat %d\n",
|
||||
err);
|
||||
goto error;
|
||||
}
|
||||
v9fs_invalidate_inode_attr(dir);
|
||||
|
||||
/* instantiate inode and assign the unopened fid to the dentry */
|
||||
fid = p9_client_walk(dfid, 1, &name, 1);
|
||||
if (IS_ERR(fid)) {
|
||||
err = PTR_ERR(fid);
|
||||
p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
|
||||
fid = NULL;
|
||||
goto error;
|
||||
}
|
||||
inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n", err);
|
||||
goto error;
|
||||
}
|
||||
/* Now set the ACL based on the default value */
|
||||
v9fs_set_create_acl(inode, fid, dacl, pacl);
|
||||
|
||||
v9fs_fid_add(dentry, fid);
|
||||
d_instantiate(dentry, inode);
|
||||
|
||||
v9inode = V9FS_I(inode);
|
||||
mutex_lock(&v9inode->v_mutex);
|
||||
if ((v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) &&
|
||||
!v9inode->writeback_fid &&
|
||||
((flags & O_ACCMODE) != O_RDONLY)) {
|
||||
/*
|
||||
* clone a fid and add it to writeback_fid
|
||||
* we do it during open time instead of
|
||||
* page dirty time via write_begin/page_mkwrite
|
||||
* because we want write after unlink usecase
|
||||
* to work.
|
||||
*/
|
||||
inode_fid = v9fs_writeback_fid(dentry);
|
||||
if (IS_ERR(inode_fid)) {
|
||||
err = PTR_ERR(inode_fid);
|
||||
mutex_unlock(&v9inode->v_mutex);
|
||||
goto err_clunk_old_fid;
|
||||
}
|
||||
v9inode->writeback_fid = (void *) inode_fid;
|
||||
}
|
||||
mutex_unlock(&v9inode->v_mutex);
|
||||
/* Since we are opening a file, assign the open fid to the file */
|
||||
err = finish_open(file, dentry, generic_file_open, opened);
|
||||
if (err)
|
||||
goto err_clunk_old_fid;
|
||||
file->private_data = ofid;
|
||||
if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE)
|
||||
v9fs_cache_inode_set_cookie(inode, file);
|
||||
*opened |= FILE_CREATED;
|
||||
out:
|
||||
v9fs_put_acl(dacl, pacl);
|
||||
dput(res);
|
||||
return err;
|
||||
|
||||
error:
|
||||
if (fid)
|
||||
p9_client_clunk(fid);
|
||||
err_clunk_old_fid:
|
||||
if (ofid)
|
||||
p9_client_clunk(ofid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_vfs_mkdir_dotl - VFS mkdir hook to create a directory
|
||||
* @dir: inode that is being unlinked
|
||||
* @dentry: dentry that is being unlinked
|
||||
* @omode: mode for new directory
|
||||
*
|
||||
*/
|
||||
|
||||
static int v9fs_vfs_mkdir_dotl(struct inode *dir,
|
||||
struct dentry *dentry, umode_t omode)
|
||||
{
|
||||
int err;
|
||||
struct v9fs_session_info *v9ses;
|
||||
struct p9_fid *fid = NULL, *dfid = NULL;
|
||||
kgid_t gid;
|
||||
char *name;
|
||||
umode_t mode;
|
||||
struct inode *inode;
|
||||
struct p9_qid qid;
|
||||
struct dentry *dir_dentry;
|
||||
struct posix_acl *dacl = NULL, *pacl = NULL;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "name %pd\n", dentry);
|
||||
err = 0;
|
||||
v9ses = v9fs_inode2v9ses(dir);
|
||||
|
||||
omode |= S_IFDIR;
|
||||
if (dir->i_mode & S_ISGID)
|
||||
omode |= S_ISGID;
|
||||
|
||||
dir_dentry = dentry->d_parent;
|
||||
dfid = v9fs_fid_lookup(dir_dentry);
|
||||
if (IS_ERR(dfid)) {
|
||||
err = PTR_ERR(dfid);
|
||||
p9_debug(P9_DEBUG_VFS, "fid lookup failed %d\n", err);
|
||||
dfid = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
gid = v9fs_get_fsgid_for_create(dir);
|
||||
mode = omode;
|
||||
/* Update mode based on ACL value */
|
||||
err = v9fs_acl_mode(dir, &mode, &dacl, &pacl);
|
||||
if (err) {
|
||||
p9_debug(P9_DEBUG_VFS, "Failed to get acl values in mkdir %d\n",
|
||||
err);
|
||||
goto error;
|
||||
}
|
||||
name = (char *) dentry->d_name.name;
|
||||
err = p9_client_mkdir_dotl(dfid, name, mode, gid, &qid);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
fid = p9_client_walk(dfid, 1, &name, 1);
|
||||
if (IS_ERR(fid)) {
|
||||
err = PTR_ERR(fid);
|
||||
p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n",
|
||||
err);
|
||||
fid = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* instantiate inode and assign the unopened fid to the dentry */
|
||||
if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
|
||||
inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n",
|
||||
err);
|
||||
goto error;
|
||||
}
|
||||
v9fs_fid_add(dentry, fid);
|
||||
v9fs_set_create_acl(inode, fid, dacl, pacl);
|
||||
d_instantiate(dentry, inode);
|
||||
fid = NULL;
|
||||
err = 0;
|
||||
} else {
|
||||
/*
|
||||
* Not in cached mode. No need to populate
|
||||
* inode with stat. We need to get an inode
|
||||
* so that we can set the acl with dentry
|
||||
*/
|
||||
inode = v9fs_get_inode(dir->i_sb, mode, 0);
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
goto error;
|
||||
}
|
||||
v9fs_set_create_acl(inode, fid, dacl, pacl);
|
||||
d_instantiate(dentry, inode);
|
||||
}
|
||||
inc_nlink(dir);
|
||||
v9fs_invalidate_inode_attr(dir);
|
||||
error:
|
||||
if (fid)
|
||||
p9_client_clunk(fid);
|
||||
v9fs_put_acl(dacl, pacl);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
v9fs_vfs_getattr_dotl(struct vfsmount *mnt, struct dentry *dentry,
|
||||
struct kstat *stat)
|
||||
{
|
||||
struct v9fs_session_info *v9ses;
|
||||
struct p9_fid *fid;
|
||||
struct p9_stat_dotl *st;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "dentry: %p\n", dentry);
|
||||
v9ses = v9fs_dentry2v9ses(dentry);
|
||||
if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
|
||||
generic_fillattr(d_inode(dentry), stat);
|
||||
return 0;
|
||||
}
|
||||
fid = v9fs_fid_lookup(dentry);
|
||||
if (IS_ERR(fid))
|
||||
return PTR_ERR(fid);
|
||||
|
||||
/* Ask for all the fields in stat structure. Server will return
|
||||
* whatever it supports
|
||||
*/
|
||||
|
||||
st = p9_client_getattr_dotl(fid, P9_STATS_ALL);
|
||||
if (IS_ERR(st))
|
||||
return PTR_ERR(st);
|
||||
|
||||
v9fs_stat2inode_dotl(st, d_inode(dentry), 0);
|
||||
generic_fillattr(d_inode(dentry), stat);
|
||||
/* Change block size to what the server returned */
|
||||
stat->blksize = st->st_blksize;
|
||||
|
||||
kfree(st);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attribute flags.
|
||||
*/
|
||||
#define P9_ATTR_MODE (1 << 0)
|
||||
#define P9_ATTR_UID (1 << 1)
|
||||
#define P9_ATTR_GID (1 << 2)
|
||||
#define P9_ATTR_SIZE (1 << 3)
|
||||
#define P9_ATTR_ATIME (1 << 4)
|
||||
#define P9_ATTR_MTIME (1 << 5)
|
||||
#define P9_ATTR_CTIME (1 << 6)
|
||||
#define P9_ATTR_ATIME_SET (1 << 7)
|
||||
#define P9_ATTR_MTIME_SET (1 << 8)
|
||||
|
||||
struct dotl_iattr_map {
|
||||
int iattr_valid;
|
||||
int p9_iattr_valid;
|
||||
};
|
||||
|
||||
static int v9fs_mapped_iattr_valid(int iattr_valid)
|
||||
{
|
||||
int i;
|
||||
int p9_iattr_valid = 0;
|
||||
struct dotl_iattr_map dotl_iattr_map[] = {
|
||||
{ ATTR_MODE, P9_ATTR_MODE },
|
||||
{ ATTR_UID, P9_ATTR_UID },
|
||||
{ ATTR_GID, P9_ATTR_GID },
|
||||
{ ATTR_SIZE, P9_ATTR_SIZE },
|
||||
{ ATTR_ATIME, P9_ATTR_ATIME },
|
||||
{ ATTR_MTIME, P9_ATTR_MTIME },
|
||||
{ ATTR_CTIME, P9_ATTR_CTIME },
|
||||
{ ATTR_ATIME_SET, P9_ATTR_ATIME_SET },
|
||||
{ ATTR_MTIME_SET, P9_ATTR_MTIME_SET },
|
||||
};
|
||||
for (i = 0; i < ARRAY_SIZE(dotl_iattr_map); i++) {
|
||||
if (iattr_valid & dotl_iattr_map[i].iattr_valid)
|
||||
p9_iattr_valid |= dotl_iattr_map[i].p9_iattr_valid;
|
||||
}
|
||||
return p9_iattr_valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_vfs_setattr_dotl - set file metadata
|
||||
* @dentry: file whose metadata to set
|
||||
* @iattr: metadata assignment structure
|
||||
*
|
||||
*/
|
||||
|
||||
int v9fs_vfs_setattr_dotl(struct dentry *dentry, struct iattr *iattr)
|
||||
{
|
||||
int retval;
|
||||
struct p9_fid *fid;
|
||||
struct p9_iattr_dotl p9attr;
|
||||
struct inode *inode = d_inode(dentry);
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "\n");
|
||||
|
||||
retval = inode_change_ok(inode, iattr);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
p9attr.valid = v9fs_mapped_iattr_valid(iattr->ia_valid);
|
||||
p9attr.mode = iattr->ia_mode;
|
||||
p9attr.uid = iattr->ia_uid;
|
||||
p9attr.gid = iattr->ia_gid;
|
||||
p9attr.size = iattr->ia_size;
|
||||
p9attr.atime_sec = iattr->ia_atime.tv_sec;
|
||||
p9attr.atime_nsec = iattr->ia_atime.tv_nsec;
|
||||
p9attr.mtime_sec = iattr->ia_mtime.tv_sec;
|
||||
p9attr.mtime_nsec = iattr->ia_mtime.tv_nsec;
|
||||
|
||||
fid = v9fs_fid_lookup(dentry);
|
||||
if (IS_ERR(fid))
|
||||
return PTR_ERR(fid);
|
||||
|
||||
/* Write all dirty data */
|
||||
if (S_ISREG(inode->i_mode))
|
||||
filemap_write_and_wait(inode->i_mapping);
|
||||
|
||||
retval = p9_client_setattr(fid, &p9attr);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
if ((iattr->ia_valid & ATTR_SIZE) &&
|
||||
iattr->ia_size != i_size_read(inode))
|
||||
truncate_setsize(inode, iattr->ia_size);
|
||||
|
||||
v9fs_invalidate_inode_attr(inode);
|
||||
setattr_copy(inode, iattr);
|
||||
mark_inode_dirty(inode);
|
||||
if (iattr->ia_valid & ATTR_MODE) {
|
||||
/* We also want to update ACL when we update mode bits */
|
||||
retval = v9fs_acl_chmod(inode, fid);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_stat2inode_dotl - populate an inode structure with stat info
|
||||
* @stat: stat structure
|
||||
* @inode: inode to populate
|
||||
* @flags: ctrl flags (e.g. V9FS_STAT2INODE_KEEP_ISIZE)
|
||||
*
|
||||
*/
|
||||
|
||||
void
|
||||
v9fs_stat2inode_dotl(struct p9_stat_dotl *stat, struct inode *inode,
|
||||
unsigned int flags)
|
||||
{
|
||||
umode_t mode;
|
||||
struct v9fs_inode *v9inode = V9FS_I(inode);
|
||||
|
||||
if ((stat->st_result_mask & P9_STATS_BASIC) == P9_STATS_BASIC) {
|
||||
inode->i_atime.tv_sec = stat->st_atime_sec;
|
||||
inode->i_atime.tv_nsec = stat->st_atime_nsec;
|
||||
inode->i_mtime.tv_sec = stat->st_mtime_sec;
|
||||
inode->i_mtime.tv_nsec = stat->st_mtime_nsec;
|
||||
inode->i_ctime.tv_sec = stat->st_ctime_sec;
|
||||
inode->i_ctime.tv_nsec = stat->st_ctime_nsec;
|
||||
inode->i_uid = stat->st_uid;
|
||||
inode->i_gid = stat->st_gid;
|
||||
set_nlink(inode, stat->st_nlink);
|
||||
|
||||
mode = stat->st_mode & S_IALLUGO;
|
||||
mode |= inode->i_mode & ~S_IALLUGO;
|
||||
inode->i_mode = mode;
|
||||
|
||||
if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE))
|
||||
v9fs_i_size_write(inode, stat->st_size);
|
||||
inode->i_blocks = stat->st_blocks;
|
||||
} else {
|
||||
if (stat->st_result_mask & P9_STATS_ATIME) {
|
||||
inode->i_atime.tv_sec = stat->st_atime_sec;
|
||||
inode->i_atime.tv_nsec = stat->st_atime_nsec;
|
||||
}
|
||||
if (stat->st_result_mask & P9_STATS_MTIME) {
|
||||
inode->i_mtime.tv_sec = stat->st_mtime_sec;
|
||||
inode->i_mtime.tv_nsec = stat->st_mtime_nsec;
|
||||
}
|
||||
if (stat->st_result_mask & P9_STATS_CTIME) {
|
||||
inode->i_ctime.tv_sec = stat->st_ctime_sec;
|
||||
inode->i_ctime.tv_nsec = stat->st_ctime_nsec;
|
||||
}
|
||||
if (stat->st_result_mask & P9_STATS_UID)
|
||||
inode->i_uid = stat->st_uid;
|
||||
if (stat->st_result_mask & P9_STATS_GID)
|
||||
inode->i_gid = stat->st_gid;
|
||||
if (stat->st_result_mask & P9_STATS_NLINK)
|
||||
set_nlink(inode, stat->st_nlink);
|
||||
if (stat->st_result_mask & P9_STATS_MODE) {
|
||||
inode->i_mode = stat->st_mode;
|
||||
if ((S_ISBLK(inode->i_mode)) ||
|
||||
(S_ISCHR(inode->i_mode)))
|
||||
init_special_inode(inode, inode->i_mode,
|
||||
inode->i_rdev);
|
||||
}
|
||||
if (stat->st_result_mask & P9_STATS_RDEV)
|
||||
inode->i_rdev = new_decode_dev(stat->st_rdev);
|
||||
if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE) &&
|
||||
stat->st_result_mask & P9_STATS_SIZE)
|
||||
v9fs_i_size_write(inode, stat->st_size);
|
||||
if (stat->st_result_mask & P9_STATS_BLOCKS)
|
||||
inode->i_blocks = stat->st_blocks;
|
||||
}
|
||||
if (stat->st_result_mask & P9_STATS_GEN)
|
||||
inode->i_generation = stat->st_gen;
|
||||
|
||||
/* Currently we don't support P9_STATS_BTIME and P9_STATS_DATA_VERSION
|
||||
* because the inode structure does not have fields for them.
|
||||
*/
|
||||
v9inode->cache_validity &= ~V9FS_INO_INVALID_ATTR;
|
||||
}
|
||||
|
||||
static int
|
||||
v9fs_vfs_symlink_dotl(struct inode *dir, struct dentry *dentry,
|
||||
const char *symname)
|
||||
{
|
||||
int err;
|
||||
kgid_t gid;
|
||||
char *name;
|
||||
struct p9_qid qid;
|
||||
struct inode *inode;
|
||||
struct p9_fid *dfid;
|
||||
struct p9_fid *fid = NULL;
|
||||
struct v9fs_session_info *v9ses;
|
||||
|
||||
name = (char *) dentry->d_name.name;
|
||||
p9_debug(P9_DEBUG_VFS, "%lu,%s,%s\n", dir->i_ino, name, symname);
|
||||
v9ses = v9fs_inode2v9ses(dir);
|
||||
|
||||
dfid = v9fs_fid_lookup(dentry->d_parent);
|
||||
if (IS_ERR(dfid)) {
|
||||
err = PTR_ERR(dfid);
|
||||
p9_debug(P9_DEBUG_VFS, "fid lookup failed %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
gid = v9fs_get_fsgid_for_create(dir);
|
||||
|
||||
/* Server doesn't alter fid on TSYMLINK. Hence no need to clone it. */
|
||||
err = p9_client_symlink(dfid, name, (char *)symname, gid, &qid);
|
||||
|
||||
if (err < 0) {
|
||||
p9_debug(P9_DEBUG_VFS, "p9_client_symlink failed %d\n", err);
|
||||
goto error;
|
||||
}
|
||||
|
||||
v9fs_invalidate_inode_attr(dir);
|
||||
if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
|
||||
/* Now walk from the parent so we can get an unopened fid. */
|
||||
fid = p9_client_walk(dfid, 1, &name, 1);
|
||||
if (IS_ERR(fid)) {
|
||||
err = PTR_ERR(fid);
|
||||
p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n",
|
||||
err);
|
||||
fid = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* instantiate inode and assign the unopened fid to dentry */
|
||||
inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n",
|
||||
err);
|
||||
goto error;
|
||||
}
|
||||
v9fs_fid_add(dentry, fid);
|
||||
d_instantiate(dentry, inode);
|
||||
fid = NULL;
|
||||
err = 0;
|
||||
} else {
|
||||
/* Not in cached mode. No need to populate inode with stat */
|
||||
inode = v9fs_get_inode(dir->i_sb, S_IFLNK, 0);
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
goto error;
|
||||
}
|
||||
d_instantiate(dentry, inode);
|
||||
}
|
||||
|
||||
error:
|
||||
if (fid)
|
||||
p9_client_clunk(fid);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_vfs_link_dotl - create a hardlink for dotl
|
||||
* @old_dentry: dentry for file to link to
|
||||
* @dir: inode destination for new link
|
||||
* @dentry: dentry for link
|
||||
*
|
||||
*/
|
||||
|
||||
static int
|
||||
v9fs_vfs_link_dotl(struct dentry *old_dentry, struct inode *dir,
|
||||
struct dentry *dentry)
|
||||
{
|
||||
int err;
|
||||
struct dentry *dir_dentry;
|
||||
struct p9_fid *dfid, *oldfid;
|
||||
struct v9fs_session_info *v9ses;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "dir ino: %lu, old_name: %pd, new_name: %pd\n",
|
||||
dir->i_ino, old_dentry, dentry);
|
||||
|
||||
v9ses = v9fs_inode2v9ses(dir);
|
||||
dir_dentry = dentry->d_parent;
|
||||
dfid = v9fs_fid_lookup(dir_dentry);
|
||||
if (IS_ERR(dfid))
|
||||
return PTR_ERR(dfid);
|
||||
|
||||
oldfid = v9fs_fid_lookup(old_dentry);
|
||||
if (IS_ERR(oldfid))
|
||||
return PTR_ERR(oldfid);
|
||||
|
||||
err = p9_client_link(dfid, oldfid, (char *)dentry->d_name.name);
|
||||
|
||||
if (err < 0) {
|
||||
p9_debug(P9_DEBUG_VFS, "p9_client_link failed %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
v9fs_invalidate_inode_attr(dir);
|
||||
if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
|
||||
/* Get the latest stat info from server. */
|
||||
struct p9_fid *fid;
|
||||
fid = v9fs_fid_lookup(old_dentry);
|
||||
if (IS_ERR(fid))
|
||||
return PTR_ERR(fid);
|
||||
|
||||
v9fs_refresh_inode_dotl(fid, d_inode(old_dentry));
|
||||
}
|
||||
ihold(d_inode(old_dentry));
|
||||
d_instantiate(dentry, d_inode(old_dentry));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_vfs_mknod_dotl - create a special file
|
||||
* @dir: inode destination for new link
|
||||
* @dentry: dentry for file
|
||||
* @omode: mode for creation
|
||||
* @rdev: device associated with special file
|
||||
*
|
||||
*/
|
||||
static int
|
||||
v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, umode_t omode,
|
||||
dev_t rdev)
|
||||
{
|
||||
int err;
|
||||
kgid_t gid;
|
||||
char *name;
|
||||
umode_t mode;
|
||||
struct v9fs_session_info *v9ses;
|
||||
struct p9_fid *fid = NULL, *dfid = NULL;
|
||||
struct inode *inode;
|
||||
struct p9_qid qid;
|
||||
struct dentry *dir_dentry;
|
||||
struct posix_acl *dacl = NULL, *pacl = NULL;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, " %lu,%pd mode: %hx MAJOR: %u MINOR: %u\n",
|
||||
dir->i_ino, dentry, omode,
|
||||
MAJOR(rdev), MINOR(rdev));
|
||||
|
||||
v9ses = v9fs_inode2v9ses(dir);
|
||||
dir_dentry = dentry->d_parent;
|
||||
dfid = v9fs_fid_lookup(dir_dentry);
|
||||
if (IS_ERR(dfid)) {
|
||||
err = PTR_ERR(dfid);
|
||||
p9_debug(P9_DEBUG_VFS, "fid lookup failed %d\n", err);
|
||||
dfid = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
gid = v9fs_get_fsgid_for_create(dir);
|
||||
mode = omode;
|
||||
/* Update mode based on ACL value */
|
||||
err = v9fs_acl_mode(dir, &mode, &dacl, &pacl);
|
||||
if (err) {
|
||||
p9_debug(P9_DEBUG_VFS, "Failed to get acl values in mknod %d\n",
|
||||
err);
|
||||
goto error;
|
||||
}
|
||||
name = (char *) dentry->d_name.name;
|
||||
|
||||
err = p9_client_mknod_dotl(dfid, name, mode, rdev, gid, &qid);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
v9fs_invalidate_inode_attr(dir);
|
||||
fid = p9_client_walk(dfid, 1, &name, 1);
|
||||
if (IS_ERR(fid)) {
|
||||
err = PTR_ERR(fid);
|
||||
p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n",
|
||||
err);
|
||||
fid = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* instantiate inode and assign the unopened fid to the dentry */
|
||||
if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
|
||||
inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n",
|
||||
err);
|
||||
goto error;
|
||||
}
|
||||
v9fs_set_create_acl(inode, fid, dacl, pacl);
|
||||
v9fs_fid_add(dentry, fid);
|
||||
d_instantiate(dentry, inode);
|
||||
fid = NULL;
|
||||
err = 0;
|
||||
} else {
|
||||
/*
|
||||
* Not in cached mode. No need to populate inode with stat.
|
||||
* socket syscall returns a fd, so we need instantiate
|
||||
*/
|
||||
inode = v9fs_get_inode(dir->i_sb, mode, rdev);
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
goto error;
|
||||
}
|
||||
v9fs_set_create_acl(inode, fid, dacl, pacl);
|
||||
d_instantiate(dentry, inode);
|
||||
}
|
||||
error:
|
||||
if (fid)
|
||||
p9_client_clunk(fid);
|
||||
v9fs_put_acl(dacl, pacl);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_vfs_follow_link_dotl - follow a symlink path
|
||||
* @dentry: dentry for symlink
|
||||
* @cookie: place to pass the data to put_link()
|
||||
*/
|
||||
|
||||
static const char *
|
||||
v9fs_vfs_follow_link_dotl(struct dentry *dentry, void **cookie)
|
||||
{
|
||||
struct p9_fid *fid = v9fs_fid_lookup(dentry);
|
||||
char *target;
|
||||
int retval;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "%pd\n", dentry);
|
||||
|
||||
if (IS_ERR(fid))
|
||||
return ERR_CAST(fid);
|
||||
retval = p9_client_readlink(fid, &target);
|
||||
if (retval)
|
||||
return ERR_PTR(retval);
|
||||
return *cookie = target;
|
||||
}
|
||||
|
||||
int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode)
|
||||
{
|
||||
struct p9_stat_dotl *st;
|
||||
struct v9fs_session_info *v9ses;
|
||||
unsigned int flags;
|
||||
|
||||
v9ses = v9fs_inode2v9ses(inode);
|
||||
st = p9_client_getattr_dotl(fid, P9_STATS_ALL);
|
||||
if (IS_ERR(st))
|
||||
return PTR_ERR(st);
|
||||
/*
|
||||
* Don't update inode if the file type is different
|
||||
*/
|
||||
if ((inode->i_mode & S_IFMT) != (st->st_mode & S_IFMT))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* We don't want to refresh inode->i_size,
|
||||
* because we may have cached data
|
||||
*/
|
||||
flags = (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) ?
|
||||
V9FS_STAT2INODE_KEEP_ISIZE : 0;
|
||||
v9fs_stat2inode_dotl(st, inode, flags);
|
||||
out:
|
||||
kfree(st);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct inode_operations v9fs_dir_inode_operations_dotl = {
|
||||
.create = v9fs_vfs_create_dotl,
|
||||
.atomic_open = v9fs_vfs_atomic_open_dotl,
|
||||
.lookup = v9fs_vfs_lookup,
|
||||
.link = v9fs_vfs_link_dotl,
|
||||
.symlink = v9fs_vfs_symlink_dotl,
|
||||
.unlink = v9fs_vfs_unlink,
|
||||
.mkdir = v9fs_vfs_mkdir_dotl,
|
||||
.rmdir = v9fs_vfs_rmdir,
|
||||
.mknod = v9fs_vfs_mknod_dotl,
|
||||
.rename = v9fs_vfs_rename,
|
||||
.getattr = v9fs_vfs_getattr_dotl,
|
||||
.setattr = v9fs_vfs_setattr_dotl,
|
||||
.setxattr = generic_setxattr,
|
||||
.getxattr = generic_getxattr,
|
||||
.removexattr = generic_removexattr,
|
||||
.listxattr = v9fs_listxattr,
|
||||
.get_acl = v9fs_iop_get_acl,
|
||||
};
|
||||
|
||||
const struct inode_operations v9fs_file_inode_operations_dotl = {
|
||||
.getattr = v9fs_vfs_getattr_dotl,
|
||||
.setattr = v9fs_vfs_setattr_dotl,
|
||||
.setxattr = generic_setxattr,
|
||||
.getxattr = generic_getxattr,
|
||||
.removexattr = generic_removexattr,
|
||||
.listxattr = v9fs_listxattr,
|
||||
.get_acl = v9fs_iop_get_acl,
|
||||
};
|
||||
|
||||
const struct inode_operations v9fs_symlink_inode_operations_dotl = {
|
||||
.readlink = generic_readlink,
|
||||
.follow_link = v9fs_vfs_follow_link_dotl,
|
||||
.put_link = kfree_put_link,
|
||||
.getattr = v9fs_vfs_getattr_dotl,
|
||||
.setattr = v9fs_vfs_setattr_dotl,
|
||||
.setxattr = generic_setxattr,
|
||||
.getxattr = generic_getxattr,
|
||||
.removexattr = generic_removexattr,
|
||||
.listxattr = v9fs_listxattr,
|
||||
};
|
||||
366
addons/9p/src/4.4.180/vfs_super.c
Normal file
366
addons/9p/src/4.4.180/vfs_super.c
Normal file
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
* linux/fs/9p/vfs_super.c
|
||||
*
|
||||
* This file contians superblock ops for 9P2000. It is intended that
|
||||
* you mount this file system on directories.
|
||||
*
|
||||
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
||||
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to:
|
||||
* Free Software Foundation
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02111-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/statfs.h>
|
||||
#include <linux/magic.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
|
||||
#include "v9fs.h"
|
||||
#include "v9fs_vfs.h"
|
||||
#include "fid.h"
|
||||
#include "xattr.h"
|
||||
#include "acl.h"
|
||||
|
||||
static const struct super_operations v9fs_super_ops, v9fs_super_ops_dotl;
|
||||
|
||||
/**
|
||||
* v9fs_set_super - set the superblock
|
||||
* @s: super block
|
||||
* @data: file system specific data
|
||||
*
|
||||
*/
|
||||
|
||||
static int v9fs_set_super(struct super_block *s, void *data)
|
||||
{
|
||||
s->s_fs_info = data;
|
||||
return set_anon_super(s, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_fill_super - populate superblock with info
|
||||
* @sb: superblock
|
||||
* @v9ses: session information
|
||||
* @flags: flags propagated from v9fs_mount()
|
||||
*
|
||||
*/
|
||||
|
||||
static void
|
||||
v9fs_fill_super(struct super_block *sb, struct v9fs_session_info *v9ses,
|
||||
int flags, void *data)
|
||||
{
|
||||
sb->s_maxbytes = MAX_LFS_FILESIZE;
|
||||
sb->s_blocksize_bits = fls(v9ses->maxdata - 1);
|
||||
sb->s_blocksize = 1 << sb->s_blocksize_bits;
|
||||
sb->s_magic = V9FS_MAGIC;
|
||||
if (v9fs_proto_dotl(v9ses)) {
|
||||
sb->s_op = &v9fs_super_ops_dotl;
|
||||
sb->s_xattr = v9fs_xattr_handlers;
|
||||
} else
|
||||
sb->s_op = &v9fs_super_ops;
|
||||
sb->s_bdi = &v9ses->bdi;
|
||||
if (v9ses->cache)
|
||||
sb->s_bdi->ra_pages = (VM_MAX_READAHEAD * 1024)/PAGE_CACHE_SIZE;
|
||||
|
||||
sb->s_flags |= MS_ACTIVE | MS_DIRSYNC | MS_NOATIME;
|
||||
if (!v9ses->cache)
|
||||
sb->s_flags |= MS_SYNCHRONOUS;
|
||||
|
||||
#ifdef CONFIG_9P_FS_POSIX_ACL
|
||||
if ((v9ses->flags & V9FS_ACL_MASK) == V9FS_POSIX_ACL)
|
||||
sb->s_flags |= MS_POSIXACL;
|
||||
#endif
|
||||
|
||||
save_mount_options(sb, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_mount - mount a superblock
|
||||
* @fs_type: file system type
|
||||
* @flags: mount flags
|
||||
* @dev_name: device name that was mounted
|
||||
* @data: mount options
|
||||
*
|
||||
*/
|
||||
|
||||
static struct dentry *v9fs_mount(struct file_system_type *fs_type, int flags,
|
||||
const char *dev_name, void *data)
|
||||
{
|
||||
struct super_block *sb = NULL;
|
||||
struct inode *inode = NULL;
|
||||
struct dentry *root = NULL;
|
||||
struct v9fs_session_info *v9ses = NULL;
|
||||
umode_t mode = S_IRWXUGO | S_ISVTX;
|
||||
struct p9_fid *fid;
|
||||
int retval = 0;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "\n");
|
||||
|
||||
v9ses = kzalloc(sizeof(struct v9fs_session_info), GFP_KERNEL);
|
||||
if (!v9ses)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
fid = v9fs_session_init(v9ses, dev_name, data);
|
||||
if (IS_ERR(fid)) {
|
||||
retval = PTR_ERR(fid);
|
||||
goto free_session;
|
||||
}
|
||||
|
||||
sb = sget(fs_type, NULL, v9fs_set_super, flags, v9ses);
|
||||
if (IS_ERR(sb)) {
|
||||
retval = PTR_ERR(sb);
|
||||
goto clunk_fid;
|
||||
}
|
||||
v9fs_fill_super(sb, v9ses, flags, data);
|
||||
|
||||
if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE)
|
||||
sb->s_d_op = &v9fs_cached_dentry_operations;
|
||||
else
|
||||
sb->s_d_op = &v9fs_dentry_operations;
|
||||
|
||||
inode = v9fs_get_inode(sb, S_IFDIR | mode, 0);
|
||||
if (IS_ERR(inode)) {
|
||||
retval = PTR_ERR(inode);
|
||||
goto release_sb;
|
||||
}
|
||||
|
||||
root = d_make_root(inode);
|
||||
if (!root) {
|
||||
retval = -ENOMEM;
|
||||
goto release_sb;
|
||||
}
|
||||
sb->s_root = root;
|
||||
if (v9fs_proto_dotl(v9ses)) {
|
||||
struct p9_stat_dotl *st = NULL;
|
||||
st = p9_client_getattr_dotl(fid, P9_STATS_BASIC);
|
||||
if (IS_ERR(st)) {
|
||||
retval = PTR_ERR(st);
|
||||
goto release_sb;
|
||||
}
|
||||
d_inode(root)->i_ino = v9fs_qid2ino(&st->qid);
|
||||
v9fs_stat2inode_dotl(st, d_inode(root), 0);
|
||||
kfree(st);
|
||||
} else {
|
||||
struct p9_wstat *st = NULL;
|
||||
st = p9_client_stat(fid);
|
||||
if (IS_ERR(st)) {
|
||||
retval = PTR_ERR(st);
|
||||
goto release_sb;
|
||||
}
|
||||
|
||||
d_inode(root)->i_ino = v9fs_qid2ino(&st->qid);
|
||||
v9fs_stat2inode(st, d_inode(root), sb, 0);
|
||||
|
||||
p9stat_free(st);
|
||||
kfree(st);
|
||||
}
|
||||
retval = v9fs_get_acl(inode, fid);
|
||||
if (retval)
|
||||
goto release_sb;
|
||||
v9fs_fid_add(root, fid);
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, " simple set mount, return 0\n");
|
||||
return dget(sb->s_root);
|
||||
|
||||
clunk_fid:
|
||||
p9_client_clunk(fid);
|
||||
v9fs_session_close(v9ses);
|
||||
free_session:
|
||||
kfree(v9ses);
|
||||
return ERR_PTR(retval);
|
||||
|
||||
release_sb:
|
||||
/*
|
||||
* we will do the session_close and root dentry release
|
||||
* in the below call. But we need to clunk fid, because we haven't
|
||||
* attached the fid to dentry so it won't get clunked
|
||||
* automatically.
|
||||
*/
|
||||
p9_client_clunk(fid);
|
||||
deactivate_locked_super(sb);
|
||||
return ERR_PTR(retval);
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_kill_super - Kill Superblock
|
||||
* @s: superblock
|
||||
*
|
||||
*/
|
||||
|
||||
static void v9fs_kill_super(struct super_block *s)
|
||||
{
|
||||
struct v9fs_session_info *v9ses = s->s_fs_info;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, " %p\n", s);
|
||||
|
||||
kill_anon_super(s);
|
||||
|
||||
v9fs_session_cancel(v9ses);
|
||||
v9fs_session_close(v9ses);
|
||||
kfree(v9ses);
|
||||
s->s_fs_info = NULL;
|
||||
p9_debug(P9_DEBUG_VFS, "exiting kill_super\n");
|
||||
}
|
||||
|
||||
static void
|
||||
v9fs_umount_begin(struct super_block *sb)
|
||||
{
|
||||
struct v9fs_session_info *v9ses;
|
||||
|
||||
v9ses = sb->s_fs_info;
|
||||
v9fs_session_begin_cancel(v9ses);
|
||||
}
|
||||
|
||||
static int v9fs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||
{
|
||||
struct v9fs_session_info *v9ses;
|
||||
struct p9_fid *fid;
|
||||
struct p9_rstatfs rs;
|
||||
int res;
|
||||
|
||||
fid = v9fs_fid_lookup(dentry);
|
||||
if (IS_ERR(fid)) {
|
||||
res = PTR_ERR(fid);
|
||||
goto done;
|
||||
}
|
||||
|
||||
v9ses = v9fs_dentry2v9ses(dentry);
|
||||
if (v9fs_proto_dotl(v9ses)) {
|
||||
res = p9_client_statfs(fid, &rs);
|
||||
if (res == 0) {
|
||||
buf->f_type = rs.type;
|
||||
buf->f_bsize = rs.bsize;
|
||||
buf->f_blocks = rs.blocks;
|
||||
buf->f_bfree = rs.bfree;
|
||||
buf->f_bavail = rs.bavail;
|
||||
buf->f_files = rs.files;
|
||||
buf->f_ffree = rs.ffree;
|
||||
buf->f_fsid.val[0] = rs.fsid & 0xFFFFFFFFUL;
|
||||
buf->f_fsid.val[1] = (rs.fsid >> 32) & 0xFFFFFFFFUL;
|
||||
buf->f_namelen = rs.namelen;
|
||||
}
|
||||
if (res != -ENOSYS)
|
||||
goto done;
|
||||
}
|
||||
res = simple_statfs(dentry, buf);
|
||||
done:
|
||||
return res;
|
||||
}
|
||||
|
||||
static int v9fs_drop_inode(struct inode *inode)
|
||||
{
|
||||
struct v9fs_session_info *v9ses;
|
||||
v9ses = v9fs_inode2v9ses(inode);
|
||||
if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE)
|
||||
return generic_drop_inode(inode);
|
||||
/*
|
||||
* in case of non cached mode always drop the
|
||||
* the inode because we want the inode attribute
|
||||
* to always match that on the server.
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int v9fs_write_inode(struct inode *inode,
|
||||
struct writeback_control *wbc)
|
||||
{
|
||||
int ret;
|
||||
struct p9_wstat wstat;
|
||||
struct v9fs_inode *v9inode;
|
||||
/*
|
||||
* send an fsync request to server irrespective of
|
||||
* wbc->sync_mode.
|
||||
*/
|
||||
p9_debug(P9_DEBUG_VFS, "%s: inode %p\n", __func__, inode);
|
||||
v9inode = V9FS_I(inode);
|
||||
if (!v9inode->writeback_fid)
|
||||
return 0;
|
||||
v9fs_blank_wstat(&wstat);
|
||||
|
||||
ret = p9_client_wstat(v9inode->writeback_fid, &wstat);
|
||||
if (ret < 0) {
|
||||
__mark_inode_dirty(inode, I_DIRTY_DATASYNC);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int v9fs_write_inode_dotl(struct inode *inode,
|
||||
struct writeback_control *wbc)
|
||||
{
|
||||
int ret;
|
||||
struct v9fs_inode *v9inode;
|
||||
/*
|
||||
* send an fsync request to server irrespective of
|
||||
* wbc->sync_mode.
|
||||
*/
|
||||
v9inode = V9FS_I(inode);
|
||||
p9_debug(P9_DEBUG_VFS, "%s: inode %p, writeback_fid %p\n",
|
||||
__func__, inode, v9inode->writeback_fid);
|
||||
if (!v9inode->writeback_fid)
|
||||
return 0;
|
||||
|
||||
ret = p9_client_fsync(v9inode->writeback_fid, 0);
|
||||
if (ret < 0) {
|
||||
__mark_inode_dirty(inode, I_DIRTY_DATASYNC);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct super_operations v9fs_super_ops = {
|
||||
.alloc_inode = v9fs_alloc_inode,
|
||||
.destroy_inode = v9fs_destroy_inode,
|
||||
.statfs = simple_statfs,
|
||||
.evict_inode = v9fs_evict_inode,
|
||||
.show_options = generic_show_options,
|
||||
.umount_begin = v9fs_umount_begin,
|
||||
.write_inode = v9fs_write_inode,
|
||||
};
|
||||
|
||||
static const struct super_operations v9fs_super_ops_dotl = {
|
||||
.alloc_inode = v9fs_alloc_inode,
|
||||
.destroy_inode = v9fs_destroy_inode,
|
||||
.statfs = v9fs_statfs,
|
||||
.drop_inode = v9fs_drop_inode,
|
||||
.evict_inode = v9fs_evict_inode,
|
||||
.show_options = generic_show_options,
|
||||
.umount_begin = v9fs_umount_begin,
|
||||
.write_inode = v9fs_write_inode_dotl,
|
||||
};
|
||||
|
||||
struct file_system_type v9fs_fs_type = {
|
||||
.name = "9p",
|
||||
.mount = v9fs_mount,
|
||||
.kill_sb = v9fs_kill_super,
|
||||
.owner = THIS_MODULE,
|
||||
.fs_flags = FS_RENAME_DOES_D_MOVE,
|
||||
};
|
||||
MODULE_ALIAS_FS("9p");
|
||||
195
addons/9p/src/4.4.180/xattr.c
Normal file
195
addons/9p/src/4.4.180/xattr.c
Normal file
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright IBM Corporation, 2010
|
||||
* Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/uio.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
|
||||
#include "fid.h"
|
||||
#include "xattr.h"
|
||||
|
||||
ssize_t v9fs_fid_xattr_get(struct p9_fid *fid, const char *name,
|
||||
void *buffer, size_t buffer_size)
|
||||
{
|
||||
ssize_t retval;
|
||||
u64 attr_size;
|
||||
struct p9_fid *attr_fid;
|
||||
struct kvec kvec = {.iov_base = buffer, .iov_len = buffer_size};
|
||||
struct iov_iter to;
|
||||
int err;
|
||||
|
||||
iov_iter_kvec(&to, READ | ITER_KVEC, &kvec, 1, buffer_size);
|
||||
|
||||
attr_fid = p9_client_xattrwalk(fid, name, &attr_size);
|
||||
if (IS_ERR(attr_fid)) {
|
||||
retval = PTR_ERR(attr_fid);
|
||||
p9_debug(P9_DEBUG_VFS, "p9_client_attrwalk failed %zd\n",
|
||||
retval);
|
||||
return retval;
|
||||
}
|
||||
if (attr_size > buffer_size) {
|
||||
if (!buffer_size) /* request to get the attr_size */
|
||||
retval = attr_size;
|
||||
else
|
||||
retval = -ERANGE;
|
||||
} else {
|
||||
iov_iter_truncate(&to, attr_size);
|
||||
retval = p9_client_read(attr_fid, 0, &to, &err);
|
||||
if (err)
|
||||
retval = err;
|
||||
}
|
||||
p9_client_clunk(attr_fid);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* v9fs_xattr_get()
|
||||
*
|
||||
* Copy an extended attribute into the buffer
|
||||
* provided, or compute the buffer size required.
|
||||
* Buffer is NULL to compute the size of the buffer required.
|
||||
*
|
||||
* Returns a negative error number on failure, or the number of bytes
|
||||
* used / required on success.
|
||||
*/
|
||||
ssize_t v9fs_xattr_get(struct dentry *dentry, const char *name,
|
||||
void *buffer, size_t buffer_size)
|
||||
{
|
||||
struct p9_fid *fid;
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "name = %s value_len = %zu\n",
|
||||
name, buffer_size);
|
||||
fid = v9fs_fid_lookup(dentry);
|
||||
if (IS_ERR(fid))
|
||||
return PTR_ERR(fid);
|
||||
|
||||
return v9fs_fid_xattr_get(fid, name, buffer, buffer_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* v9fs_xattr_set()
|
||||
*
|
||||
* Create, replace or remove an extended attribute for this inode. Buffer
|
||||
* is NULL to remove an existing extended attribute, and non-NULL to
|
||||
* either replace an existing extended attribute, or create a new extended
|
||||
* attribute. The flags XATTR_REPLACE and XATTR_CREATE
|
||||
* specify that an extended attribute must exist and must not exist
|
||||
* previous to the call, respectively.
|
||||
*
|
||||
* Returns 0, or a negative error number on failure.
|
||||
*/
|
||||
int v9fs_xattr_set(struct dentry *dentry, const char *name,
|
||||
const void *value, size_t value_len, int flags)
|
||||
{
|
||||
struct p9_fid *fid = v9fs_fid_lookup(dentry);
|
||||
if (IS_ERR(fid))
|
||||
return PTR_ERR(fid);
|
||||
return v9fs_fid_xattr_set(fid, name, value, value_len, flags);
|
||||
}
|
||||
|
||||
int v9fs_fid_xattr_set(struct p9_fid *fid, const char *name,
|
||||
const void *value, size_t value_len, int flags)
|
||||
{
|
||||
struct kvec kvec = {.iov_base = (void *)value, .iov_len = value_len};
|
||||
struct iov_iter from;
|
||||
int retval, err;
|
||||
|
||||
iov_iter_kvec(&from, WRITE | ITER_KVEC, &kvec, 1, value_len);
|
||||
|
||||
p9_debug(P9_DEBUG_VFS, "name = %s value_len = %zu flags = %d\n",
|
||||
name, value_len, flags);
|
||||
|
||||
/* Clone it */
|
||||
fid = p9_client_walk(fid, 0, NULL, 1);
|
||||
if (IS_ERR(fid))
|
||||
return PTR_ERR(fid);
|
||||
|
||||
/*
|
||||
* On success fid points to xattr
|
||||
*/
|
||||
retval = p9_client_xattrcreate(fid, name, value_len, flags);
|
||||
if (retval < 0)
|
||||
p9_debug(P9_DEBUG_VFS, "p9_client_xattrcreate failed %d\n",
|
||||
retval);
|
||||
else
|
||||
p9_client_write(fid, 0, &from, &retval);
|
||||
err = p9_client_clunk(fid);
|
||||
if (!retval && err)
|
||||
retval = err;
|
||||
return retval;
|
||||
}
|
||||
|
||||
ssize_t v9fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
|
||||
{
|
||||
return v9fs_xattr_get(dentry, NULL, buffer, buffer_size);
|
||||
}
|
||||
|
||||
static int v9fs_xattr_handler_get(const struct xattr_handler *handler,
|
||||
struct dentry *dentry, const char *name,
|
||||
void *buffer, size_t size)
|
||||
{
|
||||
const char *full_name = xattr_full_name(handler, name);
|
||||
|
||||
if (strcmp(name, "") == 0)
|
||||
return -EINVAL;
|
||||
return v9fs_xattr_get(dentry, full_name, buffer, size);
|
||||
}
|
||||
|
||||
static int v9fs_xattr_handler_set(const struct xattr_handler *handler,
|
||||
struct dentry *dentry, const char *name,
|
||||
const void *value, size_t size, int flags)
|
||||
{
|
||||
const char *full_name = xattr_full_name(handler, name);
|
||||
|
||||
if (strcmp(name, "") == 0)
|
||||
return -EINVAL;
|
||||
return v9fs_xattr_set(dentry, full_name, value, size, flags);
|
||||
}
|
||||
|
||||
static struct xattr_handler v9fs_xattr_user_handler = {
|
||||
.prefix = XATTR_USER_PREFIX,
|
||||
.get = v9fs_xattr_handler_get,
|
||||
.set = v9fs_xattr_handler_set,
|
||||
};
|
||||
|
||||
static struct xattr_handler v9fs_xattr_trusted_handler = {
|
||||
.prefix = XATTR_TRUSTED_PREFIX,
|
||||
.get = v9fs_xattr_handler_get,
|
||||
.set = v9fs_xattr_handler_set,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_9P_FS_SECURITY
|
||||
static struct xattr_handler v9fs_xattr_security_handler = {
|
||||
.prefix = XATTR_SECURITY_PREFIX,
|
||||
.get = v9fs_xattr_handler_get,
|
||||
.set = v9fs_xattr_handler_set,
|
||||
};
|
||||
#endif
|
||||
|
||||
const struct xattr_handler *v9fs_xattr_handlers[] = {
|
||||
&v9fs_xattr_user_handler,
|
||||
&v9fs_xattr_trusted_handler,
|
||||
#ifdef CONFIG_9P_FS_POSIX_ACL
|
||||
&v9fs_xattr_acl_access_handler,
|
||||
&v9fs_xattr_acl_default_handler,
|
||||
#endif
|
||||
#ifdef CONFIG_9P_FS_SECURITY
|
||||
&v9fs_xattr_security_handler,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
34
addons/9p/src/4.4.180/xattr.h
Normal file
34
addons/9p/src/4.4.180/xattr.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright IBM Corporation, 2010
|
||||
* Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
#ifndef FS_9P_XATTR_H
|
||||
#define FS_9P_XATTR_H
|
||||
|
||||
#include <linux/xattr.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
|
||||
extern const struct xattr_handler *v9fs_xattr_handlers[];
|
||||
extern const struct xattr_handler v9fs_xattr_acl_access_handler;
|
||||
extern const struct xattr_handler v9fs_xattr_acl_default_handler;
|
||||
|
||||
extern ssize_t v9fs_fid_xattr_get(struct p9_fid *, const char *,
|
||||
void *, size_t);
|
||||
extern ssize_t v9fs_xattr_get(struct dentry *, const char *,
|
||||
void *, size_t);
|
||||
extern int v9fs_fid_xattr_set(struct p9_fid *, const char *,
|
||||
const void *, size_t, int);
|
||||
extern int v9fs_xattr_set(struct dentry *, const char *,
|
||||
const void *, size_t, int);
|
||||
extern ssize_t v9fs_listxattr(struct dentry *, char *, size_t);
|
||||
#endif /* FS_9P_XATTR_H */
|
||||
Reference in New Issue
Block a user