Skip to content

Commit 91d2e9b

Browse files
committed
NFSD: Clean up the nfsd_net::nfssvc_boot field
There are two boot-time fields in struct nfsd_net: one called boot_time and one called nfssvc_boot. The latter is used only to form write verifiers, but its documenting comment declares: /* Time of server startup */ Since commit 27c438f ("nfsd: Support the server resetting the boot verifier"), this field can be reset at any time; it's no longer tied to server restart. So that comment is stale. Also, according to pahole, struct timespec64 is 16 bytes long on x86_64. The nfssvc_boot field is used only to form a write verifier, which is 8 bytes long. Let's clarify this situation by manufacturing an 8-byte verifier in nfs_reset_boot_verifier() and storing only that in struct nfsd_net. We're grabbing 128 bits of time, so compress all of those into a 64-bit verifier instead of throwing out the high-order bits. In the future, the siphash_key can be re-used for other hashed objects per-nfsd_net. Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
1 parent cdc5566 commit 91d2e9b

3 files changed

Lines changed: 45 additions & 17 deletions

File tree

fs/nfsd/netns.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <net/net_namespace.h>
1212
#include <net/netns/generic.h>
1313
#include <linux/percpu_counter.h>
14+
#include <linux/siphash.h>
1415

1516
/* Hash tables for nfs4_clientid state */
1617
#define CLIENT_HASH_BITS 4
@@ -108,9 +109,8 @@ struct nfsd_net {
108109
bool nfsd_net_up;
109110
bool lockd_up;
110111

111-
/* Time of server startup */
112-
struct timespec64 nfssvc_boot;
113-
seqlock_t boot_lock;
112+
seqlock_t writeverf_lock;
113+
unsigned char writeverf[8];
114114

115115
/*
116116
* Max number of connections this nfsd container will allow. Defaults
@@ -187,6 +187,8 @@ struct nfsd_net {
187187
char nfsd_name[UNX_MAXNODENAME+1];
188188

189189
struct nfsd_fcache_disposal *fcache_disposal;
190+
191+
siphash_key_t siphash_key;
190192
};
191193

192194
/* Simple check to find out if a given net was properly initialized */

fs/nfsd/nfsctl.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1483,7 +1483,8 @@ static __net_init int nfsd_init_net(struct net *net)
14831483
nn->clientid_counter = nn->clientid_base + 1;
14841484
nn->s2s_cp_cl_id = nn->clientid_counter++;
14851485

1486-
seqlock_init(&nn->boot_lock);
1486+
get_random_bytes(&nn->siphash_key, sizeof(nn->siphash_key));
1487+
seqlock_init(&nn->writeverf_lock);
14871488

14881489
return 0;
14891490

fs/nfsd/nfssvc.c

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/module.h>
1313
#include <linux/fs_struct.h>
1414
#include <linux/swap.h>
15+
#include <linux/siphash.h>
1516

1617
#include <linux/sunrpc/stats.h>
1718
#include <linux/sunrpc/svcsock.h>
@@ -344,33 +345,57 @@ static bool nfsd_needs_lockd(struct nfsd_net *nn)
344345
return nfsd_vers(nn, 2, NFSD_TEST) || nfsd_vers(nn, 3, NFSD_TEST);
345346
}
346347

348+
/**
349+
* nfsd_copy_boot_verifier - Atomically copy a write verifier
350+
* @verf: buffer in which to receive the verifier cookie
351+
* @nn: NFS net namespace
352+
*
353+
* This function provides a wait-free mechanism for copying the
354+
* namespace's boot verifier without tearing it.
355+
*/
347356
void nfsd_copy_boot_verifier(__be32 verf[2], struct nfsd_net *nn)
348357
{
349358
int seq = 0;
350359

351360
do {
352-
read_seqbegin_or_lock(&nn->boot_lock, &seq);
353-
/*
354-
* This is opaque to client, so no need to byte-swap. Use
355-
* __force to keep sparse happy. y2038 time_t overflow is
356-
* irrelevant in this usage
357-
*/
358-
verf[0] = (__force __be32)nn->nfssvc_boot.tv_sec;
359-
verf[1] = (__force __be32)nn->nfssvc_boot.tv_nsec;
360-
} while (need_seqretry(&nn->boot_lock, seq));
361-
done_seqretry(&nn->boot_lock, seq);
361+
read_seqbegin_or_lock(&nn->writeverf_lock, &seq);
362+
memcpy(verf, nn->writeverf, sizeof(*verf));
363+
} while (need_seqretry(&nn->writeverf_lock, seq));
364+
done_seqretry(&nn->writeverf_lock, seq);
362365
}
363366

364367
static void nfsd_reset_boot_verifier_locked(struct nfsd_net *nn)
365368
{
366-
ktime_get_raw_ts64(&nn->nfssvc_boot);
369+
struct timespec64 now;
370+
u64 verf;
371+
372+
/*
373+
* Because the time value is hashed, y2038 time_t overflow
374+
* is irrelevant in this usage.
375+
*/
376+
ktime_get_raw_ts64(&now);
377+
verf = siphash_2u64(now.tv_sec, now.tv_nsec, &nn->siphash_key);
378+
memcpy(nn->writeverf, &verf, sizeof(nn->writeverf));
367379
}
368380

381+
/**
382+
* nfsd_reset_boot_verifier - Generate a new boot verifier
383+
* @nn: NFS net namespace
384+
*
385+
* This function updates the ->writeverf field of @nn. This field
386+
* contains an opaque cookie that, according to Section 18.32.3 of
387+
* RFC 8881, "the client can use to determine whether a server has
388+
* changed instance state (e.g., server restart) between a call to
389+
* WRITE and a subsequent call to either WRITE or COMMIT. This
390+
* cookie MUST be unchanged during a single instance of the NFSv4.1
391+
* server and MUST be unique between instances of the NFSv4.1
392+
* server."
393+
*/
369394
void nfsd_reset_boot_verifier(struct nfsd_net *nn)
370395
{
371-
write_seqlock(&nn->boot_lock);
396+
write_seqlock(&nn->writeverf_lock);
372397
nfsd_reset_boot_verifier_locked(nn);
373-
write_sequnlock(&nn->boot_lock);
398+
write_sequnlock(&nn->writeverf_lock);
374399
}
375400

376401
static int nfsd_startup_net(struct net *net, const struct cred *cred)

0 commit comments

Comments
 (0)