9p: make rpc code common and rework flush code

This code moves the rpc function to the common client base,
reorganizes the flush code to be more simple and stable, and
makes the necessary adjustments to the underlying transports
to adapt to the new structure.

This reduces the overall amount of code duplication between the
transports and should make adding new transports more straightforward.

Signed-off-by: Eric Van Hensbergen <ericvh@gmail.com>

diff --git a/net/9p/client.c b/net/9p/client.c
index f2d07ef..29934fe 100644
--- a/net/9p/client.c
+++ b/net/9p/client.c
@@ -55,6 +55,9 @@
 	{Opt_err, NULL},
 };
 
+static int
+p9_client_rpc(struct p9_client *c, struct p9_fcall *tc, struct p9_fcall **rc);
+
 /**
  * v9fs_parse_options - parse mount options into session structure
  * @options: options string passed from mount
@@ -269,6 +272,36 @@
 }
 
 /**
+ * p9_client_flush - flush (cancel) a request
+ * c: client state
+ * req: request to cancel
+ *
+ * This sents a flush for a particular requests and links
+ * the flush request to the original request.  The current
+ * code only supports a single flush request although the protocol
+ * allows for multiple flush requests to be sent for a single request.
+ *
+ */
+
+static int p9_client_flush(struct p9_client *c, struct p9_req_t *req)
+{
+	struct p9_fcall *tc, *rc = NULL;
+	int err;
+
+	P9_DPRINTK(P9_DEBUG_9P, "client %p tag %d\n", c, req->tc->tag);
+
+	tc = p9_create_tflush(req->tc->tag);
+	if (IS_ERR(tc))
+		return PTR_ERR(tc);
+
+	err = p9_client_rpc(c, tc, &rc);
+
+	/* we don't free anything here because RPC isn't complete */
+
+	return err;
+}
+
+/**
  * p9_free_req - free a request and clean-up as necessary
  * c: client state
  * r: request to release
@@ -289,6 +322,224 @@
 	}
 }
 
+/**
+ * p9_client_cb - call back from transport to client
+ * c: client state
+ * req: request received
+ *
+ */
+void p9_client_cb(struct p9_client *c, struct p9_req_t *req)
+{
+	struct p9_req_t *other_req;
+	unsigned long flags;
+
+	P9_DPRINTK(P9_DEBUG_MUX, ": %d\n", req->tc->tag);
+
+	if (req->status == REQ_STATUS_ERROR)
+		wake_up(req->wq);
+
+	if (req->tc->id == P9_TFLUSH) { /* flush receive path */
+		P9_DPRINTK(P9_DEBUG_MUX, "flush: %d\n", req->tc->tag);
+		spin_lock_irqsave(&c->lock, flags);
+		other_req = p9_tag_lookup(c, req->tc->params.tflush.oldtag);
+		if (other_req->flush_tag != req->tc->tag) /* stale flush */
+			spin_unlock_irqrestore(&c->lock, flags);
+		else {
+			BUG_ON(other_req->status != REQ_STATUS_FLSH);
+			other_req->status = REQ_STATUS_FLSHD;
+			spin_unlock_irqrestore(&c->lock, flags);
+			wake_up(other_req->wq);
+		}
+		p9_free_req(c, req);
+	} else { 				/* normal receive path */
+		P9_DPRINTK(P9_DEBUG_MUX, "normal: %d\n", req->tc->tag);
+		spin_lock_irqsave(&c->lock, flags);
+		if (req->status != REQ_STATUS_FLSHD)
+			req->status = REQ_STATUS_RCVD;
+		req->flush_tag = P9_NOTAG;
+		spin_unlock_irqrestore(&c->lock, flags);
+		wake_up(req->wq);
+		P9_DPRINTK(P9_DEBUG_MUX, "wakeup: %d\n", req->tc->tag);
+	}
+}
+EXPORT_SYMBOL(p9_client_cb);
+
+/**
+ * p9_client_rpc - issue a request and wait for a response
+ * @c: client session
+ * @tc: &p9_fcall request to transmit
+ * @rc: &p9_fcall to put reponse into
+ *
+ * Returns 0 on success, error code on failure
+ */
+
+static int
+p9_client_rpc(struct p9_client *c, struct p9_fcall *tc, struct p9_fcall **rc)
+{
+	int tag, err, size;
+	char *rdata;
+	struct p9_req_t *req;
+	unsigned long flags;
+	int sigpending;
+	int flushed = 0;
+
+	P9_DPRINTK(P9_DEBUG_9P, "client %p tc %p rc %p\n", c, tc, rc);
+
+	if (c->status != Connected)
+		return -EIO;
+
+	if (signal_pending(current)) {
+		sigpending = 1;
+		clear_thread_flag(TIF_SIGPENDING);
+	} else
+		sigpending = 0;
+
+	tag = P9_NOTAG;
+	if (tc->id != P9_TVERSION) {
+		tag = p9_idpool_get(c->tagpool);
+		if (tag < 0)
+			return -ENOMEM;
+	}
+
+	req = p9_tag_alloc(c, tag);
+
+	/* if this is a flush request, backlink flush request now to
+	 * avoid race conditions later. */
+	if (tc->id == P9_TFLUSH) {
+		struct p9_req_t *other_req =
+				p9_tag_lookup(c, tc->params.tflush.oldtag);
+		if (other_req->status == REQ_STATUS_FLSH)
+			other_req->flush_tag = tag;
+	}
+
+	p9_set_tag(tc, tag);
+
+	/*
+	 * if client passed in a pre-allocated response fcall struct
+	 * then we just use that, otherwise we allocate one.
+	 */
+
+	if (rc == NULL)
+		req->rc = NULL;
+	else
+		req->rc = *rc;
+	if (req->rc == NULL) {
+		req->rc = kmalloc(sizeof(struct p9_fcall) + c->msize,
+								GFP_KERNEL);
+		if (!req->rc) {
+			err = -ENOMEM;
+			p9_idpool_put(tag, c->tagpool);
+			p9_free_req(c, req);
+			goto reterr;
+		}
+		*rc = req->rc;
+	}
+
+	rdata = (char *)req->rc+sizeof(struct p9_fcall);
+
+	req->tc = tc;
+	P9_DPRINTK(P9_DEBUG_9P, "request: tc: %p rc: %p\n", req->tc, req->rc);
+
+	err = c->trans_mod->request(c, req);
+	if (err < 0) {
+		c->status = Disconnected;
+		goto reterr;
+	}
+
+	/* if it was a flush we just transmitted, return our tag */
+	if (tc->id == P9_TFLUSH)
+		return 0;
+again:
+	P9_DPRINTK(P9_DEBUG_9P, "wait %p tag: %d\n", req->wq, tag);
+	err = wait_event_interruptible(*req->wq,
+						req->status >= REQ_STATUS_RCVD);
+	P9_DPRINTK(P9_DEBUG_9P, "wait %p tag: %d returned %d (flushed=%d)\n",
+						req->wq, tag, err, flushed);
+
+	if (req->status == REQ_STATUS_ERROR) {
+		P9_DPRINTK(P9_DEBUG_9P, "req_status error %d\n", req->t_err);
+		err = req->t_err;
+	} else if (err == -ERESTARTSYS && flushed) {
+		P9_DPRINTK(P9_DEBUG_9P, "flushed - going again\n");
+		goto again;
+	} else if (req->status == REQ_STATUS_FLSHD) {
+		P9_DPRINTK(P9_DEBUG_9P, "flushed - erestartsys\n");
+		err = -ERESTARTSYS;
+	}
+
+	if ((err == -ERESTARTSYS) && (c->status == Connected) && (!flushed)) {
+		P9_DPRINTK(P9_DEBUG_9P, "flushing\n");
+		spin_lock_irqsave(&c->lock, flags);
+		if (req->status == REQ_STATUS_SENT)
+			req->status = REQ_STATUS_FLSH;
+		spin_unlock_irqrestore(&c->lock, flags);
+		sigpending = 1;
+		flushed = 1;
+		clear_thread_flag(TIF_SIGPENDING);
+
+		if (c->trans_mod->cancel(c, req)) {
+			err = p9_client_flush(c, req);
+			if (err == 0)
+				goto again;
+		}
+	}
+
+	if (sigpending) {
+		spin_lock_irqsave(&current->sighand->siglock, flags);
+		recalc_sigpending();
+		spin_unlock_irqrestore(&current->sighand->siglock, flags);
+	}
+
+	if (err < 0)
+		goto reterr;
+
+	size = le32_to_cpu(*(__le32 *) rdata);
+
+	err = p9_deserialize_fcall(rdata, size, req->rc, c->dotu);
+	if (err < 0) {
+		P9_DPRINTK(P9_DEBUG_9P,
+			"9p debug: client rpc deserialize returned %d\n", err);
+		goto reterr;
+	}
+
+#ifdef CONFIG_NET_9P_DEBUG
+	if ((p9_debug_level&P9_DEBUG_FCALL) == P9_DEBUG_FCALL) {
+		char buf[150];
+
+		p9_printfcall(buf, sizeof(buf), req->rc, c->dotu);
+		printk(KERN_NOTICE ">>> %p %s\n", c, buf);
+	}
+#endif
+
+	if (req->rc->id == P9_RERROR) {
+		int ecode = req->rc->params.rerror.errno;
+		struct p9_str *ename = &req->rc->params.rerror.error;
+
+		P9_DPRINTK(P9_DEBUG_MUX, "Rerror %.*s\n", ename->len,
+								ename->str);
+
+		if (c->dotu)
+			err = -ecode;
+
+		if (!err) {
+			err = p9_errstr2errno(ename->str, ename->len);
+
+			/* string match failed */
+			if (!err) {
+				PRINT_FCALL_ERROR("unknown error", req->rc);
+				err = -ESERVERFAULT;
+			}
+		}
+	} else
+		err = 0;
+
+reterr:
+	p9_free_req(c, req);
+
+	P9_DPRINTK(P9_DEBUG_9P, "returning %d\n", err);
+	return err;
+}
+
 static struct p9_fid *p9_fid_create(struct p9_client *clnt)
 {
 	int err;
@@ -339,20 +590,6 @@
 	kfree(fid);
 }
 
-/**
- * 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_mod->rpc(c, tc, rc);
-}
-
 struct p9_client *p9_client_create(const char *dev_name, char *options)
 {
 	int err, n;