/*
* net/sunrpc/rpc_pipe.c
*
* Userland/kernel interface for rpcauth_gss.
* Code shamelessly plagiarized from fs/nfsd/nfsctl.c
* and fs/driverfs/inode.c
*
* Copyright (c) 2002, Trond Myklebust <trond.myklebust@fys.uio.no>
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/pagemap.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/dnotify.h>
#include <linux/kernel.h>
#include <asm/ioctls.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/wait.h>
#include <linux/seq_file.h>
#include <linux/sunrpc/clnt.h>
#include <linux/workqueue.h>
#include <linux/sunrpc/rpc_pipe_fs.h>
static struct vfsmount *rpc_mount __read_mostly;
static int rpc_mount_count;
static struct file_system_type rpc_pipe_fs_type;
static kmem_cache_t *rpc_inode_cachep __read_mostly;
#define RPC_UPCALL_TIMEOUT (30*HZ)
static void
__rpc_purge_upcall(struct inode *inode, int err)
{
struct rpc_inode *rpci = RPC_I(inode);
struct rpc_pipe_msg *msg;
while (!list_empty(&rpci->pipe)) {
msg = list_entry(rpci->pipe.next, struct rpc_pipe_msg, list);
list_del_init(&msg->list);
msg->errno = err;
rpci->ops->destroy_msg(msg);
}
while (!list_empty(&rpci->in_upcall)) {
msg = list_entry(rpci->pipe.next, struct rpc_pipe_msg, list);
list_del_init(&msg->list);
msg->errno = err;
rpci->ops->destroy_msg(msg);
}
rpci->pipelen = 0;
wake_up(&rpci->waitq);
}
static void
rpc_timeout_upcall_queue(void *data)
{
struct rpc_inode *rpci = (struct rpc_inode *)data;
struct inode *inode = &rpci->vfs_inode;
down(&inode->i_sem);
if (rpci->nreaders == 0 && !list_empty(&rpci->pipe))
__rpc_purge_upcall(inode, -ETIMEDOUT);
up(&inode->i_sem);
}
int
rpc_queue_upcall(struct inode *inode, struct rpc_pipe_msg *msg)
{
struct rpc_inode *rpci = RPC_I(inode);
int res = 0;
down(&inode->i_sem);
if (rpci->nreaders) {
list_add_tail(&msg->list, &rpci->pipe);
rpci->pipelen += msg->len;
} else if (rpci->flags & RPC_PIPE_WAIT_FOR_OPEN) {
if (list_empty(&rpci->pipe))
schedule_delayed_work(&rpci->queue_timeout,
RPC_UPCALL_TIMEOUT);
list_add_tail(&msg->list, &rpci->pipe);
rpci->pipelen += msg->len;
} else
res = -EPIPE;
up(&inode->i_sem);
wake_up(&rpci->waitq);
return res;
}
static void
rpc_close_pipes(struct inode *inode)
{
struct rpc_inode *rpci = RPC_I(inode);
cancel_delayed_work(&rpci->queue_timeout);
flush_scheduled_work();
down(&inode->i_sem);
if (rpci->ops != NULL) {
rpci->nreaders = 0;
__rpc_purge_upcall(inode, -EPIPE);
rpci->nwriters = 0;
if (rpci->ops->release_pipe)
rpci->ops->release_pipe(inode);
rpci->ops = NULL;
}
up(&inode->i_sem);
}
static inline void
rpc_inode_setowner(struct inode *inode, void *private)
{
RPC_I(inode)->private = private;
}
static struct inode *
rpc_alloc_inode(struct super_block *sb)
{
struct rpc_inode *rpci;
rpci = (struct rpc_inode *)kmem_cache_alloc(rpc_inode_cachep, SLAB_KERNEL);
if (!rpci)
return NULL;
return &rpci->vfs_inode;
}
static void
rpc_destroy_inode(struct inode *inode)
{
kmem_cache_free(rpc_inode_cachep, RPC_I(inode));
}
static int
rpc_pipe_open(struct inode *inode, struct file *filp)
{
struct rpc_inode *rpci = RPC_I(inode);
int res = -ENXIO;
down(&inode->i_sem);
if (rpci->ops != NULL) {
if (filp->f_mode & FMODE_READ)
rpci->nreaders ++;
if (filp->f_mode & FMODE_WRITE)
rpci->nwriters ++;
res = 0;
}
up(&inode->i_sem);
return res;
}
static int
rpc_pipe_release(struct inode *inode, struct file *filp)
{
struct rpc_inode *rpci = RPC_I(filp->f_dentry->d_inode);
struct rpc_pipe_msg *msg;
down(&inode->i_sem);
if (rpci->ops == NULL)
goto out;
msg = (struct rpc_pipe_msg *)filp->private_data;
if (msg != NULL) {
msg->errno = -EPIPE;
list_del_init(&msg->list);
rpci->ops->destroy_msg(msg