/*
* linux/fs/9p/mux.c
*
* Protocol Multiplexer
*
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
* Copyright (C) 2004-2005 by Latchesar Ionkov <lucho@ionkov.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to:
* Free Software Foundation
* 51 Franklin Street, Fifth Floor
* Boston, MA 02111-1301 USA
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/kthread.h>
#include <linux/idr.h>
#include <linux/mutex.h>
#include "debug.h"
#include "v9fs.h"
#include "9p.h"
#include "conv.h"
#include "transport.h"
#include "mux.h"
#define ERREQFLUSH 1
#define SCHED_TIMEOUT 10
#define MAXPOLLWADDR 2
enum {
Rworksched = 1, /* read work scheduled or running */
Rpending = 2, /* can read */
Wworksched = 4, /* write work scheduled or running */
Wpending = 8, /* can write */
};
enum {
None,
Flushing,
Flushed,
};
struct v9fs_mux_poll_task;
struct v9fs_req {
spinlock_t lock;
int tag;
struct v9fs_fcall *tcall;
struct v9fs_fcall *rcall;
int err;
v9fs_mux_req_callback cb;
void *cba;
int flush;
struct list_head req_list;
};
struct v9fs_mux_data {
spinlock_t lock;
struct list_head mux_list;
struct v9fs_mux_poll_task *poll_task;
int msize;
unsigned char *extended;
struct v9fs_transport *trans;
struct v9fs_idpool tagpool;
int err;
wait_queue_head_t equeue;
struct list_head req_list;
struct list_head unsent_req_list;
struct v9fs_fcall *rcall;
int rpos;
char *rbuf;
int wpos;
int wsize;
char *wbuf;
wait_queue_t poll_wait[MAXPOLLWADDR];
wait_queue_head_t *poll_waddr[MAXPOLLWADDR];
poll_table pt;
struct work_struct rq;
struct work_struct wq;
unsigned long wsched;
};
struct v9fs_mux_poll_task {
struct task_struct *task;
struct list_head mux_list;
int muxnum;
};
struct v9fs_mux_rpc {
struct v9fs_mux_data *m;
int err;
struct v9fs_fcall *tcall;
struct v9fs_fcall *rcall;
wait_queue_head_t wqueue;
};
static int v9fs_poll_proc(void *);
static void v9fs_read_work(void *);
static void v9fs_write_work(void *);
static void v9fs_pollwait(struct file *filp, wait_queue_head_t * wait_address,
poll_table * p);
static u16 v9fs_mux_get_tag(struct v9fs_mux_data *);
static void v9fs_mux_put_tag(struct v9fs_mux_data *, u16);
static DEFINE_MUTEX(v9fs_mux_task_lock);
static struct workqueue_struct *v9fs_mux_wq;
static int v9fs_mux_num;
static int v9fs_mux_poll_task_num;
static struct v9fs_mux_poll_task v9fs_mux_poll_tasks[100];
int v9fs_mux_global_init(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(v9fs_mux_poll_tasks); i++)
v9fs_mux_poll_tasks[i].task = NULL;
v9fs_mux_wq = create_workqueue("v9fs");
if (!v9fs_mux_wq)
return -ENOMEM;
return 0;
}
void v9fs_mux_global_exit(void)
{
destroy_workqueue(v9fs_mux_wq);
}
/**
* v9fs_mux_calc_poll_procs - calculates the number of polling procs
* based on the number of mounted v9fs filesystems.
*
* The current implementation returns sqrt of the number of mounts.
*/
static int v9fs_mux_calc_poll_procs(int muxnum)
{
int n;
if (v9fs_mux_poll_task_num)
n = muxnum / v9fs_mux_poll_task_num +
(muxnum % v9fs_mux_poll_task_num ? 1 : 0);
else
n = 1;
if (n > ARRAY_SIZE(v9fs_mux_poll_tasks))
n = ARRAY_SIZE(v9fs_mux_poll_tasks);
return n;
}
static int v9fs_mux_poll_start(struct v9fs_mux_data *m)
{
int i, n;
struct v9fs_mux_poll_task *vpt, *vptlast;
struct task_struct *pproc;
dprintk(DEBUG_MUX, "mux %p muxnum %d procnum %d\n", m, v9fs_mux_num,
v9fs_mux_poll_task_num);
mutex_lock(&v9fs_mux_task_lock