Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

nfsd: wire up GET_DIR_DELEGATION handling

Add a new routine for acquiring a read delegation on a directory. These
are recallable-only delegations with no support for CB_NOTIFY. That will
be added in a later phase.

Since the same CB_RECALL/DELEGRETURN infrastructure is used for regular
and directory delegations, a normal nfs4_delegation is used to represent
a directory delegation.

Reviewed-by: NeilBrown <neil@brown.name>
Reviewed-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Jeff Layton <jlayton@kernel.org>
Link: https://patch.msgid.link/20251111-dir-deleg-ro-v6-16-52f3feebb2f2@kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>

authored by

Jeff Layton and committed by
Christian Brauner
8b99f6a8 80c8afdd

+126 -1
+21 -1
fs/nfsd/nfs4proc.c
··· 2341 2341 union nfsd4_op_u *u) 2342 2342 { 2343 2343 struct nfsd4_get_dir_delegation *gdd = &u->get_dir_delegation; 2344 + struct nfs4_delegation *dd; 2345 + struct nfsd_file *nf; 2346 + __be32 status; 2347 + 2348 + status = nfsd_file_acquire_dir(rqstp, &cstate->current_fh, &nf); 2349 + if (status != nfs_ok) 2350 + return status; 2344 2351 2345 2352 /* 2346 2353 * RFC 8881, section 18.39.3 says: ··· 2361 2354 * return NFS4_OK with a non-fatal status of GDD4_UNAVAIL in this 2362 2355 * situation. 2363 2356 */ 2364 - gdd->gddrnf_status = GDD4_UNAVAIL; 2357 + dd = nfsd_get_dir_deleg(cstate, gdd, nf); 2358 + nfsd_file_put(nf); 2359 + if (IS_ERR(dd)) { 2360 + int err = PTR_ERR(dd); 2361 + 2362 + if (err != -EAGAIN) 2363 + return nfserrno(err); 2364 + gdd->gddrnf_status = GDD4_UNAVAIL; 2365 + return nfs_ok; 2366 + } 2367 + 2368 + gdd->gddrnf_status = GDD4_OK; 2369 + memcpy(&gdd->gddr_stateid, &dd->dl_stid.sc_stateid, sizeof(gdd->gddr_stateid)); 2370 + nfs4_put_stid(&dd->dl_stid); 2365 2371 return nfs_ok; 2366 2372 } 2367 2373
+100
fs/nfsd/nfs4state.c
··· 9347 9347 nfs4_put_stid(&dp->dl_stid); 9348 9348 return status; 9349 9349 } 9350 + 9351 + /** 9352 + * nfsd_get_dir_deleg - attempt to get a directory delegation 9353 + * @cstate: compound state 9354 + * @gdd: GET_DIR_DELEGATION arg/resp structure 9355 + * @nf: nfsd_file opened on the directory 9356 + * 9357 + * Given a GET_DIR_DELEGATION request @gdd, attempt to acquire a delegation 9358 + * on the directory to which @nf refers. Note that this does not set up any 9359 + * sort of async notifications for the delegation. 9360 + */ 9361 + struct nfs4_delegation * 9362 + nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate, 9363 + struct nfsd4_get_dir_delegation *gdd, 9364 + struct nfsd_file *nf) 9365 + { 9366 + struct nfs4_client *clp = cstate->clp; 9367 + struct nfs4_delegation *dp; 9368 + struct file_lease *fl; 9369 + struct nfs4_file *fp, *rfp; 9370 + int status = 0; 9371 + 9372 + fp = nfsd4_alloc_file(); 9373 + if (!fp) 9374 + return ERR_PTR(-ENOMEM); 9375 + 9376 + nfsd4_file_init(&cstate->current_fh, fp); 9377 + 9378 + rfp = nfsd4_file_hash_insert(fp, &cstate->current_fh); 9379 + if (unlikely(!rfp)) { 9380 + put_nfs4_file(fp); 9381 + return ERR_PTR(-ENOMEM); 9382 + } 9383 + 9384 + if (rfp != fp) { 9385 + put_nfs4_file(fp); 9386 + fp = rfp; 9387 + } 9388 + 9389 + /* if this client already has one, return that it's unavailable */ 9390 + spin_lock(&state_lock); 9391 + spin_lock(&fp->fi_lock); 9392 + /* existing delegation? */ 9393 + if (nfs4_delegation_exists(clp, fp)) { 9394 + status = -EAGAIN; 9395 + } else if (!fp->fi_deleg_file) { 9396 + fp->fi_deleg_file = nfsd_file_get(nf); 9397 + fp->fi_delegees = 1; 9398 + } else { 9399 + ++fp->fi_delegees; 9400 + } 9401 + spin_unlock(&fp->fi_lock); 9402 + spin_unlock(&state_lock); 9403 + 9404 + if (status) { 9405 + put_nfs4_file(fp); 9406 + return ERR_PTR(status); 9407 + } 9408 + 9409 + /* Try to set up the lease */ 9410 + status = -ENOMEM; 9411 + dp = alloc_init_deleg(clp, fp, NULL, NFS4_OPEN_DELEGATE_READ); 9412 + if (!dp) 9413 + goto out_delegees; 9414 + 9415 + fl = nfs4_alloc_init_lease(dp); 9416 + if (!fl) 9417 + goto out_put_stid; 9418 + 9419 + status = kernel_setlease(nf->nf_file, 9420 + fl->c.flc_type, &fl, NULL); 9421 + if (fl) 9422 + locks_free_lease(fl); 9423 + if (status) 9424 + goto out_put_stid; 9425 + 9426 + /* 9427 + * Now, try to hash it. This can fail if we race another nfsd task 9428 + * trying to set a delegation on the same file. If that happens, 9429 + * then just say UNAVAIL. 9430 + */ 9431 + spin_lock(&state_lock); 9432 + spin_lock(&clp->cl_lock); 9433 + spin_lock(&fp->fi_lock); 9434 + status = hash_delegation_locked(dp, fp); 9435 + spin_unlock(&fp->fi_lock); 9436 + spin_unlock(&clp->cl_lock); 9437 + spin_unlock(&state_lock); 9438 + 9439 + if (!status) 9440 + return dp; 9441 + 9442 + /* Something failed. Drop the lease and clean up the stid */ 9443 + kernel_setlease(fp->fi_deleg_file->nf_file, F_UNLCK, NULL, (void **)&dp); 9444 + out_put_stid: 9445 + nfs4_put_stid(&dp->dl_stid); 9446 + out_delegees: 9447 + put_deleg_file(fp); 9448 + return ERR_PTR(status); 9449 + }
+5
fs/nfsd/state.h
··· 867 867 868 868 extern __be32 nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, 869 869 struct dentry *dentry, struct nfs4_delegation **pdp); 870 + 871 + struct nfsd4_get_dir_delegation; 872 + struct nfs4_delegation *nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate, 873 + struct nfsd4_get_dir_delegation *gdd, 874 + struct nfsd_file *nf); 870 875 #endif /* NFSD4_STATE_H */