aboutsummaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/9p/Makefile1
-rw-r--r--net/9p/client.c161
-rw-r--r--net/9p/fcprint.c4
-rw-r--r--net/9p/mod.c9
-rw-r--r--net/9p/mux.c1060
-rw-r--r--net/9p/trans_fd.c1103
-rw-r--r--net/9p/trans_virtio.c355
-rw-r--r--net/9p/util.c20
-rw-r--r--net/Kconfig2
-rw-r--r--net/can/af_can.c45
-rw-r--r--net/can/raw.c24
-rw-r--r--net/core/flow.c6
-rw-r--r--net/decnet/dn_route.c2
-rw-r--r--net/ipv4/cipso_ipv4.c4
-rw-r--r--net/ipv4/ipvs/ip_vs_wrr.c3
-rw-r--r--net/ipv4/netfilter/nf_nat_core.c6
-rw-r--r--net/ipv4/route.c2
-rw-r--r--net/iucv/af_iucv.c27
-rw-r--r--net/iucv/iucv.c4
-rw-r--r--net/mac80211/Kconfig1
-rw-r--r--net/netfilter/nf_conntrack_extend.c3
-rw-r--r--net/netfilter/nf_conntrack_proto_tcp.c32
-rw-r--r--net/netfilter/xt_iprange.c3
-rw-r--r--net/netlabel/netlabel_cipso_v4.c2
-rw-r--r--net/netlabel/netlabel_cipso_v4.h3
-rw-r--r--net/netlabel/netlabel_domainhash.h1
-rw-r--r--net/netlabel/netlabel_kapi.c177
-rw-r--r--net/rxrpc/af_rxrpc.c6
-rw-r--r--net/sched/cls_flow.c17
-rw-r--r--net/sched/em_meta.c12
-rw-r--r--net/tipc/addr.h5
-rw-r--r--net/tipc/bcast.h13
-rw-r--r--net/tipc/msg.h5
-rw-r--r--net/tipc/socket.c14
-rw-r--r--net/xfrm/xfrm_algo.c17
35 files changed, 1759 insertions, 1390 deletions
diff --git a/net/9p/Makefile b/net/9p/Makefile
index d3abb246cca..8a105110189 100644
--- a/net/9p/Makefile
+++ b/net/9p/Makefile
@@ -4,7 +4,6 @@ obj-$(CONFIG_NET_9P_VIRTIO) += 9pnet_virtio.o
9pnet-objs := \
mod.o \
- mux.o \
client.o \
conv.o \
error.o \
diff --git a/net/9p/client.c b/net/9p/client.c
index af919936404..84e087e2414 100644
--- a/net/9p/client.c
+++ b/net/9p/client.c
@@ -3,6 +3,7 @@
*
* 9P Client
*
+ * Copyright (C) 2008 by Eric Van Hensbergen <ericvh@gmail.com>
* Copyright (C) 2007 by Latchesar Ionkov <lucho@ionkov.net>
*
* This program is free software; you can redistribute it and/or modify
@@ -25,6 +26,7 @@
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/fs.h>
+#include <linux/poll.h>
#include <linux/idr.h>
#include <linux/mutex.h>
#include <linux/sched.h>
@@ -32,15 +34,97 @@
#include <net/9p/9p.h>
#include <linux/parser.h>
#include <net/9p/transport.h>
-#include <net/9p/conn.h>
#include <net/9p/client.h>
static struct p9_fid *p9_fid_create(struct p9_client *clnt);
static void p9_fid_destroy(struct p9_fid *fid);
static struct p9_stat *p9_clone_stat(struct p9_stat *st, int dotu);
-struct p9_client *p9_client_create(struct p9_trans *trans, int msize,
- int dotu)
+/*
+ * Client Option Parsing (code inspired by NFS code)
+ * - a little lazy - parse all client options
+ */
+
+enum {
+ Opt_msize,
+ Opt_trans,
+ Opt_legacy,
+ Opt_err,
+};
+
+static match_table_t tokens = {
+ {Opt_msize, "msize=%u"},
+ {Opt_legacy, "noextend"},
+ {Opt_trans, "trans=%s"},
+ {Opt_err, NULL},
+};
+
+/**
+ * v9fs_parse_options - parse mount options into session structure
+ * @options: options string passed from mount
+ * @v9ses: existing v9fs session information
+ *
+ */
+
+static void parse_opts(char *options, struct p9_client *clnt)
+{
+ char *p;
+ substring_t args[MAX_OPT_ARGS];
+ int option;
+ int ret;
+
+ clnt->trans_mod = v9fs_default_trans();
+ clnt->dotu = 1;
+ clnt->msize = 8192;
+
+ if (!options)
+ return;
+
+ while ((p = strsep(&options, ",")) != NULL) {
+ int token;
+ if (!*p)
+ continue;
+ token = match_token(p, tokens, args);
+ if (token < Opt_trans) {
+ ret = match_int(&args[0], &option);
+ if (ret < 0) {
+ P9_DPRINTK(P9_DEBUG_ERROR,
+ "integer field, but no integer?\n");
+ continue;
+ }
+ }
+ switch (token) {
+ case Opt_msize:
+ clnt->msize = option;
+ break;
+ case Opt_trans:
+ clnt->trans_mod = v9fs_match_trans(&args[0]);
+ break;
+ case Opt_legacy:
+ clnt->dotu = 0;
+ break;
+ default:
+ continue;
+ }
+ }
+}
+
+
+/**
+ * p9_client_rpc - sends 9P request and waits until a response is available.
+ * The function can be interrupted.
+ * @c: client data
+ * @tc: request to be sent
+ * @rc: pointer where a pointer to the response is stored
+ */
+int
+p9_client_rpc(struct p9_client *c, struct p9_fcall *tc,
+ struct p9_fcall **rc)
+{
+ return c->trans->rpc(c->trans, tc, rc);
+}
+
+struct p9_client *p9_client_create(const char *dev_name, char *options)
{
int err, n;
struct p9_client *clnt;
@@ -54,12 +138,7 @@ struct p9_client *p9_client_create(struct p9_trans *trans, int msize,
if (!clnt)
return ERR_PTR(-ENOMEM);
- P9_DPRINTK(P9_DEBUG_9P, "clnt %p trans %p msize %d dotu %d\n",
- clnt, trans, msize, dotu);
spin_lock_init(&clnt->lock);
- clnt->trans = trans;
- clnt->msize = msize;
- clnt->dotu = dotu;
INIT_LIST_HEAD(&clnt->fidlist);
clnt->fidpool = p9_idpool_create();
if (!clnt->fidpool) {
@@ -68,13 +147,29 @@ struct p9_client *p9_client_create(struct p9_trans *trans, int msize,
goto error;
}
- clnt->conn = p9_conn_create(clnt->trans, clnt->msize, &clnt->dotu);
- if (IS_ERR(clnt->conn)) {
- err = PTR_ERR(clnt->conn);
- clnt->conn = NULL;
+ parse_opts(options, clnt);
+ if (clnt->trans_mod == NULL) {
+ err = -EPROTONOSUPPORT;
+ P9_DPRINTK(P9_DEBUG_ERROR,
+ "No transport defined or default transport\n");
goto error;
}
+ P9_DPRINTK(P9_DEBUG_9P, "clnt %p trans %p msize %d dotu %d\n",
+ clnt, clnt->trans_mod, clnt->msize, clnt->dotu);
+
+
+ clnt->trans = clnt->trans_mod->create(dev_name, options, clnt->msize,
+ clnt->dotu);
+ if (IS_ERR(clnt->trans)) {
+ err = PTR_ERR(clnt->trans);
+ clnt->trans = NULL;
+ goto error;
+ }
+
+ if ((clnt->msize+P9_IOHDRSZ) > clnt->trans_mod->maxsize)
+ clnt->msize = clnt->trans_mod->maxsize-P9_IOHDRSZ;
+
tc = p9_create_tversion(clnt->msize, clnt->dotu?"9P2000.u":"9P2000");
if (IS_ERR(tc)) {
err = PTR_ERR(tc);
@@ -82,7 +177,7 @@ struct p9_client *p9_client_create(struct p9_trans *trans, int msize,
goto error;
}
- err = p9_conn_rpc(clnt->conn, tc, &rc);
+ err = p9_client_rpc(clnt, tc, &rc);
if (err)
goto error;
@@ -117,10 +212,6 @@ void p9_client_destroy(struct p9_client *clnt)
struct p9_fid *fid, *fidptr;
P9_DPRINTK(P9_DEBUG_9P, "clnt %p\n", clnt);
- if (clnt->conn) {
- p9_conn_destroy(clnt->conn);
- clnt->conn = NULL;
- }
if (clnt->trans) {
clnt->trans->close(clnt->trans);
@@ -142,7 +233,6 @@ void p9_client_disconnect(struct p9_client *clnt)
{
P9_DPRINTK(P9_DEBUG_9P, "clnt %p\n", clnt);
clnt->trans->status = Disconnected;
- p9_conn_cancel(clnt->conn, -EIO);
}
EXPORT_SYMBOL(p9_client_disconnect);
@@ -174,7 +264,7 @@ struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *afid,
goto error;
}
- err = p9_conn_rpc(clnt->conn, tc, &rc);
+ err = p9_client_rpc(clnt, tc, &rc);
if (err)
goto error;
@@ -219,7 +309,7 @@ struct p9_fid *p9_client_auth(struct p9_client *clnt, char *uname,
goto error;
}
- err = p9_conn_rpc(clnt->conn, tc, &rc);
+ err = p9_client_rpc(clnt, tc, &rc);
if (err)
goto error;
@@ -270,7 +360,7 @@ struct p9_fid *p9_client_walk(struct p9_fid *oldfid, int nwname, char **wnames,
goto error;
}
- err = p9_conn_rpc(clnt->conn, tc, &rc);
+ err = p9_client_rpc(clnt, tc, &rc);
if (err) {
if (rc && rc->id == P9_RWALK)
goto clunk_fid;
@@ -305,7 +395,7 @@ clunk_fid:
goto error;
}
- p9_conn_rpc(clnt->conn, tc, &rc);
+ p9_client_rpc(clnt, tc, &rc);
error:
kfree(tc);
@@ -339,7 +429,7 @@ int p9_client_open(struct p9_fid *fid, int mode)
goto done;
}
- err = p9_conn_rpc(clnt->conn, tc, &rc);
+ err = p9_client_rpc(clnt, tc, &rc);
if (err)
goto done;
@@ -378,7 +468,7 @@ int p9_client_fcreate(struct p9_fid *fid, char *name, u32 perm, int mode,
goto done;
}
- err = p9_conn_rpc(clnt->conn, tc, &rc);
+ err = p9_client_rpc(clnt, tc, &rc);
if (err)
goto done;
@@ -411,7 +501,7 @@ int p9_client_clunk(struct p9_fid *fid)
goto done;
}
- err = p9_conn_rpc(clnt->conn, tc, &rc);
+ err = p9_client_rpc(clnt, tc, &rc);
if (err)
goto done;
@@ -443,7 +533,7 @@ int p9_client_remove(struct p9_fid *fid)
goto done;
}
- err = p9_conn_rpc(clnt->conn, tc, &rc);
+ err = p9_client_rpc(clnt, tc, &rc);
if (err)
goto done;
@@ -485,7 +575,7 @@ int p9_client_read(struct p9_fid *fid, char *data, u64 offset, u32 count)
goto error;
}
- err = p9_conn_rpc(clnt->conn, tc, &rc);
+ err = p9_client_rpc(clnt, tc, &rc);
if (err)
goto error;
@@ -542,7 +632,7 @@ int p9_client_write(struct p9_fid *fid, char *data, u64 offset, u32 count)
goto error;
}
- err = p9_conn_rpc(clnt->conn, tc, &rc);
+ err = p9_client_rpc(clnt, tc, &rc);
if (err)
goto error;
@@ -596,7 +686,7 @@ p9_client_uread(struct p9_fid *fid, char __user *data, u64 offset, u32 count)
goto error;
}
- err = p9_conn_rpc(clnt->conn, tc, &rc);
+ err = p9_client_rpc(clnt, tc, &rc);
if (err)
goto error;
@@ -660,7 +750,7 @@ p9_client_uwrite(struct p9_fid *fid, const char __user *data, u64 offset,
goto error;
}
- err = p9_conn_rpc(clnt->conn, tc, &rc);
+ err = p9_client_rpc(clnt, tc, &rc);
if (err)
goto error;
@@ -731,7 +821,7 @@ struct p9_stat *p9_client_stat(struct p9_fid *fid)
goto error;
}
- err = p9_conn_rpc(clnt->conn, tc, &rc);
+ err = p9_client_rpc(clnt, tc, &rc);
if (err)
goto error;
@@ -773,7 +863,7 @@ int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst)
goto done;
}
- err = p9_conn_rpc(clnt->conn, tc, &rc);
+ err = p9_client_rpc(clnt, tc, &rc);
done:
kfree(tc);
@@ -830,7 +920,7 @@ struct p9_stat *p9_client_dirread(struct p9_fid *fid, u64 offset)
goto error;
}
- err = p9_conn_rpc(clnt->conn, tc, &rc);
+ err = p9_client_rpc(clnt, tc, &rc);
if (err)
goto error;
@@ -901,16 +991,21 @@ static struct p9_stat *p9_clone_stat(struct p9_stat *st, int dotu)
memmove(ret, st, sizeof(struct p9_stat));
p = ((char *) ret) + sizeof(struct p9_stat);
memmove(p, st->name.str, st->name.len);
+ ret->name.str = p;
p += st->name.len;
memmove(p, st->uid.str, st->uid.len);
+ ret->uid.str = p;
p += st->uid.len;
memmove(p, st->gid.str, st->gid.len);
+ ret->gid.str = p;
p += st->gid.len;
memmove(p, st->muid.str, st->muid.len);
+ ret->muid.str = p;
p += st->muid.len;
if (dotu) {
memmove(p, st->extension.str, st->extension.len);
+ ret->extension.str = p;
p += st->extension.len;
}
diff --git a/net/9p/fcprint.c b/net/9p/fcprint.c
index b1ae8ec57d5..40244fbd9b0 100644
--- a/net/9p/fcprint.c
+++ b/net/9p/fcprint.c
@@ -347,12 +347,12 @@ p9_printfcall(char *buf, int buflen, struct p9_fcall *fc, int extended)
return ret;
}
-
#else
int
p9_printfcall(char *buf, int buflen, struct p9_fcall *fc, int extended)
{
return 0;
}
-EXPORT_SYMBOL(p9_printfcall);
#endif /* CONFIG_NET_9P_DEBUG */
+EXPORT_SYMBOL(p9_printfcall);
+
diff --git a/net/9p/mod.c b/net/9p/mod.c
index 8f9763a9dc1..c285aab2af0 100644
--- a/net/9p/mod.c
+++ b/net/9p/mod.c
@@ -106,15 +106,10 @@ EXPORT_SYMBOL(v9fs_default_trans);
*/
static int __init init_p9(void)
{
- int ret;
+ int ret = 0;
p9_error_init();
printk(KERN_INFO "Installing 9P2000 support\n");
- ret = p9_mux_global_init();
- if (ret) {
- printk(KERN_WARNING "9p: starting mux failed\n");
- return ret;
- }
return ret;
}
@@ -126,7 +121,7 @@ static int __init init_p9(void)
static void __exit exit_p9(void)
{
- p9_mux_global_exit();
+ printk(KERN_INFO "Unloading 9P2000 support\n");
}
module_init(init_p9)
diff --git a/net/9p/mux.c b/net/9p/mux.c
deleted file mode 100644
index c9f0805048e..00000000000
--- a/net/9p/mux.c
+++ /dev/null
@@ -1,1060 +0,0 @@
-/*
- * net/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/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 <net/9p/9p.h>
-#include <linux/parser.h>
-#include <net/9p/transport.h>
-#include <net/9p/conn.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 p9_mux_poll_task;
-
-struct p9_req {
- spinlock_t lock; /* protect request structure */
- int tag;
- struct p9_fcall *tcall;
- struct p9_fcall *rcall;
- int err;
- p9_conn_req_callback cb;
- void *cba;
- int flush;
- struct list_head req_list;
-};
-
-struct p9_conn {
- spinlock_t lock; /* protect lock structure */
- struct list_head mux_list;
- struct p9_mux_poll_task *poll_task;
- int msize;
- unsigned char *extended;
- struct p9_trans *trans;
- struct p9_idpool *tagpool;
- int err;
- wait_queue_head_t equeue;
- struct list_head req_list;
- struct list_head unsent_req_list;
- struct p9_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 p9_mux_poll_task {
- struct task_struct *task;
- struct list_head mux_list;
- int muxnum;
-};
-
-struct p9_mux_rpc {
- struct p9_conn *m;
- int err;
- struct p9_fcall *tcall;
- struct p9_fcall *rcall;
- wait_queue_head_t wqueue;
-};
-
-static int p9_poll_proc(void *);
-static void p9_read_work(struct work_struct *work);
-static void p9_write_work(struct work_struct *work);
-static void p9_pollwait(struct file *filp, wait_queue_head_t *wait_address,
- poll_table * p);
-static u16 p9_mux_get_tag(struct p9_conn *);
-static void p9_mux_put_tag(struct p9_conn *, u16);
-
-static DEFINE_MUTEX(p9_mux_task_lock);
-static struct workqueue_struct *p9_mux_wq;
-
-static int p9_mux_num;
-static int p9_mux_poll_task_num;
-static struct p9_mux_poll_task p9_mux_poll_tasks[100];
-
-int p9_mux_global_init(void)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(p9_mux_poll_tasks); i++)
- p9_mux_poll_tasks[i].task = NULL;
-
- p9_mux_wq = create_workqueue("v9fs");
- if (!p9_mux_wq) {
- printk(KERN_WARNING "v9fs: mux: creating workqueue failed\n");
- return -ENOMEM;
- }
-
- return 0;
-}
-
-void p9_mux_global_exit(void)
-{
- destroy_workqueue(p9_mux_wq);
-}
-
-/**
- * p9_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 p9_mux_calc_poll_procs(int muxnum)
-{
- int n;
-
- if (p9_mux_poll_task_num)
- n = muxnum / p9_mux_poll_task_num +
- (muxnum % p9_mux_poll_task_num ? 1 : 0);
- else
- n = 1;
-
- if (n > ARRAY_SIZE(p9_mux_poll_tasks))
- n = ARRAY_SIZE(p9_mux_poll_tasks);
-
- return n;
-}
-
-static int p9_mux_poll_start(struct p9_conn *m)
-{
- int i, n;
- struct p9_mux_poll_task *vpt, *vptlast;
- struct task_struct *pproc;
-
- P9_DPRINTK(P9_DEBUG_MUX, "mux %p muxnum %d procnum %d\n", m, p9_mux_num,
- p9_mux_poll_task_num);
- mutex_lock(&p9_mux_task_lock);
-
- n = p9_mux_calc_poll_procs(p9_mux_num + 1);
- if (n > p9_mux_poll_task_num) {
- for (i = 0; i < ARRAY_SIZE(p9_mux_poll_tasks); i++) {
- if (p9_mux_poll_tasks[i].task == NULL) {
- vpt = &p9_mux_poll_tasks[i];
- P9_DPRINTK(P9_DEBUG_MUX, "create proc %p\n",
- vpt);
- pproc = kthread_create(p9_poll_proc, vpt,
- "v9fs-poll");
-
- if (!IS_ERR(pproc)) {
- vpt->task = pproc;
- INIT_LIST_HEAD(&vpt->mux_list);
- vpt->muxnum = 0;
- p9_mux_poll_task_num++;
- wake_up_process(vpt->task);
- }
- break;
- }
- }
-
- if (i >= ARRAY_SIZE(p9_mux_poll_tasks))
- P9_DPRINTK(P9_DEBUG_ERROR,
- "warning: no free poll slots\n");
- }
-
- n = (p9_mux_num + 1) / p9_mux_poll_task_num +
- ((p9_mux_num + 1) % p9_mux_poll_task_num ? 1 : 0);
-
- vptlast = NULL;
- for (i = 0; i < ARRAY_SIZE(p9_mux_poll_tasks); i++) {
- vpt = &p9_mux_poll_tasks[i];
- if (vpt->task != NULL) {
- vptlast = vpt;
- if (vpt->muxnum < n) {
- P9_DPRINTK(P9_DEBUG_MUX, "put in proc %d\n", i);
- list_add(&m->mux_list, &vpt->mux_list);
- vpt->muxnum++;
- m->poll_task = vpt;
- memset(&m->poll_waddr, 0,
- sizeof(m->poll_waddr));
- init_poll_funcptr(&m->pt, p9_pollwait);
- break;
- }
- }
- }
-
- if (i >= ARRAY_SIZE(p9_mux_poll_tasks)) {
- if (vptlast == NULL) {
- mutex_unlock(&p9_mux_task_lock);
- return -ENOMEM;
- }
-
- P9_DPRINTK(P9_DEBUG_MUX, "put in proc %d\n", i);
- list_add(&m->mux_list, &vptlast->mux_list);
- vptlast->muxnum++;
- m->poll_task = vptlast;
- memset(&m->poll_waddr, 0, sizeof(m->poll_waddr));
- init_poll_funcptr(&m->pt, p9_pollwait);
- }
-
- p9_mux_num++;
- mutex_unlock(&p9_mux_task_lock);
-
- return 0;
-}
-
-static void p9_mux_poll_stop(struct p9_conn *m)
-{
- int i;
- struct p9_mux_poll_task *vpt;
-
- mutex_lock(&p9_mux_task_lock);
- vpt = m->poll_task;
- list_del(&m->mux_list);
- for (i = 0; i < ARRAY_SIZE(m->poll_waddr); i++) {
- if (m->poll_waddr[i] != NULL) {
- remove_wait_queue(m->poll_waddr[i], &m->poll_wait[i]);
- m->poll_waddr[i] = NULL;
- }
- }
- vpt->muxnum--;
- if (!vpt->muxnum) {
- P9_DPRINTK(P9_DEBUG_MUX, "destroy proc %p\n", vpt);
- kthread_stop(vpt->task);
- vpt->task = NULL;
- p9_mux_poll_task_num--;
- }
- p9_mux_num--;
- mutex_unlock(&p9_mux_task_lock);
-}
-
-/**
- * p9_conn_create - allocate and initialize the per-session mux data
- * Creates the polling task if this is the first session.
- *
- * @trans - transport structure
- * @msize - maximum message size
- * @extended - pointer to the extended flag
- */
-struct p9_conn *p9_conn_create(struct p9_trans *trans, int msize,
- unsigned char *extended)
-{
- int i, n;
- struct p9_conn *m, *mtmp;
-
- P9_DPRINTK(P9_DEBUG_MUX, "transport %p msize %d\n", trans, msize);
- m = kmalloc(sizeof(struct p9_conn), GFP_KERNEL);
- if (!m)
- return ERR_PTR(-ENOMEM);
-
- spin_lock_init(&m->lock);
- INIT_LIST_HEAD(&m->mux_list);
- m->msize = msize;
- m->extended = extended;
- m->trans = trans;
- m->tagpool = p9_idpool_create();
- if (IS_ERR(m->tagpool)) {
- mtmp = ERR_PTR(-ENOMEM);
- kfree(m);
- return mtmp;
- }
-
- m->err = 0;
- init_waitqueue_head(&m->equeue);
- INIT_LIST_HEAD(&m->req_list);
- INIT_LIST_HEAD(&m->unsent_req_list);
- m->rcall = NULL;
- m->rpos = 0;
- m->rbuf = NULL;
- m->wpos = m->wsize = 0;
- m->wbuf = NULL;
- INIT_WORK(&m->rq, p9_read_work);
- INIT_WORK(&m->wq, p9_write_work);
- m->wsched = 0;
- memset(&m->poll_waddr, 0, sizeof(m->poll_waddr));
- m->poll_task = NULL;
- n = p9_mux_poll_start(m);
- if (n) {
- kfree(m);
- return ERR_PTR(n);
- }
-
- n = trans->poll(trans, &m->pt);
- if (n & POLLIN) {
- P9_DPRINTK(P9_DEBUG_MUX, "mux %p can read\n", m);
- set_bit(Rpending, &m->wsched);
- }
-
- if (n & POLLOUT) {
- P9_DPRINTK(P9_DEBUG_MUX, "mux %p can write\n", m);
- set_bit(Wpending, &m->wsched);
- }
-
- for (i = 0; i < ARRAY_SIZE(m->poll_waddr); i++) {
- if (IS_ERR(m->poll_waddr[i])) {
- p9_mux_poll_stop(m);
- mtmp = (void *)m->poll_waddr; /* the error code */
- kfree(m);
- m = mtmp;
- break;
- }
- }
-
- return m;
-}
-EXPORT_SYMBOL(p9_conn_create);
-
-/**
- * p9_mux_destroy - cancels all pending requests and frees mux resources
- */
-void p9_conn_destroy(struct p9_conn *m)
-{
- P9_DPRINTK(P9_DEBUG_MUX, "mux %p prev %p next %p\n", m,
- m->mux_list.prev, m->mux_list.next);
- p9_conn_cancel(m, -ECONNRESET);
-
- if (!list_empty(&m->req_list)) {
- /* wait until all processes waiting on this session exit */
- P9_DPRINTK(P9_DEBUG_MUX,
- "mux %p waiting for empty request queue\n", m);
- wait_event_timeout(m->equeue, (list_empty(&m->req_list)), 5000);
- P9_DPRINTK(P9_DEBUG_MUX, "mux %p request queue empty: %d\n", m,
- list_empty(&m->req_list));
- }
-
- p9_mux_poll_stop(m);
- m->trans = NULL;
- p9_idpool_destroy(m->tagpool);
- kfree(m);
-}
-EXPORT_SYMBOL(p9_conn_destroy);
-
-/**
- * p9_pollwait - called by files poll operation to add v9fs-poll task
- * to files wait queue
- */
-static void
-p9_pollwait(struct file *filp, wait_queue_head_t *wait_address,
- poll_table * p)
-{
- int i;
- struct p9_conn *m;
-
- m = container_of(p, struct p9_conn, pt);
- for (i = 0; i < ARRAY_SIZE(m->poll_waddr); i++)
- if (m->poll_waddr[i] == NULL)
- break;
-
- if (i >= ARRAY_SIZE(m->poll_waddr)) {
- P9_DPRINTK(P9_DEBUG_ERROR, "not enough wait_address slots\n");
- return;
- }
-
- m->poll_waddr[i] = wait_address;
-
- if (!wait_address) {
- P9_DPRINTK(P9_DEBUG_ERROR, "no wait_address\n");
- m->poll_waddr[i] = ERR_PTR(-EIO);
- return;
- }
-
- init_waitqueue_entry(&m->poll_wait[i], m->poll_task->task);
- add_wait_queue(wait_address, &m->poll_wait[i]);
-}
-
-/**
- * p9_poll_mux - polls a mux and schedules read or write works if necessary
- */
-static void p9_poll_mux(struct p9_conn *m)
-{
- int n;
-
- if (m->err < 0)
- return;
-
- n = m->trans->poll(m->trans, NULL);
- if (n < 0 || n & (POLLERR | POLLHUP | POLLNVAL)) {
- P9_DPRINTK(P9_DEBUG_MUX, "error mux %p err %d\n", m, n);
- if (n >= 0)
- n = -ECONNRESET;
- p9_conn_cancel(m, n);
- }
-
- if (n & POLLIN) {
- set_bit(Rpending, &m->wsched);
- P9_DPRINTK(P9_DEBUG_MUX, "mux %p can read\n", m);
- if (!test_and_set_bit(Rworksched, &m->wsched)) {
- P9_DPRINTK(P9_DEBUG_MUX, "schedule read work %p\n", m);
- queue_work(p9_mux_wq, &m->rq);
- }
- }
-
- if (n & POLLOUT) {
- set_bit(Wpending, &m->wsched);
- P9_DPRINTK(P9_DEBUG_MUX, "mux %p can write\n", m);
- if ((m->wsize || !list_empty(&m->unsent_req_list))
- && !test_and_set_bit(Wworksched, &m->wsched)) {
- P9_DPRINTK(P9_DEBUG_MUX, "schedule write work %p\n", m);
- queue_work(p9_mux_wq, &m->wq);
- }
- }
-}
-
-/**
- * p9_poll_proc - polls all v9fs transports for new events and queues
- * the appropriate work to the work queue
- */
-static int p9_poll_proc(void *a)
-{
- struct p9_conn *m, *mtmp;
- struct p9_mux_poll_task *vpt;
-
- vpt = a;
- P9_DPRINTK(P9_DEBUG_MUX, "start %p %p\n", current, vpt);
- while (!kthread_should_stop()) {
- set_current_state(TASK_INTERRUPTIBLE);
-
- list_for_each_entry_safe(m, mtmp, &vpt->mux_list, mux_list) {
- p9_poll_mux(m);
- }
-
- P9_DPRINTK(P9_DEBUG_MUX, "sleeping...\n");
- schedule_timeout(SCHED_TIMEOUT * HZ);
- }
-
- __set_current_state(TASK_RUNNING);
- P9_DPRINTK(P9_DEBUG_MUX, "finish\n");
- return 0;
-}
-
-/**
- * p9_write_work - called when a transport can send some data
- */
-static void p9_write_work(struct work_struct *work)
-{
- int n, err;
- struct p9_conn *m;
- struct p9_req *req;
-
- m = container_of(work, struct p9_conn, wq);
-
- if (m->err < 0) {
- clear_bit(Wworksched, &m->wsched);
- return;
- }
-
- if (!m->wsize) {
- if (list_empty(&m->unsent_req_list)) {
- clear_bit(Wworksched, &m->wsched);
- return;
- }
-
- spin_lock(&m->lock);
-again:
- req = list_entry(m->unsent_req_list.next, struct p9_req,
- req_list);
- list_move_tail(&req->req_list, &m->req_list);
- if (req->err == ERREQFLUSH)
- goto again;
-
- m->wbuf = req->tcall->sdata;
- m->wsize = req->tcall->size;
- m->wpos = 0;
- spin_unlock(&m->lock);
- }
-
- P9_DPRINTK(P9_DEBUG_MUX, "mux %p pos %d size %d\n", m, m->wpos,
- m->wsize);
- clear_bit(Wpending, &m->wsched);
- err = m->trans->write(m->trans, m->wbuf + m->wpos, m->wsize - m->wpos);
- P9_DPRINTK(P9_DEBUG_MUX, "mux %p sent %d bytes\n", m, err);
- if (err == -EAGAIN) {
- clear_bit(Wworksched, &m->wsched);
- return;
- }
-
- 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;
-
- if (m->wsize == 0 && !list_empty(&m->unsent_req_list)) {
- if (test_and_clear_bit(Wpending, &m->wsched))
- n = POLLOUT;
- else
- n = m->trans->poll(m->trans, NULL);
-
- if (n & POLLOUT) {
- P9_DPRINTK(P9_DEBUG_MUX, "schedule write work %p\n", m);
- queue_work(p9_mux_wq, &m->wq);
- } else
- clear_bit(Wworksched, &m->wsched);
- } else
- clear_bit(Wworksched, &m->wsched);
-
- return;
-
-error:
- p9_conn_cancel(m, err);
- clear_bit(Wworksched, &m->wsched);
-}
-
-static void process_request(struct p9_conn *m, struct p9_req *req)
-{
- int ecode;
- struct p9_str *ename;
-
- if (!req->err && req->rcall->id == P9_RERROR) {
- ecode = req->rcall->params.rerror.errno;
- ename = &req->rcall->params.rerror.error;
-
- P9_DPRINTK(P9_DEBUG_MUX, "Rerror %.*s\n", ename->len,
- ename->str);
-
- if (*m->extended)
- req->err = -ecode;
-
- if (!req->err) {
- req->err = p9_errstr2errno(ename->str, ename->len);
-
- if (!req->err) { /* string match failed */
- PRINT_FCALL_ERROR("unknown error", req->rcall);
- }
-
- if (!req->err)
- req->err = -ESERVERFAULT;
- }
- } else if (req->tcall && req->rcall->id != req->tcall->id + 1) {
- P9_DPRINTK(P9_DEBUG_ERROR,
- "fcall mismatch: expected %d, got %d\n",
- req->tcall->id + 1, req->rcall->id);
- if (!req->err)
- req->err = -EIO;
- }
-}
-
-/**
- * p9_read_work - called when there is some data to be read from a transport
- */
-static void p9_read_work(struct work_struct *work)
-{
- int n, err;
- struct p9_conn *m;
- struct p9_req *req, *rptr, *rreq;
- struct p9_fcall *rcall;
- char *rbuf;
-
- m = container_of(work, struct p9_conn, rq);
-
- if (m->err < 0)
- return;
-
- rcall = NULL;
- P9_DPRINTK(P9_DEBUG_MUX, "start mux %p pos %d\n", m, m->rpos);
-
- if (!m->rcall) {
- m->rcall =
- kmalloc(sizeof(struct p9_fcall) + m->msize, GFP_KERNEL);
- if (!m->rcall) {
- err = -ENOMEM;
- goto error;
- }
-
- m->rbuf = (char *)m->rcall + sizeof(struct p9_fcall);
- m->rpos = 0;
- }
-
- clear_bit(Rpending, &m->wsched);
- err = m->trans->read(m->trans, m->rbuf + m->rpos, m->msize - m->rpos);
- P9_DPRINTK(P9_DEBUG_MUX, "mux %p got %d bytes\n", m, err);
- if (err == -EAGAIN) {
- clear_bit(Rworksched, &m->wsched);
- return;
- }
-
- if (err <= 0)
- goto error;
-
- m->rpos += err;
- while (m->rpos > 4) {
- n = le32_to_cpu(*(__le32 *) m->rbuf);
- if (n >= m->msize) {
- P9_DPRINTK(P9_DEBUG_ERROR,
- "requested packet size too big: %d\n", n);
- err = -EIO;
- goto error;
- }
-
- if (m->rpos < n)
- break;
-
- err =
- p9_deserialize_fcall(m->rbuf, n, m->rcall, *m->extended);
- if (err < 0) {
- goto error;
- }
-
-#ifdef CONFIG_NET_9P_DEBUG
- if ((p9_debug_level&P9_DEBUG_FCALL) == P9_DEBUG_FCALL) {
- char buf[150];
-
- p9_printfcall(buf, sizeof(buf), m->rcall,
- *m->extended);
- printk(KERN_NOTICE ">>> %p %s\n", m, buf);
- }
-#endif
-
- rcall = m->rcall;
- rbuf = m->rbuf;
- if (m->rpos > n) {
- m->rcall = kmalloc(sizeof(struct p9_fcall) + m->msize,
- GFP_KERNEL);
- if (!m->rcall) {
- err = -ENOMEM;
- goto error;
- }
-
- m->rbuf = (char *)m->rcall + sizeof(struct p9_fcall);
- memmove(m->rbuf, rbuf + n, m->rpos - n);
- m->rpos -= n;
- } else {
- m->rcall = NULL;
- m->rbuf = NULL;
- m->rpos = 0;
- }
-
- P9_DPRINTK(P9_DEBUG_MUX, "mux %p fcall id %d tag %d\n", m,
- rcall->id, rcall->tag);
-
- req = NULL;
- spin_lock(&m->lock);
- list_for_each_entry_safe(rreq, rptr, &m->req_list, req_list) {
- if (rreq->tag == rcall->tag) {
- req = rreq;
- if (req->flush != Flushing)
- list_del(&req->req_list);
- break;
- }
- }
- spin_unlock(&m->lock);
-
- if (req) {
- req->rcall = rcall;
- process_request(m, req);
-
- if (req->flush != Flushing) {
- if (req->cb)
- (*req->cb) (req, req->cba);
- else
- kfree(req->rcall);
-
- wake_up(&m->equeue);
- }
- } else {
- if (err >= 0 && rcall->id != P9_RFLUSH)
- P9_DPRINTK(P9_DEBUG_ERROR,
- "unexpected response mux %p id %d tag %d\n",
- m, rcall->id, rcall->tag);
- kfree(rcall);
- }
- }
-
- if (!list_empty(&m->req_list)) {
- if (test_and_clear_bit(Rpending, &m->wsched))
- n = POLLIN;
- else
- n = m->trans->poll(m->trans, NULL);
-
- if (n & POLLIN) {
- P9_DPRINTK(P9_DEBUG_MUX, "schedule read work %p\n", m);
- queue_work(p9_mux_wq, &m->rq);
- } else
- clear_bit(Rworksched, &m->wsched);
- } else
- clear_bit(Rworksched, &m->wsched);
-
- return;
-
-error:
- p9_conn_cancel(m, err);
- clear_bit(Rworksched, &m->wsched);
-}
-
-/**
- * p9_send_request - send 9P request
- * The function can sleep until the request is scheduled for sending.
- * The function can be interrupted. Return from the function is not
- * a guarantee that the request is sent successfully. Can return errors
- * that can be retrieved by PTR_ERR macros.
- *
- * @m: mux data
- * @tc: request to be sent
- * @cb: callback function to call when response is received
- * @cba: parameter to pass to the callback function
- */
-static struct p9_req *p9_send_request(struct p9_conn *m,
- struct p9_fcall *tc,
- p9_conn_req_callback cb, void *cba)
-{
- int n;
- struct p9_req *req;
-
- P9_DPRINTK(P9_DEBUG_MUX, "mux %p task %p tcall %p id %d\n", m, current,
- tc, tc->id);
- if (m->err < 0)
- return ERR_PTR(m->err);
-
- req = kmalloc(sizeof(struct p9_req), GFP_KERNEL);
- if (!req)
- return ERR_PTR(-ENOMEM);
-
- if (tc->id == P9_TVERSION)
- n = P9_NOTAG;
- else
- n = p9_mux_get_tag(m);
-
- if (n < 0)
- return ERR_PTR(-ENOMEM);
-
- p9_set_tag(tc, n);
-
-#ifdef CONFIG_NET_9P_DEBUG
- if ((p9_debug_level&P9_DEBUG_FCALL) == P9_DEBUG_FCALL) {
- char buf[150];
-
- p9_printfcall(buf, sizeof(buf), tc, *m->extended);
- printk(KERN_NOTICE "<<< %p %s\n", m, buf);
- }
-#endif
-
- spin_lock_init(&req->lock);
- req->tag = n;
- req->tcall = tc;
- req->rcall = NULL;
- req->err = 0;
- req->cb = cb;
- req->cba = cba;
- req->flush = None;
-
- spin_lock(&m->lock);
- list_add_tail(&req->req_list, &m->unsent_req_list);
- spin_unlock(&m->lock);
-
- if (test_and_clear_bit(Wpending, &m->wsched))
- n = POLLOUT;
- else
- n = m->trans->poll(m->trans, NULL);
-
- if (n & POLLOUT && !test_and_set_bit(Wworksched, &m->wsched))
- queue_work(p9_mux_wq, &m->wq);
-
- return req;
-}
-
-static void p9_mux_free_request(struct p9_conn *m, struct p9_req *req)
-{
- p9_mux_put_tag(m, req->tag);
- kfree(req);
-}
-
-static void p9_mux_flush_cb(struct p9_req *freq, void *a)
-{
- p9_conn_req_callback cb;
- int tag;
- struct p9_conn *m;
- struct p9_req *req, *rreq, *rptr;
-
- m = a;
- P9_DPRINTK(P9_DEBUG_MUX, "mux %p tc %p rc %p err %d oldtag %d\n", m,
- freq->tcall, freq->rcall, freq->err,
- freq->tcall->params.tflush.oldtag);
-
- spin_lock(&m->lock);
- cb = NULL;
- tag = freq->tcall->params.tflush.oldtag;
- req = NULL;
- list_for_each_entry_safe(rreq, rptr, &m->req_list, req_list) {
- if (rreq->tag == tag) {
- req = rreq;
- list_del(&req->req_list);
- break;
- }
- }
- spin_unlock(&m->lock);
-
- if (req) {
- spin_lock(&req->lock);
- req->flush = Flushed;
- spin_unlock(&req->lock);
-
- if (req->cb)
- (*req->cb) (req, req->cba);
- else
- kfree(req->rcall);
-
- wake_up(&m->equeue);
- }
-
- kfree(freq->tcall);
- kfree(freq->rcall);
- p9_mux_free_request(m, freq);
-}
-
-static int
-p9_mux_flush_request(struct p9_conn *m, struct p9_req *req)
-{
- struct p9_fcall *fc;
- struct p9_req *rreq, *rptr;
-
- P9_DPRINTK(P9_DEBUG_MUX, "mux %p req %p tag %d\n", m, req, req->tag);
-
- /* if a response was received for a request, do nothing */
- spin_lock(&req->lock);
- if (req->rcall || req->err) {
- spin_unlock(&req->lock);
- P9_DPRINTK(P9_DEBUG_MUX,
- "mux %p req %p response already received\n", m, req);
- return 0;
- }
-
- req->flush = Flushing;
- spin_unlock(&req->lock);
-
- spin_lock(&m->lock);
- /* if the request is not sent yet, just remove it from the list */
- list_for_each_entry_safe(rreq, rptr, &m->unsent_req_list, req_list) {
- if (rreq->tag == req->tag) {
- P9_DPRINTK(P9_DEBUG_MUX,
- "mux %p req %p request is not sent yet\n", m, req);
- list_del(&rreq->req_list);
- req->flush = Flushed;
- spin_unlock(&m->lock);
- if (req->cb)
- (*req->cb) (req, req->cba);
- return 0;
- }
- }
- spin_unlock(&m->lock);
-
- clear_thread_flag(TIF_SIGPENDING);
- fc = p9_create_tflush(req->tag);
- p9_send_request(m, fc, p9_mux_flush_cb, m);
- return 1;
-}
-
-static void
-p9_conn_rpc_cb(struct p9_req *req, void *a)
-{
- struct p9_mux_rpc *r;
-
- P9_DPRINTK(P9_DEBUG_MUX, "req %p r %p\n", req, a);
- r = a;
- r->rcall = req->rcall;
- r->err = req->err;
-
- if (req->flush != None && !req->err)
- r->err = -ERESTARTSYS;
-
- wake_up(&r->wqueue);
-}
-
-/**
- * p9_mux_rpc - sends 9P request and waits until a response is available.
- * The function can be interrupted.
- * @m: mux data
- * @tc: request to be sent
- * @rc: pointer where a pointer to the response is stored
- */
-int
-p9_conn_rpc(struct p9_conn *m, struct p9_fcall *tc,
- struct p9_fcall **rc)
-{
- int err, sigpending;
- unsigned long flags;
- struct p9_req *req;
- struct p9_mux_rpc r;
-
- r.err = 0;
- r.tcall = tc;
- r.rcall = NULL;
- r.m = m;
- init_waitqueue_head(&r.wqueue);
-
- if (rc)
- *rc = NULL;
-
- sigpending = 0;
- if (signal_pending(current)) {
- sigpending = 1;
- clear_thread_flag(TIF_SIGPENDING);
- }
-
- req = p9_send_request(m, tc, p9_conn_rpc_cb, &r);
- if (IS_ERR(req)) {
- err = PTR_ERR(req);
- P9_DPRINTK(P9_DEBUG_MUX, "error %d\n", err);
- return err;
- }
-
- err = wait_event_interruptible(r.wqueue, r.rcall != NULL || r.err < 0);
- if (r.err < 0)
- err = r.err;
-
- if (err == -ERESTARTSYS && m->trans->status == Connected
- && m->err == 0) {
- if (p9_mux_flush_request(m, req)) {
- /* wait until we get response of the flush message */
- do {
- clear_thread_flag(TIF_SIGPENDING);
- err = wait_event_interruptible(r.wqueue,
- r.rcall || r.err);
- } while (!r.rcall && !r.err && err == -ERESTARTSYS &&
- m->trans->status == Connected && !m->err);
-
- err = -ERESTARTSYS;
- }
- sigpending = 1;
- }
-
- if (sigpending) {
- spin_lock_irqsave(&current->sighand->siglock, flags);
- recalc_sigpending();
- spin_unlock_irqrestore(&current->sighand->siglock, flags);
- }
-
- if (rc)
- *rc = r.rcall;
- else
- kfree(r.rcall);
-
- p9_mux_free_request(m, req);
- if (err > 0)
- err = -EIO;
-
- return err;
-}
-EXPORT_SYMBOL(p9_conn_rpc);
-
-#ifdef P9_NONBLOCK
-/**
- * p9_conn_rpcnb - sends 9P request without waiting for response.
- * @m: mux data
- * @tc: request to be sent
- * @cb: callback function to be called when response arrives
- * @cba: value to pass to the callback function
- */
-int p9_conn_rpcnb(struct p9_conn *m, struct p9_fcall *tc,
- p9_conn_req_callback cb, void *a)
-{
- int err;
- struct p9_req *req;
-
- req = p9_send_request(m, tc, cb, a);
- if (IS_ERR(req)) {
- err = PTR_ERR(req);
- P9_DPRINTK(P9_DEBUG_MUX, "error %d\n", err);
- return PTR_ERR(req);
- }
-
- P9_DPRINTK(P9_DEBUG_MUX, "mux %p tc %p tag %d\n", m, tc, req->tag);
- return 0;
-}
-EXPORT_SYMBOL(p9_conn_rpcnb);
-#endif /* P9_NONBLOCK */
-
-/**
- * p9_conn_cancel - cancel all pending requests with error
- * @m: mux data
- * @err: error code
- */
-void p9_conn_cancel(struct p9_conn *m, int err)
-{
- struct p9_req *req, *rtmp;
- LIST_HEAD(cancel_list);
-
- P9_DPRINTK(P9_DEBUG_ERROR, "mux %p err %d\n", m, err);
- m->err = err;
- spin_lock(&m->lock);
- 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);
- }
- spin_unlock(&m->lock);
-
- list_for_each_entry_safe(req, rtmp, &cancel_list, req_list) {
- list_del(&req->req_list);
- if (!req->err)
- req->err = err;
-
- if (req->cb)
- (*req->cb) (req, req->cba);
- else
- kfree(req->rcall);
- }
-
- wake_up(&m->equeue);
-}
-EXPORT_SYMBOL(p9_conn_cancel);
-
-static u16 p9_mux_get_tag(struct p9_conn *m)
-{
- int tag;
-
- tag = p9_idpool_get(m->tagpool);
- if (tag < 0)
- return P9_NOTAG;
- else
- return (u16) tag;
-}
-
-static void p9_mux_put_tag(struct p9_conn *m, u16 tag)
-{
- if (tag != P9_NOTAG && p9_idpool_check(tag, m->tagpool))
- p9_idpool_put(tag, m->tagpool);
-}
diff --git a/net/9p/trans_fd.c b/net/9p/trans_fd.c
index 62332ed9da4..1aa9d517539 100644
--- a/net/9p/trans_fd.c
+++ b/net/9p/trans_fd.c
@@ -5,7 +5,7 @@
*
* Copyright (C) 2006 by Russ Cox <rsc@swtch.com>
* Copyright (C) 2004-2005 by Latchesar Ionkov <lucho@ionkov.net>
- * Copyright (C) 2004-2007 by Eric Van Hensbergen <ericvh@gmail.com>
+ * Copyright (C) 2004-2008 by Eric Van Hensbergen <ericvh@gmail.com>
* Copyright (C) 1997-2002 by Ron Minnich <rminnich@sarnoff.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -29,6 +29,7 @@
#include <linux/module.h>
#include <linux/net.h>
#include <linux/ipv6.h>
+#include <linux/kthread.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/un.h>
@@ -42,7 +43,9 @@
#define P9_PORT 564
#define MAX_SOCK_BUF (64*1024)
-
+#define ERREQFLUSH 1
+#define SCHED_TIMEOUT 10
+#define MAXPOLLWADDR 2
struct p9_fd_opts {
int rfd;
@@ -53,6 +56,7 @@ struct p9_fd_opts {
struct p9_trans_fd {
struct file *rd;
struct file *wr;
+ struct p9_conn *conn;
};
/*
@@ -72,6 +76,1028 @@ static match_table_t tokens = {
{Opt_err, NULL},
};
+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 p9_req;
+
+typedef void (*p9_conn_req_callback)(struct p9_req *req, void *a);
+struct p9_req {
+ spinlock_t lock; /* protect request structure */
+ int tag;
+ struct p9_fcall *tcall;
+ struct p9_fcall *rcall;
+ int err;
+ p9_conn_req_callback cb;
+ void *cba;
+ int flush;
+ struct list_head req_list;
+};
+
+struct p9_mux_poll_task;
+
+struct p9_conn {
+ spinlock_t lock; /* protect lock structure */
+ struct list_head mux_list;
+ struct p9_mux_poll_task *poll_task;
+ int msize;
+ unsigned char extended;
+ struct p9_trans *trans;
+ struct p9_idpool *tagpool;
+ int err;
+ wait_queue_head_t equeue;
+ struct list_head req_list;
+ struct list_head unsent_req_list;
+ struct p9_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 p9_mux_poll_task {
+ struct task_struct *task;
+ struct list_head mux_list;
+ int muxnum;
+};
+
+struct p9_mux_rpc {
+ struct p9_conn *m;
+ int err;
+ struct p9_fcall *tcall;
+ struct p9_fcall *rcall;
+ wait_queue_head_t wqueue;
+};
+
+static int p9_poll_proc(void *);
+static void p9_read_work(struct work_struct *work);
+static void p9_write_work(struct work_struct *work);
+static void p9_pollwait(struct file *filp, wait_queue_head_t *wait_address,
+ poll_table *p);
+static int p9_fd_write(struct p9_trans *trans, void *v, int len);
+static int p9_fd_read(struct p9_trans *trans, void *v, int len);
+
+static DEFINE_MUTEX(p9_mux_task_lock);
+static struct workqueue_struct *p9_mux_wq;
+
+static int p9_mux_num;
+static int p9_mux_poll_task_num;
+static struct p9_mux_poll_task p9_mux_poll_tasks[100];
+
+static void p9_conn_destroy(struct p9_conn *);
+static unsigned int p9_fd_poll(struct p9_trans *trans,
+ struct poll_table_struct *pt);
+
+#ifdef P9_NONBLOCK
+static int p9_conn_rpcnb(struct p9_conn *m, struct p9_fcall *tc,
+ p9_conn_req_callback cb, void *a);
+#endif /* P9_NONBLOCK */
+
+static void p9_conn_cancel(struct p9_conn *m, int err);
+
+static int p9_mux_global_init(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(p9_mux_poll_tasks); i++)
+ p9_mux_poll_tasks[i].task = NULL;
+
+ p9_mux_wq = create_workqueue("v9fs");
+ if (!p9_mux_wq) {
+ printk(KERN_WARNING "v9fs: mux: creating workqueue failed\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static u16 p9_mux_get_tag(struct p9_conn *m)
+{
+ int tag;
+
+ tag = p9_idpool_get(m->tagpool);
+ if (tag < 0)
+ return P9_NOTAG;
+ else
+ return (u16) tag;
+}
+
+static void p9_mux_put_tag(struct p9_conn *m, u16 tag)
+{
+ if (tag != P9_NOTAG && p9_idpool_check(tag, m->tagpool))
+ p9_idpool_put(tag, m->tagpool);
+}
+
+/**
+ * p9_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 p9_mux_calc_poll_procs(int muxnum)
+{
+ int n;
+
+ if (p9_mux_poll_task_num)
+ n = muxnum / p9_mux_poll_task_num +
+ (muxnum % p9_mux_poll_task_num ? 1 : 0);
+ else
+ n = 1;
+
+ if (n > ARRAY_SIZE(p9_mux_poll_tasks))
+ n = ARRAY_SIZE(p9_mux_poll_tasks);
+
+ return n;
+}
+
+static int p9_mux_poll_start(struct p9_conn *m)
+{
+ int i, n;
+ struct p9_mux_poll_task *vpt, *vptlast;
+ struct task_struct *pproc;
+
+ P9_DPRINTK(P9_DEBUG_MUX, "mux %p muxnum %d procnum %d\n", m, p9_mux_num,
+ p9_mux_poll_task_num);
+ mutex_lock(&p9_mux_task_lock);
+
+ n = p9_mux_calc_poll_procs(p9_mux_num + 1);
+ if (n > p9_mux_poll_task_num) {
+ for (i = 0; i < ARRAY_SIZE(p9_mux_poll_tasks); i++) {
+ if (p9_mux_poll_tasks[i].task == NULL) {
+ vpt = &p9_mux_poll_tasks[i];
+ P9_DPRINTK(P9_DEBUG_MUX, "create proc %p\n",
+ vpt);
+ pproc = kthread_create(p9_poll_proc, vpt,
+ "v9fs-poll");
+
+ if (!IS_ERR(pproc)) {
+ vpt->task = pproc;
+ INIT_LIST_HEAD(&vpt->mux_list);
+ vpt->muxnum = 0;
+ p9_mux_poll_task_num++;
+ wake_up_process(vpt->task);
+ }
+ break;
+ }
+ }
+
+ if (i >= ARRAY_SIZE(p9_mux_poll_tasks))
+ P9_DPRINTK(P9_DEBUG_ERROR,
+ "warning: no free poll slots\n");
+ }
+
+ n = (p9_mux_num + 1) / p9_mux_poll_task_num +
+ ((p9_mux_num + 1) % p9_mux_poll_task_num ? 1 : 0);
+
+ vptlast = NULL;
+ for (i = 0; i < ARRAY_SIZE(p9_mux_poll_tasks); i++) {
+ vpt = &p9_mux_poll_tasks[i];
+ if (vpt->task != NULL) {
+ vptlast = vpt;
+ if (vpt->muxnum < n) {
+ P9_DPRINTK(P9_DEBUG_MUX, "put in proc %d\n", i);
+ list_add(&m->mux_list, &vpt->mux_list);
+ vpt->muxnum++;
+ m->poll_task = vpt;
+ memset(&m->poll_waddr, 0,
+ sizeof(m->poll_waddr));
+ init_poll_funcptr(&m->pt, p9_pollwait);
+ break;
+ }
+ }
+ }
+
+ if (i >= ARRAY_SIZE(p9_mux_poll_tasks)) {
+ if (vptlast == NULL) {
+ mutex_unlock(&p9_mux_task_lock);
+ return -ENOMEM;
+ }
+
+ P9_DPRINTK(P9_DEBUG_MUX, "put in proc %d\n", i);
+ list_add(&m->mux_list, &vptlast->mux_list);
+ vptlast->muxnum++;
+ m->poll_task = vptlast;
+ memset(&m->poll_waddr, 0, sizeof(m->poll_waddr));
+ init_poll_funcptr(&m->pt, p9_pollwait);
+ }
+
+ p9_mux_num++;
+ mutex_unlock(&p9_mux_task_lock);
+
+ return 0;
+}
+
+static void p9_mux_poll_stop(struct p9_conn *m)
+{
+ int i;
+ struct p9_mux_poll_task *vpt;
+
+ mutex_lock(&p9_mux_task_lock);
+ vpt = m->poll_task;
+ list_del(&m->mux_list);
+ for (i = 0; i < ARRAY_SIZE(m->poll_waddr); i++) {
+ if (m->poll_waddr[i] != NULL) {
+ remove_wait_queue(m->poll_waddr[i], &m->poll_wait[i]);
+ m->poll_waddr[i] = NULL;
+ }
+ }
+ vpt->muxnum--;
+ if (!vpt->muxnum) {
+ P9_DPRINTK(P9_DEBUG_MUX, "destroy proc %p\n", vpt);
+ kthread_stop(vpt->task);
+ vpt->task = NULL;
+ p9_mux_poll_task_num--;
+ }
+ p9_mux_num--;
+ mutex_unlock(&p9_mux_task_lock);
+}
+
+/**
+ * p9_conn_create - allocate and initialize the per-session mux data
+ * Creates the polling task if this is the first session.
+ *
+ * @trans - transport structure
+ * @msize - maximum message size
+ * @extended - extended flag
+ */
+static struct p9_conn *p9_conn_create(struct p9_trans *trans)
+{
+ int i, n;
+ struct p9_conn *m, *mtmp;
+
+ P9_DPRINTK(P9_DEBUG_MUX, "transport %p msize %d\n", trans,
+ trans->msize);
+ m = kmalloc(sizeof(struct p9_conn), GFP_KERNEL);
+ if (!m)
+ return ERR_PTR(-ENOMEM);
+
+ spin_lock_init(&m->lock);
+ INIT_LIST_HEAD(&m->mux_list);
+ m->msize = trans->msize;
+ m->extended = trans->extended;
+ m->trans = trans;
+ m->tagpool = p9_idpool_create();
+ if (IS_ERR(m->tagpool)) {
+ mtmp = ERR_PTR(-ENOMEM);
+ kfree(m);
+ return mtmp;
+ }
+
+ m->err = 0;
+ init_waitqueue_head(&m->equeue);
+ INIT_LIST_HEAD(&m->req_list);
+ INIT_LIST_HEAD(&m->unsent_req_list);
+ m->rcall = NULL;
+ m->rpos = 0;
+ m->rbuf = NULL;
+ m->wpos = m->wsize = 0;
+ m->wbuf = NULL;
+ INIT_WORK(&m->rq, p9_read_work);
+ INIT_WORK(&m->wq, p9_write_work);
+ m->wsched = 0;
+ memset(&m->poll_waddr, 0, sizeof(m->poll_waddr));
+ m->poll_task = NULL;
+ n = p9_mux_poll_start(m);
+ if (n) {
+ kfree(m);
+ return ERR_PTR(n);
+ }
+
+ n = p9_fd_poll(trans, &m->pt);
+ if (n & POLLIN) {
+ P9_DPRINTK(P9_DEBUG_MUX, "mux %p can read\n", m);
+ set_bit(Rpending, &m->wsched);
+ }
+
+ if (n & POLLOUT) {
+ P9_DPRINTK(P9_DEBUG_MUX, "mux %p can write\n", m);
+ set_bit(Wpending, &m->wsched);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(m->poll_waddr); i++) {
+ if (IS_ERR(m->poll_waddr[i])) {
+ p9_mux_poll_stop(m);
+ mtmp = (void *)m->poll_waddr; /* the error code */
+ kfree(m);
+ m = mtmp;
+ break;
+ }
+ }
+
+ return m;
+}
+
+/**
+ * p9_mux_destroy - cancels all pending requests and frees mux resources
+ */
+static void p9_conn_destroy(struct p9_conn *m)
+{
+ P9_DPRINTK(P9_DEBUG_MUX, "mux %p prev %p next %p\n", m,
+ m->mux_list.prev, m->mux_list.next);
+ p9_conn_cancel(m, -ECONNRESET);
+
+ if (!list_empty(&m->req_list)) {
+ /* wait until all processes waiting on this session exit */
+ P9_DPRINTK(P9_DEBUG_MUX,
+ "mux %p waiting for empty request queue\n", m);
+ wait_event_timeout(m->equeue, (list_empty(&m->req_list)), 5000);
+ P9_DPRINTK(P9_DEBUG_MUX, "mux %p request queue empty: %d\n", m,
+ list_empty(&m->req_list));
+ }
+
+ p9_mux_poll_stop(m);
+ m->trans = NULL;
+ p9_idpool_destroy(m->tagpool);
+ kfree(m);
+}
+
+/**
+ * p9_pollwait - called by files poll operation to add v9fs-poll task
+ * to files wait queue
+ */
+static void
+p9_pollwait(struct file *filp, wait_queue_head_t *wait_address, poll_table *p)
+{
+ int i;
+ struct p9_conn *m;
+
+ m = container_of(p, struct p9_conn, pt);
+ for (i = 0; i < ARRAY_SIZE(m->poll_waddr); i++)
+ if (m->poll_waddr[i] == NULL)
+ break;
+
+ if (i >= ARRAY_SIZE(m->poll_waddr)) {
+ P9_DPRINTK(P9_DEBUG_ERROR, "not enough wait_address slots\n");
+ return;
+ }
+
+ m->poll_waddr[i] = wait_address;
+
+ if (!wait_address) {
+ P9_DPRINTK(P9_DEBUG_ERROR, "no wait_address\n");
+ m->poll_waddr[i] = ERR_PTR(-EIO);
+ return;
+ }
+
+ init_waitqueue_entry(&m->poll_wait[i], m->poll_task->task);
+ add_wait_queue(wait_address, &m->poll_wait[i]);
+}
+
+/**
+ * p9_poll_mux - polls a mux and schedules read or write works if necessary
+ */
+static void p9_poll_mux(struct p9_conn *m)
+{
+ int n;
+
+ if (m->err < 0)
+ return;
+
+ n = p9_fd_poll(m->trans, NULL);
+ if (n < 0 || n & (POLLERR | POLLHUP | POLLNVAL)) {
+ P9_DPRINTK(P9_DEBUG_MUX, "error mux %p err %d\n", m, n);
+ if (n >= 0)
+ n = -ECONNRESET;
+ p9_conn_cancel(m, n);
+ }
+
+ if (n & POLLIN) {
+ set_bit(Rpending, &m->wsched);
+ P9_DPRINTK(P9_DEBUG_MUX, "mux %p can read\n", m);
+ if (!test_and_set_bit(Rworksched, &m->wsched)) {
+ P9_DPRINTK(P9_DEBUG_MUX, "schedule read work %p\n", m);
+ queue_work(p9_mux_wq, &m->rq);
+ }
+ }
+
+ if (n & POLLOUT) {
+ set_bit(Wpending, &m->wsched);
+ P9_DPRINTK(P9_DEBUG_MUX, "mux %p can write\n", m);
+ if ((m->wsize || !list_empty(&m->unsent_req_list))
+ && !test_and_set_bit(Wworksched, &m->wsched)) {
+ P9_DPRINTK(P9_DEBUG_MUX, "schedule write work %p\n", m);
+ queue_work(p9_mux_wq, &m->wq);
+ }
+ }
+}
+
+/**
+ * p9_poll_proc - polls all v9fs transports for new events and queues
+ * the appropriate work to the work queue
+ */
+static int p9_poll_proc(void *a)
+{
+ struct p9_conn *m, *mtmp;
+ struct p9_mux_poll_task *vpt;
+
+ vpt = a;
+ P9_DPRINTK(P9_DEBUG_MUX, "start %p %p\n", current, vpt);
+ while (!kthread_should_stop()) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ list_for_each_entry_safe(m, mtmp, &vpt->mux_list, mux_list) {
+ p9_poll_mux(m);
+ }
+
+ P9_DPRINTK(P9_DEBUG_MUX, "sleeping...\n");
+ schedule_timeout(SCHED_TIMEOUT * HZ);
+ }
+
+ __set_current_state(TASK_RUNNING);
+ P9_DPRINTK(P9_DEBUG_MUX, "finish\n");
+ return 0;
+}
+
+/**
+ * p9_write_work - called when a transport can send some data
+ */
+static void p9_write_work(struct work_struct *work)
+{
+ int n, err;
+ struct p9_conn *m;
+ struct p9_req *req;
+
+ m = container_of(work, struct p9_conn, wq);
+
+ if (m->err < 0) {
+ clear_bit(Wworksched, &m->wsched);
+ return;
+ }
+
+ if (!m->wsize) {
+ if (list_empty(&m->unsent_req_list)) {
+ clear_bit(Wworksched, &m->wsched);
+ return;
+ }
+
+ spin_lock(&m->lock);
+again:
+ req = list_entry(m->unsent_req_list.next, struct p9_req,
+ req_list);
+ list_move_tail(&req->req_list, &m->req_list);
+ if (req->err == ERREQFLUSH)
+ goto again;
+
+ m->wbuf = req->tcall->sdata;
+ m->wsize = req->tcall->size;
+ m->wpos = 0;
+ spin_unlock(&m->lock);
+ }
+
+ P9_DPRINTK(P9_DEBUG_MUX, "mux %p pos %d size %d\n", m, m->wpos,
+ m->wsize);
+ clear_bit(Wpending, &m->wsched);
+ err = p9_fd_write(m->trans, m->wbuf + m->wpos, m->wsize - m->wpos);
+ P9_DPRINTK(P9_DEBUG_MUX, "mux %p sent %d bytes\n", m, err);
+ if (err == -EAGAIN) {
+ clear_bit(Wworksched, &m->wsched);
+ return;
+ }
+
+ 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;
+
+ if (m->wsize == 0 && !list_empty(&m->unsent_req_list)) {
+ if (test_and_clear_bit(Wpending, &m->wsched))
+ n = POLLOUT;
+ else
+ n = p9_fd_poll(m->trans, NULL);
+
+ if (n & POLLOUT) {
+ P9_DPRINTK(P9_DEBUG_MUX, "schedule write work %p\n", m);
+ queue_work(p9_mux_wq, &m->wq);
+ } else
+ clear_bit(Wworksched, &m->wsched);
+ } else
+ clear_bit(Wworksched, &m->wsched);
+
+ return;
+
+error:
+ p9_conn_cancel(m, err);
+ clear_bit(Wworksched, &m->wsched);
+}
+
+static void process_request(struct p9_conn *m, struct p9_req *req)
+{
+ int ecode;
+ struct p9_str *ename;
+
+ if (!req->err && req->rcall->id == P9_RERROR) {
+ ecode = req->rcall->params.rerror.errno;
+ ename = &req->rcall->params.rerror.error;
+
+ P9_DPRINTK(P9_DEBUG_MUX, "Rerror %.*s\n", ename->len,
+ ename->str);
+
+ if (m->extended)
+ req->err = -ecode;
+
+ if (!req->err) {
+ req->err = p9_errstr2errno(ename->str, ename->len);
+
+ /* string match failed */
+ if (!req->err) {
+ PRINT_FCALL_ERROR("unknown error", req->rcall);
+ req->err = -ESERVERFAULT;
+ }
+ }
+ } else if (req->tcall && req->rcall->id != req->tcall->id + 1) {
+ P9_DPRINTK(P9_DEBUG_ERROR,
+ "fcall mismatch: expected %d, got %d\n",
+ req->tcall->id + 1, req->rcall->id);
+ if (!req->err)
+ req->err = -EIO;
+ }
+}
+
+/**
+ * p9_read_work - called when there is some data to be read from a transport
+ */
+static void p9_read_work(struct work_struct *work)
+{
+ int n, err;
+ struct p9_conn *m;
+ struct p9_req *req, *rptr, *rreq;
+ struct p9_fcall *rcall;
+ char *rbuf;
+
+ m = container_of(work, struct p9_conn, rq);
+
+ if (m->err < 0)
+ return;
+
+ rcall = NULL;
+ P9_DPRINTK(P9_DEBUG_MUX, "start mux %p pos %d\n", m, m->rpos);
+
+ if (!m->rcall) {
+ m->rcall =
+ kmalloc(sizeof(struct p9_fcall) + m->msize, GFP_KERNEL);
+ if (!m->rcall) {
+ err = -ENOMEM;
+ goto error;
+ }
+
+ m->rbuf = (char *)m->rcall + sizeof(struct p9_fcall);
+ m->rpos = 0;
+ }
+
+ clear_bit(Rpending, &m->wsched);
+ err = p9_fd_read(m->trans, m->rbuf + m->rpos, m->msize - m->rpos);
+ P9_DPRINTK(P9_DEBUG_MUX, "mux %p got %d bytes\n", m, err);
+ if (err == -EAGAIN) {
+ clear_bit(Rworksched, &m->wsched);
+ return;
+ }
+
+ if (err <= 0)
+ goto error;
+
+ m->rpos += err;
+ while (m->rpos > 4) {
+ n = le32_to_cpu(*(__le32 *) m->rbuf);
+ if (n >= m->msize) {
+ P9_DPRINTK(P9_DEBUG_ERROR,
+ "requested packet size too big: %d\n", n);
+ err = -EIO;
+ goto error;
+ }
+
+ if (m->rpos < n)
+ break;
+
+ err =
+ p9_deserialize_fcall(m->rbuf, n, m->rcall, m->extended);
+ if (err < 0)
+ goto error;
+
+#ifdef CONFIG_NET_9P_DEBUG
+ if ((p9_debug_level&P9_DEBUG_FCALL) == P9_DEBUG_FCALL) {
+ char buf[150];
+
+ p9_printfcall(buf, sizeof(buf), m->rcall,
+ m->extended);
+ printk(KERN_NOTICE ">>> %p %s\n", m, buf);
+ }
+#endif
+
+ rcall = m->rcall;
+ rbuf = m->rbuf;
+ if (m->rpos > n) {
+ m->rcall = kmalloc(sizeof(struct p9_fcall) + m->msize,
+ GFP_KERNEL);
+ if (!m->rcall) {
+ err = -ENOMEM;
+ goto error;
+ }
+
+ m->rbuf = (char *)m->rcall + sizeof(struct p9_fcall);
+ memmove(m->rbuf, rbuf + n, m->rpos - n);
+ m->rpos -= n;
+ } else {
+ m->rcall = NULL;
+ m->rbuf = NULL;
+ m->rpos = 0;
+ }
+
+ P9_DPRINTK(P9_DEBUG_MUX, "mux %p fcall id %d tag %d\n", m,
+ rcall->id, rcall->tag);
+
+ req = NULL;
+ spin_lock(&m->lock);
+ list_for_each_entry_safe(rreq, rptr, &m->req_list, req_list) {
+ if (rreq->tag == rcall->tag) {
+ req = rreq;
+ if (req->flush != Flushing)
+ list_del(&req->req_list);
+ break;
+ }
+ }
+ spin_unlock(&m->lock);
+
+ if (req) {
+ req->rcall = rcall;
+ process_request(m, req);
+
+ if (req->flush != Flushing) {
+ if (req->cb)
+ (*req->cb) (req, req->cba);
+ else
+ kfree(req->rcall);
+
+ wake_up(&m->equeue);
+ }
+ } else {
+ if (err >= 0 && rcall->id != P9_RFLUSH)
+ P9_DPRINTK(P9_DEBUG_ERROR,
+ "unexpected response mux %p id %d tag %d\n",
+ m, rcall->id, rcall->tag);
+ kfree(rcall);
+ }
+ }
+
+ if (!list_empty(&m->req_list)) {
+ if (test_and_clear_bit(Rpending, &m->wsched))
+ n = POLLIN;
+ else
+ n = p9_fd_poll(m->trans, NULL);
+
+ if (n & POLLIN) {
+ P9_DPRINTK(P9_DEBUG_MUX, "schedule read work %p\n", m);
+ queue_work(p9_mux_wq, &m->rq);
+ } else
+ clear_bit(Rworksched, &m->wsched);
+ } else
+ clear_bit(Rworksched, &m->wsched);
+
+ return;
+
+error:
+ p9_conn_cancel(m, err);
+ clear_bit(Rworksched, &m->wsched);
+}
+
+/**
+ * p9_send_request - send 9P request
+ * The function can sleep until the request is scheduled for sending.
+ * The function can be interrupted. Return from the function is not
+ * a guarantee that the request is sent successfully. Can return errors
+ * that can be retrieved by PTR_ERR macros.
+ *
+ * @m: mux data
+ * @tc: request to be sent
+ * @cb: callback function to call when response is received
+ * @cba: parameter to pass to the callback function
+ */
+static struct p9_req *p9_send_request(struct p9_conn *m,
+ struct p9_fcall *tc,
+ p9_conn_req_callback cb, void *cba)
+{
+ int n;
+ struct p9_req *req;
+
+ P9_DPRINTK(P9_DEBUG_MUX, "mux %p task %p tcall %p id %d\n", m, current,
+ tc, tc->id);
+ if (m->err < 0)
+ return ERR_PTR(m->err);
+
+ req = kmalloc(sizeof(struct p9_req), GFP_KERNEL);
+ if (!req)
+ return ERR_PTR(-ENOMEM);
+
+ if (tc->id == P9_TVERSION)
+ n = P9_NOTAG;
+ else
+ n = p9_mux_get_tag(m);
+
+ if (n < 0)
+ return ERR_PTR(-ENOMEM);
+
+ p9_set_tag(tc, n);
+
+#ifdef CONFIG_NET_9P_DEBUG
+ if ((p9_debug_level&P9_DEBUG_FCALL) == P9_DEBUG_FCALL) {
+ char buf[150];
+
+ p9_printfcall(buf, sizeof(buf), tc, m->extended);
+ printk(KERN_NOTICE "<<< %p %s\n", m, buf);
+ }
+#endif
+
+ spin_lock_init(&req->lock);
+ req->tag = n;
+ req->tcall = tc;
+ req->rcall = NULL;
+ req->err = 0;
+ req->cb = cb;
+ req->cba = cba;
+ req->flush = None;
+
+ spin_lock(&m->lock);
+ list_add_tail(&req->req_list, &m->unsent_req_list);
+ spin_unlock(&m->lock);
+
+ if (test_and_clear_bit(Wpending, &m->wsched))
+ n = POLLOUT;
+ else
+ n = p9_fd_poll(m->trans, NULL);
+
+ if (n & POLLOUT && !test_and_set_bit(Wworksched, &m->wsched))
+ queue_work(p9_mux_wq, &m->wq);
+
+ return req;
+}
+
+static void p9_mux_free_request(struct p9_conn *m, struct p9_req *req)
+{
+ p9_mux_put_tag(m, req->tag);
+ kfree(req);
+}
+
+static void p9_mux_flush_cb(struct p9_req *freq, void *a)
+{
+ p9_conn_req_callback cb;
+ int tag;
+ struct p9_conn *m;
+ struct p9_req *req, *rreq, *rptr;
+
+ m = a;
+ P9_DPRINTK(P9_DEBUG_MUX, "mux %p tc %p rc %p err %d oldtag %d\n", m,
+ freq->tcall, freq->rcall, freq->err,
+ freq->tcall->params.tflush.oldtag);
+
+ spin_lock(&m->lock);
+ cb = NULL;
+ tag = freq->tcall->params.tflush.oldtag;
+ req = NULL;
+ list_for_each_entry_safe(rreq, rptr, &m->req_list, req_list) {
+ if (rreq->tag == tag) {
+ req = rreq;
+ list_del(&req->req_list);
+ break;
+ }
+ }
+ spin_unlock(&m->lock);
+
+ if (req) {
+ spin_lock(&req->lock);
+ req->flush = Flushed;
+ spin_unlock(&req->lock);
+
+ if (req->cb)
+ (*req->cb) (req, req->cba);
+ else
+ kfree(req->rcall);
+
+ wake_up(&m->equeue);
+ }
+
+ kfree(freq->tcall);
+ kfree(freq->rcall);
+ p9_mux_free_request(m, freq);
+}
+
+static int
+p9_mux_flush_request(struct p9_conn *m, struct p9_req *req)
+{
+ struct p9_fcall *fc;
+ struct p9_req *rreq, *rptr;
+
+ P9_DPRINTK(P9_DEBUG_MUX, "mux %p req %p tag %d\n", m, req, req->tag);
+
+ /* if a response was received for a request, do nothing */
+ spin_lock(&req->lock);
+ if (req->rcall || req->err) {
+ spin_unlock(&req->lock);
+ P9_DPRINTK(P9_DEBUG_MUX,
+ "mux %p req %p response already received\n", m, req);
+ return 0;
+ }
+
+ req->flush = Flushing;
+ spin_unlock(&req->lock);
+
+ spin_lock(&m->lock);
+ /* if the request is not sent yet, just remove it from the list */
+ list_for_each_entry_safe(rreq, rptr, &m->unsent_req_list, req_list) {
+ if (rreq->tag == req->tag) {
+ P9_DPRINTK(P9_DEBUG_MUX,
+ "mux %p req %p request is not sent yet\n", m, req);
+ list_del(&rreq->req_list);
+ req->flush = Flushed;
+ spin_unlock(&m->lock);
+ if (req->cb)
+ (*req->cb) (req, req->cba);
+ return 0;
+ }
+ }
+ spin_unlock(&m->lock);
+
+ clear_thread_flag(TIF_SIGPENDING);
+ fc = p9_create_tflush(req->tag);
+ p9_send_request(m, fc, p9_mux_flush_cb, m);
+ return 1;
+}
+
+static void
+p9_conn_rpc_cb(struct p9_req *req, void *a)
+{
+ struct p9_mux_rpc *r;
+
+ P9_DPRINTK(P9_DEBUG_MUX, "req %p r %p\n", req, a);
+ r = a;
+ r->rcall = req->rcall;
+ r->err = req->err;
+
+ if (req->flush != None && !req->err)
+ r->err = -ERESTARTSYS;
+
+ wake_up(&r->wqueue);
+}
+
+/**
+ * p9_fd_rpc- sends 9P request and waits until a response is available.
+ * The function can be interrupted.
+ * @m: mux data
+ * @tc: request to be sent
+ * @rc: pointer where a pointer to the response is stored
+ */
+int
+p9_fd_rpc(struct p9_trans *t, struct p9_fcall *tc, struct p9_fcall **rc)
+{
+ struct p9_trans_fd *p = t->priv;
+ struct p9_conn *m = p->conn;
+ int err, sigpending;
+ unsigned long flags;
+ struct p9_req *req;
+ struct p9_mux_rpc r;
+
+ r.err = 0;
+ r.tcall = tc;
+ r.rcall = NULL;
+ r.m = m;
+ init_waitqueue_head(&r.wqueue);
+
+ if (rc)
+ *rc = NULL;
+
+ sigpending = 0;
+ if (signal_pending(current)) {
+ sigpending = 1;
+ clear_thread_flag(TIF_SIGPENDING);
+ }
+
+ req = p9_send_request(m, tc, p9_conn_rpc_cb, &r);
+ if (IS_ERR(req)) {
+ err = PTR_ERR(req);
+ P9_DPRINTK(P9_DEBUG_MUX, "error %d\n", err);
+ return err;
+ }
+
+ err = wait_event_interruptible(r.wqueue, r.rcall != NULL || r.err < 0);
+ if (r.err < 0)
+ err = r.err;
+
+ if (err == -ERESTARTSYS && m->trans->status == Connected
+ && m->err == 0) {
+ if (p9_mux_flush_request(m, req)) {
+ /* wait until we get response of the flush message */
+ do {
+ clear_thread_flag(TIF_SIGPENDING);
+ err = wait_event_interruptible(r.wqueue,
+ r.rcall || r.err);
+ } while (!r.rcall && !r.err && err == -ERESTARTSYS &&
+ m->trans->status == Connected && !m->err);
+
+ err = -ERESTARTSYS;
+ }
+ sigpending = 1;
+ }
+
+ if (sigpending) {
+ spin_lock_irqsave(&current->sighand->siglock, flags);
+ recalc_sigpending();
+ spin_unlock_irqrestore(&current->sighand->siglock, flags);
+ }
+
+ if (rc)
+ *rc = r.rcall;
+ else
+ kfree(r.rcall);
+
+ p9_mux_free_request(m, req);
+ if (err > 0)
+ err = -EIO;
+
+ return err;
+}
+
+#ifdef P9_NONBLOCK
+/**
+ * p9_conn_rpcnb - sends 9P request without waiting for response.
+ * @m: mux data
+ * @tc: request to be sent
+ * @cb: callback function to be called when response arrives
+ * @cba: value to pass to the callback function
+ */
+int p9_conn_rpcnb(struct p9_conn *m, struct p9_fcall *tc,
+ p9_conn_req_callback cb, void *a)
+{
+ int err;
+ struct p9_req *req;
+
+ req = p9_send_request(m, tc, cb, a);
+ if (IS_ERR(req)) {
+ err = PTR_ERR(req);
+ P9_DPRINTK(P9_DEBUG_MUX, "error %d\n", err);
+ return PTR_ERR(req);
+ }
+
+ P9_DPRINTK(P9_DEBUG_MUX, "mux %p tc %p tag %d\n", m, tc, req->tag);
+ return 0;
+}
+#endif /* P9_NONBLOCK */
+
+/**
+ * p9_conn_cancel - cancel all pending requests with error
+ * @m: mux data
+ * @err: error code
+ */
+void p9_conn_cancel(struct p9_conn *m, int err)
+{
+ struct p9_req *req, *rtmp;
+ LIST_HEAD(cancel_list);
+
+ P9_DPRINTK(P9_DEBUG_ERROR, "mux %p err %d\n", m, err);
+ m->err = err;
+ spin_lock(&m->lock);
+ 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);
+ }
+ spin_unlock(&m->lock);
+
+ list_for_each_entry_safe(req, rtmp, &cancel_list, req_list) {
+ list_del(&req->req_list);
+ if (!req->err)
+ req->err = err;
+
+ if (req->cb)
+ (*req->cb) (req, req->cba);
+ else
+ kfree(req->rcall);
+ }
+
+ wake_up(&m->equeue);
+}
+
/**
* v9fs_parse_options - parse mount options into session structure
* @options: options string passed from mount
@@ -268,7 +1294,7 @@ end:
}
/**
- * p9_sock_close - shutdown socket
+ * p9_fd_close - shutdown socket
* @trans: private socket structure
*
*/
@@ -284,6 +1310,8 @@ static void p9_fd_close(struct p9_trans *trans)
if (!ts)
return;
+ p9_conn_destroy(ts->conn);
+
trans->status = Disconnected;
if (ts->rd)
fput(ts->rd);
@@ -292,13 +1320,15 @@ static void p9_fd_close(struct p9_trans *trans)
kfree(ts);
}
-static struct p9_trans *p9_trans_create_tcp(const char *addr, char *args)
+static struct p9_trans *
+p9_trans_create_tcp(const char *addr, char *args, int msize, unsigned char dotu)
{
int err;
struct p9_trans *trans;
struct socket *csocket;
struct sockaddr_in sin_server;
struct p9_fd_opts opts;
+ struct p9_trans_fd *p;
parse_opts(args, &opts);
@@ -306,11 +1336,10 @@ static struct p9_trans *p9_trans_create_tcp(const char *addr, char *args)
trans = kmalloc(sizeof(struct p9_trans), GFP_KERNEL);
if (!trans)
return ERR_PTR(-ENOMEM);
-
- trans->write = p9_fd_write;
- trans->read = p9_fd_read;
+ trans->msize = msize;
+ trans->extended = dotu;
+ trans->rpc = p9_fd_rpc;
trans->close = p9_fd_close;
- trans->poll = p9_fd_poll;
sin_server.sin_family = AF_INET;
sin_server.sin_addr.s_addr = in_aton(addr);
@@ -337,6 +1366,14 @@ static struct p9_trans *p9_trans_create_tcp(const char *addr, char *args)
if (err < 0)
goto error;
+ p = (struct p9_trans_fd *) trans->priv;
+ p->conn = p9_conn_create(trans);
+ if (IS_ERR(p->conn)) {
+ err = PTR_ERR(p->conn);
+ p->conn = NULL;
+ goto error;
+ }
+
return trans;
error:
@@ -347,22 +1384,23 @@ error:
return ERR_PTR(err);
}
-static struct p9_trans *p9_trans_create_unix(const char *addr, char *args)
+static struct p9_trans *
+p9_trans_create_unix(const char *addr, char *args, int msize,
+ unsigned char dotu)
{
int err;
struct socket *csocket;
struct sockaddr_un sun_server;
struct p9_trans *trans;
+ struct p9_trans_fd *p;
csocket = NULL;
trans = kmalloc(sizeof(struct p9_trans), GFP_KERNEL);
if (!trans)
return ERR_PTR(-ENOMEM);
- trans->write = p9_fd_write;
- trans->read = p9_fd_read;
+ trans->rpc = p9_fd_rpc;
trans->close = p9_fd_close;
- trans->poll = p9_fd_poll;
if (strlen(addr) > UNIX_PATH_MAX) {
P9_EPRINTK(KERN_ERR, "p9_trans_unix: address too long: %s\n",
@@ -387,6 +1425,16 @@ static struct p9_trans *p9_trans_create_unix(const char *addr, char *args)
if (err < 0)
goto error;
+ trans->msize = msize;
+ trans->extended = dotu;
+ p = (struct p9_trans_fd *) trans->priv;
+ p->conn = p9_conn_create(trans);
+ if (IS_ERR(p->conn)) {
+ err = PTR_ERR(p->conn);
+ p->conn = NULL;
+ goto error;
+ }
+
return trans;
error:
@@ -397,11 +1445,14 @@ error:
return ERR_PTR(err);
}
-static struct p9_trans *p9_trans_create_fd(const char *name, char *args)
+static struct p9_trans *
+p9_trans_create_fd(const char *name, char *args, int msize,
+ unsigned char extended)
{
int err;
struct p9_trans *trans;
struct p9_fd_opts opts;
+ struct p9_trans_fd *p;
parse_opts(args, &opts);
@@ -414,15 +1465,23 @@ static struct p9_trans *p9_trans_create_fd(const char *name, char *args)
if (!trans)
return ERR_PTR(-ENOMEM);
- trans->write = p9_fd_write;
- trans->read = p9_fd_read;
+ trans->rpc = p9_fd_rpc;
trans->close = p9_fd_close;
- trans->poll = p9_fd_poll;
err = p9_fd_open(trans, opts.rfd, opts.wfd);
if (err < 0)
goto error;
+ trans->msize = msize;
+ trans->extended = extended;
+ p = (struct p9_trans_fd *) trans->priv;
+ p->conn = p9_conn_create(trans);
+ if (IS_ERR(p->conn)) {
+ err = PTR_ERR(p->conn);
+ p->conn = NULL;
+ goto error;
+ }
+
return trans;
error:
@@ -453,6 +1512,12 @@ static struct p9_trans_module p9_fd_trans = {
static int __init p9_trans_fd_init(void)
{
+ int ret = p9_mux_global_init();
+ if (ret) {
+ printk(KERN_WARNING "9p: starting mux failed\n");
+ return ret;
+ }
+
v9fs_register_trans(&p9_tcp_trans);
v9fs_register_trans(&p9_unix_trans);
v9fs_register_trans(&p9_fd_trans);
@@ -460,13 +1525,7 @@ static int __init p9_trans_fd_init(void)
return 1;
}
-static void __exit p9_trans_fd_exit(void) {
- printk(KERN_ERR "Removal of 9p transports not implemented\n");
- BUG();
-}
-
module_init(p9_trans_fd_init);
-module_exit(p9_trans_fd_exit);
MODULE_AUTHOR("Latchesar Ionkov <lucho@ionkov.net>");
MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c
index 42eea5fe262..0117b9fb848 100644
--- a/net/9p/trans_virtio.c
+++ b/net/9p/trans_virtio.c
@@ -1,17 +1,8 @@
/*
* The Guest 9p transport driver
*
- * This is a trivial pipe-based transport driver based on the lguest console
- * code: we use lguest's DMA mechanism to send bytes out, and register a
- * DMA buffer to receive bytes in. It is assumed to be present and available
- * from the very beginning of boot.
- *
- * This may be have been done by just instaniating another HVC console,
- * but HVC's blocksize of 16 bytes is annoying and painful to performance.
- *
- * A more efficient transport could be built based on the virtio block driver
- * but it requires some changes in the 9p transport model (which are in
- * progress)
+ * This is a block based transport driver based on the lguest block driver
+ * code.
*
*/
/*
@@ -55,11 +46,25 @@
#include <linux/virtio.h>
#include <linux/virtio_9p.h>
+#define VIRTQUEUE_NUM 128
+
/* a single mutex to manage channel initialization and attachment */
static DECLARE_MUTEX(virtio_9p_lock);
/* global which tracks highest initialized channel */
static int chan_index;
+#define P9_INIT_MAXTAG 16
+
+#define REQ_STATUS_IDLE 0
+#define REQ_STATUS_SENT 1
+#define REQ_STATUS_RCVD 2
+#define REQ_STATUS_FLSH 3
+
+struct p9_req_t {
+ int status;
+ wait_queue_head_t *wq;
+};
+
/* We keep all per-channel information in a structure.
* This structure is allocated within the devices dev->mem space.
* A pointer to the structure will get put in the transport private.
@@ -68,146 +73,198 @@ static struct virtio_chan {
bool initialized; /* channel is initialized */
bool inuse; /* channel is in use */
- struct virtqueue *in_vq, *out_vq;
+ spinlock_t lock;
+
struct virtio_device *vdev;
+ struct virtqueue *vq;
- /* This is our input buffer, and how much data is left in it. */
- unsigned int in_len;
- char *in, *inbuf;
+ struct p9_idpool *tagpool;
+ struct p9_req_t *reqs;
+ int max_tag;
- wait_queue_head_t wq; /* waitq for buffer */
+ /* Scatterlist: can be too big for stack. */
+ struct scatterlist sg[VIRTQUEUE_NUM];
} channels[MAX_9P_CHAN];
+/* Lookup requests by tag */
+static struct p9_req_t *p9_lookup_tag(struct virtio_chan *c, u16 tag)
+{
+ /* This looks up the original request by tag so we know which
+ * buffer to read the data into */
+ tag++;
+
+ while (tag >= c->max_tag) {
+ int old_max = c->max_tag;
+ int count;
+
+ if (c->max_tag)
+ c->max_tag *= 2;
+ else
+ c->max_tag = P9_INIT_MAXTAG;
+
+ c->reqs = krealloc(c->reqs, sizeof(struct p9_req_t)*c->max_tag,
+ GFP_ATOMIC);
+ if (!c->reqs) {
+ printk(KERN_ERR "Couldn't grow tag array\n");
+ BUG();
+ }
+ for (count = old_max; count < c->max_tag; count++) {
+ c->reqs[count].status = REQ_STATUS_IDLE;
+ c->reqs[count].wq = kmalloc(sizeof(wait_queue_t),
+ GFP_ATOMIC);
+ if (!c->reqs[count].wq) {
+ printk(KERN_ERR "Couldn't grow tag array\n");
+ BUG();
+ }
+ init_waitqueue_head(c->reqs[count].wq);
+ }
+ }
+
+ return &c->reqs[tag];
+}
+
+
/* How many bytes left in this page. */
static unsigned int rest_of_page(void *data)
{
return PAGE_SIZE - ((unsigned long)data % PAGE_SIZE);
}
-static int p9_virtio_write(struct p9_trans *trans, void *buf, int count)
+static void p9_virtio_close(struct p9_trans *trans)
{
- struct virtio_chan *chan = (struct virtio_chan *) trans->priv;
- struct virtqueue *out_vq = chan->out_vq;
- struct scatterlist sg[1];
- unsigned int len;
+ struct virtio_chan *chan = trans->priv;
+ int count;
+ unsigned int flags;
- P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio write (%d)\n", count);
+ spin_lock_irqsave(&chan->lock, flags);
+ p9_idpool_destroy(chan->tagpool);
+ for (count = 0; count < chan->max_tag; count++)
+ kfree(chan->reqs[count].wq);
+ kfree(chan->reqs);
+ chan->max_tag = 0;
+ spin_unlock_irqrestore(&chan->lock, flags);
- /* keep it simple - make sure we don't overflow a page */
- if (rest_of_page(buf) < count)
- count = rest_of_page(buf);
+ down(&virtio_9p_lock);
+ chan->inuse = false;
+ up(&virtio_9p_lock);
- sg_init_one(sg, buf, count);
+ kfree(trans);
+}
- /* add_buf wants a token to identify this buffer: we hand it any
- * non-NULL pointer, since there's only ever one buffer. */
- if (out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, (void *)1) == 0) {
- /* Tell Host to go! */
- out_vq->vq_ops->kick(out_vq);
- /* Chill out until it's done with the buffer. */
- while (!out_vq->vq_ops->get_buf(out_vq, &len))
- cpu_relax();
+static void req_done(struct virtqueue *vq)
+{
+ struct virtio_chan *chan = vq->vdev->priv;
+ struct p9_fcall *rc;
+ unsigned int len;
+ unsigned long flags;
+ struct p9_req_t *req;
+
+ spin_lock_irqsave(&chan->lock, flags);
+ while ((rc = chan->vq->vq_ops->get_buf(chan->vq, &len)) != NULL) {
+ req = p9_lookup_tag(chan, rc->tag);
+ req->status = REQ_STATUS_RCVD;
+ wake_up(req->wq);
}
-
- P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio wrote (%d)\n", count);
-
- /* We're expected to return the amount of data we wrote: all of it. */
- return count;
+ /* In case queue is stopped waiting for more buffers. */
+ spin_unlock_irqrestore(&chan->lock, flags);
}
-/* Create a scatter-gather list representing our input buffer and put it in the
- * queue. */
-static void add_inbuf(struct virtio_chan *chan)
+static int
+pack_sg_list(struct scatterlist *sg, int start, int limit, char *data,
+ int count)
{
- struct scatterlist sg[1];
-
- sg_init_one(sg, chan->inbuf, PAGE_SIZE);
+ int s;
+ int index = start;
+
+ while (count) {
+ s = rest_of_page(data);
+ if (s > count)
+ s = count;
+ sg_set_buf(&sg[index++], data, s);
+ count -= s;
+ data += s;
+ if (index > limit)
+ BUG();
+ }
- /* We should always be able to add one buffer to an empty queue. */
- if (chan->in_vq->vq_ops->add_buf(chan->in_vq, sg, 0, 1, chan->inbuf))
- BUG();
- chan->in_vq->vq_ops->kick(chan->in_vq);
+ return index-start;
}
-static int p9_virtio_read(struct p9_trans *trans, void *buf, int count)
+static int
+p9_virtio_rpc(struct p9_trans *t, struct p9_fcall *tc, struct p9_fcall **rc)
{
- struct virtio_chan *chan = (struct virtio_chan *) trans->priv;
- struct virtqueue *in_vq = chan->in_vq;
-
- P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio read (%d)\n", count);
+ int in, out;
+ int n, err, size;
+ struct virtio_chan *chan = t->priv;
+ char *rdata;
+ struct p9_req_t *req;
+ unsigned long flags;
+
+ if (*rc == NULL) {
+ *rc = kmalloc(sizeof(struct p9_fcall) + t->msize, GFP_KERNEL);
+ if (!*rc)
+ return -ENOMEM;
+ }
- /* If we don't have an input queue yet, we can't get input. */
- BUG_ON(!in_vq);
+ rdata = (char *)*rc+sizeof(struct p9_fcall);
- /* No buffer? Try to get one. */
- if (!chan->in_len) {
- chan->in = in_vq->vq_ops->get_buf(in_vq, &chan->in_len);
- if (!chan->in)
- return 0;
+ n = P9_NOTAG;
+ if (tc->id != P9_TVERSION) {
+ n = p9_idpool_get(chan->tagpool);
+ if (n < 0)
+ return -ENOMEM;
}
- /* You want more than we have to give? Well, try wanting less! */
- if (chan->in_len < count)
- count = chan->in_len;
+ spin_lock_irqsave(&chan->lock, flags);
+ req = p9_lookup_tag(chan, n);
+ spin_unlock_irqrestore(&chan->lock, flags);
- /* Copy across to their buffer and increment offset. */
- memcpy(buf, chan->in, count);
- chan->in += count;
- chan->in_len -= count;
+ p9_set_tag(tc, n);
- /* Finished? Re-register buffer so Host will use it again. */
- if (chan->in_len == 0)
- add_inbuf(chan);
+ P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio rpc tag %d\n", n);
- P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio finished read (%d)\n",
- count);
+ out = pack_sg_list(chan->sg, 0, VIRTQUEUE_NUM, tc->sdata, tc->size);
+ in = pack_sg_list(chan->sg, out, VIRTQUEUE_NUM-out, rdata, t->msize);
- return count;
-}
+ req->status = REQ_STATUS_SENT;
-/* The poll function is used by 9p transports to determine if there
- * is there is activity available on a particular channel. In our case
- * we use it to wait for a callback from the input routines.
- */
-static unsigned int
-p9_virtio_poll(struct p9_trans *trans, struct poll_table_struct *pt)
-{
- struct virtio_chan *chan = (struct virtio_chan *)trans->priv;
- struct virtqueue *in_vq = chan->in_vq;
- int ret = POLLOUT; /* we can always handle more output */
+ if (chan->vq->vq_ops->add_buf(chan->vq, chan->sg, out, in, tc)) {
+ P9_DPRINTK(P9_DEBUG_TRANS,
+ "9p debug: virtio rpc add_buf returned failure");
+ return -EIO;
+ }
- poll_wait(NULL, &chan->wq, pt);
+ chan->vq->vq_ops->kick(chan->vq);
- /* No buffer? Try to get one. */
- if (!chan->in_len)
- chan->in = in_vq->vq_ops->get_buf(in_vq, &chan->in_len);
+ wait_event(*req->wq, req->status == REQ_STATUS_RCVD);
- if (chan->in_len)
- ret |= POLLIN;
+ size = le32_to_cpu(*(__le32 *) rdata);
- return ret;
-}
+ err = p9_deserialize_fcall(rdata, size, *rc, t->extended);
+ if (err < 0) {
+ P9_DPRINTK(P9_DEBUG_TRANS,
+ "9p debug: virtio rpc deserialize returned %d\n", err);
+ return err;
+ }
-static void p9_virtio_close(struct p9_trans *trans)
-{
- struct virtio_chan *chan = trans->priv;
+#ifdef CONFIG_NET_9P_DEBUG
+ if ((p9_debug_level&P9_DEBUG_FCALL) == P9_DEBUG_FCALL) {
+ char buf[150];
- down(&virtio_9p_lock);
- chan->inuse = false;
- up(&virtio_9p_lock);
+ p9_printfcall(buf, sizeof(buf), *rc, t->extended);
+ printk(KERN_NOTICE ">>> %p %s\n", t, buf);
+ }
+#endif
- kfree(trans);
-}
+ if (n != P9_NOTAG && p9_idpool_check(n, chan->tagpool))
+ p9_idpool_put(n, chan->tagpool);
-static void p9_virtio_intr(struct virtqueue *q)
-{
- struct virtio_chan *chan = q->vdev->priv;
+ req->status = REQ_STATUS_IDLE;
- P9_DPRINTK(P9_DEBUG_TRANS, "9p poll_wakeup: %p\n", &chan->wq);
- wake_up_interruptible(&chan->wq);
+ return 0;
}
-static int p9_virtio_probe(struct virtio_device *dev)
+static int p9_virtio_probe(struct virtio_device *vdev)
{
int err;
struct virtio_chan *chan;
@@ -221,44 +278,29 @@ static int p9_virtio_probe(struct virtio_device *dev)
if (chan_index > MAX_9P_CHAN) {
printk(KERN_ERR "9p: virtio: Maximum channels exceeded\n");
BUG();
- }
-
- chan->vdev = dev;
-
- /* This is the scratch page we use to receive console input */
- chan->inbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
- if (!chan->inbuf) {
err = -ENOMEM;
goto fail;
}
- /* Find the input queue. */
- dev->priv = chan;
- chan->in_vq = dev->config->find_vq(dev, 0, p9_virtio_intr);
- if (IS_ERR(chan->in_vq)) {
- err = PTR_ERR(chan->in_vq);
- goto free;
- }
+ chan->vdev = vdev;
- chan->out_vq = dev->config->find_vq(dev, 1, NULL);
- if (IS_ERR(chan->out_vq)) {
- err = PTR_ERR(chan->out_vq);
- goto free_in_vq;
+ /* We expect one virtqueue, for requests. */
+ chan->vq = vdev->config->find_vq(vdev, 0, req_done);
+ if (IS_ERR(chan->vq)) {
+ err = PTR_ERR(chan->vq);
+ goto out_free_vq;
}
+ chan->vq->vdev->priv = chan;
+ spin_lock_init(&chan->lock);
- init_waitqueue_head(&chan->wq);
+ sg_init_table(chan->sg, VIRTQUEUE_NUM);
- /* Register the input buffer the first time. */
- add_inbuf(chan);
chan->inuse = false;
chan->initialized = true;
-
return 0;
-free_in_vq:
- dev->config->del_vq(chan->in_vq);
-free:
- kfree(chan->inbuf);
+out_free_vq:
+ vdev->config->del_vq(chan->vq);
fail:
down(&virtio_9p_lock);
chan_index--;
@@ -271,11 +313,13 @@ fail:
* alternate channels by matching devname versus a virtio_config entry.
* We use a simple reference count mechanism to ensure that only a single
* mount has a channel open at a time. */
-static struct p9_trans *p9_virtio_create(const char *devname, char *args)
+static struct p9_trans *
+p9_virtio_create(const char *devname, char *args, int msize,
+ unsigned char extended)
{
struct p9_trans *trans;
- int index = 0;
struct virtio_chan *chan = channels;
+ int index = 0;
down(&virtio_9p_lock);
while (index < MAX_9P_CHAN) {
@@ -290,25 +334,45 @@ static struct p9_trans *p9_virtio_create(const char *devname, char *args)
up(&virtio_9p_lock);
if (index >= MAX_9P_CHAN) {
- printk(KERN_ERR "9p: virtio: couldn't find a free channel\n");
- return NULL;
+ printk(KERN_ERR "9p: no channels available\n");
+ return ERR_PTR(-ENODEV);
}
+ chan->tagpool = p9_idpool_create();
+ if (IS_ERR(chan->tagpool)) {
+ printk(KERN_ERR "9p: couldn't allocate tagpool\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ p9_idpool_get(chan->tagpool); /* reserve tag 0 */
+ chan->max_tag = 0;
+ chan->reqs = NULL;
+
trans = kmalloc(sizeof(struct p9_trans), GFP_KERNEL);
if (!trans) {
printk(KERN_ERR "9p: couldn't allocate transport\n");
return ERR_PTR(-ENOMEM);
}
-
- trans->write = p9_virtio_write;
- trans->read = p9_virtio_read;
+ trans->extended = extended;
+ trans->msize = msize;
trans->close = p9_virtio_close;
- trans->poll = p9_virtio_poll;
+ trans->rpc = p9_virtio_rpc;
trans->priv = chan;
return trans;
}
+static void p9_virtio_remove(struct virtio_device *vdev)
+{
+ struct virtio_chan *chan = vdev->priv;
+
+ BUG_ON(chan->inuse);
+
+ if (chan->initialized) {
+ vdev->config->del_vq(chan->vq);
+ chan->initialized = false;
+ }
+}
+
#define VIRTIO_ID_9P 9
static struct virtio_device_id id_table[] = {
@@ -322,12 +386,13 @@ static struct virtio_driver p9_virtio_drv = {
.driver.owner = THIS_MODULE,
.id_table = id_table,
.probe = p9_virtio_probe,
+ .remove = p9_virtio_remove,
};
static struct p9_trans_module p9_virtio_trans = {
.name = "virtio",
.create = p9_virtio_create,
- .maxsize = PAGE_SIZE,
+ .maxsize = PAGE_SIZE*16,
.def = 0,
};
@@ -343,7 +408,13 @@ static int __init p9_virtio_init(void)
return register_virtio_driver(&p9_virtio_drv);
}
+static void __exit p9_virtio_cleanup(void)
+{
+ unregister_virtio_driver(&p9_virtio_drv);
+}
+
module_init(p9_virtio_init);
+module_exit(p9_virtio_cleanup);
MODULE_DEVICE_TABLE(virtio, id_table);
MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
diff --git a/net/9p/util.c b/net/9p/util.c
index 22077b79395..ef7215565d8 100644
--- a/net/9p/util.c
+++ b/net/9p/util.c
@@ -33,7 +33,7 @@
#include <net/9p/9p.h>
struct p9_idpool {
- struct semaphore lock;
+ spinlock_t lock;
struct idr pool;
};
@@ -45,7 +45,7 @@ struct p9_idpool *p9_idpool_create(void)
if (!p)
return ERR_PTR(-ENOMEM);
- init_MUTEX(&p->lock);
+ spin_lock_init(&p->lock);
idr_init(&p->pool);
return p;
@@ -71,19 +71,17 @@ int p9_idpool_get(struct p9_idpool *p)
{
int i = 0;
int error;
+ unsigned int flags;
retry:
if (idr_pre_get(&p->pool, GFP_KERNEL) == 0)
return 0;
- if (down_interruptible(&p->lock) == -EINTR) {
- P9_EPRINTK(KERN_WARNING, "Interrupted while locking\n");
- return -1;
- }
+ spin_lock_irqsave(&p->lock, flags);
/* no need to store exactly p, we just need something non-null */
error = idr_get_new(&p->pool, p, &i);
- up(&p->lock);
+ spin_unlock_irqrestore(&p->lock, flags);
if (error == -EAGAIN)
goto retry;
@@ -104,12 +102,10 @@ EXPORT_SYMBOL(p9_idpool_get);
void p9_idpool_put(int id, struct p9_idpool *p)
{
- if (down_interruptible(&p->lock) == -EINTR) {
- P9_EPRINTK(KERN_WARNING, "Interrupted while locking\n");
- return;
- }
+ unsigned int flags;
+ spin_lock_irqsave(&p->lock, flags);
idr_remove(&p->pool, id);
- up(&p->lock);
+ spin_unlock_irqrestore(&p->lock, flags);
}
EXPORT_SYMBOL(p9_idpool_put);
diff --git a/net/Kconfig b/net/Kconfig
index b6a5d454f2f..6627c6ae5db 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -30,7 +30,7 @@ menu "Networking options"
config NET_NS
bool "Network namespace support"
default n
- depends on EXPERIMENTAL && !SYSFS
+ depends on EXPERIMENTAL && !SYSFS && NAMESPACES
help
Allow user space to create what appear to be multiple instances
of the network stack.
diff --git a/net/can/af_can.c b/net/can/af_can.c
index 5158e886630..36b9f22ed83 100644
--- a/net/can/af_can.c
+++ b/net/can/af_can.c
@@ -118,7 +118,6 @@ static int can_create(struct net *net, struct socket *sock, int protocol)
{
struct sock *sk;
struct can_proto *cp;
- char module_name[sizeof("can-proto-000")];
int err = 0;
sock->state = SS_UNCONNECTED;
@@ -129,26 +128,21 @@ static int can_create(struct net *net, struct socket *sock, int protocol)
if (net != &init_net)
return -EAFNOSUPPORT;
+#ifdef CONFIG_KMOD
/* try to load protocol module, when CONFIG_KMOD is defined */
if (!proto_tab[protocol]) {
- sprintf(module_name, "can-proto-%d", protocol);
- err = request_module(module_name);
+ err = request_module("can-proto-%d", protocol);
/*
* In case of error we only print a message but don't
* return the error code immediately. Below we will
* return -EPROTONOSUPPORT
*/
- if (err == -ENOSYS) {
- if (printk_ratelimit())
- printk(KERN_INFO "can: request_module(%s)"
- " not implemented.\n", module_name);
- } else if (err) {
- if (printk_ratelimit())
- printk(KERN_ERR "can: request_module(%s)"
- " failed.\n", module_name);
- }
+ if (err && printk_ratelimit())
+ printk(KERN_ERR "can: request_module "
+ "(can-proto-%d) failed.\n", protocol);
}
+#endif
spin_lock(&proto_tab_lock);
cp = proto_tab[protocol];
@@ -662,26 +656,26 @@ int can_proto_register(struct can_proto *cp)
return -EINVAL;
}
+ err = proto_register(cp->prot, 0);
+ if (err < 0)
+ return err;
+
spin_lock(&proto_tab_lock);
if (proto_tab[proto]) {
printk(KERN_ERR "can: protocol %d already registered\n",
proto);
err = -EBUSY;
- goto errout;
+ } else {
+ proto_tab[proto] = cp;
+
+ /* use generic ioctl function if not defined by module */
+ if (!cp->ops->ioctl)
+ cp->ops->ioctl = can_ioctl;
}
+ spin_unlock(&proto_tab_lock);
- err = proto_register(cp->prot, 0);
if (err < 0)
- goto errout;
-
- proto_tab[proto] = cp;
-
- /* use generic ioctl function if the module doesn't bring its own */
- if (!cp->ops->ioctl)
- cp->ops->ioctl = can_ioctl;
-
- errout:
- spin_unlock(&proto_tab_lock);
+ proto_unregister(cp->prot);
return err;
}
@@ -700,9 +694,10 @@ void can_proto_unregister(struct can_proto *cp)
printk(KERN_ERR "BUG: can: protocol %d is not registered\n",
proto);
}
- proto_unregister(cp->prot);
proto_tab[proto] = NULL;
spin_unlock(&proto_tab_lock);
+
+ proto_unregister(cp->prot);
}
EXPORT_SYMBOL(can_proto_unregister);
diff --git a/net/can/raw.c b/net/can/raw.c
index aeefd1419d0..94cd7f27c44 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -98,7 +98,6 @@ static void raw_rcv(struct sk_buff *skb, void *data)
struct sock *sk = (struct sock *)data;
struct raw_sock *ro = raw_sk(sk);
struct sockaddr_can *addr;
- int error;
if (!ro->recv_own_msgs) {
/* check the received tx sock reference */
@@ -121,14 +120,12 @@ static void raw_rcv(struct sk_buff *skb, void *data)
addr->can_family = AF_CAN;
addr->can_ifindex = skb->dev->ifindex;
- error = sock_queue_rcv_skb(sk, skb);
- if (error < 0)
+ if (sock_queue_rcv_skb(sk, skb) < 0)
kfree_skb(skb);
}
static int raw_enable_filters(struct net_device *dev, struct sock *sk,
- struct can_filter *filter,
- int count)
+ struct can_filter *filter, int count)
{
int err = 0;
int i;
@@ -163,8 +160,7 @@ static int raw_enable_errfilter(struct net_device *dev, struct sock *sk,
}
static void raw_disable_filters(struct net_device *dev, struct sock *sk,
- struct can_filter *filter,
- int count)
+ struct can_filter *filter, int count)
{
int i;
@@ -353,7 +349,6 @@ static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len)
/* filters set by default/setsockopt */
err = raw_enable_allfilters(dev, sk);
dev_put(dev);
-
} else {
ifindex = 0;
@@ -466,7 +461,6 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
if (err) {
if (count > 1)
kfree(filter);
-
goto out_fil;
}
@@ -673,25 +667,25 @@ static int raw_recvmsg(struct kiocb *iocb, struct socket *sock,
{
struct sock *sk = sock->sk;
struct sk_buff *skb;
- int error = 0;
+ int err = 0;
int noblock;
noblock = flags & MSG_DONTWAIT;
flags &= ~MSG_DONTWAIT;
- skb = skb_recv_datagram(sk, flags, noblock, &error);
+ skb = skb_recv_datagram(sk, flags, noblock, &err);
if (!skb)
- return error;
+ return err;
if (size < skb->len)
msg->msg_flags |= MSG_TRUNC;
else
size = skb->len;
- error = memcpy_toiovec(msg->msg_iov, skb->data, size);
- if (error < 0) {
+ err = memcpy_toiovec(msg->msg_iov, skb->data, size);
+ if (err < 0) {
skb_free_datagram(sk, skb);
- return error;
+ return err;
}
sock_recv_timestamp(msg, sk, skb);
diff --git a/net/core/flow.c b/net/core/flow.c
index 46b38e06e0d..a77531c139b 100644
--- a/net/core/flow.c
+++ b/net/core/flow.c
@@ -30,8 +30,8 @@ struct flow_cache_entry {
struct flow_cache_entry *next;
u16 family;
u8 dir;
- struct flowi key;
u32 genid;
+ struct flowi key;
void *object;
atomic_t *object_ref;
};
@@ -52,7 +52,7 @@ struct flow_percpu_info {
int hash_rnd_recalc;
u32 hash_rnd;
int count;
-} ____cacheline_aligned;
+};
static DEFINE_PER_CPU(struct flow_percpu_info, flow_hash_info) = { 0 };
#define flow_hash_rnd_recalc(cpu) \
@@ -346,7 +346,7 @@ static int __init flow_cache_init(void)
flow_cachep = kmem_cache_create("flow_cache",
sizeof(struct flow_cache_entry),
- 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
+ 0, SLAB_PANIC,
NULL);
flow_hash_shift = 10;
flow_lwm = 2 * flow_hash_size;
diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c
index 31be29b8b5a..9dc0abb50ea 100644
--- a/net/decnet/dn_route.c
+++ b/net/decnet/dn_route.c
@@ -94,7 +94,7 @@ struct dn_rt_hash_bucket
{
struct dn_route *chain;
spinlock_t lock;
-} __attribute__((__aligned__(8)));
+};
extern struct neigh_table dn_neigh_table;
diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c
index a2241060113..8cd357f4128 100644
--- a/net/ipv4/cipso_ipv4.c
+++ b/net/ipv4/cipso_ipv4.c
@@ -547,8 +547,8 @@ int cipso_v4_doi_remove(u32 doi,
rcu_read_lock();
list_for_each_entry_rcu(dom_iter, &doi_def->dom_list, list)
if (dom_iter->valid)
- netlbl_domhsh_remove(dom_iter->domain,
- audit_info);
+ netlbl_cfg_map_del(dom_iter->domain,
+ audit_info);
rcu_read_unlock();
cipso_v4_cache_invalidate();
call_rcu(&doi_def->rcu, callback);
diff --git a/net/ipv4/ipvs/ip_vs_wrr.c b/net/ipv4/ipvs/ip_vs_wrr.c
index 749fa044eca..85c680add6d 100644
--- a/net/ipv4/ipvs/ip_vs_wrr.c
+++ b/net/ipv4/ipvs/ip_vs_wrr.c
@@ -22,6 +22,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/net.h>
#include <net/ip_vs.h>
@@ -169,7 +170,7 @@ ip_vs_wrr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
*/
if (mark->cw == 0) {
mark->cl = &svc->destinations;
- IP_VS_INFO("ip_vs_wrr_schedule(): "
+ IP_VS_ERR_RL("ip_vs_wrr_schedule(): "
"no available servers\n");
dest = NULL;
goto out;
diff --git a/net/ipv4/netfilter/nf_nat_core.c b/net/ipv4/netfilter/nf_nat_core.c
index dd07362d2b8..0d5fa3a54d0 100644
--- a/net/ipv4/netfilter/nf_nat_core.c
+++ b/net/ipv4/netfilter/nf_nat_core.c
@@ -600,10 +600,10 @@ static void nf_nat_cleanup_conntrack(struct nf_conn *ct)
spin_unlock_bh(&nf_nat_lock);
}
-static void nf_nat_move_storage(struct nf_conn *conntrack, void *old)
+static void nf_nat_move_storage(void *new, void *old)
{
- struct nf_conn_nat *new_nat = nf_ct_ext_find(conntrack, NF_CT_EXT_NAT);
- struct nf_conn_nat *old_nat = (struct nf_conn_nat *)old;
+ struct nf_conn_nat *new_nat = new;
+ struct nf_conn_nat *old_nat = old;
struct nf_conn *ct = old_nat->ct;
if (!ct || !(ct->status & IPS_NAT_DONE_MASK))
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 8842ecb9be4..525787b52b7 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -2041,7 +2041,7 @@ int ip_route_input(struct sk_buff *skb, __be32 daddr, __be32 saddr,
int iif = dev->ifindex;
struct net *net;
- net = skb->dev->nd_net;
+ net = dev->nd_net;
tos &= IPTOS_RT_MASK;
hash = rt_hash(daddr, saddr, iif);
diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c
index 2255e3c082e..fee22caf1ba 100644
--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -482,6 +482,10 @@ static int iucv_sock_connect(struct socket *sock, struct sockaddr *addr,
/* Create path. */
iucv->path = iucv_path_alloc(IUCV_QUEUELEN_DEFAULT,
IPRMDATA, GFP_KERNEL);
+ if (!iucv->path) {
+ err = -ENOMEM;
+ goto done;
+ }
err = iucv_path_connect(iucv->path, &af_iucv_handler,
sa->siucv_user_id, NULL, user_data, sk);
if (err) {
@@ -1094,6 +1098,8 @@ static void iucv_callback_rx(struct iucv_path *path, struct iucv_message *msg)
save_message:
save_msg = kzalloc(sizeof(struct sock_msg_q), GFP_ATOMIC | GFP_DMA);
+ if (!save_msg)
+ return;
save_msg->path = path;
save_msg->msg = *msg;
@@ -1106,24 +1112,31 @@ static void iucv_callback_txdone(struct iucv_path *path,
struct iucv_message *msg)
{
struct sock *sk = path->private;
- struct sk_buff *this;
+ struct sk_buff *this = NULL;
struct sk_buff_head *list = &iucv_sk(sk)->send_skb_q;
struct sk_buff *list_skb = list->next;
unsigned long flags;
- if (list_skb) {
+ if (!skb_queue_empty(list)) {
spin_lock_irqsave(&list->lock, flags);
- do {
- this = list_skb;
+ while (list_skb != (struct sk_buff *)list) {
+ if (!memcmp(&msg->tag, list_skb->cb, 4)) {
+ this = list_skb;
+ break;
+ }
list_skb = list_skb->next;
- } while (memcmp(&msg->tag, this->cb, 4) && list_skb);
+ }
+ if (this)
+ __skb_unlink(this, list);
spin_unlock_irqrestore(&list->lock, flags);
- skb_unlink(this, &iucv_sk(sk)->send_skb_q);
- kfree_skb(this);
+ if (this)
+ kfree_skb(this);
}
+ if (!this)
+ printk(KERN_ERR "AF_IUCV msg tag %u not found\n", msg->tag);
if (sk->sk_state == IUCV_CLOSING) {
if (skb_queue_empty(&iucv_sk(sk)->send_skb_q)) {
diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c
index f13fe8821cb..2753b0c448f 100644
--- a/net/iucv/iucv.c
+++ b/net/iucv/iucv.c
@@ -693,9 +693,9 @@ int iucv_register(struct iucv_handler *handler, int smp)
iucv_setmask_up();
INIT_LIST_HEAD(&handler->paths);
- spin_lock_irq(&iucv_table_lock);
+ spin_lock_bh(&iucv_table_lock);
list_add_tail(&handler->list, &iucv_handler_list);
- spin_unlock_irq(&iucv_table_lock);
+ spin_unlock_bh(&iucv_table_lock);
rc = 0;
out_mutex:
mutex_unlock(&iucv_register_mutex);
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index e77592d050c..45c7c0c3875 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -1,6 +1,5 @@
config MAC80211
tristate "Generic IEEE 802.11 Networking Stack (mac80211)"
- depends on EXPERIMENTAL
select CRYPTO
select CRYPTO_ECB
select CRYPTO_ARC4
diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c
index cf6ba6659a8..8b9be1e978c 100644
--- a/net/netfilter/nf_conntrack_extend.c
+++ b/net/netfilter/nf_conntrack_extend.c
@@ -109,7 +109,8 @@ void *__nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp)
rcu_read_lock();
t = rcu_dereference(nf_ct_ext_types[i]);
if (t && t->move)
- t->move(ct, ct->ext + ct->ext->offset[i]);
+ t->move((void *)new + new->offset[i],
+ (void *)ct->ext + ct->ext->offset[i]);
rcu_read_unlock();
}
kfree(ct->ext);
diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c
index 3e0cccae563..202d7fa0948 100644
--- a/net/netfilter/nf_conntrack_proto_tcp.c
+++ b/net/netfilter/nf_conntrack_proto_tcp.c
@@ -125,7 +125,7 @@ enum tcp_bit_set {
* CLOSE_WAIT: ACK seen (after FIN)
* LAST_ACK: FIN seen (after FIN)
* TIME_WAIT: last ACK seen
- * CLOSE: closed connection
+ * CLOSE: closed connection (RST)
*
* LISTEN state is not used.
*
@@ -824,7 +824,21 @@ static int tcp_packet(struct nf_conn *ct,
case TCP_CONNTRACK_SYN_SENT:
if (old_state < TCP_CONNTRACK_TIME_WAIT)
break;
- if ((ct->proto.tcp.seen[!dir].flags & IP_CT_TCP_FLAG_CLOSE_INIT)
+ /* RFC 1122: "When a connection is closed actively,
+ * it MUST linger in TIME-WAIT state for a time 2xMSL
+ * (Maximum Segment Lifetime). However, it MAY accept
+ * a new SYN from the remote TCP to reopen the connection
+ * directly from TIME-WAIT state, if..."
+ * We ignore the conditions because we are in the
+ * TIME-WAIT state anyway.
+ *
+ * Handle aborted connections: we and the server
+ * think there is an existing connection but the client
+ * aborts it and starts a new one.
+ */
+ if (((ct->proto.tcp.seen[dir].flags
+ | ct->proto.tcp.seen[!dir].flags)
+ & IP_CT_TCP_FLAG_CLOSE_INIT)
|| (ct->proto.tcp.last_dir == dir
&& ct->proto.tcp.last_index == TCP_RST_SET)) {
/* Attempt to reopen a closed/aborted connection.
@@ -838,15 +852,22 @@ static int tcp_packet(struct nf_conn *ct,
case TCP_CONNTRACK_IGNORE:
/* Ignored packets:
*
+ * Our connection entry may be out of sync, so ignore
+ * packets which may signal the real connection between
+ * the client and the server.
+ *
* a) SYN in ORIGINAL
* b) SYN/ACK in REPLY
* c) ACK in reply direction after initial SYN in original.
+ *
+ * If the ignored packet is invalid, the receiver will send
+ * a RST we'll catch below.
*/
if (index == TCP_SYNACK_SET
&& ct->proto.tcp.last_index == TCP_SYN_SET
&& ct->proto.tcp.last_dir != dir
&& ntohl(th->ack_seq) == ct->proto.tcp.last_end) {
- /* This SYN/ACK acknowledges a SYN that we earlier
+ /* b) This SYN/ACK acknowledges a SYN that we earlier
* ignored as invalid. This means that the client and
* the server are both in sync, while the firewall is
* not. We kill this session and block the SYN/ACK so
@@ -870,7 +891,7 @@ static int tcp_packet(struct nf_conn *ct,
write_unlock_bh(&tcp_lock);
if (LOG_INVALID(IPPROTO_TCP))
nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
- "nf_ct_tcp: invalid packed ignored ");
+ "nf_ct_tcp: invalid packet ignored ");
return NF_ACCEPT;
case TCP_CONNTRACK_MAX:
/* Invalid packet */
@@ -924,8 +945,7 @@ static int tcp_packet(struct nf_conn *ct,
ct->proto.tcp.state = new_state;
if (old_state != new_state
- && (new_state == TCP_CONNTRACK_FIN_WAIT
- || new_state == TCP_CONNTRACK_CLOSE))
+ && new_state == TCP_CONNTRACK_CLOSE)
ct->proto.tcp.seen[dir].flags |= IP_CT_TCP_FLAG_CLOSE_INIT;
timeout = ct->proto.tcp.retrans >= nf_ct_tcp_max_retrans
&& tcp_timeouts[new_state] > nf_ct_tcp_timeout_max_retrans
diff --git a/net/netfilter/xt_iprange.c b/net/netfilter/xt_iprange.c
index 01035fc0e14..4f984dc6031 100644
--- a/net/netfilter/xt_iprange.c
+++ b/net/netfilter/xt_iprange.c
@@ -13,6 +13,7 @@
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/xt_iprange.h>
#include <linux/netfilter_ipv4/ipt_iprange.h>
static bool
@@ -148,7 +149,7 @@ static struct xt_match iprange_mt_reg[] __read_mostly = {
{
.name = "iprange",
.revision = 1,
- .family = AF_INET6,
+ .family = AF_INET,
.match = iprange_mt4,
.matchsize = sizeof(struct xt_iprange_mtinfo),
.me = THIS_MODULE,
diff --git a/net/netlabel/netlabel_cipso_v4.c b/net/netlabel/netlabel_cipso_v4.c
index becf91a952a..c7ad64d664a 100644
--- a/net/netlabel/netlabel_cipso_v4.c
+++ b/net/netlabel/netlabel_cipso_v4.c
@@ -90,7 +90,7 @@ static const struct nla_policy netlbl_cipsov4_genl_policy[NLBL_CIPSOV4_A_MAX + 1
* safely.
*
*/
-static void netlbl_cipsov4_doi_free(struct rcu_head *entry)
+void netlbl_cipsov4_doi_free(struct rcu_head *entry)
{
struct cipso_v4_doi *ptr;
diff --git a/net/netlabel/netlabel_cipso_v4.h b/net/netlabel/netlabel_cipso_v4.h
index f03cf9b7828..220cb9d06b4 100644
--- a/net/netlabel/netlabel_cipso_v4.h
+++ b/net/netlabel/netlabel_cipso_v4.h
@@ -163,4 +163,7 @@ enum {
/* NetLabel protocol functions */
int netlbl_cipsov4_genl_init(void);
+/* Free the memory associated with a CIPSOv4 DOI definition */
+void netlbl_cipsov4_doi_free(struct rcu_head *entry);
+
#endif
diff --git a/net/netlabel/netlabel_domainhash.h b/net/netlabel/netlabel_domainhash.h
index 3689956c343..8220990ceb9 100644
--- a/net/netlabel/netlabel_domainhash.h
+++ b/net/netlabel/netlabel_domainhash.h
@@ -61,6 +61,7 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry,
struct netlbl_audit *audit_info);
int netlbl_domhsh_add_default(struct netlbl_dom_map *entry,
struct netlbl_audit *audit_info);
+int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info);
int netlbl_domhsh_remove_default(struct netlbl_audit *audit_info);
struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain);
int netlbl_domhsh_walk(u32 *skip_bkt,
diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c
index c69e3e1f05c..39793a1a93a 100644
--- a/net/netlabel/netlabel_kapi.c
+++ b/net/netlabel/netlabel_kapi.c
@@ -30,6 +30,7 @@
#include <linux/init.h>
#include <linux/types.h>
+#include <linux/audit.h>
#include <net/ip.h>
#include <net/netlabel.h>
#include <net/cipso_ipv4.h>
@@ -38,10 +39,186 @@
#include "netlabel_domainhash.h"
#include "netlabel_unlabeled.h"
+#include "netlabel_cipso_v4.h"
#include "netlabel_user.h"
#include "netlabel_mgmt.h"
/*
+ * Configuration Functions
+ */
+
+/**
+ * netlbl_cfg_map_del - Remove a NetLabel/LSM domain mapping
+ * @domain: the domain mapping to remove
+ * @audit_info: NetLabel audit information
+ *
+ * Description:
+ * Removes a NetLabel/LSM domain mapping. A @domain value of NULL causes the
+ * default domain mapping to be removed. Returns zero on success, negative
+ * values on failure.
+ *
+ */
+int netlbl_cfg_map_del(const char *domain, struct netlbl_audit *audit_info)
+{
+ return netlbl_domhsh_remove(domain, audit_info);
+}
+
+/**
+ * netlbl_cfg_unlbl_add_map - Add an unlabeled NetLabel/LSM domain mapping
+ * @domain: the domain mapping to add
+ * @audit_info: NetLabel audit information
+ *
+ * Description:
+ * Adds a new unlabeled NetLabel/LSM domain mapping. A @domain value of NULL
+ * causes a new default domain mapping to be added. Returns zero on success,
+ * negative values on failure.
+ *
+ */
+int netlbl_cfg_unlbl_add_map(const char *domain,
+ struct netlbl_audit *audit_info)
+{
+ int ret_val = -ENOMEM;
+ struct netlbl_dom_map *entry;
+
+ entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
+ if (entry == NULL)
+ goto cfg_unlbl_add_map_failure;
+ if (domain != NULL) {
+ entry->domain = kstrdup(domain, GFP_ATOMIC);
+ if (entry->domain == NULL)
+ goto cfg_unlbl_add_map_failure;
+ }
+ entry->type = NETLBL_NLTYPE_UNLABELED;
+
+ ret_val = netlbl_domhsh_add(entry, audit_info);
+ if (ret_val != 0)
+ goto cfg_unlbl_add_map_failure;
+
+ return 0;
+
+cfg_unlbl_add_map_failure:
+ if (entry != NULL)
+ kfree(entry->domain);
+ kfree(entry);
+ return ret_val;
+}
+
+/**
+ * netlbl_cfg_cipsov4_add - Add a new CIPSOv4 DOI definition
+ * @doi_def: the DOI definition
+ * @audit_info: NetLabel audit information
+ *
+ * Description:
+ * Add a new CIPSOv4 DOI definition to the NetLabel subsystem. Returns zero on
+ * success, negative values on failure.
+ *
+ */
+int netlbl_cfg_cipsov4_add(struct cipso_v4_doi *doi_def,
+ struct netlbl_audit *audit_info)
+{
+ int ret_val;
+ const char *type_str;
+ struct audit_buffer *audit_buf;
+
+ ret_val = cipso_v4_doi_add(doi_def);
+
+ audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_ADD,
+ audit_info);
+ if (audit_buf != NULL) {
+ switch (doi_def->type) {
+ case CIPSO_V4_MAP_STD:
+ type_str = "std";
+ break;
+ case CIPSO_V4_MAP_PASS:
+ type_str = "pass";
+ break;
+ default:
+ type_str = "(unknown)";
+ }
+ audit_log_format(audit_buf,
+ " cipso_doi=%u cipso_type=%s res=%u",
+ doi_def->doi,
+ type_str,
+ ret_val == 0 ? 1 : 0);
+ audit_log_end(audit_buf);
+ }
+
+ return ret_val;
+}
+
+/**
+ * netlbl_cfg_cipsov4_add_map - Add a new CIPSOv4 DOI definition and mapping
+ * @doi_def: the DOI definition
+ * @domain: the domain mapping to add
+ * @audit_info: NetLabel audit information
+ *
+ * Description:
+ * Add a new CIPSOv4 DOI definition and NetLabel/LSM domain mapping for this
+ * new DOI definition to the NetLabel subsystem. A @domain value of NULL adds
+ * a new default domain mapping. Returns zero on success, negative values on
+ * failure.
+ *
+ */
+int netlbl_cfg_cipsov4_add_map(struct cipso_v4_doi *doi_def,
+ const char *domain,
+ struct netlbl_audit *audit_info)
+{
+ int ret_val = -ENOMEM;
+ struct netlbl_dom_map *entry;
+
+ entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
+ if (entry == NULL)
+ goto cfg_cipsov4_add_map_failure;
+ if (domain != NULL) {
+ entry->domain = kstrdup(domain, GFP_ATOMIC);
+ if (entry->domain == NULL)
+ goto cfg_cipsov4_add_map_failure;
+ }
+ entry->type = NETLBL_NLTYPE_CIPSOV4;
+ entry->type_def.cipsov4 = doi_def;
+
+ /* Grab a RCU read lock here so nothing happens to the doi_def variable
+ * between adding it to the CIPSOv4 protocol engine and adding a
+ * domain mapping for it. */
+
+ rcu_read_lock();
+ ret_val = netlbl_cfg_cipsov4_add(doi_def, audit_info);
+ if (ret_val != 0)
+ goto cfg_cipsov4_add_map_failure_unlock;
+ ret_val = netlbl_domhsh_add(entry, audit_info);
+ if (ret_val != 0)
+ goto cfg_cipsov4_add_map_failure_remove_doi;
+ rcu_read_unlock();
+
+ return 0;
+
+cfg_cipsov4_add_map_failure_remove_doi:
+ cipso_v4_doi_remove(doi_def->doi, audit_info, netlbl_cipsov4_doi_free);
+cfg_cipsov4_add_map_failure_unlock:
+ rcu_read_unlock();
+cfg_cipsov4_add_map_failure:
+ if (entry != NULL)
+ kfree(entry->domain);
+ kfree(entry);
+ return ret_val;
+}
+
+/**
+ * netlbl_cfg_cipsov4_del - Removean existing CIPSOv4 DOI definition
+ * @doi: the CIPSO DOI value
+ * @audit_info: NetLabel audit information
+ *
+ * Description:
+ * Removes an existing CIPSOv4 DOI definition from the NetLabel subsystem.
+ * Returns zero on success, negative values on failure.
+ *
+ */
+int netlbl_cfg_cipsov4_del(u32 doi, struct netlbl_audit *audit_info)
+{
+ return cipso_v4_doi_remove(doi, audit_info, netlbl_cipsov4_doi_free);
+}
+
+/*
* Security Attribute Functions
*/
diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c
index 5e82f1c0afb..2d0c29c837f 100644
--- a/net/rxrpc/af_rxrpc.c
+++ b/net/rxrpc/af_rxrpc.c
@@ -239,7 +239,7 @@ static struct rxrpc_transport *rxrpc_name_to_transport(struct socket *sock,
/* find a remote transport endpoint from the local one */
peer = rxrpc_get_peer(srx, gfp);
if (IS_ERR(peer))
- return ERR_PTR(PTR_ERR(peer));
+ return ERR_CAST(peer);
/* find a transport */
trans = rxrpc_get_transport(rx->local, peer, gfp);
@@ -282,7 +282,7 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock,
trans = rxrpc_name_to_transport(sock, (struct sockaddr *) srx,
sizeof(*srx), 0, gfp);
if (IS_ERR(trans)) {
- call = ERR_PTR(PTR_ERR(trans));
+ call = ERR_CAST(trans);
trans = NULL;
goto out;
}
@@ -306,7 +306,7 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock,
bundle = rxrpc_get_bundle(rx, trans, key, service_id, gfp);
if (IS_ERR(bundle)) {
- call = ERR_PTR(PTR_ERR(bundle));
+ call = ERR_CAST(bundle);
goto out;
}
diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c
index 8d7698621f0..971b867e048 100644
--- a/net/sched/cls_flow.c
+++ b/net/sched/cls_flow.c
@@ -19,6 +19,7 @@
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
+#include <linux/if_vlan.h>
#include <net/pkt_cls.h>
#include <net/ip.h>
@@ -270,6 +271,15 @@ static u32 flow_get_skgid(const struct sk_buff *skb)
return 0;
}
+static u32 flow_get_vlan_tag(const struct sk_buff *skb)
+{
+ u16 uninitialized_var(tag);
+
+ if (vlan_get_tag(skb, &tag) < 0)
+ return 0;
+ return tag & VLAN_VID_MASK;
+}
+
static u32 flow_key_get(const struct sk_buff *skb, int key)
{
switch (key) {
@@ -305,6 +315,8 @@ static u32 flow_key_get(const struct sk_buff *skb, int key)
return flow_get_skuid(skb);
case FLOW_KEY_SKGID:
return flow_get_skgid(skb);
+ case FLOW_KEY_VLAN_TAG:
+ return flow_get_vlan_tag(skb);
default:
WARN_ON(1);
return 0;
@@ -402,12 +414,13 @@ static int flow_change(struct tcf_proto *tp, unsigned long base,
if (tb[TCA_FLOW_KEYS]) {
keymask = nla_get_u32(tb[TCA_FLOW_KEYS]);
- if (fls(keymask) - 1 > FLOW_KEY_MAX)
- return -EOPNOTSUPP;
nkeys = hweight32(keymask);
if (nkeys == 0)
return -EINVAL;
+
+ if (fls(keymask) - 1 > FLOW_KEY_MAX)
+ return -EOPNOTSUPP;
}
err = tcf_exts_validate(tp, tb, tca[TCA_RATE], &e, &flow_ext_map);
diff --git a/net/sched/em_meta.c b/net/sched/em_meta.c
index 9c2ec1992a2..d417ec8e3ca 100644
--- a/net/sched/em_meta.c
+++ b/net/sched/em_meta.c
@@ -176,7 +176,7 @@ META_COLLECTOR(var_dev)
META_COLLECTOR(int_vlan_tag)
{
- unsigned short tag;
+ unsigned short uninitialized_var(tag);
if (vlan_get_tag(skb, &tag) < 0)
*err = -1;
else
@@ -735,11 +735,13 @@ static int em_meta_match(struct sk_buff *skb, struct tcf_ematch *m,
static inline void meta_delete(struct meta_match *meta)
{
- struct meta_type_ops *ops = meta_type_ops(&meta->lvalue);
+ if (meta) {
+ struct meta_type_ops *ops = meta_type_ops(&meta->lvalue);
- if (ops && ops->destroy) {
- ops->destroy(&meta->lvalue);
- ops->destroy(&meta->rvalue);
+ if (ops && ops->destroy) {
+ ops->destroy(&meta->lvalue);
+ ops->destroy(&meta->rvalue);
+ }
}
kfree(meta);
diff --git a/net/tipc/addr.h b/net/tipc/addr.h
index e4bd5335e48..3ba67e6ce03 100644
--- a/net/tipc/addr.h
+++ b/net/tipc/addr.h
@@ -57,11 +57,6 @@ static inline int in_own_cluster(u32 addr)
return !((addr ^ tipc_own_addr) >> 12);
}
-static inline int in_own_zone(u32 addr)
-{
- return !((addr ^ tipc_own_addr) >> 24);
-}
-
static inline int is_slave(u32 addr)
{
return addr & 0x800;
diff --git a/net/tipc/bcast.h b/net/tipc/bcast.h
index f910ed29d05..a2416fa6b90 100644
--- a/net/tipc/bcast.h
+++ b/net/tipc/bcast.h
@@ -74,19 +74,6 @@ extern char tipc_bclink_name[];
/**
- * nmap_get - determine if node exists in a node map
- */
-
-static inline int tipc_nmap_get(struct node_map *nm_ptr, u32 node)
-{
- int n = tipc_node(node);
- int w = n / WSIZE;
- int b = n % WSIZE;
-
- return nm_ptr->map[w] & (1 << b);
-}
-
-/**
* nmap_add - add a node to a node map
*/
diff --git a/net/tipc/msg.h b/net/tipc/msg.h
index ce265983637..e9ef6df2656 100644
--- a/net/tipc/msg.h
+++ b/net/tipc/msg.h
@@ -663,11 +663,6 @@ static inline void msg_set_remote_node(struct tipc_msg *m, u32 a)
msg_set_word(m, msg_hdr_sz(m)/4, a);
}
-static inline int msg_dataoctet(struct tipc_msg *m, u32 pos)
-{
- return(msg_data(m)[pos + 4] != 0);
-}
-
static inline void msg_set_dataoctet(struct tipc_msg *m, u32 pos)
{
msg_data(m)[pos + 4] = 1;
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index 24ddfd2ca38..22909036b9b 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -71,9 +71,9 @@ struct tipc_sock {
static u32 dispatch(struct tipc_port *tport, struct sk_buff *buf);
static void wakeupdispatch(struct tipc_port *tport);
-static struct proto_ops packet_ops;
-static struct proto_ops stream_ops;
-static struct proto_ops msg_ops;
+static const struct proto_ops packet_ops;
+static const struct proto_ops stream_ops;
+static const struct proto_ops msg_ops;
static struct proto tipc_proto;
@@ -1615,7 +1615,7 @@ static int getsockopt(struct socket *sock,
* Protocol switches for the various types of TIPC sockets
*/
-static struct proto_ops msg_ops = {
+static const struct proto_ops msg_ops = {
.owner = THIS_MODULE,
.family = AF_TIPC,
.release = release,
@@ -1636,7 +1636,7 @@ static struct proto_ops msg_ops = {
.sendpage = sock_no_sendpage
};
-static struct proto_ops packet_ops = {
+static const struct proto_ops packet_ops = {
.owner = THIS_MODULE,
.family = AF_TIPC,
.release = release,
@@ -1657,7 +1657,7 @@ static struct proto_ops packet_ops = {
.sendpage = sock_no_sendpage
};
-static struct proto_ops stream_ops = {
+static const struct proto_ops stream_ops = {
.owner = THIS_MODULE,
.family = AF_TIPC,
.release = release,
@@ -1678,7 +1678,7 @@ static struct proto_ops stream_ops = {
.sendpage = sock_no_sendpage
};
-static struct net_proto_family tipc_family_ops = {
+static const struct net_proto_family tipc_family_ops = {
.owner = THIS_MODULE,
.family = AF_TIPC,
.create = tipc_create
diff --git a/net/xfrm/xfrm_algo.c b/net/xfrm/xfrm_algo.c
index 6cc15250de6..8aa6440d689 100644
--- a/net/xfrm/xfrm_algo.c
+++ b/net/xfrm/xfrm_algo.c
@@ -399,6 +399,23 @@ static struct xfrm_algo_desc ealg_list[] = {
.sadb_alg_maxbits = 256
}
},
+{
+ .name = "rfc3686(ctr(aes))",
+
+ .uinfo = {
+ .encr = {
+ .blockbits = 128,
+ .defkeybits = 160, /* 128-bit key + 32-bit nonce */
+ }
+ },
+
+ .desc = {
+ .sadb_alg_id = SADB_X_EALG_AESCTR,
+ .sadb_alg_ivlen = 8,
+ .sadb_alg_minbits = 128,
+ .sadb_alg_maxbits = 256
+ }
+},
};
static struct xfrm_algo_desc calg_list[] = {