Merge branch 'nfs-for-2.6.39' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6
* 'nfs-for-2.6.39' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6: (28 commits) Cleanup XDR parsing for LAYOUTGET, GETDEVICEINFO NFSv4.1 convert layoutcommit sync to boolean NFSv4.1 pnfs_layoutcommit_inode fixes NFS: Determine initial mount security NFS: use secinfo when crossing mountpoints NFS: Add secinfo procedure NFS: lookup supports alternate client NFS: convert call_sync() to a function NFSv4.1 remove temp code that prevented ds commits NFSv4.1: layoutcommit NFSv4.1: filelayout driver specific code for COMMIT NFSv4.1: remove GETATTR from ds commits NFSv4.1: add generic layer hooks for pnfs COMMIT NFSv4.1: alloc and free commit_buckets NFSv4.1: shift filelayout_free_lseg NFSv4.1: pull out code from nfs_commit_release NFSv4.1: pull error handling out of nfs_commit_list NFSv4.1: add callback to nfs4_commit_done NFSv4.1: rearrange nfs_commit_rpcsetup NFSv4.1: don't send COMMIT to ds for data sync writes ...
This commit is contained in:
Коммит
40471856f2
89
fs/nfs/dir.c
89
fs/nfs/dir.c
|
@ -44,6 +44,7 @@
|
|||
/* #define NFS_DEBUG_VERBOSE 1 */
|
||||
|
||||
static int nfs_opendir(struct inode *, struct file *);
|
||||
static int nfs_closedir(struct inode *, struct file *);
|
||||
static int nfs_readdir(struct file *, void *, filldir_t);
|
||||
static struct dentry *nfs_lookup(struct inode *, struct dentry *, struct nameidata *);
|
||||
static int nfs_create(struct inode *, struct dentry *, int, struct nameidata *);
|
||||
|
@ -64,7 +65,7 @@ const struct file_operations nfs_dir_operations = {
|
|||
.read = generic_read_dir,
|
||||
.readdir = nfs_readdir,
|
||||
.open = nfs_opendir,
|
||||
.release = nfs_release,
|
||||
.release = nfs_closedir,
|
||||
.fsync = nfs_fsync_dir,
|
||||
};
|
||||
|
||||
|
@ -133,13 +134,35 @@ const struct inode_operations nfs4_dir_inode_operations = {
|
|||
|
||||
#endif /* CONFIG_NFS_V4 */
|
||||
|
||||
static struct nfs_open_dir_context *alloc_nfs_open_dir_context(struct rpc_cred *cred)
|
||||
{
|
||||
struct nfs_open_dir_context *ctx;
|
||||
ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
|
||||
if (ctx != NULL) {
|
||||
ctx->duped = 0;
|
||||
ctx->dir_cookie = 0;
|
||||
ctx->dup_cookie = 0;
|
||||
ctx->cred = get_rpccred(cred);
|
||||
} else
|
||||
ctx = ERR_PTR(-ENOMEM);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static void put_nfs_open_dir_context(struct nfs_open_dir_context *ctx)
|
||||
{
|
||||
put_rpccred(ctx->cred);
|
||||
kfree(ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Open file
|
||||
*/
|
||||
static int
|
||||
nfs_opendir(struct inode *inode, struct file *filp)
|
||||
{
|
||||
int res;
|
||||
int res = 0;
|
||||
struct nfs_open_dir_context *ctx;
|
||||
struct rpc_cred *cred;
|
||||
|
||||
dfprintk(FILE, "NFS: open dir(%s/%s)\n",
|
||||
filp->f_path.dentry->d_parent->d_name.name,
|
||||
|
@ -147,8 +170,15 @@ nfs_opendir(struct inode *inode, struct file *filp)
|
|||
|
||||
nfs_inc_stats(inode, NFSIOS_VFSOPEN);
|
||||
|
||||
/* Call generic open code in order to cache credentials */
|
||||
res = nfs_open(inode, filp);
|
||||
cred = rpc_lookup_cred();
|
||||
if (IS_ERR(cred))
|
||||
return PTR_ERR(cred);
|
||||
ctx = alloc_nfs_open_dir_context(cred);
|
||||
if (IS_ERR(ctx)) {
|
||||
res = PTR_ERR(ctx);
|
||||
goto out;
|
||||
}
|
||||
filp->private_data = ctx;
|
||||
if (filp->f_path.dentry == filp->f_path.mnt->mnt_root) {
|
||||
/* This is a mountpoint, so d_revalidate will never
|
||||
* have been called, so we need to refresh the
|
||||
|
@ -156,9 +186,18 @@ nfs_opendir(struct inode *inode, struct file *filp)
|
|||
*/
|
||||
__nfs_revalidate_inode(NFS_SERVER(inode), inode);
|
||||
}
|
||||
out:
|
||||
put_rpccred(cred);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int
|
||||
nfs_closedir(struct inode *inode, struct file *filp)
|
||||
{
|
||||
put_nfs_open_dir_context(filp->private_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct nfs_cache_array_entry {
|
||||
u64 cookie;
|
||||
u64 ino;
|
||||
|
@ -284,19 +323,20 @@ int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descri
|
|||
{
|
||||
loff_t diff = desc->file->f_pos - desc->current_index;
|
||||
unsigned int index;
|
||||
struct nfs_open_dir_context *ctx = desc->file->private_data;
|
||||
|
||||
if (diff < 0)
|
||||
goto out_eof;
|
||||
if (diff >= array->size) {
|
||||
if (array->eof_index >= 0)
|
||||
goto out_eof;
|
||||
desc->current_index += array->size;
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
index = (unsigned int)diff;
|
||||
*desc->dir_cookie = array->array[index].cookie;
|
||||
desc->cache_entry_index = index;
|
||||
ctx->duped = 0;
|
||||
return 0;
|
||||
out_eof:
|
||||
desc->eof = 1;
|
||||
|
@ -307,10 +347,18 @@ static
|
|||
int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_descriptor_t *desc)
|
||||
{
|
||||
int i;
|
||||
loff_t new_pos;
|
||||
int status = -EAGAIN;
|
||||
struct nfs_open_dir_context *ctx = desc->file->private_data;
|
||||
|
||||
for (i = 0; i < array->size; i++) {
|
||||
if (array->array[i].cookie == *desc->dir_cookie) {
|
||||
new_pos = desc->current_index + i;
|
||||
if (new_pos < desc->file->f_pos) {
|
||||
ctx->dup_cookie = *desc->dir_cookie;
|
||||
ctx->duped = 1;
|
||||
}
|
||||
desc->file->f_pos = new_pos;
|
||||
desc->cache_entry_index = i;
|
||||
return 0;
|
||||
}
|
||||
|
@ -342,6 +390,7 @@ int nfs_readdir_search_array(nfs_readdir_descriptor_t *desc)
|
|||
|
||||
if (status == -EAGAIN) {
|
||||
desc->last_cookie = array->last_cookie;
|
||||
desc->current_index += array->size;
|
||||
desc->page_index++;
|
||||
}
|
||||
nfs_readdir_release_array(desc->page);
|
||||
|
@ -354,7 +403,8 @@ static
|
|||
int nfs_readdir_xdr_filler(struct page **pages, nfs_readdir_descriptor_t *desc,
|
||||
struct nfs_entry *entry, struct file *file, struct inode *inode)
|
||||
{
|
||||
struct rpc_cred *cred = nfs_file_cred(file);
|
||||
struct nfs_open_dir_context *ctx = file->private_data;
|
||||
struct rpc_cred *cred = ctx->cred;
|
||||
unsigned long timestamp, gencount;
|
||||
int error;
|
||||
|
||||
|
@ -693,6 +743,20 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
|
|||
int i = 0;
|
||||
int res = 0;
|
||||
struct nfs_cache_array *array = NULL;
|
||||
struct nfs_open_dir_context *ctx = file->private_data;
|
||||
|
||||
if (ctx->duped != 0 && ctx->dup_cookie == *desc->dir_cookie) {
|
||||
if (printk_ratelimit()) {
|
||||
pr_notice("NFS: directory %s/%s contains a readdir loop. "
|
||||
"Please contact your server vendor. "
|
||||
"Offending cookie: %llu\n",
|
||||
file->f_dentry->d_parent->d_name.name,
|
||||
file->f_dentry->d_name.name,
|
||||
*desc->dir_cookie);
|
||||
}
|
||||
res = -ELOOP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
array = nfs_readdir_get_array(desc->page);
|
||||
if (IS_ERR(array)) {
|
||||
|
@ -785,6 +849,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
|
|||
struct inode *inode = dentry->d_inode;
|
||||
nfs_readdir_descriptor_t my_desc,
|
||||
*desc = &my_desc;
|
||||
struct nfs_open_dir_context *dir_ctx = filp->private_data;
|
||||
int res;
|
||||
|
||||
dfprintk(FILE, "NFS: readdir(%s/%s) starting at cookie %llu\n",
|
||||
|
@ -801,7 +866,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
|
|||
memset(desc, 0, sizeof(*desc));
|
||||
|
||||
desc->file = filp;
|
||||
desc->dir_cookie = &nfs_file_open_context(filp)->dir_cookie;
|
||||
desc->dir_cookie = &dir_ctx->dir_cookie;
|
||||
desc->decode = NFS_PROTO(inode)->decode_dirent;
|
||||
desc->plus = NFS_USE_READDIRPLUS(inode);
|
||||
|
||||
|
@ -853,6 +918,7 @@ static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int origin)
|
|||
{
|
||||
struct dentry *dentry = filp->f_path.dentry;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct nfs_open_dir_context *dir_ctx = filp->private_data;
|
||||
|
||||
dfprintk(FILE, "NFS: llseek dir(%s/%s, %lld, %d)\n",
|
||||
dentry->d_parent->d_name.name,
|
||||
|
@ -872,7 +938,8 @@ static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int origin)
|
|||
}
|
||||
if (offset != filp->f_pos) {
|
||||
filp->f_pos = offset;
|
||||
nfs_file_open_context(filp)->dir_cookie = 0;
|
||||
dir_ctx->dir_cookie = 0;
|
||||
dir_ctx->duped = 0;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
|
@ -1068,7 +1135,7 @@ static int nfs_lookup_revalidate(struct dentry *dentry, struct nameidata *nd)
|
|||
if (fhandle == NULL || fattr == NULL)
|
||||
goto out_error;
|
||||
|
||||
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
|
||||
error = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, &dentry->d_name, fhandle, fattr);
|
||||
if (error)
|
||||
goto out_bad;
|
||||
if (nfs_compare_fh(NFS_FH(inode), fhandle))
|
||||
|
@ -1224,7 +1291,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
|
|||
parent = dentry->d_parent;
|
||||
/* Protect against concurrent sillydeletes */
|
||||
nfs_block_sillyrename(parent);
|
||||
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
|
||||
error = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, &dentry->d_name, fhandle, fattr);
|
||||
if (error == -ENOENT)
|
||||
goto no_entry;
|
||||
if (error < 0) {
|
||||
|
@ -1562,7 +1629,7 @@ int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
|
|||
if (dentry->d_inode)
|
||||
goto out;
|
||||
if (fhandle->size == 0) {
|
||||
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
|
||||
error = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, &dentry->d_name, fhandle, fattr);
|
||||
if (error)
|
||||
goto out_error;
|
||||
}
|
||||
|
|
|
@ -326,6 +326,9 @@ nfs_file_fsync(struct file *file, int datasync)
|
|||
ret = xchg(&ctx->error, 0);
|
||||
if (!ret && status < 0)
|
||||
ret = status;
|
||||
if (!ret && !datasync)
|
||||
/* application has asked for meta-data sync */
|
||||
ret = pnfs_layoutcommit_inode(inode, true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -222,6 +222,10 @@ struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh,
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (fattr->valid & NFS_ATTR_FATTR_FSID &&
|
||||
!nfs_fsid_equal(&server->fsid, &fattr->fsid))
|
||||
memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid));
|
||||
|
||||
inode = nfs_fhget(sb, mntfh, fattr);
|
||||
if (IS_ERR(inode)) {
|
||||
dprintk("nfs_get_root: get root inode failed\n");
|
||||
|
|
|
@ -254,7 +254,9 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
|
|||
struct inode *inode = ERR_PTR(-ENOENT);
|
||||
unsigned long hash;
|
||||
|
||||
if ((fattr->valid & NFS_ATTR_FATTR_FILEID) == 0)
|
||||
nfs_attr_check_mountpoint(sb, fattr);
|
||||
|
||||
if ((fattr->valid & NFS_ATTR_FATTR_FILEID) == 0 && (fattr->valid & NFS_ATTR_FATTR_MOUNTPOINT) == 0)
|
||||
goto out_no_inode;
|
||||
if ((fattr->valid & NFS_ATTR_FATTR_TYPE) == 0)
|
||||
goto out_no_inode;
|
||||
|
@ -298,8 +300,8 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
|
|||
if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS))
|
||||
set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags);
|
||||
/* Deal with crossing mountpoints */
|
||||
if ((fattr->valid & NFS_ATTR_FATTR_FSID)
|
||||
&& !nfs_fsid_equal(&NFS_SB(sb)->fsid, &fattr->fsid)) {
|
||||
if (fattr->valid & NFS_ATTR_FATTR_MOUNTPOINT ||
|
||||
fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) {
|
||||
if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)
|
||||
inode->i_op = &nfs_referral_inode_operations;
|
||||
else
|
||||
|
@ -639,7 +641,6 @@ struct nfs_open_context *alloc_nfs_open_context(struct path *path, struct rpc_cr
|
|||
ctx->mode = f_mode;
|
||||
ctx->flags = 0;
|
||||
ctx->error = 0;
|
||||
ctx->dir_cookie = 0;
|
||||
nfs_init_lock_context(&ctx->lock_context);
|
||||
ctx->lock_context.open_context = ctx;
|
||||
INIT_LIST_HEAD(&ctx->list);
|
||||
|
@ -1471,6 +1472,7 @@ static inline void nfs4_init_once(struct nfs_inode *nfsi)
|
|||
nfsi->delegation_state = 0;
|
||||
init_rwsem(&nfsi->rwsem);
|
||||
nfsi->layout = NULL;
|
||||
atomic_set(&nfsi->commits_outstanding, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,12 @@ static inline int nfs4_has_persistent_session(const struct nfs_client *clp)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline void nfs_attr_check_mountpoint(struct super_block *parent, struct nfs_fattr *fattr)
|
||||
{
|
||||
if (!nfs_fsid_equal(&NFS_SB(parent)->fsid, &fattr->fsid))
|
||||
fattr->valid |= NFS_ATTR_FATTR_MOUNTPOINT;
|
||||
}
|
||||
|
||||
struct nfs_clone_mount {
|
||||
const struct super_block *sb;
|
||||
const struct dentry *dentry;
|
||||
|
@ -214,6 +220,7 @@ extern const u32 nfs41_maxwrite_overhead;
|
|||
/* nfs4proc.c */
|
||||
#ifdef CONFIG_NFS_V4
|
||||
extern struct rpc_procinfo nfs4_procedures[];
|
||||
void nfs_fixup_secinfo_attributes(struct nfs_fattr *, struct nfs_fh *);
|
||||
#endif
|
||||
|
||||
extern int nfs4_init_ds_session(struct nfs_client *clp);
|
||||
|
@ -276,11 +283,25 @@ extern int nfs_initiate_read(struct nfs_read_data *data, struct rpc_clnt *clnt,
|
|||
extern void nfs_read_prepare(struct rpc_task *task, void *calldata);
|
||||
|
||||
/* write.c */
|
||||
extern void nfs_commit_free(struct nfs_write_data *p);
|
||||
extern int nfs_initiate_write(struct nfs_write_data *data,
|
||||
struct rpc_clnt *clnt,
|
||||
const struct rpc_call_ops *call_ops,
|
||||
int how);
|
||||
extern void nfs_write_prepare(struct rpc_task *task, void *calldata);
|
||||
extern int nfs_initiate_commit(struct nfs_write_data *data,
|
||||
struct rpc_clnt *clnt,
|
||||
const struct rpc_call_ops *call_ops,
|
||||
int how);
|
||||
extern void nfs_init_commit(struct nfs_write_data *data,
|
||||
struct list_head *head,
|
||||
struct pnfs_layout_segment *lseg);
|
||||
void nfs_retry_commit(struct list_head *page_list,
|
||||
struct pnfs_layout_segment *lseg);
|
||||
void nfs_commit_clear_lock(struct nfs_inode *nfsi);
|
||||
void nfs_commitdata_release(void *data);
|
||||
void nfs_commit_release_pages(struct nfs_write_data *data);
|
||||
|
||||
#ifdef CONFIG_MIGRATION
|
||||
extern int nfs_migrate_page(struct address_space *,
|
||||
struct page *, struct page *);
|
||||
|
@ -296,12 +317,14 @@ extern int nfs4_init_client(struct nfs_client *clp,
|
|||
rpc_authflavor_t authflavour,
|
||||
int noresvport);
|
||||
extern void nfs4_reset_write(struct rpc_task *task, struct nfs_write_data *data);
|
||||
extern int _nfs4_call_sync(struct nfs_server *server,
|
||||
extern int _nfs4_call_sync(struct rpc_clnt *clnt,
|
||||
struct nfs_server *server,
|
||||
struct rpc_message *msg,
|
||||
struct nfs4_sequence_args *args,
|
||||
struct nfs4_sequence_res *res,
|
||||
int cache_reply);
|
||||
extern int _nfs4_call_sync_session(struct nfs_server *server,
|
||||
extern int _nfs4_call_sync_session(struct rpc_clnt *clnt,
|
||||
struct nfs_server *server,
|
||||
struct rpc_message *msg,
|
||||
struct nfs4_sequence_args *args,
|
||||
struct nfs4_sequence_res *res,
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/string.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/vfs.h>
|
||||
#include <linux/sunrpc/gss_api.h>
|
||||
#include "internal.h"
|
||||
|
||||
#define NFSDBG_FACILITY NFSDBG_VFS
|
||||
|
@ -27,7 +28,8 @@ int nfs_mountpoint_expiry_timeout = 500 * HZ;
|
|||
|
||||
static struct vfsmount *nfs_do_submount(struct dentry *dentry,
|
||||
struct nfs_fh *fh,
|
||||
struct nfs_fattr *fattr);
|
||||
struct nfs_fattr *fattr,
|
||||
rpc_authflavor_t authflavor);
|
||||
|
||||
/*
|
||||
* nfs_path - reconstruct the path given an arbitrary dentry
|
||||
|
@ -116,6 +118,100 @@ Elong:
|
|||
return ERR_PTR(-ENAMETOOLONG);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NFS_V4
|
||||
static rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors, struct inode *inode)
|
||||
{
|
||||
struct gss_api_mech *mech;
|
||||
struct xdr_netobj oid;
|
||||
int i;
|
||||
rpc_authflavor_t pseudoflavor = RPC_AUTH_UNIX;
|
||||
|
||||
for (i = 0; i < flavors->num_flavors; i++) {
|
||||
struct nfs4_secinfo_flavor *flavor;
|
||||
flavor = &flavors->flavors[i];
|
||||
|
||||
if (flavor->flavor == RPC_AUTH_NULL || flavor->flavor == RPC_AUTH_UNIX) {
|
||||
pseudoflavor = flavor->flavor;
|
||||
break;
|
||||
} else if (flavor->flavor == RPC_AUTH_GSS) {
|
||||
oid.len = flavor->gss.sec_oid4.len;
|
||||
oid.data = flavor->gss.sec_oid4.data;
|
||||
mech = gss_mech_get_by_OID(&oid);
|
||||
if (!mech)
|
||||
continue;
|
||||
pseudoflavor = gss_svc_to_pseudoflavor(mech, flavor->gss.service);
|
||||
gss_mech_put(mech);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return pseudoflavor;
|
||||
}
|
||||
|
||||
static rpc_authflavor_t nfs_negotiate_security(const struct dentry *parent, const struct dentry *dentry)
|
||||
{
|
||||
int status = 0;
|
||||
struct page *page;
|
||||
struct nfs4_secinfo_flavors *flavors;
|
||||
int (*secinfo)(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *);
|
||||
rpc_authflavor_t flavor = RPC_AUTH_UNIX;
|
||||
|
||||
secinfo = NFS_PROTO(parent->d_inode)->secinfo;
|
||||
if (secinfo != NULL) {
|
||||
page = alloc_page(GFP_KERNEL);
|
||||
if (!page) {
|
||||
status = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
flavors = page_address(page);
|
||||
status = secinfo(parent->d_inode, &dentry->d_name, flavors);
|
||||
flavor = nfs_find_best_sec(flavors, dentry->d_inode);
|
||||
put_page(page);
|
||||
}
|
||||
|
||||
return flavor;
|
||||
|
||||
out:
|
||||
status = -ENOMEM;
|
||||
return status;
|
||||
}
|
||||
|
||||
static rpc_authflavor_t nfs_lookup_with_sec(struct nfs_server *server, struct dentry *parent,
|
||||
struct dentry *dentry, struct path *path,
|
||||
struct nfs_fh *fh, struct nfs_fattr *fattr)
|
||||
{
|
||||
rpc_authflavor_t flavor;
|
||||
struct rpc_clnt *clone;
|
||||
struct rpc_auth *auth;
|
||||
int err;
|
||||
|
||||
flavor = nfs_negotiate_security(parent, path->dentry);
|
||||
if (flavor < 0)
|
||||
goto out;
|
||||
clone = rpc_clone_client(server->client);
|
||||
auth = rpcauth_create(flavor, clone);
|
||||
if (!auth) {
|
||||
flavor = -EIO;
|
||||
goto out;
|
||||
}
|
||||
err = server->nfs_client->rpc_ops->lookup(clone, parent->d_inode,
|
||||
&path->dentry->d_name,
|
||||
fh, fattr);
|
||||
if (err < 0)
|
||||
flavor = err;
|
||||
out:
|
||||
return flavor;
|
||||
}
|
||||
#else /* CONFIG_NFS_V4 */
|
||||
static inline rpc_authflavor_t nfs_lookup_with_sec(struct nfs_server *server,
|
||||
struct dentry *parent, struct dentry *dentry,
|
||||
struct path *path, struct nfs_fh *fh,
|
||||
struct nfs_fattr *fattr)
|
||||
{
|
||||
return -EPERM;
|
||||
}
|
||||
#endif /* CONFIG_NFS_V4 */
|
||||
|
||||
/*
|
||||
* nfs_d_automount - Handle crossing a mountpoint on the server
|
||||
* @path - The mountpoint
|
||||
|
@ -136,6 +232,7 @@ struct vfsmount *nfs_d_automount(struct path *path)
|
|||
struct nfs_fh *fh = NULL;
|
||||
struct nfs_fattr *fattr = NULL;
|
||||
int err;
|
||||
rpc_authflavor_t flavor = 1;
|
||||
|
||||
dprintk("--> nfs_d_automount()\n");
|
||||
|
||||
|
@ -153,9 +250,16 @@ struct vfsmount *nfs_d_automount(struct path *path)
|
|||
|
||||
/* Look it up again to get its attributes */
|
||||
parent = dget_parent(path->dentry);
|
||||
err = server->nfs_client->rpc_ops->lookup(parent->d_inode,
|
||||
err = server->nfs_client->rpc_ops->lookup(server->client, parent->d_inode,
|
||||
&path->dentry->d_name,
|
||||
fh, fattr);
|
||||
if (err == -EPERM) {
|
||||
flavor = nfs_lookup_with_sec(server, parent, path->dentry, path, fh, fattr);
|
||||
if (flavor < 0)
|
||||
err = flavor;
|
||||
else
|
||||
err = 0;
|
||||
}
|
||||
dput(parent);
|
||||
if (err != 0) {
|
||||
mnt = ERR_PTR(err);
|
||||
|
@ -165,7 +269,7 @@ struct vfsmount *nfs_d_automount(struct path *path)
|
|||
if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)
|
||||
mnt = nfs_do_refmount(path->dentry);
|
||||
else
|
||||
mnt = nfs_do_submount(path->dentry, fh, fattr);
|
||||
mnt = nfs_do_submount(path->dentry, fh, fattr, flavor);
|
||||
if (IS_ERR(mnt))
|
||||
goto out;
|
||||
|
||||
|
@ -232,17 +336,20 @@ static struct vfsmount *nfs_do_clone_mount(struct nfs_server *server,
|
|||
* @dentry - parent directory
|
||||
* @fh - filehandle for new root dentry
|
||||
* @fattr - attributes for new root inode
|
||||
* @authflavor - security flavor to use when performing the mount
|
||||
*
|
||||
*/
|
||||
static struct vfsmount *nfs_do_submount(struct dentry *dentry,
|
||||
struct nfs_fh *fh,
|
||||
struct nfs_fattr *fattr)
|
||||
struct nfs_fattr *fattr,
|
||||
rpc_authflavor_t authflavor)
|
||||
{
|
||||
struct nfs_clone_mount mountdata = {
|
||||
.sb = dentry->d_sb,
|
||||
.dentry = dentry,
|
||||
.fh = fh,
|
||||
.fattr = fattr,
|
||||
.authflavor = authflavor,
|
||||
};
|
||||
struct vfsmount *mnt = ERR_PTR(-ENOMEM);
|
||||
char *page = (char *) __get_free_page(GFP_USER);
|
||||
|
|
|
@ -141,7 +141,7 @@ nfs3_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
|
|||
}
|
||||
|
||||
static int
|
||||
nfs3_proc_lookup(struct inode *dir, struct qstr *name,
|
||||
nfs3_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qstr *name,
|
||||
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
||||
{
|
||||
struct nfs3_diropargs arg = {
|
||||
|
|
|
@ -57,7 +57,8 @@ enum nfs4_session_state {
|
|||
struct nfs4_minor_version_ops {
|
||||
u32 minor_version;
|
||||
|
||||
int (*call_sync)(struct nfs_server *server,
|
||||
int (*call_sync)(struct rpc_clnt *clnt,
|
||||
struct nfs_server *server,
|
||||
struct rpc_message *msg,
|
||||
struct nfs4_sequence_args *args,
|
||||
struct nfs4_sequence_res *res,
|
||||
|
@ -262,6 +263,8 @@ extern int nfs4_proc_destroy_session(struct nfs4_session *);
|
|||
extern int nfs4_init_session(struct nfs_server *server);
|
||||
extern int nfs4_proc_get_lease_time(struct nfs_client *clp,
|
||||
struct nfs_fsinfo *fsinfo);
|
||||
extern int nfs4_proc_layoutcommit(struct nfs4_layoutcommit_data *data,
|
||||
bool sync);
|
||||
|
||||
static inline bool
|
||||
is_ds_only_client(struct nfs_client *clp)
|
||||
|
|
|
@ -153,6 +153,23 @@ static int filelayout_read_done_cb(struct rpc_task *task,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We reference the rpc_cred of the first WRITE that triggers the need for
|
||||
* a LAYOUTCOMMIT, and use it to send the layoutcommit compound.
|
||||
* rfc5661 is not clear about which credential should be used.
|
||||
*/
|
||||
static void
|
||||
filelayout_set_layoutcommit(struct nfs_write_data *wdata)
|
||||
{
|
||||
if (FILELAYOUT_LSEG(wdata->lseg)->commit_through_mds ||
|
||||
wdata->res.verf->committed == NFS_FILE_SYNC)
|
||||
return;
|
||||
|
||||
pnfs_set_layoutcommit(wdata);
|
||||
dprintk("%s ionde %lu pls_end_pos %lu\n", __func__, wdata->inode->i_ino,
|
||||
(unsigned long) wdata->lseg->pls_end_pos);
|
||||
}
|
||||
|
||||
/*
|
||||
* Call ops for the async read/write cases
|
||||
* In the case of dense layouts, the offset needs to be reset to its
|
||||
|
@ -210,6 +227,38 @@ static int filelayout_write_done_cb(struct rpc_task *task,
|
|||
return -EAGAIN;
|
||||
}
|
||||
|
||||
filelayout_set_layoutcommit(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Fake up some data that will cause nfs_commit_release to retry the writes. */
|
||||
static void prepare_to_resend_writes(struct nfs_write_data *data)
|
||||
{
|
||||
struct nfs_page *first = nfs_list_entry(data->pages.next);
|
||||
|
||||
data->task.tk_status = 0;
|
||||
memcpy(data->verf.verifier, first->wb_verf.verifier,
|
||||
sizeof(first->wb_verf.verifier));
|
||||
data->verf.verifier[0]++; /* ensure verifier mismatch */
|
||||
}
|
||||
|
||||
static int filelayout_commit_done_cb(struct rpc_task *task,
|
||||
struct nfs_write_data *data)
|
||||
{
|
||||
int reset = 0;
|
||||
|
||||
if (filelayout_async_handle_error(task, data->args.context->state,
|
||||
data->ds_clp, &reset) == -EAGAIN) {
|
||||
dprintk("%s calling restart ds_clp %p ds_clp->cl_session %p\n",
|
||||
__func__, data->ds_clp, data->ds_clp->cl_session);
|
||||
if (reset) {
|
||||
prepare_to_resend_writes(data);
|
||||
filelayout_set_lo_fail(data->lseg);
|
||||
} else
|
||||
nfs_restart_rpc(task, data->ds_clp);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -240,6 +289,16 @@ static void filelayout_write_release(void *data)
|
|||
wdata->mds_ops->rpc_release(data);
|
||||
}
|
||||
|
||||
static void filelayout_commit_release(void *data)
|
||||
{
|
||||
struct nfs_write_data *wdata = (struct nfs_write_data *)data;
|
||||
|
||||
nfs_commit_release_pages(wdata);
|
||||
if (atomic_dec_and_test(&NFS_I(wdata->inode)->commits_outstanding))
|
||||
nfs_commit_clear_lock(NFS_I(wdata->inode));
|
||||
nfs_commitdata_release(wdata);
|
||||
}
|
||||
|
||||
struct rpc_call_ops filelayout_read_call_ops = {
|
||||
.rpc_call_prepare = filelayout_read_prepare,
|
||||
.rpc_call_done = filelayout_read_call_done,
|
||||
|
@ -252,6 +311,12 @@ struct rpc_call_ops filelayout_write_call_ops = {
|
|||
.rpc_release = filelayout_write_release,
|
||||
};
|
||||
|
||||
struct rpc_call_ops filelayout_commit_call_ops = {
|
||||
.rpc_call_prepare = filelayout_write_prepare,
|
||||
.rpc_call_done = filelayout_write_call_done,
|
||||
.rpc_release = filelayout_commit_release,
|
||||
};
|
||||
|
||||
static enum pnfs_try_status
|
||||
filelayout_read_pagelist(struct nfs_read_data *data)
|
||||
{
|
||||
|
@ -320,10 +385,6 @@ filelayout_write_pagelist(struct nfs_write_data *data, int sync)
|
|||
data->inode->i_ino, sync, (size_t) data->args.count, offset,
|
||||
ntohl(ds->ds_ip_addr), ntohs(ds->ds_port));
|
||||
|
||||
/* We can't handle commit to ds yet */
|
||||
if (!FILELAYOUT_LSEG(lseg)->commit_through_mds)
|
||||
data->args.stable = NFS_FILE_SYNC;
|
||||
|
||||
data->write_done_cb = filelayout_write_done_cb;
|
||||
data->ds_clp = ds->ds_clp;
|
||||
fh = nfs4_fl_select_ds_fh(lseg, j);
|
||||
|
@ -441,12 +502,33 @@ filelayout_decode_layout(struct pnfs_layout_hdr *flo,
|
|||
struct nfs4_layoutget_res *lgr,
|
||||
struct nfs4_deviceid *id)
|
||||
{
|
||||
uint32_t *p = (uint32_t *)lgr->layout.buf;
|
||||
struct xdr_stream stream;
|
||||
struct xdr_buf buf = {
|
||||
.pages = lgr->layoutp->pages,
|
||||
.page_len = lgr->layoutp->len,
|
||||
.buflen = lgr->layoutp->len,
|
||||
.len = lgr->layoutp->len,
|
||||
};
|
||||
struct page *scratch;
|
||||
__be32 *p;
|
||||
uint32_t nfl_util;
|
||||
int i;
|
||||
|
||||
dprintk("%s: set_layout_map Begin\n", __func__);
|
||||
|
||||
scratch = alloc_page(GFP_KERNEL);
|
||||
if (!scratch)
|
||||
return -ENOMEM;
|
||||
|
||||
xdr_init_decode(&stream, &buf, NULL);
|
||||
xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE);
|
||||
|
||||
/* 20 = ufl_util (4), first_stripe_index (4), pattern_offset (8),
|
||||
* num_fh (4) */
|
||||
p = xdr_inline_decode(&stream, NFS4_DEVICEID4_SIZE + 20);
|
||||
if (unlikely(!p))
|
||||
goto out_err;
|
||||
|
||||
memcpy(id, p, sizeof(*id));
|
||||
p += XDR_QUADLEN(NFS4_DEVICEID4_SIZE);
|
||||
print_deviceid(id);
|
||||
|
@ -468,32 +550,57 @@ filelayout_decode_layout(struct pnfs_layout_hdr *flo,
|
|||
__func__, nfl_util, fl->num_fh, fl->first_stripe_index,
|
||||
fl->pattern_offset);
|
||||
|
||||
if (!fl->num_fh)
|
||||
goto out_err;
|
||||
|
||||
fl->fh_array = kzalloc(fl->num_fh * sizeof(struct nfs_fh *),
|
||||
GFP_KERNEL);
|
||||
if (!fl->fh_array)
|
||||
return -ENOMEM;
|
||||
goto out_err;
|
||||
|
||||
for (i = 0; i < fl->num_fh; i++) {
|
||||
/* Do we want to use a mempool here? */
|
||||
fl->fh_array[i] = kmalloc(sizeof(struct nfs_fh), GFP_KERNEL);
|
||||
if (!fl->fh_array[i]) {
|
||||
filelayout_free_fh_array(fl);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (!fl->fh_array[i])
|
||||
goto out_err_free;
|
||||
|
||||
p = xdr_inline_decode(&stream, 4);
|
||||
if (unlikely(!p))
|
||||
goto out_err_free;
|
||||
fl->fh_array[i]->size = be32_to_cpup(p++);
|
||||
if (sizeof(struct nfs_fh) < fl->fh_array[i]->size) {
|
||||
printk(KERN_ERR "Too big fh %d received %d\n",
|
||||
i, fl->fh_array[i]->size);
|
||||
filelayout_free_fh_array(fl);
|
||||
return -EIO;
|
||||
goto out_err_free;
|
||||
}
|
||||
|
||||
p = xdr_inline_decode(&stream, fl->fh_array[i]->size);
|
||||
if (unlikely(!p))
|
||||
goto out_err_free;
|
||||
memcpy(fl->fh_array[i]->data, p, fl->fh_array[i]->size);
|
||||
p += XDR_QUADLEN(fl->fh_array[i]->size);
|
||||
dprintk("DEBUG: %s: fh len %d\n", __func__,
|
||||
fl->fh_array[i]->size);
|
||||
}
|
||||
|
||||
__free_page(scratch);
|
||||
return 0;
|
||||
|
||||
out_err_free:
|
||||
filelayout_free_fh_array(fl);
|
||||
out_err:
|
||||
__free_page(scratch);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static void
|
||||
filelayout_free_lseg(struct pnfs_layout_segment *lseg)
|
||||
{
|
||||
struct nfs4_filelayout_segment *fl = FILELAYOUT_LSEG(lseg);
|
||||
|
||||
dprintk("--> %s\n", __func__);
|
||||
nfs4_fl_put_deviceid(fl->dsaddr);
|
||||
kfree(fl->commit_buckets);
|
||||
_filelayout_free_lseg(fl);
|
||||
}
|
||||
|
||||
static struct pnfs_layout_segment *
|
||||
|
@ -514,19 +621,30 @@ filelayout_alloc_lseg(struct pnfs_layout_hdr *layoutid,
|
|||
_filelayout_free_lseg(fl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* This assumes there is only one IOMODE_RW lseg. What
|
||||
* we really want to do is have a layout_hdr level
|
||||
* dictionary of <multipath_list4, fh> keys, each
|
||||
* associated with a struct list_head, populated by calls
|
||||
* to filelayout_write_pagelist().
|
||||
* */
|
||||
if ((!fl->commit_through_mds) && (lgr->range.iomode == IOMODE_RW)) {
|
||||
int i;
|
||||
int size = (fl->stripe_type == STRIPE_SPARSE) ?
|
||||
fl->dsaddr->ds_num : fl->dsaddr->stripe_count;
|
||||
|
||||
fl->commit_buckets = kcalloc(size, sizeof(struct list_head), GFP_KERNEL);
|
||||
if (!fl->commit_buckets) {
|
||||
filelayout_free_lseg(&fl->generic_hdr);
|
||||
return NULL;
|
||||
}
|
||||
fl->number_of_buckets = size;
|
||||
for (i = 0; i < size; i++)
|
||||
INIT_LIST_HEAD(&fl->commit_buckets[i]);
|
||||
}
|
||||
return &fl->generic_hdr;
|
||||
}
|
||||
|
||||
static void
|
||||
filelayout_free_lseg(struct pnfs_layout_segment *lseg)
|
||||
{
|
||||
struct nfs4_filelayout_segment *fl = FILELAYOUT_LSEG(lseg);
|
||||
|
||||
dprintk("--> %s\n", __func__);
|
||||
nfs4_fl_put_deviceid(fl->dsaddr);
|
||||
_filelayout_free_lseg(fl);
|
||||
}
|
||||
|
||||
/*
|
||||
* filelayout_pg_test(). Called by nfs_can_coalesce_requests()
|
||||
*
|
||||
|
@ -552,6 +670,191 @@ filelayout_pg_test(struct nfs_pageio_descriptor *pgio, struct nfs_page *prev,
|
|||
return (p_stripe == r_stripe);
|
||||
}
|
||||
|
||||
static bool filelayout_mark_pnfs_commit(struct pnfs_layout_segment *lseg)
|
||||
{
|
||||
return !FILELAYOUT_LSEG(lseg)->commit_through_mds;
|
||||
}
|
||||
|
||||
static u32 select_bucket_index(struct nfs4_filelayout_segment *fl, u32 j)
|
||||
{
|
||||
if (fl->stripe_type == STRIPE_SPARSE)
|
||||
return nfs4_fl_calc_ds_index(&fl->generic_hdr, j);
|
||||
else
|
||||
return j;
|
||||
}
|
||||
|
||||
struct list_head *filelayout_choose_commit_list(struct nfs_page *req)
|
||||
{
|
||||
struct pnfs_layout_segment *lseg = req->wb_commit_lseg;
|
||||
struct nfs4_filelayout_segment *fl = FILELAYOUT_LSEG(lseg);
|
||||
u32 i, j;
|
||||
struct list_head *list;
|
||||
|
||||
/* Note that we are calling nfs4_fl_calc_j_index on each page
|
||||
* that ends up being committed to a data server. An attractive
|
||||
* alternative is to add a field to nfs_write_data and nfs_page
|
||||
* to store the value calculated in filelayout_write_pagelist
|
||||
* and just use that here.
|
||||
*/
|
||||
j = nfs4_fl_calc_j_index(lseg,
|
||||
(loff_t)req->wb_index << PAGE_CACHE_SHIFT);
|
||||
i = select_bucket_index(fl, j);
|
||||
list = &fl->commit_buckets[i];
|
||||
if (list_empty(list)) {
|
||||
/* Non-empty buckets hold a reference on the lseg */
|
||||
get_lseg(lseg);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
static u32 calc_ds_index_from_commit(struct pnfs_layout_segment *lseg, u32 i)
|
||||
{
|
||||
struct nfs4_filelayout_segment *flseg = FILELAYOUT_LSEG(lseg);
|
||||
|
||||
if (flseg->stripe_type == STRIPE_SPARSE)
|
||||
return i;
|
||||
else
|
||||
return nfs4_fl_calc_ds_index(lseg, i);
|
||||
}
|
||||
|
||||
static struct nfs_fh *
|
||||
select_ds_fh_from_commit(struct pnfs_layout_segment *lseg, u32 i)
|
||||
{
|
||||
struct nfs4_filelayout_segment *flseg = FILELAYOUT_LSEG(lseg);
|
||||
|
||||
if (flseg->stripe_type == STRIPE_SPARSE) {
|
||||
if (flseg->num_fh == 1)
|
||||
i = 0;
|
||||
else if (flseg->num_fh == 0)
|
||||
/* Use the MDS OPEN fh set in nfs_read_rpcsetup */
|
||||
return NULL;
|
||||
}
|
||||
return flseg->fh_array[i];
|
||||
}
|
||||
|
||||
static int filelayout_initiate_commit(struct nfs_write_data *data, int how)
|
||||
{
|
||||
struct pnfs_layout_segment *lseg = data->lseg;
|
||||
struct nfs4_pnfs_ds *ds;
|
||||
u32 idx;
|
||||
struct nfs_fh *fh;
|
||||
|
||||
idx = calc_ds_index_from_commit(lseg, data->ds_commit_index);
|
||||
ds = nfs4_fl_prepare_ds(lseg, idx);
|
||||
if (!ds) {
|
||||
printk(KERN_ERR "%s: prepare_ds failed, use MDS\n", __func__);
|
||||
set_bit(lo_fail_bit(IOMODE_RW), &lseg->pls_layout->plh_flags);
|
||||
set_bit(lo_fail_bit(IOMODE_READ), &lseg->pls_layout->plh_flags);
|
||||
prepare_to_resend_writes(data);
|
||||
data->mds_ops->rpc_release(data);
|
||||
return -EAGAIN;
|
||||
}
|
||||
dprintk("%s ino %lu, how %d\n", __func__, data->inode->i_ino, how);
|
||||
data->write_done_cb = filelayout_commit_done_cb;
|
||||
data->ds_clp = ds->ds_clp;
|
||||
fh = select_ds_fh_from_commit(lseg, data->ds_commit_index);
|
||||
if (fh)
|
||||
data->args.fh = fh;
|
||||
return nfs_initiate_commit(data, ds->ds_clp->cl_rpcclient,
|
||||
&filelayout_commit_call_ops, how);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is only useful while we are using whole file layouts.
|
||||
*/
|
||||
static struct pnfs_layout_segment *find_only_write_lseg(struct inode *inode)
|
||||
{
|
||||
struct pnfs_layout_segment *lseg, *rv = NULL;
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
list_for_each_entry(lseg, &NFS_I(inode)->layout->plh_segs, pls_list)
|
||||
if (lseg->pls_range.iomode == IOMODE_RW)
|
||||
rv = get_lseg(lseg);
|
||||
spin_unlock(&inode->i_lock);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int alloc_ds_commits(struct inode *inode, struct list_head *list)
|
||||
{
|
||||
struct pnfs_layout_segment *lseg;
|
||||
struct nfs4_filelayout_segment *fl;
|
||||
struct nfs_write_data *data;
|
||||
int i, j;
|
||||
|
||||
/* Won't need this when non-whole file layout segments are supported
|
||||
* instead we will use a pnfs_layout_hdr structure */
|
||||
lseg = find_only_write_lseg(inode);
|
||||
if (!lseg)
|
||||
return 0;
|
||||
fl = FILELAYOUT_LSEG(lseg);
|
||||
for (i = 0; i < fl->number_of_buckets; i++) {
|
||||
if (list_empty(&fl->commit_buckets[i]))
|
||||
continue;
|
||||
data = nfs_commitdata_alloc();
|
||||
if (!data)
|
||||
goto out_bad;
|
||||
data->ds_commit_index = i;
|
||||
data->lseg = lseg;
|
||||
list_add(&data->pages, list);
|
||||
}
|
||||
put_lseg(lseg);
|
||||
return 0;
|
||||
|
||||
out_bad:
|
||||
for (j = i; j < fl->number_of_buckets; j++) {
|
||||
if (list_empty(&fl->commit_buckets[i]))
|
||||
continue;
|
||||
nfs_retry_commit(&fl->commit_buckets[i], lseg);
|
||||
put_lseg(lseg); /* associated with emptying bucket */
|
||||
}
|
||||
put_lseg(lseg);
|
||||
/* Caller will clean up entries put on list */
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* This follows nfs_commit_list pretty closely */
|
||||
static int
|
||||
filelayout_commit_pagelist(struct inode *inode, struct list_head *mds_pages,
|
||||
int how)
|
||||
{
|
||||
struct nfs_write_data *data, *tmp;
|
||||
LIST_HEAD(list);
|
||||
|
||||
if (!list_empty(mds_pages)) {
|
||||
data = nfs_commitdata_alloc();
|
||||
if (!data)
|
||||
goto out_bad;
|
||||
data->lseg = NULL;
|
||||
list_add(&data->pages, &list);
|
||||
}
|
||||
|
||||
if (alloc_ds_commits(inode, &list))
|
||||
goto out_bad;
|
||||
|
||||
list_for_each_entry_safe(data, tmp, &list, pages) {
|
||||
list_del_init(&data->pages);
|
||||
atomic_inc(&NFS_I(inode)->commits_outstanding);
|
||||
if (!data->lseg) {
|
||||
nfs_init_commit(data, mds_pages, NULL);
|
||||
nfs_initiate_commit(data, NFS_CLIENT(inode),
|
||||
data->mds_ops, how);
|
||||
} else {
|
||||
nfs_init_commit(data, &FILELAYOUT_LSEG(data->lseg)->commit_buckets[data->ds_commit_index], data->lseg);
|
||||
filelayout_initiate_commit(data, how);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
out_bad:
|
||||
list_for_each_entry_safe(data, tmp, &list, pages) {
|
||||
nfs_retry_commit(&data->pages, data->lseg);
|
||||
list_del_init(&data->pages);
|
||||
nfs_commit_free(data);
|
||||
}
|
||||
nfs_retry_commit(mds_pages, NULL);
|
||||
nfs_commit_clear_lock(NFS_I(inode));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static struct pnfs_layoutdriver_type filelayout_type = {
|
||||
.id = LAYOUT_NFSV4_1_FILES,
|
||||
.name = "LAYOUT_NFSV4_1_FILES",
|
||||
|
@ -559,6 +862,9 @@ static struct pnfs_layoutdriver_type filelayout_type = {
|
|||
.alloc_lseg = filelayout_alloc_lseg,
|
||||
.free_lseg = filelayout_free_lseg,
|
||||
.pg_test = filelayout_pg_test,
|
||||
.mark_pnfs_commit = filelayout_mark_pnfs_commit,
|
||||
.choose_commit_list = filelayout_choose_commit_list,
|
||||
.commit_pagelist = filelayout_commit_pagelist,
|
||||
.read_pagelist = filelayout_read_pagelist,
|
||||
.write_pagelist = filelayout_write_pagelist,
|
||||
};
|
||||
|
|
|
@ -79,6 +79,8 @@ struct nfs4_filelayout_segment {
|
|||
struct nfs4_file_layout_dsaddr *dsaddr; /* Point to GETDEVINFO data */
|
||||
unsigned int num_fh;
|
||||
struct nfs_fh **fh_array;
|
||||
struct list_head *commit_buckets; /* Sort commits to ds */
|
||||
int number_of_buckets;
|
||||
};
|
||||
|
||||
static inline struct nfs4_filelayout_segment *
|
||||
|
|
|
@ -261,7 +261,7 @@ out:
|
|||
* Currently only support ipv4, and one multi-path address.
|
||||
*/
|
||||
static struct nfs4_pnfs_ds *
|
||||
decode_and_add_ds(__be32 **pp, struct inode *inode)
|
||||
decode_and_add_ds(struct xdr_stream *streamp, struct inode *inode)
|
||||
{
|
||||
struct nfs4_pnfs_ds *ds = NULL;
|
||||
char *buf;
|
||||
|
@ -269,25 +269,34 @@ decode_and_add_ds(__be32 **pp, struct inode *inode)
|
|||
u32 ip_addr, port;
|
||||
int nlen, rlen, i;
|
||||
int tmp[2];
|
||||
__be32 *r_netid, *r_addr, *p = *pp;
|
||||
__be32 *p;
|
||||
|
||||
/* r_netid */
|
||||
p = xdr_inline_decode(streamp, 4);
|
||||
if (unlikely(!p))
|
||||
goto out_err;
|
||||
nlen = be32_to_cpup(p++);
|
||||
r_netid = p;
|
||||
p += XDR_QUADLEN(nlen);
|
||||
|
||||
/* r_addr */
|
||||
rlen = be32_to_cpup(p++);
|
||||
r_addr = p;
|
||||
p += XDR_QUADLEN(rlen);
|
||||
*pp = p;
|
||||
p = xdr_inline_decode(streamp, nlen);
|
||||
if (unlikely(!p))
|
||||
goto out_err;
|
||||
|
||||
/* Check that netid is "tcp" */
|
||||
if (nlen != 3 || memcmp((char *)r_netid, "tcp", 3)) {
|
||||
if (nlen != 3 || memcmp((char *)p, "tcp", 3)) {
|
||||
dprintk("%s: ERROR: non ipv4 TCP r_netid\n", __func__);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
/* r_addr */
|
||||
p = xdr_inline_decode(streamp, 4);
|
||||
if (unlikely(!p))
|
||||
goto out_err;
|
||||
rlen = be32_to_cpup(p);
|
||||
|
||||
p = xdr_inline_decode(streamp, rlen);
|
||||
if (unlikely(!p))
|
||||
goto out_err;
|
||||
|
||||
/* ipv6 length plus port is legal */
|
||||
if (rlen > INET6_ADDRSTRLEN + 8) {
|
||||
dprintk("%s: Invalid address, length %d\n", __func__,
|
||||
|
@ -300,7 +309,7 @@ decode_and_add_ds(__be32 **pp, struct inode *inode)
|
|||
goto out_err;
|
||||
}
|
||||
buf[rlen] = '\0';
|
||||
memcpy(buf, r_addr, rlen);
|
||||
memcpy(buf, p, rlen);
|
||||
|
||||
/* replace the port dots with dashes for the in4_pton() delimiter*/
|
||||
for (i = 0; i < 2; i++) {
|
||||
|
@ -336,90 +345,154 @@ out_err:
|
|||
static struct nfs4_file_layout_dsaddr*
|
||||
decode_device(struct inode *ino, struct pnfs_device *pdev)
|
||||
{
|
||||
int i, dummy;
|
||||
int i;
|
||||
u32 cnt, num;
|
||||
u8 *indexp;
|
||||
__be32 *p = (__be32 *)pdev->area, *indicesp;
|
||||
struct nfs4_file_layout_dsaddr *dsaddr;
|
||||
__be32 *p;
|
||||
u8 *stripe_indices;
|
||||
u8 max_stripe_index;
|
||||
struct nfs4_file_layout_dsaddr *dsaddr = NULL;
|
||||
struct xdr_stream stream;
|
||||
struct xdr_buf buf = {
|
||||
.pages = pdev->pages,
|
||||
.page_len = pdev->pglen,
|
||||
.buflen = pdev->pglen,
|
||||
.len = pdev->pglen,
|
||||
};
|
||||
struct page *scratch;
|
||||
|
||||
/* set up xdr stream */
|
||||
scratch = alloc_page(GFP_KERNEL);
|
||||
if (!scratch)
|
||||
goto out_err;
|
||||
|
||||
xdr_init_decode(&stream, &buf, NULL);
|
||||
xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE);
|
||||
|
||||
/* Get the stripe count (number of stripe index) */
|
||||
cnt = be32_to_cpup(p++);
|
||||
p = xdr_inline_decode(&stream, 4);
|
||||
if (unlikely(!p))
|
||||
goto out_err_free_scratch;
|
||||
|
||||
cnt = be32_to_cpup(p);
|
||||
dprintk("%s stripe count %d\n", __func__, cnt);
|
||||
if (cnt > NFS4_PNFS_MAX_STRIPE_CNT) {
|
||||
printk(KERN_WARNING "%s: stripe count %d greater than "
|
||||
"supported maximum %d\n", __func__,
|
||||
cnt, NFS4_PNFS_MAX_STRIPE_CNT);
|
||||
goto out_err;
|
||||
goto out_err_free_scratch;
|
||||
}
|
||||
|
||||
/* read stripe indices */
|
||||
stripe_indices = kcalloc(cnt, sizeof(u8), GFP_KERNEL);
|
||||
if (!stripe_indices)
|
||||
goto out_err_free_scratch;
|
||||
|
||||
p = xdr_inline_decode(&stream, cnt << 2);
|
||||
if (unlikely(!p))
|
||||
goto out_err_free_stripe_indices;
|
||||
|
||||
indexp = &stripe_indices[0];
|
||||
max_stripe_index = 0;
|
||||
for (i = 0; i < cnt; i++) {
|
||||
*indexp = be32_to_cpup(p++);
|
||||
max_stripe_index = max(max_stripe_index, *indexp);
|
||||
indexp++;
|
||||
}
|
||||
|
||||
/* Check the multipath list count */
|
||||
indicesp = p;
|
||||
p += XDR_QUADLEN(cnt << 2);
|
||||
num = be32_to_cpup(p++);
|
||||
p = xdr_inline_decode(&stream, 4);
|
||||
if (unlikely(!p))
|
||||
goto out_err_free_stripe_indices;
|
||||
|
||||
num = be32_to_cpup(p);
|
||||
dprintk("%s ds_num %u\n", __func__, num);
|
||||
if (num > NFS4_PNFS_MAX_MULTI_CNT) {
|
||||
printk(KERN_WARNING "%s: multipath count %d greater than "
|
||||
"supported maximum %d\n", __func__,
|
||||
num, NFS4_PNFS_MAX_MULTI_CNT);
|
||||
goto out_err;
|
||||
goto out_err_free_stripe_indices;
|
||||
}
|
||||
|
||||
/* validate stripe indices are all < num */
|
||||
if (max_stripe_index >= num) {
|
||||
printk(KERN_WARNING "%s: stripe index %u >= num ds %u\n",
|
||||
__func__, max_stripe_index, num);
|
||||
goto out_err_free_stripe_indices;
|
||||
}
|
||||
|
||||
dsaddr = kzalloc(sizeof(*dsaddr) +
|
||||
(sizeof(struct nfs4_pnfs_ds *) * (num - 1)),
|
||||
GFP_KERNEL);
|
||||
if (!dsaddr)
|
||||
goto out_err;
|
||||
|
||||
dsaddr->stripe_indices = kzalloc(sizeof(u8) * cnt, GFP_KERNEL);
|
||||
if (!dsaddr->stripe_indices)
|
||||
goto out_err_free;
|
||||
goto out_err_free_stripe_indices;
|
||||
|
||||
dsaddr->stripe_count = cnt;
|
||||
dsaddr->stripe_indices = stripe_indices;
|
||||
stripe_indices = NULL;
|
||||
dsaddr->ds_num = num;
|
||||
|
||||
memcpy(&dsaddr->deviceid, &pdev->dev_id, sizeof(pdev->dev_id));
|
||||
|
||||
/* Go back an read stripe indices */
|
||||
p = indicesp;
|
||||
indexp = &dsaddr->stripe_indices[0];
|
||||
for (i = 0; i < dsaddr->stripe_count; i++) {
|
||||
*indexp = be32_to_cpup(p++);
|
||||
if (*indexp >= num)
|
||||
goto out_err_free;
|
||||
indexp++;
|
||||
}
|
||||
/* Skip already read multipath list count */
|
||||
p++;
|
||||
|
||||
for (i = 0; i < dsaddr->ds_num; i++) {
|
||||
int j;
|
||||
u32 mp_count;
|
||||
|
||||
dummy = be32_to_cpup(p++); /* multipath count */
|
||||
if (dummy > 1) {
|
||||
p = xdr_inline_decode(&stream, 4);
|
||||
if (unlikely(!p))
|
||||
goto out_err_free_deviceid;
|
||||
|
||||
mp_count = be32_to_cpup(p); /* multipath count */
|
||||
if (mp_count > 1) {
|
||||
printk(KERN_WARNING
|
||||
"%s: Multipath count %d not supported, "
|
||||
"skipping all greater than 1\n", __func__,
|
||||
dummy);
|
||||
mp_count);
|
||||
}
|
||||
for (j = 0; j < dummy; j++) {
|
||||
for (j = 0; j < mp_count; j++) {
|
||||
if (j == 0) {
|
||||
dsaddr->ds_list[i] = decode_and_add_ds(&p, ino);
|
||||
dsaddr->ds_list[i] = decode_and_add_ds(&stream,
|
||||
ino);
|
||||
if (dsaddr->ds_list[i] == NULL)
|
||||
goto out_err_free;
|
||||
goto out_err_free_deviceid;
|
||||
} else {
|
||||
u32 len;
|
||||
/* skip extra multipath */
|
||||
len = be32_to_cpup(p++);
|
||||
p += XDR_QUADLEN(len);
|
||||
len = be32_to_cpup(p++);
|
||||
p += XDR_QUADLEN(len);
|
||||
continue;
|
||||
|
||||
/* read len, skip */
|
||||
p = xdr_inline_decode(&stream, 4);
|
||||
if (unlikely(!p))
|
||||
goto out_err_free_deviceid;
|
||||
len = be32_to_cpup(p);
|
||||
|
||||
p = xdr_inline_decode(&stream, len);
|
||||
if (unlikely(!p))
|
||||
goto out_err_free_deviceid;
|
||||
|
||||
/* read len, skip */
|
||||
p = xdr_inline_decode(&stream, 4);
|
||||
if (unlikely(!p))
|
||||
goto out_err_free_deviceid;
|
||||
len = be32_to_cpup(p);
|
||||
|
||||
p = xdr_inline_decode(&stream, len);
|
||||
if (unlikely(!p))
|
||||
goto out_err_free_deviceid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__free_page(scratch);
|
||||
return dsaddr;
|
||||
|
||||
out_err_free:
|
||||
out_err_free_deviceid:
|
||||
nfs4_fl_free_deviceid(dsaddr);
|
||||
/* stripe_indicies was part of dsaddr */
|
||||
goto out_err_free_scratch;
|
||||
out_err_free_stripe_indices:
|
||||
kfree(stripe_indices);
|
||||
out_err_free_scratch:
|
||||
__free_page(scratch);
|
||||
out_err:
|
||||
dprintk("%s ERROR: returning NULL\n", __func__);
|
||||
return NULL;
|
||||
|
@ -498,11 +571,6 @@ get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id)
|
|||
goto out_free;
|
||||
}
|
||||
|
||||
/* set pdev->area */
|
||||
pdev->area = vmap(pages, max_pages, VM_MAP, PAGE_KERNEL);
|
||||
if (!pdev->area)
|
||||
goto out_free;
|
||||
|
||||
memcpy(&pdev->dev_id, dev_id, sizeof(*dev_id));
|
||||
pdev->layout_type = LAYOUT_NFSV4_1_FILES;
|
||||
pdev->pages = pages;
|
||||
|
@ -521,8 +589,6 @@ get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id)
|
|||
*/
|
||||
dsaddr = decode_and_add_device(inode, pdev);
|
||||
out_free:
|
||||
if (pdev->area != NULL)
|
||||
vunmap(pdev->area);
|
||||
for (i = 0; i < max_pages; i++)
|
||||
__free_page(pages[i]);
|
||||
kfree(pages);
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/sunrpc/gss_api.h>
|
||||
#include <linux/nfs.h>
|
||||
#include <linux/nfs4.h>
|
||||
#include <linux/nfs_fs.h>
|
||||
|
@ -71,7 +72,9 @@ static int _nfs4_proc_open(struct nfs4_opendata *data);
|
|||
static int _nfs4_recover_proc_open(struct nfs4_opendata *data);
|
||||
static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
|
||||
static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *, struct nfs4_state *);
|
||||
static int _nfs4_proc_lookup(struct inode *dir, const struct qstr *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
|
||||
static int _nfs4_proc_lookup(struct rpc_clnt *client, struct inode *dir,
|
||||
const struct qstr *name, struct nfs_fh *fhandle,
|
||||
struct nfs_fattr *fattr);
|
||||
static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
|
||||
static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
|
||||
struct nfs_fattr *fattr, struct iattr *sattr,
|
||||
|
@ -85,6 +88,8 @@ static int nfs4_map_errors(int err)
|
|||
switch (err) {
|
||||
case -NFS4ERR_RESOURCE:
|
||||
return -EREMOTEIO;
|
||||
case -NFS4ERR_WRONGSEC:
|
||||
return -EPERM;
|
||||
case -NFS4ERR_BADOWNER:
|
||||
case -NFS4ERR_BADNAME:
|
||||
return -EINVAL;
|
||||
|
@ -657,7 +662,8 @@ struct rpc_call_ops nfs41_call_priv_sync_ops = {
|
|||
.rpc_call_done = nfs41_call_sync_done,
|
||||
};
|
||||
|
||||
static int nfs4_call_sync_sequence(struct nfs_server *server,
|
||||
static int nfs4_call_sync_sequence(struct rpc_clnt *clnt,
|
||||
struct nfs_server *server,
|
||||
struct rpc_message *msg,
|
||||
struct nfs4_sequence_args *args,
|
||||
struct nfs4_sequence_res *res,
|
||||
|
@ -673,7 +679,7 @@ static int nfs4_call_sync_sequence(struct nfs_server *server,
|
|||
.cache_reply = cache_reply,
|
||||
};
|
||||
struct rpc_task_setup task_setup = {
|
||||
.rpc_client = server->client,
|
||||
.rpc_client = clnt,
|
||||
.rpc_message = msg,
|
||||
.callback_ops = &nfs41_call_sync_ops,
|
||||
.callback_data = &data
|
||||
|
@ -692,13 +698,14 @@ static int nfs4_call_sync_sequence(struct nfs_server *server,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int _nfs4_call_sync_session(struct nfs_server *server,
|
||||
int _nfs4_call_sync_session(struct rpc_clnt *clnt,
|
||||
struct nfs_server *server,
|
||||
struct rpc_message *msg,
|
||||
struct nfs4_sequence_args *args,
|
||||
struct nfs4_sequence_res *res,
|
||||
int cache_reply)
|
||||
{
|
||||
return nfs4_call_sync_sequence(server, msg, args, res, cache_reply, 0);
|
||||
return nfs4_call_sync_sequence(clnt, server, msg, args, res, cache_reply, 0);
|
||||
}
|
||||
|
||||
#else
|
||||
|
@ -709,19 +716,28 @@ static int nfs4_sequence_done(struct rpc_task *task,
|
|||
}
|
||||
#endif /* CONFIG_NFS_V4_1 */
|
||||
|
||||
int _nfs4_call_sync(struct nfs_server *server,
|
||||
int _nfs4_call_sync(struct rpc_clnt *clnt,
|
||||
struct nfs_server *server,
|
||||
struct rpc_message *msg,
|
||||
struct nfs4_sequence_args *args,
|
||||
struct nfs4_sequence_res *res,
|
||||
int cache_reply)
|
||||
{
|
||||
args->sa_session = res->sr_session = NULL;
|
||||
return rpc_call_sync(server->client, msg, 0);
|
||||
return rpc_call_sync(clnt, msg, 0);
|
||||
}
|
||||
|
||||
#define nfs4_call_sync(server, msg, args, res, cache_reply) \
|
||||
(server)->nfs_client->cl_mvops->call_sync((server), (msg), &(args)->seq_args, \
|
||||
&(res)->seq_res, (cache_reply))
|
||||
static inline
|
||||
int nfs4_call_sync(struct rpc_clnt *clnt,
|
||||
struct nfs_server *server,
|
||||
struct rpc_message *msg,
|
||||
struct nfs4_sequence_args *args,
|
||||
struct nfs4_sequence_res *res,
|
||||
int cache_reply)
|
||||
{
|
||||
return server->nfs_client->cl_mvops->call_sync(clnt, server, msg,
|
||||
args, res, cache_reply);
|
||||
}
|
||||
|
||||
static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo)
|
||||
{
|
||||
|
@ -1831,7 +1847,7 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
|
|||
} else
|
||||
memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid));
|
||||
|
||||
status = nfs4_call_sync(server, &msg, &arg, &res, 1);
|
||||
status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
|
||||
if (status == 0 && state != NULL)
|
||||
renew_lease(server, timestamp);
|
||||
return status;
|
||||
|
@ -2090,7 +2106,7 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
|
|||
};
|
||||
int status;
|
||||
|
||||
status = nfs4_call_sync(server, &msg, &args, &res, 0);
|
||||
status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
|
||||
if (status == 0) {
|
||||
memcpy(server->attr_bitmask, res.attr_bitmask, sizeof(server->attr_bitmask));
|
||||
server->caps &= ~(NFS_CAP_ACLS|NFS_CAP_HARDLINKS|
|
||||
|
@ -2160,7 +2176,7 @@ static int _nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
|
|||
};
|
||||
|
||||
nfs_fattr_init(info->fattr);
|
||||
return nfs4_call_sync(server, &msg, &args, &res, 0);
|
||||
return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
|
||||
}
|
||||
|
||||
static int nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
|
||||
|
@ -2176,15 +2192,43 @@ static int nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
|
|||
return err;
|
||||
}
|
||||
|
||||
static int nfs4_lookup_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
|
||||
struct nfs_fsinfo *info, rpc_authflavor_t flavor)
|
||||
{
|
||||
struct rpc_auth *auth;
|
||||
int ret;
|
||||
|
||||
auth = rpcauth_create(flavor, server->client);
|
||||
if (!auth) {
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
ret = nfs4_lookup_root(server, fhandle, info);
|
||||
if (ret < 0)
|
||||
ret = -EAGAIN;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* get the file handle for the "/" directory on the server
|
||||
*/
|
||||
static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
|
||||
struct nfs_fsinfo *info)
|
||||
{
|
||||
int status;
|
||||
int i, len, status = 0;
|
||||
rpc_authflavor_t flav_array[NFS_MAX_SECFLAVORS + 2];
|
||||
|
||||
status = nfs4_lookup_root(server, fhandle, info);
|
||||
flav_array[0] = RPC_AUTH_UNIX;
|
||||
len = gss_mech_list_pseudoflavors(&flav_array[1]);
|
||||
flav_array[1+len] = RPC_AUTH_NULL;
|
||||
len += 2;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
status = nfs4_lookup_root_sec(server, fhandle, info, flav_array[i]);
|
||||
if (status == 0)
|
||||
break;
|
||||
}
|
||||
if (status == 0)
|
||||
status = nfs4_server_capabilities(server, fhandle);
|
||||
if (status == 0)
|
||||
|
@ -2249,7 +2293,7 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
|
|||
};
|
||||
|
||||
nfs_fattr_init(fattr);
|
||||
return nfs4_call_sync(server, &msg, &args, &res, 0);
|
||||
return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
|
||||
}
|
||||
|
||||
static int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
||||
|
@ -2309,9 +2353,9 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
|
|||
return status;
|
||||
}
|
||||
|
||||
static int _nfs4_proc_lookupfh(struct nfs_server *server, const struct nfs_fh *dirfh,
|
||||
const struct qstr *name, struct nfs_fh *fhandle,
|
||||
struct nfs_fattr *fattr)
|
||||
static int _nfs4_proc_lookupfh(struct rpc_clnt *clnt, struct nfs_server *server,
|
||||
const struct nfs_fh *dirfh, const struct qstr *name,
|
||||
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
||||
{
|
||||
int status;
|
||||
struct nfs4_lookup_arg args = {
|
||||
|
@ -2333,7 +2377,7 @@ static int _nfs4_proc_lookupfh(struct nfs_server *server, const struct nfs_fh *d
|
|||
nfs_fattr_init(fattr);
|
||||
|
||||
dprintk("NFS call lookupfh %s\n", name->name);
|
||||
status = nfs4_call_sync(server, &msg, &args, &res, 0);
|
||||
status = nfs4_call_sync(clnt, server, &msg, &args.seq_args, &res.seq_res, 0);
|
||||
dprintk("NFS reply lookupfh: %d\n", status);
|
||||
return status;
|
||||
}
|
||||
|
@ -2345,7 +2389,7 @@ static int nfs4_proc_lookupfh(struct nfs_server *server, struct nfs_fh *dirfh,
|
|||
struct nfs4_exception exception = { };
|
||||
int err;
|
||||
do {
|
||||
err = _nfs4_proc_lookupfh(server, dirfh, name, fhandle, fattr);
|
||||
err = _nfs4_proc_lookupfh(server->client, server, dirfh, name, fhandle, fattr);
|
||||
/* FIXME: !!!! */
|
||||
if (err == -NFS4ERR_MOVED) {
|
||||
err = -EREMOTE;
|
||||
|
@ -2356,27 +2400,41 @@ static int nfs4_proc_lookupfh(struct nfs_server *server, struct nfs_fh *dirfh,
|
|||
return err;
|
||||
}
|
||||
|
||||
static int _nfs4_proc_lookup(struct inode *dir, const struct qstr *name,
|
||||
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
||||
static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
|
||||
const struct qstr *name, struct nfs_fh *fhandle,
|
||||
struct nfs_fattr *fattr)
|
||||
{
|
||||
int status;
|
||||
|
||||
dprintk("NFS call lookup %s\n", name->name);
|
||||
status = _nfs4_proc_lookupfh(NFS_SERVER(dir), NFS_FH(dir), name, fhandle, fattr);
|
||||
status = _nfs4_proc_lookupfh(clnt, NFS_SERVER(dir), NFS_FH(dir), name, fhandle, fattr);
|
||||
if (status == -NFS4ERR_MOVED)
|
||||
status = nfs4_get_referral(dir, name, fattr, fhandle);
|
||||
dprintk("NFS reply lookup: %d\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int nfs4_proc_lookup(struct inode *dir, struct qstr *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
||||
void nfs_fixup_secinfo_attributes(struct nfs_fattr *fattr, struct nfs_fh *fh)
|
||||
{
|
||||
memset(fh, 0, sizeof(struct nfs_fh));
|
||||
fattr->fsid.major = 1;
|
||||
fattr->valid |= NFS_ATTR_FATTR_TYPE | NFS_ATTR_FATTR_MODE |
|
||||
NFS_ATTR_FATTR_NLINK | NFS_ATTR_FATTR_FSID | NFS_ATTR_FATTR_MOUNTPOINT;
|
||||
fattr->mode = S_IFDIR | S_IRUGO | S_IXUGO;
|
||||
fattr->nlink = 2;
|
||||
}
|
||||
|
||||
static int nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qstr *name,
|
||||
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
||||
{
|
||||
struct nfs4_exception exception = { };
|
||||
int err;
|
||||
do {
|
||||
err = nfs4_handle_exception(NFS_SERVER(dir),
|
||||
_nfs4_proc_lookup(dir, name, fhandle, fattr),
|
||||
_nfs4_proc_lookup(clnt, dir, name, fhandle, fattr),
|
||||
&exception);
|
||||
if (err == -EPERM)
|
||||
nfs_fixup_secinfo_attributes(fattr, fhandle);
|
||||
} while (exception.retry);
|
||||
return err;
|
||||
}
|
||||
|
@ -2421,7 +2479,7 @@ static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry
|
|||
if (res.fattr == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
status = nfs4_call_sync(server, &msg, &args, &res, 0);
|
||||
status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
|
||||
if (!status) {
|
||||
entry->mask = 0;
|
||||
if (res.access & NFS4_ACCESS_READ)
|
||||
|
@ -2488,7 +2546,7 @@ static int _nfs4_proc_readlink(struct inode *inode, struct page *page,
|
|||
.rpc_resp = &res,
|
||||
};
|
||||
|
||||
return nfs4_call_sync(NFS_SERVER(inode), &msg, &args, &res, 0);
|
||||
return nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode), &msg, &args.seq_args, &res.seq_res, 0);
|
||||
}
|
||||
|
||||
static int nfs4_proc_readlink(struct inode *inode, struct page *page,
|
||||
|
@ -2577,7 +2635,7 @@ static int _nfs4_proc_remove(struct inode *dir, struct qstr *name)
|
|||
if (res.dir_attr == NULL)
|
||||
goto out;
|
||||
|
||||
status = nfs4_call_sync(server, &msg, &args, &res, 1);
|
||||
status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 1);
|
||||
if (status == 0) {
|
||||
update_changeattr(dir, &res.cinfo);
|
||||
nfs_post_op_update_inode(dir, res.dir_attr);
|
||||
|
@ -2678,7 +2736,7 @@ static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
|
|||
if (res.old_fattr == NULL || res.new_fattr == NULL)
|
||||
goto out;
|
||||
|
||||
status = nfs4_call_sync(server, &msg, &arg, &res, 1);
|
||||
status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
|
||||
if (!status) {
|
||||
update_changeattr(old_dir, &res.old_cinfo);
|
||||
nfs_post_op_update_inode(old_dir, res.old_fattr);
|
||||
|
@ -2729,7 +2787,7 @@ static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *
|
|||
if (res.fattr == NULL || res.dir_attr == NULL)
|
||||
goto out;
|
||||
|
||||
status = nfs4_call_sync(server, &msg, &arg, &res, 1);
|
||||
status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
|
||||
if (!status) {
|
||||
update_changeattr(dir, &res.cinfo);
|
||||
nfs_post_op_update_inode(dir, res.dir_attr);
|
||||
|
@ -2792,8 +2850,8 @@ static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir,
|
|||
|
||||
static int nfs4_do_create(struct inode *dir, struct dentry *dentry, struct nfs4_createdata *data)
|
||||
{
|
||||
int status = nfs4_call_sync(NFS_SERVER(dir), &data->msg,
|
||||
&data->arg, &data->res, 1);
|
||||
int status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &data->msg,
|
||||
&data->arg.seq_args, &data->res.seq_res, 1);
|
||||
if (status == 0) {
|
||||
update_changeattr(dir, &data->res.dir_cinfo);
|
||||
nfs_post_op_update_inode(dir, data->res.dir_fattr);
|
||||
|
@ -2905,7 +2963,7 @@ static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
|
|||
(unsigned long long)cookie);
|
||||
nfs4_setup_readdir(cookie, NFS_COOKIEVERF(dir), dentry, &args);
|
||||
res.pgbase = args.pgbase;
|
||||
status = nfs4_call_sync(NFS_SERVER(dir), &msg, &args, &res, 0);
|
||||
status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &msg, &args.seq_args, &res.seq_res, 0);
|
||||
if (status >= 0) {
|
||||
memcpy(NFS_COOKIEVERF(dir), res.verifier.data, NFS4_VERIFIER_SIZE);
|
||||
status += args.pgbase;
|
||||
|
@ -2997,7 +3055,7 @@ static int _nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
|
|||
};
|
||||
|
||||
nfs_fattr_init(fsstat->fattr);
|
||||
return nfs4_call_sync(server, &msg, &args, &res, 0);
|
||||
return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
|
||||
}
|
||||
|
||||
static int nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsstat *fsstat)
|
||||
|
@ -3028,7 +3086,7 @@ static int _nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle,
|
|||
.rpc_resp = &res,
|
||||
};
|
||||
|
||||
return nfs4_call_sync(server, &msg, &args, &res, 0);
|
||||
return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
|
||||
}
|
||||
|
||||
static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo)
|
||||
|
@ -3073,7 +3131,7 @@ static int _nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle
|
|||
}
|
||||
|
||||
nfs_fattr_init(pathconf->fattr);
|
||||
return nfs4_call_sync(server, &msg, &args, &res, 0);
|
||||
return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
|
||||
}
|
||||
|
||||
static int nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
|
||||
|
@ -3195,12 +3253,9 @@ static void nfs4_proc_write_setup(struct nfs_write_data *data, struct rpc_messag
|
|||
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_WRITE];
|
||||
}
|
||||
|
||||
static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data)
|
||||
static int nfs4_commit_done_cb(struct rpc_task *task, struct nfs_write_data *data)
|
||||
{
|
||||
struct inode *inode = data->inode;
|
||||
|
||||
if (!nfs4_sequence_done(task, &data->res.seq_res))
|
||||
return -EAGAIN;
|
||||
|
||||
if (nfs4_async_handle_error(task, NFS_SERVER(inode), NULL) == -EAGAIN) {
|
||||
nfs_restart_rpc(task, NFS_SERVER(inode)->nfs_client);
|
||||
|
@ -3210,11 +3265,24 @@ static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data)
|
||||
{
|
||||
if (!nfs4_sequence_done(task, &data->res.seq_res))
|
||||
return -EAGAIN;
|
||||
return data->write_done_cb(task, data);
|
||||
}
|
||||
|
||||
static void nfs4_proc_commit_setup(struct nfs_write_data *data, struct rpc_message *msg)
|
||||
{
|
||||
struct nfs_server *server = NFS_SERVER(data->inode);
|
||||
|
||||
data->args.bitmask = server->cache_consistency_bitmask;
|
||||
|
||||
if (data->lseg) {
|
||||
data->args.bitmask = NULL;
|
||||
data->res.fattr = NULL;
|
||||
} else
|
||||
data->args.bitmask = server->cache_consistency_bitmask;
|
||||
if (!data->write_done_cb)
|
||||
data->write_done_cb = nfs4_commit_done_cb;
|
||||
data->res.server = server;
|
||||
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMMIT];
|
||||
}
|
||||
|
@ -3452,7 +3520,7 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
|
|||
resp_buf = buf;
|
||||
buf_to_pages(buf, buflen, args.acl_pages, &args.acl_pgbase);
|
||||
}
|
||||
ret = nfs4_call_sync(NFS_SERVER(inode), &msg, &args, &res, 0);
|
||||
ret = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode), &msg, &args.seq_args, &res.seq_res, 0);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
if (res.acl_len > args.acl_len)
|
||||
|
@ -3527,7 +3595,7 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
|
|||
if (i < 0)
|
||||
return i;
|
||||
nfs_inode_return_delegation(inode);
|
||||
ret = nfs4_call_sync(server, &msg, &arg, &res, 1);
|
||||
ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
|
||||
|
||||
/*
|
||||
* Free each page after tx, so the only ref left is
|
||||
|
@ -3890,7 +3958,7 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock
|
|||
lsp = request->fl_u.nfs4_fl.owner;
|
||||
arg.lock_owner.id = lsp->ls_id.id;
|
||||
arg.lock_owner.s_dev = server->s_dev;
|
||||
status = nfs4_call_sync(server, &msg, &arg, &res, 1);
|
||||
status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
|
||||
switch (status) {
|
||||
case 0:
|
||||
request->fl_type = F_UNLCK;
|
||||
|
@ -4618,12 +4686,46 @@ int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
|
|||
nfs_fattr_init(&fs_locations->fattr);
|
||||
fs_locations->server = server;
|
||||
fs_locations->nlocations = 0;
|
||||
status = nfs4_call_sync(server, &msg, &args, &res, 0);
|
||||
status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
|
||||
nfs_fixup_referral_attributes(&fs_locations->fattr);
|
||||
dprintk("%s: returned status = %d\n", __func__, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int _nfs4_proc_secinfo(struct inode *dir, const struct qstr *name, struct nfs4_secinfo_flavors *flavors)
|
||||
{
|
||||
int status;
|
||||
struct nfs4_secinfo_arg args = {
|
||||
.dir_fh = NFS_FH(dir),
|
||||
.name = name,
|
||||
};
|
||||
struct nfs4_secinfo_res res = {
|
||||
.flavors = flavors,
|
||||
};
|
||||
struct rpc_message msg = {
|
||||
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SECINFO],
|
||||
.rpc_argp = &args,
|
||||
.rpc_resp = &res,
|
||||
};
|
||||
|
||||
dprintk("NFS call secinfo %s\n", name->name);
|
||||
status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &msg, &args.seq_args, &res.seq_res, 0);
|
||||
dprintk("NFS reply secinfo: %d\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
int nfs4_proc_secinfo(struct inode *dir, const struct qstr *name, struct nfs4_secinfo_flavors *flavors)
|
||||
{
|
||||
struct nfs4_exception exception = { };
|
||||
int err;
|
||||
do {
|
||||
err = nfs4_handle_exception(NFS_SERVER(dir),
|
||||
_nfs4_proc_secinfo(dir, name, flavors),
|
||||
&exception);
|
||||
} while (exception.retry);
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NFS_V4_1
|
||||
/*
|
||||
* Check the exchange flags returned by the server for invalid flags, having
|
||||
|
@ -5516,8 +5618,6 @@ static void nfs4_layoutget_release(void *calldata)
|
|||
struct nfs4_layoutget *lgp = calldata;
|
||||
|
||||
dprintk("--> %s\n", __func__);
|
||||
if (lgp->res.layout.buf != NULL)
|
||||
free_page((unsigned long) lgp->res.layout.buf);
|
||||
put_nfs_open_context(lgp->args.ctx);
|
||||
kfree(calldata);
|
||||
dprintk("<-- %s\n", __func__);
|
||||
|
@ -5549,12 +5649,7 @@ int nfs4_proc_layoutget(struct nfs4_layoutget *lgp)
|
|||
|
||||
dprintk("--> %s\n", __func__);
|
||||
|
||||
lgp->res.layout.buf = (void *)__get_free_page(GFP_NOFS);
|
||||
if (lgp->res.layout.buf == NULL) {
|
||||
nfs4_layoutget_release(lgp);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
lgp->res.layoutp = &lgp->args.layout;
|
||||
lgp->res.seq_res.sr_slot = NULL;
|
||||
task = rpc_run_task(&task_setup_data);
|
||||
if (IS_ERR(task))
|
||||
|
@ -5586,7 +5681,7 @@ _nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *pdev)
|
|||
int status;
|
||||
|
||||
dprintk("--> %s\n", __func__);
|
||||
status = nfs4_call_sync(server, &msg, &args, &res, 0);
|
||||
status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
|
||||
dprintk("<-- %s status=%d\n", __func__, status);
|
||||
|
||||
return status;
|
||||
|
@ -5606,6 +5701,100 @@ int nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *pdev)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(nfs4_proc_getdeviceinfo);
|
||||
|
||||
static void nfs4_layoutcommit_prepare(struct rpc_task *task, void *calldata)
|
||||
{
|
||||
struct nfs4_layoutcommit_data *data = calldata;
|
||||
struct nfs_server *server = NFS_SERVER(data->args.inode);
|
||||
|
||||
if (nfs4_setup_sequence(server, &data->args.seq_args,
|
||||
&data->res.seq_res, 1, task))
|
||||
return;
|
||||
rpc_call_start(task);
|
||||
}
|
||||
|
||||
static void
|
||||
nfs4_layoutcommit_done(struct rpc_task *task, void *calldata)
|
||||
{
|
||||
struct nfs4_layoutcommit_data *data = calldata;
|
||||
struct nfs_server *server = NFS_SERVER(data->args.inode);
|
||||
|
||||
if (!nfs4_sequence_done(task, &data->res.seq_res))
|
||||
return;
|
||||
|
||||
switch (task->tk_status) { /* Just ignore these failures */
|
||||
case NFS4ERR_DELEG_REVOKED: /* layout was recalled */
|
||||
case NFS4ERR_BADIOMODE: /* no IOMODE_RW layout for range */
|
||||
case NFS4ERR_BADLAYOUT: /* no layout */
|
||||
case NFS4ERR_GRACE: /* loca_recalim always false */
|
||||
task->tk_status = 0;
|
||||
}
|
||||
|
||||
if (nfs4_async_handle_error(task, server, NULL) == -EAGAIN) {
|
||||
nfs_restart_rpc(task, server->nfs_client);
|
||||
return;
|
||||
}
|
||||
|
||||
if (task->tk_status == 0)
|
||||
nfs_post_op_update_inode_force_wcc(data->args.inode,
|
||||
data->res.fattr);
|
||||
}
|
||||
|
||||
static void nfs4_layoutcommit_release(void *calldata)
|
||||
{
|
||||
struct nfs4_layoutcommit_data *data = calldata;
|
||||
|
||||
/* Matched by references in pnfs_set_layoutcommit */
|
||||
put_lseg(data->lseg);
|
||||
put_rpccred(data->cred);
|
||||
kfree(data);
|
||||
}
|
||||
|
||||
static const struct rpc_call_ops nfs4_layoutcommit_ops = {
|
||||
.rpc_call_prepare = nfs4_layoutcommit_prepare,
|
||||
.rpc_call_done = nfs4_layoutcommit_done,
|
||||
.rpc_release = nfs4_layoutcommit_release,
|
||||
};
|
||||
|
||||
int
|
||||
nfs4_proc_layoutcommit(struct nfs4_layoutcommit_data *data, bool sync)
|
||||
{
|
||||
struct rpc_message msg = {
|
||||
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTCOMMIT],
|
||||
.rpc_argp = &data->args,
|
||||
.rpc_resp = &data->res,
|
||||
.rpc_cred = data->cred,
|
||||
};
|
||||
struct rpc_task_setup task_setup_data = {
|
||||
.task = &data->task,
|
||||
.rpc_client = NFS_CLIENT(data->args.inode),
|
||||
.rpc_message = &msg,
|
||||
.callback_ops = &nfs4_layoutcommit_ops,
|
||||
.callback_data = data,
|
||||
.flags = RPC_TASK_ASYNC,
|
||||
};
|
||||
struct rpc_task *task;
|
||||
int status = 0;
|
||||
|
||||
dprintk("NFS: %4d initiating layoutcommit call. sync %d "
|
||||
"lbw: %llu inode %lu\n",
|
||||
data->task.tk_pid, sync,
|
||||
data->args.lastbytewritten,
|
||||
data->args.inode->i_ino);
|
||||
|
||||
task = rpc_run_task(&task_setup_data);
|
||||
if (IS_ERR(task))
|
||||
return PTR_ERR(task);
|
||||
if (sync == false)
|
||||
goto out;
|
||||
status = nfs4_wait_for_completion_rpc_task(task);
|
||||
if (status != 0)
|
||||
goto out;
|
||||
status = task->tk_status;
|
||||
out:
|
||||
dprintk("%s: status %d\n", __func__, status);
|
||||
rpc_put_task(task);
|
||||
return status;
|
||||
}
|
||||
#endif /* CONFIG_NFS_V4_1 */
|
||||
|
||||
struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = {
|
||||
|
@ -5741,6 +5930,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
|
|||
.close_context = nfs4_close_context,
|
||||
.open_context = nfs4_atomic_open,
|
||||
.init_client = nfs4_init_client,
|
||||
.secinfo = nfs4_proc_secinfo,
|
||||
};
|
||||
|
||||
static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {
|
||||
|
|
313
fs/nfs/nfs4xdr.c
313
fs/nfs/nfs4xdr.c
|
@ -46,6 +46,7 @@
|
|||
#include <linux/kdev_t.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/sunrpc/msg_prot.h>
|
||||
#include <linux/sunrpc/gss_api.h>
|
||||
#include <linux/nfs.h>
|
||||
#include <linux/nfs4.h>
|
||||
#include <linux/nfs_fs.h>
|
||||
|
@ -112,7 +113,7 @@ static int nfs4_stat_to_errno(int);
|
|||
#define encode_restorefh_maxsz (op_encode_hdr_maxsz)
|
||||
#define decode_restorefh_maxsz (op_decode_hdr_maxsz)
|
||||
#define encode_fsinfo_maxsz (encode_getattr_maxsz)
|
||||
#define decode_fsinfo_maxsz (op_decode_hdr_maxsz + 11)
|
||||
#define decode_fsinfo_maxsz (op_decode_hdr_maxsz + 15)
|
||||
#define encode_renew_maxsz (op_encode_hdr_maxsz + 3)
|
||||
#define decode_renew_maxsz (op_decode_hdr_maxsz)
|
||||
#define encode_setclientid_maxsz \
|
||||
|
@ -253,6 +254,8 @@ static int nfs4_stat_to_errno(int);
|
|||
(encode_getattr_maxsz)
|
||||
#define decode_fs_locations_maxsz \
|
||||
(0)
|
||||
#define encode_secinfo_maxsz (op_encode_hdr_maxsz + nfs4_name_maxsz)
|
||||
#define decode_secinfo_maxsz (op_decode_hdr_maxsz + 4 + (NFS_MAX_SECFLAVORS * (16 + GSS_OID_MAX_LEN)))
|
||||
|
||||
#if defined(CONFIG_NFS_V4_1)
|
||||
#define NFS4_MAX_MACHINE_NAME_LEN (64)
|
||||
|
@ -324,6 +327,18 @@ static int nfs4_stat_to_errno(int);
|
|||
#define decode_layoutget_maxsz (op_decode_hdr_maxsz + 8 + \
|
||||
decode_stateid_maxsz + \
|
||||
XDR_QUADLEN(PNFS_LAYOUT_MAXSIZE))
|
||||
#define encode_layoutcommit_maxsz (op_encode_hdr_maxsz + \
|
||||
2 /* offset */ + \
|
||||
2 /* length */ + \
|
||||
1 /* reclaim */ + \
|
||||
encode_stateid_maxsz + \
|
||||
1 /* new offset (true) */ + \
|
||||
2 /* last byte written */ + \
|
||||
1 /* nt_timechanged (false) */ + \
|
||||
1 /* layoutupdate4 layout type */ + \
|
||||
1 /* NULL filelayout layoutupdate4 payload */)
|
||||
#define decode_layoutcommit_maxsz (op_decode_hdr_maxsz + 3)
|
||||
|
||||
#else /* CONFIG_NFS_V4_1 */
|
||||
#define encode_sequence_maxsz 0
|
||||
#define decode_sequence_maxsz 0
|
||||
|
@ -676,6 +691,14 @@ static int nfs4_stat_to_errno(int);
|
|||
decode_putfh_maxsz + \
|
||||
decode_lookup_maxsz + \
|
||||
decode_fs_locations_maxsz)
|
||||
#define NFS4_enc_secinfo_sz (compound_encode_hdr_maxsz + \
|
||||
encode_sequence_maxsz + \
|
||||
encode_putfh_maxsz + \
|
||||
encode_secinfo_maxsz)
|
||||
#define NFS4_dec_secinfo_sz (compound_decode_hdr_maxsz + \
|
||||
decode_sequence_maxsz + \
|
||||
decode_putfh_maxsz + \
|
||||
decode_secinfo_maxsz)
|
||||
#if defined(CONFIG_NFS_V4_1)
|
||||
#define NFS4_enc_exchange_id_sz \
|
||||
(compound_encode_hdr_maxsz + \
|
||||
|
@ -727,6 +750,17 @@ static int nfs4_stat_to_errno(int);
|
|||
decode_sequence_maxsz + \
|
||||
decode_putfh_maxsz + \
|
||||
decode_layoutget_maxsz)
|
||||
#define NFS4_enc_layoutcommit_sz (compound_encode_hdr_maxsz + \
|
||||
encode_sequence_maxsz +\
|
||||
encode_putfh_maxsz + \
|
||||
encode_layoutcommit_maxsz + \
|
||||
encode_getattr_maxsz)
|
||||
#define NFS4_dec_layoutcommit_sz (compound_decode_hdr_maxsz + \
|
||||
decode_sequence_maxsz + \
|
||||
decode_putfh_maxsz + \
|
||||
decode_layoutcommit_maxsz + \
|
||||
decode_getattr_maxsz)
|
||||
|
||||
|
||||
const u32 nfs41_maxwrite_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
|
||||
compound_encode_hdr_maxsz +
|
||||
|
@ -1620,6 +1654,18 @@ static void encode_delegreturn(struct xdr_stream *xdr, const nfs4_stateid *state
|
|||
hdr->replen += decode_delegreturn_maxsz;
|
||||
}
|
||||
|
||||
static void encode_secinfo(struct xdr_stream *xdr, const struct qstr *name, struct compound_hdr *hdr)
|
||||
{
|
||||
int len = name->len;
|
||||
__be32 *p;
|
||||
|
||||
p = reserve_space(xdr, 8 + len);
|
||||
*p++ = cpu_to_be32(OP_SECINFO);
|
||||
xdr_encode_opaque(p, name->name, len);
|
||||
hdr->nops++;
|
||||
hdr->replen += decode_secinfo_maxsz;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_NFS_V4_1)
|
||||
/* NFSv4.1 operations */
|
||||
static void encode_exchange_id(struct xdr_stream *xdr,
|
||||
|
@ -1816,6 +1862,34 @@ encode_layoutget(struct xdr_stream *xdr,
|
|||
hdr->nops++;
|
||||
hdr->replen += decode_layoutget_maxsz;
|
||||
}
|
||||
|
||||
static int
|
||||
encode_layoutcommit(struct xdr_stream *xdr,
|
||||
const struct nfs4_layoutcommit_args *args,
|
||||
struct compound_hdr *hdr)
|
||||
{
|
||||
__be32 *p;
|
||||
|
||||
dprintk("%s: lbw: %llu type: %d\n", __func__, args->lastbytewritten,
|
||||
NFS_SERVER(args->inode)->pnfs_curr_ld->id);
|
||||
|
||||
p = reserve_space(xdr, 48 + NFS4_STATEID_SIZE);
|
||||
*p++ = cpu_to_be32(OP_LAYOUTCOMMIT);
|
||||
/* Only whole file layouts */
|
||||
p = xdr_encode_hyper(p, 0); /* offset */
|
||||
p = xdr_encode_hyper(p, NFS4_MAX_UINT64); /* length */
|
||||
*p++ = cpu_to_be32(0); /* reclaim */
|
||||
p = xdr_encode_opaque_fixed(p, args->stateid.data, NFS4_STATEID_SIZE);
|
||||
*p++ = cpu_to_be32(1); /* newoffset = TRUE */
|
||||
p = xdr_encode_hyper(p, args->lastbytewritten);
|
||||
*p++ = cpu_to_be32(0); /* Never send time_modify_changed */
|
||||
*p++ = cpu_to_be32(NFS_SERVER(args->inode)->pnfs_curr_ld->id);/* type */
|
||||
*p++ = cpu_to_be32(0); /* no file layout payload */
|
||||
|
||||
hdr->nops++;
|
||||
hdr->replen += decode_layoutcommit_maxsz;
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_NFS_V4_1 */
|
||||
|
||||
/*
|
||||
|
@ -2294,7 +2368,8 @@ static void nfs4_xdr_enc_commit(struct rpc_rqst *req, struct xdr_stream *xdr,
|
|||
encode_sequence(xdr, &args->seq_args, &hdr);
|
||||
encode_putfh(xdr, args->fh, &hdr);
|
||||
encode_commit(xdr, args, &hdr);
|
||||
encode_getfattr(xdr, args->bitmask, &hdr);
|
||||
if (args->bitmask)
|
||||
encode_getfattr(xdr, args->bitmask, &hdr);
|
||||
encode_nops(&hdr);
|
||||
}
|
||||
|
||||
|
@ -2465,6 +2540,24 @@ static void nfs4_xdr_enc_fs_locations(struct rpc_rqst *req,
|
|||
encode_nops(&hdr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode SECINFO request
|
||||
*/
|
||||
static void nfs4_xdr_enc_secinfo(struct rpc_rqst *req,
|
||||
struct xdr_stream *xdr,
|
||||
struct nfs4_secinfo_arg *args)
|
||||
{
|
||||
struct compound_hdr hdr = {
|
||||
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
|
||||
};
|
||||
|
||||
encode_compound_hdr(xdr, req, &hdr);
|
||||
encode_sequence(xdr, &args->seq_args, &hdr);
|
||||
encode_putfh(xdr, args->dir_fh, &hdr);
|
||||
encode_secinfo(xdr, args->name, &hdr);
|
||||
encode_nops(&hdr);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_NFS_V4_1)
|
||||
/*
|
||||
* EXCHANGE_ID request
|
||||
|
@ -2604,8 +2697,32 @@ static void nfs4_xdr_enc_layoutget(struct rpc_rqst *req,
|
|||
encode_sequence(xdr, &args->seq_args, &hdr);
|
||||
encode_putfh(xdr, NFS_FH(args->inode), &hdr);
|
||||
encode_layoutget(xdr, args, &hdr);
|
||||
|
||||
xdr_inline_pages(&req->rq_rcv_buf, hdr.replen << 2,
|
||||
args->layout.pages, 0, args->layout.pglen);
|
||||
|
||||
encode_nops(&hdr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode LAYOUTCOMMIT request
|
||||
*/
|
||||
static int nfs4_xdr_enc_layoutcommit(struct rpc_rqst *req,
|
||||
struct xdr_stream *xdr,
|
||||
struct nfs4_layoutcommit_args *args)
|
||||
{
|
||||
struct compound_hdr hdr = {
|
||||
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
|
||||
};
|
||||
|
||||
encode_compound_hdr(xdr, req, &hdr);
|
||||
encode_sequence(xdr, &args->seq_args, &hdr);
|
||||
encode_putfh(xdr, NFS_FH(args->inode), &hdr);
|
||||
encode_layoutcommit(xdr, args, &hdr);
|
||||
encode_getfattr(xdr, args->bitmask, &hdr);
|
||||
encode_nops(&hdr);
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_NFS_V4_1 */
|
||||
|
||||
static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
|
||||
|
@ -2925,6 +3042,7 @@ static int decode_attr_error(struct xdr_stream *xdr, uint32_t *bitmap)
|
|||
if (unlikely(!p))
|
||||
goto out_overflow;
|
||||
bitmap[0] &= ~FATTR4_WORD0_RDATTR_ERROR;
|
||||
return -be32_to_cpup(p);
|
||||
}
|
||||
return 0;
|
||||
out_overflow:
|
||||
|
@ -3912,6 +4030,10 @@ static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
|
|||
fattr->valid |= status;
|
||||
|
||||
status = decode_attr_error(xdr, bitmap);
|
||||
if (status == -NFS4ERR_WRONGSEC) {
|
||||
nfs_fixup_secinfo_attributes(fattr, fh);
|
||||
status = 0;
|
||||
}
|
||||
if (status < 0)
|
||||
goto xdr_error;
|
||||
|
||||
|
@ -4680,6 +4802,73 @@ static int decode_delegreturn(struct xdr_stream *xdr)
|
|||
return decode_op_hdr(xdr, OP_DELEGRETURN);
|
||||
}
|
||||
|
||||
static int decode_secinfo_gss(struct xdr_stream *xdr, struct nfs4_secinfo_flavor *flavor)
|
||||
{
|
||||
__be32 *p;
|
||||
|
||||
p = xdr_inline_decode(xdr, 4);
|
||||
if (unlikely(!p))
|
||||
goto out_overflow;
|
||||
flavor->gss.sec_oid4.len = be32_to_cpup(p);
|
||||
if (flavor->gss.sec_oid4.len > GSS_OID_MAX_LEN)
|
||||
goto out_err;
|
||||
|
||||
p = xdr_inline_decode(xdr, flavor->gss.sec_oid4.len);
|
||||
if (unlikely(!p))
|
||||
goto out_overflow;
|
||||
memcpy(flavor->gss.sec_oid4.data, p, flavor->gss.sec_oid4.len);
|
||||
|
||||
p = xdr_inline_decode(xdr, 8);
|
||||
if (unlikely(!p))
|
||||
goto out_overflow;
|
||||
flavor->gss.qop4 = be32_to_cpup(p++);
|
||||
flavor->gss.service = be32_to_cpup(p);
|
||||
|
||||
return 0;
|
||||
|
||||
out_overflow:
|
||||
print_overflow_msg(__func__, xdr);
|
||||
return -EIO;
|
||||
out_err:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int decode_secinfo(struct xdr_stream *xdr, struct nfs4_secinfo_res *res)
|
||||
{
|
||||
struct nfs4_secinfo_flavor *sec_flavor;
|
||||
int status;
|
||||
__be32 *p;
|
||||
int i;
|
||||
|
||||
status = decode_op_hdr(xdr, OP_SECINFO);
|
||||
p = xdr_inline_decode(xdr, 4);
|
||||
if (unlikely(!p))
|
||||
goto out_overflow;
|
||||
res->flavors->num_flavors = be32_to_cpup(p);
|
||||
|
||||
for (i = 0; i < res->flavors->num_flavors; i++) {
|
||||
sec_flavor = &res->flavors->flavors[i];
|
||||
if ((char *)&sec_flavor[1] - (char *)res > PAGE_SIZE)
|
||||
break;
|
||||
|
||||
p = xdr_inline_decode(xdr, 4);
|
||||
if (unlikely(!p))
|
||||
goto out_overflow;
|
||||
sec_flavor->flavor = be32_to_cpup(p);
|
||||
|
||||
if (sec_flavor->flavor == RPC_AUTH_GSS) {
|
||||
if (decode_secinfo_gss(xdr, sec_flavor))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_overflow:
|
||||
print_overflow_msg(__func__, xdr);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_NFS_V4_1)
|
||||
static int decode_exchange_id(struct xdr_stream *xdr,
|
||||
struct nfs41_exchange_id_res *res)
|
||||
|
@ -4950,6 +5139,9 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
|
|||
__be32 *p;
|
||||
int status;
|
||||
u32 layout_count;
|
||||
struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
|
||||
struct kvec *iov = rcvbuf->head;
|
||||
u32 hdrlen, recvd;
|
||||
|
||||
status = decode_op_hdr(xdr, OP_LAYOUTGET);
|
||||
if (status)
|
||||
|
@ -4966,17 +5158,14 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
p = xdr_inline_decode(xdr, 24);
|
||||
p = xdr_inline_decode(xdr, 28);
|
||||
if (unlikely(!p))
|
||||
goto out_overflow;
|
||||
p = xdr_decode_hyper(p, &res->range.offset);
|
||||
p = xdr_decode_hyper(p, &res->range.length);
|
||||
res->range.iomode = be32_to_cpup(p++);
|
||||
res->type = be32_to_cpup(p++);
|
||||
|
||||
status = decode_opaque_inline(xdr, &res->layout.len, (char **)&p);
|
||||
if (unlikely(status))
|
||||
return status;
|
||||
res->layoutp->len = be32_to_cpup(p);
|
||||
|
||||
dprintk("%s roff:%lu rlen:%lu riomode:%d, lo_type:0x%x, lo.len:%d\n",
|
||||
__func__,
|
||||
|
@ -4984,12 +5173,18 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
|
|||
(unsigned long)res->range.length,
|
||||
res->range.iomode,
|
||||
res->type,
|
||||
res->layout.len);
|
||||
res->layoutp->len);
|
||||
|
||||
/* nfs4_proc_layoutget allocated a single page */
|
||||
if (res->layout.len > PAGE_SIZE)
|
||||
return -ENOMEM;
|
||||
memcpy(res->layout.buf, p, res->layout.len);
|
||||
hdrlen = (u8 *) xdr->p - (u8 *) iov->iov_base;
|
||||
recvd = req->rq_rcv_buf.len - hdrlen;
|
||||
if (res->layoutp->len > recvd) {
|
||||
dprintk("NFS: server cheating in layoutget reply: "
|
||||
"layout len %u > recvd %u\n",
|
||||
res->layoutp->len, recvd);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
xdr_read_pages(xdr, res->layoutp->len);
|
||||
|
||||
if (layout_count > 1) {
|
||||
/* We only handle a length one array at the moment. Any
|
||||
|
@ -5006,6 +5201,35 @@ out_overflow:
|
|||
print_overflow_msg(__func__, xdr);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int decode_layoutcommit(struct xdr_stream *xdr,
|
||||
struct rpc_rqst *req,
|
||||
struct nfs4_layoutcommit_res *res)
|
||||
{
|
||||
__be32 *p;
|
||||
__u32 sizechanged;
|
||||
int status;
|
||||
|
||||
status = decode_op_hdr(xdr, OP_LAYOUTCOMMIT);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
p = xdr_inline_decode(xdr, 4);
|
||||
if (unlikely(!p))
|
||||
goto out_overflow;
|
||||
sizechanged = be32_to_cpup(p);
|
||||
|
||||
if (sizechanged) {
|
||||
/* throw away new size */
|
||||
p = xdr_inline_decode(xdr, 8);
|
||||
if (unlikely(!p))
|
||||
goto out_overflow;
|
||||
}
|
||||
return 0;
|
||||
out_overflow:
|
||||
print_overflow_msg(__func__, xdr);
|
||||
return -EIO;
|
||||
}
|
||||
#endif /* CONFIG_NFS_V4_1 */
|
||||
|
||||
/*
|
||||
|
@ -5723,8 +5947,9 @@ static int nfs4_xdr_dec_commit(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
|
|||
status = decode_commit(xdr, res);
|
||||
if (status)
|
||||
goto out;
|
||||
decode_getfattr(xdr, res->fattr, res->server,
|
||||
!RPC_IS_ASYNC(rqstp->rq_task));
|
||||
if (res->fattr)
|
||||
decode_getfattr(xdr, res->fattr, res->server,
|
||||
!RPC_IS_ASYNC(rqstp->rq_task));
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
@ -5919,6 +6144,32 @@ out:
|
|||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode SECINFO response
|
||||
*/
|
||||
static int nfs4_xdr_dec_secinfo(struct rpc_rqst *rqstp,
|
||||
struct xdr_stream *xdr,
|
||||
struct nfs4_secinfo_res *res)
|
||||
{
|
||||
struct compound_hdr hdr;
|
||||
int status;
|
||||
|
||||
status = decode_compound_hdr(xdr, &hdr);
|
||||
if (status)
|
||||
goto out;
|
||||
status = decode_sequence(xdr, &res->seq_res, rqstp);
|
||||
if (status)
|
||||
goto out;
|
||||
status = decode_putfh(xdr);
|
||||
if (status)
|
||||
goto out;
|
||||
status = decode_secinfo(xdr, res);
|
||||
if (status)
|
||||
goto out;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_NFS_V4_1)
|
||||
/*
|
||||
* Decode EXCHANGE_ID response
|
||||
|
@ -6066,6 +6317,34 @@ static int nfs4_xdr_dec_layoutget(struct rpc_rqst *rqstp,
|
|||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode LAYOUTCOMMIT response
|
||||
*/
|
||||
static int nfs4_xdr_dec_layoutcommit(struct rpc_rqst *rqstp,
|
||||
struct xdr_stream *xdr,
|
||||
struct nfs4_layoutcommit_res *res)
|
||||
{
|
||||
struct compound_hdr hdr;
|
||||
int status;
|
||||
|
||||
status = decode_compound_hdr(xdr, &hdr);
|
||||
if (status)
|
||||
goto out;
|
||||
status = decode_sequence(xdr, &res->seq_res, rqstp);
|
||||
if (status)
|
||||
goto out;
|
||||
status = decode_putfh(xdr);
|
||||
if (status)
|
||||
goto out;
|
||||
status = decode_layoutcommit(xdr, rqstp, res);
|
||||
if (status)
|
||||
goto out;
|
||||
decode_getfattr(xdr, res->fattr, res->server,
|
||||
!RPC_IS_ASYNC(rqstp->rq_task));
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
#endif /* CONFIG_NFS_V4_1 */
|
||||
|
||||
/**
|
||||
|
@ -6180,10 +6459,6 @@ static struct {
|
|||
{ NFS4ERR_SYMLINK, -ELOOP },
|
||||
{ NFS4ERR_OP_ILLEGAL, -EOPNOTSUPP },
|
||||
{ NFS4ERR_DEADLOCK, -EDEADLK },
|
||||
{ NFS4ERR_WRONGSEC, -EPERM }, /* FIXME: this needs
|
||||
* to be handled by a
|
||||
* middle-layer.
|
||||
*/
|
||||
{ -1, -EIO }
|
||||
};
|
||||
|
||||
|
@ -6258,6 +6533,7 @@ struct rpc_procinfo nfs4_procedures[] = {
|
|||
PROC(SETACL, enc_setacl, dec_setacl),
|
||||
PROC(FS_LOCATIONS, enc_fs_locations, dec_fs_locations),
|
||||
PROC(RELEASE_LOCKOWNER, enc_release_lockowner, dec_release_lockowner),
|
||||
PROC(SECINFO, enc_secinfo, dec_secinfo),
|
||||
#if defined(CONFIG_NFS_V4_1)
|
||||
PROC(EXCHANGE_ID, enc_exchange_id, dec_exchange_id),
|
||||
PROC(CREATE_SESSION, enc_create_session, dec_create_session),
|
||||
|
@ -6267,6 +6543,7 @@ struct rpc_procinfo nfs4_procedures[] = {
|
|||
PROC(RECLAIM_COMPLETE, enc_reclaim_complete, dec_reclaim_complete),
|
||||
PROC(GETDEVICEINFO, enc_getdeviceinfo, dec_getdeviceinfo),
|
||||
PROC(LAYOUTGET, enc_layoutget, dec_layoutget),
|
||||
PROC(LAYOUTCOMMIT, enc_layoutcommit, dec_layoutcommit),
|
||||
#endif /* CONFIG_NFS_V4_1 */
|
||||
};
|
||||
|
||||
|
|
|
@ -223,6 +223,7 @@ void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
|
|||
desc->pg_count = 0;
|
||||
desc->pg_bsize = bsize;
|
||||
desc->pg_base = 0;
|
||||
desc->pg_moreio = 0;
|
||||
desc->pg_inode = inode;
|
||||
desc->pg_doio = doio;
|
||||
desc->pg_ioflags = io_flags;
|
||||
|
@ -335,9 +336,11 @@ int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
|
|||
struct nfs_page *req)
|
||||
{
|
||||
while (!nfs_pageio_do_add_request(desc, req)) {
|
||||
desc->pg_moreio = 1;
|
||||
nfs_pageio_doio(desc);
|
||||
if (desc->pg_error < 0)
|
||||
return 0;
|
||||
desc->pg_moreio = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
@ -395,6 +398,7 @@ int nfs_scan_list(struct nfs_inode *nfsi,
|
|||
pgoff_t idx_end;
|
||||
int found, i;
|
||||
int res;
|
||||
struct list_head *list;
|
||||
|
||||
res = 0;
|
||||
if (npages == 0)
|
||||
|
@ -415,10 +419,10 @@ int nfs_scan_list(struct nfs_inode *nfsi,
|
|||
idx_start = req->wb_index + 1;
|
||||
if (nfs_set_page_tag_locked(req)) {
|
||||
kref_get(&req->wb_kref);
|
||||
nfs_list_remove_request(req);
|
||||
radix_tree_tag_clear(&nfsi->nfs_page_tree,
|
||||
req->wb_index, tag);
|
||||
nfs_list_add_request(req, dst);
|
||||
list = pnfs_choose_commit_list(req, dst);
|
||||
nfs_list_add_request(req, list);
|
||||
res++;
|
||||
if (res == INT_MAX)
|
||||
goto out;
|
||||
|
|
142
fs/nfs/pnfs.c
142
fs/nfs/pnfs.c
|
@ -259,6 +259,7 @@ put_lseg(struct pnfs_layout_segment *lseg)
|
|||
pnfs_free_lseg_list(&free_me);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(put_lseg);
|
||||
|
||||
static bool
|
||||
should_free_lseg(u32 lseg_iomode, u32 recall_iomode)
|
||||
|
@ -471,6 +472,9 @@ send_layoutget(struct pnfs_layout_hdr *lo,
|
|||
struct nfs_server *server = NFS_SERVER(ino);
|
||||
struct nfs4_layoutget *lgp;
|
||||
struct pnfs_layout_segment *lseg = NULL;
|
||||
struct page **pages = NULL;
|
||||
int i;
|
||||
u32 max_resp_sz, max_pages;
|
||||
|
||||
dprintk("--> %s\n", __func__);
|
||||
|
||||
|
@ -478,6 +482,21 @@ send_layoutget(struct pnfs_layout_hdr *lo,
|
|||
lgp = kzalloc(sizeof(*lgp), GFP_KERNEL);
|
||||
if (lgp == NULL)
|
||||
return NULL;
|
||||
|
||||
/* allocate pages for xdr post processing */
|
||||
max_resp_sz = server->nfs_client->cl_session->fc_attrs.max_resp_sz;
|
||||
max_pages = max_resp_sz >> PAGE_SHIFT;
|
||||
|
||||
pages = kzalloc(max_pages * sizeof(struct page *), GFP_KERNEL);
|
||||
if (!pages)
|
||||
goto out_err_free;
|
||||
|
||||
for (i = 0; i < max_pages; i++) {
|
||||
pages[i] = alloc_page(GFP_KERNEL);
|
||||
if (!pages[i])
|
||||
goto out_err_free;
|
||||
}
|
||||
|
||||
lgp->args.minlength = NFS4_MAX_UINT64;
|
||||
lgp->args.maxcount = PNFS_LAYOUT_MAXSIZE;
|
||||
lgp->args.range.iomode = iomode;
|
||||
|
@ -486,6 +505,8 @@ send_layoutget(struct pnfs_layout_hdr *lo,
|
|||
lgp->args.type = server->pnfs_curr_ld->id;
|
||||
lgp->args.inode = ino;
|
||||
lgp->args.ctx = get_nfs_open_context(ctx);
|
||||
lgp->args.layout.pages = pages;
|
||||
lgp->args.layout.pglen = max_pages * PAGE_SIZE;
|
||||
lgp->lsegpp = &lseg;
|
||||
|
||||
/* Synchronously retrieve layout information from server and
|
||||
|
@ -496,7 +517,26 @@ send_layoutget(struct pnfs_layout_hdr *lo,
|
|||
/* remember that LAYOUTGET failed and suspend trying */
|
||||
set_bit(lo_fail_bit(iomode), &lo->plh_flags);
|
||||
}
|
||||
|
||||
/* free xdr pages */
|
||||
for (i = 0; i < max_pages; i++)
|
||||
__free_page(pages[i]);
|
||||
kfree(pages);
|
||||
|
||||
return lseg;
|
||||
|
||||
out_err_free:
|
||||
/* free any allocated xdr pages, lgp as it's not used */
|
||||
if (pages) {
|
||||
for (i = 0; i < max_pages; i++) {
|
||||
if (!pages[i])
|
||||
break;
|
||||
__free_page(pages[i]);
|
||||
}
|
||||
kfree(pages);
|
||||
}
|
||||
kfree(lgp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool pnfs_roc(struct inode *ino)
|
||||
|
@ -945,3 +985,105 @@ pnfs_try_to_read_data(struct nfs_read_data *rdata,
|
|||
dprintk("%s End (trypnfs:%d)\n", __func__, trypnfs);
|
||||
return trypnfs;
|
||||
}
|
||||
|
||||
/*
|
||||
* Currently there is only one (whole file) write lseg.
|
||||
*/
|
||||
static struct pnfs_layout_segment *pnfs_list_write_lseg(struct inode *inode)
|
||||
{
|
||||
struct pnfs_layout_segment *lseg, *rv = NULL;
|
||||
|
||||
list_for_each_entry(lseg, &NFS_I(inode)->layout->plh_segs, pls_list)
|
||||
if (lseg->pls_range.iomode == IOMODE_RW)
|
||||
rv = lseg;
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
pnfs_set_layoutcommit(struct nfs_write_data *wdata)
|
||||
{
|
||||
struct nfs_inode *nfsi = NFS_I(wdata->inode);
|
||||
loff_t end_pos = wdata->args.offset + wdata->res.count;
|
||||
|
||||
spin_lock(&nfsi->vfs_inode.i_lock);
|
||||
if (!test_and_set_bit(NFS_INO_LAYOUTCOMMIT, &nfsi->flags)) {
|
||||
/* references matched in nfs4_layoutcommit_release */
|
||||
get_lseg(wdata->lseg);
|
||||
wdata->lseg->pls_lc_cred =
|
||||
get_rpccred(wdata->args.context->state->owner->so_cred);
|
||||
mark_inode_dirty_sync(wdata->inode);
|
||||
dprintk("%s: Set layoutcommit for inode %lu ",
|
||||
__func__, wdata->inode->i_ino);
|
||||
}
|
||||
if (end_pos > wdata->lseg->pls_end_pos)
|
||||
wdata->lseg->pls_end_pos = end_pos;
|
||||
spin_unlock(&nfsi->vfs_inode.i_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pnfs_set_layoutcommit);
|
||||
|
||||
/*
|
||||
* For the LAYOUT4_NFSV4_1_FILES layout type, NFS_DATA_SYNC WRITEs and
|
||||
* NFS_UNSTABLE WRITEs with a COMMIT to data servers must store enough
|
||||
* data to disk to allow the server to recover the data if it crashes.
|
||||
* LAYOUTCOMMIT is only needed when the NFL4_UFLG_COMMIT_THRU_MDS flag
|
||||
* is off, and a COMMIT is sent to a data server, or
|
||||
* if WRITEs to a data server return NFS_DATA_SYNC.
|
||||
*/
|
||||
int
|
||||
pnfs_layoutcommit_inode(struct inode *inode, bool sync)
|
||||
{
|
||||
struct nfs4_layoutcommit_data *data;
|
||||
struct nfs_inode *nfsi = NFS_I(inode);
|
||||
struct pnfs_layout_segment *lseg;
|
||||
struct rpc_cred *cred;
|
||||
loff_t end_pos;
|
||||
int status = 0;
|
||||
|
||||
dprintk("--> %s inode %lu\n", __func__, inode->i_ino);
|
||||
|
||||
if (!test_bit(NFS_INO_LAYOUTCOMMIT, &nfsi->flags))
|
||||
return 0;
|
||||
|
||||
/* Note kzalloc ensures data->res.seq_res.sr_slot == NULL */
|
||||
data = kzalloc(sizeof(*data), GFP_NOFS);
|
||||
if (!data) {
|
||||
mark_inode_dirty_sync(inode);
|
||||
status = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
if (!test_and_clear_bit(NFS_INO_LAYOUTCOMMIT, &nfsi->flags)) {
|
||||
spin_unlock(&inode->i_lock);
|
||||
kfree(data);
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* Currently only one (whole file) write lseg which is referenced
|
||||
* in pnfs_set_layoutcommit and will be found.
|
||||
*/
|
||||
lseg = pnfs_list_write_lseg(inode);
|
||||
|
||||
end_pos = lseg->pls_end_pos;
|
||||
cred = lseg->pls_lc_cred;
|
||||
lseg->pls_end_pos = 0;
|
||||
lseg->pls_lc_cred = NULL;
|
||||
|
||||
memcpy(&data->args.stateid.data, nfsi->layout->plh_stateid.data,
|
||||
sizeof(nfsi->layout->plh_stateid.data));
|
||||
spin_unlock(&inode->i_lock);
|
||||
|
||||
data->args.inode = inode;
|
||||
data->lseg = lseg;
|
||||
data->cred = cred;
|
||||
nfs_fattr_init(&data->fattr);
|
||||
data->args.bitmask = NFS_SERVER(inode)->cache_consistency_bitmask;
|
||||
data->res.fattr = &data->fattr;
|
||||
data->args.lastbytewritten = end_pos - 1;
|
||||
data->res.server = NFS_SERVER(inode);
|
||||
|
||||
status = nfs4_proc_layoutcommit(data, sync);
|
||||
out:
|
||||
dprintk("<-- %s status %d\n", __func__, status);
|
||||
return status;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,8 @@ struct pnfs_layout_segment {
|
|||
atomic_t pls_refcount;
|
||||
unsigned long pls_flags;
|
||||
struct pnfs_layout_hdr *pls_layout;
|
||||
struct rpc_cred *pls_lc_cred; /* LAYOUTCOMMIT credential */
|
||||
loff_t pls_end_pos; /* LAYOUTCOMMIT write end */
|
||||
};
|
||||
|
||||
enum pnfs_try_status {
|
||||
|
@ -74,6 +76,13 @@ struct pnfs_layoutdriver_type {
|
|||
/* test for nfs page cache coalescing */
|
||||
int (*pg_test)(struct nfs_pageio_descriptor *, struct nfs_page *, struct nfs_page *);
|
||||
|
||||
/* Returns true if layoutdriver wants to divert this request to
|
||||
* driver's commit routine.
|
||||
*/
|
||||
bool (*mark_pnfs_commit)(struct pnfs_layout_segment *lseg);
|
||||
struct list_head * (*choose_commit_list) (struct nfs_page *req);
|
||||
int (*commit_pagelist)(struct inode *inode, struct list_head *mds_pages, int how);
|
||||
|
||||
/*
|
||||
* Return PNFS_ATTEMPTED to indicate the layout code has attempted
|
||||
* I/O, else return PNFS_NOT_ATTEMPTED to fall back to normal NFS
|
||||
|
@ -100,7 +109,6 @@ struct pnfs_device {
|
|||
unsigned int layout_type;
|
||||
unsigned int mincount;
|
||||
struct page **pages;
|
||||
void *area;
|
||||
unsigned int pgbase;
|
||||
unsigned int pglen;
|
||||
};
|
||||
|
@ -145,7 +153,8 @@ bool pnfs_roc(struct inode *ino);
|
|||
void pnfs_roc_release(struct inode *ino);
|
||||
void pnfs_roc_set_barrier(struct inode *ino, u32 barrier);
|
||||
bool pnfs_roc_drain(struct inode *ino, u32 *barrier);
|
||||
|
||||
void pnfs_set_layoutcommit(struct nfs_write_data *wdata);
|
||||
int pnfs_layoutcommit_inode(struct inode *inode, bool sync);
|
||||
|
||||
static inline int lo_fail_bit(u32 iomode)
|
||||
{
|
||||
|
@ -169,6 +178,51 @@ static inline int pnfs_enabled_sb(struct nfs_server *nfss)
|
|||
return nfss->pnfs_curr_ld != NULL;
|
||||
}
|
||||
|
||||
static inline void
|
||||
pnfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg)
|
||||
{
|
||||
if (lseg) {
|
||||
struct pnfs_layoutdriver_type *ld;
|
||||
|
||||
ld = NFS_SERVER(req->wb_page->mapping->host)->pnfs_curr_ld;
|
||||
if (ld->mark_pnfs_commit && ld->mark_pnfs_commit(lseg)) {
|
||||
set_bit(PG_PNFS_COMMIT, &req->wb_flags);
|
||||
req->wb_commit_lseg = get_lseg(lseg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline int
|
||||
pnfs_commit_list(struct inode *inode, struct list_head *mds_pages, int how)
|
||||
{
|
||||
if (!test_and_clear_bit(NFS_INO_PNFS_COMMIT, &NFS_I(inode)->flags))
|
||||
return PNFS_NOT_ATTEMPTED;
|
||||
return NFS_SERVER(inode)->pnfs_curr_ld->commit_pagelist(inode, mds_pages, how);
|
||||
}
|
||||
|
||||
static inline struct list_head *
|
||||
pnfs_choose_commit_list(struct nfs_page *req, struct list_head *mds)
|
||||
{
|
||||
struct list_head *rv;
|
||||
|
||||
if (test_and_clear_bit(PG_PNFS_COMMIT, &req->wb_flags)) {
|
||||
struct inode *inode = req->wb_commit_lseg->pls_layout->plh_inode;
|
||||
|
||||
set_bit(NFS_INO_PNFS_COMMIT, &NFS_I(inode)->flags);
|
||||
rv = NFS_SERVER(inode)->pnfs_curr_ld->choose_commit_list(req);
|
||||
/* matched by ref taken when PG_PNFS_COMMIT is set */
|
||||
put_lseg(req->wb_commit_lseg);
|
||||
} else
|
||||
rv = mds;
|
||||
return rv;
|
||||
}
|
||||
|
||||
static inline void pnfs_clear_request_commit(struct nfs_page *req)
|
||||
{
|
||||
if (test_and_clear_bit(PG_PNFS_COMMIT, &req->wb_flags))
|
||||
put_lseg(req->wb_commit_lseg);
|
||||
}
|
||||
|
||||
#else /* CONFIG_NFS_V4_1 */
|
||||
|
||||
static inline void pnfs_destroy_all_layouts(struct nfs_client *clp)
|
||||
|
@ -252,6 +306,31 @@ pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *ino)
|
|||
pgio->pg_test = NULL;
|
||||
}
|
||||
|
||||
static inline void
|
||||
pnfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int
|
||||
pnfs_commit_list(struct inode *inode, struct list_head *mds_pages, int how)
|
||||
{
|
||||
return PNFS_NOT_ATTEMPTED;
|
||||
}
|
||||
|
||||
static inline struct list_head *
|
||||
pnfs_choose_commit_list(struct nfs_page *req, struct list_head *mds)
|
||||
{
|
||||
return mds;
|
||||
}
|
||||
|
||||
static inline void pnfs_clear_request_commit(struct nfs_page *req)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int pnfs_layoutcommit_inode(struct inode *inode, bool sync)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_NFS_V4_1 */
|
||||
|
||||
#endif /* FS_NFS_PNFS_H */
|
||||
|
|
|
@ -177,7 +177,7 @@ nfs_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
|
|||
}
|
||||
|
||||
static int
|
||||
nfs_proc_lookup(struct inode *dir, struct qstr *name,
|
||||
nfs_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qstr *name,
|
||||
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
||||
{
|
||||
struct nfs_diropargs arg = {
|
||||
|
|
230
fs/nfs/write.c
230
fs/nfs/write.c
|
@ -59,6 +59,7 @@ struct nfs_write_data *nfs_commitdata_alloc(void)
|
|||
}
|
||||
return p;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_commitdata_alloc);
|
||||
|
||||
void nfs_commit_free(struct nfs_write_data *p)
|
||||
{
|
||||
|
@ -66,6 +67,7 @@ void nfs_commit_free(struct nfs_write_data *p)
|
|||
kfree(p->pagevec);
|
||||
mempool_free(p, nfs_commit_mempool);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_commit_free);
|
||||
|
||||
struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount)
|
||||
{
|
||||
|
@ -179,8 +181,8 @@ static int wb_priority(struct writeback_control *wbc)
|
|||
if (wbc->for_reclaim)
|
||||
return FLUSH_HIGHPRI | FLUSH_STABLE;
|
||||
if (wbc->for_kupdate || wbc->for_background)
|
||||
return FLUSH_LOWPRI;
|
||||
return 0;
|
||||
return FLUSH_LOWPRI | FLUSH_COND_STABLE;
|
||||
return FLUSH_COND_STABLE;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -441,7 +443,7 @@ nfs_mark_request_dirty(struct nfs_page *req)
|
|||
* Add a request to the inode's commit list.
|
||||
*/
|
||||
static void
|
||||
nfs_mark_request_commit(struct nfs_page *req)
|
||||
nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg)
|
||||
{
|
||||
struct inode *inode = req->wb_context->path.dentry->d_inode;
|
||||
struct nfs_inode *nfsi = NFS_I(inode);
|
||||
|
@ -453,6 +455,7 @@ nfs_mark_request_commit(struct nfs_page *req)
|
|||
NFS_PAGE_TAG_COMMIT);
|
||||
nfsi->ncommit++;
|
||||
spin_unlock(&inode->i_lock);
|
||||
pnfs_mark_request_commit(req, lseg);
|
||||
inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
|
||||
inc_bdi_stat(req->wb_page->mapping->backing_dev_info, BDI_RECLAIMABLE);
|
||||
__mark_inode_dirty(inode, I_DIRTY_DATASYNC);
|
||||
|
@ -474,14 +477,18 @@ nfs_clear_request_commit(struct nfs_page *req)
|
|||
static inline
|
||||
int nfs_write_need_commit(struct nfs_write_data *data)
|
||||
{
|
||||
return data->verf.committed != NFS_FILE_SYNC;
|
||||
if (data->verf.committed == NFS_DATA_SYNC)
|
||||
return data->lseg == NULL;
|
||||
else
|
||||
return data->verf.committed != NFS_FILE_SYNC;
|
||||
}
|
||||
|
||||
static inline
|
||||
int nfs_reschedule_unstable_write(struct nfs_page *req)
|
||||
int nfs_reschedule_unstable_write(struct nfs_page *req,
|
||||
struct nfs_write_data *data)
|
||||
{
|
||||
if (test_and_clear_bit(PG_NEED_COMMIT, &req->wb_flags)) {
|
||||
nfs_mark_request_commit(req);
|
||||
nfs_mark_request_commit(req, data->lseg);
|
||||
return 1;
|
||||
}
|
||||
if (test_and_clear_bit(PG_NEED_RESCHED, &req->wb_flags)) {
|
||||
|
@ -492,7 +499,7 @@ int nfs_reschedule_unstable_write(struct nfs_page *req)
|
|||
}
|
||||
#else
|
||||
static inline void
|
||||
nfs_mark_request_commit(struct nfs_page *req)
|
||||
nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -509,7 +516,8 @@ int nfs_write_need_commit(struct nfs_write_data *data)
|
|||
}
|
||||
|
||||
static inline
|
||||
int nfs_reschedule_unstable_write(struct nfs_page *req)
|
||||
int nfs_reschedule_unstable_write(struct nfs_page *req,
|
||||
struct nfs_write_data *data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -612,9 +620,11 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode,
|
|||
}
|
||||
|
||||
if (nfs_clear_request_commit(req) &&
|
||||
radix_tree_tag_clear(&NFS_I(inode)->nfs_page_tree,
|
||||
req->wb_index, NFS_PAGE_TAG_COMMIT) != NULL)
|
||||
radix_tree_tag_clear(&NFS_I(inode)->nfs_page_tree,
|
||||
req->wb_index, NFS_PAGE_TAG_COMMIT) != NULL) {
|
||||
NFS_I(inode)->ncommit--;
|
||||
pnfs_clear_request_commit(req);
|
||||
}
|
||||
|
||||
/* Okay, the request matches. Update the region */
|
||||
if (offset < req->wb_offset) {
|
||||
|
@ -762,11 +772,12 @@ int nfs_updatepage(struct file *file, struct page *page,
|
|||
return status;
|
||||
}
|
||||
|
||||
static void nfs_writepage_release(struct nfs_page *req)
|
||||
static void nfs_writepage_release(struct nfs_page *req,
|
||||
struct nfs_write_data *data)
|
||||
{
|
||||
struct page *page = req->wb_page;
|
||||
|
||||
if (PageError(req->wb_page) || !nfs_reschedule_unstable_write(req))
|
||||
if (PageError(req->wb_page) || !nfs_reschedule_unstable_write(req, data))
|
||||
nfs_inode_remove_request(req);
|
||||
nfs_clear_page_tag_locked(req);
|
||||
nfs_end_page_writeback(page);
|
||||
|
@ -863,7 +874,7 @@ static int nfs_write_rpcsetup(struct nfs_page *req,
|
|||
data->args.context = get_nfs_open_context(req->wb_context);
|
||||
data->args.lock_context = req->wb_lock_context;
|
||||
data->args.stable = NFS_UNSTABLE;
|
||||
if (how & FLUSH_STABLE) {
|
||||
if (how & (FLUSH_STABLE | FLUSH_COND_STABLE)) {
|
||||
data->args.stable = NFS_DATA_SYNC;
|
||||
if (!nfs_need_commit(NFS_I(inode)))
|
||||
data->args.stable = NFS_FILE_SYNC;
|
||||
|
@ -912,6 +923,12 @@ static int nfs_flush_multi(struct nfs_pageio_descriptor *desc)
|
|||
|
||||
nfs_list_remove_request(req);
|
||||
|
||||
if ((desc->pg_ioflags & FLUSH_COND_STABLE) &&
|
||||
(desc->pg_moreio || NFS_I(desc->pg_inode)->ncommit ||
|
||||
desc->pg_count > wsize))
|
||||
desc->pg_ioflags &= ~FLUSH_COND_STABLE;
|
||||
|
||||
|
||||
nbytes = desc->pg_count;
|
||||
do {
|
||||
size_t len = min(nbytes, wsize);
|
||||
|
@ -1002,6 +1019,10 @@ static int nfs_flush_one(struct nfs_pageio_descriptor *desc)
|
|||
if ((!lseg) && list_is_singular(&data->pages))
|
||||
lseg = pnfs_update_layout(desc->pg_inode, req->wb_context, IOMODE_RW);
|
||||
|
||||
if ((desc->pg_ioflags & FLUSH_COND_STABLE) &&
|
||||
(desc->pg_moreio || NFS_I(desc->pg_inode)->ncommit))
|
||||
desc->pg_ioflags &= ~FLUSH_COND_STABLE;
|
||||
|
||||
/* Set up the argument struct */
|
||||
ret = nfs_write_rpcsetup(req, data, &nfs_write_full_ops, desc->pg_count, 0, lseg, desc->pg_ioflags);
|
||||
out:
|
||||
|
@ -1074,7 +1095,7 @@ static void nfs_writeback_release_partial(void *calldata)
|
|||
|
||||
out:
|
||||
if (atomic_dec_and_test(&req->wb_complete))
|
||||
nfs_writepage_release(req);
|
||||
nfs_writepage_release(req, data);
|
||||
nfs_writedata_release(calldata);
|
||||
}
|
||||
|
||||
|
@ -1141,7 +1162,7 @@ static void nfs_writeback_release_full(void *calldata)
|
|||
|
||||
if (nfs_write_need_commit(data)) {
|
||||
memcpy(&req->wb_verf, &data->verf, sizeof(req->wb_verf));
|
||||
nfs_mark_request_commit(req);
|
||||
nfs_mark_request_commit(req, data->lseg);
|
||||
dprintk(" marked for commit\n");
|
||||
goto next;
|
||||
}
|
||||
|
@ -1251,78 +1272,60 @@ void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
|
|||
#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
|
||||
static int nfs_commit_set_lock(struct nfs_inode *nfsi, int may_wait)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!test_and_set_bit(NFS_INO_COMMIT, &nfsi->flags))
|
||||
return 1;
|
||||
if (may_wait && !out_of_line_wait_on_bit_lock(&nfsi->flags,
|
||||
NFS_INO_COMMIT, nfs_wait_bit_killable,
|
||||
TASK_KILLABLE))
|
||||
return 1;
|
||||
return 0;
|
||||
if (!may_wait)
|
||||
return 0;
|
||||
ret = out_of_line_wait_on_bit_lock(&nfsi->flags,
|
||||
NFS_INO_COMMIT,
|
||||
nfs_wait_bit_killable,
|
||||
TASK_KILLABLE);
|
||||
return (ret < 0) ? ret : 1;
|
||||
}
|
||||
|
||||
static void nfs_commit_clear_lock(struct nfs_inode *nfsi)
|
||||
void nfs_commit_clear_lock(struct nfs_inode *nfsi)
|
||||
{
|
||||
clear_bit(NFS_INO_COMMIT, &nfsi->flags);
|
||||
smp_mb__after_clear_bit();
|
||||
wake_up_bit(&nfsi->flags, NFS_INO_COMMIT);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_commit_clear_lock);
|
||||
|
||||
|
||||
static void nfs_commitdata_release(void *data)
|
||||
void nfs_commitdata_release(void *data)
|
||||
{
|
||||
struct nfs_write_data *wdata = data;
|
||||
|
||||
put_lseg(wdata->lseg);
|
||||
put_nfs_open_context(wdata->args.context);
|
||||
nfs_commit_free(wdata);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_commitdata_release);
|
||||
|
||||
/*
|
||||
* Set up the argument/result storage required for the RPC call.
|
||||
*/
|
||||
static int nfs_commit_rpcsetup(struct list_head *head,
|
||||
struct nfs_write_data *data,
|
||||
int how)
|
||||
int nfs_initiate_commit(struct nfs_write_data *data, struct rpc_clnt *clnt,
|
||||
const struct rpc_call_ops *call_ops,
|
||||
int how)
|
||||
{
|
||||
struct nfs_page *first = nfs_list_entry(head->next);
|
||||
struct inode *inode = first->wb_context->path.dentry->d_inode;
|
||||
int priority = flush_task_priority(how);
|
||||
struct rpc_task *task;
|
||||
int priority = flush_task_priority(how);
|
||||
struct rpc_message msg = {
|
||||
.rpc_argp = &data->args,
|
||||
.rpc_resp = &data->res,
|
||||
.rpc_cred = first->wb_context->cred,
|
||||
.rpc_cred = data->cred,
|
||||
};
|
||||
struct rpc_task_setup task_setup_data = {
|
||||
.task = &data->task,
|
||||
.rpc_client = NFS_CLIENT(inode),
|
||||
.rpc_client = clnt,
|
||||
.rpc_message = &msg,
|
||||
.callback_ops = &nfs_commit_ops,
|
||||
.callback_ops = call_ops,
|
||||
.callback_data = data,
|
||||
.workqueue = nfsiod_workqueue,
|
||||
.flags = RPC_TASK_ASYNC,
|
||||
.priority = priority,
|
||||
};
|
||||
|
||||
/* Set up the RPC argument and reply structs
|
||||
* NB: take care not to mess about with data->commit et al. */
|
||||
|
||||
list_splice_init(head, &data->pages);
|
||||
|
||||
data->inode = inode;
|
||||
data->cred = msg.rpc_cred;
|
||||
|
||||
data->args.fh = NFS_FH(data->inode);
|
||||
/* Note: we always request a commit of the entire inode */
|
||||
data->args.offset = 0;
|
||||
data->args.count = 0;
|
||||
data->args.context = get_nfs_open_context(first->wb_context);
|
||||
data->res.count = 0;
|
||||
data->res.fattr = &data->fattr;
|
||||
data->res.verf = &data->verf;
|
||||
nfs_fattr_init(&data->fattr);
|
||||
|
||||
/* Set up the initial task struct. */
|
||||
NFS_PROTO(inode)->commit_setup(data, &msg);
|
||||
NFS_PROTO(data->inode)->commit_setup(data, &msg);
|
||||
|
||||
dprintk("NFS: %5u initiated commit call\n", data->task.tk_pid);
|
||||
|
||||
|
@ -1334,6 +1337,56 @@ static int nfs_commit_rpcsetup(struct list_head *head,
|
|||
rpc_put_task(task);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_initiate_commit);
|
||||
|
||||
/*
|
||||
* Set up the argument/result storage required for the RPC call.
|
||||
*/
|
||||
void nfs_init_commit(struct nfs_write_data *data,
|
||||
struct list_head *head,
|
||||
struct pnfs_layout_segment *lseg)
|
||||
{
|
||||
struct nfs_page *first = nfs_list_entry(head->next);
|
||||
struct inode *inode = first->wb_context->path.dentry->d_inode;
|
||||
|
||||
/* Set up the RPC argument and reply structs
|
||||
* NB: take care not to mess about with data->commit et al. */
|
||||
|
||||
list_splice_init(head, &data->pages);
|
||||
|
||||
data->inode = inode;
|
||||
data->cred = first->wb_context->cred;
|
||||
data->lseg = lseg; /* reference transferred */
|
||||
data->mds_ops = &nfs_commit_ops;
|
||||
|
||||
data->args.fh = NFS_FH(data->inode);
|
||||
/* Note: we always request a commit of the entire inode */
|
||||
data->args.offset = 0;
|
||||
data->args.count = 0;
|
||||
data->args.context = get_nfs_open_context(first->wb_context);
|
||||
data->res.count = 0;
|
||||
data->res.fattr = &data->fattr;
|
||||
data->res.verf = &data->verf;
|
||||
nfs_fattr_init(&data->fattr);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_init_commit);
|
||||
|
||||
void nfs_retry_commit(struct list_head *page_list,
|
||||
struct pnfs_layout_segment *lseg)
|
||||
{
|
||||
struct nfs_page *req;
|
||||
|
||||
while (!list_empty(page_list)) {
|
||||
req = nfs_list_entry(page_list->next);
|
||||
nfs_list_remove_request(req);
|
||||
nfs_mark_request_commit(req, lseg);
|
||||
dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
|
||||
dec_bdi_stat(req->wb_page->mapping->backing_dev_info,
|
||||
BDI_RECLAIMABLE);
|
||||
nfs_clear_page_tag_locked(req);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_retry_commit);
|
||||
|
||||
/*
|
||||
* Commit dirty pages
|
||||
|
@ -1342,7 +1395,6 @@ static int
|
|||
nfs_commit_list(struct inode *inode, struct list_head *head, int how)
|
||||
{
|
||||
struct nfs_write_data *data;
|
||||
struct nfs_page *req;
|
||||
|
||||
data = nfs_commitdata_alloc();
|
||||
|
||||
|
@ -1350,17 +1402,10 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how)
|
|||
goto out_bad;
|
||||
|
||||
/* Set up the argument struct */
|
||||
return nfs_commit_rpcsetup(head, data, how);
|
||||
nfs_init_commit(data, head, NULL);
|
||||
return nfs_initiate_commit(data, NFS_CLIENT(inode), data->mds_ops, how);
|
||||
out_bad:
|
||||
while (!list_empty(head)) {
|
||||
req = nfs_list_entry(head->next);
|
||||
nfs_list_remove_request(req);
|
||||
nfs_mark_request_commit(req);
|
||||
dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
|
||||
dec_bdi_stat(req->wb_page->mapping->backing_dev_info,
|
||||
BDI_RECLAIMABLE);
|
||||
nfs_clear_page_tag_locked(req);
|
||||
}
|
||||
nfs_retry_commit(head, NULL);
|
||||
nfs_commit_clear_lock(NFS_I(inode));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
@ -1380,10 +1425,9 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata)
|
|||
return;
|
||||
}
|
||||
|
||||
static void nfs_commit_release(void *calldata)
|
||||
void nfs_commit_release_pages(struct nfs_write_data *data)
|
||||
{
|
||||
struct nfs_write_data *data = calldata;
|
||||
struct nfs_page *req;
|
||||
struct nfs_page *req;
|
||||
int status = data->task.tk_status;
|
||||
|
||||
while (!list_empty(&data->pages)) {
|
||||
|
@ -1417,6 +1461,14 @@ static void nfs_commit_release(void *calldata)
|
|||
next:
|
||||
nfs_clear_page_tag_locked(req);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_commit_release_pages);
|
||||
|
||||
static void nfs_commit_release(void *calldata)
|
||||
{
|
||||
struct nfs_write_data *data = calldata;
|
||||
|
||||
nfs_commit_release_pages(data);
|
||||
nfs_commit_clear_lock(NFS_I(data->inode));
|
||||
nfs_commitdata_release(calldata);
|
||||
}
|
||||
|
@ -1433,23 +1485,30 @@ int nfs_commit_inode(struct inode *inode, int how)
|
|||
{
|
||||
LIST_HEAD(head);
|
||||
int may_wait = how & FLUSH_SYNC;
|
||||
int res = 0;
|
||||
int res;
|
||||
|
||||
if (!nfs_commit_set_lock(NFS_I(inode), may_wait))
|
||||
res = nfs_commit_set_lock(NFS_I(inode), may_wait);
|
||||
if (res <= 0)
|
||||
goto out_mark_dirty;
|
||||
spin_lock(&inode->i_lock);
|
||||
res = nfs_scan_commit(inode, &head, 0, 0);
|
||||
spin_unlock(&inode->i_lock);
|
||||
if (res) {
|
||||
int error = nfs_commit_list(inode, &head, how);
|
||||
int error;
|
||||
|
||||
error = pnfs_commit_list(inode, &head, how);
|
||||
if (error == PNFS_NOT_ATTEMPTED)
|
||||
error = nfs_commit_list(inode, &head, how);
|
||||
if (error < 0)
|
||||
return error;
|
||||
if (may_wait)
|
||||
wait_on_bit(&NFS_I(inode)->flags, NFS_INO_COMMIT,
|
||||
nfs_wait_bit_killable,
|
||||
TASK_KILLABLE);
|
||||
else
|
||||
if (!may_wait)
|
||||
goto out_mark_dirty;
|
||||
error = wait_on_bit(&NFS_I(inode)->flags,
|
||||
NFS_INO_COMMIT,
|
||||
nfs_wait_bit_killable,
|
||||
TASK_KILLABLE);
|
||||
if (error < 0)
|
||||
return error;
|
||||
} else
|
||||
nfs_commit_clear_lock(NFS_I(inode));
|
||||
return res;
|
||||
|
@ -1503,7 +1562,22 @@ static int nfs_commit_unstable_pages(struct inode *inode, struct writeback_contr
|
|||
|
||||
int nfs_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
{
|
||||
return nfs_commit_unstable_pages(inode, wbc);
|
||||
int ret;
|
||||
|
||||
ret = nfs_commit_unstable_pages(inode, wbc);
|
||||
if (ret >= 0 && test_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(inode)->flags)) {
|
||||
int status;
|
||||
bool sync = true;
|
||||
|
||||
if (wbc->sync_mode == WB_SYNC_NONE || wbc->nonblocking ||
|
||||
wbc->for_background)
|
||||
sync = false;
|
||||
|
||||
status = pnfs_layoutcommit_inode(inode, sync);
|
||||
if (status < 0)
|
||||
return status;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -117,7 +117,6 @@ int nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode,
|
|||
* invoked in contexts where a memory allocation failure is
|
||||
* fatal. Fortunately this fake ACL is small enough to
|
||||
* construct on the stack. */
|
||||
memset(acl2, 0, sizeof(acl2));
|
||||
posix_acl_init(acl2, 4);
|
||||
|
||||
/* Insert entries in canonical order: other orders seem
|
||||
|
|
|
@ -550,6 +550,7 @@ enum {
|
|||
NFSPROC4_CLNT_SETACL,
|
||||
NFSPROC4_CLNT_FS_LOCATIONS,
|
||||
NFSPROC4_CLNT_RELEASE_LOCKOWNER,
|
||||
NFSPROC4_CLNT_SECINFO,
|
||||
|
||||
/* nfs41 */
|
||||
NFSPROC4_CLNT_EXCHANGE_ID,
|
||||
|
@ -560,6 +561,7 @@ enum {
|
|||
NFSPROC4_CLNT_RECLAIM_COMPLETE,
|
||||
NFSPROC4_CLNT_LAYOUTGET,
|
||||
NFSPROC4_CLNT_GETDEVICEINFO,
|
||||
NFSPROC4_CLNT_LAYOUTCOMMIT,
|
||||
};
|
||||
|
||||
/* nfs41 types */
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
#define FLUSH_STABLE 4 /* commit to stable storage */
|
||||
#define FLUSH_LOWPRI 8 /* low priority background flush */
|
||||
#define FLUSH_HIGHPRI 16 /* high priority memory reclaim flush */
|
||||
#define FLUSH_COND_STABLE 32 /* conditional stable write - only stable
|
||||
* if everything fits in one RPC */
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
|
@ -93,8 +95,13 @@ struct nfs_open_context {
|
|||
int error;
|
||||
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct nfs_open_dir_context {
|
||||
struct rpc_cred *cred;
|
||||
__u64 dir_cookie;
|
||||
__u64 dup_cookie;
|
||||
int duped;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -191,6 +198,7 @@ struct nfs_inode {
|
|||
|
||||
/* pNFS layout information */
|
||||
struct pnfs_layout_hdr *layout;
|
||||
atomic_t commits_outstanding;
|
||||
#endif /* CONFIG_NFS_V4*/
|
||||
#ifdef CONFIG_NFS_FSCACHE
|
||||
struct fscache_cookie *fscache;
|
||||
|
@ -219,6 +227,8 @@ struct nfs_inode {
|
|||
#define NFS_INO_FSCACHE (5) /* inode can be cached by FS-Cache */
|
||||
#define NFS_INO_FSCACHE_LOCK (6) /* FS-Cache cookie management lock */
|
||||
#define NFS_INO_COMMIT (7) /* inode is committing unstable writes */
|
||||
#define NFS_INO_PNFS_COMMIT (8) /* use pnfs code for commit */
|
||||
#define NFS_INO_LAYOUTCOMMIT (9) /* layoutcommit required */
|
||||
|
||||
static inline struct nfs_inode *NFS_I(const struct inode *inode)
|
||||
{
|
||||
|
|
|
@ -33,11 +33,15 @@ enum {
|
|||
PG_CLEAN,
|
||||
PG_NEED_COMMIT,
|
||||
PG_NEED_RESCHED,
|
||||
PG_PNFS_COMMIT,
|
||||
};
|
||||
|
||||
struct nfs_inode;
|
||||
struct nfs_page {
|
||||
struct list_head wb_list; /* Defines state of page: */
|
||||
union {
|
||||
struct list_head wb_list; /* Defines state of page: */
|
||||
struct pnfs_layout_segment *wb_commit_lseg; /* Used when PG_PNFS_COMMIT set */
|
||||
};
|
||||
struct page *wb_page; /* page to read in/write out */
|
||||
struct nfs_open_context *wb_context; /* File state context info */
|
||||
struct nfs_lock_context *wb_lock_context; /* lock context info */
|
||||
|
@ -57,6 +61,7 @@ struct nfs_pageio_descriptor {
|
|||
size_t pg_count;
|
||||
size_t pg_bsize;
|
||||
unsigned int pg_base;
|
||||
char pg_moreio;
|
||||
|
||||
struct inode *pg_inode;
|
||||
int (*pg_doio)(struct nfs_pageio_descriptor *);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <linux/nfsacl.h>
|
||||
#include <linux/nfs3.h>
|
||||
#include <linux/sunrpc/gss_api.h>
|
||||
|
||||
/*
|
||||
* To change the maximum rsize and wsize supported by the NFS client, adjust
|
||||
|
@ -14,6 +15,9 @@
|
|||
#define NFS_DEF_FILE_IO_SIZE (4096U)
|
||||
#define NFS_MIN_FILE_IO_SIZE (1024U)
|
||||
|
||||
/* Forward declaration for NFS v3 */
|
||||
struct nfs4_secinfo_flavors;
|
||||
|
||||
struct nfs_fsid {
|
||||
uint64_t major;
|
||||
uint64_t minor;
|
||||
|
@ -78,6 +82,7 @@ struct nfs_fattr {
|
|||
#define NFS_ATTR_FATTR_CHANGE (1U << 17)
|
||||
#define NFS_ATTR_FATTR_PRECHANGE (1U << 18)
|
||||
#define NFS_ATTR_FATTR_V4_REFERRAL (1U << 19) /* NFSv4 referral */
|
||||
#define NFS_ATTR_FATTR_MOUNTPOINT (1U << 20) /* Treat as mountpoint */
|
||||
|
||||
#define NFS_ATTR_FATTR (NFS_ATTR_FATTR_TYPE \
|
||||
| NFS_ATTR_FATTR_MODE \
|
||||
|
@ -190,8 +195,9 @@ struct nfs4_get_lease_time_res {
|
|||
#define PNFS_LAYOUT_MAXSIZE 4096
|
||||
|
||||
struct nfs4_layoutdriver_data {
|
||||
struct page **pages;
|
||||
__u32 pglen;
|
||||
__u32 len;
|
||||
void *buf;
|
||||
};
|
||||
|
||||
struct pnfs_layout_range {
|
||||
|
@ -209,6 +215,7 @@ struct nfs4_layoutget_args {
|
|||
struct nfs_open_context *ctx;
|
||||
struct nfs4_sequence_args seq_args;
|
||||
nfs4_stateid stateid;
|
||||
struct nfs4_layoutdriver_data layout;
|
||||
};
|
||||
|
||||
struct nfs4_layoutget_res {
|
||||
|
@ -216,8 +223,8 @@ struct nfs4_layoutget_res {
|
|||
struct pnfs_layout_range range;
|
||||
__u32 type;
|
||||
nfs4_stateid stateid;
|
||||
struct nfs4_layoutdriver_data layout;
|
||||
struct nfs4_sequence_res seq_res;
|
||||
struct nfs4_layoutdriver_data *layoutp;
|
||||
};
|
||||
|
||||
struct nfs4_layoutget {
|
||||
|
@ -236,6 +243,29 @@ struct nfs4_getdeviceinfo_res {
|
|||
struct nfs4_sequence_res seq_res;
|
||||
};
|
||||
|
||||
struct nfs4_layoutcommit_args {
|
||||
nfs4_stateid stateid;
|
||||
__u64 lastbytewritten;
|
||||
struct inode *inode;
|
||||
const u32 *bitmask;
|
||||
struct nfs4_sequence_args seq_args;
|
||||
};
|
||||
|
||||
struct nfs4_layoutcommit_res {
|
||||
struct nfs_fattr *fattr;
|
||||
const struct nfs_server *server;
|
||||
struct nfs4_sequence_res seq_res;
|
||||
};
|
||||
|
||||
struct nfs4_layoutcommit_data {
|
||||
struct rpc_task task;
|
||||
struct nfs_fattr fattr;
|
||||
struct pnfs_layout_segment *lseg;
|
||||
struct rpc_cred *cred;
|
||||
struct nfs4_layoutcommit_args args;
|
||||
struct nfs4_layoutcommit_res res;
|
||||
};
|
||||
|
||||
/*
|
||||
* Arguments to the open call.
|
||||
*/
|
||||
|
@ -936,6 +966,38 @@ struct nfs4_fs_locations_res {
|
|||
struct nfs4_sequence_res seq_res;
|
||||
};
|
||||
|
||||
struct nfs4_secinfo_oid {
|
||||
unsigned int len;
|
||||
char data[GSS_OID_MAX_LEN];
|
||||
};
|
||||
|
||||
struct nfs4_secinfo_gss {
|
||||
struct nfs4_secinfo_oid sec_oid4;
|
||||
unsigned int qop4;
|
||||
unsigned int service;
|
||||
};
|
||||
|
||||
struct nfs4_secinfo_flavor {
|
||||
unsigned int flavor;
|
||||
struct nfs4_secinfo_gss gss;
|
||||
};
|
||||
|
||||
struct nfs4_secinfo_flavors {
|
||||
unsigned int num_flavors;
|
||||
struct nfs4_secinfo_flavor flavors[0];
|
||||
};
|
||||
|
||||
struct nfs4_secinfo_arg {
|
||||
const struct nfs_fh *dir_fh;
|
||||
const struct qstr *name;
|
||||
struct nfs4_sequence_args seq_args;
|
||||
};
|
||||
|
||||
struct nfs4_secinfo_res {
|
||||
struct nfs4_secinfo_flavors *flavors;
|
||||
struct nfs4_sequence_res seq_res;
|
||||
};
|
||||
|
||||
#endif /* CONFIG_NFS_V4 */
|
||||
|
||||
struct nfstime4 {
|
||||
|
@ -1040,6 +1102,7 @@ struct nfs_write_data {
|
|||
struct nfs_writeres res; /* result struct */
|
||||
struct pnfs_layout_segment *lseg;
|
||||
struct nfs_client *ds_clp; /* pNFS data server */
|
||||
int ds_commit_index;
|
||||
const struct rpc_call_ops *mds_ops;
|
||||
int (*write_done_cb) (struct rpc_task *task, struct nfs_write_data *data);
|
||||
#ifdef CONFIG_NFS_V4
|
||||
|
@ -1071,7 +1134,7 @@ struct nfs_rpc_ops {
|
|||
struct nfs_fattr *);
|
||||
int (*setattr) (struct dentry *, struct nfs_fattr *,
|
||||
struct iattr *);
|
||||
int (*lookup) (struct inode *, struct qstr *,
|
||||
int (*lookup) (struct rpc_clnt *clnt, struct inode *, struct qstr *,
|
||||
struct nfs_fh *, struct nfs_fattr *);
|
||||
int (*access) (struct inode *, struct nfs_access_entry *);
|
||||
int (*readlink)(struct inode *, struct page *, unsigned int,
|
||||
|
@ -1118,6 +1181,7 @@ struct nfs_rpc_ops {
|
|||
struct iattr *iattr);
|
||||
int (*init_client) (struct nfs_client *, const struct rpc_timeout *,
|
||||
const char *, rpc_authflavor_t, int);
|
||||
int (*secinfo)(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *);
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -126,6 +126,9 @@ struct gss_api_mech *gss_mech_get_by_name(const char *);
|
|||
/* Similar, but get by pseudoflavor. */
|
||||
struct gss_api_mech *gss_mech_get_by_pseudoflavor(u32);
|
||||
|
||||
/* Fill in an array with a list of supported pseudoflavors */
|
||||
int gss_mech_list_pseudoflavors(u32 *);
|
||||
|
||||
/* Just increments the mechanism's reference count and returns its input: */
|
||||
struct gss_api_mech * gss_mech_get(struct gss_api_mech *);
|
||||
|
||||
|
|
|
@ -160,6 +160,28 @@ gss_mech_get_by_name(const char *name)
|
|||
|
||||
EXPORT_SYMBOL_GPL(gss_mech_get_by_name);
|
||||
|
||||
struct gss_api_mech *
|
||||
gss_mech_get_by_OID(struct xdr_netobj *obj)
|
||||
{
|
||||
struct gss_api_mech *pos, *gm = NULL;
|
||||
|
||||
spin_lock(®istered_mechs_lock);
|
||||
list_for_each_entry(pos, ®istered_mechs, gm_list) {
|
||||
if (obj->len == pos->gm_oid.len) {
|
||||
if (0 == memcmp(obj->data, pos->gm_oid.data, obj->len)) {
|
||||
if (try_module_get(pos->gm_owner))
|
||||
gm = pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock(®istered_mechs_lock);
|
||||
return gm;
|
||||
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(gss_mech_get_by_OID);
|
||||
|
||||
static inline int
|
||||
mech_supports_pseudoflavor(struct gss_api_mech *gm, u32 pseudoflavor)
|
||||
{
|
||||
|
@ -193,6 +215,22 @@ gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
|
|||
|
||||
EXPORT_SYMBOL_GPL(gss_mech_get_by_pseudoflavor);
|
||||
|
||||
int gss_mech_list_pseudoflavors(rpc_authflavor_t *array_ptr)
|
||||
{
|
||||
struct gss_api_mech *pos = NULL;
|
||||
int i = 0;
|
||||
|
||||
spin_lock(®istered_mechs_lock);
|
||||
list_for_each_entry(pos, ®istered_mechs, gm_list) {
|
||||
array_ptr[i] = pos->gm_pfs->pseudoflavor;
|
||||
i++;
|
||||
}
|
||||
spin_unlock(®istered_mechs_lock);
|
||||
return i;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(gss_mech_list_pseudoflavors);
|
||||
|
||||
u32
|
||||
gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 service)
|
||||
{
|
||||
|
|
|
@ -710,6 +710,8 @@ static void xs_reset_transport(struct sock_xprt *transport)
|
|||
if (sk == NULL)
|
||||
return;
|
||||
|
||||
transport->srcport = 0;
|
||||
|
||||
write_lock_bh(&sk->sk_callback_lock);
|
||||
transport->inet = NULL;
|
||||
transport->sock = NULL;
|
||||
|
|
Загрузка…
Ссылка в новой задаче