mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2024-12-28 00:33:16 +00:00
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:
commit
445d9f05fa
@ -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>
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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, ©->cp_flags) &&
|
||||
!test_bit(NFSD4_COPY_F_STOPPED, ©->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, ©->cp_flags)) {
|
||||
if (--copy->cp_ttl) {
|
||||
list_del_init(©->copies);
|
||||
list_add(©->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(©->copies);
|
||||
cleanup_async_copy(copy);
|
||||
}
|
||||
}
|
||||
|
||||
static void nfs4_put_copy(struct nfsd4_copy *copy)
|
||||
{
|
||||
if (!refcount_dec_and_test(©->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, ©->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(©->refcount);
|
||||
copy->cp_clp = NULL;
|
||||
if (!list_empty(©->copies))
|
||||
list_del_init(©->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, ©->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 = ©->cp_cb_offload;
|
||||
|
||||
memcpy(&cbo->co_res, ©->cp_res, sizeof(copy->cp_res));
|
||||
memcpy(&cbo->co_fh, ©->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, ©->cp_flags);
|
||||
|
||||
set_bit(NFSD4_COPY_F_COMPLETED, ©->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",
|
||||
|
@ -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);
|
||||
|
@ -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, <);
|
||||
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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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 *,
|
||||
|
@ -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 *,
|
||||
|
@ -73,8 +73,6 @@ struct nlm_args {
|
||||
u32 fsm_mode;
|
||||
};
|
||||
|
||||
typedef struct nlm_args nlm_args;
|
||||
|
||||
/*
|
||||
* Generic lockd result
|
||||
*/
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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_ */
|
||||
|
@ -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, "# ");
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
11
tools/net/sunrpc/extract.sh
Executable 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?^ *///$??'
|
@ -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
|
||||
------ -------
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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()),
|
||||
)
|
||||
)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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);
|
@ -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);
|
@ -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;
|
||||
|
||||
|
14
tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum_be.j2
Normal file
14
tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum_be.j2
Normal 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;
|
||||
}
|
@ -1,2 +1,3 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
};
|
||||
typedef enum {{ name }} {{ name }};
|
||||
|
@ -0,0 +1,3 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
};
|
||||
typedef __be32 {{ name }};
|
@ -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;
|
||||
}
|
||||
|
14
tools/net/sunrpc/xdrgen/templates/C/enum/encoder/enum_be.j2
Normal file
14
tools/net/sunrpc/xdrgen/templates/C/enum/encoder/enum_be.j2
Normal 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;
|
||||
}
|
2
tools/net/sunrpc/xdrgen/templates/C/enum/maxsize/enum.j2
Normal file
2
tools/net/sunrpc/xdrgen/templates/C/enum/maxsize/enum.j2
Normal file
@ -0,0 +1,2 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
#define {{ '{:<31}'.format(macro) }} ({{ width }})
|
@ -0,0 +1,3 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
#define {{ '{:<31}'.format(macro) }} \
|
||||
({{ width }})
|
@ -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;
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -0,0 +1,3 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
#define {{ '{:<31}'.format(macro) }} \
|
||||
({{ width }})
|
@ -0,0 +1,3 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
#define {{ '{:<31}'.format(macro) }} \
|
||||
({{ width }})
|
@ -0,0 +1,2 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
#define {{ '{:<31}'.format(macro) }} ({{ width }})
|
@ -0,0 +1,2 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
#define {{ '{:<31}'.format(macro) }} ({{ width }})
|
@ -0,0 +1,2 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
#define {{ '{:<31}'.format(macro) }} ({{ width }})
|
@ -0,0 +1,2 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
#define {{ '{:<31}'.format(macro) }} ({{ width }})
|
@ -0,0 +1,2 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
case __constant_cpu_to_be32({{ case }}):
|
@ -0,0 +1,2 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
case __constant_cpu_to_be32({{ case }}):
|
@ -0,0 +1,3 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
#define {{ '{:<31}'.format(macro) }} \
|
||||
({{ width }})
|
@ -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":
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user