Commit 8728f986 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents e72859b8 721ccfb7
......@@ -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 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
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 @@ static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *s
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 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation
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 @@ int nfs_client_return_marked_delegations(struct nfs_client *clp)
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 @@ static int filelayout_async_handle_error(struct rpc_task *task,
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];
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);
secinfo = &flavors->flavors[i];
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)
......
This diff is collapsed.
......@@ -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 @@ static bool nfs4_copy_lock_stateid(nfs4_stateid *dst, struct nfs4_state *state,
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 @@ static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs
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 @@ static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs
* 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 @@ int nfs4_discover_server_trunking(struct nfs_client *clp,
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 @@ int nfs4_discover_server_trunking(struct nfs_client *clp,
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 @@ int nfs4_discover_server_trunking(struct nfs_client *clp,
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,
......
This diff is collapsed.
......@@ -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 @@ static int nfs_parse_mount_options(char *raw,
}
/*
* 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;
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;
}
}
dfprintk(MOUNT, "NFS: server does not support requested auth flavor\n");
nfs_umount(request);
return -EACCES;
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 @@ gss_mech_svc_setup(struct gss_api_mech *gm)
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(&registered_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(&registered_mechs_lock);
list_for_each_entry(pos, &registered_mechs, gm_list) {
......@@ -188,11 +197,8 @@ gss_mech_get_by_OID(struct xdr_netobj *obj)
}
spin_unlock(&registered_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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment