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:
Linus Torvalds 2024-09-23 12:01:45 -07:00
commit 18ba603446
200 changed files with 4959 additions and 499 deletions

View File

@ -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>

View File

@ -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,

View File

@ -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");

View File

@ -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))

View File

@ -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;

View File

@ -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 */

View File

@ -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;

View File

@ -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,

View File

@ -10,7 +10,7 @@
#define NFSCACHE_H
#include <linux/sunrpc/svc.h>
#include "netns.h"
#include "nfsd.h"
/*
* Representation of a reply cache entry.

View File

@ -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);
}
/*

View File

@ -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 */

View File

@ -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)

View File

@ -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;
};

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.
*/
return nfserr_symlink;
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(&copy->refcount))
return;
atomic_dec(&copy->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, &copy->cp_flags) ?
NFS_FILE_SYNC : NFS_UNSTABLE;
nfsd4_copy_set_sync(copy, sync);
gen_boot_verifier(&copy->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, &copy->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 = &copy->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(&copy->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(&copy->cp_res.cb_stateid, &copy->cp_stateid.cs_stid,
sizeof(copy->cp_res.cb_stateid));
memcpy(&result->cb_stateid, &copy->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;

View File

@ -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

View File

@ -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,84 +8862,76 @@ 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)
break;
nfsd_stats_wdeleg_getattr_inc(nn);
spin_unlock(&ctx->flc_lock);
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;
}
}
if (!ncf->ncf_file_modified &&
(ncf->ncf_initial_cinfo != ncf->ncf_cb_change ||
ncf->ncf_cur_fsize != ncf->ncf_cb_fsize))
ncf->ncf_file_modified = true;
if (ncf->ncf_file_modified) {
int err;
/*
* Per section 10.4.3 of RFC 8881, the server would
* not update the file's metadata with the client's
* modified size
*/
attrs.ia_mtime = attrs.ia_ctime = current_time(inode);
attrs.ia_valid = ATTR_MTIME | ATTR_CTIME | ATTR_DELEG;
inode_lock(inode);
err = notify_change(&nop_mnt_idmap, dentry, &attrs, NULL);
inode_unlock(inode);
if (err) {
nfs4_put_stid(&dp->dl_stid);
return nfserrno(err);
}
ncf->ncf_cur_fsize = ncf->ncf_cb_fsize;
*size = ncf->ncf_cur_fsize;
*modified = true;
}
nfs4_put_stid(&dp->dl_stid);
return 0;
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;
}
if (dp == NULL || dp == NON_NFSD_LEASE ||
dp->dl_recall.cb_clp == *(rqstp->rq_lease_breaker)) {
spin_unlock(&ctx->flc_lock);
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;
}
nfsd_stats_wdeleg_getattr_inc(nn);
refcount_inc(&dp->dl_stid.sc_count);
ncf = &dp->dl_cb_fattr;
nfs4_cb_getattr(&dp->dl_cb_fattr);
spin_unlock(&ctx->flc_lock);
return 0;
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))
goto out_status;
}
if (!ncf->ncf_file_modified &&
(ncf->ncf_initial_cinfo != ncf->ncf_cb_change ||
ncf->ncf_cur_fsize != ncf->ncf_cb_fsize))
ncf->ncf_file_modified = true;
if (ncf->ncf_file_modified) {
int err;
/*
* Per section 10.4.3 of RFC 8881, the server would
* not update the file's metadata with the client's
* modified size
*/
attrs.ia_mtime = attrs.ia_ctime = current_time(inode);
attrs.ia_valid = ATTR_MTIME | ATTR_CTIME | ATTR_DELEG;
inode_lock(inode);
err = notify_change(&nop_mnt_idmap, dentry, &attrs, NULL);
inode_unlock(inode);
if (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 status;
}

View File

@ -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)

View File

@ -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 = {

View File

@ -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] == '.'))

View File

@ -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)
error = nfserr_badhandle;
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,9 +227,7 @@ 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;
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)

View File

@ -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 */

View File

@ -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,10 +362,11 @@ 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,
newfhp->fh_export,
newfhp->fh_dentry,
NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS);
resp->status = nfsd_permission(
&rqstp->rq_cred,
newfhp->fh_export,
newfhp->fh_dentry,
NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS);
if (resp->status && resp->status != nfserr_rofs)
goto out_unlock;
}
@ -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;
}

View File

@ -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);
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;
nn->nfsd_versions[vers] = false;
break;
case NFSD_TEST:
if (nn->nfsd_versions)
return nn->nfsd_versions[vers];
fallthrough;
return nn->nfsd_versions[vers];
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);
}
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);
}
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);
return nn->nfsd4_minorversions[minorversion];
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);
}

View 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;
};
/*

View File

@ -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 = &copy->cp_src_stateid;
const stateid_t *dst_stp = &copy->cp_dst_stateid;
const stateid_t *cb_stp = &copy->cp_res.cb_stateid;
__entry->intra = test_bit(NFSD4_COPY_F_INTRA, &copy->cp_flags);
__entry->async = !test_bit(NFSD4_COPY_F_SYNCHRONOUS, &copy->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, &copy->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 = &copy->cp_src_stateid;
const stateid_t *dst_stp = &copy->cp_dst_stateid;
const stateid_t *cb_stp = &copy->cp_res.cb_stateid;
__entry->status = be32_to_cpu(copy->nfserr);
__entry->intra = test_bit(NFSD4_COPY_F_INTRA, &copy->cp_flags);
__entry->async = !test_bit(NFSD4_COPY_F_SYNCHRONOUS, &copy->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, &copy->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

View File

@ -100,6 +100,7 @@ nfserrno (int errno)
{ nfserr_io, -EUCLEAN },
{ nfserr_perm, -ENOKEY },
{ nfserr_no_grace, -ENOGRACE},
{ nfserr_io, -EBADMSG },
};
int i;
@ -421,8 +422,9 @@ 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,
NFSD_MAY_TRUNC | NFSD_MAY_OWNER_OVERRIDE);
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,10 +1769,7 @@ 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);
err = nfserrno(host_err);
}
dput(dnew);
out_drop_write:
@ -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;
err = nfserr_file_open;
} 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,8 +2496,8 @@ out_unlock:
* Check for a user's access permissions to this inode.
*/
__be32
nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp,
struct dentry *dentry, int acc)
nfsd_permission(struct svc_cred *cred, struct svc_export *exp,
struct dentry *dentry, int acc)
{
struct inode *inode = d_inode(dentry);
int err;
@ -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))

View File

@ -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);

View File

@ -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)

View File

@ -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;

View File

@ -281,15 +281,18 @@ enum nfsstat4 {
/* nfs42 */
NFS4ERR_PARTNER_NOTSUPP = 10088,
NFS4ERR_PARTNER_NO_AUTH = 10089,
NFS4ERR_UNION_NOTSUPP = 10090,
NFS4ERR_OFFLOAD_DENIED = 10091,
NFS4ERR_WRONG_LFS = 10092,
NFS4ERR_BADLABEL = 10093,
NFS4ERR_OFFLOAD_NO_REQS = 10094,
NFS4ERR_UNION_NOTSUPP = 10090,
NFS4ERR_OFFLOAD_DENIED = 10091,
NFS4ERR_WRONG_LFS = 10092,
NFS4ERR_BADLABEL = 10093,
NFS4ERR_OFFLOAD_NO_REQS = 10094,
/* xattr (RFC8276) */
NFS4ERR_NOXATTR = 10095,
NFS4ERR_XATTR2BIG = 10096,
NFS4ERR_NOXATTR = 10095,
NFS4ERR_XATTR2BIG = 10096,
/* can be used for internal errors */
NFS4ERR_FIRST_FREE
};
/* error codes for internal client use */

View File

@ -21,6 +21,7 @@
#include <linux/wait.h>
#include <linux/mm.h>
#include <linux/pagevec.h>
#include <linux/kthread.h>
/*
*
@ -33,9 +34,9 @@
* node traffic on multi-node NUMA NFS servers.
*/
struct svc_pool {
unsigned int sp_id; /* pool id; also node id on NUMA */
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)

View File

@ -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 */

View File

@ -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);

View File

@ -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);

View 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_ */

View 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_ */

View File

@ -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,

View File

@ -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" }, \

View File

@ -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 */

View File

@ -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:
set_bit(SP_VICTIM_REMAINS, &pool->sp_flags);
set_bit(SP_NEED_VICTIM, &pool->sp_flags);
if (!atomic_dec_and_test(&pool->sp_nrthreads))
if (pool && pool->sp_nrthreads) {
set_bit(SP_VICTIM_REMAINS, &pool->sp_flags);
set_bit(SP_NEED_VICTIM, &pool->sp_flags);
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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
View File

@ -0,0 +1,2 @@
__pycache__
generators/__pycache__

View 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

View File

@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0
# Just to make sphinx-apidoc document this directory

View 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")

View 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))

View 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))

View 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

View 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

View 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)

View 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,
)

View 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)),
)
)

View 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)

View 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)

View 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)

View 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

View File

@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0
# Just to make sphinx-apidoc document this directory

View 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

View 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

View 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

View 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

View File

@ -0,0 +1,3 @@
{# SPDX-License-Identifier: GPL-2.0 #}
enum { {{ name }} = {{ value }} };

View File

@ -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);

View 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;
}

View File

@ -0,0 +1,2 @@
{# SPDX-License-Identifier: GPL-2.0 #}
};

View File

@ -0,0 +1,2 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{{ name }} = {{ value }},

View File

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

View 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;
}

View File

@ -0,0 +1,3 @@
{# SPDX-License-Identifier: GPL-2.0 #}
#endif /* _LINUX_XDRGEN_{{ infix }}_DECL_H */

View File

@ -0,0 +1,3 @@
{# SPDX-License-Identifier: GPL-2.0 #}
#endif /* _LINUX_XDRGEN_{{ infix }}_DEF_H */

View File

@ -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>

View File

@ -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>

View File

@ -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);

View File

@ -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;

View File

@ -0,0 +1,3 @@
{# SPDX-License-Identifier: GPL-2.0 #}
return true;
};

View File

@ -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;
}

View File

@ -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;

View 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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -0,0 +1,5 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* (basic) */
{% endif %}
{{ classifier }}{{ type }} {{ name }};

View File

@ -0,0 +1,2 @@
{# SPDX-License-Identifier: GPL-2.0 #}
};

View File

@ -0,0 +1,5 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* (fixed-length array) */
{% endif %}
{{ type }} {{ name }}[{{ size }}];

View File

@ -0,0 +1,5 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* (fixed-length opaque) */
{% endif %}
u8 {{ name }}[{{ size }}];

View File

@ -0,0 +1,6 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* pointer {{ name }} */
{% endif %}
struct {{ name }} {

View File

@ -0,0 +1,5 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* (optional data) */
{% endif %}
{{ classifier }}{{ type }} *{{ name }};

View File

@ -0,0 +1,8 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* (variable-length array) */
{% endif %}
struct {
u32 count;
{{ classifier }}{{ type }} *element;
} {{ name }};

View File

@ -0,0 +1,5 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* (variable-length opaque) */
{% endif %}
opaque {{ name }};

View File

@ -0,0 +1,5 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* (variable-length string) */
{% endif %}
string {{ name }};

View 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