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

Merge tag 'nfsd-6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux

Pull nfsd updates from Chuck Lever:
"Jeff Layton contributed a scalability improvement to NFSD's NFSv4
backchannel session implementation. This improvement is intended to
increase the rate at which NFSD can safely recall NFSv4 delegations
from clients, to avoid the need to revoke them. Revoking requires a
slow state recovery process.

A wide variety of bug fixes and other incremental improvements make up
the bulk of commits in this series. As always I am grateful to the
NFSD contributors, reviewers, testers, and bug reporters who
participated during this cycle"

* tag 'nfsd-6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux: (72 commits)
nfsd: allow for up to 32 callback session slots
nfs_common: must not hold RCU while calling nfsd_file_put_local
nfsd: get rid of include ../internal.h
nfsd: fix nfs4_openowner leak when concurrent nfsd4_open occur
NFSD: Add nfsd4_copy time-to-live
NFSD: Add a laundromat reaper for async copy state
NFSD: Block DESTROY_CLIENTID only when there are ongoing async COPY operations
NFSD: Handle an NFS4ERR_DELAY response to CB_OFFLOAD
NFSD: Free async copy information in nfsd4_cb_offload_release()
NFSD: Fix nfsd4_shutdown_copy()
NFSD: Add a tracepoint to record canceled async COPY operations
nfsd: make nfsd4_session->se_flags a bool
nfsd: remove nfsd4_session->se_bchannel
nfsd: make use of warning provided by refcount_t
nfsd: Don't fail OP_SETCLIENTID when there are too many clients.
svcrdma: fix miss destroy percpu_counter in svc_rdma_proc_init()
xdrgen: Remove program_stat_to_errno() call sites
xdrgen: Update the files included in client-side source code
xdrgen: Remove check for "nfs_ok" in C templates
xdrgen: Remove tracepoint call site
...

+1115 -348
+1
MAINTAINERS
··· 12436 12436 F: include/uapi/linux/nfsd/ 12437 12437 F: include/uapi/linux/sunrpc/ 12438 12438 F: net/sunrpc/ 12439 + F: tools/net/sunrpc/ 12439 12440 12440 12441 KERNEL PACMAN PACKAGING (in addition to generic KERNEL BUILD) 12441 12442 M: Thomas Weißschuh <linux@weissschuh.net>
+3 -2
fs/lockd/clntxdr.c
··· 2 2 /* 3 3 * linux/fs/lockd/clntxdr.c 4 4 * 5 - * XDR functions to encode/decode NLM version 3 RPC arguments and results. 6 - * NLM version 3 is backwards compatible with NLM versions 1 and 2. 5 + * XDR functions to encode/decode NLM version 1 and 3 RPC 6 + * arguments and results. NLM version 2 is not specified 7 + * by a standard, thus it is not implemented. 7 8 * 8 9 * NLM client-side only. 9 10 *
+5 -15
fs/lockd/svc4proc.c
··· 46 46 if (filp != NULL) { 47 47 int mode = lock_to_openmode(&lock->fl); 48 48 49 + lock->fl.c.flc_flags = FL_POSIX; 50 + 49 51 error = nlm_lookup_file(rqstp, &file, lock); 50 52 if (error) 51 53 goto no_locks; 52 54 *filp = file; 53 55 54 56 /* Set up the missing parts of the file_lock structure */ 55 - lock->fl.c.flc_flags = FL_POSIX; 56 - lock->fl.c.flc_file = file->f_file[mode]; 57 + lock->fl.c.flc_file = file->f_file[mode]; 57 58 lock->fl.c.flc_pid = current->tgid; 58 59 lock->fl.fl_start = (loff_t)lock->lock_start; 59 60 lock->fl.fl_end = lock->lock_len ? ··· 109 108 110 109 test_owner = argp->lock.fl.c.flc_owner; 111 110 /* Now check for conflicting locks */ 112 - resp->status = nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock, &resp->cookie); 111 + resp->status = nlmsvc_testlock(rqstp, file, host, &argp->lock, 112 + &resp->lock); 113 113 if (resp->status == nlm_drop_reply) 114 114 rc = rpc_drop_reply; 115 115 else ··· 143 141 /* Obtain client and file */ 144 142 if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) 145 143 return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; 146 - 147 - #if 0 148 - /* If supplied state doesn't match current state, we assume it's 149 - * an old request that time-warped somehow. Any error return would 150 - * do in this case because it's irrelevant anyway. 151 - * 152 - * NB: We don't retrieve the remote host's state yet. 153 - */ 154 - if (host->h_nsmstate && host->h_nsmstate != argp->state) { 155 - resp->status = nlm_lck_denied_nolocks; 156 - } else 157 - #endif 158 144 159 145 /* Now try to lock the file */ 160 146 resp->status = nlmsvc_lock(rqstp, file, host, &argp->lock,
+1 -1
fs/lockd/svclock.c
··· 608 608 __be32 609 609 nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, 610 610 struct nlm_host *host, struct nlm_lock *lock, 611 - struct nlm_lock *conflock, struct nlm_cookie *cookie) 611 + struct nlm_lock *conflock) 612 612 { 613 613 int error; 614 614 int mode;
+2 -13
fs/lockd/svcproc.c
··· 130 130 test_owner = argp->lock.fl.c.flc_owner; 131 131 132 132 /* Now check for conflicting locks */ 133 - resp->status = cast_status(nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock, &resp->cookie)); 133 + resp->status = cast_status(nlmsvc_testlock(rqstp, file, host, 134 + &argp->lock, &resp->lock)); 134 135 if (resp->status == nlm_drop_reply) 135 136 rc = rpc_drop_reply; 136 137 else ··· 165 164 /* Obtain client and file */ 166 165 if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) 167 166 return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; 168 - 169 - #if 0 170 - /* If supplied state doesn't match current state, we assume it's 171 - * an old request that time-warped somehow. Any error return would 172 - * do in this case because it's irrelevant anyway. 173 - * 174 - * NB: We don't retrieve the remote host's state yet. 175 - */ 176 - if (host->h_nsmstate && host->h_nsmstate != argp->state) { 177 - resp->status = nlm_lck_denied_nolocks; 178 - } else 179 - #endif 180 167 181 168 /* Now try to lock the file */ 182 169 resp->status = cast_status(nlmsvc_lock(rqstp, file, host, &argp->lock,
-2
fs/lockd/xdr4.c
··· 89 89 return false; 90 90 91 91 locks_init_lock(fl); 92 - fl->c.flc_flags = FL_POSIX; 93 92 fl->c.flc_type = F_RDLCK; 94 93 nlm4svc_set_file_lock_range(fl, lock->lock_start, lock->lock_len); 95 94 return true; ··· 267 268 struct nlm_args *argp = rqstp->rq_argp; 268 269 struct nlm_lock *lock = &argp->lock; 269 270 270 - memset(lock, 0, sizeof(*lock)); 271 271 locks_init_lock(&lock->fl); 272 272 lock->svid = ~(u32)0; 273 273
+3 -5
fs/nfs_common/nfslocalio.c
··· 155 155 /* We have an implied reference to net thanks to nfsd_serv_try_get */ 156 156 localio = nfs_to->nfsd_open_local_fh(net, uuid->dom, rpc_clnt, 157 157 cred, nfs_fh, fmode); 158 - if (IS_ERR(localio)) { 159 - rcu_read_lock(); 160 - nfs_to->nfsd_serv_put(net); 161 - rcu_read_unlock(); 162 - } 158 + if (IS_ERR(localio)) 159 + nfs_to_nfsd_net_put(net); 160 + 163 161 return localio; 164 162 } 165 163 EXPORT_SYMBOL_GPL(nfs_open_local_fh);
+49 -8
fs/nfsd/export.c
··· 40 40 #define EXPKEY_HASHMAX (1 << EXPKEY_HASHBITS) 41 41 #define EXPKEY_HASHMASK (EXPKEY_HASHMAX -1) 42 42 43 - static void expkey_put(struct kref *ref) 43 + static void expkey_put_work(struct work_struct *work) 44 44 { 45 - struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref); 45 + struct svc_expkey *key = 46 + container_of(to_rcu_work(work), struct svc_expkey, ek_rcu_work); 46 47 47 48 if (test_bit(CACHE_VALID, &key->h.flags) && 48 49 !test_bit(CACHE_NEGATIVE, &key->h.flags)) 49 50 path_put(&key->ek_path); 50 51 auth_domain_put(key->ek_client); 51 - kfree_rcu(key, ek_rcu); 52 + kfree(key); 53 + } 54 + 55 + static void expkey_put(struct kref *ref) 56 + { 57 + struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref); 58 + 59 + INIT_RCU_WORK(&key->ek_rcu_work, expkey_put_work); 60 + queue_rcu_work(system_wq, &key->ek_rcu_work); 52 61 } 53 62 54 63 static int expkey_upcall(struct cache_detail *cd, struct cache_head *h) ··· 364 355 EXP_STATS_COUNTERS_NUM); 365 356 } 366 357 367 - static void svc_export_put(struct kref *ref) 358 + static void svc_export_put_work(struct work_struct *work) 368 359 { 369 - struct svc_export *exp = container_of(ref, struct svc_export, h.ref); 360 + struct svc_export *exp = 361 + container_of(to_rcu_work(work), struct svc_export, ex_rcu_work); 362 + 370 363 path_put(&exp->ex_path); 371 364 auth_domain_put(exp->ex_client); 372 365 nfsd4_fslocs_free(&exp->ex_fslocs); 373 366 export_stats_destroy(exp->ex_stats); 374 367 kfree(exp->ex_stats); 375 368 kfree(exp->ex_uuid); 376 - kfree_rcu(exp, ex_rcu); 369 + kfree(exp); 370 + } 371 + 372 + static void svc_export_put(struct kref *ref) 373 + { 374 + struct svc_export *exp = container_of(ref, struct svc_export, h.ref); 375 + 376 + INIT_RCU_WORK(&exp->ex_rcu_work, svc_export_put_work); 377 + queue_rcu_work(system_wq, &exp->ex_rcu_work); 377 378 } 378 379 379 380 static int svc_export_upcall(struct cache_detail *cd, struct cache_head *h) ··· 1097 1078 * check_nfsd_access - check if access to export is allowed. 1098 1079 * @exp: svc_export that is being accessed. 1099 1080 * @rqstp: svc_rqst attempting to access @exp (will be NULL for LOCALIO). 1081 + * @may_bypass_gss: reduce strictness of authorization check 1100 1082 * 1101 1083 * Return values: 1102 1084 * %nfs_ok if access is granted, or 1103 1085 * %nfserr_wrongsec if access is denied 1104 1086 */ 1105 - __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp) 1087 + __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp, 1088 + bool may_bypass_gss) 1106 1089 { 1107 1090 struct exp_flavor_info *f, *end = exp->ex_flavors + exp->ex_nflavors; 1108 1091 struct svc_xprt *xprt; ··· 1160 1139 1161 1140 if (nfsd4_spo_must_allow(rqstp)) 1162 1141 return nfs_ok; 1142 + 1143 + /* Some calls may be processed without authentication 1144 + * on GSS exports. For example NFS2/3 calls on root 1145 + * directory, see section 2.3.2 of rfc 2623. 1146 + * For "may_bypass_gss" check that export has really 1147 + * enabled some flavor with authentication (GSS or any 1148 + * other) and also check that the used auth flavor is 1149 + * without authentication (none or sys). 1150 + */ 1151 + if (may_bypass_gss && ( 1152 + rqstp->rq_cred.cr_flavor == RPC_AUTH_NULL || 1153 + rqstp->rq_cred.cr_flavor == RPC_AUTH_UNIX)) { 1154 + for (f = exp->ex_flavors; f < end; f++) { 1155 + if (f->pseudoflavor >= RPC_AUTH_DES) 1156 + return 0; 1157 + } 1158 + } 1163 1159 1164 1160 denied: 1165 1161 return nfserr_wrongsec; ··· 1444 1406 return 0; 1445 1407 } 1446 1408 1447 - exp_get(exp); 1409 + if (!cache_get_rcu(&exp->h)) 1410 + return 0; 1411 + 1448 1412 if (cache_check(cd, &exp->h, NULL)) 1449 1413 return 0; 1414 + 1450 1415 exp_put(exp); 1451 1416 return svc_export_show(m, cd, cp); 1452 1417 }
+4 -3
fs/nfsd/export.h
··· 75 75 u32 ex_layout_types; 76 76 struct nfsd4_deviceid_map *ex_devid_map; 77 77 struct cache_detail *cd; 78 - struct rcu_head ex_rcu; 78 + struct rcu_work ex_rcu_work; 79 79 unsigned long ex_xprtsec_modes; 80 80 struct export_stats *ex_stats; 81 81 }; ··· 92 92 u32 ek_fsid[6]; 93 93 94 94 struct path ek_path; 95 - struct rcu_head ek_rcu; 95 + struct rcu_work ek_rcu_work; 96 96 }; 97 97 98 98 #define EX_ISSYNC(exp) (!((exp)->ex_flags & NFSEXP_ASYNC)) ··· 101 101 102 102 struct svc_cred; 103 103 int nfsexp_flags(struct svc_cred *cred, struct svc_export *exp); 104 - __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp); 104 + __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp, 105 + bool may_bypass_gss); 105 106 106 107 /* 107 108 * Function declarations
+9 -10
fs/nfsd/filecache.c
··· 391 391 } 392 392 393 393 /** 394 - * nfsd_file_put_local - put the reference to nfsd_file and local nfsd_serv 395 - * @nf: nfsd_file of which to put the references 394 + * nfsd_file_put_local - put nfsd_file reference and arm nfsd_serv_put in caller 395 + * @nf: nfsd_file of which to put the reference 396 396 * 397 - * First put the reference of the nfsd_file and then put the 398 - * reference to the associated nn->nfsd_serv. 397 + * First save the associated net to return to caller, then put 398 + * the reference of the nfsd_file. 399 399 */ 400 - void 401 - nfsd_file_put_local(struct nfsd_file *nf) __must_hold(rcu) 400 + struct net * 401 + nfsd_file_put_local(struct nfsd_file *nf) 402 402 { 403 403 struct net *net = nf->nf_net; 404 404 405 405 nfsd_file_put(nf); 406 - nfsd_serv_put(net); 406 + return net; 407 407 } 408 408 409 409 /** ··· 1047 1047 * the last one however, since we should hold another. 1048 1048 */ 1049 1049 if (nfsd_file_lru_remove(nf)) 1050 - WARN_ON_ONCE(refcount_dec_and_test(&nf->nf_ref)); 1050 + refcount_dec(&nf->nf_ref); 1051 1051 goto wait_for_construction; 1052 1052 } 1053 1053 ··· 1120 1120 status = nfs_ok; 1121 1121 trace_nfsd_file_opened(nf, status); 1122 1122 } else { 1123 - ret = nfsd_open_verified(rqstp, fhp, may_flags, 1124 - &nf->nf_file); 1123 + ret = nfsd_open_verified(fhp, may_flags, &nf->nf_file); 1125 1124 if (ret == -EOPENSTALE && stale_retry) { 1126 1125 stale_retry = false; 1127 1126 nfsd_file_unhash(nf);
+1 -1
fs/nfsd/filecache.h
··· 55 55 int nfsd_file_cache_start_net(struct net *net); 56 56 void nfsd_file_cache_shutdown_net(struct net *net); 57 57 void nfsd_file_put(struct nfsd_file *nf); 58 - void nfsd_file_put_local(struct nfsd_file *nf); 58 + struct net *nfsd_file_put_local(struct nfsd_file *nf); 59 59 struct nfsd_file *nfsd_file_get(struct nfsd_file *nf); 60 60 struct file *nfsd_file_file(struct nfsd_file *nf); 61 61 void nfsd_file_close_inode_sync(struct inode *inode);
+11 -2
fs/nfsd/lockd.c
··· 38 38 memcpy(&fh.fh_handle.fh_raw, f->data, f->size); 39 39 fh.fh_export = NULL; 40 40 41 + /* 42 + * Allow BYPASS_GSS as some client implementations use AUTH_SYS 43 + * for NLM even when GSS is used for NFS. 44 + * Allow OWNER_OVERRIDE as permission might have been changed 45 + * after the file was opened. 46 + * Pass MAY_NLM so that authentication can be completely bypassed 47 + * if NFSEXP_NOAUTHNLM is set. Some older clients use AUTH_NULL 48 + * for NLM requests. 49 + */ 41 50 access = (mode == O_WRONLY) ? NFSD_MAY_WRITE : NFSD_MAY_READ; 42 - access |= NFSD_MAY_LOCK; 51 + access |= NFSD_MAY_NLM | NFSD_MAY_OWNER_OVERRIDE | NFSD_MAY_BYPASS_GSS; 43 52 nfserr = nfsd_open(rqstp, &fh, S_IFREG, access, filp); 44 53 fh_put(&fh); 45 - /* We return nlm error codes as nlm doesn't know 54 + /* We return nlm error codes as nlm doesn't know 46 55 * about nfsd, but nfsd does know about nlm.. 47 56 */ 48 57 switch (nfserr) {
-2
fs/nfsd/nfs4acl.c
··· 198 198 memset(pas, 0, sizeof(*pas)); 199 199 pas->mask = 07; 200 200 201 - pe = acl->a_entries + acl->a_count; 202 - 203 201 FOREACH_ACL_ENTRY(pa, acl, pe) { 204 202 switch (pa->e_tag) { 205 203 case ACL_USER_OBJ:
+101 -38
fs/nfsd/nfs4callback.c
··· 287 287 u32 length; 288 288 __be32 *p; 289 289 290 - p = xdr_inline_decode(xdr, 4 + 4); 290 + p = xdr_inline_decode(xdr, XDR_UNIT); 291 291 if (unlikely(p == NULL)) 292 292 goto out_overflow; 293 - hdr->status = be32_to_cpup(p++); 293 + hdr->status = be32_to_cpup(p); 294 294 /* Ignore the tag */ 295 - length = be32_to_cpup(p++); 296 - p = xdr_inline_decode(xdr, length + 4); 297 - if (unlikely(p == NULL)) 295 + if (xdr_stream_decode_u32(xdr, &length) < 0) 298 296 goto out_overflow; 299 - p += XDR_QUADLEN(length); 300 - hdr->nops = be32_to_cpup(p); 297 + if (xdr_inline_decode(xdr, length) == NULL) 298 + goto out_overflow; 299 + if (xdr_stream_decode_u32(xdr, &hdr->nops) < 0) 300 + goto out_overflow; 301 301 return 0; 302 302 out_overflow: 303 303 return -EIO; ··· 364 364 struct nfs4_delegation *dp = 365 365 container_of(fattr, struct nfs4_delegation, dl_cb_fattr); 366 366 struct knfsd_fh *fh = &dp->dl_stid.sc_file->fi_fhandle; 367 + u32 bmap[1]; 368 + 369 + bmap[0] = FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE; 367 370 368 371 encode_nfs_cb_opnum4(xdr, OP_CB_GETATTR); 369 372 encode_nfs_fh4(xdr, fh); 370 - encode_bitmap4(xdr, fattr->ncf_cb_bmap, ARRAY_SIZE(fattr->ncf_cb_bmap)); 373 + encode_bitmap4(xdr, bmap, ARRAY_SIZE(bmap)); 371 374 hdr->nops++; 375 + } 376 + 377 + static u32 highest_slotid(struct nfsd4_session *ses) 378 + { 379 + u32 idx; 380 + 381 + spin_lock(&ses->se_lock); 382 + idx = fls(~ses->se_cb_slot_avail); 383 + if (idx > 0) 384 + --idx; 385 + idx = max(idx, ses->se_cb_highest_slot); 386 + spin_unlock(&ses->se_lock); 387 + return idx; 372 388 } 373 389 374 390 /* ··· 413 397 encode_sessionid4(xdr, session); 414 398 415 399 p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4 + 4); 416 - *p++ = cpu_to_be32(session->se_cb_seq_nr); /* csa_sequenceid */ 417 - *p++ = xdr_zero; /* csa_slotid */ 418 - *p++ = xdr_zero; /* csa_highest_slotid */ 400 + *p++ = cpu_to_be32(session->se_cb_seq_nr[cb->cb_held_slot]); /* csa_sequenceid */ 401 + *p++ = cpu_to_be32(cb->cb_held_slot); /* csa_slotid */ 402 + *p++ = cpu_to_be32(highest_slotid(session)); /* csa_highest_slotid */ 419 403 *p++ = xdr_zero; /* csa_cachethis */ 420 404 xdr_encode_empty_array(p); /* csa_referring_call_lists */ 421 405 422 406 hdr->nops++; 407 + } 408 + 409 + static void update_cb_slot_table(struct nfsd4_session *ses, u32 target) 410 + { 411 + /* No need to do anything if nothing changed */ 412 + if (likely(target == READ_ONCE(ses->se_cb_highest_slot))) 413 + return; 414 + 415 + spin_lock(&ses->se_lock); 416 + if (target > ses->se_cb_highest_slot) { 417 + int i; 418 + 419 + target = min(target, NFSD_BC_SLOT_TABLE_SIZE - 1); 420 + 421 + /* 422 + * Growing the slot table. Reset any new sequences to 1. 423 + * 424 + * NB: There is some debate about whether the RFC requires this, 425 + * but the Linux client expects it. 426 + */ 427 + for (i = ses->se_cb_highest_slot + 1; i <= target; ++i) 428 + ses->se_cb_seq_nr[i] = 1; 429 + } 430 + ses->se_cb_highest_slot = target; 431 + spin_unlock(&ses->se_lock); 423 432 } 424 433 425 434 /* ··· 474 433 struct nfsd4_session *session = cb->cb_clp->cl_cb_session; 475 434 int status = -ESERVERFAULT; 476 435 __be32 *p; 477 - u32 dummy; 436 + u32 seqid, slotid, target; 478 437 479 438 /* 480 439 * If the server returns different values for sessionID, slotID or ··· 490 449 } 491 450 p += XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN); 492 451 493 - dummy = be32_to_cpup(p++); 494 - if (dummy != session->se_cb_seq_nr) { 452 + seqid = be32_to_cpup(p++); 453 + if (seqid != session->se_cb_seq_nr[cb->cb_held_slot]) { 495 454 dprintk("NFS: %s Invalid sequence number\n", __func__); 496 455 goto out; 497 456 } 498 457 499 - dummy = be32_to_cpup(p++); 500 - if (dummy != 0) { 458 + slotid = be32_to_cpup(p++); 459 + if (slotid != cb->cb_held_slot) { 501 460 dprintk("NFS: %s Invalid slotid\n", __func__); 502 461 goto out; 503 462 } 504 463 505 - /* 506 - * FIXME: process highest slotid and target highest slotid 507 - */ 464 + p++; // ignore current highest slot value 465 + 466 + target = be32_to_cpup(p++); 467 + update_cb_slot_table(session, target); 508 468 status = 0; 509 469 out: 510 470 cb->cb_seq_status = status; ··· 1206 1164 spin_unlock(&clp->cl_lock); 1207 1165 } 1208 1166 1167 + static int grab_slot(struct nfsd4_session *ses) 1168 + { 1169 + int idx; 1170 + 1171 + spin_lock(&ses->se_lock); 1172 + idx = ffs(ses->se_cb_slot_avail) - 1; 1173 + if (idx < 0 || idx > ses->se_cb_highest_slot) { 1174 + spin_unlock(&ses->se_lock); 1175 + return -1; 1176 + } 1177 + /* clear the bit for the slot */ 1178 + ses->se_cb_slot_avail &= ~BIT(idx); 1179 + spin_unlock(&ses->se_lock); 1180 + return idx; 1181 + } 1182 + 1209 1183 /* 1210 1184 * There's currently a single callback channel slot. 1211 1185 * If the slot is available, then mark it busy. Otherwise, set the ··· 1230 1172 static bool nfsd41_cb_get_slot(struct nfsd4_callback *cb, struct rpc_task *task) 1231 1173 { 1232 1174 struct nfs4_client *clp = cb->cb_clp; 1175 + struct nfsd4_session *ses = clp->cl_cb_session; 1233 1176 1234 - if (!cb->cb_holds_slot && 1235 - test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) { 1177 + if (cb->cb_held_slot >= 0) 1178 + return true; 1179 + cb->cb_held_slot = grab_slot(ses); 1180 + if (cb->cb_held_slot < 0) { 1236 1181 rpc_sleep_on(&clp->cl_cb_waitq, task, NULL); 1237 1182 /* Race breaker */ 1238 - if (test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) { 1239 - dprintk("%s slot is busy\n", __func__); 1183 + cb->cb_held_slot = grab_slot(ses); 1184 + if (cb->cb_held_slot < 0) 1240 1185 return false; 1241 - } 1242 1186 rpc_wake_up_queued_task(&clp->cl_cb_waitq, task); 1243 1187 } 1244 - cb->cb_holds_slot = true; 1245 1188 return true; 1246 1189 } 1247 1190 1248 1191 static void nfsd41_cb_release_slot(struct nfsd4_callback *cb) 1249 1192 { 1250 1193 struct nfs4_client *clp = cb->cb_clp; 1194 + struct nfsd4_session *ses = clp->cl_cb_session; 1251 1195 1252 - if (cb->cb_holds_slot) { 1253 - cb->cb_holds_slot = false; 1254 - clear_bit(0, &clp->cl_cb_slot_busy); 1196 + if (cb->cb_held_slot >= 0) { 1197 + spin_lock(&ses->se_lock); 1198 + ses->se_cb_slot_avail |= BIT(cb->cb_held_slot); 1199 + spin_unlock(&ses->se_lock); 1200 + cb->cb_held_slot = -1; 1255 1201 rpc_wake_up_next(&clp->cl_cb_waitq); 1256 1202 } 1257 1203 } ··· 1272 1210 } 1273 1211 1274 1212 /* 1275 - * TODO: cb_sequence should support referring call lists, cachethis, multiple 1276 - * slots, and mark callback channel down on communication errors. 1213 + * TODO: cb_sequence should support referring call lists, cachethis, 1214 + * and mark callback channel down on communication errors. 1277 1215 */ 1278 1216 static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata) 1279 1217 { ··· 1315 1253 return true; 1316 1254 } 1317 1255 1318 - if (!cb->cb_holds_slot) 1256 + if (cb->cb_held_slot < 0) 1319 1257 goto need_restart; 1320 1258 1321 1259 /* This is the operation status code for CB_SEQUENCE */ ··· 1329 1267 * If CB_SEQUENCE returns an error, then the state of the slot 1330 1268 * (sequence ID, cached reply) MUST NOT change. 1331 1269 */ 1332 - ++session->se_cb_seq_nr; 1270 + ++session->se_cb_seq_nr[cb->cb_held_slot]; 1333 1271 break; 1334 1272 case -ESERVERFAULT: 1335 - ++session->se_cb_seq_nr; 1273 + ++session->se_cb_seq_nr[cb->cb_held_slot]; 1336 1274 nfsd4_mark_cb_fault(cb->cb_clp); 1337 1275 ret = false; 1338 1276 break; ··· 1358 1296 case -NFS4ERR_BADSLOT: 1359 1297 goto retry_nowait; 1360 1298 case -NFS4ERR_SEQ_MISORDERED: 1361 - if (session->se_cb_seq_nr != 1) { 1362 - session->se_cb_seq_nr = 1; 1299 + if (session->se_cb_seq_nr[cb->cb_held_slot] != 1) { 1300 + session->se_cb_seq_nr[cb->cb_held_slot] = 1; 1363 1301 goto retry_nowait; 1364 1302 } 1365 1303 break; 1366 1304 default: 1367 1305 nfsd4_mark_cb_fault(cb->cb_clp); 1368 1306 } 1369 - nfsd41_cb_release_slot(cb); 1370 - 1371 1307 trace_nfsd_cb_free_slot(task, cb); 1308 + nfsd41_cb_release_slot(cb); 1372 1309 1373 1310 if (RPC_SIGNALLED(task)) 1374 1311 goto need_restart; ··· 1522 1461 ses = c->cn_session; 1523 1462 } 1524 1463 spin_unlock(&clp->cl_lock); 1464 + if (!c) 1465 + return; 1525 1466 1526 1467 err = setup_callback_client(clp, &conn, ses); 1527 1468 if (err) { ··· 1587 1524 INIT_WORK(&cb->cb_work, nfsd4_run_cb_work); 1588 1525 cb->cb_status = 0; 1589 1526 cb->cb_need_restart = false; 1590 - cb->cb_holds_slot = false; 1527 + cb->cb_held_slot = -1; 1591 1528 } 1592 1529 1593 1530 /**
+93 -10
fs/nfsd/nfs4proc.c
··· 57 57 MODULE_PARM_DESC(inter_copy_offload_enable, 58 58 "Enable inter server to server copy offload. Default: false"); 59 59 60 + static void cleanup_async_copy(struct nfsd4_copy *copy); 61 + 60 62 #ifdef CONFIG_NFSD_V4_2_INTER_SSC 61 63 static int nfsd4_ssc_umount_timeout = 900000; /* default to 15 mins */ 62 64 module_param(nfsd4_ssc_umount_timeout, int, 0644); ··· 1278 1276 return status; 1279 1277 } 1280 1278 1279 + /** 1280 + * nfsd4_has_active_async_copies - Check for ongoing copy operations 1281 + * @clp: Client to be checked 1282 + * 1283 + * NFSD maintains state for async COPY operations after they complete, 1284 + * and this state remains in the nfs4_client's async_copies list. 1285 + * Ongoing copies should block the destruction of the nfs4_client, but 1286 + * completed copies should not. 1287 + * 1288 + * Return values: 1289 + * %true: At least one active async COPY is ongoing 1290 + * %false: No active async COPY operations were found 1291 + */ 1292 + bool nfsd4_has_active_async_copies(struct nfs4_client *clp) 1293 + { 1294 + struct nfsd4_copy *copy; 1295 + bool result = false; 1296 + 1297 + spin_lock(&clp->async_lock); 1298 + list_for_each_entry(copy, &clp->async_copies, copies) { 1299 + if (!test_bit(NFSD4_COPY_F_COMPLETED, &copy->cp_flags) && 1300 + !test_bit(NFSD4_COPY_F_STOPPED, &copy->cp_flags)) { 1301 + result = true; 1302 + break; 1303 + } 1304 + } 1305 + spin_unlock(&clp->async_lock); 1306 + return result; 1307 + } 1308 + 1309 + /** 1310 + * nfsd4_async_copy_reaper - Purge completed copies 1311 + * @nn: Network namespace with possible active copy information 1312 + */ 1313 + void nfsd4_async_copy_reaper(struct nfsd_net *nn) 1314 + { 1315 + struct nfs4_client *clp; 1316 + struct nfsd4_copy *copy; 1317 + LIST_HEAD(reaplist); 1318 + 1319 + spin_lock(&nn->client_lock); 1320 + list_for_each_entry(clp, &nn->client_lru, cl_lru) { 1321 + struct list_head *pos, *next; 1322 + 1323 + spin_lock(&clp->async_lock); 1324 + list_for_each_safe(pos, next, &clp->async_copies) { 1325 + copy = list_entry(pos, struct nfsd4_copy, copies); 1326 + if (test_bit(NFSD4_COPY_F_OFFLOAD_DONE, &copy->cp_flags)) { 1327 + if (--copy->cp_ttl) { 1328 + list_del_init(&copy->copies); 1329 + list_add(&copy->copies, &reaplist); 1330 + } 1331 + } 1332 + } 1333 + spin_unlock(&clp->async_lock); 1334 + } 1335 + spin_unlock(&nn->client_lock); 1336 + 1337 + while (!list_empty(&reaplist)) { 1338 + copy = list_first_entry(&reaplist, struct nfsd4_copy, copies); 1339 + list_del_init(&copy->copies); 1340 + cleanup_async_copy(copy); 1341 + } 1342 + } 1343 + 1281 1344 static void nfs4_put_copy(struct nfsd4_copy *copy) 1282 1345 { 1283 1346 if (!refcount_dec_and_test(&copy->refcount)) ··· 1354 1287 1355 1288 static void nfsd4_stop_copy(struct nfsd4_copy *copy) 1356 1289 { 1290 + trace_nfsd_copy_async_cancel(copy); 1357 1291 if (!test_and_set_bit(NFSD4_COPY_F_STOPPED, &copy->cp_flags)) 1358 1292 kthread_stop(copy->copy_task); 1359 1293 nfs4_put_copy(copy); 1360 1294 } 1361 1295 1362 - static struct nfsd4_copy *nfsd4_get_copy(struct nfs4_client *clp) 1296 + static struct nfsd4_copy *nfsd4_unhash_copy(struct nfs4_client *clp) 1363 1297 { 1364 1298 struct nfsd4_copy *copy = NULL; 1365 1299 ··· 1369 1301 copy = list_first_entry(&clp->async_copies, struct nfsd4_copy, 1370 1302 copies); 1371 1303 refcount_inc(&copy->refcount); 1304 + copy->cp_clp = NULL; 1305 + if (!list_empty(&copy->copies)) 1306 + list_del_init(&copy->copies); 1372 1307 } 1373 1308 spin_unlock(&clp->async_lock); 1374 1309 return copy; ··· 1381 1310 { 1382 1311 struct nfsd4_copy *copy; 1383 1312 1384 - while ((copy = nfsd4_get_copy(clp)) != NULL) 1313 + while ((copy = nfsd4_unhash_copy(clp)) != NULL) 1385 1314 nfsd4_stop_copy(copy); 1386 1315 } 1387 1316 #ifdef CONFIG_NFSD_V4_2_INTER_SSC ··· 1669 1598 { 1670 1599 struct nfsd4_cb_offload *cbo = 1671 1600 container_of(cb, struct nfsd4_cb_offload, co_cb); 1601 + struct nfsd4_copy *copy = 1602 + container_of(cbo, struct nfsd4_copy, cp_cb_offload); 1672 1603 1673 - kfree(cbo); 1604 + set_bit(NFSD4_COPY_F_OFFLOAD_DONE, &copy->cp_flags); 1674 1605 } 1675 1606 1676 1607 static int nfsd4_cb_offload_done(struct nfsd4_callback *cb, ··· 1682 1609 container_of(cb, struct nfsd4_cb_offload, co_cb); 1683 1610 1684 1611 trace_nfsd_cb_offload_done(&cbo->co_res.cb_stateid, task); 1612 + switch (task->tk_status) { 1613 + case -NFS4ERR_DELAY: 1614 + if (cbo->co_retries--) { 1615 + rpc_delay(task, 1 * HZ); 1616 + return 0; 1617 + } 1618 + } 1685 1619 return 1; 1686 1620 } 1687 1621 ··· 1812 1732 1813 1733 static void nfsd4_send_cb_offload(struct nfsd4_copy *copy) 1814 1734 { 1815 - struct nfsd4_cb_offload *cbo; 1816 - 1817 - cbo = kzalloc(sizeof(*cbo), GFP_KERNEL); 1818 - if (!cbo) 1819 - return; 1735 + struct nfsd4_cb_offload *cbo = &copy->cp_cb_offload; 1820 1736 1821 1737 memcpy(&cbo->co_res, &copy->cp_res, sizeof(copy->cp_res)); 1822 1738 memcpy(&cbo->co_fh, &copy->fh, sizeof(copy->fh)); 1823 1739 cbo->co_nfserr = copy->nfserr; 1740 + cbo->co_retries = 5; 1824 1741 1825 1742 nfsd4_init_cb(&cbo->co_cb, copy->cp_clp, &nfsd4_cb_offload_ops, 1826 1743 NFSPROC4_CLNT_CB_OFFLOAD); ··· 1863 1786 } 1864 1787 1865 1788 do_callback: 1789 + /* The kthread exits forthwith. Ensure that a subsequent 1790 + * OFFLOAD_CANCEL won't try to kill it again. */ 1791 + set_bit(NFSD4_COPY_F_STOPPED, &copy->cp_flags); 1792 + 1866 1793 set_bit(NFSD4_COPY_F_COMPLETED, &copy->cp_flags); 1867 1794 trace_nfsd_copy_async_done(copy); 1868 1795 nfsd4_send_cb_offload(copy); 1869 - cleanup_async_copy(copy); 1870 1796 return 0; 1871 1797 } 1872 1798 ··· 1923 1843 async_copy->cp_nn = nn; 1924 1844 INIT_LIST_HEAD(&async_copy->copies); 1925 1845 refcount_set(&async_copy->refcount, 1); 1846 + async_copy->cp_ttl = NFSD_COPY_INITIAL_TTL; 1926 1847 /* Arbitrary cap on number of pending async copy operations */ 1927 1848 if (atomic_inc_return(&nn->pending_async_copies) > 1928 1849 (int)rqstp->rq_pool->sp_nrthreads) ··· 2861 2780 if (op->opdesc->op_get_currentstateid) 2862 2781 op->opdesc->op_get_currentstateid(cstate, &op->u); 2863 2782 op->status = op->opdesc->op_func(rqstp, cstate, &op->u); 2783 + trace_nfsd_compound_op_err(rqstp, op->opnum, op->status); 2864 2784 2865 2785 /* Only from SEQUENCE */ 2866 2786 if (cstate->status == nfserr_replay_cache) { ··· 2878 2796 2879 2797 if (current_fh->fh_export && 2880 2798 need_wrongsec_check(rqstp)) 2881 - op->status = check_nfsd_access(current_fh->fh_export, rqstp); 2799 + op->status = check_nfsd_access(current_fh->fh_export, rqstp, false); 2882 2800 } 2883 2801 encode_op: 2884 2802 if (op->status == nfserr_replay_me) { ··· 3534 3452 /* NFSv4.1 operations */ 3535 3453 [OP_EXCHANGE_ID] = { 3536 3454 .op_func = nfsd4_exchange_id, 3455 + .op_release = nfsd4_exchange_id_release, 3537 3456 .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP 3538 3457 | OP_MODIFIES_SOMETHING, 3539 3458 .op_name = "OP_EXCHANGE_ID",
+2 -1
fs/nfsd/nfs4recover.c
··· 659 659 return status; 660 660 status = -ENOTDIR; 661 661 if (d_is_dir(path.dentry)) { 662 - strcpy(user_recovery_dirname, recdir); 662 + strscpy(user_recovery_dirname, recdir, 663 + sizeof(user_recovery_dirname)); 663 664 status = 0; 664 665 } 665 666 path_put(&path);
+82 -45
fs/nfsd/nfs4state.c
··· 149 149 150 150 static bool is_session_dead(struct nfsd4_session *ses) 151 151 { 152 - return ses->se_flags & NFS4_SESSION_DEAD; 152 + return ses->se_dead; 153 153 } 154 154 155 155 static __be32 mark_session_dead_locked(struct nfsd4_session *ses, int ref_held_by_me) 156 156 { 157 157 if (atomic_read(&ses->se_ref) > ref_held_by_me) 158 158 return nfserr_jukebox; 159 - ses->se_flags |= NFS4_SESSION_DEAD; 159 + ses->se_dead = true; 160 160 return nfs_ok; 161 161 } 162 162 ··· 572 572 return x; 573 573 } 574 574 575 - static void nfsd4_free_file_rcu(struct rcu_head *rcu) 576 - { 577 - struct nfs4_file *fp = container_of(rcu, struct nfs4_file, fi_rcu); 578 - 579 - kmem_cache_free(file_slab, fp); 580 - } 581 - 582 575 void 583 576 put_nfs4_file(struct nfs4_file *fi) 584 577 { ··· 579 586 nfsd4_file_hash_remove(fi); 580 587 WARN_ON_ONCE(!list_empty(&fi->fi_clnt_odstate)); 581 588 WARN_ON_ONCE(!list_empty(&fi->fi_delegations)); 582 - call_rcu(&fi->fi_rcu, nfsd4_free_file_rcu); 589 + kfree_rcu(fi, fi_rcu); 583 590 } 584 591 } 585 592 ··· 1177 1184 nfsd4_init_cb(&dp->dl_cb_fattr.ncf_getattr, dp->dl_stid.sc_client, 1178 1185 &nfsd4_cb_getattr_ops, NFSPROC4_CLNT_CB_GETATTR); 1179 1186 dp->dl_cb_fattr.ncf_file_modified = false; 1180 - dp->dl_cb_fattr.ncf_cb_bmap[0] = FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE; 1181 1187 get_nfs4_file(fp); 1182 1188 dp->dl_stid.sc_file = fp; 1183 1189 return dp; ··· 1652 1660 free_ol_stateid_reaplist(&reaplist); 1653 1661 } 1654 1662 1663 + static bool nfs4_openowner_unhashed(struct nfs4_openowner *oo) 1664 + { 1665 + lockdep_assert_held(&oo->oo_owner.so_client->cl_lock); 1666 + 1667 + return list_empty(&oo->oo_owner.so_strhash) && 1668 + list_empty(&oo->oo_perclient); 1669 + } 1670 + 1655 1671 static void unhash_openowner_locked(struct nfs4_openowner *oo) 1656 1672 { 1657 1673 struct nfs4_client *clp = oo->oo_owner.so_client; ··· 2010 2010 } 2011 2011 2012 2012 memcpy(&new->se_fchannel, fattrs, sizeof(struct nfsd4_channel_attrs)); 2013 - memcpy(&new->se_bchannel, battrs, sizeof(struct nfsd4_channel_attrs)); 2014 - 2013 + new->se_cb_slot_avail = ~0U; 2014 + new->se_cb_highest_slot = min(battrs->maxreqs - 1, 2015 + NFSD_BC_SLOT_TABLE_SIZE - 1); 2016 + spin_lock_init(&new->se_lock); 2015 2017 return new; 2016 2018 out_free: 2017 2019 while (i--) ··· 2144 2142 2145 2143 INIT_LIST_HEAD(&new->se_conns); 2146 2144 2147 - new->se_cb_seq_nr = 1; 2148 - new->se_flags = cses->flags; 2145 + atomic_set(&new->se_ref, 0); 2146 + new->se_dead = false; 2149 2147 new->se_cb_prog = cses->callback_prog; 2150 2148 new->se_cb_sec = cses->cb_sec; 2151 - atomic_set(&new->se_ref, 0); 2149 + 2150 + for (idx = 0; idx < NFSD_BC_SLOT_TABLE_SIZE; ++idx) 2151 + new->se_cb_seq_nr[idx] = 1; 2152 + 2152 2153 idx = hash_sessionid(&new->se_sessionid); 2153 2154 list_add(&new->se_hash, &nn->sessionid_hashtbl[idx]); 2154 2155 spin_lock(&clp->cl_lock); ··· 2244 2239 return 1; 2245 2240 } 2246 2241 2247 - /* 2248 - * XXX Should we use a slab cache ? 2249 - * This type of memory management is somewhat inefficient, but we use it 2250 - * anyway since SETCLIENTID is not a common operation. 2251 - */ 2252 2242 static struct nfs4_client *alloc_client(struct xdr_netobj name, 2253 2243 struct nfsd_net *nn) 2254 2244 { 2255 2245 struct nfs4_client *clp; 2256 2246 int i; 2257 2247 2258 - if (atomic_read(&nn->nfs4_client_count) >= nn->nfs4_max_clients) { 2248 + if (atomic_read(&nn->nfs4_client_count) >= nn->nfs4_max_clients && 2249 + atomic_read(&nn->nfsd_courtesy_clients) > 0) 2259 2250 mod_delayed_work(laundry_wq, &nn->laundromat_work, 0); 2260 - return NULL; 2261 - } 2251 + 2262 2252 clp = kmem_cache_zalloc(client_slab, GFP_KERNEL); 2263 2253 if (clp == NULL) 2264 2254 return NULL; ··· 3160 3160 kref_init(&clp->cl_nfsdfs.cl_ref); 3161 3161 nfsd4_init_cb(&clp->cl_cb_null, clp, NULL, NFSPROC4_CLNT_CB_NULL); 3162 3162 clp->cl_time = ktime_get_boottime_seconds(); 3163 - clear_bit(0, &clp->cl_cb_slot_busy); 3164 3163 copy_verf(clp, verf); 3165 3164 memcpy(&clp->cl_addr, sa, sizeof(struct sockaddr_storage)); 3166 3165 clp->cl_cb_session = NULL; ··· 3486 3487 #endif 3487 3488 || !list_empty(&clp->cl_delegations) 3488 3489 || !list_empty(&clp->cl_sessions) 3489 - || !list_empty(&clp->async_copies); 3490 + || nfsd4_has_active_async_copies(clp); 3490 3491 } 3491 3492 3492 3493 static __be32 copy_impl_id(struct nfs4_client *clp, ··· 3523 3524 "ip_addr=%s flags %x, spa_how %u\n", 3524 3525 __func__, rqstp, exid, exid->clname.len, exid->clname.data, 3525 3526 addr_str, exid->flags, exid->spa_how); 3527 + 3528 + exid->server_impl_name = kasprintf(GFP_KERNEL, "%s %s %s %s", 3529 + utsname()->sysname, utsname()->release, 3530 + utsname()->version, utsname()->machine); 3531 + if (!exid->server_impl_name) 3532 + return nfserr_jukebox; 3526 3533 3527 3534 if (exid->flags & ~EXCHGID4_FLAG_MASK_A) 3528 3535 return nfserr_inval; ··· 3667 3662 exid->seqid = conf->cl_cs_slot.sl_seqid + 1; 3668 3663 nfsd4_set_ex_flags(conf, exid); 3669 3664 3665 + exid->nii_domain.len = sizeof("kernel.org") - 1; 3666 + exid->nii_domain.data = "kernel.org"; 3667 + 3668 + /* 3669 + * Note that RFC 8881 places no length limit on 3670 + * nii_name, but this implementation permits no 3671 + * more than NFS4_OPAQUE_LIMIT bytes. 3672 + */ 3673 + exid->nii_name.len = strlen(exid->server_impl_name); 3674 + if (exid->nii_name.len > NFS4_OPAQUE_LIMIT) 3675 + exid->nii_name.len = NFS4_OPAQUE_LIMIT; 3676 + exid->nii_name.data = exid->server_impl_name; 3677 + 3678 + /* just send zeros - the date is in nii_name */ 3679 + exid->nii_time.tv_sec = 0; 3680 + exid->nii_time.tv_nsec = 0; 3681 + 3670 3682 dprintk("nfsd4_exchange_id seqid %d flags %x\n", 3671 3683 conf->cl_cs_slot.sl_seqid, conf->cl_exchange_flags); 3672 3684 status = nfs_ok; ··· 3698 3676 expire_client(unconf); 3699 3677 } 3700 3678 return status; 3679 + } 3680 + 3681 + void 3682 + nfsd4_exchange_id_release(union nfsd4_op_u *u) 3683 + { 3684 + struct nfsd4_exchange_id *exid = &u->exchange_id; 3685 + 3686 + kfree(exid->server_impl_name); 3701 3687 } 3702 3688 3703 3689 static __be32 check_slot_seqid(u32 seqid, u32 slot_seqid, bool slot_inuse) ··· 3941 3911 cr_ses->flags &= ~SESSION4_PERSIST; 3942 3912 /* Upshifting from TCP to RDMA is not supported */ 3943 3913 cr_ses->flags &= ~SESSION4_RDMA; 3914 + /* Report the correct number of backchannel slots */ 3915 + cr_ses->back_channel.maxreqs = new->se_cb_highest_slot + 1; 3944 3916 3945 3917 init_session(rqstp, new, conf, cr_ses); 3946 3918 nfsd4_get_session_locked(new); ··· 3963 3931 return status; 3964 3932 3965 3933 out_expired_error: 3966 - old = NULL; 3967 3934 /* 3968 3935 * Revert the slot seq_nr change so the server will process 3969 3936 * the client's resend instead of returning a cached response. ··· 3977 3946 out_free_conn: 3978 3947 spin_unlock(&nn->client_lock); 3979 3948 free_conn(conn); 3980 - if (old) 3981 - expire_client(old); 3982 3949 out_free_session: 3983 3950 __free_session(new); 3984 3951 out_release_drc_mem: ··· 5004 4975 spin_lock(&oo->oo_owner.so_client->cl_lock); 5005 4976 spin_lock(&fp->fi_lock); 5006 4977 4978 + if (nfs4_openowner_unhashed(oo)) { 4979 + mutex_unlock(&stp->st_mutex); 4980 + stp = NULL; 4981 + goto out_unlock; 4982 + } 4983 + 5007 4984 retstp = nfsd4_find_existing_open(fp, open); 5008 4985 if (retstp) 5009 4986 goto out_unlock; ··· 5992 5957 path.dentry = file_dentry(nf->nf_file); 5993 5958 5994 5959 rc = vfs_getattr(&path, stat, 5995 - (STATX_SIZE | STATX_CTIME | STATX_CHANGE_COOKIE), 5960 + (STATX_MODE | STATX_SIZE | STATX_CTIME | STATX_CHANGE_COOKIE), 5996 5961 AT_STATX_SYNC_AS_STAT); 5997 5962 5998 5963 nfsd_file_put(nf); ··· 6076 6041 } 6077 6042 open->op_delegate_type = NFS4_OPEN_DELEGATE_WRITE; 6078 6043 dp->dl_cb_fattr.ncf_cur_fsize = stat.size; 6079 - dp->dl_cb_fattr.ncf_initial_cinfo = 6080 - nfsd4_change_attribute(&stat, d_inode(currentfh->fh_dentry)); 6044 + dp->dl_cb_fattr.ncf_initial_cinfo = nfsd4_change_attribute(&stat); 6081 6045 trace_nfsd_deleg_write(&dp->dl_stid.sc_stateid); 6082 6046 } else { 6083 6047 open->op_delegate_type = NFS4_OPEN_DELEGATE_READ; ··· 6161 6127 6162 6128 if (!stp) { 6163 6129 stp = init_open_stateid(fp, open); 6130 + if (!stp) { 6131 + status = nfserr_jukebox; 6132 + goto out; 6133 + } 6134 + 6164 6135 if (!open->op_stp) 6165 6136 new_stp = true; 6166 6137 } ··· 6601 6562 _free_cpntf_state_locked(nn, cps); 6602 6563 } 6603 6564 spin_unlock(&nn->s2s_cp_lock); 6565 + nfsd4_async_copy_reaper(nn); 6604 6566 nfs4_get_client_reaplist(nn, &reaplist, &lt); 6605 6567 nfs4_process_client_reaplist(&reaplist); 6606 6568 ··· 7983 7943 if (check_lock_length(lock->lk_offset, lock->lk_length)) 7984 7944 return nfserr_inval; 7985 7945 7986 - if ((status = fh_verify(rqstp, &cstate->current_fh, 7987 - S_IFREG, NFSD_MAY_LOCK))) { 7988 - dprintk("NFSD: nfsd4_lock: permission denied!\n"); 7946 + status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0); 7947 + if (status != nfs_ok) 7989 7948 return status; 7990 - } 7991 7949 sb = cstate->current_fh.fh_dentry->d_sb; 7992 7950 7993 7951 if (lock->lk_is_new) { ··· 8892 8854 * nfsd4_deleg_getattr_conflict - Recall if GETATTR causes conflict 8893 8855 * @rqstp: RPC transaction context 8894 8856 * @dentry: dentry of inode to be checked for a conflict 8895 - * @modified: return true if file was modified 8896 - * @size: new size of file if modified is true 8857 + * @pdp: returned WRITE delegation, if one was found 8897 8858 * 8898 8859 * This function is called when there is a conflict between a write 8899 8860 * delegation and a change/size GETATTR from another client. The server ··· 8902 8865 * 18.7.4. 8903 8866 * 8904 8867 * Returns 0 if there is no conflict; otherwise an nfs_stat 8905 - * code is returned. 8868 + * code is returned. If @pdp is set to a non-NULL value, then the 8869 + * caller must put the reference. 8906 8870 */ 8907 8871 __be32 8908 8872 nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry, 8909 - bool *modified, u64 *size) 8873 + struct nfs4_delegation **pdp) 8910 8874 { 8911 8875 __be32 status; 8912 8876 struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); ··· 8918 8880 struct nfs4_cb_fattr *ncf; 8919 8881 struct inode *inode = d_inode(dentry); 8920 8882 8921 - *modified = false; 8922 8883 ctx = locks_inode_context(inode); 8923 8884 if (!ctx) 8924 - return 0; 8885 + return nfs_ok; 8925 8886 8926 8887 #define NON_NFSD_LEASE ((void *)1) 8927 8888 ··· 8986 8949 goto out_status; 8987 8950 } 8988 8951 ncf->ncf_cur_fsize = ncf->ncf_cb_fsize; 8989 - *size = ncf->ncf_cur_fsize; 8990 - *modified = true; 8952 + *pdp = dp; 8953 + return nfs_ok; 8991 8954 } 8992 - status = 0; 8955 + status = nfs_ok; 8993 8956 out_status: 8994 8957 nfs4_put_stid(&dp->dl_stid); 8995 8958 return status;
+43 -30
fs/nfsd/nfs4xdr.c
··· 2652 2652 2653 2653 strlen = end - str; 2654 2654 if (strlen) { 2655 - p = xdr_reserve_space(xdr, strlen + 4); 2656 - if (!p) 2655 + if (xdr_stream_encode_opaque(xdr, str, strlen) < 0) 2657 2656 return nfserr_resource; 2658 - p = xdr_encode_opaque(p, str, strlen); 2659 2657 count++; 2660 - } 2661 - else 2658 + } else 2662 2659 end++; 2663 2660 if (found_esc) 2664 2661 end = next; ··· 2696 2699 const struct path *path) 2697 2700 { 2698 2701 struct path cur = *path; 2699 - __be32 *p; 2700 2702 struct dentry **components = NULL; 2701 2703 unsigned int ncomponents = 0; 2702 2704 __be32 err = nfserr_jukebox; ··· 2726 2730 components[ncomponents++] = cur.dentry; 2727 2731 cur.dentry = dget_parent(cur.dentry); 2728 2732 } 2729 - err = nfserr_resource; 2730 - p = xdr_reserve_space(xdr, 4); 2731 - if (!p) 2732 - goto out_free; 2733 - *p++ = cpu_to_be32(ncomponents); 2734 2733 2734 + err = nfserr_resource; 2735 + if (xdr_stream_encode_u32(xdr, ncomponents) != XDR_UNIT) 2736 + goto out_free; 2735 2737 while (ncomponents) { 2736 2738 struct dentry *dentry = components[ncomponents - 1]; 2737 - unsigned int len; 2738 2739 2739 2740 spin_lock(&dentry->d_lock); 2740 - len = dentry->d_name.len; 2741 - p = xdr_reserve_space(xdr, len + 4); 2742 - if (!p) { 2741 + if (xdr_stream_encode_opaque(xdr, dentry->d_name.name, 2742 + dentry->d_name.len) < 0) { 2743 2743 spin_unlock(&dentry->d_lock); 2744 2744 goto out_free; 2745 2745 } 2746 - p = xdr_encode_opaque(p, dentry->d_name.name, len); 2747 2746 dprintk("/%pd", dentry); 2748 2747 spin_unlock(&dentry->d_lock); 2749 2748 dput(dentry); ··· 2919 2928 struct kstat stat; 2920 2929 struct kstatfs statfs; 2921 2930 struct nfs4_acl *acl; 2922 - u64 size; 2923 2931 #ifdef CONFIG_NFSD_V4_SECURITY_LABEL 2924 2932 void *context; 2925 2933 int contextlen; ··· 3030 3040 return nfs_ok; 3031 3041 } 3032 3042 3033 - c = nfsd4_change_attribute(&args->stat, d_inode(args->dentry)); 3043 + c = nfsd4_change_attribute(&args->stat); 3034 3044 return nfsd4_encode_changeid4(xdr, c); 3035 3045 } 3036 3046 3037 3047 static __be32 nfsd4_encode_fattr4_size(struct xdr_stream *xdr, 3038 3048 const struct nfsd4_fattr_args *args) 3039 3049 { 3040 - return nfsd4_encode_uint64_t(xdr, args->size); 3050 + return nfsd4_encode_uint64_t(xdr, args->stat.size); 3041 3051 } 3042 3052 3043 3053 static __be32 nfsd4_encode_fattr4_fsid(struct xdr_stream *xdr, ··· 3502 3512 int ignore_crossmnt) 3503 3513 { 3504 3514 DECLARE_BITMAP(attr_bitmap, ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops)); 3515 + struct nfs4_delegation *dp = NULL; 3505 3516 struct nfsd4_fattr_args args; 3506 3517 struct svc_fh *tempfh = NULL; 3507 3518 int starting_len = xdr->buf->len; ··· 3517 3526 .dentry = dentry, 3518 3527 }; 3519 3528 unsigned long bit; 3520 - bool file_modified = false; 3521 - u64 size = 0; 3522 3529 3523 3530 WARN_ON_ONCE(bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1); 3524 3531 WARN_ON_ONCE(!nfsd_attrs_supported(minorversion, bmval)); ··· 3544 3555 if (status) 3545 3556 goto out; 3546 3557 } 3547 - args.size = 0; 3548 3558 if (attrmask[0] & (FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE)) { 3549 - status = nfsd4_deleg_getattr_conflict(rqstp, dentry, 3550 - &file_modified, &size); 3559 + status = nfsd4_deleg_getattr_conflict(rqstp, dentry, &dp); 3551 3560 if (status) 3552 3561 goto out; 3553 3562 } ··· 3553 3566 err = vfs_getattr(&path, &args.stat, 3554 3567 STATX_BASIC_STATS | STATX_BTIME | STATX_CHANGE_COOKIE, 3555 3568 AT_STATX_SYNC_AS_STAT); 3569 + if (dp) { 3570 + struct nfs4_cb_fattr *ncf = &dp->dl_cb_fattr; 3571 + 3572 + if (ncf->ncf_file_modified) 3573 + args.stat.size = ncf->ncf_cur_fsize; 3574 + 3575 + nfs4_put_stid(&dp->dl_stid); 3576 + } 3556 3577 if (err) 3557 3578 goto out_nfserr; 3558 - if (file_modified) 3559 - args.size = size; 3560 - else 3561 - args.size = args.stat.size; 3562 3579 3563 3580 if (!(args.stat.result_mask & STATX_BTIME)) 3564 3581 /* underlying FS does not offer btime so we can't share it */ ··· 3758 3767 nfserr = nfserrno(err); 3759 3768 goto out_put; 3760 3769 } 3761 - nfserr = check_nfsd_access(exp, cd->rd_rqstp); 3770 + nfserr = check_nfsd_access(exp, cd->rd_rqstp, false); 3762 3771 if (nfserr) 3763 3772 goto out_put; 3764 3773 ··· 4817 4826 } 4818 4827 4819 4828 static __be32 4829 + nfsd4_encode_nfs_impl_id4(struct xdr_stream *xdr, struct nfsd4_exchange_id *exid) 4830 + { 4831 + __be32 status; 4832 + 4833 + /* nii_domain */ 4834 + status = nfsd4_encode_opaque(xdr, exid->nii_domain.data, 4835 + exid->nii_domain.len); 4836 + if (status != nfs_ok) 4837 + return status; 4838 + /* nii_name */ 4839 + status = nfsd4_encode_opaque(xdr, exid->nii_name.data, 4840 + exid->nii_name.len); 4841 + if (status != nfs_ok) 4842 + return status; 4843 + /* nii_time */ 4844 + return nfsd4_encode_nfstime4(xdr, &exid->nii_time); 4845 + } 4846 + 4847 + static __be32 4820 4848 nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr, 4821 4849 union nfsd4_op_u *u) 4822 4850 { ··· 4869 4859 if (nfserr != nfs_ok) 4870 4860 return nfserr; 4871 4861 /* eir_server_impl_id<1> */ 4872 - if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT) 4862 + if (xdr_stream_encode_u32(xdr, 1) != XDR_UNIT) 4873 4863 return nfserr_resource; 4864 + nfserr = nfsd4_encode_nfs_impl_id4(xdr, exid); 4865 + if (nfserr != nfs_ok) 4866 + return nfserr; 4874 4867 4875 4868 return nfs_ok; 4876 4869 }
+22 -19
fs/nfsd/nfsfh.c
··· 320 320 { 321 321 struct nfsd_net *nn = net_generic(net, nfsd_net_id); 322 322 struct svc_export *exp = NULL; 323 + bool may_bypass_gss = false; 323 324 struct dentry *dentry; 324 325 __be32 error; 325 326 ··· 363 362 if (error) 364 363 goto out; 365 364 366 - /* 367 - * pseudoflavor restrictions are not enforced on NLM, 368 - * which clients virtually always use auth_sys for, 369 - * even while using RPCSEC_GSS for NFS. 370 - */ 371 - if (access & NFSD_MAY_LOCK || access & NFSD_MAY_BYPASS_GSS) 372 - goto skip_pseudoflavor_check; 365 + if ((access & NFSD_MAY_NLM) && (exp->ex_flags & NFSEXP_NOAUTHNLM)) 366 + /* NLM is allowed to fully bypass authentication */ 367 + goto out; 368 + 369 + if (access & NFSD_MAY_BYPASS_GSS) 370 + may_bypass_gss = true; 373 371 /* 374 372 * Clients may expect to be able to use auth_sys during mount, 375 373 * even if they use gss for everything else; see section 2.3.2 ··· 376 376 */ 377 377 if (access & NFSD_MAY_BYPASS_GSS_ON_ROOT 378 378 && exp->ex_path.dentry == dentry) 379 - goto skip_pseudoflavor_check; 379 + may_bypass_gss = true; 380 380 381 - error = check_nfsd_access(exp, rqstp); 381 + error = check_nfsd_access(exp, rqstp, may_bypass_gss); 382 382 if (error) 383 383 goto out; 384 384 385 - skip_pseudoflavor_check: 386 385 /* Finally, check access permissions. */ 387 386 error = nfsd_permission(cred, exp, dentry, access); 388 387 out: ··· 666 667 __be32 __must_check fh_fill_pre_attrs(struct svc_fh *fhp) 667 668 { 668 669 bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE); 669 - struct inode *inode; 670 670 struct kstat stat; 671 671 __be32 err; 672 672 673 673 if (fhp->fh_no_wcc || fhp->fh_pre_saved) 674 674 return nfs_ok; 675 675 676 - inode = d_inode(fhp->fh_dentry); 677 676 err = fh_getattr(fhp, &stat); 678 677 if (err) 679 678 return err; 680 679 681 680 if (v4) 682 - fhp->fh_pre_change = nfsd4_change_attribute(&stat, inode); 681 + fhp->fh_pre_change = nfsd4_change_attribute(&stat); 683 682 684 683 fhp->fh_pre_mtime = stat.mtime; 685 684 fhp->fh_pre_ctime = stat.ctime; ··· 694 697 __be32 fh_fill_post_attrs(struct svc_fh *fhp) 695 698 { 696 699 bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE); 697 - struct inode *inode = d_inode(fhp->fh_dentry); 698 700 __be32 err; 699 701 700 702 if (fhp->fh_no_wcc) ··· 709 713 fhp->fh_post_saved = true; 710 714 if (v4) 711 715 fhp->fh_post_change = 712 - nfsd4_change_attribute(&fhp->fh_post_attr, inode); 716 + nfsd4_change_attribute(&fhp->fh_post_attr); 713 717 return nfs_ok; 714 718 } 715 719 ··· 766 770 struct knfsd_fh *fh = &fhp->fh_handle; 767 771 static char buf[2+1+1+64*3+1]; 768 772 769 - if (fh->fh_size < 0 || fh->fh_size> 64) 773 + if (fh->fh_size > 64) 770 774 return "bad-fh"; 771 775 sprintf(buf, "%d: %*ph", fh->fh_size, fh->fh_size, fh->fh_raw); 772 776 return buf; ··· 800 804 return FSIDSOURCE_DEV; 801 805 } 802 806 803 - /* 807 + /** 808 + * nfsd4_change_attribute - Generate an NFSv4 change_attribute value 809 + * @stat: inode attributes 810 + * 811 + * Caller must fill in @stat before calling, typically by invoking 812 + * vfs_getattr() with STATX_MODE, STATX_CTIME, and STATX_CHANGE_COOKIE. 813 + * Returns an unsigned 64-bit changeid4 value (RFC 8881 Section 3.2). 814 + * 804 815 * We could use i_version alone as the change attribute. However, i_version 805 816 * can go backwards on a regular file after an unclean shutdown. On its own 806 817 * that doesn't necessarily cause a problem, but if i_version goes backwards ··· 824 821 * assume that the new change attr is always logged to stable storage in some 825 822 * fashion before the results can be seen. 826 823 */ 827 - u64 nfsd4_change_attribute(const struct kstat *stat, const struct inode *inode) 824 + u64 nfsd4_change_attribute(const struct kstat *stat) 828 825 { 829 826 u64 chattr; 830 827 831 828 if (stat->result_mask & STATX_CHANGE_COOKIE) { 832 829 chattr = stat->change_cookie; 833 - if (S_ISREG(inode->i_mode) && 830 + if (S_ISREG(stat->mode) && 834 831 !(stat->attributes & STATX_ATTR_CHANGE_MONOTONIC)) { 835 832 chattr += (u64)stat->ctime.tv_sec << 30; 836 833 chattr += stat->ctime.tv_nsec;
+1 -2
fs/nfsd/nfsfh.h
··· 297 297 fhp->fh_pre_saved = false; 298 298 } 299 299 300 - u64 nfsd4_change_attribute(const struct kstat *stat, 301 - const struct inode *inode); 300 + u64 nfsd4_change_attribute(const struct kstat *stat); 302 301 __be32 __must_check fh_fill_pre_attrs(struct svc_fh *fhp); 303 302 __be32 fh_fill_post_attrs(struct svc_fh *fhp); 304 303 __be32 __must_check fh_fill_both_attrs(struct svc_fh *fhp);
+28 -12
fs/nfsd/state.h
··· 71 71 struct work_struct cb_work; 72 72 int cb_seq_status; 73 73 int cb_status; 74 + int cb_held_slot; 74 75 bool cb_need_restart; 75 - bool cb_holds_slot; 76 76 }; 77 77 78 78 struct nfsd4_callback_ops { ··· 137 137 time64_t cpntf_time; /* last time stateid used */ 138 138 }; 139 139 140 + /* 141 + * RFC 7862 Section 4.8 states: 142 + * 143 + * | A copy offload stateid will be valid until either (A) the client 144 + * | or server restarts or (B) the client returns the resource by 145 + * | issuing an OFFLOAD_CANCEL operation or the client replies to a 146 + * | CB_OFFLOAD operation. 147 + * 148 + * Because a client might not reply to a CB_OFFLOAD, or a reply 149 + * might get lost due to connection loss, NFSD purges async copy 150 + * state after a short period to prevent it from accumulating 151 + * over time. 152 + */ 153 + #define NFSD_COPY_INITIAL_TTL 10 154 + 140 155 struct nfs4_cb_fattr { 141 156 struct nfsd4_callback ncf_getattr; 142 157 u32 ncf_cb_status; 143 - u32 ncf_cb_bmap[1]; 144 158 145 159 /* from CB_GETATTR reply */ 146 160 u64 ncf_cb_change; ··· 304 290 unsigned char cn_flags; 305 291 }; 306 292 293 + /* Maximum number of slots that nfsd will use in the backchannel */ 294 + #define NFSD_BC_SLOT_TABLE_SIZE (sizeof(u32) * 8) 295 + 307 296 /* 308 297 * Representation of a v4.1+ session. These are refcounted in a similar fashion 309 298 * to the nfs4_client. References are only taken when the server is actively ··· 314 297 */ 315 298 struct nfsd4_session { 316 299 atomic_t se_ref; 300 + spinlock_t se_lock; 301 + u32 se_cb_slot_avail; /* bitmap of available slots */ 302 + u32 se_cb_highest_slot; /* highest slot client wants */ 303 + u32 se_cb_prog; 304 + bool se_dead; 317 305 struct list_head se_hash; /* hash by sessionid */ 318 306 struct list_head se_perclnt; 319 - /* See SESSION4_PERSIST, etc. for standard flags; this is internal-only: */ 320 - #define NFS4_SESSION_DEAD 0x010 321 - u32 se_flags; 322 307 struct nfs4_client *se_client; 323 308 struct nfs4_sessionid se_sessionid; 324 309 struct nfsd4_channel_attrs se_fchannel; 325 - struct nfsd4_channel_attrs se_bchannel; 326 310 struct nfsd4_cb_sec se_cb_sec; 327 311 struct list_head se_conns; 328 - u32 se_cb_prog; 329 - u32 se_cb_seq_nr; 312 + u32 se_cb_seq_nr[NFSD_BC_SLOT_TABLE_SIZE]; 330 313 struct nfsd4_slot *se_slots[]; /* forward channel slots */ 331 314 }; 332 315 ··· 460 443 */ 461 444 struct dentry *cl_nfsd_info_dentry; 462 445 463 - /* for nfs41 callbacks */ 464 - /* We currently support a single back channel with a single slot */ 465 - unsigned long cl_cb_slot_busy; 466 446 struct rpc_wait_queue cl_cb_waitq; /* backchannel callers may */ 467 447 /* wait here for slots */ 468 448 struct net *net; ··· 756 742 extern bool nfsd4_run_cb(struct nfsd4_callback *cb); 757 743 extern void nfsd4_shutdown_callback(struct nfs4_client *); 758 744 extern void nfsd4_shutdown_copy(struct nfs4_client *clp); 745 + void nfsd4_async_copy_reaper(struct nfsd_net *nn); 746 + bool nfsd4_has_active_async_copies(struct nfs4_client *clp); 759 747 extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(struct xdr_netobj name, 760 748 struct xdr_netobj princhash, struct nfsd_net *nn); 761 749 extern bool nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn); ··· 800 784 } 801 785 802 786 extern __be32 nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, 803 - struct dentry *dentry, bool *file_modified, u64 *size); 787 + struct dentry *dentry, struct nfs4_delegation **pdp); 804 788 #endif /* NFSD4_STATE_H */
+25 -4
fs/nfsd/trace.h
··· 79 79 { NFSD_MAY_READ, "READ" }, \ 80 80 { NFSD_MAY_SATTR, "SATTR" }, \ 81 81 { NFSD_MAY_TRUNC, "TRUNC" }, \ 82 - { NFSD_MAY_LOCK, "LOCK" }, \ 82 + { NFSD_MAY_NLM, "NLM" }, \ 83 83 { NFSD_MAY_OWNER_OVERRIDE, "OWNER_OVERRIDE" }, \ 84 84 { NFSD_MAY_LOCAL_ACCESS, "LOCAL_ACCESS" }, \ 85 85 { NFSD_MAY_BYPASS_GSS_ON_ROOT, "BYPASS_GSS_ON_ROOT" }, \ ··· 163 163 __entry->opnum, __entry->status) 164 164 ); 165 165 166 - TRACE_EVENT(nfsd_compound_encode_err, 166 + DECLARE_EVENT_CLASS(nfsd_compound_err_class, 167 167 TP_PROTO( 168 168 const struct svc_rqst *rqstp, 169 169 u32 opnum, ··· 183 183 TP_printk("opnum=%u status=%lu", 184 184 __entry->opnum, __entry->status) 185 185 ); 186 + 187 + #define DEFINE_NFSD_COMPOUND_ERR_EVENT(name) \ 188 + DEFINE_EVENT(nfsd_compound_err_class, nfsd_compound_##name##_err, \ 189 + TP_PROTO( \ 190 + const struct svc_rqst *rqstp, \ 191 + u32 opnum, \ 192 + __be32 status \ 193 + ), \ 194 + TP_ARGS(rqstp, opnum, status)) 195 + 196 + DEFINE_NFSD_COMPOUND_ERR_EVENT(op); 197 + DEFINE_NFSD_COMPOUND_ERR_EVENT(encode); 186 198 187 199 #define show_fs_file_type(x) \ 188 200 __print_symbolic(x, \ ··· 1697 1685 __entry->cl_id = sid->clientid.cl_id; 1698 1686 __entry->seqno = sid->sequence; 1699 1687 __entry->reserved = sid->reserved; 1700 - __entry->slot_seqno = session->se_cb_seq_nr; 1688 + __entry->slot_seqno = session->se_cb_seq_nr[cb->cb_held_slot]; 1701 1689 ), 1702 1690 TP_printk(SUNRPC_TRACE_TASK_SPECIFIER 1703 1691 " sessionid=%08x:%08x:%08x:%08x new slot seqno=%u", ··· 2244 2232 ) 2245 2233 ); 2246 2234 2247 - TRACE_EVENT(nfsd_copy_async_done, 2235 + DECLARE_EVENT_CLASS(nfsd_copy_async_done_class, 2248 2236 TP_PROTO( 2249 2237 const struct nfsd4_copy *copy 2250 2238 ), ··· 2312 2300 __entry->src_cp_pos, __entry->dst_cp_pos, __entry->cp_count 2313 2301 ) 2314 2302 ); 2303 + 2304 + #define DEFINE_COPY_ASYNC_DONE_EVENT(name) \ 2305 + DEFINE_EVENT(nfsd_copy_async_done_class, \ 2306 + nfsd_copy_async_##name, \ 2307 + TP_PROTO(const struct nfsd4_copy *copy), \ 2308 + TP_ARGS(copy)) 2309 + 2310 + DEFINE_COPY_ASYNC_DONE_EVENT(done); 2311 + DEFINE_COPY_ASYNC_DONE_EVENT(cancel); 2315 2312 2316 2313 #endif /* _NFSD_TRACE_H */ 2317 2314
+6 -20
fs/nfsd/vfs.c
··· 35 35 #include "xdr3.h" 36 36 37 37 #ifdef CONFIG_NFSD_V4 38 - #include "../internal.h" 39 38 #include "acl.h" 40 39 #include "idmap.h" 41 40 #include "xdr4.h" ··· 320 321 err = nfsd_lookup_dentry(rqstp, fhp, name, len, &exp, &dentry); 321 322 if (err) 322 323 return err; 323 - err = check_nfsd_access(exp, rqstp); 324 + err = check_nfsd_access(exp, rqstp, false); 324 325 if (err) 325 326 goto out; 326 327 /* ··· 860 861 * N.B. After this call fhp needs an fh_put 861 862 */ 862 863 static int 863 - __nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, 864 - int may_flags, struct file **filp) 864 + __nfsd_open(struct svc_fh *fhp, umode_t type, int may_flags, struct file **filp) 865 865 { 866 866 struct path path; 867 867 struct inode *inode; ··· 930 932 retry: 931 933 err = fh_verify(rqstp, fhp, type, may_flags); 932 934 if (!err) { 933 - host_err = __nfsd_open(rqstp, fhp, type, may_flags, filp); 935 + host_err = __nfsd_open(fhp, type, may_flags, filp); 934 936 if (host_err == -EOPENSTALE && !retried) { 935 937 retried = true; 936 938 fh_put(fhp); ··· 943 945 944 946 /** 945 947 * nfsd_open_verified - Open a regular file for the filecache 946 - * @rqstp: RPC request 947 948 * @fhp: NFS filehandle of the file to open 948 949 * @may_flags: internal permission flags 949 950 * @filp: OUT: open "struct file *" ··· 950 953 * Returns zero on success, or a negative errno value. 951 954 */ 952 955 int 953 - nfsd_open_verified(struct svc_rqst *rqstp, struct svc_fh *fhp, int may_flags, 954 - struct file **filp) 956 + nfsd_open_verified(struct svc_fh *fhp, int may_flags, struct file **filp) 955 957 { 956 - return __nfsd_open(rqstp, fhp, S_IFREG, may_flags, filp); 958 + return __nfsd_open(fhp, S_IFREG, may_flags, filp); 957 959 } 958 960 959 961 /* ··· 2505 2509 (acc & NFSD_MAY_EXEC)? " exec" : "", 2506 2510 (acc & NFSD_MAY_SATTR)? " sattr" : "", 2507 2511 (acc & NFSD_MAY_TRUNC)? " trunc" : "", 2508 - (acc & NFSD_MAY_LOCK)? " lock" : "", 2512 + (acc & NFSD_MAY_NLM)? " nlm" : "", 2509 2513 (acc & NFSD_MAY_OWNER_OVERRIDE)? " owneroverride" : "", 2510 2514 inode->i_mode, 2511 2515 IS_IMMUTABLE(inode)? " immut" : "", ··· 2530 2534 if ((acc & NFSD_MAY_TRUNC) && IS_APPEND(inode)) 2531 2535 return nfserr_perm; 2532 2536 2533 - if (acc & NFSD_MAY_LOCK) { 2534 - /* If we cannot rely on authentication in NLM requests, 2535 - * just allow locks, otherwise require read permission, or 2536 - * ownership 2537 - */ 2538 - if (exp->ex_flags & NFSEXP_NOAUTHNLM) 2539 - return 0; 2540 - else 2541 - acc = NFSD_MAY_READ | NFSD_MAY_OWNER_OVERRIDE; 2542 - } 2543 2537 /* 2544 2538 * The file owner always gets access permission for accesses that 2545 2539 * would normally be checked at open time. This is to make
+3 -3
fs/nfsd/vfs.h
··· 20 20 #define NFSD_MAY_READ 0x004 /* == MAY_READ */ 21 21 #define NFSD_MAY_SATTR 0x008 22 22 #define NFSD_MAY_TRUNC 0x010 23 - #define NFSD_MAY_LOCK 0x020 23 + #define NFSD_MAY_NLM 0x020 /* request is from lockd */ 24 24 #define NFSD_MAY_MASK 0x03f 25 25 26 26 /* extra hints to permission and open routines: */ ··· 114 114 int nfsd_open_break_lease(struct inode *, int); 115 115 __be32 nfsd_open(struct svc_rqst *, struct svc_fh *, umode_t, 116 116 int, struct file **); 117 - int nfsd_open_verified(struct svc_rqst *rqstp, struct svc_fh *fhp, 118 - int may_flags, struct file **filp); 117 + int nfsd_open_verified(struct svc_fh *fhp, int may_flags, 118 + struct file **filp); 119 119 __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp, 120 120 struct file *file, loff_t offset, 121 121 unsigned long *count,
+8
fs/nfsd/xdr4.h
··· 567 567 struct xdr_netobj nii_domain; 568 568 struct xdr_netobj nii_name; 569 569 struct timespec64 nii_time; 570 + char *server_impl_name; 570 571 }; 571 572 572 573 struct nfsd4_sequence { ··· 676 675 struct nfsd4_callback co_cb; 677 676 struct nfsd42_write_res co_res; 678 677 __be32 co_nfserr; 678 + unsigned int co_retries; 679 679 struct knfsd_fh co_fh; 680 680 }; 681 681 ··· 695 693 #define NFSD4_COPY_F_SYNCHRONOUS (2) 696 694 #define NFSD4_COPY_F_COMMITTED (3) 697 695 #define NFSD4_COPY_F_COMPLETED (4) 696 + #define NFSD4_COPY_F_OFFLOAD_DONE (5) 698 697 699 698 /* response */ 700 699 __be32 nfserr; 701 700 struct nfsd42_write_res cp_res; 702 701 struct knfsd_fh fh; 702 + 703 + /* offload callback */ 704 + struct nfsd4_cb_offload cp_cb_offload; 703 705 704 706 struct nfs4_client *cp_clp; 705 707 ··· 715 709 struct list_head copies; 716 710 struct task_struct *copy_task; 717 711 refcount_t refcount; 712 + unsigned int cp_ttl; 718 713 719 714 struct nfsd4_ssc_umount_item *ss_nsui; 720 715 struct nfs_fh c_fh; ··· 937 930 struct nfsd4_compound_state *, union nfsd4_op_u *u); 938 931 extern __be32 nfsd4_setclientid_confirm(struct svc_rqst *rqstp, 939 932 struct nfsd4_compound_state *, union nfsd4_op_u *u); 933 + void nfsd4_exchange_id_release(union nfsd4_op_u *u); 940 934 extern __be32 nfsd4_exchange_id(struct svc_rqst *rqstp, 941 935 struct nfsd4_compound_state *, union nfsd4_op_u *u); 942 936 extern __be32 nfsd4_backchannel_ctl(struct svc_rqst *,
+3 -3
include/linux/lockd/lockd.h
··· 278 278 struct nlm_host *, struct nlm_lock *, int, 279 279 struct nlm_cookie *, int); 280 280 __be32 nlmsvc_unlock(struct net *net, struct nlm_file *, struct nlm_lock *); 281 - __be32 nlmsvc_testlock(struct svc_rqst *, struct nlm_file *, 282 - struct nlm_host *, struct nlm_lock *, 283 - struct nlm_lock *, struct nlm_cookie *); 281 + __be32 nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, 282 + struct nlm_host *host, struct nlm_lock *lock, 283 + struct nlm_lock *conflock); 284 284 __be32 nlmsvc_cancel_blocked(struct net *net, struct nlm_file *, struct nlm_lock *); 285 285 void nlmsvc_retry_blocked(struct svc_rqst *rqstp); 286 286 void nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *,
-2
include/linux/lockd/xdr.h
··· 73 73 u32 fsm_mode; 74 74 }; 75 75 76 - typedef struct nlm_args nlm_args; 77 - 78 76 /* 79 77 * Generic lockd result 80 78 */
+15 -3
include/linux/nfslocalio.h
··· 55 55 const struct cred *, 56 56 const struct nfs_fh *, 57 57 const fmode_t); 58 - void (*nfsd_file_put_local)(struct nfsd_file *); 58 + struct net *(*nfsd_file_put_local)(struct nfsd_file *); 59 59 struct file *(*nfsd_file_file)(struct nfsd_file *); 60 60 } ____cacheline_aligned; 61 61 ··· 66 66 struct rpc_clnt *, const struct cred *, 67 67 const struct nfs_fh *, const fmode_t); 68 68 69 - static inline void nfs_to_nfsd_file_put_local(struct nfsd_file *localio) 69 + static inline void nfs_to_nfsd_net_put(struct net *net) 70 70 { 71 71 /* 72 72 * Once reference to nfsd_serv is dropped, NFSD could be ··· 74 74 * by always taking RCU. 75 75 */ 76 76 rcu_read_lock(); 77 - nfs_to->nfsd_file_put_local(localio); 77 + nfs_to->nfsd_serv_put(net); 78 78 rcu_read_unlock(); 79 + } 80 + 81 + static inline void nfs_to_nfsd_file_put_local(struct nfsd_file *localio) 82 + { 83 + /* 84 + * Must not hold RCU otherwise nfsd_file_put() can easily trigger: 85 + * "Voluntary context switch within RCU read-side critical section!" 86 + * by scheduling deep in underlying filesystem (e.g. XFS). 87 + */ 88 + struct net *net = nfs_to->nfsd_file_put_local(localio); 89 + 90 + nfs_to_nfsd_net_put(net); 79 91 } 80 92 81 93 #else /* CONFIG_NFS_LOCALIO */
+21
include/linux/sunrpc/xdr.h
··· 681 681 } 682 682 683 683 /** 684 + * xdr_stream_decode_be32 - Decode a big-endian 32-bit integer 685 + * @xdr: pointer to xdr_stream 686 + * @ptr: location to store integer 687 + * 688 + * Return values: 689 + * %0 on success 690 + * %-EBADMSG on XDR buffer overflow 691 + */ 692 + static inline ssize_t 693 + xdr_stream_decode_be32(struct xdr_stream *xdr, __be32 *ptr) 694 + { 695 + const size_t count = sizeof(*ptr); 696 + __be32 *p = xdr_inline_decode(xdr, count); 697 + 698 + if (unlikely(!p)) 699 + return -EBADMSG; 700 + *ptr = *p; 701 + return 0; 702 + } 703 + 704 + /** 684 705 * xdr_stream_decode_u64 - Decode a 64-bit integer 685 706 * @xdr: pointer to xdr_stream 686 707 * @ptr: location to store 64-bit integer
+9
include/linux/sunrpc/xdrgen/_defs.h
··· 23 23 u8 *data; 24 24 } opaque; 25 25 26 + #define XDR_void (0) 27 + #define XDR_bool (1) 28 + #define XDR_int (1) 29 + #define XDR_unsigned_int (1) 30 + #define XDR_long (1) 31 + #define XDR_unsigned_long (1) 32 + #define XDR_hyper (2) 33 + #define XDR_unsigned_hyper (2) 34 + 26 35 #endif /* _SUNRPC_XDRGEN__DEFS_H_ */
+3 -1
net/sunrpc/cache.c
··· 1427 1427 seq_printf(m, "# expiry=%lld refcnt=%d flags=%lx\n", 1428 1428 convert_to_wallclock(cp->expiry_time), 1429 1429 kref_read(&cp->ref), cp->flags); 1430 - cache_get(cp); 1430 + if (!cache_get_rcu(cp)) 1431 + return 0; 1432 + 1431 1433 if (cache_check(cd, cp, NULL)) 1432 1434 /* cache_check does a cache_put on failure */ 1433 1435 seq_puts(m, "# ");
+14 -5
net/sunrpc/xprtrdma/svc_rdma.c
··· 233 233 234 234 rc = percpu_counter_init(&svcrdma_stat_read, 0, GFP_KERNEL); 235 235 if (rc) 236 - goto out_err; 236 + goto err; 237 237 rc = percpu_counter_init(&svcrdma_stat_recv, 0, GFP_KERNEL); 238 238 if (rc) 239 - goto out_err; 239 + goto err_read; 240 240 rc = percpu_counter_init(&svcrdma_stat_sq_starve, 0, GFP_KERNEL); 241 241 if (rc) 242 - goto out_err; 242 + goto err_recv; 243 243 rc = percpu_counter_init(&svcrdma_stat_write, 0, GFP_KERNEL); 244 244 if (rc) 245 - goto out_err; 245 + goto err_sq; 246 246 247 247 svcrdma_table_header = register_sysctl("sunrpc/svc_rdma", 248 248 svcrdma_parm_table); 249 + if (!svcrdma_table_header) 250 + goto err_write; 251 + 249 252 return 0; 250 253 251 - out_err: 254 + err_write: 255 + rc = -ENOMEM; 256 + percpu_counter_destroy(&svcrdma_stat_write); 257 + err_sq: 252 258 percpu_counter_destroy(&svcrdma_stat_sq_starve); 259 + err_recv: 253 260 percpu_counter_destroy(&svcrdma_stat_recv); 261 + err_read: 254 262 percpu_counter_destroy(&svcrdma_stat_read); 263 + err: 255 264 return rc; 256 265 } 257 266
+7 -1
net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
··· 493 493 if (xdr_stream_decode_u32(&rctxt->rc_stream, &segcount)) 494 494 return false; 495 495 496 - /* A bogus segcount causes this buffer overflow check to fail. */ 496 + /* Before trusting the segcount value enough to use it in 497 + * a computation, perform a simple range check. This is an 498 + * arbitrary but sensible limit (ie, not architectural). 499 + */ 500 + if (unlikely(segcount > RPCSVC_MAXPAGES)) 501 + return false; 502 + 497 503 p = xdr_inline_decode(&rctxt->rc_stream, 498 504 segcount * rpcrdma_segment_maxsz * sizeof(*p)); 499 505 return p != NULL;
+11
tools/net/sunrpc/extract.sh
··· 1 + #! /bin/sh 2 + # SPDX-License-Identifier: GPL-2.0 3 + # 4 + # Extract an RPC protocol specification from an RFC document. 5 + # The version of this script comes from RFC 8166. 6 + # 7 + # Usage: 8 + # $ extract.sh < rfcNNNN.txt > protocol.x 9 + # 10 + 11 + grep '^ *///' | sed 's?^ */// ??' | sed 's?^ *///$??'
+17
tools/net/sunrpc/xdrgen/README
··· 150 150 encoding and decoding functions. Currently one directive is 151 151 implemented: "public". 152 152 153 + Pragma big_endian 154 + ------ ---------- 155 + 156 + pragma big_endian <enum> ; 157 + 158 + For variables that might contain only a small number values, it 159 + is more efficient to avoid the byte-swap when encoding or decoding 160 + on little-endian machines. Such is often the case with error status 161 + codes. For example: 162 + 163 + pragma big_endian nfsstat3; 164 + 165 + In this case, when generating an XDR struct or union containing a 166 + field of type "nfsstat3", xdrgen will make the type of that field 167 + "__be32" instead of "enum nfsstat3". XDR unions then switch on the 168 + non-byte-swapped value of that field. 169 + 153 170 Pragma exclude 154 171 ------ ------- 155 172
+4
tools/net/sunrpc/xdrgen/generators/__init__.py
··· 111 111 def emit_encoder(self, node: _XdrAst) -> None: 112 112 """Emit one encoder function for this XDR type""" 113 113 raise NotImplementedError("Encoder generation not supported") 114 + 115 + def emit_maxsize(self, node: _XdrAst) -> None: 116 + """Emit one maxsize macro for this XDR type""" 117 + raise NotImplementedError("Maxsize macro generation not supported")
+25 -5
tools/net/sunrpc/xdrgen/generators/enum.py
··· 4 4 """Generate code to handle XDR enum types""" 5 5 6 6 from generators import SourceGenerator, create_jinja2_environment 7 - from xdr_ast import _XdrEnum, public_apis 7 + from xdr_ast import _XdrEnum, public_apis, big_endian, get_header_name 8 8 9 9 10 10 class XdrEnumGenerator(SourceGenerator): ··· 18 18 def emit_declaration(self, node: _XdrEnum) -> None: 19 19 """Emit one declaration pair for an XDR enum type""" 20 20 if node.name in public_apis: 21 - template = self.environment.get_template("declaration/close.j2") 21 + template = self.environment.get_template("declaration/enum.j2") 22 22 print(template.render(name=node.name)) 23 23 24 24 def emit_definition(self, node: _XdrEnum) -> None: ··· 30 30 for enumerator in node.enumerators: 31 31 print(template.render(name=enumerator.name, value=enumerator.value)) 32 32 33 - template = self.environment.get_template("definition/close.j2") 33 + if node.name in big_endian: 34 + template = self.environment.get_template("definition/close_be.j2") 35 + else: 36 + template = self.environment.get_template("definition/close.j2") 34 37 print(template.render(name=node.name)) 35 38 36 39 def emit_decoder(self, node: _XdrEnum) -> None: 37 40 """Emit one decoder function for an XDR enum type""" 38 - template = self.environment.get_template("decoder/enum.j2") 41 + if node.name in big_endian: 42 + template = self.environment.get_template("decoder/enum_be.j2") 43 + else: 44 + template = self.environment.get_template("decoder/enum.j2") 39 45 print(template.render(name=node.name)) 40 46 41 47 def emit_encoder(self, node: _XdrEnum) -> None: 42 48 """Emit one encoder function for an XDR enum type""" 43 - template = self.environment.get_template("encoder/enum.j2") 49 + if node.name in big_endian: 50 + template = self.environment.get_template("encoder/enum_be.j2") 51 + else: 52 + template = self.environment.get_template("encoder/enum.j2") 44 53 print(template.render(name=node.name)) 54 + 55 + def emit_maxsize(self, node: _XdrEnum) -> None: 56 + """Emit one maxsize macro for an XDR enum type""" 57 + macro_name = get_header_name().upper() + "_" + node.name + "_sz" 58 + template = self.environment.get_template("maxsize/enum.j2") 59 + print( 60 + template.render( 61 + macro=macro_name, 62 + width=" + ".join(node.symbolic_width()), 63 + ) 64 + )
+21 -5
tools/net/sunrpc/xdrgen/generators/pointer.py
··· 8 8 from generators import SourceGenerator, kernel_c_type 9 9 from generators import create_jinja2_environment, get_jinja2_template 10 10 11 - from xdr_ast import _XdrBasic, _XdrVariableLengthString 11 + from xdr_ast import _XdrBasic, _XdrString 12 12 from xdr_ast import _XdrFixedLengthOpaque, _XdrVariableLengthOpaque 13 13 from xdr_ast import _XdrFixedLengthArray, _XdrVariableLengthArray 14 14 from xdr_ast import _XdrOptionalData, _XdrPointer, _XdrDeclaration 15 - from xdr_ast import public_apis 15 + from xdr_ast import public_apis, get_header_name 16 16 17 17 18 18 def emit_pointer_declaration(environment: Environment, node: _XdrPointer) -> None: ··· 46 46 elif isinstance(field, _XdrVariableLengthOpaque): 47 47 template = get_jinja2_template(environment, "definition", field.template) 48 48 print(template.render(name=field.name)) 49 - elif isinstance(field, _XdrVariableLengthString): 49 + elif isinstance(field, _XdrString): 50 50 template = get_jinja2_template(environment, "definition", field.template) 51 51 print(template.render(name=field.name)) 52 52 elif isinstance(field, _XdrFixedLengthArray): ··· 119 119 maxsize=field.maxsize, 120 120 ) 121 121 ) 122 - elif isinstance(field, _XdrVariableLengthString): 122 + elif isinstance(field, _XdrString): 123 123 template = get_jinja2_template(environment, "decoder", field.template) 124 124 print( 125 125 template.render( ··· 198 198 maxsize=field.maxsize, 199 199 ) 200 200 ) 201 - elif isinstance(field, _XdrVariableLengthString): 201 + elif isinstance(field, _XdrString): 202 202 template = get_jinja2_template(environment, "encoder", field.template) 203 203 print( 204 204 template.render( ··· 247 247 print(template.render()) 248 248 249 249 250 + def emit_pointer_maxsize(environment: Environment, node: _XdrPointer) -> None: 251 + """Emit one maxsize macro for an XDR pointer type""" 252 + macro_name = get_header_name().upper() + "_" + node.name + "_sz" 253 + template = get_jinja2_template(environment, "maxsize", "pointer") 254 + print( 255 + template.render( 256 + macro=macro_name, 257 + width=" + ".join(node.symbolic_width()), 258 + ) 259 + ) 260 + 261 + 250 262 class XdrPointerGenerator(SourceGenerator): 251 263 """Generate source code for XDR pointer""" 252 264 ··· 282 270 def emit_encoder(self, node: _XdrPointer) -> None: 283 271 """Emit one encoder function for an XDR pointer type""" 284 272 emit_pointer_encoder(self.environment, node) 273 + 274 + def emit_maxsize(self, node: _XdrPointer) -> None: 275 + """Emit one maxsize macro for an XDR pointer type""" 276 + emit_pointer_maxsize(self.environment, node)
+21 -5
tools/net/sunrpc/xdrgen/generators/struct.py
··· 8 8 from generators import SourceGenerator, kernel_c_type 9 9 from generators import create_jinja2_environment, get_jinja2_template 10 10 11 - from xdr_ast import _XdrBasic, _XdrVariableLengthString 11 + from xdr_ast import _XdrBasic, _XdrString 12 12 from xdr_ast import _XdrFixedLengthOpaque, _XdrVariableLengthOpaque 13 13 from xdr_ast import _XdrFixedLengthArray, _XdrVariableLengthArray 14 14 from xdr_ast import _XdrOptionalData, _XdrStruct, _XdrDeclaration 15 - from xdr_ast import public_apis 15 + from xdr_ast import public_apis, get_header_name 16 16 17 17 18 18 def emit_struct_declaration(environment: Environment, node: _XdrStruct) -> None: ··· 46 46 elif isinstance(field, _XdrVariableLengthOpaque): 47 47 template = get_jinja2_template(environment, "definition", field.template) 48 48 print(template.render(name=field.name)) 49 - elif isinstance(field, _XdrVariableLengthString): 49 + elif isinstance(field, _XdrString): 50 50 template = get_jinja2_template(environment, "definition", field.template) 51 51 print(template.render(name=field.name)) 52 52 elif isinstance(field, _XdrFixedLengthArray): ··· 119 119 maxsize=field.maxsize, 120 120 ) 121 121 ) 122 - elif isinstance(field, _XdrVariableLengthString): 122 + elif isinstance(field, _XdrString): 123 123 template = get_jinja2_template(environment, "decoder", field.template) 124 124 print( 125 125 template.render( ··· 198 198 maxsize=field.maxsize, 199 199 ) 200 200 ) 201 - elif isinstance(field, _XdrVariableLengthString): 201 + elif isinstance(field, _XdrString): 202 202 template = get_jinja2_template(environment, "encoder", field.template) 203 203 print( 204 204 template.render( ··· 247 247 print(template.render()) 248 248 249 249 250 + def emit_struct_maxsize(environment: Environment, node: _XdrStruct) -> None: 251 + """Emit one maxsize macro for an XDR struct type""" 252 + macro_name = get_header_name().upper() + "_" + node.name + "_sz" 253 + template = get_jinja2_template(environment, "maxsize", "struct") 254 + print( 255 + template.render( 256 + macro=macro_name, 257 + width=" + ".join(node.symbolic_width()), 258 + ) 259 + ) 260 + 261 + 250 262 class XdrStructGenerator(SourceGenerator): 251 263 """Generate source code for XDR structs""" 252 264 ··· 282 270 def emit_encoder(self, node: _XdrStruct) -> None: 283 271 """Emit one encoder function for an XDR struct type""" 284 272 emit_struct_encoder(self.environment, node) 273 + 274 + def emit_maxsize(self, node: _XdrStruct) -> None: 275 + """Emit one maxsize macro for an XDR struct type""" 276 + emit_struct_maxsize(self.environment, node)
+22 -6
tools/net/sunrpc/xdrgen/generators/typedef.py
··· 8 8 from generators import SourceGenerator, kernel_c_type 9 9 from generators import create_jinja2_environment, get_jinja2_template 10 10 11 - from xdr_ast import _XdrBasic, _XdrTypedef, _XdrVariableLengthString 11 + from xdr_ast import _XdrBasic, _XdrTypedef, _XdrString 12 12 from xdr_ast import _XdrFixedLengthOpaque, _XdrVariableLengthOpaque 13 13 from xdr_ast import _XdrFixedLengthArray, _XdrVariableLengthArray 14 14 from xdr_ast import _XdrOptionalData, _XdrVoid, _XdrDeclaration 15 - from xdr_ast import public_apis 15 + from xdr_ast import public_apis, get_header_name 16 16 17 17 18 18 def emit_typedef_declaration(environment: Environment, node: _XdrDeclaration) -> None: ··· 28 28 classifier=node.spec.c_classifier, 29 29 ) 30 30 ) 31 - elif isinstance(node, _XdrVariableLengthString): 31 + elif isinstance(node, _XdrString): 32 32 template = get_jinja2_template(environment, "declaration", node.template) 33 33 print(template.render(name=node.name)) 34 34 elif isinstance(node, _XdrFixedLengthOpaque): ··· 74 74 classifier=node.spec.c_classifier, 75 75 ) 76 76 ) 77 - elif isinstance(node, _XdrVariableLengthString): 77 + elif isinstance(node, _XdrString): 78 78 template = get_jinja2_template(environment, "definition", node.template) 79 79 print(template.render(name=node.name)) 80 80 elif isinstance(node, _XdrFixedLengthOpaque): ··· 119 119 type=node.spec.type_name, 120 120 ) 121 121 ) 122 - elif isinstance(node, _XdrVariableLengthString): 122 + elif isinstance(node, _XdrString): 123 123 template = get_jinja2_template(environment, "decoder", node.template) 124 124 print( 125 125 template.render( ··· 180 180 type=node.spec.type_name, 181 181 ) 182 182 ) 183 - elif isinstance(node, _XdrVariableLengthString): 183 + elif isinstance(node, _XdrString): 184 184 template = get_jinja2_template(environment, "encoder", node.template) 185 185 print( 186 186 template.render( ··· 230 230 raise NotImplementedError("typedef: type not recognized") 231 231 232 232 233 + def emit_typedef_maxsize(environment: Environment, node: _XdrDeclaration) -> None: 234 + """Emit a maxsize macro for an XDR typedef""" 235 + macro_name = get_header_name().upper() + "_" + node.name + "_sz" 236 + template = get_jinja2_template(environment, "maxsize", node.template) 237 + print( 238 + template.render( 239 + macro=macro_name, 240 + width=" + ".join(node.symbolic_width()), 241 + ) 242 + ) 243 + 244 + 233 245 class XdrTypedefGenerator(SourceGenerator): 234 246 """Generate source code for XDR typedefs""" 235 247 ··· 265 253 def emit_encoder(self, node: _XdrTypedef) -> None: 266 254 """Emit one encoder function for an XDR typedef""" 267 255 emit_typedef_encoder(self.environment, node.declaration) 256 + 257 + def emit_maxsize(self, node: _XdrTypedef) -> None: 258 + """Emit one maxsize macro for an XDR typedef""" 259 + emit_typedef_maxsize(self.environment, node.declaration)
+42 -10
tools/net/sunrpc/xdrgen/generators/union.py
··· 8 8 from generators import SourceGenerator 9 9 from generators import create_jinja2_environment, get_jinja2_template 10 10 11 - from xdr_ast import _XdrBasic, _XdrUnion, _XdrVoid 12 - from xdr_ast import _XdrDeclaration, _XdrCaseSpec, public_apis 11 + from xdr_ast import _XdrBasic, _XdrUnion, _XdrVoid, get_header_name 12 + from xdr_ast import _XdrDeclaration, _XdrCaseSpec, public_apis, big_endian 13 13 14 14 15 15 def emit_union_declaration(environment: Environment, node: _XdrUnion) -> None: ··· 77 77 print(template.render(name=node.name, type=node.spec.type_name)) 78 78 79 79 80 - def emit_union_case_spec_decoder(environment: Environment, node: _XdrCaseSpec) -> None: 80 + def emit_union_case_spec_decoder( 81 + environment: Environment, node: _XdrCaseSpec, big_endian_discriminant: bool 82 + ) -> None: 81 83 """Emit decoder functions for an XDR union's case arm""" 82 84 83 85 if isinstance(node.arm, _XdrVoid): 84 86 return 85 87 86 - template = get_jinja2_template(environment, "decoder", "case_spec") 88 + if big_endian_discriminant: 89 + template = get_jinja2_template(environment, "decoder", "case_spec_be") 90 + else: 91 + template = get_jinja2_template(environment, "decoder", "case_spec") 87 92 for case in node.values: 88 93 print(template.render(case=case)) 89 94 ··· 141 136 emit_union_switch_spec_decoder(environment, node.discriminant) 142 137 143 138 for case in node.cases: 144 - emit_union_case_spec_decoder(environment, case) 139 + emit_union_case_spec_decoder( 140 + environment, 141 + case, 142 + node.discriminant.spec.type_name in big_endian, 143 + ) 145 144 146 145 emit_union_default_spec_decoder(environment, node) 147 146 ··· 162 153 print(template.render(name=node.name, type=node.spec.type_name)) 163 154 164 155 165 - def emit_union_case_spec_encoder(environment: Environment, node: _XdrCaseSpec) -> None: 156 + def emit_union_case_spec_encoder( 157 + environment: Environment, node: _XdrCaseSpec, big_endian_discriminant: bool 158 + ) -> None: 166 159 """Emit encoder functions for an XDR union's case arm""" 167 160 168 161 if isinstance(node.arm, _XdrVoid): 169 162 return 170 163 171 - template = get_jinja2_template(environment, "encoder", "case_spec") 164 + if big_endian_discriminant: 165 + template = get_jinja2_template(environment, "encoder", "case_spec_be") 166 + else: 167 + template = get_jinja2_template(environment, "encoder", "case_spec") 172 168 for case in node.values: 173 169 print(template.render(case=case)) 174 170 175 - assert isinstance(node.arm, _XdrBasic) 176 171 template = get_jinja2_template(environment, "encoder", node.arm.template) 177 172 print( 178 173 template.render( ··· 205 192 print(template.render()) 206 193 return 207 194 208 - assert isinstance(default_case.arm, _XdrBasic) 209 195 template = get_jinja2_template(environment, "encoder", default_case.arm.template) 210 196 print( 211 197 template.render( ··· 222 210 emit_union_switch_spec_encoder(environment, node.discriminant) 223 211 224 212 for case in node.cases: 225 - emit_union_case_spec_encoder(environment, case) 213 + emit_union_case_spec_encoder( 214 + environment, 215 + case, 216 + node.discriminant.spec.type_name in big_endian, 217 + ) 226 218 227 219 emit_union_default_spec_encoder(environment, node) 228 220 229 221 template = get_jinja2_template(environment, "encoder", "close") 230 222 print(template.render()) 223 + 224 + 225 + def emit_union_maxsize(environment: Environment, node: _XdrUnion) -> None: 226 + """Emit one maxsize macro for an XDR union type""" 227 + macro_name = get_header_name().upper() + "_" + node.name + "_sz" 228 + template = get_jinja2_template(environment, "maxsize", "union") 229 + print( 230 + template.render( 231 + macro=macro_name, 232 + width=" + ".join(node.symbolic_width()), 233 + ) 234 + ) 231 235 232 236 233 237 class XdrUnionGenerator(SourceGenerator): ··· 269 241 def emit_encoder(self, node: _XdrUnion) -> None: 270 242 """Emit one encoder function for an XDR union""" 271 243 emit_union_encoder(self.environment, node) 244 + 245 + def emit_maxsize(self, node: _XdrUnion) -> None: 246 + """Emit one maxsize macro for an XDR union""" 247 + emit_union_maxsize(self.environment, node)
+4 -2
tools/net/sunrpc/xdrgen/grammars/xdr.lark
··· 3 3 4 4 declaration : "opaque" identifier "[" value "]" -> fixed_length_opaque 5 5 | "opaque" identifier "<" [ value ] ">" -> variable_length_opaque 6 - | "string" identifier "<" [ value ] ">" -> variable_length_string 6 + | "string" identifier "<" [ value ] ">" -> string 7 7 | type_specifier identifier "[" value "]" -> fixed_length_array 8 8 | type_specifier identifier "<" [ value ] ">" -> variable_length_array 9 9 | type_specifier "*" identifier -> optional_data ··· 87 87 88 88 pragma_def : "pragma" directive identifier [ identifier ] ";" 89 89 90 - directive : exclude_directive 90 + directive : big_endian_directive 91 + | exclude_directive 91 92 | header_directive 92 93 | pages_directive 93 94 | public_directive 94 95 | skip_directive 95 96 97 + big_endian_directive : "big_endian" 96 98 exclude_directive : "exclude" 97 99 header_directive : "header" 98 100 pages_directive : "pages"
+21 -3
tools/net/sunrpc/xdrgen/subcmds/definitions.py
··· 28 28 logger.setLevel(logging.INFO) 29 29 30 30 31 - def emit_header_definitions( 32 - root: Specification, language: str, peer: str 33 - ) -> None: 31 + def emit_header_definitions(root: Specification, language: str, peer: str) -> None: 34 32 """Emit header definitions""" 35 33 for definition in root.definitions: 36 34 if isinstance(definition.value, _XdrConstant): ··· 50 52 gen.emit_definition(definition.value) 51 53 52 54 55 + def emit_header_maxsize(root: Specification, language: str, peer: str) -> None: 56 + """Emit header maxsize macros""" 57 + print("") 58 + for definition in root.definitions: 59 + if isinstance(definition.value, _XdrEnum): 60 + gen = XdrEnumGenerator(language, peer) 61 + elif isinstance(definition.value, _XdrPointer): 62 + gen = XdrPointerGenerator(language, peer) 63 + elif isinstance(definition.value, _XdrTypedef): 64 + gen = XdrTypedefGenerator(language, peer) 65 + elif isinstance(definition.value, _XdrStruct): 66 + gen = XdrStructGenerator(language, peer) 67 + elif isinstance(definition.value, _XdrUnion): 68 + gen = XdrUnionGenerator(language, peer) 69 + else: 70 + continue 71 + gen.emit_maxsize(definition.value) 72 + 73 + 53 74 def handle_parse_error(e: UnexpectedInput) -> bool: 54 75 """Simple parse error reporting, no recovery attempted""" 55 76 print(e) ··· 88 71 gen.emit_definition(args.filename, ast) 89 72 90 73 emit_header_definitions(ast, args.language, args.peer) 74 + emit_header_maxsize(ast, args.language, args.peer) 91 75 92 76 gen = XdrHeaderBottomGenerator(args.language, args.peer) 93 77 gen.emit_definition(args.filename, ast)
+1 -2
tools/net/sunrpc/xdrgen/subcmds/source.py
··· 83 83 gen = XdrSourceTopGenerator(language, "client") 84 84 gen.emit_source(filename, root) 85 85 86 - # cel: todo: client needs XDR size macros 87 - 86 + print("") 88 87 for definition in root.definitions: 89 88 emit_source_encoder(definition.value, language, "client") 90 89 for definition in root.definitions:
-4
tools/net/sunrpc/xdrgen/templates/C/enum/declaration/close.j2
··· 1 - {# SPDX-License-Identifier: GPL-2.0 #} 2 - 3 - bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} *ptr); 4 - bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} value);
+4
tools/net/sunrpc/xdrgen/templates/C/enum/declaration/enum.j2
··· 1 + {# SPDX-License-Identifier: GPL-2.0 #} 2 + 3 + bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ name }} *ptr); 4 + bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, {{ name }} value);
+1 -1
tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum.j2
··· 8 8 {% else %} 9 9 static bool __maybe_unused 10 10 {% endif %} 11 - xdrgen_decode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} *ptr) 11 + xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ name }} *ptr) 12 12 { 13 13 u32 val; 14 14
+14
tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum_be.j2
··· 1 + {# SPDX-License-Identifier: GPL-2.0 #} 2 + 3 + {% if annotate %} 4 + /* enum {{ name }} (big-endian) */ 5 + {% endif %} 6 + {% if name in public_apis %} 7 + bool 8 + {% else %} 9 + static bool __maybe_unused 10 + {% endif %} 11 + xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ name }} *ptr) 12 + { 13 + return xdr_stream_decode_be32(xdr, ptr) == 0; 14 + }
+1
tools/net/sunrpc/xdrgen/templates/C/enum/definition/close.j2
··· 1 1 {# SPDX-License-Identifier: GPL-2.0 #} 2 2 }; 3 + typedef enum {{ name }} {{ name }};
+3
tools/net/sunrpc/xdrgen/templates/C/enum/definition/close_be.j2
··· 1 + {# SPDX-License-Identifier: GPL-2.0 #} 2 + }; 3 + typedef __be32 {{ name }};
+1 -1
tools/net/sunrpc/xdrgen/templates/C/enum/encoder/enum.j2
··· 8 8 {% else %} 9 9 static bool __maybe_unused 10 10 {% endif %} 11 - xdrgen_encode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} value) 11 + xdrgen_encode_{{ name }}(struct xdr_stream *xdr, {{ name }} value) 12 12 { 13 13 return xdr_stream_encode_u32(xdr, value) == XDR_UNIT; 14 14 }
+14
tools/net/sunrpc/xdrgen/templates/C/enum/encoder/enum_be.j2
··· 1 + {# SPDX-License-Identifier: GPL-2.0 #} 2 + 3 + {% if annotate %} 4 + /* enum {{ name }} (big-endian) */ 5 + {% endif %} 6 + {% if name in public_apis %} 7 + bool 8 + {% else %} 9 + static bool __maybe_unused 10 + {% endif %} 11 + xdrgen_encode_{{ name }}(struct xdr_stream *xdr, {{ name }} value) 12 + { 13 + return xdr_stream_encode_be32(xdr, value) == XDR_UNIT; 14 + }
+2
tools/net/sunrpc/xdrgen/templates/C/enum/maxsize/enum.j2
··· 1 + {# SPDX-License-Identifier: GPL-2.0 #} 2 + #define {{ '{:<31}'.format(macro) }} ({{ width }})
tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/variable_length_string.j2 tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/string.j2
tools/net/sunrpc/xdrgen/templates/C/pointer/definition/variable_length_string.j2 tools/net/sunrpc/xdrgen/templates/C/pointer/definition/string.j2
tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/variable_length_string.j2 tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/string.j2
+3
tools/net/sunrpc/xdrgen/templates/C/pointer/maxsize/pointer.j2
··· 1 + {# SPDX-License-Identifier: GPL-2.0 #} 2 + #define {{ '{:<31}'.format(macro) }} \ 3 + ({{ width }})
-4
tools/net/sunrpc/xdrgen/templates/C/program/decoder/result.j2
··· 13 13 14 14 if (!xdrgen_decode_{{ result }}(xdr, result)) 15 15 return -EIO; 16 - if (result->stat != nfs_ok) { 17 - trace_nfs_xdr_status(xdr, (int)result->stat); 18 - return {{ program }}_stat_to_errno(result->stat); 19 - } 20 16 {% endif %} 21 17 return 0; 22 18 }
+7 -2
tools/net/sunrpc/xdrgen/templates/C/source_top/client.j2
··· 3 3 // XDR specification file: {{ filename }} 4 4 // XDR specification modification time: {{ mtime }} 5 5 6 - #include <linux/sunrpc/xprt.h> 6 + #include <linux/types.h> 7 7 8 - #include "{{ program }}xdr_gen.h" 8 + #include <linux/sunrpc/xdr.h> 9 + #include <linux/sunrpc/xdrgen/_defs.h> 10 + #include <linux/sunrpc/xdrgen/_builtins.h> 11 + #include <linux/sunrpc/xdrgen/nlm4.h> 12 + 13 + #include <linux/sunrpc/clnt.h>
tools/net/sunrpc/xdrgen/templates/C/struct/decoder/variable_length_string.j2 tools/net/sunrpc/xdrgen/templates/C/struct/decoder/string.j2
tools/net/sunrpc/xdrgen/templates/C/struct/definition/variable_length_string.j2 tools/net/sunrpc/xdrgen/templates/C/struct/definition/string.j2
tools/net/sunrpc/xdrgen/templates/C/struct/encoder/variable_length_string.j2 tools/net/sunrpc/xdrgen/templates/C/struct/encoder/string.j2
+3
tools/net/sunrpc/xdrgen/templates/C/struct/maxsize/struct.j2
··· 1 + {# SPDX-License-Identifier: GPL-2.0 #} 2 + #define {{ '{:<31}'.format(macro) }} \ 3 + ({{ width }})
tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/variable_length_string.j2 tools/net/sunrpc/xdrgen/templates/C/typedef/declaration/string.j2
tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/variable_length_string.j2 tools/net/sunrpc/xdrgen/templates/C/typedef/decoder/string.j2
tools/net/sunrpc/xdrgen/templates/C/typedef/definition/variable_length_string.j2 tools/net/sunrpc/xdrgen/templates/C/typedef/definition/string.j2
tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/variable_length_string.j2 tools/net/sunrpc/xdrgen/templates/C/typedef/encoder/string.j2
+3
tools/net/sunrpc/xdrgen/templates/C/typedef/maxsize/basic.j2
··· 1 + {# SPDX-License-Identifier: GPL-2.0 #} 2 + #define {{ '{:<31}'.format(macro) }} \ 3 + ({{ width }})
+2
tools/net/sunrpc/xdrgen/templates/C/typedef/maxsize/fixed_length_opaque.j2
··· 1 + {# SPDX-License-Identifier: GPL-2.0 #} 2 + #define {{ '{:<31}'.format(macro) }} ({{ width }})
+2
tools/net/sunrpc/xdrgen/templates/C/typedef/maxsize/string.j2
··· 1 + {# SPDX-License-Identifier: GPL-2.0 #} 2 + #define {{ '{:<31}'.format(macro) }} ({{ width }})
+2
tools/net/sunrpc/xdrgen/templates/C/typedef/maxsize/variable_length_array.j2
··· 1 + {# SPDX-License-Identifier: GPL-2.0 #} 2 + #define {{ '{:<31}'.format(macro) }} ({{ width }})
+2
tools/net/sunrpc/xdrgen/templates/C/typedef/maxsize/variable_length_opaque.j2
··· 1 + {# SPDX-License-Identifier: GPL-2.0 #} 2 + #define {{ '{:<31}'.format(macro) }} ({{ width }})
+2
tools/net/sunrpc/xdrgen/templates/C/union/decoder/case_spec_be.j2
··· 1 + {# SPDX-License-Identifier: GPL-2.0 #} 2 + case __constant_cpu_to_be32({{ case }}):
tools/net/sunrpc/xdrgen/templates/C/union/decoder/variable_length_string.j2 tools/net/sunrpc/xdrgen/templates/C/union/decoder/string.j2
+2
tools/net/sunrpc/xdrgen/templates/C/union/encoder/case_spec_be.j2
··· 1 + {# SPDX-License-Identifier: GPL-2.0 #} 2 + case __constant_cpu_to_be32({{ case }}):
+3
tools/net/sunrpc/xdrgen/templates/C/union/maxsize/union.j2
··· 1 + {# SPDX-License-Identifier: GPL-2.0 #} 2 + #define {{ '{:<31}'.format(macro) }} \ 3 + ({{ width }})
+277 -34
tools/net/sunrpc/xdrgen/xdr_ast.py
··· 12 12 13 13 this_module = sys.modules[__name__] 14 14 15 + big_endian = [] 15 16 excluded_apis = [] 16 17 header_name = "none" 17 18 public_apis = [] 18 - enums = set() 19 19 structs = set() 20 20 pass_by_reference = set() 21 + 22 + constants = {} 23 + 24 + 25 + def xdr_quadlen(val: str) -> int: 26 + """Return integer XDR width of an XDR type""" 27 + if val in constants: 28 + octets = constants[val] 29 + else: 30 + octets = int(val) 31 + return int((octets + 3) / 4) 32 + 33 + 34 + symbolic_widths = { 35 + "void": ["XDR_void"], 36 + "bool": ["XDR_bool"], 37 + "int": ["XDR_int"], 38 + "unsigned_int": ["XDR_unsigned_int"], 39 + "long": ["XDR_long"], 40 + "unsigned_long": ["XDR_unsigned_long"], 41 + "hyper": ["XDR_hyper"], 42 + "unsigned_hyper": ["XDR_unsigned_hyper"], 43 + } 44 + 45 + # Numeric XDR widths are tracked in a dictionary that is keyed 46 + # by type_name because sometimes a caller has nothing more than 47 + # the type_name to use to figure out the numeric width. 48 + max_widths = { 49 + "void": 0, 50 + "bool": 1, 51 + "int": 1, 52 + "unsigned_int": 1, 53 + "long": 1, 54 + "unsigned_long": 1, 55 + "hyper": 2, 56 + "unsigned_hyper": 2, 57 + } 21 58 22 59 23 60 @dataclass ··· 88 51 """Corresponds to 'type_specifier' in the XDR language grammar""" 89 52 90 53 type_name: str 91 - c_classifier: str 54 + c_classifier: str = "" 92 55 93 56 94 57 @dataclass 95 58 class _XdrDefinedType(_XdrTypeSpecifier): 96 59 """Corresponds to a type defined by the input specification""" 97 60 61 + def symbolic_width(self) -> List: 62 + """Return list containing XDR width of type's components""" 63 + return [get_header_name().upper() + "_" + self.type_name + "_sz"] 64 + 65 + def __post_init__(self): 66 + if self.type_name in structs: 67 + self.c_classifier = "struct " 68 + symbolic_widths[self.type_name] = self.symbolic_width() 69 + 98 70 99 71 @dataclass 100 72 class _XdrBuiltInType(_XdrTypeSpecifier): 101 73 """Corresponds to a built-in XDR type""" 74 + 75 + def symbolic_width(self) -> List: 76 + """Return list containing XDR width of type's components""" 77 + return symbolic_widths[self.type_name] 102 78 103 79 104 80 @dataclass ··· 127 77 size: str 128 78 template: str = "fixed_length_opaque" 129 79 80 + def max_width(self) -> int: 81 + """Return width of type in XDR_UNITS""" 82 + return xdr_quadlen(self.size) 83 + 84 + def symbolic_width(self) -> List: 85 + """Return list containing XDR width of type's components""" 86 + return ["XDR_QUADLEN(" + self.size + ")"] 87 + 88 + def __post_init__(self): 89 + max_widths[self.name] = self.max_width() 90 + symbolic_widths[self.name] = self.symbolic_width() 91 + 130 92 131 93 @dataclass 132 94 class _XdrVariableLengthOpaque(_XdrDeclaration): ··· 148 86 maxsize: str 149 87 template: str = "variable_length_opaque" 150 88 89 + def max_width(self) -> int: 90 + """Return width of type in XDR_UNITS""" 91 + return 1 + xdr_quadlen(self.maxsize) 92 + 93 + def symbolic_width(self) -> List: 94 + """Return list containing XDR width of type's components""" 95 + widths = ["XDR_unsigned_int"] 96 + if self.maxsize != "0": 97 + widths.append("XDR_QUADLEN(" + self.maxsize + ")") 98 + return widths 99 + 100 + def __post_init__(self): 101 + max_widths[self.name] = self.max_width() 102 + symbolic_widths[self.name] = self.symbolic_width() 103 + 151 104 152 105 @dataclass 153 - class _XdrVariableLengthString(_XdrDeclaration): 106 + class _XdrString(_XdrDeclaration): 154 107 """A (NUL-terminated) variable-length string declaration""" 155 108 156 109 name: str 157 110 maxsize: str 158 - template: str = "variable_length_string" 111 + template: str = "string" 112 + 113 + def max_width(self) -> int: 114 + """Return width of type in XDR_UNITS""" 115 + return 1 + xdr_quadlen(self.maxsize) 116 + 117 + def symbolic_width(self) -> List: 118 + """Return list containing XDR width of type's components""" 119 + widths = ["XDR_unsigned_int"] 120 + if self.maxsize != "0": 121 + widths.append("XDR_QUADLEN(" + self.maxsize + ")") 122 + return widths 123 + 124 + def __post_init__(self): 125 + max_widths[self.name] = self.max_width() 126 + symbolic_widths[self.name] = self.symbolic_width() 159 127 160 128 161 129 @dataclass ··· 197 105 size: str 198 106 template: str = "fixed_length_array" 199 107 108 + def max_width(self) -> int: 109 + """Return width of type in XDR_UNITS""" 110 + return xdr_quadlen(self.size) * max_widths[self.spec.type_name] 111 + 112 + def symbolic_width(self) -> List: 113 + """Return list containing XDR width of type's components""" 114 + item_width = " + ".join(symbolic_widths[self.spec.type_name]) 115 + return ["(" + self.size + " * (" + item_width + "))"] 116 + 117 + def __post_init__(self): 118 + max_widths[self.name] = self.max_width() 119 + symbolic_widths[self.name] = self.symbolic_width() 120 + 200 121 201 122 @dataclass 202 123 class _XdrVariableLengthArray(_XdrDeclaration): ··· 220 115 maxsize: str 221 116 template: str = "variable_length_array" 222 117 118 + def max_width(self) -> int: 119 + """Return width of type in XDR_UNITS""" 120 + return 1 + (xdr_quadlen(self.maxsize) * max_widths[self.spec.type_name]) 121 + 122 + def symbolic_width(self) -> List: 123 + """Return list containing XDR width of type's components""" 124 + widths = ["XDR_unsigned_int"] 125 + if self.maxsize != "0": 126 + item_width = " + ".join(symbolic_widths[self.spec.type_name]) 127 + widths.append("(" + self.maxsize + " * (" + item_width + "))") 128 + return widths 129 + 130 + def __post_init__(self): 131 + max_widths[self.name] = self.max_width() 132 + symbolic_widths[self.name] = self.symbolic_width() 133 + 223 134 224 135 @dataclass 225 136 class _XdrOptionalData(_XdrDeclaration): ··· 244 123 name: str 245 124 spec: _XdrTypeSpecifier 246 125 template: str = "optional_data" 126 + 127 + def max_width(self) -> int: 128 + """Return width of type in XDR_UNITS""" 129 + return 1 130 + 131 + def symbolic_width(self) -> List: 132 + """Return list containing XDR width of type's components""" 133 + return ["XDR_bool"] 134 + 135 + def __post_init__(self): 136 + structs.add(self.name) 137 + pass_by_reference.add(self.name) 138 + max_widths[self.name] = self.max_width() 139 + symbolic_widths[self.name] = self.symbolic_width() 247 140 248 141 249 142 @dataclass ··· 268 133 spec: _XdrTypeSpecifier 269 134 template: str = "basic" 270 135 136 + def max_width(self) -> int: 137 + """Return width of type in XDR_UNITS""" 138 + return max_widths[self.spec.type_name] 139 + 140 + def symbolic_width(self) -> List: 141 + """Return list containing XDR width of type's components""" 142 + return symbolic_widths[self.spec.type_name] 143 + 144 + def __post_init__(self): 145 + max_widths[self.name] = self.max_width() 146 + symbolic_widths[self.name] = self.symbolic_width() 147 + 271 148 272 149 @dataclass 273 150 class _XdrVoid(_XdrDeclaration): 274 151 """A void declaration""" 275 152 153 + name: str = "void" 276 154 template: str = "void" 155 + 156 + def max_width(self) -> int: 157 + """Return width of type in XDR_UNITS""" 158 + return 0 159 + 160 + def symbolic_width(self) -> List: 161 + """Return list containing XDR width of type's components""" 162 + return [] 277 163 278 164 279 165 @dataclass ··· 304 148 name: str 305 149 value: str 306 150 151 + def __post_init__(self): 152 + if self.value not in constants: 153 + constants[self.name] = int(self.value, 0) 154 + 307 155 308 156 @dataclass 309 157 class _XdrEnumerator(_XdrAst): ··· 315 155 316 156 name: str 317 157 value: str 158 + 159 + def __post_init__(self): 160 + if self.value not in constants: 161 + constants[self.name] = int(self.value, 0) 318 162 319 163 320 164 @dataclass ··· 330 166 maximum: int 331 167 enumerators: List[_XdrEnumerator] 332 168 169 + def max_width(self) -> int: 170 + """Return width of type in XDR_UNITS""" 171 + return 1 172 + 173 + def symbolic_width(self) -> List: 174 + """Return list containing XDR width of type's components""" 175 + return ["XDR_int"] 176 + 177 + def __post_init__(self): 178 + max_widths[self.name] = self.max_width() 179 + symbolic_widths[self.name] = self.symbolic_width() 180 + 333 181 334 182 @dataclass 335 183 class _XdrStruct(_XdrAst): ··· 349 173 350 174 name: str 351 175 fields: List[_XdrDeclaration] 176 + 177 + def max_width(self) -> int: 178 + """Return width of type in XDR_UNITS""" 179 + width = 0 180 + for field in self.fields: 181 + width += field.max_width() 182 + return width 183 + 184 + def symbolic_width(self) -> List: 185 + """Return list containing XDR width of type's components""" 186 + widths = [] 187 + for field in self.fields: 188 + widths += field.symbolic_width() 189 + return widths 190 + 191 + def __post_init__(self): 192 + structs.add(self.name) 193 + pass_by_reference.add(self.name) 194 + max_widths[self.name] = self.max_width() 195 + symbolic_widths[self.name] = self.symbolic_width() 352 196 353 197 354 198 @dataclass ··· 378 182 name: str 379 183 fields: List[_XdrDeclaration] 380 184 185 + def max_width(self) -> int: 186 + """Return width of type in XDR_UNITS""" 187 + width = 1 188 + for field in self.fields[0:-1]: 189 + width += field.max_width() 190 + return width 191 + 192 + def symbolic_width(self) -> List: 193 + """Return list containing XDR width of type's components""" 194 + widths = [] 195 + widths += ["XDR_bool"] 196 + for field in self.fields[0:-1]: 197 + widths += field.symbolic_width() 198 + return widths 199 + 200 + def __post_init__(self): 201 + structs.add(self.name) 202 + pass_by_reference.add(self.name) 203 + max_widths[self.name] = self.max_width() 204 + symbolic_widths[self.name] = self.symbolic_width() 205 + 381 206 382 207 @dataclass 383 208 class _XdrTypedef(_XdrAst): 384 209 """An XDR typedef""" 385 210 386 211 declaration: _XdrDeclaration 212 + 213 + def max_width(self) -> int: 214 + """Return width of type in XDR_UNITS""" 215 + return self.declaration.max_width() 216 + 217 + def symbolic_width(self) -> List: 218 + """Return list containing XDR width of type's components""" 219 + return self.declaration.symbolic_width() 220 + 221 + def __post_init__(self): 222 + if isinstance(self.declaration, _XdrBasic): 223 + new_type = self.declaration 224 + if isinstance(new_type.spec, _XdrDefinedType): 225 + if new_type.spec.type_name in pass_by_reference: 226 + pass_by_reference.add(new_type.name) 227 + max_widths[new_type.name] = self.max_width() 228 + symbolic_widths[new_type.name] = self.symbolic_width() 387 229 388 230 389 231 @dataclass ··· 449 215 discriminant: _XdrDeclaration 450 216 cases: List[_XdrCaseSpec] 451 217 default: _XdrDeclaration 218 + 219 + def max_width(self) -> int: 220 + """Return width of type in XDR_UNITS""" 221 + max_width = 0 222 + for case in self.cases: 223 + if case.arm.max_width() > max_width: 224 + max_width = case.arm.max_width() 225 + if self.default: 226 + if self.default.arm.max_width() > max_width: 227 + max_width = self.default.arm.max_width() 228 + return 1 + max_width 229 + 230 + def symbolic_width(self) -> List: 231 + """Return list containing XDR width of type's components""" 232 + max_width = 0 233 + for case in self.cases: 234 + if case.arm.max_width() > max_width: 235 + max_width = case.arm.max_width() 236 + width = case.arm.symbolic_width() 237 + if self.default: 238 + if self.default.arm.max_width() > max_width: 239 + max_width = self.default.arm.max_width() 240 + width = self.default.arm.symbolic_width() 241 + return symbolic_widths[self.discriminant.name] + width 242 + 243 + def __post_init__(self): 244 + structs.add(self.name) 245 + pass_by_reference.add(self.name) 246 + max_widths[self.name] = self.max_width() 247 + symbolic_widths[self.name] = self.symbolic_width() 452 248 453 249 454 250 @dataclass ··· 554 290 return _XdrConstantValue(value) 555 291 556 292 def type_specifier(self, children): 557 - """Instantiate one type_specifier object""" 558 - c_classifier = "" 293 + """Instantiate one _XdrTypeSpecifier object""" 559 294 if isinstance(children[0], _XdrIdentifier): 560 295 name = children[0].symbol 561 - if name in enums: 562 - c_classifier = "enum " 563 - if name in structs: 564 - c_classifier = "struct " 565 - return _XdrDefinedType( 566 - type_name=name, 567 - c_classifier=c_classifier, 568 - ) 296 + return _XdrDefinedType(type_name=name) 569 297 570 - token = children[0].data 571 - return _XdrBuiltInType( 572 - type_name=token.value, 573 - c_classifier=c_classifier, 574 - ) 298 + name = children[0].data.value 299 + return _XdrBuiltInType(type_name=name) 575 300 576 301 def constant_def(self, children): 577 302 """Instantiate one _XdrConstant object""" ··· 573 320 def enum(self, children): 574 321 """Instantiate one _XdrEnum object""" 575 322 enum_name = children[0].symbol 576 - enums.add(enum_name) 577 323 578 324 i = 0 579 325 enumerators = [] ··· 602 350 603 351 return _XdrVariableLengthOpaque(name, maxsize) 604 352 605 - def variable_length_string(self, children): 606 - """Instantiate one _XdrVariableLengthString declaration object""" 353 + def string(self, children): 354 + """Instantiate one _XdrString declaration object""" 607 355 name = children[0].symbol 608 356 if children[1] is not None: 609 357 maxsize = children[1].value 610 358 else: 611 359 maxsize = "0" 612 360 613 - return _XdrVariableLengthString(name, maxsize) 361 + return _XdrString(name, maxsize) 614 362 615 363 def fixed_length_array(self, children): 616 364 """Instantiate one _XdrFixedLengthArray declaration object""" ··· 635 383 """Instantiate one _XdrOptionalData declaration object""" 636 384 spec = children[0] 637 385 name = children[1].symbol 638 - structs.add(name) 639 - pass_by_reference.add(name) 640 386 641 387 return _XdrOptionalData(name, spec) 642 388 ··· 653 403 def struct(self, children): 654 404 """Instantiate one _XdrStruct object""" 655 405 name = children[0].symbol 656 - structs.add(name) 657 - pass_by_reference.add(name) 658 406 fields = children[1].children 659 407 660 408 last_field = fields[-1] ··· 667 419 def typedef(self, children): 668 420 """Instantiate one _XdrTypedef object""" 669 421 new_type = children[0] 670 - if isinstance(new_type, _XdrBasic) and isinstance( 671 - new_type.spec, _XdrDefinedType 672 - ): 673 - if new_type.spec.type_name in pass_by_reference: 674 - pass_by_reference.add(new_type.name) 675 422 676 423 return _XdrTypedef(new_type) 677 424 ··· 688 445 def union(self, children): 689 446 """Instantiate one _XdrUnion object""" 690 447 name = children[0].symbol 691 - structs.add(name) 692 - pass_by_reference.add(name) 693 448 694 449 body = children[1] 695 450 discriminant = body.children[0].children[0] ··· 725 484 """Instantiate one _Pragma object""" 726 485 directive = children[0].children[0].data 727 486 match directive: 487 + case "big_endian_directive": 488 + big_endian.append(children[1].symbol) 728 489 case "exclude_directive": 729 490 excluded_apis.append(children[1].symbol) 730 491 case "header_directive":
+3 -1
tools/net/sunrpc/xdrgen/xdrgen
··· 128 128 try: 129 129 if __name__ == "__main__": 130 130 sys.exit(main()) 131 - except (SystemExit, KeyboardInterrupt, BrokenPipeError): 131 + except SystemExit: 132 + sys.exit(0) 133 + except (KeyboardInterrupt, BrokenPipeError): 132 134 sys.exit(1)