Re: [RFC][PATCH] sunrpc: introduce the simple_rpc_pipefs API

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, 10 Nov 2008, Trond Myklebust wrote:

> On Mon, 2008-11-10 at 10:23 -0500, david m. richter wrote:
> > 	hello,
> > 
> > 	this patch is part of my pNFS/GFS2 work and i'd neglected at 
> > first to send it to the correct mailing list.  i'm a little unsure of how 
> > to broach it, however, since the subsequent patches that then actually 
> > make use of this aren't germane to the list.
> > 
> > 	i'd appreciate any and all criticism -- i've already gotten a 
> > couple good suggestions from someone at EMC who's been trying it out.
> > 
> > 	thanks for your time,
> > 
> > 	d
> > 	.
> > 
> > 
> > 
> > The rpc_pipefs upcall/downcall mechanism is a particularly useful means by
> > which to communicate between the kernel and userspace, and its pipe-oriented
> > nature makes it familiar to Unix programmers.
> > 
> > simple_rpc_pipefs is a small API that simplifies tasks that commonly arise with
> > rpc_pipefs, like making an upcall and blocking the calling thread until a reply
> > is received, among other things.
> > 
> > Note: the one portion of the existing rpc_pipe_fs code that has been changed is
> > that struct rpc_pipe_msg has had a u8 "flags" variable added.  The purpose is to
> > allow callers to indicate when making an upcall that a dynamically allocated
> > upcall message buffer and/or struct rpc_pipe_msg should be automatically freed
> > in the ->destroy_msg() callback; pipefs_generic_destroy_msg() implements this
> > using the new PIPEFS_AUTOFREE_UPCALL_MSG and PIPEFS_AUTOFREE_RPCMSG flags,
> > respectively.
> > 
> > The arguments for not setting those flags in pipefs_hdr_t's "flags" variable is
> > that 1) they would be exposed to userland and 2) are orthogonal to a message's
> > content.
> > ---
> >  include/linux/sunrpc/rpc_pipe_fs.h       |    4 +
> >  include/linux/sunrpc/simple_rpc_pipefs.h |   99 +++++++
> >  net/sunrpc/Makefile                      |    2 +-
> >  net/sunrpc/simple_rpc_pipefs.c           |  424 ++++++++++++++++++++++++++++++
> >  4 files changed, 528 insertions(+), 1 deletions(-)
> >  create mode 100644 include/linux/sunrpc/simple_rpc_pipefs.h
> >  create mode 100644 net/sunrpc/simple_rpc_pipefs.c
> > 
> > diff --git a/include/linux/sunrpc/rpc_pipe_fs.h b/include/linux/sunrpc/rpc_pipe_fs.h
> > index 51b977a..3850d69 100644
> > --- a/include/linux/sunrpc/rpc_pipe_fs.h
> > +++ b/include/linux/sunrpc/rpc_pipe_fs.h
> > @@ -9,6 +9,10 @@ struct rpc_pipe_msg {
> >  	size_t len;
> >  	size_t copied;
> >  	int errno;
> > +#define PIPEFS_AUTOFREE_RPCMSG       0x01 /* ->destroy_msg frees rpc_pipe_msg */
> > +#define PIPEFS_AUTOFREE_RPCMSG_DATA  0x02 /* ->destroy_msg frees rpc_pipe_msg->data */
> > +#define PIPEFS_AUTOFREE_UPCALL_MSG   PIPEFS_AUTOFREE_RPCMSG_DATA
> > +	u8 flags;
> >  };
> >  
> >  struct rpc_pipe_ops {
> > diff --git a/include/linux/sunrpc/simple_rpc_pipefs.h b/include/linux/sunrpc/simple_rpc_pipefs.h
> > new file mode 100644
> > index 0000000..bab94bc
> > --- /dev/null
> > +++ b/include/linux/sunrpc/simple_rpc_pipefs.h
> > @@ -0,0 +1,99 @@
> > +/*
> > + *  linux/fs/gfs2/simple_rpc_pipefs.h
> > + *
> > + *  Copyright (c) 2008 The Regents of the University of Michigan.
> > + *  All rights reserved.
> > + *
> > + *  David M. Richter <richterd@xxxxxxxxxxxxxx>
> > + *
> > + *  Drawing on work done by Andy Adamson <andros@xxxxxxxxxxxxxx> and
> > + *  Marius Eriksen <marius@xxxxxxxxxx>.  Thanks for the help over the
> > + *  years, guys.
> > + *
> > + *  Redistribution and use in source and binary forms, with or without
> > + *  modification, are permitted provided that the following conditions
> > + *  are met:
> > + *
> > + *  1. Redistributions of source code must retain the above copyright
> > + *     notice, this list of conditions and the following disclaimer.
> > + *  2. Redistributions in binary form must reproduce the above copyright
> > + *     notice, this list of conditions and the following disclaimer in the
> > + *     documentation and/or other materials provided with the distribution.
> > + *  3. Neither the name of the University nor the names of its
> > + *     contributors may be used to endorse or promote products derived
> > + *     from this software without specific prior written permission.
> > + *
> > + *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> > + *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> > + *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
> > + *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
> > + *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
> > + *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
> > + *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
> > + *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
> > + *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
> > + *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
> > + *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> > + *
> > + *  With thanks to CITI's project sponsor and partner, IBM.
> > + */
> > +
> > +#ifndef _SIMPLE_RPC_PIPEFS_H_
> > +#define _SIMPLE_RPC_PIPEFS_H_
> > +
> > +#include <linux/fs.h>
> > +#include <linux/list.h>
> > +#include <linux/mount.h>
> > +#include <linux/sched.h>
> > +#include <linux/sunrpc/clnt.h>
> > +#include <linux/sunrpc/rpc_pipe_fs.h>
> > +
> > +
> > +#define payload_of(headerp)  ((void *)(headerp + 1))
> > +
> > +/*
> > + * pipefs_hdr_t -- the generic message format for simple_rpc_pipefs.  Messages
> > + * may simply be the header itself, although having an optional data payload
> > + * follow the header allows much more flexibility.
> > + *
> > + * Messages are created using pipefs_alloc_init_msg() and
> > + * pipefs_alloc_init_msg_padded(), both of which accept a pointer to an
> > + * (optional) data payload.
> > + *
> > + * Given a pipefs_hdr_t *msg that has a struct foo payload, the data can be
> > + * accessed using: struct foo *foop = payload_of(msg)
> > + */
> > +typedef struct pipefs_hdr {
> > +	u32 msgid;
> > +	u8  type;
> > +	u8  flags;
> > +	u16 totallen; /* length of entire message, including hdr itself */
> > +	u32 status;
> > +} pipefs_hdr_t;
> > +
> > +/*
> > + * pipefs_list_t -- a type of list used for tracking callers who've made an
> > + * upcall and are blocked waiting for a reply.
> > + *
> > + * See pipefs_queue_upcall_waitreply() and pipefs_assign_upcall_reply().
> > + */
> > +typedef struct pipefs_list {
> > +	struct list_head list;
> > +	spinlock_t list_lock;
> > +} pipefs_list_t;
> 
> No thank you! These are structs, and hiding that using a typedef is a
> sure fire way to ensure that someone will one day decide to try to pass
> one by value.

	ooh, touché!  that's a great point; thx. 


> Please see the CodingStyle doc on this issue...
> 
> > +
> > +/* See net/sunrpc/simple_rpc_pipefs.c for more info on using these functions. */
> > +extern struct dentry* pipefs_mkpipe(const char *name, struct rpc_pipe_ops *ops, int wait_for_open);
> > +extern void pipefs_closepipe(struct dentry *pipe);
> > +extern void pipefs_init_list(pipefs_list_t *list);
> > +extern pipefs_hdr_t* pipefs_alloc_init_msg(u32 msgid, u8 type, u8 flags, void *data, u16 datalen);
> > +extern pipefs_hdr_t* pipefs_alloc_init_msg_padded(u32 msgid, u8 type, u8 flags, void *data, u16 datalen, u16 padlen);
> > +extern pipefs_hdr_t* pipefs_queue_upcall_waitreply(struct dentry *pipe, pipefs_hdr_t *msg, pipefs_list_t *uplist, u8 upflags, u32 timeout);
> > +extern int pipefs_queue_upcall_noreply(struct dentry *pipe, pipefs_hdr_t *msg, u8 upflags);
> > +extern int pipefs_assign_upcall_reply(pipefs_hdr_t *reply, pipefs_list_t *uplist);
> > +extern pipefs_hdr_t* pipefs_readmsg(struct file *filp, const char __user *src, size_t len);
> > +extern ssize_t pipefs_generic_upcall(struct file *filp, struct rpc_pipe_msg *rpcmsg, char __user *dst, size_t buflen);
> > +extern void pipefs_generic_destroy_msg(struct rpc_pipe_msg *rpcmsg);
> > +
> > +#endif /* _SIMPLE_RPC_PIPEFS_H_ */
> > diff --git a/net/sunrpc/Makefile b/net/sunrpc/Makefile
> > index db73fd2..bf44221 100644
> > --- a/net/sunrpc/Makefile
> > +++ b/net/sunrpc/Makefile
> > @@ -12,7 +12,7 @@ sunrpc-y := clnt.o xprt.o socklib.o xprtsock.o sched.o \
> >  	    svc.o svcsock.o svcauth.o svcauth_unix.o \
> >  	    rpcb_clnt.o timer.o xdr.o \
> >  	    sunrpc_syms.o cache.o rpc_pipe.o \
> > -	    svc_xprt.o
> > +	    svc_xprt.o simple_rpc_pipefs.o
> >  sunrpc-$(CONFIG_NFS_V4_1) += backchannel_rqst.o bc_svc.o
> >  sunrpc-$(CONFIG_PROC_FS) += stats.o
> >  sunrpc-$(CONFIG_SYSCTL) += sysctl.o
> > diff --git a/net/sunrpc/simple_rpc_pipefs.c b/net/sunrpc/simple_rpc_pipefs.c
> > new file mode 100644
> > index 0000000..bd95958
> > --- /dev/null
> > +++ b/net/sunrpc/simple_rpc_pipefs.c
> > @@ -0,0 +1,424 @@
> > +/*
> > + *  net/sunrpc/simple_rpc_pipefs.c
> > + *
> > + *  Copyright (c) 2008 The Regents of the University of Michigan.
> > + *  All rights reserved.
> > + *
> > + *  David M. Richter <richterd@xxxxxxxxxxxxxx>
> > + *
> > + *  Drawing on work done by Andy Adamson <andros@xxxxxxxxxxxxxx> and
> > + *  Marius Eriksen <marius@xxxxxxxxxx>.  Thanks for the help over the
> > + *  years, guys.
> > + *
> > + *  Redistribution and use in source and binary forms, with or without
> > + *  modification, are permitted provided that the following conditions
> > + *  are met:
> > + *
> > + *  1. Redistributions of source code must retain the above copyright
> > + *     notice, this list of conditions and the following disclaimer.
> > + *  2. Redistributions in binary form must reproduce the above copyright
> > + *     notice, this list of conditions and the following disclaimer in the
> > + *     documentation and/or other materials provided with the distribution.
> > + *  3. Neither the name of the University nor the names of its
> > + *     contributors may be used to endorse or promote products derived
> > + *     from this software without specific prior written permission.
> > + *
> > + *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
> > + *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
> > + *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
> > + *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
> > + *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
> > + *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
> > + *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
> > + *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
> > + *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
> > + *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
> > + *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> > + *
> > + *  With thanks to CITI's project sponsor and partner, IBM.
> > + */
> > +
> > +#include <linux/completion.h>
> > +#include <linux/uaccess.h>
> > +#include <linux/module.h>
> > +#include <linux/sunrpc/simple_rpc_pipefs.h>
> > +
> > +
> > +/*
> > + * Make an rpc_pipefs pipe named @name at the root of the mounted rpc_pipefs
> > + * filesystem.
> > + *
> > + * If @wait_for_open is non-zero and an upcall is later queued but the userland
> > + * end of the pipe has not yet been opened, the upcall will remain queued until
> > + * the pipe is opened; otherwise, the upcall queueing will return with -EPIPE.
> > + */
> > +struct dentry* pipefs_mkpipe(const char *name, struct rpc_pipe_ops *ops,
> > +			     int wait_for_open)
> > +{
> > +	struct dentry *dir, *pipe;
> > +	struct vfsmount *mnt;
> > +
> > +	mnt = rpc_get_mount();
> > +	if (IS_ERR(mnt)) {
> > +		pipe = ERR_CAST(mnt);
> > +		goto out;
> > +	}
> > +	dir = mnt->mnt_root;
> > +	if (!dir) {
> > +		pipe = ERR_PTR(-ENOENT);
> > +		goto out;
> > +	}
> > +	pipe = rpc_mkpipe(dir, name, NULL, ops,
> > +			  wait_for_open ? RPC_PIPE_WAIT_FOR_OPEN : 0);
> > +out:
> > +	return pipe;
> > +}
> > +EXPORT_SYMBOL(pipefs_mkpipe);
> > +
> > +/*
> > + * Shutdown a pipe made by pipefs_mkpipe().
> > + * XXX: do we need to retain an extra reference on the mount?
> > + */
> > +void pipefs_closepipe(struct dentry *pipe)
> > +{
> > +	rpc_unlink(pipe);
> > +	rpc_put_mount();
> > +}
> > +EXPORT_SYMBOL(pipefs_closepipe);
> > +
> > +/*
> > + * Initialize a pipefs_list_t -- which are a way to keep track of callers
> > + * who're blocked having made an upcall and are awaiting a reply.
> > + *
> > + * See pipefs_queue_upcall_waitreply() and pipefs_find_upcall_msgid() for how
> > + * to use them.
> > + */
> > +inline void pipefs_init_list(pipefs_list_t *list)
> > +{
> > +	INIT_LIST_HEAD(&list->list);
> > +	spin_lock_init(&list->list_lock);
> > +}
> > +EXPORT_SYMBOL(pipefs_init_list);
> > +
> > +/*
> > + * Alloc/init a generic pipefs message header and copy into its message body
> > + * an arbitrary data payload.
> > + *
> > + * pipefs_hdr_t's are meant to serve as generic, general-purpose message
> > + * headers for easy rpc_pipefs I/O.  When an upcall is made, the
> > + * pipefs_hdr_t is assigned to a struct rpc_pipe_msg and delivered
> > + * therein.  --And yes, the naming can seem a little confusing at first:
> > + *
> > + * When one thinks of an upcall "message", in simple_rpc_pipefs that's a
> > + * pipefs_hdr_t (possibly with an attached message body).  A
> > + * struct rpc_pipe_msg is actually only the -vehicle- by which the "real"
> > + * message is delivered and processed.
> > + */
> > +pipefs_hdr_t* pipefs_alloc_init_msg_padded(u32 msgid, u8 type, u8 flags,
> > +					   void *data, u16 datalen, u16 padlen)
> > +{
> > +	u16 totallen;
> > +	pipefs_hdr_t *msg = NULL;
> > +
> > +	totallen = sizeof(*msg) + datalen + padlen;
> > +	if (totallen > PAGE_SIZE) {
> > +		msg = ERR_PTR(-E2BIG);
> > +		goto out;
> > +	}
> > +
> > +	msg = kzalloc(totallen, GFP_KERNEL);
> > +	if (!msg) {
> > +		msg = ERR_PTR(-ENOMEM);
> > +		goto out;
> > +	}
> > +
> > +	msg->msgid = msgid;
> > +	msg->type = type;
> > +	msg->flags = flags;
> > +	msg->totallen = totallen;
> > +	memcpy(payload_of(msg), data, datalen);
> > +out:
> > +	return msg;
> > +}
> > +EXPORT_SYMBOL(pipefs_alloc_init_msg_padded);
> > +
> > +/*
> > + * See the description of pipefs_alloc_init_msg_padded().
> > + */
> > +pipefs_hdr_t* pipefs_alloc_init_msg(u32 msgid, u8 type, u8 flags,
> > +				    void *data, u16 datalen)
> > +{
> > +	return pipefs_alloc_init_msg_padded(msgid, type, flags, data,
> > +					    datalen, 0);
> > +}
> > +EXPORT_SYMBOL(pipefs_alloc_init_msg);
> > +
> > +
> > +static void pipefs_init_rpcmsg(struct rpc_pipe_msg *rpcmsg, pipefs_hdr_t *msg,
> > +			       u8 upflags)
> > +{
> > +	memset(rpcmsg, 0, sizeof(*rpcmsg));
> > +	rpcmsg->data = msg;
> > +	rpcmsg->len = msg->totallen;
> > +	rpcmsg->flags = upflags;
> > +}
> > +
> > +static struct rpc_pipe_msg* pipefs_alloc_init_rpcmsg(pipefs_hdr_t *msg,
> > +						     u8 upflags)
> > +{
> > +	struct rpc_pipe_msg *rpcmsg;
> > +
> > +	rpcmsg = kmalloc(sizeof(*rpcmsg), GFP_KERNEL);
> > +	if (!rpcmsg)
> > +		return ERR_PTR(-ENOMEM);
> > +
> > +	pipefs_init_rpcmsg(rpcmsg, msg, upflags);
> > +	return rpcmsg;
> > +}
> > +
> > +
> > +/* represents an upcall that'll block and wait for a reply */
> > +typedef struct pipefs_upcall {
> > +	u32 msgid;
> > +	struct rpc_pipe_msg rpcmsg;
> > +	struct list_head list;
> > +	wait_queue_head_t waitq;
> > +	struct pipefs_hdr *reply;
> > +} pipefs_upcall_t;
> > +
> > +
> > +static void pipefs_init_upcall_waitreply(pipefs_upcall_t *upcall,
> > +					 pipefs_hdr_t *msg, u8 upflags)
> > +{
> > +	upcall->reply = NULL;
> > +	upcall->msgid = msg->msgid;
> > +	INIT_LIST_HEAD(&upcall->list);
> > +	init_waitqueue_head(&upcall->waitq);
> > +	pipefs_init_rpcmsg(&upcall->rpcmsg, msg, upflags);
> > +}
> > +
> > +static int __pipefs_queue_upcall_waitreply(struct dentry *pipe,
> > +					   pipefs_upcall_t *upcall,
> > +					   pipefs_list_t *uplist, u32 timeout)
> > +{
> > +	int err = 0;
> > +	DECLARE_WAITQUEUE(wq, current);
> > +
> > +	add_wait_queue(&upcall->waitq, &wq);
> > +	spin_lock(&uplist->list_lock);
> > +	list_add(&upcall->list, &uplist->list);
> > +	spin_unlock(&uplist->list_lock);
> > +
> > +	err = rpc_queue_upcall(pipe->d_inode, &upcall->rpcmsg);
> > +	if (err < 0)
> > +		goto out;
> > +
> > +	if (timeout) {
> > +		/* retval of 0 means timer expired */
> > +		err = schedule_timeout_uninterruptible(timeout);
> > +		if (err == 0 && upcall->reply == NULL)
> > +			err = -ETIMEDOUT;
> > +	} else {
> > +		set_current_state(TASK_UNINTERRUPTIBLE);
> > +		schedule();
> > +		__set_current_state(TASK_RUNNING);
> > +	}
> > +
> > +	spin_lock(&uplist->list_lock);
> > +	list_del_init(&upcall->list);
> > +	spin_unlock(&uplist->list_lock);
> > +	remove_wait_queue(&upcall->waitq, &wq);
> > +out:
> > +	return err;
> > +}
> > +
> > +/*
> > + * Queue a pipefs msg for an upcall to userspace, place the calling thread
> > + * on @uplist, and block the thread to wait for a reply.  If @timeout is
> > + * nonzero, the thread will be blocked for at most @timeout jiffies.
> > + *
> > + * (To convert time units into jiffies, consider the functions
> > + *  msecs_to_jiffies(), usecs_to_jiffies(), timeval_to_jiffies(), and
> > + *  timespec_to_jiffies().)
> > + *
> > + * Once a reply is received by your downcall handler, call
> > + * pipefs_assign_upcall_reply() with @uplist to find the corresponding upcall,
> > + * assign the reply, and wake the waiting thread.
> > + *
> > + * This function's return value pointer may be an error and should be checked
> > + * with IS_ERR() before attempting to access the reply message.
> > + *
> > + * Callers are responsible for freeing @msg, unless pipefs_generic_destroy_msg()
> > + * is used as the ->destroy_msg() callback and the PIPEFS_AUTOFREE_UPCALL_MSG
> > + * flag is set in @upflags.  See also rpc_pipe_fs.h.
> 
> How does the caller know that the message has been freed by
> pipefs_generic_destroy_msg()? There be dragons here!

	yes, i should spell things out more explicitly here -- it's gotta 
be incumbent on the caller to know a priori what ->destroy_msg() is going 
to do, at least until a need and a clever suggestion arise.  i didn't want 
to try to add a bunch of apparatus and end up trying to implement a bad 
version of Obj-C's autorelease pools :)

	thanks for reading through this, trond.  oh, and i have a wiki 
entry with example code that i'll probably put on linux-nfs.org once i get 
this patch fixed up; that might help make the case and/or generate 
criticism.

	d
	.
 
> > + */
> > +pipefs_hdr_t* pipefs_queue_upcall_waitreply(struct dentry *pipe,
> > +					    pipefs_hdr_t *msg,
> > +					    pipefs_list_t *uplist,
> > +					    u8 upflags, u32 timeout)
> > +{
> > +	int err = 0;
> > +	pipefs_upcall_t upcall;
> > +
> > +	pipefs_init_upcall_waitreply(&upcall, msg, upflags);
> > +	err = __pipefs_queue_upcall_waitreply(pipe, &upcall, uplist, timeout);
> > +	if (err) {
> > +		kfree(upcall.reply);
> > +		upcall.reply = ERR_PTR(err);
> > +	}
> > +
> > +	return upcall.reply;
> > +}
> > +EXPORT_SYMBOL(pipefs_queue_upcall_waitreply);
> > +
> > +/*
> > + * Queue a pipefs msg for an upcall to userspace and immediately return (i.e.,
> > + * no reply is expected).
> > + *
> > + * Callers are responsible for freeing @msg, unless pipefs_generic_destroy_msg()
> > + * is used as the ->destroy_msg() callback and the PIPEFS_AUTOFREE_UPCALL_MSG
> > + * flag is set in @upflags.  See also rpc_pipe_fs.h.
> > + */
> > +int pipefs_queue_upcall_noreply(struct dentry *pipe, pipefs_hdr_t *msg,
> > +				u8 upflags)
> > +{
> > +	int err = 0;
> > +	struct rpc_pipe_msg *rpcmsg;
> > +
> > +	upflags |= PIPEFS_AUTOFREE_RPCMSG;
> > +	rpcmsg = pipefs_alloc_init_rpcmsg(msg, upflags);
> > +	if (IS_ERR(rpcmsg)) {
> > +		err = PTR_ERR(rpcmsg);
> > +		goto out;
> > +	}
> > +	err = rpc_queue_upcall(pipe->d_inode, rpcmsg);
> > +out:
> > +	return err;
> > +}
> > +EXPORT_SYMBOL(pipefs_queue_upcall_noreply);
> > +
> > +
> > +static pipefs_upcall_t* pipefs_find_upcall_msgid(u32 msgid,
> > +						 pipefs_list_t *uplist)
> > +{
> > +	pipefs_upcall_t *upcall;
> > +
> > +	spin_lock(&uplist->list_lock);
> > +	list_for_each_entry(upcall, &uplist->list, list)
> > +		if (upcall->msgid == msgid)
> > +			goto out;
> > +	upcall = NULL;
> > +out:
> > +	spin_unlock(&uplist->list_lock);
> > +	return upcall;
> > +}
> > +
> > +/*
> > + * In your rpc_pipe_ops->downcall() handler, once you've read in a downcall
> > + * message and have determined that it is a reply to a waiting upcall,
> > + * you can use this function to find the appropriate upcall, assign the result,
> > + * and wake the upcall thread.
> > + *
> > + * The reply message must have the same msgid as the original upcall message's.
> > + *
> > + * See also pipefs_queue_upcall_waitreply() and pipefs_readmsg().
> > + */
> > +int pipefs_assign_upcall_reply(pipefs_hdr_t *reply, pipefs_list_t *uplist)
> > +{
> > +	int err = 0;
> > +	pipefs_upcall_t *upcall;
> > +
> > +	upcall = pipefs_find_upcall_msgid(reply->msgid, uplist);
> > +	if (!upcall) {
> > +		printk(KERN_ERR "%s: ERROR: have reply but no matching upcall "
> > +			"for msgid %d\n", __func__, reply->msgid);
> > +		err = -ENOENT;
> > +		goto out;
> > +	}
> > +	upcall->reply = reply;
> > +	wake_up(&upcall->waitq);
> > +out:
> > +	return err;
> > +}
> > +EXPORT_SYMBOL(pipefs_assign_upcall_reply);
> > +
> > +/*
> > + * Generic method to read-in and return a newly-allocated message which begins
> > + * with a pipefs_hdr_t.
> > + */
> > +pipefs_hdr_t* pipefs_readmsg(struct file *filp, const char __user *src,
> > +			     size_t len)
> > +{
> > +	int err = 0, hdrsize;
> > +	pipefs_hdr_t *msg = NULL;
> > +
> > +	hdrsize = sizeof(*msg);
> > +	if (len < hdrsize) {
> > +		printk(KERN_ERR "%s: ERROR: header is too short (%d vs %d)\n",
> > +		       __func__, len, hdrsize);
> > +		err = -EINVAL;
> > +		goto out;
> > +	}
> > +
> > +	msg = kzalloc(len, GFP_KERNEL);
> > +	if (!msg) {
> > +		err = -ENOMEM;
> > +		goto out;
> > +	}
> > +	if (copy_from_user(msg, src, len))
> > +		err = -EFAULT;
> > +out:
> > +	if (err) {
> > +		if (msg)
> > +			kfree(msg);
> > +		msg = ERR_PTR(err);
> > +	}
> > +	return msg;
> > +}
> > +EXPORT_SYMBOL(pipefs_readmsg);
> > +
> > +/*
> > + * Generic rpc_pipe_ops->upcall() handler implementation.
> > + *
> > + * Don't call this directly: to make an upcall, use
> > + * pipefs_queue_upcall_waitreply() or pipefs_queue_upcall_noreply().
> > + */
> > +ssize_t pipefs_generic_upcall(struct file *filp, struct rpc_pipe_msg *rpcmsg,
> > +			      char __user *dst, size_t buflen)
> > +{
> > +	char *data;
> > +	ssize_t len, left;
> > +
> > +	data = (char *)rpcmsg->data + rpcmsg->copied;
> > +	len = rpcmsg->len - rpcmsg->copied;
> > +	if (len > buflen)
> > +		len = buflen;
> > +
> > +	left = copy_to_user(dst, data, len);
> > +	if (left < 0) {
> > +		rpcmsg->errno = left;
> > +		return left;
> > +	}
> > +
> > +	len -= left;
> > +	rpcmsg->copied += len;
> > +	rpcmsg->errno = 0;
> > +	return len;
> > +}
> > +EXPORT_SYMBOL(pipefs_generic_upcall);
> > +
> > +/*
> > + * Generic rpc_pipe_ops->destroy_msg() handler implementation.
> > + *
> > + * Items are only freed if @rpcmsg->flags has been set appropriately.
> > + * See pipefs_queue_upcall_noreply() and rpc_pipe_fs.h.
> > + */
> > +void pipefs_generic_destroy_msg(struct rpc_pipe_msg *rpcmsg)
> > +{
> > +	if (rpcmsg->flags & PIPEFS_AUTOFREE_UPCALL_MSG)
> > +		kfree(rpcmsg->data);
> > +	if (rpcmsg->flags & PIPEFS_AUTOFREE_RPCMSG)
> > +		kfree(rpcmsg);
> > +}
> > +EXPORT_SYMBOL(pipefs_generic_destroy_msg);
> > +
> 
> 

[Index of Archives]     [Linux Filesystem Development]     [Linux USB Development]     [Linux Media Development]     [Video for Linux]     [Linux NILFS]     [Linux Audio Users]     [Yosemite Info]     [Linux SCSI]

  Powered by Linux