diff options
Diffstat (limited to 'net')
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(¤t->sighand->siglock, flags); - recalc_sigpending(); - spin_unlock_irqrestore(¤t->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(¤t->sighand->siglock, flags); + recalc_sigpending(); + spin_unlock_irqrestore(¤t->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[] = { |