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:
NeilBrown 2024-09-06 12:32:03 +10:00 committed by Anna Schumaker
parent 8f6a7c9467
commit d98f722725
6 changed files with 10 additions and 24 deletions

View File

@ -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);

View File

@ -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 */

View File

@ -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);
} }

View File

@ -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);
} }

View File

@ -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;

View File

@ -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 {