NFSD 6.13 Release Notes

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.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEKLLlsBKG3yQ88j7+M2qzM29mf5cFAmdEgLQACgkQM2qzM29m
 f5cwmg/9HcfG7blepU/2qNHopzSYRO5vZw1YNJQ5/Wi3bmqIea83lf8OcCY1G/aj
 6K+jnenzHrwfhaA4u7N2FPXPVl8sPSMuOrJXY5zC4yE5QnIbranjcyEW5l5zlj3n
 ukkTYQgjUsKre3pHlvn3JmDHfUhNPEfzirsJeorP7DS3omne+OFA1LNncNP6emRu
 h0aEC6EJ43zUkYiz9nZYqPwIAwrUIA0WOrvVnq7vsi6gR4/Muk7nS+X/y4qFjli3
 9enVskEv8sFmmOAIMK3CHJq+exEeKtKEKUuYkD23QgPt2R4+IwqS70o9IM/S1ypf
 APiv958BIhxm/SwUn1IjoxIckTB5EdksMxU5/4qGr1ZxprPG4/ruKO80BkrxLzW2
 n1HmJ4ZNnpWPQvHN7RQ0WOsPNzL8byxJbGr1bpNgU4AGXnTFWPrAnB6juiyX4xb+
 YNfgkQGDY79o7r1OJ5UUdCyx0QBSnaLNACTGm2u2FpI/ukMFPdrWIE99QbBgSe1p
 MgWaiPwSY+9crFfGPJeQ4t6/siRAec6L3RO9KT9Epcd2S7/Uts3NXYRdJfwZ+Qza
 TkPY2bm7T/WCcMhW7DN372hqgfRHPWOf4tacJ1Tob+As1d6p6qXEX2zi6piCCOLj
 dmTVDSVPClRXt8YigF9WqosyWv1jUzSnh9ne+eYPBpj93Ag2YBY=
 =wBvS
 -----END PGP SIGNATURE-----

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
  ...
This commit is contained in:
Linus Torvalds 2024-11-26 12:59:30 -08:00
commit 445d9f05fa
78 changed files with 1115 additions and 348 deletions

View File

@ -12436,6 +12436,7 @@ F: include/trace/misc/sunrpc.h
F: include/uapi/linux/nfsd/
F: include/uapi/linux/sunrpc/
F: net/sunrpc/
F: tools/net/sunrpc/
KERNEL PACMAN PACKAGING (in addition to generic KERNEL BUILD)
M: Thomas Weißschuh <linux@weissschuh.net>

View File

@ -2,8 +2,9 @@
/*
* linux/fs/lockd/clntxdr.c
*
* XDR functions to encode/decode NLM version 3 RPC arguments and results.
* NLM version 3 is backwards compatible with NLM versions 1 and 2.
* XDR functions to encode/decode NLM version 1 and 3 RPC
* arguments and results. NLM version 2 is not specified
* by a standard, thus it is not implemented.
*
* NLM client-side only.
*

View File

@ -46,14 +46,15 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
if (filp != NULL) {
int mode = lock_to_openmode(&lock->fl);
lock->fl.c.flc_flags = FL_POSIX;
error = nlm_lookup_file(rqstp, &file, lock);
if (error)
goto no_locks;
*filp = file;
/* Set up the missing parts of the file_lock structure */
lock->fl.c.flc_flags = FL_POSIX;
lock->fl.c.flc_file = file->f_file[mode];
lock->fl.c.flc_file = file->f_file[mode];
lock->fl.c.flc_pid = current->tgid;
lock->fl.fl_start = (loff_t)lock->lock_start;
lock->fl.fl_end = lock->lock_len ?
@ -108,7 +109,8 @@ __nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
test_owner = argp->lock.fl.c.flc_owner;
/* Now check for conflicting locks */
resp->status = nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock, &resp->cookie);
resp->status = nlmsvc_testlock(rqstp, file, host, &argp->lock,
&resp->lock);
if (resp->status == nlm_drop_reply)
rc = rpc_drop_reply;
else
@ -142,18 +144,6 @@ __nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_res *resp)
if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
#if 0
/* If supplied state doesn't match current state, we assume it's
* an old request that time-warped somehow. Any error return would
* do in this case because it's irrelevant anyway.
*
* NB: We don't retrieve the remote host's state yet.
*/
if (host->h_nsmstate && host->h_nsmstate != argp->state) {
resp->status = nlm_lck_denied_nolocks;
} else
#endif
/* Now try to lock the file */
resp->status = nlmsvc_lock(rqstp, file, host, &argp->lock,
argp->block, &argp->cookie,

View File

@ -608,7 +608,7 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
__be32
nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
struct nlm_host *host, struct nlm_lock *lock,
struct nlm_lock *conflock, struct nlm_cookie *cookie)
struct nlm_lock *conflock)
{
int error;
int mode;

View File

@ -130,7 +130,8 @@ __nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
test_owner = argp->lock.fl.c.flc_owner;
/* Now check for conflicting locks */
resp->status = cast_status(nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock, &resp->cookie));
resp->status = cast_status(nlmsvc_testlock(rqstp, file, host,
&argp->lock, &resp->lock));
if (resp->status == nlm_drop_reply)
rc = rpc_drop_reply;
else
@ -165,18 +166,6 @@ __nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_res *resp)
if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
#if 0
/* If supplied state doesn't match current state, we assume it's
* an old request that time-warped somehow. Any error return would
* do in this case because it's irrelevant anyway.
*
* NB: We don't retrieve the remote host's state yet.
*/
if (host->h_nsmstate && host->h_nsmstate != argp->state) {
resp->status = nlm_lck_denied_nolocks;
} else
#endif
/* Now try to lock the file */
resp->status = cast_status(nlmsvc_lock(rqstp, file, host, &argp->lock,
argp->block, &argp->cookie,

View File

@ -89,7 +89,6 @@ svcxdr_decode_lock(struct xdr_stream *xdr, struct nlm_lock *lock)
return false;
locks_init_lock(fl);
fl->c.flc_flags = FL_POSIX;
fl->c.flc_type = F_RDLCK;
nlm4svc_set_file_lock_range(fl, lock->lock_start, lock->lock_len);
return true;
@ -268,7 +267,6 @@ nlm4svc_decode_shareargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
struct nlm_args *argp = rqstp->rq_argp;
struct nlm_lock *lock = &argp->lock;
memset(lock, 0, sizeof(*lock));
locks_init_lock(&lock->fl);
lock->svid = ~(u32)0;

View File

@ -155,11 +155,9 @@ struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
/* We have an implied reference to net thanks to nfsd_serv_try_get */
localio = nfs_to->nfsd_open_local_fh(net, uuid->dom, rpc_clnt,
cred, nfs_fh, fmode);
if (IS_ERR(localio)) {
rcu_read_lock();
nfs_to->nfsd_serv_put(net);
rcu_read_unlock();
}
if (IS_ERR(localio))
nfs_to_nfsd_net_put(net);
return localio;
}
EXPORT_SYMBOL_GPL(nfs_open_local_fh);

View File

@ -40,15 +40,24 @@
#define EXPKEY_HASHMAX (1 << EXPKEY_HASHBITS)
#define EXPKEY_HASHMASK (EXPKEY_HASHMAX -1)
static void expkey_put(struct kref *ref)
static void expkey_put_work(struct work_struct *work)
{
struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref);
struct svc_expkey *key =
container_of(to_rcu_work(work), struct svc_expkey, ek_rcu_work);
if (test_bit(CACHE_VALID, &key->h.flags) &&
!test_bit(CACHE_NEGATIVE, &key->h.flags))
path_put(&key->ek_path);
auth_domain_put(key->ek_client);
kfree_rcu(key, ek_rcu);
kfree(key);
}
static void expkey_put(struct kref *ref)
{
struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref);
INIT_RCU_WORK(&key->ek_rcu_work, expkey_put_work);
queue_rcu_work(system_wq, &key->ek_rcu_work);
}
static int expkey_upcall(struct cache_detail *cd, struct cache_head *h)
@ -355,16 +364,26 @@ static void export_stats_destroy(struct export_stats *stats)
EXP_STATS_COUNTERS_NUM);
}
static void svc_export_put(struct kref *ref)
static void svc_export_put_work(struct work_struct *work)
{
struct svc_export *exp = container_of(ref, struct svc_export, h.ref);
struct svc_export *exp =
container_of(to_rcu_work(work), struct svc_export, ex_rcu_work);
path_put(&exp->ex_path);
auth_domain_put(exp->ex_client);
nfsd4_fslocs_free(&exp->ex_fslocs);
export_stats_destroy(exp->ex_stats);
kfree(exp->ex_stats);
kfree(exp->ex_uuid);
kfree_rcu(exp, ex_rcu);
kfree(exp);
}
static void svc_export_put(struct kref *ref)
{
struct svc_export *exp = container_of(ref, struct svc_export, h.ref);
INIT_RCU_WORK(&exp->ex_rcu_work, svc_export_put_work);
queue_rcu_work(system_wq, &exp->ex_rcu_work);
}
static int svc_export_upcall(struct cache_detail *cd, struct cache_head *h)
@ -1078,12 +1097,14 @@ static struct svc_export *exp_find(struct cache_detail *cd,
* check_nfsd_access - check if access to export is allowed.
* @exp: svc_export that is being accessed.
* @rqstp: svc_rqst attempting to access @exp (will be NULL for LOCALIO).
* @may_bypass_gss: reduce strictness of authorization check
*
* Return values:
* %nfs_ok if access is granted, or
* %nfserr_wrongsec if access is denied
*/
__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp)
__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp,
bool may_bypass_gss)
{
struct exp_flavor_info *f, *end = exp->ex_flavors + exp->ex_nflavors;
struct svc_xprt *xprt;
@ -1140,6 +1161,23 @@ __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp)
if (nfsd4_spo_must_allow(rqstp))
return nfs_ok;
/* Some calls may be processed without authentication
* on GSS exports. For example NFS2/3 calls on root
* directory, see section 2.3.2 of rfc 2623.
* For "may_bypass_gss" check that export has really
* enabled some flavor with authentication (GSS or any
* other) and also check that the used auth flavor is
* without authentication (none or sys).
*/
if (may_bypass_gss && (
rqstp->rq_cred.cr_flavor == RPC_AUTH_NULL ||
rqstp->rq_cred.cr_flavor == RPC_AUTH_UNIX)) {
for (f = exp->ex_flavors; f < end; f++) {
if (f->pseudoflavor >= RPC_AUTH_DES)
return 0;
}
}
denied:
return nfserr_wrongsec;
}
@ -1406,9 +1444,12 @@ static int e_show(struct seq_file *m, void *p)
return 0;
}
exp_get(exp);
if (!cache_get_rcu(&exp->h))
return 0;
if (cache_check(cd, &exp->h, NULL))
return 0;
exp_put(exp);
return svc_export_show(m, cd, cp);
}

View File

@ -75,7 +75,7 @@ struct svc_export {
u32 ex_layout_types;
struct nfsd4_deviceid_map *ex_devid_map;
struct cache_detail *cd;
struct rcu_head ex_rcu;
struct rcu_work ex_rcu_work;
unsigned long ex_xprtsec_modes;
struct export_stats *ex_stats;
};
@ -92,7 +92,7 @@ struct svc_expkey {
u32 ek_fsid[6];
struct path ek_path;
struct rcu_head ek_rcu;
struct rcu_work ek_rcu_work;
};
#define EX_ISSYNC(exp) (!((exp)->ex_flags & NFSEXP_ASYNC))
@ -101,7 +101,8 @@ struct svc_expkey {
struct svc_cred;
int nfsexp_flags(struct svc_cred *cred, struct svc_export *exp);
__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp);
__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp,
bool may_bypass_gss);
/*
* Function declarations

View File

@ -391,19 +391,19 @@ nfsd_file_put(struct nfsd_file *nf)
}
/**
* nfsd_file_put_local - put the reference to nfsd_file and local nfsd_serv
* @nf: nfsd_file of which to put the references
* nfsd_file_put_local - put nfsd_file reference and arm nfsd_serv_put in caller
* @nf: nfsd_file of which to put the reference
*
* First put the reference of the nfsd_file and then put the
* reference to the associated nn->nfsd_serv.
* First save the associated net to return to caller, then put
* the reference of the nfsd_file.
*/
void
nfsd_file_put_local(struct nfsd_file *nf) __must_hold(rcu)
struct net *
nfsd_file_put_local(struct nfsd_file *nf)
{
struct net *net = nf->nf_net;
nfsd_file_put(nf);
nfsd_serv_put(net);
return net;
}
/**
@ -1047,7 +1047,7 @@ nfsd_file_do_acquire(struct svc_rqst *rqstp, struct net *net,
* the last one however, since we should hold another.
*/
if (nfsd_file_lru_remove(nf))
WARN_ON_ONCE(refcount_dec_and_test(&nf->nf_ref));
refcount_dec(&nf->nf_ref);
goto wait_for_construction;
}
@ -1120,8 +1120,7 @@ nfsd_file_do_acquire(struct svc_rqst *rqstp, struct net *net,
status = nfs_ok;
trace_nfsd_file_opened(nf, status);
} else {
ret = nfsd_open_verified(rqstp, fhp, may_flags,
&nf->nf_file);
ret = nfsd_open_verified(fhp, may_flags, &nf->nf_file);
if (ret == -EOPENSTALE && stale_retry) {
stale_retry = false;
nfsd_file_unhash(nf);

View File

@ -55,7 +55,7 @@ void nfsd_file_cache_shutdown(void);
int nfsd_file_cache_start_net(struct net *net);
void nfsd_file_cache_shutdown_net(struct net *net);
void nfsd_file_put(struct nfsd_file *nf);
void nfsd_file_put_local(struct nfsd_file *nf);
struct net *nfsd_file_put_local(struct nfsd_file *nf);
struct nfsd_file *nfsd_file_get(struct nfsd_file *nf);
struct file *nfsd_file_file(struct nfsd_file *nf);
void nfsd_file_close_inode_sync(struct inode *inode);

View File

@ -38,11 +38,20 @@ nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp,
memcpy(&fh.fh_handle.fh_raw, f->data, f->size);
fh.fh_export = NULL;
/*
* Allow BYPASS_GSS as some client implementations use AUTH_SYS
* for NLM even when GSS is used for NFS.
* Allow OWNER_OVERRIDE as permission might have been changed
* after the file was opened.
* Pass MAY_NLM so that authentication can be completely bypassed
* if NFSEXP_NOAUTHNLM is set. Some older clients use AUTH_NULL
* for NLM requests.
*/
access = (mode == O_WRONLY) ? NFSD_MAY_WRITE : NFSD_MAY_READ;
access |= NFSD_MAY_LOCK;
access |= NFSD_MAY_NLM | NFSD_MAY_OWNER_OVERRIDE | NFSD_MAY_BYPASS_GSS;
nfserr = nfsd_open(rqstp, &fh, S_IFREG, access, filp);
fh_put(&fh);
/* We return nlm error codes as nlm doesn't know
/* We return nlm error codes as nlm doesn't know
* about nfsd, but nfsd does know about nlm..
*/
switch (nfserr) {

View File

@ -198,8 +198,6 @@ summarize_posix_acl(struct posix_acl *acl, struct posix_acl_summary *pas)
memset(pas, 0, sizeof(*pas));
pas->mask = 07;
pe = acl->a_entries + acl->a_count;
FOREACH_ACL_ENTRY(pa, acl, pe) {
switch (pa->e_tag) {
case ACL_USER_OBJ:

View File

@ -287,17 +287,17 @@ static int decode_cb_compound4res(struct xdr_stream *xdr,
u32 length;
__be32 *p;
p = xdr_inline_decode(xdr, 4 + 4);
p = xdr_inline_decode(xdr, XDR_UNIT);
if (unlikely(p == NULL))
goto out_overflow;
hdr->status = be32_to_cpup(p++);
hdr->status = be32_to_cpup(p);
/* Ignore the tag */
length = be32_to_cpup(p++);
p = xdr_inline_decode(xdr, length + 4);
if (unlikely(p == NULL))
if (xdr_stream_decode_u32(xdr, &length) < 0)
goto out_overflow;
if (xdr_inline_decode(xdr, length) == NULL)
goto out_overflow;
if (xdr_stream_decode_u32(xdr, &hdr->nops) < 0)
goto out_overflow;
p += XDR_QUADLEN(length);
hdr->nops = be32_to_cpup(p);
return 0;
out_overflow:
return -EIO;
@ -364,13 +364,29 @@ encode_cb_getattr4args(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr,
struct nfs4_delegation *dp =
container_of(fattr, struct nfs4_delegation, dl_cb_fattr);
struct knfsd_fh *fh = &dp->dl_stid.sc_file->fi_fhandle;
u32 bmap[1];
bmap[0] = FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE;
encode_nfs_cb_opnum4(xdr, OP_CB_GETATTR);
encode_nfs_fh4(xdr, fh);
encode_bitmap4(xdr, fattr->ncf_cb_bmap, ARRAY_SIZE(fattr->ncf_cb_bmap));
encode_bitmap4(xdr, bmap, ARRAY_SIZE(bmap));
hdr->nops++;
}
static u32 highest_slotid(struct nfsd4_session *ses)
{
u32 idx;
spin_lock(&ses->se_lock);
idx = fls(~ses->se_cb_slot_avail);
if (idx > 0)
--idx;
idx = max(idx, ses->se_cb_highest_slot);
spin_unlock(&ses->se_lock);
return idx;
}
/*
* CB_SEQUENCE4args
*
@ -397,15 +413,40 @@ static void encode_cb_sequence4args(struct xdr_stream *xdr,
encode_sessionid4(xdr, session);
p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4 + 4);
*p++ = cpu_to_be32(session->se_cb_seq_nr); /* csa_sequenceid */
*p++ = xdr_zero; /* csa_slotid */
*p++ = xdr_zero; /* csa_highest_slotid */
*p++ = cpu_to_be32(session->se_cb_seq_nr[cb->cb_held_slot]); /* csa_sequenceid */
*p++ = cpu_to_be32(cb->cb_held_slot); /* csa_slotid */
*p++ = cpu_to_be32(highest_slotid(session)); /* csa_highest_slotid */
*p++ = xdr_zero; /* csa_cachethis */
xdr_encode_empty_array(p); /* csa_referring_call_lists */
hdr->nops++;
}
static void update_cb_slot_table(struct nfsd4_session *ses, u32 target)
{
/* No need to do anything if nothing changed */
if (likely(target == READ_ONCE(ses->se_cb_highest_slot)))
return;
spin_lock(&ses->se_lock);
if (target > ses->se_cb_highest_slot) {
int i;
target = min(target, NFSD_BC_SLOT_TABLE_SIZE - 1);
/*
* Growing the slot table. Reset any new sequences to 1.
*
* NB: There is some debate about whether the RFC requires this,
* but the Linux client expects it.
*/
for (i = ses->se_cb_highest_slot + 1; i <= target; ++i)
ses->se_cb_seq_nr[i] = 1;
}
ses->se_cb_highest_slot = target;
spin_unlock(&ses->se_lock);
}
/*
* CB_SEQUENCE4resok
*
@ -433,7 +474,7 @@ static int decode_cb_sequence4resok(struct xdr_stream *xdr,
struct nfsd4_session *session = cb->cb_clp->cl_cb_session;
int status = -ESERVERFAULT;
__be32 *p;
u32 dummy;
u32 seqid, slotid, target;
/*
* If the server returns different values for sessionID, slotID or
@ -449,21 +490,22 @@ static int decode_cb_sequence4resok(struct xdr_stream *xdr,
}
p += XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN);
dummy = be32_to_cpup(p++);
if (dummy != session->se_cb_seq_nr) {
seqid = be32_to_cpup(p++);
if (seqid != session->se_cb_seq_nr[cb->cb_held_slot]) {
dprintk("NFS: %s Invalid sequence number\n", __func__);
goto out;
}
dummy = be32_to_cpup(p++);
if (dummy != 0) {
slotid = be32_to_cpup(p++);
if (slotid != cb->cb_held_slot) {
dprintk("NFS: %s Invalid slotid\n", __func__);
goto out;
}
/*
* FIXME: process highest slotid and target highest slotid
*/
p++; // ignore current highest slot value
target = be32_to_cpup(p++);
update_cb_slot_table(session, target);
status = 0;
out:
cb->cb_seq_status = status;
@ -1164,6 +1206,22 @@ void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
spin_unlock(&clp->cl_lock);
}
static int grab_slot(struct nfsd4_session *ses)
{
int idx;
spin_lock(&ses->se_lock);
idx = ffs(ses->se_cb_slot_avail) - 1;
if (idx < 0 || idx > ses->se_cb_highest_slot) {
spin_unlock(&ses->se_lock);
return -1;
}
/* clear the bit for the slot */
ses->se_cb_slot_avail &= ~BIT(idx);
spin_unlock(&ses->se_lock);
return idx;
}
/*
* There's currently a single callback channel slot.
* If the slot is available, then mark it busy. Otherwise, set the
@ -1172,28 +1230,32 @@ void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
static bool nfsd41_cb_get_slot(struct nfsd4_callback *cb, struct rpc_task *task)
{
struct nfs4_client *clp = cb->cb_clp;
struct nfsd4_session *ses = clp->cl_cb_session;
if (!cb->cb_holds_slot &&
test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) {
if (cb->cb_held_slot >= 0)
return true;
cb->cb_held_slot = grab_slot(ses);
if (cb->cb_held_slot < 0) {
rpc_sleep_on(&clp->cl_cb_waitq, task, NULL);
/* Race breaker */
if (test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) {
dprintk("%s slot is busy\n", __func__);
cb->cb_held_slot = grab_slot(ses);
if (cb->cb_held_slot < 0)
return false;
}
rpc_wake_up_queued_task(&clp->cl_cb_waitq, task);
}
cb->cb_holds_slot = true;
return true;
}
static void nfsd41_cb_release_slot(struct nfsd4_callback *cb)
{
struct nfs4_client *clp = cb->cb_clp;
struct nfsd4_session *ses = clp->cl_cb_session;
if (cb->cb_holds_slot) {
cb->cb_holds_slot = false;
clear_bit(0, &clp->cl_cb_slot_busy);
if (cb->cb_held_slot >= 0) {
spin_lock(&ses->se_lock);
ses->se_cb_slot_avail |= BIT(cb->cb_held_slot);
spin_unlock(&ses->se_lock);
cb->cb_held_slot = -1;
rpc_wake_up_next(&clp->cl_cb_waitq);
}
}
@ -1210,8 +1272,8 @@ static void nfsd41_destroy_cb(struct nfsd4_callback *cb)
}
/*
* TODO: cb_sequence should support referring call lists, cachethis, multiple
* slots, and mark callback channel down on communication errors.
* TODO: cb_sequence should support referring call lists, cachethis,
* and mark callback channel down on communication errors.
*/
static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
{
@ -1253,7 +1315,7 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback
return true;
}
if (!cb->cb_holds_slot)
if (cb->cb_held_slot < 0)
goto need_restart;
/* This is the operation status code for CB_SEQUENCE */
@ -1267,10 +1329,10 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback
* If CB_SEQUENCE returns an error, then the state of the slot
* (sequence ID, cached reply) MUST NOT change.
*/
++session->se_cb_seq_nr;
++session->se_cb_seq_nr[cb->cb_held_slot];
break;
case -ESERVERFAULT:
++session->se_cb_seq_nr;
++session->se_cb_seq_nr[cb->cb_held_slot];
nfsd4_mark_cb_fault(cb->cb_clp);
ret = false;
break;
@ -1296,17 +1358,16 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback
case -NFS4ERR_BADSLOT:
goto retry_nowait;
case -NFS4ERR_SEQ_MISORDERED:
if (session->se_cb_seq_nr != 1) {
session->se_cb_seq_nr = 1;
if (session->se_cb_seq_nr[cb->cb_held_slot] != 1) {
session->se_cb_seq_nr[cb->cb_held_slot] = 1;
goto retry_nowait;
}
break;
default:
nfsd4_mark_cb_fault(cb->cb_clp);
}
nfsd41_cb_release_slot(cb);
trace_nfsd_cb_free_slot(task, cb);
nfsd41_cb_release_slot(cb);
if (RPC_SIGNALLED(task))
goto need_restart;
@ -1461,6 +1522,8 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb)
ses = c->cn_session;
}
spin_unlock(&clp->cl_lock);
if (!c)
return;
err = setup_callback_client(clp, &conn, ses);
if (err) {
@ -1524,7 +1587,7 @@ void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp,
INIT_WORK(&cb->cb_work, nfsd4_run_cb_work);
cb->cb_status = 0;
cb->cb_need_restart = false;
cb->cb_holds_slot = false;
cb->cb_held_slot = -1;
}
/**

View File

@ -57,6 +57,8 @@ module_param(inter_copy_offload_enable, bool, 0644);
MODULE_PARM_DESC(inter_copy_offload_enable,
"Enable inter server to server copy offload. Default: false");
static void cleanup_async_copy(struct nfsd4_copy *copy);
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
static int nfsd4_ssc_umount_timeout = 900000; /* default to 15 mins */
module_param(nfsd4_ssc_umount_timeout, int, 0644);
@ -1276,6 +1278,71 @@ nfsd4_clone(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
return status;
}
/**
* nfsd4_has_active_async_copies - Check for ongoing copy operations
* @clp: Client to be checked
*
* NFSD maintains state for async COPY operations after they complete,
* and this state remains in the nfs4_client's async_copies list.
* Ongoing copies should block the destruction of the nfs4_client, but
* completed copies should not.
*
* Return values:
* %true: At least one active async COPY is ongoing
* %false: No active async COPY operations were found
*/
bool nfsd4_has_active_async_copies(struct nfs4_client *clp)
{
struct nfsd4_copy *copy;
bool result = false;
spin_lock(&clp->async_lock);
list_for_each_entry(copy, &clp->async_copies, copies) {
if (!test_bit(NFSD4_COPY_F_COMPLETED, &copy->cp_flags) &&
!test_bit(NFSD4_COPY_F_STOPPED, &copy->cp_flags)) {
result = true;
break;
}
}
spin_unlock(&clp->async_lock);
return result;
}
/**
* nfsd4_async_copy_reaper - Purge completed copies
* @nn: Network namespace with possible active copy information
*/
void nfsd4_async_copy_reaper(struct nfsd_net *nn)
{
struct nfs4_client *clp;
struct nfsd4_copy *copy;
LIST_HEAD(reaplist);
spin_lock(&nn->client_lock);
list_for_each_entry(clp, &nn->client_lru, cl_lru) {
struct list_head *pos, *next;
spin_lock(&clp->async_lock);
list_for_each_safe(pos, next, &clp->async_copies) {
copy = list_entry(pos, struct nfsd4_copy, copies);
if (test_bit(NFSD4_COPY_F_OFFLOAD_DONE, &copy->cp_flags)) {
if (--copy->cp_ttl) {
list_del_init(&copy->copies);
list_add(&copy->copies, &reaplist);
}
}
}
spin_unlock(&clp->async_lock);
}
spin_unlock(&nn->client_lock);
while (!list_empty(&reaplist)) {
copy = list_first_entry(&reaplist, struct nfsd4_copy, copies);
list_del_init(&copy->copies);
cleanup_async_copy(copy);
}
}
static void nfs4_put_copy(struct nfsd4_copy *copy)
{
if (!refcount_dec_and_test(&copy->refcount))
@ -1287,12 +1354,13 @@ static void nfs4_put_copy(struct nfsd4_copy *copy)
static void nfsd4_stop_copy(struct nfsd4_copy *copy)
{
trace_nfsd_copy_async_cancel(copy);
if (!test_and_set_bit(NFSD4_COPY_F_STOPPED, &copy->cp_flags))
kthread_stop(copy->copy_task);
nfs4_put_copy(copy);
}
static struct nfsd4_copy *nfsd4_get_copy(struct nfs4_client *clp)
static struct nfsd4_copy *nfsd4_unhash_copy(struct nfs4_client *clp)
{
struct nfsd4_copy *copy = NULL;
@ -1301,6 +1369,9 @@ static struct nfsd4_copy *nfsd4_get_copy(struct nfs4_client *clp)
copy = list_first_entry(&clp->async_copies, struct nfsd4_copy,
copies);
refcount_inc(&copy->refcount);
copy->cp_clp = NULL;
if (!list_empty(&copy->copies))
list_del_init(&copy->copies);
}
spin_unlock(&clp->async_lock);
return copy;
@ -1310,7 +1381,7 @@ void nfsd4_shutdown_copy(struct nfs4_client *clp)
{
struct nfsd4_copy *copy;
while ((copy = nfsd4_get_copy(clp)) != NULL)
while ((copy = nfsd4_unhash_copy(clp)) != NULL)
nfsd4_stop_copy(copy);
}
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
@ -1598,8 +1669,10 @@ static void nfsd4_cb_offload_release(struct nfsd4_callback *cb)
{
struct nfsd4_cb_offload *cbo =
container_of(cb, struct nfsd4_cb_offload, co_cb);
struct nfsd4_copy *copy =
container_of(cbo, struct nfsd4_copy, cp_cb_offload);
kfree(cbo);
set_bit(NFSD4_COPY_F_OFFLOAD_DONE, &copy->cp_flags);
}
static int nfsd4_cb_offload_done(struct nfsd4_callback *cb,
@ -1609,6 +1682,13 @@ static int nfsd4_cb_offload_done(struct nfsd4_callback *cb,
container_of(cb, struct nfsd4_cb_offload, co_cb);
trace_nfsd_cb_offload_done(&cbo->co_res.cb_stateid, task);
switch (task->tk_status) {
case -NFS4ERR_DELAY:
if (cbo->co_retries--) {
rpc_delay(task, 1 * HZ);
return 0;
}
}
return 1;
}
@ -1732,15 +1812,12 @@ static void cleanup_async_copy(struct nfsd4_copy *copy)
static void nfsd4_send_cb_offload(struct nfsd4_copy *copy)
{
struct nfsd4_cb_offload *cbo;
cbo = kzalloc(sizeof(*cbo), GFP_KERNEL);
if (!cbo)
return;
struct nfsd4_cb_offload *cbo = &copy->cp_cb_offload;
memcpy(&cbo->co_res, &copy->cp_res, sizeof(copy->cp_res));
memcpy(&cbo->co_fh, &copy->fh, sizeof(copy->fh));
cbo->co_nfserr = copy->nfserr;
cbo->co_retries = 5;
nfsd4_init_cb(&cbo->co_cb, copy->cp_clp, &nfsd4_cb_offload_ops,
NFSPROC4_CLNT_CB_OFFLOAD);
@ -1786,10 +1863,13 @@ static int nfsd4_do_async_copy(void *data)
}
do_callback:
/* The kthread exits forthwith. Ensure that a subsequent
* OFFLOAD_CANCEL won't try to kill it again. */
set_bit(NFSD4_COPY_F_STOPPED, &copy->cp_flags);
set_bit(NFSD4_COPY_F_COMPLETED, &copy->cp_flags);
trace_nfsd_copy_async_done(copy);
nfsd4_send_cb_offload(copy);
cleanup_async_copy(copy);
return 0;
}
@ -1843,6 +1923,7 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
async_copy->cp_nn = nn;
INIT_LIST_HEAD(&async_copy->copies);
refcount_set(&async_copy->refcount, 1);
async_copy->cp_ttl = NFSD_COPY_INITIAL_TTL;
/* Arbitrary cap on number of pending async copy operations */
if (atomic_inc_return(&nn->pending_async_copies) >
(int)rqstp->rq_pool->sp_nrthreads)
@ -2780,6 +2861,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp)
if (op->opdesc->op_get_currentstateid)
op->opdesc->op_get_currentstateid(cstate, &op->u);
op->status = op->opdesc->op_func(rqstp, cstate, &op->u);
trace_nfsd_compound_op_err(rqstp, op->opnum, op->status);
/* Only from SEQUENCE */
if (cstate->status == nfserr_replay_cache) {
@ -2796,7 +2878,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp)
if (current_fh->fh_export &&
need_wrongsec_check(rqstp))
op->status = check_nfsd_access(current_fh->fh_export, rqstp);
op->status = check_nfsd_access(current_fh->fh_export, rqstp, false);
}
encode_op:
if (op->status == nfserr_replay_me) {
@ -3452,6 +3534,7 @@ static const struct nfsd4_operation nfsd4_ops[] = {
/* NFSv4.1 operations */
[OP_EXCHANGE_ID] = {
.op_func = nfsd4_exchange_id,
.op_release = nfsd4_exchange_id_release,
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP
| OP_MODIFIES_SOMETHING,
.op_name = "OP_EXCHANGE_ID",

View File

@ -659,7 +659,8 @@ nfs4_reset_recoverydir(char *recdir)
return status;
status = -ENOTDIR;
if (d_is_dir(path.dentry)) {
strcpy(user_recovery_dirname, recdir);
strscpy(user_recovery_dirname, recdir,
sizeof(user_recovery_dirname));
status = 0;
}
path_put(&path);

View File

@ -149,14 +149,14 @@ void nfsd4_destroy_laundry_wq(void)
static bool is_session_dead(struct nfsd4_session *ses)
{
return ses->se_flags & NFS4_SESSION_DEAD;
return ses->se_dead;
}
static __be32 mark_session_dead_locked(struct nfsd4_session *ses, int ref_held_by_me)
{
if (atomic_read(&ses->se_ref) > ref_held_by_me)
return nfserr_jukebox;
ses->se_flags |= NFS4_SESSION_DEAD;
ses->se_dead = true;
return nfs_ok;
}
@ -572,13 +572,6 @@ opaque_hashval(const void *ptr, int nbytes)
return x;
}
static void nfsd4_free_file_rcu(struct rcu_head *rcu)
{
struct nfs4_file *fp = container_of(rcu, struct nfs4_file, fi_rcu);
kmem_cache_free(file_slab, fp);
}
void
put_nfs4_file(struct nfs4_file *fi)
{
@ -586,7 +579,7 @@ put_nfs4_file(struct nfs4_file *fi)
nfsd4_file_hash_remove(fi);
WARN_ON_ONCE(!list_empty(&fi->fi_clnt_odstate));
WARN_ON_ONCE(!list_empty(&fi->fi_delegations));
call_rcu(&fi->fi_rcu, nfsd4_free_file_rcu);
kfree_rcu(fi, fi_rcu);
}
}
@ -1184,7 +1177,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp,
nfsd4_init_cb(&dp->dl_cb_fattr.ncf_getattr, dp->dl_stid.sc_client,
&nfsd4_cb_getattr_ops, NFSPROC4_CLNT_CB_GETATTR);
dp->dl_cb_fattr.ncf_file_modified = false;
dp->dl_cb_fattr.ncf_cb_bmap[0] = FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE;
get_nfs4_file(fp);
dp->dl_stid.sc_file = fp;
return dp;
@ -1660,6 +1652,14 @@ static void release_open_stateid(struct nfs4_ol_stateid *stp)
free_ol_stateid_reaplist(&reaplist);
}
static bool nfs4_openowner_unhashed(struct nfs4_openowner *oo)
{
lockdep_assert_held(&oo->oo_owner.so_client->cl_lock);
return list_empty(&oo->oo_owner.so_strhash) &&
list_empty(&oo->oo_perclient);
}
static void unhash_openowner_locked(struct nfs4_openowner *oo)
{
struct nfs4_client *clp = oo->oo_owner.so_client;
@ -2010,8 +2010,10 @@ static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fattrs,
}
memcpy(&new->se_fchannel, fattrs, sizeof(struct nfsd4_channel_attrs));
memcpy(&new->se_bchannel, battrs, sizeof(struct nfsd4_channel_attrs));
new->se_cb_slot_avail = ~0U;
new->se_cb_highest_slot = min(battrs->maxreqs - 1,
NFSD_BC_SLOT_TABLE_SIZE - 1);
spin_lock_init(&new->se_lock);
return new;
out_free:
while (i--)
@ -2142,11 +2144,14 @@ static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, stru
INIT_LIST_HEAD(&new->se_conns);
new->se_cb_seq_nr = 1;
new->se_flags = cses->flags;
atomic_set(&new->se_ref, 0);
new->se_dead = false;
new->se_cb_prog = cses->callback_prog;
new->se_cb_sec = cses->cb_sec;
atomic_set(&new->se_ref, 0);
for (idx = 0; idx < NFSD_BC_SLOT_TABLE_SIZE; ++idx)
new->se_cb_seq_nr[idx] = 1;
idx = hash_sessionid(&new->se_sessionid);
list_add(&new->se_hash, &nn->sessionid_hashtbl[idx]);
spin_lock(&clp->cl_lock);
@ -2239,21 +2244,16 @@ STALE_CLIENTID(clientid_t *clid, struct nfsd_net *nn)
return 1;
}
/*
* XXX Should we use a slab cache ?
* This type of memory management is somewhat inefficient, but we use it
* anyway since SETCLIENTID is not a common operation.
*/
static struct nfs4_client *alloc_client(struct xdr_netobj name,
struct nfsd_net *nn)
{
struct nfs4_client *clp;
int i;
if (atomic_read(&nn->nfs4_client_count) >= nn->nfs4_max_clients) {
if (atomic_read(&nn->nfs4_client_count) >= nn->nfs4_max_clients &&
atomic_read(&nn->nfsd_courtesy_clients) > 0)
mod_delayed_work(laundry_wq, &nn->laundromat_work, 0);
return NULL;
}
clp = kmem_cache_zalloc(client_slab, GFP_KERNEL);
if (clp == NULL)
return NULL;
@ -3160,7 +3160,6 @@ static struct nfs4_client *create_client(struct xdr_netobj name,
kref_init(&clp->cl_nfsdfs.cl_ref);
nfsd4_init_cb(&clp->cl_cb_null, clp, NULL, NFSPROC4_CLNT_CB_NULL);
clp->cl_time = ktime_get_boottime_seconds();
clear_bit(0, &clp->cl_cb_slot_busy);
copy_verf(clp, verf);
memcpy(&clp->cl_addr, sa, sizeof(struct sockaddr_storage));
clp->cl_cb_session = NULL;
@ -3487,7 +3486,7 @@ static bool client_has_state(struct nfs4_client *clp)
#endif
|| !list_empty(&clp->cl_delegations)
|| !list_empty(&clp->cl_sessions)
|| !list_empty(&clp->async_copies);
|| nfsd4_has_active_async_copies(clp);
}
static __be32 copy_impl_id(struct nfs4_client *clp,
@ -3525,6 +3524,12 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
__func__, rqstp, exid, exid->clname.len, exid->clname.data,
addr_str, exid->flags, exid->spa_how);
exid->server_impl_name = kasprintf(GFP_KERNEL, "%s %s %s %s",
utsname()->sysname, utsname()->release,
utsname()->version, utsname()->machine);
if (!exid->server_impl_name)
return nfserr_jukebox;
if (exid->flags & ~EXCHGID4_FLAG_MASK_A)
return nfserr_inval;
@ -3662,6 +3667,23 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
exid->seqid = conf->cl_cs_slot.sl_seqid + 1;
nfsd4_set_ex_flags(conf, exid);
exid->nii_domain.len = sizeof("kernel.org") - 1;
exid->nii_domain.data = "kernel.org";
/*
* Note that RFC 8881 places no length limit on
* nii_name, but this implementation permits no
* more than NFS4_OPAQUE_LIMIT bytes.
*/
exid->nii_name.len = strlen(exid->server_impl_name);
if (exid->nii_name.len > NFS4_OPAQUE_LIMIT)
exid->nii_name.len = NFS4_OPAQUE_LIMIT;
exid->nii_name.data = exid->server_impl_name;
/* just send zeros - the date is in nii_name */
exid->nii_time.tv_sec = 0;
exid->nii_time.tv_nsec = 0;
dprintk("nfsd4_exchange_id seqid %d flags %x\n",
conf->cl_cs_slot.sl_seqid, conf->cl_exchange_flags);
status = nfs_ok;
@ -3678,6 +3700,14 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
return status;
}
void
nfsd4_exchange_id_release(union nfsd4_op_u *u)
{
struct nfsd4_exchange_id *exid = &u->exchange_id;
kfree(exid->server_impl_name);
}
static __be32 check_slot_seqid(u32 seqid, u32 slot_seqid, bool slot_inuse)
{
/* The slot is in use, and no response has been sent. */
@ -3911,6 +3941,8 @@ nfsd4_create_session(struct svc_rqst *rqstp,
cr_ses->flags &= ~SESSION4_PERSIST;
/* Upshifting from TCP to RDMA is not supported */
cr_ses->flags &= ~SESSION4_RDMA;
/* Report the correct number of backchannel slots */
cr_ses->back_channel.maxreqs = new->se_cb_highest_slot + 1;
init_session(rqstp, new, conf, cr_ses);
nfsd4_get_session_locked(new);
@ -3931,7 +3963,6 @@ nfsd4_create_session(struct svc_rqst *rqstp,
return status;
out_expired_error:
old = NULL;
/*
* Revert the slot seq_nr change so the server will process
* the client's resend instead of returning a cached response.
@ -3946,8 +3977,6 @@ nfsd4_create_session(struct svc_rqst *rqstp,
out_free_conn:
spin_unlock(&nn->client_lock);
free_conn(conn);
if (old)
expire_client(old);
out_free_session:
__free_session(new);
out_release_drc_mem:
@ -4975,6 +5004,12 @@ init_open_stateid(struct nfs4_file *fp, struct nfsd4_open *open)
spin_lock(&oo->oo_owner.so_client->cl_lock);
spin_lock(&fp->fi_lock);
if (nfs4_openowner_unhashed(oo)) {
mutex_unlock(&stp->st_mutex);
stp = NULL;
goto out_unlock;
}
retstp = nfsd4_find_existing_open(fp, open);
if (retstp)
goto out_unlock;
@ -5957,7 +5992,7 @@ nfs4_delegation_stat(struct nfs4_delegation *dp, struct svc_fh *currentfh,
path.dentry = file_dentry(nf->nf_file);
rc = vfs_getattr(&path, stat,
(STATX_SIZE | STATX_CTIME | STATX_CHANGE_COOKIE),
(STATX_MODE | STATX_SIZE | STATX_CTIME | STATX_CHANGE_COOKIE),
AT_STATX_SYNC_AS_STAT);
nfsd_file_put(nf);
@ -6041,8 +6076,7 @@ nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
}
open->op_delegate_type = NFS4_OPEN_DELEGATE_WRITE;
dp->dl_cb_fattr.ncf_cur_fsize = stat.size;
dp->dl_cb_fattr.ncf_initial_cinfo =
nfsd4_change_attribute(&stat, d_inode(currentfh->fh_dentry));
dp->dl_cb_fattr.ncf_initial_cinfo = nfsd4_change_attribute(&stat);
trace_nfsd_deleg_write(&dp->dl_stid.sc_stateid);
} else {
open->op_delegate_type = NFS4_OPEN_DELEGATE_READ;
@ -6127,6 +6161,11 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
if (!stp) {
stp = init_open_stateid(fp, open);
if (!stp) {
status = nfserr_jukebox;
goto out;
}
if (!open->op_stp)
new_stp = true;
}
@ -6562,6 +6601,7 @@ nfs4_laundromat(struct nfsd_net *nn)
_free_cpntf_state_locked(nn, cps);
}
spin_unlock(&nn->s2s_cp_lock);
nfsd4_async_copy_reaper(nn);
nfs4_get_client_reaplist(nn, &reaplist, &lt);
nfs4_process_client_reaplist(&reaplist);
@ -7943,11 +7983,9 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (check_lock_length(lock->lk_offset, lock->lk_length))
return nfserr_inval;
if ((status = fh_verify(rqstp, &cstate->current_fh,
S_IFREG, NFSD_MAY_LOCK))) {
dprintk("NFSD: nfsd4_lock: permission denied!\n");
status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0);
if (status != nfs_ok)
return status;
}
sb = cstate->current_fh.fh_dentry->d_sb;
if (lock->lk_is_new) {
@ -8854,8 +8892,7 @@ nfsd4_get_writestateid(struct nfsd4_compound_state *cstate,
* nfsd4_deleg_getattr_conflict - Recall if GETATTR causes conflict
* @rqstp: RPC transaction context
* @dentry: dentry of inode to be checked for a conflict
* @modified: return true if file was modified
* @size: new size of file if modified is true
* @pdp: returned WRITE delegation, if one was found
*
* This function is called when there is a conflict between a write
* delegation and a change/size GETATTR from another client. The server
@ -8865,11 +8902,12 @@ nfsd4_get_writestateid(struct nfsd4_compound_state *cstate,
* 18.7.4.
*
* Returns 0 if there is no conflict; otherwise an nfs_stat
* code is returned.
* code is returned. If @pdp is set to a non-NULL value, then the
* caller must put the reference.
*/
__be32
nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
bool *modified, u64 *size)
struct nfs4_delegation **pdp)
{
__be32 status;
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
@ -8880,10 +8918,9 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
struct nfs4_cb_fattr *ncf;
struct inode *inode = d_inode(dentry);
*modified = false;
ctx = locks_inode_context(inode);
if (!ctx)
return 0;
return nfs_ok;
#define NON_NFSD_LEASE ((void *)1)
@ -8949,10 +8986,10 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
goto out_status;
}
ncf->ncf_cur_fsize = ncf->ncf_cb_fsize;
*size = ncf->ncf_cur_fsize;
*modified = true;
*pdp = dp;
return nfs_ok;
}
status = 0;
status = nfs_ok;
out_status:
nfs4_put_stid(&dp->dl_stid);
return status;

View File

@ -2652,13 +2652,10 @@ static __be32 nfsd4_encode_components_esc(struct xdr_stream *xdr, char sep,
strlen = end - str;
if (strlen) {
p = xdr_reserve_space(xdr, strlen + 4);
if (!p)
if (xdr_stream_encode_opaque(xdr, str, strlen) < 0)
return nfserr_resource;
p = xdr_encode_opaque(p, str, strlen);
count++;
}
else
} else
end++;
if (found_esc)
end = next;
@ -2699,7 +2696,6 @@ static __be32 nfsd4_encode_pathname4(struct xdr_stream *xdr,
const struct path *path)
{
struct path cur = *path;
__be32 *p;
struct dentry **components = NULL;
unsigned int ncomponents = 0;
__be32 err = nfserr_jukebox;
@ -2730,24 +2726,19 @@ static __be32 nfsd4_encode_pathname4(struct xdr_stream *xdr,
components[ncomponents++] = cur.dentry;
cur.dentry = dget_parent(cur.dentry);
}
err = nfserr_resource;
p = xdr_reserve_space(xdr, 4);
if (!p)
goto out_free;
*p++ = cpu_to_be32(ncomponents);
err = nfserr_resource;
if (xdr_stream_encode_u32(xdr, ncomponents) != XDR_UNIT)
goto out_free;
while (ncomponents) {
struct dentry *dentry = components[ncomponents - 1];
unsigned int len;
spin_lock(&dentry->d_lock);
len = dentry->d_name.len;
p = xdr_reserve_space(xdr, len + 4);
if (!p) {
if (xdr_stream_encode_opaque(xdr, dentry->d_name.name,
dentry->d_name.len) < 0) {
spin_unlock(&dentry->d_lock);
goto out_free;
}
p = xdr_encode_opaque(p, dentry->d_name.name, len);
dprintk("/%pd", dentry);
spin_unlock(&dentry->d_lock);
dput(dentry);
@ -2928,7 +2919,6 @@ struct nfsd4_fattr_args {
struct kstat stat;
struct kstatfs statfs;
struct nfs4_acl *acl;
u64 size;
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
void *context;
int contextlen;
@ -3040,14 +3030,14 @@ static __be32 nfsd4_encode_fattr4_change(struct xdr_stream *xdr,
return nfs_ok;
}
c = nfsd4_change_attribute(&args->stat, d_inode(args->dentry));
c = nfsd4_change_attribute(&args->stat);
return nfsd4_encode_changeid4(xdr, c);
}
static __be32 nfsd4_encode_fattr4_size(struct xdr_stream *xdr,
const struct nfsd4_fattr_args *args)
{
return nfsd4_encode_uint64_t(xdr, args->size);
return nfsd4_encode_uint64_t(xdr, args->stat.size);
}
static __be32 nfsd4_encode_fattr4_fsid(struct xdr_stream *xdr,
@ -3512,6 +3502,7 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
int ignore_crossmnt)
{
DECLARE_BITMAP(attr_bitmap, ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops));
struct nfs4_delegation *dp = NULL;
struct nfsd4_fattr_args args;
struct svc_fh *tempfh = NULL;
int starting_len = xdr->buf->len;
@ -3526,8 +3517,6 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
.dentry = dentry,
};
unsigned long bit;
bool file_modified = false;
u64 size = 0;
WARN_ON_ONCE(bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1);
WARN_ON_ONCE(!nfsd_attrs_supported(minorversion, bmval));
@ -3555,10 +3544,8 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
if (status)
goto out;
}
args.size = 0;
if (attrmask[0] & (FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE)) {
status = nfsd4_deleg_getattr_conflict(rqstp, dentry,
&file_modified, &size);
status = nfsd4_deleg_getattr_conflict(rqstp, dentry, &dp);
if (status)
goto out;
}
@ -3566,12 +3553,16 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
err = vfs_getattr(&path, &args.stat,
STATX_BASIC_STATS | STATX_BTIME | STATX_CHANGE_COOKIE,
AT_STATX_SYNC_AS_STAT);
if (dp) {
struct nfs4_cb_fattr *ncf = &dp->dl_cb_fattr;
if (ncf->ncf_file_modified)
args.stat.size = ncf->ncf_cur_fsize;
nfs4_put_stid(&dp->dl_stid);
}
if (err)
goto out_nfserr;
if (file_modified)
args.size = size;
else
args.size = args.stat.size;
if (!(args.stat.result_mask & STATX_BTIME))
/* underlying FS does not offer btime so we can't share it */
@ -3767,7 +3758,7 @@ nfsd4_encode_entry4_fattr(struct nfsd4_readdir *cd, const char *name,
nfserr = nfserrno(err);
goto out_put;
}
nfserr = check_nfsd_access(exp, cd->rd_rqstp);
nfserr = check_nfsd_access(exp, cd->rd_rqstp, false);
if (nfserr)
goto out_put;
@ -4825,6 +4816,25 @@ nfsd4_encode_server_owner4(struct xdr_stream *xdr, struct svc_rqst *rqstp)
return nfsd4_encode_opaque(xdr, nn->nfsd_name, strlen(nn->nfsd_name));
}
static __be32
nfsd4_encode_nfs_impl_id4(struct xdr_stream *xdr, struct nfsd4_exchange_id *exid)
{
__be32 status;
/* nii_domain */
status = nfsd4_encode_opaque(xdr, exid->nii_domain.data,
exid->nii_domain.len);
if (status != nfs_ok)
return status;
/* nii_name */
status = nfsd4_encode_opaque(xdr, exid->nii_name.data,
exid->nii_name.len);
if (status != nfs_ok)
return status;
/* nii_time */
return nfsd4_encode_nfstime4(xdr, &exid->nii_time);
}
static __be32
nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
union nfsd4_op_u *u)
@ -4859,8 +4869,11 @@ nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
if (nfserr != nfs_ok)
return nfserr;
/* eir_server_impl_id<1> */
if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT)
if (xdr_stream_encode_u32(xdr, 1) != XDR_UNIT)
return nfserr_resource;
nfserr = nfsd4_encode_nfs_impl_id4(xdr, exid);
if (nfserr != nfs_ok)
return nfserr;
return nfs_ok;
}

View File

@ -320,6 +320,7 @@ __fh_verify(struct svc_rqst *rqstp,
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct svc_export *exp = NULL;
bool may_bypass_gss = false;
struct dentry *dentry;
__be32 error;
@ -362,13 +363,12 @@ __fh_verify(struct svc_rqst *rqstp,
if (error)
goto out;
/*
* pseudoflavor restrictions are not enforced on NLM,
* which clients virtually always use auth_sys for,
* even while using RPCSEC_GSS for NFS.
*/
if (access & NFSD_MAY_LOCK || access & NFSD_MAY_BYPASS_GSS)
goto skip_pseudoflavor_check;
if ((access & NFSD_MAY_NLM) && (exp->ex_flags & NFSEXP_NOAUTHNLM))
/* NLM is allowed to fully bypass authentication */
goto out;
if (access & NFSD_MAY_BYPASS_GSS)
may_bypass_gss = true;
/*
* Clients may expect to be able to use auth_sys during mount,
* even if they use gss for everything else; see section 2.3.2
@ -376,13 +376,12 @@ __fh_verify(struct svc_rqst *rqstp,
*/
if (access & NFSD_MAY_BYPASS_GSS_ON_ROOT
&& exp->ex_path.dentry == dentry)
goto skip_pseudoflavor_check;
may_bypass_gss = true;
error = check_nfsd_access(exp, rqstp);
error = check_nfsd_access(exp, rqstp, may_bypass_gss);
if (error)
goto out;
skip_pseudoflavor_check:
/* Finally, check access permissions. */
error = nfsd_permission(cred, exp, dentry, access);
out:
@ -667,20 +666,18 @@ fh_update(struct svc_fh *fhp)
__be32 __must_check fh_fill_pre_attrs(struct svc_fh *fhp)
{
bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE);
struct inode *inode;
struct kstat stat;
__be32 err;
if (fhp->fh_no_wcc || fhp->fh_pre_saved)
return nfs_ok;
inode = d_inode(fhp->fh_dentry);
err = fh_getattr(fhp, &stat);
if (err)
return err;
if (v4)
fhp->fh_pre_change = nfsd4_change_attribute(&stat, inode);
fhp->fh_pre_change = nfsd4_change_attribute(&stat);
fhp->fh_pre_mtime = stat.mtime;
fhp->fh_pre_ctime = stat.ctime;
@ -697,7 +694,6 @@ __be32 __must_check fh_fill_pre_attrs(struct svc_fh *fhp)
__be32 fh_fill_post_attrs(struct svc_fh *fhp)
{
bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE);
struct inode *inode = d_inode(fhp->fh_dentry);
__be32 err;
if (fhp->fh_no_wcc)
@ -713,7 +709,7 @@ __be32 fh_fill_post_attrs(struct svc_fh *fhp)
fhp->fh_post_saved = true;
if (v4)
fhp->fh_post_change =
nfsd4_change_attribute(&fhp->fh_post_attr, inode);
nfsd4_change_attribute(&fhp->fh_post_attr);
return nfs_ok;
}
@ -770,7 +766,7 @@ char * SVCFH_fmt(struct svc_fh *fhp)
struct knfsd_fh *fh = &fhp->fh_handle;
static char buf[2+1+1+64*3+1];
if (fh->fh_size < 0 || fh->fh_size> 64)
if (fh->fh_size > 64)
return "bad-fh";
sprintf(buf, "%d: %*ph", fh->fh_size, fh->fh_size, fh->fh_raw);
return buf;
@ -804,7 +800,14 @@ enum fsid_source fsid_source(const struct svc_fh *fhp)
return FSIDSOURCE_DEV;
}
/*
/**
* nfsd4_change_attribute - Generate an NFSv4 change_attribute value
* @stat: inode attributes
*
* Caller must fill in @stat before calling, typically by invoking
* vfs_getattr() with STATX_MODE, STATX_CTIME, and STATX_CHANGE_COOKIE.
* Returns an unsigned 64-bit changeid4 value (RFC 8881 Section 3.2).
*
* We could use i_version alone as the change attribute. However, i_version
* can go backwards on a regular file after an unclean shutdown. On its own
* that doesn't necessarily cause a problem, but if i_version goes backwards
@ -821,13 +824,13 @@ enum fsid_source fsid_source(const struct svc_fh *fhp)
* assume that the new change attr is always logged to stable storage in some
* fashion before the results can be seen.
*/
u64 nfsd4_change_attribute(const struct kstat *stat, const struct inode *inode)
u64 nfsd4_change_attribute(const struct kstat *stat)
{
u64 chattr;
if (stat->result_mask & STATX_CHANGE_COOKIE) {
chattr = stat->change_cookie;
if (S_ISREG(inode->i_mode) &&
if (S_ISREG(stat->mode) &&
!(stat->attributes & STATX_ATTR_CHANGE_MONOTONIC)) {
chattr += (u64)stat->ctime.tv_sec << 30;
chattr += stat->ctime.tv_nsec;

View File

@ -297,8 +297,7 @@ static inline void fh_clear_pre_post_attrs(struct svc_fh *fhp)
fhp->fh_pre_saved = false;
}
u64 nfsd4_change_attribute(const struct kstat *stat,
const struct inode *inode);
u64 nfsd4_change_attribute(const struct kstat *stat);
__be32 __must_check fh_fill_pre_attrs(struct svc_fh *fhp);
__be32 fh_fill_post_attrs(struct svc_fh *fhp);
__be32 __must_check fh_fill_both_attrs(struct svc_fh *fhp);

View File

@ -71,8 +71,8 @@ struct nfsd4_callback {
struct work_struct cb_work;
int cb_seq_status;
int cb_status;
int cb_held_slot;
bool cb_need_restart;
bool cb_holds_slot;
};
struct nfsd4_callback_ops {
@ -137,10 +137,24 @@ struct nfs4_cpntf_state {
time64_t cpntf_time; /* last time stateid used */
};
/*
* RFC 7862 Section 4.8 states:
*
* | A copy offload stateid will be valid until either (A) the client
* | or server restarts or (B) the client returns the resource by
* | issuing an OFFLOAD_CANCEL operation or the client replies to a
* | CB_OFFLOAD operation.
*
* Because a client might not reply to a CB_OFFLOAD, or a reply
* might get lost due to connection loss, NFSD purges async copy
* state after a short period to prevent it from accumulating
* over time.
*/
#define NFSD_COPY_INITIAL_TTL 10
struct nfs4_cb_fattr {
struct nfsd4_callback ncf_getattr;
u32 ncf_cb_status;
u32 ncf_cb_bmap[1];
/* from CB_GETATTR reply */
u64 ncf_cb_change;
@ -290,6 +304,9 @@ struct nfsd4_conn {
unsigned char cn_flags;
};
/* Maximum number of slots that nfsd will use in the backchannel */
#define NFSD_BC_SLOT_TABLE_SIZE (sizeof(u32) * 8)
/*
* Representation of a v4.1+ session. These are refcounted in a similar fashion
* to the nfs4_client. References are only taken when the server is actively
@ -297,19 +314,19 @@ struct nfsd4_conn {
*/
struct nfsd4_session {
atomic_t se_ref;
spinlock_t se_lock;
u32 se_cb_slot_avail; /* bitmap of available slots */
u32 se_cb_highest_slot; /* highest slot client wants */
u32 se_cb_prog;
bool se_dead;
struct list_head se_hash; /* hash by sessionid */
struct list_head se_perclnt;
/* See SESSION4_PERSIST, etc. for standard flags; this is internal-only: */
#define NFS4_SESSION_DEAD 0x010
u32 se_flags;
struct nfs4_client *se_client;
struct nfs4_sessionid se_sessionid;
struct nfsd4_channel_attrs se_fchannel;
struct nfsd4_channel_attrs se_bchannel;
struct nfsd4_cb_sec se_cb_sec;
struct list_head se_conns;
u32 se_cb_prog;
u32 se_cb_seq_nr;
u32 se_cb_seq_nr[NFSD_BC_SLOT_TABLE_SIZE];
struct nfsd4_slot *se_slots[]; /* forward channel slots */
};
@ -443,9 +460,6 @@ struct nfs4_client {
*/
struct dentry *cl_nfsd_info_dentry;
/* for nfs41 callbacks */
/* We currently support a single back channel with a single slot */
unsigned long cl_cb_slot_busy;
struct rpc_wait_queue cl_cb_waitq; /* backchannel callers may */
/* wait here for slots */
struct net *net;
@ -742,6 +756,8 @@ extern void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp,
extern bool nfsd4_run_cb(struct nfsd4_callback *cb);
extern void nfsd4_shutdown_callback(struct nfs4_client *);
extern void nfsd4_shutdown_copy(struct nfs4_client *clp);
void nfsd4_async_copy_reaper(struct nfsd_net *nn);
bool nfsd4_has_active_async_copies(struct nfs4_client *clp);
extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(struct xdr_netobj name,
struct xdr_netobj princhash, struct nfsd_net *nn);
extern bool nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn);
@ -784,5 +800,5 @@ static inline bool try_to_expire_client(struct nfs4_client *clp)
}
extern __be32 nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp,
struct dentry *dentry, bool *file_modified, u64 *size);
struct dentry *dentry, struct nfs4_delegation **pdp);
#endif /* NFSD4_STATE_H */

View File

@ -79,7 +79,7 @@ DEFINE_NFSD_XDR_ERR_EVENT(cant_encode);
{ NFSD_MAY_READ, "READ" }, \
{ NFSD_MAY_SATTR, "SATTR" }, \
{ NFSD_MAY_TRUNC, "TRUNC" }, \
{ NFSD_MAY_LOCK, "LOCK" }, \
{ NFSD_MAY_NLM, "NLM" }, \
{ NFSD_MAY_OWNER_OVERRIDE, "OWNER_OVERRIDE" }, \
{ NFSD_MAY_LOCAL_ACCESS, "LOCAL_ACCESS" }, \
{ NFSD_MAY_BYPASS_GSS_ON_ROOT, "BYPASS_GSS_ON_ROOT" }, \
@ -163,7 +163,7 @@ TRACE_EVENT(nfsd_compound_decode_err,
__entry->opnum, __entry->status)
);
TRACE_EVENT(nfsd_compound_encode_err,
DECLARE_EVENT_CLASS(nfsd_compound_err_class,
TP_PROTO(
const struct svc_rqst *rqstp,
u32 opnum,
@ -184,6 +184,18 @@ TRACE_EVENT(nfsd_compound_encode_err,
__entry->opnum, __entry->status)
);
#define DEFINE_NFSD_COMPOUND_ERR_EVENT(name) \
DEFINE_EVENT(nfsd_compound_err_class, nfsd_compound_##name##_err, \
TP_PROTO( \
const struct svc_rqst *rqstp, \
u32 opnum, \
__be32 status \
), \
TP_ARGS(rqstp, opnum, status))
DEFINE_NFSD_COMPOUND_ERR_EVENT(op);
DEFINE_NFSD_COMPOUND_ERR_EVENT(encode);
#define show_fs_file_type(x) \
__print_symbolic(x, \
{ S_IFLNK, "LNK" }, \
@ -1685,7 +1697,7 @@ TRACE_EVENT(nfsd_cb_free_slot,
__entry->cl_id = sid->clientid.cl_id;
__entry->seqno = sid->sequence;
__entry->reserved = sid->reserved;
__entry->slot_seqno = session->se_cb_seq_nr;
__entry->slot_seqno = session->se_cb_seq_nr[cb->cb_held_slot];
),
TP_printk(SUNRPC_TRACE_TASK_SPECIFIER
" sessionid=%08x:%08x:%08x:%08x new slot seqno=%u",
@ -2232,7 +2244,7 @@ TRACE_EVENT(nfsd_copy_done,
)
);
TRACE_EVENT(nfsd_copy_async_done,
DECLARE_EVENT_CLASS(nfsd_copy_async_done_class,
TP_PROTO(
const struct nfsd4_copy *copy
),
@ -2301,6 +2313,15 @@ TRACE_EVENT(nfsd_copy_async_done,
)
);
#define DEFINE_COPY_ASYNC_DONE_EVENT(name) \
DEFINE_EVENT(nfsd_copy_async_done_class, \
nfsd_copy_async_##name, \
TP_PROTO(const struct nfsd4_copy *copy), \
TP_ARGS(copy))
DEFINE_COPY_ASYNC_DONE_EVENT(done);
DEFINE_COPY_ASYNC_DONE_EVENT(cancel);
#endif /* _NFSD_TRACE_H */
#undef TRACE_INCLUDE_PATH

View File

@ -35,7 +35,6 @@
#include "xdr3.h"
#ifdef CONFIG_NFSD_V4
#include "../internal.h"
#include "acl.h"
#include "idmap.h"
#include "xdr4.h"
@ -321,7 +320,7 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
err = nfsd_lookup_dentry(rqstp, fhp, name, len, &exp, &dentry);
if (err)
return err;
err = check_nfsd_access(exp, rqstp);
err = check_nfsd_access(exp, rqstp, false);
if (err)
goto out;
/*
@ -861,8 +860,7 @@ int nfsd_open_break_lease(struct inode *inode, int access)
* N.B. After this call fhp needs an fh_put
*/
static int
__nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type,
int may_flags, struct file **filp)
__nfsd_open(struct svc_fh *fhp, umode_t type, int may_flags, struct file **filp)
{
struct path path;
struct inode *inode;
@ -932,7 +930,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type,
retry:
err = fh_verify(rqstp, fhp, type, may_flags);
if (!err) {
host_err = __nfsd_open(rqstp, fhp, type, may_flags, filp);
host_err = __nfsd_open(fhp, type, may_flags, filp);
if (host_err == -EOPENSTALE && !retried) {
retried = true;
fh_put(fhp);
@ -945,7 +943,6 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type,
/**
* nfsd_open_verified - Open a regular file for the filecache
* @rqstp: RPC request
* @fhp: NFS filehandle of the file to open
* @may_flags: internal permission flags
* @filp: OUT: open "struct file *"
@ -953,10 +950,9 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type,
* Returns zero on success, or a negative errno value.
*/
int
nfsd_open_verified(struct svc_rqst *rqstp, struct svc_fh *fhp, int may_flags,
struct file **filp)
nfsd_open_verified(struct svc_fh *fhp, int may_flags, struct file **filp)
{
return __nfsd_open(rqstp, fhp, S_IFREG, may_flags, filp);
return __nfsd_open(fhp, S_IFREG, may_flags, filp);
}
/*
@ -2509,7 +2505,7 @@ nfsd_permission(struct svc_cred *cred, struct svc_export *exp,
(acc & NFSD_MAY_EXEC)? " exec" : "",
(acc & NFSD_MAY_SATTR)? " sattr" : "",
(acc & NFSD_MAY_TRUNC)? " trunc" : "",
(acc & NFSD_MAY_LOCK)? " lock" : "",
(acc & NFSD_MAY_NLM)? " nlm" : "",
(acc & NFSD_MAY_OWNER_OVERRIDE)? " owneroverride" : "",
inode->i_mode,
IS_IMMUTABLE(inode)? " immut" : "",
@ -2534,16 +2530,6 @@ nfsd_permission(struct svc_cred *cred, struct svc_export *exp,
if ((acc & NFSD_MAY_TRUNC) && IS_APPEND(inode))
return nfserr_perm;
if (acc & NFSD_MAY_LOCK) {
/* If we cannot rely on authentication in NLM requests,
* just allow locks, otherwise require read permission, or
* ownership
*/
if (exp->ex_flags & NFSEXP_NOAUTHNLM)
return 0;
else
acc = NFSD_MAY_READ | NFSD_MAY_OWNER_OVERRIDE;
}
/*
* The file owner always gets access permission for accesses that
* would normally be checked at open time. This is to make

View File

@ -20,7 +20,7 @@
#define NFSD_MAY_READ 0x004 /* == MAY_READ */
#define NFSD_MAY_SATTR 0x008
#define NFSD_MAY_TRUNC 0x010
#define NFSD_MAY_LOCK 0x020
#define NFSD_MAY_NLM 0x020 /* request is from lockd */
#define NFSD_MAY_MASK 0x03f
/* extra hints to permission and open routines: */
@ -114,8 +114,8 @@ __be32 nfsd_setxattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
int nfsd_open_break_lease(struct inode *, int);
__be32 nfsd_open(struct svc_rqst *, struct svc_fh *, umode_t,
int, struct file **);
int nfsd_open_verified(struct svc_rqst *rqstp, struct svc_fh *fhp,
int may_flags, struct file **filp);
int nfsd_open_verified(struct svc_fh *fhp, int may_flags,
struct file **filp);
__be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct file *file, loff_t offset,
unsigned long *count,

View File

@ -567,6 +567,7 @@ struct nfsd4_exchange_id {
struct xdr_netobj nii_domain;
struct xdr_netobj nii_name;
struct timespec64 nii_time;
char *server_impl_name;
};
struct nfsd4_sequence {
@ -675,6 +676,7 @@ struct nfsd4_cb_offload {
struct nfsd4_callback co_cb;
struct nfsd42_write_res co_res;
__be32 co_nfserr;
unsigned int co_retries;
struct knfsd_fh co_fh;
};
@ -693,12 +695,16 @@ struct nfsd4_copy {
#define NFSD4_COPY_F_SYNCHRONOUS (2)
#define NFSD4_COPY_F_COMMITTED (3)
#define NFSD4_COPY_F_COMPLETED (4)
#define NFSD4_COPY_F_OFFLOAD_DONE (5)
/* response */
__be32 nfserr;
struct nfsd42_write_res cp_res;
struct knfsd_fh fh;
/* offload callback */
struct nfsd4_cb_offload cp_cb_offload;
struct nfs4_client *cp_clp;
struct nfsd_file *nf_src;
@ -709,6 +715,7 @@ struct nfsd4_copy {
struct list_head copies;
struct task_struct *copy_task;
refcount_t refcount;
unsigned int cp_ttl;
struct nfsd4_ssc_umount_item *ss_nsui;
struct nfs_fh c_fh;
@ -930,6 +937,7 @@ extern __be32 nfsd4_setclientid(struct svc_rqst *rqstp,
struct nfsd4_compound_state *, union nfsd4_op_u *u);
extern __be32 nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
struct nfsd4_compound_state *, union nfsd4_op_u *u);
void nfsd4_exchange_id_release(union nfsd4_op_u *u);
extern __be32 nfsd4_exchange_id(struct svc_rqst *rqstp,
struct nfsd4_compound_state *, union nfsd4_op_u *u);
extern __be32 nfsd4_backchannel_ctl(struct svc_rqst *,

View File

@ -278,9 +278,9 @@ __be32 nlmsvc_lock(struct svc_rqst *, struct nlm_file *,
struct nlm_host *, struct nlm_lock *, int,
struct nlm_cookie *, int);
__be32 nlmsvc_unlock(struct net *net, struct nlm_file *, struct nlm_lock *);
__be32 nlmsvc_testlock(struct svc_rqst *, struct nlm_file *,
struct nlm_host *, struct nlm_lock *,
struct nlm_lock *, struct nlm_cookie *);
__be32 nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
struct nlm_host *host, struct nlm_lock *lock,
struct nlm_lock *conflock);
__be32 nlmsvc_cancel_blocked(struct net *net, struct nlm_file *, struct nlm_lock *);
void nlmsvc_retry_blocked(struct svc_rqst *rqstp);
void nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *,

View File

@ -73,8 +73,6 @@ struct nlm_args {
u32 fsm_mode;
};
typedef struct nlm_args nlm_args;
/*
* Generic lockd result
*/

View File

@ -55,7 +55,7 @@ struct nfsd_localio_operations {
const struct cred *,
const struct nfs_fh *,
const fmode_t);
void (*nfsd_file_put_local)(struct nfsd_file *);
struct net *(*nfsd_file_put_local)(struct nfsd_file *);
struct file *(*nfsd_file_file)(struct nfsd_file *);
} ____cacheline_aligned;
@ -66,7 +66,7 @@ struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *,
struct rpc_clnt *, const struct cred *,
const struct nfs_fh *, const fmode_t);
static inline void nfs_to_nfsd_file_put_local(struct nfsd_file *localio)
static inline void nfs_to_nfsd_net_put(struct net *net)
{
/*
* Once reference to nfsd_serv is dropped, NFSD could be
@ -74,10 +74,22 @@ static inline void nfs_to_nfsd_file_put_local(struct nfsd_file *localio)
* by always taking RCU.
*/
rcu_read_lock();
nfs_to->nfsd_file_put_local(localio);
nfs_to->nfsd_serv_put(net);
rcu_read_unlock();
}
static inline void nfs_to_nfsd_file_put_local(struct nfsd_file *localio)
{
/*
* Must not hold RCU otherwise nfsd_file_put() can easily trigger:
* "Voluntary context switch within RCU read-side critical section!"
* by scheduling deep in underlying filesystem (e.g. XFS).
*/
struct net *net = nfs_to->nfsd_file_put_local(localio);
nfs_to_nfsd_net_put(net);
}
#else /* CONFIG_NFS_LOCALIO */
static inline void nfsd_localio_ops_init(void)
{

View File

@ -680,6 +680,27 @@ xdr_stream_decode_u32(struct xdr_stream *xdr, __u32 *ptr)
return 0;
}
/**
* xdr_stream_decode_be32 - Decode a big-endian 32-bit integer
* @xdr: pointer to xdr_stream
* @ptr: location to store integer
*
* Return values:
* %0 on success
* %-EBADMSG on XDR buffer overflow
*/
static inline ssize_t
xdr_stream_decode_be32(struct xdr_stream *xdr, __be32 *ptr)
{
const size_t count = sizeof(*ptr);
__be32 *p = xdr_inline_decode(xdr, count);
if (unlikely(!p))
return -EBADMSG;
*ptr = *p;
return 0;
}
/**
* xdr_stream_decode_u64 - Decode a 64-bit integer
* @xdr: pointer to xdr_stream

View File

@ -23,4 +23,13 @@ typedef struct {
u8 *data;
} opaque;
#define XDR_void (0)
#define XDR_bool (1)
#define XDR_int (1)
#define XDR_unsigned_int (1)
#define XDR_long (1)
#define XDR_unsigned_long (1)
#define XDR_hyper (2)
#define XDR_unsigned_hyper (2)
#endif /* _SUNRPC_XDRGEN__DEFS_H_ */

View File

@ -1427,7 +1427,9 @@ static int c_show(struct seq_file *m, void *p)
seq_printf(m, "# expiry=%lld refcnt=%d flags=%lx\n",
convert_to_wallclock(cp->expiry_time),
kref_read(&cp->ref), cp->flags);
cache_get(cp);
if (!cache_get_rcu(cp))
return 0;
if (cache_check(cd, cp, NULL))
/* cache_check does a cache_put on failure */
seq_puts(m, "# ");

View File

@ -233,25 +233,34 @@ static int svc_rdma_proc_init(void)
rc = percpu_counter_init(&svcrdma_stat_read, 0, GFP_KERNEL);
if (rc)
goto out_err;
goto err;
rc = percpu_counter_init(&svcrdma_stat_recv, 0, GFP_KERNEL);
if (rc)
goto out_err;
goto err_read;
rc = percpu_counter_init(&svcrdma_stat_sq_starve, 0, GFP_KERNEL);
if (rc)
goto out_err;
goto err_recv;
rc = percpu_counter_init(&svcrdma_stat_write, 0, GFP_KERNEL);
if (rc)
goto out_err;
goto err_sq;
svcrdma_table_header = register_sysctl("sunrpc/svc_rdma",
svcrdma_parm_table);
if (!svcrdma_table_header)
goto err_write;
return 0;
out_err:
err_write:
rc = -ENOMEM;
percpu_counter_destroy(&svcrdma_stat_write);
err_sq:
percpu_counter_destroy(&svcrdma_stat_sq_starve);
err_recv:
percpu_counter_destroy(&svcrdma_stat_recv);
err_read:
percpu_counter_destroy(&svcrdma_stat_read);
err:
return rc;
}

View File

@ -493,7 +493,13 @@ static bool xdr_check_write_chunk(struct svc_rdma_recv_ctxt *rctxt)
if (xdr_stream_decode_u32(&rctxt->rc_stream, &segcount))
return false;
/* A bogus segcount causes this buffer overflow check to fail. */
/* Before trusting the segcount value enough to use it in
* a computation, perform a simple range check. This is an
* arbitrary but sensible limit (ie, not architectural).
*/
if (unlikely(segcount > RPCSVC_MAXPAGES))
return false;
p = xdr_inline_decode(&rctxt->rc_stream,
segcount * rpcrdma_segment_maxsz * sizeof(*p));
return p != NULL;

11
tools/net/sunrpc/extract.sh Executable file
View File

@ -0,0 +1,11 @@
#! /bin/sh
# SPDX-License-Identifier: GPL-2.0
#
# Extract an RPC protocol specification from an RFC document.
# The version of this script comes from RFC 8166.
#
# Usage:
# $ extract.sh < rfcNNNN.txt > protocol.x
#
grep '^ *///' | sed 's?^ */// ??' | sed 's?^ *///$??'

View File

@ -150,6 +150,23 @@ Pragma directives specify exceptions to the normal generation of
encoding and decoding functions. Currently one directive is
implemented: "public".
Pragma big_endian
------ ----------
pragma big_endian <enum> ;
For variables that might contain only a small number values, it
is more efficient to avoid the byte-swap when encoding or decoding
on little-endian machines. Such is often the case with error status
codes. For example:
pragma big_endian nfsstat3;
In this case, when generating an XDR struct or union containing a
field of type "nfsstat3", xdrgen will make the type of that field
"__be32" instead of "enum nfsstat3". XDR unions then switch on the
non-byte-swapped value of that field.
Pragma exclude
------ -------

View File

@ -111,3 +111,7 @@ class SourceGenerator:
def emit_encoder(self, node: _XdrAst) -> None:
"""Emit one encoder function for this XDR type"""
raise NotImplementedError("Encoder generation not supported")
def emit_maxsize(self, node: _XdrAst) -> None:
"""Emit one maxsize macro for this XDR type"""
raise NotImplementedError("Maxsize macro generation not supported")

View File

@ -4,7 +4,7 @@
"""Generate code to handle XDR enum types"""
from generators import SourceGenerator, create_jinja2_environment
from xdr_ast import _XdrEnum, public_apis
from xdr_ast import _XdrEnum, public_apis, big_endian, get_header_name
class XdrEnumGenerator(SourceGenerator):
@ -18,7 +18,7 @@ class XdrEnumGenerator(SourceGenerator):
def emit_declaration(self, node: _XdrEnum) -> None:
"""Emit one declaration pair for an XDR enum type"""
if node.name in public_apis:
template = self.environment.get_template("declaration/close.j2")
template = self.environment.get_template("declaration/enum.j2")
print(template.render(name=node.name))
def emit_definition(self, node: _XdrEnum) -> None:
@ -30,15 +30,35 @@ class XdrEnumGenerator(SourceGenerator):
for enumerator in node.enumerators:
print(template.render(name=enumerator.name, value=enumerator.value))
template = self.environment.get_template("definition/close.j2")
if node.name in big_endian:
template = self.environment.get_template("definition/close_be.j2")
else:
template = self.environment.get_template("definition/close.j2")
print(template.render(name=node.name))
def emit_decoder(self, node: _XdrEnum) -> None:
"""Emit one decoder function for an XDR enum type"""
template = self.environment.get_template("decoder/enum.j2")
if node.name in big_endian:
template = self.environment.get_template("decoder/enum_be.j2")
else:
template = self.environment.get_template("decoder/enum.j2")
print(template.render(name=node.name))
def emit_encoder(self, node: _XdrEnum) -> None:
"""Emit one encoder function for an XDR enum type"""
template = self.environment.get_template("encoder/enum.j2")
if node.name in big_endian:
template = self.environment.get_template("encoder/enum_be.j2")
else:
template = self.environment.get_template("encoder/enum.j2")
print(template.render(name=node.name))
def emit_maxsize(self, node: _XdrEnum) -> None:
"""Emit one maxsize macro for an XDR enum type"""
macro_name = get_header_name().upper() + "_" + node.name + "_sz"
template = self.environment.get_template("maxsize/enum.j2")
print(
template.render(
macro=macro_name,
width=" + ".join(node.symbolic_width()),
)
)

View File

@ -8,11 +8,11 @@ from jinja2 import Environment
from generators import SourceGenerator, kernel_c_type
from generators import create_jinja2_environment, get_jinja2_template
from xdr_ast import _XdrBasic, _XdrVariableLengthString
from xdr_ast import _XdrBasic, _XdrString
from xdr_ast import _XdrFixedLengthOpaque, _XdrVariableLengthOpaque
from xdr_ast import _XdrFixedLengthArray, _XdrVariableLengthArray
from xdr_ast import _XdrOptionalData, _XdrPointer, _XdrDeclaration
from xdr_ast import public_apis
from xdr_ast import public_apis, get_header_name
def emit_pointer_declaration(environment: Environment, node: _XdrPointer) -> None:
@ -46,7 +46,7 @@ def emit_pointer_member_definition(
elif isinstance(field, _XdrVariableLengthOpaque):
template = get_jinja2_template(environment, "definition", field.template)
print(template.render(name=field.name))
elif isinstance(field, _XdrVariableLengthString):
elif isinstance(field, _XdrString):
template = get_jinja2_template(environment, "definition", field.template)
print(template.render(name=field.name))
elif isinstance(field, _XdrFixedLengthArray):
@ -119,7 +119,7 @@ def emit_pointer_member_decoder(
maxsize=field.maxsize,
)
)
elif isinstance(field, _XdrVariableLengthString):
elif isinstance(field, _XdrString):
template = get_jinja2_template(environment, "decoder", field.template)
print(
template.render(
@ -198,7 +198,7 @@ def emit_pointer_member_encoder(
maxsize=field.maxsize,
)
)
elif isinstance(field, _XdrVariableLengthString):
elif isinstance(field, _XdrString):
template = get_jinja2_template(environment, "encoder", field.template)
print(
template.render(
@ -247,6 +247,18 @@ def emit_pointer_encoder(environment: Environment, node: _XdrPointer) -> None:
print(template.render())
def emit_pointer_maxsize(environment: Environment, node: _XdrPointer) -> None:
"""Emit one maxsize macro for an XDR pointer type"""
macro_name = get_header_name().upper() + "_" + node.name + "_sz"
template = get_jinja2_template(environment, "maxsize", "pointer")
print(
template.render(
macro=macro_name,
width=" + ".join(node.symbolic_width()),
)
)
class XdrPointerGenerator(SourceGenerator):
"""Generate source code for XDR pointer"""
@ -270,3 +282,7 @@ class XdrPointerGenerator(SourceGenerator):
def emit_encoder(self, node: _XdrPointer) -> None:
"""Emit one encoder function for an XDR pointer type"""
emit_pointer_encoder(self.environment, node)
def emit_maxsize(self, node: _XdrPointer) -> None:
"""Emit one maxsize macro for an XDR pointer type"""
emit_pointer_maxsize(self.environment, node)

View File

@ -8,11 +8,11 @@ from jinja2 import Environment
from generators import SourceGenerator, kernel_c_type
from generators import create_jinja2_environment, get_jinja2_template
from xdr_ast import _XdrBasic, _XdrVariableLengthString
from xdr_ast import _XdrBasic, _XdrString
from xdr_ast import _XdrFixedLengthOpaque, _XdrVariableLengthOpaque
from xdr_ast import _XdrFixedLengthArray, _XdrVariableLengthArray
from xdr_ast import _XdrOptionalData, _XdrStruct, _XdrDeclaration
from xdr_ast import public_apis
from xdr_ast import public_apis, get_header_name
def emit_struct_declaration(environment: Environment, node: _XdrStruct) -> None:
@ -46,7 +46,7 @@ def emit_struct_member_definition(
elif isinstance(field, _XdrVariableLengthOpaque):
template = get_jinja2_template(environment, "definition", field.template)
print(template.render(name=field.name))
elif isinstance(field, _XdrVariableLengthString):
elif isinstance(field, _XdrString):
template = get_jinja2_template(environment, "definition", field.template)
print(template.render(name=field.name))
elif isinstance(field, _XdrFixedLengthArray):
@ -119,7 +119,7 @@ def emit_struct_member_decoder(
maxsize=field.maxsize,
)
)
elif isinstance(field, _XdrVariableLengthString):
elif isinstance(field, _XdrString):
template = get_jinja2_template(environment, "decoder", field.template)
print(
template.render(
@ -198,7 +198,7 @@ def emit_struct_member_encoder(
maxsize=field.maxsize,
)
)
elif isinstance(field, _XdrVariableLengthString):
elif isinstance(field, _XdrString):
template = get_jinja2_template(environment, "encoder", field.template)
print(
template.render(
@ -247,6 +247,18 @@ def emit_struct_encoder(environment: Environment, node: _XdrStruct) -> None:
print(template.render())
def emit_struct_maxsize(environment: Environment, node: _XdrStruct) -> None:
"""Emit one maxsize macro for an XDR struct type"""
macro_name = get_header_name().upper() + "_" + node.name + "_sz"
template = get_jinja2_template(environment, "maxsize", "struct")
print(
template.render(
macro=macro_name,
width=" + ".join(node.symbolic_width()),
)
)
class XdrStructGenerator(SourceGenerator):
"""Generate source code for XDR structs"""
@ -270,3 +282,7 @@ class XdrStructGenerator(SourceGenerator):
def emit_encoder(self, node: _XdrStruct) -> None:
"""Emit one encoder function for an XDR struct type"""
emit_struct_encoder(self.environment, node)
def emit_maxsize(self, node: _XdrStruct) -> None:
"""Emit one maxsize macro for an XDR struct type"""
emit_struct_maxsize(self.environment, node)

View File

@ -8,11 +8,11 @@ from jinja2 import Environment
from generators import SourceGenerator, kernel_c_type
from generators import create_jinja2_environment, get_jinja2_template
from xdr_ast import _XdrBasic, _XdrTypedef, _XdrVariableLengthString
from xdr_ast import _XdrBasic, _XdrTypedef, _XdrString
from xdr_ast import _XdrFixedLengthOpaque, _XdrVariableLengthOpaque
from xdr_ast import _XdrFixedLengthArray, _XdrVariableLengthArray
from xdr_ast import _XdrOptionalData, _XdrVoid, _XdrDeclaration
from xdr_ast import public_apis
from xdr_ast import public_apis, get_header_name
def emit_typedef_declaration(environment: Environment, node: _XdrDeclaration) -> None:
@ -28,7 +28,7 @@ def emit_typedef_declaration(environment: Environment, node: _XdrDeclaration) ->
classifier=node.spec.c_classifier,
)
)
elif isinstance(node, _XdrVariableLengthString):
elif isinstance(node, _XdrString):
template = get_jinja2_template(environment, "declaration", node.template)
print(template.render(name=node.name))
elif isinstance(node, _XdrFixedLengthOpaque):
@ -74,7 +74,7 @@ def emit_type_definition(environment: Environment, node: _XdrDeclaration) -> Non
classifier=node.spec.c_classifier,
)
)
elif isinstance(node, _XdrVariableLengthString):
elif isinstance(node, _XdrString):
template = get_jinja2_template(environment, "definition", node.template)
print(template.render(name=node.name))
elif isinstance(node, _XdrFixedLengthOpaque):
@ -119,7 +119,7 @@ def emit_typedef_decoder(environment: Environment, node: _XdrDeclaration) -> Non
type=node.spec.type_name,
)
)
elif isinstance(node, _XdrVariableLengthString):
elif isinstance(node, _XdrString):
template = get_jinja2_template(environment, "decoder", node.template)
print(
template.render(
@ -180,7 +180,7 @@ def emit_typedef_encoder(environment: Environment, node: _XdrDeclaration) -> Non
type=node.spec.type_name,
)
)
elif isinstance(node, _XdrVariableLengthString):
elif isinstance(node, _XdrString):
template = get_jinja2_template(environment, "encoder", node.template)
print(
template.render(
@ -230,6 +230,18 @@ def emit_typedef_encoder(environment: Environment, node: _XdrDeclaration) -> Non
raise NotImplementedError("typedef: type not recognized")
def emit_typedef_maxsize(environment: Environment, node: _XdrDeclaration) -> None:
"""Emit a maxsize macro for an XDR typedef"""
macro_name = get_header_name().upper() + "_" + node.name + "_sz"
template = get_jinja2_template(environment, "maxsize", node.template)
print(
template.render(
macro=macro_name,
width=" + ".join(node.symbolic_width()),
)
)
class XdrTypedefGenerator(SourceGenerator):
"""Generate source code for XDR typedefs"""
@ -253,3 +265,7 @@ class XdrTypedefGenerator(SourceGenerator):
def emit_encoder(self, node: _XdrTypedef) -> None:
"""Emit one encoder function for an XDR typedef"""
emit_typedef_encoder(self.environment, node.declaration)
def emit_maxsize(self, node: _XdrTypedef) -> None:
"""Emit one maxsize macro for an XDR typedef"""
emit_typedef_maxsize(self.environment, node.declaration)

View File

@ -8,8 +8,8 @@ from jinja2 import Environment
from generators import SourceGenerator
from generators import create_jinja2_environment, get_jinja2_template
from xdr_ast import _XdrBasic, _XdrUnion, _XdrVoid
from xdr_ast import _XdrDeclaration, _XdrCaseSpec, public_apis
from xdr_ast import _XdrBasic, _XdrUnion, _XdrVoid, get_header_name
from xdr_ast import _XdrDeclaration, _XdrCaseSpec, public_apis, big_endian
def emit_union_declaration(environment: Environment, node: _XdrUnion) -> None:
@ -77,13 +77,18 @@ def emit_union_switch_spec_decoder(
print(template.render(name=node.name, type=node.spec.type_name))
def emit_union_case_spec_decoder(environment: Environment, node: _XdrCaseSpec) -> None:
def emit_union_case_spec_decoder(
environment: Environment, node: _XdrCaseSpec, big_endian_discriminant: bool
) -> None:
"""Emit decoder functions for an XDR union's case arm"""
if isinstance(node.arm, _XdrVoid):
return
template = get_jinja2_template(environment, "decoder", "case_spec")
if big_endian_discriminant:
template = get_jinja2_template(environment, "decoder", "case_spec_be")
else:
template = get_jinja2_template(environment, "decoder", "case_spec")
for case in node.values:
print(template.render(case=case))
@ -136,7 +141,11 @@ def emit_union_decoder(environment: Environment, node: _XdrUnion) -> None:
emit_union_switch_spec_decoder(environment, node.discriminant)
for case in node.cases:
emit_union_case_spec_decoder(environment, case)
emit_union_case_spec_decoder(
environment,
case,
node.discriminant.spec.type_name in big_endian,
)
emit_union_default_spec_decoder(environment, node)
@ -153,17 +162,21 @@ def emit_union_switch_spec_encoder(
print(template.render(name=node.name, type=node.spec.type_name))
def emit_union_case_spec_encoder(environment: Environment, node: _XdrCaseSpec) -> None:
def emit_union_case_spec_encoder(
environment: Environment, node: _XdrCaseSpec, big_endian_discriminant: bool
) -> None:
"""Emit encoder functions for an XDR union's case arm"""
if isinstance(node.arm, _XdrVoid):
return
template = get_jinja2_template(environment, "encoder", "case_spec")
if big_endian_discriminant:
template = get_jinja2_template(environment, "encoder", "case_spec_be")
else:
template = get_jinja2_template(environment, "encoder", "case_spec")
for case in node.values:
print(template.render(case=case))
assert isinstance(node.arm, _XdrBasic)
template = get_jinja2_template(environment, "encoder", node.arm.template)
print(
template.render(
@ -192,7 +205,6 @@ def emit_union_default_spec_encoder(environment: Environment, node: _XdrUnion) -
print(template.render())
return
assert isinstance(default_case.arm, _XdrBasic)
template = get_jinja2_template(environment, "encoder", default_case.arm.template)
print(
template.render(
@ -210,7 +222,11 @@ def emit_union_encoder(environment, node: _XdrUnion) -> None:
emit_union_switch_spec_encoder(environment, node.discriminant)
for case in node.cases:
emit_union_case_spec_encoder(environment, case)
emit_union_case_spec_encoder(
environment,
case,
node.discriminant.spec.type_name in big_endian,
)
emit_union_default_spec_encoder(environment, node)
@ -218,6 +234,18 @@ def emit_union_encoder(environment, node: _XdrUnion) -> None:
print(template.render())
def emit_union_maxsize(environment: Environment, node: _XdrUnion) -> None:
"""Emit one maxsize macro for an XDR union type"""
macro_name = get_header_name().upper() + "_" + node.name + "_sz"
template = get_jinja2_template(environment, "maxsize", "union")
print(
template.render(
macro=macro_name,
width=" + ".join(node.symbolic_width()),
)
)
class XdrUnionGenerator(SourceGenerator):
"""Generate source code for XDR unions"""
@ -241,3 +269,7 @@ class XdrUnionGenerator(SourceGenerator):
def emit_encoder(self, node: _XdrUnion) -> None:
"""Emit one encoder function for an XDR union"""
emit_union_encoder(self.environment, node)
def emit_maxsize(self, node: _XdrUnion) -> None:
"""Emit one maxsize macro for an XDR union"""
emit_union_maxsize(self.environment, node)

View File

@ -3,7 +3,7 @@
declaration : "opaque" identifier "[" value "]" -> fixed_length_opaque
| "opaque" identifier "<" [ value ] ">" -> variable_length_opaque
| "string" identifier "<" [ value ] ">" -> variable_length_string
| "string" identifier "<" [ value ] ">" -> string
| type_specifier identifier "[" value "]" -> fixed_length_array
| type_specifier identifier "<" [ value ] ">" -> variable_length_array
| type_specifier "*" identifier -> optional_data
@ -87,12 +87,14 @@ procedure_def : type_specifier identifier "(" type_specifier ")" "=" c
pragma_def : "pragma" directive identifier [ identifier ] ";"
directive : exclude_directive
directive : big_endian_directive
| exclude_directive
| header_directive
| pages_directive
| public_directive
| skip_directive
big_endian_directive : "big_endian"
exclude_directive : "exclude"
header_directive : "header"
pages_directive : "pages"

View File

@ -28,9 +28,7 @@ from xdr_parse import xdr_parser, set_xdr_annotate
logger.setLevel(logging.INFO)
def emit_header_definitions(
root: Specification, language: str, peer: str
) -> None:
def emit_header_definitions(root: Specification, language: str, peer: str) -> None:
"""Emit header definitions"""
for definition in root.definitions:
if isinstance(definition.value, _XdrConstant):
@ -52,6 +50,25 @@ def emit_header_definitions(
gen.emit_definition(definition.value)
def emit_header_maxsize(root: Specification, language: str, peer: str) -> None:
"""Emit header maxsize macros"""
print("")
for definition in root.definitions:
if isinstance(definition.value, _XdrEnum):
gen = XdrEnumGenerator(language, peer)
elif isinstance(definition.value, _XdrPointer):
gen = XdrPointerGenerator(language, peer)
elif isinstance(definition.value, _XdrTypedef):
gen = XdrTypedefGenerator(language, peer)
elif isinstance(definition.value, _XdrStruct):
gen = XdrStructGenerator(language, peer)
elif isinstance(definition.value, _XdrUnion):
gen = XdrUnionGenerator(language, peer)
else:
continue
gen.emit_maxsize(definition.value)
def handle_parse_error(e: UnexpectedInput) -> bool:
"""Simple parse error reporting, no recovery attempted"""
print(e)
@ -71,6 +88,7 @@ def subcmd(args: Namespace) -> int:
gen.emit_definition(args.filename, ast)
emit_header_definitions(ast, args.language, args.peer)
emit_header_maxsize(ast, args.language, args.peer)
gen = XdrHeaderBottomGenerator(args.language, args.peer)
gen.emit_definition(args.filename, ast)

View File

@ -83,8 +83,7 @@ def generate_client_source(filename: str, root: Specification, language: str) ->
gen = XdrSourceTopGenerator(language, "client")
gen.emit_source(filename, root)
# cel: todo: client needs XDR size macros
print("")
for definition in root.definitions:
emit_source_encoder(definition.value, language, "client")
for definition in root.definitions:

View File

@ -1,4 +0,0 @@
{# SPDX-License-Identifier: GPL-2.0 #}
bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} *ptr);
bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} value);

View File

@ -0,0 +1,4 @@
{# SPDX-License-Identifier: GPL-2.0 #}
bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ name }} *ptr);
bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, {{ name }} value);

View File

@ -8,7 +8,7 @@ bool
{% else %}
static bool __maybe_unused
{% endif %}
xdrgen_decode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} *ptr)
xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ name }} *ptr)
{
u32 val;

View File

@ -0,0 +1,14 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* enum {{ name }} (big-endian) */
{% endif %}
{% if name in public_apis %}
bool
{% else %}
static bool __maybe_unused
{% endif %}
xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ name }} *ptr)
{
return xdr_stream_decode_be32(xdr, ptr) == 0;
}

View File

@ -1,2 +1,3 @@
{# SPDX-License-Identifier: GPL-2.0 #}
};
typedef enum {{ name }} {{ name }};

View File

@ -0,0 +1,3 @@
{# SPDX-License-Identifier: GPL-2.0 #}
};
typedef __be32 {{ name }};

View File

@ -8,7 +8,7 @@ bool
{% else %}
static bool __maybe_unused
{% endif %}
xdrgen_encode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} value)
xdrgen_encode_{{ name }}(struct xdr_stream *xdr, {{ name }} value)
{
return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
}

View File

@ -0,0 +1,14 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* enum {{ name }} (big-endian) */
{% endif %}
{% if name in public_apis %}
bool
{% else %}
static bool __maybe_unused
{% endif %}
xdrgen_encode_{{ name }}(struct xdr_stream *xdr, {{ name }} value)
{
return xdr_stream_encode_be32(xdr, value) == XDR_UNIT;
}

View File

@ -0,0 +1,2 @@
{# SPDX-License-Identifier: GPL-2.0 #}
#define {{ '{:<31}'.format(macro) }} ({{ width }})

View File

@ -0,0 +1,3 @@
{# SPDX-License-Identifier: GPL-2.0 #}
#define {{ '{:<31}'.format(macro) }} \
({{ width }})

View File

@ -13,10 +13,6 @@ static int {{ program }}_xdr_dec_{{ result }}(struct rpc_rqst *req,
if (!xdrgen_decode_{{ result }}(xdr, result))
return -EIO;
if (result->stat != nfs_ok) {
trace_nfs_xdr_status(xdr, (int)result->stat);
return {{ program }}_stat_to_errno(result->stat);
}
{% endif %}
return 0;
}

View File

@ -3,6 +3,11 @@
// XDR specification file: {{ filename }}
// XDR specification modification time: {{ mtime }}
#include <linux/sunrpc/xprt.h>
#include <linux/types.h>
#include "{{ program }}xdr_gen.h"
#include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/xdrgen/_defs.h>
#include <linux/sunrpc/xdrgen/_builtins.h>
#include <linux/sunrpc/xdrgen/nlm4.h>
#include <linux/sunrpc/clnt.h>

View File

@ -0,0 +1,3 @@
{# SPDX-License-Identifier: GPL-2.0 #}
#define {{ '{:<31}'.format(macro) }} \
({{ width }})

View File

@ -0,0 +1,3 @@
{# SPDX-License-Identifier: GPL-2.0 #}
#define {{ '{:<31}'.format(macro) }} \
({{ width }})

View File

@ -0,0 +1,2 @@
{# SPDX-License-Identifier: GPL-2.0 #}
#define {{ '{:<31}'.format(macro) }} ({{ width }})

View File

@ -0,0 +1,2 @@
{# SPDX-License-Identifier: GPL-2.0 #}
#define {{ '{:<31}'.format(macro) }} ({{ width }})

View File

@ -0,0 +1,2 @@
{# SPDX-License-Identifier: GPL-2.0 #}
#define {{ '{:<31}'.format(macro) }} ({{ width }})

View File

@ -0,0 +1,2 @@
{# SPDX-License-Identifier: GPL-2.0 #}
#define {{ '{:<31}'.format(macro) }} ({{ width }})

View File

@ -0,0 +1,2 @@
{# SPDX-License-Identifier: GPL-2.0 #}
case __constant_cpu_to_be32({{ case }}):

View File

@ -0,0 +1,2 @@
{# SPDX-License-Identifier: GPL-2.0 #}
case __constant_cpu_to_be32({{ case }}):

View File

@ -0,0 +1,3 @@
{# SPDX-License-Identifier: GPL-2.0 #}
#define {{ '{:<31}'.format(macro) }} \
({{ width }})

View File

@ -12,13 +12,50 @@ from lark.tree import Meta
this_module = sys.modules[__name__]
big_endian = []
excluded_apis = []
header_name = "none"
public_apis = []
enums = set()
structs = set()
pass_by_reference = set()
constants = {}
def xdr_quadlen(val: str) -> int:
"""Return integer XDR width of an XDR type"""
if val in constants:
octets = constants[val]
else:
octets = int(val)
return int((octets + 3) / 4)
symbolic_widths = {
"void": ["XDR_void"],
"bool": ["XDR_bool"],
"int": ["XDR_int"],
"unsigned_int": ["XDR_unsigned_int"],
"long": ["XDR_long"],
"unsigned_long": ["XDR_unsigned_long"],
"hyper": ["XDR_hyper"],
"unsigned_hyper": ["XDR_unsigned_hyper"],
}
# Numeric XDR widths are tracked in a dictionary that is keyed
# by type_name because sometimes a caller has nothing more than
# the type_name to use to figure out the numeric width.
max_widths = {
"void": 0,
"bool": 1,
"int": 1,
"unsigned_int": 1,
"long": 1,
"unsigned_long": 1,
"hyper": 2,
"unsigned_hyper": 2,
}
@dataclass
class _XdrAst(ast_utils.Ast):
@ -51,18 +88,31 @@ class _XdrTypeSpecifier(_XdrAst):
"""Corresponds to 'type_specifier' in the XDR language grammar"""
type_name: str
c_classifier: str
c_classifier: str = ""
@dataclass
class _XdrDefinedType(_XdrTypeSpecifier):
"""Corresponds to a type defined by the input specification"""
def symbolic_width(self) -> List:
"""Return list containing XDR width of type's components"""
return [get_header_name().upper() + "_" + self.type_name + "_sz"]
def __post_init__(self):
if self.type_name in structs:
self.c_classifier = "struct "
symbolic_widths[self.type_name] = self.symbolic_width()
@dataclass
class _XdrBuiltInType(_XdrTypeSpecifier):
"""Corresponds to a built-in XDR type"""
def symbolic_width(self) -> List:
"""Return list containing XDR width of type's components"""
return symbolic_widths[self.type_name]
@dataclass
class _XdrDeclaration(_XdrAst):
@ -77,6 +127,18 @@ class _XdrFixedLengthOpaque(_XdrDeclaration):
size: str
template: str = "fixed_length_opaque"
def max_width(self) -> int:
"""Return width of type in XDR_UNITS"""
return xdr_quadlen(self.size)
def symbolic_width(self) -> List:
"""Return list containing XDR width of type's components"""
return ["XDR_QUADLEN(" + self.size + ")"]
def __post_init__(self):
max_widths[self.name] = self.max_width()
symbolic_widths[self.name] = self.symbolic_width()
@dataclass
class _XdrVariableLengthOpaque(_XdrDeclaration):
@ -86,14 +148,44 @@ class _XdrVariableLengthOpaque(_XdrDeclaration):
maxsize: str
template: str = "variable_length_opaque"
def max_width(self) -> int:
"""Return width of type in XDR_UNITS"""
return 1 + xdr_quadlen(self.maxsize)
def symbolic_width(self) -> List:
"""Return list containing XDR width of type's components"""
widths = ["XDR_unsigned_int"]
if self.maxsize != "0":
widths.append("XDR_QUADLEN(" + self.maxsize + ")")
return widths
def __post_init__(self):
max_widths[self.name] = self.max_width()
symbolic_widths[self.name] = self.symbolic_width()
@dataclass
class _XdrVariableLengthString(_XdrDeclaration):
class _XdrString(_XdrDeclaration):
"""A (NUL-terminated) variable-length string declaration"""
name: str
maxsize: str
template: str = "variable_length_string"
template: str = "string"
def max_width(self) -> int:
"""Return width of type in XDR_UNITS"""
return 1 + xdr_quadlen(self.maxsize)
def symbolic_width(self) -> List:
"""Return list containing XDR width of type's components"""
widths = ["XDR_unsigned_int"]
if self.maxsize != "0":
widths.append("XDR_QUADLEN(" + self.maxsize + ")")
return widths
def __post_init__(self):
max_widths[self.name] = self.max_width()
symbolic_widths[self.name] = self.symbolic_width()
@dataclass
@ -105,6 +197,19 @@ class _XdrFixedLengthArray(_XdrDeclaration):
size: str
template: str = "fixed_length_array"
def max_width(self) -> int:
"""Return width of type in XDR_UNITS"""
return xdr_quadlen(self.size) * max_widths[self.spec.type_name]
def symbolic_width(self) -> List:
"""Return list containing XDR width of type's components"""
item_width = " + ".join(symbolic_widths[self.spec.type_name])
return ["(" + self.size + " * (" + item_width + "))"]
def __post_init__(self):
max_widths[self.name] = self.max_width()
symbolic_widths[self.name] = self.symbolic_width()
@dataclass
class _XdrVariableLengthArray(_XdrDeclaration):
@ -115,6 +220,22 @@ class _XdrVariableLengthArray(_XdrDeclaration):
maxsize: str
template: str = "variable_length_array"
def max_width(self) -> int:
"""Return width of type in XDR_UNITS"""
return 1 + (xdr_quadlen(self.maxsize) * max_widths[self.spec.type_name])
def symbolic_width(self) -> List:
"""Return list containing XDR width of type's components"""
widths = ["XDR_unsigned_int"]
if self.maxsize != "0":
item_width = " + ".join(symbolic_widths[self.spec.type_name])
widths.append("(" + self.maxsize + " * (" + item_width + "))")
return widths
def __post_init__(self):
max_widths[self.name] = self.max_width()
symbolic_widths[self.name] = self.symbolic_width()
@dataclass
class _XdrOptionalData(_XdrDeclaration):
@ -124,6 +245,20 @@ class _XdrOptionalData(_XdrDeclaration):
spec: _XdrTypeSpecifier
template: str = "optional_data"
def max_width(self) -> int:
"""Return width of type in XDR_UNITS"""
return 1
def symbolic_width(self) -> List:
"""Return list containing XDR width of type's components"""
return ["XDR_bool"]
def __post_init__(self):
structs.add(self.name)
pass_by_reference.add(self.name)
max_widths[self.name] = self.max_width()
symbolic_widths[self.name] = self.symbolic_width()
@dataclass
class _XdrBasic(_XdrDeclaration):
@ -133,13 +268,34 @@ class _XdrBasic(_XdrDeclaration):
spec: _XdrTypeSpecifier
template: str = "basic"
def max_width(self) -> int:
"""Return width of type in XDR_UNITS"""
return max_widths[self.spec.type_name]
def symbolic_width(self) -> List:
"""Return list containing XDR width of type's components"""
return symbolic_widths[self.spec.type_name]
def __post_init__(self):
max_widths[self.name] = self.max_width()
symbolic_widths[self.name] = self.symbolic_width()
@dataclass
class _XdrVoid(_XdrDeclaration):
"""A void declaration"""
name: str = "void"
template: str = "void"
def max_width(self) -> int:
"""Return width of type in XDR_UNITS"""
return 0
def symbolic_width(self) -> List:
"""Return list containing XDR width of type's components"""
return []
@dataclass
class _XdrConstant(_XdrAst):
@ -148,6 +304,10 @@ class _XdrConstant(_XdrAst):
name: str
value: str
def __post_init__(self):
if self.value not in constants:
constants[self.name] = int(self.value, 0)
@dataclass
class _XdrEnumerator(_XdrAst):
@ -156,6 +316,10 @@ class _XdrEnumerator(_XdrAst):
name: str
value: str
def __post_init__(self):
if self.value not in constants:
constants[self.name] = int(self.value, 0)
@dataclass
class _XdrEnum(_XdrAst):
@ -166,6 +330,18 @@ class _XdrEnum(_XdrAst):
maximum: int
enumerators: List[_XdrEnumerator]
def max_width(self) -> int:
"""Return width of type in XDR_UNITS"""
return 1
def symbolic_width(self) -> List:
"""Return list containing XDR width of type's components"""
return ["XDR_int"]
def __post_init__(self):
max_widths[self.name] = self.max_width()
symbolic_widths[self.name] = self.symbolic_width()
@dataclass
class _XdrStruct(_XdrAst):
@ -174,6 +350,26 @@ class _XdrStruct(_XdrAst):
name: str
fields: List[_XdrDeclaration]
def max_width(self) -> int:
"""Return width of type in XDR_UNITS"""
width = 0
for field in self.fields:
width += field.max_width()
return width
def symbolic_width(self) -> List:
"""Return list containing XDR width of type's components"""
widths = []
for field in self.fields:
widths += field.symbolic_width()
return widths
def __post_init__(self):
structs.add(self.name)
pass_by_reference.add(self.name)
max_widths[self.name] = self.max_width()
symbolic_widths[self.name] = self.symbolic_width()
@dataclass
class _XdrPointer(_XdrAst):
@ -182,6 +378,27 @@ class _XdrPointer(_XdrAst):
name: str
fields: List[_XdrDeclaration]
def max_width(self) -> int:
"""Return width of type in XDR_UNITS"""
width = 1
for field in self.fields[0:-1]:
width += field.max_width()
return width
def symbolic_width(self) -> List:
"""Return list containing XDR width of type's components"""
widths = []
widths += ["XDR_bool"]
for field in self.fields[0:-1]:
widths += field.symbolic_width()
return widths
def __post_init__(self):
structs.add(self.name)
pass_by_reference.add(self.name)
max_widths[self.name] = self.max_width()
symbolic_widths[self.name] = self.symbolic_width()
@dataclass
class _XdrTypedef(_XdrAst):
@ -189,6 +406,23 @@ class _XdrTypedef(_XdrAst):
declaration: _XdrDeclaration
def max_width(self) -> int:
"""Return width of type in XDR_UNITS"""
return self.declaration.max_width()
def symbolic_width(self) -> List:
"""Return list containing XDR width of type's components"""
return self.declaration.symbolic_width()
def __post_init__(self):
if isinstance(self.declaration, _XdrBasic):
new_type = self.declaration
if isinstance(new_type.spec, _XdrDefinedType):
if new_type.spec.type_name in pass_by_reference:
pass_by_reference.add(new_type.name)
max_widths[new_type.name] = self.max_width()
symbolic_widths[new_type.name] = self.symbolic_width()
@dataclass
class _XdrCaseSpec(_XdrAst):
@ -216,6 +450,36 @@ class _XdrUnion(_XdrAst):
cases: List[_XdrCaseSpec]
default: _XdrDeclaration
def max_width(self) -> int:
"""Return width of type in XDR_UNITS"""
max_width = 0
for case in self.cases:
if case.arm.max_width() > max_width:
max_width = case.arm.max_width()
if self.default:
if self.default.arm.max_width() > max_width:
max_width = self.default.arm.max_width()
return 1 + max_width
def symbolic_width(self) -> List:
"""Return list containing XDR width of type's components"""
max_width = 0
for case in self.cases:
if case.arm.max_width() > max_width:
max_width = case.arm.max_width()
width = case.arm.symbolic_width()
if self.default:
if self.default.arm.max_width() > max_width:
max_width = self.default.arm.max_width()
width = self.default.arm.symbolic_width()
return symbolic_widths[self.discriminant.name] + width
def __post_init__(self):
structs.add(self.name)
pass_by_reference.add(self.name)
max_widths[self.name] = self.max_width()
symbolic_widths[self.name] = self.symbolic_width()
@dataclass
class _RpcProcedure(_XdrAst):
@ -290,24 +554,13 @@ class ParseToAst(Transformer):
return _XdrConstantValue(value)
def type_specifier(self, children):
"""Instantiate one type_specifier object"""
c_classifier = ""
"""Instantiate one _XdrTypeSpecifier object"""
if isinstance(children[0], _XdrIdentifier):
name = children[0].symbol
if name in enums:
c_classifier = "enum "
if name in structs:
c_classifier = "struct "
return _XdrDefinedType(
type_name=name,
c_classifier=c_classifier,
)
return _XdrDefinedType(type_name=name)
token = children[0].data
return _XdrBuiltInType(
type_name=token.value,
c_classifier=c_classifier,
)
name = children[0].data.value
return _XdrBuiltInType(type_name=name)
def constant_def(self, children):
"""Instantiate one _XdrConstant object"""
@ -320,7 +573,6 @@ class ParseToAst(Transformer):
def enum(self, children):
"""Instantiate one _XdrEnum object"""
enum_name = children[0].symbol
enums.add(enum_name)
i = 0
enumerators = []
@ -350,15 +602,15 @@ class ParseToAst(Transformer):
return _XdrVariableLengthOpaque(name, maxsize)
def variable_length_string(self, children):
"""Instantiate one _XdrVariableLengthString declaration object"""
def string(self, children):
"""Instantiate one _XdrString declaration object"""
name = children[0].symbol
if children[1] is not None:
maxsize = children[1].value
else:
maxsize = "0"
return _XdrVariableLengthString(name, maxsize)
return _XdrString(name, maxsize)
def fixed_length_array(self, children):
"""Instantiate one _XdrFixedLengthArray declaration object"""
@ -383,8 +635,6 @@ class ParseToAst(Transformer):
"""Instantiate one _XdrOptionalData declaration object"""
spec = children[0]
name = children[1].symbol
structs.add(name)
pass_by_reference.add(name)
return _XdrOptionalData(name, spec)
@ -403,8 +653,6 @@ class ParseToAst(Transformer):
def struct(self, children):
"""Instantiate one _XdrStruct object"""
name = children[0].symbol
structs.add(name)
pass_by_reference.add(name)
fields = children[1].children
last_field = fields[-1]
@ -419,11 +667,6 @@ class ParseToAst(Transformer):
def typedef(self, children):
"""Instantiate one _XdrTypedef object"""
new_type = children[0]
if isinstance(new_type, _XdrBasic) and isinstance(
new_type.spec, _XdrDefinedType
):
if new_type.spec.type_name in pass_by_reference:
pass_by_reference.add(new_type.name)
return _XdrTypedef(new_type)
@ -445,8 +688,6 @@ class ParseToAst(Transformer):
def union(self, children):
"""Instantiate one _XdrUnion object"""
name = children[0].symbol
structs.add(name)
pass_by_reference.add(name)
body = children[1]
discriminant = body.children[0].children[0]
@ -484,6 +725,8 @@ class ParseToAst(Transformer):
"""Instantiate one _Pragma object"""
directive = children[0].children[0].data
match directive:
case "big_endian_directive":
big_endian.append(children[1].symbol)
case "exclude_directive":
excluded_apis.append(children[1].symbol)
case "header_directive":

View File

@ -128,5 +128,7 @@ There is NO WARRANTY, to the extent permitted by law.""",
try:
if __name__ == "__main__":
sys.exit(main())
except (SystemExit, KeyboardInterrupt, BrokenPipeError):
except SystemExit:
sys.exit(0)
except (KeyboardInterrupt, BrokenPipeError):
sys.exit(1)