mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 04:02:20 +00:00
nfs: simplify and guarantee owner uniqueness.
I have evidence of an Linux NFS client getting NFS4ERR_BAD_SEQID to a v4.0 LOCK request to a Linux server (which had fixed the problem with RELEASE_LOCKOWNER bug fixed). The LOCK request presented a "new" lock owner so there are two seq ids in the request: that for the open file, and that for the new lock. Given the context I am confident that the new lock owner was reported to have the wrong seqid. As lock owner identifiers are reused, the server must still have a lock owner active which the client thinks is no longer active. I wasn't able to determine a root-cause but the simplest fix seems to be to ensure lock owners are always unique much as open owners are (thanks to a time stamp). The easiest way to ensure uniqueness is with a 64bit counter for each server. That will never cycle (if updated once a nanosecond the last 584 years. A single NFS server would not handle open/lock requests nearly that fast, and a Linux node is unlikely to have an uptime approaching that). This patch removes the 2 ida and instead uses a per-server atomic64_t to provide uniqueness. Note that the lock owner already encodes the id as 64 bits even though it is a 32bit value. So changing to a 64bit value does not change the encoding of the lock owner. The open owner encoding is now 4 bytes larger. Signed-off-by: NeilBrown <neilb@suse.de> Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
This commit is contained in:
parent
8f6a7c9467
commit
d98f722725
@ -997,8 +997,8 @@ struct nfs_server *nfs_alloc_server(void)
|
|||||||
init_waitqueue_head(&server->write_congestion_wait);
|
init_waitqueue_head(&server->write_congestion_wait);
|
||||||
atomic_long_set(&server->writeback, 0);
|
atomic_long_set(&server->writeback, 0);
|
||||||
|
|
||||||
ida_init(&server->openowner_id);
|
atomic64_set(&server->owner_ctr, 0);
|
||||||
ida_init(&server->lockowner_id);
|
|
||||||
pnfs_init_server(server);
|
pnfs_init_server(server);
|
||||||
rpc_init_wait_queue(&server->uoc_rpcwaitq, "NFS UOC");
|
rpc_init_wait_queue(&server->uoc_rpcwaitq, "NFS UOC");
|
||||||
|
|
||||||
@ -1037,8 +1037,6 @@ void nfs_free_server(struct nfs_server *server)
|
|||||||
}
|
}
|
||||||
ida_free(&s_sysfs_ids, server->s_sysfs_id);
|
ida_free(&s_sysfs_ids, server->s_sysfs_id);
|
||||||
|
|
||||||
ida_destroy(&server->lockowner_id);
|
|
||||||
ida_destroy(&server->openowner_id);
|
|
||||||
put_cred(server->cred);
|
put_cred(server->cred);
|
||||||
nfs_release_automount_timer();
|
nfs_release_automount_timer();
|
||||||
call_rcu(&server->rcu, delayed_free);
|
call_rcu(&server->rcu, delayed_free);
|
||||||
|
@ -83,7 +83,7 @@ struct nfs4_minor_version_ops {
|
|||||||
#define NFS_SEQID_CONFIRMED 1
|
#define NFS_SEQID_CONFIRMED 1
|
||||||
struct nfs_seqid_counter {
|
struct nfs_seqid_counter {
|
||||||
ktime_t create_time;
|
ktime_t create_time;
|
||||||
int owner_id;
|
u64 owner_id;
|
||||||
int flags;
|
int flags;
|
||||||
u32 counter;
|
u32 counter;
|
||||||
spinlock_t lock; /* Protects the list */
|
spinlock_t lock; /* Protects the list */
|
||||||
|
@ -501,11 +501,7 @@ nfs4_alloc_state_owner(struct nfs_server *server,
|
|||||||
sp = kzalloc(sizeof(*sp), gfp_flags);
|
sp = kzalloc(sizeof(*sp), gfp_flags);
|
||||||
if (!sp)
|
if (!sp)
|
||||||
return NULL;
|
return NULL;
|
||||||
sp->so_seqid.owner_id = ida_alloc(&server->openowner_id, gfp_flags);
|
sp->so_seqid.owner_id = atomic64_inc_return(&server->owner_ctr);
|
||||||
if (sp->so_seqid.owner_id < 0) {
|
|
||||||
kfree(sp);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
sp->so_server = server;
|
sp->so_server = server;
|
||||||
sp->so_cred = get_cred(cred);
|
sp->so_cred = get_cred(cred);
|
||||||
spin_lock_init(&sp->so_lock);
|
spin_lock_init(&sp->so_lock);
|
||||||
@ -536,7 +532,6 @@ static void nfs4_free_state_owner(struct nfs4_state_owner *sp)
|
|||||||
{
|
{
|
||||||
nfs4_destroy_seqid_counter(&sp->so_seqid);
|
nfs4_destroy_seqid_counter(&sp->so_seqid);
|
||||||
put_cred(sp->so_cred);
|
put_cred(sp->so_cred);
|
||||||
ida_free(&sp->so_server->openowner_id, sp->so_seqid.owner_id);
|
|
||||||
kfree(sp);
|
kfree(sp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -879,19 +874,13 @@ static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, f
|
|||||||
refcount_set(&lsp->ls_count, 1);
|
refcount_set(&lsp->ls_count, 1);
|
||||||
lsp->ls_state = state;
|
lsp->ls_state = state;
|
||||||
lsp->ls_owner = owner;
|
lsp->ls_owner = owner;
|
||||||
lsp->ls_seqid.owner_id = ida_alloc(&server->lockowner_id, GFP_KERNEL_ACCOUNT);
|
lsp->ls_seqid.owner_id = atomic64_inc_return(&server->owner_ctr);
|
||||||
if (lsp->ls_seqid.owner_id < 0)
|
|
||||||
goto out_free;
|
|
||||||
INIT_LIST_HEAD(&lsp->ls_locks);
|
INIT_LIST_HEAD(&lsp->ls_locks);
|
||||||
return lsp;
|
return lsp;
|
||||||
out_free:
|
|
||||||
kfree(lsp);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nfs4_free_lock_state(struct nfs_server *server, struct nfs4_lock_state *lsp)
|
void nfs4_free_lock_state(struct nfs_server *server, struct nfs4_lock_state *lsp)
|
||||||
{
|
{
|
||||||
ida_free(&server->lockowner_id, lsp->ls_seqid.owner_id);
|
|
||||||
nfs4_destroy_seqid_counter(&lsp->ls_seqid);
|
nfs4_destroy_seqid_counter(&lsp->ls_seqid);
|
||||||
kfree(lsp);
|
kfree(lsp);
|
||||||
}
|
}
|
||||||
|
@ -1424,12 +1424,12 @@ static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_opena
|
|||||||
*/
|
*/
|
||||||
encode_nfs4_seqid(xdr, arg->seqid);
|
encode_nfs4_seqid(xdr, arg->seqid);
|
||||||
encode_share_access(xdr, arg->share_access);
|
encode_share_access(xdr, arg->share_access);
|
||||||
p = reserve_space(xdr, 36);
|
p = reserve_space(xdr, 40);
|
||||||
p = xdr_encode_hyper(p, arg->clientid);
|
p = xdr_encode_hyper(p, arg->clientid);
|
||||||
*p++ = cpu_to_be32(24);
|
*p++ = cpu_to_be32(28);
|
||||||
p = xdr_encode_opaque_fixed(p, "open id:", 8);
|
p = xdr_encode_opaque_fixed(p, "open id:", 8);
|
||||||
*p++ = cpu_to_be32(arg->server->s_dev);
|
*p++ = cpu_to_be32(arg->server->s_dev);
|
||||||
*p++ = cpu_to_be32(arg->id.uniquifier);
|
p = xdr_encode_hyper(p, arg->id.uniquifier);
|
||||||
xdr_encode_hyper(p, arg->id.create_time);
|
xdr_encode_hyper(p, arg->id.create_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,8 +234,7 @@ struct nfs_server {
|
|||||||
/* the following fields are protected by nfs_client->cl_lock */
|
/* the following fields are protected by nfs_client->cl_lock */
|
||||||
struct rb_root state_owners;
|
struct rb_root state_owners;
|
||||||
#endif
|
#endif
|
||||||
struct ida openowner_id;
|
atomic64_t owner_ctr;
|
||||||
struct ida lockowner_id;
|
|
||||||
struct list_head state_owners_lru;
|
struct list_head state_owners_lru;
|
||||||
struct list_head layouts;
|
struct list_head layouts;
|
||||||
struct list_head delegations;
|
struct list_head delegations;
|
||||||
|
@ -446,7 +446,7 @@ struct nfs42_clone_res {
|
|||||||
|
|
||||||
struct stateowner_id {
|
struct stateowner_id {
|
||||||
__u64 create_time;
|
__u64 create_time;
|
||||||
__u32 uniquifier;
|
__u64 uniquifier;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct nfs4_open_delegation {
|
struct nfs4_open_delegation {
|
||||||
|
Loading…
Reference in New Issue
Block a user