mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-27 06:04:23 +08:00
NFSD 6.12 Release Notes
Notable features of this release include: - Pre-requisites for automatically determining the RPC server thread count - Clean-up and preparation for supporting LOCALIO, which will be merged via the NFS client tree - Enhancements and fixes to NFSv4.2 COPY offload - A new Python-based tool for generating kernel SunRPC XDR encoding and decoding functions, added as an aid for prototyping features in protocols based on the Linux kernel's SunRPC implementation. As always I am grateful to the NFSD contributors, reviewers, testers, and bug reporters who participated during this cycle. -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEKLLlsBKG3yQ88j7+M2qzM29mf5cFAmbxg60ACgkQM2qzM29m f5d+9A/+LiXAjR3x1vlbGFiMAW3Alixg5wE6AM7M1I/OH/dBCkWU1gzneWYaUXAk cIGp5sH2Uco2mFVswOZyQ3tX8T/2PeY+Kx5qrlK5h0bTUoz95AIyLe3LA/4o4CIL qMGlLQyVq9UolggPoRdigsDhKVwLcu3hWaG7ykkTquyrOPLBKgzRNSwVKLpFc/0/ mQToOf6HLjgFkEUR3pmXAMsVq88/BpjHIXeNhx2Z1ekWslSKjrAu2gC0rc6/s9Wi JsTtzSdnqefc2jsNVZ8FT+V7mDF1sxrN4SnHruSLhJsd5tL/3HDkiZEvdG2Sh0nH zQlDpMpNbZyCvaWs6jgaZeMRiNSSl7q31zXUgX2bkWpL/EnagujZHtLZroUgLQfA BO8HhRqdt1wJohiv2aMlFvnlp+GhSH5FdcXv1cT/CmyTNGqbXENqoCUA1OT9kE55 RvXVCLD4YbmCb5EpjLavhu/NuFOc9l9GitKlhiJlcX86QAu/C1Bu1DOyqgq5G0VW Xl/q7xIvNZz0mh7x8kKVV4bQHsm9pnoNz57CZFPahoHg/+BR4u6p8LepowpaHjHj Ef62BzYwQtuw0jCyufDea+uCt5CGwUM3Y5iBiQogtnvFK6ie8WwD0QTI2SYpcWZ/ T6RwDOX5jlMWWmuibSK2STgwkblG3vAmMot0RtEbZILvB/ld9qw= =Ybsc -----END PGP SIGNATURE----- Merge tag 'nfsd-6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux Pull nfsd updates from Chuck Lever: "Notable features of this release include: - Pre-requisites for automatically determining the RPC server thread count - Clean-up and preparation for supporting LOCALIO, which will be merged via the NFS client tree - Enhancements and fixes to NFSv4.2 COPY offload - A new Python-based tool for generating kernel SunRPC XDR encoding and decoding functions, added as an aid for prototyping features in protocols based on the Linux kernel's SunRPC implementation As always I am grateful to the NFSD contributors, reviewers, testers, and bug reporters who participated during this cycle" * tag 'nfsd-6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux: (57 commits) xdrgen: Prevent reordering of encoder and decoder functions xdrgen: typedefs should use the built-in string and opaque functions xdrgen: Fix return code checking in built-in XDR decoders tools: Add xdrgen nfsd: fix delegation_blocked() to block correctly for at least 30 seconds nfsd: fix initial getattr on write delegation nfsd: untangle code in nfsd4_deleg_getattr_conflict() nfsd: enforce upper limit for namelen in __cld_pipe_inprogress_downcall() nfsd: return -EINVAL when namelen is 0 NFSD: Wrap async copy operations with trace points NFSD: Clean up extra whitespace in trace_nfsd_copy_done NFSD: Record the callback stateid in copy tracepoints NFSD: Display copy stateids with conventional print formatting NFSD: Limit the number of concurrent async COPY operations NFSD: Async COPY result needs to return a write verifier nfsd: avoid races with wake_up_var() nfsd: use clear_and_wake_up_bit() sunrpc: xprtrdma: Use ERR_CAST() to return NFSD: Annotate struct pnfs_block_deviceaddr with __counted_by() nfsd: call cache_put if xdr_reserve_space returns NULL ...
This commit is contained in:
commit
18ba603446
3
.mailmap
3
.mailmap
@ -154,6 +154,9 @@ Christian Brauner <brauner@kernel.org> <christian.brauner@ubuntu.com>
|
||||
Christian Marangi <ansuelsmth@gmail.com>
|
||||
Christophe Ricard <christophe.ricard@gmail.com>
|
||||
Christoph Hellwig <hch@lst.de>
|
||||
Chuck Lever <chuck.lever@oracle.com> <cel@kernel.org>
|
||||
Chuck Lever <chuck.lever@oracle.com> <cel@netapp.com>
|
||||
Chuck Lever <chuck.lever@oracle.com> <cel@citi.umich.edu>
|
||||
Claudiu Beznea <claudiu.beznea@tuxon.dev> <claudiu.beznea@microchip.com>
|
||||
Colin Ian King <colin.i.king@gmail.com> <colin.king@canonical.com>
|
||||
Corey Minyard <minyard@acm.org>
|
||||
|
@ -440,7 +440,7 @@ nlm_bind_host(struct nlm_host *host)
|
||||
if ((clnt = host->h_rpcclnt) != NULL) {
|
||||
nlm_rebind_host(host);
|
||||
} else {
|
||||
unsigned long increment = nlmsvc_timeout;
|
||||
unsigned long increment = nlm_timeout * HZ;
|
||||
struct rpc_timeout timeparms = {
|
||||
.to_initval = increment,
|
||||
.to_increment = increment,
|
||||
|
@ -53,7 +53,6 @@ EXPORT_SYMBOL_GPL(nlmsvc_ops);
|
||||
static DEFINE_MUTEX(nlmsvc_mutex);
|
||||
static unsigned int nlmsvc_users;
|
||||
static struct svc_serv *nlmsvc_serv;
|
||||
unsigned long nlmsvc_timeout;
|
||||
|
||||
static void nlmsvc_request_retry(struct timer_list *tl)
|
||||
{
|
||||
@ -68,7 +67,7 @@ unsigned int lockd_net_id;
|
||||
* and also changed through the sysctl interface. -- Jamie Lokier, Aug 2003
|
||||
*/
|
||||
static unsigned long nlm_grace_period;
|
||||
static unsigned long nlm_timeout = LOCKD_DFLT_TIMEO;
|
||||
unsigned long nlm_timeout = LOCKD_DFLT_TIMEO;
|
||||
static int nlm_udpport, nlm_tcpport;
|
||||
|
||||
/* RLIM_NOFILE defaults to 1024. That seems like a reasonable default here. */
|
||||
@ -125,6 +124,8 @@ lockd(void *vrqstp)
|
||||
struct net *net = &init_net;
|
||||
struct lockd_net *ln = net_generic(net, lockd_net_id);
|
||||
|
||||
svc_thread_init_status(rqstp, 0);
|
||||
|
||||
/* try_to_freeze() is called from svc_recv() */
|
||||
set_freezable();
|
||||
|
||||
@ -333,10 +334,6 @@ static int lockd_get(void)
|
||||
printk(KERN_WARNING
|
||||
"lockd_up: no pid, %d users??\n", nlmsvc_users);
|
||||
|
||||
if (!nlm_timeout)
|
||||
nlm_timeout = LOCKD_DFLT_TIMEO;
|
||||
nlmsvc_timeout = nlm_timeout * HZ;
|
||||
|
||||
serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, lockd);
|
||||
if (!serv) {
|
||||
printk(KERN_WARNING "lockd_up: create service failed\n");
|
||||
|
@ -76,6 +76,8 @@ nfs4_callback_svc(void *vrqstp)
|
||||
{
|
||||
struct svc_rqst *rqstp = vrqstp;
|
||||
|
||||
svc_thread_init_status(rqstp, 0);
|
||||
|
||||
set_freezable();
|
||||
|
||||
while (!svc_thread_should_stop(rqstp))
|
||||
|
@ -5,26 +5,26 @@
|
||||
#include "nfsd.h"
|
||||
#include "auth.h"
|
||||
|
||||
int nfsexp_flags(struct svc_rqst *rqstp, struct svc_export *exp)
|
||||
int nfsexp_flags(struct svc_cred *cred, struct svc_export *exp)
|
||||
{
|
||||
struct exp_flavor_info *f;
|
||||
struct exp_flavor_info *end = exp->ex_flavors + exp->ex_nflavors;
|
||||
|
||||
for (f = exp->ex_flavors; f < end; f++) {
|
||||
if (f->pseudoflavor == rqstp->rq_cred.cr_flavor)
|
||||
if (f->pseudoflavor == cred->cr_flavor)
|
||||
return f->flags;
|
||||
}
|
||||
return exp->ex_flags;
|
||||
|
||||
}
|
||||
|
||||
int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
|
||||
int nfsd_setuser(struct svc_cred *cred, struct svc_export *exp)
|
||||
{
|
||||
struct group_info *rqgi;
|
||||
struct group_info *gi;
|
||||
struct cred *new;
|
||||
int i;
|
||||
int flags = nfsexp_flags(rqstp, exp);
|
||||
int flags = nfsexp_flags(cred, exp);
|
||||
|
||||
/* discard any old override before preparing the new set */
|
||||
revert_creds(get_cred(current_real_cred()));
|
||||
@ -32,10 +32,10 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
new->fsuid = rqstp->rq_cred.cr_uid;
|
||||
new->fsgid = rqstp->rq_cred.cr_gid;
|
||||
new->fsuid = cred->cr_uid;
|
||||
new->fsgid = cred->cr_gid;
|
||||
|
||||
rqgi = rqstp->rq_cred.cr_group_info;
|
||||
rqgi = cred->cr_group_info;
|
||||
|
||||
if (flags & NFSEXP_ALLSQUASH) {
|
||||
new->fsuid = exp->ex_anon_uid;
|
||||
|
@ -12,6 +12,6 @@
|
||||
* Set the current process's fsuid/fsgid etc to those of the NFS
|
||||
* client user
|
||||
*/
|
||||
int nfsd_setuser(struct svc_rqst *, struct svc_export *);
|
||||
int nfsd_setuser(struct svc_cred *cred, struct svc_export *exp);
|
||||
|
||||
#endif /* LINUX_NFSD_AUTH_H */
|
||||
|
@ -147,8 +147,7 @@ nfsd4_block_get_device_info_simple(struct super_block *sb,
|
||||
struct pnfs_block_deviceaddr *dev;
|
||||
struct pnfs_block_volume *b;
|
||||
|
||||
dev = kzalloc(sizeof(struct pnfs_block_deviceaddr) +
|
||||
sizeof(struct pnfs_block_volume), GFP_KERNEL);
|
||||
dev = kzalloc(struct_size(dev, volumes, 1), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
gdp->gd_device = dev;
|
||||
@ -255,8 +254,7 @@ nfsd4_block_get_device_info_scsi(struct super_block *sb,
|
||||
const struct pr_ops *ops;
|
||||
int ret;
|
||||
|
||||
dev = kzalloc(sizeof(struct pnfs_block_deviceaddr) +
|
||||
sizeof(struct pnfs_block_volume), GFP_KERNEL);
|
||||
dev = kzalloc(struct_size(dev, volumes, 1), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
gdp->gd_device = dev;
|
||||
|
@ -47,7 +47,7 @@ struct pnfs_block_volume {
|
||||
|
||||
struct pnfs_block_deviceaddr {
|
||||
u32 nr_volumes;
|
||||
struct pnfs_block_volume volumes[];
|
||||
struct pnfs_block_volume volumes[] __counted_by(nr_volumes);
|
||||
};
|
||||
|
||||
__be32 nfsd4_block_encode_getdeviceinfo(struct xdr_stream *xdr,
|
||||
|
@ -10,7 +10,7 @@
|
||||
#define NFSCACHE_H
|
||||
|
||||
#include <linux/sunrpc/svc.h>
|
||||
#include "netns.h"
|
||||
#include "nfsd.h"
|
||||
|
||||
/*
|
||||
* Representation of a reply cache entry.
|
||||
|
@ -1121,7 +1121,7 @@ ok:
|
||||
return 0;
|
||||
|
||||
denied:
|
||||
return rqstp->rq_vers < 4 ? nfserr_acces : nfserr_wrongsec;
|
||||
return nfserr_wrongsec;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1164,19 +1164,35 @@ gss:
|
||||
return gssexp;
|
||||
}
|
||||
|
||||
/**
|
||||
* rqst_exp_find - Find an svc_export in the context of a rqst or similar
|
||||
* @reqp: The handle to be used to suspend the request if a cache-upcall is needed
|
||||
* If NULL, missing in-cache information will result in failure.
|
||||
* @net: The network namespace in which the request exists
|
||||
* @cl: default auth_domain to use for looking up the export
|
||||
* @gsscl: an alternate auth_domain defined using deprecated gss/krb5 format.
|
||||
* @fsid_type: The type of fsid to look for
|
||||
* @fsidv: The actual fsid to look up in the context of either client.
|
||||
*
|
||||
* Perform a lookup for @cl/@fsidv in the given @net for an export. If
|
||||
* none found and @gsscl specified, repeat the lookup.
|
||||
*
|
||||
* Returns an export, or an error pointer.
|
||||
*/
|
||||
struct svc_export *
|
||||
rqst_exp_find(struct svc_rqst *rqstp, int fsid_type, u32 *fsidv)
|
||||
rqst_exp_find(struct cache_req *reqp, struct net *net,
|
||||
struct auth_domain *cl, struct auth_domain *gsscl,
|
||||
int fsid_type, u32 *fsidv)
|
||||
{
|
||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||
struct svc_export *gssexp, *exp = ERR_PTR(-ENOENT);
|
||||
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
|
||||
struct cache_detail *cd = nn->svc_export_cache;
|
||||
|
||||
if (rqstp->rq_client == NULL)
|
||||
if (!cl)
|
||||
goto gss;
|
||||
|
||||
/* First try the auth_unix client: */
|
||||
exp = exp_find(cd, rqstp->rq_client, fsid_type,
|
||||
fsidv, &rqstp->rq_chandle);
|
||||
exp = exp_find(cd, cl, fsid_type, fsidv, reqp);
|
||||
if (PTR_ERR(exp) == -ENOENT)
|
||||
goto gss;
|
||||
if (IS_ERR(exp))
|
||||
@ -1186,10 +1202,9 @@ rqst_exp_find(struct svc_rqst *rqstp, int fsid_type, u32 *fsidv)
|
||||
return exp;
|
||||
gss:
|
||||
/* Otherwise, try falling back on gss client */
|
||||
if (rqstp->rq_gssclient == NULL)
|
||||
if (!gsscl)
|
||||
return exp;
|
||||
gssexp = exp_find(cd, rqstp->rq_gssclient, fsid_type, fsidv,
|
||||
&rqstp->rq_chandle);
|
||||
gssexp = exp_find(cd, gsscl, fsid_type, fsidv, reqp);
|
||||
if (PTR_ERR(gssexp) == -ENOENT)
|
||||
return exp;
|
||||
if (!IS_ERR(exp))
|
||||
@ -1220,7 +1235,9 @@ struct svc_export *rqst_find_fsidzero_export(struct svc_rqst *rqstp)
|
||||
|
||||
mk_fsid(FSID_NUM, fsidv, 0, 0, 0, NULL);
|
||||
|
||||
return rqst_exp_find(rqstp, FSID_NUM, fsidv);
|
||||
return rqst_exp_find(&rqstp->rq_chandle, SVC_NET(rqstp),
|
||||
rqstp->rq_client, rqstp->rq_gssclient,
|
||||
FSID_NUM, fsidv);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -99,7 +99,8 @@ struct svc_expkey {
|
||||
#define EX_NOHIDE(exp) ((exp)->ex_flags & NFSEXP_NOHIDE)
|
||||
#define EX_WGATHER(exp) ((exp)->ex_flags & NFSEXP_GATHERED_WRITES)
|
||||
|
||||
int nfsexp_flags(struct svc_rqst *rqstp, struct svc_export *exp);
|
||||
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);
|
||||
|
||||
/*
|
||||
@ -127,6 +128,8 @@ static inline struct svc_export *exp_get(struct svc_export *exp)
|
||||
cache_get(&exp->h);
|
||||
return exp;
|
||||
}
|
||||
struct svc_export * rqst_exp_find(struct svc_rqst *, int, u32 *);
|
||||
struct svc_export *rqst_exp_find(struct cache_req *reqp, struct net *net,
|
||||
struct auth_domain *cl, struct auth_domain *gsscl,
|
||||
int fsid_type, u32 *fsidv);
|
||||
|
||||
#endif /* NFSD_EXPORT_H */
|
||||
|
@ -56,6 +56,7 @@
|
||||
|
||||
static DEFINE_PER_CPU(unsigned long, nfsd_file_cache_hits);
|
||||
static DEFINE_PER_CPU(unsigned long, nfsd_file_acquisitions);
|
||||
static DEFINE_PER_CPU(unsigned long, nfsd_file_allocations);
|
||||
static DEFINE_PER_CPU(unsigned long, nfsd_file_releases);
|
||||
static DEFINE_PER_CPU(unsigned long, nfsd_file_total_age);
|
||||
static DEFINE_PER_CPU(unsigned long, nfsd_file_evictions);
|
||||
@ -111,7 +112,7 @@ static void
|
||||
nfsd_file_schedule_laundrette(void)
|
||||
{
|
||||
if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags))
|
||||
queue_delayed_work(system_wq, &nfsd_filecache_laundrette,
|
||||
queue_delayed_work(system_unbound_wq, &nfsd_filecache_laundrette,
|
||||
NFSD_LAUNDRETTE_DELAY);
|
||||
}
|
||||
|
||||
@ -151,7 +152,7 @@ nfsd_file_mark_put(struct nfsd_file_mark *nfm)
|
||||
}
|
||||
|
||||
static struct nfsd_file_mark *
|
||||
nfsd_file_mark_find_or_create(struct nfsd_file *nf, struct inode *inode)
|
||||
nfsd_file_mark_find_or_create(struct inode *inode)
|
||||
{
|
||||
int err;
|
||||
struct fsnotify_mark *mark;
|
||||
@ -215,7 +216,9 @@ nfsd_file_alloc(struct net *net, struct inode *inode, unsigned char need,
|
||||
if (unlikely(!nf))
|
||||
return NULL;
|
||||
|
||||
this_cpu_inc(nfsd_file_allocations);
|
||||
INIT_LIST_HEAD(&nf->nf_lru);
|
||||
INIT_LIST_HEAD(&nf->nf_gc);
|
||||
nf->nf_birthtime = ktime_get();
|
||||
nf->nf_file = NULL;
|
||||
nf->nf_cred = get_current_cred();
|
||||
@ -393,8 +396,8 @@ nfsd_file_dispose_list(struct list_head *dispose)
|
||||
struct nfsd_file *nf;
|
||||
|
||||
while (!list_empty(dispose)) {
|
||||
nf = list_first_entry(dispose, struct nfsd_file, nf_lru);
|
||||
list_del_init(&nf->nf_lru);
|
||||
nf = list_first_entry(dispose, struct nfsd_file, nf_gc);
|
||||
list_del_init(&nf->nf_gc);
|
||||
nfsd_file_free(nf);
|
||||
}
|
||||
}
|
||||
@ -411,12 +414,12 @@ nfsd_file_dispose_list_delayed(struct list_head *dispose)
|
||||
{
|
||||
while(!list_empty(dispose)) {
|
||||
struct nfsd_file *nf = list_first_entry(dispose,
|
||||
struct nfsd_file, nf_lru);
|
||||
struct nfsd_file, nf_gc);
|
||||
struct nfsd_net *nn = net_generic(nf->nf_net, nfsd_net_id);
|
||||
struct nfsd_fcache_disposal *l = nn->fcache_disposal;
|
||||
|
||||
spin_lock(&l->lock);
|
||||
list_move_tail(&nf->nf_lru, &l->freeme);
|
||||
list_move_tail(&nf->nf_gc, &l->freeme);
|
||||
spin_unlock(&l->lock);
|
||||
svc_wake_up(nn->nfsd_serv);
|
||||
}
|
||||
@ -503,7 +506,8 @@ nfsd_file_lru_cb(struct list_head *item, struct list_lru_one *lru,
|
||||
|
||||
/* Refcount went to zero. Unhash it and queue it to the dispose list */
|
||||
nfsd_file_unhash(nf);
|
||||
list_lru_isolate_move(lru, &nf->nf_lru, head);
|
||||
list_lru_isolate(lru, &nf->nf_lru);
|
||||
list_add(&nf->nf_gc, head);
|
||||
this_cpu_inc(nfsd_file_evictions);
|
||||
trace_nfsd_file_gc_disposed(nf);
|
||||
return LRU_REMOVED;
|
||||
@ -578,7 +582,7 @@ nfsd_file_cond_queue(struct nfsd_file *nf, struct list_head *dispose)
|
||||
|
||||
/* If refcount goes to 0, then put on the dispose list */
|
||||
if (refcount_sub_and_test(decrement, &nf->nf_ref)) {
|
||||
list_add(&nf->nf_lru, dispose);
|
||||
list_add(&nf->nf_gc, dispose);
|
||||
trace_nfsd_file_closing(nf);
|
||||
}
|
||||
}
|
||||
@ -654,8 +658,8 @@ nfsd_file_close_inode_sync(struct inode *inode)
|
||||
|
||||
nfsd_file_queue_for_close(inode, &dispose);
|
||||
while (!list_empty(&dispose)) {
|
||||
nf = list_first_entry(&dispose, struct nfsd_file, nf_lru);
|
||||
list_del_init(&nf->nf_lru);
|
||||
nf = list_first_entry(&dispose, struct nfsd_file, nf_gc);
|
||||
list_del_init(&nf->nf_gc);
|
||||
nfsd_file_free(nf);
|
||||
}
|
||||
}
|
||||
@ -909,6 +913,7 @@ nfsd_file_cache_shutdown(void)
|
||||
for_each_possible_cpu(i) {
|
||||
per_cpu(nfsd_file_cache_hits, i) = 0;
|
||||
per_cpu(nfsd_file_acquisitions, i) = 0;
|
||||
per_cpu(nfsd_file_allocations, i) = 0;
|
||||
per_cpu(nfsd_file_releases, i) = 0;
|
||||
per_cpu(nfsd_file_total_age, i) = 0;
|
||||
per_cpu(nfsd_file_evictions, i) = 0;
|
||||
@ -1024,7 +1029,7 @@ retry:
|
||||
if (unlikely(nf)) {
|
||||
spin_unlock(&inode->i_lock);
|
||||
rcu_read_unlock();
|
||||
nfsd_file_slab_free(&new->nf_rcu);
|
||||
nfsd_file_free(new);
|
||||
goto wait_for_construction;
|
||||
}
|
||||
nf = new;
|
||||
@ -1035,8 +1040,6 @@ retry:
|
||||
if (likely(ret == 0))
|
||||
goto open_file;
|
||||
|
||||
if (ret == -EEXIST)
|
||||
goto retry;
|
||||
trace_nfsd_file_insert_err(rqstp, inode, may_flags, ret);
|
||||
status = nfserr_jukebox;
|
||||
goto construction_err;
|
||||
@ -1051,6 +1054,7 @@ wait_for_construction:
|
||||
status = nfserr_jukebox;
|
||||
goto construction_err;
|
||||
}
|
||||
nfsd_file_put(nf);
|
||||
open_retry = false;
|
||||
fh_put(fhp);
|
||||
goto retry;
|
||||
@ -1074,7 +1078,7 @@ out:
|
||||
|
||||
open_file:
|
||||
trace_nfsd_file_alloc(nf);
|
||||
nf->nf_mark = nfsd_file_mark_find_or_create(nf, inode);
|
||||
nf->nf_mark = nfsd_file_mark_find_or_create(inode);
|
||||
if (nf->nf_mark) {
|
||||
if (file) {
|
||||
get_file(file);
|
||||
@ -1199,7 +1203,7 @@ nfsd_file_acquire_opened(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
*/
|
||||
int nfsd_file_cache_stats_show(struct seq_file *m, void *v)
|
||||
{
|
||||
unsigned long releases = 0, evictions = 0;
|
||||
unsigned long allocations = 0, releases = 0, evictions = 0;
|
||||
unsigned long hits = 0, acquisitions = 0;
|
||||
unsigned int i, count = 0, buckets = 0;
|
||||
unsigned long lru = 0, total_age = 0;
|
||||
@ -1224,6 +1228,7 @@ int nfsd_file_cache_stats_show(struct seq_file *m, void *v)
|
||||
for_each_possible_cpu(i) {
|
||||
hits += per_cpu(nfsd_file_cache_hits, i);
|
||||
acquisitions += per_cpu(nfsd_file_acquisitions, i);
|
||||
allocations += per_cpu(nfsd_file_allocations, i);
|
||||
releases += per_cpu(nfsd_file_releases, i);
|
||||
total_age += per_cpu(nfsd_file_total_age, i);
|
||||
evictions += per_cpu(nfsd_file_evictions, i);
|
||||
@ -1234,6 +1239,7 @@ int nfsd_file_cache_stats_show(struct seq_file *m, void *v)
|
||||
seq_printf(m, "lru entries: %lu\n", lru);
|
||||
seq_printf(m, "cache hits: %lu\n", hits);
|
||||
seq_printf(m, "acquisitions: %lu\n", acquisitions);
|
||||
seq_printf(m, "allocations: %lu\n", allocations);
|
||||
seq_printf(m, "releases: %lu\n", releases);
|
||||
seq_printf(m, "evictions: %lu\n", evictions);
|
||||
if (releases)
|
||||
|
@ -44,6 +44,7 @@ struct nfsd_file {
|
||||
|
||||
struct nfsd_file_mark *nf_mark;
|
||||
struct list_head nf_lru;
|
||||
struct list_head nf_gc;
|
||||
struct rcu_head nf_rcu;
|
||||
ktime_t nf_birthtime;
|
||||
};
|
||||
|
@ -148,12 +148,13 @@ struct nfsd_net {
|
||||
u32 s2s_cp_cl_id;
|
||||
struct idr s2s_cp_stateids;
|
||||
spinlock_t s2s_cp_lock;
|
||||
atomic_t pending_async_copies;
|
||||
|
||||
/*
|
||||
* Version information
|
||||
*/
|
||||
bool *nfsd_versions;
|
||||
bool *nfsd4_minorversions;
|
||||
bool nfsd_versions[NFSD_MAXVERS + 1];
|
||||
bool nfsd4_minorversions[NFSD_SUPPORTED_MINOR_VERSION + 1];
|
||||
|
||||
/*
|
||||
* Duplicate reply cache
|
||||
@ -219,8 +220,6 @@ struct nfsd_net {
|
||||
#define nfsd_netns_ready(nn) ((nn)->sessionid_hashtbl)
|
||||
|
||||
extern bool nfsd_support_version(int vers);
|
||||
extern void nfsd_netns_free_versions(struct nfsd_net *nn);
|
||||
|
||||
extern unsigned int nfsd_net_id;
|
||||
|
||||
void nfsd_copy_write_verifier(__be32 verf[2], struct nfsd_net *nn);
|
||||
|
@ -28,6 +28,29 @@ static int nfs3_ftypes[] = {
|
||||
S_IFIFO, /* NF3FIFO */
|
||||
};
|
||||
|
||||
static __be32 nfsd3_map_status(__be32 status)
|
||||
{
|
||||
switch (status) {
|
||||
case nfs_ok:
|
||||
break;
|
||||
case nfserr_nofilehandle:
|
||||
status = nfserr_badhandle;
|
||||
break;
|
||||
case nfserr_wrongsec:
|
||||
case nfserr_file_open:
|
||||
status = nfserr_acces;
|
||||
break;
|
||||
case nfserr_symlink_not_dir:
|
||||
status = nfserr_notdir;
|
||||
break;
|
||||
case nfserr_symlink:
|
||||
case nfserr_wrong_type:
|
||||
status = nfserr_inval;
|
||||
break;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* NULL call.
|
||||
*/
|
||||
@ -57,6 +80,7 @@ nfsd3_proc_getattr(struct svc_rqst *rqstp)
|
||||
|
||||
resp->status = fh_getattr(&resp->fh, &resp->stat);
|
||||
out:
|
||||
resp->status = nfsd3_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -80,6 +104,7 @@ nfsd3_proc_setattr(struct svc_rqst *rqstp)
|
||||
if (argp->check_guard)
|
||||
guardtime = &argp->guardtime;
|
||||
resp->status = nfsd_setattr(rqstp, &resp->fh, &attrs, guardtime);
|
||||
resp->status = nfsd3_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -103,6 +128,7 @@ nfsd3_proc_lookup(struct svc_rqst *rqstp)
|
||||
resp->status = nfsd_lookup(rqstp, &resp->dirfh,
|
||||
argp->name, argp->len,
|
||||
&resp->fh);
|
||||
resp->status = nfsd3_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -122,6 +148,7 @@ nfsd3_proc_access(struct svc_rqst *rqstp)
|
||||
fh_copy(&resp->fh, &argp->fh);
|
||||
resp->access = argp->access;
|
||||
resp->status = nfsd_access(rqstp, &resp->fh, &resp->access, NULL);
|
||||
resp->status = nfsd3_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -142,6 +169,7 @@ nfsd3_proc_readlink(struct svc_rqst *rqstp)
|
||||
resp->pages = rqstp->rq_next_page++;
|
||||
resp->status = nfsd_readlink(rqstp, &resp->fh,
|
||||
page_address(*resp->pages), &resp->len);
|
||||
resp->status = nfsd3_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -179,6 +207,7 @@ nfsd3_proc_read(struct svc_rqst *rqstp)
|
||||
fh_copy(&resp->fh, &argp->fh);
|
||||
resp->status = nfsd_read(rqstp, &resp->fh, argp->offset,
|
||||
&resp->count, &resp->eof);
|
||||
resp->status = nfsd3_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -212,6 +241,7 @@ nfsd3_proc_write(struct svc_rqst *rqstp)
|
||||
rqstp->rq_vec, nvecs, &cnt,
|
||||
resp->committed, resp->verf);
|
||||
resp->count = cnt;
|
||||
resp->status = nfsd3_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -359,6 +389,7 @@ nfsd3_proc_create(struct svc_rqst *rqstp)
|
||||
newfhp = fh_init(&resp->fh, NFS3_FHSIZE);
|
||||
|
||||
resp->status = nfsd3_create_file(rqstp, dirfhp, newfhp, argp);
|
||||
resp->status = nfsd3_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -384,6 +415,7 @@ nfsd3_proc_mkdir(struct svc_rqst *rqstp)
|
||||
fh_init(&resp->fh, NFS3_FHSIZE);
|
||||
resp->status = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len,
|
||||
&attrs, S_IFDIR, 0, &resp->fh);
|
||||
resp->status = nfsd3_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -424,6 +456,7 @@ nfsd3_proc_symlink(struct svc_rqst *rqstp)
|
||||
argp->flen, argp->tname, &attrs, &resp->fh);
|
||||
kfree(argp->tname);
|
||||
out:
|
||||
resp->status = nfsd3_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -465,6 +498,7 @@ nfsd3_proc_mknod(struct svc_rqst *rqstp)
|
||||
resp->status = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len,
|
||||
&attrs, type, rdev, &resp->fh);
|
||||
out:
|
||||
resp->status = nfsd3_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -486,6 +520,7 @@ nfsd3_proc_remove(struct svc_rqst *rqstp)
|
||||
fh_copy(&resp->fh, &argp->fh);
|
||||
resp->status = nfsd_unlink(rqstp, &resp->fh, -S_IFDIR,
|
||||
argp->name, argp->len);
|
||||
resp->status = nfsd3_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -506,6 +541,7 @@ nfsd3_proc_rmdir(struct svc_rqst *rqstp)
|
||||
fh_copy(&resp->fh, &argp->fh);
|
||||
resp->status = nfsd_unlink(rqstp, &resp->fh, S_IFDIR,
|
||||
argp->name, argp->len);
|
||||
resp->status = nfsd3_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -528,6 +564,7 @@ nfsd3_proc_rename(struct svc_rqst *rqstp)
|
||||
fh_copy(&resp->tfh, &argp->tfh);
|
||||
resp->status = nfsd_rename(rqstp, &resp->ffh, argp->fname, argp->flen,
|
||||
&resp->tfh, argp->tname, argp->tlen);
|
||||
resp->status = nfsd3_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -548,6 +585,7 @@ nfsd3_proc_link(struct svc_rqst *rqstp)
|
||||
fh_copy(&resp->tfh, &argp->tfh);
|
||||
resp->status = nfsd_link(rqstp, &resp->tfh, argp->tname, argp->tlen,
|
||||
&resp->fh);
|
||||
resp->status = nfsd3_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -600,6 +638,7 @@ nfsd3_proc_readdir(struct svc_rqst *rqstp)
|
||||
/* Recycle only pages that were part of the reply */
|
||||
rqstp->rq_next_page = resp->xdr.page_ptr + 1;
|
||||
|
||||
resp->status = nfsd3_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -644,6 +683,7 @@ nfsd3_proc_readdirplus(struct svc_rqst *rqstp)
|
||||
rqstp->rq_next_page = resp->xdr.page_ptr + 1;
|
||||
|
||||
out:
|
||||
resp->status = nfsd3_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -661,6 +701,7 @@ nfsd3_proc_fsstat(struct svc_rqst *rqstp)
|
||||
|
||||
resp->status = nfsd_statfs(rqstp, &argp->fh, &resp->stats, 0);
|
||||
fh_put(&argp->fh);
|
||||
resp->status = nfsd3_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -704,6 +745,7 @@ nfsd3_proc_fsinfo(struct svc_rqst *rqstp)
|
||||
}
|
||||
|
||||
fh_put(&argp->fh);
|
||||
resp->status = nfsd3_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -746,6 +788,7 @@ nfsd3_proc_pathconf(struct svc_rqst *rqstp)
|
||||
}
|
||||
|
||||
fh_put(&argp->fh);
|
||||
resp->status = nfsd3_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -773,6 +816,7 @@ nfsd3_proc_commit(struct svc_rqst *rqstp)
|
||||
argp->count, resp->verf);
|
||||
nfsd_file_put(nf);
|
||||
out:
|
||||
resp->status = nfsd3_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
|
@ -1223,6 +1223,7 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
|
||||
* cb_seq_status is only set in decode_cb_sequence4res,
|
||||
* and so will remain 1 if an rpc level failure occurs.
|
||||
*/
|
||||
trace_nfsd_cb_rpc_prepare(clp);
|
||||
cb->cb_seq_status = 1;
|
||||
cb->cb_status = 0;
|
||||
if (minorversion && !nfsd41_cb_get_slot(cb, task))
|
||||
@ -1329,11 +1330,14 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata)
|
||||
struct nfsd4_callback *cb = calldata;
|
||||
struct nfs4_client *clp = cb->cb_clp;
|
||||
|
||||
trace_nfsd_cb_rpc_done(clp);
|
||||
|
||||
if (!nfsd4_cb_sequence_done(task, cb))
|
||||
return;
|
||||
|
||||
if (cb->cb_status) {
|
||||
WARN_ON_ONCE(task->tk_status);
|
||||
WARN_ONCE(task->tk_status, "cb_status=%d tk_status=%d",
|
||||
cb->cb_status, task->tk_status);
|
||||
task->tk_status = cb->cb_status;
|
||||
}
|
||||
|
||||
@ -1359,6 +1363,8 @@ static void nfsd4_cb_release(void *calldata)
|
||||
{
|
||||
struct nfsd4_callback *cb = calldata;
|
||||
|
||||
trace_nfsd_cb_rpc_release(cb->cb_clp);
|
||||
|
||||
if (cb->cb_need_restart)
|
||||
nfsd4_queue_cb(cb);
|
||||
else
|
||||
|
@ -581,6 +581,7 @@ static __be32 idmap_id_to_name(struct xdr_stream *xdr,
|
||||
.id = id,
|
||||
.type = type,
|
||||
};
|
||||
__be32 status = nfs_ok;
|
||||
__be32 *p;
|
||||
int ret;
|
||||
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
|
||||
@ -593,12 +594,16 @@ static __be32 idmap_id_to_name(struct xdr_stream *xdr,
|
||||
return nfserrno(ret);
|
||||
ret = strlen(item->name);
|
||||
WARN_ON_ONCE(ret > IDMAP_NAMESZ);
|
||||
|
||||
p = xdr_reserve_space(xdr, ret + 4);
|
||||
if (!p)
|
||||
return nfserr_resource;
|
||||
p = xdr_encode_opaque(p, item->name, ret);
|
||||
if (unlikely(!p)) {
|
||||
status = nfserr_resource;
|
||||
goto out_put;
|
||||
}
|
||||
xdr_encode_opaque(p, item->name, ret);
|
||||
out_put:
|
||||
cache_put(&item->h, nn->idtoname_cache);
|
||||
return 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -740,6 +740,7 @@ static const struct nfsd4_callback_ops nfsd4_cb_layout_ops = {
|
||||
.prepare = nfsd4_cb_layout_prepare,
|
||||
.done = nfsd4_cb_layout_done,
|
||||
.release = nfsd4_cb_layout_release,
|
||||
.opcode = OP_CB_LAYOUTRECALL,
|
||||
};
|
||||
|
||||
static bool
|
||||
|
@ -158,7 +158,7 @@ do_open_permission(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfs
|
||||
return fh_verify(rqstp, current_fh, S_IFREG, accmode);
|
||||
}
|
||||
|
||||
static __be32 nfsd_check_obj_isreg(struct svc_fh *fh)
|
||||
static __be32 nfsd_check_obj_isreg(struct svc_fh *fh, u32 minor_version)
|
||||
{
|
||||
umode_t mode = d_inode(fh->fh_dentry)->i_mode;
|
||||
|
||||
@ -166,14 +166,15 @@ static __be32 nfsd_check_obj_isreg(struct svc_fh *fh)
|
||||
return nfs_ok;
|
||||
if (S_ISDIR(mode))
|
||||
return nfserr_isdir;
|
||||
/*
|
||||
* Using err_symlink as our catch-all case may look odd; but
|
||||
* there's no other obvious error for this case in 4.0, and we
|
||||
* happen to know that it will cause the linux v4 client to do
|
||||
* the right thing on attempts to open something other than a
|
||||
* regular file.
|
||||
*/
|
||||
if (S_ISLNK(mode))
|
||||
return nfserr_symlink;
|
||||
|
||||
/* RFC 7530 - 16.16.6 */
|
||||
if (minor_version == 0)
|
||||
return nfserr_symlink;
|
||||
else
|
||||
return nfserr_wrong_type;
|
||||
|
||||
}
|
||||
|
||||
static void nfsd4_set_open_owner_reply_cache(struct nfsd4_compound_state *cstate, struct nfsd4_open *open, struct svc_fh *resfh)
|
||||
@ -466,7 +467,7 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru
|
||||
}
|
||||
if (status)
|
||||
goto out;
|
||||
status = nfsd_check_obj_isreg(*resfh);
|
||||
status = nfsd_check_obj_isreg(*resfh, cstate->minorversion);
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
@ -751,15 +752,6 @@ nfsd4_access(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
&access->ac_supported);
|
||||
}
|
||||
|
||||
static void gen_boot_verifier(nfs4_verifier *verifier, struct net *net)
|
||||
{
|
||||
__be32 *verf = (__be32 *)verifier->data;
|
||||
|
||||
BUILD_BUG_ON(2*sizeof(*verf) != sizeof(verifier->data));
|
||||
|
||||
nfsd_copy_write_verifier(verf, net_generic(net, nfsd_net_id));
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd4_commit(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
union nfsd4_op_u *u)
|
||||
@ -1288,6 +1280,7 @@ static void nfs4_put_copy(struct nfsd4_copy *copy)
|
||||
{
|
||||
if (!refcount_dec_and_test(©->refcount))
|
||||
return;
|
||||
atomic_dec(©->cp_nn->pending_async_copies);
|
||||
kfree(copy->cp_src);
|
||||
kfree(copy);
|
||||
}
|
||||
@ -1621,7 +1614,8 @@ static int nfsd4_cb_offload_done(struct nfsd4_callback *cb,
|
||||
|
||||
static const struct nfsd4_callback_ops nfsd4_cb_offload_ops = {
|
||||
.release = nfsd4_cb_offload_release,
|
||||
.done = nfsd4_cb_offload_done
|
||||
.done = nfsd4_cb_offload_done,
|
||||
.opcode = OP_CB_OFFLOAD,
|
||||
};
|
||||
|
||||
static void nfsd4_init_copy_res(struct nfsd4_copy *copy, bool sync)
|
||||
@ -1630,7 +1624,6 @@ static void nfsd4_init_copy_res(struct nfsd4_copy *copy, bool sync)
|
||||
test_bit(NFSD4_COPY_F_COMMITTED, ©->cp_flags) ?
|
||||
NFS_FILE_SYNC : NFS_UNSTABLE;
|
||||
nfsd4_copy_set_sync(copy, sync);
|
||||
gen_boot_verifier(©->cp_res.wr_verifier, copy->cp_clp->net);
|
||||
}
|
||||
|
||||
static ssize_t _nfsd_copy_file_range(struct nfsd4_copy *copy,
|
||||
@ -1767,7 +1760,7 @@ static int nfsd4_do_async_copy(void *data)
|
||||
{
|
||||
struct nfsd4_copy *copy = (struct nfsd4_copy *)data;
|
||||
|
||||
trace_nfsd_copy_do_async(copy);
|
||||
trace_nfsd_copy_async(copy);
|
||||
if (nfsd4_ssc_is_inter(copy)) {
|
||||
struct file *filp;
|
||||
|
||||
@ -1794,6 +1787,7 @@ static int nfsd4_do_async_copy(void *data)
|
||||
|
||||
do_callback:
|
||||
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;
|
||||
@ -1803,9 +1797,11 @@ static __be32
|
||||
nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
union nfsd4_op_u *u)
|
||||
{
|
||||
struct nfsd4_copy *copy = &u->copy;
|
||||
__be32 status;
|
||||
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
|
||||
struct nfsd4_copy *async_copy = NULL;
|
||||
struct nfsd4_copy *copy = &u->copy;
|
||||
struct nfsd42_write_res *result;
|
||||
__be32 status;
|
||||
|
||||
/*
|
||||
* Currently, async COPY is not reliable. Force all COPY
|
||||
@ -1814,6 +1810,9 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
*/
|
||||
nfsd4_copy_set_sync(copy, true);
|
||||
|
||||
result = ©->cp_res;
|
||||
nfsd_copy_write_verifier((__be32 *)&result->wr_verifier.data, nn);
|
||||
|
||||
copy->cp_clp = cstate->clp;
|
||||
if (nfsd4_ssc_is_inter(copy)) {
|
||||
trace_nfsd_copy_inter(copy);
|
||||
@ -1838,12 +1837,16 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
memcpy(©->fh, &cstate->current_fh.fh_handle,
|
||||
sizeof(struct knfsd_fh));
|
||||
if (nfsd4_copy_is_async(copy)) {
|
||||
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
|
||||
|
||||
status = nfserrno(-ENOMEM);
|
||||
async_copy = kzalloc(sizeof(struct nfsd4_copy), GFP_KERNEL);
|
||||
if (!async_copy)
|
||||
goto out_err;
|
||||
async_copy->cp_nn = nn;
|
||||
/* Arbitrary cap on number of pending async copy operations */
|
||||
if (atomic_inc_return(&nn->pending_async_copies) >
|
||||
(int)rqstp->rq_pool->sp_nrthreads) {
|
||||
atomic_dec(&nn->pending_async_copies);
|
||||
goto out_err;
|
||||
}
|
||||
INIT_LIST_HEAD(&async_copy->copies);
|
||||
refcount_set(&async_copy->refcount, 1);
|
||||
async_copy->cp_src = kmalloc(sizeof(*async_copy->cp_src), GFP_KERNEL);
|
||||
@ -1851,8 +1854,8 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
goto out_err;
|
||||
if (!nfs4_init_copy_state(nn, copy))
|
||||
goto out_err;
|
||||
memcpy(©->cp_res.cb_stateid, ©->cp_stateid.cs_stid,
|
||||
sizeof(copy->cp_res.cb_stateid));
|
||||
memcpy(&result->cb_stateid, ©->cp_stateid.cs_stid,
|
||||
sizeof(result->cb_stateid));
|
||||
dup_copy_fields(copy, async_copy);
|
||||
async_copy->copy_task = kthread_create(nfsd4_do_async_copy,
|
||||
async_copy, "%s", "copy thread");
|
||||
@ -1883,7 +1886,7 @@ out_err:
|
||||
}
|
||||
if (async_copy)
|
||||
cleanup_async_copy(async_copy);
|
||||
status = nfserrno(-ENOMEM);
|
||||
status = nfserr_jukebox;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -1942,7 +1945,7 @@ nfsd4_copy_notify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
struct nfsd4_copy_notify *cn = &u->copy_notify;
|
||||
__be32 status;
|
||||
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
|
||||
struct nfs4_stid *stid;
|
||||
struct nfs4_stid *stid = NULL;
|
||||
struct nfs4_cpntf_state *cps;
|
||||
struct nfs4_client *clp = cstate->clp;
|
||||
|
||||
@ -1951,6 +1954,8 @@ nfsd4_copy_notify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
&stid);
|
||||
if (status)
|
||||
return status;
|
||||
if (!stid)
|
||||
return nfserr_bad_stateid;
|
||||
|
||||
cn->cpn_lease_time.tv_sec = nn->nfsd4_lease;
|
||||
cn->cpn_lease_time.tv_nsec = 0;
|
||||
@ -2231,7 +2236,9 @@ nfsd4_getdeviceinfo(struct svc_rqst *rqstp,
|
||||
return nfserr_noent;
|
||||
}
|
||||
|
||||
exp = rqst_exp_find(rqstp, map->fsid_type, map->fsid);
|
||||
exp = rqst_exp_find(&rqstp->rq_chandle, SVC_NET(rqstp),
|
||||
rqstp->rq_client, rqstp->rq_gssclient,
|
||||
map->fsid_type, map->fsid);
|
||||
if (IS_ERR(exp)) {
|
||||
dprintk("%s: could not find device id\n", __func__);
|
||||
return nfserr_noent;
|
||||
|
@ -809,6 +809,10 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg,
|
||||
ci = &cmsg->cm_u.cm_clntinfo;
|
||||
if (get_user(namelen, &ci->cc_name.cn_len))
|
||||
return -EFAULT;
|
||||
if (namelen == 0 || namelen > NFS4_OPAQUE_LIMIT) {
|
||||
dprintk("%s: invalid namelen (%u)", __func__, namelen);
|
||||
return -EINVAL;
|
||||
}
|
||||
name.data = memdup_user(&ci->cc_name.cn_id, namelen);
|
||||
if (IS_ERR(name.data))
|
||||
return PTR_ERR(name.data);
|
||||
@ -831,6 +835,10 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg,
|
||||
cnm = &cmsg->cm_u.cm_name;
|
||||
if (get_user(namelen, &cnm->cn_len))
|
||||
return -EFAULT;
|
||||
if (namelen == 0 || namelen > NFS4_OPAQUE_LIMIT) {
|
||||
dprintk("%s: invalid namelen (%u)", __func__, namelen);
|
||||
return -EINVAL;
|
||||
}
|
||||
name.data = memdup_user(&cnm->cn_id, namelen);
|
||||
if (IS_ERR(name.data))
|
||||
return PTR_ERR(name.data);
|
||||
@ -1895,10 +1903,7 @@ nfsd4_cltrack_upcall_lock(struct nfs4_client *clp)
|
||||
static void
|
||||
nfsd4_cltrack_upcall_unlock(struct nfs4_client *clp)
|
||||
{
|
||||
smp_mb__before_atomic();
|
||||
clear_bit(NFSD4_CLIENT_UPCALL_LOCK, &clp->cl_flags);
|
||||
smp_mb__after_atomic();
|
||||
wake_up_bit(&clp->cl_flags, NFSD4_CLIENT_UPCALL_LOCK);
|
||||
clear_and_wake_up_bit(NFSD4_CLIENT_UPCALL_LOCK, &clp->cl_flags);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -400,6 +400,7 @@ static const struct nfsd4_callback_ops nfsd4_cb_notify_lock_ops = {
|
||||
.prepare = nfsd4_cb_notify_lock_prepare,
|
||||
.done = nfsd4_cb_notify_lock_done,
|
||||
.release = nfsd4_cb_notify_lock_release,
|
||||
.opcode = OP_CB_NOTIFY_LOCK,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -1077,7 +1078,8 @@ static void nfs4_free_deleg(struct nfs4_stid *stid)
|
||||
* When a delegation is recalled, the filehandle is stored in the "new"
|
||||
* filter.
|
||||
* Every 30 seconds we swap the filters and clear the "new" one,
|
||||
* unless both are empty of course.
|
||||
* unless both are empty of course. This results in delegations for a
|
||||
* given filehandle being blocked for between 30 and 60 seconds.
|
||||
*
|
||||
* Each filter is 256 bits. We hash the filehandle to 32bit and use the
|
||||
* low 3 bytes as hash-table indices.
|
||||
@ -1106,9 +1108,9 @@ static int delegation_blocked(struct knfsd_fh *fh)
|
||||
if (ktime_get_seconds() - bd->swap_time > 30) {
|
||||
bd->entries -= bd->old_entries;
|
||||
bd->old_entries = bd->entries;
|
||||
bd->new = 1-bd->new;
|
||||
memset(bd->set[bd->new], 0,
|
||||
sizeof(bd->set[0]));
|
||||
bd->new = 1-bd->new;
|
||||
bd->swap_time = ktime_get_seconds();
|
||||
}
|
||||
spin_unlock(&blocked_delegations_lock);
|
||||
@ -1663,9 +1665,7 @@ static void release_openowner(struct nfs4_openowner *oo)
|
||||
{
|
||||
struct nfs4_ol_stateid *stp;
|
||||
struct nfs4_client *clp = oo->oo_owner.so_client;
|
||||
struct list_head reaplist;
|
||||
|
||||
INIT_LIST_HEAD(&reaplist);
|
||||
LIST_HEAD(reaplist);
|
||||
|
||||
spin_lock(&clp->cl_lock);
|
||||
unhash_openowner_locked(oo);
|
||||
@ -2369,9 +2369,8 @@ __destroy_client(struct nfs4_client *clp)
|
||||
int i;
|
||||
struct nfs4_openowner *oo;
|
||||
struct nfs4_delegation *dp;
|
||||
struct list_head reaplist;
|
||||
LIST_HEAD(reaplist);
|
||||
|
||||
INIT_LIST_HEAD(&reaplist);
|
||||
spin_lock(&state_lock);
|
||||
while (!list_empty(&clp->cl_delegations)) {
|
||||
dp = list_entry(clp->cl_delegations.next, struct nfs4_delegation, dl_perclnt);
|
||||
@ -2692,7 +2691,7 @@ static int client_info_show(struct seq_file *m, void *v)
|
||||
clp->cl_nii_time.tv_sec, clp->cl_nii_time.tv_nsec);
|
||||
}
|
||||
seq_printf(m, "callback state: %s\n", cb_state2str(clp->cl_cb_state));
|
||||
seq_printf(m, "callback address: %pISpc\n", &clp->cl_cb_conn.cb_addr);
|
||||
seq_printf(m, "callback address: \"%pISpc\"\n", &clp->cl_cb_conn.cb_addr);
|
||||
seq_printf(m, "admin-revoked states: %d\n",
|
||||
atomic_read(&clp->cl_admin_revoked));
|
||||
drop_client(clp);
|
||||
@ -3059,7 +3058,10 @@ nfsd4_cb_getattr_done(struct nfsd4_callback *cb, struct rpc_task *task)
|
||||
{
|
||||
struct nfs4_cb_fattr *ncf =
|
||||
container_of(cb, struct nfs4_cb_fattr, ncf_getattr);
|
||||
struct nfs4_delegation *dp =
|
||||
container_of(ncf, struct nfs4_delegation, dl_cb_fattr);
|
||||
|
||||
trace_nfsd_cb_getattr_done(&dp->dl_stid.sc_stateid, task);
|
||||
ncf->ncf_cb_status = task->tk_status;
|
||||
switch (task->tk_status) {
|
||||
case -NFS4ERR_DELAY:
|
||||
@ -3078,19 +3080,20 @@ nfsd4_cb_getattr_release(struct nfsd4_callback *cb)
|
||||
struct nfs4_delegation *dp =
|
||||
container_of(ncf, struct nfs4_delegation, dl_cb_fattr);
|
||||
|
||||
clear_bit(CB_GETATTR_BUSY, &ncf->ncf_cb_flags);
|
||||
wake_up_bit(&ncf->ncf_cb_flags, CB_GETATTR_BUSY);
|
||||
clear_and_wake_up_bit(CB_GETATTR_BUSY, &ncf->ncf_cb_flags);
|
||||
nfs4_put_stid(&dp->dl_stid);
|
||||
}
|
||||
|
||||
static const struct nfsd4_callback_ops nfsd4_cb_recall_any_ops = {
|
||||
.done = nfsd4_cb_recall_any_done,
|
||||
.release = nfsd4_cb_recall_any_release,
|
||||
.opcode = OP_CB_RECALL_ANY,
|
||||
};
|
||||
|
||||
static const struct nfsd4_callback_ops nfsd4_cb_getattr_ops = {
|
||||
.done = nfsd4_cb_getattr_done,
|
||||
.release = nfsd4_cb_getattr_release,
|
||||
.opcode = OP_CB_GETATTR,
|
||||
};
|
||||
|
||||
static void nfs4_cb_getattr(struct nfs4_cb_fattr *ncf)
|
||||
@ -4704,6 +4707,7 @@ void nfsd4_cstate_clear_replay(struct nfsd4_compound_state *cstate)
|
||||
if (so != NULL) {
|
||||
cstate->replay_owner = NULL;
|
||||
atomic_set(&so->so_replay.rp_locked, RP_UNLOCKED);
|
||||
smp_mb__after_atomic();
|
||||
wake_up_var(&so->so_replay.rp_locked);
|
||||
nfs4_put_stateowner(so);
|
||||
}
|
||||
@ -5004,6 +5008,7 @@ move_to_close_lru(struct nfs4_ol_stateid *s, struct net *net)
|
||||
* so tell them to stop waiting.
|
||||
*/
|
||||
atomic_set(&oo->oo_owner.so_replay.rp_locked, RP_UNHASHED);
|
||||
smp_mb__after_atomic();
|
||||
wake_up_var(&oo->oo_owner.so_replay.rp_locked);
|
||||
wait_event(close_wq, refcount_read(&s->st_stid.sc_count) == 2);
|
||||
|
||||
@ -5218,6 +5223,7 @@ static const struct nfsd4_callback_ops nfsd4_cb_recall_ops = {
|
||||
.prepare = nfsd4_cb_recall_prepare,
|
||||
.done = nfsd4_cb_recall_done,
|
||||
.release = nfsd4_cb_recall_release,
|
||||
.opcode = OP_CB_RECALL,
|
||||
};
|
||||
|
||||
static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
|
||||
@ -5277,11 +5283,8 @@ static bool nfsd_breaker_owns_lease(struct file_lease *fl)
|
||||
struct svc_rqst *rqst;
|
||||
struct nfs4_client *clp;
|
||||
|
||||
if (!i_am_nfsd())
|
||||
return false;
|
||||
rqst = kthread_data(current);
|
||||
/* Note rq_prog == NFS_ACL_PROGRAM is also possible: */
|
||||
if (rqst->rq_prog != NFS_PROGRAM || rqst->rq_vers < 4)
|
||||
rqst = nfsd_current_rqst();
|
||||
if (!nfsd_v4client(rqst))
|
||||
return false;
|
||||
clp = *(rqst->rq_lease_breaker);
|
||||
return dl->dl_stid.sc_client == clp;
|
||||
@ -5859,7 +5862,7 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
|
||||
|
||||
/*
|
||||
* Now that the deleg is set, check again to ensure that nothing
|
||||
* raced in and changed the mode while we weren't lookng.
|
||||
* raced in and changed the mode while we weren't looking.
|
||||
*/
|
||||
status = nfsd4_verify_setuid_write(open, fp->fi_deleg_file);
|
||||
if (status)
|
||||
@ -5912,6 +5915,28 @@ static void nfsd4_open_deleg_none_ext(struct nfsd4_open *open, int status)
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
nfs4_delegation_stat(struct nfs4_delegation *dp, struct svc_fh *currentfh,
|
||||
struct kstat *stat)
|
||||
{
|
||||
struct nfsd_file *nf = find_rw_file(dp->dl_stid.sc_file);
|
||||
struct path path;
|
||||
int rc;
|
||||
|
||||
if (!nf)
|
||||
return false;
|
||||
|
||||
path.mnt = currentfh->fh_export->ex_path.mnt;
|
||||
path.dentry = file_dentry(nf->nf_file);
|
||||
|
||||
rc = vfs_getattr(&path, stat,
|
||||
(STATX_SIZE | STATX_CTIME | STATX_CHANGE_COOKIE),
|
||||
AT_STATX_SYNC_AS_STAT);
|
||||
|
||||
nfsd_file_put(nf);
|
||||
return rc == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The Linux NFS server does not offer write delegations to NFSv4.0
|
||||
* clients in order to avoid conflicts between write delegations and
|
||||
@ -5947,7 +5972,6 @@ nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
|
||||
int cb_up;
|
||||
int status = 0;
|
||||
struct kstat stat;
|
||||
struct path path;
|
||||
|
||||
cb_up = nfsd4_cb_channel_good(oo->oo_owner.so_client);
|
||||
open->op_recall = false;
|
||||
@ -5983,20 +6007,16 @@ nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
|
||||
memcpy(&open->op_delegate_stateid, &dp->dl_stid.sc_stateid, sizeof(dp->dl_stid.sc_stateid));
|
||||
|
||||
if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) {
|
||||
open->op_delegate_type = NFS4_OPEN_DELEGATE_WRITE;
|
||||
trace_nfsd_deleg_write(&dp->dl_stid.sc_stateid);
|
||||
path.mnt = currentfh->fh_export->ex_path.mnt;
|
||||
path.dentry = currentfh->fh_dentry;
|
||||
if (vfs_getattr(&path, &stat,
|
||||
(STATX_SIZE | STATX_CTIME | STATX_CHANGE_COOKIE),
|
||||
AT_STATX_SYNC_AS_STAT)) {
|
||||
if (!nfs4_delegation_stat(dp, currentfh, &stat)) {
|
||||
nfs4_put_stid(&dp->dl_stid);
|
||||
destroy_delegation(dp);
|
||||
goto out_no_deleg;
|
||||
}
|
||||
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));
|
||||
trace_nfsd_deleg_write(&dp->dl_stid.sc_stateid);
|
||||
} else {
|
||||
open->op_delegate_type = NFS4_OPEN_DELEGATE_READ;
|
||||
trace_nfsd_deleg_read(&dp->dl_stid.sc_stateid);
|
||||
@ -6271,7 +6291,6 @@ void nfsd4_ssc_init_umount_work(struct nfsd_net *nn)
|
||||
INIT_LIST_HEAD(&nn->nfsd_ssc_mount_list);
|
||||
init_waitqueue_head(&nn->nfsd_ssc_waitq);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfsd4_ssc_init_umount_work);
|
||||
|
||||
/*
|
||||
* This is called when nfsd is being shutdown, after all inter_ssc
|
||||
@ -6619,9 +6638,8 @@ deleg_reaper(struct nfsd_net *nn)
|
||||
{
|
||||
struct list_head *pos, *next;
|
||||
struct nfs4_client *clp;
|
||||
struct list_head cblist;
|
||||
LIST_HEAD(cblist);
|
||||
|
||||
INIT_LIST_HEAD(&cblist);
|
||||
spin_lock(&nn->client_lock);
|
||||
list_for_each_safe(pos, next, &nn->client_lru) {
|
||||
clp = list_entry(pos, struct nfs4_client, cl_lru);
|
||||
@ -6647,7 +6665,6 @@ deleg_reaper(struct nfsd_net *nn)
|
||||
cl_ra_cblist);
|
||||
list_del_init(&clp->cl_ra_cblist);
|
||||
clp->cl_ra->ra_keep = 0;
|
||||
clp->cl_ra->ra_bmval[0] = BIT(RCA4_TYPE_MASK_RDATA_DLG);
|
||||
clp->cl_ra->ra_bmval[0] = BIT(RCA4_TYPE_MASK_RDATA_DLG) |
|
||||
BIT(RCA4_TYPE_MASK_WDATA_DLG);
|
||||
trace_nfsd_cb_recall_any(clp->cl_ra);
|
||||
@ -6892,7 +6909,8 @@ nfs4_check_file(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfs4_stid *s,
|
||||
|
||||
nf = nfs4_find_file(s, flags);
|
||||
if (nf) {
|
||||
status = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry,
|
||||
status = nfsd_permission(&rqstp->rq_cred,
|
||||
fhp->fh_export, fhp->fh_dentry,
|
||||
acc | NFSD_MAY_OWNER_OVERRIDE);
|
||||
if (status) {
|
||||
nfsd_file_put(nf);
|
||||
@ -7023,11 +7041,7 @@ nfs4_preprocess_stateid_op(struct svc_rqst *rqstp,
|
||||
*nfp = NULL;
|
||||
|
||||
if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) {
|
||||
if (cstid)
|
||||
status = nfserr_bad_stateid;
|
||||
else
|
||||
status = check_special_stateids(net, fhp, stateid,
|
||||
flags);
|
||||
status = check_special_stateids(net, fhp, stateid, flags);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@ -7481,8 +7495,9 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
goto put_stateid;
|
||||
|
||||
trace_nfsd_deleg_return(stateid);
|
||||
wake_up_var(d_inode(cstate->current_fh.fh_dentry));
|
||||
destroy_delegation(dp);
|
||||
smp_mb__after_atomic();
|
||||
wake_up_var(d_inode(cstate->current_fh.fh_dentry));
|
||||
put_stateid:
|
||||
nfs4_put_stid(&dp->dl_stid);
|
||||
out:
|
||||
@ -8338,7 +8353,7 @@ out:
|
||||
* @cstate: NFSv4 COMPOUND state
|
||||
* @u: RELEASE_LOCKOWNER arguments
|
||||
*
|
||||
* Check if theree are any locks still held and if not - free the lockowner
|
||||
* Check if there are any locks still held and if not, free the lockowner
|
||||
* and any lock state that is owned.
|
||||
*
|
||||
* Return values:
|
||||
@ -8557,6 +8572,7 @@ static int nfs4_state_create_net(struct net *net)
|
||||
spin_lock_init(&nn->client_lock);
|
||||
spin_lock_init(&nn->s2s_cp_lock);
|
||||
idr_init(&nn->s2s_cp_stateids);
|
||||
atomic_set(&nn->pending_async_copies, 0);
|
||||
|
||||
spin_lock_init(&nn->blocked_locks_lock);
|
||||
INIT_LIST_HEAD(&nn->blocked_locks_lru);
|
||||
@ -8836,6 +8852,7 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
|
||||
__be32 status;
|
||||
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
|
||||
struct file_lock_context *ctx;
|
||||
struct nfs4_delegation *dp = NULL;
|
||||
struct file_lease *fl;
|
||||
struct iattr attrs;
|
||||
struct nfs4_cb_fattr *ncf;
|
||||
@ -8845,53 +8862,48 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
|
||||
ctx = locks_inode_context(inode);
|
||||
if (!ctx)
|
||||
return 0;
|
||||
|
||||
#define NON_NFSD_LEASE ((void *)1)
|
||||
|
||||
spin_lock(&ctx->flc_lock);
|
||||
for_each_file_lock(fl, &ctx->flc_lease) {
|
||||
unsigned char type = fl->c.flc_type;
|
||||
|
||||
if (fl->c.flc_flags == FL_LAYOUT)
|
||||
continue;
|
||||
if (fl->fl_lmops != &nfsd_lease_mng_ops) {
|
||||
/*
|
||||
* non-nfs lease, if it's a lease with F_RDLCK then
|
||||
* we are done; there isn't any write delegation
|
||||
* on this inode
|
||||
*/
|
||||
if (type == F_RDLCK)
|
||||
if (fl->c.flc_type == F_WRLCK) {
|
||||
if (fl->fl_lmops == &nfsd_lease_mng_ops)
|
||||
dp = fl->c.flc_owner;
|
||||
else
|
||||
dp = NON_NFSD_LEASE;
|
||||
}
|
||||
break;
|
||||
|
||||
nfsd_stats_wdeleg_getattr_inc(nn);
|
||||
}
|
||||
if (dp == NULL || dp == NON_NFSD_LEASE ||
|
||||
dp->dl_recall.cb_clp == *(rqstp->rq_lease_breaker)) {
|
||||
spin_unlock(&ctx->flc_lock);
|
||||
|
||||
status = nfserrno(nfsd_open_break_lease(inode, NFSD_MAY_READ));
|
||||
if (dp == NON_NFSD_LEASE) {
|
||||
status = nfserrno(nfsd_open_break_lease(inode,
|
||||
NFSD_MAY_READ));
|
||||
if (status != nfserr_jukebox ||
|
||||
!nfsd_wait_for_delegreturn(rqstp, inode))
|
||||
return status;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (type == F_WRLCK) {
|
||||
struct nfs4_delegation *dp = fl->c.flc_owner;
|
||||
|
||||
if (dp->dl_recall.cb_clp == *(rqstp->rq_lease_breaker)) {
|
||||
spin_unlock(&ctx->flc_lock);
|
||||
return 0;
|
||||
}
|
||||
nfsd_stats_wdeleg_getattr_inc(nn);
|
||||
dp = fl->c.flc_owner;
|
||||
refcount_inc(&dp->dl_stid.sc_count);
|
||||
ncf = &dp->dl_cb_fattr;
|
||||
nfs4_cb_getattr(&dp->dl_cb_fattr);
|
||||
spin_unlock(&ctx->flc_lock);
|
||||
|
||||
wait_on_bit_timeout(&ncf->ncf_cb_flags, CB_GETATTR_BUSY,
|
||||
TASK_INTERRUPTIBLE, NFSD_CB_GETATTR_TIMEOUT);
|
||||
if (ncf->ncf_cb_status) {
|
||||
/* Recall delegation only if client didn't respond */
|
||||
status = nfserrno(nfsd_open_break_lease(inode, NFSD_MAY_READ));
|
||||
if (status != nfserr_jukebox ||
|
||||
!nfsd_wait_for_delegreturn(rqstp, inode)) {
|
||||
nfs4_put_stid(&dp->dl_stid);
|
||||
return status;
|
||||
}
|
||||
!nfsd_wait_for_delegreturn(rqstp, inode))
|
||||
goto out_status;
|
||||
}
|
||||
if (!ncf->ncf_file_modified &&
|
||||
(ncf->ncf_initial_cinfo != ncf->ncf_cb_change ||
|
||||
@ -8911,18 +8923,15 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
|
||||
err = notify_change(&nop_mnt_idmap, dentry, &attrs, NULL);
|
||||
inode_unlock(inode);
|
||||
if (err) {
|
||||
nfs4_put_stid(&dp->dl_stid);
|
||||
return nfserrno(err);
|
||||
status = nfserrno(err);
|
||||
goto out_status;
|
||||
}
|
||||
ncf->ncf_cur_fsize = ncf->ncf_cb_fsize;
|
||||
*size = ncf->ncf_cur_fsize;
|
||||
*modified = true;
|
||||
}
|
||||
status = 0;
|
||||
out_status:
|
||||
nfs4_put_stid(&dp->dl_stid);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
spin_unlock(&ctx->flc_lock);
|
||||
return 0;
|
||||
return status;
|
||||
}
|
||||
|
@ -1245,14 +1245,6 @@ nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
|
||||
return nfs_ok;
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd4_decode_putpubfh(struct nfsd4_compoundargs *argp, union nfsd4_op_u *p)
|
||||
{
|
||||
if (argp->minorversion == 0)
|
||||
return nfs_ok;
|
||||
return nfserr_notsupp;
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd4_decode_read(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
|
||||
{
|
||||
@ -2374,7 +2366,7 @@ static const nfsd4_dec nfsd4_dec_ops[] = {
|
||||
[OP_OPEN_CONFIRM] = nfsd4_decode_open_confirm,
|
||||
[OP_OPEN_DOWNGRADE] = nfsd4_decode_open_downgrade,
|
||||
[OP_PUTFH] = nfsd4_decode_putfh,
|
||||
[OP_PUTPUBFH] = nfsd4_decode_putpubfh,
|
||||
[OP_PUTPUBFH] = nfsd4_decode_noop,
|
||||
[OP_PUTROOTFH] = nfsd4_decode_noop,
|
||||
[OP_READ] = nfsd4_decode_read,
|
||||
[OP_READDIR] = nfsd4_decode_readdir,
|
||||
@ -5731,6 +5723,23 @@ __be32 nfsd4_check_resp_size(struct nfsd4_compoundres *resp, u32 respsize)
|
||||
return nfserr_rep_too_big;
|
||||
}
|
||||
|
||||
static __be32 nfsd4_map_status(__be32 status, u32 minor)
|
||||
{
|
||||
switch (status) {
|
||||
case nfs_ok:
|
||||
break;
|
||||
case nfserr_wrong_type:
|
||||
/* RFC 8881 - 15.1.2.9 */
|
||||
if (minor == 0)
|
||||
status = nfserr_inval;
|
||||
break;
|
||||
case nfserr_symlink_not_dir:
|
||||
status = nfserr_symlink;
|
||||
break;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
void
|
||||
nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
|
||||
{
|
||||
@ -5798,6 +5807,8 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
|
||||
so->so_replay.rp_buf, len);
|
||||
}
|
||||
status:
|
||||
op->status = nfsd4_map_status(op->status,
|
||||
resp->cstate.minorversion);
|
||||
*p = op->status;
|
||||
release:
|
||||
if (opdesc && opdesc->op_release)
|
||||
|
@ -174,6 +174,13 @@ static int export_features_show(struct seq_file *m, void *v)
|
||||
|
||||
DEFINE_SHOW_ATTRIBUTE(export_features);
|
||||
|
||||
static int nfsd_pool_stats_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct nfsd_net *nn = net_generic(inode->i_sb->s_fs_info, nfsd_net_id);
|
||||
|
||||
return svc_pool_stats_open(&nn->nfsd_info, file);
|
||||
}
|
||||
|
||||
static const struct file_operations pool_stats_operations = {
|
||||
.open = nfsd_pool_stats_open,
|
||||
.read = seq_read,
|
||||
@ -1762,7 +1769,7 @@ int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
struct svc_pool *sp = &nn->nfsd_serv->sv_pools[i];
|
||||
|
||||
err = nla_put_u32(skb, NFSD_A_SERVER_THREADS,
|
||||
atomic_read(&sp->sp_nrthreads));
|
||||
sp->sp_nrthreads);
|
||||
if (err)
|
||||
goto err_unlock;
|
||||
}
|
||||
@ -2224,8 +2231,9 @@ err_free_msg:
|
||||
*/
|
||||
static __net_init int nfsd_net_init(struct net *net)
|
||||
{
|
||||
int retval;
|
||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||
int retval;
|
||||
int i;
|
||||
|
||||
retval = nfsd_export_init(net);
|
||||
if (retval)
|
||||
@ -2239,8 +2247,10 @@ static __net_init int nfsd_net_init(struct net *net)
|
||||
goto out_repcache_error;
|
||||
memset(&nn->nfsd_svcstats, 0, sizeof(nn->nfsd_svcstats));
|
||||
nn->nfsd_svcstats.program = &nfsd_program;
|
||||
nn->nfsd_versions = NULL;
|
||||
nn->nfsd4_minorversions = NULL;
|
||||
for (i = 0; i < sizeof(nn->nfsd_versions); i++)
|
||||
nn->nfsd_versions[i] = nfsd_support_version(i);
|
||||
for (i = 0; i < sizeof(nn->nfsd4_minorversions); i++)
|
||||
nn->nfsd4_minorversions[i] = nfsd_support_version(4);
|
||||
nn->nfsd_info.mutex = &nfsd_mutex;
|
||||
nn->nfsd_serv = NULL;
|
||||
nfsd4_init_leases_net(nn);
|
||||
@ -2271,7 +2281,6 @@ static __net_exit void nfsd_net_exit(struct net *net)
|
||||
percpu_counter_destroy_many(nn->counter, NFSD_STATS_COUNTERS_NUM);
|
||||
nfsd_idmap_shutdown(net);
|
||||
nfsd_export_shutdown(net);
|
||||
nfsd_netns_free_versions(nn);
|
||||
}
|
||||
|
||||
static struct pernet_operations nfsd_net_ops = {
|
||||
|
@ -23,9 +23,7 @@
|
||||
|
||||
#include <uapi/linux/nfsd/debug.h>
|
||||
|
||||
#include "netns.h"
|
||||
#include "export.h"
|
||||
#include "stats.h"
|
||||
|
||||
#undef ifdebug
|
||||
#ifdef CONFIG_SUNRPC_DEBUG
|
||||
@ -37,7 +35,14 @@
|
||||
/*
|
||||
* nfsd version
|
||||
*/
|
||||
#define NFSD_MINVERS 2
|
||||
#define NFSD_MAXVERS 4
|
||||
#define NFSD_SUPPORTED_MINOR_VERSION 2
|
||||
bool nfsd_support_version(int vers);
|
||||
|
||||
#include "netns.h"
|
||||
#include "stats.h"
|
||||
|
||||
/*
|
||||
* Maximum blocksizes supported by daemon under various circumstances.
|
||||
*/
|
||||
@ -111,11 +116,9 @@ int nfsd_nrthreads(struct net *);
|
||||
int nfsd_nrpools(struct net *);
|
||||
int nfsd_get_nrthreads(int n, int *, struct net *);
|
||||
int nfsd_set_nrthreads(int n, int *, struct net *);
|
||||
int nfsd_pool_stats_open(struct inode *, struct file *);
|
||||
int nfsd_pool_stats_release(struct inode *, struct file *);
|
||||
void nfsd_shutdown_threads(struct net *net);
|
||||
|
||||
bool i_am_nfsd(void);
|
||||
struct svc_rqst *nfsd_current_rqst(void);
|
||||
|
||||
struct nfsdfs_client {
|
||||
struct kref cl_ref;
|
||||
@ -156,7 +159,7 @@ extern int nfsd_max_blksize;
|
||||
|
||||
static inline int nfsd_v4client(struct svc_rqst *rq)
|
||||
{
|
||||
return rq->rq_prog == NFS_PROGRAM && rq->rq_vers == 4;
|
||||
return rq && rq->rq_prog == NFS_PROGRAM && rq->rq_vers == 4;
|
||||
}
|
||||
static inline struct user_namespace *
|
||||
nfsd_user_namespace(const struct svc_rqst *rqstp)
|
||||
@ -327,17 +330,36 @@ void nfsd_lockd_shutdown(void);
|
||||
#define nfserr_xattr2big cpu_to_be32(NFS4ERR_XATTR2BIG)
|
||||
#define nfserr_noxattr cpu_to_be32(NFS4ERR_NOXATTR)
|
||||
|
||||
/* error codes for internal use */
|
||||
/*
|
||||
* Error codes for internal use. We use enum to choose numbers that are
|
||||
* not already assigned, then covert to be32 resulting in a number that
|
||||
* cannot conflict with any existing be32 nfserr value.
|
||||
*/
|
||||
enum {
|
||||
NFSERR_DROPIT = NFS4ERR_FIRST_FREE,
|
||||
/* if a request fails due to kmalloc failure, it gets dropped.
|
||||
* Client should resend eventually
|
||||
*/
|
||||
#define nfserr_dropit cpu_to_be32(30000)
|
||||
#define nfserr_dropit cpu_to_be32(NFSERR_DROPIT)
|
||||
|
||||
/* end-of-file indicator in readdir */
|
||||
#define nfserr_eof cpu_to_be32(30001)
|
||||
NFSERR_EOF,
|
||||
#define nfserr_eof cpu_to_be32(NFSERR_EOF)
|
||||
|
||||
/* replay detected */
|
||||
#define nfserr_replay_me cpu_to_be32(11001)
|
||||
NFSERR_REPLAY_ME,
|
||||
#define nfserr_replay_me cpu_to_be32(NFSERR_REPLAY_ME)
|
||||
|
||||
/* nfs41 replay detected */
|
||||
#define nfserr_replay_cache cpu_to_be32(11002)
|
||||
NFSERR_REPLAY_CACHE,
|
||||
#define nfserr_replay_cache cpu_to_be32(NFSERR_REPLAY_CACHE)
|
||||
|
||||
/* symlink found where dir expected - handled differently to
|
||||
* other symlink found errors by NFSv3.
|
||||
*/
|
||||
NFSERR_SYMLINK_NOT_DIR,
|
||||
#define nfserr_symlink_not_dir cpu_to_be32(NFSERR_SYMLINK_NOT_DIR)
|
||||
};
|
||||
|
||||
/* Check for dir entries '.' and '..' */
|
||||
#define isdotent(n, l) (l < 3 && n[0] == '.' && (l == 1 || n[1] == '.'))
|
||||
|
@ -62,8 +62,7 @@ static int nfsd_acceptable(void *expv, struct dentry *dentry)
|
||||
* the write call).
|
||||
*/
|
||||
static inline __be32
|
||||
nfsd_mode_check(struct svc_rqst *rqstp, struct dentry *dentry,
|
||||
umode_t requested)
|
||||
nfsd_mode_check(struct dentry *dentry, umode_t requested)
|
||||
{
|
||||
umode_t mode = d_inode(dentry)->i_mode & S_IFMT;
|
||||
|
||||
@ -76,17 +75,16 @@ nfsd_mode_check(struct svc_rqst *rqstp, struct dentry *dentry,
|
||||
}
|
||||
return nfs_ok;
|
||||
}
|
||||
/*
|
||||
* v4 has an error more specific than err_notdir which we should
|
||||
* return in preference to err_notdir:
|
||||
*/
|
||||
if (rqstp->rq_vers == 4 && mode == S_IFLNK)
|
||||
if (mode == S_IFLNK) {
|
||||
if (requested == S_IFDIR)
|
||||
return nfserr_symlink_not_dir;
|
||||
return nfserr_symlink;
|
||||
}
|
||||
if (requested == S_IFDIR)
|
||||
return nfserr_notdir;
|
||||
if (mode == S_IFDIR)
|
||||
return nfserr_isdir;
|
||||
return nfserr_inval;
|
||||
return nfserr_wrong_type;
|
||||
}
|
||||
|
||||
static bool nfsd_originating_port_ok(struct svc_rqst *rqstp, int flags)
|
||||
@ -102,7 +100,7 @@ static bool nfsd_originating_port_ok(struct svc_rqst *rqstp, int flags)
|
||||
static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp,
|
||||
struct svc_export *exp)
|
||||
{
|
||||
int flags = nfsexp_flags(rqstp, exp);
|
||||
int flags = nfsexp_flags(&rqstp->rq_cred, exp);
|
||||
|
||||
/* Check if the request originated from a secure port. */
|
||||
if (!nfsd_originating_port_ok(rqstp, flags)) {
|
||||
@ -113,22 +111,14 @@ static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp,
|
||||
}
|
||||
|
||||
/* Set user creds for this exportpoint */
|
||||
return nfserrno(nfsd_setuser(rqstp, exp));
|
||||
return nfserrno(nfsd_setuser(&rqstp->rq_cred, exp));
|
||||
}
|
||||
|
||||
static inline __be32 check_pseudo_root(struct svc_rqst *rqstp,
|
||||
struct dentry *dentry, struct svc_export *exp)
|
||||
static inline __be32 check_pseudo_root(struct dentry *dentry,
|
||||
struct svc_export *exp)
|
||||
{
|
||||
if (!(exp->ex_flags & NFSEXP_V4ROOT))
|
||||
return nfs_ok;
|
||||
/*
|
||||
* v2/v3 clients have no need for the V4ROOT export--they use
|
||||
* the mount protocl instead; also, further V4ROOT checks may be
|
||||
* in v4-specific code, in which case v2/v3 clients could bypass
|
||||
* them.
|
||||
*/
|
||||
if (!nfsd_v4client(rqstp))
|
||||
return nfserr_stale;
|
||||
/*
|
||||
* We're exposing only the directories and symlinks that have to be
|
||||
* traversed on the way to real exports:
|
||||
@ -162,10 +152,8 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
|
||||
int len;
|
||||
__be32 error;
|
||||
|
||||
error = nfserr_stale;
|
||||
if (rqstp->rq_vers > 2)
|
||||
error = nfserr_badhandle;
|
||||
if (rqstp->rq_vers == 4 && fh->fh_size == 0)
|
||||
if (fh->fh_size == 0)
|
||||
return nfserr_nofilehandle;
|
||||
|
||||
if (fh->fh_version != 1)
|
||||
@ -195,7 +183,9 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
|
||||
data_left -= len;
|
||||
if (data_left < 0)
|
||||
return error;
|
||||
exp = rqst_exp_find(rqstp, fh->fh_fsid_type, fh->fh_fsid);
|
||||
exp = rqst_exp_find(&rqstp->rq_chandle, SVC_NET(rqstp),
|
||||
rqstp->rq_client, rqstp->rq_gssclient,
|
||||
fh->fh_fsid_type, fh->fh_fsid);
|
||||
fid = (struct fid *)(fh->fh_fsid + len);
|
||||
|
||||
error = nfserr_stale;
|
||||
@ -237,8 +227,6 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
|
||||
/*
|
||||
* Look up the dentry using the NFS file handle.
|
||||
*/
|
||||
error = nfserr_stale;
|
||||
if (rqstp->rq_vers > 2)
|
||||
error = nfserr_badhandle;
|
||||
|
||||
fileid_type = fh->fh_fileid_type;
|
||||
@ -282,13 +270,21 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
|
||||
case 4:
|
||||
if (dentry->d_sb->s_export_op->flags & EXPORT_OP_NOATOMIC_ATTR)
|
||||
fhp->fh_no_atomic_attr = true;
|
||||
fhp->fh_64bit_cookies = true;
|
||||
break;
|
||||
case 3:
|
||||
if (dentry->d_sb->s_export_op->flags & EXPORT_OP_NOWCC)
|
||||
fhp->fh_no_wcc = true;
|
||||
fhp->fh_64bit_cookies = true;
|
||||
if (exp->ex_flags & NFSEXP_V4ROOT)
|
||||
goto out;
|
||||
break;
|
||||
case 2:
|
||||
fhp->fh_no_wcc = true;
|
||||
if (EX_WGATHER(exp))
|
||||
fhp->fh_use_wgather = true;
|
||||
if (exp->ex_flags & NFSEXP_V4ROOT)
|
||||
goto out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -358,7 +354,7 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, int access)
|
||||
* (for example, if different id-squashing options are in
|
||||
* effect on the new filesystem).
|
||||
*/
|
||||
error = check_pseudo_root(rqstp, dentry, exp);
|
||||
error = check_pseudo_root(dentry, exp);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
@ -366,7 +362,7 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, int access)
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
error = nfsd_mode_check(rqstp, dentry, type);
|
||||
error = nfsd_mode_check(dentry, type);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
@ -392,7 +388,7 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, int access)
|
||||
|
||||
skip_pseudoflavor_check:
|
||||
/* Finally, check access permissions. */
|
||||
error = nfsd_permission(rqstp, exp, dentry, access);
|
||||
error = nfsd_permission(&rqstp->rq_cred, exp, dentry, access);
|
||||
out:
|
||||
trace_nfsd_fh_verify_err(rqstp, fhp, type, access, error);
|
||||
if (error == nfserr_stale)
|
||||
|
@ -88,6 +88,8 @@ typedef struct svc_fh {
|
||||
* wcc data is not atomic with
|
||||
* operation
|
||||
*/
|
||||
bool fh_use_wgather; /* NFSv2 wgather option */
|
||||
bool fh_64bit_cookies;/* readdir cookie size */
|
||||
int fh_flags; /* FH flags */
|
||||
bool fh_post_saved; /* post-op attrs saved */
|
||||
bool fh_pre_saved; /* pre-op attrs saved */
|
||||
|
@ -13,6 +13,31 @@
|
||||
|
||||
#define NFSDDBG_FACILITY NFSDDBG_PROC
|
||||
|
||||
static __be32 nfsd_map_status(__be32 status)
|
||||
{
|
||||
switch (status) {
|
||||
case nfs_ok:
|
||||
break;
|
||||
case nfserr_nofilehandle:
|
||||
case nfserr_badhandle:
|
||||
status = nfserr_stale;
|
||||
break;
|
||||
case nfserr_wrongsec:
|
||||
case nfserr_xdev:
|
||||
case nfserr_file_open:
|
||||
status = nfserr_acces;
|
||||
break;
|
||||
case nfserr_symlink_not_dir:
|
||||
status = nfserr_notdir;
|
||||
break;
|
||||
case nfserr_symlink:
|
||||
case nfserr_wrong_type:
|
||||
status = nfserr_inval;
|
||||
break;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd_proc_null(struct svc_rqst *rqstp)
|
||||
{
|
||||
@ -38,6 +63,7 @@ nfsd_proc_getattr(struct svc_rqst *rqstp)
|
||||
goto out;
|
||||
resp->status = fh_getattr(&resp->fh, &resp->stat);
|
||||
out:
|
||||
resp->status = nfsd_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -109,6 +135,7 @@ nfsd_proc_setattr(struct svc_rqst *rqstp)
|
||||
|
||||
resp->status = fh_getattr(&resp->fh, &resp->stat);
|
||||
out:
|
||||
resp->status = nfsd_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -143,6 +170,7 @@ nfsd_proc_lookup(struct svc_rqst *rqstp)
|
||||
|
||||
resp->status = fh_getattr(&resp->fh, &resp->stat);
|
||||
out:
|
||||
resp->status = nfsd_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -164,6 +192,7 @@ nfsd_proc_readlink(struct svc_rqst *rqstp)
|
||||
page_address(resp->page), &resp->len);
|
||||
|
||||
fh_put(&argp->fh);
|
||||
resp->status = nfsd_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -200,6 +229,7 @@ nfsd_proc_read(struct svc_rqst *rqstp)
|
||||
resp->status = fh_getattr(&resp->fh, &resp->stat);
|
||||
else if (resp->status == nfserr_jukebox)
|
||||
set_bit(RQ_DROPME, &rqstp->rq_flags);
|
||||
resp->status = nfsd_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -235,6 +265,7 @@ nfsd_proc_write(struct svc_rqst *rqstp)
|
||||
resp->status = fh_getattr(&resp->fh, &resp->stat);
|
||||
else if (resp->status == nfserr_jukebox)
|
||||
set_bit(RQ_DROPME, &rqstp->rq_flags);
|
||||
resp->status = nfsd_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -331,7 +362,8 @@ nfsd_proc_create(struct svc_rqst *rqstp)
|
||||
* echo thing > device-special-file-or-pipe
|
||||
* by doing a CREATE with type==0
|
||||
*/
|
||||
resp->status = nfsd_permission(rqstp,
|
||||
resp->status = nfsd_permission(
|
||||
&rqstp->rq_cred,
|
||||
newfhp->fh_export,
|
||||
newfhp->fh_dentry,
|
||||
NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS);
|
||||
@ -403,6 +435,7 @@ done:
|
||||
goto out;
|
||||
resp->status = fh_getattr(&resp->fh, &resp->stat);
|
||||
out:
|
||||
resp->status = nfsd_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -419,6 +452,7 @@ nfsd_proc_remove(struct svc_rqst *rqstp)
|
||||
resp->status = nfsd_unlink(rqstp, &argp->fh, -S_IFDIR,
|
||||
argp->name, argp->len);
|
||||
fh_put(&argp->fh);
|
||||
resp->status = nfsd_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -437,6 +471,7 @@ nfsd_proc_rename(struct svc_rqst *rqstp)
|
||||
&argp->tfh, argp->tname, argp->tlen);
|
||||
fh_put(&argp->ffh);
|
||||
fh_put(&argp->tfh);
|
||||
resp->status = nfsd_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -457,6 +492,7 @@ nfsd_proc_link(struct svc_rqst *rqstp)
|
||||
&argp->ffh);
|
||||
fh_put(&argp->ffh);
|
||||
fh_put(&argp->tfh);
|
||||
resp->status = nfsd_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -495,6 +531,7 @@ nfsd_proc_symlink(struct svc_rqst *rqstp)
|
||||
fh_put(&argp->ffh);
|
||||
fh_put(&newfh);
|
||||
out:
|
||||
resp->status = nfsd_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -528,6 +565,7 @@ nfsd_proc_mkdir(struct svc_rqst *rqstp)
|
||||
|
||||
resp->status = fh_getattr(&resp->fh, &resp->stat);
|
||||
out:
|
||||
resp->status = nfsd_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -545,6 +583,7 @@ nfsd_proc_rmdir(struct svc_rqst *rqstp)
|
||||
resp->status = nfsd_unlink(rqstp, &argp->fh, S_IFDIR,
|
||||
argp->name, argp->len);
|
||||
fh_put(&argp->fh);
|
||||
resp->status = nfsd_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -590,6 +629,7 @@ nfsd_proc_readdir(struct svc_rqst *rqstp)
|
||||
nfssvc_encode_nfscookie(resp, offset);
|
||||
|
||||
fh_put(&argp->fh);
|
||||
resp->status = nfsd_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -607,6 +647,7 @@ nfsd_proc_statfs(struct svc_rqst *rqstp)
|
||||
resp->status = nfsd_statfs(rqstp, &argp->fh, &resp->stats,
|
||||
NFSD_MAY_BYPASS_GSS_ON_ROOT);
|
||||
fh_put(&argp->fh);
|
||||
resp->status = nfsd_map_status(resp->status);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
|
108
fs/nfsd/nfssvc.c
108
fs/nfsd/nfssvc.c
@ -106,7 +106,7 @@ static struct svc_program nfsd_acl_program = {
|
||||
|
||||
#endif /* defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) */
|
||||
|
||||
static const struct svc_version *nfsd_version[] = {
|
||||
static const struct svc_version *nfsd_version[NFSD_MAXVERS+1] = {
|
||||
#if defined(CONFIG_NFSD_V2)
|
||||
[2] = &nfsd_version2,
|
||||
#endif
|
||||
@ -116,15 +116,12 @@ static const struct svc_version *nfsd_version[] = {
|
||||
#endif
|
||||
};
|
||||
|
||||
#define NFSD_MINVERS 2
|
||||
#define NFSD_NRVERS ARRAY_SIZE(nfsd_version)
|
||||
|
||||
struct svc_program nfsd_program = {
|
||||
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
|
||||
.pg_next = &nfsd_acl_program,
|
||||
#endif
|
||||
.pg_prog = NFS_PROGRAM, /* program number */
|
||||
.pg_nvers = NFSD_NRVERS, /* nr of entries in nfsd_version */
|
||||
.pg_nvers = NFSD_MAXVERS+1, /* nr of entries in nfsd_version */
|
||||
.pg_vers = nfsd_version, /* version table */
|
||||
.pg_name = "nfsd", /* program name */
|
||||
.pg_class = "nfsd", /* authentication class */
|
||||
@ -135,78 +132,24 @@ struct svc_program nfsd_program = {
|
||||
|
||||
bool nfsd_support_version(int vers)
|
||||
{
|
||||
if (vers >= NFSD_MINVERS && vers < NFSD_NRVERS)
|
||||
if (vers >= NFSD_MINVERS && vers <= NFSD_MAXVERS)
|
||||
return nfsd_version[vers] != NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool *
|
||||
nfsd_alloc_versions(void)
|
||||
{
|
||||
bool *vers = kmalloc_array(NFSD_NRVERS, sizeof(bool), GFP_KERNEL);
|
||||
unsigned i;
|
||||
|
||||
if (vers) {
|
||||
/* All compiled versions are enabled by default */
|
||||
for (i = 0; i < NFSD_NRVERS; i++)
|
||||
vers[i] = nfsd_support_version(i);
|
||||
}
|
||||
return vers;
|
||||
}
|
||||
|
||||
static bool *
|
||||
nfsd_alloc_minorversions(void)
|
||||
{
|
||||
bool *vers = kmalloc_array(NFSD_SUPPORTED_MINOR_VERSION + 1,
|
||||
sizeof(bool), GFP_KERNEL);
|
||||
unsigned i;
|
||||
|
||||
if (vers) {
|
||||
/* All minor versions are enabled by default */
|
||||
for (i = 0; i <= NFSD_SUPPORTED_MINOR_VERSION; i++)
|
||||
vers[i] = nfsd_support_version(4);
|
||||
}
|
||||
return vers;
|
||||
}
|
||||
|
||||
void
|
||||
nfsd_netns_free_versions(struct nfsd_net *nn)
|
||||
{
|
||||
kfree(nn->nfsd_versions);
|
||||
kfree(nn->nfsd4_minorversions);
|
||||
nn->nfsd_versions = NULL;
|
||||
nn->nfsd4_minorversions = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
nfsd_netns_init_versions(struct nfsd_net *nn)
|
||||
{
|
||||
if (!nn->nfsd_versions) {
|
||||
nn->nfsd_versions = nfsd_alloc_versions();
|
||||
nn->nfsd4_minorversions = nfsd_alloc_minorversions();
|
||||
if (!nn->nfsd_versions || !nn->nfsd4_minorversions)
|
||||
nfsd_netns_free_versions(nn);
|
||||
}
|
||||
}
|
||||
|
||||
int nfsd_vers(struct nfsd_net *nn, int vers, enum vers_op change)
|
||||
{
|
||||
if (vers < NFSD_MINVERS || vers >= NFSD_NRVERS)
|
||||
if (vers < NFSD_MINVERS || vers > NFSD_MAXVERS)
|
||||
return 0;
|
||||
switch(change) {
|
||||
case NFSD_SET:
|
||||
if (nn->nfsd_versions)
|
||||
nn->nfsd_versions[vers] = nfsd_support_version(vers);
|
||||
break;
|
||||
case NFSD_CLEAR:
|
||||
nfsd_netns_init_versions(nn);
|
||||
if (nn->nfsd_versions)
|
||||
nn->nfsd_versions[vers] = false;
|
||||
break;
|
||||
case NFSD_TEST:
|
||||
if (nn->nfsd_versions)
|
||||
return nn->nfsd_versions[vers];
|
||||
fallthrough;
|
||||
case NFSD_AVAIL:
|
||||
return nfsd_support_version(vers);
|
||||
}
|
||||
@ -233,23 +176,16 @@ int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change
|
||||
|
||||
switch(change) {
|
||||
case NFSD_SET:
|
||||
if (nn->nfsd4_minorversions) {
|
||||
nfsd_vers(nn, 4, NFSD_SET);
|
||||
nn->nfsd4_minorversions[minorversion] =
|
||||
nfsd_vers(nn, 4, NFSD_TEST);
|
||||
}
|
||||
break;
|
||||
case NFSD_CLEAR:
|
||||
nfsd_netns_init_versions(nn);
|
||||
if (nn->nfsd4_minorversions) {
|
||||
nn->nfsd4_minorversions[minorversion] = false;
|
||||
nfsd_adjust_nfsd_versions4(nn);
|
||||
}
|
||||
break;
|
||||
case NFSD_TEST:
|
||||
if (nn->nfsd4_minorversions)
|
||||
return nn->nfsd4_minorversions[minorversion];
|
||||
return nfsd_vers(nn, 4, NFSD_TEST);
|
||||
case NFSD_AVAIL:
|
||||
return minorversion <= NFSD_SUPPORTED_MINOR_VERSION &&
|
||||
nfsd_vers(nn, 4, NFSD_AVAIL);
|
||||
@ -568,11 +504,11 @@ void nfsd_reset_versions(struct nfsd_net *nn)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NFSD_NRVERS; i++)
|
||||
for (i = 0; i <= NFSD_MAXVERS; i++)
|
||||
if (nfsd_vers(nn, i, NFSD_TEST))
|
||||
return;
|
||||
|
||||
for (i = 0; i < NFSD_NRVERS; i++)
|
||||
for (i = 0; i <= NFSD_MAXVERS; i++)
|
||||
if (i != 4)
|
||||
nfsd_vers(nn, i, NFSD_SET);
|
||||
else {
|
||||
@ -642,9 +578,11 @@ void nfsd_shutdown_threads(struct net *net)
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
}
|
||||
|
||||
bool i_am_nfsd(void)
|
||||
struct svc_rqst *nfsd_current_rqst(void)
|
||||
{
|
||||
return kthread_func(current) == nfsd;
|
||||
if (kthread_func(current) == nfsd)
|
||||
return kthread_data(current);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int nfsd_create_serv(struct net *net)
|
||||
@ -705,7 +643,7 @@ int nfsd_get_nrthreads(int n, int *nthreads, struct net *net)
|
||||
|
||||
if (serv)
|
||||
for (i = 0; i < serv->sv_nrpools && i < n; i++)
|
||||
nthreads[i] = atomic_read(&serv->sv_pools[i].sp_nrthreads);
|
||||
nthreads[i] = serv->sv_pools[i].sp_nrthreads;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -905,17 +843,17 @@ nfsd_init_request(struct svc_rqst *rqstp,
|
||||
if (likely(nfsd_vers(nn, rqstp->rq_vers, NFSD_TEST)))
|
||||
return svc_generic_init_request(rqstp, progp, ret);
|
||||
|
||||
ret->mismatch.lovers = NFSD_NRVERS;
|
||||
for (i = NFSD_MINVERS; i < NFSD_NRVERS; i++) {
|
||||
ret->mismatch.lovers = NFSD_MAXVERS + 1;
|
||||
for (i = NFSD_MINVERS; i <= NFSD_MAXVERS; i++) {
|
||||
if (nfsd_vers(nn, i, NFSD_TEST)) {
|
||||
ret->mismatch.lovers = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ret->mismatch.lovers == NFSD_NRVERS)
|
||||
if (ret->mismatch.lovers > NFSD_MAXVERS)
|
||||
return rpc_prog_unavail;
|
||||
ret->mismatch.hivers = NFSD_MINVERS;
|
||||
for (i = NFSD_NRVERS - 1; i >= NFSD_MINVERS; i--) {
|
||||
for (i = NFSD_MAXVERS; i >= NFSD_MINVERS; i--) {
|
||||
if (nfsd_vers(nn, i, NFSD_TEST)) {
|
||||
ret->mismatch.hivers = i;
|
||||
break;
|
||||
@ -937,11 +875,9 @@ nfsd(void *vrqstp)
|
||||
|
||||
/* At this point, the thread shares current->fs
|
||||
* with the init process. We need to create files with the
|
||||
* umask as defined by the client instead of init's umask. */
|
||||
if (unshare_fs_struct() < 0) {
|
||||
printk("Unable to start nfsd thread: out of memory\n");
|
||||
goto out;
|
||||
}
|
||||
* umask as defined by the client instead of init's umask.
|
||||
*/
|
||||
svc_thread_init_status(rqstp, unshare_fs_struct());
|
||||
|
||||
current->fs->umask = 0;
|
||||
|
||||
@ -963,7 +899,6 @@ nfsd(void *vrqstp)
|
||||
|
||||
atomic_dec(&nfsd_th_cnt);
|
||||
|
||||
out:
|
||||
/* Release the thread */
|
||||
svc_exit_thread(rqstp);
|
||||
return 0;
|
||||
@ -1084,10 +1019,3 @@ bool nfssvc_encode_voidres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int nfsd_pool_stats_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct nfsd_net *nn = net_generic(inode->i_sb->s_fs_info, nfsd_net_id);
|
||||
|
||||
return svc_pool_stats_open(&nn->nfsd_info, file);
|
||||
}
|
||||
|
@ -79,6 +79,7 @@ struct nfsd4_callback_ops {
|
||||
void (*prepare)(struct nfsd4_callback *);
|
||||
int (*done)(struct nfsd4_callback *, struct rpc_task *);
|
||||
void (*release)(struct nfsd4_callback *);
|
||||
uint32_t opcode;
|
||||
};
|
||||
|
||||
/*
|
||||
|
124
fs/nfsd/trace.h
124
fs/nfsd/trace.h
@ -1486,6 +1486,9 @@ DEFINE_NFSD_CB_EVENT(new_state);
|
||||
DEFINE_NFSD_CB_EVENT(probe);
|
||||
DEFINE_NFSD_CB_EVENT(lost);
|
||||
DEFINE_NFSD_CB_EVENT(shutdown);
|
||||
DEFINE_NFSD_CB_EVENT(rpc_prepare);
|
||||
DEFINE_NFSD_CB_EVENT(rpc_done);
|
||||
DEFINE_NFSD_CB_EVENT(rpc_release);
|
||||
|
||||
TRACE_DEFINE_ENUM(RPC_AUTH_NULL);
|
||||
TRACE_DEFINE_ENUM(RPC_AUTH_UNIX);
|
||||
@ -1553,6 +1556,19 @@ TRACE_EVENT(nfsd_cb_setup_err,
|
||||
__entry->error)
|
||||
);
|
||||
|
||||
/* Not a real opcode, but there is no 0 operation. */
|
||||
#define _CB_NULL 0
|
||||
|
||||
#define show_nfsd_cb_opcode(val) \
|
||||
__print_symbolic(val, \
|
||||
{ _CB_NULL, "CB_NULL" }, \
|
||||
{ OP_CB_GETATTR, "CB_GETATTR" }, \
|
||||
{ OP_CB_RECALL, "CB_RECALL" }, \
|
||||
{ OP_CB_LAYOUTRECALL, "CB_LAYOUTRECALL" }, \
|
||||
{ OP_CB_RECALL_ANY, "CB_RECALL_ANY" }, \
|
||||
{ OP_CB_NOTIFY_LOCK, "CB_NOTIFY_LOCK" }, \
|
||||
{ OP_CB_OFFLOAD, "CB_OFFLOAD" })
|
||||
|
||||
DECLARE_EVENT_CLASS(nfsd_cb_lifetime_class,
|
||||
TP_PROTO(
|
||||
const struct nfs4_client *clp,
|
||||
@ -1563,6 +1579,7 @@ DECLARE_EVENT_CLASS(nfsd_cb_lifetime_class,
|
||||
__field(u32, cl_boot)
|
||||
__field(u32, cl_id)
|
||||
__field(const void *, cb)
|
||||
__field(unsigned long, opcode)
|
||||
__field(bool, need_restart)
|
||||
__sockaddr(addr, clp->cl_cb_conn.cb_addrlen)
|
||||
),
|
||||
@ -1570,14 +1587,15 @@ DECLARE_EVENT_CLASS(nfsd_cb_lifetime_class,
|
||||
__entry->cl_boot = clp->cl_clientid.cl_boot;
|
||||
__entry->cl_id = clp->cl_clientid.cl_id;
|
||||
__entry->cb = cb;
|
||||
__entry->opcode = cb->cb_ops ? cb->cb_ops->opcode : _CB_NULL;
|
||||
__entry->need_restart = cb->cb_need_restart;
|
||||
__assign_sockaddr(addr, &clp->cl_cb_conn.cb_addr,
|
||||
clp->cl_cb_conn.cb_addrlen)
|
||||
),
|
||||
TP_printk("addr=%pISpc client %08x:%08x cb=%p%s",
|
||||
__get_sockaddr(addr), __entry->cl_boot, __entry->cl_id,
|
||||
__entry->cb, __entry->need_restart ?
|
||||
" (need restart)" : " (first try)"
|
||||
TP_printk("addr=%pISpc client %08x:%08x cb=%p%s opcode=%s",
|
||||
__get_sockaddr(addr), __entry->cl_boot, __entry->cl_id, __entry->cb,
|
||||
__entry->need_restart ? " (need restart)" : " (first try)",
|
||||
show_nfsd_cb_opcode(__entry->opcode)
|
||||
)
|
||||
);
|
||||
|
||||
@ -1830,6 +1848,7 @@ DEFINE_NFSD_CB_DONE_EVENT(nfsd_cb_recall_done);
|
||||
DEFINE_NFSD_CB_DONE_EVENT(nfsd_cb_notify_lock_done);
|
||||
DEFINE_NFSD_CB_DONE_EVENT(nfsd_cb_layout_done);
|
||||
DEFINE_NFSD_CB_DONE_EVENT(nfsd_cb_offload_done);
|
||||
DEFINE_NFSD_CB_DONE_EVENT(nfsd_cb_getattr_done);
|
||||
|
||||
TRACE_EVENT(nfsd_cb_recall_any_done,
|
||||
TP_PROTO(
|
||||
@ -2127,6 +2146,10 @@ DECLARE_EVENT_CLASS(nfsd_copy_class,
|
||||
__field(u32, dst_cl_id)
|
||||
__field(u32, dst_so_id)
|
||||
__field(u32, dst_si_generation)
|
||||
__field(u32, cb_cl_boot)
|
||||
__field(u32, cb_cl_id)
|
||||
__field(u32, cb_so_id)
|
||||
__field(u32, cb_si_generation)
|
||||
__field(u64, src_cp_pos)
|
||||
__field(u64, dst_cp_pos)
|
||||
__field(u64, cp_count)
|
||||
@ -2135,6 +2158,7 @@ DECLARE_EVENT_CLASS(nfsd_copy_class,
|
||||
TP_fast_assign(
|
||||
const stateid_t *src_stp = ©->cp_src_stateid;
|
||||
const stateid_t *dst_stp = ©->cp_dst_stateid;
|
||||
const stateid_t *cb_stp = ©->cp_res.cb_stateid;
|
||||
|
||||
__entry->intra = test_bit(NFSD4_COPY_F_INTRA, ©->cp_flags);
|
||||
__entry->async = !test_bit(NFSD4_COPY_F_SYNCHRONOUS, ©->cp_flags);
|
||||
@ -2146,6 +2170,10 @@ DECLARE_EVENT_CLASS(nfsd_copy_class,
|
||||
__entry->dst_cl_id = dst_stp->si_opaque.so_clid.cl_id;
|
||||
__entry->dst_so_id = dst_stp->si_opaque.so_id;
|
||||
__entry->dst_si_generation = dst_stp->si_generation;
|
||||
__entry->cb_cl_boot = cb_stp->si_opaque.so_clid.cl_boot;
|
||||
__entry->cb_cl_id = cb_stp->si_opaque.so_clid.cl_id;
|
||||
__entry->cb_so_id = cb_stp->si_opaque.so_id;
|
||||
__entry->cb_si_generation = cb_stp->si_generation;
|
||||
__entry->src_cp_pos = copy->cp_src_pos;
|
||||
__entry->dst_cp_pos = copy->cp_dst_pos;
|
||||
__entry->cp_count = copy->cp_count;
|
||||
@ -2153,14 +2181,17 @@ DECLARE_EVENT_CLASS(nfsd_copy_class,
|
||||
sizeof(struct sockaddr_in6));
|
||||
),
|
||||
TP_printk("client=%pISpc intra=%d async=%d "
|
||||
"src_stateid[si_generation:0x%x cl_boot:0x%x cl_id:0x%x so_id:0x%x] "
|
||||
"dst_stateid[si_generation:0x%x cl_boot:0x%x cl_id:0x%x so_id:0x%x] "
|
||||
"src_client %08x:%08x src_stateid %08x:%08x "
|
||||
"dst_client %08x:%08x dst_stateid %08x:%08x "
|
||||
"cb_client %08x:%08x cb_stateid %08x:%08x "
|
||||
"cp_src_pos=%llu cp_dst_pos=%llu cp_count=%llu",
|
||||
__get_sockaddr(addr), __entry->intra, __entry->async,
|
||||
__entry->src_si_generation, __entry->src_cl_boot,
|
||||
__entry->src_cl_id, __entry->src_so_id,
|
||||
__entry->dst_si_generation, __entry->dst_cl_boot,
|
||||
__entry->dst_cl_id, __entry->dst_so_id,
|
||||
__entry->src_cl_boot, __entry->src_cl_id,
|
||||
__entry->src_so_id, __entry->src_si_generation,
|
||||
__entry->dst_cl_boot, __entry->dst_cl_id,
|
||||
__entry->dst_so_id, __entry->dst_si_generation,
|
||||
__entry->cb_cl_boot, __entry->cb_cl_id,
|
||||
__entry->cb_so_id, __entry->cb_si_generation,
|
||||
__entry->src_cp_pos, __entry->dst_cp_pos, __entry->cp_count
|
||||
)
|
||||
);
|
||||
@ -2172,7 +2203,7 @@ DEFINE_EVENT(nfsd_copy_class, nfsd_copy_##name, \
|
||||
|
||||
DEFINE_COPY_EVENT(inter);
|
||||
DEFINE_COPY_EVENT(intra);
|
||||
DEFINE_COPY_EVENT(do_async);
|
||||
DEFINE_COPY_EVENT(async);
|
||||
|
||||
TRACE_EVENT(nfsd_copy_done,
|
||||
TP_PROTO(
|
||||
@ -2193,11 +2224,80 @@ TRACE_EVENT(nfsd_copy_done,
|
||||
__assign_sockaddr(addr, ©->cp_clp->cl_addr,
|
||||
sizeof(struct sockaddr_in6));
|
||||
),
|
||||
TP_printk("addr=%pISpc status=%d intra=%d async=%d ",
|
||||
TP_printk("addr=%pISpc status=%d intra=%d async=%d",
|
||||
__get_sockaddr(addr), __entry->status, __entry->intra, __entry->async
|
||||
)
|
||||
);
|
||||
|
||||
TRACE_EVENT(nfsd_copy_async_done,
|
||||
TP_PROTO(
|
||||
const struct nfsd4_copy *copy
|
||||
),
|
||||
TP_ARGS(copy),
|
||||
TP_STRUCT__entry(
|
||||
__field(int, status)
|
||||
__field(bool, intra)
|
||||
__field(bool, async)
|
||||
__field(u32, src_cl_boot)
|
||||
__field(u32, src_cl_id)
|
||||
__field(u32, src_so_id)
|
||||
__field(u32, src_si_generation)
|
||||
__field(u32, dst_cl_boot)
|
||||
__field(u32, dst_cl_id)
|
||||
__field(u32, dst_so_id)
|
||||
__field(u32, dst_si_generation)
|
||||
__field(u32, cb_cl_boot)
|
||||
__field(u32, cb_cl_id)
|
||||
__field(u32, cb_so_id)
|
||||
__field(u32, cb_si_generation)
|
||||
__field(u64, src_cp_pos)
|
||||
__field(u64, dst_cp_pos)
|
||||
__field(u64, cp_count)
|
||||
__sockaddr(addr, sizeof(struct sockaddr_in6))
|
||||
),
|
||||
TP_fast_assign(
|
||||
const stateid_t *src_stp = ©->cp_src_stateid;
|
||||
const stateid_t *dst_stp = ©->cp_dst_stateid;
|
||||
const stateid_t *cb_stp = ©->cp_res.cb_stateid;
|
||||
|
||||
__entry->status = be32_to_cpu(copy->nfserr);
|
||||
__entry->intra = test_bit(NFSD4_COPY_F_INTRA, ©->cp_flags);
|
||||
__entry->async = !test_bit(NFSD4_COPY_F_SYNCHRONOUS, ©->cp_flags);
|
||||
__entry->src_cl_boot = src_stp->si_opaque.so_clid.cl_boot;
|
||||
__entry->src_cl_id = src_stp->si_opaque.so_clid.cl_id;
|
||||
__entry->src_so_id = src_stp->si_opaque.so_id;
|
||||
__entry->src_si_generation = src_stp->si_generation;
|
||||
__entry->dst_cl_boot = dst_stp->si_opaque.so_clid.cl_boot;
|
||||
__entry->dst_cl_id = dst_stp->si_opaque.so_clid.cl_id;
|
||||
__entry->dst_so_id = dst_stp->si_opaque.so_id;
|
||||
__entry->dst_si_generation = dst_stp->si_generation;
|
||||
__entry->cb_cl_boot = cb_stp->si_opaque.so_clid.cl_boot;
|
||||
__entry->cb_cl_id = cb_stp->si_opaque.so_clid.cl_id;
|
||||
__entry->cb_so_id = cb_stp->si_opaque.so_id;
|
||||
__entry->cb_si_generation = cb_stp->si_generation;
|
||||
__entry->src_cp_pos = copy->cp_src_pos;
|
||||
__entry->dst_cp_pos = copy->cp_dst_pos;
|
||||
__entry->cp_count = copy->cp_count;
|
||||
__assign_sockaddr(addr, ©->cp_clp->cl_addr,
|
||||
sizeof(struct sockaddr_in6));
|
||||
),
|
||||
TP_printk("client=%pISpc status=%d intra=%d async=%d "
|
||||
"src_client %08x:%08x src_stateid %08x:%08x "
|
||||
"dst_client %08x:%08x dst_stateid %08x:%08x "
|
||||
"cb_client %08x:%08x cb_stateid %08x:%08x "
|
||||
"cp_src_pos=%llu cp_dst_pos=%llu cp_count=%llu",
|
||||
__get_sockaddr(addr),
|
||||
__entry->status, __entry->intra, __entry->async,
|
||||
__entry->src_cl_boot, __entry->src_cl_id,
|
||||
__entry->src_so_id, __entry->src_si_generation,
|
||||
__entry->dst_cl_boot, __entry->dst_cl_id,
|
||||
__entry->dst_so_id, __entry->dst_si_generation,
|
||||
__entry->cb_cl_boot, __entry->cb_cl_id,
|
||||
__entry->cb_so_id, __entry->cb_si_generation,
|
||||
__entry->src_cp_pos, __entry->dst_cp_pos, __entry->cp_count
|
||||
)
|
||||
);
|
||||
|
||||
#endif /* _NFSD_TRACE_H */
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
|
@ -100,6 +100,7 @@ nfserrno (int errno)
|
||||
{ nfserr_io, -EUCLEAN },
|
||||
{ nfserr_perm, -ENOKEY },
|
||||
{ nfserr_no_grace, -ENOGRACE},
|
||||
{ nfserr_io, -EBADMSG },
|
||||
};
|
||||
int i;
|
||||
|
||||
@ -421,7 +422,8 @@ nfsd_get_write_access(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
if (iap->ia_size < inode->i_size) {
|
||||
__be32 err;
|
||||
|
||||
err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry,
|
||||
err = nfsd_permission(&rqstp->rq_cred,
|
||||
fhp->fh_export, fhp->fh_dentry,
|
||||
NFSD_MAY_TRUNC | NFSD_MAY_OWNER_OVERRIDE);
|
||||
if (err)
|
||||
return err;
|
||||
@ -814,7 +816,8 @@ nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access, u32 *suppor
|
||||
|
||||
sresult |= map->access;
|
||||
|
||||
err2 = nfsd_permission(rqstp, export, dentry, map->how);
|
||||
err2 = nfsd_permission(&rqstp->rq_cred, export,
|
||||
dentry, map->how);
|
||||
switch (err2) {
|
||||
case nfs_ok:
|
||||
result |= map->access;
|
||||
@ -1160,7 +1163,6 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfsd_file *nf,
|
||||
errseq_t since;
|
||||
__be32 nfserr;
|
||||
int host_err;
|
||||
int use_wgather;
|
||||
loff_t pos = offset;
|
||||
unsigned long exp_op_flags = 0;
|
||||
unsigned int pflags = current->flags;
|
||||
@ -1186,12 +1188,11 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfsd_file *nf,
|
||||
}
|
||||
|
||||
exp = fhp->fh_export;
|
||||
use_wgather = (rqstp->rq_vers == 2) && EX_WGATHER(exp);
|
||||
|
||||
if (!EX_ISSYNC(exp))
|
||||
stable = NFS_UNSTABLE;
|
||||
|
||||
if (stable && !use_wgather)
|
||||
if (stable && !fhp->fh_use_wgather)
|
||||
flags |= RWF_SYNC;
|
||||
|
||||
iov_iter_kvec(&iter, ITER_SOURCE, vec, vlen, *cnt);
|
||||
@ -1210,7 +1211,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfsd_file *nf,
|
||||
if (host_err < 0)
|
||||
goto out_nfserr;
|
||||
|
||||
if (stable && use_wgather) {
|
||||
if (stable && fhp->fh_use_wgather) {
|
||||
host_err = wait_for_concurrent_writes(file);
|
||||
if (host_err < 0)
|
||||
commit_reset_write_verifier(nn, rqstp, host_err);
|
||||
@ -1475,7 +1476,8 @@ nfsd_create_locked(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
dirp = d_inode(dentry);
|
||||
|
||||
dchild = dget(resfhp->fh_dentry);
|
||||
err = nfsd_permission(rqstp, fhp->fh_export, dentry, NFSD_MAY_CREATE);
|
||||
err = nfsd_permission(&rqstp->rq_cred, fhp->fh_export, dentry,
|
||||
NFSD_MAY_CREATE);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
@ -1767,9 +1769,6 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
|
||||
if (!err)
|
||||
err = nfserrno(commit_metadata(tfhp));
|
||||
} else {
|
||||
if (host_err == -EXDEV && rqstp->rq_vers == 2)
|
||||
err = nfserr_acces;
|
||||
else
|
||||
err = nfserrno(host_err);
|
||||
}
|
||||
dput(dnew);
|
||||
@ -1836,7 +1835,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
|
||||
if (!flen || isdotent(fname, flen) || !tlen || isdotent(tname, tlen))
|
||||
goto out;
|
||||
|
||||
err = (rqstp->rq_vers == 2) ? nfserr_acces : nfserr_xdev;
|
||||
err = nfserr_xdev;
|
||||
if (ffhp->fh_export->ex_path.mnt != tfhp->fh_export->ex_path.mnt)
|
||||
goto out;
|
||||
if (ffhp->fh_export->ex_path.dentry != tfhp->fh_export->ex_path.dentry)
|
||||
@ -1851,7 +1850,7 @@ retry:
|
||||
|
||||
trap = lock_rename(tdentry, fdentry);
|
||||
if (IS_ERR(trap)) {
|
||||
err = (rqstp->rq_vers == 2) ? nfserr_acces : nfserr_xdev;
|
||||
err = nfserr_xdev;
|
||||
goto out_want_write;
|
||||
}
|
||||
err = fh_fill_pre_attrs(ffhp);
|
||||
@ -2020,10 +2019,7 @@ out_nfserr:
|
||||
/* name is mounted-on. There is no perfect
|
||||
* error status.
|
||||
*/
|
||||
if (nfsd_v4client(rqstp))
|
||||
err = nfserr_file_open;
|
||||
else
|
||||
err = nfserr_acces;
|
||||
} else {
|
||||
err = nfserrno(host_err);
|
||||
}
|
||||
@ -2178,8 +2174,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp,
|
||||
loff_t offset = *offsetp;
|
||||
int may_flags = NFSD_MAY_READ;
|
||||
|
||||
/* NFSv2 only supports 32 bit cookies */
|
||||
if (rqstp->rq_vers > 2)
|
||||
if (fhp->fh_64bit_cookies)
|
||||
may_flags |= NFSD_MAY_64BIT_COOKIE;
|
||||
|
||||
err = nfsd_open(rqstp, fhp, S_IFDIR, may_flags, &file);
|
||||
@ -2255,9 +2250,9 @@ nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct kstatfs *stat, in
|
||||
return err;
|
||||
}
|
||||
|
||||
static int exp_rdonly(struct svc_rqst *rqstp, struct svc_export *exp)
|
||||
static int exp_rdonly(struct svc_cred *cred, struct svc_export *exp)
|
||||
{
|
||||
return nfsexp_flags(rqstp, exp) & NFSEXP_READONLY;
|
||||
return nfsexp_flags(cred, exp) & NFSEXP_READONLY;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NFSD_V4
|
||||
@ -2501,7 +2496,7 @@ out_unlock:
|
||||
* Check for a user's access permissions to this inode.
|
||||
*/
|
||||
__be32
|
||||
nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp,
|
||||
nfsd_permission(struct svc_cred *cred, struct svc_export *exp,
|
||||
struct dentry *dentry, int acc)
|
||||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
@ -2533,7 +2528,7 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp,
|
||||
*/
|
||||
if (!(acc & NFSD_MAY_LOCAL_ACCESS))
|
||||
if (acc & (NFSD_MAY_WRITE | NFSD_MAY_SATTR | NFSD_MAY_TRUNC)) {
|
||||
if (exp_rdonly(rqstp, exp) ||
|
||||
if (exp_rdonly(cred, exp) ||
|
||||
__mnt_is_readonly(exp->ex_path.mnt))
|
||||
return nfserr_rofs;
|
||||
if (/* (acc & NFSD_MAY_WRITE) && */ IS_IMMUTABLE(inode))
|
||||
|
@ -153,8 +153,8 @@ __be32 nfsd_readdir(struct svc_rqst *, struct svc_fh *,
|
||||
__be32 nfsd_statfs(struct svc_rqst *, struct svc_fh *,
|
||||
struct kstatfs *, int access);
|
||||
|
||||
__be32 nfsd_permission(struct svc_rqst *, struct svc_export *,
|
||||
struct dentry *, int);
|
||||
__be32 nfsd_permission(struct svc_cred *cred, struct svc_export *exp,
|
||||
struct dentry *dentry, int acc);
|
||||
|
||||
void nfsd_filp_close(struct file *fp);
|
||||
|
||||
|
@ -713,6 +713,7 @@ struct nfsd4_copy {
|
||||
struct nfsd4_ssc_umount_item *ss_nsui;
|
||||
struct nfs_fh c_fh;
|
||||
nfs4_stateid stateid;
|
||||
struct nfsd_net *cp_nn;
|
||||
};
|
||||
|
||||
static inline void nfsd4_copy_set_sync(struct nfsd4_copy *copy, bool sync)
|
||||
|
@ -200,7 +200,7 @@ extern const struct svc_procedure nlmsvc_procedures[24];
|
||||
extern const struct svc_procedure nlmsvc_procedures4[24];
|
||||
#endif
|
||||
extern int nlmsvc_grace_period;
|
||||
extern unsigned long nlmsvc_timeout;
|
||||
extern unsigned long nlm_timeout;
|
||||
extern bool nsm_use_hostnames;
|
||||
extern u32 nsm_local_state;
|
||||
|
||||
|
@ -290,6 +290,9 @@ enum nfsstat4 {
|
||||
/* xattr (RFC8276) */
|
||||
NFS4ERR_NOXATTR = 10095,
|
||||
NFS4ERR_XATTR2BIG = 10096,
|
||||
|
||||
/* can be used for internal errors */
|
||||
NFS4ERR_FIRST_FREE
|
||||
};
|
||||
|
||||
/* error codes for internal client use */
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <linux/wait.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/pagevec.h>
|
||||
#include <linux/kthread.h>
|
||||
|
||||
/*
|
||||
*
|
||||
@ -35,7 +36,7 @@
|
||||
struct svc_pool {
|
||||
unsigned int sp_id; /* pool id; also node id on NUMA */
|
||||
struct lwq sp_xprts; /* pending transports */
|
||||
atomic_t sp_nrthreads; /* # of threads in pool */
|
||||
unsigned int sp_nrthreads; /* # of threads in pool */
|
||||
struct list_head sp_all_threads; /* all server threads */
|
||||
struct llist_head sp_idle_threads; /* idle server threads */
|
||||
|
||||
@ -232,6 +233,11 @@ struct svc_rqst {
|
||||
struct net *rq_bc_net; /* pointer to backchannel's
|
||||
* net namespace
|
||||
*/
|
||||
|
||||
int rq_err; /* Thread sets this to inidicate
|
||||
* initialisation success.
|
||||
*/
|
||||
|
||||
unsigned long bc_to_initval;
|
||||
unsigned int bc_to_retries;
|
||||
void ** rq_lease_breaker; /* The v4 client breaking a lease */
|
||||
@ -305,6 +311,31 @@ static inline bool svc_thread_should_stop(struct svc_rqst *rqstp)
|
||||
return test_bit(RQ_VICTIM, &rqstp->rq_flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_thread_init_status - report whether thread has initialised successfully
|
||||
* @rqstp: the thread in question
|
||||
* @err: errno code
|
||||
*
|
||||
* After performing any initialisation that could fail, and before starting
|
||||
* normal work, each sunrpc svc_thread must call svc_thread_init_status()
|
||||
* with an appropriate error, or zero.
|
||||
*
|
||||
* If zero is passed, the thread is ready and must continue until
|
||||
* svc_thread_should_stop() returns true. If a non-zero error is passed
|
||||
* the call will not return - the thread will exit.
|
||||
*/
|
||||
static inline void svc_thread_init_status(struct svc_rqst *rqstp, int err)
|
||||
{
|
||||
rqstp->rq_err = err;
|
||||
/* memory barrier ensures assignment to error above is visible before
|
||||
* waitqueue_active() test below completes.
|
||||
*/
|
||||
smp_mb();
|
||||
wake_up_var(&rqstp->rq_err);
|
||||
if (err)
|
||||
kthread_exit(1);
|
||||
}
|
||||
|
||||
struct svc_deferred_req {
|
||||
u32 prot; /* protocol (UDP or TCP) */
|
||||
struct svc_xprt *xprt;
|
||||
@ -401,17 +432,13 @@ struct svc_procedure {
|
||||
*/
|
||||
int sunrpc_set_pool_mode(const char *val);
|
||||
int sunrpc_get_pool_mode(char *val, size_t size);
|
||||
int svc_rpcb_setup(struct svc_serv *serv, struct net *net);
|
||||
void svc_rpcb_cleanup(struct svc_serv *serv, struct net *net);
|
||||
int svc_bind(struct svc_serv *serv, struct net *net);
|
||||
struct svc_serv *svc_create(struct svc_program *, unsigned int,
|
||||
int (*threadfn)(void *data));
|
||||
struct svc_rqst *svc_rqst_alloc(struct svc_serv *serv,
|
||||
struct svc_pool *pool, int node);
|
||||
bool svc_rqst_replace_page(struct svc_rqst *rqstp,
|
||||
struct page *page);
|
||||
void svc_rqst_release_pages(struct svc_rqst *rqstp);
|
||||
void svc_rqst_free(struct svc_rqst *);
|
||||
void svc_exit_thread(struct svc_rqst *);
|
||||
struct svc_serv * svc_create_pooled(struct svc_program *prog,
|
||||
struct svc_stat *stats,
|
||||
@ -446,11 +473,6 @@ int svc_generic_rpcbind_set(struct net *net,
|
||||
u32 version, int family,
|
||||
unsigned short proto,
|
||||
unsigned short port);
|
||||
int svc_rpcbind_set_version(struct net *net,
|
||||
const struct svc_program *progp,
|
||||
u32 version, int family,
|
||||
unsigned short proto,
|
||||
unsigned short port);
|
||||
|
||||
#define RPC_MAX_ADDRBUFLEN (63U)
|
||||
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include <linux/sunrpc/rpc_rdma.h>
|
||||
#include <linux/sunrpc/rpc_rdma_cid.h>
|
||||
#include <linux/sunrpc/svc_rdma_pcl.h>
|
||||
#include <linux/sunrpc/rdma_rn.h>
|
||||
|
||||
#include <linux/percpu_counter.h>
|
||||
#include <rdma/ib_verbs.h>
|
||||
@ -76,6 +77,7 @@ struct svcxprt_rdma {
|
||||
struct svc_xprt sc_xprt; /* SVC transport structure */
|
||||
struct rdma_cm_id *sc_cm_id; /* RDMA connection id */
|
||||
struct list_head sc_accept_q; /* Conn. waiting accept */
|
||||
struct rpcrdma_notification sc_rn; /* removal notification */
|
||||
int sc_ord; /* RDMA read limit */
|
||||
int sc_max_send_sges;
|
||||
bool sc_snd_w_inv; /* OK to use Send With Invalidate */
|
||||
|
@ -151,7 +151,6 @@ struct auth_ops {
|
||||
|
||||
struct svc_xprt;
|
||||
|
||||
extern enum svc_auth_status svc_authenticate(struct svc_rqst *rqstp);
|
||||
extern rpc_authflavor_t svc_auth_flavor(struct svc_rqst *rqstp);
|
||||
extern int svc_authorise(struct svc_rqst *rqstp);
|
||||
extern enum svc_auth_status svc_set_client(struct svc_rqst *rqstp);
|
||||
|
@ -58,8 +58,6 @@ static inline u32 svc_sock_final_rec(struct svc_sock *svsk)
|
||||
*/
|
||||
void svc_recv(struct svc_rqst *rqstp);
|
||||
void svc_send(struct svc_rqst *rqstp);
|
||||
void svc_drop(struct svc_rqst *);
|
||||
void svc_sock_update_bufs(struct svc_serv *serv);
|
||||
int svc_addsock(struct svc_serv *serv, struct net *net,
|
||||
const int fd, char *name_return, const size_t len,
|
||||
const struct cred *cred);
|
||||
|
243
include/linux/sunrpc/xdrgen/_builtins.h
Normal file
243
include/linux/sunrpc/xdrgen/_builtins.h
Normal file
@ -0,0 +1,243 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2024 Oracle and/or its affiliates.
|
||||
*
|
||||
* This header defines XDR data type primitives specified in
|
||||
* Section 4 of RFC 4506, used by RPC programs implemented
|
||||
* in the Linux kernel.
|
||||
*/
|
||||
|
||||
#ifndef _SUNRPC_XDRGEN__BUILTINS_H_
|
||||
#define _SUNRPC_XDRGEN__BUILTINS_H_
|
||||
|
||||
#include <linux/sunrpc/xdr.h>
|
||||
|
||||
static inline bool
|
||||
xdrgen_decode_void(struct xdr_stream *xdr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
xdrgen_encode_void(struct xdr_stream *xdr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
xdrgen_decode_bool(struct xdr_stream *xdr, bool *ptr)
|
||||
{
|
||||
__be32 *p = xdr_inline_decode(xdr, XDR_UNIT);
|
||||
|
||||
if (unlikely(!p))
|
||||
return false;
|
||||
*ptr = (*p != xdr_zero);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
xdrgen_encode_bool(struct xdr_stream *xdr, bool val)
|
||||
{
|
||||
__be32 *p = xdr_reserve_space(xdr, XDR_UNIT);
|
||||
|
||||
if (unlikely(!p))
|
||||
return false;
|
||||
*p = val ? xdr_one : xdr_zero;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
xdrgen_decode_int(struct xdr_stream *xdr, s32 *ptr)
|
||||
{
|
||||
__be32 *p = xdr_inline_decode(xdr, XDR_UNIT);
|
||||
|
||||
if (unlikely(!p))
|
||||
return false;
|
||||
*ptr = be32_to_cpup(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
xdrgen_encode_int(struct xdr_stream *xdr, s32 val)
|
||||
{
|
||||
__be32 *p = xdr_reserve_space(xdr, XDR_UNIT);
|
||||
|
||||
if (unlikely(!p))
|
||||
return false;
|
||||
*p = cpu_to_be32(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
xdrgen_decode_unsigned_int(struct xdr_stream *xdr, u32 *ptr)
|
||||
{
|
||||
__be32 *p = xdr_inline_decode(xdr, XDR_UNIT);
|
||||
|
||||
if (unlikely(!p))
|
||||
return false;
|
||||
*ptr = be32_to_cpup(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
xdrgen_encode_unsigned_int(struct xdr_stream *xdr, u32 val)
|
||||
{
|
||||
__be32 *p = xdr_reserve_space(xdr, XDR_UNIT);
|
||||
|
||||
if (unlikely(!p))
|
||||
return false;
|
||||
*p = cpu_to_be32(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
xdrgen_decode_long(struct xdr_stream *xdr, s32 *ptr)
|
||||
{
|
||||
__be32 *p = xdr_inline_decode(xdr, XDR_UNIT);
|
||||
|
||||
if (unlikely(!p))
|
||||
return false;
|
||||
*ptr = be32_to_cpup(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
xdrgen_encode_long(struct xdr_stream *xdr, s32 val)
|
||||
{
|
||||
__be32 *p = xdr_reserve_space(xdr, XDR_UNIT);
|
||||
|
||||
if (unlikely(!p))
|
||||
return false;
|
||||
*p = cpu_to_be32(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
xdrgen_decode_unsigned_long(struct xdr_stream *xdr, u32 *ptr)
|
||||
{
|
||||
__be32 *p = xdr_inline_decode(xdr, XDR_UNIT);
|
||||
|
||||
if (unlikely(!p))
|
||||
return false;
|
||||
*ptr = be32_to_cpup(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
xdrgen_encode_unsigned_long(struct xdr_stream *xdr, u32 val)
|
||||
{
|
||||
__be32 *p = xdr_reserve_space(xdr, XDR_UNIT);
|
||||
|
||||
if (unlikely(!p))
|
||||
return false;
|
||||
*p = cpu_to_be32(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
xdrgen_decode_hyper(struct xdr_stream *xdr, s64 *ptr)
|
||||
{
|
||||
__be32 *p = xdr_inline_decode(xdr, XDR_UNIT * 2);
|
||||
|
||||
if (unlikely(!p))
|
||||
return false;
|
||||
*ptr = get_unaligned_be64(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
xdrgen_encode_hyper(struct xdr_stream *xdr, s64 val)
|
||||
{
|
||||
__be32 *p = xdr_reserve_space(xdr, XDR_UNIT * 2);
|
||||
|
||||
if (unlikely(!p))
|
||||
return false;
|
||||
put_unaligned_be64(val, p);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
xdrgen_decode_unsigned_hyper(struct xdr_stream *xdr, u64 *ptr)
|
||||
{
|
||||
__be32 *p = xdr_inline_decode(xdr, XDR_UNIT * 2);
|
||||
|
||||
if (unlikely(!p))
|
||||
return false;
|
||||
*ptr = get_unaligned_be64(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
xdrgen_encode_unsigned_hyper(struct xdr_stream *xdr, u64 val)
|
||||
{
|
||||
__be32 *p = xdr_reserve_space(xdr, XDR_UNIT * 2);
|
||||
|
||||
if (unlikely(!p))
|
||||
return false;
|
||||
put_unaligned_be64(val, p);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
xdrgen_decode_string(struct xdr_stream *xdr, string *ptr, u32 maxlen)
|
||||
{
|
||||
__be32 *p;
|
||||
u32 len;
|
||||
|
||||
if (unlikely(xdr_stream_decode_u32(xdr, &len) < 0))
|
||||
return false;
|
||||
if (unlikely(maxlen && len > maxlen))
|
||||
return false;
|
||||
if (len != 0) {
|
||||
p = xdr_inline_decode(xdr, len);
|
||||
if (unlikely(!p))
|
||||
return false;
|
||||
ptr->data = (unsigned char *)p;
|
||||
}
|
||||
ptr->len = len;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
xdrgen_encode_string(struct xdr_stream *xdr, string val, u32 maxlen)
|
||||
{
|
||||
__be32 *p = xdr_reserve_space(xdr, XDR_UNIT + xdr_align_size(val.len));
|
||||
|
||||
if (unlikely(!p))
|
||||
return false;
|
||||
xdr_encode_opaque(p, val.data, val.len);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
xdrgen_decode_opaque(struct xdr_stream *xdr, opaque *ptr, u32 maxlen)
|
||||
{
|
||||
__be32 *p;
|
||||
u32 len;
|
||||
|
||||
if (unlikely(xdr_stream_decode_u32(xdr, &len) < 0))
|
||||
return false;
|
||||
if (unlikely(maxlen && len > maxlen))
|
||||
return false;
|
||||
if (len != 0) {
|
||||
p = xdr_inline_decode(xdr, len);
|
||||
if (unlikely(!p))
|
||||
return false;
|
||||
ptr->data = (u8 *)p;
|
||||
}
|
||||
ptr->len = len;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
xdrgen_encode_opaque(struct xdr_stream *xdr, opaque val)
|
||||
{
|
||||
__be32 *p = xdr_reserve_space(xdr, XDR_UNIT + xdr_align_size(val.len));
|
||||
|
||||
if (unlikely(!p))
|
||||
return false;
|
||||
xdr_encode_opaque(p, val.data, val.len);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* _SUNRPC_XDRGEN__BUILTINS_H_ */
|
26
include/linux/sunrpc/xdrgen/_defs.h
Normal file
26
include/linux/sunrpc/xdrgen/_defs.h
Normal file
@ -0,0 +1,26 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2024 Oracle and/or its affiliates.
|
||||
*
|
||||
* This header defines XDR data type primitives specified in
|
||||
* Section 4 of RFC 4506, used by RPC programs implemented
|
||||
* in the Linux kernel.
|
||||
*/
|
||||
|
||||
#ifndef _SUNRPC_XDRGEN__DEFS_H_
|
||||
#define _SUNRPC_XDRGEN__DEFS_H_
|
||||
|
||||
#define TRUE (true)
|
||||
#define FALSE (false)
|
||||
|
||||
typedef struct {
|
||||
u32 len;
|
||||
unsigned char *data;
|
||||
} string;
|
||||
|
||||
typedef struct {
|
||||
u32 len;
|
||||
u8 *data;
|
||||
} opaque;
|
||||
|
||||
#endif /* _SUNRPC_XDRGEN__DEFS_H_ */
|
@ -2172,6 +2172,29 @@ TRACE_EVENT(svcrdma_qp_error,
|
||||
)
|
||||
);
|
||||
|
||||
TRACE_EVENT(svcrdma_device_removal,
|
||||
TP_PROTO(
|
||||
const struct rdma_cm_id *id
|
||||
),
|
||||
|
||||
TP_ARGS(id),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__string(name, id->device->name)
|
||||
__array(unsigned char, addr, sizeof(struct sockaddr_in6))
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__assign_str(name);
|
||||
memcpy(__entry->addr, &id->route.addr.dst_addr,
|
||||
sizeof(struct sockaddr_in6));
|
||||
),
|
||||
|
||||
TP_printk("device %s to be removed, disconnecting %pISpc\n",
|
||||
__get_str(name), __entry->addr
|
||||
)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(svcrdma_sendqueue_class,
|
||||
TP_PROTO(
|
||||
const struct svcxprt_rdma *rdma,
|
||||
|
@ -51,6 +51,7 @@ TRACE_DEFINE_ENUM(NFSERR_JUKEBOX);
|
||||
{ NFSERR_IO, "IO" }, \
|
||||
{ NFSERR_NXIO, "NXIO" }, \
|
||||
{ ECHILD, "CHILD" }, \
|
||||
{ ETIMEDOUT, "TIMEDOUT" }, \
|
||||
{ NFSERR_EAGAIN, "AGAIN" }, \
|
||||
{ NFSERR_ACCES, "ACCES" }, \
|
||||
{ NFSERR_EXIST, "EXIST" }, \
|
||||
|
@ -36,7 +36,11 @@ static inline int sock_is_loopback(struct sock *sk)
|
||||
return loopback;
|
||||
}
|
||||
|
||||
struct svc_serv;
|
||||
struct svc_rqst;
|
||||
int rpc_clients_notifier_register(void);
|
||||
void rpc_clients_notifier_unregister(void);
|
||||
void auth_domain_cleanup(void);
|
||||
void svc_sock_update_bufs(struct svc_serv *serv);
|
||||
enum svc_auth_status svc_authenticate(struct svc_rqst *rqstp);
|
||||
#endif /* _NET_SUNRPC_SUNRPC_H */
|
||||
|
126
net/sunrpc/svc.c
126
net/sunrpc/svc.c
@ -32,6 +32,7 @@
|
||||
#include <trace/events/sunrpc.h>
|
||||
|
||||
#include "fail.h"
|
||||
#include "sunrpc.h"
|
||||
|
||||
#define RPCDBG_FACILITY RPCDBG_SVCDSP
|
||||
|
||||
@ -417,7 +418,7 @@ struct svc_pool *svc_pool_for_cpu(struct svc_serv *serv)
|
||||
return &serv->sv_pools[pidx % serv->sv_nrpools];
|
||||
}
|
||||
|
||||
int svc_rpcb_setup(struct svc_serv *serv, struct net *net)
|
||||
static int svc_rpcb_setup(struct svc_serv *serv, struct net *net)
|
||||
{
|
||||
int err;
|
||||
|
||||
@ -429,7 +430,6 @@ int svc_rpcb_setup(struct svc_serv *serv, struct net *net)
|
||||
svc_unregister(serv, net);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(svc_rpcb_setup);
|
||||
|
||||
void svc_rpcb_cleanup(struct svc_serv *serv, struct net *net)
|
||||
{
|
||||
@ -664,8 +664,21 @@ svc_release_buffer(struct svc_rqst *rqstp)
|
||||
put_page(rqstp->rq_pages[i]);
|
||||
}
|
||||
|
||||
struct svc_rqst *
|
||||
svc_rqst_alloc(struct svc_serv *serv, struct svc_pool *pool, int node)
|
||||
static void
|
||||
svc_rqst_free(struct svc_rqst *rqstp)
|
||||
{
|
||||
folio_batch_release(&rqstp->rq_fbatch);
|
||||
svc_release_buffer(rqstp);
|
||||
if (rqstp->rq_scratch_page)
|
||||
put_page(rqstp->rq_scratch_page);
|
||||
kfree(rqstp->rq_resp);
|
||||
kfree(rqstp->rq_argp);
|
||||
kfree(rqstp->rq_auth_data);
|
||||
kfree_rcu(rqstp, rq_rcu_head);
|
||||
}
|
||||
|
||||
static struct svc_rqst *
|
||||
svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool, int node)
|
||||
{
|
||||
struct svc_rqst *rqstp;
|
||||
|
||||
@ -693,27 +706,10 @@ svc_rqst_alloc(struct svc_serv *serv, struct svc_pool *pool, int node)
|
||||
if (!svc_init_buffer(rqstp, serv->sv_max_mesg, node))
|
||||
goto out_enomem;
|
||||
|
||||
return rqstp;
|
||||
out_enomem:
|
||||
svc_rqst_free(rqstp);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(svc_rqst_alloc);
|
||||
rqstp->rq_err = -EAGAIN; /* No error yet */
|
||||
|
||||
static struct svc_rqst *
|
||||
svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool, int node)
|
||||
{
|
||||
struct svc_rqst *rqstp;
|
||||
|
||||
rqstp = svc_rqst_alloc(serv, pool, node);
|
||||
if (!rqstp)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
spin_lock_bh(&serv->sv_lock);
|
||||
serv->sv_nrthreads += 1;
|
||||
spin_unlock_bh(&serv->sv_lock);
|
||||
|
||||
atomic_inc(&pool->sp_nrthreads);
|
||||
pool->sp_nrthreads += 1;
|
||||
|
||||
/* Protected by whatever lock the service uses when calling
|
||||
* svc_set_num_threads()
|
||||
@ -721,6 +717,10 @@ svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool, int node)
|
||||
list_add_rcu(&rqstp->rq_all, &pool->sp_all_threads);
|
||||
|
||||
return rqstp;
|
||||
|
||||
out_enomem:
|
||||
svc_rqst_free(rqstp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -768,31 +768,22 @@ svc_pool_victim(struct svc_serv *serv, struct svc_pool *target_pool,
|
||||
struct svc_pool *pool;
|
||||
unsigned int i;
|
||||
|
||||
retry:
|
||||
pool = target_pool;
|
||||
|
||||
if (pool != NULL) {
|
||||
if (atomic_inc_not_zero(&pool->sp_nrthreads))
|
||||
goto found_pool;
|
||||
return NULL;
|
||||
} else {
|
||||
if (!pool) {
|
||||
for (i = 0; i < serv->sv_nrpools; i++) {
|
||||
pool = &serv->sv_pools[--(*state) % serv->sv_nrpools];
|
||||
if (atomic_inc_not_zero(&pool->sp_nrthreads))
|
||||
goto found_pool;
|
||||
if (pool->sp_nrthreads)
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
found_pool:
|
||||
if (pool && pool->sp_nrthreads) {
|
||||
set_bit(SP_VICTIM_REMAINS, &pool->sp_flags);
|
||||
set_bit(SP_NEED_VICTIM, &pool->sp_flags);
|
||||
if (!atomic_dec_and_test(&pool->sp_nrthreads))
|
||||
return pool;
|
||||
/* Nothing left in this pool any more */
|
||||
clear_bit(SP_NEED_VICTIM, &pool->sp_flags);
|
||||
clear_bit(SP_VICTIM_REMAINS, &pool->sp_flags);
|
||||
goto retry;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -803,6 +794,7 @@ svc_start_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
|
||||
struct svc_pool *chosen_pool;
|
||||
unsigned int state = serv->sv_nrthreads-1;
|
||||
int node;
|
||||
int err;
|
||||
|
||||
do {
|
||||
nrservs--;
|
||||
@ -810,8 +802,8 @@ svc_start_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
|
||||
node = svc_pool_map_get_node(chosen_pool->sp_id);
|
||||
|
||||
rqstp = svc_prepare_thread(serv, chosen_pool, node);
|
||||
if (IS_ERR(rqstp))
|
||||
return PTR_ERR(rqstp);
|
||||
if (!rqstp)
|
||||
return -ENOMEM;
|
||||
task = kthread_create_on_node(serv->sv_threadfn, rqstp,
|
||||
node, "%s", serv->sv_name);
|
||||
if (IS_ERR(task)) {
|
||||
@ -825,6 +817,13 @@ svc_start_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
|
||||
|
||||
svc_sock_update_bufs(serv);
|
||||
wake_up_process(task);
|
||||
|
||||
wait_var_event(&rqstp->rq_err, rqstp->rq_err != -EAGAIN);
|
||||
err = rqstp->rq_err;
|
||||
if (err) {
|
||||
svc_exit_thread(rqstp);
|
||||
return err;
|
||||
}
|
||||
} while (nrservs > 0);
|
||||
|
||||
return 0;
|
||||
@ -871,7 +870,7 @@ svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
|
||||
if (!pool)
|
||||
nrservs -= serv->sv_nrthreads;
|
||||
else
|
||||
nrservs -= atomic_read(&pool->sp_nrthreads);
|
||||
nrservs -= pool->sp_nrthreads;
|
||||
|
||||
if (nrservs > 0)
|
||||
return svc_start_kthreads(serv, pool, nrservs);
|
||||
@ -933,24 +932,20 @@ void svc_rqst_release_pages(struct svc_rqst *rqstp)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from a server thread as it's exiting. Caller must hold the "service
|
||||
* mutex" for the service.
|
||||
/**
|
||||
* svc_exit_thread - finalise the termination of a sunrpc server thread
|
||||
* @rqstp: the svc_rqst which represents the thread.
|
||||
*
|
||||
* When a thread started with svc_new_thread() exits it must call
|
||||
* svc_exit_thread() as its last act. This must be done with the
|
||||
* service mutex held. Normally this is held by a DIFFERENT thread, the
|
||||
* one that is calling svc_set_num_threads() and which will wait for
|
||||
* SP_VICTIM_REMAINS to be cleared before dropping the mutex. If the
|
||||
* thread exits for any reason other than svc_thread_should_stop()
|
||||
* returning %true (which indicated that svc_set_num_threads() is
|
||||
* waiting for it to exit), then it must take the service mutex itself,
|
||||
* which can only safely be done using mutex_try_lock().
|
||||
*/
|
||||
void
|
||||
svc_rqst_free(struct svc_rqst *rqstp)
|
||||
{
|
||||
folio_batch_release(&rqstp->rq_fbatch);
|
||||
svc_release_buffer(rqstp);
|
||||
if (rqstp->rq_scratch_page)
|
||||
put_page(rqstp->rq_scratch_page);
|
||||
kfree(rqstp->rq_resp);
|
||||
kfree(rqstp->rq_argp);
|
||||
kfree(rqstp->rq_auth_data);
|
||||
kfree_rcu(rqstp, rq_rcu_head);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(svc_rqst_free);
|
||||
|
||||
void
|
||||
svc_exit_thread(struct svc_rqst *rqstp)
|
||||
{
|
||||
@ -959,11 +954,8 @@ svc_exit_thread(struct svc_rqst *rqstp)
|
||||
|
||||
list_del_rcu(&rqstp->rq_all);
|
||||
|
||||
atomic_dec(&pool->sp_nrthreads);
|
||||
|
||||
spin_lock_bh(&serv->sv_lock);
|
||||
pool->sp_nrthreads -= 1;
|
||||
serv->sv_nrthreads -= 1;
|
||||
spin_unlock_bh(&serv->sv_lock);
|
||||
svc_sock_update_bufs(serv);
|
||||
|
||||
svc_rqst_free(rqstp);
|
||||
@ -1098,6 +1090,7 @@ static int __svc_register(struct net *net, const char *progname,
|
||||
return error;
|
||||
}
|
||||
|
||||
static
|
||||
int svc_rpcbind_set_version(struct net *net,
|
||||
const struct svc_program *progp,
|
||||
u32 version, int family,
|
||||
@ -1108,7 +1101,6 @@ int svc_rpcbind_set_version(struct net *net,
|
||||
version, family, proto, port);
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(svc_rpcbind_set_version);
|
||||
|
||||
int svc_generic_rpcbind_set(struct net *net,
|
||||
const struct svc_program *progp,
|
||||
@ -1526,6 +1518,14 @@ err_system_err:
|
||||
goto sendit;
|
||||
}
|
||||
|
||||
/*
|
||||
* Drop request
|
||||
*/
|
||||
static void svc_drop(struct svc_rqst *rqstp)
|
||||
{
|
||||
trace_svc_drop(rqstp);
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_process - Execute one RPC transaction
|
||||
* @rqstp: RPC transaction context
|
||||
|
@ -905,15 +905,6 @@ void svc_recv(struct svc_rqst *rqstp)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(svc_recv);
|
||||
|
||||
/*
|
||||
* Drop request
|
||||
*/
|
||||
void svc_drop(struct svc_rqst *rqstp)
|
||||
{
|
||||
trace_svc_drop(rqstp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(svc_drop);
|
||||
|
||||
/**
|
||||
* svc_send - Return reply to client
|
||||
* @rqstp: RPC transaction context
|
||||
|
@ -98,7 +98,6 @@ enum svc_auth_status svc_authenticate(struct svc_rqst *rqstp)
|
||||
rqstp->rq_authop = aops;
|
||||
return aops->accept(rqstp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(svc_authenticate);
|
||||
|
||||
/**
|
||||
* svc_set_client - Assign an appropriate 'auth_domain' as the client
|
||||
|
@ -1378,7 +1378,6 @@ void svc_sock_update_bufs(struct svc_serv *serv)
|
||||
set_bit(XPT_CHNGBUF, &svsk->sk_xprt.xpt_flags);
|
||||
spin_unlock_bh(&serv->sv_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(svc_sock_update_bufs);
|
||||
|
||||
/*
|
||||
* Initialize socket for RPC use and create svc_sock struct
|
||||
|
@ -339,7 +339,6 @@ static int svc_rdma_cma_handler(struct rdma_cm_id *cma_id,
|
||||
svc_xprt_enqueue(xprt);
|
||||
break;
|
||||
case RDMA_CM_EVENT_DISCONNECTED:
|
||||
case RDMA_CM_EVENT_DEVICE_REMOVAL:
|
||||
svc_xprt_deferred_close(xprt);
|
||||
break;
|
||||
default:
|
||||
@ -370,7 +369,7 @@ static struct svc_xprt *svc_rdma_create(struct svc_serv *serv,
|
||||
listen_id = svc_rdma_create_listen_id(net, sa, cma_xprt);
|
||||
if (IS_ERR(listen_id)) {
|
||||
kfree(cma_xprt);
|
||||
return (struct svc_xprt *)listen_id;
|
||||
return ERR_CAST(listen_id);
|
||||
}
|
||||
cma_xprt->sc_cm_id = listen_id;
|
||||
|
||||
@ -384,6 +383,16 @@ static struct svc_xprt *svc_rdma_create(struct svc_serv *serv,
|
||||
return &cma_xprt->sc_xprt;
|
||||
}
|
||||
|
||||
static void svc_rdma_xprt_done(struct rpcrdma_notification *rn)
|
||||
{
|
||||
struct svcxprt_rdma *rdma = container_of(rn, struct svcxprt_rdma,
|
||||
sc_rn);
|
||||
struct rdma_cm_id *id = rdma->sc_cm_id;
|
||||
|
||||
trace_svcrdma_device_removal(id);
|
||||
svc_xprt_close(&rdma->sc_xprt);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the xpo_recvfrom function for listening endpoints. Its
|
||||
* purpose is to accept incoming connections. The CMA callback handler
|
||||
@ -425,6 +434,9 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
|
||||
dev = newxprt->sc_cm_id->device;
|
||||
newxprt->sc_port_num = newxprt->sc_cm_id->port_num;
|
||||
|
||||
if (rpcrdma_rn_register(dev, &newxprt->sc_rn, svc_rdma_xprt_done))
|
||||
goto errout;
|
||||
|
||||
newxprt->sc_max_req_size = svcrdma_max_req_size;
|
||||
newxprt->sc_max_requests = svcrdma_max_requests;
|
||||
newxprt->sc_max_bc_requests = svcrdma_max_bc_requests;
|
||||
@ -580,6 +592,7 @@ static void __svc_rdma_free(struct work_struct *work)
|
||||
{
|
||||
struct svcxprt_rdma *rdma =
|
||||
container_of(work, struct svcxprt_rdma, sc_work);
|
||||
struct ib_device *device = rdma->sc_cm_id->device;
|
||||
|
||||
/* This blocks until the Completion Queues are empty */
|
||||
if (rdma->sc_qp && !IS_ERR(rdma->sc_qp))
|
||||
@ -608,6 +621,7 @@ static void __svc_rdma_free(struct work_struct *work)
|
||||
/* Destroy the CM ID */
|
||||
rdma_destroy_id(rdma->sc_cm_id);
|
||||
|
||||
rpcrdma_rn_unregister(device, &rdma->sc_rn);
|
||||
kfree(rdma);
|
||||
}
|
||||
|
||||
|
2
tools/net/sunrpc/xdrgen/.gitignore
vendored
Normal file
2
tools/net/sunrpc/xdrgen/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
__pycache__
|
||||
generators/__pycache__
|
244
tools/net/sunrpc/xdrgen/README
Normal file
244
tools/net/sunrpc/xdrgen/README
Normal file
@ -0,0 +1,244 @@
|
||||
xdrgen - Linux Kernel XDR code generator
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
SunRPC programs are typically specified using a language defined by
|
||||
RFC 4506. In fact, all IETF-published NFS specifications provide a
|
||||
description of the specified protocol using this language.
|
||||
|
||||
Since the 1990's, user space consumers of SunRPC have had access to
|
||||
a tool that could read such XDR specifications and then generate C
|
||||
code that implements the RPC portions of that protocol. This tool is
|
||||
called rpcgen.
|
||||
|
||||
This RPC-level code is code that handles input directly from the
|
||||
network, and thus a high degree of memory safety and sanity checking
|
||||
is needed to help ensure proper levels of security. Bugs in this
|
||||
code can have significant impact on security and performance.
|
||||
|
||||
However, it is code that is repetitive and tedious to write by hand.
|
||||
|
||||
The C code generated by rpcgen makes extensive use of the facilities
|
||||
of the user space TI-RPC library and libc. Furthermore, the dialect
|
||||
of the generated code is very traditional K&R C.
|
||||
|
||||
The Linux kernel's implementation of SunRPC-based protocols hand-roll
|
||||
their XDR implementation. There are two main reasons for this:
|
||||
|
||||
1. libtirpc (and its predecessors) operate only in user space. The
|
||||
kernel's RPC implementation and its API are significantly
|
||||
different than libtirpc.
|
||||
|
||||
2. rpcgen-generated code is believed to be less efficient than code
|
||||
that is hand-written.
|
||||
|
||||
These days, gcc and its kin are capable of optimizing code better
|
||||
than human authors. There are only a few instances where writing
|
||||
XDR code by hand will make a measurable performance different.
|
||||
|
||||
In addition, the current hand-written code in the Linux kernel is
|
||||
difficult to audit and prove that it implements exactly what is in
|
||||
the protocol specification.
|
||||
|
||||
In order to accrue the benefits of machine-generated XDR code in the
|
||||
kernel, a tool is needed that will output C code that works against
|
||||
the kernel's SunRPC implementation rather than libtirpc.
|
||||
|
||||
Enter xdrgen.
|
||||
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
These dependencies are typically packaged by Linux distributions:
|
||||
|
||||
- python3
|
||||
- python3-lark
|
||||
- python3-jinja2
|
||||
|
||||
These dependencies are available via PyPi:
|
||||
|
||||
- pip install 'lark[interegular]'
|
||||
|
||||
|
||||
XDR Specifications
|
||||
------------------
|
||||
|
||||
When adding a new protocol implementation to the kernel, the XDR
|
||||
specification can be derived by feeding a .txt copy of the RFC to
|
||||
the script located in tools/net/sunrpc/extract.sh.
|
||||
|
||||
$ extract.sh < rfc0001.txt > new2.x
|
||||
|
||||
|
||||
Operation
|
||||
---------
|
||||
|
||||
Once a .x file is available, use xdrgen to generate source and
|
||||
header files containing an implementation of XDR encoding and
|
||||
decoding functions for the specified protocol.
|
||||
|
||||
$ ./xdrgen definitions new2.x > include/linux/sunrpc/xdrgen/new2.h
|
||||
$ ./xdrgen declarations new2.x > new2xdr_gen.h
|
||||
|
||||
and
|
||||
|
||||
$ ./xdrgen source new2.x > new2xdr_gen.c
|
||||
|
||||
The files are ready to use for a server-side protocol implementation,
|
||||
or may be used as a guide for implementing these routines by hand.
|
||||
|
||||
By default, the only comments added to this code are kdoc comments
|
||||
that appear directly in front of the public per-procedure APIs. For
|
||||
deeper introspection, specifying the "--annotate" flag will insert
|
||||
additional comments in the generated code to help readers match the
|
||||
generated code to specific parts of the XDR specification.
|
||||
|
||||
Because the generated code is targeted for the Linux kernel, it
|
||||
is tagged with a GPLv2-only license.
|
||||
|
||||
The xdrgen tool can also provide lexical and syntax checking of
|
||||
an XDR specification:
|
||||
|
||||
$ ./xdrgen lint xdr/new.x
|
||||
|
||||
|
||||
How It Works
|
||||
------------
|
||||
|
||||
xdrgen does not use machine learning to generate source code. The
|
||||
translation is entirely deterministic.
|
||||
|
||||
RFC 4506 Section 6 contains a BNF grammar of the XDR specification
|
||||
language. The grammar has been adapted for use by the Python Lark
|
||||
module.
|
||||
|
||||
The xdr.ebnf file in this directory contains the grammar used to
|
||||
parse XDR specifications. xdrgen configures Lark using the grammar
|
||||
in xdr.ebnf. Lark parses the target XDR specification using this
|
||||
grammar, creating a parse tree.
|
||||
|
||||
xdrgen then transforms the parse tree into an abstract syntax tree.
|
||||
This tree is passed to a series of code generators.
|
||||
|
||||
The generators are implemented as Python classes residing in the
|
||||
generators/ directory. Each generator emits code created from Jinja2
|
||||
templates stored in the templates/ directory.
|
||||
|
||||
The source code is generated in the same order in which they appear
|
||||
in the specification to ensure the generated code compiles. This
|
||||
conforms with the behavior of rpcgen.
|
||||
|
||||
xdrgen assumes that the generated source code is further compiled by
|
||||
a compiler that can optimize in a number of ways, including:
|
||||
|
||||
- Unused functions are discarded (ie, not added to the executable)
|
||||
|
||||
- Aggressive function inlining removes unnecessary stack frames
|
||||
|
||||
- Single-arm switch statements are replaced by a single conditional
|
||||
branch
|
||||
|
||||
And so on.
|
||||
|
||||
|
||||
Pragmas
|
||||
-------
|
||||
|
||||
Pragma directives specify exceptions to the normal generation of
|
||||
encoding and decoding functions. Currently one directive is
|
||||
implemented: "public".
|
||||
|
||||
Pragma exclude
|
||||
------ -------
|
||||
|
||||
pragma exclude <RPC procedure> ;
|
||||
|
||||
In some cases, a procedure encoder or decoder function might need
|
||||
special processing that cannot be automatically generated. The
|
||||
automatically-generated functions might conflict or interfere with
|
||||
the hand-rolled function. To avoid editing the generated source code
|
||||
by hand, a pragma can specify that the procedure's encoder and
|
||||
decoder functions are not included in the generated header and
|
||||
source.
|
||||
|
||||
For example:
|
||||
|
||||
pragma exclude NFSPROC3_READDIRPLUS;
|
||||
|
||||
Excludes the decoder function for the READDIRPLUS argument and the
|
||||
encoder function for the READDIRPLUS result.
|
||||
|
||||
Note that because data item encoder and decoder functions are
|
||||
defined "static __maybe_unused", subsequent compilation
|
||||
automatically excludes data item encoder and decoder functions that
|
||||
are used only by excluded procedure.
|
||||
|
||||
Pragma header
|
||||
------ ------
|
||||
|
||||
pragma header <string> ;
|
||||
|
||||
Provide a name to use for the header file. For example:
|
||||
|
||||
pragma header nlm4;
|
||||
|
||||
Adds
|
||||
|
||||
#include "nlm4xdr_gen.h"
|
||||
|
||||
to the generated source file.
|
||||
|
||||
Pragma public
|
||||
------ ------
|
||||
|
||||
pragma public <XDR data item> ;
|
||||
|
||||
Normally XDR encoder and decoder functions are "static". In case an
|
||||
implementer wants to call these functions from other source code,
|
||||
s/he can add a public pragma in the input .x file to indicate a set
|
||||
of functions that should get a prototype in the generated header,
|
||||
and the function definitions will not be declared static.
|
||||
|
||||
For example:
|
||||
|
||||
pragma public nfsstat3;
|
||||
|
||||
Adds these prototypes in the generated header:
|
||||
|
||||
bool xdrgen_decode_nfsstat3(struct xdr_stream *xdr, enum nfsstat3 *ptr);
|
||||
bool xdrgen_encode_nfsstat3(struct xdr_stream *xdr, enum nfsstat3 value);
|
||||
|
||||
And, in the generated source code, both of these functions appear
|
||||
without the "static __maybe_unused" modifiers.
|
||||
|
||||
|
||||
Future Work
|
||||
-----------
|
||||
|
||||
Finish implementing XDR pointer and list types.
|
||||
|
||||
Generate client-side procedure functions
|
||||
|
||||
Expand the README into a user guide similar to rpcgen(1)
|
||||
|
||||
Add more pragma directives:
|
||||
|
||||
* @pages -- use xdr_read/write_pages() for the specified opaque
|
||||
field
|
||||
* @skip -- do not decode, but rather skip, the specified argument
|
||||
field
|
||||
|
||||
Enable something like a #include to dynamically insert the content
|
||||
of other specification files
|
||||
|
||||
Properly support line-by-line pass-through via the "%" decorator
|
||||
|
||||
Build a unit test suite for verifying translation of XDR language
|
||||
into compilable code
|
||||
|
||||
Add a command-line option to insert trace_printk call sites in the
|
||||
generated source code, for improved (temporary) observability
|
||||
|
||||
Generate kernel Rust code as well as C code
|
2
tools/net/sunrpc/xdrgen/__init__.py
Normal file
2
tools/net/sunrpc/xdrgen/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Just to make sphinx-apidoc document this directory
|
113
tools/net/sunrpc/xdrgen/generators/__init__.py
Normal file
113
tools/net/sunrpc/xdrgen/generators/__init__.py
Normal file
@ -0,0 +1,113 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
"""Define a base code generator class"""
|
||||
|
||||
import sys
|
||||
from jinja2 import Environment, FileSystemLoader, Template
|
||||
|
||||
from xdr_ast import _XdrAst, Specification, _RpcProgram, _XdrTypeSpecifier
|
||||
from xdr_ast import public_apis, pass_by_reference, get_header_name
|
||||
from xdr_parse import get_xdr_annotate
|
||||
|
||||
|
||||
def create_jinja2_environment(language: str, xdr_type: str) -> Environment:
|
||||
"""Open a set of templates based on output language"""
|
||||
match language:
|
||||
case "C":
|
||||
environment = Environment(
|
||||
loader=FileSystemLoader(sys.path[0] + "/templates/C/" + xdr_type + "/"),
|
||||
trim_blocks=True,
|
||||
lstrip_blocks=True,
|
||||
)
|
||||
environment.globals["annotate"] = get_xdr_annotate()
|
||||
environment.globals["public_apis"] = public_apis
|
||||
environment.globals["pass_by_reference"] = pass_by_reference
|
||||
return environment
|
||||
case _:
|
||||
raise NotImplementedError("Language not supported")
|
||||
|
||||
|
||||
def get_jinja2_template(
|
||||
environment: Environment, template_type: str, template_name: str
|
||||
) -> Template:
|
||||
"""Retrieve a Jinja2 template for emitting source code"""
|
||||
return environment.get_template(template_type + "/" + template_name + ".j2")
|
||||
|
||||
|
||||
def find_xdr_program_name(root: Specification) -> str:
|
||||
"""Retrieve the RPC program name from an abstract syntax tree"""
|
||||
raw_name = get_header_name()
|
||||
if raw_name != "none":
|
||||
return raw_name.lower()
|
||||
for definition in root.definitions:
|
||||
if isinstance(definition.value, _RpcProgram):
|
||||
raw_name = definition.value.name
|
||||
return raw_name.lower().removesuffix("_program").removesuffix("_prog")
|
||||
return "noprog"
|
||||
|
||||
|
||||
def header_guard_infix(filename: str) -> str:
|
||||
"""Extract the header guard infix from the specification filename"""
|
||||
basename = filename.split("/")[-1]
|
||||
program = basename.replace(".x", "")
|
||||
return program.upper()
|
||||
|
||||
|
||||
def kernel_c_type(spec: _XdrTypeSpecifier) -> str:
|
||||
"""Return name of C type"""
|
||||
builtin_native_c_type = {
|
||||
"bool": "bool",
|
||||
"int": "s32",
|
||||
"unsigned_int": "u32",
|
||||
"long": "s32",
|
||||
"unsigned_long": "u32",
|
||||
"hyper": "s64",
|
||||
"unsigned_hyper": "u64",
|
||||
}
|
||||
if spec.type_name in builtin_native_c_type:
|
||||
return builtin_native_c_type[spec.type_name]
|
||||
return spec.type_name
|
||||
|
||||
|
||||
class Boilerplate:
|
||||
"""Base class to generate boilerplate for source files"""
|
||||
|
||||
def __init__(self, language: str, peer: str):
|
||||
"""Initialize an instance of this class"""
|
||||
raise NotImplementedError("No language support defined")
|
||||
|
||||
def emit_declaration(self, filename: str, root: Specification) -> None:
|
||||
"""Emit declaration header boilerplate"""
|
||||
raise NotImplementedError("Header boilerplate generation not supported")
|
||||
|
||||
def emit_definition(self, filename: str, root: Specification) -> None:
|
||||
"""Emit definition header boilerplate"""
|
||||
raise NotImplementedError("Header boilerplate generation not supported")
|
||||
|
||||
def emit_source(self, filename: str, root: Specification) -> None:
|
||||
"""Emit generic source code for this XDR type"""
|
||||
raise NotImplementedError("Source boilerplate generation not supported")
|
||||
|
||||
|
||||
class SourceGenerator:
|
||||
"""Base class to generate header and source code for XDR types"""
|
||||
|
||||
def __init__(self, language: str, peer: str):
|
||||
"""Initialize an instance of this class"""
|
||||
raise NotImplementedError("No language support defined")
|
||||
|
||||
def emit_declaration(self, node: _XdrAst) -> None:
|
||||
"""Emit one function declaration for this XDR type"""
|
||||
raise NotImplementedError("Declaration generation not supported")
|
||||
|
||||
def emit_decoder(self, node: _XdrAst) -> None:
|
||||
"""Emit one decoder function for this XDR type"""
|
||||
raise NotImplementedError("Decoder generation not supported")
|
||||
|
||||
def emit_definition(self, node: _XdrAst) -> None:
|
||||
"""Emit one definition for this XDR type"""
|
||||
raise NotImplementedError("Definition generation not supported")
|
||||
|
||||
def emit_encoder(self, node: _XdrAst) -> None:
|
||||
"""Emit one encoder function for this XDR type"""
|
||||
raise NotImplementedError("Encoder generation not supported")
|
20
tools/net/sunrpc/xdrgen/generators/constant.py
Normal file
20
tools/net/sunrpc/xdrgen/generators/constant.py
Normal file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env python3
|
||||
# ex: set filetype=python:
|
||||
|
||||
"""Generate code to handle XDR constants"""
|
||||
|
||||
from generators import SourceGenerator, create_jinja2_environment
|
||||
from xdr_ast import _XdrConstant
|
||||
|
||||
class XdrConstantGenerator(SourceGenerator):
|
||||
"""Generate source code for XDR constants"""
|
||||
|
||||
def __init__(self, language: str, peer: str):
|
||||
"""Initialize an instance of this class"""
|
||||
self.environment = create_jinja2_environment(language, "constants")
|
||||
self.peer = peer
|
||||
|
||||
def emit_definition(self, node: _XdrConstant) -> None:
|
||||
"""Emit one definition for a constant"""
|
||||
template = self.environment.get_template("definition.j2")
|
||||
print(template.render(name=node.name, value=node.value))
|
44
tools/net/sunrpc/xdrgen/generators/enum.py
Normal file
44
tools/net/sunrpc/xdrgen/generators/enum.py
Normal file
@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env python3
|
||||
# ex: set filetype=python:
|
||||
|
||||
"""Generate code to handle XDR enum types"""
|
||||
|
||||
from generators import SourceGenerator, create_jinja2_environment
|
||||
from xdr_ast import _XdrEnum, public_apis
|
||||
|
||||
|
||||
class XdrEnumGenerator(SourceGenerator):
|
||||
"""Generate source code for XDR enum types"""
|
||||
|
||||
def __init__(self, language: str, peer: str):
|
||||
"""Initialize an instance of this class"""
|
||||
self.environment = create_jinja2_environment(language, "enum")
|
||||
self.peer = peer
|
||||
|
||||
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")
|
||||
print(template.render(name=node.name))
|
||||
|
||||
def emit_definition(self, node: _XdrEnum) -> None:
|
||||
"""Emit one definition for an XDR enum type"""
|
||||
template = self.environment.get_template("definition/open.j2")
|
||||
print(template.render(name=node.name))
|
||||
|
||||
template = self.environment.get_template("definition/enumerator.j2")
|
||||
for enumerator in node.enumerators:
|
||||
print(template.render(name=enumerator.name, value=enumerator.value))
|
||||
|
||||
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")
|
||||
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")
|
||||
print(template.render(name=node.name))
|
33
tools/net/sunrpc/xdrgen/generators/header_bottom.py
Normal file
33
tools/net/sunrpc/xdrgen/generators/header_bottom.py
Normal file
@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env python3
|
||||
# ex: set filetype=python:
|
||||
|
||||
"""Generate header bottom boilerplate"""
|
||||
|
||||
import os.path
|
||||
import time
|
||||
|
||||
from generators import Boilerplate, header_guard_infix
|
||||
from generators import create_jinja2_environment, get_jinja2_template
|
||||
from xdr_ast import Specification
|
||||
|
||||
|
||||
class XdrHeaderBottomGenerator(Boilerplate):
|
||||
"""Generate header boilerplate"""
|
||||
|
||||
def __init__(self, language: str, peer: str):
|
||||
"""Initialize an instance of this class"""
|
||||
self.environment = create_jinja2_environment(language, "header_bottom")
|
||||
self.peer = peer
|
||||
|
||||
def emit_declaration(self, filename: str, root: Specification) -> None:
|
||||
"""Emit the bottom header guard"""
|
||||
template = get_jinja2_template(self.environment, "declaration", "header")
|
||||
print(template.render(infix=header_guard_infix(filename)))
|
||||
|
||||
def emit_definition(self, filename: str, root: Specification) -> None:
|
||||
"""Emit the bottom header guard"""
|
||||
template = get_jinja2_template(self.environment, "definition", "header")
|
||||
print(template.render(infix=header_guard_infix(filename)))
|
||||
|
||||
def emit_source(self, filename: str, root: Specification) -> None:
|
||||
pass
|
45
tools/net/sunrpc/xdrgen/generators/header_top.py
Normal file
45
tools/net/sunrpc/xdrgen/generators/header_top.py
Normal file
@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env python3
|
||||
# ex: set filetype=python:
|
||||
|
||||
"""Generate header top boilerplate"""
|
||||
|
||||
import os.path
|
||||
import time
|
||||
|
||||
from generators import Boilerplate, header_guard_infix
|
||||
from generators import create_jinja2_environment, get_jinja2_template
|
||||
from xdr_ast import Specification
|
||||
|
||||
|
||||
class XdrHeaderTopGenerator(Boilerplate):
|
||||
"""Generate header boilerplate"""
|
||||
|
||||
def __init__(self, language: str, peer: str):
|
||||
"""Initialize an instance of this class"""
|
||||
self.environment = create_jinja2_environment(language, "header_top")
|
||||
self.peer = peer
|
||||
|
||||
def emit_declaration(self, filename: str, root: Specification) -> None:
|
||||
"""Emit the top header guard"""
|
||||
template = get_jinja2_template(self.environment, "declaration", "header")
|
||||
print(
|
||||
template.render(
|
||||
infix=header_guard_infix(filename),
|
||||
filename=filename,
|
||||
mtime=time.ctime(os.path.getmtime(filename)),
|
||||
)
|
||||
)
|
||||
|
||||
def emit_definition(self, filename: str, root: Specification) -> None:
|
||||
"""Emit the top header guard"""
|
||||
template = get_jinja2_template(self.environment, "definition", "header")
|
||||
print(
|
||||
template.render(
|
||||
infix=header_guard_infix(filename),
|
||||
filename=filename,
|
||||
mtime=time.ctime(os.path.getmtime(filename)),
|
||||
)
|
||||
)
|
||||
|
||||
def emit_source(self, filename: str, root: Specification) -> None:
|
||||
pass
|
272
tools/net/sunrpc/xdrgen/generators/pointer.py
Normal file
272
tools/net/sunrpc/xdrgen/generators/pointer.py
Normal file
@ -0,0 +1,272 @@
|
||||
#!/usr/bin/env python3
|
||||
# ex: set filetype=python:
|
||||
|
||||
"""Generate code to handle XDR pointer types"""
|
||||
|
||||
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 _XdrFixedLengthOpaque, _XdrVariableLengthOpaque
|
||||
from xdr_ast import _XdrFixedLengthArray, _XdrVariableLengthArray
|
||||
from xdr_ast import _XdrOptionalData, _XdrPointer, _XdrDeclaration
|
||||
from xdr_ast import public_apis
|
||||
|
||||
|
||||
def emit_pointer_declaration(environment: Environment, node: _XdrPointer) -> None:
|
||||
"""Emit a declaration pair for an XDR pointer type"""
|
||||
if node.name in public_apis:
|
||||
template = get_jinja2_template(environment, "declaration", "close")
|
||||
print(template.render(name=node.name))
|
||||
|
||||
|
||||
def emit_pointer_member_definition(
|
||||
environment: Environment, field: _XdrDeclaration
|
||||
) -> None:
|
||||
"""Emit a definition for one field in an XDR struct"""
|
||||
if isinstance(field, _XdrBasic):
|
||||
template = get_jinja2_template(environment, "definition", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
type=kernel_c_type(field.spec),
|
||||
classifier=field.spec.c_classifier,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrFixedLengthOpaque):
|
||||
template = get_jinja2_template(environment, "definition", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
size=field.size,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrVariableLengthOpaque):
|
||||
template = get_jinja2_template(environment, "definition", field.template)
|
||||
print(template.render(name=field.name))
|
||||
elif isinstance(field, _XdrVariableLengthString):
|
||||
template = get_jinja2_template(environment, "definition", field.template)
|
||||
print(template.render(name=field.name))
|
||||
elif isinstance(field, _XdrFixedLengthArray):
|
||||
template = get_jinja2_template(environment, "definition", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
type=kernel_c_type(field.spec),
|
||||
size=field.size,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrVariableLengthArray):
|
||||
template = get_jinja2_template(environment, "definition", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
type=kernel_c_type(field.spec),
|
||||
classifier=field.spec.c_classifier,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrOptionalData):
|
||||
template = get_jinja2_template(environment, "definition", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
type=kernel_c_type(field.spec),
|
||||
classifier=field.spec.c_classifier,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def emit_pointer_definition(environment: Environment, node: _XdrPointer) -> None:
|
||||
"""Emit a definition for an XDR pointer type"""
|
||||
template = get_jinja2_template(environment, "definition", "open")
|
||||
print(template.render(name=node.name))
|
||||
|
||||
for field in node.fields[0:-1]:
|
||||
emit_pointer_member_definition(environment, field)
|
||||
|
||||
template = get_jinja2_template(environment, "definition", "close")
|
||||
print(template.render(name=node.name))
|
||||
|
||||
|
||||
def emit_pointer_member_decoder(
|
||||
environment: Environment, field: _XdrDeclaration
|
||||
) -> None:
|
||||
"""Emit a decoder for one field in an XDR pointer"""
|
||||
if isinstance(field, _XdrBasic):
|
||||
template = get_jinja2_template(environment, "decoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
type=field.spec.type_name,
|
||||
classifier=field.spec.c_classifier,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrFixedLengthOpaque):
|
||||
template = get_jinja2_template(environment, "decoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
size=field.size,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrVariableLengthOpaque):
|
||||
template = get_jinja2_template(environment, "decoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
maxsize=field.maxsize,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrVariableLengthString):
|
||||
template = get_jinja2_template(environment, "decoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
maxsize=field.maxsize,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrFixedLengthArray):
|
||||
template = get_jinja2_template(environment, "decoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
type=field.spec.type_name,
|
||||
size=field.size,
|
||||
classifier=field.spec.c_classifier,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrVariableLengthArray):
|
||||
template = get_jinja2_template(environment, "decoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
type=field.spec.type_name,
|
||||
maxsize=field.maxsize,
|
||||
classifier=field.spec.c_classifier,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrOptionalData):
|
||||
template = get_jinja2_template(environment, "decoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
type=field.spec.type_name,
|
||||
classifier=field.spec.c_classifier,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def emit_pointer_decoder(environment: Environment, node: _XdrPointer) -> None:
|
||||
"""Emit one decoder function for an XDR pointer type"""
|
||||
template = get_jinja2_template(environment, "decoder", "open")
|
||||
print(template.render(name=node.name))
|
||||
|
||||
for field in node.fields[0:-1]:
|
||||
emit_pointer_member_decoder(environment, field)
|
||||
|
||||
template = get_jinja2_template(environment, "decoder", "close")
|
||||
print(template.render())
|
||||
|
||||
|
||||
def emit_pointer_member_encoder(
|
||||
environment: Environment, field: _XdrDeclaration
|
||||
) -> None:
|
||||
"""Emit an encoder for one field in a XDR pointer"""
|
||||
if isinstance(field, _XdrBasic):
|
||||
template = get_jinja2_template(environment, "encoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
type=field.spec.type_name,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrFixedLengthOpaque):
|
||||
template = get_jinja2_template(environment, "encoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
size=field.size,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrVariableLengthOpaque):
|
||||
template = get_jinja2_template(environment, "encoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
maxsize=field.maxsize,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrVariableLengthString):
|
||||
template = get_jinja2_template(environment, "encoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
maxsize=field.maxsize,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrFixedLengthArray):
|
||||
template = get_jinja2_template(environment, "encoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
type=field.spec.type_name,
|
||||
size=field.size,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrVariableLengthArray):
|
||||
template = get_jinja2_template(environment, "encoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
type=field.spec.type_name,
|
||||
maxsize=field.maxsize,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrOptionalData):
|
||||
template = get_jinja2_template(environment, "encoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
type=field.spec.type_name,
|
||||
classifier=field.spec.c_classifier,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def emit_pointer_encoder(environment: Environment, node: _XdrPointer) -> None:
|
||||
"""Emit one encoder function for an XDR pointer type"""
|
||||
template = get_jinja2_template(environment, "encoder", "open")
|
||||
print(template.render(name=node.name))
|
||||
|
||||
for field in node.fields[0:-1]:
|
||||
emit_pointer_member_encoder(environment, field)
|
||||
|
||||
template = get_jinja2_template(environment, "encoder", "close")
|
||||
print(template.render())
|
||||
|
||||
|
||||
class XdrPointerGenerator(SourceGenerator):
|
||||
"""Generate source code for XDR pointer"""
|
||||
|
||||
def __init__(self, language: str, peer: str):
|
||||
"""Initialize an instance of this class"""
|
||||
self.environment = create_jinja2_environment(language, "pointer")
|
||||
self.peer = peer
|
||||
|
||||
def emit_declaration(self, node: _XdrPointer) -> None:
|
||||
"""Emit one declaration pair for an XDR pointer type"""
|
||||
emit_pointer_declaration(self.environment, node)
|
||||
|
||||
def emit_definition(self, node: _XdrPointer) -> None:
|
||||
"""Emit one declaration for an XDR pointer type"""
|
||||
emit_pointer_definition(self.environment, node)
|
||||
|
||||
def emit_decoder(self, node: _XdrPointer) -> None:
|
||||
"""Emit one decoder function for an XDR pointer type"""
|
||||
emit_pointer_decoder(self.environment, node)
|
||||
|
||||
def emit_encoder(self, node: _XdrPointer) -> None:
|
||||
"""Emit one encoder function for an XDR pointer type"""
|
||||
emit_pointer_encoder(self.environment, node)
|
168
tools/net/sunrpc/xdrgen/generators/program.py
Normal file
168
tools/net/sunrpc/xdrgen/generators/program.py
Normal file
@ -0,0 +1,168 @@
|
||||
#!/usr/bin/env python3
|
||||
# ex: set filetype=python:
|
||||
|
||||
"""Generate code for an RPC program's procedures"""
|
||||
|
||||
from jinja2 import Environment
|
||||
|
||||
from generators import SourceGenerator, create_jinja2_environment
|
||||
from xdr_ast import _RpcProgram, _RpcVersion, excluded_apis
|
||||
|
||||
|
||||
def emit_version_definitions(
|
||||
environment: Environment, program: str, version: _RpcVersion
|
||||
) -> None:
|
||||
"""Emit procedure numbers for each RPC version's procedures"""
|
||||
template = environment.get_template("definition/open.j2")
|
||||
print(template.render(program=program.upper()))
|
||||
|
||||
template = environment.get_template("definition/procedure.j2")
|
||||
for procedure in version.procedures:
|
||||
if procedure.name not in excluded_apis:
|
||||
print(
|
||||
template.render(
|
||||
name=procedure.name,
|
||||
value=procedure.number,
|
||||
)
|
||||
)
|
||||
|
||||
template = environment.get_template("definition/close.j2")
|
||||
print(template.render())
|
||||
|
||||
|
||||
def emit_version_declarations(
|
||||
environment: Environment, program: str, version: _RpcVersion
|
||||
) -> None:
|
||||
"""Emit declarations for each RPC version's procedures"""
|
||||
arguments = dict.fromkeys([])
|
||||
for procedure in version.procedures:
|
||||
if procedure.name not in excluded_apis:
|
||||
arguments[procedure.argument.type_name] = None
|
||||
if len(arguments) > 0:
|
||||
print("")
|
||||
template = environment.get_template("declaration/argument.j2")
|
||||
for argument in arguments:
|
||||
print(template.render(program=program, argument=argument))
|
||||
|
||||
results = dict.fromkeys([])
|
||||
for procedure in version.procedures:
|
||||
if procedure.name not in excluded_apis:
|
||||
results[procedure.result.type_name] = None
|
||||
if len(results) > 0:
|
||||
print("")
|
||||
template = environment.get_template("declaration/result.j2")
|
||||
for result in results:
|
||||
print(template.render(program=program, result=result))
|
||||
|
||||
|
||||
def emit_version_argument_decoders(
|
||||
environment: Environment, program: str, version: _RpcVersion
|
||||
) -> None:
|
||||
"""Emit server argument decoders for each RPC version's procedures"""
|
||||
arguments = dict.fromkeys([])
|
||||
for procedure in version.procedures:
|
||||
if procedure.name not in excluded_apis:
|
||||
arguments[procedure.argument.type_name] = None
|
||||
|
||||
template = environment.get_template("decoder/argument.j2")
|
||||
for argument in arguments:
|
||||
print(template.render(program=program, argument=argument))
|
||||
|
||||
|
||||
def emit_version_result_decoders(
|
||||
environment: Environment, program: str, version: _RpcVersion
|
||||
) -> None:
|
||||
"""Emit client result decoders for each RPC version's procedures"""
|
||||
results = dict.fromkeys([])
|
||||
for procedure in version.procedures:
|
||||
if procedure.name not in excluded_apis:
|
||||
results[procedure.result.type_name] = None
|
||||
|
||||
template = environment.get_template("decoder/result.j2")
|
||||
for result in results:
|
||||
print(template.render(program=program, result=result))
|
||||
|
||||
|
||||
def emit_version_argument_encoders(
|
||||
environment: Environment, program: str, version: _RpcVersion
|
||||
) -> None:
|
||||
"""Emit client argument encoders for each RPC version's procedures"""
|
||||
arguments = dict.fromkeys([])
|
||||
for procedure in version.procedures:
|
||||
if procedure.name not in excluded_apis:
|
||||
arguments[procedure.argument.type_name] = None
|
||||
|
||||
template = environment.get_template("encoder/argument.j2")
|
||||
for argument in arguments:
|
||||
print(template.render(program=program, argument=argument))
|
||||
|
||||
|
||||
def emit_version_result_encoders(
|
||||
environment: Environment, program: str, version: _RpcVersion
|
||||
) -> None:
|
||||
"""Emit server result encoders for each RPC version's procedures"""
|
||||
results = dict.fromkeys([])
|
||||
for procedure in version.procedures:
|
||||
if procedure.name not in excluded_apis:
|
||||
results[procedure.result.type_name] = None
|
||||
|
||||
template = environment.get_template("encoder/result.j2")
|
||||
for result in results:
|
||||
print(template.render(program=program, result=result))
|
||||
|
||||
|
||||
class XdrProgramGenerator(SourceGenerator):
|
||||
"""Generate source code for an RPC program's procedures"""
|
||||
|
||||
def __init__(self, language: str, peer: str):
|
||||
"""Initialize an instance of this class"""
|
||||
self.environment = create_jinja2_environment(language, "program")
|
||||
self.peer = peer
|
||||
|
||||
def emit_definition(self, node: _RpcProgram) -> None:
|
||||
"""Emit procedure numbers for each of an RPC programs's procedures"""
|
||||
raw_name = node.name
|
||||
program = raw_name.lower().removesuffix("_program").removesuffix("_prog")
|
||||
|
||||
for version in node.versions:
|
||||
emit_version_definitions(self.environment, program, version)
|
||||
|
||||
def emit_declaration(self, node: _RpcProgram) -> None:
|
||||
"""Emit a declaration pair for each of an RPC programs's procedures"""
|
||||
raw_name = node.name
|
||||
program = raw_name.lower().removesuffix("_program").removesuffix("_prog")
|
||||
|
||||
for version in node.versions:
|
||||
emit_version_declarations(self.environment, program, version)
|
||||
|
||||
def emit_decoder(self, node: _RpcProgram) -> None:
|
||||
"""Emit all decoder functions for an RPC program's procedures"""
|
||||
raw_name = node.name
|
||||
program = raw_name.lower().removesuffix("_program").removesuffix("_prog")
|
||||
match self.peer:
|
||||
case "server":
|
||||
for version in node.versions:
|
||||
emit_version_argument_decoders(
|
||||
self.environment, program, version,
|
||||
)
|
||||
case "client":
|
||||
for version in node.versions:
|
||||
emit_version_result_decoders(
|
||||
self.environment, program, version,
|
||||
)
|
||||
|
||||
def emit_encoder(self, node: _RpcProgram) -> None:
|
||||
"""Emit all encoder functions for an RPC program's procedures"""
|
||||
raw_name = node.name
|
||||
program = raw_name.lower().removesuffix("_program").removesuffix("_prog")
|
||||
match self.peer:
|
||||
case "server":
|
||||
for version in node.versions:
|
||||
emit_version_result_encoders(
|
||||
self.environment, program, version,
|
||||
)
|
||||
case "client":
|
||||
for version in node.versions:
|
||||
emit_version_argument_encoders(
|
||||
self.environment, program, version,
|
||||
)
|
32
tools/net/sunrpc/xdrgen/generators/source_top.py
Normal file
32
tools/net/sunrpc/xdrgen/generators/source_top.py
Normal file
@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python3
|
||||
# ex: set filetype=python:
|
||||
|
||||
"""Generate source code boilerplate"""
|
||||
|
||||
import os.path
|
||||
import time
|
||||
|
||||
from generators import Boilerplate
|
||||
from generators import find_xdr_program_name, create_jinja2_environment
|
||||
from xdr_ast import _RpcProgram, Specification, get_header_name
|
||||
|
||||
|
||||
class XdrSourceTopGenerator(Boilerplate):
|
||||
"""Generate source code boilerplate"""
|
||||
|
||||
def __init__(self, language: str, peer: str):
|
||||
"""Initialize an instance of this class"""
|
||||
self.environment = create_jinja2_environment(language, "source_top")
|
||||
self.peer = peer
|
||||
|
||||
def emit_source(self, filename: str, root: Specification) -> None:
|
||||
"""Emit the top source boilerplate"""
|
||||
name = find_xdr_program_name(root)
|
||||
template = self.environment.get_template(self.peer + ".j2")
|
||||
print(
|
||||
template.render(
|
||||
program=name,
|
||||
filename=filename,
|
||||
mtime=time.ctime(os.path.getmtime(filename)),
|
||||
)
|
||||
)
|
272
tools/net/sunrpc/xdrgen/generators/struct.py
Normal file
272
tools/net/sunrpc/xdrgen/generators/struct.py
Normal file
@ -0,0 +1,272 @@
|
||||
#!/usr/bin/env python3
|
||||
# ex: set filetype=python:
|
||||
|
||||
"""Generate code to handle XDR struct types"""
|
||||
|
||||
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 _XdrFixedLengthOpaque, _XdrVariableLengthOpaque
|
||||
from xdr_ast import _XdrFixedLengthArray, _XdrVariableLengthArray
|
||||
from xdr_ast import _XdrOptionalData, _XdrStruct, _XdrDeclaration
|
||||
from xdr_ast import public_apis
|
||||
|
||||
|
||||
def emit_struct_declaration(environment: Environment, node: _XdrStruct) -> None:
|
||||
"""Emit one declaration pair for an XDR struct type"""
|
||||
if node.name in public_apis:
|
||||
template = get_jinja2_template(environment, "declaration", "close")
|
||||
print(template.render(name=node.name))
|
||||
|
||||
|
||||
def emit_struct_member_definition(
|
||||
environment: Environment, field: _XdrDeclaration
|
||||
) -> None:
|
||||
"""Emit a definition for one field in an XDR struct"""
|
||||
if isinstance(field, _XdrBasic):
|
||||
template = get_jinja2_template(environment, "definition", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
type=kernel_c_type(field.spec),
|
||||
classifier=field.spec.c_classifier,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrFixedLengthOpaque):
|
||||
template = get_jinja2_template(environment, "definition", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
size=field.size,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrVariableLengthOpaque):
|
||||
template = get_jinja2_template(environment, "definition", field.template)
|
||||
print(template.render(name=field.name))
|
||||
elif isinstance(field, _XdrVariableLengthString):
|
||||
template = get_jinja2_template(environment, "definition", field.template)
|
||||
print(template.render(name=field.name))
|
||||
elif isinstance(field, _XdrFixedLengthArray):
|
||||
template = get_jinja2_template(environment, "definition", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
type=kernel_c_type(field.spec),
|
||||
size=field.size,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrVariableLengthArray):
|
||||
template = get_jinja2_template(environment, "definition", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
type=kernel_c_type(field.spec),
|
||||
classifier=field.spec.c_classifier,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrOptionalData):
|
||||
template = get_jinja2_template(environment, "definition", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
type=kernel_c_type(field.spec),
|
||||
classifier=field.spec.c_classifier,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def emit_struct_definition(environment: Environment, node: _XdrStruct) -> None:
|
||||
"""Emit one definition for an XDR struct type"""
|
||||
template = get_jinja2_template(environment, "definition", "open")
|
||||
print(template.render(name=node.name))
|
||||
|
||||
for field in node.fields:
|
||||
emit_struct_member_definition(environment, field)
|
||||
|
||||
template = get_jinja2_template(environment, "definition", "close")
|
||||
print(template.render(name=node.name))
|
||||
|
||||
|
||||
def emit_struct_member_decoder(
|
||||
environment: Environment, field: _XdrDeclaration
|
||||
) -> None:
|
||||
"""Emit a decoder for one field in an XDR struct"""
|
||||
if isinstance(field, _XdrBasic):
|
||||
template = get_jinja2_template(environment, "decoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
type=field.spec.type_name,
|
||||
classifier=field.spec.c_classifier,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrFixedLengthOpaque):
|
||||
template = get_jinja2_template(environment, "decoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
size=field.size,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrVariableLengthOpaque):
|
||||
template = get_jinja2_template(environment, "decoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
maxsize=field.maxsize,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrVariableLengthString):
|
||||
template = get_jinja2_template(environment, "decoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
maxsize=field.maxsize,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrFixedLengthArray):
|
||||
template = get_jinja2_template(environment, "decoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
type=field.spec.type_name,
|
||||
size=field.size,
|
||||
classifier=field.spec.c_classifier,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrVariableLengthArray):
|
||||
template = get_jinja2_template(environment, "decoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
type=field.spec.type_name,
|
||||
maxsize=field.maxsize,
|
||||
classifier=field.spec.c_classifier,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrOptionalData):
|
||||
template = get_jinja2_template(environment, "decoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
type=field.spec.type_name,
|
||||
classifier=field.spec.c_classifier,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def emit_struct_decoder(environment: Environment, node: _XdrStruct) -> None:
|
||||
"""Emit one decoder function for an XDR struct type"""
|
||||
template = get_jinja2_template(environment, "decoder", "open")
|
||||
print(template.render(name=node.name))
|
||||
|
||||
for field in node.fields:
|
||||
emit_struct_member_decoder(environment, field)
|
||||
|
||||
template = get_jinja2_template(environment, "decoder", "close")
|
||||
print(template.render())
|
||||
|
||||
|
||||
def emit_struct_member_encoder(
|
||||
environment: Environment, field: _XdrDeclaration
|
||||
) -> None:
|
||||
"""Emit an encoder for one field in an XDR struct"""
|
||||
if isinstance(field, _XdrBasic):
|
||||
template = get_jinja2_template(environment, "encoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
type=field.spec.type_name,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrFixedLengthOpaque):
|
||||
template = get_jinja2_template(environment, "encoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
size=field.size,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrVariableLengthOpaque):
|
||||
template = get_jinja2_template(environment, "encoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
maxsize=field.maxsize,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrVariableLengthString):
|
||||
template = get_jinja2_template(environment, "encoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
maxsize=field.maxsize,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrFixedLengthArray):
|
||||
template = get_jinja2_template(environment, "encoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
type=field.spec.type_name,
|
||||
size=field.size,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrVariableLengthArray):
|
||||
template = get_jinja2_template(environment, "encoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
type=field.spec.type_name,
|
||||
maxsize=field.maxsize,
|
||||
)
|
||||
)
|
||||
elif isinstance(field, _XdrOptionalData):
|
||||
template = get_jinja2_template(environment, "encoder", field.template)
|
||||
print(
|
||||
template.render(
|
||||
name=field.name,
|
||||
type=field.spec.type_name,
|
||||
classifier=field.spec.c_classifier,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def emit_struct_encoder(environment: Environment, node: _XdrStruct) -> None:
|
||||
"""Emit one encoder function for an XDR struct type"""
|
||||
template = get_jinja2_template(environment, "encoder", "open")
|
||||
print(template.render(name=node.name))
|
||||
|
||||
for field in node.fields:
|
||||
emit_struct_member_encoder(environment, field)
|
||||
|
||||
template = get_jinja2_template(environment, "encoder", "close")
|
||||
print(template.render())
|
||||
|
||||
|
||||
class XdrStructGenerator(SourceGenerator):
|
||||
"""Generate source code for XDR structs"""
|
||||
|
||||
def __init__(self, language: str, peer: str):
|
||||
"""Initialize an instance of this class"""
|
||||
self.environment = create_jinja2_environment(language, "struct")
|
||||
self.peer = peer
|
||||
|
||||
def emit_declaration(self, node: _XdrStruct) -> None:
|
||||
"""Emit one declaration pair for an XDR struct type"""
|
||||
emit_struct_declaration(self.environment, node)
|
||||
|
||||
def emit_definition(self, node: _XdrStruct) -> None:
|
||||
"""Emit one definition for an XDR struct type"""
|
||||
emit_struct_definition(self.environment, node)
|
||||
|
||||
def emit_decoder(self, node: _XdrStruct) -> None:
|
||||
"""Emit one decoder function for an XDR struct type"""
|
||||
emit_struct_decoder(self.environment, node)
|
||||
|
||||
def emit_encoder(self, node: _XdrStruct) -> None:
|
||||
"""Emit one encoder function for an XDR struct type"""
|
||||
emit_struct_encoder(self.environment, node)
|
255
tools/net/sunrpc/xdrgen/generators/typedef.py
Normal file
255
tools/net/sunrpc/xdrgen/generators/typedef.py
Normal file
@ -0,0 +1,255 @@
|
||||
#!/usr/bin/env python3
|
||||
# ex: set filetype=python:
|
||||
|
||||
"""Generate code to handle XDR typedefs"""
|
||||
|
||||
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 _XdrFixedLengthOpaque, _XdrVariableLengthOpaque
|
||||
from xdr_ast import _XdrFixedLengthArray, _XdrVariableLengthArray
|
||||
from xdr_ast import _XdrOptionalData, _XdrVoid, _XdrDeclaration
|
||||
from xdr_ast import public_apis
|
||||
|
||||
|
||||
def emit_typedef_declaration(environment: Environment, node: _XdrDeclaration) -> None:
|
||||
"""Emit a declaration pair for one XDR typedef"""
|
||||
if node.name not in public_apis:
|
||||
return
|
||||
if isinstance(node, _XdrBasic):
|
||||
template = get_jinja2_template(environment, "declaration", node.template)
|
||||
print(
|
||||
template.render(
|
||||
name=node.name,
|
||||
type=kernel_c_type(node.spec),
|
||||
classifier=node.spec.c_classifier,
|
||||
)
|
||||
)
|
||||
elif isinstance(node, _XdrVariableLengthString):
|
||||
template = get_jinja2_template(environment, "declaration", node.template)
|
||||
print(template.render(name=node.name))
|
||||
elif isinstance(node, _XdrFixedLengthOpaque):
|
||||
template = get_jinja2_template(environment, "declaration", node.template)
|
||||
print(template.render(name=node.name, size=node.size))
|
||||
elif isinstance(node, _XdrVariableLengthOpaque):
|
||||
template = get_jinja2_template(environment, "declaration", node.template)
|
||||
print(template.render(name=node.name))
|
||||
elif isinstance(node, _XdrFixedLengthArray):
|
||||
template = get_jinja2_template(environment, "declaration", node.template)
|
||||
print(
|
||||
template.render(
|
||||
name=node.name,
|
||||
type=node.spec.type_name,
|
||||
size=node.size,
|
||||
)
|
||||
)
|
||||
elif isinstance(node, _XdrVariableLengthArray):
|
||||
template = get_jinja2_template(environment, "declaration", node.template)
|
||||
print(
|
||||
template.render(
|
||||
name=node.name,
|
||||
type=node.spec.type_name,
|
||||
classifier=node.spec.c_classifier,
|
||||
)
|
||||
)
|
||||
elif isinstance(node, _XdrOptionalData):
|
||||
raise NotImplementedError("<optional_data> typedef not yet implemented")
|
||||
elif isinstance(node, _XdrVoid):
|
||||
raise NotImplementedError("<void> typedef not yet implemented")
|
||||
else:
|
||||
raise NotImplementedError("typedef: type not recognized")
|
||||
|
||||
|
||||
def emit_type_definition(environment: Environment, node: _XdrDeclaration) -> None:
|
||||
"""Emit a definition for one XDR typedef"""
|
||||
if isinstance(node, _XdrBasic):
|
||||
template = get_jinja2_template(environment, "definition", node.template)
|
||||
print(
|
||||
template.render(
|
||||
name=node.name,
|
||||
type=kernel_c_type(node.spec),
|
||||
classifier=node.spec.c_classifier,
|
||||
)
|
||||
)
|
||||
elif isinstance(node, _XdrVariableLengthString):
|
||||
template = get_jinja2_template(environment, "definition", node.template)
|
||||
print(template.render(name=node.name))
|
||||
elif isinstance(node, _XdrFixedLengthOpaque):
|
||||
template = get_jinja2_template(environment, "definition", node.template)
|
||||
print(template.render(name=node.name, size=node.size))
|
||||
elif isinstance(node, _XdrVariableLengthOpaque):
|
||||
template = get_jinja2_template(environment, "definition", node.template)
|
||||
print(template.render(name=node.name))
|
||||
elif isinstance(node, _XdrFixedLengthArray):
|
||||
template = get_jinja2_template(environment, "definition", node.template)
|
||||
print(
|
||||
template.render(
|
||||
name=node.name,
|
||||
type=node.spec.type_name,
|
||||
size=node.size,
|
||||
)
|
||||
)
|
||||
elif isinstance(node, _XdrVariableLengthArray):
|
||||
template = get_jinja2_template(environment, "definition", node.template)
|
||||
print(
|
||||
template.render(
|
||||
name=node.name,
|
||||
type=node.spec.type_name,
|
||||
classifier=node.spec.c_classifier,
|
||||
)
|
||||
)
|
||||
elif isinstance(node, _XdrOptionalData):
|
||||
raise NotImplementedError("<optional_data> typedef not yet implemented")
|
||||
elif isinstance(node, _XdrVoid):
|
||||
raise NotImplementedError("<void> typedef not yet implemented")
|
||||
else:
|
||||
raise NotImplementedError("typedef: type not recognized")
|
||||
|
||||
|
||||
def emit_typedef_decoder(environment: Environment, node: _XdrDeclaration) -> None:
|
||||
"""Emit a decoder function for one XDR typedef"""
|
||||
if isinstance(node, _XdrBasic):
|
||||
template = get_jinja2_template(environment, "decoder", node.template)
|
||||
print(
|
||||
template.render(
|
||||
name=node.name,
|
||||
type=node.spec.type_name,
|
||||
)
|
||||
)
|
||||
elif isinstance(node, _XdrVariableLengthString):
|
||||
template = get_jinja2_template(environment, "decoder", node.template)
|
||||
print(
|
||||
template.render(
|
||||
name=node.name,
|
||||
maxsize=node.maxsize,
|
||||
)
|
||||
)
|
||||
elif isinstance(node, _XdrFixedLengthOpaque):
|
||||
template = get_jinja2_template(environment, "decoder", node.template)
|
||||
print(
|
||||
template.render(
|
||||
name=node.name,
|
||||
size=node.size,
|
||||
)
|
||||
)
|
||||
elif isinstance(node, _XdrVariableLengthOpaque):
|
||||
template = get_jinja2_template(environment, "decoder", node.template)
|
||||
print(
|
||||
template.render(
|
||||
name=node.name,
|
||||
maxsize=node.maxsize,
|
||||
)
|
||||
)
|
||||
elif isinstance(node, _XdrFixedLengthArray):
|
||||
template = get_jinja2_template(environment, "decoder", node.template)
|
||||
print(
|
||||
template.render(
|
||||
name=node.name,
|
||||
type=node.spec.type_name,
|
||||
size=node.size,
|
||||
classifier=node.spec.c_classifier,
|
||||
)
|
||||
)
|
||||
elif isinstance(node, _XdrVariableLengthArray):
|
||||
template = get_jinja2_template(environment, "decoder", node.template)
|
||||
print(
|
||||
template.render(
|
||||
name=node.name,
|
||||
type=node.spec.type_name,
|
||||
maxsize=node.maxsize,
|
||||
)
|
||||
)
|
||||
elif isinstance(node, _XdrOptionalData):
|
||||
raise NotImplementedError("<optional_data> typedef not yet implemented")
|
||||
elif isinstance(node, _XdrVoid):
|
||||
raise NotImplementedError("<void> typedef not yet implemented")
|
||||
else:
|
||||
raise NotImplementedError("typedef: type not recognized")
|
||||
|
||||
|
||||
def emit_typedef_encoder(environment: Environment, node: _XdrDeclaration) -> None:
|
||||
"""Emit an encoder function for one XDR typedef"""
|
||||
if isinstance(node, _XdrBasic):
|
||||
template = get_jinja2_template(environment, "encoder", node.template)
|
||||
print(
|
||||
template.render(
|
||||
name=node.name,
|
||||
type=node.spec.type_name,
|
||||
)
|
||||
)
|
||||
elif isinstance(node, _XdrVariableLengthString):
|
||||
template = get_jinja2_template(environment, "encoder", node.template)
|
||||
print(
|
||||
template.render(
|
||||
name=node.name,
|
||||
maxsize=node.maxsize,
|
||||
)
|
||||
)
|
||||
elif isinstance(node, _XdrFixedLengthOpaque):
|
||||
template = get_jinja2_template(environment, "encoder", node.template)
|
||||
print(
|
||||
template.render(
|
||||
name=node.name,
|
||||
size=node.size,
|
||||
)
|
||||
)
|
||||
elif isinstance(node, _XdrVariableLengthOpaque):
|
||||
template = get_jinja2_template(environment, "encoder", node.template)
|
||||
print(
|
||||
template.render(
|
||||
name=node.name,
|
||||
maxsize=node.maxsize,
|
||||
)
|
||||
)
|
||||
elif isinstance(node, _XdrFixedLengthArray):
|
||||
template = get_jinja2_template(environment, "encoder", node.template)
|
||||
print(
|
||||
template.render(
|
||||
name=node.name,
|
||||
type=node.spec.type_name,
|
||||
size=node.size,
|
||||
)
|
||||
)
|
||||
elif isinstance(node, _XdrVariableLengthArray):
|
||||
template = get_jinja2_template(environment, "encoder", node.template)
|
||||
print(
|
||||
template.render(
|
||||
name=node.name,
|
||||
type=node.spec.type_name,
|
||||
maxsize=node.maxsize,
|
||||
)
|
||||
)
|
||||
elif isinstance(node, _XdrOptionalData):
|
||||
raise NotImplementedError("<optional_data> typedef not yet implemented")
|
||||
elif isinstance(node, _XdrVoid):
|
||||
raise NotImplementedError("<void> typedef not yet implemented")
|
||||
else:
|
||||
raise NotImplementedError("typedef: type not recognized")
|
||||
|
||||
|
||||
class XdrTypedefGenerator(SourceGenerator):
|
||||
"""Generate source code for XDR typedefs"""
|
||||
|
||||
def __init__(self, language: str, peer: str):
|
||||
"""Initialize an instance of this class"""
|
||||
self.environment = create_jinja2_environment(language, "typedef")
|
||||
self.peer = peer
|
||||
|
||||
def emit_declaration(self, node: _XdrTypedef) -> None:
|
||||
"""Emit one declaration pair for an XDR enum type"""
|
||||
emit_typedef_declaration(self.environment, node.declaration)
|
||||
|
||||
def emit_definition(self, node: _XdrTypedef) -> None:
|
||||
"""Emit one definition for an XDR typedef"""
|
||||
emit_type_definition(self.environment, node.declaration)
|
||||
|
||||
def emit_decoder(self, node: _XdrTypedef) -> None:
|
||||
"""Emit one decoder function for an XDR typedef"""
|
||||
emit_typedef_decoder(self.environment, node.declaration)
|
||||
|
||||
def emit_encoder(self, node: _XdrTypedef) -> None:
|
||||
"""Emit one encoder function for an XDR typedef"""
|
||||
emit_typedef_encoder(self.environment, node.declaration)
|
243
tools/net/sunrpc/xdrgen/generators/union.py
Normal file
243
tools/net/sunrpc/xdrgen/generators/union.py
Normal file
@ -0,0 +1,243 @@
|
||||
#!/usr/bin/env python3
|
||||
# ex: set filetype=python:
|
||||
|
||||
"""Generate code to handle XDR unions"""
|
||||
|
||||
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
|
||||
|
||||
|
||||
def emit_union_declaration(environment: Environment, node: _XdrUnion) -> None:
|
||||
"""Emit one declaration pair for an XDR union type"""
|
||||
if node.name in public_apis:
|
||||
template = get_jinja2_template(environment, "declaration", "close")
|
||||
print(template.render(name=node.name))
|
||||
|
||||
|
||||
def emit_union_switch_spec_definition(
|
||||
environment: Environment, node: _XdrDeclaration
|
||||
) -> None:
|
||||
"""Emit a definition for an XDR union's discriminant"""
|
||||
assert isinstance(node, _XdrBasic)
|
||||
template = get_jinja2_template(environment, "definition", "switch_spec")
|
||||
print(
|
||||
template.render(
|
||||
name=node.name,
|
||||
type=node.spec.type_name,
|
||||
classifier=node.spec.c_classifier,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def emit_union_case_spec_definition(
|
||||
environment: Environment, node: _XdrDeclaration
|
||||
) -> None:
|
||||
"""Emit a definition for an XDR union's case arm"""
|
||||
if isinstance(node.arm, _XdrVoid):
|
||||
return
|
||||
assert isinstance(node.arm, _XdrBasic)
|
||||
template = get_jinja2_template(environment, "definition", "case_spec")
|
||||
print(
|
||||
template.render(
|
||||
name=node.arm.name,
|
||||
type=node.arm.spec.type_name,
|
||||
classifier=node.arm.spec.c_classifier,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def emit_union_definition(environment: Environment, node: _XdrUnion) -> None:
|
||||
"""Emit one XDR union definition"""
|
||||
template = get_jinja2_template(environment, "definition", "open")
|
||||
print(template.render(name=node.name))
|
||||
|
||||
emit_union_switch_spec_definition(environment, node.discriminant)
|
||||
|
||||
for case in node.cases:
|
||||
emit_union_case_spec_definition(environment, case)
|
||||
|
||||
if node.default is not None:
|
||||
emit_union_case_spec_definition(environment, node.default)
|
||||
|
||||
template = get_jinja2_template(environment, "definition", "close")
|
||||
print(template.render(name=node.name))
|
||||
|
||||
|
||||
def emit_union_switch_spec_decoder(
|
||||
environment: Environment, node: _XdrDeclaration
|
||||
) -> None:
|
||||
"""Emit a decoder for an XDR union's discriminant"""
|
||||
assert isinstance(node, _XdrBasic)
|
||||
template = get_jinja2_template(environment, "decoder", "switch_spec")
|
||||
print(template.render(name=node.name, type=node.spec.type_name))
|
||||
|
||||
|
||||
def emit_union_case_spec_decoder(environment: Environment, node: _XdrCaseSpec) -> 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")
|
||||
for case in node.values:
|
||||
print(template.render(case=case))
|
||||
|
||||
assert isinstance(node.arm, _XdrBasic)
|
||||
template = get_jinja2_template(environment, "decoder", node.arm.template)
|
||||
print(
|
||||
template.render(
|
||||
name=node.arm.name,
|
||||
type=node.arm.spec.type_name,
|
||||
classifier=node.arm.spec.c_classifier,
|
||||
)
|
||||
)
|
||||
|
||||
template = get_jinja2_template(environment, "decoder", "break")
|
||||
print(template.render())
|
||||
|
||||
|
||||
def emit_union_default_spec_decoder(environment: Environment, node: _XdrUnion) -> None:
|
||||
"""Emit a decoder function for an XDR union's default arm"""
|
||||
default_case = node.default
|
||||
|
||||
# Avoid a gcc warning about a default case with boolean discriminant
|
||||
if default_case is None and node.discriminant.spec.type_name == "bool":
|
||||
return
|
||||
|
||||
template = get_jinja2_template(environment, "decoder", "default_spec")
|
||||
print(template.render())
|
||||
|
||||
if default_case is None or isinstance(default_case.arm, _XdrVoid):
|
||||
template = get_jinja2_template(environment, "decoder", "break")
|
||||
print(template.render())
|
||||
return
|
||||
|
||||
assert isinstance(default_case.arm, _XdrBasic)
|
||||
template = get_jinja2_template(environment, "decoder", default_case.arm.template)
|
||||
print(
|
||||
template.render(
|
||||
name=default_case.arm.name,
|
||||
type=default_case.arm.spec.type_name,
|
||||
classifier=default_case.arm.spec.c_classifier,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def emit_union_decoder(environment: Environment, node: _XdrUnion) -> None:
|
||||
"""Emit one XDR union decoder"""
|
||||
template = get_jinja2_template(environment, "decoder", "open")
|
||||
print(template.render(name=node.name))
|
||||
|
||||
emit_union_switch_spec_decoder(environment, node.discriminant)
|
||||
|
||||
for case in node.cases:
|
||||
emit_union_case_spec_decoder(environment, case)
|
||||
|
||||
emit_union_default_spec_decoder(environment, node)
|
||||
|
||||
template = get_jinja2_template(environment, "decoder", "close")
|
||||
print(template.render())
|
||||
|
||||
|
||||
def emit_union_switch_spec_encoder(
|
||||
environment: Environment, node: _XdrDeclaration
|
||||
) -> None:
|
||||
"""Emit an encoder for an XDR union's discriminant"""
|
||||
assert isinstance(node, _XdrBasic)
|
||||
template = get_jinja2_template(environment, "encoder", "switch_spec")
|
||||
print(template.render(name=node.name, type=node.spec.type_name))
|
||||
|
||||
|
||||
def emit_union_case_spec_encoder(environment: Environment, node: _XdrCaseSpec) -> 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")
|
||||
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(
|
||||
name=node.arm.name,
|
||||
type=node.arm.spec.type_name,
|
||||
)
|
||||
)
|
||||
|
||||
template = get_jinja2_template(environment, "encoder", "break")
|
||||
print(template.render())
|
||||
|
||||
|
||||
def emit_union_default_spec_encoder(environment: Environment, node: _XdrUnion) -> None:
|
||||
"""Emit an encoder function for an XDR union's default arm"""
|
||||
default_case = node.default
|
||||
|
||||
# Avoid a gcc warning about a default case with boolean discriminant
|
||||
if default_case is None and node.discriminant.spec.type_name == "bool":
|
||||
return
|
||||
|
||||
template = get_jinja2_template(environment, "encoder", "default_spec")
|
||||
print(template.render())
|
||||
|
||||
if default_case is None or isinstance(default_case.arm, _XdrVoid):
|
||||
template = get_jinja2_template(environment, "encoder", "break")
|
||||
print(template.render())
|
||||
return
|
||||
|
||||
assert isinstance(default_case.arm, _XdrBasic)
|
||||
template = get_jinja2_template(environment, "encoder", default_case.arm.template)
|
||||
print(
|
||||
template.render(
|
||||
name=default_case.arm.name,
|
||||
type=default_case.arm.spec.type_name,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def emit_union_encoder(environment, node: _XdrUnion) -> None:
|
||||
"""Emit one XDR union encoder"""
|
||||
template = get_jinja2_template(environment, "encoder", "open")
|
||||
print(template.render(name=node.name))
|
||||
|
||||
emit_union_switch_spec_encoder(environment, node.discriminant)
|
||||
|
||||
for case in node.cases:
|
||||
emit_union_case_spec_encoder(environment, case)
|
||||
|
||||
emit_union_default_spec_encoder(environment, node)
|
||||
|
||||
template = get_jinja2_template(environment, "encoder", "close")
|
||||
print(template.render())
|
||||
|
||||
|
||||
class XdrUnionGenerator(SourceGenerator):
|
||||
"""Generate source code for XDR unions"""
|
||||
|
||||
def __init__(self, language: str, peer: str):
|
||||
"""Initialize an instance of this class"""
|
||||
self.environment = create_jinja2_environment(language, "union")
|
||||
self.peer = peer
|
||||
|
||||
def emit_declaration(self, node: _XdrUnion) -> None:
|
||||
"""Emit one declaration pair for an XDR union"""
|
||||
emit_union_declaration(self.environment, node)
|
||||
|
||||
def emit_definition(self, node: _XdrUnion) -> None:
|
||||
"""Emit one definition for an XDR union"""
|
||||
emit_union_definition(self.environment, node)
|
||||
|
||||
def emit_decoder(self, node: _XdrUnion) -> None:
|
||||
"""Emit one decoder function for an XDR union"""
|
||||
emit_union_decoder(self.environment, node)
|
||||
|
||||
def emit_encoder(self, node: _XdrUnion) -> None:
|
||||
"""Emit one encoder function for an XDR union"""
|
||||
emit_union_encoder(self.environment, node)
|
119
tools/net/sunrpc/xdrgen/grammars/xdr.lark
Normal file
119
tools/net/sunrpc/xdrgen/grammars/xdr.lark
Normal file
@ -0,0 +1,119 @@
|
||||
// A Lark grammar for the XDR specification language based on
|
||||
// https://tools.ietf.org/html/rfc4506 Section 6.3
|
||||
|
||||
declaration : "opaque" identifier "[" value "]" -> fixed_length_opaque
|
||||
| "opaque" identifier "<" [ value ] ">" -> variable_length_opaque
|
||||
| "string" identifier "<" [ value ] ">" -> variable_length_string
|
||||
| type_specifier identifier "[" value "]" -> fixed_length_array
|
||||
| type_specifier identifier "<" [ value ] ">" -> variable_length_array
|
||||
| type_specifier "*" identifier -> optional_data
|
||||
| type_specifier identifier -> basic
|
||||
| "void" -> void
|
||||
|
||||
value : decimal_constant
|
||||
| hexadecimal_constant
|
||||
| octal_constant
|
||||
| identifier
|
||||
|
||||
constant : decimal_constant | hexadecimal_constant | octal_constant
|
||||
|
||||
type_specifier : unsigned_hyper
|
||||
| unsigned_long
|
||||
| unsigned_int
|
||||
| hyper
|
||||
| long
|
||||
| int
|
||||
| float
|
||||
| double
|
||||
| quadruple
|
||||
| bool
|
||||
| enum_type_spec
|
||||
| struct_type_spec
|
||||
| union_type_spec
|
||||
| identifier
|
||||
|
||||
unsigned_hyper : "unsigned" "hyper"
|
||||
unsigned_long : "unsigned" "long"
|
||||
unsigned_int : "unsigned" "int"
|
||||
hyper : "hyper"
|
||||
long : "long"
|
||||
int : "int"
|
||||
float : "float"
|
||||
double : "double"
|
||||
quadruple : "quadruple"
|
||||
bool : "bool"
|
||||
|
||||
enum_type_spec : "enum" enum_body
|
||||
|
||||
enum_body : "{" ( identifier "=" value ) ( "," identifier "=" value )* "}"
|
||||
|
||||
struct_type_spec : "struct" struct_body
|
||||
|
||||
struct_body : "{" ( declaration ";" )+ "}"
|
||||
|
||||
union_type_spec : "union" union_body
|
||||
|
||||
union_body : switch_spec "{" case_spec+ [ default_spec ] "}"
|
||||
|
||||
switch_spec : "switch" "(" declaration ")"
|
||||
|
||||
case_spec : ( "case" value ":" )+ declaration ";"
|
||||
|
||||
default_spec : "default" ":" declaration ";"
|
||||
|
||||
constant_def : "const" identifier "=" value ";"
|
||||
|
||||
type_def : "typedef" declaration ";" -> typedef
|
||||
| "enum" identifier enum_body ";" -> enum
|
||||
| "struct" identifier struct_body ";" -> struct
|
||||
| "union" identifier union_body ";" -> union
|
||||
|
||||
specification : definition*
|
||||
|
||||
definition : constant_def
|
||||
| type_def
|
||||
| program_def
|
||||
| pragma_def
|
||||
|
||||
//
|
||||
// RPC program definitions not specified in RFC 4506
|
||||
//
|
||||
|
||||
program_def : "program" identifier "{" version_def+ "}" "=" constant ";"
|
||||
|
||||
version_def : "version" identifier "{" procedure_def+ "}" "=" constant ";"
|
||||
|
||||
procedure_def : type_specifier identifier "(" type_specifier ")" "=" constant ";"
|
||||
|
||||
pragma_def : "pragma" directive identifier [ identifier ] ";"
|
||||
|
||||
directive : exclude_directive
|
||||
| header_directive
|
||||
| pages_directive
|
||||
| public_directive
|
||||
| skip_directive
|
||||
|
||||
exclude_directive : "exclude"
|
||||
header_directive : "header"
|
||||
pages_directive : "pages"
|
||||
public_directive : "public"
|
||||
skip_directive : "skip"
|
||||
|
||||
//
|
||||
// XDR language primitives
|
||||
//
|
||||
|
||||
identifier : /([a-z]|[A-Z])(_|[a-z]|[A-Z]|[0-9])*/
|
||||
|
||||
decimal_constant : /[\+-]?(0|[1-9][0-9]*)/
|
||||
hexadecimal_constant : /0x([a-f]|[A-F]|[0-9])+/
|
||||
octal_constant : /0[0-7]+/
|
||||
|
||||
PASSTHRU : "%" | "%" /.+/
|
||||
%ignore PASSTHRU
|
||||
|
||||
%import common.C_COMMENT
|
||||
%ignore C_COMMENT
|
||||
|
||||
%import common.WS
|
||||
%ignore WS
|
2
tools/net/sunrpc/xdrgen/subcmds/__init__.py
Normal file
2
tools/net/sunrpc/xdrgen/subcmds/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Just to make sphinx-apidoc document this directory
|
76
tools/net/sunrpc/xdrgen/subcmds/declarations.py
Normal file
76
tools/net/sunrpc/xdrgen/subcmds/declarations.py
Normal file
@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python3
|
||||
# ex: set filetype=python:
|
||||
|
||||
"""Translate an XDR specification into executable code that
|
||||
can be compiled for the Linux kernel."""
|
||||
|
||||
import logging
|
||||
|
||||
from argparse import Namespace
|
||||
from lark import logger
|
||||
from lark.exceptions import UnexpectedInput
|
||||
|
||||
from generators.constant import XdrConstantGenerator
|
||||
from generators.enum import XdrEnumGenerator
|
||||
from generators.header_bottom import XdrHeaderBottomGenerator
|
||||
from generators.header_top import XdrHeaderTopGenerator
|
||||
from generators.pointer import XdrPointerGenerator
|
||||
from generators.program import XdrProgramGenerator
|
||||
from generators.typedef import XdrTypedefGenerator
|
||||
from generators.struct import XdrStructGenerator
|
||||
from generators.union import XdrUnionGenerator
|
||||
|
||||
from xdr_ast import transform_parse_tree, _RpcProgram, Specification
|
||||
from xdr_ast import _XdrConstant, _XdrEnum, _XdrPointer
|
||||
from xdr_ast import _XdrTypedef, _XdrStruct, _XdrUnion
|
||||
from xdr_parse import xdr_parser, set_xdr_annotate
|
||||
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
|
||||
def emit_header_declarations(
|
||||
root: Specification, language: str, peer: str
|
||||
) -> None:
|
||||
"""Emit header declarations"""
|
||||
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)
|
||||
elif isinstance(definition.value, _RpcProgram):
|
||||
gen = XdrProgramGenerator(language, peer)
|
||||
else:
|
||||
continue
|
||||
gen.emit_declaration(definition.value)
|
||||
|
||||
|
||||
def handle_parse_error(e: UnexpectedInput) -> bool:
|
||||
"""Simple parse error reporting, no recovery attempted"""
|
||||
print(e)
|
||||
return True
|
||||
|
||||
|
||||
def subcmd(args: Namespace) -> int:
|
||||
"""Generate definitions and declarations"""
|
||||
|
||||
set_xdr_annotate(args.annotate)
|
||||
parser = xdr_parser()
|
||||
with open(args.filename, encoding="utf-8") as f:
|
||||
parse_tree = parser.parse(f.read(), on_error=handle_parse_error)
|
||||
ast = transform_parse_tree(parse_tree)
|
||||
|
||||
gen = XdrHeaderTopGenerator(args.language, args.peer)
|
||||
gen.emit_declaration(args.filename, ast)
|
||||
|
||||
emit_header_declarations(ast, args.language, args.peer)
|
||||
|
||||
gen = XdrHeaderBottomGenerator(args.language, args.peer)
|
||||
gen.emit_declaration(args.filename, ast)
|
||||
|
||||
return 0
|
78
tools/net/sunrpc/xdrgen/subcmds/definitions.py
Normal file
78
tools/net/sunrpc/xdrgen/subcmds/definitions.py
Normal file
@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python3
|
||||
# ex: set filetype=python:
|
||||
|
||||
"""Translate an XDR specification into executable code that
|
||||
can be compiled for the Linux kernel."""
|
||||
|
||||
import logging
|
||||
|
||||
from argparse import Namespace
|
||||
from lark import logger
|
||||
from lark.exceptions import UnexpectedInput
|
||||
|
||||
from generators.constant import XdrConstantGenerator
|
||||
from generators.enum import XdrEnumGenerator
|
||||
from generators.header_bottom import XdrHeaderBottomGenerator
|
||||
from generators.header_top import XdrHeaderTopGenerator
|
||||
from generators.pointer import XdrPointerGenerator
|
||||
from generators.program import XdrProgramGenerator
|
||||
from generators.typedef import XdrTypedefGenerator
|
||||
from generators.struct import XdrStructGenerator
|
||||
from generators.union import XdrUnionGenerator
|
||||
|
||||
from xdr_ast import transform_parse_tree, Specification
|
||||
from xdr_ast import _RpcProgram, _XdrConstant, _XdrEnum, _XdrPointer
|
||||
from xdr_ast import _XdrTypedef, _XdrStruct, _XdrUnion
|
||||
from xdr_parse import xdr_parser, set_xdr_annotate
|
||||
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
|
||||
def emit_header_definitions(
|
||||
root: Specification, language: str, peer: str
|
||||
) -> None:
|
||||
"""Emit header definitions"""
|
||||
for definition in root.definitions:
|
||||
if isinstance(definition.value, _XdrConstant):
|
||||
gen = XdrConstantGenerator(language, peer)
|
||||
elif isinstance(definition.value, _XdrEnum):
|
||||
gen = XdrEnumGenerator(language, peer)
|
||||
elif isinstance(definition.value, _XdrPointer):
|
||||
gen = XdrPointerGenerator(language, peer)
|
||||
elif isinstance(definition.value, _RpcProgram):
|
||||
gen = XdrProgramGenerator(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_definition(definition.value)
|
||||
|
||||
|
||||
def handle_parse_error(e: UnexpectedInput) -> bool:
|
||||
"""Simple parse error reporting, no recovery attempted"""
|
||||
print(e)
|
||||
return True
|
||||
|
||||
|
||||
def subcmd(args: Namespace) -> int:
|
||||
"""Generate definitions"""
|
||||
|
||||
set_xdr_annotate(args.annotate)
|
||||
parser = xdr_parser()
|
||||
with open(args.filename, encoding="utf-8") as f:
|
||||
parse_tree = parser.parse(f.read(), on_error=handle_parse_error)
|
||||
ast = transform_parse_tree(parse_tree)
|
||||
|
||||
gen = XdrHeaderTopGenerator(args.language, args.peer)
|
||||
gen.emit_definition(args.filename, ast)
|
||||
|
||||
emit_header_definitions(ast, args.language, args.peer)
|
||||
|
||||
gen = XdrHeaderBottomGenerator(args.language, args.peer)
|
||||
gen.emit_definition(args.filename, ast)
|
||||
|
||||
return 0
|
33
tools/net/sunrpc/xdrgen/subcmds/lint.py
Normal file
33
tools/net/sunrpc/xdrgen/subcmds/lint.py
Normal file
@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env python3
|
||||
# ex: set filetype=python:
|
||||
|
||||
"""Translate an XDR specification into executable code that
|
||||
can be compiled for the Linux kernel."""
|
||||
|
||||
import logging
|
||||
|
||||
from argparse import Namespace
|
||||
from lark import logger
|
||||
from lark.exceptions import UnexpectedInput
|
||||
|
||||
from xdr_parse import xdr_parser
|
||||
from xdr_ast import transform_parse_tree
|
||||
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
|
||||
def handle_parse_error(e: UnexpectedInput) -> bool:
|
||||
"""Simple parse error reporting, no recovery attempted"""
|
||||
print(e)
|
||||
return True
|
||||
|
||||
|
||||
def subcmd(args: Namespace) -> int:
|
||||
"""Lexical and syntax check of an XDR specification"""
|
||||
|
||||
parser = xdr_parser()
|
||||
with open(args.filename, encoding="utf-8") as f:
|
||||
parse_tree = parser.parse(f.read(), on_error=handle_parse_error)
|
||||
transform_parse_tree(parse_tree)
|
||||
|
||||
return 0
|
118
tools/net/sunrpc/xdrgen/subcmds/source.py
Normal file
118
tools/net/sunrpc/xdrgen/subcmds/source.py
Normal file
@ -0,0 +1,118 @@
|
||||
#!/usr/bin/env python3
|
||||
# ex: set filetype=python:
|
||||
|
||||
"""Translate an XDR specification into executable code that
|
||||
can be compiled for the Linux kernel."""
|
||||
|
||||
import logging
|
||||
|
||||
from argparse import Namespace
|
||||
from lark import logger
|
||||
from lark.exceptions import UnexpectedInput
|
||||
|
||||
from generators.source_top import XdrSourceTopGenerator
|
||||
from generators.enum import XdrEnumGenerator
|
||||
from generators.pointer import XdrPointerGenerator
|
||||
from generators.program import XdrProgramGenerator
|
||||
from generators.typedef import XdrTypedefGenerator
|
||||
from generators.struct import XdrStructGenerator
|
||||
from generators.union import XdrUnionGenerator
|
||||
|
||||
from xdr_ast import transform_parse_tree, _RpcProgram, Specification
|
||||
from xdr_ast import _XdrAst, _XdrEnum, _XdrPointer
|
||||
from xdr_ast import _XdrStruct, _XdrTypedef, _XdrUnion
|
||||
|
||||
from xdr_parse import xdr_parser, set_xdr_annotate
|
||||
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
|
||||
def emit_source_decoder(node: _XdrAst, language: str, peer: str) -> None:
|
||||
"""Emit one XDR decoder function for a source file"""
|
||||
if isinstance(node, _XdrEnum):
|
||||
gen = XdrEnumGenerator(language, peer)
|
||||
elif isinstance(node, _XdrPointer):
|
||||
gen = XdrPointerGenerator(language, peer)
|
||||
elif isinstance(node, _XdrTypedef):
|
||||
gen = XdrTypedefGenerator(language, peer)
|
||||
elif isinstance(node, _XdrStruct):
|
||||
gen = XdrStructGenerator(language, peer)
|
||||
elif isinstance(node, _XdrUnion):
|
||||
gen = XdrUnionGenerator(language, peer)
|
||||
elif isinstance(node, _RpcProgram):
|
||||
gen = XdrProgramGenerator(language, peer)
|
||||
else:
|
||||
return
|
||||
gen.emit_decoder(node)
|
||||
|
||||
|
||||
def emit_source_encoder(node: _XdrAst, language: str, peer: str) -> None:
|
||||
"""Emit one XDR encoder function for a source file"""
|
||||
if isinstance(node, _XdrEnum):
|
||||
gen = XdrEnumGenerator(language, peer)
|
||||
elif isinstance(node, _XdrPointer):
|
||||
gen = XdrPointerGenerator(language, peer)
|
||||
elif isinstance(node, _XdrTypedef):
|
||||
gen = XdrTypedefGenerator(language, peer)
|
||||
elif isinstance(node, _XdrStruct):
|
||||
gen = XdrStructGenerator(language, peer)
|
||||
elif isinstance(node, _XdrUnion):
|
||||
gen = XdrUnionGenerator(language, peer)
|
||||
elif isinstance(node, _RpcProgram):
|
||||
gen = XdrProgramGenerator(language, peer)
|
||||
else:
|
||||
return
|
||||
gen.emit_encoder(node)
|
||||
|
||||
|
||||
def generate_server_source(filename: str, root: Specification, language: str) -> None:
|
||||
"""Generate server-side source code"""
|
||||
|
||||
gen = XdrSourceTopGenerator(language, "server")
|
||||
gen.emit_source(filename, root)
|
||||
|
||||
for definition in root.definitions:
|
||||
emit_source_decoder(definition.value, language, "server")
|
||||
for definition in root.definitions:
|
||||
emit_source_encoder(definition.value, language, "server")
|
||||
|
||||
|
||||
def generate_client_source(filename: str, root: Specification, language: str) -> None:
|
||||
"""Generate server-side source code"""
|
||||
|
||||
gen = XdrSourceTopGenerator(language, "client")
|
||||
gen.emit_source(filename, root)
|
||||
|
||||
# cel: todo: client needs XDR size macros
|
||||
|
||||
for definition in root.definitions:
|
||||
emit_source_encoder(definition.value, language, "client")
|
||||
for definition in root.definitions:
|
||||
emit_source_decoder(definition.value, language, "client")
|
||||
|
||||
# cel: todo: client needs PROC macros
|
||||
|
||||
|
||||
def handle_parse_error(e: UnexpectedInput) -> bool:
|
||||
"""Simple parse error reporting, no recovery attempted"""
|
||||
print(e)
|
||||
return True
|
||||
|
||||
|
||||
def subcmd(args: Namespace) -> int:
|
||||
"""Generate encoder and decoder functions"""
|
||||
|
||||
set_xdr_annotate(args.annotate)
|
||||
parser = xdr_parser()
|
||||
with open(args.filename, encoding="utf-8") as f:
|
||||
parse_tree = parser.parse(f.read(), on_error=handle_parse_error)
|
||||
ast = transform_parse_tree(parse_tree)
|
||||
match args.peer:
|
||||
case "server":
|
||||
generate_server_source(args.filename, ast, args.language)
|
||||
case "client":
|
||||
generate_client_source(args.filename, ast, args.language)
|
||||
case _:
|
||||
print("Code generation for", args.peer, "is not yet supported")
|
||||
|
||||
return 0
|
@ -0,0 +1,3 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
|
||||
enum { {{ name }} = {{ value }} };
|
@ -0,0 +1,4 @@
|
||||
{# 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);
|
19
tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum.j2
Normal file
19
tools/net/sunrpc/xdrgen/templates/C/enum/decoder/enum.j2
Normal file
@ -0,0 +1,19 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
|
||||
{% if annotate %}
|
||||
/* enum {{ name }} */
|
||||
{% endif %}
|
||||
{% if name in public_apis %}
|
||||
bool
|
||||
{% else %}
|
||||
static bool __maybe_unused
|
||||
{% endif %}
|
||||
xdrgen_decode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} *ptr)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (xdr_stream_decode_u32(xdr, &val) < 0)
|
||||
return false;
|
||||
*ptr = val;
|
||||
return true;
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
};
|
@ -0,0 +1,2 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
{{ name }} = {{ value }},
|
@ -0,0 +1,3 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
|
||||
enum {{ name }} {
|
14
tools/net/sunrpc/xdrgen/templates/C/enum/encoder/enum.j2
Normal file
14
tools/net/sunrpc/xdrgen/templates/C/enum/encoder/enum.j2
Normal file
@ -0,0 +1,14 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
|
||||
{% if annotate %}
|
||||
/* enum {{ name }} */
|
||||
{% endif %}
|
||||
{% if name in public_apis %}
|
||||
bool
|
||||
{% else %}
|
||||
static bool __maybe_unused
|
||||
{% endif %}
|
||||
xdrgen_encode_{{ name }}(struct xdr_stream *xdr, enum {{ name }} value)
|
||||
{
|
||||
return xdr_stream_encode_u32(xdr, value) == XDR_UNIT;
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
|
||||
#endif /* _LINUX_XDRGEN_{{ infix }}_DECL_H */
|
@ -0,0 +1,3 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
|
||||
#endif /* _LINUX_XDRGEN_{{ infix }}_DEF_H */
|
@ -0,0 +1,14 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Generated by xdrgen. Manual edits will be lost. */
|
||||
/* XDR specification file: {{ filename }} */
|
||||
/* XDR specification modification time: {{ mtime }} */
|
||||
|
||||
#ifndef _LINUX_XDRGEN_{{ infix }}_DECL_H
|
||||
#define _LINUX_XDRGEN_{{ infix }}_DECL_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <linux/sunrpc/xdr.h>
|
||||
#include <linux/sunrpc/xdrgen/_defs.h>
|
||||
#include <linux/sunrpc/xdrgen/_builtins.h>
|
||||
#include <linux/sunrpc/xdrgen/{{ infix.lower() }}.h>
|
@ -0,0 +1,10 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Generated by xdrgen. Manual edits will be lost. */
|
||||
/* XDR specification file: {{ filename }} */
|
||||
/* XDR specification modification time: {{ mtime }} */
|
||||
|
||||
#ifndef _LINUX_XDRGEN_{{ infix }}_DEF_H
|
||||
#define _LINUX_XDRGEN_{{ infix }}_DEF_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/sunrpc/xdrgen/_defs.h>
|
@ -0,0 +1,4 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
|
||||
bool xdrgen_decode_{{ name }}(struct xdr_stream *xdr, struct {{ name }} *ptr);
|
||||
bool xdrgen_encode_{{ name }}(struct xdr_stream *xdr, const struct {{ name }} *value);
|
@ -0,0 +1,6 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
{% if annotate %}
|
||||
/* member {{ name }} (basic) */
|
||||
{% endif %}
|
||||
if (!xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }}))
|
||||
return false;
|
@ -0,0 +1,3 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
return true;
|
||||
};
|
@ -0,0 +1,8 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
{% if annotate %}
|
||||
/* member {{ name }} (fixed-length array) */
|
||||
{% endif %}
|
||||
for (u32 i = 0; i < {{ size }}; i++) {
|
||||
if (xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }}.items[i]) < 0)
|
||||
return false;
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
{% if annotate %}
|
||||
/* member {{ name }} (fixed-length opaque) */
|
||||
{% endif %}
|
||||
if (xdr_stream_decode_opaque_fixed(xdr, ptr->{{ name }}, {{ size }}) < 0)
|
||||
return false;
|
22
tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/open.j2
Normal file
22
tools/net/sunrpc/xdrgen/templates/C/pointer/decoder/open.j2
Normal file
@ -0,0 +1,22 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
|
||||
{% if annotate %}
|
||||
/* pointer {{ name }} */
|
||||
{% endif %}
|
||||
{% if name in public_apis %}
|
||||
bool
|
||||
{% else %}
|
||||
static bool __maybe_unused
|
||||
{% endif %}
|
||||
xdrgen_decode_{{ name }}(struct xdr_stream *xdr, struct {{ name }} *ptr)
|
||||
{
|
||||
bool opted;
|
||||
|
||||
{% if annotate %}
|
||||
/* opted */
|
||||
{% endif %}
|
||||
if (!xdrgen_decode_bool(xdr, &opted))
|
||||
return false;
|
||||
if (!opted)
|
||||
return true;
|
||||
|
@ -0,0 +1,6 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
{% if annotate %}
|
||||
/* member {{ name }} (optional data) */
|
||||
{% endif %}
|
||||
if (!xdrgen_decode_{{ type }}(xdr, ptr->{{ name }}))
|
||||
return false;
|
@ -0,0 +1,13 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
{% if annotate %}
|
||||
/* member {{ name }} (variable-length array) */
|
||||
{% endif %}
|
||||
if (xdr_stream_decode_u32(xdr, &ptr->{{ name }}.count) < 0)
|
||||
return false;
|
||||
{% if maxsize != "0" %}
|
||||
if (ptr->{{ name }}.count > {{ maxsize }})
|
||||
return false;
|
||||
{% endif %}
|
||||
for (u32 i = 0; i < ptr->{{ name }}.count; i++)
|
||||
if (!xdrgen_decode_{{ type }}(xdr, &ptr->{{ name }}.element[i]))
|
||||
return false;
|
@ -0,0 +1,6 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
{% if annotate %}
|
||||
/* member {{ name }} (variable-length opaque) */
|
||||
{% endif %}
|
||||
if (!xdrgen_decode_opaque(xdr, (opaque *)ptr, {{ maxsize }}))
|
||||
return false;
|
@ -0,0 +1,6 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
{% if annotate %}
|
||||
/* member {{ name }} (variable-length string) */
|
||||
{% endif %}
|
||||
if (!xdrgen_decode_string(xdr, (string *)ptr, {{ maxsize }}))
|
||||
return false;
|
@ -0,0 +1,5 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
{% if annotate %}
|
||||
/* (basic) */
|
||||
{% endif %}
|
||||
{{ classifier }}{{ type }} {{ name }};
|
@ -0,0 +1,2 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
};
|
@ -0,0 +1,5 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
{% if annotate %}
|
||||
/* (fixed-length array) */
|
||||
{% endif %}
|
||||
{{ type }} {{ name }}[{{ size }}];
|
@ -0,0 +1,5 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
{% if annotate %}
|
||||
/* (fixed-length opaque) */
|
||||
{% endif %}
|
||||
u8 {{ name }}[{{ size }}];
|
@ -0,0 +1,6 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
|
||||
{% if annotate %}
|
||||
/* pointer {{ name }} */
|
||||
{% endif %}
|
||||
struct {{ name }} {
|
@ -0,0 +1,5 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
{% if annotate %}
|
||||
/* (optional data) */
|
||||
{% endif %}
|
||||
{{ classifier }}{{ type }} *{{ name }};
|
@ -0,0 +1,8 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
{% if annotate %}
|
||||
/* (variable-length array) */
|
||||
{% endif %}
|
||||
struct {
|
||||
u32 count;
|
||||
{{ classifier }}{{ type }} *element;
|
||||
} {{ name }};
|
@ -0,0 +1,5 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
{% if annotate %}
|
||||
/* (variable-length opaque) */
|
||||
{% endif %}
|
||||
opaque {{ name }};
|
@ -0,0 +1,5 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
{% if annotate %}
|
||||
/* (variable-length string) */
|
||||
{% endif %}
|
||||
string {{ name }};
|
10
tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/basic.j2
Normal file
10
tools/net/sunrpc/xdrgen/templates/C/pointer/encoder/basic.j2
Normal file
@ -0,0 +1,10 @@
|
||||
{# SPDX-License-Identifier: GPL-2.0 #}
|
||||
{% if annotate %}
|
||||
/* member {{ name }} (basic) */
|
||||
{% endif %}
|
||||
{% if type in pass_by_reference %}
|
||||
if (!xdrgen_encode_{{ type }}(xdr, &value->{{ name }}))
|
||||
{% else %}
|
||||
if (!xdrgen_encode_{{ type }}(xdr, value->{{ name }}))
|
||||
{% endif %}
|
||||
return false;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user