* [RFC2][PATCH] 9p/net: add zero copy support
@ 2010-09-04 20:24 Latchesar Ionkov
0 siblings, 0 replies; only message in thread
From: Latchesar Ionkov @ 2010-09-04 20:24 UTC (permalink / raw)
To: v9fs-developer; +Cc: linux-fsdevel
Adds support for scatterlists for the transports. If the transport
indicates that it can handle scatterlists (P9_TRANS_SG in the flags
field), it should check the sg field in the p9_fcall structs. If
sg field is non-NULL, the transport should write/read the 9P
message into the memory pointed by the scatterlist.
The protocol and client code prepares the transmitting/receiving
buffers so the data is directly written from/read to the user data
buffer.
Signed-off-by: Latchesar Ionkov <lucho@ionkov.net>
---
include/net/9p/9p.h | 20 ++-
include/net/9p/client.h | 24 ++
include/net/9p/transport.h | 11 +
net/9p/client.c | 512 ++++++++++++++++++++++++++++++++++----------
net/9p/protocol.c | 56 ++++--
net/9p/protocol.h | 3 +
net/9p/trans_fd.c | 20 +-
net/9p/util.c | 2 +-
8 files changed, 502 insertions(+), 146 deletions(-)
diff --git a/include/net/9p/9p.h b/include/net/9p/9p.h
index a8de812..871e90d 100644
--- a/include/net/9p/9p.h
+++ b/include/net/9p/9p.h
@@ -27,6 +27,8 @@
#ifndef NET_9P_H
#define NET_9P_H
+#include <linux/scatterlist.h>
+
/**
* enum p9_debug_flags - bits for mount time debug parameter
* @P9_DEBUG_ERROR: more verbose error messages including original error string
@@ -304,6 +306,8 @@ enum p9_qid_t {
/* ample room for Twrite/Rread header */
#define P9_IOHDRSZ 24
+#define P9_TWRITE_HDRSZ 23
+#define P9_RREAD_HDRSZ 11
/* Room for readdir header */
#define P9_READDIRHDRSZ 24
@@ -633,9 +637,12 @@ struct p9_rwstat {
* @size: prefixed length of the structure
* @id: protocol operating identifier of type &p9_msg_t
* @tag: transaction id of the request
- * @offset: used by marshalling routines to track currentposition in buffer
+ * @offset: used by marshalling routines to track current position in buffer
* @capacity: used by marshalling routines to track total capacity
- * @sdata: payload
+ * @sgcount: number of entries in the scatterlist array
+ * @sg: array of scatterlist structs that describe the payload. The first
+ * entry in the array is always mapped and pointer to it is stored in &buf
+ * @buf: payload (whole if sg is NULL, otherwise the first entry).
*
* &p9_fcall represents the structure for all 9P RPC
* transactions. Requests are packaged into fcalls, and reponses
@@ -645,14 +652,21 @@ struct p9_rwstat {
*/
struct p9_fcall {
+ /* 9P message header data */
u32 size;
u8 id;
u16 tag;
+ /* data related to marshalling */
size_t offset;
size_t capacity;
- uint8_t *sdata;
+ /* buffer description */
+ int sgcount;
+ struct scatterlist *sg;
+ uint8_t *buf;
+
+ struct list_head fclist;
};
struct p9_idpool;
diff --git a/include/net/9p/client.h b/include/net/9p/client.h
index d1aa2cf..ccc3ab7 100644
--- a/include/net/9p/client.h
+++ b/include/net/9p/client.h
@@ -95,6 +95,8 @@ enum p9_req_status_t {
* @wq: wait_queue for the client to block on for this request
* @tc: the request fcall structure
* @rc: the response fcall structure
+ * @tb: the request buffer
+ * @rb: the response buffer
* @aux: transport specific data (provided for trans_fd migration)
* @req_list: link for higher level objects to chain requests
*
@@ -110,6 +112,7 @@ enum p9_req_status_t {
*/
struct p9_req_t {
+ u16 tag;
int status;
int t_err;
wait_queue_head_t *wq;
@@ -134,6 +137,12 @@ struct p9_req_t {
* @tagpool - transaction id accounting for session
* @reqs - 2D array of requests
* @max_tag - current maximum tag id allocated
+ * @sblist - list of small pdu
+ * @sbcount - number of elements in sblist
+ * @lblist - list of large (->msize) pdu
+ * @lbcount - number of elements in lblist
+ * @sgblist - list of scatterlist pdu
+ * @sgbcount - number of elements in sgblist
*
* The client structure is used to keep track of various per-client
* state that has been instantiated.
@@ -146,12 +155,20 @@ struct p9_req_t {
* Each row is 256 requests and we'll support up to 256 rows for
* a total of 64k concurrent requests per session.
*
+ * The client structure keeps track of two classes of 9P message buffers:
+ * - large buffers (lblist, lbcount) that can hold up to &msize bytes
+ * messages.
+ * - small buffers (sblist, sbcount) that can hold up to P9_SMALL_SIZE
+ * byte header + scatterlist for up to &msize/PAGE_SIZE+2 pages.
+ * There are up to P9_MAX_BUFS messages kept in the lists.
+ *
* Bugs: duplicated data and potentially unnecessary elements.
*/
struct p9_client {
spinlock_t lock; /* protect client structure */
int msize;
+ int maxsgcount;
unsigned char proto_version;
struct p9_trans_module *trans_mod;
enum p9_trans_status status;
@@ -164,6 +181,11 @@ struct p9_client {
struct p9_idpool *tagpool;
struct p9_req_t *reqs[P9_ROW_MAXTAG];
int max_tag;
+
+ struct list_head sblist;
+ int sbcount;
+ struct list_head lblist;
+ int lbcount;
};
/**
@@ -257,6 +279,8 @@ void p9_client_cb(struct p9_client *c, struct p9_req_t *req);
int p9_parse_header(struct p9_fcall *, int32_t *, int8_t *, int16_t *, int);
int p9stat_read(char *, int, struct p9_wstat *, int);
void p9stat_free(struct p9_wstat *);
+struct p9_fcall *p9_fcall_alloc(struct p9_client *c, int size);
+void p9_fcall_free(struct p9_client *c, struct p9_fcall *fc);
int p9_is_proto_dotu(struct p9_client *clnt);
int p9_is_proto_dotl(struct p9_client *clnt);
diff --git a/include/net/9p/transport.h b/include/net/9p/transport.h
index 6d5886e..4912dc1 100644
--- a/include/net/9p/transport.h
+++ b/include/net/9p/transport.h
@@ -43,11 +43,16 @@
* BUGS: the transport module list isn't protected.
*/
+enum {
+ P9_TRANS_SG = 1, /* support for scatterlists */
+};
+
struct p9_trans_module {
struct list_head list;
char *name; /* name of transport */
int maxsize; /* max message size of transport */
int def; /* this transport should be default */
+ int flags; /* P9_TRANS_* flags */
struct module *owner;
int (*create)(struct p9_client *, const char *, char *);
void (*close) (struct p9_client *);
@@ -60,4 +65,10 @@ void v9fs_unregister_trans(struct p9_trans_module *m);
struct p9_trans_module *v9fs_get_trans_by_name(const substring_t *name);
struct p9_trans_module *v9fs_get_default_trans(void);
void v9fs_put_trans(struct p9_trans_module *m);
+
+static int inline p9_trans_sg_support(struct p9_trans_module *ts)
+{
+ return ts->flags & P9_TRANS_SG;
+}
+
#endif /* NET_9P_TRANSPORT_H */
diff --git a/net/9p/client.c b/net/9p/client.c
index 9eb7250..8536411 100644
--- a/net/9p/client.c
+++ b/net/9p/client.c
@@ -32,12 +32,16 @@
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
+#include <linux/highmem.h>
#include <net/9p/9p.h>
#include <linux/parser.h>
#include <net/9p/client.h>
#include <net/9p/transport.h>
#include "protocol.h"
+#define P9_FCALL_MINSZ 256
+#define P9_MAX_BUF 32
+
/*
* Client Option Parsing (code inspired by NFS code)
* - a little lazy - parse all client options
@@ -92,8 +96,8 @@ static int get_protocol_version(const substring_t *name)
return version;
}
-static struct p9_req_t *
-p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...);
+static struct p9_req_t *p9_client_rpc(struct p9_client *c, int8_t type,
+ struct p9_fcall *tc, struct p9_fcall *rc, const char *fmt, ...);
/**
* parse_options - parse mount options into client structure
@@ -229,29 +233,11 @@ static struct p9_req_t *p9_tag_alloc(struct p9_client *c, u16 tag)
return ERR_PTR(-ENOMEM);
}
init_waitqueue_head(req->wq);
- req->tc = kmalloc(sizeof(struct p9_fcall)+c->msize,
- GFP_KERNEL);
- req->rc = kmalloc(sizeof(struct p9_fcall)+c->msize,
- GFP_KERNEL);
- if ((!req->tc) || (!req->rc)) {
- printk(KERN_ERR "Couldn't grow tag array\n");
- kfree(req->tc);
- kfree(req->rc);
- kfree(req->wq);
- req->tc = req->rc = NULL;
- req->wq = NULL;
- return ERR_PTR(-ENOMEM);
- }
- req->tc->sdata = (char *) req->tc + sizeof(struct p9_fcall);
- req->tc->capacity = c->msize;
- req->rc->sdata = (char *) req->rc + sizeof(struct p9_fcall);
- req->rc->capacity = c->msize;
+ req->tc = NULL;
+ req->rc = NULL;
}
- p9pdu_reset(req->tc);
- p9pdu_reset(req->rc);
-
- req->tc->tag = tag-1;
+ req->tag = tag - 1;
req->status = REQ_STATUS_ALLOC;
return &c->reqs[row][col];
@@ -357,14 +343,91 @@ static void p9_tag_cleanup(struct p9_client *c)
static void p9_free_req(struct p9_client *c, struct p9_req_t *r)
{
- int tag = r->tc->tag;
- P9_DPRINTK(P9_DEBUG_MUX, "clnt %p req %p tag: %d\n", c, r, tag);
+ int tag = r->tag;
+ P9_DPRINTK(P9_DEBUG_MUX, "clnt %p req %p tag: %d\n", c, r, tag);
+ p9_fcall_free(c, r->tc);
+ r->tc = NULL;
+ p9_fcall_free(c, r->rc);
+ r->rc = NULL;
r->status = REQ_STATUS_IDLE;
if (tag != P9_NOTAG && p9_idpool_check(tag, c->tagpool))
p9_idpool_put(tag, c->tagpool);
}
+struct p9_fcall *p9_fcall_alloc(struct p9_client *c, int size)
+{
+ unsigned long flags;
+ struct p9_fcall *fc;
+ int *count;
+ struct list_head *list;
+
+ fc = NULL;
+ if (size < P9_FCALL_MINSZ) {
+ size = P9_FCALL_MINSZ + c->maxsgcount*sizeof(struct scatterlist);
+ list = &c->sblist;
+ count = &c->sbcount;
+ } else {
+ size = c->msize;
+ list = &c->lblist;
+ count = &c->lbcount;
+ }
+
+ spin_lock_irqsave(&c->lock, flags);
+ if (*count > 0) {
+ fc = list_first_entry(list, struct p9_fcall, fclist);
+ list_del(&fc->fclist);
+ (*count)--;
+ }
+ spin_unlock_irqrestore(&c->lock, flags);
+ if (fc)
+ return fc;
+
+ /* no free fcalls, allocate one */
+ fc = kmalloc(sizeof(struct p9_fcall) + size, GFP_KERNEL);
+ if (!fc)
+ return NULL;
+
+ INIT_LIST_HEAD(&fc->fclist);
+ fc->capacity = size;
+ fc->buf = (uint8_t *) &fc[1];
+ fc->sgcount = 0;
+ fc->sg = NULL;
+
+ return fc;
+}
+EXPORT_SYMBOL(p9_fcall_alloc);
+
+void p9_fcall_free(struct p9_client *c, struct p9_fcall *fc)
+{
+ unsigned long flags;
+ int *count;
+ struct list_head *list;
+
+ if (!fc)
+ return;
+
+ fc->sgcount = 0;
+ fc->sg = NULL;
+ if (fc->capacity == c->msize) {
+ count = &c->lbcount;
+ list = &c->lblist;
+ } else {
+ count = &c->sbcount;
+ list = &c->sblist;
+ }
+
+ spin_lock_irqsave(&c->lock, flags);
+ if (*count < P9_MAX_BUF) {
+ list_add_tail(&fc->fclist, list);
+ fc = NULL;
+ (*count)++;
+ }
+ spin_unlock_irqrestore(&c->lock, flags);
+ kfree(fc);
+}
+EXPORT_SYMBOL(p9_fcall_free);
+
/**
* p9_client_cb - call back from transport to client
* c: client state
@@ -399,14 +462,12 @@ p9_parse_header(struct p9_fcall *pdu, int32_t *size, int8_t *type, int16_t *tag,
int err;
pdu->offset = 0;
- if (pdu->size == 0)
- pdu->size = 7;
-
err = p9pdu_readf(pdu, 0, "dbw", &r_size, &r_type, &r_tag);
if (err)
goto rewind_and_exit;
- pdu->size = r_size;
+/* we allow short reads
+ pdu->size = r_size; */
pdu->id = r_type;
pdu->tag = r_tag;
@@ -428,6 +489,44 @@ rewind_and_exit:
}
EXPORT_SYMBOL(p9_parse_header);
+static int p9_sg_read_error(struct p9_fcall *rc, int proto_version,
+ char **ename, int *ecode)
+{
+ int err, n, fecode;
+ int16_t len, count;
+ char *str;
+
+ /* the string len is in the header */
+ err = p9pdu_readf(rc, proto_version, "w", &len);
+ if (err < 0)
+ return err;
+
+ count = len;
+ fecode = (proto_version == p9_proto_2000u) ||
+ (proto_version == p9_proto_2000L);
+
+ if (fecode)
+ count += 4;
+
+ str = kmalloc(count + 1, GFP_KERNEL);
+ if (!str)
+ return -ENOMEM;
+
+ n = sg_copy_to_buffer(rc->sg, rc->sgcount, str, count);
+ if (fecode) {
+ /* if we got short read, set ecode to -EINVAL */
+ if (n == count) {
+ *ecode = le32_to_cpu(*(__le32 *) &str[len]);
+ n -= 4;
+ } else
+ *ecode = -EINVAL;
+ }
+
+ str[n] = '\0';
+ *ename = str;
+ return 0;
+}
+
/**
* p9_check_errors - check 9p packet for error return and process it
* @c: current client instance
@@ -442,28 +541,50 @@ EXPORT_SYMBOL(p9_parse_header);
static int p9_check_errors(struct p9_client *c, struct p9_req_t *req)
{
int8_t type;
+ int32_t size;
int err;
+ struct p9_fcall *rc;
- err = p9_parse_header(req->rc, NULL, &type, NULL, 0);
+ rc = req->rc;
+ err = p9_parse_header(rc, &size, &type, NULL, 0);
if (err) {
P9_DPRINTK(P9_DEBUG_ERROR, "couldn't parse header %d\n", err);
return err;
}
- if (type == P9_RERROR) {
+ if (type == req->tc->id+1) {
+ if (size != rc->size) {
+ P9_DPRINTK(P9_DEBUG_ERROR, "short read: %d got %d\n",
+ rc->size, size);
+ err = -EINVAL;
+ } else
+ err = 0;
+ } else if (type == P9_RERROR) {
int ecode;
char *ename;
- err = p9pdu_readf(req->rc, c->proto_version, "s?d",
+ /* error handling is complicated because of the
+ * Tread/Rerror case if scatterlist is used */
+ if (rc->sg)
+ err = p9_sg_read_error(req->rc, c->proto_version,
&ename, &ecode);
+ else
+ err = p9pdu_readf(req->rc, c->proto_version, "s?d",
+ &ename, &ecode);
+
if (err) {
- P9_DPRINTK(P9_DEBUG_ERROR, "couldn't parse error%d\n",
- err);
- return err;
+ if (size != rc->size) {
+ ename = kstrdup("Error name too long",
+ GFP_KERNEL);
+ ecode = -EINVAL;
+ } else {
+ P9_DPRINTK(P9_DEBUG_ERROR,
+ "couldn't parse error%d\n", err);
+ return err;
+ }
}
- if (p9_is_proto_dotu(c) ||
- p9_is_proto_dotl(c))
+ if (p9_is_proto_dotu(c) || p9_is_proto_dotl(c))
err = -ecode;
if (!err || !IS_ERR_VALUE(err))
@@ -472,8 +593,11 @@ static int p9_check_errors(struct p9_client *c, struct p9_req_t *req)
P9_DPRINTK(P9_DEBUG_9P, "<<< RERROR (%d) %s\n", -ecode, ename);
kfree(ename);
- } else
- err = 0;
+ } else {
+ P9_DPRINTK(P9_DEBUG_ERROR, "mismatch: expected %d, got %d\n",
+ req->tc->id + 1, type);
+ err = -EINVAL;
+ }
return err;
}
@@ -502,7 +626,7 @@ static int p9_client_flush(struct p9_client *c, struct p9_req_t *oldreq)
P9_DPRINTK(P9_DEBUG_9P, ">>> TFLUSH tag %d\n", oldtag);
- req = p9_client_rpc(c, P9_TFLUSH, "w", oldtag);
+ req = p9_client_rpc(c, P9_TFLUSH, NULL, NULL, "w", oldtag);
if (IS_ERR(req))
return PTR_ERR(req);
@@ -522,13 +646,13 @@ static int p9_client_flush(struct p9_client *c, struct p9_req_t *oldreq)
* p9_client_rpc - issue a request and wait for a response
* @c: client session
* @type: type of request
- * @fmt: protocol format string (see protocol.c)
+ * @tcfmt: request protocol format string (see protocol.c)
+ * @rcfmt: response protocol format string
*
- * Returns request structure (which client must free using p9_free_req)
*/
-static struct p9_req_t *
-p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...)
+static struct p9_req_t *p9_client_rpc(struct p9_client *c, int8_t type,
+ struct p9_fcall *tc, struct p9_fcall *rc, const char *fmt, ...)
{
va_list ap;
int tag, err;
@@ -563,13 +687,43 @@ p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...)
if (IS_ERR(req))
return req;
+ /* set up the outgoing buffer */
+ req->tc = tc;
+ if (!req->tc)
+ req->tc = p9_fcall_alloc(c, 0);
+
+ if (!req->tc) {
+ err = -ENOMEM;
+ goto reterr;
+ }
+ req->tc->tag = req->tag;
+
/* marshall the data */
- p9pdu_prepare(req->tc, tag, type);
va_start(ap, fmt);
+ p9pdu_prepare(req->tc, tag, type);
err = p9pdu_vwritef(req->tc, c->proto_version, fmt, ap);
- va_end(ap);
p9pdu_finalize(req->tc);
+ va_end(ap);
+ if (err<0 && !tc) {
+ /* the reason for the failure may be that the small buffer
+ was too small. Try with the large one */
+ p9_fcall_free(c, req->tc);
+ req->tc = p9_fcall_alloc(c, c->msize);
+ if (!req->tc) {
+ err = -ENOMEM;
+ goto reterr;
+ }
+ va_start(ap, fmt);
+ p9pdu_prepare(req->tc, tag, type);
+ err = p9pdu_vwritef(req->tc, c->proto_version, fmt, ap);
+ p9pdu_finalize(req->tc);
+ va_end(ap);
+ }
+
+ /* we'll let the transport to allocate the appropriate response buffer
+ in case it is not specified by the caller */
+ req->rc = rc;
err = c->trans_mod->request(c, req);
if (err < 0) {
c->status = Disconnected;
@@ -683,15 +837,15 @@ int p9_client_version(struct p9_client *c)
switch (c->proto_version) {
case p9_proto_2000L:
- req = p9_client_rpc(c, P9_TVERSION, "ds",
+ req = p9_client_rpc(c, P9_TVERSION, NULL, NULL, "ds",
c->msize, "9P2000.L");
break;
case p9_proto_2000u:
- req = p9_client_rpc(c, P9_TVERSION, "ds",
+ req = p9_client_rpc(c, P9_TVERSION, NULL, NULL, "ds",
c->msize, "9P2000.u");
break;
case p9_proto_legacy:
- req = p9_client_rpc(c, P9_TVERSION, "ds",
+ req = p9_client_rpc(c, P9_TVERSION, NULL, NULL, "ds",
c->msize, "9P2000");
break;
default:
@@ -721,8 +875,10 @@ int p9_client_version(struct p9_client *c)
goto error;
}
- if (msize < c->msize)
+ if (msize < c->msize) {
c->msize = msize;
+ c->maxsgcount = c->msize/PAGE_SIZE + 2;
+ }
error:
kfree(version);
@@ -746,6 +902,10 @@ struct p9_client *p9_client_create(const char *dev_name, char *options)
clnt->trans = NULL;
spin_lock_init(&clnt->lock);
INIT_LIST_HEAD(&clnt->fidlist);
+ clnt->sbcount = 0;
+ INIT_LIST_HEAD(&clnt->sblist);
+ clnt->lbcount = 0;
+ INIT_LIST_HEAD(&clnt->lblist);
p9_tag_init(clnt);
@@ -780,6 +940,7 @@ struct p9_client *p9_client_create(const char *dev_name, char *options)
if ((clnt->msize+P9_IOHDRSZ) > clnt->trans_mod->maxsize)
clnt->msize = clnt->trans_mod->maxsize-P9_IOHDRSZ;
+ clnt->maxsgcount = clnt->msize/PAGE_SIZE + 2;
err = p9_client_version(clnt);
if (err)
goto close_trans;
@@ -856,7 +1017,7 @@ struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *afid,
goto error;
}
- req = p9_client_rpc(clnt, P9_TATTACH, "ddss?d", fid->fid,
+ req = p9_client_rpc(clnt, P9_TATTACH, NULL, NULL, "ddss?d", fid->fid,
afid ? afid->fid : P9_NOFID, uname, aname, n_uname);
if (IS_ERR(req)) {
err = PTR_ERR(req);
@@ -905,7 +1066,7 @@ p9_client_auth(struct p9_client *clnt, char *uname, u32 n_uname, char *aname)
goto error;
}
- req = p9_client_rpc(clnt, P9_TAUTH, "dss?d",
+ req = p9_client_rpc(clnt, P9_TAUTH, NULL, NULL, "dss?d",
afid ? afid->fid : P9_NOFID, uname, aname, n_uname);
if (IS_ERR(req)) {
err = PTR_ERR(req);
@@ -964,7 +1125,7 @@ struct p9_fid *p9_client_walk(struct p9_fid *oldfid, int nwname, char **wnames,
P9_DPRINTK(P9_DEBUG_9P, ">>> TWALK fids %d,%d nwname %d wname[0] %s\n",
oldfid->fid, fid->fid, nwname, wnames ? wnames[0] : NULL);
- req = p9_client_rpc(clnt, P9_TWALK, "ddT", oldfid->fid, fid->fid,
+ req = p9_client_rpc(clnt, P9_TWALK, NULL, NULL, "ddT", oldfid->fid, fid->fid,
nwname, wnames);
if (IS_ERR(req)) {
err = PTR_ERR(req);
@@ -1030,9 +1191,9 @@ int p9_client_open(struct p9_fid *fid, int mode)
return -EINVAL;
if (p9_is_proto_dotl(clnt))
- req = p9_client_rpc(clnt, P9_TLOPEN, "dd", fid->fid, mode);
+ req = p9_client_rpc(clnt, P9_TLOPEN, NULL, NULL, "dd", fid->fid, mode);
else
- req = p9_client_rpc(clnt, P9_TOPEN, "db", fid->fid, mode);
+ req = p9_client_rpc(clnt, P9_TOPEN, NULL, NULL, "db", fid->fid, mode);
if (IS_ERR(req)) {
err = PTR_ERR(req);
goto error;
@@ -1074,8 +1235,8 @@ int p9_client_create_dotl(struct p9_fid *ofid, char *name, u32 flags, u32 mode,
if (ofid->mode != -1)
return -EINVAL;
- req = p9_client_rpc(clnt, P9_TLCREATE, "dsddd", ofid->fid, name, flags,
- mode, gid);
+ req = p9_client_rpc(clnt, P9_TLCREATE, NULL, NULL, "dsddd", ofid->fid, name,
+ flags, mode, gid);
if (IS_ERR(req)) {
err = PTR_ERR(req);
goto error;
@@ -1119,7 +1280,7 @@ int p9_client_fcreate(struct p9_fid *fid, char *name, u32 perm, int mode,
if (fid->mode != -1)
return -EINVAL;
- req = p9_client_rpc(clnt, P9_TCREATE, "dsdb?s", fid->fid, name, perm,
+ req = p9_client_rpc(clnt, P9_TCREATE, NULL, NULL, "dsdb?s", fid->fid, name, perm,
mode, extension);
if (IS_ERR(req)) {
err = PTR_ERR(req);
@@ -1158,8 +1319,8 @@ int p9_client_symlink(struct p9_fid *dfid, char *name, char *symtgt, gid_t gid,
dfid->fid, name, symtgt);
clnt = dfid->clnt;
- req = p9_client_rpc(clnt, P9_TSYMLINK, "dssd", dfid->fid, name, symtgt,
- gid);
+ req = p9_client_rpc(clnt, P9_TSYMLINK, NULL, NULL, "dssd", dfid->fid,
+ name, symtgt, gid);
if (IS_ERR(req)) {
err = PTR_ERR(req);
goto error;
@@ -1189,8 +1350,8 @@ int p9_client_link(struct p9_fid *dfid, struct p9_fid *oldfid, char *newname)
P9_DPRINTK(P9_DEBUG_9P, ">>> TLINK dfid %d oldfid %d newname %s\n",
dfid->fid, oldfid->fid, newname);
clnt = dfid->clnt;
- req = p9_client_rpc(clnt, P9_TLINK, "dds", dfid->fid, oldfid->fid,
- newname);
+ req = p9_client_rpc(clnt, P9_TLINK, NULL, NULL, "dds", dfid->fid,
+ oldfid->fid, newname);
if (IS_ERR(req))
return PTR_ERR(req);
@@ -1210,7 +1371,7 @@ int p9_client_clunk(struct p9_fid *fid)
err = 0;
clnt = fid->clnt;
- req = p9_client_rpc(clnt, P9_TCLUNK, "d", fid->fid);
+ req = p9_client_rpc(clnt, P9_TCLUNK, NULL, NULL, "d", fid->fid);
if (IS_ERR(req)) {
err = PTR_ERR(req);
goto error;
@@ -1236,7 +1397,7 @@ int p9_client_remove(struct p9_fid *fid)
err = 0;
clnt = fid->clnt;
- req = p9_client_rpc(clnt, P9_TREMOVE, "d", fid->fid);
+ req = p9_client_rpc(clnt, P9_TREMOVE, NULL, NULL, "d", fid->fid);
if (IS_ERR(req)) {
err = PTR_ERR(req);
goto error;
@@ -1251,54 +1412,148 @@ error:
}
EXPORT_SYMBOL(p9_client_remove);
-int
-p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset,
- u32 count)
+int p9_sg_prepare(struct p9_fcall *fc, int sz0, char *data,
+ const char __user *udata, u32 count, int rw)
{
- int err, rsize, total;
+ int i, m, err, off;
+ struct page *pages[64];
+
+ err = 0;
+ fc->sgcount = min(count/PAGE_SIZE + 3,
+ (fc->capacity - P9_FCALL_MINSZ) / sizeof(struct scatterlist));
+ fc->sg = (struct scatterlist *) &fc->buf[P9_FCALL_MINSZ];
+ sg_init_table(fc->sg, fc->sgcount);
+ sg_set_buf(&fc->sg[0], fc->buf, sz0);
+
+ if (udata) {
+ fc->sgcount = min(fc->sgcount, (int)(ARRAY_SIZE(pages) + 1));
+ err = get_user_pages_fast((unsigned long) udata, fc->sgcount-1,
+ rw, pages);
+ if (err < 0)
+ goto error;
+
+ off = ((unsigned long) udata) & ~PAGE_MASK;
+ for(i = 0; count > 0; off = 0, i++) {
+ m = min((int)(PAGE_SIZE - off), (int) count);
+ sg_set_page(&fc->sg[i+1], pages[i], m, off);
+ count -= m;
+ }
+ } else {
+ off = ((unsigned long) data) & ~PAGE_MASK;
+ for(i = 0; count > 0; off = 0, i++) {
+ m = min((int)(PAGE_SIZE - off), (int) count);
+ sg_set_buf(&fc->sg[i+1], data, m);
+ count -= m;
+ data += m;
+ }
+ }
+
+ sg_mark_end(&fc->sg[i]);
+ fc->sgcount = i+1;
+ fc->capacity = P9_FCALL_MINSZ;
+ return 0;
+
+error:
+ fc->sg = NULL;
+ fc->sgcount = 0;
+ return err;
+}
+
+void p9_sg_release(struct p9_client *c, struct p9_fcall *fc, char *data,
+ const char __user *udata)
+{
+ struct scatterlist *sg;
+ struct page *p;
+
+ if (!fc || !fc->sg)
+ return;
+
+ if (udata) {
+ /* fc->sg[0] is for the header and pointing to kernel space */
+ for(sg=&fc->sg[1]; sg != NULL; sg = sg_next(sg)) {
+ p = sg_page(sg);
+ kunmap(p);
+ put_page(p);
+ }
+ }
+
+ fc->sgcount = 0;
+ fc->sg = NULL;
+ fc->capacity = 256 + c->maxsgcount*sizeof(struct scatterlist);
+}
+
+int p9_client_read(struct p9_fid *fid, char *data, char __user *udata,
+ u64 offset, u32 count)
+{
+ int err, sgsupport;
struct p9_client *clnt;
struct p9_req_t *req;
+ struct p9_fcall *rc;
char *dataptr;
P9_DPRINTK(P9_DEBUG_9P, ">>> TREAD fid %d offset %llu %d\n", fid->fid,
(long long unsigned) offset, count);
+ rc = NULL;
err = 0;
clnt = fid->clnt;
- total = 0;
-
- rsize = fid->iounit;
- if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
- rsize = clnt->msize - P9_IOHDRSZ;
+ if (count > clnt->msize-P9_IOHDRSZ)
+ count = clnt->msize-P9_IOHDRSZ;
+
+ if (fid->iounit>0 && count>fid->iounit)
+ count = fid->iounit;
+
+ sgsupport = p9_trans_sg_support(clnt->trans_mod);
+ if (sgsupport) {
+ rc = p9_fcall_alloc(clnt, 0);
+ if (!rc)
+ return -ENOMEM;
+
+ /* don't use scatterlist for small buffers */
+ if (count >= 256) {
+ err = p9_sg_prepare(rc, P9_RREAD_HDRSZ, data, udata,
+ count, 1);
+ if (err < 0) {
+ p9_fcall_free(clnt, rc);
+ return err;
+ }
+ }
+ }
- if (count < rsize)
- rsize = count;
+ req = p9_client_rpc(clnt, P9_TREAD, NULL, rc, "dqd", fid->fid, offset,
+ count);
- req = p9_client_rpc(clnt, P9_TREAD, "dqd", fid->fid, offset, rsize);
if (IS_ERR(req)) {
err = PTR_ERR(req);
goto error;
}
- err = p9pdu_readf(req->rc, clnt->proto_version, "D", &count, &dataptr);
+ P9_DPRINTK(P9_DEBUG_9P, "<<< RREAD count %d\n", count);
+
+ rc = req->rc;
+ if (rc->sg)
+ err = p9pdu_readf(req->rc, clnt->proto_version, "d", &count);
+ else
+ err = p9pdu_readf(req->rc, clnt->proto_version, "D", &count, &dataptr);
+
if (err) {
p9pdu_dump(1, req->rc);
goto free_and_error;
}
- P9_DPRINTK(P9_DEBUG_9P, "<<< RREAD count %d\n", count);
-
- if (data) {
- memmove(data, dataptr, count);
- }
+ if (!rc->sg) {
+ if (data)
+ memmove(data, dataptr, count);
- if (udata) {
- err = copy_to_user(udata, dataptr, count);
- if (err) {
- err = -EFAULT;
- goto free_and_error;
+ if (udata) {
+ err = copy_to_user(udata, dataptr, count);
+ if (err) {
+ err = -EFAULT;
+ goto free_and_error;
+ }
}
}
+ p9_sg_release(clnt, rc, data, udata);
p9_free_req(clnt, req);
return count;
@@ -1313,28 +1568,54 @@ int
p9_client_write(struct p9_fid *fid, char *data, const char __user *udata,
u64 offset, u32 count)
{
- int err, rsize, total;
+ int err, sgsupport;
struct p9_client *clnt;
struct p9_req_t *req;
+ struct p9_fcall *tc;
P9_DPRINTK(P9_DEBUG_9P, ">>> TWRITE fid %d offset %llu count %d\n",
fid->fid, (long long unsigned) offset, count);
+ tc = NULL;
err = 0;
clnt = fid->clnt;
- total = 0;
- rsize = fid->iounit;
- if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
- rsize = clnt->msize - P9_IOHDRSZ;
+ if (count > clnt->msize-P9_IOHDRSZ)
+ count = clnt->msize-P9_IOHDRSZ;
+
+ if (fid->iounit>0 && count>fid->iounit)
+ count = fid->iounit;
+
+ sgsupport = p9_trans_sg_support(clnt->trans_mod);
+ if (sgsupport) {
+ tc = p9_fcall_alloc(clnt, 0);
+ if (!tc)
+ return -ENOMEM;
+
+ /* don't use scatterlist for small buffers */
+ if (count >= 256) {
+ err = p9_sg_prepare(tc, P9_TWRITE_HDRSZ, data, udata,
+ count, 0);
+ if (err < 0) {
+ p9_fcall_free(clnt, tc);
+ return err;
+ }
+ }
+ }
+
+ if (tc && tc->sg) {
+ /* the data is already put in the appropriate place in sg,
+ just put the fid, the offset and the count */
+ req = p9_client_rpc(clnt, P9_TWRITE, tc, NULL, "dqd", fid->fid,
+ offset, count);
+ } else {
+ if (data)
+ req = p9_client_rpc(clnt, P9_TWRITE, NULL, NULL, "dqD",
+ fid->fid, offset, count, data);
+ else
+ req = p9_client_rpc(clnt, P9_TWRITE, NULL, NULL, "dqU",
+ fid->fid, offset, count, udata);
+ }
- if (count < rsize)
- rsize = count;
- if (data)
- req = p9_client_rpc(clnt, P9_TWRITE, "dqD", fid->fid, offset,
- rsize, data);
- else
- req = p9_client_rpc(clnt, P9_TWRITE, "dqU", fid->fid, offset,
- rsize, udata);
if (IS_ERR(req)) {
err = PTR_ERR(req);
goto error;
@@ -1348,6 +1629,7 @@ p9_client_write(struct p9_fid *fid, char *data, const char __user *udata,
P9_DPRINTK(P9_DEBUG_9P, "<<< RWRITE count %d\n", count);
+ p9_sg_release(clnt, req->tc, data, udata);
p9_free_req(clnt, req);
return count;
@@ -1374,7 +1656,7 @@ struct p9_wstat *p9_client_stat(struct p9_fid *fid)
err = 0;
clnt = fid->clnt;
- req = p9_client_rpc(clnt, P9_TSTAT, "d", fid->fid);
+ req = p9_client_rpc(clnt, P9_TSTAT, NULL, NULL, "d", fid->fid);
if (IS_ERR(req)) {
err = PTR_ERR(req);
goto error;
@@ -1425,7 +1707,8 @@ struct p9_stat_dotl *p9_client_getattr_dotl(struct p9_fid *fid,
err = 0;
clnt = fid->clnt;
- req = p9_client_rpc(clnt, P9_TGETATTR, "dq", fid->fid, request_mask);
+ req = p9_client_rpc(clnt, P9_TGETATTR, NULL, NULL, "dq", fid->fid,
+ request_mask);
if (IS_ERR(req)) {
err = PTR_ERR(req);
goto error;
@@ -1516,7 +1799,8 @@ int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst)
wst->name, wst->uid, wst->gid, wst->muid, wst->extension,
wst->n_uid, wst->n_gid, wst->n_muid);
- req = p9_client_rpc(clnt, P9_TWSTAT, "dwS", fid->fid, wst->size+2, wst);
+ req = p9_client_rpc(clnt, P9_TWSTAT, NULL, NULL, "dwS", fid->fid,
+ wst->size+2, wst);
if (IS_ERR(req)) {
err = PTR_ERR(req);
goto error;
@@ -1547,7 +1831,8 @@ int p9_client_setattr(struct p9_fid *fid, struct p9_iattr_dotl *p9attr)
p9attr->size, p9attr->atime_sec, p9attr->atime_nsec,
p9attr->mtime_sec, p9attr->mtime_nsec);
- req = p9_client_rpc(clnt, P9_TSETATTR, "dI", fid->fid, p9attr);
+ req = p9_client_rpc(clnt, P9_TSETATTR, NULL, NULL, "dI", fid->fid,
+ p9attr);
if (IS_ERR(req)) {
err = PTR_ERR(req);
@@ -1571,7 +1856,7 @@ int p9_client_statfs(struct p9_fid *fid, struct p9_rstatfs *sb)
P9_DPRINTK(P9_DEBUG_9P, ">>> TSTATFS fid %d\n", fid->fid);
- req = p9_client_rpc(clnt, P9_TSTATFS, "d", fid->fid);
+ req = p9_client_rpc(clnt, P9_TSTATFS, NULL, NULL, "d", fid->fid);
if (IS_ERR(req)) {
err = PTR_ERR(req);
goto error;
@@ -1611,7 +1896,7 @@ int p9_client_rename(struct p9_fid *fid, struct p9_fid *newdirfid, char *name)
P9_DPRINTK(P9_DEBUG_9P, ">>> TRENAME fid %d newdirfid %d name %s\n",
fid->fid, newdirfid->fid, name);
- req = p9_client_rpc(clnt, P9_TRENAME, "dds", fid->fid,
+ req = p9_client_rpc(clnt, P9_TRENAME, NULL, NULL, "dds", fid->fid,
newdirfid->fid, name);
if (IS_ERR(req)) {
err = PTR_ERR(req);
@@ -1649,7 +1934,7 @@ struct p9_fid *p9_client_xattrwalk(struct p9_fid *file_fid,
">>> TXATTRWALK file_fid %d, attr_fid %d name %s\n",
file_fid->fid, attr_fid->fid, attr_name);
- req = p9_client_rpc(clnt, P9_TXATTRWALK, "dds",
+ req = p9_client_rpc(clnt, P9_TXATTRWALK, NULL, NULL, "dds",
file_fid->fid, attr_fid->fid, attr_name);
if (IS_ERR(req)) {
err = PTR_ERR(req);
@@ -1688,7 +1973,7 @@ int p9_client_xattrcreate(struct p9_fid *fid, const char *name,
fid->fid, name, (long long)attr_size, flags);
err = 0;
clnt = fid->clnt;
- req = p9_client_rpc(clnt, P9_TXATTRCREATE, "dsqd",
+ req = p9_client_rpc(clnt, P9_TXATTRCREATE, NULL, NULL, "dsqd",
fid->fid, name, attr_size, flags);
if (IS_ERR(req)) {
err = PTR_ERR(req);
@@ -1722,7 +2007,8 @@ int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset)
if (count < rsize)
rsize = count;
- req = p9_client_rpc(clnt, P9_TREADDIR, "dqd", fid->fid, offset, rsize);
+ req = p9_client_rpc(clnt, P9_TREADDIR, NULL, NULL, "dqd", fid->fid,
+ offset, rsize);
if (IS_ERR(req)) {
err = PTR_ERR(req);
goto error;
@@ -1760,8 +2046,8 @@ int p9_client_mknod_dotl(struct p9_fid *fid, char *name, int mode,
clnt = fid->clnt;
P9_DPRINTK(P9_DEBUG_9P, ">>> TMKNOD fid %d name %s mode %d major %d "
"minor %d\n", fid->fid, name, mode, MAJOR(rdev), MINOR(rdev));
- req = p9_client_rpc(clnt, P9_TMKNOD, "dsdddd", fid->fid, name, mode,
- MAJOR(rdev), MINOR(rdev), gid);
+ req = p9_client_rpc(clnt, P9_TMKNOD, NULL, NULL, "dsdddd", fid->fid,
+ name, mode, MAJOR(rdev), MINOR(rdev), gid);
if (IS_ERR(req))
return PTR_ERR(req);
@@ -1791,8 +2077,8 @@ int p9_client_mkdir_dotl(struct p9_fid *fid, char *name, int mode,
clnt = fid->clnt;
P9_DPRINTK(P9_DEBUG_9P, ">>> TMKDIR fid %d name %s mode %d gid %d\n",
fid->fid, name, mode, gid);
- req = p9_client_rpc(clnt, P9_TMKDIR, "dsdd", fid->fid, name, mode,
- gid);
+ req = p9_client_rpc(clnt, P9_TMKDIR, NULL, NULL, "dsdd", fid->fid,
+ name, mode, gid);
if (IS_ERR(req))
return PTR_ERR(req);
diff --git a/net/9p/protocol.c b/net/9p/protocol.c
index 3acd3af..36ab7bf 100644
--- a/net/9p/protocol.c
+++ b/net/9p/protocol.c
@@ -31,6 +31,7 @@
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/types.h>
+#include <linux/highmem.h>
#include <net/9p/9p.h>
#include <net/9p/client.h>
#include "protocol.h"
@@ -60,7 +61,7 @@ void
p9pdu_dump(int way, struct p9_fcall *pdu)
{
int i, n;
- u8 *data = pdu->sdata;
+ u8 *data = pdu->buf;
int datalen = pdu->size;
char buf[255];
int buflen = 255;
@@ -104,30 +105,38 @@ EXPORT_SYMBOL(p9stat_free);
static size_t pdu_read(struct p9_fcall *pdu, void *data, size_t size)
{
- size_t len = MIN(pdu->size - pdu->offset, size);
- memcpy(data, &pdu->sdata[pdu->offset], len);
- pdu->offset += len;
- return size - len;
+ if (pdu->size-pdu->offset < size)
+ return 1;
+
+ memcpy(data, &pdu->buf[pdu->offset], size);
+ pdu->offset += size;
+ return 0;
}
static size_t pdu_write(struct p9_fcall *pdu, const void *data, size_t size)
{
- size_t len = MIN(pdu->capacity - pdu->size, size);
- memcpy(&pdu->sdata[pdu->size], data, len);
- pdu->size += len;
- return size - len;
+ if (pdu->capacity-pdu->offset < size)
+ return 1;
+
+ memcpy(&pdu->buf[pdu->offset], data, size);
+ pdu->offset += size;
+ return 0;
}
static size_t
pdu_write_u(struct p9_fcall *pdu, const char __user *udata, size_t size)
{
- size_t len = MIN(pdu->capacity - pdu->size, size);
- int err = copy_from_user(&pdu->sdata[pdu->size], udata, len);
+ int err;
+
+ if (pdu->capacity-pdu->offset < size)
+ return 1;
+
+ err = copy_from_user(&pdu->buf[pdu->offset], udata, size);
if (err)
- printk(KERN_WARNING "pdu_write_u returning: %d\n", err);
+ return 1;
- pdu->size += len;
- return size - len;
+ pdu->offset += size;
+ return 0;
}
/*
@@ -259,7 +268,7 @@ p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt,
*count =
MIN(*count,
pdu->size - pdu->offset);
- *data = &pdu->sdata[pdu->offset];
+ *data = &pdu->buf[pdu->offset];
}
}
break;
@@ -582,7 +591,7 @@ int p9stat_read(char *buf, int len, struct p9_wstat *st, int proto_version)
fake_pdu.size = len;
fake_pdu.capacity = len;
- fake_pdu.sdata = buf;
+ fake_pdu.buf = buf;
fake_pdu.offset = 0;
ret = p9pdu_readf(&fake_pdu, proto_version, "S", st);
@@ -597,16 +606,25 @@ EXPORT_SYMBOL(p9stat_read);
int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type)
{
+ pdu->id = type;
+ pdu->offset = 0;
return p9pdu_writef(pdu, 0, "dbw", 0, type, tag);
}
int p9pdu_finalize(struct p9_fcall *pdu)
{
- int size = pdu->size;
+ int size;
int err;
+ struct scatterlist *sg;
- pdu->size = 0;
+ size = pdu->offset;
+ if (pdu->sg) {
+ for(sg = &pdu->sg[1]; sg != NULL; sg = sg_next(sg))
+ size += sg->length;
+ }
+ pdu->offset = 0;
err = p9pdu_writef(pdu, 0, "d", size);
+ pdu->offset = size;
pdu->size = size;
#ifdef CONFIG_NET_9P_DEBUG
@@ -635,7 +653,7 @@ int p9dirent_read(char *buf, int len, struct p9_dirent *dirent,
fake_pdu.size = len;
fake_pdu.capacity = len;
- fake_pdu.sdata = buf;
+ fake_pdu.buf = buf;
fake_pdu.offset = 0;
ret = p9pdu_readf(&fake_pdu, proto_version, "Qqbs", &dirent->qid,
diff --git a/net/9p/protocol.h b/net/9p/protocol.h
index 2431c0f..09da1a7 100644
--- a/net/9p/protocol.h
+++ b/net/9p/protocol.h
@@ -32,3 +32,6 @@ int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type);
int p9pdu_finalize(struct p9_fcall *pdu);
void p9pdu_dump(int, struct p9_fcall *);
void p9pdu_reset(struct p9_fcall *pdu);
+int p9pdu_prepare_sg(struct p9_fcall *pdu, int32_t hdrsz, int32_t count,
+ int rw, const void __user *data);
+void p9pdu_free_sg(struct p9_fcall *pdu);
diff --git a/net/9p/trans_fd.c b/net/9p/trans_fd.c
index c85109d..9843376 100644
--- a/net/9p/trans_fd.c
+++ b/net/9p/trans_fd.c
@@ -347,16 +347,15 @@ static void p9_read_work(struct work_struct *work)
goto error;
}
- if (m->req->rc == NULL) {
- m->req->rc = kmalloc(sizeof(struct p9_fcall) +
- m->client->msize, GFP_KERNEL);
- if (!m->req->rc) {
- m->req = NULL;
- err = -ENOMEM;
- goto error;
- }
+ /* trans_fd doesn't support scatterlists, rc can't e non-NULL */
+ BUG_ON(m->req->rc != NULL);
+ m->req->rc = p9_fcall_alloc(m->client, n);
+ if (!m->req->rc) {
+ m->req = NULL;
+ err = -ENOMEM;
+ goto error;
}
- m->rbuf = (char *)m->req->rc + sizeof(struct p9_fcall);
+ m->rbuf = (char *)m->req->rc->buf;
memcpy(m->rbuf, m->tmp_buf, m->rsize);
m->rsize = n;
}
@@ -364,6 +363,7 @@ static void p9_read_work(struct work_struct *work)
/* not an else because some packets (like clunk) have no payload */
if ((m->req) && (m->rpos == m->rsize)) { /* packet is read in */
P9_DPRINTK(P9_DEBUG_TRANS, "got new packet\n");
+ m->req->rc->size = m->rsize;
spin_lock(&m->client->lock);
if (m->req->status != REQ_STATUS_ERROR)
m->req->status = REQ_STATUS_RCVD;
@@ -462,7 +462,7 @@ static void p9_write_work(struct work_struct *work)
P9_DPRINTK(P9_DEBUG_TRANS, "move req %p\n", req);
list_move_tail(&req->req_list, &m->req_list);
- m->wbuf = req->tc->sdata;
+ m->wbuf = req->tc->buf;
m->wsize = req->tc->size;
m->wpos = 0;
spin_unlock(&m->client->lock);
diff --git a/net/9p/util.c b/net/9p/util.c
index e048701..f8c35f8 100644
--- a/net/9p/util.c
+++ b/net/9p/util.c
@@ -66,7 +66,7 @@ struct p9_idpool *p9_idpool_create(void)
EXPORT_SYMBOL(p9_idpool_create);
/**
- * p9_idpool_destroy - create a new per-connection id pool
+ * p9_idpool_destroy - destroy the per-connection id pool
* @p: idpool to destory
*/
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2010-09-04 20:25 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-09-04 20:24 [RFC2][PATCH] 9p/net: add zero copy support Latchesar Ionkov
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.