2007-07-11 06:57:28 +08:00
|
|
|
/*
|
|
|
|
* linux/fs/9p/trans_fd.c
|
|
|
|
*
|
|
|
|
* Fd transport layer. Includes deprecated socket layer.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2006 by Russ Cox <rsc@swtch.com>
|
|
|
|
* Copyright (C) 2004-2005 by Latchesar Ionkov <lucho@ionkov.net>
|
2008-02-07 09:25:03 +08:00
|
|
|
* Copyright (C) 2004-2008 by Eric Van Hensbergen <ericvh@gmail.com>
|
2007-07-11 06:57:28 +08:00
|
|
|
* Copyright (C) 1997-2002 by Ron Minnich <rminnich@sarnoff.com>
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2011-11-29 02:40:46 +08:00
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
|
2007-07-11 06:57:28 +08:00
|
|
|
#include <linux/in.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/net.h>
|
|
|
|
#include <linux/ipv6.h>
|
2008-02-07 09:25:03 +08:00
|
|
|
#include <linux/kthread.h>
|
2007-07-11 06:57:28 +08:00
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/un.h>
|
|
|
|
#include <linux/uaccess.h>
|
|
|
|
#include <linux/inet.h>
|
|
|
|
#include <linux/idr.h>
|
|
|
|
#include <linux/file.h>
|
2007-10-18 03:31:07 +08:00
|
|
|
#include <linux/parser.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2007-07-11 06:57:28 +08:00
|
|
|
#include <net/9p/9p.h>
|
2008-10-14 07:45:25 +08:00
|
|
|
#include <net/9p/client.h>
|
2007-07-11 06:57:28 +08:00
|
|
|
#include <net/9p/transport.h>
|
|
|
|
|
2009-08-06 06:02:43 +08:00
|
|
|
#include <linux/syscalls.h> /* killme */
|
|
|
|
|
2007-07-11 06:57:28 +08:00
|
|
|
#define P9_PORT 564
|
2007-10-18 03:31:07 +08:00
|
|
|
#define MAX_SOCK_BUF (64*1024)
|
2008-02-07 09:25:03 +08:00
|
|
|
#define MAXPOLLWADDR 2
|
2007-10-18 03:31:07 +08:00
|
|
|
|
2008-03-05 21:08:09 +08:00
|
|
|
/**
|
|
|
|
* struct p9_fd_opts - per-transport options
|
|
|
|
* @rfd: file descriptor for reading (trans=fd)
|
|
|
|
* @wfd: file descriptor for writing (trans=fd)
|
|
|
|
* @port: port to connect to (trans=tcp)
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2007-10-18 03:31:07 +08:00
|
|
|
struct p9_fd_opts {
|
|
|
|
int rfd;
|
|
|
|
int wfd;
|
|
|
|
u16 port;
|
2013-05-30 03:15:07 +08:00
|
|
|
int privport;
|
2007-10-18 03:31:07 +08:00
|
|
|
};
|
2007-07-11 06:57:28 +08:00
|
|
|
|
2008-03-05 21:08:09 +08:00
|
|
|
/**
|
|
|
|
* struct p9_trans_fd - transport state
|
|
|
|
* @rd: reference to file to read from
|
|
|
|
* @wr: reference of file to write to
|
|
|
|
* @conn: connection state reference
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2007-07-11 06:57:28 +08:00
|
|
|
struct p9_trans_fd {
|
|
|
|
struct file *rd;
|
|
|
|
struct file *wr;
|
2008-02-07 09:25:03 +08:00
|
|
|
struct p9_conn *conn;
|
2007-07-11 06:57:28 +08:00
|
|
|
};
|
|
|
|
|
2007-10-18 03:31:07 +08:00
|
|
|
/*
|
|
|
|
* Option Parsing (code inspired by NFS code)
|
|
|
|
* - a little lazy - parse all fd-transport options
|
|
|
|
*/
|
2007-07-11 06:57:28 +08:00
|
|
|
|
2007-10-18 03:31:07 +08:00
|
|
|
enum {
|
|
|
|
/* Options that take integer arguments */
|
2007-11-06 22:02:53 +08:00
|
|
|
Opt_port, Opt_rfdno, Opt_wfdno, Opt_err,
|
2013-05-30 03:15:07 +08:00
|
|
|
/* Options that take no arguments */
|
|
|
|
Opt_privport,
|
2007-10-18 03:31:07 +08:00
|
|
|
};
|
2007-07-11 06:57:28 +08:00
|
|
|
|
2008-10-13 17:46:57 +08:00
|
|
|
static const match_table_t tokens = {
|
2007-10-18 03:31:07 +08:00
|
|
|
{Opt_port, "port=%u"},
|
|
|
|
{Opt_rfdno, "rfdno=%u"},
|
|
|
|
{Opt_wfdno, "wfdno=%u"},
|
2013-05-30 03:15:07 +08:00
|
|
|
{Opt_privport, "privport"},
|
2007-11-06 22:02:53 +08:00
|
|
|
{Opt_err, NULL},
|
2007-10-18 03:31:07 +08:00
|
|
|
};
|
2007-07-11 06:57:28 +08:00
|
|
|
|
2008-02-07 09:25:03 +08:00
|
|
|
enum {
|
|
|
|
Rworksched = 1, /* read work scheduled or running */
|
|
|
|
Rpending = 2, /* can read */
|
|
|
|
Wworksched = 4, /* write work scheduled or running */
|
|
|
|
Wpending = 8, /* can write */
|
|
|
|
};
|
|
|
|
|
2008-10-14 07:45:25 +08:00
|
|
|
struct p9_poll_wait {
|
|
|
|
struct p9_conn *conn;
|
|
|
|
wait_queue_t wait;
|
|
|
|
wait_queue_head_t *wait_addr;
|
2008-03-05 21:08:09 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* struct p9_conn - fd mux connection state information
|
|
|
|
* @mux_list: list link for mux to manage multiple connections (?)
|
2008-10-14 07:45:25 +08:00
|
|
|
* @client: reference to client instance for this connection
|
2008-03-05 21:08:09 +08:00
|
|
|
* @err: error state
|
|
|
|
* @req_list: accounting for requests which have been sent
|
|
|
|
* @unsent_req_list: accounting for requests that haven't been sent
|
2008-10-14 07:45:22 +08:00
|
|
|
* @req: current request being processed (if any)
|
|
|
|
* @tmp_buf: temporary buffer to read in header
|
|
|
|
* @rsize: amount to read for current frame
|
2008-03-05 21:08:09 +08:00
|
|
|
* @rpos: read position in current frame
|
|
|
|
* @rbuf: current read buffer
|
|
|
|
* @wpos: write position for current frame
|
|
|
|
* @wsize: amount of data to write for current frame
|
|
|
|
* @wbuf: current write buffer
|
2009-07-20 03:41:55 +08:00
|
|
|
* @poll_pending_link: pending links to be polled per conn
|
2008-03-05 21:08:09 +08:00
|
|
|
* @poll_wait: array of wait_q's for various worker threads
|
|
|
|
* @pt: poll state
|
|
|
|
* @rq: current read work
|
|
|
|
* @wq: current write work
|
|
|
|
* @wsched: ????
|
|
|
|
*
|
|
|
|
*/
|
2008-02-07 09:25:03 +08:00
|
|
|
|
|
|
|
struct p9_conn {
|
|
|
|
struct list_head mux_list;
|
2008-10-14 07:45:25 +08:00
|
|
|
struct p9_client *client;
|
2008-02-07 09:25:03 +08:00
|
|
|
int err;
|
|
|
|
struct list_head req_list;
|
|
|
|
struct list_head unsent_req_list;
|
2008-10-14 07:45:22 +08:00
|
|
|
struct p9_req_t *req;
|
|
|
|
char tmp_buf[7];
|
|
|
|
int rsize;
|
2008-02-07 09:25:03 +08:00
|
|
|
int rpos;
|
|
|
|
char *rbuf;
|
|
|
|
int wpos;
|
|
|
|
int wsize;
|
|
|
|
char *wbuf;
|
2008-10-14 07:45:25 +08:00
|
|
|
struct list_head poll_pending_link;
|
|
|
|
struct p9_poll_wait poll_wait[MAXPOLLWADDR];
|
2008-02-07 09:25:03 +08:00
|
|
|
poll_table pt;
|
|
|
|
struct work_struct rq;
|
|
|
|
struct work_struct wq;
|
|
|
|
unsigned long wsched;
|
|
|
|
};
|
|
|
|
|
2011-02-01 18:42:43 +08:00
|
|
|
static void p9_poll_workfn(struct work_struct *work);
|
|
|
|
|
2008-10-14 07:45:25 +08:00
|
|
|
static DEFINE_SPINLOCK(p9_poll_lock);
|
|
|
|
static LIST_HEAD(p9_poll_pending_list);
|
2011-02-01 18:42:43 +08:00
|
|
|
static DECLARE_WORK(p9_poll_work, p9_poll_workfn);
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2013-05-30 03:15:07 +08:00
|
|
|
static unsigned int p9_ipport_resv_min = P9_DEF_MIN_RESVPORT;
|
|
|
|
static unsigned int p9_ipport_resv_max = P9_DEF_MAX_RESVPORT;
|
|
|
|
|
2008-10-14 07:45:25 +08:00
|
|
|
static void p9_mux_poll_stop(struct p9_conn *m)
|
2008-02-07 09:25:03 +08:00
|
|
|
{
|
2008-10-14 07:45:25 +08:00
|
|
|
unsigned long flags;
|
|
|
|
int i;
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-14 07:45:25 +08:00
|
|
|
for (i = 0; i < ARRAY_SIZE(m->poll_wait); i++) {
|
|
|
|
struct p9_poll_wait *pwait = &m->poll_wait[i];
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-14 07:45:25 +08:00
|
|
|
if (pwait->wait_addr) {
|
|
|
|
remove_wait_queue(pwait->wait_addr, &pwait->wait);
|
|
|
|
pwait->wait_addr = NULL;
|
2008-02-07 09:25:03 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-10-14 07:45:25 +08:00
|
|
|
spin_lock_irqsave(&p9_poll_lock, flags);
|
|
|
|
list_del_init(&m->poll_pending_link);
|
|
|
|
spin_unlock_irqrestore(&p9_poll_lock, flags);
|
2008-02-07 09:25:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-10-14 07:45:24 +08:00
|
|
|
* p9_conn_cancel - cancel all pending requests with error
|
|
|
|
* @m: mux data
|
|
|
|
* @err: error code
|
2008-02-07 09:25:03 +08:00
|
|
|
*
|
|
|
|
*/
|
2008-03-05 21:08:09 +08:00
|
|
|
|
2008-10-16 21:30:07 +08:00
|
|
|
static void p9_conn_cancel(struct p9_conn *m, int err)
|
2008-02-07 09:25:03 +08:00
|
|
|
{
|
2008-10-14 07:45:22 +08:00
|
|
|
struct p9_req_t *req, *rtmp;
|
2008-10-14 07:45:21 +08:00
|
|
|
unsigned long flags;
|
2008-10-14 07:45:24 +08:00
|
|
|
LIST_HEAD(cancel_list);
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_ERROR, "mux %p err %d\n", m, err);
|
2008-10-18 01:45:40 +08:00
|
|
|
|
2008-10-14 07:45:21 +08:00
|
|
|
spin_lock_irqsave(&m->client->lock, flags);
|
2008-10-18 01:45:40 +08:00
|
|
|
|
|
|
|
if (m->err) {
|
|
|
|
spin_unlock_irqrestore(&m->client->lock, flags);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m->err = err;
|
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
list_for_each_entry_safe(req, rtmp, &m->req_list, req_list) {
|
|
|
|
list_move(&req->req_list, &cancel_list);
|
|
|
|
}
|
|
|
|
list_for_each_entry_safe(req, rtmp, &m->unsent_req_list, req_list) {
|
|
|
|
list_move(&req->req_list, &cancel_list);
|
2008-02-07 09:25:03 +08:00
|
|
|
}
|
2008-10-14 07:45:21 +08:00
|
|
|
spin_unlock_irqrestore(&m->client->lock, flags);
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
list_for_each_entry_safe(req, rtmp, &cancel_list, req_list) {
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_ERROR, "call back req %p\n", req);
|
2009-04-06 05:28:59 +08:00
|
|
|
list_del(&req->req_list);
|
2014-01-18 01:31:00 +08:00
|
|
|
if (!req->t_err)
|
|
|
|
req->t_err = err;
|
|
|
|
p9_client_cb(m->client, req, REQ_STATUS_ERROR);
|
2008-02-07 09:25:03 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-05 17:00:21 +08:00
|
|
|
static int
|
2008-10-14 07:45:24 +08:00
|
|
|
p9_fd_poll(struct p9_client *client, struct poll_table_struct *pt)
|
2008-02-07 09:25:03 +08:00
|
|
|
{
|
2008-10-14 07:45:24 +08:00
|
|
|
int ret, n;
|
|
|
|
struct p9_trans_fd *ts = NULL;
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
if (client && client->status == Connected)
|
|
|
|
ts = client->trans;
|
2008-09-25 05:22:23 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
if (!ts)
|
|
|
|
return -EREMOTEIO;
|
2008-09-25 05:22:23 +08:00
|
|
|
|
2013-09-23 04:27:52 +08:00
|
|
|
if (!ts->rd->f_op->poll)
|
2008-10-14 07:45:24 +08:00
|
|
|
return -EIO;
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2013-09-23 04:27:52 +08:00
|
|
|
if (!ts->wr->f_op->poll)
|
2008-10-14 07:45:24 +08:00
|
|
|
return -EIO;
|
2008-10-14 07:45:25 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
ret = ts->rd->f_op->poll(ts->rd, pt);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2008-10-14 07:45:25 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
if (ts->rd != ts->wr) {
|
|
|
|
n = ts->wr->f_op->poll(ts->wr, pt);
|
|
|
|
if (n < 0)
|
|
|
|
return n;
|
|
|
|
ret = (ret & ~POLLOUT) | (n & ~POLLIN);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2008-10-14 07:45:25 +08:00
|
|
|
}
|
|
|
|
|
2008-02-07 09:25:03 +08:00
|
|
|
/**
|
2008-10-14 07:45:24 +08:00
|
|
|
* p9_fd_read- read from a fd
|
|
|
|
* @client: client instance
|
|
|
|
* @v: buffer to receive data into
|
|
|
|
* @len: size of receive buffer
|
2008-03-05 21:08:09 +08:00
|
|
|
*
|
2008-02-07 09:25:03 +08:00
|
|
|
*/
|
2008-03-05 21:08:09 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
static int p9_fd_read(struct p9_client *client, void *v, int len)
|
2008-02-07 09:25:03 +08:00
|
|
|
{
|
2008-10-14 07:45:24 +08:00
|
|
|
int ret;
|
|
|
|
struct p9_trans_fd *ts = NULL;
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
if (client && client->status != Disconnected)
|
|
|
|
ts = client->trans;
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
if (!ts)
|
|
|
|
return -EREMOTEIO;
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
if (!(ts->rd->f_flags & O_NONBLOCK))
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_ERROR, "blocking read ...\n");
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
ret = kernel_read(ts->rd, ts->rd->f_pos, v, len);
|
|
|
|
if (ret <= 0 && ret != -ERESTARTSYS && ret != -EAGAIN)
|
|
|
|
client->status = Disconnected;
|
|
|
|
return ret;
|
2008-02-07 09:25:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-10-14 07:45:24 +08:00
|
|
|
* p9_read_work - called when there is some data to be read from a transport
|
|
|
|
* @work: container of work to be done
|
2008-03-05 21:08:09 +08:00
|
|
|
*
|
2008-02-07 09:25:03 +08:00
|
|
|
*/
|
2008-03-05 21:08:09 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
static void p9_read_work(struct work_struct *work)
|
2008-02-07 09:25:03 +08:00
|
|
|
{
|
2008-10-14 07:45:24 +08:00
|
|
|
int n, err;
|
|
|
|
struct p9_conn *m;
|
2014-01-18 01:31:00 +08:00
|
|
|
int status = REQ_STATUS_ERROR;
|
2008-10-14 07:45:24 +08:00
|
|
|
|
|
|
|
m = container_of(work, struct p9_conn, rq);
|
2008-02-07 09:25:03 +08:00
|
|
|
|
|
|
|
if (m->err < 0)
|
|
|
|
return;
|
|
|
|
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_TRANS, "start mux %p pos %d\n", m, m->rpos);
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-14 07:45:22 +08:00
|
|
|
if (!m->rbuf) {
|
|
|
|
m->rbuf = m->tmp_buf;
|
2008-10-14 07:45:24 +08:00
|
|
|
m->rpos = 0;
|
2008-10-14 07:45:22 +08:00
|
|
|
m->rsize = 7; /* start by reading header */
|
2008-02-07 09:25:03 +08:00
|
|
|
}
|
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
clear_bit(Rpending, &m->wsched);
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_TRANS, "read mux %p pos %d size: %d = %d\n",
|
|
|
|
m, m->rpos, m->rsize, m->rsize-m->rpos);
|
2008-10-14 07:45:24 +08:00
|
|
|
err = p9_fd_read(m->client, m->rbuf + m->rpos,
|
2008-10-14 07:45:22 +08:00
|
|
|
m->rsize - m->rpos);
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_TRANS, "mux %p got %d bytes\n", m, err);
|
2008-10-14 07:45:24 +08:00
|
|
|
if (err == -EAGAIN) {
|
2012-09-17 21:16:28 +08:00
|
|
|
goto end_clear;
|
2008-02-07 09:25:03 +08:00
|
|
|
}
|
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
if (err <= 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
m->rpos += err;
|
2008-10-14 07:45:22 +08:00
|
|
|
|
|
|
|
if ((!m->req) && (m->rpos == m->rsize)) { /* header read in */
|
|
|
|
u16 tag;
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_TRANS, "got new header\n");
|
2008-10-14 07:45:22 +08:00
|
|
|
|
|
|
|
n = le32_to_cpu(*(__le32 *) m->rbuf); /* read packet size */
|
2008-10-14 07:45:24 +08:00
|
|
|
if (n >= m->client->msize) {
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_ERROR,
|
|
|
|
"requested packet size too big: %d\n", n);
|
2008-10-14 07:45:24 +08:00
|
|
|
err = -EIO;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2008-10-14 07:45:22 +08:00
|
|
|
tag = le16_to_cpu(*(__le16 *) (m->rbuf+5)); /* read tag */
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_TRANS,
|
|
|
|
"mux %p pkt: size: %d bytes tag: %d\n", m, n, tag);
|
2008-10-14 07:45:22 +08:00
|
|
|
|
|
|
|
m->req = p9_tag_lookup(m->client, tag);
|
2009-04-06 05:28:59 +08:00
|
|
|
if (!m->req || (m->req->status != REQ_STATUS_SENT &&
|
|
|
|
m->req->status != REQ_STATUS_FLSH)) {
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_ERROR, "Unexpected packet tag %d\n",
|
|
|
|
tag);
|
2008-10-14 07:45:22 +08:00
|
|
|
err = -EIO;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m->req->rc == NULL) {
|
|
|
|
m->req->rc = kmalloc(sizeof(struct p9_fcall) +
|
2011-03-08 19:09:47 +08:00
|
|
|
m->client->msize, GFP_NOFS);
|
2008-10-14 07:45:22 +08:00
|
|
|
if (!m->req->rc) {
|
|
|
|
m->req = NULL;
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m->rbuf = (char *)m->req->rc + sizeof(struct p9_fcall);
|
|
|
|
memcpy(m->rbuf, m->tmp_buf, m->rsize);
|
|
|
|
m->rsize = n;
|
|
|
|
}
|
2008-10-14 07:45:24 +08:00
|
|
|
|
2008-10-14 07:45:22 +08:00
|
|
|
/* not an else because some packets (like clunk) have no payload */
|
|
|
|
if ((m->req) && (m->rpos == m->rsize)) { /* packet is read in */
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_TRANS, "got new packet\n");
|
2008-10-18 01:45:40 +08:00
|
|
|
spin_lock(&m->client->lock);
|
2009-04-06 05:28:59 +08:00
|
|
|
if (m->req->status != REQ_STATUS_ERROR)
|
2014-01-18 01:31:00 +08:00
|
|
|
status = REQ_STATUS_RCVD;
|
2008-10-14 07:45:21 +08:00
|
|
|
list_del(&m->req->req_list);
|
2008-10-18 01:45:40 +08:00
|
|
|
spin_unlock(&m->client->lock);
|
2014-01-18 01:31:00 +08:00
|
|
|
p9_client_cb(m->client, m->req, status);
|
2008-10-14 07:45:22 +08:00
|
|
|
m->rbuf = NULL;
|
|
|
|
m->rpos = 0;
|
|
|
|
m->rsize = 0;
|
|
|
|
m->req = NULL;
|
2008-10-14 07:45:24 +08:00
|
|
|
}
|
|
|
|
|
2012-09-17 21:16:28 +08:00
|
|
|
end_clear:
|
|
|
|
clear_bit(Rworksched, &m->wsched);
|
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
if (!list_empty(&m->req_list)) {
|
|
|
|
if (test_and_clear_bit(Rpending, &m->wsched))
|
|
|
|
n = POLLIN;
|
|
|
|
else
|
|
|
|
n = p9_fd_poll(m->client, NULL);
|
|
|
|
|
2012-09-17 21:16:28 +08:00
|
|
|
if ((n & POLLIN) && !test_and_set_bit(Rworksched, &m->wsched)) {
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_TRANS, "sched read work %p\n", m);
|
2011-02-01 18:42:43 +08:00
|
|
|
schedule_work(&m->rq);
|
2012-09-17 21:16:28 +08:00
|
|
|
}
|
|
|
|
}
|
2008-10-14 07:45:24 +08:00
|
|
|
|
|
|
|
return;
|
|
|
|
error:
|
|
|
|
p9_conn_cancel(m, err);
|
|
|
|
clear_bit(Rworksched, &m->wsched);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* p9_fd_write - write to a socket
|
|
|
|
* @client: client instance
|
|
|
|
* @v: buffer to send data from
|
|
|
|
* @len: size of send buffer
|
2008-03-05 21:08:09 +08:00
|
|
|
*
|
2008-02-07 09:25:03 +08:00
|
|
|
*/
|
2008-03-05 21:08:09 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
static int p9_fd_write(struct p9_client *client, void *v, int len)
|
2008-02-07 09:25:03 +08:00
|
|
|
{
|
2008-10-14 07:45:24 +08:00
|
|
|
int ret;
|
|
|
|
mm_segment_t oldfs;
|
|
|
|
struct p9_trans_fd *ts = NULL;
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
if (client && client->status != Disconnected)
|
|
|
|
ts = client->trans;
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
if (!ts)
|
|
|
|
return -EREMOTEIO;
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
if (!(ts->wr->f_flags & O_NONBLOCK))
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_ERROR, "blocking write ...\n");
|
2008-10-14 07:45:25 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
oldfs = get_fs();
|
|
|
|
set_fs(get_ds());
|
|
|
|
/* The cast to a user pointer is valid due to the set_fs() */
|
2009-02-25 18:31:37 +08:00
|
|
|
ret = vfs_write(ts->wr, (__force void __user *)v, len, &ts->wr->f_pos);
|
2008-10-14 07:45:24 +08:00
|
|
|
set_fs(oldfs);
|
2008-10-14 07:45:25 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
if (ret <= 0 && ret != -ERESTARTSYS && ret != -EAGAIN)
|
|
|
|
client->status = Disconnected;
|
|
|
|
return ret;
|
2008-02-07 09:25:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* p9_write_work - called when a transport can send some data
|
2008-03-05 21:08:09 +08:00
|
|
|
* @work: container for work to be done
|
|
|
|
*
|
2008-02-07 09:25:03 +08:00
|
|
|
*/
|
2008-03-05 21:08:09 +08:00
|
|
|
|
2008-02-07 09:25:03 +08:00
|
|
|
static void p9_write_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
int n, err;
|
|
|
|
struct p9_conn *m;
|
2008-10-14 07:45:22 +08:00
|
|
|
struct p9_req_t *req;
|
2008-02-07 09:25:03 +08:00
|
|
|
|
|
|
|
m = container_of(work, struct p9_conn, wq);
|
|
|
|
|
|
|
|
if (m->err < 0) {
|
|
|
|
clear_bit(Wworksched, &m->wsched);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m->wsize) {
|
2012-09-17 21:16:31 +08:00
|
|
|
spin_lock(&m->client->lock);
|
2008-02-07 09:25:03 +08:00
|
|
|
if (list_empty(&m->unsent_req_list)) {
|
|
|
|
clear_bit(Wworksched, &m->wsched);
|
2012-09-17 21:16:31 +08:00
|
|
|
spin_unlock(&m->client->lock);
|
2008-02-07 09:25:03 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-10-14 07:45:22 +08:00
|
|
|
req = list_entry(m->unsent_req_list.next, struct p9_req_t,
|
2008-02-07 09:25:03 +08:00
|
|
|
req_list);
|
2008-10-14 07:45:22 +08:00
|
|
|
req->status = REQ_STATUS_SENT;
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_TRANS, "move req %p\n", req);
|
2008-02-07 09:25:03 +08:00
|
|
|
list_move_tail(&req->req_list, &m->req_list);
|
|
|
|
|
2008-10-14 07:45:22 +08:00
|
|
|
m->wbuf = req->tc->sdata;
|
|
|
|
m->wsize = req->tc->size;
|
2008-02-07 09:25:03 +08:00
|
|
|
m->wpos = 0;
|
2008-10-14 07:45:22 +08:00
|
|
|
spin_unlock(&m->client->lock);
|
2008-02-07 09:25:03 +08:00
|
|
|
}
|
|
|
|
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_TRANS, "mux %p pos %d size %d\n",
|
|
|
|
m, m->wpos, m->wsize);
|
2008-02-07 09:25:03 +08:00
|
|
|
clear_bit(Wpending, &m->wsched);
|
2008-10-14 07:45:25 +08:00
|
|
|
err = p9_fd_write(m->client, m->wbuf + m->wpos, m->wsize - m->wpos);
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_TRANS, "mux %p sent %d bytes\n", m, err);
|
2012-09-17 21:16:30 +08:00
|
|
|
if (err == -EAGAIN)
|
|
|
|
goto end_clear;
|
|
|
|
|
2008-02-07 09:25:03 +08:00
|
|
|
|
|
|
|
if (err < 0)
|
|
|
|
goto error;
|
|
|
|
else if (err == 0) {
|
|
|
|
err = -EREMOTEIO;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
m->wpos += err;
|
|
|
|
if (m->wpos == m->wsize)
|
|
|
|
m->wpos = m->wsize = 0;
|
|
|
|
|
2012-09-17 21:16:30 +08:00
|
|
|
end_clear:
|
|
|
|
clear_bit(Wworksched, &m->wsched);
|
|
|
|
|
2012-09-17 21:16:29 +08:00
|
|
|
if (m->wsize || !list_empty(&m->unsent_req_list)) {
|
2008-02-07 09:25:03 +08:00
|
|
|
if (test_and_clear_bit(Wpending, &m->wsched))
|
|
|
|
n = POLLOUT;
|
|
|
|
else
|
2008-10-14 07:45:25 +08:00
|
|
|
n = p9_fd_poll(m->client, NULL);
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2012-09-17 21:16:30 +08:00
|
|
|
if ((n & POLLOUT) &&
|
|
|
|
!test_and_set_bit(Wworksched, &m->wsched)) {
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_TRANS, "sched write work %p\n", m);
|
2011-02-01 18:42:43 +08:00
|
|
|
schedule_work(&m->wq);
|
2012-09-17 21:16:30 +08:00
|
|
|
}
|
|
|
|
}
|
2008-02-07 09:25:03 +08:00
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
error:
|
|
|
|
p9_conn_cancel(m, err);
|
|
|
|
clear_bit(Wworksched, &m->wsched);
|
|
|
|
}
|
|
|
|
|
2012-04-15 13:58:06 +08:00
|
|
|
static int p9_pollwake(wait_queue_t *wait, unsigned int mode, int sync, void *key)
|
2008-02-07 09:25:03 +08:00
|
|
|
{
|
2008-10-14 07:45:24 +08:00
|
|
|
struct p9_poll_wait *pwait =
|
|
|
|
container_of(wait, struct p9_poll_wait, wait);
|
|
|
|
struct p9_conn *m = pwait->conn;
|
|
|
|
unsigned long flags;
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
spin_lock_irqsave(&p9_poll_lock, flags);
|
|
|
|
if (list_empty(&m->poll_pending_link))
|
|
|
|
list_add_tail(&m->poll_pending_link, &p9_poll_pending_list);
|
|
|
|
spin_unlock_irqrestore(&p9_poll_lock, flags);
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2011-02-01 18:42:43 +08:00
|
|
|
schedule_work(&p9_poll_work);
|
|
|
|
return 1;
|
2008-02-07 09:25:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-10-14 07:45:24 +08:00
|
|
|
* p9_pollwait - add poll task to the wait queue
|
|
|
|
* @filp: file pointer being polled
|
|
|
|
* @wait_address: wait_q to block on
|
|
|
|
* @p: poll state
|
2008-03-05 21:08:09 +08:00
|
|
|
*
|
2008-10-14 07:45:24 +08:00
|
|
|
* called by files poll operation to add v9fs-poll task to files wait queue
|
2008-02-07 09:25:03 +08:00
|
|
|
*/
|
2008-03-05 21:08:09 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
static void
|
|
|
|
p9_pollwait(struct file *filp, wait_queue_head_t *wait_address, poll_table *p)
|
2008-02-07 09:25:03 +08:00
|
|
|
{
|
2008-10-14 07:45:24 +08:00
|
|
|
struct p9_conn *m = container_of(p, struct p9_conn, pt);
|
|
|
|
struct p9_poll_wait *pwait = NULL;
|
|
|
|
int i;
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
for (i = 0; i < ARRAY_SIZE(m->poll_wait); i++) {
|
|
|
|
if (m->poll_wait[i].wait_addr == NULL) {
|
|
|
|
pwait = &m->poll_wait[i];
|
|
|
|
break;
|
2008-02-07 09:25:03 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
if (!pwait) {
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_ERROR, "not enough wait_address slots\n");
|
2008-02-07 09:25:03 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
pwait->conn = m;
|
|
|
|
pwait->wait_addr = wait_address;
|
|
|
|
init_waitqueue_func_entry(&pwait->wait, p9_pollwake);
|
|
|
|
add_wait_queue(wait_address, &pwait->wait);
|
|
|
|
}
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
/**
|
|
|
|
* p9_conn_create - allocate and initialize the per-session mux data
|
|
|
|
* @client: client instance
|
|
|
|
*
|
|
|
|
* Note: Creates the polling task if this is the first session.
|
|
|
|
*/
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
static struct p9_conn *p9_conn_create(struct p9_client *client)
|
|
|
|
{
|
2008-10-14 07:45:20 +08:00
|
|
|
int n;
|
2008-10-14 07:45:24 +08:00
|
|
|
struct p9_conn *m;
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_TRANS, "client %p msize %d\n", client, client->msize);
|
2008-10-14 07:45:24 +08:00
|
|
|
m = kzalloc(sizeof(struct p9_conn), GFP_KERNEL);
|
|
|
|
if (!m)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
INIT_LIST_HEAD(&m->mux_list);
|
|
|
|
m->client = client;
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
INIT_LIST_HEAD(&m->req_list);
|
|
|
|
INIT_LIST_HEAD(&m->unsent_req_list);
|
|
|
|
INIT_WORK(&m->rq, p9_read_work);
|
|
|
|
INIT_WORK(&m->wq, p9_write_work);
|
|
|
|
INIT_LIST_HEAD(&m->poll_pending_link);
|
|
|
|
init_poll_funcptr(&m->pt, p9_pollwait);
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
n = p9_fd_poll(client, &m->pt);
|
|
|
|
if (n & POLLIN) {
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_TRANS, "mux %p can read\n", m);
|
2008-10-14 07:45:24 +08:00
|
|
|
set_bit(Rpending, &m->wsched);
|
|
|
|
}
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
if (n & POLLOUT) {
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_TRANS, "mux %p can write\n", m);
|
2008-10-14 07:45:24 +08:00
|
|
|
set_bit(Wpending, &m->wsched);
|
|
|
|
}
|
|
|
|
|
|
|
|
return m;
|
|
|
|
}
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
/**
|
|
|
|
* p9_poll_mux - polls a mux and schedules read or write works if necessary
|
|
|
|
* @m: connection to poll
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void p9_poll_mux(struct p9_conn *m)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
|
|
|
|
if (m->err < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
n = p9_fd_poll(m->client, NULL);
|
|
|
|
if (n < 0 || n & (POLLERR | POLLHUP | POLLNVAL)) {
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_TRANS, "error mux %p err %d\n", m, n);
|
2008-10-14 07:45:24 +08:00
|
|
|
if (n >= 0)
|
|
|
|
n = -ECONNRESET;
|
|
|
|
p9_conn_cancel(m, n);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (n & POLLIN) {
|
|
|
|
set_bit(Rpending, &m->wsched);
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_TRANS, "mux %p can read\n", m);
|
2008-10-14 07:45:24 +08:00
|
|
|
if (!test_and_set_bit(Rworksched, &m->wsched)) {
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_TRANS, "sched read work %p\n", m);
|
2011-02-01 18:42:43 +08:00
|
|
|
schedule_work(&m->rq);
|
2008-10-14 07:45:24 +08:00
|
|
|
}
|
|
|
|
}
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
if (n & POLLOUT) {
|
|
|
|
set_bit(Wpending, &m->wsched);
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_TRANS, "mux %p can write\n", m);
|
2009-11-30 08:55:45 +08:00
|
|
|
if ((m->wsize || !list_empty(&m->unsent_req_list)) &&
|
|
|
|
!test_and_set_bit(Wworksched, &m->wsched)) {
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_TRANS, "sched write work %p\n", m);
|
2011-02-01 18:42:43 +08:00
|
|
|
schedule_work(&m->wq);
|
2008-10-14 07:45:24 +08:00
|
|
|
}
|
|
|
|
}
|
2008-02-07 09:25:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-10-14 07:45:21 +08:00
|
|
|
* p9_fd_request - send 9P request
|
2008-02-07 09:25:03 +08:00
|
|
|
* The function can sleep until the request is scheduled for sending.
|
|
|
|
* The function can be interrupted. Return from the function is not
|
2008-10-14 07:45:21 +08:00
|
|
|
* a guarantee that the request is sent successfully.
|
2008-02-07 09:25:03 +08:00
|
|
|
*
|
2008-10-14 07:45:21 +08:00
|
|
|
* @client: client instance
|
|
|
|
* @req: request to be sent
|
2008-03-05 21:08:09 +08:00
|
|
|
*
|
2008-02-07 09:25:03 +08:00
|
|
|
*/
|
2008-03-05 21:08:09 +08:00
|
|
|
|
2008-10-14 07:45:21 +08:00
|
|
|
static int p9_fd_request(struct p9_client *client, struct p9_req_t *req)
|
2008-02-07 09:25:03 +08:00
|
|
|
{
|
|
|
|
int n;
|
2008-10-14 07:45:21 +08:00
|
|
|
struct p9_trans_fd *ts = client->trans;
|
|
|
|
struct p9_conn *m = ts->conn;
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_TRANS, "mux %p task %p tcall %p id %d\n",
|
|
|
|
m, current, req->tc, req->tc->id);
|
2008-02-07 09:25:03 +08:00
|
|
|
if (m->err < 0)
|
2008-10-14 07:45:21 +08:00
|
|
|
return m->err;
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-14 07:45:21 +08:00
|
|
|
spin_lock(&client->lock);
|
2008-10-18 01:45:40 +08:00
|
|
|
req->status = REQ_STATUS_UNSENT;
|
2008-02-07 09:25:03 +08:00
|
|
|
list_add_tail(&req->req_list, &m->unsent_req_list);
|
2008-10-14 07:45:21 +08:00
|
|
|
spin_unlock(&client->lock);
|
2008-02-07 09:25:03 +08:00
|
|
|
|
|
|
|
if (test_and_clear_bit(Wpending, &m->wsched))
|
|
|
|
n = POLLOUT;
|
|
|
|
else
|
2008-10-14 07:45:25 +08:00
|
|
|
n = p9_fd_poll(m->client, NULL);
|
2008-02-07 09:25:03 +08:00
|
|
|
|
|
|
|
if (n & POLLOUT && !test_and_set_bit(Wworksched, &m->wsched))
|
2011-02-01 18:42:43 +08:00
|
|
|
schedule_work(&m->wq);
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-14 07:45:21 +08:00
|
|
|
return 0;
|
2008-02-07 09:25:03 +08:00
|
|
|
}
|
|
|
|
|
2008-10-14 07:45:21 +08:00
|
|
|
static int p9_fd_cancel(struct p9_client *client, struct p9_req_t *req)
|
2008-02-07 09:25:03 +08:00
|
|
|
{
|
2008-10-18 01:45:40 +08:00
|
|
|
int ret = 1;
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_TRANS, "client %p req %p\n", client, req);
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-14 07:45:21 +08:00
|
|
|
spin_lock(&client->lock);
|
|
|
|
|
|
|
|
if (req->status == REQ_STATUS_UNSENT) {
|
2009-04-06 05:28:59 +08:00
|
|
|
list_del(&req->req_list);
|
2008-10-14 07:45:21 +08:00
|
|
|
req->status = REQ_STATUS_FLSHD;
|
2008-10-18 01:45:40 +08:00
|
|
|
ret = 0;
|
2009-04-06 05:28:59 +08:00
|
|
|
} else if (req->status == REQ_STATUS_SENT)
|
|
|
|
req->status = REQ_STATUS_FLSH;
|
2008-02-07 09:25:03 +08:00
|
|
|
|
2008-10-18 01:45:40 +08:00
|
|
|
spin_unlock(&client->lock);
|
|
|
|
|
|
|
|
return ret;
|
2008-02-07 09:25:03 +08:00
|
|
|
}
|
|
|
|
|
2007-10-18 03:31:07 +08:00
|
|
|
/**
|
2009-07-20 03:41:55 +08:00
|
|
|
* parse_opts - parse mount options into p9_fd_opts structure
|
|
|
|
* @params: options string passed from mount
|
|
|
|
* @opts: fd transport-specific structure to parse options into
|
2007-10-18 03:31:07 +08:00
|
|
|
*
|
2008-03-08 00:53:53 +08:00
|
|
|
* Returns 0 upon success, -ERRNO upon failure
|
2007-10-18 03:31:07 +08:00
|
|
|
*/
|
2007-07-11 06:57:28 +08:00
|
|
|
|
2008-03-08 00:53:53 +08:00
|
|
|
static int parse_opts(char *params, struct p9_fd_opts *opts)
|
2007-07-11 06:57:28 +08:00
|
|
|
{
|
2007-10-18 03:31:07 +08:00
|
|
|
char *p;
|
|
|
|
substring_t args[MAX_OPT_ARGS];
|
|
|
|
int option;
|
2010-02-09 06:23:23 +08:00
|
|
|
char *options, *tmp_options;
|
2007-07-11 06:57:28 +08:00
|
|
|
|
2007-10-18 03:31:07 +08:00
|
|
|
opts->port = P9_PORT;
|
|
|
|
opts->rfd = ~0;
|
|
|
|
opts->wfd = ~0;
|
2007-07-11 06:57:28 +08:00
|
|
|
|
2008-03-08 00:53:53 +08:00
|
|
|
if (!params)
|
|
|
|
return 0;
|
|
|
|
|
2010-02-09 06:23:23 +08:00
|
|
|
tmp_options = kstrdup(params, GFP_KERNEL);
|
|
|
|
if (!tmp_options) {
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_ERROR,
|
|
|
|
"failed to allocate copy of option string\n");
|
2008-03-08 00:53:53 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2010-02-09 06:23:23 +08:00
|
|
|
options = tmp_options;
|
2007-07-11 06:57:28 +08:00
|
|
|
|
2007-10-18 03:31:07 +08:00
|
|
|
while ((p = strsep(&options, ",")) != NULL) {
|
|
|
|
int token;
|
2008-03-08 00:53:53 +08:00
|
|
|
int r;
|
2007-10-18 03:31:07 +08:00
|
|
|
if (!*p)
|
|
|
|
continue;
|
|
|
|
token = match_token(p, tokens, args);
|
2013-05-30 03:15:07 +08:00
|
|
|
if ((token != Opt_err) && (token != Opt_privport)) {
|
2009-07-01 16:50:54 +08:00
|
|
|
r = match_int(&args[0], &option);
|
|
|
|
if (r < 0) {
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_ERROR,
|
|
|
|
"integer field, but no integer?\n");
|
2009-07-01 16:50:54 +08:00
|
|
|
continue;
|
|
|
|
}
|
2007-10-18 03:31:07 +08:00
|
|
|
}
|
|
|
|
switch (token) {
|
|
|
|
case Opt_port:
|
|
|
|
opts->port = option;
|
|
|
|
break;
|
|
|
|
case Opt_rfdno:
|
|
|
|
opts->rfd = option;
|
|
|
|
break;
|
|
|
|
case Opt_wfdno:
|
|
|
|
opts->wfd = option;
|
|
|
|
break;
|
2013-05-30 03:15:07 +08:00
|
|
|
case Opt_privport:
|
|
|
|
opts->privport = 1;
|
|
|
|
break;
|
2007-10-18 03:31:07 +08:00
|
|
|
default:
|
|
|
|
continue;
|
|
|
|
}
|
2007-07-11 06:57:28 +08:00
|
|
|
}
|
2010-02-09 06:23:23 +08:00
|
|
|
|
|
|
|
kfree(tmp_options);
|
2008-03-08 00:53:53 +08:00
|
|
|
return 0;
|
2007-07-11 06:57:28 +08:00
|
|
|
}
|
|
|
|
|
2008-10-14 07:45:25 +08:00
|
|
|
static int p9_fd_open(struct p9_client *client, int rfd, int wfd)
|
2007-07-11 06:57:28 +08:00
|
|
|
{
|
2007-10-18 03:31:07 +08:00
|
|
|
struct p9_trans_fd *ts = kmalloc(sizeof(struct p9_trans_fd),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!ts)
|
|
|
|
return -ENOMEM;
|
2007-07-11 06:57:28 +08:00
|
|
|
|
2007-10-18 03:31:07 +08:00
|
|
|
ts->rd = fget(rfd);
|
|
|
|
ts->wr = fget(wfd);
|
|
|
|
if (!ts->rd || !ts->wr) {
|
|
|
|
if (ts->rd)
|
|
|
|
fput(ts->rd);
|
|
|
|
if (ts->wr)
|
|
|
|
fput(ts->wr);
|
|
|
|
kfree(ts);
|
|
|
|
return -EIO;
|
2007-07-11 06:57:28 +08:00
|
|
|
}
|
|
|
|
|
2008-10-14 07:45:25 +08:00
|
|
|
client->trans = ts;
|
|
|
|
client->status = Connected;
|
2007-07-11 06:57:28 +08:00
|
|
|
|
2007-10-18 03:31:07 +08:00
|
|
|
return 0;
|
2007-07-11 06:57:28 +08:00
|
|
|
}
|
|
|
|
|
2008-10-14 07:45:25 +08:00
|
|
|
static int p9_socket_open(struct p9_client *client, struct socket *csocket)
|
2007-07-11 06:57:28 +08:00
|
|
|
{
|
2009-08-06 06:02:43 +08:00
|
|
|
struct p9_trans_fd *p;
|
2012-08-18 12:25:51 +08:00
|
|
|
struct file *file;
|
|
|
|
int ret;
|
2009-08-06 06:02:43 +08:00
|
|
|
|
|
|
|
p = kmalloc(sizeof(struct p9_trans_fd), GFP_KERNEL);
|
|
|
|
if (!p)
|
|
|
|
return -ENOMEM;
|
2007-07-11 06:57:28 +08:00
|
|
|
|
|
|
|
csocket->sk->sk_allocation = GFP_NOIO;
|
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull vfs update from Al Viro:
- big one - consolidation of descriptor-related logics; almost all of
that is moved to fs/file.c
(BTW, I'm seriously tempted to rename the result to fd.c. As it is,
we have a situation when file_table.c is about handling of struct
file and file.c is about handling of descriptor tables; the reasons
are historical - file_table.c used to be about a static array of
struct file we used to have way back).
A lot of stray ends got cleaned up and converted to saner primitives,
disgusting mess in android/binder.c is still disgusting, but at least
doesn't poke so much in descriptor table guts anymore. A bunch of
relatively minor races got fixed in process, plus an ext4 struct file
leak.
- related thing - fget_light() partially unuglified; see fdget() in
there (and yes, it generates the code as good as we used to have).
- also related - bits of Cyrill's procfs stuff that got entangled into
that work; _not_ all of it, just the initial move to fs/proc/fd.c and
switch of fdinfo to seq_file.
- Alex's fs/coredump.c spiltoff - the same story, had been easier to
take that commit than mess with conflicts. The rest is a separate
pile, this was just a mechanical code movement.
- a few misc patches all over the place. Not all for this cycle,
there'll be more (and quite a few currently sit in akpm's tree)."
Fix up trivial conflicts in the android binder driver, and some fairly
simple conflicts due to two different changes to the sock_alloc_file()
interface ("take descriptor handling from sock_alloc_file() to callers"
vs "net: Providing protocol type via system.sockprotoname xattr of
/proc/PID/fd entries" adding a dentry name to the socket)
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (72 commits)
MAX_LFS_FILESIZE should be a loff_t
compat: fs: Generic compat_sys_sendfile implementation
fs: push rcu_barrier() from deactivate_locked_super() to filesystems
btrfs: reada_extent doesn't need kref for refcount
coredump: move core dump functionality into its own file
coredump: prevent double-free on an error path in core dumper
usb/gadget: fix misannotations
fcntl: fix misannotations
ceph: don't abuse d_delete() on failure exits
hypfs: ->d_parent is never NULL or negative
vfs: delete surplus inode NULL check
switch simple cases of fget_light to fdget
new helpers: fdget()/fdput()
switch o2hb_region_dev_write() to fget_light()
proc_map_files_readdir(): don't bother with grabbing files
make get_file() return its argument
vhost_set_vring(): turn pollstart/pollstop into bool
switch prctl_set_mm_exe_file() to fget_light()
switch xfs_find_handle() to fget_light()
switch xfs_swapext() to fget_light()
...
2012-10-03 11:25:04 +08:00
|
|
|
file = sock_alloc_file(csocket, 0, NULL);
|
2012-08-18 12:25:51 +08:00
|
|
|
if (IS_ERR(file)) {
|
2011-11-29 02:40:46 +08:00
|
|
|
pr_err("%s (%d): failed to map fd\n",
|
|
|
|
__func__, task_pid_nr(current));
|
2009-08-06 06:02:43 +08:00
|
|
|
sock_release(csocket);
|
|
|
|
kfree(p);
|
2012-08-18 12:25:51 +08:00
|
|
|
return PTR_ERR(file);
|
2007-07-11 06:57:28 +08:00
|
|
|
}
|
|
|
|
|
2012-08-18 12:25:51 +08:00
|
|
|
get_file(file);
|
|
|
|
p->wr = p->rd = file;
|
2009-08-06 06:02:43 +08:00
|
|
|
client->trans = p;
|
|
|
|
client->status = Connected;
|
|
|
|
|
|
|
|
p->rd->f_flags |= O_NONBLOCK;
|
|
|
|
|
|
|
|
p->conn = p9_conn_create(client);
|
|
|
|
if (IS_ERR(p->conn)) {
|
|
|
|
ret = PTR_ERR(p->conn);
|
|
|
|
p->conn = NULL;
|
|
|
|
kfree(p);
|
|
|
|
sockfd_put(csocket);
|
2007-07-11 06:57:28 +08:00
|
|
|
sockfd_put(csocket);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-10-14 07:45:24 +08:00
|
|
|
* p9_mux_destroy - cancels all pending requests and frees mux resources
|
|
|
|
* @m: mux to destroy
|
2007-07-11 06:57:28 +08:00
|
|
|
*
|
|
|
|
*/
|
2008-03-05 21:08:09 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
static void p9_conn_destroy(struct p9_conn *m)
|
2007-07-11 06:57:28 +08:00
|
|
|
{
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_TRANS, "mux %p prev %p next %p\n",
|
|
|
|
m, m->mux_list.prev, m->mux_list.next);
|
2007-07-11 06:57:28 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
p9_mux_poll_stop(m);
|
|
|
|
cancel_work_sync(&m->rq);
|
|
|
|
cancel_work_sync(&m->wq);
|
2007-07-11 06:57:28 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
p9_conn_cancel(m, -ECONNRESET);
|
2007-07-11 06:57:28 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
m->client = NULL;
|
|
|
|
kfree(m);
|
2007-07-11 06:57:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-10-14 07:45:25 +08:00
|
|
|
* p9_fd_close - shutdown file descriptor transport
|
|
|
|
* @client: client instance
|
2007-07-11 06:57:28 +08:00
|
|
|
*
|
|
|
|
*/
|
2008-03-05 21:08:09 +08:00
|
|
|
|
2008-10-14 07:45:25 +08:00
|
|
|
static void p9_fd_close(struct p9_client *client)
|
2007-07-11 06:57:28 +08:00
|
|
|
{
|
|
|
|
struct p9_trans_fd *ts;
|
|
|
|
|
2008-10-14 07:45:25 +08:00
|
|
|
if (!client)
|
2007-07-11 06:57:28 +08:00
|
|
|
return;
|
|
|
|
|
2008-10-14 07:45:25 +08:00
|
|
|
ts = client->trans;
|
2007-07-11 06:57:28 +08:00
|
|
|
if (!ts)
|
|
|
|
return;
|
|
|
|
|
2008-10-14 07:45:25 +08:00
|
|
|
client->status = Disconnected;
|
|
|
|
|
2008-02-07 09:25:03 +08:00
|
|
|
p9_conn_destroy(ts->conn);
|
|
|
|
|
2007-07-11 06:57:28 +08:00
|
|
|
if (ts->rd)
|
|
|
|
fput(ts->rd);
|
|
|
|
if (ts->wr)
|
|
|
|
fput(ts->wr);
|
2008-10-14 07:45:25 +08:00
|
|
|
|
2007-07-11 06:57:28 +08:00
|
|
|
kfree(ts);
|
|
|
|
}
|
|
|
|
|
2008-05-09 09:26:37 +08:00
|
|
|
/*
|
|
|
|
* stolen from NFS - maybe should be made a generic function?
|
|
|
|
*/
|
|
|
|
static inline int valid_ipaddr4(const char *buf)
|
|
|
|
{
|
|
|
|
int rc, count, in[4];
|
|
|
|
|
|
|
|
rc = sscanf(buf, "%d.%d.%d.%d", &in[0], &in[1], &in[2], &in[3]);
|
|
|
|
if (rc != 4)
|
|
|
|
return -EINVAL;
|
|
|
|
for (count = 0; count < 4; count++) {
|
|
|
|
if (in[count] > 255)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-05-30 03:15:07 +08:00
|
|
|
static int p9_bind_privport(struct socket *sock)
|
|
|
|
{
|
|
|
|
struct sockaddr_in cl;
|
|
|
|
int port, err = -EINVAL;
|
|
|
|
|
|
|
|
memset(&cl, 0, sizeof(cl));
|
|
|
|
cl.sin_family = AF_INET;
|
|
|
|
cl.sin_addr.s_addr = INADDR_ANY;
|
|
|
|
for (port = p9_ipport_resv_max; port >= p9_ipport_resv_min; port--) {
|
|
|
|
cl.sin_port = htons((ushort)port);
|
|
|
|
err = kernel_bind(sock, (struct sockaddr *)&cl, sizeof(cl));
|
|
|
|
if (err != -EADDRINUSE)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-14 07:45:25 +08:00
|
|
|
static int
|
|
|
|
p9_fd_create_tcp(struct p9_client *client, const char *addr, char *args)
|
2007-10-18 03:31:07 +08:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
struct socket *csocket;
|
|
|
|
struct sockaddr_in sin_server;
|
|
|
|
struct p9_fd_opts opts;
|
|
|
|
|
2008-03-08 00:53:53 +08:00
|
|
|
err = parse_opts(args, &opts);
|
|
|
|
if (err < 0)
|
2008-10-14 07:45:25 +08:00
|
|
|
return err;
|
2007-10-18 03:31:07 +08:00
|
|
|
|
2008-05-09 09:26:37 +08:00
|
|
|
if (valid_ipaddr4(addr) < 0)
|
2008-10-14 07:45:25 +08:00
|
|
|
return -EINVAL;
|
2008-05-09 09:26:37 +08:00
|
|
|
|
2007-10-18 03:31:07 +08:00
|
|
|
csocket = NULL;
|
|
|
|
|
|
|
|
sin_server.sin_family = AF_INET;
|
|
|
|
sin_server.sin_addr.s_addr = in_aton(addr);
|
|
|
|
sin_server.sin_port = htons(opts.port);
|
2011-05-24 10:46:56 +08:00
|
|
|
err = __sock_create(read_pnet(¤t->nsproxy->net_ns), PF_INET,
|
|
|
|
SOCK_STREAM, IPPROTO_TCP, &csocket, 1);
|
2009-08-06 06:02:43 +08:00
|
|
|
if (err) {
|
2011-11-29 02:40:46 +08:00
|
|
|
pr_err("%s (%d): problem creating socket\n",
|
|
|
|
__func__, task_pid_nr(current));
|
2009-08-06 06:02:43 +08:00
|
|
|
return err;
|
2007-10-18 03:31:07 +08:00
|
|
|
}
|
|
|
|
|
2013-05-30 03:15:07 +08:00
|
|
|
if (opts.privport) {
|
|
|
|
err = p9_bind_privport(csocket);
|
|
|
|
if (err < 0) {
|
|
|
|
pr_err("%s (%d): problem binding to privport\n",
|
|
|
|
__func__, task_pid_nr(current));
|
|
|
|
sock_release(csocket);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-10-18 03:31:07 +08:00
|
|
|
err = csocket->ops->connect(csocket,
|
|
|
|
(struct sockaddr *)&sin_server,
|
|
|
|
sizeof(struct sockaddr_in), 0);
|
|
|
|
if (err < 0) {
|
2011-11-29 02:40:46 +08:00
|
|
|
pr_err("%s (%d): problem connecting socket to %s\n",
|
|
|
|
__func__, task_pid_nr(current), addr);
|
2007-10-18 03:31:07 +08:00
|
|
|
sock_release(csocket);
|
2009-08-06 06:02:43 +08:00
|
|
|
return err;
|
|
|
|
}
|
2007-10-18 03:31:07 +08:00
|
|
|
|
2009-08-06 06:02:43 +08:00
|
|
|
return p9_socket_open(client, csocket);
|
2007-10-18 03:31:07 +08:00
|
|
|
}
|
|
|
|
|
2008-10-14 07:45:25 +08:00
|
|
|
static int
|
|
|
|
p9_fd_create_unix(struct p9_client *client, const char *addr, char *args)
|
2007-10-18 03:31:07 +08:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
struct socket *csocket;
|
|
|
|
struct sockaddr_un sun_server;
|
|
|
|
|
|
|
|
csocket = NULL;
|
|
|
|
|
2010-07-10 17:51:54 +08:00
|
|
|
if (strlen(addr) >= UNIX_PATH_MAX) {
|
2011-11-29 02:40:46 +08:00
|
|
|
pr_err("%s (%d): address too long: %s\n",
|
|
|
|
__func__, task_pid_nr(current), addr);
|
2009-08-06 06:02:43 +08:00
|
|
|
return -ENAMETOOLONG;
|
2007-10-18 03:31:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
sun_server.sun_family = PF_UNIX;
|
|
|
|
strcpy(sun_server.sun_path, addr);
|
2011-05-24 10:46:56 +08:00
|
|
|
err = __sock_create(read_pnet(¤t->nsproxy->net_ns), PF_UNIX,
|
|
|
|
SOCK_STREAM, 0, &csocket, 1);
|
2009-08-06 06:02:43 +08:00
|
|
|
if (err < 0) {
|
2011-11-29 02:40:46 +08:00
|
|
|
pr_err("%s (%d): problem creating socket\n",
|
|
|
|
__func__, task_pid_nr(current));
|
|
|
|
|
2009-08-06 06:02:43 +08:00
|
|
|
return err;
|
|
|
|
}
|
2007-10-18 03:31:07 +08:00
|
|
|
err = csocket->ops->connect(csocket, (struct sockaddr *)&sun_server,
|
|
|
|
sizeof(struct sockaddr_un) - 1, 0);
|
|
|
|
if (err < 0) {
|
2011-11-29 02:40:46 +08:00
|
|
|
pr_err("%s (%d): problem connecting socket: %s: %d\n",
|
|
|
|
__func__, task_pid_nr(current), addr, err);
|
2007-10-18 03:31:07 +08:00
|
|
|
sock_release(csocket);
|
2009-08-06 06:02:43 +08:00
|
|
|
return err;
|
|
|
|
}
|
2007-10-18 03:31:07 +08:00
|
|
|
|
2009-08-06 06:02:43 +08:00
|
|
|
return p9_socket_open(client, csocket);
|
2007-10-18 03:31:07 +08:00
|
|
|
}
|
|
|
|
|
2008-10-14 07:45:25 +08:00
|
|
|
static int
|
|
|
|
p9_fd_create(struct p9_client *client, const char *addr, char *args)
|
2007-10-18 03:31:07 +08:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
struct p9_fd_opts opts;
|
2009-08-06 06:02:43 +08:00
|
|
|
struct p9_trans_fd *p;
|
2007-10-18 03:31:07 +08:00
|
|
|
|
|
|
|
parse_opts(args, &opts);
|
|
|
|
|
|
|
|
if (opts.rfd == ~0 || opts.wfd == ~0) {
|
2011-11-29 02:40:46 +08:00
|
|
|
pr_err("Insufficient options for proto=fd\n");
|
2008-10-14 07:45:25 +08:00
|
|
|
return -ENOPROTOOPT;
|
2007-10-18 03:31:07 +08:00
|
|
|
}
|
|
|
|
|
2008-10-14 07:45:25 +08:00
|
|
|
err = p9_fd_open(client, opts.rfd, opts.wfd);
|
2007-10-18 03:31:07 +08:00
|
|
|
if (err < 0)
|
2009-08-06 06:02:43 +08:00
|
|
|
return err;
|
2007-10-18 03:31:07 +08:00
|
|
|
|
2008-10-14 07:45:25 +08:00
|
|
|
p = (struct p9_trans_fd *) client->trans;
|
|
|
|
p->conn = p9_conn_create(client);
|
2008-02-07 09:25:03 +08:00
|
|
|
if (IS_ERR(p->conn)) {
|
|
|
|
err = PTR_ERR(p->conn);
|
|
|
|
p->conn = NULL;
|
2009-08-06 06:02:43 +08:00
|
|
|
fput(p->rd);
|
|
|
|
fput(p->wr);
|
|
|
|
return err;
|
2008-02-07 09:25:03 +08:00
|
|
|
}
|
|
|
|
|
2008-10-14 07:45:25 +08:00
|
|
|
return 0;
|
2007-10-18 03:31:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct p9_trans_module p9_tcp_trans = {
|
|
|
|
.name = "tcp",
|
|
|
|
.maxsize = MAX_SOCK_BUF,
|
2013-11-13 00:20:03 +08:00
|
|
|
.def = 0,
|
2008-10-14 07:45:25 +08:00
|
|
|
.create = p9_fd_create_tcp,
|
|
|
|
.close = p9_fd_close,
|
2008-10-14 07:45:21 +08:00
|
|
|
.request = p9_fd_request,
|
|
|
|
.cancel = p9_fd_cancel,
|
2008-09-25 05:22:23 +08:00
|
|
|
.owner = THIS_MODULE,
|
2007-10-18 03:31:07 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct p9_trans_module p9_unix_trans = {
|
|
|
|
.name = "unix",
|
|
|
|
.maxsize = MAX_SOCK_BUF,
|
|
|
|
.def = 0,
|
2008-10-14 07:45:25 +08:00
|
|
|
.create = p9_fd_create_unix,
|
|
|
|
.close = p9_fd_close,
|
2008-10-14 07:45:21 +08:00
|
|
|
.request = p9_fd_request,
|
|
|
|
.cancel = p9_fd_cancel,
|
2008-09-25 05:22:23 +08:00
|
|
|
.owner = THIS_MODULE,
|
2007-10-18 03:31:07 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct p9_trans_module p9_fd_trans = {
|
|
|
|
.name = "fd",
|
|
|
|
.maxsize = MAX_SOCK_BUF,
|
|
|
|
.def = 0,
|
2008-10-14 07:45:25 +08:00
|
|
|
.create = p9_fd_create,
|
|
|
|
.close = p9_fd_close,
|
2008-10-14 07:45:21 +08:00
|
|
|
.request = p9_fd_request,
|
|
|
|
.cancel = p9_fd_cancel,
|
2008-09-25 05:22:23 +08:00
|
|
|
.owner = THIS_MODULE,
|
2007-10-18 03:31:07 +08:00
|
|
|
};
|
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
/**
|
|
|
|
* p9_poll_proc - poll worker thread
|
|
|
|
* @a: thread state and arguments
|
|
|
|
*
|
|
|
|
* polls all v9fs transports for new events and queues the appropriate
|
|
|
|
* work to the work queue
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2011-02-01 18:42:43 +08:00
|
|
|
static void p9_poll_workfn(struct work_struct *work)
|
2008-10-14 07:45:24 +08:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_TRANS, "start %p\n", current);
|
2011-02-01 18:42:43 +08:00
|
|
|
|
2008-10-14 07:45:24 +08:00
|
|
|
spin_lock_irqsave(&p9_poll_lock, flags);
|
|
|
|
while (!list_empty(&p9_poll_pending_list)) {
|
|
|
|
struct p9_conn *conn = list_first_entry(&p9_poll_pending_list,
|
|
|
|
struct p9_conn,
|
|
|
|
poll_pending_link);
|
|
|
|
list_del_init(&conn->poll_pending_link);
|
|
|
|
spin_unlock_irqrestore(&p9_poll_lock, flags);
|
|
|
|
|
|
|
|
p9_poll_mux(conn);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&p9_poll_lock, flags);
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&p9_poll_lock, flags);
|
|
|
|
|
2011-11-29 02:40:46 +08:00
|
|
|
p9_debug(P9_DEBUG_TRANS, "finish\n");
|
2008-10-14 07:45:24 +08:00
|
|
|
}
|
|
|
|
|
2008-05-09 09:26:37 +08:00
|
|
|
int p9_trans_fd_init(void)
|
2007-10-18 03:31:07 +08:00
|
|
|
{
|
|
|
|
v9fs_register_trans(&p9_tcp_trans);
|
|
|
|
v9fs_register_trans(&p9_unix_trans);
|
|
|
|
v9fs_register_trans(&p9_fd_trans);
|
|
|
|
|
2008-03-29 05:15:57 +08:00
|
|
|
return 0;
|
2007-10-18 03:31:07 +08:00
|
|
|
}
|
2008-09-25 05:22:23 +08:00
|
|
|
|
|
|
|
void p9_trans_fd_exit(void)
|
|
|
|
{
|
2012-08-21 05:51:24 +08:00
|
|
|
flush_work(&p9_poll_work);
|
2008-09-25 05:22:23 +08:00
|
|
|
v9fs_unregister_trans(&p9_tcp_trans);
|
|
|
|
v9fs_unregister_trans(&p9_unix_trans);
|
|
|
|
v9fs_unregister_trans(&p9_fd_trans);
|
|
|
|
}
|