[PATCH 9/10] 9p: tcp listener implementation

From: Latchesar Ionkov
Date: Fri Nov 02 2007 - 13:08:55 EST


This patch adds a TCP listener for the trans_fd transport. The listener
allows the in-kernel servers to listen on a TCP port for client connections.

Signed-off-by: Latchesar Ionkov <lucho@xxxxxxxxxx>

---
net/9p/mux.c | 269 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 265 insertions(+), 4 deletions(-)

diff --git a/net/9p/mux.c b/net/9p/mux.c
index ace135a..ad2e9a4 100644
--- a/net/9p/mux.c
+++ b/net/9p/mux.c
@@ -64,6 +64,7 @@ struct p9_trans_fd {
struct file *rd; /* read descriptor */
struct file *wr; /* write descriptor */
u32 msize;
+ int client; /* non-zero if client transport */
struct list_head trans_list;
struct p9fd_poll_task *poll_task;
wait_queue_head_t equeue;
@@ -90,6 +91,7 @@ enum {
Rpending = 2, /* can read */
Wworksched = 4, /* write work scheduled or running */
Wpending = 8, /* can write */
+ Destroy = 9,
};


@@ -112,6 +114,15 @@ enum {
Flushed,
};

+struct p9fd_tcp_listener {
+ struct sockaddr_in saddr;
+ struct socket *sock;
+ struct p9_trans_listener listener;
+ struct work_struct wq;
+ void (*dr_save)(struct sock *, int);
+ u32 msize;
+};
+
enum {
/* Options that take integer arguments */
Opt_port, Opt_rfdno, Opt_wfdno, Opt_msize,
@@ -145,6 +156,10 @@ static struct p9_trans *p9fd_create_unix_client(const char *devname,
char *options);
static struct p9_trans *p9fd_create_fd_client(const char *devname,
char *options);
+static struct p9_trans_listener *p9fd_listener_create(char *);
+static void p9fd_trans_listener_destroy(struct p9_trans_listener *);
+static void p9fd_tcp_data_ready(struct sock *sock, int count);
+static void p9fd_tcp_accept(struct work_struct *work);

static DEFINE_MUTEX(p9fd_task_lock);
static struct workqueue_struct *p9fd_wq;
@@ -158,6 +173,7 @@ static struct p9_trans_module p9_tcp_trans = {
.maxsize = MAX_SOCK_BUF,
.def = 1,
.create_client = p9fd_create_tcp_client,
+ .create_listener = p9fd_listener_create,
};

static struct p9_trans_module p9_unix_trans = {
@@ -292,8 +308,9 @@ static void p9fd_poll_stop(struct p9_trans_fd *trans)
* @rfd - read file descriptor
* @wfd - write file descriptor
* @msize - maximum message size
+ * @client - nonzero if client transport
*/
-struct p9_trans *p9fd_trans_create(int rfd, int wfd, u32 msize)
+struct p9_trans *p9fd_trans_create(int rfd, int wfd, u32 msize, int client)
{
int i, n, err;
struct p9_trans *trans;
@@ -317,6 +334,7 @@ struct p9_trans *p9fd_trans_create(int rfd, int wfd, u32 msize)
spin_lock_init(&ts->lock);
ts->trans = trans;
ts->msize = msize;
+ ts->client = client;
ts->rd = fget(rfd);
ts->wr = fget(wfd);
if (!ts->rd || !ts->wr) {
@@ -386,6 +404,9 @@ void p9fd_trans_destroy(struct p9_trans *trans)
struct p9_trans_fd *ts;

ts = trans->priv;
+ if (test_and_set_bit(Destroy, &ts->wsched))
+ return;
+
P9_DPRINTK(P9_DEBUG_TRANS, "trans %p prev %p next %p\n", ts,
ts->trans_list.prev, ts->trans_list.next);
p9fd_trans_shutdown(ts, -ECONNRESET);
@@ -426,6 +447,9 @@ static void p9fd_trans_shutdown(struct p9_trans_fd *ts, int err)
(*fdreq->req->cb)(trans, fdreq->req);
kfree(fdreq);
}
+
+ if (!ts->client && !test_bit(Destroy, &ts->wsched))
+ (*trans->request)(trans, NULL);
}

/**
@@ -540,7 +564,11 @@ static void p9fd_fill_wbuf(struct p9_trans_fd *ts)
req = list_first_entry(&ts->unsent_req_list,
struct p9fd_trans_req, req_list);

- tc = req->req->tc;
+ if (ts->client)
+ tc = req->req->tc;
+ else
+ tc = req->req->rc;
+
P9_DPRINTK(P9_DEBUG_TRANS, "tag %d size %d\n", tc->tag,
tc->size);
if (tc->size + ts->wsize > ts->msize) {
@@ -710,6 +738,9 @@ void p9fd_client_request(struct p9_trans *trans, struct p9_trans_req *req)
struct p9_trans_fd *ts;
struct p9fd_trans_req *fdreq;

+ if (trans->err)
+ return;
+
ts = trans->priv;
P9_DPRINTK(P9_DEBUG_TRANS, "trans %p tag %d\n", ts, req->tag);
fdreq = p9fd_req_create(ts, req);
@@ -810,6 +841,94 @@ int p9fd_client_rcvd(struct p9_trans_fd *ts, u16 tag, int start, int count)
return 0;
}

+int p9fd_server_sent(struct p9_trans_fd *ts, struct p9fd_trans_req *fdreq)
+{
+ struct p9_trans_req *req;
+
+ req = fdreq->req;
+ kfree(req->tc);
+ kfree(req->rc);
+ kfree(req);
+ kfree(fdreq);
+ return 0;
+}
+
+/* called when the server is ready with the response */
+void p9fd_server_respond(struct p9_trans *trans, struct p9_trans_req *req)
+{
+ int n;
+ struct p9_trans_fd *ts;
+ struct p9fd_trans_req *fdreq;
+
+ ts = trans->priv;
+ fdreq = req->cba;
+
+ spin_lock(&ts->lock);
+ list_move_tail(&fdreq->req_list, &ts->unsent_req_list);
+ spin_unlock(&ts->lock);
+
+ if (test_and_clear_bit(Wpending, &ts->wsched))
+ n = POLLOUT;
+ else
+ n = p9fd_poll(ts, NULL);
+
+ if (n & POLLOUT && !test_and_set_bit(Wworksched, &ts->wsched)) {
+ P9_DPRINTK(P9_DEBUG_TRANS, "schedule write work %p\n", ts);
+ queue_work(p9fd_wq, &ts->wq);
+ }
+}
+
+int p9fd_server_rcvd(struct p9_trans_fd *ts, u16 tag, int start, int count)
+{
+ int n;
+ struct p9_trans_req *req;
+ struct p9fd_trans_req *fdreq;
+
+ req = kmalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ fdreq = p9fd_req_create(ts, req);
+ if (IS_ERR(fdreq)) {
+ kfree(req);
+ return PTR_ERR(fdreq);
+ }
+
+ req->tag = tag;
+ req->err = 0;
+ req->tc = p9_fcall_alloc(count);
+ n = p9_fcall_put(req->tc, ts->rbuf + start, count);
+ if (n < 0)
+ goto error;
+
+ n = p9_deserialize_fcall(req->tc, ts->trans->dotu);
+ if (n < 0)
+ goto error;
+
+ req->rc = NULL;
+ req->cb = p9fd_server_respond;
+ req->cba = fdreq;
+
+ spin_lock(&ts->lock);
+ list_add_tail(&fdreq->req_list, &ts->req_list);
+ spin_unlock(&ts->lock);
+
+ (*ts->trans->request)(ts->trans, req);
+ return n;
+
+error:
+ kfree(req->tc);
+ kfree(req);
+ kfree(fdreq);
+
+ return n;
+}
+
+int p9fd_server_cancel(struct p9_trans *trans, struct p9_trans_req *req)
+{
+ return -ENOENT;
+}
+
static struct p9fd_trans_req *p9fd_req_create(struct p9_trans_fd *trans,
struct p9_trans_req *req)
{
@@ -982,7 +1101,7 @@ static struct p9_trans *p9fd_socket_open(struct socket *csocket, int msize)
return ERR_PTR(fd);
}

- trans = p9fd_trans_create(fd, fd, msize);
+ trans = p9fd_trans_create(fd, fd, msize, 1);
if (IS_ERR(trans)) {
P9_EPRINTK(KERN_ERR, "p9_socket_open: failed to open fd\n");
sockfd_put(csocket);
@@ -1109,7 +1228,7 @@ static struct p9_trans *p9fd_create_fd_client(const char *name, char *args)
return ERR_PTR(-ENOPROTOOPT);
}

- trans = p9fd_trans_create(opts.rfd, opts.wfd, opts.msize);
+ trans = p9fd_trans_create(opts.rfd, opts.wfd, opts.msize, 1);
if (IS_ERR(trans))
return trans;

@@ -1123,6 +1242,148 @@ static struct p9_trans *p9fd_create_fd_client(const char *name, char *args)
return trans;
}

+static struct p9_trans_listener *p9fd_listener_create(char *options)
+{
+ int n, err;
+ struct p9fd_tcp_listener *ls;
+ struct p9_fd_opts opts;
+
+ P9_DPRINTK(P9_DEBUG_TRANS, "options '%s'\n", options);
+ parse_opts(options, &opts);
+ ls = kmalloc(sizeof(*ls), GFP_KERNEL);
+ if (!ls)
+ return ERR_PTR(-ENOMEM);
+
+ ls->listener.err = 0;
+ ls->listener.aux = NULL;
+ ls->listener.taux = ls;
+ ls->listener.newtrans = NULL;
+ ls->listener.destroy = p9fd_trans_listener_destroy;
+ INIT_WORK(&ls->wq, p9fd_tcp_accept);
+ ls->sock = NULL;
+ ls->msize = opts.msize;
+ err = sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &ls->sock);
+ if (!ls->sock) {
+ P9_DPRINTK(P9_DEBUG_TRANS, "cannot create socket %d\n", err);
+ goto error;
+ }
+
+ ls->saddr.sin_family = AF_INET;
+ memset(&ls->saddr.sin_zero, 0, sizeof(ls->saddr.sin_zero));
+ ls->saddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ ls->saddr.sin_port = htons(opts.port);
+ err = ls->sock->ops->bind(ls->sock, (struct sockaddr *) &ls->saddr,
+ sizeof(ls->saddr));
+ if (err < 0) {
+ P9_DPRINTK(P9_DEBUG_TRANS, "cannot create bind %d\n", err);
+ goto error;
+ }
+
+ n = 1;
+ err = kernel_setsockopt(ls->sock, SOL_SOCKET, SO_REUSEADDR,
+ (char *) &n, sizeof(n));
+ if (err < 0) {
+ P9_DPRINTK(P9_DEBUG_TRANS, "cannot create setsockopt\n");
+ goto error;
+ }
+
+ err = ls->sock->ops->listen(ls->sock, 5);
+ if (err < 0) {
+ P9_DPRINTK(P9_DEBUG_TRANS, "cannot listen %d\n", err);
+ goto error;
+ }
+
+ ls->dr_save = ls->sock->sk->sk_data_ready;
+ ls->sock->sk->sk_data_ready = p9fd_tcp_data_ready;
+ ls->sock->sk->sk_user_data = ls;
+
+ return &ls->listener;
+
+error:
+ if (ls->sock)
+ sock_release(ls->sock);
+
+ kfree(ls);
+ return ERR_PTR(err);
+}
+
+static void p9fd_trans_listener_destroy(struct p9_trans_listener *lis)
+{
+ struct p9fd_tcp_listener *ls;
+
+ ls = container_of(lis, struct p9fd_tcp_listener, listener);
+ if (ls->sock) {
+ ls->sock->ops->shutdown(ls->sock, 2);
+ sock_release(ls->sock);
+ ls->sock = NULL;
+ }
+
+ kfree(ls);
+}
+
+static void p9fd_tcp_data_ready(struct sock *sock, int count)
+{
+ struct p9fd_tcp_listener *ls;
+
+ ls = sock->sk_user_data;
+ BUG_ON(ls == NULL);
+ queue_work(p9fd_wq, &ls->wq);
+}
+
+static void p9fd_tcp_accept(struct work_struct *work)
+{
+ int err, fd;
+ struct p9fd_tcp_listener *ls;
+ struct socket *sock;
+ struct p9_trans *trans;
+ struct p9_trans_fd *ts;
+
+ ls = container_of(work, struct p9fd_tcp_listener, wq);
+ err = sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &sock);
+ if (!sock) {
+ P9_DPRINTK(P9_DEBUG_TRANS, "cannot create socket %d\n", err);
+ goto error;
+ }
+
+ sock->type = ls->sock->type;
+ sock->ops = ls->sock->ops;
+ err = ls->sock->ops->accept(ls->sock, sock, 0);
+ if (err) {
+ P9_DPRINTK(P9_DEBUG_TRANS, "cannot accept %d\n", err);
+ goto error;
+ }
+
+ sock->sk->sk_data_ready = ls->dr_save;
+ sock->sk->sk_user_data = NULL;
+ sock->sk->sk_allocation = GFP_NOIO;
+
+ fd = sock_map_fd(sock);
+ if (fd < 0) {
+ err = fd;
+ P9_DPRINTK(P9_DEBUG_TRANS, "cannot map fd %d\n", err);
+ goto error;
+ }
+
+ trans = p9fd_trans_create(fd, fd, ls->msize, 0);
+ if (IS_ERR(trans)) {
+ err = PTR_ERR(trans);
+ P9_DPRINTK(P9_DEBUG_TRANS, "cannot create trans %d\n", err);
+ goto error;
+ }
+
+ trans->request = NULL;
+ trans->cancel = p9fd_server_cancel;
+ trans->destroy = p9fd_trans_destroy;
+ ts = trans->priv;
+ ts->sent = p9fd_server_sent;
+ ts->rcvd = p9fd_server_rcvd;
+ (*ls->listener.newtrans)(&ls->listener, trans);
+
+error:
+ ls->listener.err = err;
+ (*ls->listener.newtrans)(&ls->listener, NULL);
+}
+
static int __init p9_trans_fd_init(void)
{
int i;
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/