Merge git://git.linux-nfs.org/pub/linux/nfs-2.6

* git://git.linux-nfs.org/pub/linux/nfs-2.6: (122 commits)
  sunrpc: drop BKL around wrap and unwrap
  NFSv4: Make sure unlock is really an unlock when cancelling a lock
  NLM: fix source address of callback to client
  SUNRPC client: add interface for binding to a local address
  SUNRPC server: record the destination address of a request
  SUNRPC: cleanup transport creation argument passing
  NFSv4: Make the NFS state model work with the nosharedcache mount option
  NFS: Error when mounting the same filesystem with different options
  NFS: Add the mount option "nosharecache"
  NFS: Add support for mounting NFSv4 file systems with string options
  NFS: Add final pieces to support in-kernel mount option parsing
  NFS: Introduce generic mount client API
  NFS: Add enums and match tables for mount option parsing
  NFS: Improve debugging output in NFS in-kernel mount client
  NFS: Clean up in-kernel NFS mount
  NFS: Remake nfsroot_mount as a permanent part of NFS client
  SUNRPC: Add a convenient default for the hostname when calling rpc_create()
  SUNRPC: Rename rpcb_getport to be consistent with new rpcb_getport_sync name
  SUNRPC: Rename rpcb_getport_external routine
  SUNRPC: Allow rpcbind requests to be interrupted by a signal.
  ...
This commit is contained in:
Linus Torvalds 2007-07-13 16:46:18 -07:00
commit 16cefa8c38
56 changed files with 3325 additions and 1814 deletions

View File

@ -44,9 +44,8 @@ static struct nsm_handle * nsm_find(const struct sockaddr_in *sin,
*/ */
static struct nlm_host * static struct nlm_host *
nlm_lookup_host(int server, const struct sockaddr_in *sin, nlm_lookup_host(int server, const struct sockaddr_in *sin,
int proto, int version, int proto, int version, const char *hostname,
const char *hostname, int hostname_len, const struct sockaddr_in *ssin)
int hostname_len)
{ {
struct hlist_head *chain; struct hlist_head *chain;
struct hlist_node *pos; struct hlist_node *pos;
@ -54,7 +53,9 @@ nlm_lookup_host(int server, const struct sockaddr_in *sin,
struct nsm_handle *nsm = NULL; struct nsm_handle *nsm = NULL;
int hash; int hash;
dprintk("lockd: nlm_lookup_host(%u.%u.%u.%u, p=%d, v=%d, my role=%s, name=%.*s)\n", dprintk("lockd: nlm_lookup_host("NIPQUAD_FMT"->"NIPQUAD_FMT
", p=%d, v=%d, my role=%s, name=%.*s)\n",
NIPQUAD(ssin->sin_addr.s_addr),
NIPQUAD(sin->sin_addr.s_addr), proto, version, NIPQUAD(sin->sin_addr.s_addr), proto, version,
server? "server" : "client", server? "server" : "client",
hostname_len, hostname_len,
@ -91,6 +92,8 @@ nlm_lookup_host(int server, const struct sockaddr_in *sin,
continue; continue;
if (host->h_server != server) if (host->h_server != server)
continue; continue;
if (!nlm_cmp_addr(&host->h_saddr, ssin))
continue;
/* Move to head of hash chain. */ /* Move to head of hash chain. */
hlist_del(&host->h_hash); hlist_del(&host->h_hash);
@ -118,6 +121,7 @@ nlm_lookup_host(int server, const struct sockaddr_in *sin,
host->h_name = nsm->sm_name; host->h_name = nsm->sm_name;
host->h_addr = *sin; host->h_addr = *sin;
host->h_addr.sin_port = 0; /* ouch! */ host->h_addr.sin_port = 0; /* ouch! */
host->h_saddr = *ssin;
host->h_version = version; host->h_version = version;
host->h_proto = proto; host->h_proto = proto;
host->h_rpcclnt = NULL; host->h_rpcclnt = NULL;
@ -161,15 +165,9 @@ nlm_destroy_host(struct nlm_host *host)
*/ */
nsm_unmonitor(host); nsm_unmonitor(host);
if ((clnt = host->h_rpcclnt) != NULL) { clnt = host->h_rpcclnt;
if (atomic_read(&clnt->cl_users)) { if (clnt != NULL)
printk(KERN_WARNING rpc_shutdown_client(clnt);
"lockd: active RPC handle\n");
clnt->cl_dead = 1;
} else {
rpc_destroy_client(host->h_rpcclnt);
}
}
kfree(host); kfree(host);
} }
@ -180,8 +178,10 @@ struct nlm_host *
nlmclnt_lookup_host(const struct sockaddr_in *sin, int proto, int version, nlmclnt_lookup_host(const struct sockaddr_in *sin, int proto, int version,
const char *hostname, int hostname_len) const char *hostname, int hostname_len)
{ {
struct sockaddr_in ssin = {0};
return nlm_lookup_host(0, sin, proto, version, return nlm_lookup_host(0, sin, proto, version,
hostname, hostname_len); hostname, hostname_len, &ssin);
} }
/* /*
@ -191,9 +191,12 @@ struct nlm_host *
nlmsvc_lookup_host(struct svc_rqst *rqstp, nlmsvc_lookup_host(struct svc_rqst *rqstp,
const char *hostname, int hostname_len) const char *hostname, int hostname_len)
{ {
struct sockaddr_in ssin = {0};
ssin.sin_addr = rqstp->rq_daddr.addr;
return nlm_lookup_host(1, svc_addr_in(rqstp), return nlm_lookup_host(1, svc_addr_in(rqstp),
rqstp->rq_prot, rqstp->rq_vers, rqstp->rq_prot, rqstp->rq_vers,
hostname, hostname_len); hostname, hostname_len, &ssin);
} }
/* /*
@ -204,8 +207,9 @@ nlm_bind_host(struct nlm_host *host)
{ {
struct rpc_clnt *clnt; struct rpc_clnt *clnt;
dprintk("lockd: nlm_bind_host(%08x)\n", dprintk("lockd: nlm_bind_host("NIPQUAD_FMT"->"NIPQUAD_FMT")\n",
(unsigned)ntohl(host->h_addr.sin_addr.s_addr)); NIPQUAD(host->h_saddr.sin_addr),
NIPQUAD(host->h_addr.sin_addr));
/* Lock host handle */ /* Lock host handle */
mutex_lock(&host->h_mutex); mutex_lock(&host->h_mutex);
@ -232,6 +236,7 @@ nlm_bind_host(struct nlm_host *host)
.protocol = host->h_proto, .protocol = host->h_proto,
.address = (struct sockaddr *)&host->h_addr, .address = (struct sockaddr *)&host->h_addr,
.addrsize = sizeof(host->h_addr), .addrsize = sizeof(host->h_addr),
.saddress = (struct sockaddr *)&host->h_saddr,
.timeout = &timeparms, .timeout = &timeparms,
.servername = host->h_name, .servername = host->h_name,
.program = &nlm_program, .program = &nlm_program,

View File

@ -61,6 +61,7 @@ nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res)
status); status);
else else
status = 0; status = 0;
rpc_shutdown_client(clnt);
out: out:
return status; return status;
} }
@ -138,7 +139,6 @@ nsm_create(void)
.program = &nsm_program, .program = &nsm_program,
.version = SM_VERSION, .version = SM_VERSION,
.authflavor = RPC_AUTH_NULL, .authflavor = RPC_AUTH_NULL,
.flags = (RPC_CLNT_CREATE_ONESHOT),
}; };
return rpc_create(&args); return rpc_create(&args);

View File

@ -123,9 +123,6 @@ lockd(struct svc_rqst *rqstp)
/* Process request with signals blocked, but allow SIGKILL. */ /* Process request with signals blocked, but allow SIGKILL. */
allow_signal(SIGKILL); allow_signal(SIGKILL);
/* kick rpciod */
rpciod_up();
dprintk("NFS locking service started (ver " LOCKD_VERSION ").\n"); dprintk("NFS locking service started (ver " LOCKD_VERSION ").\n");
if (!nlm_timeout) if (!nlm_timeout)
@ -202,9 +199,6 @@ lockd(struct svc_rqst *rqstp)
/* Exit the RPC thread */ /* Exit the RPC thread */
svc_exit_thread(rqstp); svc_exit_thread(rqstp);
/* release rpciod */
rpciod_down();
/* Release module */ /* Release module */
unlock_kernel(); unlock_kernel();
module_put_and_exit(0); module_put_and_exit(0);

View File

@ -6,8 +6,8 @@ obj-$(CONFIG_NFS_FS) += nfs.o
nfs-y := client.o dir.o file.o getroot.o inode.o super.o nfs2xdr.o \ nfs-y := client.o dir.o file.o getroot.o inode.o super.o nfs2xdr.o \
pagelist.o proc.o read.o symlink.o unlink.o \ pagelist.o proc.o read.o symlink.o unlink.o \
write.o namespace.o write.o namespace.o mount_clnt.o
nfs-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o nfs-$(CONFIG_ROOT_NFS) += nfsroot.o
nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o
nfs-$(CONFIG_NFS_V3_ACL) += nfs3acl.o nfs-$(CONFIG_NFS_V3_ACL) += nfs3acl.o
nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \ nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \

View File

@ -102,19 +102,10 @@ static struct nfs_client *nfs_alloc_client(const char *hostname,
int nfsversion) int nfsversion)
{ {
struct nfs_client *clp; struct nfs_client *clp;
int error;
if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL)
goto error_0; goto error_0;
error = rpciod_up();
if (error < 0) {
dprintk("%s: couldn't start rpciod! Error = %d\n",
__FUNCTION__, error);
goto error_1;
}
__set_bit(NFS_CS_RPCIOD, &clp->cl_res_state);
if (nfsversion == 4) { if (nfsversion == 4) {
if (nfs_callback_up() < 0) if (nfs_callback_up() < 0)
goto error_2; goto error_2;
@ -139,8 +130,6 @@ static struct nfs_client *nfs_alloc_client(const char *hostname,
#ifdef CONFIG_NFS_V4 #ifdef CONFIG_NFS_V4
init_rwsem(&clp->cl_sem); init_rwsem(&clp->cl_sem);
INIT_LIST_HEAD(&clp->cl_delegations); INIT_LIST_HEAD(&clp->cl_delegations);
INIT_LIST_HEAD(&clp->cl_state_owners);
INIT_LIST_HEAD(&clp->cl_unused);
spin_lock_init(&clp->cl_lock); spin_lock_init(&clp->cl_lock);
INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state); INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state);
rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client"); rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client");
@ -154,9 +143,6 @@ error_3:
if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
nfs_callback_down(); nfs_callback_down();
error_2: error_2:
rpciod_down();
__clear_bit(NFS_CS_RPCIOD, &clp->cl_res_state);
error_1:
kfree(clp); kfree(clp);
error_0: error_0:
return NULL; return NULL;
@ -167,16 +153,7 @@ static void nfs4_shutdown_client(struct nfs_client *clp)
#ifdef CONFIG_NFS_V4 #ifdef CONFIG_NFS_V4
if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state)) if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state))
nfs4_kill_renewd(clp); nfs4_kill_renewd(clp);
while (!list_empty(&clp->cl_unused)) { BUG_ON(!RB_EMPTY_ROOT(&clp->cl_state_owners));
struct nfs4_state_owner *sp;
sp = list_entry(clp->cl_unused.next,
struct nfs4_state_owner,
so_list);
list_del(&sp->so_list);
kfree(sp);
}
BUG_ON(!list_empty(&clp->cl_state_owners));
if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state)) if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state))
nfs_idmap_delete(clp); nfs_idmap_delete(clp);
#endif #endif
@ -198,9 +175,6 @@ static void nfs_free_client(struct nfs_client *clp)
if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
nfs_callback_down(); nfs_callback_down();
if (__test_and_clear_bit(NFS_CS_RPCIOD, &clp->cl_res_state))
rpciod_down();
kfree(clp->cl_hostname); kfree(clp->cl_hostname);
kfree(clp); kfree(clp);

View File

@ -27,6 +27,13 @@ static void nfs_free_delegation(struct nfs_delegation *delegation)
kfree(delegation); kfree(delegation);
} }
static void nfs_free_delegation_callback(struct rcu_head *head)
{
struct nfs_delegation *delegation = container_of(head, struct nfs_delegation, rcu);
nfs_free_delegation(delegation);
}
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)
{ {
struct inode *inode = state->inode; struct inode *inode = state->inode;
@ -57,7 +64,7 @@ out_err:
return status; return status;
} }
static void nfs_delegation_claim_opens(struct inode *inode) static void nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *stateid)
{ {
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_open_context *ctx; struct nfs_open_context *ctx;
@ -72,9 +79,11 @@ again:
continue; continue;
if (!test_bit(NFS_DELEGATED_STATE, &state->flags)) if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
continue; continue;
if (memcmp(state->stateid.data, stateid->data, sizeof(state->stateid.data)) != 0)
continue;
get_nfs_open_context(ctx); get_nfs_open_context(ctx);
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
err = nfs4_open_delegation_recall(ctx->dentry, state); err = nfs4_open_delegation_recall(ctx, state, stateid);
if (err >= 0) if (err >= 0)
err = nfs_delegation_claim_locks(ctx, state); err = nfs_delegation_claim_locks(ctx, state);
put_nfs_open_context(ctx); put_nfs_open_context(ctx);
@ -115,10 +124,6 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
int status = 0; int status = 0;
/* Ensure we first revalidate the attributes and page cache! */
if ((nfsi->cache_validity & (NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_ATTR)))
__nfs_revalidate_inode(NFS_SERVER(inode), inode);
delegation = kmalloc(sizeof(*delegation), GFP_KERNEL); delegation = kmalloc(sizeof(*delegation), GFP_KERNEL);
if (delegation == NULL) if (delegation == NULL)
return -ENOMEM; return -ENOMEM;
@ -131,10 +136,10 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
delegation->inode = inode; delegation->inode = inode;
spin_lock(&clp->cl_lock); spin_lock(&clp->cl_lock);
if (nfsi->delegation == NULL) { if (rcu_dereference(nfsi->delegation) == NULL) {
list_add(&delegation->super_list, &clp->cl_delegations); list_add_rcu(&delegation->super_list, &clp->cl_delegations);
nfsi->delegation = delegation;
nfsi->delegation_state = delegation->type; nfsi->delegation_state = delegation->type;
rcu_assign_pointer(nfsi->delegation, delegation);
delegation = NULL; delegation = NULL;
} else { } else {
if (memcmp(&delegation->stateid, &nfsi->delegation->stateid, if (memcmp(&delegation->stateid, &nfsi->delegation->stateid,
@ -145,6 +150,12 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
status = -EIO; status = -EIO;
} }
} }
/* Ensure we revalidate the attributes and page cache! */
spin_lock(&inode->i_lock);
nfsi->cache_validity |= NFS_INO_REVAL_FORCED;
spin_unlock(&inode->i_lock);
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
kfree(delegation); kfree(delegation);
return status; return status;
@ -155,7 +166,7 @@ static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *
int res = 0; int res = 0;
res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid); res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid);
nfs_free_delegation(delegation); call_rcu(&delegation->rcu, nfs_free_delegation_callback);
return res; return res;
} }
@ -170,33 +181,55 @@ static void nfs_msync_inode(struct inode *inode)
/* /*
* Basic procedure for returning a delegation to the server * Basic procedure for returning a delegation to the server
*/ */
int __nfs_inode_return_delegation(struct inode *inode) static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation)
{ {
struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_delegation *delegation;
int res = 0;
nfs_msync_inode(inode); nfs_msync_inode(inode);
down_read(&clp->cl_sem); down_read(&clp->cl_sem);
/* Guard against new delegated open calls */ /* Guard against new delegated open calls */
down_write(&nfsi->rwsem); down_write(&nfsi->rwsem);
spin_lock(&clp->cl_lock); nfs_delegation_claim_opens(inode, &delegation->stateid);
delegation = nfsi->delegation;
if (delegation != NULL) {
list_del_init(&delegation->super_list);
nfsi->delegation = NULL;
nfsi->delegation_state = 0;
}
spin_unlock(&clp->cl_lock);
nfs_delegation_claim_opens(inode);
up_write(&nfsi->rwsem); up_write(&nfsi->rwsem);
up_read(&clp->cl_sem); up_read(&clp->cl_sem);
nfs_msync_inode(inode); nfs_msync_inode(inode);
if (delegation != NULL) return nfs_do_return_delegation(inode, delegation);
res = nfs_do_return_delegation(inode, delegation); }
return res;
static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid)
{
struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation);
if (delegation == NULL)
goto nomatch;
if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data,
sizeof(delegation->stateid.data)) != 0)
goto nomatch;
list_del_rcu(&delegation->super_list);
nfsi->delegation_state = 0;
rcu_assign_pointer(nfsi->delegation, NULL);
return delegation;
nomatch:
return NULL;
}
int nfs_inode_return_delegation(struct inode *inode)
{
struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_delegation *delegation;
int err = 0;
if (rcu_dereference(nfsi->delegation) != NULL) {
spin_lock(&clp->cl_lock);
delegation = nfs_detach_delegation_locked(nfsi, NULL);
spin_unlock(&clp->cl_lock);
if (delegation != NULL)
err = __nfs_inode_return_delegation(inode, delegation);
}
return err;
} }
/* /*
@ -211,19 +244,23 @@ void nfs_return_all_delegations(struct super_block *sb)
if (clp == NULL) if (clp == NULL)
return; return;
restart: restart:
spin_lock(&clp->cl_lock); rcu_read_lock();
list_for_each_entry(delegation, &clp->cl_delegations, super_list) { list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
if (delegation->inode->i_sb != sb) if (delegation->inode->i_sb != sb)
continue; continue;
inode = igrab(delegation->inode); inode = igrab(delegation->inode);
if (inode == NULL) if (inode == NULL)
continue; continue;
spin_lock(&clp->cl_lock);
delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
nfs_inode_return_delegation(inode); rcu_read_unlock();
if (delegation != NULL)
__nfs_inode_return_delegation(inode, delegation);
iput(inode); iput(inode);
goto restart; goto restart;
} }
spin_unlock(&clp->cl_lock); rcu_read_unlock();
} }
static int nfs_do_expire_all_delegations(void *ptr) static int nfs_do_expire_all_delegations(void *ptr)
@ -234,22 +271,26 @@ static int nfs_do_expire_all_delegations(void *ptr)
allow_signal(SIGKILL); allow_signal(SIGKILL);
restart: restart:
spin_lock(&clp->cl_lock);
if (test_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) != 0) if (test_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) != 0)
goto out; goto out;
if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0)
goto out; goto out;
list_for_each_entry(delegation, &clp->cl_delegations, super_list) { rcu_read_lock();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
inode = igrab(delegation->inode); inode = igrab(delegation->inode);
if (inode == NULL) if (inode == NULL)
continue; continue;
spin_lock(&clp->cl_lock);
delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
nfs_inode_return_delegation(inode); rcu_read_unlock();
if (delegation)
__nfs_inode_return_delegation(inode, delegation);
iput(inode); iput(inode);
goto restart; goto restart;
} }
rcu_read_unlock();
out: out:
spin_unlock(&clp->cl_lock);
nfs_put_client(clp); nfs_put_client(clp);
module_put_and_exit(0); module_put_and_exit(0);
} }
@ -280,17 +321,21 @@ void nfs_handle_cb_pathdown(struct nfs_client *clp)
if (clp == NULL) if (clp == NULL)
return; return;
restart: restart:
spin_lock(&clp->cl_lock); rcu_read_lock();
list_for_each_entry(delegation, &clp->cl_delegations, super_list) { list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
inode = igrab(delegation->inode); inode = igrab(delegation->inode);
if (inode == NULL) if (inode == NULL)
continue; continue;
spin_lock(&clp->cl_lock);
delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
nfs_inode_return_delegation(inode); rcu_read_unlock();
if (delegation != NULL)
__nfs_inode_return_delegation(inode, delegation);
iput(inode); iput(inode);
goto restart; goto restart;
} }
spin_unlock(&clp->cl_lock); rcu_read_unlock();
} }
struct recall_threadargs { struct recall_threadargs {
@ -316,21 +361,14 @@ static int recall_thread(void *data)
down_read(&clp->cl_sem); down_read(&clp->cl_sem);
down_write(&nfsi->rwsem); down_write(&nfsi->rwsem);
spin_lock(&clp->cl_lock); spin_lock(&clp->cl_lock);
delegation = nfsi->delegation; delegation = nfs_detach_delegation_locked(nfsi, args->stateid);
if (delegation != NULL && memcmp(delegation->stateid.data, if (delegation != NULL)
args->stateid->data,
sizeof(delegation->stateid.data)) == 0) {
list_del_init(&delegation->super_list);
nfsi->delegation = NULL;
nfsi->delegation_state = 0;
args->result = 0; args->result = 0;
} else { else
delegation = NULL;
args->result = -ENOENT; args->result = -ENOENT;
}
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
complete(&args->started); complete(&args->started);
nfs_delegation_claim_opens(inode); nfs_delegation_claim_opens(inode, args->stateid);
up_write(&nfsi->rwsem); up_write(&nfsi->rwsem);
up_read(&clp->cl_sem); up_read(&clp->cl_sem);
nfs_msync_inode(inode); nfs_msync_inode(inode);
@ -371,14 +409,14 @@ struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs
{ {
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
struct inode *res = NULL; struct inode *res = NULL;
spin_lock(&clp->cl_lock); rcu_read_lock();
list_for_each_entry(delegation, &clp->cl_delegations, super_list) { list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
if (nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { if (nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
res = igrab(delegation->inode); res = igrab(delegation->inode);
break; break;
} }
} }
spin_unlock(&clp->cl_lock); rcu_read_unlock();
return res; return res;
} }
@ -388,10 +426,10 @@ struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs
void nfs_delegation_mark_reclaim(struct nfs_client *clp) void nfs_delegation_mark_reclaim(struct nfs_client *clp)
{ {
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
spin_lock(&clp->cl_lock); rcu_read_lock();
list_for_each_entry(delegation, &clp->cl_delegations, super_list) list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list)
delegation->flags |= NFS_DELEGATION_NEED_RECLAIM; delegation->flags |= NFS_DELEGATION_NEED_RECLAIM;
spin_unlock(&clp->cl_lock); rcu_read_unlock();
} }
/* /*
@ -399,39 +437,35 @@ void nfs_delegation_mark_reclaim(struct nfs_client *clp)
*/ */
void nfs_delegation_reap_unclaimed(struct nfs_client *clp) void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
{ {
struct nfs_delegation *delegation, *n; struct nfs_delegation *delegation;
LIST_HEAD(head); restart:
spin_lock(&clp->cl_lock); rcu_read_lock();
list_for_each_entry_safe(delegation, n, &clp->cl_delegations, super_list) { list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
if ((delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0) if ((delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0)
continue; continue;
list_move(&delegation->super_list, &head); spin_lock(&clp->cl_lock);
NFS_I(delegation->inode)->delegation = NULL; delegation = nfs_detach_delegation_locked(NFS_I(delegation->inode), NULL);
NFS_I(delegation->inode)->delegation_state = 0; spin_unlock(&clp->cl_lock);
} rcu_read_unlock();
spin_unlock(&clp->cl_lock); if (delegation != NULL)
while(!list_empty(&head)) { call_rcu(&delegation->rcu, nfs_free_delegation_callback);
delegation = list_entry(head.next, struct nfs_delegation, super_list); goto restart;
list_del(&delegation->super_list);
nfs_free_delegation(delegation);
} }
rcu_read_unlock();
} }
int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode) int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode)
{ {
struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
int res = 0; int ret = 0;
if (nfsi->delegation_state == 0) rcu_read_lock();
return 0; delegation = rcu_dereference(nfsi->delegation);
spin_lock(&clp->cl_lock);
delegation = nfsi->delegation;
if (delegation != NULL) { if (delegation != NULL) {
memcpy(dst->data, delegation->stateid.data, sizeof(dst->data)); memcpy(dst->data, delegation->stateid.data, sizeof(dst->data));
res = 1; ret = 1;
} }
spin_unlock(&clp->cl_lock); rcu_read_unlock();
return res; return ret;
} }

View File

@ -22,11 +22,12 @@ struct nfs_delegation {
long flags; long flags;
loff_t maxsize; loff_t maxsize;
__u64 change_attr; __u64 change_attr;
struct rcu_head rcu;
}; };
int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
int __nfs_inode_return_delegation(struct inode *inode); int nfs_inode_return_delegation(struct inode *inode);
int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid); int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid);
struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle); struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle);
@ -39,27 +40,24 @@ void nfs_delegation_reap_unclaimed(struct nfs_client *clp);
/* NFSv4 delegation-related procedures */ /* NFSv4 delegation-related procedures */
int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid); int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid);
int nfs4_open_delegation_recall(struct dentry *dentry, struct nfs4_state *state); 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 nfs4_state *state, struct file_lock *fl);
int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode); int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode);
static inline int nfs_have_delegation(struct inode *inode, int flags) static inline int nfs_have_delegation(struct inode *inode, int flags)
{ {
struct nfs_delegation *delegation;
int ret = 0;
flags &= FMODE_READ|FMODE_WRITE; flags &= FMODE_READ|FMODE_WRITE;
smp_rmb(); rcu_read_lock();
if ((NFS_I(inode)->delegation_state & flags) == flags) delegation = rcu_dereference(NFS_I(inode)->delegation);
return 1; if (delegation != NULL && (delegation->type & flags) == flags)
return 0; ret = 1;
rcu_read_unlock();
return ret;
} }
static inline int nfs_inode_return_delegation(struct inode *inode)
{
int err = 0;
if (NFS_I(inode)->delegation != NULL)
err = __nfs_inode_return_delegation(inode);
return err;
}
#else #else
static inline int nfs_have_delegation(struct inode *inode, int flags) static inline int nfs_have_delegation(struct inode *inode, int flags)
{ {

View File

@ -897,14 +897,13 @@ int nfs_is_exclusive_create(struct inode *dir, struct nameidata *nd)
return (nd->intent.open.flags & O_EXCL) != 0; return (nd->intent.open.flags & O_EXCL) != 0;
} }
static inline int nfs_reval_fsid(struct vfsmount *mnt, struct inode *dir, static inline int nfs_reval_fsid(struct inode *dir, const struct nfs_fattr *fattr)
struct nfs_fh *fh, struct nfs_fattr *fattr)
{ {
struct nfs_server *server = NFS_SERVER(dir); struct nfs_server *server = NFS_SERVER(dir);
if (!nfs_fsid_equal(&server->fsid, &fattr->fsid)) if (!nfs_fsid_equal(&server->fsid, &fattr->fsid))
/* Revalidate fsid on root dir */ /* Revalidate fsid using the parent directory */
return __nfs_revalidate_inode(server, mnt->mnt_root->d_inode); return __nfs_revalidate_inode(server, dir);
return 0; return 0;
} }
@ -946,7 +945,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
res = ERR_PTR(error); res = ERR_PTR(error);
goto out_unlock; goto out_unlock;
} }
error = nfs_reval_fsid(nd->mnt, dir, &fhandle, &fattr); error = nfs_reval_fsid(dir, &fattr);
if (error < 0) { if (error < 0) {
res = ERR_PTR(error); res = ERR_PTR(error);
goto out_unlock; goto out_unlock;
@ -1244,7 +1243,7 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode,
attr.ia_mode = mode; attr.ia_mode = mode;
attr.ia_valid = ATTR_MODE; attr.ia_valid = ATTR_MODE;
if (nd && (nd->flags & LOOKUP_CREATE)) if ((nd->flags & LOOKUP_CREATE) != 0)
open_flags = nd->intent.open.flags; open_flags = nd->intent.open.flags;
lock_kernel(); lock_kernel();
@ -1535,7 +1534,7 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym
lock_kernel(); lock_kernel();
page = alloc_page(GFP_KERNEL); page = alloc_page(GFP_HIGHUSER);
if (!page) { if (!page) {
unlock_kernel(); unlock_kernel();
return -ENOMEM; return -ENOMEM;
@ -1744,8 +1743,8 @@ int nfs_access_cache_shrinker(int nr_to_scan, gfp_t gfp_mask)
struct nfs_inode *nfsi; struct nfs_inode *nfsi;
struct nfs_access_entry *cache; struct nfs_access_entry *cache;
spin_lock(&nfs_access_lru_lock);
restart: restart:
spin_lock(&nfs_access_lru_lock);
list_for_each_entry(nfsi, &nfs_access_lru_list, access_cache_inode_lru) { list_for_each_entry(nfsi, &nfs_access_lru_list, access_cache_inode_lru) {
struct inode *inode; struct inode *inode;
@ -1770,6 +1769,7 @@ remove_lru_entry:
clear_bit(NFS_INO_ACL_LRU_SET, &nfsi->flags); clear_bit(NFS_INO_ACL_LRU_SET, &nfsi->flags);
} }
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
spin_unlock(&nfs_access_lru_lock);
iput(inode); iput(inode);
goto restart; goto restart;
} }

View File

@ -266,7 +266,7 @@ static const struct rpc_call_ops nfs_read_direct_ops = {
static ssize_t nfs_direct_read_schedule(struct nfs_direct_req *dreq, unsigned long user_addr, size_t count, loff_t pos) static ssize_t nfs_direct_read_schedule(struct nfs_direct_req *dreq, unsigned long user_addr, size_t count, loff_t pos)
{ {
struct nfs_open_context *ctx = dreq->ctx; struct nfs_open_context *ctx = dreq->ctx;
struct inode *inode = ctx->dentry->d_inode; struct inode *inode = ctx->path.dentry->d_inode;
size_t rsize = NFS_SERVER(inode)->rsize; size_t rsize = NFS_SERVER(inode)->rsize;
unsigned int pgbase; unsigned int pgbase;
int result; int result;
@ -295,9 +295,14 @@ static ssize_t nfs_direct_read_schedule(struct nfs_direct_req *dreq, unsigned lo
break; break;
} }
if ((unsigned)result < data->npages) { if ((unsigned)result < data->npages) {
nfs_direct_release_pages(data->pagevec, result); bytes = result * PAGE_SIZE;
nfs_readdata_release(data); if (bytes <= pgbase) {
break; nfs_direct_release_pages(data->pagevec, result);
nfs_readdata_release(data);
break;
}
bytes -= pgbase;
data->npages = result;
} }
get_dreq(dreq); get_dreq(dreq);
@ -601,7 +606,7 @@ static const struct rpc_call_ops nfs_write_direct_ops = {
static ssize_t nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned long user_addr, size_t count, loff_t pos, int sync) static ssize_t nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned long user_addr, size_t count, loff_t pos, int sync)
{ {
struct nfs_open_context *ctx = dreq->ctx; struct nfs_open_context *ctx = dreq->ctx;
struct inode *inode = ctx->dentry->d_inode; struct inode *inode = ctx->path.dentry->d_inode;
size_t wsize = NFS_SERVER(inode)->wsize; size_t wsize = NFS_SERVER(inode)->wsize;
unsigned int pgbase; unsigned int pgbase;
int result; int result;
@ -630,9 +635,14 @@ static ssize_t nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned l
break; break;
} }
if ((unsigned)result < data->npages) { if ((unsigned)result < data->npages) {
nfs_direct_release_pages(data->pagevec, result); bytes = result * PAGE_SIZE;
nfs_writedata_release(data); if (bytes <= pgbase) {
break; nfs_direct_release_pages(data->pagevec, result);
nfs_writedata_release(data);
break;
}
bytes -= pgbase;
data->npages = result;
} }
get_dreq(dreq); get_dreq(dreq);
@ -763,10 +773,8 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov,
(unsigned long) count, (long long) pos); (unsigned long) count, (long long) pos);
if (nr_segs != 1) if (nr_segs != 1)
return -EINVAL;
if (count < 0)
goto out; goto out;
retval = -EFAULT; retval = -EFAULT;
if (!access_ok(VERIFY_WRITE, buf, count)) if (!access_ok(VERIFY_WRITE, buf, count))
goto out; goto out;
@ -814,7 +822,7 @@ out:
ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov, ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos) unsigned long nr_segs, loff_t pos)
{ {
ssize_t retval; ssize_t retval = -EINVAL;
struct file *file = iocb->ki_filp; struct file *file = iocb->ki_filp;
struct address_space *mapping = file->f_mapping; struct address_space *mapping = file->f_mapping;
/* XXX: temporary */ /* XXX: temporary */
@ -827,7 +835,7 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
(unsigned long) count, (long long) pos); (unsigned long) count, (long long) pos);
if (nr_segs != 1) if (nr_segs != 1)
return -EINVAL; goto out;
retval = generic_write_checks(file, &pos, &count, 0); retval = generic_write_checks(file, &pos, &count, 0);
if (retval) if (retval)

View File

@ -461,14 +461,14 @@ static struct nfs_open_context *alloc_nfs_open_context(struct vfsmount *mnt, str
ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
if (ctx != NULL) { if (ctx != NULL) {
atomic_set(&ctx->count, 1); ctx->path.dentry = dget(dentry);
ctx->dentry = dget(dentry); ctx->path.mnt = mntget(mnt);
ctx->vfsmnt = mntget(mnt);
ctx->cred = get_rpccred(cred); ctx->cred = get_rpccred(cred);
ctx->state = NULL; ctx->state = NULL;
ctx->lockowner = current->files; ctx->lockowner = current->files;
ctx->error = 0; ctx->error = 0;
ctx->dir_cookie = 0; ctx->dir_cookie = 0;
kref_init(&ctx->kref);
} }
return ctx; return ctx;
} }
@ -476,27 +476,33 @@ static struct nfs_open_context *alloc_nfs_open_context(struct vfsmount *mnt, str
struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx) struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx)
{ {
if (ctx != NULL) if (ctx != NULL)
atomic_inc(&ctx->count); kref_get(&ctx->kref);
return ctx; return ctx;
} }
static void nfs_free_open_context(struct kref *kref)
{
struct nfs_open_context *ctx = container_of(kref,
struct nfs_open_context, kref);
if (!list_empty(&ctx->list)) {
struct inode *inode = ctx->path.dentry->d_inode;
spin_lock(&inode->i_lock);
list_del(&ctx->list);
spin_unlock(&inode->i_lock);
}
if (ctx->state != NULL)
nfs4_close_state(&ctx->path, ctx->state, ctx->mode);
if (ctx->cred != NULL)
put_rpccred(ctx->cred);
dput(ctx->path.dentry);
mntput(ctx->path.mnt);
kfree(ctx);
}
void put_nfs_open_context(struct nfs_open_context *ctx) void put_nfs_open_context(struct nfs_open_context *ctx)
{ {
if (atomic_dec_and_test(&ctx->count)) { kref_put(&ctx->kref, nfs_free_open_context);
if (!list_empty(&ctx->list)) {
struct inode *inode = ctx->dentry->d_inode;
spin_lock(&inode->i_lock);
list_del(&ctx->list);
spin_unlock(&inode->i_lock);
}
if (ctx->state != NULL)
nfs4_close_state(ctx->state, ctx->mode);
if (ctx->cred != NULL)
put_rpccred(ctx->cred);
dput(ctx->dentry);
mntput(ctx->vfsmnt);
kfree(ctx);
}
} }
/* /*
@ -961,8 +967,8 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
goto out_changed; goto out_changed;
server = NFS_SERVER(inode); server = NFS_SERVER(inode);
/* Update the fsid if and only if this is the root directory */ /* Update the fsid? */
if (inode == inode->i_sb->s_root->d_inode if (S_ISDIR(inode->i_mode)
&& !nfs_fsid_equal(&server->fsid, &fattr->fsid)) && !nfs_fsid_equal(&server->fsid, &fattr->fsid))
server->fsid = fattr->fsid; server->fsid = fattr->fsid;
@ -1066,8 +1072,10 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
invalid &= ~NFS_INO_INVALID_DATA; invalid &= ~NFS_INO_INVALID_DATA;
if (data_stable) if (data_stable)
invalid &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME|NFS_INO_REVAL_PAGECACHE); invalid &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME|NFS_INO_REVAL_PAGECACHE);
if (!nfs_have_delegation(inode, FMODE_READ)) if (!nfs_have_delegation(inode, FMODE_READ) ||
(nfsi->cache_validity & NFS_INO_REVAL_FORCED))
nfsi->cache_validity |= invalid; nfsi->cache_validity |= invalid;
nfsi->cache_validity &= ~NFS_INO_REVAL_FORCED;
return 0; return 0;
out_changed: out_changed:
@ -1103,27 +1111,10 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
*/ */
void nfs4_clear_inode(struct inode *inode) void nfs4_clear_inode(struct inode *inode)
{ {
struct nfs_inode *nfsi = NFS_I(inode);
/* If we are holding a delegation, return it! */ /* If we are holding a delegation, return it! */
nfs_inode_return_delegation(inode); nfs_inode_return_delegation(inode);
/* First call standard NFS clear_inode() code */ /* First call standard NFS clear_inode() code */
nfs_clear_inode(inode); nfs_clear_inode(inode);
/* Now clear out any remaining state */
while (!list_empty(&nfsi->open_states)) {
struct nfs4_state *state;
state = list_entry(nfsi->open_states.next,
struct nfs4_state,
inode_states);
dprintk("%s(%s/%Ld): found unclaimed NFSv4 state %p\n",
__FUNCTION__,
inode->i_sb->s_id,
(long long)NFS_FILEID(inode),
state);
BUG_ON(atomic_read(&state->count) != 1);
nfs4_close_state(state, state->state);
}
} }
#endif #endif
@ -1165,15 +1156,11 @@ static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flag
struct nfs_inode *nfsi = (struct nfs_inode *) foo; struct nfs_inode *nfsi = (struct nfs_inode *) foo;
inode_init_once(&nfsi->vfs_inode); inode_init_once(&nfsi->vfs_inode);
spin_lock_init(&nfsi->req_lock);
INIT_LIST_HEAD(&nfsi->dirty);
INIT_LIST_HEAD(&nfsi->commit);
INIT_LIST_HEAD(&nfsi->open_files); INIT_LIST_HEAD(&nfsi->open_files);
INIT_LIST_HEAD(&nfsi->access_cache_entry_lru); INIT_LIST_HEAD(&nfsi->access_cache_entry_lru);
INIT_LIST_HEAD(&nfsi->access_cache_inode_lru); INIT_LIST_HEAD(&nfsi->access_cache_inode_lru);
INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC); INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC);
atomic_set(&nfsi->data_updates, 0); atomic_set(&nfsi->data_updates, 0);
nfsi->ndirty = 0;
nfsi->ncommit = 0; nfsi->ncommit = 0;
nfsi->npages = 0; nfsi->npages = 0;
nfs4_init_once(nfsi); nfs4_init_once(nfsi);

View File

@ -183,9 +183,9 @@ unsigned long nfs_block_bits(unsigned long bsize, unsigned char *nrbitsp)
/* /*
* Calculate the number of 512byte blocks used. * Calculate the number of 512byte blocks used.
*/ */
static inline unsigned long nfs_calc_block_size(u64 tsize) static inline blkcnt_t nfs_calc_block_size(u64 tsize)
{ {
loff_t used = (tsize + 511) >> 9; blkcnt_t used = (tsize + 511) >> 9;
return (used > ULONG_MAX) ? ULONG_MAX : used; return (used > ULONG_MAX) ? ULONG_MAX : used;
} }

View File

@ -1,7 +1,5 @@
/* /*
* linux/fs/nfs/mount_clnt.c * In-kernel MOUNT protocol client
*
* MOUNT client to support NFSroot.
* *
* Copyright (C) 1997, Olaf Kirch <okir@monad.swb.de> * Copyright (C) 1997, Olaf Kirch <okir@monad.swb.de>
*/ */
@ -18,33 +16,31 @@
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
#ifdef RPC_DEBUG #ifdef RPC_DEBUG
# define NFSDBG_FACILITY NFSDBG_ROOT # define NFSDBG_FACILITY NFSDBG_MOUNT
#endif #endif
/*
#define MOUNT_PROGRAM 100005
#define MOUNT_VERSION 1
#define MOUNT_MNT 1
#define MOUNT_UMNT 3
*/
static struct rpc_clnt * mnt_create(char *, struct sockaddr_in *,
int, int);
static struct rpc_program mnt_program; static struct rpc_program mnt_program;
struct mnt_fhstatus { struct mnt_fhstatus {
unsigned int status; u32 status;
struct nfs_fh * fh; struct nfs_fh *fh;
}; };
/* /**
* Obtain an NFS file handle for the given host and path * nfs_mount - Obtain an NFS file handle for the given host and path
* @addr: pointer to server's address
* @len: size of server's address
* @hostname: name of server host, or NULL
* @path: pointer to string containing export path to mount
* @version: mount version to use for this request
* @protocol: transport protocol to use for thie request
* @fh: pointer to location to place returned file handle
*
* Uses default timeout parameters specified by underlying transport.
*/ */
int int nfs_mount(struct sockaddr *addr, size_t len, char *hostname, char *path,
nfsroot_mount(struct sockaddr_in *addr, char *path, struct nfs_fh *fh, int version, int protocol, struct nfs_fh *fh)
int version, int protocol)
{ {
struct rpc_clnt *mnt_clnt;
struct mnt_fhstatus result = { struct mnt_fhstatus result = {
.fh = fh .fh = fh
}; };
@ -52,16 +48,25 @@ nfsroot_mount(struct sockaddr_in *addr, char *path, struct nfs_fh *fh,
.rpc_argp = path, .rpc_argp = path,
.rpc_resp = &result, .rpc_resp = &result,
}; };
char hostname[32]; struct rpc_create_args args = {
.protocol = protocol,
.address = addr,
.addrsize = len,
.servername = hostname,
.program = &mnt_program,
.version = version,
.authflavor = RPC_AUTH_UNIX,
.flags = RPC_CLNT_CREATE_INTR,
};
struct rpc_clnt *mnt_clnt;
int status; int status;
dprintk("NFS: nfs_mount(%08x:%s)\n", dprintk("NFS: sending MNT request for %s:%s\n",
(unsigned)ntohl(addr->sin_addr.s_addr), path); (hostname ? hostname : "server"), path);
sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(addr->sin_addr.s_addr)); mnt_clnt = rpc_create(&args);
mnt_clnt = mnt_create(hostname, addr, version, protocol);
if (IS_ERR(mnt_clnt)) if (IS_ERR(mnt_clnt))
return PTR_ERR(mnt_clnt); goto out_clnt_err;
if (version == NFS_MNT3_VERSION) if (version == NFS_MNT3_VERSION)
msg.rpc_proc = &mnt_clnt->cl_procinfo[MOUNTPROC3_MNT]; msg.rpc_proc = &mnt_clnt->cl_procinfo[MOUNTPROC3_MNT];
@ -69,33 +74,39 @@ nfsroot_mount(struct sockaddr_in *addr, char *path, struct nfs_fh *fh,
msg.rpc_proc = &mnt_clnt->cl_procinfo[MNTPROC_MNT]; msg.rpc_proc = &mnt_clnt->cl_procinfo[MNTPROC_MNT];
status = rpc_call_sync(mnt_clnt, &msg, 0); status = rpc_call_sync(mnt_clnt, &msg, 0);
return status < 0? status : (result.status? -EACCES : 0); rpc_shutdown_client(mnt_clnt);
}
static struct rpc_clnt * if (status < 0)
mnt_create(char *hostname, struct sockaddr_in *srvaddr, int version, goto out_call_err;
int protocol) if (result.status != 0)
{ goto out_mnt_err;
struct rpc_create_args args = {
.protocol = protocol,
.address = (struct sockaddr *)srvaddr,
.addrsize = sizeof(*srvaddr),
.servername = hostname,
.program = &mnt_program,
.version = version,
.authflavor = RPC_AUTH_UNIX,
.flags = (RPC_CLNT_CREATE_ONESHOT |
RPC_CLNT_CREATE_INTR),
};
return rpc_create(&args); dprintk("NFS: MNT request succeeded\n");
status = 0;
out:
return status;
out_clnt_err:
status = PTR_ERR(mnt_clnt);
dprintk("NFS: failed to create RPC client, status=%d\n", status);
goto out;
out_call_err:
dprintk("NFS: failed to start MNT request, status=%d\n", status);
goto out;
out_mnt_err:
dprintk("NFS: MNT server returned result %d\n", result.status);
status = -EACCES;
goto out;
} }
/* /*
* XDR encode/decode functions for MOUNT * XDR encode/decode functions for MOUNT
*/ */
static int static int xdr_encode_dirpath(struct rpc_rqst *req, __be32 *p,
xdr_encode_dirpath(struct rpc_rqst *req, __be32 *p, const char *path) const char *path)
{ {
p = xdr_encode_string(p, path); p = xdr_encode_string(p, path);
@ -103,8 +114,8 @@ xdr_encode_dirpath(struct rpc_rqst *req, __be32 *p, const char *path)
return 0; return 0;
} }
static int static int xdr_decode_fhstatus(struct rpc_rqst *req, __be32 *p,
xdr_decode_fhstatus(struct rpc_rqst *req, __be32 *p, struct mnt_fhstatus *res) struct mnt_fhstatus *res)
{ {
struct nfs_fh *fh = res->fh; struct nfs_fh *fh = res->fh;
@ -115,8 +126,8 @@ xdr_decode_fhstatus(struct rpc_rqst *req, __be32 *p, struct mnt_fhstatus *res)
return 0; return 0;
} }
static int static int xdr_decode_fhstatus3(struct rpc_rqst *req, __be32 *p,
xdr_decode_fhstatus3(struct rpc_rqst *req, __be32 *p, struct mnt_fhstatus *res) struct mnt_fhstatus *res)
{ {
struct nfs_fh *fh = res->fh; struct nfs_fh *fh = res->fh;
@ -135,53 +146,53 @@ xdr_decode_fhstatus3(struct rpc_rqst *req, __be32 *p, struct mnt_fhstatus *res)
#define MNT_fhstatus_sz (1 + 8) #define MNT_fhstatus_sz (1 + 8)
#define MNT_fhstatus3_sz (1 + 16) #define MNT_fhstatus3_sz (1 + 16)
static struct rpc_procinfo mnt_procedures[] = { static struct rpc_procinfo mnt_procedures[] = {
[MNTPROC_MNT] = { [MNTPROC_MNT] = {
.p_proc = MNTPROC_MNT, .p_proc = MNTPROC_MNT,
.p_encode = (kxdrproc_t) xdr_encode_dirpath, .p_encode = (kxdrproc_t) xdr_encode_dirpath,
.p_decode = (kxdrproc_t) xdr_decode_fhstatus, .p_decode = (kxdrproc_t) xdr_decode_fhstatus,
.p_arglen = MNT_dirpath_sz, .p_arglen = MNT_dirpath_sz,
.p_replen = MNT_fhstatus_sz, .p_replen = MNT_fhstatus_sz,
.p_statidx = MNTPROC_MNT, .p_statidx = MNTPROC_MNT,
.p_name = "MOUNT", .p_name = "MOUNT",
}, },
}; };
static struct rpc_procinfo mnt3_procedures[] = { static struct rpc_procinfo mnt3_procedures[] = {
[MOUNTPROC3_MNT] = { [MOUNTPROC3_MNT] = {
.p_proc = MOUNTPROC3_MNT, .p_proc = MOUNTPROC3_MNT,
.p_encode = (kxdrproc_t) xdr_encode_dirpath, .p_encode = (kxdrproc_t) xdr_encode_dirpath,
.p_decode = (kxdrproc_t) xdr_decode_fhstatus3, .p_decode = (kxdrproc_t) xdr_decode_fhstatus3,
.p_arglen = MNT_dirpath_sz, .p_arglen = MNT_dirpath_sz,
.p_replen = MNT_fhstatus3_sz, .p_replen = MNT_fhstatus3_sz,
.p_statidx = MOUNTPROC3_MNT, .p_statidx = MOUNTPROC3_MNT,
.p_name = "MOUNT", .p_name = "MOUNT",
}, },
}; };
static struct rpc_version mnt_version1 = { static struct rpc_version mnt_version1 = {
.number = 1, .number = 1,
.nrprocs = 2, .nrprocs = 2,
.procs = mnt_procedures .procs = mnt_procedures,
}; };
static struct rpc_version mnt_version3 = { static struct rpc_version mnt_version3 = {
.number = 3, .number = 3,
.nrprocs = 2, .nrprocs = 2,
.procs = mnt3_procedures .procs = mnt3_procedures,
}; };
static struct rpc_version * mnt_version[] = { static struct rpc_version *mnt_version[] = {
NULL, NULL,
&mnt_version1, &mnt_version1,
NULL, NULL,
&mnt_version3, &mnt_version3,
}; };
static struct rpc_stat mnt_stats; static struct rpc_stat mnt_stats;
static struct rpc_program mnt_program = { static struct rpc_program mnt_program = {
.name = "mount", .name = "mount",
.number = NFS_MNT_PROGRAM, .number = NFS_MNT_PROGRAM,
.nrvers = ARRAY_SIZE(mnt_version), .nrvers = ARRAY_SIZE(mnt_version),

View File

@ -223,7 +223,7 @@ nfs_xdr_diropargs(struct rpc_rqst *req, __be32 *p, struct nfs_diropargs *args)
static int static int
nfs_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args) nfs_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args)
{ {
struct rpc_auth *auth = req->rq_task->tk_auth; struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
unsigned int replen; unsigned int replen;
u32 offset = (u32)args->offset; u32 offset = (u32)args->offset;
u32 count = args->count; u32 count = args->count;
@ -380,7 +380,7 @@ static int
nfs_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs_readdirargs *args) nfs_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs_readdirargs *args)
{ {
struct rpc_task *task = req->rq_task; struct rpc_task *task = req->rq_task;
struct rpc_auth *auth = task->tk_auth; struct rpc_auth *auth = task->tk_msg.rpc_cred->cr_auth;
unsigned int replen; unsigned int replen;
u32 count = args->count; u32 count = args->count;
@ -541,7 +541,7 @@ nfs_xdr_diropres(struct rpc_rqst *req, __be32 *p, struct nfs_diropok *res)
static int static int
nfs_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs_readlinkargs *args) nfs_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs_readlinkargs *args)
{ {
struct rpc_auth *auth = req->rq_task->tk_auth; struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
unsigned int replen; unsigned int replen;
p = xdr_encode_fhandle(p, args->fh); p = xdr_encode_fhandle(p, args->fh);

View File

@ -335,9 +335,7 @@ again:
* not sure this buys us anything (and I'd have * not sure this buys us anything (and I'd have
* to revamp the NFSv3 XDR code) */ * to revamp the NFSv3 XDR code) */
status = nfs3_proc_setattr(dentry, &fattr, sattr); status = nfs3_proc_setattr(dentry, &fattr, sattr);
if (status == 0) nfs_post_op_update_inode(dentry->d_inode, &fattr);
nfs_setattr_update_inode(dentry->d_inode, sattr);
nfs_refresh_inode(dentry->d_inode, &fattr);
dprintk("NFS reply setattr (post-create): %d\n", status); dprintk("NFS reply setattr (post-create): %d\n", status);
} }
if (status != 0) if (status != 0)

View File

@ -319,7 +319,7 @@ nfs3_xdr_accessargs(struct rpc_rqst *req, __be32 *p, struct nfs3_accessargs *arg
static int static int
nfs3_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args) nfs3_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args)
{ {
struct rpc_auth *auth = req->rq_task->tk_auth; struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
unsigned int replen; unsigned int replen;
u32 count = args->count; u32 count = args->count;
@ -458,7 +458,7 @@ nfs3_xdr_linkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_linkargs *args)
static int static int
nfs3_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirargs *args) nfs3_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirargs *args)
{ {
struct rpc_auth *auth = req->rq_task->tk_auth; struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
unsigned int replen; unsigned int replen;
u32 count = args->count; u32 count = args->count;
@ -643,7 +643,7 @@ static int
nfs3_xdr_getaclargs(struct rpc_rqst *req, __be32 *p, nfs3_xdr_getaclargs(struct rpc_rqst *req, __be32 *p,
struct nfs3_getaclargs *args) struct nfs3_getaclargs *args)
{ {
struct rpc_auth *auth = req->rq_task->tk_auth; struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
unsigned int replen; unsigned int replen;
p = xdr_encode_fhandle(p, args->fh); p = xdr_encode_fhandle(p, args->fh);
@ -773,7 +773,7 @@ nfs3_xdr_accessres(struct rpc_rqst *req, __be32 *p, struct nfs3_accessres *res)
static int static int
nfs3_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readlinkargs *args) nfs3_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readlinkargs *args)
{ {
struct rpc_auth *auth = req->rq_task->tk_auth; struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
unsigned int replen; unsigned int replen;
p = xdr_encode_fhandle(p, args->fh); p = xdr_encode_fhandle(p, args->fh);

View File

@ -70,19 +70,26 @@ static inline void nfs_confirm_seqid(struct nfs_seqid_counter *seqid, int status
seqid->flags |= NFS_SEQID_CONFIRMED; seqid->flags |= NFS_SEQID_CONFIRMED;
} }
struct nfs_unique_id {
struct rb_node rb_node;
__u64 id;
};
/* /*
* NFS4 state_owners and lock_owners are simply labels for ordered * NFS4 state_owners and lock_owners are simply labels for ordered
* sequences of RPC calls. Their sole purpose is to provide once-only * sequences of RPC calls. Their sole purpose is to provide once-only
* semantics by allowing the server to identify replayed requests. * semantics by allowing the server to identify replayed requests.
*/ */
struct nfs4_state_owner { struct nfs4_state_owner {
spinlock_t so_lock; struct nfs_unique_id so_owner_id;
struct list_head so_list; /* per-clientid list of state_owners */
struct nfs_client *so_client; struct nfs_client *so_client;
u32 so_id; /* 32-bit identifier, unique */ struct nfs_server *so_server;
atomic_t so_count; struct rb_node so_client_node;
struct rpc_cred *so_cred; /* Associated cred */ struct rpc_cred *so_cred; /* Associated cred */
spinlock_t so_lock;
atomic_t so_count;
struct list_head so_states; struct list_head so_states;
struct list_head so_delegations; struct list_head so_delegations;
struct nfs_seqid_counter so_seqid; struct nfs_seqid_counter so_seqid;
@ -108,7 +115,7 @@ struct nfs4_lock_state {
#define NFS_LOCK_INITIALIZED 1 #define NFS_LOCK_INITIALIZED 1
int ls_flags; int ls_flags;
struct nfs_seqid_counter ls_seqid; struct nfs_seqid_counter ls_seqid;
u32 ls_id; struct nfs_unique_id ls_id;
nfs4_stateid ls_stateid; nfs4_stateid ls_stateid;
atomic_t ls_count; atomic_t ls_count;
}; };
@ -116,7 +123,10 @@ struct nfs4_lock_state {
/* bits for nfs4_state->flags */ /* bits for nfs4_state->flags */
enum { enum {
LK_STATE_IN_USE, LK_STATE_IN_USE,
NFS_DELEGATED_STATE, NFS_DELEGATED_STATE, /* Current stateid is delegation */
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 */
}; };
struct nfs4_state { struct nfs4_state {
@ -130,11 +140,14 @@ struct nfs4_state {
unsigned long flags; /* Do we hold any locks? */ unsigned long flags; /* Do we hold any locks? */
spinlock_t state_lock; /* Protects the lock_states list */ spinlock_t state_lock; /* Protects the lock_states list */
nfs4_stateid stateid; seqlock_t seqlock; /* Protects the stateid/open_stateid */
nfs4_stateid stateid; /* Current stateid: may be delegation */
nfs4_stateid open_stateid; /* OPEN stateid */
unsigned int n_rdonly; /* The following 3 fields are protected by owner->so_lock */
unsigned int n_wronly; unsigned int n_rdonly; /* Number of read-only references */
unsigned int n_rdwr; unsigned int n_wronly; /* Number of write-only references */
unsigned int n_rdwr; /* Number of read/write references */
int state; /* State on the server (R,W, or RW) */ int state; /* State on the server (R,W, or RW) */
atomic_t count; atomic_t count;
}; };
@ -165,7 +178,7 @@ extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, struc
extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct rpc_cred *); extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct rpc_cred *);
extern int nfs4_proc_async_renew(struct nfs_client *, struct rpc_cred *); extern int nfs4_proc_async_renew(struct nfs_client *, struct rpc_cred *);
extern int nfs4_proc_renew(struct nfs_client *, struct rpc_cred *); extern int nfs4_proc_renew(struct nfs_client *, struct rpc_cred *);
extern int nfs4_do_close(struct inode *inode, struct nfs4_state *state); extern int nfs4_do_close(struct path *path, struct nfs4_state *state);
extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *); extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *);
extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *); extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *);
extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle); extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle);
@ -189,14 +202,13 @@ extern void nfs4_renew_state(struct work_struct *);
/* nfs4state.c */ /* nfs4state.c */
struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp); struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp);
extern u32 nfs4_alloc_lockowner_id(struct nfs_client *);
extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *); extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *);
extern void nfs4_put_state_owner(struct nfs4_state_owner *); extern void nfs4_put_state_owner(struct nfs4_state_owner *);
extern void nfs4_drop_state_owner(struct nfs4_state_owner *); extern void nfs4_drop_state_owner(struct nfs4_state_owner *);
extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *);
extern void nfs4_put_open_state(struct nfs4_state *); extern void nfs4_put_open_state(struct nfs4_state *);
extern void nfs4_close_state(struct nfs4_state *, mode_t); extern void nfs4_close_state(struct path *, struct nfs4_state *, mode_t);
extern void nfs4_state_set_mode_locked(struct nfs4_state *, mode_t); extern void nfs4_state_set_mode_locked(struct nfs4_state *, mode_t);
extern void nfs4_schedule_state_recovery(struct nfs_client *); extern void nfs4_schedule_state_recovery(struct nfs_client *);
extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp); extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp);
@ -222,7 +234,7 @@ extern struct svc_version nfs4_callback_version1;
#else #else
#define nfs4_close_state(a, b) do { } while (0) #define nfs4_close_state(a, b, c) do { } while (0)
#endif /* CONFIG_NFS_V4 */ #endif /* CONFIG_NFS_V4 */
#endif /* __LINUX_FS_NFS_NFS4_FS.H */ #endif /* __LINUX_FS_NFS_NFS4_FS.H */

File diff suppressed because it is too large Load Diff

View File

@ -38,12 +38,14 @@
* subsequent patch. * subsequent patch.
*/ */
#include <linux/kernel.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
#include <linux/nfs_idmap.h> #include <linux/nfs_idmap.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/random.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/bitops.h> #include <linux/bitops.h>
@ -69,33 +71,14 @@ static int nfs4_init_client(struct nfs_client *clp, struct rpc_cred *cred)
return status; return status;
} }
u32
nfs4_alloc_lockowner_id(struct nfs_client *clp)
{
return clp->cl_lockowner_id ++;
}
static struct nfs4_state_owner *
nfs4_client_grab_unused(struct nfs_client *clp, struct rpc_cred *cred)
{
struct nfs4_state_owner *sp = NULL;
if (!list_empty(&clp->cl_unused)) {
sp = list_entry(clp->cl_unused.next, struct nfs4_state_owner, so_list);
atomic_inc(&sp->so_count);
sp->so_cred = cred;
list_move(&sp->so_list, &clp->cl_state_owners);
clp->cl_nunused--;
}
return sp;
}
struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp) struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp)
{ {
struct nfs4_state_owner *sp; struct nfs4_state_owner *sp;
struct rb_node *pos;
struct rpc_cred *cred = NULL; struct rpc_cred *cred = NULL;
list_for_each_entry(sp, &clp->cl_state_owners, so_list) { for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) {
sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
if (list_empty(&sp->so_states)) if (list_empty(&sp->so_states))
continue; continue;
cred = get_rpccred(sp->so_cred); cred = get_rpccred(sp->so_cred);
@ -107,32 +90,146 @@ struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp)
static struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp) static struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp)
{ {
struct nfs4_state_owner *sp; struct nfs4_state_owner *sp;
struct rb_node *pos;
if (!list_empty(&clp->cl_state_owners)) { pos = rb_first(&clp->cl_state_owners);
sp = list_entry(clp->cl_state_owners.next, if (pos != NULL) {
struct nfs4_state_owner, so_list); sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
return get_rpccred(sp->so_cred); return get_rpccred(sp->so_cred);
} }
return NULL; return NULL;
} }
static struct nfs4_state_owner * static void nfs_alloc_unique_id(struct rb_root *root, struct nfs_unique_id *new,
nfs4_find_state_owner(struct nfs_client *clp, struct rpc_cred *cred) __u64 minval, int maxbits)
{ {
struct rb_node **p, *parent;
struct nfs_unique_id *pos;
__u64 mask = ~0ULL;
if (maxbits < 64)
mask = (1ULL << maxbits) - 1ULL;
/* Ensure distribution is more or less flat */
get_random_bytes(&new->id, sizeof(new->id));
new->id &= mask;
if (new->id < minval)
new->id += minval;
retry:
p = &root->rb_node;
parent = NULL;
while (*p != NULL) {
parent = *p;
pos = rb_entry(parent, struct nfs_unique_id, rb_node);
if (new->id < pos->id)
p = &(*p)->rb_left;
else if (new->id > pos->id)
p = &(*p)->rb_right;
else
goto id_exists;
}
rb_link_node(&new->rb_node, parent, p);
rb_insert_color(&new->rb_node, root);
return;
id_exists:
for (;;) {
new->id++;
if (new->id < minval || (new->id & mask) != new->id) {
new->id = minval;
break;
}
parent = rb_next(parent);
if (parent == NULL)
break;
pos = rb_entry(parent, struct nfs_unique_id, rb_node);
if (new->id < pos->id)
break;
}
goto retry;
}
static void nfs_free_unique_id(struct rb_root *root, struct nfs_unique_id *id)
{
rb_erase(&id->rb_node, root);
}
static struct nfs4_state_owner *
nfs4_find_state_owner(struct nfs_server *server, struct rpc_cred *cred)
{
struct nfs_client *clp = server->nfs_client;
struct rb_node **p = &clp->cl_state_owners.rb_node,
*parent = NULL;
struct nfs4_state_owner *sp, *res = NULL; struct nfs4_state_owner *sp, *res = NULL;
list_for_each_entry(sp, &clp->cl_state_owners, so_list) { while (*p != NULL) {
if (sp->so_cred != cred) parent = *p;
sp = rb_entry(parent, struct nfs4_state_owner, so_client_node);
if (server < sp->so_server) {
p = &parent->rb_left;
continue; continue;
atomic_inc(&sp->so_count); }
/* Move to the head of the list */ if (server > sp->so_server) {
list_move(&sp->so_list, &clp->cl_state_owners); p = &parent->rb_right;
res = sp; continue;
break; }
if (cred < sp->so_cred)
p = &parent->rb_left;
else if (cred > sp->so_cred)
p = &parent->rb_right;
else {
atomic_inc(&sp->so_count);
res = sp;
break;
}
} }
return res; return res;
} }
static struct nfs4_state_owner *
nfs4_insert_state_owner(struct nfs_client *clp, struct nfs4_state_owner *new)
{
struct rb_node **p = &clp->cl_state_owners.rb_node,
*parent = NULL;
struct nfs4_state_owner *sp;
while (*p != NULL) {
parent = *p;
sp = rb_entry(parent, struct nfs4_state_owner, so_client_node);
if (new->so_server < sp->so_server) {
p = &parent->rb_left;
continue;
}
if (new->so_server > sp->so_server) {
p = &parent->rb_right;
continue;
}
if (new->so_cred < sp->so_cred)
p = &parent->rb_left;
else if (new->so_cred > sp->so_cred)
p = &parent->rb_right;
else {
atomic_inc(&sp->so_count);
return sp;
}
}
nfs_alloc_unique_id(&clp->cl_openowner_id, &new->so_owner_id, 1, 64);
rb_link_node(&new->so_client_node, parent, p);
rb_insert_color(&new->so_client_node, &clp->cl_state_owners);
return new;
}
static void
nfs4_remove_state_owner(struct nfs_client *clp, struct nfs4_state_owner *sp)
{
if (!RB_EMPTY_NODE(&sp->so_client_node))
rb_erase(&sp->so_client_node, &clp->cl_state_owners);
nfs_free_unique_id(&clp->cl_openowner_id, &sp->so_owner_id);
}
/* /*
* nfs4_alloc_state_owner(): this is called on the OPEN or CREATE path to * nfs4_alloc_state_owner(): this is called on the OPEN or CREATE path to
* create a new state_owner. * create a new state_owner.
@ -160,10 +257,14 @@ nfs4_alloc_state_owner(void)
void void
nfs4_drop_state_owner(struct nfs4_state_owner *sp) nfs4_drop_state_owner(struct nfs4_state_owner *sp)
{ {
struct nfs_client *clp = sp->so_client; if (!RB_EMPTY_NODE(&sp->so_client_node)) {
spin_lock(&clp->cl_lock); struct nfs_client *clp = sp->so_client;
list_del_init(&sp->so_list);
spin_unlock(&clp->cl_lock); spin_lock(&clp->cl_lock);
rb_erase(&sp->so_client_node, &clp->cl_state_owners);
RB_CLEAR_NODE(&sp->so_client_node);
spin_unlock(&clp->cl_lock);
}
} }
/* /*
@ -175,26 +276,25 @@ struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct
struct nfs_client *clp = server->nfs_client; struct nfs_client *clp = server->nfs_client;
struct nfs4_state_owner *sp, *new; struct nfs4_state_owner *sp, *new;
get_rpccred(cred);
new = nfs4_alloc_state_owner();
spin_lock(&clp->cl_lock); spin_lock(&clp->cl_lock);
sp = nfs4_find_state_owner(clp, cred); sp = nfs4_find_state_owner(server, cred);
if (sp == NULL)
sp = nfs4_client_grab_unused(clp, cred);
if (sp == NULL && new != NULL) {
list_add(&new->so_list, &clp->cl_state_owners);
new->so_client = clp;
new->so_id = nfs4_alloc_lockowner_id(clp);
new->so_cred = cred;
sp = new;
new = NULL;
}
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
kfree(new);
if (sp != NULL) if (sp != NULL)
return sp; return sp;
put_rpccred(cred); new = nfs4_alloc_state_owner();
return NULL; if (new == NULL)
return NULL;
new->so_client = clp;
new->so_server = server;
new->so_cred = cred;
spin_lock(&clp->cl_lock);
sp = nfs4_insert_state_owner(clp, new);
spin_unlock(&clp->cl_lock);
if (sp == new)
get_rpccred(cred);
else
kfree(new);
return sp;
} }
/* /*
@ -208,18 +308,7 @@ void nfs4_put_state_owner(struct nfs4_state_owner *sp)
if (!atomic_dec_and_lock(&sp->so_count, &clp->cl_lock)) if (!atomic_dec_and_lock(&sp->so_count, &clp->cl_lock))
return; return;
if (clp->cl_nunused >= OPENOWNER_POOL_SIZE) nfs4_remove_state_owner(clp, sp);
goto out_free;
if (list_empty(&sp->so_list))
goto out_free;
list_move(&sp->so_list, &clp->cl_unused);
clp->cl_nunused++;
spin_unlock(&clp->cl_lock);
put_rpccred(cred);
cred = NULL;
return;
out_free:
list_del(&sp->so_list);
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
put_rpccred(cred); put_rpccred(cred);
kfree(sp); kfree(sp);
@ -236,6 +325,7 @@ nfs4_alloc_open_state(void)
atomic_set(&state->count, 1); atomic_set(&state->count, 1);
INIT_LIST_HEAD(&state->lock_states); INIT_LIST_HEAD(&state->lock_states);
spin_lock_init(&state->state_lock); spin_lock_init(&state->state_lock);
seqlock_init(&state->seqlock);
return state; return state;
} }
@ -263,13 +353,10 @@ __nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner)
struct nfs4_state *state; struct nfs4_state *state;
list_for_each_entry(state, &nfsi->open_states, inode_states) { list_for_each_entry(state, &nfsi->open_states, inode_states) {
/* Is this in the process of being freed? */ if (state->owner != owner)
if (state->state == 0)
continue; continue;
if (state->owner == owner) { if (atomic_inc_not_zero(&state->count))
atomic_inc(&state->count);
return state; return state;
}
} }
return NULL; return NULL;
} }
@ -341,16 +428,15 @@ void nfs4_put_open_state(struct nfs4_state *state)
/* /*
* Close the current file. * Close the current file.
*/ */
void nfs4_close_state(struct nfs4_state *state, mode_t mode) void nfs4_close_state(struct path *path, struct nfs4_state *state, mode_t mode)
{ {
struct inode *inode = state->inode;
struct nfs4_state_owner *owner = state->owner; struct nfs4_state_owner *owner = state->owner;
int oldstate, newstate = 0; int call_close = 0;
int newstate;
atomic_inc(&owner->so_count); atomic_inc(&owner->so_count);
/* Protect against nfs4_find_state() */ /* Protect against nfs4_find_state() */
spin_lock(&owner->so_lock); spin_lock(&owner->so_lock);
spin_lock(&inode->i_lock);
switch (mode & (FMODE_READ | FMODE_WRITE)) { switch (mode & (FMODE_READ | FMODE_WRITE)) {
case FMODE_READ: case FMODE_READ:
state->n_rdonly--; state->n_rdonly--;
@ -361,24 +447,29 @@ void nfs4_close_state(struct nfs4_state *state, mode_t mode)
case FMODE_READ|FMODE_WRITE: case FMODE_READ|FMODE_WRITE:
state->n_rdwr--; state->n_rdwr--;
} }
oldstate = newstate = state->state; newstate = FMODE_READ|FMODE_WRITE;
if (state->n_rdwr == 0) { if (state->n_rdwr == 0) {
if (state->n_rdonly == 0) if (state->n_rdonly == 0) {
newstate &= ~FMODE_READ; newstate &= ~FMODE_READ;
if (state->n_wronly == 0) call_close |= test_bit(NFS_O_RDONLY_STATE, &state->flags);
call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags);
}
if (state->n_wronly == 0) {
newstate &= ~FMODE_WRITE; newstate &= ~FMODE_WRITE;
call_close |= test_bit(NFS_O_WRONLY_STATE, &state->flags);
call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags);
}
if (newstate == 0)
clear_bit(NFS_DELEGATED_STATE, &state->flags);
} }
if (test_bit(NFS_DELEGATED_STATE, &state->flags)) { nfs4_state_set_mode_locked(state, newstate);
nfs4_state_set_mode_locked(state, newstate);
oldstate = newstate;
}
spin_unlock(&inode->i_lock);
spin_unlock(&owner->so_lock); spin_unlock(&owner->so_lock);
if (oldstate != newstate && nfs4_do_close(inode, state) == 0) if (!call_close) {
return; nfs4_put_open_state(state);
nfs4_put_open_state(state); nfs4_put_state_owner(owner);
nfs4_put_state_owner(owner); } else
nfs4_do_close(path, state);
} }
/* /*
@ -415,12 +506,22 @@ static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, f
atomic_set(&lsp->ls_count, 1); atomic_set(&lsp->ls_count, 1);
lsp->ls_owner = fl_owner; lsp->ls_owner = fl_owner;
spin_lock(&clp->cl_lock); spin_lock(&clp->cl_lock);
lsp->ls_id = nfs4_alloc_lockowner_id(clp); nfs_alloc_unique_id(&clp->cl_lockowner_id, &lsp->ls_id, 1, 64);
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
INIT_LIST_HEAD(&lsp->ls_locks); INIT_LIST_HEAD(&lsp->ls_locks);
return lsp; return lsp;
} }
static void nfs4_free_lock_state(struct nfs4_lock_state *lsp)
{
struct nfs_client *clp = lsp->ls_state->owner->so_client;
spin_lock(&clp->cl_lock);
nfs_free_unique_id(&clp->cl_lockowner_id, &lsp->ls_id);
spin_unlock(&clp->cl_lock);
kfree(lsp);
}
/* /*
* Return a compatible lock_state. If no initialized lock_state structure * Return a compatible lock_state. If no initialized lock_state structure
* exists, return an uninitialized one. * exists, return an uninitialized one.
@ -450,7 +551,8 @@ static struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_
return NULL; return NULL;
} }
spin_unlock(&state->state_lock); spin_unlock(&state->state_lock);
kfree(new); if (new != NULL)
nfs4_free_lock_state(new);
return lsp; return lsp;
} }
@ -471,7 +573,7 @@ void nfs4_put_lock_state(struct nfs4_lock_state *lsp)
if (list_empty(&state->lock_states)) if (list_empty(&state->lock_states))
clear_bit(LK_STATE_IN_USE, &state->flags); clear_bit(LK_STATE_IN_USE, &state->flags);
spin_unlock(&state->state_lock); spin_unlock(&state->state_lock);
kfree(lsp); nfs4_free_lock_state(lsp);
} }
static void nfs4_fl_copy_lock(struct file_lock *dst, struct file_lock *src) static void nfs4_fl_copy_lock(struct file_lock *dst, struct file_lock *src)
@ -513,8 +615,12 @@ int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl)
void nfs4_copy_stateid(nfs4_stateid *dst, struct nfs4_state *state, fl_owner_t fl_owner) void nfs4_copy_stateid(nfs4_stateid *dst, struct nfs4_state *state, fl_owner_t fl_owner)
{ {
struct nfs4_lock_state *lsp; struct nfs4_lock_state *lsp;
int seq;
memcpy(dst, &state->stateid, sizeof(*dst)); do {
seq = read_seqbegin(&state->seqlock);
memcpy(dst, &state->stateid, sizeof(*dst));
} while (read_seqretry(&state->seqlock, seq));
if (test_bit(LK_STATE_IN_USE, &state->flags) == 0) if (test_bit(LK_STATE_IN_USE, &state->flags) == 0)
return; return;
@ -557,12 +663,18 @@ void nfs_free_seqid(struct nfs_seqid *seqid)
* failed with a seqid incrementing error - * failed with a seqid incrementing error -
* see comments nfs_fs.h:seqid_mutating_error() * see comments nfs_fs.h:seqid_mutating_error()
*/ */
static inline void nfs_increment_seqid(int status, struct nfs_seqid *seqid) static void nfs_increment_seqid(int status, struct nfs_seqid *seqid)
{ {
switch (status) { switch (status) {
case 0: case 0:
break; break;
case -NFS4ERR_BAD_SEQID: case -NFS4ERR_BAD_SEQID:
if (seqid->sequence->flags & NFS_SEQID_CONFIRMED)
return;
printk(KERN_WARNING "NFS: v4 server returned a bad"
"sequence-id error on an"
"unconfirmed sequence %p!\n",
seqid->sequence);
case -NFS4ERR_STALE_CLIENTID: case -NFS4ERR_STALE_CLIENTID:
case -NFS4ERR_STALE_STATEID: case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_BAD_STATEID: case -NFS4ERR_BAD_STATEID:
@ -586,7 +698,7 @@ void nfs_increment_open_seqid(int status, struct nfs_seqid *seqid)
struct nfs4_state_owner, so_seqid); struct nfs4_state_owner, so_seqid);
nfs4_drop_state_owner(sp); nfs4_drop_state_owner(sp);
} }
return nfs_increment_seqid(status, seqid); nfs_increment_seqid(status, seqid);
} }
/* /*
@ -596,7 +708,7 @@ void nfs_increment_open_seqid(int status, struct nfs_seqid *seqid)
*/ */
void nfs_increment_lock_seqid(int status, struct nfs_seqid *seqid) void nfs_increment_lock_seqid(int status, struct nfs_seqid *seqid)
{ {
return nfs_increment_seqid(status, seqid); nfs_increment_seqid(status, seqid);
} }
int nfs_wait_on_sequence(struct nfs_seqid *seqid, struct rpc_task *task) int nfs_wait_on_sequence(struct nfs_seqid *seqid, struct rpc_task *task)
@ -748,15 +860,21 @@ out_err:
static void nfs4_state_mark_reclaim(struct nfs_client *clp) static void nfs4_state_mark_reclaim(struct nfs_client *clp)
{ {
struct nfs4_state_owner *sp; struct nfs4_state_owner *sp;
struct rb_node *pos;
struct nfs4_state *state; struct nfs4_state *state;
struct nfs4_lock_state *lock; struct nfs4_lock_state *lock;
/* Reset all sequence ids to zero */ /* Reset all sequence ids to zero */
list_for_each_entry(sp, &clp->cl_state_owners, so_list) { for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) {
sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
sp->so_seqid.counter = 0; sp->so_seqid.counter = 0;
sp->so_seqid.flags = 0; sp->so_seqid.flags = 0;
spin_lock(&sp->so_lock); spin_lock(&sp->so_lock);
list_for_each_entry(state, &sp->so_states, open_states) { list_for_each_entry(state, &sp->so_states, open_states) {
clear_bit(NFS_DELEGATED_STATE, &state->flags);
clear_bit(NFS_O_RDONLY_STATE, &state->flags);
clear_bit(NFS_O_WRONLY_STATE, &state->flags);
clear_bit(NFS_O_RDWR_STATE, &state->flags);
list_for_each_entry(lock, &state->lock_states, ls_locks) { list_for_each_entry(lock, &state->lock_states, ls_locks) {
lock->ls_seqid.counter = 0; lock->ls_seqid.counter = 0;
lock->ls_seqid.flags = 0; lock->ls_seqid.flags = 0;
@ -771,6 +889,7 @@ static int reclaimer(void *ptr)
{ {
struct nfs_client *clp = ptr; struct nfs_client *clp = ptr;
struct nfs4_state_owner *sp; struct nfs4_state_owner *sp;
struct rb_node *pos;
struct nfs4_state_recovery_ops *ops; struct nfs4_state_recovery_ops *ops;
struct rpc_cred *cred; struct rpc_cred *cred;
int status = 0; int status = 0;
@ -816,7 +935,8 @@ restart_loop:
/* Mark all delegations for reclaim */ /* Mark all delegations for reclaim */
nfs_delegation_mark_reclaim(clp); nfs_delegation_mark_reclaim(clp);
/* Note: list is protected by exclusive lock on cl->cl_sem */ /* Note: list is protected by exclusive lock on cl->cl_sem */
list_for_each_entry(sp, &clp->cl_state_owners, so_list) { for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) {
sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
status = nfs4_reclaim_open_state(ops, sp); status = nfs4_reclaim_open_state(ops, sp);
if (status < 0) { if (status < 0) {
if (status == -NFS4ERR_NO_GRACE) { if (status == -NFS4ERR_NO_GRACE) {

View File

@ -68,9 +68,10 @@ static int nfs4_stat_to_errno(int);
#endif #endif
/* lock,open owner id: /* lock,open owner id:
* we currently use size 1 (u32) out of (NFS4_OPAQUE_LIMIT >> 2) * we currently use size 2 (u64) out of (NFS4_OPAQUE_LIMIT >> 2)
*/ */
#define owner_id_maxsz (1 + 1) #define open_owner_id_maxsz (1 + 4)
#define lock_owner_id_maxsz (1 + 4)
#define compound_encode_hdr_maxsz (3 + (NFS4_MAXTAGLEN >> 2)) #define compound_encode_hdr_maxsz (3 + (NFS4_MAXTAGLEN >> 2))
#define compound_decode_hdr_maxsz (3 + (NFS4_MAXTAGLEN >> 2)) #define compound_decode_hdr_maxsz (3 + (NFS4_MAXTAGLEN >> 2))
#define op_encode_hdr_maxsz (1) #define op_encode_hdr_maxsz (1)
@ -87,9 +88,11 @@ static int nfs4_stat_to_errno(int);
#define encode_getattr_maxsz (op_encode_hdr_maxsz + nfs4_fattr_bitmap_maxsz) #define encode_getattr_maxsz (op_encode_hdr_maxsz + nfs4_fattr_bitmap_maxsz)
#define nfs4_name_maxsz (1 + ((3 + NFS4_MAXNAMLEN) >> 2)) #define nfs4_name_maxsz (1 + ((3 + NFS4_MAXNAMLEN) >> 2))
#define nfs4_path_maxsz (1 + ((3 + NFS4_MAXPATHLEN) >> 2)) #define nfs4_path_maxsz (1 + ((3 + NFS4_MAXPATHLEN) >> 2))
#define nfs4_owner_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ))
#define nfs4_group_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ))
/* This is based on getfattr, which uses the most attributes: */ /* This is based on getfattr, which uses the most attributes: */
#define nfs4_fattr_value_maxsz (1 + (1 + 2 + 2 + 4 + 2 + 1 + 1 + 2 + 2 + \ #define nfs4_fattr_value_maxsz (1 + (1 + 2 + 2 + 4 + 2 + 1 + 1 + 2 + 2 + \
3 + 3 + 3 + 2 * nfs4_name_maxsz)) 3 + 3 + 3 + nfs4_owner_maxsz + nfs4_group_maxsz))
#define nfs4_fattr_maxsz (nfs4_fattr_bitmap_maxsz + \ #define nfs4_fattr_maxsz (nfs4_fattr_bitmap_maxsz + \
nfs4_fattr_value_maxsz) nfs4_fattr_value_maxsz)
#define decode_getattr_maxsz (op_decode_hdr_maxsz + nfs4_fattr_maxsz) #define decode_getattr_maxsz (op_decode_hdr_maxsz + nfs4_fattr_maxsz)
@ -116,8 +119,27 @@ static int nfs4_stat_to_errno(int);
3 + (NFS4_VERIFIER_SIZE >> 2)) 3 + (NFS4_VERIFIER_SIZE >> 2))
#define decode_setclientid_confirm_maxsz \ #define decode_setclientid_confirm_maxsz \
(op_decode_hdr_maxsz) (op_decode_hdr_maxsz)
#define encode_lookup_maxsz (op_encode_hdr_maxsz + \ #define encode_lookup_maxsz (op_encode_hdr_maxsz + nfs4_name_maxsz)
1 + ((3 + NFS4_FHSIZE) >> 2)) #define decode_lookup_maxsz (op_decode_hdr_maxsz)
#define encode_share_access_maxsz \
(2)
#define encode_createmode_maxsz (1 + nfs4_fattr_maxsz)
#define encode_opentype_maxsz (1 + encode_createmode_maxsz)
#define encode_claim_null_maxsz (1 + nfs4_name_maxsz)
#define encode_open_maxsz (op_encode_hdr_maxsz + \
2 + encode_share_access_maxsz + 2 + \
open_owner_id_maxsz + \
encode_opentype_maxsz + \
encode_claim_null_maxsz)
#define decode_ace_maxsz (3 + nfs4_owner_maxsz)
#define decode_delegation_maxsz (1 + XDR_QUADLEN(NFS4_STATEID_SIZE) + 1 + \
decode_ace_maxsz)
#define decode_change_info_maxsz (5)
#define decode_open_maxsz (op_decode_hdr_maxsz + \
XDR_QUADLEN(NFS4_STATEID_SIZE) + \
decode_change_info_maxsz + 1 + \
nfs4_fattr_bitmap_maxsz + \
decode_delegation_maxsz)
#define encode_remove_maxsz (op_encode_hdr_maxsz + \ #define encode_remove_maxsz (op_encode_hdr_maxsz + \
nfs4_name_maxsz) nfs4_name_maxsz)
#define encode_rename_maxsz (op_encode_hdr_maxsz + \ #define encode_rename_maxsz (op_encode_hdr_maxsz + \
@ -134,9 +156,15 @@ static int nfs4_stat_to_errno(int);
#define encode_create_maxsz (op_encode_hdr_maxsz + \ #define encode_create_maxsz (op_encode_hdr_maxsz + \
2 + nfs4_name_maxsz + \ 2 + nfs4_name_maxsz + \
nfs4_fattr_maxsz) nfs4_fattr_maxsz)
#define decode_create_maxsz (op_decode_hdr_maxsz + 8) #define decode_create_maxsz (op_decode_hdr_maxsz + \
decode_change_info_maxsz + \
nfs4_fattr_bitmap_maxsz)
#define encode_delegreturn_maxsz (op_encode_hdr_maxsz + 4) #define encode_delegreturn_maxsz (op_encode_hdr_maxsz + 4)
#define decode_delegreturn_maxsz (op_decode_hdr_maxsz) #define decode_delegreturn_maxsz (op_decode_hdr_maxsz)
#define encode_fs_locations_maxsz \
(encode_getattr_maxsz)
#define decode_fs_locations_maxsz \
(0)
#define NFS4_enc_compound_sz (1024) /* XXX: large enough? */ #define NFS4_enc_compound_sz (1024) /* XXX: large enough? */
#define NFS4_dec_compound_sz (1024) /* XXX: large enough? */ #define NFS4_dec_compound_sz (1024) /* XXX: large enough? */
#define NFS4_enc_read_sz (compound_encode_hdr_maxsz + \ #define NFS4_enc_read_sz (compound_encode_hdr_maxsz + \
@ -174,16 +202,21 @@ static int nfs4_stat_to_errno(int);
op_decode_hdr_maxsz + 2 + \ op_decode_hdr_maxsz + 2 + \
decode_getattr_maxsz) decode_getattr_maxsz)
#define NFS4_enc_open_sz (compound_encode_hdr_maxsz + \ #define NFS4_enc_open_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
op_encode_hdr_maxsz + \ encode_savefh_maxsz + \
13 + 3 + 2 + 64 + \ encode_open_maxsz + \
encode_getattr_maxsz + \ encode_getfh_maxsz + \
encode_getfh_maxsz) encode_getattr_maxsz + \
encode_restorefh_maxsz + \
encode_getattr_maxsz)
#define NFS4_dec_open_sz (compound_decode_hdr_maxsz + \ #define NFS4_dec_open_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
op_decode_hdr_maxsz + 4 + 5 + 2 + 3 + \ decode_savefh_maxsz + \
decode_getattr_maxsz + \ decode_open_maxsz + \
decode_getfh_maxsz) decode_getfh_maxsz + \
decode_getattr_maxsz + \
decode_restorefh_maxsz + \
decode_getattr_maxsz)
#define NFS4_enc_open_confirm_sz \ #define NFS4_enc_open_confirm_sz \
(compound_encode_hdr_maxsz + \ (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
@ -193,12 +226,12 @@ static int nfs4_stat_to_errno(int);
op_decode_hdr_maxsz + 4) op_decode_hdr_maxsz + 4)
#define NFS4_enc_open_noattr_sz (compound_encode_hdr_maxsz + \ #define NFS4_enc_open_noattr_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
op_encode_hdr_maxsz + \ encode_open_maxsz + \
11) encode_getattr_maxsz)
#define NFS4_dec_open_noattr_sz (compound_decode_hdr_maxsz + \ #define NFS4_dec_open_noattr_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
op_decode_hdr_maxsz + \ decode_open_maxsz + \
4 + 5 + 2 + 3) decode_getattr_maxsz)
#define NFS4_enc_open_downgrade_sz \ #define NFS4_enc_open_downgrade_sz \
(compound_encode_hdr_maxsz + \ (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
@ -256,19 +289,19 @@ static int nfs4_stat_to_errno(int);
op_encode_hdr_maxsz + \ op_encode_hdr_maxsz + \
1 + 1 + 2 + 2 + \ 1 + 1 + 2 + 2 + \
1 + 4 + 1 + 2 + \ 1 + 4 + 1 + 2 + \
owner_id_maxsz) lock_owner_id_maxsz)
#define NFS4_dec_lock_sz (compound_decode_hdr_maxsz + \ #define NFS4_dec_lock_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
decode_getattr_maxsz + \ decode_getattr_maxsz + \
op_decode_hdr_maxsz + \ op_decode_hdr_maxsz + \
2 + 2 + 1 + 2 + \ 2 + 2 + 1 + 2 + \
owner_id_maxsz) lock_owner_id_maxsz)
#define NFS4_enc_lockt_sz (compound_encode_hdr_maxsz + \ #define NFS4_enc_lockt_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
encode_getattr_maxsz + \ encode_getattr_maxsz + \
op_encode_hdr_maxsz + \ op_encode_hdr_maxsz + \
1 + 2 + 2 + 2 + \ 1 + 2 + 2 + 2 + \
owner_id_maxsz) lock_owner_id_maxsz)
#define NFS4_dec_lockt_sz (NFS4_dec_lock_sz) #define NFS4_dec_lockt_sz (NFS4_dec_lock_sz)
#define NFS4_enc_locku_sz (compound_encode_hdr_maxsz + \ #define NFS4_enc_locku_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
@ -298,7 +331,7 @@ static int nfs4_stat_to_errno(int);
encode_getfh_maxsz) encode_getfh_maxsz)
#define NFS4_dec_lookup_sz (compound_decode_hdr_maxsz + \ #define NFS4_dec_lookup_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
op_decode_hdr_maxsz + \ decode_lookup_maxsz + \
decode_getattr_maxsz + \ decode_getattr_maxsz + \
decode_getfh_maxsz) decode_getfh_maxsz)
#define NFS4_enc_lookup_root_sz (compound_encode_hdr_maxsz + \ #define NFS4_enc_lookup_root_sz (compound_encode_hdr_maxsz + \
@ -417,12 +450,13 @@ static int nfs4_stat_to_errno(int);
#define NFS4_enc_fs_locations_sz \ #define NFS4_enc_fs_locations_sz \
(compound_encode_hdr_maxsz + \ (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
encode_getattr_maxsz) encode_lookup_maxsz + \
encode_fs_locations_maxsz)
#define NFS4_dec_fs_locations_sz \ #define NFS4_dec_fs_locations_sz \
(compound_decode_hdr_maxsz + \ (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
op_decode_hdr_maxsz + \ decode_lookup_maxsz + \
nfs4_fattr_bitmap_maxsz) decode_fs_locations_maxsz)
static struct { static struct {
unsigned int mode; unsigned int mode;
@ -793,13 +827,14 @@ static int encode_lock(struct xdr_stream *xdr, const struct nfs_lock_args *args)
WRITE64(nfs4_lock_length(args->fl)); WRITE64(nfs4_lock_length(args->fl));
WRITE32(args->new_lock_owner); WRITE32(args->new_lock_owner);
if (args->new_lock_owner){ if (args->new_lock_owner){
RESERVE_SPACE(4+NFS4_STATEID_SIZE+20); RESERVE_SPACE(4+NFS4_STATEID_SIZE+32);
WRITE32(args->open_seqid->sequence->counter); WRITE32(args->open_seqid->sequence->counter);
WRITEMEM(args->open_stateid->data, NFS4_STATEID_SIZE); WRITEMEM(args->open_stateid->data, NFS4_STATEID_SIZE);
WRITE32(args->lock_seqid->sequence->counter); WRITE32(args->lock_seqid->sequence->counter);
WRITE64(args->lock_owner.clientid); WRITE64(args->lock_owner.clientid);
WRITE32(4); WRITE32(16);
WRITE32(args->lock_owner.id); WRITEMEM("lock id:", 8);
WRITE64(args->lock_owner.id);
} }
else { else {
RESERVE_SPACE(NFS4_STATEID_SIZE+4); RESERVE_SPACE(NFS4_STATEID_SIZE+4);
@ -814,14 +849,15 @@ static int encode_lockt(struct xdr_stream *xdr, const struct nfs_lockt_args *arg
{ {
__be32 *p; __be32 *p;
RESERVE_SPACE(40); RESERVE_SPACE(52);
WRITE32(OP_LOCKT); WRITE32(OP_LOCKT);
WRITE32(nfs4_lock_type(args->fl, 0)); WRITE32(nfs4_lock_type(args->fl, 0));
WRITE64(args->fl->fl_start); WRITE64(args->fl->fl_start);
WRITE64(nfs4_lock_length(args->fl)); WRITE64(nfs4_lock_length(args->fl));
WRITE64(args->lock_owner.clientid); WRITE64(args->lock_owner.clientid);
WRITE32(4); WRITE32(16);
WRITE32(args->lock_owner.id); WRITEMEM("lock id:", 8);
WRITE64(args->lock_owner.id);
return 0; return 0;
} }
@ -886,10 +922,11 @@ static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_opena
WRITE32(OP_OPEN); WRITE32(OP_OPEN);
WRITE32(arg->seqid->sequence->counter); WRITE32(arg->seqid->sequence->counter);
encode_share_access(xdr, arg->open_flags); encode_share_access(xdr, arg->open_flags);
RESERVE_SPACE(16); RESERVE_SPACE(28);
WRITE64(arg->clientid); WRITE64(arg->clientid);
WRITE32(4); WRITE32(16);
WRITE32(arg->id); WRITEMEM("open id:", 8);
WRITE64(arg->id);
} }
static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_openargs *arg) static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_openargs *arg)
@ -1071,7 +1108,7 @@ static int encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args)
static int encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg *readdir, struct rpc_rqst *req) static int encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg *readdir, struct rpc_rqst *req)
{ {
struct rpc_auth *auth = req->rq_task->tk_auth; struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
uint32_t attrs[2] = { uint32_t attrs[2] = {
FATTR4_WORD0_RDATTR_ERROR|FATTR4_WORD0_FILEID, FATTR4_WORD0_RDATTR_ERROR|FATTR4_WORD0_FILEID,
FATTR4_WORD1_MOUNTED_ON_FILEID, FATTR4_WORD1_MOUNTED_ON_FILEID,
@ -1117,7 +1154,7 @@ static int encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg
static int encode_readlink(struct xdr_stream *xdr, const struct nfs4_readlink *readlink, struct rpc_rqst *req) static int encode_readlink(struct xdr_stream *xdr, const struct nfs4_readlink *readlink, struct rpc_rqst *req)
{ {
struct rpc_auth *auth = req->rq_task->tk_auth; struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
unsigned int replen; unsigned int replen;
__be32 *p; __be32 *p;
@ -1735,7 +1772,7 @@ out:
*/ */
static int nfs4_xdr_enc_read(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args) static int nfs4_xdr_enc_read(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args)
{ {
struct rpc_auth *auth = req->rq_task->tk_auth; struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
struct xdr_stream xdr; struct xdr_stream xdr;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 2, .nops = 2,
@ -1795,7 +1832,7 @@ nfs4_xdr_enc_getacl(struct rpc_rqst *req, __be32 *p,
struct nfs_getaclargs *args) struct nfs_getaclargs *args)
{ {
struct xdr_stream xdr; struct xdr_stream xdr;
struct rpc_auth *auth = req->rq_task->tk_auth; struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 2, .nops = 2,
}; };
@ -2030,7 +2067,7 @@ static int nfs4_xdr_enc_fs_locations(struct rpc_rqst *req, __be32 *p, struct nfs
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 3, .nops = 3,
}; };
struct rpc_auth *auth = req->rq_task->tk_auth; struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
int replen; int replen;
int status; int status;
@ -3269,7 +3306,7 @@ static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res)
static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res) static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res)
{ {
__be32 *p; __be32 *p;
uint32_t bmlen; uint32_t savewords, bmlen, i;
int status; int status;
status = decode_op_hdr(xdr, OP_OPEN); status = decode_op_hdr(xdr, OP_OPEN);
@ -3287,7 +3324,12 @@ static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res)
goto xdr_error; goto xdr_error;
READ_BUF(bmlen << 2); READ_BUF(bmlen << 2);
p += bmlen; savewords = min_t(uint32_t, bmlen, NFS4_BITMAP_SIZE);
for (i = 0; i < savewords; ++i)
READ32(res->attrset[i]);
for (; i < NFS4_BITMAP_SIZE; i++)
res->attrset[i] = 0;
return decode_delegation(xdr, res); return decode_delegation(xdr, res);
xdr_error: xdr_error:
dprintk("%s: Bitmap too large! Length = %u\n", __FUNCTION__, bmlen); dprintk("%s: Bitmap too large! Length = %u\n", __FUNCTION__, bmlen);

View File

@ -428,7 +428,7 @@ static int __init root_nfs_getport(int program, int version, int proto)
printk(KERN_NOTICE "Looking up port of RPC %d/%d on %u.%u.%u.%u\n", printk(KERN_NOTICE "Looking up port of RPC %d/%d on %u.%u.%u.%u\n",
program, version, NIPQUAD(servaddr)); program, version, NIPQUAD(servaddr));
set_sockaddr(&sin, servaddr, 0); set_sockaddr(&sin, servaddr, 0);
return rpcb_getport_external(&sin, program, version, proto); return rpcb_getport_sync(&sin, program, version, proto);
} }
@ -496,7 +496,8 @@ static int __init root_nfs_get_handle(void)
NFS_MNT3_VERSION : NFS_MNT_VERSION; NFS_MNT3_VERSION : NFS_MNT_VERSION;
set_sockaddr(&sin, servaddr, htons(mount_port)); set_sockaddr(&sin, servaddr, htons(mount_port));
status = nfsroot_mount(&sin, nfs_path, &fh, version, protocol); status = nfs_mount((struct sockaddr *) &sin, sizeof(sin), NULL,
nfs_path, version, protocol, &fh);
if (status < 0) if (status < 0)
printk(KERN_ERR "Root-NFS: Server returned error %d " printk(KERN_ERR "Root-NFS: Server returned error %d "
"while mounting %s\n", status, nfs_path); "while mounting %s\n", status, nfs_path);

View File

@ -85,9 +85,8 @@ nfs_create_request(struct nfs_open_context *ctx, struct inode *inode,
req->wb_offset = offset; req->wb_offset = offset;
req->wb_pgbase = offset; req->wb_pgbase = offset;
req->wb_bytes = count; req->wb_bytes = count;
atomic_set(&req->wb_count, 1);
req->wb_context = get_nfs_open_context(ctx); req->wb_context = get_nfs_open_context(ctx);
kref_init(&req->wb_kref);
return req; return req;
} }
@ -109,30 +108,31 @@ void nfs_unlock_request(struct nfs_page *req)
} }
/** /**
* nfs_set_page_writeback_locked - Lock a request for writeback * nfs_set_page_tag_locked - Tag a request as locked
* @req: * @req:
*/ */
int nfs_set_page_writeback_locked(struct nfs_page *req) static int nfs_set_page_tag_locked(struct nfs_page *req)
{ {
struct nfs_inode *nfsi = NFS_I(req->wb_context->dentry->d_inode); struct nfs_inode *nfsi = NFS_I(req->wb_context->path.dentry->d_inode);
if (!nfs_lock_request(req)) if (!nfs_lock_request(req))
return 0; return 0;
radix_tree_tag_set(&nfsi->nfs_page_tree, req->wb_index, NFS_PAGE_TAG_WRITEBACK); radix_tree_tag_set(&nfsi->nfs_page_tree, req->wb_index, NFS_PAGE_TAG_LOCKED);
return 1; return 1;
} }
/** /**
* nfs_clear_page_writeback - Unlock request and wake up sleepers * nfs_clear_page_tag_locked - Clear request tag and wake up sleepers
*/ */
void nfs_clear_page_writeback(struct nfs_page *req) void nfs_clear_page_tag_locked(struct nfs_page *req)
{ {
struct nfs_inode *nfsi = NFS_I(req->wb_context->dentry->d_inode); struct inode *inode = req->wb_context->path.dentry->d_inode;
struct nfs_inode *nfsi = NFS_I(inode);
if (req->wb_page != NULL) { if (req->wb_page != NULL) {
spin_lock(&nfsi->req_lock); spin_lock(&inode->i_lock);
radix_tree_tag_clear(&nfsi->nfs_page_tree, req->wb_index, NFS_PAGE_TAG_WRITEBACK); radix_tree_tag_clear(&nfsi->nfs_page_tree, req->wb_index, NFS_PAGE_TAG_LOCKED);
spin_unlock(&nfsi->req_lock); spin_unlock(&inode->i_lock);
} }
nfs_unlock_request(req); nfs_unlock_request(req);
} }
@ -160,11 +160,9 @@ void nfs_clear_request(struct nfs_page *req)
* *
* Note: Should never be called with the spinlock held! * Note: Should never be called with the spinlock held!
*/ */
void static void nfs_free_request(struct kref *kref)
nfs_release_request(struct nfs_page *req)
{ {
if (!atomic_dec_and_test(&req->wb_count)) struct nfs_page *req = container_of(kref, struct nfs_page, wb_kref);
return;
/* Release struct file or cached credential */ /* Release struct file or cached credential */
nfs_clear_request(req); nfs_clear_request(req);
@ -172,6 +170,11 @@ nfs_release_request(struct nfs_page *req)
nfs_page_free(req); nfs_page_free(req);
} }
void nfs_release_request(struct nfs_page *req)
{
kref_put(&req->wb_kref, nfs_free_request);
}
static int nfs_wait_bit_interruptible(void *word) static int nfs_wait_bit_interruptible(void *word)
{ {
int ret = 0; int ret = 0;
@ -193,7 +196,7 @@ static int nfs_wait_bit_interruptible(void *word)
int int
nfs_wait_on_request(struct nfs_page *req) nfs_wait_on_request(struct nfs_page *req)
{ {
struct rpc_clnt *clnt = NFS_CLIENT(req->wb_context->dentry->d_inode); struct rpc_clnt *clnt = NFS_CLIENT(req->wb_context->path.dentry->d_inode);
sigset_t oldmask; sigset_t oldmask;
int ret = 0; int ret = 0;
@ -379,20 +382,20 @@ void nfs_pageio_cond_complete(struct nfs_pageio_descriptor *desc, pgoff_t index)
/** /**
* nfs_scan_list - Scan a list for matching requests * nfs_scan_list - Scan a list for matching requests
* @nfsi: NFS inode * @nfsi: NFS inode
* @head: One of the NFS inode request lists
* @dst: Destination list * @dst: Destination list
* @idx_start: lower bound of page->index to scan * @idx_start: lower bound of page->index to scan
* @npages: idx_start + npages sets the upper bound to scan. * @npages: idx_start + npages sets the upper bound to scan.
* @tag: tag to scan for
* *
* Moves elements from one of the inode request lists. * Moves elements from one of the inode request lists.
* If the number of requests is set to 0, the entire address_space * If the number of requests is set to 0, the entire address_space
* starting at index idx_start, is scanned. * starting at index idx_start, is scanned.
* The requests are *not* checked to ensure that they form a contiguous set. * The requests are *not* checked to ensure that they form a contiguous set.
* You must be holding the inode's req_lock when calling this function * You must be holding the inode's i_lock when calling this function
*/ */
int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *head, int nfs_scan_list(struct nfs_inode *nfsi,
struct list_head *dst, pgoff_t idx_start, struct list_head *dst, pgoff_t idx_start,
unsigned int npages) unsigned int npages, int tag)
{ {
struct nfs_page *pgvec[NFS_SCAN_MAXENTRIES]; struct nfs_page *pgvec[NFS_SCAN_MAXENTRIES];
struct nfs_page *req; struct nfs_page *req;
@ -407,9 +410,9 @@ int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *head,
idx_end = idx_start + npages - 1; idx_end = idx_start + npages - 1;
for (;;) { for (;;) {
found = radix_tree_gang_lookup(&nfsi->nfs_page_tree, found = radix_tree_gang_lookup_tag(&nfsi->nfs_page_tree,
(void **)&pgvec[0], idx_start, (void **)&pgvec[0], idx_start,
NFS_SCAN_MAXENTRIES); NFS_SCAN_MAXENTRIES, tag);
if (found <= 0) if (found <= 0)
break; break;
for (i = 0; i < found; i++) { for (i = 0; i < found; i++) {
@ -417,15 +420,18 @@ int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *head,
if (req->wb_index > idx_end) if (req->wb_index > idx_end)
goto out; goto out;
idx_start = req->wb_index + 1; idx_start = req->wb_index + 1;
if (req->wb_list_head != head) if (nfs_set_page_tag_locked(req)) {
continue;
if (nfs_set_page_writeback_locked(req)) {
nfs_list_remove_request(req); nfs_list_remove_request(req);
radix_tree_tag_clear(&nfsi->nfs_page_tree,
req->wb_index, tag);
nfs_list_add_request(req, dst); nfs_list_add_request(req, dst);
res++; res++;
if (res == INT_MAX)
goto out;
} }
} }
/* for latency reduction */
cond_resched_lock(&nfsi->vfs_inode.i_lock);
} }
out: out:
return res; return res;

View File

@ -145,8 +145,8 @@ static void nfs_readpage_release(struct nfs_page *req)
unlock_page(req->wb_page); unlock_page(req->wb_page);
dprintk("NFS: read done (%s/%Ld %d@%Ld)\n", dprintk("NFS: read done (%s/%Ld %d@%Ld)\n",
req->wb_context->dentry->d_inode->i_sb->s_id, req->wb_context->path.dentry->d_inode->i_sb->s_id,
(long long)NFS_FILEID(req->wb_context->dentry->d_inode), (long long)NFS_FILEID(req->wb_context->path.dentry->d_inode),
req->wb_bytes, req->wb_bytes,
(long long)req_offset(req)); (long long)req_offset(req));
nfs_clear_request(req); nfs_clear_request(req);
@ -164,7 +164,7 @@ static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data,
int flags; int flags;
data->req = req; data->req = req;
data->inode = inode = req->wb_context->dentry->d_inode; data->inode = inode = req->wb_context->path.dentry->d_inode;
data->cred = req->wb_context->cred; data->cred = req->wb_context->cred;
data->args.fh = NFS_FH(inode); data->args.fh = NFS_FH(inode);
@ -483,17 +483,19 @@ int nfs_readpage(struct file *file, struct page *page)
*/ */
error = nfs_wb_page(inode, page); error = nfs_wb_page(inode, page);
if (error) if (error)
goto out_error; goto out_unlock;
if (PageUptodate(page))
goto out_unlock;
error = -ESTALE; error = -ESTALE;
if (NFS_STALE(inode)) if (NFS_STALE(inode))
goto out_error; goto out_unlock;
if (file == NULL) { if (file == NULL) {
error = -EBADF; error = -EBADF;
ctx = nfs_find_open_context(inode, NULL, FMODE_READ); ctx = nfs_find_open_context(inode, NULL, FMODE_READ);
if (ctx == NULL) if (ctx == NULL)
goto out_error; goto out_unlock;
} else } else
ctx = get_nfs_open_context((struct nfs_open_context *) ctx = get_nfs_open_context((struct nfs_open_context *)
file->private_data); file->private_data);
@ -502,8 +504,7 @@ int nfs_readpage(struct file *file, struct page *page)
put_nfs_open_context(ctx); put_nfs_open_context(ctx);
return error; return error;
out_unlock:
out_error:
unlock_page(page); unlock_page(page);
return error; return error;
} }
@ -520,21 +521,32 @@ readpage_async_filler(void *data, struct page *page)
struct inode *inode = page->mapping->host; struct inode *inode = page->mapping->host;
struct nfs_page *new; struct nfs_page *new;
unsigned int len; unsigned int len;
int error;
error = nfs_wb_page(inode, page);
if (error)
goto out_unlock;
if (PageUptodate(page))
goto out_unlock;
nfs_wb_page(inode, page);
len = nfs_page_length(page); len = nfs_page_length(page);
if (len == 0) if (len == 0)
return nfs_return_empty_page(page); return nfs_return_empty_page(page);
new = nfs_create_request(desc->ctx, inode, page, 0, len); new = nfs_create_request(desc->ctx, inode, page, 0, len);
if (IS_ERR(new)) { if (IS_ERR(new))
SetPageError(page); goto out_error;
unlock_page(page);
return PTR_ERR(new);
}
if (len < PAGE_CACHE_SIZE) if (len < PAGE_CACHE_SIZE)
zero_user_page(page, len, PAGE_CACHE_SIZE - len, KM_USER0); zero_user_page(page, len, PAGE_CACHE_SIZE - len, KM_USER0);
nfs_pageio_add_request(desc->pgio, new); nfs_pageio_add_request(desc->pgio, new);
return 0; return 0;
out_error:
error = PTR_ERR(new);
SetPageError(page);
out_unlock:
unlock_page(page);
return error;
} }
int nfs_readpages(struct file *filp, struct address_space *mapping, int nfs_readpages(struct file *filp, struct address_space *mapping,

File diff suppressed because it is too large Load Diff

View File

@ -117,19 +117,19 @@ static struct nfs_page *nfs_page_find_request_locked(struct page *page)
if (PagePrivate(page)) { if (PagePrivate(page)) {
req = (struct nfs_page *)page_private(page); req = (struct nfs_page *)page_private(page);
if (req != NULL) if (req != NULL)
atomic_inc(&req->wb_count); kref_get(&req->wb_kref);
} }
return req; return req;
} }
static struct nfs_page *nfs_page_find_request(struct page *page) static struct nfs_page *nfs_page_find_request(struct page *page)
{ {
struct inode *inode = page->mapping->host;
struct nfs_page *req = NULL; struct nfs_page *req = NULL;
spinlock_t *req_lock = &NFS_I(page->mapping->host)->req_lock;
spin_lock(req_lock); spin_lock(&inode->i_lock);
req = nfs_page_find_request_locked(page); req = nfs_page_find_request_locked(page);
spin_unlock(req_lock); spin_unlock(&inode->i_lock);
return req; return req;
} }
@ -191,8 +191,6 @@ static int nfs_writepage_setup(struct nfs_open_context *ctx, struct page *page,
} }
/* Update file length */ /* Update file length */
nfs_grow_file(page, offset, count); nfs_grow_file(page, offset, count);
/* Set the PG_uptodate flag? */
nfs_mark_uptodate(page, offset, count);
nfs_unlock_request(req); nfs_unlock_request(req);
return 0; return 0;
} }
@ -253,16 +251,16 @@ static void nfs_end_page_writeback(struct page *page)
static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio, static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
struct page *page) struct page *page)
{ {
struct inode *inode = page->mapping->host;
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_page *req; struct nfs_page *req;
struct nfs_inode *nfsi = NFS_I(page->mapping->host);
spinlock_t *req_lock = &nfsi->req_lock;
int ret; int ret;
spin_lock(req_lock); spin_lock(&inode->i_lock);
for(;;) { for(;;) {
req = nfs_page_find_request_locked(page); req = nfs_page_find_request_locked(page);
if (req == NULL) { if (req == NULL) {
spin_unlock(req_lock); spin_unlock(&inode->i_lock);
return 1; return 1;
} }
if (nfs_lock_request_dontget(req)) if (nfs_lock_request_dontget(req))
@ -272,28 +270,28 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
* succeed provided that someone hasn't already marked the * succeed provided that someone hasn't already marked the
* request as dirty (in which case we don't care). * request as dirty (in which case we don't care).
*/ */
spin_unlock(req_lock); spin_unlock(&inode->i_lock);
ret = nfs_wait_on_request(req); ret = nfs_wait_on_request(req);
nfs_release_request(req); nfs_release_request(req);
if (ret != 0) if (ret != 0)
return ret; return ret;
spin_lock(req_lock); spin_lock(&inode->i_lock);
} }
if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) { if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) {
/* This request is marked for commit */ /* This request is marked for commit */
spin_unlock(req_lock); spin_unlock(&inode->i_lock);
nfs_unlock_request(req); nfs_unlock_request(req);
nfs_pageio_complete(pgio); nfs_pageio_complete(pgio);
return 1; return 1;
} }
if (nfs_set_page_writeback(page) != 0) { if (nfs_set_page_writeback(page) != 0) {
spin_unlock(req_lock); spin_unlock(&inode->i_lock);
BUG(); BUG();
} }
radix_tree_tag_set(&nfsi->nfs_page_tree, req->wb_index, radix_tree_tag_set(&nfsi->nfs_page_tree, req->wb_index,
NFS_PAGE_TAG_WRITEBACK); NFS_PAGE_TAG_LOCKED);
ret = test_bit(PG_NEED_FLUSH, &req->wb_flags); ret = test_bit(PG_NEED_FLUSH, &req->wb_flags);
spin_unlock(req_lock); spin_unlock(&inode->i_lock);
nfs_pageio_add_request(pgio, req); nfs_pageio_add_request(pgio, req);
return ret; return ret;
} }
@ -400,7 +398,7 @@ static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
if (PageDirty(req->wb_page)) if (PageDirty(req->wb_page))
set_bit(PG_NEED_FLUSH, &req->wb_flags); set_bit(PG_NEED_FLUSH, &req->wb_flags);
nfsi->npages++; nfsi->npages++;
atomic_inc(&req->wb_count); kref_get(&req->wb_kref);
return 0; return 0;
} }
@ -409,12 +407,12 @@ static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
*/ */
static void nfs_inode_remove_request(struct nfs_page *req) static void nfs_inode_remove_request(struct nfs_page *req)
{ {
struct inode *inode = req->wb_context->dentry->d_inode; struct inode *inode = req->wb_context->path.dentry->d_inode;
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
BUG_ON (!NFS_WBACK_BUSY(req)); BUG_ON (!NFS_WBACK_BUSY(req));
spin_lock(&nfsi->req_lock); spin_lock(&inode->i_lock);
set_page_private(req->wb_page, 0); set_page_private(req->wb_page, 0);
ClearPagePrivate(req->wb_page); ClearPagePrivate(req->wb_page);
radix_tree_delete(&nfsi->nfs_page_tree, req->wb_index); radix_tree_delete(&nfsi->nfs_page_tree, req->wb_index);
@ -422,11 +420,11 @@ static void nfs_inode_remove_request(struct nfs_page *req)
__set_page_dirty_nobuffers(req->wb_page); __set_page_dirty_nobuffers(req->wb_page);
nfsi->npages--; nfsi->npages--;
if (!nfsi->npages) { if (!nfsi->npages) {
spin_unlock(&nfsi->req_lock); spin_unlock(&inode->i_lock);
nfs_end_data_update(inode); nfs_end_data_update(inode);
iput(inode); iput(inode);
} else } else
spin_unlock(&nfsi->req_lock); spin_unlock(&inode->i_lock);
nfs_clear_request(req); nfs_clear_request(req);
nfs_release_request(req); nfs_release_request(req);
} }
@ -457,14 +455,16 @@ nfs_dirty_request(struct nfs_page *req)
static void static void
nfs_mark_request_commit(struct nfs_page *req) nfs_mark_request_commit(struct nfs_page *req)
{ {
struct inode *inode = req->wb_context->dentry->d_inode; struct inode *inode = req->wb_context->path.dentry->d_inode;
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
spin_lock(&nfsi->req_lock); spin_lock(&inode->i_lock);
nfs_list_add_request(req, &nfsi->commit);
nfsi->ncommit++; nfsi->ncommit++;
set_bit(PG_NEED_COMMIT, &(req)->wb_flags); set_bit(PG_NEED_COMMIT, &(req)->wb_flags);
spin_unlock(&nfsi->req_lock); radix_tree_tag_set(&nfsi->nfs_page_tree,
req->wb_index,
NFS_PAGE_TAG_COMMIT);
spin_unlock(&inode->i_lock);
inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
__mark_inode_dirty(inode, I_DIRTY_DATASYNC); __mark_inode_dirty(inode, I_DIRTY_DATASYNC);
} }
@ -526,18 +526,18 @@ static int nfs_wait_on_requests_locked(struct inode *inode, pgoff_t idx_start, u
idx_end = idx_start + npages - 1; idx_end = idx_start + npages - 1;
next = idx_start; next = idx_start;
while (radix_tree_gang_lookup_tag(&nfsi->nfs_page_tree, (void **)&req, next, 1, NFS_PAGE_TAG_WRITEBACK)) { while (radix_tree_gang_lookup_tag(&nfsi->nfs_page_tree, (void **)&req, next, 1, NFS_PAGE_TAG_LOCKED)) {
if (req->wb_index > idx_end) if (req->wb_index > idx_end)
break; break;
next = req->wb_index + 1; next = req->wb_index + 1;
BUG_ON(!NFS_WBACK_BUSY(req)); BUG_ON(!NFS_WBACK_BUSY(req));
atomic_inc(&req->wb_count); kref_get(&req->wb_kref);
spin_unlock(&nfsi->req_lock); spin_unlock(&inode->i_lock);
error = nfs_wait_on_request(req); error = nfs_wait_on_request(req);
nfs_release_request(req); nfs_release_request(req);
spin_lock(&nfsi->req_lock); spin_lock(&inode->i_lock);
if (error < 0) if (error < 0)
return error; return error;
res++; res++;
@ -577,10 +577,9 @@ nfs_scan_commit(struct inode *inode, struct list_head *dst, pgoff_t idx_start, u
int res = 0; int res = 0;
if (nfsi->ncommit != 0) { if (nfsi->ncommit != 0) {
res = nfs_scan_list(nfsi, &nfsi->commit, dst, idx_start, npages); res = nfs_scan_list(nfsi, dst, idx_start, npages,
NFS_PAGE_TAG_COMMIT);
nfsi->ncommit -= res; nfsi->ncommit -= res;
if ((nfsi->ncommit == 0) != list_empty(&nfsi->commit))
printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n");
} }
return res; return res;
} }
@ -603,7 +602,6 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx,
{ {
struct address_space *mapping = page->mapping; struct address_space *mapping = page->mapping;
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_page *req, *new = NULL; struct nfs_page *req, *new = NULL;
pgoff_t rqend, end; pgoff_t rqend, end;
@ -613,13 +611,13 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx,
/* Loop over all inode entries and see if we find /* Loop over all inode entries and see if we find
* A request for the page we wish to update * A request for the page we wish to update
*/ */
spin_lock(&nfsi->req_lock); spin_lock(&inode->i_lock);
req = nfs_page_find_request_locked(page); req = nfs_page_find_request_locked(page);
if (req) { if (req) {
if (!nfs_lock_request_dontget(req)) { if (!nfs_lock_request_dontget(req)) {
int error; int error;
spin_unlock(&nfsi->req_lock); spin_unlock(&inode->i_lock);
error = nfs_wait_on_request(req); error = nfs_wait_on_request(req);
nfs_release_request(req); nfs_release_request(req);
if (error < 0) { if (error < 0) {
@ -629,7 +627,7 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx,
} }
continue; continue;
} }
spin_unlock(&nfsi->req_lock); spin_unlock(&inode->i_lock);
if (new) if (new)
nfs_release_request(new); nfs_release_request(new);
break; break;
@ -640,14 +638,14 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx,
nfs_lock_request_dontget(new); nfs_lock_request_dontget(new);
error = nfs_inode_add_request(inode, new); error = nfs_inode_add_request(inode, new);
if (error) { if (error) {
spin_unlock(&nfsi->req_lock); spin_unlock(&inode->i_lock);
nfs_unlock_request(new); nfs_unlock_request(new);
return ERR_PTR(error); return ERR_PTR(error);
} }
spin_unlock(&nfsi->req_lock); spin_unlock(&inode->i_lock);
return new; return new;
} }
spin_unlock(&nfsi->req_lock); spin_unlock(&inode->i_lock);
new = nfs_create_request(ctx, inode, page, offset, bytes); new = nfs_create_request(ctx, inode, page, offset, bytes);
if (IS_ERR(new)) if (IS_ERR(new))
@ -751,12 +749,17 @@ int nfs_updatepage(struct file *file, struct page *page,
static void nfs_writepage_release(struct nfs_page *req) static void nfs_writepage_release(struct nfs_page *req)
{ {
if (PageError(req->wb_page) || !nfs_reschedule_unstable_write(req)) { if (PageError(req->wb_page)) {
nfs_end_page_writeback(req->wb_page);
nfs_inode_remove_request(req);
} else if (!nfs_reschedule_unstable_write(req)) {
/* Set the PG_uptodate flag */
nfs_mark_uptodate(req->wb_page, req->wb_pgbase, req->wb_bytes);
nfs_end_page_writeback(req->wb_page); nfs_end_page_writeback(req->wb_page);
nfs_inode_remove_request(req); nfs_inode_remove_request(req);
} else } else
nfs_end_page_writeback(req->wb_page); nfs_end_page_writeback(req->wb_page);
nfs_clear_page_writeback(req); nfs_clear_page_tag_locked(req);
} }
static inline int flush_task_priority(int how) static inline int flush_task_priority(int how)
@ -786,7 +789,7 @@ static void nfs_write_rpcsetup(struct nfs_page *req,
* NB: take care not to mess about with data->commit et al. */ * NB: take care not to mess about with data->commit et al. */
data->req = req; data->req = req;
data->inode = inode = req->wb_context->dentry->d_inode; data->inode = inode = req->wb_context->path.dentry->d_inode;
data->cred = req->wb_context->cred; data->cred = req->wb_context->cred;
data->args.fh = NFS_FH(inode); data->args.fh = NFS_FH(inode);
@ -885,7 +888,7 @@ out_bad:
} }
nfs_redirty_request(req); nfs_redirty_request(req);
nfs_end_page_writeback(req->wb_page); nfs_end_page_writeback(req->wb_page);
nfs_clear_page_writeback(req); nfs_clear_page_tag_locked(req);
return -ENOMEM; return -ENOMEM;
} }
@ -928,7 +931,7 @@ static int nfs_flush_one(struct inode *inode, struct list_head *head, unsigned i
nfs_list_remove_request(req); nfs_list_remove_request(req);
nfs_redirty_request(req); nfs_redirty_request(req);
nfs_end_page_writeback(req->wb_page); nfs_end_page_writeback(req->wb_page);
nfs_clear_page_writeback(req); nfs_clear_page_tag_locked(req);
} }
return -ENOMEM; return -ENOMEM;
} }
@ -954,8 +957,8 @@ static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata)
struct page *page = req->wb_page; struct page *page = req->wb_page;
dprintk("NFS: write (%s/%Ld %d@%Ld)", dprintk("NFS: write (%s/%Ld %d@%Ld)",
req->wb_context->dentry->d_inode->i_sb->s_id, req->wb_context->path.dentry->d_inode->i_sb->s_id,
(long long)NFS_FILEID(req->wb_context->dentry->d_inode), (long long)NFS_FILEID(req->wb_context->path.dentry->d_inode),
req->wb_bytes, req->wb_bytes,
(long long)req_offset(req)); (long long)req_offset(req));
@ -970,9 +973,9 @@ static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata)
} }
if (nfs_write_need_commit(data)) { if (nfs_write_need_commit(data)) {
spinlock_t *req_lock = &NFS_I(page->mapping->host)->req_lock; struct inode *inode = page->mapping->host;
spin_lock(req_lock); spin_lock(&inode->i_lock);
if (test_bit(PG_NEED_RESCHED, &req->wb_flags)) { if (test_bit(PG_NEED_RESCHED, &req->wb_flags)) {
/* Do nothing we need to resend the writes */ /* Do nothing we need to resend the writes */
} else if (!test_and_set_bit(PG_NEED_COMMIT, &req->wb_flags)) { } else if (!test_and_set_bit(PG_NEED_COMMIT, &req->wb_flags)) {
@ -983,7 +986,7 @@ static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata)
clear_bit(PG_NEED_COMMIT, &req->wb_flags); clear_bit(PG_NEED_COMMIT, &req->wb_flags);
dprintk(" server reboot detected\n"); dprintk(" server reboot detected\n");
} }
spin_unlock(req_lock); spin_unlock(&inode->i_lock);
} else } else
dprintk(" OK\n"); dprintk(" OK\n");
@ -1020,8 +1023,8 @@ static void nfs_writeback_done_full(struct rpc_task *task, void *calldata)
page = req->wb_page; page = req->wb_page;
dprintk("NFS: write (%s/%Ld %d@%Ld)", dprintk("NFS: write (%s/%Ld %d@%Ld)",
req->wb_context->dentry->d_inode->i_sb->s_id, req->wb_context->path.dentry->d_inode->i_sb->s_id,
(long long)NFS_FILEID(req->wb_context->dentry->d_inode), (long long)NFS_FILEID(req->wb_context->path.dentry->d_inode),
req->wb_bytes, req->wb_bytes,
(long long)req_offset(req)); (long long)req_offset(req));
@ -1039,12 +1042,14 @@ static void nfs_writeback_done_full(struct rpc_task *task, void *calldata)
dprintk(" marked for commit\n"); dprintk(" marked for commit\n");
goto next; goto next;
} }
/* Set the PG_uptodate flag? */
nfs_mark_uptodate(page, req->wb_pgbase, req->wb_bytes);
dprintk(" OK\n"); dprintk(" OK\n");
remove_request: remove_request:
nfs_end_page_writeback(page); nfs_end_page_writeback(page);
nfs_inode_remove_request(req); nfs_inode_remove_request(req);
next: next:
nfs_clear_page_writeback(req); nfs_clear_page_tag_locked(req);
} }
} }
@ -1157,7 +1162,7 @@ static void nfs_commit_rpcsetup(struct list_head *head,
list_splice_init(head, &data->pages); list_splice_init(head, &data->pages);
first = nfs_list_entry(data->pages.next); first = nfs_list_entry(data->pages.next);
inode = first->wb_context->dentry->d_inode; inode = first->wb_context->path.dentry->d_inode;
data->inode = inode; data->inode = inode;
data->cred = first->wb_context->cred; data->cred = first->wb_context->cred;
@ -1207,7 +1212,7 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how)
nfs_list_remove_request(req); nfs_list_remove_request(req);
nfs_mark_request_commit(req); nfs_mark_request_commit(req);
dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
nfs_clear_page_writeback(req); nfs_clear_page_tag_locked(req);
} }
return -ENOMEM; return -ENOMEM;
} }
@ -1234,8 +1239,8 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata)
dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
dprintk("NFS: commit (%s/%Ld %d@%Ld)", dprintk("NFS: commit (%s/%Ld %d@%Ld)",
req->wb_context->dentry->d_inode->i_sb->s_id, req->wb_context->path.dentry->d_inode->i_sb->s_id,
(long long)NFS_FILEID(req->wb_context->dentry->d_inode), (long long)NFS_FILEID(req->wb_context->path.dentry->d_inode),
req->wb_bytes, req->wb_bytes,
(long long)req_offset(req)); (long long)req_offset(req));
if (task->tk_status < 0) { if (task->tk_status < 0) {
@ -1249,6 +1254,9 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata)
* returned by the server against all stored verfs. */ * returned by the server against all stored verfs. */
if (!memcmp(req->wb_verf.verifier, data->verf.verifier, sizeof(data->verf.verifier))) { if (!memcmp(req->wb_verf.verifier, data->verf.verifier, sizeof(data->verf.verifier))) {
/* We have a match */ /* We have a match */
/* Set the PG_uptodate flag */
nfs_mark_uptodate(req->wb_page, req->wb_pgbase,
req->wb_bytes);
nfs_inode_remove_request(req); nfs_inode_remove_request(req);
dprintk(" OK\n"); dprintk(" OK\n");
goto next; goto next;
@ -1257,7 +1265,7 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata)
dprintk(" mismatch\n"); dprintk(" mismatch\n");
nfs_redirty_request(req); nfs_redirty_request(req);
next: next:
nfs_clear_page_writeback(req); nfs_clear_page_tag_locked(req);
} }
} }
@ -1268,13 +1276,12 @@ static const struct rpc_call_ops nfs_commit_ops = {
int nfs_commit_inode(struct inode *inode, int how) int nfs_commit_inode(struct inode *inode, int how)
{ {
struct nfs_inode *nfsi = NFS_I(inode);
LIST_HEAD(head); LIST_HEAD(head);
int res; int res;
spin_lock(&nfsi->req_lock); spin_lock(&inode->i_lock);
res = nfs_scan_commit(inode, &head, 0, 0); res = nfs_scan_commit(inode, &head, 0, 0);
spin_unlock(&nfsi->req_lock); spin_unlock(&inode->i_lock);
if (res) { if (res) {
int error = nfs_commit_list(inode, &head, how); int error = nfs_commit_list(inode, &head, how);
if (error < 0) if (error < 0)
@ -1292,7 +1299,6 @@ static inline int nfs_commit_list(struct inode *inode, struct list_head *head, i
long nfs_sync_mapping_wait(struct address_space *mapping, struct writeback_control *wbc, int how) long nfs_sync_mapping_wait(struct address_space *mapping, struct writeback_control *wbc, int how)
{ {
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
struct nfs_inode *nfsi = NFS_I(inode);
pgoff_t idx_start, idx_end; pgoff_t idx_start, idx_end;
unsigned int npages = 0; unsigned int npages = 0;
LIST_HEAD(head); LIST_HEAD(head);
@ -1314,7 +1320,7 @@ long nfs_sync_mapping_wait(struct address_space *mapping, struct writeback_contr
} }
} }
how &= ~FLUSH_NOCOMMIT; how &= ~FLUSH_NOCOMMIT;
spin_lock(&nfsi->req_lock); spin_lock(&inode->i_lock);
do { do {
ret = nfs_wait_on_requests_locked(inode, idx_start, npages); ret = nfs_wait_on_requests_locked(inode, idx_start, npages);
if (ret != 0) if (ret != 0)
@ -1325,18 +1331,19 @@ long nfs_sync_mapping_wait(struct address_space *mapping, struct writeback_contr
if (pages == 0) if (pages == 0)
break; break;
if (how & FLUSH_INVALIDATE) { if (how & FLUSH_INVALIDATE) {
spin_unlock(&nfsi->req_lock); spin_unlock(&inode->i_lock);
nfs_cancel_commit_list(&head); nfs_cancel_commit_list(&head);
ret = pages; ret = pages;
spin_lock(&nfsi->req_lock); spin_lock(&inode->i_lock);
continue; continue;
} }
pages += nfs_scan_commit(inode, &head, 0, 0); pages += nfs_scan_commit(inode, &head, 0, 0);
spin_unlock(&nfsi->req_lock); spin_unlock(&inode->i_lock);
ret = nfs_commit_list(inode, &head, how); ret = nfs_commit_list(inode, &head, how);
spin_lock(&nfsi->req_lock); spin_lock(&inode->i_lock);
} while (ret >= 0); } while (ret >= 0);
spin_unlock(&nfsi->req_lock); spin_unlock(&inode->i_lock);
return ret; return ret;
} }
@ -1430,7 +1437,6 @@ int nfs_set_page_dirty(struct page *page)
{ {
struct address_space *mapping = page->mapping; struct address_space *mapping = page->mapping;
struct inode *inode; struct inode *inode;
spinlock_t *req_lock;
struct nfs_page *req; struct nfs_page *req;
int ret; int ret;
@ -1439,18 +1445,17 @@ int nfs_set_page_dirty(struct page *page)
inode = mapping->host; inode = mapping->host;
if (!inode) if (!inode)
goto out_raced; goto out_raced;
req_lock = &NFS_I(inode)->req_lock; spin_lock(&inode->i_lock);
spin_lock(req_lock);
req = nfs_page_find_request_locked(page); req = nfs_page_find_request_locked(page);
if (req != NULL) { if (req != NULL) {
/* Mark any existing write requests for flushing */ /* Mark any existing write requests for flushing */
ret = !test_and_set_bit(PG_NEED_FLUSH, &req->wb_flags); ret = !test_and_set_bit(PG_NEED_FLUSH, &req->wb_flags);
spin_unlock(req_lock); spin_unlock(&inode->i_lock);
nfs_release_request(req); nfs_release_request(req);
return ret; return ret;
} }
ret = __set_page_dirty_nobuffers(page); ret = __set_page_dirty_nobuffers(page);
spin_unlock(req_lock); spin_unlock(&inode->i_lock);
return ret; return ret;
out_raced: out_raced:
return !TestSetPageDirty(page); return !TestSetPageDirty(page);

View File

@ -394,7 +394,6 @@ nfsd4_probe_callback(struct nfs4_client *clp)
.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL], .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL],
.rpc_argp = clp, .rpc_argp = clp,
}; };
char clientname[16];
int status; int status;
if (atomic_read(&cb->cb_set)) if (atomic_read(&cb->cb_set))
@ -417,11 +416,6 @@ nfsd4_probe_callback(struct nfs4_client *clp)
memset(program->stats, 0, sizeof(cb->cb_stat)); memset(program->stats, 0, sizeof(cb->cb_stat));
program->stats->program = program; program->stats->program = program;
/* Just here to make some printk's more useful: */
snprintf(clientname, sizeof(clientname),
"%u.%u.%u.%u", NIPQUAD(addr.sin_addr));
args.servername = clientname;
/* Create RPC client */ /* Create RPC client */
cb->cb_client = rpc_create(&args); cb->cb_client = rpc_create(&args);
if (IS_ERR(cb->cb_client)) { if (IS_ERR(cb->cb_client)) {
@ -429,29 +423,23 @@ nfsd4_probe_callback(struct nfs4_client *clp)
goto out_err; goto out_err;
} }
/* Kick rpciod, put the call on the wire. */
if (rpciod_up() != 0)
goto out_clnt;
/* the task holds a reference to the nfs4_client struct */ /* the task holds a reference to the nfs4_client struct */
atomic_inc(&clp->cl_count); atomic_inc(&clp->cl_count);
msg.rpc_cred = nfsd4_lookupcred(clp,0); msg.rpc_cred = nfsd4_lookupcred(clp,0);
if (IS_ERR(msg.rpc_cred)) if (IS_ERR(msg.rpc_cred))
goto out_rpciod; goto out_release_clp;
status = rpc_call_async(cb->cb_client, &msg, RPC_TASK_ASYNC, &nfs4_cb_null_ops, NULL); status = rpc_call_async(cb->cb_client, &msg, RPC_TASK_ASYNC, &nfs4_cb_null_ops, NULL);
put_rpccred(msg.rpc_cred); put_rpccred(msg.rpc_cred);
if (status != 0) { if (status != 0) {
dprintk("NFSD: asynchronous NFSPROC4_CB_NULL failed!\n"); dprintk("NFSD: asynchronous NFSPROC4_CB_NULL failed!\n");
goto out_rpciod; goto out_release_clp;
} }
return; return;
out_rpciod: out_release_clp:
atomic_dec(&clp->cl_count); atomic_dec(&clp->cl_count);
rpciod_down();
out_clnt:
rpc_shutdown_client(cb->cb_client); rpc_shutdown_client(cb->cb_client);
out_err: out_err:
cb->cb_client = NULL; cb->cb_client = NULL;

View File

@ -378,7 +378,6 @@ shutdown_callback_client(struct nfs4_client *clp)
if (clnt) { if (clnt) {
clp->cl_callback.cb_client = NULL; clp->cl_callback.cb_client = NULL;
rpc_shutdown_client(clnt); rpc_shutdown_client(clnt);
rpciod_down();
} }
} }

View File

@ -39,6 +39,7 @@
struct nlm_host { struct nlm_host {
struct hlist_node h_hash; /* doubly linked list */ struct hlist_node h_hash; /* doubly linked list */
struct sockaddr_in h_addr; /* peer address */ struct sockaddr_in h_addr; /* peer address */
struct sockaddr_in h_saddr; /* our address (optional) */
struct rpc_clnt * h_rpcclnt; /* RPC client to talk to peer */ struct rpc_clnt * h_rpcclnt; /* RPC client to talk to peer */
char * h_name; /* remote hostname */ char * h_name; /* remote hostname */
u32 h_version; /* interface version */ u32 h_version; /* interface version */

View File

@ -15,6 +15,7 @@
#include <linux/types.h> #include <linux/types.h>
#define NFS4_BITMAP_SIZE 2
#define NFS4_VERIFIER_SIZE 8 #define NFS4_VERIFIER_SIZE 8
#define NFS4_STATEID_SIZE 16 #define NFS4_STATEID_SIZE 16
#define NFS4_FHSIZE 128 #define NFS4_FHSIZE 128

View File

@ -65,6 +65,7 @@ struct nfs4_mount_data {
#define NFS4_MOUNT_NOCTO 0x0010 /* 1 */ #define NFS4_MOUNT_NOCTO 0x0010 /* 1 */
#define NFS4_MOUNT_NOAC 0x0020 /* 1 */ #define NFS4_MOUNT_NOAC 0x0020 /* 1 */
#define NFS4_MOUNT_STRICTLOCK 0x1000 /* 1 */ #define NFS4_MOUNT_STRICTLOCK 0x1000 /* 1 */
#define NFS4_MOUNT_FLAGMASK 0xFFFF #define NFS4_MOUNT_UNSHARED 0x8000 /* 1 */
#define NFS4_MOUNT_FLAGMASK 0x9033
#endif #endif

View File

@ -30,7 +30,9 @@
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/in.h> #include <linux/in.h>
#include <linux/kref.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/namei.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/rbtree.h> #include <linux/rbtree.h>
#include <linux/rwsem.h> #include <linux/rwsem.h>
@ -69,9 +71,8 @@ struct nfs_access_entry {
struct nfs4_state; struct nfs4_state;
struct nfs_open_context { struct nfs_open_context {
atomic_t count; struct kref kref;
struct vfsmount *vfsmnt; struct path path;
struct dentry *dentry;
struct rpc_cred *cred; struct rpc_cred *cred;
struct nfs4_state *state; struct nfs4_state *state;
fl_owner_t lockowner; fl_owner_t lockowner;
@ -155,13 +156,9 @@ struct nfs_inode {
/* /*
* This is the list of dirty unwritten pages. * This is the list of dirty unwritten pages.
*/ */
spinlock_t req_lock;
struct list_head dirty;
struct list_head commit;
struct radix_tree_root nfs_page_tree; struct radix_tree_root nfs_page_tree;
unsigned int ndirty, unsigned long ncommit,
ncommit,
npages; npages;
/* Open contexts for shared mmap writes */ /* Open contexts for shared mmap writes */
@ -187,6 +184,7 @@ struct nfs_inode {
#define NFS_INO_INVALID_ACCESS 0x0008 /* cached access cred invalid */ #define NFS_INO_INVALID_ACCESS 0x0008 /* cached access cred invalid */
#define NFS_INO_INVALID_ACL 0x0010 /* cached acls are invalid */ #define NFS_INO_INVALID_ACL 0x0010 /* cached acls are invalid */
#define NFS_INO_REVAL_PAGECACHE 0x0020 /* must revalidate pagecache */ #define NFS_INO_REVAL_PAGECACHE 0x0020 /* must revalidate pagecache */
#define NFS_INO_REVAL_FORCED 0x0040 /* force revalidation ignoring a delegation */
/* /*
* Bit offsets in flags field * Bit offsets in flags field
@ -496,21 +494,18 @@ static inline void nfs3_forget_cached_acls(struct inode *inode)
/* /*
* linux/fs/mount_clnt.c * linux/fs/mount_clnt.c
* (Used only by nfsroot module)
*/ */
extern int nfsroot_mount(struct sockaddr_in *, char *, struct nfs_fh *, extern int nfs_mount(struct sockaddr *, size_t, char *, char *,
int, int); int, int, struct nfs_fh *);
/* /*
* inline functions * inline functions
*/ */
static inline loff_t static inline loff_t nfs_size_to_loff_t(__u64 size)
nfs_size_to_loff_t(__u64 size)
{ {
loff_t maxsz = (((loff_t) ULONG_MAX) << PAGE_CACHE_SHIFT) + PAGE_CACHE_SIZE - 1; if (size > (__u64) OFFSET_MAX - 1)
if (size > maxsz) return OFFSET_MAX - 1;
return maxsz;
return (loff_t) size; return (loff_t) size;
} }
@ -557,6 +552,7 @@ extern void * nfs_root_data(void);
#define NFSDBG_ROOT 0x0080 #define NFSDBG_ROOT 0x0080
#define NFSDBG_CALLBACK 0x0100 #define NFSDBG_CALLBACK 0x0100
#define NFSDBG_CLIENT 0x0200 #define NFSDBG_CLIENT 0x0200
#define NFSDBG_MOUNT 0x0400
#define NFSDBG_ALL 0xFFFF #define NFSDBG_ALL 0xFFFF
#ifdef __KERNEL__ #ifdef __KERNEL__

View File

@ -16,7 +16,6 @@ struct nfs_client {
#define NFS_CS_INITING 1 /* busy initialising */ #define NFS_CS_INITING 1 /* busy initialising */
int cl_nfsversion; /* NFS protocol version */ int cl_nfsversion; /* NFS protocol version */
unsigned long cl_res_state; /* NFS resources state */ unsigned long cl_res_state; /* NFS resources state */
#define NFS_CS_RPCIOD 0 /* - rpciod started */
#define NFS_CS_CALLBACK 1 /* - callback started */ #define NFS_CS_CALLBACK 1 /* - callback started */
#define NFS_CS_IDMAP 2 /* - idmap started */ #define NFS_CS_IDMAP 2 /* - idmap started */
#define NFS_CS_RENEWD 3 /* - renewd started */ #define NFS_CS_RENEWD 3 /* - renewd started */
@ -35,7 +34,8 @@ struct nfs_client {
nfs4_verifier cl_confirm; nfs4_verifier cl_confirm;
unsigned long cl_state; unsigned long cl_state;
u32 cl_lockowner_id; struct rb_root cl_openowner_id;
struct rb_root cl_lockowner_id;
/* /*
* The following rwsem ensures exclusive access to the server * The following rwsem ensures exclusive access to the server
@ -44,9 +44,7 @@ struct nfs_client {
struct rw_semaphore cl_sem; struct rw_semaphore cl_sem;
struct list_head cl_delegations; struct list_head cl_delegations;
struct list_head cl_state_owners; struct rb_root cl_state_owners;
struct list_head cl_unused;
int cl_nunused;
spinlock_t cl_lock; spinlock_t cl_lock;
unsigned long cl_lease_time; unsigned long cl_lease_time;

View File

@ -37,7 +37,7 @@ struct nfs_mount_data {
int acdirmin; /* 1 */ int acdirmin; /* 1 */
int acdirmax; /* 1 */ int acdirmax; /* 1 */
struct sockaddr_in addr; /* 1 */ struct sockaddr_in addr; /* 1 */
char hostname[256]; /* 1 */ char hostname[NFS_MAXNAMLEN + 1]; /* 1 */
int namlen; /* 2 */ int namlen; /* 2 */
unsigned int bsize; /* 3 */ unsigned int bsize; /* 3 */
struct nfs3_fh root; /* 4 */ struct nfs3_fh root; /* 4 */
@ -62,6 +62,7 @@ struct nfs_mount_data {
#define NFS_MOUNT_STRICTLOCK 0x1000 /* reserved for NFSv4 */ #define NFS_MOUNT_STRICTLOCK 0x1000 /* reserved for NFSv4 */
#define NFS_MOUNT_SECFLAVOUR 0x2000 /* 5 */ #define NFS_MOUNT_SECFLAVOUR 0x2000 /* 5 */
#define NFS_MOUNT_NORDIRPLUS 0x4000 /* 5 */ #define NFS_MOUNT_NORDIRPLUS 0x4000 /* 5 */
#define NFS_MOUNT_UNSHARED 0x8000 /* 5 */
#define NFS_MOUNT_FLAGMASK 0xFFFF #define NFS_MOUNT_FLAGMASK 0xFFFF
#endif #endif

View File

@ -16,12 +16,13 @@
#include <linux/sunrpc/auth.h> #include <linux/sunrpc/auth.h>
#include <linux/nfs_xdr.h> #include <linux/nfs_xdr.h>
#include <asm/atomic.h> #include <linux/kref.h>
/* /*
* Valid flags for the radix tree * Valid flags for the radix tree
*/ */
#define NFS_PAGE_TAG_WRITEBACK 0 #define NFS_PAGE_TAG_LOCKED 0
#define NFS_PAGE_TAG_COMMIT 1
/* /*
* Valid flags for a dirty buffer * Valid flags for a dirty buffer
@ -33,8 +34,7 @@
struct nfs_inode; struct nfs_inode;
struct nfs_page { struct nfs_page {
struct list_head wb_list, /* Defines state of page: */ struct list_head wb_list; /* Defines state of page: */
*wb_list_head; /* read/write/commit */
struct page *wb_page; /* page to read in/write out */ struct page *wb_page; /* page to read in/write out */
struct nfs_open_context *wb_context; /* File state context info */ struct nfs_open_context *wb_context; /* File state context info */
atomic_t wb_complete; /* i/os we're waiting for */ atomic_t wb_complete; /* i/os we're waiting for */
@ -42,7 +42,7 @@ struct nfs_page {
unsigned int wb_offset, /* Offset & ~PAGE_CACHE_MASK */ unsigned int wb_offset, /* Offset & ~PAGE_CACHE_MASK */
wb_pgbase, /* Start of page data */ wb_pgbase, /* Start of page data */
wb_bytes; /* Length of request */ wb_bytes; /* Length of request */
atomic_t wb_count; /* reference count */ struct kref wb_kref; /* reference count */
unsigned long wb_flags; unsigned long wb_flags;
struct nfs_writeverf wb_verf; /* Commit cookie */ struct nfs_writeverf wb_verf; /* Commit cookie */
}; };
@ -71,8 +71,8 @@ extern void nfs_clear_request(struct nfs_page *req);
extern void nfs_release_request(struct nfs_page *req); extern void nfs_release_request(struct nfs_page *req);
extern int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *head, struct list_head *dst, extern int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *dst,
pgoff_t idx_start, unsigned int npages); pgoff_t idx_start, unsigned int npages, int tag);
extern void nfs_pageio_init(struct nfs_pageio_descriptor *desc, extern void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
struct inode *inode, struct inode *inode,
int (*doio)(struct inode *, struct list_head *, unsigned int, size_t, int), int (*doio)(struct inode *, struct list_head *, unsigned int, size_t, int),
@ -84,12 +84,11 @@ extern void nfs_pageio_complete(struct nfs_pageio_descriptor *desc);
extern void nfs_pageio_cond_complete(struct nfs_pageio_descriptor *, pgoff_t); extern void nfs_pageio_cond_complete(struct nfs_pageio_descriptor *, pgoff_t);
extern int nfs_wait_on_request(struct nfs_page *); extern int nfs_wait_on_request(struct nfs_page *);
extern void nfs_unlock_request(struct nfs_page *req); extern void nfs_unlock_request(struct nfs_page *req);
extern int nfs_set_page_writeback_locked(struct nfs_page *req); extern void nfs_clear_page_tag_locked(struct nfs_page *req);
extern void nfs_clear_page_writeback(struct nfs_page *req);
/* /*
* Lock the page of an asynchronous request without incrementing the wb_count * Lock the page of an asynchronous request without getting a new reference
*/ */
static inline int static inline int
nfs_lock_request_dontget(struct nfs_page *req) nfs_lock_request_dontget(struct nfs_page *req)
@ -98,14 +97,14 @@ nfs_lock_request_dontget(struct nfs_page *req)
} }
/* /*
* Lock the page of an asynchronous request * Lock the page of an asynchronous request and take a reference
*/ */
static inline int static inline int
nfs_lock_request(struct nfs_page *req) nfs_lock_request(struct nfs_page *req)
{ {
if (test_and_set_bit(PG_BUSY, &req->wb_flags)) if (test_and_set_bit(PG_BUSY, &req->wb_flags))
return 0; return 0;
atomic_inc(&req->wb_count); kref_get(&req->wb_kref);
return 1; return 1;
} }
@ -118,7 +117,6 @@ static inline void
nfs_list_add_request(struct nfs_page *req, struct list_head *head) nfs_list_add_request(struct nfs_page *req, struct list_head *head)
{ {
list_add_tail(&req->wb_list, head); list_add_tail(&req->wb_list, head);
req->wb_list_head = head;
} }
@ -132,7 +130,6 @@ nfs_list_remove_request(struct nfs_page *req)
if (list_empty(&req->wb_list)) if (list_empty(&req->wb_list))
return; return;
list_del_init(&req->wb_list); list_del_init(&req->wb_list);
req->wb_list_head = NULL;
} }
static inline struct nfs_page * static inline struct nfs_page *

View File

@ -119,7 +119,7 @@ struct nfs_openargs {
struct nfs_seqid * seqid; struct nfs_seqid * seqid;
int open_flags; int open_flags;
__u64 clientid; __u64 clientid;
__u32 id; __u64 id;
union { union {
struct iattr * attrs; /* UNCHECKED, GUARDED */ struct iattr * attrs; /* UNCHECKED, GUARDED */
nfs4_verifier verifier; /* EXCLUSIVE */ nfs4_verifier verifier; /* EXCLUSIVE */
@ -144,6 +144,7 @@ struct nfs_openres {
nfs4_stateid delegation; nfs4_stateid delegation;
__u32 do_recall; __u32 do_recall;
__u64 maxsize; __u64 maxsize;
__u32 attrset[NFS4_BITMAP_SIZE];
}; };
/* /*
@ -180,7 +181,7 @@ struct nfs_closeres {
* */ * */
struct nfs_lowner { struct nfs_lowner {
__u64 clientid; __u64 clientid;
u32 id; __u64 id;
}; };
struct nfs_lock_args { struct nfs_lock_args {

View File

@ -16,6 +16,7 @@
#include <linux/sunrpc/xdr.h> #include <linux/sunrpc/xdr.h>
#include <asm/atomic.h> #include <asm/atomic.h>
#include <linux/rcupdate.h>
/* size of the nodename buffer */ /* size of the nodename buffer */
#define UNX_MAXNODENAME 32 #define UNX_MAXNODENAME 32
@ -30,22 +31,28 @@ struct auth_cred {
/* /*
* Client user credentials * Client user credentials
*/ */
struct rpc_auth;
struct rpc_credops;
struct rpc_cred { struct rpc_cred {
struct hlist_node cr_hash; /* hash chain */ struct hlist_node cr_hash; /* hash chain */
struct rpc_credops * cr_ops; struct list_head cr_lru; /* lru garbage collection */
unsigned long cr_expire; /* when to gc */ struct rcu_head cr_rcu;
atomic_t cr_count; /* ref count */ struct rpc_auth * cr_auth;
unsigned short cr_flags; /* various flags */ const struct rpc_credops *cr_ops;
#ifdef RPC_DEBUG #ifdef RPC_DEBUG
unsigned long cr_magic; /* 0x0f4aa4f0 */ unsigned long cr_magic; /* 0x0f4aa4f0 */
#endif #endif
unsigned long cr_expire; /* when to gc */
unsigned long cr_flags; /* various flags */
atomic_t cr_count; /* ref count */
uid_t cr_uid; uid_t cr_uid;
/* per-flavor data */ /* per-flavor data */
}; };
#define RPCAUTH_CRED_NEW 0x0001 #define RPCAUTH_CRED_NEW 0
#define RPCAUTH_CRED_UPTODATE 0x0002 #define RPCAUTH_CRED_UPTODATE 1
#define RPCAUTH_CRED_HASHED 2
#define RPCAUTH_CRED_MAGIC 0x0f4aa4f0 #define RPCAUTH_CRED_MAGIC 0x0f4aa4f0
@ -56,10 +63,10 @@ struct rpc_cred {
#define RPC_CREDCACHE_MASK (RPC_CREDCACHE_NR - 1) #define RPC_CREDCACHE_MASK (RPC_CREDCACHE_NR - 1)
struct rpc_cred_cache { struct rpc_cred_cache {
struct hlist_head hashtable[RPC_CREDCACHE_NR]; struct hlist_head hashtable[RPC_CREDCACHE_NR];
unsigned long nextgc; /* next garbage collection */ spinlock_t lock;
unsigned long expire; /* cache expiry interval */
}; };
struct rpc_authops;
struct rpc_auth { struct rpc_auth {
unsigned int au_cslack; /* call cred size estimate */ unsigned int au_cslack; /* call cred size estimate */
/* guess at number of u32's auth adds before /* guess at number of u32's auth adds before
@ -69,7 +76,7 @@ struct rpc_auth {
unsigned int au_verfsize; unsigned int au_verfsize;
unsigned int au_flags; /* various flags */ unsigned int au_flags; /* various flags */
struct rpc_authops * au_ops; /* operations */ const struct rpc_authops *au_ops; /* operations */
rpc_authflavor_t au_flavor; /* pseudoflavor (note may rpc_authflavor_t au_flavor; /* pseudoflavor (note may
* differ from the flavor in * differ from the flavor in
* au_ops->au_flavor in gss * au_ops->au_flavor in gss
@ -115,17 +122,19 @@ struct rpc_credops {
void *, __be32 *, void *); void *, __be32 *, void *);
}; };
extern struct rpc_authops authunix_ops; extern const struct rpc_authops authunix_ops;
extern struct rpc_authops authnull_ops; extern const struct rpc_authops authnull_ops;
#ifdef CONFIG_SUNRPC_SECURE
extern struct rpc_authops authdes_ops;
#endif
int rpcauth_register(struct rpc_authops *); void __init rpc_init_authunix(void);
int rpcauth_unregister(struct rpc_authops *); void __init rpcauth_init_module(void);
void __exit rpcauth_remove_module(void);
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 *); struct rpc_auth * rpcauth_create(rpc_authflavor_t, struct rpc_clnt *);
void rpcauth_destroy(struct rpc_auth *); void rpcauth_release(struct rpc_auth *);
struct rpc_cred * rpcauth_lookup_credcache(struct rpc_auth *, struct auth_cred *, 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 *);
struct rpc_cred * rpcauth_lookupcred(struct rpc_auth *, int); struct rpc_cred * rpcauth_lookupcred(struct rpc_auth *, int);
struct rpc_cred * rpcauth_bindcred(struct rpc_task *); struct rpc_cred * rpcauth_bindcred(struct rpc_task *);
void rpcauth_holdcred(struct rpc_task *); void rpcauth_holdcred(struct rpc_task *);
@ -138,8 +147,9 @@ int rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp,
int rpcauth_refreshcred(struct rpc_task *); int rpcauth_refreshcred(struct rpc_task *);
void rpcauth_invalcred(struct rpc_task *); void rpcauth_invalcred(struct rpc_task *);
int rpcauth_uptodatecred(struct rpc_task *); int rpcauth_uptodatecred(struct rpc_task *);
int rpcauth_init_credcache(struct rpc_auth *, unsigned long); int rpcauth_init_credcache(struct rpc_auth *);
void rpcauth_free_credcache(struct rpc_auth *); void rpcauth_destroy_credcache(struct rpc_auth *);
void rpcauth_clear_credcache(struct rpc_cred_cache *);
static inline static inline
struct rpc_cred * get_rpccred(struct rpc_cred *cred) struct rpc_cred * get_rpccred(struct rpc_cred *cred)

View File

@ -75,6 +75,7 @@ struct gss_cl_ctx {
struct xdr_netobj gc_wire_ctx; struct xdr_netobj gc_wire_ctx;
u32 gc_win; u32 gc_win;
unsigned long gc_expiry; unsigned long gc_expiry;
struct rcu_head gc_rcu;
}; };
struct gss_upcall_msg; struct gss_upcall_msg;
@ -85,11 +86,6 @@ struct gss_cred {
struct gss_upcall_msg *gc_upcall; struct gss_upcall_msg *gc_upcall;
}; };
#define gc_uid gc_base.cr_uid
#define gc_count gc_base.cr_count
#define gc_flags gc_base.cr_flags
#define gc_expire gc_base.cr_expire
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* _LINUX_SUNRPC_AUTH_GSS_H */ #endif /* _LINUX_SUNRPC_AUTH_GSS_H */

View File

@ -24,8 +24,10 @@ struct rpc_inode;
* The high-level client handle * The high-level client handle
*/ */
struct rpc_clnt { struct rpc_clnt {
atomic_t cl_count; /* Number of clones */ struct kref cl_kref; /* Number of references */
atomic_t cl_users; /* number of references */ struct list_head cl_clients; /* Global list of clients */
struct list_head cl_tasks; /* List of tasks */
spinlock_t cl_lock; /* spinlock */
struct rpc_xprt * cl_xprt; /* transport */ struct rpc_xprt * cl_xprt; /* transport */
struct rpc_procinfo * cl_procinfo; /* procedure info */ struct rpc_procinfo * cl_procinfo; /* procedure info */
u32 cl_prog, /* RPC program number */ u32 cl_prog, /* RPC program number */
@ -41,9 +43,7 @@ struct rpc_clnt {
unsigned int cl_softrtry : 1,/* soft timeouts */ unsigned int cl_softrtry : 1,/* soft timeouts */
cl_intr : 1,/* interruptible */ cl_intr : 1,/* interruptible */
cl_discrtry : 1,/* disconnect before retry */ cl_discrtry : 1,/* disconnect before retry */
cl_autobind : 1,/* use getport() */ cl_autobind : 1;/* use getport() */
cl_oneshot : 1,/* dispose after use */
cl_dead : 1;/* abandoned */
struct rpc_rtt * cl_rtt; /* RTO estimator data */ struct rpc_rtt * cl_rtt; /* RTO estimator data */
@ -98,6 +98,7 @@ struct rpc_create_args {
int protocol; int protocol;
struct sockaddr *address; struct sockaddr *address;
size_t addrsize; size_t addrsize;
struct sockaddr *saddress;
struct rpc_timeout *timeout; struct rpc_timeout *timeout;
char *servername; char *servername;
struct rpc_program *program; struct rpc_program *program;
@ -110,20 +111,20 @@ struct rpc_create_args {
#define RPC_CLNT_CREATE_HARDRTRY (1UL << 0) #define RPC_CLNT_CREATE_HARDRTRY (1UL << 0)
#define RPC_CLNT_CREATE_INTR (1UL << 1) #define RPC_CLNT_CREATE_INTR (1UL << 1)
#define RPC_CLNT_CREATE_AUTOBIND (1UL << 2) #define RPC_CLNT_CREATE_AUTOBIND (1UL << 2)
#define RPC_CLNT_CREATE_ONESHOT (1UL << 3) #define RPC_CLNT_CREATE_NONPRIVPORT (1UL << 3)
#define RPC_CLNT_CREATE_NONPRIVPORT (1UL << 4) #define RPC_CLNT_CREATE_NOPING (1UL << 4)
#define RPC_CLNT_CREATE_NOPING (1UL << 5) #define RPC_CLNT_CREATE_DISCRTRY (1UL << 5)
#define RPC_CLNT_CREATE_DISCRTRY (1UL << 6)
struct rpc_clnt *rpc_create(struct rpc_create_args *args); struct rpc_clnt *rpc_create(struct rpc_create_args *args);
struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *, struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *,
struct rpc_program *, int); struct rpc_program *, int);
struct rpc_clnt *rpc_clone_client(struct rpc_clnt *); struct rpc_clnt *rpc_clone_client(struct rpc_clnt *);
int rpc_shutdown_client(struct rpc_clnt *); void rpc_shutdown_client(struct rpc_clnt *);
int rpc_destroy_client(struct rpc_clnt *);
void rpc_release_client(struct rpc_clnt *); void rpc_release_client(struct rpc_clnt *);
int rpcb_register(u32, u32, int, unsigned short, int *); int rpcb_register(u32, u32, int, unsigned short, int *);
void rpcb_getport(struct rpc_task *); int rpcb_getport_sync(struct sockaddr_in *, __u32, __u32, int);
void rpcb_getport_async(struct rpc_task *);
void rpc_call_setup(struct rpc_task *, struct rpc_message *, int); void rpc_call_setup(struct rpc_task *, struct rpc_message *, int);
@ -132,20 +133,16 @@ int rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg,
void *calldata); void *calldata);
int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg,
int flags); int flags);
struct rpc_task *rpc_call_null(struct rpc_clnt *clnt, struct rpc_cred *cred,
int flags);
void rpc_restart_call(struct rpc_task *); void rpc_restart_call(struct rpc_task *);
void rpc_clnt_sigmask(struct rpc_clnt *clnt, sigset_t *oldset); void rpc_clnt_sigmask(struct rpc_clnt *clnt, sigset_t *oldset);
void rpc_clnt_sigunmask(struct rpc_clnt *clnt, sigset_t *oldset); void rpc_clnt_sigunmask(struct rpc_clnt *clnt, sigset_t *oldset);
void rpc_setbufsize(struct rpc_clnt *, unsigned int, unsigned int); void rpc_setbufsize(struct rpc_clnt *, unsigned int, unsigned int);
size_t rpc_max_payload(struct rpc_clnt *); size_t rpc_max_payload(struct rpc_clnt *);
void rpc_force_rebind(struct rpc_clnt *); void rpc_force_rebind(struct rpc_clnt *);
int rpc_ping(struct rpc_clnt *clnt, int flags);
size_t rpc_peeraddr(struct rpc_clnt *, struct sockaddr *, size_t); size_t rpc_peeraddr(struct rpc_clnt *, struct sockaddr *, size_t);
char * rpc_peeraddr2str(struct rpc_clnt *, enum rpc_display_format_t); char * rpc_peeraddr2str(struct rpc_clnt *, enum rpc_display_format_t);
/*
* Helper function for NFSroot support
*/
int rpcb_getport_external(struct sockaddr_in *, __u32, __u32, int);
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* _LINUX_SUNRPC_CLNT_H */ #endif /* _LINUX_SUNRPC_CLNT_H */

View File

@ -77,7 +77,7 @@ struct gss_api_mech {
struct module *gm_owner; struct module *gm_owner;
struct xdr_netobj gm_oid; struct xdr_netobj gm_oid;
char *gm_name; char *gm_name;
struct gss_api_ops *gm_ops; const struct gss_api_ops *gm_ops;
/* pseudoflavors supported by this mechanism: */ /* pseudoflavors supported by this mechanism: */
int gm_pf_num; int gm_pf_num;
struct pf_desc * gm_pfs; struct pf_desc * gm_pfs;

View File

@ -23,9 +23,11 @@ struct rpc_inode {
void *private; void *private;
struct list_head pipe; struct list_head pipe;
struct list_head in_upcall; struct list_head in_upcall;
struct list_head in_downcall;
int pipelen; int pipelen;
int nreaders; int nreaders;
int nwriters; int nwriters;
int nkern_readwriters;
wait_queue_head_t waitq; wait_queue_head_t waitq;
#define RPC_PIPE_WAIT_FOR_OPEN 1 #define RPC_PIPE_WAIT_FOR_OPEN 1
int flags; int flags;

View File

@ -98,7 +98,6 @@ struct rpc_task {
unsigned short tk_pid; /* debugging aid */ unsigned short tk_pid; /* debugging aid */
#endif #endif
}; };
#define tk_auth tk_client->cl_auth
#define tk_xprt tk_client->cl_xprt #define tk_xprt tk_client->cl_xprt
/* support walking a list of tasks on a wait queue */ /* support walking a list of tasks on a wait queue */
@ -110,11 +109,6 @@ struct rpc_task {
if (!list_empty(head) && \ if (!list_empty(head) && \
((task=list_entry((head)->next, struct rpc_task, u.tk_wait.list)),1)) ((task=list_entry((head)->next, struct rpc_task, u.tk_wait.list)),1))
/* .. and walking list of all tasks */
#define alltask_for_each(task, pos, head) \
list_for_each(pos, head) \
if ((task=list_entry(pos, struct rpc_task, tk_task)),1)
typedef void (*rpc_action)(struct rpc_task *); typedef void (*rpc_action)(struct rpc_task *);
struct rpc_call_ops { struct rpc_call_ops {

View File

@ -59,6 +59,7 @@ struct svc_sock {
/* cache of various info for TCP sockets */ /* cache of various info for TCP sockets */
void *sk_info_authunix; void *sk_info_authunix;
struct sockaddr_storage sk_local; /* local address */
struct sockaddr_storage sk_remote; /* remote peer's address */ struct sockaddr_storage sk_remote; /* remote peer's address */
int sk_remotelen; /* length of address */ int sk_remotelen; /* length of address */
}; };

View File

@ -17,6 +17,8 @@
#include <linux/sunrpc/xdr.h> #include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/msg_prot.h> #include <linux/sunrpc/msg_prot.h>
#ifdef __KERNEL__
extern unsigned int xprt_udp_slot_table_entries; extern unsigned int xprt_udp_slot_table_entries;
extern unsigned int xprt_tcp_slot_table_entries; extern unsigned int xprt_tcp_slot_table_entries;
@ -194,7 +196,13 @@ struct rpc_xprt {
char * address_strings[RPC_DISPLAY_MAX]; char * address_strings[RPC_DISPLAY_MAX];
}; };
#ifdef __KERNEL__ struct rpc_xprtsock_create {
int proto; /* IPPROTO_UDP or IPPROTO_TCP */
struct sockaddr * srcaddr; /* optional local address */
struct sockaddr * dstaddr; /* remote peer address */
size_t addrlen;
struct rpc_timeout * timeout; /* optional timeout parameters */
};
/* /*
* Transport operations used by ULPs * Transport operations used by ULPs
@ -204,7 +212,7 @@ void xprt_set_timeout(struct rpc_timeout *to, unsigned int retr, unsigned long
/* /*
* Generic internal transport functions * Generic internal transport functions
*/ */
struct rpc_xprt * xprt_create_transport(int proto, struct sockaddr *addr, size_t size, struct rpc_timeout *toparms); struct rpc_xprt * xprt_create_transport(struct rpc_xprtsock_create *args);
void xprt_connect(struct rpc_task *task); void xprt_connect(struct rpc_task *task);
void xprt_reserve(struct rpc_task *task); void xprt_reserve(struct rpc_task *task);
int xprt_reserve_xprt(struct rpc_task *task); int xprt_reserve_xprt(struct rpc_task *task);
@ -242,8 +250,8 @@ void xprt_disconnect(struct rpc_xprt *xprt);
/* /*
* Socket transport setup operations * Socket transport setup operations
*/ */
struct rpc_xprt * xs_setup_udp(struct sockaddr *addr, size_t addrlen, struct rpc_timeout *to); struct rpc_xprt * xs_setup_udp(struct rpc_xprtsock_create *args);
struct rpc_xprt * xs_setup_tcp(struct sockaddr *addr, size_t addrlen, struct rpc_timeout *to); struct rpc_xprt * xs_setup_tcp(struct rpc_xprtsock_create *args);
int init_socket_xprt(void); int init_socket_xprt(void);
void cleanup_socket_xprt(void); void cleanup_socket_xprt(void);

View File

@ -13,17 +13,22 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/sunrpc/clnt.h> #include <linux/sunrpc/clnt.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/smp_lock.h>
#ifdef RPC_DEBUG #ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH # define RPCDBG_FACILITY RPCDBG_AUTH
#endif #endif
static struct rpc_authops * auth_flavors[RPC_AUTH_MAXFLAVOR] = { static DEFINE_SPINLOCK(rpc_authflavor_lock);
static const struct rpc_authops *auth_flavors[RPC_AUTH_MAXFLAVOR] = {
&authnull_ops, /* AUTH_NULL */ &authnull_ops, /* AUTH_NULL */
&authunix_ops, /* AUTH_UNIX */ &authunix_ops, /* AUTH_UNIX */
NULL, /* others can be loadable modules */ NULL, /* others can be loadable modules */
}; };
static LIST_HEAD(cred_unused);
static unsigned long number_cred_unused;
static u32 static u32
pseudoflavor_to_flavor(u32 flavor) { pseudoflavor_to_flavor(u32 flavor) {
if (flavor >= RPC_AUTH_MAXFLAVOR) if (flavor >= RPC_AUTH_MAXFLAVOR)
@ -32,55 +37,67 @@ pseudoflavor_to_flavor(u32 flavor) {
} }
int int
rpcauth_register(struct rpc_authops *ops) rpcauth_register(const struct rpc_authops *ops)
{ {
rpc_authflavor_t flavor; rpc_authflavor_t flavor;
int ret = -EPERM;
if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR) if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)
return -EINVAL; return -EINVAL;
if (auth_flavors[flavor] != NULL) spin_lock(&rpc_authflavor_lock);
return -EPERM; /* what else? */ if (auth_flavors[flavor] == NULL) {
auth_flavors[flavor] = ops; auth_flavors[flavor] = ops;
return 0; ret = 0;
}
spin_unlock(&rpc_authflavor_lock);
return ret;
} }
int int
rpcauth_unregister(struct rpc_authops *ops) rpcauth_unregister(const struct rpc_authops *ops)
{ {
rpc_authflavor_t flavor; rpc_authflavor_t flavor;
int ret = -EPERM;
if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR) if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)
return -EINVAL; return -EINVAL;
if (auth_flavors[flavor] != ops) spin_lock(&rpc_authflavor_lock);
return -EPERM; /* what else? */ if (auth_flavors[flavor] == ops) {
auth_flavors[flavor] = NULL; auth_flavors[flavor] = NULL;
return 0; ret = 0;
}
spin_unlock(&rpc_authflavor_lock);
return ret;
} }
struct rpc_auth * struct rpc_auth *
rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt) rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt)
{ {
struct rpc_auth *auth; struct rpc_auth *auth;
struct rpc_authops *ops; const struct rpc_authops *ops;
u32 flavor = pseudoflavor_to_flavor(pseudoflavor); u32 flavor = pseudoflavor_to_flavor(pseudoflavor);
auth = ERR_PTR(-EINVAL); auth = ERR_PTR(-EINVAL);
if (flavor >= RPC_AUTH_MAXFLAVOR) if (flavor >= RPC_AUTH_MAXFLAVOR)
goto out; goto out;
/* FIXME - auth_flavors[] really needs an rw lock,
* and module refcounting. */
#ifdef CONFIG_KMOD #ifdef CONFIG_KMOD
if ((ops = auth_flavors[flavor]) == NULL) if ((ops = auth_flavors[flavor]) == NULL)
request_module("rpc-auth-%u", flavor); request_module("rpc-auth-%u", flavor);
#endif #endif
if ((ops = auth_flavors[flavor]) == NULL) spin_lock(&rpc_authflavor_lock);
ops = auth_flavors[flavor];
if (ops == NULL || !try_module_get(ops->owner)) {
spin_unlock(&rpc_authflavor_lock);
goto out; goto out;
}
spin_unlock(&rpc_authflavor_lock);
auth = ops->create(clnt, pseudoflavor); auth = ops->create(clnt, pseudoflavor);
module_put(ops->owner);
if (IS_ERR(auth)) if (IS_ERR(auth))
return auth; return auth;
if (clnt->cl_auth) if (clnt->cl_auth)
rpcauth_destroy(clnt->cl_auth); rpcauth_release(clnt->cl_auth);
clnt->cl_auth = auth; clnt->cl_auth = auth;
out: out:
@ -88,7 +105,7 @@ out:
} }
void void
rpcauth_destroy(struct rpc_auth *auth) rpcauth_release(struct rpc_auth *auth)
{ {
if (!atomic_dec_and_test(&auth->au_count)) if (!atomic_dec_and_test(&auth->au_count))
return; return;
@ -97,11 +114,31 @@ rpcauth_destroy(struct rpc_auth *auth)
static DEFINE_SPINLOCK(rpc_credcache_lock); static DEFINE_SPINLOCK(rpc_credcache_lock);
static void
rpcauth_unhash_cred_locked(struct rpc_cred *cred)
{
hlist_del_rcu(&cred->cr_hash);
smp_mb__before_clear_bit();
clear_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags);
}
static void
rpcauth_unhash_cred(struct rpc_cred *cred)
{
spinlock_t *cache_lock;
cache_lock = &cred->cr_auth->au_credcache->lock;
spin_lock(cache_lock);
if (atomic_read(&cred->cr_count) == 0)
rpcauth_unhash_cred_locked(cred);
spin_unlock(cache_lock);
}
/* /*
* Initialize RPC credential cache * Initialize RPC credential cache
*/ */
int int
rpcauth_init_credcache(struct rpc_auth *auth, unsigned long expire) rpcauth_init_credcache(struct rpc_auth *auth)
{ {
struct rpc_cred_cache *new; struct rpc_cred_cache *new;
int i; int i;
@ -111,8 +148,7 @@ rpcauth_init_credcache(struct rpc_auth *auth, unsigned long expire)
return -ENOMEM; return -ENOMEM;
for (i = 0; i < RPC_CREDCACHE_NR; i++) for (i = 0; i < RPC_CREDCACHE_NR; i++)
INIT_HLIST_HEAD(&new->hashtable[i]); INIT_HLIST_HEAD(&new->hashtable[i]);
new->expire = expire; spin_lock_init(&new->lock);
new->nextgc = jiffies + (expire >> 1);
auth->au_credcache = new; auth->au_credcache = new;
return 0; return 0;
} }
@ -121,13 +157,13 @@ rpcauth_init_credcache(struct rpc_auth *auth, unsigned long expire)
* Destroy a list of credentials * Destroy a list of credentials
*/ */
static inline static inline
void rpcauth_destroy_credlist(struct hlist_head *head) void rpcauth_destroy_credlist(struct list_head *head)
{ {
struct rpc_cred *cred; struct rpc_cred *cred;
while (!hlist_empty(head)) { while (!list_empty(head)) {
cred = hlist_entry(head->first, struct rpc_cred, cr_hash); cred = list_entry(head->next, struct rpc_cred, cr_lru);
hlist_del_init(&cred->cr_hash); list_del_init(&cred->cr_lru);
put_rpccred(cred); put_rpccred(cred);
} }
} }
@ -137,58 +173,95 @@ void rpcauth_destroy_credlist(struct hlist_head *head)
* that are not referenced. * that are not referenced.
*/ */
void void
rpcauth_free_credcache(struct rpc_auth *auth) rpcauth_clear_credcache(struct rpc_cred_cache *cache)
{ {
struct rpc_cred_cache *cache = auth->au_credcache; LIST_HEAD(free);
HLIST_HEAD(free); struct hlist_head *head;
struct hlist_node *pos, *next;
struct rpc_cred *cred; struct rpc_cred *cred;
int i; int i;
spin_lock(&rpc_credcache_lock); spin_lock(&rpc_credcache_lock);
spin_lock(&cache->lock);
for (i = 0; i < RPC_CREDCACHE_NR; i++) { for (i = 0; i < RPC_CREDCACHE_NR; i++) {
hlist_for_each_safe(pos, next, &cache->hashtable[i]) { head = &cache->hashtable[i];
cred = hlist_entry(pos, struct rpc_cred, cr_hash); while (!hlist_empty(head)) {
__hlist_del(&cred->cr_hash); cred = hlist_entry(head->first, struct rpc_cred, cr_hash);
hlist_add_head(&cred->cr_hash, &free); get_rpccred(cred);
if (!list_empty(&cred->cr_lru)) {
list_del(&cred->cr_lru);
number_cred_unused--;
}
list_add_tail(&cred->cr_lru, &free);
rpcauth_unhash_cred_locked(cred);
} }
} }
spin_unlock(&cache->lock);
spin_unlock(&rpc_credcache_lock); spin_unlock(&rpc_credcache_lock);
rpcauth_destroy_credlist(&free); rpcauth_destroy_credlist(&free);
} }
static void /*
rpcauth_prune_expired(struct rpc_auth *auth, struct rpc_cred *cred, struct hlist_head *free) * Destroy the RPC credential cache
*/
void
rpcauth_destroy_credcache(struct rpc_auth *auth)
{ {
if (atomic_read(&cred->cr_count) != 1) struct rpc_cred_cache *cache = auth->au_credcache;
return;
if (time_after(jiffies, cred->cr_expire + auth->au_credcache->expire)) if (cache) {
cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; auth->au_credcache = NULL;
if (!(cred->cr_flags & RPCAUTH_CRED_UPTODATE)) { rpcauth_clear_credcache(cache);
__hlist_del(&cred->cr_hash); kfree(cache);
hlist_add_head(&cred->cr_hash, free);
} }
} }
/* /*
* Remove stale credentials. Avoid sleeping inside the loop. * Remove stale credentials. Avoid sleeping inside the loop.
*/ */
static void static int
rpcauth_gc_credcache(struct rpc_auth *auth, struct hlist_head *free) rpcauth_prune_expired(struct list_head *free, int nr_to_scan)
{ {
struct rpc_cred_cache *cache = auth->au_credcache; spinlock_t *cache_lock;
struct hlist_node *pos, *next; struct rpc_cred *cred;
struct rpc_cred *cred;
int i;
dprintk("RPC: gc'ing RPC credentials for auth %p\n", auth); while (!list_empty(&cred_unused)) {
for (i = 0; i < RPC_CREDCACHE_NR; i++) { cred = list_entry(cred_unused.next, struct rpc_cred, cr_lru);
hlist_for_each_safe(pos, next, &cache->hashtable[i]) { list_del_init(&cred->cr_lru);
cred = hlist_entry(pos, struct rpc_cred, cr_hash); number_cred_unused--;
rpcauth_prune_expired(auth, cred, free); if (atomic_read(&cred->cr_count) != 0)
continue;
cache_lock = &cred->cr_auth->au_credcache->lock;
spin_lock(cache_lock);
if (atomic_read(&cred->cr_count) == 0) {
get_rpccred(cred);
list_add_tail(&cred->cr_lru, free);
rpcauth_unhash_cred_locked(cred);
nr_to_scan--;
} }
spin_unlock(cache_lock);
if (nr_to_scan == 0)
break;
} }
cache->nextgc = jiffies + cache->expire; return nr_to_scan;
}
/*
* Run memory cache shrinker.
*/
static int
rpcauth_cache_shrinker(int nr_to_scan, gfp_t gfp_mask)
{
LIST_HEAD(free);
int res;
if (list_empty(&cred_unused))
return 0;
spin_lock(&rpc_credcache_lock);
nr_to_scan = rpcauth_prune_expired(&free, nr_to_scan);
res = (number_cred_unused / 100) * sysctl_vfs_cache_pressure;
spin_unlock(&rpc_credcache_lock);
rpcauth_destroy_credlist(&free);
return res;
} }
/* /*
@ -198,53 +271,56 @@ struct rpc_cred *
rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred, rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred,
int flags) int flags)
{ {
LIST_HEAD(free);
struct rpc_cred_cache *cache = auth->au_credcache; struct rpc_cred_cache *cache = auth->au_credcache;
HLIST_HEAD(free); struct hlist_node *pos;
struct hlist_node *pos, *next; struct rpc_cred *cred = NULL,
struct rpc_cred *new = NULL, *entry, *new;
*cred = NULL;
int nr = 0; int nr = 0;
if (!(flags & RPCAUTH_LOOKUP_ROOTCREDS)) if (!(flags & RPCAUTH_LOOKUP_ROOTCREDS))
nr = acred->uid & RPC_CREDCACHE_MASK; nr = acred->uid & RPC_CREDCACHE_MASK;
retry:
spin_lock(&rpc_credcache_lock); rcu_read_lock();
if (time_before(cache->nextgc, jiffies)) hlist_for_each_entry_rcu(entry, pos, &cache->hashtable[nr], cr_hash) {
rpcauth_gc_credcache(auth, &free); if (!entry->cr_ops->crmatch(acred, entry, flags))
hlist_for_each_safe(pos, next, &cache->hashtable[nr]) { continue;
struct rpc_cred *entry; spin_lock(&cache->lock);
entry = hlist_entry(pos, struct rpc_cred, cr_hash); if (test_bit(RPCAUTH_CRED_HASHED, &entry->cr_flags) == 0) {
if (entry->cr_ops->crmatch(acred, entry, flags)) { spin_unlock(&cache->lock);
hlist_del(&entry->cr_hash); continue;
cred = entry;
break;
} }
rpcauth_prune_expired(auth, entry, &free); cred = get_rpccred(entry);
spin_unlock(&cache->lock);
break;
} }
if (new) { rcu_read_unlock();
if (cred)
hlist_add_head(&new->cr_hash, &free);
else
cred = new;
}
if (cred) {
hlist_add_head(&cred->cr_hash, &cache->hashtable[nr]);
get_rpccred(cred);
}
spin_unlock(&rpc_credcache_lock);
rpcauth_destroy_credlist(&free); if (cred != NULL)
goto found;
if (!cred) { new = auth->au_ops->crcreate(auth, acred, flags);
new = auth->au_ops->crcreate(auth, acred, flags); if (IS_ERR(new)) {
if (!IS_ERR(new)) { cred = new;
#ifdef RPC_DEBUG goto out;
new->cr_magic = RPCAUTH_CRED_MAGIC; }
#endif
goto retry; spin_lock(&cache->lock);
} else hlist_for_each_entry(entry, pos, &cache->hashtable[nr], cr_hash) {
cred = new; if (!entry->cr_ops->crmatch(acred, entry, flags))
} else if ((cred->cr_flags & RPCAUTH_CRED_NEW) continue;
cred = get_rpccred(entry);
break;
}
if (cred == NULL) {
cred = new;
set_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags);
hlist_add_head_rcu(&cred->cr_hash, &cache->hashtable[nr]);
} else
list_add_tail(&new->cr_lru, &free);
spin_unlock(&cache->lock);
found:
if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags)
&& cred->cr_ops->cr_init != NULL && cred->cr_ops->cr_init != NULL
&& !(flags & RPCAUTH_LOOKUP_NEW)) { && !(flags & RPCAUTH_LOOKUP_NEW)) {
int res = cred->cr_ops->cr_init(auth, cred); int res = cred->cr_ops->cr_init(auth, cred);
@ -253,8 +329,9 @@ retry:
cred = ERR_PTR(res); cred = ERR_PTR(res);
} }
} }
rpcauth_destroy_credlist(&free);
return (struct rpc_cred *) cred; out:
return cred;
} }
struct rpc_cred * struct rpc_cred *
@ -275,10 +352,27 @@ rpcauth_lookupcred(struct rpc_auth *auth, int flags)
return ret; return ret;
} }
void
rpcauth_init_cred(struct rpc_cred *cred, const struct auth_cred *acred,
struct rpc_auth *auth, const struct rpc_credops *ops)
{
INIT_HLIST_NODE(&cred->cr_hash);
INIT_LIST_HEAD(&cred->cr_lru);
atomic_set(&cred->cr_count, 1);
cred->cr_auth = auth;
cred->cr_ops = ops;
cred->cr_expire = jiffies;
#ifdef RPC_DEBUG
cred->cr_magic = RPCAUTH_CRED_MAGIC;
#endif
cred->cr_uid = acred->uid;
}
EXPORT_SYMBOL(rpcauth_init_cred);
struct rpc_cred * struct rpc_cred *
rpcauth_bindcred(struct rpc_task *task) rpcauth_bindcred(struct rpc_task *task)
{ {
struct rpc_auth *auth = task->tk_auth; struct rpc_auth *auth = task->tk_client->cl_auth;
struct auth_cred acred = { struct auth_cred acred = {
.uid = current->fsuid, .uid = current->fsuid,
.gid = current->fsgid, .gid = current->fsgid,
@ -288,7 +382,7 @@ rpcauth_bindcred(struct rpc_task *task)
int flags = 0; int flags = 0;
dprintk("RPC: %5u looking up %s cred\n", dprintk("RPC: %5u looking up %s cred\n",
task->tk_pid, task->tk_auth->au_ops->au_name); task->tk_pid, task->tk_client->cl_auth->au_ops->au_name);
get_group_info(acred.group_info); get_group_info(acred.group_info);
if (task->tk_flags & RPC_TASK_ROOTCREDS) if (task->tk_flags & RPC_TASK_ROOTCREDS)
flags |= RPCAUTH_LOOKUP_ROOTCREDS; flags |= RPCAUTH_LOOKUP_ROOTCREDS;
@ -304,19 +398,42 @@ rpcauth_bindcred(struct rpc_task *task)
void void
rpcauth_holdcred(struct rpc_task *task) rpcauth_holdcred(struct rpc_task *task)
{ {
dprintk("RPC: %5u holding %s cred %p\n", struct rpc_cred *cred = task->tk_msg.rpc_cred;
task->tk_pid, task->tk_auth->au_ops->au_name, if (cred != NULL) {
task->tk_msg.rpc_cred); get_rpccred(cred);
if (task->tk_msg.rpc_cred) dprintk("RPC: %5u holding %s cred %p\n", task->tk_pid,
get_rpccred(task->tk_msg.rpc_cred); cred->cr_auth->au_ops->au_name, cred);
}
} }
void void
put_rpccred(struct rpc_cred *cred) put_rpccred(struct rpc_cred *cred)
{ {
cred->cr_expire = jiffies; /* Fast path for unhashed credentials */
if (test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) != 0)
goto need_lock;
if (!atomic_dec_and_test(&cred->cr_count)) if (!atomic_dec_and_test(&cred->cr_count))
return; return;
goto out_destroy;
need_lock:
if (!atomic_dec_and_lock(&cred->cr_count, &rpc_credcache_lock))
return;
if (!list_empty(&cred->cr_lru)) {
number_cred_unused--;
list_del_init(&cred->cr_lru);
}
if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) == 0)
rpcauth_unhash_cred(cred);
else if (test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) != 0) {
cred->cr_expire = jiffies;
list_add_tail(&cred->cr_lru, &cred_unused);
number_cred_unused++;
spin_unlock(&rpc_credcache_lock);
return;
}
spin_unlock(&rpc_credcache_lock);
out_destroy:
cred->cr_ops->crdestroy(cred); cred->cr_ops->crdestroy(cred);
} }
@ -326,7 +443,7 @@ rpcauth_unbindcred(struct rpc_task *task)
struct rpc_cred *cred = task->tk_msg.rpc_cred; struct rpc_cred *cred = task->tk_msg.rpc_cred;
dprintk("RPC: %5u releasing %s cred %p\n", dprintk("RPC: %5u releasing %s cred %p\n",
task->tk_pid, task->tk_auth->au_ops->au_name, cred); task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
put_rpccred(cred); put_rpccred(cred);
task->tk_msg.rpc_cred = NULL; task->tk_msg.rpc_cred = NULL;
@ -338,7 +455,7 @@ rpcauth_marshcred(struct rpc_task *task, __be32 *p)
struct rpc_cred *cred = task->tk_msg.rpc_cred; struct rpc_cred *cred = task->tk_msg.rpc_cred;
dprintk("RPC: %5u marshaling %s cred %p\n", dprintk("RPC: %5u marshaling %s cred %p\n",
task->tk_pid, task->tk_auth->au_ops->au_name, cred); task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
return cred->cr_ops->crmarshal(task, p); return cred->cr_ops->crmarshal(task, p);
} }
@ -349,7 +466,7 @@ rpcauth_checkverf(struct rpc_task *task, __be32 *p)
struct rpc_cred *cred = task->tk_msg.rpc_cred; struct rpc_cred *cred = task->tk_msg.rpc_cred;
dprintk("RPC: %5u validating %s cred %p\n", dprintk("RPC: %5u validating %s cred %p\n",
task->tk_pid, task->tk_auth->au_ops->au_name, cred); task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
return cred->cr_ops->crvalidate(task, p); return cred->cr_ops->crvalidate(task, p);
} }
@ -359,13 +476,17 @@ rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp,
__be32 *data, void *obj) __be32 *data, void *obj)
{ {
struct rpc_cred *cred = task->tk_msg.rpc_cred; struct rpc_cred *cred = task->tk_msg.rpc_cred;
int ret;
dprintk("RPC: %5u using %s cred %p to wrap rpc data\n", dprintk("RPC: %5u using %s cred %p to wrap rpc data\n",
task->tk_pid, cred->cr_ops->cr_name, cred); task->tk_pid, cred->cr_ops->cr_name, cred);
if (cred->cr_ops->crwrap_req) if (cred->cr_ops->crwrap_req)
return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj); return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj);
/* By default, we encode the arguments normally. */ /* By default, we encode the arguments normally. */
return encode(rqstp, data, obj); lock_kernel();
ret = encode(rqstp, data, obj);
unlock_kernel();
return ret;
} }
int int
@ -373,6 +494,7 @@ rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp,
__be32 *data, void *obj) __be32 *data, void *obj)
{ {
struct rpc_cred *cred = task->tk_msg.rpc_cred; struct rpc_cred *cred = task->tk_msg.rpc_cred;
int ret;
dprintk("RPC: %5u using %s cred %p to unwrap rpc data\n", dprintk("RPC: %5u using %s cred %p to unwrap rpc data\n",
task->tk_pid, cred->cr_ops->cr_name, cred); task->tk_pid, cred->cr_ops->cr_name, cred);
@ -380,7 +502,10 @@ rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp,
return cred->cr_ops->crunwrap_resp(task, decode, rqstp, return cred->cr_ops->crunwrap_resp(task, decode, rqstp,
data, obj); data, obj);
/* By default, we decode the arguments normally. */ /* By default, we decode the arguments normally. */
return decode(rqstp, data, obj); lock_kernel();
ret = decode(rqstp, data, obj);
unlock_kernel();
return ret;
} }
int int
@ -390,7 +515,7 @@ rpcauth_refreshcred(struct rpc_task *task)
int err; int err;
dprintk("RPC: %5u refreshing %s cred %p\n", dprintk("RPC: %5u refreshing %s cred %p\n",
task->tk_pid, task->tk_auth->au_ops->au_name, cred); task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
err = cred->cr_ops->crrefresh(task); err = cred->cr_ops->crrefresh(task);
if (err < 0) if (err < 0)
@ -401,17 +526,34 @@ rpcauth_refreshcred(struct rpc_task *task)
void void
rpcauth_invalcred(struct rpc_task *task) rpcauth_invalcred(struct rpc_task *task)
{ {
struct rpc_cred *cred = task->tk_msg.rpc_cred;
dprintk("RPC: %5u invalidating %s cred %p\n", dprintk("RPC: %5u invalidating %s cred %p\n",
task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_msg.rpc_cred); task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
spin_lock(&rpc_credcache_lock); if (cred)
if (task->tk_msg.rpc_cred) clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
task->tk_msg.rpc_cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
spin_unlock(&rpc_credcache_lock);
} }
int int
rpcauth_uptodatecred(struct rpc_task *task) rpcauth_uptodatecred(struct rpc_task *task)
{ {
return !(task->tk_msg.rpc_cred) || struct rpc_cred *cred = task->tk_msg.rpc_cred;
(task->tk_msg.rpc_cred->cr_flags & RPCAUTH_CRED_UPTODATE);
return cred == NULL ||
test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0;
}
static struct shrinker *rpc_cred_shrinker;
void __init rpcauth_init_module(void)
{
rpc_init_authunix();
rpc_cred_shrinker = set_shrinker(DEFAULT_SEEKS, rpcauth_cache_shrinker);
}
void __exit rpcauth_remove_module(void)
{
if (rpc_cred_shrinker != NULL)
remove_shrinker(rpc_cred_shrinker);
} }

View File

@ -54,9 +54,10 @@
#include <linux/sunrpc/gss_api.h> #include <linux/sunrpc/gss_api.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
static struct rpc_authops authgss_ops; static const struct rpc_authops authgss_ops;
static struct rpc_credops gss_credops; static const struct rpc_credops gss_credops;
static const struct rpc_credops gss_nullops;
#ifdef RPC_DEBUG #ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH # define RPCDBG_FACILITY RPCDBG_AUTH
@ -64,7 +65,6 @@ static struct rpc_credops gss_credops;
#define NFS_NGROUPS 16 #define NFS_NGROUPS 16
#define GSS_CRED_EXPIRE (60 * HZ) /* XXX: reasonable? */
#define GSS_CRED_SLACK 1024 /* XXX: unused */ #define GSS_CRED_SLACK 1024 /* XXX: unused */
/* length of a krb5 verifier (48), plus data added before arguments when /* length of a krb5 verifier (48), plus data added before arguments when
* using integrity (two 4-byte integers): */ * using integrity (two 4-byte integers): */
@ -79,19 +79,16 @@ static struct rpc_credops gss_credops;
/* dump the buffer in `emacs-hexl' style */ /* dump the buffer in `emacs-hexl' style */
#define isprint(c) ((c > 0x1f) && (c < 0x7f)) #define isprint(c) ((c > 0x1f) && (c < 0x7f))
static DEFINE_RWLOCK(gss_ctx_lock);
struct gss_auth { struct gss_auth {
struct kref kref;
struct rpc_auth rpc_auth; struct rpc_auth rpc_auth;
struct gss_api_mech *mech; struct gss_api_mech *mech;
enum rpc_gss_svc service; enum rpc_gss_svc service;
struct list_head upcalls;
struct rpc_clnt *client; struct rpc_clnt *client;
struct dentry *dentry; struct dentry *dentry;
spinlock_t lock;
}; };
static void gss_destroy_ctx(struct gss_cl_ctx *); static void gss_free_ctx(struct gss_cl_ctx *);
static struct rpc_pipe_ops gss_upcall_ops; static struct rpc_pipe_ops gss_upcall_ops;
static inline struct gss_cl_ctx * static inline struct gss_cl_ctx *
@ -105,20 +102,24 @@ static inline void
gss_put_ctx(struct gss_cl_ctx *ctx) gss_put_ctx(struct gss_cl_ctx *ctx)
{ {
if (atomic_dec_and_test(&ctx->count)) if (atomic_dec_and_test(&ctx->count))
gss_destroy_ctx(ctx); gss_free_ctx(ctx);
} }
/* gss_cred_set_ctx:
* called by gss_upcall_callback and gss_create_upcall in order
* to set the gss context. The actual exchange of an old context
* and a new one is protected by the inode->i_lock.
*/
static void static void
gss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx) gss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx)
{ {
struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
struct gss_cl_ctx *old; struct gss_cl_ctx *old;
write_lock(&gss_ctx_lock);
old = gss_cred->gc_ctx; old = gss_cred->gc_ctx;
gss_cred->gc_ctx = ctx; rcu_assign_pointer(gss_cred->gc_ctx, ctx);
cred->cr_flags |= RPCAUTH_CRED_UPTODATE; set_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
cred->cr_flags &= ~RPCAUTH_CRED_NEW; clear_bit(RPCAUTH_CRED_NEW, &cred->cr_flags);
write_unlock(&gss_ctx_lock);
if (old) if (old)
gss_put_ctx(old); gss_put_ctx(old);
} }
@ -129,10 +130,10 @@ gss_cred_is_uptodate_ctx(struct rpc_cred *cred)
struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
int res = 0; int res = 0;
read_lock(&gss_ctx_lock); rcu_read_lock();
if ((cred->cr_flags & RPCAUTH_CRED_UPTODATE) && gss_cred->gc_ctx) if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) && gss_cred->gc_ctx)
res = 1; res = 1;
read_unlock(&gss_ctx_lock); rcu_read_unlock();
return res; return res;
} }
@ -171,10 +172,10 @@ gss_cred_get_ctx(struct rpc_cred *cred)
struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
struct gss_cl_ctx *ctx = NULL; struct gss_cl_ctx *ctx = NULL;
read_lock(&gss_ctx_lock); rcu_read_lock();
if (gss_cred->gc_ctx) if (gss_cred->gc_ctx)
ctx = gss_get_ctx(gss_cred->gc_ctx); ctx = gss_get_ctx(gss_cred->gc_ctx);
read_unlock(&gss_ctx_lock); rcu_read_unlock();
return ctx; return ctx;
} }
@ -269,10 +270,10 @@ gss_release_msg(struct gss_upcall_msg *gss_msg)
} }
static struct gss_upcall_msg * static struct gss_upcall_msg *
__gss_find_upcall(struct gss_auth *gss_auth, uid_t uid) __gss_find_upcall(struct rpc_inode *rpci, uid_t uid)
{ {
struct gss_upcall_msg *pos; struct gss_upcall_msg *pos;
list_for_each_entry(pos, &gss_auth->upcalls, list) { list_for_each_entry(pos, &rpci->in_downcall, list) {
if (pos->uid != uid) if (pos->uid != uid)
continue; continue;
atomic_inc(&pos->count); atomic_inc(&pos->count);
@ -290,24 +291,24 @@ __gss_find_upcall(struct gss_auth *gss_auth, uid_t uid)
static inline struct gss_upcall_msg * static inline struct gss_upcall_msg *
gss_add_msg(struct gss_auth *gss_auth, struct gss_upcall_msg *gss_msg) gss_add_msg(struct gss_auth *gss_auth, struct gss_upcall_msg *gss_msg)
{ {
struct inode *inode = gss_auth->dentry->d_inode;
struct rpc_inode *rpci = RPC_I(inode);
struct gss_upcall_msg *old; struct gss_upcall_msg *old;
spin_lock(&gss_auth->lock); spin_lock(&inode->i_lock);
old = __gss_find_upcall(gss_auth, gss_msg->uid); old = __gss_find_upcall(rpci, gss_msg->uid);
if (old == NULL) { if (old == NULL) {
atomic_inc(&gss_msg->count); atomic_inc(&gss_msg->count);
list_add(&gss_msg->list, &gss_auth->upcalls); list_add(&gss_msg->list, &rpci->in_downcall);
} else } else
gss_msg = old; gss_msg = old;
spin_unlock(&gss_auth->lock); spin_unlock(&inode->i_lock);
return gss_msg; return gss_msg;
} }
static void static void
__gss_unhash_msg(struct gss_upcall_msg *gss_msg) __gss_unhash_msg(struct gss_upcall_msg *gss_msg)
{ {
if (list_empty(&gss_msg->list))
return;
list_del_init(&gss_msg->list); list_del_init(&gss_msg->list);
rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno); rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno);
wake_up_all(&gss_msg->waitqueue); wake_up_all(&gss_msg->waitqueue);
@ -318,10 +319,14 @@ static void
gss_unhash_msg(struct gss_upcall_msg *gss_msg) gss_unhash_msg(struct gss_upcall_msg *gss_msg)
{ {
struct gss_auth *gss_auth = gss_msg->auth; struct gss_auth *gss_auth = gss_msg->auth;
struct inode *inode = gss_auth->dentry->d_inode;
spin_lock(&gss_auth->lock); if (list_empty(&gss_msg->list))
__gss_unhash_msg(gss_msg); return;
spin_unlock(&gss_auth->lock); spin_lock(&inode->i_lock);
if (!list_empty(&gss_msg->list))
__gss_unhash_msg(gss_msg);
spin_unlock(&inode->i_lock);
} }
static void static void
@ -330,16 +335,16 @@ gss_upcall_callback(struct rpc_task *task)
struct gss_cred *gss_cred = container_of(task->tk_msg.rpc_cred, struct gss_cred *gss_cred = container_of(task->tk_msg.rpc_cred,
struct gss_cred, gc_base); struct gss_cred, gc_base);
struct gss_upcall_msg *gss_msg = gss_cred->gc_upcall; struct gss_upcall_msg *gss_msg = gss_cred->gc_upcall;
struct inode *inode = gss_msg->auth->dentry->d_inode;
BUG_ON(gss_msg == NULL); spin_lock(&inode->i_lock);
if (gss_msg->ctx) if (gss_msg->ctx)
gss_cred_set_ctx(task->tk_msg.rpc_cred, gss_get_ctx(gss_msg->ctx)); gss_cred_set_ctx(task->tk_msg.rpc_cred, gss_get_ctx(gss_msg->ctx));
else else
task->tk_status = gss_msg->msg.errno; task->tk_status = gss_msg->msg.errno;
spin_lock(&gss_msg->auth->lock);
gss_cred->gc_upcall = NULL; gss_cred->gc_upcall = NULL;
rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno); rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno);
spin_unlock(&gss_msg->auth->lock); spin_unlock(&inode->i_lock);
gss_release_msg(gss_msg); gss_release_msg(gss_msg);
} }
@ -386,11 +391,12 @@ static inline int
gss_refresh_upcall(struct rpc_task *task) gss_refresh_upcall(struct rpc_task *task)
{ {
struct rpc_cred *cred = task->tk_msg.rpc_cred; struct rpc_cred *cred = task->tk_msg.rpc_cred;
struct gss_auth *gss_auth = container_of(task->tk_client->cl_auth, struct gss_auth *gss_auth = container_of(cred->cr_auth,
struct gss_auth, rpc_auth); struct gss_auth, rpc_auth);
struct gss_cred *gss_cred = container_of(cred, struct gss_cred *gss_cred = container_of(cred,
struct gss_cred, gc_base); struct gss_cred, gc_base);
struct gss_upcall_msg *gss_msg; struct gss_upcall_msg *gss_msg;
struct inode *inode = gss_auth->dentry->d_inode;
int err = 0; int err = 0;
dprintk("RPC: %5u gss_refresh_upcall for uid %u\n", task->tk_pid, dprintk("RPC: %5u gss_refresh_upcall for uid %u\n", task->tk_pid,
@ -400,7 +406,7 @@ gss_refresh_upcall(struct rpc_task *task)
err = PTR_ERR(gss_msg); err = PTR_ERR(gss_msg);
goto out; goto out;
} }
spin_lock(&gss_auth->lock); spin_lock(&inode->i_lock);
if (gss_cred->gc_upcall != NULL) if (gss_cred->gc_upcall != NULL)
rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL, NULL); rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL, NULL);
else if (gss_msg->ctx == NULL && gss_msg->msg.errno >= 0) { else if (gss_msg->ctx == NULL && gss_msg->msg.errno >= 0) {
@ -411,7 +417,7 @@ gss_refresh_upcall(struct rpc_task *task)
rpc_sleep_on(&gss_msg->rpc_waitqueue, task, gss_upcall_callback, NULL); rpc_sleep_on(&gss_msg->rpc_waitqueue, task, gss_upcall_callback, NULL);
} else } else
err = gss_msg->msg.errno; err = gss_msg->msg.errno;
spin_unlock(&gss_auth->lock); spin_unlock(&inode->i_lock);
gss_release_msg(gss_msg); gss_release_msg(gss_msg);
out: out:
dprintk("RPC: %5u gss_refresh_upcall for uid %u result %d\n", dprintk("RPC: %5u gss_refresh_upcall for uid %u result %d\n",
@ -422,6 +428,7 @@ out:
static inline int static inline int
gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred) gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred)
{ {
struct inode *inode = gss_auth->dentry->d_inode;
struct rpc_cred *cred = &gss_cred->gc_base; struct rpc_cred *cred = &gss_cred->gc_base;
struct gss_upcall_msg *gss_msg; struct gss_upcall_msg *gss_msg;
DEFINE_WAIT(wait); DEFINE_WAIT(wait);
@ -435,12 +442,11 @@ gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred)
} }
for (;;) { for (;;) {
prepare_to_wait(&gss_msg->waitqueue, &wait, TASK_INTERRUPTIBLE); prepare_to_wait(&gss_msg->waitqueue, &wait, TASK_INTERRUPTIBLE);
spin_lock(&gss_auth->lock); spin_lock(&inode->i_lock);
if (gss_msg->ctx != NULL || gss_msg->msg.errno < 0) { if (gss_msg->ctx != NULL || gss_msg->msg.errno < 0) {
spin_unlock(&gss_auth->lock);
break; break;
} }
spin_unlock(&gss_auth->lock); spin_unlock(&inode->i_lock);
if (signalled()) { if (signalled()) {
err = -ERESTARTSYS; err = -ERESTARTSYS;
goto out_intr; goto out_intr;
@ -451,6 +457,7 @@ gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred)
gss_cred_set_ctx(cred, gss_get_ctx(gss_msg->ctx)); gss_cred_set_ctx(cred, gss_get_ctx(gss_msg->ctx));
else else
err = gss_msg->msg.errno; err = gss_msg->msg.errno;
spin_unlock(&inode->i_lock);
out_intr: out_intr:
finish_wait(&gss_msg->waitqueue, &wait); finish_wait(&gss_msg->waitqueue, &wait);
gss_release_msg(gss_msg); gss_release_msg(gss_msg);
@ -489,12 +496,11 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
const void *p, *end; const void *p, *end;
void *buf; void *buf;
struct rpc_clnt *clnt; struct rpc_clnt *clnt;
struct gss_auth *gss_auth;
struct rpc_cred *cred;
struct gss_upcall_msg *gss_msg; struct gss_upcall_msg *gss_msg;
struct inode *inode = filp->f_path.dentry->d_inode;
struct gss_cl_ctx *ctx; struct gss_cl_ctx *ctx;
uid_t uid; uid_t uid;
int err = -EFBIG; ssize_t err = -EFBIG;
if (mlen > MSG_BUF_MAXSIZE) if (mlen > MSG_BUF_MAXSIZE)
goto out; goto out;
@ -503,7 +509,7 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
if (!buf) if (!buf)
goto out; goto out;
clnt = RPC_I(filp->f_path.dentry->d_inode)->private; clnt = RPC_I(inode)->private;
err = -EFAULT; err = -EFAULT;
if (copy_from_user(buf, src, mlen)) if (copy_from_user(buf, src, mlen))
goto err; goto err;
@ -519,43 +525,38 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
ctx = gss_alloc_context(); ctx = gss_alloc_context();
if (ctx == NULL) if (ctx == NULL)
goto err; goto err;
err = 0;
gss_auth = container_of(clnt->cl_auth, struct gss_auth, rpc_auth); err = -ENOENT;
p = gss_fill_context(p, end, ctx, gss_auth->mech); /* Find a matching upcall */
spin_lock(&inode->i_lock);
gss_msg = __gss_find_upcall(RPC_I(inode), uid);
if (gss_msg == NULL) {
spin_unlock(&inode->i_lock);
goto err_put_ctx;
}
list_del_init(&gss_msg->list);
spin_unlock(&inode->i_lock);
p = gss_fill_context(p, end, ctx, gss_msg->auth->mech);
if (IS_ERR(p)) { if (IS_ERR(p)) {
err = PTR_ERR(p); err = PTR_ERR(p);
if (err != -EACCES) gss_msg->msg.errno = (err == -EACCES) ? -EACCES : -EAGAIN;
goto err_put_ctx; goto err_release_msg;
} }
spin_lock(&gss_auth->lock); gss_msg->ctx = gss_get_ctx(ctx);
gss_msg = __gss_find_upcall(gss_auth, uid); err = mlen;
if (gss_msg) {
if (err == 0 && gss_msg->ctx == NULL) err_release_msg:
gss_msg->ctx = gss_get_ctx(ctx); spin_lock(&inode->i_lock);
gss_msg->msg.errno = err; __gss_unhash_msg(gss_msg);
__gss_unhash_msg(gss_msg); spin_unlock(&inode->i_lock);
spin_unlock(&gss_auth->lock); gss_release_msg(gss_msg);
gss_release_msg(gss_msg);
} else {
struct auth_cred acred = { .uid = uid };
spin_unlock(&gss_auth->lock);
cred = rpcauth_lookup_credcache(clnt->cl_auth, &acred, RPCAUTH_LOOKUP_NEW);
if (IS_ERR(cred)) {
err = PTR_ERR(cred);
goto err_put_ctx;
}
gss_cred_set_ctx(cred, gss_get_ctx(ctx));
}
gss_put_ctx(ctx);
kfree(buf);
dprintk("RPC: gss_pipe_downcall returning length %Zu\n", mlen);
return mlen;
err_put_ctx: err_put_ctx:
gss_put_ctx(ctx); gss_put_ctx(ctx);
err: err:
kfree(buf); kfree(buf);
out: out:
dprintk("RPC: gss_pipe_downcall returning %d\n", err); dprintk("RPC: gss_pipe_downcall returning %Zd\n", err);
return err; return err;
} }
@ -563,27 +564,21 @@ static void
gss_pipe_release(struct inode *inode) gss_pipe_release(struct inode *inode)
{ {
struct rpc_inode *rpci = RPC_I(inode); struct rpc_inode *rpci = RPC_I(inode);
struct rpc_clnt *clnt; struct gss_upcall_msg *gss_msg;
struct rpc_auth *auth;
struct gss_auth *gss_auth;
clnt = rpci->private; spin_lock(&inode->i_lock);
auth = clnt->cl_auth; while (!list_empty(&rpci->in_downcall)) {
gss_auth = container_of(auth, struct gss_auth, rpc_auth);
spin_lock(&gss_auth->lock);
while (!list_empty(&gss_auth->upcalls)) {
struct gss_upcall_msg *gss_msg;
gss_msg = list_entry(gss_auth->upcalls.next, gss_msg = list_entry(rpci->in_downcall.next,
struct gss_upcall_msg, list); struct gss_upcall_msg, list);
gss_msg->msg.errno = -EPIPE; gss_msg->msg.errno = -EPIPE;
atomic_inc(&gss_msg->count); atomic_inc(&gss_msg->count);
__gss_unhash_msg(gss_msg); __gss_unhash_msg(gss_msg);
spin_unlock(&gss_auth->lock); spin_unlock(&inode->i_lock);
gss_release_msg(gss_msg); gss_release_msg(gss_msg);
spin_lock(&gss_auth->lock); spin_lock(&inode->i_lock);
} }
spin_unlock(&gss_auth->lock); spin_unlock(&inode->i_lock);
} }
static void static void
@ -637,18 +632,13 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
gss_auth->service = gss_pseudoflavor_to_service(gss_auth->mech, flavor); gss_auth->service = gss_pseudoflavor_to_service(gss_auth->mech, flavor);
if (gss_auth->service == 0) if (gss_auth->service == 0)
goto err_put_mech; goto err_put_mech;
INIT_LIST_HEAD(&gss_auth->upcalls);
spin_lock_init(&gss_auth->lock);
auth = &gss_auth->rpc_auth; auth = &gss_auth->rpc_auth;
auth->au_cslack = GSS_CRED_SLACK >> 2; auth->au_cslack = GSS_CRED_SLACK >> 2;
auth->au_rslack = GSS_VERF_SLACK >> 2; auth->au_rslack = GSS_VERF_SLACK >> 2;
auth->au_ops = &authgss_ops; auth->au_ops = &authgss_ops;
auth->au_flavor = flavor; auth->au_flavor = flavor;
atomic_set(&auth->au_count, 1); atomic_set(&auth->au_count, 1);
kref_init(&gss_auth->kref);
err = rpcauth_init_credcache(auth, GSS_CRED_EXPIRE);
if (err)
goto err_put_mech;
gss_auth->dentry = rpc_mkpipe(clnt->cl_dentry, gss_auth->mech->gm_name, gss_auth->dentry = rpc_mkpipe(clnt->cl_dentry, gss_auth->mech->gm_name,
clnt, &gss_upcall_ops, RPC_PIPE_WAIT_FOR_OPEN); clnt, &gss_upcall_ops, RPC_PIPE_WAIT_FOR_OPEN);
@ -657,7 +647,13 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
goto err_put_mech; goto err_put_mech;
} }
err = rpcauth_init_credcache(auth);
if (err)
goto err_unlink_pipe;
return auth; return auth;
err_unlink_pipe:
rpc_unlink(gss_auth->dentry);
err_put_mech: err_put_mech:
gss_mech_put(gss_auth->mech); gss_mech_put(gss_auth->mech);
err_free: err_free:
@ -667,6 +663,25 @@ out_dec:
return ERR_PTR(err); return ERR_PTR(err);
} }
static void
gss_free(struct gss_auth *gss_auth)
{
rpc_unlink(gss_auth->dentry);
gss_auth->dentry = NULL;
gss_mech_put(gss_auth->mech);
kfree(gss_auth);
module_put(THIS_MODULE);
}
static void
gss_free_callback(struct kref *kref)
{
struct gss_auth *gss_auth = container_of(kref, struct gss_auth, kref);
gss_free(gss_auth);
}
static void static void
gss_destroy(struct rpc_auth *auth) gss_destroy(struct rpc_auth *auth)
{ {
@ -675,23 +690,51 @@ gss_destroy(struct rpc_auth *auth)
dprintk("RPC: destroying GSS authenticator %p flavor %d\n", dprintk("RPC: destroying GSS authenticator %p flavor %d\n",
auth, auth->au_flavor); auth, auth->au_flavor);
gss_auth = container_of(auth, struct gss_auth, rpc_auth); rpcauth_destroy_credcache(auth);
rpc_unlink(gss_auth->dentry);
gss_auth->dentry = NULL;
gss_mech_put(gss_auth->mech);
rpcauth_free_credcache(auth); gss_auth = container_of(auth, struct gss_auth, rpc_auth);
kfree(gss_auth); kref_put(&gss_auth->kref, gss_free_callback);
module_put(THIS_MODULE);
} }
/* gss_destroy_cred (and gss_destroy_ctx) are used to clean up after failure /*
* gss_destroying_context will cause the RPCSEC_GSS to send a NULL RPC call
* to the server with the GSS control procedure field set to
* RPC_GSS_PROC_DESTROY. This should normally cause the server to release
* all RPCSEC_GSS state associated with that context.
*/
static int
gss_destroying_context(struct rpc_cred *cred)
{
struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth);
struct rpc_task *task;
if (gss_cred->gc_ctx == NULL ||
gss_cred->gc_ctx->gc_proc == RPC_GSS_PROC_DESTROY)
return 0;
gss_cred->gc_ctx->gc_proc = RPC_GSS_PROC_DESTROY;
cred->cr_ops = &gss_nullops;
/* Take a reference to ensure the cred will be destroyed either
* by the RPC call or by the put_rpccred() below */
get_rpccred(cred);
task = rpc_call_null(gss_auth->client, cred, RPC_TASK_ASYNC);
if (!IS_ERR(task))
rpc_put_task(task);
put_rpccred(cred);
return 1;
}
/* gss_destroy_cred (and gss_free_ctx) are used to clean up after failure
* to create a new cred or context, so they check that things have been * to create a new cred or context, so they check that things have been
* allocated before freeing them. */ * allocated before freeing them. */
static void static void
gss_destroy_ctx(struct gss_cl_ctx *ctx) gss_do_free_ctx(struct gss_cl_ctx *ctx)
{ {
dprintk("RPC: gss_destroy_ctx\n"); dprintk("RPC: gss_free_ctx\n");
if (ctx->gc_gss_ctx) if (ctx->gc_gss_ctx)
gss_delete_sec_context(&ctx->gc_gss_ctx); gss_delete_sec_context(&ctx->gc_gss_ctx);
@ -701,15 +744,46 @@ gss_destroy_ctx(struct gss_cl_ctx *ctx)
} }
static void static void
gss_destroy_cred(struct rpc_cred *rc) gss_free_ctx_callback(struct rcu_head *head)
{ {
struct gss_cred *cred = container_of(rc, struct gss_cred, gc_base); struct gss_cl_ctx *ctx = container_of(head, struct gss_cl_ctx, gc_rcu);
gss_do_free_ctx(ctx);
}
dprintk("RPC: gss_destroy_cred \n"); static void
gss_free_ctx(struct gss_cl_ctx *ctx)
{
call_rcu(&ctx->gc_rcu, gss_free_ctx_callback);
}
if (cred->gc_ctx) static void
gss_put_ctx(cred->gc_ctx); gss_free_cred(struct gss_cred *gss_cred)
kfree(cred); {
dprintk("RPC: gss_free_cred %p\n", gss_cred);
kfree(gss_cred);
}
static void
gss_free_cred_callback(struct rcu_head *head)
{
struct gss_cred *gss_cred = container_of(head, struct gss_cred, gc_base.cr_rcu);
gss_free_cred(gss_cred);
}
static void
gss_destroy_cred(struct rpc_cred *cred)
{
struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth);
struct gss_cl_ctx *ctx = gss_cred->gc_ctx;
if (gss_destroying_context(cred))
return;
rcu_assign_pointer(gss_cred->gc_ctx, NULL);
call_rcu(&cred->cr_rcu, gss_free_cred_callback);
if (ctx)
gss_put_ctx(ctx);
kref_put(&gss_auth->kref, gss_free_callback);
} }
/* /*
@ -734,16 +808,14 @@ gss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
if (!(cred = kzalloc(sizeof(*cred), GFP_KERNEL))) if (!(cred = kzalloc(sizeof(*cred), GFP_KERNEL)))
goto out_err; goto out_err;
atomic_set(&cred->gc_count, 1); rpcauth_init_cred(&cred->gc_base, acred, auth, &gss_credops);
cred->gc_uid = acred->uid;
/* /*
* Note: in order to force a call to call_refresh(), we deliberately * Note: in order to force a call to call_refresh(), we deliberately
* fail to flag the credential as RPCAUTH_CRED_UPTODATE. * fail to flag the credential as RPCAUTH_CRED_UPTODATE.
*/ */
cred->gc_flags = 0; cred->gc_base.cr_flags = 1UL << RPCAUTH_CRED_NEW;
cred->gc_base.cr_ops = &gss_credops;
cred->gc_base.cr_flags = RPCAUTH_CRED_NEW;
cred->gc_service = gss_auth->service; cred->gc_service = gss_auth->service;
kref_get(&gss_auth->kref);
return &cred->gc_base; return &cred->gc_base;
out_err: out_err:
@ -774,7 +846,7 @@ gss_match(struct auth_cred *acred, struct rpc_cred *rc, int flags)
* we don't really care if the credential has expired or not, * we don't really care if the credential has expired or not,
* since the caller should be prepared to reinitialise it. * since the caller should be prepared to reinitialise it.
*/ */
if ((flags & RPCAUTH_LOOKUP_NEW) && (rc->cr_flags & RPCAUTH_CRED_NEW)) if ((flags & RPCAUTH_LOOKUP_NEW) && test_bit(RPCAUTH_CRED_NEW, &rc->cr_flags))
goto out; goto out;
/* Don't match with creds that have expired. */ /* Don't match with creds that have expired. */
if (gss_cred->gc_ctx && time_after(jiffies, gss_cred->gc_ctx->gc_expiry)) if (gss_cred->gc_ctx && time_after(jiffies, gss_cred->gc_ctx->gc_expiry))
@ -830,7 +902,7 @@ gss_marshal(struct rpc_task *task, __be32 *p)
mic.data = (u8 *)(p + 1); mic.data = (u8 *)(p + 1);
maj_stat = gss_get_mic(ctx->gc_gss_ctx, &verf_buf, &mic); maj_stat = gss_get_mic(ctx->gc_gss_ctx, &verf_buf, &mic);
if (maj_stat == GSS_S_CONTEXT_EXPIRED) { if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
} else if (maj_stat != 0) { } else if (maj_stat != 0) {
printk("gss_marshal: gss_get_mic FAILED (%d)\n", maj_stat); printk("gss_marshal: gss_get_mic FAILED (%d)\n", maj_stat);
goto out_put_ctx; goto out_put_ctx;
@ -855,6 +927,13 @@ gss_refresh(struct rpc_task *task)
return 0; return 0;
} }
/* Dummy refresh routine: used only when destroying the context */
static int
gss_refresh_null(struct rpc_task *task)
{
return -EACCES;
}
static __be32 * static __be32 *
gss_validate(struct rpc_task *task, __be32 *p) gss_validate(struct rpc_task *task, __be32 *p)
{ {
@ -883,12 +962,15 @@ gss_validate(struct rpc_task *task, __be32 *p)
maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic); maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic);
if (maj_stat == GSS_S_CONTEXT_EXPIRED) if (maj_stat == GSS_S_CONTEXT_EXPIRED)
cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
if (maj_stat) if (maj_stat) {
dprintk("RPC: %5u gss_validate: gss_verify_mic returned"
"error 0x%08x\n", task->tk_pid, maj_stat);
goto out_bad; goto out_bad;
}
/* We leave it to unwrap to calculate au_rslack. For now we just /* We leave it to unwrap to calculate au_rslack. For now we just
* calculate the length of the verifier: */ * calculate the length of the verifier: */
task->tk_auth->au_verfsize = XDR_QUADLEN(len) + 2; cred->cr_auth->au_verfsize = XDR_QUADLEN(len) + 2;
gss_put_ctx(ctx); gss_put_ctx(ctx);
dprintk("RPC: %5u gss_validate: gss_verify_mic succeeded.\n", dprintk("RPC: %5u gss_validate: gss_verify_mic succeeded.\n",
task->tk_pid); task->tk_pid);
@ -917,7 +999,9 @@ gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;
*p++ = htonl(rqstp->rq_seqno); *p++ = htonl(rqstp->rq_seqno);
lock_kernel();
status = encode(rqstp, p, obj); status = encode(rqstp, p, obj);
unlock_kernel();
if (status) if (status)
return status; return status;
@ -937,7 +1021,7 @@ gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
maj_stat = gss_get_mic(ctx->gc_gss_ctx, &integ_buf, &mic); maj_stat = gss_get_mic(ctx->gc_gss_ctx, &integ_buf, &mic);
status = -EIO; /* XXX? */ status = -EIO; /* XXX? */
if (maj_stat == GSS_S_CONTEXT_EXPIRED) if (maj_stat == GSS_S_CONTEXT_EXPIRED)
cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
else if (maj_stat) else if (maj_stat)
return status; return status;
q = xdr_encode_opaque(p, NULL, mic.len); q = xdr_encode_opaque(p, NULL, mic.len);
@ -1011,7 +1095,9 @@ gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;
*p++ = htonl(rqstp->rq_seqno); *p++ = htonl(rqstp->rq_seqno);
lock_kernel();
status = encode(rqstp, p, obj); status = encode(rqstp, p, obj);
unlock_kernel();
if (status) if (status)
return status; return status;
@ -1036,7 +1122,7 @@ gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
/* We're assuming that when GSS_S_CONTEXT_EXPIRED, the encryption was /* We're assuming that when GSS_S_CONTEXT_EXPIRED, the encryption was
* done anyway, so it's safe to put the request on the wire: */ * done anyway, so it's safe to put the request on the wire: */
if (maj_stat == GSS_S_CONTEXT_EXPIRED) if (maj_stat == GSS_S_CONTEXT_EXPIRED)
cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
else if (maj_stat) else if (maj_stat)
return status; return status;
@ -1070,12 +1156,16 @@ gss_wrap_req(struct rpc_task *task,
/* The spec seems a little ambiguous here, but I think that not /* The spec seems a little ambiguous here, but I think that not
* wrapping context destruction requests makes the most sense. * wrapping context destruction requests makes the most sense.
*/ */
lock_kernel();
status = encode(rqstp, p, obj); status = encode(rqstp, p, obj);
unlock_kernel();
goto out; goto out;
} }
switch (gss_cred->gc_service) { switch (gss_cred->gc_service) {
case RPC_GSS_SVC_NONE: case RPC_GSS_SVC_NONE:
lock_kernel();
status = encode(rqstp, p, obj); status = encode(rqstp, p, obj);
unlock_kernel();
break; break;
case RPC_GSS_SVC_INTEGRITY: case RPC_GSS_SVC_INTEGRITY:
status = gss_wrap_req_integ(cred, ctx, encode, status = gss_wrap_req_integ(cred, ctx, encode,
@ -1123,7 +1213,7 @@ gss_unwrap_resp_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &integ_buf, &mic); maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &integ_buf, &mic);
if (maj_stat == GSS_S_CONTEXT_EXPIRED) if (maj_stat == GSS_S_CONTEXT_EXPIRED)
cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
if (maj_stat != GSS_S_COMPLETE) if (maj_stat != GSS_S_COMPLETE)
return status; return status;
return 0; return 0;
@ -1148,7 +1238,7 @@ gss_unwrap_resp_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
maj_stat = gss_unwrap(ctx->gc_gss_ctx, offset, rcv_buf); maj_stat = gss_unwrap(ctx->gc_gss_ctx, offset, rcv_buf);
if (maj_stat == GSS_S_CONTEXT_EXPIRED) if (maj_stat == GSS_S_CONTEXT_EXPIRED)
cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
if (maj_stat != GSS_S_COMPLETE) if (maj_stat != GSS_S_COMPLETE)
return status; return status;
if (ntohl(*(*p)++) != rqstp->rq_seqno) if (ntohl(*(*p)++) != rqstp->rq_seqno)
@ -1188,10 +1278,12 @@ gss_unwrap_resp(struct rpc_task *task,
break; break;
} }
/* take into account extra slack for integrity and privacy cases: */ /* take into account extra slack for integrity and privacy cases: */
task->tk_auth->au_rslack = task->tk_auth->au_verfsize + (p - savedp) cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize + (p - savedp)
+ (savedlen - head->iov_len); + (savedlen - head->iov_len);
out_decode: out_decode:
lock_kernel();
status = decode(rqstp, p, obj); status = decode(rqstp, p, obj);
unlock_kernel();
out: out:
gss_put_ctx(ctx); gss_put_ctx(ctx);
dprintk("RPC: %5u gss_unwrap_resp returning %d\n", task->tk_pid, dprintk("RPC: %5u gss_unwrap_resp returning %d\n", task->tk_pid,
@ -1199,7 +1291,7 @@ out:
return status; return status;
} }
static struct rpc_authops authgss_ops = { static const struct rpc_authops authgss_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.au_flavor = RPC_AUTH_GSS, .au_flavor = RPC_AUTH_GSS,
#ifdef RPC_DEBUG #ifdef RPC_DEBUG
@ -1211,7 +1303,7 @@ static struct rpc_authops authgss_ops = {
.crcreate = gss_create_cred .crcreate = gss_create_cred
}; };
static struct rpc_credops gss_credops = { static const struct rpc_credops gss_credops = {
.cr_name = "AUTH_GSS", .cr_name = "AUTH_GSS",
.crdestroy = gss_destroy_cred, .crdestroy = gss_destroy_cred,
.cr_init = gss_cred_init, .cr_init = gss_cred_init,
@ -1223,6 +1315,17 @@ static struct rpc_credops gss_credops = {
.crunwrap_resp = gss_unwrap_resp, .crunwrap_resp = gss_unwrap_resp,
}; };
static const struct rpc_credops gss_nullops = {
.cr_name = "AUTH_GSS",
.crdestroy = gss_destroy_cred,
.crmatch = gss_match,
.crmarshal = gss_marshal,
.crrefresh = gss_refresh_null,
.crvalidate = gss_validate,
.crwrap_req = gss_wrap_req,
.crunwrap_resp = gss_unwrap_resp,
};
static struct rpc_pipe_ops gss_upcall_ops = { static struct rpc_pipe_ops gss_upcall_ops = {
.upcall = gss_pipe_upcall, .upcall = gss_pipe_upcall,
.downcall = gss_pipe_downcall, .downcall = gss_pipe_downcall,

View File

@ -201,7 +201,7 @@ gss_delete_sec_context_kerberos(void *internal_ctx) {
kfree(kctx); kfree(kctx);
} }
static struct gss_api_ops gss_kerberos_ops = { static const struct gss_api_ops gss_kerberos_ops = {
.gss_import_sec_context = gss_import_sec_context_kerberos, .gss_import_sec_context = gss_import_sec_context_kerberos,
.gss_get_mic = gss_get_mic_kerberos, .gss_get_mic = gss_get_mic_kerberos,
.gss_verify_mic = gss_verify_mic_kerberos, .gss_verify_mic = gss_verify_mic_kerberos,

View File

@ -202,7 +202,7 @@ gss_get_mic_spkm3(struct gss_ctx *ctx,
return err; return err;
} }
static struct gss_api_ops gss_spkm3_ops = { static const struct gss_api_ops gss_spkm3_ops = {
.gss_import_sec_context = gss_import_sec_context_spkm3, .gss_import_sec_context = gss_import_sec_context_spkm3,
.gss_get_mic = gss_get_mic_spkm3, .gss_get_mic = gss_get_mic_spkm3,
.gss_verify_mic = gss_verify_mic_spkm3, .gss_verify_mic = gss_verify_mic_spkm3,

View File

@ -76,7 +76,7 @@ nul_marshal(struct rpc_task *task, __be32 *p)
static int static int
nul_refresh(struct rpc_task *task) nul_refresh(struct rpc_task *task)
{ {
task->tk_msg.rpc_cred->cr_flags |= RPCAUTH_CRED_UPTODATE; set_bit(RPCAUTH_CRED_UPTODATE, &task->tk_msg.rpc_cred->cr_flags);
return 0; return 0;
} }
@ -101,7 +101,7 @@ nul_validate(struct rpc_task *task, __be32 *p)
return p; return p;
} }
struct rpc_authops authnull_ops = { const struct rpc_authops authnull_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.au_flavor = RPC_AUTH_NULL, .au_flavor = RPC_AUTH_NULL,
#ifdef RPC_DEBUG #ifdef RPC_DEBUG
@ -122,7 +122,7 @@ struct rpc_auth null_auth = {
}; };
static static
struct rpc_credops null_credops = { const struct rpc_credops null_credops = {
.cr_name = "AUTH_NULL", .cr_name = "AUTH_NULL",
.crdestroy = nul_destroy_cred, .crdestroy = nul_destroy_cred,
.crmatch = nul_match, .crmatch = nul_match,
@ -133,9 +133,11 @@ struct rpc_credops null_credops = {
static static
struct rpc_cred null_cred = { struct rpc_cred null_cred = {
.cr_lru = LIST_HEAD_INIT(null_cred.cr_lru),
.cr_auth = &null_auth,
.cr_ops = &null_credops, .cr_ops = &null_credops,
.cr_count = ATOMIC_INIT(1), .cr_count = ATOMIC_INIT(1),
.cr_flags = RPCAUTH_CRED_UPTODATE, .cr_flags = 1UL << RPCAUTH_CRED_UPTODATE,
#ifdef RPC_DEBUG #ifdef RPC_DEBUG
.cr_magic = RPCAUTH_CRED_MAGIC, .cr_magic = RPCAUTH_CRED_MAGIC,
#endif #endif

View File

@ -20,11 +20,6 @@ struct unx_cred {
gid_t uc_gids[NFS_NGROUPS]; gid_t uc_gids[NFS_NGROUPS];
}; };
#define uc_uid uc_base.cr_uid #define uc_uid uc_base.cr_uid
#define uc_count uc_base.cr_count
#define uc_flags uc_base.cr_flags
#define uc_expire uc_base.cr_expire
#define UNX_CRED_EXPIRE (60 * HZ)
#define UNX_WRITESLACK (21 + (UNX_MAXNODENAME >> 2)) #define UNX_WRITESLACK (21 + (UNX_MAXNODENAME >> 2))
@ -34,15 +29,14 @@ struct unx_cred {
static struct rpc_auth unix_auth; static struct rpc_auth unix_auth;
static struct rpc_cred_cache unix_cred_cache; static struct rpc_cred_cache unix_cred_cache;
static struct rpc_credops unix_credops; static const struct rpc_credops unix_credops;
static struct rpc_auth * static struct rpc_auth *
unx_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor) unx_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
{ {
dprintk("RPC: creating UNIX authenticator for client %p\n", dprintk("RPC: creating UNIX authenticator for client %p\n",
clnt); clnt);
if (atomic_inc_return(&unix_auth.au_count) == 0) atomic_inc(&unix_auth.au_count);
unix_cred_cache.nextgc = jiffies + (unix_cred_cache.expire >> 1);
return &unix_auth; return &unix_auth;
} }
@ -50,7 +44,7 @@ static void
unx_destroy(struct rpc_auth *auth) unx_destroy(struct rpc_auth *auth)
{ {
dprintk("RPC: destroying UNIX authenticator %p\n", auth); dprintk("RPC: destroying UNIX authenticator %p\n", auth);
rpcauth_free_credcache(auth); rpcauth_clear_credcache(auth->au_credcache);
} }
/* /*
@ -74,8 +68,8 @@ unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
if (!(cred = kmalloc(sizeof(*cred), GFP_KERNEL))) if (!(cred = kmalloc(sizeof(*cred), GFP_KERNEL)))
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
atomic_set(&cred->uc_count, 1); rpcauth_init_cred(&cred->uc_base, acred, auth, &unix_credops);
cred->uc_flags = RPCAUTH_CRED_UPTODATE; cred->uc_base.cr_flags = 1UL << RPCAUTH_CRED_UPTODATE;
if (flags & RPCAUTH_LOOKUP_ROOTCREDS) { if (flags & RPCAUTH_LOOKUP_ROOTCREDS) {
cred->uc_uid = 0; cred->uc_uid = 0;
cred->uc_gid = 0; cred->uc_gid = 0;
@ -85,22 +79,34 @@ unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
if (groups > NFS_NGROUPS) if (groups > NFS_NGROUPS)
groups = NFS_NGROUPS; groups = NFS_NGROUPS;
cred->uc_uid = acred->uid;
cred->uc_gid = acred->gid; cred->uc_gid = acred->gid;
for (i = 0; i < groups; i++) for (i = 0; i < groups; i++)
cred->uc_gids[i] = GROUP_AT(acred->group_info, i); cred->uc_gids[i] = GROUP_AT(acred->group_info, i);
if (i < NFS_NGROUPS) if (i < NFS_NGROUPS)
cred->uc_gids[i] = NOGROUP; cred->uc_gids[i] = NOGROUP;
} }
cred->uc_base.cr_ops = &unix_credops;
return (struct rpc_cred *) cred; return &cred->uc_base;
}
static void
unx_free_cred(struct unx_cred *unx_cred)
{
dprintk("RPC: unx_free_cred %p\n", unx_cred);
kfree(unx_cred);
}
static void
unx_free_cred_callback(struct rcu_head *head)
{
struct unx_cred *unx_cred = container_of(head, struct unx_cred, uc_base.cr_rcu);
unx_free_cred(unx_cred);
} }
static void static void
unx_destroy_cred(struct rpc_cred *cred) unx_destroy_cred(struct rpc_cred *cred)
{ {
kfree(cred); call_rcu(&cred->cr_rcu, unx_free_cred_callback);
} }
/* /*
@ -111,7 +117,7 @@ unx_destroy_cred(struct rpc_cred *cred)
static int static int
unx_match(struct auth_cred *acred, struct rpc_cred *rcred, int flags) unx_match(struct auth_cred *acred, struct rpc_cred *rcred, int flags)
{ {
struct unx_cred *cred = (struct unx_cred *) rcred; struct unx_cred *cred = container_of(rcred, struct unx_cred, uc_base);
int i; int i;
if (!(flags & RPCAUTH_LOOKUP_ROOTCREDS)) { if (!(flags & RPCAUTH_LOOKUP_ROOTCREDS)) {
@ -142,7 +148,7 @@ static __be32 *
unx_marshal(struct rpc_task *task, __be32 *p) unx_marshal(struct rpc_task *task, __be32 *p)
{ {
struct rpc_clnt *clnt = task->tk_client; struct rpc_clnt *clnt = task->tk_client;
struct unx_cred *cred = (struct unx_cred *) task->tk_msg.rpc_cred; struct unx_cred *cred = container_of(task->tk_msg.rpc_cred, struct unx_cred, uc_base);
__be32 *base, *hold; __be32 *base, *hold;
int i; int i;
@ -175,7 +181,7 @@ unx_marshal(struct rpc_task *task, __be32 *p)
static int static int
unx_refresh(struct rpc_task *task) unx_refresh(struct rpc_task *task)
{ {
task->tk_msg.rpc_cred->cr_flags |= RPCAUTH_CRED_UPTODATE; set_bit(RPCAUTH_CRED_UPTODATE, &task->tk_msg.rpc_cred->cr_flags);
return 0; return 0;
} }
@ -198,13 +204,18 @@ unx_validate(struct rpc_task *task, __be32 *p)
printk("RPC: giant verf size: %u\n", size); printk("RPC: giant verf size: %u\n", size);
return NULL; return NULL;
} }
task->tk_auth->au_rslack = (size >> 2) + 2; task->tk_msg.rpc_cred->cr_auth->au_rslack = (size >> 2) + 2;
p += (size >> 2); p += (size >> 2);
return p; return p;
} }
struct rpc_authops authunix_ops = { void __init rpc_init_authunix(void)
{
spin_lock_init(&unix_cred_cache.lock);
}
const struct rpc_authops authunix_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.au_flavor = RPC_AUTH_UNIX, .au_flavor = RPC_AUTH_UNIX,
#ifdef RPC_DEBUG #ifdef RPC_DEBUG
@ -218,7 +229,6 @@ struct rpc_authops authunix_ops = {
static static
struct rpc_cred_cache unix_cred_cache = { struct rpc_cred_cache unix_cred_cache = {
.expire = UNX_CRED_EXPIRE,
}; };
static static
@ -232,7 +242,7 @@ struct rpc_auth unix_auth = {
}; };
static static
struct rpc_credops unix_credops = { const struct rpc_credops unix_credops = {
.cr_name = "AUTH_UNIX", .cr_name = "AUTH_UNIX",
.crdestroy = unx_destroy_cred, .crdestroy = unx_destroy_cred,
.crmatch = unx_match, .crmatch = unx_match,

View File

@ -44,6 +44,12 @@
dprintk("RPC: %5u %s (status %d)\n", t->tk_pid, \ dprintk("RPC: %5u %s (status %d)\n", t->tk_pid, \
__FUNCTION__, t->tk_status) __FUNCTION__, t->tk_status)
/*
* All RPC clients are linked into this list
*/
static LIST_HEAD(all_clients);
static DEFINE_SPINLOCK(rpc_client_lock);
static DECLARE_WAIT_QUEUE_HEAD(destroy_wait); static DECLARE_WAIT_QUEUE_HEAD(destroy_wait);
@ -66,6 +72,21 @@ static void call_connect_status(struct rpc_task *task);
static __be32 * call_header(struct rpc_task *task); static __be32 * call_header(struct rpc_task *task);
static __be32 * call_verify(struct rpc_task *task); static __be32 * call_verify(struct rpc_task *task);
static int rpc_ping(struct rpc_clnt *clnt, int flags);
static void rpc_register_client(struct rpc_clnt *clnt)
{
spin_lock(&rpc_client_lock);
list_add(&clnt->cl_clients, &all_clients);
spin_unlock(&rpc_client_lock);
}
static void rpc_unregister_client(struct rpc_clnt *clnt)
{
spin_lock(&rpc_client_lock);
list_del(&clnt->cl_clients);
spin_unlock(&rpc_client_lock);
}
static int static int
rpc_setup_pipedir(struct rpc_clnt *clnt, char *dir_name) rpc_setup_pipedir(struct rpc_clnt *clnt, char *dir_name)
@ -111,6 +132,9 @@ static struct rpc_clnt * rpc_new_client(struct rpc_xprt *xprt, char *servname, s
dprintk("RPC: creating %s client for %s (xprt %p)\n", dprintk("RPC: creating %s client for %s (xprt %p)\n",
program->name, servname, xprt); program->name, servname, xprt);
err = rpciod_up();
if (err)
goto out_no_rpciod;
err = -EINVAL; err = -EINVAL;
if (!xprt) if (!xprt)
goto out_no_xprt; goto out_no_xprt;
@ -121,8 +145,6 @@ static struct rpc_clnt * rpc_new_client(struct rpc_xprt *xprt, char *servname, s
clnt = kzalloc(sizeof(*clnt), GFP_KERNEL); clnt = kzalloc(sizeof(*clnt), GFP_KERNEL);
if (!clnt) if (!clnt)
goto out_err; goto out_err;
atomic_set(&clnt->cl_users, 0);
atomic_set(&clnt->cl_count, 1);
clnt->cl_parent = clnt; clnt->cl_parent = clnt;
clnt->cl_server = clnt->cl_inline_name; clnt->cl_server = clnt->cl_inline_name;
@ -148,6 +170,8 @@ static struct rpc_clnt * rpc_new_client(struct rpc_xprt *xprt, char *servname, s
if (clnt->cl_metrics == NULL) if (clnt->cl_metrics == NULL)
goto out_no_stats; goto out_no_stats;
clnt->cl_program = program; clnt->cl_program = program;
INIT_LIST_HEAD(&clnt->cl_tasks);
spin_lock_init(&clnt->cl_lock);
if (!xprt_bound(clnt->cl_xprt)) if (!xprt_bound(clnt->cl_xprt))
clnt->cl_autobind = 1; clnt->cl_autobind = 1;
@ -155,6 +179,8 @@ static struct rpc_clnt * rpc_new_client(struct rpc_xprt *xprt, char *servname, s
clnt->cl_rtt = &clnt->cl_rtt_default; clnt->cl_rtt = &clnt->cl_rtt_default;
rpc_init_rtt(&clnt->cl_rtt_default, xprt->timeout.to_initval); rpc_init_rtt(&clnt->cl_rtt_default, xprt->timeout.to_initval);
kref_init(&clnt->cl_kref);
err = rpc_setup_pipedir(clnt, program->pipe_dir_name); err = rpc_setup_pipedir(clnt, program->pipe_dir_name);
if (err < 0) if (err < 0)
goto out_no_path; goto out_no_path;
@ -172,6 +198,7 @@ static struct rpc_clnt * rpc_new_client(struct rpc_xprt *xprt, char *servname, s
if (clnt->cl_nodelen > UNX_MAXNODENAME) if (clnt->cl_nodelen > UNX_MAXNODENAME)
clnt->cl_nodelen = UNX_MAXNODENAME; clnt->cl_nodelen = UNX_MAXNODENAME;
memcpy(clnt->cl_nodename, utsname()->nodename, clnt->cl_nodelen); memcpy(clnt->cl_nodename, utsname()->nodename, clnt->cl_nodelen);
rpc_register_client(clnt);
return clnt; return clnt;
out_no_auth: out_no_auth:
@ -188,6 +215,8 @@ out_no_stats:
out_err: out_err:
xprt_put(xprt); xprt_put(xprt);
out_no_xprt: out_no_xprt:
rpciod_down();
out_no_rpciod:
return ERR_PTR(err); return ERR_PTR(err);
} }
@ -205,12 +234,31 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args)
{ {
struct rpc_xprt *xprt; struct rpc_xprt *xprt;
struct rpc_clnt *clnt; struct rpc_clnt *clnt;
struct rpc_xprtsock_create xprtargs = {
.proto = args->protocol,
.srcaddr = args->saddress,
.dstaddr = args->address,
.addrlen = args->addrsize,
.timeout = args->timeout
};
char servername[20];
xprt = xprt_create_transport(args->protocol, args->address, xprt = xprt_create_transport(&xprtargs);
args->addrsize, args->timeout);
if (IS_ERR(xprt)) if (IS_ERR(xprt))
return (struct rpc_clnt *)xprt; return (struct rpc_clnt *)xprt;
/*
* If the caller chooses not to specify a hostname, whip
* up a string representation of the passed-in address.
*/
if (args->servername == NULL) {
struct sockaddr_in *addr =
(struct sockaddr_in *) &args->address;
snprintf(servername, sizeof(servername), NIPQUAD_FMT,
NIPQUAD(addr->sin_addr.s_addr));
args->servername = servername;
}
/* /*
* By default, kernel RPC client connects from a reserved port. * By default, kernel RPC client connects from a reserved port.
* CAP_NET_BIND_SERVICE will not be set for unprivileged requesters, * CAP_NET_BIND_SERVICE will not be set for unprivileged requesters,
@ -245,8 +293,6 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args)
clnt->cl_intr = 1; clnt->cl_intr = 1;
if (args->flags & RPC_CLNT_CREATE_AUTOBIND) if (args->flags & RPC_CLNT_CREATE_AUTOBIND)
clnt->cl_autobind = 1; clnt->cl_autobind = 1;
if (args->flags & RPC_CLNT_CREATE_ONESHOT)
clnt->cl_oneshot = 1;
if (args->flags & RPC_CLNT_CREATE_DISCRTRY) if (args->flags & RPC_CLNT_CREATE_DISCRTRY)
clnt->cl_discrtry = 1; clnt->cl_discrtry = 1;
@ -268,24 +314,25 @@ rpc_clone_client(struct rpc_clnt *clnt)
new = kmemdup(clnt, sizeof(*new), GFP_KERNEL); new = kmemdup(clnt, sizeof(*new), GFP_KERNEL);
if (!new) if (!new)
goto out_no_clnt; goto out_no_clnt;
atomic_set(&new->cl_count, 1); new->cl_parent = clnt;
atomic_set(&new->cl_users, 0); /* Turn off autobind on clones */
new->cl_autobind = 0;
INIT_LIST_HEAD(&new->cl_tasks);
spin_lock_init(&new->cl_lock);
rpc_init_rtt(&new->cl_rtt_default, clnt->cl_xprt->timeout.to_initval);
new->cl_metrics = rpc_alloc_iostats(clnt); new->cl_metrics = rpc_alloc_iostats(clnt);
if (new->cl_metrics == NULL) if (new->cl_metrics == NULL)
goto out_no_stats; goto out_no_stats;
kref_init(&new->cl_kref);
err = rpc_setup_pipedir(new, clnt->cl_program->pipe_dir_name); err = rpc_setup_pipedir(new, clnt->cl_program->pipe_dir_name);
if (err != 0) if (err != 0)
goto out_no_path; goto out_no_path;
new->cl_parent = clnt;
atomic_inc(&clnt->cl_count);
new->cl_xprt = xprt_get(clnt->cl_xprt);
/* Turn off autobind on clones */
new->cl_autobind = 0;
new->cl_oneshot = 0;
new->cl_dead = 0;
rpc_init_rtt(&new->cl_rtt_default, clnt->cl_xprt->timeout.to_initval);
if (new->cl_auth) if (new->cl_auth)
atomic_inc(&new->cl_auth->au_count); atomic_inc(&new->cl_auth->au_count);
xprt_get(clnt->cl_xprt);
kref_get(&clnt->cl_kref);
rpc_register_client(new);
rpciod_up();
return new; return new;
out_no_path: out_no_path:
rpc_free_iostats(new->cl_metrics); rpc_free_iostats(new->cl_metrics);
@ -298,86 +345,86 @@ out_no_clnt:
/* /*
* Properly shut down an RPC client, terminating all outstanding * Properly shut down an RPC client, terminating all outstanding
* requests. Note that we must be certain that cl_oneshot and * requests.
* cl_dead are cleared, or else the client would be destroyed
* when the last task releases it.
*/ */
int void rpc_shutdown_client(struct rpc_clnt *clnt)
rpc_shutdown_client(struct rpc_clnt *clnt)
{ {
dprintk("RPC: shutting down %s client for %s, tasks=%d\n", dprintk("RPC: shutting down %s client for %s\n",
clnt->cl_protname, clnt->cl_server, clnt->cl_protname, clnt->cl_server);
atomic_read(&clnt->cl_users));
while (atomic_read(&clnt->cl_users) > 0) { while (!list_empty(&clnt->cl_tasks)) {
/* Don't let rpc_release_client destroy us */
clnt->cl_oneshot = 0;
clnt->cl_dead = 0;
rpc_killall_tasks(clnt); rpc_killall_tasks(clnt);
wait_event_timeout(destroy_wait, wait_event_timeout(destroy_wait,
!atomic_read(&clnt->cl_users), 1*HZ); list_empty(&clnt->cl_tasks), 1*HZ);
} }
if (atomic_read(&clnt->cl_users) < 0) { rpc_release_client(clnt);
printk(KERN_ERR "RPC: rpc_shutdown_client clnt %p tasks=%d\n",
clnt, atomic_read(&clnt->cl_users));
#ifdef RPC_DEBUG
rpc_show_tasks();
#endif
BUG();
}
return rpc_destroy_client(clnt);
} }
/* /*
* Delete an RPC client * Free an RPC client
*/ */
int static void
rpc_destroy_client(struct rpc_clnt *clnt) rpc_free_client(struct kref *kref)
{ {
if (!atomic_dec_and_test(&clnt->cl_count)) struct rpc_clnt *clnt = container_of(kref, struct rpc_clnt, cl_kref);
return 1;
BUG_ON(atomic_read(&clnt->cl_users) != 0);
dprintk("RPC: destroying %s client for %s\n", dprintk("RPC: destroying %s client for %s\n",
clnt->cl_protname, clnt->cl_server); clnt->cl_protname, clnt->cl_server);
if (clnt->cl_auth) {
rpcauth_destroy(clnt->cl_auth);
clnt->cl_auth = NULL;
}
if (!IS_ERR(clnt->cl_dentry)) { if (!IS_ERR(clnt->cl_dentry)) {
rpc_rmdir(clnt->cl_dentry); rpc_rmdir(clnt->cl_dentry);
rpc_put_mount(); rpc_put_mount();
} }
if (clnt->cl_parent != clnt) { if (clnt->cl_parent != clnt) {
rpc_destroy_client(clnt->cl_parent); rpc_release_client(clnt->cl_parent);
goto out_free; goto out_free;
} }
if (clnt->cl_server != clnt->cl_inline_name) if (clnt->cl_server != clnt->cl_inline_name)
kfree(clnt->cl_server); kfree(clnt->cl_server);
out_free: out_free:
rpc_unregister_client(clnt);
rpc_free_iostats(clnt->cl_metrics); rpc_free_iostats(clnt->cl_metrics);
clnt->cl_metrics = NULL; clnt->cl_metrics = NULL;
xprt_put(clnt->cl_xprt); xprt_put(clnt->cl_xprt);
rpciod_down();
kfree(clnt); kfree(clnt);
return 0;
} }
/* /*
* Release an RPC client * Free an RPC client
*/
static void
rpc_free_auth(struct kref *kref)
{
struct rpc_clnt *clnt = container_of(kref, struct rpc_clnt, cl_kref);
if (clnt->cl_auth == NULL) {
rpc_free_client(kref);
return;
}
/*
* Note: RPCSEC_GSS may need to send NULL RPC calls in order to
* release remaining GSS contexts. This mechanism ensures
* that it can do so safely.
*/
kref_init(kref);
rpcauth_release(clnt->cl_auth);
clnt->cl_auth = NULL;
kref_put(kref, rpc_free_client);
}
/*
* Release reference to the RPC client
*/ */
void void
rpc_release_client(struct rpc_clnt *clnt) rpc_release_client(struct rpc_clnt *clnt)
{ {
dprintk("RPC: rpc_release_client(%p, %d)\n", dprintk("RPC: rpc_release_client(%p)\n", clnt);
clnt, atomic_read(&clnt->cl_users));
if (!atomic_dec_and_test(&clnt->cl_users)) if (list_empty(&clnt->cl_tasks))
return; wake_up(&destroy_wait);
wake_up(&destroy_wait); kref_put(&clnt->cl_kref, rpc_free_auth);
if (clnt->cl_oneshot || clnt->cl_dead)
rpc_destroy_client(clnt);
} }
/** /**
@ -468,82 +515,96 @@ void rpc_clnt_sigunmask(struct rpc_clnt *clnt, sigset_t *oldset)
rpc_restore_sigmask(oldset); rpc_restore_sigmask(oldset);
} }
/* static
* New rpc_call implementation struct rpc_task *rpc_do_run_task(struct rpc_clnt *clnt,
struct rpc_message *msg,
int flags,
const struct rpc_call_ops *ops,
void *data)
{
struct rpc_task *task, *ret;
sigset_t oldset;
task = rpc_new_task(clnt, flags, ops, data);
if (task == NULL) {
rpc_release_calldata(ops, data);
return ERR_PTR(-ENOMEM);
}
/* Mask signals on synchronous RPC calls and RPCSEC_GSS upcalls */
rpc_task_sigmask(task, &oldset);
if (msg != NULL) {
rpc_call_setup(task, msg, 0);
if (task->tk_status != 0) {
ret = ERR_PTR(task->tk_status);
rpc_put_task(task);
goto out;
}
}
atomic_inc(&task->tk_count);
rpc_execute(task);
ret = task;
out:
rpc_restore_sigmask(&oldset);
return ret;
}
/**
* rpc_call_sync - Perform a synchronous RPC call
* @clnt: pointer to RPC client
* @msg: RPC call parameters
* @flags: RPC call flags
*/ */
int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags) int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
{ {
struct rpc_task *task; struct rpc_task *task;
sigset_t oldset; int status;
int status;
/* If this client is slain all further I/O fails */
if (clnt->cl_dead)
return -EIO;
BUG_ON(flags & RPC_TASK_ASYNC); BUG_ON(flags & RPC_TASK_ASYNC);
task = rpc_new_task(clnt, flags, &rpc_default_ops, NULL); task = rpc_do_run_task(clnt, msg, flags, &rpc_default_ops, NULL);
if (task == NULL) if (IS_ERR(task))
return -ENOMEM; return PTR_ERR(task);
/* Mask signals on RPC calls _and_ GSS_AUTH upcalls */
rpc_task_sigmask(task, &oldset);
/* Set up the call info struct and execute the task */
rpc_call_setup(task, msg, 0);
if (task->tk_status == 0) {
atomic_inc(&task->tk_count);
rpc_execute(task);
}
status = task->tk_status; status = task->tk_status;
rpc_put_task(task); rpc_put_task(task);
rpc_restore_sigmask(&oldset);
return status; return status;
} }
/* /**
* New rpc_call implementation * rpc_call_async - Perform an asynchronous RPC call
* @clnt: pointer to RPC client
* @msg: RPC call parameters
* @flags: RPC call flags
* @ops: RPC call ops
* @data: user call data
*/ */
int int
rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg, int flags, rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg, int flags,
const struct rpc_call_ops *tk_ops, void *data) const struct rpc_call_ops *tk_ops, void *data)
{ {
struct rpc_task *task; struct rpc_task *task;
sigset_t oldset;
int status;
/* If this client is slain all further I/O fails */ task = rpc_do_run_task(clnt, msg, flags|RPC_TASK_ASYNC, tk_ops, data);
status = -EIO; if (IS_ERR(task))
if (clnt->cl_dead) return PTR_ERR(task);
goto out_release; rpc_put_task(task);
return 0;
flags |= RPC_TASK_ASYNC;
/* Create/initialize a new RPC task */
status = -ENOMEM;
if (!(task = rpc_new_task(clnt, flags, tk_ops, data)))
goto out_release;
/* Mask signals on GSS_AUTH upcalls */
rpc_task_sigmask(task, &oldset);
rpc_call_setup(task, msg, 0);
/* Set up the call info struct and execute the task */
status = task->tk_status;
if (status == 0)
rpc_execute(task);
else
rpc_put_task(task);
rpc_restore_sigmask(&oldset);
return status;
out_release:
rpc_release_calldata(tk_ops, data);
return status;
} }
/**
* rpc_run_task - Allocate a new RPC task, then run rpc_execute against it
* @clnt: pointer to RPC client
* @flags: RPC flags
* @ops: RPC call ops
* @data: user call data
*/
struct rpc_task *rpc_run_task(struct rpc_clnt *clnt, int flags,
const struct rpc_call_ops *tk_ops,
void *data)
{
return rpc_do_run_task(clnt, NULL, flags, tk_ops, data);
}
EXPORT_SYMBOL(rpc_run_task);
void void
rpc_call_setup(struct rpc_task *task, struct rpc_message *msg, int flags) rpc_call_setup(struct rpc_task *task, struct rpc_message *msg, int flags)
@ -745,7 +806,7 @@ call_reserveresult(struct rpc_task *task)
static void static void
call_allocate(struct rpc_task *task) call_allocate(struct rpc_task *task)
{ {
unsigned int slack = task->tk_auth->au_cslack; unsigned int slack = task->tk_msg.rpc_cred->cr_auth->au_cslack;
struct rpc_rqst *req = task->tk_rqstp; struct rpc_rqst *req = task->tk_rqstp;
struct rpc_xprt *xprt = task->tk_xprt; struct rpc_xprt *xprt = task->tk_xprt;
struct rpc_procinfo *proc = task->tk_msg.rpc_proc; struct rpc_procinfo *proc = task->tk_msg.rpc_proc;
@ -843,10 +904,8 @@ call_encode(struct rpc_task *task)
if (encode == NULL) if (encode == NULL)
return; return;
lock_kernel();
task->tk_status = rpcauth_wrap_req(task, encode, req, p, task->tk_status = rpcauth_wrap_req(task, encode, req, p,
task->tk_msg.rpc_argp); task->tk_msg.rpc_argp);
unlock_kernel();
if (task->tk_status == -ENOMEM) { if (task->tk_status == -ENOMEM) {
/* XXX: Is this sane? */ /* XXX: Is this sane? */
rpc_delay(task, 3*HZ); rpc_delay(task, 3*HZ);
@ -1177,10 +1236,8 @@ call_decode(struct rpc_task *task)
task->tk_action = rpc_exit_task; task->tk_action = rpc_exit_task;
if (decode) { if (decode) {
lock_kernel();
task->tk_status = rpcauth_unwrap_resp(task, decode, req, p, task->tk_status = rpcauth_unwrap_resp(task, decode, req, p,
task->tk_msg.rpc_resp); task->tk_msg.rpc_resp);
unlock_kernel();
} }
dprintk("RPC: %5u call_decode result %d\n", task->tk_pid, dprintk("RPC: %5u call_decode result %d\n", task->tk_pid,
task->tk_status); task->tk_status);
@ -1273,9 +1330,9 @@ call_verify(struct rpc_task *task)
* - if it isn't pointer subtraction in the NFS client may give * - if it isn't pointer subtraction in the NFS client may give
* undefined results * undefined results
*/ */
printk(KERN_WARNING dprintk("RPC: %5u %s: XDR representation not a multiple of"
"call_verify: XDR representation not a multiple of" " 4 bytes: 0x%x\n", task->tk_pid, __FUNCTION__,
" 4 bytes: 0x%x\n", task->tk_rqstp->rq_rcv_buf.len); task->tk_rqstp->rq_rcv_buf.len);
goto out_eio; goto out_eio;
} }
if ((len -= 3) < 0) if ((len -= 3) < 0)
@ -1283,7 +1340,8 @@ call_verify(struct rpc_task *task)
p += 1; /* skip XID */ p += 1; /* skip XID */
if ((n = ntohl(*p++)) != RPC_REPLY) { if ((n = ntohl(*p++)) != RPC_REPLY) {
printk(KERN_WARNING "call_verify: not an RPC reply: %x\n", n); dprintk("RPC: %5u %s: not an RPC reply: %x\n",
task->tk_pid, __FUNCTION__, n);
goto out_garbage; goto out_garbage;
} }
if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) { if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) {
@ -1334,7 +1392,8 @@ call_verify(struct rpc_task *task)
"authentication.\n", task->tk_client->cl_server); "authentication.\n", task->tk_client->cl_server);
break; break;
default: default:
printk(KERN_WARNING "call_verify: unknown auth error: %x\n", n); dprintk("RPC: %5u %s: unknown auth error: %x\n",
task->tk_pid, __FUNCTION__, n);
error = -EIO; error = -EIO;
} }
dprintk("RPC: %5u %s: call rejected %d\n", dprintk("RPC: %5u %s: call rejected %d\n",
@ -1342,7 +1401,8 @@ call_verify(struct rpc_task *task)
goto out_err; goto out_err;
} }
if (!(p = rpcauth_checkverf(task, p))) { if (!(p = rpcauth_checkverf(task, p))) {
printk(KERN_WARNING "call_verify: auth check failed\n"); dprintk("RPC: %5u %s: auth check failed\n",
task->tk_pid, __FUNCTION__);
goto out_garbage; /* bad verifier, retry */ goto out_garbage; /* bad verifier, retry */
} }
len = p - (__be32 *)iov->iov_base - 1; len = p - (__be32 *)iov->iov_base - 1;
@ -1381,7 +1441,8 @@ call_verify(struct rpc_task *task)
task->tk_pid, __FUNCTION__); task->tk_pid, __FUNCTION__);
break; /* retry */ break; /* retry */
default: default:
printk(KERN_WARNING "call_verify: server accept status: %x\n", n); dprintk("RPC: %5u %s: server accept status: %x\n",
task->tk_pid, __FUNCTION__, n);
/* Also retry */ /* Also retry */
} }
@ -1395,14 +1456,16 @@ out_garbage:
out_retry: out_retry:
return ERR_PTR(-EAGAIN); return ERR_PTR(-EAGAIN);
} }
printk(KERN_WARNING "RPC %s: retry failed, exit EIO\n", __FUNCTION__);
out_eio: out_eio:
error = -EIO; error = -EIO;
out_err: out_err:
rpc_exit(task, error); rpc_exit(task, error);
dprintk("RPC: %5u %s: call failed with error %d\n", task->tk_pid,
__FUNCTION__, error);
return ERR_PTR(error); return ERR_PTR(error);
out_overflow: out_overflow:
printk(KERN_WARNING "RPC %s: server reply was truncated.\n", __FUNCTION__); dprintk("RPC: %5u %s: server reply was truncated.\n", task->tk_pid,
__FUNCTION__);
goto out_garbage; goto out_garbage;
} }
@ -1421,7 +1484,7 @@ static struct rpc_procinfo rpcproc_null = {
.p_decode = rpcproc_decode_null, .p_decode = rpcproc_decode_null,
}; };
int rpc_ping(struct rpc_clnt *clnt, int flags) static int rpc_ping(struct rpc_clnt *clnt, int flags)
{ {
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &rpcproc_null, .rpc_proc = &rpcproc_null,
@ -1432,3 +1495,51 @@ int rpc_ping(struct rpc_clnt *clnt, int flags)
put_rpccred(msg.rpc_cred); put_rpccred(msg.rpc_cred);
return err; return err;
} }
struct rpc_task *rpc_call_null(struct rpc_clnt *clnt, struct rpc_cred *cred, int flags)
{
struct rpc_message msg = {
.rpc_proc = &rpcproc_null,
.rpc_cred = cred,
};
return rpc_do_run_task(clnt, &msg, flags, &rpc_default_ops, NULL);
}
EXPORT_SYMBOL(rpc_call_null);
#ifdef RPC_DEBUG
void rpc_show_tasks(void)
{
struct rpc_clnt *clnt;
struct rpc_task *t;
spin_lock(&rpc_client_lock);
if (list_empty(&all_clients))
goto out;
printk("-pid- proc flgs status -client- -prog- --rqstp- -timeout "
"-rpcwait -action- ---ops--\n");
list_for_each_entry(clnt, &all_clients, cl_clients) {
if (list_empty(&clnt->cl_tasks))
continue;
spin_lock(&clnt->cl_lock);
list_for_each_entry(t, &clnt->cl_tasks, tk_task) {
const char *rpc_waitq = "none";
if (RPC_IS_QUEUED(t))
rpc_waitq = rpc_qname(t->u.tk_wait.rpc_waitq);
printk("%5u %04d %04x %6d %8p %6d %8p %8ld %8s %8p %8p\n",
t->tk_pid,
(t->tk_msg.rpc_proc ? t->tk_msg.rpc_proc->p_proc : -1),
t->tk_flags, t->tk_status,
t->tk_client,
(t->tk_client ? t->tk_client->cl_prog : 0),
t->tk_rqstp, t->tk_timeout,
rpc_waitq,
t->tk_action, t->tk_ops);
}
spin_unlock(&clnt->cl_lock);
}
out:
spin_unlock(&rpc_client_lock);
}
#endif

View File

@ -344,7 +344,7 @@ rpc_info_open(struct inode *inode, struct file *file)
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
clnt = RPC_I(inode)->private; clnt = RPC_I(inode)->private;
if (clnt) { if (clnt) {
atomic_inc(&clnt->cl_users); kref_get(&clnt->cl_kref);
m->private = clnt; m->private = clnt;
} else { } else {
single_release(inode, file); single_release(inode, file);
@ -448,6 +448,15 @@ void rpc_put_mount(void)
simple_release_fs(&rpc_mount, &rpc_mount_count); simple_release_fs(&rpc_mount, &rpc_mount_count);
} }
static int rpc_delete_dentry(struct dentry *dentry)
{
return 1;
}
static struct dentry_operations rpc_dentry_operations = {
.d_delete = rpc_delete_dentry,
};
static int static int
rpc_lookup_parent(char *path, struct nameidata *nd) rpc_lookup_parent(char *path, struct nameidata *nd)
{ {
@ -506,7 +515,7 @@ rpc_get_inode(struct super_block *sb, int mode)
* FIXME: This probably has races. * FIXME: This probably has races.
*/ */
static void static void
rpc_depopulate(struct dentry *parent) rpc_depopulate(struct dentry *parent, int start, int eof)
{ {
struct inode *dir = parent->d_inode; struct inode *dir = parent->d_inode;
struct list_head *pos, *next; struct list_head *pos, *next;
@ -518,6 +527,10 @@ repeat:
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
list_for_each_safe(pos, next, &parent->d_subdirs) { list_for_each_safe(pos, next, &parent->d_subdirs) {
dentry = list_entry(pos, struct dentry, d_u.d_child); dentry = list_entry(pos, struct dentry, d_u.d_child);
if (!dentry->d_inode ||
dentry->d_inode->i_ino < start ||
dentry->d_inode->i_ino >= eof)
continue;
spin_lock(&dentry->d_lock); spin_lock(&dentry->d_lock);
if (!d_unhashed(dentry)) { if (!d_unhashed(dentry)) {
dget_locked(dentry); dget_locked(dentry);
@ -533,11 +546,11 @@ repeat:
if (n) { if (n) {
do { do {
dentry = dvec[--n]; dentry = dvec[--n];
if (dentry->d_inode) { if (S_ISREG(dentry->d_inode->i_mode))
rpc_close_pipes(dentry->d_inode);
simple_unlink(dir, dentry); simple_unlink(dir, dentry);
} else if (S_ISDIR(dentry->d_inode->i_mode))
inode_dir_notify(dir, DN_DELETE); simple_rmdir(dir, dentry);
d_delete(dentry);
dput(dentry); dput(dentry);
} while (n); } while (n);
goto repeat; goto repeat;
@ -560,6 +573,7 @@ rpc_populate(struct dentry *parent,
dentry = d_alloc_name(parent, files[i].name); dentry = d_alloc_name(parent, files[i].name);
if (!dentry) if (!dentry)
goto out_bad; goto out_bad;
dentry->d_op = &rpc_dentry_operations;
mode = files[i].mode; mode = files[i].mode;
inode = rpc_get_inode(dir->i_sb, mode); inode = rpc_get_inode(dir->i_sb, mode);
if (!inode) { if (!inode) {
@ -607,21 +621,14 @@ static int
__rpc_rmdir(struct inode *dir, struct dentry *dentry) __rpc_rmdir(struct inode *dir, struct dentry *dentry)
{ {
int error; int error;
error = simple_rmdir(dir, dentry);
shrink_dcache_parent(dentry); if (!error)
if (d_unhashed(dentry)) d_delete(dentry);
return 0; return error;
if ((error = simple_rmdir(dir, dentry)) != 0)
return error;
if (!error) {
inode_dir_notify(dir, DN_DELETE);
d_drop(dentry);
}
return 0;
} }
static struct dentry * static struct dentry *
rpc_lookup_create(struct dentry *parent, const char *name, int len) rpc_lookup_create(struct dentry *parent, const char *name, int len, int exclusive)
{ {
struct inode *dir = parent->d_inode; struct inode *dir = parent->d_inode;
struct dentry *dentry; struct dentry *dentry;
@ -630,7 +637,9 @@ rpc_lookup_create(struct dentry *parent, const char *name, int len)
dentry = lookup_one_len(name, parent, len); dentry = lookup_one_len(name, parent, len);
if (IS_ERR(dentry)) if (IS_ERR(dentry))
goto out_err; goto out_err;
if (dentry->d_inode) { if (!dentry->d_inode)
dentry->d_op = &rpc_dentry_operations;
else if (exclusive) {
dput(dentry); dput(dentry);
dentry = ERR_PTR(-EEXIST); dentry = ERR_PTR(-EEXIST);
goto out_err; goto out_err;
@ -649,7 +658,7 @@ rpc_lookup_negative(char *path, struct nameidata *nd)
if ((error = rpc_lookup_parent(path, nd)) != 0) if ((error = rpc_lookup_parent(path, nd)) != 0)
return ERR_PTR(error); return ERR_PTR(error);
dentry = rpc_lookup_create(nd->dentry, nd->last.name, nd->last.len); dentry = rpc_lookup_create(nd->dentry, nd->last.name, nd->last.len, 1);
if (IS_ERR(dentry)) if (IS_ERR(dentry))
rpc_release_path(nd); rpc_release_path(nd);
return dentry; return dentry;
@ -681,7 +690,7 @@ out:
rpc_release_path(&nd); rpc_release_path(&nd);
return dentry; return dentry;
err_depopulate: err_depopulate:
rpc_depopulate(dentry); rpc_depopulate(dentry, RPCAUTH_info, RPCAUTH_EOF);
__rpc_rmdir(dir, dentry); __rpc_rmdir(dir, dentry);
err_dput: err_dput:
dput(dentry); dput(dentry);
@ -701,7 +710,7 @@ rpc_rmdir(struct dentry *dentry)
parent = dget_parent(dentry); parent = dget_parent(dentry);
dir = parent->d_inode; dir = parent->d_inode;
mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT); mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT);
rpc_depopulate(dentry); rpc_depopulate(dentry, RPCAUTH_info, RPCAUTH_EOF);
error = __rpc_rmdir(dir, dentry); error = __rpc_rmdir(dir, dentry);
dput(dentry); dput(dentry);
mutex_unlock(&dir->i_mutex); mutex_unlock(&dir->i_mutex);
@ -716,10 +725,21 @@ rpc_mkpipe(struct dentry *parent, const char *name, void *private, struct rpc_pi
struct inode *dir, *inode; struct inode *dir, *inode;
struct rpc_inode *rpci; struct rpc_inode *rpci;
dentry = rpc_lookup_create(parent, name, strlen(name)); dentry = rpc_lookup_create(parent, name, strlen(name), 0);
if (IS_ERR(dentry)) if (IS_ERR(dentry))
return dentry; return dentry;
dir = parent->d_inode; dir = parent->d_inode;
if (dentry->d_inode) {
rpci = RPC_I(dentry->d_inode);
if (rpci->private != private ||
rpci->ops != ops ||
rpci->flags != flags) {
dput (dentry);
dentry = ERR_PTR(-EBUSY);
}
rpci->nkern_readwriters++;
goto out;
}
inode = rpc_get_inode(dir->i_sb, S_IFIFO | S_IRUSR | S_IWUSR); inode = rpc_get_inode(dir->i_sb, S_IFIFO | S_IRUSR | S_IWUSR);
if (!inode) if (!inode)
goto err_dput; goto err_dput;
@ -730,6 +750,7 @@ rpc_mkpipe(struct dentry *parent, const char *name, void *private, struct rpc_pi
rpci->private = private; rpci->private = private;
rpci->flags = flags; rpci->flags = flags;
rpci->ops = ops; rpci->ops = ops;
rpci->nkern_readwriters = 1;
inode_dir_notify(dir, DN_CREATE); inode_dir_notify(dir, DN_CREATE);
dget(dentry); dget(dentry);
out: out:
@ -754,13 +775,11 @@ rpc_unlink(struct dentry *dentry)
parent = dget_parent(dentry); parent = dget_parent(dentry);
dir = parent->d_inode; dir = parent->d_inode;
mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT); mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT);
if (!d_unhashed(dentry)) { if (--RPC_I(dentry->d_inode)->nkern_readwriters == 0) {
d_drop(dentry); rpc_close_pipes(dentry->d_inode);
if (dentry->d_inode) { error = simple_unlink(dir, dentry);
rpc_close_pipes(dentry->d_inode); if (!error)
error = simple_unlink(dir, dentry); d_delete(dentry);
}
inode_dir_notify(dir, DN_DELETE);
} }
dput(dentry); dput(dentry);
mutex_unlock(&dir->i_mutex); mutex_unlock(&dir->i_mutex);
@ -833,6 +852,7 @@ init_once(void * foo, struct kmem_cache * cachep, unsigned long flags)
rpci->nreaders = 0; rpci->nreaders = 0;
rpci->nwriters = 0; rpci->nwriters = 0;
INIT_LIST_HEAD(&rpci->in_upcall); INIT_LIST_HEAD(&rpci->in_upcall);
INIT_LIST_HEAD(&rpci->in_downcall);
INIT_LIST_HEAD(&rpci->pipe); INIT_LIST_HEAD(&rpci->pipe);
rpci->pipelen = 0; rpci->pipelen = 0;
init_waitqueue_head(&rpci->waitq); init_waitqueue_head(&rpci->waitq);

View File

@ -12,6 +12,8 @@
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de> * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
*/ */
#include <linux/module.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/socket.h> #include <linux/socket.h>
#include <linux/kernel.h> #include <linux/kernel.h>
@ -184,8 +186,8 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
.program = &rpcb_program, .program = &rpcb_program,
.version = version, .version = version,
.authflavor = RPC_AUTH_UNIX, .authflavor = RPC_AUTH_UNIX,
.flags = (RPC_CLNT_CREATE_ONESHOT | .flags = (RPC_CLNT_CREATE_NOPING |
RPC_CLNT_CREATE_NOPING), RPC_CLNT_CREATE_INTR),
}; };
((struct sockaddr_in *)srvaddr)->sin_port = htons(RPCBIND_PORT); ((struct sockaddr_in *)srvaddr)->sin_port = htons(RPCBIND_PORT);
@ -238,6 +240,7 @@ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
error = rpc_call_sync(rpcb_clnt, &msg, 0); error = rpc_call_sync(rpcb_clnt, &msg, 0);
rpc_shutdown_client(rpcb_clnt);
if (error < 0) if (error < 0)
printk(KERN_WARNING "RPC: failed to contact local rpcbind " printk(KERN_WARNING "RPC: failed to contact local rpcbind "
"server (errno %d).\n", -error); "server (errno %d).\n", -error);
@ -246,21 +249,20 @@ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
return error; return error;
} }
#ifdef CONFIG_ROOT_NFS
/** /**
* rpcb_getport_external - obtain the port for an RPC service on a given host * rpcb_getport_sync - obtain the port for an RPC service on a given host
* @sin: address of remote peer * @sin: address of remote peer
* @prog: RPC program number to bind * @prog: RPC program number to bind
* @vers: RPC version number to bind * @vers: RPC version number to bind
* @prot: transport protocol to use to make this request * @prot: transport protocol to use to make this request
* *
* Called from outside the RPC client in a synchronous task context. * Called from outside the RPC client in a synchronous task context.
* Uses default timeout parameters specified by underlying transport.
* *
* For now, this supports only version 2 queries, but is used only by * XXX: Needs to support IPv6, and rpcbind versions 3 and 4
* mount_clnt for NFS_ROOT.
*/ */
int rpcb_getport_external(struct sockaddr_in *sin, __u32 prog, int rpcb_getport_sync(struct sockaddr_in *sin, __u32 prog,
__u32 vers, int prot) __u32 vers, int prot)
{ {
struct rpcbind_args map = { struct rpcbind_args map = {
.r_prog = prog, .r_prog = prog,
@ -277,15 +279,16 @@ int rpcb_getport_external(struct sockaddr_in *sin, __u32 prog,
char hostname[40]; char hostname[40];
int status; int status;
dprintk("RPC: rpcb_getport_external(%u.%u.%u.%u, %u, %u, %d)\n", dprintk("RPC: %s(" NIPQUAD_FMT ", %u, %u, %d)\n",
NIPQUAD(sin->sin_addr.s_addr), prog, vers, prot); __FUNCTION__, NIPQUAD(sin->sin_addr.s_addr), prog, vers, prot);
sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(sin->sin_addr.s_addr)); sprintf(hostname, NIPQUAD_FMT, NIPQUAD(sin->sin_addr.s_addr));
rpcb_clnt = rpcb_create(hostname, (struct sockaddr *)sin, prot, 2, 0); rpcb_clnt = rpcb_create(hostname, (struct sockaddr *)sin, prot, 2, 0);
if (IS_ERR(rpcb_clnt)) if (IS_ERR(rpcb_clnt))
return PTR_ERR(rpcb_clnt); return PTR_ERR(rpcb_clnt);
status = rpc_call_sync(rpcb_clnt, &msg, 0); status = rpc_call_sync(rpcb_clnt, &msg, 0);
rpc_shutdown_client(rpcb_clnt);
if (status >= 0) { if (status >= 0) {
if (map.r_port != 0) if (map.r_port != 0)
@ -294,16 +297,16 @@ int rpcb_getport_external(struct sockaddr_in *sin, __u32 prog,
} }
return status; return status;
} }
#endif EXPORT_SYMBOL_GPL(rpcb_getport_sync);
/** /**
* rpcb_getport - obtain the port for a given RPC service on a given host * rpcb_getport_async - obtain the port for a given RPC service on a given host
* @task: task that is waiting for portmapper request * @task: task that is waiting for portmapper request
* *
* This one can be called for an ongoing RPC request, and can be used in * This one can be called for an ongoing RPC request, and can be used in
* an async (rpciod) context. * an async (rpciod) context.
*/ */
void rpcb_getport(struct rpc_task *task) void rpcb_getport_async(struct rpc_task *task)
{ {
struct rpc_clnt *clnt = task->tk_client; struct rpc_clnt *clnt = task->tk_client;
int bind_version; int bind_version;
@ -314,17 +317,17 @@ void rpcb_getport(struct rpc_task *task)
struct sockaddr addr; struct sockaddr addr;
int status; int status;
dprintk("RPC: %5u rpcb_getport(%s, %u, %u, %d)\n", dprintk("RPC: %5u %s(%s, %u, %u, %d)\n",
task->tk_pid, clnt->cl_server, task->tk_pid, __FUNCTION__,
clnt->cl_prog, clnt->cl_vers, xprt->prot); clnt->cl_server, clnt->cl_prog, clnt->cl_vers, xprt->prot);
/* Autobind on cloned rpc clients is discouraged */ /* Autobind on cloned rpc clients is discouraged */
BUG_ON(clnt->cl_parent != clnt); BUG_ON(clnt->cl_parent != clnt);
if (xprt_test_and_set_binding(xprt)) { if (xprt_test_and_set_binding(xprt)) {
status = -EACCES; /* tell caller to check again */ status = -EACCES; /* tell caller to check again */
dprintk("RPC: %5u rpcb_getport waiting for another binder\n", dprintk("RPC: %5u %s: waiting for another binder\n",
task->tk_pid); task->tk_pid, __FUNCTION__);
goto bailout_nowake; goto bailout_nowake;
} }
@ -335,27 +338,28 @@ void rpcb_getport(struct rpc_task *task)
/* Someone else may have bound if we slept */ /* Someone else may have bound if we slept */
if (xprt_bound(xprt)) { if (xprt_bound(xprt)) {
status = 0; status = 0;
dprintk("RPC: %5u rpcb_getport already bound\n", task->tk_pid); dprintk("RPC: %5u %s: already bound\n",
task->tk_pid, __FUNCTION__);
goto bailout_nofree; goto bailout_nofree;
} }
if (rpcb_next_version[xprt->bind_index].rpc_proc == NULL) { if (rpcb_next_version[xprt->bind_index].rpc_proc == NULL) {
xprt->bind_index = 0; xprt->bind_index = 0;
status = -EACCES; /* tell caller to try again later */ status = -EACCES; /* tell caller to try again later */
dprintk("RPC: %5u rpcb_getport no more getport versions " dprintk("RPC: %5u %s: no more getport versions available\n",
"available\n", task->tk_pid); task->tk_pid, __FUNCTION__);
goto bailout_nofree; goto bailout_nofree;
} }
bind_version = rpcb_next_version[xprt->bind_index].rpc_vers; bind_version = rpcb_next_version[xprt->bind_index].rpc_vers;
dprintk("RPC: %5u rpcb_getport trying rpcbind version %u\n", dprintk("RPC: %5u %s: trying rpcbind version %u\n",
task->tk_pid, bind_version); task->tk_pid, __FUNCTION__, bind_version);
map = kzalloc(sizeof(struct rpcbind_args), GFP_ATOMIC); map = kzalloc(sizeof(struct rpcbind_args), GFP_ATOMIC);
if (!map) { if (!map) {
status = -ENOMEM; status = -ENOMEM;
dprintk("RPC: %5u rpcb_getport no memory available\n", dprintk("RPC: %5u %s: no memory available\n",
task->tk_pid); task->tk_pid, __FUNCTION__);
goto bailout_nofree; goto bailout_nofree;
} }
map->r_prog = clnt->cl_prog; map->r_prog = clnt->cl_prog;
@ -373,16 +377,17 @@ void rpcb_getport(struct rpc_task *task)
rpcb_clnt = rpcb_create(clnt->cl_server, &addr, xprt->prot, bind_version, 0); rpcb_clnt = rpcb_create(clnt->cl_server, &addr, xprt->prot, bind_version, 0);
if (IS_ERR(rpcb_clnt)) { if (IS_ERR(rpcb_clnt)) {
status = PTR_ERR(rpcb_clnt); status = PTR_ERR(rpcb_clnt);
dprintk("RPC: %5u rpcb_getport rpcb_create failed, error %ld\n", dprintk("RPC: %5u %s: rpcb_create failed, error %ld\n",
task->tk_pid, PTR_ERR(rpcb_clnt)); task->tk_pid, __FUNCTION__, PTR_ERR(rpcb_clnt));
goto bailout; goto bailout;
} }
child = rpc_run_task(rpcb_clnt, RPC_TASK_ASYNC, &rpcb_getport_ops, map); child = rpc_run_task(rpcb_clnt, RPC_TASK_ASYNC, &rpcb_getport_ops, map);
rpc_release_client(rpcb_clnt);
if (IS_ERR(child)) { if (IS_ERR(child)) {
status = -EIO; status = -EIO;
dprintk("RPC: %5u rpcb_getport rpc_run_task failed\n", dprintk("RPC: %5u %s: rpc_run_task failed\n",
task->tk_pid); task->tk_pid, __FUNCTION__);
goto bailout_nofree; goto bailout_nofree;
} }
rpc_put_task(child); rpc_put_task(child);

View File

@ -25,7 +25,6 @@
#ifdef RPC_DEBUG #ifdef RPC_DEBUG
#define RPCDBG_FACILITY RPCDBG_SCHED #define RPCDBG_FACILITY RPCDBG_SCHED
#define RPC_TASK_MAGIC_ID 0xf00baa #define RPC_TASK_MAGIC_ID 0xf00baa
static int rpc_task_id;
#endif #endif
/* /*
@ -40,7 +39,6 @@ static mempool_t *rpc_task_mempool __read_mostly;
static mempool_t *rpc_buffer_mempool __read_mostly; static mempool_t *rpc_buffer_mempool __read_mostly;
static void __rpc_default_timer(struct rpc_task *task); static void __rpc_default_timer(struct rpc_task *task);
static void rpciod_killall(void);
static void rpc_async_schedule(struct work_struct *); static void rpc_async_schedule(struct work_struct *);
static void rpc_release_task(struct rpc_task *task); static void rpc_release_task(struct rpc_task *task);
@ -49,23 +47,13 @@ static void rpc_release_task(struct rpc_task *task);
*/ */
static RPC_WAITQ(delay_queue, "delayq"); static RPC_WAITQ(delay_queue, "delayq");
/*
* All RPC tasks are linked into this list
*/
static LIST_HEAD(all_tasks);
/* /*
* rpciod-related stuff * rpciod-related stuff
*/ */
static DEFINE_MUTEX(rpciod_mutex); static DEFINE_MUTEX(rpciod_mutex);
static unsigned int rpciod_users; static atomic_t rpciod_users = ATOMIC_INIT(0);
struct workqueue_struct *rpciod_workqueue; struct workqueue_struct *rpciod_workqueue;
/*
* Spinlock for other critical sections of code.
*/
static DEFINE_SPINLOCK(rpc_sched_lock);
/* /*
* Disable the timer for a given RPC task. Should be called with * Disable the timer for a given RPC task. Should be called with
* queue->lock and bh_disabled in order to avoid races within * queue->lock and bh_disabled in order to avoid races within
@ -267,18 +255,33 @@ static int rpc_wait_bit_interruptible(void *word)
return 0; return 0;
} }
#ifdef RPC_DEBUG
static void rpc_task_set_debuginfo(struct rpc_task *task)
{
static atomic_t rpc_pid;
task->tk_magic = RPC_TASK_MAGIC_ID;
task->tk_pid = atomic_inc_return(&rpc_pid);
}
#else
static inline void rpc_task_set_debuginfo(struct rpc_task *task)
{
}
#endif
static void rpc_set_active(struct rpc_task *task) static void rpc_set_active(struct rpc_task *task)
{ {
struct rpc_clnt *clnt;
if (test_and_set_bit(RPC_TASK_ACTIVE, &task->tk_runstate) != 0) if (test_and_set_bit(RPC_TASK_ACTIVE, &task->tk_runstate) != 0)
return; return;
spin_lock(&rpc_sched_lock); rpc_task_set_debuginfo(task);
#ifdef RPC_DEBUG
task->tk_magic = RPC_TASK_MAGIC_ID;
task->tk_pid = rpc_task_id++;
#endif
/* Add to global list of all tasks */ /* Add to global list of all tasks */
list_add_tail(&task->tk_task, &all_tasks); clnt = task->tk_client;
spin_unlock(&rpc_sched_lock); if (clnt != NULL) {
spin_lock(&clnt->cl_lock);
list_add_tail(&task->tk_task, &clnt->cl_tasks);
spin_unlock(&clnt->cl_lock);
}
} }
/* /*
@ -818,6 +821,7 @@ void rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt, int flags, cons
if (tk_ops->rpc_call_prepare != NULL) if (tk_ops->rpc_call_prepare != NULL)
task->tk_action = rpc_prepare_task; task->tk_action = rpc_prepare_task;
task->tk_calldata = calldata; task->tk_calldata = calldata;
INIT_LIST_HEAD(&task->tk_task);
/* Initialize retry counters */ /* Initialize retry counters */
task->tk_garb_retry = 2; task->tk_garb_retry = 2;
@ -830,7 +834,7 @@ void rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt, int flags, cons
task->tk_workqueue = rpciod_workqueue; task->tk_workqueue = rpciod_workqueue;
if (clnt) { if (clnt) {
atomic_inc(&clnt->cl_users); kref_get(&clnt->cl_kref);
if (clnt->cl_softrtry) if (clnt->cl_softrtry)
task->tk_flags |= RPC_TASK_SOFT; task->tk_flags |= RPC_TASK_SOFT;
if (!clnt->cl_intr) if (!clnt->cl_intr)
@ -860,9 +864,7 @@ static void rpc_free_task(struct rcu_head *rcu)
} }
/* /*
* Create a new task for the specified client. We have to * Create a new task for the specified client.
* clean up after an allocation failure, as the client may
* have specified "oneshot".
*/ */
struct rpc_task *rpc_new_task(struct rpc_clnt *clnt, int flags, const struct rpc_call_ops *tk_ops, void *calldata) struct rpc_task *rpc_new_task(struct rpc_clnt *clnt, int flags, const struct rpc_call_ops *tk_ops, void *calldata)
{ {
@ -870,7 +872,7 @@ struct rpc_task *rpc_new_task(struct rpc_clnt *clnt, int flags, const struct rpc
task = rpc_alloc_task(); task = rpc_alloc_task();
if (!task) if (!task)
goto cleanup; goto out;
rpc_init_task(task, clnt, flags, tk_ops, calldata); rpc_init_task(task, clnt, flags, tk_ops, calldata);
@ -878,16 +880,6 @@ struct rpc_task *rpc_new_task(struct rpc_clnt *clnt, int flags, const struct rpc
task->tk_flags |= RPC_TASK_DYNAMIC; task->tk_flags |= RPC_TASK_DYNAMIC;
out: out:
return task; return task;
cleanup:
/* Check whether to release the client */
if (clnt) {
printk("rpc_new_task: failed, users=%d, oneshot=%d\n",
atomic_read(&clnt->cl_users), clnt->cl_oneshot);
atomic_inc(&clnt->cl_users); /* pretend we were used ... */
rpc_release_client(clnt);
}
goto out;
} }
@ -920,11 +912,13 @@ static void rpc_release_task(struct rpc_task *task)
#endif #endif
dprintk("RPC: %5u release task\n", task->tk_pid); dprintk("RPC: %5u release task\n", task->tk_pid);
/* Remove from global task list */ if (!list_empty(&task->tk_task)) {
spin_lock(&rpc_sched_lock); struct rpc_clnt *clnt = task->tk_client;
list_del(&task->tk_task); /* Remove from client task list */
spin_unlock(&rpc_sched_lock); spin_lock(&clnt->cl_lock);
list_del(&task->tk_task);
spin_unlock(&clnt->cl_lock);
}
BUG_ON (RPC_IS_QUEUED(task)); BUG_ON (RPC_IS_QUEUED(task));
/* Synchronously delete any running timer */ /* Synchronously delete any running timer */
@ -939,29 +933,6 @@ static void rpc_release_task(struct rpc_task *task)
rpc_put_task(task); rpc_put_task(task);
} }
/**
* rpc_run_task - Allocate a new RPC task, then run rpc_execute against it
* @clnt: pointer to RPC client
* @flags: RPC flags
* @ops: RPC call ops
* @data: user call data
*/
struct rpc_task *rpc_run_task(struct rpc_clnt *clnt, int flags,
const struct rpc_call_ops *ops,
void *data)
{
struct rpc_task *task;
task = rpc_new_task(clnt, flags, ops, data);
if (task == NULL) {
rpc_release_calldata(ops, data);
return ERR_PTR(-ENOMEM);
}
atomic_inc(&task->tk_count);
rpc_execute(task);
return task;
}
EXPORT_SYMBOL(rpc_run_task);
/* /*
* Kill all tasks for the given client. * Kill all tasks for the given client.
* XXX: kill their descendants as well? * XXX: kill their descendants as well?
@ -969,44 +940,25 @@ EXPORT_SYMBOL(rpc_run_task);
void rpc_killall_tasks(struct rpc_clnt *clnt) void rpc_killall_tasks(struct rpc_clnt *clnt)
{ {
struct rpc_task *rovr; struct rpc_task *rovr;
struct list_head *le;
if (list_empty(&clnt->cl_tasks))
return;
dprintk("RPC: killing all tasks for client %p\n", clnt); dprintk("RPC: killing all tasks for client %p\n", clnt);
/* /*
* Spin lock all_tasks to prevent changes... * Spin lock all_tasks to prevent changes...
*/ */
spin_lock(&rpc_sched_lock); spin_lock(&clnt->cl_lock);
alltask_for_each(rovr, le, &all_tasks) { list_for_each_entry(rovr, &clnt->cl_tasks, tk_task) {
if (! RPC_IS_ACTIVATED(rovr)) if (! RPC_IS_ACTIVATED(rovr))
continue; continue;
if (!clnt || rovr->tk_client == clnt) { if (!(rovr->tk_flags & RPC_TASK_KILLED)) {
rovr->tk_flags |= RPC_TASK_KILLED; rovr->tk_flags |= RPC_TASK_KILLED;
rpc_exit(rovr, -EIO); rpc_exit(rovr, -EIO);
rpc_wake_up_task(rovr); rpc_wake_up_task(rovr);
} }
} }
spin_unlock(&rpc_sched_lock); spin_unlock(&clnt->cl_lock);
}
static void rpciod_killall(void)
{
unsigned long flags;
while (!list_empty(&all_tasks)) {
clear_thread_flag(TIF_SIGPENDING);
rpc_killall_tasks(NULL);
flush_workqueue(rpciod_workqueue);
if (!list_empty(&all_tasks)) {
dprintk("RPC: rpciod_killall: waiting for tasks "
"to exit\n");
yield();
}
}
spin_lock_irqsave(&current->sighand->siglock, flags);
recalc_sigpending();
spin_unlock_irqrestore(&current->sighand->siglock, flags);
} }
/* /*
@ -1018,28 +970,27 @@ rpciod_up(void)
struct workqueue_struct *wq; struct workqueue_struct *wq;
int error = 0; int error = 0;
if (atomic_inc_not_zero(&rpciod_users))
return 0;
mutex_lock(&rpciod_mutex); mutex_lock(&rpciod_mutex);
dprintk("RPC: rpciod_up: users %u\n", rpciod_users);
rpciod_users++; /* Guard against races with rpciod_down() */
if (rpciod_workqueue) if (rpciod_workqueue != NULL)
goto out; goto out_ok;
/*
* If there's no pid, we should be the first user.
*/
if (rpciod_users > 1)
printk(KERN_WARNING "rpciod_up: no workqueue, %u users??\n", rpciod_users);
/* /*
* Create the rpciod thread and wait for it to start. * Create the rpciod thread and wait for it to start.
*/ */
dprintk("RPC: creating workqueue rpciod\n");
error = -ENOMEM; error = -ENOMEM;
wq = create_workqueue("rpciod"); wq = create_workqueue("rpciod");
if (wq == NULL) { if (wq == NULL)
printk(KERN_WARNING "rpciod_up: create workqueue failed, error=%d\n", error);
rpciod_users--;
goto out; goto out;
}
rpciod_workqueue = wq; rpciod_workqueue = wq;
error = 0; error = 0;
out_ok:
atomic_inc(&rpciod_users);
out: out:
mutex_unlock(&rpciod_mutex); mutex_unlock(&rpciod_mutex);
return error; return error;
@ -1048,59 +999,19 @@ out:
void void
rpciod_down(void) rpciod_down(void)
{ {
if (!atomic_dec_and_test(&rpciod_users))
return;
mutex_lock(&rpciod_mutex); mutex_lock(&rpciod_mutex);
dprintk("RPC: rpciod_down sema %u\n", rpciod_users); dprintk("RPC: destroying workqueue rpciod\n");
if (rpciod_users) {
if (--rpciod_users)
goto out;
} else
printk(KERN_WARNING "rpciod_down: no users??\n");
if (!rpciod_workqueue) { if (atomic_read(&rpciod_users) == 0 && rpciod_workqueue != NULL) {
dprintk("RPC: rpciod_down: Nothing to do!\n"); destroy_workqueue(rpciod_workqueue);
goto out; rpciod_workqueue = NULL;
} }
rpciod_killall();
destroy_workqueue(rpciod_workqueue);
rpciod_workqueue = NULL;
out:
mutex_unlock(&rpciod_mutex); mutex_unlock(&rpciod_mutex);
} }
#ifdef RPC_DEBUG
void rpc_show_tasks(void)
{
struct list_head *le;
struct rpc_task *t;
spin_lock(&rpc_sched_lock);
if (list_empty(&all_tasks)) {
spin_unlock(&rpc_sched_lock);
return;
}
printk("-pid- proc flgs status -client- -prog- --rqstp- -timeout "
"-rpcwait -action- ---ops--\n");
alltask_for_each(t, le, &all_tasks) {
const char *rpc_waitq = "none";
if (RPC_IS_QUEUED(t))
rpc_waitq = rpc_qname(t->u.tk_wait.rpc_waitq);
printk("%5u %04d %04x %6d %8p %6d %8p %8ld %8s %8p %8p\n",
t->tk_pid,
(t->tk_msg.rpc_proc ? t->tk_msg.rpc_proc->p_proc : -1),
t->tk_flags, t->tk_status,
t->tk_client,
(t->tk_client ? t->tk_client->cl_prog : 0),
t->tk_rqstp, t->tk_timeout,
rpc_waitq,
t->tk_action, t->tk_ops);
}
spin_unlock(&rpc_sched_lock);
}
#endif
void void
rpc_destroy_mempool(void) rpc_destroy_mempool(void)
{ {

View File

@ -28,15 +28,11 @@ EXPORT_SYMBOL(rpc_init_task);
EXPORT_SYMBOL(rpc_sleep_on); EXPORT_SYMBOL(rpc_sleep_on);
EXPORT_SYMBOL(rpc_wake_up_next); EXPORT_SYMBOL(rpc_wake_up_next);
EXPORT_SYMBOL(rpc_wake_up_task); EXPORT_SYMBOL(rpc_wake_up_task);
EXPORT_SYMBOL(rpciod_down);
EXPORT_SYMBOL(rpciod_up);
EXPORT_SYMBOL(rpc_new_task);
EXPORT_SYMBOL(rpc_wake_up_status); EXPORT_SYMBOL(rpc_wake_up_status);
/* RPC client functions */ /* RPC client functions */
EXPORT_SYMBOL(rpc_clone_client); EXPORT_SYMBOL(rpc_clone_client);
EXPORT_SYMBOL(rpc_bind_new_program); EXPORT_SYMBOL(rpc_bind_new_program);
EXPORT_SYMBOL(rpc_destroy_client);
EXPORT_SYMBOL(rpc_shutdown_client); EXPORT_SYMBOL(rpc_shutdown_client);
EXPORT_SYMBOL(rpc_killall_tasks); EXPORT_SYMBOL(rpc_killall_tasks);
EXPORT_SYMBOL(rpc_call_sync); EXPORT_SYMBOL(rpc_call_sync);
@ -61,7 +57,7 @@ EXPORT_SYMBOL(rpcauth_unregister);
EXPORT_SYMBOL(rpcauth_create); EXPORT_SYMBOL(rpcauth_create);
EXPORT_SYMBOL(rpcauth_lookupcred); EXPORT_SYMBOL(rpcauth_lookupcred);
EXPORT_SYMBOL(rpcauth_lookup_credcache); EXPORT_SYMBOL(rpcauth_lookup_credcache);
EXPORT_SYMBOL(rpcauth_free_credcache); EXPORT_SYMBOL(rpcauth_destroy_credcache);
EXPORT_SYMBOL(rpcauth_init_credcache); EXPORT_SYMBOL(rpcauth_init_credcache);
EXPORT_SYMBOL(put_rpccred); EXPORT_SYMBOL(put_rpccred);
@ -156,6 +152,7 @@ init_sunrpc(void)
cache_register(&ip_map_cache); cache_register(&ip_map_cache);
cache_register(&unix_gid_cache); cache_register(&unix_gid_cache);
init_socket_xprt(); init_socket_xprt();
rpcauth_init_module();
out: out:
return err; return err;
} }
@ -163,6 +160,7 @@ out:
static void __exit static void __exit
cleanup_sunrpc(void) cleanup_sunrpc(void)
{ {
rpcauth_remove_module();
cleanup_socket_xprt(); cleanup_socket_xprt();
unregister_rpc_pipefs(); unregister_rpc_pipefs();
rpc_destroy_mempool(); rpc_destroy_mempool();

View File

@ -644,6 +644,7 @@ svc_recvfrom(struct svc_rqst *rqstp, struct kvec *iov, int nr, int buflen)
struct msghdr msg = { struct msghdr msg = {
.msg_flags = MSG_DONTWAIT, .msg_flags = MSG_DONTWAIT,
}; };
struct sockaddr *sin;
int len; int len;
len = kernel_recvmsg(svsk->sk_sock, &msg, iov, nr, buflen, len = kernel_recvmsg(svsk->sk_sock, &msg, iov, nr, buflen,
@ -654,6 +655,19 @@ svc_recvfrom(struct svc_rqst *rqstp, struct kvec *iov, int nr, int buflen)
memcpy(&rqstp->rq_addr, &svsk->sk_remote, svsk->sk_remotelen); memcpy(&rqstp->rq_addr, &svsk->sk_remote, svsk->sk_remotelen);
rqstp->rq_addrlen = svsk->sk_remotelen; rqstp->rq_addrlen = svsk->sk_remotelen;
/* Destination address in request is needed for binding the
* source address in RPC callbacks later.
*/
sin = (struct sockaddr *)&svsk->sk_local;
switch (sin->sa_family) {
case AF_INET:
rqstp->rq_daddr.addr = ((struct sockaddr_in *)sin)->sin_addr;
break;
case AF_INET6:
rqstp->rq_daddr.addr6 = ((struct sockaddr_in6 *)sin)->sin6_addr;
break;
}
dprintk("svc: socket %p recvfrom(%p, %Zu) = %d\n", dprintk("svc: socket %p recvfrom(%p, %Zu) = %d\n",
svsk, iov[0].iov_base, iov[0].iov_len, len); svsk, iov[0].iov_base, iov[0].iov_len, len);
@ -1064,6 +1078,12 @@ svc_tcp_accept(struct svc_sock *svsk)
goto failed; goto failed;
memcpy(&newsvsk->sk_remote, sin, slen); memcpy(&newsvsk->sk_remote, sin, slen);
newsvsk->sk_remotelen = slen; newsvsk->sk_remotelen = slen;
err = kernel_getsockname(newsock, sin, &slen);
if (unlikely(err < 0)) {
dprintk("svc_tcp_accept: kernel_getsockname error %d\n", -err);
slen = offsetof(struct sockaddr, sa_data);
}
memcpy(&newsvsk->sk_local, sin, slen);
svc_sock_received(newsvsk); svc_sock_received(newsvsk);

View File

@ -127,7 +127,7 @@ static void xprt_clear_locked(struct rpc_xprt *xprt)
clear_bit(XPRT_LOCKED, &xprt->state); clear_bit(XPRT_LOCKED, &xprt->state);
smp_mb__after_clear_bit(); smp_mb__after_clear_bit();
} else } else
schedule_work(&xprt->task_cleanup); queue_work(rpciod_workqueue, &xprt->task_cleanup);
} }
/* /*
@ -515,7 +515,7 @@ xprt_init_autodisconnect(unsigned long data)
if (xprt_connecting(xprt)) if (xprt_connecting(xprt))
xprt_release_write(xprt, NULL); xprt_release_write(xprt, NULL);
else else
schedule_work(&xprt->task_cleanup); queue_work(rpciod_workqueue, &xprt->task_cleanup);
return; return;
out_abort: out_abort:
spin_unlock(&xprt->transport_lock); spin_unlock(&xprt->transport_lock);
@ -886,27 +886,24 @@ void xprt_set_timeout(struct rpc_timeout *to, unsigned int retr, unsigned long i
/** /**
* xprt_create_transport - create an RPC transport * xprt_create_transport - create an RPC transport
* @proto: requested transport protocol * @args: rpc transport creation arguments
* @ap: remote peer address
* @size: length of address
* @to: timeout parameters
* *
*/ */
struct rpc_xprt *xprt_create_transport(int proto, struct sockaddr *ap, size_t size, struct rpc_timeout *to) struct rpc_xprt *xprt_create_transport(struct rpc_xprtsock_create *args)
{ {
struct rpc_xprt *xprt; struct rpc_xprt *xprt;
struct rpc_rqst *req; struct rpc_rqst *req;
switch (proto) { switch (args->proto) {
case IPPROTO_UDP: case IPPROTO_UDP:
xprt = xs_setup_udp(ap, size, to); xprt = xs_setup_udp(args);
break; break;
case IPPROTO_TCP: case IPPROTO_TCP:
xprt = xs_setup_tcp(ap, size, to); xprt = xs_setup_tcp(args);
break; break;
default: default:
printk(KERN_ERR "RPC: unrecognized transport protocol: %d\n", printk(KERN_ERR "RPC: unrecognized transport protocol: %d\n",
proto); args->proto);
return ERR_PTR(-EIO); return ERR_PTR(-EIO);
} }
if (IS_ERR(xprt)) { if (IS_ERR(xprt)) {

View File

@ -235,6 +235,7 @@ struct sock_xprt {
* Connection of transports * Connection of transports
*/ */
struct delayed_work connect_worker; struct delayed_work connect_worker;
struct sockaddr_storage addr;
unsigned short port; unsigned short port;
/* /*
@ -653,8 +654,7 @@ static void xs_destroy(struct rpc_xprt *xprt)
dprintk("RPC: xs_destroy xprt %p\n", xprt); dprintk("RPC: xs_destroy xprt %p\n", xprt);
cancel_delayed_work(&transport->connect_worker); cancel_rearming_delayed_work(&transport->connect_worker);
flush_scheduled_work();
xprt_disconnect(xprt); xprt_disconnect(xprt);
xs_close(xprt); xs_close(xprt);
@ -1001,7 +1001,7 @@ static void xs_tcp_state_change(struct sock *sk)
/* Try to schedule an autoclose RPC calls */ /* Try to schedule an autoclose RPC calls */
set_bit(XPRT_CLOSE_WAIT, &xprt->state); set_bit(XPRT_CLOSE_WAIT, &xprt->state);
if (test_and_set_bit(XPRT_LOCKED, &xprt->state) == 0) if (test_and_set_bit(XPRT_LOCKED, &xprt->state) == 0)
schedule_work(&xprt->task_cleanup); queue_work(rpciod_workqueue, &xprt->task_cleanup);
default: default:
xprt_disconnect(xprt); xprt_disconnect(xprt);
} }
@ -1146,31 +1146,36 @@ static void xs_set_port(struct rpc_xprt *xprt, unsigned short port)
sap->sin_port = htons(port); sap->sin_port = htons(port);
} }
static int xs_bindresvport(struct sock_xprt *transport, struct socket *sock) static int xs_bind(struct sock_xprt *transport, struct socket *sock)
{ {
struct sockaddr_in myaddr = { struct sockaddr_in myaddr = {
.sin_family = AF_INET, .sin_family = AF_INET,
}; };
struct sockaddr_in *sa;
int err; int err;
unsigned short port = transport->port; unsigned short port = transport->port;
if (!transport->xprt.resvport)
port = 0;
sa = (struct sockaddr_in *)&transport->addr;
myaddr.sin_addr = sa->sin_addr;
do { do {
myaddr.sin_port = htons(port); myaddr.sin_port = htons(port);
err = kernel_bind(sock, (struct sockaddr *) &myaddr, err = kernel_bind(sock, (struct sockaddr *) &myaddr,
sizeof(myaddr)); sizeof(myaddr));
if (!transport->xprt.resvport)
break;
if (err == 0) { if (err == 0) {
transport->port = port; transport->port = port;
dprintk("RPC: xs_bindresvport bound to port %u\n", break;
port);
return 0;
} }
if (port <= xprt_min_resvport) if (port <= xprt_min_resvport)
port = xprt_max_resvport; port = xprt_max_resvport;
else else
port--; port--;
} while (err == -EADDRINUSE && port != transport->port); } while (err == -EADDRINUSE && port != transport->port);
dprintk("RPC: xs_bind "NIPQUAD_FMT":%u: %s (%d)\n",
dprintk("RPC: can't bind to reserved port (%d).\n", -err); NIPQUAD(myaddr.sin_addr), port, err ? "failed" : "ok", err);
return err; return err;
} }
@ -1229,7 +1234,7 @@ static void xs_udp_connect_worker(struct work_struct *work)
} }
xs_reclassify_socket(sock); xs_reclassify_socket(sock);
if (xprt->resvport && xs_bindresvport(transport, sock) < 0) { if (xs_bind(transport, sock)) {
sock_release(sock); sock_release(sock);
goto out; goto out;
} }
@ -1316,7 +1321,7 @@ static void xs_tcp_connect_worker(struct work_struct *work)
} }
xs_reclassify_socket(sock); xs_reclassify_socket(sock);
if (xprt->resvport && xs_bindresvport(transport, sock) < 0) { if (xs_bind(transport, sock)) {
sock_release(sock); sock_release(sock);
goto out; goto out;
} }
@ -1410,18 +1415,16 @@ static void xs_connect(struct rpc_task *task)
dprintk("RPC: xs_connect delayed xprt %p for %lu " dprintk("RPC: xs_connect delayed xprt %p for %lu "
"seconds\n", "seconds\n",
xprt, xprt->reestablish_timeout / HZ); xprt, xprt->reestablish_timeout / HZ);
schedule_delayed_work(&transport->connect_worker, queue_delayed_work(rpciod_workqueue,
xprt->reestablish_timeout); &transport->connect_worker,
xprt->reestablish_timeout);
xprt->reestablish_timeout <<= 1; xprt->reestablish_timeout <<= 1;
if (xprt->reestablish_timeout > XS_TCP_MAX_REEST_TO) if (xprt->reestablish_timeout > XS_TCP_MAX_REEST_TO)
xprt->reestablish_timeout = XS_TCP_MAX_REEST_TO; xprt->reestablish_timeout = XS_TCP_MAX_REEST_TO;
} else { } else {
dprintk("RPC: xs_connect scheduled xprt %p\n", xprt); dprintk("RPC: xs_connect scheduled xprt %p\n", xprt);
schedule_delayed_work(&transport->connect_worker, 0); queue_delayed_work(rpciod_workqueue,
&transport->connect_worker, 0);
/* flush_scheduled_work can sleep... */
if (!RPC_IS_ASYNC(task))
flush_scheduled_work();
} }
} }
@ -1476,7 +1479,7 @@ static struct rpc_xprt_ops xs_udp_ops = {
.set_buffer_size = xs_udp_set_buffer_size, .set_buffer_size = xs_udp_set_buffer_size,
.reserve_xprt = xprt_reserve_xprt_cong, .reserve_xprt = xprt_reserve_xprt_cong,
.release_xprt = xprt_release_xprt_cong, .release_xprt = xprt_release_xprt_cong,
.rpcbind = rpcb_getport, .rpcbind = rpcb_getport_async,
.set_port = xs_set_port, .set_port = xs_set_port,
.connect = xs_connect, .connect = xs_connect,
.buf_alloc = rpc_malloc, .buf_alloc = rpc_malloc,
@ -1493,7 +1496,7 @@ static struct rpc_xprt_ops xs_udp_ops = {
static struct rpc_xprt_ops xs_tcp_ops = { static struct rpc_xprt_ops xs_tcp_ops = {
.reserve_xprt = xprt_reserve_xprt, .reserve_xprt = xprt_reserve_xprt,
.release_xprt = xs_tcp_release_xprt, .release_xprt = xs_tcp_release_xprt,
.rpcbind = rpcb_getport, .rpcbind = rpcb_getport_async,
.set_port = xs_set_port, .set_port = xs_set_port,
.connect = xs_connect, .connect = xs_connect,
.buf_alloc = rpc_malloc, .buf_alloc = rpc_malloc,
@ -1505,12 +1508,12 @@ static struct rpc_xprt_ops xs_tcp_ops = {
.print_stats = xs_tcp_print_stats, .print_stats = xs_tcp_print_stats,
}; };
static struct rpc_xprt *xs_setup_xprt(struct sockaddr *addr, size_t addrlen, unsigned int slot_table_size) static struct rpc_xprt *xs_setup_xprt(struct rpc_xprtsock_create *args, unsigned int slot_table_size)
{ {
struct rpc_xprt *xprt; struct rpc_xprt *xprt;
struct sock_xprt *new; struct sock_xprt *new;
if (addrlen > sizeof(xprt->addr)) { if (args->addrlen > sizeof(xprt->addr)) {
dprintk("RPC: xs_setup_xprt: address too large\n"); dprintk("RPC: xs_setup_xprt: address too large\n");
return ERR_PTR(-EBADF); return ERR_PTR(-EBADF);
} }
@ -1532,8 +1535,10 @@ static struct rpc_xprt *xs_setup_xprt(struct sockaddr *addr, size_t addrlen, uns
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
memcpy(&xprt->addr, addr, addrlen); memcpy(&xprt->addr, args->dstaddr, args->addrlen);
xprt->addrlen = addrlen; xprt->addrlen = args->addrlen;
if (args->srcaddr)
memcpy(&new->addr, args->srcaddr, args->addrlen);
new->port = xs_get_random_port(); new->port = xs_get_random_port();
return xprt; return xprt;
@ -1541,22 +1546,20 @@ static struct rpc_xprt *xs_setup_xprt(struct sockaddr *addr, size_t addrlen, uns
/** /**
* xs_setup_udp - Set up transport to use a UDP socket * xs_setup_udp - Set up transport to use a UDP socket
* @addr: address of remote server * @args: rpc transport creation arguments
* @addrlen: length of address in bytes
* @to: timeout parameters
* *
*/ */
struct rpc_xprt *xs_setup_udp(struct sockaddr *addr, size_t addrlen, struct rpc_timeout *to) struct rpc_xprt *xs_setup_udp(struct rpc_xprtsock_create *args)
{ {
struct rpc_xprt *xprt; struct rpc_xprt *xprt;
struct sock_xprt *transport; struct sock_xprt *transport;
xprt = xs_setup_xprt(addr, addrlen, xprt_udp_slot_table_entries); xprt = xs_setup_xprt(args, xprt_udp_slot_table_entries);
if (IS_ERR(xprt)) if (IS_ERR(xprt))
return xprt; return xprt;
transport = container_of(xprt, struct sock_xprt, xprt); transport = container_of(xprt, struct sock_xprt, xprt);
if (ntohs(((struct sockaddr_in *)addr)->sin_port) != 0) if (ntohs(((struct sockaddr_in *)args->dstaddr)->sin_port) != 0)
xprt_set_bound(xprt); xprt_set_bound(xprt);
xprt->prot = IPPROTO_UDP; xprt->prot = IPPROTO_UDP;
@ -1572,8 +1575,8 @@ struct rpc_xprt *xs_setup_udp(struct sockaddr *addr, size_t addrlen, struct rpc_
xprt->ops = &xs_udp_ops; xprt->ops = &xs_udp_ops;
if (to) if (args->timeout)
xprt->timeout = *to; xprt->timeout = *args->timeout;
else else
xprt_set_timeout(&xprt->timeout, 5, 5 * HZ); xprt_set_timeout(&xprt->timeout, 5, 5 * HZ);
@ -1586,22 +1589,20 @@ struct rpc_xprt *xs_setup_udp(struct sockaddr *addr, size_t addrlen, struct rpc_
/** /**
* xs_setup_tcp - Set up transport to use a TCP socket * xs_setup_tcp - Set up transport to use a TCP socket
* @addr: address of remote server * @args: rpc transport creation arguments
* @addrlen: length of address in bytes
* @to: timeout parameters
* *
*/ */
struct rpc_xprt *xs_setup_tcp(struct sockaddr *addr, size_t addrlen, struct rpc_timeout *to) struct rpc_xprt *xs_setup_tcp(struct rpc_xprtsock_create *args)
{ {
struct rpc_xprt *xprt; struct rpc_xprt *xprt;
struct sock_xprt *transport; struct sock_xprt *transport;
xprt = xs_setup_xprt(addr, addrlen, xprt_tcp_slot_table_entries); xprt = xs_setup_xprt(args, xprt_tcp_slot_table_entries);
if (IS_ERR(xprt)) if (IS_ERR(xprt))
return xprt; return xprt;
transport = container_of(xprt, struct sock_xprt, xprt); transport = container_of(xprt, struct sock_xprt, xprt);
if (ntohs(((struct sockaddr_in *)addr)->sin_port) != 0) if (ntohs(((struct sockaddr_in *)args->dstaddr)->sin_port) != 0)
xprt_set_bound(xprt); xprt_set_bound(xprt);
xprt->prot = IPPROTO_TCP; xprt->prot = IPPROTO_TCP;
@ -1616,8 +1617,8 @@ struct rpc_xprt *xs_setup_tcp(struct sockaddr *addr, size_t addrlen, struct rpc_
xprt->ops = &xs_tcp_ops; xprt->ops = &xs_tcp_ops;
if (to) if (args->timeout)
xprt->timeout = *to; xprt->timeout = *args->timeout;
else else
xprt_set_timeout(&xprt->timeout, 2, 60 * HZ); xprt_set_timeout(&xprt->timeout, 2, 60 * HZ);