NFS client bugfixes and cleanups for 3.10
- NLM: stable fix for NFSv2/v3 blocking locks - NFSv4.x: stable fixes for the delegation recall error handling code - NFSv4.x: Security flavour negotiation fixes and cleanups by Chuck Lever - SUNRPC: A number of RPCSEC_GSS fixes and cleanups also from Chuck - NFSv4.x assorted state management and reboot recovery bugfixes - NFSv4.1: In cases where we have already looked up a file, and hold a valid filehandle, use the new open-by-filehandle operation instead of opening by name. - Allow the NFSv4.1 callback thread to freeze - NFSv4.x: ensure that file unlock waits for readahead to complete - NFSv4.1: ensure that the RPC layer doesn't override the NFS session table size negotiation by limiting the number of slots. - NFSv4.x: Fix SETATTR spec compatibility issues -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.13 (GNU/Linux) iQIcBAABAgAGBQJRfu+cAAoJEGcL54qWCgDylxkP/24cXOLHMKMYnIab0cQYIW2m SQgADGE+MqgTVlWjGVWublVMDY1R51iINsksAjxMtXYt50FdBJEqV2uxIGi4VnbR nR9eppqQ6vk5e6r5+aZyVmWKoLFnJ4MBF6OpPUZB5mf8iH/fiixmSYLvseopPdDj bjHwCxg+xEgew5EhQF/xqkEfkAp2NN84xUksTWb9uDIW2c3SJweY/ZVR2Zsqpugm oqYVtrSLvNKqINQG8OP10s+mRWULwoqapF+kEHlxNbRy26C0zlbXPaneSgYzqHsY OyRkAT7uJJqStYlqdW7k+DhyNMB+T33WAGJpWQlfJGYk5d/n0rtBJDVo0hfhCSQr VkOXiO9J08NMbelCu4+0CJii7h5GCaqpuJEEmNL6AlF/TJVkIQJuRaG2+WDmEtO2 oYd4UfXlAbUuts1SW7u/yyN/yrjVTm1tZYRBqn2VJdqh1s8dMxEWPct2Yn314mpS ODAnbDkEhtWlc9cloSRnwKec5WcxMZb19IJeK9ZvHm7PfIu/QHtj6Ren8s1//bZI OMQxC/Vf/wcjMdNtr7QdMNxWG1aK8DL9mYP5XwCrkZ560LIrtxmhyqYeoGAfgO5u +K/gKmQwjsaPhEa8jbP2/wI0II9yKPWj/fVwqhbhqaBUx5GA2iAKcdpPP6JAMAti +PXkLTtkyrIgSNwzl63S =Hgot -----END PGP SIGNATURE----- Merge tag 'nfs-for-3.10-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs Pull NFS client bugfixes and cleanups from Trond Myklebust: - NLM: stable fix for NFSv2/v3 blocking locks - NFSv4.x: stable fixes for the delegation recall error handling code - NFSv4.x: Security flavour negotiation fixes and cleanups by Chuck Lever - SUNRPC: A number of RPCSEC_GSS fixes and cleanups also from Chuck - NFSv4.x assorted state management and reboot recovery bugfixes - NFSv4.1: In cases where we have already looked up a file, and hold a valid filehandle, use the new open-by-filehandle operation instead of opening by name. - Allow the NFSv4.1 callback thread to freeze - NFSv4.x: ensure that file unlock waits for readahead to complete - NFSv4.1: ensure that the RPC layer doesn't override the NFS session table size negotiation by limiting the number of slots. - NFSv4.x: Fix SETATTR spec compatibility issues * tag 'nfs-for-3.10-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: (67 commits) NFSv4: Warn once about servers that incorrectly apply open mode to setattr NFSv4: Servers should only check SETATTR stateid open mode on size change NFSv4: Don't recheck permissions on open in case of recovery cached open NFSv4.1: Don't do a delegated open for NFS4_OPEN_CLAIM_DELEG_CUR_FH modes NFSv4.1: Use the more efficient open_noattr call for open-by-filehandle NFS: Retry SETCLIENTID with AUTH_SYS instead of AUTH_NONE NFSv4: Ensure that we clear the NFS_OPEN_STATE flag when appropriate LOCKD: Ensure that nlmclnt_block resets block->b_status after a server reboot NFSv4: Ensure the LOCK call cannot use the delegation stateid NFSv4: Use the open stateid if the delegation has the wrong mode nfs: Send atime and mtime as a 64bit value NFSv4: Record the OPEN create mode used in the nfs4_opendata structure NFSv4.1: Set the RPC_CLNT_CREATE_INFINITE_SLOTS flag for NFSv4.1 transports SUNRPC: Allow rpc_create() to request that TCP slots be unlimited SUNRPC: Fix a livelock problem in the xprt->backlog queue NFSv4: Fix handling of revoked delegations by setattr NFSv4 release the sequence id in the return on close case nfs: remove unnecessary check for NULL inode->i_flock from nfs_delegation_claim_locks NFS: Ensure that NFS file unlock waits for readahead to complete NFS: Add functionality to allow waiting on all outstanding reads to complete ...
This commit is contained in:
commit
8728f986fe
|
@ -144,6 +144,9 @@ int nlmclnt_block(struct nlm_wait *block, struct nlm_rqst *req, long timeout)
|
|||
timeout);
|
||||
if (ret < 0)
|
||||
return -ERESTARTSYS;
|
||||
/* Reset the lock status after a server reboot so we resend */
|
||||
if (block->b_status == nlm_lck_denied_grace_period)
|
||||
block->b_status = nlm_lck_blocked;
|
||||
req->a_res.status = block->b_status;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -550,9 +550,6 @@ again:
|
|||
status = nlmclnt_block(block, req, NLMCLNT_POLL_TIMEOUT);
|
||||
if (status < 0)
|
||||
break;
|
||||
/* Resend the blocking lock request after a server reboot */
|
||||
if (resp->status == nlm_lck_denied_grace_period)
|
||||
continue;
|
||||
if (resp->status != nlm_lck_blocked)
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -125,6 +125,9 @@ nfs41_callback_svc(void *vrqstp)
|
|||
set_freezable();
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
if (try_to_freeze())
|
||||
continue;
|
||||
|
||||
prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE);
|
||||
spin_lock_bh(&serv->sv_cb_lock);
|
||||
if (!list_empty(&serv->sv_cb_list)) {
|
||||
|
|
|
@ -500,7 +500,7 @@ __be32 nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy,
|
|||
&args->craa_type_mask))
|
||||
pnfs_recall_all_layouts(cps->clp);
|
||||
if (flags)
|
||||
nfs_expire_all_delegation_types(cps->clp, flags);
|
||||
nfs_expire_unused_delegation_types(cps->clp, flags);
|
||||
out:
|
||||
dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
|
||||
return status;
|
||||
|
|
|
@ -593,6 +593,8 @@ int nfs_create_rpc_client(struct nfs_client *clp,
|
|||
args.flags |= RPC_CLNT_CREATE_DISCRTRY;
|
||||
if (test_bit(NFS_CS_NORESVPORT, &clp->cl_flags))
|
||||
args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
|
||||
if (test_bit(NFS_CS_INFINITE_SLOTS, &clp->cl_flags))
|
||||
args.flags |= RPC_CLNT_CREATE_INFINITE_SLOTS;
|
||||
|
||||
if (!IS_ERR(clp->cl_rpcclient))
|
||||
return 0;
|
||||
|
|
|
@ -64,17 +64,15 @@ int nfs4_have_delegation(struct inode *inode, fmode_t flags)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state)
|
||||
static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid)
|
||||
{
|
||||
struct inode *inode = state->inode;
|
||||
struct file_lock *fl;
|
||||
int status = 0;
|
||||
|
||||
if (inode->i_flock == NULL)
|
||||
return 0;
|
||||
|
||||
if (inode->i_flock == NULL)
|
||||
goto out;
|
||||
|
||||
/* Protect inode->i_flock using the file locks lock */
|
||||
lock_flocks();
|
||||
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
|
||||
|
@ -83,7 +81,7 @@ static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_
|
|||
if (nfs_file_open_context(fl->fl_file) != ctx)
|
||||
continue;
|
||||
unlock_flocks();
|
||||
status = nfs4_lock_delegation_recall(state, fl);
|
||||
status = nfs4_lock_delegation_recall(fl, state, stateid);
|
||||
if (status < 0)
|
||||
goto out;
|
||||
lock_flocks();
|
||||
|
@ -120,7 +118,7 @@ again:
|
|||
seq = raw_seqcount_begin(&sp->so_reclaim_seqcount);
|
||||
err = nfs4_open_delegation_recall(ctx, state, stateid);
|
||||
if (!err)
|
||||
err = nfs_delegation_claim_locks(ctx, state);
|
||||
err = nfs_delegation_claim_locks(ctx, state, stateid);
|
||||
if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq))
|
||||
err = -EAGAIN;
|
||||
mutex_unlock(&sp->so_delegreturn_mutex);
|
||||
|
@ -389,6 +387,24 @@ out:
|
|||
return err;
|
||||
}
|
||||
|
||||
static bool nfs_delegation_need_return(struct nfs_delegation *delegation)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags))
|
||||
ret = true;
|
||||
if (test_and_clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) && !ret) {
|
||||
struct inode *inode;
|
||||
|
||||
spin_lock(&delegation->lock);
|
||||
inode = delegation->inode;
|
||||
if (inode && list_empty(&NFS_I(inode)->open_files))
|
||||
ret = true;
|
||||
spin_unlock(&delegation->lock);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfs_client_return_marked_delegations - return previously marked delegations
|
||||
* @clp: nfs_client to process
|
||||
|
@ -411,8 +427,7 @@ restart:
|
|||
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
|
||||
list_for_each_entry_rcu(delegation, &server->delegations,
|
||||
super_list) {
|
||||
if (!test_and_clear_bit(NFS_DELEGATION_RETURN,
|
||||
&delegation->flags))
|
||||
if (!nfs_delegation_need_return(delegation))
|
||||
continue;
|
||||
inode = nfs_delegation_grab_inode(delegation);
|
||||
if (inode == NULL)
|
||||
|
@ -471,6 +486,13 @@ int nfs4_inode_return_delegation(struct inode *inode)
|
|||
return err;
|
||||
}
|
||||
|
||||
static void nfs_mark_return_if_closed_delegation(struct nfs_server *server,
|
||||
struct nfs_delegation *delegation)
|
||||
{
|
||||
set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
|
||||
set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
|
||||
}
|
||||
|
||||
static void nfs_mark_return_delegation(struct nfs_server *server,
|
||||
struct nfs_delegation *delegation)
|
||||
{
|
||||
|
@ -478,6 +500,45 @@ static void nfs_mark_return_delegation(struct nfs_server *server,
|
|||
set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
|
||||
}
|
||||
|
||||
static bool nfs_server_mark_return_all_delegations(struct nfs_server *server)
|
||||
{
|
||||
struct nfs_delegation *delegation;
|
||||
bool ret = false;
|
||||
|
||||
list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
|
||||
nfs_mark_return_delegation(server, delegation);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void nfs_client_mark_return_all_delegations(struct nfs_client *clp)
|
||||
{
|
||||
struct nfs_server *server;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
|
||||
nfs_server_mark_return_all_delegations(server);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static void nfs_delegation_run_state_manager(struct nfs_client *clp)
|
||||
{
|
||||
if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state))
|
||||
nfs4_schedule_state_manager(clp);
|
||||
}
|
||||
|
||||
/**
|
||||
* nfs_expire_all_delegations
|
||||
* @clp: client to process
|
||||
*
|
||||
*/
|
||||
void nfs_expire_all_delegations(struct nfs_client *clp)
|
||||
{
|
||||
nfs_client_mark_return_all_delegations(clp);
|
||||
nfs_delegation_run_state_manager(clp);
|
||||
}
|
||||
|
||||
/**
|
||||
* nfs_super_return_all_delegations - return delegations for one superblock
|
||||
* @sb: sb to process
|
||||
|
@ -486,24 +547,22 @@ static void nfs_mark_return_delegation(struct nfs_server *server,
|
|||
void nfs_server_return_all_delegations(struct nfs_server *server)
|
||||
{
|
||||
struct nfs_client *clp = server->nfs_client;
|
||||
struct nfs_delegation *delegation;
|
||||
bool need_wait;
|
||||
|
||||
if (clp == NULL)
|
||||
return;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
|
||||
spin_lock(&delegation->lock);
|
||||
set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
|
||||
spin_unlock(&delegation->lock);
|
||||
}
|
||||
need_wait = nfs_server_mark_return_all_delegations(server);
|
||||
rcu_read_unlock();
|
||||
|
||||
if (nfs_client_return_marked_delegations(clp) != 0)
|
||||
if (need_wait) {
|
||||
nfs4_schedule_state_manager(clp);
|
||||
nfs4_wait_clnt_recover(clp);
|
||||
}
|
||||
}
|
||||
|
||||
static void nfs_mark_return_all_delegation_types(struct nfs_server *server,
|
||||
static void nfs_mark_return_unused_delegation_types(struct nfs_server *server,
|
||||
fmode_t flags)
|
||||
{
|
||||
struct nfs_delegation *delegation;
|
||||
|
@ -512,27 +571,21 @@ static void nfs_mark_return_all_delegation_types(struct nfs_server *server,
|
|||
if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE))
|
||||
continue;
|
||||
if (delegation->type & flags)
|
||||
nfs_mark_return_delegation(server, delegation);
|
||||
nfs_mark_return_if_closed_delegation(server, delegation);
|
||||
}
|
||||
}
|
||||
|
||||
static void nfs_client_mark_return_all_delegation_types(struct nfs_client *clp,
|
||||
static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *clp,
|
||||
fmode_t flags)
|
||||
{
|
||||
struct nfs_server *server;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
|
||||
nfs_mark_return_all_delegation_types(server, flags);
|
||||
nfs_mark_return_unused_delegation_types(server, flags);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static void nfs_delegation_run_state_manager(struct nfs_client *clp)
|
||||
{
|
||||
if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state))
|
||||
nfs4_schedule_state_manager(clp);
|
||||
}
|
||||
|
||||
void nfs_remove_bad_delegation(struct inode *inode)
|
||||
{
|
||||
struct nfs_delegation *delegation;
|
||||
|
@ -546,27 +599,17 @@ void nfs_remove_bad_delegation(struct inode *inode)
|
|||
EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation);
|
||||
|
||||
/**
|
||||
* nfs_expire_all_delegation_types
|
||||
* nfs_expire_unused_delegation_types
|
||||
* @clp: client to process
|
||||
* @flags: delegation types to expire
|
||||
*
|
||||
*/
|
||||
void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags)
|
||||
void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags)
|
||||
{
|
||||
nfs_client_mark_return_all_delegation_types(clp, flags);
|
||||
nfs_client_mark_return_unused_delegation_types(clp, flags);
|
||||
nfs_delegation_run_state_manager(clp);
|
||||
}
|
||||
|
||||
/**
|
||||
* nfs_expire_all_delegations
|
||||
* @clp: client to process
|
||||
*
|
||||
*/
|
||||
void nfs_expire_all_delegations(struct nfs_client *clp)
|
||||
{
|
||||
nfs_expire_all_delegation_types(clp, FMODE_READ|FMODE_WRITE);
|
||||
}
|
||||
|
||||
static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server)
|
||||
{
|
||||
struct nfs_delegation *delegation;
|
||||
|
@ -574,7 +617,7 @@ static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server)
|
|||
list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
|
||||
if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))
|
||||
continue;
|
||||
nfs_mark_return_delegation(server, delegation);
|
||||
nfs_mark_return_if_closed_delegation(server, delegation);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ struct nfs_delegation {
|
|||
enum {
|
||||
NFS_DELEGATION_NEED_RECLAIM = 0,
|
||||
NFS_DELEGATION_RETURN,
|
||||
NFS_DELEGATION_RETURN_IF_CLOSED,
|
||||
NFS_DELEGATION_REFERENCED,
|
||||
NFS_DELEGATION_RETURNING,
|
||||
};
|
||||
|
@ -41,7 +42,7 @@ void nfs_inode_return_delegation_noreclaim(struct inode *inode);
|
|||
struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle);
|
||||
void nfs_server_return_all_delegations(struct nfs_server *);
|
||||
void nfs_expire_all_delegations(struct nfs_client *clp);
|
||||
void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags);
|
||||
void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags);
|
||||
void nfs_expire_unreferenced_delegations(struct nfs_client *clp);
|
||||
int nfs_client_return_marked_delegations(struct nfs_client *clp);
|
||||
int nfs_delegations_present(struct nfs_client *clp);
|
||||
|
@ -53,7 +54,7 @@ void nfs_delegation_reap_unclaimed(struct nfs_client *clp);
|
|||
/* NFSv4 delegation-related procedures */
|
||||
int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid, int issync);
|
||||
int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid);
|
||||
int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl);
|
||||
int nfs4_lock_delegation_recall(struct file_lock *fl, struct nfs4_state *state, const nfs4_stateid *stateid);
|
||||
bool nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode, fmode_t flags);
|
||||
|
||||
void nfs_mark_delegation_referenced(struct nfs_delegation *delegation);
|
||||
|
|
|
@ -1486,6 +1486,8 @@ static int nfs4_lookup_revalidate(struct dentry *dentry, unsigned int flags)
|
|||
goto no_open;
|
||||
if (d_mountpoint(dentry))
|
||||
goto no_open;
|
||||
if (NFS_SB(dentry->d_sb)->caps & NFS_CAP_ATOMIC_OPEN_V1)
|
||||
goto no_open;
|
||||
|
||||
inode = dentry->d_inode;
|
||||
parent = dget_parent(dentry);
|
||||
|
|
|
@ -744,6 +744,7 @@ static int
|
|||
do_unlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
|
||||
{
|
||||
struct inode *inode = filp->f_mapping->host;
|
||||
struct nfs_lock_context *l_ctx;
|
||||
int status;
|
||||
|
||||
/*
|
||||
|
@ -752,6 +753,14 @@ do_unlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
|
|||
*/
|
||||
nfs_sync_mapping(filp->f_mapping);
|
||||
|
||||
l_ctx = nfs_get_lock_context(nfs_file_open_context(filp));
|
||||
if (!IS_ERR(l_ctx)) {
|
||||
status = nfs_iocounter_wait(&l_ctx->io_count);
|
||||
nfs_put_lock_context(l_ctx);
|
||||
if (status < 0)
|
||||
return status;
|
||||
}
|
||||
|
||||
/* NOTE: special case
|
||||
* If we're signalled while cleaning up locks on process exit, we
|
||||
* still need to complete the unlock.
|
||||
|
|
|
@ -561,20 +561,22 @@ static void nfs_init_lock_context(struct nfs_lock_context *l_ctx)
|
|||
l_ctx->lockowner.l_owner = current->files;
|
||||
l_ctx->lockowner.l_pid = current->tgid;
|
||||
INIT_LIST_HEAD(&l_ctx->list);
|
||||
nfs_iocounter_init(&l_ctx->io_count);
|
||||
}
|
||||
|
||||
static struct nfs_lock_context *__nfs_find_lock_context(struct nfs_open_context *ctx)
|
||||
{
|
||||
struct nfs_lock_context *pos;
|
||||
struct nfs_lock_context *head = &ctx->lock_context;
|
||||
struct nfs_lock_context *pos = head;
|
||||
|
||||
list_for_each_entry(pos, &ctx->lock_context.list, list) {
|
||||
do {
|
||||
if (pos->lockowner.l_owner != current->files)
|
||||
continue;
|
||||
if (pos->lockowner.l_pid != current->tgid)
|
||||
continue;
|
||||
atomic_inc(&pos->count);
|
||||
return pos;
|
||||
}
|
||||
} while ((pos = list_entry(pos->list.next, typeof(*pos), list)) != head);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -229,6 +229,13 @@ extern void nfs_pgheader_init(struct nfs_pageio_descriptor *desc,
|
|||
struct nfs_pgio_header *hdr,
|
||||
void (*release)(struct nfs_pgio_header *hdr));
|
||||
void nfs_set_pgio_error(struct nfs_pgio_header *hdr, int error, loff_t pos);
|
||||
int nfs_iocounter_wait(struct nfs_io_counter *c);
|
||||
|
||||
static inline void nfs_iocounter_init(struct nfs_io_counter *c)
|
||||
{
|
||||
c->flags = 0;
|
||||
atomic_set(&c->io_count, 0);
|
||||
}
|
||||
|
||||
/* nfs2xdr.c */
|
||||
extern struct rpc_procinfo nfs_procedures[];
|
||||
|
|
|
@ -36,6 +36,7 @@ enum nfs4_client_state {
|
|||
|
||||
struct nfs4_minor_version_ops {
|
||||
u32 minor_version;
|
||||
unsigned init_caps;
|
||||
|
||||
int (*call_sync)(struct rpc_clnt *clnt,
|
||||
struct nfs_server *server,
|
||||
|
@ -143,12 +144,14 @@ struct nfs4_lock_state {
|
|||
enum {
|
||||
LK_STATE_IN_USE,
|
||||
NFS_DELEGATED_STATE, /* Current stateid is delegation */
|
||||
NFS_OPEN_STATE, /* OPEN stateid is set */
|
||||
NFS_O_RDONLY_STATE, /* OPEN stateid has read-only state */
|
||||
NFS_O_WRONLY_STATE, /* OPEN stateid has write-only state */
|
||||
NFS_O_RDWR_STATE, /* OPEN stateid has read/write state */
|
||||
NFS_STATE_RECLAIM_REBOOT, /* OPEN stateid server rebooted */
|
||||
NFS_STATE_RECLAIM_NOGRACE, /* OPEN stateid needs to recover state */
|
||||
NFS_STATE_POSIX_LOCKS, /* Posix locks are supported */
|
||||
NFS_STATE_RECOVERY_FAILED, /* OPEN stateid state recovery failed */
|
||||
};
|
||||
|
||||
struct nfs4_state {
|
||||
|
@ -233,6 +236,10 @@ extern struct rpc_clnt *nfs4_proc_lookup_mountpoint(struct inode *, struct qstr
|
|||
extern int nfs4_proc_secinfo(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *);
|
||||
extern int nfs4_release_lockowner(struct nfs4_lock_state *);
|
||||
extern const struct xattr_handler *nfs4_xattr_handlers[];
|
||||
extern int nfs4_set_rw_stateid(nfs4_stateid *stateid,
|
||||
const struct nfs_open_context *ctx,
|
||||
const struct nfs_lock_context *l_ctx,
|
||||
fmode_t fmode);
|
||||
|
||||
#if defined(CONFIG_NFS_V4_1)
|
||||
static inline struct nfs4_session *nfs4_get_session(const struct nfs_server *server)
|
||||
|
@ -347,13 +354,13 @@ extern int nfs4_wait_clnt_recover(struct nfs_client *clp);
|
|||
extern int nfs4_client_recover_expired_lease(struct nfs_client *clp);
|
||||
extern void nfs4_schedule_state_manager(struct nfs_client *);
|
||||
extern void nfs4_schedule_path_down_recovery(struct nfs_client *clp);
|
||||
extern void nfs4_schedule_stateid_recovery(const struct nfs_server *, struct nfs4_state *);
|
||||
extern int nfs4_schedule_stateid_recovery(const struct nfs_server *, struct nfs4_state *);
|
||||
extern void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags);
|
||||
extern void nfs41_handle_server_scope(struct nfs_client *,
|
||||
struct nfs41_server_scope **);
|
||||
extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp);
|
||||
extern int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl);
|
||||
extern void nfs4_select_rw_stateid(nfs4_stateid *, struct nfs4_state *,
|
||||
extern int nfs4_select_rw_stateid(nfs4_stateid *, struct nfs4_state *,
|
||||
fmode_t, const struct nfs_lockowner *);
|
||||
|
||||
extern struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter, gfp_t gfp_mask);
|
||||
|
@ -412,6 +419,11 @@ static inline bool nfs4_stateid_match(const nfs4_stateid *dst, const nfs4_statei
|
|||
return memcmp(dst, src, sizeof(*dst)) == 0;
|
||||
}
|
||||
|
||||
static inline bool nfs4_valid_open_stateid(const struct nfs4_state *state)
|
||||
{
|
||||
return test_bit(NFS_STATE_RECOVERY_FAILED, &state->flags) == 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define nfs4_close_state(a, b) do { } while (0)
|
||||
|
|
|
@ -198,8 +198,12 @@ struct nfs_client *nfs4_init_client(struct nfs_client *clp,
|
|||
/* Check NFS protocol revision and initialize RPC op vector */
|
||||
clp->rpc_ops = &nfs_v4_clientops;
|
||||
|
||||
if (clp->cl_minorversion != 0)
|
||||
__set_bit(NFS_CS_INFINITE_SLOTS, &clp->cl_flags);
|
||||
__set_bit(NFS_CS_DISCRTRY, &clp->cl_flags);
|
||||
error = nfs_create_rpc_client(clp, timeparms, authflavour);
|
||||
error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_GSS_KRB5I);
|
||||
if (error == -EINVAL)
|
||||
error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_NULL);
|
||||
if (error < 0)
|
||||
goto error;
|
||||
|
||||
|
@ -730,6 +734,19 @@ static int nfs4_server_common_setup(struct nfs_server *server,
|
|||
if (error < 0)
|
||||
goto out;
|
||||
|
||||
/* Set the basic capabilities */
|
||||
server->caps |= server->nfs_client->cl_mvops->init_caps;
|
||||
if (server->flags & NFS_MOUNT_NORDIRPLUS)
|
||||
server->caps &= ~NFS_CAP_READDIRPLUS;
|
||||
/*
|
||||
* Don't use NFS uid/gid mapping if we're using AUTH_SYS or lower
|
||||
* authentication.
|
||||
*/
|
||||
if (nfs4_disable_idmapping &&
|
||||
server->client->cl_auth->au_flavor == RPC_AUTH_UNIX)
|
||||
server->caps |= NFS_CAP_UIDGID_NOMAP;
|
||||
|
||||
|
||||
/* Probe the root fh to retrieve its FSID and filehandle */
|
||||
error = nfs4_get_rootfh(server, mntfh);
|
||||
if (error < 0)
|
||||
|
@ -773,9 +790,6 @@ static int nfs4_init_server(struct nfs_server *server,
|
|||
|
||||
/* Initialise the client representation from the mount data */
|
||||
server->flags = data->flags;
|
||||
server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR|NFS_CAP_POSIX_LOCK;
|
||||
if (!(data->flags & NFS_MOUNT_NORDIRPLUS))
|
||||
server->caps |= NFS_CAP_READDIRPLUS;
|
||||
server->options = data->options;
|
||||
|
||||
/* Get a client record */
|
||||
|
@ -792,13 +806,6 @@ static int nfs4_init_server(struct nfs_server *server,
|
|||
if (error < 0)
|
||||
goto error;
|
||||
|
||||
/*
|
||||
* Don't use NFS uid/gid mapping if we're using AUTH_SYS or lower
|
||||
* authentication.
|
||||
*/
|
||||
if (nfs4_disable_idmapping && data->auth_flavors[0] == RPC_AUTH_UNIX)
|
||||
server->caps |= NFS_CAP_UIDGID_NOMAP;
|
||||
|
||||
if (data->rsize)
|
||||
server->rsize = nfs_block_size(data->rsize, NULL);
|
||||
if (data->wsize)
|
||||
|
@ -876,7 +883,6 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
|
|||
|
||||
/* Initialise the client representation from the parent server */
|
||||
nfs_server_copy_userdata(server, parent_server);
|
||||
server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR;
|
||||
|
||||
/* Get a client representation.
|
||||
* Note: NFSv4 always uses TCP, */
|
||||
|
|
|
@ -158,11 +158,14 @@ static int filelayout_async_handle_error(struct rpc_task *task,
|
|||
case -NFS4ERR_OPENMODE:
|
||||
if (state == NULL)
|
||||
break;
|
||||
nfs4_schedule_stateid_recovery(mds_server, state);
|
||||
if (nfs4_schedule_stateid_recovery(mds_server, state) < 0)
|
||||
goto out_bad_stateid;
|
||||
goto wait_on_recovery;
|
||||
case -NFS4ERR_EXPIRED:
|
||||
if (state != NULL)
|
||||
nfs4_schedule_stateid_recovery(mds_server, state);
|
||||
if (state != NULL) {
|
||||
if (nfs4_schedule_stateid_recovery(mds_server, state) < 0)
|
||||
goto out_bad_stateid;
|
||||
}
|
||||
nfs4_schedule_lease_recovery(mds_client);
|
||||
goto wait_on_recovery;
|
||||
/* DS session errors */
|
||||
|
@ -226,6 +229,9 @@ reset:
|
|||
out:
|
||||
task->tk_status = 0;
|
||||
return -EAGAIN;
|
||||
out_bad_stateid:
|
||||
task->tk_status = -EIO;
|
||||
return 0;
|
||||
wait_on_recovery:
|
||||
rpc_sleep_on(&mds_client->cl_rpcwaitq, task, NULL);
|
||||
if (test_bit(NFS4CLNT_MANAGER_RUNNING, &mds_client->cl_state) == 0)
|
||||
|
@ -299,6 +305,10 @@ static void filelayout_read_prepare(struct rpc_task *task, void *data)
|
|||
{
|
||||
struct nfs_read_data *rdata = data;
|
||||
|
||||
if (unlikely(test_bit(NFS_CONTEXT_BAD, &rdata->args.context->flags))) {
|
||||
rpc_exit(task, -EIO);
|
||||
return;
|
||||
}
|
||||
if (filelayout_reset_to_mds(rdata->header->lseg)) {
|
||||
dprintk("%s task %u reset io to MDS\n", __func__, task->tk_pid);
|
||||
filelayout_reset_read(rdata);
|
||||
|
@ -307,10 +317,13 @@ static void filelayout_read_prepare(struct rpc_task *task, void *data)
|
|||
}
|
||||
rdata->read_done_cb = filelayout_read_done_cb;
|
||||
|
||||
nfs41_setup_sequence(rdata->ds_clp->cl_session,
|
||||
if (nfs41_setup_sequence(rdata->ds_clp->cl_session,
|
||||
&rdata->args.seq_args,
|
||||
&rdata->res.seq_res,
|
||||
task);
|
||||
task))
|
||||
return;
|
||||
nfs4_set_rw_stateid(&rdata->args.stateid, rdata->args.context,
|
||||
rdata->args.lock_context, FMODE_READ);
|
||||
}
|
||||
|
||||
static void filelayout_read_call_done(struct rpc_task *task, void *data)
|
||||
|
@ -401,16 +414,23 @@ static void filelayout_write_prepare(struct rpc_task *task, void *data)
|
|||
{
|
||||
struct nfs_write_data *wdata = data;
|
||||
|
||||
if (unlikely(test_bit(NFS_CONTEXT_BAD, &wdata->args.context->flags))) {
|
||||
rpc_exit(task, -EIO);
|
||||
return;
|
||||
}
|
||||
if (filelayout_reset_to_mds(wdata->header->lseg)) {
|
||||
dprintk("%s task %u reset io to MDS\n", __func__, task->tk_pid);
|
||||
filelayout_reset_write(wdata);
|
||||
rpc_exit(task, 0);
|
||||
return;
|
||||
}
|
||||
nfs41_setup_sequence(wdata->ds_clp->cl_session,
|
||||
if (nfs41_setup_sequence(wdata->ds_clp->cl_session,
|
||||
&wdata->args.seq_args,
|
||||
&wdata->res.seq_res,
|
||||
task);
|
||||
task))
|
||||
return;
|
||||
nfs4_set_rw_stateid(&wdata->args.stateid, wdata->args.context,
|
||||
wdata->args.lock_context, FMODE_WRITE);
|
||||
}
|
||||
|
||||
static void filelayout_write_call_done(struct rpc_task *task, void *data)
|
||||
|
|
|
@ -134,33 +134,38 @@ static size_t nfs_parse_server_name(char *string, size_t len,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfs_find_best_sec - Find a security mechanism supported locally
|
||||
* @flavors: List of security tuples returned by SECINFO procedure
|
||||
*
|
||||
* Return the pseudoflavor of the first security mechanism in
|
||||
* "flavors" that is locally supported. Return RPC_AUTH_UNIX if
|
||||
* no matching flavor is found in the array. The "flavors" array
|
||||
* is searched in the order returned from the server, per RFC 3530
|
||||
* recommendation.
|
||||
*/
|
||||
rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors)
|
||||
{
|
||||
struct gss_api_mech *mech;
|
||||
struct xdr_netobj oid;
|
||||
int i;
|
||||
rpc_authflavor_t pseudoflavor = RPC_AUTH_UNIX;
|
||||
rpc_authflavor_t pseudoflavor;
|
||||
struct nfs4_secinfo4 *secinfo;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < flavors->num_flavors; i++) {
|
||||
struct nfs4_secinfo_flavor *flavor;
|
||||
flavor = &flavors->flavors[i];
|
||||
secinfo = &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);
|
||||
switch (secinfo->flavor) {
|
||||
case RPC_AUTH_NULL:
|
||||
case RPC_AUTH_UNIX:
|
||||
case RPC_AUTH_GSS:
|
||||
pseudoflavor = rpcauth_get_pseudoflavor(secinfo->flavor,
|
||||
&secinfo->flavor_info);
|
||||
if (pseudoflavor != RPC_AUTH_MAXFLAVOR)
|
||||
return pseudoflavor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return pseudoflavor;
|
||||
return RPC_AUTH_UNIX;
|
||||
}
|
||||
|
||||
static rpc_authflavor_t nfs4_negotiate_security(struct inode *inode, struct qstr *name)
|
||||
|
|
|
@ -107,6 +107,8 @@ static int nfs4_map_errors(int err)
|
|||
return -EPROTONOSUPPORT;
|
||||
case -NFS4ERR_ACCESS:
|
||||
return -EACCES;
|
||||
case -NFS4ERR_FILE_OPEN:
|
||||
return -EBUSY;
|
||||
default:
|
||||
dprintk("%s could not handle NFSv4 error %d\n",
|
||||
__func__, -err);
|
||||
|
@ -295,19 +297,30 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc
|
|||
}
|
||||
if (state == NULL)
|
||||
break;
|
||||
nfs4_schedule_stateid_recovery(server, state);
|
||||
ret = nfs4_schedule_stateid_recovery(server, state);
|
||||
if (ret < 0)
|
||||
break;
|
||||
goto wait_on_recovery;
|
||||
case -NFS4ERR_DELEG_REVOKED:
|
||||
case -NFS4ERR_ADMIN_REVOKED:
|
||||
case -NFS4ERR_BAD_STATEID:
|
||||
if (inode != NULL && nfs4_have_delegation(inode, FMODE_READ)) {
|
||||
nfs_remove_bad_delegation(inode);
|
||||
exception->retry = 1;
|
||||
break;
|
||||
}
|
||||
if (state == NULL)
|
||||
break;
|
||||
nfs_remove_bad_delegation(state->inode);
|
||||
nfs4_schedule_stateid_recovery(server, state);
|
||||
ret = nfs4_schedule_stateid_recovery(server, state);
|
||||
if (ret < 0)
|
||||
break;
|
||||
goto wait_on_recovery;
|
||||
case -NFS4ERR_EXPIRED:
|
||||
if (state != NULL)
|
||||
nfs4_schedule_stateid_recovery(server, state);
|
||||
if (state != NULL) {
|
||||
ret = nfs4_schedule_stateid_recovery(server, state);
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
case -NFS4ERR_STALE_STATEID:
|
||||
case -NFS4ERR_STALE_CLIENTID:
|
||||
nfs4_schedule_lease_recovery(clp);
|
||||
|
@ -756,10 +769,40 @@ struct nfs4_opendata {
|
|||
struct iattr attrs;
|
||||
unsigned long timestamp;
|
||||
unsigned int rpc_done : 1;
|
||||
unsigned int is_recover : 1;
|
||||
int rpc_status;
|
||||
int cancelled;
|
||||
};
|
||||
|
||||
static bool nfs4_clear_cap_atomic_open_v1(struct nfs_server *server,
|
||||
int err, struct nfs4_exception *exception)
|
||||
{
|
||||
if (err != -EINVAL)
|
||||
return false;
|
||||
if (!(server->caps & NFS_CAP_ATOMIC_OPEN_V1))
|
||||
return false;
|
||||
server->caps &= ~NFS_CAP_ATOMIC_OPEN_V1;
|
||||
exception->retry = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
static enum open_claim_type4
|
||||
nfs4_map_atomic_open_claim(struct nfs_server *server,
|
||||
enum open_claim_type4 claim)
|
||||
{
|
||||
if (server->caps & NFS_CAP_ATOMIC_OPEN_V1)
|
||||
return claim;
|
||||
switch (claim) {
|
||||
default:
|
||||
return claim;
|
||||
case NFS4_OPEN_CLAIM_FH:
|
||||
return NFS4_OPEN_CLAIM_NULL;
|
||||
case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
|
||||
return NFS4_OPEN_CLAIM_DELEGATE_CUR;
|
||||
case NFS4_OPEN_CLAIM_DELEG_PREV_FH:
|
||||
return NFS4_OPEN_CLAIM_DELEGATE_PREV;
|
||||
}
|
||||
}
|
||||
|
||||
static void nfs4_init_opendata_res(struct nfs4_opendata *p)
|
||||
{
|
||||
|
@ -775,6 +818,7 @@ static void nfs4_init_opendata_res(struct nfs4_opendata *p)
|
|||
static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
|
||||
struct nfs4_state_owner *sp, fmode_t fmode, int flags,
|
||||
const struct iattr *attrs,
|
||||
enum open_claim_type4 claim,
|
||||
gfp_t gfp_mask)
|
||||
{
|
||||
struct dentry *parent = dget_parent(dentry);
|
||||
|
@ -793,7 +837,6 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
|
|||
p->dir = parent;
|
||||
p->owner = sp;
|
||||
atomic_inc(&sp->so_count);
|
||||
p->o_arg.fh = NFS_FH(dir);
|
||||
p->o_arg.open_flags = flags;
|
||||
p->o_arg.fmode = fmode & (FMODE_READ|FMODE_WRITE);
|
||||
/* don't put an ACCESS op in OPEN compound if O_EXCL, because ACCESS
|
||||
|
@ -811,7 +854,19 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
|
|||
p->o_arg.server = server;
|
||||
p->o_arg.bitmask = server->attr_bitmask;
|
||||
p->o_arg.open_bitmap = &nfs4_fattr_bitmap[0];
|
||||
p->o_arg.claim = NFS4_OPEN_CLAIM_NULL;
|
||||
p->o_arg.claim = nfs4_map_atomic_open_claim(server, claim);
|
||||
switch (p->o_arg.claim) {
|
||||
case NFS4_OPEN_CLAIM_NULL:
|
||||
case NFS4_OPEN_CLAIM_DELEGATE_CUR:
|
||||
case NFS4_OPEN_CLAIM_DELEGATE_PREV:
|
||||
p->o_arg.fh = NFS_FH(dir);
|
||||
break;
|
||||
case NFS4_OPEN_CLAIM_PREVIOUS:
|
||||
case NFS4_OPEN_CLAIM_FH:
|
||||
case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
|
||||
case NFS4_OPEN_CLAIM_DELEG_PREV_FH:
|
||||
p->o_arg.fh = NFS_FH(dentry->d_inode);
|
||||
}
|
||||
if (attrs != NULL && attrs->ia_valid != 0) {
|
||||
__be32 verf[2];
|
||||
|
||||
|
@ -924,6 +979,7 @@ static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *
|
|||
if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
|
||||
nfs4_stateid_copy(&state->stateid, stateid);
|
||||
nfs4_stateid_copy(&state->open_stateid, stateid);
|
||||
set_bit(NFS_OPEN_STATE, &state->flags);
|
||||
switch (fmode) {
|
||||
case FMODE_READ:
|
||||
set_bit(NFS_O_RDONLY_STATE, &state->flags);
|
||||
|
@ -1047,9 +1103,11 @@ static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
|
|||
nfs4_stateid_copy(&stateid, &delegation->stateid);
|
||||
rcu_read_unlock();
|
||||
nfs_release_seqid(opendata->o_arg.seqid);
|
||||
ret = nfs_may_open(state->inode, state->owner->so_cred, open_mode);
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
if (!opendata->is_recover) {
|
||||
ret = nfs_may_open(state->inode, state->owner->so_cred, open_mode);
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
}
|
||||
ret = -EAGAIN;
|
||||
|
||||
/* Try to update the stateid using the delegation */
|
||||
|
@ -1194,11 +1252,13 @@ static struct nfs_open_context *nfs4_state_find_open_context(struct nfs4_state *
|
|||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
|
||||
static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context *ctx, struct nfs4_state *state)
|
||||
static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context *ctx,
|
||||
struct nfs4_state *state, enum open_claim_type4 claim)
|
||||
{
|
||||
struct nfs4_opendata *opendata;
|
||||
|
||||
opendata = nfs4_opendata_alloc(ctx->dentry, state->owner, 0, 0, NULL, GFP_NOFS);
|
||||
opendata = nfs4_opendata_alloc(ctx->dentry, state->owner, 0, 0,
|
||||
NULL, claim, GFP_NOFS);
|
||||
if (opendata == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
opendata->state = state;
|
||||
|
@ -1234,6 +1294,7 @@ static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *
|
|||
|
||||
/* memory barrier prior to reading state->n_* */
|
||||
clear_bit(NFS_DELEGATED_STATE, &state->flags);
|
||||
clear_bit(NFS_OPEN_STATE, &state->flags);
|
||||
smp_rmb();
|
||||
if (state->n_rdwr != 0) {
|
||||
clear_bit(NFS_O_RDWR_STATE, &state->flags);
|
||||
|
@ -1284,11 +1345,10 @@ static int _nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state
|
|||
fmode_t delegation_type = 0;
|
||||
int status;
|
||||
|
||||
opendata = nfs4_open_recoverdata_alloc(ctx, state);
|
||||
opendata = nfs4_open_recoverdata_alloc(ctx, state,
|
||||
NFS4_OPEN_CLAIM_PREVIOUS);
|
||||
if (IS_ERR(opendata))
|
||||
return PTR_ERR(opendata);
|
||||
opendata->o_arg.claim = NFS4_OPEN_CLAIM_PREVIOUS;
|
||||
opendata->o_arg.fh = NFS_FH(state->inode);
|
||||
rcu_read_lock();
|
||||
delegation = rcu_dereference(NFS_I(state->inode)->delegation);
|
||||
if (delegation != NULL && test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) != 0)
|
||||
|
@ -1307,6 +1367,8 @@ static int nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state
|
|||
int err;
|
||||
do {
|
||||
err = _nfs4_do_open_reclaim(ctx, state);
|
||||
if (nfs4_clear_cap_atomic_open_v1(server, err, &exception))
|
||||
continue;
|
||||
if (err != -NFS4ERR_DELAY)
|
||||
break;
|
||||
nfs4_handle_exception(server, err, &exception);
|
||||
|
@ -1321,71 +1383,72 @@ static int nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *sta
|
|||
|
||||
ctx = nfs4_state_find_open_context(state);
|
||||
if (IS_ERR(ctx))
|
||||
return PTR_ERR(ctx);
|
||||
return -EAGAIN;
|
||||
ret = nfs4_do_open_reclaim(ctx, state);
|
||||
put_nfs_open_context(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid)
|
||||
static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct nfs4_state *state, const nfs4_stateid *stateid, int err)
|
||||
{
|
||||
struct nfs4_opendata *opendata;
|
||||
int ret;
|
||||
|
||||
opendata = nfs4_open_recoverdata_alloc(ctx, state);
|
||||
if (IS_ERR(opendata))
|
||||
return PTR_ERR(opendata);
|
||||
opendata->o_arg.claim = NFS4_OPEN_CLAIM_DELEGATE_CUR;
|
||||
nfs4_stateid_copy(&opendata->o_arg.u.delegation, stateid);
|
||||
ret = nfs4_open_recover(opendata, state);
|
||||
nfs4_opendata_put(opendata);
|
||||
return ret;
|
||||
switch (err) {
|
||||
default:
|
||||
printk(KERN_ERR "NFS: %s: unhandled error "
|
||||
"%d.\n", __func__, err);
|
||||
case 0:
|
||||
case -ENOENT:
|
||||
case -ESTALE:
|
||||
break;
|
||||
case -NFS4ERR_BADSESSION:
|
||||
case -NFS4ERR_BADSLOT:
|
||||
case -NFS4ERR_BAD_HIGH_SLOT:
|
||||
case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
|
||||
case -NFS4ERR_DEADSESSION:
|
||||
set_bit(NFS_DELEGATED_STATE, &state->flags);
|
||||
nfs4_schedule_session_recovery(server->nfs_client->cl_session, err);
|
||||
return -EAGAIN;
|
||||
case -NFS4ERR_STALE_CLIENTID:
|
||||
case -NFS4ERR_STALE_STATEID:
|
||||
set_bit(NFS_DELEGATED_STATE, &state->flags);
|
||||
case -NFS4ERR_EXPIRED:
|
||||
/* Don't recall a delegation if it was lost */
|
||||
nfs4_schedule_lease_recovery(server->nfs_client);
|
||||
return -EAGAIN;
|
||||
case -NFS4ERR_DELEG_REVOKED:
|
||||
case -NFS4ERR_ADMIN_REVOKED:
|
||||
case -NFS4ERR_BAD_STATEID:
|
||||
case -NFS4ERR_OPENMODE:
|
||||
nfs_inode_find_state_and_recover(state->inode,
|
||||
stateid);
|
||||
nfs4_schedule_stateid_recovery(server, state);
|
||||
return 0;
|
||||
case -NFS4ERR_DELAY:
|
||||
case -NFS4ERR_GRACE:
|
||||
set_bit(NFS_DELEGATED_STATE, &state->flags);
|
||||
ssleep(1);
|
||||
return -EAGAIN;
|
||||
case -ENOMEM:
|
||||
case -NFS4ERR_DENIED:
|
||||
/* kill_proc(fl->fl_pid, SIGLOST, 1); */
|
||||
return 0;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid)
|
||||
{
|
||||
struct nfs4_exception exception = { };
|
||||
struct nfs_server *server = NFS_SERVER(state->inode);
|
||||
struct nfs4_opendata *opendata;
|
||||
int err;
|
||||
do {
|
||||
err = _nfs4_open_delegation_recall(ctx, state, stateid);
|
||||
switch (err) {
|
||||
case 0:
|
||||
case -ENOENT:
|
||||
case -ESTALE:
|
||||
goto out;
|
||||
case -NFS4ERR_BADSESSION:
|
||||
case -NFS4ERR_BADSLOT:
|
||||
case -NFS4ERR_BAD_HIGH_SLOT:
|
||||
case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
|
||||
case -NFS4ERR_DEADSESSION:
|
||||
set_bit(NFS_DELEGATED_STATE, &state->flags);
|
||||
nfs4_schedule_session_recovery(server->nfs_client->cl_session, err);
|
||||
err = -EAGAIN;
|
||||
goto out;
|
||||
case -NFS4ERR_STALE_CLIENTID:
|
||||
case -NFS4ERR_STALE_STATEID:
|
||||
set_bit(NFS_DELEGATED_STATE, &state->flags);
|
||||
case -NFS4ERR_EXPIRED:
|
||||
/* Don't recall a delegation if it was lost */
|
||||
nfs4_schedule_lease_recovery(server->nfs_client);
|
||||
err = -EAGAIN;
|
||||
goto out;
|
||||
case -NFS4ERR_DELEG_REVOKED:
|
||||
case -NFS4ERR_ADMIN_REVOKED:
|
||||
case -NFS4ERR_BAD_STATEID:
|
||||
nfs_inode_find_state_and_recover(state->inode,
|
||||
stateid);
|
||||
nfs4_schedule_stateid_recovery(server, state);
|
||||
case -ENOMEM:
|
||||
err = 0;
|
||||
goto out;
|
||||
}
|
||||
set_bit(NFS_DELEGATED_STATE, &state->flags);
|
||||
err = nfs4_handle_exception(server, err, &exception);
|
||||
} while (exception.retry);
|
||||
out:
|
||||
return err;
|
||||
|
||||
opendata = nfs4_open_recoverdata_alloc(ctx, state,
|
||||
NFS4_OPEN_CLAIM_DELEG_CUR_FH);
|
||||
if (IS_ERR(opendata))
|
||||
return PTR_ERR(opendata);
|
||||
nfs4_stateid_copy(&opendata->o_arg.u.delegation, stateid);
|
||||
err = nfs4_open_recover(opendata, state);
|
||||
nfs4_opendata_put(opendata);
|
||||
return nfs4_handle_delegation_recall_error(server, state, stateid, err);
|
||||
}
|
||||
|
||||
static void nfs4_open_confirm_done(struct rpc_task *task, void *calldata)
|
||||
|
@ -1468,6 +1531,7 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
|
|||
{
|
||||
struct nfs4_opendata *data = calldata;
|
||||
struct nfs4_state_owner *sp = data->owner;
|
||||
struct nfs_client *clp = sp->so_server->nfs_client;
|
||||
|
||||
if (nfs_wait_on_sequence(data->o_arg.seqid, task) != 0)
|
||||
goto out_wait;
|
||||
|
@ -1483,15 +1547,20 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
|
|||
rcu_read_lock();
|
||||
delegation = rcu_dereference(NFS_I(data->state->inode)->delegation);
|
||||
if (data->o_arg.claim != NFS4_OPEN_CLAIM_DELEGATE_CUR &&
|
||||
data->o_arg.claim != NFS4_OPEN_CLAIM_DELEG_CUR_FH &&
|
||||
can_open_delegated(delegation, data->o_arg.fmode))
|
||||
goto unlock_no_action;
|
||||
rcu_read_unlock();
|
||||
}
|
||||
/* Update client id. */
|
||||
data->o_arg.clientid = sp->so_server->nfs_client->cl_clientid;
|
||||
if (data->o_arg.claim == NFS4_OPEN_CLAIM_PREVIOUS) {
|
||||
task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR];
|
||||
data->o_arg.clientid = clp->cl_clientid;
|
||||
switch (data->o_arg.claim) {
|
||||
case NFS4_OPEN_CLAIM_PREVIOUS:
|
||||
case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
|
||||
case NFS4_OPEN_CLAIM_DELEG_PREV_FH:
|
||||
data->o_arg.open_bitmap = &nfs4_open_noattr_bitmap[0];
|
||||
case NFS4_OPEN_CLAIM_FH:
|
||||
task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR];
|
||||
nfs_copy_fh(&data->o_res.fh, data->o_arg.fh);
|
||||
}
|
||||
data->timestamp = jiffies;
|
||||
|
@ -1500,6 +1569,16 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
|
|||
&data->o_res.seq_res,
|
||||
task) != 0)
|
||||
nfs_release_seqid(data->o_arg.seqid);
|
||||
|
||||
/* Set the create mode (note dependency on the session type) */
|
||||
data->o_arg.createmode = NFS4_CREATE_UNCHECKED;
|
||||
if (data->o_arg.open_flags & O_EXCL) {
|
||||
data->o_arg.createmode = NFS4_CREATE_EXCLUSIVE;
|
||||
if (nfs4_has_persistent_session(clp))
|
||||
data->o_arg.createmode = NFS4_CREATE_GUARDED;
|
||||
else if (clp->cl_mvops->minor_version > 0)
|
||||
data->o_arg.createmode = NFS4_CREATE_EXCLUSIVE4_1;
|
||||
}
|
||||
return;
|
||||
unlock_no_action:
|
||||
rcu_read_unlock();
|
||||
|
@ -1595,8 +1674,11 @@ static int nfs4_run_open_task(struct nfs4_opendata *data, int isrecover)
|
|||
data->rpc_done = 0;
|
||||
data->rpc_status = 0;
|
||||
data->cancelled = 0;
|
||||
if (isrecover)
|
||||
data->is_recover = 0;
|
||||
if (isrecover) {
|
||||
nfs4_set_sequence_privileged(&o_arg->seq_args);
|
||||
data->is_recover = 1;
|
||||
}
|
||||
task = rpc_run_task(&task_setup_data);
|
||||
if (IS_ERR(task))
|
||||
return PTR_ERR(task);
|
||||
|
@ -1721,7 +1803,8 @@ static int _nfs4_open_expired(struct nfs_open_context *ctx, struct nfs4_state *s
|
|||
struct nfs4_opendata *opendata;
|
||||
int ret;
|
||||
|
||||
opendata = nfs4_open_recoverdata_alloc(ctx, state);
|
||||
opendata = nfs4_open_recoverdata_alloc(ctx, state,
|
||||
NFS4_OPEN_CLAIM_FH);
|
||||
if (IS_ERR(opendata))
|
||||
return PTR_ERR(opendata);
|
||||
ret = nfs4_open_recover(opendata, state);
|
||||
|
@ -1739,6 +1822,8 @@ static int nfs4_do_open_expired(struct nfs_open_context *ctx, struct nfs4_state
|
|||
|
||||
do {
|
||||
err = _nfs4_open_expired(ctx, state);
|
||||
if (nfs4_clear_cap_atomic_open_v1(server, err, &exception))
|
||||
continue;
|
||||
switch (err) {
|
||||
default:
|
||||
goto out;
|
||||
|
@ -1759,7 +1844,7 @@ static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *sta
|
|||
|
||||
ctx = nfs4_state_find_open_context(state);
|
||||
if (IS_ERR(ctx))
|
||||
return PTR_ERR(ctx);
|
||||
return -EAGAIN;
|
||||
ret = nfs4_do_open_expired(ctx, state);
|
||||
put_nfs_open_context(ctx);
|
||||
return ret;
|
||||
|
@ -1821,6 +1906,7 @@ static int nfs41_check_open_stateid(struct nfs4_state *state)
|
|||
clear_bit(NFS_O_RDONLY_STATE, &state->flags);
|
||||
clear_bit(NFS_O_WRONLY_STATE, &state->flags);
|
||||
clear_bit(NFS_O_RDWR_STATE, &state->flags);
|
||||
clear_bit(NFS_OPEN_STATE, &state->flags);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
@ -1881,10 +1967,8 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata,
|
|||
if (ret != 0)
|
||||
goto out;
|
||||
|
||||
if (read_seqcount_retry(&sp->so_reclaim_seqcount, seq)) {
|
||||
if (read_seqcount_retry(&sp->so_reclaim_seqcount, seq))
|
||||
nfs4_schedule_stateid_recovery(server, state);
|
||||
nfs4_wait_clnt_recover(server->nfs_client);
|
||||
}
|
||||
*res = state;
|
||||
out:
|
||||
return ret;
|
||||
|
@ -1906,6 +1990,7 @@ static int _nfs4_do_open(struct inode *dir,
|
|||
struct nfs4_state *state = NULL;
|
||||
struct nfs_server *server = NFS_SERVER(dir);
|
||||
struct nfs4_opendata *opendata;
|
||||
enum open_claim_type4 claim = NFS4_OPEN_CLAIM_NULL;
|
||||
int status;
|
||||
|
||||
/* Protect against reboot recovery conflicts */
|
||||
|
@ -1921,7 +2006,10 @@ static int _nfs4_do_open(struct inode *dir,
|
|||
if (dentry->d_inode != NULL)
|
||||
nfs4_return_incompatible_delegation(dentry->d_inode, fmode);
|
||||
status = -ENOMEM;
|
||||
opendata = nfs4_opendata_alloc(dentry, sp, fmode, flags, sattr, GFP_KERNEL);
|
||||
if (dentry->d_inode)
|
||||
claim = NFS4_OPEN_CLAIM_FH;
|
||||
opendata = nfs4_opendata_alloc(dentry, sp, fmode, flags, sattr,
|
||||
claim, GFP_KERNEL);
|
||||
if (opendata == NULL)
|
||||
goto err_put_state_owner;
|
||||
|
||||
|
@ -1938,7 +2026,8 @@ static int _nfs4_do_open(struct inode *dir,
|
|||
if (status != 0)
|
||||
goto err_opendata_put;
|
||||
|
||||
if (opendata->o_arg.open_flags & O_EXCL) {
|
||||
if ((opendata->o_arg.open_flags & O_EXCL) &&
|
||||
(opendata->o_arg.createmode != NFS4_CREATE_GUARDED)) {
|
||||
nfs4_exclusive_attrset(opendata, sattr);
|
||||
|
||||
nfs_fattr_init(opendata->o_res.f_attr);
|
||||
|
@ -1979,6 +2068,7 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir,
|
|||
struct rpc_cred *cred,
|
||||
struct nfs4_threshold **ctx_th)
|
||||
{
|
||||
struct nfs_server *server = NFS_SERVER(dir);
|
||||
struct nfs4_exception exception = { };
|
||||
struct nfs4_state *res;
|
||||
int status;
|
||||
|
@ -2022,7 +2112,9 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir,
|
|||
exception.retry = 1;
|
||||
continue;
|
||||
}
|
||||
res = ERR_PTR(nfs4_handle_exception(NFS_SERVER(dir),
|
||||
if (nfs4_clear_cap_atomic_open_v1(server, status, &exception))
|
||||
continue;
|
||||
res = ERR_PTR(nfs4_handle_exception(server,
|
||||
status, &exception));
|
||||
} while (exception.retry);
|
||||
return res;
|
||||
|
@ -2050,20 +2142,25 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
|
|||
.rpc_cred = cred,
|
||||
};
|
||||
unsigned long timestamp = jiffies;
|
||||
fmode_t fmode;
|
||||
bool truncate;
|
||||
int status;
|
||||
|
||||
nfs_fattr_init(fattr);
|
||||
|
||||
if (state != NULL) {
|
||||
/* Servers should only apply open mode checks for file size changes */
|
||||
truncate = (sattr->ia_valid & ATTR_SIZE) ? true : false;
|
||||
fmode = truncate ? FMODE_WRITE : FMODE_READ;
|
||||
|
||||
if (nfs4_copy_delegation_stateid(&arg.stateid, inode, fmode)) {
|
||||
/* Use that stateid */
|
||||
} else if (truncate && state != NULL && nfs4_valid_open_stateid(state)) {
|
||||
struct nfs_lockowner lockowner = {
|
||||
.l_owner = current->files,
|
||||
.l_pid = current->tgid,
|
||||
};
|
||||
nfs4_select_rw_stateid(&arg.stateid, state, FMODE_WRITE,
|
||||
&lockowner);
|
||||
} else if (nfs4_copy_delegation_stateid(&arg.stateid, inode,
|
||||
FMODE_WRITE)) {
|
||||
/* Use that stateid */
|
||||
} else
|
||||
nfs4_stateid_copy(&arg.stateid, &zero_stateid);
|
||||
|
||||
|
@ -2087,6 +2184,13 @@ static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
|
|||
err = _nfs4_do_setattr(inode, cred, fattr, sattr, state);
|
||||
switch (err) {
|
||||
case -NFS4ERR_OPENMODE:
|
||||
if (!(sattr->ia_valid & ATTR_SIZE)) {
|
||||
pr_warn_once("NFSv4: server %s is incorrectly "
|
||||
"applying open mode checks to "
|
||||
"a SETATTR that is not "
|
||||
"changing file size.\n",
|
||||
server->nfs_client->cl_hostname);
|
||||
}
|
||||
if (state && !(state->state & FMODE_WRITE)) {
|
||||
err = -EBADF;
|
||||
if (sattr->ia_valid & ATTR_OPEN)
|
||||
|
@ -2130,11 +2234,19 @@ static void nfs4_close_clear_stateid_flags(struct nfs4_state *state,
|
|||
fmode_t fmode)
|
||||
{
|
||||
spin_lock(&state->owner->so_lock);
|
||||
if (!(fmode & FMODE_READ))
|
||||
clear_bit(NFS_O_RDONLY_STATE, &state->flags);
|
||||
if (!(fmode & FMODE_WRITE))
|
||||
clear_bit(NFS_O_WRONLY_STATE, &state->flags);
|
||||
clear_bit(NFS_O_RDWR_STATE, &state->flags);
|
||||
switch (fmode & (FMODE_READ|FMODE_WRITE)) {
|
||||
case FMODE_WRITE:
|
||||
clear_bit(NFS_O_RDONLY_STATE, &state->flags);
|
||||
break;
|
||||
case FMODE_READ:
|
||||
clear_bit(NFS_O_WRONLY_STATE, &state->flags);
|
||||
break;
|
||||
case 0:
|
||||
clear_bit(NFS_O_RDONLY_STATE, &state->flags);
|
||||
clear_bit(NFS_O_WRONLY_STATE, &state->flags);
|
||||
clear_bit(NFS_OPEN_STATE, &state->flags);
|
||||
}
|
||||
spin_unlock(&state->owner->so_lock);
|
||||
}
|
||||
|
||||
|
@ -2202,6 +2314,8 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
|
|||
calldata->arg.fmode &= ~FMODE_WRITE;
|
||||
}
|
||||
}
|
||||
if (!nfs4_valid_open_stateid(state))
|
||||
call_close = 0;
|
||||
spin_unlock(&state->owner->so_lock);
|
||||
|
||||
if (!call_close) {
|
||||
|
@ -2212,8 +2326,10 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
|
|||
if (calldata->arg.fmode == 0) {
|
||||
task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE];
|
||||
if (calldata->roc &&
|
||||
pnfs_roc_drain(inode, &calldata->roc_barrier, task))
|
||||
pnfs_roc_drain(inode, &calldata->roc_barrier, task)) {
|
||||
nfs_release_seqid(calldata->arg.seqid);
|
||||
goto out_wait;
|
||||
}
|
||||
}
|
||||
|
||||
nfs_fattr_init(calldata->res.fattr);
|
||||
|
@ -2444,7 +2560,7 @@ static int nfs4_lookup_root_sec(struct nfs_server *server, struct nfs_fh *fhandl
|
|||
|
||||
auth = rpcauth_create(flavor, server->client);
|
||||
if (IS_ERR(auth)) {
|
||||
ret = -EIO;
|
||||
ret = -EACCES;
|
||||
goto out;
|
||||
}
|
||||
ret = nfs4_lookup_root(server, fhandle, info);
|
||||
|
@ -2452,27 +2568,36 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Retry pseudoroot lookup with various security flavors. We do this when:
|
||||
*
|
||||
* NFSv4.0: the PUTROOTFH operation returns NFS4ERR_WRONGSEC
|
||||
* NFSv4.1: the server does not support the SECINFO_NO_NAME operation
|
||||
*
|
||||
* Returns zero on success, or a negative NFS4ERR value, or a
|
||||
* negative errno value.
|
||||
*/
|
||||
static int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
|
||||
struct nfs_fsinfo *info)
|
||||
{
|
||||
int i, len, status = 0;
|
||||
rpc_authflavor_t flav_array[NFS_MAX_SECFLAVORS];
|
||||
|
||||
len = rpcauth_list_flavors(flav_array, ARRAY_SIZE(flav_array));
|
||||
if (len < 0)
|
||||
return len;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
/* AUTH_UNIX is the default flavor if none was specified,
|
||||
* thus has already been tried. */
|
||||
if (flav_array[i] == RPC_AUTH_UNIX)
|
||||
continue;
|
||||
/* Per 3530bis 15.33.5 */
|
||||
static const rpc_authflavor_t flav_array[] = {
|
||||
RPC_AUTH_GSS_KRB5P,
|
||||
RPC_AUTH_GSS_KRB5I,
|
||||
RPC_AUTH_GSS_KRB5,
|
||||
RPC_AUTH_UNIX, /* courtesy */
|
||||
RPC_AUTH_NULL,
|
||||
};
|
||||
int status = -EPERM;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(flav_array); i++) {
|
||||
status = nfs4_lookup_root_sec(server, fhandle, info, flav_array[i]);
|
||||
if (status == -NFS4ERR_WRONGSEC || status == -EACCES)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* -EACCESS could mean that the user doesn't have correct permissions
|
||||
* to access the mount. It could also mean that we tried to mount
|
||||
|
@ -2485,24 +2610,36 @@ static int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
|
|||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* get the file handle for the "/" directory on the server
|
||||
static int nfs4_do_find_root_sec(struct nfs_server *server,
|
||||
struct nfs_fh *fhandle, struct nfs_fsinfo *info)
|
||||
{
|
||||
int mv = server->nfs_client->cl_minorversion;
|
||||
return nfs_v4_minor_ops[mv]->find_root_sec(server, fhandle, info);
|
||||
}
|
||||
|
||||
/**
|
||||
* nfs4_proc_get_rootfh - get file handle for server's pseudoroot
|
||||
* @server: initialized nfs_server handle
|
||||
* @fhandle: we fill in the pseudo-fs root file handle
|
||||
* @info: we fill in an FSINFO struct
|
||||
*
|
||||
* Returns zero on success, or a negative errno.
|
||||
*/
|
||||
int nfs4_proc_get_rootfh(struct nfs_server *server, struct nfs_fh *fhandle,
|
||||
struct nfs_fsinfo *info)
|
||||
{
|
||||
int minor_version = server->nfs_client->cl_minorversion;
|
||||
int status = nfs4_lookup_root(server, fhandle, info);
|
||||
if ((status == -NFS4ERR_WRONGSEC) && !(server->flags & NFS_MOUNT_SECFLAVOUR))
|
||||
/*
|
||||
* A status of -NFS4ERR_WRONGSEC will be mapped to -EPERM
|
||||
* by nfs4_map_errors() as this function exits.
|
||||
*/
|
||||
status = nfs_v4_minor_ops[minor_version]->find_root_sec(server, fhandle, info);
|
||||
int status;
|
||||
|
||||
status = nfs4_lookup_root(server, fhandle, info);
|
||||
if ((status == -NFS4ERR_WRONGSEC) &&
|
||||
!(server->flags & NFS_MOUNT_SECFLAVOUR))
|
||||
status = nfs4_do_find_root_sec(server, fhandle, info);
|
||||
|
||||
if (status == 0)
|
||||
status = nfs4_server_capabilities(server, fhandle);
|
||||
if (status == 0)
|
||||
status = nfs4_do_fsinfo(server, fhandle, info);
|
||||
|
||||
return nfs4_map_errors(status);
|
||||
}
|
||||
|
||||
|
@ -3381,12 +3518,21 @@ static int _nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle,
|
|||
static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo)
|
||||
{
|
||||
struct nfs4_exception exception = { };
|
||||
unsigned long now = jiffies;
|
||||
int err;
|
||||
|
||||
do {
|
||||
err = nfs4_handle_exception(server,
|
||||
_nfs4_do_fsinfo(server, fhandle, fsinfo),
|
||||
&exception);
|
||||
err = _nfs4_do_fsinfo(server, fhandle, fsinfo);
|
||||
if (err == 0) {
|
||||
struct nfs_client *clp = server->nfs_client;
|
||||
|
||||
spin_lock(&clp->cl_lock);
|
||||
clp->cl_lease_time = fsinfo->lease_time * HZ;
|
||||
clp->cl_last_renewal = now;
|
||||
spin_unlock(&clp->cl_lock);
|
||||
break;
|
||||
}
|
||||
err = nfs4_handle_exception(server, err, &exception);
|
||||
} while (exception.retry);
|
||||
return err;
|
||||
}
|
||||
|
@ -3446,6 +3592,46 @@ static int nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
|
|||
return err;
|
||||
}
|
||||
|
||||
int nfs4_set_rw_stateid(nfs4_stateid *stateid,
|
||||
const struct nfs_open_context *ctx,
|
||||
const struct nfs_lock_context *l_ctx,
|
||||
fmode_t fmode)
|
||||
{
|
||||
const struct nfs_lockowner *lockowner = NULL;
|
||||
|
||||
if (l_ctx != NULL)
|
||||
lockowner = &l_ctx->lockowner;
|
||||
return nfs4_select_rw_stateid(stateid, ctx->state, fmode, lockowner);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs4_set_rw_stateid);
|
||||
|
||||
static bool nfs4_stateid_is_current(nfs4_stateid *stateid,
|
||||
const struct nfs_open_context *ctx,
|
||||
const struct nfs_lock_context *l_ctx,
|
||||
fmode_t fmode)
|
||||
{
|
||||
nfs4_stateid current_stateid;
|
||||
|
||||
if (nfs4_set_rw_stateid(¤t_stateid, ctx, l_ctx, fmode))
|
||||
return false;
|
||||
return nfs4_stateid_match(stateid, ¤t_stateid);
|
||||
}
|
||||
|
||||
static bool nfs4_error_stateid_expired(int err)
|
||||
{
|
||||
switch (err) {
|
||||
case -NFS4ERR_DELEG_REVOKED:
|
||||
case -NFS4ERR_ADMIN_REVOKED:
|
||||
case -NFS4ERR_BAD_STATEID:
|
||||
case -NFS4ERR_STALE_STATEID:
|
||||
case -NFS4ERR_OLD_STATEID:
|
||||
case -NFS4ERR_OPENMODE:
|
||||
case -NFS4ERR_EXPIRED:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void __nfs4_read_done_cb(struct nfs_read_data *data)
|
||||
{
|
||||
nfs_invalidate_atime(data->header->inode);
|
||||
|
@ -3466,6 +3652,20 @@ static int nfs4_read_done_cb(struct rpc_task *task, struct nfs_read_data *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool nfs4_read_stateid_changed(struct rpc_task *task,
|
||||
struct nfs_readargs *args)
|
||||
{
|
||||
|
||||
if (!nfs4_error_stateid_expired(task->tk_status) ||
|
||||
nfs4_stateid_is_current(&args->stateid,
|
||||
args->context,
|
||||
args->lock_context,
|
||||
FMODE_READ))
|
||||
return false;
|
||||
rpc_restart_call_prepare(task);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data)
|
||||
{
|
||||
|
||||
|
@ -3473,7 +3673,8 @@ static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data)
|
|||
|
||||
if (!nfs4_sequence_done(task, &data->res.seq_res))
|
||||
return -EAGAIN;
|
||||
|
||||
if (nfs4_read_stateid_changed(task, &data->args))
|
||||
return -EAGAIN;
|
||||
return data->read_done_cb ? data->read_done_cb(task, data) :
|
||||
nfs4_read_done_cb(task, data);
|
||||
}
|
||||
|
@ -3488,10 +3689,13 @@ static void nfs4_proc_read_setup(struct nfs_read_data *data, struct rpc_message
|
|||
|
||||
static void nfs4_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data)
|
||||
{
|
||||
nfs4_setup_sequence(NFS_SERVER(data->header->inode),
|
||||
if (nfs4_setup_sequence(NFS_SERVER(data->header->inode),
|
||||
&data->args.seq_args,
|
||||
&data->res.seq_res,
|
||||
task);
|
||||
task))
|
||||
return;
|
||||
nfs4_set_rw_stateid(&data->args.stateid, data->args.context,
|
||||
data->args.lock_context, FMODE_READ);
|
||||
}
|
||||
|
||||
static int nfs4_write_done_cb(struct rpc_task *task, struct nfs_write_data *data)
|
||||
|
@ -3509,10 +3713,26 @@ static int nfs4_write_done_cb(struct rpc_task *task, struct nfs_write_data *data
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool nfs4_write_stateid_changed(struct rpc_task *task,
|
||||
struct nfs_writeargs *args)
|
||||
{
|
||||
|
||||
if (!nfs4_error_stateid_expired(task->tk_status) ||
|
||||
nfs4_stateid_is_current(&args->stateid,
|
||||
args->context,
|
||||
args->lock_context,
|
||||
FMODE_WRITE))
|
||||
return false;
|
||||
rpc_restart_call_prepare(task);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data)
|
||||
{
|
||||
if (!nfs4_sequence_done(task, &data->res.seq_res))
|
||||
return -EAGAIN;
|
||||
if (nfs4_write_stateid_changed(task, &data->args))
|
||||
return -EAGAIN;
|
||||
return data->write_done_cb ? data->write_done_cb(task, data) :
|
||||
nfs4_write_done_cb(task, data);
|
||||
}
|
||||
|
@ -3552,10 +3772,13 @@ static void nfs4_proc_write_setup(struct nfs_write_data *data, struct rpc_messag
|
|||
|
||||
static void nfs4_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data)
|
||||
{
|
||||
nfs4_setup_sequence(NFS_SERVER(data->header->inode),
|
||||
if (nfs4_setup_sequence(NFS_SERVER(data->header->inode),
|
||||
&data->args.seq_args,
|
||||
&data->res.seq_res,
|
||||
task);
|
||||
task))
|
||||
return;
|
||||
nfs4_set_rw_stateid(&data->args.stateid, data->args.context,
|
||||
data->args.lock_context, FMODE_WRITE);
|
||||
}
|
||||
|
||||
static void nfs4_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data)
|
||||
|
@ -3657,7 +3880,7 @@ static int nfs4_proc_async_renew(struct nfs_client *clp, struct rpc_cred *cred,
|
|||
return -ENOMEM;
|
||||
data->client = clp;
|
||||
data->timestamp = jiffies;
|
||||
return rpc_call_async(clp->cl_rpcclient, &msg, RPC_TASK_SOFT,
|
||||
return rpc_call_async(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT,
|
||||
&nfs4_renew_ops, data);
|
||||
}
|
||||
|
||||
|
@ -3671,7 +3894,7 @@ static int nfs4_proc_renew(struct nfs_client *clp, struct rpc_cred *cred)
|
|||
unsigned long now = jiffies;
|
||||
int status;
|
||||
|
||||
status = rpc_call_sync(clp->cl_rpcclient, &msg, 0);
|
||||
status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
|
||||
if (status < 0)
|
||||
return status;
|
||||
do_renew_lease(clp, now);
|
||||
|
@ -3981,11 +4204,14 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
|
|||
case -NFS4ERR_OPENMODE:
|
||||
if (state == NULL)
|
||||
break;
|
||||
nfs4_schedule_stateid_recovery(server, state);
|
||||
if (nfs4_schedule_stateid_recovery(server, state) < 0)
|
||||
goto stateid_invalid;
|
||||
goto wait_on_recovery;
|
||||
case -NFS4ERR_EXPIRED:
|
||||
if (state != NULL)
|
||||
nfs4_schedule_stateid_recovery(server, state);
|
||||
if (state != NULL) {
|
||||
if (nfs4_schedule_stateid_recovery(server, state) < 0)
|
||||
goto stateid_invalid;
|
||||
}
|
||||
case -NFS4ERR_STALE_STATEID:
|
||||
case -NFS4ERR_STALE_CLIENTID:
|
||||
nfs4_schedule_lease_recovery(clp);
|
||||
|
@ -4017,6 +4243,9 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
|
|||
}
|
||||
task->tk_status = nfs4_map_errors(task->tk_status);
|
||||
return 0;
|
||||
stateid_invalid:
|
||||
task->tk_status = -EIO;
|
||||
return 0;
|
||||
wait_on_recovery:
|
||||
rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL);
|
||||
if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) == 0)
|
||||
|
@ -4144,27 +4373,17 @@ int nfs4_proc_setclientid_confirm(struct nfs_client *clp,
|
|||
struct nfs4_setclientid_res *arg,
|
||||
struct rpc_cred *cred)
|
||||
{
|
||||
struct nfs_fsinfo fsinfo;
|
||||
struct rpc_message msg = {
|
||||
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID_CONFIRM],
|
||||
.rpc_argp = arg,
|
||||
.rpc_resp = &fsinfo,
|
||||
.rpc_cred = cred,
|
||||
};
|
||||
unsigned long now;
|
||||
int status;
|
||||
|
||||
dprintk("NFS call setclientid_confirm auth=%s, (client ID %llx)\n",
|
||||
clp->cl_rpcclient->cl_auth->au_ops->au_name,
|
||||
clp->cl_clientid);
|
||||
now = jiffies;
|
||||
status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
|
||||
if (status == 0) {
|
||||
spin_lock(&clp->cl_lock);
|
||||
clp->cl_lease_time = fsinfo.lease_time * HZ;
|
||||
clp->cl_last_renewal = now;
|
||||
spin_unlock(&clp->cl_lock);
|
||||
}
|
||||
dprintk("NFS reply setclientid_confirm: %d\n", status);
|
||||
return status;
|
||||
}
|
||||
|
@ -4628,17 +4847,23 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
|
|||
if (nfs_wait_on_sequence(data->arg.open_seqid, task) != 0) {
|
||||
goto out_release_lock_seqid;
|
||||
}
|
||||
data->arg.open_stateid = &state->stateid;
|
||||
data->arg.open_stateid = &state->open_stateid;
|
||||
data->arg.new_lock_owner = 1;
|
||||
data->res.open_seqid = data->arg.open_seqid;
|
||||
} else
|
||||
data->arg.new_lock_owner = 0;
|
||||
if (!nfs4_valid_open_stateid(state)) {
|
||||
data->rpc_status = -EBADF;
|
||||
task->tk_action = NULL;
|
||||
goto out_release_open_seqid;
|
||||
}
|
||||
data->timestamp = jiffies;
|
||||
if (nfs4_setup_sequence(data->server,
|
||||
&data->arg.seq_args,
|
||||
&data->res.seq_res,
|
||||
task) == 0)
|
||||
return;
|
||||
out_release_open_seqid:
|
||||
nfs_release_seqid(data->arg.open_seqid);
|
||||
out_release_lock_seqid:
|
||||
nfs_release_seqid(data->arg.lock_seqid);
|
||||
|
@ -4984,58 +5209,16 @@ nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request)
|
|||
return status;
|
||||
}
|
||||
|
||||
int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl)
|
||||
int nfs4_lock_delegation_recall(struct file_lock *fl, struct nfs4_state *state, const nfs4_stateid *stateid)
|
||||
{
|
||||
struct nfs_server *server = NFS_SERVER(state->inode);
|
||||
struct nfs4_exception exception = { };
|
||||
int err;
|
||||
|
||||
err = nfs4_set_lock_state(state, fl);
|
||||
if (err != 0)
|
||||
goto out;
|
||||
do {
|
||||
err = _nfs4_do_setlk(state, F_SETLK, fl, NFS_LOCK_NEW);
|
||||
switch (err) {
|
||||
default:
|
||||
printk(KERN_ERR "NFS: %s: unhandled error "
|
||||
"%d.\n", __func__, err);
|
||||
case 0:
|
||||
case -ESTALE:
|
||||
goto out;
|
||||
case -NFS4ERR_STALE_CLIENTID:
|
||||
case -NFS4ERR_STALE_STATEID:
|
||||
set_bit(NFS_DELEGATED_STATE, &state->flags);
|
||||
case -NFS4ERR_EXPIRED:
|
||||
nfs4_schedule_lease_recovery(server->nfs_client);
|
||||
err = -EAGAIN;
|
||||
goto out;
|
||||
case -NFS4ERR_BADSESSION:
|
||||
case -NFS4ERR_BADSLOT:
|
||||
case -NFS4ERR_BAD_HIGH_SLOT:
|
||||
case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
|
||||
case -NFS4ERR_DEADSESSION:
|
||||
set_bit(NFS_DELEGATED_STATE, &state->flags);
|
||||
nfs4_schedule_session_recovery(server->nfs_client->cl_session, err);
|
||||
err = -EAGAIN;
|
||||
goto out;
|
||||
case -NFS4ERR_DELEG_REVOKED:
|
||||
case -NFS4ERR_ADMIN_REVOKED:
|
||||
case -NFS4ERR_BAD_STATEID:
|
||||
case -NFS4ERR_OPENMODE:
|
||||
nfs4_schedule_stateid_recovery(server, state);
|
||||
err = 0;
|
||||
goto out;
|
||||
case -ENOMEM:
|
||||
case -NFS4ERR_DENIED:
|
||||
/* kill_proc(fl->fl_pid, SIGLOST, 1); */
|
||||
err = 0;
|
||||
goto out;
|
||||
}
|
||||
set_bit(NFS_DELEGATED_STATE, &state->flags);
|
||||
err = nfs4_handle_exception(server, err, &exception);
|
||||
} while (exception.retry);
|
||||
out:
|
||||
return err;
|
||||
return err;
|
||||
err = _nfs4_do_setlk(state, F_SETLK, fl, NFS_LOCK_NEW);
|
||||
return nfs4_handle_delegation_recall_error(server, state, stateid, err);
|
||||
}
|
||||
|
||||
struct nfs_release_lockowner_data {
|
||||
|
@ -5849,7 +6032,7 @@ static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp,
|
|||
.rpc_client = clp->cl_rpcclient,
|
||||
.rpc_message = &msg,
|
||||
.callback_ops = &nfs41_sequence_ops,
|
||||
.flags = RPC_TASK_ASYNC | RPC_TASK_SOFT,
|
||||
.flags = RPC_TASK_ASYNC | RPC_TASK_TIMEOUT,
|
||||
};
|
||||
|
||||
if (!atomic_inc_not_zero(&clp->cl_count))
|
||||
|
@ -6726,6 +6909,10 @@ static const struct nfs4_state_maintenance_ops nfs41_state_renewal_ops = {
|
|||
|
||||
static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = {
|
||||
.minor_version = 0,
|
||||
.init_caps = NFS_CAP_READDIRPLUS
|
||||
| NFS_CAP_ATOMIC_OPEN
|
||||
| NFS_CAP_CHANGE_ATTR
|
||||
| NFS_CAP_POSIX_LOCK,
|
||||
.call_sync = _nfs4_call_sync,
|
||||
.match_stateid = nfs4_match_stateid,
|
||||
.find_root_sec = nfs4_find_root_sec,
|
||||
|
@ -6737,6 +6924,12 @@ static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = {
|
|||
#if defined(CONFIG_NFS_V4_1)
|
||||
static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
|
||||
.minor_version = 1,
|
||||
.init_caps = NFS_CAP_READDIRPLUS
|
||||
| NFS_CAP_ATOMIC_OPEN
|
||||
| NFS_CAP_CHANGE_ATTR
|
||||
| NFS_CAP_POSIX_LOCK
|
||||
| NFS_CAP_STATEID_NFSV41
|
||||
| NFS_CAP_ATOMIC_OPEN_V1,
|
||||
.call_sync = nfs4_call_sync_sequence,
|
||||
.match_stateid = nfs41_match_stateid,
|
||||
.find_root_sec = nfs41_find_root_sec,
|
||||
|
|
|
@ -154,18 +154,6 @@ struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp)
|
|||
return cred;
|
||||
}
|
||||
|
||||
static void nfs4_clear_machine_cred(struct nfs_client *clp)
|
||||
{
|
||||
struct rpc_cred *cred;
|
||||
|
||||
spin_lock(&clp->cl_lock);
|
||||
cred = clp->cl_machine_cred;
|
||||
clp->cl_machine_cred = NULL;
|
||||
spin_unlock(&clp->cl_lock);
|
||||
if (cred != NULL)
|
||||
put_rpccred(cred);
|
||||
}
|
||||
|
||||
static struct rpc_cred *
|
||||
nfs4_get_renew_cred_server_locked(struct nfs_server *server)
|
||||
{
|
||||
|
@ -699,6 +687,8 @@ __nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner)
|
|||
list_for_each_entry(state, &nfsi->open_states, inode_states) {
|
||||
if (state->owner != owner)
|
||||
continue;
|
||||
if (!nfs4_valid_open_stateid(state))
|
||||
continue;
|
||||
if (atomic_inc_not_zero(&state->count))
|
||||
return state;
|
||||
}
|
||||
|
@ -987,13 +977,14 @@ int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool nfs4_copy_lock_stateid(nfs4_stateid *dst, struct nfs4_state *state,
|
||||
static int nfs4_copy_lock_stateid(nfs4_stateid *dst,
|
||||
struct nfs4_state *state,
|
||||
const struct nfs_lockowner *lockowner)
|
||||
{
|
||||
struct nfs4_lock_state *lsp;
|
||||
fl_owner_t fl_owner;
|
||||
pid_t fl_pid;
|
||||
bool ret = false;
|
||||
int ret = -ENOENT;
|
||||
|
||||
|
||||
if (lockowner == NULL)
|
||||
|
@ -1008,7 +999,10 @@ static bool nfs4_copy_lock_stateid(nfs4_stateid *dst, struct nfs4_state *state,
|
|||
lsp = __nfs4_find_lock_state(state, fl_owner, fl_pid, NFS4_ANY_LOCK_TYPE);
|
||||
if (lsp != NULL && test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags) != 0) {
|
||||
nfs4_stateid_copy(dst, &lsp->ls_stateid);
|
||||
ret = true;
|
||||
ret = 0;
|
||||
smp_rmb();
|
||||
if (!list_empty(&lsp->ls_seqid.list))
|
||||
ret = -EWOULDBLOCK;
|
||||
}
|
||||
spin_unlock(&state->state_lock);
|
||||
nfs4_put_lock_state(lsp);
|
||||
|
@ -1016,28 +1010,44 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void nfs4_copy_open_stateid(nfs4_stateid *dst, struct nfs4_state *state)
|
||||
static int nfs4_copy_open_stateid(nfs4_stateid *dst, struct nfs4_state *state)
|
||||
{
|
||||
const nfs4_stateid *src;
|
||||
int ret;
|
||||
int seq;
|
||||
|
||||
do {
|
||||
src = &zero_stateid;
|
||||
seq = read_seqbegin(&state->seqlock);
|
||||
nfs4_stateid_copy(dst, &state->stateid);
|
||||
if (test_bit(NFS_OPEN_STATE, &state->flags))
|
||||
src = &state->open_stateid;
|
||||
nfs4_stateid_copy(dst, src);
|
||||
ret = 0;
|
||||
smp_rmb();
|
||||
if (!list_empty(&state->owner->so_seqid.list))
|
||||
ret = -EWOULDBLOCK;
|
||||
} while (read_seqretry(&state->seqlock, seq));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Byte-range lock aware utility to initialize the stateid of read/write
|
||||
* requests.
|
||||
*/
|
||||
void nfs4_select_rw_stateid(nfs4_stateid *dst, struct nfs4_state *state,
|
||||
int nfs4_select_rw_stateid(nfs4_stateid *dst, struct nfs4_state *state,
|
||||
fmode_t fmode, const struct nfs_lockowner *lockowner)
|
||||
{
|
||||
int ret = 0;
|
||||
if (nfs4_copy_delegation_stateid(dst, state->inode, fmode))
|
||||
return;
|
||||
if (nfs4_copy_lock_stateid(dst, state, lockowner))
|
||||
return;
|
||||
nfs4_copy_open_stateid(dst, state);
|
||||
goto out;
|
||||
ret = nfs4_copy_lock_stateid(dst, state, lockowner);
|
||||
if (ret != -ENOENT)
|
||||
goto out;
|
||||
ret = nfs4_copy_open_stateid(dst, state);
|
||||
out:
|
||||
if (nfs_server_capable(state->inode, NFS_CAP_STATEID_NFSV41))
|
||||
dst->seqid = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter, gfp_t gfp_mask)
|
||||
|
@ -1286,14 +1296,17 @@ static int nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_s
|
|||
return 1;
|
||||
}
|
||||
|
||||
void nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4_state *state)
|
||||
int nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4_state *state)
|
||||
{
|
||||
struct nfs_client *clp = server->nfs_client;
|
||||
|
||||
if (!nfs4_valid_open_stateid(state))
|
||||
return -EBADF;
|
||||
nfs4_state_mark_reclaim_nograce(clp, state);
|
||||
dprintk("%s: scheduling stateid recovery for server %s\n", __func__,
|
||||
clp->cl_hostname);
|
||||
nfs4_schedule_state_manager(clp);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs4_schedule_stateid_recovery);
|
||||
|
||||
|
@ -1323,6 +1336,27 @@ void nfs_inode_find_state_and_recover(struct inode *inode,
|
|||
nfs4_schedule_state_manager(clp);
|
||||
}
|
||||
|
||||
static void nfs4_state_mark_open_context_bad(struct nfs4_state *state)
|
||||
{
|
||||
struct inode *inode = state->inode;
|
||||
struct nfs_inode *nfsi = NFS_I(inode);
|
||||
struct nfs_open_context *ctx;
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
list_for_each_entry(ctx, &nfsi->open_files, list) {
|
||||
if (ctx->state != state)
|
||||
continue;
|
||||
set_bit(NFS_CONTEXT_BAD, &ctx->flags);
|
||||
}
|
||||
spin_unlock(&inode->i_lock);
|
||||
}
|
||||
|
||||
static void nfs4_state_mark_recovery_failed(struct nfs4_state *state, int error)
|
||||
{
|
||||
set_bit(NFS_STATE_RECOVERY_FAILED, &state->flags);
|
||||
nfs4_state_mark_open_context_bad(state);
|
||||
}
|
||||
|
||||
|
||||
static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_recovery_ops *ops)
|
||||
{
|
||||
|
@ -1398,6 +1432,8 @@ restart:
|
|||
list_for_each_entry(state, &sp->so_states, open_states) {
|
||||
if (!test_and_clear_bit(ops->state_flag_bit, &state->flags))
|
||||
continue;
|
||||
if (!nfs4_valid_open_stateid(state))
|
||||
continue;
|
||||
if (state->state == 0)
|
||||
continue;
|
||||
atomic_inc(&state->count);
|
||||
|
@ -1430,11 +1466,10 @@ restart:
|
|||
* Open state on this file cannot be recovered
|
||||
* All we can do is revert to using the zero stateid.
|
||||
*/
|
||||
memset(&state->stateid, 0,
|
||||
sizeof(state->stateid));
|
||||
/* Mark the file as being 'closed' */
|
||||
state->state = 0;
|
||||
nfs4_state_mark_recovery_failed(state, status);
|
||||
break;
|
||||
case -EAGAIN:
|
||||
ssleep(1);
|
||||
case -NFS4ERR_ADMIN_REVOKED:
|
||||
case -NFS4ERR_STALE_STATEID:
|
||||
case -NFS4ERR_BAD_STATEID:
|
||||
|
@ -1696,6 +1731,10 @@ static int nfs4_check_lease(struct nfs_client *clp)
|
|||
}
|
||||
status = ops->renew_lease(clp, cred);
|
||||
put_rpccred(cred);
|
||||
if (status == -ETIMEDOUT) {
|
||||
set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
|
||||
return 0;
|
||||
}
|
||||
out:
|
||||
return nfs4_recovery_handle_error(clp, status);
|
||||
}
|
||||
|
@ -1725,10 +1764,6 @@ static int nfs4_handle_reclaim_lease_error(struct nfs_client *clp, int status)
|
|||
clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
|
||||
return -EPERM;
|
||||
case -EACCES:
|
||||
if (clp->cl_machine_cred == NULL)
|
||||
return -EACCES;
|
||||
/* Handle case where the user hasn't set up machine creds */
|
||||
nfs4_clear_machine_cred(clp);
|
||||
case -NFS4ERR_DELAY:
|
||||
case -ETIMEDOUT:
|
||||
case -EAGAIN:
|
||||
|
@ -1823,31 +1858,18 @@ int nfs4_discover_server_trunking(struct nfs_client *clp,
|
|||
{
|
||||
const struct nfs4_state_recovery_ops *ops =
|
||||
clp->cl_mvops->reboot_recovery_ops;
|
||||
rpc_authflavor_t *flavors, flav, save;
|
||||
struct rpc_clnt *clnt;
|
||||
struct rpc_cred *cred;
|
||||
int i, len, status;
|
||||
int i, status;
|
||||
|
||||
dprintk("NFS: %s: testing '%s'\n", __func__, clp->cl_hostname);
|
||||
|
||||
len = NFS_MAX_SECFLAVORS;
|
||||
flavors = kcalloc(len, sizeof(*flavors), GFP_KERNEL);
|
||||
if (flavors == NULL) {
|
||||
status = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
len = rpcauth_list_flavors(flavors, len);
|
||||
if (len < 0) {
|
||||
status = len;
|
||||
goto out_free;
|
||||
}
|
||||
clnt = clp->cl_rpcclient;
|
||||
save = clnt->cl_auth->au_flavor;
|
||||
i = 0;
|
||||
|
||||
mutex_lock(&nfs_clid_init_mutex);
|
||||
status = -ENOENT;
|
||||
again:
|
||||
status = -ENOENT;
|
||||
cred = ops->get_clid_cred(clp);
|
||||
if (cred == NULL)
|
||||
goto out_unlock;
|
||||
|
@ -1857,12 +1879,6 @@ again:
|
|||
switch (status) {
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case -EACCES:
|
||||
if (clp->cl_machine_cred == NULL)
|
||||
break;
|
||||
/* Handle case where the user hasn't set up machine creds */
|
||||
nfs4_clear_machine_cred(clp);
|
||||
case -NFS4ERR_DELAY:
|
||||
case -ETIMEDOUT:
|
||||
case -EAGAIN:
|
||||
|
@ -1871,17 +1887,12 @@ again:
|
|||
dprintk("NFS: %s after status %d, retrying\n",
|
||||
__func__, status);
|
||||
goto again;
|
||||
|
||||
case -EACCES:
|
||||
if (i++)
|
||||
break;
|
||||
case -NFS4ERR_CLID_INUSE:
|
||||
case -NFS4ERR_WRONGSEC:
|
||||
status = -EPERM;
|
||||
if (i >= len)
|
||||
break;
|
||||
|
||||
flav = flavors[i++];
|
||||
if (flav == save)
|
||||
flav = flavors[i++];
|
||||
clnt = rpc_clone_client_set_auth(clnt, flav);
|
||||
clnt = rpc_clone_client_set_auth(clnt, RPC_AUTH_UNIX);
|
||||
if (IS_ERR(clnt)) {
|
||||
status = PTR_ERR(clnt);
|
||||
break;
|
||||
|
@ -1903,13 +1914,15 @@ again:
|
|||
case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
|
||||
* in nfs4_exchange_id */
|
||||
status = -EKEYEXPIRED;
|
||||
break;
|
||||
default:
|
||||
pr_warn("NFS: %s unhandled error %d. Exiting with error EIO\n",
|
||||
__func__, status);
|
||||
status = -EIO;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&nfs_clid_init_mutex);
|
||||
out_free:
|
||||
kfree(flavors);
|
||||
out:
|
||||
dprintk("NFS: %s: status = %d\n", __func__, status);
|
||||
return status;
|
||||
}
|
||||
|
|
|
@ -252,6 +252,8 @@ struct dentry *nfs4_try_mount(int flags, const char *dev_name,
|
|||
|
||||
dfprintk(MOUNT, "--> nfs4_try_mount()\n");
|
||||
|
||||
if (data->auth_flavors[0] == RPC_AUTH_MAXFLAVOR)
|
||||
data->auth_flavors[0] = RPC_AUTH_UNIX;
|
||||
export_path = data->nfs_server.export_path;
|
||||
data->nfs_server.export_path = "/";
|
||||
root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, mount_info,
|
||||
|
|
176
fs/nfs/nfs4xdr.c
176
fs/nfs/nfs4xdr.c
|
@ -530,14 +530,10 @@ static int nfs4_stat_to_errno(int);
|
|||
decode_setclientid_maxsz)
|
||||
#define NFS4_enc_setclientid_confirm_sz \
|
||||
(compound_encode_hdr_maxsz + \
|
||||
encode_setclientid_confirm_maxsz + \
|
||||
encode_putrootfh_maxsz + \
|
||||
encode_fsinfo_maxsz)
|
||||
encode_setclientid_confirm_maxsz)
|
||||
#define NFS4_dec_setclientid_confirm_sz \
|
||||
(compound_decode_hdr_maxsz + \
|
||||
decode_setclientid_confirm_maxsz + \
|
||||
decode_putrootfh_maxsz + \
|
||||
decode_fsinfo_maxsz)
|
||||
decode_setclientid_confirm_maxsz)
|
||||
#define NFS4_enc_lock_sz (compound_encode_hdr_maxsz + \
|
||||
encode_sequence_maxsz + \
|
||||
encode_putfh_maxsz + \
|
||||
|
@ -1058,8 +1054,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const
|
|||
if (iap->ia_valid & ATTR_ATIME_SET) {
|
||||
bmval1 |= FATTR4_WORD1_TIME_ACCESS_SET;
|
||||
*p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
|
||||
*p++ = cpu_to_be32(0);
|
||||
*p++ = cpu_to_be32(iap->ia_atime.tv_sec);
|
||||
p = xdr_encode_hyper(p, (s64)iap->ia_atime.tv_sec);
|
||||
*p++ = cpu_to_be32(iap->ia_atime.tv_nsec);
|
||||
}
|
||||
else if (iap->ia_valid & ATTR_ATIME) {
|
||||
|
@ -1069,8 +1064,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const
|
|||
if (iap->ia_valid & ATTR_MTIME_SET) {
|
||||
bmval1 |= FATTR4_WORD1_TIME_MODIFY_SET;
|
||||
*p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
|
||||
*p++ = cpu_to_be32(0);
|
||||
*p++ = cpu_to_be32(iap->ia_mtime.tv_sec);
|
||||
p = xdr_encode_hyper(p, (s64)iap->ia_mtime.tv_sec);
|
||||
*p++ = cpu_to_be32(iap->ia_mtime.tv_nsec);
|
||||
}
|
||||
else if (iap->ia_valid & ATTR_MTIME) {
|
||||
|
@ -1366,33 +1360,28 @@ static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_opena
|
|||
|
||||
static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_openargs *arg)
|
||||
{
|
||||
struct iattr dummy;
|
||||
__be32 *p;
|
||||
struct nfs_client *clp;
|
||||
|
||||
p = reserve_space(xdr, 4);
|
||||
switch(arg->open_flags & O_EXCL) {
|
||||
case 0:
|
||||
switch(arg->createmode) {
|
||||
case NFS4_CREATE_UNCHECKED:
|
||||
*p = cpu_to_be32(NFS4_CREATE_UNCHECKED);
|
||||
encode_attrs(xdr, arg->u.attrs, arg->server);
|
||||
break;
|
||||
default:
|
||||
clp = arg->server->nfs_client;
|
||||
if (clp->cl_mvops->minor_version > 0) {
|
||||
if (nfs4_has_persistent_session(clp)) {
|
||||
*p = cpu_to_be32(NFS4_CREATE_GUARDED);
|
||||
encode_attrs(xdr, arg->u.attrs, arg->server);
|
||||
} else {
|
||||
struct iattr dummy;
|
||||
|
||||
*p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE4_1);
|
||||
encode_nfs4_verifier(xdr, &arg->u.verifier);
|
||||
dummy.ia_valid = 0;
|
||||
encode_attrs(xdr, &dummy, arg->server);
|
||||
}
|
||||
} else {
|
||||
*p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE);
|
||||
encode_nfs4_verifier(xdr, &arg->u.verifier);
|
||||
}
|
||||
case NFS4_CREATE_GUARDED:
|
||||
*p = cpu_to_be32(NFS4_CREATE_GUARDED);
|
||||
encode_attrs(xdr, arg->u.attrs, arg->server);
|
||||
break;
|
||||
case NFS4_CREATE_EXCLUSIVE:
|
||||
*p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE);
|
||||
encode_nfs4_verifier(xdr, &arg->u.verifier);
|
||||
break;
|
||||
case NFS4_CREATE_EXCLUSIVE4_1:
|
||||
*p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE4_1);
|
||||
encode_nfs4_verifier(xdr, &arg->u.verifier);
|
||||
dummy.ia_valid = 0;
|
||||
encode_attrs(xdr, &dummy, arg->server);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1459,6 +1448,23 @@ static inline void encode_claim_delegate_cur(struct xdr_stream *xdr, const struc
|
|||
encode_string(xdr, name->len, name->name);
|
||||
}
|
||||
|
||||
static inline void encode_claim_fh(struct xdr_stream *xdr)
|
||||
{
|
||||
__be32 *p;
|
||||
|
||||
p = reserve_space(xdr, 4);
|
||||
*p = cpu_to_be32(NFS4_OPEN_CLAIM_FH);
|
||||
}
|
||||
|
||||
static inline void encode_claim_delegate_cur_fh(struct xdr_stream *xdr, const nfs4_stateid *stateid)
|
||||
{
|
||||
__be32 *p;
|
||||
|
||||
p = reserve_space(xdr, 4);
|
||||
*p = cpu_to_be32(NFS4_OPEN_CLAIM_DELEG_CUR_FH);
|
||||
encode_nfs4_stateid(xdr, stateid);
|
||||
}
|
||||
|
||||
static void encode_open(struct xdr_stream *xdr, const struct nfs_openargs *arg, struct compound_hdr *hdr)
|
||||
{
|
||||
encode_op_hdr(xdr, OP_OPEN, decode_open_maxsz, hdr);
|
||||
|
@ -1474,6 +1480,12 @@ static void encode_open(struct xdr_stream *xdr, const struct nfs_openargs *arg,
|
|||
case NFS4_OPEN_CLAIM_DELEGATE_CUR:
|
||||
encode_claim_delegate_cur(xdr, arg->name, &arg->u.delegation);
|
||||
break;
|
||||
case NFS4_OPEN_CLAIM_FH:
|
||||
encode_claim_fh(xdr);
|
||||
break;
|
||||
case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
|
||||
encode_claim_delegate_cur_fh(xdr, &arg->u.delegation);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
@ -1506,35 +1518,12 @@ static void encode_putrootfh(struct xdr_stream *xdr, struct compound_hdr *hdr)
|
|||
encode_op_hdr(xdr, OP_PUTROOTFH, decode_putrootfh_maxsz, hdr);
|
||||
}
|
||||
|
||||
static void encode_open_stateid(struct xdr_stream *xdr,
|
||||
const struct nfs_open_context *ctx,
|
||||
const struct nfs_lock_context *l_ctx,
|
||||
fmode_t fmode,
|
||||
int zero_seqid)
|
||||
{
|
||||
nfs4_stateid stateid;
|
||||
|
||||
if (ctx->state != NULL) {
|
||||
const struct nfs_lockowner *lockowner = NULL;
|
||||
|
||||
if (l_ctx != NULL)
|
||||
lockowner = &l_ctx->lockowner;
|
||||
nfs4_select_rw_stateid(&stateid, ctx->state,
|
||||
fmode, lockowner);
|
||||
if (zero_seqid)
|
||||
stateid.seqid = 0;
|
||||
encode_nfs4_stateid(xdr, &stateid);
|
||||
} else
|
||||
encode_nfs4_stateid(xdr, &zero_stateid);
|
||||
}
|
||||
|
||||
static void encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args, struct compound_hdr *hdr)
|
||||
{
|
||||
__be32 *p;
|
||||
|
||||
encode_op_hdr(xdr, OP_READ, decode_read_maxsz, hdr);
|
||||
encode_open_stateid(xdr, args->context, args->lock_context,
|
||||
FMODE_READ, hdr->minorversion);
|
||||
encode_nfs4_stateid(xdr, &args->stateid);
|
||||
|
||||
p = reserve_space(xdr, 12);
|
||||
p = xdr_encode_hyper(p, args->offset);
|
||||
|
@ -1670,8 +1659,7 @@ static void encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *arg
|
|||
__be32 *p;
|
||||
|
||||
encode_op_hdr(xdr, OP_WRITE, decode_write_maxsz, hdr);
|
||||
encode_open_stateid(xdr, args->context, args->lock_context,
|
||||
FMODE_WRITE, hdr->minorversion);
|
||||
encode_nfs4_stateid(xdr, &args->stateid);
|
||||
|
||||
p = reserve_space(xdr, 16);
|
||||
p = xdr_encode_hyper(p, args->offset);
|
||||
|
@ -2609,12 +2597,9 @@ static void nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req,
|
|||
struct compound_hdr hdr = {
|
||||
.nops = 0,
|
||||
};
|
||||
const u32 lease_bitmap[3] = { FATTR4_WORD0_LEASE_TIME };
|
||||
|
||||
encode_compound_hdr(xdr, req, &hdr);
|
||||
encode_setclientid_confirm(xdr, arg, &hdr);
|
||||
encode_putrootfh(xdr, &hdr);
|
||||
encode_fsinfo(xdr, lease_bitmap, &hdr);
|
||||
encode_nops(&hdr);
|
||||
}
|
||||
|
||||
|
@ -3497,8 +3482,11 @@ static int decode_pathname(struct xdr_stream *xdr, struct nfs4_pathname *path)
|
|||
if (n == 0)
|
||||
goto root_path;
|
||||
dprintk("pathname4: ");
|
||||
path->ncomponents = 0;
|
||||
while (path->ncomponents < n) {
|
||||
if (n > NFS4_PATHNAME_MAXCOMPONENTS) {
|
||||
dprintk("cannot parse %d components in path\n", n);
|
||||
goto out_eio;
|
||||
}
|
||||
for (path->ncomponents = 0; path->ncomponents < n; path->ncomponents++) {
|
||||
struct nfs4_string *component = &path->components[path->ncomponents];
|
||||
status = decode_opaque_inline(xdr, &component->len, &component->data);
|
||||
if (unlikely(status != 0))
|
||||
|
@ -3507,12 +3495,6 @@ static int decode_pathname(struct xdr_stream *xdr, struct nfs4_pathname *path)
|
|||
pr_cont("%s%.*s ",
|
||||
(path->ncomponents != n ? "/ " : ""),
|
||||
component->len, component->data);
|
||||
if (path->ncomponents < NFS4_PATHNAME_MAXCOMPONENTS)
|
||||
path->ncomponents++;
|
||||
else {
|
||||
dprintk("cannot parse %d components in path\n", n);
|
||||
goto out_eio;
|
||||
}
|
||||
}
|
||||
out:
|
||||
return status;
|
||||
|
@ -3557,27 +3539,23 @@ static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, st
|
|||
n = be32_to_cpup(p);
|
||||
if (n <= 0)
|
||||
goto out_eio;
|
||||
res->nlocations = 0;
|
||||
while (res->nlocations < n) {
|
||||
for (res->nlocations = 0; res->nlocations < n; res->nlocations++) {
|
||||
u32 m;
|
||||
struct nfs4_fs_location *loc = &res->locations[res->nlocations];
|
||||
struct nfs4_fs_location *loc;
|
||||
|
||||
if (res->nlocations == NFS4_FS_LOCATIONS_MAXENTRIES)
|
||||
break;
|
||||
loc = &res->locations[res->nlocations];
|
||||
p = xdr_inline_decode(xdr, 4);
|
||||
if (unlikely(!p))
|
||||
goto out_overflow;
|
||||
m = be32_to_cpup(p);
|
||||
|
||||
loc->nservers = 0;
|
||||
dprintk("%s: servers:\n", __func__);
|
||||
while (loc->nservers < m) {
|
||||
struct nfs4_string *server = &loc->servers[loc->nservers];
|
||||
status = decode_opaque_inline(xdr, &server->len, &server->data);
|
||||
if (unlikely(status != 0))
|
||||
goto out_eio;
|
||||
dprintk("%s ", server->data);
|
||||
if (loc->nservers < NFS4_FS_LOCATION_MAXSERVERS)
|
||||
loc->nservers++;
|
||||
else {
|
||||
for (loc->nservers = 0; loc->nservers < m; loc->nservers++) {
|
||||
struct nfs4_string *server;
|
||||
|
||||
if (loc->nservers == NFS4_FS_LOCATION_MAXSERVERS) {
|
||||
unsigned int i;
|
||||
dprintk("%s: using first %u of %u servers "
|
||||
"returned for location %u\n",
|
||||
|
@ -3591,13 +3569,17 @@ static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, st
|
|||
if (unlikely(status != 0))
|
||||
goto out_eio;
|
||||
}
|
||||
break;
|
||||
}
|
||||
server = &loc->servers[loc->nservers];
|
||||
status = decode_opaque_inline(xdr, &server->len, &server->data);
|
||||
if (unlikely(status != 0))
|
||||
goto out_eio;
|
||||
dprintk("%s ", server->data);
|
||||
}
|
||||
status = decode_pathname(xdr, &loc->rootpath);
|
||||
if (unlikely(status != 0))
|
||||
goto out_eio;
|
||||
if (res->nlocations < NFS4_FS_LOCATIONS_MAXENTRIES)
|
||||
res->nlocations++;
|
||||
}
|
||||
if (res->nlocations != 0)
|
||||
status = NFS_ATTR_FATTR_V4_LOCATIONS;
|
||||
|
@ -5209,27 +5191,30 @@ 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)
|
||||
static int decode_secinfo_gss(struct xdr_stream *xdr,
|
||||
struct nfs4_secinfo4 *flavor)
|
||||
{
|
||||
u32 oid_len;
|
||||
__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)
|
||||
oid_len = be32_to_cpup(p);
|
||||
if (oid_len > GSS_OID_MAX_LEN)
|
||||
goto out_err;
|
||||
|
||||
p = xdr_inline_decode(xdr, flavor->gss.sec_oid4.len);
|
||||
p = xdr_inline_decode(xdr, oid_len);
|
||||
if (unlikely(!p))
|
||||
goto out_overflow;
|
||||
memcpy(flavor->gss.sec_oid4.data, p, flavor->gss.sec_oid4.len);
|
||||
memcpy(flavor->flavor_info.oid.data, p, oid_len);
|
||||
flavor->flavor_info.oid.len = oid_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);
|
||||
flavor->flavor_info.qop = be32_to_cpup(p++);
|
||||
flavor->flavor_info.service = be32_to_cpup(p);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -5242,10 +5227,10 @@ out_err:
|
|||
|
||||
static int decode_secinfo_common(struct xdr_stream *xdr, struct nfs4_secinfo_res *res)
|
||||
{
|
||||
struct nfs4_secinfo_flavor *sec_flavor;
|
||||
struct nfs4_secinfo4 *sec_flavor;
|
||||
unsigned int i, num_flavors;
|
||||
int status;
|
||||
__be32 *p;
|
||||
int i, num_flavors;
|
||||
|
||||
p = xdr_inline_decode(xdr, 4);
|
||||
if (unlikely(!p))
|
||||
|
@ -6648,8 +6633,7 @@ static int nfs4_xdr_dec_setclientid(struct rpc_rqst *req,
|
|||
* Decode SETCLIENTID_CONFIRM response
|
||||
*/
|
||||
static int nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req,
|
||||
struct xdr_stream *xdr,
|
||||
struct nfs_fsinfo *fsinfo)
|
||||
struct xdr_stream *xdr)
|
||||
{
|
||||
struct compound_hdr hdr;
|
||||
int status;
|
||||
|
@ -6657,10 +6641,6 @@ static int nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req,
|
|||
status = decode_compound_hdr(xdr, &hdr);
|
||||
if (!status)
|
||||
status = decode_setclientid_confirm(xdr);
|
||||
if (!status)
|
||||
status = decode_putrootfh(xdr);
|
||||
if (!status)
|
||||
status = decode_fsinfo(xdr, fsinfo);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
|
|
@ -84,6 +84,55 @@ nfs_page_free(struct nfs_page *p)
|
|||
kmem_cache_free(nfs_page_cachep, p);
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_iocounter_inc(struct nfs_io_counter *c)
|
||||
{
|
||||
atomic_inc(&c->io_count);
|
||||
}
|
||||
|
||||
static void
|
||||
nfs_iocounter_dec(struct nfs_io_counter *c)
|
||||
{
|
||||
if (atomic_dec_and_test(&c->io_count)) {
|
||||
clear_bit(NFS_IO_INPROGRESS, &c->flags);
|
||||
smp_mb__after_clear_bit();
|
||||
wake_up_bit(&c->flags, NFS_IO_INPROGRESS);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
__nfs_iocounter_wait(struct nfs_io_counter *c)
|
||||
{
|
||||
wait_queue_head_t *wq = bit_waitqueue(&c->flags, NFS_IO_INPROGRESS);
|
||||
DEFINE_WAIT_BIT(q, &c->flags, NFS_IO_INPROGRESS);
|
||||
int ret = 0;
|
||||
|
||||
do {
|
||||
prepare_to_wait(wq, &q.wait, TASK_KILLABLE);
|
||||
set_bit(NFS_IO_INPROGRESS, &c->flags);
|
||||
if (atomic_read(&c->io_count) == 0)
|
||||
break;
|
||||
ret = nfs_wait_bit_killable(&c->flags);
|
||||
} while (atomic_read(&c->io_count) != 0);
|
||||
finish_wait(wq, &q.wait);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfs_iocounter_wait - wait for i/o to complete
|
||||
* @c: nfs_io_counter to use
|
||||
*
|
||||
* returns -ERESTARTSYS if interrupted by a fatal signal.
|
||||
* Otherwise returns 0 once the io_count hits 0.
|
||||
*/
|
||||
int
|
||||
nfs_iocounter_wait(struct nfs_io_counter *c)
|
||||
{
|
||||
if (atomic_read(&c->io_count) == 0)
|
||||
return 0;
|
||||
return __nfs_iocounter_wait(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* nfs_create_request - Create an NFS read/write request.
|
||||
* @ctx: open context to use
|
||||
|
@ -104,6 +153,8 @@ nfs_create_request(struct nfs_open_context *ctx, struct inode *inode,
|
|||
struct nfs_page *req;
|
||||
struct nfs_lock_context *l_ctx;
|
||||
|
||||
if (test_bit(NFS_CONTEXT_BAD, &ctx->flags))
|
||||
return ERR_PTR(-EBADF);
|
||||
/* try to allocate the request struct */
|
||||
req = nfs_page_alloc();
|
||||
if (req == NULL)
|
||||
|
@ -116,6 +167,7 @@ nfs_create_request(struct nfs_open_context *ctx, struct inode *inode,
|
|||
return ERR_CAST(l_ctx);
|
||||
}
|
||||
req->wb_lock_context = l_ctx;
|
||||
nfs_iocounter_inc(&l_ctx->io_count);
|
||||
|
||||
/* Initialize the request struct. Initially, we assume a
|
||||
* long write-back delay. This will be adjusted in
|
||||
|
@ -175,6 +227,7 @@ static void nfs_clear_request(struct nfs_page *req)
|
|||
req->wb_page = NULL;
|
||||
}
|
||||
if (l_ctx != NULL) {
|
||||
nfs_iocounter_dec(&l_ctx->io_count);
|
||||
nfs_put_lock_context(l_ctx);
|
||||
req->wb_lock_context = NULL;
|
||||
}
|
||||
|
|
|
@ -718,6 +718,8 @@ pnfs_choose_layoutget_stateid(nfs4_stateid *dst, struct pnfs_layout_hdr *lo,
|
|||
spin_lock(&lo->plh_inode->i_lock);
|
||||
if (pnfs_layoutgets_blocked(lo, 1)) {
|
||||
status = -EAGAIN;
|
||||
} else if (!nfs4_valid_open_stateid(open_state)) {
|
||||
status = -EBADF;
|
||||
} else if (list_empty(&lo->plh_segs)) {
|
||||
int seq;
|
||||
|
||||
|
|
|
@ -514,6 +514,8 @@ void nfs_read_prepare(struct rpc_task *task, void *calldata)
|
|||
{
|
||||
struct nfs_read_data *data = calldata;
|
||||
NFS_PROTO(data->header->inode)->read_rpc_prepare(task, data);
|
||||
if (unlikely(test_bit(NFS_CONTEXT_BAD, &data->args.context->flags)))
|
||||
rpc_exit(task, -EIO);
|
||||
}
|
||||
|
||||
static const struct rpc_call_ops nfs_read_common_ops = {
|
||||
|
|
|
@ -920,7 +920,7 @@ static struct nfs_parsed_mount_data *nfs_alloc_parsed_mount_data(void)
|
|||
data->mount_server.port = NFS_UNSPEC_PORT;
|
||||
data->nfs_server.port = NFS_UNSPEC_PORT;
|
||||
data->nfs_server.protocol = XPRT_TRANSPORT_TCP;
|
||||
data->auth_flavors[0] = RPC_AUTH_UNIX;
|
||||
data->auth_flavors[0] = RPC_AUTH_MAXFLAVOR;
|
||||
data->auth_flavor_len = 1;
|
||||
data->minorversion = 0;
|
||||
data->need_mount = true;
|
||||
|
@ -1608,49 +1608,57 @@ out_security_failure:
|
|||
}
|
||||
|
||||
/*
|
||||
* Match the requested auth flavors with the list returned by
|
||||
* the server. Returns zero and sets the mount's authentication
|
||||
* flavor on success; returns -EACCES if server does not support
|
||||
* the requested flavor.
|
||||
* Select a security flavor for this mount. The selected flavor
|
||||
* is planted in args->auth_flavors[0].
|
||||
*/
|
||||
static int nfs_walk_authlist(struct nfs_parsed_mount_data *args,
|
||||
struct nfs_mount_request *request)
|
||||
static void nfs_select_flavor(struct nfs_parsed_mount_data *args,
|
||||
struct nfs_mount_request *request)
|
||||
{
|
||||
unsigned int i, j, server_authlist_len = *(request->auth_flav_len);
|
||||
unsigned int i, count = *(request->auth_flav_len);
|
||||
rpc_authflavor_t flavor;
|
||||
|
||||
if (args->auth_flavors[0] != RPC_AUTH_MAXFLAVOR)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* The NFSv2 MNT operation does not return a flavor list.
|
||||
*/
|
||||
if (args->mount_server.version != NFS_MNT3_VERSION)
|
||||
goto out_default;
|
||||
|
||||
/*
|
||||
* Certain releases of Linux's mountd return an empty
|
||||
* flavor list. To prevent behavioral regression with
|
||||
* these servers (ie. rejecting mounts that used to
|
||||
* succeed), revert to pre-2.6.32 behavior (no checking)
|
||||
* if the returned flavor list is empty.
|
||||
* flavor list in some cases.
|
||||
*/
|
||||
if (server_authlist_len == 0)
|
||||
return 0;
|
||||
if (count == 0)
|
||||
goto out_default;
|
||||
|
||||
/*
|
||||
* We avoid sophisticated negotiating here, as there are
|
||||
* plenty of cases where we can get it wrong, providing
|
||||
* either too little or too much security.
|
||||
*
|
||||
* RFC 2623, section 2.7 suggests we SHOULD prefer the
|
||||
* flavor listed first. However, some servers list
|
||||
* AUTH_NULL first. Our caller plants AUTH_SYS, the
|
||||
* preferred default, in args->auth_flavors[0] if user
|
||||
* didn't specify sec= mount option.
|
||||
* AUTH_NULL first. Avoid ever choosing AUTH_NULL.
|
||||
*/
|
||||
for (i = 0; i < args->auth_flavor_len; i++)
|
||||
for (j = 0; j < server_authlist_len; j++)
|
||||
if (args->auth_flavors[i] == request->auth_flavs[j]) {
|
||||
dfprintk(MOUNT, "NFS: using auth flavor %d\n",
|
||||
request->auth_flavs[j]);
|
||||
args->auth_flavors[0] = request->auth_flavs[j];
|
||||
return 0;
|
||||
}
|
||||
for (i = 0; i < count; i++) {
|
||||
struct rpcsec_gss_info info;
|
||||
|
||||
dfprintk(MOUNT, "NFS: server does not support requested auth flavor\n");
|
||||
nfs_umount(request);
|
||||
return -EACCES;
|
||||
flavor = request->auth_flavs[i];
|
||||
switch (flavor) {
|
||||
case RPC_AUTH_UNIX:
|
||||
goto out_set;
|
||||
case RPC_AUTH_NULL:
|
||||
continue;
|
||||
default:
|
||||
if (rpcauth_get_gssinfo(flavor, &info) == 0)
|
||||
goto out_set;
|
||||
}
|
||||
}
|
||||
|
||||
out_default:
|
||||
flavor = RPC_AUTH_UNIX;
|
||||
out_set:
|
||||
args->auth_flavors[0] = flavor;
|
||||
out:
|
||||
dfprintk(MOUNT, "NFS: using auth flavor %d\n", args->auth_flavors[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1713,12 +1721,8 @@ static int nfs_request_mount(struct nfs_parsed_mount_data *args,
|
|||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* MNTv1 (NFSv2) does not support auth flavor negotiation.
|
||||
*/
|
||||
if (args->mount_server.version != NFS_MNT3_VERSION)
|
||||
return 0;
|
||||
return nfs_walk_authlist(args, &request);
|
||||
nfs_select_flavor(args, &request);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dentry *nfs_try_mount(int flags, const char *dev_name,
|
||||
|
|
|
@ -1251,6 +1251,8 @@ void nfs_write_prepare(struct rpc_task *task, void *calldata)
|
|||
{
|
||||
struct nfs_write_data *data = calldata;
|
||||
NFS_PROTO(data->header->inode)->write_rpc_prepare(task, data);
|
||||
if (unlikely(test_bit(NFS_CONTEXT_BAD, &data->args.context->flags)))
|
||||
rpc_exit(task, -EIO);
|
||||
}
|
||||
|
||||
void nfs_commit_prepare(struct rpc_task *task, void *calldata)
|
||||
|
|
|
@ -3138,10 +3138,9 @@ nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_
|
|||
|
||||
static __be32
|
||||
nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp,
|
||||
__be32 nfserr,struct svc_export *exp)
|
||||
__be32 nfserr, struct svc_export *exp)
|
||||
{
|
||||
int i = 0;
|
||||
u32 nflavs;
|
||||
u32 i, nflavs;
|
||||
struct exp_flavor_info *flavs;
|
||||
struct exp_flavor_info def_flavs[2];
|
||||
__be32 *p;
|
||||
|
@ -3172,30 +3171,29 @@ nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp,
|
|||
WRITE32(nflavs);
|
||||
ADJUST_ARGS();
|
||||
for (i = 0; i < nflavs; i++) {
|
||||
u32 flav = flavs[i].pseudoflavor;
|
||||
struct gss_api_mech *gm = gss_mech_get_by_pseudoflavor(flav);
|
||||
struct rpcsec_gss_info info;
|
||||
|
||||
if (gm) {
|
||||
if (rpcauth_get_gssinfo(flavs[i].pseudoflavor, &info) == 0) {
|
||||
RESERVE_SPACE(4);
|
||||
WRITE32(RPC_AUTH_GSS);
|
||||
ADJUST_ARGS();
|
||||
RESERVE_SPACE(4 + gm->gm_oid.len);
|
||||
WRITE32(gm->gm_oid.len);
|
||||
WRITEMEM(gm->gm_oid.data, gm->gm_oid.len);
|
||||
RESERVE_SPACE(4 + info.oid.len);
|
||||
WRITE32(info.oid.len);
|
||||
WRITEMEM(info.oid.data, info.oid.len);
|
||||
ADJUST_ARGS();
|
||||
RESERVE_SPACE(4);
|
||||
WRITE32(0); /* qop */
|
||||
WRITE32(info.qop);
|
||||
ADJUST_ARGS();
|
||||
RESERVE_SPACE(4);
|
||||
WRITE32(gss_pseudoflavor_to_service(gm, flav));
|
||||
WRITE32(info.service);
|
||||
ADJUST_ARGS();
|
||||
gss_mech_put(gm);
|
||||
} else {
|
||||
RESERVE_SPACE(4);
|
||||
WRITE32(flav);
|
||||
WRITE32(flavs[i].pseudoflavor);
|
||||
ADJUST_ARGS();
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (exp)
|
||||
exp_put(exp);
|
||||
|
|
|
@ -59,11 +59,18 @@ struct nfs_lockowner {
|
|||
pid_t l_pid;
|
||||
};
|
||||
|
||||
#define NFS_IO_INPROGRESS 0
|
||||
struct nfs_io_counter {
|
||||
unsigned long flags;
|
||||
atomic_t io_count;
|
||||
};
|
||||
|
||||
struct nfs_lock_context {
|
||||
atomic_t count;
|
||||
struct list_head list;
|
||||
struct nfs_open_context *open_context;
|
||||
struct nfs_lockowner lockowner;
|
||||
struct nfs_io_counter io_count;
|
||||
};
|
||||
|
||||
struct nfs4_state;
|
||||
|
@ -77,6 +84,7 @@ struct nfs_open_context {
|
|||
unsigned long flags;
|
||||
#define NFS_CONTEXT_ERROR_WRITE (0)
|
||||
#define NFS_CONTEXT_RESEND_WRITES (1)
|
||||
#define NFS_CONTEXT_BAD (2)
|
||||
int error;
|
||||
|
||||
struct list_head list;
|
||||
|
|
|
@ -40,6 +40,7 @@ struct nfs_client {
|
|||
#define NFS_CS_NORESVPORT 0 /* - use ephemeral src port */
|
||||
#define NFS_CS_DISCRTRY 1 /* - disconnect on RPC retry */
|
||||
#define NFS_CS_MIGRATION 2 /* - transparent state migr */
|
||||
#define NFS_CS_INFINITE_SLOTS 3 /* - don't limit TCP slots */
|
||||
struct sockaddr_storage cl_addr; /* server identifier */
|
||||
size_t cl_addrlen;
|
||||
char * cl_hostname; /* hostname of server */
|
||||
|
@ -197,5 +198,7 @@ struct nfs_server {
|
|||
#define NFS_CAP_MTIME (1U << 13)
|
||||
#define NFS_CAP_POSIX_LOCK (1U << 14)
|
||||
#define NFS_CAP_UIDGID_NOMAP (1U << 15)
|
||||
#define NFS_CAP_STATEID_NFSV41 (1U << 16)
|
||||
#define NFS_CAP_ATOMIC_OPEN_V1 (1U << 17)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
#define NFS_DEF_FILE_IO_SIZE (4096U)
|
||||
#define NFS_MIN_FILE_IO_SIZE (1024U)
|
||||
|
||||
/* Forward declaration for NFS v3 */
|
||||
struct nfs4_secinfo_flavors;
|
||||
|
||||
struct nfs4_string {
|
||||
unsigned int len;
|
||||
char *data;
|
||||
|
@ -349,6 +346,7 @@ struct nfs_openargs {
|
|||
const u32 * bitmask;
|
||||
const u32 * open_bitmap;
|
||||
__u32 claim;
|
||||
enum createmode4 createmode;
|
||||
};
|
||||
|
||||
struct nfs_openres {
|
||||
|
@ -486,6 +484,7 @@ struct nfs_readargs {
|
|||
struct nfs_fh * fh;
|
||||
struct nfs_open_context *context;
|
||||
struct nfs_lock_context *lock_context;
|
||||
nfs4_stateid stateid;
|
||||
__u64 offset;
|
||||
__u32 count;
|
||||
unsigned int pgbase;
|
||||
|
@ -507,6 +506,7 @@ struct nfs_writeargs {
|
|||
struct nfs_fh * fh;
|
||||
struct nfs_open_context *context;
|
||||
struct nfs_lock_context *lock_context;
|
||||
nfs4_stateid stateid;
|
||||
__u64 offset;
|
||||
__u32 count;
|
||||
enum nfs3_stable_how stable;
|
||||
|
@ -1050,25 +1050,14 @@ struct nfs4_fs_locations_res {
|
|||
struct nfs4_fs_locations *fs_locations;
|
||||
};
|
||||
|
||||
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_secinfo4 {
|
||||
u32 flavor;
|
||||
struct rpcsec_gss_info flavor_info;
|
||||
};
|
||||
|
||||
struct nfs4_secinfo_flavors {
|
||||
unsigned int num_flavors;
|
||||
struct nfs4_secinfo_flavor flavors[0];
|
||||
unsigned int num_flavors;
|
||||
struct nfs4_secinfo4 flavors[0];
|
||||
};
|
||||
|
||||
struct nfs4_secinfo_arg {
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
/* size of the nodename buffer */
|
||||
#define UNX_MAXNODENAME 32
|
||||
|
||||
struct rpcsec_gss_info;
|
||||
|
||||
/* Work around the lack of a VFS credential */
|
||||
struct auth_cred {
|
||||
kuid_t uid;
|
||||
|
@ -103,6 +105,9 @@ struct rpc_authops {
|
|||
int (*pipes_create)(struct rpc_auth *);
|
||||
void (*pipes_destroy)(struct rpc_auth *);
|
||||
int (*list_pseudoflavors)(rpc_authflavor_t *, int);
|
||||
rpc_authflavor_t (*info2flavor)(struct rpcsec_gss_info *);
|
||||
int (*flavor2info)(rpc_authflavor_t,
|
||||
struct rpcsec_gss_info *);
|
||||
};
|
||||
|
||||
struct rpc_credops {
|
||||
|
@ -137,6 +142,10 @@ int rpcauth_register(const struct rpc_authops *);
|
|||
int rpcauth_unregister(const struct rpc_authops *);
|
||||
struct rpc_auth * rpcauth_create(rpc_authflavor_t, struct rpc_clnt *);
|
||||
void rpcauth_release(struct rpc_auth *);
|
||||
rpc_authflavor_t rpcauth_get_pseudoflavor(rpc_authflavor_t,
|
||||
struct rpcsec_gss_info *);
|
||||
int rpcauth_get_gssinfo(rpc_authflavor_t,
|
||||
struct rpcsec_gss_info *);
|
||||
int rpcauth_list_flavors(rpc_authflavor_t *, int);
|
||||
struct rpc_cred * rpcauth_lookup_credcache(struct rpc_auth *, struct auth_cred *, int);
|
||||
void rpcauth_init_cred(struct rpc_cred *, const struct auth_cred *, struct rpc_auth *, const struct rpc_credops *);
|
||||
|
|
|
@ -124,6 +124,7 @@ struct rpc_create_args {
|
|||
#define RPC_CLNT_CREATE_NOPING (1UL << 4)
|
||||
#define RPC_CLNT_CREATE_DISCRTRY (1UL << 5)
|
||||
#define RPC_CLNT_CREATE_QUIET (1UL << 6)
|
||||
#define RPC_CLNT_CREATE_INFINITE_SLOTS (1UL << 7)
|
||||
|
||||
struct rpc_clnt *rpc_create(struct rpc_create_args *args);
|
||||
struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *,
|
||||
|
|
|
@ -25,10 +25,21 @@ struct gss_ctx {
|
|||
|
||||
#define GSS_C_NO_BUFFER ((struct xdr_netobj) 0)
|
||||
#define GSS_C_NO_CONTEXT ((struct gss_ctx *) 0)
|
||||
#define GSS_C_NULL_OID ((struct xdr_netobj) 0)
|
||||
#define GSS_C_QOP_DEFAULT (0)
|
||||
|
||||
/*XXX arbitrary length - is this set somewhere? */
|
||||
#define GSS_OID_MAX_LEN 32
|
||||
struct rpcsec_gss_oid {
|
||||
unsigned int len;
|
||||
u8 data[GSS_OID_MAX_LEN];
|
||||
};
|
||||
|
||||
/* From RFC 3530 */
|
||||
struct rpcsec_gss_info {
|
||||
struct rpcsec_gss_oid oid;
|
||||
u32 qop;
|
||||
u32 service;
|
||||
};
|
||||
|
||||
/* gss-api prototypes; note that these are somewhat simplified versions of
|
||||
* the prototypes specified in RFC 2744. */
|
||||
|
@ -58,12 +69,14 @@ u32 gss_unwrap(
|
|||
u32 gss_delete_sec_context(
|
||||
struct gss_ctx **ctx_id);
|
||||
|
||||
u32 gss_svc_to_pseudoflavor(struct gss_api_mech *, u32 service);
|
||||
rpc_authflavor_t gss_svc_to_pseudoflavor(struct gss_api_mech *, u32 qop,
|
||||
u32 service);
|
||||
u32 gss_pseudoflavor_to_service(struct gss_api_mech *, u32 pseudoflavor);
|
||||
char *gss_service_to_auth_domain_name(struct gss_api_mech *, u32 service);
|
||||
|
||||
struct pf_desc {
|
||||
u32 pseudoflavor;
|
||||
u32 qop;
|
||||
u32 service;
|
||||
char *name;
|
||||
char *auth_domain_name;
|
||||
|
@ -76,7 +89,7 @@ struct pf_desc {
|
|||
struct gss_api_mech {
|
||||
struct list_head gm_list;
|
||||
struct module *gm_owner;
|
||||
struct xdr_netobj gm_oid;
|
||||
struct rpcsec_gss_oid gm_oid;
|
||||
char *gm_name;
|
||||
const struct gss_api_ops *gm_ops;
|
||||
/* pseudoflavors supported by this mechanism: */
|
||||
|
@ -117,9 +130,11 @@ struct gss_api_ops {
|
|||
int gss_mech_register(struct gss_api_mech *);
|
||||
void gss_mech_unregister(struct gss_api_mech *);
|
||||
|
||||
/* returns a mechanism descriptor given an OID, and increments the mechanism's
|
||||
* reference count. */
|
||||
struct gss_api_mech * gss_mech_get_by_OID(struct xdr_netobj *);
|
||||
/* Given a GSS security tuple, look up a pseudoflavor */
|
||||
rpc_authflavor_t gss_mech_info2flavor(struct rpcsec_gss_info *);
|
||||
|
||||
/* Given a pseudoflavor, look up a GSS security tuple */
|
||||
int gss_mech_flavor2info(rpc_authflavor_t, struct rpcsec_gss_info *);
|
||||
|
||||
/* Returns a reference to a mechanism, given a name like "krb5" etc. */
|
||||
struct gss_api_mech *gss_mech_get_by_name(const char *);
|
||||
|
@ -130,9 +145,6 @@ 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(rpc_authflavor_t *, int);
|
||||
|
||||
/* Just increments the mechanism's reference count and returns its input: */
|
||||
struct gss_api_mech * gss_mech_get(struct gss_api_mech *);
|
||||
|
||||
/* For every successful gss_mech_get or gss_mech_get_by_* call there must be a
|
||||
* corresponding call to gss_mech_put. */
|
||||
void gss_mech_put(struct gss_api_mech *);
|
||||
|
|
|
@ -255,6 +255,8 @@ static inline int bc_prealloc(struct rpc_rqst *req)
|
|||
}
|
||||
#endif /* CONFIG_SUNRPC_BACKCHANNEL */
|
||||
|
||||
#define XPRT_CREATE_INFINITE_SLOTS (1U)
|
||||
|
||||
struct xprt_create {
|
||||
int ident; /* XPRT_TRANSPORT identifier */
|
||||
struct net * net;
|
||||
|
@ -263,6 +265,7 @@ struct xprt_create {
|
|||
size_t addrlen;
|
||||
const char *servername;
|
||||
struct svc_xprt *bc_xprt; /* NFSv4.1 backchannel */
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
struct xprt_class {
|
||||
|
@ -279,6 +282,7 @@ struct xprt_class {
|
|||
struct rpc_xprt *xprt_create_transport(struct xprt_create *args);
|
||||
void xprt_connect(struct rpc_task *task);
|
||||
void xprt_reserve(struct rpc_task *task);
|
||||
void xprt_retry_reserve(struct rpc_task *task);
|
||||
int xprt_reserve_xprt(struct rpc_xprt *xprt, struct rpc_task *task);
|
||||
int xprt_reserve_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task);
|
||||
void xprt_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task);
|
||||
|
@ -334,6 +338,7 @@ int xs_swapper(struct rpc_xprt *xprt, int enable);
|
|||
#define XPRT_CLOSING (6)
|
||||
#define XPRT_CONNECTION_ABORT (7)
|
||||
#define XPRT_CONNECTION_CLOSE (8)
|
||||
#define XPRT_CONGESTED (9)
|
||||
|
||||
static inline void xprt_set_connected(struct rpc_xprt *xprt)
|
||||
{
|
||||
|
|
|
@ -3,6 +3,7 @@ config SUNRPC
|
|||
|
||||
config SUNRPC_GSS
|
||||
tristate
|
||||
select OID_REGISTRY
|
||||
|
||||
config SUNRPC_BACKCHANNEL
|
||||
bool
|
||||
|
@ -24,7 +25,6 @@ config SUNRPC_XPRT_RDMA
|
|||
config SUNRPC_SWAP
|
||||
bool
|
||||
depends on SUNRPC
|
||||
select NETVM
|
||||
|
||||
config RPCSEC_GSS_KRB5
|
||||
tristate "Secure RPC: Kerberos V mechanism"
|
||||
|
|
|
@ -82,7 +82,7 @@ MODULE_PARM_DESC(auth_hashtable_size, "RPC credential cache hashtable size");
|
|||
|
||||
static u32
|
||||
pseudoflavor_to_flavor(u32 flavor) {
|
||||
if (flavor >= RPC_AUTH_MAXFLAVOR)
|
||||
if (flavor > RPC_AUTH_MAXFLAVOR)
|
||||
return RPC_AUTH_GSS;
|
||||
return flavor;
|
||||
}
|
||||
|
@ -123,6 +123,79 @@ rpcauth_unregister(const struct rpc_authops *ops)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(rpcauth_unregister);
|
||||
|
||||
/**
|
||||
* rpcauth_get_pseudoflavor - check if security flavor is supported
|
||||
* @flavor: a security flavor
|
||||
* @info: a GSS mech OID, quality of protection, and service value
|
||||
*
|
||||
* Verifies that an appropriate kernel module is available or already loaded.
|
||||
* Returns an equivalent pseudoflavor, or RPC_AUTH_MAXFLAVOR if "flavor" is
|
||||
* not supported locally.
|
||||
*/
|
||||
rpc_authflavor_t
|
||||
rpcauth_get_pseudoflavor(rpc_authflavor_t flavor, struct rpcsec_gss_info *info)
|
||||
{
|
||||
const struct rpc_authops *ops;
|
||||
rpc_authflavor_t pseudoflavor;
|
||||
|
||||
ops = auth_flavors[flavor];
|
||||
if (ops == NULL)
|
||||
request_module("rpc-auth-%u", flavor);
|
||||
spin_lock(&rpc_authflavor_lock);
|
||||
ops = auth_flavors[flavor];
|
||||
if (ops == NULL || !try_module_get(ops->owner)) {
|
||||
spin_unlock(&rpc_authflavor_lock);
|
||||
return RPC_AUTH_MAXFLAVOR;
|
||||
}
|
||||
spin_unlock(&rpc_authflavor_lock);
|
||||
|
||||
pseudoflavor = flavor;
|
||||
if (ops->info2flavor != NULL)
|
||||
pseudoflavor = ops->info2flavor(info);
|
||||
|
||||
module_put(ops->owner);
|
||||
return pseudoflavor;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rpcauth_get_pseudoflavor);
|
||||
|
||||
/**
|
||||
* rpcauth_get_gssinfo - find GSS tuple matching a GSS pseudoflavor
|
||||
* @pseudoflavor: GSS pseudoflavor to match
|
||||
* @info: rpcsec_gss_info structure to fill in
|
||||
*
|
||||
* Returns zero and fills in "info" if pseudoflavor matches a
|
||||
* supported mechanism.
|
||||
*/
|
||||
int
|
||||
rpcauth_get_gssinfo(rpc_authflavor_t pseudoflavor, struct rpcsec_gss_info *info)
|
||||
{
|
||||
rpc_authflavor_t flavor = pseudoflavor_to_flavor(pseudoflavor);
|
||||
const struct rpc_authops *ops;
|
||||
int result;
|
||||
|
||||
if (flavor >= RPC_AUTH_MAXFLAVOR)
|
||||
return -EINVAL;
|
||||
|
||||
ops = auth_flavors[flavor];
|
||||
if (ops == NULL)
|
||||
request_module("rpc-auth-%u", flavor);
|
||||
spin_lock(&rpc_authflavor_lock);
|
||||
ops = auth_flavors[flavor];
|
||||
if (ops == NULL || !try_module_get(ops->owner)) {
|
||||
spin_unlock(&rpc_authflavor_lock);
|
||||
return -ENOENT;
|
||||
}
|
||||
spin_unlock(&rpc_authflavor_lock);
|
||||
|
||||
result = -ENOENT;
|
||||
if (ops->flavor2info != NULL)
|
||||
result = ops->flavor2info(pseudoflavor, info);
|
||||
|
||||
module_put(ops->owner);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rpcauth_get_gssinfo);
|
||||
|
||||
/**
|
||||
* rpcauth_list_flavors - discover registered flavors and pseudoflavors
|
||||
* @array: array to fill in
|
||||
|
|
|
@ -1641,6 +1641,8 @@ static const struct rpc_authops authgss_ops = {
|
|||
.pipes_create = gss_pipes_dentries_create,
|
||||
.pipes_destroy = gss_pipes_dentries_destroy,
|
||||
.list_pseudoflavors = gss_mech_list_pseudoflavors,
|
||||
.info2flavor = gss_mech_info2flavor,
|
||||
.flavor2info = gss_mech_flavor2info,
|
||||
};
|
||||
|
||||
static const struct rpc_credops gss_credops = {
|
||||
|
@ -1733,6 +1735,7 @@ static void __exit exit_rpcsec_gss(void)
|
|||
rcu_barrier(); /* Wait for completion of call_rcu()'s */
|
||||
}
|
||||
|
||||
MODULE_ALIAS("rpc-auth-6");
|
||||
MODULE_LICENSE("GPL");
|
||||
module_param_named(expired_cred_retry_delay,
|
||||
gss_expired_cred_retry_delay,
|
||||
|
|
|
@ -729,16 +729,19 @@ static const struct gss_api_ops gss_kerberos_ops = {
|
|||
static struct pf_desc gss_kerberos_pfs[] = {
|
||||
[0] = {
|
||||
.pseudoflavor = RPC_AUTH_GSS_KRB5,
|
||||
.qop = GSS_C_QOP_DEFAULT,
|
||||
.service = RPC_GSS_SVC_NONE,
|
||||
.name = "krb5",
|
||||
},
|
||||
[1] = {
|
||||
.pseudoflavor = RPC_AUTH_GSS_KRB5I,
|
||||
.qop = GSS_C_QOP_DEFAULT,
|
||||
.service = RPC_GSS_SVC_INTEGRITY,
|
||||
.name = "krb5i",
|
||||
},
|
||||
[2] = {
|
||||
.pseudoflavor = RPC_AUTH_GSS_KRB5P,
|
||||
.qop = GSS_C_QOP_DEFAULT,
|
||||
.service = RPC_GSS_SVC_PRIVACY,
|
||||
.name = "krb5p",
|
||||
},
|
||||
|
@ -750,11 +753,12 @@ MODULE_ALIAS("rpc-auth-gss-krb5p");
|
|||
MODULE_ALIAS("rpc-auth-gss-390003");
|
||||
MODULE_ALIAS("rpc-auth-gss-390004");
|
||||
MODULE_ALIAS("rpc-auth-gss-390005");
|
||||
MODULE_ALIAS("rpc-auth-gss-1.2.840.113554.1.2.2");
|
||||
|
||||
static struct gss_api_mech gss_kerberos_mech = {
|
||||
.gm_name = "krb5",
|
||||
.gm_owner = THIS_MODULE,
|
||||
.gm_oid = {9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"},
|
||||
.gm_oid = { 9, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" },
|
||||
.gm_ops = &gss_kerberos_ops,
|
||||
.gm_pf_num = ARRAY_SIZE(gss_kerberos_pfs),
|
||||
.gm_pfs = gss_kerberos_pfs,
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/oid_registry.h>
|
||||
#include <linux/sunrpc/msg_prot.h>
|
||||
#include <linux/sunrpc/gss_asn1.h>
|
||||
#include <linux/sunrpc/auth_gss.h>
|
||||
|
@ -102,8 +103,13 @@ out:
|
|||
return status;
|
||||
}
|
||||
|
||||
int
|
||||
gss_mech_register(struct gss_api_mech *gm)
|
||||
/**
|
||||
* gss_mech_register - register a GSS mechanism
|
||||
* @gm: GSS mechanism handle
|
||||
*
|
||||
* Returns zero if successful, or a negative errno.
|
||||
*/
|
||||
int gss_mech_register(struct gss_api_mech *gm)
|
||||
{
|
||||
int status;
|
||||
|
||||
|
@ -116,11 +122,14 @@ gss_mech_register(struct gss_api_mech *gm)
|
|||
dprintk("RPC: registered gss mechanism %s\n", gm->gm_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(gss_mech_register);
|
||||
|
||||
void
|
||||
gss_mech_unregister(struct gss_api_mech *gm)
|
||||
/**
|
||||
* gss_mech_unregister - release a GSS mechanism
|
||||
* @gm: GSS mechanism handle
|
||||
*
|
||||
*/
|
||||
void gss_mech_unregister(struct gss_api_mech *gm)
|
||||
{
|
||||
spin_lock(®istered_mechs_lock);
|
||||
list_del(&gm->gm_list);
|
||||
|
@ -128,18 +137,14 @@ gss_mech_unregister(struct gss_api_mech *gm)
|
|||
dprintk("RPC: unregistered gss mechanism %s\n", gm->gm_name);
|
||||
gss_mech_free(gm);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(gss_mech_unregister);
|
||||
|
||||
struct gss_api_mech *
|
||||
gss_mech_get(struct gss_api_mech *gm)
|
||||
static struct gss_api_mech *gss_mech_get(struct gss_api_mech *gm)
|
||||
{
|
||||
__module_get(gm->gm_owner);
|
||||
return gm;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(gss_mech_get);
|
||||
|
||||
static struct gss_api_mech *
|
||||
_gss_mech_get_by_name(const char *name)
|
||||
{
|
||||
|
@ -169,12 +174,16 @@ struct gss_api_mech * gss_mech_get_by_name(const char *name)
|
|||
}
|
||||
return gm;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gss_mech_get_by_name);
|
||||
|
||||
struct gss_api_mech *
|
||||
gss_mech_get_by_OID(struct xdr_netobj *obj)
|
||||
static struct gss_api_mech *gss_mech_get_by_OID(struct rpcsec_gss_oid *obj)
|
||||
{
|
||||
struct gss_api_mech *pos, *gm = NULL;
|
||||
char buf[32];
|
||||
|
||||
if (sprint_oid(obj->data, obj->len, buf, sizeof(buf)) < 0)
|
||||
return NULL;
|
||||
dprintk("RPC: %s(%s)\n", __func__, buf);
|
||||
request_module("rpc-auth-gss-%s", buf);
|
||||
|
||||
spin_lock(®istered_mechs_lock);
|
||||
list_for_each_entry(pos, ®istered_mechs, gm_list) {
|
||||
|
@ -188,11 +197,8 @@ gss_mech_get_by_OID(struct xdr_netobj *obj)
|
|||
}
|
||||
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)
|
||||
{
|
||||
|
@ -237,8 +243,6 @@ gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
|
|||
return gm;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(gss_mech_get_by_pseudoflavor);
|
||||
|
||||
/**
|
||||
* gss_mech_list_pseudoflavors - Discover registered GSS pseudoflavors
|
||||
* @array: array to fill in
|
||||
|
@ -268,19 +272,82 @@ int gss_mech_list_pseudoflavors(rpc_authflavor_t *array_ptr, int size)
|
|||
return i;
|
||||
}
|
||||
|
||||
u32
|
||||
gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 service)
|
||||
/**
|
||||
* gss_svc_to_pseudoflavor - map a GSS service number to a pseudoflavor
|
||||
* @gm: GSS mechanism handle
|
||||
* @qop: GSS quality-of-protection value
|
||||
* @service: GSS service value
|
||||
*
|
||||
* Returns a matching security flavor, or RPC_AUTH_MAXFLAVOR if none is found.
|
||||
*/
|
||||
rpc_authflavor_t gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 qop,
|
||||
u32 service)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < gm->gm_pf_num; i++) {
|
||||
if (gm->gm_pfs[i].service == service) {
|
||||
if (gm->gm_pfs[i].qop == qop &&
|
||||
gm->gm_pfs[i].service == service) {
|
||||
return gm->gm_pfs[i].pseudoflavor;
|
||||
}
|
||||
}
|
||||
return RPC_AUTH_MAXFLAVOR; /* illegal value */
|
||||
return RPC_AUTH_MAXFLAVOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* gss_mech_info2flavor - look up a pseudoflavor given a GSS tuple
|
||||
* @info: a GSS mech OID, quality of protection, and service value
|
||||
*
|
||||
* Returns a matching pseudoflavor, or RPC_AUTH_MAXFLAVOR if the tuple is
|
||||
* not supported.
|
||||
*/
|
||||
rpc_authflavor_t gss_mech_info2flavor(struct rpcsec_gss_info *info)
|
||||
{
|
||||
rpc_authflavor_t pseudoflavor;
|
||||
struct gss_api_mech *gm;
|
||||
|
||||
gm = gss_mech_get_by_OID(&info->oid);
|
||||
if (gm == NULL)
|
||||
return RPC_AUTH_MAXFLAVOR;
|
||||
|
||||
pseudoflavor = gss_svc_to_pseudoflavor(gm, info->qop, info->service);
|
||||
|
||||
gss_mech_put(gm);
|
||||
return pseudoflavor;
|
||||
}
|
||||
|
||||
/**
|
||||
* gss_mech_flavor2info - look up a GSS tuple for a given pseudoflavor
|
||||
* @pseudoflavor: GSS pseudoflavor to match
|
||||
* @info: rpcsec_gss_info structure to fill in
|
||||
*
|
||||
* Returns zero and fills in "info" if pseudoflavor matches a
|
||||
* supported mechanism. Otherwise a negative errno is returned.
|
||||
*/
|
||||
int gss_mech_flavor2info(rpc_authflavor_t pseudoflavor,
|
||||
struct rpcsec_gss_info *info)
|
||||
{
|
||||
struct gss_api_mech *gm;
|
||||
int i;
|
||||
|
||||
gm = gss_mech_get_by_pseudoflavor(pseudoflavor);
|
||||
if (gm == NULL)
|
||||
return -ENOENT;
|
||||
|
||||
for (i = 0; i < gm->gm_pf_num; i++) {
|
||||
if (gm->gm_pfs[i].pseudoflavor == pseudoflavor) {
|
||||
memcpy(info->oid.data, gm->gm_oid.data, gm->gm_oid.len);
|
||||
info->oid.len = gm->gm_oid.len;
|
||||
info->qop = gm->gm_pfs[i].qop;
|
||||
info->service = gm->gm_pfs[i].service;
|
||||
gss_mech_put(gm);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
gss_mech_put(gm);
|
||||
return -ENOENT;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gss_svc_to_pseudoflavor);
|
||||
|
||||
u32
|
||||
gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor)
|
||||
|
@ -294,8 +361,6 @@ gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor)
|
|||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(gss_pseudoflavor_to_service);
|
||||
|
||||
char *
|
||||
gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service)
|
||||
{
|
||||
|
@ -308,8 +373,6 @@ gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(gss_service_to_auth_domain_name);
|
||||
|
||||
void
|
||||
gss_mech_put(struct gss_api_mech * gm)
|
||||
{
|
||||
|
@ -317,8 +380,6 @@ gss_mech_put(struct gss_api_mech * gm)
|
|||
module_put(gm->gm_owner);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(gss_mech_put);
|
||||
|
||||
/* The mech could probably be determined from the token instead, but it's just
|
||||
* as easy for now to pass it in. */
|
||||
int
|
||||
|
|
|
@ -1220,7 +1220,9 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
|
|||
svcdata->rsci = rsci;
|
||||
cache_get(&rsci->h);
|
||||
rqstp->rq_cred.cr_flavor = gss_svc_to_pseudoflavor(
|
||||
rsci->mechctx->mech_type, gc->gc_svc);
|
||||
rsci->mechctx->mech_type,
|
||||
GSS_C_QOP_DEFAULT,
|
||||
gc->gc_svc);
|
||||
ret = SVC_OK;
|
||||
goto out;
|
||||
}
|
||||
|
|
|
@ -411,6 +411,8 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args)
|
|||
};
|
||||
char servername[48];
|
||||
|
||||
if (args->flags & RPC_CLNT_CREATE_INFINITE_SLOTS)
|
||||
xprtargs.flags |= XPRT_CREATE_INFINITE_SLOTS;
|
||||
/*
|
||||
* If the caller chooses not to specify a hostname, whip
|
||||
* up a string representation of the passed-in address.
|
||||
|
@ -1301,6 +1303,8 @@ call_reserve(struct rpc_task *task)
|
|||
xprt_reserve(task);
|
||||
}
|
||||
|
||||
static void call_retry_reserve(struct rpc_task *task);
|
||||
|
||||
/*
|
||||
* 1b. Grok the result of xprt_reserve()
|
||||
*/
|
||||
|
@ -1342,7 +1346,7 @@ call_reserveresult(struct rpc_task *task)
|
|||
case -ENOMEM:
|
||||
rpc_delay(task, HZ >> 2);
|
||||
case -EAGAIN: /* woken up; retry */
|
||||
task->tk_action = call_reserve;
|
||||
task->tk_action = call_retry_reserve;
|
||||
return;
|
||||
case -EIO: /* probably a shutdown */
|
||||
break;
|
||||
|
@ -1354,6 +1358,19 @@ call_reserveresult(struct rpc_task *task)
|
|||
rpc_exit(task, status);
|
||||
}
|
||||
|
||||
/*
|
||||
* 1c. Retry reserving an RPC call slot
|
||||
*/
|
||||
static void
|
||||
call_retry_reserve(struct rpc_task *task)
|
||||
{
|
||||
dprint_status(task);
|
||||
|
||||
task->tk_status = 0;
|
||||
task->tk_action = call_reserveresult;
|
||||
xprt_retry_reserve(task);
|
||||
}
|
||||
|
||||
/*
|
||||
* 2. Bind and/or refresh the credentials
|
||||
*/
|
||||
|
@ -1639,22 +1656,26 @@ call_connect_status(struct rpc_task *task)
|
|||
|
||||
dprint_status(task);
|
||||
|
||||
task->tk_status = 0;
|
||||
if (status >= 0 || status == -EAGAIN) {
|
||||
clnt->cl_stats->netreconn++;
|
||||
task->tk_action = call_transmit;
|
||||
return;
|
||||
}
|
||||
|
||||
trace_rpc_connect_status(task, status);
|
||||
switch (status) {
|
||||
/* if soft mounted, test if we've timed out */
|
||||
case -ETIMEDOUT:
|
||||
task->tk_action = call_timeout;
|
||||
break;
|
||||
default:
|
||||
rpc_exit(task, -EIO);
|
||||
return;
|
||||
case -ECONNREFUSED:
|
||||
case -ECONNRESET:
|
||||
case -ENETUNREACH:
|
||||
if (RPC_IS_SOFTCONN(task))
|
||||
break;
|
||||
/* retry with existing socket, after a delay */
|
||||
case 0:
|
||||
case -EAGAIN:
|
||||
task->tk_status = 0;
|
||||
clnt->cl_stats->netreconn++;
|
||||
task->tk_action = call_transmit;
|
||||
return;
|
||||
}
|
||||
rpc_exit(task, status);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -948,6 +948,34 @@ void xprt_transmit(struct rpc_task *task)
|
|||
spin_unlock_bh(&xprt->transport_lock);
|
||||
}
|
||||
|
||||
static void xprt_add_backlog(struct rpc_xprt *xprt, struct rpc_task *task)
|
||||
{
|
||||
set_bit(XPRT_CONGESTED, &xprt->state);
|
||||
rpc_sleep_on(&xprt->backlog, task, NULL);
|
||||
}
|
||||
|
||||
static void xprt_wake_up_backlog(struct rpc_xprt *xprt)
|
||||
{
|
||||
if (rpc_wake_up_next(&xprt->backlog) == NULL)
|
||||
clear_bit(XPRT_CONGESTED, &xprt->state);
|
||||
}
|
||||
|
||||
static bool xprt_throttle_congested(struct rpc_xprt *xprt, struct rpc_task *task)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
if (!test_bit(XPRT_CONGESTED, &xprt->state))
|
||||
goto out;
|
||||
spin_lock(&xprt->reserve_lock);
|
||||
if (test_bit(XPRT_CONGESTED, &xprt->state)) {
|
||||
rpc_sleep_on(&xprt->backlog, task, NULL);
|
||||
ret = true;
|
||||
}
|
||||
spin_unlock(&xprt->reserve_lock);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct rpc_rqst *xprt_dynamic_alloc_slot(struct rpc_xprt *xprt, gfp_t gfp_flags)
|
||||
{
|
||||
struct rpc_rqst *req = ERR_PTR(-EAGAIN);
|
||||
|
@ -992,7 +1020,7 @@ void xprt_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task)
|
|||
task->tk_status = -ENOMEM;
|
||||
break;
|
||||
case -EAGAIN:
|
||||
rpc_sleep_on(&xprt->backlog, task, NULL);
|
||||
xprt_add_backlog(xprt, task);
|
||||
dprintk("RPC: waiting for request slot\n");
|
||||
default:
|
||||
task->tk_status = -EAGAIN;
|
||||
|
@ -1028,7 +1056,7 @@ static void xprt_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req)
|
|||
memset(req, 0, sizeof(*req)); /* mark unused */
|
||||
list_add(&req->rq_list, &xprt->free);
|
||||
}
|
||||
rpc_wake_up_next(&xprt->backlog);
|
||||
xprt_wake_up_backlog(xprt);
|
||||
spin_unlock(&xprt->reserve_lock);
|
||||
}
|
||||
|
||||
|
@ -1092,13 +1120,40 @@ EXPORT_SYMBOL_GPL(xprt_free);
|
|||
* xprt_reserve - allocate an RPC request slot
|
||||
* @task: RPC task requesting a slot allocation
|
||||
*
|
||||
* If no more slots are available, place the task on the transport's
|
||||
* If the transport is marked as being congested, or if no more
|
||||
* slots are available, place the task on the transport's
|
||||
* backlog queue.
|
||||
*/
|
||||
void xprt_reserve(struct rpc_task *task)
|
||||
{
|
||||
struct rpc_xprt *xprt;
|
||||
|
||||
task->tk_status = 0;
|
||||
if (task->tk_rqstp != NULL)
|
||||
return;
|
||||
|
||||
task->tk_timeout = 0;
|
||||
task->tk_status = -EAGAIN;
|
||||
rcu_read_lock();
|
||||
xprt = rcu_dereference(task->tk_client->cl_xprt);
|
||||
if (!xprt_throttle_congested(xprt, task))
|
||||
xprt->ops->alloc_slot(xprt, task);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* xprt_retry_reserve - allocate an RPC request slot
|
||||
* @task: RPC task requesting a slot allocation
|
||||
*
|
||||
* If no more slots are available, place the task on the transport's
|
||||
* backlog queue.
|
||||
* Note that the only difference with xprt_reserve is that we now
|
||||
* ignore the value of the XPRT_CONGESTED flag.
|
||||
*/
|
||||
void xprt_retry_reserve(struct rpc_task *task)
|
||||
{
|
||||
struct rpc_xprt *xprt;
|
||||
|
||||
task->tk_status = 0;
|
||||
if (task->tk_rqstp != NULL)
|
||||
return;
|
||||
|
|
|
@ -2207,10 +2207,6 @@ static void xs_tcp_setup_socket(struct work_struct *work)
|
|||
*/
|
||||
xs_tcp_force_close(xprt);
|
||||
break;
|
||||
case -ECONNREFUSED:
|
||||
case -ECONNRESET:
|
||||
case -ENETUNREACH:
|
||||
/* retry with existing socket, after a delay */
|
||||
case 0:
|
||||
case -EINPROGRESS:
|
||||
case -EALREADY:
|
||||
|
@ -2221,6 +2217,10 @@ static void xs_tcp_setup_socket(struct work_struct *work)
|
|||
/* Happens, for instance, if the user specified a link
|
||||
* local IPv6 address without a scope-id.
|
||||
*/
|
||||
case -ECONNREFUSED:
|
||||
case -ECONNRESET:
|
||||
case -ENETUNREACH:
|
||||
/* retry with existing socket, after a delay */
|
||||
goto out;
|
||||
}
|
||||
out_eagain:
|
||||
|
@ -2767,9 +2767,13 @@ static struct rpc_xprt *xs_setup_tcp(struct xprt_create *args)
|
|||
struct rpc_xprt *xprt;
|
||||
struct sock_xprt *transport;
|
||||
struct rpc_xprt *ret;
|
||||
unsigned int max_slot_table_size = xprt_max_tcp_slot_table_entries;
|
||||
|
||||
if (args->flags & XPRT_CREATE_INFINITE_SLOTS)
|
||||
max_slot_table_size = RPC_MAX_SLOT_TABLE_LIMIT;
|
||||
|
||||
xprt = xs_setup_xprt(args, xprt_tcp_slot_table_entries,
|
||||
xprt_max_tcp_slot_table_entries);
|
||||
max_slot_table_size);
|
||||
if (IS_ERR(xprt))
|
||||
return xprt;
|
||||
transport = container_of(xprt, struct sock_xprt, xprt);
|
||||
|
|
Loading…
Reference in a new issue